gsd-pi 2.51.0 → 2.52.0-dev.655ad8a
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 +59 -36
- package/dist/headless-events.d.ts +18 -0
- package/dist/headless-events.js +36 -0
- package/dist/headless-query.js +1 -1
- 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/get-secrets-from-user.js +7 -0
- package/dist/resources/extensions/gsd/auto/phases.js +34 -8
- package/dist/resources/extensions/gsd/auto-dispatch.js +23 -1
- 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 +91 -14
- package/dist/resources/extensions/gsd/auto.js +30 -4
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +99 -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/guided-flow.js +4 -3
- 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/parallel-orchestrator.js +18 -2
- 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 +38 -30
- 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 +14 -4
- 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 +11 -11
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +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 +11 -11
- 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.87fd909ae0110f50.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-bca0e732db0dcec3.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/get-secrets-from-user.ts +8 -0
- package/src/resources/extensions/gsd/auto/phases.ts +44 -7
- package/src/resources/extensions/gsd/auto-dispatch.ts +25 -1
- 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 +94 -14
- package/src/resources/extensions/gsd/auto.ts +31 -4
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +118 -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/guided-flow.ts +4 -3
- 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/parallel-orchestrator.ts +23 -1
- 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 +39 -30
- package/src/resources/extensions/gsd/status-guards.ts +13 -0
- package/src/resources/extensions/gsd/tests/active-milestone-id-guard.test.ts +91 -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/auto-stale-lock-self-kill.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +80 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +1 -1
- 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/collect-from-manifest.test.ts +39 -0
- 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 +65 -31
- 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/milestone-report-path.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/parallel-orchestrator-zombie-cleanup.test.ts +277 -0
- package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +103 -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/rate-limit-model-fallback.test.ts +90 -0
- 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/session-lock-transient-read.test.ts +9 -8
- package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +125 -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/validate-milestone.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/validation-gate-patterns.test.ts +124 -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 +22 -4
- 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 → zpvUPKoW5jRAMB_fWHlPi}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → zpvUPKoW5jRAMB_fWHlPi}/_ssgManifest.js +0 -0
- /package/src/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
|
|
37
37
|
import { findMilestoneIds } from './milestone-ids.js';
|
|
38
38
|
import { loadQueueOrder, sortByQueueOrder } from './queue-order.js';
|
|
39
|
+
import { isClosedStatus } from './status-guards.js';
|
|
39
40
|
import { nativeBatchParseGsdFiles, type BatchParsedFile } from './native-parser-bridge.js';
|
|
40
41
|
|
|
41
42
|
import { join, resolve } from 'path';
|
|
@@ -89,18 +90,13 @@ export function isMilestoneComplete(roadmap: Roadmap): boolean {
|
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
/**
|
|
92
|
-
* Check whether a VALIDATION file's verdict is terminal
|
|
93
|
-
*
|
|
94
|
-
*
|
|
93
|
+
* Check whether a VALIDATION file's verdict is terminal.
|
|
94
|
+
* Any successfully extracted verdict (pass, needs-attention, needs-remediation,
|
|
95
|
+
* fail, etc.) means validation completed. Only return false when no verdict
|
|
96
|
+
* could be parsed — i.e. extractVerdict() returns undefined (#2769).
|
|
95
97
|
*/
|
|
96
98
|
export function isValidationTerminal(validationContent: string): boolean {
|
|
97
|
-
|
|
98
|
-
if (!v) return false;
|
|
99
|
-
// 'pass' and 'needs-attention' are always terminal.
|
|
100
|
-
// 'needs-remediation' is treated as terminal to prevent infinite loops
|
|
101
|
-
// when no remediation slices exist in the roadmap (#832). The validation
|
|
102
|
-
// report is preserved on disk for manual review.
|
|
103
|
-
return v === 'pass' || v === 'needs-attention' || v === 'needs-remediation';
|
|
99
|
+
return extractVerdict(validationContent) != null;
|
|
104
100
|
}
|
|
105
101
|
|
|
106
102
|
// ─── State Derivation ──────────────────────────────────────────────────────
|
|
@@ -211,7 +207,24 @@ export async function deriveState(basePath: string): Promise<GSDState> {
|
|
|
211
207
|
|
|
212
208
|
// Dual-path: try DB-backed derivation first when hierarchy tables are populated
|
|
213
209
|
if (isDbAvailable()) {
|
|
214
|
-
|
|
210
|
+
let dbMilestones = getAllMilestones();
|
|
211
|
+
|
|
212
|
+
// Disk→DB reconciliation (#2631): when the milestones table is empty
|
|
213
|
+
// (e.g. failed initial migration per #2529), the reconciliation code
|
|
214
|
+
// inside deriveStateFromDb is unreachable. Populate from disk here so
|
|
215
|
+
// the DB path activates correctly.
|
|
216
|
+
if (dbMilestones.length === 0) {
|
|
217
|
+
const diskIds = findMilestoneIds(basePath);
|
|
218
|
+
let synced = false;
|
|
219
|
+
for (const diskId of diskIds) {
|
|
220
|
+
if (!isGhostMilestone(basePath, diskId)) {
|
|
221
|
+
insertMilestone({ id: diskId, status: 'active' });
|
|
222
|
+
synced = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (synced) dbMilestones = getAllMilestones();
|
|
226
|
+
}
|
|
227
|
+
|
|
215
228
|
if (dbMilestones.length > 0) {
|
|
216
229
|
const stopDbTimer = debugTime("derive-state-db");
|
|
217
230
|
result = await deriveStateFromDb(basePath);
|
|
@@ -255,13 +268,6 @@ function extractContextTitle(content: string | null, fallback: string): string {
|
|
|
255
268
|
|
|
256
269
|
// ─── DB-backed State Derivation ────────────────────────────────────────────
|
|
257
270
|
|
|
258
|
-
/**
|
|
259
|
-
* Helper: check if a DB status counts as "done" (handles K002 ambiguity).
|
|
260
|
-
*/
|
|
261
|
-
function isStatusDone(status: string): boolean {
|
|
262
|
-
return status === 'complete' || status === 'done';
|
|
263
|
-
}
|
|
264
|
-
|
|
265
271
|
/**
|
|
266
272
|
* Derive GSD state from the milestones/slices/tasks DB tables.
|
|
267
273
|
* Flag files (PARKED, VALIDATION, CONTINUE, REPLAN, REPLAN-TRIGGER, CONTEXT-DRAFT)
|
|
@@ -351,7 +357,7 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
351
357
|
continue;
|
|
352
358
|
}
|
|
353
359
|
|
|
354
|
-
if (
|
|
360
|
+
if (isClosedStatus(m.status)) {
|
|
355
361
|
completeMilestoneIds.add(m.id);
|
|
356
362
|
continue;
|
|
357
363
|
}
|
|
@@ -365,7 +371,7 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
365
371
|
|
|
366
372
|
// Check roadmap: all slices done means milestone is complete
|
|
367
373
|
const slices = getMilestoneSlices(m.id);
|
|
368
|
-
if (slices.length > 0 && slices.every(s =>
|
|
374
|
+
if (slices.length > 0 && slices.every(s => isClosedStatus(s.status))) {
|
|
369
375
|
// All slices done but no summary — still counts as complete for dep resolution
|
|
370
376
|
// if a summary file exists
|
|
371
377
|
// Note: without summary file, the milestone is in validating/completing state, not complete
|
|
@@ -387,7 +393,7 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
387
393
|
|
|
388
394
|
// Ghost milestone check: no slices in DB AND no substantive files on disk
|
|
389
395
|
const slices = getMilestoneSlices(m.id);
|
|
390
|
-
if (slices.length === 0 && !
|
|
396
|
+
if (slices.length === 0 && !isClosedStatus(m.status)) {
|
|
391
397
|
// Check disk for ghost detection
|
|
392
398
|
if (isGhostMilestone(basePath, m.id)) continue;
|
|
393
399
|
}
|
|
@@ -410,7 +416,7 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
410
416
|
}
|
|
411
417
|
|
|
412
418
|
// Not complete — determine if it should be active
|
|
413
|
-
const allSlicesDone = slices.length > 0 && slices.every(s =>
|
|
419
|
+
const allSlicesDone = slices.length > 0 && slices.every(s => isClosedStatus(s.status));
|
|
414
420
|
|
|
415
421
|
// Get title — prefer DB, fall back to context file extraction
|
|
416
422
|
let title = stripMilestonePrefix(m.title) || m.id;
|
|
@@ -562,7 +568,10 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
562
568
|
}
|
|
563
569
|
|
|
564
570
|
// ── All slices done → validating/completing ─────────────────────────
|
|
565
|
-
|
|
571
|
+
// Guard: [].every() === true (vacuous truth). Without the length check,
|
|
572
|
+
// an empty slice array causes a premature phase transition to
|
|
573
|
+
// validating-milestone. See: https://github.com/gsd-build/gsd-2/issues/2667
|
|
574
|
+
const allSlicesDone = activeMilestoneSlices.length > 0 && activeMilestoneSlices.every(s => isClosedStatus(s.status));
|
|
566
575
|
if (allSlicesDone) {
|
|
567
576
|
const validationFile = resolveMilestoneFile(basePath, activeMilestone.id, "VALIDATION");
|
|
568
577
|
const validationContent = validationFile ? await loadFile(validationFile) : null;
|
|
@@ -595,19 +604,19 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
595
604
|
|
|
596
605
|
// ── Find active slice (first incomplete with deps satisfied) ─────────
|
|
597
606
|
const sliceProgress = {
|
|
598
|
-
done: activeMilestoneSlices.filter(s =>
|
|
607
|
+
done: activeMilestoneSlices.filter(s => isClosedStatus(s.status)).length,
|
|
599
608
|
total: activeMilestoneSlices.length,
|
|
600
609
|
};
|
|
601
610
|
|
|
602
611
|
const doneSliceIds = new Set(
|
|
603
|
-
activeMilestoneSlices.filter(s =>
|
|
612
|
+
activeMilestoneSlices.filter(s => isClosedStatus(s.status)).map(s => s.id)
|
|
604
613
|
);
|
|
605
614
|
|
|
606
615
|
let activeSlice: ActiveRef | null = null;
|
|
607
616
|
let activeSliceRow: SliceRow | null = null;
|
|
608
617
|
|
|
609
618
|
for (const s of activeMilestoneSlices) {
|
|
610
|
-
if (
|
|
619
|
+
if (isClosedStatus(s.status)) continue;
|
|
611
620
|
if (s.depends.every(dep => doneSliceIds.has(dep))) {
|
|
612
621
|
activeSlice = { id: s.id, title: s.title };
|
|
613
622
|
activeSliceRow = s;
|
|
@@ -650,7 +659,7 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
650
659
|
// causing the dispatcher to re-dispatch the same completed task forever.
|
|
651
660
|
let reconciled = false;
|
|
652
661
|
for (const t of tasks) {
|
|
653
|
-
if (
|
|
662
|
+
if (isClosedStatus(t.status)) continue;
|
|
654
663
|
const summaryPath = resolveTaskFile(basePath, activeMilestone.id, activeSlice.id, t.id, "SUMMARY");
|
|
655
664
|
if (summaryPath && existsSync(summaryPath)) {
|
|
656
665
|
try {
|
|
@@ -673,11 +682,11 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
673
682
|
}
|
|
674
683
|
|
|
675
684
|
const taskProgress = {
|
|
676
|
-
done: tasks.filter(t =>
|
|
685
|
+
done: tasks.filter(t => isClosedStatus(t.status)).length,
|
|
677
686
|
total: tasks.length,
|
|
678
687
|
};
|
|
679
688
|
|
|
680
|
-
const activeTaskRow = tasks.find(t => !
|
|
689
|
+
const activeTaskRow = tasks.find(t => !isClosedStatus(t.status));
|
|
681
690
|
|
|
682
691
|
if (!activeTaskRow && tasks.length > 0) {
|
|
683
692
|
// All tasks done but slice not marked complete → summarizing
|
|
@@ -738,7 +747,7 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
738
747
|
}
|
|
739
748
|
|
|
740
749
|
// ── Blocker detection: check completed tasks for blocker_discovered ──
|
|
741
|
-
const completedTasks = tasks.filter(t =>
|
|
750
|
+
const completedTasks = tasks.filter(t => isClosedStatus(t.status));
|
|
742
751
|
let blockerTaskId: string | null = null;
|
|
743
752
|
for (const ct of completedTasks) {
|
|
744
753
|
if (ct.blocker_discovered) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status predicates for GSD state-machine guards.
|
|
3
|
+
*
|
|
4
|
+
* The DB stores status as free-form strings. Two values indicate
|
|
5
|
+
* "closed": "complete" (canonical) and "done" (legacy / alias).
|
|
6
|
+
* Every inline `status === "complete" || status === "done"` should
|
|
7
|
+
* use isClosedStatus() instead.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Returns true when a milestone, slice, or task status indicates closure. */
|
|
11
|
+
export function isClosedStatus(status: string): boolean {
|
|
12
|
+
return status === "complete" || status === "done";
|
|
13
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #2773 — activeMilestone.id guard
|
|
3
|
+
*
|
|
4
|
+
* When activeMilestone is a non-null object with `id: undefined` (corrupted
|
|
5
|
+
* state), the old `!state.activeMilestone` truthiness check passed through,
|
|
6
|
+
* causing a downstream crash when code assumed `.id` was a valid string.
|
|
7
|
+
*
|
|
8
|
+
* The fix uses optional chaining (`!state.activeMilestone?.id`) so all three
|
|
9
|
+
* "no usable milestone" shapes are caught:
|
|
10
|
+
* 1. activeMilestone === null
|
|
11
|
+
* 2. activeMilestone === undefined
|
|
12
|
+
* 3. activeMilestone === { id: undefined, title: "..." }
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, it } from 'node:test'
|
|
16
|
+
import assert from 'node:assert/strict'
|
|
17
|
+
|
|
18
|
+
import type { GSDState, ActiveRef } from '../types.ts'
|
|
19
|
+
|
|
20
|
+
// ─── Guard Under Test ────────────────────────────────────────────────────────
|
|
21
|
+
// Extracted guard logic identical to headless-query.ts (line 74) and
|
|
22
|
+
// guided-flow.ts (lines 522, 1047).
|
|
23
|
+
|
|
24
|
+
function activeMilestoneIsUsable(activeMilestone: ActiveRef | null | undefined): boolean {
|
|
25
|
+
return !!activeMilestone?.id
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
describe('activeMilestone?.id guard (#2773)', () => {
|
|
31
|
+
it('rejects null activeMilestone', () => {
|
|
32
|
+
assert.equal(activeMilestoneIsUsable(null), false)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('rejects undefined activeMilestone', () => {
|
|
36
|
+
assert.equal(activeMilestoneIsUsable(undefined), false)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('rejects malformed activeMilestone with id: undefined', () => {
|
|
40
|
+
// This is the crash case from #2773 — object exists but id is undefined
|
|
41
|
+
const malformed = { id: undefined, title: 'Ghost Milestone' } as unknown as ActiveRef
|
|
42
|
+
assert.equal(activeMilestoneIsUsable(malformed), false)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('rejects malformed activeMilestone with id: empty string', () => {
|
|
46
|
+
const malformed = { id: '', title: 'Empty ID Milestone' } as unknown as ActiveRef
|
|
47
|
+
assert.equal(activeMilestoneIsUsable(malformed), false)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('accepts valid activeMilestone with a real id', () => {
|
|
51
|
+
const valid: ActiveRef = { id: 'M001', title: 'Real Milestone' }
|
|
52
|
+
assert.equal(activeMilestoneIsUsable(valid), true)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
describe('headless-query stop behavior with corrupted milestone', () => {
|
|
57
|
+
// Simulates the decision logic from handleQuery (headless-query.ts:74-78)
|
|
58
|
+
function deriveNextAction(activeMilestone: ActiveRef | null | undefined, phase: string) {
|
|
59
|
+
if (!activeMilestone?.id) {
|
|
60
|
+
return {
|
|
61
|
+
action: 'stop' as const,
|
|
62
|
+
reason: phase === 'complete' ? 'All milestones complete.' : 'No active milestone.',
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { action: 'dispatch' as const, unitId: activeMilestone.id }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
it('returns stop when activeMilestone is null', () => {
|
|
69
|
+
const result = deriveNextAction(null, 'pre-planning')
|
|
70
|
+
assert.equal(result.action, 'stop')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('returns stop when activeMilestone has undefined id', () => {
|
|
74
|
+
const corrupted = { id: undefined, title: 'Corrupted' } as unknown as ActiveRef
|
|
75
|
+
const result = deriveNextAction(corrupted, 'executing')
|
|
76
|
+
assert.equal(result.action, 'stop')
|
|
77
|
+
assert.equal(result.reason, 'No active milestone.')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('returns dispatch with valid milestone id', () => {
|
|
81
|
+
const valid: ActiveRef = { id: 'M001', title: 'Valid' }
|
|
82
|
+
const result = deriveNextAction(valid, 'executing')
|
|
83
|
+
assert.equal(result.action, 'dispatch')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('returns correct stop reason when phase is complete', () => {
|
|
87
|
+
const result = deriveNextAction(null, 'complete')
|
|
88
|
+
assert.equal(result.action, 'stop')
|
|
89
|
+
assert.equal(result.reason, 'All milestones complete.')
|
|
90
|
+
})
|
|
91
|
+
})
|
|
@@ -102,7 +102,7 @@ test("pauseAuto calls resolveAgentEndCancelled to unblock the loop", () => {
|
|
|
102
102
|
const fnBlock = source.slice(fnIdx, source.indexOf("\n/**\n * Build", fnIdx + 100));
|
|
103
103
|
|
|
104
104
|
assert.ok(
|
|
105
|
-
fnBlock.includes("resolveAgentEndCancelled(
|
|
105
|
+
fnBlock.includes("resolveAgentEndCancelled("),
|
|
106
106
|
"pauseAuto must call resolveAgentEndCancelled to unblock the auto-loop promise",
|
|
107
107
|
);
|
|
108
108
|
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import { parseMilestoneTarget } from "../commands/handlers/auto.js";
|
|
5
|
+
|
|
6
|
+
describe("parseMilestoneTarget", () => {
|
|
7
|
+
it("extracts a simple milestone ID", () => {
|
|
8
|
+
const result = parseMilestoneTarget("auto M016");
|
|
9
|
+
assert.equal(result.milestoneId, "M016");
|
|
10
|
+
assert.equal(result.rest, "auto");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("extracts a milestone ID with unique suffix", () => {
|
|
14
|
+
const result = parseMilestoneTarget("auto M001-a3b4c5 --verbose");
|
|
15
|
+
assert.equal(result.milestoneId, "M001-a3b4c5");
|
|
16
|
+
assert.equal(result.rest, "auto --verbose");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("returns null when no milestone ID is present", () => {
|
|
20
|
+
const result = parseMilestoneTarget("auto --verbose");
|
|
21
|
+
assert.equal(result.milestoneId, null);
|
|
22
|
+
assert.equal(result.rest, "auto --verbose");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("extracts milestone ID with flags in any order", () => {
|
|
26
|
+
const result = parseMilestoneTarget("auto --verbose M003 --debug");
|
|
27
|
+
assert.equal(result.milestoneId, "M003");
|
|
28
|
+
assert.equal(result.rest, "auto --verbose --debug");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("returns null for plain 'auto'", () => {
|
|
32
|
+
const result = parseMilestoneTarget("auto");
|
|
33
|
+
assert.equal(result.milestoneId, null);
|
|
34
|
+
assert.equal(result.rest, "auto");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("extracts from 'next' command", () => {
|
|
38
|
+
const result = parseMilestoneTarget("next M012");
|
|
39
|
+
assert.equal(result.milestoneId, "M012");
|
|
40
|
+
assert.equal(result.rest, "next");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("handles milestone ID at the start of input", () => {
|
|
44
|
+
const result = parseMilestoneTarget("M007");
|
|
45
|
+
assert.equal(result.milestoneId, "M007");
|
|
46
|
+
assert.equal(result.rest, "");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("picks the first milestone ID when multiple appear", () => {
|
|
50
|
+
// Edge case: user accidentally types two. First one wins.
|
|
51
|
+
const result = parseMilestoneTarget("auto M001 M002");
|
|
52
|
+
assert.equal(result.milestoneId, "M001");
|
|
53
|
+
// M002 remains in rest since only the first match is removed
|
|
54
|
+
assert.ok(result.rest.includes("M002"));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("does not match bare numbers without M prefix", () => {
|
|
58
|
+
const result = parseMilestoneTarget("auto 016");
|
|
59
|
+
assert.equal(result.milestoneId, null);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdirSync, mkdtempSync, writeFileSync, existsSync, rmSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
|
|
7
|
+
import { writeLock, readCrashLock, clearLock } from "../crash-recovery.ts";
|
|
8
|
+
import { checkRemoteAutoSession, stopAutoRemote } from "../auto.ts";
|
|
9
|
+
|
|
10
|
+
function makeTmpProject(): string {
|
|
11
|
+
const dir = mkdtempSync(join(tmpdir(), "gsd-stale-lock-test-"));
|
|
12
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
13
|
+
return dir;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ─── checkRemoteAutoSession: own-PID filtering (#2730) ───────────────────
|
|
17
|
+
|
|
18
|
+
test("#2730: checkRemoteAutoSession returns { running: false } when lock PID matches current process", (t) => {
|
|
19
|
+
const dir = makeTmpProject();
|
|
20
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
21
|
+
|
|
22
|
+
// Write a lock with the current process PID — simulates a stale lock
|
|
23
|
+
// left behind after step-mode exit without full cleanup.
|
|
24
|
+
writeLock(dir, "execute-task", "M001/S01/T01");
|
|
25
|
+
|
|
26
|
+
const lock = readCrashLock(dir);
|
|
27
|
+
assert.ok(lock, "lock file should exist");
|
|
28
|
+
assert.equal(lock!.pid, process.pid, "lock should have our PID");
|
|
29
|
+
|
|
30
|
+
const result = checkRemoteAutoSession(dir);
|
|
31
|
+
assert.equal(result.running, false, "own PID must not be treated as a remote session");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("#2730: checkRemoteAutoSession still detects a genuine remote session (different PID)", (t) => {
|
|
35
|
+
const dir = makeTmpProject();
|
|
36
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
37
|
+
|
|
38
|
+
// Use parent PID — guaranteed alive, guaranteed not our PID.
|
|
39
|
+
const remotePid = process.ppid;
|
|
40
|
+
const lockData = {
|
|
41
|
+
pid: remotePid,
|
|
42
|
+
startedAt: new Date().toISOString(),
|
|
43
|
+
unitType: "execute-task",
|
|
44
|
+
unitId: "M001/S01/T02",
|
|
45
|
+
unitStartedAt: new Date().toISOString(),
|
|
46
|
+
};
|
|
47
|
+
writeFileSync(join(dir, ".gsd", "auto.lock"), JSON.stringify(lockData, null, 2));
|
|
48
|
+
|
|
49
|
+
const result = checkRemoteAutoSession(dir);
|
|
50
|
+
assert.equal(result.running, true, "different live PID should be detected as running");
|
|
51
|
+
assert.equal(result.pid, remotePid);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ─── stopAutoRemote: self-kill prevention (#2730) ────────────────────────
|
|
55
|
+
|
|
56
|
+
test("#2730: stopAutoRemote does not send SIGTERM when lock PID matches current process", (t) => {
|
|
57
|
+
const dir = makeTmpProject();
|
|
58
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
59
|
+
|
|
60
|
+
// Write a lock with our own PID
|
|
61
|
+
writeLock(dir, "execute-task", "M001/S01/T01");
|
|
62
|
+
|
|
63
|
+
const result = stopAutoRemote(dir);
|
|
64
|
+
assert.equal(result.found, false, "own PID must not be signalled");
|
|
65
|
+
|
|
66
|
+
// The lock should be cleared as part of the self-detection cleanup
|
|
67
|
+
assert.ok(!existsSync(join(dir, ".gsd", "auto.lock")), "stale self-lock should be cleared");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("#2730: stopAutoRemote clears stale lock from dead remote process without error", (t) => {
|
|
71
|
+
const dir = makeTmpProject();
|
|
72
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
73
|
+
|
|
74
|
+
// Simulate a stale lock from a process that no longer exists
|
|
75
|
+
const lockData = {
|
|
76
|
+
pid: 9999999,
|
|
77
|
+
startedAt: "2026-03-01T00:00:00Z",
|
|
78
|
+
unitType: "plan-slice",
|
|
79
|
+
unitId: "M001/S02",
|
|
80
|
+
unitStartedAt: "2026-03-01T00:05:00Z",
|
|
81
|
+
};
|
|
82
|
+
writeFileSync(join(dir, ".gsd", "auto.lock"), JSON.stringify(lockData, null, 2));
|
|
83
|
+
|
|
84
|
+
const result = stopAutoRemote(dir);
|
|
85
|
+
assert.equal(result.found, false, "dead remote PID should not be reported as found");
|
|
86
|
+
assert.ok(!existsSync(join(dir, ".gsd", "auto.lock")), "stale lock should be cleaned up");
|
|
87
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auto-worktree-auto-resolve.test.ts — Unit tests for isSafeToAutoResolve.
|
|
3
|
+
*
|
|
4
|
+
* Covers: .gsd/ state files, build artifacts (.tsbuildinfo, .pyc, __pycache__,
|
|
5
|
+
* .DS_Store, .map), and rejection of real source files.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, test } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
isSafeToAutoResolve,
|
|
13
|
+
SAFE_AUTO_RESOLVE_PATTERNS,
|
|
14
|
+
} from "../auto-worktree.ts";
|
|
15
|
+
|
|
16
|
+
describe("isSafeToAutoResolve", () => {
|
|
17
|
+
// ─── .gsd/ state files ───────────────────────────────────────────────────
|
|
18
|
+
test("returns true for .gsd/ prefixed paths", () => {
|
|
19
|
+
assert.ok(isSafeToAutoResolve(".gsd/STATE.md"));
|
|
20
|
+
assert.ok(isSafeToAutoResolve(".gsd/milestones/M001/CONTEXT.md"));
|
|
21
|
+
assert.ok(isSafeToAutoResolve(".gsd/gsd.db"));
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// ─── Build artifact patterns ─────────────────────────────────────────────
|
|
25
|
+
test("returns true for .tsbuildinfo files", () => {
|
|
26
|
+
assert.ok(isSafeToAutoResolve("tsconfig.tsbuildinfo"));
|
|
27
|
+
assert.ok(isSafeToAutoResolve("dist/tsconfig.tsbuildinfo"));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("returns true for .pyc files", () => {
|
|
31
|
+
assert.ok(isSafeToAutoResolve("module.pyc"));
|
|
32
|
+
assert.ok(isSafeToAutoResolve("src/utils/helpers.pyc"));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("returns true for __pycache__/ paths", () => {
|
|
36
|
+
assert.ok(isSafeToAutoResolve("src/__pycache__/module.cpython-311.pyc"));
|
|
37
|
+
assert.ok(isSafeToAutoResolve("lib/__pycache__/foo.py"));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("returns true for .DS_Store files", () => {
|
|
41
|
+
assert.ok(isSafeToAutoResolve(".DS_Store"));
|
|
42
|
+
assert.ok(isSafeToAutoResolve("src/.DS_Store"));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("returns true for .map source map files", () => {
|
|
46
|
+
assert.ok(isSafeToAutoResolve("dist/index.js.map"));
|
|
47
|
+
assert.ok(isSafeToAutoResolve("out/bundle.css.map"));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// ─── Real source files (should NOT be auto-resolved) ─────────────────────
|
|
51
|
+
test("returns false for .ts source files", () => {
|
|
52
|
+
assert.ok(!isSafeToAutoResolve("src/index.ts"));
|
|
53
|
+
assert.ok(!isSafeToAutoResolve("lib/utils.ts"));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("returns false for .js source files", () => {
|
|
57
|
+
assert.ok(!isSafeToAutoResolve("src/index.js"));
|
|
58
|
+
assert.ok(!isSafeToAutoResolve("lib/helpers.js"));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("returns false for .py source files", () => {
|
|
62
|
+
assert.ok(!isSafeToAutoResolve("src/main.py"));
|
|
63
|
+
assert.ok(!isSafeToAutoResolve("scripts/deploy.py"));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("returns false for config and data files", () => {
|
|
67
|
+
assert.ok(!isSafeToAutoResolve("package.json"));
|
|
68
|
+
assert.ok(!isSafeToAutoResolve("tsconfig.json"));
|
|
69
|
+
assert.ok(!isSafeToAutoResolve("README.md"));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ─── SAFE_AUTO_RESOLVE_PATTERNS export ────────────────────────────────────
|
|
73
|
+
test("SAFE_AUTO_RESOLVE_PATTERNS is a non-empty array of RegExp", () => {
|
|
74
|
+
assert.ok(Array.isArray(SAFE_AUTO_RESOLVE_PATTERNS));
|
|
75
|
+
assert.ok(SAFE_AUTO_RESOLVE_PATTERNS.length > 0);
|
|
76
|
+
for (const pattern of SAFE_AUTO_RESOLVE_PATTERNS) {
|
|
77
|
+
assert.ok(pattern instanceof RegExp);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -77,7 +77,7 @@ function addSliceToMilestone(
|
|
|
77
77
|
run(`git branch -d ${sliceBranch}`, wtPath);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
describe("auto-worktree-milestone-merge", () => {
|
|
80
|
+
describe("auto-worktree-milestone-merge", { timeout: 300_000 }, () => {
|
|
81
81
|
const savedCwd = process.cwd();
|
|
82
82
|
const tempDirs: string[] = [];
|
|
83
83
|
|