sillyspec 3.20.2 → 3.20.4

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 (133) hide show
  1. package/.claude/skills/sillyspec-archive/SKILL.md +21 -21
  2. package/.claude/skills/sillyspec-auto/SKILL.md +83 -83
  3. package/.claude/skills/sillyspec-brainstorm/SKILL.md +44 -44
  4. package/.claude/skills/sillyspec-commit/SKILL.md +106 -106
  5. package/.claude/skills/sillyspec-continue/SKILL.md +45 -45
  6. package/.claude/skills/sillyspec-doctor/SKILL.md +31 -31
  7. package/.claude/skills/sillyspec-execute/SKILL.md +30 -30
  8. package/.claude/skills/sillyspec-explore/SKILL.md +109 -109
  9. package/.claude/skills/sillyspec-knowledge/SKILL.md +269 -269
  10. package/.claude/skills/sillyspec-plan/SKILL.md +21 -21
  11. package/.claude/skills/sillyspec-propose/SKILL.md +21 -21
  12. package/.claude/skills/sillyspec-quick/SKILL.md +21 -21
  13. package/.claude/skills/sillyspec-resume/SKILL.md +68 -68
  14. package/.claude/skills/sillyspec-scan/SKILL.md +21 -21
  15. package/.claude/skills/sillyspec-state/SKILL.md +54 -54
  16. package/.claude/skills/sillyspec-status/SKILL.md +21 -21
  17. package/.claude/skills/sillyspec-verify/SKILL.md +21 -21
  18. package/.claude/skills/sillyspec-workspace/SKILL.md +157 -157
  19. package/.husky/pre-push +13 -13
  20. package/CLAUDE.md +18 -18
  21. package/README.md +198 -188
  22. package/SKILL.md +90 -91
  23. package/bin/sillyspec.js +2 -2
  24. package/docs/brainstorm-plan-contract.md +64 -64
  25. package/docs/plan-execute-contract.md +123 -123
  26. package/docs/platform-scan-protocol.md +298 -298
  27. package/docs/revision-mode.md +115 -115
  28. package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +99 -99
  29. package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +218 -218
  30. package/docs/sillyspec/file-lifecycle/stage-artifacts.md +167 -167
  31. package/docs/sillyspec/file-lifecycle/storage-and-state.md +148 -148
  32. package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +211 -193
  33. package/docs/sillyspec/file-lifecycle.md +125 -125
  34. package/docs/workflow-contract-regression.md +106 -106
  35. package/docs/worktree-isolation.md +252 -252
  36. package/package.json +40 -40
  37. package/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -7446
  38. package/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -1
  39. package/packages/dashboard/dist/index.html +16 -16
  40. package/packages/dashboard/index.html +15 -15
  41. package/packages/dashboard/package-lock.json +2384 -2384
  42. package/packages/dashboard/package.json +25 -25
  43. package/packages/dashboard/server/executor.js +86 -86
  44. package/packages/dashboard/server/index.js +588 -588
  45. package/packages/dashboard/server/parser.js +526 -526
  46. package/packages/dashboard/server/watcher.js +344 -344
  47. package/packages/dashboard/src/App.vue +558 -558
  48. package/packages/dashboard/src/components/ActionBar.vue +93 -93
  49. package/packages/dashboard/src/components/CommandPalette.vue +96 -96
  50. package/packages/dashboard/src/components/DetailPanel.vue +137 -137
  51. package/packages/dashboard/src/components/LogStream.vue +65 -65
  52. package/packages/dashboard/src/components/PipelineStage.vue +95 -95
  53. package/packages/dashboard/src/components/PipelineView.vue +156 -156
  54. package/packages/dashboard/src/components/ProjectList.vue +210 -210
  55. package/packages/dashboard/src/components/StageBadge.vue +67 -67
  56. package/packages/dashboard/src/components/StepCard.vue +94 -94
  57. package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -48
  58. package/packages/dashboard/src/components/detail/GitDetail.vue +61 -61
  59. package/packages/dashboard/src/components/detail/TechDetail.vue +43 -43
  60. package/packages/dashboard/src/composables/useDashboard.js +170 -170
  61. package/packages/dashboard/src/composables/useKeyboard.js +119 -119
  62. package/packages/dashboard/src/composables/useWebSocket.js +129 -129
  63. package/packages/dashboard/src/main.js +8 -8
  64. package/packages/dashboard/src/style.css +132 -132
  65. package/packages/dashboard/vite.config.js +18 -18
  66. package/src/brainstorm-postcheck.js +158 -158
  67. package/src/change-list.js +52 -52
  68. package/src/change-risk-profile.js +352 -352
  69. package/src/classify-change.js +73 -73
  70. package/src/constants.js +70 -70
  71. package/src/contract-matrix.js +278 -278
  72. package/src/db.js +201 -201
  73. package/src/endpoint-extractor.js +315 -315
  74. package/src/hooks/claude-pre-tool-use.cjs +125 -125
  75. package/src/hooks/worktree-guard.js +653 -653
  76. package/src/index.js +922 -900
  77. package/src/init.js +431 -431
  78. package/src/knowledge-match.js +130 -130
  79. package/src/migrate.js +117 -117
  80. package/src/modules.js +482 -482
  81. package/src/progress.js +1734 -1734
  82. package/src/run.js +3465 -3358
  83. package/src/scan-postcheck.js +387 -383
  84. package/src/setup.js +398 -398
  85. package/src/stage-contract.js +700 -700
  86. package/src/stages/archive.js +160 -160
  87. package/src/stages/brainstorm-auto.js +229 -229
  88. package/src/stages/brainstorm.js +645 -645
  89. package/src/stages/doctor.js +365 -365
  90. package/src/stages/execute.js +625 -625
  91. package/src/stages/explore.js +34 -34
  92. package/src/stages/index.js +29 -29
  93. package/src/stages/knowledge.js +498 -498
  94. package/src/stages/plan-postcheck.js +511 -513
  95. package/src/stages/plan.js +582 -582
  96. package/src/stages/propose.js +174 -174
  97. package/src/stages/quick.js +82 -82
  98. package/src/stages/scan.js +558 -558
  99. package/src/stages/status.js +65 -65
  100. package/src/stages/verify.js +322 -322
  101. package/src/sync.js +497 -497
  102. package/src/task-review.js +346 -346
  103. package/src/workflow.js +785 -785
  104. package/src/worktree-apply.js +549 -549
  105. package/src/worktree-deps.js +185 -0
  106. package/src/worktree.js +982 -932
  107. package/templates/workflows/archive-impact.yaml +79 -79
  108. package/templates/workflows/scan-docs.yaml +132 -132
  109. package/test/brainstorm-plan-contract.test.mjs +273 -273
  110. package/test/check-syntax.mjs +26 -26
  111. package/test/contract-artifacts.test.mjs +323 -323
  112. package/test/decision-supersede.test.mjs +277 -277
  113. package/test/knowledge-match.test.mjs +231 -231
  114. package/test/plan-execute-contract.test.mjs +330 -330
  115. package/test/plan-optimization.test.mjs +572 -572
  116. package/test/platform-artifacts.test.mjs +166 -166
  117. package/test/platform-failure-samples.test.mjs +199 -199
  118. package/test/platform-recovery-chain.test.mjs +167 -167
  119. package/test/platform-recovery.test.mjs +136 -136
  120. package/test/platform-scan-p0.test.mjs +168 -168
  121. package/test/revision-v1.test.mjs +1145 -1145
  122. package/test/run-scan-project-parse.test.mjs +200 -200
  123. package/test/run-tests.mjs +48 -48
  124. package/test/scan-knowledge.test.mjs +175 -175
  125. package/test/scan-paths.test.mjs +68 -68
  126. package/test/scan-postcheck.test.mjs +197 -197
  127. package/test/spec-dir.test.mjs +206 -206
  128. package/test/stage-contract.test.mjs +299 -299
  129. package/test/stage-definitions.test.mjs +39 -39
  130. package/test/wait-gates.test.mjs +496 -496
  131. package/test/worktree-deps-provision.test.mjs +148 -0
  132. package/test/worktree-guard.test.mjs +71 -71
  133. package/test/worktree-native-overlay.test.mjs +188 -188
@@ -0,0 +1,185 @@
1
+ /**
2
+ * worktree-deps.js — Worktree 依赖供给引擎
3
+ *
4
+ * 在 worktree.create() 的 baseline overlay 之后调用,让 worktree 立即可构建/测试。
5
+ * 策略:junction/symlink 快路径(lockfile 一致)+ install 兜底;多语言按 local.yaml
6
+ * project.type + lockfile 推断 install 命令。供给可失败,但状态写进 meta 供验证硬门读取。
7
+ *
8
+ * 见 change 2026-06-28-worktree-deps-provision / D-005@v1, D-007@v1。
9
+ */
10
+
11
+ import { existsSync, readFileSync, realpathSync } from 'fs';
12
+ import { join } from 'path';
13
+ import { execSync } from 'child_process';
14
+ import { createHash } from 'crypto';
15
+
16
+ const LOCKFILES = ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock'];
17
+ const DEFAULT_TIMEOUT_MS = 300 * 1000;
18
+
19
+ /**
20
+ * 取目录下首个命中的 lockfile 的 sha256 前 16 位;
21
+ * 无 lockfile 则 hash package.json;都没有返回 null。
22
+ */
23
+ export function lockfileHash(dir) {
24
+ if (!dir || !existsSync(dir)) return null;
25
+ for (const lf of LOCKFILES) {
26
+ const p = join(dir, lf);
27
+ if (existsSync(p)) {
28
+ return createHash('sha256').update(readFileSync(p)).digest('hex').slice(0, 16);
29
+ }
30
+ }
31
+ const pkg = join(dir, 'package.json');
32
+ if (existsSync(pkg)) {
33
+ return createHash('sha256').update(readFileSync(pkg)).digest('hex').slice(0, 16);
34
+ }
35
+ return null;
36
+ }
37
+
38
+ /** 命中目录下的 lockfile 文件名(用于判断 nodejs 子类)*/
39
+ function detectLockfile(dir) {
40
+ if (!dir || !existsSync(dir)) return null;
41
+ for (const lf of LOCKFILES) {
42
+ if (existsSync(join(dir, lf))) return lf;
43
+ }
44
+ return null;
45
+ }
46
+
47
+ /** 读取 local.yaml 文本(specBase 优先,回退 worktreePath/.sillyspec;不读 process.cwd 避免环境配置泄漏)*/
48
+ function readLocalYaml(specBase, worktreePath) {
49
+ const candidates = [
50
+ specBase ? join(specBase, 'local.yaml') : null,
51
+ worktreePath ? join(worktreePath, '.sillyspec', 'local.yaml') : null,
52
+ ].filter(Boolean);
53
+ for (const p of candidates) {
54
+ if (existsSync(p)) return readFileSync(p, 'utf8');
55
+ }
56
+ return null;
57
+ }
58
+
59
+ /** 从 local.yaml 文本提取 commands.install(轻量正则,不引 yaml 依赖,与 scan-postcheck 一致)*/
60
+ function extractUserInstall(yamlText) {
61
+ if (!yamlText) return null;
62
+ // 匹配 commands: 段下的 install: "..." 或 install: '...'(unavailable 视为无)
63
+ const m = yamlText.match(/install:\s*["']([^"']+)["']/);
64
+ if (m && m[1] && m[1].toLowerCase() !== 'unavailable') return m[1];
65
+ return null;
66
+ }
67
+
68
+ /** 从 local.yaml 提取 project.type;缺失时按文件特征推断 */
69
+ function detectProjectType(worktreePath, specBase) {
70
+ const yamlText = readLocalYaml(specBase, worktreePath);
71
+ if (yamlText) {
72
+ const m = yamlText.match(/type:\s*(\S+)/);
73
+ if (m && m[1]) return m[1];
74
+ }
75
+ if (existsSync(join(worktreePath, 'pom.xml'))) return 'maven';
76
+ if (existsSync(join(worktreePath, 'build.gradle')) || existsSync(join(worktreePath, 'build.gradle.kts'))) return 'gradle';
77
+ if (existsSync(join(worktreePath, 'package.json'))) return 'nodejs';
78
+ return 'generic';
79
+ }
80
+
81
+ /** 按 project.type + lockfile 推断 install 命令(无 commands.install 时)*/
82
+ function inferInstallCommand(projectType, worktreePath, userInstall) {
83
+ if (userInstall) return userInstall;
84
+ switch (projectType) {
85
+ case 'nodejs':
86
+ if (existsSync(join(worktreePath, 'pnpm-lock.yaml'))) return 'pnpm install --frozen-lockfile';
87
+ if (existsSync(join(worktreePath, 'package-lock.json'))) return 'npm ci';
88
+ if (existsSync(join(worktreePath, 'yarn.lock'))) return 'yarn install --frozen-lockfile';
89
+ return 'npm install'; // 无 lockfile 兜底(X-2)
90
+ case 'maven':
91
+ return 'mvn -o test';
92
+ case 'gradle':
93
+ return './gradlew test';
94
+ default:
95
+ return null; // generic → n/a
96
+ }
97
+ }
98
+
99
+ /** 在 worktreePath 创建 node_modules 链接到 mainNodeModules;失败回退 */
100
+ function tryLink(mainNodeModules, linkPath) {
101
+ // 已存在:确认是否已指向 main(幂等)
102
+ if (existsSync(linkPath)) {
103
+ try {
104
+ const resolved = realpathSync(linkPath);
105
+ const resolvedTarget = realpathSync(mainNodeModules);
106
+ if (resolved === resolvedTarget) return { ok: true, method: process.platform === 'win32' ? 'junction' : 'symlink' };
107
+ } catch {}
108
+ // 指向别处 → 不 clobber,视为已有依赖(installed 语义)
109
+ return { ok: true, method: 'install', preexisting: true };
110
+ }
111
+ try {
112
+ if (process.platform === 'win32') {
113
+ execSync(`mklink /J "${linkPath}" "${mainNodeModules}"`, { shell: 'cmd.exe', stdio: ['pipe', 'pipe', 'pipe'] });
114
+ return { ok: true, method: 'junction' };
115
+ }
116
+ execSync(`ln -s "${mainNodeModules}" "${linkPath}"`, { stdio: ['pipe', 'pipe', 'pipe'] });
117
+ return { ok: true, method: 'symlink' };
118
+ } catch (e) {
119
+ return { ok: false, error: e.message };
120
+ }
121
+ }
122
+
123
+ /** 执行 install 命令(带超时)*/
124
+ function tryInstall(cmd, cwd, timeout) {
125
+ try {
126
+ execSync(cmd, { cwd, timeout, stdio: ['pipe', 'pipe', 'pipe'] });
127
+ return { ok: true };
128
+ } catch (e) {
129
+ const msg = e.killed ? `timeout after ${timeout}ms` : ((e.stderr && e.stderr.toString()) || e.message);
130
+ return { ok: false, error: `${cmd} failed: ${msg}` };
131
+ }
132
+ }
133
+
134
+ /**
135
+ * 供给依赖。返回 deps 状态对象(合并进 meta)。
136
+ * @param {string} worktreePath - worktree 根目录
137
+ * @param {string} mainCwd - 主 checkout 根目录(node_modules 来源)
138
+ * @param {{ specBase?: string, timeout?: number }} opts
139
+ * @returns {{ depsStatus, depsMethod, depsSource, depsLockHash, depsCheckedAt, depsError? }}
140
+ */
141
+ export function provisionDeps(worktreePath, mainCwd, opts = {}) {
142
+ const { specBase = null, timeout = DEFAULT_TIMEOUT_MS } = opts;
143
+ const depsCheckedAt = new Date().toISOString();
144
+ const wtHash = lockfileHash(worktreePath);
145
+
146
+ const projectType = detectProjectType(worktreePath, specBase);
147
+ const userInstall = extractUserInstall(readLocalYaml(specBase, worktreePath));
148
+ const installCmd = inferInstallCommand(projectType, worktreePath, userInstall);
149
+
150
+ // generic / 无可执行 install → n/a
151
+ if (!installCmd) {
152
+ return { depsStatus: 'n/a', depsMethod: null, depsSource: null, depsLockHash: wtHash, depsCheckedAt };
153
+ }
154
+
155
+ // 快路径:main 有 node_modules 且 lockfile hash 一致 → junction/symlink
156
+ const mainNodeModules = mainCwd ? join(mainCwd, 'node_modules') : null;
157
+ const mainHash = lockfileHash(mainCwd);
158
+ if (mainNodeModules && existsSync(mainNodeModules) && mainHash && wtHash && mainHash === wtHash) {
159
+ const linkResult = tryLink(mainNodeModules, join(worktreePath, 'node_modules'));
160
+ if (linkResult.ok) {
161
+ return {
162
+ depsStatus: 'linked',
163
+ depsMethod: linkResult.method,
164
+ depsSource: linkResult.preexisting ? 'install' : 'main-checkout',
165
+ depsLockHash: wtHash,
166
+ depsCheckedAt,
167
+ };
168
+ }
169
+ // link 失败 → 回退 install
170
+ }
171
+
172
+ // 兜底:install
173
+ const installResult = tryInstall(installCmd, worktreePath, timeout);
174
+ if (installResult.ok) {
175
+ return { depsStatus: 'installed', depsMethod: 'install', depsSource: 'install', depsLockHash: wtHash, depsCheckedAt };
176
+ }
177
+ return {
178
+ depsStatus: 'failed',
179
+ depsMethod: null,
180
+ depsSource: null,
181
+ depsLockHash: wtHash,
182
+ depsCheckedAt,
183
+ depsError: installResult.error,
184
+ };
185
+ }