@opengsd/get-shit-done-redux 1.0.0
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/LICENSE +21 -0
- package/README.ja-JP.md +870 -0
- package/README.ko-KR.md +861 -0
- package/README.md +300 -0
- package/README.pt-BR.md +492 -0
- package/README.zh-CN.md +842 -0
- package/agents/gsd-advisor-researcher.md +127 -0
- package/agents/gsd-ai-researcher.md +133 -0
- package/agents/gsd-assumptions-analyzer.md +105 -0
- package/agents/gsd-code-fixer.md +668 -0
- package/agents/gsd-code-reviewer.md +387 -0
- package/agents/gsd-codebase-mapper.md +853 -0
- package/agents/gsd-debug-session-manager.md +314 -0
- package/agents/gsd-debugger.md +1452 -0
- package/agents/gsd-doc-classifier.md +168 -0
- package/agents/gsd-doc-synthesizer.md +204 -0
- package/agents/gsd-doc-verifier.md +217 -0
- package/agents/gsd-doc-writer.md +615 -0
- package/agents/gsd-domain-researcher.md +153 -0
- package/agents/gsd-eval-auditor.md +191 -0
- package/agents/gsd-eval-planner.md +154 -0
- package/agents/gsd-executor.md +774 -0
- package/agents/gsd-framework-selector.md +160 -0
- package/agents/gsd-integration-checker.md +470 -0
- package/agents/gsd-intel-updater.md +342 -0
- package/agents/gsd-nyquist-auditor.md +203 -0
- package/agents/gsd-pattern-mapper.md +335 -0
- package/agents/gsd-phase-researcher.md +928 -0
- package/agents/gsd-plan-checker.md +978 -0
- package/agents/gsd-planner.md +1278 -0
- package/agents/gsd-project-researcher.md +677 -0
- package/agents/gsd-research-synthesizer.md +247 -0
- package/agents/gsd-roadmapper.md +688 -0
- package/agents/gsd-security-auditor.md +155 -0
- package/agents/gsd-ui-auditor.md +495 -0
- package/agents/gsd-ui-checker.md +309 -0
- package/agents/gsd-ui-researcher.md +380 -0
- package/agents/gsd-user-profiler.md +171 -0
- package/agents/gsd-verifier.md +917 -0
- package/bin/gsd-sdk.js +37 -0
- package/bin/install.js +11468 -0
- package/bin/lib/ui-safety-gate.cjs +107 -0
- package/commands/gsd/add-tests.md +42 -0
- package/commands/gsd/ai-integration-phase.md +37 -0
- package/commands/gsd/audit-fix.md +34 -0
- package/commands/gsd/audit-milestone.md +37 -0
- package/commands/gsd/audit-uat.md +24 -0
- package/commands/gsd/autonomous.md +46 -0
- package/commands/gsd/capture.md +62 -0
- package/commands/gsd/cleanup.md +24 -0
- package/commands/gsd/code-review.md +59 -0
- package/commands/gsd/complete-milestone.md +143 -0
- package/commands/gsd/config.md +58 -0
- package/commands/gsd/debug.md +52 -0
- package/commands/gsd/discuss-phase.md +76 -0
- package/commands/gsd/docs-update.md +49 -0
- package/commands/gsd/eval-review.md +33 -0
- package/commands/gsd/execute-phase.md +64 -0
- package/commands/gsd/explore.md +27 -0
- package/commands/gsd/extract-learnings.md +23 -0
- package/commands/gsd/fast.md +31 -0
- package/commands/gsd/forensics.md +57 -0
- package/commands/gsd/graphify.md +199 -0
- package/commands/gsd/health.md +31 -0
- package/commands/gsd/help.md +28 -0
- package/commands/gsd/import.md +41 -0
- package/commands/gsd/inbox.md +39 -0
- package/commands/gsd/ingest-docs.md +42 -0
- package/commands/gsd/manager.md +45 -0
- package/commands/gsd/map-codebase.md +83 -0
- package/commands/gsd/milestone-summary.md +51 -0
- package/commands/gsd/mvp-phase.md +45 -0
- package/commands/gsd/new-milestone.md +45 -0
- package/commands/gsd/new-project.md +47 -0
- package/commands/gsd/ns-context.md +23 -0
- package/commands/gsd/ns-ideate.md +24 -0
- package/commands/gsd/ns-manage.md +29 -0
- package/commands/gsd/ns-project.md +22 -0
- package/commands/gsd/ns-review.md +26 -0
- package/commands/gsd/ns-workflow.md +28 -0
- package/commands/gsd/pause-work.md +43 -0
- package/commands/gsd/phase.md +56 -0
- package/commands/gsd/plan-phase.md +62 -0
- package/commands/gsd/plan-review-convergence.md +59 -0
- package/commands/gsd/pr-branch.md +26 -0
- package/commands/gsd/profile-user.md +46 -0
- package/commands/gsd/progress.md +46 -0
- package/commands/gsd/quick.md +174 -0
- package/commands/gsd/resume-work.md +30 -0
- package/commands/gsd/review-backlog.md +63 -0
- package/commands/gsd/review.md +41 -0
- package/commands/gsd/secure-phase.md +36 -0
- package/commands/gsd/settings.md +29 -0
- package/commands/gsd/ship.md +24 -0
- package/commands/gsd/sketch.md +60 -0
- package/commands/gsd/spec-phase.md +63 -0
- package/commands/gsd/spike.md +57 -0
- package/commands/gsd/stats.md +19 -0
- package/commands/gsd/surface.md +155 -0
- package/commands/gsd/thread.md +24 -0
- package/commands/gsd/ui-phase.md +35 -0
- package/commands/gsd/ui-review.md +33 -0
- package/commands/gsd/ultraplan-phase.md +34 -0
- package/commands/gsd/undo.md +35 -0
- package/commands/gsd/update.md +48 -0
- package/commands/gsd/validate-phase.md +36 -0
- package/commands/gsd/verify-work.md +39 -0
- package/commands/gsd/workspace.md +52 -0
- package/commands/gsd/workstreams.md +70 -0
- package/get-shit-done/bin/check-latest-version.cjs +104 -0
- package/get-shit-done/bin/gsd-tools.cjs +1630 -0
- package/get-shit-done/bin/lib/active-workstream-store.cjs +85 -0
- package/get-shit-done/bin/lib/adr-parser.cjs +394 -0
- package/get-shit-done/bin/lib/artifacts.cjs +53 -0
- package/get-shit-done/bin/lib/audit.cjs +755 -0
- package/get-shit-done/bin/lib/cjs-command-router-adapter.cjs +39 -0
- package/get-shit-done/bin/lib/cjs-sdk-bridge.cjs +136 -0
- package/get-shit-done/bin/lib/clusters.cjs +135 -0
- package/get-shit-done/bin/lib/code-review-flags.cjs +74 -0
- package/get-shit-done/bin/lib/command-aliases.generated.cjs +824 -0
- package/get-shit-done/bin/lib/command-routing-hub.cjs +239 -0
- package/get-shit-done/bin/lib/commands.cjs +1035 -0
- package/get-shit-done/bin/lib/config-schema.cjs +31 -0
- package/get-shit-done/bin/lib/config.cjs +704 -0
- package/get-shit-done/bin/lib/configuration.generated.cjs +253 -0
- package/get-shit-done/bin/lib/context-utilization.cjs +47 -0
- package/get-shit-done/bin/lib/core.cjs +1922 -0
- package/get-shit-done/bin/lib/decisions.cjs +19 -0
- package/get-shit-done/bin/lib/decisions.generated.cjs +121 -0
- package/get-shit-done/bin/lib/docs.cjs +270 -0
- package/get-shit-done/bin/lib/drift.cjs +388 -0
- package/get-shit-done/bin/lib/fallow-runner.cjs +109 -0
- package/get-shit-done/bin/lib/frontmatter.cjs +389 -0
- package/get-shit-done/bin/lib/gap-checker.cjs +205 -0
- package/get-shit-done/bin/lib/graphify.cjs +592 -0
- package/get-shit-done/bin/lib/gsd2-import.cjs +514 -0
- package/get-shit-done/bin/lib/init-command-router.cjs +174 -0
- package/get-shit-done/bin/lib/init.cjs +2096 -0
- package/get-shit-done/bin/lib/install-profiles.cjs +603 -0
- package/get-shit-done/bin/lib/installer-migration-authoring.cjs +117 -0
- package/get-shit-done/bin/lib/installer-migration-report.cjs +354 -0
- package/get-shit-done/bin/lib/installer-migrations/000-first-time-baseline.cjs +220 -0
- package/get-shit-done/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +41 -0
- package/get-shit-done/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +80 -0
- package/get-shit-done/bin/lib/installer-migrations.cjs +776 -0
- package/get-shit-done/bin/lib/intel.cjs +643 -0
- package/get-shit-done/bin/lib/learnings.cjs +379 -0
- package/get-shit-done/bin/lib/milestone.cjs +314 -0
- package/get-shit-done/bin/lib/model-catalog.cjs +136 -0
- package/get-shit-done/bin/lib/model-profiles.cjs +25 -0
- package/get-shit-done/bin/lib/phase-command-router.cjs +226 -0
- package/get-shit-done/bin/lib/phase.cjs +1490 -0
- package/get-shit-done/bin/lib/phases-command-router.cjs +97 -0
- package/get-shit-done/bin/lib/plan-scan.cjs +26 -0
- package/get-shit-done/bin/lib/plan-scan.generated.cjs +97 -0
- package/get-shit-done/bin/lib/planning-workspace.cjs +415 -0
- package/get-shit-done/bin/lib/profile-output.cjs +1130 -0
- package/get-shit-done/bin/lib/profile-pipeline.cjs +539 -0
- package/get-shit-done/bin/lib/project-root.generated.cjs +117 -0
- package/get-shit-done/bin/lib/prompt-budget.cjs +399 -0
- package/get-shit-done/bin/lib/review-reviewer-selection.cjs +125 -0
- package/get-shit-done/bin/lib/roadmap-command-router.cjs +99 -0
- package/get-shit-done/bin/lib/roadmap.cjs +642 -0
- package/get-shit-done/bin/lib/runtime-artifact-layout.cjs +301 -0
- package/get-shit-done/bin/lib/runtime-homes.cjs +185 -0
- package/get-shit-done/bin/lib/runtime-slash.cjs +109 -0
- package/get-shit-done/bin/lib/schema-detect.cjs +21 -0
- package/get-shit-done/bin/lib/schema-detect.generated.cjs +170 -0
- package/get-shit-done/bin/lib/secrets.cjs +20 -0
- package/get-shit-done/bin/lib/secrets.generated.cjs +37 -0
- package/get-shit-done/bin/lib/security.cjs +504 -0
- package/get-shit-done/bin/lib/shell-command-projection.cjs +552 -0
- package/get-shit-done/bin/lib/state-command-router.cjs +346 -0
- package/get-shit-done/bin/lib/state-document.cjs +12 -0
- package/get-shit-done/bin/lib/state-document.generated.cjs +127 -0
- package/get-shit-done/bin/lib/state.cjs +1940 -0
- package/get-shit-done/bin/lib/surface.cjs +430 -0
- package/get-shit-done/bin/lib/template.cjs +228 -0
- package/get-shit-done/bin/lib/uat.cjs +289 -0
- package/get-shit-done/bin/lib/validate-command-router.cjs +129 -0
- package/get-shit-done/bin/lib/verify-command-router.cjs +122 -0
- package/get-shit-done/bin/lib/verify.cjs +1458 -0
- package/get-shit-done/bin/lib/workstream-inventory-builder.generated.cjs +79 -0
- package/get-shit-done/bin/lib/workstream-inventory.cjs +132 -0
- package/get-shit-done/bin/lib/workstream-name-policy.cjs +19 -0
- package/get-shit-done/bin/lib/workstream-name-policy.generated.cjs +61 -0
- package/get-shit-done/bin/lib/workstream.cjs +374 -0
- package/get-shit-done/bin/lib/worktree-safety.cjs +985 -0
- package/get-shit-done/bin/verify-reapply-patches.cjs +336 -0
- package/get-shit-done/contexts/dev.md +21 -0
- package/get-shit-done/contexts/research.md +22 -0
- package/get-shit-done/contexts/review.md +23 -0
- package/get-shit-done/references/agent-contracts.md +79 -0
- package/get-shit-done/references/ai-evals.md +156 -0
- package/get-shit-done/references/ai-frameworks.md +186 -0
- package/get-shit-done/references/artifact-types.md +131 -0
- package/get-shit-done/references/autonomous-smart-discuss.md +277 -0
- package/get-shit-done/references/checkpoints.md +814 -0
- package/get-shit-done/references/common-bug-patterns.md +114 -0
- package/get-shit-done/references/context-budget.md +85 -0
- package/get-shit-done/references/continuation-format.md +253 -0
- package/get-shit-done/references/debugger-philosophy.md +76 -0
- package/get-shit-done/references/decimal-phase-calculation.md +64 -0
- package/get-shit-done/references/doc-conflict-engine.md +91 -0
- package/get-shit-done/references/domain-probes.md +125 -0
- package/get-shit-done/references/execute-mvp-tdd.md +81 -0
- package/get-shit-done/references/executor-examples.md +110 -0
- package/get-shit-done/references/few-shot-examples/plan-checker.md +73 -0
- package/get-shit-done/references/few-shot-examples/verifier.md +109 -0
- package/get-shit-done/references/gate-prompts.md +100 -0
- package/get-shit-done/references/gates.md +70 -0
- package/get-shit-done/references/git-integration.md +298 -0
- package/get-shit-done/references/git-planning-commit.md +40 -0
- package/get-shit-done/references/ios-scaffold.md +123 -0
- package/get-shit-done/references/mandatory-initial-read.md +2 -0
- package/get-shit-done/references/model-profile-resolution.md +38 -0
- package/get-shit-done/references/model-profiles.md +245 -0
- package/get-shit-done/references/mvp-concepts.md +49 -0
- package/get-shit-done/references/phase-argument-parsing.md +61 -0
- package/get-shit-done/references/planner-antipatterns.md +89 -0
- package/get-shit-done/references/planner-chunked.md +49 -0
- package/get-shit-done/references/planner-gap-closure.md +62 -0
- package/get-shit-done/references/planner-graphify-auto-update.md +67 -0
- package/get-shit-done/references/planner-human-verify-mode.md +57 -0
- package/get-shit-done/references/planner-mvp-mode.md +53 -0
- package/get-shit-done/references/planner-reviews.md +39 -0
- package/get-shit-done/references/planner-revision.md +87 -0
- package/get-shit-done/references/planner-source-audit.md +73 -0
- package/get-shit-done/references/planning-config.md +471 -0
- package/get-shit-done/references/project-skills-discovery.md +19 -0
- package/get-shit-done/references/questioning.md +162 -0
- package/get-shit-done/references/revision-loop.md +97 -0
- package/get-shit-done/references/scout-codebase.md +51 -0
- package/get-shit-done/references/skeleton-template.md +48 -0
- package/get-shit-done/references/sketch-interactivity.md +41 -0
- package/get-shit-done/references/sketch-theme-system.md +94 -0
- package/get-shit-done/references/sketch-tooling.md +45 -0
- package/get-shit-done/references/sketch-variant-patterns.md +81 -0
- package/get-shit-done/references/spidr-splitting.md +69 -0
- package/get-shit-done/references/tdd.md +330 -0
- package/get-shit-done/references/thinking-models-debug.md +44 -0
- package/get-shit-done/references/thinking-models-execution.md +50 -0
- package/get-shit-done/references/thinking-models-planning.md +62 -0
- package/get-shit-done/references/thinking-models-research.md +50 -0
- package/get-shit-done/references/thinking-models-verification.md +55 -0
- package/get-shit-done/references/thinking-partner.md +96 -0
- package/get-shit-done/references/ui-brand.md +160 -0
- package/get-shit-done/references/universal-anti-patterns.md +63 -0
- package/get-shit-done/references/user-profiling.md +681 -0
- package/get-shit-done/references/user-story-template.md +58 -0
- package/get-shit-done/references/verification-overrides.md +227 -0
- package/get-shit-done/references/verification-patterns.md +612 -0
- package/get-shit-done/references/verify-mvp-mode.md +85 -0
- package/get-shit-done/references/workstream-flag.md +111 -0
- package/get-shit-done/references/worktree-path-safety.md +89 -0
- package/get-shit-done/templates/AI-SPEC.md +246 -0
- package/get-shit-done/templates/DEBUG.md +169 -0
- package/get-shit-done/templates/README.md +77 -0
- package/get-shit-done/templates/SECURITY.md +61 -0
- package/get-shit-done/templates/UAT.md +265 -0
- package/get-shit-done/templates/UI-SPEC.md +100 -0
- package/get-shit-done/templates/VALIDATION.md +76 -0
- package/get-shit-done/templates/claude-md.md +145 -0
- package/get-shit-done/templates/codebase/architecture.md +255 -0
- package/get-shit-done/templates/codebase/concerns.md +310 -0
- package/get-shit-done/templates/codebase/conventions.md +307 -0
- package/get-shit-done/templates/codebase/integrations.md +280 -0
- package/get-shit-done/templates/codebase/stack.md +186 -0
- package/get-shit-done/templates/codebase/structure.md +285 -0
- package/get-shit-done/templates/codebase/testing.md +480 -0
- package/get-shit-done/templates/config.json +62 -0
- package/get-shit-done/templates/context.md +352 -0
- package/get-shit-done/templates/continue-here.md +78 -0
- package/get-shit-done/templates/copilot-instructions.md +7 -0
- package/get-shit-done/templates/debug-subagent-prompt.md +91 -0
- package/get-shit-done/templates/dev-preferences.md +21 -0
- package/get-shit-done/templates/discovery.md +146 -0
- package/get-shit-done/templates/discussion-log.md +63 -0
- package/get-shit-done/templates/milestone-archive.md +123 -0
- package/get-shit-done/templates/milestone.md +115 -0
- package/get-shit-done/templates/phase-prompt.md +610 -0
- package/get-shit-done/templates/planner-subagent-prompt.md +117 -0
- package/get-shit-done/templates/project.md +186 -0
- package/get-shit-done/templates/requirements.md +231 -0
- package/get-shit-done/templates/research-project/ARCHITECTURE.md +204 -0
- package/get-shit-done/templates/research-project/FEATURES.md +147 -0
- package/get-shit-done/templates/research-project/PITFALLS.md +200 -0
- package/get-shit-done/templates/research-project/STACK.md +120 -0
- package/get-shit-done/templates/research-project/SUMMARY.md +170 -0
- package/get-shit-done/templates/research.md +592 -0
- package/get-shit-done/templates/retrospective.md +54 -0
- package/get-shit-done/templates/roadmap.md +202 -0
- package/get-shit-done/templates/spec.md +307 -0
- package/get-shit-done/templates/state.md +184 -0
- package/get-shit-done/templates/summary-complex.md +59 -0
- package/get-shit-done/templates/summary-minimal.md +41 -0
- package/get-shit-done/templates/summary-standard.md +48 -0
- package/get-shit-done/templates/summary.md +248 -0
- package/get-shit-done/templates/user-profile.md +146 -0
- package/get-shit-done/templates/user-setup.md +311 -0
- package/get-shit-done/templates/verification-report.md +322 -0
- package/get-shit-done/workflows/add-backlog.md +101 -0
- package/get-shit-done/workflows/add-phase.md +123 -0
- package/get-shit-done/workflows/add-tests.md +365 -0
- package/get-shit-done/workflows/add-todo.md +171 -0
- package/get-shit-done/workflows/ai-integration-phase.md +305 -0
- package/get-shit-done/workflows/analyze-dependencies.md +96 -0
- package/get-shit-done/workflows/audit-fix.md +188 -0
- package/get-shit-done/workflows/audit-milestone.md +368 -0
- package/get-shit-done/workflows/audit-uat.md +120 -0
- package/get-shit-done/workflows/autonomous.md +805 -0
- package/get-shit-done/workflows/check-todos.md +190 -0
- package/get-shit-done/workflows/cleanup.md +165 -0
- package/get-shit-done/workflows/code-review-fix.md +512 -0
- package/get-shit-done/workflows/code-review.md +666 -0
- package/get-shit-done/workflows/complete-milestone.md +865 -0
- package/get-shit-done/workflows/debug.md +242 -0
- package/get-shit-done/workflows/diagnose-issues.md +251 -0
- package/get-shit-done/workflows/discovery-phase.md +291 -0
- package/get-shit-done/workflows/discuss-phase/modes/advisor.md +175 -0
- package/get-shit-done/workflows/discuss-phase/modes/all.md +28 -0
- package/get-shit-done/workflows/discuss-phase/modes/analyze.md +44 -0
- package/get-shit-done/workflows/discuss-phase/modes/auto.md +56 -0
- package/get-shit-done/workflows/discuss-phase/modes/batch.md +52 -0
- package/get-shit-done/workflows/discuss-phase/modes/chain.md +97 -0
- package/get-shit-done/workflows/discuss-phase/modes/default.md +141 -0
- package/get-shit-done/workflows/discuss-phase/modes/power.md +44 -0
- package/get-shit-done/workflows/discuss-phase/modes/text.md +55 -0
- package/get-shit-done/workflows/discuss-phase/templates/checkpoint.json +18 -0
- package/get-shit-done/workflows/discuss-phase/templates/context.md +136 -0
- package/get-shit-done/workflows/discuss-phase/templates/discussion-log.md +50 -0
- package/get-shit-done/workflows/discuss-phase-assumptions.md +685 -0
- package/get-shit-done/workflows/discuss-phase-power.md +291 -0
- package/get-shit-done/workflows/discuss-phase.md +499 -0
- package/get-shit-done/workflows/do.md +122 -0
- package/get-shit-done/workflows/docs-update.md +1172 -0
- package/get-shit-done/workflows/edit-phase.md +305 -0
- package/get-shit-done/workflows/eval-review.md +166 -0
- package/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md +81 -0
- package/get-shit-done/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/get-shit-done/workflows/execute-phase/steps/post-merge-gate.md +116 -0
- package/get-shit-done/workflows/execute-phase.md +1717 -0
- package/get-shit-done/workflows/execute-plan.md +536 -0
- package/get-shit-done/workflows/explore.md +154 -0
- package/get-shit-done/workflows/extract-learnings.md +253 -0
- package/get-shit-done/workflows/fast.md +124 -0
- package/get-shit-done/workflows/forensics.md +289 -0
- package/get-shit-done/workflows/graduation.md +206 -0
- package/get-shit-done/workflows/health.md +234 -0
- package/get-shit-done/workflows/help/modes/brief.md +22 -0
- package/get-shit-done/workflows/help/modes/default.md +50 -0
- package/get-shit-done/workflows/help/modes/full.md +784 -0
- package/get-shit-done/workflows/help/modes/topic.md +74 -0
- package/get-shit-done/workflows/help.md +24 -0
- package/get-shit-done/workflows/import.md +264 -0
- package/get-shit-done/workflows/inbox.md +387 -0
- package/get-shit-done/workflows/ingest-docs.md +339 -0
- package/get-shit-done/workflows/insert-phase.md +162 -0
- package/get-shit-done/workflows/list-phase-assumptions.md +178 -0
- package/get-shit-done/workflows/list-workspaces.md +67 -0
- package/get-shit-done/workflows/manager.md +403 -0
- package/get-shit-done/workflows/map-codebase.md +454 -0
- package/get-shit-done/workflows/milestone-summary.md +234 -0
- package/get-shit-done/workflows/mvp-phase.md +232 -0
- package/get-shit-done/workflows/new-milestone.md +645 -0
- package/get-shit-done/workflows/new-project.md +1487 -0
- package/get-shit-done/workflows/new-workspace.md +250 -0
- package/get-shit-done/workflows/next.md +231 -0
- package/get-shit-done/workflows/node-repair.md +92 -0
- package/get-shit-done/workflows/note.md +158 -0
- package/get-shit-done/workflows/pause-work.md +254 -0
- package/get-shit-done/workflows/plan-milestone-gaps.md +291 -0
- package/get-shit-done/workflows/plan-phase.md +1800 -0
- package/get-shit-done/workflows/plan-review-convergence.md +340 -0
- package/get-shit-done/workflows/plant-seed.md +240 -0
- package/get-shit-done/workflows/pr-branch.md +157 -0
- package/get-shit-done/workflows/profile-user.md +463 -0
- package/get-shit-done/workflows/progress.md +660 -0
- package/get-shit-done/workflows/quick.md +1049 -0
- package/get-shit-done/workflows/reapply-patches.md +426 -0
- package/get-shit-done/workflows/remove-phase.md +166 -0
- package/get-shit-done/workflows/remove-workspace.md +118 -0
- package/get-shit-done/workflows/resume-project.md +342 -0
- package/get-shit-done/workflows/review.md +633 -0
- package/get-shit-done/workflows/scan.md +115 -0
- package/get-shit-done/workflows/secure-phase.md +190 -0
- package/get-shit-done/workflows/session-report.md +146 -0
- package/get-shit-done/workflows/settings-advanced.md +590 -0
- package/get-shit-done/workflows/settings-integrations.md +292 -0
- package/get-shit-done/workflows/settings.md +545 -0
- package/get-shit-done/workflows/ship.md +366 -0
- package/get-shit-done/workflows/sketch-wrap-up.md +296 -0
- package/get-shit-done/workflows/sketch.md +371 -0
- package/get-shit-done/workflows/spec-phase.md +262 -0
- package/get-shit-done/workflows/spike-wrap-up.md +317 -0
- package/get-shit-done/workflows/spike.md +463 -0
- package/get-shit-done/workflows/stats.md +90 -0
- package/get-shit-done/workflows/sync-skills.md +182 -0
- package/get-shit-done/workflows/thread.md +232 -0
- package/get-shit-done/workflows/transition.md +704 -0
- package/get-shit-done/workflows/ui-phase.md +338 -0
- package/get-shit-done/workflows/ui-review.md +203 -0
- package/get-shit-done/workflows/ultraplan-phase.md +209 -0
- package/get-shit-done/workflows/undo.md +314 -0
- package/get-shit-done/workflows/update.md +664 -0
- package/get-shit-done/workflows/validate-phase.md +189 -0
- package/get-shit-done/workflows/verify-phase.md +554 -0
- package/get-shit-done/workflows/verify-work.md +791 -0
- package/hooks/dist/gsd-check-update-worker.js +117 -0
- package/hooks/dist/gsd-check-update.js +64 -0
- package/hooks/dist/gsd-context-monitor.js +192 -0
- package/hooks/dist/gsd-graphify-update.sh +158 -0
- package/hooks/dist/gsd-phase-boundary.sh +47 -0
- package/hooks/dist/gsd-prompt-guard.js +97 -0
- package/hooks/dist/gsd-read-guard.js +101 -0
- package/hooks/dist/gsd-read-injection-scanner.js +152 -0
- package/hooks/dist/gsd-session-state.sh +59 -0
- package/hooks/dist/gsd-statusline.js +537 -0
- package/hooks/dist/gsd-update-banner.js +134 -0
- package/hooks/dist/gsd-validate-commit.sh +57 -0
- package/hooks/dist/gsd-workflow-guard.js +94 -0
- package/hooks/dist/lib/git-cmd.js +150 -0
- package/hooks/dist/lib/gsd-graphify-rebuild.sh +65 -0
- package/hooks/gsd-check-update-worker.js +117 -0
- package/hooks/gsd-check-update.js +64 -0
- package/hooks/gsd-context-monitor.js +192 -0
- package/hooks/gsd-graphify-update.sh +158 -0
- package/hooks/gsd-phase-boundary.sh +47 -0
- package/hooks/gsd-prompt-guard.js +97 -0
- package/hooks/gsd-read-guard.js +101 -0
- package/hooks/gsd-read-injection-scanner.js +152 -0
- package/hooks/gsd-session-state.sh +59 -0
- package/hooks/gsd-statusline.js +537 -0
- package/hooks/gsd-update-banner.js +134 -0
- package/hooks/gsd-validate-commit.sh +57 -0
- package/hooks/gsd-workflow-guard.js +94 -0
- package/hooks/lib/git-cmd.js +150 -0
- package/hooks/lib/gsd-graphify-rebuild.sh +65 -0
- package/package.json +98 -0
- package/scripts/audit-workflow-script-paths.cjs +73 -0
- package/scripts/base64-scan.sh +262 -0
- package/scripts/build-hooks.js +227 -0
- package/scripts/changeset/cli.cjs +408 -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 +114 -0
- package/scripts/changeset/render.cjs +34 -0
- package/scripts/changeset/serialize.cjs +130 -0
- package/scripts/command-contract-helpers.cjs +64 -0
- package/scripts/diff-touches-shipped-paths.cjs +147 -0
- package/scripts/fix-slash-commands.cjs +147 -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-docs-required.cjs +222 -0
- package/scripts/lint-no-source-grep-extras.cjs +81 -0
- package/scripts/lint-no-source-grep.cjs +174 -0
- package/scripts/lint-pr-check-project-dir.cjs +98 -0
- package/scripts/lint-shared-module-handsync.cjs +331 -0
- package/scripts/lint-shell-command-projection-drift.cjs +57 -0
- package/scripts/lint-skill-deps.cjs +180 -0
- package/scripts/lint-test-file-count.allowlist.json +35 -0
- package/scripts/lint-test-file-count.cjs +190 -0
- package/scripts/pr-template-policy.cjs +268 -0
- package/scripts/prompt-injection-scan.sh +203 -0
- package/scripts/release-tarball-smoke.cjs +677 -0
- package/scripts/run-tests.cjs +178 -0
- package/scripts/secret-scan.sh +229 -0
- package/scripts/shared-module-handsync-allowlist.json +145 -0
- package/scripts/strip-prose-atrefs.cjs +106 -0
- package/scripts/sync-rulesets.sh +34 -0
- package/scripts/verify-tarball-sdk-dist.sh +69 -0
- 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 +108 -0
- package/sdk/dist/config.d.ts.map +1 -0
- package/sdk/dist/config.js +116 -0
- package/sdk/dist/config.js.map +1 -0
- package/sdk/dist/configuration/index.d.ts +85 -0
- package/sdk/dist/configuration/index.d.ts.map +1 -0
- package/sdk/dist/configuration/index.js +257 -0
- package/sdk/dist/configuration/index.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/gsd-tools-error.d.ts +23 -0
- package/sdk/dist/gsd-tools-error.d.ts.map +1 -0
- package/sdk/dist/gsd-tools-error.js +29 -0
- package/sdk/dist/gsd-tools-error.js.map +1 -0
- package/sdk/dist/gsd-tools.d.ts +97 -0
- package/sdk/dist/gsd-tools.d.ts.map +1 -0
- package/sdk/dist/gsd-tools.js +168 -0
- package/sdk/dist/gsd-tools.js.map +1 -0
- package/sdk/dist/gsd-transport-policy.d.ts +10 -0
- package/sdk/dist/gsd-transport-policy.d.ts.map +1 -0
- package/sdk/dist/gsd-transport-policy.js +32 -0
- package/sdk/dist/gsd-transport-policy.js.map +1 -0
- package/sdk/dist/gsd-transport.d.ts +39 -0
- package/sdk/dist/gsd-transport.d.ts.map +1 -0
- package/sdk/dist/gsd-transport.js +78 -0
- package/sdk/dist/gsd-transport.js.map +1 -0
- package/sdk/dist/index.d.ts +127 -0
- package/sdk/dist/index.d.ts.map +1 -0
- package/sdk/dist/index.js +300 -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/project-root/index.d.ts +46 -0
- package/sdk/dist/project-root/index.d.ts.map +1 -0
- package/sdk/dist/project-root/index.js +138 -0
- package/sdk/dist/project-root/index.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 +17 -0
- package/sdk/dist/query/check-ship-ready.d.ts.map +1 -0
- package/sdk/dist/query/check-ship-ready.js +121 -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 +133 -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 +101 -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 +60 -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 +16 -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 +16 -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 +110 -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 +106 -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 +179 -0
- package/sdk/dist/query/commit.d.ts.map +1 -0
- package/sdk/dist/query/commit.js +632 -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 +602 -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 +277 -0
- package/sdk/dist/query/config-query.js.map +1 -0
- package/sdk/dist/query/config-schema.d.ts +19 -0
- package/sdk/dist/query/config-schema.d.ts.map +1 -0
- package/sdk/dist/query/config-schema.js +26 -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 +165 -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 +299 -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 +364 -0
- package/sdk/dist/query/frontmatter.js.map +1 -0
- package/sdk/dist/query/helpers.d.ts +194 -0
- package/sdk/dist/query/helpers.d.ts.map +1 -0
- package/sdk/dist/query/helpers.js +540 -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 +735 -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 +1228 -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 +1823 -0
- package/sdk/dist/query/phase-lifecycle.js.map +1 -0
- package/sdk/dist/query/phase-list-queries.d.ts +20 -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 +25 -0
- package/sdk/dist/query/phase-roadmap-mutation.d.ts.map +1 -0
- package/sdk/dist/query/phase-roadmap-mutation.js +76 -0
- package/sdk/dist/query/phase-roadmap-mutation.js.map +1 -0
- package/sdk/dist/query/phase-uat-passed.d.ts +46 -0
- package/sdk/dist/query/phase-uat-passed.d.ts.map +1 -0
- package/sdk/dist/query/phase-uat-passed.js +238 -0
- package/sdk/dist/query/phase-uat-passed.js.map +1 -0
- package/sdk/dist/query/phase.d.ts +104 -0
- package/sdk/dist/query/phase.d.ts.map +1 -0
- package/sdk/dist/query/phase.js +617 -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/prompt-budget.d.ts +14 -0
- package/sdk/dist/query/prompt-budget.d.ts.map +1 -0
- package/sdk/dist/query/prompt-budget.js +417 -0
- package/sdk/dist/query/prompt-budget.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-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.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 +160 -0
- package/sdk/dist/query/roadmap.d.ts.map +1 -0
- package/sdk/dist/query/roadmap.js +982 -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 +1635 -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 +443 -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 +42 -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 +1001 -0
- package/sdk/dist/query/validate.js.map +1 -0
- package/sdk/dist/query/verify.d.ts +98 -0
- package/sdk/dist/query/verify.d.ts.map +1 -0
- package/sdk/dist/query/verify.js +593 -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 +24 -0
- package/sdk/dist/query/workstream-inventory.d.ts.map +1 -0
- package/sdk/dist/query/workstream-inventory.js +120 -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 +9 -0
- package/sdk/dist/query/worktree.d.ts.map +1 -0
- package/sdk/dist/query/worktree.js +79 -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-gsd-tools-path.d.ts +2 -0
- package/sdk/dist/query-gsd-tools-path.d.ts.map +1 -0
- package/sdk/dist/query-gsd-tools-path.js +2 -0
- package/sdk/dist/query-gsd-tools-path.js.map +1 -0
- package/sdk/dist/query-gsd-tools-runtime.d.ts +20 -0
- package/sdk/dist/query-gsd-tools-runtime.d.ts.map +1 -0
- package/sdk/dist/query-gsd-tools-runtime.js +47 -0
- package/sdk/dist/query-gsd-tools-runtime.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 +86 -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-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-bridge-sync/index.d.ts +96 -0
- package/sdk/dist/runtime-bridge-sync/index.d.ts.map +1 -0
- package/sdk/dist/runtime-bridge-sync/index.js +109 -0
- package/sdk/dist/runtime-bridge-sync/index.js.map +1 -0
- package/sdk/dist/runtime-bridge-sync/worker.d.ts +2 -0
- package/sdk/dist/runtime-bridge-sync/worker.d.ts.map +1 -0
- package/sdk/dist/runtime-bridge-sync/worker.js +180 -0
- package/sdk/dist/runtime-bridge-sync/worker.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/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-inventory/builder.d.ts +88 -0
- package/sdk/dist/workstream-inventory/builder.d.ts.map +1 -0
- package/sdk/dist/workstream-inventory/builder.js +84 -0
- package/sdk/dist/workstream-inventory/builder.js.map +1 -0
- package/sdk/dist/workstream-name-policy.d.ts +37 -0
- package/sdk/dist/workstream-name-policy.d.ts.map +1 -0
- package/sdk/dist/workstream-name-policy.js +53 -0
- package/sdk/dist/workstream-name-policy.js.map +1 -0
- package/sdk/dist/workstream-utils.d.ts +23 -0
- package/sdk/dist/workstream-utils.d.ts.map +1 -0
- package/sdk/dist/workstream-utils.js +34 -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 +2530 -0
- package/sdk/package.json +77 -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/config-defaults.manifest.json +75 -0
- package/sdk/shared/config-schema.manifest.json +151 -0
- package/sdk/shared/model-catalog.json +122 -0
- package/sdk/src/assembled-prompts.test.ts +349 -0
- package/sdk/src/bug-3589-planning-paths-validation.test.ts +89 -0
- package/sdk/src/bug-3591-gsdtools-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 +277 -0
- package/sdk/src/config.ts +202 -0
- package/sdk/src/configuration/index.test.ts +318 -0
- package/sdk/src/configuration/index.ts +325 -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 +1031 -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/gsd-tools-error.test.ts +21 -0
- package/sdk/src/gsd-tools-error.ts +65 -0
- package/sdk/src/gsd-tools.test.ts +472 -0
- package/sdk/src/gsd-tools.ts +237 -0
- package/sdk/src/gsd-transport-policy.test.ts +34 -0
- package/sdk/src/gsd-transport-policy.ts +48 -0
- package/sdk/src/gsd-transport.test.ts +299 -0
- package/sdk/src/gsd-transport.ts +118 -0
- package/sdk/src/index.ts +366 -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.ts +259 -0
- package/sdk/src/phase-runner.integration.test.ts +377 -0
- package/sdk/src/phase-runner.test.ts +3660 -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/project-root/index.test.ts +186 -0
- package/sdk/src/project-root/index.ts +144 -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 +303 -0
- package/sdk/src/query/check-ship-ready.ts +136 -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 +154 -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 +123 -0
- package/sdk/src/query/command-manifest.init.ts +24 -0
- package/sdk/src/query/command-manifest.non-family.ts +86 -0
- package/sdk/src/query/command-manifest.phase.ts +17 -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 +17 -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 +111 -0
- package/sdk/src/query/command-static-catalog-foundation.ts +111 -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 +717 -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 +705 -0
- package/sdk/src/query/config-query.test.ts +472 -0
- package/sdk/src/query/config-query.ts +314 -0
- package/sdk/src/query/config-schema.ts +35 -0
- package/sdk/src/query/decisions.test.ts +221 -0
- package/sdk/src/query/decisions.ts +196 -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 +328 -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 +566 -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 +788 -0
- package/sdk/src/query/init-complex.ts +815 -0
- package/sdk/src/query/init-workstream-milestone-op.test.ts +321 -0
- package/sdk/src/query/init.test.ts +791 -0
- package/sdk/src/query/init.ts +1335 -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 +1971 -0
- package/sdk/src/query/phase-lifecycle.ts +2210 -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 +82 -0
- package/sdk/src/query/phase-uat-passed.test.ts +593 -0
- package/sdk/src/query/phase-uat-passed.ts +297 -0
- package/sdk/src/query/phase.test.ts +693 -0
- package/sdk/src/query/phase.ts +741 -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/prompt-budget.ts +556 -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.ts +5 -0
- package/sdk/src/query/query-dispatch-formatting.ts +5 -0
- package/sdk/src/query/query-dispatch-observability.ts +6 -0
- package/sdk/src/query/query-dispatch.test.ts +699 -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 +1250 -0
- package/sdk/src/query/roadmap.ts +1131 -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 +1210 -0
- package/sdk/src/query/state-mutation.ts +1814 -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 +476 -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 +924 -0
- package/sdk/src/query/validate.ts +1054 -0
- package/sdk/src/query/verify.test.ts +414 -0
- package/sdk/src/query/verify.ts +656 -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 +143 -0
- package/sdk/src/query/workstream.test.ts +153 -0
- package/sdk/src/query/workstream.ts +324 -0
- package/sdk/src/query/worktree.ts +84 -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-gsd-tools-path.ts +1 -0
- package/sdk/src/query-gsd-tools-runtime.ts +89 -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 +93 -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-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-bridge-sync/index.test.ts +164 -0
- package/sdk/src/runtime-bridge-sync/index.ts +154 -0
- package/sdk/src/runtime-bridge-sync/projectdir-regression.test.ts +150 -0
- package/sdk/src/runtime-bridge-sync/worker.ts +224 -0
- package/sdk/src/runtime-gate.test.ts +84 -0
- package/sdk/src/runtime-gate.ts +52 -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-inventory/builder.test.ts +241 -0
- package/sdk/src/workstream-inventory/builder.ts +170 -0
- package/sdk/src/workstream-name-policy.ts +57 -0
- package/sdk/src/workstream-utils.ts +36 -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
|
@@ -0,0 +1,1940 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State — STATE.md operations and progression engine
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
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');
|
|
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 gsd-tools invocation.
|
|
24
|
+
const _diskScanCache = new Map();
|
|
25
|
+
|
|
26
|
+
/** Shorthand — every state command needs this path */
|
|
27
|
+
function getStatePath(cwd) {
|
|
28
|
+
return planningPaths(cwd).state;
|
|
29
|
+
}
|
|
30
|
+
|
|
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
|
+
});
|
|
40
|
+
|
|
41
|
+
function cmdStateLoad(cwd, raw) {
|
|
42
|
+
const config = loadConfig(cwd);
|
|
43
|
+
const planDir = planningPaths(cwd).planning;
|
|
44
|
+
|
|
45
|
+
const stateRaw = platformReadSync(path.join(planDir, 'STATE.md')) || '';
|
|
46
|
+
|
|
47
|
+
const configExists = fs.existsSync(path.join(planDir, 'config.json'));
|
|
48
|
+
const roadmapExists = fs.existsSync(path.join(planDir, 'ROADMAP.md'));
|
|
49
|
+
const stateExists = stateRaw.length > 0;
|
|
50
|
+
|
|
51
|
+
const result = {
|
|
52
|
+
config,
|
|
53
|
+
state_raw: stateRaw,
|
|
54
|
+
state_exists: stateExists,
|
|
55
|
+
roadmap_exists: roadmapExists,
|
|
56
|
+
config_exists: configExists,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// For --raw, output a condensed key=value format
|
|
60
|
+
if (raw) {
|
|
61
|
+
const c = config;
|
|
62
|
+
const lines = [
|
|
63
|
+
`model_profile=${c.model_profile}`,
|
|
64
|
+
`commit_docs=${c.commit_docs}`,
|
|
65
|
+
`branching_strategy=${c.branching_strategy}`,
|
|
66
|
+
`phase_branch_template=${c.phase_branch_template}`,
|
|
67
|
+
`milestone_branch_template=${c.milestone_branch_template}`,
|
|
68
|
+
`parallelization=${c.parallelization}`,
|
|
69
|
+
`research=${c.research}`,
|
|
70
|
+
`plan_checker=${c.plan_checker}`,
|
|
71
|
+
`verifier=${c.verifier}`,
|
|
72
|
+
`config_exists=${configExists}`,
|
|
73
|
+
`roadmap_exists=${roadmapExists}`,
|
|
74
|
+
`state_exists=${stateExists}`,
|
|
75
|
+
];
|
|
76
|
+
process.stdout.write(lines.join('\n'));
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
output(result);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function cmdStateGet(cwd, section, raw) {
|
|
84
|
+
const statePath = planningPaths(cwd).state;
|
|
85
|
+
const content = platformReadSync(statePath);
|
|
86
|
+
if (content === null) {
|
|
87
|
+
error('STATE.md not found');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
{
|
|
91
|
+
|
|
92
|
+
if (!section) {
|
|
93
|
+
output({ content }, raw, content);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Try to find markdown section or field
|
|
98
|
+
const fieldEscaped = escapeRegex(section);
|
|
99
|
+
|
|
100
|
+
// Check for **field:** value (bold format)
|
|
101
|
+
const boldPattern = new RegExp(`\\*\\*${fieldEscaped}:\\*\\*\\s*(.*)`, 'i');
|
|
102
|
+
const boldMatch = content.match(boldPattern);
|
|
103
|
+
if (boldMatch) {
|
|
104
|
+
output({ [section]: boldMatch[1].trim() }, raw, boldMatch[1].trim());
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check for field: value (plain format)
|
|
109
|
+
const plainPattern = new RegExp(`^${fieldEscaped}:\\s*(.*)`, 'im');
|
|
110
|
+
const plainMatch = content.match(plainPattern);
|
|
111
|
+
if (plainMatch) {
|
|
112
|
+
output({ [section]: plainMatch[1].trim() }, raw, plainMatch[1].trim());
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check for ## Section
|
|
117
|
+
const sectionPattern = new RegExp(`##\\s*${fieldEscaped}\\s*\n([\\s\\S]*?)(?=\\n##|$)`, 'i');
|
|
118
|
+
const sectionMatch = content.match(sectionPattern);
|
|
119
|
+
if (sectionMatch) {
|
|
120
|
+
output({ [section]: sectionMatch[1].trim() }, raw, sectionMatch[1].trim());
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
output({ error: `Section or field "${section}" not found` }, raw, '');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function readTextArgOrFile(cwd, value, filePath, label) {
|
|
129
|
+
if (!filePath) return value;
|
|
130
|
+
|
|
131
|
+
// Path traversal guard: ensure file resolves within project directory
|
|
132
|
+
const { validatePath } = require('./security.cjs');
|
|
133
|
+
const pathCheck = validatePath(filePath, cwd, { allowAbsolute: true });
|
|
134
|
+
if (!pathCheck.safe) {
|
|
135
|
+
throw new Error(`${label} path rejected: ${pathCheck.error}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
return fs.readFileSync(pathCheck.resolved, 'utf-8').trimEnd();
|
|
140
|
+
} catch {
|
|
141
|
+
throw new Error(`${label} file not found: ${filePath}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function cmdStatePatch(cwd, patches, raw) {
|
|
146
|
+
// Validate all field names before processing
|
|
147
|
+
const { validateFieldName } = require('./security.cjs');
|
|
148
|
+
for (const field of Object.keys(patches)) {
|
|
149
|
+
const fieldCheck = validateFieldName(field);
|
|
150
|
+
if (!fieldCheck.valid) {
|
|
151
|
+
error(`state patch: ${fieldCheck.error}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const statePath = planningPaths(cwd).state;
|
|
156
|
+
try {
|
|
157
|
+
const results = { updated: [], failed: [] };
|
|
158
|
+
|
|
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
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return content;
|
|
171
|
+
}, cwd);
|
|
172
|
+
|
|
173
|
+
output(results, raw, results.updated.length > 0 ? 'true' : 'false');
|
|
174
|
+
} catch {
|
|
175
|
+
error('STATE.md not found');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function cmdStateUpdate(cwd, field, value) {
|
|
180
|
+
if (!field || value === undefined) {
|
|
181
|
+
error('field and value required for state update');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Validate field name to prevent regex injection via crafted field names
|
|
185
|
+
const { validateFieldName } = require('./security.cjs');
|
|
186
|
+
const fieldCheck = validateFieldName(field);
|
|
187
|
+
if (!fieldCheck.valid) {
|
|
188
|
+
error(`state update: ${fieldCheck.error}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const statePath = planningPaths(cwd).state;
|
|
192
|
+
try {
|
|
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) {
|
|
211
|
+
output({ updated: true });
|
|
212
|
+
} else {
|
|
213
|
+
output({ updated: false, reason: `Field "${field}" not found in STATE.md` });
|
|
214
|
+
}
|
|
215
|
+
} catch {
|
|
216
|
+
output({ updated: false, reason: 'STATE.md not found' });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ─── State Progression Engine ────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Replace a STATE.md field with fallback field name support.
|
|
224
|
+
* Tries `primary` first, then `fallback` (if provided), returns content unchanged
|
|
225
|
+
* if neither matches. This consolidates the replaceWithFallback pattern that was
|
|
226
|
+
* previously duplicated inline across phase.cjs, milestone.cjs, and state.cjs.
|
|
227
|
+
*/
|
|
228
|
+
function stateReplaceFieldWithFallback(content, primary, fallback, value) {
|
|
229
|
+
let result = stateReplaceField(content, primary, value);
|
|
230
|
+
if (result) return result;
|
|
231
|
+
if (fallback) {
|
|
232
|
+
result = stateReplaceField(content, fallback, value);
|
|
233
|
+
if (result) return result;
|
|
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
|
+
`[gsd-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
|
+
);
|
|
241
|
+
return content;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Update fields within the ## Current Position section of STATE.md.
|
|
246
|
+
* This keeps the Current Position body in sync with the bold frontmatter fields.
|
|
247
|
+
* Only updates fields that already exist in the section; does not add new lines.
|
|
248
|
+
* Fixes #1365: advance-plan could not update Status/Last activity after begin-phase.
|
|
249
|
+
*/
|
|
250
|
+
function updateCurrentPositionFields(content, fields) {
|
|
251
|
+
const posPattern = /(##\s*Current Position\s*\n)([\s\S]*?)(?=\n##|$)/i;
|
|
252
|
+
const posMatch = content.match(posPattern);
|
|
253
|
+
if (!posMatch) return content;
|
|
254
|
+
|
|
255
|
+
let posBody = posMatch[2];
|
|
256
|
+
|
|
257
|
+
if (fields.status && /^Status:/m.test(posBody)) {
|
|
258
|
+
posBody = posBody.replace(/^Status:.*$/m, `Status: ${fields.status}`);
|
|
259
|
+
}
|
|
260
|
+
if (fields.lastActivity && /^Last activity:/im.test(posBody)) {
|
|
261
|
+
posBody = posBody.replace(/^Last activity:.*$/im, `Last activity: ${fields.lastActivity}`);
|
|
262
|
+
}
|
|
263
|
+
if (fields.plan && /^Plan:/m.test(posBody)) {
|
|
264
|
+
posBody = posBody.replace(/^Plan:.*$/m, `Plan: ${fields.plan}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return content.replace(posPattern, () => `${posMatch[1]}${posBody}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function cmdStateAdvancePlan(cwd, raw) {
|
|
271
|
+
const statePath = planningPaths(cwd).state;
|
|
272
|
+
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
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
|
+
}
|
|
296
|
+
|
|
297
|
+
if (isNaN(currentPlan) || isNaN(totalPlans)) {
|
|
298
|
+
result = { error: true };
|
|
299
|
+
return content;
|
|
300
|
+
}
|
|
301
|
+
|
|
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);
|
|
325
|
+
|
|
326
|
+
if (!result || result.error) {
|
|
327
|
+
output({ error: 'Cannot parse Current Plan or Total Plans in Phase from STATE.md' }, raw);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (result.advanced === false) {
|
|
332
|
+
output(result, raw, 'false');
|
|
333
|
+
} else {
|
|
334
|
+
output(result, raw, 'true');
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function cmdStateRecordMetric(cwd, options, raw) {
|
|
339
|
+
const statePath = planningPaths(cwd).state;
|
|
340
|
+
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
341
|
+
|
|
342
|
+
const { phase, plan, duration, tasks, files } = options;
|
|
343
|
+
|
|
344
|
+
if (!phase || !plan || !duration) {
|
|
345
|
+
output({ error: 'phase, plan, and duration required' }, raw);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
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);
|
|
355
|
+
|
|
356
|
+
const newRow = `| Phase ${phase} P${plan} | ${duration} | ${tasks || '-'} tasks | ${files || '-'} files |`;
|
|
357
|
+
|
|
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`);
|
|
369
|
+
}
|
|
370
|
+
|
|
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');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function cmdStateUpdateProgress(cwd, raw) {
|
|
394
|
+
const statePath = planningPaths(cwd).state;
|
|
395
|
+
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
396
|
+
|
|
397
|
+
// Count summaries across current milestone phases only (outside lock — read-only)
|
|
398
|
+
const phasesDir = planningPaths(cwd).phases;
|
|
399
|
+
let totalPlans = 0;
|
|
400
|
+
let totalSummaries = 0;
|
|
401
|
+
|
|
402
|
+
if (fs.existsSync(phasesDir)) {
|
|
403
|
+
const isDirInMilestone = getMilestonePhaseFilter(cwd);
|
|
404
|
+
const phaseDirs = fs.readdirSync(phasesDir, { withFileTypes: true })
|
|
405
|
+
.filter(e => e.isDirectory()).map(e => e.name)
|
|
406
|
+
.filter(isDirInMilestone);
|
|
407
|
+
for (const dir of phaseDirs) {
|
|
408
|
+
const { planCount, summaryCount } = scanPhasePlans(path.join(phasesDir, dir));
|
|
409
|
+
totalPlans += planCount;
|
|
410
|
+
totalSummaries += summaryCount;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const percent = totalPlans > 0 ? Math.min(100, Math.round(totalSummaries / totalPlans * 100)) : 0;
|
|
415
|
+
const barWidth = 10;
|
|
416
|
+
const filled = Math.round(percent / 100 * barWidth);
|
|
417
|
+
const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(barWidth - filled);
|
|
418
|
+
const progressStr = `[${bar}] ${percent}%`;
|
|
419
|
+
|
|
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);
|
|
440
|
+
} else {
|
|
441
|
+
output({ updated: false, reason: 'Progress field not found in STATE.md' }, raw, 'false');
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function cmdStateAddDecision(cwd, options, raw) {
|
|
446
|
+
const statePath = planningPaths(cwd).state;
|
|
447
|
+
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
448
|
+
|
|
449
|
+
const { phase, summary, summary_file, rationale, rationale_file } = options;
|
|
450
|
+
let summaryText = null;
|
|
451
|
+
let rationaleText = '';
|
|
452
|
+
|
|
453
|
+
try {
|
|
454
|
+
summaryText = readTextArgOrFile(cwd, summary, summary_file, 'summary');
|
|
455
|
+
rationaleText = readTextArgOrFile(cwd, rationale || '', rationale_file, 'rationale');
|
|
456
|
+
} catch (err) {
|
|
457
|
+
output({ added: false, reason: err.message }, raw, 'false');
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (!summaryText) { output({ error: 'summary required' }, raw); return; }
|
|
462
|
+
|
|
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
|
+
}
|
|
480
|
+
|
|
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');
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function cmdStateAddBlocker(cwd, text, raw) {
|
|
502
|
+
const statePath = planningPaths(cwd).state;
|
|
503
|
+
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
504
|
+
const blockerOptions = typeof text === 'object' && text !== null ? text : { text };
|
|
505
|
+
let blockerText = null;
|
|
506
|
+
|
|
507
|
+
try {
|
|
508
|
+
blockerText = readTextArgOrFile(cwd, blockerOptions.text, blockerOptions.text_file, 'blocker');
|
|
509
|
+
} catch (err) {
|
|
510
|
+
output({ added: false, reason: err.message }, raw, 'false');
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (!blockerText) { output({ error: 'text required' }, raw); return; }
|
|
515
|
+
|
|
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
|
+
}
|
|
531
|
+
|
|
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');
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function cmdStateResolveBlocker(cwd, text, raw) {
|
|
552
|
+
const statePath = planningPaths(cwd).state;
|
|
553
|
+
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
554
|
+
if (!text) { output({ error: 'text required' }, raw); return; }
|
|
555
|
+
|
|
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
|
+
});
|
|
569
|
+
|
|
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}`);
|
|
578
|
+
}
|
|
579
|
+
return content;
|
|
580
|
+
}, cwd);
|
|
581
|
+
|
|
582
|
+
if (resolved) {
|
|
583
|
+
output({ resolved: true, blocker: text }, raw, 'true');
|
|
584
|
+
} else {
|
|
585
|
+
output({ resolved: false, reason: 'Blockers section not found in STATE.md' }, raw, 'false');
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function cmdStateRecordSession(cwd, options, raw) {
|
|
590
|
+
const statePath = planningPaths(cwd).state;
|
|
591
|
+
if (!fs.existsSync(statePath)) { output({ error: 'STATE.md not found' }, raw); return; }
|
|
592
|
+
|
|
593
|
+
const now = new Date().toISOString();
|
|
594
|
+
const updated = [];
|
|
595
|
+
|
|
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
|
+
}
|
|
609
|
+
|
|
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'); }
|
|
615
|
+
|
|
616
|
+
return content;
|
|
617
|
+
}, cwd);
|
|
618
|
+
|
|
619
|
+
if (updated.length > 0) {
|
|
620
|
+
output({ recorded: true, updated }, raw, 'true');
|
|
621
|
+
} else {
|
|
622
|
+
output({ recorded: false, reason: 'No session fields found in STATE.md' }, raw, 'false');
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function cmdStateSnapshot(cwd, raw) {
|
|
627
|
+
const statePath = planningPaths(cwd).state;
|
|
628
|
+
|
|
629
|
+
if (!fs.existsSync(statePath)) {
|
|
630
|
+
output({ error: 'STATE.md not found' }, raw);
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const content = fs.readFileSync(statePath, 'utf-8');
|
|
635
|
+
|
|
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');
|
|
666
|
+
|
|
667
|
+
// Parse numeric fields
|
|
668
|
+
const totalPhases = totalPhasesRaw ? parseInt(totalPhasesRaw, 10) : null;
|
|
669
|
+
const totalPlansInPhase = totalPlansRaw ? parseInt(totalPlansRaw, 10) : null;
|
|
670
|
+
const progressPercent = progressRaw ? parseInt(progressRaw.replace('%', ''), 10) : null;
|
|
671
|
+
|
|
672
|
+
// Extract decisions table
|
|
673
|
+
const decisions = [];
|
|
674
|
+
const decisionsMatch = body.match(/##\s*Decisions Made[\s\S]*?\n\|[^\n]+\n\|[-|\s]+\n([\s\S]*?)(?=\n##|\n$|$)/i);
|
|
675
|
+
if (decisionsMatch) {
|
|
676
|
+
const tableBody = decisionsMatch[1];
|
|
677
|
+
const rows = tableBody.trim().split('\n').filter(r => r.includes('|'));
|
|
678
|
+
for (const row of rows) {
|
|
679
|
+
const cells = row.split('|').map(c => c.trim()).filter(Boolean);
|
|
680
|
+
if (cells.length >= 3) {
|
|
681
|
+
decisions.push({
|
|
682
|
+
phase: cells[0],
|
|
683
|
+
summary: cells[1],
|
|
684
|
+
rationale: cells[2],
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Extract blockers list
|
|
691
|
+
const blockers = [];
|
|
692
|
+
const blockersMatch = body.match(/##\s*Blockers\s*\n([\s\S]*?)(?=\n##|$)/i);
|
|
693
|
+
if (blockersMatch) {
|
|
694
|
+
const blockersSection = blockersMatch[1];
|
|
695
|
+
const items = blockersSection.match(/^-\s+(.+)$/gm) || [];
|
|
696
|
+
for (const item of items) {
|
|
697
|
+
blockers.push(item.replace(/^-\s+/, '').trim());
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Extract session info
|
|
702
|
+
const session = {
|
|
703
|
+
last_date: null,
|
|
704
|
+
stopped_at: null,
|
|
705
|
+
resume_file: null,
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
const sessionMatch = body.match(/##\s*Session\s*\n([\s\S]*?)(?=\n##|$)/i);
|
|
709
|
+
if (sessionMatch) {
|
|
710
|
+
const sessionSection = sessionMatch[1];
|
|
711
|
+
const lastDateMatch = sessionSection.match(/\*\*Last Date:\*\*\s*(.+)/i)
|
|
712
|
+
|| sessionSection.match(/^Last Date:\s*(.+)/im);
|
|
713
|
+
const stoppedAtMatch = sessionSection.match(/\*\*Stopped At:\*\*\s*(.+)/i)
|
|
714
|
+
|| sessionSection.match(/^Stopped At:\s*(.+)/im);
|
|
715
|
+
const resumeFileMatch = sessionSection.match(/\*\*Resume File:\*\*\s*(.+)/i)
|
|
716
|
+
|| sessionSection.match(/^Resume File:\s*(.+)/im);
|
|
717
|
+
|
|
718
|
+
if (lastDateMatch) session.last_date = lastDateMatch[1].trim();
|
|
719
|
+
if (stoppedAtMatch) session.stopped_at = stoppedAtMatch[1].trim();
|
|
720
|
+
if (resumeFileMatch) session.resume_file = resumeFileMatch[1].trim();
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const result = {
|
|
724
|
+
current_phase: currentPhase,
|
|
725
|
+
current_phase_name: currentPhaseName,
|
|
726
|
+
total_phases: totalPhases,
|
|
727
|
+
current_plan: currentPlan,
|
|
728
|
+
total_plans_in_phase: totalPlansInPhase,
|
|
729
|
+
status,
|
|
730
|
+
progress_percent: progressPercent,
|
|
731
|
+
last_activity: lastActivity,
|
|
732
|
+
last_activity_desc: lastActivityDesc,
|
|
733
|
+
decisions,
|
|
734
|
+
blockers,
|
|
735
|
+
paused_at: pausedAt,
|
|
736
|
+
session,
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
output(result, raw);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// ─── State Frontmatter Sync ──────────────────────────────────────────────────
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Extract machine-readable fields from STATE.md markdown body and build
|
|
746
|
+
* a YAML frontmatter object. Allows hooks and scripts to read state
|
|
747
|
+
* reliably via `state json` instead of fragile regex parsing.
|
|
748
|
+
*/
|
|
749
|
+
function buildStateFrontmatter(bodyContent, cwd) {
|
|
750
|
+
const currentPhase = stateExtractField(bodyContent, 'Current Phase');
|
|
751
|
+
const currentPhaseName = stateExtractField(bodyContent, 'Current Phase Name');
|
|
752
|
+
const currentPlan = stateExtractField(bodyContent, 'Current Plan');
|
|
753
|
+
const totalPhasesRaw = stateExtractField(bodyContent, 'Total Phases');
|
|
754
|
+
const totalPlansRaw = stateExtractField(bodyContent, 'Total Plans in Phase');
|
|
755
|
+
const status = stateExtractField(bodyContent, 'Status');
|
|
756
|
+
const progressRaw = stateExtractField(bodyContent, 'Progress');
|
|
757
|
+
const lastActivity = stateExtractField(bodyContent, 'Last Activity');
|
|
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');
|
|
765
|
+
const pausedAt = stateExtractField(bodyContent, 'Paused At');
|
|
766
|
+
|
|
767
|
+
let milestone = null;
|
|
768
|
+
let milestoneName = null;
|
|
769
|
+
if (cwd) {
|
|
770
|
+
try {
|
|
771
|
+
const info = getMilestoneInfo(cwd);
|
|
772
|
+
milestone = info.version;
|
|
773
|
+
milestoneName = info.name;
|
|
774
|
+
} catch { /* intentionally empty */ }
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
let totalPhases = totalPhasesRaw ? parseInt(totalPhasesRaw, 10) : null;
|
|
778
|
+
let completedPhases = null;
|
|
779
|
+
let totalPlans = totalPlansRaw ? parseInt(totalPlansRaw, 10) : null;
|
|
780
|
+
let completedPlans = null;
|
|
781
|
+
|
|
782
|
+
if (cwd) {
|
|
783
|
+
try {
|
|
784
|
+
const phasesDir = planningPaths(cwd).phases;
|
|
785
|
+
if (fs.existsSync(phasesDir)) {
|
|
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);
|
|
838
|
+
}
|
|
839
|
+
totalPhases = cached.totalPhases;
|
|
840
|
+
completedPhases = cached.completedPhases;
|
|
841
|
+
totalPlans = cached.totalPlans;
|
|
842
|
+
completedPlans = cached.completedPlans;
|
|
843
|
+
}
|
|
844
|
+
} catch { /* intentionally empty */ }
|
|
845
|
+
}
|
|
846
|
+
|
|
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) {
|
|
854
|
+
const pctMatch = progressRaw.match(/(\d+)%/);
|
|
855
|
+
if (pctMatch) progressPercent = parseInt(pctMatch[1], 10);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
const normalizedStatus = normalizeStateStatus(status, pausedAt);
|
|
859
|
+
|
|
860
|
+
const fm = { gsd_state_version: '1.0' };
|
|
861
|
+
|
|
862
|
+
if (milestone) fm.milestone = milestone;
|
|
863
|
+
if (milestoneName) fm.milestone_name = milestoneName;
|
|
864
|
+
if (currentPhase) fm.current_phase = currentPhase;
|
|
865
|
+
if (currentPhaseName) fm.current_phase_name = currentPhaseName;
|
|
866
|
+
if (currentPlan) fm.current_plan = currentPlan;
|
|
867
|
+
fm.status = normalizedStatus;
|
|
868
|
+
if (stoppedAt) fm.stopped_at = stoppedAt;
|
|
869
|
+
if (pausedAt) fm.paused_at = pausedAt;
|
|
870
|
+
fm.last_updated = new Date().toISOString();
|
|
871
|
+
if (lastActivity) fm.last_activity = lastActivity;
|
|
872
|
+
|
|
873
|
+
const progress = {};
|
|
874
|
+
if (totalPhases !== null) progress.total_phases = totalPhases;
|
|
875
|
+
if (completedPhases !== null) progress.completed_phases = completedPhases;
|
|
876
|
+
if (totalPlans !== null) progress.total_plans = totalPlans;
|
|
877
|
+
if (completedPlans !== null) progress.completed_plans = completedPlans;
|
|
878
|
+
if (progressPercent !== null) progress.percent = progressPercent;
|
|
879
|
+
if (Object.keys(progress).length > 0) fm.progress = progress;
|
|
880
|
+
|
|
881
|
+
return fm;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
function stripFrontmatter(content) {
|
|
885
|
+
// Strip ALL frontmatter blocks at the start of the file.
|
|
886
|
+
// Handles CRLF line endings and multiple stacked blocks (corruption recovery).
|
|
887
|
+
// Greedy: keeps stripping ---...--- blocks separated by optional whitespace.
|
|
888
|
+
let result = content;
|
|
889
|
+
// eslint-disable-next-line no-constant-condition
|
|
890
|
+
while (true) {
|
|
891
|
+
const stripped = result.replace(/^\s*---\r?\n[\s\S]*?\r?\n---\s*/, '');
|
|
892
|
+
if (stripped === result) break;
|
|
893
|
+
result = stripped;
|
|
894
|
+
}
|
|
895
|
+
return result;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
function syncStateFrontmatter(content, cwd) {
|
|
899
|
+
// Read existing frontmatter BEFORE stripping — it may contain values
|
|
900
|
+
// that the body no longer has (e.g., Status field removed by an agent).
|
|
901
|
+
const existingFm = extractFrontmatter(content);
|
|
902
|
+
const body = stripFrontmatter(content);
|
|
903
|
+
const derivedFm = buildStateFrontmatter(body, cwd);
|
|
904
|
+
|
|
905
|
+
// Preserve existing frontmatter status when body-derived status is 'unknown'.
|
|
906
|
+
// This prevents a missing Status: field in the body from overwriting a
|
|
907
|
+
// previously valid status (e.g., 'executing' → 'unknown').
|
|
908
|
+
if (derivedFm.status === 'unknown' && existingFm.status && existingFm.status !== 'unknown') {
|
|
909
|
+
derivedFm.status = existingFm.status;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
const yamlStr = reconstructFrontmatter(derivedFm);
|
|
913
|
+
return `---\n${yamlStr}\n---\n\n${body}`;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// Transient errno codes that indicate a temporary filesystem condition under
|
|
917
|
+
// concurrent O_EXCL races — Docker overlay-fs (ENOENT/EINVAL/EIO), NFS
|
|
918
|
+
// (ESTALE), and OS-level interrupt/retry signals (EAGAIN/EINTR). These are
|
|
919
|
+
// recoverable; acquireStateLock retries instead of propagating them.
|
|
920
|
+
// Truly fatal codes (EMFILE, ENOSPC, EROFS, EACCES) are NOT in this set and
|
|
921
|
+
// will still throw immediately.
|
|
922
|
+
const ACQUIRE_LOCK_RETRY_ERRNOS = new Set([
|
|
923
|
+
'EPERM', // Windows / macOS AV scanner holds the file open during delete
|
|
924
|
+
'EBUSY', // Windows: file in use by another process
|
|
925
|
+
'EAGAIN', // POSIX: resource temporarily unavailable
|
|
926
|
+
'EINTR', // POSIX: syscall interrupted by signal
|
|
927
|
+
'EINVAL', // Docker overlay-fs: transient during concurrent O_EXCL creation
|
|
928
|
+
'EIO', // Docker overlay-fs / NFS: transient I/O error
|
|
929
|
+
'ENOENT', // Docker overlay-fs: parent dir transiently missing during race
|
|
930
|
+
'ESTALE', // NFS: stale file handle (self-resolves on retry)
|
|
931
|
+
]);
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* Acquire a lockfile for STATE.md operations.
|
|
935
|
+
* Returns the lock path for later release.
|
|
936
|
+
*/
|
|
937
|
+
function acquireStateLock(statePath) {
|
|
938
|
+
const lockPath = statePath + '.lock';
|
|
939
|
+
const retryDelay = 200; // ms
|
|
940
|
+
const staleThresholdMs = 10000;
|
|
941
|
+
const maxWaitMs = 30000;
|
|
942
|
+
const startedAt = Date.now();
|
|
943
|
+
|
|
944
|
+
// eslint-disable-next-line no-constant-condition
|
|
945
|
+
while (true) {
|
|
946
|
+
try {
|
|
947
|
+
const fd = fs.openSync(lockPath, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY);
|
|
948
|
+
fs.writeSync(fd, String(process.pid));
|
|
949
|
+
fs.closeSync(fd);
|
|
950
|
+
// Exit-time cleanup keeps a crashed locked region from leaving a stale file (#1916).
|
|
951
|
+
_heldStateLocks.add(lockPath);
|
|
952
|
+
return lockPath;
|
|
953
|
+
} catch (err) {
|
|
954
|
+
// Transient filesystem errors (Docker overlay-fs, NFS, OS signals, AV scanners)
|
|
955
|
+
// are recoverable — retry the acquisition loop rather than propagating.
|
|
956
|
+
// See ACQUIRE_LOCK_RETRY_ERRNOS for the full list and rationale.
|
|
957
|
+
if (ACQUIRE_LOCK_RETRY_ERRNOS.has(err.code)) { continue; }
|
|
958
|
+
if (err.code !== 'EEXIST') throw err; // propagate — silent bypass causes lost updates
|
|
959
|
+
// Only unlink a lock we did not place when it has crossed the staleness
|
|
960
|
+
// threshold (crashed holder). Nuking a fresh lock held by a slow-but-live
|
|
961
|
+
// writer causes lost updates (#3711 regression).
|
|
962
|
+
try {
|
|
963
|
+
const stat = fs.statSync(lockPath);
|
|
964
|
+
if (Date.now() - stat.mtimeMs > staleThresholdMs) {
|
|
965
|
+
try { fs.unlinkSync(lockPath); } catch { /* already gone */ }
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
} catch { continue; /* released between EEXIST and stat */ }
|
|
969
|
+
if (Date.now() - startedAt >= maxWaitMs) {
|
|
970
|
+
throw new Error(
|
|
971
|
+
'acquireStateLock: ' + lockPath + ' held by live process for ' +
|
|
972
|
+
(Date.now() - startedAt) + 'ms (exceeded ' + maxWaitMs + 'ms budget)'
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
const jitter = Math.floor(Math.random() * 50);
|
|
976
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, retryDelay + jitter);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function releaseStateLock(lockPath) {
|
|
982
|
+
_heldStateLocks.delete(lockPath);
|
|
983
|
+
try { fs.unlinkSync(lockPath); } catch { /* lock already gone */ }
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* Write STATE.md with synchronized YAML frontmatter.
|
|
988
|
+
* All STATE.md writes should use this instead of raw writeFileSync.
|
|
989
|
+
* Uses a simple lockfile to prevent parallel agents from overwriting
|
|
990
|
+
* each other's changes (race condition with read-modify-write cycle).
|
|
991
|
+
*/
|
|
992
|
+
function writeStateMd(statePath, content, cwd) {
|
|
993
|
+
// Invalidate disk scan cache before computing new frontmatter — the write
|
|
994
|
+
// may create new PLAN/SUMMARY files that buildStateFrontmatter must see.
|
|
995
|
+
// Safe for any calling pattern, not just short-lived CLI processes (#1967).
|
|
996
|
+
if (cwd) _diskScanCache.delete(cwd);
|
|
997
|
+
const synced = syncStateFrontmatter(content, cwd);
|
|
998
|
+
const lockPath = acquireStateLock(statePath);
|
|
999
|
+
try {
|
|
1000
|
+
platformWriteSync(statePath, synced);
|
|
1001
|
+
} finally {
|
|
1002
|
+
releaseStateLock(lockPath);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* Atomic read-modify-write for STATE.md.
|
|
1008
|
+
* Holds the lock across the entire read -> transform -> write cycle,
|
|
1009
|
+
* preventing the lost-update problem where two agents read the same
|
|
1010
|
+
* content and the second write clobbers the first.
|
|
1011
|
+
*
|
|
1012
|
+
* @param {string} statePath
|
|
1013
|
+
* @param {function} transformFn - (content: string) => string
|
|
1014
|
+
* @param {string} cwd
|
|
1015
|
+
* @param {{ resync?: boolean }} [options]
|
|
1016
|
+
* resync: when true (default) rebuilds the entire frontmatter from disk after
|
|
1017
|
+
* the transform. Pass { resync: false } for body-only updates (e.g. state.update
|
|
1018
|
+
* on a single field) that must not trample manually-curated cross-milestone
|
|
1019
|
+
* progress.* counters in the frontmatter (#3242 Bug A).
|
|
1020
|
+
* When resync is false, syncStateFrontmatter still runs to maintain/create the
|
|
1021
|
+
* frontmatter block, but any existing progress.* sub-keys are preserved from
|
|
1022
|
+
* the pre-transform file rather than being rebuilt from disk.
|
|
1023
|
+
*/
|
|
1024
|
+
function readModifyWriteStateMd(statePath, transformFn, cwd, options) {
|
|
1025
|
+
const resync = !options || options.resync !== false;
|
|
1026
|
+
const lockPath = acquireStateLock(statePath);
|
|
1027
|
+
try {
|
|
1028
|
+
const content = platformReadSync(statePath) || '';
|
|
1029
|
+
// Snapshot the existing progress block BEFORE the transform so we can
|
|
1030
|
+
// restore it when resync is false.
|
|
1031
|
+
const preFm = resync ? null : extractFrontmatter(content);
|
|
1032
|
+
const modified = transformFn(content);
|
|
1033
|
+
let synced = syncStateFrontmatter(modified, cwd);
|
|
1034
|
+
|
|
1035
|
+
if (!resync && preFm && preFm.progress) {
|
|
1036
|
+
// Re-apply the curated progress block that syncStateFrontmatter just
|
|
1037
|
+
// overwrote with disk-derived values. Only restore keys that were present
|
|
1038
|
+
// in the snapshot — this preserves any new non-progress frontmatter fields
|
|
1039
|
+
// (e.g., status, current_phase) that syncStateFrontmatter legitimately
|
|
1040
|
+
// derived from the updated body.
|
|
1041
|
+
const postFm = extractFrontmatter(synced);
|
|
1042
|
+
postFm.progress = preFm.progress;
|
|
1043
|
+
const yamlStr = reconstructFrontmatter(postFm);
|
|
1044
|
+
const body = stripFrontmatter(synced);
|
|
1045
|
+
synced = `---\n${yamlStr}\n---\n\n${body}`;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
platformWriteSync(statePath, synced);
|
|
1049
|
+
} finally {
|
|
1050
|
+
releaseStateLock(lockPath);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
function cmdStateJson(cwd, raw) {
|
|
1055
|
+
const statePath = planningPaths(cwd).state;
|
|
1056
|
+
if (!fs.existsSync(statePath)) {
|
|
1057
|
+
output({ error: 'STATE.md not found' }, raw, 'STATE.md not found');
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
const content = fs.readFileSync(statePath, 'utf-8');
|
|
1062
|
+
const existingFm = extractFrontmatter(content);
|
|
1063
|
+
const body = stripFrontmatter(content);
|
|
1064
|
+
|
|
1065
|
+
// Always rebuild from body + disk so progress counters reflect current state.
|
|
1066
|
+
// Returning cached frontmatter directly causes stale percent/completed_plans
|
|
1067
|
+
// when SUMMARY files were added after the last STATE.md write (#1589).
|
|
1068
|
+
const built = buildStateFrontmatter(body, cwd);
|
|
1069
|
+
|
|
1070
|
+
// Preserve frontmatter-only fields that cannot be recovered from the body.
|
|
1071
|
+
if (existingFm && existingFm.stopped_at && !built.stopped_at) {
|
|
1072
|
+
built.stopped_at = existingFm.stopped_at;
|
|
1073
|
+
}
|
|
1074
|
+
if (existingFm && existingFm.paused_at && !built.paused_at) {
|
|
1075
|
+
built.paused_at = existingFm.paused_at;
|
|
1076
|
+
}
|
|
1077
|
+
// Preserve existing status when body-derived status is 'unknown' (same logic as syncStateFrontmatter).
|
|
1078
|
+
if (built.status === 'unknown' && existingFm && existingFm.status && existingFm.status !== 'unknown') {
|
|
1079
|
+
built.status = existingFm.status;
|
|
1080
|
+
}
|
|
1081
|
+
// Preserve curated cross-milestone aggregates when local disk scanning sees
|
|
1082
|
+
// only a narrower realized subset (#3242 Bug A). Stale lower counters still
|
|
1083
|
+
// rebuild from disk because they do not exceed the derived scan.
|
|
1084
|
+
if (existingFm && shouldPreserveExistingProgress(existingFm.progress, built.progress)) {
|
|
1085
|
+
built.progress = normalizeProgressNumbers(existingFm.progress);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
output(built, raw, JSON.stringify(built, null, 2));
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* Update STATE.md when a new phase begins execution.
|
|
1093
|
+
* Updates body text fields (Current focus, Status, Last Activity, Current Position)
|
|
1094
|
+
* and synchronizes frontmatter via writeStateMd.
|
|
1095
|
+
* Fixes: #1102 (plan counts), #1103 (status/last_activity), #1104 (body text).
|
|
1096
|
+
*/
|
|
1097
|
+
function cmdStateBeginPhase(cwd, phaseNumber, phaseName, planCount, raw) {
|
|
1098
|
+
const statePath = planningPaths(cwd).state;
|
|
1099
|
+
if (!fs.existsSync(statePath)) {
|
|
1100
|
+
output({ error: 'STATE.md not found' }, raw);
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1105
|
+
const updated = [];
|
|
1106
|
+
|
|
1107
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
1108
|
+
// Idempotency guard (#3127): if the phase is already mid-flight, do NOT
|
|
1109
|
+
// overwrite execution-progress fields (Current Plan, plan body line,
|
|
1110
|
+
// Last Activity Description). Only update fields that are safe to
|
|
1111
|
+
// refresh on resume (Last Activity date, Status if inconsistent).
|
|
1112
|
+
// A phase is considered mid-flight when Status contains 'Executing Phase N'
|
|
1113
|
+
// for the current phase number.
|
|
1114
|
+
const currentStatus = stateExtractField(content, 'Status') || '';
|
|
1115
|
+
const isAlreadyExecuting = new RegExp(`Executing Phase\\s+${escapeRegex(String(phaseNumber))}\\b`, 'i').test(currentStatus);
|
|
1116
|
+
|
|
1117
|
+
// Update Status field
|
|
1118
|
+
const statusValue = `Executing Phase ${phaseNumber}`;
|
|
1119
|
+
let result = stateReplaceField(content, 'Status', statusValue);
|
|
1120
|
+
if (result) { content = result; updated.push('Status'); }
|
|
1121
|
+
|
|
1122
|
+
// Update Last Activity (safe to update on resume — tracks when execute-phase ran)
|
|
1123
|
+
result = stateReplaceField(content, 'Last Activity', today);
|
|
1124
|
+
if (result) { content = result; updated.push('Last Activity'); }
|
|
1125
|
+
|
|
1126
|
+
if (!isAlreadyExecuting) {
|
|
1127
|
+
// First-time execution: set all progress fields
|
|
1128
|
+
|
|
1129
|
+
// Update Last Activity Description
|
|
1130
|
+
const activityDesc = `Phase ${phaseNumber} execution started`;
|
|
1131
|
+
result = stateReplaceField(content, 'Last Activity Description', activityDesc);
|
|
1132
|
+
if (result) { content = result; updated.push('Last Activity Description'); }
|
|
1133
|
+
|
|
1134
|
+
// Update Current Phase
|
|
1135
|
+
result = stateReplaceField(content, 'Current Phase', String(phaseNumber));
|
|
1136
|
+
if (result) { content = result; updated.push('Current Phase'); }
|
|
1137
|
+
|
|
1138
|
+
// Update Current Phase Name
|
|
1139
|
+
if (phaseName) {
|
|
1140
|
+
result = stateReplaceField(content, 'Current Phase Name', phaseName);
|
|
1141
|
+
if (result) { content = result; updated.push('Current Phase Name'); }
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// Update Current Plan to 1 (starting from the first plan)
|
|
1145
|
+
result = stateReplaceField(content, 'Current Plan', '1');
|
|
1146
|
+
if (result) { content = result; updated.push('Current Plan'); }
|
|
1147
|
+
|
|
1148
|
+
// Update Total Plans in Phase
|
|
1149
|
+
if (planCount) {
|
|
1150
|
+
result = stateReplaceField(content, 'Total Plans in Phase', String(planCount));
|
|
1151
|
+
if (result) { content = result; updated.push('Total Plans in Phase'); }
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
// Update **Current focus:** body text line (#1104)
|
|
1155
|
+
const focusLabel = phaseName ? `Phase ${phaseNumber} — ${phaseName}` : `Phase ${phaseNumber}`;
|
|
1156
|
+
const focusPattern = /(\*\*Current focus:\*\*\s*).*/i;
|
|
1157
|
+
if (focusPattern.test(content)) {
|
|
1158
|
+
content = content.replace(focusPattern, (_match, prefix) => `${prefix}${focusLabel}`);
|
|
1159
|
+
updated.push('Current focus');
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// Update ## Current Position section (#1104, #1365)
|
|
1163
|
+
const positionPattern = /(##\s*Current Position\s*\n)([\s\S]*?)(?=\n##|$)/i;
|
|
1164
|
+
const positionMatch = content.match(positionPattern);
|
|
1165
|
+
if (positionMatch) {
|
|
1166
|
+
const header = positionMatch[1];
|
|
1167
|
+
let posBody = positionMatch[2];
|
|
1168
|
+
|
|
1169
|
+
// Update or insert Phase line
|
|
1170
|
+
const newPhase = `Phase: ${phaseNumber}${phaseName ? ` (${phaseName})` : ''} — EXECUTING`;
|
|
1171
|
+
if (/^Phase:/m.test(posBody)) {
|
|
1172
|
+
posBody = posBody.replace(/^Phase:.*$/m, newPhase);
|
|
1173
|
+
} else {
|
|
1174
|
+
posBody = newPhase + '\n' + posBody;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// Update or insert Plan line
|
|
1178
|
+
const newPlan = `Plan: 1 of ${planCount || '?'}`;
|
|
1179
|
+
if (/^Plan:/m.test(posBody)) {
|
|
1180
|
+
posBody = posBody.replace(/^Plan:.*$/m, newPlan);
|
|
1181
|
+
} else {
|
|
1182
|
+
posBody = posBody.replace(/^(Phase:.*$)/m, `$1\n${newPlan}`);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// Update Status line if present
|
|
1186
|
+
const newStatus = `Status: Executing Phase ${phaseNumber}`;
|
|
1187
|
+
if (/^Status:/m.test(posBody)) {
|
|
1188
|
+
posBody = posBody.replace(/^Status:.*$/m, newStatus);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
// Update Last activity line if present
|
|
1192
|
+
const newActivity = `Last activity: ${today} -- Phase ${phaseNumber} execution started`;
|
|
1193
|
+
if (/^Last activity:/im.test(posBody)) {
|
|
1194
|
+
posBody = posBody.replace(/^Last activity:.*$/im, newActivity);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
content = content.replace(positionPattern, () => `${header}${posBody}`);
|
|
1198
|
+
updated.push('Current Position');
|
|
1199
|
+
}
|
|
1200
|
+
} else {
|
|
1201
|
+
// Resume path: only update Last activity timestamp in Current Position
|
|
1202
|
+
// (do not touch Plan:, stopped_at, progress.percent, or plan counter)
|
|
1203
|
+
const positionPattern = /(##\s*Current Position\s*\n)([\s\S]*?)(?=\n##|$)/i;
|
|
1204
|
+
const positionMatch = content.match(positionPattern);
|
|
1205
|
+
if (positionMatch) {
|
|
1206
|
+
const header = positionMatch[1];
|
|
1207
|
+
let posBody = positionMatch[2];
|
|
1208
|
+
const resumeActivity = `Last activity: ${today} -- Phase ${phaseNumber} execution resumed (wave continue)`;
|
|
1209
|
+
if (/^Last activity:/im.test(posBody)) {
|
|
1210
|
+
posBody = posBody.replace(/^Last activity:.*$/im, resumeActivity);
|
|
1211
|
+
content = content.replace(positionPattern, () => `${header}${posBody}`);
|
|
1212
|
+
updated.push('Last activity (resume)');
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
return content;
|
|
1218
|
+
}, cwd);
|
|
1219
|
+
|
|
1220
|
+
output({ updated, phase: phaseNumber, phase_name: phaseName || null, plan_count: planCount || null }, raw, updated.length > 0 ? 'true' : 'false');
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* Write a WAITING.json signal file when GSD hits a decision point.
|
|
1225
|
+
* External watchers (fswatch, polling, orchestrators) can detect this.
|
|
1226
|
+
* File is written to .planning/WAITING.json (or .gsd/WAITING.json if .gsd exists).
|
|
1227
|
+
* Fixes #1034.
|
|
1228
|
+
*/
|
|
1229
|
+
function cmdSignalWaiting(cwd, type, question, options, phase, raw) {
|
|
1230
|
+
const gsdDir = fs.existsSync(path.join(cwd, '.gsd')) ? path.join(cwd, '.gsd') : planningDir(cwd);
|
|
1231
|
+
const waitingPath = path.join(gsdDir, 'WAITING.json');
|
|
1232
|
+
|
|
1233
|
+
const signal = {
|
|
1234
|
+
status: 'waiting',
|
|
1235
|
+
type: type || 'decision_point',
|
|
1236
|
+
question: question || null,
|
|
1237
|
+
options: options ? options.split('|').map(o => o.trim()) : [],
|
|
1238
|
+
since: new Date().toISOString(),
|
|
1239
|
+
phase: phase || null,
|
|
1240
|
+
};
|
|
1241
|
+
|
|
1242
|
+
try {
|
|
1243
|
+
platformEnsureDir(gsdDir);
|
|
1244
|
+
platformWriteSync(waitingPath, JSON.stringify(signal, null, 2));
|
|
1245
|
+
output({ signaled: true, path: waitingPath }, raw, 'true');
|
|
1246
|
+
} catch (e) {
|
|
1247
|
+
output({ signaled: false, error: e.message }, raw, 'false');
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* Remove the WAITING.json signal file when user answers and agent resumes.
|
|
1253
|
+
*/
|
|
1254
|
+
function cmdSignalResume(cwd, raw) {
|
|
1255
|
+
const paths = [
|
|
1256
|
+
path.join(cwd, '.gsd', 'WAITING.json'),
|
|
1257
|
+
path.join(planningDir(cwd), 'WAITING.json'),
|
|
1258
|
+
];
|
|
1259
|
+
|
|
1260
|
+
let removed = false;
|
|
1261
|
+
for (const p of paths) {
|
|
1262
|
+
if (fs.existsSync(p)) {
|
|
1263
|
+
try { fs.unlinkSync(p); removed = true; } catch {}
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
output({ resumed: true, removed }, raw, removed ? 'true' : 'false');
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// ─── Gate Functions (STATE.md consistency enforcement) ────────────────────────
|
|
1271
|
+
|
|
1272
|
+
/**
|
|
1273
|
+
* Update the ## Performance Metrics section in STATE.md content.
|
|
1274
|
+
* Increments Velocity totals and upserts a By Phase table row.
|
|
1275
|
+
* Returns modified content string.
|
|
1276
|
+
*/
|
|
1277
|
+
function updatePerformanceMetricsSection(content, cwd, phaseNum, planCount, summaryCount) {
|
|
1278
|
+
// Update Velocity: Total plans completed
|
|
1279
|
+
const totalMatch = content.match(/Total plans completed:\s*(\d+|\[N\])/);
|
|
1280
|
+
const prevTotal = totalMatch && totalMatch[1] !== '[N]' ? parseInt(totalMatch[1], 10) : 0;
|
|
1281
|
+
const newTotal = prevTotal + summaryCount;
|
|
1282
|
+
content = content.replace(
|
|
1283
|
+
/Total plans completed:\s*(\d+|\[N\])/,
|
|
1284
|
+
`Total plans completed: ${newTotal}`
|
|
1285
|
+
);
|
|
1286
|
+
|
|
1287
|
+
// Update By Phase table — upsert row for this phase
|
|
1288
|
+
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;
|
|
1289
|
+
const byPhaseMatch = content.match(byPhaseTablePattern);
|
|
1290
|
+
if (byPhaseMatch) {
|
|
1291
|
+
let tableBody = byPhaseMatch[2].trim();
|
|
1292
|
+
const phaseRowPattern = new RegExp(`^\\|\\s*${escapeRegex(String(phaseNum))}\\s*\\|.*$`, 'm');
|
|
1293
|
+
const newRow = `| ${phaseNum} | ${summaryCount} | - | - |`;
|
|
1294
|
+
|
|
1295
|
+
if (phaseRowPattern.test(tableBody)) {
|
|
1296
|
+
// Update existing row
|
|
1297
|
+
tableBody = tableBody.replace(phaseRowPattern, newRow);
|
|
1298
|
+
} else {
|
|
1299
|
+
// Remove placeholder row and add new row
|
|
1300
|
+
tableBody = tableBody.replace(/^\|\s*-\s*\|\s*-\s*\|\s*-\s*\|\s*-\s*\|$/m, '').trim();
|
|
1301
|
+
tableBody = tableBody ? tableBody + '\n' + newRow : newRow;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
content = content.replace(byPhaseTablePattern, (_match, tableHeader) => `${tableHeader}${tableBody}\n`);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
return content;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* Gate 3a: Record state after plan-phase completes.
|
|
1312
|
+
* Updates Status to "Ready to execute", Total Plans, Last Activity.
|
|
1313
|
+
*/
|
|
1314
|
+
function cmdStatePlannedPhase(cwd, phaseNumber, planCount, raw) {
|
|
1315
|
+
const statePath = planningPaths(cwd).state;
|
|
1316
|
+
if (!fs.existsSync(statePath)) {
|
|
1317
|
+
output({ error: 'STATE.md not found' }, raw);
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
let content = fs.readFileSync(statePath, 'utf-8');
|
|
1322
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1323
|
+
const updated = [];
|
|
1324
|
+
|
|
1325
|
+
// Update Status
|
|
1326
|
+
let result = stateReplaceField(content, 'Status', 'Ready to execute');
|
|
1327
|
+
if (result) { content = result; updated.push('Status'); }
|
|
1328
|
+
|
|
1329
|
+
// Update Total Plans in Phase
|
|
1330
|
+
if (planCount !== null && planCount !== undefined) {
|
|
1331
|
+
result = stateReplaceField(content, 'Total Plans in Phase', String(planCount));
|
|
1332
|
+
if (result) { content = result; updated.push('Total Plans in Phase'); }
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
// Update Last Activity
|
|
1336
|
+
result = stateReplaceField(content, 'Last Activity', today);
|
|
1337
|
+
if (result) { content = result; updated.push('Last Activity'); }
|
|
1338
|
+
|
|
1339
|
+
// Update Last Activity Description
|
|
1340
|
+
result = stateReplaceField(content, 'Last Activity Description', `Phase ${phaseNumber} planning complete — ${planCount || '?'} plans ready`);
|
|
1341
|
+
if (result) { content = result; updated.push('Last Activity Description'); }
|
|
1342
|
+
|
|
1343
|
+
// Update Current Position section
|
|
1344
|
+
content = updateCurrentPositionFields(content, {
|
|
1345
|
+
status: 'Ready to execute',
|
|
1346
|
+
lastActivity: `${today} -- Phase ${phaseNumber} planning complete`,
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
if (updated.length > 0) {
|
|
1350
|
+
writeStateMd(statePath, content, cwd);
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
output({ updated, phase: phaseNumber, plan_count: planCount }, raw, updated.length > 0 ? 'true' : 'false');
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
/**
|
|
1357
|
+
* Bug #2630: reset STATE.md for a new milestone cycle.
|
|
1358
|
+
* Stomps frontmatter milestone/milestone_name/status/progress AND rewrites
|
|
1359
|
+
* the Current Position body. Preserves Accumulated Context.
|
|
1360
|
+
* Symmetric with the SDK `stateMilestoneSwitch` handler.
|
|
1361
|
+
*/
|
|
1362
|
+
function cmdStateMilestoneSwitch(cwd, version, name, raw) {
|
|
1363
|
+
if (!version || !String(version).trim()) {
|
|
1364
|
+
output({ error: 'milestone required (--milestone <vX.Y>)' }, raw);
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
const resolvedName = (name && String(name).trim()) || 'milestone';
|
|
1368
|
+
const statePath = planningPaths(cwd).state;
|
|
1369
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1370
|
+
|
|
1371
|
+
const lockPath = acquireStateLock(statePath);
|
|
1372
|
+
try {
|
|
1373
|
+
const content = platformReadSync(statePath) || '';
|
|
1374
|
+
const existingFm = extractFrontmatter(content);
|
|
1375
|
+
const body = stripFrontmatter(content);
|
|
1376
|
+
|
|
1377
|
+
const positionPattern = /(##\s*Current Position\s*\n)([\s\S]*?)(?=\n##|$)/i;
|
|
1378
|
+
const resetPositionBody =
|
|
1379
|
+
`\nPhase: Not started (defining requirements)\n` +
|
|
1380
|
+
`Plan: —\n` +
|
|
1381
|
+
`Status: Defining requirements\n` +
|
|
1382
|
+
`Last activity: ${today} — Milestone ${version} started\n\n`;
|
|
1383
|
+
let newBody;
|
|
1384
|
+
if (positionPattern.test(body)) {
|
|
1385
|
+
newBody = body.replace(positionPattern, (_m, header) => `${header}${resetPositionBody}`);
|
|
1386
|
+
} else {
|
|
1387
|
+
const preface = body.trim().length > 0 ? body : '# Project State\n';
|
|
1388
|
+
newBody = `${preface.trimEnd()}\n\n## Current Position\n${resetPositionBody}`;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
const fm = {
|
|
1392
|
+
gsd_state_version: existingFm.gsd_state_version || '1.0',
|
|
1393
|
+
milestone: version,
|
|
1394
|
+
milestone_name: resolvedName,
|
|
1395
|
+
status: 'planning',
|
|
1396
|
+
last_updated: new Date().toISOString(),
|
|
1397
|
+
last_activity: today,
|
|
1398
|
+
progress: {
|
|
1399
|
+
total_phases: 0,
|
|
1400
|
+
completed_phases: 0,
|
|
1401
|
+
total_plans: 0,
|
|
1402
|
+
completed_plans: 0,
|
|
1403
|
+
percent: 0,
|
|
1404
|
+
},
|
|
1405
|
+
};
|
|
1406
|
+
|
|
1407
|
+
const yamlStr = reconstructFrontmatter(fm);
|
|
1408
|
+
const assembled = `---\n${yamlStr}\n---\n\n${newBody.replace(/^\n+/, '')}`;
|
|
1409
|
+
platformWriteSync(statePath, assembled);
|
|
1410
|
+
output(
|
|
1411
|
+
{ switched: true, version, name: resolvedName, status: 'planning' },
|
|
1412
|
+
raw,
|
|
1413
|
+
'true',
|
|
1414
|
+
);
|
|
1415
|
+
} finally {
|
|
1416
|
+
releaseStateLock(lockPath);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
/**
|
|
1421
|
+
* Gate 1: Validate STATE.md against filesystem.
|
|
1422
|
+
* Returns { valid, warnings, drift } JSON.
|
|
1423
|
+
*/
|
|
1424
|
+
function cmdStateValidate(cwd, raw) {
|
|
1425
|
+
const statePath = planningPaths(cwd).state;
|
|
1426
|
+
if (!fs.existsSync(statePath)) {
|
|
1427
|
+
output({ error: 'STATE.md not found' }, raw);
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
const content = fs.readFileSync(statePath, 'utf-8');
|
|
1432
|
+
const warnings = [];
|
|
1433
|
+
const drift = {};
|
|
1434
|
+
|
|
1435
|
+
const status = stateExtractField(content, 'Status') || '';
|
|
1436
|
+
const currentPhase = stateExtractField(content, 'Current Phase');
|
|
1437
|
+
const totalPlansRaw = stateExtractField(content, 'Total Plans in Phase');
|
|
1438
|
+
const totalPlansInPhase = totalPlansRaw ? parseInt(totalPlansRaw, 10) : null;
|
|
1439
|
+
|
|
1440
|
+
const phasesDir = planningPaths(cwd).phases;
|
|
1441
|
+
|
|
1442
|
+
// Scan disk for current phase
|
|
1443
|
+
if (currentPhase && fs.existsSync(phasesDir)) {
|
|
1444
|
+
const normalized = currentPhase.replace(/\s+of\s+\d+.*/, '').trim();
|
|
1445
|
+
try {
|
|
1446
|
+
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
|
1447
|
+
const phaseDir = entries.find(e => e.isDirectory() && e.name.startsWith(normalized.replace(/^0+/, '').padStart(2, '0')));
|
|
1448
|
+
if (phaseDir) {
|
|
1449
|
+
const phaseDirPath = path.join(phasesDir, phaseDir.name);
|
|
1450
|
+
const { planCount: diskPlans, summaryCount: diskSummaries } = scanPhasePlans(phaseDirPath);
|
|
1451
|
+
|
|
1452
|
+
// Check plan count mismatch
|
|
1453
|
+
if (totalPlansInPhase !== null && diskPlans !== totalPlansInPhase) {
|
|
1454
|
+
warnings.push(`Plan count mismatch: STATE.md says ${totalPlansInPhase} plans, disk has ${diskPlans}`);
|
|
1455
|
+
drift.plan_count = { state: totalPlansInPhase, disk: diskPlans };
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
// Check for VERIFICATION.md
|
|
1459
|
+
const files = fs.readdirSync(phaseDirPath);
|
|
1460
|
+
const verificationFiles = files.filter(f => f.includes('VERIFICATION') && f.endsWith('.md'));
|
|
1461
|
+
for (const vf of verificationFiles) {
|
|
1462
|
+
try {
|
|
1463
|
+
const vContent = fs.readFileSync(path.join(phaseDirPath, vf), 'utf-8');
|
|
1464
|
+
if (/status:\s*passed/i.test(vContent) && /executing/i.test(status)) {
|
|
1465
|
+
warnings.push(`Status drift: STATE.md says "${status}" but ${vf} shows verification passed — phase may be complete`);
|
|
1466
|
+
drift.verification_status = { state_status: status, verification: 'passed' };
|
|
1467
|
+
}
|
|
1468
|
+
} catch { /* intentionally empty */ }
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
// Check if all plans have summaries but status still says executing
|
|
1472
|
+
if (diskPlans > 0 && diskSummaries >= diskPlans && /executing/i.test(status)) {
|
|
1473
|
+
// Only warn if no verification exists (if verification passed, the above warning covers it)
|
|
1474
|
+
if (verificationFiles.length === 0) {
|
|
1475
|
+
warnings.push(`All ${diskPlans} plans have summaries but status is still "${status}" — phase may be ready for verification`);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
} catch { /* intentionally empty */ }
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const valid = warnings.length === 0;
|
|
1483
|
+
output({ valid, warnings, drift }, raw);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
/**
|
|
1487
|
+
* Gate 2: Sync STATE.md from filesystem ground truth.
|
|
1488
|
+
* Scans phase dirs, reconstructs counters, progress, metrics.
|
|
1489
|
+
* Supports --verify for dry-run mode.
|
|
1490
|
+
*/
|
|
1491
|
+
function cmdStateSync(cwd, options, raw) {
|
|
1492
|
+
const statePath = planningPaths(cwd).state;
|
|
1493
|
+
if (!fs.existsSync(statePath)) {
|
|
1494
|
+
output({ error: 'STATE.md not found' }, raw);
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
const verify = options && options.verify;
|
|
1499
|
+
const content = fs.readFileSync(statePath, 'utf-8');
|
|
1500
|
+
const changes = [];
|
|
1501
|
+
let modified = content;
|
|
1502
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1503
|
+
|
|
1504
|
+
const phasesDir = planningPaths(cwd).phases;
|
|
1505
|
+
if (!fs.existsSync(phasesDir)) {
|
|
1506
|
+
output({ synced: true, changes: [], dry_run: !!verify }, raw);
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
// Scan all phases
|
|
1511
|
+
let entries;
|
|
1512
|
+
try {
|
|
1513
|
+
entries = fs.readdirSync(phasesDir, { withFileTypes: true })
|
|
1514
|
+
.filter(e => e.isDirectory())
|
|
1515
|
+
.map(e => e.name)
|
|
1516
|
+
.sort();
|
|
1517
|
+
} catch {
|
|
1518
|
+
output({ synced: true, changes: [], dry_run: !!verify }, raw);
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
let totalDiskPlans = 0;
|
|
1523
|
+
let totalDiskSummaries = 0;
|
|
1524
|
+
let diskCompletedPhases = 0;
|
|
1525
|
+
let highestIncompletePhase = null;
|
|
1526
|
+
let highestIncompletePhaseNum = null;
|
|
1527
|
+
let highestIncompletePhaseplanCount = 0;
|
|
1528
|
+
let highestIncompletePhaseSummaryCount = 0;
|
|
1529
|
+
|
|
1530
|
+
for (const dir of entries) {
|
|
1531
|
+
const dirPath = path.join(phasesDir, dir);
|
|
1532
|
+
const { planCount: plans, summaryCount: summaries, completed } = scanPhasePlans(dirPath);
|
|
1533
|
+
totalDiskPlans += plans;
|
|
1534
|
+
totalDiskSummaries += summaries;
|
|
1535
|
+
if (completed) diskCompletedPhases++;
|
|
1536
|
+
|
|
1537
|
+
// Track the highest phase with incomplete plans (or any plans)
|
|
1538
|
+
const phaseMatch = dir.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);
|
|
1539
|
+
if (phaseMatch && plans > 0) {
|
|
1540
|
+
if (summaries < plans) {
|
|
1541
|
+
// Incomplete phase — this is likely the current one
|
|
1542
|
+
highestIncompletePhase = dir;
|
|
1543
|
+
highestIncompletePhaseNum = phaseMatch[1];
|
|
1544
|
+
highestIncompletePhaseplanCount = plans;
|
|
1545
|
+
highestIncompletePhaseSummaryCount = summaries;
|
|
1546
|
+
} else if (!highestIncompletePhase) {
|
|
1547
|
+
// All complete, track as potential current
|
|
1548
|
+
highestIncompletePhase = dir;
|
|
1549
|
+
highestIncompletePhaseNum = phaseMatch[1];
|
|
1550
|
+
highestIncompletePhaseplanCount = plans;
|
|
1551
|
+
highestIncompletePhaseSummaryCount = summaries;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// Determine total phases from ROADMAP (may be larger than realized disk dirs).
|
|
1557
|
+
// Mirrors the logic in buildStateFrontmatter so both report consistent percents (#3242 Bug B).
|
|
1558
|
+
let syncTotalPhases = null;
|
|
1559
|
+
try {
|
|
1560
|
+
const isDirInMilestone = getMilestonePhaseFilter(cwd);
|
|
1561
|
+
if (isDirInMilestone.phaseCount > 0) {
|
|
1562
|
+
syncTotalPhases = Math.max(entries.length, isDirInMilestone.phaseCount);
|
|
1563
|
+
} else {
|
|
1564
|
+
syncTotalPhases = entries.length;
|
|
1565
|
+
}
|
|
1566
|
+
} catch { /* intentionally empty */ }
|
|
1567
|
+
|
|
1568
|
+
// Sync Total Plans in Phase
|
|
1569
|
+
if (highestIncompletePhase) {
|
|
1570
|
+
const currentPlansField = stateExtractField(modified, 'Total Plans in Phase');
|
|
1571
|
+
if (currentPlansField && parseInt(currentPlansField, 10) !== highestIncompletePhaseplanCount) {
|
|
1572
|
+
changes.push(`Total Plans in Phase: ${currentPlansField} -> ${highestIncompletePhaseplanCount}`);
|
|
1573
|
+
const result = stateReplaceField(modified, 'Total Plans in Phase', String(highestIncompletePhaseplanCount));
|
|
1574
|
+
if (result) modified = result;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
// Sync Progress — use shared helper so formula stays in one place (#3242 Bug B).
|
|
1579
|
+
// computeProgressPercent applies min(plan_fraction, phase_fraction) so unrealised
|
|
1580
|
+
// ROADMAP phases cap the reported percent rather than allowing a false 100%.
|
|
1581
|
+
const percent = (() => {
|
|
1582
|
+
const p = computeProgressPercent(totalDiskSummaries, totalDiskPlans, diskCompletedPhases, syncTotalPhases);
|
|
1583
|
+
return p !== null ? p : 0;
|
|
1584
|
+
})();
|
|
1585
|
+
const currentProgress = stateExtractField(modified, 'Progress');
|
|
1586
|
+
if (currentProgress) {
|
|
1587
|
+
const currentPercent = parseInt(currentProgress.replace(/[^\d]/g, ''), 10);
|
|
1588
|
+
if (currentPercent !== percent) {
|
|
1589
|
+
const barWidth = 10;
|
|
1590
|
+
const filled = Math.round(percent / 100 * barWidth);
|
|
1591
|
+
const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(barWidth - filled);
|
|
1592
|
+
const progressStr = `[${bar}] ${percent}%`;
|
|
1593
|
+
changes.push(`Progress: ${currentProgress} -> ${progressStr}`);
|
|
1594
|
+
const result = stateReplaceField(modified, 'Progress', progressStr);
|
|
1595
|
+
if (result) modified = result;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// Sync Last Activity
|
|
1600
|
+
const result = stateReplaceField(modified, 'Last Activity', today);
|
|
1601
|
+
if (result) {
|
|
1602
|
+
const oldActivity = stateExtractField(modified, 'Last Activity');
|
|
1603
|
+
if (oldActivity !== today) {
|
|
1604
|
+
changes.push(`Last Activity: ${oldActivity} -> ${today}`);
|
|
1605
|
+
}
|
|
1606
|
+
modified = result;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
if (verify) {
|
|
1610
|
+
output({ synced: false, changes, dry_run: true }, raw);
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
if (changes.length > 0 || modified !== content) {
|
|
1615
|
+
writeStateMd(statePath, modified, cwd);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
output({ synced: true, changes, dry_run: false }, raw);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/**
|
|
1622
|
+
* Prune old entries from STATE.md sections that grow unboundedly (#1970).
|
|
1623
|
+
* Moves decisions, recently-completed summaries, and resolved blockers
|
|
1624
|
+
* older than keepRecent phases to STATE-ARCHIVE.md.
|
|
1625
|
+
*
|
|
1626
|
+
* Options:
|
|
1627
|
+
* keepRecent: number of recent phases to retain (default: 3)
|
|
1628
|
+
* dryRun: if true, return what would be pruned without modifying STATE.md
|
|
1629
|
+
*/
|
|
1630
|
+
function cmdStatePrune(cwd, options, raw) {
|
|
1631
|
+
const silent = !!options.silent;
|
|
1632
|
+
const emit = silent ? () => {} : (result, r, v) => output(result, r, v);
|
|
1633
|
+
const statePath = planningPaths(cwd).state;
|
|
1634
|
+
if (!fs.existsSync(statePath)) { emit({ error: 'STATE.md not found' }, raw); return; }
|
|
1635
|
+
|
|
1636
|
+
const keepRecent = parseInt(options.keepRecent, 10) || 3;
|
|
1637
|
+
const dryRun = !!options.dryRun;
|
|
1638
|
+
const currentPhaseRaw = stateExtractField(fs.readFileSync(statePath, 'utf-8'), 'Current Phase');
|
|
1639
|
+
const currentPhase = parseInt(currentPhaseRaw, 10) || 0;
|
|
1640
|
+
const cutoff = currentPhase - keepRecent;
|
|
1641
|
+
|
|
1642
|
+
if (cutoff <= 0) {
|
|
1643
|
+
emit({ pruned: false, reason: `Only ${currentPhase} phases — nothing to prune with --keep-recent ${keepRecent}` }, raw, 'false');
|
|
1644
|
+
return;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
const archivePath = path.join(path.dirname(statePath), 'STATE-ARCHIVE.md');
|
|
1648
|
+
const archived = [];
|
|
1649
|
+
|
|
1650
|
+
// Shared pruning logic applied to both dry-run and real passes.
|
|
1651
|
+
// Returns { newContent, archivedSections }.
|
|
1652
|
+
function prunePass(content) {
|
|
1653
|
+
const sections = [];
|
|
1654
|
+
|
|
1655
|
+
// Prune Decisions section: entries like "- [Phase N]: ..."
|
|
1656
|
+
const decisionPattern = /(###?\s*(?:Decisions|Decisions Made|Accumulated.*Decisions)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
|
|
1657
|
+
const decMatch = content.match(decisionPattern);
|
|
1658
|
+
if (decMatch) {
|
|
1659
|
+
const lines = decMatch[2].split('\n');
|
|
1660
|
+
const keep = [];
|
|
1661
|
+
const archive = [];
|
|
1662
|
+
for (const line of lines) {
|
|
1663
|
+
const phaseMatch = line.match(/^\s*-\s*\[Phase\s+(\d+)/i);
|
|
1664
|
+
if (phaseMatch && parseInt(phaseMatch[1], 10) <= cutoff) {
|
|
1665
|
+
archive.push(line);
|
|
1666
|
+
} else {
|
|
1667
|
+
keep.push(line);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
if (archive.length > 0) {
|
|
1671
|
+
sections.push({ section: 'Decisions', count: archive.length, lines: archive });
|
|
1672
|
+
content = content.replace(decisionPattern, (_m, header) => `${header}${keep.join('\n')}`);
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
// Prune Recently Completed section: entries mentioning phase numbers
|
|
1677
|
+
const recentPattern = /(###?\s*Recently Completed\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
|
|
1678
|
+
const recMatch = content.match(recentPattern);
|
|
1679
|
+
if (recMatch) {
|
|
1680
|
+
const lines = recMatch[2].split('\n');
|
|
1681
|
+
const keep = [];
|
|
1682
|
+
const archive = [];
|
|
1683
|
+
for (const line of lines) {
|
|
1684
|
+
const phaseMatch = line.match(/Phase\s+(\d+)/i);
|
|
1685
|
+
if (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: 'Recently Completed', count: archive.length, lines: archive });
|
|
1693
|
+
content = content.replace(recentPattern, (_m, header) => `${header}${keep.join('\n')}`);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// Prune resolved blockers: lines marked as resolved (strikethrough ~~text~~
|
|
1698
|
+
// or "[RESOLVED]" prefix) with a phase reference older than cutoff
|
|
1699
|
+
const blockersPattern = /(###?\s*(?:Blockers|Blockers\/Concerns|Blockers\s*&\s*Concerns)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
|
|
1700
|
+
const blockersMatch = content.match(blockersPattern);
|
|
1701
|
+
if (blockersMatch) {
|
|
1702
|
+
const lines = blockersMatch[2].split('\n');
|
|
1703
|
+
const keep = [];
|
|
1704
|
+
const archive = [];
|
|
1705
|
+
for (const line of lines) {
|
|
1706
|
+
const isResolved = /~~.*~~|\[RESOLVED\]/i.test(line);
|
|
1707
|
+
const phaseMatch = line.match(/Phase\s+(\d+)/i);
|
|
1708
|
+
if (isResolved && phaseMatch && parseInt(phaseMatch[1], 10) <= cutoff) {
|
|
1709
|
+
archive.push(line);
|
|
1710
|
+
} else {
|
|
1711
|
+
keep.push(line);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
if (archive.length > 0) {
|
|
1715
|
+
sections.push({ section: 'Blockers (resolved)', count: archive.length, lines: archive });
|
|
1716
|
+
content = content.replace(blockersPattern, (_m, header) => `${header}${keep.join('\n')}`);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
// Prune Performance Metrics table rows: keep only rows for phases > cutoff.
|
|
1721
|
+
// Preserves header rows (| Phase | ... and |---|...) and any prose around the table.
|
|
1722
|
+
const metricsPattern = /(###?\s*Performance Metrics\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
|
|
1723
|
+
const metricsMatch = content.match(metricsPattern);
|
|
1724
|
+
if (metricsMatch) {
|
|
1725
|
+
const sectionLines = metricsMatch[2].split('\n');
|
|
1726
|
+
const keep = [];
|
|
1727
|
+
const archive = [];
|
|
1728
|
+
for (const line of sectionLines) {
|
|
1729
|
+
// Table data row: starts with | followed by a number (phase)
|
|
1730
|
+
const tableRowMatch = line.match(/^\|\s*(\d+)\s*\|/);
|
|
1731
|
+
if (tableRowMatch) {
|
|
1732
|
+
const rowPhase = parseInt(tableRowMatch[1], 10);
|
|
1733
|
+
if (rowPhase <= cutoff) {
|
|
1734
|
+
archive.push(line);
|
|
1735
|
+
} else {
|
|
1736
|
+
keep.push(line);
|
|
1737
|
+
}
|
|
1738
|
+
} else {
|
|
1739
|
+
// Header row, separator row, or prose — always keep
|
|
1740
|
+
keep.push(line);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
if (archive.length > 0) {
|
|
1744
|
+
sections.push({ section: 'Performance Metrics', count: archive.length, lines: archive });
|
|
1745
|
+
content = content.replace(metricsPattern, (_m, header) => `${header}${keep.join('\n')}`);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
return { newContent: content, archivedSections: sections };
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
if (dryRun) {
|
|
1753
|
+
// Dry-run: compute what would be pruned without writing anything
|
|
1754
|
+
const content = fs.readFileSync(statePath, 'utf-8');
|
|
1755
|
+
const result = prunePass(content);
|
|
1756
|
+
const totalPruned = result.archivedSections.reduce((sum, s) => sum + s.count, 0);
|
|
1757
|
+
emit({
|
|
1758
|
+
pruned: false,
|
|
1759
|
+
dry_run: true,
|
|
1760
|
+
cutoff_phase: cutoff,
|
|
1761
|
+
keep_recent: keepRecent,
|
|
1762
|
+
sections: result.archivedSections.map(s => ({ section: s.section, entries_would_archive: s.count })),
|
|
1763
|
+
total_would_archive: totalPruned,
|
|
1764
|
+
note: totalPruned > 0 ? 'Run without --dry-run to actually prune' : 'Nothing to prune',
|
|
1765
|
+
}, raw, totalPruned > 0 ? 'true' : 'false');
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
1770
|
+
const result = prunePass(content);
|
|
1771
|
+
archived.push(...result.archivedSections);
|
|
1772
|
+
return result.newContent;
|
|
1773
|
+
}, cwd);
|
|
1774
|
+
|
|
1775
|
+
// Write archived entries to STATE-ARCHIVE.md
|
|
1776
|
+
if (archived.length > 0) {
|
|
1777
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
|
1778
|
+
let archiveContent = platformReadSync(archivePath);
|
|
1779
|
+
if (archiveContent === null) {
|
|
1780
|
+
archiveContent = '# STATE Archive\n\nPruned entries from STATE.md. Recoverable but no longer loaded into agent context.\n\n';
|
|
1781
|
+
}
|
|
1782
|
+
archiveContent += `## Pruned ${timestamp} (phases 1-${cutoff}, kept recent ${keepRecent})\n\n`;
|
|
1783
|
+
for (const section of archived) {
|
|
1784
|
+
archiveContent += `### ${section.section}\n\n${section.lines.join('\n')}\n\n`;
|
|
1785
|
+
}
|
|
1786
|
+
platformWriteSync(archivePath, archiveContent);
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
const totalPruned = archived.reduce((sum, s) => sum + s.count, 0);
|
|
1790
|
+
emit({
|
|
1791
|
+
pruned: totalPruned > 0,
|
|
1792
|
+
cutoff_phase: cutoff,
|
|
1793
|
+
keep_recent: keepRecent,
|
|
1794
|
+
sections: archived.map(s => ({ section: s.section, entries_archived: s.count })),
|
|
1795
|
+
total_archived: totalPruned,
|
|
1796
|
+
archive_file: totalPruned > 0 ? 'STATE-ARCHIVE.md' : null,
|
|
1797
|
+
}, raw, totalPruned > 0 ? 'true' : 'false');
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
/**
|
|
1801
|
+
* Mark the current phase as COMPLETE in STATE.md.
|
|
1802
|
+
* Updates Status, Last Activity, and the Current Position section to reflect
|
|
1803
|
+
* that the phase execution is finished and the project is ready for the next phase.
|
|
1804
|
+
* Implements the `gsd state complete-phase` subcommand (issue #2735).
|
|
1805
|
+
*/
|
|
1806
|
+
function resolvePhaseIdForCompletePhase(content, overridePhase) {
|
|
1807
|
+
const candidate = overridePhase ||
|
|
1808
|
+
stateExtractField(content, 'Current Phase') ||
|
|
1809
|
+
stateExtractField(content, 'Phase') ||
|
|
1810
|
+
'';
|
|
1811
|
+
|
|
1812
|
+
// Accept canonical phase token only (e.g. 3, 03, 3A, 3.3, 10.2)
|
|
1813
|
+
const phaseMatch = String(candidate).match(/(\d+[A-Z]?(?:\.\d+)*)/i);
|
|
1814
|
+
return phaseMatch ? phaseMatch[1] : null;
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
function cmdStateCompletePhase(cwd, raw, overridePhase) {
|
|
1818
|
+
const statePath = planningPaths(cwd).state;
|
|
1819
|
+
if (!fs.existsSync(statePath)) {
|
|
1820
|
+
output({ error: 'STATE.md not found' }, raw);
|
|
1821
|
+
return;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
const content = fs.readFileSync(statePath, 'utf-8');
|
|
1825
|
+
const resolvedPhase = resolvePhaseIdForCompletePhase(content, overridePhase);
|
|
1826
|
+
if (!resolvedPhase || /^phase$/i.test(resolvedPhase)) {
|
|
1827
|
+
output({ error: 'Unable to resolve current phase. Pass an explicit phase: state complete-phase --phase <N>' }, raw);
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
// Idempotency guard (#3489). If STATE.md's canonical `Current Phase` field
|
|
1832
|
+
// already names a phase distinct from the one we are being asked to mark
|
|
1833
|
+
// complete, the project has advanced past the requested phase (e.g. a
|
|
1834
|
+
// follow-up phase was inserted, or the next phase began). Re-running
|
|
1835
|
+
// `state complete-phase --phase <N>` in that situation previously rolled
|
|
1836
|
+
// STATE.md back to <N>'s moment-of-completion — silently clobbering Status,
|
|
1837
|
+
// Last Activity, Last Activity Description, and the Current Position body.
|
|
1838
|
+
// The handler is now a no-op in that case so re-invocation from downstream
|
|
1839
|
+
// workflows cannot regress the project state.
|
|
1840
|
+
const existingCurrentPhaseRaw = stateExtractField(content, 'Current Phase') || '';
|
|
1841
|
+
const existingCurrentPhaseMatch = String(existingCurrentPhaseRaw).match(/(\d+[A-Z]?(?:\.\d+)*)/i);
|
|
1842
|
+
const existingCurrentPhase = existingCurrentPhaseMatch ? existingCurrentPhaseMatch[1] : null;
|
|
1843
|
+
if (existingCurrentPhase && existingCurrentPhase !== resolvedPhase) {
|
|
1844
|
+
output(
|
|
1845
|
+
{ updated: [], phase: resolvedPhase, idempotent: true, note: 'phase already superseded; no-op' },
|
|
1846
|
+
raw,
|
|
1847
|
+
'false',
|
|
1848
|
+
);
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1853
|
+
const updated = [];
|
|
1854
|
+
|
|
1855
|
+
readModifyWriteStateMd(statePath, (content) => {
|
|
1856
|
+
const currentPhase = resolvedPhase;
|
|
1857
|
+
|
|
1858
|
+
// Update Status field
|
|
1859
|
+
const statusValue = `Phase ${currentPhase} complete`;
|
|
1860
|
+
let result = stateReplaceField(content, 'Status', statusValue);
|
|
1861
|
+
if (result) { content = result; updated.push('Status'); }
|
|
1862
|
+
|
|
1863
|
+
// Update Last Activity date
|
|
1864
|
+
result = stateReplaceField(content, 'Last Activity', today);
|
|
1865
|
+
if (result) { content = result; updated.push('Last Activity'); }
|
|
1866
|
+
|
|
1867
|
+
// Update Last Activity Description
|
|
1868
|
+
const activityDesc = `Phase ${currentPhase} marked complete`;
|
|
1869
|
+
result = stateReplaceField(content, 'Last Activity Description', activityDesc);
|
|
1870
|
+
if (result) { content = result; updated.push('Last Activity Description'); }
|
|
1871
|
+
|
|
1872
|
+
// Update ## Current Position section
|
|
1873
|
+
const positionPattern = /(##\s*Current Position\s*\n)([\s\S]*?)(?=\n##|$)/i;
|
|
1874
|
+
const positionMatch = content.match(positionPattern);
|
|
1875
|
+
if (positionMatch) {
|
|
1876
|
+
const header = positionMatch[1];
|
|
1877
|
+
let posBody = positionMatch[2];
|
|
1878
|
+
|
|
1879
|
+
// Update Phase line to show COMPLETE
|
|
1880
|
+
const newPhase = `Phase: ${currentPhase} — COMPLETE`;
|
|
1881
|
+
if (/^Phase:/m.test(posBody)) {
|
|
1882
|
+
posBody = posBody.replace(/^Phase:.*$/m, newPhase);
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
// Update Status line if present
|
|
1886
|
+
const newStatus = `Status: Phase ${currentPhase} complete`;
|
|
1887
|
+
if (/^Status:/m.test(posBody)) {
|
|
1888
|
+
posBody = posBody.replace(/^Status:.*$/m, newStatus);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
// Update Last activity line if present
|
|
1892
|
+
const newActivity = `Last activity: ${today} -- Phase ${currentPhase} marked complete`;
|
|
1893
|
+
if (/^Last activity:/im.test(posBody)) {
|
|
1894
|
+
posBody = posBody.replace(/^Last activity:.*$/im, newActivity);
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
content = content.replace(positionPattern, () => `${header}${posBody}`);
|
|
1898
|
+
updated.push('Current Position');
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
return content;
|
|
1902
|
+
}, cwd);
|
|
1903
|
+
|
|
1904
|
+
output(
|
|
1905
|
+
{ updated, phase: resolvedPhase },
|
|
1906
|
+
raw,
|
|
1907
|
+
updated.length > 0 ? 'true' : 'false',
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
module.exports = {
|
|
1912
|
+
stateExtractField,
|
|
1913
|
+
stateReplaceField,
|
|
1914
|
+
stateReplaceFieldWithFallback,
|
|
1915
|
+
writeStateMd,
|
|
1916
|
+
readModifyWriteStateMd,
|
|
1917
|
+
updatePerformanceMetricsSection,
|
|
1918
|
+
cmdStateLoad,
|
|
1919
|
+
cmdStateGet,
|
|
1920
|
+
cmdStatePatch,
|
|
1921
|
+
cmdStateUpdate,
|
|
1922
|
+
cmdStateAdvancePlan,
|
|
1923
|
+
cmdStateRecordMetric,
|
|
1924
|
+
cmdStateUpdateProgress,
|
|
1925
|
+
cmdStateAddDecision,
|
|
1926
|
+
cmdStateAddBlocker,
|
|
1927
|
+
cmdStateResolveBlocker,
|
|
1928
|
+
cmdStateRecordSession,
|
|
1929
|
+
cmdStateSnapshot,
|
|
1930
|
+
cmdStateJson,
|
|
1931
|
+
cmdStateBeginPhase,
|
|
1932
|
+
cmdStatePlannedPhase,
|
|
1933
|
+
cmdStateCompletePhase,
|
|
1934
|
+
cmdStateValidate,
|
|
1935
|
+
cmdStateSync,
|
|
1936
|
+
cmdStatePrune,
|
|
1937
|
+
cmdStateMilestoneSwitch,
|
|
1938
|
+
cmdSignalWaiting,
|
|
1939
|
+
cmdSignalResume,
|
|
1940
|
+
};
|