코워크와 코드의 작업 폴더 선택 멈춤을 완화한다
- 작업 폴더 변경 직후 UI 스레드에서 실행되던 스킬 소스 재탐색을 백그라운드 재로드로 분리한다. - 조건부 스킬 활성화 경로를 재로드와 분리해 첨부 파일 변경처럼 폴더가 바뀌지 않는 경우 불필요한 스킬 재탐색을 줄인다. - README와 DEVELOPMENT 문서에 2026-04-14 19:02(KST) 기준 작업 이력과 검증 결과를 반영한다. - 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_folderpick\\ -p:IntermediateOutputPath=obj\\verify_folderpick\\ 경고 0 / 오류 0
This commit is contained in:
@@ -7,6 +7,11 @@ Windows 전용 시맨틱 런처 & 워크스페이스 매니저
|
||||
개발 참고: Claw Code 동등성 작업 추적 문서
|
||||
`docs/claw-code-parity-plan.md`
|
||||
|
||||
- 업데이트: 2026-04-14 19:02 (KST)
|
||||
- 코워크/코드의 작업 폴더 선택 후 UI가 2~3초 멈추던 흐름을 점검해, 폴더 변경 직후 실행되던 스킬 소스 재탐색을 UI 스레드 밖으로 분리했습니다. 이제 작업 폴더 변경, 탭 전환, 대화 복원 시 필요한 스킬 재로드는 백그라운드에서 수행하고, 조건부 스킬 활성화만 UI에 다시 반영합니다.
|
||||
- 첨부 파일 추가/제거처럼 작업 폴더가 바뀌지 않는 경로는 기존 스킬 집합만 기준으로 조건부 스킬을 갱신하도록 분리해 불필요한 재탐색도 줄였습니다.
|
||||
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_folderpick\\ -p:IntermediateOutputPath=obj\\verify_folderpick\\` 경고 0 / 오류 0
|
||||
|
||||
- 업데이트: 2026-04-14 18:45 (KST)
|
||||
- AX Agent 내부 설정의 스킬 탭 안내 블록에 커스텀 라벨을 추가했습니다. 이제 `.claude/skills/.../SKILL.md` 프로젝트 호환 경로가 스킬 탭 첫 화면에서 바로 보여, 워크스페이스에 같은 구조가 있으면 AX가 함께 읽는다는 점을 UI에서도 확인할 수 있습니다.
|
||||
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_skilllabel\\ -p:IntermediateOutputPath=obj\\verify_skilllabel\\` 경고 0 / 오류 0
|
||||
|
||||
@@ -835,3 +835,8 @@ UI 디자인 대규모 리팩토링 등 위험 작업 전 기록한 안전 복
|
||||
- 업데이트: 2026-04-14 18:45 (KST)
|
||||
- AX Agent 내부 설정의 스킬 탭 안내 블록에 커스텀 라벨을 추가했습니다. 이제 .claude/skills/.../SKILL.md 프로젝트 호환 경로가 스킬 탭 첫 화면에서 바로 보여, 워크스페이스에 같은 구조가 있으면 AX가 함께 읽는다는 점을 UI에서도 확인할 수 있습니다.
|
||||
- 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_skilllabel\\ -p:IntermediateOutputPath=obj\\verify_skilllabel\\ 경고 0 / 오류 0
|
||||
|
||||
- 업데이트: 2026-04-14 19:02 (KST)
|
||||
- 코워크/코드의 작업 폴더 선택 후 UI가 2~3초 멈추던 흐름을 점검해, 폴더 변경 직후 실행되던 스킬 소스 재탐색을 UI 스레드 밖으로 분리했습니다. 이제 작업 폴더 변경, 탭 전환, 대화 복원 시 필요한 스킬 재로드는 백그라운드에서 수행하고, 조건부 스킬 활성화만 UI에 다시 반영합니다.
|
||||
- 첨부 파일 추가/제거처럼 작업 폴더가 바뀌지 않는 경로는 기존 스킬 집합만 기준으로 조건부 스킬을 갱신하도록 분리해 불필요한 재탐색도 줄였습니다.
|
||||
- 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_folderpick\\ -p:IntermediateOutputPath=obj\\verify_folderpick\\ 경고 0 / 오류 0
|
||||
|
||||
@@ -130,6 +130,8 @@ public partial class ChatWindow : Window
|
||||
private readonly DispatcherTimer _agentProgressHintTimer;
|
||||
private readonly DispatcherTimer _tokenUsagePopupCloseTimer;
|
||||
private readonly DispatcherTimer _responsiveLayoutTimer;
|
||||
private CancellationTokenSource? _workspaceSkillRefreshCts;
|
||||
private int _workspaceSkillRefreshVersion;
|
||||
private CancellationTokenSource? _gitStatusRefreshCts;
|
||||
private int _displayedLength; // 현재 화면에 표시된 글자 수
|
||||
private ResourceDictionary? _agentThemeDictionary;
|
||||
@@ -1833,7 +1835,7 @@ public partial class ChatWindow : Window
|
||||
UpdateChatTitle();
|
||||
RefreshConversationList();
|
||||
UpdateFolderBar();
|
||||
UpdateConditionalSkillActivation(reset: true);
|
||||
UpdateConditionalSkillActivation(reset: true, reloadSkillSources: true);
|
||||
Services.LogService.Info($"[SwitchTab] NO_SESSION done, emptyState={EmptyState.Visibility}");
|
||||
}
|
||||
|
||||
@@ -2228,7 +2230,7 @@ public partial class ChatWindow : Window
|
||||
UpdateFolderSelectButtonStyle();
|
||||
ScheduleGitBranchRefresh();
|
||||
|
||||
UpdateConditionalSkillActivation(reset: true);
|
||||
UpdateConditionalSkillActivation(reset: true, reloadSkillSources: true);
|
||||
}
|
||||
|
||||
private string GetCurrentWorkFolder()
|
||||
@@ -2250,18 +2252,81 @@ public partial class ChatWindow : Window
|
||||
SkillService.LoadSkills(_settings.Settings.Llm.SkillsFolderPath, GetCurrentWorkFolder(), _settings.Settings.Llm.AdditionalSkillFolders);
|
||||
}
|
||||
|
||||
private void ScheduleWorkspaceSkillRefresh(bool reset = false)
|
||||
{
|
||||
if (!_settings.Settings.Llm.EnableSkillSystem)
|
||||
return;
|
||||
|
||||
var version = Interlocked.Increment(ref _workspaceSkillRefreshVersion);
|
||||
_workspaceSkillRefreshCts?.Cancel();
|
||||
_workspaceSkillRefreshCts?.Dispose();
|
||||
_workspaceSkillRefreshCts = new CancellationTokenSource();
|
||||
var ct = _workspaceSkillRefreshCts.Token;
|
||||
|
||||
var skillFolder = _settings.Settings.Llm.SkillsFolderPath;
|
||||
var workFolder = GetCurrentWorkFolder();
|
||||
var additionalFolders = (_settings.Settings.Llm.AdditionalSkillFolders ?? [])
|
||||
.Where(path => !string.IsNullOrWhiteSpace(path))
|
||||
.ToArray();
|
||||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
SkillService.EnsureSkillFolder();
|
||||
SkillService.LoadSkills(skillFolder, workFolder, additionalFolders);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
if (ct.IsCancellationRequested || version != _workspaceSkillRefreshVersion)
|
||||
return;
|
||||
|
||||
ApplyConditionalSkillActivation(reset, workFolder);
|
||||
}), DispatcherPriority.Background);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Services.LogService.Debug($"작업 폴더 스킬 재로드 실패: {ex.Message}");
|
||||
}
|
||||
}, ct);
|
||||
}
|
||||
|
||||
private void ApplyConditionalSkillActivation(bool reset = false, string? cwd = null)
|
||||
{
|
||||
if (!_settings.Settings.Llm.EnableSkillSystem)
|
||||
return;
|
||||
|
||||
cwd ??= GetCurrentWorkFolder();
|
||||
if (string.IsNullOrWhiteSpace(cwd) || !System.IO.Directory.Exists(cwd))
|
||||
return;
|
||||
|
||||
if (reset)
|
||||
SkillService.ResetConditionalSkillActivation();
|
||||
|
||||
SkillService.ActivateConditionalSkillsForPaths(_attachedFiles, cwd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 작업 컨텍스트(첨부 파일 + 작업 폴더) 기준으로
|
||||
/// 조건부 paths 스킬 활성화를 갱신합니다.
|
||||
/// </summary>
|
||||
private void UpdateConditionalSkillActivation(bool reset = false)
|
||||
private void UpdateConditionalSkillActivation(bool reset = false, bool reloadSkillSources = false)
|
||||
{
|
||||
if (!_settings.Settings.Llm.EnableSkillSystem) return;
|
||||
EnsureSkillSystemLoadedForCurrentWorkspace();
|
||||
var cwd = GetCurrentWorkFolder();
|
||||
if (string.IsNullOrWhiteSpace(cwd) || !System.IO.Directory.Exists(cwd)) return;
|
||||
if (reset) SkillService.ResetConditionalSkillActivation();
|
||||
SkillService.ActivateConditionalSkillsForPaths(_attachedFiles, cwd);
|
||||
if (!_settings.Settings.Llm.EnableSkillSystem)
|
||||
return;
|
||||
|
||||
if (reloadSkillSources)
|
||||
{
|
||||
ScheduleWorkspaceSkillRefresh(reset);
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyConditionalSkillActivation(reset);
|
||||
}
|
||||
|
||||
private void BtnFolderClear_Click(object sender, RoutedEventArgs e)
|
||||
@@ -3102,7 +3167,7 @@ public partial class ChatWindow : Window
|
||||
UpdateChatTitle();
|
||||
UpdateFolderBar();
|
||||
UpdateSelectedPresetGuide();
|
||||
UpdateConditionalSkillActivation(reset: true);
|
||||
UpdateConditionalSkillActivation(reset: true, reloadSkillSources: true);
|
||||
RenderMessages();
|
||||
RefreshConversationList();
|
||||
BuildTopicButtons();
|
||||
|
||||
Reference in New Issue
Block a user