@ghyper9023/pi-dev-workflow 0.7.0 → 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.
- package/.version/RELEASE-v0.8.0.md +93 -0
- package/README.md +140 -108
- package/extensions/dev-prompts.ts +117 -930
- package/extensions/git-commands.ts +2 -2
- package/extensions/grill-me-agent.ts +66 -31
- package/extensions/pre-check.ts +190 -0
- package/extensions/review-detect.ts +123 -0
- package/extensions/session-utils.ts +9 -10
- package/extensions/ui-helpers.ts +25 -5
- package/package.json +1 -1
- package/prompts/APPEND_SYSTEM.md +14 -4
- package/tests/test-no-subagents.mjs +149 -31
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* test-no-subagents.mjs —
|
|
2
|
+
* test-no-subagents.mjs — 结构性回归断言
|
|
3
3
|
*
|
|
4
4
|
* Run: node tests/test-no-subagents.mjs
|
|
5
|
+
*
|
|
6
|
+
* 覆盖:
|
|
7
|
+
* 1. 子代理基础设施已删除
|
|
8
|
+
* 2. dev 命令:4 个命令、参数即任务、意图识别复用 pre-check、不再绑定 Grill/PRD
|
|
9
|
+
* 3. 自动审查检测独立为 review-detect.ts
|
|
10
|
+
* 4. git 命令直接执行,不再委派给子代理
|
|
11
|
+
* 5. /grill 与 /prd 是独立命令
|
|
12
|
+
* 6. UI 组件与输出目录约定
|
|
13
|
+
* 7. /dev-pre-check 意图校验(confirmIntent 为共享实现)
|
|
14
|
+
* 8. notify 调用使用合法类型(info / warning / error)
|
|
15
|
+
* 9. 当前版本的 RELEASE 说明覆盖该版本区间内的全部 commit
|
|
5
16
|
*/
|
|
6
17
|
|
|
7
18
|
import * as fs from "node:fs";
|
|
8
19
|
import * as path from "node:path";
|
|
20
|
+
import { execFileSync } from "node:child_process";
|
|
9
21
|
import { fileURLToPath } from "node:url";
|
|
10
22
|
|
|
11
23
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -57,24 +69,49 @@ assertNotExists("agents/", "agents/ 目录已删除");
|
|
|
57
69
|
assertNotExists(".doc/AGENT-FRONTMATTER-REFERENCE.md", "AGENT-FRONTMATTER-REFERENCE.md 已删除");
|
|
58
70
|
|
|
59
71
|
// ═══════════════════════════════════════════════════════════════
|
|
60
|
-
// 2. dev-
|
|
72
|
+
// 2. dev 命令:4 个命令,参数即任务,意图识别复用 pre-check
|
|
61
73
|
// ═══════════════════════════════════════════════════════════════
|
|
62
74
|
|
|
63
75
|
console.log("\n📋 dev-prompts.ts\n");
|
|
64
76
|
|
|
65
77
|
assertNotIncludes("extensions/dev-prompts.ts", "./sub-agents", "不再引入 sub-agents");
|
|
66
78
|
assertNotIncludes("extensions/dev-prompts.ts", "./workflow-engine", "不再引入 workflow-engine");
|
|
67
|
-
assertNotIncludes("extensions/dev-prompts.ts", "
|
|
68
|
-
assertNotIncludes("extensions/dev-prompts.ts", "
|
|
69
|
-
assertNotIncludes("extensions/dev-prompts.ts", "
|
|
70
|
-
|
|
71
|
-
assertIncludes("extensions/dev-prompts.ts",
|
|
72
|
-
assertIncludes("extensions/dev-prompts.ts", "
|
|
73
|
-
assertIncludes("extensions/dev-prompts.ts", "
|
|
74
|
-
assertIncludes("extensions/dev-prompts.ts", "
|
|
79
|
+
assertNotIncludes("extensions/dev-prompts.ts", "./grill-me-agent", "不再绑定 Grill/PRD 运行时");
|
|
80
|
+
assertNotIncludes("extensions/dev-prompts.ts", "runGrillPhase", "不再触发 Grill 追问");
|
|
81
|
+
assertNotIncludes("extensions/dev-prompts.ts", "runPRDPhase", "不再触发 PRD 生成");
|
|
82
|
+
assertNotIncludes("extensions/dev-prompts.ts", "WizardQuestion", "向导式提问已移除");
|
|
83
|
+
assertIncludes("extensions/dev-prompts.ts", 'import { confirmIntent } from "./pre-check"', "意图识别复用 pre-check");
|
|
84
|
+
assertIncludes("extensions/dev-prompts.ts", "uiTaskArg(ctx, args", "任务原文来自命令参数,缺省才弹输入框");
|
|
85
|
+
assertIncludes("extensions/dev-prompts.ts", "detectProjectDefaults", "默认验收标准来自项目探测");
|
|
86
|
+
assertIncludes("extensions/dev-prompts.ts", "defaultAcceptanceItems", "默认验收标准按条目生成");
|
|
87
|
+
assertIncludes("extensions/dev-prompts.ts", "## 身份与职责", "提示词包含身份与职责段");
|
|
88
|
+
assertIncludes("extensions/dev-prompts.ts", "## 已确认的任务意图", "提示词携带已确认的意图");
|
|
89
|
+
assertIncludes("extensions/dev-prompts.ts", "## 验收标准", "提示词包含默认验收标准段");
|
|
90
|
+
assertIncludes("extensions/dev-prompts.ts", "pi.sendUserMessage(prompt", "组装后的提示词直接发送给当前代理");
|
|
91
|
+
|
|
92
|
+
for (const cmd of ["dev-feat", "dev-fix", "dev-refactor", "dev-test"]) {
|
|
93
|
+
assertIncludes("extensions/dev-prompts.ts", `registerDev(pi, "${cmd}"`, `注册 /${cmd}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (const cmd of ["dev-doc", "dev-perf", "dev-style", "dev-security", "dev-chore", "dev-explain", "dev-compare"]) {
|
|
97
|
+
assertNotIncludes("extensions/dev-prompts.ts", `"${cmd}"`, `/${cmd} 已移除`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ═══════════════════════════════════════════════════════════════
|
|
101
|
+
// 3. 自动审查检测独立为 review-detect.ts
|
|
102
|
+
// ═══════════════════════════════════════════════════════════════
|
|
103
|
+
|
|
104
|
+
console.log("\n📋 review-detect.ts\n");
|
|
105
|
+
|
|
106
|
+
assertExists("extensions/review-detect.ts", "自动审查检测独立成扩展");
|
|
107
|
+
assertNotIncludes("extensions/review-detect.ts", "./sub-agents", "不再引入 sub-agents");
|
|
108
|
+
assertIncludes("extensions/review-detect.ts", 'pi.on("input"', "拦截带审查意图的输入");
|
|
109
|
+
assertIncludes("extensions/review-detect.ts", 'event.source === "extension"', "过滤扩展注入消息,防止递归");
|
|
110
|
+
assertIncludes("extensions/review-detect.ts", "expandPromptTemplates: true", "skill 命令展开执行");
|
|
111
|
+
assertIncludes("extensions/review-detect.ts", '"pi-review"', "自动审查仍查找 pi-review/ 输出目录");
|
|
75
112
|
|
|
76
113
|
// ═══════════════════════════════════════════════════════════════
|
|
77
|
-
//
|
|
114
|
+
// 4. Git 命令直接执行,不再委派给子代理
|
|
78
115
|
// ═══════════════════════════════════════════════════════════════
|
|
79
116
|
|
|
80
117
|
console.log("\n📋 git-commands.ts\n");
|
|
@@ -88,47 +125,128 @@ assertIncludes("extensions/git-commands.ts", "git-push", "保留 /git-push 命
|
|
|
88
125
|
assertIncludes("extensions/git-commands.ts", "git-commit-push", "保留 /git-commit-push 命令");
|
|
89
126
|
|
|
90
127
|
// ═══════════════════════════════════════════════════════════════
|
|
91
|
-
//
|
|
128
|
+
// 5. Grill / PRD 是独立命令,运行在当前代理中
|
|
92
129
|
// ═══════════════════════════════════════════════════════════════
|
|
93
130
|
|
|
94
131
|
console.log("\n📋 grill-me-agent.ts\n");
|
|
95
132
|
|
|
96
133
|
assertNotIncludes("extensions/grill-me-agent.ts", "./sub-agents", "不再引入 sub-agents");
|
|
97
134
|
assertNotIncludes("extensions/grill-me-agent.ts", "spawnSubagent", "不再 spawn 子进程");
|
|
135
|
+
assertNotIncludes("extensions/grill-me-agent.ts", "recoverFromBackup", "断点恢复随向导一并移除");
|
|
136
|
+
assertIncludes("extensions/grill-me-agent.ts", 'registerCommand("grill"', "注册独立 /grill 命令");
|
|
137
|
+
assertIncludes("extensions/grill-me-agent.ts", 'registerCommand("prd"', "注册独立 /prd 命令");
|
|
98
138
|
assertIncludes("extensions/grill-me-agent.ts", "pollFor", "轮询等待当前代理产物后读取结果");
|
|
99
139
|
assertIncludes("extensions/grill-me-agent.ts", "GRILL_ANSWERS_DIRNAME = \"answers\"", "保留 answers 子目录");
|
|
100
140
|
assertIncludes("extensions/grill-me-agent.ts", "GRILL_QUESTIONS_DIRNAME = \"questions\"", "保留 questions 子目录");
|
|
101
141
|
|
|
102
142
|
// ═══════════════════════════════════════════════════════════════
|
|
103
|
-
//
|
|
143
|
+
// 6. UI 组件与输出目录约定
|
|
104
144
|
// ═══════════════════════════════════════════════════════════════
|
|
105
145
|
|
|
106
|
-
console.log("\n📋
|
|
146
|
+
console.log("\n📋 输出目录与 UI 组件\n");
|
|
107
147
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
148
|
+
const reviewSkill = fs.readFileSync(path.resolve(ROOT, "skills/review-html/SKILL.md"), "utf-8");
|
|
149
|
+
assert(reviewSkill.includes(".pi-dev-output/pi-review/html/"), "review-html 仍写入 pi-review/html/");
|
|
150
|
+
|
|
151
|
+
assertIncludes("extensions/session-utils.ts", "detectProjectDefaults", "项目探测(语言/测试/lint/pre-commit/CI)");
|
|
152
|
+
assertIncludes("extensions/session-utils.ts", "defaultAcceptanceItems", "生成默认验收标准条目");
|
|
111
153
|
assertIncludes("extensions/ui-helpers.ts", "export function uiSelect", "保留 uiSelect");
|
|
112
154
|
assertIncludes("extensions/ui-helpers.ts", "export function uiConfirm", "保留 uiConfirm");
|
|
113
155
|
assertIncludes("extensions/ui-helpers.ts", "export function uiInput", "保留 uiInput");
|
|
156
|
+
assertIncludes("extensions/ui-helpers.ts", "export async function uiTaskArg", "命令参数与输入框二选一取任务文本");
|
|
157
|
+
assertNotIncludes("extensions/ui-helpers.ts", "updateWorkflowWidget", "移除工作流 widget");
|
|
158
|
+
assertNotIncludes("extensions/ui-helpers.ts", "sendWorkflowResult", "移除工作流结果消息");
|
|
159
|
+
assertNotIncludes("extensions/ui-helpers.ts", "WorkflowStepWidgetState", "移除工作流状态类型");
|
|
114
160
|
|
|
115
161
|
// ═══════════════════════════════════════════════════════════════
|
|
116
|
-
//
|
|
162
|
+
// 7. /dev-pre-check 独立意图校验扩展
|
|
117
163
|
// ═══════════════════════════════════════════════════════════════
|
|
118
164
|
|
|
119
|
-
console.log("\n📋
|
|
165
|
+
console.log("\n📋 pre-check.ts\n");
|
|
120
166
|
|
|
121
|
-
|
|
122
|
-
assertIncludes("
|
|
167
|
+
assertExists("extensions/pre-check.ts", "提供独立的 pre-check 扩展");
|
|
168
|
+
assertIncludes("extensions/pre-check.ts", 'registerCommand("dev-pre-check"', "注册 /dev-pre-check 命令");
|
|
169
|
+
assertIncludes("extensions/pre-check.ts", "export async function confirmIntent", "导出共享的意图确认循环");
|
|
170
|
+
assertIncludes("extensions/pre-check.ts", "用自己的话重述你认为用户的目标是什么,以及用户试图解决的问题是什么", "固定指令:用自己的话重述目标与问题");
|
|
171
|
+
assertIncludes("extensions/pre-check.ts", "[pre-check] 任务意图校验:只复述,不执行", "意图校验提示词声明只复述不执行");
|
|
172
|
+
assertIncludes("extensions/pre-check.ts", "禁止修改、创建、删除任何文件", "约束禁止任何实质改动");
|
|
173
|
+
assertIncludes("extensions/pre-check.ts", "pi.sendUserMessage(buildExecutionPrompt", "确认通过后才发送执行提示词");
|
|
174
|
+
assertIncludes("extensions/pre-check.ts", "buildExecutionPrompt(originalPrompt: string, intent: string)", "执行提示词携带已确认的意图复述");
|
|
175
|
+
assertIncludes("extensions/pre-check.ts", "ctx.isIdle()", "以代理空闲作为复述完成信号之一");
|
|
176
|
+
assertIncludes("extensions/pre-check.ts", "pollFor", "轮询等待复述产物");
|
|
177
|
+
assertNotIncludes("extensions/pre-check.ts", "./dev-prompts", "不依赖 /dev-* 命令实现");
|
|
178
|
+
assertNotIncludes("extensions/pre-check.ts", "./grill-me-agent", "不依赖 Grill/PRD 运行时");
|
|
179
|
+
assertNotIncludes("extensions/dev-prompts.ts", "dev-pre-check", "dev-prompts 不注册覆盖 pre-check 的命令");
|
|
123
180
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
181
|
+
// ═══════════════════════════════════════════════════════════════
|
|
182
|
+
// 8. notify 调用使用合法类型
|
|
183
|
+
// ═══════════════════════════════════════════════════════════════
|
|
184
|
+
|
|
185
|
+
console.log("\n📋 notify 类型\n");
|
|
186
|
+
|
|
187
|
+
const NOTIFY_TYPES = ["info", "warning", "error"];
|
|
188
|
+
const badNotify = [];
|
|
189
|
+
for (const file of fs.readdirSync(path.resolve(ROOT, "extensions"))) {
|
|
190
|
+
if (!file.endsWith(".ts")) continue;
|
|
191
|
+
const lines = fs.readFileSync(path.resolve(ROOT, "extensions", file), "utf-8").split("\n");
|
|
192
|
+
for (const line of lines) {
|
|
193
|
+
const matched = line.match(/\.notify\(.*,\s*"([^"]+)"\s*\)/);
|
|
194
|
+
if (matched && !NOTIFY_TYPES.includes(matched[1])) badNotify.push(`${file} → ${matched[1]}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
assert(
|
|
198
|
+
badNotify.length === 0,
|
|
199
|
+
badNotify.length === 0 ? "所有 notify 调用使用合法类型(info / warning / error)" : `非法 notify 类型: ${badNotify.join(", ")}`,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// ═══════════════════════════════════════════════════════════════
|
|
203
|
+
// 9. 版本说明覆盖该版本全部 commit
|
|
204
|
+
// ═══════════════════════════════════════════════════════════════
|
|
205
|
+
|
|
206
|
+
console.log("\n📋 版本说明\n");
|
|
207
|
+
|
|
208
|
+
function git(args) {
|
|
209
|
+
return execFileSync("git", args, { cwd: ROOT, encoding: "utf-8" }).trim();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const version = JSON.parse(fs.readFileSync(path.resolve(ROOT, "package.json"), "utf-8")).version;
|
|
213
|
+
const releaseDoc = `.version/RELEASE-v${version}.md`;
|
|
214
|
+
assertExists(releaseDoc, `存在 ${releaseDoc}`);
|
|
215
|
+
|
|
216
|
+
const docText = fs.readFileSync(path.resolve(ROOT, releaseDoc), "utf-8");
|
|
217
|
+
const listed = docText
|
|
218
|
+
.split("\n")
|
|
219
|
+
.map((line) => line.match(/^[0-9a-f]{7,40}\b/)?.[0])
|
|
220
|
+
.filter(Boolean);
|
|
221
|
+
assert(listed.length > 0, "Commit History 至少列出一条 commit");
|
|
222
|
+
|
|
223
|
+
const unknown = listed.filter((h) => {
|
|
224
|
+
try {
|
|
225
|
+
git(["rev-parse", "--verify", "--quiet", `${h}^{commit}`]);
|
|
226
|
+
return false;
|
|
227
|
+
} catch {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
assert(unknown.length === 0, `列出的 commit 都真实存在(${unknown.join(", ") || "无缺失"})`);
|
|
232
|
+
|
|
233
|
+
// 以文档中最新的一条 commit 作为覆盖终点,避免把后续版本的提交算进本版本
|
|
234
|
+
const tip = listed.reduce((a, b) =>
|
|
235
|
+
Number(git(["rev-list", "--count", a])) >= Number(git(["rev-list", "--count", b])) ? a : b,
|
|
236
|
+
);
|
|
237
|
+
const prevTag = git(["tag", "--merged", tip, "--sort=-v:refname"])
|
|
238
|
+
.split("\n")
|
|
239
|
+
.find((tag) => tag && tag !== `v${version}`);
|
|
240
|
+
assert(Boolean(prevTag), `找到上一版本 tag(${prevTag})`);
|
|
241
|
+
|
|
242
|
+
const range = git(["log", "--format=%h", `${prevTag}..${tip}`]).split("\n").filter(Boolean);
|
|
243
|
+
const missing = range.filter((h) => !listed.some((l) => h.startsWith(l) || l.startsWith(h)));
|
|
244
|
+
assert(
|
|
245
|
+
missing.length === 0,
|
|
246
|
+
missing.length === 0
|
|
247
|
+
? `${releaseDoc} 覆盖 ${prevTag}..${tip} 全部 ${range.length} 个 commit`
|
|
248
|
+
: `${releaseDoc} 缺少 ${missing.length} 个 commit: ${missing.join(", ")}`,
|
|
249
|
+
);
|
|
132
250
|
|
|
133
251
|
// ═══════════════════════════════════════════════════════════════
|
|
134
252
|
// Summary
|
|
@@ -142,5 +260,5 @@ if (fail > 0) {
|
|
|
142
260
|
console.error("\n⚠️ 部分测试未通过");
|
|
143
261
|
process.exit(1);
|
|
144
262
|
} else {
|
|
145
|
-
console.log("\n✅ 所有测试通过
|
|
146
|
-
}
|
|
263
|
+
console.log("\n✅ 所有测试通过");
|
|
264
|
+
}
|