gsd-pi 2.65.0 → 2.66.0-dev.6c91c1f
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/finalize-timeout.js +2 -0
- package/dist/resources/extensions/gsd/auto/loop.js +2 -2
- package/dist/resources/extensions/gsd/auto/phases.js +48 -5
- 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/types.js +2 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +2 -1
- 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 +175 -12
- 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/system-context.js +3 -1
- 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 +23 -2
- 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/files.js +17 -0
- 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/notification-overlay.js +1 -1
- package/dist/resources/extensions/gsd/notification-widget.js +2 -1
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
- package/dist/resources/extensions/gsd/pre-execution-checks.js +26 -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/system.md +2 -2
- 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/resources/extensions/subagent/agents.js +19 -5
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- 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/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +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/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- 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/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
- 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/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +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/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.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/package.json +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/pkg/package.json +1 -1
- 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/finalize-timeout.ts +3 -0
- package/src/resources/extensions/gsd/auto/loop.ts +2 -2
- package/src/resources/extensions/gsd/auto/phases.ts +68 -3
- 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/types.ts +5 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
- 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 +188 -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/system-context.ts +3 -1
- 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 +26 -2
- 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/files.ts +19 -0
- 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/notification-overlay.ts +1 -1
- package/src/resources/extensions/gsd/notification-widget.ts +2 -1
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
- package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -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/system.md +2 -2
- 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/finalize-timeout-guard.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +69 -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/journal-integration.test.ts +11 -10
- 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/orphaned-worktree-audit.test.ts +189 -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 +284 -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/subagent-agent-discovery.test.ts +47 -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/src/resources/extensions/subagent/agents.ts +30 -6
- package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- /package/dist/web/standalone/.next/static/{MRM3OSYIAa4HMDqVGQ9nt → _X1i-S7l1jZfb7lmIZozb}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{MRM3OSYIAa4HMDqVGQ9nt → _X1i-S7l1jZfb7lmIZozb}/_ssgManifest.js +0 -0
|
@@ -409,6 +409,7 @@ function initSchema(db: DbAdapter, fileBacked: boolean): void {
|
|
|
409
409
|
db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
|
|
410
410
|
db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
|
|
411
411
|
db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
|
|
412
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
|
|
412
413
|
|
|
413
414
|
// v14 index — slice dependency lookups
|
|
414
415
|
db.exec("CREATE INDEX IF NOT EXISTS idx_slice_deps_target ON slice_dependencies(milestone_id, depends_on_slice_id)");
|
|
@@ -450,6 +451,25 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
450
451
|
const currentVersion = row ? (row["v"] as number) : 0;
|
|
451
452
|
if (currentVersion >= SCHEMA_VERSION) return;
|
|
452
453
|
|
|
454
|
+
// Backup database before migration so a mid-migration crash doesn't
|
|
455
|
+
// leave a partially-migrated DB with no recovery path.
|
|
456
|
+
// WAL-safe: checkpoint first to flush WAL into the main DB file, then copy.
|
|
457
|
+
if (currentPath && currentPath !== ":memory:" && existsSync(currentPath)) {
|
|
458
|
+
try {
|
|
459
|
+
const backupPath = `${currentPath}.backup-v${currentVersion}`;
|
|
460
|
+
if (!existsSync(backupPath)) {
|
|
461
|
+
// Flush WAL to main DB file before copying — without this, the backup
|
|
462
|
+
// may be missing committed data that only exists in the -wal file.
|
|
463
|
+
try { db.exec("PRAGMA wal_checkpoint(TRUNCATE)"); } catch { /* checkpoint is best-effort */ }
|
|
464
|
+
copyFileSync(currentPath, backupPath);
|
|
465
|
+
}
|
|
466
|
+
} catch (backupErr) {
|
|
467
|
+
// Log but proceed — blocking migration leaves the DB stuck at an old
|
|
468
|
+
// schema version permanently on read-only or full filesystems.
|
|
469
|
+
logWarning("db", `Pre-migration backup failed: ${backupErr instanceof Error ? backupErr.message : String(backupErr)}`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
453
473
|
db.exec("BEGIN");
|
|
454
474
|
try {
|
|
455
475
|
if (currentVersion < 2) {
|
|
@@ -722,6 +742,7 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
722
742
|
db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
|
|
723
743
|
db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
|
|
724
744
|
db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
|
|
745
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
|
|
725
746
|
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
726
747
|
":version": 13,
|
|
727
748
|
":applied_at": new Date().toISOString(),
|
|
@@ -997,9 +1018,21 @@ export function _resetProvider(): void {
|
|
|
997
1018
|
|
|
998
1019
|
export function upsertDecision(d: Omit<Decision, "seq">): void {
|
|
999
1020
|
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1021
|
+
// Use ON CONFLICT DO UPDATE instead of INSERT OR REPLACE to preserve the
|
|
1022
|
+
// seq column. INSERT OR REPLACE deletes then reinserts, resetting seq and
|
|
1023
|
+
// corrupting decision ordering in DECISIONS.md after reconcile replay.
|
|
1000
1024
|
currentDb.prepare(
|
|
1001
|
-
`INSERT
|
|
1002
|
-
VALUES (:id, :when_context, :scope, :decision, :choice, :rationale, :revisable, :made_by, :superseded_by)
|
|
1025
|
+
`INSERT INTO decisions (id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
|
|
1026
|
+
VALUES (:id, :when_context, :scope, :decision, :choice, :rationale, :revisable, :made_by, :superseded_by)
|
|
1027
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1028
|
+
when_context = excluded.when_context,
|
|
1029
|
+
scope = excluded.scope,
|
|
1030
|
+
decision = excluded.decision,
|
|
1031
|
+
choice = excluded.choice,
|
|
1032
|
+
rationale = excluded.rationale,
|
|
1033
|
+
revisable = excluded.revisable,
|
|
1034
|
+
made_by = excluded.made_by,
|
|
1035
|
+
superseded_by = excluded.superseded_by`,
|
|
1003
1036
|
).run({
|
|
1004
1037
|
":id": d.id,
|
|
1005
1038
|
":when_context": d.when_context,
|
|
@@ -1119,7 +1152,9 @@ export function insertMilestone(m: {
|
|
|
1119
1152
|
).run({
|
|
1120
1153
|
":id": m.id,
|
|
1121
1154
|
":title": m.title ?? "",
|
|
1122
|
-
"
|
|
1155
|
+
// Default to "queued" — never auto-create milestones as "active" (#3380).
|
|
1156
|
+
// Callers that need "active" must pass it explicitly.
|
|
1157
|
+
":status": m.status ?? "queued",
|
|
1123
1158
|
":depends_on": JSON.stringify(m.depends_on ?? []),
|
|
1124
1159
|
":created_at": new Date().toISOString(),
|
|
1125
1160
|
":vision": m.planning?.vision ?? "",
|
|
@@ -1349,6 +1384,13 @@ export function updateTaskStatus(milestoneId: string, sliceId: string, taskId: s
|
|
|
1349
1384
|
});
|
|
1350
1385
|
}
|
|
1351
1386
|
|
|
1387
|
+
export function setTaskBlockerDiscovered(milestoneId: string, sliceId: string, taskId: string, discovered: boolean): void {
|
|
1388
|
+
if (!currentDb) return;
|
|
1389
|
+
currentDb.prepare(
|
|
1390
|
+
`UPDATE tasks SET blocker_discovered = :discovered WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid`,
|
|
1391
|
+
).run({ ":discovered": discovered ? 1 : 0, ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1352
1394
|
export function upsertTaskPlanning(milestoneId: string, sliceId: string, taskId: string, planning: Partial<TaskPlanningRecord>): void {
|
|
1353
1395
|
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1354
1396
|
currentDb.prepare(
|
|
@@ -1543,7 +1585,7 @@ export function insertVerificationEvidence(e: {
|
|
|
1543
1585
|
}): void {
|
|
1544
1586
|
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1545
1587
|
currentDb.prepare(
|
|
1546
|
-
`INSERT INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
|
|
1588
|
+
`INSERT OR IGNORE INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
|
|
1547
1589
|
VALUES (:task_id, :slice_id, :milestone_id, :command, :exit_code, :verdict, :duration_ms, :created_at)`,
|
|
1548
1590
|
).run({
|
|
1549
1591
|
":task_id": e.taskId,
|
|
@@ -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 {
|