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,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worktree-deps-provision 测试
|
|
3
|
+
* 覆盖 FR-01/02/03/05:lockfileHash + provisionDeps(junction 快路径 / install 兜底判定 / generic→n/a)
|
|
4
|
+
* change 2026-06-28-worktree-deps-provision
|
|
5
|
+
*/
|
|
6
|
+
import { provisionDeps, lockfileHash } from '../src/worktree-deps.js'
|
|
7
|
+
import { mkdtempSync, writeFileSync, mkdirSync, existsSync, rmSync } from 'fs'
|
|
8
|
+
import { join } from 'path'
|
|
9
|
+
import { tmpdir } from 'os'
|
|
10
|
+
|
|
11
|
+
let failed = 0
|
|
12
|
+
let passed = 0
|
|
13
|
+
const failures = []
|
|
14
|
+
function assert(cond, msg) {
|
|
15
|
+
if (cond) { passed++; console.log(` ✅ PASS: ${msg}`) }
|
|
16
|
+
else { failed++; failures.push(msg); console.log(` ❌ FAIL: ${msg}`) }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const tmpDirs = []
|
|
20
|
+
function mkTmp(prefix) {
|
|
21
|
+
const d = mkdtempSync(join(tmpdir(), `wtdeps-${prefix}-`))
|
|
22
|
+
tmpDirs.push(d)
|
|
23
|
+
return d
|
|
24
|
+
}
|
|
25
|
+
function cleanup() {
|
|
26
|
+
for (const d of tmpDirs) {
|
|
27
|
+
try { rmSync(d, { recursive: true, force: true }) } catch {}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log('=== worktree-deps-provision 测试 ===\n')
|
|
32
|
+
|
|
33
|
+
// ── lockfileHash ──
|
|
34
|
+
console.log('--- lockfileHash ---')
|
|
35
|
+
{
|
|
36
|
+
const d = mkTmp('lh')
|
|
37
|
+
assert(lockfileHash(d) === null, '空目录返回 null')
|
|
38
|
+
writeFileSync(join(d, 'pnpm-lock.yaml'), 'lockversion: 6.0\npackages: []\n')
|
|
39
|
+
const h1 = lockfileHash(d)
|
|
40
|
+
assert(typeof h1 === 'string' && h1.length === 16, `pnpm-lock 返回 16 位 hash: ${h1}`)
|
|
41
|
+
|
|
42
|
+
const d2 = mkTmp('lh2')
|
|
43
|
+
writeFileSync(join(d2, 'package-lock.json'), '{"lockfileVersion":3}')
|
|
44
|
+
const h2 = lockfileHash(d2)
|
|
45
|
+
assert(typeof h2 === 'string' && h2.length === 16, `package-lock 返回 hash: ${h2}`)
|
|
46
|
+
assert(h1 !== h2, '不同 lockfile 内容产生不同 hash')
|
|
47
|
+
|
|
48
|
+
const d3 = mkTmp('lh3')
|
|
49
|
+
writeFileSync(join(d3, 'yarn.lock'), '# yarn lock v1\n')
|
|
50
|
+
assert(typeof lockfileHash(d3) === 'string', 'yarn.lock 返回 hash')
|
|
51
|
+
|
|
52
|
+
const d4 = mkTmp('lh4')
|
|
53
|
+
writeFileSync(join(d4, 'package.json'), '{"name":"x"}')
|
|
54
|
+
assert(typeof lockfileHash(d4) === 'string', '无 lockfile 时回退 hash package.json')
|
|
55
|
+
|
|
56
|
+
// 一致性:相同内容相同 hash
|
|
57
|
+
const d5 = mkTmp('lh5')
|
|
58
|
+
writeFileSync(join(d5, 'pnpm-lock.yaml'), 'lockversion: 6.0\npackages: []\n')
|
|
59
|
+
assert(lockfileHash(d5) === h1, '相同 lockfile 内容产生相同 hash(快路径判定基础)')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── provisionDeps: generic → n/a ──
|
|
63
|
+
console.log('\n--- provisionDeps: generic / 无依赖 → n/a ---')
|
|
64
|
+
{
|
|
65
|
+
const wt = mkTmp('gen-wt')
|
|
66
|
+
const main = mkTmp('gen-main')
|
|
67
|
+
const r = provisionDeps(wt, main, {})
|
|
68
|
+
assert(r.depsStatus === 'n/a', `无 package.json/lockfile 的目录 → n/a(实际 ${r.depsStatus})`)
|
|
69
|
+
assert(!existsSync(join(wt, 'node_modules')), 'n/a 不创建 node_modules')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── provisionDeps: nodejs junction 快路径(lockfile 一致)──
|
|
73
|
+
console.log('\n--- provisionDeps: nodejs lockfile 一致 → linked (junction/symlink) ---')
|
|
74
|
+
{
|
|
75
|
+
const main = mkTmp('link-main')
|
|
76
|
+
const wt = mkTmp('link-wt')
|
|
77
|
+
// 主 checkout:有 node_modules + lockfile
|
|
78
|
+
mkdirSync(join(main, 'node_modules'))
|
|
79
|
+
writeFileSync(join(main, 'node_modules', '.placeholder'), 'x')
|
|
80
|
+
const lockContent = 'lockversion: 6.0\npackages: []\n'
|
|
81
|
+
writeFileSync(join(main, 'package-lock.json'), lockContent)
|
|
82
|
+
writeFileSync(join(main, 'package.json'), '{"name":"main","scripts":{}}')
|
|
83
|
+
// worktree:相同 lockfile(hash 一致)+ package.json
|
|
84
|
+
writeFileSync(join(wt, 'package-lock.json'), lockContent)
|
|
85
|
+
writeFileSync(join(wt, 'package.json'), '{"name":"wt","scripts":{}}')
|
|
86
|
+
|
|
87
|
+
const r = provisionDeps(wt, main, {})
|
|
88
|
+
assert(r.depsStatus === 'linked', `lockfile 一致 → linked(实际 ${r.depsStatus})`)
|
|
89
|
+
assert(['junction', 'symlink', 'install'].includes(r.depsMethod), `depsMethod 合法(${r.depsMethod})`)
|
|
90
|
+
assert(r.depsSource === 'main-checkout', `depsSource=main-checkout(${r.depsSource})`)
|
|
91
|
+
assert(existsSync(join(wt, 'node_modules')), 'worktree/node_modules 已创建(junction/symlink)')
|
|
92
|
+
assert(existsSync(join(wt, 'node_modules', '.placeholder')), 'junction 指向 main 的内容可访问')
|
|
93
|
+
assert(typeof r.depsLockHash === 'string', 'depsLockHash 已记录')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── provisionDeps: lockfile 不一致 → 走 install 兜底(不验证真实 install,只验证不走 linked)──
|
|
97
|
+
console.log('\n--- provisionDeps: lockfile 不一致 → 不走 linked 快路径 ---')
|
|
98
|
+
{
|
|
99
|
+
const main = mkTmp('mis-main')
|
|
100
|
+
const wt = mkTmp('mis-wt')
|
|
101
|
+
mkdirSync(join(main, 'node_modules'))
|
|
102
|
+
writeFileSync(join(main, 'package-lock.json'), 'lockversion-A')
|
|
103
|
+
writeFileSync(join(main, 'package.json'), '{"name":"main"}')
|
|
104
|
+
writeFileSync(join(wt, 'package-lock.json'), 'lockversion-B-DIFFERENT') // 不一致
|
|
105
|
+
writeFileSync(join(wt, 'package.json'), '{"name":"wt"}')
|
|
106
|
+
|
|
107
|
+
const r = provisionDeps(wt, main, {})
|
|
108
|
+
// lockfile 不一致 → 不会 linked;会尝试 install(真实 install 在 CI 可能失败/超时)
|
|
109
|
+
assert(r.depsStatus !== 'linked', `lockfile 不一致时不应 linked(实际 ${r.depsStatus})`)
|
|
110
|
+
assert(['installed', 'failed'].includes(r.depsStatus), `兜底为 installed 或 failed(实际 ${r.depsStatus})`)
|
|
111
|
+
// install 失败时应带 depsError
|
|
112
|
+
if (r.depsStatus === 'failed') {
|
|
113
|
+
assert(typeof r.depsError === 'string' && r.depsError.length > 0, 'failed 带 depsError 信息')
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── provisionDeps: main 无 node_modules → 不走 linked ──
|
|
118
|
+
console.log('\n--- provisionDeps: main 无 node_modules → 不走 linked ---')
|
|
119
|
+
{
|
|
120
|
+
const main = mkTmp('nomain-main')
|
|
121
|
+
const wt = mkTmp('nomain-wt')
|
|
122
|
+
const lockContent = 'same-lock\n'
|
|
123
|
+
writeFileSync(join(main, 'package-lock.json'), lockContent)
|
|
124
|
+
writeFileSync(join(main, 'package.json'), '{"name":"main"}')
|
|
125
|
+
writeFileSync(join(wt, 'package-lock.json'), lockContent)
|
|
126
|
+
writeFileSync(join(wt, 'package.json'), '{"name":"wt"}')
|
|
127
|
+
// main 无 node_modules
|
|
128
|
+
|
|
129
|
+
const r = provisionDeps(wt, main, {})
|
|
130
|
+
assert(r.depsStatus !== 'linked', `main 无 node_modules 时不应 linked(实际 ${r.depsStatus})`)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ── provisionDeps: maven/gradle 推断(无 node_modules,验证不崩)──
|
|
134
|
+
console.log('\n--- provisionDeps: 非 nodejs 项目类型不崩 ---')
|
|
135
|
+
{
|
|
136
|
+
const wt = mkTmp('maven-wt')
|
|
137
|
+
writeFileSync(join(wt, 'pom.xml'), '<project></project>')
|
|
138
|
+
const r = provisionDeps(wt, mkTmp('maven-main'), {})
|
|
139
|
+
assert(['installed', 'failed'].includes(r.depsStatus), `maven 项目返回 installed/failed(${r.depsStatus},mvn 可能不可用)`)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
cleanup()
|
|
143
|
+
|
|
144
|
+
console.log(`\n==================================================`)
|
|
145
|
+
console.log(`✅ 通过: ${passed} ❌ 失败: ${failed}`)
|
|
146
|
+
console.log(failed === 0 ? '全部通过' : `❌ 失败项: ${failures.join('; ')}`)
|
|
147
|
+
console.log(`==================================================`)
|
|
148
|
+
process.exit(failed === 0 ? 0 : 1)
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import assert from 'node:assert/strict'
|
|
2
|
-
import { mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
3
|
-
import { join } from 'node:path'
|
|
4
|
-
import { tmpdir } from 'node:os'
|
|
5
|
-
import { shouldBlock } from '../src/hooks/worktree-guard.js'
|
|
6
|
-
|
|
7
|
-
const root = join(tmpdir(), `sillyspec-guard-test-${Date.now()}`)
|
|
8
|
-
const changeName = '2026-06-04-guard-test'
|
|
9
|
-
const runtimeDir = join(root, '.sillyspec', '.runtime')
|
|
10
|
-
const registeredWorktree = join(runtimeDir, 'worktrees', changeName)
|
|
11
|
-
const unregisteredWorktree = join(runtimeDir, 'worktrees', 'other-change')
|
|
12
|
-
|
|
13
|
-
mkdirSync(registeredWorktree, { recursive: true })
|
|
14
|
-
mkdirSync(unregisteredWorktree, { recursive: true })
|
|
15
|
-
writeFileSync(join(runtimeDir, 'gate-status.json'), JSON.stringify({
|
|
16
|
-
stage: 'execute',
|
|
17
|
-
changes: [changeName],
|
|
18
|
-
updatedAt: new Date().toISOString(),
|
|
19
|
-
}, null, 2))
|
|
20
|
-
writeFileSync(join(registeredWorktree, 'meta.json'), JSON.stringify({
|
|
21
|
-
changeName,
|
|
22
|
-
worktreePath: registeredWorktree,
|
|
23
|
-
mode: 'worktree',
|
|
24
|
-
}, null, 2))
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
assert.equal(
|
|
28
|
-
shouldBlock({ tool: 'Write', filePath: join(registeredWorktree, 'src', 'ok.js'), cwd: root }).blocked,
|
|
29
|
-
false,
|
|
30
|
-
'registered worktree writes should be allowed'
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
assert.equal(
|
|
34
|
-
shouldBlock({ tool: 'Bash', command: 'npm run build', cwd: registeredWorktree }).blocked,
|
|
35
|
-
false,
|
|
36
|
-
'bash commands from a registered worktree cwd should be allowed'
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
assert.equal(
|
|
40
|
-
shouldBlock({ tool: 'Write', filePath: join(unregisteredWorktree, 'src', 'blocked.js'), cwd: root }).blocked,
|
|
41
|
-
true,
|
|
42
|
-
'unregistered worktree storage writes should be blocked'
|
|
43
|
-
)
|
|
44
|
-
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { shouldBlock } from '../src/hooks/worktree-guard.js'
|
|
6
|
+
|
|
7
|
+
const root = join(tmpdir(), `sillyspec-guard-test-${Date.now()}`)
|
|
8
|
+
const changeName = '2026-06-04-guard-test'
|
|
9
|
+
const runtimeDir = join(root, '.sillyspec', '.runtime')
|
|
10
|
+
const registeredWorktree = join(runtimeDir, 'worktrees', changeName)
|
|
11
|
+
const unregisteredWorktree = join(runtimeDir, 'worktrees', 'other-change')
|
|
12
|
+
|
|
13
|
+
mkdirSync(registeredWorktree, { recursive: true })
|
|
14
|
+
mkdirSync(unregisteredWorktree, { recursive: true })
|
|
15
|
+
writeFileSync(join(runtimeDir, 'gate-status.json'), JSON.stringify({
|
|
16
|
+
stage: 'execute',
|
|
17
|
+
changes: [changeName],
|
|
18
|
+
updatedAt: new Date().toISOString(),
|
|
19
|
+
}, null, 2))
|
|
20
|
+
writeFileSync(join(registeredWorktree, 'meta.json'), JSON.stringify({
|
|
21
|
+
changeName,
|
|
22
|
+
worktreePath: registeredWorktree,
|
|
23
|
+
mode: 'worktree',
|
|
24
|
+
}, null, 2))
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
assert.equal(
|
|
28
|
+
shouldBlock({ tool: 'Write', filePath: join(registeredWorktree, 'src', 'ok.js'), cwd: root }).blocked,
|
|
29
|
+
false,
|
|
30
|
+
'registered worktree writes should be allowed'
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
assert.equal(
|
|
34
|
+
shouldBlock({ tool: 'Bash', command: 'npm run build', cwd: registeredWorktree }).blocked,
|
|
35
|
+
false,
|
|
36
|
+
'bash commands from a registered worktree cwd should be allowed'
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
assert.equal(
|
|
40
|
+
shouldBlock({ tool: 'Write', filePath: join(unregisteredWorktree, 'src', 'blocked.js'), cwd: root }).blocked,
|
|
41
|
+
true,
|
|
42
|
+
'unregistered worktree storage writes should be blocked'
|
|
43
|
+
)
|
|
44
|
+
|
|
45
45
|
assert.equal(
|
|
46
46
|
shouldBlock({ tool: 'Write', filePath: join(root, '.sillyspec', 'docs', 'note.md'), cwd: root }).blocked,
|
|
47
47
|
false,
|
|
@@ -107,30 +107,30 @@ try {
|
|
|
107
107
|
)
|
|
108
108
|
|
|
109
109
|
rmSync(join(runtimeDir, 'gate-status.json'), { force: true })
|
|
110
|
-
writeFileSync(join(root, '.sillyspec', 'local.yaml'), [
|
|
111
|
-
'worktreeHook:',
|
|
112
|
-
' readonlyCommands:',
|
|
113
|
-
' - custom-read',
|
|
114
|
-
'',
|
|
115
|
-
].join('\n'))
|
|
116
|
-
assert.equal(
|
|
117
|
-
shouldBlock({ tool: 'Bash', command: 'custom-read status', cwd: root }).blocked,
|
|
118
|
-
false,
|
|
119
|
-
'.sillyspec/local.yaml readonlyCommands should extend the bash whitelist'
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
writeFileSync(join(runtimeDir, 'gate-status.json'), JSON.stringify({
|
|
123
|
-
stage: 'quick',
|
|
124
|
-
changes: [changeName],
|
|
125
|
-
updatedAt: new Date().toISOString(),
|
|
126
|
-
}, null, 2))
|
|
127
|
-
assert.equal(
|
|
128
|
-
shouldBlock({ tool: 'Write', filePath: join(root, 'src', 'quick.js'), cwd: root }).blocked,
|
|
129
|
-
false,
|
|
130
|
-
'quick writes should still be allowed in the main workspace'
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
console.log('✅ worktree guard regression checks passed')
|
|
134
|
-
} finally {
|
|
135
|
-
rmSync(root, { recursive: true, force: true })
|
|
136
|
-
}
|
|
110
|
+
writeFileSync(join(root, '.sillyspec', 'local.yaml'), [
|
|
111
|
+
'worktreeHook:',
|
|
112
|
+
' readonlyCommands:',
|
|
113
|
+
' - custom-read',
|
|
114
|
+
'',
|
|
115
|
+
].join('\n'))
|
|
116
|
+
assert.equal(
|
|
117
|
+
shouldBlock({ tool: 'Bash', command: 'custom-read status', cwd: root }).blocked,
|
|
118
|
+
false,
|
|
119
|
+
'.sillyspec/local.yaml readonlyCommands should extend the bash whitelist'
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
writeFileSync(join(runtimeDir, 'gate-status.json'), JSON.stringify({
|
|
123
|
+
stage: 'quick',
|
|
124
|
+
changes: [changeName],
|
|
125
|
+
updatedAt: new Date().toISOString(),
|
|
126
|
+
}, null, 2))
|
|
127
|
+
assert.equal(
|
|
128
|
+
shouldBlock({ tool: 'Write', filePath: join(root, 'src', 'quick.js'), cwd: root }).blocked,
|
|
129
|
+
false,
|
|
130
|
+
'quick writes should still be allowed in the main workspace'
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
console.log('✅ worktree guard regression checks passed')
|
|
134
|
+
} finally {
|
|
135
|
+
rmSync(root, { recursive: true, force: true })
|
|
136
|
+
}
|