# AX Copilot - 개발 문서 > 최종 업데이트: 2026-04-09 · 버전 0.7.3 --- ## 1. 프로젝트 개요 AX Copilot은 Windows용 생산성 런처 + AI 에이전트 데스크톱 앱입니다. - **런처**: Alfred/Raycast 스타일의 퍼지 검색, 명령 실행, 위젯 - **에이전트**: LLM 기반 대화형 코드/문서 작업 자동화 (도구 호출 루프) - **독 바**: 시스템 리소스, 클립보드, 스크린샷 등 빠른 접근 --- ## 2. 기술 스택 | 항목 | 값 | |------|-----| | 프레임워크 | .NET 8 (net8.0-windows10.0.17763.0) | | UI | WPF + Windows Forms (하이브리드) | | 언어 | C# 12 | | 패턴 | MVVM, 이벤트 기반, 싱글톤 서비스 | | 테스트 | xUnit 2.9 + FluentAssertions 6.12 | | 빌드 | dotnet CLI, PublishSingleFile | ### 주요 NuGet 패키지 | 패키지 | 용도 | |--------|------| | DocumentFormat.OpenXml 3.2.0 | DOCX/XLSX/PPTX 생성 | | Markdig 0.37.0 | Markdown → HTML 렌더링 | | Microsoft.Data.Sqlite 8.0 | SQLite (대화 저장소) | | Microsoft.Web.WebView2 | HTML 미리보기, 가이드 뷰어 | | QRCoder 1.6.0 | QR 코드 생성 | | System.Security.Cryptography.ProtectedData | DPAPI 암호화 | | UglyToad.PdfPig | PDF 읽기 | --- ## 3. 솔루션 구조 ``` src/ ├── AxCopilot/ # 메인 WPF 앱 (v0.7.3) │ ├── Assets/ # 아이콘, 프리셋 JSON, 암호화된 가이드, 마스코트 │ ├── Core/ # FuzzyEngine, CommandResolver, InputListener, PluginHost │ ├── Handlers/ # 136개 빌트인 명령 핸들러 │ ├── Models/ # AppSettings, ChatModels, McpSettings │ ├── Security/ # AntiTamper (디버거/디컴파일러 탐지) │ ├── Services/ # 60개 서비스 │ │ └── Agent/ # 에이전트 루프 + 114개 도구 │ ├── Themes/ # 9개 테마 (Dark, Light, OLED, Nord, Monokai 등) │ ├── ViewModels/ # LauncherViewModel, SettingsViewModel, StatisticsViewModel │ └── Views/ # 30개 XAML 윈도우 ├── AxCopilot.SDK/ # 플러그인 SDK (IActionHandler 인터페이스) ├── AxCopilot.Installer/ # Windows Forms 설치 프로그램 (.NET Framework 4.8) ├── AxCopilot.Tests/ # xUnit 단위/통합 테스트 └── AxKeyEncryptor/ # API 키 DPAPI 암호화 유틸리티 ``` --- ## 4. 앱 시작 흐름 (App.xaml.cs) ``` OnStartup() ├─ AntiTamper 디버거 감지 (Release 빌드) ├─ 단일 인스턴스 뮤텍스 확인 ├─ SettingsService 초기화 + 설정 로드 ├─ ChatStorageService 보관 정책 실행 (만료 대화 정리) ├─ L10n 언어 초기화 ├─ 서비스 초기화 │ ├─ AgentMemoryService │ ├─ ChatSessionStateService │ ├─ AppStateService │ ├─ IndexService (백그라운드 파일 인덱싱) │ ├─ FuzzyEngine + CommandResolver │ ├─ ContextManager │ ├─ SessionTrackingService │ ├─ WorktimeReminderService │ └─ ClipboardHistoryService ├─ 빌트인 핸들러 등록 (136개) ├─ SchedulerService + PluginHost 초기화 ├─ InputListener 시작 (글로벌 핫키) └─ 런처/설정/트레이 윈도우 생성 ``` --- ## 5. 핵심 아키텍처 ### 5.1 런처 (Launcher) **검색 파이프라인**: 사용자 입력 → `CommandResolver` (접두어 매칭) → `FuzzyEngine` (퍼지 검색) → 결과 정렬 → UI 렌더링 - `FuzzyEngine`: 파일 인덱스 기반 퍼지 매칭, 점수 순위 - `CommandResolver`: 핸들러 라우팅 (접두어 `@`, `!`, `#`, `~`, `>`, `$` 등) - `IndexService`: 백그라운드 파일 인덱싱 (`.git`, `node_modules` 등 제외) **위젯**: 성능 모니터, 포모도로, 메모, 날씨, 캘린더, 배터리 ### 5.2 에이전트 (Agent Loop) ``` 사용자 메시지 → LlmService.StreamAsync() (LLM API 호출) → 응답 스트리밍 수신 → 도구 호출 감지 시: → ToolRegistry에서 도구 조회 → 권한 확인 (AskPermissionCallback) → 도구 실행 → 결과를 컨텍스트에 추가 → LLM 재호출 (반복) → 최종 텍스트 응답 반환 ``` **핵심 클래스**: - `AgentLoopService` — 루프 엔진 (반복, 일시정지/재개, 이벤트 발행) - `AxAgentExecutionEngine` — 도구 실행 조율 - `AgentLoopParallelExecution` — 병렬 도구 실행 - `AgentLoopTransitions` / `.Execution` — 상태 전이 로직 - `ToolRegistry` — 도구 등록/조회 - `ContextCondenser` — 컨텍스트 압축 (토큰 관리) **도구 카테고리** (114개): | 카테고리 | 예시 | |---------|------| | 파일 I/O | FileReadTool, FileEditTool, FileManageTool, FileWriteTool | | 검색 | GlobTool, GrepTool, CodeSearchTool, FileSearchTool | | 문서 | DocumentReaderTool, ExcelSkill, DocxSkill, PptxSkill, CsvSkill, HtmlSkill | | 코드 | BuildRunTool, SnippetRunnerTool, CodeReviewTool, TestLoopTool, LspTool | | 데이터 | JsonTool, XmlTool, SqlTool, DataPivotTool, RegexTool | | 시스템 | ProcessTool, EnvTool, ZipTool, ClipboardTool | | 계획/추적 | TodoWriteTool, TaskTrackerTool, CheckpointTool, PlaybookTool | | 사용자 | UserAskTool, SuggestActionsTool, NotifyTool | | MCP | McpTool, McpListResourcesTool, McpReadResourceTool | ### 5.3 LLM 서비스 **지원 공급자**: | 서비스 | 설명 | |--------|------| | `claude` / `sigmoid` | Anthropic Claude (Sigmoid API 경유) | | `gemini` | Google Gemini API | | `vllm` | OpenAI 호환 vLLM (IBM CP4D 지원 포함) | | `ollama` | 로컬 Ollama 모델 | **모델 라우팅**: `ModelRouterService`를 통한 오버라이드 스택 — 대화 중 모델/서비스를 동적으로 전환 가능 **토큰 관리**: `TokenEstimator`로 컨텍스트 길이 추정, 오버플로우 시 `ContextCondenser`가 자동 압축 ### 5.4 대화 저장소 - `ChatStorageService`: SQLite 기반 대화 영속화 - `ChatSessionStateService`: 메모리 내 세션 상태 관리 - `ChatConversation`: 메시지 목록 + 실행 이벤트 타임라인 --- ## 6. UI 계층 ### 주요 윈도우 | 윈도우 | 역할 | |--------|------| | `LauncherWindow` | 메인 런처 (검색, 위젯, 결과 목록) | | `ChatWindow` | AI 에이전트 대화 (채팅/Cowork/코드 탭) | | `DockBarWindow` | 독 바 (시스템 리소스, 빠른 접근) | | `SettingsWindow` | 설정 관리 | | `AgentSettingsWindow` | 에이전트 전용 설정 | | `AgentStatsDashboardWindow` | 에이전트 통계 대시보드 | | `SkillEditorWindow` | 스킬 편집기 | | `SkillGalleryWindow` | 스킬 갤러리 | | `TrayMenuWindow` | 시스템 트레이 메뉴 | | `PreviewWindow` | 문서 미리보기 (WebView2) | ### ChatWindow 분할 구조 `ChatWindow.xaml.cs`는 partial class로 기능별 분할: | 파일 | 역할 | |------|------| | `ChatWindow.xaml.cs` | 메인 오케스트레이션, 스트리밍, 입력 처리 | | `ChatWindow.AgentEventProcessor.cs` | 에이전트 이벤트 수신/라우팅 | | `ChatWindow.AgentEventRendering.cs` | 에이전트 이벤트 배너/카드 렌더링 | | `ChatWindow.ComposerQueuePresentation.cs` | 작성기 큐 UI | | `ChatWindow.ContextUsagePresentation.cs` | 컨텍스트 사용량 링/팝업 | | `ChatWindow.ConversationFilterPresentation.cs` | 대화 필터링 | | `ChatWindow.ConversationListPresentation.cs` | 사이드바 대화 목록 | | `ChatWindow.ConversationManagementPresentation.cs` | 대화 생성/삭제/관리 | | `ChatWindow.FileBrowserPresentation.cs` | 파일 브라우저 UI | | `ChatWindow.FooterPresentation.cs` | 하단 바 (폴더, 권한) | | `ChatWindow.GitBranchPresentation.cs` | Git 브랜치 표시/전환 | | `ChatWindow.LiveProgressPresentation.cs` | 실시간 진행 상태 | | `ChatWindow.MessageBubblePresentation.cs` | 메시지 버블 렌더링 | | `ChatWindow.MessageInteractions.cs` | 메시지 복사/편집/재전송 | | `ChatWindow.PermissionPresentation.cs` | 권한 팝업/배너 UI | | `ChatWindow.PlanApprovalPresentation.cs` | 계획 승인 카드 | | `ChatWindow.PopupPresentation.cs` | 공통 팝업 구성 | | `ChatWindow.PreviewPresentation.cs` | 파일 미리보기 탭 | | `ChatWindow.SelectionPopupPresentation.cs` | 워크트리 선택 팝업 | | `ChatWindow.SidebarInteractionPresentation.cs` | 사이드바 상호작용 | | `ChatWindow.StatusPresentation.cs` | 상태 배지/스트립 | | `ChatWindow.SurfaceVisualPresentation.cs` | 시각 효과 (글로우, 펄스 등) | | `ChatWindow.TaskSummary.cs` | 작업 요약 카드 | | `ChatWindow.TimelinePresentation.cs` | 타임라인 정렬, 캐시, 이벤트 필터링 | | `ChatWindow.TopicPresetPresentation.cs` | 주제 프리셋 UI | | `ChatWindow.TranscriptHost.cs` | 트랜스크립트 호스트 컨테이너 | | `ChatWindow.TranscriptPolicy.cs` | 트랜스크립트 표시 정책 | | `ChatWindow.TranscriptRenderExecution.cs` | 트랜스크립트 렌더 실행 | | `ChatWindow.TranscriptRenderPlanner.cs` | 트랜스크립트 렌더 계획 | | `ChatWindow.TranscriptRendering.cs` | 트랜스크립트 렌더링 | | `ChatWindow.TranscriptVirtualization.cs` | 트랜스크립트 가상화 (대규모 대화) | | `ChatWindow.UserAskPresentation.cs` | 사용자 질문 인라인 카드 | | `ChatWindow.VisualInteractionHelpers.cs` | 시각 상호작용 헬퍼 | ### 테마 시스템 9개 테마 XAML 리소스 딕셔너리: `Dark`, `Light`, `OLED`, `Nord`, `Monokai`, `Catppuccin`, `Sepia`, `Alfred`, `AlfredLight` 런타임 테마 전환: `SettingsService.Settings.Launcher.Theme` 변경 → 리소스 딕셔너리 교체 --- ## 7. 설정 구조 (AppSettings) ### 최상위 설정 | 속성 | 기본값 | 설명 | |------|--------|------| | `AiEnabled` | true | AI 기능 활성화 | | `OperationMode` | "internal" | 운영 모드 (internal/external) | | `Hotkey` | "Alt+Space" | 런처 단축키 | | `CleanupPeriodDays` | 30 | 대화 보관 기간 (일) | | `InternalModeEnabled` | true | 사내 모드 여부 | ### LauncherSettings (중첩) | 그룹 | 주요 속성 | |------|----------| | 표시 | `Theme`, `Opacity`, `Position`, `Width`, `MaxResults` | | 글로우 | `EnableRainbowGlow`, `EnableSelectionGlow`, `ShowLauncherBorder` | | 위젯 | `ShowWidgetPerf`, `ShowWidgetPomo`, `ShowWidgetNote`, `ShowWidgetWeather`, `ShowWidgetCalendar`, `ShowWidgetBattery` | | 독 바 | `DockBarItems`, `DockBarAutoShow`, `DockBarOpacity`, `DockBarRainbowGlow` | | 기능 | `EnableFavorites`, `EnableRecent`, `EnableActionMode`, `EnableClipboardAutoCategory` | ### LlmSettings (중첩) 에이전트의 LLM 연결 설정: 서비스 선택, 모델, API 키 (DPAPI 암호화), 엔드포인트, 온도, 최대 토큰 등 | 속성 | 기본값 | 설명 | |------|--------|------| | `UseAutomaticProfileTemperature` | true | 등록 모델 프로파일의 자동 temperature 정책 | | `EnableDetailedLog` | false | 워크플로우 상세 로그 (LLM 요청/응답, 도구 이력) | | `DetailedLogRetentionDays` | 3 | 상세 로그 보관 기간 (일) | | `EnableRawLlmLog` | false | LLM 요청/응답 원문 기록 (디버깅용) | ### RegisteredModel 실행 프로파일 모델별 `ExecutionProfile`로 도구 호출 강도, 재시도, 메모리/압축 주입량을 조절: | 프로파일 | 설명 | |---------|------| | `balanced` | 기본 균형 모드 | | `tool_call_strict` | 도구 호출 강제/엄격 모드 | | `reasoning_first` | 추론 우선 모드 | | `fast_readonly` | 빠른 읽기 전용 모드 | | `document_heavy` | 문서 처리 집중 모드 | --- ## 8. 플러그인 시스템 ### SDK (AxCopilot.SDK) ```csharp public interface IActionHandler { string? Prefix { get; } // 접두어 (null이면 퍼지 검색만) PluginMetadata Metadata { get; } Task> GetItemsAsync(string query, CancellationToken ct); Task ExecuteAsync(LauncherItem item, CancellationToken ct); } ``` ### 개발 방법 1. `AxCopilot.SDK` 참조하여 `IActionHandler` 구현 2. 빌드된 `.dll`을 `settings.json`의 `Plugins` 배열에 경로 등록 3. `PluginHost`가 앱 시작 시 동적 로드 --- ## 9. 빌드 및 실행 ### 개발 빌드 ```bash dotnet build src/AxCopilot/AxCopilot.csproj ``` ### 릴리스 빌드 (단일 파일) ```bash dotnet publish src/AxCopilot/AxCopilot.csproj -c Release -r win-x64 --self-contained ``` 릴리스 빌드 옵션: - `PublishSingleFile`: 단일 실행 파일 - `EnableCompressionInSingleFile`: 압축 적용 - `PublishReadyToRun`: AOT 프리컴파일 - `DebugType=none`: 디버그 심볼 제거 - `TrimMode=partial`: IL 트리밍 ### 테스트 ```bash dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj ``` --- ## 10. 버전 관리 - `AxCopilot.csproj`의 `` 태그 하나만 변경하면 앱 전체에 반영 - 설정 스키마 버전은 `SettingsService.cs` → `CurrentSettingsVersion`에서 별도 관리 - 마이그레이션: `SettingsService`가 이전 버전 설정 파일을 자동 업그레이드 --- ## 11. 보안 | 항목 | 구현 | |------|------| | API 키 저장 | DPAPI 암호화 (System.Security.Cryptography.ProtectedData) | | 키 관리 도구 | AxKeyEncryptor (별도 유틸리티) | | 안티 탬퍼 | 디버거/디컴파일러 감지 (Release 빌드, `Security/AntiTamper.cs`) | | Unsafe 코드 | `AllowUnsafeBlocks=true` (ScreenCaptureHandler 포인터 연산용) | --- ## 12. 성능 최적화 내역 ### 유휴 CPU 최적화 (2026-04-09) | 대상 | 변경 전 | 변경 후 | |------|---------|---------| | PerformanceMonitorService 폴링 | 2초 | 5초 | | 위젯 타이머 | 1초 | 3초 | | 레인보우 글로우 타이머 | 150ms | 300ms | | ServerStatusService 핑 | 15초 | 60초 | ### 스트리밍 렌더링 최적화 (2026-04-09) - **TypingTimer**: 50ms → 80ms, `string.Concat` → `char[]` 버퍼 재사용 - **CursorTimer**: 전체 문자열 재생성 → 마지막 문자만 교체 - **StringBuilder.ToString()**: 30ms 최소 간격 쓰로틀링 - **RenderMessages**: 스트리밍 중 불필요한 전체 재렌더링 방지 (조기 반환) - **타임라인 이벤트**: 접힌 모드에서 연속 동일 ToolCall 병합 ### 런타임 안정성 수정 (2026-04-09) | 파일 | 수정 내용 | |------|----------| | `CsvSkill.cs` | JSON 배열 첫 요소 `ValueKind` 검증 추가 | | `HtmlSkill.cs` | gradient `Split(',')` 결과 `Length >= 2` 가드 추가 | | `ChatWindow.xaml.cs` | `ParseGenericAction` 빈 배열 가드, `ShowDropActionMenu` null 가드, `GetAgentLoop` `.FirstOrDefault()` 전환 | | `ChatWindow.GitBranchPresentation.cs` | async void 핸들러 try/catch 보호 | | `ChatWindow.xaml.cs` (BtnGitBranch_Click) | async void 핸들러 try/catch 보호 | ### UI 스레드 부하 최적화 2차 (2026-04-09) | 대상 | 변경 전 | 변경 후 | 효과 | |------|---------|---------|------| | 스크롤 애니메이션 | 매번 새 16ms 타이머 생성 | 재사용 32ms 타이머 1개 | GC 압력 + 타이머 누적 해소 | | 사이드바 애니메이션 | 매번 새 10ms 타이머 생성 | 재사용 32ms 타이머 1개 | 동일 | | Git 브랜치 UI | `Dispatcher.Invoke` (블로킹) | `Dispatcher.InvokeAsync` (논블로킹) | UI 스레드 차단 해소 | | 토큰 사용량 호 | 매 250ms PathGeometry 재생성 | 1% 미만 변화 시 렌더링 생략 | 불필요한 레이아웃 연산 제거 | | 대화 검색 타이머 | 140ms | 300ms | 초당 7회 → 3회 | | 에이전트 이벤트 타이머 | 140ms (스트리밍: 300/420) | 200ms (스트리밍: 350/500) | 이벤트 처리 빈도 완화 | | 반응형 레이아웃 타이머 | 120ms | 250ms | 리사이즈 디바운스 강화 | | 대화 목록 LINQ | Where×2 + Count×3 = 리스트 5회 순회 | Where 1회 병합 + 단일 루프 카운트 | 할당/순회 대폭 감소 | ### 구조적 메모리/안정성 수정 (2026-04-09) | 문제 | 위치 | 수정 | |------|------|------| | Events 컬렉션 무한 성장 | `AgentLoopService.cs` | 500개 초과 시 오래된 이벤트 자동 제거 | | 파일 브라우저 타이머 좀비 | `ChatWindow.FileBrowserPresentation.cs` | 매번 새 타이머 생성 → 재사용 패턴 | | 엘리먼트 캐시 미정리 | `ChatWindow.TranscriptVirtualization.cs` | 보유 한도 240→120, 1.5배 초과 시 정리 | | WorkflowAnalyzer UI 블로킹 | `WorkflowAnalyzerWindow.xaml.cs` | `Dispatcher.Invoke` → `InvokeAsync` | ### 구조적 리팩토링 P1 (2026-04-09) | 대상 | 파일 | 변경 | |------|------|------| | 인크리멘탈 렌더 hiddenCount 안정화 | `ChatWindow.TranscriptRenderPlanner.cs` | 스트리밍 중 hiddenCount 감소 차단 → prefix 키 불일치로 인한 전체 재빌드 폴백 방지 | | 비가시 렌더 차단 | `ChatWindow.TranscriptRendering.cs` | 최소화/숨김 상태에서 RenderMessages 즉시 반환 → 불필요한 UI 재구축 제거 | | ConversationList 이벤트 위임 | `ChatWindow.ConversationListPresentation.cs` | 항목당 5개 람다 핸들러 → ConversationPanel에 단일 위임 핸들러 (Tag 기반 분기). 탭 전환 시 250개 핸들러 누적 해소 | | TopicPreset 이벤트 위임 | `ChatWindow.TopicPresetPresentation.cs` | 카드당 3개 람다 핸들러 → TopicButtonPanel에 단일 위임 핸들러. 탭 전환 시 45개 핸들러 누적 해소 | | 공통 VisualTree 헬퍼 | `ChatWindow.VisualInteractionHelpers.cs` | `FindAncestorWithTag`, `FindAncestor` 유틸 추가 | ### 구조적 리팩토링 P2 (2026-04-09) | 대상 | 파일 | 변경 | |------|------|------| | _agentLiveContainer 인크리멘탈 허용 | `TranscriptRenderPlanner.cs`, `TranscriptRenderExecution.cs` | 라이브 컨테이너를 expectedChildCount에 포함, 인크리멘탈 시 임시 분리/재삽입 → `hasExternalChildren` 차단 해소 | | 스트리밍 append-only 렌더 | `TranscriptRenderExecution.cs`, `TranscriptRendering.cs` | prefix 비교 우회하는 `TryApplyStreamingAppendRender` 추가 — stable 키 부분집합 관계만 확인, 새 항목만 추가 | | Permission 이벤트 위임 | `ChatWindow.PermissionPresentation.cs` | 행당 4개 람다 → PermissionItems에 단일 위임 핸들러 + `PermissionItemTag` | | Preview 탭 이벤트 위임 | `ChatWindow.PreviewPresentation.cs` | 탭당 7개 람다 → PreviewTabPanel에 단일 위임 핸들러 + `PreviewTabTag` | | GitBranch 이벤트 위임 | `ChatWindow.GitBranchPresentation.cs`, `SelectionPopupPresentation.cs` | `CreateFlatPopupRow`/`CreatePopupMenuRow` 행의 람다 → GitBranchItems에 단일 위임 + `PopupRowTag` | ### 구조적 리팩토링 P3 (2026-04-09) | 대상 | 파일 | 변경 | |------|------|------| | FileBrowser 명시적 해제 | `ChatWindow.FileBrowserPresentation.cs` | TreeViewItem 람다→명명 메서드(`FileTreeItem_Expanded/DoubleClick/RightClick`) 전환. `BuildFileTree()` 시 `DetachFileTreeHandlers()` 재귀 호출로 Clear 전 핸들러 해제. 트리 재구축당 300개 핸들러 누적 해소 | > 전체 계획 완료. `docs/STRUCTURAL_REFACTORING_PLAN.md` 참조. ### 런처 · 에이전트 리소스/안정성 수정 (2026-04-09) | 대상 | 파일 | 변경 | |------|------|------| | LauncherWindow 이벤트 누수 | `LauncherWindow.xaml.cs` | `vm.CloseRequested`, `vm.PropertyChanged`, `app.IndexService.IndexRebuilt` 핸들러를 필드 저장 → `OnClosed`에서 `-=` 해제. ViewModel보다 Window가 먼저 닫힐 때 GC 누수 방지 | | ChatWindow 타이머 정리 | `ChatWindow.xaml.cs` | `Closed` 핸들러에 누락된 8개 타이머 명시적 `Stop()` 추가 + `StopAgentEventProcessor()` 호출 | | Events 스레드 안전 | `AgentLoopService.cs` | Dispatcher 없을 때 `Events` 접근에 `lock(Events)` 추가 — 동시 EmitEvent 호출 시 IndexOutOfRange 크래시 방지 | | NotifyTool 타이머 누적 | `NotifyTool.cs` | 알림당 `new DispatcherTimer` → `DoubleAnimation.Completed` 콜백으로 대체. 100개 알림 시 100개 타이머 동시 존재 해소 | | LauncherWindow 토스트 타이머 | `LauncherWindow.xaml.cs` | `ShowToast()` 매 호출 `new DispatcherTimer` → 재사용 패턴 + 명명 메서드(`ToastTimer_Tick`) | | LauncherWindow 타이머 정리 | `LauncherWindow.xaml.cs` | `OnClosed`에 `_toastTimer?.Stop()`, `_indexStatusTimer?.Stop()` 추가 | ### Hot path · 리소스 추가 최적화 (2026-04-09) | 대상 | 파일 | 변경 | |------|------|------| | GetRuntimeActiveTools 캐시 | `AgentLoopService.cs` | 반복당 1~4회 호출 → `cachedActiveTools` 로컬 변수로 1회 캐시. foreach 내 `activeToolNames` 계산도 루프 밖으로 호이스트 | | SubAgentTool 취소 전파 | `SubAgentTool.cs` | `CancellationTokenSource.CreateLinkedTokenSource(ct)` 연동. Task.Run + loop.RunAsync에 토큰 전달. 부모 중지 시 자식 즉시 취소 | | 아이콘 애니메이션 재귀 제어 | `LauncherWindow.xaml.cs` | `sb.Completed`에서 즉시 재귀 → `_iconAnimationDelayTimer` 8초 딜레이. 할당 빈도 75% 감소. 클릭 시 딜레이 취소 후 즉시 전환 | | JsonSerializerOptions 공유 | `AgentLoopService.cs` | `s_jsonOpts` 정적 필드 추가, 4개 `JsonSerializer.Serialize` 호출에 적용. L4096 `System.Text.Json.` 접두사 정규화 | ### Claude Desktop 스타일 UI 개선 (2026-04-09) | 항목 | 파일 | 수정 내용 | |------|------|----------| | 미리보기 Split Button | `ChatWindow.xaml` | 기존 `BtnPreviewToggle` (Ellipse 점 + "프리뷰") → `[▶ 미리보기 | ∨]` Split Button으로 교체. 좌측 토글, 우측 셰브론 드롭다운 | | 미리보기 드롭다운 | `ChatWindow.PreviewPresentation.cs` | `ShowPreviewTabDropdown()` — 열린 탭 목록 팝업, 파일 확장자별 아이콘, 활성 탭 하이라이트 | | PreviewDot → PreviewIcon | `ChatWindow.PreviewPresentation.cs` | `PreviewDot.Fill` 4곳 → `PreviewIcon.Foreground` (AccentColor/SecondaryText) 전환 | | 셰브론 동기화 | `ChatWindow.PreviewPresentation.cs` | `UpdatePreviewChevronState()` — `_previewTabs.Count` 기반 IsHitTestVisible/Opacity 제어 | | 계획 버튼 이동 | `ChatWindow.xaml` | MoodIconPanel 동적 주입 → StatusBar XAML 선언 요소 `BtnPlanViewer`로 이동 | | ShowPlanButton 리팩토링 | `ChatWindow.PlanApprovalPresentation.cs` | 동적 Add/Remove → `Visibility` 토글 단순화 + 레거시 정리 유지 | ### 에이전트 루프 문서 생성 흐름 수정 (2026-04-09) | 파일 | 수정 내용 | |------|----------| | `AgentLoopTransitions.Documents.cs` | `TryHandleTerminalDocumentCompletionTransitionAsync`에서 `document_plan` 없이 바로 문서 도구 호출 시 조기 종료 방지 — LLM이 추가 반복으로 내용을 보강할 수 있도록 허용 | | `HtmlSkill.cs` | `MarkdownToHtml`에서 LLM이 삽입한 `
` 태그가 이스케이프되는 버그 수정 — 이스케이프 전 플레이스홀더로 보존 후 복원 | --- ## 13. 디렉토리별 가이드 | 디렉토리 | 수정 시 주의사항 | |---------|----------------| | `Core/` | `FuzzyEngine` 점수 공식 변경 시 검색 품질에 직접 영향 | | `Handlers/` | 새 핸들러 추가 시 `App.xaml.cs`에 등록 필요 | | `Services/Agent/` | 새 도구 추가 시 `ToolRegistry`에 등록 + 스킬 파일(`.skill.md`) 작성 | | `Themes/` | 리소스 키 변경 시 모든 테마에 동일하게 적용 필요 | | `Models/AppSettings.cs` | 속성 추가 시 `SettingsService` 마이그레이션 고려 | | `Views/ChatWindow.*` | partial class 분할 — 관련 기능은 해당 파일에서 수정 | --- ## 14. 관련 문서 | 문서 | 내용 | |------|------| | `docs/AGENT_ROADMAP.md` | 에이전트 기능 로드맵 | | `docs/LAUNCHER_ROADMAP.md` | 런처 기능 로드맵 | | `docs/CLAW_CODE_PARITY_PLAN.md` | Claude Code 기능 대응 계획 | | `docs/TOOL_PARITY_REPORT.md` | 도구 호환성 리포트 | | `docs/AX_AGENT_UI_CHECKLIST.md` | 에이전트 UI 체크리스트 | | `docs/UI_UX_CHECKLIST.md` | UI/UX 체크리스트 | --- ### 선택적 탐색 구조 개선 (2026-04-09 10:36 KST) - `claude-code`의 `Glob/Grep/FileRead` 프롬프트와 `toolOrchestration.ts` 흐름을 다시 대조한 결과, AX는 `folder_map`을 너무 쉽게 먼저 호출하도록 유도하는 규칙 때문에 질문과 무관한 전체 워크스페이스를 훑는 경향이 있었습니다. - `src/AxCopilot/Views/ChatWindow.xaml.cs` - Cowork/Code 시스템 프롬프트에서 `folder_map`을 항상 첫 단계로 요구하던 문구를 완화했습니다. - 좁은 범위의 질문은 `glob/grep + targeted file_read`를 우선하고, 저장소 전체 구조가 정말 필요할 때만 `folder_map`을 쓰도록 바꿨습니다. - `src/AxCopilot/Services/Agent/FolderMapTool.cs` - 기본 탐색 depth를 `3 -> 2`로 낮췄습니다. - `include_files` 기본값을 `true -> false`로 바꿔 첫 패스에서 구조 확인 위주로 동작하게 했습니다. - `src/AxCopilot/Services/Agent/MultiReadTool.cs` - 한 번에 읽을 수 있는 최대 파일 수를 `20 -> 8`로 낮춰 초기 과탐색과 토큰 낭비를 줄였습니다. - `src/AxCopilot/Services/Agent/AgentLoopExplorationPolicy.cs` - 새 partial 파일을 추가해 탐색 범위를 `Localized / TopicBased / RepoWide / OpenEnded`로 분류하는 정책을 도입했습니다. - `folder_map` 반복, 대량 `multi_read`, broad scan 패턴이 나오면 관련 파일만 다시 고르도록 corrective hint를 주입하는 규칙을 넣었습니다. - `src/AxCopilot/Services/Agent/AgentLoopService.cs` - 루프 시작 시 탐색 범위 가이드를 시스템 메시지로 주입합니다. - 도구 실행 중 `folder_map` 호출 수, `multi_read` 파일 수, 총 읽은 파일 수를 추적합니다. - 좁은 범위 질문에서 broad scan이 감지되면 `탐색 범위를 좁히는 중 · 관련 파일만 다시 선택합니다` 진행 메시지를 남기고 선택적 탐색으로 되돌립니다. - `src/AxCopilot/Services/AgentPerformanceLogService.cs` - `%APPDATA%\\AxCopilot\\perf`에 `exploration_breadth` 성능 로그를 남겨 broad scan 여부와 selective hit 여부를 실사용 기준으로 검증할 수 있게 했습니다.