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.
- package/.claude/skills/sillyspec-archive/SKILL.md +21 -21
- package/.claude/skills/sillyspec-auto/SKILL.md +83 -83
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +44 -44
- package/.claude/skills/sillyspec-commit/SKILL.md +106 -106
- package/.claude/skills/sillyspec-continue/SKILL.md +45 -45
- package/.claude/skills/sillyspec-doctor/SKILL.md +31 -31
- package/.claude/skills/sillyspec-execute/SKILL.md +30 -30
- package/.claude/skills/sillyspec-explore/SKILL.md +109 -109
- package/.claude/skills/sillyspec-knowledge/SKILL.md +269 -269
- package/.claude/skills/sillyspec-plan/SKILL.md +21 -21
- package/.claude/skills/sillyspec-propose/SKILL.md +21 -21
- package/.claude/skills/sillyspec-quick/SKILL.md +21 -21
- package/.claude/skills/sillyspec-resume/SKILL.md +68 -68
- package/.claude/skills/sillyspec-scan/SKILL.md +21 -21
- package/.claude/skills/sillyspec-state/SKILL.md +54 -54
- package/.claude/skills/sillyspec-status/SKILL.md +21 -21
- package/.claude/skills/sillyspec-verify/SKILL.md +21 -21
- package/.claude/skills/sillyspec-workspace/SKILL.md +157 -157
- package/.husky/pre-push +13 -13
- package/CLAUDE.md +18 -18
- package/README.md +198 -188
- package/SKILL.md +90 -91
- package/bin/sillyspec.js +2 -2
- package/docs/brainstorm-plan-contract.md +64 -64
- package/docs/plan-execute-contract.md +123 -123
- package/docs/platform-scan-protocol.md +298 -298
- package/docs/revision-mode.md +115 -115
- package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +99 -99
- package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +218 -218
- package/docs/sillyspec/file-lifecycle/stage-artifacts.md +167 -167
- package/docs/sillyspec/file-lifecycle/storage-and-state.md +148 -148
- package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +211 -193
- package/docs/sillyspec/file-lifecycle.md +125 -125
- package/docs/workflow-contract-regression.md +106 -106
- package/docs/worktree-isolation.md +252 -252
- package/package.json +40 -40
- package/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -7446
- package/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -1
- package/packages/dashboard/dist/index.html +16 -16
- package/packages/dashboard/index.html +15 -15
- package/packages/dashboard/package-lock.json +2384 -2384
- package/packages/dashboard/package.json +25 -25
- package/packages/dashboard/server/executor.js +86 -86
- package/packages/dashboard/server/index.js +588 -588
- package/packages/dashboard/server/parser.js +526 -526
- package/packages/dashboard/server/watcher.js +344 -344
- package/packages/dashboard/src/App.vue +558 -558
- package/packages/dashboard/src/components/ActionBar.vue +93 -93
- package/packages/dashboard/src/components/CommandPalette.vue +96 -96
- package/packages/dashboard/src/components/DetailPanel.vue +137 -137
- package/packages/dashboard/src/components/LogStream.vue +65 -65
- package/packages/dashboard/src/components/PipelineStage.vue +95 -95
- package/packages/dashboard/src/components/PipelineView.vue +156 -156
- package/packages/dashboard/src/components/ProjectList.vue +210 -210
- package/packages/dashboard/src/components/StageBadge.vue +67 -67
- package/packages/dashboard/src/components/StepCard.vue +94 -94
- package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -48
- package/packages/dashboard/src/components/detail/GitDetail.vue +61 -61
- package/packages/dashboard/src/components/detail/TechDetail.vue +43 -43
- package/packages/dashboard/src/composables/useDashboard.js +170 -170
- package/packages/dashboard/src/composables/useKeyboard.js +119 -119
- package/packages/dashboard/src/composables/useWebSocket.js +129 -129
- package/packages/dashboard/src/main.js +8 -8
- package/packages/dashboard/src/style.css +132 -132
- package/packages/dashboard/vite.config.js +18 -18
- package/src/brainstorm-postcheck.js +158 -158
- package/src/change-list.js +52 -52
- package/src/change-risk-profile.js +352 -352
- package/src/classify-change.js +73 -73
- package/src/constants.js +70 -70
- package/src/contract-matrix.js +278 -278
- package/src/db.js +201 -201
- package/src/endpoint-extractor.js +315 -315
- package/src/hooks/claude-pre-tool-use.cjs +125 -125
- package/src/hooks/worktree-guard.js +653 -653
- package/src/index.js +922 -900
- package/src/init.js +431 -431
- package/src/knowledge-match.js +130 -130
- package/src/migrate.js +117 -117
- package/src/modules.js +482 -482
- package/src/progress.js +1734 -1734
- package/src/run.js +3465 -3358
- package/src/scan-postcheck.js +387 -383
- package/src/setup.js +398 -398
- package/src/stage-contract.js +700 -700
- package/src/stages/archive.js +160 -160
- package/src/stages/brainstorm-auto.js +229 -229
- package/src/stages/brainstorm.js +645 -645
- package/src/stages/doctor.js +365 -365
- package/src/stages/execute.js +625 -625
- package/src/stages/explore.js +34 -34
- package/src/stages/index.js +29 -29
- package/src/stages/knowledge.js +498 -498
- package/src/stages/plan-postcheck.js +511 -513
- package/src/stages/plan.js +582 -582
- package/src/stages/propose.js +174 -174
- package/src/stages/quick.js +82 -82
- package/src/stages/scan.js +558 -558
- package/src/stages/status.js +65 -65
- package/src/stages/verify.js +322 -322
- package/src/sync.js +497 -497
- package/src/task-review.js +346 -346
- package/src/workflow.js +785 -785
- package/src/worktree-apply.js +549 -549
- package/src/worktree-deps.js +185 -0
- package/src/worktree.js +982 -932
- package/templates/workflows/archive-impact.yaml +79 -79
- package/templates/workflows/scan-docs.yaml +132 -132
- package/test/brainstorm-plan-contract.test.mjs +273 -273
- package/test/check-syntax.mjs +26 -26
- package/test/contract-artifacts.test.mjs +323 -323
- package/test/decision-supersede.test.mjs +277 -277
- package/test/knowledge-match.test.mjs +231 -231
- package/test/plan-execute-contract.test.mjs +330 -330
- package/test/plan-optimization.test.mjs +572 -572
- package/test/platform-artifacts.test.mjs +166 -166
- package/test/platform-failure-samples.test.mjs +199 -199
- package/test/platform-recovery-chain.test.mjs +167 -167
- package/test/platform-recovery.test.mjs +136 -136
- package/test/platform-scan-p0.test.mjs +168 -168
- package/test/revision-v1.test.mjs +1145 -1145
- package/test/run-scan-project-parse.test.mjs +200 -200
- package/test/run-tests.mjs +48 -48
- package/test/scan-knowledge.test.mjs +175 -175
- package/test/scan-paths.test.mjs +68 -68
- package/test/scan-postcheck.test.mjs +197 -197
- package/test/spec-dir.test.mjs +206 -206
- package/test/stage-contract.test.mjs +299 -299
- package/test/stage-definitions.test.mjs +39 -39
- package/test/wait-gates.test.mjs +496 -496
- package/test/worktree-deps-provision.test.mjs +148 -0
- package/test/worktree-guard.test.mjs +71 -71
- 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
|
+
}
|