AX Agent 슬래시 명령/도구 정책 정렬 및 개발문서 진행 이력 반영
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- /chrome: 인자 없는 진단 모드와 실행 라우팅 분리, MCP 재연결 자동 재시도 경로 보강 - /mcp: status/enable/disable/reconnect 명령 정리 및 상태 라벨 표준화 - /settings, /permissions 하위 액션 명확화, /verify·/commit 로컬 실행 흐름 정리 - /commit files:path1,path2 :: message 형태의 부분 커밋 지원 추가 - GitTool commit 경로의 레거시 비활성 응답 제거로 정책 일관성 확보 - ChatWindowSlashPolicyTests 신규 추가 및 AgentParityToolsTests 회귀 방지 테스트 보강 - docs/DEVELOPMENT.md, docs/AGENT_ROADMAP.md에 2026-04-04 진행 기록/스냅샷 반영
This commit is contained in:
@@ -38,6 +38,16 @@
|
||||
- `dotnet test --filter "Suite=ReplayStability"`: 14/14 통과.
|
||||
- `dotnet test`: 379/379 통과.
|
||||
|
||||
## 7. 명령/도구 보강 스냅샷 (2026-04-04)
|
||||
- 슬래시 명령 고도화: `/chrome`, `/mcp`, `/verify`, `/commit`, `/settings`, `/permissions` 하위 동작 정리.
|
||||
- `/mcp` 상태 라벨 표준화: `Connected`, `NeedsAuth`, `Configured`, `Disconnected`, `Disabled`.
|
||||
- `/chrome` 런타임 재시도: 초기 probe 실패 시 `/mcp reconnect all` 자동 수행 후 1회 재평가.
|
||||
- Git 정책 정렬: `git_tool`의 `commit` 비활성 문구 제거(로컬 커밋 경로와 정책 일치).
|
||||
- `/commit` 부분 커밋 지원: `files:path1,path2 :: 메시지` 형식으로 선택 파일만 stage+commit 가능.
|
||||
- 테스트 보강:
|
||||
- `ChatWindowSlashPolicyTests`: 슬래시 파서/검증 프롬프트/MCP 상태 라벨 단위 검증 추가.
|
||||
- `AgentParityToolsTests`: `git_tool commit` 레거시 비활성 메시지 회귀 방지 테스트 추가.
|
||||
|
||||
## 7. 권한 Hook 계약 (P2 마감 기준)
|
||||
- lifecycle hook 키:
|
||||
- `__permission_request__` (pre)
|
||||
|
||||
@@ -2690,3 +2690,36 @@ else:
|
||||
| 현재 (v1.6.1) | 스킬 프롬프트에서 양식 자동 감지 안내 | 완료 |
|
||||
| 다음 | 시스템 프롬프트에 양식 목록 자동 주입 (BuildCoworkSystemPrompt) | 낮음 |
|
||||
| 장기 | `.ax/templates/` 공용 양식 폴더 + 프로젝트 규칙에서 기본 양식 지정 | 중간 |
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-04 진행 기록 (AX Agent 명령/도구 보강)
|
||||
|
||||
### 1. 슬래시 명령 체계 보강
|
||||
- `/chrome`: 인자 미입력 시 MCP 런타임 진단, 인자 입력 시 브라우저 작업 실행 경로로 라우팅.
|
||||
- `/mcp`: `status`, `enable|disable`, `reconnect` 동작 정리 및 상태 라벨 표준화.
|
||||
- `/settings`: `model|permissions|mcp|theme` 하위 액션 지원.
|
||||
- `/permissions`: `ask|auto|deny|status` 하위 액션 지원.
|
||||
- `/verify`: Cowork/Code 탭 전용 검증 프롬프트 경로로 고정.
|
||||
- `/commit`: 승인 기반 커밋 실행 + `files:path1,path2 :: message` 부분 커밋 지원.
|
||||
- `/compact`: 수동 컨텍스트 압축 명령 유지 및 slash 팝업 힌트/도움말 반영.
|
||||
|
||||
### 2. 도구/권한 정책 정렬
|
||||
- `GitTool`의 commit 경로에서 레거시 비활성 응답(`커밋 비활성`) 제거.
|
||||
- 세션 단위 MCP 활성/비활성 오버라이드 적용(영구 설정 미오염).
|
||||
- `/chrome` 진단 실패 시 `/mcp reconnect all` 자동 1회 재시도 후 상태 재평가.
|
||||
|
||||
### 3. 테스트 보강
|
||||
- `ChatWindowSlashPolicyTests`:
|
||||
- 슬래시 파서(`ParseGenericAction`, `ParseMcpAction`) 검증.
|
||||
- `/verify` 시스템 프롬프트 필수 섹션 검증.
|
||||
- MCP 상태 라벨 매핑 검증.
|
||||
- `/commit` 입력 파서 검증.
|
||||
- `AgentParityToolsTests`:
|
||||
- `git_tool commit` 레거시 비활성 메시지 회귀 방지 테스트 추가.
|
||||
|
||||
### 4. 품질 게이트 결과
|
||||
- `dotnet build AxCopilot.sln` 통과 (경고 0, 오류 0).
|
||||
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj` 통과.
|
||||
- 기준 시점 전체 테스트: 403 passed, 0 failed.
|
||||
|
||||
|
||||
@@ -200,4 +200,34 @@ public class AgentParityToolsTests
|
||||
foreach (var name in required)
|
||||
names.Should().Contain(name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GitTool_Commit_ShouldNotReturnLegacyDisabledMessage()
|
||||
{
|
||||
var workDir = Path.Combine(Path.GetTempPath(), "ax-git-commit-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(workDir);
|
||||
Directory.CreateDirectory(Path.Combine(workDir, ".git"));
|
||||
try
|
||||
{
|
||||
var context = new AgentContext
|
||||
{
|
||||
WorkFolder = workDir,
|
||||
Permission = "Auto",
|
||||
OperationMode = "external",
|
||||
};
|
||||
|
||||
var tool = new GitTool();
|
||||
var result = await tool.ExecuteAsync(
|
||||
JsonDocument.Parse("""{"action":"commit","args":"테스트 커밋 메시지"}""").RootElement,
|
||||
context,
|
||||
CancellationToken.None);
|
||||
|
||||
result.Output.Should().NotContain("비활성 상태");
|
||||
result.Output.Should().NotContain("향후 버전에서 활성화");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { if (Directory.Exists(workDir)) Directory.Delete(workDir, true); } catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
77
src/AxCopilot.Tests/Views/ChatWindowSlashPolicyTests.cs
Normal file
77
src/AxCopilot.Tests/Views/ChatWindowSlashPolicyTests.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using AxCopilot.Views;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Views;
|
||||
|
||||
public class ChatWindowSlashPolicyTests
|
||||
{
|
||||
[Fact]
|
||||
public void BuildVerifySystemPrompt_ShouldContainStructuredSections()
|
||||
{
|
||||
var prompt = ChatWindow.BuildVerifySystemPrompt("/verify");
|
||||
|
||||
prompt.Should().Contain("[검증결과]");
|
||||
prompt.Should().Contain("[실행요약]");
|
||||
prompt.Should().Contain("[변경파일]");
|
||||
prompt.Should().Contain("[잔여리스크]");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/mcp", "status", "")]
|
||||
[InlineData("enable all", "enable", "all")]
|
||||
[InlineData("reconnect chrome", "reconnect", "chrome")]
|
||||
[InlineData("status", "status", "")]
|
||||
public void ParseMcpAction_ShouldParseExpected(string displayText, string expectedAction, string expectedTarget)
|
||||
{
|
||||
var (action, target) = ChatWindow.ParseMcpAction(displayText);
|
||||
|
||||
action.Should().Be(expectedAction);
|
||||
target.Should().Be(expectedTarget);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/settings", "open", "")]
|
||||
[InlineData("model", "model", "")]
|
||||
[InlineData("ask", "ask", "")]
|
||||
[InlineData("reconnect all", "reconnect", "all")]
|
||||
public void ParseGenericAction_ShouldParseExpected(string displayText, string expectedAction, string expectedArgument)
|
||||
{
|
||||
var (action, argument) = ChatWindow.ParseGenericAction(displayText, "/settings");
|
||||
|
||||
action.Should().Be(expectedAction);
|
||||
argument.Should().Be(expectedArgument);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, "stdio", true, false, null, "Disabled")]
|
||||
[InlineData(true, "stdio", false, false, null, "Enabled")]
|
||||
[InlineData(true, "sse", true, false, null, "Configured")]
|
||||
[InlineData(true, "stdio", true, false, null, "Disconnected")]
|
||||
[InlineData(true, "stdio", true, true, 0, "NeedsAuth (도구 0개)")]
|
||||
[InlineData(true, "stdio", true, true, 3, "Connected (도구 3개)")]
|
||||
public void ResolveMcpDisplayStatus_ShouldReturnNormalizedLabel(
|
||||
bool enabled,
|
||||
string transport,
|
||||
bool runtimeCheck,
|
||||
bool connected,
|
||||
int? toolCount,
|
||||
string expected)
|
||||
{
|
||||
var label = ChatWindow.ResolveMcpDisplayStatus(enabled, transport, runtimeCheck, connected, toolCount);
|
||||
label.Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/commit", 0, "")]
|
||||
[InlineData("feat: 로그인 오류 수정", 0, "feat: 로그인 오류 수정")]
|
||||
[InlineData("files:src/A.cs, src/B.cs :: feat: 부분 커밋", 2, "feat: 부분 커밋")]
|
||||
[InlineData("files:src/A.cs,src/A.cs", 1, "")]
|
||||
public void ParseCommitCommandInput_ShouldParseExpected(string input, int expectedFileCount, string expectedMessage)
|
||||
{
|
||||
var (files, message) = ChatWindow.ParseCommitCommandInput(input);
|
||||
|
||||
files.Count.Should().Be(expectedFileCount);
|
||||
message.Should().Be(expectedMessage);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace AxCopilot.Services.Agent;
|
||||
/// <summary>
|
||||
/// Git 버전 관리 도구.
|
||||
/// 사내 GitHub Enterprise 환경을 고려하여 안전한 Git 작업을 지원합니다.
|
||||
/// push/force push는 차단되며, 사용자가 직접 수행해야 합니다.
|
||||
/// push/force push는 차단되며, 사용자가 직접 수행해야 합니다.
|
||||
/// </summary>
|
||||
public class GitTool : IAgentTool
|
||||
{
|
||||
@@ -126,16 +126,6 @@ public class GitTool : IAgentTool
|
||||
return ToolResult.Fail("Git 쓰기 권한이 거부되었습니다.");
|
||||
}
|
||||
|
||||
// Git 커밋 — 현재 비활성 (향후 활성화 예정)
|
||||
// 의사결정 수준에서 무조건 확인을 받더라도, 커밋 자체를 차단합니다.
|
||||
if (action == "commit")
|
||||
{
|
||||
return ToolResult.Fail(
|
||||
"Git 커밋 기능은 현재 비활성 상태입니다.\n" +
|
||||
"안전을 위해 커밋은 사용자가 직접 수행하세요.\n" +
|
||||
"향후 버전에서 활성화될 예정입니다.");
|
||||
}
|
||||
|
||||
// 명령 실행
|
||||
try
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user