sillyspec 3.26.11 → 3.26.13

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 (58) hide show
  1. package/.claude/skills/sillyspec-archive/SKILL.md +1 -1
  2. package/.claude/skills/sillyspec-execute/SKILL.md +1 -1
  3. package/.claude/skills/sillyspec-knowledge/SKILL.md +1 -1
  4. package/.claude/skills/sillyspec-plan/SKILL.md +5 -5
  5. package/.claude/skills/sillyspec-quick/SKILL.md +5 -1
  6. package/README.md +7 -5
  7. package/SKILL.md +5 -4
  8. package/package.json +1 -1
  9. package/packages/dashboard/server/executor.js +10 -1
  10. package/packages/dashboard/server/index.js +108 -46
  11. package/src/config-schema.js +3 -0
  12. package/src/contract-matrix.js +22 -13
  13. package/src/db.js +24 -4
  14. package/src/dispatch/probe.js +99 -63
  15. package/src/docs-check.js +58 -13
  16. package/src/docs-debt.js +6 -2
  17. package/src/docs-gate.js +1 -0
  18. package/src/doctor-diagnostics.js +208 -21
  19. package/src/fs-atomic.js +3 -1
  20. package/src/git-helper.js +7 -4
  21. package/src/hooks/worktree-guard.js +8 -2
  22. package/src/index.js +161 -12
  23. package/src/init.js +3 -2
  24. package/src/local-detect.js +9 -1
  25. package/src/modules.js +7 -5
  26. package/src/progress/stage-machine.js +589 -578
  27. package/src/progress/step-store.js +1 -1
  28. package/src/progress.js +184 -30
  29. package/src/quicklog.js +47 -16
  30. package/src/run/command.js +31 -8
  31. package/src/run/complete-handlers.js +21 -14
  32. package/src/run/complete.js +1181 -1121
  33. package/src/run/gates.js +148 -25
  34. package/src/run/prompt.js +9 -5
  35. package/src/run/shared.js +138 -58
  36. package/src/run/stage.js +41 -1
  37. package/src/setup.js +8 -10
  38. package/src/sillyhub-mcp/client.js +74 -6
  39. package/src/spec-dir-typo.js +3 -24
  40. package/src/spec-sync.js +43 -8
  41. package/src/stage-contract.js +12 -38
  42. package/src/stages/archive.js +4 -3
  43. package/src/stages/brainstorm-auto.js +1 -1
  44. package/src/stages/brainstorm.js +2 -1
  45. package/src/stages/doctor.js +28 -13
  46. package/src/stages/execute.js +164 -153
  47. package/src/stages/plan-postcheck.js +49 -28
  48. package/src/stages/plan.js +57 -40
  49. package/src/stages/quick.js +3 -1
  50. package/src/stages/verify.js +1 -1
  51. package/src/sync.js +323 -221
  52. package/src/task-review.js +246 -61
  53. package/src/taskcard.js +178 -0
  54. package/src/verify-postcheck.js +48 -35
  55. package/src/worktree-apply.js +58 -34
  56. package/src/worktree-deps.js +120 -20
  57. package/src/worktree.js +55 -9
  58. package/.npmrc-publish +0 -2
@@ -134,7 +134,8 @@ const stepGeneratePlan = {
134
134
  1. 读取 plan.md frontmatter 的 \`plan_level:\` 字段(上一步已落盘为持久锚点;文件不存在或无该字段时回退读上一步输出的分类结果)
135
135
  2. 读取 tasks.md 和 design.md 了解需求范围
136
136
  3. 按 plan_level 选择对应模板输出
137
- 4. 保存 plan.md(审查在下一步"审查计划"独立进行,不在本步自审——避免生成与自审同一次输出);frontmatter 保留 plan_level 字段不删
137
+ 4. **写回任务清单**:把展开后的任务清单写回 tasks.md(checkbox 行 \`- [ ] task-XX: 一句话任务名\`,可附 \`[model:xxx]\`/\`(depends_on: task-01,02)\` 行内标注)。**写回规则(D-002@v1)**:保留 frontmatter/中文标题/所有非 task-XX 行(quick 挂载的 ql-xxx 勾选行、注记等逐行保留),仅重写 task-XX checkbox 行集合——防摧毁 quick 挂载条目
138
+ 5. 保存 plan.md(审查在下一步"审查计划"独立进行,不在本步自审——避免生成与自审同一次输出);frontmatter 保留 plan_level 字段不删。**plan.md Wave 段下任务一律纯 ID 引用行**(\`- task-XX\`,不重抄任务名——任务名唯一真相在 tasks.md,重抄即双写漂移)
138
139
 
139
140
  ---
140
141
 
@@ -151,17 +152,15 @@ plan_level: none
151
152
  <一句话说明判定理由>
152
153
 
153
154
  ## 建议直接 execute
154
- 直接进入 execute 阶段完成下列最小任务。
155
-
156
- ## Tasks
157
- - [ ] task-01: 按用户需求完成小范围明确修改
155
+ 直接进入 execute 阶段完成最小任务(任务清单见 tasks.md)。
158
156
 
159
157
  ## 验收
160
158
  - 修改范围符合用户需求
161
159
  - 不引入额外无关变更
162
160
  - 必要测试或检查通过
163
161
  \`\`\`
164
- **注意:** 所有 plan_level 都必须包含 \`- [ ] task-XX:\` 格式的 checkbox 任务,execute 阶段依赖此格式解析任务。
162
+ 同时把最小任务写入 tasks.md:\`- [ ] task-01: 按用户需求完成小范围明确修改\`(同样遵守写回规则)。
163
+ **注意:** 任务 checkbox 只在 tasks.md(\`- [ ] task-XX:\` 格式,execute 阶段从 tasks.md 解析任务、按 plan.md Wave 引用分组)。
165
164
 
166
165
  ---
167
166
 
@@ -181,11 +180,6 @@ plan_level: light
181
180
  ## 范围
182
181
  - 涉及的文件/模块清单
183
182
 
184
- ## Tasks
185
- - [ ] task-01: ...(覆盖:FR-01, D-001@v1)
186
- - [ ] task-02: ...
187
- - [ ] task-03: ...
188
-
189
183
  ## 验收
190
184
  - 具体可验证的验收条目
191
185
 
@@ -194,6 +188,7 @@ plan_level: light
194
188
  |---|---|---|
195
189
  | D-001@v1 | task-01 | AC-01 |
196
190
  \`\`\`
191
+ 任务清单一律写 tasks.md(本步操作 4 写回;light 级 plan.md **无任务区**——execute 自动把注册表合成为单个隐式 Wave 串行执行)。
197
192
 
198
193
  light 计划的约束:
199
194
  - **禁止**生成 Mermaid 图
@@ -201,10 +196,10 @@ light 计划的约束:
201
196
  - **禁止**泛泛风险分析(如"需要充分测试")
202
197
  - **禁止**放实现细节(函数签名、代码示例)
203
198
  - 来源/目标直接引用已有文档,不重新生成
204
- - 如果存在 decisions.md,所有当前版本 D-xxx@vN 必须在 Tasks 或覆盖矩阵中出现(CLI 只校验 D-xxx@vN ID 字面出现在 plan.md,warning 不阻断;矩阵结构供人类追溯,CLI 不校验 D→FR→task 映射完整性)
199
+ - 如果存在 decisions.md,所有当前版本 D-xxx@vN 必须在覆盖矩阵中出现(CLI 只校验 D-xxx@vN ID 字面出现在 plan.md,warning 不阻断;矩阵结构供人类追溯,CLI 不校验 D→FR→task 映射完整性)
205
200
  - 如果存在 P0/P1 unresolved blocker,不生成 plan.md
206
- - 任务列表控制在 10 条以内
207
- - **任务必须使用 checkbox 格式**(\`- [ ] task-XX:\`),不要用纯编号列表(\`1. 2.\`),execute 阶段依赖此格式解析任务
201
+ - 任务清单(tasks.md)控制在 10 条以内
202
+ - 任务名使用 tasks.md checkbox 格式(\`- [ ] task-XX:\`),plan.md 不放任务行
208
203
 
209
204
  ---
210
205
 
@@ -226,11 +221,11 @@ plan_level: full
226
221
  > 技术不确定性高时才需要 Spike。无不确定性则跳过此节。
227
222
 
228
223
  ## Wave 1(并行,无依赖)
229
- - [ ] task-01: 添加用户创建接口(覆盖:FR-01, D-001@v1)
230
- - [ ] task-02: 添加角色创建接口(覆盖:FR-02)
224
+ - task-01
225
+ - task-02
231
226
 
232
227
  ## Wave 2(依赖 Wave 1)
233
- - [ ] task-03: 用户创建接口联调
228
+ - task-03
234
229
 
235
230
  ## 任务总表
236
231
  | 编号 | 任务 | Wave | 优先级 | 依赖 | 覆盖 FR/D | 说明 |
@@ -243,9 +238,11 @@ plan_level: full
243
238
  task-01 → task-03(最长路径,决定最短交付周期)
244
239
 
245
240
  ## 全局验收标准
246
- - [ ] 所有单元测试通过
247
- - [ ] 集成敏感 task(路由/layout/跨进程装配)建议加集成冒烟验收——组件单测全绿 ≠ 集成正确
248
- - [ ] (brownfield)未配置新功能时行为不变
241
+ 1. 所有单元测试通过
242
+ 2. 集成敏感 task(路由/layout/跨进程装配)建议加集成冒烟验收——组件单测全绿 ≠ 集成正确
243
+ 3. (brownfield)未配置新功能时行为不变
244
+
245
+ > 验收结论不落在 plan.md(此段是验收清单,plan.md 不做执行态文件)——逐项核验结果由 verify 阶段写入 verify-result.md(PASS/FAIL 结论与证据);task 级验收对照 TaskCard frontmatter 的 acceptance 字段。
249
246
 
250
247
  ## 覆盖矩阵(如存在 decisions.md)
251
248
  | ID | 覆盖任务 | 验收证据 |
@@ -257,7 +254,7 @@ full 计划的约束:
257
254
  - **禁止**估时(任务总表不含估时列)
258
255
  - **禁止**泛泛风险分析("需要充分测试"类废话转为具体验收条目)
259
256
  - Mermaid 依赖关系图**仅当依赖关系非平凡时生成**(线性依赖或全并行时不生成)
260
- - **Wave 下的 checkbox 行必须保留**(execute 阶段解析依赖 \`- [ ] task-XX:\` 格式)
257
+ - **Wave 段 ID 引用行必须保留**(\`- task-XX\` 纯 ID、无 checkbox 无任务名——机器解析依赖;任务名唯一真相在 tasks.md,plan.md 重抄即双写漂移)
261
258
  - plan.md 包含 Wave 分组 + 任务总表 + 关键路径 + 全局验收标准,**不放实现细节**
262
259
  - 如果存在 decisions.md,plan.md 建议包含当前版本 D-xxx@vN/FR-xxx 覆盖矩阵(CLI 只校验 D-xxx@vN/FR-xxx ID 字面出现在 plan.md,warning 不阻断;矩阵的 D→FR→task 映射完整性供人类追溯,CLI 不校验——勿以为画了矩阵就被结构校验)
263
260
  - 如果存在 P0/P1 unresolved blocker,不生成 plan.md,输出阻塞清单
@@ -284,11 +281,12 @@ full 计划的约束:
284
281
  ---
285
282
 
286
283
  ### 通用操作(所有级别)
287
- 1. 读取 tasks.md 获取任务列表
284
+ 1. 读取 tasks.md 获取任务列表(注册表唯一真相)
288
285
  2. 读取 design.md 获取文件变更清单
289
286
  3. 读取 plan.md frontmatter 的 \`plan_level:\` 字段(持久锚点;缺失时回退上一步输出)
290
287
  4. 按对应级别模板生成内容
291
288
  5. 保存到变更目录下的 plan.md(路径格式:\`{SPEC_ROOT}/changes/<change-name>/plan.md\`,其中 <change-name> 是变更目录名,直接使用,不加子目录。正确路径示例:\`{SPEC_ROOT}/changes/2026-05-28-agent-log-streaming/plan.md\`)
289
+ 6. 写回 tasks.md(本步操作 4 的写回规则:仅重写 task-XX checkbox 行集合,保留非 task-XX 行)
292
290
  **plan_level 为 none 时生成最小 plan.md(占位),不生成完整蓝图。**
293
291
 
294
292
  ---
@@ -392,6 +390,7 @@ execute/verify 阶段会按实际代码变更更新此文档;archive 阶段会
392
390
  * 在保留 plan 任务拆分完整性的同时减少子代理调用数量。
393
391
  */
394
392
  export function buildCoordinatorStep(changeDir, taskNames) {
393
+ const changeName = path.basename(changeDir)
395
394
  const taskList = taskNames.map(t => {
396
395
  const num = t.num
397
396
  return `- task-${num}: ${t.name}`
@@ -484,8 +483,10 @@ ${taskList}
484
483
  ## 操作
485
484
  1. 读取 ${changeDir}/design.md 和 ${changeDir}/plan.md 了解整体上下文
486
485
  2. 读取本 batch 涉及的相关源文件
487
- 3. 为 batch 中的**每一个 task 独立生成**一个 task-N.md 文件
488
- 4. 每个文件使用如下模板(把 <task-id> / <task-name> 替换为具体编号和名称):
486
+ 3. 为 batch 中的**每一个 task** 先运行 CLI 生成 Windows 安全骨架(在项目根目录执行;编号/标题自动取自 plan.md,author/created_at 自动填写):
487
+ sillyspec taskcard ${changeName} --task <本 batch 任务号,逗号分隔,如 task-01,task-02>
488
+ 4. 用 Edit tool 逐卡填充骨架占位符(allowed_paths/goal/implementation/acceptance/verify/constraints 等)。**禁止用 Write 整文件重写**——手写整卡是 CRLF 行尾/漏闭合 --- /漏硬校验字段三类 postcheck 拒绝的根源,骨架 + Edit 从源头消灭
489
+ 5. 骨架字段含义与可选字段(provides/expects_from/related_tests,按需插进 frontmatter)参考下述模板:
489
490
 
490
491
  ${taskcardTemplate}
491
492
 
@@ -498,7 +499,7 @@ ${taskcardTemplate}
498
499
 
499
500
  ## 完成标志
500
501
  - 本 batch 的每个 task-N.md 都已写入 ${changeDir}/tasks/
501
- - 每个文件非空且 frontmatter 完整
502
+ - 每个文件非空且 frontmatter 完整(保持骨架的 --- 闭合与 LF 行尾)
502
503
  \`\`\`
503
504
 
504
505
  ## 验收(生成后自查,不另开步骤)
@@ -558,21 +559,23 @@ export const fixedSuffix = [] // postcheck 是动态生成的(需要 changeDir
558
559
  // ═══════════════════════════════════════════════════════════════
559
560
 
560
561
  /**
561
- * 解析 plan.md 获取任务数量
562
+ * 解析任务清单内容获取任务数量(2026-08-20-task-truth-unify:源迁 tasks.md 注册表;
563
+ * 传入内容为注册表内容,plan.md 回退场景由调用方决定传谁)
562
564
  */
563
- function parseTaskCount(planContent) {
564
- if (!planContent || typeof planContent !== 'string') return 0
565
- const matches = planContent.match(/^[-*]\s*\[[ x]\]\s*task-\d+/gm)
565
+ function parseTaskCount(registryContent) {
566
+ if (!registryContent || typeof registryContent !== 'string') return 0
567
+ const matches = registryContent.match(/^[-*]\s*\[[ x]\]\s*task-\d+/gm)
566
568
  return matches ? matches.length : 0
567
569
  }
568
570
 
569
571
  /**
570
- * 从 plan.md 解析任务名列表
572
+ * 解析任务名列表(源=tasks.md 注册表;taskcard.js 复用,口径须同源)
573
+ * export 供 taskcard.js 复用(sillyspec taskcard 从 checkbox 行自动带出编号/标题)
571
574
  */
572
- function parseTaskNames(planContent) {
573
- // 返回 [{name, num}]:保留 plan.md 原始编号,避免 buildCoordinatorStep 按 i+1 重编号与 execute 原始编号错位
575
+ export function parseTaskNames(registryContent) {
576
+ // 返回 [{name, num}]:保留原始编号,避免 buildCoordinatorStep 按 i+1 重编号与 execute 原始编号错位
574
577
  const tasks = []
575
- const lines = planContent.split('\n')
578
+ const lines = String(registryContent || '').split('\n')
576
579
  for (const line of lines) {
577
580
  const m = line.match(/^[-*]\s*\[[ x]\]\s*task-(\d+):\s*(.+)/i)
578
581
  if (m) tasks.push({ num: String(m[1]).padStart(2, '0'), name: m[2].trim() })
@@ -580,6 +583,18 @@ function parseTaskNames(planContent) {
580
583
  return tasks
581
584
  }
582
585
 
586
+ /**
587
+ * 读变更目录的任务注册表内容(tasks.md 唯一真相;缺失回退 plan.md——旧归档变更兼容读侧)
588
+ */
589
+ export function readTaskRegistryContent(changeDir) {
590
+ if (!changeDir) return null
591
+ const tasksFile = path.join(changeDir, 'tasks.md')
592
+ if (existsSync(tasksFile)) return readFileSync(tasksFile, 'utf8')
593
+ const planFile = path.join(changeDir, 'plan.md')
594
+ if (existsSync(planFile)) return readFileSync(planFile, 'utf8')
595
+ return null
596
+ }
597
+
583
598
  /**
584
599
  * 动态构建 plan 步骤列表
585
600
  * 新架构:3 个 LLM 步骤 + 1 个 noAI 步骤 = 4 阶段
@@ -591,15 +606,17 @@ function parseTaskNames(planContent) {
591
606
  export function buildPlanSteps(changeDir = null, planContent = null) {
592
607
  let taskCount = 0
593
608
 
594
- // 尝试从 plan.md 解析任务数
595
- if (planContent) {
609
+ // 尝试从任务注册表解析任务数(tasks.md 唯一真相;2026-08-20-task-truth-unify 起
610
+ // plan.md Wave 段为纯 ID 引用行,checkbox 计数须读 tasks.md。planContent 入参兼容
611
+ // 旧调用(视为注册表内容);changeDir 兜底读 tasks.md → plan.md)
612
+ if (changeDir) {
613
+ const registryContent = readTaskRegistryContent(changeDir)
614
+ if (registryContent != null) taskCount = parseTaskCount(registryContent)
615
+ } else if (planContent) {
596
616
  taskCount = parseTaskCount(planContent)
597
- } else if (changeDir) {
598
- const planFile = path.join(changeDir, 'plan.md')
599
- if (existsSync(planFile)) {
600
- taskCount = parseTaskCount(readFileSync(planFile, 'utf8'))
601
- }
602
617
  }
618
+ // 兜底:注册表计数 0(写回前空窗/骨架未落)时回退入参 planContent 计数,防误走「无任务」分支
619
+ if (taskCount === 0 && planContent) taskCount = parseTaskCount(planContent)
603
620
 
604
621
  // 没有任务数则用固定步骤(兼容旧流程,无蓝图步骤无 postcheck)
605
622
  if (taskCount === 0) {
@@ -16,7 +16,7 @@ export const definition = {
16
16
  step 3 --done 审计会把 src 核心文件(CLI / 状态机 / 注入框架等危险清单)、新增文件、删除文件判为危险变更并拦截。若你**预判**本 quick 的修复目标本身就是这类文件,重新启动 quick 时带上 --force-baseline(受保护/危险文件)/ --allow-new(新增文件)/ --allow-delete(删除文件)预声明——flag 会持久化进会话守卫,后续 --done 直接生效,省一轮拦截重跑。不预带也可:step 3 拦截后按提示带 flag 重跑 --done 同样解锁。
17
17
 
18
18
  ### 🧭 文件预声明(多 agent 并发仓库推荐)
19
- --done 审计按「时间窗 diff」归属文件:quick 窗口内**并行会话**改的文件也会被算进本会话的 QUICKLOG「文件:」行。多个 agent 同时操作本仓库时,启动 quick 建议带 \`--files <a.js,b.js>\` 声明预计要改的文件——声明即归属:文件行只落声明命中的文件,窗口内未声明脏文件以「审计:」行落盘追溯(不混入、不静默丢)。不预判也行:--done 后按 QUICKLOG 核对说明手动精修文件行兜底。
19
+ --done 审计按「时间窗 diff」归属文件:quick 窗口内**并行会话**改的文件也会被算进本会话的 QUICKLOG「文件:」行。多个 agent 同时操作本仓库时,启动 quick 建议带 \`--files <a.js,b.js>\` 声明预计要改的文件——声明即归属:文件行只落声明命中的文件,窗口内未声明脏文件以「审计:」行落盘追溯(不混入、不静默丢)。不预判也行:--done 后按 QUICKLOG 核对说明手动精修文件行兜底。中途发现要改声明外文件(如新写的测试),带新增文件恢复会话即追加边界(追加不替换):\`sillyspec run quick --files <新增文件> --change <sessionId>\`。
20
20
 
21
21
  ### 操作
22
22
  1. 检查关联变更(\`<linked-changes>\`,逗号分隔的变更名列表;显示「(无)」= 不关联变更),确定记录方式
@@ -37,6 +37,8 @@ step 3 --done 审计会把 src 核心文件(CLI / 状态机 / 注入框架等
37
37
  - 在 \`{SPEC_ROOT}/quicklog/QUICKLOG-<git-user>.md\` 写入「进行中」条目(含关联变更与预估文件)
38
38
  - 对每个关联变更 \`<c>\`:在 \`{SPEC_ROOT}/changes/<c>/tasks.md\` 追加未勾选 task \`- [ ] <quicklog-id> <任务描述>\`
39
39
 
40
+ > 💡 启动时带 \`--input "<一句话任务描述>"\` 可让条目从第一分钟就有语义标题(平台「快速修复」列表默认隐藏进行中的「(quick 任务)」占位条目);未带时标题要到 step 3 --done 才从「需求:」回填。若你启动时漏带且任务较长,建议按 CLI 启动警告指引重启会话补带。
41
+
40
42
  **你不要创建或修改任何 QUICKLOG 的条目骨架 / tasks.md 记录**——ql-ID 分配、「进行中」条目、状态翻转、task 勾选、轮转全由 CLI 接管,你无需手写。<quicklog-id> 可用于 design.md / plan.md / archive / 模块变更索引引用。
41
43
 
42
44
  > ℹ️ step 3 --done 时,CLI 已落盘结构化 QUICKLOG 条目(标题从「需求:」提取——届时写一句语义化短标题,规则见 step3 结果摘要模板;正文四字段分行、文件行可用 --file-notes 写多行括注)。事后只需按需核对,详见 step 3「QUICKLOG 正文核对」。
@@ -98,7 +98,7 @@ export const definition = {
98
98
  },
99
99
  {
100
100
  name: '逐项检查任务',
101
- prompt: `对照 tasks.md 检查每个任务完成状态。
101
+ prompt: `对照 tasks.md(任务注册表唯一真相)检查每个任务完成状态。勾选由 execute 双路写入(agent 按 review gate 手动勾 + CLI autoCheckPlanFromReviews 机器勾选器按 review.json 自动勾),本阶段只读对照、不改勾。
102
102
 
103
103
  ### 操作
104
104
  对每个 checkbox: