코드 리뷰 회복 흐름과 자동 실행 가드 정비
- 비 Git 작업 폴더에서 git_tool(diff)만 반복 호출하지 않도록 AgentLoop 실패 복구, 우선순위, 태스크 가이드를 code_review(file_review) 대안까지 포함하도록 조정했습니다. - CodeReviewTool diff_review가 실제 Git 저장소 루트와 Git 실행 가능 여부를 먼저 확인하고, 저장소가 아니거나 Git이 없으면 file_review 대안을 즉시 안내하도록 보강했습니다. - OpenExternalTool에 사용자 명시 요청 기반 auto-open 차단을 추가하고, Cowork 및 Code 시스템 프롬프트에도 결과물 자동 열기와 미리보기 서버 자동 실행 금지 규칙을 반영했습니다. - AgentLoopCodeQualityTests, OperationModeReadinessTests를 확장해 비 Git 리뷰 회복 경로와 암묵적 open_external 차단 회귀를 고정했습니다. - 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_review_policy_fix\\ -p:IntermediateOutputPath=obj\\verify_review_policy_fix\\ 및 dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter AgentLoopCodeQualityTests,OperationModeReadinessTests -p:OutputPath=bin\\verify_review_policy_fix_tests\\ -p:IntermediateOutputPath=obj\\verify_review_policy_fix_tests\
This commit is contained in:
@@ -1,5 +1,14 @@
|
|||||||
# AX Commander
|
# AX Commander
|
||||||
|
|
||||||
|
- 업데이트: 2026-04-15 20:55 (KST)
|
||||||
|
- Code 탭 리뷰 로그 기준으로 비 Git 작업 폴더 회복 흐름을 보강했습니다. `src/AxCopilot/Services/Agent/AgentLoopService.cs`, `src/AxCopilot/Services/Agent/TaskTypePolicy.cs`가 `git_tool(diff)`만 고집하지 않고 `code_review(file_review)` 또는 직접 파일 검토 경로를 함께 안내하도록 바뀌어, `현재 작업 폴더는 Git 저장소가 아닙니다`와 `Git을 찾을 수 없습니다` 이후 같은 Git 계열 도구를 반복 호출하던 흐름을 줄였습니다.
|
||||||
|
- `src/AxCopilot/Services/Agent/CodeReviewTool.cs`는 `diff_review` 전에 실제 Git 저장소 루트를 확인하고, 저장소가 아니거나 Git 실행이 불가능하면 바로 `file_review` 대안을 반환하도록 보강했습니다. Git 탐지도 `where.exe` 기반으로 맞춰 `git_tool`과 `code_review` 사이 탐지 불일치를 줄였습니다.
|
||||||
|
- `src/AxCopilot/Services/Agent/OpenExternalTool.cs`, `src/AxCopilot/Views/ChatWindow.SystemPromptBuilder.cs`, `src/AxCopilot/Services/Agent/IAgentTool.cs`에는 자동 실행 가드를 추가했습니다. 사용자가 명시적으로 열기/실행/미리보기를 요청하지 않은 경우 `open_external`은 차단되고, Cowork/Code 시스템 프롬프트도 결과물 생성 뒤 브라우저 실행·미리보기 서버 시작을 자동으로 하지 말라고 명시합니다.
|
||||||
|
- 테스트: `src/AxCopilot.Tests/Services/AgentLoopCodeQualityTests.cs`, `src/AxCopilot.Tests/Services/OperationModeReadinessTests.cs`
|
||||||
|
- 검증
|
||||||
|
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_review_policy_fix\\ -p:IntermediateOutputPath=obj\\verify_review_policy_fix\\` 경고 0 / 오류 0
|
||||||
|
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AgentLoopCodeQualityTests|OperationModeReadinessTests" -p:OutputPath=bin\\verify_review_policy_fix_tests\\ -p:IntermediateOutputPath=obj\\verify_review_policy_fix_tests\\` 통과 133
|
||||||
|
|
||||||
- 업데이트: 2026-04-15 20:41 (KST)
|
- 업데이트: 2026-04-15 20:41 (KST)
|
||||||
- AX Agent 좌측 대화 목록을 Codex 스타일에 가깝게 1줄형 카드로 단순화했습니다. `src/AxCopilot/Views/ChatWindow.xaml`의 `ConversationItemTemplate`는 제목과 시간을 한 줄에 배치하고, 선택된 항목은 전체 배경과 테두리가 현재 테마(`HintBackground`, `AccentColor`)를 따라 강조되도록 바뀌었습니다.
|
- AX Agent 좌측 대화 목록을 Codex 스타일에 가깝게 1줄형 카드로 단순화했습니다. `src/AxCopilot/Views/ChatWindow.xaml`의 `ConversationItemTemplate`는 제목과 시간을 한 줄에 배치하고, 선택된 항목은 전체 배경과 테두리가 현재 테마(`HintBackground`, `AccentColor`)를 따라 강조되도록 바뀌었습니다.
|
||||||
- `src/AxCopilot/Views/ChatWindow.ConversationListPresentation.cs`, `src/AxCopilot/ViewModels/ChatWindowViewModel.cs`, `src/AxCopilot/Views/ChatWindow.xaml.cs`를 통해 실행 중인 대화는 앞쪽 링 표시로, 백그라운드 완료 후 아직 열어보지 않은 대화는 테마색 완료 점으로 구분하도록 정리했습니다. 완료 점은 해당 대화를 열면 바로 사라집니다.
|
- `src/AxCopilot/Views/ChatWindow.ConversationListPresentation.cs`, `src/AxCopilot/ViewModels/ChatWindowViewModel.cs`, `src/AxCopilot/Views/ChatWindow.xaml.cs`를 통해 실행 중인 대화는 앞쪽 링 표시로, 백그라운드 완료 후 아직 열어보지 않은 대화는 테마색 완료 점으로 구분하도록 정리했습니다. 완료 점은 해당 대화를 열면 바로 사라집니다.
|
||||||
|
|||||||
@@ -1556,3 +1556,10 @@ UI ?遺우쁽????域뱀뮆???귐뗫솯?醫딆춦 ???袁る퓮 ?臾믩씜 ??疫
|
|||||||
- 검증:
|
- 검증:
|
||||||
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_conversation_list_refresh\\ -p:IntermediateOutputPath=obj\\verify_conversation_list_refresh\\` 경고 0 / 오류 0
|
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_conversation_list_refresh\\ -p:IntermediateOutputPath=obj\\verify_conversation_list_refresh\\` 경고 0 / 오류 0
|
||||||
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ChatWindowSlashPolicyTests" -p:OutputPath=bin\\verify_conversation_list_refresh_tests\\ -p:IntermediateOutputPath=obj\\verify_conversation_list_refresh_tests\\` 통과 59
|
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ChatWindowSlashPolicyTests" -p:OutputPath=bin\\verify_conversation_list_refresh_tests\\ -p:IntermediateOutputPath=obj\\verify_conversation_list_refresh_tests\\` 통과 59
|
||||||
|
업데이트: 2026-04-15 20:55 (KST)
|
||||||
|
- Code 탭 리뷰 로그를 기준으로 비 Git 작업 폴더 회복 흐름을 조정했습니다. `src/AxCopilot/Services/Agent/AgentLoopService.cs`와 `src/AxCopilot/Services/Agent/TaskTypePolicy.cs`가 `git_tool(diff)`만 고집하지 않고 `code_review(file_review)` 또는 직접 파일 검토 경로를 함께 제시하도록 바뀌어, 저장소가 아닌 폴더에서 리뷰/검증 작업이 같은 Git 계열 도구를 반복 호출하던 회귀를 줄였습니다.
|
||||||
|
- `src/AxCopilot/Services/Agent/CodeReviewTool.cs`는 `diff_review` 전에 실제 Git 저장소 루트를 확인하고, 저장소가 아니거나 Git 실행이 불가능하면 즉시 `file_review` 대안을 반환합니다. Git 탐지도 `where.exe` 기반으로 보강해 `git_tool`과 `code_review`의 Git 탐지 결과가 달라지던 문제를 함께 줄였습니다.
|
||||||
|
- `src/AxCopilot/Services/Agent/OpenExternalTool.cs`, `src/AxCopilot/Views/ChatWindow.SystemPromptBuilder.cs`, `src/AxCopilot/Services/Agent/IAgentTool.cs`에는 자동 열기 실행 가드를 추가했습니다. 사용자가 명시적으로 열기/실행/미리보기를 요청하지 않은 경우 `open_external`은 차단되고, Cowork/Code 시스템 프롬프트도 결과물 생성 뒤 브라우저 실행이나 미리보기 서버 시작을 자동으로 하지 않도록 고정했습니다.
|
||||||
|
- 테스트: `src/AxCopilot.Tests/Services/AgentLoopCodeQualityTests.cs`, `src/AxCopilot.Tests/Services/OperationModeReadinessTests.cs`
|
||||||
|
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_review_policy_fix\\ -p:IntermediateOutputPath=obj\\verify_review_policy_fix\\` 경고 0 / 오류 0
|
||||||
|
- 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AgentLoopCodeQualityTests|OperationModeReadinessTests" -p:OutputPath=bin\\verify_review_policy_fix_tests\\ -p:IntermediateOutputPath=obj\\verify_review_policy_fix_tests\\` 통과 133
|
||||||
|
|||||||
@@ -219,10 +219,26 @@ public class AgentLoopCodeQualityTests
|
|||||||
"bugfix");
|
"bugfix");
|
||||||
|
|
||||||
message.Should().Contain("Fallback sequence");
|
message.Should().Contain("Fallback sequence");
|
||||||
message.Should().Contain("file_read -> grep/glob -> git_tool(diff)");
|
message.Should().Contain("git_tool(diff)");
|
||||||
message.Should().Contain("repro/root-cause");
|
message.Should().Contain("repro/root-cause");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BuildFailureReflectionMessage_UsesFileReviewFallbackWhenWorkspaceIsNotGitRepository()
|
||||||
|
{
|
||||||
|
var prompt = InvokePrivateStatic<string>(
|
||||||
|
"BuildFailureReflectionMessage",
|
||||||
|
"git_tool",
|
||||||
|
ToolResult.Fail("현재 작업 폴더는 Git 저장소가 아닙니다."),
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
"review");
|
||||||
|
|
||||||
|
prompt.Should().Contain("저장소 컨텍스트");
|
||||||
|
prompt.Should().Contain("code_review(file_review)");
|
||||||
|
prompt.Should().NotContain("git_tool(diff) -> targeted tool retry");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void BuildFailureNextToolPriorityPrompt_IncludesOrderedPriority()
|
public void BuildFailureNextToolPriorityPrompt_IncludesOrderedPriority()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Text.Json;
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
using AxCopilot.Handlers;
|
using AxCopilot.Handlers;
|
||||||
using AxCopilot.Services;
|
using AxCopilot.Services;
|
||||||
using AxCopilot.Services.Agent;
|
using AxCopilot.Services.Agent;
|
||||||
@@ -104,4 +105,45 @@ public class OperationModeReadinessTests
|
|||||||
result.Success.Should().BeFalse();
|
result.Success.Should().BeFalse();
|
||||||
result.Output.Should().Contain("사내모드");
|
result.Output.Should().Contain("사내모드");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task OpenExternal_RejectsImplicitAutoOpenWithoutExplicitUserIntent()
|
||||||
|
{
|
||||||
|
var tool = new OpenExternalTool();
|
||||||
|
using var doc = JsonDocument.Parse("""{"path":"report.html"}""");
|
||||||
|
var context = new AgentContext
|
||||||
|
{
|
||||||
|
OperationMode = OperationModePolicy.ExternalMode,
|
||||||
|
Permission = "Auto",
|
||||||
|
InitialUserQuery = "리뷰 보고서를 html로 만들어줘"
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await tool.ExecuteAsync(doc.RootElement, context, CancellationToken.None);
|
||||||
|
|
||||||
|
result.Success.Should().BeFalse();
|
||||||
|
result.Output.Should().Contain("명시적으로 요청");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task OpenExternal_AllowsExplicitOpenIntentToProceedPastIntentGate()
|
||||||
|
{
|
||||||
|
var tool = new OpenExternalTool();
|
||||||
|
var tempDir = Path.Combine(Path.GetTempPath(), "AxCopilotOpenIntentTests");
|
||||||
|
Directory.CreateDirectory(tempDir);
|
||||||
|
|
||||||
|
using var doc = JsonDocument.Parse("""{"path":"report.html"}""");
|
||||||
|
var context = new AgentContext
|
||||||
|
{
|
||||||
|
OperationMode = OperationModePolicy.ExternalMode,
|
||||||
|
Permission = "Auto",
|
||||||
|
WorkFolder = tempDir,
|
||||||
|
InitialUserQuery = "생성한 report.html 열어줘"
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await tool.ExecuteAsync(doc.RootElement, context, CancellationToken.None);
|
||||||
|
|
||||||
|
result.Success.Should().BeFalse();
|
||||||
|
result.Output.Should().Contain("경로를 찾을 수 없습니다");
|
||||||
|
result.Output.Should().NotContain("명시적으로 요청");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,6 +296,7 @@ public partial class AgentLoopService
|
|||||||
string? lastModifiedCodeFilePath = null;
|
string? lastModifiedCodeFilePath = null;
|
||||||
|
|
||||||
var context = BuildContext();
|
var context = BuildContext();
|
||||||
|
context.InitialUserQuery = userQuery;
|
||||||
runState.WorkspaceAppearsEmpty = DetectEmptyWorkspace(context.WorkFolder);
|
runState.WorkspaceAppearsEmpty = DetectEmptyWorkspace(context.WorkFolder);
|
||||||
|
|
||||||
var preferredInitialToolSequence = BuildPreferredInitialToolSequence(
|
var preferredInitialToolSequence = BuildPreferredInitialToolSequence(
|
||||||
@@ -2725,7 +2726,7 @@ public partial class AgentLoopService
|
|||||||
IReadOnlyCollection<ParsedFailurePattern> parsedPatterns)
|
IReadOnlyCollection<ParsedFailurePattern> parsedPatterns)
|
||||||
{
|
{
|
||||||
if (parsedPatterns.Count == 0)
|
if (parsedPatterns.Count == 0)
|
||||||
return "실행 지침: 같은 명령/파라미터 반복을 금지하고, 읽기 근거(file_read/grep/git_tool) 후에만 재시도하세요.";
|
return "실행 지침: 같은 명령/파라미터 반복을 금지하고, 읽기 근거(file_read/grep/git_tool(diff) 또는 code_review(file_review)) 후에만 재시도하세요.";
|
||||||
|
|
||||||
var repeatedTools = parsedPatterns
|
var repeatedTools = parsedPatterns
|
||||||
.Select(p => (p.ToolName ?? "").Trim().ToLowerInvariant())
|
.Select(p => (p.ToolName ?? "").Trim().ToLowerInvariant())
|
||||||
@@ -2740,9 +2741,6 @@ public partial class AgentLoopService
|
|||||||
? "실패 도구 공통 패턴"
|
? "실패 도구 공통 패턴"
|
||||||
: string.Join(", ", repeatedTools);
|
: string.Join(", ", repeatedTools);
|
||||||
var hasBuildLoopRisk = repeatedTools.Any(t => t.Contains("build_run") || t.Contains("test_loop"));
|
var hasBuildLoopRisk = repeatedTools.Any(t => t.Contains("build_run") || t.Contains("test_loop"));
|
||||||
var priority = hasBuildLoopRisk
|
|
||||||
? "file_read -> grep/glob -> git_tool(diff) -> file_edit -> build_run/test_loop"
|
|
||||||
: "file_read -> grep/glob -> git_tool(diff) -> targeted retry";
|
|
||||||
var dominantFailureKinds = parsedPatterns
|
var dominantFailureKinds = parsedPatterns
|
||||||
.Select(p => (p.FailureKind ?? "").Trim().ToLowerInvariant())
|
.Select(p => (p.FailureKind ?? "").Trim().ToLowerInvariant())
|
||||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||||
@@ -2751,6 +2749,7 @@ public partial class AgentLoopService
|
|||||||
.Take(2)
|
.Take(2)
|
||||||
.Select(g => g.Key)
|
.Select(g => g.Key)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
var avoidGitDiff = dominantFailureKinds.Any(kind => ShouldPreferDirectChangeReview(ParseFailureKindToken(kind)));
|
||||||
var kindHint = dominantFailureKinds.Count == 0
|
var kindHint = dominantFailureKinds.Count == 0
|
||||||
? "일반 실패"
|
? "일반 실패"
|
||||||
: string.Join(", ", dominantFailureKinds.Select(DescribeFailureKindToken));
|
: string.Join(", ", dominantFailureKinds.Select(DescribeFailureKindToken));
|
||||||
@@ -2763,6 +2762,9 @@ public partial class AgentLoopService
|
|||||||
"review" => "추측이 아닌 결함 근거를 먼저 확보하세요.",
|
"review" => "추측이 아닌 결함 근거를 먼저 확보하세요.",
|
||||||
_ => "실패 로그 근거를 먼저 확보하세요."
|
_ => "실패 로그 근거를 먼저 확보하세요."
|
||||||
};
|
};
|
||||||
|
var priority = hasBuildLoopRisk
|
||||||
|
? BuildChangeReviewPrioritySequence(avoidGitDiff, includeEditAndVerify: true)
|
||||||
|
: BuildChangeReviewPrioritySequence(avoidGitDiff, includeEditAndVerify: false);
|
||||||
|
|
||||||
return "실행 지침:\n" +
|
return "실행 지침:\n" +
|
||||||
$"- 반복 경향 도구: {toolHint}\n" +
|
$"- 반복 경향 도구: {toolHint}\n" +
|
||||||
@@ -2780,11 +2782,47 @@ public partial class AgentLoopService
|
|||||||
"path" => "경로",
|
"path" => "경로",
|
||||||
"command" => "명령/파라미터",
|
"command" => "명령/파라미터",
|
||||||
"dependency" => "의존성/환경",
|
"dependency" => "의존성/환경",
|
||||||
|
"repository" => "저장소 컨텍스트",
|
||||||
"timeout" => "타임아웃",
|
"timeout" => "타임아웃",
|
||||||
_ => "일반",
|
_ => "일반",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static FailureRecoveryKind ParseFailureKindToken(string token)
|
||||||
|
{
|
||||||
|
return token switch
|
||||||
|
{
|
||||||
|
"permission" => FailureRecoveryKind.Permission,
|
||||||
|
"path" => FailureRecoveryKind.Path,
|
||||||
|
"command" => FailureRecoveryKind.Command,
|
||||||
|
"dependency" => FailureRecoveryKind.Dependency,
|
||||||
|
"repository" => FailureRecoveryKind.Repository,
|
||||||
|
"timeout" => FailureRecoveryKind.Timeout,
|
||||||
|
_ => FailureRecoveryKind.Unknown,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ShouldPreferDirectChangeReview(FailureRecoveryKind kind)
|
||||||
|
=> kind is FailureRecoveryKind.Dependency or FailureRecoveryKind.Repository;
|
||||||
|
|
||||||
|
private static string BuildChangeReviewPrioritySequence(bool preferDirectReview, bool includeEditAndVerify)
|
||||||
|
{
|
||||||
|
var reviewStep = preferDirectReview
|
||||||
|
? "code_review(file_review)/직접 파일 검토"
|
||||||
|
: "git_tool(diff)";
|
||||||
|
|
||||||
|
return includeEditAndVerify
|
||||||
|
? $"file_read -> grep/glob -> {reviewStep} -> file_edit -> build_run/test_loop"
|
||||||
|
: $"file_read -> grep/glob -> {reviewStep} -> targeted retry";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildChangeReviewInstruction(FailureRecoveryKind kind)
|
||||||
|
{
|
||||||
|
return ShouldPreferDirectChangeReview(kind)
|
||||||
|
? "Use code_review(file_review) or re-read the changed files directly to confirm what actually changed."
|
||||||
|
: "Review the current diff with git_tool(diff) or an equivalent review tool to confirm what actually changed.";
|
||||||
|
}
|
||||||
|
|
||||||
private static string BuildCodeQualityFollowUpPrompt(string toolName, ToolResult result, bool highImpact, bool hasBaselineBuildOrTest, TaskTypePolicy taskPolicy)
|
private static string BuildCodeQualityFollowUpPrompt(string toolName, ToolResult result, bool highImpact, bool hasBaselineBuildOrTest, TaskTypePolicy taskPolicy)
|
||||||
{
|
{
|
||||||
var fileRef = string.IsNullOrWhiteSpace(result.FilePath)
|
var fileRef = string.IsNullOrWhiteSpace(result.FilePath)
|
||||||
@@ -2818,7 +2856,7 @@ public partial class AgentLoopService
|
|||||||
taskTypeLine +
|
taskTypeLine +
|
||||||
"1. file_read로 방금 수정한 파일을 다시 읽어 실제 반영 상태를 확인합니다.\n" +
|
"1. file_read로 방금 수정한 파일을 다시 읽어 실제 반영 상태를 확인합니다.\n" +
|
||||||
"2. grep 또는 glob으로 영향받는 호출부/참조 지점을 다시 확인합니다.\n" +
|
"2. grep 또는 glob으로 영향받는 호출부/참조 지점을 다시 확인합니다.\n" +
|
||||||
"3. git_tool diff 또는 동등한 검토 도구로 변경 범위를 확인합니다.\n" +
|
"3. git_tool(diff) 또는 code_review(file_review) 같은 검토 도구로 변경 범위를 확인합니다.\n" +
|
||||||
extraSteps;
|
extraSteps;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2834,7 +2872,7 @@ public partial class AgentLoopService
|
|||||||
{
|
{
|
||||||
var failureKind = ClassifyFailureRecoveryKind(toolName, result.Output);
|
var failureKind = ClassifyFailureRecoveryKind(toolName, result.Output);
|
||||||
var failureHint = BuildFailureTypeRecoveryHint(failureKind, toolName);
|
var failureHint = BuildFailureTypeRecoveryHint(failureKind, toolName);
|
||||||
var fallbackSequence = BuildFallbackToolSequenceHint(toolName, taskPolicy);
|
var fallbackSequence = BuildFallbackToolSequenceHint(toolName, taskPolicy, failureKind);
|
||||||
if (toolName is "build_run" or "test_loop")
|
if (toolName is "build_run" or "test_loop")
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
@@ -2842,7 +2880,7 @@ public partial class AgentLoopService
|
|||||||
"Before retrying, do all of the following:\n" +
|
"Before retrying, do all of the following:\n" +
|
||||||
"1. Re-read the files you changed most recently.\n" +
|
"1. Re-read the files you changed most recently.\n" +
|
||||||
"2. Inspect impacted callers/references with grep or glob.\n" +
|
"2. Inspect impacted callers/references with grep or glob.\n" +
|
||||||
"3. Review the current diff to confirm what actually changed.\n" +
|
$"3. {BuildChangeReviewInstruction(failureKind)}\n" +
|
||||||
"4. Only then fix the root cause and re-run build/test.\n" +
|
"4. Only then fix the root cause and re-run build/test.\n" +
|
||||||
failureHint +
|
failureHint +
|
||||||
fallbackSequence +
|
fallbackSequence +
|
||||||
@@ -2877,16 +2915,17 @@ public partial class AgentLoopService
|
|||||||
maxRetry,
|
maxRetry,
|
||||||
TaskTypePolicy.FromTaskType(taskType));
|
TaskTypePolicy.FromTaskType(taskType));
|
||||||
|
|
||||||
private static string BuildFallbackToolSequenceHint(string toolName, TaskTypePolicy taskPolicy)
|
private static string BuildFallbackToolSequenceHint(string toolName, TaskTypePolicy taskPolicy, FailureRecoveryKind failureKind)
|
||||||
{
|
{
|
||||||
|
var includeEditAndVerify = toolName is "build_run" or "test_loop" or "file_edit" or "file_write";
|
||||||
var sequence = toolName switch
|
var sequence = toolName switch
|
||||||
{
|
{
|
||||||
"build_run" or "test_loop" =>
|
"build_run" or "test_loop" =>
|
||||||
"Fallback sequence: file_read -> grep/glob -> git_tool(diff) -> file_edit -> build_run/test_loop.\n",
|
$"Fallback sequence: {BuildChangeReviewPrioritySequence(ShouldPreferDirectChangeReview(failureKind), includeEditAndVerify: true)}.\n",
|
||||||
"file_edit" or "file_write" =>
|
"file_edit" or "file_write" =>
|
||||||
"Fallback sequence: file_read -> grep/glob -> git_tool(diff) -> file_edit -> build_run/test_loop.\n",
|
$"Fallback sequence: {BuildChangeReviewPrioritySequence(ShouldPreferDirectChangeReview(failureKind), includeEditAndVerify: true)}.\n",
|
||||||
_ =>
|
_ =>
|
||||||
"Fallback sequence: file_read -> grep/glob -> git_tool(diff) -> targeted tool retry.\n",
|
$"Fallback sequence: {BuildChangeReviewPrioritySequence(ShouldPreferDirectChangeReview(failureKind), includeEditAndVerify: includeEditAndVerify)}.\n",
|
||||||
};
|
};
|
||||||
|
|
||||||
var taskHint = taskPolicy.TaskType switch
|
var taskHint = taskPolicy.TaskType switch
|
||||||
@@ -2965,7 +3004,7 @@ public partial class AgentLoopService
|
|||||||
return "[System:FailureInvestigation] build/test가 실패했습니다. 다음 순서로 원인을 찾으세요.\n" +
|
return "[System:FailureInvestigation] build/test가 실패했습니다. 다음 순서로 원인을 찾으세요.\n" +
|
||||||
fileLine +
|
fileLine +
|
||||||
"2. grep/glob으로 영향받는 호출부와 참조 지점을 확인합니다.\n" +
|
"2. grep/glob으로 영향받는 호출부와 참조 지점을 확인합니다.\n" +
|
||||||
"3. git diff 또는 동등한 방법으로 실제 변경 범위를 검토합니다.\n" +
|
$"3. {BuildChangeReviewInstruction(ClassifyFailureRecoveryKind(toolName, toolOutput))}\n" +
|
||||||
failureHint +
|
failureHint +
|
||||||
taskTypeLine +
|
taskTypeLine +
|
||||||
highImpactLine;
|
highImpactLine;
|
||||||
@@ -2981,6 +3020,7 @@ public partial class AgentLoopService
|
|||||||
Path,
|
Path,
|
||||||
Command,
|
Command,
|
||||||
Dependency,
|
Dependency,
|
||||||
|
Repository,
|
||||||
Timeout,
|
Timeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2989,11 +3029,13 @@ public partial class AgentLoopService
|
|||||||
var text = ((output ?? "") + "\n" + toolName).ToLowerInvariant();
|
var text = ((output ?? "") + "\n" + toolName).ToLowerInvariant();
|
||||||
if (ContainsAny(text, "permission", "denied", "forbidden", "unauthorized", "access is denied", "권한", "차단", "승인"))
|
if (ContainsAny(text, "permission", "denied", "forbidden", "unauthorized", "access is denied", "권한", "차단", "승인"))
|
||||||
return FailureRecoveryKind.Permission;
|
return FailureRecoveryKind.Permission;
|
||||||
|
if (ContainsAny(text, "git 저장소가 아닙니다", "not a git repository", "work tree", "git repository"))
|
||||||
|
return FailureRecoveryKind.Repository;
|
||||||
if (ContainsAny(text, "not found", "no such file", "cannot find", "directory not found", "path", "경로", "파일을 찾을", "존재하지"))
|
if (ContainsAny(text, "not found", "no such file", "cannot find", "directory not found", "path", "경로", "파일을 찾을", "존재하지"))
|
||||||
return FailureRecoveryKind.Path;
|
return FailureRecoveryKind.Path;
|
||||||
if (ContainsAny(text, "command not found", "not recognized", "unknown option", "invalid argument", "syntax", "잘못된 옵션", "명령", "인식할 수"))
|
if (ContainsAny(text, "command not found", "not recognized", "unknown option", "invalid argument", "syntax", "잘못된 옵션", "명령", "인식할 수"))
|
||||||
return FailureRecoveryKind.Command;
|
return FailureRecoveryKind.Command;
|
||||||
if (ContainsAny(text, "module not found", "package not found", "sdk", "runtime", "missing dependency", "의존성", "설치되지"))
|
if (ContainsAny(text, "module not found", "package not found", "sdk", "runtime", "missing dependency", "의존성", "설치되지", "git을 찾을 수 없습니다"))
|
||||||
return FailureRecoveryKind.Dependency;
|
return FailureRecoveryKind.Dependency;
|
||||||
if (ContainsAny(text, "timeout", "timed out", "time out", "시간 초과"))
|
if (ContainsAny(text, "timeout", "timed out", "time out", "시간 초과"))
|
||||||
return FailureRecoveryKind.Timeout;
|
return FailureRecoveryKind.Timeout;
|
||||||
@@ -3011,7 +3053,9 @@ public partial class AgentLoopService
|
|||||||
FailureRecoveryKind.Command =>
|
FailureRecoveryKind.Command =>
|
||||||
$"원인 분류: 명령/파라미터 이슈. '{toolName}' 입력 파라미터와 옵션 이름을 재검토하고, 최소 파라미터로 먼저 검증하세요.\n",
|
$"원인 분류: 명령/파라미터 이슈. '{toolName}' 입력 파라미터와 옵션 이름을 재검토하고, 최소 파라미터로 먼저 검증하세요.\n",
|
||||||
FailureRecoveryKind.Dependency =>
|
FailureRecoveryKind.Dependency =>
|
||||||
"원인 분류: 의존성/환경 이슈. 설치/버전 누락 여부를 확인하고, 대체 가능한 도구 흐름(file_read/grep/git_tool)으로 우회하세요.\n",
|
"원인 분류: 의존성/환경 이슈. 설치/버전 누락 여부를 확인하고, Git/리뷰 도구가 불안정하면 file_read/grep/code_review(file_review) 흐름으로 우회하세요.\n",
|
||||||
|
FailureRecoveryKind.Repository =>
|
||||||
|
"원인 분류: 저장소 컨텍스트 이슈. 현재 폴더가 Git 저장소가 아니면 git diff 대신 file_read/grep/code_review(file_review)로 파일별 검토를 진행하세요.\n",
|
||||||
FailureRecoveryKind.Timeout =>
|
FailureRecoveryKind.Timeout =>
|
||||||
"원인 분류: 타임아웃. 대상 범위를 축소(파일/테스트 필터)해 더 짧은 실행 단위로 분해한 뒤 재시도하세요.\n",
|
"원인 분류: 타임아웃. 대상 범위를 축소(파일/테스트 필터)해 더 짧은 실행 단위로 분해한 뒤 재시도하세요.\n",
|
||||||
_ =>
|
_ =>
|
||||||
@@ -5262,7 +5306,7 @@ public partial class AgentLoopService
|
|||||||
"1. 직전 실패 로그에서 핵심 오류 1~2줄을 먼저 인용하세요.\n" +
|
"1. 직전 실패 로그에서 핵심 오류 1~2줄을 먼저 인용하세요.\n" +
|
||||||
"2. 다음 재시도는 이전과 최소 1개 축을 다르게 실행하세요 (대상 프로젝트/테스트 필터/설정/작업 디렉터리).\n" +
|
"2. 다음 재시도는 이전과 최소 1개 축을 다르게 실행하세요 (대상 프로젝트/테스트 필터/설정/작업 디렉터리).\n" +
|
||||||
"3. 코드 변경 없이 동일 명령 재실행은 금지합니다.\n" +
|
"3. 코드 변경 없이 동일 명령 재실행은 금지합니다.\n" +
|
||||||
"4. 재시도 전 file_read/grep/git_tool(diff) 중 최소 2개 근거를 확보하세요.\n";
|
"4. 재시도 전 file_read/grep/(git_tool(diff) 또는 code_review(file_review)) 중 최소 2개 근거를 확보하세요.\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string BuildFailureNextToolPriorityPrompt(
|
private static string BuildFailureNextToolPriorityPrompt(
|
||||||
@@ -5302,15 +5346,17 @@ public partial class AgentLoopService
|
|||||||
FailureRecoveryKind.Command =>
|
FailureRecoveryKind.Command =>
|
||||||
"도구 스키마 재확인 -> 최소 파라미터 호출 -> 옵션 확장 -> 필요 시 대체 도구",
|
"도구 스키마 재확인 -> 최소 파라미터 호출 -> 옵션 확장 -> 필요 시 대체 도구",
|
||||||
FailureRecoveryKind.Dependency =>
|
FailureRecoveryKind.Dependency =>
|
||||||
"dev_env_detect/process 환경 확인 -> file_read/grep/git_tool 근거 확보 -> 가능한 범위 우회",
|
"dev_env_detect/process 환경 확인 -> file_read/grep -> code_review(file_review) 또는 직접 검토 -> 가능한 범위 우회",
|
||||||
|
FailureRecoveryKind.Repository =>
|
||||||
|
"file_read -> grep/glob -> code_review(file_review) 또는 직접 파일 검토 -> 필요 시 file_edit/build_run/test_loop",
|
||||||
FailureRecoveryKind.Timeout =>
|
FailureRecoveryKind.Timeout =>
|
||||||
"대상 범위 축소 -> 필터 적용 실행 -> 분할 실행 -> 최종 전체 실행",
|
"대상 범위 축소 -> 필터 적용 실행 -> 분할 실행 -> 최종 전체 실행",
|
||||||
_ => failedToolName switch
|
_ => failedToolName switch
|
||||||
{
|
{
|
||||||
"build_run" or "test_loop" => "file_read -> grep/glob -> git_tool(diff) -> file_edit -> build_run/test_loop",
|
"build_run" or "test_loop" => BuildChangeReviewPrioritySequence(preferDirectReview: false, includeEditAndVerify: true),
|
||||||
"file_edit" or "file_write" => "file_read -> grep/glob -> git_tool(diff) -> file_edit -> build_run/test_loop",
|
"file_edit" or "file_write" => BuildChangeReviewPrioritySequence(preferDirectReview: false, includeEditAndVerify: true),
|
||||||
"grep" or "glob" => "glob/grep 재구성 -> file_read -> folder_map(필요 시만)",
|
"grep" or "glob" => "glob/grep 재구성 -> file_read -> folder_map(필요 시만)",
|
||||||
_ => "file_read -> grep/glob -> git_tool(diff) -> targeted tool retry"
|
_ => BuildChangeReviewPrioritySequence(preferDirectReview: false, includeEditAndVerify: false)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,9 +73,21 @@ public class CodeReviewTool : IAgentTool
|
|||||||
|
|
||||||
private async Task<ToolResult> DiffReviewAsync(AgentContext ctx, string target, string focus, CancellationToken ct)
|
private async Task<ToolResult> DiffReviewAsync(AgentContext ctx, string target, string focus, CancellationToken ct)
|
||||||
{
|
{
|
||||||
|
if (!TryResolveGitRepositoryRoot(ctx.WorkFolder, out var gitRoot))
|
||||||
|
{
|
||||||
|
return ToolResult.Fail(
|
||||||
|
"현재 작업 폴더는 Git 저장소가 아닙니다. " +
|
||||||
|
"diff_review 대신 code_review(file_review)로 핵심 파일을 직접 검토하세요.");
|
||||||
|
}
|
||||||
|
|
||||||
var diffArgs = string.IsNullOrEmpty(target) ? "diff" : $"diff {target}";
|
var diffArgs = string.IsNullOrEmpty(target) ? "diff" : $"diff {target}";
|
||||||
var diffResult = await RunGitAsync(ctx.WorkFolder, diffArgs, ct);
|
var diffResult = await RunGitAsync(gitRoot, diffArgs, ct);
|
||||||
if (diffResult == null) return ToolResult.Fail("Git을 찾을 수 없습니다.");
|
if (diffResult == null)
|
||||||
|
{
|
||||||
|
return ToolResult.Fail(
|
||||||
|
"Git을 찾을 수 없거나 실행할 수 없습니다. " +
|
||||||
|
"Git diff를 쓸 수 없으면 code_review(file_review)로 파일별 리뷰를 진행하세요.");
|
||||||
|
}
|
||||||
if (string.IsNullOrWhiteSpace(diffResult))
|
if (string.IsNullOrWhiteSpace(diffResult))
|
||||||
return ToolResult.Ok("변경사항이 없습니다. (clean working tree)");
|
return ToolResult.Ok("변경사항이 없습니다. (clean working tree)");
|
||||||
|
|
||||||
@@ -368,6 +380,25 @@ public class CodeReviewTool : IAgentTool
|
|||||||
|
|
||||||
private static string? FindGit()
|
private static string? FindGit()
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var psi = new ProcessStartInfo("where.exe", "git")
|
||||||
|
{
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
};
|
||||||
|
using var proc = Process.Start(psi);
|
||||||
|
if (proc == null) return null;
|
||||||
|
var output = proc.StandardOutput.ReadToEnd().Trim();
|
||||||
|
proc.WaitForExit(3000);
|
||||||
|
if (!string.IsNullOrWhiteSpace(output))
|
||||||
|
return output.Split('\n')[0].Trim();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
var paths = new[] { "git", @"C:\Program Files\Git\bin\git.exe", @"C:\Program Files (x86)\Git\bin\git.exe" };
|
var paths = new[] { "git", @"C:\Program Files\Git\bin\git.exe", @"C:\Program Files (x86)\Git\bin\git.exe" };
|
||||||
foreach (var p in paths)
|
foreach (var p in paths)
|
||||||
{
|
{
|
||||||
@@ -384,6 +415,30 @@ public class CodeReviewTool : IAgentTool
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryResolveGitRepositoryRoot(string workDir, out string gitRoot)
|
||||||
|
{
|
||||||
|
gitRoot = "";
|
||||||
|
if (string.IsNullOrWhiteSpace(workDir) || !Directory.Exists(workDir))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var checkDir = workDir;
|
||||||
|
while (!string.IsNullOrWhiteSpace(checkDir))
|
||||||
|
{
|
||||||
|
if (Directory.Exists(Path.Combine(checkDir, ".git")))
|
||||||
|
{
|
||||||
|
gitRoot = checkDir;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parent = Directory.GetParent(checkDir)?.FullName;
|
||||||
|
if (string.IsNullOrWhiteSpace(parent) || string.Equals(parent, checkDir, StringComparison.OrdinalIgnoreCase))
|
||||||
|
break;
|
||||||
|
checkDir = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Diff 파서 ──────────────────────────────────────────────────────────
|
// ─── Diff 파서 ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private static List<DiffFile> ParseDiffFiles(string diff)
|
private static List<DiffFile> ParseDiffFiles(string diff)
|
||||||
|
|||||||
@@ -143,6 +143,9 @@ public class AgentContext
|
|||||||
/// <summary>현재 활성 탭. "Chat" | "Cowork" | "Code".</summary>
|
/// <summary>현재 활성 탭. "Chat" | "Cowork" | "Code".</summary>
|
||||||
public string ActiveTab { get; set; } = "Chat";
|
public string ActiveTab { get; set; } = "Chat";
|
||||||
|
|
||||||
|
/// <summary>현재 실행 턴의 원본 사용자 요청. 실행/열기 의도 가드에 사용합니다.</summary>
|
||||||
|
public string InitialUserQuery { get; set; } = "";
|
||||||
|
|
||||||
/// <summary>운영 모드. internal(사내) | external(사외).
|
/// <summary>운영 모드. internal(사내) | external(사외).
|
||||||
/// 실행 중 사용자가 설정에서 모드를 바꾸면 SyncContextFromSettings를 통해 업데이트됨.</summary>
|
/// 실행 중 사용자가 설정에서 모드를 바꾸면 SyncContextFromSettings를 통해 업데이트됨.</summary>
|
||||||
public string OperationMode { get; set; } = AxCopilot.Services.OperationModePolicy.InternalMode;
|
public string OperationMode { get; set; } = AxCopilot.Services.OperationModePolicy.InternalMode;
|
||||||
|
|||||||
@@ -7,11 +7,18 @@ namespace AxCopilot.Services.Agent;
|
|||||||
/// <summary>파일/URL을 시스템 기본 앱으로 여는 도구.</summary>
|
/// <summary>파일/URL을 시스템 기본 앱으로 여는 도구.</summary>
|
||||||
public class OpenExternalTool : IAgentTool
|
public class OpenExternalTool : IAgentTool
|
||||||
{
|
{
|
||||||
|
private static readonly string[] ExplicitOpenIntentKeywords =
|
||||||
|
[
|
||||||
|
"열어", "열어줘", "열어 줘", "오픈", "띄워", "띄워줘", "보여줘", "보여 줘",
|
||||||
|
"실행", "실행해", "실행해줘", "실행해 줘", "미리보기", "브라우저", "launch",
|
||||||
|
"open", "preview", "show", "serve", "run", "execute"
|
||||||
|
];
|
||||||
|
|
||||||
public string Name => "open_external";
|
public string Name => "open_external";
|
||||||
public string Description =>
|
public string Description =>
|
||||||
"Open a file with its default application or open a URL in the default browser. " +
|
"Open a file with its default application or open a URL in the default browser. " +
|
||||||
"Also supports opening a folder in File Explorer. " +
|
"Also supports opening a folder in File Explorer. " +
|
||||||
"Use after creating documents, reports, or charts for the user to view.";
|
"Only use when the user explicitly asks to open, preview, or launch the result.";
|
||||||
|
|
||||||
public ToolParameterSchema Parameters => new()
|
public ToolParameterSchema Parameters => new()
|
||||||
{
|
{
|
||||||
@@ -44,6 +51,13 @@ public class OpenExternalTool : IAgentTool
|
|||||||
return Task.FromResult(ToolResult.Ok($"외부 URI 열기: {rawPath}"));
|
return Task.FromResult(ToolResult.Ok($"외부 URI 열기: {rawPath}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!HasExplicitOpenIntent(context.InitialUserQuery))
|
||||||
|
{
|
||||||
|
return Task.FromResult(ToolResult.Fail(
|
||||||
|
"사용자가 열기/실행/미리보기를 명시적으로 요청하지 않았습니다. " +
|
||||||
|
"결과 경로만 보고하고 자동 실행하지 마세요."));
|
||||||
|
}
|
||||||
|
|
||||||
// 파일/폴더 경로
|
// 파일/폴더 경로
|
||||||
var path = Path.IsPathRooted(rawPath) ? rawPath : Path.Combine(context.WorkFolder, rawPath);
|
var path = Path.IsPathRooted(rawPath) ? rawPath : Path.Combine(context.WorkFolder, rawPath);
|
||||||
|
|
||||||
@@ -69,4 +83,13 @@ public class OpenExternalTool : IAgentTool
|
|||||||
return Task.FromResult(ToolResult.Fail($"열기 오류: {ex.Message}"));
|
return Task.FromResult(ToolResult.Fail($"열기 오류: {ex.Message}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool HasExplicitOpenIntent(string? query)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ExplicitOpenIntentKeywords.Any(keyword =>
|
||||||
|
query.Contains(keyword, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ internal sealed class TaskTypePolicy
|
|||||||
TaskType = "bugfix",
|
TaskType = "bugfix",
|
||||||
GuidanceMessage =
|
GuidanceMessage =
|
||||||
"[System:TaskType] This is a bug-fix task. Prioritize reproduction evidence, root cause linkage, smallest safe fix, and regression verification. " +
|
"[System:TaskType] This is a bug-fix task. Prioritize reproduction evidence, root cause linkage, smallest safe fix, and regression verification. " +
|
||||||
"Preferred tool order: targeted file_read or grep/glob/lsp -> file_edit -> build_run/test_loop as needed -> git_tool(diff) when it helps confirm the final change.",
|
"Preferred tool order: targeted file_read or grep/glob/lsp -> file_edit -> build_run/test_loop as needed -> git_tool(diff) or code_review(file_review) when it helps confirm the final change.",
|
||||||
FailurePatternFocus = "Check reproduction conditions and root-cause linkage first.",
|
FailurePatternFocus = "Check reproduction conditions and root-cause linkage first.",
|
||||||
FollowUpTaskLine = "Task type: bugfix. Verify the fix is directly linked to the symptom and confirm non-regression.\n",
|
FollowUpTaskLine = "Task type: bugfix. Verify the fix is directly linked to the symptom and confirm non-regression.\n",
|
||||||
FailureInvestigationTaskLine = "Extra check: confirm the symptom is no longer reproducible and root-cause linkage is valid.\n",
|
FailureInvestigationTaskLine = "Extra check: confirm the symptom is no longer reproducible and root-cause linkage is valid.\n",
|
||||||
@@ -33,7 +33,7 @@ internal sealed class TaskTypePolicy
|
|||||||
TaskType = "feature",
|
TaskType = "feature",
|
||||||
GuidanceMessage =
|
GuidanceMessage =
|
||||||
"[System:TaskType] This is a feature task. Prioritize affected interfaces/callers, data flow, validation paths, and test/documentation needs. " +
|
"[System:TaskType] This is a feature task. Prioritize affected interfaces/callers, data flow, validation paths, and test/documentation needs. " +
|
||||||
"Preferred tool order: targeted file_read or grep/glob/lsp -> file_edit/file_write -> build_run/test_loop as needed -> git_tool(diff). " +
|
"Preferred tool order: targeted file_read or grep/glob/lsp -> file_edit/file_write -> build_run/test_loop as needed -> git_tool(diff) or code_review(file_review). " +
|
||||||
"Use folder_map only when the user explicitly needs folder structure or file listing.",
|
"Use folder_map only when the user explicitly needs folder structure or file listing.",
|
||||||
FailurePatternFocus = "Check new behavior flow and caller linkage first.",
|
FailurePatternFocus = "Check new behavior flow and caller linkage first.",
|
||||||
FollowUpTaskLine = "Task type: feature. Verify behavior flow, input/output path, caller impact, and test additions.\n",
|
FollowUpTaskLine = "Task type: feature. Verify behavior flow, input/output path, caller impact, and test additions.\n",
|
||||||
@@ -45,7 +45,7 @@ internal sealed class TaskTypePolicy
|
|||||||
TaskType = "refactor",
|
TaskType = "refactor",
|
||||||
GuidanceMessage =
|
GuidanceMessage =
|
||||||
"[System:TaskType] This is a refactor task. Prioritize behavior preservation, reference impact, diff review, and non-regression evidence. " +
|
"[System:TaskType] This is a refactor task. Prioritize behavior preservation, reference impact, diff review, and non-regression evidence. " +
|
||||||
"Preferred tool order: targeted file_read or grep/glob/lsp -> file_edit -> build_run/test_loop as needed -> git_tool(diff).",
|
"Preferred tool order: targeted file_read or grep/glob/lsp -> file_edit -> build_run/test_loop as needed -> git_tool(diff) or code_review(file_review).",
|
||||||
FailurePatternFocus = "Check behavior preservation and impact scope first.",
|
FailurePatternFocus = "Check behavior preservation and impact scope first.",
|
||||||
FollowUpTaskLine = "Task type: refactor. Prioritize behavior-preservation evidence over cosmetic cleanup.\n",
|
FollowUpTaskLine = "Task type: refactor. Prioritize behavior-preservation evidence over cosmetic cleanup.\n",
|
||||||
FailureInvestigationTaskLine = "Extra check: validate existing call flow remains behavior-compatible.\n",
|
FailureInvestigationTaskLine = "Extra check: validate existing call flow remains behavior-compatible.\n",
|
||||||
@@ -57,7 +57,7 @@ internal sealed class TaskTypePolicy
|
|||||||
GuidanceMessage =
|
GuidanceMessage =
|
||||||
"[System:TaskType] This is a review task. Prioritize concrete defects, regressions, risky assumptions, and missing tests before summaries. " +
|
"[System:TaskType] This is a review task. Prioritize concrete defects, regressions, risky assumptions, and missing tests before summaries. " +
|
||||||
"Report findings with P0-P3 severity and file evidence, then separate Fixed vs Unfixed status. " +
|
"Report findings with P0-P3 severity and file evidence, then separate Fixed vs Unfixed status. " +
|
||||||
"Preferred tool order: targeted file_read or grep/glob/lsp -> git_tool(diff) when available -> evidence-first findings.",
|
"Preferred tool order: targeted file_read or grep/glob/lsp -> git_tool(diff) when available, otherwise code_review(file_review) or direct file review -> evidence-first findings.",
|
||||||
FailurePatternFocus = "Review focus: severity accuracy (P0-P3), file-grounded evidence, and unresolved-risk clarity.",
|
FailurePatternFocus = "Review focus: severity accuracy (P0-P3), file-grounded evidence, and unresolved-risk clarity.",
|
||||||
FollowUpTaskLine = "Task type: review-follow-up. For each finding, state status as Fixed or Unfixed with verification evidence.\n",
|
FollowUpTaskLine = "Task type: review-follow-up. For each finding, state status as Fixed or Unfixed with verification evidence.\n",
|
||||||
FailureInvestigationTaskLine = "Extra check: every risk must have either a concrete fix or an explicit unresolved rationale and impact.\n",
|
FailureInvestigationTaskLine = "Extra check: every risk must have either a concrete fix or an explicit unresolved rationale and impact.\n",
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ public partial class ChatWindow
|
|||||||
sb.AppendLine("When writing a new document, avoid repetitive same-shape sections. Tailor the structure to the purpose and use summaries, findings, comparison tables, timelines, recommendations, appendices, or action items when they improve clarity.");
|
sb.AppendLine("When writing a new document, avoid repetitive same-shape sections. Tailor the structure to the purpose and use summaries, findings, comparison tables, timelines, recommendations, appendices, or action items when they improve clarity.");
|
||||||
sb.AppendLine("Prefer concrete and useful content over filler. If a section benefits from bullets, tables, or structured comparison, use them instead of flat generic paragraphs.");
|
sb.AppendLine("Prefer concrete and useful content over filler. If a section benefits from bullets, tables, or structured comparison, use them instead of flat generic paragraphs.");
|
||||||
sb.AppendLine("After creating files, summarize what was created and include the actual output path.");
|
sb.AppendLine("After creating files, summarize what was created and include the actual output path.");
|
||||||
|
sb.AppendLine("Do NOT call open_external, launch a browser, or start a preview/server process unless the user explicitly asks to open, preview, serve, launch, or run the result.");
|
||||||
sb.AppendLine("IMPORTANT: In your FINAL response after ALL work is done, provide a structured completion summary in this format:");
|
sb.AppendLine("IMPORTANT: In your FINAL response after ALL work is done, provide a structured completion summary in this format:");
|
||||||
sb.AppendLine(" - 작업 유형: (e.g., report/analysis/proposal)");
|
sb.AppendLine(" - 작업 유형: (e.g., report/analysis/proposal)");
|
||||||
sb.AppendLine(" - 산출물 파일: full absolute path (e.g., E:\\test\\report.html)");
|
sb.AppendLine(" - 산출물 파일: full absolute path (e.g., E:\\test\\report.html)");
|
||||||
@@ -251,6 +252,7 @@ public partial class ChatWindow
|
|||||||
sb.AppendLine("Available tools: file_read, file_write, file_edit (supports replace_all), glob, grep (supports context_lines, case_sensitive), lsp_code_intel, folder_map, process, dev_env_detect, build_run, git_tool.");
|
sb.AppendLine("Available tools: file_read, file_write, file_edit (supports replace_all), glob, grep (supports context_lines, case_sensitive), lsp_code_intel, folder_map, process, dev_env_detect, build_run, git_tool.");
|
||||||
sb.AppendLine("Do not pause after partial progress. Keep executing consecutive steps until completion or a concrete blocker is reached.");
|
sb.AppendLine("Do not pause after partial progress. Keep executing consecutive steps until completion or a concrete blocker is reached.");
|
||||||
sb.AppendLine("IMPORTANT: When creating documents with dates, always use today's actual date above.");
|
sb.AppendLine("IMPORTANT: When creating documents with dates, always use today's actual date above.");
|
||||||
|
sb.AppendLine("Do NOT call open_external, launch browsers, or start preview/server commands unless the user explicitly asks to open, preview, serve, launch, or run the result.");
|
||||||
|
|
||||||
sb.AppendLine("\n## Core Workflow");
|
sb.AppendLine("\n## Core Workflow");
|
||||||
sb.AppendLine("1. ORIENT: Pick the smallest next step that can answer the request.");
|
sb.AppendLine("1. ORIENT: Pick the smallest next step that can answer the request.");
|
||||||
|
|||||||
Reference in New Issue
Block a user