@ghyper9023/pi-dev-workflow 0.6.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.github/workflows/release.yml +56 -0
  2. package/.pre-commit-config.yaml +75 -0
  3. package/.version/RELEASE-v0.7.0.md +85 -0
  4. package/.version/RELEASE-v0.8.0.md +93 -0
  5. package/README.md +178 -294
  6. package/extensions/dev-prompts.ts +121 -1033
  7. package/extensions/git-commands.ts +62 -171
  8. package/extensions/grill-me-agent.ts +160 -164
  9. package/extensions/pre-check.ts +190 -0
  10. package/extensions/review-detect.ts +123 -0
  11. package/extensions/session-utils.ts +168 -0
  12. package/extensions/ui-helpers.ts +22 -780
  13. package/package.json +2 -2
  14. package/prompts/APPEND_SYSTEM.md +73 -59
  15. package/skills/review-html/SKILL.md +1 -1
  16. package/tests/test-no-subagents.mjs +264 -0
  17. package/.doc/AGENT-FRONTMATTER-REFERENCE.md +0 -198
  18. package/agents/git-agent.md +0 -44
  19. package/agents/grill/dev-doc-grill-agent.md +0 -42
  20. package/agents/grill/dev-fix-grill-agent.md +0 -44
  21. package/agents/grill/dev-grill-agent.md +0 -40
  22. package/agents/grill/dev-perf-grill-agent.md +0 -45
  23. package/agents/grill/dev-prd-agent.md +0 -60
  24. package/agents/grill/dev-refactor-grill-agent.md +0 -46
  25. package/agents/grill/dev-test-grill-agent.md +0 -45
  26. package/agents/review-agent.md +0 -53
  27. package/agents/workflow/docWriter-agent.md +0 -53
  28. package/agents/workflow/planner-agent.md +0 -131
  29. package/agents/workflow/reviewer-agent.md +0 -128
  30. package/agents/workflow/trimmer-agent.md +0 -78
  31. package/agents/workflow/worker-agent.md +0 -70
  32. package/extensions/sub-agents.ts +0 -954
  33. package/extensions/workflow-engine.ts +0 -2005
  34. package/tests/test-grill-json-fix.mjs +0 -243
  35. package/tests/test-loopcount-timeout-fix.mjs +0 -336
  36. package/tests/test-output-directory-structure.mjs +0 -177
  37. package/tests/test-save-answer-file-workflow.mjs +0 -187
  38. package/tests/test-workflow-config.mjs +0 -244
  39. package/tests/test-workflow-engine-bugs.mjs +0 -908
  40. package/tests/test-workflow-engine.mjs +0 -518
@@ -1,1058 +1,146 @@
1
1
  /**
2
2
  * Dev Prompts Extension
3
3
  *
4
- * Registers /dev-* commands that interactively collect missing context for
5
- * high-quality prompt templates (from ai提示词优化.md) and send the assembled
6
- * prompt directly to the main agent.
4
+ * 四个命令:/dev-feat、/dev-fix、/dev-refactor、/dev-test。
7
5
  *
8
- * Commands:
9
- * /dev-feat - New feature / creative generation
10
- * /dev-fix - Bug fix / error troubleshooting
11
- * /dev-doc - Documentation generation
12
- * /dev-refactor - Code refactoring
13
- * /dev-test - Test case generation
14
- * /dev-chore - Maintenance / automation
15
- * /dev-perf - Performance optimization
16
- * /dev-style - Style / format adjustment
17
- * /dev-security - Security review
18
- * /dev-explain - Concept explanation
19
- * /dev-compare - Comparison evaluation
6
+ * 每个命令的流程一致:
7
+ * 1. 命令参数就是任务原文;不带参数时弹一次必填输入框
8
+ * 2. 复用 pre-check 的意图确认循环:代理先复述目标与问题,用户确认后才继续
9
+ * 3. 提示词只补两件事:AI 的身份与职责、未说明时的默认收尾验收标准
10
+ * —— 任务目标来自第 2 步已确认的意图,不在这里重新解释需求
11
+ * 4. 投递给当前代理执行
20
12
  *
21
- * Usage: type /dev-<type> and follow the wizard.
22
- * Leave a field empty (Enter) to skip its section.
23
- * Press Esc to cancel the entire wizard.
13
+ * 追问完善(/grill)与 PRD(/prd)是独立命令,不由这里触发。
24
14
  */
25
15
 
26
16
  import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
27
- import { runGrillPhase, runPRDPhase, saveAnswerFile, recoverFromBackup, type GrillOptions } from "./grill-me-agent";
28
- import { discoverAgents } from "./sub-agents";
29
- import { runWorkflow, loadCheckpointFromFile, type WorkflowStepDef } from "./workflow-engine";
30
- import { uiSelect, uiConfirm, uiInput, BACK_MARKER } from "./ui-helpers";
17
+ import { confirmIntent } from "./pre-check";
18
+ import { detectProjectDefaults, defaultAcceptanceItems, type ProjectDefaults } from "./session-utils";
19
+ import { uiTaskArg } from "./ui-helpers";
31
20
 
32
- // ── Helpers ──────────────────────────────────────────────────
33
-
34
- /** Ask a single question with proper wrapping. Returns `undefined` on cancel (Esc), or BACK_MARKER for back. */
35
- async function ask(
36
- ctx: ExtensionCommandContext,
37
- label: string,
38
- placeholder: string,
39
- backable = false,
40
- initialValue = "",
41
- ): Promise<string | undefined> {
42
- return uiInput(ctx, label, placeholder, false, backable, initialValue);
43
- }
44
-
45
- /** Check if a field value is empty or explicitly "无". */
46
- function isEmpty(val: string | undefined): boolean {
47
- return !val || val.trim() === "" || val.trim() === "无";
48
- }
49
-
50
- /** Wrap a non-empty value for template insertion. */
51
- function wrap(val: string | undefined, fallback = "..."): string {
52
- if (isEmpty(val)) return fallback;
53
- return val!.trim();
54
- }
55
-
56
- // ── Template Assemblers ──────────────────────────────────────
57
-
58
- interface FeatFields {
59
- language: string;
60
- techStack: string;
61
- module: string;
21
+ interface DevType {
62
22
  description: string;
63
- painPoint: string;
64
- testCmd: string;
65
- }
66
-
67
- function assembleFeatPrompt(f: FeatFields): string {
68
- const lines: string[] = [];
69
- lines.push(`[feat] 在 ${wrap(f.module)} 中实现 ${wrap(f.description)}`);
70
- lines.push("");
71
- lines.push(`**角色**:你是一个资深 ${wrap(f.language)} 工程师。`);
72
- if (!isEmpty(f.techStack) || !isEmpty(f.painPoint)) {
73
- lines.push(
74
- `**背景**:项目使用 ${wrap(f.techStack)},当前缺少 ${wrap(f.description)}` +
75
- (isEmpty(f.painPoint) ? "。" : `,用户痛点是 ${f.painPoint!.trim()}。`),
76
- );
77
- }
78
- lines.push("**任务**:");
79
- lines.push("1. 先分析代码库结构,给出逐步实施计划(列出要修改/创建的文件、数据库迁移、对现有代码的假设)。");
80
- lines.push("2. 计划经我确认后再编写代码。");
81
- if (!isEmpty(f.testCmd)) {
82
- lines.push(`3. 实现后编写测试用例验证核心逻辑,并运行 ${f.testCmd!.trim()} 确认通过。`);
83
- } else {
84
- lines.push("3. 实现后编写测试用例验证核心逻辑。");
85
- }
86
- lines.push("**输出**:提供 unified diff 和两句话的变更说明。");
87
- lines.push("**约束**:禁止顺手重构无关代码;保持所有公共 API 签名兼容;不要为假设性需求添加抽象层。");
88
- if (!isEmpty(f.testCmd)) {
89
- lines.push(`**验证**:运行 ${f.testCmd!.trim()} 确保无回归。`);
90
- }
91
- return lines.join("\n");
92
- }
93
-
94
- interface FixFields {
95
- filePath: string;
96
- lineNumber: string;
97
- bugDesc: string;
98
- inputDesc: string;
99
- expected: string;
100
- actualError: string;
101
- testCmd: string;
102
- }
103
-
104
- function assembleFixPrompt(f: FixFields): string {
105
- const lines: string[] = [];
106
- const loc = isEmpty(f.lineNumber) ? f.filePath.trim() : `${f.filePath.trim()} #L${f.lineNumber!.trim()}`;
107
- lines.push(`[fix] 修复 ${loc} 中的 ${wrap(f.bugDesc)}`);
108
- lines.push("");
109
- lines.push("**背景**:");
110
- lines.push(`- 输入:${isEmpty(f.inputDesc) ? "见代码上下文" : f.inputDesc!.trim()}`);
111
- lines.push(`- 预期行为:${wrap(f.expected, "请描述预期结果")}`);
112
- lines.push(`- 当前错误:${wrap(f.actualError, "请描述当前错误")}`);
113
- lines.push("**任务**:");
114
- lines.push("1. 不要仅仅消除报错(Suppress),要解决根本原因。");
115
- lines.push("2. 先读取相关代码和日志,诊断根因(多步推理,不要先给结论)。");
116
- lines.push("3. 提供至少一种修复方案,并说明为什么这样做。");
117
- lines.push("4. 编写测试用例复现该 Bug 并确认修复有效。");
118
- lines.push("**输出**:提供 diff 和两句话的根因分析。");
119
- lines.push("**约束**:只修 bug,不做重构;最小化改动;不要假设错误是微不足道的。");
120
- if (!isEmpty(f.testCmd)) {
121
- lines.push(`**验证**:运行 ${f.testCmd!.trim()} 确认修复。`);
122
- }
123
- return lines.join("\n");
124
- }
125
-
126
- interface DocFields {
127
- moduleName: string;
128
- audience: string;
129
- keyInfo: string;
130
- language: string;
131
- existingMaterial: string;
132
- }
133
-
134
- function assembleDocPrompt(f: DocFields): string {
135
- const lines: string[] = [];
136
- lines.push(`[doc] 为 ${wrap(f.moduleName)} 撰写一份文档`);
137
- lines.push("");
138
- lines.push("**角色**:你是一位技术文档工程师。");
139
- if (!isEmpty(f.audience) || !isEmpty(f.keyInfo)) {
140
- lines.push(`**背景**:目标受众是 ${wrap(f.audience)},他们需要了解 ${wrap(f.keyInfo)}。`);
141
- }
142
- lines.push("**任务**:");
143
- lines.push("1. 提取核心要点,按逻辑结构重组(概述 → 快速开始 → 详细说明 → 常见问题)。");
144
- lines.push(`2. 添加至少 1 个真实可运行的示例(使用 ${wrap(f.language)} 语法高亮)。`);
145
- lines.push("3. 如存在争议点,列出不同观点并注明\"无共识\"。");
146
- if (!isEmpty(f.existingMaterial)) {
147
- lines.push(`**已有材料**:${f.existingMaterial!.trim()}`);
148
- }
149
- lines.push("**输出格式**:Markdown 层级标题,必要时插入表格/列表。");
150
- lines.push("**约束**:避免空洞词汇(如\"细致入微\"\"深入探究\");每段都应有实质信息;保持原意,不添加原文没有的事实。");
151
- lines.push("**验证**:请先提供大纲,经我确认后再扩展。");
152
- return lines.join("\n");
153
- }
154
-
155
- interface RefactorFields {
156
- filePath: string;
157
- lineCount: string;
158
- problems: string;
159
- goal: string;
160
- testCmd: string;
161
- }
162
-
163
- function assembleRefactorPrompt(f: RefactorFields): string {
164
- const lines: string[] = [];
165
- const loc = isEmpty(f.lineCount) ? f.filePath.trim() : `${f.filePath.trim()}(约 ${f.lineCount!.trim()} 行)`;
166
- lines.push(`[refactor] 对 ${loc} 进行重构,提升 ${wrap(f.goal, "可读性 / 可维护性")}`);
167
- lines.push("");
168
- lines.push(`**背景**:当前代码存在 ${wrap(f.problems)}。`);
169
- lines.push("**任务**:");
170
- lines.push("1. 识别主要问题。");
171
- lines.push("2. 提出重构方案,说明改动前后差异。");
172
- lines.push("3. 输出重构后的完整版本。");
173
- lines.push("**硬性约束**:");
174
- lines.push("- 不改变任何行为,保留所有公共 API 签名不变。");
175
- lines.push("- 禁止顺手优化、禁止添加新功能、禁止修改业务逻辑。");
176
- if (!isEmpty(f.testCmd)) {
177
- lines.push(`- 拆分后运行 ${f.testCmd!.trim()} 确认无回归。`);
178
- }
179
- lines.push("**输出**:提供 diff 和新模块的依赖关系图。");
180
- if (!isEmpty(f.testCmd)) {
181
- lines.push(`**验证**:运行 ${f.testCmd!.trim()} 并确保全部通过。`);
182
- }
183
- return lines.join("\n");
184
- }
185
-
186
- interface TestFields {
187
- filePath: string;
188
- framework: string;
189
- coverage: string;
190
- edgeCases: string;
191
- testCmd: string;
192
- }
193
-
194
- function assembleTestPrompt(f: TestFields): string {
195
- const lines: string[] = [];
196
- lines.push(`[test] 为 ${wrap(f.filePath)} 中的变更生成表驱动测试`);
197
- lines.push("");
198
- lines.push(`**角色**:你是一个资深测试工程师。`);
199
- lines.push(`**背景**:使用 ${wrap(f.framework)} 框架,追求 ≥${wrap(f.coverage, "90")}% 分支覆盖率。`);
200
- lines.push("**任务**:");
201
- lines.push(`1. 覆盖维度:${isEmpty(f.edgeCases) ? "null 值、空值、超时、幂等性、重试、成功路径、4xx/5xx 错误、边界条件" : f.edgeCases!.trim()}。`);
202
- lines.push("2. 优先让测试先失败(红),再提供补丁使其通过(绿)。");
203
- lines.push("**输出格式**:表格列出场景 → 预期结果 → 权重,末尾附评分模板。");
204
- lines.push("**约束**:评分准则必须无歧义;不要假设输入总是合法的。");
205
- if (!isEmpty(f.testCmd)) {
206
- lines.push(`**验证**:运行 ${f.testCmd!.trim()} 并展示覆盖率报告。`);
207
- }
208
- return lines.join("\n");
209
- }
210
-
211
- interface ChoreFields {
212
- configPath: string;
213
- task: string;
214
- envDesc: string;
215
- targetVersion: string;
216
- verifyCmd: string;
217
- }
218
-
219
- function assembleChorePrompt(f: ChoreFields): string {
220
- const lines: string[] = [];
221
- lines.push(`[chore] 在 ${wrap(f.configPath)} 中 ${wrap(f.task)}`);
222
- lines.push("");
223
- lines.push("**角色**:你是一个 DevOps 工程师。");
224
- lines.push(`**背景**:当前环境 ${wrap(f.envDesc)},目标版本 ${wrap(f.targetVersion)}。`);
225
- lines.push("**任务**:");
226
- lines.push(`1. 只做 ${wrap(f.task)},不做任何其他改动。`);
227
- if (!isEmpty(f.verifyCmd)) {
228
- lines.push(`2. 改动后运行 ${f.verifyCmd!.trim()} 确认无破坏性变更。`);
229
- }
230
- lines.push("**硬性约束**:");
231
- lines.push("- NEVER 修改生产环境配置文件(如 config/production.yml)。");
232
- lines.push("- NEVER 运行任何部署命令(除非用户明确要求)。");
233
- lines.push("- 禁止顺手升级无关依赖、禁止修改代码逻辑。");
234
- lines.push("**输出**:提供变更前后对比和影响说明。");
235
- if (!isEmpty(f.verifyCmd)) {
236
- lines.push(`**验证**:运行 ${f.verifyCmd!.trim()} 并展示结果。`);
237
- }
238
- return lines.join("\n");
239
- }
240
-
241
- interface PerfFields {
242
- filePath: string;
243
- bottleneck: string;
244
- currentCost: string;
245
- targetLatency: string;
246
- benchCmd: string;
247
- }
248
-
249
- function assemblePerfPrompt(f: PerfFields): string {
250
- const lines: string[] = [];
251
- lines.push(`[perf] 优化 ${wrap(f.filePath)} 中的 ${wrap(f.bottleneck)}`);
252
- lines.push("");
253
- lines.push("**角色**:你是一位性能优化专家。");
254
- lines.push(`**背景**:当前执行耗时约 ${wrap(f.currentCost)},用户可接受的延迟为 ${wrap(f.targetLatency)}。`);
255
- lines.push("**任务**:");
256
- lines.push("1. Think deeply about this performance issue.");
257
- lines.push("2. 先分析当前性能数据,给出基准指标。");
258
- lines.push("3. 列出 ≥2 种优化方案,分析每个方案的预估提升幅度、实现复杂度、潜在风险。");
259
- if (!isEmpty(f.benchCmd)) {
260
- lines.push(`4. 选择推荐方案并实现。优化后运行 ${f.benchCmd!.trim()} 对比前后数据。`);
261
- } else {
262
- lines.push("4. 选择推荐方案并实现。优化后运行基准测试对比前后数据。");
263
- }
264
- lines.push("**输出**:提供 before/after 性能对比表格。");
265
- lines.push("**约束**:不要牺牲核心准确性;优先给出低风险改动;不为了微优化牺牲可读性。");
266
- return lines.join("\n");
267
- }
268
-
269
- interface StyleFields {
270
- targetStyle: string;
271
- description: string;
272
- terms: string;
273
- lintCmd: string;
274
- }
275
-
276
- function assembleStylePrompt(f: StyleFields): string {
277
- const lines: string[] = [];
278
- lines.push(`[style] 将以下内容调整为 ${wrap(f.targetStyle)}`);
279
- lines.push("");
280
- lines.push("**角色**:你是一个代码风格专家。");
281
- lines.push(`**原文**:${wrap(f.description, "(见当前上下文)")}`);
282
- lines.push("**任务**:");
283
- lines.push("1. 保持原意和信息完整,仅改变表达风格/代码格式。");
284
- if (!isEmpty(f.terms)) {
285
- lines.push(`2. 术语统一为:${f.terms!.trim()}。`);
286
- lines.push("3. 输出两种备选风格供我对比。");
287
- } else {
288
- lines.push("2. 输出两种备选风格供我对比。");
289
- }
290
- lines.push("**约束**:不要添加原文没有的新事实,不要改变关键数据和逻辑;同时指出原文中可能存在的歧义表达。");
291
- if (!isEmpty(f.lintCmd)) {
292
- lines.push(`**验证**:对代码运行 ${f.lintCmd!.trim()} 确保符合规范。`);
293
- }
294
- return lines.join("\n");
295
- }
296
-
297
- interface SecurityFields {
298
- filePath: string;
299
- focus: string;
300
- }
301
-
302
- function assembleSecurityPrompt(f: SecurityFields): string {
303
- const lines: string[] = [];
304
- lines.push(`[security] 对 ${wrap(f.filePath)} 运行安全审查`);
305
- lines.push("");
306
- lines.push("**角色**:你是一名安全审计专家(独立于编写代码的 Agent)。");
307
- lines.push("**任务**:");
308
- lines.push(`1. 审查清单:${isEmpty(f.focus) ? "认证边界、注入漏洞、敏感数据暴露、CSRF/CORS 配置、权限校验缺失" : f.focus!.trim()}。`);
309
- lines.push("2. 提供带行号的修复方案及理由。");
310
- lines.push("3. 只审查不修改,输出审查报告。");
311
- lines.push("**硬性约束**:在隔离上下文中运行,不继承主 Agent 的记忆。");
312
- lines.push("**输出**:Markdown 报告,每个问题包含严重级别、行号、风险描述、修复建议。");
313
- return lines.join("\n");
314
- }
315
-
316
- interface ExplainFields {
317
- concept: string;
318
- audience: string;
319
- depth: string;
320
- }
321
-
322
- function assembleExplainPrompt(f: ExplainFields): string {
323
- const lines: string[] = [];
324
- lines.push(`[explain] 解释 ${wrap(f.concept)}`);
325
- lines.push("");
326
- lines.push("**角色**:你是一位资深技术导师,擅长用类比引导初学者理解复杂概念。");
327
- lines.push(`**背景**:目标受众是 ${wrap(f.audience)},需要理解 ${wrap(f.concept)} 的 ${wrap(f.depth, "基础")} 层面。`);
328
- lines.push("**任务**:");
329
- lines.push("1. 用生活化的类比引入概念,建立直觉。");
330
- lines.push("2. 由浅入深逐步展开,先给大局观再进入细节。");
331
- lines.push("3. 提供至少一个真实世界的应用场景。");
332
- lines.push("4. 如有常见误区,明确指出。");
333
- lines.push("**输出格式**:Markdown,必要时插入图示描述。");
334
- lines.push("**验证**:请先给出一句话总结,经我确认后再展开。");
335
- return lines.join("\n");
336
- }
337
-
338
- interface CompareFields {
339
- itemA: string;
340
- itemB: string;
341
- dimensions: string;
342
- }
343
-
344
- function assembleComparePrompt(f: CompareFields): string {
345
- const lines: string[] = [];
346
- lines.push(`[compare] 对比 ${wrap(f.itemA)} 与 ${wrap(f.itemB)}`);
347
- lines.push("");
348
- lines.push("**角色**:你是一位客观中立的评测专家。");
349
- lines.push(`**背景**:需要从 ${wrap(f.dimensions, "多个方面")} 对 ${wrap(f.itemA)} 和 ${wrap(f.itemB)} 进行全面对比。`);
350
- lines.push("**任务**:");
351
- lines.push("1. 构建多维度评估矩阵,量化或半量化评分。");
352
- lines.push("2. 分析各维度的权衡(Trade-offs),说明在什么场景下哪个更优。");
353
- lines.push("3. 给出综合结论和建议。");
354
- lines.push("**输出格式**:Markdown 表格 + 简短分析。");
355
- lines.push("**约束**:客观中立,不偏袒任何一方,明确标注不确定的结论。");
356
- return lines.join("\n");
357
- }
358
-
359
- // ── Specialized Agent Definitions (loaded from agents/grill/ directory) ──
360
-
361
- const _fixGrillAgent = discoverAgents().find(a => a.name === "dev-fix-grill-agent")!;
362
- const _docGrillAgent = discoverAgents().find(a => a.name === "dev-doc-grill-agent")!;
363
- const _refactorGrillAgent = discoverAgents().find(a => a.name === "dev-refactor-grill-agent")!;
364
- const _testGrillAgent = discoverAgents().find(a => a.name === "dev-test-grill-agent")!;
365
- const _perfGrillAgent = discoverAgents().find(a => a.name === "dev-perf-grill-agent")!;
366
-
367
- // ── Workflow configurations ──────────────────────────────────
368
-
369
- const FEAT_WORKFLOW_STEPS: WorkflowStepDef[] = [
370
- {
371
- id: "planner",
372
- label: "📋 生成实施计划",
373
- type: "auto",
374
- agentName: "planner",
375
- timeoutMs: 900_000,
376
- },
377
- {
378
- id: "worker-reviewer",
379
- label: "🔧 实施代码 → 审查",
380
- type: "loop-group",
381
- loopAgentName: "worker",
382
- reviewAgentName: "reviewer",
383
- maxLoops: 3,
384
- timeoutMs: 1_800_000,
385
- reviewTimeoutMs: 900_000,
386
- },
387
- {
388
- id: "trimmer-reviewer",
389
- label: "✂️ 精简代码 → 审查",
390
- type: "loop-group",
391
- loopAgentName: "trimmer",
392
- reviewAgentName: "reviewer",
393
- maxLoops: 3,
394
- timeoutMs: 1_200_000,
395
- reviewTimeoutMs: 900_000,
396
- },
397
- {
398
- id: "docWriter",
399
- label: "📝 更新文档",
400
- type: "confirm",
401
- agentName: "docWriter",
402
- timeoutMs: 600_000,
403
- },
404
- ];
405
-
406
- const FIX_WORKFLOW_STEPS: WorkflowStepDef[] = [
407
- {
408
- id: "planner",
409
- label: "📋 分析根因并制定修复计划",
410
- type: "auto",
411
- agentName: "planner",
412
- timeoutMs: 900_000,
413
- },
414
- {
415
- id: "worker-reviewer",
416
- label: "🔧 修复代码 → 审查",
417
- type: "loop-group",
418
- loopAgentName: "worker",
419
- reviewAgentName: "reviewer",
420
- maxLoops: 3,
421
- timeoutMs: 1_800_000,
422
- reviewTimeoutMs: 900_000,
423
- },
424
- {
425
- id: "docWriter",
426
- label: "📝 更新文档",
427
- type: "confirm",
428
- agentName: "docWriter",
429
- timeoutMs: 600_000,
430
- },
431
- ];
432
-
433
- const REFACTOR_WORKFLOW_STEPS: WorkflowStepDef[] = [
434
- {
435
- id: "planner",
436
- label: "📋 分析重构计划",
437
- type: "auto",
438
- agentName: "planner",
439
- timeoutMs: 900_000,
440
- },
441
- {
442
- id: "worker-reviewer",
443
- label: "🔧 重构代码 → 审查",
444
- type: "loop-group",
445
- loopAgentName: "worker",
446
- reviewAgentName: "reviewer",
447
- maxLoops: 3,
448
- timeoutMs: 1_800_000,
449
- reviewTimeoutMs: 900_000,
450
- },
451
- {
452
- id: "trimmer-reviewer",
453
- label: "✂️ 精简代码 → 审查",
454
- type: "loop-group",
455
- loopAgentName: "trimmer",
456
- reviewAgentName: "reviewer",
457
- maxLoops: 3,
458
- timeoutMs: 1_200_000,
459
- reviewTimeoutMs: 900_000,
460
- },
461
- ];
462
-
463
- const PERF_WORKFLOW_STEPS: WorkflowStepDef[] = [
464
- {
465
- id: "planner",
466
- label: "📋 分析性能问题并制定优化计划",
467
- type: "auto",
468
- agentName: "planner",
469
- timeoutMs: 900_000,
470
- },
471
- {
472
- id: "worker-reviewer",
473
- label: "⚡ 优化代码 → 审查",
474
- type: "loop-group",
475
- loopAgentName: "worker",
476
- reviewAgentName: "reviewer",
477
- maxLoops: 3,
478
- timeoutMs: 1_800_000,
479
- reviewTimeoutMs: 900_000,
480
- },
481
- ];
482
-
483
- const TEST_WORKFLOW_STEPS: WorkflowStepDef[] = [
484
- {
485
- id: "planner",
486
- label: "📋 分析测试计划",
487
- type: "auto",
488
- agentName: "planner",
489
- timeoutMs: 900_000,
490
- },
491
- {
492
- id: "worker-reviewer",
493
- label: "🧪 编写测试 → 审查",
494
- type: "loop-group",
495
- loopAgentName: "worker",
496
- reviewAgentName: "reviewer",
497
- maxLoops: 3,
498
- timeoutMs: 1_800_000,
499
- reviewTimeoutMs: 900_000,
500
- },
501
- ];
502
-
503
- const DOC_WORKFLOW_STEPS: WorkflowStepDef[] = [
504
- {
505
- id: "planner",
506
- label: "📋 分析文档需求",
507
- type: "auto",
508
- agentName: "planner",
509
- timeoutMs: 900_000,
510
- },
511
- {
512
- id: "docWriter",
513
- label: "📝 撰写文档",
514
- type: "auto",
515
- agentName: "docWriter",
516
- timeoutMs: 600_000,
517
- },
518
- ];
519
-
520
- const STYLE_WORKFLOW_STEPS: WorkflowStepDef[] = [
521
- {
522
- id: "trimmer-reviewer",
523
- label: "✂️ 风格调整 → 审查",
524
- type: "loop-group",
525
- loopAgentName: "trimmer",
526
- reviewAgentName: "reviewer",
527
- maxLoops: 2,
528
- timeoutMs: 1_200_000,
529
- reviewTimeoutMs: 900_000,
530
- },
531
- ];
532
-
533
- const SECURITY_WORKFLOW_STEPS: WorkflowStepDef[] = [
534
- {
535
- id: "reviewer",
536
- label: "🔒 安全审查",
537
- type: "auto",
538
- agentName: "reviewer",
539
- timeoutMs: 900_000,
540
- },
541
- ];
542
-
543
- // ── Command runner ───────────────────────────────────────────
544
-
545
- /** Format workflow steps into a readable list. */
546
- function formatWorkflowSteps(steps: WorkflowStepDef[]): string {
547
- return steps.map((s, i) => `${i + 1}. ${s.label}`).join("\n");
548
- }
549
-
550
- /**
551
- * Prompt user to choose workflow mode and optionally customize sub-agent chain.
552
- *
553
- * Returns true if workflow was started and handled (caller should return immediately).
554
- * Returns false if caller should fall through to direct prompt sending.
555
- */
556
- async function promptWorkflowDecision(
557
- ctx: ExtensionCommandContext,
23
+ /** 身份描述,拼在「你是…」之后。 */
24
+ role: (d: ProjectDefaults) => string;
25
+ /** 职责清单。 */
26
+ duties: string[];
27
+ /** 在项目默认验收标准之上追加的条目。 */
28
+ extraAcceptance?: string[];
29
+ }
30
+
31
+ const FEAT: DevType = {
32
+ description: "(dev) 新功能实现 — 先复述确认意图,再按 身份职责 + 默认验收标准 组装提示词执行",
33
+ role: (d) => `资深 ${d.language || "项目"} 工程师`,
34
+ duties: [
35
+ "先读代码库再动手:给出逐步实施计划(要修改/新建的文件、迁移、对现有代码的假设),经我确认后再写代码",
36
+ "只实现任务要求的功能,不顺手重构无关代码",
37
+ "保持现有公共 API 兼容,不为假设性需求添加抽象层",
38
+ ],
39
+ };
40
+
41
+ const FIX: DevType = {
42
+ description: "(dev) 问题修复 — 先复述确认意图,再按 根因定位职责 + 默认验收标准 组装提示词执行",
43
+ role: (d) => `资深 ${d.language || "项目"} 调试工程师`,
44
+ duties: [
45
+ "先读相关代码与日志定位根因,不用消除报错的方式掩盖症状",
46
+ "修复前说明根因与方案取舍,只改与该问题相关的代码",
47
+ "补一个能复现该问题的测试",
48
+ ],
49
+ extraAcceptance: ["新增的复现测试在修复前失败、修复后通过"],
50
+ };
51
+
52
+ const REFACTOR: DevType = {
53
+ description: "(dev) 重构 — 先复述确认意图,再按 行为不变职责 + 默认验收标准 组装提示词执行",
54
+ role: (d) => `资深 ${d.language || "项目"} 工程师`,
55
+ duties: [
56
+ "重构前后对外行为与公共 API 签名完全一致",
57
+ "先说明要消除的具体结构性问题再动手;不做与目标无关的调整",
58
+ "不新增功能、不修改业务逻辑",
59
+ ],
60
+ extraAcceptance: ["对外行为与公共 API 不变,现有测试全部通过"],
61
+ };
62
+
63
+ const TEST: DevType = {
64
+ description: "(dev) 测试补充 — 先复述确认意图,再按 测试职责 + 默认验收标准 组装提示词执行",
65
+ role: () => "资深测试工程师",
66
+ duties: [
67
+ "覆盖成功路径、边界条件与错误分支,测试必须真实执行",
68
+ "断言明确无歧义,不写只验证 mock 的测试",
69
+ "保持与项目既有测试框架和风格一致",
70
+ ],
71
+ extraAcceptance: ["新增测试覆盖成功路径、边界条件与错误分支,且全部通过"],
72
+ };
73
+
74
+ /** 取任务首行作为提示词标题,避免把整段需求塞进标题。 */
75
+ function taskSummary(task: string): string {
76
+ return task.split("\n")[0]!.trim().slice(0, 100);
77
+ }
78
+
79
+ function buildDevPrompt(
80
+ command: string,
81
+ type: DevType,
82
+ task: string,
83
+ intent: string,
84
+ d: ProjectDefaults,
85
+ ): string {
86
+ const acceptance = [...defaultAcceptanceItems(d), ...(type.extraAcceptance ?? [])];
87
+ return [
88
+ `[${command}] ${taskSummary(task)}`,
89
+ "",
90
+ "## 任务(原始描述)",
91
+ task,
92
+ "",
93
+ "## 已确认的任务意图",
94
+ intent,
95
+ "",
96
+ "## 身份与职责",
97
+ `你是${type.role(d)}。`,
98
+ ...type.duties.map((duty) => `- ${duty}`),
99
+ "",
100
+ "## 验收标准",
101
+ "以下为默认收尾验收基线;任务描述中另有明确验收标准时,以任务描述为准。",
102
+ ...acceptance.map((item) => `- ${item}`),
103
+ ].join("\n");
104
+ }
105
+
106
+ async function runDevCommand(
558
107
  pi: ExtensionAPI,
559
- finalPrompt: string,
560
- defaultSteps: WorkflowStepDef[],
561
- ): Promise<boolean> {
562
- if (!defaultSteps || defaultSteps.length === 0) return false;
563
-
564
- const choice = await uiSelect(
565
- ctx,
566
- "🚀 选择工作流模式",
567
- [
568
- "1. 使用默认链式子代理(推荐)",
569
- "2. 自定义链式子代理",
570
- "3. 退出工作流(直接发送 prompt 给主代理)",
571
- ],
572
- );
573
-
574
- if (!choice || choice.startsWith("3")) {
575
- return false;
576
- }
577
-
578
- if (choice.startsWith("1")) {
579
- saveAnswerFile(ctx.cwd, finalPrompt);
580
- await runWorkflow(ctx, pi, finalPrompt, { steps: defaultSteps }, "快速链式");
581
- return true;
582
- }
583
-
584
- // ── Custom mode: let user pick steps and set timeouts (with back support) ──
585
- const customSteps: WorkflowStepDef[] = [];
586
- let stepIdx = 0;
587
- while (stepIdx >= 0 && stepIdx < defaultSteps.length) {
588
- const step = defaultSteps[stepIdx]!;
589
-
590
- // If this step was already added, show current config, else show default
591
- const existingStep = customSteps.find(cs => cs.id === step.id);
592
- const include = await uiConfirm(
593
- ctx,
594
- `📌 ${step.label}${existingStep ? ' (已添加)' : ''}`,
595
- `类型: ${step.type}\n默认超时: ${(step.timeoutMs / 60000).toFixed(0)} 分钟`,
596
- stepIdx > 0, // backable only after first step
597
- );
598
- if (include === "back") {
599
- // Go back: remove the last added step
600
- if (customSteps.length > 0) {
601
- customSteps.pop();
602
- stepIdx--;
603
- continue;
604
- }
605
- return false; // nothing to go back to
606
- }
607
- if (include === undefined) return false; // Esc
608
-
609
- if (!include) {
610
- stepIdx++;
611
- continue; // skip this step
612
- }
613
-
614
- const existingTimeout = existingStep?.timeoutMs;
615
- const timeoutStr = await uiInput(
616
- ctx,
617
- `⏱️ ${step.label} - 超时时间(分钟)`,
618
- existingTimeout
619
- ? `当前: ${(existingTimeout / 60000).toFixed(0)} 分钟,留空保持`
620
- : `留空保持默认 (${(step.timeoutMs / 60000).toFixed(0)} 分钟)`,
621
- );
622
- if (timeoutStr === undefined) { stepIdx++; continue; } // Esc on input = skip
623
-
624
- if (existingStep) {
625
- // Update existing step's timeout
626
- existingStep.timeoutMs = timeoutStr ? parseInt(timeoutStr, 10) * 60 * 1000 || step.timeoutMs : step.timeoutMs;
627
- } else {
628
- customSteps.push({
629
- ...step,
630
- timeoutMs: timeoutStr ? parseInt(timeoutStr, 10) * 60 * 1000 || step.timeoutMs : step.timeoutMs,
631
- });
632
- }
633
- stepIdx++;
634
- }
635
-
636
- if (customSteps.length === 0) {
637
- return false;
638
- }
639
-
640
- saveAnswerFile(ctx.cwd, finalPrompt);
641
- await runWorkflow(ctx, pi, finalPrompt, { steps: customSteps }, "自定义");
642
- return true;
643
- }
644
-
645
- async function runWizardWithGrill(
646
108
  ctx: ExtensionCommandContext,
647
- pi: ExtensionAPI,
648
- type: string,
649
- label: string,
650
- questions: Array<{ label: string; placeholder: string; key: string }>,
651
- assembler: (answers: Record<string, string>) => string,
652
- grillOptions?: GrillOptions,
653
- workflowConfig?: { steps: WorkflowStepDef[] },
109
+ args: string,
110
+ command: string,
111
+ type: DevType,
654
112
  ): Promise<void> {
655
- const answers: Record<string, string> = {};
656
- let idx = 0;
657
-
658
- while (idx >= 0 && idx < questions.length) {
659
- const q = questions[idx]!;
660
- const existingVal = answers[q.key];
661
- const placeholder = existingVal
662
- ? `(之前: ${existingVal.slice(0, 60)}) ${q.placeholder}`
663
- : q.placeholder;
664
- const val = await ask(ctx, q.label, placeholder, true, existingVal || "");
665
- if (val === undefined) {
666
- return;
667
- }
668
- if (val === BACK_MARKER) {
669
- if (idx > 0) {
670
- idx--;
671
- continue;
672
- }
673
- return;
674
- }
675
- answers[q.key] = val;
676
- idx++;
113
+ if (!ctx.hasUI) {
114
+ ctx.ui.notify(`/${command} 需要交互式界面才能确认意图`, "error");
115
+ return;
677
116
  }
678
117
 
679
- const basePrompt = assembler(answers);
118
+ const task = await uiTaskArg(ctx, args, "任务描述(必填)", "如 实现邮箱密码登录接口");
119
+ if (!task) return;
680
120
 
681
- // ── Grill phase (if agentDef provided) ────────────────────
682
- let finalPrompt = basePrompt;
683
- if (grillOptions) {
684
- const grillResult = await runGrillPhase(basePrompt, ctx, {
685
- agentDef: grillOptions.agentDef,
686
- title: grillOptions.title,
687
- description: grillOptions.description,
688
- questionTitle: grillOptions.questionTitle,
689
- loaderLabel: grillOptions.loaderLabel,
690
- });
691
- if (grillResult.cancelled) {
692
- return;
693
- }
694
- finalPrompt = grillResult.enhancedPrompt;
121
+ const intent = await confirmIntent(task, ctx, pi);
122
+ if (!intent) {
123
+ ctx.ui.notify("已取消,未执行任何改动", "info");
124
+ return;
695
125
  }
696
126
 
697
- // ── Workflow phase ───────────────────────────
698
- if (workflowConfig && workflowConfig.steps.length > 0) {
699
- const handled = await promptWorkflowDecision(ctx, pi, finalPrompt, workflowConfig.steps);
700
- if (handled) return;
701
- }
702
-
703
- // ── Guard & persist before sending ──────────────────────
704
- if (!finalPrompt) {
705
- const recovered = recoverFromBackup(ctx.cwd);
706
- if (recovered) {
707
- finalPrompt = recovered;
708
- } else {
709
- return;
710
- }
711
- }
712
- const answerPath = saveAnswerFile(ctx.cwd, finalPrompt);
713
- pi.sendUserMessage(finalPrompt, { deliverAs: "followUp" });
714
- }
715
-
716
- /**
717
- * Run a wizard: ask questions, assemble prompt, send to agent.
718
- */
719
- async function runWizard(
720
- ctx: ExtensionCommandContext,
721
- pi: ExtensionAPI,
722
- type: string,
723
- label: string,
724
- questions: Array<{ label: string; placeholder: string; key: string }>,
725
- assembler: (answers: Record<string, string>) => string,
726
- workflowConfig?: { steps: WorkflowStepDef[] },
727
- ): Promise<void> {
728
- const answers: Record<string, string> = {};
729
- let idx = 0;
730
-
731
- while (idx >= 0 && idx < questions.length) {
732
- const q = questions[idx]!;
733
- const existingVal = answers[q.key];
734
- const placeholder = existingVal
735
- ? `(之前: ${existingVal.slice(0, 60)}) ${q.placeholder}`
736
- : q.placeholder;
737
- const val = await ask(ctx, q.label, placeholder, true, existingVal || "");
738
- if (val === undefined) {
739
- // Esc → cancel whole wizard
740
- return;
741
- }
742
- if (val === BACK_MARKER) {
743
- // Go back to previous question
744
- if (idx > 0) {
745
- idx--;
746
- continue;
747
- }
748
- // Already at first question → cancel
749
- return;
750
- }
751
- answers[q.key] = val;
752
- idx++;
753
- }
754
-
755
- const prompt = assembler(answers);
756
-
757
- // ── Workflow phase ───────────────────────────
758
- if (workflowConfig && workflowConfig.steps.length > 0) {
759
- const handled = await promptWorkflowDecision(ctx, pi, prompt, workflowConfig.steps);
760
- if (handled) return;
761
- }
762
-
763
- // Persist prompt before sending
764
- saveAnswerFile(ctx.cwd, prompt);
765
-
766
- // Send the assembled prompt to the main agent
127
+ const prompt = buildDevPrompt(command, type, task, intent, detectProjectDefaults(ctx.cwd));
767
128
  pi.sendUserMessage(prompt, { deliverAs: "followUp" });
129
+ ctx.ui.notify(`意图已确认,/${command} 提示词已发送给当前代理`, "info");
768
130
  }
769
131
 
770
- // ── Questions for each command ────────────────────────────────
771
-
772
- const FEAT_QUESTIONS = [
773
- { label: "编程语言/框架", placeholder: "如 TypeScript, Python, Rust...", key: "language" },
774
- { label: "技术栈", placeholder: "如 NestJS + Prisma, React + Express...", key: "techStack" },
775
- { label: "目标模块/文件名", placeholder: "如 src/auth/login.ts", key: "module" },
776
- { label: "核心功能描述", placeholder: "用户可以通过邮箱+密码注册并登录", key: "description" },
777
- { label: "用户痛点/当前缺少", placeholder: "当前缺少用户认证系统,每次手动校验身份", key: "painPoint" },
778
- { label: "测试命令(可选)", placeholder: "如 npm test, cargo test, go test...", key: "testCmd" },
779
- ];
780
-
781
- const FIX_QUESTIONS = [
782
- { label: "文件路径", placeholder: "如 src/auth/login.ts", key: "filePath" },
783
- { label: "行号(可选)", placeholder: "如 42,留空则扫描整个文件", key: "lineNumber" },
784
- { label: "Bug 描述", placeholder: "登录接口在密码正确时返回 401", key: "bugDesc" },
785
- { label: "输入/现象", placeholder: "输入正确邮箱和密码,返回 401 错误", key: "inputDesc" },
786
- { label: "预期行为", placeholder: "应返回 200 和 token", key: "expected" },
787
- { label: "当前错误信息", placeholder: "Unauthorized (401) - 不符合预期的输出", key: "actualError" },
788
- { label: "测试命令(可选)", placeholder: "如 npm test, go test...", key: "testCmd" },
789
- ];
790
-
791
- const DOC_QUESTIONS = [
792
- { label: "模块/API/doc 名称", placeholder: "如 AuthService, REST API v2...", key: "moduleName" },
793
- { label: "目标受众", placeholder: "如 小白 / 前端开发者 / 架构师", key: "audience" },
794
- { label: "关键信息点", placeholder: "他们需要了解如何使用认证接口", key: "keyInfo" },
795
- { label: "示例语言", placeholder: "如 TypeScript, Python, curl...", key: "language" },
796
- { label: "已有材料(可选)", placeholder: "现有 README、笔记、文件路径等,留空则从零生成", key: "existingMaterial" },
797
- ];
798
-
799
- const REFACTOR_QUESTIONS = [
800
- { label: "文件路径", placeholder: "如 src/auth/login.ts", key: "filePath" },
801
- { label: "代码行数(可选)", placeholder: "如 200 行", key: "lineCount" },
802
- { label: "具体问题", placeholder: "如 重复逻辑、耦合度高、可读性差", key: "problems" },
803
- { label: "重构目标", placeholder: "如 可读性 / 可维护性 / 模块化", key: "goal" },
804
- { label: "测试命令(可选)", placeholder: "如 npm test, cargo test...", key: "testCmd" },
805
- ];
806
-
807
- const TEST_QUESTIONS = [
808
- { label: "文件路径", placeholder: "如 src/auth/login.ts", key: "filePath" },
809
- { label: "测试框架", placeholder: "如 Jest / Vitest / pytest / Go test", key: "framework" },
810
- { label: "目标覆盖率", placeholder: "如 90,留空默认 90%", key: "coverage" },
811
- { label: "边界条件", placeholder: "如 null 值、空值、超时、幂等性、4xx/5xx 错误", key: "edgeCases" },
812
- { label: "测试命令(可选)", placeholder: "如 npm test -- --coverage", key: "testCmd" },
813
- ];
814
-
815
- const CHORE_QUESTIONS = [
816
- { label: "配置文件/目标路径", placeholder: "如 package.json, .github/workflows/ci.yml", key: "configPath" },
817
- { label: "具体任务", placeholder: "如 更新依赖、修改构建脚本、调整 CI 配置", key: "task" },
818
- { label: "当前环境描述", placeholder: "如 Node 18, pnpm 8", key: "envDesc" },
819
- { label: "目标版本", placeholder: "如 Node 20, pnpm 9", key: "targetVersion" },
820
- { label: "验证命令(可选)", placeholder: "如 npm run build, pnpm test...", key: "verifyCmd" },
821
- ];
822
-
823
- const PERF_QUESTIONS = [
824
- { label: "文件路径", placeholder: "如 src/services/query.ts", key: "filePath" },
825
- { label: "瓶颈描述", placeholder: "如 数据库查询延迟过高、内存泄漏", key: "bottleneck" },
826
- { label: "当前执行耗时", placeholder: "如 5 秒 / 成本 $0.02/次", key: "currentCost" },
827
- { label: "目标延迟", placeholder: "如 200ms", key: "targetLatency" },
828
- { label: "基准测试命令(可选)", placeholder: "如 npm run bench, go test -bench=.", key: "benchCmd" },
829
- ];
830
-
831
- const STYLE_QUESTIONS = [
832
- { label: "目标风格", placeholder: "如 正式商务 / 幽默 / 简洁要点式 / Prettier 规范", key: "targetStyle" },
833
- { label: "待调整内容描述", placeholder: "如 以下函数需要调整命名风格,或粘贴文本", key: "description" },
834
- { label: "术语统一(可选)", placeholder: "如 API → 接口, user → 用户", key: "terms" },
835
- { label: "Linter/格式化命令(可选)", placeholder: "如 npx prettier --check, npm run lint", key: "lintCmd" },
836
- ];
837
-
838
- const SECURITY_QUESTIONS = [
839
- { label: "文件路径", placeholder: "如 src/api/auth.ts", key: "filePath" },
840
- { label: "审查重点(可选)", placeholder: "如 认证边界、注入漏洞、敏感数据暴露、CSRF/CORS、权限校验", key: "focus" },
841
- ];
842
-
843
- const EXPLAIN_QUESTIONS = [
844
- { label: "概念名称", placeholder: "如 React Server Component, HTTP/3", key: "concept" },
845
- { label: "目标受众", placeholder: "如 小白 / 开发者 / 架构师", key: "audience" },
846
- { label: "理解深度(可选)", placeholder: "如 基础 / 进阶,留空默认基础", key: "depth" },
847
- ];
848
-
849
- const COMPARE_QUESTIONS = [
850
- { label: "对比对象 A", placeholder: "如 Vue 3, Next.js, PostgreSQL...", key: "itemA" },
851
- { label: "对比对象 B", placeholder: "如 React 18, Nuxt 3, MySQL...", key: "itemB" },
852
- { label: "评估维度(可选)", placeholder: "如 性能、生态、学习曲线、社区支持", key: "dimensions" },
853
- ];
854
-
855
- // ── Extension ────────────────────────────────────────────────
856
-
857
- export default function (pi: ExtensionAPI) {
858
- // ── /dev-feat ──────────────────────────────────────────────
859
- pi.registerCommand("dev-feat", {
860
- description: "(prompt wizard) 新功能/创意生成 — 支持设计方案追问完善 (Grill) + 自动化工作流",
861
- handler: async (_args, ctx) => {
862
- const answers: Record<string, string> = {};
863
- let featIdx = 0;
864
- while (featIdx >= 0 && featIdx < FEAT_QUESTIONS.length) {
865
- const q = FEAT_QUESTIONS[featIdx]!;
866
- const existingVal = answers[q.key];
867
- const placeholder = existingVal
868
- ? `(之前: ${existingVal.slice(0, 60)}) ${q.placeholder}`
869
- : q.placeholder;
870
- const val = await ask(ctx, q.label, placeholder, true, existingVal || "");
871
- if (val === undefined) {
872
- return;
873
- }
874
- if (val === BACK_MARKER) {
875
- if (featIdx > 0) {
876
- featIdx--;
877
- continue;
878
- }
879
- return;
880
- }
881
- answers[q.key] = val;
882
- featIdx++;
883
- }
884
-
885
- const basePrompt = assembleFeatPrompt(answers as FeatFields);
886
-
887
- const grillResult = await runGrillPhase(basePrompt, ctx);
888
- if (grillResult.cancelled) {
889
- return;
890
- }
891
- const finalPrompt = grillResult.enhancedPrompt;
892
-
893
- if (FEAT_WORKFLOW_STEPS.length > 0) {
894
- const handled = await promptWorkflowDecision(ctx, pi, finalPrompt, FEAT_WORKFLOW_STEPS);
895
- if (handled) return;
896
- }
897
-
898
- if (!finalPrompt) {
899
- const recovered = recoverFromBackup(ctx.cwd);
900
- if (recovered) {
901
- const answerPath = saveAnswerFile(ctx.cwd, recovered);
902
- pi.sendUserMessage(recovered, { deliverAs: "followUp" });
903
- return;
904
- }
905
- return;
906
- }
907
- const answerPath = saveAnswerFile(ctx.cwd, finalPrompt);
908
- pi.sendUserMessage(finalPrompt, { deliverAs: "followUp" });
909
- },
910
- });
911
-
912
- // ── /dev-fix ───────────────────────────────────────────────
913
- pi.registerCommand("dev-fix", {
914
- description: "(prompt wizard) 问题排查/错误修正 — 支持 Bug 根因追问 (Grill)",
915
- handler: async (_args, ctx) => {
916
- await runWizardWithGrill(
917
- ctx, pi, "fix", "问题排查/错误修正",
918
- FIX_QUESTIONS, assembleFixPrompt,
919
- {
920
- agentDef: _fixGrillAgent,
921
- title: "🐛 Bug 根因追问",
922
- description: "AI 会通过一步步追问帮你精准定位根因:从复现条件到根本原因推理,再到修复方案验证和回归风险评估。",
923
- questionTitle: "Bug 根因分析",
924
- loaderLabel: "🧠 AI 正在分析代码并生成根因追问问题...",
925
- },
926
- { steps: FIX_WORKFLOW_STEPS },
927
- );
928
- },
929
- });
930
-
931
- // ── /dev-doc ───────────────────────────────────────────────
932
- pi.registerCommand("dev-doc", {
933
- description: "(prompt wizard) 文档生成/总结 — 支持文档大纲追问完善 (Grill)",
934
- handler: async (_args, ctx) => {
935
- await runWizardWithGrill(
936
- ctx, pi, "doc", "文档生成/总结",
937
- DOC_QUESTIONS, assembleDocPrompt,
938
- {
939
- agentDef: _docGrillAgent,
940
- title: "📄 文档大纲追问完善",
941
- description: "AI 会通过追问帮你完善文档大纲:从受众定位到结构安排,确认术语一致性和示例覆盖范围。",
942
- questionTitle: "文档大纲追问完善",
943
- loaderLabel: "🧠 AI 正在分析并生成文档大纲追问问题...",
944
- },
945
- { steps: DOC_WORKFLOW_STEPS },
946
- );
947
- },
948
- });
949
-
950
- // ── /dev-refactor ──────────────────────────────────────────
951
- pi.registerCommand("dev-refactor", {
952
- description: "(prompt wizard) 重构/优化现有结构 — 支持重构方案追问 (Grill)",
953
- handler: async (_args, ctx) => {
954
- await runWizardWithGrill(
955
- ctx, pi, "refactor", "重构/优化",
956
- REFACTOR_QUESTIONS, assembleRefactorPrompt,
957
- {
958
- agentDef: _refactorGrillAgent,
959
- title: "🔧 重构方案追问",
960
- description: "AI 会通过追问帮你识别隐藏耦合风险:从模块边界到 API 兼容性,验证行为保持和迁移路径安全性。",
961
- questionTitle: "重构方案追问",
962
- loaderLabel: "🧠 AI 正在分析代码并生成重构追问问题...",
963
- },
964
- { steps: REFACTOR_WORKFLOW_STEPS },
965
- );
966
- },
967
- });
968
-
969
- // ── /dev-test ──────────────────────────────────────────────
970
- pi.registerCommand("dev-test", {
971
- description: "(prompt wizard) 测试用例生成 — 支持测试策略追问 (Grill)",
972
- handler: async (_args, ctx) => {
973
- await runWizardWithGrill(
974
- ctx, pi, "test", "测试用例/评估",
975
- TEST_QUESTIONS, assembleTestPrompt,
976
- {
977
- agentDef: _testGrillAgent,
978
- title: "🧪 测试策略追问",
979
- description: "AI 会通过追问帮你发现测试缺口:从覆盖维度到边界条件,验证模拟策略和测试隔离是否到位。",
980
- questionTitle: "测试策略追问",
981
- loaderLabel: "🧠 AI 正在分析并生成测试追问问题...",
982
- },
983
- { steps: TEST_WORKFLOW_STEPS },
984
- );
985
- },
986
- });
987
-
988
- // ── /dev-chore ─────────────────────────────────────────────
989
- pi.registerCommand("dev-chore", {
990
- description: "(prompt wizard) 日常维护/杂项自动化 — 交互填写后发送优化提示词给主代理",
991
- handler: async (_args, ctx) => {
992
- await runWizard(ctx, pi, "chore", "日常维护/自动化", CHORE_QUESTIONS, assembleChorePrompt);
993
- },
994
- });
995
-
996
- // ── /dev-perf ──────────────────────────────────────────────
997
- pi.registerCommand("dev-perf", {
998
- description: "(prompt wizard) 性能优化 — 支持性能优化方案追问 (Grill)",
999
- handler: async (_args, ctx) => {
1000
- await runWizardWithGrill(
1001
- ctx, pi, "perf", "性能优化",
1002
- PERF_QUESTIONS, assemblePerfPrompt,
1003
- {
1004
- agentDef: _perfGrillAgent,
1005
- title: "⚡ 性能优化方案追问",
1006
- description: "AI 会通过追问帮你验证瓶颈判断和优化方向:从基准测试方法到潜在回归风险,确保方案合理性。",
1007
- questionTitle: "性能优化方案追问",
1008
- loaderLabel: "🧠 AI 正在分析并生成性能优化追问问题...",
1009
- },
1010
- { steps: PERF_WORKFLOW_STEPS },
1011
- );
1012
- },
1013
- });
1014
-
1015
- // ── /dev-style ─────────────────────────────────────────────
1016
- pi.registerCommand("dev-style", {
1017
- description: "(prompt wizard) 风格/格式调整 — 交互填写后发送优化提示词给主代理",
1018
- handler: async (_args, ctx) => {
1019
- await runWizard(ctx, pi, "style", "风格/格式调整", STYLE_QUESTIONS, assembleStylePrompt, { steps: STYLE_WORKFLOW_STEPS });
1020
- },
1021
- });
1022
-
1023
- // ── /dev-security ──────────────────────────────────────────
1024
- pi.registerCommand("dev-security", {
1025
- description: "(prompt wizard) 安全审查 — 交互填写后发送优化提示词给主代理",
1026
- handler: async (_args, ctx) => {
1027
- await runWizard(ctx, pi, "security", "安全审查", SECURITY_QUESTIONS, assembleSecurityPrompt, { steps: SECURITY_WORKFLOW_STEPS });
1028
- },
1029
- });
1030
-
1031
- // ── /dev-explain ───────────────────────────────────────────
1032
- pi.registerCommand("dev-explain", {
1033
- description: "(prompt wizard) 概念解释 — 交互填写后发送优化提示词给主代理",
1034
- handler: async (_args, ctx) => {
1035
- await runWizard(ctx, pi, "explain", "概念解释", EXPLAIN_QUESTIONS, assembleExplainPrompt);
1036
- },
1037
- });
1038
-
1039
- // ── /dev-compare ───────────────────────────────────────────
1040
- pi.registerCommand("dev-compare", {
1041
- description: "(prompt wizard) 对比评估 — 交互填写后发送优化提示词给主代理",
1042
- handler: async (_args, ctx) => {
1043
- await runWizard(ctx, pi, "compare", "对比评估", COMPARE_QUESTIONS, assembleComparePrompt);
132
+ function registerDev(pi: ExtensionAPI, command: string, type: DevType): void {
133
+ pi.registerCommand(command, {
134
+ description: type.description,
135
+ handler: async (args, ctx) => {
136
+ await runDevCommand(pi, ctx, args, command, type);
1044
137
  },
1045
138
  });
139
+ }
1046
140
 
1047
- // ── /dev-workflow-continue — 恢复中断的工作流 ─────────────
1048
- pi.registerCommand("dev-workflow-continue", {
1049
- description: "恢复上次中断的自动化工作流(从 checkpoint 继续)",
1050
- handler: async (_args, ctx) => {
1051
- const cp = loadCheckpointFromFile(ctx.cwd);
1052
- if (!cp) {
1053
- return;
1054
- }
1055
- await runWorkflow(ctx, pi, cp.prompt, { steps: FEAT_WORKFLOW_STEPS }, "恢复");
1056
- },
1057
- });
141
+ export default function (pi: ExtensionAPI) {
142
+ registerDev(pi, "dev-feat", FEAT);
143
+ registerDev(pi, "dev-fix", FIX);
144
+ registerDev(pi, "dev-refactor", REFACTOR);
145
+ registerDev(pi, "dev-test", TEST);
1058
146
  }