에이전트 선택적 탐색 구조 개선과 경고 정리 반영
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) 기준 개발 이력을 반영함
This commit is contained in:
2026-04-09 14:27:59 +09:00
parent 7931566212
commit 33c1db4dae
119 changed files with 4453 additions and 6943 deletions

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
@@ -12,9 +12,108 @@ namespace AxCopilot.Views;
public partial class ChatWindow
{
// ── A-1: TopicButtonPanel 이벤트 위임 ──
private Border? _lastHoveredTopicBorder;
private bool _topicPanelDelegationInitialized;
/// <summary>프리셋 카드 Border.Tag에 저장하는 메타 데이터.</summary>
private sealed class TopicCardTag
{
public required string Action { get; init; } // "preset" | "etc" | "add" | "custom_preset"
public TopicPreset? Preset { get; init; }
public required Brush NormalBackground { get; init; }
public required Brush HoverBackground { get; init; }
public Brush? NormalBorderBrush { get; init; }
}
/// <summary>TopicButtonPanel에 이벤트 위임 핸들러를 1회 등록합니다.</summary>
private void InitTopicPanelDelegation()
{
if (_topicPanelDelegationInitialized || TopicButtonPanel == null)
return;
_topicPanelDelegationInitialized = true;
TopicButtonPanel.Background = Brushes.Transparent; // 갭 영역도 히트테스트 대상으로 등록
TopicButtonPanel.MouseMove += TopicPanel_DelegatedMouseMove;
TopicButtonPanel.MouseLeave += TopicPanel_DelegatedMouseLeave;
TopicButtonPanel.PreviewMouseLeftButtonDown += TopicPanel_DelegatedLeftButtonDown;
TopicButtonPanel.PreviewMouseRightButtonUp += TopicPanel_DelegatedRightButtonUp;
}
private void TopicPanel_DelegatedMouseMove(object sender, MouseEventArgs e)
{
var border = FindAncestorWithTag<Border>(e.OriginalSource as DependencyObject);
if (ReferenceEquals(border, _lastHoveredTopicBorder))
return;
// 이전 호버 해제
if (_lastHoveredTopicBorder?.Tag is TopicCardTag prevTag)
{
_lastHoveredTopicBorder.Background = prevTag.NormalBackground;
if (prevTag.NormalBorderBrush != null)
_lastHoveredTopicBorder.BorderBrush = prevTag.NormalBorderBrush;
}
_lastHoveredTopicBorder = border;
// 새 호버 적용
if (border?.Tag is TopicCardTag tag)
{
border.Background = tag.HoverBackground;
border.BorderBrush = TryFindResource("AccentColor") as Brush ?? tag.NormalBorderBrush ?? border.BorderBrush;
}
}
private void TopicPanel_DelegatedMouseLeave(object sender, MouseEventArgs e)
{
if (_lastHoveredTopicBorder?.Tag is TopicCardTag prevTag)
{
_lastHoveredTopicBorder.Background = prevTag.NormalBackground;
if (prevTag.NormalBorderBrush != null)
_lastHoveredTopicBorder.BorderBrush = prevTag.NormalBorderBrush;
}
_lastHoveredTopicBorder = null;
}
private void TopicPanel_DelegatedLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var border = FindAncestorWithTag<Border>(e.OriginalSource as DependencyObject);
if (border?.Tag is not TopicCardTag tag)
return;
e.Handled = true;
switch (tag.Action)
{
case "preset" or "custom_preset" when tag.Preset != null:
SelectTopic(tag.Preset);
break;
case "etc":
EmptyState.Visibility = Visibility.Collapsed;
InputBox.Focus();
break;
case "add":
ShowCustomPresetDialog();
break;
}
}
private void TopicPanel_DelegatedRightButtonUp(object sender, MouseButtonEventArgs e)
{
var border = FindAncestorWithTag<Border>(e.OriginalSource as DependencyObject);
if (border?.Tag is not TopicCardTag tag)
return;
if (tag.Action == "custom_preset" && tag.Preset != null)
{
e.Handled = true;
ShowCustomPresetContextMenu(border, tag.Preset);
}
}
/// <summary>프리셋 대화 주제 버튼을 동적으로 생성합니다.</summary>
private void BuildTopicButtons()
{
_lastHoveredTopicBorder = null;
TopicButtonPanel.Children.Clear();
TopicButtonPanel.Visibility = Visibility.Visible;
if (TopicPresetScrollViewer != null)
@@ -52,29 +151,8 @@ public partial class ChatWindow
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
void AttachTopicCardHover(Border card, Brush normalBackground, Brush hoverBackground)
{
card.MouseEnter += (sender, _) =>
{
if (sender is Border hovered)
{
hovered.Background = hoverBackground;
hovered.BorderBrush = TryFindResource("AccentColor") as Brush ?? cardBorder;
}
};
card.MouseLeave += (sender, _) =>
{
if (sender is Border hovered)
{
hovered.Background = normalBackground;
hovered.BorderBrush = cardBorder;
}
};
}
foreach (var preset in presets)
{
var capturedPreset = preset;
var buttonColor = BrushFromHex(preset.Color);
var border = new Border
@@ -133,7 +211,7 @@ public partial class ChatWindow
contentGrid.Children.Add(stack);
if (capturedPreset.IsCustom)
if (preset.IsCustom)
{
var badge = new Border
{
@@ -158,17 +236,16 @@ public partial class ChatWindow
}
border.Child = contentGrid;
AttachTopicCardHover(border, cardBackground, cardHoverBackground);
border.MouseLeftButtonDown += (_, _) => SelectTopic(capturedPreset);
if (capturedPreset.IsCustom)
// A-1: 이벤트 위임 — 개별 람다 대신 Tag에 메타 저장
border.Tag = new TopicCardTag
{
border.MouseRightButtonUp += (sender, args) =>
{
args.Handled = true;
ShowCustomPresetContextMenu(sender as Border, capturedPreset);
};
}
Action = preset.IsCustom ? "custom_preset" : "preset",
Preset = preset,
NormalBackground = cardBackground,
HoverBackground = cardHoverBackground,
NormalBorderBrush = cardBorder,
};
TopicButtonPanel.Children.Add(border);
}
@@ -225,11 +302,12 @@ public partial class ChatWindow
});
etcGrid.Children.Add(etcStack);
etcBorder.Child = etcGrid;
AttachTopicCardHover(etcBorder, cardBackground, cardHoverBackground);
etcBorder.MouseLeftButtonDown += (_, _) =>
etcBorder.Tag = new TopicCardTag
{
EmptyState.Visibility = Visibility.Collapsed;
InputBox.Focus();
Action = "etc",
NormalBackground = cardBackground,
HoverBackground = cardHoverBackground,
NormalBorderBrush = cardBorder,
};
TopicButtonPanel.Children.Add(etcBorder);
@@ -274,8 +352,13 @@ public partial class ChatWindow
addGrid.Children.Add(addStack);
addBorder.Child = addGrid;
AttachTopicCardHover(addBorder, Brushes.Transparent, cardHoverBackground);
addBorder.MouseLeftButtonDown += (_, _) => ShowCustomPresetDialog();
addBorder.Tag = new TopicCardTag
{
Action = "add",
NormalBackground = Brushes.Transparent,
HoverBackground = cardHoverBackground,
NormalBorderBrush = cardBorder,
};
TopicButtonPanel.Children.Add(addBorder);
UpdateTopicPresetScrollMode();