gsd-pi 2.51.0 → 2.52.0-dev.585e355
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/headless-events.d.ts +18 -0
- package/dist/headless-events.js +36 -0
- package/dist/headless-types.d.ts +28 -0
- package/dist/headless-types.js +7 -0
- package/dist/headless.d.ts +8 -3
- package/dist/headless.js +47 -16
- package/dist/help-text.js +16 -5
- package/dist/onboarding.js +5 -4
- package/dist/remote-questions-config.js +1 -1
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +29 -17
- package/dist/resources/extensions/async-jobs/job-manager.js +4 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +18 -19
- package/dist/resources/extensions/gsd/auto/phases.js +6 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +18 -0
- package/dist/resources/extensions/gsd/auto-start.js +2 -0
- package/dist/resources/extensions/gsd/auto-timers.js +24 -2
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +25 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +21 -0
- package/dist/resources/extensions/gsd/auto.js +8 -4
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +105 -70
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +12 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +1 -1
- package/dist/resources/extensions/gsd/claude-import.js +60 -9
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +69 -6
- package/dist/resources/extensions/gsd/commands-config.js +10 -5
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +4 -4
- package/dist/resources/extensions/gsd/detection.js +6 -6
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +5 -5
- package/dist/resources/extensions/gsd/error-classifier.js +105 -0
- package/dist/resources/extensions/gsd/git-service.js +4 -3
- package/dist/resources/extensions/gsd/gitignore.js +7 -7
- package/dist/resources/extensions/gsd/gsd-db.js +298 -45
- package/dist/resources/extensions/gsd/init-wizard.js +2 -2
- package/dist/resources/extensions/gsd/key-manager.js +7 -16
- package/dist/resources/extensions/gsd/markdown-renderer.js +5 -4
- package/dist/resources/extensions/gsd/memory-store.js +28 -13
- package/dist/resources/extensions/gsd/milestone-actions.js +19 -0
- package/dist/resources/extensions/gsd/preferences-models.js +1 -13
- package/dist/resources/extensions/gsd/preferences-types.js +1 -1
- package/dist/resources/extensions/gsd/preferences.js +13 -13
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/provider-error-pause.js +0 -44
- package/dist/resources/extensions/gsd/rule-registry.js +1 -1
- package/dist/resources/extensions/gsd/service-tier.js +13 -2
- package/dist/resources/extensions/gsd/state.js +33 -19
- package/dist/resources/extensions/gsd/status-guards.js +12 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +7 -13
- package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -20
- package/dist/resources/extensions/gsd/tools/complete-task.js +11 -21
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +28 -29
- package/dist/resources/extensions/gsd/tools/plan-slice.js +27 -26
- package/dist/resources/extensions/gsd/tools/plan-task.js +23 -23
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +50 -41
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +4 -3
- package/dist/resources/extensions/gsd/tools/reopen-task.js +5 -4
- package/dist/resources/extensions/gsd/tools/replan-slice.js +51 -41
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +23 -16
- package/dist/resources/extensions/gsd/validation.js +21 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +0 -1
- package/dist/resources/extensions/remote-questions/config.js +1 -1
- package/dist/resources/extensions/remote-questions/remote-command.js +1 -1
- package/dist/resources/extensions/search-the-web/native-search.js +1 -1
- package/dist/resources/extensions/search-the-web/provider.js +1 -1
- package/dist/resources/extensions/shared/rtk.js +5 -3
- package/dist/rtk.js +3 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
- 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 +4 -4
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +5 -5
- 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 +5 -5
- 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 +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- 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/2229.js +3 -3
- package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.21054f459af5cc78.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-b950e4e384cc62b3.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-cfc9a116e6450a6b.js → webpack-024d82be84800e52.js} +1 -1
- package/dist/web/standalone/.next/static/css/a58ef8a151aa0493.css +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/wizard.js +4 -1
- package/package.json +2 -2
- package/packages/mcp-server/README.md +202 -0
- package/packages/mcp-server/package.json +36 -0
- package/packages/mcp-server/src/cli.ts +68 -0
- package/packages/mcp-server/src/index.ts +14 -0
- package/packages/mcp-server/src/mcp-server.test.ts +628 -0
- package/packages/mcp-server/src/server.ts +278 -0
- package/packages/mcp-server/src/session-manager.ts +328 -0
- package/packages/mcp-server/src/types.ts +107 -0
- package/packages/mcp-server/tsconfig.json +24 -0
- package/packages/pi-ai/dist/models.d.ts +14 -3
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.js +53 -10
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/models.test.js +102 -1
- package/packages/pi-ai/dist/models.test.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +30 -0
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/models.test.ts +114 -1
- package/packages/pi-ai/src/models.ts +70 -13
- package/packages/pi-ai/src/types.ts +31 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts +2 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +3 -0
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.js +5 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +9 -4
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts +19 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +83 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +5 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +5 -3
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +0 -2
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +28 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +49 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +114 -6
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts +9 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js +831 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +66 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.js +0 -1
- package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/cli/args.ts +4 -0
- package/packages/pi-coding-agent/src/core/bash-executor.ts +5 -1
- package/packages/pi-coding-agent/src/core/model-registry.ts +10 -3
- package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +101 -0
- package/packages/pi-coding-agent/src/core/tools/bash.ts +5 -1
- package/packages/pi-coding-agent/src/index.ts +3 -0
- package/packages/pi-coding-agent/src/main.ts +5 -3
- package/packages/pi-coding-agent/src/modes/index.ts +8 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +0 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +54 -1
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +124 -6
- package/packages/pi-coding-agent/src/modes/rpc/rpc-protocol-v2.test.ts +971 -0
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +61 -4
- package/packages/pi-coding-agent/src/utils/shell.ts +0 -1
- package/packages/rpc-client/package.json +20 -0
- package/pkg/package.json +1 -1
- package/scripts/ensure-workspace-builds.cjs +36 -8
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +22 -11
- package/src/resources/extensions/async-jobs/job-manager.ts +4 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +19 -20
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +21 -0
- package/src/resources/extensions/gsd/auto/phases.ts +6 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +19 -0
- package/src/resources/extensions/gsd/auto-start.ts +2 -0
- package/src/resources/extensions/gsd/auto-timers.ts +25 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +30 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +21 -0
- package/src/resources/extensions/gsd/auto.ts +10 -4
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -73
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +11 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +1 -1
- package/src/resources/extensions/gsd/claude-import.ts +58 -9
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +73 -6
- package/src/resources/extensions/gsd/commands-config.ts +11 -5
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -4
- package/src/resources/extensions/gsd/detection.ts +6 -6
- package/src/resources/extensions/gsd/docs/preferences-reference.md +5 -5
- package/src/resources/extensions/gsd/error-classifier.ts +139 -0
- package/src/resources/extensions/gsd/git-service.ts +4 -3
- package/src/resources/extensions/gsd/gitignore.ts +7 -7
- package/src/resources/extensions/gsd/gsd-db.ts +355 -63
- package/src/resources/extensions/gsd/init-wizard.ts +2 -2
- package/src/resources/extensions/gsd/key-manager.ts +7 -16
- package/src/resources/extensions/gsd/markdown-renderer.ts +5 -4
- package/src/resources/extensions/gsd/memory-store.ts +29 -18
- package/src/resources/extensions/gsd/milestone-actions.ts +17 -0
- package/src/resources/extensions/gsd/preferences-models.ts +1 -13
- package/src/resources/extensions/gsd/preferences-types.ts +1 -1
- package/src/resources/extensions/gsd/preferences.ts +12 -13
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/provider-error-pause.ts +0 -57
- package/src/resources/extensions/gsd/rule-registry.ts +1 -1
- package/src/resources/extensions/gsd/service-tier.ts +14 -2
- package/src/resources/extensions/gsd/state.ts +34 -20
- package/src/resources/extensions/gsd/status-guards.ts +13 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +61 -0
- package/src/resources/extensions/gsd/tests/claude-import-marketplace-discovery.test.ts +191 -0
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/commands-config.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +35 -7
- package/src/resources/extensions/gsd/tests/detection.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/empty-db-reconciliation.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +37 -4
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +119 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +16 -1
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +77 -70
- package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/status-guards.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +42 -31
- package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/vacuous-truth-slices.test.ts +115 -0
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/validation.test.ts +72 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +81 -1
- package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +130 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -17
- package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -24
- package/src/resources/extensions/gsd/tools/complete-task.ts +13 -25
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +30 -32
- package/src/resources/extensions/gsd/tools/plan-slice.ts +30 -30
- package/src/resources/extensions/gsd/tools/plan-task.ts +26 -26
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +57 -46
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +4 -3
- package/src/resources/extensions/gsd/tools/reopen-task.ts +5 -4
- package/src/resources/extensions/gsd/tools/replan-slice.ts +55 -44
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +26 -20
- package/src/resources/extensions/gsd/validation.ts +23 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +0 -1
- package/src/resources/extensions/remote-questions/config.ts +1 -1
- package/src/resources/extensions/remote-questions/remote-command.ts +1 -1
- package/src/resources/extensions/search-the-web/native-search.ts +1 -1
- package/src/resources/extensions/search-the-web/provider.ts +1 -1
- package/src/resources/extensions/shared/rtk.ts +12 -3
- package/dist/web/standalone/.next/static/chunks/4024.9ad5def014d90ce4.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-fbecd1237e2d6d1f.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/css/de141508b083f922.css +0 -1
- /package/dist/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
- /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → KTe1kB5nPLQFIIFz2OcmI}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → KTe1kB5nPLQFIIFz2OcmI}/_ssgManifest.js +0 -0
- /package/src/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portable tests for marketplace discovery in claude-import.
|
|
3
|
+
*
|
|
4
|
+
* Validates that categorizePluginRoots correctly discovers marketplace repos
|
|
5
|
+
* nested inside container directories (the Claude Code convention), and that
|
|
6
|
+
* discoverClaudePlugins recognizes .claude-plugin/plugin.json in addition to
|
|
7
|
+
* package.json.
|
|
8
|
+
*
|
|
9
|
+
* Uses temp-dir fixtures — no real marketplace repos required.
|
|
10
|
+
*
|
|
11
|
+
* Fixes: https://github.com/gsd-build/gsd-2/issues/2717
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
15
|
+
import assert from "node:assert/strict";
|
|
16
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { tmpdir } from "node:os";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import { categorizePluginRoots } from "../claude-import.js";
|
|
20
|
+
|
|
21
|
+
describe("categorizePluginRoots", () => {
|
|
22
|
+
let tmpDir: string;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
tmpDir = mkdtempSync(join(tmpdir(), "gsd-mktplace-test-"));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should detect a direct marketplace root", () => {
|
|
33
|
+
// Root itself has .claude-plugin/marketplace.json
|
|
34
|
+
mkdirSync(join(tmpDir, ".claude-plugin"), { recursive: true });
|
|
35
|
+
writeFileSync(
|
|
36
|
+
join(tmpDir, ".claude-plugin", "marketplace.json"),
|
|
37
|
+
JSON.stringify({ name: "direct", plugins: [] })
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const { marketplaces, flat } = categorizePluginRoots([tmpDir]);
|
|
41
|
+
|
|
42
|
+
assert.equal(marketplaces.length, 1);
|
|
43
|
+
assert.equal(marketplaces[0], tmpDir);
|
|
44
|
+
assert.equal(flat.length, 0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should discover marketplace repos nested one level inside a container directory", () => {
|
|
48
|
+
// Simulate ~/.claude/plugins/marketplaces/ with two marketplace subdirs
|
|
49
|
+
const mktA = join(tmpDir, "marketplace-a");
|
|
50
|
+
const mktB = join(tmpDir, "marketplace-b");
|
|
51
|
+
|
|
52
|
+
mkdirSync(join(mktA, ".claude-plugin"), { recursive: true });
|
|
53
|
+
writeFileSync(
|
|
54
|
+
join(mktA, ".claude-plugin", "marketplace.json"),
|
|
55
|
+
JSON.stringify({ name: "a", plugins: [] })
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
mkdirSync(join(mktB, ".claude-plugin"), { recursive: true });
|
|
59
|
+
writeFileSync(
|
|
60
|
+
join(mktB, ".claude-plugin", "marketplace.json"),
|
|
61
|
+
JSON.stringify({ name: "b", plugins: [] })
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const { marketplaces, flat } = categorizePluginRoots([tmpDir]);
|
|
65
|
+
|
|
66
|
+
assert.equal(marketplaces.length, 2);
|
|
67
|
+
assert.ok(marketplaces.includes(mktA));
|
|
68
|
+
assert.ok(marketplaces.includes(mktB));
|
|
69
|
+
assert.equal(flat.length, 0);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should fall back to flat when no child is a marketplace", () => {
|
|
73
|
+
// Container with no marketplace subdirs
|
|
74
|
+
mkdirSync(join(tmpDir, "some-dir"), { recursive: true });
|
|
75
|
+
|
|
76
|
+
const { marketplaces, flat } = categorizePluginRoots([tmpDir]);
|
|
77
|
+
|
|
78
|
+
assert.equal(marketplaces.length, 0);
|
|
79
|
+
assert.equal(flat.length, 1);
|
|
80
|
+
assert.equal(flat[0], tmpDir);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should handle a mix of direct marketplace and container roots", () => {
|
|
84
|
+
// Root A is a direct marketplace
|
|
85
|
+
const directRoot = join(tmpDir, "direct");
|
|
86
|
+
mkdirSync(join(directRoot, ".claude-plugin"), { recursive: true });
|
|
87
|
+
writeFileSync(
|
|
88
|
+
join(directRoot, ".claude-plugin", "marketplace.json"),
|
|
89
|
+
JSON.stringify({ name: "direct", plugins: [] })
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Root B is a container with a child marketplace
|
|
93
|
+
const container = join(tmpDir, "container");
|
|
94
|
+
const child = join(container, "child-marketplace");
|
|
95
|
+
mkdirSync(join(child, ".claude-plugin"), { recursive: true });
|
|
96
|
+
writeFileSync(
|
|
97
|
+
join(child, ".claude-plugin", "marketplace.json"),
|
|
98
|
+
JSON.stringify({ name: "child", plugins: [] })
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Root C has nothing
|
|
102
|
+
const emptyRoot = join(tmpDir, "empty");
|
|
103
|
+
mkdirSync(emptyRoot, { recursive: true });
|
|
104
|
+
|
|
105
|
+
const { marketplaces, flat } = categorizePluginRoots([
|
|
106
|
+
directRoot,
|
|
107
|
+
container,
|
|
108
|
+
emptyRoot,
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
assert.equal(marketplaces.length, 2);
|
|
112
|
+
assert.ok(marketplaces.includes(directRoot));
|
|
113
|
+
assert.ok(marketplaces.includes(child));
|
|
114
|
+
assert.equal(flat.length, 1);
|
|
115
|
+
assert.equal(flat[0], emptyRoot);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("should not duplicate when the same marketplace appears via multiple roots", () => {
|
|
119
|
+
// Direct reference AND container reference to the same marketplace
|
|
120
|
+
const mkt = join(tmpDir, "mkt");
|
|
121
|
+
mkdirSync(join(mkt, ".claude-plugin"), { recursive: true });
|
|
122
|
+
writeFileSync(
|
|
123
|
+
join(mkt, ".claude-plugin", "marketplace.json"),
|
|
124
|
+
JSON.stringify({ name: "mkt", plugins: [] })
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const { marketplaces } = categorizePluginRoots([mkt, tmpDir]);
|
|
128
|
+
|
|
129
|
+
assert.equal(marketplaces.length, 1);
|
|
130
|
+
assert.equal(marketplaces[0], mkt);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should skip .git and node_modules subdirectories", () => {
|
|
134
|
+
// Put a marketplace.json inside .git — should be ignored
|
|
135
|
+
mkdirSync(join(tmpDir, ".git", ".claude-plugin"), { recursive: true });
|
|
136
|
+
writeFileSync(
|
|
137
|
+
join(tmpDir, ".git", ".claude-plugin", "marketplace.json"),
|
|
138
|
+
JSON.stringify({ name: "hidden", plugins: [] })
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const { marketplaces, flat } = categorizePluginRoots([tmpDir]);
|
|
142
|
+
|
|
143
|
+
assert.equal(marketplaces.length, 0);
|
|
144
|
+
assert.equal(flat.length, 1);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("should handle non-existent root gracefully", () => {
|
|
148
|
+
const missing = join(tmpDir, "does-not-exist");
|
|
149
|
+
// categorizePluginRoots receives paths from uniqueExistingDirs, but
|
|
150
|
+
// be defensive — it should not crash on a missing root
|
|
151
|
+
const { marketplaces, flat } = categorizePluginRoots([missing]);
|
|
152
|
+
|
|
153
|
+
assert.equal(marketplaces.length, 0);
|
|
154
|
+
assert.equal(flat.length, 1); // falls through to flat
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe("discoverClaudePlugins — Claude plugin.json recognition", () => {
|
|
159
|
+
let tmpDir: string;
|
|
160
|
+
|
|
161
|
+
beforeEach(() => {
|
|
162
|
+
tmpDir = mkdtempSync(join(tmpdir(), "gsd-plugin-disc-"));
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
afterEach(() => {
|
|
166
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("should discover a plugin with .claude-plugin/plugin.json (no package.json)", async () => {
|
|
170
|
+
// Simulate a cached Claude marketplace plugin
|
|
171
|
+
const pluginDir = join(tmpDir, "my-plugin");
|
|
172
|
+
mkdirSync(join(pluginDir, ".claude-plugin"), { recursive: true });
|
|
173
|
+
mkdirSync(join(pluginDir, "skills", "my-skill"), { recursive: true });
|
|
174
|
+
writeFileSync(
|
|
175
|
+
join(pluginDir, ".claude-plugin", "plugin.json"),
|
|
176
|
+
JSON.stringify({ name: "my-plugin", version: "1.0.0", description: "Test plugin" })
|
|
177
|
+
);
|
|
178
|
+
writeFileSync(join(pluginDir, "skills", "my-skill", "SKILL.md"), "# My Skill");
|
|
179
|
+
|
|
180
|
+
// Import discoverClaudePlugins dynamically since it depends on getClaudeSearchRoots
|
|
181
|
+
// which uses hardcoded paths. Instead, test the flat-path discovery logic directly
|
|
182
|
+
// by checking that the plugin.json file is recognized.
|
|
183
|
+
const claudePluginPath = join(pluginDir, ".claude-plugin", "plugin.json");
|
|
184
|
+
assert.ok(existsSync(claudePluginPath), "Claude plugin.json should exist");
|
|
185
|
+
|
|
186
|
+
// The fix ensures walkDirs checks for .claude-plugin/plugin.json in addition
|
|
187
|
+
// to package.json. We verify the file structure is correct for discovery.
|
|
188
|
+
const pkgPath = join(pluginDir, "package.json");
|
|
189
|
+
assert.ok(!existsSync(pkgPath), "package.json should NOT exist — this is a Claude plugin");
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
test("commands-config source-level: tool key lookup skips empty api_key entries", () => {
|
|
11
|
+
const source = readFileSync(join(__dirname, "..", "commands-config.ts"), "utf-8");
|
|
12
|
+
assert.ok(
|
|
13
|
+
source.includes('getCredentialsForProvider(providerId)'),
|
|
14
|
+
"commands-config should read the full credential list",
|
|
15
|
+
);
|
|
16
|
+
assert.ok(
|
|
17
|
+
source.includes('c.type === "api_key" && c.key'),
|
|
18
|
+
"commands-config should require a non-empty api_key when resolving stored tool keys",
|
|
19
|
+
);
|
|
20
|
+
assert.ok(
|
|
21
|
+
!source.includes("auth.get(tool.id)"),
|
|
22
|
+
"commands-config should not rely on auth.get(tool.id), which can return an empty shadowing entry",
|
|
23
|
+
);
|
|
24
|
+
});
|
|
@@ -125,9 +125,9 @@ console.log('\n=== complete-slice: schema v6 migration ===');
|
|
|
125
125
|
|
|
126
126
|
const adapter = _getAdapter()!;
|
|
127
127
|
|
|
128
|
-
// Verify schema version is current (
|
|
128
|
+
// Verify schema version is current (v14 after indexes + slice_dependencies)
|
|
129
129
|
const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
130
|
-
assertEq(versionRow?.['v'],
|
|
130
|
+
assertEq(versionRow?.['v'], 14, 'schema version should be 14');
|
|
131
131
|
|
|
132
132
|
// Verify slices table has full_summary_md and full_uat_md columns
|
|
133
133
|
const cols = adapter.prepare("PRAGMA table_info(slices)").all();
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { describe, it, afterEach } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
|
|
8
|
+
import { handleCompleteTask } from "../tools/complete-task.js";
|
|
9
|
+
import {
|
|
10
|
+
openDatabase,
|
|
11
|
+
closeDatabase,
|
|
12
|
+
_getAdapter,
|
|
13
|
+
insertMilestone,
|
|
14
|
+
insertSlice,
|
|
15
|
+
} from "../gsd-db.js";
|
|
16
|
+
import { clearPathCache } from "../paths.js";
|
|
17
|
+
import { clearParseCache } from "../files.js";
|
|
18
|
+
|
|
19
|
+
function makeTmpBase(): string {
|
|
20
|
+
const base = join(tmpdir(), `gsd-ct-rollback-${randomUUID()}`);
|
|
21
|
+
// Create the full tasks directory so the success path works
|
|
22
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks"), { recursive: true });
|
|
23
|
+
return base;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const VALID_PARAMS = {
|
|
27
|
+
milestoneId: "M001",
|
|
28
|
+
sliceId: "S01",
|
|
29
|
+
taskId: "T01",
|
|
30
|
+
oneLiner: "Test task",
|
|
31
|
+
narrative: "Did the thing",
|
|
32
|
+
verification: "Checked it",
|
|
33
|
+
deviations: "None.",
|
|
34
|
+
knownIssues: "None.",
|
|
35
|
+
keyFiles: ["src/foo.ts"],
|
|
36
|
+
keyDecisions: ["Used approach A"],
|
|
37
|
+
blockerDiscovered: false,
|
|
38
|
+
verificationEvidence: [
|
|
39
|
+
{ command: "npm test", exitCode: 0, verdict: "✅ pass", durationMs: 1000 },
|
|
40
|
+
{ command: "npm run lint", exitCode: 0, verdict: "✅ pass", durationMs: 500 },
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
describe("complete-task rollback cleans up verification_evidence (#2724)", () => {
|
|
45
|
+
let base: string;
|
|
46
|
+
|
|
47
|
+
afterEach(() => {
|
|
48
|
+
clearPathCache();
|
|
49
|
+
clearParseCache();
|
|
50
|
+
try { closeDatabase(); } catch { /* */ }
|
|
51
|
+
if (base) {
|
|
52
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* */ }
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("inserts verification_evidence rows on success", async () => {
|
|
57
|
+
base = makeTmpBase();
|
|
58
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
59
|
+
insertMilestone({ id: "M001" });
|
|
60
|
+
insertSlice({ id: "S01", milestoneId: "M001" });
|
|
61
|
+
|
|
62
|
+
// Write a minimal slice plan so renderPlanCheckboxes doesn't error
|
|
63
|
+
writeFileSync(
|
|
64
|
+
join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md"),
|
|
65
|
+
"# S01 Plan\n\n## Tasks\n\n- [ ] **T01: Test task**\n",
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const result = await handleCompleteTask(VALID_PARAMS, base);
|
|
69
|
+
assert.ok(!("error" in result), `unexpected error: ${"error" in result ? result.error : ""}`);
|
|
70
|
+
|
|
71
|
+
const adapter = _getAdapter()!;
|
|
72
|
+
const rows = adapter.prepare(
|
|
73
|
+
`SELECT * FROM verification_evidence WHERE task_id = 'T01' AND slice_id = 'S01' AND milestone_id = 'M001'`,
|
|
74
|
+
).all();
|
|
75
|
+
assert.equal(rows.length, 2, "should have 2 evidence rows after success");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("deletes verification_evidence rows on disk-render rollback", async () => {
|
|
79
|
+
base = makeTmpBase();
|
|
80
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
81
|
+
insertMilestone({ id: "M001" });
|
|
82
|
+
insertSlice({ id: "S01", milestoneId: "M001" });
|
|
83
|
+
|
|
84
|
+
// Replace the tasks directory with a file so disk write fails (cross-platform)
|
|
85
|
+
const tasksDir = join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks");
|
|
86
|
+
rmSync(tasksDir, { recursive: true, force: true });
|
|
87
|
+
writeFileSync(tasksDir, "not-a-directory");
|
|
88
|
+
|
|
89
|
+
const result = await handleCompleteTask(VALID_PARAMS, base);
|
|
90
|
+
assert.ok("error" in result, "should return error when disk write fails");
|
|
91
|
+
|
|
92
|
+
// Task should be rolled back to pending
|
|
93
|
+
const adapter = _getAdapter()!;
|
|
94
|
+
const task = adapter.prepare(
|
|
95
|
+
`SELECT status FROM tasks WHERE milestone_id = 'M001' AND slice_id = 'S01' AND id = 'T01'`,
|
|
96
|
+
).get() as { status: string } | undefined;
|
|
97
|
+
assert.ok(task, "task row should still exist");
|
|
98
|
+
assert.equal(task!.status, "pending", "task status should be rolled back to pending");
|
|
99
|
+
|
|
100
|
+
// Verification evidence should be cleaned up — no orphaned rows
|
|
101
|
+
const evidenceRows = adapter.prepare(
|
|
102
|
+
`SELECT * FROM verification_evidence WHERE task_id = 'T01' AND slice_id = 'S01' AND milestone_id = 'M001'`,
|
|
103
|
+
).all();
|
|
104
|
+
assert.equal(evidenceRows.length, 0, "verification_evidence should be empty after rollback");
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -109,9 +109,9 @@ console.log('\n=== complete-task: schema v5 migration ===');
|
|
|
109
109
|
|
|
110
110
|
const adapter = _getAdapter()!;
|
|
111
111
|
|
|
112
|
-
// Verify schema version is current (
|
|
112
|
+
// Verify schema version is current (v14 after indexes + slice_dependencies)
|
|
113
113
|
const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
114
|
-
assertEq(versionRow?.['v'],
|
|
114
|
+
assertEq(versionRow?.['v'], 14, 'schema version should be 14');
|
|
115
115
|
|
|
116
116
|
// Verify all 4 new tables exist
|
|
117
117
|
const tables = adapter.prepare(
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
getAllMilestones,
|
|
15
15
|
insertSlice,
|
|
16
16
|
insertTask,
|
|
17
|
+
updateTaskStatus,
|
|
17
18
|
} from '../gsd-db.ts';
|
|
18
19
|
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
19
20
|
|
|
@@ -116,10 +117,17 @@ describe('derive-state-db', async () => {
|
|
|
116
117
|
invalidateStateCache();
|
|
117
118
|
const fileState = await deriveState(base);
|
|
118
119
|
|
|
119
|
-
// Now open DB, insert matching artifacts
|
|
120
|
+
// Now open DB, insert matching artifacts + milestone hierarchy
|
|
120
121
|
openDatabase(':memory:');
|
|
121
122
|
assert.ok(isDbAvailable(), 'db-match: DB is available after open');
|
|
122
123
|
|
|
124
|
+
// Insert milestone hierarchy so deriveState takes the DB path (#2631 fix)
|
|
125
|
+
insertMilestone({ id: 'M001', title: 'Test Milestone', status: 'active' });
|
|
126
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First Slice', status: 'active', risk: 'low', depends: [] });
|
|
127
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second Slice', status: 'pending', risk: 'low', depends: ['S01'] });
|
|
128
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First Task', status: 'pending' });
|
|
129
|
+
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Done Task', status: 'complete' });
|
|
130
|
+
|
|
123
131
|
insertArtifactRow('milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT, {
|
|
124
132
|
artifact_type: 'roadmap',
|
|
125
133
|
milestone_id: 'M001',
|
|
@@ -197,18 +205,21 @@ describe('derive-state-db', async () => {
|
|
|
197
205
|
writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
|
|
198
206
|
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
199
207
|
|
|
200
|
-
// Open DB but insert nothing — empty
|
|
208
|
+
// Open DB but insert nothing — empty tables.
|
|
209
|
+
// With #2631 fix, deriveState will sync disk milestones into DB
|
|
210
|
+
// and then take the DB path. The result should still reflect the
|
|
211
|
+
// disk milestone correctly.
|
|
201
212
|
openDatabase(':memory:');
|
|
202
213
|
assert.ok(isDbAvailable(), 'empty-db: DB is available');
|
|
203
214
|
|
|
204
215
|
invalidateStateCache();
|
|
205
216
|
const state = await deriveState(base);
|
|
206
217
|
|
|
207
|
-
//
|
|
208
|
-
assert.deepStrictEqual(state.phase, 'executing', 'empty-db: phase is executing');
|
|
218
|
+
// Milestone should be detected (synced from disk)
|
|
209
219
|
assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'empty-db: activeMilestone is M001');
|
|
210
|
-
|
|
211
|
-
|
|
220
|
+
// The DB path without explicit slice/task rows may derive a different
|
|
221
|
+
// phase than the filesystem path, but the milestone must be found.
|
|
222
|
+
assert.ok(state.activeMilestone !== null, 'empty-db: activeMilestone is not null');
|
|
212
223
|
|
|
213
224
|
closeDatabase();
|
|
214
225
|
} finally {
|
|
@@ -228,8 +239,12 @@ describe('derive-state-db', async () => {
|
|
|
228
239
|
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
229
240
|
writeFile(base, 'REQUIREMENTS.md', REQUIREMENTS_CONTENT);
|
|
230
241
|
|
|
231
|
-
// Open DB
|
|
242
|
+
// Open DB — insert milestone hierarchy + partial artifacts (#2631 fix)
|
|
232
243
|
openDatabase(':memory:');
|
|
244
|
+
insertMilestone({ id: 'M001', title: 'Test Milestone', status: 'active' });
|
|
245
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First Slice', status: 'active', risk: 'low', depends: [] });
|
|
246
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First Task', status: 'pending' });
|
|
247
|
+
// Only insert the roadmap artifact — plan and requirements missing from DB
|
|
233
248
|
insertArtifactRow('milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT, {
|
|
234
249
|
artifact_type: 'roadmap',
|
|
235
250
|
milestone_id: 'M001',
|
|
@@ -314,6 +329,13 @@ describe('derive-state-db', async () => {
|
|
|
314
329
|
|
|
315
330
|
// Put roadmap content in DB only
|
|
316
331
|
openDatabase(':memory:');
|
|
332
|
+
// Insert milestone rows so deriveState takes the DB path (#2631 fix:
|
|
333
|
+
// empty milestones table now triggers disk→DB sync, which would create
|
|
334
|
+
// rows without slices — insert explicitly to get the full DB path).
|
|
335
|
+
insertMilestone({ id: 'M001', title: 'First Milestone', status: 'complete' });
|
|
336
|
+
insertMilestone({ id: 'M002', title: 'Second Milestone', status: 'active' });
|
|
337
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Done', status: 'complete', risk: 'low', depends: [] });
|
|
338
|
+
insertSlice({ id: 'S01', milestoneId: 'M002', title: 'In Progress', status: 'active', risk: 'low', depends: [] });
|
|
317
339
|
insertArtifactRow('milestones/M001/M001-ROADMAP.md', completedRoadmap, {
|
|
318
340
|
artifact_type: 'roadmap',
|
|
319
341
|
milestone_id: 'M001',
|
|
@@ -355,6 +377,10 @@ describe('derive-state-db', async () => {
|
|
|
355
377
|
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
356
378
|
|
|
357
379
|
openDatabase(':memory:');
|
|
380
|
+
// Insert milestone/slice/task rows so deriveState takes the DB path (#2631 fix)
|
|
381
|
+
insertMilestone({ id: 'M001', title: 'Test Milestone', status: 'active' });
|
|
382
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First Slice', status: 'active', risk: 'low', depends: [] });
|
|
383
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First Task', status: 'pending' });
|
|
358
384
|
insertArtifactRow('milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT, {
|
|
359
385
|
artifact_type: 'roadmap',
|
|
360
386
|
milestone_id: 'M001',
|
|
@@ -378,6 +404,8 @@ describe('derive-state-db', async () => {
|
|
|
378
404
|
});
|
|
379
405
|
// Also update file on disk (cachedLoadFile may read from disk for some paths)
|
|
380
406
|
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', updatedPlan);
|
|
407
|
+
// Update task status in DB so DB-path also sees completion (#2631 fix)
|
|
408
|
+
updateTaskStatus('M001', 'S01', 'T01', 'complete');
|
|
381
409
|
|
|
382
410
|
// Without invalidation, should return cached result (T01 still active)
|
|
383
411
|
const state2 = await deriveState(base);
|
|
@@ -99,7 +99,7 @@ test("detectProjectState: detects preferences in .gsd/", (t) => {
|
|
|
99
99
|
t.after(() => cleanup(dir));
|
|
100
100
|
|
|
101
101
|
mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
|
|
102
|
-
writeFileSync(join(dir, ".gsd", "
|
|
102
|
+
writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), "---\nversion: 1\n---\n", "utf-8");
|
|
103
103
|
const result = detectProjectState(dir);
|
|
104
104
|
assert.ok(result.v2);
|
|
105
105
|
assert.equal(result.v2!.hasPreferences, true);
|
|
@@ -64,11 +64,11 @@ _None_
|
|
|
64
64
|
return dir;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
/** Write a .gsd/
|
|
67
|
+
/** Write a .gsd/PREFERENCES.md with the given git isolation mode. */
|
|
68
68
|
function writePreferencesFile(dir: string, isolation: "none" | "worktree" | "branch"): void {
|
|
69
69
|
const gsdDir = join(dir, ".gsd");
|
|
70
70
|
mkdirSync(gsdDir, { recursive: true });
|
|
71
|
-
writeFileSync(join(gsdDir, "
|
|
71
|
+
writeFileSync(join(gsdDir, "PREFERENCES.md"), `---\ngit:\n isolation: "${isolation}"\n---\n`);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/** Create a repo with an in-progress milestone. */
|
|
@@ -302,7 +302,7 @@ describe('doctor-git', async () => {
|
|
|
302
302
|
// ─── Test 7: none-mode skips orphaned worktree check ───────────────
|
|
303
303
|
// NOTE: loadEffectiveGSDPreferences() resolves PROJECT_PREFERENCES_PATH
|
|
304
304
|
// at module load time from process.cwd(). We write the prefs file to
|
|
305
|
-
// the test runner's cwd .gsd/
|
|
305
|
+
// the test runner's cwd .gsd/PREFERENCES.md and clean up afterwards.
|
|
306
306
|
if (process.platform !== "win32") {
|
|
307
307
|
test('none-mode skips orphaned worktree', async () => {
|
|
308
308
|
const dir = createRepoWithCompletedMilestone();
|
|
@@ -409,7 +409,7 @@ describe('doctor-git', async () => {
|
|
|
409
409
|
cleanups.push(dir);
|
|
410
410
|
|
|
411
411
|
run("git branch trunk", dir);
|
|
412
|
-
writeFileSync(join(dir, ".gsd", "
|
|
412
|
+
writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), `---\ngit:\n isolation: "worktree"\n main_branch: "trunk"\n---\n`);
|
|
413
413
|
|
|
414
414
|
const metaPath = join(dir, ".gsd", "milestones", "M001", "M001-META.json");
|
|
415
415
|
writeFileSync(metaPath, JSON.stringify({ integrationBranch: "feat/does-not-exist" }, null, 2));
|
|
@@ -297,7 +297,7 @@ describe('doctor-proactive', async () => {
|
|
|
297
297
|
cleanups.push(dir);
|
|
298
298
|
|
|
299
299
|
run("git branch trunk", dir);
|
|
300
|
-
writeFileSync(join(dir, ".gsd", "
|
|
300
|
+
writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), `---\ngit:\n main_branch: "trunk"\n---\n`);
|
|
301
301
|
const metaPath = join(dir, ".gsd", "milestones", "M001", "M001-META.json");
|
|
302
302
|
writeFileSync(metaPath, JSON.stringify({ integrationBranch: "feature/missing" }, null, 2));
|
|
303
303
|
|
|
@@ -419,7 +419,7 @@ test("runProviderChecks uses provider-qualified anthropic-vertex model IDs", ()
|
|
|
419
419
|
const repo = realpathSync(mkdtempSync(join(tmpdir(), "gsd-providers-vertex-prefix-repo-")));
|
|
420
420
|
mkdirSync(join(repo, ".gsd"), { recursive: true });
|
|
421
421
|
writeFileSync(
|
|
422
|
-
join(repo, ".gsd", "
|
|
422
|
+
join(repo, ".gsd", "PREFERENCES.md"),
|
|
423
423
|
[
|
|
424
424
|
"---",
|
|
425
425
|
"models:",
|
|
@@ -454,7 +454,7 @@ test("runProviderChecks uses object provider field for anthropic-vertex models",
|
|
|
454
454
|
const repo = realpathSync(mkdtempSync(join(tmpdir(), "gsd-providers-vertex-provider-repo-")));
|
|
455
455
|
mkdirSync(join(repo, ".gsd"), { recursive: true });
|
|
456
456
|
writeFileSync(
|
|
457
|
-
join(repo, ".gsd", "
|
|
457
|
+
join(repo, ".gsd", "PREFERENCES.md"),
|
|
458
458
|
[
|
|
459
459
|
"---",
|
|
460
460
|
"models:",
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #2631: deriveState disk→DB reconciliation must
|
|
3
|
+
* run even when the milestones table starts empty.
|
|
4
|
+
*
|
|
5
|
+
* When getAllMilestones() returns [] (e.g. after a failed initial migration),
|
|
6
|
+
* the reconciliation code inside deriveStateFromDb was unreachable because
|
|
7
|
+
* deriveState only called it when dbMilestones.length > 0. The fix moves
|
|
8
|
+
* disk→DB sync into deriveState itself, before the length check.
|
|
9
|
+
*/
|
|
10
|
+
import { test } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
|
|
16
|
+
import { deriveState, invalidateStateCache } from "../state.ts";
|
|
17
|
+
import {
|
|
18
|
+
openDatabase,
|
|
19
|
+
closeDatabase,
|
|
20
|
+
getAllMilestones,
|
|
21
|
+
} from "../gsd-db.ts";
|
|
22
|
+
|
|
23
|
+
test("deriveState populates empty DB from disk milestones (#2631)", async () => {
|
|
24
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-empty-db-"));
|
|
25
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// Create a milestone on disk with a CONTEXT file (not a ghost)
|
|
29
|
+
writeFileSync(
|
|
30
|
+
join(base, ".gsd", "milestones", "M001", "M001-CONTEXT.md"),
|
|
31
|
+
"# M001: Test Milestone\n\nSome context about this milestone.",
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Open DB — milestones table is empty (simulating failed migration)
|
|
35
|
+
openDatabase(":memory:");
|
|
36
|
+
const before = getAllMilestones();
|
|
37
|
+
assert.equal(before.length, 0, "DB should start with 0 milestones");
|
|
38
|
+
|
|
39
|
+
// deriveState should reconcile disk → DB
|
|
40
|
+
invalidateStateCache();
|
|
41
|
+
const state = await deriveState(base);
|
|
42
|
+
|
|
43
|
+
// After deriveState, the DB should now have the disk milestone
|
|
44
|
+
const after = getAllMilestones();
|
|
45
|
+
assert.ok(after.length > 0, "DB should have milestones after reconciliation");
|
|
46
|
+
assert.equal(after[0]!.id, "M001", "reconciled milestone should be M001");
|
|
47
|
+
|
|
48
|
+
// State should reflect the milestone (not "No milestones found")
|
|
49
|
+
assert.ok(
|
|
50
|
+
state.activeMilestone !== null,
|
|
51
|
+
"activeMilestone should not be null after reconciliation",
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
closeDatabase();
|
|
55
|
+
} finally {
|
|
56
|
+
closeDatabase();
|
|
57
|
+
rmSync(base, { recursive: true, force: true });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("deriveState does NOT insert ghost milestones into DB (#2631 guard)", async () => {
|
|
62
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-empty-db-"));
|
|
63
|
+
// Create a ghost milestone directory (empty — no CONTEXT, no ROADMAP)
|
|
64
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
openDatabase(":memory:");
|
|
68
|
+
invalidateStateCache();
|
|
69
|
+
await deriveState(base);
|
|
70
|
+
|
|
71
|
+
const milestones = getAllMilestones();
|
|
72
|
+
assert.equal(milestones.length, 0, "ghost milestone should NOT be inserted");
|
|
73
|
+
|
|
74
|
+
closeDatabase();
|
|
75
|
+
} finally {
|
|
76
|
+
closeDatabase();
|
|
77
|
+
rmSync(base, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
});
|