diff --git a/test/mock/mock_server.e2e.tests/e2e/a_to_c/ChatCommandP0RegressionCoverageTests.cs b/test/mock/mock_server.e2e.tests/e2e/a_to_c/ChatCommandP0RegressionCoverageTests.cs
new file mode 100644
index 0000000000..b66cb98e4e
--- /dev/null
+++ b/test/mock/mock_server.e2e.tests/e2e/a_to_c/ChatCommandP0RegressionCoverageTests.cs
@@ -0,0 +1,44 @@
+namespace MockServer.E2E.Tests;
+
+///
+/// P0 bug 回归锁定 E2E 覆盖测试 — TASK065 第六波
+/// 锁定第三/五波修复的生产 bug 不复发: /vendor catalog 崩溃降级、/mcp add 子命令识别、/model 别名透传直切、/effort 设置
+/// 真实 jcc 进程 + MockServer 模拟 LLM,NonInteractive 单轮形态
+///
+public sealed class ChatCommandP0RegressionCoverageTests : CoverageTestBase {
+ public ChatCommandP0RegressionCoverageTests(ITestOutputHelper output) : base(output) { }
+
+ // ============================================================
+ // Phase 6: P0 bug 回归锁定 — 2026-10-23 新增
+ // ============================================================
+
+ /// /vendor 无参 — VendorCommand catalog 崩溃修复回归(无参列出供应商)
+ [Fact]
+ public async Task VendorCommand_ShouldListVendors() {
+ await RunScriptAsync(ChatCommandConversationScripts.VendorCommand).ConfigureAwait(true);
+ }
+
+ /// /vendor deepseek — VendorCommand 切换降级修复回归(catalog 可用时正常切换)
+ [Fact]
+ public async Task VendorSwitchDeepSeekCommand_ShouldSwitchVendor() {
+ await RunScriptAsync(ChatCommandConversationScripts.VendorSwitchDeepSeekCommand).ConfigureAwait(true);
+ }
+
+ /// /mcp add testsvr — MCP add 子命令识别修复回归(输出用法提示而非未知操作)
+ [Fact]
+ public async Task McpAddUsageCommand_ShouldShowAddUsage() {
+ await RunScriptAsync(ChatCommandConversationScripts.McpAddUsageCommand).ConfigureAwait(true);
+ }
+
+ /// /model gpt-4o — ModelCommand catalog 崩溃修复回归(别名透传直切)
+ [Fact]
+ public async Task ModelSwitchCommand_ShouldSwitchModelDirectly() {
+ await RunScriptAsync(ChatCommandConversationScripts.ModelSwitchCommand).ConfigureAwait(true);
+ }
+
+ /// /effort high — EffortCommand 设置形态回归(输出力度设置成功)
+ [Fact]
+ public async Task EffortSetHighCommand_ShouldSetEffortLevel() {
+ await RunScriptAsync(ChatCommandConversationScripts.EffortSetHighCommand).ConfigureAwait(true);
+ }
+}
diff --git a/test/mock/mock_server.e2e.tests/e2e/a_to_c/ChatCommandP1CoverageTests.cs b/test/mock/mock_server.e2e.tests/e2e/a_to_c/ChatCommandP1CoverageTests.cs
new file mode 100644
index 0000000000..9b930a78b4
--- /dev/null
+++ b/test/mock/mock_server.e2e.tests/e2e/a_to_c/ChatCommandP1CoverageTests.cs
@@ -0,0 +1,39 @@
+namespace MockServer.E2E.Tests;
+
+///
+/// P1 高频命令 E2E 覆盖测试 — TASK065 第六波补覆盖
+/// 包含: /debuglog -e(错误段查询)、/??(需求澄清)、/sampling(无参查询)、/switch(无参显示模式)、/diag recent(崩溃快照查询)
+///
+public sealed class ChatCommandP1CoverageTests : CoverageTestBase {
+ public ChatCommandP1CoverageTests(ITestOutputHelper output) : base(output) { }
+
+ /// /debuglog -e — 断言错误段标题输出且无错误
+ [Fact]
+ public async Task DebugLogErrorCommand_ShouldShowErrorSection() {
+ await RunScriptAsync(ChatCommandConversationScripts.DebugLogErrorCommand).ConfigureAwait(true);
+ }
+
+ /// /?? 需求描述 — mock 响应含 ClarifyDoneMarker,断言澄清模式横幅输出且无错误
+ [Fact]
+ public async Task AskClarifyCommand_ShouldCompleteClarifyLoop() {
+ await RunScriptAsync(ChatCommandConversationScripts.AskClarifyCommand).ConfigureAwait(true);
+ }
+
+ /// /sampling 无参 — 断言采样参数查询输出且无错误
+ [Fact]
+ public async Task SamplingCommand_ShouldShowSamplingParams() {
+ await RunScriptAsync(ChatCommandConversationScripts.SamplingCommand).ConfigureAwait(true);
+ }
+
+ /// /switch 无参 — 断言当前模式输出且无错误
+ [Fact]
+ public async Task SwitchCommand_ShouldShowCurrentMode() {
+ await RunScriptAsync(ChatCommandConversationScripts.SwitchCommand).ConfigureAwait(true);
+ }
+
+ /// /diag recent — 断言崩溃快照查询输出且无错误
+ [Fact]
+ public async Task DiagRecentCommand_ShouldShowCrashSnapshotReport() {
+ await RunScriptAsync(ChatCommandConversationScripts.DiagRecentCommand).ConfigureAwait(true);
+ }
+}
diff --git a/test/mock/mock_server.e2e.tests/e2e/a_to_c/ChatCommandP2IsolatedCoverageTests.cs b/test/mock/mock_server.e2e.tests/e2e/a_to_c/ChatCommandP2IsolatedCoverageTests.cs
new file mode 100644
index 0000000000..d2c84f78b9
--- /dev/null
+++ b/test/mock/mock_server.e2e.tests/e2e/a_to_c/ChatCommandP2IsolatedCoverageTests.cs
@@ -0,0 +1,84 @@
+namespace MockServer.E2E.Tests;
+
+///
+/// P2 隔离写命令 E2E 覆盖测试 — TASK065 第六波补覆盖
+/// 包含: /reset-config all(JCC_APP_DATA_FOLDER 隔离目录重置)、/code-index(fixture 目录索引重建)
+/// 隔离验证: /reset-config 写入目标为 runner 设置的 %TEMP%\jcc_test_{guid}(Path.Combine 对绝对路径直接采用),不碰真实 ~/.jcc
+///
+public sealed class ChatCommandP2IsolatedCoverageTests : CoverageTestBase {
+ public ChatCommandP2IsolatedCoverageTests(ITestOutputHelper output) : base(output) { }
+
+ /// /reset-config all — 断言隔离 AppData 配置重置输出(重置开始/配置树骨架/重置完成)且无错误
+ [Fact]
+ public async Task ResetConfigAllCommand_ShouldResetIsolatedAppData() {
+ await RunScriptAsync(ChatCommandConversationScripts.ResetConfigAllCommand).ConfigureAwait(true);
+ }
+
+ /// /code-index — 断言 fixture 目录索引构建完成(构建开始/完成提示/统计输出)且无错误
+ [Fact]
+ public async Task CodeIndexCommand_ShouldBuildIndexInFixtureDir() {
+ var fixtureDir = await CreateCodeIndexFixtureAsync().ConfigureAwait(true);
+ ChatCommandConversationScripts.CodeIndexFixtureDirectory = fixtureDir;
+ try {
+ await RunScriptAsync(ChatCommandConversationScripts.CodeIndexCommand).ConfigureAwait(true);
+ } finally {
+ ChatCommandConversationScripts.CodeIndexFixtureDirectory = null;
+ CleanupCodeIndexFixture(fixtureDir);
+ }
+ }
+
+ ///
+ /// 创建 /code-index fixture 目录 — %TEMP%\jcc_e2e_codeindex_{guid} + 2 个小 .cs 文件
+ /// 小目录控制 RebuildIndexAsync 耗时(Ast 集成测试 WorkingDirectory 指向 fixture 的先例)
+ ///
+ /// fixture 目录绝对路径
+ private static async Task CreateCodeIndexFixtureAsync() {
+ var fs = TestConfiguration.FileSystem;
+ var fixtureDir = fs.CombinePath(Path.GetTempPath(), $"jcc_e2e_codeindex_{Guid.NewGuid():N}");
+ fs.CreateDirectory(fixtureDir);
+ await fs.WriteAllTextAsync(fs.CombinePath(fixtureDir, "Calculator.cs"), CalculatorFixtureSource).ConfigureAwait(true);
+ await fs.WriteAllTextAsync(fs.CombinePath(fixtureDir, "Greeter.cs"), GreeterFixtureSource).ConfigureAwait(true);
+ return fixtureDir;
+ }
+
+ ///
+ /// 清理 fixture 目录(含索引产物 {fixture}/.jcc/code-index/)— %TEMP% 下测试自建产物
+ /// 清理失败不阻断测试结果(%TEMP% 残留无害,同 DualRoleConversationRunner 清理临时目录先例)
+ ///
+ /// fixture 目录绝对路径
+ private void CleanupCodeIndexFixture(string fixtureDir) {
+ try {
+ var fs = TestConfiguration.FileSystem;
+ if (fs.DirectoryExists(fixtureDir)) {
+ fs.DeleteDirectory(fixtureDir, recursive: true);
+ }
+ } catch (Exception ex) {
+ Output.WriteLine($"[P2] ⚠ fixture 清理失败(%TEMP% 残留无害): {ex.Message}");
+ }
+ }
+
+ /// fixture 源码 1 — 计算器(含类/方法符号,供索引器解析)
+ private const string CalculatorFixtureSource = """
+ namespace CodeIndexFixture;
+
+ /// fixture 计算器 — /code-index E2E 索引目标
+ public static class Calculator {
+ /// 两数相加
+ public static int Add(int a, int b) => a + b;
+
+ /// 两数相乘
+ public static int Multiply(int a, int b) => a * b;
+ }
+ """;
+
+ /// fixture 源码 2 — 问候器(含类/方法符号,供索引器解析)
+ private const string GreeterFixtureSource = """
+ namespace CodeIndexFixture;
+
+ /// fixture 问候器 — /code-index E2E 索引目标
+ public static class Greeter {
+ /// 生成问候语
+ public static string Greet(string name) => $"Hello, {name}!";
+ }
+ """;
+}
diff --git a/test/mock/mock_server.e2e.tests/scripts/ChatCommandConversationScripts.Phase6.cs b/test/mock/mock_server.e2e.tests/scripts/ChatCommandConversationScripts.Phase6.cs
new file mode 100644
index 0000000000..e8312e779b
--- /dev/null
+++ b/test/mock/mock_server.e2e.tests/scripts/ChatCommandConversationScripts.Phase6.cs
@@ -0,0 +1,139 @@
+namespace MockServer.E2E.Tests.Scripts;
+
+///
+/// Phase 6 E2E 脚本 — TASK065 第六波 P0 bug 回归锁定
+/// 锁定第三/五波修复的 4 类 bug: /vendor catalog 崩溃降级、/mcp add 子命令识别、/model 别名透传直切、/effort max 降级
+/// 断言文案全部对齐命令源码真实输出(VendorCommand/McpCommand/ModelCommand/EffortCommand)
+///
+public static partial class ChatCommandConversationScripts {
+ ///
+ /// /vendor 命令(无参) — 列出可用供应商
+ /// 对齐 VendorCommand.cs 无参分支: "=== 可用供应商 ===" + 枚举值逐行输出(" openai ← 当前")
+ ///
+ public static ConversationScript VendorCommand => new() {
+ Name = "/vendor 命令(无参列表)",
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/vendor",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = ""
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "可用供应商", Description = "应输出可用供应商列表标题" },
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "openai", Description = "列表应包含 openai 供应商(VendorKind 枚举遍历)" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "不应有错误" },
+ ]
+ }
+ ]
+ };
+
+ ///
+ /// /vendor deepseek 命令 — 切换供应商
+ /// 对齐 VendorCommand.cs 切换分支: "已切换供应商: openai → deepseek"(E2E 环境 JCC_VENDOR=openai,catalog 全装配不崩溃)
+ ///
+ public static ConversationScript VendorSwitchDeepSeekCommand => new() {
+ Name = "/vendor deepseek 命令(切换供应商)",
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/vendor deepseek",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = ""
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "已切换供应商", Description = "应输出切换成功文案" },
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "deepseek", Description = "目标供应商 deepseek 应出现在切换输出中" },
+ new OutputAssert { Type = AssertType.NotContainsText, Expected = "未知供应商", Description = "deepseek 在 VendorKind 枚举内,不应触发未知供应商拒绝" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "切换不应有错误(catalog 崩溃修复回归锁定)" },
+ ]
+ }
+ ]
+ };
+
+ ///
+ /// /mcp add 命令(缺少 url/command 参数) — AddServerAsync 用法提示分支
+ /// 对齐 McpCommand.cs AddServerAsync: args.Length<3 时输出 "用法: /mcp add ..."
+ /// 锁定 CrudAction.Add 识别修复 — 修复前 add 走 default 分支输出 "未知操作: add"
+ ///
+ public static ConversationScript McpAddUsageCommand => new() {
+ Name = "/mcp add 命令(用法提示)",
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/mcp add testsvr",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = ""
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "用法: /mcp add", Description = "应输出 add 子命令用法提示(AddServerAsync 分支)" },
+ new OutputAssert { Type = AssertType.NotContainsText, Expected = "未知操作", Description = "add 应被 CrudAction.Add case 识别,不应走 default 未知操作分支(bug 回归锁定)" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "不应有错误" },
+ ]
+ }
+ ]
+ };
+
+ ///
+ /// /model 命令(带参直切) — 别名透传直切
+ /// 对齐 ModelCommand.cs SwitchToModelAsync: "已切换模型: gpt-4o"(E2E settings.json openai 无别名表,ResolveAlias 返回 null 透传输入)
+ ///
+ public static ConversationScript ModelSwitchCommand => new() {
+ Name = "/model gpt-4o 命令(带参直切)",
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/model gpt-4o",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = ""
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "已切换模型: gpt-4o", Description = "应输出直切成功文案(catalog 可用,别名透传)" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "切换不应有错误(catalog 崩溃修复回归锁定)" },
+ ]
+ }
+ ]
+ };
+
+ ///
+ /// /effort 命令(设置形态) — 设置推理力度 high
+ /// 对齐 EffortCommand.cs 设置分支: "推理力度: high (全面,广泛测试)"
+ ///
+ public static ConversationScript EffortSetHighCommand => new() {
+ Name = "/effort high 命令(设置推理力度)",
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/effort high",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = ""
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "推理力度: high", Description = "应输出 high 力度设置成功文案" },
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "全面,广泛测试", Description = "应输出 high 等级对应描述(GetEffortDescription)" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "不应有错误" },
+ ]
+ }
+ ]
+ };
+}
diff --git a/test/mock/mock_server.e2e.tests/scripts/ChatCommandConversationScripts.Phase7.cs b/test/mock/mock_server.e2e.tests/scripts/ChatCommandConversationScripts.Phase7.cs
new file mode 100644
index 0000000000..4baa07c9d7
--- /dev/null
+++ b/test/mock/mock_server.e2e.tests/scripts/ChatCommandConversationScripts.Phase7.cs
@@ -0,0 +1,134 @@
+namespace MockServer.E2E.Tests.Scripts;
+
+///
+/// Phase 7 E2E 脚本 — P1 高频命令补覆盖(TASK065 第六波)
+/// 包含: /debuglog -e、/??(需求澄清)、/sampling(无参查询)、/switch(无参显示模式)、/diag recent
+///
+public static partial class ChatCommandConversationScripts {
+ ///
+ /// /debuglog -e 命令 — 仅显示错误段
+ /// DebugLogRenderer.AppendErrors 固定输出 "═══ 错误 ═══" 段落标题;无错误时追加 "无错误记录"
+ ///
+ public static ConversationScript DebugLogErrorCommand => new() {
+ Name = "/debuglog -e 命令",
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/debuglog -e",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = ""
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "错误", Description = "应包含错误段标题(═══ 错误 ═══)" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "不应有错误" },
+ ]
+ }
+ ]
+ };
+
+ ///
+ /// /?? 命令 — 需求澄清模式(AskClarify)
+ /// 关键: mock 响应文本必须含 ClarifyDoneMarker("【需求已明确】"),否则澄清循环进 ReadLine 卡 60s 超时
+ /// 命令固定输出 "╔══ 需求澄清模式 ══╗" 横幅;响应含标记时输出 "✓ 需求澄清完成" 后退出子循环
+ ///
+ public static ConversationScript AskClarifyCommand => new() {
+ Name = "/?? 需求澄清命令",
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/?? 帮我写一个待办事项应用",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = "【需求已明确】需求总结:开发一个本地待办事项应用,支持增删改查与完成状态标记。"
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "需求澄清", Description = "应包含需求澄清模式横幅" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "不应有错误" },
+ ]
+ }
+ ]
+ };
+
+ ///
+ /// /sampling 命令 — 无参查询当前采样参数
+ /// SamplingCommand 无参时输出 "采样参数: 温度 {x}, 最大 {y} tokens"
+ ///
+ public static ConversationScript SamplingCommand => new() {
+ Name = "/sampling 命令",
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/sampling",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = ""
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "采样参数", Description = "应包含采样参数查询输出" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "不应有错误" },
+ ]
+ }
+ ]
+ };
+
+ ///
+ /// /switch 命令 — 无参显示当前模式
+ /// SwitchCommand 无参时 ShowCurrentMode 输出 "当前模式: 显示全部子代理输出"(默认)及活跃子代理列表
+ ///
+ public static ConversationScript SwitchCommand => new() {
+ Name = "/switch 命令",
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/switch",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = ""
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "当前模式", Description = "应包含当前模式输出" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "不应有错误" },
+ ]
+ }
+ ]
+ };
+
+ ///
+ /// /diag recent 命令 — 崩溃快照查询
+ /// E2E 隔离目录快照为空时 FormatReport 返回 "无崩溃快照记录。";非空时输出 "崩溃快照报告(最近 …)"
+ /// 两种输出均含 "崩溃快照",据此断言查询正常执行
+ ///
+ public static ConversationScript DiagRecentCommand => new() {
+ Name = "/diag recent 命令",
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/diag recent",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = ""
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "崩溃快照", Description = "应包含崩溃快照查询输出" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "不应有错误" },
+ ]
+ }
+ ]
+ };
+}
diff --git a/test/mock/mock_server.e2e.tests/scripts/ChatCommandConversationScripts.Phase8.cs b/test/mock/mock_server.e2e.tests/scripts/ChatCommandConversationScripts.Phase8.cs
new file mode 100644
index 0000000000..21572df603
--- /dev/null
+++ b/test/mock/mock_server.e2e.tests/scripts/ChatCommandConversationScripts.Phase8.cs
@@ -0,0 +1,77 @@
+namespace MockServer.E2E.Tests.Scripts;
+
+///
+/// Phase 8 E2E 脚本 — P2 隔离写命令补覆盖(TASK065 第六波)
+/// 包含: /reset-config all(JCC_APP_DATA_FOLDER 隔离目录重置)、/code-index(小 fixture 目录控索引耗时)
+///
+public static partial class ChatCommandConversationScripts {
+ ///
+ /// /code-index fixture 目录 — 由测试类创建/清理,脚本属性消费
+ /// 指向 %TEMP% 下含 2 个小 .cs 文件的临时目录,控制 RebuildIndexAsync 耗时
+ ///
+ internal static string? CodeIndexFixtureDirectory { get; set; }
+
+ ///
+ /// /reset-config all 命令 — 重置隔离 AppData 配置(单轮)
+ /// 隔离链路: DualRoleConversationRunner 设 JCC_APP_DATA_FOLDER=%TEMP%\jcc_test_{guid},
+ /// AppDataPaths.Default 解析为绝对路径后 ResetConfigCommand 的 Path.Combine(UserProfile, 绝对路径) 直接采用绝对路径 → 写入隔离目录,不碰真实 ~/.jcc
+ /// 断言 ResetAllAsync 固定输出: "重置所有配置文件..."(首行)、"配置树已重置为默认骨架"(settings 重置)、"✓ 所有配置已重置"(收尾,颜色码不影响子串匹配)
+ ///
+ public static ConversationScript ResetConfigAllCommand => new() {
+ Name = "/reset-config all 命令",
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/reset-config all",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = ""
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "重置所有配置文件", Description = "应包含重置开始提示(ResetAllAsync 首行)" },
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "配置树已重置为默认骨架", Description = "应包含配置树重置输出(settings.json + current/ + providers_llm/ + providers_search/)" },
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "所有配置已重置", Description = "应包含重置完成提示(✓ 所有配置已重置)" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "不应有错误" },
+ ]
+ }
+ ]
+ };
+
+ ///
+ /// /code-index 命令 — 显式重建 fixture 目录代码索引(单轮)
+ /// WorkingDirectory 指向小 fixture(2 个 .cs 文件)控制索引耗时(Ast 集成测试 WorkingDirectory 先例);
+ /// 断言 CodeIndexCommand 固定输出: "正在构建代码索引..."(无条件首行)、"代码索引构建完成"(成功路径)、"文件数"(GetStatsAsync 统计行)
+ ///
+ /// CodeIndexFixtureDirectory 未就绪时抛出 — 测试类须先设置 fixture 目录再访问本脚本
+ public static ConversationScript CodeIndexCommand => new() {
+ Name = "/code-index 命令",
+ WorkingDirectory = CodeIndexFixtureDirectory
+ ?? throw new InvalidOperationException(
+ "[E2E-FIXTURE] /code-index 脚本的 CodeIndexFixtureDirectory 未就绪。" +
+ "触发原因: 测试类未先创建 fixture 目录。" +
+ "拦截位置: ChatCommandConversationScripts.CodeIndexCommand 属性 getter。" +
+ "正确写法: 先在 ChatCommandP2IsolatedCoverageTests 中调用 CreateCodeIndexFixtureAsync() 并赋值 CodeIndexFixtureDirectory,再访问本脚本。"),
+ Turns =
+ [
+ new ConversationTurn
+ {
+ UserInput = "/code-index",
+ AiResponse = new MockResponseScript
+ {
+ Type = MockResponseType.TextOnly,
+ TextResponse = ""
+ },
+ Asserts =
+ [
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "正在构建代码索引", Description = "应包含索引构建开始提示(无条件输出)" },
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "代码索引构建完成", Description = "应包含索引构建完成提示(含耗时 ms)" },
+ new OutputAssert { Type = AssertType.ContainsText, Expected = "文件数", Description = "应包含索引统计输出(文件数/符号数/调用边/依赖边/项目数)" },
+ new OutputAssert { Type = AssertType.NoErrors, Expected = "", Description = "不应有错误" },
+ ]
+ }
+ ]
+ };
+}