sillyspec 3.26.7 → 3.26.8
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-doctor/SKILL.md +2 -0
- package/.husky/pre-push +9 -2
- package/bin/sillyspec.js +1 -1
- package/package.json +1 -1
- package/src/config-schema.js +305 -287
- package/src/docs-check.js +333 -0
- package/src/docs-debt.js +226 -0
- package/src/docs-gate.js +106 -0
- package/src/doctor-diagnostics.js +23 -2
- package/src/index.js +1880 -1787
- package/src/init.js +81 -14
- package/src/modules.js +490 -486
- package/src/progress.js +37 -13
- package/src/run/command.js +54 -24
- package/src/run/complete-handlers.js +1254 -1128
- package/src/run/complete.js +0 -32
- package/src/run/gates.js +18 -0
- package/src/run/prompt.js +713 -640
- package/src/run/quick-audit.js +137 -123
- package/src/run/shared.js +1109 -929
- package/src/scan-staleness.js +90 -0
- package/src/stage-contract-spec.js +15 -0
- package/src/stage-contract.js +8 -5
- package/src/stages/brainstorm-auto.js +0 -1
- package/src/stages/brainstorm.js +1 -1
- package/src/stages/execute.js +985 -967
- package/src/stages/plan-postcheck.js +93 -36
- package/src/stages/verify.js +1 -1
- package/src/sync.js +27 -17
- package/src/task-review.js +2 -1
- package/src/worktree-apply.js +6 -3
package/src/progress.js
CHANGED
|
@@ -20,26 +20,21 @@ import { ChangeRegistry } from './progress/change-registry.js';
|
|
|
20
20
|
import { StepStore } from './progress/step-store.js';
|
|
21
21
|
import { StageMachine } from './progress/stage-machine.js';
|
|
22
22
|
import { STAGE_ORDER, MAIN_FLOW_ORDER, VALID_STAGES, STAGE_LABELS, SPEC_DIR_NAME, CURRENT_VERSION, emptyStage } from './progress/shared.js';
|
|
23
|
+
// resolveSpecDir 单一真相源在 src/run/shared.js(含 home 拒绝守卫,坑 cwd-correction-home-collision
|
|
24
|
+
// 根治:home 下 .sillyspec 恒不命中,防 smoke/临时目录污染自我延续)。此处 re-export 保持
|
|
25
|
+
// 既有 import 路径兼容;run/shared.js 对 progress.js 只有动态 import,无静态循环。
|
|
26
|
+
import { resolveSpecDir } from './run/shared.js';
|
|
27
|
+
import { checkPlatformManaged, PLATFORM_MANAGED_FILENAME } from './run/shared.js';
|
|
28
|
+
export { resolveSpecDir };
|
|
23
29
|
|
|
24
30
|
// 默认规范目录名(相对于 cwd)
|
|
25
31
|
// SPEC_DIR_NAME → ./progress/shared.js(W6 Step9d)
|
|
26
32
|
const RUNTIME_SUBDIR = '.runtime';
|
|
27
33
|
|
|
28
34
|
/**
|
|
29
|
-
* 向上查找含 .sillyspec
|
|
30
|
-
*
|
|
35
|
+
* 向上查找含 .sillyspec 目录的祖先目录——实现已收敛至 src/run/shared.js(单一真相源,
|
|
36
|
+
* 含 home 拒绝守卫),此处 re-export。见顶部 import 说明。
|
|
31
37
|
*/
|
|
32
|
-
export function resolveSpecDir(startDir) {
|
|
33
|
-
let dir = resolve(startDir);
|
|
34
|
-
while (true) {
|
|
35
|
-
const candidate = join(dir, SPEC_DIR_NAME);
|
|
36
|
-
if (existsSync(candidate)) return candidate;
|
|
37
|
-
const parent = dirname(dir);
|
|
38
|
-
if (parent === dir) break; // 到达根目录
|
|
39
|
-
dir = parent;
|
|
40
|
-
}
|
|
41
|
-
return join(resolve(startDir), SPEC_DIR_NAME);
|
|
42
|
-
}
|
|
43
38
|
|
|
44
39
|
/**
|
|
45
40
|
* 平台指针不可达错误。pointer 存在但失效时抛出,阻止静默回退到本地孤儿 db。
|
|
@@ -59,6 +54,26 @@ export class PointerUnreachableError extends Error {
|
|
|
59
54
|
}
|
|
60
55
|
}
|
|
61
56
|
|
|
57
|
+
/**
|
|
58
|
+
* 平台接管声明生效错误(指针缺失但声明存在)。fail-closed 入口一:
|
|
59
|
+
* 防指针被 cleanup/STALE 清理或项目挪动后裸调静默建本地进度库(状态分裂根因)。
|
|
60
|
+
*
|
|
61
|
+
* ⚠️ 不覆写 this.name(保持父类的 'PointerUnreachableError')——CLI 顶层 catch 按
|
|
62
|
+
* err?.name === 'PointerUnreachableError' 严格字符串匹配(index.js),子类改名会
|
|
63
|
+
* 落通用错误分支打 stack noise。靠 message 首行"平台接管声明生效"区分场景。
|
|
64
|
+
*/
|
|
65
|
+
export class PlatformManagedError extends PointerUnreachableError {
|
|
66
|
+
constructor({ declarationPath, specRoot }) {
|
|
67
|
+
super({
|
|
68
|
+
pointerPath: declarationPath,
|
|
69
|
+
specRoot,
|
|
70
|
+
reason: `平台接管声明生效——本项目已由平台托管,但恢复指针缺失(可能被 platform pointer --cleanup 清理或项目目录被移动),拒绝静默回退本地模式`,
|
|
71
|
+
hint: `① 重跑平台 scan/init(带 --spec-root)重建指针;② 确认不再使用平台:sillyspec platform disconnect(删除接管声明);③ 显式 --spec-dir <路径> 临时指定目录`,
|
|
72
|
+
});
|
|
73
|
+
this.declarationPath = declarationPath;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
62
77
|
/**
|
|
63
78
|
* 平台感知的 specDir 解析(fail-closed,所有 CLI 子命令统一入口)。
|
|
64
79
|
*
|
|
@@ -75,6 +90,15 @@ export function resolvePlatformSpecDir(cwd, explicitSpecDir = null) {
|
|
|
75
90
|
if (explicitSpecDir) return resolve(explicitSpecDir);
|
|
76
91
|
const pointerPath = join(resolve(cwd), '.sillyspec-platform.json');
|
|
77
92
|
if (!existsSync(pointerPath)) {
|
|
93
|
+
// 指针缺失:查平台接管声明(fail-closed 入口一)。声明存在 = 项目进过平台模式,
|
|
94
|
+
// 指针丢失不该静默当纯本地项目处理(会建本地进度库 → 状态分裂)。
|
|
95
|
+
const decl = checkPlatformManaged(cwd);
|
|
96
|
+
if (decl) {
|
|
97
|
+
throw new PlatformManagedError({
|
|
98
|
+
declarationPath: join(resolve(cwd), PLATFORM_MANAGED_FILENAME),
|
|
99
|
+
specRoot: decl.specRoot,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
78
102
|
return resolveSpecDir(cwd);
|
|
79
103
|
}
|
|
80
104
|
// pointer 存在 = 进过平台模式,严格校验,不静默回退
|
package/src/run/command.js
CHANGED
|
@@ -23,7 +23,7 @@ import { join, resolve, dirname } from 'node:path'
|
|
|
23
23
|
import { existsSync, readdirSync, mkdirSync, writeFileSync, readFileSync, rmSync } from 'node:fs'
|
|
24
24
|
import { randomBytes, randomUUID } from 'node:crypto'
|
|
25
25
|
import { writeAtomicSync } from '../fs-atomic.js'
|
|
26
|
-
import { resolveSpecDir, countAncestorSpecDirs, resolveChangeDir, triggerSync, getStageSteps, formatWaitOptions, checkApproval, didYouMean, assertSafeChangeName, detectQuickSessionDrift, detectWorktreeSpecDrift, resolveRuntimeRoot } from './shared.js'
|
|
26
|
+
import { resolveSpecDir, countAncestorSpecDirs, resolveChangeDir, triggerSync, getStageSteps, formatWaitOptions, checkApproval, didYouMean, assertSafeChangeName, detectQuickSessionDrift, detectWorktreeSpecDrift, resolveRuntimeRoot, writePlatformPointer, checkPlatformManaged, PLATFORM_MANAGED_FILENAME } from './shared.js'
|
|
27
27
|
import { resolveQuickLinkedChanges } from './quick-audit.js'
|
|
28
28
|
import { outputStep } from './prompt.js'
|
|
29
29
|
import { completeStep, skipStep, waitStep, continueStep } from './complete.js'
|
|
@@ -125,6 +125,31 @@ export async function ensureStageSteps(progress, stageName, cwd, specDir = null)
|
|
|
125
125
|
return false
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* run <stage> --help/-h 的用法帮助(runCommand 内 --help 短路调用)。
|
|
130
|
+
* 只打印不落盘:帮助查询不该有副作用(建会话/写 QUICKLOG)。
|
|
131
|
+
*/
|
|
132
|
+
function printStageUsage(stageName) {
|
|
133
|
+
const stage = stageRegistry[stageName]
|
|
134
|
+
console.log(`
|
|
135
|
+
sillyspec run ${stageName}${stage && stage.title ? ` — ${stage.title}` : ''}${stage && stage.description ? `\n ${stage.description}` : ''}
|
|
136
|
+
|
|
137
|
+
用法:
|
|
138
|
+
sillyspec run ${stageName} 开始/继续该阶段(输出下一步 prompt)
|
|
139
|
+
sillyspec run ${stageName} --status 查看阶段进度
|
|
140
|
+
sillyspec run ${stageName} --done --output "摘要" [--input "用户原话"] 完成当前步骤
|
|
141
|
+
sillyspec run ${stageName} --change <名> 指定变更(多活跃变更必填)
|
|
142
|
+
|
|
143
|
+
通用参数: --done --output --input --status --skip --reset --reopen --from-step
|
|
144
|
+
--wait --continue --answer --change --spec-dir --non-interactive
|
|
145
|
+
--interactive --skip-approval --json(不支持)
|
|
146
|
+
quick 专属: --linked-changes none|a,b --files a.js,b.js --allow-new
|
|
147
|
+
--allow-delete --force-baseline --confirm --file-notes
|
|
148
|
+
scan 专属: --quick --standard --deep --force-rescan
|
|
149
|
+
archive专属: --confirm
|
|
150
|
+
`)
|
|
151
|
+
}
|
|
152
|
+
|
|
128
153
|
/**
|
|
129
154
|
* sillyspec run <stage> 主命令
|
|
130
155
|
*/
|
|
@@ -289,31 +314,27 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
289
314
|
}
|
|
290
315
|
}
|
|
291
316
|
}
|
|
317
|
+
// ── 平台接管声明 fail-closed(入口二,D-B@v2)──
|
|
318
|
+
// runCommand 有独立指针恢复链(不经 resolvePlatformSpecDir),指针缺失时若不在此
|
|
319
|
+
// 封堵,run/quick/scan 等全部 stage 命令会静默落本地库(与入口一行为分裂)。
|
|
320
|
+
// 触发条件:命令行没传平台参数 + 指针与 platform-scan.json 皆未命中 + 接管声明存在。
|
|
321
|
+
// 显式 --spec-dir/--spec-root 传参时 platformOpts.specRoot 已赋值,天然不进此分支(逃生口)。
|
|
322
|
+
if (!platformOpts.specRoot && !platformOpts.runtimeRoot && !platformFileExists) {
|
|
323
|
+
const decl = checkPlatformManaged(cwd)
|
|
324
|
+
if (decl) {
|
|
325
|
+
console.error(`❌ 平台接管声明生效:本项目已由平台托管(原 specRoot: ${decl.specRoot || '(未记录)'}),但恢复指针缺失,拒绝静默回退本地模式。`)
|
|
326
|
+
console.error(` 声明文件: ${join(cwd, PLATFORM_MANAGED_FILENAME)}`)
|
|
327
|
+
console.error(' 恢复:① 重跑平台 scan/init(带 --spec-root)重建指针;② 确认不再使用平台:sillyspec platform disconnect(删除接管声明);③ 显式 --spec-dir <路径> 临时指定目录。')
|
|
328
|
+
// exit(1) 而非邻近环境错的 exit(2):这是"状态保护阻断"(对齐 PointerUnreachableError
|
|
329
|
+
// 顶层 catch 语义),非"用法/环境错"——见 design.md §5.3 v2 复审观察 (a)
|
|
330
|
+
process.exit(1)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
292
333
|
// 持久化 platformOpts
|
|
293
|
-
// 在 specRoot/.runtime/ 写主文件,同时在 cwd
|
|
334
|
+
// 在 specRoot/.runtime/ 写主文件,同时在 cwd 下写恢复指针
|
|
335
|
+
// (writePlatformPointer 收敛双写逻辑,cmdInit 平台模式共用,防两处字段漂移)
|
|
294
336
|
if (platformOpts.specRoot || platformOpts.runtimeRoot) {
|
|
295
|
-
|
|
296
|
-
const { mkdirSync, writeFileSync } = await import('fs')
|
|
297
|
-
mkdirSync(join(specRoot, '.runtime'), { recursive: true })
|
|
298
|
-
writeFileSync(platformOptsFile, JSON.stringify({
|
|
299
|
-
specRoot: platformOpts.specRoot,
|
|
300
|
-
runtimeRoot: platformOpts.runtimeRoot,
|
|
301
|
-
workspaceId: platformOpts.workspaceId,
|
|
302
|
-
scanRunId: platformOpts.scanRunId,
|
|
303
|
-
savedAt: new Date().toISOString(),
|
|
304
|
-
}, null, 2) + '\n')
|
|
305
|
-
// 恢复指针:在 cwd 下写 .sillyspec-platform.json(不在 .sillyspec 内,不污染源码结构)
|
|
306
|
-
// 供后续 --done(不带 --spec-root)找到 specDir
|
|
307
|
-
writeFileSync(join(cwd, '.sillyspec-platform.json'), JSON.stringify({
|
|
308
|
-
specRoot: platformOpts.specRoot,
|
|
309
|
-
runtimeRoot: platformOpts.runtimeRoot,
|
|
310
|
-
workspaceId: platformOpts.workspaceId,
|
|
311
|
-
scanRunId: platformOpts.scanRunId,
|
|
312
|
-
savedAt: new Date().toISOString(),
|
|
313
|
-
}, null, 2) + '\n')
|
|
314
|
-
} catch {
|
|
315
|
-
// 静默失败,不影响主流程
|
|
316
|
-
}
|
|
337
|
+
writePlatformPointer(cwd, platformOpts)
|
|
317
338
|
}
|
|
318
339
|
|
|
319
340
|
// 统一规范基路径:平台模式用 specRoot,本地模式用 cwd/.sillyspec
|
|
@@ -513,6 +534,7 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
513
534
|
'--json', '--dir', '--help',
|
|
514
535
|
'--reopen', '--from-step', '--mode',
|
|
515
536
|
'--deep', '--quick', '--standard', // scan profile 三档显式选择(scan-profile.js 从 argv 读;互斥见下方 PROFILE_FLAGS 检测)
|
|
537
|
+
'-h',
|
|
516
538
|
])
|
|
517
539
|
for (let i = 0; i < flags.length; i++) {
|
|
518
540
|
const f = flags[i]
|
|
@@ -531,6 +553,14 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
531
553
|
}
|
|
532
554
|
}
|
|
533
555
|
|
|
556
|
+
// ── --help/-h 短路:flag 校验通过后、任何副作用(cwd 纠正/会话创建/QUICKLOG 落盘)之前 ──
|
|
557
|
+
// --help 此前在 knownFlags 白名单里被静默吞掉,run quick --help 会误开 quick 会话+写骨架
|
|
558
|
+
// 条目(查询意图不该有副作用);-h 更是被当未知参数 exit 2。帮助查询 = 用法展示 = 退出 0。
|
|
559
|
+
if (flags.includes('--help') || flags.includes('-h')) {
|
|
560
|
+
printStageUsage(stageName)
|
|
561
|
+
process.exit(0)
|
|
562
|
+
}
|
|
563
|
+
|
|
534
564
|
const isAuxiliary = auxiliaryStages.includes(stageName)
|
|
535
565
|
// scan 元数据追踪(存储在 stageData.scanMeta 中,completeStep 通过 progress 访问)
|
|
536
566
|
|