gsd-pi 2.63.0 → 2.64.0-dev.9c14bd0
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 +46 -134
- package/dist/cli.js +48 -6
- package/dist/headless-query.js +11 -1
- package/dist/help-text.js +4 -1
- package/dist/onboarding.js +15 -8
- package/dist/resource-loader.js +18 -3
- package/dist/resources/extensions/cmux/index.js +21 -12
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +27 -0
- package/dist/resources/extensions/gsd/auto/finalize-timeout.js +40 -0
- package/dist/resources/extensions/gsd/auto/loop.js +4 -0
- package/dist/resources/extensions/gsd/auto/phases.js +157 -22
- package/dist/resources/extensions/gsd/auto/session.js +12 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +14 -8
- package/dist/resources/extensions/gsd/auto-model-selection.js +32 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +222 -11
- package/dist/resources/extensions/gsd/auto-prompts.js +25 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +15 -7
- package/dist/resources/extensions/gsd/auto-start.js +10 -21
- package/dist/resources/extensions/gsd/auto-timers.js +2 -1
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +17 -0
- package/dist/resources/extensions/gsd/auto-verification.js +138 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +13 -7
- package/dist/resources/extensions/gsd/auto.js +24 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +147 -75
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +13 -0
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +85 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +3 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
- package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +54 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +30 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +9 -4
- package/dist/resources/extensions/gsd/constants.js +42 -0
- package/dist/resources/extensions/gsd/db-writer.js +72 -4
- package/dist/resources/extensions/gsd/forensics.js +20 -4
- package/dist/resources/extensions/gsd/gsd-db.js +64 -17
- package/dist/resources/extensions/gsd/guided-flow.js +19 -0
- package/dist/resources/extensions/gsd/metrics.js +27 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +5 -3
- package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +464 -0
- package/dist/resources/extensions/gsd/preferences-types.js +6 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +33 -0
- package/dist/resources/extensions/gsd/preferences.js +11 -2
- package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -0
- package/dist/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
- package/dist/resources/extensions/gsd/prompts/forensics.md +2 -0
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
- package/dist/resources/extensions/gsd/prompts/system.md +4 -7
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/dist/resources/extensions/gsd/roadmap-mutations.js +1 -1
- package/dist/resources/extensions/gsd/roadmap-slices.js +9 -5
- package/dist/resources/extensions/gsd/safety/content-validator.js +73 -0
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +34 -0
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +109 -0
- package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +83 -0
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +71 -0
- package/dist/resources/extensions/gsd/safety/git-checkpoint.js +91 -0
- package/dist/resources/extensions/gsd/safety/safety-harness.js +64 -0
- package/dist/resources/extensions/gsd/slice-parallel-conflict.js +67 -0
- package/dist/resources/extensions/gsd/slice-parallel-eligibility.js +51 -0
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +378 -0
- package/dist/resources/extensions/gsd/state.js +74 -14
- package/dist/resources/extensions/gsd/status-guards.js +11 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +17 -12
- package/dist/resources/extensions/gsd/tools/complete-slice.js +40 -26
- package/dist/resources/extensions/gsd/tools/complete-task.js +12 -12
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +33 -25
- package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -8
- package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +21 -5
- package/dist/resources/extensions/gsd/worktree-manager.js +82 -29
- package/dist/resources/extensions/gsd/worktree-resolver.js +4 -3
- package/dist/resources/extensions/mcp-client/auth.js +101 -0
- package/dist/resources/extensions/mcp-client/index.js +10 -1
- package/dist/resources/extensions/ollama/index.js +28 -22
- package/dist/resources/extensions/ollama/model-capabilities.js +37 -34
- package/dist/resources/extensions/ollama/ndjson-stream.js +54 -0
- package/dist/resources/extensions/ollama/ollama-chat-provider.js +380 -0
- package/dist/resources/extensions/ollama/ollama-client.js +23 -32
- package/dist/resources/extensions/ollama/ollama-discovery.js +2 -7
- package/dist/resources/extensions/ollama/ollama-tool.js +62 -0
- package/dist/resources/extensions/ollama/thinking-parser.js +104 -0
- package/dist/update-cmd.js +4 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +4 -4
- 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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +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.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.js.nft.json +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.js.nft.json +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 +15 -15
- package/dist/web/standalone/.next/server/chunks/6897.js +12 -0
- 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.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/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/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/dist/welcome-screen.js +1 -1
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts +8 -0
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +50 -0
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +221 -5
- package/packages/pi-agent-core/src/agent-loop.ts +53 -0
- package/packages/pi-ai/dist/types.d.ts +16 -1
- 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/types.ts +18 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +50 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +41 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +31 -4
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +28 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +12 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +3 -3
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +23 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +80 -56
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +53 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +66 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +39 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +34 -4
- package/packages/pi-coding-agent/src/core/extensions/provider-registration.test.ts +81 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +14 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +3 -3
- package/packages/pi-coding-agent/src/core/resource-loader.ts +89 -56
- package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/cmux/index.ts +18 -12
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +27 -0
- package/src/resources/extensions/gsd/auto/finalize-timeout.ts +46 -0
- package/src/resources/extensions/gsd/auto/loop.ts +5 -0
- package/src/resources/extensions/gsd/auto/phases.ts +194 -33
- package/src/resources/extensions/gsd/auto/session.ts +14 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +16 -7
- package/src/resources/extensions/gsd/auto-model-selection.ts +36 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +263 -12
- package/src/resources/extensions/gsd/auto-prompts.ts +21 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +9 -8
- package/src/resources/extensions/gsd/auto-start.ts +11 -20
- package/src/resources/extensions/gsd/auto-timers.ts +2 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
- package/src/resources/extensions/gsd/auto-verification.ts +190 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +14 -6
- package/src/resources/extensions/gsd/auto.ts +26 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +160 -88
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +15 -0
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +98 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +36 -1
- package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +57 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +31 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +10 -4
- package/src/resources/extensions/gsd/constants.ts +44 -0
- package/src/resources/extensions/gsd/db-writer.ts +78 -4
- package/src/resources/extensions/gsd/forensics.ts +21 -5
- package/src/resources/extensions/gsd/gsd-db.ts +64 -17
- package/src/resources/extensions/gsd/guided-flow.ts +22 -0
- package/src/resources/extensions/gsd/metrics.ts +28 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +5 -3
- package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +573 -0
- package/src/resources/extensions/gsd/preferences-types.ts +44 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +33 -0
- package/src/resources/extensions/gsd/preferences.ts +13 -2
- package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -0
- package/src/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
- package/src/resources/extensions/gsd/prompts/forensics.md +2 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
- package/src/resources/extensions/gsd/prompts/system.md +4 -7
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/src/resources/extensions/gsd/roadmap-mutations.ts +1 -1
- package/src/resources/extensions/gsd/roadmap-slices.ts +10 -5
- package/src/resources/extensions/gsd/safety/content-validator.ts +98 -0
- package/src/resources/extensions/gsd/safety/destructive-guard.ts +49 -0
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +151 -0
- package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +120 -0
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +108 -0
- package/src/resources/extensions/gsd/safety/git-checkpoint.ts +106 -0
- package/src/resources/extensions/gsd/safety/safety-harness.ts +105 -0
- package/src/resources/extensions/gsd/slice-parallel-conflict.ts +86 -0
- package/src/resources/extensions/gsd/slice-parallel-eligibility.ts +73 -0
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +477 -0
- package/src/resources/extensions/gsd/state.ts +67 -12
- package/src/resources/extensions/gsd/status-guards.ts +13 -0
- package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +288 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +34 -13
- package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/cmux.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +211 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +109 -0
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +13 -9
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +134 -0
- package/src/resources/extensions/gsd/tests/deferred-slice-dispatch.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +130 -0
- package/src/resources/extensions/gsd/tests/doctor-fix-flag.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/git-checkpoint.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/insert-slice-no-wipe.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +27 -7
- package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +116 -1
- package/src/resources/extensions/gsd/tests/milestone-status-tool.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/plan-milestone-title.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +82 -18
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +999 -0
- package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-conflict.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-eligibility.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +349 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -2
- package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +148 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +34 -20
- package/src/resources/extensions/gsd/tools/complete-slice.ts +41 -26
- package/src/resources/extensions/gsd/tools/complete-task.ts +12 -12
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +55 -30
- package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -8
- package/src/resources/extensions/gsd/types.ts +44 -22
- package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
- package/src/resources/extensions/gsd/workflow-projections.ts +23 -5
- package/src/resources/extensions/gsd/worktree-manager.ts +76 -28
- package/src/resources/extensions/gsd/worktree-resolver.ts +4 -3
- package/src/resources/extensions/mcp-client/auth.ts +149 -0
- package/src/resources/extensions/mcp-client/index.ts +16 -1
- package/src/resources/extensions/ollama/index.ts +26 -25
- package/src/resources/extensions/ollama/model-capabilities.ts +41 -34
- package/src/resources/extensions/ollama/ndjson-stream.ts +63 -0
- package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +20 -0
- package/src/resources/extensions/ollama/ollama-chat-provider.ts +459 -0
- package/src/resources/extensions/ollama/ollama-client.ts +30 -30
- package/src/resources/extensions/ollama/ollama-discovery.ts +5 -8
- package/src/resources/extensions/ollama/ollama-tool.ts +69 -0
- package/src/resources/extensions/ollama/tests/ollama-chat-provider-stream.test.ts +82 -0
- package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +0 -27
- package/src/resources/extensions/ollama/thinking-parser.ts +116 -0
- package/src/resources/extensions/ollama/types.ts +23 -0
- package/dist/web/standalone/.next/server/chunks/2229.js +0 -12
- 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/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// GSD2 — Read-only query tools exposing DB state to the LLM via the WAL connection
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
|
+
import { logWarning } from "../workflow-logger.js";
|
|
4
|
+
export function registerQueryTools(pi) {
|
|
5
|
+
pi.registerTool({
|
|
6
|
+
name: "gsd_milestone_status",
|
|
7
|
+
label: "Milestone Status",
|
|
8
|
+
description: "Read the current status of a milestone and all its slices from the GSD database. " +
|
|
9
|
+
"Returns milestone metadata, per-slice status, and task counts per slice. " +
|
|
10
|
+
"Use this instead of querying .gsd/gsd.db directly via sqlite3 or better-sqlite3.",
|
|
11
|
+
promptSnippet: "Get milestone status, slice statuses, and task counts for a given milestoneId",
|
|
12
|
+
promptGuidelines: [
|
|
13
|
+
"Use this tool — not sqlite3 or better-sqlite3 — to inspect milestone or slice state from the DB.",
|
|
14
|
+
],
|
|
15
|
+
parameters: Type.Object({
|
|
16
|
+
milestoneId: Type.String({ description: "Milestone ID to query (e.g. M001)" }),
|
|
17
|
+
}),
|
|
18
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
19
|
+
try {
|
|
20
|
+
// Strictly read-only: only use an already-open DB connection.
|
|
21
|
+
// Do NOT call ensureDbOpen() — it can create/migrate the DB as a side effect.
|
|
22
|
+
const { isDbAvailable, getMilestone, getSliceStatusSummary, getSliceTaskCounts, _getAdapter, } = await import("../gsd-db.js");
|
|
23
|
+
if (!isDbAvailable()) {
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: "text", text: "Error: GSD database is not available." }],
|
|
26
|
+
details: { operation: "milestone_status", error: "db_unavailable" },
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// Wrap all reads in a single transaction for snapshot consistency.
|
|
30
|
+
// SQLite WAL mode guarantees reads within a transaction see a single
|
|
31
|
+
// consistent snapshot, preventing torn reads from concurrent writes.
|
|
32
|
+
const adapter = _getAdapter();
|
|
33
|
+
adapter.exec("BEGIN"); // eslint-disable-line -- SQLite exec, not child_process
|
|
34
|
+
try {
|
|
35
|
+
const milestone = getMilestone(params.milestoneId);
|
|
36
|
+
if (!milestone) {
|
|
37
|
+
adapter.exec("COMMIT"); // eslint-disable-line
|
|
38
|
+
return {
|
|
39
|
+
content: [{ type: "text", text: `Milestone ${params.milestoneId} not found in database.` }],
|
|
40
|
+
details: { operation: "milestone_status", milestoneId: params.milestoneId, found: false },
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const sliceStatuses = getSliceStatusSummary(params.milestoneId);
|
|
44
|
+
const slices = sliceStatuses.map((s) => {
|
|
45
|
+
const counts = getSliceTaskCounts(params.milestoneId, s.id);
|
|
46
|
+
return {
|
|
47
|
+
id: s.id,
|
|
48
|
+
status: s.status,
|
|
49
|
+
taskCounts: counts,
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
adapter.exec("COMMIT"); // eslint-disable-line
|
|
53
|
+
const result = {
|
|
54
|
+
milestoneId: milestone.id,
|
|
55
|
+
title: milestone.title,
|
|
56
|
+
status: milestone.status,
|
|
57
|
+
createdAt: milestone.created_at,
|
|
58
|
+
completedAt: milestone.completed_at,
|
|
59
|
+
sliceCount: slices.length,
|
|
60
|
+
slices,
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
64
|
+
details: { operation: "milestone_status", milestoneId: milestone.id, sliceCount: slices.length },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (txErr) {
|
|
68
|
+
try {
|
|
69
|
+
adapter.exec("ROLLBACK");
|
|
70
|
+
}
|
|
71
|
+
catch { /* swallow */ } // eslint-disable-line
|
|
72
|
+
throw txErr;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
77
|
+
logWarning("tool", `gsd_milestone_status tool failed: ${msg}`);
|
|
78
|
+
return {
|
|
79
|
+
content: [{ type: "text", text: `Error querying milestone status: ${msg}` }],
|
|
80
|
+
details: { operation: "milestone_status", error: msg },
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
// GSD2 — Extension registration: wires all GSD tools, commands, and hooks into pi
|
|
1
2
|
import { registerGSDCommand } from "../commands.js";
|
|
2
3
|
import { registerExitCommand } from "../exit-command.js";
|
|
3
4
|
import { registerWorktreeCommand } from "../worktree-command.js";
|
|
4
5
|
import { registerDbTools } from "./db-tools.js";
|
|
5
6
|
import { registerDynamicTools } from "./dynamic-tools.js";
|
|
6
7
|
import { registerJournalTools } from "./journal-tools.js";
|
|
8
|
+
import { registerQueryTools } from "./query-tools.js";
|
|
7
9
|
import { registerHooks } from "./register-hooks.js";
|
|
8
10
|
import { registerShortcuts } from "./register-shortcuts.js";
|
|
9
11
|
export function handleRecoverableExtensionProcessError(err) {
|
|
@@ -48,6 +50,7 @@ export function registerGsdExtension(pi) {
|
|
|
48
50
|
registerDynamicTools(pi);
|
|
49
51
|
registerDbTools(pi);
|
|
50
52
|
registerJournalTools(pi);
|
|
53
|
+
registerQueryTools(pi);
|
|
51
54
|
registerShortcuts(pi);
|
|
52
55
|
registerHooks(pi);
|
|
53
56
|
}
|
|
@@ -10,11 +10,14 @@ import { getDiscussionMilestoneId } from "../guided-flow.js";
|
|
|
10
10
|
import { loadToolApiKeys } from "../commands-config.js";
|
|
11
11
|
import { loadFile, saveFile, formatContinue } from "../files.js";
|
|
12
12
|
import { deriveState } from "../state.js";
|
|
13
|
-
import { getAutoDashboardData, isAutoActive, isAutoPaused, markToolEnd, markToolStart } from "../auto.js";
|
|
13
|
+
import { getAutoDashboardData, isAutoActive, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto.js";
|
|
14
14
|
import { isParallelActive, shutdownParallel } from "../parallel-orchestrator.js";
|
|
15
15
|
import { checkToolCallLoop, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
|
|
16
16
|
import { saveActivityLog } from "../activity-log.js";
|
|
17
17
|
import { resetAskUserQuestionsCache } from "../../ask-user-questions.js";
|
|
18
|
+
import { recordToolCall as safetyRecordToolCall, recordToolResult as safetyRecordToolResult } from "../safety/evidence-collector.js";
|
|
19
|
+
import { classifyCommand } from "../safety/destructive-guard.js";
|
|
20
|
+
import { logWarning as safetyLogWarning } from "../workflow-logger.js";
|
|
18
21
|
// Skip the welcome screen on the very first session_start — cli.ts already
|
|
19
22
|
// printed it before the TUI launched. Only re-print on /clear (subsequent sessions).
|
|
20
23
|
let isFirstSession = true;
|
|
@@ -187,6 +190,22 @@ export function registerHooks(pi) {
|
|
|
187
190
|
if (result.block)
|
|
188
191
|
return result;
|
|
189
192
|
});
|
|
193
|
+
// ── Safety harness: evidence collection + destructive command warnings ──
|
|
194
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
195
|
+
if (!isAutoActive())
|
|
196
|
+
return;
|
|
197
|
+
safetyRecordToolCall(event.toolName, event.input);
|
|
198
|
+
// Destructive command classification (warn only, never block)
|
|
199
|
+
if (isToolCallEventType("bash", event)) {
|
|
200
|
+
const classification = classifyCommand(event.input.command);
|
|
201
|
+
if (classification.destructive) {
|
|
202
|
+
safetyLogWarning("safety", `destructive command: ${classification.labels.join(", ")}`, {
|
|
203
|
+
command: String(event.input.command).slice(0, 200),
|
|
204
|
+
});
|
|
205
|
+
ctx.ui.notify(`Destructive command detected: ${classification.labels.join(", ")}`, "warning");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
190
209
|
pi.on("tool_result", async (event) => {
|
|
191
210
|
if (event.toolName !== "ask_user_questions")
|
|
192
211
|
return;
|
|
@@ -243,6 +262,18 @@ export function registerHooks(pi) {
|
|
|
243
262
|
});
|
|
244
263
|
pi.on("tool_execution_end", async (event) => {
|
|
245
264
|
markToolEnd(event.toolCallId);
|
|
265
|
+
// #2883: Capture tool invocation errors (malformed/truncated JSON arguments)
|
|
266
|
+
// so postUnitPreVerification can break the retry loop instead of re-dispatching.
|
|
267
|
+
if (event.isError && event.toolName.startsWith("gsd_")) {
|
|
268
|
+
const errorText = typeof event.result === "string"
|
|
269
|
+
? event.result
|
|
270
|
+
: (typeof event.result?.content?.[0]?.text === "string" ? event.result.content[0].text : String(event.result));
|
|
271
|
+
recordToolInvocationError(event.toolName, errorText);
|
|
272
|
+
}
|
|
273
|
+
// Safety harness: record tool execution results for evidence cross-referencing
|
|
274
|
+
if (isAutoActive()) {
|
|
275
|
+
safetyRecordToolResult(event.toolCallId, event.toolName, event.result, event.isError);
|
|
276
|
+
}
|
|
246
277
|
});
|
|
247
278
|
pi.on("model_select", async (_event, ctx) => {
|
|
248
279
|
await syncServiceTierStatus(ctx);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input sanitization for gsd_complete_milestone parameters.
|
|
3
|
+
*
|
|
4
|
+
* The Claude SDK deserializes tool-call JSON before the handler runs.
|
|
5
|
+
* When an LLM (especially smaller models like haiku) generates large markdown
|
|
6
|
+
* parameters, the JSON can arrive with subtly wrong types — numbers where
|
|
7
|
+
* strings are expected, null where arrays belong, string "true" instead of
|
|
8
|
+
* boolean true, etc. This sanitizer normalizes all fields so
|
|
9
|
+
* handleCompleteMilestone never crashes on type mismatches.
|
|
10
|
+
*
|
|
11
|
+
* See: https://github.com/gsd-build/gsd-2/issues/3013
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Coerce an unknown value to a trimmed string.
|
|
15
|
+
* Returns "" for null / undefined.
|
|
16
|
+
*/
|
|
17
|
+
function toStr(v) {
|
|
18
|
+
if (v == null)
|
|
19
|
+
return "";
|
|
20
|
+
return String(v).trim();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Coerce an unknown value to an array of trimmed, non-empty strings.
|
|
24
|
+
* - If already an array, filter/trim each element.
|
|
25
|
+
* - Otherwise return [].
|
|
26
|
+
*/
|
|
27
|
+
function toStrArray(v) {
|
|
28
|
+
if (!Array.isArray(v))
|
|
29
|
+
return [];
|
|
30
|
+
return v
|
|
31
|
+
.map((item) => (item == null ? "" : String(item).trim()))
|
|
32
|
+
.filter((s) => s.length > 0);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Sanitize raw params from the tool-call framework into well-typed
|
|
36
|
+
* CompleteMilestoneParams, tolerating type mismatches from LLM JSON quirks.
|
|
37
|
+
*/
|
|
38
|
+
export function sanitizeCompleteMilestoneParams(raw) {
|
|
39
|
+
return {
|
|
40
|
+
milestoneId: toStr(raw.milestoneId),
|
|
41
|
+
title: toStr(raw.title),
|
|
42
|
+
oneLiner: toStr(raw.oneLiner),
|
|
43
|
+
narrative: toStr(raw.narrative),
|
|
44
|
+
successCriteriaResults: toStr(raw.successCriteriaResults),
|
|
45
|
+
definitionOfDoneResults: toStr(raw.definitionOfDoneResults),
|
|
46
|
+
requirementOutcomes: toStr(raw.requirementOutcomes),
|
|
47
|
+
keyDecisions: toStrArray(raw.keyDecisions),
|
|
48
|
+
keyFiles: toStrArray(raw.keyFiles),
|
|
49
|
+
lessonsLearned: toStrArray(raw.lessonsLearned),
|
|
50
|
+
followUps: toStr(raw.followUps),
|
|
51
|
+
deviations: toStr(raw.deviations),
|
|
52
|
+
verificationPassed: raw.verificationPassed === true || raw.verificationPassed === "true",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -3,9 +3,10 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { logWarning } from "../workflow-logger.js";
|
|
5
5
|
import { debugTime } from "../debug-logger.js";
|
|
6
|
-
import { loadPrompt } from "../prompt-loader.js";
|
|
6
|
+
import { loadPrompt, getTemplatesDir } from "../prompt-loader.js";
|
|
7
7
|
import { readForensicsMarker } from "../forensics.js";
|
|
8
8
|
import { resolveAllSkillReferences, renderPreferencesForSystemPrompt, loadEffectiveGSDPreferences } from "../preferences.js";
|
|
9
|
+
import { resolveSkillReference } from "../preferences-skills.js";
|
|
9
10
|
import { resolveGsdRootFile, resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTaskFiles, resolveTasksDir, relSliceFile, relSlicePath, relTaskFile } from "../paths.js";
|
|
10
11
|
import { hasSkillSnapshot, detectNewSkills, formatSkillsXml } from "../skill-discovery.js";
|
|
11
12
|
import { getActiveAutoWorktreeContext } from "../auto-worktree.js";
|
|
@@ -15,6 +16,30 @@ import { formatOverridesSection, loadActiveOverrides, loadFile, parseContinue, p
|
|
|
15
16
|
import { toPosixPath } from "../../shared/mod.js";
|
|
16
17
|
import { markCmuxPromptShown, shouldPromptToEnableCmux } from "../../cmux/index.js";
|
|
17
18
|
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
19
|
+
/**
|
|
20
|
+
* Bundled skill triggers — resolved dynamically at runtime instead of
|
|
21
|
+
* hardcoding absolute paths in the system prompt template. Only skills
|
|
22
|
+
* that actually exist on disk are included in the table. (#3575)
|
|
23
|
+
*/
|
|
24
|
+
const BUNDLED_SKILL_TRIGGERS = [
|
|
25
|
+
{ trigger: "Frontend UI - web components, pages, landing pages, dashboards, React/HTML/CSS, styling", skill: "frontend-design" },
|
|
26
|
+
{ trigger: "macOS or iOS apps - SwiftUI, Xcode, App Store", skill: "swiftui" },
|
|
27
|
+
{ trigger: "Debugging - complex bugs, failing tests, root-cause investigation after standard approaches fail", skill: "debug-like-expert" },
|
|
28
|
+
];
|
|
29
|
+
function buildBundledSkillsTable() {
|
|
30
|
+
const cwd = process.cwd();
|
|
31
|
+
const rows = [];
|
|
32
|
+
for (const { trigger, skill } of BUNDLED_SKILL_TRIGGERS) {
|
|
33
|
+
const resolution = resolveSkillReference(skill, cwd);
|
|
34
|
+
if (resolution.method === "unresolved")
|
|
35
|
+
continue; // skill not installed — omit from prompt
|
|
36
|
+
rows.push(`| ${trigger} | \`${resolution.resolvedPath}\` |`);
|
|
37
|
+
}
|
|
38
|
+
if (rows.length === 0) {
|
|
39
|
+
return "*No bundled skills found. Install skills to `~/.agents/skills/` or `~/.claude/skills/`.*";
|
|
40
|
+
}
|
|
41
|
+
return `| Trigger | Skill to load |\n|---|---|\n${rows.join("\n")}`;
|
|
42
|
+
}
|
|
18
43
|
function warnDeprecatedAgentInstructions() {
|
|
19
44
|
const paths = [
|
|
20
45
|
join(gsdHome, "agent-instructions.md"),
|
|
@@ -32,7 +57,10 @@ export async function buildBeforeAgentStartResult(event, ctx) {
|
|
|
32
57
|
if (!existsSync(join(process.cwd(), ".gsd")))
|
|
33
58
|
return undefined;
|
|
34
59
|
const stopContextTimer = debugTime("context-inject");
|
|
35
|
-
const systemContent = loadPrompt("system"
|
|
60
|
+
const systemContent = loadPrompt("system", {
|
|
61
|
+
bundledSkillsTable: buildBundledSkillsTable(),
|
|
62
|
+
templatesDir: getTemplatesDir(),
|
|
63
|
+
});
|
|
36
64
|
const loadedPreferences = loadEffectiveGSDPreferences();
|
|
37
65
|
if (shouldPromptToEnableCmux(loadedPreferences?.preferences)) {
|
|
38
66
|
markCmuxPromptShown();
|
|
@@ -27,21 +27,26 @@ export function dispatchDoctorHeal(pi, scope, reportText, structuredIssues) {
|
|
|
27
27
|
const content = `Read the following GSD workflow protocol and execute exactly.\n\n${workflow}\n\n## Your Task\n\n${prompt}`;
|
|
28
28
|
pi.sendMessage({ customType: "gsd-doctor-heal", content, display: false }, { triggerTurn: true });
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
/** Parse doctor command args into structured flags and positionals (pure, no I/O). */
|
|
31
|
+
export function parseDoctorArgs(args) {
|
|
31
32
|
const trimmed = args.trim();
|
|
32
|
-
// Extract flags before positional parsing
|
|
33
33
|
const jsonMode = trimmed.includes("--json");
|
|
34
34
|
const dryRun = trimmed.includes("--dry-run");
|
|
35
|
+
const fixFlag = trimmed.includes("--fix");
|
|
35
36
|
const includeBuild = trimmed.includes("--build");
|
|
36
37
|
const includeTests = trimmed.includes("--test");
|
|
37
|
-
const stripped = trimmed.replace(/--json|--dry-run|--build|--test/g, "").trim();
|
|
38
|
+
const stripped = trimmed.replace(/--json|--dry-run|--build|--test|--fix/g, "").trim();
|
|
38
39
|
const parts = stripped ? stripped.split(/\s+/) : [];
|
|
39
40
|
const mode = parts[0] === "fix" || parts[0] === "heal" || parts[0] === "audit" ? parts[0] : "doctor";
|
|
40
41
|
const requestedScope = mode === "doctor" ? parts[0] : parts[1];
|
|
42
|
+
return { jsonMode, dryRun, fixFlag, includeBuild, includeTests, mode, requestedScope };
|
|
43
|
+
}
|
|
44
|
+
export async function handleDoctor(args, ctx, pi) {
|
|
45
|
+
const { jsonMode, dryRun, fixFlag, includeBuild, includeTests, mode, requestedScope } = parseDoctorArgs(args);
|
|
41
46
|
const scope = await selectDoctorScope(projectRoot(), requestedScope);
|
|
42
47
|
const effectiveScope = mode === "audit" ? requestedScope : scope;
|
|
43
48
|
const report = await runGSDDoctor(projectRoot(), {
|
|
44
|
-
fix: mode === "fix" || mode === "heal" || dryRun,
|
|
49
|
+
fix: mode === "fix" || mode === "heal" || dryRun || fixFlag,
|
|
45
50
|
dryRun,
|
|
46
51
|
scope: effectiveScope,
|
|
47
52
|
includeBuild,
|
|
@@ -13,3 +13,45 @@ export const DEFAULT_BASH_TIMEOUT_SECS = 120;
|
|
|
13
13
|
export const DIR_CACHE_MAX = 200;
|
|
14
14
|
/** Max parse-cache entries before eviction. */
|
|
15
15
|
export const CACHE_MAX = 50;
|
|
16
|
+
// ─── Tool Scoping ─────────────────────────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* GSD tools allowed during discuss flows (#2949).
|
|
19
|
+
*
|
|
20
|
+
* xAI/Grok (and potentially other providers with grammar-based constrained
|
|
21
|
+
* decoding) return "Grammar is too complex" (HTTP 400) when the combined
|
|
22
|
+
* tool schemas exceed their internal grammar limit. The full GSD tool set
|
|
23
|
+
* registers ~33 tools with deeply nested schemas; discuss flows only need
|
|
24
|
+
* a small subset.
|
|
25
|
+
*
|
|
26
|
+
* By scoping tools to this allowlist during discuss dispatches, the grammar
|
|
27
|
+
* sent to the provider stays well under provider limits.
|
|
28
|
+
*
|
|
29
|
+
* Included tools and why:
|
|
30
|
+
* - gsd_summary_save: writes CONTEXT.md artifacts (all discuss prompts)
|
|
31
|
+
* - gsd_save_summary: alias for above
|
|
32
|
+
* - gsd_decision_save: records decisions (discuss.md output phase)
|
|
33
|
+
* - gsd_save_decision: alias for above
|
|
34
|
+
* - gsd_plan_milestone: writes roadmap (discuss.md single/multi milestone)
|
|
35
|
+
* - gsd_milestone_plan: alias for above
|
|
36
|
+
* - gsd_milestone_generate_id: generates milestone IDs (discuss.md multi-milestone)
|
|
37
|
+
* - gsd_generate_milestone_id: alias for above
|
|
38
|
+
* - gsd_requirement_update: updates requirements during discuss
|
|
39
|
+
* - gsd_update_requirement: alias for above
|
|
40
|
+
*/
|
|
41
|
+
export const DISCUSS_TOOLS_ALLOWLIST = [
|
|
42
|
+
// Context / summary writing
|
|
43
|
+
"gsd_summary_save",
|
|
44
|
+
"gsd_save_summary",
|
|
45
|
+
// Decision recording
|
|
46
|
+
"gsd_decision_save",
|
|
47
|
+
"gsd_save_decision",
|
|
48
|
+
// Milestone planning (needed for discuss.md output phase)
|
|
49
|
+
"gsd_plan_milestone",
|
|
50
|
+
"gsd_milestone_plan",
|
|
51
|
+
// Milestone ID generation (multi-milestone flow)
|
|
52
|
+
"gsd_milestone_generate_id",
|
|
53
|
+
"gsd_generate_milestone_id",
|
|
54
|
+
// Requirement updates
|
|
55
|
+
"gsd_requirement_update",
|
|
56
|
+
"gsd_update_requirement",
|
|
57
|
+
];
|
|
@@ -402,6 +402,23 @@ export async function saveDecisionToDb(fields, basePath) {
|
|
|
402
402
|
adapter?.prepare('DELETE FROM decisions WHERE id = :id').run({ ':id': id });
|
|
403
403
|
throw diskErr;
|
|
404
404
|
}
|
|
405
|
+
// #2661: When a decision defers a slice, update the slice status in the DB
|
|
406
|
+
// so the dispatcher skips it. Without this, STATE.md and DECISIONS.md are
|
|
407
|
+
// in split-brain: the decision says "deferred" but the state still says
|
|
408
|
+
// "active", causing auto-mode to keep dispatching the deferred work.
|
|
409
|
+
try {
|
|
410
|
+
const sliceRef = extractDeferredSliceRef(fields);
|
|
411
|
+
if (sliceRef) {
|
|
412
|
+
db.updateSliceStatus(sliceRef.milestoneId, sliceRef.sliceId, 'deferred');
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
catch (deferErr) {
|
|
416
|
+
// Non-fatal — log but don't fail the decision save
|
|
417
|
+
logError('manifest', 'failed to update deferred slice status', {
|
|
418
|
+
fn: 'saveDecisionToDb',
|
|
419
|
+
error: String(deferErr.message),
|
|
420
|
+
});
|
|
421
|
+
}
|
|
405
422
|
// Invalidate file-read caches so deriveState() sees the updated markdown.
|
|
406
423
|
// Do NOT clear the artifacts table — we just wrote to it intentionally.
|
|
407
424
|
invalidateStateCache();
|
|
@@ -414,6 +431,33 @@ export async function saveDecisionToDb(fields, basePath) {
|
|
|
414
431
|
throw err;
|
|
415
432
|
}
|
|
416
433
|
}
|
|
434
|
+
/**
|
|
435
|
+
* Extract a milestone/slice reference from a deferral decision.
|
|
436
|
+
*
|
|
437
|
+
* Detects deferrals by checking:
|
|
438
|
+
* - scope contains "defer" (e.g., "deferral", "defer")
|
|
439
|
+
* - choice or decision contains "defer" + an M###/S## pattern
|
|
440
|
+
*
|
|
441
|
+
* Returns { milestoneId, sliceId } if found, null otherwise.
|
|
442
|
+
*/
|
|
443
|
+
export function extractDeferredSliceRef(fields) {
|
|
444
|
+
const isDeferral = /\bdefer(?:ral|red|ring|s)?\b/i.test(fields.scope) ||
|
|
445
|
+
/\bdefer(?:ral|red|ring|s)?\b/i.test(fields.choice) ||
|
|
446
|
+
/\bdefer(?:ral|red|ring|s)?\b/i.test(fields.decision);
|
|
447
|
+
if (!isDeferral)
|
|
448
|
+
return null;
|
|
449
|
+
// Look for M###/S## pattern in choice first, then decision
|
|
450
|
+
const slicePattern = /\b(M\d{3,4})\/(S\d{2,3})\b/;
|
|
451
|
+
const choiceMatch = fields.choice.match(slicePattern);
|
|
452
|
+
if (choiceMatch) {
|
|
453
|
+
return { milestoneId: choiceMatch[1], sliceId: choiceMatch[2] };
|
|
454
|
+
}
|
|
455
|
+
const decisionMatch = fields.decision.match(slicePattern);
|
|
456
|
+
if (decisionMatch) {
|
|
457
|
+
return { milestoneId: decisionMatch[1], sliceId: decisionMatch[2] };
|
|
458
|
+
}
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
417
461
|
// ─── Update Requirement in DB + Regenerate Markdown ───────────────────────
|
|
418
462
|
/**
|
|
419
463
|
* Update a requirement in DB and regenerate REQUIREMENTS.md.
|
|
@@ -422,10 +466,34 @@ export async function saveDecisionToDb(fields, basePath) {
|
|
|
422
466
|
export async function updateRequirementInDb(id, updates, basePath) {
|
|
423
467
|
try {
|
|
424
468
|
const db = await import('./gsd-db.js');
|
|
425
|
-
|
|
426
|
-
// If requirement doesn't exist in DB,
|
|
427
|
-
//
|
|
428
|
-
//
|
|
469
|
+
let existing = db.getRequirementById(id);
|
|
470
|
+
// If requirement doesn't exist in DB, seed the entire requirements table
|
|
471
|
+
// from REQUIREMENTS.md first (#3346). This handles the standard workflow
|
|
472
|
+
// where requirements are authored in markdown during discussion but never
|
|
473
|
+
// imported into the database — making gsd_requirement_update always fail
|
|
474
|
+
// with "not_found" at milestone completion.
|
|
475
|
+
if (!existing) {
|
|
476
|
+
const reqFilePath = resolveGsdRootFile(basePath, 'REQUIREMENTS');
|
|
477
|
+
try {
|
|
478
|
+
const content = readFileSync(reqFilePath, 'utf-8');
|
|
479
|
+
const { parseRequirementsSections } = await import('./md-importer.js');
|
|
480
|
+
const parsed = parseRequirementsSections(content);
|
|
481
|
+
if (parsed.length > 0) {
|
|
482
|
+
logWarning('manifest', `Seeding ${parsed.length} requirements from REQUIREMENTS.md into DB (first update triggers import)`, { fn: 'updateRequirementInDb' });
|
|
483
|
+
for (const req of parsed) {
|
|
484
|
+
// Only seed if not already in DB (avoid overwriting concurrent inserts)
|
|
485
|
+
if (!db.getRequirementById(req.id)) {
|
|
486
|
+
db.upsertRequirement(req);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Re-check after seeding
|
|
490
|
+
existing = db.getRequirementById(id);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
// REQUIREMENTS.md missing or unparseable — fall through to skeleton
|
|
495
|
+
}
|
|
496
|
+
}
|
|
429
497
|
const base = existing ?? {
|
|
430
498
|
id,
|
|
431
499
|
class: '',
|
|
@@ -498,13 +498,29 @@ function getDbCompletionCounts() {
|
|
|
498
498
|
};
|
|
499
499
|
}
|
|
500
500
|
// ─── Anomaly Detectors ───────────────────────────────────────────────────────
|
|
501
|
-
|
|
502
|
-
|
|
501
|
+
/**
|
|
502
|
+
* Detect units that were dispatched multiple times (stuck in a loop).
|
|
503
|
+
*
|
|
504
|
+
* Counts distinct dispatches by grouping on (type, id, startedAt) first to
|
|
505
|
+
* collapse idle-watchdog duplicate snapshots (#1943), then counts unique
|
|
506
|
+
* startedAt values per type/id to determine actual dispatch count.
|
|
507
|
+
*
|
|
508
|
+
* Exported for testability.
|
|
509
|
+
*/
|
|
510
|
+
export function detectStuckLoops(units, anomalies) {
|
|
511
|
+
// First, collect unique startedAt values per type/id key
|
|
512
|
+
const dispatchMap = new Map();
|
|
503
513
|
for (const u of units) {
|
|
504
514
|
const key = `${u.type}/${u.id}`;
|
|
505
|
-
|
|
515
|
+
let starts = dispatchMap.get(key);
|
|
516
|
+
if (!starts) {
|
|
517
|
+
starts = new Set();
|
|
518
|
+
dispatchMap.set(key, starts);
|
|
519
|
+
}
|
|
520
|
+
starts.add(u.startedAt);
|
|
506
521
|
}
|
|
507
|
-
for (const [key,
|
|
522
|
+
for (const [key, starts] of dispatchMap) {
|
|
523
|
+
const count = starts.size;
|
|
508
524
|
if (count > 1) {
|
|
509
525
|
const [unitType, ...idParts] = key.split("/");
|
|
510
526
|
anomalies.push({
|
|
@@ -1023,11 +1023,12 @@ export function insertMilestone(m) {
|
|
|
1023
1023
|
":boundary_map_markdown": m.planning?.boundaryMapMarkdown ?? "",
|
|
1024
1024
|
});
|
|
1025
1025
|
}
|
|
1026
|
-
export function upsertMilestonePlanning(milestoneId, planning
|
|
1026
|
+
export function upsertMilestonePlanning(milestoneId, planning) {
|
|
1027
1027
|
if (!currentDb)
|
|
1028
1028
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1029
1029
|
currentDb.prepare(`UPDATE milestones SET
|
|
1030
|
-
title = COALESCE(:title, title),
|
|
1030
|
+
title = COALESCE(NULLIF(:title, ''), title),
|
|
1031
|
+
status = COALESCE(NULLIF(:status, ''), status),
|
|
1031
1032
|
vision = COALESCE(:vision, vision),
|
|
1032
1033
|
success_criteria = COALESCE(:success_criteria, success_criteria),
|
|
1033
1034
|
key_risks = COALESCE(:key_risks, key_risks),
|
|
@@ -1041,7 +1042,8 @@ export function upsertMilestonePlanning(milestoneId, planning, title) {
|
|
|
1041
1042
|
boundary_map_markdown = COALESCE(:boundary_map_markdown, boundary_map_markdown)
|
|
1042
1043
|
WHERE id = :id`).run({
|
|
1043
1044
|
":id": milestoneId,
|
|
1044
|
-
":title": title ??
|
|
1045
|
+
":title": planning.title ?? "",
|
|
1046
|
+
":status": planning.status ?? "",
|
|
1045
1047
|
":vision": planning.vision ?? null,
|
|
1046
1048
|
":success_criteria": planning.successCriteria ? JSON.stringify(planning.successCriteria) : null,
|
|
1047
1049
|
":key_risks": planning.keyRisks ? JSON.stringify(planning.keyRisks) : null,
|
|
@@ -1058,13 +1060,25 @@ export function upsertMilestonePlanning(milestoneId, planning, title) {
|
|
|
1058
1060
|
export function insertSlice(s) {
|
|
1059
1061
|
if (!currentDb)
|
|
1060
1062
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1061
|
-
currentDb.prepare(`INSERT
|
|
1063
|
+
currentDb.prepare(`INSERT INTO slices (
|
|
1062
1064
|
milestone_id, id, title, status, risk, depends, demo, created_at,
|
|
1063
1065
|
goal, success_criteria, proof_level, integration_closure, observability_impact, sequence
|
|
1064
1066
|
) VALUES (
|
|
1065
1067
|
:milestone_id, :id, :title, :status, :risk, :depends, :demo, :created_at,
|
|
1066
1068
|
:goal, :success_criteria, :proof_level, :integration_closure, :observability_impact, :sequence
|
|
1067
|
-
)
|
|
1069
|
+
)
|
|
1070
|
+
ON CONFLICT (milestone_id, id) DO UPDATE SET
|
|
1071
|
+
title = CASE WHEN :raw_title IS NOT NULL THEN excluded.title ELSE slices.title END,
|
|
1072
|
+
status = CASE WHEN slices.status IN ('complete', 'done') THEN slices.status ELSE excluded.status END,
|
|
1073
|
+
risk = CASE WHEN :raw_risk IS NOT NULL THEN excluded.risk ELSE slices.risk END,
|
|
1074
|
+
depends = excluded.depends,
|
|
1075
|
+
demo = CASE WHEN :raw_demo IS NOT NULL THEN excluded.demo ELSE slices.demo END,
|
|
1076
|
+
goal = CASE WHEN :raw_goal IS NOT NULL THEN excluded.goal ELSE slices.goal END,
|
|
1077
|
+
success_criteria = CASE WHEN :raw_success_criteria IS NOT NULL THEN excluded.success_criteria ELSE slices.success_criteria END,
|
|
1078
|
+
proof_level = CASE WHEN :raw_proof_level IS NOT NULL THEN excluded.proof_level ELSE slices.proof_level END,
|
|
1079
|
+
integration_closure = CASE WHEN :raw_integration_closure IS NOT NULL THEN excluded.integration_closure ELSE slices.integration_closure END,
|
|
1080
|
+
observability_impact = CASE WHEN :raw_observability_impact IS NOT NULL THEN excluded.observability_impact ELSE slices.observability_impact END,
|
|
1081
|
+
sequence = CASE WHEN :raw_sequence IS NOT NULL THEN excluded.sequence ELSE slices.sequence END`).run({
|
|
1068
1082
|
":milestone_id": s.milestoneId,
|
|
1069
1083
|
":id": s.id,
|
|
1070
1084
|
":title": s.title ?? "",
|
|
@@ -1079,6 +1093,16 @@ export function insertSlice(s) {
|
|
|
1079
1093
|
":integration_closure": s.planning?.integrationClosure ?? "",
|
|
1080
1094
|
":observability_impact": s.planning?.observabilityImpact ?? "",
|
|
1081
1095
|
":sequence": s.sequence ?? 0,
|
|
1096
|
+
// Raw sentinel params: NULL when caller omitted the field, used in ON CONFLICT guards
|
|
1097
|
+
":raw_title": s.title ?? null,
|
|
1098
|
+
":raw_risk": s.risk ?? null,
|
|
1099
|
+
":raw_demo": s.demo ?? null,
|
|
1100
|
+
":raw_goal": s.planning?.goal ?? null,
|
|
1101
|
+
":raw_success_criteria": s.planning?.successCriteria ?? null,
|
|
1102
|
+
":raw_proof_level": s.planning?.proofLevel ?? null,
|
|
1103
|
+
":raw_integration_closure": s.planning?.integrationClosure ?? null,
|
|
1104
|
+
":raw_observability_impact": s.planning?.observabilityImpact ?? null,
|
|
1105
|
+
":raw_sequence": s.sequence ?? null,
|
|
1082
1106
|
});
|
|
1083
1107
|
}
|
|
1084
1108
|
export function upsertSlicePlanning(milestoneId, sliceId, planning) {
|
|
@@ -1574,19 +1598,31 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
|
|
|
1574
1598
|
definition_of_done, requirement_coverage, boundary_map_markdown
|
|
1575
1599
|
FROM wt.milestones
|
|
1576
1600
|
`).run());
|
|
1577
|
-
// Merge slices — preserve worktree progress
|
|
1601
|
+
// Merge slices — preserve worktree progress but never downgrade completed status (#2558).
|
|
1602
|
+
// Uses INSERT OR REPLACE with a subquery that picks the best status — if the main DB
|
|
1603
|
+
// already has a completed slice, keep that status even if the worktree copy is stale.
|
|
1578
1604
|
merged.slices = countChanges(adapter.prepare(`
|
|
1579
1605
|
INSERT OR REPLACE INTO slices (
|
|
1580
1606
|
milestone_id, id, title, status, risk, depends, demo, created_at, completed_at,
|
|
1581
1607
|
full_summary_md, full_uat_md, goal, success_criteria, proof_level,
|
|
1582
1608
|
integration_closure, observability_impact, sequence, replan_triggered_at
|
|
1583
1609
|
)
|
|
1584
|
-
SELECT milestone_id, id, title,
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1610
|
+
SELECT w.milestone_id, w.id, w.title,
|
|
1611
|
+
CASE
|
|
1612
|
+
WHEN m.status IN ('complete', 'done') AND w.status NOT IN ('complete', 'done')
|
|
1613
|
+
THEN m.status ELSE w.status
|
|
1614
|
+
END,
|
|
1615
|
+
w.risk, w.depends, w.demo, w.created_at,
|
|
1616
|
+
CASE
|
|
1617
|
+
WHEN m.status IN ('complete', 'done') AND w.status NOT IN ('complete', 'done')
|
|
1618
|
+
THEN m.completed_at ELSE w.completed_at
|
|
1619
|
+
END,
|
|
1620
|
+
w.full_summary_md, w.full_uat_md, w.goal, w.success_criteria, w.proof_level,
|
|
1621
|
+
w.integration_closure, w.observability_impact, w.sequence, w.replan_triggered_at
|
|
1622
|
+
FROM wt.slices w
|
|
1623
|
+
LEFT JOIN slices m ON m.milestone_id = w.milestone_id AND m.id = w.id
|
|
1588
1624
|
`).run());
|
|
1589
|
-
// Merge tasks — preserve execution results, status
|
|
1625
|
+
// Merge tasks — preserve execution results, never downgrade completed status (#2558)
|
|
1590
1626
|
merged.tasks = countChanges(adapter.prepare(`
|
|
1591
1627
|
INSERT OR REPLACE INTO tasks (
|
|
1592
1628
|
milestone_id, slice_id, id, title, status, one_liner, narrative,
|
|
@@ -1595,12 +1631,23 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
|
|
|
1595
1631
|
description, estimate, files, verify, inputs, expected_output,
|
|
1596
1632
|
observability_impact, full_plan_md, sequence
|
|
1597
1633
|
)
|
|
1598
|
-
SELECT milestone_id, slice_id, id, title,
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1634
|
+
SELECT w.milestone_id, w.slice_id, w.id, w.title,
|
|
1635
|
+
CASE
|
|
1636
|
+
WHEN m.status IN ('complete', 'done') AND w.status NOT IN ('complete', 'done')
|
|
1637
|
+
THEN m.status ELSE w.status
|
|
1638
|
+
END,
|
|
1639
|
+
w.one_liner, w.narrative,
|
|
1640
|
+
w.verification_result, w.duration,
|
|
1641
|
+
CASE
|
|
1642
|
+
WHEN m.status IN ('complete', 'done') AND w.status NOT IN ('complete', 'done')
|
|
1643
|
+
THEN m.completed_at ELSE w.completed_at
|
|
1644
|
+
END,
|
|
1645
|
+
w.blocker_discovered,
|
|
1646
|
+
w.deviations, w.known_issues, w.key_files, w.key_decisions, w.full_summary_md,
|
|
1647
|
+
w.description, w.estimate, w.files, w.verify, w.inputs, w.expected_output,
|
|
1648
|
+
w.observability_impact, w.full_plan_md, w.sequence
|
|
1649
|
+
FROM wt.tasks w
|
|
1650
|
+
LEFT JOIN tasks m ON m.milestone_id = w.milestone_id AND m.slice_id = w.slice_id AND m.id = w.id
|
|
1604
1651
|
`).run());
|
|
1605
1652
|
// Merge memories — keep worktree-learned insights
|
|
1606
1653
|
merged.memories = countChanges(adapter.prepare(`
|