@wnlen/agent-execution-template 0.8.17 → 0.8.18

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 CHANGED
@@ -444,11 +444,25 @@ Run the self-test:
444
444
  npm test
445
445
  ```
446
446
 
447
+ Run the release consistency check:
448
+
449
+ ```bash
450
+ npm run check:release
451
+ ```
452
+
447
453
  The test suite verifies the core CLI contract:
448
454
 
449
455
  - `init` creates the expected protocol and project files.
450
456
  - `update` does not overwrite `ai/project/**`.
451
457
  - `doctor` reports missing and empty required files correctly.
458
+ - `check:release` verifies versions, template shape, installed protocol state,
459
+ and the spec's package version.
460
+
461
+ When maintaining this npm package source checkout, test the local CLI with
462
+ `node bin/agent-execution-template.js <command>`. Use
463
+ `npx -y @wnlen/agent-execution-template <command>` in user projects only.
464
+ Maintainer-local `ai/project/**` bootstrap content should not be committed as
465
+ product changes.
452
466
 
453
467
  ## Contributing
454
468
 
package/README.zh-CN.md CHANGED
@@ -449,11 +449,22 @@ License: MIT
449
449
  npm test
450
450
  ```
451
451
 
452
+ 发布前检查:
453
+
454
+ ```bash
455
+ npm run check:release
456
+ ```
457
+
452
458
  测试会验证核心 CLI 契约:
453
459
 
454
460
  - `init` 创建预期的协议和项目文件。
455
461
  - `update` 不覆盖 `ai/project/**`。
456
462
  - `doctor` 正确报告缺失文件和空的必要文件。
463
+ - `check:release` 验证版本号、模板结构、安装态协议和规格文档一致。
464
+
465
+ 维护这个 npm 包源码仓库时,用 `node bin/agent-execution-template.js <command>`
466
+ 测试当前 checkout;用户项目才使用 `npx -y @wnlen/agent-execution-template <command>`。
467
+ 维护者本地 `ai/project/**` 初始化内容不应作为产品改动提交。
457
468
 
458
469
  ## 贡献
459
470
 
@@ -155,6 +155,10 @@ const TEXT = {
155
155
  nextReviewProposal: "已有方向修订提案。先审查提案;确认后对 AI 说:",
156
156
  nextContinuePrompt: "继续推进这个项目。执行前先拆 L1 任务;若 L1 >= 2,自动启用边界内连续执行;只有 Red 风险停下来确认。",
157
157
  repairHint: "缺失的 project 推荐文件可通过重新运行 init 安全补齐;已有 ai/project/** 不会被覆盖。",
158
+ sourceCheckoutNotice: `维护者提示: 当前目录看起来是 @wnlen/agent-execution-template 源码仓库。
159
+ 源码仓库内调试请使用: node bin/agent-execution-template.js <command>
160
+ 用户项目中安装才使用: npx -y @wnlen/agent-execution-template <command>
161
+ 不要把维护者本地初始化产生的 ai/project/** 当成产品改动提交。`,
158
162
  permissionDenied: "无法写入目标路径",
159
163
  permissionHint: `请检查 ai/** 的归属和权限。常见修复:
160
164
  sudo chown -R "$(id -un):$(id -gn)" ai
@@ -260,6 +264,10 @@ Usage:
260
264
  nextReviewProposal: "A direction amendment proposal exists. Review it first; after confirmation, tell the AI:",
261
265
  nextContinuePrompt: "Continue this project. Before execution, decompose L1 tasks; if L1 >= 2, automatically use bounded continuous execution; only Red risk stops for confirmation.",
262
266
  repairHint: "Missing recommended project files can be safely added by running init again; existing ai/project/** files are not overwritten.",
267
+ sourceCheckoutNotice: `Maintainer note: this directory looks like the @wnlen/agent-execution-template source checkout.
268
+ In the source repository, test with: node bin/agent-execution-template.js <command>
269
+ In user projects, install with: npx -y @wnlen/agent-execution-template <command>
270
+ Do not commit maintainer-local ai/project/** bootstrap content as product changes.`,
263
271
  permissionDenied: "Cannot write target path",
264
272
  permissionHint: `Check ownership and permissions under ai/**. Common fix:
265
273
  sudo chown -R "$(id -un):$(id -gn)" ai
@@ -291,6 +299,17 @@ function readPackageVersion() {
291
299
  }
292
300
  }
293
301
 
302
+ function readTargetPackageName() {
303
+ const packageFile = path.join(process.cwd(), "package.json");
304
+ if (!fs.existsSync(packageFile)) return null;
305
+ try {
306
+ const pkg = JSON.parse(fs.readFileSync(packageFile, "utf8"));
307
+ return pkg.name || null;
308
+ } catch {
309
+ return null;
310
+ }
311
+ }
312
+
294
313
  function readInstalledLang() {
295
314
  const langFile = path.join(TARGET_AI, "template", "LANG");
296
315
  if (!fs.existsSync(langFile)) return DEFAULT_LANG;
@@ -298,6 +317,24 @@ function readInstalledLang() {
298
317
  return SUPPORTED_LANGS.has(lang) ? lang : DEFAULT_LANG;
299
318
  }
300
319
 
320
+ function isSourceCheckout() {
321
+ return process.cwd() === PACKAGE_ROOT &&
322
+ readTargetPackageName() === "@wnlen/agent-execution-template";
323
+ }
324
+
325
+ function commandHint(command) {
326
+ if (isSourceCheckout()) {
327
+ return `node bin/agent-execution-template.js ${command}`;
328
+ }
329
+ return `npx -y @wnlen/agent-execution-template ${command}`;
330
+ }
331
+
332
+ function printSourceCheckoutNotice(lang) {
333
+ if (isSourceCheckout()) {
334
+ console.log(`${getText(lang).sourceCheckoutNotice}\n`);
335
+ }
336
+ }
337
+
301
338
  function parseLang(args, fallback = DEFAULT_LANG) {
302
339
  let lang = fallback;
303
340
  for (let index = 0; index < args.length; index += 1) {
@@ -487,12 +524,14 @@ function init({ lang = DEFAULT_LANG, verbose = false, quiet = false } = {}) {
487
524
  .map((change) => ({ ...change, path: path.join("ai/project", change.path) })));
488
525
 
489
526
  if (!quiet) {
527
+ const sourceNotice = isSourceCheckout() ? `${text.sourceCheckoutNotice}\n\n` : "";
490
528
  console.log(`${text.ready}
491
529
 
492
530
  ${text.initGuide}
493
531
 
532
+ ${sourceNotice}
494
533
  ${text.files}: ${summarizeChanges(changes, lang)}
495
- ${text.check}: npx -y @wnlen/agent-execution-template doctor
534
+ ${text.check}: ${commandHint("doctor")}
496
535
  `);
497
536
 
498
537
  if (verbose) {
@@ -546,12 +585,13 @@ function next({ lang = readInstalledLang() } = {}) {
546
585
  }
547
586
 
548
587
  console.log(`${text.nextTitle}\n`);
588
+ printSourceCheckoutNotice(lang);
549
589
 
550
590
  const templatePath = path.join(TARGET_AI, "template");
551
591
  const projectPath = path.join(TARGET_AI, "project");
552
592
  if (!fs.existsSync(templatePath) || !fs.existsSync(projectPath)) {
553
593
  console.log(`${text.nextRunInit}
554
- npx -y @wnlen/agent-execution-template init
594
+ ${commandHint("init")}
555
595
  `);
556
596
  return;
557
597
  }
@@ -678,6 +718,7 @@ function doctor() {
678
718
  const lang = readInstalledLang();
679
719
  const text = getText(lang);
680
720
  console.log(`${text.doctorTitle}\n`);
721
+ printSourceCheckoutNotice(lang);
681
722
  console.log(`${text.templateVersion}: ${readVersion(path.join(TARGET_AI, "template")) || text.unknown}`);
682
723
  console.log(`${text.templateLang}: ${lang}\n`);
683
724
 
@@ -748,7 +789,7 @@ function doctor() {
748
789
  }
749
790
 
750
791
  if (missing > 0) {
751
- console.log(`\n[${text.fail}] ${text.runInit}`);
792
+ console.log(`\n[${text.fail}] ${commandHint("init")}`);
752
793
  process.exitCode = 1;
753
794
  } else if (warnings > 0) {
754
795
  console.log(`\n[${text.pass}] ${text.readyWithWarnings}`);
package/docs/SPEC.md CHANGED
@@ -22,7 +22,7 @@ npx 安装协议 -> AI 整理项目上下文 -> 人类确认 -> AI 生成任务
22
22
 
23
23
  ```text
24
24
  Protocol: v0.8
25
- Package: @wnlen/agent-execution-template@0.8.17
25
+ Package: @wnlen/agent-execution-template@0.8.18
26
26
  中文安装: npx -y @wnlen/agent-execution-template init
27
27
  英文安装: npx -y @wnlen/agent-execution-template init --lang en
28
28
  ```
@@ -392,7 +392,7 @@ npx -y @wnlen/agent-execution-template doctor
392
392
  ```text
393
393
  Agent Execution Template 检查
394
394
 
395
- 模板版本: 0.8.17
395
+ 模板版本: 0.8.18
396
396
  模板语言: zh
397
397
 
398
398
  [通过] ai/template/LANG
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wnlen/agent-execution-template",
3
- "version": "0.8.17",
3
+ "version": "0.8.18",
4
4
  "description": "Low-friction AI execution protocol template for coding agents.",
5
5
  "bin": {
6
6
  "agent-execution-template": "bin/agent-execution-template.js"
@@ -14,7 +14,8 @@
14
14
  ],
15
15
  "scripts": {
16
16
  "doctor": "node bin/agent-execution-template.js doctor",
17
- "test": "node test/selftest.js"
17
+ "check:release": "node test/check-release.js",
18
+ "test": "node test/selftest.js && node test/check-release.js"
18
19
  },
19
20
  "keywords": [
20
21
  "ai",
@@ -114,22 +114,5 @@ From a real project back into the template repo:
114
114
  - Return only `ai/template/**`.
115
115
  - Never return `ai/project/**`.
116
116
 
117
- ## Bootstrap Rule
118
-
119
- - The human provides intent, hard constraints, and final acceptance.
120
- - The agent drafts `project/project.md` and relevant `project/refs/*` from existing docs, manifests, refs, and project files.
121
- - The agent drafts `project/task.md` after the human provides the current task goal.
122
- - The human reviews and confirms project and task drafts before execution.
123
- - Bootstrap may write only project context files, plus `project/task.md` when a current task is provided.
124
- - Bootstrap must not edit source code, tests, configuration, dependency files, generated files, runtime files, result files, or metrics files.
125
- - Ask at most 3 clarification questions.
126
- - Ask only when the answer changes scope, risk, permission, or acceptance.
127
- - Repeated assumptions should become `project/runtime.md` update proposals.
128
-
129
- ## Model Division Protocol
130
-
131
- - Model policy lives in `project/task.md.model_policy`.
132
- - Use `cheap` by default for routine execution.
133
- - Use `standard` for moderate implementation complexity.
134
- - Use `strong` only for planning, risk judgment, architecture review, failure review, or acceptance judgment.
135
- - Record actual tier, trigger, role, and escalation reason in `project/metrics.json`.
117
+ Detailed execution rules live in `template/protocol.md`,
118
+ `template/bootstrap.md`, `template/execution-policy.md`, and `template/rules/`.
@@ -1 +1 @@
1
- 0.8.17
1
+ 0.8.18
@@ -112,22 +112,5 @@ ai/project/inbox/ideas/
112
112
  - 只回流 `ai/template/**`。
113
113
  - 永远不要回流 `ai/project/**`。
114
114
 
115
- ## 引导规则
116
-
117
- - 人类提供意图、硬约束和最终验收。
118
- - Agent 从现有文档、清单、引用和项目文件中起草 `project/project.md` 和相关 `project/refs/*`。
119
- - 人类提供当前任务目标后,Agent 起草 `project/task.md`。
120
- - 人类在执行前检查并确认项目和任务草稿。
121
- - 引导只能写项目上下文文件;当提供当前任务时,也可以写 `project/task.md`。
122
- - 引导不得编辑源码、测试、配置、依赖文件、生成文件、运行时文件、结果文件或指标文件。
123
- - 最多问 3 个澄清问题。
124
- - 只在答案会改变范围、风险、权限或验收时提问。
125
- - 重复出现的假设应变成 `project/runtime.md` 更新建议。
126
-
127
- ## 模型分工协议
128
-
129
- - 模型策略位于 `project/task.md.model_policy`。
130
- - 常规执行默认使用 `cheap`。
131
- - 中等实现复杂度使用 `standard`。
132
- - 只有规划、风险判断、架构复核、失败复盘或验收判断才使用 `strong`。
133
- - 在 `project/metrics.json` 中记录实际档位、触发条件、角色和升级原因。
115
+ 详细执行规则见 `template/protocol.md`、`template/bootstrap.md`、
116
+ `template/execution-policy.md` 和 `template/rules/`。
@@ -1 +1 @@
1
- 0.8.17
1
+ 0.8.18
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const repoRoot = path.resolve(__dirname, "..");
7
+ const packagePath = path.join(repoRoot, "package.json");
8
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8"));
9
+
10
+ function fail(message) {
11
+ throw new Error(message);
12
+ }
13
+
14
+ function read(relativePath) {
15
+ return fs.readFileSync(path.join(repoRoot, relativePath), "utf8");
16
+ }
17
+
18
+ function listFiles(relativeDir) {
19
+ const root = path.join(repoRoot, relativeDir);
20
+ const files = [];
21
+
22
+ function walk(dir) {
23
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
24
+ const fullPath = path.join(dir, entry.name);
25
+ if (entry.isDirectory()) {
26
+ walk(fullPath);
27
+ } else if (entry.isFile()) {
28
+ files.push(path.relative(root, fullPath).split(path.sep).join("/"));
29
+ }
30
+ }
31
+ }
32
+
33
+ walk(root);
34
+ return files.sort();
35
+ }
36
+
37
+ function assertEqual(actual, expected, message) {
38
+ if (actual !== expected) {
39
+ fail(`${message}\nExpected: ${expected}\nActual: ${actual}`);
40
+ }
41
+ }
42
+
43
+ function assertIncludes(content, expected, message) {
44
+ if (!content.includes(expected)) {
45
+ fail(`${message}\nMissing: ${expected}`);
46
+ }
47
+ }
48
+
49
+ function assertFileListsMatch(leftDir, rightDir, message) {
50
+ const left = listFiles(leftDir).join("\n");
51
+ const right = listFiles(rightDir).join("\n");
52
+ assertEqual(left, right, message);
53
+ }
54
+
55
+ function assertTreesMatch(leftDir, rightDir, message) {
56
+ const leftFiles = listFiles(leftDir);
57
+ const rightFiles = listFiles(rightDir);
58
+ assertEqual(leftFiles.join("\n"), rightFiles.join("\n"), `${message}: file list differs`);
59
+
60
+ for (const file of leftFiles) {
61
+ const left = read(path.join(leftDir, file));
62
+ const right = read(path.join(rightDir, file));
63
+ assertEqual(left, right, `${message}: ${file} differs`);
64
+ }
65
+ }
66
+
67
+ function main() {
68
+ const version = packageJson.version;
69
+ assertEqual(packageJson.name, "@wnlen/agent-execution-template", "package name changed unexpectedly");
70
+ assertEqual(
71
+ packageJson.bin && packageJson.bin["agent-execution-template"],
72
+ "bin/agent-execution-template.js",
73
+ "package bin entry should point to the CLI"
74
+ );
75
+
76
+ assertEqual(read("template/zh/ai/template/VERSION").trim(), version, "zh template version must match package version");
77
+ assertEqual(read("template/en/ai/template/VERSION").trim(), version, "en template version must match package version");
78
+ assertEqual(read("ai/template/VERSION").trim(), version, "installed zh template version must match package version");
79
+
80
+ assertFileListsMatch("template/zh/ai", "template/en/ai", "zh and en templates must expose the same file layout");
81
+ assertTreesMatch("template/zh/ai/template", "ai/template", "installed ai/template must mirror template/zh/ai/template");
82
+ assertEqual(read("template/zh/ai/README.md"), read("ai/README.md"), "installed ai/README.md must mirror template/zh/ai/README.md");
83
+
84
+ const spec = read("docs/SPEC.md");
85
+ assertIncludes(spec, `Package: @wnlen/agent-execution-template@${version}`, "SPEC package version must match package.json");
86
+
87
+ const scripts = packageJson.scripts || {};
88
+ assertIncludes(scripts.test || "", "test/selftest.js", "npm test should run the CLI selftest");
89
+ assertIncludes(scripts.test || "", "test/check-release.js", "npm test should run release consistency checks");
90
+
91
+ console.log("release check ok");
92
+ }
93
+
94
+ main();
package/test/selftest.js CHANGED
@@ -122,6 +122,7 @@ function testInitUpdateDoctor() {
122
122
  assert(initOutput.includes("把 ai/project/inbox/ideas/ 里的新灵感生成方向修订提案"), "init output should provide natural strategy prompt");
123
123
  assert(initOutput.includes("agent-execution-template next"), "init output should tell users how to recover the next step");
124
124
  assert(initOutput.includes("文件:"), "init output should summarize file changes");
125
+ assert(!initOutput.includes("维护者提示"), "init output should not show source checkout guidance in user projects");
125
126
  assert(!initOutput.includes("[已更新]"), "init output should hide detailed file changes by default");
126
127
  assert(!initOutput.includes("Read ai/template/bootstrap.md"), "init output should not use weak Read bootstrap command");
127
128
  assert(run(["init", "--verbose"], cwd).includes("[已更新] ai/template/VERSION"), "init --verbose should show detailed file changes");
@@ -202,6 +203,7 @@ function testEnglishInitUpdateDoctor() {
202
203
  assert(initOutput.includes("Generate a direction amendment proposal from ai/project/inbox/ideas/"), "English init output should provide natural strategy prompt");
203
204
  assert(initOutput.includes("agent-execution-template next"), "English init output should tell users how to recover the next step");
204
205
  assert(initOutput.includes("Files:"), "English init output should summarize file changes");
206
+ assert(!initOutput.includes("Maintainer note"), "English init output should not show source checkout guidance in user projects");
205
207
  assert(!initOutput.includes("[UPDATED]"), "English init output should hide detailed file changes by default");
206
208
  assert(run(["init", "--lang=en", "--verbose"], cwd).includes("[UPDATED] ai/template/VERSION"), "English init --verbose should show detailed file changes");
207
209
 
@@ -338,6 +340,16 @@ function testPermissionErrorIsActionable() {
338
340
  }
339
341
  }
340
342
 
343
+ function testSourceCheckoutNotice() {
344
+ const doctorOutput = run(["doctor"], repoRoot);
345
+ assert(doctorOutput.includes("维护者提示"), "doctor should warn when run in the package source checkout");
346
+ assert(doctorOutput.includes("node bin/agent-execution-template.js <command>"), "source checkout notice should show local node command");
347
+ assert(doctorOutput.includes("不要把维护者本地初始化产生的 ai/project/** 当成产品改动提交"), "source checkout notice should warn against committing local bootstrap context");
348
+
349
+ const nextOutput = run(["next"], repoRoot);
350
+ assert(nextOutput.includes("维护者提示"), "next should warn when run in the package source checkout");
351
+ }
352
+
341
353
  function main() {
342
354
  testInitUpdateDoctor();
343
355
  testEnglishInitUpdateDoctor();
@@ -345,6 +357,7 @@ function main() {
345
357
  testRefreshBacksUpAndImportsOldProject();
346
358
  testNextCommandRoutesByProjectState();
347
359
  testPermissionErrorIsActionable();
360
+ testSourceCheckoutNotice();
348
361
  console.log("selftest ok");
349
362
  }
350
363