@wnlen/agent-execution-template 0.8.19 → 0.8.21
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/README.md +19 -2
- package/README.zh-CN.md +14 -0
- package/bin/agent-execution-template.js +220 -27
- package/docs/SPEC.md +55 -12
- package/package.json +1 -1
- package/template/en/ai/README.md +4 -1
- package/template/en/ai/template/VERSION +1 -1
- package/template/en/ai/template/bootstrap.md +4 -1
- package/template/en/ai/template/execution-policy.md +5 -0
- package/template/en/ai/template/prompt.md +12 -9
- package/template/en/ai/template/protocol.md +9 -3
- package/template/en/ai/template/rules/output.md +4 -1
- package/template/zh/ai/README.md +2 -0
- package/template/zh/ai/project/runtime.md +11 -11
- package/template/zh/ai/project/task.md +20 -25
- package/template/zh/ai/template/VERSION +1 -1
- package/template/zh/ai/template/bootstrap.md +24 -28
- package/template/zh/ai/template/execution-policy.md +3 -0
- package/template/zh/ai/template/prompt.md +45 -54
- package/template/zh/ai/template/protocol.md +32 -33
- package/template/zh/ai/template/reconcile.md +21 -28
- package/template/zh/ai/template/rules/core.md +19 -24
- package/template/zh/ai/template/rules/output.md +3 -1
- package/test/selftest.js +144 -7
package/test/selftest.js
CHANGED
|
@@ -40,6 +40,17 @@ function write(cwd, relativePath, content) {
|
|
|
40
40
|
fs.writeFileSync(path.join(cwd, relativePath), content);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
function countOccurrences(content, pattern) {
|
|
44
|
+
return content.split(pattern).length - 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function managedEntrypointBlock(content) {
|
|
48
|
+
const start = content.indexOf("<!-- agent-execution-template:start -->");
|
|
49
|
+
const end = content.indexOf("<!-- agent-execution-template:end -->", start);
|
|
50
|
+
assert(start >= 0 && end >= 0, "entrypoint content should include a managed block");
|
|
51
|
+
return content.slice(start, end + "<!-- agent-execution-template:end -->".length);
|
|
52
|
+
}
|
|
53
|
+
|
|
43
54
|
function createTempProject(name) {
|
|
44
55
|
return fs.mkdtempSync(path.join(os.tmpdir(), `${name}-`));
|
|
45
56
|
}
|
|
@@ -48,12 +59,24 @@ function testInitUpdateDoctor() {
|
|
|
48
59
|
const cwd = createTempProject("agent-execution-template-selftest");
|
|
49
60
|
|
|
50
61
|
run(["init"], cwd);
|
|
62
|
+
assert(exists(cwd, "AGENTS.md"), "init should create root AGENTS.md entrypoint");
|
|
63
|
+
assert(exists(cwd, "CLAUDE.md"), "init should create root CLAUDE.md entrypoint");
|
|
64
|
+
assert(read(cwd, "AGENTS.md").includes("agent-execution-template:start"), "AGENTS.md should include managed protocol block");
|
|
65
|
+
assert(read(cwd, "AGENTS.md").includes("ai/template/prompt.md"), "AGENTS.md should route agents to the protocol prompt");
|
|
66
|
+
assert(read(cwd, "CLAUDE.md").includes("ai/template/prompt.md"), "CLAUDE.md should route Claude to the same protocol prompt");
|
|
67
|
+
assert(managedEntrypointBlock(read(cwd, "AGENTS.md")) === managedEntrypointBlock(read(cwd, "CLAUDE.md")), "AGENTS.md and CLAUDE.md should contain the same managed compatibility block");
|
|
68
|
+
assert(read(cwd, "AGENTS.md").includes("有意同时写入 `AGENTS.md` 和 `CLAUDE.md`"), "AGENTS.md should explain intentional compatibility duplication");
|
|
69
|
+
assert(read(cwd, "AGENTS.md").includes("在 `ai/template/prompt.md` 完成路由前"), "AGENTS.md should stay a short router");
|
|
70
|
+
assert(!read(cwd, "AGENTS.md").includes("ai/template/bootstrap.md"), "AGENTS.md should not duplicate bootstrap routing details");
|
|
71
|
+
assert(!read(cwd, "CLAUDE.md").includes("ai/template/bootstrap.md"), "CLAUDE.md should not duplicate bootstrap routing details");
|
|
51
72
|
assert(read(cwd, "ai/template/LANG") === "zh\n", "init should default to zh template");
|
|
52
73
|
assert(exists(cwd, "ai/template/VERSION"), "init should create template VERSION");
|
|
53
74
|
assert(exists(cwd, "ai/template/bootstrap.md"), "init should create template bootstrap prompt");
|
|
54
75
|
assert(exists(cwd, "ai/template/execution-policy.md"), "init should create execution policy prompt");
|
|
55
76
|
assert(exists(cwd, "ai/template/prompt.md"), "init should create template prompt");
|
|
56
77
|
assert(exists(cwd, "ai/template/reconcile.md"), "init should create template reconcile prompt");
|
|
78
|
+
assert(exists(cwd, "ai/template/schemas/result.schema.json"), "init should create result schema");
|
|
79
|
+
assert(exists(cwd, "ai/template/schemas/metrics.schema.json"), "init should create metrics schema");
|
|
57
80
|
assert(exists(cwd, "ai/project/inbox/.gitkeep"), "init should create inbox directory");
|
|
58
81
|
assert(exists(cwd, "ai/project/project.md"), "init should create project.md");
|
|
59
82
|
assert(exists(cwd, "ai/project/task.md"), "init should create task.md");
|
|
@@ -78,10 +101,12 @@ function testInitUpdateDoctor() {
|
|
|
78
101
|
assert(read(cwd, "ai/template/bootstrap.md").includes("未吸收资料"), "bootstrap handoff should audit unabsorbed material");
|
|
79
102
|
assert(read(cwd, "ai/template/bootstrap.md").includes("冲突处理"), "bootstrap handoff should audit conflict handling");
|
|
80
103
|
assert(read(cwd, "ai/template/prompt.md").includes("任务草稿交接"), "execution prompt should include task handoff");
|
|
81
|
-
assert(read(cwd, "ai/template/prompt.md").includes("
|
|
104
|
+
assert(read(cwd, "ai/template/prompt.md").includes("本文件只负责路由"), "execution prompt should be a lightweight router");
|
|
105
|
+
assert(read(cwd, "ai/template/prompt.md").includes("才读取 `ai/template/protocol.md`"), "execution prompt should lazy-load execution policy only for execution");
|
|
106
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("不要把 `AGENTS.md` 或 `CLAUDE.md` 当作项目业务证据"), "bootstrap should not treat root agent entrypoints as project evidence");
|
|
82
107
|
assert(read(cwd, "ai/template/execution-policy.md").includes("风险分级"), "execution policy should include risk rubric");
|
|
83
108
|
assert(read(cwd, "ai/template/execution-policy.md").includes("execution_policy.task_tree"), "execution policy should require task tree persistence");
|
|
84
|
-
assert(read(cwd, "ai/template/prompt.md").includes("
|
|
109
|
+
assert(read(cwd, "ai/template/prompt.md").includes("也默认只处理 `ai/project/inbox/*.md`"), "execution prompt should narrow inbox reconciliation");
|
|
85
110
|
assert(read(cwd, "ai/template/protocol.md").includes("`bounded_continuous`"), "protocol should include bounded continuous execution");
|
|
86
111
|
assert(read(cwd, "ai/template/execution-policy.md").includes("垂直切片"), "protocol should require vertical-slice progress for continuous execution");
|
|
87
112
|
assert(read(cwd, "ai/template/execution-policy.md").includes("可独立验收的垂直切片"), "execution policy should define L1 granularity");
|
|
@@ -89,6 +114,8 @@ function testInitUpdateDoctor() {
|
|
|
89
114
|
assert(read(cwd, "ai/template/execution-policy.md").includes("不要为每个微小 L3 操作写回"), "execution policy should limit task tree write-back churn");
|
|
90
115
|
assert(read(cwd, "ai/template/execution-policy.md").includes("公共接口、数据模型、权限、安全"), "execution policy should constrain Yellow corrections");
|
|
91
116
|
assert(read(cwd, "ai/template/execution-policy.md").includes("用户可见输出"), "execution policy should define user-visible output rules");
|
|
117
|
+
assert(read(cwd, "ai/template/execution-policy.md").includes("用户可见的计划"), "execution policy should keep user-visible planning in the installed language");
|
|
118
|
+
assert(read(cwd, "ai/template/rules/output.md").includes("默认使用 `ai/template/LANG`"), "output rules should keep human-readable results in the installed language");
|
|
92
119
|
assert(read(cwd, "ai/template/execution-policy.md").includes("不要默认展示完整 L2/L3/L4"), "execution policy should avoid exposing full subtask trees by default");
|
|
93
120
|
assert(read(cwd, "ai/template/execution-policy.md").includes("不要展示内部协议字段"), "execution policy should hide internal protocol details by default");
|
|
94
121
|
assert(read(cwd, "ai/template/execution-policy.md").includes("L1 为 2 个或更多,自动启用"), "protocol should auto-enable continuous execution from L1 count");
|
|
@@ -110,12 +137,12 @@ function testInitUpdateDoctor() {
|
|
|
110
137
|
assert(read(cwd, "ai/template/prompt.md").includes("不要重新 bootstrap"), "execution prompt should reconcile inbox material when project context already exists");
|
|
111
138
|
assert(read(cwd, "ai/template/prompt.md").includes("整合 ai/project/inbox/ 里的新资料"), "execution prompt should route natural reconcile entry");
|
|
112
139
|
assert(read(cwd, "ai/template/prompt.md").includes("继续推进这个项目"), "execution prompt should route natural continue entry");
|
|
113
|
-
assert(read(cwd, "ai/template/prompt.md").includes("
|
|
140
|
+
assert(read(cwd, "ai/template/prompt.md").includes("草稿不能直接执行"), "execution prompt should stop after drafting a task");
|
|
114
141
|
assert(read(cwd, "ai/template/prompt.md").includes("用户可见输出"), "execution prompt should reference user-visible output rules");
|
|
115
142
|
assert(read(cwd, "ai/template/prompt.md").includes("strategy_update"), "execution prompt should route strategy updates");
|
|
116
143
|
assert(read(cwd, "ai/template/reconcile.md").includes("上下文整合"), "init should install reconcile prompt");
|
|
117
144
|
assert(read(cwd, "ai/template/reconcile.md").includes("整合计划"), "reconcile prompt should require a plan first");
|
|
118
|
-
assert(read(cwd, "ai/template/reconcile.md").includes("不要递归读取 `processed/**` 或 `ideas/**`"), "reconcile prompt should exclude processed and ideas recursively");
|
|
145
|
+
assert(read(cwd, "ai/template/reconcile.md").includes("不要递归读取") && read(cwd, "ai/template/reconcile.md").includes("`processed/**` 或 `ideas/**`"), "reconcile prompt should exclude processed and ideas recursively");
|
|
119
146
|
assert(read(cwd, "ai/template/reconcile.md").includes("ai/project/inbox/processed/raw/file.md"), "reconcile prompt should archive absorbed raw inbox material");
|
|
120
147
|
assert(read(cwd, "ai/template/reconcile.md").includes("未吸收资料"), "reconcile handoff should audit unabsorbed material");
|
|
121
148
|
assert(read(cwd, "ai/template/reconcile.md").includes("冲突处理"), "reconcile handoff should audit conflict handling");
|
|
@@ -128,7 +155,9 @@ function testInitUpdateDoctor() {
|
|
|
128
155
|
assert(read(cwd, "ai/project/proposals/final-shape-updates/_template.md").includes("`accepted`"), "proposal template should describe accepted status");
|
|
129
156
|
const initOutput = run(["init"], cwd);
|
|
130
157
|
assert(initOutput.includes("开始初始化这个项目"), "init output should provide compact natural bootstrap prompt");
|
|
131
|
-
assert(initOutput.includes("
|
|
158
|
+
assert(initOutput.includes("根目录 AI 兼容入口: AGENTS.md / CLAUDE.md"), "init output should mention root AI compatibility entrypoints");
|
|
159
|
+
assert(initOutput.includes("协议已安装。项目上下文尚未初始化"), "init output should distinguish protocol install from project context bootstrap");
|
|
160
|
+
assert(initOutput.includes("[下一步:让 AI 初始化项目上下文]"), "init output should guide the AI bootstrap as the next step");
|
|
132
161
|
assert(initOutput.includes("开始初始化这个项目,并吸收 ai/project/inbox/ 里的资料"), "init output should explain bootstrap with existing material");
|
|
133
162
|
assert(initOutput.includes("整合 ai/project/inbox/ 里的新资料"), "init output should provide compact natural reconcile prompt");
|
|
134
163
|
assert(initOutput.includes("优化上下文"), "init output should expose project context refresh in user language");
|
|
@@ -138,7 +167,11 @@ function testInitUpdateDoctor() {
|
|
|
138
167
|
assert(!initOutput.includes("维护者提示"), "init output should not show source checkout guidance in user projects");
|
|
139
168
|
assert(!initOutput.includes("[已更新]"), "init output should hide detailed file changes by default");
|
|
140
169
|
assert(!initOutput.includes("Read ai/template/bootstrap.md"), "init output should not use weak Read bootstrap command");
|
|
170
|
+
assert(countOccurrences(read(cwd, "AGENTS.md"), "agent-execution-template:start") === 1, "init should not duplicate AGENTS.md managed blocks");
|
|
171
|
+
assert(countOccurrences(read(cwd, "CLAUDE.md"), "agent-execution-template:start") === 1, "init should not duplicate CLAUDE.md managed blocks");
|
|
141
172
|
assert(run(["init", "--verbose"], cwd).includes("[已更新] ai/template/VERSION"), "init --verbose should show detailed file changes");
|
|
173
|
+
assert(countOccurrences(read(cwd, "AGENTS.md"), "agent-execution-template:start") === 1, "re-running init should keep one AGENTS.md managed block");
|
|
174
|
+
assert(countOccurrences(read(cwd, "CLAUDE.md"), "agent-execution-template:start") === 1, "re-running init should keep one CLAUDE.md managed block");
|
|
142
175
|
const reconcileOutput = run(["reconcile"], cwd);
|
|
143
176
|
assert(reconcileOutput.includes("Agent Execution Template 上下文整合"), "reconcile should use installed Chinese language");
|
|
144
177
|
assert(reconcileOutput.includes("整合 ai/project/inbox/ 里的新资料"), "reconcile should print natural Chinese prompt");
|
|
@@ -150,10 +183,14 @@ function testInitUpdateDoctor() {
|
|
|
150
183
|
write(cwd, "ai/project/project.md", "USER PROJECT MARKER\n");
|
|
151
184
|
run(["update"], cwd);
|
|
152
185
|
assert(read(cwd, "ai/project/project.md") === "USER PROJECT MARKER\n", "update must not overwrite project.md");
|
|
186
|
+
assert(read(cwd, "AGENTS.md").includes("ai/template/prompt.md"), "update should keep root agent entrypoint block installed");
|
|
153
187
|
|
|
154
188
|
const doctorOutput = run(["doctor"], cwd);
|
|
189
|
+
assert(doctorOutput.includes("根目录 AI 兼容入口已安装: AGENTS.md / CLAUDE.md"), "doctor should report root AI compatibility entrypoint status");
|
|
155
190
|
assert(doctorOutput.includes("ai/project/result.json JSON"), "doctor should validate result JSON");
|
|
191
|
+
assert(doctorOutput.includes("ai/project/result.json schema"), "doctor should validate result schema");
|
|
156
192
|
assert(doctorOutput.includes("ai/project/metrics.json JSON"), "doctor should validate metrics JSON");
|
|
193
|
+
assert(doctorOutput.includes("ai/project/metrics.json schema"), "doctor should validate metrics schema");
|
|
157
194
|
assert(doctorOutput.includes("ai/project/task.md front matter"), "doctor should validate task front matter");
|
|
158
195
|
}
|
|
159
196
|
|
|
@@ -161,8 +198,19 @@ function testEnglishInitUpdateDoctor() {
|
|
|
161
198
|
const cwd = createTempProject("agent-execution-template-en");
|
|
162
199
|
|
|
163
200
|
const initOutput = run(["init", "--lang", "en"], cwd);
|
|
201
|
+
assert(exists(cwd, "AGENTS.md"), "English init should create root AGENTS.md entrypoint");
|
|
202
|
+
assert(exists(cwd, "CLAUDE.md"), "English init should create root CLAUDE.md entrypoint");
|
|
203
|
+
assert(read(cwd, "AGENTS.md").includes("ai/template/prompt.md"), "English AGENTS.md should route agents to the protocol prompt");
|
|
204
|
+
assert(read(cwd, "CLAUDE.md").includes("ai/template/prompt.md"), "English CLAUDE.md should route Claude to the same protocol prompt");
|
|
205
|
+
assert(managedEntrypointBlock(read(cwd, "AGENTS.md")) === managedEntrypointBlock(read(cwd, "CLAUDE.md")), "English AGENTS.md and CLAUDE.md should contain the same managed compatibility block");
|
|
206
|
+
assert(read(cwd, "AGENTS.md").includes("intentionally duplicated in `AGENTS.md` and `CLAUDE.md`"), "English AGENTS.md should explain intentional compatibility duplication");
|
|
207
|
+
assert(read(cwd, "AGENTS.md").includes("before `ai/template/prompt.md` routes"), "English AGENTS.md should stay a short router");
|
|
208
|
+
assert(!read(cwd, "AGENTS.md").includes("ai/template/bootstrap.md"), "English AGENTS.md should not duplicate bootstrap routing details");
|
|
209
|
+
assert(!read(cwd, "CLAUDE.md").includes("ai/template/bootstrap.md"), "English CLAUDE.md should not duplicate bootstrap routing details");
|
|
164
210
|
assert(read(cwd, "ai/template/LANG") === "en\n", "init --lang en should install English template");
|
|
165
211
|
assert(exists(cwd, "ai/template/execution-policy.md"), "English init should create execution policy prompt");
|
|
212
|
+
assert(exists(cwd, "ai/template/schemas/result.schema.json"), "English init should create result schema");
|
|
213
|
+
assert(exists(cwd, "ai/template/schemas/metrics.schema.json"), "English init should create metrics schema");
|
|
166
214
|
assert(read(cwd, "ai/template/bootstrap.md").includes("Confirmation Dimensions"), "English init should install English bootstrap prompt");
|
|
167
215
|
assert(read(cwd, "ai/template/bootstrap.md").includes("Do not summarize this file"), "English bootstrap prompt should prevent summary-only behavior");
|
|
168
216
|
assert(read(cwd, "ai/template/bootstrap.md").includes("ai/project/refs/final-shape.md"), "English bootstrap prompt should initialize the North Star");
|
|
@@ -175,7 +223,9 @@ function testEnglishInitUpdateDoctor() {
|
|
|
175
223
|
assert(read(cwd, "ai/template/bootstrap.md").includes("Unabsorbed material"), "English bootstrap handoff should audit unabsorbed material");
|
|
176
224
|
assert(read(cwd, "ai/template/bootstrap.md").includes("Conflict handling"), "English bootstrap handoff should audit conflict handling");
|
|
177
225
|
assert(read(cwd, "ai/template/prompt.md").includes("Start initializing this project"), "English execution prompt should route natural bootstrap entry");
|
|
178
|
-
assert(read(cwd, "ai/template/prompt.md").includes("
|
|
226
|
+
assert(read(cwd, "ai/template/prompt.md").includes("This file only routes the workflow"), "English execution prompt should be a lightweight router");
|
|
227
|
+
assert(read(cwd, "ai/template/prompt.md").includes("Only then read `ai/template/protocol.md`"), "English execution prompt should lazy-load execution policy only for execution");
|
|
228
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("Do not treat `AGENTS.md` or `CLAUDE.md` as project business evidence"), "English bootstrap should not treat root agent entrypoints as project evidence");
|
|
179
229
|
assert(read(cwd, "ai/template/execution-policy.md").includes("Risk Rubric"), "English execution policy should include risk rubric");
|
|
180
230
|
assert(read(cwd, "ai/template/execution-policy.md").includes("execution_policy.task_tree"), "English execution policy should require task tree persistence");
|
|
181
231
|
assert(read(cwd, "ai/template/prompt.md").includes("default to only `ai/project/inbox/*.md`"), "English execution prompt should narrow inbox reconciliation");
|
|
@@ -186,6 +236,8 @@ function testEnglishInitUpdateDoctor() {
|
|
|
186
236
|
assert(read(cwd, "ai/template/execution-policy.md").includes("every tiny L3 operation"), "English execution policy should limit task tree write-back churn");
|
|
187
237
|
assert(read(cwd, "ai/template/execution-policy.md").includes("public interfaces, data models, permissions"), "English execution policy should constrain Yellow corrections");
|
|
188
238
|
assert(read(cwd, "ai/template/execution-policy.md").includes("User-Visible Output"), "English execution policy should define user-visible output rules");
|
|
239
|
+
assert(read(cwd, "ai/template/execution-policy.md").includes("user-visible plans"), "English execution policy should keep user-visible planning in the installed language");
|
|
240
|
+
assert(read(cwd, "ai/template/rules/output.md").includes("installed language from `ai/template/LANG`"), "English output rules should keep human-readable results in the installed language");
|
|
189
241
|
assert(read(cwd, "ai/template/execution-policy.md").includes("do not show full L2/L3/L4 by default"), "English execution policy should avoid exposing full subtask trees by default");
|
|
190
242
|
assert(read(cwd, "ai/template/execution-policy.md").includes("do not show internal protocol fields"), "English execution policy should hide internal protocol details by default");
|
|
191
243
|
assert(read(cwd, "ai/template/execution-policy.md").includes("Automatically use `bounded_continuous`"), "English protocol should auto-enable continuous execution from L1 count");
|
|
@@ -222,7 +274,9 @@ function testEnglishInitUpdateDoctor() {
|
|
|
222
274
|
assert(read(cwd, "ai/template/reconcile.md").includes("Unabsorbed material"), "English reconcile handoff should audit unabsorbed material");
|
|
223
275
|
assert(read(cwd, "ai/template/reconcile.md").includes("Conflict handling"), "English reconcile handoff should audit conflict handling");
|
|
224
276
|
assert(initOutput.includes("Start initializing this project"), "English init output should provide English bootstrap prompt");
|
|
225
|
-
assert(initOutput.includes("
|
|
277
|
+
assert(initOutput.includes("root AI compatibility entrypoints: AGENTS.md / CLAUDE.md"), "English init output should mention root AI compatibility entrypoints");
|
|
278
|
+
assert(initOutput.includes("protocol installed. Project context is not initialized yet"), "English init output should distinguish protocol install from project context bootstrap");
|
|
279
|
+
assert(initOutput.includes("[Next: ask the AI to initialize project context]"), "English init output should guide the AI bootstrap as the next step");
|
|
226
280
|
assert(initOutput.includes("Start initializing this project and absorb the material in ai/project/inbox/"), "English init output should explain bootstrap with existing material");
|
|
227
281
|
assert(initOutput.includes("Reconcile the new material in ai/project/inbox/"), "English init output should provide English reconcile prompt");
|
|
228
282
|
assert(initOutput.includes("Improve context"), "English init output should expose context refresh in user language");
|
|
@@ -240,7 +294,9 @@ function testEnglishInitUpdateDoctor() {
|
|
|
240
294
|
|
|
241
295
|
const doctorOutput = run(["doctor"], cwd);
|
|
242
296
|
assert(doctorOutput.includes("Template language: en"), "doctor should show installed English language");
|
|
297
|
+
assert(doctorOutput.includes("root AI compatibility entrypoints installed: AGENTS.md / CLAUDE.md"), "English doctor should report root AI compatibility entrypoint status");
|
|
243
298
|
assert(doctorOutput.includes("ai/project/result.json JSON"), "English doctor should validate result JSON");
|
|
299
|
+
assert(doctorOutput.includes("ai/project/result.json schema"), "English doctor should validate result schema");
|
|
244
300
|
assert(doctorOutput.includes("ai/project/task.md front matter"), "English doctor should validate task front matter");
|
|
245
301
|
assert(doctorOutput.includes("[OK] Ready to run"), "doctor should use installed English language");
|
|
246
302
|
const reconcileOutput = run(["reconcile"], cwd);
|
|
@@ -271,6 +327,60 @@ function testDoctorFailureAndWarning() {
|
|
|
271
327
|
const invalidJsonOutput = run(["doctor"], invalidJsonCwd, 1);
|
|
272
328
|
assert(invalidJsonOutput.includes("JSON 无效"), "doctor should fail invalid result JSON");
|
|
273
329
|
|
|
330
|
+
const invalidResultSchemaCwd = createTempProject("agent-execution-template-invalid-result-schema");
|
|
331
|
+
run(["init"], invalidResultSchemaCwd);
|
|
332
|
+
write(invalidResultSchemaCwd, "ai/project/result.json", JSON.stringify({
|
|
333
|
+
protocol_version: "0.8",
|
|
334
|
+
status: "success",
|
|
335
|
+
scope_followed: true,
|
|
336
|
+
files_read: [],
|
|
337
|
+
refs_read: [],
|
|
338
|
+
files_changed: [],
|
|
339
|
+
commands_run: [],
|
|
340
|
+
verification: {
|
|
341
|
+
level: "none",
|
|
342
|
+
passed: false,
|
|
343
|
+
evidence: []
|
|
344
|
+
},
|
|
345
|
+
assumptions: [],
|
|
346
|
+
issues: [],
|
|
347
|
+
next: [],
|
|
348
|
+
runtime_update: {
|
|
349
|
+
required: false,
|
|
350
|
+
changes: [],
|
|
351
|
+
reason: ""
|
|
352
|
+
}
|
|
353
|
+
}, null, 2));
|
|
354
|
+
const invalidResultSchemaOutput = run(["doctor"], invalidResultSchemaCwd, 1);
|
|
355
|
+
assert(invalidResultSchemaOutput.includes("不符合协议 schema"), "doctor should fail result schema violations");
|
|
356
|
+
assert(invalidResultSchemaOutput.includes("$.verification.passed must be true"), "doctor should enforce success verification");
|
|
357
|
+
|
|
358
|
+
const invalidMetricsSchemaCwd = createTempProject("agent-execution-template-invalid-metrics-schema");
|
|
359
|
+
run(["init"], invalidMetricsSchemaCwd);
|
|
360
|
+
write(invalidMetricsSchemaCwd, "ai/project/metrics.json", JSON.stringify({
|
|
361
|
+
protocol_version: "0.8",
|
|
362
|
+
task_id: "",
|
|
363
|
+
task_type: "",
|
|
364
|
+
model: "",
|
|
365
|
+
model_tier: "cheap",
|
|
366
|
+
escalated: true,
|
|
367
|
+
escalation_reason: "",
|
|
368
|
+
model_policy_followed: true,
|
|
369
|
+
escalation_trigger_hit: "",
|
|
370
|
+
strong_model_role: "",
|
|
371
|
+
input_tokens_estimated: 0,
|
|
372
|
+
output_tokens_estimated: 0,
|
|
373
|
+
duration_minutes: 0,
|
|
374
|
+
success: false,
|
|
375
|
+
human_fix_required: false,
|
|
376
|
+
failure_reason: "",
|
|
377
|
+
reuse_potential: "low",
|
|
378
|
+
notes: []
|
|
379
|
+
}, null, 2));
|
|
380
|
+
const invalidMetricsSchemaOutput = run(["doctor"], invalidMetricsSchemaCwd, 1);
|
|
381
|
+
assert(invalidMetricsSchemaOutput.includes("不符合协议 schema"), "doctor should fail metrics schema violations");
|
|
382
|
+
assert(invalidMetricsSchemaOutput.includes("$.escalation_reason must have length >= 1"), "doctor should enforce escalated metrics details");
|
|
383
|
+
|
|
274
384
|
const taskWarnCwd = createTempProject("agent-execution-template-task-frontmatter");
|
|
275
385
|
run(["init"], taskWarnCwd);
|
|
276
386
|
write(taskWarnCwd, "ai/project/task.md", "# Task only\n");
|
|
@@ -297,6 +407,32 @@ permission: {}
|
|
|
297
407
|
assert(taskPolicyWarnOutput.includes("任务 front matter 缺少关键字段"), "doctor should warn when execution policy fields are incomplete");
|
|
298
408
|
}
|
|
299
409
|
|
|
410
|
+
function testRootEntrypointPreservesUserContent() {
|
|
411
|
+
const cwd = createTempProject("agent-execution-template-entrypoints");
|
|
412
|
+
write(cwd, "AGENTS.md", "# Existing agent rules\n\nKeep this user rule.\n");
|
|
413
|
+
|
|
414
|
+
run(["init"], cwd);
|
|
415
|
+
assert(read(cwd, "AGENTS.md").includes("Keep this user rule."), "init should preserve existing AGENTS.md content");
|
|
416
|
+
assert(read(cwd, "AGENTS.md").includes("agent-execution-template:start"), "init should append a managed AGENTS.md block");
|
|
417
|
+
assert(countOccurrences(read(cwd, "AGENTS.md"), "agent-execution-template:start") === 1, "init should append one AGENTS.md block");
|
|
418
|
+
|
|
419
|
+
run(["init"], cwd);
|
|
420
|
+
assert(read(cwd, "AGENTS.md").includes("Keep this user rule."), "re-running init should preserve existing AGENTS.md content");
|
|
421
|
+
assert(countOccurrences(read(cwd, "AGENTS.md"), "agent-execution-template:start") === 1, "re-running init should replace, not duplicate, the managed AGENTS.md block");
|
|
422
|
+
|
|
423
|
+
run(["update"], cwd);
|
|
424
|
+
assert(read(cwd, "AGENTS.md").includes("Keep this user rule."), "update should preserve existing AGENTS.md content");
|
|
425
|
+
assert(countOccurrences(read(cwd, "AGENTS.md"), "agent-execution-template:start") === 1, "update should keep one managed AGENTS.md block");
|
|
426
|
+
|
|
427
|
+
fs.unlinkSync(path.join(cwd, "CLAUDE.md"));
|
|
428
|
+
const missingClaudeOutput = run(["doctor"], cwd);
|
|
429
|
+
assert(missingClaudeOutput.includes("缺少根目录 AI 兼容入口托管块"), "doctor should warn when one root agent entrypoint is missing");
|
|
430
|
+
|
|
431
|
+
fs.unlinkSync(path.join(cwd, "AGENTS.md"));
|
|
432
|
+
const doctorOutput = run(["doctor"], cwd);
|
|
433
|
+
assert(doctorOutput.includes("缺少根目录 AI 兼容入口托管块"), "doctor should warn when root agent entrypoints are missing");
|
|
434
|
+
}
|
|
435
|
+
|
|
300
436
|
function testRefreshBacksUpAndImportsOldProject() {
|
|
301
437
|
const cwd = createTempProject("agent-execution-template-refresh");
|
|
302
438
|
run(["init"], cwd);
|
|
@@ -380,6 +516,7 @@ function main() {
|
|
|
380
516
|
testInitUpdateDoctor();
|
|
381
517
|
testEnglishInitUpdateDoctor();
|
|
382
518
|
testDoctorFailureAndWarning();
|
|
519
|
+
testRootEntrypointPreservesUserContent();
|
|
383
520
|
testRefreshBacksUpAndImportsOldProject();
|
|
384
521
|
testNextCommandRoutesByProjectState();
|
|
385
522
|
testPermissionErrorIsActionable();
|