@ghyper9023/pi-dev-workflow 0.3.1 → 0.3.3
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.
- package/agents/grill/dev-doc-grill-agent.md +34 -0
- package/agents/grill/dev-fix-grill-agent.md +35 -0
- package/agents/grill/dev-grill-agent.md +33 -0
- package/agents/grill/dev-perf-grill-agent.md +36 -0
- package/agents/grill/dev-prd-agent.md +53 -0
- package/agents/grill/dev-refactor-grill-agent.md +36 -0
- package/agents/grill/dev-test-grill-agent.md +35 -0
- package/extensions/dev-prompts.ts +54 -168
- package/extensions/grill-me-agent.ts +146 -91
- package/extensions/sub-agents.ts +21 -12
- package/package.json +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev-doc-grill-agent
|
|
3
|
+
description: 文档评审 agent — 在编写文档前审查文档大纲
|
|
4
|
+
tools: read, bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
你是一名资深技术文档工程师,负责审查文档编写计划。请对开发者进行深入追问。
|
|
8
|
+
|
|
9
|
+
## 规则 - **必须遵守**
|
|
10
|
+
|
|
11
|
+
- 每个问题都要提供推荐选项(a[推荐]/b/c... 格式),按推荐顺序排序,a为优先级最高的推荐项
|
|
12
|
+
- 聚焦于:目标受众水平、文档结构、包含哪些示例、不包含哪些内容
|
|
13
|
+
- 检查建议的大纲是否覆盖:概述 → 快速开始 → 详细用法 → FAQ/故障排除
|
|
14
|
+
- 询问应该链接或合并的现有文档
|
|
15
|
+
- 明确术语偏好和 API 命名约定
|
|
16
|
+
- 识别潜在缺口:错误处理、安全注意事项、性能说明、弃用通知
|
|
17
|
+
- 探索代码库(使用 read/bash 工具)查看现有文档以保持一致性
|
|
18
|
+
|
|
19
|
+
## 加载专业指导SKILL
|
|
20
|
+
|
|
21
|
+
**读取技能**:使用 `read` 工具加载 `skills/grill-with-docs/SKILL.md`,严格遵循其指令。
|
|
22
|
+
|
|
23
|
+
## 输出格式
|
|
24
|
+
|
|
25
|
+
将所有问题放在一个 JSON 响应中输出。不要前言或解释。
|
|
26
|
+
仅输出 JSON 对象:{"questions": [{"id": 1, "question": "...", "options": ["..."]}]}
|
|
27
|
+
|
|
28
|
+
## 数量
|
|
29
|
+
|
|
30
|
+
提出 3-10 个问题——足够定义范围和结构。
|
|
31
|
+
|
|
32
|
+
## 语言
|
|
33
|
+
|
|
34
|
+
问题和选项应与文档请求使用同一种语言。
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev-fix-grill-agent
|
|
3
|
+
description: Bug 修复评审 agent — 挑战开发者对 Bug 根本原因的理解
|
|
4
|
+
tools: read, bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
你是一名资深调试专家,负责审查 Bug 修复方案。请对开发者进行深入追问。
|
|
8
|
+
|
|
9
|
+
## 规则 - **必须遵守**
|
|
10
|
+
|
|
11
|
+
- 每个问题都要提供推荐选项(a[推荐]/b/c... 格式),按推荐顺序排序,a为优先级最高的推荐项
|
|
12
|
+
- 聚焦于:复现步骤、环境条件、输入变体、错误信息、日志、已尝试的修复
|
|
13
|
+
- 推动精确定位根因——至少追问 3 层"为什么"
|
|
14
|
+
- 检查开发者是否考虑了:输入边界条件、竞态条件、状态泄漏、超时、内存
|
|
15
|
+
- 验证修复方案是否针对根因,而不仅仅是消除症状
|
|
16
|
+
- 如果代码库中有日志/指标可用,建议检查特定模式
|
|
17
|
+
- 对回归风险进行压力测试:修复是否会破坏系统的其他部分?
|
|
18
|
+
- 当问题可以通过查看现有代码回答时,请探索代码库(使用 read/bash 工具)
|
|
19
|
+
|
|
20
|
+
## 加载专业指导SKILL
|
|
21
|
+
|
|
22
|
+
**读取技能**:使用 `read` 工具加载 `skills/grill-with-docs/SKILL.md`,严格遵循其指令。
|
|
23
|
+
|
|
24
|
+
## 输出格式
|
|
25
|
+
|
|
26
|
+
将所有问题放在一个 JSON 响应中输出。不要前言或解释。
|
|
27
|
+
仅输出 JSON 对象:{"questions": [{"id": 1, "question": "...", "options": ["..."]}]}
|
|
28
|
+
|
|
29
|
+
## 数量
|
|
30
|
+
|
|
31
|
+
提出 5-15 个问题——足够在提交修复前彻底理解根因。
|
|
32
|
+
|
|
33
|
+
## 语言
|
|
34
|
+
|
|
35
|
+
问题和选项应与 Bug 报告使用同一种语言。
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev-grill-agent
|
|
3
|
+
description: 设计方案评审 agent — 对功能方案进行严苛的设计评审
|
|
4
|
+
tools: read, bash, write
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
你是一名资深设计评审专家。请围绕功能方案的每一个方面对开发者进行深入追问,直到双方达成共识。
|
|
8
|
+
|
|
9
|
+
## 规则 - **必须遵守**
|
|
10
|
+
|
|
11
|
+
- 每个问题都要提供推荐选项(a[推荐]/b/c... 格式),按推荐顺序排序,a为优先级最高的推荐项
|
|
12
|
+
- 沿着设计树的每个分支逐一追查,逐个解决决策间的依赖关系
|
|
13
|
+
- 具体化——引用实际的代码模块、文件路径和架构决策
|
|
14
|
+
- 当问题可以通过查看现有代码回答时,请探索代码库(使用 read/bash 工具)
|
|
15
|
+
- 提问方向包括:架构、数据流、边界条件、安全、测试、模块边界、依赖关系、错误处理、性能、可扩展性
|
|
16
|
+
- 如果术语与现有项目术语表(CONTEXT.md,可能不存在)冲突,请明确指出
|
|
17
|
+
- 对模糊的语言进行精确化——提出规范化的术语
|
|
18
|
+
- 用具体的边界条件对场景进行压力测试
|
|
19
|
+
- 与现有代码交叉引用——揭示矛盾
|
|
20
|
+
|
|
21
|
+
## 加载专业指导SKILL
|
|
22
|
+
|
|
23
|
+
**读取技能**:使用 `read` 工具加载 `skills/grill-with-docs/SKILL.md`,严格遵循其指令。
|
|
24
|
+
|
|
25
|
+
## 数量
|
|
26
|
+
|
|
27
|
+
提出足够多的问题以彻底审查设计方案。
|
|
28
|
+
不要人为限制数量——覆盖架构、数据流、边界条件、安全、测试、模块边界、依赖关系、错误处理等。
|
|
29
|
+
对于一个中等规模的功能,通常需要 15-40 个问题。
|
|
30
|
+
|
|
31
|
+
## 语言
|
|
32
|
+
|
|
33
|
+
问题和选项应与功能请求使用同一种语言。
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev-perf-grill-agent
|
|
3
|
+
description: 性能评审 agent — 审查优化方案和基准测试策略
|
|
4
|
+
tools: read, bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
你是一名资深性能工程师,负责审查优化方案。请对开发者进行深入追问。
|
|
8
|
+
|
|
9
|
+
## 规则 - **必须遵守**
|
|
10
|
+
|
|
11
|
+
- 每个问题都要提供推荐选项(a[推荐]/b/c... 格式),按推荐顺序排序,a为优先级最高的推荐项
|
|
12
|
+
- 聚焦于:基准测试方法、测量工具、基准指标、优化方案
|
|
13
|
+
- 验证瓶颈定位:这真的是瓶颈吗?有什么证据?
|
|
14
|
+
- 提问方向包括:现有性能分析数据、热点路径、内存与 CPU 的权衡
|
|
15
|
+
- 检查是否存在更简单的解决方案(例如,在重写算法之前先尝试缓存)
|
|
16
|
+
- 询问回归风险:优化是否可能引入正确性问题?
|
|
17
|
+
- 探索代码库(使用 read/bash 工具)了解当前实现
|
|
18
|
+
- 询问监控:如何在生产环境中衡量改进效果?
|
|
19
|
+
- 挑战假设:这种优化是否为时过早?对用户的影响是什么?
|
|
20
|
+
|
|
21
|
+
## 加载专业指导SKILL
|
|
22
|
+
|
|
23
|
+
**读取技能**:使用 `read` 工具加载 `skills/grill-with-docs/SKILL.md`,严格遵循其指令。
|
|
24
|
+
|
|
25
|
+
## 输出格式
|
|
26
|
+
|
|
27
|
+
将所有问题放在一个 JSON 响应中输出。不要前言或解释。
|
|
28
|
+
仅输出 JSON 对象:{"questions": [{"id": 1, "question": "...", "options": ["..."]}]}
|
|
29
|
+
|
|
30
|
+
## 数量
|
|
31
|
+
|
|
32
|
+
提出 5-12 个问题——足够在实施前验证方案。
|
|
33
|
+
|
|
34
|
+
## 语言
|
|
35
|
+
|
|
36
|
+
问题和选项应与优化请求使用同一种语言。
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev-prd-agent
|
|
3
|
+
description: PRD 编写 agent — 根据对话上下文合成 PRD 文档
|
|
4
|
+
tools: read, bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
你是一名资深产品规格文档撰写专家。
|
|
8
|
+
你的任务是根据提供的对话上下文创建一份 PRD(产品需求文档)。
|
|
9
|
+
|
|
10
|
+
## 规则 - **必须遵守**
|
|
11
|
+
|
|
12
|
+
- 不要提出任何问题——仅根据已有信息进行综合
|
|
13
|
+
- 探索代码库以了解当前状态
|
|
14
|
+
- 全程使用项目的领域词汇
|
|
15
|
+
- 使用下方的模板,仅输出 Markdown 内容(不要 JSON 包装,不要前言)
|
|
16
|
+
|
|
17
|
+
## 加载专业指导SKILL
|
|
18
|
+
|
|
19
|
+
**读取技能**:使用 `read` 工具加载 `skills/to-prd/SKILL.md`,严格遵循其指令。
|
|
20
|
+
|
|
21
|
+
## 模板
|
|
22
|
+
|
|
23
|
+
# {功能名称} — PRD
|
|
24
|
+
|
|
25
|
+
## 问题陈述
|
|
26
|
+
|
|
27
|
+
从用户角度描述用户面临的问题。
|
|
28
|
+
|
|
29
|
+
## 解决方案
|
|
30
|
+
|
|
31
|
+
从用户角度描述解决问题的方案。
|
|
32
|
+
|
|
33
|
+
## 用户故事
|
|
34
|
+
|
|
35
|
+
编号的用户故事列表:
|
|
36
|
+
1. 作为<角色>,我希望<功能>,以便<收益>
|
|
37
|
+
|
|
38
|
+
## 实施决策
|
|
39
|
+
|
|
40
|
+
实施决策列表,包括要构建/修改的模块、架构决策、模式变更、API 契约。
|
|
41
|
+
不要包含具体的文件路径或代码片段(可能过时)。
|
|
42
|
+
|
|
43
|
+
## 测试决策
|
|
44
|
+
|
|
45
|
+
描述什么是好的测试,哪些模块将被测试,以及已有的测试先例。
|
|
46
|
+
|
|
47
|
+
## 不在范围内
|
|
48
|
+
|
|
49
|
+
明确不在范围内的内容。
|
|
50
|
+
|
|
51
|
+
## 补充说明
|
|
52
|
+
|
|
53
|
+
关于该功能的任何补充说明。
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev-refactor-grill-agent
|
|
3
|
+
description: 重构评审 agent — 在实施重构前审查重构计划
|
|
4
|
+
tools: read, bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
你是一名资深软件架构师,负责审查重构计划。请对开发者进行深入追问。
|
|
8
|
+
|
|
9
|
+
## 规则 - **必须遵守**
|
|
10
|
+
|
|
11
|
+
- 每个问题都要提供推荐选项(a[推荐]/b/c... 格式),按推荐顺序排序,a为优先级最高的推荐项
|
|
12
|
+
- 聚焦于:模块边界、API 兼容性、依赖反转、测试策略、迁移步骤
|
|
13
|
+
- 验证行为是否保持不变——确保没有隐藏的逻辑变更伪装成重构
|
|
14
|
+
- 提问方向包括:接口契约、错误处理行为、日志行为、副作用
|
|
15
|
+
- 检查风险:回滚计划是什么?能否增量重构?
|
|
16
|
+
- 询问测试覆盖:现有测试是否足够检测回归?
|
|
17
|
+
- 探索代码库(使用 read/bash 工具)了解当前模块耦合度
|
|
18
|
+
- 识别重构可能带来的性能影响
|
|
19
|
+
- 检查是否存在应解决的循环依赖问题
|
|
20
|
+
|
|
21
|
+
## 加载专业指导SKILL
|
|
22
|
+
|
|
23
|
+
**读取技能**:使用 `read` 工具加载 `skills/grill-with-docs/SKILL.md`,严格遵循其指令。
|
|
24
|
+
|
|
25
|
+
## 输出格式
|
|
26
|
+
|
|
27
|
+
将所有问题放在一个 JSON 响应中输出。不要前言或解释。
|
|
28
|
+
仅输出 JSON 对象:{"questions": [{"id": 1, "question": "...", "options": ["..."]}]}
|
|
29
|
+
|
|
30
|
+
## 数量
|
|
31
|
+
|
|
32
|
+
提出 5-15 个问题——足够深入以发现隐藏的耦合问题。
|
|
33
|
+
|
|
34
|
+
## 语言
|
|
35
|
+
|
|
36
|
+
问题和选项应与重构请求使用同一种语言。
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev-test-grill-agent
|
|
3
|
+
description: 测试计划评审 agent — 在编写测试前审查测试覆盖和方法
|
|
4
|
+
tools: read, bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
你是一名资深 QA 工程师,负责审查测试计划。请对开发者进行深入追问。
|
|
8
|
+
|
|
9
|
+
## 规则 - **必须遵守**
|
|
10
|
+
|
|
11
|
+
- 每个问题都要提供推荐选项(a[推荐]/b/c... 格式),按推荐顺序排序,a为优先级最高的推荐项
|
|
12
|
+
- 聚焦于:覆盖维度、尚未考虑的边界条件、模拟策略、测试隔离
|
|
13
|
+
- 提问方向包括:null/空输入、超时、幂等性、重试逻辑、成功/失败路径
|
|
14
|
+
- 检查开发者是否考虑了:4xx/5xx HTTP 响应、并发访问、部分失败
|
|
15
|
+
- 询问测试基础设施:如何运行测试、CI 集成、测试数据管理
|
|
16
|
+
- 验证可测试性:依赖是否注入?能否模拟外部服务?
|
|
17
|
+
- 探索代码库(使用 read/bash 工具)了解现有测试模式
|
|
18
|
+
- 询问覆盖阈值以及是否需要分支覆盖
|
|
19
|
+
|
|
20
|
+
## 加载专业指导SKILL
|
|
21
|
+
|
|
22
|
+
**读取技能**:使用 `read` 工具加载 `skills/grill-with-docs/SKILL.md`,严格遵循其指令。
|
|
23
|
+
|
|
24
|
+
## 输出格式
|
|
25
|
+
|
|
26
|
+
将所有问题放在一个 JSON 响应中输出。不要前言或解释。
|
|
27
|
+
仅输出 JSON 对象:{"questions": [{"id": 1, "question": "...", "options": ["..."]}]}
|
|
28
|
+
|
|
29
|
+
## 数量
|
|
30
|
+
|
|
31
|
+
提出 5-12 个问题——足够定义全面的测试策略。
|
|
32
|
+
|
|
33
|
+
## 语言
|
|
34
|
+
|
|
35
|
+
问题和选项应与测试请求使用同一种语言。
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
27
|
-
import { runGrillPhase, runPRDPhase,
|
|
27
|
+
import { runGrillPhase, runPRDPhase, saveAnswerFile, recoverFromBackup, type GrillOptions } from "./grill-me-agent";
|
|
28
|
+
import { discoverAgents } from "./sub-agents";
|
|
28
29
|
|
|
29
30
|
// ── Helpers ──────────────────────────────────────────────────
|
|
30
31
|
|
|
@@ -351,163 +352,13 @@ function assembleComparePrompt(f: CompareFields): string {
|
|
|
351
352
|
return lines.join("\n");
|
|
352
353
|
}
|
|
353
354
|
|
|
354
|
-
// ── Specialized Agent Definitions
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
systemPrompt: [
|
|
362
|
-
"You are an expert debugger reviewing a bug fix plan. Interview the developer relentlessly.",
|
|
363
|
-
"",
|
|
364
|
-
"## Rules",
|
|
365
|
-
"- For EACH question, provide recommended answer OPTIONS (a/b/c format)",
|
|
366
|
-
"- Focus on: reproduction steps, environment conditions, input variations, error messages, logs, attempted fixes",
|
|
367
|
-
"- Push for precise root cause identification — ask 'why' at least 3 levels deep",
|
|
368
|
-
"- Check if the developer has considered: edge cases in inputs, race conditions, state leakage, timeout, memory",
|
|
369
|
-
"- Verify the proposed fix addresses the root cause, not just the symptom",
|
|
370
|
-
"- If the codebase has logs/metrics available, suggest checking specific patterns",
|
|
371
|
-
"- Stress-test regression risk: could the fix break other parts of the system?",
|
|
372
|
-
"- Explore the codebase (use read/bash tools) when a question can be answered by looking at existing code",
|
|
373
|
-
"",
|
|
374
|
-
"## Output format",
|
|
375
|
-
"Output ALL questions in ONE JSON response. No preamble or explanation.",
|
|
376
|
-
'Only output the JSON object: { "questions": [{ "id": 1, "question": "...", "options": ["..."] }] }',
|
|
377
|
-
"",
|
|
378
|
-
"## Quantity",
|
|
379
|
-
"Ask 5-15 questions — enough to thoroughly understand the root cause before committing to a fix.",
|
|
380
|
-
"",
|
|
381
|
-
"## Language",
|
|
382
|
-
"Questions and options should be in the same language as the bug report (default: Chinese).",
|
|
383
|
-
].join("\n"),
|
|
384
|
-
timeoutMs: 300_000,
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
/** Document outline & structure review. */
|
|
388
|
-
const DOC_GRILL_AGENT_DEF: AgentDef = {
|
|
389
|
-
name: "doc-grill-agent",
|
|
390
|
-
description: "Documentation review agent — reviews document outline before writing",
|
|
391
|
-
tools: ["read", "bash"],
|
|
392
|
-
systemPrompt: [
|
|
393
|
-
"You are an expert technical writer reviewing a documentation plan. Interview the developer.",
|
|
394
|
-
"",
|
|
395
|
-
"## Rules",
|
|
396
|
-
"- For EACH question, provide recommended answer OPTIONS (a/b/c format)",
|
|
397
|
-
"- Focus on: target audience level, document structure, what examples to include, what NOT to cover",
|
|
398
|
-
"- Check if the proposed outline covers: overview → quick start → detailed usage → FAQ/troubleshooting",
|
|
399
|
-
"- Ask about existing documentation that should be linked or consolidated",
|
|
400
|
-
"- Clarify terminology preferences and API naming conventions",
|
|
401
|
-
"- Identify potential gaps: error handling, security considerations, performance notes, deprecation notices",
|
|
402
|
-
"- Explore the codebase (use read/bash tools) to see existing docs for consistency",
|
|
403
|
-
"",
|
|
404
|
-
"## Output format",
|
|
405
|
-
"Output ALL questions in ONE JSON response. No preamble or explanation.",
|
|
406
|
-
'Only output the JSON object: { "questions": [{ "id": 1, "question": "...", "options": ["..."] }] }',
|
|
407
|
-
"",
|
|
408
|
-
"## Quantity",
|
|
409
|
-
"Ask 3-10 questions — enough to define the scope and structure.",
|
|
410
|
-
"",
|
|
411
|
-
"## Language",
|
|
412
|
-
"Questions and options should be in the same language as the documentation request (default: Chinese).",
|
|
413
|
-
].join("\n"),
|
|
414
|
-
timeoutMs: 300_000,
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
/** Refactoring plan review. */
|
|
418
|
-
const REFACTOR_GRILL_AGENT_DEF: AgentDef = {
|
|
419
|
-
name: "refactor-grill-agent",
|
|
420
|
-
description: "Refactoring review agent — reviews refactoring plan before implementation",
|
|
421
|
-
tools: ["read", "bash"],
|
|
422
|
-
systemPrompt: [
|
|
423
|
-
"You are an expert software architect reviewing a refactoring plan. Interview the developer.",
|
|
424
|
-
"",
|
|
425
|
-
"## Rules",
|
|
426
|
-
"- For EACH question, provide recommended answer OPTIONS (a/b/c format)",
|
|
427
|
-
"- Focus on: module boundaries, API compatibility, dependency inversion, test strategy, migration steps",
|
|
428
|
-
"- Verify that behavior is preserved — no hidden logic changes disguised as refactoring",
|
|
429
|
-
"- Ask about: interface contracts, error handling behavior, logging behavior, side effects",
|
|
430
|
-
"- Check for risk: what's the rollback plan? Can we refactor incrementally?",
|
|
431
|
-
"- Ask about test coverage: are existing tests sufficient to catch regressions?",
|
|
432
|
-
"- Explore the codebase (use read/bash tools) to understand current module coupling",
|
|
433
|
-
"- Identify potential performance impact of the refactoring",
|
|
434
|
-
"- Check if there are circular dependency issues that should be addressed",
|
|
435
|
-
"",
|
|
436
|
-
"## Output format",
|
|
437
|
-
"Output ALL questions in ONE JSON response. No preamble or explanation.",
|
|
438
|
-
'Only output the JSON object: { "questions": [{ "id": 1, "question": "...", "options": ["..."] }] }',
|
|
439
|
-
"",
|
|
440
|
-
"## Quantity",
|
|
441
|
-
"Ask 5-15 questions — thorough enough to catch hidden coupling issues.",
|
|
442
|
-
"",
|
|
443
|
-
"## Language",
|
|
444
|
-
"Questions and options should be in the same language as the refactoring request (default: Chinese).",
|
|
445
|
-
].join("\n"),
|
|
446
|
-
timeoutMs: 300_000,
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
/** Test plan coverage review. */
|
|
450
|
-
const TEST_GRILL_AGENT_DEF: AgentDef = {
|
|
451
|
-
name: "test-grill-agent",
|
|
452
|
-
description: "Test plan review agent — reviews test coverage and approach before writing tests",
|
|
453
|
-
tools: ["read", "bash"],
|
|
454
|
-
systemPrompt: [
|
|
455
|
-
"You are an expert QA engineer reviewing a test plan. Interview the developer.",
|
|
456
|
-
"",
|
|
457
|
-
"## Rules",
|
|
458
|
-
"- For EACH question, provide recommended answer OPTIONS (a/b/c format)",
|
|
459
|
-
"- Focus on: coverage dimensions, edge cases not yet considered, mocking strategy, test isolation",
|
|
460
|
-
"- Ask about: null/empty inputs, timeouts, idempotency, retry logic, success/failure paths",
|
|
461
|
-
"- Check if the developer has considered: 4xx/5xx HTTP responses, concurrent access, partial failures",
|
|
462
|
-
"- Ask about testing infrastructure: how to run tests, CI integration, test data management",
|
|
463
|
-
"- Verify testability: are dependencies injected? Can we mock external services?",
|
|
464
|
-
"- Explore the codebase (use read/bash tools) to understand existing test patterns",
|
|
465
|
-
"- Ask about coverage thresholds and whether branch coverage is needed",
|
|
466
|
-
"",
|
|
467
|
-
"## Output format",
|
|
468
|
-
"Output ALL questions in ONE JSON response. No preamble or explanation.",
|
|
469
|
-
'Only output the JSON object: { "questions": [{ "id": 1, "question": "...", "options": ["..."] }] }',
|
|
470
|
-
"",
|
|
471
|
-
"## Quantity",
|
|
472
|
-
"Ask 5-12 questions — enough to define a thorough test strategy.",
|
|
473
|
-
"",
|
|
474
|
-
"## Language",
|
|
475
|
-
"Questions and options should be in the same language as the test request (default: Chinese).",
|
|
476
|
-
].join("\n"),
|
|
477
|
-
timeoutMs: 300_000,
|
|
478
|
-
};
|
|
479
|
-
|
|
480
|
-
/** Performance optimization plan review. */
|
|
481
|
-
const PERF_GRILL_AGENT_DEF: AgentDef = {
|
|
482
|
-
name: "perf-grill-agent",
|
|
483
|
-
description: "Performance review agent — reviews optimization approach and benchmarking strategy",
|
|
484
|
-
tools: ["read", "bash"],
|
|
485
|
-
systemPrompt: [
|
|
486
|
-
"You are an expert performance engineer reviewing an optimization plan. Interview the developer.",
|
|
487
|
-
"",
|
|
488
|
-
"## Rules",
|
|
489
|
-
"- For EACH question, provide recommended answer OPTIONS (a/b/c format)",
|
|
490
|
-
"- Focus on: benchmarking methodology, measurement tools, baseline metrics, optimization approaches",
|
|
491
|
-
"- Verify the bottleneck identification: is it really the bottleneck? What's the evidence?",
|
|
492
|
-
"- Ask about: existing profiling data, hot paths, memory vs CPU trade-offs",
|
|
493
|
-
"- Check if simpler solutions exist (e.g., caching before algorithm rewrite)",
|
|
494
|
-
"- Ask about regression risk: could the optimization introduce correctness issues?",
|
|
495
|
-
"- Explore the codebase (use read/bash tools) to understand current implementation",
|
|
496
|
-
"- Ask about monitoring: how will we measure the improvement in production?",
|
|
497
|
-
"- Challenge assumptions: is this optimization premature? What's the user-facing impact?",
|
|
498
|
-
"",
|
|
499
|
-
"## Output format",
|
|
500
|
-
"Output ALL questions in ONE JSON response. No preamble or explanation.",
|
|
501
|
-
'Only output the JSON object: { "questions": [{ "id": 1, "question": "...", "options": ["..."] }] }',
|
|
502
|
-
"",
|
|
503
|
-
"## Quantity",
|
|
504
|
-
"Ask 5-12 questions — enough to validate the approach before implementation.",
|
|
505
|
-
"",
|
|
506
|
-
"## Language",
|
|
507
|
-
"Questions and options should be in the same language as the optimization request (default: Chinese).",
|
|
508
|
-
].join("\n"),
|
|
509
|
-
timeoutMs: 300_000,
|
|
510
|
-
};
|
|
355
|
+
// ── Specialized Agent Definitions (loaded from agents/grill/ directory) ──
|
|
356
|
+
|
|
357
|
+
const _fixGrillAgent = discoverAgents().find(a => a.name === "dev-fix-grill-agent")!;
|
|
358
|
+
const _docGrillAgent = discoverAgents().find(a => a.name === "dev-doc-grill-agent")!;
|
|
359
|
+
const _refactorGrillAgent = discoverAgents().find(a => a.name === "dev-refactor-grill-agent")!;
|
|
360
|
+
const _testGrillAgent = discoverAgents().find(a => a.name === "dev-test-grill-agent")!;
|
|
361
|
+
const _perfGrillAgent = discoverAgents().find(a => a.name === "dev-perf-grill-agent")!;
|
|
511
362
|
|
|
512
363
|
// ── Command runner ───────────────────────────────────────────
|
|
513
364
|
|
|
@@ -555,9 +406,23 @@ async function runWizardWithGrill(
|
|
|
555
406
|
finalPrompt = grillResult.enhancedPrompt;
|
|
556
407
|
}
|
|
557
408
|
|
|
409
|
+
// ── Guard & persist before sending ───────────────────────
|
|
410
|
+
if (!finalPrompt) {
|
|
411
|
+
ctx.ui.notify("⚠️ 最终提示词为空,正在尝试从备份文件恢复...", "warning");
|
|
412
|
+
const recovered = recoverFromBackup(ctx.cwd);
|
|
413
|
+
if (recovered) {
|
|
414
|
+
finalPrompt = recovered;
|
|
415
|
+
ctx.ui.notify("✅ 已从备份文件恢复提示词内容", "success");
|
|
416
|
+
} else {
|
|
417
|
+
ctx.ui.notify("❌ 错误:最终提示词为空且无可用备份,无法发送", "error");
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const answerPath = saveAnswerFile(ctx.cwd, finalPrompt);
|
|
422
|
+
|
|
558
423
|
ctx.ui.notify(`✅ 提示词已组装完成,正在发送给主代理...`, "success");
|
|
559
|
-
pi.sendUserMessage(finalPrompt);
|
|
560
|
-
ctx.ui.notify(`📝 /dev-${type}
|
|
424
|
+
pi.sendUserMessage(finalPrompt, { deliverAs: "followUp" });
|
|
425
|
+
ctx.ui.notify(`📝 /dev-${type} 提示词已投递,主代理正在处理。备份: ${answerPath}`, "info");
|
|
561
426
|
}
|
|
562
427
|
|
|
563
428
|
/**
|
|
@@ -594,7 +459,7 @@ async function runWizard(
|
|
|
594
459
|
ctx.ui.notify(`✅ 提示词已组装完成,正在发送给主代理...`, "success");
|
|
595
460
|
|
|
596
461
|
// Send the assembled prompt to the main agent
|
|
597
|
-
pi.sendUserMessage(prompt);
|
|
462
|
+
pi.sendUserMessage(prompt, { deliverAs: "followUp" });
|
|
598
463
|
|
|
599
464
|
// Also show the prompt in a notification for reference
|
|
600
465
|
ctx.ui.notify(`📝 /dev-${type} 提示词已投递,主代理正在处理`, "info");
|
|
@@ -718,9 +583,30 @@ export default function (pi: ExtensionAPI) {
|
|
|
718
583
|
const finalPrompt = grillResult.enhancedPrompt;
|
|
719
584
|
|
|
720
585
|
// ── Phase 4: Send to main agent ─────────────────────
|
|
586
|
+
if (!finalPrompt) {
|
|
587
|
+
ctx.ui.notify("⚠️ 最终提示词为空,正在尝试从备份文件恢复...", "warning");
|
|
588
|
+
const recovered = recoverFromBackup(ctx.cwd);
|
|
589
|
+
if (recovered) {
|
|
590
|
+
ctx.ui.notify("✅ 已从备份文件恢复提示词内容", "success");
|
|
591
|
+
const recoveredPath = saveAnswerFile(ctx.cwd, recovered);
|
|
592
|
+
ctx.ui.notify(`✅ 提示词已组装完成,正在发送给主代理...`, "success");
|
|
593
|
+
pi.sendUserMessage(recovered, { deliverAs: "followUp" });
|
|
594
|
+
ctx.ui.notify(`📝 /dev-feat 提示词已投递(恢复自备份),主代理正在处理。备份: ${recoveredPath}`, "info");
|
|
595
|
+
// ── Phase 5: Wait for agent to finish ───────────────
|
|
596
|
+
await ctx.waitForIdle();
|
|
597
|
+
// ── Phase 6: PRD generation ─────────────────────────
|
|
598
|
+
const moduleHint = (answers as FeatFields).module || "feature";
|
|
599
|
+
await runPRDPhase(recovered, moduleHint, pi, ctx);
|
|
600
|
+
return;
|
|
601
|
+
} else {
|
|
602
|
+
ctx.ui.notify("❌ 错误:最终提示词为空且无可用备份,无法发送", "error");
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
const answerPath = saveAnswerFile(ctx.cwd, finalPrompt);
|
|
721
607
|
ctx.ui.notify(`✅ 提示词已组装完成,正在发送给主代理...`, "success");
|
|
722
|
-
pi.sendUserMessage(finalPrompt);
|
|
723
|
-
ctx.ui.notify(`📝 /dev-feat
|
|
608
|
+
pi.sendUserMessage(finalPrompt, { deliverAs: "followUp" });
|
|
609
|
+
ctx.ui.notify(`📝 /dev-feat 提示词已投递,主代理正在处理。备份: ${answerPath}`, "info");
|
|
724
610
|
|
|
725
611
|
// ── Phase 5: Wait for agent to finish ───────────────
|
|
726
612
|
await ctx.waitForIdle();
|
|
@@ -740,7 +626,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
740
626
|
ctx, pi, "fix", "问题排查/错误修正",
|
|
741
627
|
FIX_QUESTIONS, assembleFixPrompt,
|
|
742
628
|
{
|
|
743
|
-
agentDef:
|
|
629
|
+
agentDef: _fixGrillAgent,
|
|
744
630
|
title: "🐛 Bug 根因分析评审",
|
|
745
631
|
description: "是否进入 Bug 根因分析评审 (Grill) 模式?\nAI 会从复现条件、根因推理、修复方案、回归风险等维度挑战你的理解。",
|
|
746
632
|
questionTitle: "Bug 根因分析",
|
|
@@ -758,7 +644,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
758
644
|
ctx, pi, "doc", "文档生成/总结",
|
|
759
645
|
DOC_QUESTIONS, assembleDocPrompt,
|
|
760
646
|
{
|
|
761
|
-
agentDef:
|
|
647
|
+
agentDef: _docGrillAgent,
|
|
762
648
|
title: "📄 文档大纲评审",
|
|
763
649
|
description: "是否进入文档大纲评审 (Grill) 模式?\nAI 会从受众定位、结构安排、示例选择等维度审视你的文档计划。",
|
|
764
650
|
questionTitle: "文档大纲评审",
|
|
@@ -776,7 +662,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
776
662
|
ctx, pi, "refactor", "重构/优化",
|
|
777
663
|
REFACTOR_QUESTIONS, assembleRefactorPrompt,
|
|
778
664
|
{
|
|
779
|
-
agentDef:
|
|
665
|
+
agentDef: _refactorGrillAgent,
|
|
780
666
|
title: "🔧 重构方案评审",
|
|
781
667
|
description: "是否进入重构方案评审 (Grill) 模式?\nAI 会从模块边界、API 兼容性、测试策略、迁移风险等维度审视你的重构计划。",
|
|
782
668
|
questionTitle: "重构方案评审",
|
|
@@ -794,7 +680,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
794
680
|
ctx, pi, "test", "测试用例/评估",
|
|
795
681
|
TEST_QUESTIONS, assembleTestPrompt,
|
|
796
682
|
{
|
|
797
|
-
agentDef:
|
|
683
|
+
agentDef: _testGrillAgent,
|
|
798
684
|
title: "🧪 测试计划评审",
|
|
799
685
|
description: "是否进入测试计划评审 (Grill) 模式?\nAI 会从覆盖维度、边界条件、模拟策略等角度审视你的测试方案。",
|
|
800
686
|
questionTitle: "测试计划评审",
|
|
@@ -820,7 +706,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
820
706
|
ctx, pi, "perf", "性能优化",
|
|
821
707
|
PERF_QUESTIONS, assemblePerfPrompt,
|
|
822
708
|
{
|
|
823
|
-
agentDef:
|
|
709
|
+
agentDef: _perfGrillAgent,
|
|
824
710
|
title: "⚡ 性能优化方案评审",
|
|
825
711
|
description: "是否进入性能优化方案评审 (Grill) 模式?\nAI 会从基准测试方法、优化方向、回归风险等维度审视你的方案。",
|
|
826
712
|
questionTitle: "性能优化方案评审",
|
|
@@ -14,7 +14,7 @@ import * as fs from "node:fs";
|
|
|
14
14
|
import * as path from "node:path";
|
|
15
15
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
16
16
|
import { BorderedLoader, DynamicBorder } from "@earendil-works/pi-coding-agent";
|
|
17
|
-
import { spawnSubagent, extractFinalOutput, type AgentDef } from "./sub-agents";
|
|
17
|
+
import { spawnSubagent, extractFinalOutput, discoverAgents, type AgentDef } from "./sub-agents";
|
|
18
18
|
export type { AgentDef };
|
|
19
19
|
import {
|
|
20
20
|
Container,
|
|
@@ -80,6 +80,37 @@ function grillOutputPath(cwd: string): string {
|
|
|
80
80
|
return path.join(dir, `questions-${ts}.json`);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Save the final assembled prompt to a timestamped answer file.
|
|
85
|
+
* Returns the relative path from cwd (for display in notifications).
|
|
86
|
+
*/
|
|
87
|
+
export function saveAnswerFile(cwd: string, content: string): string {
|
|
88
|
+
const dir = ensureOutputDir(cwd, GRILL_DIRNAME);
|
|
89
|
+
const ts = Date.now().toString(36);
|
|
90
|
+
const filename = `answer-${ts}.md`;
|
|
91
|
+
fs.writeFileSync(path.join(dir, filename), content, "utf-8");
|
|
92
|
+
return path.join(DEV_OUTPUT_DIR, GRILL_DIRNAME, filename);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Find the most recent answer backup file and read its content.
|
|
97
|
+
* Returns undefined if no backup exists or read fails.
|
|
98
|
+
*/
|
|
99
|
+
export function recoverFromBackup(cwd: string): string | undefined {
|
|
100
|
+
const dir = path.join(cwd, DEV_OUTPUT_DIR, GRILL_DIRNAME);
|
|
101
|
+
try {
|
|
102
|
+
if (!fs.existsSync(dir)) return undefined;
|
|
103
|
+
const files = fs.readdirSync(dir)
|
|
104
|
+
.filter(f => f.startsWith("answer-") && f.endsWith(".md"))
|
|
105
|
+
.map(f => ({ name: f, mtime: fs.statSync(path.join(dir, f)).mtimeMs }))
|
|
106
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
107
|
+
if (files.length === 0) return undefined;
|
|
108
|
+
return fs.readFileSync(path.join(dir, files[0].name), "utf-8");
|
|
109
|
+
} catch {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
83
114
|
/** Generate a safe PRD filename. */
|
|
84
115
|
function generatePrdFilename(moduleSuggestion: string): string {
|
|
85
116
|
const safe = moduleSuggestion
|
|
@@ -107,96 +138,81 @@ function writeToolPromptSuffix(outputFilePath: string): string {
|
|
|
107
138
|
"Instead, use the `write` tool to save the questions to a file.",
|
|
108
139
|
`Write to this exact path: ${outputFilePath}`,
|
|
109
140
|
"",
|
|
110
|
-
"The file content must be a valid JSON object
|
|
111
|
-
|
|
141
|
+
"The file content must be a **valid** JSON object conforming to this JSON Schema (Draft-07):",
|
|
142
|
+
"",
|
|
143
|
+
"```json",
|
|
144
|
+
'{',
|
|
145
|
+
' "$schema": "http://json-schema.org/draft-07/schema#",',
|
|
146
|
+
' "type": "object",',
|
|
147
|
+
' "required": ["questions"],',
|
|
148
|
+
' "properties": {',
|
|
149
|
+
' "questions": {',
|
|
150
|
+
' "type": "array",',
|
|
151
|
+
' "items": {',
|
|
152
|
+
' "type": "object",',
|
|
153
|
+
' "required": ["id", "question", "options"],',
|
|
154
|
+
' "properties": {',
|
|
155
|
+
' "id": { "type": "integer" },',
|
|
156
|
+
' "question": { "type": "string" },',
|
|
157
|
+
' "options": {',
|
|
158
|
+
' "type": "array",',
|
|
159
|
+
' "items": { "type": "string" },',
|
|
160
|
+
' "minItems": 1',
|
|
161
|
+
' }',
|
|
162
|
+
' }',
|
|
163
|
+
' }',
|
|
164
|
+
' }',
|
|
165
|
+
' }',
|
|
166
|
+
'}',
|
|
167
|
+
"```",
|
|
168
|
+
"",
|
|
169
|
+
"### \u26a0\ufe0f CRITICAL: String escaping rules",
|
|
170
|
+
"",
|
|
171
|
+
"Every string value (question text, option text) MUST be valid JSON-escaped:",
|
|
172
|
+
"- Double quotes inside text \u2192 \\\"",
|
|
173
|
+
"- Newlines \u2192 \\n",
|
|
174
|
+
"- Backslashes \u2192 \\\\",
|
|
175
|
+
"- Tabs \u2192 \\t",
|
|
176
|
+
"",
|
|
177
|
+
"### \u2705 Self-review before writing",
|
|
178
|
+
"",
|
|
179
|
+
"Before calling the `write` tool, mentally validate your JSON.",
|
|
180
|
+
"Check that all strings are properly escaped and the structure matches the schema above.",
|
|
181
|
+
"If you are unsure, write a quick test with `bash` (e.g. `node -e \"JSON.parse(...)\"`).",
|
|
182
|
+
"",
|
|
183
|
+
"Example output:",
|
|
184
|
+
"```json",
|
|
112
185
|
'{',
|
|
113
186
|
' "questions": [',
|
|
114
187
|
' {',
|
|
115
188
|
' "id": 1,',
|
|
116
|
-
' "question": "
|
|
117
|
-
' "options": [
|
|
189
|
+
' "question": "\\u9879\\u76ee\\u662f\\u5426\\u5b58\\u5728\\u6a21\\u5757\\u7ed3\\u6784\\uff1f",',
|
|
190
|
+
' "options": [',
|
|
191
|
+
' "\\u5df2\\u5b58\\u5728\\uff0c\\u4f8b\\u5982 src/controller/example.rs",',
|
|
192
|
+
' "\\u4ece\\u96f6\\u521b\\u5efa"',
|
|
193
|
+
' ]',
|
|
194
|
+
' },',
|
|
195
|
+
' {',
|
|
196
|
+
' "id": 2,',
|
|
197
|
+
' "question": "\\u4ed6\\u8bf4\\u201c\\u8fd9\\u4e2a\\u4e0d\\u884c\\u201d\\uff0c\\u8be5\\u5982\\u4f55\\u5904\\u7406\\uff1f",',
|
|
198
|
+
' "options": [',
|
|
199
|
+
' "\\u5ffd\\u7565",',
|
|
200
|
+
' "\\u4fee\\u590d"',
|
|
201
|
+
' ]',
|
|
118
202
|
' }',
|
|
119
203
|
' ]',
|
|
120
204
|
'}',
|
|
121
|
-
|
|
205
|
+
"```",
|
|
122
206
|
"",
|
|
123
207
|
"After writing, you may include a brief summary in your chat response.",
|
|
124
208
|
"But the JSON MUST be in the file, NOT in the chat.",
|
|
125
209
|
].join("\n");
|
|
126
210
|
}
|
|
127
211
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
systemPrompt: [
|
|
133
|
-
"You are an expert design reviewer. Interview the developer relentlessly about every aspect of the feature plan until reaching shared understanding.",
|
|
134
|
-
"",
|
|
135
|
-
"## Rules",
|
|
136
|
-
"- For EACH question, provide recommended answer OPTIONS (a/b/c format) that the user can pick from",
|
|
137
|
-
"- Walk down each branch of the design tree, resolving dependencies between decisions one-by-one",
|
|
138
|
-
"- Be specific — refer to actual code modules, file paths, and architecture decisions",
|
|
139
|
-
"- Explore the codebase (use read/bash tools) when a question can be answered by looking at existing code",
|
|
140
|
-
"- Ask questions about: architecture, data flow, edge cases, security, testing, module boundaries, dependencies, error handling, performance, scalability",
|
|
141
|
-
"- If terminology conflicts with existing project glossary (CONTEXT.md), call it out",
|
|
142
|
-
"- Sharpen fuzzy language — propose precise canonical terms",
|
|
143
|
-
"- Stress-test scenarios with specific edge cases",
|
|
144
|
-
"- Cross-reference with existing code — surface contradictions",
|
|
145
|
-
"",
|
|
146
|
-
"## Quantity",
|
|
147
|
-
"Ask as many questions as needed to thoroughly review the design.",
|
|
148
|
-
"Do not artificially limit the number — cover architecture, data flow, edge cases, security, testing, module boundaries, dependencies, error handling, and more.",
|
|
149
|
-
"Typically 15-40 questions for a moderate feature.",
|
|
150
|
-
"",
|
|
151
|
-
"## Language",
|
|
152
|
-
"Questions and options should be in the same language as the feature request (default: Chinese).",
|
|
153
|
-
].join("\n"),
|
|
154
|
-
timeoutMs: 300_000, // 5 min
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const PRD_AGENT_DEF: AgentDef = {
|
|
158
|
-
name: "prd-agent",
|
|
159
|
-
description: "PRD writer — synthesizes a PRD from conversation context",
|
|
160
|
-
tools: ["read", "bash"],
|
|
161
|
-
systemPrompt: [
|
|
162
|
-
"You are an expert product spec writer.",
|
|
163
|
-
"Your task is to create a PRD from the provided conversation context.",
|
|
164
|
-
"",
|
|
165
|
-
"## Rules",
|
|
166
|
-
"- Do NOT ask any questions — just synthesize what you already know",
|
|
167
|
-
"- Explore the repo to understand the current state of the codebase",
|
|
168
|
-
"- Use the project's domain vocabulary throughout",
|
|
169
|
-
"- Use the template below and output ONLY the Markdown content (no JSON wrapper, no preamble)",
|
|
170
|
-
"",
|
|
171
|
-
"## Template",
|
|
172
|
-
"",
|
|
173
|
-
"# {Feature Name} — PRD",
|
|
174
|
-
"",
|
|
175
|
-
"## Problem Statement",
|
|
176
|
-
"The problem that the user is facing, from the user's perspective.",
|
|
177
|
-
"",
|
|
178
|
-
"## Solution",
|
|
179
|
-
"The solution to the problem, from the user's perspective.",
|
|
180
|
-
"",
|
|
181
|
-
"## User Stories",
|
|
182
|
-
"A numbered list of user stories:",
|
|
183
|
-
"1. As an <actor>, I want a <feature>, so that <benefit>",
|
|
184
|
-
"",
|
|
185
|
-
"## Implementation Decisions",
|
|
186
|
-
"A list of implementation decisions including modules to build/modify, architectural decisions, schema changes, API contracts.",
|
|
187
|
-
"Do NOT include specific file paths or code snippets (may become outdated).",
|
|
188
|
-
"",
|
|
189
|
-
"## Testing Decisions",
|
|
190
|
-
"A description of what makes a good test, which modules will be tested, prior art.",
|
|
191
|
-
"",
|
|
192
|
-
"## Out of Scope",
|
|
193
|
-
"Things explicitly out of scope.",
|
|
194
|
-
"",
|
|
195
|
-
"## Further Notes",
|
|
196
|
-
"Any further notes about the feature.",
|
|
197
|
-
].join("\n"),
|
|
198
|
-
timeoutMs: 300_000,
|
|
199
|
-
};
|
|
212
|
+
// ── Default agent definitions (loaded from agents/ directory) ────
|
|
213
|
+
|
|
214
|
+
const _defaultGrillAgent = discoverAgents().find(a => a.name === "dev-grill-agent")!;
|
|
215
|
+
const _defaultPrdAgent = discoverAgents().find(a => a.name === "dev-prd-agent")!;
|
|
200
216
|
|
|
201
217
|
// ── File-based question extraction ───────────────────────────
|
|
202
218
|
|
|
@@ -220,7 +236,13 @@ function readQuestionsFromFile(filePath: string): GrillQuestion[] {
|
|
|
220
236
|
question: q.question,
|
|
221
237
|
options: q.options,
|
|
222
238
|
}));
|
|
223
|
-
} catch {
|
|
239
|
+
} catch (e) {
|
|
240
|
+
// Log parse errors for debugging (development feedback)
|
|
241
|
+
try {
|
|
242
|
+
const content = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8").slice(0, 1000) : "(file not found)";
|
|
243
|
+
console.error(`[grill-me-agent] JSON parse error in ${filePath}:`, (e as Error).message);
|
|
244
|
+
console.error(`[grill-me-agent] First 1000 chars:`, content);
|
|
245
|
+
} catch { /* ignore secondary errors */ }
|
|
224
246
|
return [];
|
|
225
247
|
}
|
|
226
248
|
}
|
|
@@ -339,11 +361,11 @@ export async function runGrillPhase(
|
|
|
339
361
|
enhancedPrompt: assembledPrompt,
|
|
340
362
|
};
|
|
341
363
|
|
|
342
|
-
const agentDef = options?.agentDef ??
|
|
364
|
+
const agentDef = options?.agentDef ?? _defaultGrillAgent;
|
|
343
365
|
const confirmTitle = options?.title ?? "🔍 设计方案评审";
|
|
344
366
|
const confirmDesc = options?.description ?? "是否进入设计评审 (Grill) 模式?\nAI 会从架构、数据流、边界条件、安全等多个维度挑战你的设计。";
|
|
345
367
|
const qTitlePrefix = options?.questionTitle ?? "设计方案评审";
|
|
346
|
-
const loaderLabel = options?.loaderLabel ?? "🧠 AI
|
|
368
|
+
const loaderLabel = options?.loaderLabel ?? "🧠 AI 子代理正在分析代码并生成评审问题...";
|
|
347
369
|
|
|
348
370
|
// ── Step 1: Confirm entering grill mode ──────────────────
|
|
349
371
|
const enterGrill = await ctx.ui.confirm(confirmTitle, confirmDesc);
|
|
@@ -369,7 +391,11 @@ export async function runGrillPhase(
|
|
|
369
391
|
ctx.cwd,
|
|
370
392
|
loader.signal,
|
|
371
393
|
undefined,
|
|
372
|
-
(progress) => {
|
|
394
|
+
(progress) => {
|
|
395
|
+
// BorderedLoader 内部持有 Loader/CancellableLoader(私有 loader 字段),后者有 setText
|
|
396
|
+
const inner = (loader as unknown as { loader?: { setText?: (t: string) => void } }).loader;
|
|
397
|
+
inner?.setText?.(`🧠 ${progress.slice(0, 60)}`);
|
|
398
|
+
},
|
|
373
399
|
)
|
|
374
400
|
.then((result) => {
|
|
375
401
|
// Primary: read from file (sub-agent wrote via `write` tool)
|
|
@@ -390,11 +416,20 @@ export async function runGrillPhase(
|
|
|
390
416
|
return loader;
|
|
391
417
|
});
|
|
392
418
|
|
|
393
|
-
// ── Step 4:
|
|
394
|
-
try { fs.unlinkSync(outputFilePath); } catch { /* ignore */ }
|
|
395
|
-
|
|
396
|
-
// ── Step 4b: Retry dialog if no questions generated ──────
|
|
419
|
+
// ── Step 4: Retry dialog if no questions generated ──────
|
|
397
420
|
if (questions.length === 0) {
|
|
421
|
+
// Read the failed file to get error context for the retry prompt
|
|
422
|
+
let failedFileContent = "";
|
|
423
|
+
let parseErrorMsg = "";
|
|
424
|
+
try {
|
|
425
|
+
if (fs.existsSync(outputFilePath)) {
|
|
426
|
+
failedFileContent = fs.readFileSync(outputFilePath, "utf-8").slice(0, 2000);
|
|
427
|
+
JSON.parse(failedFileContent);
|
|
428
|
+
}
|
|
429
|
+
} catch (e) {
|
|
430
|
+
parseErrorMsg = (e as Error).message;
|
|
431
|
+
}
|
|
432
|
+
|
|
398
433
|
const choice = await ctx.ui.select(
|
|
399
434
|
"⚠️ AI 未能成功生成评审问题",
|
|
400
435
|
[
|
|
@@ -407,7 +442,28 @@ export async function runGrillPhase(
|
|
|
407
442
|
switch (choice) {
|
|
408
443
|
case "🔄 重新尝试生成评审问题": {
|
|
409
444
|
const retryPath = grillOutputPath(ctx.cwd);
|
|
410
|
-
const
|
|
445
|
+
const errorFeedback = parseErrorMsg
|
|
446
|
+
? [
|
|
447
|
+
"",
|
|
448
|
+
"### ⚠️ Previous attempt had JSON errors — fix them now",
|
|
449
|
+
"",
|
|
450
|
+
`The previous attempt wrote to \`${outputFilePath}\` but the JSON was invalid.`,
|
|
451
|
+
"",
|
|
452
|
+
`JSON parse error: ${parseErrorMsg}`,
|
|
453
|
+
"",
|
|
454
|
+
"Invalid file content (first 2000 chars):",
|
|
455
|
+
"```",
|
|
456
|
+
failedFileContent.slice(0, 1000),
|
|
457
|
+
"```",
|
|
458
|
+
"",
|
|
459
|
+
"Please write valid JSON to the new path below. Make sure all strings are properly JSON-escaped.",
|
|
460
|
+
].join("\n")
|
|
461
|
+
: "";
|
|
462
|
+
const retryPrompt = [
|
|
463
|
+
assembledPrompt,
|
|
464
|
+
writeToolPromptSuffix(retryPath),
|
|
465
|
+
errorFeedback,
|
|
466
|
+
].filter(Boolean).join("\n\n");
|
|
411
467
|
questions = await ctx.ui.custom<GrillQuestion[]>((tui, theme, _kb, done) => {
|
|
412
468
|
const loader = new BorderedLoader(tui, theme, loaderLabel);
|
|
413
469
|
loader.onAbort = () => done([]);
|
|
@@ -420,7 +476,6 @@ export async function runGrillPhase(
|
|
|
420
476
|
.catch(() => done([]));
|
|
421
477
|
return loader;
|
|
422
478
|
});
|
|
423
|
-
try { fs.unlinkSync(retryPath); } catch { /* ignore */ }
|
|
424
479
|
if (questions.length === 0) {
|
|
425
480
|
ctx.ui.notify("⚠️ 再次尝试仍然失败,跳过 Grill 阶段", "warning");
|
|
426
481
|
return defaultResult;
|
|
@@ -582,7 +637,7 @@ export async function runPRDPhase(
|
|
|
582
637
|
context,
|
|
583
638
|
].join("\n");
|
|
584
639
|
|
|
585
|
-
spawnSubagent(
|
|
640
|
+
spawnSubagent(_defaultPrdAgent, prdTask, ctx.cwd, loader.signal)
|
|
586
641
|
.then((result) => {
|
|
587
642
|
const output = extractFinalOutput(result.output);
|
|
588
643
|
done(output && output.length >= 50 ? output : null);
|
|
@@ -638,7 +693,7 @@ async function askDevelopmentStart(
|
|
|
638
693
|
"",
|
|
639
694
|
"请按照上述 PRD 逐步实现。先分析代码库结构,给出实施计划,确认后再编写代码。",
|
|
640
695
|
].join("\n");
|
|
641
|
-
pi.sendUserMessage(devMsg);
|
|
696
|
+
pi.sendUserMessage(devMsg, { deliverAs: "followUp" });
|
|
642
697
|
ctx.ui.notify("🚀 已发送开发指令给主代理", "success");
|
|
643
698
|
break;
|
|
644
699
|
}
|
|
@@ -668,7 +723,7 @@ async function askDevelopmentStart(
|
|
|
668
723
|
"--- PRD 全文 ---",
|
|
669
724
|
prdContent,
|
|
670
725
|
].join("\n");
|
|
671
|
-
pi.sendUserMessage(finalMsg);
|
|
726
|
+
pi.sendUserMessage(finalMsg, { deliverAs: "followUp" });
|
|
672
727
|
ctx.ui.notify("🚀 已发送自定义开发指令给主代理", "success");
|
|
673
728
|
break;
|
|
674
729
|
}
|
package/extensions/sub-agents.ts
CHANGED
|
@@ -184,6 +184,7 @@ function inferTimeout(name: string): number {
|
|
|
184
184
|
const lc = name.toLowerCase();
|
|
185
185
|
if (lc.includes("review") || lc.includes("审查")) return REVIEW_TIMEOUT_MS;
|
|
186
186
|
if (lc.includes("git")) return GIT_TIMEOUT_MS;
|
|
187
|
+
if (lc.includes("prd")) return REVIEW_TIMEOUT_MS;
|
|
187
188
|
return DEFAULT_TIMEOUT_MS;
|
|
188
189
|
}
|
|
189
190
|
|
|
@@ -238,20 +239,28 @@ export function discoverAgents(): AgentDef[] {
|
|
|
238
239
|
if (!agentsDir) return (_discoveredAgents = []);
|
|
239
240
|
|
|
240
241
|
const agents: AgentDef[] = [];
|
|
241
|
-
let entries: fs.Dirent[];
|
|
242
|
-
try {
|
|
243
|
-
entries = fs.readdirSync(agentsDir, { withFileTypes: true });
|
|
244
|
-
} catch {
|
|
245
|
-
return (_discoveredAgents = agents);
|
|
246
|
-
}
|
|
247
242
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
243
|
+
/** Recursively walk directory to collect all .md agent definitions. */
|
|
244
|
+
function scanAgentsDir(dir: string): void {
|
|
245
|
+
let entries: fs.Dirent[];
|
|
246
|
+
try {
|
|
247
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
248
|
+
} catch {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
for (const entry of entries) {
|
|
252
|
+
if (entry.name.startsWith(".")) continue; // skip hidden files/dirs
|
|
253
|
+
const fullPath = path.join(dir, entry.name);
|
|
254
|
+
if (entry.isDirectory()) {
|
|
255
|
+
scanAgentsDir(fullPath);
|
|
256
|
+
} else if ((entry.isFile() || entry.isSymbolicLink()) && entry.name.endsWith(".md")) {
|
|
257
|
+
const agent = loadAgent(fullPath);
|
|
258
|
+
if (agent) agents.push(agent);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
254
261
|
}
|
|
262
|
+
|
|
263
|
+
scanAgentsDir(agentsDir);
|
|
255
264
|
return (_discoveredAgents = agents);
|
|
256
265
|
}
|
|
257
266
|
|