diff --git a/README.md b/README.md index 9e92d70..00c571e 100644 --- a/README.md +++ b/README.md @@ -806,6 +806,10 @@ ow + toggle 시각 언어로 통일했습니다. - 이제 `도구` 탭에서는 훅과 도구/커넥터 목록을, `스킬` 탭에서는 스킬 폴더, 슬래시 설정, 드래그 앤 드롭, 로드된 스킬, 폴백 모델, MCP 서버를, `차단` 탭에서는 차단 경로/확장자만 관리합니다. 같이 [SettingsWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml.cs) 에서 메인 설정의 `AX Agent` 바로가기 탭을 좌측 사이드바 맨 아래로 재배치했습니다. - 런처 하단 바도 요소별로 제어할 수 있게 바꿨습니다. [AppSettings.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Models/AppSettings.cs), [SettingsViewModel.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/ViewModels/SettingsViewModel.cs), [SettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml) 에 `성능 / 포모도로 / 메모 / 날씨 / 일정 / 배터리` 하단 위젯 표시 토글을 추가해서 일반 설정에서 항목별로 바로 켜고 끌 수 있게 했습니다. - [LauncherWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/LauncherWindow.xaml), [LauncherWindow.Widgets.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/LauncherWindow.Widgets.cs) 에서는 `Ollama / API / MCP` 서버 상태 위젯을 런처 하단 기능에서 완전히 제거했고, 남은 위젯들만 설정값에 따라 실제 표시되도록 연결했습니다. 배터리 위젯도 노트북 상태와 사용자 토글을 함께 반영해 보이게 정리했습니다. +- `claw-code` 기준으로 계획 UX도 다시 눌렀습니다. [AgentLoopService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentLoopService.cs) 에서 저장된 `PlanMode` 값과 무관하게 런타임 계획 모드를 `off`로 고정해, 코워크/코드에서 매번 계획 승인 팝업이 뜨지 않도록 바꿨습니다. +- [SettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml), [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml) 에서는 메인 설정과 AX Agent 내부 설정의 `계획 모드` 행을 숨겼고, [SettingsViewModel.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/ViewModels/SettingsViewModel.cs), [AppStateService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/AppStateService.cs) 에서도 항상 `off`만 저장/반영되게 정리했습니다. +- 계획 확인 팝업은 [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs), [PlanViewerWindow.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/PlanViewerWindow.cs) 기준으로 AX Agent 창을 owner로 받아 리소스를 그대로 합치게 바꿨고, 채팅 본문에 별도 인라인 승인 버튼을 다시 꽂지 않도록 정리했습니다. +- 업데이트: 2026-04-05 16:20 (KST) - 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\` 경고 0 / 오류 0 - 업데이트: 2026-04-05 15:16 (KST) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 2ad432a..b3e905e 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -4570,3 +4570,8 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎. - 배터리 위젯은 사용자 토글과 실제 배터리 가용 상태를 함께 반영하도록 `UpdateWidgetVisibility()`에서 최종 가시성을 결정하게 바꿨고, 모든 위젯이 꺼져 있으면 하단 위젯 바 전체도 자동으로 숨깁니다. - 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\` 경고 0 / 오류 0 - 업데이트: 2026-04-05 15:16 (KST) +- `claw-code` 기준 계획 UX 정리도 반영했습니다. [AgentLoopService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentLoopService.cs) 의 `ResolveEffectivePlanMode(...)` 는 이제 항상 `off`를 반환해, 저장된 예전 계획 모드 값이 남아 있어도 런타임에서는 자동 계획/자동 승인 팝업이 다시 살아나지 않게 했습니다. +- [SettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml) 과 [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml) 에서 메인 설정과 AX Agent 내부 설정의 `계획 모드` UI를 `Collapsed`로 전환했고, [SettingsViewModel.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/ViewModels/SettingsViewModel.cs), [AppStateService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/AppStateService.cs) 도 `off` 고정으로 바꿔 예전 persisted 값이 다시 UI나 상태바에 반영되지 않게 정리했습니다. +- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 의 `CreatePlanDecisionCallback()`은 더 이상 채팅 본문에 인라인 승인 버튼을 추가하지 않고, [PlanViewerWindow.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/PlanViewerWindow.cs) 를 AX Agent 창 owner와 merged resources 기준으로 생성해 플랜 팝업이 AX Agent 테마와 같은 리소스 축을 따르도록 바꿨습니다. +- 검증 예정: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\` +- 업데이트: 2026-04-05 16:20 (KST) diff --git a/src/AxCopilot/Services/Agent/AgentLoopService.cs b/src/AxCopilot/Services/Agent/AgentLoopService.cs index 94b0bea..3b45347 100644 --- a/src/AxCopilot/Services/Agent/AgentLoopService.cs +++ b/src/AxCopilot/Services/Agent/AgentLoopService.cs @@ -2098,20 +2098,7 @@ public partial class AgentLoopService internal static string ResolveEffectivePlanMode(string? configuredPlanMode, string? activeTab, string? taskType) { - var normalizedPlanMode = (configuredPlanMode ?? "off").Trim().ToLowerInvariant(); - if (normalizedPlanMode is not ("off" or "auto" or "always")) - normalizedPlanMode = "off"; - - if (string.Equals(activeTab, "Cowork", StringComparison.OrdinalIgnoreCase) - && string.Equals(taskType, "docs", StringComparison.OrdinalIgnoreCase) - && normalizedPlanMode == "off") - { - // claw-code 레퍼런스처럼 문서형 코워크는 실행 전에 구조를 먼저 세우고, - // 그 계획을 바탕으로 실제 산출물 생성 단계까지 이어가도록 기본값을 보정합니다. - return "always"; - } - - return normalizedPlanMode; + return "off"; } private static void InjectTaskTypeGuidance(List messages, TaskTypePolicy taskPolicy) diff --git a/src/AxCopilot/Services/AppStateService.cs b/src/AxCopilot/Services/AppStateService.cs index 91c28c3..89e199d 100644 --- a/src/AxCopilot/Services/AppStateService.cs +++ b/src/AxCopilot/Services/AppStateService.cs @@ -196,7 +196,7 @@ public sealed class AppStateService Permissions.FilePermission = PermissionModeCatalog.NormalizeGlobalMode(llm.FilePermission); Permissions.AgentDecisionLevel = llm.AgentDecisionLevel ?? "detailed"; - Permissions.PlanMode = llm.PlanMode ?? "off"; + Permissions.PlanMode = "off"; Permissions.ToolOverrideCount = llm.ToolPermissions?.Count ?? 0; Permissions.ToolOverrides = llm.ToolPermissions? .OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase) diff --git a/src/AxCopilot/ViewModels/SettingsViewModel.cs b/src/AxCopilot/ViewModels/SettingsViewModel.cs index 0af6500..be17317 100644 --- a/src/AxCopilot/ViewModels/SettingsViewModel.cs +++ b/src/AxCopilot/ViewModels/SettingsViewModel.cs @@ -370,11 +370,13 @@ public class SettingsViewModel : INotifyPropertyChanged set { _agentDecisionLevel = value; OnPropertyChanged(); } } - private string _planMode = "off"; public string PlanMode { - get => _planMode; - set { _planMode = value; OnPropertyChanged(); } + get => "off"; + set + { + OnPropertyChanged(); + } } private bool _enableMultiPassDocument; @@ -1138,7 +1140,6 @@ public class SettingsViewModel : INotifyPropertyChanged _planDiffSeverityMediumRatioPercent = llm.PlanDiffSeverityMediumRatioPercent > 0 ? Math.Clamp(llm.PlanDiffSeverityMediumRatioPercent, 1, 100) : 25; _planDiffSeverityHighRatioPercent = llm.PlanDiffSeverityHighRatioPercent > 0 ? Math.Clamp(llm.PlanDiffSeverityHighRatioPercent, 1, 100) : 60; _agentDecisionLevel = llm.AgentDecisionLevel; - _planMode = string.IsNullOrEmpty(llm.PlanMode) ? "off" : llm.PlanMode; _enableMultiPassDocument = llm.EnableMultiPassDocument; _enableCoworkVerification = llm.EnableCoworkVerification; _enableFilePathHighlight = llm.EnableFilePathHighlight; @@ -1580,7 +1581,7 @@ public class SettingsViewModel : INotifyPropertyChanged s.Llm.PlanDiffSeverityMediumRatioPercent = _planDiffSeverityMediumRatioPercent; s.Llm.PlanDiffSeverityHighRatioPercent = _planDiffSeverityHighRatioPercent; s.Llm.AgentDecisionLevel = _agentDecisionLevel; - s.Llm.PlanMode = _planMode; + s.Llm.PlanMode = "off"; s.Llm.EnableMultiPassDocument = _enableMultiPassDocument; s.Llm.EnableCoworkVerification = _enableCoworkVerification; s.Llm.EnableFilePathHighlight = _enableFilePathHighlight; diff --git a/src/AxCopilot/Views/ChatWindow.xaml b/src/AxCopilot/Views/ChatWindow.xaml index 30f43ad..c278bcb 100644 --- a/src/AxCopilot/Views/ChatWindow.xaml +++ b/src/AxCopilot/Views/ChatWindow.xaml @@ -2880,7 +2880,7 @@ - + diff --git a/src/AxCopilot/Views/ChatWindow.xaml.cs b/src/AxCopilot/Views/ChatWindow.xaml.cs index 8736273..576c625 100644 --- a/src/AxCopilot/Views/ChatWindow.xaml.cs +++ b/src/AxCopilot/Views/ChatWindow.xaml.cs @@ -8735,7 +8735,8 @@ public partial class ChatWindow : Window sb.AppendLine("You are AX Copilot Agent. You can read, write, and edit files using the provided tools."); sb.AppendLine($"Today's date: {DateTime.Now:yyyy년 M월 d일} ({DateTime.Now:yyyy-MM-dd}, {DateTime.Now:dddd})."); sb.AppendLine("Available skills: excel_create (.xlsx), docx_create (.docx), csv_create (.csv), markdown_create (.md), html_create (.html), script_create (.bat/.ps1), document_review (품질 검증), format_convert (포맷 변환)."); - sb.AppendLine("Always produce a concrete execution plan before major Cowork tasks. For document/report/proposal/manual requests, create a 3-7 step plan first, then execute it."); + sb.AppendLine("Only present a step-by-step execution plan when the user explicitly asks for a plan or when the session is already in plan mode."); + sb.AppendLine("For ordinary Cowork requests, proceed directly with the work instead of stopping for plan approval."); sb.AppendLine("After creating files, summarize what was created and include the actual output path."); sb.AppendLine("Do not stop after a single step. Continue autonomously until the request is completed or a concrete blocker (permission denial, missing dependency, hard error) is encountered."); sb.AppendLine("When adapting external references, rewrite names/structure/comments to AX Copilot style. Avoid clone-like outputs."); @@ -9858,7 +9859,7 @@ public partial class ChatWindow : Window // PlanViewerWindow 생성 또는 재사용 if (_planViewerWindow == null || !IsWindowAlive(_planViewerWindow)) { - _planViewerWindow = new PlanViewerWindow(); + _planViewerWindow = new PlanViewerWindow(this); _planViewerWindow.Closing += (_, e) => { e.Cancel = true; @@ -9869,9 +9870,6 @@ public partial class ChatWindow : Window // 계획 표시 + 승인 대기 _planViewerWindow.ShowPlanAsync(planSummary, steps, tcs); - // 채팅 창에 간략 배너 추가 + 인라인 승인 버튼도 표시 - AddDecisionButtons(tcs, options); - // 하단 바 계획 버튼 표시 ShowPlanButton(true); }); @@ -14091,7 +14089,7 @@ public partial class ChatWindow : Window BtnInlineFastMode.Content = GetQuickActionLabel("Fast", llm.FreeTierMode ? "켜짐" : "꺼짐"); BtnInlineReasoning.Content = GetQuickActionLabel("추론", ReasoningLabel(llm.AgentDecisionLevel)); - BtnInlinePlanMode.Content = GetQuickActionLabel("계획", PlanModeLabel(llm.PlanMode)); + BtnInlinePlanMode.Content = GetQuickActionLabel("계획", PlanModeLabel("off")); BtnInlinePermission.Content = GetQuickActionLabel("권한", PermissionModeCatalog.ToDisplayLabel(llm.FilePermission)); BtnInlineSkill.Content = $"스킬 · {(llm.EnableSkillSystem ? "On" : "Off")}"; BtnInlineCommandBrowser.Content = "명령/스킬 브라우저"; @@ -14102,7 +14100,7 @@ public partial class ChatWindow : Window ApplyQuickActionVisual(BtnInlineFastMode, llm.FreeTierMode, "#ECFDF5", "#166534"); ApplyQuickActionVisual(BtnInlineReasoning, !string.Equals(llm.AgentDecisionLevel, "normal", StringComparison.OrdinalIgnoreCase), "#EEF2FF", "#1D4ED8"); - ApplyQuickActionVisual(BtnInlinePlanMode, !string.Equals(llm.PlanMode, "off", StringComparison.OrdinalIgnoreCase), "#EEF2FF", "#4338CA"); + ApplyQuickActionVisual(BtnInlinePlanMode, false, "#EEF2FF", "#4338CA"); ApplyQuickActionVisual(BtnInlinePermission, !string.Equals(PermissionModeCatalog.NormalizeGlobalMode(llm.FilePermission), PermissionModeCatalog.Deny, StringComparison.OrdinalIgnoreCase), "#FFF7ED", @@ -16337,7 +16335,7 @@ public partial class ChatWindow : Window SelectComboTag(CmbOverlayOperationMode, OperationModePolicy.Normalize(_settings.Settings.OperationMode)); SelectComboTag(CmbOverlayFolderDataUsage, _folderDataUsage); SelectComboTag(CmbOverlayPermission, PermissionModeCatalog.NormalizeGlobalMode(llm.FilePermission)); - SelectComboTag(CmbOverlayPlanMode, llm.PlanMode); + SelectComboTag(CmbOverlayPlanMode, "off"); SelectComboTag(CmbOverlayReasoning, llm.AgentDecisionLevel); SelectComboTag(CmbOverlayFastMode, llm.FreeTierMode ? "on" : "off"); SelectComboTag(CmbOverlayDefaultOutputFormat, llm.DefaultOutputFormat ?? "auto"); @@ -16835,10 +16833,10 @@ public partial class ChatWindow : Window private void CmbOverlayPlanMode_SelectionChanged(object sender, SelectionChangedEventArgs e) { - if (_isOverlaySettingsSyncing || CmbOverlayPlanMode.SelectedItem is not ComboBoxItem selected || selected.Tag is not string tag) + if (_isOverlaySettingsSyncing) return; - _settings.Settings.Llm.PlanMode = tag; + _settings.Settings.Llm.PlanMode = "off"; PersistOverlaySettingsState(refreshOverlayDeferredInputs: false); } @@ -17051,7 +17049,7 @@ public partial class ChatWindow : Window private void BtnInlinePlanMode_Click(object sender, RoutedEventArgs e) { var llm = _settings.Settings.Llm; - llm.PlanMode = NextPlanMode(llm.PlanMode); + llm.PlanMode = "off"; _settings.Save(); _appState.LoadFromSettings(_settings); RefreshInlineSettingsPanel(); diff --git a/src/AxCopilot/Views/PlanViewerWindow.cs b/src/AxCopilot/Views/PlanViewerWindow.cs index 1f8cbcb..dcc023c 100644 --- a/src/AxCopilot/Views/PlanViewerWindow.cs +++ b/src/AxCopilot/Views/PlanViewerWindow.cs @@ -49,28 +49,37 @@ internal sealed class PlanViewerWindow : Window private bool _isExecuting; private readonly string _uiExpressionLevel; - public PlanViewerWindow() + public PlanViewerWindow(Window? owner = null) { + if (owner != null) + { + Owner = owner; + WindowStartupLocation = WindowStartupLocation.CenterOwner; + Resources.MergedDictionaries.Add(owner.Resources); + } + _uiExpressionLevel = ResolveUiExpressionLevel(); Width = 640; Height = 520; MinWidth = 480; MinHeight = 360; - WindowStartupLocation = WindowStartupLocation.CenterScreen; + WindowStartupLocation = owner == null + ? WindowStartupLocation.CenterScreen + : WindowStartupLocation.CenterOwner; WindowStyle = WindowStyle.None; AllowsTransparency = true; Background = Brushes.Transparent; ResizeMode = ResizeMode.CanResize; // WndProc로 직접 처리 ShowInTaskbar = false; - var bgBrush = Application.Current.TryFindResource("LauncherBackground") as Brush + var bgBrush = TryFindResource("LauncherBackground") as Brush ?? new SolidColorBrush(Color.FromRgb(0x1A, 0x1B, 0x2E)); - var primaryText = Application.Current.TryFindResource("PrimaryText") as Brush ?? Brushes.White; - var secondaryText = Application.Current.TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; - var accentBrush = Application.Current.TryFindResource("AccentColor") as Brush + var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White; + var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray; + var accentBrush = TryFindResource("AccentColor") as Brush ?? new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC)); - var borderBrush = Application.Current.TryFindResource("BorderColor") as Brush ?? Brushes.Gray; + var borderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray; var root = new Border { @@ -151,7 +160,7 @@ internal sealed class PlanViewerWindow : Window mainGrid.Children.Add(_statusBar); // ── 툴바: 모두 열기 / 모두 닫기 ── - var hoverBgTb = Application.Current.TryFindResource("ItemHoverBackground") as Brush + var hoverBgTb = TryFindResource("ItemHoverBackground") as Brush ?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF)); var toolBar = new StackPanel { diff --git a/src/AxCopilot/Views/SettingsWindow.xaml b/src/AxCopilot/Views/SettingsWindow.xaml index 42a27a9..fe06430 100644 --- a/src/AxCopilot/Views/SettingsWindow.xaml +++ b/src/AxCopilot/Views/SettingsWindow.xaml @@ -4653,7 +4653,7 @@ - +