gsd-pi 2.65.0-dev.5c8557b → 2.65.0-dev.d0517ff
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/dist/mcp-server.js +6 -2
- package/dist/resources/extensions/browser-tools/capture.js +20 -1
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +13 -2
- package/dist/resources/extensions/gsd/auto/session.js +4 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +99 -9
- package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
- package/dist/resources/extensions/gsd/auto-post-unit.js +17 -6
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +40 -22
- package/dist/resources/extensions/gsd/auto-start.js +42 -11
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
- package/dist/resources/extensions/gsd/auto.js +21 -15
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -3
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +31 -1
- package/dist/resources/extensions/gsd/commands/context.js +8 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +20 -0
- package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
- package/dist/resources/extensions/gsd/config-overlay.js +312 -0
- package/dist/resources/extensions/gsd/db-writer.js +13 -3
- package/dist/resources/extensions/gsd/detection.js +1 -1
- package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
- package/dist/resources/extensions/gsd/doctor.js +2 -1
- package/dist/resources/extensions/gsd/gitignore.js +1 -0
- package/dist/resources/extensions/gsd/gsd-db.js +47 -4
- package/dist/resources/extensions/gsd/guided-flow.js +220 -29
- package/dist/resources/extensions/gsd/index.js +1 -1
- package/dist/resources/extensions/gsd/json-persistence.js +5 -2
- package/dist/resources/extensions/gsd/md-importer.js +14 -7
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
- package/dist/resources/extensions/gsd/pre-execution-checks.js +12 -5
- package/dist/resources/extensions/gsd/preferences-types.js +3 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +45 -1
- package/dist/resources/extensions/gsd/preferences.js +9 -2
- package/dist/resources/extensions/gsd/preparation.js +1092 -0
- package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
- package/dist/resources/extensions/gsd/prompts/queue.md +2 -0
- package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
- package/dist/resources/extensions/gsd/quick.js +19 -15
- package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
- package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
- package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
- package/dist/resources/extensions/gsd/session-lock.js +23 -1
- package/dist/resources/extensions/gsd/state.js +115 -28
- package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +15 -3
- package/dist/resources/extensions/gsd/tools/complete-slice.js +27 -6
- package/dist/resources/extensions/gsd/tools/complete-task.js +31 -7
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
- package/dist/resources/extensions/gsd/triage-resolution.js +33 -16
- package/dist/resources/extensions/gsd/undo.js +3 -2
- package/dist/resources/extensions/gsd/workflow-events.js +1 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
- package/dist/resources/extensions/gsd/workflow-projections.js +7 -9
- package/dist/resources/extensions/gsd/workflow-reconcile.js +100 -9
- package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
- package/dist/resources/extensions/gsd/worktree.js +9 -0
- package/dist/resources/extensions/shared/interview-ui.js +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +20 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
- package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
- package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +20 -4
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
- package/packages/pi-tui/dist/components/image.d.ts +2 -0
- package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/image.js +4 -0
- package/packages/pi-tui/dist/components/image.js.map +1 -1
- package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
- package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/image.test.js +32 -0
- package/packages/pi-tui/dist/components/image.test.js.map +1 -0
- package/packages/pi-tui/src/components/image.test.ts +36 -0
- package/packages/pi-tui/src/components/image.ts +5 -0
- package/src/resources/extensions/browser-tools/capture.ts +19 -1
- package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
- package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
- package/src/resources/extensions/gsd/auto/session.ts +4 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
- package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
- package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
- package/src/resources/extensions/gsd/auto-start.ts +45 -10
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
- package/src/resources/extensions/gsd/auto.ts +19 -8
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -3
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +36 -1
- package/src/resources/extensions/gsd/commands/context.ts +7 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +23 -0
- package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
- package/src/resources/extensions/gsd/config-overlay.ts +331 -0
- package/src/resources/extensions/gsd/db-writer.ts +11 -3
- package/src/resources/extensions/gsd/detection.ts +1 -1
- package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
- package/src/resources/extensions/gsd/doctor.ts +2 -1
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/gsd-db.ts +46 -4
- package/src/resources/extensions/gsd/guided-flow.ts +254 -30
- package/src/resources/extensions/gsd/index.ts +1 -0
- package/src/resources/extensions/gsd/json-persistence.ts +6 -3
- package/src/resources/extensions/gsd/md-importer.ts +13 -6
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
- package/src/resources/extensions/gsd/pre-execution-checks.ts +15 -7
- package/src/resources/extensions/gsd/preferences-types.ts +25 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
- package/src/resources/extensions/gsd/preferences.ts +9 -2
- package/src/resources/extensions/gsd/preparation.ts +1419 -0
- package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
- package/src/resources/extensions/gsd/prompts/queue.md +2 -0
- package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
- package/src/resources/extensions/gsd/quick.ts +20 -15
- package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
- package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
- package/src/resources/extensions/gsd/session-lock.ts +17 -1
- package/src/resources/extensions/gsd/state.ts +115 -26
- package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
- package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
- package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
- package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
- package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
- package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +218 -20
- package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
- package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
- package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
- package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/wave5-consistency-regressions.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
- package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +127 -2
- package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
- package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
- package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
- package/src/resources/extensions/gsd/types.ts +4 -0
- package/src/resources/extensions/gsd/undo.ts +3 -2
- package/src/resources/extensions/gsd/workflow-events.ts +5 -3
- package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
- package/src/resources/extensions/gsd/workflow-projections.ts +7 -8
- package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
- package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
- package/src/resources/extensions/gsd/worktree.ts +10 -0
- package/src/resources/extensions/shared/interview-ui.ts +1 -1
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
- package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
- /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → JwdBI3y1H8vtBKiYvWfEK}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → JwdBI3y1H8vtBKiYvWfEK}/_ssgManifest.js +0 -0
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { ExtensionAPI, ExtensionContext, ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
10
10
|
import { showNextAction } from "../shared/tui.js";
|
|
11
|
-
import { loadFile } from "./files.js";
|
|
11
|
+
import { loadFile, saveFile } from "./files.js";
|
|
12
12
|
import { isDbAvailable, getMilestoneSlices } from "./gsd-db.js";
|
|
13
13
|
import { parseRoadmapSlices } from "./roadmap-slices.js";
|
|
14
14
|
import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
|
|
@@ -40,6 +40,28 @@ import { findMilestoneIds, nextMilestoneId, reserveMilestoneId, getReservedMiles
|
|
|
40
40
|
import { parkMilestone, discardMilestone } from "./milestone-actions.js";
|
|
41
41
|
import { selectAndApplyModel } from "./auto-model-selection.js";
|
|
42
42
|
import { DISCUSS_TOOLS_ALLOWLIST } from "./constants.js";
|
|
43
|
+
import {
|
|
44
|
+
runPreparation,
|
|
45
|
+
formatCodebaseBrief,
|
|
46
|
+
formatPriorContextBrief,
|
|
47
|
+
formatEcosystemBrief,
|
|
48
|
+
type PreparationResult,
|
|
49
|
+
} from "./preparation.js";
|
|
50
|
+
|
|
51
|
+
// ─── Preparation result storage ─────────────────────────────────────────────
|
|
52
|
+
// Stores the most recent preparation result for injection into discuss prompts.
|
|
53
|
+
// S02 will consume this when building the prepared discussion prompt.
|
|
54
|
+
let lastPreparationResult: PreparationResult | null = null;
|
|
55
|
+
|
|
56
|
+
/** Get the most recent preparation result (for S02 prompt building). */
|
|
57
|
+
export function getLastPreparationResult(): PreparationResult | null {
|
|
58
|
+
return lastPreparationResult;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Clear the preparation result (called after discussion completes). */
|
|
62
|
+
export function clearPreparationResult(): void {
|
|
63
|
+
lastPreparationResult = null;
|
|
64
|
+
}
|
|
43
65
|
|
|
44
66
|
// ─── Re-exports (preserve public API for existing importers) ────────────────
|
|
45
67
|
export {
|
|
@@ -85,6 +107,7 @@ interface PendingAutoStartEntry {
|
|
|
85
107
|
basePath: string;
|
|
86
108
|
milestoneId: string; // the milestone being discussed
|
|
87
109
|
step?: boolean; // preserve step mode through discuss → auto transition
|
|
110
|
+
createdAt: number; // timestamp for staleness detection (#3274)
|
|
88
111
|
}
|
|
89
112
|
|
|
90
113
|
const pendingAutoStartMap = new Map<string, PendingAutoStartEntry>();
|
|
@@ -104,8 +127,8 @@ function _getPendingAutoStart(basePath?: string): PendingAutoStartEntry | null {
|
|
|
104
127
|
* Store pending auto-start state for a project.
|
|
105
128
|
* Exported for testing (#2985).
|
|
106
129
|
*/
|
|
107
|
-
export function setPendingAutoStart(basePath: string, entry: { basePath: string; milestoneId: string; ctx?: ExtensionCommandContext; pi?: ExtensionAPI; step?: boolean }): void {
|
|
108
|
-
pendingAutoStartMap.set(basePath, entry as PendingAutoStartEntry);
|
|
130
|
+
export function setPendingAutoStart(basePath: string, entry: { basePath: string; milestoneId: string; ctx?: ExtensionCommandContext; pi?: ExtensionAPI; step?: boolean; createdAt?: number }): void {
|
|
131
|
+
pendingAutoStartMap.set(basePath, { createdAt: Date.now(), ...entry } as PendingAutoStartEntry);
|
|
109
132
|
}
|
|
110
133
|
|
|
111
134
|
/**
|
|
@@ -295,8 +318,10 @@ async function dispatchWorkflow(
|
|
|
295
318
|
// "Grammar is too complex" when the combined tool schema is too large.
|
|
296
319
|
// Discuss flows only need a small subset of GSD tools — strip the heavy
|
|
297
320
|
// planning/execution/completion tools to keep the grammar within limits.
|
|
321
|
+
let savedTools: string[] | null = null;
|
|
298
322
|
if (unitType?.startsWith("discuss-")) {
|
|
299
323
|
const currentTools = pi.getActiveTools();
|
|
324
|
+
savedTools = currentTools;
|
|
300
325
|
// Keep all non-GSD tools (builtins, other extensions) and only the
|
|
301
326
|
// GSD tools on the discuss allowlist.
|
|
302
327
|
const scopedTools = currentTools.filter(
|
|
@@ -322,6 +347,13 @@ async function dispatchWorkflow(
|
|
|
322
347
|
},
|
|
323
348
|
{ triggerTurn: true },
|
|
324
349
|
);
|
|
350
|
+
|
|
351
|
+
// Restore full tool set after the message is queued. The LLM turn has
|
|
352
|
+
// already captured the scoped set — restoring prevents the narrowed
|
|
353
|
+
// tools from leaking into subsequent dispatches (#3628).
|
|
354
|
+
if (savedTools) {
|
|
355
|
+
pi.setActiveTools(savedTools);
|
|
356
|
+
}
|
|
325
357
|
}
|
|
326
358
|
|
|
327
359
|
/**
|
|
@@ -411,6 +443,104 @@ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePa
|
|
|
411
443
|
});
|
|
412
444
|
}
|
|
413
445
|
|
|
446
|
+
/**
|
|
447
|
+
* Build the prepared discuss prompt with brief injection.
|
|
448
|
+
* Uses the discuss-prepared template which encodes the 4-layer discussion protocol.
|
|
449
|
+
*
|
|
450
|
+
* @param nextId - The milestone ID being discussed
|
|
451
|
+
* @param preamble - Preamble text for the discuss prompt
|
|
452
|
+
* @param _basePath - Root directory of the project (unused, kept for signature consistency)
|
|
453
|
+
* @param prepResult - Preparation result containing briefs to inject
|
|
454
|
+
* @returns The prepared discuss prompt string
|
|
455
|
+
*/
|
|
456
|
+
function buildPreparedPrompt(
|
|
457
|
+
nextId: string,
|
|
458
|
+
preamble: string,
|
|
459
|
+
_basePath: string,
|
|
460
|
+
prepResult: PreparationResult,
|
|
461
|
+
): string {
|
|
462
|
+
const milestoneRel = `.gsd/milestones/${nextId}`;
|
|
463
|
+
|
|
464
|
+
// Use context-enhanced instead of context for prepared discussions
|
|
465
|
+
const inlinedTemplates = [
|
|
466
|
+
inlineTemplate("project", "Project"),
|
|
467
|
+
inlineTemplate("requirements", "Requirements"),
|
|
468
|
+
inlineTemplate("context-enhanced", "Context Enhanced"),
|
|
469
|
+
inlineTemplate("roadmap", "Roadmap"),
|
|
470
|
+
inlineTemplate("decisions", "Decisions"),
|
|
471
|
+
].join("\n\n---\n\n");
|
|
472
|
+
|
|
473
|
+
// Format the briefs from the preparation result
|
|
474
|
+
const codebaseBrief = prepResult.codebaseBrief || formatCodebaseBrief(prepResult.codebase);
|
|
475
|
+
const priorContextBrief = prepResult.priorContextBrief || formatPriorContextBrief(prepResult.priorContext);
|
|
476
|
+
const ecosystemBrief = prepResult.ecosystemBrief || formatEcosystemBrief(prepResult.ecosystem);
|
|
477
|
+
|
|
478
|
+
return loadPrompt("discuss-prepared", {
|
|
479
|
+
milestoneId: nextId,
|
|
480
|
+
preamble,
|
|
481
|
+
codebaseBrief,
|
|
482
|
+
priorContextBrief,
|
|
483
|
+
ecosystemBrief,
|
|
484
|
+
contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
|
|
485
|
+
roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
|
|
486
|
+
inlinedTemplates,
|
|
487
|
+
commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
|
|
488
|
+
multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Run preparation phase if enabled, then build the discuss prompt.
|
|
494
|
+
* This is the main entry point for new milestone discussions with preparation.
|
|
495
|
+
* Stores the preparation result for S02 to inject into the discuss prompt.
|
|
496
|
+
*
|
|
497
|
+
* When preparation succeeds, uses the discuss-prepared template with brief injection.
|
|
498
|
+
* Falls back to the standard discuss template when preparation is disabled or fails.
|
|
499
|
+
*
|
|
500
|
+
* @param ctx - Extension command context with UI for progress notifications
|
|
501
|
+
* @param nextId - The milestone ID being discussed
|
|
502
|
+
* @param preamble - Preamble text for the discuss prompt
|
|
503
|
+
* @param basePath - Root directory of the project
|
|
504
|
+
* @returns The discuss prompt string
|
|
505
|
+
*/
|
|
506
|
+
async function prepareAndBuildDiscussPrompt(
|
|
507
|
+
ctx: ExtensionCommandContext,
|
|
508
|
+
nextId: string,
|
|
509
|
+
preamble: string,
|
|
510
|
+
basePath: string,
|
|
511
|
+
): Promise<string> {
|
|
512
|
+
// Clear stale preparation result immediately to prevent cross-session/project
|
|
513
|
+
// state leaks. This ensures data from a prior milestone/project never leaks
|
|
514
|
+
// into subsequent discussions (adversarial review fix #3602).
|
|
515
|
+
lastPreparationResult = null;
|
|
516
|
+
|
|
517
|
+
const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
|
|
518
|
+
|
|
519
|
+
// Run preparation if enabled (default: true)
|
|
520
|
+
if (prefs.discuss_preparation !== false) {
|
|
521
|
+
try {
|
|
522
|
+
const prepResult = await runPreparation(basePath, ctx.ui, {
|
|
523
|
+
discuss_preparation: prefs.discuss_preparation,
|
|
524
|
+
discuss_web_research: prefs.discuss_web_research,
|
|
525
|
+
discuss_depth: prefs.discuss_depth,
|
|
526
|
+
});
|
|
527
|
+
lastPreparationResult = prepResult;
|
|
528
|
+
|
|
529
|
+
// Use prepared prompt if preparation was enabled and produced results
|
|
530
|
+
if (prepResult.enabled) {
|
|
531
|
+
return buildPreparedPrompt(nextId, preamble, basePath, prepResult);
|
|
532
|
+
}
|
|
533
|
+
} catch {
|
|
534
|
+
// If preparation throws, ensure stale data doesn't persist
|
|
535
|
+
lastPreparationResult = null;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Fall back to standard discuss prompt for backward compatibility
|
|
540
|
+
// lastPreparationResult is already null (cleared at entry or on error)
|
|
541
|
+
return buildDiscussPrompt(nextId, preamble, basePath);
|
|
542
|
+
}
|
|
543
|
+
|
|
414
544
|
/**
|
|
415
545
|
* Bootstrap a .gsd/ project from scratch for headless use.
|
|
416
546
|
* Ensures git repo, .gsd/ structure, gitignore, and preferences all exist.
|
|
@@ -460,7 +590,7 @@ export async function showHeadlessMilestoneCreation(
|
|
|
460
590
|
const prompt = buildHeadlessDiscussPrompt(nextId, seedContext, basePath);
|
|
461
591
|
|
|
462
592
|
// Set pending auto start (auto-mode triggers on "Milestone X ready." via checkAutoStartAfterDiscuss)
|
|
463
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId });
|
|
593
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, createdAt: Date.now() });
|
|
464
594
|
|
|
465
595
|
// Dispatch — headless milestone creation is a planning activity
|
|
466
596
|
await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "plan-milestone");
|
|
@@ -480,7 +610,7 @@ async function buildDiscussSlicePrompt(
|
|
|
480
610
|
sid: string,
|
|
481
611
|
sTitle: string,
|
|
482
612
|
base: string,
|
|
483
|
-
options?: { rediscuss?: boolean },
|
|
613
|
+
options?: { rediscuss?: boolean; structuredQuestionsAvailable?: string },
|
|
484
614
|
): Promise<string> {
|
|
485
615
|
const inlined: string[] = [];
|
|
486
616
|
|
|
@@ -560,6 +690,7 @@ async function buildDiscussSlicePrompt(
|
|
|
560
690
|
contextPath: sliceContextPath,
|
|
561
691
|
projectRoot: base,
|
|
562
692
|
inlinedTemplates,
|
|
693
|
+
structuredQuestionsAvailable: options?.structuredQuestionsAvailable ?? "false",
|
|
563
694
|
commitInstruction: buildDocsCommitInstruction(`docs(${mid}/${sid}): slice context from discuss`),
|
|
564
695
|
});
|
|
565
696
|
}
|
|
@@ -585,6 +716,16 @@ export async function showDiscuss(
|
|
|
585
716
|
|
|
586
717
|
const state = await deriveState(basePath);
|
|
587
718
|
|
|
719
|
+
// Rebuild STATE.md from derived state before any dispatch (#3475).
|
|
720
|
+
// Without this, guided prompts read a stale STATE.md cache and the
|
|
721
|
+
// agent bootstraps from the wrong milestone.
|
|
722
|
+
try {
|
|
723
|
+
const { buildStateMarkdown } = await import("./doctor.js");
|
|
724
|
+
await saveFile(resolveGsdRootFile(basePath, "STATE"), buildStateMarkdown(state));
|
|
725
|
+
} catch (err) {
|
|
726
|
+
logWarning("guided", `STATE.md rebuild failed: ${(err as Error).message}`);
|
|
727
|
+
}
|
|
728
|
+
|
|
588
729
|
// No active milestone (or corrupted milestone with undefined id) —
|
|
589
730
|
// check for pending milestones to discuss instead
|
|
590
731
|
if (!state.activeMilestone?.id) {
|
|
@@ -636,26 +777,28 @@ export async function showDiscuss(
|
|
|
636
777
|
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
|
637
778
|
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
638
779
|
commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
|
|
780
|
+
fastPathInstruction: "",
|
|
639
781
|
});
|
|
640
782
|
const seed = draftContent
|
|
641
783
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
642
784
|
: basePrompt;
|
|
643
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: mid, step: false });
|
|
785
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: mid, step: false, createdAt: Date.now() });
|
|
644
786
|
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
|
645
787
|
} else if (choice === "discuss_fresh") {
|
|
646
788
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
647
789
|
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
|
648
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: mid, step: false });
|
|
790
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: mid, step: false, createdAt: Date.now() });
|
|
649
791
|
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
650
792
|
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
651
793
|
commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
|
|
794
|
+
fastPathInstruction: "",
|
|
652
795
|
}), "gsd-discuss", ctx, "discuss-milestone");
|
|
653
796
|
} else if (choice === "skip_milestone") {
|
|
654
797
|
const milestoneIds = findMilestoneIds(basePath);
|
|
655
798
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
656
799
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
657
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: false });
|
|
658
|
-
await dispatchWorkflow(pi,
|
|
800
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: false, createdAt: Date.now() });
|
|
801
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
659
802
|
}
|
|
660
803
|
return;
|
|
661
804
|
}
|
|
@@ -801,7 +944,8 @@ export async function showDiscuss(
|
|
|
801
944
|
if (confirm !== "rediscuss") continue;
|
|
802
945
|
}
|
|
803
946
|
|
|
804
|
-
const
|
|
947
|
+
const sqAvail = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
|
948
|
+
const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath, { rediscuss: isRediscuss, structuredQuestionsAvailable: sqAvail });
|
|
805
949
|
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-slice");
|
|
806
950
|
|
|
807
951
|
// Wait for the discuss session to finish, then loop back to the picker
|
|
@@ -851,7 +995,36 @@ async function showDiscussQueuedMilestone(
|
|
|
851
995
|
const chosen = pendingMilestones.find(m => m.id === choice);
|
|
852
996
|
if (!chosen) return;
|
|
853
997
|
|
|
854
|
-
|
|
998
|
+
const hasDraft = !!resolveMilestoneFile(basePath, chosen.id, "CONTEXT-DRAFT");
|
|
999
|
+
let fastPath = hasDraft;
|
|
1000
|
+
|
|
1001
|
+
if (!hasDraft) {
|
|
1002
|
+
const mode = await showNextAction(ctx, {
|
|
1003
|
+
title: `Discuss ${chosen.id}`,
|
|
1004
|
+
summary: [
|
|
1005
|
+
"Choose how to start the discussion.",
|
|
1006
|
+
"Fast path skips generic scouting — use it when you already know the scope.",
|
|
1007
|
+
],
|
|
1008
|
+
actions: [
|
|
1009
|
+
{
|
|
1010
|
+
id: "full",
|
|
1011
|
+
label: "Full discussion",
|
|
1012
|
+
description: "Scout the codebase, ask open-ended questions, explore deeply",
|
|
1013
|
+
recommended: true,
|
|
1014
|
+
},
|
|
1015
|
+
{
|
|
1016
|
+
id: "fast",
|
|
1017
|
+
label: "I have the scope — fast path",
|
|
1018
|
+
description: "Treat your first message as authoritative seed context; skip scouting",
|
|
1019
|
+
},
|
|
1020
|
+
],
|
|
1021
|
+
notYetMessage: "Run /gsd discuss when ready.",
|
|
1022
|
+
});
|
|
1023
|
+
if (mode === "not_yet") return;
|
|
1024
|
+
fastPath = mode === "fast";
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
await dispatchDiscussForMilestone(ctx, pi, basePath, chosen.id, chosen.title, { fastPath });
|
|
855
1028
|
}
|
|
856
1029
|
|
|
857
1030
|
/**
|
|
@@ -865,9 +1038,21 @@ async function dispatchDiscussForMilestone(
|
|
|
865
1038
|
basePath: string,
|
|
866
1039
|
mid: string,
|
|
867
1040
|
milestoneTitle: string,
|
|
1041
|
+
opts: { fastPath?: boolean } = {},
|
|
868
1042
|
): Promise<void> {
|
|
869
1043
|
const draftFile = resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT");
|
|
870
1044
|
const draftContent = draftFile ? await loadFile(draftFile) : null;
|
|
1045
|
+
const hasSeed = !!(draftContent || opts.fastPath);
|
|
1046
|
+
const fastPathInstruction = hasSeed
|
|
1047
|
+
? [
|
|
1048
|
+
"> **Fast path active — scope provided.**",
|
|
1049
|
+
"> Do NOT perform a generic codebase scouting pass.",
|
|
1050
|
+
"> Do at most 2 targeted reads to check for obvious conflicts with existing work.",
|
|
1051
|
+
"> Treat the seed context or the operator's first message as authoritative.",
|
|
1052
|
+
"> Move directly to the depth summary and write step.",
|
|
1053
|
+
"> Ask only questions where the answer would materially change scope.",
|
|
1054
|
+
].join("\n")
|
|
1055
|
+
: "";
|
|
871
1056
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
872
1057
|
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
|
873
1058
|
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
|
@@ -876,6 +1061,7 @@ async function dispatchDiscussForMilestone(
|
|
|
876
1061
|
inlinedTemplates: discussMilestoneTemplates,
|
|
877
1062
|
structuredQuestionsAvailable,
|
|
878
1063
|
commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
|
|
1064
|
+
fastPathInstruction,
|
|
879
1065
|
});
|
|
880
1066
|
const prompt = draftContent
|
|
881
1067
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
@@ -1016,8 +1202,8 @@ async function handleMilestoneActions(
|
|
|
1016
1202
|
const milestoneIds = findMilestoneIds(basePath);
|
|
1017
1203
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1018
1204
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1019
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1020
|
-
await dispatchWorkflow(pi,
|
|
1205
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1206
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId,
|
|
1021
1207
|
`New milestone ${nextId}.`,
|
|
1022
1208
|
basePath
|
|
1023
1209
|
), "gsd-run", ctx, "discuss-milestone");
|
|
@@ -1137,14 +1323,36 @@ export async function showSmartEntry(
|
|
|
1137
1323
|
|
|
1138
1324
|
const state = await deriveState(basePath);
|
|
1139
1325
|
|
|
1326
|
+
// Rebuild STATE.md from derived state before any dispatch (#3475).
|
|
1327
|
+
try {
|
|
1328
|
+
const { buildStateMarkdown } = await import("./doctor.js");
|
|
1329
|
+
await saveFile(resolveGsdRootFile(basePath, "STATE"), buildStateMarkdown(state));
|
|
1330
|
+
} catch (err) {
|
|
1331
|
+
logWarning("guided", `STATE.md rebuild failed: ${(err as Error).message}`);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1140
1334
|
if (!state.activeMilestone?.id) {
|
|
1141
1335
|
// Guard: if a discuss session is already in flight, don't re-inject the prompt.
|
|
1142
1336
|
// Both /gsd and /gsd auto reach this branch when no milestone exists yet.
|
|
1143
1337
|
// Without this guard, every subsequent /gsd call overwrites the pending auto-start
|
|
1144
1338
|
// and fires another dispatchWorkflow, resetting the conversation mid-interview.
|
|
1145
1339
|
if (pendingAutoStartMap.has(basePath)) {
|
|
1146
|
-
|
|
1147
|
-
|
|
1340
|
+
// #3274: If /clear interrupted the discussion, the pending entry is stale.
|
|
1341
|
+
// Detect staleness: no manifest, no CONTEXT.md, AND entry is older than
|
|
1342
|
+
// 30s (avoids race between .set() and LLM writing first artifact).
|
|
1343
|
+
const entry = pendingAutoStartMap.get(basePath)!;
|
|
1344
|
+
const ageMs = Date.now() - (entry.createdAt || 0);
|
|
1345
|
+
const manifestExists = existsSync(join(gsdRoot(basePath), "DISCUSSION-MANIFEST.json"));
|
|
1346
|
+
const milestoneHasContext = existsSync(
|
|
1347
|
+
join(gsdRoot(basePath), "milestones", entry.milestoneId, `${entry.milestoneId}-CONTEXT.md`),
|
|
1348
|
+
);
|
|
1349
|
+
if (!manifestExists && !milestoneHasContext && ageMs > 30_000) {
|
|
1350
|
+
// Stale entry from an interrupted discussion — clear and continue
|
|
1351
|
+
pendingAutoStartMap.delete(basePath);
|
|
1352
|
+
} else {
|
|
1353
|
+
ctx.ui.notify("Discussion already in progress — answer the question above to continue.", "info");
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1148
1356
|
}
|
|
1149
1357
|
|
|
1150
1358
|
const milestoneIds = findMilestoneIds(basePath);
|
|
@@ -1175,8 +1383,8 @@ export async function showSmartEntry(
|
|
|
1175
1383
|
|
|
1176
1384
|
if (isFirst) {
|
|
1177
1385
|
// First ever — skip wizard, just ask directly
|
|
1178
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1179
|
-
await dispatchWorkflow(pi,
|
|
1386
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1387
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId,
|
|
1180
1388
|
`New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`,
|
|
1181
1389
|
basePath
|
|
1182
1390
|
), "gsd-run", ctx, "discuss-milestone");
|
|
@@ -1196,8 +1404,8 @@ export async function showSmartEntry(
|
|
|
1196
1404
|
});
|
|
1197
1405
|
|
|
1198
1406
|
if (choice === "new_milestone") {
|
|
1199
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1200
|
-
await dispatchWorkflow(pi,
|
|
1407
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1408
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId,
|
|
1201
1409
|
`New milestone ${nextId}.`,
|
|
1202
1410
|
basePath
|
|
1203
1411
|
), "gsd-run", ctx, "discuss-milestone");
|
|
@@ -1235,8 +1443,8 @@ export async function showSmartEntry(
|
|
|
1235
1443
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1236
1444
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1237
1445
|
|
|
1238
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1239
|
-
await dispatchWorkflow(pi,
|
|
1446
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1447
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId,
|
|
1240
1448
|
`New milestone ${nextId}.`,
|
|
1241
1449
|
basePath
|
|
1242
1450
|
), "gsd-run", ctx, "discuss-milestone");
|
|
@@ -1282,26 +1490,28 @@ export async function showSmartEntry(
|
|
|
1282
1490
|
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
|
1283
1491
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1284
1492
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
1493
|
+
fastPathInstruction: "",
|
|
1285
1494
|
});
|
|
1286
1495
|
const seed = draftContent
|
|
1287
1496
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
1288
1497
|
: basePrompt;
|
|
1289
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
1498
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode, createdAt: Date.now() });
|
|
1290
1499
|
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
|
1291
1500
|
} else if (choice === "discuss_fresh") {
|
|
1292
1501
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1293
1502
|
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
|
1294
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
1503
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode, createdAt: Date.now() });
|
|
1295
1504
|
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
1296
1505
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1297
1506
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
1507
|
+
fastPathInstruction: "",
|
|
1298
1508
|
}), "gsd-discuss", ctx, "discuss-milestone");
|
|
1299
1509
|
} else if (choice === "skip_milestone") {
|
|
1300
1510
|
const milestoneIds = findMilestoneIds(basePath);
|
|
1301
1511
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1302
1512
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1303
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1304
|
-
await dispatchWorkflow(pi,
|
|
1513
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1514
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId,
|
|
1305
1515
|
`New milestone ${nextId}.`,
|
|
1306
1516
|
basePath
|
|
1307
1517
|
), "gsd-run", ctx, "discuss-milestone");
|
|
@@ -1314,7 +1524,19 @@ export async function showSmartEntry(
|
|
|
1314
1524
|
const roadmapFile = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
1315
1525
|
const hasRoadmap = !!(roadmapFile && await loadFile(roadmapFile));
|
|
1316
1526
|
|
|
1317
|
-
|
|
1527
|
+
// A roadmap file with zero parseable slices (placeholder text) should be
|
|
1528
|
+
// treated the same as no roadmap — offer "Create roadmap" instead of "Go auto"
|
|
1529
|
+
// which would immediately get stuck in blocked state (#3441).
|
|
1530
|
+
let roadmapHasSlices = false;
|
|
1531
|
+
if (hasRoadmap) {
|
|
1532
|
+
const roadmapContent = await loadFile(roadmapFile!);
|
|
1533
|
+
if (roadmapContent) {
|
|
1534
|
+
const parsed = parseRoadmapSlices(roadmapContent);
|
|
1535
|
+
roadmapHasSlices = parsed.length > 0;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
if (!hasRoadmap || !roadmapHasSlices) {
|
|
1318
1540
|
// No roadmap → discuss or plan
|
|
1319
1541
|
const contextFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT");
|
|
1320
1542
|
const hasContext = !!(contextFile && await loadFile(contextFile));
|
|
@@ -1353,7 +1575,7 @@ export async function showSmartEntry(
|
|
|
1353
1575
|
});
|
|
1354
1576
|
|
|
1355
1577
|
if (choice === "plan") {
|
|
1356
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
1578
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode, createdAt: Date.now() });
|
|
1357
1579
|
const planMilestoneTemplates = [
|
|
1358
1580
|
inlineTemplate("roadmap", "Roadmap"),
|
|
1359
1581
|
inlineTemplate("plan", "Slice Plan"),
|
|
@@ -1379,13 +1601,14 @@ export async function showSmartEntry(
|
|
|
1379
1601
|
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
1380
1602
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1381
1603
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
1604
|
+
fastPathInstruction: "",
|
|
1382
1605
|
}), "gsd-run", ctx, "discuss-milestone");
|
|
1383
1606
|
} else if (choice === "skip_milestone") {
|
|
1384
1607
|
const milestoneIds = findMilestoneIds(basePath);
|
|
1385
1608
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1386
1609
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1387
|
-
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1388
|
-
await dispatchWorkflow(pi,
|
|
1610
|
+
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1611
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId,
|
|
1389
1612
|
`New milestone ${nextId}.`,
|
|
1390
1613
|
basePath
|
|
1391
1614
|
), "gsd-run", ctx, "discuss-milestone");
|
|
@@ -1514,7 +1737,8 @@ export async function showSmartEntry(
|
|
|
1514
1737
|
}),
|
|
1515
1738
|
}), "gsd-run", ctx, "plan-slice");
|
|
1516
1739
|
} else if (choice === "discuss") {
|
|
1517
|
-
|
|
1740
|
+
const sqAvail = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
|
1741
|
+
await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext, structuredQuestionsAvailable: sqAvail }), "gsd-run", ctx, "discuss-slice");
|
|
1518
1742
|
} else if (choice === "research") {
|
|
1519
1743
|
const researchTemplates = inlineTemplate("research", "Research");
|
|
1520
1744
|
await dispatchWorkflow(pi, loadPrompt("guided-research-slice", {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, unlinkSync } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Load a JSON file with validation, returning a default on failure.
|
|
@@ -51,9 +52,11 @@ export function loadJsonFileOrNull<T>(
|
|
|
51
52
|
export function saveJsonFile<T>(filePath: string, data: T): void {
|
|
52
53
|
try {
|
|
53
54
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
54
|
-
|
|
55
|
+
// Use randomized tmp suffix to prevent concurrent-write data loss
|
|
56
|
+
const tmp = `${filePath}.tmp.${randomBytes(4).toString("hex")}`;
|
|
55
57
|
writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
56
58
|
renameSync(tmp, filePath);
|
|
59
|
+
// No cleanup needed — renameSync atomically removes tmp on success
|
|
57
60
|
} catch {
|
|
58
61
|
// Non-fatal — don't let persistence failures break operation
|
|
59
62
|
}
|
|
@@ -66,7 +69,7 @@ export function saveJsonFile<T>(filePath: string, data: T): void {
|
|
|
66
69
|
export function writeJsonFileAtomic<T>(filePath: string, data: T): void {
|
|
67
70
|
try {
|
|
68
71
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
69
|
-
const tmp = filePath
|
|
72
|
+
const tmp = `${filePath}.tmp.${randomBytes(4).toString("hex")}`;
|
|
70
73
|
writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
|
|
71
74
|
renameSync(tmp, filePath);
|
|
72
75
|
} catch {
|
|
@@ -530,11 +530,6 @@ export function migrateHierarchyToDb(basePath: string): {
|
|
|
530
530
|
// Ghost milestone: no CONTEXT, ROADMAP, or SUMMARY → skip
|
|
531
531
|
if (!hasRoadmap && !hasContext && !hasSummary) continue;
|
|
532
532
|
|
|
533
|
-
// Determine milestone status
|
|
534
|
-
let milestoneStatus = 'active';
|
|
535
|
-
if (hasSummary) milestoneStatus = 'complete';
|
|
536
|
-
else if (hasParked) milestoneStatus = 'parked';
|
|
537
|
-
|
|
538
533
|
// Determine milestone title from roadmap H1 or CONTEXT heading
|
|
539
534
|
let milestoneTitle = '';
|
|
540
535
|
let roadmapContent: string | null = null;
|
|
@@ -544,6 +539,16 @@ export function migrateHierarchyToDb(basePath: string): {
|
|
|
544
539
|
roadmap = parseRoadmap(roadmapContent);
|
|
545
540
|
milestoneTitle = roadmap.title;
|
|
546
541
|
}
|
|
542
|
+
|
|
543
|
+
// Determine milestone status
|
|
544
|
+
let milestoneStatus = 'active';
|
|
545
|
+
if (hasSummary) milestoneStatus = 'complete';
|
|
546
|
+
else if (hasParked) milestoneStatus = 'parked';
|
|
547
|
+
// Import milestones with all-done roadmap slices as complete (#3390, #3379)
|
|
548
|
+
// even when SUMMARY.md is missing — the roadmap checkboxes are authoritative.
|
|
549
|
+
else if (roadmap && roadmap.slices.length > 0 && roadmap.slices.every(s => s.done)) {
|
|
550
|
+
milestoneStatus = 'complete';
|
|
551
|
+
}
|
|
547
552
|
if (!milestoneTitle && hasContext) {
|
|
548
553
|
const contextContent = readFileSync(contextPath!, 'utf-8');
|
|
549
554
|
const h1Match = contextContent.match(/^#\s+(.+)/m);
|
|
@@ -586,7 +591,8 @@ export function migrateHierarchyToDb(basePath: string): {
|
|
|
586
591
|
// Parse roadmap for slices
|
|
587
592
|
if (!roadmap) continue;
|
|
588
593
|
|
|
589
|
-
for (
|
|
594
|
+
for (let si = 0; si < roadmap.slices.length; si++) {
|
|
595
|
+
const sliceEntry = roadmap.slices[si]!;
|
|
590
596
|
// Per K002: use 'complete' not 'done'
|
|
591
597
|
const sliceStatus = sliceEntry.done ? 'complete' : 'pending';
|
|
592
598
|
|
|
@@ -606,6 +612,7 @@ export function migrateHierarchyToDb(basePath: string): {
|
|
|
606
612
|
risk: sliceEntry.risk,
|
|
607
613
|
depends: sliceEntry.depends,
|
|
608
614
|
demo: sliceEntry.demo,
|
|
615
|
+
sequence: si + 1, // Preserve roadmap parse order (#3356)
|
|
609
616
|
planning: {
|
|
610
617
|
goal: plan?.goal ?? '',
|
|
611
618
|
},
|
|
@@ -550,19 +550,27 @@ export function spawnWorker(
|
|
|
550
550
|
|
|
551
551
|
let child: ChildProcess;
|
|
552
552
|
try {
|
|
553
|
+
const workerEnv: Record<string, string | undefined> = {
|
|
554
|
+
...process.env,
|
|
555
|
+
GSD_MILESTONE_LOCK: milestoneId,
|
|
556
|
+
// Pass the real project root so workers don't need to re-derive it.
|
|
557
|
+
// Without this, process.cwd() resolves symlinks and the worktree
|
|
558
|
+
// path heuristic can match the user-level ~/.gsd instead of the
|
|
559
|
+
// project .gsd, causing writes to ~ and corrupting user config.
|
|
560
|
+
GSD_PROJECT_ROOT: basePath,
|
|
561
|
+
// Prevent workers from spawning their own parallel sessions
|
|
562
|
+
GSD_PARALLEL_WORKER: "1",
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
// Apply worker model override if configured, so workers use a cheaper
|
|
566
|
+
// model (e.g. Haiku) rather than inheriting the coordinator's model.
|
|
567
|
+
if (state.config.worker_model) {
|
|
568
|
+
workerEnv.GSD_WORKER_MODEL = state.config.worker_model;
|
|
569
|
+
}
|
|
570
|
+
|
|
553
571
|
child = spawn(process.execPath, [binPath, "headless", "--json", "auto"], {
|
|
554
572
|
cwd: worker.worktreePath,
|
|
555
|
-
env:
|
|
556
|
-
...process.env,
|
|
557
|
-
GSD_MILESTONE_LOCK: milestoneId,
|
|
558
|
-
// Pass the real project root so workers don't need to re-derive it.
|
|
559
|
-
// Without this, process.cwd() resolves symlinks and the worktree
|
|
560
|
-
// path heuristic can match the user-level ~/.gsd instead of the
|
|
561
|
-
// project .gsd, causing writes to ~ and corrupting user config.
|
|
562
|
-
GSD_PROJECT_ROOT: basePath,
|
|
563
|
-
// Prevent workers from spawning their own parallel sessions
|
|
564
|
-
GSD_PARALLEL_WORKER: "1",
|
|
565
|
-
},
|
|
573
|
+
env: workerEnv,
|
|
566
574
|
stdio: ["ignore", "pipe", "pipe"],
|
|
567
575
|
detached: false,
|
|
568
576
|
});
|