sillyspec 3.27.12 → 3.28.2
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-execute/SKILL.md +214 -214
- package/.claude/skills/sillyspec-plan/SKILL.md +156 -156
- package/.claude/skills/sillyspec-quick/SKILL.md +116 -116
- package/package.json +1 -1
- package/src/agent-session-log.js +77 -10
- package/src/archive-delta.js +419 -0
- package/src/config-schema.js +2 -2
- package/src/contract-matrix.js +15 -2
- package/src/design-facts.js +354 -0
- package/src/docs-check.js +79 -8
- package/src/doctor-diagnostics.js +75 -3
- package/src/endpoint-baseline.js +109 -0
- package/src/endpoint-extractor.js +100 -24
- package/src/git-helper.js +31 -0
- package/src/index.js +248 -8
- package/src/machine-interface.js +63 -0
- package/src/modules.js +41 -12
- package/src/progress/change-registry.js +20 -0
- package/src/progress/consistency-doctor.js +124 -5
- package/src/progress/stage-machine.js +118 -2
- package/src/progress.js +6 -0
- package/src/quicklog.js +51 -0
- package/src/run/command.js +71 -2
- package/src/run/complete-handlers.js +84 -7
- package/src/run/complete.js +58 -5
- package/src/run/gates.js +333 -2
- package/src/run/prompt.js +70 -0
- package/src/run/quick-audit.js +98 -0
- package/src/run/scan-profile.js +98 -6
- package/src/run/shared.js +66 -1
- package/src/run/stage.js +25 -12
- package/src/scan-diff.js +54 -1
- package/src/scan-facts.js +173 -0
- package/src/scan-postcheck.js +110 -3
- package/src/spec-sync.js +210 -16
- package/src/stage-review.js +62 -3
- package/src/stages/archive.js +5 -5
- package/src/stages/brainstorm.js +10 -10
- package/src/stages/execute.js +21 -15
- package/src/stages/plan-postcheck.js +194 -1
- package/src/stages/plan.js +6 -2
- package/src/stages/scan.js +44 -102
- package/src/stages/verify.js +9 -7
- package/src/sync.js +122 -4
- package/src/task-review.js +51 -4
- package/src/taskcard.js +8 -1
- package/src/verify-postcheck.js +670 -10
- package/src/verify-probes.js +92 -10
- package/src/workflow.js +30 -0
- package/templates/agents-instruction.md +1 -1
- package/templates/prompts/taskcard-rules.md +5 -0
- package/templates/workflows/scan-docs.yaml +6 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-archive/SKILL.md +0 -87
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-auto/SKILL.md +0 -93
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-brainstorm/SKILL.md +0 -114
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-commit/SKILL.md +0 -67
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-continue/SKILL.md +0 -47
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-doctor/SKILL.md +0 -76
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-execute/SKILL.md +0 -214
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-explore/SKILL.md +0 -102
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-export/SKILL.md +0 -57
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-init/SKILL.md +0 -177
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-plan/SKILL.md +0 -156
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-quick/SKILL.md +0 -116
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-resume/SKILL.md +0 -70
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-scan/SKILL.md +0 -79
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-state/SKILL.md +0 -64
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-status/SKILL.md +0 -54
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-verify/SKILL.md +0 -87
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-workspace/SKILL.md +0 -175
- package/.claude/worktrees/sillyspec-data-model-research/.gitattributes +0 -2
- package/.claude/worktrees/sillyspec-data-model-research/.husky/pre-push +0 -20
- package/.claude/worktrees/sillyspec-data-model-research/README.md +0 -203
- package/.claude/worktrees/sillyspec-data-model-research/SKILL.md +0 -91
- package/.claude/worktrees/sillyspec-data-model-research/bin/sillyspec.js +0 -2
- package/.claude/worktrees/sillyspec-data-model-research/logo.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/package-lock.json +0 -977
- package/.claude/worktrees/sillyspec-data-model-research/package.json +0 -39
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-Bq_Z2hne.js +0 -7446
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/favicon.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/index.html +0 -16
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/logo.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-dashboard.html +0 -836
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-overview.html +0 -256
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/package.json +0 -25
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/executor.js +0 -95
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/index.js +0 -679
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/parser.js +0 -526
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/watcher.js +0 -357
- package/.claude/worktrees/sillyspec-data-model-research/src/agent-session-log.js +0 -803
- package/.claude/worktrees/sillyspec-data-model-research/src/change-list.js +0 -290
- package/.claude/worktrees/sillyspec-data-model-research/src/change-risk-profile.js +0 -270
- package/.claude/worktrees/sillyspec-data-model-research/src/check-primitives.js +0 -65
- package/.claude/worktrees/sillyspec-data-model-research/src/classify-change.js +0 -109
- package/.claude/worktrees/sillyspec-data-model-research/src/commit-suggest.js +0 -202
- package/.claude/worktrees/sillyspec-data-model-research/src/config-cat.js +0 -88
- package/.claude/worktrees/sillyspec-data-model-research/src/config-schema.js +0 -328
- package/.claude/worktrees/sillyspec-data-model-research/src/constants.js +0 -97
- package/.claude/worktrees/sillyspec-data-model-research/src/contract-matrix.js +0 -480
- package/.claude/worktrees/sillyspec-data-model-research/src/datetime.js +0 -20
- package/.claude/worktrees/sillyspec-data-model-research/src/db-engine.js +0 -55
- package/.claude/worktrees/sillyspec-data-model-research/src/db.js +0 -381
- package/.claude/worktrees/sillyspec-data-model-research/src/decision-distill.js +0 -517
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/local-agent.js +0 -155
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/sillyhub-mcp.js +0 -245
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/probe.js +0 -246
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/strategy.js +0 -121
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-check.js +0 -825
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-debt.js +0 -393
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-gate.js +0 -107
- package/.claude/worktrees/sillyspec-data-model-research/src/doctor-diagnostics.js +0 -1099
- package/.claude/worktrees/sillyspec-data-model-research/src/endpoint-extractor.js +0 -420
- package/.claude/worktrees/sillyspec-data-model-research/src/foreign-declared.js +0 -78
- package/.claude/worktrees/sillyspec-data-model-research/src/fs-atomic.js +0 -73
- package/.claude/worktrees/sillyspec-data-model-research/src/git-helper.js +0 -150
- package/.claude/worktrees/sillyspec-data-model-research/src/hooks/claude-pre-tool-use.cjs +0 -130
- package/.claude/worktrees/sillyspec-data-model-research/src/hooks/worktree-guard.js +0 -892
- package/.claude/worktrees/sillyspec-data-model-research/src/index.js +0 -2798
- package/.claude/worktrees/sillyspec-data-model-research/src/init.js +0 -626
- package/.claude/worktrees/sillyspec-data-model-research/src/knowledge-match.js +0 -231
- package/.claude/worktrees/sillyspec-data-model-research/src/local-detect.js +0 -128
- package/.claude/worktrees/sillyspec-data-model-research/src/local-register.js +0 -104
- package/.claude/worktrees/sillyspec-data-model-research/src/machine-interface.js +0 -543
- package/.claude/worktrees/sillyspec-data-model-research/src/migrate.js +0 -127
- package/.claude/worktrees/sillyspec-data-model-research/src/module-impact.js +0 -166
- package/.claude/worktrees/sillyspec-data-model-research/src/module-resolve.js +0 -222
- package/.claude/worktrees/sillyspec-data-model-research/src/modules.js +0 -580
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/change-registry.js +0 -336
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/consistency-doctor.js +0 -355
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/shared.js +0 -36
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/stage-machine.js +0 -594
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/step-store.js +0 -265
- package/.claude/worktrees/sillyspec-data-model-research/src/progress.js +0 -1304
- package/.claude/worktrees/sillyspec-data-model-research/src/quick-recommend.js +0 -115
- package/.claude/worktrees/sillyspec-data-model-research/src/quicklog.js +0 -852
- package/.claude/worktrees/sillyspec-data-model-research/src/review-tier.js +0 -77
- package/.claude/worktrees/sillyspec-data-model-research/src/run/command.js +0 -1595
- package/.claude/worktrees/sillyspec-data-model-research/src/run/complete-handlers.js +0 -1552
- package/.claude/worktrees/sillyspec-data-model-research/src/run/complete.js +0 -1259
- package/.claude/worktrees/sillyspec-data-model-research/src/run/concurrent-detect.js +0 -156
- package/.claude/worktrees/sillyspec-data-model-research/src/run/gates.js +0 -1084
- package/.claude/worktrees/sillyspec-data-model-research/src/run/multi-repo-context.js +0 -208
- package/.claude/worktrees/sillyspec-data-model-research/src/run/next.js +0 -128
- package/.claude/worktrees/sillyspec-data-model-research/src/run/prompt.js +0 -843
- package/.claude/worktrees/sillyspec-data-model-research/src/run/quick-audit.js +0 -186
- package/.claude/worktrees/sillyspec-data-model-research/src/run/scan-profile.js +0 -269
- package/.claude/worktrees/sillyspec-data-model-research/src/run/shared.js +0 -1562
- package/.claude/worktrees/sillyspec-data-model-research/src/run/stage.js +0 -584
- package/.claude/worktrees/sillyspec-data-model-research/src/run.js +0 -23
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-diff.js +0 -388
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-postcheck.js +0 -512
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-staleness.js +0 -95
- package/.claude/worktrees/sillyspec-data-model-research/src/setup.js +0 -414
- package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/client.js +0 -597
- package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/config.js +0 -62
- package/.claude/worktrees/sillyspec-data-model-research/src/spec-dir-typo.js +0 -50
- package/.claude/worktrees/sillyspec-data-model-research/src/spec-sync.js +0 -358
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-engine.js +0 -171
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-spec.js +0 -550
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract.js +0 -889
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-review.js +0 -597
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/archive.js +0 -199
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm-auto.js +0 -236
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm.js +0 -617
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/cmd-existence.js +0 -145
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/doctor.js +0 -540
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/execute.js +0 -1275
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/explore.js +0 -43
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/index.js +0 -29
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/knowledge.js +0 -514
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan-postcheck.js +0 -1344
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan.js +0 -650
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/quick.js +0 -167
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/scan.js +0 -608
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/status.js +0 -64
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/verify.js +0 -246
- package/.claude/worktrees/sillyspec-data-model-research/src/sync.js +0 -1470
- package/.claude/worktrees/sillyspec-data-model-research/src/task-review.js +0 -1498
- package/.claude/worktrees/sillyspec-data-model-research/src/taskcard.js +0 -210
- package/.claude/worktrees/sillyspec-data-model-research/src/verify-postcheck.js +0 -1718
- package/.claude/worktrees/sillyspec-data-model-research/src/verify-probes.js +0 -277
- package/.claude/worktrees/sillyspec-data-model-research/src/version.js +0 -19
- package/.claude/worktrees/sillyspec-data-model-research/src/workflow.js +0 -600
- package/.claude/worktrees/sillyspec-data-model-research/src/workspace.js +0 -125
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree-apply.js +0 -1446
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree-deps.js +0 -576
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree.js +0 -1715
- package/.claude/worktrees/sillyspec-data-model-research/templates/claude-instruction.md +0 -23
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/taskcard-rules.md +0 -28
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/testcase-design.md +0 -12
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/verify-probes.md +0 -32
- package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/archive-impact.yaml +0 -79
- package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/scan-docs.yaml +0 -132
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-archive/SKILL.md +0 -87
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-auto/SKILL.md +0 -93
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-brainstorm/SKILL.md +0 -114
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-commit/SKILL.md +0 -67
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-continue/SKILL.md +0 -47
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-doctor/SKILL.md +0 -76
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-execute/SKILL.md +0 -214
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-explore/SKILL.md +0 -102
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-export/SKILL.md +0 -57
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-init/SKILL.md +0 -177
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-plan/SKILL.md +0 -156
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-propose/SKILL.md +0 -87
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-quick/SKILL.md +0 -116
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-resume/SKILL.md +0 -70
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-scan/SKILL.md +0 -79
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-state/SKILL.md +0 -64
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-status/SKILL.md +0 -54
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-verify/SKILL.md +0 -87
- package/.worktrees/2c37b1d7/.claude/skills/sillyspec-workspace/SKILL.md +0 -175
- package/.worktrees/2c37b1d7/.claude/skills/verify-per-user/SKILL.md +0 -6
- package/.worktrees/2c37b1d7/.gitattributes +0 -2
- package/.worktrees/2c37b1d7/.husky/pre-push +0 -20
- package/.worktrees/2c37b1d7/README.md +0 -203
- package/.worktrees/2c37b1d7/SKILL.md +0 -91
- package/.worktrees/2c37b1d7/architecture-analysis.md +0 -448
- package/.worktrees/2c37b1d7/bin/sillyspec.js +0 -2
- package/.worktrees/2c37b1d7/eval/README.md +0 -86
- package/.worktrees/2c37b1d7/eval/eval.config.json +0 -28
- package/.worktrees/2c37b1d7/eval/lib/adapters/cli.mjs +0 -63
- package/.worktrees/2c37b1d7/eval/lib/adapters/dryrun.mjs +0 -27
- package/.worktrees/2c37b1d7/eval/lib/config.mjs +0 -82
- package/.worktrees/2c37b1d7/eval/lib/db.mjs +0 -76
- package/.worktrees/2c37b1d7/eval/lib/report.mjs +0 -120
- package/.worktrees/2c37b1d7/eval/lib/runner.mjs +0 -157
- package/.worktrees/2c37b1d7/eval/lib/tasks.mjs +0 -73
- package/.worktrees/2c37b1d7/eval/lib/util.mjs +0 -107
- package/.worktrees/2c37b1d7/eval/lib/workdir.mjs +0 -30
- package/.worktrees/2c37b1d7/eval/run.mjs +0 -83
- package/.worktrees/2c37b1d7/eval/tasks/001-slugify/files/package.json +0 -6
- package/.worktrees/2c37b1d7/eval/tasks/001-slugify/solution/src/slugify.mjs +0 -26
- package/.worktrees/2c37b1d7/eval/tasks/001-slugify/task.json +0 -6
- package/.worktrees/2c37b1d7/eval/tasks/001-slugify/verify.mjs +0 -28
- package/.worktrees/2c37b1d7/eval/tasks/002-each-day/files/package.json +0 -6
- package/.worktrees/2c37b1d7/eval/tasks/002-each-day/files/src/dates.mjs +0 -4
- package/.worktrees/2c37b1d7/eval/tasks/002-each-day/solution/src/dates.mjs +0 -30
- package/.worktrees/2c37b1d7/eval/tasks/002-each-day/task.json +0 -6
- package/.worktrees/2c37b1d7/eval/tasks/002-each-day/verify.mjs +0 -31
- package/.worktrees/2c37b1d7/eval/tasks/003-word-report/files/data/notes.txt +0 -5
- package/.worktrees/2c37b1d7/eval/tasks/003-word-report/files/package.json +0 -6
- package/.worktrees/2c37b1d7/eval/tasks/003-word-report/solution/bin/report.mjs +0 -36
- package/.worktrees/2c37b1d7/eval/tasks/003-word-report/task.json +0 -6
- package/.worktrees/2c37b1d7/eval/tasks/003-word-report/verify.mjs +0 -38
- package/.worktrees/2c37b1d7/logo.jpg +0 -0
- package/.worktrees/2c37b1d7/package-lock.json +0 -977
- package/.worktrees/2c37b1d7/package.json +0 -41
- package/.worktrees/2c37b1d7/packages/dashboard/dist/assets/index-Bq_Z2hne.js +0 -7446
- package/.worktrees/2c37b1d7/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
- package/.worktrees/2c37b1d7/packages/dashboard/dist/favicon.jpg +0 -0
- package/.worktrees/2c37b1d7/packages/dashboard/dist/index.html +0 -16
- package/.worktrees/2c37b1d7/packages/dashboard/dist/logo.jpg +0 -0
- package/.worktrees/2c37b1d7/packages/dashboard/dist/prototype-dashboard.html +0 -836
- package/.worktrees/2c37b1d7/packages/dashboard/dist/prototype-overview.html +0 -256
- package/.worktrees/2c37b1d7/packages/dashboard/package.json +0 -25
- package/.worktrees/2c37b1d7/packages/dashboard/server/executor.js +0 -95
- package/.worktrees/2c37b1d7/packages/dashboard/server/index.js +0 -679
- package/.worktrees/2c37b1d7/packages/dashboard/server/parser.js +0 -526
- package/.worktrees/2c37b1d7/packages/dashboard/server/watcher.js +0 -357
- package/.worktrees/2c37b1d7/src/agent-session-log.js +0 -803
- package/.worktrees/2c37b1d7/src/change-list.js +0 -290
- package/.worktrees/2c37b1d7/src/change-risk-profile.js +0 -339
- package/.worktrees/2c37b1d7/src/check-primitives.js +0 -65
- package/.worktrees/2c37b1d7/src/classify-change.js +0 -109
- package/.worktrees/2c37b1d7/src/commit-suggest.js +0 -202
- package/.worktrees/2c37b1d7/src/config-cat.js +0 -88
- package/.worktrees/2c37b1d7/src/config-schema.js +0 -328
- package/.worktrees/2c37b1d7/src/constants.js +0 -97
- package/.worktrees/2c37b1d7/src/contract-matrix.js +0 -533
- package/.worktrees/2c37b1d7/src/datetime.js +0 -20
- package/.worktrees/2c37b1d7/src/db-engine.js +0 -55
- package/.worktrees/2c37b1d7/src/db.js +0 -381
- package/.worktrees/2c37b1d7/src/decision-distill.js +0 -517
- package/.worktrees/2c37b1d7/src/dispatch/backends/local-agent.js +0 -155
- package/.worktrees/2c37b1d7/src/dispatch/backends/sillyhub-mcp.js +0 -245
- package/.worktrees/2c37b1d7/src/dispatch/probe.js +0 -246
- package/.worktrees/2c37b1d7/src/dispatch/strategy.js +0 -121
- package/.worktrees/2c37b1d7/src/docs-check.js +0 -825
- package/.worktrees/2c37b1d7/src/docs-debt.js +0 -393
- package/.worktrees/2c37b1d7/src/docs-gate.js +0 -107
- package/.worktrees/2c37b1d7/src/doctor-diagnostics.js +0 -1099
- package/.worktrees/2c37b1d7/src/endpoint-extractor.js +0 -409
- package/.worktrees/2c37b1d7/src/foreign-declared.js +0 -155
- package/.worktrees/2c37b1d7/src/fs-atomic.js +0 -73
- package/.worktrees/2c37b1d7/src/git-helper.js +0 -150
- package/.worktrees/2c37b1d7/src/hooks/claude-pre-tool-use.cjs +0 -130
- package/.worktrees/2c37b1d7/src/hooks/worktree-guard.js +0 -892
- package/.worktrees/2c37b1d7/src/index.js +0 -2854
- package/.worktrees/2c37b1d7/src/init.js +0 -626
- package/.worktrees/2c37b1d7/src/knowledge-match.js +0 -231
- package/.worktrees/2c37b1d7/src/local-detect.js +0 -128
- package/.worktrees/2c37b1d7/src/local-register.js +0 -104
- package/.worktrees/2c37b1d7/src/machine-interface.js +0 -543
- package/.worktrees/2c37b1d7/src/migrate.js +0 -127
- package/.worktrees/2c37b1d7/src/module-impact.js +0 -166
- package/.worktrees/2c37b1d7/src/module-resolve.js +0 -222
- package/.worktrees/2c37b1d7/src/modules.js +0 -580
- package/.worktrees/2c37b1d7/src/plan-adopt-waves.js +0 -126
- package/.worktrees/2c37b1d7/src/progress/change-registry.js +0 -336
- package/.worktrees/2c37b1d7/src/progress/consistency-doctor.js +0 -355
- package/.worktrees/2c37b1d7/src/progress/shared.js +0 -36
- package/.worktrees/2c37b1d7/src/progress/stage-machine.js +0 -594
- package/.worktrees/2c37b1d7/src/progress/step-store.js +0 -265
- package/.worktrees/2c37b1d7/src/progress.js +0 -1304
- package/.worktrees/2c37b1d7/src/quick-recommend.js +0 -115
- package/.worktrees/2c37b1d7/src/quicklog.js +0 -883
- package/.worktrees/2c37b1d7/src/review-tier.js +0 -77
- package/.worktrees/2c37b1d7/src/run/command.js +0 -1641
- package/.worktrees/2c37b1d7/src/run/complete-handlers.js +0 -1552
- package/.worktrees/2c37b1d7/src/run/complete.js +0 -1259
- package/.worktrees/2c37b1d7/src/run/concurrent-detect.js +0 -156
- package/.worktrees/2c37b1d7/src/run/gates.js +0 -1111
- package/.worktrees/2c37b1d7/src/run/multi-repo-context.js +0 -208
- package/.worktrees/2c37b1d7/src/run/next.js +0 -128
- package/.worktrees/2c37b1d7/src/run/prompt.js +0 -843
- package/.worktrees/2c37b1d7/src/run/quick-audit.js +0 -186
- package/.worktrees/2c37b1d7/src/run/scan-profile.js +0 -269
- package/.worktrees/2c37b1d7/src/run/shared.js +0 -1562
- package/.worktrees/2c37b1d7/src/run/stage.js +0 -587
- package/.worktrees/2c37b1d7/src/run.js +0 -23
- package/.worktrees/2c37b1d7/src/scan-diff.js +0 -388
- package/.worktrees/2c37b1d7/src/scan-postcheck.js +0 -524
- package/.worktrees/2c37b1d7/src/scan-staleness.js +0 -95
- package/.worktrees/2c37b1d7/src/setup.js +0 -414
- package/.worktrees/2c37b1d7/src/sillyhub-mcp/client.js +0 -597
- package/.worktrees/2c37b1d7/src/sillyhub-mcp/config.js +0 -62
- package/.worktrees/2c37b1d7/src/spec-dir-typo.js +0 -50
- package/.worktrees/2c37b1d7/src/spec-sync.js +0 -358
- package/.worktrees/2c37b1d7/src/stage-contract-engine.js +0 -171
- package/.worktrees/2c37b1d7/src/stage-contract-spec.js +0 -551
- package/.worktrees/2c37b1d7/src/stage-contract.js +0 -948
- package/.worktrees/2c37b1d7/src/stage-review.js +0 -597
- package/.worktrees/2c37b1d7/src/stages/archive.js +0 -199
- package/.worktrees/2c37b1d7/src/stages/brainstorm-auto.js +0 -285
- package/.worktrees/2c37b1d7/src/stages/brainstorm.js +0 -624
- package/.worktrees/2c37b1d7/src/stages/cmd-existence.js +0 -145
- package/.worktrees/2c37b1d7/src/stages/doctor.js +0 -540
- package/.worktrees/2c37b1d7/src/stages/execute.js +0 -1277
- package/.worktrees/2c37b1d7/src/stages/explore.js +0 -43
- package/.worktrees/2c37b1d7/src/stages/index.js +0 -29
- package/.worktrees/2c37b1d7/src/stages/knowledge.js +0 -514
- package/.worktrees/2c37b1d7/src/stages/plan-postcheck.js +0 -1398
- package/.worktrees/2c37b1d7/src/stages/plan.js +0 -655
- package/.worktrees/2c37b1d7/src/stages/quick.js +0 -167
- package/.worktrees/2c37b1d7/src/stages/scan.js +0 -608
- package/.worktrees/2c37b1d7/src/stages/status.js +0 -64
- package/.worktrees/2c37b1d7/src/stages/verify.js +0 -246
- package/.worktrees/2c37b1d7/src/sync.js +0 -1470
- package/.worktrees/2c37b1d7/src/task-review.js +0 -1498
- package/.worktrees/2c37b1d7/src/taskcard-placeholders.js +0 -21
- package/.worktrees/2c37b1d7/src/taskcard.js +0 -250
- package/.worktrees/2c37b1d7/src/verify-postcheck.js +0 -1718
- package/.worktrees/2c37b1d7/src/verify-probes.js +0 -327
- package/.worktrees/2c37b1d7/src/version.js +0 -19
- package/.worktrees/2c37b1d7/src/workflow.js +0 -600
- package/.worktrees/2c37b1d7/src/workspace.js +0 -125
- package/.worktrees/2c37b1d7/src/worktree-apply.js +0 -1611
- package/.worktrees/2c37b1d7/src/worktree-deps.js +0 -668
- package/.worktrees/2c37b1d7/src/worktree.js +0 -1849
- package/.worktrees/2c37b1d7/templates/claude-instruction.md +0 -23
- package/.worktrees/2c37b1d7/templates/prompts/taskcard-rules.md +0 -29
- package/.worktrees/2c37b1d7/templates/prompts/testcase-design.md +0 -12
- package/.worktrees/2c37b1d7/templates/prompts/verify-probes.md +0 -32
- package/.worktrees/2c37b1d7/templates/workflows/archive-impact.yaml +0 -79
- package/.worktrees/2c37b1d7/templates/workflows/scan-docs.yaml +0 -132
- package/.worktrees/542c4415/.claude/skills/sillyspec-archive/SKILL.md +0 -87
- package/.worktrees/542c4415/.claude/skills/sillyspec-auto/SKILL.md +0 -93
- package/.worktrees/542c4415/.claude/skills/sillyspec-brainstorm/SKILL.md +0 -114
- package/.worktrees/542c4415/.claude/skills/sillyspec-commit/SKILL.md +0 -67
- package/.worktrees/542c4415/.claude/skills/sillyspec-continue/SKILL.md +0 -47
- package/.worktrees/542c4415/.claude/skills/sillyspec-doctor/SKILL.md +0 -76
- package/.worktrees/542c4415/.claude/skills/sillyspec-execute/SKILL.md +0 -214
- package/.worktrees/542c4415/.claude/skills/sillyspec-explore/SKILL.md +0 -102
- package/.worktrees/542c4415/.claude/skills/sillyspec-export/SKILL.md +0 -57
- package/.worktrees/542c4415/.claude/skills/sillyspec-init/SKILL.md +0 -177
- package/.worktrees/542c4415/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
- package/.worktrees/542c4415/.claude/skills/sillyspec-plan/SKILL.md +0 -156
- package/.worktrees/542c4415/.claude/skills/sillyspec-quick/SKILL.md +0 -116
- package/.worktrees/542c4415/.claude/skills/sillyspec-resume/SKILL.md +0 -70
- package/.worktrees/542c4415/.claude/skills/sillyspec-scan/SKILL.md +0 -79
- package/.worktrees/542c4415/.claude/skills/sillyspec-state/SKILL.md +0 -64
- package/.worktrees/542c4415/.claude/skills/sillyspec-status/SKILL.md +0 -54
- package/.worktrees/542c4415/.claude/skills/sillyspec-verify/SKILL.md +0 -87
- package/.worktrees/542c4415/.claude/skills/sillyspec-workspace/SKILL.md +0 -175
- package/.worktrees/542c4415/.gitattributes +0 -2
- package/.worktrees/542c4415/.husky/pre-push +0 -20
- package/.worktrees/542c4415/README.md +0 -203
- package/.worktrees/542c4415/SKILL.md +0 -91
- package/.worktrees/542c4415/architecture-analysis.md +0 -448
- package/.worktrees/542c4415/bin/sillyspec.js +0 -2
- package/.worktrees/542c4415/eval/README.md +0 -86
- package/.worktrees/542c4415/eval/eval.config.json +0 -28
- package/.worktrees/542c4415/eval/lib/adapters/cli.mjs +0 -63
- package/.worktrees/542c4415/eval/lib/adapters/dryrun.mjs +0 -27
- package/.worktrees/542c4415/eval/lib/config.mjs +0 -82
- package/.worktrees/542c4415/eval/lib/db.mjs +0 -76
- package/.worktrees/542c4415/eval/lib/report.mjs +0 -120
- package/.worktrees/542c4415/eval/lib/runner.mjs +0 -157
- package/.worktrees/542c4415/eval/lib/tasks.mjs +0 -73
- package/.worktrees/542c4415/eval/lib/util.mjs +0 -107
- package/.worktrees/542c4415/eval/lib/workdir.mjs +0 -30
- package/.worktrees/542c4415/eval/run.mjs +0 -83
- package/.worktrees/542c4415/eval/tasks/001-slugify/files/package.json +0 -6
- package/.worktrees/542c4415/eval/tasks/001-slugify/solution/src/slugify.mjs +0 -26
- package/.worktrees/542c4415/eval/tasks/001-slugify/task.json +0 -6
- package/.worktrees/542c4415/eval/tasks/001-slugify/verify.mjs +0 -28
- package/.worktrees/542c4415/eval/tasks/002-each-day/files/package.json +0 -6
- package/.worktrees/542c4415/eval/tasks/002-each-day/files/src/dates.mjs +0 -4
- package/.worktrees/542c4415/eval/tasks/002-each-day/solution/src/dates.mjs +0 -30
- package/.worktrees/542c4415/eval/tasks/002-each-day/task.json +0 -6
- package/.worktrees/542c4415/eval/tasks/002-each-day/verify.mjs +0 -31
- package/.worktrees/542c4415/eval/tasks/003-word-report/files/data/notes.txt +0 -5
- package/.worktrees/542c4415/eval/tasks/003-word-report/files/package.json +0 -6
- package/.worktrees/542c4415/eval/tasks/003-word-report/solution/bin/report.mjs +0 -36
- package/.worktrees/542c4415/eval/tasks/003-word-report/task.json +0 -6
- package/.worktrees/542c4415/eval/tasks/003-word-report/verify.mjs +0 -38
- package/.worktrees/542c4415/logo.jpg +0 -0
- package/.worktrees/542c4415/package-lock.json +0 -977
- package/.worktrees/542c4415/package.json +0 -41
- package/.worktrees/542c4415/packages/dashboard/dist/assets/index-Bq_Z2hne.js +0 -7446
- package/.worktrees/542c4415/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
- package/.worktrees/542c4415/packages/dashboard/dist/favicon.jpg +0 -0
- package/.worktrees/542c4415/packages/dashboard/dist/index.html +0 -16
- package/.worktrees/542c4415/packages/dashboard/dist/logo.jpg +0 -0
- package/.worktrees/542c4415/packages/dashboard/dist/prototype-dashboard.html +0 -836
- package/.worktrees/542c4415/packages/dashboard/dist/prototype-overview.html +0 -256
- package/.worktrees/542c4415/packages/dashboard/package.json +0 -25
- package/.worktrees/542c4415/packages/dashboard/server/executor.js +0 -95
- package/.worktrees/542c4415/packages/dashboard/server/index.js +0 -679
- package/.worktrees/542c4415/packages/dashboard/server/parser.js +0 -526
- package/.worktrees/542c4415/packages/dashboard/server/watcher.js +0 -357
- package/.worktrees/542c4415/src/agent-session-log.js +0 -803
- package/.worktrees/542c4415/src/change-list.js +0 -290
- package/.worktrees/542c4415/src/change-risk-profile.js +0 -339
- package/.worktrees/542c4415/src/check-primitives.js +0 -65
- package/.worktrees/542c4415/src/classify-change.js +0 -109
- package/.worktrees/542c4415/src/commit-suggest.js +0 -202
- package/.worktrees/542c4415/src/config-cat.js +0 -88
- package/.worktrees/542c4415/src/config-schema.js +0 -328
- package/.worktrees/542c4415/src/constants.js +0 -97
- package/.worktrees/542c4415/src/contract-matrix.js +0 -533
- package/.worktrees/542c4415/src/datetime.js +0 -20
- package/.worktrees/542c4415/src/db-engine.js +0 -55
- package/.worktrees/542c4415/src/db.js +0 -381
- package/.worktrees/542c4415/src/decision-distill.js +0 -517
- package/.worktrees/542c4415/src/dispatch/backends/local-agent.js +0 -155
- package/.worktrees/542c4415/src/dispatch/backends/sillyhub-mcp.js +0 -245
- package/.worktrees/542c4415/src/dispatch/probe.js +0 -246
- package/.worktrees/542c4415/src/dispatch/strategy.js +0 -121
- package/.worktrees/542c4415/src/docs-check.js +0 -825
- package/.worktrees/542c4415/src/docs-debt.js +0 -393
- package/.worktrees/542c4415/src/docs-gate.js +0 -107
- package/.worktrees/542c4415/src/doctor-diagnostics.js +0 -1099
- package/.worktrees/542c4415/src/endpoint-extractor.js +0 -409
- package/.worktrees/542c4415/src/foreign-declared.js +0 -155
- package/.worktrees/542c4415/src/fs-atomic.js +0 -73
- package/.worktrees/542c4415/src/git-helper.js +0 -150
- package/.worktrees/542c4415/src/hooks/claude-pre-tool-use.cjs +0 -130
- package/.worktrees/542c4415/src/hooks/worktree-guard.js +0 -892
- package/.worktrees/542c4415/src/index.js +0 -2854
- package/.worktrees/542c4415/src/init.js +0 -626
- package/.worktrees/542c4415/src/knowledge-match.js +0 -231
- package/.worktrees/542c4415/src/local-detect.js +0 -128
- package/.worktrees/542c4415/src/local-register.js +0 -104
- package/.worktrees/542c4415/src/machine-interface.js +0 -543
- package/.worktrees/542c4415/src/migrate.js +0 -127
- package/.worktrees/542c4415/src/module-impact.js +0 -166
- package/.worktrees/542c4415/src/module-resolve.js +0 -222
- package/.worktrees/542c4415/src/modules.js +0 -580
- package/.worktrees/542c4415/src/plan-adopt-waves.js +0 -126
- package/.worktrees/542c4415/src/progress/change-registry.js +0 -336
- package/.worktrees/542c4415/src/progress/consistency-doctor.js +0 -355
- package/.worktrees/542c4415/src/progress/shared.js +0 -36
- package/.worktrees/542c4415/src/progress/stage-machine.js +0 -594
- package/.worktrees/542c4415/src/progress/step-store.js +0 -265
- package/.worktrees/542c4415/src/progress.js +0 -1304
- package/.worktrees/542c4415/src/quick-recommend.js +0 -115
- package/.worktrees/542c4415/src/quicklog.js +0 -883
- package/.worktrees/542c4415/src/review-tier.js +0 -77
- package/.worktrees/542c4415/src/run/command.js +0 -1641
- package/.worktrees/542c4415/src/run/complete-handlers.js +0 -1552
- package/.worktrees/542c4415/src/run/complete.js +0 -1259
- package/.worktrees/542c4415/src/run/concurrent-detect.js +0 -156
- package/.worktrees/542c4415/src/run/gates.js +0 -1111
- package/.worktrees/542c4415/src/run/multi-repo-context.js +0 -208
- package/.worktrees/542c4415/src/run/next.js +0 -128
- package/.worktrees/542c4415/src/run/prompt.js +0 -843
- package/.worktrees/542c4415/src/run/quick-audit.js +0 -186
- package/.worktrees/542c4415/src/run/scan-profile.js +0 -269
- package/.worktrees/542c4415/src/run/shared.js +0 -1562
- package/.worktrees/542c4415/src/run/stage.js +0 -587
- package/.worktrees/542c4415/src/run.js +0 -23
- package/.worktrees/542c4415/src/scan-diff.js +0 -388
- package/.worktrees/542c4415/src/scan-postcheck.js +0 -524
- package/.worktrees/542c4415/src/scan-staleness.js +0 -95
- package/.worktrees/542c4415/src/setup.js +0 -414
- package/.worktrees/542c4415/src/sillyhub-mcp/client.js +0 -597
- package/.worktrees/542c4415/src/sillyhub-mcp/config.js +0 -62
- package/.worktrees/542c4415/src/spec-dir-typo.js +0 -50
- package/.worktrees/542c4415/src/spec-sync.js +0 -358
- package/.worktrees/542c4415/src/stage-contract-engine.js +0 -171
- package/.worktrees/542c4415/src/stage-contract-spec.js +0 -551
- package/.worktrees/542c4415/src/stage-contract.js +0 -948
- package/.worktrees/542c4415/src/stage-review.js +0 -597
- package/.worktrees/542c4415/src/stages/archive.js +0 -199
- package/.worktrees/542c4415/src/stages/brainstorm-auto.js +0 -285
- package/.worktrees/542c4415/src/stages/brainstorm.js +0 -624
- package/.worktrees/542c4415/src/stages/cmd-existence.js +0 -145
- package/.worktrees/542c4415/src/stages/doctor.js +0 -540
- package/.worktrees/542c4415/src/stages/execute.js +0 -1277
- package/.worktrees/542c4415/src/stages/explore.js +0 -43
- package/.worktrees/542c4415/src/stages/index.js +0 -29
- package/.worktrees/542c4415/src/stages/knowledge.js +0 -514
- package/.worktrees/542c4415/src/stages/plan-postcheck.js +0 -1398
- package/.worktrees/542c4415/src/stages/plan.js +0 -655
- package/.worktrees/542c4415/src/stages/quick.js +0 -167
- package/.worktrees/542c4415/src/stages/scan.js +0 -608
- package/.worktrees/542c4415/src/stages/status.js +0 -64
- package/.worktrees/542c4415/src/stages/verify.js +0 -246
- package/.worktrees/542c4415/src/sync.js +0 -1470
- package/.worktrees/542c4415/src/task-review.js +0 -1498
- package/.worktrees/542c4415/src/taskcard-placeholders.js +0 -21
- package/.worktrees/542c4415/src/taskcard.js +0 -250
- package/.worktrees/542c4415/src/verify-postcheck.js +0 -1718
- package/.worktrees/542c4415/src/verify-probes.js +0 -327
- package/.worktrees/542c4415/src/version.js +0 -19
- package/.worktrees/542c4415/src/workflow.js +0 -600
- package/.worktrees/542c4415/src/workspace.js +0 -125
- package/.worktrees/542c4415/src/worktree-apply.js +0 -1611
- package/.worktrees/542c4415/src/worktree-deps.js +0 -668
- package/.worktrees/542c4415/src/worktree.js +0 -1849
- package/.worktrees/542c4415/templates/claude-instruction.md +0 -23
- package/.worktrees/542c4415/templates/prompts/taskcard-rules.md +0 -29
- package/.worktrees/542c4415/templates/prompts/testcase-design.md +0 -12
- package/.worktrees/542c4415/templates/prompts/verify-probes.md +0 -32
- package/.worktrees/542c4415/templates/workflows/archive-impact.yaml +0 -79
- package/.worktrees/542c4415/templates/workflows/scan-docs.yaml +0 -132
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-archive/SKILL.md +0 -87
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-auto/SKILL.md +0 -93
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-brainstorm/SKILL.md +0 -114
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-commit/SKILL.md +0 -67
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-continue/SKILL.md +0 -47
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-doctor/SKILL.md +0 -76
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-execute/SKILL.md +0 -214
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-explore/SKILL.md +0 -102
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-export/SKILL.md +0 -57
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-init/SKILL.md +0 -177
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-plan/SKILL.md +0 -156
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-propose/SKILL.md +0 -87
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-quick/SKILL.md +0 -116
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-resume/SKILL.md +0 -70
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-scan/SKILL.md +0 -79
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-state/SKILL.md +0 -64
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-status/SKILL.md +0 -54
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-verify/SKILL.md +0 -87
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-workspace/SKILL.md +0 -175
- package/.worktrees/8d2d0392/.claude/skills/verify-per-user/SKILL.md +0 -6
- package/.worktrees/8d2d0392/.gitattributes +0 -2
- package/.worktrees/8d2d0392/.husky/pre-push +0 -20
- package/.worktrees/8d2d0392/README.md +0 -203
- package/.worktrees/8d2d0392/SKILL.md +0 -91
- package/.worktrees/8d2d0392/architecture-analysis.md +0 -448
- package/.worktrees/8d2d0392/bin/sillyspec.js +0 -2
- package/.worktrees/8d2d0392/eval/README.md +0 -86
- package/.worktrees/8d2d0392/eval/eval.config.json +0 -28
- package/.worktrees/8d2d0392/eval/lib/adapters/cli.mjs +0 -63
- package/.worktrees/8d2d0392/eval/lib/adapters/dryrun.mjs +0 -27
- package/.worktrees/8d2d0392/eval/lib/config.mjs +0 -82
- package/.worktrees/8d2d0392/eval/lib/db.mjs +0 -76
- package/.worktrees/8d2d0392/eval/lib/report.mjs +0 -120
- package/.worktrees/8d2d0392/eval/lib/runner.mjs +0 -157
- package/.worktrees/8d2d0392/eval/lib/tasks.mjs +0 -73
- package/.worktrees/8d2d0392/eval/lib/util.mjs +0 -107
- package/.worktrees/8d2d0392/eval/lib/workdir.mjs +0 -30
- package/.worktrees/8d2d0392/eval/run.mjs +0 -83
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/files/package.json +0 -6
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/solution/src/slugify.mjs +0 -26
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/task.json +0 -6
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/verify.mjs +0 -28
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/package.json +0 -6
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/src/dates.mjs +0 -4
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/solution/src/dates.mjs +0 -30
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/task.json +0 -6
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/verify.mjs +0 -31
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/data/notes.txt +0 -5
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/package.json +0 -6
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/solution/bin/report.mjs +0 -36
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/task.json +0 -6
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/verify.mjs +0 -38
- package/.worktrees/8d2d0392/logo.jpg +0 -0
- package/.worktrees/8d2d0392/package-lock.json +0 -977
- package/.worktrees/8d2d0392/package.json +0 -41
- package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-Bq_Z2hne.js +0 -7446
- package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
- package/.worktrees/8d2d0392/packages/dashboard/dist/favicon.jpg +0 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/index.html +0 -16
- package/.worktrees/8d2d0392/packages/dashboard/dist/logo.jpg +0 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-dashboard.html +0 -836
- package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-overview.html +0 -256
- package/.worktrees/8d2d0392/packages/dashboard/package.json +0 -25
- package/.worktrees/8d2d0392/packages/dashboard/server/executor.js +0 -95
- package/.worktrees/8d2d0392/packages/dashboard/server/index.js +0 -679
- package/.worktrees/8d2d0392/packages/dashboard/server/parser.js +0 -526
- package/.worktrees/8d2d0392/packages/dashboard/server/watcher.js +0 -357
- package/.worktrees/8d2d0392/src/agent-session-log.js +0 -803
- package/.worktrees/8d2d0392/src/change-list.js +0 -290
- package/.worktrees/8d2d0392/src/change-risk-profile.js +0 -339
- package/.worktrees/8d2d0392/src/check-primitives.js +0 -65
- package/.worktrees/8d2d0392/src/classify-change.js +0 -109
- package/.worktrees/8d2d0392/src/commit-suggest.js +0 -202
- package/.worktrees/8d2d0392/src/config-cat.js +0 -88
- package/.worktrees/8d2d0392/src/config-schema.js +0 -328
- package/.worktrees/8d2d0392/src/constants.js +0 -97
- package/.worktrees/8d2d0392/src/contract-matrix.js +0 -533
- package/.worktrees/8d2d0392/src/datetime.js +0 -20
- package/.worktrees/8d2d0392/src/db-engine.js +0 -55
- package/.worktrees/8d2d0392/src/db.js +0 -381
- package/.worktrees/8d2d0392/src/decision-distill.js +0 -517
- package/.worktrees/8d2d0392/src/dispatch/backends/local-agent.js +0 -155
- package/.worktrees/8d2d0392/src/dispatch/backends/sillyhub-mcp.js +0 -245
- package/.worktrees/8d2d0392/src/dispatch/probe.js +0 -246
- package/.worktrees/8d2d0392/src/dispatch/strategy.js +0 -121
- package/.worktrees/8d2d0392/src/docs-check.js +0 -825
- package/.worktrees/8d2d0392/src/docs-debt.js +0 -393
- package/.worktrees/8d2d0392/src/docs-gate.js +0 -107
- package/.worktrees/8d2d0392/src/doctor-diagnostics.js +0 -1099
- package/.worktrees/8d2d0392/src/endpoint-extractor.js +0 -409
- package/.worktrees/8d2d0392/src/foreign-declared.js +0 -78
- package/.worktrees/8d2d0392/src/fs-atomic.js +0 -73
- package/.worktrees/8d2d0392/src/git-helper.js +0 -150
- package/.worktrees/8d2d0392/src/hooks/claude-pre-tool-use.cjs +0 -130
- package/.worktrees/8d2d0392/src/hooks/worktree-guard.js +0 -892
- package/.worktrees/8d2d0392/src/index.js +0 -2854
- package/.worktrees/8d2d0392/src/init.js +0 -626
- package/.worktrees/8d2d0392/src/knowledge-match.js +0 -231
- package/.worktrees/8d2d0392/src/local-detect.js +0 -128
- package/.worktrees/8d2d0392/src/local-register.js +0 -104
- package/.worktrees/8d2d0392/src/machine-interface.js +0 -543
- package/.worktrees/8d2d0392/src/migrate.js +0 -127
- package/.worktrees/8d2d0392/src/module-impact.js +0 -166
- package/.worktrees/8d2d0392/src/module-resolve.js +0 -222
- package/.worktrees/8d2d0392/src/modules.js +0 -580
- package/.worktrees/8d2d0392/src/plan-adopt-waves.js +0 -126
- package/.worktrees/8d2d0392/src/progress/change-registry.js +0 -336
- package/.worktrees/8d2d0392/src/progress/consistency-doctor.js +0 -355
- package/.worktrees/8d2d0392/src/progress/shared.js +0 -36
- package/.worktrees/8d2d0392/src/progress/stage-machine.js +0 -594
- package/.worktrees/8d2d0392/src/progress/step-store.js +0 -265
- package/.worktrees/8d2d0392/src/progress.js +0 -1304
- package/.worktrees/8d2d0392/src/quick-recommend.js +0 -115
- package/.worktrees/8d2d0392/src/quicklog.js +0 -883
- package/.worktrees/8d2d0392/src/review-tier.js +0 -77
- package/.worktrees/8d2d0392/src/run/command.js +0 -1641
- package/.worktrees/8d2d0392/src/run/complete-handlers.js +0 -1552
- package/.worktrees/8d2d0392/src/run/complete.js +0 -1259
- package/.worktrees/8d2d0392/src/run/concurrent-detect.js +0 -156
- package/.worktrees/8d2d0392/src/run/gates.js +0 -1094
- package/.worktrees/8d2d0392/src/run/multi-repo-context.js +0 -208
- package/.worktrees/8d2d0392/src/run/next.js +0 -128
- package/.worktrees/8d2d0392/src/run/prompt.js +0 -843
- package/.worktrees/8d2d0392/src/run/quick-audit.js +0 -186
- package/.worktrees/8d2d0392/src/run/scan-profile.js +0 -269
- package/.worktrees/8d2d0392/src/run/shared.js +0 -1562
- package/.worktrees/8d2d0392/src/run/stage.js +0 -587
- package/.worktrees/8d2d0392/src/run.js +0 -23
- package/.worktrees/8d2d0392/src/scan-diff.js +0 -388
- package/.worktrees/8d2d0392/src/scan-postcheck.js +0 -524
- package/.worktrees/8d2d0392/src/scan-staleness.js +0 -95
- package/.worktrees/8d2d0392/src/setup.js +0 -414
- package/.worktrees/8d2d0392/src/sillyhub-mcp/client.js +0 -597
- package/.worktrees/8d2d0392/src/sillyhub-mcp/config.js +0 -62
- package/.worktrees/8d2d0392/src/spec-dir-typo.js +0 -50
- package/.worktrees/8d2d0392/src/spec-sync.js +0 -358
- package/.worktrees/8d2d0392/src/stage-contract-engine.js +0 -171
- package/.worktrees/8d2d0392/src/stage-contract-spec.js +0 -551
- package/.worktrees/8d2d0392/src/stage-contract.js +0 -948
- package/.worktrees/8d2d0392/src/stage-review.js +0 -597
- package/.worktrees/8d2d0392/src/stages/archive.js +0 -199
- package/.worktrees/8d2d0392/src/stages/brainstorm-auto.js +0 -285
- package/.worktrees/8d2d0392/src/stages/brainstorm.js +0 -624
- package/.worktrees/8d2d0392/src/stages/cmd-existence.js +0 -145
- package/.worktrees/8d2d0392/src/stages/doctor.js +0 -540
- package/.worktrees/8d2d0392/src/stages/execute.js +0 -1277
- package/.worktrees/8d2d0392/src/stages/explore.js +0 -43
- package/.worktrees/8d2d0392/src/stages/index.js +0 -29
- package/.worktrees/8d2d0392/src/stages/knowledge.js +0 -514
- package/.worktrees/8d2d0392/src/stages/plan-postcheck.js +0 -1398
- package/.worktrees/8d2d0392/src/stages/plan.js +0 -651
- package/.worktrees/8d2d0392/src/stages/quick.js +0 -167
- package/.worktrees/8d2d0392/src/stages/scan.js +0 -608
- package/.worktrees/8d2d0392/src/stages/status.js +0 -64
- package/.worktrees/8d2d0392/src/stages/verify.js +0 -246
- package/.worktrees/8d2d0392/src/sync.js +0 -1470
- package/.worktrees/8d2d0392/src/task-review.js +0 -1498
- package/.worktrees/8d2d0392/src/taskcard-placeholders.js +0 -21
- package/.worktrees/8d2d0392/src/taskcard.js +0 -217
- package/.worktrees/8d2d0392/src/verify-postcheck.js +0 -1718
- package/.worktrees/8d2d0392/src/verify-probes.js +0 -327
- package/.worktrees/8d2d0392/src/version.js +0 -19
- package/.worktrees/8d2d0392/src/workflow.js +0 -600
- package/.worktrees/8d2d0392/src/workspace.js +0 -125
- package/.worktrees/8d2d0392/src/worktree-apply.js +0 -1611
- package/.worktrees/8d2d0392/src/worktree-deps.js +0 -576
- package/.worktrees/8d2d0392/src/worktree.js +0 -1838
- package/.worktrees/8d2d0392/templates/claude-instruction.md +0 -23
- package/.worktrees/8d2d0392/templates/prompts/taskcard-rules.md +0 -29
- package/.worktrees/8d2d0392/templates/prompts/testcase-design.md +0 -12
- package/.worktrees/8d2d0392/templates/prompts/verify-probes.md +0 -32
- package/.worktrees/8d2d0392/templates/workflows/archive-impact.yaml +0 -79
- package/.worktrees/8d2d0392/templates/workflows/scan-docs.yaml +0 -132
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-archive/SKILL.md +0 -87
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-auto/SKILL.md +0 -93
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-brainstorm/SKILL.md +0 -114
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-commit/SKILL.md +0 -67
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-continue/SKILL.md +0 -47
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-doctor/SKILL.md +0 -76
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-execute/SKILL.md +0 -214
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-explore/SKILL.md +0 -102
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-export/SKILL.md +0 -57
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-init/SKILL.md +0 -177
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-plan/SKILL.md +0 -156
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-quick/SKILL.md +0 -116
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-resume/SKILL.md +0 -70
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-scan/SKILL.md +0 -79
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-state/SKILL.md +0 -64
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-status/SKILL.md +0 -54
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-verify/SKILL.md +0 -87
- package/.worktrees/c1eae35e/.claude/skills/sillyspec-workspace/SKILL.md +0 -175
- package/.worktrees/c1eae35e/.gitattributes +0 -2
- package/.worktrees/c1eae35e/.husky/pre-push +0 -20
- package/.worktrees/c1eae35e/README.md +0 -203
- package/.worktrees/c1eae35e/SKILL.md +0 -91
- package/.worktrees/c1eae35e/architecture-analysis.md +0 -448
- package/.worktrees/c1eae35e/bin/sillyspec.js +0 -2
- package/.worktrees/c1eae35e/eval/README.md +0 -86
- package/.worktrees/c1eae35e/eval/eval.config.json +0 -28
- package/.worktrees/c1eae35e/eval/lib/adapters/cli.mjs +0 -63
- package/.worktrees/c1eae35e/eval/lib/adapters/dryrun.mjs +0 -27
- package/.worktrees/c1eae35e/eval/lib/config.mjs +0 -82
- package/.worktrees/c1eae35e/eval/lib/db.mjs +0 -76
- package/.worktrees/c1eae35e/eval/lib/report.mjs +0 -120
- package/.worktrees/c1eae35e/eval/lib/runner.mjs +0 -157
- package/.worktrees/c1eae35e/eval/lib/tasks.mjs +0 -73
- package/.worktrees/c1eae35e/eval/lib/util.mjs +0 -107
- package/.worktrees/c1eae35e/eval/lib/workdir.mjs +0 -30
- package/.worktrees/c1eae35e/eval/run.mjs +0 -83
- package/.worktrees/c1eae35e/eval/tasks/001-slugify/files/package.json +0 -6
- package/.worktrees/c1eae35e/eval/tasks/001-slugify/solution/src/slugify.mjs +0 -26
- package/.worktrees/c1eae35e/eval/tasks/001-slugify/task.json +0 -6
- package/.worktrees/c1eae35e/eval/tasks/001-slugify/verify.mjs +0 -28
- package/.worktrees/c1eae35e/eval/tasks/002-each-day/files/package.json +0 -6
- package/.worktrees/c1eae35e/eval/tasks/002-each-day/files/src/dates.mjs +0 -4
- package/.worktrees/c1eae35e/eval/tasks/002-each-day/solution/src/dates.mjs +0 -30
- package/.worktrees/c1eae35e/eval/tasks/002-each-day/task.json +0 -6
- package/.worktrees/c1eae35e/eval/tasks/002-each-day/verify.mjs +0 -31
- package/.worktrees/c1eae35e/eval/tasks/003-word-report/files/data/notes.txt +0 -5
- package/.worktrees/c1eae35e/eval/tasks/003-word-report/files/package.json +0 -6
- package/.worktrees/c1eae35e/eval/tasks/003-word-report/solution/bin/report.mjs +0 -36
- package/.worktrees/c1eae35e/eval/tasks/003-word-report/task.json +0 -6
- package/.worktrees/c1eae35e/eval/tasks/003-word-report/verify.mjs +0 -38
- package/.worktrees/c1eae35e/logo.jpg +0 -0
- package/.worktrees/c1eae35e/package-lock.json +0 -977
- package/.worktrees/c1eae35e/package.json +0 -41
- package/.worktrees/c1eae35e/packages/dashboard/dist/assets/index-Bq_Z2hne.js +0 -7446
- package/.worktrees/c1eae35e/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
- package/.worktrees/c1eae35e/packages/dashboard/dist/favicon.jpg +0 -0
- package/.worktrees/c1eae35e/packages/dashboard/dist/index.html +0 -16
- package/.worktrees/c1eae35e/packages/dashboard/dist/logo.jpg +0 -0
- package/.worktrees/c1eae35e/packages/dashboard/dist/prototype-dashboard.html +0 -836
- package/.worktrees/c1eae35e/packages/dashboard/dist/prototype-overview.html +0 -256
- package/.worktrees/c1eae35e/packages/dashboard/package.json +0 -25
- package/.worktrees/c1eae35e/packages/dashboard/server/executor.js +0 -95
- package/.worktrees/c1eae35e/packages/dashboard/server/index.js +0 -679
- package/.worktrees/c1eae35e/packages/dashboard/server/parser.js +0 -526
- package/.worktrees/c1eae35e/packages/dashboard/server/watcher.js +0 -357
- package/.worktrees/c1eae35e/src/agent-session-log.js +0 -803
- package/.worktrees/c1eae35e/src/change-list.js +0 -290
- package/.worktrees/c1eae35e/src/change-risk-profile.js +0 -339
- package/.worktrees/c1eae35e/src/check-primitives.js +0 -65
- package/.worktrees/c1eae35e/src/classify-change.js +0 -109
- package/.worktrees/c1eae35e/src/commit-suggest.js +0 -202
- package/.worktrees/c1eae35e/src/config-cat.js +0 -88
- package/.worktrees/c1eae35e/src/config-schema.js +0 -328
- package/.worktrees/c1eae35e/src/constants.js +0 -97
- package/.worktrees/c1eae35e/src/contract-matrix.js +0 -533
- package/.worktrees/c1eae35e/src/datetime.js +0 -20
- package/.worktrees/c1eae35e/src/db-engine.js +0 -55
- package/.worktrees/c1eae35e/src/db.js +0 -381
- package/.worktrees/c1eae35e/src/decision-distill.js +0 -517
- package/.worktrees/c1eae35e/src/dispatch/backends/local-agent.js +0 -155
- package/.worktrees/c1eae35e/src/dispatch/backends/sillyhub-mcp.js +0 -245
- package/.worktrees/c1eae35e/src/dispatch/probe.js +0 -246
- package/.worktrees/c1eae35e/src/dispatch/strategy.js +0 -121
- package/.worktrees/c1eae35e/src/docs-check.js +0 -825
- package/.worktrees/c1eae35e/src/docs-debt.js +0 -393
- package/.worktrees/c1eae35e/src/docs-gate.js +0 -107
- package/.worktrees/c1eae35e/src/doctor-diagnostics.js +0 -1099
- package/.worktrees/c1eae35e/src/endpoint-extractor.js +0 -409
- package/.worktrees/c1eae35e/src/foreign-declared.js +0 -155
- package/.worktrees/c1eae35e/src/fs-atomic.js +0 -73
- package/.worktrees/c1eae35e/src/git-helper.js +0 -150
- package/.worktrees/c1eae35e/src/hooks/claude-pre-tool-use.cjs +0 -130
- package/.worktrees/c1eae35e/src/hooks/worktree-guard.js +0 -892
- package/.worktrees/c1eae35e/src/index.js +0 -2854
- package/.worktrees/c1eae35e/src/init.js +0 -626
- package/.worktrees/c1eae35e/src/knowledge-match.js +0 -231
- package/.worktrees/c1eae35e/src/local-detect.js +0 -128
- package/.worktrees/c1eae35e/src/local-register.js +0 -104
- package/.worktrees/c1eae35e/src/machine-interface.js +0 -543
- package/.worktrees/c1eae35e/src/migrate.js +0 -127
- package/.worktrees/c1eae35e/src/module-impact.js +0 -166
- package/.worktrees/c1eae35e/src/module-resolve.js +0 -222
- package/.worktrees/c1eae35e/src/modules.js +0 -580
- package/.worktrees/c1eae35e/src/plan-adopt-waves.js +0 -126
- package/.worktrees/c1eae35e/src/progress/change-registry.js +0 -336
- package/.worktrees/c1eae35e/src/progress/consistency-doctor.js +0 -355
- package/.worktrees/c1eae35e/src/progress/shared.js +0 -36
- package/.worktrees/c1eae35e/src/progress/stage-machine.js +0 -594
- package/.worktrees/c1eae35e/src/progress/step-store.js +0 -265
- package/.worktrees/c1eae35e/src/progress.js +0 -1304
- package/.worktrees/c1eae35e/src/quick-recommend.js +0 -115
- package/.worktrees/c1eae35e/src/quicklog.js +0 -883
- package/.worktrees/c1eae35e/src/review-tier.js +0 -77
- package/.worktrees/c1eae35e/src/run/command.js +0 -1641
- package/.worktrees/c1eae35e/src/run/complete-handlers.js +0 -1552
- package/.worktrees/c1eae35e/src/run/complete.js +0 -1259
- package/.worktrees/c1eae35e/src/run/concurrent-detect.js +0 -156
- package/.worktrees/c1eae35e/src/run/gates.js +0 -1111
- package/.worktrees/c1eae35e/src/run/multi-repo-context.js +0 -208
- package/.worktrees/c1eae35e/src/run/next.js +0 -128
- package/.worktrees/c1eae35e/src/run/prompt.js +0 -843
- package/.worktrees/c1eae35e/src/run/quick-audit.js +0 -186
- package/.worktrees/c1eae35e/src/run/scan-profile.js +0 -269
- package/.worktrees/c1eae35e/src/run/shared.js +0 -1562
- package/.worktrees/c1eae35e/src/run/stage.js +0 -587
- package/.worktrees/c1eae35e/src/run.js +0 -23
- package/.worktrees/c1eae35e/src/scan-diff.js +0 -388
- package/.worktrees/c1eae35e/src/scan-postcheck.js +0 -524
- package/.worktrees/c1eae35e/src/scan-staleness.js +0 -95
- package/.worktrees/c1eae35e/src/setup.js +0 -414
- package/.worktrees/c1eae35e/src/sillyhub-mcp/client.js +0 -597
- package/.worktrees/c1eae35e/src/sillyhub-mcp/config.js +0 -62
- package/.worktrees/c1eae35e/src/spec-dir-typo.js +0 -50
- package/.worktrees/c1eae35e/src/spec-sync.js +0 -358
- package/.worktrees/c1eae35e/src/stage-contract-engine.js +0 -171
- package/.worktrees/c1eae35e/src/stage-contract-spec.js +0 -551
- package/.worktrees/c1eae35e/src/stage-contract.js +0 -948
- package/.worktrees/c1eae35e/src/stage-review.js +0 -597
- package/.worktrees/c1eae35e/src/stages/archive.js +0 -199
- package/.worktrees/c1eae35e/src/stages/brainstorm-auto.js +0 -285
- package/.worktrees/c1eae35e/src/stages/brainstorm.js +0 -624
- package/.worktrees/c1eae35e/src/stages/cmd-existence.js +0 -145
- package/.worktrees/c1eae35e/src/stages/doctor.js +0 -540
- package/.worktrees/c1eae35e/src/stages/execute.js +0 -1277
- package/.worktrees/c1eae35e/src/stages/explore.js +0 -43
- package/.worktrees/c1eae35e/src/stages/index.js +0 -29
- package/.worktrees/c1eae35e/src/stages/knowledge.js +0 -514
- package/.worktrees/c1eae35e/src/stages/plan-postcheck.js +0 -1398
- package/.worktrees/c1eae35e/src/stages/plan.js +0 -655
- package/.worktrees/c1eae35e/src/stages/quick.js +0 -167
- package/.worktrees/c1eae35e/src/stages/scan.js +0 -608
- package/.worktrees/c1eae35e/src/stages/status.js +0 -64
- package/.worktrees/c1eae35e/src/stages/verify.js +0 -246
- package/.worktrees/c1eae35e/src/sync.js +0 -1470
- package/.worktrees/c1eae35e/src/task-review.js +0 -1498
- package/.worktrees/c1eae35e/src/taskcard-placeholders.js +0 -21
- package/.worktrees/c1eae35e/src/taskcard.js +0 -250
- package/.worktrees/c1eae35e/src/verify-postcheck.js +0 -1718
- package/.worktrees/c1eae35e/src/verify-probes.js +0 -327
- package/.worktrees/c1eae35e/src/version.js +0 -19
- package/.worktrees/c1eae35e/src/workflow.js +0 -600
- package/.worktrees/c1eae35e/src/workspace.js +0 -125
- package/.worktrees/c1eae35e/src/worktree-apply.js +0 -1611
- package/.worktrees/c1eae35e/src/worktree-deps.js +0 -668
- package/.worktrees/c1eae35e/src/worktree.js +0 -1849
- package/.worktrees/c1eae35e/templates/claude-instruction.md +0 -23
- package/.worktrees/c1eae35e/templates/prompts/taskcard-rules.md +0 -29
- package/.worktrees/c1eae35e/templates/prompts/testcase-design.md +0 -12
- package/.worktrees/c1eae35e/templates/prompts/verify-probes.md +0 -32
- package/.worktrees/c1eae35e/templates/workflows/archive-impact.yaml +0 -79
- package/.worktrees/c1eae35e/templates/workflows/scan-docs.yaml +0 -132
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-archive/SKILL.md +0 -87
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-auto/SKILL.md +0 -93
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-brainstorm/SKILL.md +0 -114
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-commit/SKILL.md +0 -67
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-continue/SKILL.md +0 -47
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-doctor/SKILL.md +0 -76
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-execute/SKILL.md +0 -214
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-explore/SKILL.md +0 -102
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-export/SKILL.md +0 -57
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-init/SKILL.md +0 -177
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-plan/SKILL.md +0 -156
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-propose/SKILL.md +0 -87
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-quick/SKILL.md +0 -116
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-resume/SKILL.md +0 -70
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-scan/SKILL.md +0 -79
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-state/SKILL.md +0 -64
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-status/SKILL.md +0 -54
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-verify/SKILL.md +0 -87
- package/.worktrees/dafc71ad/.claude/skills/sillyspec-workspace/SKILL.md +0 -175
- package/.worktrees/dafc71ad/.gitattributes +0 -2
- package/.worktrees/dafc71ad/.husky/pre-push +0 -20
- package/.worktrees/dafc71ad/README.md +0 -203
- package/.worktrees/dafc71ad/SKILL.md +0 -91
- package/.worktrees/dafc71ad/analysis.md +0 -366
- package/.worktrees/dafc71ad/architecture-analysis.md +0 -448
- package/.worktrees/dafc71ad/bin/sillyspec.js +0 -2
- package/.worktrees/dafc71ad/eval/README.md +0 -86
- package/.worktrees/dafc71ad/eval/eval.config.json +0 -28
- package/.worktrees/dafc71ad/eval/lib/adapters/cli.mjs +0 -63
- package/.worktrees/dafc71ad/eval/lib/adapters/dryrun.mjs +0 -27
- package/.worktrees/dafc71ad/eval/lib/config.mjs +0 -82
- package/.worktrees/dafc71ad/eval/lib/db.mjs +0 -76
- package/.worktrees/dafc71ad/eval/lib/report.mjs +0 -120
- package/.worktrees/dafc71ad/eval/lib/runner.mjs +0 -157
- package/.worktrees/dafc71ad/eval/lib/tasks.mjs +0 -73
- package/.worktrees/dafc71ad/eval/lib/util.mjs +0 -107
- package/.worktrees/dafc71ad/eval/lib/workdir.mjs +0 -30
- package/.worktrees/dafc71ad/eval/run.mjs +0 -83
- package/.worktrees/dafc71ad/eval/tasks/001-slugify/files/package.json +0 -6
- package/.worktrees/dafc71ad/eval/tasks/001-slugify/solution/src/slugify.mjs +0 -26
- package/.worktrees/dafc71ad/eval/tasks/001-slugify/task.json +0 -6
- package/.worktrees/dafc71ad/eval/tasks/001-slugify/verify.mjs +0 -28
- package/.worktrees/dafc71ad/eval/tasks/002-each-day/files/package.json +0 -6
- package/.worktrees/dafc71ad/eval/tasks/002-each-day/files/src/dates.mjs +0 -4
- package/.worktrees/dafc71ad/eval/tasks/002-each-day/solution/src/dates.mjs +0 -30
- package/.worktrees/dafc71ad/eval/tasks/002-each-day/task.json +0 -6
- package/.worktrees/dafc71ad/eval/tasks/002-each-day/verify.mjs +0 -31
- package/.worktrees/dafc71ad/eval/tasks/003-word-report/files/data/notes.txt +0 -5
- package/.worktrees/dafc71ad/eval/tasks/003-word-report/files/package.json +0 -6
- package/.worktrees/dafc71ad/eval/tasks/003-word-report/solution/bin/report.mjs +0 -36
- package/.worktrees/dafc71ad/eval/tasks/003-word-report/task.json +0 -6
- package/.worktrees/dafc71ad/eval/tasks/003-word-report/verify.mjs +0 -38
- package/.worktrees/dafc71ad/logo.jpg +0 -0
- package/.worktrees/dafc71ad/package-lock.json +0 -977
- package/.worktrees/dafc71ad/package.json +0 -41
- package/.worktrees/dafc71ad/packages/dashboard/dist/assets/index-Bq_Z2hne.js +0 -7446
- package/.worktrees/dafc71ad/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
- package/.worktrees/dafc71ad/packages/dashboard/dist/favicon.jpg +0 -0
- package/.worktrees/dafc71ad/packages/dashboard/dist/index.html +0 -16
- package/.worktrees/dafc71ad/packages/dashboard/dist/logo.jpg +0 -0
- package/.worktrees/dafc71ad/packages/dashboard/dist/prototype-dashboard.html +0 -836
- package/.worktrees/dafc71ad/packages/dashboard/dist/prototype-overview.html +0 -256
- package/.worktrees/dafc71ad/packages/dashboard/package.json +0 -25
- package/.worktrees/dafc71ad/packages/dashboard/server/executor.js +0 -95
- package/.worktrees/dafc71ad/packages/dashboard/server/index.js +0 -679
- package/.worktrees/dafc71ad/packages/dashboard/server/parser.js +0 -526
- package/.worktrees/dafc71ad/packages/dashboard/server/watcher.js +0 -357
- package/.worktrees/dafc71ad/src/agent-session-log.js +0 -803
- package/.worktrees/dafc71ad/src/change-list.js +0 -290
- package/.worktrees/dafc71ad/src/change-risk-profile.js +0 -339
- package/.worktrees/dafc71ad/src/check-primitives.js +0 -65
- package/.worktrees/dafc71ad/src/classify-change.js +0 -109
- package/.worktrees/dafc71ad/src/commit-suggest.js +0 -202
- package/.worktrees/dafc71ad/src/config-cat.js +0 -88
- package/.worktrees/dafc71ad/src/config-schema.js +0 -328
- package/.worktrees/dafc71ad/src/constants.js +0 -97
- package/.worktrees/dafc71ad/src/contract-matrix.js +0 -533
- package/.worktrees/dafc71ad/src/datetime.js +0 -20
- package/.worktrees/dafc71ad/src/db-engine.js +0 -55
- package/.worktrees/dafc71ad/src/db.js +0 -381
- package/.worktrees/dafc71ad/src/decision-distill.js +0 -517
- package/.worktrees/dafc71ad/src/dispatch/backends/local-agent.js +0 -155
- package/.worktrees/dafc71ad/src/dispatch/backends/sillyhub-mcp.js +0 -245
- package/.worktrees/dafc71ad/src/dispatch/probe.js +0 -246
- package/.worktrees/dafc71ad/src/dispatch/strategy.js +0 -121
- package/.worktrees/dafc71ad/src/docs-check.js +0 -825
- package/.worktrees/dafc71ad/src/docs-debt.js +0 -393
- package/.worktrees/dafc71ad/src/docs-gate.js +0 -107
- package/.worktrees/dafc71ad/src/doctor-diagnostics.js +0 -1099
- package/.worktrees/dafc71ad/src/endpoint-extractor.js +0 -409
- package/.worktrees/dafc71ad/src/foreign-declared.js +0 -155
- package/.worktrees/dafc71ad/src/fs-atomic.js +0 -73
- package/.worktrees/dafc71ad/src/git-helper.js +0 -150
- package/.worktrees/dafc71ad/src/hooks/claude-pre-tool-use.cjs +0 -130
- package/.worktrees/dafc71ad/src/hooks/worktree-guard.js +0 -892
- package/.worktrees/dafc71ad/src/index.js +0 -2854
- package/.worktrees/dafc71ad/src/init.js +0 -626
- package/.worktrees/dafc71ad/src/knowledge-match.js +0 -231
- package/.worktrees/dafc71ad/src/local-detect.js +0 -128
- package/.worktrees/dafc71ad/src/local-register.js +0 -104
- package/.worktrees/dafc71ad/src/machine-interface.js +0 -543
- package/.worktrees/dafc71ad/src/migrate.js +0 -127
- package/.worktrees/dafc71ad/src/module-impact.js +0 -166
- package/.worktrees/dafc71ad/src/module-resolve.js +0 -222
- package/.worktrees/dafc71ad/src/modules.js +0 -580
- package/.worktrees/dafc71ad/src/plan-adopt-waves.js +0 -126
- package/.worktrees/dafc71ad/src/progress/change-registry.js +0 -336
- package/.worktrees/dafc71ad/src/progress/consistency-doctor.js +0 -355
- package/.worktrees/dafc71ad/src/progress/shared.js +0 -36
- package/.worktrees/dafc71ad/src/progress/stage-machine.js +0 -594
- package/.worktrees/dafc71ad/src/progress/step-store.js +0 -265
- package/.worktrees/dafc71ad/src/progress.js +0 -1304
- package/.worktrees/dafc71ad/src/quick-recommend.js +0 -115
- package/.worktrees/dafc71ad/src/quicklog.js +0 -883
- package/.worktrees/dafc71ad/src/review-tier.js +0 -77
- package/.worktrees/dafc71ad/src/run/command.js +0 -1641
- package/.worktrees/dafc71ad/src/run/complete-handlers.js +0 -1552
- package/.worktrees/dafc71ad/src/run/complete.js +0 -1259
- package/.worktrees/dafc71ad/src/run/concurrent-detect.js +0 -156
- package/.worktrees/dafc71ad/src/run/gates.js +0 -1111
- package/.worktrees/dafc71ad/src/run/multi-repo-context.js +0 -208
- package/.worktrees/dafc71ad/src/run/next.js +0 -128
- package/.worktrees/dafc71ad/src/run/prompt.js +0 -843
- package/.worktrees/dafc71ad/src/run/quick-audit.js +0 -186
- package/.worktrees/dafc71ad/src/run/scan-profile.js +0 -269
- package/.worktrees/dafc71ad/src/run/shared.js +0 -1562
- package/.worktrees/dafc71ad/src/run/stage.js +0 -587
- package/.worktrees/dafc71ad/src/run.js +0 -23
- package/.worktrees/dafc71ad/src/scan-diff.js +0 -388
- package/.worktrees/dafc71ad/src/scan-postcheck.js +0 -524
- package/.worktrees/dafc71ad/src/scan-staleness.js +0 -95
- package/.worktrees/dafc71ad/src/setup.js +0 -414
- package/.worktrees/dafc71ad/src/sillyhub-mcp/client.js +0 -597
- package/.worktrees/dafc71ad/src/sillyhub-mcp/config.js +0 -62
- package/.worktrees/dafc71ad/src/spec-dir-typo.js +0 -50
- package/.worktrees/dafc71ad/src/spec-sync.js +0 -358
- package/.worktrees/dafc71ad/src/stage-contract-engine.js +0 -171
- package/.worktrees/dafc71ad/src/stage-contract-spec.js +0 -551
- package/.worktrees/dafc71ad/src/stage-contract.js +0 -948
- package/.worktrees/dafc71ad/src/stage-review.js +0 -597
- package/.worktrees/dafc71ad/src/stages/archive.js +0 -199
- package/.worktrees/dafc71ad/src/stages/brainstorm-auto.js +0 -285
- package/.worktrees/dafc71ad/src/stages/brainstorm.js +0 -624
- package/.worktrees/dafc71ad/src/stages/cmd-existence.js +0 -145
- package/.worktrees/dafc71ad/src/stages/doctor.js +0 -540
- package/.worktrees/dafc71ad/src/stages/execute.js +0 -1277
- package/.worktrees/dafc71ad/src/stages/explore.js +0 -43
- package/.worktrees/dafc71ad/src/stages/index.js +0 -29
- package/.worktrees/dafc71ad/src/stages/knowledge.js +0 -514
- package/.worktrees/dafc71ad/src/stages/plan-postcheck.js +0 -1398
- package/.worktrees/dafc71ad/src/stages/plan.js +0 -655
- package/.worktrees/dafc71ad/src/stages/quick.js +0 -167
- package/.worktrees/dafc71ad/src/stages/scan.js +0 -608
- package/.worktrees/dafc71ad/src/stages/status.js +0 -64
- package/.worktrees/dafc71ad/src/stages/verify.js +0 -246
- package/.worktrees/dafc71ad/src/sync.js +0 -1470
- package/.worktrees/dafc71ad/src/task-review.js +0 -1498
- package/.worktrees/dafc71ad/src/taskcard-placeholders.js +0 -21
- package/.worktrees/dafc71ad/src/taskcard.js +0 -250
- package/.worktrees/dafc71ad/src/verify-postcheck.js +0 -1718
- package/.worktrees/dafc71ad/src/verify-probes.js +0 -327
- package/.worktrees/dafc71ad/src/version.js +0 -19
- package/.worktrees/dafc71ad/src/workflow.js +0 -600
- package/.worktrees/dafc71ad/src/workspace.js +0 -125
- package/.worktrees/dafc71ad/src/worktree-apply.js +0 -1611
- package/.worktrees/dafc71ad/src/worktree-deps.js +0 -668
- package/.worktrees/dafc71ad/src/worktree.js +0 -1849
- package/.worktrees/dafc71ad/templates/claude-instruction.md +0 -23
- package/.worktrees/dafc71ad/templates/prompts/taskcard-rules.md +0 -29
- package/.worktrees/dafc71ad/templates/prompts/testcase-design.md +0 -12
- package/.worktrees/dafc71ad/templates/prompts/verify-probes.md +0 -32
- package/.worktrees/dafc71ad/templates/workflows/archive-impact.yaml +0 -79
- package/.worktrees/dafc71ad/templates/workflows/scan-docs.yaml +0 -132
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-archive/SKILL.md +0 -87
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-auto/SKILL.md +0 -93
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-brainstorm/SKILL.md +0 -114
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-commit/SKILL.md +0 -67
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-continue/SKILL.md +0 -47
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-doctor/SKILL.md +0 -76
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-execute/SKILL.md +0 -214
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-explore/SKILL.md +0 -102
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-export/SKILL.md +0 -57
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-init/SKILL.md +0 -177
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-plan/SKILL.md +0 -156
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-propose/SKILL.md +0 -87
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-quick/SKILL.md +0 -116
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-resume/SKILL.md +0 -70
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-scan/SKILL.md +0 -79
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-state/SKILL.md +0 -64
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-status/SKILL.md +0 -54
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-verify/SKILL.md +0 -87
- package/.worktrees/e58fcc4c/.claude/skills/sillyspec-workspace/SKILL.md +0 -175
- package/.worktrees/e58fcc4c/.gitattributes +0 -2
- package/.worktrees/e58fcc4c/.husky/pre-push +0 -20
- package/.worktrees/e58fcc4c/README.md +0 -203
- package/.worktrees/e58fcc4c/SKILL.md +0 -91
- package/.worktrees/e58fcc4c/architecture-analysis.md +0 -503
- package/.worktrees/e58fcc4c/bin/sillyspec.js +0 -2
- package/.worktrees/e58fcc4c/eval/README.md +0 -86
- package/.worktrees/e58fcc4c/eval/eval.config.json +0 -28
- package/.worktrees/e58fcc4c/eval/lib/adapters/cli.mjs +0 -63
- package/.worktrees/e58fcc4c/eval/lib/adapters/dryrun.mjs +0 -27
- package/.worktrees/e58fcc4c/eval/lib/config.mjs +0 -82
- package/.worktrees/e58fcc4c/eval/lib/db.mjs +0 -76
- package/.worktrees/e58fcc4c/eval/lib/report.mjs +0 -120
- package/.worktrees/e58fcc4c/eval/lib/runner.mjs +0 -157
- package/.worktrees/e58fcc4c/eval/lib/tasks.mjs +0 -73
- package/.worktrees/e58fcc4c/eval/lib/util.mjs +0 -107
- package/.worktrees/e58fcc4c/eval/lib/workdir.mjs +0 -30
- package/.worktrees/e58fcc4c/eval/run.mjs +0 -83
- package/.worktrees/e58fcc4c/eval/tasks/001-slugify/files/package.json +0 -6
- package/.worktrees/e58fcc4c/eval/tasks/001-slugify/solution/src/slugify.mjs +0 -26
- package/.worktrees/e58fcc4c/eval/tasks/001-slugify/task.json +0 -6
- package/.worktrees/e58fcc4c/eval/tasks/001-slugify/verify.mjs +0 -28
- package/.worktrees/e58fcc4c/eval/tasks/002-each-day/files/package.json +0 -6
- package/.worktrees/e58fcc4c/eval/tasks/002-each-day/files/src/dates.mjs +0 -4
- package/.worktrees/e58fcc4c/eval/tasks/002-each-day/solution/src/dates.mjs +0 -30
- package/.worktrees/e58fcc4c/eval/tasks/002-each-day/task.json +0 -6
- package/.worktrees/e58fcc4c/eval/tasks/002-each-day/verify.mjs +0 -31
- package/.worktrees/e58fcc4c/eval/tasks/003-word-report/files/data/notes.txt +0 -5
- package/.worktrees/e58fcc4c/eval/tasks/003-word-report/files/package.json +0 -6
- package/.worktrees/e58fcc4c/eval/tasks/003-word-report/solution/bin/report.mjs +0 -36
- package/.worktrees/e58fcc4c/eval/tasks/003-word-report/task.json +0 -6
- package/.worktrees/e58fcc4c/eval/tasks/003-word-report/verify.mjs +0 -38
- package/.worktrees/e58fcc4c/logo.jpg +0 -0
- package/.worktrees/e58fcc4c/package-lock.json +0 -977
- package/.worktrees/e58fcc4c/package.json +0 -41
- package/.worktrees/e58fcc4c/packages/dashboard/dist/assets/index-Bq_Z2hne.js +0 -7446
- package/.worktrees/e58fcc4c/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
- package/.worktrees/e58fcc4c/packages/dashboard/dist/favicon.jpg +0 -0
- package/.worktrees/e58fcc4c/packages/dashboard/dist/index.html +0 -16
- package/.worktrees/e58fcc4c/packages/dashboard/dist/logo.jpg +0 -0
- package/.worktrees/e58fcc4c/packages/dashboard/dist/prototype-dashboard.html +0 -836
- package/.worktrees/e58fcc4c/packages/dashboard/dist/prototype-overview.html +0 -256
- package/.worktrees/e58fcc4c/packages/dashboard/package.json +0 -25
- package/.worktrees/e58fcc4c/packages/dashboard/server/executor.js +0 -95
- package/.worktrees/e58fcc4c/packages/dashboard/server/index.js +0 -679
- package/.worktrees/e58fcc4c/packages/dashboard/server/parser.js +0 -526
- package/.worktrees/e58fcc4c/packages/dashboard/server/watcher.js +0 -357
- package/.worktrees/e58fcc4c/src/agent-session-log.js +0 -803
- package/.worktrees/e58fcc4c/src/change-list.js +0 -290
- package/.worktrees/e58fcc4c/src/change-risk-profile.js +0 -339
- package/.worktrees/e58fcc4c/src/check-primitives.js +0 -65
- package/.worktrees/e58fcc4c/src/classify-change.js +0 -109
- package/.worktrees/e58fcc4c/src/commit-suggest.js +0 -202
- package/.worktrees/e58fcc4c/src/config-cat.js +0 -88
- package/.worktrees/e58fcc4c/src/config-schema.js +0 -328
- package/.worktrees/e58fcc4c/src/constants.js +0 -97
- package/.worktrees/e58fcc4c/src/contract-matrix.js +0 -533
- package/.worktrees/e58fcc4c/src/datetime.js +0 -20
- package/.worktrees/e58fcc4c/src/db-engine.js +0 -55
- package/.worktrees/e58fcc4c/src/db.js +0 -381
- package/.worktrees/e58fcc4c/src/decision-distill.js +0 -517
- package/.worktrees/e58fcc4c/src/dispatch/backends/local-agent.js +0 -155
- package/.worktrees/e58fcc4c/src/dispatch/backends/sillyhub-mcp.js +0 -245
- package/.worktrees/e58fcc4c/src/dispatch/probe.js +0 -246
- package/.worktrees/e58fcc4c/src/dispatch/strategy.js +0 -121
- package/.worktrees/e58fcc4c/src/docs-check.js +0 -825
- package/.worktrees/e58fcc4c/src/docs-debt.js +0 -393
- package/.worktrees/e58fcc4c/src/docs-gate.js +0 -107
- package/.worktrees/e58fcc4c/src/doctor-diagnostics.js +0 -1099
- package/.worktrees/e58fcc4c/src/endpoint-extractor.js +0 -409
- package/.worktrees/e58fcc4c/src/foreign-declared.js +0 -155
- package/.worktrees/e58fcc4c/src/fs-atomic.js +0 -73
- package/.worktrees/e58fcc4c/src/git-helper.js +0 -150
- package/.worktrees/e58fcc4c/src/hooks/claude-pre-tool-use.cjs +0 -130
- package/.worktrees/e58fcc4c/src/hooks/worktree-guard.js +0 -892
- package/.worktrees/e58fcc4c/src/index.js +0 -2854
- package/.worktrees/e58fcc4c/src/init.js +0 -626
- package/.worktrees/e58fcc4c/src/knowledge-match.js +0 -231
- package/.worktrees/e58fcc4c/src/local-detect.js +0 -128
- package/.worktrees/e58fcc4c/src/local-register.js +0 -104
- package/.worktrees/e58fcc4c/src/machine-interface.js +0 -543
- package/.worktrees/e58fcc4c/src/migrate.js +0 -127
- package/.worktrees/e58fcc4c/src/module-impact.js +0 -166
- package/.worktrees/e58fcc4c/src/module-resolve.js +0 -222
- package/.worktrees/e58fcc4c/src/modules.js +0 -580
- package/.worktrees/e58fcc4c/src/plan-adopt-waves.js +0 -126
- package/.worktrees/e58fcc4c/src/progress/change-registry.js +0 -336
- package/.worktrees/e58fcc4c/src/progress/consistency-doctor.js +0 -355
- package/.worktrees/e58fcc4c/src/progress/shared.js +0 -36
- package/.worktrees/e58fcc4c/src/progress/stage-machine.js +0 -594
- package/.worktrees/e58fcc4c/src/progress/step-store.js +0 -265
- package/.worktrees/e58fcc4c/src/progress.js +0 -1304
- package/.worktrees/e58fcc4c/src/quick-recommend.js +0 -115
- package/.worktrees/e58fcc4c/src/quicklog.js +0 -883
- package/.worktrees/e58fcc4c/src/review-tier.js +0 -77
- package/.worktrees/e58fcc4c/src/run/command.js +0 -1641
- package/.worktrees/e58fcc4c/src/run/complete-handlers.js +0 -1552
- package/.worktrees/e58fcc4c/src/run/complete.js +0 -1259
- package/.worktrees/e58fcc4c/src/run/concurrent-detect.js +0 -156
- package/.worktrees/e58fcc4c/src/run/gates.js +0 -1111
- package/.worktrees/e58fcc4c/src/run/multi-repo-context.js +0 -208
- package/.worktrees/e58fcc4c/src/run/next.js +0 -128
- package/.worktrees/e58fcc4c/src/run/prompt.js +0 -843
- package/.worktrees/e58fcc4c/src/run/quick-audit.js +0 -186
- package/.worktrees/e58fcc4c/src/run/scan-profile.js +0 -269
- package/.worktrees/e58fcc4c/src/run/shared.js +0 -1562
- package/.worktrees/e58fcc4c/src/run/stage.js +0 -587
- package/.worktrees/e58fcc4c/src/run.js +0 -23
- package/.worktrees/e58fcc4c/src/scan-diff.js +0 -388
- package/.worktrees/e58fcc4c/src/scan-postcheck.js +0 -524
- package/.worktrees/e58fcc4c/src/scan-staleness.js +0 -95
- package/.worktrees/e58fcc4c/src/setup.js +0 -414
- package/.worktrees/e58fcc4c/src/sillyhub-mcp/client.js +0 -597
- package/.worktrees/e58fcc4c/src/sillyhub-mcp/config.js +0 -62
- package/.worktrees/e58fcc4c/src/spec-dir-typo.js +0 -50
- package/.worktrees/e58fcc4c/src/spec-sync.js +0 -358
- package/.worktrees/e58fcc4c/src/stage-contract-engine.js +0 -171
- package/.worktrees/e58fcc4c/src/stage-contract-spec.js +0 -551
- package/.worktrees/e58fcc4c/src/stage-contract.js +0 -948
- package/.worktrees/e58fcc4c/src/stage-review.js +0 -597
- package/.worktrees/e58fcc4c/src/stages/archive.js +0 -199
- package/.worktrees/e58fcc4c/src/stages/brainstorm-auto.js +0 -285
- package/.worktrees/e58fcc4c/src/stages/brainstorm.js +0 -624
- package/.worktrees/e58fcc4c/src/stages/cmd-existence.js +0 -145
- package/.worktrees/e58fcc4c/src/stages/doctor.js +0 -540
- package/.worktrees/e58fcc4c/src/stages/execute.js +0 -1277
- package/.worktrees/e58fcc4c/src/stages/explore.js +0 -43
- package/.worktrees/e58fcc4c/src/stages/index.js +0 -29
- package/.worktrees/e58fcc4c/src/stages/knowledge.js +0 -514
- package/.worktrees/e58fcc4c/src/stages/plan-postcheck.js +0 -1398
- package/.worktrees/e58fcc4c/src/stages/plan.js +0 -655
- package/.worktrees/e58fcc4c/src/stages/quick.js +0 -167
- package/.worktrees/e58fcc4c/src/stages/scan.js +0 -608
- package/.worktrees/e58fcc4c/src/stages/status.js +0 -64
- package/.worktrees/e58fcc4c/src/stages/verify.js +0 -246
- package/.worktrees/e58fcc4c/src/sync.js +0 -1470
- package/.worktrees/e58fcc4c/src/task-review.js +0 -1498
- package/.worktrees/e58fcc4c/src/taskcard-placeholders.js +0 -21
- package/.worktrees/e58fcc4c/src/taskcard.js +0 -250
- package/.worktrees/e58fcc4c/src/verify-postcheck.js +0 -1718
- package/.worktrees/e58fcc4c/src/verify-probes.js +0 -327
- package/.worktrees/e58fcc4c/src/version.js +0 -19
- package/.worktrees/e58fcc4c/src/workflow.js +0 -600
- package/.worktrees/e58fcc4c/src/workspace.js +0 -125
- package/.worktrees/e58fcc4c/src/worktree-apply.js +0 -1611
- package/.worktrees/e58fcc4c/src/worktree-deps.js +0 -668
- package/.worktrees/e58fcc4c/src/worktree.js +0 -1849
- package/.worktrees/e58fcc4c/templates/claude-instruction.md +0 -23
- package/.worktrees/e58fcc4c/templates/prompts/taskcard-rules.md +0 -29
- package/.worktrees/e58fcc4c/templates/prompts/testcase-design.md +0 -12
- package/.worktrees/e58fcc4c/templates/prompts/verify-probes.md +0 -32
- package/.worktrees/e58fcc4c/templates/workflows/archive-impact.yaml +0 -79
- package/.worktrees/e58fcc4c/templates/workflows/scan-docs.yaml +0 -132
|
@@ -1,1849 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SillySpec WorktreeManager — git worktree 生命周期管理
|
|
3
|
-
*
|
|
4
|
-
* 封装 git worktree 的 create/list/cleanup/getMeta 操作,
|
|
5
|
-
* 为 execute 阶段提供代码隔离环境。
|
|
6
|
-
*
|
|
7
|
-
* worktree 存储目录:.sillyspec/.runtime/worktrees/<change-name>/
|
|
8
|
-
* 分支命名:sillyspec/<change-name>
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { execFileSync } from 'child_process';
|
|
12
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, readlinkSync, unlinkSync, copyFileSync } from 'fs';
|
|
13
|
-
import { join, resolve, dirname, relative, isAbsolute } from 'path';
|
|
14
|
-
import { createHash } from 'crypto';
|
|
15
|
-
import { provisionDeps, checkDepsFreshness, detectEditableInstallEscape } from './worktree-deps.js';
|
|
16
|
-
import { writeAtomicSync } from './fs-atomic.js';
|
|
17
|
-
import { git, gitQuiet } from './git-helper.js';
|
|
18
|
-
|
|
19
|
-
// meta.json 会被 hook 进程与其它 CLI 进程并发读取(worktree-guard / getMeta / create 幽灵判定),
|
|
20
|
-
// 必须原子写:半截 JSON 会让 getMeta 返回 null → 触发幽灵 worktree 强删(可能丢 gitignored 改动)。
|
|
21
|
-
const writeMetaAtomic = (metaPath, meta) => writeAtomicSync(metaPath, JSON.stringify(meta, null, 2) + '\n');
|
|
22
|
-
|
|
23
|
-
const WORKTREES_REL = '.sillyspec/.runtime/worktrees';
|
|
24
|
-
const BRANCH_PREFIX = 'sillyspec/';
|
|
25
|
-
const META_FILE = 'meta.json';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 解除 worktree 内全部 node_modules junction/symlink(根 + meta.depsModules 各子模块)。
|
|
29
|
-
*
|
|
30
|
-
* 坑 ghost-dir-junction-pierce(2026-08-23 实证:worktree apply 后目录残留,人工 rm -rf 经
|
|
31
|
-
* junction 穿透删主仓 node_modules——user-inputs 两次事故实录;npm ci / git worktree remove
|
|
32
|
-
* 同样跟随 junction)。任何删除 worktree 目录的路径(cleanup / create 幽灵强删 / doctor 修复)
|
|
33
|
-
* 必须先解链;此前解链逻辑三处复制粘贴且**不覆盖 modules 子模块 junction**
|
|
34
|
-
* (meta.depsModules 记录的 wt/<module>/node_modules)。
|
|
35
|
-
*
|
|
36
|
-
* lstat/rmdir 失败 fail-loud(D-001@v1:静默跳过解链的后果是穿透删主仓,宁可阻断)。
|
|
37
|
-
*
|
|
38
|
-
* @param {string} worktreePath worktree 根目录
|
|
39
|
-
* @param {object} [meta] 可含 depsModules([{path}] 子模块清单);null 时只解根
|
|
40
|
-
* @param {string[]} [details] 可选,记录解链明细(cleanup details 兼容)
|
|
41
|
-
* @returns {number} 实际解除的链接数
|
|
42
|
-
*/
|
|
43
|
-
export function unlinkNodeModulesLinks(worktreePath, meta = null, details = null) {
|
|
44
|
-
let removed = 0;
|
|
45
|
-
const targets = [join(worktreePath, 'node_modules')];
|
|
46
|
-
for (const m of Array.isArray(meta && meta.depsModules) ? meta.depsModules : []) {
|
|
47
|
-
// 子模块 path 安全校验(与 worktree-deps isSafeModulePath 同口径):拒绝绝对路径与 .. 段
|
|
48
|
-
if (m && typeof m.path === 'string' && m.path && !isAbsolute(m.path) && !m.path.split(/[\\/]+/).includes('..')) {
|
|
49
|
-
targets.push(join(worktreePath, m.path, 'node_modules'));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
for (const nm of targets) {
|
|
53
|
-
if (!existsSync(nm)) continue;
|
|
54
|
-
let isLink;
|
|
55
|
-
try {
|
|
56
|
-
isLink = lstatSync(nm).isSymbolicLink();
|
|
57
|
-
} catch (e) {
|
|
58
|
-
throw new Error(`worktree node_modules junction 检测失败(疑似 EPERM:杀毒/索引锁 junction),阻断删除以保护主仓 node_modules:${e.message}。请关闭占用进程或手动 rmdir "${nm}" 后重试`);
|
|
59
|
-
}
|
|
60
|
-
if (!isLink) continue;
|
|
61
|
-
try {
|
|
62
|
-
if (process.platform === 'win32') {
|
|
63
|
-
// Windows rmdir 删 junction(reparse point)不跟随目标;execFileSync 数组形式不经 shell
|
|
64
|
-
execFileSync('cmd.exe', ['/c', 'rmdir', nm]);
|
|
65
|
-
} else {
|
|
66
|
-
unlinkSync(nm);
|
|
67
|
-
}
|
|
68
|
-
removed++;
|
|
69
|
-
if (details) details.push(`worktree node_modules junction/symlink removed: ${relative(worktreePath, nm) || '(root)'} (protect main checkout)`);
|
|
70
|
-
} catch (e) {
|
|
71
|
-
throw new Error(`worktree node_modules junction 解链失败,阻断删除以保护主仓 node_modules:${e.message}。请手动 rmdir "${nm}" 后重试`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return removed;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* 安全删除 worktree 目录:先解链全部 node_modules junction 再 rmSync。
|
|
79
|
-
* 幽灵目录清理(create 强删 / doctor ghost 修复)的统一出口——不再裸 rmSync。
|
|
80
|
-
*/
|
|
81
|
-
export function safeRemoveWorktreeDir(worktreePath, meta = null) {
|
|
82
|
-
unlinkNodeModulesLinks(worktreePath, meta);
|
|
83
|
-
rmSync(worktreePath, { recursive: true, force: true });
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 进程级缓存:cwd → 主仓库根目录。避免每次 new WorktreeManager 都 spawn 一次
|
|
87
|
-
// `git rev-parse --git-common-dir`(Windows 上每次 spawn 约 30-100ms,execute 一次命令
|
|
88
|
-
// 会 new 多个 WorktreeManager)。git-common-dir 在进程内对同一 cwd 稳定,缓存安全。
|
|
89
|
-
const _mainRepoRootByCwd = new Map();
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* 检测当前目录的隔离状态
|
|
93
|
-
* 返回 { inWorktree: boolean, inSubmodule: boolean }
|
|
94
|
-
*
|
|
95
|
-
* 用 git rev-parse --git-dir 和 --git-common-dir 判断:
|
|
96
|
-
* - GIT_DIR != GIT_COMMON 通常是 linked worktree
|
|
97
|
-
* - 但在 git submodule 里也会出现这种情况
|
|
98
|
-
* - 所以必须额外检查 --show-superproject-working-tree 排除 submodule
|
|
99
|
-
*/
|
|
100
|
-
export function detectIsolation(cwd = process.cwd()) {
|
|
101
|
-
try {
|
|
102
|
-
const gitDir = git(cwd, ['rev-parse', '--git-dir']);
|
|
103
|
-
const gitCommonDir = git(cwd, ['rev-parse', '--git-common-dir']);
|
|
104
|
-
const superProject = gitQuiet(cwd, ['rev-parse', '--show-superproject-working-tree']);
|
|
105
|
-
|
|
106
|
-
const inWorktree = gitDir !== gitCommonDir && !superProject;
|
|
107
|
-
const inSubmodule = !!superProject;
|
|
108
|
-
|
|
109
|
-
return { inWorktree, inSubmodule, gitDir, gitCommonDir };
|
|
110
|
-
} catch {
|
|
111
|
-
return { inWorktree: false, inSubmodule: false, gitDir: null, gitCommonDir: null };
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* 检查 worktree 存储目录是否被 .gitignore 忽略
|
|
117
|
-
* @param {string} cwd - 项目根目录
|
|
118
|
-
* @returns {{ ignored: boolean, path: string }}
|
|
119
|
-
*/
|
|
120
|
-
function checkWorktreeDirIgnored(cwd = process.cwd()) {
|
|
121
|
-
const relPath = WORKTREES_REL;
|
|
122
|
-
try {
|
|
123
|
-
git(cwd, ['check-ignore', '-q', relPath]);
|
|
124
|
-
return { ignored: true, path: relPath };
|
|
125
|
-
} catch {
|
|
126
|
-
return { ignored: false, path: relPath };
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* 检测 worktree base(主仓库 HEAD)与 origin 默认分支的同步状态(只读:不 ff、不改 HEAD)。
|
|
132
|
-
* 替代旧的 fetch + merge --ff-only:旧逻辑分叉时 ff 失败被静默吞(syncStatus=failed),
|
|
133
|
-
* 成功时 baseHash 锚点又不更新(ff 引入的 main 内容污染 apply patch)。现改为只检测+报告,
|
|
134
|
-
* 不阻断 create,对齐 origin 的动作留给用户/agent。
|
|
135
|
-
*
|
|
136
|
-
* @param {string} cwd 主仓库目录
|
|
137
|
-
* @param {string} baseHash worktree base commit(= 主仓库 HEAD)
|
|
138
|
-
* @returns {{ status: 'up-to-date'|'behind'|'diverged'|'ahead'|'unknown', defaultBranch: string|null, behind: number, ahead: number }}
|
|
139
|
-
*/
|
|
140
|
-
export function computeBaseSync(cwd, baseHash) {
|
|
141
|
-
const diag = { status: 'unknown', defaultBranch: null, behind: 0, ahead: 0 };
|
|
142
|
-
// 推断默认分支:优先 origin/HEAD 指向的真实分支名(修复旧 339-342 的运算符优先级 bug
|
|
143
|
-
// ——旧代码在 origin/HEAD 存在时恒返回 'main',丢弃 symbolic-ref 的真实结果)。
|
|
144
|
-
const headRef = gitQuiet(cwd, ['symbolic-ref', 'refs/remotes/origin/HEAD', '--short']);
|
|
145
|
-
let defaultBranch = headRef ? headRef.replace('origin/', '') : null;
|
|
146
|
-
if (!defaultBranch) {
|
|
147
|
-
// fallback:origin/HEAD 未设置时,探测常见默认分支是否存在
|
|
148
|
-
for (const cand of ['main', 'master']) {
|
|
149
|
-
if (gitQuiet(cwd, ['rev-parse', '--verify', '--quiet', `refs/remotes/origin/${cand}`])) {
|
|
150
|
-
defaultBranch = cand;
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (!defaultBranch) return diag; // 无 origin 或推不出默认分支 → unknown
|
|
156
|
-
diag.defaultBranch = defaultBranch;
|
|
157
|
-
|
|
158
|
-
// best-effort fetch(只更新 remote-tracking,不改工作区/HEAD/分支;失败静默降级用缓存)
|
|
159
|
-
gitQuiet(cwd, ['fetch', 'origin', '--quiet'], { timeout: 60000 });
|
|
160
|
-
const remoteHead = gitQuiet(cwd, ['rev-parse', '--verify', '--quiet', `refs/remotes/origin/${defaultBranch}`]);
|
|
161
|
-
if (!baseHash || !remoteHead) return diag;
|
|
162
|
-
if (baseHash === remoteHead) {
|
|
163
|
-
diag.status = 'up-to-date';
|
|
164
|
-
return diag;
|
|
165
|
-
}
|
|
166
|
-
const behind = Number(gitQuiet(cwd, ['rev-list', '--count', `${baseHash}..${remoteHead}`]) || 0);
|
|
167
|
-
const ahead = Number(gitQuiet(cwd, ['rev-list', '--count', `${remoteHead}..${baseHash}`]) || 0);
|
|
168
|
-
diag.behind = behind;
|
|
169
|
-
diag.ahead = ahead;
|
|
170
|
-
diag.status = (behind > 0 && ahead > 0) ? 'diverged' : (behind > 0 ? 'behind' : 'ahead');
|
|
171
|
-
return diag;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* 把 base 同步检测结果打印到 stdout(醒目但不阻断 create)。
|
|
176
|
-
* up-to-date 静默不刷屏;其余给风险等级 + 对齐命令,由用户/agent 决定是否处理。
|
|
177
|
-
*/
|
|
178
|
-
function printSyncReport(diag, baseHash, changeName) {
|
|
179
|
-
const short = (baseHash || '').slice(0, 8);
|
|
180
|
-
if (diag.status === 'up-to-date') return;
|
|
181
|
-
if (diag.status === 'unknown') {
|
|
182
|
-
console.log(`ℹ️ base 同步检测:无 origin 远端或未设置默认分支,跳过(base ${short})`);
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const b = diag.defaultBranch;
|
|
186
|
-
const ch = changeName || '<change>';
|
|
187
|
-
if (diag.status === 'behind') {
|
|
188
|
-
console.log(`⚠️ base 同步检测:落后 origin/${b} ${diag.behind} 个 commit(base ${short})`);
|
|
189
|
-
console.log(` apply 时 git --3way 会自动三路合并主干已提交推进;只有同文件同区域重叠才冲突。`);
|
|
190
|
-
console.log(` 建议先对齐以减小冲突面:`);
|
|
191
|
-
console.log(` git merge --ff-only origin/${b}`);
|
|
192
|
-
console.log(` sillyspec worktree cleanup ${ch} && sillyspec run execute --change ${ch}`);
|
|
193
|
-
console.log(` 或继续,重叠冲突时用 --merge 兜底(会引入合并提交)。`);
|
|
194
|
-
} else if (diag.status === 'diverged') {
|
|
195
|
-
console.log(`⚠️ base 同步检测:与 origin/${b} 分叉(领先 ${diag.ahead} / 落后 ${diag.behind},base ${short})`);
|
|
196
|
-
console.log(` apply 回 main 大概率冲突。建议先在主仓库对齐(rebase 或 merge origin/${b}),`);
|
|
197
|
-
console.log(` 再 sillyspec worktree cleanup ${ch} + 重跑 execute。`);
|
|
198
|
-
} else {
|
|
199
|
-
// ahead:本地有未 push 的提交,无 apply 冲突风险
|
|
200
|
-
console.log(`ℹ️ base 同步检测:领先 origin/${b} ${diag.ahead} 个 commit(本地有未 push 的提交,无冲突风险)`);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function parseJSON(raw) {
|
|
205
|
-
try { return JSON.parse(raw); } catch { return null; }
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* 跨平台同步等待(cleanup 重试退避用)
|
|
210
|
-
* 不依赖外部 sleep 命令——Windows cmd.exe 无 sleep;当 Git for Windows 未把 usr/bin 加入
|
|
211
|
-
* PATH 时,execSync('sleep 0.5') 会抛错并从 catch 块冒泡,直接中断整个 cleanup()。
|
|
212
|
-
* 改用 busy-wait 规避,保证 retry 之间真有间隔且不依赖环境。
|
|
213
|
-
*/
|
|
214
|
-
function sleepMs(ms) {
|
|
215
|
-
const end = Date.now() + ms;
|
|
216
|
-
while (Date.now() < end) { /* spin */ }
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
export function computeBaselineHash(cwd) {
|
|
220
|
-
// 排除非交付物的元数据/文档 churn,避免多操作者仓库里别人改这些 → 整树 hash 变 → apply 误阻断
|
|
221
|
-
// (execute 自身也会改 .sillyspec/docs.sillyspec,否则每个 execute 收尾都因自身改动 BLOCKED)。
|
|
222
|
-
// 安全:coarse hash 只挡「未提交」dirty(git apply --3way 危险区,实测必拦);真冲突由
|
|
223
|
-
// applyWorktree step5a(未提交∩changedFiles 精确点名)+step7(--3way 冲突回滚)兜底,
|
|
224
|
-
// 「已提交」推进不再拦(step5b 已放宽交 --3way 自动三路合并),不读这里的 exclude。
|
|
225
|
-
// - .sillyspec/:brainstorm/plan 蓝图 + runtime 产物
|
|
226
|
-
// - .claude/:agent 配置/skills/CLAUDE.md(多操作者 agent 指引 churn)
|
|
227
|
-
// - docs/:文档(非代码交付物)
|
|
228
|
-
// - CLAUDE.md:根 agent 指引(多操作者常改)
|
|
229
|
-
// 必须和 applyWorktree step 4.5 (worktree-apply.js) 使用相同的排除规则。
|
|
230
|
-
const exclude = ['--', '.', ':(exclude).sillyspec/', ':(exclude).claude/', ':(exclude)docs/', ':(exclude)CLAUDE.md'];
|
|
231
|
-
const staged = gitQuiet(cwd, ['diff', '--cached', ...exclude], { timeout: 30000 }) || '';
|
|
232
|
-
const unstaged = gitQuiet(cwd, ['diff', ...exclude], { timeout: 30000 }) || '';
|
|
233
|
-
const untracked = gitQuiet(cwd, ['ls-files', '--others', '--exclude-standard', ...exclude], { timeout: 30000 }) || '';
|
|
234
|
-
const raw = `staged:${staged}
|
|
235
|
-
unstaged:${unstaged}
|
|
236
|
-
untracked:${untracked}`;
|
|
237
|
-
return createHash('sha256').update(raw).digest('hex').slice(0, 16);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function validateChangeName(changeName) {
|
|
241
|
-
if (!changeName || typeof changeName !== 'string' || changeName.trim() === '') {
|
|
242
|
-
throw new Error('changeName 不能为空');
|
|
243
|
-
}
|
|
244
|
-
const trimmed = changeName.trim();
|
|
245
|
-
// 禁止路径穿越
|
|
246
|
-
if (trimmed.includes('..') || trimmed.includes('/') || trimmed.includes('\\')) {
|
|
247
|
-
throw new Error(`changeName 不合法: "${changeName}",不能包含 ..、/ 或 \\`);
|
|
248
|
-
}
|
|
249
|
-
return trimmed;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* 检测 git worktree 是否可用
|
|
254
|
-
* @param {string} cwd
|
|
255
|
-
* @returns {{ supported: boolean, version: string|null, reason?: string }}
|
|
256
|
-
*/
|
|
257
|
-
function isGitWorktreeSupported(cwd = process.cwd()) {
|
|
258
|
-
try {
|
|
259
|
-
const raw = git(cwd, ['--version']);
|
|
260
|
-
const match = raw.match(/git version (\d+)\.(\d+)/);
|
|
261
|
-
if (!match) return { supported: false, version: raw, reason: 'cannot parse version' };
|
|
262
|
-
const major = parseInt(match[1], 10);
|
|
263
|
-
const minor = parseInt(match[2], 10);
|
|
264
|
-
if (major > 2 || (major === 2 && minor >= 15)) {
|
|
265
|
-
return { supported: true, version: raw };
|
|
266
|
-
}
|
|
267
|
-
return { supported: false, version: raw, reason: 'git version < 2.15' };
|
|
268
|
-
} catch {
|
|
269
|
-
return { supported: false, version: null, reason: 'git not found' };
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* 复制单个 untracked 条目到 worktree(baseline overlay 用)。
|
|
275
|
-
*
|
|
276
|
-
* 防御目录:untracked 项若是目录(如 Claude Code agent worktree 隔离目录 .worktrees/<hash>/),
|
|
277
|
-
* readFileSync 会 EISDIR → 整个 overlay fail-fast。目录跳过(overlay 只同步文件层未提交改动,
|
|
278
|
-
* 目录结构由后续文件写入的 mkdirSync 重建)。坑 execute-worktree-overlay-untracked-dir-eisdir。
|
|
279
|
-
*
|
|
280
|
-
* @param {string} src 主仓库源路径
|
|
281
|
-
* @param {string} dst worktree 目标路径
|
|
282
|
-
* @returns {{status:'copied'|'skipped-dir'|'missing'|'error', error?:string}}
|
|
283
|
-
*/
|
|
284
|
-
export function copyUntrackedEntry(src, dst) {
|
|
285
|
-
if (!existsSync(src)) return { status: 'missing' };
|
|
286
|
-
let st;
|
|
287
|
-
try { st = statSync(src); } catch { return { status: 'missing' }; }
|
|
288
|
-
if (st.isDirectory()) return { status: 'skipped-dir' };
|
|
289
|
-
try {
|
|
290
|
-
mkdirSync(dirname(dst), { recursive: true });
|
|
291
|
-
writeFileSync(dst, readFileSync(src));
|
|
292
|
-
return { status: 'copied' };
|
|
293
|
-
} catch (e) {
|
|
294
|
-
return { status: 'error', error: e.message };
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* execute 阶段级核验(防空跑谎报,D-002@v1 / FR-04/05/06):聚合 review.changedFiles 的交付文件,
|
|
300
|
-
* 逐个核验存在于 worktree 分支 tree 或 worktree 工作区。两处皆无 = 声称实现但从未落盘。
|
|
301
|
-
*
|
|
302
|
-
* - checked:false:worktreePath 不存在或 branch 不存在(git rev-parse --verify <branch> 失败)
|
|
303
|
-
* → 无法核验(worktree 已丢 / 分支已删),调用方保守提示人工确认。
|
|
304
|
-
* - 逐文件:`git rev-parse --verify --quiet <branch>:<file>` 成功(在分支 tree)或
|
|
305
|
-
* existsSync(worktreePath/<file>)(在工作区)→ verified;两处皆无 → missing。
|
|
306
|
-
* tree 探测用 rev-parse --verify --quiet 而非 `git cat-file -e`:语义等价(按 branch tree 解析
|
|
307
|
-
* blob),但 cat-file 对不存在路径会往 stderr 打 `fatal: path ... does not exist` 噪音
|
|
308
|
-
* (git-helper gitQuiet 只吞退出码不吞 stderr),--quiet 完全静默。
|
|
309
|
-
*
|
|
310
|
-
* git 调用走项目统一入口 gitQuiet(git-helper.js,数组形式 execFileSync 不经 shell,Windows 安全)。
|
|
311
|
-
* 宽松非阻断:本函数只返回结果不 warn/exit,由调用方决定(design 为 warn 不 exit)。
|
|
312
|
-
*
|
|
313
|
-
* @param {object} p
|
|
314
|
-
* @param {string} p.worktreePath - worktree 根目录(绝对路径,meta.worktreePath)
|
|
315
|
-
* @param {string} p.branch - sillyspec/<change> 分支名(核验 tree)
|
|
316
|
-
* @param {string[]} p.changedFiles - review 声称的交付文件(调用方已过滤主仓 repo、非 .sillyspec)
|
|
317
|
-
* @returns {{ missing: string[], verified: string[], checked: boolean }}
|
|
318
|
-
* checked=false 表示无法核验(worktree/分支不存在),调用方保守提示人工确认。
|
|
319
|
-
*/
|
|
320
|
-
export function findMissingDeliverables({ worktreePath, branch, changedFiles }) {
|
|
321
|
-
const files = Array.isArray(changedFiles)
|
|
322
|
-
? changedFiles.filter(f => typeof f === 'string' && f.trim() !== '')
|
|
323
|
-
: []
|
|
324
|
-
// worktree 目录不存在 → 无法核验(目录已丢,如 apply 后 cleanup / 并行 session 清过)
|
|
325
|
-
if (!worktreePath || !existsSync(worktreePath)) {
|
|
326
|
-
return { missing: [], verified: [], checked: false }
|
|
327
|
-
}
|
|
328
|
-
// branch 不存在(rev-parse --verify 失败)→ 无法核验分支 tree
|
|
329
|
-
if (!branch || !gitQuiet(worktreePath, ['rev-parse', '--verify', '--quiet', branch])) {
|
|
330
|
-
return { missing: [], verified: [], checked: false }
|
|
331
|
-
}
|
|
332
|
-
const missing = []
|
|
333
|
-
const verified = []
|
|
334
|
-
for (const raw of files) {
|
|
335
|
-
// 统一正斜杠:review.json 可能落 Windows 反斜杠路径,rev-parse tree 路径一律 /
|
|
336
|
-
const f = raw.replace(/\\/g, '/')
|
|
337
|
-
const inBranch = gitQuiet(worktreePath, ['rev-parse', '--verify', '--quiet', `${branch}:${f}`]) !== null
|
|
338
|
-
const inWorktree = existsSync(join(worktreePath, f))
|
|
339
|
-
if (inBranch || inWorktree) verified.push(raw)
|
|
340
|
-
else missing.push(raw)
|
|
341
|
-
}
|
|
342
|
-
return { missing, verified, checked: true }
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
export class WorktreeManager {
|
|
346
|
-
constructor({ cwd, worktreeDir } = {}) {
|
|
347
|
-
this.cwd = cwd || process.cwd();
|
|
348
|
-
|
|
349
|
-
// worktreeBase 必须固定到主仓库路径,不能跟着 cwd 变化。
|
|
350
|
-
// native-worktree 模式下 cwd 是 worktree 子目录,用 cwd 推导 worktreeBase
|
|
351
|
-
// 会导致 meta 写入 worktree 内部路径,worktree 内再次执行时找不到。
|
|
352
|
-
// 解决:用 git rev-parse --git-common-dir 反推主仓库路径。
|
|
353
|
-
if (worktreeDir) {
|
|
354
|
-
this.worktreeBase = worktreeDir;
|
|
355
|
-
} else {
|
|
356
|
-
this.worktreeBase = resolve(this._resolveMainRepoRoot(), WORKTREES_REL);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* 解析当前 git 环境对应的主仓库根目录
|
|
362
|
-
* 在主仓库内执行:返回 cwd 自身
|
|
363
|
-
* 在 linked worktree 内执行:返回 git-common-dir 的父目录(即主仓库 .git 所在地)
|
|
364
|
-
* @private
|
|
365
|
-
*/
|
|
366
|
-
_resolveMainRepoRoot() {
|
|
367
|
-
const cached = _mainRepoRootByCwd.get(this.cwd);
|
|
368
|
-
if (cached !== undefined) return cached;
|
|
369
|
-
|
|
370
|
-
let root = this.cwd;
|
|
371
|
-
try {
|
|
372
|
-
// git-common-dir 在主仓库内 = <main>/.git,在 linked worktree 内 = <main>/.git(git 共享 .git 目录)。
|
|
373
|
-
// 但 git 可能返回**相对路径**(如 `.git`)。必须相对 this.cwd 绝对化,否则下面的
|
|
374
|
-
// existsSync/statSync 会相对 process.cwd() 解析——当 process.cwd 是另一个 git 仓库
|
|
375
|
-
// (未 chdir 的脚本、或 CLI 在别处跑)时,worktreeBase 会错解析到 process.cwd 仓库,
|
|
376
|
-
// getMeta 读错位置返回 null。resolve() 对绝对参数原样返回、对相对参数相对 this.cwd 解析,两者皆稳。
|
|
377
|
-
const commonDir = gitQuiet(this.cwd, ['rev-parse', '--git-common-dir']);
|
|
378
|
-
if (commonDir) {
|
|
379
|
-
const absCommonDir = resolve(this.cwd, commonDir);
|
|
380
|
-
if (existsSync(absCommonDir) && statSync(absCommonDir).isDirectory()) {
|
|
381
|
-
root = dirname(absCommonDir);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
} catch (e) {
|
|
385
|
-
// 静默 fallback:主仓库内执行或 git 异常
|
|
386
|
-
}
|
|
387
|
-
_mainRepoRootByCwd.set(this.cwd, root);
|
|
388
|
-
return root;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* 获取 worktree 目录绝对路径
|
|
393
|
-
* @param {string} changeName
|
|
394
|
-
* @returns {string}
|
|
395
|
-
*/
|
|
396
|
-
getWorktreePath(changeName) {
|
|
397
|
-
return resolve(this.worktreeBase, changeName);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* 读取 worktree 元数据
|
|
402
|
-
* @param {string} changeName
|
|
403
|
-
* @returns {object|null} meta.json 内容,不存在或损坏返回 null
|
|
404
|
-
*/
|
|
405
|
-
getMeta(changeName) {
|
|
406
|
-
const name = validateChangeName(changeName);
|
|
407
|
-
const metaPath = join(this.getWorktreePath(name), META_FILE);
|
|
408
|
-
if (!existsSync(metaPath)) return null;
|
|
409
|
-
return parseJSON(readFileSync(metaPath, 'utf8'));
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* 创建 worktree
|
|
414
|
-
* @param {string} changeName - 变更名
|
|
415
|
-
* @param {{ base?: string }} opts - base: 基础分支,默认当前 HEAD
|
|
416
|
-
* @returns {{ branch: string, worktreePath: string, baseHash: string }}
|
|
417
|
-
* @throws {Error} worktree 已存在、git 不可用、changeName 为空
|
|
418
|
-
*/
|
|
419
|
-
create(changeName, { base, adoptBranch = false } = {}) {
|
|
420
|
-
const name = validateChangeName(changeName);
|
|
421
|
-
const worktreePath = this.getWorktreePath(name);
|
|
422
|
-
const branch = BRANCH_PREFIX + name;
|
|
423
|
-
|
|
424
|
-
// 0. 检测当前环境隔离状态(submodule guard)
|
|
425
|
-
const isolation = detectIsolation(this.cwd);
|
|
426
|
-
if (isolation.inSubmodule) {
|
|
427
|
-
throw new Error(
|
|
428
|
-
'当前目录在 git submodule 内,SillySpec worktree 不支持在 submodule 中创建。' +
|
|
429
|
-
'\n请在主仓库根目录执行 execute。'
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
if (isolation.inWorktree) {
|
|
433
|
-
// 已在 linked worktree 中,复用当前目录作为 worktree 路径
|
|
434
|
-
console.log(`ℹ️ 已在 linked worktree 中(git-dir: ${isolation.gitDir}),复用当前隔离环境。`);
|
|
435
|
-
|
|
436
|
-
// 幂等守卫:meta 已存在时不重新 overlay baseline
|
|
437
|
-
const existingMeta = this.getMeta(name)
|
|
438
|
-
if (existingMeta) {
|
|
439
|
-
return { branch: existingMeta.branch, worktreePath: existingMeta.worktreePath, baseHash: existingMeta.baseHash, mode: existingMeta.mode }
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// meta 不存在但已在 worktree 内:可能是 meta 被损坏/误删。
|
|
443
|
-
// 绝对禁止 overlay baseline(source === target 会冲突),
|
|
444
|
-
// 只恢复 meta 引用,不触碰文件系统。
|
|
445
|
-
return this._recoverNativeWorktreeMeta(name, {
|
|
446
|
-
worktreePath: this.cwd,
|
|
447
|
-
branch: gitQuiet(this.cwd, ['symbolic-ref', '--short', 'HEAD']) || 'detached',
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// 1. 检查 worktree 目录是否被 gitignore
|
|
452
|
-
const ignoreStatus = checkWorktreeDirIgnored(this.cwd);
|
|
453
|
-
if (!ignoreStatus.ignored) {
|
|
454
|
-
throw new Error(
|
|
455
|
-
`worktree 存储目录 ${ignoreStatus.path} 未被 .gitignore 忽略,` +
|
|
456
|
-
`创建 worktree 可能导致内容被误提交。\n` +
|
|
457
|
-
`请先在 .gitignore 中添加: ${ignoreStatus.path}/\n` +
|
|
458
|
-
`或运行 sillyspec doctor 检查修复。`
|
|
459
|
-
);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// 2. 检查 worktree 是否已存在
|
|
463
|
-
if (existsSync(worktreePath)) {
|
|
464
|
-
// 目录在但 meta.json 不存在(幽灵状态)—— 删之前必须确认无未提交改动,
|
|
465
|
-
// 否则会丢失 execute 期间未 commit 的代码(不可恢复,3.22.4 修复)。
|
|
466
|
-
if (!this.getMeta(name)) {
|
|
467
|
-
let uncommitted = '';
|
|
468
|
-
try { uncommitted = git(worktreePath, ['status', '--porcelain'], { timeout: 30000 }) } catch {}
|
|
469
|
-
if (uncommitted.trim()) {
|
|
470
|
-
throw new Error(
|
|
471
|
-
`检测到幽灵 worktree(无 meta.json)但含未提交改动,拒绝自动清理(防丢代码)。\n` +
|
|
472
|
-
` 目录:${worktreePath}\n` +
|
|
473
|
-
` 请先检查/commit/备份该目录,再手动清理:sillyspec worktree cleanup ${name} --force\n` +
|
|
474
|
-
` (未提交文件数:${uncommitted.trim().split('\n').length})`
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
console.log(`⚠️ 检测到幽灵 worktree 目录(无 meta.json,无未提交改动),自动清理...`);
|
|
478
|
-
// 安全删除(坑 ghost-dir-junction-pierce):git status 拦不住 junction(gitignored 的
|
|
479
|
-
// node_modules 不进 status,junction 目标内容更非「未提交改动」),裸 rmSync/人工 rm -rf
|
|
480
|
-
// 会穿透 junction 删主仓 node_modules——先解链再删。
|
|
481
|
-
try { safeRemoveWorktreeDir(worktreePath); } catch (unlinkErr) {
|
|
482
|
-
throw new Error(`幽灵 worktree 安全清理失败(已阻断,主仓 node_modules 未受影响):${unlinkErr.message}`)
|
|
483
|
-
}
|
|
484
|
-
// 同步清理 git worktree 注册(git 内部状态),否则后续 worktree add 会因「已注册」失败。
|
|
485
|
-
// 不再盲删同名分支(坑 worktree-user-branch-conflict,2026-08-24 用户反馈五期①:该分支
|
|
486
|
-
// 可能是用户有意创建的,无凭据区分遗留 vs 自建——统一交由下方分支决策菜单处置)。
|
|
487
|
-
try { gitQuiet(this.cwd, ['worktree', 'prune'], { timeout: 30000 }); } catch {}
|
|
488
|
-
} else {
|
|
489
|
-
throw new Error(`worktree already exists: ${name}. Run cleanup first.`);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// 2. 检查分支是否已存在(坑 worktree-user-branch-conflict,2026-08-24 用户反馈五期①:
|
|
494
|
-
// 「用户要求在指定分支上做」时既有同名分支让 execute 直接死锁,旧报错只说 Run cleanup
|
|
495
|
-
// first、修复建议无条件推荐删分支——用户自建分支被引导误删。现给决策菜单;--adopt-branch
|
|
496
|
-
// 显式收编:检出该分支为 worktree 工作分支,分支现状作 baseline,仅后续新改动计入交付。)
|
|
497
|
-
const branchExists = !!gitQuiet(this.cwd, ['rev-parse', '--verify', `refs/heads/${branch}`]);
|
|
498
|
-
let adoptedBranch = false;
|
|
499
|
-
if (branchExists && !adoptBranch) {
|
|
500
|
-
throw new Error(
|
|
501
|
-
`分支已存在:${branch}(worktree create 需新建同名分支,直接冲突)。按实际情况三选一:\n` +
|
|
502
|
-
` ① 本变更的遗留分支(上次 execute 残留、内容已落地或作废):确认后 git branch -D ${branch} 再重跑;\n` +
|
|
503
|
-
` ② 该分支是你有意让本变更在其上做的(用户指定分支):sillyspec run execute --change ${name} --adopt-branch\n` +
|
|
504
|
-
` —— 收编为 worktree 工作分支,分支既有内容计入 baseline(仅后续新改动算交付 diff);\n` +
|
|
505
|
-
` ③ 换变更名重跑。`
|
|
506
|
-
);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// 3. 解析 base 分支
|
|
510
|
-
let baseBranch = base;
|
|
511
|
-
let baseHash;
|
|
512
|
-
if (baseBranch) {
|
|
513
|
-
baseHash = git(this.cwd, ['rev-parse', baseBranch]);
|
|
514
|
-
} else {
|
|
515
|
-
// 默认用当前 HEAD
|
|
516
|
-
baseBranch = gitQuiet(this.cwd, ['symbolic-ref', '--short', 'HEAD']) || git(this.cwd, ['rev-parse', 'HEAD']);
|
|
517
|
-
baseHash = git(this.cwd, ['rev-parse', 'HEAD']);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// 4. 创建 worktree 根目录
|
|
521
|
-
if (!existsSync(this.worktreeBase)) {
|
|
522
|
-
mkdirSync(this.worktreeBase, { recursive: true });
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// 4.5 Windows 长路径支持:archive/ 下嵌套的 .runtime/artifacts 超长文件名(>260 字符)
|
|
526
|
-
// 会让 worktree add 的 checkout 报 "Filename too long" 失败 → 降级 in-place 且主工作区
|
|
527
|
-
// 未切分支,直接写代码污染 main(见缺陷 execute-in-place-windows-pitfalls 坑1)。
|
|
528
|
-
// core.longpaths=true 让 git 用 \\?\ 前缀绕过 MAX_PATH,幂等、Windows 推荐、低风险,失败不阻断。
|
|
529
|
-
if (process.platform === 'win32') {
|
|
530
|
-
try { gitQuiet(this.cwd, ['config', 'core.longpaths', 'true']); } catch {}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// 5. 创建 worktree(含版本检测 + sandbox fallback)
|
|
534
|
-
try {
|
|
535
|
-
if (branchExists && adoptBranch) {
|
|
536
|
-
// --adopt-branch 收编(坑 worktree-user-branch-conflict):检出既有分支为工作分支(不 -b),
|
|
537
|
-
// 分支当前 HEAD 作 baseHash——分支存量内容归 baseline,仅子代理新改动计入交付 diff
|
|
538
|
-
// (与 dirty baseline overlay 同哲学)。baseBranch 仍记主仓侧锚(sync 诊断用)。
|
|
539
|
-
git(this.cwd, ['worktree', 'add', worktreePath, branch], { timeout: 120000 });
|
|
540
|
-
baseHash = git(this.cwd, ['rev-parse', branch]);
|
|
541
|
-
adoptedBranch = true;
|
|
542
|
-
console.log(`📌 已收编既有分支 ${branch} 为 worktree 工作分支(--adopt-branch):baseline = 分支 HEAD ${String(baseHash).slice(0, 8)},分支既有内容不计交付 diff`);
|
|
543
|
-
} else {
|
|
544
|
-
git(this.cwd, ['worktree', 'add', worktreePath, '-b', branch, baseHash], { timeout: 120000 });
|
|
545
|
-
}
|
|
546
|
-
} catch (e) {
|
|
547
|
-
const check = isGitWorktreeSupported(this.cwd);
|
|
548
|
-
if (!check.supported) {
|
|
549
|
-
throw new Error(`git worktree add 失败: ${e.stderr || e.message}\n\n${check.reason ? `原因: ${check.reason}` : ''}\n建议: 升级 git 到 >= 2.15;或运行 \`sillyspec worktree doctor --fix\` 检查 worktree 状态。`);
|
|
550
|
-
}
|
|
551
|
-
// 体检 BUG-16 并发竞态防御:多 agent 同时首建同名 change 时,败者的 add 失败可能只是
|
|
552
|
-
// 「赢家刚建成目录/分支」。此时降级 in-place 会造成一个 worktree + 一个 in-place 的
|
|
553
|
-
// 分裂状态(两 agent 各写各的基线)——先重查,并发赢家已建成则按 already exists 抛错。
|
|
554
|
-
if (existsSync(worktreePath) || gitQuiet(this.cwd, ['rev-parse', '--verify', `refs/heads/${branch}`])) {
|
|
555
|
-
throw new Error(`worktree already exists: ${name}(并发创建,另一进程已抢先建成,本进程不降级 in-place). Run cleanup first.`);
|
|
556
|
-
}
|
|
557
|
-
// sandbox/permission fallback: 降级为 in-place + baseline protection
|
|
558
|
-
console.log(`⚠️ git worktree add 失败(可能是沙箱权限限制),降级为 in-place 模式 + baseline protection`);
|
|
559
|
-
console.log(` 原因: ${e.stderr || e.message}`);
|
|
560
|
-
return this._createInPlaceMeta(name, {
|
|
561
|
-
worktreePath: this.cwd,
|
|
562
|
-
branch,
|
|
563
|
-
baseBranch,
|
|
564
|
-
baseHash,
|
|
565
|
-
mode: 'in-place-fallback',
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// 5.4b 立即写占位 meta(防 create 中断导致幽灵 worktree:目录在 + meta 没 →
|
|
570
|
-
// 下次 create 误判幽灵强删)。后续 fetch/overlay/provision 步骤若抛错,占位
|
|
571
|
-
// meta 让 getMeta 返回非 null,下次 create 走 "already exists" 分支(保护
|
|
572
|
-
// worktree 内任何已写入的内容,不触发幽灵清理)。最终完整 meta 在 step 6 覆盖。
|
|
573
|
-
try {
|
|
574
|
-
const placeholderMeta = {
|
|
575
|
-
name_zh: 'worktree 元数据',
|
|
576
|
-
changeName: name,
|
|
577
|
-
branch,
|
|
578
|
-
baseBranch,
|
|
579
|
-
baseHash,
|
|
580
|
-
worktreePath,
|
|
581
|
-
mode: 'worktree',
|
|
582
|
-
createdAt: new Date().toISOString(),
|
|
583
|
-
provisioning: true,
|
|
584
|
-
};
|
|
585
|
-
writeMetaAtomic(join(worktreePath, META_FILE), placeholderMeta);
|
|
586
|
-
} catch (e) {
|
|
587
|
-
// 占位 meta 是「防 create 中断 → 幽灵强删」的守卫。写失败不能静默:否则中断后
|
|
588
|
-
// getMeta 仍返回 null,下次 create 会把含 gitignored 改动的 worktree 当幽灵删掉。
|
|
589
|
-
console.warn(`⚠️ 占位 meta 写入失败: ${e.message}(若 create 中断,下次可能误判幽灵 worktree)`);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// 5.5 base 同步检测(只读:不 ff、不改 HEAD;best-effort fetch 失败降级用缓存)。
|
|
593
|
-
// 旧逻辑 fetch + merge --ff-only:分叉时 ff 失败被静默吞(syncStatus=failed),成功时
|
|
594
|
-
// baseHash 锚点又不更新(ff 引入的 main 内容污染 apply patch)。现改为只检测 + 报告,
|
|
595
|
-
// 不阻断 create,对齐 origin 的动作留给用户/agent。
|
|
596
|
-
const syncDiagnostic = computeBaseSync(this.cwd, baseHash);
|
|
597
|
-
printSyncReport(syncDiagnostic, baseHash, name);
|
|
598
|
-
|
|
599
|
-
// 5.6 Dirty baseline overlay:将主工作区未提交变更同步到 worktree
|
|
600
|
-
const baselineResult = this._overlayBaseline(this.cwd, worktreePath);
|
|
601
|
-
const baselineFiles = baselineResult.files;
|
|
602
|
-
const baselineHash = baselineResult.baselineHash;
|
|
603
|
-
|
|
604
|
-
// 5.7 创建 baseline checkpoint(有 dirty baseline 时才创建)
|
|
605
|
-
let baselineCommit = null;
|
|
606
|
-
if (adoptedBranch) baselineCommit = baseHash; // adopt:分支 HEAD 恒作交付 diff 锚(无 dirty overlay 也要锚定)
|
|
607
|
-
if (baselineFiles.length > 0) {
|
|
608
|
-
baselineCommit = this._createBaselineCheckpoint(worktreePath, name, baselineFiles);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
// 5.8 依赖供给(change 2026-06-28-worktree-deps-provision)
|
|
612
|
-
// baseline overlay 后让 worktree 立即可构建/测试;失败不阻断 create,只记 meta。
|
|
613
|
-
let deps = {};
|
|
614
|
-
try {
|
|
615
|
-
deps = provisionDeps(worktreePath, this.cwd, { specBase: join(this.cwd, '.sillyspec') }) || {};
|
|
616
|
-
} catch (e) {
|
|
617
|
-
deps = { depsStatus: 'failed', depsError: `provisionDeps crashed: ${e.message}` };
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// 6. 写入 meta.json
|
|
621
|
-
const meta = {
|
|
622
|
-
name_zh: 'worktree 元数据',
|
|
623
|
-
changeName: name,
|
|
624
|
-
branch,
|
|
625
|
-
baseBranch,
|
|
626
|
-
baseHash,
|
|
627
|
-
actualBaseHash: gitQuiet(worktreePath, ['rev-parse', 'HEAD']) || baseHash,
|
|
628
|
-
createdAt: new Date().toISOString(),
|
|
629
|
-
worktreePath,
|
|
630
|
-
mode: 'worktree',
|
|
631
|
-
baselineFiles,
|
|
632
|
-
baselineCommit,
|
|
633
|
-
baselineHash,
|
|
634
|
-
syncDiagnostic,
|
|
635
|
-
depsStatus: deps.depsStatus,
|
|
636
|
-
depsMethod: deps.depsMethod || null,
|
|
637
|
-
depsSource: deps.depsSource || null,
|
|
638
|
-
depsLockHash: deps.depsLockHash || null,
|
|
639
|
-
depsCheckedAt: deps.depsCheckedAt || null,
|
|
640
|
-
...(deps.depsError ? { depsError: deps.depsError } : {}),
|
|
641
|
-
...(adoptedBranch ? { adoptedBranch: true } : {}),
|
|
642
|
-
};
|
|
643
|
-
|
|
644
|
-
const metaPath = join(worktreePath, META_FILE);
|
|
645
|
-
writeMetaAtomic(metaPath, meta);
|
|
646
|
-
|
|
647
|
-
return { branch, worktreePath, baseHash, mode: meta.mode, syncDiagnostic, ...(adoptedBranch ? { adoptedBranch: true } : {}) };
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
/**
|
|
651
|
-
* native-worktree 模式下恢复 meta 引用
|
|
652
|
-
* 当 meta.json 被损坏/误删时,只重建 meta 文件,不触碰文件系统(不 overlay)
|
|
653
|
-
* @private
|
|
654
|
-
*/
|
|
655
|
-
_recoverNativeWorktreeMeta(name, { worktreePath, branch }) {
|
|
656
|
-
const baseHash = gitQuiet(worktreePath, ['rev-parse', 'HEAD']) || null
|
|
657
|
-
const meta = {
|
|
658
|
-
name_zh: 'worktree 元数据',
|
|
659
|
-
changeName: name,
|
|
660
|
-
branch: branch || BRANCH_PREFIX + name,
|
|
661
|
-
baseBranch: branch,
|
|
662
|
-
baseHash,
|
|
663
|
-
actualBaseHash: baseHash,
|
|
664
|
-
createdAt: new Date().toISOString(),
|
|
665
|
-
worktreePath,
|
|
666
|
-
mode: 'native-worktree',
|
|
667
|
-
baselineFiles: [],
|
|
668
|
-
baselineCommit: null,
|
|
669
|
-
baselineHash: null,
|
|
670
|
-
recoveredAt: new Date().toISOString(),
|
|
671
|
-
recoveryNote: 'meta was missing in native-worktree; recovered without baseline overlay',
|
|
672
|
-
}
|
|
673
|
-
if (!existsSync(this.worktreeBase)) mkdirSync(this.worktreeBase, { recursive: true })
|
|
674
|
-
const metaDir = join(this.worktreeBase, name)
|
|
675
|
-
if (!existsSync(metaDir)) mkdirSync(metaDir, { recursive: true })
|
|
676
|
-
writeMetaAtomic(join(metaDir, META_FILE), meta)
|
|
677
|
-
console.log(`🔗 native-worktree meta 已恢复: ${metaDir}/meta.json`)
|
|
678
|
-
return { branch: meta.branch, worktreePath, baseHash, mode: meta.mode }
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* 创建 in-place 模式的 meta.json(降级路径)
|
|
683
|
-
* 不创建 git worktree,直接在当前目录记录 baseline 并写入 meta
|
|
684
|
-
* @private
|
|
685
|
-
*/
|
|
686
|
-
_createInPlaceMeta(name, { worktreePath, branch, baseBranch, baseHash, mode } = {}) {
|
|
687
|
-
// 幂等守卫:meta 已存在时不重新创建(避免 overlay baseline 和已有改动冲突)
|
|
688
|
-
const existingMeta = this.getMeta(name)
|
|
689
|
-
if (existingMeta) {
|
|
690
|
-
return { branch: existingMeta.branch, worktreePath: existingMeta.worktreePath, baseHash: existingMeta.baseHash, mode: existingMeta.mode }
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
// 供给 deps(与 native worktree 路径一致)。in-place-fallback 模式漏写 depsStatus 会致
|
|
694
|
-
// enforceDepsGate(run.js)把 undefined 当 unknown 阻断 execute --done,死锁无法推进。
|
|
695
|
-
// 见 docs/sillyspec/execute-inplace-deps-gate.md(2026-07-08 发现)。
|
|
696
|
-
let deps = {}
|
|
697
|
-
try {
|
|
698
|
-
deps = provisionDeps(worktreePath, this.cwd, { specBase: join(this.cwd, '.sillyspec') }) || {}
|
|
699
|
-
} catch (e) {
|
|
700
|
-
deps = { depsStatus: 'failed', depsError: `provisionDeps crashed: ${e.message}` }
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// 硬规则:禁止 self-overlay(source 和 target 相同时 overlay 必然冲突)
|
|
704
|
-
const resolvedSource = resolve(this.cwd)
|
|
705
|
-
const resolvedTarget = resolve(worktreePath)
|
|
706
|
-
if (resolvedSource === resolvedTarget) {
|
|
707
|
-
console.warn('⚠️ 跳过 baseline overlay:当前目录与目标目录相同(native-worktree 或 in-place 模式)')
|
|
708
|
-
// 写 meta 但不 overlay
|
|
709
|
-
baseBranch = baseBranch || gitQuiet(this.cwd, ['symbolic-ref', '--short', 'HEAD']) || gitQuiet(this.cwd, ['rev-parse', 'HEAD'])
|
|
710
|
-
baseHash = baseHash || git(this.cwd, ['rev-parse', 'HEAD'])
|
|
711
|
-
const meta = {
|
|
712
|
-
name_zh: 'worktree 元数据',
|
|
713
|
-
changeName: name,
|
|
714
|
-
branch: branch || BRANCH_PREFIX + name,
|
|
715
|
-
baseBranch,
|
|
716
|
-
baseHash,
|
|
717
|
-
actualBaseHash: gitQuiet(worktreePath, ['rev-parse', 'HEAD']) || baseHash,
|
|
718
|
-
createdAt: new Date().toISOString(),
|
|
719
|
-
worktreePath,
|
|
720
|
-
mode: mode || 'in-place-fallback',
|
|
721
|
-
baselineFiles: [],
|
|
722
|
-
baselineCommit: null,
|
|
723
|
-
baselineHash: null,
|
|
724
|
-
...deps,
|
|
725
|
-
}
|
|
726
|
-
if (!existsSync(this.worktreeBase)) mkdirSync(this.worktreeBase, { recursive: true })
|
|
727
|
-
const metaDir = join(this.worktreeBase, name)
|
|
728
|
-
if (!existsSync(metaDir)) mkdirSync(metaDir, { recursive: true })
|
|
729
|
-
writeMetaAtomic(join(metaDir, META_FILE), meta)
|
|
730
|
-
return { branch: meta.branch, worktreePath, baseHash, mode: meta.mode }
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
// 解析 base
|
|
734
|
-
if (!baseHash) {
|
|
735
|
-
baseBranch = baseBranch || gitQuiet(this.cwd, ['symbolic-ref', '--short', 'HEAD']) || gitQuiet(this.cwd, ['rev-parse', 'HEAD']);
|
|
736
|
-
baseHash = git(this.cwd, ['rev-parse', 'HEAD']);
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
const baselineResult = this._overlayBaseline(this.cwd, this.cwd);
|
|
740
|
-
const baselineFiles = baselineResult.files;
|
|
741
|
-
const baselineHash = baselineResult.baselineHash;
|
|
742
|
-
|
|
743
|
-
let baselineCommit = null;
|
|
744
|
-
if (baselineFiles.length > 0) {
|
|
745
|
-
baselineCommit = this._createBaselineCheckpoint(this.cwd, name, baselineFiles);
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
const meta = {
|
|
749
|
-
name_zh: 'worktree 元数据',
|
|
750
|
-
changeName: name,
|
|
751
|
-
branch: branch || BRANCH_PREFIX + name,
|
|
752
|
-
baseBranch,
|
|
753
|
-
baseHash,
|
|
754
|
-
actualBaseHash: gitQuiet(worktreePath, ['rev-parse', 'HEAD']) || baseHash,
|
|
755
|
-
createdAt: new Date().toISOString(),
|
|
756
|
-
worktreePath,
|
|
757
|
-
mode: mode || 'in-place-fallback',
|
|
758
|
-
baselineFiles,
|
|
759
|
-
baselineCommit,
|
|
760
|
-
baselineHash,
|
|
761
|
-
...deps,
|
|
762
|
-
};
|
|
763
|
-
|
|
764
|
-
// in-place 模式下 meta 写入 worktreeBase(避免污染主工作区)
|
|
765
|
-
if (!existsSync(this.worktreeBase)) {
|
|
766
|
-
mkdirSync(this.worktreeBase, { recursive: true });
|
|
767
|
-
}
|
|
768
|
-
const metaPath = join(this.worktreeBase, name, META_FILE);
|
|
769
|
-
const metaDir = join(this.worktreeBase, name);
|
|
770
|
-
if (!existsSync(metaDir)) {
|
|
771
|
-
mkdirSync(metaDir, { recursive: true });
|
|
772
|
-
}
|
|
773
|
-
writeMetaAtomic(metaPath, meta);
|
|
774
|
-
|
|
775
|
-
return { branch: meta.branch, worktreePath, baseHash, mode: meta.mode };
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
/**
|
|
779
|
-
* 构建 isolation 信息对象,用于写入 sillyspec.db(_writeIsolationToDB,isolation_status 列)
|
|
780
|
-
* @param {string} changeName
|
|
781
|
-
* @returns {{ status: string, mode: string, path: string } | null}
|
|
782
|
-
*/
|
|
783
|
-
getIsolationInfo(changeName) {
|
|
784
|
-
const meta = this.getMeta(changeName);
|
|
785
|
-
if (!meta) return null;
|
|
786
|
-
|
|
787
|
-
const mode = meta.mode || 'worktree';
|
|
788
|
-
const statusMap = {
|
|
789
|
-
'worktree': 'verified',
|
|
790
|
-
'native-worktree': 'verified',
|
|
791
|
-
'in-place-fallback': 'degraded',
|
|
792
|
-
};
|
|
793
|
-
|
|
794
|
-
return {
|
|
795
|
-
status: statusMap[mode] || 'verified',
|
|
796
|
-
mode,
|
|
797
|
-
path: meta.worktreePath,
|
|
798
|
-
branch: meta.branch,
|
|
799
|
-
};
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
/**
|
|
803
|
-
* 获取 worktree 的运行模式
|
|
804
|
-
* @param {string} changeName
|
|
805
|
-
* @returns {'worktree'|'native-worktree'|'in-place-fallback'|null}
|
|
806
|
-
*/
|
|
807
|
-
getMode(changeName) {
|
|
808
|
-
const meta = this.getMeta(changeName);
|
|
809
|
-
return meta?.mode || null;
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
/**
|
|
813
|
-
* 列出所有活跃 worktree
|
|
814
|
-
* @returns {Array<{ changeName: string, branch: string, baseHash: string, createdAt: string, worktreePath: string }>}
|
|
815
|
-
*/
|
|
816
|
-
list() {
|
|
817
|
-
const results = [];
|
|
818
|
-
if (!existsSync(this.worktreeBase)) return results;
|
|
819
|
-
|
|
820
|
-
const entries = readdirSync(this.worktreeBase, { withFileTypes: true });
|
|
821
|
-
for (const entry of entries) {
|
|
822
|
-
if (!entry.isDirectory()) continue;
|
|
823
|
-
const metaPath = join(this.worktreeBase, entry.name, META_FILE);
|
|
824
|
-
if (!existsSync(metaPath)) continue;
|
|
825
|
-
const meta = parseJSON(readFileSync(metaPath, 'utf8'));
|
|
826
|
-
if (!meta) continue;
|
|
827
|
-
results.push({
|
|
828
|
-
changeName: meta.changeName,
|
|
829
|
-
branch: meta.branch,
|
|
830
|
-
baseHash: meta.baseHash,
|
|
831
|
-
baseBranch: meta.baseBranch,
|
|
832
|
-
createdAt: meta.createdAt,
|
|
833
|
-
worktreePath: meta.worktreePath,
|
|
834
|
-
mode: meta.mode || 'worktree',
|
|
835
|
-
});
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
return results;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
/**
|
|
842
|
-
* 清理 worktree(仅限 SillySpec 创建的临时 worktree)
|
|
843
|
-
* 幂等:重复调用不报错。
|
|
844
|
-
* 三重清理:git worktree 注册 + worktree 目录 + meta 目录。
|
|
845
|
-
* @param {string} changeName
|
|
846
|
-
* @param {{ force?: boolean, maxRetries?: number }} opts
|
|
847
|
-
* @returns {{ result: 'blocked'|'cleaned'|'force-cleaned'|'skipped'|'kept'|'partial', mode: string|null, details: string[], residual: string[] }}
|
|
848
|
-
* result 取值:blocked=有未落主仓交付变更,fail-closed 拒绝清理(需 force 绕过,D-001@v1);
|
|
849
|
-
* cleaned=git remove 成功;force-cleaned=git remove 失败但 fallback 清理完成;
|
|
850
|
-
* partial=有残留(目录/meta/git 注册未清);skipped=无需清理;kept=native-worktree 保留。
|
|
851
|
-
* residual:未清干净的路径/引用列表(空数组表示干净)。
|
|
852
|
-
*/
|
|
853
|
-
/**
|
|
854
|
-
* fail-closed:清理删分支前的审计引用校验(2026-08-18,两例实录:execute --done 批量完成 /
|
|
855
|
-
* worktree apply 后自动 cleanup 均无条件删分支 ref,task commit 变 dangling)。
|
|
856
|
-
* apply 只复制文件内容不携带 commit(主仓重 commit 后 hash 不同),分支 ref 一删,
|
|
857
|
-
* task review.json 的 base/head 引用即悬空(gc 后真丢,git diff base..head 无法复核)。
|
|
858
|
-
* 有引用 → cleanup 保留分支并提示手动删;校验自身异常也按"有引用"处理(宁保留勿误删)。
|
|
859
|
-
* @private
|
|
860
|
-
* @param {string} branch - 待删分支名
|
|
861
|
-
* @returns {string[]} 引用该分支 commit 的 review.json 路径列表(空 = 无引用,删分支安全)
|
|
862
|
-
*/
|
|
863
|
-
_branchReviewReferences(branch) {
|
|
864
|
-
const runsDir = join(this.cwd, '.sillyspec', '.runtime', 'execute-runs');
|
|
865
|
-
if (!existsSync(runsDir)) return [];
|
|
866
|
-
const branchCommits = new Set(
|
|
867
|
-
(gitQuiet(this.cwd, ['rev-list', branch]) || '').split('\n').map(s => s.trim()).filter(Boolean)
|
|
868
|
-
);
|
|
869
|
-
if (branchCommits.size === 0) return [];
|
|
870
|
-
const refs = [];
|
|
871
|
-
const visit = (dir) => {
|
|
872
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
873
|
-
const p = join(dir, entry.name);
|
|
874
|
-
if (entry.isDirectory()) visit(p);
|
|
875
|
-
else if (entry.name === 'review.json') {
|
|
876
|
-
try {
|
|
877
|
-
const r = JSON.parse(readFileSync(p, 'utf8'));
|
|
878
|
-
for (const key of ['base', 'head']) {
|
|
879
|
-
const h = typeof r[key] === 'string' ? r[key].trim() : '';
|
|
880
|
-
if (h && branchCommits.has(h)) { refs.push(p); break; }
|
|
881
|
-
}
|
|
882
|
-
} catch { /* review.json 损坏不参与判定 */ }
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
};
|
|
886
|
-
visit(runsDir);
|
|
887
|
-
return refs;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
/**
|
|
891
|
-
* 清理前打捞 spec 流程产物(坑 worktree-spec-artifact-misplace,2026-08-24 用户实证)。
|
|
892
|
-
*
|
|
893
|
-
* 子代理 cwd=worktree 时会把流程产物(verify-result.md / module-impact.md / 任务卡 / 模块文档)
|
|
894
|
-
* 写进 worktree 内的 .sillyspec——而 apply 的 filterDeliverableFiles 把 .sillyspec/changes/ 排除在
|
|
895
|
-
* 交付外(spec 文档不进代码 patch),cleanup 删除 worktree 这些产物即蒸发、主仓流程看不到。
|
|
896
|
-
* 删除前扫 worktree 内两棵子树做最后打捞:
|
|
897
|
-
* changes/<name>/** → 主仓缺失则 copy 回(verify-result.md 等核心场景);同名不同内容仅列
|
|
898
|
-
* 清单 warn 不覆盖(主仓版本可能是更新的,覆盖会倒退)
|
|
899
|
-
* docs/** → 主仓缺失则 copy 回(模块文档卡片;同名差异不提示——tracked checkout 副本常态)
|
|
900
|
-
* 只扫本变更子树(其它 change 的副本文件不越权搬运)。异常只 warn 不阻断清理。
|
|
901
|
-
* @private
|
|
902
|
-
*/
|
|
903
|
-
_salvageSpecArtifacts(name, worktreePath, details) {
|
|
904
|
-
try {
|
|
905
|
-
const mainSpec = join(this._resolveMainRepoRoot(), '.sillyspec');
|
|
906
|
-
const wtSpec = join(worktreePath, '.sillyspec');
|
|
907
|
-
if (!existsSync(wtSpec)) return;
|
|
908
|
-
if (resolve(wtSpec) === resolve(mainSpec)) return; // in-place:同一目录,无需打捞
|
|
909
|
-
const salvaged = [];
|
|
910
|
-
const conflicts = [];
|
|
911
|
-
const salvageTree = (subDir, reportConflict) => {
|
|
912
|
-
const srcRoot = join(wtSpec, subDir);
|
|
913
|
-
if (!existsSync(srcRoot)) return;
|
|
914
|
-
const walk = (dir) => {
|
|
915
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
916
|
-
const src = join(dir, entry.name);
|
|
917
|
-
if (entry.isDirectory()) { walk(src); continue; }
|
|
918
|
-
if (!entry.isFile()) continue;
|
|
919
|
-
const rel = relative(wtSpec, src);
|
|
920
|
-
const dst = join(mainSpec, rel);
|
|
921
|
-
try {
|
|
922
|
-
if (!existsSync(dst)) {
|
|
923
|
-
mkdirSync(dirname(dst), { recursive: true });
|
|
924
|
-
copyFileSync(src, dst);
|
|
925
|
-
salvaged.push(rel);
|
|
926
|
-
} else if (reportConflict && readFileSync(src, 'utf8') !== readFileSync(dst, 'utf8')) {
|
|
927
|
-
conflicts.push(rel);
|
|
928
|
-
}
|
|
929
|
-
} catch { /* 单文件失败不拖垮整体打捞 */ }
|
|
930
|
-
}
|
|
931
|
-
};
|
|
932
|
-
walk(srcRoot);
|
|
933
|
-
};
|
|
934
|
-
salvageTree(join('changes', name), true);
|
|
935
|
-
salvageTree('docs', false);
|
|
936
|
-
if (salvaged.length > 0) {
|
|
937
|
-
console.warn(`⚠️ worktree 清理前打捞:${salvaged.length} 个 spec 流程产物只存在于 worktree 副本,已复制回主仓(apply 不搬运 .sillyspec/changes,不捞即随清理蒸发):`);
|
|
938
|
-
for (const f of salvaged.slice(0, 20)) console.warn(` ${join(mainSpec, f)}`);
|
|
939
|
-
if (salvaged.length > 20) console.warn(` …等共 ${salvaged.length} 个`);
|
|
940
|
-
details.push(`salvaged ${salvaged.length} spec artifacts to main repo`);
|
|
941
|
-
}
|
|
942
|
-
if (conflicts.length > 0) {
|
|
943
|
-
console.warn(`⚠️ worktree 与主仓存在同名但内容不同的流程产物(不覆盖,请人工确认是否需要合并):`);
|
|
944
|
-
for (const f of conflicts.slice(0, 20)) console.warn(` ${join(mainSpec, f)}`);
|
|
945
|
-
if (conflicts.length > 20) console.warn(` …等共 ${conflicts.length} 个`);
|
|
946
|
-
details.push(`conflicting spec artifacts: ${conflicts.length}`);
|
|
947
|
-
}
|
|
948
|
-
} catch (e) {
|
|
949
|
-
console.warn(`⚠️ spec 产物打捞跳过(不阻断清理): ${e?.message || e}`);
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
cleanup(changeName, { force = false, maxRetries = 3 } = {}) {
|
|
954
|
-
const name = validateChangeName(changeName);
|
|
955
|
-
const meta = this.getMeta(name);
|
|
956
|
-
const worktreePath = this.getWorktreePath(name);
|
|
957
|
-
const metaDir = join(this.worktreeBase, name);
|
|
958
|
-
const details = [];
|
|
959
|
-
|
|
960
|
-
// 幂等:什么都不存在 → 直接跳过
|
|
961
|
-
if (!meta && !existsSync(worktreePath) && !existsSync(metaDir)) {
|
|
962
|
-
return { result: 'skipped', mode: null, details };
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
const mode = meta?.mode || 'worktree';
|
|
966
|
-
// in-place 模式:worktreePath === 主工作区,绝对禁止删除目录本身,但 meta 目录仍应清理
|
|
967
|
-
// (否则永久残留)。native-worktree:外部隔离环境,整体跳过。
|
|
968
|
-
const isInPlace = mode === 'in-place-fallback';
|
|
969
|
-
|
|
970
|
-
// 安全检查:native-worktree 是外部隔离环境,非 force 不碰
|
|
971
|
-
if (!force && mode === 'native-worktree') {
|
|
972
|
-
return { result: 'kept', mode, details: ['native-worktree: 外部隔离环境,跳过清理'], residual: [] };
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
// fail-closed 保护(D-001@v1):junction 解链与 git worktree remove --force 之前,检查未落主仓交付变更。
|
|
976
|
-
// 清理即蒸发:worktree 有未 commit / 未 apply 到主仓 HEAD 的交付文件,拒绝清理,需显式 --force 绕过。
|
|
977
|
-
// hasUnappliedChanges 判定 main HEAD(byte-identical),git apply --3way 不 commit → apply 后仍判 true,
|
|
978
|
-
// 故 apply 后自动 cleanup 与 execute reset 显式传 force:true 绕过(D-006)。in-place / native-worktree
|
|
979
|
-
// 由 hasUnappliedChanges 内部返回 hasChanges:false,自然跳过保护,零回归。
|
|
980
|
-
if (!force) {
|
|
981
|
-
const check = this.hasUnappliedChanges(name);
|
|
982
|
-
if (check.hasChanges) {
|
|
983
|
-
console.error(`🚫 worktree cleanup 拒绝:${check.changedFiles.length} 个交付变更未落地主工作区 HEAD,清理会丢失代码。`);
|
|
984
|
-
for (const f of check.changedFiles) console.error(` ${f}`);
|
|
985
|
-
console.error(' 请先落地(sillyspec worktree apply <name>)或 commit 到分支,或显式 --force 强制清理。');
|
|
986
|
-
return { result: 'blocked', mode, details: [...details, 'blocked: uncommitted deliverable changes'], residual: [] };
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
const branch = (meta && meta.branch) || BRANCH_PREFIX + name;
|
|
991
|
-
|
|
992
|
-
// 打捞 spec 流程产物(坑 worktree-spec-artifact-misplace,2026-08-24 用户实证):必须在任何
|
|
993
|
-
// 删除动作之前。in-place(worktree 即主工作区)与 native-worktree(外部隔离环境)跳过。
|
|
994
|
-
if (!isInPlace && mode !== 'native-worktree' && existsSync(worktreePath)) {
|
|
995
|
-
this._salvageSpecArtifacts(name, worktreePath, details);
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
// Windows 保护:先解链接 worktree 内全部 node_modules junction(根 + modules 子模块,
|
|
999
|
-
// 坑 ghost-dir-junction-pierce:子模块 junction 此前不覆盖——npm ci / rm -rf / git worktree
|
|
1000
|
-
// remove 跟随任一 junction 都会穿透删主仓对应 node_modules)。
|
|
1001
|
-
if (!isInPlace && existsSync(worktreePath)) {
|
|
1002
|
-
unlinkNodeModulesLinks(worktreePath, meta, details)
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
// 1. git worktree remove(带 retry)—— in-place 跳过:无 git worktree 注册,且 worktreePath 即主工作区
|
|
1006
|
-
let gitRemoveOk = false;
|
|
1007
|
-
if (!isInPlace && existsSync(worktreePath)) {
|
|
1008
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1009
|
-
try {
|
|
1010
|
-
git(this.cwd, ['worktree', 'remove', worktreePath, '--force'], { timeout: 60000 });
|
|
1011
|
-
gitRemoveOk = true;
|
|
1012
|
-
details.push(`git worktree remove succeeded (attempt ${attempt})`);
|
|
1013
|
-
break;
|
|
1014
|
-
} catch (e) {
|
|
1015
|
-
details.push(`git worktree remove attempt ${attempt}/${maxRetries} failed: ${e.message}`);
|
|
1016
|
-
if (attempt < maxRetries) {
|
|
1017
|
-
// 短暂等待后重试(跨平台 busy-wait,见 sleepMs)
|
|
1018
|
-
sleepMs(500);
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
// 2. fallback: 确保 worktree 目录已删除(in-place 跳过——worktreePath 是主工作区)
|
|
1025
|
-
if (!isInPlace && existsSync(worktreePath)) {
|
|
1026
|
-
try {
|
|
1027
|
-
rmSync(worktreePath, { recursive: true, force: true });
|
|
1028
|
-
details.push('worktree directory force-removed (fallback)');
|
|
1029
|
-
} catch (e) {
|
|
1030
|
-
details.push(`worktree directory force-remove failed: ${e.message}`);
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
// 3. git worktree prune(清理 git 内部注册信息)
|
|
1035
|
-
try {
|
|
1036
|
-
gitQuiet(this.cwd, ['worktree', 'prune'], { timeout: 30000 });
|
|
1037
|
-
} catch {
|
|
1038
|
-
// prune 失败不阻断
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
// 4. 删除分支(fail-closed:task review 引用可达性校验。force 也不绕过——force 语义 =
|
|
1042
|
-
// 丢弃内容,不含丢弃审计链;确要删用 git branch -D 手动执行)
|
|
1043
|
-
// native-worktree 豁免(坑 worktree-user-branch-conflict,2026-08-24):该模式 meta.branch
|
|
1044
|
-
// 记的是用户自己的检出分支(非 sillyspec 所建),force 也不删——只清 sillyspec 侧注册。
|
|
1045
|
-
if (mode === 'native-worktree') {
|
|
1046
|
-
details.push(`branch kept: native-worktree 模式的 branch(${branch})是用户自己的检出分支,不删除`);
|
|
1047
|
-
} else
|
|
1048
|
-
// 升级(坑 cleanup-branch-review-anchor-tag,2026-08-21 实证):分支被 review.json 引用
|
|
1049
|
-
// 时不再只能手动保留——打 `sillyspec-audit/<branch>` 轻量 tag 锚定分支 tip(ref 前缀独立
|
|
1050
|
-
// 于 sillyspec/* 分支族,doctor 孤儿分支扫描不误伤),commit 经 tag 保持可达(gc 安全),
|
|
1051
|
-
// 随后正常删分支。tag 创建失败 → 回退保留(宁保留勿丢审计链)。
|
|
1052
|
-
try {
|
|
1053
|
-
const reviewRefs = this._branchReviewReferences(branch);
|
|
1054
|
-
if (reviewRefs.length > 0) {
|
|
1055
|
-
const tagName = 'sillyspec-audit/' + branch;
|
|
1056
|
-
let tagOk = false;
|
|
1057
|
-
try {
|
|
1058
|
-
if (!gitQuiet(this.cwd, ['rev-parse', '--verify', '--quiet', `refs/tags/${tagName}`])) {
|
|
1059
|
-
git(this.cwd, ['tag', tagName, branch]);
|
|
1060
|
-
}
|
|
1061
|
-
tagOk = true;
|
|
1062
|
-
} catch (e) {
|
|
1063
|
-
details.push(`audit tag creation failed: ${e.message}`);
|
|
1064
|
-
}
|
|
1065
|
-
if (tagOk) {
|
|
1066
|
-
gitQuiet(this.cwd, ['branch', '-D', branch]);
|
|
1067
|
-
details.push(`branch deleted after anchoring tip to tag ${tagName} (audit 链可达)`);
|
|
1068
|
-
console.log(`🔗 审计锚定:${branch} 被 ${reviewRefs.length} 个 task review.json 引用其 commit——已打 tag ${tagName} 锚定(commit 保持可达,gc 安全)并删除分支 ref。`);
|
|
1069
|
-
console.log(` 确认不再需要该审计链后可删 tag:git tag -d ${tagName}`);
|
|
1070
|
-
} else {
|
|
1071
|
-
details.push(`branch kept: ${reviewRefs.length} 个 task review.json 引用分支上的 commit(base/head 审计保护)`);
|
|
1072
|
-
console.log(`🔗 分支保留:${branch} 上有 ${reviewRefs.length} 个 task review.json 引用其 commit(apply 只复制文件内容,ref 删除后 base/head 悬空无法复核)。确要删除:git branch -D ${branch}`);
|
|
1073
|
-
}
|
|
1074
|
-
} else {
|
|
1075
|
-
gitQuiet(this.cwd, ['branch', '-D', branch]);
|
|
1076
|
-
details.push('branch deleted');
|
|
1077
|
-
}
|
|
1078
|
-
} catch {
|
|
1079
|
-
// 分支可能已被删除(幂等跳过);引用校验自身异常也归入此分支——不因校验失败而误删
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
// 5. 清除 meta 目录(in-place 模式也执行——这是 in-place meta 残留的修复点)
|
|
1083
|
-
if (existsSync(metaDir)) {
|
|
1084
|
-
try {
|
|
1085
|
-
rmSync(metaDir, { recursive: true, force: true });
|
|
1086
|
-
details.push('meta directory cleaned');
|
|
1087
|
-
} catch (e) {
|
|
1088
|
-
details.push(`meta directory cleanup failed: ${e.message}`);
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
// 6. 最终验证:确认清理完成(in-place 模式 worktreePath=主工作区,不纳入残留检查)
|
|
1093
|
-
const residual = [];
|
|
1094
|
-
if (!isInPlace && existsSync(worktreePath)) residual.push(`worktree dir: ${worktreePath}`);
|
|
1095
|
-
if (existsSync(metaDir)) residual.push(`meta dir: ${metaDir}`);
|
|
1096
|
-
// 斜杠方向归一后比较(体检 BUG-07):Windows 下 git worktree list 输出正斜杠、
|
|
1097
|
-
// worktreePath 是反斜杠,直接 includes 恒 false → 残留场景漏报、cleanup 误报 cleaned
|
|
1098
|
-
const toPosixNorm = (p) => String(p || '').replace(/\\/g, '/').toLowerCase()
|
|
1099
|
-
const wtListOut = gitQuiet(this.cwd, ['worktree', 'list'], { timeout: 30000 })
|
|
1100
|
-
if (!isInPlace && wtListOut && toPosixNorm(wtListOut).includes(toPosixNorm(worktreePath))) {
|
|
1101
|
-
residual.push('git worktree list still references this worktree');
|
|
1102
|
-
}
|
|
1103
|
-
if (residual.length > 0) {
|
|
1104
|
-
details.push(`⚠️ 残留: ${residual.join('; ')}`);
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
// result:有残留→partial;in-place(无隔离目录可删,只清了 meta)→cleaned;否则按 git remove 成败
|
|
1108
|
-
let result;
|
|
1109
|
-
if (residual.length > 0) {
|
|
1110
|
-
result = 'partial';
|
|
1111
|
-
} else if (isInPlace) {
|
|
1112
|
-
result = 'cleaned';
|
|
1113
|
-
} else {
|
|
1114
|
-
result = gitRemoveOk ? 'cleaned' : 'force-cleaned';
|
|
1115
|
-
}
|
|
1116
|
-
return { result, mode, details, residual };
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
/**
|
|
1120
|
-
* worktree 健康检查 + 可选修复
|
|
1121
|
-
* 检查项:
|
|
1122
|
-
* - git worktree list 中的孤儿条目(目录不存在)
|
|
1123
|
-
* - worktree 目录存在但 git 不认识
|
|
1124
|
-
* - meta 存在但 worktree 目录不存在
|
|
1125
|
-
* - worktree 目录存在但 meta 不存在(幽灵目录)
|
|
1126
|
-
* - SillySpec 分支残留(sillyspec/* 但无对应 meta)
|
|
1127
|
-
* - 超过指定小时的过期 worktree
|
|
1128
|
-
*
|
|
1129
|
-
* @param {{ fix?: boolean, staleHours?: number }} opts
|
|
1130
|
-
* @returns {{ issues: Array<{ type: string, name: string, detail: string, fixable: boolean }>, fixed: string[], unfixable: string[] }}
|
|
1131
|
-
*/
|
|
1132
|
-
/**
|
|
1133
|
-
* doctor --fix 的依赖重供给:先解 worktree/node_modules junction(保护主仓 node_modules),
|
|
1134
|
-
* 再 provisionDeps(force:true) 强制走 install 分支重装。
|
|
1135
|
-
*
|
|
1136
|
-
* Windows 保护(与 cleanup 722-743 同源坑):不解链直接 install 会经 junction 误改主仓
|
|
1137
|
-
* node_modules 内容。in-place-fallback 无独立 node_modules(worktreePath 即主仓),跳过解链,
|
|
1138
|
-
* 仅 install。force=true 绕过 provisionDeps 的 lockfile 一致快路径,确保真重装而非幂等 linked。
|
|
1139
|
-
*
|
|
1140
|
-
* @param {string} name - changeName
|
|
1141
|
-
* @param {string} wtPath - worktree 根目录(meta.worktreePath)
|
|
1142
|
-
* @returns {{ ok: boolean, msg: string }}
|
|
1143
|
-
*/
|
|
1144
|
-
_doctorReprovision(name, wtPath, opts = {}) {
|
|
1145
|
-
try {
|
|
1146
|
-
const meta = this.getMeta(name) || {};
|
|
1147
|
-
const isInPlace = meta.mode === 'in-place-fallback';
|
|
1148
|
-
// 先解 junction(非 in-place):统一走共享解链(坑 ghost-dir-junction-pierce:覆盖根 +
|
|
1149
|
-
// depsModules 子模块;lstat/rmdir 失败 fail-loud,D-001@v1 / review-2026-08-09 #4——
|
|
1150
|
-
// 静默跳过解链会让 install 经 junction 误改主仓 node_modules)
|
|
1151
|
-
if (!isInPlace && existsSync(wtPath)) {
|
|
1152
|
-
unlinkNodeModulesLinks(wtPath, meta)
|
|
1153
|
-
}
|
|
1154
|
-
// 触发分流(坑 doctor-reprovision-junction-missing,2026-08-22 实证:junction 未建的
|
|
1155
|
-
// missing/failed 场景被 force install 强制重装——install 静默失败面大且慢;lockfile 一致时
|
|
1156
|
-
// 重建 junction 才是对症动作。opts.relinkFirst(missing/failed 触发)→ 非 force 跑:解链后
|
|
1157
|
-
// node_modules 不存在,tryLink 直接重建;lockfile 漂移自动落 install。stale/main-drift 维持
|
|
1158
|
-
// force 重装(依赖确实变了)。
|
|
1159
|
-
const deps = provisionDeps(wtPath, this.cwd, {
|
|
1160
|
-
specBase: join(this.cwd, '.sillyspec'),
|
|
1161
|
-
force: !opts.relinkFirst,
|
|
1162
|
-
}) || {};
|
|
1163
|
-
const metaPath = join(this.getWorktreePath(name), META_FILE);
|
|
1164
|
-
// 错误保真(坑 doctor-reprovision-error-erased,2026-08-23 实证:provisionDeps 的子模块
|
|
1165
|
-
// 后验证/mismatch 摘要追加 depsError 但不降级 depsStatus——原「depsStatus!=='failed' 即删
|
|
1166
|
-
// depsError」把失败证据从 meta 抹掉,msg 也不含 → doctor 报 re-provisioned 成功而子模块
|
|
1167
|
-
// 实际无链接无提示)。三改:①本次供给全量干净(无 depsError 且非 failed)才清旧值;
|
|
1168
|
-
// ②msg 拼上 depsError;③failed/有错误 → ok:false 落 unfixable(原无条件 ok:true 把失败
|
|
1169
|
-
// 也进 fixed 数组,CLI 打 ✅ 误导「已修好」)。
|
|
1170
|
-
const hasDepError = deps.depsStatus === 'failed' || !!deps.depsError;
|
|
1171
|
-
const merged = { ...meta, ...deps };
|
|
1172
|
-
if (!hasDepError) delete merged.depsError;
|
|
1173
|
-
writeMetaAtomic(metaPath, merged);
|
|
1174
|
-
const msg = `re-provisioned ${name}: depsStatus=${deps.depsStatus}` + (deps.depsError ? `;⚠️ ${deps.depsError}` : '');
|
|
1175
|
-
return hasDepError ? { ok: false, msg } : { ok: true, msg };
|
|
1176
|
-
} catch (e) {
|
|
1177
|
-
return { ok: false, msg: `re-provision failed for ${name}: ${e.message}` };
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
/**
|
|
1182
|
-
* worktree 健康检查 + 可选修复
|
|
1183
|
-
* 检查项:
|
|
1184
|
-
* - git worktree list 中的孤儿条目(目录不存在)
|
|
1185
|
-
* - worktree 目录存在但 git 不认识
|
|
1186
|
-
* - meta 存在但 worktree 目录不存在
|
|
1187
|
-
* - worktree 目录存在但 meta 不存在(幽灵目录)
|
|
1188
|
-
* - SillySpec 分支残留(sillyspec/* 但无对应 meta)
|
|
1189
|
-
* - 超过指定小时的过期 worktree
|
|
1190
|
-
* - deps 依赖状态(failed/missing/stale/main-drift,复用 checkDepsFreshness 统一判定)
|
|
1191
|
-
*
|
|
1192
|
-
* @param {{ fix?: boolean, staleHours?: number, changeName?: string|null }} opts
|
|
1193
|
-
* - changeName:非 null 时仅扫描该 change(deps/ghost/stale/orphan-git/orphan-branch 全部按 changeName 过滤);
|
|
1194
|
-
* 不传则全量扫(兼容现有行为)。
|
|
1195
|
-
* @returns {{ issues: Array<{ type: string, name: string, detail: string, fixable: boolean }>, fixed: string[], unfixable: string[] }}
|
|
1196
|
-
*/
|
|
1197
|
-
async doctor({ fix = false, staleHours = 24, changeName = null } = {}) {
|
|
1198
|
-
const issues = [];
|
|
1199
|
-
const fixed = [];
|
|
1200
|
-
const unfixable = [];
|
|
1201
|
-
|
|
1202
|
-
// 活跃变更注册表(坑 doctor-fix-orphan-branch-parallel-active,2026-08-20 实证):孤儿分支
|
|
1203
|
-
// 判定原只看本地 meta 目录注册表(metaNames),与变更活跃态的权威注册表(进度库 changes 表)
|
|
1204
|
-
// 数据源不一致——并行会话的活跃变更(meta 已清/in-place/平台模式 meta 在别处)分支会被全局
|
|
1205
|
-
// doctor --fix 误删。删分支前交叉核对:分支名 ∈ 活跃变更 → 保留(fixable:false 提示人工确认)。
|
|
1206
|
-
// 先探 DB 文件存在再实例化 ProgressManager(坑7 读路径建库:_ensureDB 不存在会建库污染)。
|
|
1207
|
-
// null = 无法核对(读失败 或 无进度库——坑 worktree-user-branch-conflict 2026-08-24 收紧:
|
|
1208
|
-
// 无库时无法区分孤儿与用户自建分支,从「照删」改保守保留,git-only 便利让位于不误删);
|
|
1209
|
-
// Set = 进度库可读(空集=无活跃变更,非活跃孤儿仍可删)。
|
|
1210
|
-
let activeChanges = null;
|
|
1211
|
-
let dbMissing = false;
|
|
1212
|
-
try {
|
|
1213
|
-
const specBaseOfWt = dirname(dirname(this.worktreeBase)); // <specBase>(.runtime/worktrees 上两级)
|
|
1214
|
-
if (existsSync(join(specBaseOfWt, '.runtime', 'sillyspec.db'))) {
|
|
1215
|
-
const { ProgressManager } = await import('./progress.js');
|
|
1216
|
-
activeChanges = new Set(new ProgressManager({ specDir: specBaseOfWt }).listChanges(this.cwd));
|
|
1217
|
-
} else {
|
|
1218
|
-
activeChanges = null;
|
|
1219
|
-
dbMissing = true;
|
|
1220
|
-
}
|
|
1221
|
-
} catch (e) {
|
|
1222
|
-
console.warn(`⚠️ doctor 进度库读取失败,孤儿分支的活跃态交叉核对不可用(保守不自动删): ${e.message}`);
|
|
1223
|
-
activeChanges = null;
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
// 1. 列出 git worktree list 中的条目
|
|
1227
|
-
let gitWorktreeList = [];
|
|
1228
|
-
try {
|
|
1229
|
-
const raw = git(this.cwd, ['worktree', 'list', '--porcelain'], { timeout: 30000 });
|
|
1230
|
-
const entries = raw.split(/\n\n/).filter(Boolean);
|
|
1231
|
-
for (const entry of entries) {
|
|
1232
|
-
const lines = entry.split('\n');
|
|
1233
|
-
const wtPath = lines.find(l => l.startsWith('worktree '))?.replace('worktree ', '');
|
|
1234
|
-
// git 2.20+ porcelain 在目录缺失时输出 `missing` 行;作为 existsSync 的权威交叉验证
|
|
1235
|
-
const missing = lines.some(l => l === 'missing');
|
|
1236
|
-
if (wtPath && wtPath !== this.cwd) { // 排除主工作区
|
|
1237
|
-
gitWorktreeList.push({ path: wtPath, missing, raw: entry });
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
} catch {
|
|
1241
|
-
// git worktree 不可用,跳过
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
// 2. 列出 SillySpec meta 条目
|
|
1245
|
-
const metaEntries = this.list();
|
|
1246
|
-
const metaNames = new Set(metaEntries.map(m => m.changeName));
|
|
1247
|
-
|
|
1248
|
-
// 3. 检查 git worktree list 中的孤儿条目
|
|
1249
|
-
// ⚠️ 只在 git 自己标记 missing 时自动 prune。目录不可见但 git 未标记 missing 的
|
|
1250
|
-
// 情况(平台模式/容器路径不一致、符号链接、Windows 路径格式、旧 git 无 missing 标记)
|
|
1251
|
-
// 只告警不自动 prune —— existsSync 在这些场景会误判,自动 prune 会杀死实际在用的
|
|
1252
|
-
// worktree(sillyspec-worktree-platform-mode-bug)。git 内部状态是 worktree 生命周期的
|
|
1253
|
-
// 权威来源,用它做 prune 前提可杜绝误杀。
|
|
1254
|
-
for (const wt of gitWorktreeList) {
|
|
1255
|
-
if (!existsSync(wt.path)) {
|
|
1256
|
-
const name = this._pathToChangeName(wt.path);
|
|
1257
|
-
if (changeName && name !== changeName) continue; // --change 过滤:仅扫指定 change
|
|
1258
|
-
if (wt.missing === true) {
|
|
1259
|
-
// git 明确标记目录缺失(git 2.20+)→ 真 orphan,prune 安全
|
|
1260
|
-
issues.push({ type: 'orphan-git-entry', name: name || wt.path, detail: `git worktree 标记 missing: ${wt.path}`, fixable: true });
|
|
1261
|
-
if (fix) {
|
|
1262
|
-
try { gitQuiet(this.cwd, ['worktree', 'prune'], { timeout: 30000 }); fixed.push(`pruned orphan: ${wt.path}`); } catch { unfixable.push(`prune failed for: ${wt.path}`); }
|
|
1263
|
-
}
|
|
1264
|
-
} else {
|
|
1265
|
-
// 目录不可见但 git 未标记 missing → 可能 existsSync 误判,保守不自动 prune
|
|
1266
|
-
issues.push({ type: 'orphan-git-entry', name: name || wt.path,
|
|
1267
|
-
detail: `git worktree 引用 ${wt.path} 目录不可见但 git 未标记 missing——可能路径/权限导致误判,未自动 prune(避免误杀在用的 worktree)。确认废弃后手动: git worktree prune`,
|
|
1268
|
-
fixable: false });
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
// 4. 扫描 worktreeBase 目录,检查幽灵目录和孤儿 meta
|
|
1274
|
-
if (existsSync(this.worktreeBase)) {
|
|
1275
|
-
const entries = readdirSync(this.worktreeBase, { withFileTypes: true });
|
|
1276
|
-
for (const entry of entries) {
|
|
1277
|
-
if (!entry.isDirectory()) continue;
|
|
1278
|
-
const name = entry.name;
|
|
1279
|
-
if (changeName && name !== changeName) continue; // --change 过滤:仅扫指定 change
|
|
1280
|
-
const dirPath = join(this.worktreeBase, name);
|
|
1281
|
-
const hasMeta = existsSync(join(dirPath, META_FILE));
|
|
1282
|
-
const meta = hasMeta ? this.getMeta(name) : null;
|
|
1283
|
-
|
|
1284
|
-
// deps 依赖状态检查(change 2026-06-28-worktree-deps-provision / 2026-08-05-tooling-feedback-fixes)
|
|
1285
|
-
// 复用 checkDepsFreshness(H1,worktree-deps.js),优先级 failed→missing→stale→main-drift→fresh。
|
|
1286
|
-
// 放宽原 909 in-place-fallback 守卫:in-place 也跑(main-drift/stale/failed 对它同样有意义,
|
|
1287
|
-
// 真 in-place 的 wtPath===主仓→wtHash===mainHash→不会误报 main-drift,仅 failed/stale 有意义)。
|
|
1288
|
-
// fix 时 _doctorReprovision 自行判断是否解链(in-place 不解链,仅 install)。
|
|
1289
|
-
// 归档态前置闸(坑 doctor-reprovision-archived-change,2026-08-21 实证:已归档变更的
|
|
1290
|
-
// worktree 被 doctor 当活跃任务 re-provision——给死目录装依赖)。归档判定(changes/archive/
|
|
1291
|
-
// 实体 或 进度库 status='archived')命中 → 跳过供给,fix 时走 cleanup(其内建
|
|
1292
|
-
// hasUnappliedChanges 护栏仍在,未落仓内容照样拒删)。
|
|
1293
|
-
if (meta && meta.worktreePath && existsSync(meta.worktreePath)) {
|
|
1294
|
-
const isArchivedChange = existsSync(join(this.cwd, '.sillyspec', 'changes', 'archive', name))
|
|
1295
|
-
|| this._isChangeArchivedInDb(name);
|
|
1296
|
-
if (isArchivedChange) {
|
|
1297
|
-
issues.push({ type: 'worktree-archived-change', name, fixable: meta.mode !== 'native-worktree',
|
|
1298
|
-
detail: `变更 ${name} 已归档但 worktree 残留——不供给依赖(死目录),建议清理(cleanup 内建未落仓护栏)` });
|
|
1299
|
-
if (fix && meta.mode !== 'native-worktree') {
|
|
1300
|
-
try {
|
|
1301
|
-
const r = this.cleanup(name);
|
|
1302
|
-
if (r.result === 'cleaned' || r.result === 'force-cleaned') {
|
|
1303
|
-
fixed.push(`cleaned archived-change worktree: ${name}`);
|
|
1304
|
-
} else if (r.result === 'blocked') {
|
|
1305
|
-
unfixable.push(`cleanup blocked (archived change 有未落仓内容): ${name} — 先 apply/commit 再清`);
|
|
1306
|
-
} else {
|
|
1307
|
-
unfixable.push(`cleanup skipped (archived change): ${name}`);
|
|
1308
|
-
}
|
|
1309
|
-
} catch { unfixable.push(`cleanup failed (archived change): ${name}`); }
|
|
1310
|
-
}
|
|
1311
|
-
} else {
|
|
1312
|
-
const wtPath = meta.worktreePath;
|
|
1313
|
-
const fresh = checkDepsFreshness(meta, wtPath, this.cwd);
|
|
1314
|
-
const issueTypeByStatus = {
|
|
1315
|
-
failed: 'deps-failed',
|
|
1316
|
-
missing: 'deps-missing',
|
|
1317
|
-
stale: 'deps-stale',
|
|
1318
|
-
'main-drift': 'deps-main-drift',
|
|
1319
|
-
};
|
|
1320
|
-
const issueType = issueTypeByStatus[fresh.status] || null;
|
|
1321
|
-
if (issueType) {
|
|
1322
|
-
issues.push({ type: issueType, name, detail: fresh.detail, fixable: true });
|
|
1323
|
-
if (fix) {
|
|
1324
|
-
// relinkFirst:missing/failed 多为 junction 丢失/上次供给失败——link 优先重建;
|
|
1325
|
-
// stale/main-drift 是依赖真变了——force 重装
|
|
1326
|
-
const r = this._doctorReprovision(name, wtPath, { relinkFirst: issueType === 'deps-missing' || issueType === 'deps-failed' });
|
|
1327
|
-
(r.ok ? fixed : unfixable).push(r.msg);
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
// editable install 越界检查(坑 worktree-editable-install-escape,2026-08-25 实证
|
|
1331
|
-
// gen:types 坑):worktree venv 的 editable install 指向 worktree 外(主仓)时,
|
|
1332
|
-
// gen:types/后端命令/pytest 静默加载旧代码零报错——此前靠模块文档人工记忆,内建检查化。
|
|
1333
|
-
// fixable:false(重装决策留给用户:uv sync / pip install -e . 需按项目方式选)。
|
|
1334
|
-
try {
|
|
1335
|
-
const escapes = detectEditableInstallEscape(wtPath);
|
|
1336
|
-
if (escapes.length > 0) {
|
|
1337
|
-
issues.push({ type: 'editable-install-escape', name, fixable: false,
|
|
1338
|
-
detail: `worktree venv 含 ${escapes.length} 个 editable install 指向 worktree 外(${escapes.slice(0, 5).map(e => `${e.pkg}→${e.target}`).join(', ')}${escapes.length > 5 ? ' 等' : ''})——gen:types/后端命令会静默加载 worktree 外旧代码。修复:在 worktree 内重装(uv sync / uv pip install -e .)后重跑生成命令` });
|
|
1339
|
-
}
|
|
1340
|
-
} catch { /* 探测失败不阻断 doctor */ }
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
// meta 存在但 worktree 目录不存在
|
|
1345
|
-
if (meta && meta.worktreePath && !existsSync(meta.worktreePath)) {
|
|
1346
|
-
issues.push({ type: 'meta-no-dir', name, detail: `meta 存在但 worktree 目录不存在: ${meta.worktreePath}`, fixable: true });
|
|
1347
|
-
if (fix) {
|
|
1348
|
-
try { rmSync(dirPath, { recursive: true, force: true }); fixed.push(`cleaned orphan meta: ${name}`); } catch { unfixable.push(`cleanup failed for: ${name}`); }
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
|
|
1352
|
-
// worktree 目录存在但 meta 不存在(幽灵目录)
|
|
1353
|
-
if (!hasMeta && existsSync(dirPath)) {
|
|
1354
|
-
// 可能是 in-place 模式的 meta-only 目录,或者真正的幽灵
|
|
1355
|
-
const files = readdirSync(dirPath);
|
|
1356
|
-
if (files.length === 0 || (files.length === 1 && files[0] === META_FILE)) {
|
|
1357
|
-
issues.push({ type: 'ghost-dir', name, detail: `空目录/幽灵目录: ${dirPath}`, fixable: true });
|
|
1358
|
-
if (fix) {
|
|
1359
|
-
// 安全删除(坑 ghost-dir-junction-pierce):统一先解 node_modules junction 再删
|
|
1360
|
-
try { safeRemoveWorktreeDir(dirPath); fixed.push(`removed ghost dir: ${name}`); } catch (e) { unfixable.push(`remove failed for: ${name} (${e.message.slice(0, 120)})`); }
|
|
1361
|
-
}
|
|
1362
|
-
} else {
|
|
1363
|
-
// junction 侦测(坑 ghost-dir-junction-pierce):无 meta 的含文件目录不自动删(内容
|
|
1364
|
-
// 不明,fixable:false 交人工裁决),但必须警示穿透风险 + 给安全手动指引——人工
|
|
1365
|
-
// rm -rf(Git Bash coreutils 跟随 junction)会删主仓 node_modules。
|
|
1366
|
-
const junctionHits = [];
|
|
1367
|
-
for (const cand of [join(dirPath, 'node_modules'), ...files.filter(f => !f.startsWith('.')).slice(0, 20).map(f => join(dirPath, f, 'node_modules'))]) {
|
|
1368
|
-
try { if (lstatSync(cand).isSymbolicLink()) junctionHits.push(relative(dirPath, cand) || '(root)') } catch {}
|
|
1369
|
-
}
|
|
1370
|
-
const junctionNote = junctionHits.length > 0
|
|
1371
|
-
? ` ⚠️ 含 node_modules junction(${junctionHits.join(', ')})——手动删除前必须先解链(cmd /c rmdir "<目录>\\<相对路径>"),直接 rm -rf 会穿透删除主仓 node_modules;解链后删除目录或跑 sillyspec worktree doctor --fix`
|
|
1372
|
-
: '';
|
|
1373
|
-
issues.push({ type: 'ghost-dir-with-files', name, detail: `目录存在但无 meta.json: ${dirPath} (含 ${files.length} 文件)${junctionNote}`, fixable: false });
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
// 检查过期 worktree
|
|
1378
|
-
if (meta && meta.createdAt) {
|
|
1379
|
-
const ageMs = Date.now() - new Date(meta.createdAt).getTime();
|
|
1380
|
-
const ageHours = ageMs / (1000 * 60 * 60);
|
|
1381
|
-
const staleFixable = meta.mode !== 'native-worktree';
|
|
1382
|
-
if (ageHours > staleHours) {
|
|
1383
|
-
issues.push({ type: 'stale', name, detail: `worktree 已存在 ${Math.round(ageHours)} 小时(超过 ${staleHours}h 阈值)${staleFixable ? '' : '(native-worktree 外部环境,不可自动清理)'}`, fixable: staleFixable });
|
|
1384
|
-
if (fix && staleFixable) {
|
|
1385
|
-
try {
|
|
1386
|
-
const result = this.cleanup(name);
|
|
1387
|
-
if (result.result === 'cleaned' || result.result === 'force-cleaned') {
|
|
1388
|
-
fixed.push(`cleaned stale: ${name}`);
|
|
1389
|
-
} else if (result.result === 'blocked') {
|
|
1390
|
-
console.error(`🚫 拒绝清理过期 worktree ${name}: 有未落主仓交付变更,请先 sillyspec worktree apply ${name} 或 commit 到分支,再重试 doctor --fix`);
|
|
1391
|
-
unfixable.push(`cleanup blocked: ${name}(有未落主仓交付变更)`);
|
|
1392
|
-
} else {
|
|
1393
|
-
unfixable.push(`cleanup skipped: ${name}`);
|
|
1394
|
-
}
|
|
1395
|
-
} catch { unfixable.push(`cleanup failed: ${name}`); }
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
// 5. 检查 SillySpec 分支残留(活跃变更交叉核对,见方法头 activeChanges 注释)
|
|
1403
|
-
try {
|
|
1404
|
-
// QUAL-01 收口:裸 execFileSync → git-helper git(原调用点未传 timeout,统一入口默认 5s 足够 branch --list)
|
|
1405
|
-
const branches = git(this.cwd, ['branch', '--list', `${BRANCH_PREFIX}*`]);
|
|
1406
|
-
if (branches) {
|
|
1407
|
-
for (const line of branches.split('\n').filter(Boolean)) {
|
|
1408
|
-
const branch = line.replace(/^\*?\s+/, '').trim();
|
|
1409
|
-
const name = branch.replace(BRANCH_PREFIX, '');
|
|
1410
|
-
if (changeName && name !== changeName) continue; // --change 过滤:仅扫指定 change
|
|
1411
|
-
if (!metaNames.has(name)) {
|
|
1412
|
-
if (activeChanges && activeChanges.has(name)) {
|
|
1413
|
-
// 变更仍注册活跃:分支大概率是并行会话在用(或 apply 后被 review 引用审计保护保留)
|
|
1414
|
-
// ——数据源不一致的正解是保留 + 人工确认,不是删
|
|
1415
|
-
issues.push({ type: 'active-branch', name, fixable: false,
|
|
1416
|
-
detail: `分支 ${branch} 无本地 meta,但变更 "${name}" 在进度库仍注册为活跃(并行会话在跑 / meta 已清分支在用)——保留。确认废弃后:先归档/结束该变更,或手动 git branch -D ${branch}` });
|
|
1417
|
-
continue;
|
|
1418
|
-
}
|
|
1419
|
-
if (activeChanges === null) {
|
|
1420
|
-
// 无法核对活跃态(读失败 或 无进度库):保守不自动删(宁可漏删不可误删——
|
|
1421
|
-
// 坑 worktree-user-branch-conflict:无库时该分支可能是用户自建的,无凭据区分)
|
|
1422
|
-
const why = dbMissing ? '无进度库,无法区分孤儿与用户自建分支' : '进度库不可读,无法交叉核对活跃态'
|
|
1423
|
-
issues.push({ type: 'orphan-branch', name, fixable: false,
|
|
1424
|
-
detail: `分支疑似残留(无对应 meta,且${why}): ${branch}——保守不自动删,人工确认后 git branch -D ${branch}` });
|
|
1425
|
-
continue;
|
|
1426
|
-
}
|
|
1427
|
-
// review 锚点复核(坑 worktree-user-branch-conflict):分支上有 task review base/head
|
|
1428
|
-
// 引用的 commit → 审计链在用,不自动删(与 cleanup 的分支删除审计保护同口径)
|
|
1429
|
-
let reviewRefs = [];
|
|
1430
|
-
try { reviewRefs = this._branchReviewReferences(branch); } catch { reviewRefs = [] }
|
|
1431
|
-
if (reviewRefs.length > 0) {
|
|
1432
|
-
issues.push({ type: 'orphan-branch', name, fixable: false,
|
|
1433
|
-
detail: `分支无对应 meta 但被 ${reviewRefs.length} 个 task review.json 的 base/head 引用(审计锚点)——保留。确要删除:先归档/迁移审计引用,或手动 git branch -D ${branch}` });
|
|
1434
|
-
continue;
|
|
1435
|
-
}
|
|
1436
|
-
issues.push({ type: 'orphan-branch', name, detail: `分支残留(无对应 meta,且非活跃变更): ${branch}`, fixable: true });
|
|
1437
|
-
if (fix) {
|
|
1438
|
-
try { gitQuiet(this.cwd, ['branch', '-D', branch]); fixed.push(`deleted orphan branch: ${branch}`); } catch { unfixable.push(`branch delete failed: ${branch}`); }
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
} catch {}
|
|
1444
|
-
|
|
1445
|
-
return { issues, fixed, unfixable };
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
/**
|
|
1449
|
-
* 检查 worktree 是否有尚未落到主工作区的交付变更。
|
|
1450
|
-
*
|
|
1451
|
-
* 语义:worktree 相对 baseline 的交付变更(tracked + untracked,排除 .sillyspec//meta.json)里,
|
|
1452
|
-
* 哪些还没 byte-identical 出现在主工作区 HEAD。全部已在 main HEAD(含 cherry-pick/rebase/merge/apply
|
|
1453
|
-
* 落地)→ hasChanges:false(调用方可安全 cleanup);否则 hasChanges:true(保留 worktree)。
|
|
1454
|
-
* 检测失败/拿不准 → 保守 hasChanges:true(防误删未落代码)。
|
|
1455
|
-
* @param {string} changeName
|
|
1456
|
-
* @returns {{ hasChanges: boolean, changedFiles: string[], reason?: string }}
|
|
1457
|
-
*/
|
|
1458
|
-
hasUnappliedChanges(changeName) {
|
|
1459
|
-
const name = validateChangeName(changeName);
|
|
1460
|
-
const meta = this.getMeta(name);
|
|
1461
|
-
if (!meta) return { hasChanges: false, changedFiles: [], reason: 'no meta' };
|
|
1462
|
-
|
|
1463
|
-
const worktreePath = meta.worktreePath;
|
|
1464
|
-
if (!worktreePath || !existsSync(worktreePath)) {
|
|
1465
|
-
// 目录不存在:无法判定是否有未落代码——分支可能有已 commit 但未 apply 的交付(execute 批量完成
|
|
1466
|
-
// cleanup 删分支 ref 盲区,memory execute-batch-cleanup-deletes-branch-recovery)。保守保留
|
|
1467
|
-
// (hasChanges:true),防 cleanup 误删分支让 commit 变 dangling。目录真丢时用户 --force 强清。
|
|
1468
|
-
return { hasChanges: true, changedFiles: [], reason: 'worktree dir not found (conservative keep)' };
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
// in-place 模式没有隔离目录,不算有未 apply 的变更
|
|
1472
|
-
if (meta.mode === 'in-place-fallback') {
|
|
1473
|
-
return { hasChanges: false, changedFiles: [], reason: 'in-place mode' };
|
|
1474
|
-
}
|
|
1475
|
-
// native-worktree 是用户外部隔离环境,不纳入"未应用"判定(与 execute 完成路径一致)
|
|
1476
|
-
if (meta.mode === 'native-worktree') {
|
|
1477
|
-
return { hasChanges: false, changedFiles: [], reason: 'native-worktree (external)' };
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
|
-
const diffBase = meta.baselineCommit || meta.baseHash;
|
|
1481
|
-
if (!diffBase) {
|
|
1482
|
-
// 有 meta 但缺 diff 起点:无法判定是否有未落代码,保守保留(防 cleanup 删分支)
|
|
1483
|
-
return { hasChanges: true, changedFiles: [], reason: 'no diff base (conservative keep)' };
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
const isDeliverable = f => f && !f.startsWith('.sillyspec/') && f !== 'meta.json';
|
|
1487
|
-
const isDeliverablePath = isDeliverable;
|
|
1488
|
-
|
|
1489
|
-
// 合并可达性短路(坑 cleanup-merged-branch-byte-false-positive,2026-08-23 实证:分支已
|
|
1490
|
-
// merge 进主仓后主仓后续提交又改了同一批文件——下方逐字节判定必然不等 → 误报「未落地」
|
|
1491
|
-
// 拦 cleanup 要 --force,用户只能 git branch --merged 自证。历史可达性才是「交付已进主仓」
|
|
1492
|
-
// 的权威口径:分支 tip 是主仓 HEAD 的祖先 ⟺ diffBase..tip 的全部交付已在主仓历史里,后续
|
|
1493
|
-
// 演进不改写这一事实)。四条件全真才短路,任一拿不准(ref 已删/漂移/git 失败)落回原逐字节
|
|
1494
|
-
// 逻辑 fail-open,cherry-pick 落地(tip 非祖先)同样走原路径——零回归。
|
|
1495
|
-
try {
|
|
1496
|
-
const branch = meta.branch;
|
|
1497
|
-
if (branch) {
|
|
1498
|
-
const tip = gitQuiet(this.cwd, ['rev-parse', '--verify', branch]);
|
|
1499
|
-
const wtHead = gitQuiet(worktreePath, ['rev-parse', 'HEAD']);
|
|
1500
|
-
// 条件②③:worktree HEAD 就是分支 tip(无漂移/未删),且工作区无未 commit/未 track 的
|
|
1501
|
-
// 交付改动(那些不在祖先可达性内,须走逐字节路径逐个核对)
|
|
1502
|
-
if (tip && wtHead && tip === wtHead) {
|
|
1503
|
-
const wtStatus = gitQuiet(worktreePath, ['status', '--porcelain'], { timeout: 30000 });
|
|
1504
|
-
if (wtStatus !== null) {
|
|
1505
|
-
const deliverableDirty = wtStatus.split('\n').filter(Boolean)
|
|
1506
|
-
.map(l => l.slice(3).split(' -> ').pop())
|
|
1507
|
-
.some(isDeliverablePath);
|
|
1508
|
-
if (!deliverableDirty) {
|
|
1509
|
-
// 条件④:merge-base(branch, HEAD) === tip ⟺ tip 是 HEAD 祖先(不用 --is-ancestor:
|
|
1510
|
-
// gitQuiet 把 exit 1「非祖先」与真失败都归 null 无法区分;merge-base 恒 exit 0,
|
|
1511
|
-
// 输出等于 tip 即祖先,ref 失败返回 null 自然落回原逻辑)
|
|
1512
|
-
const mb = gitQuiet(this.cwd, ['merge-base', branch, 'HEAD']);
|
|
1513
|
-
if (mb === tip) {
|
|
1514
|
-
return { hasChanges: false, changedFiles: [], reason: `branch fully merged into main HEAD (tip ${tip.slice(0, 8)} is ancestor; post-merge main edits are later history)` };
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
} catch { /* 可达性判定异常 → 落回原逐字节逻辑(fail-open) */ }
|
|
1521
|
-
|
|
1522
|
-
try {
|
|
1523
|
-
// 1) 候选交付变更(worktree 工作区相对 diffBase)。--no-renames:rename 退化成 D+A,两侧文件都进集
|
|
1524
|
-
const trackedRaw = gitQuiet(worktreePath, ['diff', '--no-renames', '--name-only', diffBase], { timeout: 30000 });
|
|
1525
|
-
const untrackedRaw = gitQuiet(worktreePath, ['ls-files', '--others', '--exclude-standard'], { timeout: 30000 });
|
|
1526
|
-
// git 检测失败(返回 null,如 worktree .git 半坏/目录部分删除)→ 不能当"无改动",保守保留。
|
|
1527
|
-
// 只有 git 成功返回空才判"真无改动"(可安全清理)。
|
|
1528
|
-
if (trackedRaw === null || untrackedRaw === null) {
|
|
1529
|
-
return { hasChanges: true, changedFiles: [], reason: `git detection failed (conservative keep): tracked=${trackedRaw === null ? 'null' : 'ok'} untracked=${untrackedRaw === null ? 'null' : 'ok'}` };
|
|
1530
|
-
}
|
|
1531
|
-
const tracked = trackedRaw.split('\n').filter(Boolean).filter(isDeliverable);
|
|
1532
|
-
const untracked = untrackedRaw.split('\n').filter(Boolean).filter(isDeliverable);
|
|
1533
|
-
|
|
1534
|
-
if (tracked.length === 0 && untracked.length === 0) {
|
|
1535
|
-
return { hasChanges: false, changedFiles: [], reason: 'no changes in worktree' };
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
// 2) 候选集中尚未落到主工作区 HEAD 的子集
|
|
1539
|
-
const pending = this._changesAlreadyOnMain(worktreePath, tracked, untracked);
|
|
1540
|
-
// 3) main 工作区副本降噪层(坑 unapplied-false-positive-workspace-copy,2026-08-21 实证:
|
|
1541
|
-
// apply 后 main 工作区有逐字节一致副本但未 commit → HEAD-only 判定报「未落仓」虚警拦
|
|
1542
|
-
// doctor)。原注释「不查 main 工作区未提交副本(防误删)」的保护意图是内容未落地——
|
|
1543
|
-
// 逐字节一致时内容确已落地(删 worktree 无损),仅逐字节不同的才保守保留。
|
|
1544
|
-
const pendingAfterWs = pending.filter(f => {
|
|
1545
|
-
try {
|
|
1546
|
-
const mainCopy = join(this.cwd, f);
|
|
1547
|
-
if (!existsSync(mainCopy)) return true; // main 工作区无副本 → 仍算未落仓
|
|
1548
|
-
const wtCopy = join(worktreePath, f);
|
|
1549
|
-
if (!existsSync(wtCopy)) return true; // worktree 侧是删除态、main 侧却在 → 内容分叉,保留
|
|
1550
|
-
const a = readFileSync(wtCopy); const b = readFileSync(mainCopy);
|
|
1551
|
-
return !(a.length === b.length && a.equals(b)); // 逐字节一致 → 已落地(剔除)
|
|
1552
|
-
} catch { return true }
|
|
1553
|
-
})
|
|
1554
|
-
if (pendingAfterWs.length === 0) {
|
|
1555
|
-
return { hasChanges: false, changedFiles: [], reason: 'all changes already on main (HEAD or identical workspace copy)' };
|
|
1556
|
-
}
|
|
1557
|
-
return { hasChanges: true, changedFiles: pendingAfterWs };
|
|
1558
|
-
} catch (e) {
|
|
1559
|
-
// 检测失败时保守处理:视为有变更,保留 worktree
|
|
1560
|
-
return { hasChanges: true, changedFiles: [], reason: `check failed: ${e.message}` };
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
/**
|
|
1565
|
-
* 判定候选变更里哪些还没到主工作区 HEAD。
|
|
1566
|
-
* - tracked:`git -C worktree diff --no-renames --name-only <mainHead> -- <files>`,
|
|
1567
|
-
* 比较 worktree 工作区(含未提交)vs main HEAD,限定到候选集;空 = 已在 main HEAD。
|
|
1568
|
-
* - untracked:worktree `hash-object`(默认带 filter,与 tree blob 同口径)vs main `ls-tree HEAD` blob;
|
|
1569
|
-
* 不等 = 该新文件未在 main HEAD。HEAD-only,不查 main 工作区未提交副本(防误删)。
|
|
1570
|
-
* 三种 mode 共享主 repo 对象库,mainHead 可在 worktree cwd 解析。
|
|
1571
|
-
* @private
|
|
1572
|
-
* @param {string} worktreePath
|
|
1573
|
-
* @param {string[]} trackedFiles
|
|
1574
|
-
* @param {string[]} untrackedFiles
|
|
1575
|
-
* @returns {string[]} 尚未落到 main HEAD 的文件
|
|
1576
|
-
*/
|
|
1577
|
-
_changesAlreadyOnMain(worktreePath, trackedFiles, untrackedFiles) {
|
|
1578
|
-
const mainHead = git(this.cwd, ['rev-parse', 'HEAD']); // 失败即抛 → 外层 catch fail-safe
|
|
1579
|
-
const pending = [];
|
|
1580
|
-
|
|
1581
|
-
if (trackedFiles.length > 0) {
|
|
1582
|
-
const diverged = (gitQuiet(worktreePath,
|
|
1583
|
-
['diff', '--no-renames', '--name-only', mainHead, '--', ...trackedFiles], { timeout: 30000 }) || '')
|
|
1584
|
-
.split('\n').filter(Boolean);
|
|
1585
|
-
pending.push(...diverged);
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1588
|
-
if (untrackedFiles.length > 0) {
|
|
1589
|
-
// hash-object 按 argv 顺序逐行输出 blob hash;某文件不存在则整命令失败 → gitQuiet 返回 null
|
|
1590
|
-
const wtHashes = (gitQuiet(worktreePath, ['hash-object', '--', ...untrackedFiles], { timeout: 30000 }) || '')
|
|
1591
|
-
.split('\n');
|
|
1592
|
-
const mainBlobs = this._lsTreeBlobs(this.cwd, 'HEAD', untrackedFiles);
|
|
1593
|
-
for (let i = 0; i < untrackedFiles.length; i++) {
|
|
1594
|
-
// wtHashes[i] 缺失(命令失败/行数不齐)→ 视为未应用,保守保留
|
|
1595
|
-
if (wtHashes[i] !== (mainBlobs.get(untrackedFiles[i]) ?? null)) {
|
|
1596
|
-
pending.push(untrackedFiles[i]);
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
return [...new Set(pending)];
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
/**
|
|
1605
|
-
* 一次 `git ls-tree <treeish> -- <files>` → Map<path, blobHash>(仅 tree 中存在者)。
|
|
1606
|
-
* 与 worktree-apply.js 的 getBlobHashMap 同逻辑;此处内联私有,避免反向 import 循环依赖。
|
|
1607
|
-
* @private
|
|
1608
|
-
* @param {string} cwd
|
|
1609
|
-
* @param {string} treeish
|
|
1610
|
-
* @param {string[]} files
|
|
1611
|
-
* @returns {Map<string, string>}
|
|
1612
|
-
*/
|
|
1613
|
-
_lsTreeBlobs(cwd, treeish, files) {
|
|
1614
|
-
const map = new Map();
|
|
1615
|
-
if (!files || files.length === 0) return map; // 空 pathspec 会列整棵树,必须拦
|
|
1616
|
-
const raw = gitQuiet(cwd, ['ls-tree', treeish, '--', ...files]);
|
|
1617
|
-
if (!raw) return map;
|
|
1618
|
-
for (const line of raw.split('\n')) {
|
|
1619
|
-
if (!line) continue;
|
|
1620
|
-
const tab = line.indexOf('\t');
|
|
1621
|
-
if (tab === -1) continue;
|
|
1622
|
-
const hash = line.slice(0, tab).split(' ')[2]; // "<mode> <type> <hash>"
|
|
1623
|
-
if (hash) map.set(line.slice(tab + 1), hash);
|
|
1624
|
-
}
|
|
1625
|
-
return map;
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1628
|
-
/**
|
|
1629
|
-
* DB 归档态探测(坑 doctor-reprovision-archived-change):changes 表 status='archived'。
|
|
1630
|
-
* 只读直查(node:sqlite)——doctor 是同步方法,惰性动态 import 不可用;DB 不存在/无行/
|
|
1631
|
-
* 读失败 → false(保守退回普通 deps 供给路径)。
|
|
1632
|
-
* @param {string} name
|
|
1633
|
-
* @returns {boolean}
|
|
1634
|
-
*/
|
|
1635
|
-
_isChangeArchivedInDb(name) {
|
|
1636
|
-
try {
|
|
1637
|
-
const dbPath = join(this.cwd, '.sillyspec', '.runtime', 'sillyspec.db');
|
|
1638
|
-
if (!existsSync(dbPath)) return false;
|
|
1639
|
-
const { openDatabase } = require('./db-engine.js');
|
|
1640
|
-
const db = openDatabase(dbPath, { readOnly: true });
|
|
1641
|
-
try {
|
|
1642
|
-
const row = db.prepare('SELECT status FROM changes WHERE name = ?').get(name);
|
|
1643
|
-
return row?.status === 'archived';
|
|
1644
|
-
} finally { try { db.close() } catch {} }
|
|
1645
|
-
} catch { return false }
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
/**
|
|
1649
|
-
* 从 worktree 路径反推 changeName
|
|
1650
|
-
* @private
|
|
1651
|
-
*/
|
|
1652
|
-
_pathToChangeName(wtPath) {
|
|
1653
|
-
try {
|
|
1654
|
-
const resolved = resolve(wtPath);
|
|
1655
|
-
const baseResolved = resolve(this.worktreeBase);
|
|
1656
|
-
// 用 path.relative 比 startsWith(base + sep) 更稳:
|
|
1657
|
-
// - 自动用当前平台分隔符比较(Windows 上 resolve 返回反斜杠,硬编码 '/' 恒 false)
|
|
1658
|
-
// - 跨盘符(C: vs D:)时返回绝对路径 → isAbsolute 守卫放行失败兜底 null
|
|
1659
|
-
// - 同路径返回 '' → 视作非 changeName
|
|
1660
|
-
const rel = relative(baseResolved, resolved);
|
|
1661
|
-
if (rel && !rel.startsWith('..') && !isAbsolute(rel)) {
|
|
1662
|
-
return rel;
|
|
1663
|
-
}
|
|
1664
|
-
} catch {}
|
|
1665
|
-
return null;
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
/**
|
|
1669
|
-
* 将主工作区未提交变更同步到 worktree(dirty baseline overlay)
|
|
1670
|
-
* 覆盖 staged + unstaged 的文件变更,以及 untracked 文件。
|
|
1671
|
-
* 使用 git diff + git apply 确保正确处理删除/rename/binary。
|
|
1672
|
-
*
|
|
1673
|
-
* .sillyspec/ 隔离(坑 baseline-overlay-cross-change-contamination,2026-08-21 实证):
|
|
1674
|
-
* overlay 原样全量吸收主仓未提交改动——多变更并行时其他变更的 spec 文档
|
|
1675
|
-
* (changes/<他变更>/、ROADMAP.md、quicklog 等 39 个文件实测)被 checkpoint 进本变更
|
|
1676
|
-
* baseline,apply 回 main 即随本变更交付,需人工隔离。spec 文档不参与 worktree 内
|
|
1677
|
-
* 构建/测试(execute 的 spec 读写经 specDriftAnchor 锚回主仓),排除零功能损失;
|
|
1678
|
-
* 排除清单显式打印保持可见(哪些跨变更文件被隔离、去哪了)。
|
|
1679
|
-
* @param {string} mainCwd - 主工作区路径
|
|
1680
|
-
* @param {string} worktreePath - worktree 路径
|
|
1681
|
-
* @returns {Array<string>} overlay 的文件列表
|
|
1682
|
-
*/
|
|
1683
|
-
_overlayBaseline(mainCwd, worktreePath) {
|
|
1684
|
-
const files = [];
|
|
1685
|
-
const errors = [];
|
|
1686
|
-
// git pathspec 排除(execFileSync 数组传参不经 shell,字面安全):.sillyspec/ 整目录
|
|
1687
|
-
const EXCLUDE_PATHSPEC = ':(exclude).sillyspec';
|
|
1688
|
-
const collectExcluded = (raw, excluded) => {
|
|
1689
|
-
for (const f of raw.split('\n').filter(Boolean)) {
|
|
1690
|
-
const norm = f.replace(/\\/g, '/')
|
|
1691
|
-
if (norm === '.sillyspec' || norm.startsWith('.sillyspec/')) excluded.push(f)
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
try {
|
|
1696
|
-
const excludedSpecFiles = [];
|
|
1697
|
-
// staged 变更(pathspec 排除 .sillyspec/——跨变更 spec 文档不进 baseline)
|
|
1698
|
-
const staged = gitQuiet(mainCwd, ['diff', '--cached', '--name-only', '--', '.', EXCLUDE_PATHSPEC], { timeout: 30000 }) || ''
|
|
1699
|
-
const stagedAll = gitQuiet(mainCwd, ['diff', '--cached', '--name-only'], { timeout: 30000 }) || ''
|
|
1700
|
-
collectExcluded(stagedAll, excludedSpecFiles)
|
|
1701
|
-
if (staged) {
|
|
1702
|
-
try {
|
|
1703
|
-
// 用 Buffer 模式读取,避免二进制 patch 被 UTF-8 解码损坏
|
|
1704
|
-
// QUAL-01 收口:git-helper 新增 encoding:'buffer' 支持(二进制输出专用,跳过 trim)
|
|
1705
|
-
const patchBuf = git(mainCwd, ['diff', '--cached', '--binary', '--', '.', EXCLUDE_PATHSPEC], { encoding: 'buffer', timeout: 30000 });
|
|
1706
|
-
if (patchBuf && patchBuf.length > 0) {
|
|
1707
|
-
const patchFile = join(worktreePath, '.sillyspec-baseline-staged.patch');
|
|
1708
|
-
try {
|
|
1709
|
-
writeFileSync(patchFile, patchBuf);
|
|
1710
|
-
git(worktreePath, ['apply', '--binary', patchFile], { timeout: 30000 });
|
|
1711
|
-
} finally {
|
|
1712
|
-
// git apply 抛错时也要清掉 patch 临时文件,避免泄漏到 worktree/主仓根
|
|
1713
|
-
// (filterDeliverableFiles 不排除 .sillyspec-baseline-*,泄漏会级联 BLOCKED 下游 apply)
|
|
1714
|
-
try { rmSync(patchFile, { force: true }); } catch {}
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
} catch (e) {
|
|
1718
|
-
errors.push(`staged: ${e.message}`);
|
|
1719
|
-
}
|
|
1720
|
-
files.push(...staged.split('\n').filter(Boolean));
|
|
1721
|
-
}
|
|
1722
|
-
|
|
1723
|
-
// unstaged 变更(同上 pathspec 排除)
|
|
1724
|
-
const unstaged = gitQuiet(mainCwd, ['diff', '--name-only', '--', '.', EXCLUDE_PATHSPEC], { timeout: 30000 }) || ''
|
|
1725
|
-
const unstagedAll = gitQuiet(mainCwd, ['diff', '--name-only'], { timeout: 30000 }) || ''
|
|
1726
|
-
collectExcluded(unstagedAll, excludedSpecFiles)
|
|
1727
|
-
if (unstaged) {
|
|
1728
|
-
try {
|
|
1729
|
-
// 用 Buffer 模式读取,避免二进制 patch 被 UTF-8 解码损坏(QUAL-01 收口,同上)
|
|
1730
|
-
const patchBuf = git(mainCwd, ['diff', '--binary', '--', '.', EXCLUDE_PATHSPEC], { encoding: 'buffer', timeout: 30000 });
|
|
1731
|
-
if (patchBuf && patchBuf.length > 0) {
|
|
1732
|
-
const patchFile = join(worktreePath, '.sillyspec-baseline-unstaged.patch');
|
|
1733
|
-
try {
|
|
1734
|
-
writeFileSync(patchFile, patchBuf);
|
|
1735
|
-
git(worktreePath, ['apply', '--binary', patchFile], { timeout: 30000 });
|
|
1736
|
-
} finally {
|
|
1737
|
-
// git apply 抛错时也要清掉 patch 临时文件,避免泄漏到 worktree/主仓根
|
|
1738
|
-
try { rmSync(patchFile, { force: true }); } catch {}
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
} catch (e) {
|
|
1742
|
-
errors.push(`unstaged: ${e.message}`);
|
|
1743
|
-
}
|
|
1744
|
-
files.push(...unstaged.split('\n').filter(Boolean));
|
|
1745
|
-
}
|
|
1746
|
-
|
|
1747
|
-
// untracked 文件(排除 .sillyspec/.runtime 等);目录跳过避免 readFileSync EISDIR
|
|
1748
|
-
const untracked = gitQuiet(mainCwd, ['ls-files', '--others', '--exclude-standard'], { timeout: 30000 }) || '';
|
|
1749
|
-
const skippedDirs = [];
|
|
1750
|
-
if (untracked) {
|
|
1751
|
-
for (const f of untracked.split('\n').filter(Boolean)) {
|
|
1752
|
-
const norm = f.replace(/\\/g, '/')
|
|
1753
|
-
if (norm === '.sillyspec' || norm.startsWith('.sillyspec/')) { excludedSpecFiles.push(f); continue }
|
|
1754
|
-
const r = copyUntrackedEntry(join(mainCwd, f), join(worktreePath, f));
|
|
1755
|
-
if (r.status === 'copied') files.push(f);
|
|
1756
|
-
else if (r.status === 'skipped-dir') skippedDirs.push(f);
|
|
1757
|
-
else if (r.status === 'error') errors.push(`untracked ${f}: ${r.error}`);
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
if (skippedDirs.length > 0) {
|
|
1761
|
-
console.log(`ℹ️ baseline overlay 跳过 ${skippedDirs.length} 个 untracked 目录(不读目录避免 EISDIR,坑 execute-worktree-overlay-untracked-dir-eisdir)`);
|
|
1762
|
-
}
|
|
1763
|
-
// 跨变更 spec 文档隔离可见性:列出被排除的 .sillyspec/ 文件(去重,截断展示),
|
|
1764
|
-
// 让「ROADMAP 被谁改动、为何不在本 baseline」在 create 时刻可查,不需事后考古
|
|
1765
|
-
const excludedUnique = [...new Set(excludedSpecFiles)]
|
|
1766
|
-
if (excludedUnique.length > 0) {
|
|
1767
|
-
const preview = excludedUnique.slice(0, 8).join(', ') + (excludedUnique.length > 8 ? ` …(共 ${excludedUnique.length} 个)` : '')
|
|
1768
|
-
console.log(`🧹 baseline overlay 已隔离 ${excludedUnique.length} 个 .sillyspec/ 未提交文件(跨变更 spec 文档不进本变更 baseline,留在主仓各自归属):${preview}`)
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
if (files.length > 0) {
|
|
1772
|
-
console.log(`📁 baseline overlay: ${files.length} 个未提交文件已同步到 worktree`);
|
|
1773
|
-
// 非阻断 advisory(治「已做完没提交」被裹进 execute 交付):这批未提交改动会单独 commit
|
|
1774
|
-
// 成 baseline checkpoint(git 历史与本阶段新改动分层,可分开验收),但 apply 回 main 时会
|
|
1775
|
-
// 随本阶段交付落地。若它们是独立变更、需要独立验收,先提交到独立分支另行处理——
|
|
1776
|
-
// 注意不能在 main 直接 commit(那等于无验收并入主干、污染 main,正是坑1 形态)。
|
|
1777
|
-
// sillyspec 只做确定性提示(改动是事实),该不该拆分属意图判定,留给 agent/用户。
|
|
1778
|
-
console.log(` ℹ️ 它们将作为独立 baseline checkpoint 提交到 worktree 分支(与本阶段新改动分层、可分开验收),`);
|
|
1779
|
-
console.log(` 但 apply 回 main 会随本阶段交付。若是独立变更、需独立验收:先提交到独立分支另行处理`);
|
|
1780
|
-
console.log(` (不要在 main 直接 commit——那等于无验收并入主干、污染 main),再回到本变更跑 execute。`);
|
|
1781
|
-
}
|
|
1782
|
-
} catch (e) {
|
|
1783
|
-
errors.push(`unexpected: ${e.message}`);
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
// 有 pending 文件但 overlay 部分失败 → fail-fast
|
|
1787
|
-
if (errors.length > 0) {
|
|
1788
|
-
throw new Error(`baseline overlay 失败 (${errors.length} 个错误): ${errors.join('; ')}`);
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
const uniqueFiles = [...new Set(files)];
|
|
1792
|
-
|
|
1793
|
-
// 计算 baseline hash(用于 merge 前校验主工作区是否变化)
|
|
1794
|
-
const baselineHash = uniqueFiles.length > 0 ? computeBaselineHash(mainCwd) : null;
|
|
1795
|
-
|
|
1796
|
-
return { files: uniqueFiles, baselineHash };
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
/**
|
|
1800
|
-
* 在 worktree 内创建 baseline checkpoint commit
|
|
1801
|
-
* 用于区分 "前置 dirty baseline" 和 "子代理新增改动"
|
|
1802
|
-
* @param {string} worktreePath
|
|
1803
|
-
* @param {string} changeName
|
|
1804
|
-
* @param {string[]} [baselineFiles] 主仓并行在途文件清单(_overlayBaseline 返回的 files,
|
|
1805
|
-
* 已排除 .sillyspec)——写入提交信息正文(坑 baseline-checkpoint-opaque-carriage,
|
|
1806
|
-
* 2026-08-24 用户反馈三期③:checkpoint 把主仓并行脏文件带进分支 diff,逐任务归因要靠
|
|
1807
|
-
* 人肉区分;清单入 message 后 `git log` 一眼可辨、可机械排除)
|
|
1808
|
-
* @returns {string} commit hash
|
|
1809
|
-
*/
|
|
1810
|
-
_createBaselineCheckpoint(worktreePath, changeName, baselineFiles = []) {
|
|
1811
|
-
// 使用临时 git identity,避免用户未配置 user.name/user.email 导致失败
|
|
1812
|
-
const env = {
|
|
1813
|
-
GIT_AUTHOR_NAME: 'sillyspec',
|
|
1814
|
-
GIT_AUTHOR_EMAIL: 'sillyspec@baseline',
|
|
1815
|
-
GIT_COMMITTER_NAME: 'sillyspec',
|
|
1816
|
-
GIT_COMMITTER_EMAIL: 'sillyspec@baseline',
|
|
1817
|
-
};
|
|
1818
|
-
try {
|
|
1819
|
-
git(worktreePath, ['add', '-A'], { timeout: 30000 });
|
|
1820
|
-
// 检查是否有实际变更(可能 overlay 后和 HEAD 完全一致)
|
|
1821
|
-
const status = gitQuiet(worktreePath, ['status', '--porcelain'], { timeout: 30000 });
|
|
1822
|
-
if (!status) {
|
|
1823
|
-
return gitQuiet(worktreePath, ['rev-parse', 'HEAD']);
|
|
1824
|
-
}
|
|
1825
|
-
// 提交信息正文列夹带文件(封顶 30 行,超出记总数)——清单与 meta.baselineFiles 同源
|
|
1826
|
-
const CAP = 30
|
|
1827
|
-
const files = Array.isArray(baselineFiles) ? baselineFiles.filter(Boolean) : []
|
|
1828
|
-
const body = files.length > 0
|
|
1829
|
-
? ['\n主仓并行在途文件(非本变更改动,逐任务归因时排除):',
|
|
1830
|
-
...files.slice(0, CAP).map(f => `- ${f}`),
|
|
1831
|
-
...(files.length > CAP ? [`…等共 ${files.length} 个`] : [])].join('\n')
|
|
1832
|
-
: ''
|
|
1833
|
-
// --no-verify:baseline 是锚点不是交付物,只是把主仓库 dirty 文件快照到 worktree
|
|
1834
|
-
// 分支上以便区分「前置 baseline」与「子代理新增改动」。它不该触发项目 pre-commit
|
|
1835
|
-
// hook(如 ruff format),否则主仓库 dirty 文件中任一不达标的会被 hook reformat
|
|
1836
|
-
// 致 commit 失败 → worktree 创建失败 → execute 无法启动。
|
|
1837
|
-
execFileSync(
|
|
1838
|
-
'git',
|
|
1839
|
-
['commit', '--no-verify', '-m', `sillyspec: baseline checkpoint for ${changeName}${body}`],
|
|
1840
|
-
{ cwd: worktreePath, encoding: 'utf8', stdio: ['pipe','pipe','pipe'], env }
|
|
1841
|
-
);
|
|
1842
|
-
const hash = git(worktreePath, ['rev-parse', 'HEAD']);
|
|
1843
|
-
console.log(`📌 baseline checkpoint: ${hash}`);
|
|
1844
|
-
return hash;
|
|
1845
|
-
} catch (e) {
|
|
1846
|
-
throw new Error(`baseline checkpoint 创建失败: ${e.message}`);
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
}
|