From a62ef240d14ffc38a8f48eb9909696c9831e4818 Mon Sep 17 00:00:00 2001 From: mika kuns Date: Tue, 19 May 2026 08:58:43 +0200 Subject: [PATCH] refactor(config): consolidate model aliases into ModelRegistry Replaces three scattered model lists (ListSettingsModalViewModel, DetailsIslandViewModel, GeneralSettingsTabViewModel) and the hardcoded planning model with a single source. Planning launcher now uses the opus alias instead of pinning claude-opus-4-7. --- src/ClaudeDo.Data/Models/ModelRegistry.cs | 12 ++++++++++++ .../ViewModels/Islands/DetailsIslandViewModel.cs | 16 +++++++--------- .../Modals/ListSettingsModalViewModel.cs | 14 ++++++-------- .../Settings/GeneralSettingsTabViewModel.cs | 5 +++-- src/ClaudeDo.Worker/Hub/WorkerHub.cs | 2 +- .../Planning/WindowsTerminalPlanningLauncher.cs | 3 ++- 6 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 src/ClaudeDo.Data/Models/ModelRegistry.cs diff --git a/src/ClaudeDo.Data/Models/ModelRegistry.cs b/src/ClaudeDo.Data/Models/ModelRegistry.cs new file mode 100644 index 0000000..81fca7e --- /dev/null +++ b/src/ClaudeDo.Data/Models/ModelRegistry.cs @@ -0,0 +1,12 @@ +namespace ClaudeDo.Data.Models; + +public static class ModelRegistry +{ + public static readonly IReadOnlyList Aliases = new[] { "sonnet", "opus", "haiku" }; + + public const string DefaultAlias = "sonnet"; + public const string PlanningAlias = "opus"; + + public const string ListDefaultSentinel = "(default)"; + public const string TaskInheritSentinel = "(inherit)"; +} diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs index 16f17f1..24fd6fb 100644 --- a/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs @@ -95,7 +95,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase [ObservableProperty] private string? _model; // Agent settings overrides - [ObservableProperty] private string _taskModelSelection = "(inherit)"; + [ObservableProperty] private string _taskModelSelection = ModelRegistry.TaskInheritSentinel; [ObservableProperty] private string _taskSystemPrompt = ""; [ObservableProperty] private AgentInfo? _taskSelectedAgent; @@ -103,10 +103,8 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase [ObservableProperty] private string _effectiveSystemPromptHint = ""; [ObservableProperty] private string _effectiveAgentHint = ""; - public System.Collections.ObjectModel.ObservableCollection TaskModelOptions { get; } = new() - { - "(inherit)", "sonnet", "opus", "haiku", - }; + public System.Collections.ObjectModel.ObservableCollection TaskModelOptions { get; } = new( + new[] { ModelRegistry.TaskInheritSentinel }.Concat(ModelRegistry.Aliases)); public System.Collections.ObjectModel.ObservableCollection TaskAgentOptions { get; } = new(); @@ -365,7 +363,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase await System.Threading.Tasks.Task.Delay(300, ct); if (Task is null) return; - var model = TaskModelSelection == "(inherit)" ? null : TaskModelSelection; + var model = TaskModelSelection == ModelRegistry.TaskInheritSentinel ? null : TaskModelSelection; var sp = string.IsNullOrWhiteSpace(TaskSystemPrompt) ? null : TaskSystemPrompt; var ap = TaskSelectedAgent is null || string.IsNullOrWhiteSpace(TaskSelectedAgent.Path) ? null : TaskSelectedAgent.Path; @@ -384,11 +382,11 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase try { TaskAgentOptions.Clear(); - TaskAgentOptions.Add(new AgentInfo("(inherit)", "", "")); + TaskAgentOptions.Add(new AgentInfo(ModelRegistry.TaskInheritSentinel, "", "")); var agents = await _worker.GetAgentsAsync(); foreach (var a in agents) TaskAgentOptions.Add(a); - TaskModelSelection = string.IsNullOrWhiteSpace(entity.Model) ? "(inherit)" : entity.Model!; + TaskModelSelection = string.IsNullOrWhiteSpace(entity.Model) ? ModelRegistry.TaskInheritSentinel : entity.Model!; TaskSystemPrompt = entity.SystemPrompt ?? ""; TaskSelectedAgent = string.IsNullOrWhiteSpace(entity.AgentPath) ? TaskAgentOptions[0] @@ -439,7 +437,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase _suppressAgentSave = true; try { - TaskModelSelection = "(inherit)"; + TaskModelSelection = ModelRegistry.TaskInheritSentinel; TaskSystemPrompt = ""; TaskSelectedAgent = null; } diff --git a/src/ClaudeDo.Ui/ViewModels/Modals/ListSettingsModalViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Modals/ListSettingsModalViewModel.cs index 6434025..08f4e78 100644 --- a/src/ClaudeDo.Ui/ViewModels/Modals/ListSettingsModalViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Modals/ListSettingsModalViewModel.cs @@ -16,14 +16,12 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase [ObservableProperty] private string _workingDir = ""; [ObservableProperty] private string _defaultCommitType = "chore"; - [ObservableProperty] private string _selectedModel = "(default)"; + [ObservableProperty] private string _selectedModel = ModelRegistry.ListDefaultSentinel; [ObservableProperty] private string _systemPrompt = ""; [ObservableProperty] private AgentInfo? _selectedAgent; - public ObservableCollection ModelOptions { get; } = new() - { - "(default)", "sonnet", "opus", "haiku", - }; + public ObservableCollection ModelOptions { get; } = new( + new[] { ModelRegistry.ListDefaultSentinel }.Concat(ModelRegistry.Aliases)); public ObservableCollection CommitTypeOptions { get; } = new() { @@ -57,7 +55,7 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase foreach (var a in agents) Agents.Add(a); var config = await _worker.GetListConfigAsync(listId); - SelectedModel = string.IsNullOrWhiteSpace(config?.Model) ? "(default)" : config!.Model!; + SelectedModel = string.IsNullOrWhiteSpace(config?.Model) ? ModelRegistry.ListDefaultSentinel : config!.Model!; SystemPrompt = config?.SystemPrompt ?? ""; SelectedAgent = string.IsNullOrWhiteSpace(config?.AgentPath) ? Agents[0] @@ -67,7 +65,7 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase [RelayCommand] private async Task SaveAsync() { - var model = SelectedModel == "(default)" ? null : SelectedModel; + var model = SelectedModel == ModelRegistry.ListDefaultSentinel ? null : SelectedModel; var sp = string.IsNullOrWhiteSpace(SystemPrompt) ? null : SystemPrompt; var ap = SelectedAgent is null || string.IsNullOrWhiteSpace(SelectedAgent.Path) ? null : SelectedAgent.Path; @@ -89,7 +87,7 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase [RelayCommand] private void ResetAgentSettings() { - SelectedModel = "(default)"; + SelectedModel = ModelRegistry.ListDefaultSentinel; SystemPrompt = ""; SelectedAgent = Agents.Count > 0 ? Agents[0] : null; } diff --git a/src/ClaudeDo.Ui/ViewModels/Modals/Settings/GeneralSettingsTabViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Modals/Settings/GeneralSettingsTabViewModel.cs index e9fe848..4507425 100644 --- a/src/ClaudeDo.Ui/ViewModels/Modals/Settings/GeneralSettingsTabViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Modals/Settings/GeneralSettingsTabViewModel.cs @@ -1,3 +1,4 @@ +using ClaudeDo.Data.Models; using CommunityToolkit.Mvvm.ComponentModel; namespace ClaudeDo.Ui.ViewModels.Modals.Settings; @@ -5,11 +6,11 @@ namespace ClaudeDo.Ui.ViewModels.Modals.Settings; public sealed partial class GeneralSettingsTabViewModel : ViewModelBase { [ObservableProperty] private string _defaultClaudeInstructions = ""; - [ObservableProperty] private string _defaultModel = "sonnet"; + [ObservableProperty] private string _defaultModel = ModelRegistry.DefaultAlias; [ObservableProperty] private int _defaultMaxTurns = 100; [ObservableProperty] private string _defaultPermissionMode = "auto"; - public IReadOnlyList Models { get; } = new[] { "opus", "sonnet", "haiku" }; + public IReadOnlyList Models { get; } = ModelRegistry.Aliases; public IReadOnlyList PermissionModes { get; } = new[] { "auto", "bypassPermissions", "acceptEdits", "plan", "default" }; diff --git a/src/ClaudeDo.Worker/Hub/WorkerHub.cs b/src/ClaudeDo.Worker/Hub/WorkerHub.cs index 49b2dcc..c147790 100644 --- a/src/ClaudeDo.Worker/Hub/WorkerHub.cs +++ b/src/ClaudeDo.Worker/Hub/WorkerHub.cs @@ -210,7 +210,7 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub { Id = AppSettingsEntity.SingletonId, DefaultClaudeInstructions = dto.DefaultClaudeInstructions ?? "", - DefaultModel = dto.DefaultModel ?? "sonnet", + DefaultModel = dto.DefaultModel ?? ModelRegistry.DefaultAlias, DefaultMaxTurns = dto.DefaultMaxTurns, DefaultPermissionMode = dto.DefaultPermissionMode ?? "bypassPermissions", WorktreeStrategy = dto.WorktreeStrategy ?? "sibling", diff --git a/src/ClaudeDo.Worker/Planning/WindowsTerminalPlanningLauncher.cs b/src/ClaudeDo.Worker/Planning/WindowsTerminalPlanningLauncher.cs index 5e59ce2..3769b64 100644 --- a/src/ClaudeDo.Worker/Planning/WindowsTerminalPlanningLauncher.cs +++ b/src/ClaudeDo.Worker/Planning/WindowsTerminalPlanningLauncher.cs @@ -7,13 +7,14 @@ // No cmd /k shim — arbitrary initial-prompt content would be re-parsed by cmd.exe otherwise. using System.Diagnostics; +using ClaudeDo.Data.Models; namespace ClaudeDo.Worker.Planning; public sealed class WindowsTerminalPlanningLauncher : IPlanningTerminalLauncher { private const string AllowedTools = "mcp__claudedo__*,Read,Grep,Glob,WebFetch,WebSearch,Skill"; - private const string Model = "claude-opus-4-7"; + private const string Model = ModelRegistry.PlanningAlias; private readonly string _wtPath; private readonly string _claudePath;