Files
AX-Copilot-Codex/docs/DEVELOPMENT.md
lacvet 33c1db4dae
Some checks failed
Release Gate / gate (push) Has been cancelled
에이전트 선택적 탐색 구조 개선과 경고 정리 반영
- claude-code 선택적 탐색 흐름을 참고해 Cowork/Code 시스템 프롬프트에서 folder_map 상시 선행 지시를 완화하고 glob/grep 기반 좁은 탐색을 우선하도록 조정함

- FolderMapTool 기본 depth를 2로, include_files 기본값을 false로 낮추고 MultiReadTool 최대 파일 수를 8개로 줄여 초기 과탐색 폭을 보수적으로 조정함

- AgentLoopExplorationPolicy partial을 추가해 탐색 범위 분류, broad-scan corrective hint, exploration_breadth 성능 로그를 연결함

- AgentLoopService에 탐색 범위 가이드 주입과 실행 중 탐색 폭 추적을 추가하고, 좁은 질문에서 반복적인 folder_map/대량 multi_read를 교정하도록 정리함

- DocxToHtmlConverter nullable 경고를 수정해 Release 빌드 경고 0 / 오류 0 기준을 다시 충족함

- README와 docs/DEVELOPMENT.md에 2026-04-09 10:36 (KST) 기준 개발 이력을 반영함
2026-04-09 14:27:59 +09:00

507 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<IEnumerable<LauncherItem>> 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``<Version>` 태그 하나만 변경하면 앱 전체에 반영
- 설정 스키마 버전은 `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<T>`, `FindAncestor<T>` 유틸 추가 |
### 구조적 리팩토링 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이 삽입한 `<br>` 태그가 이스케이프되는 버그 수정 — 이스케이프 전 플레이스홀더로 보존 후 복원 |
---
## 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 여부를 실사용 기준으로 검증할 수 있게 했습니다.