sillyspec 3.25.8 → 3.25.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.
- package/.claude/skills/sillyspec-auto/SKILL.md +8 -13
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +8 -6
- package/.claude/skills/sillyspec-continue/SKILL.md +3 -3
- package/.claude/skills/sillyspec-doctor/SKILL.md +1 -1
- package/.claude/skills/sillyspec-execute/SKILL.md +10 -4
- package/.claude/skills/sillyspec-plan/SKILL.md +1 -1
- package/.claude/skills/sillyspec-verify/SKILL.md +1 -1
- package/README.md +9 -0
- package/package.json +2 -2
- package/src/change-list.js +11 -8
- package/src/change-risk-profile.js +22 -0
- package/src/db.js +160 -159
- package/src/dispatch/backends/local-agent.js +152 -0
- package/src/dispatch/backends/sillyhub-mcp.js +242 -0
- package/src/dispatch/probe.js +208 -0
- package/src/dispatch/strategy.js +121 -0
- package/src/doctor-diagnostics.js +19 -25
- package/src/fs-atomic.js +69 -69
- package/src/git-helper.js +66 -0
- package/src/hooks/worktree-guard.js +64 -55
- package/src/index.js +215 -34
- package/src/init.js +7 -14
- package/src/machine-interface.js +54 -19
- package/src/migrate.js +14 -4
- package/src/modules.js +16 -5
- package/src/progress/change-registry.js +72 -70
- package/src/progress/consistency-doctor.js +71 -14
- package/src/progress/shared.js +6 -3
- package/src/progress/stage-machine.js +61 -65
- package/src/progress/step-store.js +62 -67
- package/src/progress.js +155 -233
- package/src/quicklog.js +13 -5
- package/src/run/command.js +65 -37
- package/src/run/complete-handlers.js +1005 -889
- package/src/run/complete.js +115 -208
- package/src/run/concurrent-detect.js +132 -0
- package/src/run/gates.js +289 -17
- package/src/run/prompt.js +22 -68
- package/src/run/quick-audit.js +10 -9
- package/src/run/shared.js +648 -600
- package/src/run/stage.js +43 -24
- package/src/scan-postcheck.js +9 -31
- package/src/sillyhub-mcp/client.js +389 -0
- package/src/spec-dir-typo.js +71 -0
- package/src/stage-contract-engine.js +22 -1
- package/src/stage-contract-spec.js +11 -4
- package/src/stage-contract.js +26 -3
- package/src/stage-review.js +13 -9
- package/src/stages/archive.js +4 -6
- package/src/stages/brainstorm-auto.js +26 -20
- package/src/stages/brainstorm.js +29 -5
- package/src/stages/cmd-existence.js +145 -0
- package/src/stages/doctor.js +9 -6
- package/src/stages/execute.js +61 -20
- package/src/stages/plan-postcheck.js +152 -0
- package/src/stages/plan.js +16 -31
- package/src/stages/quick.js +1 -1
- package/src/stages/scan.js +9 -3
- package/src/stages/status.js +0 -1
- package/src/stages/verify.js +12 -7
- package/src/sync.js +4 -4
- package/src/task-review.js +30 -1
- package/src/verify-postcheck.js +933 -862
- package/src/version.js +19 -0
- package/src/worktree-apply.js +85 -68
- package/src/worktree-deps.js +89 -5
- package/src/worktree.js +213 -111
- package/templates/prompts/taskcard-rules.md +27 -0
- package/templates/prompts/verify-probes.md +3 -3
- package/.claude/skills/sillyspec-propose/SKILL.md +0 -87
- package/src/stages/propose.js +0 -181
package/src/quicklog.js
CHANGED
|
@@ -330,11 +330,19 @@ function flipEntryInContent(content, qlId, result, changedFiles = []) {
|
|
|
330
330
|
}
|
|
331
331
|
// 多行结果(结构化 需求/根因/方案/结果 字段块):逐行插入为独立字段行。
|
|
332
332
|
// 单行结果:保持「结果:<一句话>」一行,向后兼容简单用例。
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
333
|
+
const resultLines = /\r?\n/.test(body)
|
|
334
|
+
? body.split(/\r?\n/).filter(l => l.trim() !== '')
|
|
335
|
+
: [`结果:${body}`]
|
|
336
|
+
// 结果块属本条目:从 endIdx 往前跳过本条目尾部空行,在最后一个非空行之后插入。
|
|
337
|
+
// 否则结果块会落在尾空行「之后」,与下一条目标题紧贴(缺空行分隔),且被空行从本条目
|
|
338
|
+
// 「文件:」行隔开——视觉上像属于下一条目(用户实证多条目 QUICKLOG 间距 bug)。
|
|
339
|
+
let insertAt = endIdx
|
|
340
|
+
while (insertAt - 1 > startIdx && lines[insertAt - 1].trim() === '') insertAt--
|
|
341
|
+
lines.splice(insertAt, 0, ...resultLines)
|
|
342
|
+
// 兜底:结果块之后、下一个 ## 标题之间若无空行(条目原本无尾空行 / 轮转归档手改等),补一个。
|
|
343
|
+
const after = insertAt + resultLines.length
|
|
344
|
+
if (after < lines.length && lines[after].startsWith('## ')) {
|
|
345
|
+
lines.splice(after, 0, '')
|
|
338
346
|
}
|
|
339
347
|
}
|
|
340
348
|
return lines.join('\n')
|
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 } from './shared.js'
|
|
26
|
+
import { resolveSpecDir, countAncestorSpecDirs, resolveChangeDir, triggerSync, getStageSteps, formatWaitOptions, checkApproval, didYouMean, assertSafeChangeName, detectQuickSessionDrift, detectWorktreeSpecDrift, resolveRuntimeRoot } 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'
|
|
@@ -214,7 +214,6 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
214
214
|
const waitReason = getFlagValue('--reason')
|
|
215
215
|
const waitOptions = getFlagValue('--options')
|
|
216
216
|
const continueAnswer = getFlagValue('--answer')
|
|
217
|
-
const confirmMode = getFlagValue('--confirm-mode')
|
|
218
217
|
const resolvedSpecDir = specDir || getFlagValue('--spec-dir') || getFlagValue('--spec-root')
|
|
219
218
|
const platformOpts = {
|
|
220
219
|
specRoot: resolvedSpecDir ? resolve(resolvedSpecDir) : null,
|
|
@@ -291,7 +290,8 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
291
290
|
|
|
292
291
|
// 统一规范基路径:平台模式用 specRoot,本地模式用 cwd/.sillyspec
|
|
293
292
|
// runCommand 后续所有 .sillyspec/ 操作必须用 specBase
|
|
294
|
-
|
|
293
|
+
// let:下方 worktree 副本漂移守卫命中时锚回 wt.mainSpecBase(task-05/D-03)
|
|
294
|
+
let specBase = platformOpts.specRoot || join(cwd, '.sillyspec')
|
|
295
295
|
|
|
296
296
|
// 漂移提醒:cwd 祖先链 ≥2 个 .sillyspec = monorepo 多实例,当前命中的「最近」实例
|
|
297
297
|
// 可能不是用户意图的项目(如 cd 进被独立 scan 的子项目跑测试后忘回根)。
|
|
@@ -407,6 +407,7 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
407
407
|
// 3. 仍读不到 → 生成新 UUID(兼容旧行为;进度可能命中空行,由后续 pm.read 兜底)
|
|
408
408
|
const QUICK_SID_RE = /^quick-[0-9a-f]{8}$/
|
|
409
409
|
let quickSessionId = null
|
|
410
|
+
let quickFallbackUsed = false // Q7: --done 未带 --change 时 fallback 读 current-quick-run-id 命中(区别于显式 --change quick-<hex>)
|
|
410
411
|
if (stageName === 'quick') {
|
|
411
412
|
// 1373-1376 已把 quick 的 --change 值清进 linkedChanges、changeName 置 null。
|
|
412
413
|
// 此处回看 --change 原始值:若恰好是单个 quick-<8hex> → 识别为本会话 sessionId(精确恢复),
|
|
@@ -423,11 +424,11 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
423
424
|
const isDoneLike = isDone || isStatus || isSkip || isReset || isReopen
|
|
424
425
|
if (isDoneLike) {
|
|
425
426
|
try {
|
|
426
|
-
const runtimeRoot = platformOpts
|
|
427
|
+
const runtimeRoot = resolveRuntimeRoot(platformOpts, specRoot)
|
|
427
428
|
const idFile = join(runtimeRoot, 'current-quick-run-id')
|
|
428
429
|
if (existsSync(idFile)) {
|
|
429
430
|
const v = readFileSync(idFile, 'utf8').trim()
|
|
430
|
-
if (QUICK_SID_RE.test(v)) quickSessionId = v
|
|
431
|
+
if (QUICK_SID_RE.test(v)) { quickSessionId = v; quickFallbackUsed = true }
|
|
431
432
|
}
|
|
432
433
|
} catch {}
|
|
433
434
|
}
|
|
@@ -484,7 +485,8 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
484
485
|
const isAuxiliary = auxiliaryStages.includes(stageName)
|
|
485
486
|
// scan 元数据追踪(存储在 stageData.scanMeta 中,completeStep 通过 progress 访问)
|
|
486
487
|
|
|
487
|
-
|
|
488
|
+
// let:下方 worktree 副本漂移守卫命中时用主仓 specRoot 重建 pm(task-05/D-03)
|
|
489
|
+
let pm = new ProgressManager({ specDir: specRoot })
|
|
488
490
|
|
|
489
491
|
// quick 阶段:确定关联变更
|
|
490
492
|
// 优先级:--linked-changes / --change 显式 > 已持久化 quick-guard.json(--done 复用)> 交互式 > 非交互 fallback
|
|
@@ -522,18 +524,29 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
522
524
|
process.exit(2) // 用法错(execute 必须显式 --change)→ exit 2
|
|
523
525
|
}
|
|
524
526
|
|
|
525
|
-
// worktree
|
|
526
|
-
// checkout 副本
|
|
527
|
-
//
|
|
528
|
-
//
|
|
529
|
-
//
|
|
527
|
+
// worktree 副本漂移自动锚定守卫(坑 worktree-execute-spec-drift,D-03@v1):specBase 命中 worktree 内
|
|
528
|
+
// .sillyspec checkout 副本 = agent 误 cd 进 worktree 跑 plan/execute/verify/archive(100% 误操作场景)。
|
|
529
|
+
// 不 exit:自动把 specBase 锚回主仓 wt.mainSpecBase 后继续——进度/产出落主仓,不再写分裂副本(副本随
|
|
530
|
+
// 工作树清理丢失)。必须在 validateChangeExists 之前:副本里 change 目录真实存在,存在性校验会被骗
|
|
531
|
+
// 放行;锚定后用主仓 specBase 复查。覆盖 plan/execute/verify/archive(validateChangeExists 同 Set)。
|
|
532
|
+
// 关键坑:连带重写 specBase/specRoot/specDir + 重建 pm——四者同源派生(pm._customSpecDir 持旧 specRoot),
|
|
533
|
+
// 只改 specBase 会让下游 validateChangeExists(specBase) 与 pm.read(走 pm._customSpecDir) 仍指副本。
|
|
534
|
+
// 平台模式/显式 --spec-dir 跳过(已明确指定,与 line~285 warn / quick 守卫条件对齐)。
|
|
535
|
+
// 其他漂移(下方 changeMissing 守卫 / quick session drift 守卫)非副本场景,仍 exit(2) 不自动纠正。
|
|
530
536
|
if (!platformOpts.specRoot && !specDir
|
|
531
537
|
&& ['plan', 'execute', 'verify', 'archive'].includes(stageName)) {
|
|
532
538
|
const wt = detectWorktreeSpecDrift(specBase)
|
|
533
539
|
if (wt) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
540
|
+
specBase = wt.mainSpecBase
|
|
541
|
+
specRoot = wt.mainSpecBase
|
|
542
|
+
specDir = wt.mainSpecBase
|
|
543
|
+
pm = new ProgressManager({ specDir: specRoot })
|
|
544
|
+
// 【坑 execute-runs-isolation】下游 runtimeRoot 解析(execute-runs/stage-reviews 落点)经
|
|
545
|
+
// platformOpts 透传读此锚点,落主仓 .runtime,不随 worktree cleanup 整目录删消失。
|
|
546
|
+
// D-02:只参与 runtimeRoot 解析(resolveRuntimeRoot),绝不设 specRoot/runtimeRoot——
|
|
547
|
+
// 否则触发平台 sentinel 副作用(triggerSync/checkApproval 跳过 + prompt 误进平台渲染分支)。
|
|
548
|
+
platformOpts.specDriftAnchor = wt.mainSpecBase
|
|
549
|
+
console.warn(`⚠️ 已自动锚定主仓 spec:${wt.mainSpecBase}(原 cwd 命中 worktree 副本 ${wt.changeName},已纠正,流程继续)`)
|
|
537
550
|
}
|
|
538
551
|
}
|
|
539
552
|
|
|
@@ -564,13 +577,13 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
564
577
|
}
|
|
565
578
|
}
|
|
566
579
|
|
|
567
|
-
let progress =
|
|
580
|
+
let progress = pm.read(cwd, changeName)
|
|
568
581
|
|
|
569
582
|
if (!progress) {
|
|
570
583
|
// 如果指定了变更名或有变更目录,自动初始化变更的 progress
|
|
571
584
|
const autoChange = changeName || resolveChangeNameAuto(cwd, specRoot)
|
|
572
585
|
if (autoChange) {
|
|
573
|
-
progress =
|
|
586
|
+
progress = pm.initChange(cwd, autoChange)
|
|
574
587
|
} else if (isAuxiliary) {
|
|
575
588
|
let autoName = changeName || resolveChangeNameAuto(cwd, specRoot) || 'default'
|
|
576
589
|
// archive 特例:归档后变更从活跃列表排除(listChanges WHERE status='active'),
|
|
@@ -585,14 +598,14 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
585
598
|
.pop()
|
|
586
599
|
if (latest) {
|
|
587
600
|
autoName = latest.replace(/^\d{4}-\d{2}-\d{2}-/, '')
|
|
588
|
-
progress =
|
|
601
|
+
progress = pm.read(cwd, autoName)
|
|
589
602
|
}
|
|
590
603
|
}
|
|
591
604
|
} catch {}
|
|
592
605
|
}
|
|
593
606
|
changeName = autoName
|
|
594
607
|
if (!progress) {
|
|
595
|
-
progress =
|
|
608
|
+
progress = pm.initChange(cwd, autoName)
|
|
596
609
|
// initChange 可能因 project 表为空返回 null
|
|
597
610
|
if (!progress) {
|
|
598
611
|
progress = { currentStage: stageName, stages: {}, lastActive: new Date().toLocaleString('zh-CN', { hour12: false }), project: '' }
|
|
@@ -612,7 +625,7 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
612
625
|
console.log(`🔄 自动创建变更:${autoName}`)
|
|
613
626
|
console.log(` 提示:可以用 --change <名称> 指定自定义变更名`)
|
|
614
627
|
console.log(` 或事后重命名:sillyspec change-rename ${autoName} <新名称>`)
|
|
615
|
-
progress =
|
|
628
|
+
progress = pm.initChange(cwd, autoName)
|
|
616
629
|
changeName = autoName
|
|
617
630
|
} else {
|
|
618
631
|
console.error('❌ 未找到进度数据,请先运行 sillyspec init 或指定 --change <变更名>')
|
|
@@ -624,6 +637,21 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
624
637
|
// 确保 progress 有 currentChange
|
|
625
638
|
const effectiveChange = changeName || progress.currentChange || resolveChangeName(cwd, progress, specRoot)
|
|
626
639
|
|
|
640
|
+
// ── Q7:quick --done 不带 --change 时 fallback 读 current-quick-run-id 可能命中他者会话 ──
|
|
641
|
+
// 并发两 quick 会话:B 后启动覆盖 A 的 id(last-writer-wins),A 的 --done 不带 --change → 读到 B 的 sessionId
|
|
642
|
+
// → 误操作 B 的 progress/QUICKLOG。fallback 命中的会话若已完成或无可推进步骤(id stale / 他者已收尾),
|
|
643
|
+
// 拒绝推进并要求显式 --change。(live A/B race 需 per-session 锚 commit,见 review Q2/D2,本守卫只兜 stale/completed。)
|
|
644
|
+
if (stageName === 'quick' && isDone && quickFallbackUsed) {
|
|
645
|
+
const qs = progress.stages.quick
|
|
646
|
+
const hasActionable = qs?.steps && qs.steps.some(s => s.status === 'pending' || s.status === 'waiting' || s.status === 'in-progress')
|
|
647
|
+
if (!qs || qs.status === 'completed' || !hasActionable) {
|
|
648
|
+
console.error(`\n❌ --done 未带 --change,回退读到的 quick 会话 ${changeName} 已不可推进(已完成或无待办步骤)。`)
|
|
649
|
+
console.error(` 并发多 quick 会话时 .runtime/current-quick-run-id 可能指向他者会话(last-writer-wins)。`)
|
|
650
|
+
console.error(` 请显式带 --change <quick-session-id> 指定要完成的会话(sillyspec run quick --status 查各会话进度)。`)
|
|
651
|
+
process.exit(2) // 用法错(fallback 命中 stale/他者会话)→ exit 2
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
627
655
|
// -- auto 模式:自动推进所有流程阶段
|
|
628
656
|
if (stageName === 'auto') {
|
|
629
657
|
return await runAutoMode(pm, progress, cwd, flags, effectiveChange, platformOpts)
|
|
@@ -632,7 +660,7 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
632
660
|
// --change 只作为变更名标识,不再拦截流程
|
|
633
661
|
// 注册变更到全局活跃列表(如果尚未注册)
|
|
634
662
|
if (effectiveChange) {
|
|
635
|
-
|
|
663
|
+
pm.registerChange(cwd, effectiveChange)
|
|
636
664
|
}
|
|
637
665
|
|
|
638
666
|
// --reset
|
|
@@ -669,12 +697,12 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
669
697
|
if (freshSteps && freshSteps.length > 0) {
|
|
670
698
|
if (!progress.stages[stageName]) progress.stages[stageName] = { status: 'stale', steps: [] }
|
|
671
699
|
progress.stages[stageName].steps = freshSteps.map(s => ({ name: s.name, status: 'pending' }))
|
|
672
|
-
|
|
673
|
-
progress =
|
|
700
|
+
pm._write(cwd, progress, effectiveChange)
|
|
701
|
+
progress = pm.read(cwd, effectiveChange) || progress
|
|
674
702
|
}
|
|
675
703
|
}
|
|
676
704
|
|
|
677
|
-
const result =
|
|
705
|
+
const result = pm.reopenStage(cwd, stageName, {
|
|
678
706
|
fromStep: fromStepValue,
|
|
679
707
|
changeName: effectiveChange,
|
|
680
708
|
})
|
|
@@ -692,7 +720,7 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
692
720
|
console.log('')
|
|
693
721
|
|
|
694
722
|
// 重新读取 progress
|
|
695
|
-
progress =
|
|
723
|
+
progress = pm.read(cwd, effectiveChange) || progress
|
|
696
724
|
|
|
697
725
|
// 注入 revision context 到 platformOpts,供 outputStep 使用
|
|
698
726
|
const stageData = progress.stages[stageName]
|
|
@@ -724,7 +752,7 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
724
752
|
// currentIdx === -1 自动重置(~1944 行);in-progress 输出当前 step;全新由
|
|
725
753
|
// ensureStageSteps 初始化;显式从头用 --reset(已由 !isReset 排除出本分支)。
|
|
726
754
|
try {
|
|
727
|
-
const runtimeRoot = platformOpts
|
|
755
|
+
const runtimeRoot = resolveRuntimeRoot(platformOpts, specRoot)
|
|
728
756
|
mkdirSync(runtimeRoot, { recursive: true })
|
|
729
757
|
writeAtomicSync(join(runtimeRoot, 'current-quick-run-id'), quickSessionId + '\n')
|
|
730
758
|
} catch (e) {
|
|
@@ -740,9 +768,9 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
740
768
|
// 确保步骤已初始化
|
|
741
769
|
const changed = await ensureStageSteps(progress, stageName, cwd, specRoot)
|
|
742
770
|
if (changed && effectiveChange) {
|
|
743
|
-
|
|
771
|
+
pm._write(cwd, progress, effectiveChange)
|
|
744
772
|
triggerSync(cwd, effectiveChange, platformOpts)
|
|
745
|
-
progress =
|
|
773
|
+
progress = pm.read(cwd, effectiveChange) || progress
|
|
746
774
|
}
|
|
747
775
|
|
|
748
776
|
// --status
|
|
@@ -772,7 +800,7 @@ export async function runCommand(args, cwd, specDir = null, opts = {}) {
|
|
|
772
800
|
// --done
|
|
773
801
|
if (isDone) {
|
|
774
802
|
const doneAnswer = getFlagValue('--answer')
|
|
775
|
-
return await completeStep(pm, progress, stageName, cwd, outputText, inputText, { confirm: isConfirm, changeName: effectiveChange, nonInteractive: isNonInteractive && !isInteractive, platformOpts,
|
|
803
|
+
return await completeStep(pm, progress, stageName, cwd, outputText, inputText, { confirm: isConfirm, changeName: effectiveChange, nonInteractive: isNonInteractive && !isInteractive, platformOpts, doneAnswer, isForceBaseline, isAllowNew })
|
|
776
804
|
}
|
|
777
805
|
|
|
778
806
|
// 默认:输出当前步骤
|
|
@@ -898,7 +926,7 @@ async function resetStage(pm, progress, stageName, cwd, changeName, platformOpts
|
|
|
898
926
|
steps: defSteps ? defSteps.map(s => ({ name: s.name, status: 'pending' })) : []
|
|
899
927
|
}
|
|
900
928
|
progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
|
|
901
|
-
|
|
929
|
+
pm._write(cwd, progress, changeName)
|
|
902
930
|
triggerSync(cwd, changeName, platformOpts)
|
|
903
931
|
console.log(`🔄 ${stageName} 阶段已重置`)
|
|
904
932
|
}
|
|
@@ -948,18 +976,18 @@ async function runAutoMode(pm, progress, cwd, flags, changeName, platformOpts =
|
|
|
948
976
|
completedAt: null,
|
|
949
977
|
steps: brainstormAutoDef.steps.map(s => ({ name: s.name, status: 'pending' }))
|
|
950
978
|
}
|
|
951
|
-
|
|
979
|
+
pm._write(cwd, progress, changeName)
|
|
952
980
|
triggerSync(cwd, changeName, platformOpts)
|
|
953
|
-
progress =
|
|
981
|
+
progress = pm.read(cwd, changeName)
|
|
954
982
|
return progress
|
|
955
983
|
}
|
|
956
984
|
}
|
|
957
985
|
const changed = await ensureStageSteps(progress, stage, cwd)
|
|
958
986
|
if (stageChanged || changed) {
|
|
959
|
-
|
|
987
|
+
pm._write(cwd, progress, changeName)
|
|
960
988
|
triggerSync(cwd, changeName, platformOpts)
|
|
961
989
|
}
|
|
962
|
-
progress =
|
|
990
|
+
progress = pm.read(cwd, changeName)
|
|
963
991
|
return progress
|
|
964
992
|
}
|
|
965
993
|
|
|
@@ -1051,7 +1079,7 @@ async function runAutoMode(pm, progress, cwd, flags, changeName, platformOpts =
|
|
|
1051
1079
|
|
|
1052
1080
|
const result = await completeStep(pm, progress, currentStage, cwd, outputText, inputText, { printNext: false, changeName, platformOpts })
|
|
1053
1081
|
if (!result) return
|
|
1054
|
-
progress =
|
|
1082
|
+
progress = pm.read(cwd, changeName)
|
|
1055
1083
|
|
|
1056
1084
|
const nextPendingIdx = progress.stages[currentStage]?.steps?.findIndex(step => step.status === 'pending' || step.status === 'in-progress') ?? -1
|
|
1057
1085
|
if (nextPendingIdx !== -1) {
|
|
@@ -1084,7 +1112,7 @@ async function runAutoMode(pm, progress, cwd, flags, changeName, platformOpts =
|
|
|
1084
1112
|
if (currentStage === 'brainstorm' && next === 'plan') {
|
|
1085
1113
|
const changeDir = resolveChangeDir(cwd, progress, platformOpts?.specRoot || null)
|
|
1086
1114
|
if (changeDir) {
|
|
1087
|
-
const nextActionFile = join(changeDir, '
|
|
1115
|
+
const nextActionFile = join(changeDir, 'next-action.json')
|
|
1088
1116
|
try {
|
|
1089
1117
|
const nextAction = JSON.parse(readFileSync(nextActionFile, 'utf8'))
|
|
1090
1118
|
if (nextAction.has_blocking_questions === true) {
|
|
@@ -1115,9 +1143,9 @@ async function runAutoMode(pm, progress, cwd, flags, changeName, platformOpts =
|
|
|
1115
1143
|
}
|
|
1116
1144
|
progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
|
|
1117
1145
|
await ensureStageSteps(progress, next, cwd)
|
|
1118
|
-
|
|
1146
|
+
pm._write(cwd, progress, changeName)
|
|
1119
1147
|
triggerSync(cwd, changeName, platformOpts)
|
|
1120
|
-
progress =
|
|
1148
|
+
progress = pm.read(cwd, changeName)
|
|
1121
1149
|
|
|
1122
1150
|
console.log(`\n${currentStage} complete. Auto advanced to ${next}.`)
|
|
1123
1151
|
const nextSteps = await getAutoSteps(next)
|