@bhargavvc/sdd-cc 1.30.1 → 1.42.3
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/README.ja-JP.md +165 -129
- package/README.ko-KR.md +161 -123
- package/README.md +103 -679
- package/README.pt-BR.md +92 -52
- package/README.zh-CN.md +145 -103
- package/agents/sdd-advisor-researcher.md +23 -0
- package/agents/sdd-ai-researcher.md +133 -0
- package/agents/sdd-code-fixer.md +668 -0
- package/agents/sdd-code-reviewer.md +387 -0
- package/agents/sdd-codebase-mapper.md +86 -3
- package/agents/sdd-debug-session-manager.md +314 -0
- package/agents/sdd-debugger.md +157 -78
- package/agents/sdd-doc-classifier.md +168 -0
- package/agents/sdd-doc-synthesizer.md +204 -0
- package/agents/sdd-doc-verifier.md +217 -0
- package/agents/sdd-doc-writer.md +615 -0
- package/agents/sdd-domain-researcher.md +153 -0
- package/agents/sdd-eval-auditor.md +191 -0
- package/agents/sdd-eval-planner.md +154 -0
- package/agents/sdd-executor.md +283 -40
- package/agents/sdd-framework-selector.md +160 -0
- package/agents/sdd-integration-checker.md +30 -3
- package/agents/sdd-intel-updater.md +342 -0
- package/agents/sdd-nyquist-auditor.md +31 -4
- package/agents/sdd-pattern-mapper.md +335 -0
- package/agents/sdd-phase-researcher.md +254 -24
- package/agents/sdd-plan-checker.md +223 -18
- package/agents/sdd-planner.md +286 -362
- package/agents/sdd-project-researcher.md +28 -5
- package/agents/sdd-research-synthesizer.md +4 -4
- package/agents/sdd-roadmapper.md +14 -5
- package/agents/sdd-security-auditor.md +155 -0
- package/agents/sdd-ui-auditor.md +60 -4
- package/agents/sdd-ui-checker.md +11 -2
- package/agents/sdd-ui-researcher.md +27 -4
- package/agents/sdd-user-profiler.md +2 -2
- package/agents/sdd-verifier.md +258 -41
- package/bin/install.js +6862 -618
- package/bin/sdd-sdk.js +37 -0
- package/commands/sdd/add-tests.md +3 -2
- package/commands/sdd/ai-integration-phase.md +37 -0
- package/commands/sdd/audit-fix.md +34 -0
- package/commands/sdd/audit-milestone.md +3 -2
- package/commands/sdd/autonomous.md +10 -5
- package/commands/sdd/capture.md +62 -0
- package/commands/sdd/cleanup.md +7 -1
- package/commands/sdd/code-review.md +59 -0
- package/commands/sdd/complete-milestone.md +11 -4
- package/commands/sdd/config.md +58 -0
- package/commands/sdd/debug.md +23 -144
- package/commands/sdd/discuss-phase.md +22 -10
- package/commands/sdd/docs-update.md +49 -0
- package/commands/sdd/eval-review.md +33 -0
- package/commands/sdd/execute-phase.md +9 -4
- package/commands/sdd/explore.md +27 -0
- package/commands/sdd/extract-learnings.md +23 -0
- package/commands/sdd/fast.md +2 -1
- package/commands/sdd/forensics.md +3 -2
- package/commands/sdd/graphify.md +199 -0
- package/commands/sdd/health.md +12 -3
- package/commands/sdd/help.md +3 -1
- package/commands/sdd/import.md +41 -0
- package/commands/sdd/inbox.md +39 -0
- package/commands/sdd/ingest-docs.md +42 -0
- package/commands/sdd/manager.md +9 -3
- package/commands/sdd/map-codebase.md +15 -3
- package/commands/sdd/milestone-summary.md +1 -1
- package/commands/sdd/mvp-phase.md +45 -0
- package/commands/sdd/new-milestone.md +3 -2
- package/commands/sdd/new-project.md +7 -2
- package/commands/sdd/ns-context.md +23 -0
- package/commands/sdd/ns-ideate.md +24 -0
- package/commands/sdd/ns-manage.md +29 -0
- package/commands/sdd/ns-project.md +22 -0
- package/commands/sdd/ns-review.md +26 -0
- package/commands/sdd/ns-workflow.md +28 -0
- package/commands/sdd/pause-work.md +6 -1
- package/commands/sdd/phase.md +56 -0
- package/commands/sdd/plan-phase.md +19 -4
- package/commands/sdd/plan-review-convergence.md +59 -0
- package/commands/sdd/pr-branch.md +2 -1
- package/commands/sdd/profile-user.md +2 -2
- package/commands/sdd/progress.md +27 -5
- package/commands/sdd/quick.md +132 -5
- package/commands/sdd/resume-work.md +2 -12
- package/commands/sdd/review-backlog.md +4 -2
- package/commands/sdd/review.md +7 -3
- package/commands/sdd/secure-phase.md +36 -0
- package/commands/sdd/settings.md +2 -9
- package/commands/sdd/ship.md +1 -0
- package/commands/sdd/sketch.md +60 -0
- package/commands/sdd/spec-phase.md +63 -0
- package/commands/sdd/spike.md +57 -0
- package/commands/sdd/stats.md +2 -1
- package/commands/sdd/surface.md +129 -0
- package/commands/sdd/thread.md +8 -111
- package/commands/sdd/ui-phase.md +3 -2
- package/commands/sdd/ui-review.md +3 -2
- package/commands/sdd/ultraplan-phase.md +34 -0
- package/commands/sdd/undo.md +35 -0
- package/commands/sdd/update.md +21 -10
- package/commands/sdd/validate-phase.md +3 -2
- package/commands/sdd/verify-work.md +4 -3
- package/commands/sdd/workspace.md +52 -0
- package/commands/sdd/workstreams.md +15 -8
- package/hooks/dist/sdd-check-update-worker.js +116 -0
- package/hooks/dist/sdd-check-update.js +19 -69
- package/hooks/dist/sdd-context-monitor.js +43 -7
- package/hooks/dist/sdd-phase-boundary.sh +47 -0
- package/hooks/dist/sdd-prompt-guard.js +1 -0
- package/hooks/dist/sdd-read-guard.js +101 -0
- package/hooks/dist/sdd-read-injection-scanner.js +152 -0
- package/hooks/dist/sdd-session-state.sh +59 -0
- package/hooks/dist/sdd-statusline.js +439 -21
- package/hooks/dist/sdd-update-banner.js +134 -0
- package/hooks/dist/sdd-validate-commit.sh +57 -0
- package/hooks/dist/sdd-workflow-guard.js +2 -2
- package/hooks/lib/git-cmd.js +150 -0
- package/hooks/sdd-check-update-worker.js +116 -0
- package/hooks/sdd-check-update.js +64 -0
- package/hooks/sdd-context-monitor.js +192 -0
- package/hooks/sdd-phase-boundary.sh +47 -0
- package/hooks/sdd-prompt-guard.js +97 -0
- package/hooks/sdd-read-guard.js +101 -0
- package/hooks/sdd-read-injection-scanner.js +152 -0
- package/hooks/sdd-session-state.sh +59 -0
- package/hooks/sdd-statusline.js +537 -0
- package/hooks/sdd-update-banner.js +134 -0
- package/hooks/sdd-validate-commit.sh +57 -0
- package/hooks/sdd-workflow-guard.js +94 -0
- package/package.json +34 -9
- package/scripts/audit-workflow-script-paths.cjs +73 -0
- package/scripts/build-hooks.js +114 -9
- package/scripts/changeset/cli.cjs +269 -0
- package/scripts/changeset/github-release-notes.cjs +198 -0
- package/scripts/changeset/lint.cjs +110 -0
- package/scripts/changeset/new.cjs +137 -0
- package/scripts/changeset/parse.cjs +60 -0
- package/scripts/changeset/render.cjs +34 -0
- package/scripts/changeset/serialize.cjs +74 -0
- package/scripts/command-contract-helpers.cjs +61 -0
- package/scripts/diff-touches-shipped-paths.cjs +147 -0
- package/scripts/fix-slash-commands.cjs +106 -0
- package/scripts/gen-inventory-manifest.cjs +109 -0
- package/scripts/lint-command-contract.cjs +108 -0
- package/scripts/lint-descriptions.cjs +83 -0
- package/scripts/lint-no-source-grep-extras.cjs +81 -0
- package/scripts/lint-no-source-grep.cjs +174 -0
- package/scripts/lint-shell-command-projection-drift.cjs +57 -0
- package/scripts/lint-skill-deps.cjs +180 -0
- package/scripts/pr-template-policy.cjs +169 -0
- package/scripts/prompt-injection-scan.sh +3 -0
- package/scripts/rebrand-gsd-to-sdd.sh +222 -220
- package/scripts/run-tests.cjs +5 -1
- package/scripts/strip-prose-atrefs.cjs +106 -0
- package/scripts/verify-tarball-sdk-dist.sh +69 -0
- package/sdd/bin/check-latest-version.cjs +104 -0
- package/sdd/bin/lib/active-workstream-store.cjs +85 -0
- package/sdd/bin/lib/adr-parser.cjs +394 -0
- package/sdd/bin/lib/artifacts.cjs +53 -0
- package/sdd/bin/lib/audit.cjs +755 -0
- package/sdd/bin/lib/cjs-command-router-adapter.cjs +39 -0
- package/sdd/bin/lib/clusters.cjs +135 -0
- package/sdd/bin/lib/command-aliases.generated.cjs +838 -0
- package/sdd/bin/lib/commands.cjs +179 -107
- package/sdd/bin/lib/config-schema.cjs +135 -0
- package/sdd/bin/lib/config.cjs +313 -86
- package/sdd/bin/lib/context-utilization.cjs +47 -0
- package/sdd/bin/lib/core.cjs +1146 -391
- package/sdd/bin/lib/decisions.cjs +48 -0
- package/sdd/bin/lib/docs.cjs +270 -0
- package/sdd/bin/lib/drift.cjs +379 -0
- package/sdd/bin/lib/fallow-runner.cjs +109 -0
- package/sdd/bin/lib/frontmatter.cjs +389 -336
- package/sdd/bin/lib/gap-checker.cjs +197 -0
- package/sdd/bin/lib/graphify.cjs +577 -0
- package/sdd/bin/lib/init-command-router.cjs +70 -0
- package/sdd/bin/lib/init.cjs +692 -97
- package/sdd/bin/lib/install-profiles.cjs +572 -0
- package/sdd/bin/lib/installer-migration-authoring.cjs +117 -0
- package/sdd/bin/lib/installer-migration-report.cjs +328 -0
- package/sdd/bin/lib/installer-migrations/000-first-time-baseline.cjs +220 -0
- package/sdd/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +41 -0
- package/sdd/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +80 -0
- package/sdd/bin/lib/installer-migrations.cjs +703 -0
- package/sdd/bin/lib/intel.cjs +643 -0
- package/sdd/bin/lib/learnings.cjs +379 -0
- package/sdd/bin/lib/milestone.cjs +313 -252
- package/sdd/bin/lib/model-catalog.cjs +136 -0
- package/sdd/bin/lib/model-profiles.cjs +25 -68
- package/sdd/bin/lib/phase-command-router.cjs +96 -0
- package/sdd/bin/lib/phase.cjs +868 -335
- package/sdd/bin/lib/phases-command-router.cjs +39 -0
- package/sdd/bin/lib/plan-scan.cjs +138 -0
- package/sdd/bin/lib/planning-workspace.cjs +361 -0
- package/sdd/bin/lib/profile-output.cjs +197 -35
- package/sdd/bin/lib/profile-pipeline.cjs +1 -1
- package/sdd/bin/lib/review-reviewer-selection.cjs +125 -0
- package/sdd/bin/lib/roadmap-command-router.cjs +23 -0
- package/sdd/bin/lib/roadmap.cjs +416 -124
- package/sdd/bin/lib/runtime-homes.cjs +178 -0
- package/sdd/bin/lib/schema-detect.cjs +238 -0
- package/sdd/bin/lib/sdd2-import.cjs +511 -0
- package/sdd/bin/lib/secrets.cjs +33 -0
- package/sdd/bin/lib/security.cjs +131 -9
- package/sdd/bin/lib/shell-command-projection.cjs +548 -0
- package/sdd/bin/lib/state-command-router.cjs +100 -0
- package/sdd/bin/lib/state-document.cjs +12 -0
- package/sdd/bin/lib/state-document.generated.cjs +127 -0
- package/sdd/bin/lib/state.cjs +1253 -367
- package/sdd/bin/lib/surface.cjs +398 -0
- package/sdd/bin/lib/template.cjs +11 -5
- package/sdd/bin/lib/uat.cjs +9 -2
- package/sdd/bin/lib/validate-command-router.cjs +55 -0
- package/sdd/bin/lib/verify-command-router.cjs +34 -0
- package/sdd/bin/lib/verify.cjs +648 -140
- package/sdd/bin/lib/workstream-inventory.cjs +159 -0
- package/sdd/bin/lib/workstream-name-policy.cjs +33 -0
- package/sdd/bin/lib/workstream.cjs +78 -196
- package/sdd/bin/lib/worktree-safety.cjs +563 -0
- package/sdd/bin/sdd-tools.cjs +528 -222
- package/sdd/bin/verify-reapply-patches.cjs +247 -0
- package/sdd/contexts/dev.md +21 -0
- package/sdd/contexts/research.md +22 -0
- package/sdd/contexts/review.md +23 -0
- package/sdd/references/agent-contracts.md +79 -0
- package/sdd/references/ai-evals.md +156 -0
- package/sdd/references/ai-frameworks.md +186 -0
- package/sdd/references/artifact-types.md +131 -0
- package/sdd/references/autonomous-smart-discuss.md +277 -0
- package/sdd/references/checkpoints.md +36 -0
- package/sdd/references/common-bug-patterns.md +114 -0
- package/sdd/references/context-budget.md +85 -0
- package/sdd/references/continuation-format.md +30 -26
- package/sdd/references/debugger-philosophy.md +76 -0
- package/sdd/references/decimal-phase-calculation.md +5 -5
- package/sdd/references/doc-conflict-engine.md +91 -0
- package/sdd/references/domain-probes.md +125 -0
- package/sdd/references/execute-mvp-tdd.md +81 -0
- package/sdd/references/executor-examples.md +110 -0
- package/sdd/references/few-shot-examples/plan-checker.md +73 -0
- package/sdd/references/few-shot-examples/verifier.md +109 -0
- package/sdd/references/gate-prompts.md +100 -0
- package/sdd/references/gates.md +70 -0
- package/sdd/references/git-integration.md +9 -6
- package/sdd/references/git-planning-commit.md +6 -4
- package/sdd/references/ios-scaffold.md +123 -0
- package/sdd/references/mandatory-initial-read.md +2 -0
- package/sdd/references/model-profile-resolution.md +2 -0
- package/sdd/references/model-profiles.md +128 -22
- package/sdd/references/mvp-concepts.md +49 -0
- package/sdd/references/phase-argument-parsing.md +3 -3
- package/sdd/references/planner-antipatterns.md +89 -0
- package/sdd/references/planner-chunked.md +49 -0
- package/sdd/references/planner-gap-closure.md +62 -0
- package/sdd/references/planner-human-verify-mode.md +57 -0
- package/sdd/references/planner-mvp-mode.md +53 -0
- package/sdd/references/planner-reviews.md +39 -0
- package/sdd/references/planner-revision.md +87 -0
- package/sdd/references/planner-source-audit.md +73 -0
- package/sdd/references/planning-config.md +276 -7
- package/sdd/references/project-skills-discovery.md +19 -0
- package/sdd/references/revision-loop.md +97 -0
- package/sdd/references/scout-codebase.md +51 -0
- package/sdd/references/skeleton-template.md +48 -0
- package/sdd/references/sketch-interactivity.md +41 -0
- package/sdd/references/sketch-theme-system.md +94 -0
- package/sdd/references/sketch-tooling.md +45 -0
- package/sdd/references/sketch-variant-patterns.md +81 -0
- package/sdd/references/spidr-splitting.md +69 -0
- package/sdd/references/tdd.md +67 -0
- package/sdd/references/thinking-models-debug.md +44 -0
- package/sdd/references/thinking-models-execution.md +50 -0
- package/sdd/references/thinking-models-planning.md +62 -0
- package/sdd/references/thinking-models-research.md +50 -0
- package/sdd/references/thinking-models-verification.md +55 -0
- package/sdd/references/thinking-partner.md +96 -0
- package/sdd/references/ui-brand.md +4 -4
- package/sdd/references/universal-anti-patterns.md +63 -0
- package/sdd/references/user-story-template.md +58 -0
- package/sdd/references/verification-overrides.md +227 -0
- package/sdd/references/verify-mvp-mode.md +85 -0
- package/sdd/references/workstream-flag.md +63 -10
- package/sdd/references/worktree-path-safety.md +89 -0
- package/sdd/templates/AI-SPEC.md +246 -0
- package/sdd/templates/DEBUG.md +7 -2
- package/sdd/templates/README.md +77 -0
- package/sdd/templates/SECURITY.md +61 -0
- package/sdd/templates/VALIDATION.md +3 -3
- package/sdd/templates/claude-md.md +27 -4
- package/sdd/templates/config.json +20 -2
- package/sdd/templates/discovery.md +2 -2
- package/sdd/templates/research.md +40 -0
- package/sdd/templates/spec.md +307 -0
- package/sdd/templates/state.md +10 -2
- package/sdd/workflows/add-backlog.md +90 -0
- package/sdd/workflows/add-phase.md +12 -12
- package/sdd/workflows/add-tests.md +6 -3
- package/sdd/workflows/add-todo.md +8 -6
- package/sdd/workflows/ai-integration-phase.md +294 -0
- package/sdd/workflows/analyze-dependencies.md +96 -0
- package/sdd/workflows/audit-fix.md +177 -0
- package/sdd/workflows/audit-milestone.md +35 -18
- package/sdd/workflows/audit-uat.md +1 -1
- package/sdd/workflows/autonomous.md +202 -304
- package/sdd/workflows/check-todos.md +12 -10
- package/sdd/workflows/cleanup.md +3 -1
- package/sdd/workflows/code-review-fix.md +501 -0
- package/sdd/workflows/code-review.md +613 -0
- package/sdd/workflows/complete-milestone.md +115 -28
- package/sdd/workflows/debug.md +231 -0
- package/sdd/workflows/diagnose-issues.md +14 -5
- package/sdd/workflows/discovery-phase.md +3 -1
- package/sdd/workflows/discuss-phase/modes/advisor.md +175 -0
- package/sdd/workflows/discuss-phase/modes/all.md +28 -0
- package/sdd/workflows/discuss-phase/modes/analyze.md +44 -0
- package/sdd/workflows/discuss-phase/modes/auto.md +56 -0
- package/sdd/workflows/discuss-phase/modes/batch.md +52 -0
- package/sdd/workflows/discuss-phase/modes/chain.md +97 -0
- package/sdd/workflows/discuss-phase/modes/default.md +141 -0
- package/sdd/workflows/discuss-phase/modes/power.md +44 -0
- package/sdd/workflows/discuss-phase/modes/text.md +55 -0
- package/sdd/workflows/discuss-phase/templates/checkpoint.json +18 -0
- package/sdd/workflows/discuss-phase/templates/context.md +136 -0
- package/sdd/workflows/discuss-phase/templates/discussion-log.md +50 -0
- package/sdd/workflows/discuss-phase-assumptions.md +41 -20
- package/sdd/workflows/discuss-phase-power.md +291 -0
- package/sdd/workflows/discuss-phase.md +242 -792
- package/sdd/workflows/do.md +13 -7
- package/sdd/workflows/docs-update.md +1161 -0
- package/sdd/workflows/edit-phase.md +294 -0
- package/sdd/workflows/eval-review.md +155 -0
- package/sdd/workflows/execute-phase/steps/codebase-drift-gate.md +81 -0
- package/sdd/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/sdd/workflows/execute-phase/steps/post-merge-gate.md +116 -0
- package/sdd/workflows/execute-phase.md +1062 -108
- package/sdd/workflows/execute-plan.md +118 -107
- package/sdd/workflows/explore.md +143 -0
- package/sdd/workflows/extract-learnings.md +242 -0
- package/sdd/workflows/forensics.md +17 -4
- package/sdd/workflows/graduation.md +195 -0
- package/sdd/workflows/health.md +45 -3
- package/sdd/workflows/help.md +265 -88
- package/sdd/workflows/import.md +253 -0
- package/sdd/workflows/inbox.md +387 -0
- package/sdd/workflows/ingest-docs.md +339 -0
- package/sdd/workflows/insert-phase.md +37 -16
- package/sdd/workflows/list-phase-assumptions.md +2 -2
- package/sdd/workflows/list-workspaces.md +3 -3
- package/sdd/workflows/manager.md +62 -32
- package/sdd/workflows/map-codebase.md +90 -24
- package/sdd/workflows/milestone-summary.md +6 -6
- package/sdd/workflows/mvp-phase.md +221 -0
- package/sdd/workflows/new-milestone.md +168 -20
- package/sdd/workflows/new-project.md +273 -47
- package/sdd/workflows/new-workspace.md +8 -6
- package/sdd/workflows/next.md +127 -4
- package/sdd/workflows/note.md +7 -5
- package/sdd/workflows/pause-work.md +79 -12
- package/sdd/workflows/plan-milestone-gaps.md +14 -7
- package/sdd/workflows/plan-phase.md +987 -62
- package/sdd/workflows/plan-review-convergence.md +329 -0
- package/sdd/workflows/plant-seed.md +145 -85
- package/sdd/workflows/pr-branch.md +41 -13
- package/sdd/workflows/profile-user.md +20 -18
- package/sdd/workflows/progress.md +186 -44
- package/sdd/workflows/quick.md +470 -58
- package/sdd/workflows/reapply-patches.md +390 -0
- package/sdd/workflows/remove-phase.md +12 -12
- package/sdd/workflows/remove-workspace.md +24 -7
- package/sdd/workflows/resume-project.md +18 -15
- package/sdd/workflows/review.md +242 -11
- package/sdd/workflows/scan.md +104 -0
- package/sdd/workflows/secure-phase.md +179 -0
- package/sdd/workflows/session-report.md +2 -2
- package/sdd/workflows/settings-advanced.md +579 -0
- package/sdd/workflows/settings-integrations.md +281 -0
- package/sdd/workflows/settings.md +221 -16
- package/sdd/workflows/ship.md +140 -13
- package/sdd/workflows/sketch-wrap-up.md +285 -0
- package/sdd/workflows/sketch.md +360 -0
- package/sdd/workflows/spec-phase.md +262 -0
- package/sdd/workflows/spike-wrap-up.md +306 -0
- package/sdd/workflows/spike.md +452 -0
- package/sdd/workflows/stats.md +20 -1
- package/sdd/workflows/sync-skills.md +182 -0
- package/sdd/workflows/thread.md +221 -0
- package/sdd/workflows/transition.md +44 -22
- package/sdd/workflows/ui-phase.md +39 -14
- package/sdd/workflows/ui-review.md +33 -6
- package/sdd/workflows/ultraplan-phase.md +198 -0
- package/sdd/workflows/undo.md +314 -0
- package/sdd/workflows/update.md +350 -29
- package/sdd/workflows/validate-phase.md +10 -6
- package/sdd/workflows/verify-phase.md +307 -18
- package/sdd/workflows/verify-work.md +153 -10
- package/sdk/dist/cli-transport.d.ts +19 -0
- package/sdk/dist/cli-transport.d.ts.map +1 -0
- package/sdk/dist/cli-transport.js +104 -0
- package/sdk/dist/cli-transport.js.map +1 -0
- package/sdk/dist/cli.d.ts +46 -0
- package/sdk/dist/cli.d.ts.map +1 -0
- package/sdk/dist/cli.js +511 -0
- package/sdk/dist/cli.js.map +1 -0
- package/sdk/dist/config.d.ts +84 -0
- package/sdk/dist/config.d.ts.map +1 -0
- package/sdk/dist/config.js +135 -0
- package/sdk/dist/config.js.map +1 -0
- package/sdk/dist/context-engine.d.ts +49 -0
- package/sdk/dist/context-engine.d.ts.map +1 -0
- package/sdk/dist/context-engine.js +142 -0
- package/sdk/dist/context-engine.js.map +1 -0
- package/sdk/dist/context-truncation.d.ts +33 -0
- package/sdk/dist/context-truncation.d.ts.map +1 -0
- package/sdk/dist/context-truncation.js +197 -0
- package/sdk/dist/context-truncation.js.map +1 -0
- package/sdk/dist/errors.d.ts +46 -0
- package/sdk/dist/errors.d.ts.map +1 -0
- package/sdk/dist/errors.js +64 -0
- package/sdk/dist/errors.js.map +1 -0
- package/sdk/dist/event-stream.d.ts +53 -0
- package/sdk/dist/event-stream.d.ts.map +1 -0
- package/sdk/dist/event-stream.js +321 -0
- package/sdk/dist/event-stream.js.map +1 -0
- package/sdk/dist/golden/capture.d.ts +15 -0
- package/sdk/dist/golden/capture.d.ts.map +1 -0
- package/sdk/dist/golden/capture.js +67 -0
- package/sdk/dist/golden/capture.js.map +1 -0
- package/sdk/dist/golden/golden-integration-covered.d.ts +6 -0
- package/sdk/dist/golden/golden-integration-covered.d.ts.map +1 -0
- package/sdk/dist/golden/golden-integration-covered.js +30 -0
- package/sdk/dist/golden/golden-integration-covered.js.map +1 -0
- package/sdk/dist/golden/golden-mutation-covered.d.ts +7 -0
- package/sdk/dist/golden/golden-mutation-covered.d.ts.map +1 -0
- package/sdk/dist/golden/golden-mutation-covered.js +17 -0
- package/sdk/dist/golden/golden-mutation-covered.js.map +1 -0
- package/sdk/dist/golden/golden-policy.d.ts +10 -0
- package/sdk/dist/golden/golden-policy.d.ts.map +1 -0
- package/sdk/dist/golden/golden-policy.js +98 -0
- package/sdk/dist/golden/golden-policy.js.map +1 -0
- package/sdk/dist/golden/init-golden-normalize.d.ts +8 -0
- package/sdk/dist/golden/init-golden-normalize.d.ts.map +1 -0
- package/sdk/dist/golden/init-golden-normalize.js +14 -0
- package/sdk/dist/golden/init-golden-normalize.js.map +1 -0
- package/sdk/dist/golden/read-only-golden-rows.d.ts +20 -0
- package/sdk/dist/golden/read-only-golden-rows.d.ts.map +1 -0
- package/sdk/dist/golden/read-only-golden-rows.js +67 -0
- package/sdk/dist/golden/read-only-golden-rows.js.map +1 -0
- package/sdk/dist/golden/registry-canonical-commands.d.ts +6 -0
- package/sdk/dist/golden/registry-canonical-commands.d.ts.map +1 -0
- package/sdk/dist/golden/registry-canonical-commands.js +30 -0
- package/sdk/dist/golden/registry-canonical-commands.js.map +1 -0
- package/sdk/dist/index.d.ts +125 -0
- package/sdk/dist/index.d.ts.map +1 -0
- package/sdk/dist/index.js +298 -0
- package/sdk/dist/index.js.map +1 -0
- package/sdk/dist/init-runner.d.ts +90 -0
- package/sdk/dist/init-runner.d.ts.map +1 -0
- package/sdk/dist/init-runner.js +613 -0
- package/sdk/dist/init-runner.js.map +1 -0
- package/sdk/dist/logger.d.ts +50 -0
- package/sdk/dist/logger.d.ts.map +1 -0
- package/sdk/dist/logger.js +70 -0
- package/sdk/dist/logger.js.map +1 -0
- package/sdk/dist/model-catalog.d.ts +31 -0
- package/sdk/dist/model-catalog.d.ts.map +1 -0
- package/sdk/dist/model-catalog.js +31 -0
- package/sdk/dist/model-catalog.js.map +1 -0
- package/sdk/dist/phase-prompt.d.ts +72 -0
- package/sdk/dist/phase-prompt.d.ts.map +1 -0
- package/sdk/dist/phase-prompt.js +213 -0
- package/sdk/dist/phase-prompt.js.map +1 -0
- package/sdk/dist/phase-runner.d.ts +145 -0
- package/sdk/dist/phase-runner.d.ts.map +1 -0
- package/sdk/dist/phase-runner.js +1206 -0
- package/sdk/dist/phase-runner.js.map +1 -0
- package/sdk/dist/plan-parser.d.ts +55 -0
- package/sdk/dist/plan-parser.d.ts.map +1 -0
- package/sdk/dist/plan-parser.js +389 -0
- package/sdk/dist/plan-parser.js.map +1 -0
- package/sdk/dist/planning-journal.d.ts +64 -0
- package/sdk/dist/planning-journal.d.ts.map +1 -0
- package/sdk/dist/planning-journal.js +88 -0
- package/sdk/dist/planning-journal.js.map +1 -0
- package/sdk/dist/planning-runtime.d.ts +67 -0
- package/sdk/dist/planning-runtime.d.ts.map +1 -0
- package/sdk/dist/planning-runtime.js +58 -0
- package/sdk/dist/planning-runtime.js.map +1 -0
- package/sdk/dist/prompt-builder.d.ts +44 -0
- package/sdk/dist/prompt-builder.d.ts.map +1 -0
- package/sdk/dist/prompt-builder.js +180 -0
- package/sdk/dist/prompt-builder.js.map +1 -0
- package/sdk/dist/prompt-sanitizer.d.ts +35 -0
- package/sdk/dist/prompt-sanitizer.d.ts.map +1 -0
- package/sdk/dist/prompt-sanitizer.js +101 -0
- package/sdk/dist/prompt-sanitizer.js.map +1 -0
- package/sdk/dist/query/active-workstream-store.d.ts +7 -0
- package/sdk/dist/query/active-workstream-store.d.ts.map +1 -0
- package/sdk/dist/query/active-workstream-store.js +56 -0
- package/sdk/dist/query/active-workstream-store.js.map +1 -0
- package/sdk/dist/query/agent-failure-classifier.d.ts +38 -0
- package/sdk/dist/query/agent-failure-classifier.d.ts.map +1 -0
- package/sdk/dist/query/agent-failure-classifier.js +83 -0
- package/sdk/dist/query/agent-failure-classifier.js.map +1 -0
- package/sdk/dist/query/audit-open.d.ts +46 -0
- package/sdk/dist/query/audit-open.d.ts.map +1 -0
- package/sdk/dist/query/audit-open.js +662 -0
- package/sdk/dist/query/audit-open.js.map +1 -0
- package/sdk/dist/query/check-auto-mode.d.ts +13 -0
- package/sdk/dist/query/check-auto-mode.d.ts.map +1 -0
- package/sdk/dist/query/check-auto-mode.js +40 -0
- package/sdk/dist/query/check-auto-mode.js.map +1 -0
- package/sdk/dist/query/check-completion.d.ts +10 -0
- package/sdk/dist/query/check-completion.d.ts.map +1 -0
- package/sdk/dist/query/check-completion.js +157 -0
- package/sdk/dist/query/check-completion.js.map +1 -0
- package/sdk/dist/query/check-decision-coverage.d.ts +33 -0
- package/sdk/dist/query/check-decision-coverage.d.ts.map +1 -0
- package/sdk/dist/query/check-decision-coverage.js +472 -0
- package/sdk/dist/query/check-decision-coverage.js.map +1 -0
- package/sdk/dist/query/check-gates.d.ts +10 -0
- package/sdk/dist/query/check-gates.d.ts.map +1 -0
- package/sdk/dist/query/check-gates.js +89 -0
- package/sdk/dist/query/check-gates.js.map +1 -0
- package/sdk/dist/query/check-ship-ready.d.ts +10 -0
- package/sdk/dist/query/check-ship-ready.d.ts.map +1 -0
- package/sdk/dist/query/check-ship-ready.js +93 -0
- package/sdk/dist/query/check-ship-ready.js.map +1 -0
- package/sdk/dist/query/check-verification-status.d.ts +10 -0
- package/sdk/dist/query/check-verification-status.d.ts.map +1 -0
- package/sdk/dist/query/check-verification-status.js +142 -0
- package/sdk/dist/query/check-verification-status.js.map +1 -0
- package/sdk/dist/query/command-aliases.generated.d.ts +31 -0
- package/sdk/dist/query/command-aliases.generated.d.ts.map +1 -0
- package/sdk/dist/query/command-aliases.generated.js +135 -0
- package/sdk/dist/query/command-aliases.generated.js.map +1 -0
- package/sdk/dist/query/command-catalog.d.ts +9 -0
- package/sdk/dist/query/command-catalog.d.ts.map +1 -0
- package/sdk/dist/query/command-catalog.js +17 -0
- package/sdk/dist/query/command-catalog.js.map +1 -0
- package/sdk/dist/query/command-definition.d.ts +19 -0
- package/sdk/dist/query/command-definition.d.ts.map +1 -0
- package/sdk/dist/query/command-definition.js +44 -0
- package/sdk/dist/query/command-definition.js.map +1 -0
- package/sdk/dist/query/command-family-handlers.d.ts +3 -0
- package/sdk/dist/query/command-family-handlers.d.ts.map +1 -0
- package/sdk/dist/query/command-family-handlers.js +94 -0
- package/sdk/dist/query/command-family-handlers.js.map +1 -0
- package/sdk/dist/query/command-manifest.d.ts +2 -0
- package/sdk/dist/query/command-manifest.d.ts.map +1 -0
- package/sdk/dist/query/command-manifest.init.d.ts +6 -0
- package/sdk/dist/query/command-manifest.init.d.ts.map +1 -0
- package/sdk/dist/query/command-manifest.init.js +23 -0
- package/sdk/dist/query/command-manifest.init.js.map +1 -0
- package/sdk/dist/query/command-manifest.js +17 -0
- package/sdk/dist/query/command-manifest.js.map +1 -0
- package/sdk/dist/query/command-manifest.non-family.d.ts +9 -0
- package/sdk/dist/query/command-manifest.non-family.d.ts.map +1 -0
- package/sdk/dist/query/command-manifest.non-family.js +59 -0
- package/sdk/dist/query/command-manifest.non-family.js.map +1 -0
- package/sdk/dist/query/command-manifest.phase.d.ts +6 -0
- package/sdk/dist/query/command-manifest.phase.d.ts.map +1 -0
- package/sdk/dist/query/command-manifest.phase.js +15 -0
- package/sdk/dist/query/command-manifest.phase.js.map +1 -0
- package/sdk/dist/query/command-manifest.phases.d.ts +7 -0
- package/sdk/dist/query/command-manifest.phases.d.ts.map +1 -0
- package/sdk/dist/query/command-manifest.phases.js +10 -0
- package/sdk/dist/query/command-manifest.phases.js.map +1 -0
- package/sdk/dist/query/command-manifest.roadmap.d.ts +6 -0
- package/sdk/dist/query/command-manifest.roadmap.d.ts.map +1 -0
- package/sdk/dist/query/command-manifest.roadmap.js +10 -0
- package/sdk/dist/query/command-manifest.roadmap.js.map +1 -0
- package/sdk/dist/query/command-manifest.state.d.ts +9 -0
- package/sdk/dist/query/command-manifest.state.d.ts.map +1 -0
- package/sdk/dist/query/command-manifest.state.js +30 -0
- package/sdk/dist/query/command-manifest.state.js.map +1 -0
- package/sdk/dist/query/command-manifest.types.d.ts +12 -0
- package/sdk/dist/query/command-manifest.types.d.ts.map +1 -0
- package/sdk/dist/query/command-manifest.types.js +2 -0
- package/sdk/dist/query/command-manifest.types.js.map +1 -0
- package/sdk/dist/query/command-manifest.validate.d.ts +6 -0
- package/sdk/dist/query/command-manifest.validate.d.ts.map +1 -0
- package/sdk/dist/query/command-manifest.validate.js +10 -0
- package/sdk/dist/query/command-manifest.validate.js.map +1 -0
- package/sdk/dist/query/command-manifest.verify.d.ts +6 -0
- package/sdk/dist/query/command-manifest.verify.d.ts.map +1 -0
- package/sdk/dist/query/command-manifest.verify.js +14 -0
- package/sdk/dist/query/command-manifest.verify.js.map +1 -0
- package/sdk/dist/query/command-static-catalog-domain.d.ts +3 -0
- package/sdk/dist/query/command-static-catalog-domain.d.ts.map +1 -0
- package/sdk/dist/query/command-static-catalog-domain.js +116 -0
- package/sdk/dist/query/command-static-catalog-domain.js.map +1 -0
- package/sdk/dist/query/command-static-catalog-foundation.d.ts +7 -0
- package/sdk/dist/query/command-static-catalog-foundation.d.ts.map +1 -0
- package/sdk/dist/query/command-static-catalog-foundation.js +98 -0
- package/sdk/dist/query/command-static-catalog-foundation.js.map +1 -0
- package/sdk/dist/query/command-topology.d.ts +32 -0
- package/sdk/dist/query/command-topology.d.ts.map +1 -0
- package/sdk/dist/query/command-topology.js +66 -0
- package/sdk/dist/query/command-topology.js.map +1 -0
- package/sdk/dist/query/commands-list.d.ts +14 -0
- package/sdk/dist/query/commands-list.d.ts.map +1 -0
- package/sdk/dist/query/commands-list.js +18 -0
- package/sdk/dist/query/commands-list.js.map +1 -0
- package/sdk/dist/query/commit.d.ts +79 -0
- package/sdk/dist/query/commit.d.ts.map +1 -0
- package/sdk/dist/query/commit.js +340 -0
- package/sdk/dist/query/commit.js.map +1 -0
- package/sdk/dist/query/config-gates.d.ts +12 -0
- package/sdk/dist/query/config-gates.d.ts.map +1 -0
- package/sdk/dist/query/config-gates.js +66 -0
- package/sdk/dist/query/config-gates.js.map +1 -0
- package/sdk/dist/query/config-mutation.d.ts +86 -0
- package/sdk/dist/query/config-mutation.d.ts.map +1 -0
- package/sdk/dist/query/config-mutation.js +518 -0
- package/sdk/dist/query/config-mutation.js.map +1 -0
- package/sdk/dist/query/config-query.d.ts +57 -0
- package/sdk/dist/query/config-query.d.ts.map +1 -0
- package/sdk/dist/query/config-query.js +208 -0
- package/sdk/dist/query/config-query.js.map +1 -0
- package/sdk/dist/query/config-schema.d.ts +36 -0
- package/sdk/dist/query/config-schema.d.ts.map +1 -0
- package/sdk/dist/query/config-schema.js +147 -0
- package/sdk/dist/query/config-schema.js.map +1 -0
- package/sdk/dist/query/decisions.d.ts +58 -0
- package/sdk/dist/query/decisions.d.ts.map +1 -0
- package/sdk/dist/query/decisions.js +161 -0
- package/sdk/dist/query/decisions.js.map +1 -0
- package/sdk/dist/query/detect-custom-files.d.ts +11 -0
- package/sdk/dist/query/detect-custom-files.d.ts.map +1 -0
- package/sdk/dist/query/detect-custom-files.js +89 -0
- package/sdk/dist/query/detect-custom-files.js.map +1 -0
- package/sdk/dist/query/detect-phase-type.d.ts +9 -0
- package/sdk/dist/query/detect-phase-type.d.ts.map +1 -0
- package/sdk/dist/query/detect-phase-type.js +124 -0
- package/sdk/dist/query/detect-phase-type.js.map +1 -0
- package/sdk/dist/query/docs-init.d.ts +26 -0
- package/sdk/dist/query/docs-init.d.ts.map +1 -0
- package/sdk/dist/query/docs-init.js +231 -0
- package/sdk/dist/query/docs-init.js.map +1 -0
- package/sdk/dist/query/fallow-audit.d.ts +44 -0
- package/sdk/dist/query/fallow-audit.d.ts.map +1 -0
- package/sdk/dist/query/fallow-audit.js +44 -0
- package/sdk/dist/query/fallow-audit.js.map +1 -0
- package/sdk/dist/query/frontmatter-mutation.d.ts +77 -0
- package/sdk/dist/query/frontmatter-mutation.d.ts.map +1 -0
- package/sdk/dist/query/frontmatter-mutation.js +317 -0
- package/sdk/dist/query/frontmatter-mutation.js.map +1 -0
- package/sdk/dist/query/frontmatter.d.ts +93 -0
- package/sdk/dist/query/frontmatter.d.ts.map +1 -0
- package/sdk/dist/query/frontmatter.js +365 -0
- package/sdk/dist/query/frontmatter.js.map +1 -0
- package/sdk/dist/query/helpers.d.ts +191 -0
- package/sdk/dist/query/helpers.d.ts.map +1 -0
- package/sdk/dist/query/helpers.js +613 -0
- package/sdk/dist/query/helpers.js.map +1 -0
- package/sdk/dist/query/index.d.ts +8 -0
- package/sdk/dist/query/index.d.ts.map +1 -0
- package/sdk/dist/query/index.js +6 -0
- package/sdk/dist/query/index.js.map +1 -0
- package/sdk/dist/query/init-complex.d.ts +47 -0
- package/sdk/dist/query/init-complex.d.ts.map +1 -0
- package/sdk/dist/query/init-complex.js +718 -0
- package/sdk/dist/query/init-complex.js.map +1 -0
- package/sdk/dist/query/init.d.ts +106 -0
- package/sdk/dist/query/init.d.ts.map +1 -0
- package/sdk/dist/query/init.js +1159 -0
- package/sdk/dist/query/init.js.map +1 -0
- package/sdk/dist/query/intel.d.ts +43 -0
- package/sdk/dist/query/intel.d.ts.map +1 -0
- package/sdk/dist/query/intel.js +416 -0
- package/sdk/dist/query/intel.js.map +1 -0
- package/sdk/dist/query/mutation-event-decorator.d.ts +5 -0
- package/sdk/dist/query/mutation-event-decorator.d.ts.map +1 -0
- package/sdk/dist/query/mutation-event-decorator.js +28 -0
- package/sdk/dist/query/mutation-event-decorator.js.map +1 -0
- package/sdk/dist/query/mutation-event-mapper.d.ts +4 -0
- package/sdk/dist/query/mutation-event-mapper.d.ts.map +1 -0
- package/sdk/dist/query/mutation-event-mapper.js +70 -0
- package/sdk/dist/query/mutation-event-mapper.js.map +1 -0
- package/sdk/dist/query/mvp.d.ts +113 -0
- package/sdk/dist/query/mvp.d.ts.map +1 -0
- package/sdk/dist/query/mvp.js +225 -0
- package/sdk/dist/query/mvp.js.map +1 -0
- package/sdk/dist/query/phase-filesystem-adapter.d.ts +4 -0
- package/sdk/dist/query/phase-filesystem-adapter.d.ts.map +1 -0
- package/sdk/dist/query/phase-filesystem-adapter.js +33 -0
- package/sdk/dist/query/phase-filesystem-adapter.js.map +1 -0
- package/sdk/dist/query/phase-lifecycle-policy.d.ts +34 -0
- package/sdk/dist/query/phase-lifecycle-policy.d.ts.map +1 -0
- package/sdk/dist/query/phase-lifecycle-policy.js +138 -0
- package/sdk/dist/query/phase-lifecycle-policy.js.map +1 -0
- package/sdk/dist/query/phase-lifecycle.d.ts +116 -0
- package/sdk/dist/query/phase-lifecycle.d.ts.map +1 -0
- package/sdk/dist/query/phase-lifecycle.js +1486 -0
- package/sdk/dist/query/phase-lifecycle.js.map +1 -0
- package/sdk/dist/query/phase-list-queries.d.ts +18 -0
- package/sdk/dist/query/phase-list-queries.d.ts.map +1 -0
- package/sdk/dist/query/phase-list-queries.js +129 -0
- package/sdk/dist/query/phase-list-queries.js.map +1 -0
- package/sdk/dist/query/phase-ready.d.ts +9 -0
- package/sdk/dist/query/phase-ready.d.ts.map +1 -0
- package/sdk/dist/query/phase-ready.js +132 -0
- package/sdk/dist/query/phase-ready.js.map +1 -0
- package/sdk/dist/query/phase-roadmap-mutation.d.ts +13 -0
- package/sdk/dist/query/phase-roadmap-mutation.d.ts.map +1 -0
- package/sdk/dist/query/phase-roadmap-mutation.js +65 -0
- package/sdk/dist/query/phase-roadmap-mutation.js.map +1 -0
- package/sdk/dist/query/phase.d.ts +48 -0
- package/sdk/dist/query/phase.d.ts.map +1 -0
- package/sdk/dist/query/phase.js +451 -0
- package/sdk/dist/query/phase.js.map +1 -0
- package/sdk/dist/query/pipeline.d.ts +53 -0
- package/sdk/dist/query/pipeline.d.ts.map +1 -0
- package/sdk/dist/query/pipeline.js +198 -0
- package/sdk/dist/query/pipeline.js.map +1 -0
- package/sdk/dist/query/plan-scan.d.ts +14 -0
- package/sdk/dist/query/plan-scan.d.ts.map +1 -0
- package/sdk/dist/query/plan-scan.js +70 -0
- package/sdk/dist/query/plan-scan.js.map +1 -0
- package/sdk/dist/query/plan-task-structure.d.ts +9 -0
- package/sdk/dist/query/plan-task-structure.d.ts.map +1 -0
- package/sdk/dist/query/plan-task-structure.js +59 -0
- package/sdk/dist/query/plan-task-structure.js.map +1 -0
- package/sdk/dist/query/profile-extract-messages.d.ts +40 -0
- package/sdk/dist/query/profile-extract-messages.d.ts.map +1 -0
- package/sdk/dist/query/profile-extract-messages.js +195 -0
- package/sdk/dist/query/profile-extract-messages.js.map +1 -0
- package/sdk/dist/query/profile-output.d.ts +11 -0
- package/sdk/dist/query/profile-output.d.ts.map +1 -0
- package/sdk/dist/query/profile-output.js +873 -0
- package/sdk/dist/query/profile-output.js.map +1 -0
- package/sdk/dist/query/profile-questionnaire-data.d.ts +21 -0
- package/sdk/dist/query/profile-questionnaire-data.d.ts.map +1 -0
- package/sdk/dist/query/profile-questionnaire-data.js +171 -0
- package/sdk/dist/query/profile-questionnaire-data.js.map +1 -0
- package/sdk/dist/query/profile-sample.d.ts +22 -0
- package/sdk/dist/query/profile-sample.d.ts.map +1 -0
- package/sdk/dist/query/profile-sample.js +136 -0
- package/sdk/dist/query/profile-sample.js.map +1 -0
- package/sdk/dist/query/profile-scan-sessions.d.ts +49 -0
- package/sdk/dist/query/profile-scan-sessions.d.ts.map +1 -0
- package/sdk/dist/query/profile-scan-sessions.js +137 -0
- package/sdk/dist/query/profile-scan-sessions.js.map +1 -0
- package/sdk/dist/query/profile.d.ts +61 -0
- package/sdk/dist/query/profile.d.ts.map +1 -0
- package/sdk/dist/query/profile.js +307 -0
- package/sdk/dist/query/profile.js.map +1 -0
- package/sdk/dist/query/progress.d.ts +77 -0
- package/sdk/dist/query/progress.d.ts.map +1 -0
- package/sdk/dist/query/progress.js +481 -0
- package/sdk/dist/query/progress.js.map +1 -0
- package/sdk/dist/query/query-cli-adapter.d.ts +8 -0
- package/sdk/dist/query/query-cli-adapter.d.ts.map +1 -0
- package/sdk/dist/query/query-cli-adapter.js +32 -0
- package/sdk/dist/query/query-cli-adapter.js.map +1 -0
- package/sdk/dist/query/query-cli-output.d.ts +9 -0
- package/sdk/dist/query/query-cli-output.d.ts.map +1 -0
- package/sdk/dist/query/query-cli-output.js +28 -0
- package/sdk/dist/query/query-cli-output.js.map +1 -0
- package/sdk/dist/query/query-command-diagnosis.d.ts +6 -0
- package/sdk/dist/query/query-command-diagnosis.d.ts.map +1 -0
- package/sdk/dist/query/query-command-diagnosis.js +6 -0
- package/sdk/dist/query/query-command-diagnosis.js.map +1 -0
- package/sdk/dist/query/query-command-resolution-strategy.d.ts +29 -0
- package/sdk/dist/query/query-command-resolution-strategy.d.ts.map +1 -0
- package/sdk/dist/query/query-command-resolution-strategy.js +103 -0
- package/sdk/dist/query/query-command-resolution-strategy.js.map +1 -0
- package/sdk/dist/query/query-command-semantics.d.ts +7 -0
- package/sdk/dist/query/query-command-semantics.d.ts.map +1 -0
- package/sdk/dist/query/query-command-semantics.js +7 -0
- package/sdk/dist/query/query-command-semantics.js.map +1 -0
- package/sdk/dist/query/query-dispatch-contract.d.ts +21 -0
- package/sdk/dist/query/query-dispatch-contract.d.ts.map +1 -0
- package/sdk/dist/query/query-dispatch-contract.js +2 -0
- package/sdk/dist/query/query-dispatch-contract.js.map +1 -0
- package/sdk/dist/query/query-dispatch-error-mapper.d.ts +6 -0
- package/sdk/dist/query/query-dispatch-error-mapper.d.ts.map +1 -0
- package/sdk/dist/query/query-dispatch-error-mapper.js +6 -0
- package/sdk/dist/query/query-dispatch-error-mapper.js.map +1 -0
- package/sdk/dist/query/query-dispatch-formatting.d.ts +6 -0
- package/sdk/dist/query/query-dispatch-formatting.d.ts.map +1 -0
- package/sdk/dist/query/query-dispatch-formatting.js +6 -0
- package/sdk/dist/query/query-dispatch-formatting.js.map +1 -0
- package/sdk/dist/query/query-dispatch-input-validation.d.ts +6 -0
- package/sdk/dist/query/query-dispatch-input-validation.d.ts.map +1 -0
- package/sdk/dist/query/query-dispatch-input-validation.js +6 -0
- package/sdk/dist/query/query-dispatch-input-validation.js.map +1 -0
- package/sdk/dist/query/query-dispatch-observability.d.ts +2 -0
- package/sdk/dist/query/query-dispatch-observability.d.ts.map +1 -0
- package/sdk/dist/query/query-dispatch-observability.js +7 -0
- package/sdk/dist/query/query-dispatch-observability.js.map +1 -0
- package/sdk/dist/query/query-dispatch-plan.d.ts +6 -0
- package/sdk/dist/query/query-dispatch-plan.d.ts.map +1 -0
- package/sdk/dist/query/query-dispatch-plan.js +6 -0
- package/sdk/dist/query/query-dispatch-plan.js.map +1 -0
- package/sdk/dist/query/query-dispatch-result-builder.d.ts +6 -0
- package/sdk/dist/query/query-dispatch-result-builder.d.ts.map +1 -0
- package/sdk/dist/query/query-dispatch-result-builder.js +6 -0
- package/sdk/dist/query/query-dispatch-result-builder.js.map +1 -0
- package/sdk/dist/query/query-dispatch.d.ts +48 -0
- package/sdk/dist/query/query-dispatch.d.ts.map +1 -0
- package/sdk/dist/query/query-dispatch.js +175 -0
- package/sdk/dist/query/query-dispatch.js.map +1 -0
- package/sdk/dist/query/query-error-details-schema.d.ts +19 -0
- package/sdk/dist/query/query-error-details-schema.d.ts.map +1 -0
- package/sdk/dist/query/query-error-details-schema.js +10 -0
- package/sdk/dist/query/query-error-details-schema.js.map +1 -0
- package/sdk/dist/query/query-error-taxonomy.d.ts +38 -0
- package/sdk/dist/query/query-error-taxonomy.d.ts.map +1 -0
- package/sdk/dist/query/query-error-taxonomy.js +74 -0
- package/sdk/dist/query/query-error-taxonomy.js.map +1 -0
- package/sdk/dist/query/query-fallback-bridge-adapter.d.ts +14 -0
- package/sdk/dist/query/query-fallback-bridge-adapter.d.ts.map +1 -0
- package/sdk/dist/query/query-fallback-bridge-adapter.js +33 -0
- package/sdk/dist/query/query-fallback-bridge-adapter.js.map +1 -0
- package/sdk/dist/query/query-fallback-executor.d.ts +11 -0
- package/sdk/dist/query/query-fallback-executor.d.ts.map +1 -0
- package/sdk/dist/query/query-fallback-executor.js +31 -0
- package/sdk/dist/query/query-fallback-executor.js.map +1 -0
- package/sdk/dist/query/query-fallback-output-classifier.d.ts +6 -0
- package/sdk/dist/query/query-fallback-output-classifier.d.ts.map +1 -0
- package/sdk/dist/query/query-fallback-output-classifier.js +27 -0
- package/sdk/dist/query/query-fallback-output-classifier.js.map +1 -0
- package/sdk/dist/query/query-fallback-policy.d.ts +6 -0
- package/sdk/dist/query/query-fallback-policy.d.ts.map +1 -0
- package/sdk/dist/query/query-fallback-policy.js +7 -0
- package/sdk/dist/query/query-fallback-policy.js.map +1 -0
- package/sdk/dist/query/query-native-dispatch-adapter.d.ts +7 -0
- package/sdk/dist/query/query-native-dispatch-adapter.d.ts.map +1 -0
- package/sdk/dist/query/query-native-dispatch-adapter.js +6 -0
- package/sdk/dist/query/query-native-dispatch-adapter.js.map +1 -0
- package/sdk/dist/query/query-policy-capability.d.ts +10 -0
- package/sdk/dist/query/query-policy-capability.d.ts.map +1 -0
- package/sdk/dist/query/query-policy-capability.js +17 -0
- package/sdk/dist/query/query-policy-capability.js.map +1 -0
- package/sdk/dist/query/query-runtime-context.d.ts +19 -0
- package/sdk/dist/query/query-runtime-context.d.ts.map +1 -0
- package/sdk/dist/query/query-runtime-context.js +31 -0
- package/sdk/dist/query/query-runtime-context.js.map +1 -0
- package/sdk/dist/query/query-unknown-command-hints.d.ts +2 -0
- package/sdk/dist/query/query-unknown-command-hints.d.ts.map +1 -0
- package/sdk/dist/query/query-unknown-command-hints.js +6 -0
- package/sdk/dist/query/query-unknown-command-hints.js.map +1 -0
- package/sdk/dist/query/registry-assembly-descriptor.d.ts +12 -0
- package/sdk/dist/query/registry-assembly-descriptor.d.ts.map +1 -0
- package/sdk/dist/query/registry-assembly-descriptor.js +61 -0
- package/sdk/dist/query/registry-assembly-descriptor.js.map +1 -0
- package/sdk/dist/query/registry-assembly-invariants.d.ts +30 -0
- package/sdk/dist/query/registry-assembly-invariants.d.ts.map +1 -0
- package/sdk/dist/query/registry-assembly-invariants.js +77 -0
- package/sdk/dist/query/registry-assembly-invariants.js.map +1 -0
- package/sdk/dist/query/registry-assembly.d.ts +10 -0
- package/sdk/dist/query/registry-assembly.d.ts.map +1 -0
- package/sdk/dist/query/registry-assembly.js +53 -0
- package/sdk/dist/query/registry-assembly.js.map +1 -0
- package/sdk/dist/query/registry.d.ts +90 -0
- package/sdk/dist/query/registry.d.ts.map +1 -0
- package/sdk/dist/query/registry.js +129 -0
- package/sdk/dist/query/registry.js.map +1 -0
- package/sdk/dist/query/requirements-extract-from-plans.d.ts +9 -0
- package/sdk/dist/query/requirements-extract-from-plans.d.ts.map +1 -0
- package/sdk/dist/query/requirements-extract-from-plans.js +76 -0
- package/sdk/dist/query/requirements-extract-from-plans.js.map +1 -0
- package/sdk/dist/query/roadmap-update-plan-progress.d.ts +11 -0
- package/sdk/dist/query/roadmap-update-plan-progress.d.ts.map +1 -0
- package/sdk/dist/query/roadmap-update-plan-progress.js +124 -0
- package/sdk/dist/query/roadmap-update-plan-progress.js.map +1 -0
- package/sdk/dist/query/roadmap.d.ts +137 -0
- package/sdk/dist/query/roadmap.d.ts.map +1 -0
- package/sdk/dist/query/roadmap.js +753 -0
- package/sdk/dist/query/roadmap.js.map +1 -0
- package/sdk/dist/query/route-next-action.d.ts +9 -0
- package/sdk/dist/query/route-next-action.d.ts.map +1 -0
- package/sdk/dist/query/route-next-action.js +318 -0
- package/sdk/dist/query/route-next-action.js.map +1 -0
- package/sdk/dist/query/schema-detect.d.ts +21 -0
- package/sdk/dist/query/schema-detect.d.ts.map +1 -0
- package/sdk/dist/query/schema-detect.js +146 -0
- package/sdk/dist/query/schema-detect.js.map +1 -0
- package/sdk/dist/query/secrets.d.ts +27 -0
- package/sdk/dist/query/secrets.d.ts.map +1 -0
- package/sdk/dist/query/secrets.js +42 -0
- package/sdk/dist/query/secrets.js.map +1 -0
- package/sdk/dist/query/skill-manifest.d.ts +50 -0
- package/sdk/dist/query/skill-manifest.d.ts.map +1 -0
- package/sdk/dist/query/skill-manifest.js +171 -0
- package/sdk/dist/query/skill-manifest.js.map +1 -0
- package/sdk/dist/query/skills.d.ts +27 -0
- package/sdk/dist/query/skills.d.ts.map +1 -0
- package/sdk/dist/query/skills.js +137 -0
- package/sdk/dist/query/skills.js.map +1 -0
- package/sdk/dist/query/state-document.d.ts +14 -0
- package/sdk/dist/query/state-document.d.ts.map +1 -0
- package/sdk/dist/query/state-document.js +110 -0
- package/sdk/dist/query/state-document.js.map +1 -0
- package/sdk/dist/query/state-mutation.d.ts +224 -0
- package/sdk/dist/query/state-mutation.d.ts.map +1 -0
- package/sdk/dist/query/state-mutation.js +1539 -0
- package/sdk/dist/query/state-mutation.js.map +1 -0
- package/sdk/dist/query/state-project-load.d.ts +23 -0
- package/sdk/dist/query/state-project-load.d.ts.map +1 -0
- package/sdk/dist/query/state-project-load.js +75 -0
- package/sdk/dist/query/state-project-load.js.map +1 -0
- package/sdk/dist/query/state.d.ts +78 -0
- package/sdk/dist/query/state.d.ts.map +1 -0
- package/sdk/dist/query/state.js +430 -0
- package/sdk/dist/query/state.js.map +1 -0
- package/sdk/dist/query/summary.d.ts +18 -0
- package/sdk/dist/query/summary.d.ts.map +1 -0
- package/sdk/dist/query/summary.js +249 -0
- package/sdk/dist/query/summary.js.map +1 -0
- package/sdk/dist/query/template.d.ts +46 -0
- package/sdk/dist/query/template.d.ts.map +1 -0
- package/sdk/dist/query/template.js +210 -0
- package/sdk/dist/query/template.js.map +1 -0
- package/sdk/dist/query/uat.d.ts +34 -0
- package/sdk/dist/query/uat.d.ts.map +1 -0
- package/sdk/dist/query/uat.js +339 -0
- package/sdk/dist/query/uat.js.map +1 -0
- package/sdk/dist/query/utils.d.ts +59 -0
- package/sdk/dist/query/utils.d.ts.map +1 -0
- package/sdk/dist/query/utils.js +74 -0
- package/sdk/dist/query/utils.js.map +1 -0
- package/sdk/dist/query/validate.d.ts +67 -0
- package/sdk/dist/query/validate.d.ts.map +1 -0
- package/sdk/dist/query/validate.js +908 -0
- package/sdk/dist/query/validate.js.map +1 -0
- package/sdk/dist/query/verify.d.ts +110 -0
- package/sdk/dist/query/verify.d.ts.map +1 -0
- package/sdk/dist/query/verify.js +631 -0
- package/sdk/dist/query/verify.js.map +1 -0
- package/sdk/dist/query/websearch.d.ts +24 -0
- package/sdk/dist/query/websearch.d.ts.map +1 -0
- package/sdk/dist/query/websearch.js +68 -0
- package/sdk/dist/query/websearch.js.map +1 -0
- package/sdk/dist/query/workspace.d.ts +62 -0
- package/sdk/dist/query/workspace.d.ts.map +1 -0
- package/sdk/dist/query/workspace.js +104 -0
- package/sdk/dist/query/workspace.js.map +1 -0
- package/sdk/dist/query/workstream-inventory.d.ts +52 -0
- package/sdk/dist/query/workstream-inventory.d.ts.map +1 -0
- package/sdk/dist/query/workstream-inventory.js +141 -0
- package/sdk/dist/query/workstream-inventory.js.map +1 -0
- package/sdk/dist/query/workstream.d.ts +35 -0
- package/sdk/dist/query/workstream.d.ts.map +1 -0
- package/sdk/dist/query/workstream.js +298 -0
- package/sdk/dist/query/workstream.js.map +1 -0
- package/sdk/dist/query/worktree.d.ts +3 -0
- package/sdk/dist/query/worktree.d.ts.map +1 -0
- package/sdk/dist/query/worktree.js +36 -0
- package/sdk/dist/query/worktree.js.map +1 -0
- package/sdk/dist/query-command-executor.d.ts +22 -0
- package/sdk/dist/query-command-executor.d.ts.map +1 -0
- package/sdk/dist/query-command-executor.js +22 -0
- package/sdk/dist/query-command-executor.js.map +1 -0
- package/sdk/dist/query-execution-policy.d.ts +24 -0
- package/sdk/dist/query-execution-policy.d.ts.map +1 -0
- package/sdk/dist/query-execution-policy.js +27 -0
- package/sdk/dist/query-execution-policy.js.map +1 -0
- package/sdk/dist/query-failure-classification.d.ts +9 -0
- package/sdk/dist/query-failure-classification.d.ts.map +1 -0
- package/sdk/dist/query-failure-classification.js +32 -0
- package/sdk/dist/query-failure-classification.js.map +1 -0
- package/sdk/dist/query-hotpath-methods.d.ts +19 -0
- package/sdk/dist/query-hotpath-methods.d.ts.map +1 -0
- package/sdk/dist/query-hotpath-methods.js +34 -0
- package/sdk/dist/query-hotpath-methods.js.map +1 -0
- package/sdk/dist/query-native-direct-adapter.d.ts +20 -0
- package/sdk/dist/query-native-direct-adapter.d.ts.map +1 -0
- package/sdk/dist/query-native-direct-adapter.js +52 -0
- package/sdk/dist/query-native-direct-adapter.js.map +1 -0
- package/sdk/dist/query-native-hotpath-adapter.d.ts +15 -0
- package/sdk/dist/query-native-hotpath-adapter.d.ts.map +1 -0
- package/sdk/dist/query-native-hotpath-adapter.js +32 -0
- package/sdk/dist/query-native-hotpath-adapter.js.map +1 -0
- package/sdk/dist/query-raw-output-projection.d.ts +6 -0
- package/sdk/dist/query-raw-output-projection.d.ts.map +1 -0
- package/sdk/dist/query-raw-output-projection.js +67 -0
- package/sdk/dist/query-raw-output-projection.js.map +1 -0
- package/sdk/dist/query-runtime-bridge.d.ts +61 -0
- package/sdk/dist/query-runtime-bridge.d.ts.map +1 -0
- package/sdk/dist/query-runtime-bridge.js +144 -0
- package/sdk/dist/query-runtime-bridge.js.map +1 -0
- package/sdk/dist/query-sdd-tools-path.d.ts +2 -0
- package/sdk/dist/query-sdd-tools-path.d.ts.map +1 -0
- package/sdk/dist/query-sdd-tools-path.js +2 -0
- package/sdk/dist/query-sdd-tools-path.js.map +1 -0
- package/sdk/dist/query-sdd-tools-runtime.d.ts +20 -0
- package/sdk/dist/query-sdd-tools-runtime.d.ts.map +1 -0
- package/sdk/dist/query-sdd-tools-runtime.js +47 -0
- package/sdk/dist/query-sdd-tools-runtime.js.map +1 -0
- package/sdk/dist/query-subprocess-adapter.d.ts +18 -0
- package/sdk/dist/query-subprocess-adapter.d.ts.map +1 -0
- package/sdk/dist/query-subprocess-adapter.js +92 -0
- package/sdk/dist/query-subprocess-adapter.js.map +1 -0
- package/sdk/dist/query-tools-error-factory.d.ts +16 -0
- package/sdk/dist/query-tools-error-factory.d.ts.map +1 -0
- package/sdk/dist/query-tools-error-factory.js +33 -0
- package/sdk/dist/query-tools-error-factory.js.map +1 -0
- package/sdk/dist/research-gate.d.ts +24 -0
- package/sdk/dist/research-gate.d.ts.map +1 -0
- package/sdk/dist/research-gate.js +70 -0
- package/sdk/dist/research-gate.js.map +1 -0
- package/sdk/dist/runtime-gate.d.ts +14 -0
- package/sdk/dist/runtime-gate.d.ts.map +1 -0
- package/sdk/dist/runtime-gate.js +48 -0
- package/sdk/dist/runtime-gate.js.map +1 -0
- package/sdk/dist/sdd-tools-error.d.ts +23 -0
- package/sdk/dist/sdd-tools-error.d.ts.map +1 -0
- package/sdk/dist/sdd-tools-error.js +29 -0
- package/sdk/dist/sdd-tools-error.js.map +1 -0
- package/sdk/dist/sdd-tools.d.ts +97 -0
- package/sdk/dist/sdd-tools.d.ts.map +1 -0
- package/sdk/dist/sdd-tools.js +168 -0
- package/sdk/dist/sdd-tools.js.map +1 -0
- package/sdk/dist/sdd-transport-policy.d.ts +10 -0
- package/sdk/dist/sdd-transport-policy.d.ts.map +1 -0
- package/sdk/dist/sdd-transport-policy.js +32 -0
- package/sdk/dist/sdd-transport-policy.js.map +1 -0
- package/sdk/dist/sdd-transport.d.ts +39 -0
- package/sdk/dist/sdd-transport.d.ts.map +1 -0
- package/sdk/dist/sdd-transport.js +78 -0
- package/sdk/dist/sdd-transport.js.map +1 -0
- package/sdk/dist/sdk-package-compatibility.d.ts +38 -0
- package/sdk/dist/sdk-package-compatibility.d.ts.map +1 -0
- package/sdk/dist/sdk-package-compatibility.js +90 -0
- package/sdk/dist/sdk-package-compatibility.js.map +1 -0
- package/sdk/dist/session-runner.d.ts +40 -0
- package/sdk/dist/session-runner.d.ts.map +1 -0
- package/sdk/dist/session-runner.js +274 -0
- package/sdk/dist/session-runner.js.map +1 -0
- package/sdk/dist/tool-scoping.d.ts +31 -0
- package/sdk/dist/tool-scoping.d.ts.map +1 -0
- package/sdk/dist/tool-scoping.js +54 -0
- package/sdk/dist/tool-scoping.js.map +1 -0
- package/sdk/dist/types.d.ts +794 -0
- package/sdk/dist/types.d.ts.map +1 -0
- package/sdk/dist/types.js +77 -0
- package/sdk/dist/types.js.map +1 -0
- package/sdk/dist/workstream-name-policy.d.ts +13 -0
- package/sdk/dist/workstream-name-policy.d.ts.map +1 -0
- package/sdk/dist/workstream-name-policy.js +24 -0
- package/sdk/dist/workstream-name-policy.js.map +1 -0
- package/sdk/dist/workstream-utils.d.ts +15 -0
- package/sdk/dist/workstream-utils.d.ts.map +1 -0
- package/sdk/dist/workstream-utils.js +21 -0
- package/sdk/dist/workstream-utils.js.map +1 -0
- package/sdk/dist/ws-transport.d.ts +32 -0
- package/sdk/dist/ws-transport.d.ts.map +1 -0
- package/sdk/dist/ws-transport.js +84 -0
- package/sdk/dist/ws-transport.js.map +1 -0
- package/sdk/package-lock.json +2502 -0
- package/sdk/package.json +57 -0
- package/sdk/prompts/templates/project.md +186 -0
- package/sdk/prompts/templates/requirements.md +231 -0
- package/sdk/prompts/templates/research-project/ARCHITECTURE.md +204 -0
- package/sdk/prompts/templates/research-project/FEATURES.md +147 -0
- package/sdk/prompts/templates/research-project/PITFALLS.md +200 -0
- package/sdk/prompts/templates/research-project/STACK.md +120 -0
- package/sdk/prompts/templates/research-project/SUMMARY.md +170 -0
- package/sdk/prompts/templates/roadmap.md +202 -0
- package/sdk/prompts/templates/state.md +175 -0
- package/sdk/shared/model-catalog.json +122 -0
- package/sdk/src/assembled-prompts.test.ts +349 -0
- package/sdk/src/bug-3591-sddtools-runtime-workstream.test.ts +179 -0
- package/sdk/src/cli-transport.test.ts +388 -0
- package/sdk/src/cli-transport.ts +130 -0
- package/sdk/src/cli.test.ts +426 -0
- package/sdk/src/cli.ts +589 -0
- package/sdk/src/config.test.ts +271 -0
- package/sdk/src/config.ts +218 -0
- package/sdk/src/context-engine.test.ts +295 -0
- package/sdk/src/context-engine.ts +170 -0
- package/sdk/src/context-truncation.test.ts +163 -0
- package/sdk/src/context-truncation.ts +233 -0
- package/sdk/src/e2e.integration.test.ts +181 -0
- package/sdk/src/errors.ts +72 -0
- package/sdk/src/event-stream.test.ts +661 -0
- package/sdk/src/event-stream.ts +441 -0
- package/sdk/src/golden/capture.ts +95 -0
- package/sdk/src/golden/fixtures/generate-slug.golden.json +1 -0
- package/sdk/src/golden/fixtures/profile-sample-sessions/demo-project/sample.jsonl +3 -0
- package/sdk/src/golden/fixtures/summary-extract-sample.md +26 -0
- package/sdk/src/golden/fixtures/uat-render-checkpoint-sample.md +15 -0
- package/sdk/src/golden/golden-integration-covered.ts +30 -0
- package/sdk/src/golden/golden-mutation-covered.ts +17 -0
- package/sdk/src/golden/golden-policy.test.ts +8 -0
- package/sdk/src/golden/golden-policy.ts +120 -0
- package/sdk/src/golden/golden.integration.test.ts +677 -0
- package/sdk/src/golden/init-golden-normalize.ts +15 -0
- package/sdk/src/golden/read-only-golden-rows.ts +77 -0
- package/sdk/src/golden/read-only-parity.integration.test.ts +133 -0
- package/sdk/src/golden/registry-canonical-commands.ts +31 -0
- package/sdk/src/index.ts +352 -0
- package/sdk/src/init-e2e.integration.test.ts +138 -0
- package/sdk/src/init-runner.test.ts +740 -0
- package/sdk/src/init-runner.ts +734 -0
- package/sdk/src/lifecycle-e2e.integration.test.ts +258 -0
- package/sdk/src/logger.test.ts +149 -0
- package/sdk/src/logger.ts +113 -0
- package/sdk/src/milestone-runner.test.ts +421 -0
- package/sdk/src/model-catalog.ts +70 -0
- package/sdk/src/phase-prompt.test.ts +535 -0
- package/sdk/src/phase-prompt.ts +259 -0
- package/sdk/src/phase-runner-types.test.ts +421 -0
- package/sdk/src/phase-runner.integration.test.ts +377 -0
- package/sdk/src/phase-runner.test.ts +2720 -0
- package/sdk/src/phase-runner.ts +1442 -0
- package/sdk/src/plan-parser.test.ts +579 -0
- package/sdk/src/plan-parser.ts +431 -0
- package/sdk/src/planning-journal.test.ts +70 -0
- package/sdk/src/planning-journal.ts +153 -0
- package/sdk/src/planning-runtime.test.ts +29 -0
- package/sdk/src/planning-runtime.ts +100 -0
- package/sdk/src/prompt-builder.test.ts +318 -0
- package/sdk/src/prompt-builder.ts +218 -0
- package/sdk/src/prompt-sanitizer.test.ts +260 -0
- package/sdk/src/prompt-sanitizer.ts +116 -0
- package/sdk/src/query/QUERY-HANDLERS.md +349 -0
- package/sdk/src/query/active-workstream-store.ts +50 -0
- package/sdk/src/query/agent-failure-classifier.test.ts +157 -0
- package/sdk/src/query/agent-failure-classifier.ts +105 -0
- package/sdk/src/query/audit-open.ts +722 -0
- package/sdk/src/query/check-auto-mode.test.ts +77 -0
- package/sdk/src/query/check-auto-mode.ts +49 -0
- package/sdk/src/query/check-completion.test.ts +113 -0
- package/sdk/src/query/check-completion.ts +182 -0
- package/sdk/src/query/check-decision-coverage.test.ts +519 -0
- package/sdk/src/query/check-decision-coverage.ts +554 -0
- package/sdk/src/query/check-gates.test.ts +103 -0
- package/sdk/src/query/check-gates.ts +112 -0
- package/sdk/src/query/check-ship-ready.test.ts +111 -0
- package/sdk/src/query/check-ship-ready.ts +104 -0
- package/sdk/src/query/check-verification-status.test.ts +143 -0
- package/sdk/src/query/check-verification-status.ts +160 -0
- package/sdk/src/query/command-aliases.generated.ts +156 -0
- package/sdk/src/query/command-catalog.ts +31 -0
- package/sdk/src/query/command-definition.test.ts +47 -0
- package/sdk/src/query/command-definition.ts +70 -0
- package/sdk/src/query/command-family-handlers.ts +117 -0
- package/sdk/src/query/command-manifest.init.ts +24 -0
- package/sdk/src/query/command-manifest.non-family.ts +85 -0
- package/sdk/src/query/command-manifest.phase.ts +16 -0
- package/sdk/src/query/command-manifest.phases.ts +11 -0
- package/sdk/src/query/command-manifest.roadmap.ts +11 -0
- package/sdk/src/query/command-manifest.state.ts +31 -0
- package/sdk/src/query/command-manifest.ts +17 -0
- package/sdk/src/query/command-manifest.types.ts +13 -0
- package/sdk/src/query/command-manifest.validate.ts +11 -0
- package/sdk/src/query/command-manifest.verify.ts +15 -0
- package/sdk/src/query/command-resolution.test.ts +70 -0
- package/sdk/src/query/command-seam-coverage.test.ts +118 -0
- package/sdk/src/query/command-static-catalog-domain.ts +117 -0
- package/sdk/src/query/command-static-catalog-foundation.ts +103 -0
- package/sdk/src/query/command-topology.test.ts +28 -0
- package/sdk/src/query/command-topology.ts +114 -0
- package/sdk/src/query/commands-list.test.ts +36 -0
- package/sdk/src/query/commands-list.ts +19 -0
- package/sdk/src/query/commit.test.ts +485 -0
- package/sdk/src/query/commit.ts +383 -0
- package/sdk/src/query/config-gates.test.ts +89 -0
- package/sdk/src/query/config-gates.ts +69 -0
- package/sdk/src/query/config-mutation.test.ts +598 -0
- package/sdk/src/query/config-mutation.ts +575 -0
- package/sdk/src/query/config-query.test.ts +367 -0
- package/sdk/src/query/config-query.ts +244 -0
- package/sdk/src/query/config-schema.ts +159 -0
- package/sdk/src/query/decisions.test.ts +215 -0
- package/sdk/src/query/decisions.ts +192 -0
- package/sdk/src/query/decomposed-handlers.test.ts +431 -0
- package/sdk/src/query/detect-custom-files.test.ts +115 -0
- package/sdk/src/query/detect-custom-files.ts +96 -0
- package/sdk/src/query/detect-phase-type.test.ts +105 -0
- package/sdk/src/query/detect-phase-type.ts +141 -0
- package/sdk/src/query/docs-init.ts +258 -0
- package/sdk/src/query/fallow-audit.ts +88 -0
- package/sdk/src/query/frontmatter-array.test.ts +14 -0
- package/sdk/src/query/frontmatter-mutation.test.ts +259 -0
- package/sdk/src/query/frontmatter-mutation.ts +343 -0
- package/sdk/src/query/frontmatter.test.ts +326 -0
- package/sdk/src/query/frontmatter.ts +395 -0
- package/sdk/src/query/helpers.test.ts +615 -0
- package/sdk/src/query/helpers.ts +646 -0
- package/sdk/src/query/index-thin-seam.test.ts +16 -0
- package/sdk/src/query/index.ts +9 -0
- package/sdk/src/query/init-complex.test.ts +616 -0
- package/sdk/src/query/init-complex.ts +799 -0
- package/sdk/src/query/init-progress-precedence.test.ts +177 -0
- package/sdk/src/query/init-workstream-milestone-op.test.ts +321 -0
- package/sdk/src/query/init.test.ts +792 -0
- package/sdk/src/query/init.ts +1262 -0
- package/sdk/src/query/intel.test.ts +90 -0
- package/sdk/src/query/intel.ts +404 -0
- package/sdk/src/query/mutation-event-decorator.test.ts +45 -0
- package/sdk/src/query/mutation-event-decorator.ts +37 -0
- package/sdk/src/query/mutation-event-mapper.test.ts +33 -0
- package/sdk/src/query/mutation-event-mapper.ts +102 -0
- package/sdk/src/query/mvp.test.ts +335 -0
- package/sdk/src/query/mvp.ts +292 -0
- package/sdk/src/query/normalize-query-command.test.ts +102 -0
- package/sdk/src/query/phase-filesystem-adapter.ts +35 -0
- package/sdk/src/query/phase-lifecycle-policy.ts +171 -0
- package/sdk/src/query/phase-lifecycle.test.ts +1750 -0
- package/sdk/src/query/phase-lifecycle.ts +1833 -0
- package/sdk/src/query/phase-list-queries.test.ts +88 -0
- package/sdk/src/query/phase-list-queries.ts +152 -0
- package/sdk/src/query/phase-ready.test.ts +65 -0
- package/sdk/src/query/phase-ready.ts +159 -0
- package/sdk/src/query/phase-roadmap-mutation.ts +77 -0
- package/sdk/src/query/phase.test.ts +651 -0
- package/sdk/src/query/phase.ts +550 -0
- package/sdk/src/query/pipeline.test.ts +169 -0
- package/sdk/src/query/pipeline.ts +243 -0
- package/sdk/src/query/plan-scan.test.ts +35 -0
- package/sdk/src/query/plan-scan.ts +82 -0
- package/sdk/src/query/plan-task-structure.test.ts +65 -0
- package/sdk/src/query/plan-task-structure.ts +63 -0
- package/sdk/src/query/policy-convergence.test.ts +28 -0
- package/sdk/src/query/profile-extract-messages.ts +247 -0
- package/sdk/src/query/profile-output.ts +929 -0
- package/sdk/src/query/profile-questionnaire-data.ts +181 -0
- package/sdk/src/query/profile-sample.ts +184 -0
- package/sdk/src/query/profile-scan-sessions.ts +174 -0
- package/sdk/src/query/profile.test.ts +136 -0
- package/sdk/src/query/profile.ts +337 -0
- package/sdk/src/query/progress.test.ts +156 -0
- package/sdk/src/query/progress.ts +566 -0
- package/sdk/src/query/query-cli-adapter.test.ts +79 -0
- package/sdk/src/query/query-cli-adapter.ts +39 -0
- package/sdk/src/query/query-cli-output.test.ts +33 -0
- package/sdk/src/query/query-cli-output.ts +35 -0
- package/sdk/src/query/query-command-diagnosis.test.ts +22 -0
- package/sdk/src/query/query-command-diagnosis.ts +5 -0
- package/sdk/src/query/query-command-resolution-strategy.test.ts +34 -0
- package/sdk/src/query/query-command-resolution-strategy.ts +121 -0
- package/sdk/src/query/query-command-semantics.test.ts +22 -0
- package/sdk/src/query/query-command-semantics.ts +22 -0
- package/sdk/src/query/query-dispatch-contract.ts +30 -0
- package/sdk/src/query/query-dispatch-error-mapper.test.ts +62 -0
- package/sdk/src/query/query-dispatch-error-mapper.ts +5 -0
- package/sdk/src/query/query-dispatch-formatting.test.ts +28 -0
- package/sdk/src/query/query-dispatch-formatting.ts +5 -0
- package/sdk/src/query/query-dispatch-input-validation.test.ts +23 -0
- package/sdk/src/query/query-dispatch-input-validation.ts +5 -0
- package/sdk/src/query/query-dispatch-observability.test.ts +10 -0
- package/sdk/src/query/query-dispatch-observability.ts +6 -0
- package/sdk/src/query/query-dispatch-plan.test.ts +25 -0
- package/sdk/src/query/query-dispatch-plan.ts +5 -0
- package/sdk/src/query/query-dispatch-result-builder.test.ts +16 -0
- package/sdk/src/query/query-dispatch-result-builder.ts +5 -0
- package/sdk/src/query/query-dispatch.test.ts +399 -0
- package/sdk/src/query/query-dispatch.ts +243 -0
- package/sdk/src/query/query-error-details-schema.ts +29 -0
- package/sdk/src/query/query-error-taxonomy.test.ts +39 -0
- package/sdk/src/query/query-error-taxonomy.ts +117 -0
- package/sdk/src/query/query-fallback-bridge-adapter.test.ts +32 -0
- package/sdk/src/query/query-fallback-bridge-adapter.ts +54 -0
- package/sdk/src/query/query-fallback-executor.test.ts +82 -0
- package/sdk/src/query/query-fallback-executor.ts +44 -0
- package/sdk/src/query/query-fallback-output-classifier.test.ts +36 -0
- package/sdk/src/query/query-fallback-output-classifier.ts +31 -0
- package/sdk/src/query/query-fallback-policy.test.ts +13 -0
- package/sdk/src/query/query-fallback-policy.ts +11 -0
- package/sdk/src/query/query-native-dispatch-adapter.ts +16 -0
- package/sdk/src/query/query-policy-capability.test.ts +10 -0
- package/sdk/src/query/query-policy-capability.ts +26 -0
- package/sdk/src/query/query-policy-snapshot.test.ts +9 -0
- package/sdk/src/query/query-registry-capability.test.ts +14 -0
- package/sdk/src/query/query-runtime-context.ts +44 -0
- package/sdk/src/query/query-unknown-command-hints.test.ts +9 -0
- package/sdk/src/query/query-unknown-command-hints.ts +5 -0
- package/sdk/src/query/registry-assembly-descriptor.ts +87 -0
- package/sdk/src/query/registry-assembly-invariants.ts +127 -0
- package/sdk/src/query/registry-assembly.test.ts +138 -0
- package/sdk/src/query/registry-assembly.ts +78 -0
- package/sdk/src/query/registry.test.ts +208 -0
- package/sdk/src/query/registry.ts +142 -0
- package/sdk/src/query/requirements-extract-from-plans.test.ts +58 -0
- package/sdk/src/query/requirements-extract-from-plans.ts +86 -0
- package/sdk/src/query/roadmap-update-plan-progress.test.ts +233 -0
- package/sdk/src/query/roadmap-update-plan-progress.ts +159 -0
- package/sdk/src/query/roadmap.test.ts +1181 -0
- package/sdk/src/query/roadmap.ts +894 -0
- package/sdk/src/query/route-next-action.test.ts +61 -0
- package/sdk/src/query/route-next-action.ts +345 -0
- package/sdk/src/query/schema-detect.ts +189 -0
- package/sdk/src/query/secrets.test.ts +66 -0
- package/sdk/src/query/secrets.ts +43 -0
- package/sdk/src/query/skill-manifest.test.ts +62 -0
- package/sdk/src/query/skill-manifest.ts +216 -0
- package/sdk/src/query/skills.test.ts +234 -0
- package/sdk/src/query/skills.ts +143 -0
- package/sdk/src/query/state-document.test.ts +197 -0
- package/sdk/src/query/state-document.ts +129 -0
- package/sdk/src/query/state-mutation.test.ts +1198 -0
- package/sdk/src/query/state-mutation.ts +1718 -0
- package/sdk/src/query/state-project-load.ts +80 -0
- package/sdk/src/query/state.test.ts +616 -0
- package/sdk/src/query/state.ts +463 -0
- package/sdk/src/query/sub-repos-root.integration.test.ts +79 -0
- package/sdk/src/query/summary.test.ts +95 -0
- package/sdk/src/query/summary.ts +296 -0
- package/sdk/src/query/template.test.ts +180 -0
- package/sdk/src/query/template.ts +242 -0
- package/sdk/src/query/uat.test.ts +77 -0
- package/sdk/src/query/uat.ts +365 -0
- package/sdk/src/query/utils.test.ts +82 -0
- package/sdk/src/query/utils.ts +106 -0
- package/sdk/src/query/validate.test.ts +831 -0
- package/sdk/src/query/validate.ts +952 -0
- package/sdk/src/query/verify.test.ts +414 -0
- package/sdk/src/query/verify.ts +692 -0
- package/sdk/src/query/websearch.test.ts +31 -0
- package/sdk/src/query/websearch.ts +82 -0
- package/sdk/src/query/workspace.test.ts +120 -0
- package/sdk/src/query/workspace.ts +145 -0
- package/sdk/src/query/workstream-inventory.ts +195 -0
- package/sdk/src/query/workstream.test.ts +153 -0
- package/sdk/src/query/workstream.ts +324 -0
- package/sdk/src/query/worktree.ts +39 -0
- package/sdk/src/query-command-executor.ts +31 -0
- package/sdk/src/query-execution-policy.test.ts +52 -0
- package/sdk/src/query-execution-policy.ts +46 -0
- package/sdk/src/query-failure-classification.test.ts +23 -0
- package/sdk/src/query-failure-classification.ts +42 -0
- package/sdk/src/query-hotpath-methods.ts +48 -0
- package/sdk/src/query-native-direct-adapter.test.ts +35 -0
- package/sdk/src/query-native-direct-adapter.ts +70 -0
- package/sdk/src/query-native-hotpath-adapter.test.ts +43 -0
- package/sdk/src/query-native-hotpath-adapter.ts +45 -0
- package/sdk/src/query-raw-output-projection.test.ts +39 -0
- package/sdk/src/query-raw-output-projection.ts +74 -0
- package/sdk/src/query-runtime-bridge.test.ts +150 -0
- package/sdk/src/query-runtime-bridge.ts +215 -0
- package/sdk/src/query-runtime-seam-coverage.test.ts +20 -0
- package/sdk/src/query-sdd-tools-path.ts +1 -0
- package/sdk/src/query-sdd-tools-runtime.ts +89 -0
- package/sdk/src/query-subprocess-adapter.test.ts +84 -0
- package/sdk/src/query-subprocess-adapter.ts +146 -0
- package/sdk/src/query-tools-error-factory.test.ts +35 -0
- package/sdk/src/query-tools-error-factory.ts +76 -0
- package/sdk/src/research-gate.test.ts +190 -0
- package/sdk/src/research-gate.ts +94 -0
- package/sdk/src/runtime-bridge-options.test.ts +33 -0
- package/sdk/src/runtime-gate.test.ts +84 -0
- package/sdk/src/runtime-gate.ts +52 -0
- package/sdk/src/sdd-tools-error.test.ts +21 -0
- package/sdk/src/sdd-tools-error.ts +65 -0
- package/sdk/src/sdd-tools.test.ts +472 -0
- package/sdk/src/sdd-tools.ts +237 -0
- package/sdk/src/sdd-transport-policy.test.ts +34 -0
- package/sdk/src/sdd-transport-policy.ts +48 -0
- package/sdk/src/sdd-transport.test.ts +292 -0
- package/sdk/src/sdd-transport.ts +117 -0
- package/sdk/src/sdk-package-compatibility.test.ts +97 -0
- package/sdk/src/sdk-package-compatibility.ts +141 -0
- package/sdk/src/session-runner.test.ts +164 -0
- package/sdk/src/session-runner.ts +327 -0
- package/sdk/src/tool-scoping.test.ts +160 -0
- package/sdk/src/tool-scoping.ts +61 -0
- package/sdk/src/types.ts +927 -0
- package/sdk/src/workflow-agent-skills-consistency.test.ts +98 -0
- package/sdk/src/workstream-name-policy.ts +24 -0
- package/sdk/src/workstream-utils.ts +21 -0
- package/sdk/src/ws-flag.test.ts +285 -0
- package/sdk/src/ws-transport.test.ts +161 -0
- package/sdk/src/ws-transport.ts +93 -0
- package/sdk/tsconfig.json +20 -0
- package/commands/sdd/add-backlog.md +0 -76
- package/commands/sdd/add-phase.md +0 -43
- package/commands/sdd/add-todo.md +0 -47
- package/commands/sdd/check-todos.md +0 -45
- package/commands/sdd/do.md +0 -30
- package/commands/sdd/insert-phase.md +0 -32
- package/commands/sdd/join-discord.md +0 -18
- package/commands/sdd/list-phase-assumptions.md +0 -46
- package/commands/sdd/list-workspaces.md +0 -19
- package/commands/sdd/new-workspace.md +0 -44
- package/commands/sdd/next.md +0 -24
- package/commands/sdd/note.md +0 -34
- package/commands/sdd/plan-milestone-gaps.md +0 -34
- package/commands/sdd/plant-seed.md +0 -28
- package/commands/sdd/reapply-patches.md +0 -123
- package/commands/sdd/remove-phase.md +0 -31
- package/commands/sdd/remove-workspace.md +0 -26
- package/commands/sdd/research-phase.md +0 -195
- package/commands/sdd/session-report.md +0 -19
- package/commands/sdd/set-profile.md +0 -12
- package/scripts/sync-upstream.sh +0 -56
- package/sdd/commands/sdd/workstreams.md +0 -63
- package/sdd/workflows/research-phase.md +0 -82
package/sdd/bin/lib/state.cjs
CHANGED
|
@@ -4,34 +4,45 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
-
const { escapeRegex, loadConfig, getMilestoneInfo, getMilestonePhaseFilter,
|
|
7
|
+
const { escapeRegex, loadConfig, getMilestoneInfo, getMilestonePhaseFilter, output, error } = require('./core.cjs');
|
|
8
|
+
const { platformWriteSync, platformReadSync, platformEnsureDir } = require('./shell-command-projection.cjs');
|
|
9
|
+
const { planningDir, planningPaths } = require('./planning-workspace.cjs');
|
|
8
10
|
const { extractFrontmatter, reconstructFrontmatter } = require('./frontmatter.cjs');
|
|
11
|
+
const scanPhasePlans = require('./plan-scan.cjs');
|
|
12
|
+
const {
|
|
13
|
+
computeProgressPercent,
|
|
14
|
+
normalizeProgressNumbers,
|
|
15
|
+
normalizeStateStatus,
|
|
16
|
+
shouldPreserveExistingProgress,
|
|
17
|
+
stateExtractField,
|
|
18
|
+
stateReplaceField,
|
|
19
|
+
} = require('./state-document.cjs');
|
|
20
|
+
|
|
21
|
+
// Cache disk scan results from buildStateFrontmatter per cwd per process (#1967).
|
|
22
|
+
// Avoids re-reading N+1 directories on every state write when the phase structure
|
|
23
|
+
// hasn't changed within the same sdd-tools invocation.
|
|
24
|
+
const _diskScanCache = new Map();
|
|
9
25
|
|
|
10
26
|
/** Shorthand — every state command needs this path */
|
|
11
27
|
function getStatePath(cwd) {
|
|
12
28
|
return planningPaths(cwd).state;
|
|
13
29
|
}
|
|
14
30
|
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return plainMatch ? plainMatch[1].trim() : null;
|
|
25
|
-
}
|
|
31
|
+
// Track all lock files held by this process so they can be removed on exit.
|
|
32
|
+
// process.on('exit') fires even on process.exit(1), unlike try/finally which is
|
|
33
|
+
// skipped when error() calls process.exit(1) inside a locked region (#1916).
|
|
34
|
+
const _heldStateLocks = new Set();
|
|
35
|
+
process.on('exit', () => {
|
|
36
|
+
for (const lockPath of _heldStateLocks) {
|
|
37
|
+
try { require('fs').unlinkSync(lockPath); } catch { /* already gone */ }
|
|
38
|
+
}
|
|
39
|
+
});
|
|
26
40
|
|
|
27
41
|
function cmdStateLoad(cwd, raw) {
|
|
28
42
|
const config = loadConfig(cwd);
|
|
29
43
|
const planDir = planningPaths(cwd).planning;
|
|
30
44
|
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
stateRaw = fs.readFileSync(path.join(planDir, 'STATE.md'), 'utf-8');
|
|
34
|
-
} catch { /* intentionally empty */ }
|
|
45
|
+
const stateRaw = platformReadSync(path.join(planDir, 'STATE.md')) || '';
|
|
35
46
|
|
|
36
47
|
const configExists = fs.existsSync(path.join(planDir, 'config.json'));
|
|
37
48
|
const roadmapExists = fs.existsSync(path.join(planDir, 'ROADMAP.md'));
|
|
@@ -71,8 +82,12 @@ function cmdStateLoad(cwd, raw) {
|
|
|
71
82
|
|
|
72
83
|
function cmdStateGet(cwd, section, raw) {
|
|
73
84
|
const statePath = planningPaths(cwd).state;
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
const content = platformReadSync(statePath);
|
|
86
|
+
if (content === null) {
|
|
87
|
+
error('STATE.md not found');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
{
|
|
76
91
|
|
|
77
92
|
if (!section) {
|
|
78
93
|
output({ content }, raw, content);
|
|
@@ -107,8 +122,6 @@ function cmdStateGet(cwd, section, raw) {
|
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
output({ error: `Section or field "${section}" not found` }, raw, '');
|
|
110
|
-
} catch {
|
|
111
|
-
error('STATE.md not found');
|
|
112
125
|
}
|
|
113
126
|
}
|
|
114
127
|
|
|
@@ -141,29 +154,21 @@ function cmdStatePatch(cwd, patches, raw) {
|
|
|
141
154
|
|
|
142
155
|
const statePath = planningPaths(cwd).state;
|
|
143
156
|
try {
|
|
144
|
-
let content = fs.readFileSync(statePath, 'utf-8');
|
|
145
157
|
const results = { updated: [], failed: [] };
|
|
146
158
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
content = content.replace(plainPattern, (_match, prefix) => `${prefix}${value}`);
|
|
158
|
-
results.updated.push(field);
|
|
159
|
-
} else {
|
|
160
|
-
results.failed.push(field);
|
|
159
|
+
// Use atomic read-modify-write to prevent lost updates from concurrent agents
|
|
160
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
161
|
+
for (const [field, value] of Object.entries(patches)) {
|
|
162
|
+
const result = stateReplaceField(content, field, value);
|
|
163
|
+
if (result) {
|
|
164
|
+
content = result;
|
|
165
|
+
results.updated.push(field);
|
|
166
|
+
} else {
|
|
167
|
+
results.failed.push(field);
|
|
168
|
+
}
|
|
161
169
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (results.updated.length > 0) {
|
|
165
|
-
writeStateMd(statePath, content, cwd);
|
|
166
|
-
}
|
|
170
|
+
return content;
|
|
171
|
+
}, cwd);
|
|
167
172
|
|
|
168
173
|
output(results, raw, results.updated.length > 0 ? 'true' : 'false');
|
|
169
174
|
} catch {
|
|
@@ -185,18 +190,24 @@ function cmdStateUpdate(cwd, field, value) {
|
|
|
185
190
|
|
|
186
191
|
const statePath = planningPaths(cwd).state;
|
|
187
192
|
try {
|
|
188
|
-
let
|
|
189
|
-
const
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
193
|
+
let updated = false;
|
|
194
|
+
const shouldResync = ['Progress', 'Total Plans in Phase', 'Total Phases'].includes(field);
|
|
195
|
+
// Preserve curated progress for body-only updates, but allow fields that
|
|
196
|
+
// directly project into progress.* frontmatter to rebuild after mutation.
|
|
197
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
198
|
+
const body = stripFrontmatter(content);
|
|
199
|
+
const result = stateReplaceField(body, field, value);
|
|
200
|
+
if (result) {
|
|
201
|
+
updated = true;
|
|
202
|
+
const existingFm = extractFrontmatter(content);
|
|
203
|
+
if (Object.keys(existingFm).length > 0) {
|
|
204
|
+
return `---\n${reconstructFrontmatter(existingFm)}\n---\n\n${result}`;
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
return content;
|
|
209
|
+
}, cwd, { resync: shouldResync });
|
|
210
|
+
if (updated) {
|
|
200
211
|
output({ updated: true });
|
|
201
212
|
} else {
|
|
202
213
|
output({ updated: false, reason: `Field "${field}" not found in STATE.md` });
|
|
@@ -207,21 +218,6 @@ function cmdStateUpdate(cwd, field, value) {
|
|
|
207
218
|
}
|
|
208
219
|
|
|
209
220
|
// ─── State Progression Engine ────────────────────────────────────────────────
|
|
210
|
-
// stateExtractField is defined above (shared helper) — do not duplicate.
|
|
211
|
-
|
|
212
|
-
function stateReplaceField(content, fieldName, newValue) {
|
|
213
|
-
const escaped = escapeRegex(fieldName);
|
|
214
|
-
// Try **Field:** bold format first, then plain Field: format
|
|
215
|
-
const boldPattern = new RegExp(`(\\*\\*${escaped}:\\*\\*\\s*)(.*)`, 'i');
|
|
216
|
-
if (boldPattern.test(content)) {
|
|
217
|
-
return content.replace(boldPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
218
|
-
}
|
|
219
|
-
const plainPattern = new RegExp(`(^${escaped}:\\s*)(.*)`, 'im');
|
|
220
|
-
if (plainPattern.test(content)) {
|
|
221
|
-
return content.replace(plainPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
222
|
-
}
|
|
223
|
-
return null;
|
|
224
|
-
}
|
|
225
221
|
|
|
226
222
|
/**
|
|
227
223
|
* Replace a STATE.md field with fallback field name support.
|
|
@@ -236,6 +232,12 @@ function stateReplaceFieldWithFallback(content, primary, fallback, value) {
|
|
|
236
232
|
result = stateReplaceField(content, fallback, value);
|
|
237
233
|
if (result) return result;
|
|
238
234
|
}
|
|
235
|
+
// Neither pattern matched — field may have been reformatted or removed.
|
|
236
|
+
// Log diagnostic so template drift is detected early rather than silently swallowed.
|
|
237
|
+
process.stderr.write(
|
|
238
|
+
`[sdd-tools] WARNING: STATE.md field "${primary}"${fallback ? ` (fallback: "${fallback}")` : ''} not found — update skipped. ` +
|
|
239
|
+
`This may indicate STATE.md was externally modified or uses an unexpected format.\n`
|
|
240
|
+
);
|
|
239
241
|
return content;
|
|
240
242
|
}
|
|
241
243
|
|
|
@@ -262,62 +264,74 @@ function updateCurrentPositionFields(content, fields) {
|
|
|
262
264
|
posBody = posBody.replace(/^Plan:.*$/m, `Plan: ${fields.plan}`);
|
|
263
265
|
}
|
|
264
266
|
|
|
265
|
-
return content.replace(posPattern, `${posMatch[1]}${posBody}`);
|
|
267
|
+
return content.replace(posPattern, () => `${posMatch[1]}${posBody}`);
|
|
266
268
|
}
|
|
267
269
|
|
|
268
270
|
function cmdStateAdvancePlan(cwd, raw) {
|
|
269
271
|
const statePath = planningPaths(cwd).state;
|
|
270
272
|
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
271
273
|
|
|
272
|
-
let content = fs.readFileSync(statePath, 'utf-8');
|
|
273
274
|
const today = new Date().toISOString().split('T')[0];
|
|
275
|
+
let result = null;
|
|
276
|
+
|
|
277
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
278
|
+
// Try legacy separate fields first, then compound "Plan: X of Y" format
|
|
279
|
+
const legacyPlan = stateExtractField(content, 'Current Plan');
|
|
280
|
+
const legacyTotal = stateExtractField(content, 'Total Plans in Phase');
|
|
281
|
+
const planField = stateExtractField(content, 'Plan');
|
|
282
|
+
|
|
283
|
+
let currentPlan, totalPlans;
|
|
284
|
+
let useCompoundFormat = false;
|
|
285
|
+
|
|
286
|
+
if (legacyPlan && legacyTotal) {
|
|
287
|
+
currentPlan = parseInt(legacyPlan, 10);
|
|
288
|
+
totalPlans = parseInt(legacyTotal, 10);
|
|
289
|
+
} else if (planField) {
|
|
290
|
+
// Compound format: "2 of 6 in current phase" or "2 of 6"
|
|
291
|
+
currentPlan = parseInt(planField, 10);
|
|
292
|
+
const ofMatch = planField.match(/of\s+(\d+)/);
|
|
293
|
+
totalPlans = ofMatch ? parseInt(ofMatch[1], 10) : NaN;
|
|
294
|
+
useCompoundFormat = true;
|
|
295
|
+
}
|
|
274
296
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
let currentPlan, totalPlans;
|
|
281
|
-
let useCompoundFormat = false;
|
|
297
|
+
if (isNaN(currentPlan) || isNaN(totalPlans)) {
|
|
298
|
+
result = { error: true };
|
|
299
|
+
return content;
|
|
300
|
+
}
|
|
282
301
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
302
|
+
if (currentPlan >= totalPlans) {
|
|
303
|
+
content = stateReplaceFieldWithFallback(content, 'Status', null, 'Phase complete — ready for verification');
|
|
304
|
+
content = stateReplaceFieldWithFallback(content, 'Last Activity', 'Last activity', today);
|
|
305
|
+
content = updateCurrentPositionFields(content, { status: 'Phase complete — ready for verification', lastActivity: today });
|
|
306
|
+
result = { advanced: false, reason: 'last_plan', current_plan: currentPlan, total_plans: totalPlans, status: 'ready_for_verification' };
|
|
307
|
+
} else {
|
|
308
|
+
const newPlan = currentPlan + 1;
|
|
309
|
+
let planDisplayValue;
|
|
310
|
+
if (useCompoundFormat) {
|
|
311
|
+
// Preserve compound format: "X of Y in current phase" → replace X only
|
|
312
|
+
planDisplayValue = planField.replace(/^\d+/, String(newPlan));
|
|
313
|
+
content = stateReplaceField(content, 'Plan', planDisplayValue) || content;
|
|
314
|
+
} else {
|
|
315
|
+
planDisplayValue = `${newPlan} of ${totalPlans}`;
|
|
316
|
+
content = stateReplaceField(content, 'Current Plan', String(newPlan)) || content;
|
|
317
|
+
}
|
|
318
|
+
content = stateReplaceFieldWithFallback(content, 'Status', null, 'Ready to execute');
|
|
319
|
+
content = stateReplaceFieldWithFallback(content, 'Last Activity', 'Last activity', today);
|
|
320
|
+
content = updateCurrentPositionFields(content, { status: 'Ready to execute', lastActivity: today, plan: planDisplayValue });
|
|
321
|
+
result = { advanced: true, previous_plan: currentPlan, current_plan: newPlan, total_plans: totalPlans };
|
|
322
|
+
}
|
|
323
|
+
return content;
|
|
324
|
+
}, cwd);
|
|
293
325
|
|
|
294
|
-
if (
|
|
326
|
+
if (!result || result.error) {
|
|
295
327
|
output({ error: 'Cannot parse Current Plan or Total Plans in Phase from STATE.md' }, raw);
|
|
296
328
|
return;
|
|
297
329
|
}
|
|
298
330
|
|
|
299
|
-
if (
|
|
300
|
-
|
|
301
|
-
content = stateReplaceFieldWithFallback(content, 'Last Activity', 'Last activity', today);
|
|
302
|
-
content = updateCurrentPositionFields(content, { status: 'Phase complete — ready for verification', lastActivity: today });
|
|
303
|
-
writeStateMd(statePath, content, cwd);
|
|
304
|
-
output({ advanced: false, reason: 'last_plan', current_plan: currentPlan, total_plans: totalPlans, status: 'ready_for_verification' }, raw, 'false');
|
|
331
|
+
if (result.advanced === false) {
|
|
332
|
+
output(result, raw, 'false');
|
|
305
333
|
} else {
|
|
306
|
-
|
|
307
|
-
let planDisplayValue;
|
|
308
|
-
if (useCompoundFormat) {
|
|
309
|
-
// Preserve compound format: "X of Y in current phase" → replace X only
|
|
310
|
-
planDisplayValue = planField.replace(/^\d+/, String(newPlan));
|
|
311
|
-
content = stateReplaceField(content, 'Plan', planDisplayValue) || content;
|
|
312
|
-
} else {
|
|
313
|
-
planDisplayValue = `${newPlan} of ${totalPlans}`;
|
|
314
|
-
content = stateReplaceField(content, 'Current Plan', String(newPlan)) || content;
|
|
315
|
-
}
|
|
316
|
-
content = stateReplaceFieldWithFallback(content, 'Status', null, 'Ready to execute');
|
|
317
|
-
content = stateReplaceFieldWithFallback(content, 'Last Activity', 'Last activity', today);
|
|
318
|
-
content = updateCurrentPositionFields(content, { status: 'Ready to execute', lastActivity: today, plan: planDisplayValue });
|
|
319
|
-
writeStateMd(statePath, content, cwd);
|
|
320
|
-
output({ advanced: true, previous_plan: currentPlan, current_plan: newPlan, total_plans: totalPlans }, raw, 'true');
|
|
334
|
+
output(result, raw, 'true');
|
|
321
335
|
}
|
|
322
336
|
}
|
|
323
337
|
|
|
@@ -325,7 +339,6 @@ function cmdStateRecordMetric(cwd, options, raw) {
|
|
|
325
339
|
const statePath = planningPaths(cwd).state;
|
|
326
340
|
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
327
341
|
|
|
328
|
-
let content = fs.readFileSync(statePath, 'utf-8');
|
|
329
342
|
const { phase, plan, duration, tasks, files } = options;
|
|
330
343
|
|
|
331
344
|
if (!phase || !plan || !duration) {
|
|
@@ -333,35 +346,55 @@ function cmdStateRecordMetric(cwd, options, raw) {
|
|
|
333
346
|
return;
|
|
334
347
|
}
|
|
335
348
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
349
|
+
let recorded = false;
|
|
350
|
+
let created = false;
|
|
351
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
352
|
+
// Find Performance Metrics section and its table
|
|
353
|
+
const metricsPattern = /(##\s*Performance Metrics[\s\S]*?\n\|[^\n]+\n\|[-|\s]+\n)([\s\S]*?)(?=\n##|\n$|$)/i;
|
|
354
|
+
const metricsMatch = content.match(metricsPattern);
|
|
339
355
|
|
|
340
|
-
if (metricsMatch) {
|
|
341
|
-
let tableBody = metricsMatch[2].trimEnd();
|
|
342
356
|
const newRow = `| Phase ${phase} P${plan} | ${duration} | ${tasks || '-'} tasks | ${files || '-'} files |`;
|
|
343
357
|
|
|
344
|
-
if (
|
|
345
|
-
tableBody =
|
|
346
|
-
|
|
347
|
-
tableBody
|
|
358
|
+
if (metricsMatch) {
|
|
359
|
+
let tableBody = metricsMatch[2].trimEnd();
|
|
360
|
+
|
|
361
|
+
if (tableBody.trim() === '' || tableBody.includes('None yet')) {
|
|
362
|
+
tableBody = newRow;
|
|
363
|
+
} else {
|
|
364
|
+
tableBody = tableBody + '\n' + newRow;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
recorded = true;
|
|
368
|
+
return content.replace(metricsPattern, (_match, header) => `${header}${tableBody}\n`);
|
|
348
369
|
}
|
|
349
370
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
371
|
+
// Section absent — DWIM: auto-create canonical ## Performance Metrics scaffold,
|
|
372
|
+
// then append the row. Matches state begin-phase / advance-plan DWIM behavior.
|
|
373
|
+
const scaffold = [
|
|
374
|
+
'',
|
|
375
|
+
'## Performance Metrics',
|
|
376
|
+
'',
|
|
377
|
+
'| Phase | Plan | Duration | Notes |',
|
|
378
|
+
'|-------|------|----------|-------|',
|
|
379
|
+
newRow,
|
|
380
|
+
'',
|
|
381
|
+
].join('\n');
|
|
382
|
+
recorded = true;
|
|
383
|
+
created = true;
|
|
384
|
+
return content.trimEnd() + '\n' + scaffold;
|
|
385
|
+
}, cwd);
|
|
386
|
+
|
|
387
|
+
// Auto-create fallback guarantees recorded === true; no else branch needed.
|
|
388
|
+
const result = { recorded: true, phase, plan, duration };
|
|
389
|
+
if (created) result.created = true;
|
|
390
|
+
output(result, raw, 'true');
|
|
356
391
|
}
|
|
357
392
|
|
|
358
393
|
function cmdStateUpdateProgress(cwd, raw) {
|
|
359
394
|
const statePath = planningPaths(cwd).state;
|
|
360
395
|
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
361
396
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
// Count summaries across current milestone phases only
|
|
397
|
+
// Count summaries across current milestone phases only (outside lock — read-only)
|
|
365
398
|
const phasesDir = planningPaths(cwd).phases;
|
|
366
399
|
let totalPlans = 0;
|
|
367
400
|
let totalSummaries = 0;
|
|
@@ -372,9 +405,9 @@ function cmdStateUpdateProgress(cwd, raw) {
|
|
|
372
405
|
.filter(e => e.isDirectory()).map(e => e.name)
|
|
373
406
|
.filter(isDirInMilestone);
|
|
374
407
|
for (const dir of phaseDirs) {
|
|
375
|
-
const
|
|
376
|
-
totalPlans +=
|
|
377
|
-
totalSummaries +=
|
|
408
|
+
const { planCount, summaryCount } = scanPhasePlans(path.join(phasesDir, dir));
|
|
409
|
+
totalPlans += planCount;
|
|
410
|
+
totalSummaries += summaryCount;
|
|
378
411
|
}
|
|
379
412
|
}
|
|
380
413
|
|
|
@@ -384,17 +417,26 @@ function cmdStateUpdateProgress(cwd, raw) {
|
|
|
384
417
|
const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(barWidth - filled);
|
|
385
418
|
const progressStr = `[${bar}] ${percent}%`;
|
|
386
419
|
|
|
387
|
-
|
|
388
|
-
const
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
420
|
+
let updated = false;
|
|
421
|
+
const _totalPlans = totalPlans;
|
|
422
|
+
const _totalSummaries = totalSummaries;
|
|
423
|
+
|
|
424
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
425
|
+
// Try **Progress:** bold format first, then plain Progress: format
|
|
426
|
+
const boldProgressPattern = /(\*\*Progress:\*\*\s*).*/i;
|
|
427
|
+
const plainProgressPattern = /^(Progress:\s*).*/im;
|
|
428
|
+
if (boldProgressPattern.test(content)) {
|
|
429
|
+
updated = true;
|
|
430
|
+
return content.replace(boldProgressPattern, (_match, prefix) => `${prefix}${progressStr}`);
|
|
431
|
+
} else if (plainProgressPattern.test(content)) {
|
|
432
|
+
updated = true;
|
|
433
|
+
return content.replace(plainProgressPattern, (_match, prefix) => `${prefix}${progressStr}`);
|
|
434
|
+
}
|
|
435
|
+
return content;
|
|
436
|
+
}, cwd);
|
|
437
|
+
|
|
438
|
+
if (updated) {
|
|
439
|
+
output({ updated: true, percent, completed: _totalSummaries, total: _totalPlans, bar: progressStr }, raw, progressStr);
|
|
398
440
|
} else {
|
|
399
441
|
output({ updated: false, reason: 'Progress field not found in STATE.md' }, raw, 'false');
|
|
400
442
|
}
|
|
@@ -418,24 +460,42 @@ function cmdStateAddDecision(cwd, options, raw) {
|
|
|
418
460
|
|
|
419
461
|
if (!summaryText) { output({ error: 'summary required' }, raw); return; }
|
|
420
462
|
|
|
421
|
-
let content = fs.readFileSync(statePath, 'utf-8');
|
|
422
463
|
const entry = `- [Phase ${phase || '?'}]: ${summaryText}${rationaleText ? ` — ${rationaleText}` : ''}`;
|
|
464
|
+
let added = false;
|
|
465
|
+
let created = false;
|
|
466
|
+
|
|
467
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
468
|
+
// Find Decisions section (various heading patterns)
|
|
469
|
+
const sectionPattern = /(###?\s*(?:Decisions|Decisions Made|Accumulated.*Decisions)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
|
|
470
|
+
const match = content.match(sectionPattern);
|
|
471
|
+
|
|
472
|
+
if (match) {
|
|
473
|
+
let sectionBody = match[2];
|
|
474
|
+
// Remove placeholders
|
|
475
|
+
sectionBody = sectionBody.replace(/None yet\.?\s*\n?/gi, '').replace(/No decisions yet\.?\s*\n?/gi, '');
|
|
476
|
+
sectionBody = sectionBody.trimEnd() + '\n' + entry + '\n';
|
|
477
|
+
added = true;
|
|
478
|
+
return content.replace(sectionPattern, (_match, header) => `${header}${sectionBody}`);
|
|
479
|
+
}
|
|
423
480
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
|
|
481
|
+
// Section absent — DWIM: auto-create canonical ## Decisions scaffold,
|
|
482
|
+
// then append the entry. Matches state begin-phase / advance-plan DWIM behavior.
|
|
483
|
+
const scaffold = [
|
|
484
|
+
'',
|
|
485
|
+
'## Decisions',
|
|
486
|
+
'',
|
|
487
|
+
entry,
|
|
488
|
+
'',
|
|
489
|
+
].join('\n');
|
|
490
|
+
added = true;
|
|
491
|
+
created = true;
|
|
492
|
+
return content.trimEnd() + '\n' + scaffold;
|
|
493
|
+
}, cwd);
|
|
494
|
+
|
|
495
|
+
// Auto-create fallback guarantees added === true; no else branch needed.
|
|
496
|
+
const result = { added: true, decision: entry };
|
|
497
|
+
if (created) result.created = true;
|
|
498
|
+
output(result, raw, 'true');
|
|
439
499
|
}
|
|
440
500
|
|
|
441
501
|
function cmdStateAddBlocker(cwd, text, raw) {
|
|
@@ -453,22 +513,39 @@ function cmdStateAddBlocker(cwd, text, raw) {
|
|
|
453
513
|
|
|
454
514
|
if (!blockerText) { output({ error: 'text required' }, raw); return; }
|
|
455
515
|
|
|
456
|
-
let content = fs.readFileSync(statePath, 'utf-8');
|
|
457
516
|
const entry = `- ${blockerText}`;
|
|
517
|
+
let added = false;
|
|
518
|
+
let created = false;
|
|
519
|
+
|
|
520
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
521
|
+
const sectionPattern = /(###?\s*(?:Blockers|Blockers\/Concerns|Concerns)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
|
|
522
|
+
const match = content.match(sectionPattern);
|
|
523
|
+
|
|
524
|
+
if (match) {
|
|
525
|
+
let sectionBody = match[2];
|
|
526
|
+
sectionBody = sectionBody.replace(/None\.?\s*\n?/gi, '').replace(/None yet\.?\s*\n?/gi, '');
|
|
527
|
+
sectionBody = sectionBody.trimEnd() + '\n' + entry + '\n';
|
|
528
|
+
added = true;
|
|
529
|
+
return content.replace(sectionPattern, (_match, header) => `${header}${sectionBody}`);
|
|
530
|
+
}
|
|
458
531
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
532
|
+
// Section absent — DWIM: auto-create canonical ### Blockers scaffold.
|
|
533
|
+
const scaffold = [
|
|
534
|
+
'',
|
|
535
|
+
'### Blockers',
|
|
536
|
+
'',
|
|
537
|
+
entry,
|
|
538
|
+
'',
|
|
539
|
+
].join('\n');
|
|
540
|
+
added = true;
|
|
541
|
+
created = true;
|
|
542
|
+
return content.trimEnd() + '\n' + scaffold;
|
|
543
|
+
}, cwd);
|
|
544
|
+
|
|
545
|
+
// Auto-create fallback guarantees added === true; no else branch needed.
|
|
546
|
+
const result = { added: true, blocker: blockerText };
|
|
547
|
+
if (created) result.created = true;
|
|
548
|
+
output(result, raw, 'true');
|
|
472
549
|
}
|
|
473
550
|
|
|
474
551
|
function cmdStateResolveBlocker(cwd, text, raw) {
|
|
@@ -476,27 +553,33 @@ function cmdStateResolveBlocker(cwd, text, raw) {
|
|
|
476
553
|
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
477
554
|
if (!text) { output({ error: 'text required' }, raw); return; }
|
|
478
555
|
|
|
479
|
-
let
|
|
556
|
+
let resolved = false;
|
|
557
|
+
|
|
558
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
559
|
+
const sectionPattern = /(###?\s*(?:Blockers|Blockers\/Concerns|Concerns)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
|
|
560
|
+
const match = content.match(sectionPattern);
|
|
561
|
+
|
|
562
|
+
if (match) {
|
|
563
|
+
const sectionBody = match[2];
|
|
564
|
+
const lines = sectionBody.split('\n');
|
|
565
|
+
const filtered = lines.filter(line => {
|
|
566
|
+
if (!line.startsWith('- ')) return true;
|
|
567
|
+
return !line.toLowerCase().includes(text.toLowerCase());
|
|
568
|
+
});
|
|
480
569
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
return !line.toLowerCase().includes(text.toLowerCase());
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
let newBody = filtered.join('\n');
|
|
493
|
-
// If section is now empty, add placeholder
|
|
494
|
-
if (!newBody.trim() || !newBody.includes('- ')) {
|
|
495
|
-
newBody = 'None\n';
|
|
570
|
+
let newBody = filtered.join('\n');
|
|
571
|
+
// If section is now empty, add placeholder
|
|
572
|
+
if (!newBody.trim() || !newBody.includes('- ')) {
|
|
573
|
+
newBody = 'None\n';
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
resolved = true;
|
|
577
|
+
return content.replace(sectionPattern, (_match, header) => `${header}${newBody}`);
|
|
496
578
|
}
|
|
579
|
+
return content;
|
|
580
|
+
}, cwd);
|
|
497
581
|
|
|
498
|
-
|
|
499
|
-
writeStateMd(statePath, content, cwd);
|
|
582
|
+
if (resolved) {
|
|
500
583
|
output({ resolved: true, blocker: text }, raw, 'true');
|
|
501
584
|
} else {
|
|
502
585
|
output({ resolved: false, reason: 'Blockers section not found in STATE.md' }, raw, 'false');
|
|
@@ -507,31 +590,33 @@ function cmdStateRecordSession(cwd, options, raw) {
|
|
|
507
590
|
const statePath = planningPaths(cwd).state;
|
|
508
591
|
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
509
592
|
|
|
510
|
-
let content = fs.readFileSync(statePath, 'utf-8');
|
|
511
593
|
const now = new Date().toISOString();
|
|
512
594
|
const updated = [];
|
|
513
595
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
596
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
597
|
+
// Update Last session / Last Date
|
|
598
|
+
let result = stateReplaceField(content, 'Last session', now);
|
|
599
|
+
if (result) { content = result; updated.push('Last session'); }
|
|
600
|
+
result = stateReplaceField(content, 'Last Date', now);
|
|
601
|
+
if (result) { content = result; updated.push('Last Date'); }
|
|
602
|
+
|
|
603
|
+
// Update Stopped at
|
|
604
|
+
if (options.stopped_at) {
|
|
605
|
+
result = stateReplaceField(content, 'Stopped At', options.stopped_at);
|
|
606
|
+
if (!result) result = stateReplaceField(content, 'Stopped at', options.stopped_at);
|
|
607
|
+
if (result) { content = result; updated.push('Stopped At'); }
|
|
608
|
+
}
|
|
519
609
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
result = stateReplaceField(content, '
|
|
523
|
-
if (!result) result = stateReplaceField(content, '
|
|
524
|
-
if (result) { content = result; updated.push('
|
|
525
|
-
}
|
|
610
|
+
// Update Resume file
|
|
611
|
+
const resumeFile = options.resume_file || 'None';
|
|
612
|
+
result = stateReplaceField(content, 'Resume File', resumeFile);
|
|
613
|
+
if (!result) result = stateReplaceField(content, 'Resume file', resumeFile);
|
|
614
|
+
if (result) { content = result; updated.push('Resume File'); }
|
|
526
615
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
result = stateReplaceField(content, 'Resume File', resumeFile);
|
|
530
|
-
if (!result) result = stateReplaceField(content, 'Resume file', resumeFile);
|
|
531
|
-
if (result) { content = result; updated.push('Resume File'); }
|
|
616
|
+
return content;
|
|
617
|
+
}, cwd);
|
|
532
618
|
|
|
533
619
|
if (updated.length > 0) {
|
|
534
|
-
writeStateMd(statePath, content, cwd);
|
|
535
620
|
output({ recorded: true, updated }, raw, 'true');
|
|
536
621
|
} else {
|
|
537
622
|
output({ recorded: false, reason: 'No session fields found in STATE.md' }, raw, 'false');
|
|
@@ -548,17 +633,36 @@ function cmdStateSnapshot(cwd, raw) {
|
|
|
548
633
|
|
|
549
634
|
const content = fs.readFileSync(statePath, 'utf-8');
|
|
550
635
|
|
|
551
|
-
//
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
const
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
636
|
+
// Bug #3265: prefer YAML frontmatter for canonical scalar fields so that a
|
|
637
|
+
// body table cell containing **Status:** Y cannot shadow the authoritative
|
|
638
|
+
// frontmatter value. Mirrors the fix in sdk/src/query/state.ts.
|
|
639
|
+
const fm = extractFrontmatter(content);
|
|
640
|
+
const body = stripFrontmatter(content);
|
|
641
|
+
|
|
642
|
+
// Helper: return frontmatter scalar value when present and non-empty.
|
|
643
|
+
// Accepts strings, numbers, and booleans — coercing non-string primitives to
|
|
644
|
+
// their string representation so callers always receive string | null.
|
|
645
|
+
// Returns null for missing, null/undefined, or empty-after-trim values so
|
|
646
|
+
// the caller falls back to body extraction.
|
|
647
|
+
const fmScalar = (key) => {
|
|
648
|
+
const v = fm[key];
|
|
649
|
+
if (v === null || v === undefined) return null;
|
|
650
|
+
if (typeof v === 'string') return v.trim() || null;
|
|
651
|
+
if (typeof v === 'number' || typeof v === 'boolean') return String(v);
|
|
652
|
+
return null;
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
// Extract basic fields — frontmatter keys take precedence over body
|
|
656
|
+
const currentPhase = fmScalar('current_phase') ?? stateExtractField(body, 'Current Phase');
|
|
657
|
+
const currentPhaseName = fmScalar('current_phase_name') ?? stateExtractField(body, 'Current Phase Name');
|
|
658
|
+
const totalPhasesRaw = fmScalar('total_phases') ?? stateExtractField(body, 'Total Phases');
|
|
659
|
+
const currentPlan = fmScalar('current_plan') ?? stateExtractField(body, 'Current Plan');
|
|
660
|
+
const totalPlansRaw = fmScalar('total_plans_in_phase') ?? stateExtractField(body, 'Total Plans in Phase');
|
|
661
|
+
const status = fmScalar('status') ?? stateExtractField(body, 'Status');
|
|
662
|
+
const progressRaw = fmScalar('progress') ?? stateExtractField(body, 'Progress');
|
|
663
|
+
const lastActivity = fmScalar('last_activity') ?? stateExtractField(body, 'Last Activity');
|
|
664
|
+
const lastActivityDesc = fmScalar('last_activity_desc') ?? stateExtractField(body, 'Last Activity Description');
|
|
665
|
+
const pausedAt = fmScalar('paused_at') ?? stateExtractField(body, 'Paused At');
|
|
562
666
|
|
|
563
667
|
// Parse numeric fields
|
|
564
668
|
const totalPhases = totalPhasesRaw ? parseInt(totalPhasesRaw, 10) : null;
|
|
@@ -567,7 +671,7 @@ function cmdStateSnapshot(cwd, raw) {
|
|
|
567
671
|
|
|
568
672
|
// Extract decisions table
|
|
569
673
|
const decisions = [];
|
|
570
|
-
const decisionsMatch =
|
|
674
|
+
const decisionsMatch = body.match(/##\s*Decisions Made[\s\S]*?\n\|[^\n]+\n\|[-|\s]+\n([\s\S]*?)(?=\n##|\n$|$)/i);
|
|
571
675
|
if (decisionsMatch) {
|
|
572
676
|
const tableBody = decisionsMatch[1];
|
|
573
677
|
const rows = tableBody.trim().split('\n').filter(r => r.includes('|'));
|
|
@@ -585,7 +689,7 @@ function cmdStateSnapshot(cwd, raw) {
|
|
|
585
689
|
|
|
586
690
|
// Extract blockers list
|
|
587
691
|
const blockers = [];
|
|
588
|
-
const blockersMatch =
|
|
692
|
+
const blockersMatch = body.match(/##\s*Blockers\s*\n([\s\S]*?)(?=\n##|$)/i);
|
|
589
693
|
if (blockersMatch) {
|
|
590
694
|
const blockersSection = blockersMatch[1];
|
|
591
695
|
const items = blockersSection.match(/^-\s+(.+)$/gm) || [];
|
|
@@ -601,7 +705,7 @@ function cmdStateSnapshot(cwd, raw) {
|
|
|
601
705
|
resume_file: null,
|
|
602
706
|
};
|
|
603
707
|
|
|
604
|
-
const sessionMatch =
|
|
708
|
+
const sessionMatch = body.match(/##\s*Session\s*\n([\s\S]*?)(?=\n##|$)/i);
|
|
605
709
|
if (sessionMatch) {
|
|
606
710
|
const sessionSection = sessionMatch[1];
|
|
607
711
|
const lastDateMatch = sessionSection.match(/\*\*Last Date:\*\*\s*(.+)/i)
|
|
@@ -651,7 +755,13 @@ function buildStateFrontmatter(bodyContent, cwd) {
|
|
|
651
755
|
const status = stateExtractField(bodyContent, 'Status');
|
|
652
756
|
const progressRaw = stateExtractField(bodyContent, 'Progress');
|
|
653
757
|
const lastActivity = stateExtractField(bodyContent, 'Last Activity');
|
|
654
|
-
|
|
758
|
+
// Bug #2444: scope Stopped At extraction to the ## Session section so that
|
|
759
|
+
// historical "Stopped at:" prose elsewhere in the body (e.g. in a
|
|
760
|
+
// Session Continuity Archive section) never overwrites the current value.
|
|
761
|
+
// Fall back to full-body search only when no ## Session section exists.
|
|
762
|
+
const sessionSectionMatch = bodyContent.match(/##\s*Session\s*\n([\s\S]*?)(?=\n##|$)/i);
|
|
763
|
+
const sessionBodyScope = sessionSectionMatch ? sessionSectionMatch[1] : bodyContent;
|
|
764
|
+
const stoppedAt = stateExtractField(sessionBodyScope, 'Stopped At') || stateExtractField(sessionBodyScope, 'Stopped at');
|
|
655
765
|
const pausedAt = stateExtractField(bodyContent, 'Paused At');
|
|
656
766
|
|
|
657
767
|
let milestone = null;
|
|
@@ -673,56 +783,79 @@ function buildStateFrontmatter(bodyContent, cwd) {
|
|
|
673
783
|
try {
|
|
674
784
|
const phasesDir = planningPaths(cwd).phases;
|
|
675
785
|
if (fs.existsSync(phasesDir)) {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
786
|
+
// Use cached disk scan when available — avoids N+1 readdirSync calls
|
|
787
|
+
// on repeated buildStateFrontmatter invocations within the same process (#1967)
|
|
788
|
+
let cached = _diskScanCache.get(cwd);
|
|
789
|
+
if (!cached) {
|
|
790
|
+
const isDirInMilestone = getMilestonePhaseFilter(cwd);
|
|
791
|
+
const allMatchingDirs = fs.readdirSync(phasesDir, { withFileTypes: true })
|
|
792
|
+
.filter(e => e.isDirectory()).map(e => e.name)
|
|
793
|
+
.filter(isDirInMilestone);
|
|
794
|
+
|
|
795
|
+
// Bug #2445: when stale phase dirs from a prior milestone remain in
|
|
796
|
+
// .planning/phases/ alongside new dirs with the same phase number,
|
|
797
|
+
// de-duplicate by normalized phase number keeping the most recently
|
|
798
|
+
// modified dir. This prevents double-counting (e.g. two "Phase 1" dirs).
|
|
799
|
+
const seenPhaseNums = new Map(); // normalizedNum -> dirName
|
|
800
|
+
for (const dir of allMatchingDirs) {
|
|
801
|
+
const m = dir.match(/^0*(\d+[A-Za-z]?(?:\.\d+)*)/);
|
|
802
|
+
const key = m ? m[1].toLowerCase() : dir;
|
|
803
|
+
if (!seenPhaseNums.has(key)) {
|
|
804
|
+
seenPhaseNums.set(key, dir);
|
|
805
|
+
} else {
|
|
806
|
+
// Keep the dir that is newer on disk (more likely current milestone)
|
|
807
|
+
try {
|
|
808
|
+
const existing = path.join(phasesDir, seenPhaseNums.get(key));
|
|
809
|
+
const candidate = path.join(phasesDir, dir);
|
|
810
|
+
if (fs.statSync(candidate).mtimeMs > fs.statSync(existing).mtimeMs) {
|
|
811
|
+
seenPhaseNums.set(key, dir);
|
|
812
|
+
}
|
|
813
|
+
} catch { /* keep existing on stat error */ }
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
const phaseDirs = [...seenPhaseNums.values()];
|
|
817
|
+
|
|
818
|
+
let diskTotalPlans = 0;
|
|
819
|
+
let diskTotalSummaries = 0;
|
|
820
|
+
let diskCompletedPhases = 0;
|
|
821
|
+
|
|
822
|
+
for (const dir of phaseDirs) {
|
|
823
|
+
const phaseDir = path.join(phasesDir, dir);
|
|
824
|
+
const { planCount, summaryCount, completed } = scanPhasePlans(phaseDir);
|
|
825
|
+
diskTotalPlans += planCount;
|
|
826
|
+
diskTotalSummaries += summaryCount;
|
|
827
|
+
if (completed) diskCompletedPhases++;
|
|
828
|
+
}
|
|
829
|
+
cached = {
|
|
830
|
+
totalPhases: isDirInMilestone.phaseCount > 0
|
|
831
|
+
? Math.max(phaseDirs.length, isDirInMilestone.phaseCount)
|
|
832
|
+
: phaseDirs.length,
|
|
833
|
+
completedPhases: diskCompletedPhases,
|
|
834
|
+
totalPlans: diskTotalPlans,
|
|
835
|
+
completedPlans: diskTotalSummaries,
|
|
836
|
+
};
|
|
837
|
+
_diskScanCache.set(cwd, cached);
|
|
691
838
|
}
|
|
692
|
-
totalPhases =
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
totalPlans = diskTotalPlans;
|
|
697
|
-
completedPlans = diskTotalSummaries;
|
|
839
|
+
totalPhases = cached.totalPhases;
|
|
840
|
+
completedPhases = cached.completedPhases;
|
|
841
|
+
totalPlans = cached.totalPlans;
|
|
842
|
+
completedPlans = cached.completedPlans;
|
|
698
843
|
}
|
|
699
844
|
} catch { /* intentionally empty */ }
|
|
700
845
|
}
|
|
701
846
|
|
|
702
|
-
|
|
703
|
-
|
|
847
|
+
// Derive percent from disk counts when available (ground truth).
|
|
848
|
+
// Uses min(plan_fraction, phase_fraction) via computeProgressPercent so that
|
|
849
|
+
// ROADMAP-declared-but-unrealized future phases cap the reported completion
|
|
850
|
+
// instead of a false 100% from plan-only coverage (#3242 Bug B).
|
|
851
|
+
// Falls back to the body Progress: field only when no plan files exist on disk.
|
|
852
|
+
let progressPercent = computeProgressPercent(completedPlans, totalPlans, completedPhases, totalPhases);
|
|
853
|
+
if (progressPercent === null && progressRaw) {
|
|
704
854
|
const pctMatch = progressRaw.match(/(\d+)%/);
|
|
705
855
|
if (pctMatch) progressPercent = parseInt(pctMatch[1], 10);
|
|
706
856
|
}
|
|
707
857
|
|
|
708
|
-
|
|
709
|
-
let normalizedStatus = status || 'unknown';
|
|
710
|
-
const statusLower = (status || '').toLowerCase();
|
|
711
|
-
if (statusLower.includes('paused') || statusLower.includes('stopped') || pausedAt) {
|
|
712
|
-
normalizedStatus = 'paused';
|
|
713
|
-
} else if (statusLower.includes('executing') || statusLower.includes('in progress')) {
|
|
714
|
-
normalizedStatus = 'executing';
|
|
715
|
-
} else if (statusLower.includes('planning') || statusLower.includes('ready to plan')) {
|
|
716
|
-
normalizedStatus = 'planning';
|
|
717
|
-
} else if (statusLower.includes('discussing')) {
|
|
718
|
-
normalizedStatus = 'discussing';
|
|
719
|
-
} else if (statusLower.includes('verif')) {
|
|
720
|
-
normalizedStatus = 'verifying';
|
|
721
|
-
} else if (statusLower.includes('complete') || statusLower.includes('done')) {
|
|
722
|
-
normalizedStatus = 'completed';
|
|
723
|
-
} else if (statusLower.includes('ready to execute')) {
|
|
724
|
-
normalizedStatus = 'executing';
|
|
725
|
-
}
|
|
858
|
+
const normalizedStatus = normalizeStateStatus(status, pausedAt);
|
|
726
859
|
|
|
727
860
|
const fm = { sdd_state_version: '1.0' };
|
|
728
861
|
|
|
@@ -781,55 +914,117 @@ function syncStateFrontmatter(content, cwd) {
|
|
|
781
914
|
}
|
|
782
915
|
|
|
783
916
|
/**
|
|
784
|
-
*
|
|
785
|
-
*
|
|
786
|
-
* Uses a simple lockfile to prevent parallel agents from overwriting
|
|
787
|
-
* each other's changes (race condition with read-modify-write cycle).
|
|
917
|
+
* Acquire a lockfile for STATE.md operations.
|
|
918
|
+
* Returns the lock path for later release.
|
|
788
919
|
*/
|
|
789
|
-
function
|
|
790
|
-
const synced = syncStateFrontmatter(content, cwd);
|
|
920
|
+
function acquireStateLock(statePath) {
|
|
791
921
|
const lockPath = statePath + '.lock';
|
|
792
922
|
const maxRetries = 10;
|
|
793
923
|
const retryDelay = 200; // ms
|
|
794
924
|
|
|
795
|
-
// Acquire lock (spin with backoff)
|
|
796
925
|
for (let i = 0; i < maxRetries; i++) {
|
|
797
926
|
try {
|
|
798
|
-
// O_EXCL fails if file already exists — atomic lock
|
|
799
927
|
const fd = fs.openSync(lockPath, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY);
|
|
800
928
|
fs.writeSync(fd, String(process.pid));
|
|
801
929
|
fs.closeSync(fd);
|
|
802
|
-
|
|
930
|
+
// Register for exit-time cleanup so process.exit(1) inside a locked region
|
|
931
|
+
// cannot leave a stale lock file (#1916).
|
|
932
|
+
_heldStateLocks.add(lockPath);
|
|
933
|
+
return lockPath;
|
|
803
934
|
} catch (err) {
|
|
804
935
|
if (err.code === 'EEXIST') {
|
|
805
|
-
// Check for stale lock (> 10s old)
|
|
806
936
|
try {
|
|
807
937
|
const stat = fs.statSync(lockPath);
|
|
808
938
|
if (Date.now() - stat.mtimeMs > 10000) {
|
|
809
939
|
fs.unlinkSync(lockPath);
|
|
810
|
-
continue;
|
|
940
|
+
continue;
|
|
811
941
|
}
|
|
812
942
|
} catch { /* lock was released between check — retry */ }
|
|
813
943
|
|
|
814
944
|
if (i === maxRetries - 1) {
|
|
815
|
-
// Last resort: write anyway rather than losing data
|
|
816
945
|
try { fs.unlinkSync(lockPath); } catch {}
|
|
817
|
-
|
|
946
|
+
return lockPath;
|
|
818
947
|
}
|
|
819
|
-
// Spin-wait with small jitter
|
|
820
948
|
const jitter = Math.floor(Math.random() * 50);
|
|
821
|
-
|
|
822
|
-
while (Date.now() - start < retryDelay + jitter) { /* busy wait */ }
|
|
949
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, retryDelay + jitter);
|
|
823
950
|
continue;
|
|
824
951
|
}
|
|
825
|
-
|
|
952
|
+
return lockPath; // non-EEXIST error — proceed without lock
|
|
826
953
|
}
|
|
827
954
|
}
|
|
955
|
+
return statePath + '.lock';
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function releaseStateLock(lockPath) {
|
|
959
|
+
_heldStateLocks.delete(lockPath);
|
|
960
|
+
try { fs.unlinkSync(lockPath); } catch { /* lock already gone */ }
|
|
961
|
+
}
|
|
828
962
|
|
|
963
|
+
/**
|
|
964
|
+
* Write STATE.md with synchronized YAML frontmatter.
|
|
965
|
+
* All STATE.md writes should use this instead of raw writeFileSync.
|
|
966
|
+
* Uses a simple lockfile to prevent parallel agents from overwriting
|
|
967
|
+
* each other's changes (race condition with read-modify-write cycle).
|
|
968
|
+
*/
|
|
969
|
+
function writeStateMd(statePath, content, cwd) {
|
|
970
|
+
// Invalidate disk scan cache before computing new frontmatter — the write
|
|
971
|
+
// may create new PLAN/SUMMARY files that buildStateFrontmatter must see.
|
|
972
|
+
// Safe for any calling pattern, not just short-lived CLI processes (#1967).
|
|
973
|
+
if (cwd) _diskScanCache.delete(cwd);
|
|
974
|
+
const synced = syncStateFrontmatter(content, cwd);
|
|
975
|
+
const lockPath = acquireStateLock(statePath);
|
|
829
976
|
try {
|
|
830
|
-
|
|
977
|
+
platformWriteSync(statePath, synced);
|
|
831
978
|
} finally {
|
|
832
|
-
|
|
979
|
+
releaseStateLock(lockPath);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Atomic read-modify-write for STATE.md.
|
|
985
|
+
* Holds the lock across the entire read -> transform -> write cycle,
|
|
986
|
+
* preventing the lost-update problem where two agents read the same
|
|
987
|
+
* content and the second write clobbers the first.
|
|
988
|
+
*
|
|
989
|
+
* @param {string} statePath
|
|
990
|
+
* @param {function} transformFn - (content: string) => string
|
|
991
|
+
* @param {string} cwd
|
|
992
|
+
* @param {{ resync?: boolean }} [options]
|
|
993
|
+
* resync: when true (default) rebuilds the entire frontmatter from disk after
|
|
994
|
+
* the transform. Pass { resync: false } for body-only updates (e.g. state.update
|
|
995
|
+
* on a single field) that must not trample manually-curated cross-milestone
|
|
996
|
+
* progress.* counters in the frontmatter (#3242 Bug A).
|
|
997
|
+
* When resync is false, syncStateFrontmatter still runs to maintain/create the
|
|
998
|
+
* frontmatter block, but any existing progress.* sub-keys are preserved from
|
|
999
|
+
* the pre-transform file rather than being rebuilt from disk.
|
|
1000
|
+
*/
|
|
1001
|
+
function readModifyWriteStateMd(statePath, transformFn, cwd, options) {
|
|
1002
|
+
const resync = !options || options.resync !== false;
|
|
1003
|
+
const lockPath = acquireStateLock(statePath);
|
|
1004
|
+
try {
|
|
1005
|
+
const content = platformReadSync(statePath) || '';
|
|
1006
|
+
// Snapshot the existing progress block BEFORE the transform so we can
|
|
1007
|
+
// restore it when resync is false.
|
|
1008
|
+
const preFm = resync ? null : extractFrontmatter(content);
|
|
1009
|
+
const modified = transformFn(content);
|
|
1010
|
+
let synced = syncStateFrontmatter(modified, cwd);
|
|
1011
|
+
|
|
1012
|
+
if (!resync && preFm && preFm.progress) {
|
|
1013
|
+
// Re-apply the curated progress block that syncStateFrontmatter just
|
|
1014
|
+
// overwrote with disk-derived values. Only restore keys that were present
|
|
1015
|
+
// in the snapshot — this preserves any new non-progress frontmatter fields
|
|
1016
|
+
// (e.g., status, current_phase) that syncStateFrontmatter legitimately
|
|
1017
|
+
// derived from the updated body.
|
|
1018
|
+
const postFm = extractFrontmatter(synced);
|
|
1019
|
+
postFm.progress = preFm.progress;
|
|
1020
|
+
const yamlStr = reconstructFrontmatter(postFm);
|
|
1021
|
+
const body = stripFrontmatter(synced);
|
|
1022
|
+
synced = `---\n${yamlStr}\n---\n\n${body}`;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
platformWriteSync(statePath, synced);
|
|
1026
|
+
} finally {
|
|
1027
|
+
releaseStateLock(lockPath);
|
|
833
1028
|
}
|
|
834
1029
|
}
|
|
835
1030
|
|
|
@@ -841,16 +1036,33 @@ function cmdStateJson(cwd, raw) {
|
|
|
841
1036
|
}
|
|
842
1037
|
|
|
843
1038
|
const content = fs.readFileSync(statePath, 'utf-8');
|
|
844
|
-
const
|
|
1039
|
+
const existingFm = extractFrontmatter(content);
|
|
1040
|
+
const body = stripFrontmatter(content);
|
|
845
1041
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1042
|
+
// Always rebuild from body + disk so progress counters reflect current state.
|
|
1043
|
+
// Returning cached frontmatter directly causes stale percent/completed_plans
|
|
1044
|
+
// when SUMMARY files were added after the last STATE.md write (#1589).
|
|
1045
|
+
const built = buildStateFrontmatter(body, cwd);
|
|
1046
|
+
|
|
1047
|
+
// Preserve frontmatter-only fields that cannot be recovered from the body.
|
|
1048
|
+
if (existingFm && existingFm.stopped_at && !built.stopped_at) {
|
|
1049
|
+
built.stopped_at = existingFm.stopped_at;
|
|
1050
|
+
}
|
|
1051
|
+
if (existingFm && existingFm.paused_at && !built.paused_at) {
|
|
1052
|
+
built.paused_at = existingFm.paused_at;
|
|
1053
|
+
}
|
|
1054
|
+
// Preserve existing status when body-derived status is 'unknown' (same logic as syncStateFrontmatter).
|
|
1055
|
+
if (built.status === 'unknown' && existingFm && existingFm.status && existingFm.status !== 'unknown') {
|
|
1056
|
+
built.status = existingFm.status;
|
|
1057
|
+
}
|
|
1058
|
+
// Preserve curated cross-milestone aggregates when local disk scanning sees
|
|
1059
|
+
// only a narrower realized subset (#3242 Bug A). Stale lower counters still
|
|
1060
|
+
// rebuild from disk because they do not exceed the derived scan.
|
|
1061
|
+
if (existingFm && shouldPreserveExistingProgress(existingFm.progress, built.progress)) {
|
|
1062
|
+
built.progress = normalizeProgressNumbers(existingFm.progress);
|
|
851
1063
|
}
|
|
852
1064
|
|
|
853
|
-
output(
|
|
1065
|
+
output(built, raw, JSON.stringify(built, null, 2));
|
|
854
1066
|
}
|
|
855
1067
|
|
|
856
1068
|
/**
|
|
@@ -866,96 +1078,121 @@ function cmdStateBeginPhase(cwd, phaseNumber, phaseName, planCount, raw) {
|
|
|
866
1078
|
return;
|
|
867
1079
|
}
|
|
868
1080
|
|
|
869
|
-
let content = fs.readFileSync(statePath, 'utf-8');
|
|
870
1081
|
const today = new Date().toISOString().split('T')[0];
|
|
871
1082
|
const updated = [];
|
|
872
1083
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1084
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
1085
|
+
// Idempotency guard (#3127): if the phase is already mid-flight, do NOT
|
|
1086
|
+
// overwrite execution-progress fields (Current Plan, plan body line,
|
|
1087
|
+
// Last Activity Description). Only update fields that are safe to
|
|
1088
|
+
// refresh on resume (Last Activity date, Status if inconsistent).
|
|
1089
|
+
// A phase is considered mid-flight when Status contains 'Executing Phase N'
|
|
1090
|
+
// for the current phase number.
|
|
1091
|
+
const currentStatus = stateExtractField(content, 'Status') || '';
|
|
1092
|
+
const isAlreadyExecuting = new RegExp(`Executing Phase\\s+${escapeRegex(String(phaseNumber))}\\b`, 'i').test(currentStatus);
|
|
1093
|
+
|
|
1094
|
+
// Update Status field
|
|
1095
|
+
const statusValue = `Executing Phase ${phaseNumber}`;
|
|
1096
|
+
let result = stateReplaceField(content, 'Status', statusValue);
|
|
1097
|
+
if (result) { content = result; updated.push('Status'); }
|
|
1098
|
+
|
|
1099
|
+
// Update Last Activity (safe to update on resume — tracks when execute-phase ran)
|
|
1100
|
+
result = stateReplaceField(content, 'Last Activity', today);
|
|
1101
|
+
if (result) { content = result; updated.push('Last Activity'); }
|
|
1102
|
+
|
|
1103
|
+
if (!isAlreadyExecuting) {
|
|
1104
|
+
// First-time execution: set all progress fields
|
|
1105
|
+
|
|
1106
|
+
// Update Last Activity Description
|
|
1107
|
+
const activityDesc = `Phase ${phaseNumber} execution started`;
|
|
1108
|
+
result = stateReplaceField(content, 'Last Activity Description', activityDesc);
|
|
1109
|
+
if (result) { content = result; updated.push('Last Activity Description'); }
|
|
1110
|
+
|
|
1111
|
+
// Update Current Phase
|
|
1112
|
+
result = stateReplaceField(content, 'Current Phase', String(phaseNumber));
|
|
1113
|
+
if (result) { content = result; updated.push('Current Phase'); }
|
|
1114
|
+
|
|
1115
|
+
// Update Current Phase Name
|
|
1116
|
+
if (phaseName) {
|
|
1117
|
+
result = stateReplaceField(content, 'Current Phase Name', phaseName);
|
|
1118
|
+
if (result) { content = result; updated.push('Current Phase Name'); }
|
|
1119
|
+
}
|
|
886
1120
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1121
|
+
// Update Current Plan to 1 (starting from the first plan)
|
|
1122
|
+
result = stateReplaceField(content, 'Current Plan', '1');
|
|
1123
|
+
if (result) { content = result; updated.push('Current Plan'); }
|
|
890
1124
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
1125
|
+
// Update Total Plans in Phase
|
|
1126
|
+
if (planCount) {
|
|
1127
|
+
result = stateReplaceField(content, 'Total Plans in Phase', String(planCount));
|
|
1128
|
+
if (result) { content = result; updated.push('Total Plans in Phase'); }
|
|
1129
|
+
}
|
|
896
1130
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1131
|
+
// Update **Current focus:** body text line (#1104)
|
|
1132
|
+
const focusLabel = phaseName ? `Phase ${phaseNumber} — ${phaseName}` : `Phase ${phaseNumber}`;
|
|
1133
|
+
const focusPattern = /(\*\*Current focus:\*\*\s*).*/i;
|
|
1134
|
+
if (focusPattern.test(content)) {
|
|
1135
|
+
content = content.replace(focusPattern, (_match, prefix) => `${prefix}${focusLabel}`);
|
|
1136
|
+
updated.push('Current focus');
|
|
1137
|
+
}
|
|
900
1138
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1139
|
+
// Update ## Current Position section (#1104, #1365)
|
|
1140
|
+
const positionPattern = /(##\s*Current Position\s*\n)([\s\S]*?)(?=\n##|$)/i;
|
|
1141
|
+
const positionMatch = content.match(positionPattern);
|
|
1142
|
+
if (positionMatch) {
|
|
1143
|
+
const header = positionMatch[1];
|
|
1144
|
+
let posBody = positionMatch[2];
|
|
1145
|
+
|
|
1146
|
+
// Update or insert Phase line
|
|
1147
|
+
const newPhase = `Phase: ${phaseNumber}${phaseName ? ` (${phaseName})` : ''} — EXECUTING`;
|
|
1148
|
+
if (/^Phase:/m.test(posBody)) {
|
|
1149
|
+
posBody = posBody.replace(/^Phase:.*$/m, newPhase);
|
|
1150
|
+
} else {
|
|
1151
|
+
posBody = newPhase + '\n' + posBody;
|
|
1152
|
+
}
|
|
906
1153
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1154
|
+
// Update or insert Plan line
|
|
1155
|
+
const newPlan = `Plan: 1 of ${planCount || '?'}`;
|
|
1156
|
+
if (/^Plan:/m.test(posBody)) {
|
|
1157
|
+
posBody = posBody.replace(/^Plan:.*$/m, newPlan);
|
|
1158
|
+
} else {
|
|
1159
|
+
posBody = posBody.replace(/^(Phase:.*$)/m, `$1\n${newPlan}`);
|
|
1160
|
+
}
|
|
914
1161
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
if (positionMatch) {
|
|
921
|
-
const header = positionMatch[1];
|
|
922
|
-
let posBody = positionMatch[2];
|
|
1162
|
+
// Update Status line if present
|
|
1163
|
+
const newStatus = `Status: Executing Phase ${phaseNumber}`;
|
|
1164
|
+
if (/^Status:/m.test(posBody)) {
|
|
1165
|
+
posBody = posBody.replace(/^Status:.*$/m, newStatus);
|
|
1166
|
+
}
|
|
923
1167
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
posBody = newPhase + '\n' + posBody;
|
|
930
|
-
}
|
|
1168
|
+
// Update Last activity line if present
|
|
1169
|
+
const newActivity = `Last activity: ${today} -- Phase ${phaseNumber} execution started`;
|
|
1170
|
+
if (/^Last activity:/im.test(posBody)) {
|
|
1171
|
+
posBody = posBody.replace(/^Last activity:.*$/im, newActivity);
|
|
1172
|
+
}
|
|
931
1173
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
posBody = posBody.replace(/^Plan:.*$/m, newPlan);
|
|
1174
|
+
content = content.replace(positionPattern, () => `${header}${posBody}`);
|
|
1175
|
+
updated.push('Current Position');
|
|
1176
|
+
}
|
|
936
1177
|
} else {
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1178
|
+
// Resume path: only update Last activity timestamp in Current Position
|
|
1179
|
+
// (do not touch Plan:, stopped_at, progress.percent, or plan counter)
|
|
1180
|
+
const positionPattern = /(##\s*Current Position\s*\n)([\s\S]*?)(?=\n##|$)/i;
|
|
1181
|
+
const positionMatch = content.match(positionPattern);
|
|
1182
|
+
if (positionMatch) {
|
|
1183
|
+
const header = positionMatch[1];
|
|
1184
|
+
let posBody = positionMatch[2];
|
|
1185
|
+
const resumeActivity = `Last activity: ${today} -- Phase ${phaseNumber} execution resumed (wave continue)`;
|
|
1186
|
+
if (/^Last activity:/im.test(posBody)) {
|
|
1187
|
+
posBody = posBody.replace(/^Last activity:.*$/im, resumeActivity);
|
|
1188
|
+
content = content.replace(positionPattern, () => `${header}${posBody}`);
|
|
1189
|
+
updated.push('Last activity (resume)');
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
950
1192
|
}
|
|
951
1193
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
if (updated.length > 0) {
|
|
957
|
-
writeStateMd(statePath, content, cwd);
|
|
958
|
-
}
|
|
1194
|
+
return content;
|
|
1195
|
+
}, cwd);
|
|
959
1196
|
|
|
960
1197
|
output({ updated, phase: phaseNumber, phase_name: phaseName || null, plan_count: planCount || null }, raw, updated.length > 0 ? 'true' : 'false');
|
|
961
1198
|
}
|
|
@@ -980,8 +1217,8 @@ function cmdSignalWaiting(cwd, type, question, options, phase, raw) {
|
|
|
980
1217
|
};
|
|
981
1218
|
|
|
982
1219
|
try {
|
|
983
|
-
|
|
984
|
-
|
|
1220
|
+
platformEnsureDir(sddDir);
|
|
1221
|
+
platformWriteSync(waitingPath, JSON.stringify(signal, null, 2));
|
|
985
1222
|
output({ signaled: true, path: waitingPath }, raw, 'true');
|
|
986
1223
|
} catch (e) {
|
|
987
1224
|
output({ signaled: false, error: e.message }, raw, 'false');
|
|
@@ -1007,11 +1244,654 @@ function cmdSignalResume(cwd, raw) {
|
|
|
1007
1244
|
output({ resumed: true, removed }, raw, removed ? 'true' : 'false');
|
|
1008
1245
|
}
|
|
1009
1246
|
|
|
1247
|
+
// ─── Gate Functions (STATE.md consistency enforcement) ────────────────────────
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* Update the ## Performance Metrics section in STATE.md content.
|
|
1251
|
+
* Increments Velocity totals and upserts a By Phase table row.
|
|
1252
|
+
* Returns modified content string.
|
|
1253
|
+
*/
|
|
1254
|
+
function updatePerformanceMetricsSection(content, cwd, phaseNum, planCount, summaryCount) {
|
|
1255
|
+
// Update Velocity: Total plans completed
|
|
1256
|
+
const totalMatch = content.match(/Total plans completed:\s*(\d+|\[N\])/);
|
|
1257
|
+
const prevTotal = totalMatch && totalMatch[1] !== '[N]' ? parseInt(totalMatch[1], 10) : 0;
|
|
1258
|
+
const newTotal = prevTotal + summaryCount;
|
|
1259
|
+
content = content.replace(
|
|
1260
|
+
/Total plans completed:\s*(\d+|\[N\])/,
|
|
1261
|
+
`Total plans completed: ${newTotal}`
|
|
1262
|
+
);
|
|
1263
|
+
|
|
1264
|
+
// Update By Phase table — upsert row for this phase
|
|
1265
|
+
const byPhaseTablePattern = /(\|\s*Phase\s*\|\s*Plans\s*\|\s*Total\s*\|\s*Avg\/Plan\s*\|[ \t]*\n\|(?:[- :\t]+\|)+[ \t]*\n)((?:[ \t]*\|[^\n]*\n)*)(?=\n|$)/i;
|
|
1266
|
+
const byPhaseMatch = content.match(byPhaseTablePattern);
|
|
1267
|
+
if (byPhaseMatch) {
|
|
1268
|
+
let tableBody = byPhaseMatch[2].trim();
|
|
1269
|
+
const phaseRowPattern = new RegExp(`^\\|\\s*${escapeRegex(String(phaseNum))}\\s*\\|.*$`, 'm');
|
|
1270
|
+
const newRow = `| ${phaseNum} | ${summaryCount} | - | - |`;
|
|
1271
|
+
|
|
1272
|
+
if (phaseRowPattern.test(tableBody)) {
|
|
1273
|
+
// Update existing row
|
|
1274
|
+
tableBody = tableBody.replace(phaseRowPattern, newRow);
|
|
1275
|
+
} else {
|
|
1276
|
+
// Remove placeholder row and add new row
|
|
1277
|
+
tableBody = tableBody.replace(/^\|\s*-\s*\|\s*-\s*\|\s*-\s*\|\s*-\s*\|$/m, '').trim();
|
|
1278
|
+
tableBody = tableBody ? tableBody + '\n' + newRow : newRow;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
content = content.replace(byPhaseTablePattern, (_match, tableHeader) => `${tableHeader}${tableBody}\n`);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
return content;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
/**
|
|
1288
|
+
* Gate 3a: Record state after plan-phase completes.
|
|
1289
|
+
* Updates Status to "Ready to execute", Total Plans, Last Activity.
|
|
1290
|
+
*/
|
|
1291
|
+
function cmdStatePlannedPhase(cwd, phaseNumber, planCount, raw) {
|
|
1292
|
+
const statePath = planningPaths(cwd).state;
|
|
1293
|
+
if (!fs.existsSync(statePath)) {
|
|
1294
|
+
output({ error: 'STATE.md not found' }, raw);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
let content = fs.readFileSync(statePath, 'utf-8');
|
|
1299
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1300
|
+
const updated = [];
|
|
1301
|
+
|
|
1302
|
+
// Update Status
|
|
1303
|
+
let result = stateReplaceField(content, 'Status', 'Ready to execute');
|
|
1304
|
+
if (result) { content = result; updated.push('Status'); }
|
|
1305
|
+
|
|
1306
|
+
// Update Total Plans in Phase
|
|
1307
|
+
if (planCount !== null && planCount !== undefined) {
|
|
1308
|
+
result = stateReplaceField(content, 'Total Plans in Phase', String(planCount));
|
|
1309
|
+
if (result) { content = result; updated.push('Total Plans in Phase'); }
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// Update Last Activity
|
|
1313
|
+
result = stateReplaceField(content, 'Last Activity', today);
|
|
1314
|
+
if (result) { content = result; updated.push('Last Activity'); }
|
|
1315
|
+
|
|
1316
|
+
// Update Last Activity Description
|
|
1317
|
+
result = stateReplaceField(content, 'Last Activity Description', `Phase ${phaseNumber} planning complete — ${planCount || '?'} plans ready`);
|
|
1318
|
+
if (result) { content = result; updated.push('Last Activity Description'); }
|
|
1319
|
+
|
|
1320
|
+
// Update Current Position section
|
|
1321
|
+
content = updateCurrentPositionFields(content, {
|
|
1322
|
+
status: 'Ready to execute',
|
|
1323
|
+
lastActivity: `${today} -- Phase ${phaseNumber} planning complete`,
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1326
|
+
if (updated.length > 0) {
|
|
1327
|
+
writeStateMd(statePath, content, cwd);
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
output({ updated, phase: phaseNumber, plan_count: planCount }, raw, updated.length > 0 ? 'true' : 'false');
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* Bug #2630: reset STATE.md for a new milestone cycle.
|
|
1335
|
+
* Stomps frontmatter milestone/milestone_name/status/progress AND rewrites
|
|
1336
|
+
* the Current Position body. Preserves Accumulated Context.
|
|
1337
|
+
* Symmetric with the SDK `stateMilestoneSwitch` handler.
|
|
1338
|
+
*/
|
|
1339
|
+
function cmdStateMilestoneSwitch(cwd, version, name, raw) {
|
|
1340
|
+
if (!version || !String(version).trim()) {
|
|
1341
|
+
output({ error: 'milestone required (--milestone <vX.Y>)' }, raw);
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
const resolvedName = (name && String(name).trim()) || 'milestone';
|
|
1345
|
+
const statePath = planningPaths(cwd).state;
|
|
1346
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1347
|
+
|
|
1348
|
+
const lockPath = acquireStateLock(statePath);
|
|
1349
|
+
try {
|
|
1350
|
+
const content = platformReadSync(statePath) || '';
|
|
1351
|
+
const existingFm = extractFrontmatter(content);
|
|
1352
|
+
const body = stripFrontmatter(content);
|
|
1353
|
+
|
|
1354
|
+
const positionPattern = /(##\s*Current Position\s*\n)([\s\S]*?)(?=\n##|$)/i;
|
|
1355
|
+
const resetPositionBody =
|
|
1356
|
+
`\nPhase: Not started (defining requirements)\n` +
|
|
1357
|
+
`Plan: —\n` +
|
|
1358
|
+
`Status: Defining requirements\n` +
|
|
1359
|
+
`Last activity: ${today} — Milestone ${version} started\n\n`;
|
|
1360
|
+
let newBody;
|
|
1361
|
+
if (positionPattern.test(body)) {
|
|
1362
|
+
newBody = body.replace(positionPattern, (_m, header) => `${header}${resetPositionBody}`);
|
|
1363
|
+
} else {
|
|
1364
|
+
const preface = body.trim().length > 0 ? body : '# Project State\n';
|
|
1365
|
+
newBody = `${preface.trimEnd()}\n\n## Current Position\n${resetPositionBody}`;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
const fm = {
|
|
1369
|
+
sdd_state_version: existingFm.sdd_state_version || '1.0',
|
|
1370
|
+
milestone: version,
|
|
1371
|
+
milestone_name: resolvedName,
|
|
1372
|
+
status: 'planning',
|
|
1373
|
+
last_updated: new Date().toISOString(),
|
|
1374
|
+
last_activity: today,
|
|
1375
|
+
progress: {
|
|
1376
|
+
total_phases: 0,
|
|
1377
|
+
completed_phases: 0,
|
|
1378
|
+
total_plans: 0,
|
|
1379
|
+
completed_plans: 0,
|
|
1380
|
+
percent: 0,
|
|
1381
|
+
},
|
|
1382
|
+
};
|
|
1383
|
+
|
|
1384
|
+
const yamlStr = reconstructFrontmatter(fm);
|
|
1385
|
+
const assembled = `---\n${yamlStr}\n---\n\n${newBody.replace(/^\n+/, '')}`;
|
|
1386
|
+
platformWriteSync(statePath, assembled);
|
|
1387
|
+
output(
|
|
1388
|
+
{ switched: true, version, name: resolvedName, status: 'planning' },
|
|
1389
|
+
raw,
|
|
1390
|
+
'true',
|
|
1391
|
+
);
|
|
1392
|
+
} finally {
|
|
1393
|
+
releaseStateLock(lockPath);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
/**
|
|
1398
|
+
* Gate 1: Validate STATE.md against filesystem.
|
|
1399
|
+
* Returns { valid, warnings, drift } JSON.
|
|
1400
|
+
*/
|
|
1401
|
+
function cmdStateValidate(cwd, raw) {
|
|
1402
|
+
const statePath = planningPaths(cwd).state;
|
|
1403
|
+
if (!fs.existsSync(statePath)) {
|
|
1404
|
+
output({ error: 'STATE.md not found' }, raw);
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
const content = fs.readFileSync(statePath, 'utf-8');
|
|
1409
|
+
const warnings = [];
|
|
1410
|
+
const drift = {};
|
|
1411
|
+
|
|
1412
|
+
const status = stateExtractField(content, 'Status') || '';
|
|
1413
|
+
const currentPhase = stateExtractField(content, 'Current Phase');
|
|
1414
|
+
const totalPlansRaw = stateExtractField(content, 'Total Plans in Phase');
|
|
1415
|
+
const totalPlansInPhase = totalPlansRaw ? parseInt(totalPlansRaw, 10) : null;
|
|
1416
|
+
|
|
1417
|
+
const phasesDir = planningPaths(cwd).phases;
|
|
1418
|
+
|
|
1419
|
+
// Scan disk for current phase
|
|
1420
|
+
if (currentPhase && fs.existsSync(phasesDir)) {
|
|
1421
|
+
const normalized = currentPhase.replace(/\s+of\s+\d+.*/, '').trim();
|
|
1422
|
+
try {
|
|
1423
|
+
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
|
1424
|
+
const phaseDir = entries.find(e => e.isDirectory() && e.name.startsWith(normalized.replace(/^0+/, '').padStart(2, '0')));
|
|
1425
|
+
if (phaseDir) {
|
|
1426
|
+
const phaseDirPath = path.join(phasesDir, phaseDir.name);
|
|
1427
|
+
const { planCount: diskPlans, summaryCount: diskSummaries } = scanPhasePlans(phaseDirPath);
|
|
1428
|
+
|
|
1429
|
+
// Check plan count mismatch
|
|
1430
|
+
if (totalPlansInPhase !== null && diskPlans !== totalPlansInPhase) {
|
|
1431
|
+
warnings.push(`Plan count mismatch: STATE.md says ${totalPlansInPhase} plans, disk has ${diskPlans}`);
|
|
1432
|
+
drift.plan_count = { state: totalPlansInPhase, disk: diskPlans };
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// Check for VERIFICATION.md
|
|
1436
|
+
const files = fs.readdirSync(phaseDirPath);
|
|
1437
|
+
const verificationFiles = files.filter(f => f.includes('VERIFICATION') && f.endsWith('.md'));
|
|
1438
|
+
for (const vf of verificationFiles) {
|
|
1439
|
+
try {
|
|
1440
|
+
const vContent = fs.readFileSync(path.join(phaseDirPath, vf), 'utf-8');
|
|
1441
|
+
if (/status:\s*passed/i.test(vContent) && /executing/i.test(status)) {
|
|
1442
|
+
warnings.push(`Status drift: STATE.md says "${status}" but ${vf} shows verification passed — phase may be complete`);
|
|
1443
|
+
drift.verification_status = { state_status: status, verification: 'passed' };
|
|
1444
|
+
}
|
|
1445
|
+
} catch { /* intentionally empty */ }
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// Check if all plans have summaries but status still says executing
|
|
1449
|
+
if (diskPlans > 0 && diskSummaries >= diskPlans && /executing/i.test(status)) {
|
|
1450
|
+
// Only warn if no verification exists (if verification passed, the above warning covers it)
|
|
1451
|
+
if (verificationFiles.length === 0) {
|
|
1452
|
+
warnings.push(`All ${diskPlans} plans have summaries but status is still "${status}" — phase may be ready for verification`);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
} catch { /* intentionally empty */ }
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
const valid = warnings.length === 0;
|
|
1460
|
+
output({ valid, warnings, drift }, raw);
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* Gate 2: Sync STATE.md from filesystem ground truth.
|
|
1465
|
+
* Scans phase dirs, reconstructs counters, progress, metrics.
|
|
1466
|
+
* Supports --verify for dry-run mode.
|
|
1467
|
+
*/
|
|
1468
|
+
function cmdStateSync(cwd, options, raw) {
|
|
1469
|
+
const statePath = planningPaths(cwd).state;
|
|
1470
|
+
if (!fs.existsSync(statePath)) {
|
|
1471
|
+
output({ error: 'STATE.md not found' }, raw);
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
const verify = options && options.verify;
|
|
1476
|
+
const content = fs.readFileSync(statePath, 'utf-8');
|
|
1477
|
+
const changes = [];
|
|
1478
|
+
let modified = content;
|
|
1479
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1480
|
+
|
|
1481
|
+
const phasesDir = planningPaths(cwd).phases;
|
|
1482
|
+
if (!fs.existsSync(phasesDir)) {
|
|
1483
|
+
output({ synced: true, changes: [], dry_run: !!verify }, raw);
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
// Scan all phases
|
|
1488
|
+
let entries;
|
|
1489
|
+
try {
|
|
1490
|
+
entries = fs.readdirSync(phasesDir, { withFileTypes: true })
|
|
1491
|
+
.filter(e => e.isDirectory())
|
|
1492
|
+
.map(e => e.name)
|
|
1493
|
+
.sort();
|
|
1494
|
+
} catch {
|
|
1495
|
+
output({ synced: true, changes: [], dry_run: !!verify }, raw);
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
let totalDiskPlans = 0;
|
|
1500
|
+
let totalDiskSummaries = 0;
|
|
1501
|
+
let diskCompletedPhases = 0;
|
|
1502
|
+
let highestIncompletePhase = null;
|
|
1503
|
+
let highestIncompletePhaseNum = null;
|
|
1504
|
+
let highestIncompletePhaseplanCount = 0;
|
|
1505
|
+
let highestIncompletePhaseSummaryCount = 0;
|
|
1506
|
+
|
|
1507
|
+
for (const dir of entries) {
|
|
1508
|
+
const dirPath = path.join(phasesDir, dir);
|
|
1509
|
+
const { planCount: plans, summaryCount: summaries, completed } = scanPhasePlans(dirPath);
|
|
1510
|
+
totalDiskPlans += plans;
|
|
1511
|
+
totalDiskSummaries += summaries;
|
|
1512
|
+
if (completed) diskCompletedPhases++;
|
|
1513
|
+
|
|
1514
|
+
// Track the highest phase with incomplete plans (or any plans)
|
|
1515
|
+
const phaseMatch = dir.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);
|
|
1516
|
+
if (phaseMatch && plans > 0) {
|
|
1517
|
+
if (summaries < plans) {
|
|
1518
|
+
// Incomplete phase — this is likely the current one
|
|
1519
|
+
highestIncompletePhase = dir;
|
|
1520
|
+
highestIncompletePhaseNum = phaseMatch[1];
|
|
1521
|
+
highestIncompletePhaseplanCount = plans;
|
|
1522
|
+
highestIncompletePhaseSummaryCount = summaries;
|
|
1523
|
+
} else if (!highestIncompletePhase) {
|
|
1524
|
+
// All complete, track as potential current
|
|
1525
|
+
highestIncompletePhase = dir;
|
|
1526
|
+
highestIncompletePhaseNum = phaseMatch[1];
|
|
1527
|
+
highestIncompletePhaseplanCount = plans;
|
|
1528
|
+
highestIncompletePhaseSummaryCount = summaries;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
// Determine total phases from ROADMAP (may be larger than realized disk dirs).
|
|
1534
|
+
// Mirrors the logic in buildStateFrontmatter so both report consistent percents (#3242 Bug B).
|
|
1535
|
+
let syncTotalPhases = null;
|
|
1536
|
+
try {
|
|
1537
|
+
const isDirInMilestone = getMilestonePhaseFilter(cwd);
|
|
1538
|
+
if (isDirInMilestone.phaseCount > 0) {
|
|
1539
|
+
syncTotalPhases = Math.max(entries.length, isDirInMilestone.phaseCount);
|
|
1540
|
+
} else {
|
|
1541
|
+
syncTotalPhases = entries.length;
|
|
1542
|
+
}
|
|
1543
|
+
} catch { /* intentionally empty */ }
|
|
1544
|
+
|
|
1545
|
+
// Sync Total Plans in Phase
|
|
1546
|
+
if (highestIncompletePhase) {
|
|
1547
|
+
const currentPlansField = stateExtractField(modified, 'Total Plans in Phase');
|
|
1548
|
+
if (currentPlansField && parseInt(currentPlansField, 10) !== highestIncompletePhaseplanCount) {
|
|
1549
|
+
changes.push(`Total Plans in Phase: ${currentPlansField} -> ${highestIncompletePhaseplanCount}`);
|
|
1550
|
+
const result = stateReplaceField(modified, 'Total Plans in Phase', String(highestIncompletePhaseplanCount));
|
|
1551
|
+
if (result) modified = result;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
// Sync Progress — use shared helper so formula stays in one place (#3242 Bug B).
|
|
1556
|
+
// computeProgressPercent applies min(plan_fraction, phase_fraction) so unrealised
|
|
1557
|
+
// ROADMAP phases cap the reported percent rather than allowing a false 100%.
|
|
1558
|
+
const percent = (() => {
|
|
1559
|
+
const p = computeProgressPercent(totalDiskSummaries, totalDiskPlans, diskCompletedPhases, syncTotalPhases);
|
|
1560
|
+
return p !== null ? p : 0;
|
|
1561
|
+
})();
|
|
1562
|
+
const currentProgress = stateExtractField(modified, 'Progress');
|
|
1563
|
+
if (currentProgress) {
|
|
1564
|
+
const currentPercent = parseInt(currentProgress.replace(/[^\d]/g, ''), 10);
|
|
1565
|
+
if (currentPercent !== percent) {
|
|
1566
|
+
const barWidth = 10;
|
|
1567
|
+
const filled = Math.round(percent / 100 * barWidth);
|
|
1568
|
+
const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(barWidth - filled);
|
|
1569
|
+
const progressStr = `[${bar}] ${percent}%`;
|
|
1570
|
+
changes.push(`Progress: ${currentProgress} -> ${progressStr}`);
|
|
1571
|
+
const result = stateReplaceField(modified, 'Progress', progressStr);
|
|
1572
|
+
if (result) modified = result;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
// Sync Last Activity
|
|
1577
|
+
const result = stateReplaceField(modified, 'Last Activity', today);
|
|
1578
|
+
if (result) {
|
|
1579
|
+
const oldActivity = stateExtractField(modified, 'Last Activity');
|
|
1580
|
+
if (oldActivity !== today) {
|
|
1581
|
+
changes.push(`Last Activity: ${oldActivity} -> ${today}`);
|
|
1582
|
+
}
|
|
1583
|
+
modified = result;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
if (verify) {
|
|
1587
|
+
output({ synced: false, changes, dry_run: true }, raw);
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
if (changes.length > 0 || modified !== content) {
|
|
1592
|
+
writeStateMd(statePath, modified, cwd);
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
output({ synced: true, changes, dry_run: false }, raw);
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
/**
|
|
1599
|
+
* Prune old entries from STATE.md sections that grow unboundedly (#1970).
|
|
1600
|
+
* Moves decisions, recently-completed summaries, and resolved blockers
|
|
1601
|
+
* older than keepRecent phases to STATE-ARCHIVE.md.
|
|
1602
|
+
*
|
|
1603
|
+
* Options:
|
|
1604
|
+
* keepRecent: number of recent phases to retain (default: 3)
|
|
1605
|
+
* dryRun: if true, return what would be pruned without modifying STATE.md
|
|
1606
|
+
*/
|
|
1607
|
+
function cmdStatePrune(cwd, options, raw) {
|
|
1608
|
+
const silent = !!options.silent;
|
|
1609
|
+
const emit = silent ? () => {} : (result, r, v) => output(result, r, v);
|
|
1610
|
+
const statePath = planningPaths(cwd).state;
|
|
1611
|
+
if (!fs.existsSync(statePath)) { emit({ error: 'STATE.md not found' }, raw); return; }
|
|
1612
|
+
|
|
1613
|
+
const keepRecent = parseInt(options.keepRecent, 10) || 3;
|
|
1614
|
+
const dryRun = !!options.dryRun;
|
|
1615
|
+
const currentPhaseRaw = stateExtractField(fs.readFileSync(statePath, 'utf-8'), 'Current Phase');
|
|
1616
|
+
const currentPhase = parseInt(currentPhaseRaw, 10) || 0;
|
|
1617
|
+
const cutoff = currentPhase - keepRecent;
|
|
1618
|
+
|
|
1619
|
+
if (cutoff <= 0) {
|
|
1620
|
+
emit({ pruned: false, reason: `Only ${currentPhase} phases — nothing to prune with --keep-recent ${keepRecent}` }, raw, 'false');
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
const archivePath = path.join(path.dirname(statePath), 'STATE-ARCHIVE.md');
|
|
1625
|
+
const archived = [];
|
|
1626
|
+
|
|
1627
|
+
// Shared pruning logic applied to both dry-run and real passes.
|
|
1628
|
+
// Returns { newContent, archivedSections }.
|
|
1629
|
+
function prunePass(content) {
|
|
1630
|
+
const sections = [];
|
|
1631
|
+
|
|
1632
|
+
// Prune Decisions section: entries like "- [Phase N]: ..."
|
|
1633
|
+
const decisionPattern = /(###?\s*(?:Decisions|Decisions Made|Accumulated.*Decisions)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
|
|
1634
|
+
const decMatch = content.match(decisionPattern);
|
|
1635
|
+
if (decMatch) {
|
|
1636
|
+
const lines = decMatch[2].split('\n');
|
|
1637
|
+
const keep = [];
|
|
1638
|
+
const archive = [];
|
|
1639
|
+
for (const line of lines) {
|
|
1640
|
+
const phaseMatch = line.match(/^\s*-\s*\[Phase\s+(\d+)/i);
|
|
1641
|
+
if (phaseMatch && parseInt(phaseMatch[1], 10) <= cutoff) {
|
|
1642
|
+
archive.push(line);
|
|
1643
|
+
} else {
|
|
1644
|
+
keep.push(line);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
if (archive.length > 0) {
|
|
1648
|
+
sections.push({ section: 'Decisions', count: archive.length, lines: archive });
|
|
1649
|
+
content = content.replace(decisionPattern, (_m, header) => `${header}${keep.join('\n')}`);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// Prune Recently Completed section: entries mentioning phase numbers
|
|
1654
|
+
const recentPattern = /(###?\s*Recently Completed\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
|
|
1655
|
+
const recMatch = content.match(recentPattern);
|
|
1656
|
+
if (recMatch) {
|
|
1657
|
+
const lines = recMatch[2].split('\n');
|
|
1658
|
+
const keep = [];
|
|
1659
|
+
const archive = [];
|
|
1660
|
+
for (const line of lines) {
|
|
1661
|
+
const phaseMatch = line.match(/Phase\s+(\d+)/i);
|
|
1662
|
+
if (phaseMatch && parseInt(phaseMatch[1], 10) <= cutoff) {
|
|
1663
|
+
archive.push(line);
|
|
1664
|
+
} else {
|
|
1665
|
+
keep.push(line);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
if (archive.length > 0) {
|
|
1669
|
+
sections.push({ section: 'Recently Completed', count: archive.length, lines: archive });
|
|
1670
|
+
content = content.replace(recentPattern, (_m, header) => `${header}${keep.join('\n')}`);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
// Prune resolved blockers: lines marked as resolved (strikethrough ~~text~~
|
|
1675
|
+
// or "[RESOLVED]" prefix) with a phase reference older than cutoff
|
|
1676
|
+
const blockersPattern = /(###?\s*(?:Blockers|Blockers\/Concerns|Blockers\s*&\s*Concerns)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
|
|
1677
|
+
const blockersMatch = content.match(blockersPattern);
|
|
1678
|
+
if (blockersMatch) {
|
|
1679
|
+
const lines = blockersMatch[2].split('\n');
|
|
1680
|
+
const keep = [];
|
|
1681
|
+
const archive = [];
|
|
1682
|
+
for (const line of lines) {
|
|
1683
|
+
const isResolved = /~~.*~~|\[RESOLVED\]/i.test(line);
|
|
1684
|
+
const phaseMatch = line.match(/Phase\s+(\d+)/i);
|
|
1685
|
+
if (isResolved && phaseMatch && parseInt(phaseMatch[1], 10) <= cutoff) {
|
|
1686
|
+
archive.push(line);
|
|
1687
|
+
} else {
|
|
1688
|
+
keep.push(line);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
if (archive.length > 0) {
|
|
1692
|
+
sections.push({ section: 'Blockers (resolved)', count: archive.length, lines: archive });
|
|
1693
|
+
content = content.replace(blockersPattern, (_m, header) => `${header}${keep.join('\n')}`);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// Prune Performance Metrics table rows: keep only rows for phases > cutoff.
|
|
1698
|
+
// Preserves header rows (| Phase | ... and |---|...) and any prose around the table.
|
|
1699
|
+
const metricsPattern = /(###?\s*Performance Metrics\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
|
|
1700
|
+
const metricsMatch = content.match(metricsPattern);
|
|
1701
|
+
if (metricsMatch) {
|
|
1702
|
+
const sectionLines = metricsMatch[2].split('\n');
|
|
1703
|
+
const keep = [];
|
|
1704
|
+
const archive = [];
|
|
1705
|
+
for (const line of sectionLines) {
|
|
1706
|
+
// Table data row: starts with | followed by a number (phase)
|
|
1707
|
+
const tableRowMatch = line.match(/^\|\s*(\d+)\s*\|/);
|
|
1708
|
+
if (tableRowMatch) {
|
|
1709
|
+
const rowPhase = parseInt(tableRowMatch[1], 10);
|
|
1710
|
+
if (rowPhase <= cutoff) {
|
|
1711
|
+
archive.push(line);
|
|
1712
|
+
} else {
|
|
1713
|
+
keep.push(line);
|
|
1714
|
+
}
|
|
1715
|
+
} else {
|
|
1716
|
+
// Header row, separator row, or prose — always keep
|
|
1717
|
+
keep.push(line);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
if (archive.length > 0) {
|
|
1721
|
+
sections.push({ section: 'Performance Metrics', count: archive.length, lines: archive });
|
|
1722
|
+
content = content.replace(metricsPattern, (_m, header) => `${header}${keep.join('\n')}`);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
return { newContent: content, archivedSections: sections };
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
if (dryRun) {
|
|
1730
|
+
// Dry-run: compute what would be pruned without writing anything
|
|
1731
|
+
const content = fs.readFileSync(statePath, 'utf-8');
|
|
1732
|
+
const result = prunePass(content);
|
|
1733
|
+
const totalPruned = result.archivedSections.reduce((sum, s) => sum + s.count, 0);
|
|
1734
|
+
emit({
|
|
1735
|
+
pruned: false,
|
|
1736
|
+
dry_run: true,
|
|
1737
|
+
cutoff_phase: cutoff,
|
|
1738
|
+
keep_recent: keepRecent,
|
|
1739
|
+
sections: result.archivedSections.map(s => ({ section: s.section, entries_would_archive: s.count })),
|
|
1740
|
+
total_would_archive: totalPruned,
|
|
1741
|
+
note: totalPruned > 0 ? 'Run without --dry-run to actually prune' : 'Nothing to prune',
|
|
1742
|
+
}, raw, totalPruned > 0 ? 'true' : 'false');
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
1747
|
+
const result = prunePass(content);
|
|
1748
|
+
archived.push(...result.archivedSections);
|
|
1749
|
+
return result.newContent;
|
|
1750
|
+
}, cwd);
|
|
1751
|
+
|
|
1752
|
+
// Write archived entries to STATE-ARCHIVE.md
|
|
1753
|
+
if (archived.length > 0) {
|
|
1754
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
|
1755
|
+
let archiveContent = platformReadSync(archivePath);
|
|
1756
|
+
if (archiveContent === null) {
|
|
1757
|
+
archiveContent = '# STATE Archive\n\nPruned entries from STATE.md. Recoverable but no longer loaded into agent context.\n\n';
|
|
1758
|
+
}
|
|
1759
|
+
archiveContent += `## Pruned ${timestamp} (phases 1-${cutoff}, kept recent ${keepRecent})\n\n`;
|
|
1760
|
+
for (const section of archived) {
|
|
1761
|
+
archiveContent += `### ${section.section}\n\n${section.lines.join('\n')}\n\n`;
|
|
1762
|
+
}
|
|
1763
|
+
platformWriteSync(archivePath, archiveContent);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
const totalPruned = archived.reduce((sum, s) => sum + s.count, 0);
|
|
1767
|
+
emit({
|
|
1768
|
+
pruned: totalPruned > 0,
|
|
1769
|
+
cutoff_phase: cutoff,
|
|
1770
|
+
keep_recent: keepRecent,
|
|
1771
|
+
sections: archived.map(s => ({ section: s.section, entries_archived: s.count })),
|
|
1772
|
+
total_archived: totalPruned,
|
|
1773
|
+
archive_file: totalPruned > 0 ? 'STATE-ARCHIVE.md' : null,
|
|
1774
|
+
}, raw, totalPruned > 0 ? 'true' : 'false');
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
/**
|
|
1778
|
+
* Mark the current phase as COMPLETE in STATE.md.
|
|
1779
|
+
* Updates Status, Last Activity, and the Current Position section to reflect
|
|
1780
|
+
* that the phase execution is finished and the project is ready for the next phase.
|
|
1781
|
+
* Implements the `sdd state complete-phase` subcommand (issue #2735).
|
|
1782
|
+
*/
|
|
1783
|
+
function resolvePhaseIdForCompletePhase(content, overridePhase) {
|
|
1784
|
+
const candidate = overridePhase ||
|
|
1785
|
+
stateExtractField(content, 'Current Phase') ||
|
|
1786
|
+
stateExtractField(content, 'Phase') ||
|
|
1787
|
+
'';
|
|
1788
|
+
|
|
1789
|
+
// Accept canonical phase token only (e.g. 3, 03, 3A, 3.3, 10.2)
|
|
1790
|
+
const phaseMatch = String(candidate).match(/(\d+[A-Z]?(?:\.\d+)*)/i);
|
|
1791
|
+
return phaseMatch ? phaseMatch[1] : null;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
function cmdStateCompletePhase(cwd, raw, overridePhase) {
|
|
1795
|
+
const statePath = planningPaths(cwd).state;
|
|
1796
|
+
if (!fs.existsSync(statePath)) {
|
|
1797
|
+
output({ error: 'STATE.md not found' }, raw);
|
|
1798
|
+
return;
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
const content = fs.readFileSync(statePath, 'utf-8');
|
|
1802
|
+
const resolvedPhase = resolvePhaseIdForCompletePhase(content, overridePhase);
|
|
1803
|
+
if (!resolvedPhase || /^phase$/i.test(resolvedPhase)) {
|
|
1804
|
+
output({ error: 'Unable to resolve current phase. Pass an explicit phase: state complete-phase --phase <N>' }, raw);
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// Idempotency guard (#3489). If STATE.md's canonical `Current Phase` field
|
|
1809
|
+
// already names a phase distinct from the one we are being asked to mark
|
|
1810
|
+
// complete, the project has advanced past the requested phase (e.g. a
|
|
1811
|
+
// follow-up phase was inserted, or the next phase began). Re-running
|
|
1812
|
+
// `state complete-phase --phase <N>` in that situation previously rolled
|
|
1813
|
+
// STATE.md back to <N>'s moment-of-completion — silently clobbering Status,
|
|
1814
|
+
// Last Activity, Last Activity Description, and the Current Position body.
|
|
1815
|
+
// The handler is now a no-op in that case so re-invocation from downstream
|
|
1816
|
+
// workflows cannot regress the project state.
|
|
1817
|
+
const existingCurrentPhaseRaw = stateExtractField(content, 'Current Phase') || '';
|
|
1818
|
+
const existingCurrentPhaseMatch = String(existingCurrentPhaseRaw).match(/(\d+[A-Z]?(?:\.\d+)*)/i);
|
|
1819
|
+
const existingCurrentPhase = existingCurrentPhaseMatch ? existingCurrentPhaseMatch[1] : null;
|
|
1820
|
+
if (existingCurrentPhase && existingCurrentPhase !== resolvedPhase) {
|
|
1821
|
+
output(
|
|
1822
|
+
{ updated: [], phase: resolvedPhase, idempotent: true, note: 'phase already superseded; no-op' },
|
|
1823
|
+
raw,
|
|
1824
|
+
'false',
|
|
1825
|
+
);
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1830
|
+
const updated = [];
|
|
1831
|
+
|
|
1832
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
1833
|
+
const currentPhase = resolvedPhase;
|
|
1834
|
+
|
|
1835
|
+
// Update Status field
|
|
1836
|
+
const statusValue = `Phase ${currentPhase} complete`;
|
|
1837
|
+
let result = stateReplaceField(content, 'Status', statusValue);
|
|
1838
|
+
if (result) { content = result; updated.push('Status'); }
|
|
1839
|
+
|
|
1840
|
+
// Update Last Activity date
|
|
1841
|
+
result = stateReplaceField(content, 'Last Activity', today);
|
|
1842
|
+
if (result) { content = result; updated.push('Last Activity'); }
|
|
1843
|
+
|
|
1844
|
+
// Update Last Activity Description
|
|
1845
|
+
const activityDesc = `Phase ${currentPhase} marked complete`;
|
|
1846
|
+
result = stateReplaceField(content, 'Last Activity Description', activityDesc);
|
|
1847
|
+
if (result) { content = result; updated.push('Last Activity Description'); }
|
|
1848
|
+
|
|
1849
|
+
// Update ## Current Position section
|
|
1850
|
+
const positionPattern = /(##\s*Current Position\s*\n)([\s\S]*?)(?=\n##|$)/i;
|
|
1851
|
+
const positionMatch = content.match(positionPattern);
|
|
1852
|
+
if (positionMatch) {
|
|
1853
|
+
const header = positionMatch[1];
|
|
1854
|
+
let posBody = positionMatch[2];
|
|
1855
|
+
|
|
1856
|
+
// Update Phase line to show COMPLETE
|
|
1857
|
+
const newPhase = `Phase: ${currentPhase} — COMPLETE`;
|
|
1858
|
+
if (/^Phase:/m.test(posBody)) {
|
|
1859
|
+
posBody = posBody.replace(/^Phase:.*$/m, newPhase);
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
// Update Status line if present
|
|
1863
|
+
const newStatus = `Status: Phase ${currentPhase} complete`;
|
|
1864
|
+
if (/^Status:/m.test(posBody)) {
|
|
1865
|
+
posBody = posBody.replace(/^Status:.*$/m, newStatus);
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
// Update Last activity line if present
|
|
1869
|
+
const newActivity = `Last activity: ${today} -- Phase ${currentPhase} marked complete`;
|
|
1870
|
+
if (/^Last activity:/im.test(posBody)) {
|
|
1871
|
+
posBody = posBody.replace(/^Last activity:.*$/im, newActivity);
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
content = content.replace(positionPattern, () => `${header}${posBody}`);
|
|
1875
|
+
updated.push('Current Position');
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
return content;
|
|
1879
|
+
}, cwd);
|
|
1880
|
+
|
|
1881
|
+
output(
|
|
1882
|
+
{ updated, phase: resolvedPhase },
|
|
1883
|
+
raw,
|
|
1884
|
+
updated.length > 0 ? 'true' : 'false',
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1010
1888
|
module.exports = {
|
|
1011
1889
|
stateExtractField,
|
|
1012
1890
|
stateReplaceField,
|
|
1013
1891
|
stateReplaceFieldWithFallback,
|
|
1014
1892
|
writeStateMd,
|
|
1893
|
+
readModifyWriteStateMd,
|
|
1894
|
+
updatePerformanceMetricsSection,
|
|
1015
1895
|
cmdStateLoad,
|
|
1016
1896
|
cmdStateGet,
|
|
1017
1897
|
cmdStatePatch,
|
|
@@ -1026,6 +1906,12 @@ module.exports = {
|
|
|
1026
1906
|
cmdStateSnapshot,
|
|
1027
1907
|
cmdStateJson,
|
|
1028
1908
|
cmdStateBeginPhase,
|
|
1909
|
+
cmdStatePlannedPhase,
|
|
1910
|
+
cmdStateCompletePhase,
|
|
1911
|
+
cmdStateValidate,
|
|
1912
|
+
cmdStateSync,
|
|
1913
|
+
cmdStatePrune,
|
|
1914
|
+
cmdStateMilestoneSwitch,
|
|
1029
1915
|
cmdSignalWaiting,
|
|
1030
1916
|
cmdSignalResume,
|
|
1031
1917
|
};
|