sillyspec 3.26.10 → 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 (101) hide show
  1. package/.claude/skills/sillyspec-archive/SKILL.md +85 -85
  2. package/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -114
  3. package/.claude/skills/sillyspec-commit/SKILL.md +109 -109
  4. package/.claude/skills/sillyspec-continue/SKILL.md +45 -45
  5. package/.claude/skills/sillyspec-doctor/SKILL.md +76 -76
  6. package/.claude/skills/sillyspec-execute/SKILL.md +200 -200
  7. package/.claude/skills/sillyspec-explore/SKILL.md +95 -95
  8. package/.claude/skills/sillyspec-knowledge/SKILL.md +269 -269
  9. package/.claude/skills/sillyspec-plan/SKILL.md +148 -148
  10. package/.claude/skills/sillyspec-quick/SKILL.md +115 -111
  11. package/.claude/skills/sillyspec-resume/SKILL.md +68 -68
  12. package/.claude/skills/sillyspec-scan/SKILL.md +79 -79
  13. package/.claude/skills/sillyspec-state/SKILL.md +64 -64
  14. package/.claude/skills/sillyspec-status/SKILL.md +54 -54
  15. package/.claude/skills/sillyspec-verify/SKILL.md +83 -83
  16. package/.claude/skills/sillyspec-workspace/SKILL.md +169 -169
  17. package/.gitattributes +2 -0
  18. package/.husky/pre-push +20 -20
  19. package/README.md +203 -201
  20. package/SKILL.md +91 -90
  21. package/package.json +39 -39
  22. package/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -7446
  23. package/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -1
  24. package/packages/dashboard/dist/index.html +16 -16
  25. package/packages/dashboard/package.json +25 -25
  26. package/packages/dashboard/server/executor.js +95 -86
  27. package/packages/dashboard/server/index.js +663 -601
  28. package/packages/dashboard/server/parser.js +526 -526
  29. package/src/change-risk-profile.js +254 -254
  30. package/src/classify-change.js +109 -109
  31. package/src/config-schema.js +313 -310
  32. package/src/contract-matrix.js +388 -379
  33. package/src/db.js +370 -350
  34. package/src/dispatch/backends/local-agent.js +155 -155
  35. package/src/dispatch/backends/sillyhub-mcp.js +245 -245
  36. package/src/dispatch/probe.js +246 -210
  37. package/src/dispatch/strategy.js +121 -121
  38. package/src/docs-check.js +188 -16
  39. package/src/docs-debt.js +6 -2
  40. package/src/docs-gate.js +1 -0
  41. package/src/doctor-diagnostics.js +898 -711
  42. package/src/endpoint-extractor.js +401 -401
  43. package/src/fs-atomic.js +71 -69
  44. package/src/git-helper.js +82 -79
  45. package/src/hooks/claude-pre-tool-use.cjs +125 -125
  46. package/src/hooks/worktree-guard.js +854 -848
  47. package/src/index.js +251 -20
  48. package/src/init.js +624 -623
  49. package/src/knowledge-match.js +130 -130
  50. package/src/local-detect.js +128 -120
  51. package/src/machine-interface.js +543 -543
  52. package/src/migrate.js +127 -127
  53. package/src/modules.js +7 -5
  54. package/src/progress/change-registry.js +297 -260
  55. package/src/progress/consistency-doctor.js +355 -355
  56. package/src/progress/shared.js +36 -36
  57. package/src/progress/stage-machine.js +589 -550
  58. package/src/progress/step-store.js +261 -261
  59. package/src/progress.js +1285 -1127
  60. package/src/quicklog.js +724 -693
  61. package/src/review-tier.js +77 -77
  62. package/src/run/command.js +1408 -1364
  63. package/src/run/complete-handlers.js +1450 -1399
  64. package/src/run/complete.js +1181 -920
  65. package/src/run/gates.js +878 -755
  66. package/src/run/prompt.js +765 -761
  67. package/src/run/quick-audit.js +155 -146
  68. package/src/run/shared.js +1348 -1215
  69. package/src/run/stage.js +527 -479
  70. package/src/scan-postcheck.js +371 -371
  71. package/src/setup.js +414 -416
  72. package/src/sillyhub-mcp/client.js +597 -529
  73. package/src/spec-dir-typo.js +3 -24
  74. package/src/spec-sync.js +358 -319
  75. package/src/stage-contract.js +871 -897
  76. package/src/stage-review.js +549 -549
  77. package/src/stages/archive.js +156 -155
  78. package/src/stages/brainstorm-auto.js +236 -236
  79. package/src/stages/brainstorm.js +597 -597
  80. package/src/stages/cmd-existence.js +145 -145
  81. package/src/stages/doctor.js +386 -371
  82. package/src/stages/execute.js +1014 -1003
  83. package/src/stages/explore.js +34 -34
  84. package/src/stages/index.js +29 -29
  85. package/src/stages/knowledge.js +498 -498
  86. package/src/stages/plan-postcheck.js +1323 -1302
  87. package/src/stages/plan.js +642 -623
  88. package/src/stages/quick.js +153 -151
  89. package/src/stages/scan.js +607 -607
  90. package/src/stages/status.js +64 -64
  91. package/src/stages/verify.js +264 -264
  92. package/src/sync.js +1325 -1135
  93. package/src/task-review.js +1204 -1019
  94. package/src/taskcard.js +178 -0
  95. package/src/verify-postcheck.js +1199 -1186
  96. package/src/workflow.js +600 -600
  97. package/src/worktree-apply.js +1237 -1136
  98. package/src/worktree-deps.js +453 -353
  99. package/src/worktree.js +1527 -1481
  100. package/templates/workflows/archive-impact.yaml +79 -79
  101. package/templates/workflows/scan-docs.yaml +132 -132
@@ -0,0 +1,178 @@
1
+ /**
2
+ * sillyspec taskcard — 生成 Windows 安全的 TaskCard 骨架
3
+ *
4
+ * 背景:plan postcheck 任务卡被拒的根因集中在「LLM 子代理手写 frontmatter 不可靠」——
5
+ * CRLF 行尾、漏闭合 ---、缺 constraints 等硬校验字段(校验侧已对 CRLF 容错,但闭合/字段
6
+ * 缺失仍拦)。本命令从源头消灭这类格式错误:骨架由 CLI 用 writeFileSync 直写(字符串
7
+ * 只含 \n,天然 LF),frontmatter 闭合与硬校验 9 字段由代码保证,子代理只需 Edit 填充
8
+ * 占位符,不再手写整卡。
9
+ *
10
+ * 任务编号/标题从 plan.md checkbox 行自动带出(与 plan.js parseTaskNames 同源,
11
+ * 保编号不跳号、名称与 plan.md 一致)。
12
+ */
13
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
14
+ import { join } from 'path'
15
+ import { git } from './git-helper.js'
16
+ import { assertSafeChangeName, resolveSpecDir } from './run/shared.js'
17
+ import { parseTaskNames } from './stages/plan.js'
18
+
19
+ /**
20
+ * 任务号归一:接受 task-01 / task-1 / 01 / 1,统一为 task-NN(两位补零,与任务卡文件名一致)。
21
+ * @param {string} raw
22
+ * @returns {string} 形如 task-01
23
+ */
24
+ export function normalizeTaskId(raw) {
25
+ const s = String(raw || '').trim()
26
+ const m = s.match(/^(?:task-)?(\d+)$/i)
27
+ if (!m) {
28
+ throw new Error(`任务号格式非法: "${s}"(应为 task-NN 或 NN,可逗号分隔多个)`)
29
+ }
30
+ return `task-${String(m[1]).padStart(2, '0')}`
31
+ }
32
+
33
+ /**
34
+ * YAML 标量安全序列化(坑 taskcard-title-backtick-yaml,2026-08-20 实证):plan checkbox 行名
35
+ * 是自由文本,带反引号(如 "`-p` 参数支持")时裸插值进 frontmatter——`` ` `` 与 @ 是 YAML 保留
36
+ * 指示符,js-yaml 直接抛错,契约字段(allowed_paths/constraints 等)随整个 frontmatter 静默丢失,
37
+ * 靠 plan postcheck 才兜住。统一单引号包裹 + 内部 ' 双写(YAML 标准转义),无边界字符要判。
38
+ * @param {string} v
39
+ * @returns {string}
40
+ */
41
+ function yamlScalar(v) {
42
+ const s = String(v ?? '')
43
+ return `'${s.replace(/'/g, "''")}'`
44
+ }
45
+
46
+ /**
47
+ * 生成 TaskCard 骨架字符串(纯函数)。
48
+ *
49
+ * 硬校验 9 字段(plan-postcheck feasibility)+ 规范字段全部就位,占位符由子代理 Edit 填充;
50
+ * 可选字段(provides/expects_from/related_tests)默认不生成——缩小 YAML 出错面,何时加的
51
+ * 判据写在文件尾注释(规则详情见 templates/prompts/taskcard-rules.md)。
52
+ * 字符串只含 \n:writeFileSync 直写不经任何行尾转换,Windows 下天然 LF 安全。
53
+ * title/title_zh/author 经 yamlScalar 转义(自由文本防 YAML 指示符炸解析)。
54
+ *
55
+ * @param {{ taskId: string, title: string, titleZh: string, author: string, now: string }} p
56
+ * @returns {string}
57
+ */
58
+ export function buildTaskcardSkeleton({ taskId, title, titleZh, author, now }) {
59
+ return `---
60
+ id: ${taskId}
61
+ title: ${yamlScalar(title)}
62
+ title_zh: ${yamlScalar(titleZh)}
63
+ author: ${yamlScalar(author)}
64
+ created_at: ${now}
65
+ priority: P0
66
+ depends_on: []
67
+ blocks: []
68
+ requirement_ids: [FR-XX]
69
+ decision_ids: [D-XXX@vN]
70
+ allowed_paths:
71
+ - src/example/file.ts
72
+ goal: >
73
+ 一句话说明这个 task 要做什么、为什么。
74
+ implementation:
75
+ - 具体步骤 1
76
+ - 具体步骤 2
77
+ acceptance:
78
+ - 可验证的验收条件 1
79
+ - 可验证的验收条件 2
80
+ verify:
81
+ - cd frontend && pnpm exec tsc --noEmit
82
+ constraints:
83
+ - 边界约束 1(如:不加测试)
84
+ - 边界约束 2(如:不修改传入参数)
85
+ ---
86
+
87
+ <!-- 骨架由 sillyspec taskcard 生成(LF 行尾 + frontmatter 已闭合 + 硬校验 9 字段齐全)。
88
+ 用 Edit tool 填充上方占位符(allowed_paths/goal/implementation/acceptance/verify/constraints 等),
89
+ 勿用 Write 整文件重写——会引入 CRLF 行尾/漏闭合 ---/漏字段回归。
90
+ 可选字段按需插进上方 frontmatter(规则见 taskcard-rules):
91
+ provides: 仅当本 task 给其他 task 提供接口/DTO/响应时填
92
+ expects_from: 仅当本 task 消费其他 task 的契约时填
93
+ related_tests: 仅当本 task 改动导致既有测试断言失效时填(测试路径须同时进 allowed_paths) -->
94
+ `
95
+ }
96
+
97
+ /**
98
+ * taskcard 命令主逻辑:定位变更目录 → 解析任务清单(--task 显式列表 / 'all' 从 plan.md 取)→
99
+ * 逐个写骨架。
100
+ *
101
+ * @param {string} changeName 变更名(.sillyspec/changes/ 下目录名)
102
+ * @param {{ cwd: string, specDir?: string|null, taskIds: string[]|'all', title?: string|null,
103
+ * titleZh?: string|null, force?: boolean }} opts
104
+ * taskIds:normalizeTaskId 归一后的数组;或字面 'all' 表示从 plan.md 取全部 checkbox 任务
105
+ * @returns {{ created: string[], skipped: string[], tasksDir: string }}
106
+ */
107
+ export function cmdTaskcard(changeName, opts = {}) {
108
+ const { cwd, specDir = null, taskIds, title = null, titleZh = null, force = false } = opts
109
+ assertSafeChangeName(changeName, '变更名')
110
+
111
+ const changeDir = join(resolveSpecDir(cwd, { specDir }), 'changes', changeName)
112
+ if (!existsSync(changeDir)) {
113
+ throw new Error(`变更目录不存在: ${changeDir}(先完成 plan 前置步骤,或检查 --change 名)`)
114
+ }
115
+
116
+ // 任务注册表 tasks.md(2026-08-20-task-truth-unify 唯一真相;CRLF 归一后解析,
117
+ // --all 时强依赖)。tasks.md 缺失回退 plan.md(旧归档变更兼容读侧)
118
+ let planTasks = []
119
+ const tasksMdPath = join(changeDir, 'tasks.md')
120
+ const planPath = join(changeDir, 'plan.md')
121
+ const registryPath = existsSync(tasksMdPath) ? tasksMdPath : planPath
122
+ if (existsSync(registryPath)) {
123
+ const registryContent = readFileSync(registryPath, 'utf8').replace(/\r\n/g, '\n')
124
+ planTasks = parseTaskNames(registryContent)
125
+ }
126
+
127
+ let ids
128
+ if (taskIds === 'all') {
129
+ if (planTasks.length === 0) {
130
+ throw new Error(
131
+ `--all 需要 tasks.md 中存在 checkbox 任务行(格式 "- [ ] task-XX: 任务名")` +
132
+ (existsSync(registryPath) ? ',当前任务清单未解析到任何任务行' : ',且变更目录下未找到 tasks.md/plan.md')
133
+ )
134
+ }
135
+ ids = planTasks.map(t => `task-${t.num}`)
136
+ } else if (Array.isArray(taskIds) && taskIds.length > 0) {
137
+ ids = taskIds
138
+ } else {
139
+ throw new Error('缺少任务清单:需 --task task-01[,task-02...] 或 --all')
140
+ }
141
+
142
+ // 标题优先级:--title/--title-zh 参数 > plan.md checkbox 行名称 > 占位符。
143
+ // 参数仅单任务时可用(多任务各卡标题不同,统一参数必错)。
144
+ if ((title || titleZh) && ids.length > 1) {
145
+ throw new Error('--title/--title-zh 仅在 --task 指定单个任务时可用(多任务标题自动取自 plan.md checkbox 行)')
146
+ }
147
+
148
+ const author = (() => {
149
+ try {
150
+ // QUAL-01 收口:原 execSync 字符串拼接(经 shell,违反自家注入规约)→ git-helper 数组形式
151
+ return git(cwd, ['config', 'user.name']) || 'unknown'
152
+ } catch {
153
+ return 'unknown'
154
+ }
155
+ })()
156
+ const now = new Date().toISOString().slice(0, 19).replace('T', ' ')
157
+
158
+ const tasksDir = join(changeDir, 'tasks')
159
+ mkdirSync(tasksDir, { recursive: true })
160
+
161
+ const created = []
162
+ const skipped = []
163
+ for (const id of ids) {
164
+ const planMatch = planTasks.find(t => `task-${t.num}` === id)
165
+ const finalTitle = title || (planMatch ? planMatch.name : null) || `${id} fill-english-title`
166
+ const finalTitleZh = titleZh || (planMatch ? planMatch.name : null) || finalTitle
167
+ const filePath = join(tasksDir, `${id}.md`)
168
+ if (existsSync(filePath) && !force) {
169
+ skipped.push(filePath)
170
+ continue
171
+ }
172
+ writeFileSync(filePath, buildTaskcardSkeleton({
173
+ taskId: id, title: finalTitle, titleZh: finalTitleZh, author, now,
174
+ }), 'utf8')
175
+ created.push(filePath)
176
+ }
177
+ return { created, skipped, tasksDir }
178
+ }