gsd-pi 2.50.0-dev.d210a87 → 2.51.0-dev.7d435fe
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/headless-events.d.ts +18 -0
- package/dist/headless-events.js +36 -0
- package/dist/headless-types.d.ts +28 -0
- package/dist/headless-types.js +7 -0
- package/dist/headless.d.ts +8 -3
- package/dist/headless.js +47 -16
- package/dist/help-text.js +16 -5
- package/dist/onboarding.js +5 -4
- package/dist/remote-questions-config.js +1 -1
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +29 -17
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +18 -19
- package/dist/resources/extensions/gsd/auto-dispatch.js +18 -0
- package/dist/resources/extensions/gsd/auto-start.js +2 -0
- package/dist/resources/extensions/gsd/auto-timers.js +24 -2
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +25 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +21 -0
- package/dist/resources/extensions/gsd/auto.js +4 -2
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +95 -69
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +12 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +1 -1
- package/dist/resources/extensions/gsd/claude-import.js +60 -9
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +69 -6
- package/dist/resources/extensions/gsd/commands-config.js +10 -5
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/detection.js +6 -6
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +3 -3
- package/dist/resources/extensions/gsd/error-classifier.js +105 -0
- package/dist/resources/extensions/gsd/gitignore.js +7 -7
- package/dist/resources/extensions/gsd/gsd-db.js +298 -45
- package/dist/resources/extensions/gsd/init-wizard.js +2 -2
- package/dist/resources/extensions/gsd/key-manager.js +7 -16
- package/dist/resources/extensions/gsd/memory-store.js +28 -13
- package/dist/resources/extensions/gsd/milestone-actions.js +19 -0
- package/dist/resources/extensions/gsd/preferences-models.js +1 -13
- package/dist/resources/extensions/gsd/preferences.js +13 -13
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/provider-error-pause.js +0 -44
- package/dist/resources/extensions/gsd/rule-registry.js +1 -1
- package/dist/resources/extensions/gsd/service-tier.js +13 -2
- package/dist/resources/extensions/gsd/state.js +21 -2
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -10
- package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -17
- package/dist/resources/extensions/gsd/tools/complete-task.js +7 -18
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +26 -17
- package/dist/resources/extensions/gsd/tools/plan-slice.js +25 -14
- package/dist/resources/extensions/gsd/tools/plan-task.js +21 -11
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +47 -37
- package/dist/resources/extensions/gsd/tools/replan-slice.js +49 -38
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +23 -16
- package/dist/resources/extensions/gsd/workflow-logger.js +0 -1
- package/dist/resources/extensions/remote-questions/config.js +1 -1
- package/dist/resources/extensions/remote-questions/remote-command.js +1 -1
- package/dist/resources/extensions/search-the-web/native-search.js +1 -1
- package/dist/resources/extensions/search-the-web/provider.js +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +21 -21
- 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/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_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 +2 -2
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- 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 +2 -2
- 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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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 +1 -1
- 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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +1 -1
- 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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.js +1 -1
- 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_client-reference-manifest.js +1 -1
- 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_client-reference-manifest.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_client-reference-manifest.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_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 +2 -2
- 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 +2 -2
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +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 +21 -21
- package/dist/web/standalone/.next/server/chunks/2229.js +2 -2
- 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/4024.21054f459af5cc78.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-cfc9a116e6450a6b.js → webpack-024d82be84800e52.js} +1 -1
- package/dist/web/standalone/.next/static/css/a58ef8a151aa0493.css +1 -0
- package/dist/wizard.js +4 -1
- package/package.json +2 -2
- package/packages/pi-ai/dist/models.d.ts +14 -3
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.js +53 -10
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/models.test.js +102 -1
- package/packages/pi-ai/dist/models.test.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +30 -0
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/models.test.ts +114 -1
- package/packages/pi-ai/src/models.ts +70 -13
- package/packages/pi-ai/src/types.ts +31 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts +2 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +3 -0
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.js +5 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +9 -4
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts +19 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +83 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +5 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +5 -3
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +0 -2
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +28 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +49 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +114 -6
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts +9 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js +831 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +66 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.js +0 -1
- package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/cli/args.ts +4 -0
- package/packages/pi-coding-agent/src/core/bash-executor.ts +5 -1
- package/packages/pi-coding-agent/src/core/model-registry.ts +10 -3
- package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +101 -0
- package/packages/pi-coding-agent/src/core/tools/bash.ts +5 -1
- package/packages/pi-coding-agent/src/index.ts +3 -0
- package/packages/pi-coding-agent/src/main.ts +5 -3
- package/packages/pi-coding-agent/src/modes/index.ts +8 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +0 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +54 -1
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +124 -6
- package/packages/pi-coding-agent/src/modes/rpc/rpc-protocol-v2.test.ts +971 -0
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +61 -4
- package/packages/pi-coding-agent/src/utils/shell.ts +0 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +22 -11
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +19 -20
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +21 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +19 -0
- package/src/resources/extensions/gsd/auto-start.ts +2 -0
- package/src/resources/extensions/gsd/auto-timers.ts +25 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +30 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +21 -0
- package/src/resources/extensions/gsd/auto.ts +5 -2
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +115 -72
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +11 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +1 -1
- package/src/resources/extensions/gsd/claude-import.ts +58 -9
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +73 -6
- package/src/resources/extensions/gsd/commands-config.ts +11 -5
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/detection.ts +6 -6
- package/src/resources/extensions/gsd/docs/preferences-reference.md +3 -3
- package/src/resources/extensions/gsd/error-classifier.ts +139 -0
- package/src/resources/extensions/gsd/gitignore.ts +7 -7
- package/src/resources/extensions/gsd/gsd-db.ts +355 -63
- package/src/resources/extensions/gsd/init-wizard.ts +2 -2
- package/src/resources/extensions/gsd/key-manager.ts +7 -16
- package/src/resources/extensions/gsd/memory-store.ts +29 -18
- package/src/resources/extensions/gsd/milestone-actions.ts +17 -0
- package/src/resources/extensions/gsd/preferences-models.ts +1 -13
- package/src/resources/extensions/gsd/preferences.ts +12 -13
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/provider-error-pause.ts +0 -57
- package/src/resources/extensions/gsd/rule-registry.ts +1 -1
- package/src/resources/extensions/gsd/service-tier.ts +14 -2
- package/src/resources/extensions/gsd/state.ts +22 -2
- package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +61 -0
- package/src/resources/extensions/gsd/tests/claude-import-marketplace-discovery.test.ts +191 -0
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/commands-config.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +35 -7
- package/src/resources/extensions/gsd/tests/detection.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/empty-db-reconciliation.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +119 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +16 -1
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +77 -70
- package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +42 -31
- package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/vacuous-truth-slices.test.ts +115 -0
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +81 -1
- package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +130 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -14
- package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -21
- package/src/resources/extensions/gsd/tools/complete-task.ts +9 -22
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +28 -18
- package/src/resources/extensions/gsd/tools/plan-slice.ts +28 -16
- package/src/resources/extensions/gsd/tools/plan-task.ts +24 -12
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +54 -42
- package/src/resources/extensions/gsd/tools/replan-slice.ts +53 -40
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +26 -20
- package/src/resources/extensions/gsd/workflow-logger.ts +0 -1
- package/src/resources/extensions/remote-questions/config.ts +1 -1
- package/src/resources/extensions/remote-questions/remote-command.ts +1 -1
- package/src/resources/extensions/search-the-web/native-search.ts +1 -1
- package/src/resources/extensions/search-the-web/provider.ts +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.9ad5def014d90ce4.js +0 -9
- package/dist/web/standalone/.next/static/css/de141508b083f922.css +0 -1
- /package/dist/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
- /package/dist/web/standalone/.next/static/{yJIyd5cXPNpmXTv18ZlyC → RqOU-jOv9uZ1Q03P6L6nn}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{yJIyd5cXPNpmXTv18ZlyC → RqOU-jOv9uZ1Q03P6L6nn}/_ssgManifest.js +0 -0
- /package/src/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
|
@@ -75,25 +75,34 @@ function normalizeRows(rows) {
|
|
|
75
75
|
}
|
|
76
76
|
function createAdapter(rawDb) {
|
|
77
77
|
const db = rawDb;
|
|
78
|
+
const stmtCache = new Map();
|
|
79
|
+
function wrapStmt(raw) {
|
|
80
|
+
return {
|
|
81
|
+
run(...params) {
|
|
82
|
+
return raw.run(...params);
|
|
83
|
+
},
|
|
84
|
+
get(...params) {
|
|
85
|
+
return normalizeRow(raw.get(...params));
|
|
86
|
+
},
|
|
87
|
+
all(...params) {
|
|
88
|
+
return normalizeRows(raw.all(...params));
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
78
92
|
return {
|
|
79
93
|
exec(sql) {
|
|
80
94
|
db.exec(sql);
|
|
81
95
|
},
|
|
82
96
|
prepare(sql) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return normalizeRow(stmt.get(...params));
|
|
90
|
-
},
|
|
91
|
-
all(...params) {
|
|
92
|
-
return normalizeRows(stmt.all(...params));
|
|
93
|
-
},
|
|
94
|
-
};
|
|
97
|
+
let cached = stmtCache.get(sql);
|
|
98
|
+
if (cached)
|
|
99
|
+
return cached;
|
|
100
|
+
cached = wrapStmt(db.prepare(sql));
|
|
101
|
+
stmtCache.set(sql, cached);
|
|
102
|
+
return cached;
|
|
95
103
|
},
|
|
96
104
|
close() {
|
|
105
|
+
stmtCache.clear();
|
|
97
106
|
db.close();
|
|
98
107
|
},
|
|
99
108
|
};
|
|
@@ -109,12 +118,21 @@ function openRawDb(path) {
|
|
|
109
118
|
const Database = providerModule;
|
|
110
119
|
return new Database(path);
|
|
111
120
|
}
|
|
112
|
-
const SCHEMA_VERSION =
|
|
121
|
+
const SCHEMA_VERSION = 14;
|
|
113
122
|
function initSchema(db, fileBacked) {
|
|
114
123
|
if (fileBacked)
|
|
115
124
|
db.exec("PRAGMA journal_mode=WAL");
|
|
116
125
|
if (fileBacked)
|
|
117
126
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
127
|
+
if (fileBacked)
|
|
128
|
+
db.exec("PRAGMA synchronous = NORMAL");
|
|
129
|
+
if (fileBacked)
|
|
130
|
+
db.exec("PRAGMA auto_vacuum = INCREMENTAL");
|
|
131
|
+
if (fileBacked)
|
|
132
|
+
db.exec("PRAGMA cache_size = -8000"); // 8 MB page cache
|
|
133
|
+
if (fileBacked)
|
|
134
|
+
db.exec("PRAGMA mmap_size = 67108864"); // 64 MB mmap
|
|
135
|
+
db.exec("PRAGMA temp_store = MEMORY");
|
|
118
136
|
db.exec("PRAGMA foreign_keys = ON");
|
|
119
137
|
db.exec("BEGIN");
|
|
120
138
|
try {
|
|
@@ -226,7 +244,7 @@ function initSchema(db, fileBacked) {
|
|
|
226
244
|
proof_level TEXT NOT NULL DEFAULT '',
|
|
227
245
|
integration_closure TEXT NOT NULL DEFAULT '',
|
|
228
246
|
observability_impact TEXT NOT NULL DEFAULT '',
|
|
229
|
-
sequence INTEGER DEFAULT 0, --
|
|
247
|
+
sequence INTEGER DEFAULT 0, -- Ordering hint: tools may set this to control execution order
|
|
230
248
|
replan_triggered_at TEXT DEFAULT NULL,
|
|
231
249
|
PRIMARY KEY (milestone_id, id),
|
|
232
250
|
FOREIGN KEY (milestone_id) REFERENCES milestones(id)
|
|
@@ -258,7 +276,7 @@ function initSchema(db, fileBacked) {
|
|
|
258
276
|
expected_output TEXT NOT NULL DEFAULT '[]',
|
|
259
277
|
observability_impact TEXT NOT NULL DEFAULT '',
|
|
260
278
|
full_plan_md TEXT NOT NULL DEFAULT '',
|
|
261
|
-
sequence INTEGER DEFAULT 0, --
|
|
279
|
+
sequence INTEGER DEFAULT 0, -- Ordering hint: tools may set this to control execution order
|
|
262
280
|
PRIMARY KEY (milestone_id, slice_id, id),
|
|
263
281
|
FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id)
|
|
264
282
|
)
|
|
@@ -318,9 +336,28 @@ function initSchema(db, fileBacked) {
|
|
|
318
336
|
PRIMARY KEY (milestone_id, slice_id, gate_id, task_id),
|
|
319
337
|
FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id)
|
|
320
338
|
)
|
|
339
|
+
`);
|
|
340
|
+
// Slice dependency junction table (v14)
|
|
341
|
+
db.exec(`
|
|
342
|
+
CREATE TABLE IF NOT EXISTS slice_dependencies (
|
|
343
|
+
milestone_id TEXT NOT NULL,
|
|
344
|
+
slice_id TEXT NOT NULL,
|
|
345
|
+
depends_on_slice_id TEXT NOT NULL,
|
|
346
|
+
PRIMARY KEY (milestone_id, slice_id, depends_on_slice_id),
|
|
347
|
+
FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id),
|
|
348
|
+
FOREIGN KEY (milestone_id, depends_on_slice_id) REFERENCES slices(milestone_id, id)
|
|
349
|
+
)
|
|
321
350
|
`);
|
|
322
351
|
db.exec("CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(superseded_by)");
|
|
323
352
|
db.exec("CREATE INDEX IF NOT EXISTS idx_replan_history_milestone ON replan_history(milestone_id, created_at)");
|
|
353
|
+
// v13 indexes — hot-path dispatch queries
|
|
354
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_active ON tasks(milestone_id, slice_id, status)");
|
|
355
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_slices_active ON slices(milestone_id, status)");
|
|
356
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
|
|
357
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
|
|
358
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
|
|
359
|
+
// v14 index — slice dependency lookups
|
|
360
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_slice_deps_target ON slice_dependencies(milestone_id, depends_on_slice_id)");
|
|
324
361
|
db.exec(`CREATE VIEW IF NOT EXISTS active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL`);
|
|
325
362
|
db.exec(`CREATE VIEW IF NOT EXISTS active_requirements AS SELECT * FROM requirements WHERE superseded_by IS NULL`);
|
|
326
363
|
db.exec(`CREATE VIEW IF NOT EXISTS active_memories AS SELECT * FROM memories WHERE superseded_by IS NULL`);
|
|
@@ -599,6 +636,35 @@ function migrateSchema(db) {
|
|
|
599
636
|
":applied_at": new Date().toISOString(),
|
|
600
637
|
});
|
|
601
638
|
}
|
|
639
|
+
if (currentVersion < 13) {
|
|
640
|
+
// Hot-path indexes for auto-loop dispatch queries
|
|
641
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_active ON tasks(milestone_id, slice_id, status)");
|
|
642
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_slices_active ON slices(milestone_id, status)");
|
|
643
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
|
|
644
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
|
|
645
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
|
|
646
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
647
|
+
":version": 13,
|
|
648
|
+
":applied_at": new Date().toISOString(),
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
if (currentVersion < 14) {
|
|
652
|
+
db.exec(`
|
|
653
|
+
CREATE TABLE IF NOT EXISTS slice_dependencies (
|
|
654
|
+
milestone_id TEXT NOT NULL,
|
|
655
|
+
slice_id TEXT NOT NULL,
|
|
656
|
+
depends_on_slice_id TEXT NOT NULL,
|
|
657
|
+
PRIMARY KEY (milestone_id, slice_id, depends_on_slice_id),
|
|
658
|
+
FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id),
|
|
659
|
+
FOREIGN KEY (milestone_id, depends_on_slice_id) REFERENCES slices(milestone_id, id)
|
|
660
|
+
)
|
|
661
|
+
`);
|
|
662
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_slice_deps_target ON slice_dependencies(milestone_id, depends_on_slice_id)");
|
|
663
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
664
|
+
":version": 14,
|
|
665
|
+
":applied_at": new Date().toISOString(),
|
|
666
|
+
});
|
|
667
|
+
}
|
|
602
668
|
db.exec("COMMIT");
|
|
603
669
|
}
|
|
604
670
|
catch (err) {
|
|
@@ -655,6 +721,11 @@ export function closeDatabase() {
|
|
|
655
721
|
currentDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
656
722
|
}
|
|
657
723
|
catch { /* non-fatal — best effort before close */ }
|
|
724
|
+
try {
|
|
725
|
+
// Incremental vacuum to reclaim space without blocking
|
|
726
|
+
currentDb.exec('PRAGMA incremental_vacuum(64)');
|
|
727
|
+
}
|
|
728
|
+
catch { /* non-fatal */ }
|
|
658
729
|
try {
|
|
659
730
|
currentDb.close();
|
|
660
731
|
}
|
|
@@ -666,9 +737,31 @@ export function closeDatabase() {
|
|
|
666
737
|
currentPid = 0;
|
|
667
738
|
}
|
|
668
739
|
}
|
|
740
|
+
/** Run a full VACUUM — call sparingly (e.g. after milestone completion). */
|
|
741
|
+
export function vacuumDatabase() {
|
|
742
|
+
if (!currentDb)
|
|
743
|
+
return;
|
|
744
|
+
try {
|
|
745
|
+
currentDb.exec('VACUUM');
|
|
746
|
+
}
|
|
747
|
+
catch { /* non-fatal */ }
|
|
748
|
+
}
|
|
749
|
+
let _txDepth = 0;
|
|
669
750
|
export function transaction(fn) {
|
|
670
751
|
if (!currentDb)
|
|
671
752
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
753
|
+
// Re-entrant: if already inside a transaction, just run fn() without
|
|
754
|
+
// starting a new one. SQLite does not support nested BEGIN/COMMIT.
|
|
755
|
+
if (_txDepth > 0) {
|
|
756
|
+
_txDepth++;
|
|
757
|
+
try {
|
|
758
|
+
return fn();
|
|
759
|
+
}
|
|
760
|
+
finally {
|
|
761
|
+
_txDepth--;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
_txDepth++;
|
|
672
765
|
currentDb.exec("BEGIN");
|
|
673
766
|
try {
|
|
674
767
|
const result = fn();
|
|
@@ -679,6 +772,9 @@ export function transaction(fn) {
|
|
|
679
772
|
currentDb.exec("ROLLBACK");
|
|
680
773
|
throw err;
|
|
681
774
|
}
|
|
775
|
+
finally {
|
|
776
|
+
_txDepth--;
|
|
777
|
+
}
|
|
682
778
|
}
|
|
683
779
|
export function insertDecision(d) {
|
|
684
780
|
if (!currentDb)
|
|
@@ -1112,6 +1208,16 @@ export function updateSliceStatus(milestoneId, sliceId, status, completedAt) {
|
|
|
1112
1208
|
":id": sliceId,
|
|
1113
1209
|
});
|
|
1114
1210
|
}
|
|
1211
|
+
export function setTaskSummaryMd(milestoneId, sliceId, taskId, md) {
|
|
1212
|
+
if (!currentDb)
|
|
1213
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1214
|
+
currentDb.prepare(`UPDATE tasks SET full_summary_md = :md WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid`).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId, ":md": md });
|
|
1215
|
+
}
|
|
1216
|
+
export function setSliceSummaryMd(milestoneId, sliceId, summaryMd, uatMd) {
|
|
1217
|
+
if (!currentDb)
|
|
1218
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1219
|
+
currentDb.prepare(`UPDATE slices SET full_summary_md = :summary_md, full_uat_md = :uat_md WHERE milestone_id = :mid AND id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId, ":summary_md": summaryMd, ":uat_md": uatMd });
|
|
1220
|
+
}
|
|
1115
1221
|
function rowToTask(row) {
|
|
1116
1222
|
return {
|
|
1117
1223
|
milestone_id: row["milestone_id"],
|
|
@@ -1216,6 +1322,16 @@ export function getMilestone(id) {
|
|
|
1216
1322
|
return null;
|
|
1217
1323
|
return rowToMilestone(row);
|
|
1218
1324
|
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Update a milestone's status in the database.
|
|
1327
|
+
* Used by park/unpark to keep the DB in sync with the filesystem marker.
|
|
1328
|
+
* See: https://github.com/gsd-build/gsd-2/issues/2694
|
|
1329
|
+
*/
|
|
1330
|
+
export function updateMilestoneStatus(milestoneId, status, completedAt) {
|
|
1331
|
+
if (!currentDb)
|
|
1332
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1333
|
+
currentDb.prepare(`UPDATE milestones SET status = :status, completed_at = :completed_at WHERE id = :id`).run({ ":status": status, ":completed_at": completedAt ?? null, ":id": milestoneId });
|
|
1334
|
+
}
|
|
1219
1335
|
export function getActiveMilestoneFromDb() {
|
|
1220
1336
|
if (!currentDb)
|
|
1221
1337
|
return null;
|
|
@@ -1227,18 +1343,22 @@ export function getActiveMilestoneFromDb() {
|
|
|
1227
1343
|
export function getActiveSliceFromDb(milestoneId) {
|
|
1228
1344
|
if (!currentDb)
|
|
1229
1345
|
return null;
|
|
1230
|
-
|
|
1231
|
-
|
|
1346
|
+
// Single query: find the first non-complete slice whose dependencies are all satisfied.
|
|
1347
|
+
// Uses json_each() to expand the JSON depends array and checks each dep is complete.
|
|
1348
|
+
const row = currentDb.prepare(`SELECT s.* FROM slices s
|
|
1349
|
+
WHERE s.milestone_id = :mid
|
|
1350
|
+
AND s.status NOT IN ('complete', 'done')
|
|
1351
|
+
AND NOT EXISTS (
|
|
1352
|
+
SELECT 1 FROM json_each(s.depends) AS dep
|
|
1353
|
+
WHERE dep.value NOT IN (
|
|
1354
|
+
SELECT id FROM slices WHERE milestone_id = :mid AND status IN ('complete', 'done')
|
|
1355
|
+
)
|
|
1356
|
+
)
|
|
1357
|
+
ORDER BY s.sequence, s.id
|
|
1358
|
+
LIMIT 1`).get({ ":mid": milestoneId });
|
|
1359
|
+
if (!row)
|
|
1232
1360
|
return null;
|
|
1233
|
-
|
|
1234
|
-
const completedIds = new Set(completedRows.map((r) => r["id"]));
|
|
1235
|
-
for (const row of rows) {
|
|
1236
|
-
const slice = rowToSlice(row);
|
|
1237
|
-
if (slice.depends.length === 0 || slice.depends.every((d) => completedIds.has(d))) {
|
|
1238
|
-
return slice;
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
return null;
|
|
1361
|
+
return rowToSlice(row);
|
|
1242
1362
|
}
|
|
1243
1363
|
export function getActiveTaskFromDb(milestoneId, sliceId) {
|
|
1244
1364
|
if (!currentDb)
|
|
@@ -1262,6 +1382,60 @@ export function getArtifact(path) {
|
|
|
1262
1382
|
return null;
|
|
1263
1383
|
return rowToArtifact(row);
|
|
1264
1384
|
}
|
|
1385
|
+
// ─── Lightweight Query Variants (hot-path optimized) ─────────────────────
|
|
1386
|
+
/** Fast milestone status check — avoids deserializing JSON planning fields. */
|
|
1387
|
+
export function getActiveMilestoneIdFromDb() {
|
|
1388
|
+
if (!currentDb)
|
|
1389
|
+
return null;
|
|
1390
|
+
const row = currentDb.prepare("SELECT id, status FROM milestones WHERE status NOT IN ('complete', 'parked') ORDER BY id LIMIT 1").get();
|
|
1391
|
+
if (!row)
|
|
1392
|
+
return null;
|
|
1393
|
+
return { id: row["id"], status: row["status"] };
|
|
1394
|
+
}
|
|
1395
|
+
/** Fast slice status check — avoids deserializing JSON depends/planning fields. */
|
|
1396
|
+
export function getSliceStatusSummary(milestoneId) {
|
|
1397
|
+
if (!currentDb)
|
|
1398
|
+
return [];
|
|
1399
|
+
return currentDb.prepare("SELECT id, status FROM slices WHERE milestone_id = :mid ORDER BY sequence, id").all({ ":mid": milestoneId }).map((r) => ({ id: r["id"], status: r["status"] }));
|
|
1400
|
+
}
|
|
1401
|
+
/** Fast task status check — avoids deserializing JSON arrays and large text fields. */
|
|
1402
|
+
export function getActiveTaskIdFromDb(milestoneId, sliceId) {
|
|
1403
|
+
if (!currentDb)
|
|
1404
|
+
return null;
|
|
1405
|
+
const row = currentDb.prepare("SELECT id, status, title FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND status NOT IN ('complete', 'done') ORDER BY sequence, id LIMIT 1").get({ ":mid": milestoneId, ":sid": sliceId });
|
|
1406
|
+
if (!row)
|
|
1407
|
+
return null;
|
|
1408
|
+
return { id: row["id"], status: row["status"], title: row["title"] };
|
|
1409
|
+
}
|
|
1410
|
+
/** Count tasks by status for a slice — useful for progress reporting without full row load. */
|
|
1411
|
+
export function getSliceTaskCounts(milestoneId, sliceId) {
|
|
1412
|
+
if (!currentDb)
|
|
1413
|
+
return { total: 0, done: 0, pending: 0 };
|
|
1414
|
+
const row = currentDb.prepare(`SELECT
|
|
1415
|
+
COUNT(*) as total,
|
|
1416
|
+
SUM(CASE WHEN status IN ('complete', 'done') THEN 1 ELSE 0 END) as done,
|
|
1417
|
+
SUM(CASE WHEN status NOT IN ('complete', 'done') THEN 1 ELSE 0 END) as pending
|
|
1418
|
+
FROM tasks WHERE milestone_id = :mid AND slice_id = :sid`).get({ ":mid": milestoneId, ":sid": sliceId });
|
|
1419
|
+
if (!row)
|
|
1420
|
+
return { total: 0, done: 0, pending: 0 };
|
|
1421
|
+
return { total: row["total"] ?? 0, done: row["done"] ?? 0, pending: row["pending"] ?? 0 };
|
|
1422
|
+
}
|
|
1423
|
+
// ─── Slice Dependencies (junction table) ─────────────────────────────────
|
|
1424
|
+
/** Sync the slice_dependencies junction table from a slice's JSON depends array. */
|
|
1425
|
+
export function syncSliceDependencies(milestoneId, sliceId, depends) {
|
|
1426
|
+
if (!currentDb)
|
|
1427
|
+
return;
|
|
1428
|
+
currentDb.prepare("DELETE FROM slice_dependencies WHERE milestone_id = :mid AND slice_id = :sid").run({ ":mid": milestoneId, ":sid": sliceId });
|
|
1429
|
+
for (const dep of depends) {
|
|
1430
|
+
currentDb.prepare("INSERT OR IGNORE INTO slice_dependencies (milestone_id, slice_id, depends_on_slice_id) VALUES (:mid, :sid, :dep)").run({ ":mid": milestoneId, ":sid": sliceId, ":dep": dep });
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
/** Get all slices that depend on a given slice. */
|
|
1434
|
+
export function getDependentSlices(milestoneId, sliceId) {
|
|
1435
|
+
if (!currentDb)
|
|
1436
|
+
return [];
|
|
1437
|
+
return currentDb.prepare("SELECT slice_id FROM slice_dependencies WHERE milestone_id = :mid AND depends_on_slice_id = :sid").all({ ":mid": milestoneId, ":sid": sliceId }).map((r) => r["slice_id"]);
|
|
1438
|
+
}
|
|
1265
1439
|
// ─── Worktree DB Helpers ──────────────────────────────────────────────────
|
|
1266
1440
|
export function copyWorktreeDb(srcDbPath, destDbPath) {
|
|
1267
1441
|
try {
|
|
@@ -1278,10 +1452,13 @@ export function copyWorktreeDb(srcDbPath, destDbPath) {
|
|
|
1278
1452
|
}
|
|
1279
1453
|
}
|
|
1280
1454
|
export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
|
|
1281
|
-
const zero = { decisions: 0, requirements: 0, artifacts: 0, conflicts: [] };
|
|
1455
|
+
const zero = { decisions: 0, requirements: 0, artifacts: 0, milestones: 0, slices: 0, tasks: 0, memories: 0, verification_evidence: 0, conflicts: [] };
|
|
1282
1456
|
if (!existsSync(worktreeDbPath))
|
|
1283
1457
|
return zero;
|
|
1284
|
-
|
|
1458
|
+
// Sanitize path: reject any characters that could break ATTACH syntax.
|
|
1459
|
+
// ATTACH DATABASE doesn't support parameterized paths in all providers,
|
|
1460
|
+
// so we use strict allowlist validation instead.
|
|
1461
|
+
if (/['";\x00]/.test(worktreeDbPath)) {
|
|
1285
1462
|
process.stderr.write("gsd-db: worktree DB reconciliation failed: path contains unsafe characters\n");
|
|
1286
1463
|
return zero;
|
|
1287
1464
|
}
|
|
@@ -1305,17 +1482,19 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
|
|
|
1305
1482
|
const reqConf = adapter.prepare(`SELECT m.id FROM requirements m INNER JOIN wt.requirements w ON m.id = w.id WHERE m.description != w.description OR m.status != w.status OR m.notes != w.notes OR m.superseded_by IS NOT w.superseded_by`).all();
|
|
1306
1483
|
for (const row of reqConf)
|
|
1307
1484
|
conflicts.push(`requirement ${row["id"]}: modified in both`);
|
|
1308
|
-
const merged = { decisions: 0, requirements: 0, artifacts: 0 };
|
|
1485
|
+
const merged = { decisions: 0, requirements: 0, artifacts: 0, milestones: 0, slices: 0, tasks: 0, memories: 0, verification_evidence: 0 };
|
|
1486
|
+
function countChanges(result) {
|
|
1487
|
+
return typeof result === "object" && result !== null ? (result.changes ?? 0) : 0;
|
|
1488
|
+
}
|
|
1309
1489
|
adapter.exec("BEGIN");
|
|
1310
1490
|
try {
|
|
1311
|
-
|
|
1491
|
+
merged.decisions = countChanges(adapter.prepare(`
|
|
1312
1492
|
INSERT OR REPLACE INTO decisions (
|
|
1313
1493
|
id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by
|
|
1314
1494
|
)
|
|
1315
1495
|
SELECT id, when_context, scope, decision, choice, rationale, revisable, ${hasMadeBy ? "made_by" : "'agent'"}, superseded_by FROM wt.decisions
|
|
1316
|
-
`).run();
|
|
1317
|
-
merged.
|
|
1318
|
-
const rR = adapter.prepare(`
|
|
1496
|
+
`).run());
|
|
1497
|
+
merged.requirements = countChanges(adapter.prepare(`
|
|
1319
1498
|
INSERT OR REPLACE INTO requirements (
|
|
1320
1499
|
id, class, status, description, why, source, primary_owner,
|
|
1321
1500
|
supporting_slices, validation, notes, full_content, superseded_by
|
|
@@ -1323,16 +1502,74 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
|
|
|
1323
1502
|
SELECT id, class, status, description, why, source, primary_owner,
|
|
1324
1503
|
supporting_slices, validation, notes, full_content, superseded_by
|
|
1325
1504
|
FROM wt.requirements
|
|
1326
|
-
`).run();
|
|
1327
|
-
merged.
|
|
1328
|
-
const aR = adapter.prepare(`
|
|
1505
|
+
`).run());
|
|
1506
|
+
merged.artifacts = countChanges(adapter.prepare(`
|
|
1329
1507
|
INSERT OR REPLACE INTO artifacts (
|
|
1330
1508
|
path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
|
|
1331
1509
|
)
|
|
1332
1510
|
SELECT path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
|
|
1333
1511
|
FROM wt.artifacts
|
|
1334
|
-
`).run();
|
|
1335
|
-
|
|
1512
|
+
`).run());
|
|
1513
|
+
// Merge milestones — worktree may have updated status/planning fields
|
|
1514
|
+
merged.milestones = countChanges(adapter.prepare(`
|
|
1515
|
+
INSERT OR REPLACE INTO milestones (
|
|
1516
|
+
id, title, status, depends_on, created_at, completed_at,
|
|
1517
|
+
vision, success_criteria, key_risks, proof_strategy,
|
|
1518
|
+
verification_contract, verification_integration, verification_operational, verification_uat,
|
|
1519
|
+
definition_of_done, requirement_coverage, boundary_map_markdown
|
|
1520
|
+
)
|
|
1521
|
+
SELECT id, title, status, depends_on, created_at, completed_at,
|
|
1522
|
+
vision, success_criteria, key_risks, proof_strategy,
|
|
1523
|
+
verification_contract, verification_integration, verification_operational, verification_uat,
|
|
1524
|
+
definition_of_done, requirement_coverage, boundary_map_markdown
|
|
1525
|
+
FROM wt.milestones
|
|
1526
|
+
`).run());
|
|
1527
|
+
// Merge slices — preserve worktree progress (status, summaries, planning)
|
|
1528
|
+
merged.slices = countChanges(adapter.prepare(`
|
|
1529
|
+
INSERT OR REPLACE INTO slices (
|
|
1530
|
+
milestone_id, id, title, status, risk, depends, demo, created_at, completed_at,
|
|
1531
|
+
full_summary_md, full_uat_md, goal, success_criteria, proof_level,
|
|
1532
|
+
integration_closure, observability_impact, sequence, replan_triggered_at
|
|
1533
|
+
)
|
|
1534
|
+
SELECT milestone_id, id, title, status, risk, depends, demo, created_at, completed_at,
|
|
1535
|
+
full_summary_md, full_uat_md, goal, success_criteria, proof_level,
|
|
1536
|
+
integration_closure, observability_impact, sequence, replan_triggered_at
|
|
1537
|
+
FROM wt.slices
|
|
1538
|
+
`).run());
|
|
1539
|
+
// Merge tasks — preserve execution results, status, summaries
|
|
1540
|
+
merged.tasks = countChanges(adapter.prepare(`
|
|
1541
|
+
INSERT OR REPLACE INTO tasks (
|
|
1542
|
+
milestone_id, slice_id, id, title, status, one_liner, narrative,
|
|
1543
|
+
verification_result, duration, completed_at, blocker_discovered,
|
|
1544
|
+
deviations, known_issues, key_files, key_decisions, full_summary_md,
|
|
1545
|
+
description, estimate, files, verify, inputs, expected_output,
|
|
1546
|
+
observability_impact, full_plan_md, sequence
|
|
1547
|
+
)
|
|
1548
|
+
SELECT milestone_id, slice_id, id, title, status, one_liner, narrative,
|
|
1549
|
+
verification_result, duration, completed_at, blocker_discovered,
|
|
1550
|
+
deviations, known_issues, key_files, key_decisions, full_summary_md,
|
|
1551
|
+
description, estimate, files, verify, inputs, expected_output,
|
|
1552
|
+
observability_impact, full_plan_md, sequence
|
|
1553
|
+
FROM wt.tasks
|
|
1554
|
+
`).run());
|
|
1555
|
+
// Merge memories — keep worktree-learned insights
|
|
1556
|
+
merged.memories = countChanges(adapter.prepare(`
|
|
1557
|
+
INSERT OR REPLACE INTO memories (
|
|
1558
|
+
seq, id, category, content, confidence, source_unit_type, source_unit_id,
|
|
1559
|
+
created_at, updated_at, superseded_by, hit_count
|
|
1560
|
+
)
|
|
1561
|
+
SELECT seq, id, category, content, confidence, source_unit_type, source_unit_id,
|
|
1562
|
+
created_at, updated_at, superseded_by, hit_count
|
|
1563
|
+
FROM wt.memories
|
|
1564
|
+
`).run());
|
|
1565
|
+
// Merge verification evidence — append-only, use INSERT OR IGNORE to avoid duplicates
|
|
1566
|
+
merged.verification_evidence = countChanges(adapter.prepare(`
|
|
1567
|
+
INSERT OR IGNORE INTO verification_evidence (
|
|
1568
|
+
task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at
|
|
1569
|
+
)
|
|
1570
|
+
SELECT task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at
|
|
1571
|
+
FROM wt.verification_evidence
|
|
1572
|
+
`).run());
|
|
1336
1573
|
adapter.exec("COMMIT");
|
|
1337
1574
|
}
|
|
1338
1575
|
catch (txErr) {
|
|
@@ -1388,20 +1625,36 @@ export function insertAssessment(entry) {
|
|
|
1388
1625
|
":created_at": new Date().toISOString(),
|
|
1389
1626
|
});
|
|
1390
1627
|
}
|
|
1391
|
-
export function
|
|
1628
|
+
export function deleteAssessmentByScope(milestoneId, scope) {
|
|
1629
|
+
if (!currentDb)
|
|
1630
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1631
|
+
currentDb.prepare(`DELETE FROM assessments WHERE milestone_id = :mid AND scope = :scope`).run({ ":mid": milestoneId, ":scope": scope });
|
|
1632
|
+
}
|
|
1633
|
+
export function deleteVerificationEvidence(milestoneId, sliceId, taskId) {
|
|
1392
1634
|
if (!currentDb)
|
|
1393
1635
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1394
|
-
// Must delete verification_evidence first (FK constraint)
|
|
1395
1636
|
currentDb.prepare(`DELETE FROM verification_evidence WHERE milestone_id = :mid AND slice_id = :sid AND task_id = :tid`).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
|
|
1396
|
-
|
|
1637
|
+
}
|
|
1638
|
+
export function deleteTask(milestoneId, sliceId, taskId) {
|
|
1639
|
+
if (!currentDb)
|
|
1640
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1641
|
+
transaction(() => {
|
|
1642
|
+
// Must delete verification_evidence first (FK constraint)
|
|
1643
|
+
currentDb.prepare(`DELETE FROM verification_evidence WHERE milestone_id = :mid AND slice_id = :sid AND task_id = :tid`).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
|
|
1644
|
+
currentDb.prepare(`DELETE FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid`).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
|
|
1645
|
+
});
|
|
1397
1646
|
}
|
|
1398
1647
|
export function deleteSlice(milestoneId, sliceId) {
|
|
1399
1648
|
if (!currentDb)
|
|
1400
1649
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1650
|
+
transaction(() => {
|
|
1651
|
+
// Cascade-style manual deletion: evidence → tasks → dependencies → slice
|
|
1652
|
+
currentDb.prepare(`DELETE FROM verification_evidence WHERE milestone_id = :mid AND slice_id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId });
|
|
1653
|
+
currentDb.prepare(`DELETE FROM tasks WHERE milestone_id = :mid AND slice_id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId });
|
|
1654
|
+
currentDb.prepare(`DELETE FROM slice_dependencies WHERE milestone_id = :mid AND slice_id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId });
|
|
1655
|
+
currentDb.prepare(`DELETE FROM slice_dependencies WHERE milestone_id = :mid AND depends_on_slice_id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId });
|
|
1656
|
+
currentDb.prepare(`DELETE FROM slices WHERE milestone_id = :mid AND id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId });
|
|
1657
|
+
});
|
|
1405
1658
|
}
|
|
1406
1659
|
export function updateSliceFields(milestoneId, sliceId, fields) {
|
|
1407
1660
|
if (!currentDb)
|
|
@@ -336,9 +336,9 @@ function bootstrapGsdDirectory(basePath, prefs, signals) {
|
|
|
336
336
|
assertSafeDirectory(basePath);
|
|
337
337
|
const gsd = gsdRoot(basePath);
|
|
338
338
|
mkdirSync(join(gsd, "milestones"), { recursive: true });
|
|
339
|
-
// Write
|
|
339
|
+
// Write PREFERENCES.md from wizard answers
|
|
340
340
|
const preferencesContent = buildPreferencesFile(prefs);
|
|
341
|
-
writeFileSync(join(gsd, "
|
|
341
|
+
writeFileSync(join(gsd, "PREFERENCES.md"), preferencesContent, "utf-8");
|
|
342
342
|
// Seed CONTEXT.md with detected project signals
|
|
343
343
|
const contextContent = buildContextSeed(signals);
|
|
344
344
|
if (contextContent) {
|
|
@@ -110,21 +110,12 @@ export function findProvider(idOrLabel) {
|
|
|
110
110
|
*/
|
|
111
111
|
export function getAllKeyStatuses(auth) {
|
|
112
112
|
return PROVIDER_REGISTRY.map((provider) => {
|
|
113
|
-
const
|
|
113
|
+
const rawCreds = auth.getCredentialsForProvider(provider.id);
|
|
114
|
+
// Filter out empty-key entries (left by legacy removeProviderToken or skipped onboarding)
|
|
115
|
+
const creds = rawCreds.filter((c) => !(c.type === "api_key" && !c.key));
|
|
114
116
|
const envKey = provider.envVar ? process.env[provider.envVar] : undefined;
|
|
115
117
|
if (creds.length > 0) {
|
|
116
118
|
const firstCred = creds[0];
|
|
117
|
-
// Skip empty keys (from skipped onboarding)
|
|
118
|
-
if (firstCred.type === "api_key" && !firstCred.key) {
|
|
119
|
-
return {
|
|
120
|
-
provider,
|
|
121
|
-
configured: false,
|
|
122
|
-
source: "none",
|
|
123
|
-
credentialCount: 0,
|
|
124
|
-
description: "empty key (skipped setup)",
|
|
125
|
-
backedOff: false,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
119
|
const desc = creds.length > 1
|
|
129
120
|
? `${creds.length} keys (round-robin)`
|
|
130
121
|
: describeCredential(firstCred);
|
|
@@ -222,7 +213,7 @@ export async function handleAddKey(providerArg, ctx, auth) {
|
|
|
222
213
|
else {
|
|
223
214
|
// Interactive provider picker
|
|
224
215
|
const options = PROVIDER_REGISTRY.map((p) => {
|
|
225
|
-
const creds = auth.getCredentialsForProvider(p.id);
|
|
216
|
+
const creds = auth.getCredentialsForProvider(p.id).filter((c) => !(c.type === "api_key" && !c.key));
|
|
226
217
|
const existing = creds.length > 0 ? " (configured)" : "";
|
|
227
218
|
return `[${p.category}] ${p.label}${existing}`;
|
|
228
219
|
});
|
|
@@ -285,7 +276,7 @@ export async function handleRemoveKey(providerArg, ctx, auth) {
|
|
|
285
276
|
else {
|
|
286
277
|
// Show only configured providers
|
|
287
278
|
const configured = PROVIDER_REGISTRY.filter((p) => {
|
|
288
|
-
const creds = auth.getCredentialsForProvider(p.id);
|
|
279
|
+
const creds = auth.getCredentialsForProvider(p.id).filter((c) => !(c.type === "api_key" && !c.key));
|
|
289
280
|
return creds.length > 0;
|
|
290
281
|
});
|
|
291
282
|
if (configured.length === 0) {
|
|
@@ -508,7 +499,7 @@ export async function handleRotateKey(providerArg, ctx, auth) {
|
|
|
508
499
|
// Show only configured API key providers
|
|
509
500
|
const configured = PROVIDER_REGISTRY.filter((p) => {
|
|
510
501
|
const creds = auth.getCredentialsForProvider(p.id);
|
|
511
|
-
return creds.some((c) => c.type === "api_key");
|
|
502
|
+
return creds.some((c) => c.type === "api_key" && c.key);
|
|
512
503
|
});
|
|
513
504
|
if (configured.length === 0) {
|
|
514
505
|
ctx.ui.notify("No API keys configured to rotate.", "info");
|
|
@@ -646,7 +637,7 @@ export function runKeyDoctor(auth) {
|
|
|
646
637
|
if (!envValue)
|
|
647
638
|
continue;
|
|
648
639
|
const creds = auth.getCredentialsForProvider(provider.id);
|
|
649
|
-
const apiKey = creds.find((c) => c.type === "api_key");
|
|
640
|
+
const apiKey = creds.find((c) => c.type === "api_key" && c.key);
|
|
650
641
|
if (apiKey?.key && apiKey.key !== envValue) {
|
|
651
642
|
findings.push({
|
|
652
643
|
severity: "warning",
|
|
@@ -71,6 +71,9 @@ export function getActiveMemoriesRanked(limit = 30) {
|
|
|
71
71
|
/**
|
|
72
72
|
* Generate the next memory ID: MEM + zero-padded 3-digit from MAX(seq).
|
|
73
73
|
* Returns MEM001 if no memories exist.
|
|
74
|
+
*
|
|
75
|
+
* NOTE: For race-safe creation, prefer createMemory() which inserts with a
|
|
76
|
+
* placeholder ID then updates to the seq-derived ID atomically.
|
|
74
77
|
*/
|
|
75
78
|
export function nextMemoryId() {
|
|
76
79
|
if (!isDbAvailable())
|
|
@@ -94,7 +97,9 @@ export function nextMemoryId() {
|
|
|
94
97
|
}
|
|
95
98
|
// ─── Mutation Functions ─────────────────────────────────────────────────────
|
|
96
99
|
/**
|
|
97
|
-
* Insert a new memory with auto-assigned ID.
|
|
100
|
+
* Insert a new memory with a race-safe auto-assigned ID.
|
|
101
|
+
* Uses AUTOINCREMENT seq to derive the ID after insert, avoiding
|
|
102
|
+
* the read-then-write race in concurrent scenarios (e.g. worktrees).
|
|
98
103
|
* Returns the assigned ID, or null on failure.
|
|
99
104
|
*/
|
|
100
105
|
export function createMemory(fields) {
|
|
@@ -104,11 +109,12 @@ export function createMemory(fields) {
|
|
|
104
109
|
if (!adapter)
|
|
105
110
|
return null;
|
|
106
111
|
try {
|
|
107
|
-
const id = nextMemoryId();
|
|
108
112
|
const now = new Date().toISOString();
|
|
113
|
+
// Insert with a temporary placeholder ID — seq is auto-assigned
|
|
114
|
+
const placeholder = `_TMP_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
109
115
|
adapter.prepare(`INSERT INTO memories (id, category, content, confidence, source_unit_type, source_unit_id, created_at, updated_at)
|
|
110
116
|
VALUES (:id, :category, :content, :confidence, :source_unit_type, :source_unit_id, :created_at, :updated_at)`).run({
|
|
111
|
-
':id':
|
|
117
|
+
':id': placeholder,
|
|
112
118
|
':category': fields.category,
|
|
113
119
|
':content': fields.content,
|
|
114
120
|
':confidence': fields.confidence ?? 0.8,
|
|
@@ -117,7 +123,17 @@ export function createMemory(fields) {
|
|
|
117
123
|
':created_at': now,
|
|
118
124
|
':updated_at': now,
|
|
119
125
|
});
|
|
120
|
-
|
|
126
|
+
// Derive the real ID from the assigned seq
|
|
127
|
+
const row = adapter.prepare('SELECT seq FROM memories WHERE id = :id').get({ ':id': placeholder });
|
|
128
|
+
if (!row)
|
|
129
|
+
return placeholder; // fallback — should not happen
|
|
130
|
+
const seq = row['seq'];
|
|
131
|
+
const realId = `MEM${String(seq).padStart(3, '0')}`;
|
|
132
|
+
adapter.prepare('UPDATE memories SET id = :real_id WHERE id = :placeholder').run({
|
|
133
|
+
':real_id': realId,
|
|
134
|
+
':placeholder': placeholder,
|
|
135
|
+
});
|
|
136
|
+
return realId;
|
|
121
137
|
}
|
|
122
138
|
catch {
|
|
123
139
|
return null;
|
|
@@ -258,15 +274,14 @@ export function enforceMemoryCap(max = 50) {
|
|
|
258
274
|
if (count <= max)
|
|
259
275
|
return;
|
|
260
276
|
const excess = count - max;
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
WHERE
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
277
|
+
// Batch update: supersede lowest-ranked active memories in a single statement
|
|
278
|
+
adapter.prepare(`UPDATE memories SET superseded_by = 'CAP_EXCEEDED', updated_at = :now
|
|
279
|
+
WHERE id IN (
|
|
280
|
+
SELECT id FROM memories
|
|
281
|
+
WHERE superseded_by IS NULL
|
|
282
|
+
ORDER BY (confidence * (1.0 + hit_count * 0.1)) ASC
|
|
283
|
+
LIMIT :limit
|
|
284
|
+
)`).run({ ':now': new Date().toISOString(), ':limit': excess });
|
|
270
285
|
}
|
|
271
286
|
catch {
|
|
272
287
|
// non-fatal
|