sillyspec 3.26.8 → 3.26.10

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.
Files changed (55) hide show
  1. package/.claude/skills/sillyspec-archive/SKILL.md +1 -2
  2. package/.claude/skills/sillyspec-brainstorm/SKILL.md +1 -2
  3. package/.claude/skills/sillyspec-execute/SKILL.md +11 -3
  4. package/.claude/skills/sillyspec-explore/SKILL.md +0 -1
  5. package/.claude/skills/sillyspec-plan/SKILL.md +1 -2
  6. package/.claude/skills/sillyspec-quick/SKILL.md +4 -5
  7. package/.claude/skills/sillyspec-scan/SKILL.md +2 -2
  8. package/.claude/skills/sillyspec-status/SKILL.md +0 -1
  9. package/.claude/skills/sillyspec-verify/SKILL.md +4 -3
  10. package/README.md +5 -9
  11. package/package.json +2 -2
  12. package/packages/dashboard/server/index.js +14 -8
  13. package/src/change-risk-profile.js +5 -207
  14. package/src/config-schema.js +6 -1
  15. package/src/constants.js +97 -106
  16. package/src/db-engine.js +1 -1
  17. package/src/docs-check.js +34 -5
  18. package/src/git-helper.js +2 -2
  19. package/src/hooks/worktree-guard.js +1 -1
  20. package/src/index.js +96 -13
  21. package/src/init.js +14 -3
  22. package/src/machine-interface.js +7 -4
  23. package/src/progress/stage-machine.js +3 -0
  24. package/src/quicklog.js +203 -4
  25. package/src/run/command.js +101 -3
  26. package/src/run/complete-handlers.js +1399 -1254
  27. package/src/run/complete.js +9 -1
  28. package/src/run/concurrent-detect.js +22 -0
  29. package/src/run/gates.js +67 -1
  30. package/src/run/prompt.js +761 -713
  31. package/src/run/quick-audit.js +146 -137
  32. package/src/run/shared.js +1215 -1109
  33. package/src/run/stage.js +17 -1
  34. package/src/scan-diff.js +388 -0
  35. package/src/scan-staleness.js +18 -13
  36. package/src/spec-sync.js +319 -0
  37. package/src/stage-contract-spec.js +1 -1
  38. package/src/stage-review.js +1 -1
  39. package/src/stages/archive.js +2 -1
  40. package/src/stages/brainstorm.js +2 -2
  41. package/src/stages/execute.js +1003 -985
  42. package/src/stages/plan-postcheck.js +22 -1
  43. package/src/stages/plan.js +74 -40
  44. package/src/stages/quick.js +13 -5
  45. package/src/stages/scan.js +4 -3
  46. package/src/stages/verify.js +14 -12
  47. package/src/sync.js +22 -0
  48. package/src/task-review.js +12 -4
  49. package/src/workflow.js +8 -185
  50. package/src/worktree-apply.js +122 -0
  51. package/src/worktree.js +50 -6
  52. package/templates/claude-instruction.md +1 -1
  53. package/templates/prompts/taskcard-rules.md +1 -0
  54. package/templates/prompts/verify-probes.md +2 -2
  55. package/templates/skills/sillyspec-onboard/SKILL.md +0 -92
@@ -33,8 +33,7 @@ sillyspec run archive --reopen --from-step N # 重新打开已完成阶段修
33
33
  | `--change <名>` | 指定变更名(多活跃变更必填,单变更可省略自动检测) |
34
34
  | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
35
35
  | `--non-interactive` | CI/脚本下禁用交互式 prompt |
36
- | `--skip-approval` | 跳过审批/校验门控(需明确意图) |
37
- | `--json` | 输出 JSON(程序化读取) |
36
+ | `--skip-approval` | 跳过阶段转换/审批检查(不能跳产物校验 gate——review.json/文档产物硬校验仍在) |
38
37
 
39
38
  ## archive 特有
40
39
 
@@ -41,8 +41,7 @@ sillyspec run brainstorm --done --answer "..." --output "..." # 一步完成
41
41
  | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
42
42
  | `--non-interactive` | CI/脚本下禁用交互式 prompt |
43
43
  | `--interactive` | 强制交互(即便 stdin 非 TTY) |
44
- | `--skip-approval` | 跳过审批/校验门控(需明确意图) |
45
- | `--json` | 输出 JSON(程序化读取) |
44
+ | `--skip-approval` | 跳过阶段转换/审批检查(不能跳产物校验 gate——review.json/文档产物硬校验仍在) |
46
45
 
47
46
  ## brainstorm 特有:requiresWait 步骤
48
47
 
@@ -33,8 +33,7 @@ sillyspec run execute --reopen --from-step N # 重新打开已完成阶段修
33
33
  | `--change <名>` | 指定变更名(多活跃变更必填,单变更可省略自动检测) |
34
34
  | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
35
35
  | `--non-interactive` | CI/脚本下禁用交互式 prompt |
36
- | `--skip-approval` | 跳过审批/校验门控(需明确意图) |
37
- | `--json` | 输出 JSON(程序化读取) |
36
+ | `--skip-approval` | 跳过阶段转换/审批检查(不能跳产物校验 gate——review.json/文档产物硬校验仍在) |
38
37
 
39
38
  ## execute 特有:Worktree 隔离
40
39
 
@@ -56,6 +55,14 @@ sillyspec worktree doctor --fix --change <变更名>
56
55
 
57
56
  `linked / installed / n/a` 放行;`missing / stale / failed / unknown` 阻断。Wave 内所有 task 声明 `no_deps_verify: true` 时可 opt-out。
58
57
 
58
+ ### 符号影响面报告(「加载上下文」步硬门)
59
+
60
+ execute 前缀步「加载上下文」的 `--done` 会硬校验符号影响面报告:
61
+
62
+ - 路径:`{SPEC_ROOT}/changes/<变更名>/symbol-impact.md`(步骤 prompt 中会输出具体路径)。
63
+ - 要求:plan.md 中**每个 task-XX 都要有一行结论**——涉及签名级变更(构造函数参数/接口/DTO/方法签名增删改)就列变更类型 + 受影响调用点 + 是否在任务范围内;无签名级变更也要显式写「无签名级变更」。
64
+ - 文件缺失或未覆盖全部 task → `--done` 被阻断(进度不推进),补全后重跑即可。
65
+
59
66
  ### Task Review Gate
60
67
 
61
68
  execute 完成时,每个 task 必须有 `review.json` 且 verdict 通过,否则阻断完成。**例外**:task 在 `tasks/task-XX.md` frontmatter 声明 `low_risk: true`(type-only / 机械迁移等低逻辑风险)时,缺 review.json 只发 warning 不阻断。`cannot_verify` 的 task 会写入 `verify-required-evidence.json`,由 verify 阶段消费。
@@ -109,6 +116,7 @@ execute 还有**第二道**独立的 stage 级审查:除逐 task review.json
109
116
 
110
117
  - `docHash` = `sha256(主审查文档内容)`(hex)—— execute 主审查文档是 `design.md`,即 `reviewedFiles[0]`。CLI 会重算 sha256 比对,不符判伪造(fail-closed);找不到主文档也 fail。**改 design.md 后须重算 docHash 再写**,可用 `sillyspec run execute --done` 触发的 prompt 注入版契约(运行时注入的 schema 表 + JSON 示例 + docHash 算法)逐字改值。
111
118
  - `tier=independent` 时必须由独立 QA 子代理产出该 review.json(独立上下文,不共享实现者分析);`tier=self`(变更 ≤3 文件)降级为当前 agent 自审。
119
+ - **审查范围分级**(tier=independent 时省重复消耗):Task Review Gate 已产出 review.json 且 specVerdict/qualityVerdict 双 pass 的 task,QA 子代理只抽查(读 1-2 个核心 diff 文件抽验 reviewerNotes 与实际改动相符);fail/cannot_verify/缺失的 task 全量重审。三项始终必查:跨 task 交界、design.md 整体对照、组装行为(全量测试/构建/启动)——task review 只看单 task diff,覆盖不到这三项。
112
120
  - 该 acceptance review 同时覆盖"代码审查"视角,后续代码审查步骤仅需轻量复审。
113
121
 
114
122
  > 运行时 CLI 会把精确 schema 表 + 完整 JSON 示例 + docHash 算法注入到该步 prompt。本段为常驻摘要;以你实际收到的注入版契约为权威逐字模板。
@@ -139,7 +147,7 @@ execute 还有**第二道**独立的 stage 级审查:除逐 task review.json
139
147
 
140
148
  - **主仓 task**:子代理 workdir = 主仓 worktree 路径(CLI 自动创建的隔离 worktree)。
141
149
  - **跨仓 task**:子代理 workdir = 跨仓仓根目录(**直接在跨仓仓主干工作区改+commit,不经主仓 worktree、不建分支**)。commit 到跨仓仓主干即落盘。
142
- - 同一个 Wave 内允许混合主仓 + 跨仓 task(每个 task 独立子代理,各传各的 workdir)。Wave prompt 会注入 per-task workdir 表,按表选。
150
+ - 同一个 Wave 内允许混合主仓 + 跨仓 task。默认每 task 独立子代理(各传各的 workdir,Wave prompt 注入 per-task workdir 表,按表选);同 Wave 内满足**文件正交 / 无契约链 / 组 ≤3** 三个条件的 task 可合并为一个 batch 子代理串行实现——batch 只合并实现,task 审查、review.json 产出与 checkbox 勾选仍归主 agent(子代理返回后逐 task 进行)。
143
151
 
144
152
  ### 跨仓 task 的双锡点(CLI 写入,子代理不改)
145
153
 
@@ -30,7 +30,6 @@ sillyspec run explore --status # 查看阶段状态
30
30
  |---|---|
31
31
  | `--change <名>` | 指定变更名(多活跃变更时) |
32
32
  | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
33
- | `--json` | 输出 JSON(程序化读取) |
34
33
 
35
34
  ## 这是什么模式
36
35
 
@@ -37,8 +37,7 @@ sillyspec run plan --done --answer "..." --output "..." # 一步完成 wait+d
37
37
  | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
38
38
  | `--non-interactive` | CI/脚本下禁用交互式 prompt |
39
39
  | `--interactive` | 强制交互(即便 stdin 非 TTY) |
40
- | `--skip-approval` | 跳过审批/校验门控(需明确意图) |
41
- | `--json` | 输出 JSON(程序化读取) |
40
+ | `--skip-approval` | 跳过阶段转换/审批检查(不能跳产物校验 gate——review.json/文档产物硬校验仍在) |
42
41
 
43
42
  ## plan 特有
44
43
 
@@ -52,15 +52,14 @@ sillyspec run quick --done --change quick-<hash> --output "…" # 完成该会
52
52
  | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
53
53
  | `--non-interactive` | CI/脚本下禁用交互式 prompt |
54
54
  | `--interactive` | 强制交互(即便 stdin 非 TTY) |
55
- | `--skip-approval` | 跳过审批/校验门控(需明确意图) |
56
- | `--json` | 输出 JSON(程序化读取) |
55
+ | `--skip-approval` | 跳过阶段转换/审批检查(不能跳产物校验 gate——review.json/文档产物硬校验仍在) |
57
56
 
58
57
  ## quick 特有参数
59
58
 
60
59
  | 参数 | 说明 |
61
60
  |---|---|
62
61
  | `--linked-changes none\|a,b` | **显式关联变更(取代 `--change`,推荐)**。none=不关联,a,b=关联列表 |
63
- | `--files a.js,b.js` | 显式声明本次允许修改的文件(边界保护) |
62
+ | `--files a.js,b.js` | 显式声明本次允许修改的文件(边界保护 + 声明即归属:多 agent 并发仓防他者窗口文件混入 QUICKLOG 文件行,未声明窗口文件进「审计:」行追溯) |
64
63
  | `--file-notes "p::注 \|\| p::注"` | quick `--done` 用:QUICKLOG「文件:」行落盘为多行带括注 bullet(省事后手改文件行)。格式 `path::括注`,`\|\|` 分隔多条;**只随 step3 --done 同命令传**(CLI 短进程,step1/step2 传无效,不带到 step3) |
65
64
  | `--allow-new` | 允许新增文件(默认禁止,防意外创建) |
66
65
  | `--allow-delete` | 允许删除文件(默认 fail-closed,删除是破坏性操作;确认删除带此 flag 显式解锁) |
@@ -104,8 +103,8 @@ sillyspec run quick --files src/phone.ts,src/phone.test.ts
104
103
  - 完成后立即 `--done`,不跳过
105
104
  - QUICKLOG 记录的**骨架由 CLI 接管**:启动时 CLI 自动分配 ql-ID 并在 `.sillyspec/quicklog/QUICKLOG-<user>.md` 写「进行中」条目(含关联变更 tasks.md),`--done` 时 CLI 自动翻「已完成」+ 勾选 task + 回填文件路径。ql-ID 分配/状态/task 你无需手写,只需用注入的 `<quicklog-id>` 在模块文档变更索引引用
106
105
  - **QUICKLOG 落盘已结构化(`--done` 后按需核对,多数无需手改)**:CLI 已落盘结构化条目——标题从 `--output` 的「需求:」自动提取、正文四字段自动分行、文件行用 `--file-notes` 时为多行带括注。`--done` 后只需核对:标题弱才改(禁留 `(quick 任务)` 占位);没用 `--file-notes` 时文件行是单行、可事后补括注(参照同文件早期丰富条目);正文 `需求:`/`根因:`/`方案:`/`结果:` 四段按需充实(禁只留一段「结果:」)。一条 quick = 一条独立 ql,不追加到旧条目
107
- - **收尾顺序(模块文档在 `--done` 前,QUICKLOG 在 `--done` 后,别记混)**:① 命中模块→改模块文档→`git add`;② `sillyspec run quick --done --change <id> --output "四字段" [--file-notes "..."]`(CLI 自动翻完成 + 勾 task + 落盘 QUICKLOG 标题/文件/正文);③ 核对 QUICKLOG 标题(CLI 已从「需求:」提取,仅弱标题才改)→若改了再 `git add`
108
- - **最后一步 `--done --output` 必须按结构化结果模板给全四字段**(逐项一句话):`需求:… 根因:… 方案:… 结果:…`。这是 QUICKLOG「结果:」归档的唯一来源;CLI 校验缺字段会拒绝 `--done`(exit 1),补全后重跑即可。正文内避免嵌套全角冒号(如「方案:(说明 xxx):」这样标签后紧接嵌套冒号会被拆分判定缺字段),直接让标签接正文。前两个 step 的 `--output` 是中间摘要,不用此模板
106
+ - **收尾顺序(模块文档在 `--done` 前,QUICKLOG 在 `--done` 后,别记混)**:① 命中模块→改模块文档→`git add`;② `sillyspec run quick --done --change <id> --output "四字段" [--file-notes "..."]`(CLI 自动翻完成 + 勾 task + 落盘 QUICKLOG 标题/文件/正文;若 `--linked-changes` 关联的真实变更其 tasks.md 已全部勾选,CLI 会自动将其归档到 `changes/archive/`,无需再跑 archive 阶段);③ 核对 QUICKLOG 标题(CLI 已从「需求:」提取并截断,写成短标题则无需改)→若改了再 `git add`
107
+ - **最后一步 `--done --output` 必须按结构化结果模板给全四字段**(逐项一句话):`需求:… 根因:… 方案:… 结果:…`。这是 QUICKLOG「结果:」归档的唯一来源;CLI 校验缺字段会拒绝 `--done`(exit 1),补全后重跑即可。**「需求:」写一句语义化短标题**(写「改了什么」,如「登录限流修复——INCR 计数误清」)——CLI 把它提取为 QUICKLOG 条目标题,截到首个标点、超 80 字截断;写完整需求长句会被截成语义不完整的状语前半段,需求背景放「根因:/方案:」。正文内避免嵌套全角冒号(如「方案:(说明 xxx):」这样标签后紧接嵌套冒号会被拆分判定缺字段),直接让标签接正文。前两个 step 的 `--output` 是中间摘要,不用此模板
109
108
  - **禁止**在没有运行 CLI 的情况下自行决定流程
110
109
 
111
110
  ## 用户指令
@@ -31,8 +31,7 @@ sillyspec run scan --reset # 重置阶段(从头开始)
31
31
  |---|---|
32
32
  | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
33
33
  | `--non-interactive` | CI/脚本下禁用交互式 prompt |
34
- | `--skip-approval` | 跳过审批/校验门控 |
35
- | `--json` | 输出 JSON(程序化读取) |
34
+ | `--skip-approval` | 跳过阶段转换/审批检查(不能跳产物校验 gate——review.json/文档产物硬校验仍在) |
36
35
 
37
36
  ## scan 特有参数
38
37
 
@@ -42,6 +41,7 @@ sillyspec run scan --reset # 重置阶段(从头开始)
42
41
  | `--standard` | 强制 standard profile(压缩步骤,最多 1 子代理) |
43
42
  | `--deep` | 强制 deep profile(完整流程,不按规模裁剪) |
44
43
  | `--force-rescan` | 覆盖已有 scan 文档的保护(默认覆盖需 source_commit/updated_at 匹配) |
44
+ | `--diff` | 增量漂移清单:`sillyspec scan diff`(或 `--diff` flag)算 source_commit..HEAD 的 A/D/M/R 四分类漂移,按 module-map paths 归模块,agent 据此定点补文档而非全量重扫(2026-08-16) |
45
45
 
46
46
  ### scanProfile(显式选择优先,否则按规模自动裁剪)
47
47
 
@@ -35,7 +35,6 @@ sillyspec run status --status # 查看阶段状态
35
35
  |---|---|
36
36
  | `--change <名>` | 指定变更名(多变更时查看指定变更详情) |
37
37
  | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
38
- | `--json` | 输出 JSON(程序化读取) |
39
38
 
40
39
  ## 配套的只读查询命令(不经 run)
41
40
 
@@ -34,12 +34,13 @@ sillyspec run verify --reopen --from-step N # 重新打开已完成阶段修
34
34
  | `--change <名>` | 指定变更名(多活跃变更必填,单变更可省略自动检测) |
35
35
  | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
36
36
  | `--non-interactive` | CI/脚本下禁用交互式 prompt |
37
- | `--skip-approval` | 跳过审批/校验门控(需明确意图) |
38
- | `--json` | 输出 JSON(程序化读取) |
37
+ | `--skip-approval` | 跳过阶段转换/审批检查(不能跳产物校验 gate——review.json/文档产物硬校验仍在) |
39
38
 
40
39
  ## verify 特有:完成门控(重要)
41
40
 
42
- verify 是只读阶段(**禁止改代码/改 git 状态**,只检查 + 写报告)。完成时有硬校验:
41
+ verify 是只读阶段(**禁止改代码/改 git 状态**,只检查 + 写报告)。
42
+
43
+ **diff 对账基点**:本变更走 worktree 时,「加载规范并锚定」步骤会注入 worktree 基线锚点(分支名 + `git merge-base` 真实基点)——对照设计做 diff 对账时以它为基点,**不要拿主仓当前 HEAD 当基点**(主仓在 execute 期间可能被并行推进,用错基点会把别人的演进误判为本变更越权改动)。task review 的 base/head 引用分支上的 commit,逐 task 核验用 `git diff <base>..<head>`。完成时有硬校验:
43
44
 
44
45
  - **必须产出 `verify-result.md`**——不存在则阻断完成(不能跳过报告直接 `--done`)
45
46
  - **结论为 `FAIL` 则阻断完成**——不能带着 FAIL 标记 verify 完成
package/README.md CHANGED
@@ -29,7 +29,7 @@ SillySpec 把一个变更的完整生命周期固化为一组**强制阶段**—
29
29
 
30
30
  ## 快速开始
31
31
 
32
- 需要 Node.js >= 18,一条命令完成初始化:
32
+ 需要 Node.js >= 22.13,一条命令完成初始化:
33
33
 
34
34
  ```bash
35
35
  npx sillyspec init
@@ -50,18 +50,16 @@ npx sillyspec init
50
50
 
51
51
  ```bash
52
52
  npx sillyspec init --tool claude # 指定工具
53
- npx sillyspec init --workspace # 多项目工作区模式
54
53
  npx sillyspec init --dir /path/to/x # 指定项目目录
55
54
  npx sillyspec init --interactive # 完整引导
56
55
  ```
57
56
 
58
- > 💡 启动 AI 时推荐使用跳过权限模式(如 `claude --dangerously-skip-permissions`)。SillySpec 会频繁执行 git、文件读写、校验脚本等操作,逐个批准会打断节奏——这也是 GSD / OpenSpec 的预期使用方式。
59
57
 
60
58
  ## 平台支持
61
59
 
62
60
  SillySpec 进度库使用 SQLite 持久化(基于 Node.js 内置的 `node:sqlite` 模块,纯 JS 无原生编译依赖)。
63
61
 
64
- - **Node.js 版本要求 >= 22.11.0**:`node:sqlite` 自 v22.11.0 起免 `--experimental-sqlite` flag 直接可用。支持 Linux x64、macOS x64 / arm64、Windows x64 等所有 Node.js 官方支持平台。
62
+ - **Node.js 版本要求 >= 22.13.0**:`node:sqlite` 自 v22.13.0 起免 `--experimental-sqlite` flag 直接可用。支持 Linux x64、macOS x64 / arm64、Windows x64 等所有 Node.js 官方支持平台。
65
63
  - **零编译安装**:不依赖任何原生绑定 / node-gyp / C++ 工具链,npm install 即装即用,无平台编译失败风险。
66
64
 
67
65
  ## 从哪里开始
@@ -101,7 +99,7 @@ SillySpec 进度库使用 SQLite 持久化(基于 Node.js 内置的 `node:sqli
101
99
  | `/sillyspec:brainstorm` | 需求探索 + 规范生成:产出 design.md + tasks.md |
102
100
  | `/sillyspec:plan` | 实现计划:文件路径 + 任务描述 + Wave 拓扑分组 |
103
101
  | `/sillyspec:execute` | TDD 执行:子代理并行 + worktree 隔离 |
104
- | `/sillyspec:verify` | 验证:对照规范 + 测试套件 + 代码审查 + E2E |
102
+ | `/sillyspec:verify` | 验证:对照规范 + 测试套件 + 代码审查 |
105
103
  | `/sillyspec:archive` | 归档:规范沉淀到 knowledge/ |
106
104
  | `/sillyspec:auto` | 全自动推进全部阶段 |
107
105
 
@@ -142,7 +140,7 @@ sillyspec doctor 全量自检 + 修复进度
142
140
  - **拓扑排序 Wave** — plan 阶段按蓝图依赖关系自动重排 Wave 分组
143
141
  - **进度持久化** — SQLite(sql.js WASM)持久化,支持断点恢复
144
142
  - **模块文档** — 模块级知识库,AI 执行时按需加载相关上下文
145
- - **E2E 验证** — 内置 E2E 测试流程,支持 Playwright / 浏览器 MCP,跨会话自动修复
143
+ - **浏览器自动化** — 可选 MCP(Playwright / Chrome DevTools),供集成/E2E 冒烟验证
146
144
  - **平台同步** — 可选对接 SillyHub,文档同步 + 团队审批
147
145
 
148
146
  ## 可靠性保障
@@ -152,7 +150,6 @@ SillySpec 不只是 prompt,还有硬校验兜底:
152
150
  - **锚定确认** — 各阶段执行前必须逐个确认读过规范文件
153
151
  - **Hard Gate 自检** — 关键命令生成文件后强制自检格式,不通过则修正
154
152
  - **postcheck 识别偷懒** — 自动识别占位符、fallback、未分析等 AI 偷懒模式
155
- - **校验脚本** — shell 脚本自动化验证 AI 输出(validate-proposal/plan/scan/all)
156
153
  - **归档确认** — archive 操作前展示内容等待用户确认
157
154
 
158
155
  ## 防幻觉机制
@@ -162,14 +159,13 @@ SillySpec 不只是 prompt,还有硬校验兜底:
162
159
  | scan | 强制扫描代码库结构 / 约定 / 集成 / 测试 / 债务,生成 7 份文档 |
163
160
  | brainstorm | 必须读 ARCHITECTURE.md 数据模型,支持原型图分析 |
164
161
  | execute | 写代码前强制读现有源码,禁止编造方法调用 |
165
- | 全流程 | postcheck + 校验脚本兜底 |
162
+ | 全流程 | postcheck + 硬门兜底 |
166
163
 
167
164
  ## MCP 增强
168
165
 
169
166
  通过 `sillyspec setup` 一键安装 MCP 工具,增强 AI 能力:
170
167
 
171
168
  - **Context7** — 查询最新库文档和 API 参考
172
- - **grep.app** — 搜索开源代码实现
173
169
  - **Chrome DevTools** — 浏览器自动化,支持 E2E 验证
174
170
 
175
171
  ## 目录结构
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sillyspec",
3
- "version": "3.26.8",
3
+ "version": "3.26.10",
4
4
  "description": "SillySpec CLI — 流程状态机,让 AI 严格按步骤来",
5
5
  "icon": "logo.jpg",
6
6
  "homepage": "https://sillyspec.ppdmq.top/",
@@ -13,7 +13,7 @@
13
13
  "sillyspec": "bin/sillyspec.js"
14
14
  },
15
15
  "engines": {
16
- "node": ">=22.11.0"
16
+ "node": ">=22.13.0"
17
17
  },
18
18
  "keywords": [
19
19
  "sillyspec",
@@ -564,14 +564,10 @@ function startServer({ port = 3456, open: openBrowser = true } = {}) {
564
564
  })
565
565
  })
566
566
 
567
- try {
568
- startWatcher((projects) => {
569
- broadcast({ type: 'projects:updated', data: projects })
570
- })
571
- } catch (err) {
572
- console.error('Failed to start file watcher:', err)
573
- }
574
-
567
+ // listen 前置:Windows 下 startWatcher 的初始全盘同步扫描(homedir/Temp/桌面 depth 2 +
568
+ // 每项目多次 execSync('git'))实测 150s+,若排在其后则服务永不响应(假死)。
569
+ // 先绑定端口(listen 回调立即可达),扫描用 setImmediate 延后到事件循环——端口立即响应,
570
+ // 初始 projects 数据由扫描完成后 broadcast 补发。
575
571
  server.listen(port, '127.0.0.1', () => {
576
572
  console.log(`Dashboard server running on http://127.0.0.1:${port}`)
577
573
  if (openBrowser) {
@@ -579,6 +575,16 @@ function startServer({ port = 3456, open: openBrowser = true } = {}) {
579
575
  }
580
576
  })
581
577
 
578
+ setImmediate(() => {
579
+ try {
580
+ startWatcher((projects) => {
581
+ broadcast({ type: 'projects:updated', data: projects })
582
+ })
583
+ } catch (err) {
584
+ console.error('Failed to start file watcher:', err)
585
+ }
586
+ })
587
+
582
588
  const shutdown = () => {
583
589
  stopWatcher()
584
590
  activeProcesses.forEach(kill => kill())
@@ -2,56 +2,13 @@
2
2
  * change-risk-profile.js — 变更风险分级检测
3
3
  *
4
4
  * 根据变更涉及的文件类型、关键词、git diff、brainstorm 产物,
5
- * 自动判定 P0/P1/P2 风险等级,产出结构化 risk-profile.json。
6
- *
7
- * P0 = 阻塞确认(必须用户确认)
8
- * P1 = 自动推进但记录
9
- * P2 = 自动通过
5
+ * 判定 5 级风险(doc-only / unit-sufficient / contract-required /
6
+ * integration-critical / deployment-critical),产出门控验证需求。
10
7
  */
11
8
 
12
- // ============ P0 触发规则 ============
13
-
14
- const P0_FILE_PATTERNS = [
15
- { id: 'R-002', pattern: /(?:^|\/)(?:migrations?|migration)\/(?:.+)/i, desc: '数据库 migration' },
16
- { id: 'R-002', pattern: /(?:^|\/)schema\.(?:sql|prisma|ts|js)$/i, desc: '数据库 schema' },
17
- { id: 'R-003', pattern: /(?:^|\/)(?:auth|permission|role|login|token)\b/i, desc: '鉴权/权限' },
18
- { id: 'R-004', pattern: /(?:^|\/)(?:payment|billing|stripe|paypal)\b/i, desc: '支付/资金' },
19
- { id: 'R-005', pattern: /\.(?:env|env\.\w+)$/, desc: '环境配置' },
20
- { id: 'R-005', pattern: /(?:^|\/)(?:Dockerfile|docker-compose\.\w+)$/i, desc: '部署配置' },
21
- { id: 'R-005', pattern: /(?:^|\/)config\.(?:yaml|yml|json|toml)$/i, desc: '生产配置' },
22
- ]
23
-
24
- const P0_CONTENT_PATTERNS = [
25
- { id: 'R-001', pattern: /\b(?:DROP\s+TABLE|TRUNCATE\s+TABLE|DELETE\s+FROM\s+\w+\s+(?!WHERE\s+.+\s+LIMIT))/i, desc: '删除数据' },
26
- { id: 'R-002', pattern: /\b(?:CREATE\s+TABLE|ALTER\s+TABLE|ADD\s+COLUMN|DROP\s+COLUMN|CREATE\s+INDEX)/i, desc: '数据库 migration' },
27
- ]
28
-
29
- // ============ P1 触发规则 ============
30
-
31
- const P1_FILE_PATTERNS = [
32
- { id: 'R-101', pattern: /(?:^|\/)(?:routes|controllers?|handlers?)\//i, desc: 'API 层' },
33
- { id: 'R-102', pattern: /(?:^|\/)(?:models|entities)\//i, desc: '数据模型层' },
34
- { id: 'R-104', pattern: /(?:^|\/)(?:services)\//i, desc: '业务逻辑层(跨模块)' },
35
- { id: 'R-106', pattern: /(?:^|\/)index\.(?:ts|js)$/i, desc: 're-export 入口' },
36
- ]
37
-
38
- const P1_CONTENT_PATTERNS = [
39
- { id: 'R-101', pattern: /\b(?:daemon|backend|grpc|websocket|cross.?process|ipc|message.?queue)\b/i, desc: '跨进程通信' },
40
- { id: 'R-101', pattern: /\b(?:session|lease|agent.?run|lifecycle|state.?transition|claim|heartbeat)\b/i, desc: '状态机/生命周期' },
41
- { id: 'R-102', pattern: /\b(?:api|client|contract|dto)\b/i, desc: 'API contract' },
42
- { id: 'R-105', pattern: /(?:^|\/)(?:workflow|daemon|session|lifecycle|state-machine)\b/i, desc: '核心模块' },
43
- ]
44
-
45
- // ============ P0/P1 命中文件名关键词(用于 git diff 检测) ============
46
-
47
- const PROTECTED_FILE_NAMES = [
48
- '.env', 'package.json', 'tsconfig.json', 'vite.config.ts', 'vite.config.js',
49
- 'prisma/schema.prisma', 'docker-compose.yml', 'docker-compose.yaml',
50
- ]
51
-
52
9
  // ============ 向后兼容:旧的 INTEGRATION_CRITICAL_PATTERNS ============
53
10
 
54
- export const INTEGRATION_CRITICAL_PATTERNS = [
11
+ const INTEGRATION_CRITICAL_PATTERNS = [
55
12
  /\bdaemon\b/i,
56
13
  /\bbackend\b/i,
57
14
  /\bclient.*api\b/i,
@@ -78,7 +35,7 @@ export const INTEGRATION_CRITICAL_PATTERNS = [
78
35
  /\bdocker.?compose\b/i,
79
36
  ]
80
37
 
81
- export const INTEGRATION_FILE_PATTERNS = [
38
+ const INTEGRATION_FILE_PATTERNS = [
82
39
  /daemon/i,
83
40
  /session.?manager/i,
84
41
  /agent.?run/i,
@@ -114,165 +71,6 @@ export function isEndToEndTaskText(text = '') {
114
71
  return END_TO_END_TASK_KEYWORDS.some(re => re.test(text))
115
72
  }
116
73
 
117
- // ============ 核心检测函数 ============
118
-
119
- /**
120
- * 三级风险检测:P0 / P1 / P2
121
- * @param {object} opts
122
- * @param {string} [opts.designContent] - design.md 内容
123
- * @param {string} [opts.planContent] - plan.md 内容
124
- * @param {string[]} [opts.changedFiles] - 变更文件列表
125
- * @param {string} [opts.diffContent] - git diff 内容(可选,用于更精确检测)
126
- * @param {object} [opts.nextAction] - next-action.json 解析后的对象(可选)
127
- * @param {string[]} [opts.protectedFiles] - 项目自定义 protected files 列表
128
- * @returns {RiskProfile}
129
- *
130
- * @typedef {object} RiskProfile
131
- * @property {string} level - 'P0' | 'P1' | 'P2'
132
- * @property {string[]} triggers - 触发的规则 ID 列表
133
- * @property {object[]} assessedFrom - 评估依据
134
- * @property {string[]} applyBlockers - apply 阻塞原因
135
- * @property {boolean} canAutoApply - 是否可自动 apply
136
- * @property {string} applyReason - apply 决策原因
137
- */
138
- export function detectRiskProfile({ designContent = '', planContent = '', changedFiles = [], diffContent = '', nextAction = null, protectedFiles = [] } = {}) {
139
- const triggers = []
140
- const assessedFrom = []
141
-
142
- const combined = [designContent, planContent].join('\n')
143
-
144
- // ── P0 检测(最高优先级)──
145
-
146
- // 基于文件路径
147
- for (const rule of P0_FILE_PATTERNS) {
148
- for (const file of changedFiles) {
149
- if (rule.pattern.test(file)) {
150
- rule.pattern.lastIndex = 0
151
- if (!triggers.includes(rule.id)) {
152
- triggers.push(rule.id)
153
- assessedFrom.push({ source: 'file_path', pattern: rule.desc, matchedFiles: [file] })
154
- }
155
- }
156
- }
157
- }
158
-
159
- // 基于 diff 内容(P0 关键词)
160
- const diffText = diffContent || combined
161
- for (const rule of P0_CONTENT_PATTERNS) {
162
- if (rule.pattern.test(diffText)) {
163
- rule.pattern.lastIndex = 0
164
- if (!triggers.includes(rule.id)) {
165
- triggers.push(rule.id)
166
- assessedFrom.push({ source: 'content', pattern: rule.desc })
167
- }
168
- }
169
- }
170
-
171
- // protected files
172
- for (const file of changedFiles) {
173
- if (PROTECTED_FILE_NAMES.some(p => file === p || file.endsWith('/' + p))) {
174
- const id = 'R-005'
175
- if (!triggers.includes(id)) {
176
- triggers.push(id)
177
- assessedFrom.push({ source: 'protected_file', pattern: file })
178
- }
179
- }
180
- // 用户自定义 protected files
181
- if (protectedFiles.includes(file)) {
182
- const id = 'R-005'
183
- if (!triggers.includes(id)) {
184
- triggers.push(id)
185
- assessedFrom.push({ source: 'protected_file', pattern: file })
186
- }
187
- }
188
- }
189
-
190
- // next-action.json 有 blocking questions
191
- if (nextAction && nextAction.has_blocking_questions === true) {
192
- const id = 'R-009'
193
- if (!triggers.includes(id)) {
194
- triggers.push(id)
195
- assessedFrom.push({ source: 'brainstorm', pattern: 'has_blocking_questions === true' })
196
- }
197
- }
198
-
199
- // ── P1 检测(只在无 P0 时检测)──
200
- if (!triggers.some(t => t.startsWith('R-00'))) {
201
- // 基于文件路径
202
- for (const rule of P1_FILE_PATTERNS) {
203
- for (const file of changedFiles) {
204
- if (rule.pattern.test(file)) {
205
- rule.pattern.lastIndex = 0
206
- if (!triggers.includes(rule.id)) {
207
- triggers.push(rule.id)
208
- assessedFrom.push({ source: 'file_path', pattern: rule.desc, matchedFiles: [file] })
209
- }
210
- }
211
- }
212
- }
213
-
214
- // 基于内容关键词
215
- for (const rule of P1_CONTENT_PATTERNS) {
216
- if (rule.pattern.test(combined)) {
217
- rule.pattern.lastIndex = 0
218
- if (!triggers.includes(rule.id)) {
219
- triggers.push(rule.id)
220
- assessedFrom.push({ source: 'content', pattern: rule.desc })
221
- }
222
- }
223
- }
224
-
225
- // 跨模块检测(> 3 个文件的变更)
226
- if (changedFiles.length > 10) {
227
- const id = 'R-101'
228
- if (!triggers.includes(id)) {
229
- triggers.push(id)
230
- assessedFrom.push({ source: 'file_count', pattern: `${changedFiles.length} files changed` })
231
- }
232
- }
233
-
234
- // next-action.json 有 NEEDS_REVIEW 决策
235
- if (nextAction && Array.isArray(nextAction.auto_decisions)) {
236
- for (const d of nextAction.auto_decisions) {
237
- if (d.status === 'NEEDS_REVIEW') {
238
- const id = 'R-102'
239
- if (!triggers.includes(id)) {
240
- triggers.push(id)
241
- assessedFrom.push({ source: 'brainstorm', pattern: `NEEDS_REVIEW: ${d.decision}` })
242
- }
243
- }
244
- }
245
- }
246
- }
247
-
248
- // ── 判定等级 ──
249
- let level = 'P2'
250
- if (triggers.some(t => t.match(/^R-0\d+$/))) {
251
- level = 'P0'
252
- } else if (triggers.some(t => t.match(/^R-1\d+$/))) {
253
- level = 'P1'
254
- }
255
-
256
- // ── apply 决策 ──
257
- const applyBlockers = []
258
- if (level === 'P0') {
259
- applyBlockers.push('风险等级 P0:需要用户确认后才能 apply')
260
- }
261
-
262
- const canAutoApply = level !== 'P0' && applyBlockers.length === 0
263
- const applyReason = canAutoApply
264
- ? `风险等级 ${level},无 P0 触发,无 protected files 修改`
265
- : applyBlockers.join('; ')
266
-
267
- return { level, triggers, assessedFrom, applyBlockers, canAutoApply, applyReason }
268
- }
269
-
270
- // ============ 向后兼容 ============
271
-
272
- /**
273
- * 旧的 detectChangeRisk 接口保持向后兼容
274
- * @returns {{ level: string, triggers: string[], requiredVerification: string[] }}
275
- */
276
74
  // ============ 门控可执行化(A:报错说人话) ============
277
75
  //
278
76
  // 历史教训:integration-critical / deployment-critical 门控只报「缺少真实集成证据 / 需要真实
@@ -320,7 +118,7 @@ export const RISK_LEVEL_CAUSES = {
320
118
  }
321
119
 
322
120
  /** design.md frontmatter 可显式声明的合法 risk_level 值(与 detectChangeRisk 判级结果同集合) */
323
- export const RISK_LEVELS = ['doc-only', 'unit-sufficient', 'contract-required', 'integration-critical', 'deployment-critical']
121
+ const RISK_LEVELS = ['doc-only', 'unit-sufficient', 'contract-required', 'integration-critical', 'deployment-critical']
324
122
 
325
123
  /**
326
124
  * 从 design.md 顶部 frontmatter 提取显式 risk_level 声明。
@@ -150,9 +150,10 @@ export const LOCAL_YAML_SCHEMA = {
150
150
  title: '文档引用校验',
151
151
  note: 'sillyspec docs check 的扫描与断言配置(2026-08-15 docs-check-productize)。glob 相对源码仓根展开,仅支持 目录/**/*.扩展、目录/*.扩展、字面路径 三形态。',
152
152
  keys: [
153
- { path: 'docs-check.paths', type: 'array', optional: true, status: 'live', readers: ['docs check (src/index.js case docs) + runDocsCheck (src/docs-check.js)'], desc: '扫描的文档 glob 列表,缺省 docs/**/*.md。', example: 'docs/**/*.md' },
153
+ { path: 'docs-check.paths', type: 'array', optional: true, status: 'live', readers: ['docs check (src/index.js case docs) + runDocsCheck (src/docs-check.js)'], desc: '扫描的文档 glob 列表,缺省 docs/**/*.md 与 .sillyspec/docs/**/*.md(scan/modules 产物同纳入)。', example: 'docs/**/*.md' },
154
154
  { path: 'docs-check.skip', type: 'array', optional: true, status: 'live', readers: ['walkGlob (src/docs-check.js)'], desc: '排除的路径/glob 列表,缺省空。', example: 'docs/sillyspec/archive' },
155
155
  { path: 'docs-check.keywordAssert', type: 'boolean', optional: true, status: 'live', readers: ['runDocsCheck (src/docs-check.js)'], desc: '层2 关键词断言开关,缺省 true(关闭时 warning 提示仅做存在性校验)。', example: 'true' },
156
+ { path: 'docs-check.living-docs', type: 'array', optional: true, status: 'live', readers: ['resolveLivingDocs (src/run/shared.js) — 经 readLocalYamlRaw 直读'], desc: '活文档监控点追加列表(docs check 之外,quick 审计的 livingDocDrift 提示也监控)。只追加不覆盖缺省集合(缺省:docs/sillyspec/platform-interface-map.md)。配了是加哨兵,不该把缺省监控点挤掉。', example: 'docs/sillyspec/architecture.md' },
156
157
  ],
157
158
  },
158
159
  ],
@@ -295,11 +296,15 @@ auto_mode:
295
296
  - fix typo
296
297
 
297
298
  # ── 文档引用校验(sillyspec docs check;glob 相对源码仓根,三形态:递归/单层/字面路径)──
299
+ # 缺省已扫 docs/**/*.md 与 .sillyspec/docs/**/*.md;本段仅在需要收窄/排除时配置
298
300
  docs-check:
299
301
  paths:
300
302
  - docs/**/*.md
303
+ - .sillyspec/docs/**/*.md
301
304
  # skip:
302
305
  # - docs/sillyspec/archive
303
306
  keywordAssert: true
307
+ # living-docs: # 活文档监控点追加(缺省已含 docs/sillyspec/platform-interface-map.md,只加哨兵不覆盖)
308
+ # - docs/sillyspec/architecture.md
304
309
  `;
305
310
  }