@wnlen/agent-execution-template 0.8.14
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/LICENSE +21 -0
- package/README.md +440 -0
- package/README.zh-CN.md +447 -0
- package/bin/agent-execution-template.js +792 -0
- package/docs/SPEC.md +1039 -0
- package/docs/token-efficient-protocol-v0.1.md +101 -0
- package/package.json +35 -0
- package/template/en/ai/README.md +130 -0
- package/template/en/ai/project/archive/.gitkeep +0 -0
- package/template/en/ai/project/inbox/.gitkeep +1 -0
- package/template/en/ai/project/inbox/ideas/.gitkeep +1 -0
- package/template/en/ai/project/inbox/processed/.gitkeep +0 -0
- package/template/en/ai/project/inbox/raw/.gitkeep +1 -0
- package/template/en/ai/project/metrics.json +20 -0
- package/template/en/ai/project/project.md +42 -0
- package/template/en/ai/project/proposals/final-shape-updates/.gitkeep +1 -0
- package/template/en/ai/project/proposals/final-shape-updates/_template.md +64 -0
- package/template/en/ai/project/refs/architecture.md +23 -0
- package/template/en/ai/project/refs/commands.md +43 -0
- package/template/en/ai/project/refs/constraints.md +24 -0
- package/template/en/ai/project/refs/decisions.md +13 -0
- package/template/en/ai/project/refs/final-shape.md +59 -0
- package/template/en/ai/project/refs/module-map.md +31 -0
- package/template/en/ai/project/refs/roadmap.md +31 -0
- package/template/en/ai/project/result.json +34 -0
- package/template/en/ai/project/result.md +32 -0
- package/template/en/ai/project/runtime.md +74 -0
- package/template/en/ai/project/task.md +110 -0
- package/template/en/ai/template/LANG +1 -0
- package/template/en/ai/template/VERSION +1 -0
- package/template/en/ai/template/bootstrap.md +194 -0
- package/template/en/ai/template/prompt.md +118 -0
- package/template/en/ai/template/protocol.md +332 -0
- package/template/en/ai/template/reconcile.md +140 -0
- package/template/en/ai/template/rules/core.md +197 -0
- package/template/en/ai/template/rules/output.md +51 -0
- package/template/en/ai/template/schemas/metrics.schema.json +119 -0
- package/template/en/ai/template/schemas/result.schema.json +234 -0
- package/template/zh/ai/README.md +129 -0
- package/template/zh/ai/project/archive/.gitkeep +0 -0
- package/template/zh/ai/project/inbox/.gitkeep +1 -0
- package/template/zh/ai/project/inbox/ideas/.gitkeep +1 -0
- package/template/zh/ai/project/inbox/processed/.gitkeep +0 -0
- package/template/zh/ai/project/inbox/raw/.gitkeep +1 -0
- package/template/zh/ai/project/metrics.json +20 -0
- package/template/zh/ai/project/project.md +42 -0
- package/template/zh/ai/project/proposals/final-shape-updates/.gitkeep +1 -0
- package/template/zh/ai/project/proposals/final-shape-updates/_template.md +64 -0
- package/template/zh/ai/project/refs/architecture.md +23 -0
- package/template/zh/ai/project/refs/commands.md +43 -0
- package/template/zh/ai/project/refs/constraints.md +24 -0
- package/template/zh/ai/project/refs/decisions.md +13 -0
- package/template/zh/ai/project/refs/final-shape.md +54 -0
- package/template/zh/ai/project/refs/module-map.md +30 -0
- package/template/zh/ai/project/refs/roadmap.md +29 -0
- package/template/zh/ai/project/result.json +34 -0
- package/template/zh/ai/project/result.md +32 -0
- package/template/zh/ai/project/runtime.md +74 -0
- package/template/zh/ai/project/task.md +106 -0
- package/template/zh/ai/template/LANG +1 -0
- package/template/zh/ai/template/VERSION +1 -0
- package/template/zh/ai/template/bootstrap.md +180 -0
- package/template/zh/ai/template/prompt.md +104 -0
- package/template/zh/ai/template/protocol.md +300 -0
- package/template/zh/ai/template/reconcile.md +134 -0
- package/template/zh/ai/template/rules/core.md +174 -0
- package/template/zh/ai/template/rules/output.md +51 -0
- package/template/zh/ai/template/schemas/metrics.schema.json +119 -0
- package/template/zh/ai/template/schemas/result.schema.json +234 -0
- package/test/selftest.js +280 -0
package/test/selftest.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { spawnSync } = require("child_process");
|
|
7
|
+
|
|
8
|
+
const repoRoot = path.resolve(__dirname, "..");
|
|
9
|
+
const cli = path.join(repoRoot, "bin", "agent-execution-template.js");
|
|
10
|
+
|
|
11
|
+
function assert(condition, message) {
|
|
12
|
+
if (!condition) {
|
|
13
|
+
throw new Error(message);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function run(args, cwd, expectedStatus = 0) {
|
|
18
|
+
const result = spawnSync(process.execPath, [cli, ...args], {
|
|
19
|
+
cwd,
|
|
20
|
+
encoding: "utf8"
|
|
21
|
+
});
|
|
22
|
+
const output = `${result.stdout}${result.stderr}`;
|
|
23
|
+
if (result.status !== expectedStatus) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Command failed: ${args.join(" ")}\nExpected: ${expectedStatus}\nActual: ${result.status}\n${output}`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return output;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function exists(cwd, relativePath) {
|
|
32
|
+
return fs.existsSync(path.join(cwd, relativePath));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function read(cwd, relativePath) {
|
|
36
|
+
return fs.readFileSync(path.join(cwd, relativePath), "utf8");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function write(cwd, relativePath, content) {
|
|
40
|
+
fs.writeFileSync(path.join(cwd, relativePath), content);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createTempProject(name) {
|
|
44
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), `${name}-`));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function testInitUpdateDoctor() {
|
|
48
|
+
const cwd = createTempProject("agent-execution-template-selftest");
|
|
49
|
+
|
|
50
|
+
run(["init"], cwd);
|
|
51
|
+
assert(read(cwd, "ai/template/LANG") === "zh\n", "init should default to zh template");
|
|
52
|
+
assert(exists(cwd, "ai/template/VERSION"), "init should create template VERSION");
|
|
53
|
+
assert(exists(cwd, "ai/template/bootstrap.md"), "init should create template bootstrap prompt");
|
|
54
|
+
assert(exists(cwd, "ai/template/prompt.md"), "init should create template prompt");
|
|
55
|
+
assert(exists(cwd, "ai/template/reconcile.md"), "init should create template reconcile prompt");
|
|
56
|
+
assert(exists(cwd, "ai/project/inbox/.gitkeep"), "init should create inbox directory");
|
|
57
|
+
assert(exists(cwd, "ai/project/project.md"), "init should create project.md");
|
|
58
|
+
assert(exists(cwd, "ai/project/task.md"), "init should create task.md");
|
|
59
|
+
assert(exists(cwd, "ai/project/refs/final-shape.md"), "init should create project North Star");
|
|
60
|
+
assert(exists(cwd, "ai/project/refs/module-map.md"), "init should create module map");
|
|
61
|
+
assert(exists(cwd, "ai/project/refs/roadmap.md"), "init should create roadmap");
|
|
62
|
+
assert(exists(cwd, "ai/project/inbox/ideas/.gitkeep"), "init should create ideas inbox");
|
|
63
|
+
assert(exists(cwd, "ai/project/inbox/processed/.gitkeep"), "init should create processed inbox");
|
|
64
|
+
assert(exists(cwd, "ai/project/proposals/final-shape-updates/.gitkeep"), "init should create strategy proposal directory");
|
|
65
|
+
assert(exists(cwd, "ai/project/proposals/final-shape-updates/_template.md"), "init should create strategy proposal template");
|
|
66
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("确认维度"), "init should install bootstrap prompt");
|
|
67
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("不要总结这个文件"), "bootstrap prompt should prevent summary-only behavior");
|
|
68
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("ai/project/refs/final-shape.md"), "bootstrap prompt should initialize the North Star");
|
|
69
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("ai/project/refs/module-map.md"), "bootstrap prompt should initialize module map");
|
|
70
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("ai/project/refs/roadmap.md"), "bootstrap prompt should initialize roadmap");
|
|
71
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("引导后交接"), "bootstrap prompt should include handoff");
|
|
72
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("我建议下一步做"), "bootstrap prompt should recommend next steps");
|
|
73
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("不要让人类主动去文件管理器里寻找问题"), "bootstrap prompt should not offload file inspection to humans");
|
|
74
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("任务草稿摘要"), "bootstrap prompt should support task draft summary when a goal is provided");
|
|
75
|
+
assert(read(cwd, "ai/template/prompt.md").includes("任务草稿交接"), "execution prompt should include task handoff");
|
|
76
|
+
assert(read(cwd, "ai/template/prompt.md").includes("开始初始化这个项目"), "execution prompt should route natural bootstrap entry");
|
|
77
|
+
assert(read(cwd, "ai/template/prompt.md").includes("开始初始化这个项目,并吸收 ai/project/inbox/ 里的资料"), "execution prompt should route bootstrap with inbox material");
|
|
78
|
+
assert(read(cwd, "ai/template/prompt.md").includes("整合 ai/project/inbox/ 里的新资料"), "execution prompt should route natural reconcile entry");
|
|
79
|
+
assert(read(cwd, "ai/template/prompt.md").includes("继续推进这个项目"), "execution prompt should route natural continue entry");
|
|
80
|
+
assert(read(cwd, "ai/template/prompt.md").includes("strategy_update"), "execution prompt should route strategy updates");
|
|
81
|
+
assert(read(cwd, "ai/template/reconcile.md").includes("上下文整合"), "init should install reconcile prompt");
|
|
82
|
+
assert(read(cwd, "ai/template/reconcile.md").includes("整合计划"), "reconcile prompt should require a plan first");
|
|
83
|
+
assert(read(cwd, "ai/template/protocol.md").includes("引导读取范围"), "init should install bootstrap protocol");
|
|
84
|
+
assert(read(cwd, "ai/template/protocol.md").includes("推荐下一步最值得做的任务"), "protocol should require recommended next steps");
|
|
85
|
+
assert(read(cwd, "ai/template/protocol.md").includes("上下文整合模式"), "protocol should include context reconcile mode");
|
|
86
|
+
assert(read(cwd, "ai/template/protocol.md").includes("策略修订模式"), "protocol should include strategy update mode");
|
|
87
|
+
assert(read(cwd, "ai/template/rules/core.md").includes("策略修订门"), "core rules should include strategy update gate");
|
|
88
|
+
assert(read(cwd, "ai/template/rules/core.md").includes("ai/project/refs/final-shape.md"), "core rules should route direction refs");
|
|
89
|
+
assert(read(cwd, "ai/project/proposals/final-shape-updates/_template.md").includes("`accepted`"), "proposal template should describe accepted status");
|
|
90
|
+
const initOutput = run(["init"], cwd);
|
|
91
|
+
assert(initOutput.includes("开始初始化这个项目"), "init output should provide compact natural bootstrap prompt");
|
|
92
|
+
assert(initOutput.includes("[初始化]"), "init output should distinguish pre-bootstrap guidance");
|
|
93
|
+
assert(initOutput.includes("开始初始化这个项目,并吸收 ai/project/inbox/ 里的资料"), "init output should explain bootstrap with existing material");
|
|
94
|
+
assert(initOutput.includes("整合 ai/project/inbox/ 里的新资料"), "init output should provide compact natural reconcile prompt");
|
|
95
|
+
assert(initOutput.includes("优化上下文"), "init output should expose project context refresh in user language");
|
|
96
|
+
assert(initOutput.includes("把 ai/project/inbox/ideas/ 里的新灵感生成方向修订提案"), "init output should provide natural strategy prompt");
|
|
97
|
+
assert(initOutput.includes("agent-execution-template next"), "init output should tell users how to recover the next step");
|
|
98
|
+
assert(initOutput.includes("文件:"), "init output should summarize file changes");
|
|
99
|
+
assert(!initOutput.includes("[已更新]"), "init output should hide detailed file changes by default");
|
|
100
|
+
assert(!initOutput.includes("Read ai/template/bootstrap.md"), "init output should not use weak Read bootstrap command");
|
|
101
|
+
assert(run(["init", "--verbose"], cwd).includes("[已更新] ai/template/VERSION"), "init --verbose should show detailed file changes");
|
|
102
|
+
const reconcileOutput = run(["reconcile"], cwd);
|
|
103
|
+
assert(reconcileOutput.includes("Agent Execution Template 上下文整合"), "reconcile should use installed Chinese language");
|
|
104
|
+
assert(reconcileOutput.includes("整合 ai/project/inbox/ 里的新资料"), "reconcile should print natural Chinese prompt");
|
|
105
|
+
const strategyOutput = run(["strategy"], cwd);
|
|
106
|
+
assert(strategyOutput.includes("Agent Execution Template 方向修订"), "strategy should use installed Chinese language");
|
|
107
|
+
assert(strategyOutput.includes("ai/project/inbox/ideas/"), "strategy should point to ideas inbox");
|
|
108
|
+
assert(strategyOutput.includes("方向修订提案"), "strategy should print natural Chinese strategy prompt");
|
|
109
|
+
|
|
110
|
+
write(cwd, "ai/project/project.md", "USER PROJECT MARKER\n");
|
|
111
|
+
run(["update"], cwd);
|
|
112
|
+
assert(read(cwd, "ai/project/project.md") === "USER PROJECT MARKER\n", "update must not overwrite project.md");
|
|
113
|
+
|
|
114
|
+
const doctorOutput = run(["doctor"], cwd);
|
|
115
|
+
assert(doctorOutput.includes("ai/project/result.json JSON"), "doctor should validate result JSON");
|
|
116
|
+
assert(doctorOutput.includes("ai/project/metrics.json JSON"), "doctor should validate metrics JSON");
|
|
117
|
+
assert(doctorOutput.includes("ai/project/task.md front matter"), "doctor should validate task front matter");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function testEnglishInitUpdateDoctor() {
|
|
121
|
+
const cwd = createTempProject("agent-execution-template-en");
|
|
122
|
+
|
|
123
|
+
const initOutput = run(["init", "--lang", "en"], cwd);
|
|
124
|
+
assert(read(cwd, "ai/template/LANG") === "en\n", "init --lang en should install English template");
|
|
125
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("Confirmation Dimensions"), "English init should install English bootstrap prompt");
|
|
126
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("Do not summarize this file"), "English bootstrap prompt should prevent summary-only behavior");
|
|
127
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("ai/project/refs/final-shape.md"), "English bootstrap prompt should initialize the North Star");
|
|
128
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("ai/project/refs/module-map.md"), "English bootstrap prompt should initialize module map");
|
|
129
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("ai/project/refs/roadmap.md"), "English bootstrap prompt should initialize roadmap");
|
|
130
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("Recommended next step"), "English bootstrap prompt should recommend next steps");
|
|
131
|
+
assert(read(cwd, "ai/template/bootstrap.md").includes("Do not make the human hunt through files"), "English bootstrap prompt should not offload file inspection to humans");
|
|
132
|
+
assert(read(cwd, "ai/template/prompt.md").includes("Start initializing this project"), "English execution prompt should route natural bootstrap entry");
|
|
133
|
+
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");
|
|
134
|
+
assert(read(cwd, "ai/template/prompt.md").includes("Reconcile the new material in ai/project/inbox/"), "English execution prompt should route natural reconcile entry");
|
|
135
|
+
assert(read(cwd, "ai/template/prompt.md").includes("Continue this project"), "English execution prompt should route natural continue entry");
|
|
136
|
+
assert(read(cwd, "ai/template/prompt.md").includes("strategy_update"), "English execution prompt should route strategy updates");
|
|
137
|
+
assert(exists(cwd, "ai/project/refs/final-shape.md"), "English init should create project North Star");
|
|
138
|
+
assert(exists(cwd, "ai/project/refs/module-map.md"), "English init should create module map");
|
|
139
|
+
assert(exists(cwd, "ai/project/refs/roadmap.md"), "English init should create roadmap");
|
|
140
|
+
assert(exists(cwd, "ai/project/proposals/final-shape-updates/_template.md"), "English init should create strategy proposal template");
|
|
141
|
+
assert(read(cwd, "ai/template/rules/core.md").includes("Strategy Update Gate"), "English core rules should include strategy update gate");
|
|
142
|
+
assert(read(cwd, "ai/project/proposals/final-shape-updates/_template.md").includes("`accepted`"), "English proposal template should describe accepted status");
|
|
143
|
+
assert(read(cwd, "ai/template/reconcile.md").includes("Context Reconcile"), "English init should install English reconcile prompt");
|
|
144
|
+
assert(read(cwd, "ai/template/reconcile.md").includes("reconciliation plan"), "English reconcile prompt should require a plan first");
|
|
145
|
+
assert(initOutput.includes("Start initializing this project"), "English init output should provide English bootstrap prompt");
|
|
146
|
+
assert(initOutput.includes("[Initialize]"), "English init output should distinguish pre-bootstrap guidance");
|
|
147
|
+
assert(initOutput.includes("Start initializing this project and absorb the material in ai/project/inbox/"), "English init output should explain bootstrap with existing material");
|
|
148
|
+
assert(initOutput.includes("Reconcile the new material in ai/project/inbox/"), "English init output should provide English reconcile prompt");
|
|
149
|
+
assert(initOutput.includes("Improve context"), "English init output should expose context refresh in user language");
|
|
150
|
+
assert(initOutput.includes("Generate a direction amendment proposal from ai/project/inbox/ideas/"), "English init output should provide natural strategy prompt");
|
|
151
|
+
assert(initOutput.includes("agent-execution-template next"), "English init output should tell users how to recover the next step");
|
|
152
|
+
assert(initOutput.includes("Files:"), "English init output should summarize file changes");
|
|
153
|
+
assert(!initOutput.includes("[UPDATED]"), "English init output should hide detailed file changes by default");
|
|
154
|
+
assert(run(["init", "--lang=en", "--verbose"], cwd).includes("[UPDATED] ai/template/VERSION"), "English init --verbose should show detailed file changes");
|
|
155
|
+
|
|
156
|
+
write(cwd, "ai/project/project.md", "USER PROJECT MARKER\n");
|
|
157
|
+
const updateOutput = run(["update"], cwd);
|
|
158
|
+
assert(updateOutput.includes("Agent Execution Template update"), "update should use installed English language");
|
|
159
|
+
assert(read(cwd, "ai/project/project.md") === "USER PROJECT MARKER\n", "English update must not overwrite project.md");
|
|
160
|
+
|
|
161
|
+
const doctorOutput = run(["doctor"], cwd);
|
|
162
|
+
assert(doctorOutput.includes("Template language: en"), "doctor should show installed English language");
|
|
163
|
+
assert(doctorOutput.includes("ai/project/result.json JSON"), "English doctor should validate result JSON");
|
|
164
|
+
assert(doctorOutput.includes("ai/project/task.md front matter"), "English doctor should validate task front matter");
|
|
165
|
+
assert(doctorOutput.includes("[OK] Ready to run"), "doctor should use installed English language");
|
|
166
|
+
const reconcileOutput = run(["reconcile"], cwd);
|
|
167
|
+
assert(reconcileOutput.includes("Agent Execution Template Context Reconcile"), "reconcile should use installed English language");
|
|
168
|
+
assert(reconcileOutput.includes("Reconcile the new material in ai/project/inbox/"), "reconcile should print natural English prompt");
|
|
169
|
+
const strategyOutput = run(["strategy"], cwd);
|
|
170
|
+
assert(strategyOutput.includes("Agent Execution Template Strategy Update"), "strategy should use installed English language");
|
|
171
|
+
assert(strategyOutput.includes("ai/project/inbox/ideas/"), "strategy should point to ideas inbox");
|
|
172
|
+
assert(strategyOutput.includes("direction amendment proposal"), "strategy should print natural English strategy prompt");
|
|
173
|
+
assert(run(["reconcile", "--lang", "zh"], cwd).includes("整合 ai/project/inbox/ 里的新资料"), "reconcile --lang zh should override installed language");
|
|
174
|
+
assert(run(["strategy", "--lang", "zh"], cwd).includes("方向修订提案"), "strategy --lang zh should override installed language");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function testDoctorFailureAndWarning() {
|
|
178
|
+
const missingCwd = createTempProject("agent-execution-template-missing");
|
|
179
|
+
run(["init"], missingCwd);
|
|
180
|
+
fs.unlinkSync(path.join(missingCwd, "ai/project/runtime.md"));
|
|
181
|
+
run(["doctor"], missingCwd, 1);
|
|
182
|
+
|
|
183
|
+
const warnCwd = createTempProject("agent-execution-template-warn");
|
|
184
|
+
run(["init"], warnCwd);
|
|
185
|
+
write(warnCwd, "ai/project/runtime.md", "");
|
|
186
|
+
run(["doctor"], warnCwd);
|
|
187
|
+
|
|
188
|
+
const invalidJsonCwd = createTempProject("agent-execution-template-invalid-json");
|
|
189
|
+
run(["init"], invalidJsonCwd);
|
|
190
|
+
write(invalidJsonCwd, "ai/project/result.json", "{invalid\n");
|
|
191
|
+
const invalidJsonOutput = run(["doctor"], invalidJsonCwd, 1);
|
|
192
|
+
assert(invalidJsonOutput.includes("JSON 无效"), "doctor should fail invalid result JSON");
|
|
193
|
+
|
|
194
|
+
const taskWarnCwd = createTempProject("agent-execution-template-task-frontmatter");
|
|
195
|
+
run(["init"], taskWarnCwd);
|
|
196
|
+
write(taskWarnCwd, "ai/project/task.md", "# Task only\n");
|
|
197
|
+
const taskWarnOutput = run(["doctor"], taskWarnCwd);
|
|
198
|
+
assert(taskWarnOutput.includes("任务 front matter 缺少关键字段"), "doctor should warn incomplete task front matter");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function testRefreshBacksUpAndImportsOldProject() {
|
|
202
|
+
const cwd = createTempProject("agent-execution-template-refresh");
|
|
203
|
+
run(["init"], cwd);
|
|
204
|
+
write(cwd, "ai/project/project.md", "OLD PROJECT CONTEXT\n");
|
|
205
|
+
|
|
206
|
+
const output = run(["refresh"], cwd);
|
|
207
|
+
const backups = fs.readdirSync(path.join(cwd, "ai"))
|
|
208
|
+
.filter((entry) => entry.startsWith("project.backup."));
|
|
209
|
+
|
|
210
|
+
assert(backups.length === 1, "refresh should back up the old project directory");
|
|
211
|
+
assert(read(cwd, "ai/project/project.md") !== "OLD PROJECT CONTEXT\n", "refresh should create a fresh project directory");
|
|
212
|
+
assert(
|
|
213
|
+
read(cwd, "ai/project/inbox/raw/old-project/project.md") === "OLD PROJECT CONTEXT\n",
|
|
214
|
+
"refresh should import old project context into the new inbox"
|
|
215
|
+
);
|
|
216
|
+
assert(output.includes("Agent Execution Template 项目上下文重整"), "refresh should print a refresh summary");
|
|
217
|
+
assert(output.includes("基于旧上下文重新生成更精良的 ai/project/"), "refresh should print the next agent prompt");
|
|
218
|
+
|
|
219
|
+
const improveOutput = run(["improve-context"], cwd);
|
|
220
|
+
assert(improveOutput.includes("Agent Execution Template 上下文总结优化"), "improve-context should use user-facing context improvement language");
|
|
221
|
+
assert(improveOutput.includes("基于旧上下文重新生成更精良的 ai/project/"), "improve-context should reuse refresh behavior");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function testNextCommandRoutesByProjectState() {
|
|
225
|
+
const missingCwd = createTempProject("agent-execution-template-next-missing");
|
|
226
|
+
const missingOutput = run(["next"], missingCwd);
|
|
227
|
+
assert(missingOutput.includes("agent-execution-template init"), "next should tell uninitialized projects to install first");
|
|
228
|
+
|
|
229
|
+
const cwd = createTempProject("agent-execution-template-next");
|
|
230
|
+
run(["init"], cwd);
|
|
231
|
+
assert(run(["next"], cwd).includes("开始初始化这个项目"), "next should bootstrap a freshly installed project");
|
|
232
|
+
|
|
233
|
+
write(cwd, "ai/project/project.md", "USER PROJECT MARKER\n");
|
|
234
|
+
assert(run(["next"], cwd).includes("继续推进这个项目"), "next should continue when no intake is waiting");
|
|
235
|
+
|
|
236
|
+
write(cwd, "ai/project/inbox/product.md", "# Product material\n");
|
|
237
|
+
assert(run(["next"], cwd).includes("整合 ai/project/inbox/ 里的新资料"), "next should route material inbox to reconcile");
|
|
238
|
+
fs.unlinkSync(path.join(cwd, "ai/project/inbox/product.md"));
|
|
239
|
+
|
|
240
|
+
write(cwd, "ai/project/inbox/processed/product.md", "# Processed material\n");
|
|
241
|
+
assert(run(["next"], cwd).includes("继续推进这个项目"), "next should ignore processed inbox material");
|
|
242
|
+
fs.unlinkSync(path.join(cwd, "ai/project/inbox/processed/product.md"));
|
|
243
|
+
|
|
244
|
+
write(cwd, "ai/project/inbox/ideas/new-direction.md", "# Direction idea\n");
|
|
245
|
+
assert(run(["next"], cwd).includes("方向修订提案"), "next should route ideas inbox to strategy update");
|
|
246
|
+
fs.unlinkSync(path.join(cwd, "ai/project/inbox/ideas/new-direction.md"));
|
|
247
|
+
|
|
248
|
+
write(cwd, "ai/project/proposals/final-shape-updates/proposal.md", "---\nstatus: \"applied\"\n---\n");
|
|
249
|
+
assert(run(["next"], cwd).includes("继续推进这个项目"), "next should ignore already applied proposals");
|
|
250
|
+
|
|
251
|
+
write(cwd, "ai/project/proposals/final-shape-updates/proposal.md", "---\nstatus: \"proposed\"\n---\n");
|
|
252
|
+
assert(run(["next"], cwd).includes("已有方向修订提案"), "next should route existing proposals to human review");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function testPermissionErrorIsActionable() {
|
|
256
|
+
const cwd = createTempProject("agent-execution-template-permission");
|
|
257
|
+
const inbox = path.join(cwd, "ai/project/inbox");
|
|
258
|
+
fs.mkdirSync(inbox, { recursive: true });
|
|
259
|
+
fs.chmodSync(inbox, 0o555);
|
|
260
|
+
try {
|
|
261
|
+
const output = run(["init"], cwd, 1);
|
|
262
|
+
assert(output.includes("无法写入目标路径"), "permission failures should explain the blocked target");
|
|
263
|
+
assert(output.includes("sudo chown -R"), "permission failures should include an ownership repair command");
|
|
264
|
+
assert(!output.includes("node:fs"), "permission failures should not expose the raw Node stack");
|
|
265
|
+
} finally {
|
|
266
|
+
fs.chmodSync(inbox, 0o755);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function main() {
|
|
271
|
+
testInitUpdateDoctor();
|
|
272
|
+
testEnglishInitUpdateDoctor();
|
|
273
|
+
testDoctorFailureAndWarning();
|
|
274
|
+
testRefreshBacksUpAndImportsOldProject();
|
|
275
|
+
testNextCommandRoutesByProjectState();
|
|
276
|
+
testPermissionErrorIsActionable();
|
|
277
|
+
console.log("selftest ok");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
main();
|