@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,1971 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for phase lifecycle handlers.
|
|
3
|
+
*
|
|
4
|
+
* Tests phaseAdd, phaseAddBatch, phaseInsert, phaseScaffold, replaceInCurrentMilestone,
|
|
5
|
+
* and readModifyWriteRoadmapMd.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
9
|
+
import { mkdtemp, writeFile, readFile, rm, mkdir, readdir } from 'node:fs/promises';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
|
|
14
|
+
// ─── Fixtures ─────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
const MINIMAL_ROADMAP = `# Roadmap
|
|
17
|
+
|
|
18
|
+
## Current Milestone: v3.0 SDK-First Migration
|
|
19
|
+
|
|
20
|
+
### Phase 9: Foundation
|
|
21
|
+
|
|
22
|
+
**Goal:** Build foundation
|
|
23
|
+
**Requirements**: TBD
|
|
24
|
+
**Depends on:** Phase 8
|
|
25
|
+
**Plans:** 3 plans
|
|
26
|
+
|
|
27
|
+
Plans:
|
|
28
|
+
- [x] 09-01 (Foundation setup)
|
|
29
|
+
|
|
30
|
+
### Phase 10: Read-Only Queries
|
|
31
|
+
|
|
32
|
+
**Goal:** Port queries.
|
|
33
|
+
**Requirements**: TBD
|
|
34
|
+
**Depends on:** Phase 9
|
|
35
|
+
**Plans:** 3 plans
|
|
36
|
+
|
|
37
|
+
Plans:
|
|
38
|
+
- [x] 10-01 (Query setup)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
*Last updated: 2026-04-08*
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
const ROADMAP_WITH_DETAILS = `# Roadmap
|
|
45
|
+
|
|
46
|
+
<details>
|
|
47
|
+
<summary>v1.0 (shipped)</summary>
|
|
48
|
+
|
|
49
|
+
### Phase 1: Old Phase
|
|
50
|
+
|
|
51
|
+
**Goal:** Shipped already
|
|
52
|
+
**Plans:** 2 plans
|
|
53
|
+
|
|
54
|
+
</details>
|
|
55
|
+
|
|
56
|
+
## Current Milestone: v3.0 SDK-First Migration
|
|
57
|
+
|
|
58
|
+
### Phase 9: Foundation
|
|
59
|
+
|
|
60
|
+
**Goal:** Build foundation
|
|
61
|
+
**Requirements**: TBD
|
|
62
|
+
**Plans:** 3 plans
|
|
63
|
+
|
|
64
|
+
### Phase 10: Read-Only Queries
|
|
65
|
+
|
|
66
|
+
**Goal:** Port queries.
|
|
67
|
+
**Requirements**: TBD
|
|
68
|
+
**Plans:** 3 plans
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
*Last updated: 2026-04-08*
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
const MINIMAL_STATE = `---
|
|
75
|
+
gsd_state_version: 1.0
|
|
76
|
+
milestone: v3.0
|
|
77
|
+
milestone_name: SDK-First Migration
|
|
78
|
+
status: executing
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
# Project State
|
|
82
|
+
|
|
83
|
+
## Current Position
|
|
84
|
+
|
|
85
|
+
Phase: 10 (Read-Only Queries) — EXECUTING
|
|
86
|
+
Plan: 2 of 3
|
|
87
|
+
Status: Executing Phase 10
|
|
88
|
+
|
|
89
|
+
## Session Continuity
|
|
90
|
+
|
|
91
|
+
Last session: 2026-04-07T10:00:00.000Z
|
|
92
|
+
Stopped at: Completed 10-02-PLAN.md
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
/** Create a test project with .planning structure. */
|
|
96
|
+
async function setupTestProject(
|
|
97
|
+
tmpDir: string,
|
|
98
|
+
opts?: { roadmap?: string; state?: string; config?: Record<string, unknown>; phases?: string[] }
|
|
99
|
+
): Promise<string> {
|
|
100
|
+
const planningDir = join(tmpDir, '.planning');
|
|
101
|
+
await mkdir(planningDir, { recursive: true });
|
|
102
|
+
const phasesDir = join(planningDir, 'phases');
|
|
103
|
+
await mkdir(phasesDir, { recursive: true });
|
|
104
|
+
await writeFile(join(planningDir, 'ROADMAP.md'), opts?.roadmap || MINIMAL_ROADMAP, 'utf-8');
|
|
105
|
+
await writeFile(join(planningDir, 'STATE.md'), opts?.state || MINIMAL_STATE, 'utf-8');
|
|
106
|
+
await writeFile(
|
|
107
|
+
join(planningDir, 'config.json'),
|
|
108
|
+
JSON.stringify(opts?.config || { model_profile: 'balanced', phase_naming: 'sequential' }),
|
|
109
|
+
'utf-8'
|
|
110
|
+
);
|
|
111
|
+
// Create phase directories if requested
|
|
112
|
+
if (opts?.phases) {
|
|
113
|
+
for (const phase of opts.phases) {
|
|
114
|
+
await mkdir(join(phasesDir, phase), { recursive: true });
|
|
115
|
+
await writeFile(join(phasesDir, phase, '.gitkeep'), '', 'utf-8');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return tmpDir;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ─── Tests ────────────────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
let tmpDir: string;
|
|
124
|
+
|
|
125
|
+
beforeEach(async () => {
|
|
126
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'gsd-lifecycle-'));
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
afterEach(async () => {
|
|
130
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// ─── replaceInCurrentMilestone ──────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
describe('replaceInCurrentMilestone', () => {
|
|
136
|
+
it('replaces in full content when no details blocks', async () => {
|
|
137
|
+
const { replaceInCurrentMilestone } = await import('./phase-lifecycle.js');
|
|
138
|
+
const content = '### Phase 9: Foundation\n**Plans:** 3 plans\n';
|
|
139
|
+
const result = replaceInCurrentMilestone(content, /3 plans/, '4 plans');
|
|
140
|
+
expect(result).toContain('4 plans');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('only replaces after last </details> block', async () => {
|
|
144
|
+
const { replaceInCurrentMilestone } = await import('./phase-lifecycle.js');
|
|
145
|
+
const content = '<details>\n### Phase 1: Old\n**Plans:** 3 plans\n</details>\n\n### Phase 9: Current\n**Plans:** 3 plans\n';
|
|
146
|
+
const result = replaceInCurrentMilestone(content, /3 plans/, '4 plans');
|
|
147
|
+
// Should only replace in the current milestone section (after </details>)
|
|
148
|
+
const before = result.slice(0, result.indexOf('</details>') + '</details>'.length);
|
|
149
|
+
const after = result.slice(result.indexOf('</details>') + '</details>'.length);
|
|
150
|
+
expect(before).toContain('3 plans'); // old milestone untouched
|
|
151
|
+
expect(after).toContain('4 plans'); // current milestone updated
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('replaces only in current milestone when older milestones are wrapped in <details>', async () => {
|
|
155
|
+
const { replaceInCurrentMilestone } = await import('./phase-lifecycle.js');
|
|
156
|
+
const content = [
|
|
157
|
+
'# Roadmap',
|
|
158
|
+
'',
|
|
159
|
+
'<details>',
|
|
160
|
+
'<summary>✅ v1.18 (shipped)</summary>',
|
|
161
|
+
'',
|
|
162
|
+
'### Phase 1: Old Phase',
|
|
163
|
+
'',
|
|
164
|
+
'- [ ] Phase 1: Old Phase',
|
|
165
|
+
'',
|
|
166
|
+
'</details>',
|
|
167
|
+
'',
|
|
168
|
+
'<details>',
|
|
169
|
+
'<summary>✅ v1.19 (shipped)</summary>',
|
|
170
|
+
'',
|
|
171
|
+
'### Phase 2: Another Old Phase',
|
|
172
|
+
'',
|
|
173
|
+
'</details>',
|
|
174
|
+
'',
|
|
175
|
+
'## Current Milestone: v1.20',
|
|
176
|
+
'',
|
|
177
|
+
'- [ ] Phase 3: Current work',
|
|
178
|
+
'',
|
|
179
|
+
'### Phase 3: Current work',
|
|
180
|
+
'',
|
|
181
|
+
'**Plans:** 0/2 plans',
|
|
182
|
+
'',
|
|
183
|
+
].join('\n');
|
|
184
|
+
|
|
185
|
+
const pattern = /\*\*Plans:\*\* [^\n]+/;
|
|
186
|
+
const result = replaceInCurrentMilestone(content, pattern, '**Plans:** 2/2 plans complete');
|
|
187
|
+
|
|
188
|
+
// Should update Phase 3's Plans line (current milestone)
|
|
189
|
+
expect(result).toContain('**Plans:** 2/2 plans complete');
|
|
190
|
+
// Should NOT touch v1.18 or v1.19 sections
|
|
191
|
+
expect(result).toContain('✅ v1.18');
|
|
192
|
+
expect(result).toContain('✅ v1.19');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('replaces inside active milestone when it is wrapped in a <details> block', async () => {
|
|
196
|
+
const { replaceInCurrentMilestone } = await import('./phase-lifecycle.js');
|
|
197
|
+
// Scenario: active milestone is collapsed in <details> (e.g. user collapsed it)
|
|
198
|
+
const content = [
|
|
199
|
+
'# Roadmap',
|
|
200
|
+
'',
|
|
201
|
+
'<details>',
|
|
202
|
+
'<summary>✅ v1.18 (shipped)</summary>',
|
|
203
|
+
'',
|
|
204
|
+
'### Phase 1: Old Phase',
|
|
205
|
+
'',
|
|
206
|
+
'**Plans:** 1/1 plans',
|
|
207
|
+
'',
|
|
208
|
+
'</details>',
|
|
209
|
+
'',
|
|
210
|
+
'<details>',
|
|
211
|
+
'<summary>🚧 v1.19 in-progress</summary>',
|
|
212
|
+
'',
|
|
213
|
+
'### Phase 2: Current Work',
|
|
214
|
+
'',
|
|
215
|
+
'**Plans:** 1/2 plans',
|
|
216
|
+
'',
|
|
217
|
+
'</details>',
|
|
218
|
+
'',
|
|
219
|
+
].join('\n');
|
|
220
|
+
|
|
221
|
+
const pattern = /\*\*Plans:\*\* [^\n]+/g;
|
|
222
|
+
const result = replaceInCurrentMilestone(content, pattern, '**Plans:** 2/2 plans complete');
|
|
223
|
+
|
|
224
|
+
// The replacement should happen somewhere in the content (not silently dropped)
|
|
225
|
+
expect(result).toContain('**Plans:** 2/2 plans complete');
|
|
226
|
+
// v1.18 old plans line should remain untouched
|
|
227
|
+
expect(result).toContain('**Plans:** 1/1 plans');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('replaces inside active <details> even when footer text exists after </details>', async () => {
|
|
231
|
+
const { replaceInCurrentMilestone } = await import('./phase-lifecycle.js');
|
|
232
|
+
// Scenario: active milestone is the last <details> block, but a footer
|
|
233
|
+
// (e.g. "---\n*Last updated*") follows it. The fast-path sees after.trim()
|
|
234
|
+
// non-empty and replaces in the footer instead of inside the active block.
|
|
235
|
+
const content = [
|
|
236
|
+
'# Roadmap',
|
|
237
|
+
'',
|
|
238
|
+
'<details>',
|
|
239
|
+
'<summary>v1.0 (Archived)</summary>',
|
|
240
|
+
'',
|
|
241
|
+
'**Plans:** 1/1 plans',
|
|
242
|
+
'',
|
|
243
|
+
'</details>',
|
|
244
|
+
'',
|
|
245
|
+
'<details>',
|
|
246
|
+
'<summary>v2.0 (Active)</summary>',
|
|
247
|
+
'',
|
|
248
|
+
'**Plans:** 1/2 plans',
|
|
249
|
+
'',
|
|
250
|
+
'</details>',
|
|
251
|
+
'',
|
|
252
|
+
'---',
|
|
253
|
+
'*Last updated: 2026-01-01*',
|
|
254
|
+
].join('\n');
|
|
255
|
+
|
|
256
|
+
const pattern = /\*\*Plans:\*\* [^\n]+/g;
|
|
257
|
+
const result = replaceInCurrentMilestone(content, pattern, '**Plans:** 2/2 plans complete');
|
|
258
|
+
|
|
259
|
+
// Active milestone inside last <details> should be updated
|
|
260
|
+
expect(result).toContain('**Plans:** 2/2 plans complete');
|
|
261
|
+
// Archived milestone should remain untouched
|
|
262
|
+
expect(result).toContain('**Plans:** 1/1 plans');
|
|
263
|
+
// Footer should be preserved verbatim
|
|
264
|
+
expect(result).toContain('---');
|
|
265
|
+
expect(result).toContain('*Last updated: 2026-01-01*');
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// ─── readModifyWriteRoadmapMd ───────────────────────────────────────────
|
|
270
|
+
|
|
271
|
+
describe('readModifyWriteRoadmapMd', () => {
|
|
272
|
+
it('reads, modifies, and writes ROADMAP.md atomically', async () => {
|
|
273
|
+
const { readModifyWriteRoadmapMd } = await import('./phase-lifecycle.js');
|
|
274
|
+
await setupTestProject(tmpDir);
|
|
275
|
+
const result = await readModifyWriteRoadmapMd(tmpDir, (content) => {
|
|
276
|
+
return content.replace('Port queries.', 'Port all queries.');
|
|
277
|
+
});
|
|
278
|
+
expect(result).toContain('Port all queries.');
|
|
279
|
+
const ondisk = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
280
|
+
expect(ondisk).toContain('Port all queries.');
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('creates and releases lockfile', async () => {
|
|
284
|
+
const { readModifyWriteRoadmapMd } = await import('./phase-lifecycle.js');
|
|
285
|
+
await setupTestProject(tmpDir);
|
|
286
|
+
await readModifyWriteRoadmapMd(tmpDir, (c) => c);
|
|
287
|
+
// Lock should be released after operation
|
|
288
|
+
const lockPath = join(tmpDir, '.planning', 'ROADMAP.md.lock');
|
|
289
|
+
expect(existsSync(lockPath)).toBe(false);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// ─── phaseAdd ──────────────────────────────────────────────────────────
|
|
294
|
+
|
|
295
|
+
describe('phaseAdd', () => {
|
|
296
|
+
it('creates directory and updates ROADMAP.md for sequential phase', async () => {
|
|
297
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
298
|
+
await setupTestProject(tmpDir, {
|
|
299
|
+
phases: ['09-foundation', '10-read-only-queries'],
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const result = await phaseAdd(['New Feature'], tmpDir);
|
|
303
|
+
const data = result.data as Record<string, unknown>;
|
|
304
|
+
|
|
305
|
+
expect(data.phase_number).toBe(11);
|
|
306
|
+
expect(data.padded).toBe('11');
|
|
307
|
+
expect(data.name).toBe('New Feature');
|
|
308
|
+
expect(data.slug).toBe('new-feature');
|
|
309
|
+
expect(data.naming_mode).toBe('sequential');
|
|
310
|
+
|
|
311
|
+
// Verify directory was created
|
|
312
|
+
const dir = data.directory as string;
|
|
313
|
+
expect(dir).toContain('11-new-feature');
|
|
314
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
315
|
+
const entries = await readdir(phasesDir, { withFileTypes: true });
|
|
316
|
+
const newDir = entries.find(e => e.isDirectory() && e.name.includes('11-new-feature'));
|
|
317
|
+
expect(newDir).toBeTruthy();
|
|
318
|
+
|
|
319
|
+
// Verify .gitkeep
|
|
320
|
+
expect(existsSync(join(phasesDir, newDir!.name, '.gitkeep'))).toBe(true);
|
|
321
|
+
|
|
322
|
+
// Verify ROADMAP.md updated
|
|
323
|
+
const roadmap = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
324
|
+
expect(roadmap).toContain('### Phase 11: New Feature');
|
|
325
|
+
expect(roadmap).toContain('**Goal:** [To be planned]');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('skips exactly phase 999 (backlog sentinel) when calculating next number', async () => {
|
|
329
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
330
|
+
const roadmapWith999 = MINIMAL_ROADMAP.replace(
|
|
331
|
+
'---\n*Last updated',
|
|
332
|
+
'### Phase 999: Backlog\n\n**Goal:** Backlog items\n**Plans:** 0 plans\n\n---\n*Last updated'
|
|
333
|
+
);
|
|
334
|
+
await setupTestProject(tmpDir, { roadmap: roadmapWith999 });
|
|
335
|
+
|
|
336
|
+
const result = await phaseAdd(['After Ten'], tmpDir);
|
|
337
|
+
const data = result.data as Record<string, unknown>;
|
|
338
|
+
// Should be 11, not 1000
|
|
339
|
+
expect(data.phase_number).toBe(11);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('returns correct next phase id for projects using 1000+ canonical phase numbers (regression #3774)', async () => {
|
|
343
|
+
// Bug: scanSequentialMaxPhaseFromMilestone and scanSequentialMaxPhaseFromDirs
|
|
344
|
+
// used `num >= 999` instead of `num === 999`, causing every phase ≥ 1000 to be
|
|
345
|
+
// excluded from the max-scan. computeNextSequentialPhaseId returned 1 (0+1)
|
|
346
|
+
// instead of 1501 for a project whose highest phase is 1500.
|
|
347
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
348
|
+
|
|
349
|
+
const roadmapWith1000Plus = [
|
|
350
|
+
'# Roadmap',
|
|
351
|
+
'',
|
|
352
|
+
'## Current Milestone: v10.0 Large Project',
|
|
353
|
+
'',
|
|
354
|
+
'### Phase 1000: Foundation',
|
|
355
|
+
'',
|
|
356
|
+
'**Goal:** Foundation',
|
|
357
|
+
'**Requirements**: TBD',
|
|
358
|
+
'**Plans:** 1 plans',
|
|
359
|
+
'',
|
|
360
|
+
'Plans:',
|
|
361
|
+
'- [x] 1000-01 (Foundation setup)',
|
|
362
|
+
'',
|
|
363
|
+
'### Phase 1500: Latest',
|
|
364
|
+
'',
|
|
365
|
+
'**Goal:** Latest',
|
|
366
|
+
'**Requirements**: TBD',
|
|
367
|
+
'**Depends on:** Phase 1499',
|
|
368
|
+
'**Plans:** 1 plans',
|
|
369
|
+
'',
|
|
370
|
+
'Plans:',
|
|
371
|
+
'- [x] 1500-01 (Latest step)',
|
|
372
|
+
'',
|
|
373
|
+
'---',
|
|
374
|
+
'*Last updated: 2026-05-20*',
|
|
375
|
+
'',
|
|
376
|
+
].join('\n');
|
|
377
|
+
|
|
378
|
+
const phases = [
|
|
379
|
+
'1000-foundation',
|
|
380
|
+
'1100-alpha',
|
|
381
|
+
'1200-beta',
|
|
382
|
+
'1300-gamma',
|
|
383
|
+
'1400-delta',
|
|
384
|
+
'1500-latest',
|
|
385
|
+
];
|
|
386
|
+
|
|
387
|
+
await setupTestProject(tmpDir, { roadmap: roadmapWith1000Plus, phases });
|
|
388
|
+
|
|
389
|
+
const result = await phaseAdd(['Next After 1500'], tmpDir);
|
|
390
|
+
const data = result.data as Record<string, unknown>;
|
|
391
|
+
// Must be 1501, not 1 (the pre-fix bug value)
|
|
392
|
+
expect(data.phase_number).toBe(1501);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('distinguishes === 999 guard from === 1000 off-by-one: [999, 1000] fixture must yield 1001 (regression #3774)', async () => {
|
|
396
|
+
// Distinguishing fixture: phases [999, 1000] on disk + in ROADMAP.
|
|
397
|
+
// With correct guard (=== 999): skips 999, keeps 1000 as max → next = 1001 ✓
|
|
398
|
+
// With off-by-one guard (=== 1000): skips 1000, keeps 999 → max = 999 but
|
|
399
|
+
// 999 is itself skipped by the equality guard (999 === 999 is true when
|
|
400
|
+
// the guard fires), so actually keeps nothing → max = 0 → next = 1 ✗.
|
|
401
|
+
// Either way, the result diverges from 1001, catching the regression.
|
|
402
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
403
|
+
|
|
404
|
+
const roadmapWith999and1000 = [
|
|
405
|
+
'# Roadmap',
|
|
406
|
+
'',
|
|
407
|
+
'## Current Milestone: v1.0',
|
|
408
|
+
'',
|
|
409
|
+
'### Phase 999: Backlog',
|
|
410
|
+
'',
|
|
411
|
+
'**Goal:** Backlog sentinel',
|
|
412
|
+
'**Plans:** 0 plans',
|
|
413
|
+
'',
|
|
414
|
+
'### Phase 1000: First Four-Digit Phase',
|
|
415
|
+
'',
|
|
416
|
+
'**Goal:** First canonical phase above backlog sentinel',
|
|
417
|
+
'**Requirements**: TBD',
|
|
418
|
+
'**Plans:** 1 plans',
|
|
419
|
+
'',
|
|
420
|
+
'Plans:',
|
|
421
|
+
'- [x] 1000-01 (initial work)',
|
|
422
|
+
'',
|
|
423
|
+
'---',
|
|
424
|
+
'*Last updated: 2026-05-21*',
|
|
425
|
+
'',
|
|
426
|
+
].join('\n');
|
|
427
|
+
|
|
428
|
+
const phases = [
|
|
429
|
+
'999-backlog',
|
|
430
|
+
'1000-first-four-digit',
|
|
431
|
+
];
|
|
432
|
+
|
|
433
|
+
await setupTestProject(tmpDir, { roadmap: roadmapWith999and1000, phases });
|
|
434
|
+
|
|
435
|
+
const result = await phaseAdd(['After One Thousand'], tmpDir);
|
|
436
|
+
const data = result.data as Record<string, unknown>;
|
|
437
|
+
// Must be 1001: skips 999 (backlog sentinel), keeps 1000 as max, adds 1
|
|
438
|
+
expect(data.phase_number).toBe(1001);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('throws GSDError with Validation for empty description', async () => {
|
|
442
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
443
|
+
await setupTestProject(tmpDir);
|
|
444
|
+
|
|
445
|
+
await expect(phaseAdd([], tmpDir)).rejects.toThrow('description required');
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('inserts phase entry before last --- separator', async () => {
|
|
449
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
450
|
+
await setupTestProject(tmpDir);
|
|
451
|
+
|
|
452
|
+
await phaseAdd(['Inserted Phase'], tmpDir);
|
|
453
|
+
const roadmap = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
454
|
+
|
|
455
|
+
// The new phase should appear before the trailing ---
|
|
456
|
+
const phaseIdx = roadmap.indexOf('### Phase 11: Inserted Phase');
|
|
457
|
+
const sepIdx = roadmap.lastIndexOf('\n---');
|
|
458
|
+
expect(phaseIdx).toBeLessThan(sepIdx);
|
|
459
|
+
expect(phaseIdx).toBeGreaterThan(0);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it('detects max phase from bullet checklist format (regression #2726)', async () => {
|
|
463
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
464
|
+
|
|
465
|
+
const roadmap = [
|
|
466
|
+
'# Roadmap',
|
|
467
|
+
'',
|
|
468
|
+
'## Current Milestone: v5.0',
|
|
469
|
+
'',
|
|
470
|
+
'- [x] Phase 76: Data Import',
|
|
471
|
+
'- [x] Phase 77: Data Transform',
|
|
472
|
+
'- [ ] Phase 88: Final Cleanup',
|
|
473
|
+
'',
|
|
474
|
+
].join('\n');
|
|
475
|
+
|
|
476
|
+
await setupTestProject(tmpDir, {
|
|
477
|
+
roadmap,
|
|
478
|
+
state: MINIMAL_STATE,
|
|
479
|
+
phases: [],
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
const result = await phaseAdd(['new-feature'], tmpDir);
|
|
483
|
+
const data = result.data as Record<string, unknown>;
|
|
484
|
+
|
|
485
|
+
expect(data.phase_number).toBe(89);
|
|
486
|
+
expect(data.padded).toBe('89');
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('detects max phase from bold inline format (regression #2726)', async () => {
|
|
490
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
491
|
+
|
|
492
|
+
const roadmap = [
|
|
493
|
+
'# Roadmap',
|
|
494
|
+
'',
|
|
495
|
+
'## Current Milestone: v5.0',
|
|
496
|
+
'',
|
|
497
|
+
'**Phase 50: Core Infrastructure**',
|
|
498
|
+
'**Phase 51: API Layer**',
|
|
499
|
+
'',
|
|
500
|
+
].join('\n');
|
|
501
|
+
|
|
502
|
+
await setupTestProject(tmpDir, {
|
|
503
|
+
roadmap,
|
|
504
|
+
state: MINIMAL_STATE,
|
|
505
|
+
phases: [],
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
const result = await phaseAdd(['new-feature'], tmpDir);
|
|
509
|
+
const data = result.data as Record<string, unknown>;
|
|
510
|
+
|
|
511
|
+
expect(data.phase_number).toBe(52);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('falls back to filesystem scan when no phase matches in ROADMAP (regression #2726)', async () => {
|
|
515
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
516
|
+
|
|
517
|
+
// ROADMAP with no recognizable phase entries
|
|
518
|
+
const roadmap = '# Roadmap\n\n## Current Milestone: v5.0\n\nSome content without phases\n';
|
|
519
|
+
|
|
520
|
+
await setupTestProject(tmpDir, {
|
|
521
|
+
roadmap,
|
|
522
|
+
state: MINIMAL_STATE,
|
|
523
|
+
phases: ['45-legacy-phase', '46-another-phase'],
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const result = await phaseAdd(['new-feature'], tmpDir);
|
|
527
|
+
const data = result.data as Record<string, unknown>;
|
|
528
|
+
|
|
529
|
+
// Should detect phases 45 and 46 on disk, so new phase = 47
|
|
530
|
+
expect(data.phase_number).toBe(47);
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
it('filesystem fallback handles project-code-prefixed phase directories (regression coderabbit)', async () => {
|
|
534
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
535
|
+
|
|
536
|
+
const roadmap = '# Roadmap\n\n## Current Milestone: v5.0\n\nSome content\n';
|
|
537
|
+
|
|
538
|
+
await setupTestProject(tmpDir, {
|
|
539
|
+
roadmap,
|
|
540
|
+
state: MINIMAL_STATE,
|
|
541
|
+
phases: [],
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
// Create prefixed directories manually (project_code = "CK" scenario)
|
|
545
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
546
|
+
await mkdir(join(phasesDir, 'CK-45-legacy-phase'), { recursive: true });
|
|
547
|
+
await mkdir(join(phasesDir, 'CK-46-another-phase'), { recursive: true });
|
|
548
|
+
|
|
549
|
+
const result = await phaseAdd(['new-feature'], tmpDir);
|
|
550
|
+
const data = result.data as Record<string, unknown>;
|
|
551
|
+
|
|
552
|
+
// Should detect CK-45 and CK-46, so new phase = 47
|
|
553
|
+
expect(data.phase_number).toBe(47);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// ── Symptom A: --dry-run flag (#3226) ─────────────────────────────────
|
|
557
|
+
|
|
558
|
+
it('--dry-run returns JSON result without creating any files or modifying ROADMAP', async () => {
|
|
559
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
560
|
+
await setupTestProject(tmpDir, {
|
|
561
|
+
phases: ['09-foundation', '10-read-only-queries'],
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
const roadmapBefore = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
565
|
+
const result = await phaseAdd(['Dry Run Phase', '--dry-run'], tmpDir);
|
|
566
|
+
const data = result.data as Record<string, unknown>;
|
|
567
|
+
|
|
568
|
+
// Result must include the computed fields
|
|
569
|
+
expect(data.phase_number).toBe(11);
|
|
570
|
+
expect(data.padded).toBe('11');
|
|
571
|
+
expect(data.name).toBe('Dry Run Phase');
|
|
572
|
+
expect(data.slug).toBe('dry-run-phase');
|
|
573
|
+
expect(data.dry_run).toBe(true);
|
|
574
|
+
expect(typeof data.roadmap_entry).toBe('string');
|
|
575
|
+
expect((data.roadmap_entry as string)).toContain('### Phase 11: Dry Run Phase');
|
|
576
|
+
|
|
577
|
+
// ROADMAP.md must be unchanged
|
|
578
|
+
const roadmapAfter = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
579
|
+
expect(roadmapAfter).toBe(roadmapBefore);
|
|
580
|
+
|
|
581
|
+
// No new phase directory must have been created
|
|
582
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
583
|
+
const entries = await readdir(phasesDir, { withFileTypes: true });
|
|
584
|
+
const newDir = entries.find(e => e.isDirectory() && e.name.includes('11-dry-run-phase'));
|
|
585
|
+
expect(newDir).toBeUndefined();
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('--dry-run works when flag appears after customId position', async () => {
|
|
589
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
590
|
+
await setupTestProject(tmpDir, {
|
|
591
|
+
phases: ['09-foundation', '10-read-only-queries'],
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
const roadmapBefore = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
595
|
+
// description + --dry-run — no customId; flag must not be mistaken for customId
|
|
596
|
+
const result = await phaseAdd(['My Feature', '--dry-run'], tmpDir);
|
|
597
|
+
const data = result.data as Record<string, unknown>;
|
|
598
|
+
|
|
599
|
+
expect(data.dry_run).toBe(true);
|
|
600
|
+
expect(data.phase_number).toBe(11);
|
|
601
|
+
|
|
602
|
+
// ROADMAP must still be untouched
|
|
603
|
+
const roadmapAfter = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
604
|
+
expect(roadmapAfter).toBe(roadmapBefore);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
// ── Symptom C: unknown flag rejection (#3226) ──────────────────────────
|
|
608
|
+
|
|
609
|
+
it('rejects unknown --flags with a validation error naming the flag', async () => {
|
|
610
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
611
|
+
await setupTestProject(tmpDir);
|
|
612
|
+
|
|
613
|
+
await expect(phaseAdd(['My Feature', '--bogus-flag'], tmpDir)).rejects.toThrow('--bogus-flag');
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it('rejects any unknown --flag even when mixed with dry-run', async () => {
|
|
617
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
618
|
+
await setupTestProject(tmpDir);
|
|
619
|
+
|
|
620
|
+
await expect(phaseAdd(['Desc', '--dry-run', '--unknown'], tmpDir)).rejects.toThrow('--unknown');
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
// ── Symptom B: ROADMAP heading scan counts ### Phase N: (#3226 verify) ─
|
|
624
|
+
|
|
625
|
+
it('scans ### Phase N: headings in ROADMAP when no on-disk dirs exist (B already fixed)', async () => {
|
|
626
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
627
|
+
|
|
628
|
+
const roadmap = [
|
|
629
|
+
'# Roadmap',
|
|
630
|
+
'',
|
|
631
|
+
'## Current Milestone: v5.0',
|
|
632
|
+
'',
|
|
633
|
+
'### Phase 5: Foundation',
|
|
634
|
+
'',
|
|
635
|
+
'**Goal:** Build foundation',
|
|
636
|
+
'**Plans:** 0 plans',
|
|
637
|
+
'',
|
|
638
|
+
].join('\n');
|
|
639
|
+
|
|
640
|
+
await setupTestProject(tmpDir, {
|
|
641
|
+
roadmap,
|
|
642
|
+
state: MINIMAL_STATE,
|
|
643
|
+
phases: [], // no on-disk dirs — must rely on ROADMAP scan
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
const result = await phaseAdd(['Next Phase'], tmpDir);
|
|
647
|
+
const data = result.data as Record<string, unknown>;
|
|
648
|
+
|
|
649
|
+
// Must detect Phase 5 from ### heading → next = 6, not 1
|
|
650
|
+
expect(data.phase_number).toBe(6);
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
// ── Concurrent phase.add: no duplicate IDs (CR finding) ────────────────
|
|
654
|
+
it('concurrent phase.add calls produce distinct sequential phase numbers', async () => {
|
|
655
|
+
const { phaseAdd } = await import('./phase-lifecycle.js');
|
|
656
|
+
await setupTestProject(tmpDir, {
|
|
657
|
+
phases: ['09-foundation', '10-read-only-queries'],
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
// Fire two phase.add calls simultaneously. If computation happens outside
|
|
661
|
+
// the lock both will observe maxPhase=10 and claim newPhaseId=11 — collision.
|
|
662
|
+
const [r1, r2] = await Promise.all([
|
|
663
|
+
phaseAdd(['Concurrent Alpha'], tmpDir),
|
|
664
|
+
phaseAdd(['Concurrent Beta'], tmpDir),
|
|
665
|
+
]);
|
|
666
|
+
|
|
667
|
+
const n1 = (r1.data as Record<string, unknown>).phase_number as number;
|
|
668
|
+
const n2 = (r2.data as Record<string, unknown>).phase_number as number;
|
|
669
|
+
|
|
670
|
+
// Both must succeed and produce DIFFERENT numbers
|
|
671
|
+
expect(n1).not.toBe(n2);
|
|
672
|
+
|
|
673
|
+
// The pair must be {11, 12} — no gaps, no duplicates
|
|
674
|
+
const sorted = [n1, n2].sort((a, b) => a - b);
|
|
675
|
+
expect(sorted).toEqual([11, 12]);
|
|
676
|
+
|
|
677
|
+
// ROADMAP.md must contain exactly one entry for each phase
|
|
678
|
+
const roadmap = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
679
|
+
const phase11Count = (roadmap.match(/### Phase 11:/g) || []).length;
|
|
680
|
+
const phase12Count = (roadmap.match(/### Phase 12:/g) || []).length;
|
|
681
|
+
expect(phase11Count).toBe(1);
|
|
682
|
+
expect(phase12Count).toBe(1);
|
|
683
|
+
|
|
684
|
+
// Both phase directories must exist on disk
|
|
685
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
686
|
+
const entries = await readdir(phasesDir, { withFileTypes: true });
|
|
687
|
+
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
688
|
+
const has11 = dirs.some(d => d.startsWith('11-'));
|
|
689
|
+
const has12 = dirs.some(d => d.startsWith('12-'));
|
|
690
|
+
expect(has11).toBe(true);
|
|
691
|
+
expect(has12).toBe(true);
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
// ─── phaseAddBatch ─────────────────────────────────────────────────────
|
|
696
|
+
|
|
697
|
+
describe('phaseAddBatch', () => {
|
|
698
|
+
it('adds multiple sequential phases in one pass', async () => {
|
|
699
|
+
const { phaseAddBatch } = await import('./phase-lifecycle.js');
|
|
700
|
+
await setupTestProject(tmpDir, {
|
|
701
|
+
phases: ['09-foundation', '10-read-only-queries'],
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
const result = await phaseAddBatch(['Alpha', 'Beta'], tmpDir);
|
|
705
|
+
const data = result.data as { phases: Array<Record<string, unknown>>; count: number };
|
|
706
|
+
|
|
707
|
+
expect(data.count).toBe(2);
|
|
708
|
+
expect(data.phases[0].phase_number).toBe(11);
|
|
709
|
+
expect(data.phases[0].name).toBe('Alpha');
|
|
710
|
+
expect(data.phases[1].phase_number).toBe(12);
|
|
711
|
+
expect(data.phases[1].name).toBe('Beta');
|
|
712
|
+
|
|
713
|
+
const roadmap = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
714
|
+
expect(roadmap).toContain('### Phase 11: Alpha');
|
|
715
|
+
expect(roadmap).toContain('### Phase 12: Beta');
|
|
716
|
+
|
|
717
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
718
|
+
expect(existsSync(join(phasesDir, '11-alpha', '.gitkeep'))).toBe(true);
|
|
719
|
+
expect(existsSync(join(phasesDir, '12-beta', '.gitkeep'))).toBe(true);
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
it('accepts --descriptions JSON array', async () => {
|
|
723
|
+
const { phaseAddBatch } = await import('./phase-lifecycle.js');
|
|
724
|
+
await setupTestProject(tmpDir, { phases: ['09-foundation', '10-read-only-queries'] });
|
|
725
|
+
|
|
726
|
+
const result = await phaseAddBatch(
|
|
727
|
+
['--descriptions', JSON.stringify(['One', 'Two'])],
|
|
728
|
+
tmpDir,
|
|
729
|
+
);
|
|
730
|
+
const data = result.data as { count: number };
|
|
731
|
+
expect(data.count).toBe(2);
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
it('throws when no descriptions', async () => {
|
|
735
|
+
const { phaseAddBatch } = await import('./phase-lifecycle.js');
|
|
736
|
+
await setupTestProject(tmpDir);
|
|
737
|
+
|
|
738
|
+
await expect(phaseAddBatch([], tmpDir)).rejects.toThrow('descriptions array required');
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
// ─── phaseInsert ────────────────────────────────────────────────────────
|
|
743
|
+
|
|
744
|
+
describe('phaseInsert', () => {
|
|
745
|
+
it('creates decimal phase directory after target phase', async () => {
|
|
746
|
+
const { phaseInsert } = await import('./phase-lifecycle.js');
|
|
747
|
+
await setupTestProject(tmpDir, {
|
|
748
|
+
phases: ['09-foundation', '10-read-only-queries'],
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
const result = await phaseInsert(['10', 'Urgent Fix'], tmpDir);
|
|
752
|
+
const data = result.data as Record<string, unknown>;
|
|
753
|
+
|
|
754
|
+
expect(data.phase_number).toBe('10.1');
|
|
755
|
+
expect(data.after_phase).toBe('10');
|
|
756
|
+
expect(data.name).toBe('Urgent Fix');
|
|
757
|
+
expect(data.slug).toBe('urgent-fix');
|
|
758
|
+
|
|
759
|
+
// Verify directory created
|
|
760
|
+
const dir = data.directory as string;
|
|
761
|
+
expect(dir).toContain('10.1-urgent-fix');
|
|
762
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
763
|
+
const entries = await readdir(phasesDir, { withFileTypes: true });
|
|
764
|
+
const newDir = entries.find(e => e.isDirectory() && e.name.includes('10.1-urgent-fix'));
|
|
765
|
+
expect(newDir).toBeTruthy();
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
it('scans both directories and ROADMAP.md for existing decimals to avoid collisions', async () => {
|
|
769
|
+
const { phaseInsert } = await import('./phase-lifecycle.js');
|
|
770
|
+
await setupTestProject(tmpDir, {
|
|
771
|
+
phases: ['09-foundation', '10-read-only-queries', '10.1-hotfix'],
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
const result = await phaseInsert(['10', 'Another Fix'], tmpDir);
|
|
775
|
+
const data = result.data as Record<string, unknown>;
|
|
776
|
+
// Should be 10.2 since 10.1 already exists on disk
|
|
777
|
+
expect(data.phase_number).toBe('10.2');
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
it('inserts section in ROADMAP.md after target phase', async () => {
|
|
781
|
+
const { phaseInsert } = await import('./phase-lifecycle.js');
|
|
782
|
+
await setupTestProject(tmpDir);
|
|
783
|
+
|
|
784
|
+
await phaseInsert(['10', 'Urgent Fix'], tmpDir);
|
|
785
|
+
const roadmap = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
786
|
+
|
|
787
|
+
expect(roadmap).toContain('### Phase 10.1: Urgent Fix (INSERTED)');
|
|
788
|
+
// Should appear after Phase 10 section
|
|
789
|
+
const phase10Idx = roadmap.indexOf('### Phase 10:');
|
|
790
|
+
const insertedIdx = roadmap.indexOf('### Phase 10.1:');
|
|
791
|
+
expect(insertedIdx).toBeGreaterThan(phase10Idx);
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
it('throws GSDError for missing target phase', async () => {
|
|
795
|
+
const { phaseInsert } = await import('./phase-lifecycle.js');
|
|
796
|
+
await setupTestProject(tmpDir);
|
|
797
|
+
|
|
798
|
+
await expect(phaseInsert(['99', 'Missing'], tmpDir)).rejects.toThrow('Phase 99 not found');
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
it('throws GSDError with Validation for missing args', async () => {
|
|
802
|
+
const { phaseInsert } = await import('./phase-lifecycle.js');
|
|
803
|
+
await setupTestProject(tmpDir);
|
|
804
|
+
|
|
805
|
+
await expect(phaseInsert([], tmpDir)).rejects.toThrow('after-phase and description required');
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
it('#3098 preserved: throws when bullet-only entry in a heading-style ROADMAP (hybrid = missing detail section)', async () => {
|
|
809
|
+
// A hybrid ROADMAP: phase 9 has a heading but phase 10 only has a bullet
|
|
810
|
+
// summary entry. Insert must still reject with "missing a detail section"
|
|
811
|
+
// because the surrounding ROADMAP uses heading-style phases.
|
|
812
|
+
const { phaseInsert } = await import('./phase-lifecycle.js');
|
|
813
|
+
const hybridRoadmap = `# Roadmap\n\n## Current Milestone\n\n### Phase 9: Foundation\n\n- [ ] **Phase 10: Queries**\n`;
|
|
814
|
+
await setupTestProject(tmpDir, {
|
|
815
|
+
roadmap: hybridRoadmap,
|
|
816
|
+
phases: ['09-foundation'],
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
await expect(phaseInsert(['10', 'Hotfix'], tmpDir)).rejects.toThrow('missing a detail section');
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
// ─── #3815: checked-bullet ROADMAP format ─────────────────────────────
|
|
823
|
+
|
|
824
|
+
const BULLET_ONLY_ROADMAP = `# Roadmap
|
|
825
|
+
|
|
826
|
+
## Current Milestone: v2.0 Agent Skills
|
|
827
|
+
|
|
828
|
+
- [x] **Phase 01: bootstrap** — completed 2026-04-01
|
|
829
|
+
- [ ] **Phase 02: core-loop**
|
|
830
|
+
- [ ] **Phase 03: persistence**
|
|
831
|
+
|
|
832
|
+
---
|
|
833
|
+
*Last updated: 2026-05-01*
|
|
834
|
+
`;
|
|
835
|
+
|
|
836
|
+
it('#3815: inserts decimal phase between bullets in a checked-bullet ROADMAP', async () => {
|
|
837
|
+
const { phaseInsert } = await import('./phase-lifecycle.js');
|
|
838
|
+
await setupTestProject(tmpDir, {
|
|
839
|
+
roadmap: BULLET_ONLY_ROADMAP,
|
|
840
|
+
phases: ['01-bootstrap', '02-core-loop', '03-persistence'],
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
const result = await phaseInsert(['02', 'hot patch'], tmpDir);
|
|
844
|
+
const data = result.data as Record<string, unknown>;
|
|
845
|
+
|
|
846
|
+
expect(data.phase_number).toBe('02.1');
|
|
847
|
+
expect(data.after_phase).toBe('02');
|
|
848
|
+
expect(data.name).toBe('hot patch');
|
|
849
|
+
|
|
850
|
+
const roadmap = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
851
|
+
|
|
852
|
+
// New bullet must be present
|
|
853
|
+
expect(roadmap).toMatch(/- \[ \].*Phase 02\.1:.*hot patch/i);
|
|
854
|
+
|
|
855
|
+
// New bullet must appear AFTER the Phase 02 bullet
|
|
856
|
+
const phase02Idx = roadmap.indexOf('Phase 02: core-loop');
|
|
857
|
+
const insertedIdx = roadmap.search(/Phase 02\.1:/i);
|
|
858
|
+
expect(phase02Idx).toBeGreaterThanOrEqual(0);
|
|
859
|
+
expect(insertedIdx).toBeGreaterThan(phase02Idx);
|
|
860
|
+
|
|
861
|
+
// New bullet must appear BEFORE the Phase 03 bullet (positional assertion)
|
|
862
|
+
const phase03Idx = roadmap.indexOf('Phase 03: persistence');
|
|
863
|
+
expect(insertedIdx).toBeLessThan(phase03Idx);
|
|
864
|
+
|
|
865
|
+
// Must NOT corrupt the surrounding bullet structure
|
|
866
|
+
expect(roadmap).toContain('- [x] **Phase 01: bootstrap**');
|
|
867
|
+
expect(roadmap).toContain('- [ ] **Phase 02: core-loop**');
|
|
868
|
+
expect(roadmap).toContain('- [ ] **Phase 03: persistence**');
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
it('#3815: inserts at end of bullet list when target is the last phase', async () => {
|
|
872
|
+
const { phaseInsert } = await import('./phase-lifecycle.js');
|
|
873
|
+
await setupTestProject(tmpDir, {
|
|
874
|
+
roadmap: BULLET_ONLY_ROADMAP,
|
|
875
|
+
phases: ['01-bootstrap', '02-core-loop', '03-persistence'],
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
const result = await phaseInsert(['03', 'final extra'], tmpDir);
|
|
879
|
+
const data = result.data as Record<string, unknown>;
|
|
880
|
+
|
|
881
|
+
expect(data.phase_number).toBe('03.1');
|
|
882
|
+
|
|
883
|
+
const roadmap = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
884
|
+
expect(roadmap).toMatch(/- \[ \].*Phase 03\.1:.*final extra/i);
|
|
885
|
+
|
|
886
|
+
// Must appear after Phase 03 bullet
|
|
887
|
+
const phase03Idx = roadmap.indexOf('Phase 03: persistence');
|
|
888
|
+
const insertedIdx = roadmap.search(/Phase 03\.1:/i);
|
|
889
|
+
expect(insertedIdx).toBeGreaterThan(phase03Idx);
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
it('#3815: plain bullet format (no bold) also works', async () => {
|
|
893
|
+
const { phaseInsert } = await import('./phase-lifecycle.js');
|
|
894
|
+
const PLAIN_BULLET_ROADMAP = `# Roadmap
|
|
895
|
+
|
|
896
|
+
## Current Milestone: v2.0
|
|
897
|
+
|
|
898
|
+
- [ ] Phase 01: foo
|
|
899
|
+
- [ ] Phase 02: bar
|
|
900
|
+
- [ ] Phase 03: baz
|
|
901
|
+
|
|
902
|
+
---
|
|
903
|
+
*Last updated: 2026-05-01*
|
|
904
|
+
`;
|
|
905
|
+
await setupTestProject(tmpDir, {
|
|
906
|
+
roadmap: PLAIN_BULLET_ROADMAP,
|
|
907
|
+
phases: ['01-foo', '02-bar', '03-baz'],
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
const result = await phaseInsert(['02', 'inserted'], tmpDir);
|
|
911
|
+
const data = result.data as Record<string, unknown>;
|
|
912
|
+
|
|
913
|
+
expect(data.phase_number).toBe('02.1');
|
|
914
|
+
|
|
915
|
+
const roadmap = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
916
|
+
expect(roadmap).toMatch(/- \[ \].*Phase 02\.1:.*inserted/i);
|
|
917
|
+
|
|
918
|
+
const phase02Idx = roadmap.indexOf('Phase 02: bar');
|
|
919
|
+
const insertedIdx = roadmap.search(/Phase 02\.1:/i);
|
|
920
|
+
const phase03Idx = roadmap.indexOf('Phase 03: baz');
|
|
921
|
+
expect(insertedIdx).toBeGreaterThan(phase02Idx);
|
|
922
|
+
expect(insertedIdx).toBeLessThan(phase03Idx);
|
|
923
|
+
});
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
// ─── phaseScaffold ──────────────────────────────────────────────────────
|
|
927
|
+
|
|
928
|
+
describe('phaseScaffold', () => {
|
|
929
|
+
it('creates context template for a phase', async () => {
|
|
930
|
+
const { phaseScaffold } = await import('./phase-lifecycle.js');
|
|
931
|
+
await setupTestProject(tmpDir, {
|
|
932
|
+
phases: ['09-foundation'],
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
const result = await phaseScaffold(['context', '9'], tmpDir);
|
|
936
|
+
const data = result.data as Record<string, unknown>;
|
|
937
|
+
|
|
938
|
+
expect(data.created).toBe(true);
|
|
939
|
+
const filePath = data.path as string;
|
|
940
|
+
expect(filePath).toContain('09-CONTEXT.md');
|
|
941
|
+
|
|
942
|
+
// Check content
|
|
943
|
+
const fullPath = join(tmpDir, '.planning', 'phases', '09-foundation', '09-CONTEXT.md');
|
|
944
|
+
expect(existsSync(fullPath)).toBe(true);
|
|
945
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
946
|
+
expect(content).toContain('phase: "09"');
|
|
947
|
+
expect(content).toContain('Context');
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
it('creates uat template', async () => {
|
|
951
|
+
const { phaseScaffold } = await import('./phase-lifecycle.js');
|
|
952
|
+
await setupTestProject(tmpDir, {
|
|
953
|
+
phases: ['09-foundation'],
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
const result = await phaseScaffold(['uat', '9'], tmpDir);
|
|
957
|
+
const data = result.data as Record<string, unknown>;
|
|
958
|
+
|
|
959
|
+
expect(data.created).toBe(true);
|
|
960
|
+
const fullPath = join(tmpDir, '.planning', 'phases', '09-foundation', '09-UAT.md');
|
|
961
|
+
expect(existsSync(fullPath)).toBe(true);
|
|
962
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
963
|
+
expect(content).toContain('User Acceptance Testing');
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
it('creates verification template', async () => {
|
|
967
|
+
const { phaseScaffold } = await import('./phase-lifecycle.js');
|
|
968
|
+
await setupTestProject(tmpDir, {
|
|
969
|
+
phases: ['09-foundation'],
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
const result = await phaseScaffold(['verification', '9'], tmpDir);
|
|
973
|
+
const data = result.data as Record<string, unknown>;
|
|
974
|
+
|
|
975
|
+
expect(data.created).toBe(true);
|
|
976
|
+
const fullPath = join(tmpDir, '.planning', 'phases', '09-foundation', '09-VERIFICATION.md');
|
|
977
|
+
expect(existsSync(fullPath)).toBe(true);
|
|
978
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
979
|
+
expect(content).toContain('Verification');
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
it('creates phase-dir under phases/', async () => {
|
|
983
|
+
const { phaseScaffold } = await import('./phase-lifecycle.js');
|
|
984
|
+
await setupTestProject(tmpDir);
|
|
985
|
+
|
|
986
|
+
const result = await phaseScaffold(['phase-dir', '15', 'New Module'], tmpDir);
|
|
987
|
+
const data = result.data as Record<string, unknown>;
|
|
988
|
+
|
|
989
|
+
expect(data.created).toBe(true);
|
|
990
|
+
const dir = data.directory as string;
|
|
991
|
+
expect(dir).toContain('15-new-module');
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
it('returns already_exists for existing file', async () => {
|
|
995
|
+
const { phaseScaffold } = await import('./phase-lifecycle.js');
|
|
996
|
+
await setupTestProject(tmpDir, {
|
|
997
|
+
phases: ['09-foundation'],
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
// Create first
|
|
1001
|
+
await phaseScaffold(['context', '9'], tmpDir);
|
|
1002
|
+
// Second call should return already_exists
|
|
1003
|
+
const result = await phaseScaffold(['context', '9'], tmpDir);
|
|
1004
|
+
const data = result.data as Record<string, unknown>;
|
|
1005
|
+
expect(data.created).toBe(false);
|
|
1006
|
+
expect(data.reason).toBe('already_exists');
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
it('throws GSDError for unknown type', async () => {
|
|
1010
|
+
const { phaseScaffold } = await import('./phase-lifecycle.js');
|
|
1011
|
+
await setupTestProject(tmpDir, {
|
|
1012
|
+
phases: ['09-foundation'],
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
await expect(phaseScaffold(['badtype', '9'], tmpDir)).rejects.toThrow('Unknown scaffold type');
|
|
1016
|
+
});
|
|
1017
|
+
});
|
|
1018
|
+
|
|
1019
|
+
// ─── phaseRemove ─────────────────────────────────────────────────────────
|
|
1020
|
+
|
|
1021
|
+
const ROADMAP_FOR_REMOVE = `# Roadmap
|
|
1022
|
+
|
|
1023
|
+
## Current Milestone: v3.0 SDK-First Migration
|
|
1024
|
+
|
|
1025
|
+
### Phase 5: Auth
|
|
1026
|
+
|
|
1027
|
+
**Goal:** Build authentication
|
|
1028
|
+
**Requirements**: TBD
|
|
1029
|
+
**Depends on:** Phase 4
|
|
1030
|
+
**Plans:** 2 plans
|
|
1031
|
+
|
|
1032
|
+
Plans:
|
|
1033
|
+
- [x] 05-01 (Auth setup)
|
|
1034
|
+
- [x] 05-02 (Auth complete)
|
|
1035
|
+
|
|
1036
|
+
### Phase 6: Dashboard
|
|
1037
|
+
|
|
1038
|
+
**Goal:** Build dashboard
|
|
1039
|
+
**Requirements**: TBD
|
|
1040
|
+
**Depends on:** Phase 5
|
|
1041
|
+
**Plans:** 3 plans
|
|
1042
|
+
|
|
1043
|
+
Plans:
|
|
1044
|
+
- [ ] 06-01 (Dashboard setup)
|
|
1045
|
+
|
|
1046
|
+
### Phase 7: API
|
|
1047
|
+
|
|
1048
|
+
**Goal:** Build API layer
|
|
1049
|
+
**Requirements**: TBD
|
|
1050
|
+
**Depends on:** Phase 6
|
|
1051
|
+
**Plans:** 2 plans
|
|
1052
|
+
|
|
1053
|
+
Plans:
|
|
1054
|
+
- [ ] 07-01 (API setup)
|
|
1055
|
+
|
|
1056
|
+
---
|
|
1057
|
+
*Last updated: 2026-04-08*
|
|
1058
|
+
`;
|
|
1059
|
+
|
|
1060
|
+
const STATE_FOR_REMOVE = `---
|
|
1061
|
+
gsd_state_version: 1.0
|
|
1062
|
+
milestone: v3.0
|
|
1063
|
+
milestone_name: SDK-First Migration
|
|
1064
|
+
status: executing
|
|
1065
|
+
progress:
|
|
1066
|
+
total_phases: 7
|
|
1067
|
+
completed_phases: 4
|
|
1068
|
+
total_plans: 15
|
|
1069
|
+
completed_plans: 12
|
|
1070
|
+
percent: 80
|
|
1071
|
+
---
|
|
1072
|
+
|
|
1073
|
+
# Project State
|
|
1074
|
+
|
|
1075
|
+
## Current Position
|
|
1076
|
+
|
|
1077
|
+
Phase: 6 (Dashboard) — EXECUTING
|
|
1078
|
+
Plan: 1 of 3
|
|
1079
|
+
Status: Executing Phase 6
|
|
1080
|
+
|
|
1081
|
+
## Session Continuity
|
|
1082
|
+
|
|
1083
|
+
Last session: 2026-04-08T10:00:00.000Z
|
|
1084
|
+
Stopped at: Started
|
|
1085
|
+
`;
|
|
1086
|
+
|
|
1087
|
+
describe('phaseRemove', () => {
|
|
1088
|
+
it('removes integer phase directory and renumbers subsequent phases', async () => {
|
|
1089
|
+
const { phaseRemove } = await import('./phase-lifecycle.js');
|
|
1090
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
1091
|
+
await setupTestProject(tmpDir, {
|
|
1092
|
+
roadmap: ROADMAP_FOR_REMOVE,
|
|
1093
|
+
state: STATE_FOR_REMOVE,
|
|
1094
|
+
phases: ['05-auth', '06-dashboard', '07-api'],
|
|
1095
|
+
});
|
|
1096
|
+
// Create files inside directories to verify file renaming
|
|
1097
|
+
await writeFile(join(phasesDir, '06-dashboard', '06-01-PLAN.md'), 'plan', 'utf-8');
|
|
1098
|
+
await writeFile(join(phasesDir, '07-api', '07-01-PLAN.md'), 'plan', 'utf-8');
|
|
1099
|
+
|
|
1100
|
+
const result = await phaseRemove(['6'], tmpDir);
|
|
1101
|
+
const data = result.data as Record<string, unknown>;
|
|
1102
|
+
|
|
1103
|
+
expect(data.removed).toBe('6');
|
|
1104
|
+
expect(data.directory_deleted).toBeTruthy();
|
|
1105
|
+
expect(data.roadmap_updated).toBe(true);
|
|
1106
|
+
expect(data.state_updated).toBe(true);
|
|
1107
|
+
|
|
1108
|
+
// Phase 6 dir should be gone
|
|
1109
|
+
const entries = await readdir(phasesDir, { withFileTypes: true });
|
|
1110
|
+
const dirNames = entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
1111
|
+
expect(dirNames.find(d => d.includes('06-dashboard'))).toBeUndefined();
|
|
1112
|
+
|
|
1113
|
+
// Phase 7 should have been renamed to 06
|
|
1114
|
+
const renamedDir = dirNames.find(d => d.includes('06-api'));
|
|
1115
|
+
expect(renamedDir).toBeTruthy();
|
|
1116
|
+
|
|
1117
|
+
// Files inside renamed dir should also be renamed
|
|
1118
|
+
const files = await readdir(join(phasesDir, renamedDir!));
|
|
1119
|
+
expect(files.some(f => f.includes('06-01'))).toBe(true);
|
|
1120
|
+
expect(files.some(f => f.includes('07-01'))).toBe(false);
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
it('removes decimal phase and renumbers sibling decimals', async () => {
|
|
1124
|
+
const { phaseRemove } = await import('./phase-lifecycle.js');
|
|
1125
|
+
const decimalRoadmap = ROADMAP_FOR_REMOVE.replace(
|
|
1126
|
+
'### Phase 7: API',
|
|
1127
|
+
'### Phase 6.1: Hotfix A\n\n**Goal:** Fix A\n**Plans:** 1 plans\n\n### Phase 6.2: Hotfix B\n\n**Goal:** Fix B\n**Plans:** 1 plans\n\n### Phase 6.3: Hotfix C\n\n**Goal:** Fix C\n**Plans:** 1 plans\n\n### Phase 7: API'
|
|
1128
|
+
);
|
|
1129
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
1130
|
+
await setupTestProject(tmpDir, {
|
|
1131
|
+
roadmap: decimalRoadmap,
|
|
1132
|
+
state: STATE_FOR_REMOVE,
|
|
1133
|
+
phases: ['05-auth', '06-dashboard', '06.1-hotfix-a', '06.2-hotfix-b', '06.3-hotfix-c', '07-api'],
|
|
1134
|
+
});
|
|
1135
|
+
// Create files with phase ID in name
|
|
1136
|
+
await writeFile(join(phasesDir, '06.2-hotfix-b', '06.2-01-PLAN.md'), 'plan', 'utf-8');
|
|
1137
|
+
await writeFile(join(phasesDir, '06.3-hotfix-c', '06.3-01-PLAN.md'), 'plan', 'utf-8');
|
|
1138
|
+
|
|
1139
|
+
const result = await phaseRemove(['6.1'], tmpDir);
|
|
1140
|
+
const data = result.data as Record<string, unknown>;
|
|
1141
|
+
|
|
1142
|
+
expect(data.removed).toBe('6.1');
|
|
1143
|
+
|
|
1144
|
+
// 06.1 should be gone
|
|
1145
|
+
const entries = await readdir(phasesDir, { withFileTypes: true });
|
|
1146
|
+
const dirNames = entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
1147
|
+
expect(dirNames.find(d => d.includes('06.1-hotfix-a'))).toBeUndefined();
|
|
1148
|
+
|
|
1149
|
+
// 06.2 should become 06.1, 06.3 should become 06.2
|
|
1150
|
+
expect(dirNames.find(d => d.includes('06.1-hotfix-b'))).toBeTruthy();
|
|
1151
|
+
expect(dirNames.find(d => d.includes('06.2-hotfix-c'))).toBeTruthy();
|
|
1152
|
+
expect(dirNames.find(d => d.includes('06.3'))).toBeUndefined();
|
|
1153
|
+
|
|
1154
|
+
// Files inside renamed dirs should be renamed
|
|
1155
|
+
const dir1Files = await readdir(join(phasesDir, '06.1-hotfix-b'));
|
|
1156
|
+
expect(dir1Files.some(f => f.includes('06.1-01'))).toBe(true);
|
|
1157
|
+
const dir2Files = await readdir(join(phasesDir, '06.2-hotfix-c'));
|
|
1158
|
+
expect(dir2Files.some(f => f.includes('06.2-01'))).toBe(true);
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
it('requires --force to remove phase with SUMMARY files', async () => {
|
|
1162
|
+
const { phaseRemove } = await import('./phase-lifecycle.js');
|
|
1163
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
1164
|
+
await setupTestProject(tmpDir, {
|
|
1165
|
+
roadmap: ROADMAP_FOR_REMOVE,
|
|
1166
|
+
state: STATE_FOR_REMOVE,
|
|
1167
|
+
phases: ['05-auth', '06-dashboard', '07-api'],
|
|
1168
|
+
});
|
|
1169
|
+
// Create a SUMMARY file to simulate executed work
|
|
1170
|
+
await writeFile(join(phasesDir, '06-dashboard', '06-01-SUMMARY.md'), 'summary', 'utf-8');
|
|
1171
|
+
|
|
1172
|
+
await expect(phaseRemove(['6'], tmpDir)).rejects.toThrow('--force');
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
it('allows removal with --force even when SUMMARY files exist', async () => {
|
|
1176
|
+
const { phaseRemove } = await import('./phase-lifecycle.js');
|
|
1177
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
1178
|
+
await setupTestProject(tmpDir, {
|
|
1179
|
+
roadmap: ROADMAP_FOR_REMOVE,
|
|
1180
|
+
state: STATE_FOR_REMOVE,
|
|
1181
|
+
phases: ['05-auth', '06-dashboard', '07-api'],
|
|
1182
|
+
});
|
|
1183
|
+
await writeFile(join(phasesDir, '06-dashboard', '06-01-SUMMARY.md'), 'summary', 'utf-8');
|
|
1184
|
+
|
|
1185
|
+
const result = await phaseRemove(['6', '--force'], tmpDir);
|
|
1186
|
+
const data = result.data as Record<string, unknown>;
|
|
1187
|
+
expect(data.removed).toBe('6');
|
|
1188
|
+
expect(data.directory_deleted).toBeTruthy();
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
it('bug-3409: accepts --force before phase id', async () => {
|
|
1192
|
+
const { phaseRemove } = await import('./phase-lifecycle.js');
|
|
1193
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
1194
|
+
await setupTestProject(tmpDir, {
|
|
1195
|
+
roadmap: ROADMAP_FOR_REMOVE,
|
|
1196
|
+
state: STATE_FOR_REMOVE,
|
|
1197
|
+
phases: ['05-auth', '06-dashboard', '07-api'],
|
|
1198
|
+
});
|
|
1199
|
+
await writeFile(join(phasesDir, '06-dashboard', '06-01-SUMMARY.md'), 'summary', 'utf-8');
|
|
1200
|
+
|
|
1201
|
+
const result = await phaseRemove(['--force', '6'], tmpDir);
|
|
1202
|
+
const data = result.data as Record<string, unknown>;
|
|
1203
|
+
expect(data.removed).toBe('6');
|
|
1204
|
+
expect(data.directory_deleted).toBeTruthy();
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
it('throws GSDError when ROADMAP.md is missing', async () => {
|
|
1208
|
+
const { phaseRemove } = await import('./phase-lifecycle.js');
|
|
1209
|
+
// Set up without ROADMAP.md
|
|
1210
|
+
const planningDir = join(tmpDir, '.planning');
|
|
1211
|
+
await mkdir(planningDir, { recursive: true });
|
|
1212
|
+
const phasesDir = join(planningDir, 'phases');
|
|
1213
|
+
await mkdir(phasesDir, { recursive: true });
|
|
1214
|
+
await writeFile(join(planningDir, 'STATE.md'), STATE_FOR_REMOVE, 'utf-8');
|
|
1215
|
+
|
|
1216
|
+
await expect(phaseRemove(['6'], tmpDir)).rejects.toThrow('ROADMAP.md not found');
|
|
1217
|
+
});
|
|
1218
|
+
|
|
1219
|
+
it('throws GSDError when phase number is missing', async () => {
|
|
1220
|
+
const { phaseRemove } = await import('./phase-lifecycle.js');
|
|
1221
|
+
await setupTestProject(tmpDir, {
|
|
1222
|
+
roadmap: ROADMAP_FOR_REMOVE,
|
|
1223
|
+
state: STATE_FOR_REMOVE,
|
|
1224
|
+
});
|
|
1225
|
+
|
|
1226
|
+
await expect(phaseRemove([], tmpDir)).rejects.toThrow('phase number required');
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
it('throws GSDError when target phase does not exist and does not mutate STATE.md', async () => {
|
|
1230
|
+
const { phaseRemove } = await import('./phase-lifecycle.js');
|
|
1231
|
+
await setupTestProject(tmpDir, {
|
|
1232
|
+
roadmap: ROADMAP_FOR_REMOVE,
|
|
1233
|
+
state: STATE_FOR_REMOVE,
|
|
1234
|
+
phases: ['05-auth', '06-dashboard', '07-api'],
|
|
1235
|
+
});
|
|
1236
|
+
|
|
1237
|
+
await expect(phaseRemove(['99'], tmpDir)).rejects.toThrow('Phase 99 not found');
|
|
1238
|
+
const stateContent = await readFile(join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
|
1239
|
+
expect(stateContent).toMatch(/total_phases:\s*7/);
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1242
|
+
it('updates ROADMAP.md by removing phase section and renumbering', async () => {
|
|
1243
|
+
const { phaseRemove } = await import('./phase-lifecycle.js');
|
|
1244
|
+
await setupTestProject(tmpDir, {
|
|
1245
|
+
roadmap: ROADMAP_FOR_REMOVE,
|
|
1246
|
+
state: STATE_FOR_REMOVE,
|
|
1247
|
+
phases: ['05-auth', '06-dashboard', '07-api'],
|
|
1248
|
+
});
|
|
1249
|
+
|
|
1250
|
+
await phaseRemove(['6'], tmpDir);
|
|
1251
|
+
|
|
1252
|
+
const roadmap = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
1253
|
+
// Phase 6 section should be removed
|
|
1254
|
+
expect(roadmap).not.toContain('### Phase 6: Dashboard');
|
|
1255
|
+
// Phase 7 should be renumbered to 6
|
|
1256
|
+
expect(roadmap).toContain('### Phase 6: API');
|
|
1257
|
+
// Plan references should be renumbered
|
|
1258
|
+
expect(roadmap).toContain('06-01');
|
|
1259
|
+
expect(roadmap).not.toContain('07-01');
|
|
1260
|
+
});
|
|
1261
|
+
|
|
1262
|
+
it('decrements total_phases in STATE.md frontmatter', async () => {
|
|
1263
|
+
const { phaseRemove } = await import('./phase-lifecycle.js');
|
|
1264
|
+
await setupTestProject(tmpDir, {
|
|
1265
|
+
roadmap: ROADMAP_FOR_REMOVE,
|
|
1266
|
+
state: STATE_FOR_REMOVE,
|
|
1267
|
+
phases: ['05-auth', '06-dashboard', '07-api'],
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
await phaseRemove(['6'], tmpDir);
|
|
1271
|
+
|
|
1272
|
+
const stateContent = await readFile(join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
|
1273
|
+
// total_phases should be decremented from 7 to 6
|
|
1274
|
+
expect(stateContent).toMatch(/total_phases:\s*6/);
|
|
1275
|
+
});
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
// ─── phaseComplete ─────────────────────────────────────────────────────────
|
|
1279
|
+
|
|
1280
|
+
const ROADMAP_FOR_COMPLETE = `# Roadmap
|
|
1281
|
+
|
|
1282
|
+
<details>
|
|
1283
|
+
<summary>v1.0 (shipped)</summary>
|
|
1284
|
+
|
|
1285
|
+
### Phase 1: Old Phase
|
|
1286
|
+
|
|
1287
|
+
**Goal:** Shipped already
|
|
1288
|
+
**Plans:** 2 plans
|
|
1289
|
+
|
|
1290
|
+
</details>
|
|
1291
|
+
|
|
1292
|
+
## Current Milestone: v3.0 SDK-First Migration
|
|
1293
|
+
|
|
1294
|
+
| Phase | Plans | Status | Completed |
|
|
1295
|
+
|-------|-------|--------|-----------|
|
|
1296
|
+
| 9. | 3/3 | Complete | 2026-04-01 |
|
|
1297
|
+
| 10. | 0/3 | In Progress | |
|
|
1298
|
+
| 11. | 0/2 | Not Started | |
|
|
1299
|
+
|
|
1300
|
+
- [x] Phase 9: Foundation (completed 2026-04-01)
|
|
1301
|
+
- [ ] Phase 10: Read-Only Queries
|
|
1302
|
+
- [ ] Phase 11: Final Phase
|
|
1303
|
+
|
|
1304
|
+
### Phase 9: Foundation
|
|
1305
|
+
|
|
1306
|
+
**Goal:** Build foundation
|
|
1307
|
+
**Requirements**: FOUND-01, FOUND-02
|
|
1308
|
+
**Depends on:** Phase 8
|
|
1309
|
+
**Plans:** 3/3 plans complete
|
|
1310
|
+
|
|
1311
|
+
Plans:
|
|
1312
|
+
- [x] 09-01 (Foundation setup)
|
|
1313
|
+
- [x] 09-02 (Foundation core)
|
|
1314
|
+
- [x] 09-03 (Foundation tests)
|
|
1315
|
+
|
|
1316
|
+
### Phase 10: Read-Only Queries
|
|
1317
|
+
|
|
1318
|
+
**Goal:** Port queries
|
|
1319
|
+
**Requirements**: QUERY-01
|
|
1320
|
+
**Depends on:** Phase 9
|
|
1321
|
+
**Plans:** 3 plans
|
|
1322
|
+
|
|
1323
|
+
Plans:
|
|
1324
|
+
- [x] 10-01 (Query setup)
|
|
1325
|
+
- [x] 10-02 (Query core)
|
|
1326
|
+
- [ ] 10-03 (Query tests)
|
|
1327
|
+
|
|
1328
|
+
### Phase 11: Final Phase
|
|
1329
|
+
|
|
1330
|
+
**Goal:** Final work
|
|
1331
|
+
**Requirements**: FINAL-01
|
|
1332
|
+
**Depends on:** Phase 10
|
|
1333
|
+
**Plans:** 2 plans
|
|
1334
|
+
|
|
1335
|
+
Plans:
|
|
1336
|
+
- [ ] 11-01 (Final setup)
|
|
1337
|
+
- [ ] 11-02 (Final complete)
|
|
1338
|
+
|
|
1339
|
+
---
|
|
1340
|
+
*Last updated: 2026-04-08*
|
|
1341
|
+
`;
|
|
1342
|
+
|
|
1343
|
+
const STATE_FOR_COMPLETE = `---
|
|
1344
|
+
gsd_state_version: 1.0
|
|
1345
|
+
milestone: v3.0
|
|
1346
|
+
milestone_name: SDK-First Migration
|
|
1347
|
+
status: executing
|
|
1348
|
+
progress:
|
|
1349
|
+
total_phases: 3
|
|
1350
|
+
completed_phases: 1
|
|
1351
|
+
total_plans: 8
|
|
1352
|
+
completed_plans: 5
|
|
1353
|
+
percent: 33
|
|
1354
|
+
---
|
|
1355
|
+
|
|
1356
|
+
# Project State
|
|
1357
|
+
|
|
1358
|
+
## Current Position
|
|
1359
|
+
|
|
1360
|
+
Phase: 10 of 3 (Read-Only Queries) — EXECUTING
|
|
1361
|
+
Plan: 3 of 3
|
|
1362
|
+
Status: Executing Phase 10
|
|
1363
|
+
Last activity: 2026-04-08
|
|
1364
|
+
|
|
1365
|
+
## Performance Metrics
|
|
1366
|
+
|
|
1367
|
+
**Velocity:**
|
|
1368
|
+
|
|
1369
|
+
- Total plans completed: 3
|
|
1370
|
+
- Average duration: --
|
|
1371
|
+
- Total execution time: 0 hours
|
|
1372
|
+
|
|
1373
|
+
**By Phase:**
|
|
1374
|
+
|
|
1375
|
+
| Phase | Plans | Total | Avg/Plan |
|
|
1376
|
+
|-------|-------|-------|----------|
|
|
1377
|
+
| 9 | 3 | - | - |
|
|
1378
|
+
|
|
1379
|
+
## Session Continuity
|
|
1380
|
+
|
|
1381
|
+
Last session: 2026-04-08T10:00:00.000Z
|
|
1382
|
+
Stopped at: Completed 10-03-PLAN.md
|
|
1383
|
+
`;
|
|
1384
|
+
|
|
1385
|
+
const REQUIREMENTS_FOR_COMPLETE = `# Requirements
|
|
1386
|
+
|
|
1387
|
+
## Checklist
|
|
1388
|
+
|
|
1389
|
+
- [x] **FOUND-01** Foundation setup
|
|
1390
|
+
- [x] **FOUND-02** Foundation core
|
|
1391
|
+
- [ ] **QUERY-01** Query implementation
|
|
1392
|
+
- [ ] **FINAL-01** Final work
|
|
1393
|
+
|
|
1394
|
+
## Traceability
|
|
1395
|
+
|
|
1396
|
+
| Requirement | Phase | Status |
|
|
1397
|
+
|-------------|-------|--------|
|
|
1398
|
+
| FOUND-01 | Phase 9 | Complete |
|
|
1399
|
+
| FOUND-02 | Phase 9 | Complete |
|
|
1400
|
+
| QUERY-01 | Phase 10 | In Progress |
|
|
1401
|
+
| FINAL-01 | Phase 11 | Pending |
|
|
1402
|
+
`;
|
|
1403
|
+
|
|
1404
|
+
describe('phaseComplete', () => {
|
|
1405
|
+
it('marks phase checkbox, updates progress table, and plan count in ROADMAP.md', async () => {
|
|
1406
|
+
const { phaseComplete } = await import('./phase-lifecycle.js');
|
|
1407
|
+
await setupTestProject(tmpDir, {
|
|
1408
|
+
roadmap: ROADMAP_FOR_COMPLETE,
|
|
1409
|
+
state: STATE_FOR_COMPLETE,
|
|
1410
|
+
phases: ['09-foundation', '10-read-only-queries', '11-final-phase'],
|
|
1411
|
+
});
|
|
1412
|
+
// Create PLAN and SUMMARY files for phase 10
|
|
1413
|
+
const p10Dir = join(tmpDir, '.planning', 'phases', '10-read-only-queries');
|
|
1414
|
+
await writeFile(join(p10Dir, '10-01-PLAN.md'), 'plan1', 'utf-8');
|
|
1415
|
+
await writeFile(join(p10Dir, '10-02-PLAN.md'), 'plan2', 'utf-8');
|
|
1416
|
+
await writeFile(join(p10Dir, '10-03-PLAN.md'), 'plan3', 'utf-8');
|
|
1417
|
+
await writeFile(join(p10Dir, '10-01-SUMMARY.md'), 'summary1', 'utf-8');
|
|
1418
|
+
await writeFile(join(p10Dir, '10-02-SUMMARY.md'), 'summary2', 'utf-8');
|
|
1419
|
+
await writeFile(join(p10Dir, '10-03-SUMMARY.md'), 'summary3', 'utf-8');
|
|
1420
|
+
// Create REQUIREMENTS.md
|
|
1421
|
+
await writeFile(join(tmpDir, '.planning', 'REQUIREMENTS.md'), REQUIREMENTS_FOR_COMPLETE, 'utf-8');
|
|
1422
|
+
|
|
1423
|
+
const result = await phaseComplete(['10'], tmpDir);
|
|
1424
|
+
const data = result.data as Record<string, unknown>;
|
|
1425
|
+
|
|
1426
|
+
expect(data.completed_phase).toBe('10');
|
|
1427
|
+
expect(data.plans_executed).toBe('3/3');
|
|
1428
|
+
expect(data.is_last_phase).toBe(false);
|
|
1429
|
+
expect(data.next_phase).toBeTruthy();
|
|
1430
|
+
expect(data.roadmap_updated).toBe(true);
|
|
1431
|
+
|
|
1432
|
+
// Check ROADMAP.md updates
|
|
1433
|
+
const roadmap = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
1434
|
+
// Checkbox should be marked
|
|
1435
|
+
expect(roadmap).toMatch(/\[x\].*Phase 10/);
|
|
1436
|
+
// Progress table should show Complete
|
|
1437
|
+
expect(roadmap).toMatch(/10\.?\s*\|.*3\/3.*\|.*Complete/i);
|
|
1438
|
+
// Plan count in section should be updated
|
|
1439
|
+
expect(roadmap).toContain('3/3 plans complete');
|
|
1440
|
+
// Plan checkboxes should be [x]
|
|
1441
|
+
expect(roadmap).toMatch(/\[x\] 10-01/);
|
|
1442
|
+
expect(roadmap).toMatch(/\[x\] 10-02/);
|
|
1443
|
+
expect(roadmap).toMatch(/\[x\] 10-03/);
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
it('updates REQUIREMENTS.md checkboxes and traceability table', async () => {
|
|
1447
|
+
const { phaseComplete } = await import('./phase-lifecycle.js');
|
|
1448
|
+
await setupTestProject(tmpDir, {
|
|
1449
|
+
roadmap: ROADMAP_FOR_COMPLETE,
|
|
1450
|
+
state: STATE_FOR_COMPLETE,
|
|
1451
|
+
phases: ['09-foundation', '10-read-only-queries', '11-final-phase'],
|
|
1452
|
+
});
|
|
1453
|
+
const p10Dir = join(tmpDir, '.planning', 'phases', '10-read-only-queries');
|
|
1454
|
+
await writeFile(join(p10Dir, '10-01-PLAN.md'), 'plan1', 'utf-8');
|
|
1455
|
+
await writeFile(join(p10Dir, '10-02-PLAN.md'), 'plan2', 'utf-8');
|
|
1456
|
+
await writeFile(join(p10Dir, '10-03-PLAN.md'), 'plan3', 'utf-8');
|
|
1457
|
+
await writeFile(join(p10Dir, '10-01-SUMMARY.md'), 'summary1', 'utf-8');
|
|
1458
|
+
await writeFile(join(p10Dir, '10-02-SUMMARY.md'), 'summary2', 'utf-8');
|
|
1459
|
+
await writeFile(join(p10Dir, '10-03-SUMMARY.md'), 'summary3', 'utf-8');
|
|
1460
|
+
await writeFile(join(tmpDir, '.planning', 'REQUIREMENTS.md'), REQUIREMENTS_FOR_COMPLETE, 'utf-8');
|
|
1461
|
+
|
|
1462
|
+
await phaseComplete(['10'], tmpDir);
|
|
1463
|
+
|
|
1464
|
+
const req = await readFile(join(tmpDir, '.planning', 'REQUIREMENTS.md'), 'utf-8');
|
|
1465
|
+
// QUERY-01 checkbox should be marked
|
|
1466
|
+
expect(req).toMatch(/\[x\].*\*\*QUERY-01\*\*/);
|
|
1467
|
+
// Traceability should show Complete for QUERY-01
|
|
1468
|
+
expect(req).toMatch(/QUERY-01\s*\|.*\|\s*Complete\s*\|/);
|
|
1469
|
+
// FINAL-01 should remain Pending
|
|
1470
|
+
expect(req).toMatch(/FINAL-01\s*\|.*\|\s*Pending\s*\|/);
|
|
1471
|
+
});
|
|
1472
|
+
|
|
1473
|
+
it('updates STATE.md fields: current phase, status, completed phases, percent', async () => {
|
|
1474
|
+
const { phaseComplete } = await import('./phase-lifecycle.js');
|
|
1475
|
+
await setupTestProject(tmpDir, {
|
|
1476
|
+
roadmap: ROADMAP_FOR_COMPLETE,
|
|
1477
|
+
state: STATE_FOR_COMPLETE,
|
|
1478
|
+
phases: ['09-foundation', '10-read-only-queries', '11-final-phase'],
|
|
1479
|
+
});
|
|
1480
|
+
const p10Dir = join(tmpDir, '.planning', 'phases', '10-read-only-queries');
|
|
1481
|
+
await writeFile(join(p10Dir, '10-01-PLAN.md'), 'plan', 'utf-8');
|
|
1482
|
+
await writeFile(join(p10Dir, '10-02-PLAN.md'), 'plan', 'utf-8');
|
|
1483
|
+
await writeFile(join(p10Dir, '10-03-PLAN.md'), 'plan', 'utf-8');
|
|
1484
|
+
await writeFile(join(p10Dir, '10-01-SUMMARY.md'), 'summary', 'utf-8');
|
|
1485
|
+
await writeFile(join(p10Dir, '10-02-SUMMARY.md'), 'summary', 'utf-8');
|
|
1486
|
+
await writeFile(join(p10Dir, '10-03-SUMMARY.md'), 'summary', 'utf-8');
|
|
1487
|
+
await writeFile(join(tmpDir, '.planning', 'REQUIREMENTS.md'), REQUIREMENTS_FOR_COMPLETE, 'utf-8');
|
|
1488
|
+
|
|
1489
|
+
await phaseComplete(['10'], tmpDir);
|
|
1490
|
+
|
|
1491
|
+
const state = await readFile(join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
|
1492
|
+
// Phase should advance to 11
|
|
1493
|
+
expect(state).toMatch(/Phase:\s*11/);
|
|
1494
|
+
// Status should indicate ready to plan
|
|
1495
|
+
expect(state).toMatch(/Status:\s*Ready to plan/);
|
|
1496
|
+
// Completed phases should be incremented from 1 to 2
|
|
1497
|
+
expect(state).toMatch(/completed_phases:\s*2/);
|
|
1498
|
+
// Percent should be recalculated (2/3 = 67%)
|
|
1499
|
+
expect(state).toMatch(/percent:\s*67/);
|
|
1500
|
+
});
|
|
1501
|
+
|
|
1502
|
+
it('detects next phase from filesystem, falls back to ROADMAP.md', async () => {
|
|
1503
|
+
const { phaseComplete } = await import('./phase-lifecycle.js');
|
|
1504
|
+
await setupTestProject(tmpDir, {
|
|
1505
|
+
roadmap: ROADMAP_FOR_COMPLETE,
|
|
1506
|
+
state: STATE_FOR_COMPLETE,
|
|
1507
|
+
phases: ['09-foundation', '10-read-only-queries', '11-final-phase'],
|
|
1508
|
+
});
|
|
1509
|
+
const p10Dir = join(tmpDir, '.planning', 'phases', '10-read-only-queries');
|
|
1510
|
+
await writeFile(join(p10Dir, '10-01-PLAN.md'), 'plan', 'utf-8');
|
|
1511
|
+
await writeFile(join(p10Dir, '10-01-SUMMARY.md'), 'summary', 'utf-8');
|
|
1512
|
+
await writeFile(join(tmpDir, '.planning', 'REQUIREMENTS.md'), REQUIREMENTS_FOR_COMPLETE, 'utf-8');
|
|
1513
|
+
|
|
1514
|
+
const result = await phaseComplete(['10'], tmpDir);
|
|
1515
|
+
const data = result.data as Record<string, unknown>;
|
|
1516
|
+
|
|
1517
|
+
// Next phase should be 11 (from filesystem)
|
|
1518
|
+
expect(data.next_phase).toBe('11');
|
|
1519
|
+
expect(data.is_last_phase).toBe(false);
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
it('sets is_last_phase when completing the final phase', async () => {
|
|
1523
|
+
const { phaseComplete } = await import('./phase-lifecycle.js');
|
|
1524
|
+
await setupTestProject(tmpDir, {
|
|
1525
|
+
roadmap: ROADMAP_FOR_COMPLETE,
|
|
1526
|
+
state: STATE_FOR_COMPLETE,
|
|
1527
|
+
phases: ['09-foundation', '10-read-only-queries', '11-final-phase'],
|
|
1528
|
+
});
|
|
1529
|
+
const p11Dir = join(tmpDir, '.planning', 'phases', '11-final-phase');
|
|
1530
|
+
await writeFile(join(p11Dir, '11-01-PLAN.md'), 'plan', 'utf-8');
|
|
1531
|
+
await writeFile(join(p11Dir, '11-01-SUMMARY.md'), 'summary', 'utf-8');
|
|
1532
|
+
await writeFile(join(tmpDir, '.planning', 'REQUIREMENTS.md'), REQUIREMENTS_FOR_COMPLETE, 'utf-8');
|
|
1533
|
+
|
|
1534
|
+
const result = await phaseComplete(['11'], tmpDir);
|
|
1535
|
+
const data = result.data as Record<string, unknown>;
|
|
1536
|
+
|
|
1537
|
+
expect(data.is_last_phase).toBe(true);
|
|
1538
|
+
expect(data.next_phase).toBeNull();
|
|
1539
|
+
|
|
1540
|
+
// State should show milestone complete
|
|
1541
|
+
const state = await readFile(join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
|
1542
|
+
expect(state).toMatch(/Status:\s*Milestone complete/);
|
|
1543
|
+
});
|
|
1544
|
+
|
|
1545
|
+
it('collects UAT/VERIFICATION warnings without blocking', async () => {
|
|
1546
|
+
const { phaseComplete } = await import('./phase-lifecycle.js');
|
|
1547
|
+
await setupTestProject(tmpDir, {
|
|
1548
|
+
roadmap: ROADMAP_FOR_COMPLETE,
|
|
1549
|
+
state: STATE_FOR_COMPLETE,
|
|
1550
|
+
phases: ['09-foundation', '10-read-only-queries', '11-final-phase'],
|
|
1551
|
+
});
|
|
1552
|
+
const p10Dir = join(tmpDir, '.planning', 'phases', '10-read-only-queries');
|
|
1553
|
+
await writeFile(join(p10Dir, '10-01-PLAN.md'), 'plan', 'utf-8');
|
|
1554
|
+
await writeFile(join(p10Dir, '10-01-SUMMARY.md'), 'summary', 'utf-8');
|
|
1555
|
+
// Create UAT file with pending status
|
|
1556
|
+
await writeFile(join(p10Dir, '10-UAT.md'), '---\nresult: pending\n---\nPending tests', 'utf-8');
|
|
1557
|
+
// Create VERIFICATION file with gaps
|
|
1558
|
+
await writeFile(join(p10Dir, '10-VERIFICATION.md'), '---\nstatus: gaps_found\n---\nGaps', 'utf-8');
|
|
1559
|
+
await writeFile(join(tmpDir, '.planning', 'REQUIREMENTS.md'), REQUIREMENTS_FOR_COMPLETE, 'utf-8');
|
|
1560
|
+
|
|
1561
|
+
const result = await phaseComplete(['10'], tmpDir);
|
|
1562
|
+
const data = result.data as Record<string, unknown>;
|
|
1563
|
+
|
|
1564
|
+
// Should complete despite warnings
|
|
1565
|
+
expect(data.completed_phase).toBe('10');
|
|
1566
|
+
expect(data.has_warnings).toBe(true);
|
|
1567
|
+
const warnings = data.warnings as string[];
|
|
1568
|
+
expect(warnings.length).toBeGreaterThan(0);
|
|
1569
|
+
expect(warnings.some(w => w.includes('pending'))).toBe(true);
|
|
1570
|
+
expect(warnings.some(w => w.includes('gaps'))).toBe(true);
|
|
1571
|
+
});
|
|
1572
|
+
|
|
1573
|
+
it('throws GSDError for missing phase', async () => {
|
|
1574
|
+
const { phaseComplete } = await import('./phase-lifecycle.js');
|
|
1575
|
+
await setupTestProject(tmpDir, {
|
|
1576
|
+
roadmap: ROADMAP_FOR_COMPLETE,
|
|
1577
|
+
state: STATE_FOR_COMPLETE,
|
|
1578
|
+
phases: ['09-foundation'],
|
|
1579
|
+
});
|
|
1580
|
+
await writeFile(join(tmpDir, '.planning', 'REQUIREMENTS.md'), REQUIREMENTS_FOR_COMPLETE, 'utf-8');
|
|
1581
|
+
|
|
1582
|
+
await expect(phaseComplete(['99'], tmpDir)).rejects.toThrow('Phase 99 not found');
|
|
1583
|
+
});
|
|
1584
|
+
|
|
1585
|
+
it('updates performance metrics table in STATE.md', async () => {
|
|
1586
|
+
const { phaseComplete } = await import('./phase-lifecycle.js');
|
|
1587
|
+
await setupTestProject(tmpDir, {
|
|
1588
|
+
roadmap: ROADMAP_FOR_COMPLETE,
|
|
1589
|
+
state: STATE_FOR_COMPLETE,
|
|
1590
|
+
phases: ['09-foundation', '10-read-only-queries', '11-final-phase'],
|
|
1591
|
+
});
|
|
1592
|
+
const p10Dir = join(tmpDir, '.planning', 'phases', '10-read-only-queries');
|
|
1593
|
+
await writeFile(join(p10Dir, '10-01-PLAN.md'), 'plan', 'utf-8');
|
|
1594
|
+
await writeFile(join(p10Dir, '10-02-PLAN.md'), 'plan', 'utf-8');
|
|
1595
|
+
await writeFile(join(p10Dir, '10-03-PLAN.md'), 'plan', 'utf-8');
|
|
1596
|
+
await writeFile(join(p10Dir, '10-01-SUMMARY.md'), 'summary', 'utf-8');
|
|
1597
|
+
await writeFile(join(p10Dir, '10-02-SUMMARY.md'), 'summary', 'utf-8');
|
|
1598
|
+
await writeFile(join(p10Dir, '10-03-SUMMARY.md'), 'summary', 'utf-8');
|
|
1599
|
+
await writeFile(join(tmpDir, '.planning', 'REQUIREMENTS.md'), REQUIREMENTS_FOR_COMPLETE, 'utf-8');
|
|
1600
|
+
|
|
1601
|
+
await phaseComplete(['10'], tmpDir);
|
|
1602
|
+
|
|
1603
|
+
const state = await readFile(join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
|
1604
|
+
// Total plans completed should be incremented: 3 + 3 = 6
|
|
1605
|
+
expect(state).toContain('Total plans completed: 6');
|
|
1606
|
+
// By Phase table should have a row for phase 10
|
|
1607
|
+
expect(state).toMatch(/\|\s*10\s*\|\s*3\s*\|/);
|
|
1608
|
+
});
|
|
1609
|
+
|
|
1610
|
+
it('does not overwrite plan checkbox when **Plans:** is on its own line (regression #2728)', async () => {
|
|
1611
|
+
const { phaseComplete } = await import('./phase-lifecycle.js');
|
|
1612
|
+
|
|
1613
|
+
const roadmap = [
|
|
1614
|
+
'# Roadmap',
|
|
1615
|
+
'',
|
|
1616
|
+
'## Current Milestone: v3.0',
|
|
1617
|
+
'',
|
|
1618
|
+
'- [ ] Phase 7: marketing-landing-v2',
|
|
1619
|
+
'',
|
|
1620
|
+
'### Phase 7: marketing-landing-v2',
|
|
1621
|
+
'',
|
|
1622
|
+
'**Goal:** Landing page',
|
|
1623
|
+
'**Plans:**',
|
|
1624
|
+
'- [x] 07-01-cherry-pick-foundation-PLAN.md — Wave 1',
|
|
1625
|
+
'- [x] 07-02-routing-auth-seo-PLAN.md — Wave 2',
|
|
1626
|
+
'',
|
|
1627
|
+
'### Phase 8: p3-nice-to-haves',
|
|
1628
|
+
'',
|
|
1629
|
+
'**Goal:** Nice to haves',
|
|
1630
|
+
'**Plans:** 3 plans',
|
|
1631
|
+
'',
|
|
1632
|
+
].join('\n');
|
|
1633
|
+
|
|
1634
|
+
const state = [
|
|
1635
|
+
'---',
|
|
1636
|
+
'gsd_state_version: 1.0',
|
|
1637
|
+
'milestone: v3.0',
|
|
1638
|
+
'status: executing',
|
|
1639
|
+
'progress:',
|
|
1640
|
+
' total_phases: 2',
|
|
1641
|
+
' completed_phases: 0',
|
|
1642
|
+
' total_plans: 4',
|
|
1643
|
+
' completed_plans: 2',
|
|
1644
|
+
' percent: 50',
|
|
1645
|
+
'---',
|
|
1646
|
+
'',
|
|
1647
|
+
'# Project State',
|
|
1648
|
+
'',
|
|
1649
|
+
'Phase: 7 of 2 — EXECUTING',
|
|
1650
|
+
'Status: Executing Phase 7',
|
|
1651
|
+
].join('\n');
|
|
1652
|
+
|
|
1653
|
+
await setupTestProject(tmpDir, {
|
|
1654
|
+
roadmap,
|
|
1655
|
+
state,
|
|
1656
|
+
phases: ['07-marketing-landing-v2', '08-p3-nice-to-haves'],
|
|
1657
|
+
});
|
|
1658
|
+
|
|
1659
|
+
const p7Dir = join(tmpDir, '.planning', 'phases', '07-marketing-landing-v2');
|
|
1660
|
+
await writeFile(join(p7Dir, '07-01-PLAN.md'), 'plan1', 'utf-8');
|
|
1661
|
+
await writeFile(join(p7Dir, '07-02-PLAN.md'), 'plan2', 'utf-8');
|
|
1662
|
+
await writeFile(join(p7Dir, '07-01-SUMMARY.md'), 'summary1', 'utf-8');
|
|
1663
|
+
await writeFile(join(p7Dir, '07-02-SUMMARY.md'), 'summary2', 'utf-8');
|
|
1664
|
+
|
|
1665
|
+
await phaseComplete(['7'], tmpDir);
|
|
1666
|
+
|
|
1667
|
+
const updated = await readFile(join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
|
1668
|
+
|
|
1669
|
+
// The plan lines must NOT be replaced with "N/N plans complete"
|
|
1670
|
+
expect(updated).toContain('07-01-cherry-pick-foundation-PLAN.md');
|
|
1671
|
+
expect(updated).toContain('07-02-routing-auth-seo-PLAN.md');
|
|
1672
|
+
expect(updated).not.toMatch(/^2\/2 plans complete/m);
|
|
1673
|
+
|
|
1674
|
+
// Phase 8's **Plans:** line must NOT be touched
|
|
1675
|
+
expect(updated).toContain('**Plans:** 3 plans');
|
|
1676
|
+
});
|
|
1677
|
+
});
|
|
1678
|
+
|
|
1679
|
+
// ─── phasesClear ────────────────────────────────────────────────────────────
|
|
1680
|
+
|
|
1681
|
+
describe('phasesClear', () => {
|
|
1682
|
+
it('throws GSDError without --confirm flag, showing count', async () => {
|
|
1683
|
+
const { phasesClear } = await import('./phase-lifecycle.js');
|
|
1684
|
+
await setupTestProject(tmpDir, {
|
|
1685
|
+
phases: ['09-foundation', '10-read-only-queries', '999.1-backlog'],
|
|
1686
|
+
});
|
|
1687
|
+
|
|
1688
|
+
// Should throw with count of dirs to delete (2, not 3 since 999.1 is excluded)
|
|
1689
|
+
await expect(phasesClear([], tmpDir)).rejects.toThrow(/2 phase director/);
|
|
1690
|
+
});
|
|
1691
|
+
|
|
1692
|
+
it('deletes all dirs except 999.x with --confirm', async () => {
|
|
1693
|
+
const { phasesClear } = await import('./phase-lifecycle.js');
|
|
1694
|
+
await setupTestProject(tmpDir, {
|
|
1695
|
+
phases: ['09-foundation', '10-read-only-queries', '999.1-backlog'],
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1698
|
+
const result = await phasesClear(['--confirm'], tmpDir);
|
|
1699
|
+
const data = result.data as Record<string, unknown>;
|
|
1700
|
+
|
|
1701
|
+
expect(data.cleared).toBe(2);
|
|
1702
|
+
|
|
1703
|
+
// Verify filesystem
|
|
1704
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
1705
|
+
const entries = await readdir(phasesDir, { withFileTypes: true });
|
|
1706
|
+
const dirNames = entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
1707
|
+
expect(dirNames.length).toBe(1);
|
|
1708
|
+
expect(dirNames[0]).toContain('999');
|
|
1709
|
+
});
|
|
1710
|
+
|
|
1711
|
+
it('returns 0 cleared when phases dir is empty', async () => {
|
|
1712
|
+
const { phasesClear } = await import('./phase-lifecycle.js');
|
|
1713
|
+
await setupTestProject(tmpDir, { phases: [] });
|
|
1714
|
+
|
|
1715
|
+
const result = await phasesClear(['--confirm'], tmpDir);
|
|
1716
|
+
const data = result.data as Record<string, unknown>;
|
|
1717
|
+
expect(data.cleared).toBe(0);
|
|
1718
|
+
});
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
// ─── phasesArchive ──────────────────────────────────────────────────────────
|
|
1722
|
+
|
|
1723
|
+
describe('phasesArchive', () => {
|
|
1724
|
+
it('moves milestone phase dirs to milestones/{version}-phases/', async () => {
|
|
1725
|
+
const { phasesArchive } = await import('./phase-lifecycle.js');
|
|
1726
|
+
await setupTestProject(tmpDir, {
|
|
1727
|
+
phases: ['09-foundation', '10-read-only-queries'],
|
|
1728
|
+
});
|
|
1729
|
+
|
|
1730
|
+
const result = await phasesArchive(['v3.0'], tmpDir);
|
|
1731
|
+
const data = result.data as Record<string, unknown>;
|
|
1732
|
+
|
|
1733
|
+
expect(data.version).toBe('v3.0');
|
|
1734
|
+
expect((data.archived as number)).toBeGreaterThan(0);
|
|
1735
|
+
|
|
1736
|
+
// Verify archive directory exists
|
|
1737
|
+
const archiveDir = join(tmpDir, '.planning', 'milestones', 'v3.0-phases');
|
|
1738
|
+
expect(existsSync(archiveDir)).toBe(true);
|
|
1739
|
+
|
|
1740
|
+
// Verify dirs were moved
|
|
1741
|
+
const archivedEntries = await readdir(archiveDir, { withFileTypes: true });
|
|
1742
|
+
const archivedDirs = archivedEntries.filter(e => e.isDirectory()).map(e => e.name);
|
|
1743
|
+
expect(archivedDirs.length).toBeGreaterThan(0);
|
|
1744
|
+
|
|
1745
|
+
// Original dirs should be gone
|
|
1746
|
+
const phasesDir = join(tmpDir, '.planning', 'phases');
|
|
1747
|
+
const remaining = await readdir(phasesDir, { withFileTypes: true });
|
|
1748
|
+
const remainingDirs = remaining.filter(e => e.isDirectory()).map(e => e.name);
|
|
1749
|
+
expect(remainingDirs.length).toBe(0);
|
|
1750
|
+
});
|
|
1751
|
+
});
|
|
1752
|
+
|
|
1753
|
+
// ─── milestoneComplete help-flag defense (#3259) ────────────────────────────
|
|
1754
|
+
|
|
1755
|
+
describe('milestoneComplete help-flag defense', () => {
|
|
1756
|
+
it('rejects --help as a version value with GSDError before any disk write', async () => {
|
|
1757
|
+
const { milestoneComplete } = await import('./phase-lifecycle.js');
|
|
1758
|
+
const { GSDError, ErrorClassification } = await import('../errors.js');
|
|
1759
|
+
await setupTestProject(tmpDir);
|
|
1760
|
+
|
|
1761
|
+
// Capture pre-invocation filesystem state
|
|
1762
|
+
const planningDir = join(tmpDir, '.planning');
|
|
1763
|
+
const milestonesPath = join(planningDir, 'MILESTONES.md');
|
|
1764
|
+
const statePath = join(planningDir, 'STATE.md');
|
|
1765
|
+
const preStateStat = await import('node:fs').then((m) => m.statSync(statePath));
|
|
1766
|
+
const milestonesExistedBefore = existsSync(milestonesPath);
|
|
1767
|
+
|
|
1768
|
+
let thrown: unknown;
|
|
1769
|
+
try {
|
|
1770
|
+
await milestoneComplete(['--help'], tmpDir);
|
|
1771
|
+
} catch (e) {
|
|
1772
|
+
thrown = e;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
expect(thrown).toBeInstanceOf(GSDError);
|
|
1776
|
+
const err = thrown as InstanceType<typeof GSDError>;
|
|
1777
|
+
expect(err.classification).toBe(ErrorClassification.Validation);
|
|
1778
|
+
expect(err.message).toContain('--help');
|
|
1779
|
+
|
|
1780
|
+
// Assert no files were written
|
|
1781
|
+
const postStateStat = await import('node:fs').then((m) => m.statSync(statePath));
|
|
1782
|
+
expect(postStateStat.mtimeMs).toBe(preStateStat.mtimeMs);
|
|
1783
|
+
expect(existsSync(milestonesPath)).toBe(milestonesExistedBefore);
|
|
1784
|
+
});
|
|
1785
|
+
|
|
1786
|
+
it('rejects -h as a version value with GSDError before any disk write', async () => {
|
|
1787
|
+
const { milestoneComplete } = await import('./phase-lifecycle.js');
|
|
1788
|
+
const { GSDError, ErrorClassification } = await import('../errors.js');
|
|
1789
|
+
await setupTestProject(tmpDir);
|
|
1790
|
+
|
|
1791
|
+
const statePath = join(tmpDir, '.planning', 'STATE.md');
|
|
1792
|
+
const preStateStat = await import('node:fs').then((m) => m.statSync(statePath));
|
|
1793
|
+
const milestonesPath = join(tmpDir, '.planning', 'MILESTONES.md');
|
|
1794
|
+
const milestonesExistedBefore = existsSync(milestonesPath);
|
|
1795
|
+
|
|
1796
|
+
let thrown: unknown;
|
|
1797
|
+
try {
|
|
1798
|
+
await milestoneComplete(['-h'], tmpDir);
|
|
1799
|
+
} catch (e) {
|
|
1800
|
+
thrown = e;
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
expect(thrown).toBeInstanceOf(GSDError);
|
|
1804
|
+
const err = thrown as InstanceType<typeof GSDError>;
|
|
1805
|
+
expect(err.classification).toBe(ErrorClassification.Validation);
|
|
1806
|
+
expect(err.message).toContain('-h');
|
|
1807
|
+
|
|
1808
|
+
// Assert no files were written
|
|
1809
|
+
const postStateStat = await import('node:fs').then((m) => m.statSync(statePath));
|
|
1810
|
+
expect(postStateStat.mtimeMs).toBe(preStateStat.mtimeMs);
|
|
1811
|
+
expect(existsSync(milestonesPath)).toBe(milestonesExistedBefore);
|
|
1812
|
+
});
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1815
|
+
// ─── Registry integration ──────────────────────────────────────────────────
|
|
1816
|
+
|
|
1817
|
+
describe('lifecycle handlers in registry', () => {
|
|
1818
|
+
// Registry assembly (invariant checks + large handler map) routinely takes
|
|
1819
|
+
// 5–10 s on a cold ESM module cache. The second test below reuses the
|
|
1820
|
+
// cached import and completes instantly, but the first import is the cold
|
|
1821
|
+
// path. Raise the per-test timeout so the warm-up doesn't surface as a
|
|
1822
|
+
// spurious "Test timed out" failure (pre-existing: STACK_TRACE_ERROR mask).
|
|
1823
|
+
it('registers all 7 lifecycle handlers with dot notation', async () => {
|
|
1824
|
+
const { createRegistry } = await import('./index.js');
|
|
1825
|
+
const registry = createRegistry();
|
|
1826
|
+
|
|
1827
|
+
const commands = [
|
|
1828
|
+
'phase.add', 'phase.insert', 'phase.remove', 'phase.complete',
|
|
1829
|
+
'phase.scaffold', 'phases.clear', 'phases.archive',
|
|
1830
|
+
];
|
|
1831
|
+
|
|
1832
|
+
for (const cmd of commands) {
|
|
1833
|
+
const handler = registry.getHandler(cmd);
|
|
1834
|
+
expect(handler, `${cmd} should be registered`).toBeDefined();
|
|
1835
|
+
}
|
|
1836
|
+
}, 30_000);
|
|
1837
|
+
|
|
1838
|
+
it('registers space-delimited aliases', async () => {
|
|
1839
|
+
const { createRegistry } = await import('./index.js');
|
|
1840
|
+
const registry = createRegistry();
|
|
1841
|
+
|
|
1842
|
+
const commands = [
|
|
1843
|
+
'phase add', 'phase insert', 'phase remove', 'phase complete',
|
|
1844
|
+
'phase scaffold', 'phases clear', 'phases archive',
|
|
1845
|
+
];
|
|
1846
|
+
|
|
1847
|
+
for (const cmd of commands) {
|
|
1848
|
+
const handler = registry.getHandler(cmd);
|
|
1849
|
+
expect(handler, `${cmd} should be registered`).toBeDefined();
|
|
1850
|
+
}
|
|
1851
|
+
});
|
|
1852
|
+
});
|
|
1853
|
+
|
|
1854
|
+
// ─── CR-3267 regression: error-propagation in listDirectories ─────────────
|
|
1855
|
+
|
|
1856
|
+
describe('listDirectories — CR-3267 finding 1: non-ENOENT errors propagate', () => {
|
|
1857
|
+
it('propagates EACCES from readdir instead of returning []', async () => {
|
|
1858
|
+
const { listDirectories } = await import('./phase-filesystem-adapter.js');
|
|
1859
|
+
// Create a real directory then remove read permission
|
|
1860
|
+
const dir = await mkdtemp(join(tmpdir(), 'gsd-fs-acl-'));
|
|
1861
|
+
const inner = join(dir, 'phases');
|
|
1862
|
+
await mkdir(inner);
|
|
1863
|
+
try {
|
|
1864
|
+
await import('node:fs/promises').then(m => m.chmod(inner, 0o000));
|
|
1865
|
+
await expect(listDirectories(inner)).rejects.toThrow();
|
|
1866
|
+
} finally {
|
|
1867
|
+
// Restore so cleanup can delete
|
|
1868
|
+
await import('node:fs/promises').then(m => m.chmod(inner, 0o755));
|
|
1869
|
+
await rm(dir, { recursive: true, force: true });
|
|
1870
|
+
}
|
|
1871
|
+
});
|
|
1872
|
+
|
|
1873
|
+
it('returns [] for ENOENT (directory gone between existsSync and readdir)', async () => {
|
|
1874
|
+
// existsSync passes, but the directory has been removed before readdir —
|
|
1875
|
+
// the ENOENT branch must still return [].
|
|
1876
|
+
const { listDirectories } = await import('./phase-filesystem-adapter.js');
|
|
1877
|
+
// We can't easily race the real FS, but we can verify the function tolerates
|
|
1878
|
+
// a path that truly does not exist (existsSync returns false → early []).
|
|
1879
|
+
const nonExistent = join(tmpdir(), 'gsd-does-not-exist-' + Date.now());
|
|
1880
|
+
const result = await listDirectories(nonExistent);
|
|
1881
|
+
expect(result).toEqual([]);
|
|
1882
|
+
});
|
|
1883
|
+
});
|
|
1884
|
+
|
|
1885
|
+
// ─── CR-3267 regression: error-propagation in readModifyWriteRoadmapMd ─────
|
|
1886
|
+
|
|
1887
|
+
describe('readModifyWriteRoadmapMd — CR-3267 finding 4: non-ENOENT errors propagate', () => {
|
|
1888
|
+
it('propagates EACCES on ROADMAP.md readFile instead of treating as empty', async () => {
|
|
1889
|
+
const { readModifyWriteRoadmapMd } = await import('./phase-lifecycle.js');
|
|
1890
|
+
const dir = await mkdtemp(join(tmpdir(), 'gsd-roadmap-acl-'));
|
|
1891
|
+
const planningDir = join(dir, '.planning');
|
|
1892
|
+
await mkdir(planningDir, { recursive: true });
|
|
1893
|
+
const roadmapPath = join(planningDir, 'ROADMAP.md');
|
|
1894
|
+
await writeFile(roadmapPath, '# Roadmap\n', 'utf-8');
|
|
1895
|
+
try {
|
|
1896
|
+
await import('node:fs/promises').then(m => m.chmod(roadmapPath, 0o000));
|
|
1897
|
+
await expect(
|
|
1898
|
+
readModifyWriteRoadmapMd(dir, (c) => c)
|
|
1899
|
+
).rejects.toThrow();
|
|
1900
|
+
} finally {
|
|
1901
|
+
await import('node:fs/promises').then(m => m.chmod(roadmapPath, 0o644));
|
|
1902
|
+
await rm(dir, { recursive: true, force: true });
|
|
1903
|
+
}
|
|
1904
|
+
});
|
|
1905
|
+
|
|
1906
|
+
it('starts with empty content when ROADMAP.md is absent (ENOENT)', async () => {
|
|
1907
|
+
const { readModifyWriteRoadmapMd } = await import('./phase-lifecycle.js');
|
|
1908
|
+
const dir = await mkdtemp(join(tmpdir(), 'gsd-roadmap-noent-'));
|
|
1909
|
+
const planningDir = join(dir, '.planning');
|
|
1910
|
+
await mkdir(planningDir, { recursive: true });
|
|
1911
|
+
// No ROADMAP.md written — must default to '' and create it
|
|
1912
|
+
try {
|
|
1913
|
+
const result = await readModifyWriteRoadmapMd(dir, (c) => c + 'NEW');
|
|
1914
|
+
expect(result).toBe('NEW');
|
|
1915
|
+
} finally {
|
|
1916
|
+
await rm(dir, { recursive: true, force: true });
|
|
1917
|
+
}
|
|
1918
|
+
});
|
|
1919
|
+
});
|
|
1920
|
+
|
|
1921
|
+
// ─── CR-3267 regression: buildPhaseRoadmapEntry — no "Phase 0" dependency ──
|
|
1922
|
+
|
|
1923
|
+
describe('buildPhaseRoadmapEntry — CR-3267 finding 2: first sequential phase has no predecessor', () => {
|
|
1924
|
+
it('omits Depends on line when phaseId is 1', async () => {
|
|
1925
|
+
const { buildPhaseRoadmapEntry } = await import('./phase-lifecycle-policy.js');
|
|
1926
|
+
const entry = buildPhaseRoadmapEntry(1, 'Bootstrap', 'sequential');
|
|
1927
|
+
expect(entry).not.toContain('Depends on');
|
|
1928
|
+
expect(entry).not.toContain('Phase 0');
|
|
1929
|
+
});
|
|
1930
|
+
|
|
1931
|
+
it('includes Depends on line when phaseId is 2', async () => {
|
|
1932
|
+
const { buildPhaseRoadmapEntry } = await import('./phase-lifecycle-policy.js');
|
|
1933
|
+
const entry = buildPhaseRoadmapEntry(2, 'Second Phase', 'sequential');
|
|
1934
|
+
expect(entry).toContain('**Depends on:** Phase 1');
|
|
1935
|
+
});
|
|
1936
|
+
|
|
1937
|
+
it('omits Depends on line for custom naming mode regardless of id', async () => {
|
|
1938
|
+
const { buildPhaseRoadmapEntry } = await import('./phase-lifecycle-policy.js');
|
|
1939
|
+
const entry = buildPhaseRoadmapEntry('ALPHA', 'Custom', 'custom');
|
|
1940
|
+
expect(entry).not.toContain('Depends on');
|
|
1941
|
+
});
|
|
1942
|
+
});
|
|
1943
|
+
|
|
1944
|
+
// ─── CR-3267 regression: collectDecimalSuffixesFromDirNames prefix grammar ─
|
|
1945
|
+
|
|
1946
|
+
describe('collectDecimalSuffixesFromDirNames — CR-3267 finding 3: alphanumeric prefixes accepted', () => {
|
|
1947
|
+
it('matches directories with long alphanumeric project-code prefix', async () => {
|
|
1948
|
+
const { collectDecimalSuffixesFromDirNames } = await import('./phase-lifecycle-policy.js');
|
|
1949
|
+
// Prefix "MYAPP01" is longer than 6 chars and contains digits — was rejected before fix
|
|
1950
|
+
const dirs = ['MYAPP01-3.1-some-work', 'MYAPP01-3.2-other-work', 'unrelated-dir'];
|
|
1951
|
+
const result = collectDecimalSuffixesFromDirNames('3', dirs);
|
|
1952
|
+
expect(result.has(1)).toBe(true);
|
|
1953
|
+
expect(result.has(2)).toBe(true);
|
|
1954
|
+
});
|
|
1955
|
+
|
|
1956
|
+
it('still matches directories with short uppercase-only prefix', async () => {
|
|
1957
|
+
const { collectDecimalSuffixesFromDirNames } = await import('./phase-lifecycle-policy.js');
|
|
1958
|
+
const dirs = ['AB-5.1-task', 'AB-5.3-other'];
|
|
1959
|
+
const result = collectDecimalSuffixesFromDirNames('5', dirs);
|
|
1960
|
+
expect(result.has(1)).toBe(true);
|
|
1961
|
+
expect(result.has(3)).toBe(true);
|
|
1962
|
+
});
|
|
1963
|
+
|
|
1964
|
+
it('matches directories with no prefix', async () => {
|
|
1965
|
+
const { collectDecimalSuffixesFromDirNames } = await import('./phase-lifecycle-policy.js');
|
|
1966
|
+
const dirs = ['3.1-plain', '3.2-also-plain'];
|
|
1967
|
+
const result = collectDecimalSuffixesFromDirNames('3', dirs);
|
|
1968
|
+
expect(result.has(1)).toBe(true);
|
|
1969
|
+
expect(result.has(2)).toBe(true);
|
|
1970
|
+
});
|
|
1971
|
+
});
|