gsd-pi 2.62.1 → 2.63.0-dev.351157b
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 +44 -6
- 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/finalize-timeout.js +40 -0
- package/dist/resources/extensions/gsd/auto/loop.js +12 -1
- package/dist/resources/extensions/gsd/auto/phases.js +133 -25
- package/dist/resources/extensions/gsd/auto/session.js +8 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +9 -3
- package/dist/resources/extensions/gsd/auto-post-unit.js +47 -10
- 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-tool-tracking.js +17 -0
- package/dist/resources/extensions/gsd/auto-verification.js +14 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +13 -7
- package/dist/resources/extensions/gsd/auto.js +19 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +73 -60
- 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 +9 -1
- package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +54 -0
- 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/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences.js +7 -2
- 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 +1 -0
- 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/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 +75 -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 +43 -29
- 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/workflow-logger.js +13 -8
- package/dist/resources/extensions/gsd/workflow-projections.js +21 -5
- package/dist/resources/extensions/gsd/workflow-reconcile.js +3 -1
- 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 +6 -12
- 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/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/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/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 +1 -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 +10 -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/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +2 -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 +11 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/cmux/index.ts +18 -12
- package/src/resources/extensions/gsd/auto/finalize-timeout.ts +46 -0
- package/src/resources/extensions/gsd/auto/loop.ts +13 -1
- package/src/resources/extensions/gsd/auto/phases.ts +164 -40
- package/src/resources/extensions/gsd/auto/session.ts +9 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +11 -3
- package/src/resources/extensions/gsd/auto-post-unit.ts +56 -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-tool-tracking.ts +19 -0
- package/src/resources/extensions/gsd/auto-verification.ts +14 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +14 -6
- package/src/resources/extensions/gsd/auto.ts +22 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +74 -60
- 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 +9 -1
- package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +57 -0
- 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/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences.ts +9 -2
- 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 +1 -0
- 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/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 +68 -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/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-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/finalize-timeout-guard.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +103 -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/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/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/workflow-logger.test.ts +17 -41
- 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 +44 -31
- 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/workflow-logger.ts +13 -8
- package/src/resources/extensions/gsd/workflow-projections.ts +23 -5
- package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -1
- 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 +6 -14
- 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-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-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/{86gWhNPP3233lZ7KPwda7 → QmuF-eAbuU_2MQ03t38qr}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{86gWhNPP3233lZ7KPwda7 → QmuF-eAbuU_2MQ03t38qr}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worktree-teardown-safety.test.ts — Regression test for #2365.
|
|
3
|
+
*
|
|
4
|
+
* Ensures that removeWorktree() and teardownAutoWorktree() never delete
|
|
5
|
+
* directories outside .gsd/worktrees/. The bug: removeWorktree overrides
|
|
6
|
+
* the computed worktree path with whatever `git worktree list` reports.
|
|
7
|
+
* When .gsd/ was (or is) a symlink, git resolves the symlink at creation
|
|
8
|
+
* time, so its registered path can point to an external directory. If that
|
|
9
|
+
* external path happens to be a project data directory, teardown destroys it.
|
|
10
|
+
*
|
|
11
|
+
* The fix adds path validation so rmSync / nativeWorktreeRemove only operate
|
|
12
|
+
* on paths that are actually under .gsd/worktrees/.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
mkdtempSync,
|
|
17
|
+
mkdirSync,
|
|
18
|
+
writeFileSync,
|
|
19
|
+
rmSync,
|
|
20
|
+
existsSync,
|
|
21
|
+
realpathSync,
|
|
22
|
+
readFileSync,
|
|
23
|
+
} from "node:fs";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
import { tmpdir } from "node:os";
|
|
26
|
+
import { execSync } from "node:child_process";
|
|
27
|
+
import { describe, it, after } from "node:test";
|
|
28
|
+
|
|
29
|
+
import { createWorktree, removeWorktree, worktreePath, isInsideWorktreesDir } from "../worktree-manager.ts";
|
|
30
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
31
|
+
|
|
32
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
33
|
+
|
|
34
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
function run(command: string, cwd: string): string {
|
|
37
|
+
return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createTempRepo(): string {
|
|
41
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), "wt-safety-test-")));
|
|
42
|
+
run("git init", dir);
|
|
43
|
+
run("git config user.email test@test.com", dir);
|
|
44
|
+
run("git config user.name Test", dir);
|
|
45
|
+
writeFileSync(join(dir, "README.md"), "# test\n");
|
|
46
|
+
run("git add .", dir);
|
|
47
|
+
run("git commit -m init", dir);
|
|
48
|
+
run("git branch -M main", dir);
|
|
49
|
+
return dir;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ─── Tests ────────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
describe("worktree-teardown-safety", () => {
|
|
55
|
+
const dirs: string[] = [];
|
|
56
|
+
|
|
57
|
+
after(() => {
|
|
58
|
+
for (const d of dirs) rmSync(d, { recursive: true, force: true });
|
|
59
|
+
report();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("removeWorktree does not delete sibling data directories", () => {
|
|
63
|
+
const tempDir = createTempRepo();
|
|
64
|
+
dirs.push(tempDir);
|
|
65
|
+
|
|
66
|
+
// Create a project data directory that lives alongside .gsd/
|
|
67
|
+
const dataDir = join(tempDir, "project-data");
|
|
68
|
+
mkdirSync(dataDir, { recursive: true });
|
|
69
|
+
writeFileSync(join(dataDir, "important.db"), "precious data");
|
|
70
|
+
|
|
71
|
+
// Create a worktree normally
|
|
72
|
+
const wt = createWorktree(tempDir, "test-wt");
|
|
73
|
+
assertTrue(existsSync(wt.path), "worktree created successfully");
|
|
74
|
+
|
|
75
|
+
// Remove the worktree
|
|
76
|
+
removeWorktree(tempDir, "test-wt");
|
|
77
|
+
|
|
78
|
+
// The worktree directory should be gone
|
|
79
|
+
assertTrue(!existsSync(wt.path), "worktree directory removed");
|
|
80
|
+
|
|
81
|
+
// The project data directory MUST still exist
|
|
82
|
+
assertTrue(existsSync(dataDir), "project data directory survives teardown");
|
|
83
|
+
assertTrue(
|
|
84
|
+
existsSync(join(dataDir, "important.db")),
|
|
85
|
+
"project data files survive teardown",
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("path validation rejects paths outside .gsd/worktrees/", () => {
|
|
90
|
+
const tempDir = createTempRepo();
|
|
91
|
+
dirs.push(tempDir);
|
|
92
|
+
|
|
93
|
+
const externalDir = join(tempDir, "external-state");
|
|
94
|
+
mkdirSync(externalDir, { recursive: true });
|
|
95
|
+
writeFileSync(join(externalDir, "state.json"), '{"critical": true}');
|
|
96
|
+
|
|
97
|
+
// Create and then remove a worktree that has a legitimate path
|
|
98
|
+
const wt2 = createWorktree(tempDir, "safe-wt");
|
|
99
|
+
assertTrue(existsSync(wt2.path), "second worktree created");
|
|
100
|
+
|
|
101
|
+
removeWorktree(tempDir, "safe-wt");
|
|
102
|
+
assertTrue(!existsSync(wt2.path), "second worktree removed cleanly");
|
|
103
|
+
|
|
104
|
+
// External directory must be untouched
|
|
105
|
+
assertTrue(existsSync(externalDir), "external directory survives second teardown");
|
|
106
|
+
assertEq(
|
|
107
|
+
readFileSync(join(externalDir, "state.json"), "utf-8"),
|
|
108
|
+
'{"critical": true}',
|
|
109
|
+
"external directory contents intact after teardown",
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("worktreePath always returns paths under .gsd/worktrees/", () => {
|
|
114
|
+
const tempDir = createTempRepo();
|
|
115
|
+
dirs.push(tempDir);
|
|
116
|
+
|
|
117
|
+
const wtPathResult = worktreePath(tempDir, "anything");
|
|
118
|
+
assertTrue(
|
|
119
|
+
wtPathResult.startsWith(join(tempDir, ".gsd", "worktrees")),
|
|
120
|
+
"worktreePath returns path under .gsd/worktrees/",
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("isInsideWorktreesDir rejects path traversal attempts", () => {
|
|
125
|
+
const tempDir = createTempRepo();
|
|
126
|
+
dirs.push(tempDir);
|
|
127
|
+
|
|
128
|
+
assertTrue(
|
|
129
|
+
isInsideWorktreesDir(tempDir, join(tempDir, ".gsd", "worktrees", "my-wt")),
|
|
130
|
+
"path inside .gsd/worktrees/ is accepted",
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
assertTrue(
|
|
134
|
+
!isInsideWorktreesDir(tempDir, join(tempDir, "project-data")),
|
|
135
|
+
"path outside .gsd/worktrees/ is rejected",
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
assertTrue(
|
|
139
|
+
!isInsideWorktreesDir(tempDir, join(tempDir, ".gsd", "worktrees", "..", "..", "project-data")),
|
|
140
|
+
"path traversal via .. is rejected",
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
assertTrue(
|
|
144
|
+
!isInsideWorktreesDir(tempDir, "/tmp/some-other-dir"),
|
|
145
|
+
"completely external path is rejected",
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -20,7 +20,7 @@ import { resolveMilestonePath, clearPathCache } from "../paths.js";
|
|
|
20
20
|
import { isClosedStatus } from "../status-guards.js";
|
|
21
21
|
import { saveFile, clearParseCache } from "../files.js";
|
|
22
22
|
import { invalidateStateCache } from "../state.js";
|
|
23
|
-
import { renderAllProjections } from "../workflow-projections.js";
|
|
23
|
+
import { renderAllProjections, stripIdPrefix } from "../workflow-projections.js";
|
|
24
24
|
import { writeManifest } from "../workflow-manifest.js";
|
|
25
25
|
import { appendEvent } from "../workflow-events.js";
|
|
26
26
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -30,15 +30,23 @@ export interface CompleteMilestoneParams {
|
|
|
30
30
|
title: string;
|
|
31
31
|
oneLiner: string;
|
|
32
32
|
narrative: string;
|
|
33
|
-
successCriteriaResults: string;
|
|
34
|
-
definitionOfDoneResults: string;
|
|
35
|
-
requirementOutcomes: string;
|
|
36
|
-
keyDecisions: string[];
|
|
37
|
-
keyFiles: string[];
|
|
38
|
-
lessonsLearned: string[];
|
|
39
|
-
followUps: string;
|
|
40
|
-
deviations: string;
|
|
41
33
|
verificationPassed: boolean;
|
|
34
|
+
/** @optional — defaults to "Not provided." when omitted by models with limited tool-calling */
|
|
35
|
+
successCriteriaResults?: string;
|
|
36
|
+
/** @optional — defaults to "Not provided." when omitted */
|
|
37
|
+
definitionOfDoneResults?: string;
|
|
38
|
+
/** @optional — defaults to "Not provided." when omitted */
|
|
39
|
+
requirementOutcomes?: string;
|
|
40
|
+
/** @optional — defaults to [] when omitted */
|
|
41
|
+
keyDecisions?: string[];
|
|
42
|
+
/** @optional — defaults to [] when omitted */
|
|
43
|
+
keyFiles?: string[];
|
|
44
|
+
/** @optional — defaults to [] when omitted */
|
|
45
|
+
lessonsLearned?: string[];
|
|
46
|
+
/** @optional — defaults to "None." when omitted */
|
|
47
|
+
followUps?: string;
|
|
48
|
+
/** @optional — defaults to "None." when omitted */
|
|
49
|
+
deviations?: string;
|
|
42
50
|
/** Optional caller-provided identity for audit trail */
|
|
43
51
|
actorName?: string;
|
|
44
52
|
/** Optional caller-provided reason this action was triggered */
|
|
@@ -52,22 +60,28 @@ export interface CompleteMilestoneResult {
|
|
|
52
60
|
|
|
53
61
|
function renderMilestoneSummaryMarkdown(params: CompleteMilestoneParams): string {
|
|
54
62
|
const now = new Date().toISOString();
|
|
63
|
+
const displayTitle = stripIdPrefix(params.title, params.milestoneId);
|
|
55
64
|
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
// Apply defaults for optional enrichment fields (#2771)
|
|
66
|
+
const keyDecisions = params.keyDecisions ?? [];
|
|
67
|
+
const keyFiles = params.keyFiles ?? [];
|
|
68
|
+
const lessonsLearned = params.lessonsLearned ?? [];
|
|
69
|
+
|
|
70
|
+
const keyDecisionsYaml = keyDecisions.length > 0
|
|
71
|
+
? keyDecisions.map(d => ` - ${d}`).join("\n")
|
|
58
72
|
: " - (none)";
|
|
59
73
|
|
|
60
|
-
const keyFilesYaml =
|
|
61
|
-
?
|
|
74
|
+
const keyFilesYaml = keyFiles.length > 0
|
|
75
|
+
? keyFiles.map(f => ` - ${f}`).join("\n")
|
|
62
76
|
: " - (none)";
|
|
63
77
|
|
|
64
|
-
const lessonsYaml =
|
|
65
|
-
?
|
|
78
|
+
const lessonsYaml = lessonsLearned.length > 0
|
|
79
|
+
? lessonsLearned.map(l => ` - ${l}`).join("\n")
|
|
66
80
|
: " - (none)";
|
|
67
81
|
|
|
68
82
|
return `---
|
|
69
83
|
id: ${params.milestoneId}
|
|
70
|
-
title: "${
|
|
84
|
+
title: "${displayTitle}"
|
|
71
85
|
status: complete
|
|
72
86
|
completed_at: ${now}
|
|
73
87
|
key_decisions:
|
|
@@ -78,7 +92,7 @@ lessons_learned:
|
|
|
78
92
|
${lessonsYaml}
|
|
79
93
|
---
|
|
80
94
|
|
|
81
|
-
# ${params.milestoneId}: ${
|
|
95
|
+
# ${params.milestoneId}: ${displayTitle}
|
|
82
96
|
|
|
83
97
|
**${params.oneLiner}**
|
|
84
98
|
|
|
@@ -88,15 +102,15 @@ ${params.narrative}
|
|
|
88
102
|
|
|
89
103
|
## Success Criteria Results
|
|
90
104
|
|
|
91
|
-
${params.successCriteriaResults}
|
|
105
|
+
${params.successCriteriaResults ?? "Not provided."}
|
|
92
106
|
|
|
93
107
|
## Definition of Done Results
|
|
94
108
|
|
|
95
|
-
${params.definitionOfDoneResults}
|
|
109
|
+
${params.definitionOfDoneResults ?? "Not provided."}
|
|
96
110
|
|
|
97
111
|
## Requirement Outcomes
|
|
98
112
|
|
|
99
|
-
${params.requirementOutcomes}
|
|
113
|
+
${params.requirementOutcomes ?? "Not provided."}
|
|
100
114
|
|
|
101
115
|
## Deviations
|
|
102
116
|
|
|
@@ -46,58 +46,73 @@ export interface CompleteSliceResult {
|
|
|
46
46
|
function renderSliceSummaryMarkdown(params: CompleteSliceParams): string {
|
|
47
47
|
const now = new Date().toISOString();
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
// Apply defaults for optional enrichment arrays (#2771)
|
|
50
|
+
const provides = params.provides ?? [];
|
|
51
|
+
const requires = params.requires ?? [];
|
|
52
|
+
const affects = params.affects ?? [];
|
|
53
|
+
const keyFiles = params.keyFiles ?? [];
|
|
54
|
+
const keyDecisions = params.keyDecisions ?? [];
|
|
55
|
+
const patternsEstablished = params.patternsEstablished ?? [];
|
|
56
|
+
const observabilitySurfaces = params.observabilitySurfaces ?? [];
|
|
57
|
+
const drillDownPaths = params.drillDownPaths ?? [];
|
|
58
|
+
const requirementsAdvanced = params.requirementsAdvanced ?? [];
|
|
59
|
+
const requirementsValidated = params.requirementsValidated ?? [];
|
|
60
|
+
const requirementsSurfaced = params.requirementsSurfaced ?? [];
|
|
61
|
+
const requirementsInvalidated = params.requirementsInvalidated ?? [];
|
|
62
|
+
const filesModified = params.filesModified ?? [];
|
|
63
|
+
|
|
64
|
+
const providesYaml = provides.length > 0
|
|
65
|
+
? provides.map(p => ` - ${p}`).join("\n")
|
|
51
66
|
: " - (none)";
|
|
52
67
|
|
|
53
|
-
const requiresYaml =
|
|
54
|
-
?
|
|
68
|
+
const requiresYaml = requires.length > 0
|
|
69
|
+
? requires.map(r => ` - slice: ${r.slice}\n provides: ${r.provides}`).join("\n")
|
|
55
70
|
: " []";
|
|
56
71
|
|
|
57
|
-
const affectsYaml =
|
|
58
|
-
?
|
|
72
|
+
const affectsYaml = affects.length > 0
|
|
73
|
+
? affects.map(a => ` - ${a}`).join("\n")
|
|
59
74
|
: " []";
|
|
60
75
|
|
|
61
|
-
const keyFilesYaml =
|
|
62
|
-
?
|
|
76
|
+
const keyFilesYaml = keyFiles.length > 0
|
|
77
|
+
? keyFiles.map(f => ` - ${f}`).join("\n")
|
|
63
78
|
: " - (none)";
|
|
64
79
|
|
|
65
|
-
const keyDecisionsYaml =
|
|
66
|
-
?
|
|
80
|
+
const keyDecisionsYaml = keyDecisions.length > 0
|
|
81
|
+
? keyDecisions.map(d => ` - ${d}`).join("\n")
|
|
67
82
|
: " - (none)";
|
|
68
83
|
|
|
69
|
-
const patternsYaml =
|
|
70
|
-
?
|
|
84
|
+
const patternsYaml = patternsEstablished.length > 0
|
|
85
|
+
? patternsEstablished.map(p => ` - ${p}`).join("\n")
|
|
71
86
|
: " - (none)";
|
|
72
87
|
|
|
73
|
-
const observabilityYaml =
|
|
74
|
-
?
|
|
88
|
+
const observabilityYaml = observabilitySurfaces.length > 0
|
|
89
|
+
? observabilitySurfaces.map(o => ` - ${o}`).join("\n")
|
|
75
90
|
: " - none";
|
|
76
91
|
|
|
77
|
-
const drillDownYaml =
|
|
78
|
-
?
|
|
92
|
+
const drillDownYaml = drillDownPaths.length > 0
|
|
93
|
+
? drillDownPaths.map(d => ` - ${d}`).join("\n")
|
|
79
94
|
: " []";
|
|
80
95
|
|
|
81
96
|
// Requirements sections
|
|
82
|
-
const reqAdvanced =
|
|
83
|
-
?
|
|
97
|
+
const reqAdvanced = requirementsAdvanced.length > 0
|
|
98
|
+
? requirementsAdvanced.map(r => `- ${r.id} — ${r.how}`).join("\n")
|
|
84
99
|
: "None.";
|
|
85
100
|
|
|
86
|
-
const reqValidated =
|
|
87
|
-
?
|
|
101
|
+
const reqValidated = requirementsValidated.length > 0
|
|
102
|
+
? requirementsValidated.map(r => `- ${r.id} — ${r.proof}`).join("\n")
|
|
88
103
|
: "None.";
|
|
89
104
|
|
|
90
|
-
const reqSurfaced =
|
|
91
|
-
?
|
|
105
|
+
const reqSurfaced = requirementsSurfaced.length > 0
|
|
106
|
+
? requirementsSurfaced.map(r => `- ${r}`).join("\n")
|
|
92
107
|
: "None.";
|
|
93
108
|
|
|
94
|
-
const reqInvalidated =
|
|
95
|
-
?
|
|
109
|
+
const reqInvalidated = requirementsInvalidated.length > 0
|
|
110
|
+
? requirementsInvalidated.map(r => `- ${r.id} — ${r.what}`).join("\n")
|
|
96
111
|
: "None.";
|
|
97
112
|
|
|
98
113
|
// Files modified
|
|
99
|
-
const filesMod =
|
|
100
|
-
?
|
|
114
|
+
const filesMod = filesModified.length > 0
|
|
115
|
+
? filesModified.map(f => `- \`${f.path}\` — ${f.description}`).join("\n")
|
|
101
116
|
: "None.";
|
|
102
117
|
|
|
103
118
|
return `---
|
|
@@ -292,13 +307,11 @@ export async function handleCompleteSlice(
|
|
|
292
307
|
// Toggle roadmap checkbox via renderer module
|
|
293
308
|
const roadmapToggled = await renderRoadmapCheckboxes(basePath, params.milestoneId);
|
|
294
309
|
if (!roadmapToggled) {
|
|
295
|
-
|
|
296
|
-
`gsd-db: complete_slice — could not find roadmap for ${params.milestoneId}, skipping checkbox toggle\n`,
|
|
297
|
-
);
|
|
310
|
+
logWarning("tool", `complete_slice — could not find roadmap for ${params.milestoneId}, skipping checkbox toggle`);
|
|
298
311
|
}
|
|
299
312
|
} catch (renderErr) {
|
|
300
313
|
// Disk render failed — roll back DB status so state stays consistent
|
|
301
|
-
logWarning("tool", `complete_slice — disk render failed, rolling back DB status:
|
|
314
|
+
logWarning("tool", `complete_slice — disk render failed for ${params.milestoneId}/${params.sliceId}, rolling back DB status`, { error: (renderErr as Error).message });
|
|
302
315
|
updateSliceStatus(params.milestoneId, params.sliceId, 'pending');
|
|
303
316
|
invalidateStateCache();
|
|
304
317
|
return { error: `disk render failed: ${(renderErr as Error).message}` };
|
|
@@ -325,7 +338,7 @@ export async function handleCompleteSlice(
|
|
|
325
338
|
trigger_reason: params.triggerReason,
|
|
326
339
|
});
|
|
327
340
|
} catch (hookErr) {
|
|
328
|
-
logWarning("tool", `complete-slice post-mutation hook
|
|
341
|
+
logWarning("tool", `complete-slice post-mutation hook failed for ${params.milestoneId}/${params.sliceId}`, { error: (hookErr as Error).message });
|
|
329
342
|
}
|
|
330
343
|
|
|
331
344
|
return {
|
|
@@ -60,11 +60,11 @@ function paramsToTaskRow(params: CompleteTaskParams, completedAt: string): TaskR
|
|
|
60
60
|
verification_result: params.verification,
|
|
61
61
|
duration: "",
|
|
62
62
|
completed_at: completedAt,
|
|
63
|
-
blocker_discovered: params.blockerDiscovered,
|
|
64
|
-
deviations: params.deviations,
|
|
65
|
-
known_issues: params.knownIssues,
|
|
66
|
-
key_files: params.keyFiles,
|
|
67
|
-
key_decisions: params.keyDecisions,
|
|
63
|
+
blocker_discovered: params.blockerDiscovered ?? false,
|
|
64
|
+
deviations: params.deviations ?? "",
|
|
65
|
+
known_issues: params.knownIssues ?? "",
|
|
66
|
+
key_files: params.keyFiles ?? [],
|
|
67
|
+
key_decisions: params.keyDecisions ?? [],
|
|
68
68
|
full_summary_md: "",
|
|
69
69
|
description: "",
|
|
70
70
|
estimate: "",
|
|
@@ -152,14 +152,14 @@ export async function handleCompleteTask(
|
|
|
152
152
|
narrative: params.narrative,
|
|
153
153
|
verificationResult: params.verification,
|
|
154
154
|
duration: "",
|
|
155
|
-
blockerDiscovered: params.blockerDiscovered,
|
|
156
|
-
deviations: params.deviations,
|
|
157
|
-
knownIssues: params.knownIssues,
|
|
158
|
-
keyFiles: params.keyFiles,
|
|
159
|
-
keyDecisions: params.keyDecisions,
|
|
155
|
+
blockerDiscovered: params.blockerDiscovered ?? false,
|
|
156
|
+
deviations: params.deviations ?? "None.",
|
|
157
|
+
knownIssues: params.knownIssues ?? "None.",
|
|
158
|
+
keyFiles: params.keyFiles ?? [],
|
|
159
|
+
keyDecisions: params.keyDecisions ?? [],
|
|
160
160
|
});
|
|
161
161
|
|
|
162
|
-
for (const evidence of params.verificationEvidence) {
|
|
162
|
+
for (const evidence of (params.verificationEvidence ?? [])) {
|
|
163
163
|
insertVerificationEvidence({
|
|
164
164
|
taskId: params.taskId,
|
|
165
165
|
sliceId: params.sliceId,
|
|
@@ -182,7 +182,7 @@ export async function handleCompleteTask(
|
|
|
182
182
|
|
|
183
183
|
// Render summary markdown via the single source of truth (#2720)
|
|
184
184
|
const taskRow = paramsToTaskRow(params, completedAt);
|
|
185
|
-
const summaryMd = renderSummaryContent(taskRow, params.sliceId, params.milestoneId, params.verificationEvidence);
|
|
185
|
+
const summaryMd = renderSummaryContent(taskRow, params.sliceId, params.milestoneId, params.verificationEvidence ?? []);
|
|
186
186
|
|
|
187
187
|
// Resolve and write summary to disk
|
|
188
188
|
let summaryPath: string;
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
transaction,
|
|
6
6
|
getMilestone,
|
|
7
7
|
getMilestoneSlices,
|
|
8
|
+
getSlice,
|
|
8
9
|
insertMilestone,
|
|
9
10
|
insertSlice,
|
|
10
11
|
upsertMilestonePlanning,
|
|
@@ -33,24 +34,34 @@ export interface PlanMilestoneSliceInput {
|
|
|
33
34
|
export interface PlanMilestoneParams {
|
|
34
35
|
milestoneId: string;
|
|
35
36
|
title: string;
|
|
37
|
+
vision: string;
|
|
38
|
+
slices: PlanMilestoneSliceInput[];
|
|
36
39
|
status?: string;
|
|
37
40
|
dependsOn?: string[];
|
|
38
41
|
/** Optional caller-provided identity for audit trail */
|
|
39
42
|
actorName?: string;
|
|
40
43
|
/** Optional caller-provided reason this action was triggered */
|
|
41
44
|
triggerReason?: string;
|
|
42
|
-
|
|
43
|
-
successCriteria
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
/** @optional — defaults to [] when omitted by models with limited tool-calling */
|
|
46
|
+
successCriteria?: string[];
|
|
47
|
+
/** @optional — defaults to [] when omitted */
|
|
48
|
+
keyRisks?: Array<{ risk: string; whyItMatters: string }>;
|
|
49
|
+
/** @optional — defaults to [] when omitted */
|
|
50
|
+
proofStrategy?: Array<{ riskOrUnknown: string; retireIn: string; whatWillBeProven: string }>;
|
|
51
|
+
/** @optional — defaults to "Not provided." when omitted */
|
|
52
|
+
verificationContract?: string;
|
|
53
|
+
/** @optional — defaults to "Not provided." when omitted */
|
|
54
|
+
verificationIntegration?: string;
|
|
55
|
+
/** @optional — defaults to "Not provided." when omitted */
|
|
56
|
+
verificationOperational?: string;
|
|
57
|
+
/** @optional — defaults to "Not provided." when omitted */
|
|
58
|
+
verificationUat?: string;
|
|
59
|
+
/** @optional — defaults to [] when omitted */
|
|
60
|
+
definitionOfDone?: string[];
|
|
61
|
+
/** @optional — defaults to "Not provided." when omitted */
|
|
62
|
+
requirementCoverage?: string;
|
|
63
|
+
/** @optional — defaults to "Not provided." when omitted */
|
|
64
|
+
boundaryMapMarkdown?: string;
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
export interface PlanMilestoneResult {
|
|
@@ -149,20 +160,21 @@ function validateParams(params: PlanMilestoneParams): PlanMilestoneParams {
|
|
|
149
160
|
if (!isNonEmptyString(params?.milestoneId)) throw new Error("milestoneId is required");
|
|
150
161
|
if (!isNonEmptyString(params?.title)) throw new Error("title is required");
|
|
151
162
|
if (!isNonEmptyString(params?.vision)) throw new Error("vision is required");
|
|
152
|
-
if (!isNonEmptyString(params?.verificationContract)) throw new Error("verificationContract is required");
|
|
153
|
-
if (!isNonEmptyString(params?.verificationIntegration)) throw new Error("verificationIntegration is required");
|
|
154
|
-
if (!isNonEmptyString(params?.verificationOperational)) throw new Error("verificationOperational is required");
|
|
155
|
-
if (!isNonEmptyString(params?.verificationUat)) throw new Error("verificationUat is required");
|
|
156
|
-
if (!isNonEmptyString(params?.requirementCoverage)) throw new Error("requirementCoverage is required");
|
|
157
|
-
if (!isNonEmptyString(params?.boundaryMapMarkdown)) throw new Error("boundaryMapMarkdown is required");
|
|
158
163
|
|
|
159
164
|
return {
|
|
160
165
|
...params,
|
|
161
166
|
dependsOn: params.dependsOn ? validateStringArray(params.dependsOn, "dependsOn") : [],
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
167
|
+
// Apply defaults for optional enrichment fields (#2771)
|
|
168
|
+
successCriteria: params.successCriteria ? validateStringArray(params.successCriteria, "successCriteria") : [],
|
|
169
|
+
keyRisks: params.keyRisks ? validateRiskEntries(params.keyRisks) : [],
|
|
170
|
+
proofStrategy: params.proofStrategy ? validateProofStrategy(params.proofStrategy) : [],
|
|
171
|
+
verificationContract: params.verificationContract ?? "Not provided.",
|
|
172
|
+
verificationIntegration: params.verificationIntegration ?? "Not provided.",
|
|
173
|
+
verificationOperational: params.verificationOperational ?? "Not provided.",
|
|
174
|
+
verificationUat: params.verificationUat ?? "Not provided.",
|
|
175
|
+
definitionOfDone: params.definitionOfDone ? validateStringArray(params.definitionOfDone, "definitionOfDone") : [],
|
|
176
|
+
requirementCoverage: params.requirementCoverage ?? "Not provided.",
|
|
177
|
+
boundaryMapMarkdown: params.boundaryMapMarkdown ?? "Not provided.",
|
|
166
178
|
slices: validateSlices(params.slices),
|
|
167
179
|
};
|
|
168
180
|
}
|
|
@@ -191,15 +203,19 @@ export async function handlePlanMilestone(
|
|
|
191
203
|
return;
|
|
192
204
|
}
|
|
193
205
|
|
|
194
|
-
// Guard: refuse to re-plan a milestone that
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
206
|
+
// Guard: refuse to re-plan a milestone that would drop completed slices (#2960).
|
|
207
|
+
// Allow re-planning when all completed slices are still present in the
|
|
208
|
+
// incoming plan — their status is preserved below (#2558). Block only when
|
|
209
|
+
// the new plan omits a completed slice, which could shadow completed work.
|
|
198
210
|
const existingSlices = getMilestoneSlices(params.milestoneId);
|
|
199
211
|
const completedSlices = existingSlices.filter(s => isClosedStatus(s.status));
|
|
200
212
|
if (completedSlices.length > 0) {
|
|
201
|
-
|
|
202
|
-
|
|
213
|
+
const incomingSliceIds = new Set(params.slices.map(s => s.sliceId));
|
|
214
|
+
const droppedCompleted = completedSlices.filter(s => !incomingSliceIds.has(s.id));
|
|
215
|
+
if (droppedCompleted.length > 0) {
|
|
216
|
+
guardError = `cannot re-plan milestone ${params.milestoneId}: ${droppedCompleted.length} completed slice(s) would be dropped (${droppedCompleted.map(s => s.id).join(", ")}). Use gsd_reassess_roadmap to modify the roadmap.`;
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
203
219
|
}
|
|
204
220
|
|
|
205
221
|
// Validate depends_on: all dependencies must exist and be complete
|
|
@@ -225,6 +241,8 @@ export async function handlePlanMilestone(
|
|
|
225
241
|
});
|
|
226
242
|
|
|
227
243
|
upsertMilestonePlanning(params.milestoneId, {
|
|
244
|
+
title: params.title,
|
|
245
|
+
status: params.status ?? "active",
|
|
228
246
|
vision: params.vision,
|
|
229
247
|
successCriteria: params.successCriteria,
|
|
230
248
|
keyRisks: params.keyRisks,
|
|
@@ -236,14 +254,21 @@ export async function handlePlanMilestone(
|
|
|
236
254
|
definitionOfDone: params.definitionOfDone,
|
|
237
255
|
requirementCoverage: params.requirementCoverage,
|
|
238
256
|
boundaryMapMarkdown: params.boundaryMapMarkdown,
|
|
239
|
-
}
|
|
257
|
+
});
|
|
240
258
|
|
|
241
259
|
for (const slice of params.slices) {
|
|
260
|
+
// Preserve completed/done status on re-plan (#2558).
|
|
261
|
+
// Without this, a re-plan after milestone transition would reset
|
|
262
|
+
// already-completed slices back to "pending".
|
|
263
|
+
const existing = getSlice(params.milestoneId, slice.sliceId);
|
|
264
|
+
const status = existing && (existing.status === "complete" || existing.status === "done")
|
|
265
|
+
? existing.status
|
|
266
|
+
: "pending";
|
|
242
267
|
insertSlice({
|
|
243
268
|
id: slice.sliceId,
|
|
244
269
|
milestoneId: params.milestoneId,
|
|
245
270
|
title: slice.title,
|
|
246
|
-
status
|
|
271
|
+
status,
|
|
247
272
|
risk: slice.risk,
|
|
248
273
|
depends: slice.depends,
|
|
249
274
|
demo: slice.demo,
|
|
@@ -35,11 +35,15 @@ export interface PlanSliceParams {
|
|
|
35
35
|
milestoneId: string;
|
|
36
36
|
sliceId: string;
|
|
37
37
|
goal: string;
|
|
38
|
-
successCriteria: string;
|
|
39
|
-
proofLevel: string;
|
|
40
|
-
integrationClosure: string;
|
|
41
|
-
observabilityImpact: string;
|
|
42
38
|
tasks: PlanSliceTaskInput[];
|
|
39
|
+
/** @optional — defaults to "Not provided." when omitted by models with limited tool-calling */
|
|
40
|
+
successCriteria?: string;
|
|
41
|
+
/** @optional — defaults to "Not provided." when omitted */
|
|
42
|
+
proofLevel?: string;
|
|
43
|
+
/** @optional — defaults to "Not provided." when omitted */
|
|
44
|
+
integrationClosure?: string;
|
|
45
|
+
/** @optional — defaults to "Not provided." when omitted */
|
|
46
|
+
observabilityImpact?: string;
|
|
43
47
|
/** Optional caller-provided identity for audit trail */
|
|
44
48
|
actorName?: string;
|
|
45
49
|
/** Optional caller-provided reason this action was triggered */
|
|
@@ -112,13 +116,14 @@ function validateParams(params: PlanSliceParams): PlanSliceParams {
|
|
|
112
116
|
if (!isNonEmptyString(params?.milestoneId)) throw new Error("milestoneId is required");
|
|
113
117
|
if (!isNonEmptyString(params?.sliceId)) throw new Error("sliceId is required");
|
|
114
118
|
if (!isNonEmptyString(params?.goal)) throw new Error("goal is required");
|
|
115
|
-
if (!isNonEmptyString(params?.successCriteria)) throw new Error("successCriteria is required");
|
|
116
|
-
if (!isNonEmptyString(params?.proofLevel)) throw new Error("proofLevel is required");
|
|
117
|
-
if (!isNonEmptyString(params?.integrationClosure)) throw new Error("integrationClosure is required");
|
|
118
|
-
if (!isNonEmptyString(params?.observabilityImpact)) throw new Error("observabilityImpact is required");
|
|
119
119
|
|
|
120
120
|
return {
|
|
121
121
|
...params,
|
|
122
|
+
// Apply defaults for optional enrichment fields (#2771)
|
|
123
|
+
successCriteria: params.successCriteria ?? "Not provided.",
|
|
124
|
+
proofLevel: params.proofLevel ?? "Not provided.",
|
|
125
|
+
integrationClosure: params.integrationClosure ?? "Not provided.",
|
|
126
|
+
observabilityImpact: params.observabilityImpact ?? "Not provided.",
|
|
122
127
|
tasks: validateTasks(params.tasks),
|
|
123
128
|
};
|
|
124
129
|
}
|