@peterxiaoyang/superspec 0.1.14 → 0.1.16-alpha
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 +37 -36
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +301 -0
- package/dist/format.d.ts +29 -0
- package/dist/format.js +141 -0
- package/dist/install.d.ts +18 -0
- package/dist/install.js +161 -0
- package/dist/next.d.ts +3 -0
- package/dist/next.js +161 -0
- package/dist/openspec.d.ts +9 -0
- package/dist/openspec.js +41 -0
- package/dist/record.d.ts +20 -0
- package/dist/record.js +266 -0
- package/dist/skill_loop.d.ts +23 -0
- package/dist/skill_loop.js +58 -0
- package/dist/store.d.ts +31 -0
- package/dist/store.js +213 -0
- package/dist/sync.d.ts +3 -0
- package/dist/sync.js +135 -0
- package/dist/task.d.ts +7 -0
- package/dist/task.js +44 -0
- package/dist/transition.d.ts +34 -0
- package/dist/transition.js +400 -0
- package/dist/types.d.ts +141 -0
- package/dist/types.js +3 -0
- package/package.json +8 -53
- package/templates/workflow/agents/architect.toml +13 -0
- package/{adapters/codex → templates/workflow}/agents/code-reviewer.toml +2 -2
- package/templates/workflow/agents/critic.toml +13 -0
- package/{adapters/codex → templates/workflow}/agents/executor.toml +2 -2
- package/{adapters/codex → templates/workflow}/agents/explore.toml +3 -3
- package/templates/workflow/agents/final-audit.toml +13 -0
- package/templates/workflow/agents/proposal-auditor.toml +13 -0
- package/{adapters/codex → templates/workflow}/agents/test-engineer.toml +4 -4
- package/{adapters/codex → templates/workflow}/agents/test-runner.toml +2 -2
- package/{adapters/codex → templates/workflow}/agents/verifier.toml +2 -2
- package/templates/workflow/prompts/architect.md +16 -0
- package/templates/workflow/prompts/critic.md +16 -0
- package/templates/workflow/prompts/explore.md +1 -1
- package/templates/workflow/prompts/final-audit.md +32 -0
- package/templates/workflow/prompts/proposal-auditor.md +34 -0
- package/templates/workflow/prompts/test-engineer.md +16 -0
- package/templates/workflow/skills/superspec-apply/SKILL.md +39 -121
- package/templates/workflow/skills/superspec-archive/SKILL.md +17 -49
- package/templates/workflow/skills/superspec-explore/SKILL.md +34 -70
- package/templates/workflow/skills/superspec-propose/SKILL.md +57 -60
- package/templates/workflow/skills/superspec-review/SKILL.md +18 -85
- package/adapters/codex/agents/architect.toml +0 -13
- package/adapters/codex/agents/critic.toml +0 -13
- package/adapters/codex/install-map.json +0 -116
- package/bin/launch.js +0 -37
- package/bin/superspec-check.js +0 -4
- package/bin/superspec-hook.js +0 -4
- package/bin/superspec-init.js +0 -4
- package/bin/superspec.js +0 -4
- package/dist/src/apply_worker_chain.d.ts +0 -60
- package/dist/src/apply_worker_chain.js +0 -1283
- package/dist/src/apply_worker_chain_lifecycle.d.ts +0 -21
- package/dist/src/apply_worker_chain_lifecycle.js +0 -409
- package/dist/src/archive.d.ts +0 -23
- package/dist/src/archive.js +0 -430
- package/dist/src/cli.d.ts +0 -1
- package/dist/src/cli.js +0 -33
- package/dist/src/cli_args.d.ts +0 -32
- package/dist/src/cli_args.js +0 -454
- package/dist/src/core.d.ts +0 -20
- package/dist/src/core.js +0 -394
- package/dist/src/disclosure.d.ts +0 -35
- package/dist/src/disclosure.js +0 -671
- package/dist/src/doctor.d.ts +0 -44
- package/dist/src/doctor.js +0 -230
- package/dist/src/evidence.d.ts +0 -29
- package/dist/src/evidence.js +0 -1107
- package/dist/src/gates.d.ts +0 -19
- package/dist/src/gates.js +0 -1631
- package/dist/src/git.d.ts +0 -8
- package/dist/src/git.js +0 -112
- package/dist/src/hooks/adapter.d.ts +0 -5
- package/dist/src/hooks/adapter.js +0 -347
- package/dist/src/hooks/guard_api.d.ts +0 -12
- package/dist/src/hooks/guard_api.js +0 -797
- package/dist/src/hooks/health.d.ts +0 -4
- package/dist/src/hooks/health.js +0 -86
- package/dist/src/hooks/policy_event.d.ts +0 -41
- package/dist/src/hooks/policy_event.js +0 -2634
- package/dist/src/hooks/types.d.ts +0 -72
- package/dist/src/hooks/types.js +0 -1
- package/dist/src/hooks/validation.d.ts +0 -3
- package/dist/src/hooks/validation.js +0 -70
- package/dist/src/i18n.d.ts +0 -21
- package/dist/src/i18n.js +0 -720
- package/dist/src/init_cli.d.ts +0 -28
- package/dist/src/init_cli.js +0 -404
- package/dist/src/install_engine.d.ts +0 -71
- package/dist/src/install_engine.js +0 -538
- package/dist/src/invariants.d.ts +0 -16
- package/dist/src/invariants.js +0 -363
- package/dist/src/openspec.d.ts +0 -37
- package/dist/src/openspec.js +0 -274
- package/dist/src/packet_render.d.ts +0 -5
- package/dist/src/packet_render.js +0 -1767
- package/dist/src/packet_schema.d.ts +0 -59
- package/dist/src/packet_schema.js +0 -1
- package/dist/src/paths.d.ts +0 -22
- package/dist/src/paths.js +0 -203
- package/dist/src/project_init.d.ts +0 -33
- package/dist/src/project_init.js +0 -191
- package/dist/src/self_update.d.ts +0 -14
- package/dist/src/self_update.js +0 -56
- package/dist/src/state.d.ts +0 -37
- package/dist/src/state.js +0 -464
- package/dist/src/tasks.d.ts +0 -38
- package/dist/src/tasks.js +0 -373
- package/dist/src/util.d.ts +0 -163
- package/dist/src/util.js +0 -792
- package/dist/superspec.d.ts +0 -6
- package/dist/superspec.js +0 -97
- package/dist/superspec_guard.d.ts +0 -4
- package/dist/superspec_guard.js +0 -19
- package/dist/superspec_hook.d.ts +0 -4
- package/dist/superspec_hook.js +0 -35
- package/dist/superspec_init.d.ts +0 -2
- package/dist/superspec_init.js +0 -17
- package/schemas/hook-event.schema.json +0 -27
- package/schemas/install-manifest.schema.json +0 -97
- package/templates/hooks/codex-hooks.json +0 -35
- package/templates/sidecar/archive-preservation.json +0 -11
- package/templates/sidecar/business-invariants.md +0 -38
- package/templates/sidecar/config.yaml +0 -13
- package/templates/sidecar/discovery.md +0 -33
- package/templates/sidecar/test-contract.md +0 -27
package/README.md
CHANGED
|
@@ -77,17 +77,16 @@ npm install -g @peterxiaoyang/superspec@latest
|
|
|
77
77
|
进入你的项目根目录,然后运行:
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
|
-
superspec
|
|
80
|
+
superspec install
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
这条命令的意思是:把 SuperSpec 当前可用的工作流入口安装到项目里。
|
|
84
|
-
|
|
85
|
-
初始化还会安装托管的 `.codex/hooks.json`。默认 manifest 只在子智能体启动和停止时调用 SuperSpec,记录 best-effort 审计信息;它不会拦截普通文件写入,也不会在测试命令后自动记录结果。
|
|
84
|
+
当前 beta 会安装 `.superspec/` 引擎目录、`.codex/skills/superspec-*` 阶段入口、`.codex/prompts/*.md` 角色 prompt、`.codex/agents/*.toml` 子智能体配置,并补齐 `.codex/config.toml` 的多 agent 开关。`superspec init --scope project` 仍作为兼容别名可用。
|
|
86
85
|
|
|
87
86
|
Windows PowerShell 如果拦截 npm 的 `.ps1` 脚本,请改用:
|
|
88
87
|
|
|
89
88
|
```powershell
|
|
90
|
-
superspec.cmd
|
|
89
|
+
superspec.cmd install
|
|
91
90
|
```
|
|
92
91
|
|
|
93
92
|
### 3. 按步骤使用
|
|
@@ -136,6 +135,8 @@ superspec.cmd init --scope project
|
|
|
136
135
|
|
|
137
136
|
你日常主要记住这五个入口就够了。
|
|
138
137
|
|
|
138
|
+
CLI 不带 `--risk` 时默认是 `normal`;但内置的 `superspec-explore` 和 `superspec-propose` 技能默认用 `--risk strict` 驱动。探索阶段会创建 `critic` 工作项审查需求澄清记录;计划阶段会创建 `proposal-auditor`、`critic`、`architect` 和 `test-engineer` 工作项后再进入实现准备。
|
|
139
|
+
|
|
139
140
|
## 它会多保存哪些记录
|
|
140
141
|
|
|
141
142
|
SuperSpec 会在每次变更下面保存一些辅助记录,方便后续追溯。
|
|
@@ -200,34 +201,24 @@ superspec --version
|
|
|
200
201
|
安装到当前项目:
|
|
201
202
|
|
|
202
203
|
```bash
|
|
203
|
-
superspec
|
|
204
|
+
superspec install
|
|
204
205
|
```
|
|
205
206
|
|
|
206
|
-
|
|
207
|
+
`superspec init --scope project` 是兼容别名,也会执行同一套安装逻辑。
|
|
207
208
|
|
|
208
|
-
|
|
209
|
-
superspec update --scope project
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
这条命令会先通过 npm 更新全局 `@peterxiaoyang/superspec`,再用新版本更新当前项目里的入口文件。只想使用当前已安装包更新项目文件时,可以运行:
|
|
209
|
+
检查当前项目的 OpenSpec 探测结果:
|
|
213
210
|
|
|
214
211
|
```bash
|
|
215
|
-
superspec
|
|
212
|
+
superspec status
|
|
216
213
|
```
|
|
217
214
|
|
|
218
|
-
|
|
215
|
+
更新当前 beta CLI:
|
|
219
216
|
|
|
220
217
|
```bash
|
|
221
|
-
superspec
|
|
218
|
+
superspec update
|
|
222
219
|
```
|
|
223
220
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
诊断全局安装、PATH、OpenSpec 依赖和 npm bin 指向问题:
|
|
227
|
-
|
|
228
|
-
```bash
|
|
229
|
-
superspec doctor
|
|
230
|
-
```
|
|
221
|
+
这条命令只检查当前 `.superspec/changes` 运行时是否已经是新引擎布局;真正升级全局 npm 包仍需使用 `npm install -g @peterxiaoyang/superspec@<version>`。
|
|
231
222
|
|
|
232
223
|
## 进阶信息
|
|
233
224
|
|
|
@@ -235,24 +226,35 @@ superspec doctor
|
|
|
235
226
|
|
|
236
227
|
```text
|
|
237
228
|
.codex/
|
|
238
|
-
hooks.json
|
|
239
229
|
skills/superspec-explore/
|
|
240
230
|
skills/superspec-propose/
|
|
241
231
|
skills/superspec-apply/
|
|
242
232
|
skills/superspec-review/
|
|
243
233
|
skills/superspec-archive/
|
|
234
|
+
prompts/architect.md
|
|
235
|
+
prompts/code-reviewer.md
|
|
236
|
+
prompts/critic.md
|
|
237
|
+
prompts/executor.md
|
|
238
|
+
prompts/explore.md
|
|
239
|
+
prompts/final-audit.md
|
|
240
|
+
prompts/proposal-auditor.md
|
|
241
|
+
prompts/test-engineer.md
|
|
242
|
+
prompts/test-runner.md
|
|
243
|
+
prompts/verifier.md
|
|
244
|
+
agents/architect.toml
|
|
245
|
+
agents/code-reviewer.toml
|
|
246
|
+
agents/critic.toml
|
|
247
|
+
agents/executor.toml
|
|
248
|
+
agents/explore.toml
|
|
249
|
+
agents/final-audit.toml
|
|
250
|
+
agents/proposal-auditor.toml
|
|
251
|
+
agents/test-engineer.toml
|
|
252
|
+
agents/test-runner.toml
|
|
253
|
+
agents/verifier.toml
|
|
254
|
+
config.toml
|
|
244
255
|
```
|
|
245
256
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
```bash
|
|
249
|
-
superspec check check-init --change <变更ID>
|
|
250
|
-
superspec check check-apply-ready --change <变更ID>
|
|
251
|
-
superspec check check-review-ready --change <变更ID>
|
|
252
|
-
superspec check check-archive-ready --change <变更ID>
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
普通使用者通常不需要手动运行这些命令;对应的阶段入口会在需要时使用它们。
|
|
257
|
+
当前 beta 的阶段入口由 `superspec transition next --change <变更ID>` 驱动,不再提供旧版 `superspec check ...` surface。
|
|
256
258
|
|
|
257
259
|
如果你要开发 SuperSpec 本身:
|
|
258
260
|
|
|
@@ -260,14 +262,13 @@ superspec check check-archive-ready --change <变更ID>
|
|
|
260
262
|
npm run build
|
|
261
263
|
npm run typecheck
|
|
262
264
|
npm test
|
|
263
|
-
npm
|
|
265
|
+
npm pack --dry-run
|
|
264
266
|
```
|
|
265
267
|
|
|
266
268
|
更多细节见:
|
|
267
269
|
|
|
268
|
-
- `docs/
|
|
269
|
-
- `
|
|
270
|
-
- `.codex/skills/superspec-*/SKILL.md`:当前 Codex 适配器使用的阶段入口说明
|
|
270
|
+
- `docs/plans/SUPERSPEC_TRANSITION_ENGINE_SPEC_LITE.md`:transition engine 设计
|
|
271
|
+
- `templates/workflow/skills/superspec-*/SKILL.md`:当前 Codex 适配器使用的阶段入口说明
|
|
271
272
|
|
|
272
273
|
## 致谢与灵感来源
|
|
273
274
|
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SuperSpec 流程引擎 — CLI 入口
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { installProject } from "./install.js";
|
|
5
|
+
import { writeSnapshot } from "./store.js";
|
|
6
|
+
import { rebuildSnapshot } from "./sync.js";
|
|
7
|
+
import { next as nextCmd } from "./next.js";
|
|
8
|
+
import { proposeReady, commitTransition, transitionInit, transitionExplore, startApply, taskStart, taskComplete, reviewReady, accept, archive } from "./transition.js";
|
|
9
|
+
import { recordJobSubmit, recordUserDecision, jobsList, jobsPacket } from "./record.js";
|
|
10
|
+
import { recordTestRun } from "./task.js";
|
|
11
|
+
import { probeOpenSpec, openspecStatus, changeRoot } from "./openspec.js";
|
|
12
|
+
// ===== 参数解析 =====
|
|
13
|
+
function parseArgs(argv) {
|
|
14
|
+
const [command, subcommand, ...rest] = argv;
|
|
15
|
+
const opts = {};
|
|
16
|
+
for (let i = 0; i < rest.length; i++) {
|
|
17
|
+
if (rest[i].startsWith("--")) {
|
|
18
|
+
const key = rest[i].slice(2);
|
|
19
|
+
const val = rest[i + 1] && !rest[i + 1].startsWith("--") ? rest[++i] : "true";
|
|
20
|
+
opts[key] = val;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// 如果 subcommand 以 -- 开头,说明没有 subcommand
|
|
24
|
+
if (subcommand && subcommand.startsWith("--")) {
|
|
25
|
+
Object.entries(parseFlags([subcommand, ...rest])).forEach(([k, v]) => { opts[k] = v; });
|
|
26
|
+
return { command, opts };
|
|
27
|
+
}
|
|
28
|
+
return { command, subcommand, opts };
|
|
29
|
+
}
|
|
30
|
+
function parseFlags(args) {
|
|
31
|
+
const opts = {};
|
|
32
|
+
for (let i = 0; i < args.length; i++) {
|
|
33
|
+
if (args[i].startsWith("--")) {
|
|
34
|
+
const key = args[i].slice(2);
|
|
35
|
+
const val = args[i + 1] && !args[i + 1].startsWith("--") ? args[++i] : "true";
|
|
36
|
+
opts[key] = val;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return opts;
|
|
40
|
+
}
|
|
41
|
+
// ===== 初始化 transition init =====
|
|
42
|
+
// ===== init/explore 现在在 transition.ts 中(走统一锁内路径)=====
|
|
43
|
+
// ===== 主分发 =====
|
|
44
|
+
async function main(argv) {
|
|
45
|
+
// --help / 无参数 → 打印用法
|
|
46
|
+
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
|
|
47
|
+
console.log(`SuperSpec 流程引擎 0.1.16-alpha
|
|
48
|
+
|
|
49
|
+
用法:superspec <命令> [选项]
|
|
50
|
+
|
|
51
|
+
命令:
|
|
52
|
+
status --change <C> 查看状态
|
|
53
|
+
transition <子命令> --change <C> 状态流转(见下)
|
|
54
|
+
record <子命令> --change <C> 登记证据(见下)
|
|
55
|
+
jobs <子命令> --change <C> 工作项管理(见下)
|
|
56
|
+
install 安装项目工作流入口
|
|
57
|
+
init --scope project install 的兼容别名
|
|
58
|
+
update 更新 SuperSpec
|
|
59
|
+
version 版本号
|
|
60
|
+
|
|
61
|
+
transition 子命令:
|
|
62
|
+
init / explore / sync / next / propose-ready / start-apply
|
|
63
|
+
task-start --task <T> / task-complete --task <T>
|
|
64
|
+
review-ready / accept / archive
|
|
65
|
+
|
|
66
|
+
record 子命令:
|
|
67
|
+
job-submit --job <J> --report <F>
|
|
68
|
+
user-decision --input <F>
|
|
69
|
+
test-run --input <F>
|
|
70
|
+
|
|
71
|
+
jobs 子命令:
|
|
72
|
+
list / packet --job <J>
|
|
73
|
+
`);
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
// version
|
|
77
|
+
if (argv[0] === "version" || argv[0] === "--version" || argv[0] === "-v") {
|
|
78
|
+
console.log("SuperSpec 0.1.16-alpha");
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
const { command, subcommand, opts } = parseArgs(argv);
|
|
82
|
+
const projectRoot = process.cwd();
|
|
83
|
+
// install / init / update 不需要 --change
|
|
84
|
+
if (command === "install" || command === "init") {
|
|
85
|
+
if (command === "init" && opts.scope && opts.scope !== "project") {
|
|
86
|
+
console.log(JSON.stringify({
|
|
87
|
+
ok: false,
|
|
88
|
+
message: "当前 beta 只支持 init --scope project",
|
|
89
|
+
}));
|
|
90
|
+
return 1;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
console.log(JSON.stringify(installProject(projectRoot)));
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
console.log(JSON.stringify({
|
|
98
|
+
ok: false,
|
|
99
|
+
message: err.message,
|
|
100
|
+
}));
|
|
101
|
+
return 1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (command === "update") {
|
|
105
|
+
// 检测是否从老版 update 过来
|
|
106
|
+
const { existsSync } = await import("node:fs");
|
|
107
|
+
const isLegacyUpdate = !existsSync(join(projectRoot, ".superspec", "changes"));
|
|
108
|
+
if (isLegacyUpdate) {
|
|
109
|
+
console.log(JSON.stringify({
|
|
110
|
+
ok: false,
|
|
111
|
+
message: "SuperSpec 0.1.16-alpha 是全新引擎,不能从 0.x 直接 update。\n" +
|
|
112
|
+
"请用 npm install -g @peterxiaoyang/superspec@0.1.16-alpha 手动安装。\n" +
|
|
113
|
+
"现有 change 请先用 0.1.x 完成归档。",
|
|
114
|
+
}));
|
|
115
|
+
return 1;
|
|
116
|
+
}
|
|
117
|
+
console.log(JSON.stringify({ ok: true, message: "已是最新版本 0.1.16-alpha" }));
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
const change = opts.change;
|
|
121
|
+
if (!change && command !== "status") {
|
|
122
|
+
console.error("错误:缺少 --change(用 --help 查看用法)");
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
switch (command) {
|
|
127
|
+
case "status": {
|
|
128
|
+
if (!change) {
|
|
129
|
+
// 全局 status
|
|
130
|
+
const probe = probeOpenSpec(projectRoot);
|
|
131
|
+
console.log(JSON.stringify({ openspec: probe }, null, 2));
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
const cr = changeRoot(projectRoot, change);
|
|
135
|
+
const snapshot = rebuildSnapshot(projectRoot, change, cr);
|
|
136
|
+
const jobs = jobsList(projectRoot, change);
|
|
137
|
+
console.log(JSON.stringify({
|
|
138
|
+
change,
|
|
139
|
+
state: snapshot.state,
|
|
140
|
+
open_jobs: jobs.open.length,
|
|
141
|
+
accepted_jobs: jobs.accepted.length,
|
|
142
|
+
rejected_jobs: jobs.rejected.length,
|
|
143
|
+
document_digests: snapshot.document_digests,
|
|
144
|
+
last_transition: snapshot.last_transition,
|
|
145
|
+
}, null, 2));
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
case "transition": {
|
|
149
|
+
const cr = changeRoot(projectRoot, change);
|
|
150
|
+
switch (subcommand) {
|
|
151
|
+
case "init":
|
|
152
|
+
console.log(JSON.stringify(transitionInit(projectRoot, change, cr), null, 2));
|
|
153
|
+
return 0;
|
|
154
|
+
case "explore":
|
|
155
|
+
{
|
|
156
|
+
const risk = opts.risk ?? "normal";
|
|
157
|
+
console.log(JSON.stringify(transitionExplore(projectRoot, change, cr, risk), null, 2));
|
|
158
|
+
}
|
|
159
|
+
return 0;
|
|
160
|
+
case "sync": {
|
|
161
|
+
const snapshot = rebuildSnapshot(projectRoot, change, cr, openspecStatus(projectRoot, change));
|
|
162
|
+
writeSnapshot(projectRoot, change, snapshot);
|
|
163
|
+
console.log(JSON.stringify({
|
|
164
|
+
ok: true,
|
|
165
|
+
state: snapshot.state,
|
|
166
|
+
message: "snapshot 已重建",
|
|
167
|
+
stale_accepted_removed: 0, // sync 不报告具体移除(轻量)
|
|
168
|
+
}, null, 2));
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
case "next": {
|
|
172
|
+
const risk = opts.risk ?? "normal";
|
|
173
|
+
const result = nextCmd(projectRoot, change, cr, risk);
|
|
174
|
+
console.log(JSON.stringify(result, null, 2));
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
case "propose-ready": {
|
|
178
|
+
const risk = opts.risk ?? "normal";
|
|
179
|
+
const result = proposeReady(projectRoot, change, cr, risk);
|
|
180
|
+
console.log(JSON.stringify(result, null, 2));
|
|
181
|
+
return result.events_written === 0 && result.message.includes("不能") ? 1 : 0;
|
|
182
|
+
}
|
|
183
|
+
case "start-apply": {
|
|
184
|
+
const result = startApply(projectRoot, change, cr);
|
|
185
|
+
console.log(JSON.stringify(result, null, 2));
|
|
186
|
+
return result.events_written === 0 ? 1 : 0;
|
|
187
|
+
}
|
|
188
|
+
case "task-start": {
|
|
189
|
+
const taskId = opts.task;
|
|
190
|
+
if (!taskId) {
|
|
191
|
+
console.error("task-start 需要 --task");
|
|
192
|
+
return 1;
|
|
193
|
+
}
|
|
194
|
+
const result = taskStart(projectRoot, change, cr, taskId);
|
|
195
|
+
console.log(JSON.stringify(result, null, 2));
|
|
196
|
+
return result.events_written === 0 ? 1 : 0;
|
|
197
|
+
}
|
|
198
|
+
case "task-complete": {
|
|
199
|
+
const taskId = opts.task;
|
|
200
|
+
if (!taskId) {
|
|
201
|
+
console.error("task-complete 需要 --task");
|
|
202
|
+
return 1;
|
|
203
|
+
}
|
|
204
|
+
const result = taskComplete(projectRoot, change, cr, taskId);
|
|
205
|
+
console.log(JSON.stringify(result, null, 2));
|
|
206
|
+
return result.events_written === 0 ? 1 : 0;
|
|
207
|
+
}
|
|
208
|
+
case "review-ready": {
|
|
209
|
+
const risk = opts.risk ?? "normal";
|
|
210
|
+
const result = reviewReady(projectRoot, change, cr, risk);
|
|
211
|
+
console.log(JSON.stringify(result, null, 2));
|
|
212
|
+
return result.events_written === 0 ? 1 : 0;
|
|
213
|
+
}
|
|
214
|
+
case "accept": {
|
|
215
|
+
const result = accept(projectRoot, change, cr);
|
|
216
|
+
console.log(JSON.stringify(result, null, 2));
|
|
217
|
+
return result.events_written === 0 ? 1 : 0;
|
|
218
|
+
}
|
|
219
|
+
case "archive": {
|
|
220
|
+
const result = archive(projectRoot, change, cr);
|
|
221
|
+
console.log(JSON.stringify(result, null, 2));
|
|
222
|
+
return result.events_written === 0 ? 1 : 0;
|
|
223
|
+
}
|
|
224
|
+
default:
|
|
225
|
+
console.error(`未知的 transition 子命令:${subcommand}`);
|
|
226
|
+
return 1;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
case "record": {
|
|
230
|
+
const cr = changeRoot(projectRoot, change);
|
|
231
|
+
switch (subcommand) {
|
|
232
|
+
case "job-submit": {
|
|
233
|
+
const jobId = opts.job;
|
|
234
|
+
const report = opts.report;
|
|
235
|
+
if (!jobId || !report) {
|
|
236
|
+
console.error("record job-submit 需要 --job 和 --report");
|
|
237
|
+
return 1;
|
|
238
|
+
}
|
|
239
|
+
const result = recordJobSubmit(projectRoot, change, cr, jobId, report);
|
|
240
|
+
console.log(JSON.stringify(result, null, 2));
|
|
241
|
+
return result.accepted ? 0 : 1;
|
|
242
|
+
}
|
|
243
|
+
case "user-decision": {
|
|
244
|
+
const inputFile = opts.input;
|
|
245
|
+
if (!inputFile) {
|
|
246
|
+
console.error("record user-decision 需要 --input");
|
|
247
|
+
return 1;
|
|
248
|
+
}
|
|
249
|
+
const result = recordUserDecision(projectRoot, change, inputFile);
|
|
250
|
+
console.log(JSON.stringify(result, null, 2));
|
|
251
|
+
return result.accepted ? 0 : 1;
|
|
252
|
+
}
|
|
253
|
+
case "test-run": {
|
|
254
|
+
const inputFile = opts.input;
|
|
255
|
+
if (!inputFile) {
|
|
256
|
+
console.error("record test-run 需要 --input");
|
|
257
|
+
return 1;
|
|
258
|
+
}
|
|
259
|
+
const result = recordTestRun(projectRoot, change, inputFile);
|
|
260
|
+
console.log(JSON.stringify(result, null, 2));
|
|
261
|
+
return result.accepted ? 0 : 1;
|
|
262
|
+
}
|
|
263
|
+
default:
|
|
264
|
+
console.error(`未知的 record 子命令:${subcommand}`);
|
|
265
|
+
return 1;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
case "jobs": {
|
|
269
|
+
switch (subcommand) {
|
|
270
|
+
case "list": {
|
|
271
|
+
const result = jobsList(projectRoot, change);
|
|
272
|
+
console.log(JSON.stringify(result, null, 2));
|
|
273
|
+
return 0;
|
|
274
|
+
}
|
|
275
|
+
case "packet": {
|
|
276
|
+
const jobId = opts.job;
|
|
277
|
+
if (!jobId) {
|
|
278
|
+
console.error("jobs packet 需要 --job");
|
|
279
|
+
return 1;
|
|
280
|
+
}
|
|
281
|
+
const result = jobsPacket(projectRoot, change, jobId);
|
|
282
|
+
console.log(JSON.stringify(result, null, 2));
|
|
283
|
+
return result.found ? 0 : 1;
|
|
284
|
+
}
|
|
285
|
+
default:
|
|
286
|
+
console.error(`未知的 jobs 子命令:${subcommand}`);
|
|
287
|
+
return 1;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
default:
|
|
291
|
+
console.error(`未知命令:${command}。可用:status, transition, record, jobs, install, init, update, version`);
|
|
292
|
+
return 1;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
console.error(`错误:${err.message}`);
|
|
297
|
+
return 2;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// 入口
|
|
301
|
+
main(process.argv.slice(2)).then(exitCode => process.exit(exitCode));
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** 从 discovery.md 提取"待确认问题"段内的未确认项数量 */
|
|
2
|
+
export declare function countDiscoveryOpenQuestions(content: string): number;
|
|
3
|
+
/** 完整校验 discovery.md:存在 + 非空 + 无未确认问题 */
|
|
4
|
+
export declare function validateDiscovery(changeRoot: string): {
|
|
5
|
+
ok: boolean;
|
|
6
|
+
message: string;
|
|
7
|
+
openCount: number;
|
|
8
|
+
};
|
|
9
|
+
export interface ParsedTask {
|
|
10
|
+
taskId: string;
|
|
11
|
+
lineIdx: number;
|
|
12
|
+
done: boolean;
|
|
13
|
+
tddRequired: boolean;
|
|
14
|
+
noTddReason: string | null;
|
|
15
|
+
}
|
|
16
|
+
/** 解析 tasks.md 的全部任务行 */
|
|
17
|
+
export declare function parseTasksMd(content: string): ParsedTask[];
|
|
18
|
+
/** 在 tasks.md 中按 taskId 精确查找任务(词边界,不误判子串) */
|
|
19
|
+
export declare function findTaskInLines(lines: string[], taskId: string): number;
|
|
20
|
+
/** tasks.md 结构指纹(复选框归一化) */
|
|
21
|
+
export declare function tasksStructureDigest(content: string, sha256Text: (s: string) => string): string;
|
|
22
|
+
export declare function validateTestRunInput(tr: Record<string, unknown>): {
|
|
23
|
+
ok: boolean;
|
|
24
|
+
message: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function validateUserDecision(d: Record<string, unknown>): {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
message: string;
|
|
29
|
+
};
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// SuperSpec 流程引擎 — format.ts:文档格式解析的唯一权威源
|
|
2
|
+
//
|
|
3
|
+
// 所有文档的格式定义和解析逻辑都在这里。skills 文案引用这里的格式。
|
|
4
|
+
// 修改格式 = 修改这里 + 对应 skill。禁止在其它地方重复解析。
|
|
5
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
// ===== discovery.md =====
|
|
8
|
+
//
|
|
9
|
+
// 格式(explore skill 定义):
|
|
10
|
+
// ## 待确认问题
|
|
11
|
+
// - [ ] 问题1的描述
|
|
12
|
+
// - [ ] 问题2的描述
|
|
13
|
+
//
|
|
14
|
+
// 引擎只解析"待确认问题"段内的 `- [ ]`,不误判正常 checklist。
|
|
15
|
+
/** 从 discovery.md 提取"待确认问题"段内的未确认项数量 */
|
|
16
|
+
export function countDiscoveryOpenQuestions(content) {
|
|
17
|
+
// 找"待确认问题"标题(中英文兼容)
|
|
18
|
+
const sectionMatch = content.match(/^#{1,6}\s*(待确认问题|Open Questions|Pending Questions)\s*$/im);
|
|
19
|
+
if (!sectionMatch)
|
|
20
|
+
return 0;
|
|
21
|
+
const sectionStart = sectionMatch.index + sectionMatch[0].length;
|
|
22
|
+
// 截取到下一个标题或文件末尾
|
|
23
|
+
const restContent = content.slice(sectionStart);
|
|
24
|
+
const nextHeadingMatch = restContent.match(/^#{1,6}\s+/m);
|
|
25
|
+
const sectionBody = nextHeadingMatch ? restContent.slice(0, nextHeadingMatch.index) : restContent;
|
|
26
|
+
// 数未确认项
|
|
27
|
+
const matches = sectionBody.match(/- \[ \]/g);
|
|
28
|
+
return matches ? matches.length : 0;
|
|
29
|
+
}
|
|
30
|
+
/** 完整校验 discovery.md:存在 + 非空 + 无未确认问题 */
|
|
31
|
+
export function validateDiscovery(changeRoot) {
|
|
32
|
+
const path = join(changeRoot, ".superspec", "artifacts", "discovery.md");
|
|
33
|
+
if (!existsSync(path))
|
|
34
|
+
return { ok: false, message: "discovery.md 不存在", openCount: -1 };
|
|
35
|
+
const content = readFileSync(path, "utf8");
|
|
36
|
+
if (!content.trim())
|
|
37
|
+
return { ok: false, message: "discovery.md 为空", openCount: -1 };
|
|
38
|
+
const openCount = countDiscoveryOpenQuestions(content);
|
|
39
|
+
if (openCount > 0)
|
|
40
|
+
return { ok: false, message: `discovery.md 有 ${openCount} 个未确认问题`, openCount };
|
|
41
|
+
return { ok: true, message: "discovery.md 就绪", openCount: 0 };
|
|
42
|
+
}
|
|
43
|
+
const TASK_LINE_RE = /^(- \[([ xX])\])\s+(\S+)/;
|
|
44
|
+
/** 解析 tasks.md 的全部任务行 */
|
|
45
|
+
export function parseTasksMd(content) {
|
|
46
|
+
const lines = content.split("\n");
|
|
47
|
+
const tasks = [];
|
|
48
|
+
for (let i = 0; i < lines.length; i++) {
|
|
49
|
+
const m = lines[i].match(TASK_LINE_RE);
|
|
50
|
+
if (!m)
|
|
51
|
+
continue;
|
|
52
|
+
const taskId = m[3];
|
|
53
|
+
const done = m[2].toLowerCase() === "x";
|
|
54
|
+
// 用正则锚定字段边界,不依赖 includes()
|
|
55
|
+
const tddMatch = lines[i].match(/\btdd_required:(true|false)\b/);
|
|
56
|
+
const reasonMatch = lines[i].match(/\bno_tdd_reason:(\S+)\b/);
|
|
57
|
+
tasks.push({
|
|
58
|
+
taskId,
|
|
59
|
+
lineIdx: i,
|
|
60
|
+
done,
|
|
61
|
+
tddRequired: tddMatch ? tddMatch[1] === "true" : true, // 默认 true
|
|
62
|
+
noTddReason: reasonMatch ? reasonMatch[1] : null,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return tasks;
|
|
66
|
+
}
|
|
67
|
+
/** 在 tasks.md 中按 taskId 精确查找任务(词边界,不误判子串) */
|
|
68
|
+
export function findTaskInLines(lines, taskId) {
|
|
69
|
+
for (let i = 0; i < lines.length; i++) {
|
|
70
|
+
// 精确匹配行内的 taskId token
|
|
71
|
+
const m = lines[i].match(TASK_LINE_RE);
|
|
72
|
+
if (m && m[3] === taskId)
|
|
73
|
+
return i;
|
|
74
|
+
// 回退:用转义正则匹配(兼容 taskId 后跟标点的情况)
|
|
75
|
+
const escaped = taskId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
76
|
+
if (new RegExp("(?:^|\\s)" + escaped + "(?:\\s|$|[.,:;!?)\\]])").test(lines[i]))
|
|
77
|
+
return i;
|
|
78
|
+
}
|
|
79
|
+
return -1;
|
|
80
|
+
}
|
|
81
|
+
/** tasks.md 结构指纹(复选框归一化) */
|
|
82
|
+
export function tasksStructureDigest(content, sha256Text) {
|
|
83
|
+
return sha256Text(content.replace(/- \[[xX]\]/g, "- [ ]"));
|
|
84
|
+
}
|
|
85
|
+
// ===== test-run JSON =====
|
|
86
|
+
//
|
|
87
|
+
// 格式(apply skill 定义):
|
|
88
|
+
// {
|
|
89
|
+
// "test_id": "TEST-XXX",
|
|
90
|
+
// "attempt_id": "ATT-TASK-XXX-...",
|
|
91
|
+
// "task_structure_digest": "sha256:...",
|
|
92
|
+
// "command": "npm test",
|
|
93
|
+
// "cwd": "/path",
|
|
94
|
+
// "exit_code": 1,
|
|
95
|
+
// "semantic_status": "expected_failure",
|
|
96
|
+
// "target_fingerprint": "sha256:..."
|
|
97
|
+
// }
|
|
98
|
+
//
|
|
99
|
+
// 引擎校验:test_id + task_structure_digest 必填,其余可选
|
|
100
|
+
export function validateTestRunInput(tr) {
|
|
101
|
+
if (!tr.test_id)
|
|
102
|
+
return { ok: false, message: "缺少 test_id" };
|
|
103
|
+
if (!tr.task_structure_digest)
|
|
104
|
+
return { ok: false, message: "缺少 task_structure_digest" };
|
|
105
|
+
return { ok: true, message: "" };
|
|
106
|
+
}
|
|
107
|
+
// ===== user-decision JSON =====
|
|
108
|
+
//
|
|
109
|
+
// 格式(explore skill 定义):
|
|
110
|
+
// {
|
|
111
|
+
// "scope": "enter_propose",
|
|
112
|
+
// "question": "是否进入计划阶段?",
|
|
113
|
+
// "answer": "yes"
|
|
114
|
+
// }
|
|
115
|
+
//
|
|
116
|
+
// 引擎校验:scope + answer 必填
|
|
117
|
+
export function validateUserDecision(d) {
|
|
118
|
+
if (!d.scope)
|
|
119
|
+
return { ok: false, message: "决策文件缺少 scope" };
|
|
120
|
+
if (!d.answer)
|
|
121
|
+
return { ok: false, message: "决策文件缺少 answer" };
|
|
122
|
+
return { ok: true, message: "" };
|
|
123
|
+
}
|
|
124
|
+
// ===== business-invariants.md =====
|
|
125
|
+
//
|
|
126
|
+
// 格式(propose skill 定义):
|
|
127
|
+
// # Business Invariants
|
|
128
|
+
// - INV-001 用户密码必须加密存储
|
|
129
|
+
//
|
|
130
|
+
// 引擎行为:Phase 1-5 只校验文件存在性(轻量)。
|
|
131
|
+
// 内部结构(INV-XXX 编号)是 agent 指引,引擎不逐行解析。
|
|
132
|
+
// ===== test-contract.md =====
|
|
133
|
+
//
|
|
134
|
+
// 格式(propose skill 定义):
|
|
135
|
+
// # Test Contract
|
|
136
|
+
// | test_id | invariant | scenario |
|
|
137
|
+
// |---|---|---|
|
|
138
|
+
// | TEST-001 | INV-001 | 注册时密码被加密 |
|
|
139
|
+
//
|
|
140
|
+
// 引擎行为:Phase 1-5 只校验文件存在性(轻量)。
|
|
141
|
+
// 表格结构是 agent 指引,引擎不逐行解析。
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const WORKFLOW_SKILLS: readonly ["superspec-explore", "superspec-propose", "superspec-apply", "superspec-review", "superspec-archive"];
|
|
2
|
+
export declare const WORKFLOW_PROMPTS: readonly ["architect.md", "code-reviewer.md", "critic.md", "executor.md", "explore.md", "final-audit.md", "proposal-auditor.md", "test-engineer.md", "test-runner.md", "verifier.md"];
|
|
3
|
+
export declare const WORKFLOW_AGENTS: readonly ["architect.toml", "code-reviewer.toml", "critic.toml", "executor.toml", "explore.toml", "final-audit.toml", "proposal-auditor.toml", "test-engineer.toml", "test-runner.toml", "verifier.toml"];
|
|
4
|
+
export interface InstallResult {
|
|
5
|
+
ok: boolean;
|
|
6
|
+
message: string;
|
|
7
|
+
installed: {
|
|
8
|
+
engine_dir: string;
|
|
9
|
+
skills: string[];
|
|
10
|
+
prompts: string[];
|
|
11
|
+
agents: string[];
|
|
12
|
+
config: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface InstallOptions {
|
|
16
|
+
templateRoot?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function installProject(projectRoot: string, options?: InstallOptions): InstallResult;
|