@wnlen/agent-execution-template 0.8.20 → 0.8.22

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/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,6 +59,16 @@ 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");
@@ -80,7 +101,9 @@ function testInitUpdateDoctor() {
80
101
  assert(read(cwd, "ai/template/bootstrap.md").includes("未吸收资料"), "bootstrap handoff should audit unabsorbed material");
81
102
  assert(read(cwd, "ai/template/bootstrap.md").includes("冲突处理"), "bootstrap handoff should audit conflict handling");
82
103
  assert(read(cwd, "ai/template/prompt.md").includes("任务草稿交接"), "execution prompt should include task handoff");
83
- assert(read(cwd, "ai/template/prompt.md").includes("ai/template/execution-policy.md"), "execution prompt should read execution policy");
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");
84
107
  assert(read(cwd, "ai/template/execution-policy.md").includes("风险分级"), "execution policy should include risk rubric");
85
108
  assert(read(cwd, "ai/template/execution-policy.md").includes("execution_policy.task_tree"), "execution policy should require task tree persistence");
86
109
  assert(read(cwd, "ai/template/prompt.md").includes("也默认只处理 `ai/project/inbox/*.md`"), "execution prompt should narrow inbox reconciliation");
@@ -103,12 +126,12 @@ function testInitUpdateDoctor() {
103
126
  assert(read(cwd, "ai/template/rules/core.md").includes("需要扩大范围、权限、命令、网络或验收时"), "core rules should stop continuous execution before boundary expansion");
104
127
  assert(read(cwd, "ai/project/task.md").includes("execution_policy:"), "task template should include execution policy");
105
128
  assert(read(cwd, "ai/project/task.md").includes("readiness:"), "task template should include readiness state");
106
- assert(read(cwd, "ai/project/task.md").includes("activation_rule: \"auto_enable_when_l1_count_gte_2\""), "task template should define automatic activation rule");
107
- assert(read(cwd, "ai/project/task.md").includes("l1_granularity: \"independently_acceptable_vertical_slice\""), "task template should define L1 granularity");
108
- assert(read(cwd, "ai/project/task.md").includes("write_back_policy: \"l1_start_done_red_blocked_scope_change_final\""), "task template should define task tree write-back policy");
109
- assert(read(cwd, "ai/project/task.md").includes("risk_gate:"), "task template should define risk gate");
129
+ assert(read(cwd, "ai/project/task.md").includes("compact task contract"), "task template should default to compact contracts");
130
+ assert(read(cwd, "ai/project/task.md").includes("expanded 任务"), "task template should explain when to expand task contracts");
131
+ assert(!read(cwd, "ai/project/task.md").includes("activation_rule: \"auto_enable_when_l1_count_gte_2\""), "task template should not default to expanded activation metadata");
132
+ assert(!read(cwd, "ai/project/task.md").includes("checkpoint_budget:"), "task template should not default to checkpoint budget fields");
133
+ assert(!read(cwd, "ai/project/task.md").includes("model_policy:"), "task template should not default to model policy fields");
110
134
  assert(read(cwd, "ai/project/task.md").includes("status: \"pending | running | done | blocked\""), "task template should define task tree node status");
111
- assert(read(cwd, "ai/project/task.md").includes("progress_unit: \"vertical_slice\""), "task template should define continuous progress unit");
112
135
  assert(read(cwd, "ai/template/prompt.md").includes("开始初始化这个项目"), "execution prompt should route natural bootstrap entry");
113
136
  assert(read(cwd, "ai/template/prompt.md").includes("开始初始化这个项目,并吸收 ai/project/inbox/ 里的资料"), "execution prompt should route bootstrap with inbox material");
114
137
  assert(read(cwd, "ai/template/prompt.md").includes("不要重新 bootstrap"), "execution prompt should reconcile inbox material when project context already exists");
@@ -132,17 +155,23 @@ function testInitUpdateDoctor() {
132
155
  assert(read(cwd, "ai/project/proposals/final-shape-updates/_template.md").includes("`accepted`"), "proposal template should describe accepted status");
133
156
  const initOutput = run(["init"], cwd);
134
157
  assert(initOutput.includes("开始初始化这个项目"), "init output should provide compact natural bootstrap prompt");
135
- assert(initOutput.includes("[初始化]"), "init output should distinguish pre-bootstrap guidance");
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 coding 工具"), "init output should guide the AI bootstrap as the next step");
136
161
  assert(initOutput.includes("开始初始化这个项目,并吸收 ai/project/inbox/ 里的资料"), "init output should explain bootstrap with existing material");
137
- assert(initOutput.includes("整合 ai/project/inbox/ 里的新资料"), "init output should provide compact natural reconcile prompt");
138
- assert(initOutput.includes("优化上下文"), "init output should expose project context refresh in user language");
139
- assert(initOutput.includes("把 ai/project/inbox/ideas/ 里的新灵感生成方向修订提案"), "init output should provide natural strategy prompt");
162
+ assert(initOutput.includes("已确定资料: ai/project/inbox/"), "init output should explain confirmed material path");
163
+ assert(initOutput.includes("未决定的新想法: ai/project/inbox/ideas/"), "init output should explain idea inbox path");
140
164
  assert(initOutput.includes("agent-execution-template next"), "init output should tell users how to recover the next step");
141
- assert(initOutput.includes("文件:"), "init output should summarize file changes");
165
+ assert(initOutput.includes("文件已就绪:"), "init output should summarize file changes");
166
+ assert(initOutput.includes("检查安装:"), "init output should show install check command");
142
167
  assert(!initOutput.includes("维护者提示"), "init output should not show source checkout guidance in user projects");
143
168
  assert(!initOutput.includes("[已更新]"), "init output should hide detailed file changes by default");
144
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");
145
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");
146
175
  const reconcileOutput = run(["reconcile"], cwd);
147
176
  assert(reconcileOutput.includes("Agent Execution Template 上下文整合"), "reconcile should use installed Chinese language");
148
177
  assert(reconcileOutput.includes("整合 ai/project/inbox/ 里的新资料"), "reconcile should print natural Chinese prompt");
@@ -154,8 +183,10 @@ function testInitUpdateDoctor() {
154
183
  write(cwd, "ai/project/project.md", "USER PROJECT MARKER\n");
155
184
  run(["update"], cwd);
156
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");
157
187
 
158
188
  const doctorOutput = run(["doctor"], cwd);
189
+ assert(doctorOutput.includes("根目录 AI 兼容入口已安装: AGENTS.md / CLAUDE.md"), "doctor should report root AI compatibility entrypoint status");
159
190
  assert(doctorOutput.includes("ai/project/result.json JSON"), "doctor should validate result JSON");
160
191
  assert(doctorOutput.includes("ai/project/result.json schema"), "doctor should validate result schema");
161
192
  assert(doctorOutput.includes("ai/project/metrics.json JSON"), "doctor should validate metrics JSON");
@@ -167,6 +198,15 @@ function testEnglishInitUpdateDoctor() {
167
198
  const cwd = createTempProject("agent-execution-template-en");
168
199
 
169
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");
170
210
  assert(read(cwd, "ai/template/LANG") === "en\n", "init --lang en should install English template");
171
211
  assert(exists(cwd, "ai/template/execution-policy.md"), "English init should create execution policy prompt");
172
212
  assert(exists(cwd, "ai/template/schemas/result.schema.json"), "English init should create result schema");
@@ -183,7 +223,9 @@ function testEnglishInitUpdateDoctor() {
183
223
  assert(read(cwd, "ai/template/bootstrap.md").includes("Unabsorbed material"), "English bootstrap handoff should audit unabsorbed material");
184
224
  assert(read(cwd, "ai/template/bootstrap.md").includes("Conflict handling"), "English bootstrap handoff should audit conflict handling");
185
225
  assert(read(cwd, "ai/template/prompt.md").includes("Start initializing this project"), "English execution prompt should route natural bootstrap entry");
186
- assert(read(cwd, "ai/template/prompt.md").includes("ai/template/execution-policy.md"), "English execution prompt should read execution policy");
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");
187
229
  assert(read(cwd, "ai/template/execution-policy.md").includes("Risk Rubric"), "English execution policy should include risk rubric");
188
230
  assert(read(cwd, "ai/template/execution-policy.md").includes("execution_policy.task_tree"), "English execution policy should require task tree persistence");
189
231
  assert(read(cwd, "ai/template/prompt.md").includes("default to only `ai/project/inbox/*.md`"), "English execution prompt should narrow inbox reconciliation");
@@ -206,12 +248,12 @@ function testEnglishInitUpdateDoctor() {
206
248
  assert(read(cwd, "ai/template/rules/core.md").includes("expand scope, permission, commands, network access, or acceptance"), "English core rules should stop continuous execution before boundary expansion");
207
249
  assert(read(cwd, "ai/project/task.md").includes("execution_policy:"), "English task template should include execution policy");
208
250
  assert(read(cwd, "ai/project/task.md").includes("readiness:"), "English task template should include readiness state");
209
- assert(read(cwd, "ai/project/task.md").includes("activation_rule: \"auto_enable_when_l1_count_gte_2\""), "English task template should define automatic activation rule");
210
- assert(read(cwd, "ai/project/task.md").includes("l1_granularity: \"independently_acceptable_vertical_slice\""), "English task template should define L1 granularity");
211
- assert(read(cwd, "ai/project/task.md").includes("write_back_policy: \"l1_start_done_red_blocked_scope_change_final\""), "English task template should define task tree write-back policy");
212
- assert(read(cwd, "ai/project/task.md").includes("risk_gate:"), "English task template should define risk gate");
251
+ assert(read(cwd, "ai/project/task.md").includes("compact task contract"), "English task template should default to compact contracts");
252
+ assert(read(cwd, "ai/project/task.md").includes("Expanded tasks"), "English task template should explain when to expand task contracts");
253
+ assert(!read(cwd, "ai/project/task.md").includes("activation_rule: \"auto_enable_when_l1_count_gte_2\""), "English task template should not default to expanded activation metadata");
254
+ assert(!read(cwd, "ai/project/task.md").includes("checkpoint_budget:"), "English task template should not default to checkpoint budget fields");
255
+ assert(!read(cwd, "ai/project/task.md").includes("model_policy:"), "English task template should not default to model policy fields");
213
256
  assert(read(cwd, "ai/project/task.md").includes("status: \"pending | running | done | blocked\""), "English task template should define task tree node status");
214
- assert(read(cwd, "ai/project/task.md").includes("progress_unit: \"vertical_slice\""), "English task template should define continuous progress unit");
215
257
  assert(read(cwd, "ai/template/prompt.md").includes("Start initializing this project and absorb the material in ai/project/inbox/"), "English execution prompt should route bootstrap with inbox material");
216
258
  assert(read(cwd, "ai/template/prompt.md").includes("instead of bootstrapping again"), "English execution prompt should reconcile inbox material when project context already exists");
217
259
  assert(read(cwd, "ai/template/prompt.md").includes("Reconcile the new material in ai/project/inbox/"), "English execution prompt should route natural reconcile entry");
@@ -232,13 +274,15 @@ function testEnglishInitUpdateDoctor() {
232
274
  assert(read(cwd, "ai/template/reconcile.md").includes("Unabsorbed material"), "English reconcile handoff should audit unabsorbed material");
233
275
  assert(read(cwd, "ai/template/reconcile.md").includes("Conflict handling"), "English reconcile handoff should audit conflict handling");
234
276
  assert(initOutput.includes("Start initializing this project"), "English init output should provide English bootstrap prompt");
235
- assert(initOutput.includes("[Initialize]"), "English init output should distinguish pre-bootstrap guidance");
277
+ assert(initOutput.includes("Root AI 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, send this to your AI coding tool"), "English init output should guide the AI bootstrap as the next step");
236
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");
237
- assert(initOutput.includes("Reconcile the new material in ai/project/inbox/"), "English init output should provide English reconcile prompt");
238
- assert(initOutput.includes("Improve context"), "English init output should expose context refresh in user language");
239
- assert(initOutput.includes("Generate a direction amendment proposal from ai/project/inbox/ideas/"), "English init output should provide natural strategy prompt");
281
+ assert(initOutput.includes("Confirmed material: ai/project/inbox/"), "English init output should explain confirmed material path");
282
+ assert(initOutput.includes("Undecided ideas: ai/project/inbox/ideas/"), "English init output should explain idea inbox path");
240
283
  assert(initOutput.includes("agent-execution-template next"), "English init output should tell users how to recover the next step");
241
- assert(initOutput.includes("Files:"), "English init output should summarize file changes");
284
+ assert(initOutput.includes("Files ready:"), "English init output should summarize file changes");
285
+ assert(initOutput.includes("Check install:"), "English init output should show install check command");
242
286
  assert(!initOutput.includes("Maintainer note"), "English init output should not show source checkout guidance in user projects");
243
287
  assert(!initOutput.includes("[UPDATED]"), "English init output should hide detailed file changes by default");
244
288
  assert(run(["init", "--lang=en", "--verbose"], cwd).includes("[UPDATED] ai/template/VERSION"), "English init --verbose should show detailed file changes");
@@ -250,6 +294,7 @@ function testEnglishInitUpdateDoctor() {
250
294
 
251
295
  const doctorOutput = run(["doctor"], cwd);
252
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");
253
298
  assert(doctorOutput.includes("ai/project/result.json JSON"), "English doctor should validate result JSON");
254
299
  assert(doctorOutput.includes("ai/project/result.json schema"), "English doctor should validate result schema");
255
300
  assert(doctorOutput.includes("ai/project/task.md front matter"), "English doctor should validate task front matter");
@@ -362,6 +407,32 @@ permission: {}
362
407
  assert(taskPolicyWarnOutput.includes("任务 front matter 缺少关键字段"), "doctor should warn when execution policy fields are incomplete");
363
408
  }
364
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
+
365
436
  function testRefreshBacksUpAndImportsOldProject() {
366
437
  const cwd = createTempProject("agent-execution-template-refresh");
367
438
  run(["init"], cwd);
@@ -445,6 +516,7 @@ function main() {
445
516
  testInitUpdateDoctor();
446
517
  testEnglishInitUpdateDoctor();
447
518
  testDoctorFailureAndWarning();
519
+ testRootEntrypointPreservesUserContent();
448
520
  testRefreshBacksUpAndImportsOldProject();
449
521
  testNextCommandRoutesByProjectState();
450
522
  testPermissionErrorIsActionable();