sillyspec 3.26.9 → 3.26.11

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 (98) 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 +111 -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 -81
  16. package/.claude/skills/sillyspec-workspace/SKILL.md +169 -169
  17. package/.gitattributes +2 -0
  18. package/.husky/pre-push +20 -20
  19. package/.npmrc-publish +2 -0
  20. package/README.md +201 -201
  21. package/SKILL.md +90 -90
  22. package/package.json +39 -39
  23. package/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -7446
  24. package/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -1
  25. package/packages/dashboard/dist/index.html +16 -16
  26. package/packages/dashboard/package.json +25 -25
  27. package/packages/dashboard/server/executor.js +86 -86
  28. package/packages/dashboard/server/index.js +601 -601
  29. package/packages/dashboard/server/parser.js +526 -526
  30. package/src/change-risk-profile.js +254 -254
  31. package/src/classify-change.js +109 -109
  32. package/src/config-schema.js +310 -310
  33. package/src/contract-matrix.js +379 -379
  34. package/src/db.js +350 -350
  35. package/src/dispatch/backends/local-agent.js +155 -155
  36. package/src/dispatch/backends/sillyhub-mcp.js +245 -245
  37. package/src/dispatch/probe.js +210 -210
  38. package/src/dispatch/strategy.js +121 -121
  39. package/src/docs-check.js +131 -4
  40. package/src/doctor-diagnostics.js +711 -711
  41. package/src/endpoint-extractor.js +401 -401
  42. package/src/fs-atomic.js +69 -69
  43. package/src/git-helper.js +79 -79
  44. package/src/hooks/claude-pre-tool-use.cjs +125 -125
  45. package/src/hooks/worktree-guard.js +848 -848
  46. package/src/index.js +90 -8
  47. package/src/init.js +623 -623
  48. package/src/knowledge-match.js +130 -130
  49. package/src/local-detect.js +120 -120
  50. package/src/machine-interface.js +543 -543
  51. package/src/migrate.js +127 -127
  52. package/src/progress/change-registry.js +297 -260
  53. package/src/progress/consistency-doctor.js +355 -355
  54. package/src/progress/shared.js +36 -36
  55. package/src/progress/stage-machine.js +29 -1
  56. package/src/progress/step-store.js +261 -261
  57. package/src/progress.js +1131 -1127
  58. package/src/quicklog.js +693 -675
  59. package/src/review-tier.js +77 -77
  60. package/src/run/command.js +1385 -1364
  61. package/src/run/complete-handlers.js +198 -9
  62. package/src/run/complete.js +225 -24
  63. package/src/run/concurrent-detect.js +22 -0
  64. package/src/run/gates.js +755 -755
  65. package/src/run/prompt.js +761 -721
  66. package/src/run/quick-audit.js +155 -146
  67. package/src/run/shared.js +1268 -1197
  68. package/src/run/stage.js +487 -479
  69. package/src/scan-postcheck.js +371 -371
  70. package/src/setup.js +416 -416
  71. package/src/sillyhub-mcp/client.js +529 -529
  72. package/src/spec-sync.js +323 -0
  73. package/src/stage-contract.js +897 -897
  74. package/src/stage-review.js +549 -549
  75. package/src/stages/archive.js +155 -154
  76. package/src/stages/brainstorm-auto.js +236 -236
  77. package/src/stages/brainstorm.js +596 -597
  78. package/src/stages/cmd-existence.js +145 -145
  79. package/src/stages/doctor.js +371 -371
  80. package/src/stages/execute.js +1003 -991
  81. package/src/stages/explore.js +34 -34
  82. package/src/stages/index.js +29 -29
  83. package/src/stages/knowledge.js +498 -498
  84. package/src/stages/plan-postcheck.js +1302 -1281
  85. package/src/stages/plan.js +625 -604
  86. package/src/stages/quick.js +151 -143
  87. package/src/stages/scan.js +606 -605
  88. package/src/stages/status.js +64 -64
  89. package/src/stages/verify.js +264 -261
  90. package/src/sync.js +1223 -1125
  91. package/src/task-review.js +1019 -1019
  92. package/src/verify-postcheck.js +1186 -1186
  93. package/src/workflow.js +600 -600
  94. package/src/worktree-apply.js +1213 -1136
  95. package/src/worktree-deps.js +353 -353
  96. package/src/worktree.js +1481 -1437
  97. package/templates/workflows/archive-impact.yaml +79 -79
  98. package/templates/workflows/scan-docs.yaml +132 -132
@@ -1,145 +1,145 @@
1
- /**
2
- * cmd-existence.js — 共享命令存在性校验 helper(H2)
3
- *
4
- * 抽自 scan-postcheck.js:118-158 的 `npm run <script>` 存在性判定,扩展到 pnpm/yarn,
5
- * 并感知 monorepo 子目录(`cd <subdir> &&` 前缀 或 local.yaml `modules` 块定位)。
6
- *
7
- * 严重度由调用方决定(D-04 同 helper 双严重度):
8
- * - scan-postcheck 调用时维持 warning(local.yaml 命令误报不阻断 init)
9
- * - plan-postcheck 调用时升 error(plan 阶段命令更结构化可硬阻断)
10
- *
11
- * 仅校验 `npm|pnpm|yarn run <script>` 这一类可静态对账的命令;
12
- * install / typecheck / npx / uv run 等直接包管理器调用不在范围(沿用 scan-postcheck:126-129 注释立场)。
13
- */
14
-
15
- import { existsSync, readFileSync } from 'fs'
16
- import { join } from 'path'
17
-
18
- // 提取 `npm|pnpm|yarn run <script>` 命令。
19
- // 对齐 scan-postcheck:136 的 `/npm run (\S+)/g`,扩展包管理器 + 容忍多空白。
20
- // 捕获组 1 = 包管理器(npm|pnpm|yarn),捕获组 2 = script 名。
21
- const SCRIPT_CMD_RE = /(npm|pnpm|yarn)\s+run\s+(\S+)/g
22
-
23
- // 识别紧贴命令前的 `cd <subdir> &&` 前缀(anchored at end of the slice before the command)。
24
- // subdir token 不含空白与 & ; | 等分隔符;末尾的 `&&` 可带任意空白后接命令。
25
- const CD_PREFIX_RE = /cd\s+([^\s&;|()]+)\s*&&\s*$/
26
-
27
- /**
28
- * 读取 package.json 的 scripts 字段。
29
- * @param {string} pkgPath
30
- * @returns {{ status: 'ok'|'missing'|'unreadable'|'no-scripts', scripts: object|null }}
31
- * - ok : 文件存在且 JSON 解析成功,含 scripts 对象
32
- * - missing : 文件不存在
33
- * - unreadable : 文件存在但 JSON 解析失败(与 scan-postcheck:147 try/catch 一致,不抛)
34
- * - no-scripts : 解析成功但无 scripts 字段(视作 script 不存在)
35
- */
36
- function readScripts(pkgPath) {
37
- if (!existsSync(pkgPath)) return { status: 'missing', scripts: null }
38
- try {
39
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
40
- if (pkg && typeof pkg === 'object' && pkg.scripts && typeof pkg.scripts === 'object') {
41
- return { status: 'ok', scripts: pkg.scripts }
42
- }
43
- return { status: 'no-scripts', scripts: null }
44
- } catch {
45
- return { status: 'unreadable', scripts: null }
46
- }
47
- }
48
-
49
- /**
50
- * 为单条命令计算候选 package.json 路径列表。
51
- *
52
- * 规则(blueprint task-02 + design §5 H2):
53
- * - 有 `cd <subdir> &&` 前缀 → 仅查 `<projectRoot>/<subdir>/package.json`(cd 锁定子目录,不回退根)
54
- * - 无前缀 + modules 块提供 → 查每个 module.path 的子包 + 根 package.json(多候选任一命中即视为存在)
55
- * - 无前缀 + 无 modules → 仅查 `<projectRoot>/package.json`
56
- *
57
- * @param {string} text 全文
58
- * @param {number} matchIndex 当前命令在 text 中的起始位置
59
- * @param {string} projectRoot
60
- * @param {object|null|undefined} modules local.yaml modules 块({ name: { path, test } })
61
- * @returns {string[]} 候选 package.json 绝对路径列表(至少 1 个)
62
- */
63
- function resolveCandidates(text, matchIndex, projectRoot, modules) {
64
- const before = text.slice(0, matchIndex)
65
- const cdMatch = before.match(CD_PREFIX_RE)
66
- if (cdMatch) {
67
- const subdir = cdMatch[1].replace(/^["']|["']$/g, '')
68
- return [join(projectRoot, subdir, 'package.json')]
69
- }
70
- if (modules && typeof modules === 'object') {
71
- const candidates = []
72
- for (const [, mod] of Object.entries(modules)) {
73
- if (mod && typeof mod === 'object' && typeof mod.path === 'string' && mod.path.trim() !== '') {
74
- candidates.push(join(projectRoot, mod.path, 'package.json'))
75
- }
76
- }
77
- candidates.push(join(projectRoot, 'package.json'))
78
- return candidates
79
- }
80
- return [join(projectRoot, 'package.json')]
81
- }
82
-
83
- /**
84
- * 校验文本中所有 `npm|pnpm|yarn run <script>` 命令在 package.json scripts 中存在(monorepo 感知)。
85
- *
86
- * @param {string} text 待校验文本(local.yaml 内容 / TaskCard verify+implementation 字段等)
87
- * @param {{ projectRoot: string, modules?: object|null }} opts
88
- * - projectRoot : 项目根目录绝对路径
89
- * - modules : local.yaml modules 块(可选),结构如 `{ frontend: { path: "frontend/", test: "..." } }`
90
- * @returns {{ invalid: Array<{ cmd: string, reason: string }>, checked: number }}
91
- * - invalid : 找不到 script 的命令列表,每项含 { cmd, reason };reason 区分
92
- * 「package.json 不存在」「package.json 解析失败」「package.json 无 <script> script」
93
- * - checked : 从 text 提取到的命令总数(含合法与非法)
94
- */
95
- export function validateScriptCommands(text, { projectRoot, modules } = {}) {
96
- const invalid = []
97
- if (!text || typeof text !== 'string') {
98
- return { invalid, checked: 0 }
99
- }
100
-
101
- const matches = [...text.matchAll(SCRIPT_CMD_RE)]
102
- const checked = matches.length
103
-
104
- for (const m of matches) {
105
- const pm = m[1]
106
- const scriptName = m[2]
107
- const cmd = `${pm} run ${scriptName}`
108
-
109
- const candidates = resolveCandidates(text, m.index ?? 0, projectRoot, modules)
110
-
111
- let found = false
112
- let existedAny = false // 至少一个候选文件存在且可解析(ok 或 no-scripts)
113
- let parseFailedAny = false // 至少一个候选文件解析失败(unreadable)
114
- for (const pkgPath of candidates) {
115
- const result = readScripts(pkgPath)
116
- if (result.status === 'ok') {
117
- existedAny = true
118
- if (Object.prototype.hasOwnProperty.call(result.scripts, scriptName)) {
119
- found = true
120
- break
121
- }
122
- } else if (result.status === 'no-scripts') {
123
- existedAny = true
124
- // scripts 字段缺失 → 该候选肯定不含此 script,继续看其他候选
125
- } else if (result.status === 'unreadable') {
126
- parseFailedAny = true
127
- }
128
- // missing → 既不算 existed 也不算 parseFailed
129
- }
130
-
131
- if (!found) {
132
- let reason
133
- if (!existedAny && parseFailedAny) {
134
- reason = `package.json 解析失败: ${candidates.join(', ')}`
135
- } else if (!existedAny) {
136
- reason = `package.json 不存在: ${candidates.join(', ')}`
137
- } else {
138
- reason = `package.json 无 ${scriptName} script`
139
- }
140
- invalid.push({ cmd, reason })
141
- }
142
- }
143
-
144
- return { invalid, checked }
145
- }
1
+ /**
2
+ * cmd-existence.js — 共享命令存在性校验 helper(H2)
3
+ *
4
+ * 抽自 scan-postcheck.js:118-158 的 `npm run <script>` 存在性判定,扩展到 pnpm/yarn,
5
+ * 并感知 monorepo 子目录(`cd <subdir> &&` 前缀 或 local.yaml `modules` 块定位)。
6
+ *
7
+ * 严重度由调用方决定(D-04 同 helper 双严重度):
8
+ * - scan-postcheck 调用时维持 warning(local.yaml 命令误报不阻断 init)
9
+ * - plan-postcheck 调用时升 error(plan 阶段命令更结构化可硬阻断)
10
+ *
11
+ * 仅校验 `npm|pnpm|yarn run <script>` 这一类可静态对账的命令;
12
+ * install / typecheck / npx / uv run 等直接包管理器调用不在范围(沿用 scan-postcheck:126-129 注释立场)。
13
+ */
14
+
15
+ import { existsSync, readFileSync } from 'fs'
16
+ import { join } from 'path'
17
+
18
+ // 提取 `npm|pnpm|yarn run <script>` 命令。
19
+ // 对齐 scan-postcheck:136 的 `/npm run (\S+)/g`,扩展包管理器 + 容忍多空白。
20
+ // 捕获组 1 = 包管理器(npm|pnpm|yarn),捕获组 2 = script 名。
21
+ const SCRIPT_CMD_RE = /(npm|pnpm|yarn)\s+run\s+(\S+)/g
22
+
23
+ // 识别紧贴命令前的 `cd <subdir> &&` 前缀(anchored at end of the slice before the command)。
24
+ // subdir token 不含空白与 & ; | 等分隔符;末尾的 `&&` 可带任意空白后接命令。
25
+ const CD_PREFIX_RE = /cd\s+([^\s&;|()]+)\s*&&\s*$/
26
+
27
+ /**
28
+ * 读取 package.json 的 scripts 字段。
29
+ * @param {string} pkgPath
30
+ * @returns {{ status: 'ok'|'missing'|'unreadable'|'no-scripts', scripts: object|null }}
31
+ * - ok : 文件存在且 JSON 解析成功,含 scripts 对象
32
+ * - missing : 文件不存在
33
+ * - unreadable : 文件存在但 JSON 解析失败(与 scan-postcheck:147 try/catch 一致,不抛)
34
+ * - no-scripts : 解析成功但无 scripts 字段(视作 script 不存在)
35
+ */
36
+ function readScripts(pkgPath) {
37
+ if (!existsSync(pkgPath)) return { status: 'missing', scripts: null }
38
+ try {
39
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
40
+ if (pkg && typeof pkg === 'object' && pkg.scripts && typeof pkg.scripts === 'object') {
41
+ return { status: 'ok', scripts: pkg.scripts }
42
+ }
43
+ return { status: 'no-scripts', scripts: null }
44
+ } catch {
45
+ return { status: 'unreadable', scripts: null }
46
+ }
47
+ }
48
+
49
+ /**
50
+ * 为单条命令计算候选 package.json 路径列表。
51
+ *
52
+ * 规则(blueprint task-02 + design §5 H2):
53
+ * - 有 `cd <subdir> &&` 前缀 → 仅查 `<projectRoot>/<subdir>/package.json`(cd 锁定子目录,不回退根)
54
+ * - 无前缀 + modules 块提供 → 查每个 module.path 的子包 + 根 package.json(多候选任一命中即视为存在)
55
+ * - 无前缀 + 无 modules → 仅查 `<projectRoot>/package.json`
56
+ *
57
+ * @param {string} text 全文
58
+ * @param {number} matchIndex 当前命令在 text 中的起始位置
59
+ * @param {string} projectRoot
60
+ * @param {object|null|undefined} modules local.yaml modules 块({ name: { path, test } })
61
+ * @returns {string[]} 候选 package.json 绝对路径列表(至少 1 个)
62
+ */
63
+ function resolveCandidates(text, matchIndex, projectRoot, modules) {
64
+ const before = text.slice(0, matchIndex)
65
+ const cdMatch = before.match(CD_PREFIX_RE)
66
+ if (cdMatch) {
67
+ const subdir = cdMatch[1].replace(/^["']|["']$/g, '')
68
+ return [join(projectRoot, subdir, 'package.json')]
69
+ }
70
+ if (modules && typeof modules === 'object') {
71
+ const candidates = []
72
+ for (const [, mod] of Object.entries(modules)) {
73
+ if (mod && typeof mod === 'object' && typeof mod.path === 'string' && mod.path.trim() !== '') {
74
+ candidates.push(join(projectRoot, mod.path, 'package.json'))
75
+ }
76
+ }
77
+ candidates.push(join(projectRoot, 'package.json'))
78
+ return candidates
79
+ }
80
+ return [join(projectRoot, 'package.json')]
81
+ }
82
+
83
+ /**
84
+ * 校验文本中所有 `npm|pnpm|yarn run <script>` 命令在 package.json scripts 中存在(monorepo 感知)。
85
+ *
86
+ * @param {string} text 待校验文本(local.yaml 内容 / TaskCard verify+implementation 字段等)
87
+ * @param {{ projectRoot: string, modules?: object|null }} opts
88
+ * - projectRoot : 项目根目录绝对路径
89
+ * - modules : local.yaml modules 块(可选),结构如 `{ frontend: { path: "frontend/", test: "..." } }`
90
+ * @returns {{ invalid: Array<{ cmd: string, reason: string }>, checked: number }}
91
+ * - invalid : 找不到 script 的命令列表,每项含 { cmd, reason };reason 区分
92
+ * 「package.json 不存在」「package.json 解析失败」「package.json 无 <script> script」
93
+ * - checked : 从 text 提取到的命令总数(含合法与非法)
94
+ */
95
+ export function validateScriptCommands(text, { projectRoot, modules } = {}) {
96
+ const invalid = []
97
+ if (!text || typeof text !== 'string') {
98
+ return { invalid, checked: 0 }
99
+ }
100
+
101
+ const matches = [...text.matchAll(SCRIPT_CMD_RE)]
102
+ const checked = matches.length
103
+
104
+ for (const m of matches) {
105
+ const pm = m[1]
106
+ const scriptName = m[2]
107
+ const cmd = `${pm} run ${scriptName}`
108
+
109
+ const candidates = resolveCandidates(text, m.index ?? 0, projectRoot, modules)
110
+
111
+ let found = false
112
+ let existedAny = false // 至少一个候选文件存在且可解析(ok 或 no-scripts)
113
+ let parseFailedAny = false // 至少一个候选文件解析失败(unreadable)
114
+ for (const pkgPath of candidates) {
115
+ const result = readScripts(pkgPath)
116
+ if (result.status === 'ok') {
117
+ existedAny = true
118
+ if (Object.prototype.hasOwnProperty.call(result.scripts, scriptName)) {
119
+ found = true
120
+ break
121
+ }
122
+ } else if (result.status === 'no-scripts') {
123
+ existedAny = true
124
+ // scripts 字段缺失 → 该候选肯定不含此 script,继续看其他候选
125
+ } else if (result.status === 'unreadable') {
126
+ parseFailedAny = true
127
+ }
128
+ // missing → 既不算 existed 也不算 parseFailed
129
+ }
130
+
131
+ if (!found) {
132
+ let reason
133
+ if (!existedAny && parseFailedAny) {
134
+ reason = `package.json 解析失败: ${candidates.join(', ')}`
135
+ } else if (!existedAny) {
136
+ reason = `package.json 不存在: ${candidates.join(', ')}`
137
+ } else {
138
+ reason = `package.json 无 ${scriptName} script`
139
+ }
140
+ invalid.push({ cmd, reason })
141
+ }
142
+ }
143
+
144
+ return { invalid, checked }
145
+ }