sillyspec 3.31.0 → 3.32.0

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 (74) hide show
  1. package/.claude/skills/sillyspec-auto/SKILL.md +1 -1
  2. package/.claude/skills/sillyspec-brainstorm/SKILL.md +2 -2
  3. package/.claude/skills/sillyspec-commit/SKILL.md +1 -1
  4. package/.claude/skills/sillyspec-flow/SKILL.md +10 -6
  5. package/.claude/skills/sillyspec-quick/SKILL.md +105 -7
  6. package/.zcodeignore +66 -0
  7. package/AGENTS.md +16 -16
  8. package/assets/command-cards/flow.md +9 -3
  9. package/assets/command-cards/run-quick.md +4 -1
  10. package/package.json +2 -2
  11. package/src/agent-session-log.js +27 -5
  12. package/src/config-schema.js +21 -0
  13. package/src/datetime.js +51 -0
  14. package/src/db.js +10 -3
  15. package/src/decision-distill.js +21 -12
  16. package/src/design-facts.js +46 -6
  17. package/src/flow-draft.js +468 -95
  18. package/src/flow-parity.js +62 -4
  19. package/src/flow-review.js +136 -6
  20. package/src/flow.js +729 -125
  21. package/src/foreign-declared.js +71 -3
  22. package/src/fr-index.js +148 -162
  23. package/src/git-helper.js +16 -3
  24. package/src/hooks/worktree-guard.js +16 -5
  25. package/src/hunk-attribution.js +28 -5
  26. package/src/index.js +186 -19
  27. package/src/knowledge-classify.js +3 -1
  28. package/src/knowledge-digest.js +55 -26
  29. package/src/knowledge-match.js +135 -7
  30. package/src/knowledge-stats.js +66 -4
  31. package/src/knowledge-vector.js +160 -0
  32. package/src/machine-interface.js +90 -2
  33. package/src/module-impact.js +22 -1
  34. package/src/modules.js +9 -0
  35. package/src/progress/change-registry.js +20 -0
  36. package/src/progress/stage-machine.js +4 -7
  37. package/src/progress.js +8 -1
  38. package/src/quicklog.js +20 -0
  39. package/src/redomain.js +23 -7
  40. package/src/route-hindsight.js +276 -0
  41. package/src/run/archive-distill.js +3 -1
  42. package/src/run/command.js +65 -12
  43. package/src/run/complete-handlers.js +219 -5
  44. package/src/run/complete.js +77 -11
  45. package/src/run/gate-snapshot.js +15 -0
  46. package/src/run/gates.js +60 -4
  47. package/src/run/prompt.js +159 -31
  48. package/src/run/quick-audit.js +66 -7
  49. package/src/run/shared.js +6 -11
  50. package/src/run/stage.js +5 -1
  51. package/src/run/verify-quality-scan.js +84 -0
  52. package/src/scan-facts.js +2 -1
  53. package/src/scope-audit.js +9 -2
  54. package/src/sentinel-assertions.js +67 -16
  55. package/src/stage-contract-engine.js +4 -1
  56. package/src/stage-contract-spec.js +103 -4
  57. package/src/stage-contract.js +55 -7
  58. package/src/stages/brainstorm.js +27 -14
  59. package/src/stages/knowledge.js +4 -3
  60. package/src/stages/plan-postcheck.js +98 -3
  61. package/src/stages/plan.js +22 -2
  62. package/src/stages/quick.js +1 -1
  63. package/src/stages/verify.js +2 -1
  64. package/src/task-review.js +1 -1
  65. package/src/task-tick.js +121 -0
  66. package/src/taskcard-frontmatter.js +31 -0
  67. package/src/ui-visual.js +43 -8
  68. package/src/verify-postcheck.js +509 -37
  69. package/src/verify-probes.js +216 -5
  70. package/src/watcher.js +1067 -1031
  71. package/src/worktree-apply.js +9 -1
  72. package/src/worktree.js +21 -6
  73. package/templates/agents-instruction.md +15 -15
  74. package/templates/prompts/taskcard-rules.md +1 -0
@@ -195,20 +195,29 @@ export function parseModulePathsSubset(content) {
195
195
  let cur = null
196
196
  let key = null
197
197
  for (const line of content.replace(/\r\n/g, '\n').split('\n')) {
198
+ // 空行/注释行:保状态(列表块内允许空行与注释穿插)
199
+ if (line.trim() === '' || line.trim().startsWith('#')) continue
198
200
  const mm = line.match(/^ ([a-zA-Z0-9_-]+):$/)
199
201
  if (mm) { cur = mm[1]; modules[cur] = { paths: [] }; key = null; continue }
200
202
  if (!cur) continue
203
+ const item = line.match(/^ - (.+)$/)
204
+ if (item && key) { modules[cur][key].push(item[1].trim().replace(/^['"]|['"]$/g, '')); continue }
201
205
  const inline = line.match(/^ (paths|core_files): \[(.*)\]$/)
202
206
  if (inline) {
203
207
  modules[cur][inline[1]] = inline[2].split(',').map(s => s.trim().replace(/^['"]|['"]$/g, '')).filter(Boolean)
204
208
  key = null; continue
205
209
  }
206
- const block = line.match(/^ (paths|core_files):$/)
210
+ const block = line.match(/^ (paths|core_files):\s*$/)
207
211
  if (block) { key = block[1]; modules[cur][key] = modules[cur][key] || []; continue }
208
212
  const doc = line.match(/^ doc: (.+)$/)
209
213
  if (doc) { modules[cur].doc = doc[1].trim().replace(/^['"]|['"]$/g, ''); key = null; continue }
210
- const item = line.match(/^ - (.+)$/)
211
- if (item && key) modules[cur][key].push(item[1].trim().replace(/^['"]|['"]$/g, ''))
214
+ // 结构守卫(坑 module-map-list-leak,2026-10-06-wallclock-entry 实测发现):list 收集只在
215
+ // 「6 缩进项行 / 空行 / 注释」上存活,其余任何行——字段头(字段名开放集不枚举)、顶层段
216
+ // (span_risk:/blast:)、异缩进项(2 缩进 "- prefixes:")——终结收集。此前 paths: 块后的
217
+ // tags/aliases/entrypoints/depends_on 字段项与顶层 blast prefixes 全部漏进 paths(8 项目
218
+ // map 500 实声明 vs 1375 泄漏项;散文斜杠词 git/DB/JSON 前缀命中 tag 'git' 误路由触达域、
219
+ // bin 模块双重归属 src/db.js)。
220
+ key = null
212
221
  }
213
222
  return modules
214
223
  }
@@ -518,17 +527,17 @@ export function distillIntoKnowledge(changeDir, knowledgeRoot, headHash, moduleI
518
527
  const waitParts = []
519
528
  const distillable = []
520
529
  for (const e of selected) {
521
- if (e.selected === 'rejected' && (!e.rejectReason || !e.revisitWhen)) {
522
- const missing = []
523
- if (!e.rejectReason) missing.push('否决理由')
524
- if (!e.revisitWhen) missing.push('复潮条件')
525
- waitParts.push(`${e.id} 缺${missing.join('、')}`)
526
- } else {
527
- distillable.push(e)
528
- }
530
+ // 标题必填(2026-09-29-decision-route-vocab:裸号条目检索面不可见——路由词形派生与回显
531
+ // 排序都吃标题,缺标题=知识入库即隐身);rejected 缺否决理由/复潮条件 → 不写盘(其余照常)
532
+ const missing = []
533
+ if (!e.title) missing.push('标题(## D-xxx@vN 后接短标题——检索词形来源,裸号条目入库即隐身)')
534
+ if (e.selected === 'rejected' && !e.rejectReason) missing.push('否决理由')
535
+ if (e.selected === 'rejected' && !e.revisitWhen) missing.push('复潮条件')
536
+ if (missing.length > 0) waitParts.push(`${e.id} 缺${missing.join('、')}`)
537
+ else distillable.push(e)
529
538
  }
530
539
  const needsWait = waitParts.length > 0
531
- ? `${waitParts.join(';')}——rejected 条目必填,补齐后重跑提炼`
540
+ ? `${waitParts.join(';')}——条目必填字段缺失,补齐后重跑提炼`
532
541
  : null
533
542
  if (distillable.length === 0) return { written: [], skipped: null, needsWait }
534
543
 
@@ -22,7 +22,7 @@
22
22
  * decision_module_check_skipped(skipped 原因字符串,接线层包 code)。
23
23
  */
24
24
  import { existsSync, readFileSync, readdirSync } from 'node:fs'
25
- import { join } from 'node:path'
25
+ import { join, relative } from 'node:path'
26
26
  import { parseDecisions } from './decision-distill.js'
27
27
  import { parseFileChangeListDetailed } from './change-list.js'
28
28
  import { parseDesignCoverageByRepo, parseRepoRegistry } from './stages/plan-postcheck.js'
@@ -295,7 +295,9 @@ export function generateDesignSkeleton({ changeName, decisionsText, author, now
295
295
  // P2-e provenance 戳(noai-ir-roadmap §5):CLI 骨架出品的可审计标记——铁律 8 收窄为
296
296
  //「骨架优先,仅手写补文档才手填元数据」的依据(validateMetadata 未来可据此只认 CLI 戳)
297
297
  lines.push(`generated_by: sillyspec-design-init`)
298
- lines.push('scale: large')
298
+ // scale 不预填(2026-09-29-brainstorm-exit-thin-default):此前预填 large 是静默厚默认——
299
+ // 不主动改就是五阶段;现由 brainstorm Step 8 规模评估按复杂度/上下文轴落值,拿不准默认 small
300
+ lines.push('scale: "" # TODO:Step 8 规模评估落值——单上下文可吞吐=small(flow start 收编);需 Wave 编排/上下文分片/多阶段治理=large(run plan);拿不准 small')
299
301
  lines.push('---')
300
302
  lines.push('')
301
303
  lines.push(`# 设计文档(Design)— ${change}`)
@@ -339,7 +341,8 @@ export function generateDesignSkeleton({ changeName, decisionsText, author, now
339
341
 
340
342
  lines.push('## 接口定义')
341
343
  lines.push('')
342
- lines.push('<!-- TODO(代码类任务必填):方法签名、数据结构 -->')
344
+ lines.push('<!-- TODO(代码类任务必填):方法签名、数据结构。本变更无接口改动时,把下面这行声明粘贴到本节正文(探针矩阵认该声明行,同义式「无接口变更/不涉及接口/零端点」亦可;留在本注释内不生效——2026-10-06-verify-friction-fix): -->')
345
+ lines.push('<!-- 本变更接口面:0 端点(无接口变更) -->')
343
346
  lines.push('')
344
347
 
345
348
  lines.push('## 生命周期契约表')
@@ -415,14 +418,47 @@ function stripPathPlaceholders(p) {
415
418
  * @param {{ changeDir: string, cwd: string }} opts
416
419
  * @returns {{ ok: boolean, errors: Array<{path, message}>, warnings: string[] }}
417
420
  */
421
+ /**
422
+ * 相近既有路径建议(2026-10-07-flow-friction-batch3 task-02):design 清单幻觉路径的
423
+ * basename did-you-mean。受控 BFS:跳过 node_modules/.git/dist/build/venv 类重目录与
424
+ * 隐藏目录,扫描量上限 8000 文件(超限即止——建议是锦上添花不是承诺);只收 basename
425
+ * 全等命中,至多 3 条按路径长度升序(最短=最像根级缺前缀的正确答案)。纯读无副作用。
426
+ * @param {string} root 仓根(或跨仓根)
427
+ * @param {string} normalized 失效路径(已剥占位符)
428
+ * @returns {string[]} 仓根相对 posix 路径建议(≤3 条,零命中返回 [])
429
+ */
430
+ function suggestClosePaths(root, normalized) {
431
+ const base = normalized.split('/').pop().toLowerCase()
432
+ if (!base || base.length < 3) return []
433
+ const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', 'out', 'venv', '.venv', '__pycache__', '.runtime', 'coverage', 'target', '.sillyspec'])
434
+ const hits = []
435
+ let scanned = 0
436
+ const queue = [root]
437
+ while (queue.length > 0 && hits.length < 3 && scanned < 8000) {
438
+ const dir = queue.shift()
439
+ let entries
440
+ try { entries = readdirSync(dir, { withFileTypes: true }) } catch { continue }
441
+ for (const e of entries) {
442
+ if (e.name.startsWith('.') || SKIP_DIRS.has(e.name)) continue
443
+ const abs = join(dir, e.name)
444
+ if (e.isDirectory()) { queue.push(abs); continue }
445
+ scanned++
446
+ if (e.name.toLowerCase() === base) {
447
+ hits.push(relative(root, abs).split('\\').join('/'))
448
+ if (hits.length >= 3) break
449
+ }
450
+ }
451
+ }
452
+ return hits.sort((a, b) => a.length - b.length)
453
+ }
454
+
418
455
  export function validateDesignFileList({ changeDir, cwd } = {}) {
419
456
  const errors = []
420
457
  const warnings = []
421
458
  try {
422
459
  if (!changeDir || !cwd) {
423
460
  return { ok: true, errors, warnings: ['design 清单核验跳过:缺少 changeDir/cwd'] }
424
- }
425
- const designPath = join(changeDir, 'design.md')
461
+ } const designPath = join(changeDir, 'design.md')
426
462
  if (!existsSync(designPath)) {
427
463
  return { ok: true, errors, warnings: ['design 清单核验跳过:design.md 不存在'] }
428
464
  }
@@ -440,9 +476,13 @@ export function validateDesignFileList({ changeDir, cwd } = {}) {
440
476
  const normalized = stripPathPlaceholders(rawPath)
441
477
  if (!normalized || normalized === '.') continue
442
478
  if (!existsSync(join(root, normalized))) {
479
+ // 相近路径指路(2026-10-07-flow-friction-batch3 task-02):幻觉路径多为缺目录前缀
480
+ // (如漏 backend/)或近似书写——basename did-you-mean 把「三轮试错」压成「一眼改对」;
481
+ // 无相近命中不附建议段(不加噪)
482
+ const close = suggestClosePaths(root, normalized)
443
483
  errors.push({
444
484
  path: rawPath,
445
- message: `design_file_ref_invalid:文件变更清单条目「${rawPath}」在${repoLabel}(${root})下不存在且无 NEW: 前缀(幻觉路径/书写错误)——修正路径,或计划新建的文件改为 NEW:${rawPath} 前缀`,
485
+ message: `design_file_ref_invalid:文件变更清单条目「${rawPath}」在${repoLabel}(${root})下不存在且无 NEW: 前缀(幻觉路径/书写错误)——修正路径,或计划新建的文件改为 NEW:${rawPath} 前缀${close.length > 0 ? `;相近既有路径:${close.join('、')}` : ''}`,
446
486
  })
447
487
  }
448
488
  }