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
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for zombie worker cleanup (#2736).
|
|
3
|
+
*
|
|
4
|
+
* Verifies that:
|
|
5
|
+
* 1. refreshWorkerStatuses() deactivates the orchestrator when all workers
|
|
6
|
+
* are in terminal states (error/stopped).
|
|
7
|
+
* 2. restoreRuntimeState() (via getWorkerStatuses) returns empty when the
|
|
8
|
+
* cached state has only dead workers.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import test from "node:test";
|
|
12
|
+
import assert from "node:assert/strict";
|
|
13
|
+
import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { tmpdir } from "node:os";
|
|
16
|
+
import { randomUUID } from "node:crypto";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
persistState,
|
|
20
|
+
resetOrchestrator,
|
|
21
|
+
refreshWorkerStatuses,
|
|
22
|
+
isParallelActive,
|
|
23
|
+
getOrchestratorState,
|
|
24
|
+
getWorkerStatuses,
|
|
25
|
+
type PersistedState,
|
|
26
|
+
} from "../parallel-orchestrator.ts";
|
|
27
|
+
|
|
28
|
+
function makeTmpBase(): string {
|
|
29
|
+
const base = join(tmpdir(), `gsd-test-zombie-${randomUUID()}`);
|
|
30
|
+
mkdirSync(join(base, ".gsd", "parallel"), { recursive: true });
|
|
31
|
+
return base;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function cleanup(base: string): void {
|
|
35
|
+
try {
|
|
36
|
+
rmSync(base, { recursive: true, force: true });
|
|
37
|
+
} catch { /* non-fatal */ }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Write a fake orchestrator.json to simulate persisted state. */
|
|
41
|
+
function writePersistedState(basePath: string, data: PersistedState): void {
|
|
42
|
+
const dest = join(basePath, ".gsd", "orchestrator.json");
|
|
43
|
+
writeFileSync(dest, JSON.stringify(data, null, 2), "utf-8");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Write a fake session status file to .gsd/parallel/<milestoneId>.status.json */
|
|
47
|
+
function writeSessionStatusFile(
|
|
48
|
+
basePath: string,
|
|
49
|
+
milestoneId: string,
|
|
50
|
+
state: "running" | "paused" | "stopped" | "error",
|
|
51
|
+
pid: number,
|
|
52
|
+
): void {
|
|
53
|
+
const dest = join(basePath, ".gsd", "parallel", `${milestoneId}.status.json`);
|
|
54
|
+
writeFileSync(
|
|
55
|
+
dest,
|
|
56
|
+
JSON.stringify({
|
|
57
|
+
milestoneId,
|
|
58
|
+
pid,
|
|
59
|
+
state,
|
|
60
|
+
currentUnit: null,
|
|
61
|
+
completedUnits: 0,
|
|
62
|
+
cost: 0.5,
|
|
63
|
+
lastHeartbeat: Date.now(),
|
|
64
|
+
startedAt: Date.now() - 60_000,
|
|
65
|
+
worktreePath: join(basePath, "worktrees", milestoneId),
|
|
66
|
+
}),
|
|
67
|
+
"utf-8",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Use a PID that is guaranteed dead — PID 1 is init/launchd and won't be
|
|
72
|
+
// killable by this process, but 2147483647 is unlikely to exist.
|
|
73
|
+
const DEAD_PID = 2147483647;
|
|
74
|
+
|
|
75
|
+
// ─── refreshWorkerStatuses: deactivates when all workers dead ──────────
|
|
76
|
+
|
|
77
|
+
test("#2736: refreshWorkerStatuses deactivates orchestrator when all workers are error/stopped", (t) => {
|
|
78
|
+
const base = makeTmpBase();
|
|
79
|
+
t.after(() => {
|
|
80
|
+
resetOrchestrator();
|
|
81
|
+
cleanup(base);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Seed persisted state with two workers using current PID (alive) so
|
|
85
|
+
// restoreState() accepts them, then immediately mark them as error via
|
|
86
|
+
// session status files so refreshWorkerStatuses sees terminal states.
|
|
87
|
+
const persisted: PersistedState = {
|
|
88
|
+
active: true,
|
|
89
|
+
workers: [
|
|
90
|
+
{
|
|
91
|
+
milestoneId: "M001",
|
|
92
|
+
title: "Milestone 1",
|
|
93
|
+
pid: process.pid, // alive PID so restoreState accepts it
|
|
94
|
+
worktreePath: join(base, "worktrees", "M001"),
|
|
95
|
+
startedAt: Date.now() - 60_000,
|
|
96
|
+
state: "running",
|
|
97
|
+
cost: 1.0,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
milestoneId: "M002",
|
|
101
|
+
title: "Milestone 2",
|
|
102
|
+
pid: process.pid,
|
|
103
|
+
worktreePath: join(base, "worktrees", "M002"),
|
|
104
|
+
startedAt: Date.now() - 60_000,
|
|
105
|
+
state: "running",
|
|
106
|
+
cost: 0.5,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
totalCost: 1.5,
|
|
110
|
+
startedAt: Date.now() - 60_000,
|
|
111
|
+
configSnapshot: { max_workers: 3 },
|
|
112
|
+
};
|
|
113
|
+
writePersistedState(base, persisted);
|
|
114
|
+
|
|
115
|
+
// First, restore the state into memory via getWorkerStatuses (triggers restoreIfNeeded)
|
|
116
|
+
const workers = getWorkerStatuses(base);
|
|
117
|
+
assert.equal(workers.length, 2, "should have 2 workers after restore");
|
|
118
|
+
assert.ok(isParallelActive(), "orchestrator should be active after restore");
|
|
119
|
+
|
|
120
|
+
// Now write session status files marking both workers as error
|
|
121
|
+
writeSessionStatusFile(base, "M001", "error", process.pid);
|
|
122
|
+
writeSessionStatusFile(base, "M002", "error", process.pid);
|
|
123
|
+
|
|
124
|
+
// Refresh — should detect all-dead and deactivate
|
|
125
|
+
refreshWorkerStatuses(base);
|
|
126
|
+
|
|
127
|
+
assert.equal(isParallelActive(), false, "orchestrator should be inactive after all workers died");
|
|
128
|
+
assert.equal(getOrchestratorState(), null, "state should be null after cleanup");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("#2736: refreshWorkerStatuses keeps orchestrator active when some workers are still running", (t) => {
|
|
132
|
+
const base = makeTmpBase();
|
|
133
|
+
t.after(() => {
|
|
134
|
+
resetOrchestrator();
|
|
135
|
+
cleanup(base);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const persisted: PersistedState = {
|
|
139
|
+
active: true,
|
|
140
|
+
workers: [
|
|
141
|
+
{
|
|
142
|
+
milestoneId: "M001",
|
|
143
|
+
title: "Milestone 1",
|
|
144
|
+
pid: process.pid,
|
|
145
|
+
worktreePath: join(base, "worktrees", "M001"),
|
|
146
|
+
startedAt: Date.now() - 60_000,
|
|
147
|
+
state: "running",
|
|
148
|
+
cost: 1.0,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
milestoneId: "M002",
|
|
152
|
+
title: "Milestone 2",
|
|
153
|
+
pid: process.pid,
|
|
154
|
+
worktreePath: join(base, "worktrees", "M002"),
|
|
155
|
+
startedAt: Date.now() - 60_000,
|
|
156
|
+
state: "running",
|
|
157
|
+
cost: 0.5,
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
totalCost: 1.5,
|
|
161
|
+
startedAt: Date.now() - 60_000,
|
|
162
|
+
configSnapshot: { max_workers: 3 },
|
|
163
|
+
};
|
|
164
|
+
writePersistedState(base, persisted);
|
|
165
|
+
|
|
166
|
+
// Restore state
|
|
167
|
+
getWorkerStatuses(base);
|
|
168
|
+
|
|
169
|
+
// Mark M001 as error but keep M002 running
|
|
170
|
+
writeSessionStatusFile(base, "M001", "error", process.pid);
|
|
171
|
+
writeSessionStatusFile(base, "M002", "running", process.pid);
|
|
172
|
+
|
|
173
|
+
refreshWorkerStatuses(base);
|
|
174
|
+
|
|
175
|
+
assert.ok(isParallelActive(), "orchestrator should remain active with a running worker");
|
|
176
|
+
assert.ok(getOrchestratorState() !== null, "state should still exist");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// ─── restoreRuntimeState: returns false when cached state has only dead workers ─
|
|
180
|
+
|
|
181
|
+
test("#2736: getWorkerStatuses returns empty when all cached workers are in error state", (t) => {
|
|
182
|
+
const base = makeTmpBase();
|
|
183
|
+
t.after(() => {
|
|
184
|
+
resetOrchestrator();
|
|
185
|
+
cleanup(base);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// First, set up active state with live workers
|
|
189
|
+
const persisted: PersistedState = {
|
|
190
|
+
active: true,
|
|
191
|
+
workers: [
|
|
192
|
+
{
|
|
193
|
+
milestoneId: "M001",
|
|
194
|
+
title: "Milestone 1",
|
|
195
|
+
pid: process.pid,
|
|
196
|
+
worktreePath: join(base, "worktrees", "M001"),
|
|
197
|
+
startedAt: Date.now() - 60_000,
|
|
198
|
+
state: "running",
|
|
199
|
+
cost: 0.5,
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
totalCost: 0.5,
|
|
203
|
+
startedAt: Date.now() - 60_000,
|
|
204
|
+
configSnapshot: { max_workers: 3 },
|
|
205
|
+
};
|
|
206
|
+
writePersistedState(base, persisted);
|
|
207
|
+
|
|
208
|
+
// Restore into memory
|
|
209
|
+
getWorkerStatuses(base);
|
|
210
|
+
assert.ok(isParallelActive(), "should be active initially");
|
|
211
|
+
|
|
212
|
+
// Simulate all workers dying: write error status then refresh to update
|
|
213
|
+
writeSessionStatusFile(base, "M001", "error", process.pid);
|
|
214
|
+
refreshWorkerStatuses(base);
|
|
215
|
+
|
|
216
|
+
// State should now be cleared
|
|
217
|
+
assert.equal(getOrchestratorState(), null, "state should be null after all workers error");
|
|
218
|
+
|
|
219
|
+
// Reset and try again — getWorkerStatuses with restoreIfNeeded should
|
|
220
|
+
// find no live workers on disk (orchestrator.json was cleaned up)
|
|
221
|
+
const workers = getWorkerStatuses(base);
|
|
222
|
+
assert.equal(workers.length, 0, "should return empty when no live workers exist");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("#2736: restoreRuntimeState clears stale state when all workers are stopped", (t) => {
|
|
226
|
+
const base = makeTmpBase();
|
|
227
|
+
t.after(() => {
|
|
228
|
+
resetOrchestrator();
|
|
229
|
+
cleanup(base);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Set up and restore state
|
|
233
|
+
const persisted: PersistedState = {
|
|
234
|
+
active: true,
|
|
235
|
+
workers: [
|
|
236
|
+
{
|
|
237
|
+
milestoneId: "M001",
|
|
238
|
+
title: "Milestone 1",
|
|
239
|
+
pid: process.pid,
|
|
240
|
+
worktreePath: join(base, "worktrees", "M001"),
|
|
241
|
+
startedAt: Date.now() - 60_000,
|
|
242
|
+
state: "running",
|
|
243
|
+
cost: 0.3,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
milestoneId: "M002",
|
|
247
|
+
title: "Milestone 2",
|
|
248
|
+
pid: process.pid,
|
|
249
|
+
worktreePath: join(base, "worktrees", "M002"),
|
|
250
|
+
startedAt: Date.now() - 60_000,
|
|
251
|
+
state: "running",
|
|
252
|
+
cost: 0.7,
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
totalCost: 1.0,
|
|
256
|
+
startedAt: Date.now() - 60_000,
|
|
257
|
+
configSnapshot: { max_workers: 3 },
|
|
258
|
+
};
|
|
259
|
+
writePersistedState(base, persisted);
|
|
260
|
+
|
|
261
|
+
// Restore into memory
|
|
262
|
+
getWorkerStatuses(base);
|
|
263
|
+
assert.ok(isParallelActive(), "should be active initially");
|
|
264
|
+
|
|
265
|
+
// Mark all as stopped via session status, then refresh
|
|
266
|
+
writeSessionStatusFile(base, "M001", "stopped", process.pid);
|
|
267
|
+
writeSessionStatusFile(base, "M002", "stopped", process.pid);
|
|
268
|
+
refreshWorkerStatuses(base);
|
|
269
|
+
|
|
270
|
+
// Orchestrator should be deactivated and state cleaned
|
|
271
|
+
assert.equal(isParallelActive(), false, "should be inactive after all workers stopped");
|
|
272
|
+
assert.equal(getOrchestratorState(), null, "state should be null");
|
|
273
|
+
|
|
274
|
+
// Verify the state file was removed
|
|
275
|
+
const stateFile = join(base, ".gsd", "orchestrator.json");
|
|
276
|
+
assert.equal(existsSync(stateFile), false, "orchestrator.json should be removed");
|
|
277
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #2694: parkMilestone and unparkMilestone must
|
|
3
|
+
* update the DB milestone status alongside the filesystem marker.
|
|
4
|
+
*
|
|
5
|
+
* Without this, deriveStateFromDb skips unparked milestones because
|
|
6
|
+
* the DB still has status='parked', causing "All milestones complete".
|
|
7
|
+
*/
|
|
8
|
+
import { test } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
|
|
14
|
+
import { parkMilestone, unparkMilestone } from "../milestone-actions.ts";
|
|
15
|
+
import {
|
|
16
|
+
openDatabase,
|
|
17
|
+
closeDatabase,
|
|
18
|
+
insertMilestone,
|
|
19
|
+
getMilestone,
|
|
20
|
+
} from "../gsd-db.ts";
|
|
21
|
+
|
|
22
|
+
function createBase(): string {
|
|
23
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-park-db-"));
|
|
24
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
25
|
+
writeFileSync(
|
|
26
|
+
join(base, ".gsd", "milestones", "M001", "M001-CONTEXT.md"),
|
|
27
|
+
"# M001\n\nContext.",
|
|
28
|
+
);
|
|
29
|
+
return base;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test("parkMilestone updates DB status to 'parked' (#2694)", () => {
|
|
33
|
+
const base = createBase();
|
|
34
|
+
try {
|
|
35
|
+
openDatabase(":memory:");
|
|
36
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
37
|
+
|
|
38
|
+
assert.equal(getMilestone("M001")!.status, "active", "starts active");
|
|
39
|
+
|
|
40
|
+
parkMilestone(base, "M001", "deprioritized");
|
|
41
|
+
|
|
42
|
+
assert.equal(getMilestone("M001")!.status, "parked", "DB status should be parked");
|
|
43
|
+
|
|
44
|
+
closeDatabase();
|
|
45
|
+
} finally {
|
|
46
|
+
closeDatabase();
|
|
47
|
+
rmSync(base, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("unparkMilestone updates DB status to 'active' (#2694)", () => {
|
|
52
|
+
const base = createBase();
|
|
53
|
+
try {
|
|
54
|
+
openDatabase(":memory:");
|
|
55
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
56
|
+
|
|
57
|
+
// Park first
|
|
58
|
+
parkMilestone(base, "M001", "deprioritized");
|
|
59
|
+
assert.equal(getMilestone("M001")!.status, "parked");
|
|
60
|
+
|
|
61
|
+
// Unpark
|
|
62
|
+
unparkMilestone(base, "M001");
|
|
63
|
+
assert.equal(getMilestone("M001")!.status, "active", "DB status should be active after unpark");
|
|
64
|
+
|
|
65
|
+
closeDatabase();
|
|
66
|
+
} finally {
|
|
67
|
+
closeDatabase();
|
|
68
|
+
rmSync(base, { recursive: true, force: true });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("park/unpark are safe when DB is not available (#2694 guard)", () => {
|
|
73
|
+
const base = createBase();
|
|
74
|
+
try {
|
|
75
|
+
// No openDatabase — DB not available
|
|
76
|
+
// park/unpark should still work (filesystem-only, no throw)
|
|
77
|
+
const parked = parkMilestone(base, "M001", "test");
|
|
78
|
+
assert.ok(parked, "parkMilestone succeeds without DB");
|
|
79
|
+
|
|
80
|
+
const unparked = unparkMilestone(base, "M001");
|
|
81
|
+
assert.ok(unparked, "unparkMilestone succeeds without DB");
|
|
82
|
+
} finally {
|
|
83
|
+
rmSync(base, { recursive: true, force: true });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* phases-merge-error-stops-auto.test.ts — Regression test for #2766.
|
|
3
|
+
*
|
|
4
|
+
* When mergeAndExit throws a non-MergeConflictError, the auto loop must
|
|
5
|
+
* stop instead of continuing with unmerged work. This test verifies that
|
|
6
|
+
* all catch blocks in auto/phases.ts that handle mergeAndExit errors
|
|
7
|
+
* call stopAuto and return { action: "break" } for non-conflict errors.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
13
|
+
|
|
14
|
+
const { assertTrue, report } = createTestContext();
|
|
15
|
+
|
|
16
|
+
const phasesPath = join(import.meta.dirname, "..", "auto", "phases.ts");
|
|
17
|
+
const phasesSrc = readFileSync(phasesPath, "utf-8");
|
|
18
|
+
|
|
19
|
+
console.log("\n=== #2766: Non-MergeConflictError stops auto mode ===");
|
|
20
|
+
|
|
21
|
+
// ── Test 1: phases.ts calls logError for non-conflict merge errors ──────
|
|
22
|
+
|
|
23
|
+
assertTrue(
|
|
24
|
+
phasesPath.length > 0 && phasesPath.endsWith("phases.ts"),
|
|
25
|
+
"phases.ts file exists and is readable",
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// Count all mergeAndExit catch blocks by finding "} catch (mergeErr)" patterns
|
|
29
|
+
const mergeErrCatches = [...phasesPath.matchAll(/\} catch \(mergeErr\)/g)];
|
|
30
|
+
// Use the source itself for matching
|
|
31
|
+
const mergeErrCatchCount = [...phasesSrc.matchAll(/\} catch \(mergeErr\)/g)].length;
|
|
32
|
+
assertTrue(
|
|
33
|
+
mergeErrCatchCount >= 3,
|
|
34
|
+
`all mergeAndExit call sites have catch (mergeErr) blocks (found ${mergeErrCatchCount}, expected >= 3)`,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// ── Test 2: Every mergeErr catch block handles non-MergeConflictError ───
|
|
38
|
+
|
|
39
|
+
// Find each catch block and verify it has the non-conflict error handling pattern
|
|
40
|
+
const catchPattern = /\} catch \(mergeErr\) \{/g;
|
|
41
|
+
let match;
|
|
42
|
+
let blocksWithNonConflictHandling = 0;
|
|
43
|
+
let blocksTotal = 0;
|
|
44
|
+
|
|
45
|
+
while ((match = catchPattern.exec(phasesSrc)) !== null) {
|
|
46
|
+
blocksTotal++;
|
|
47
|
+
// Look at the ~800 chars after the catch to find both the MergeConflictError
|
|
48
|
+
// instanceof check AND the non-conflict handling
|
|
49
|
+
const afterCatch = phasesSrc.slice(match.index, match.index + 1200);
|
|
50
|
+
|
|
51
|
+
const hasInstanceofCheck = afterCatch.includes("instanceof MergeConflictError");
|
|
52
|
+
const hasNonConflictStop = afterCatch.includes('reason: "merge-failed"');
|
|
53
|
+
const hasStopAuto = afterCatch.includes("stopAuto");
|
|
54
|
+
const hasLogError = afterCatch.includes("logError");
|
|
55
|
+
|
|
56
|
+
if (hasInstanceofCheck && hasNonConflictStop && hasStopAuto && hasLogError) {
|
|
57
|
+
blocksWithNonConflictHandling++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
assertTrue(
|
|
62
|
+
blocksWithNonConflictHandling === blocksTotal && blocksTotal >= 3,
|
|
63
|
+
`all ${blocksTotal} mergeAndExit catch blocks stop auto on non-conflict errors (${blocksWithNonConflictHandling}/${blocksTotal})`,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// ── Test 3: Non-conflict handler returns break (does not continue) ──────
|
|
67
|
+
|
|
68
|
+
// Verify the pattern: after the MergeConflictError instanceof block,
|
|
69
|
+
// the non-conflict path returns { action: "break", reason: "merge-failed" }
|
|
70
|
+
const mergeFailedReasons = [...phasesSrc.matchAll(/reason: "merge-failed"/g)].length;
|
|
71
|
+
assertTrue(
|
|
72
|
+
mergeFailedReasons >= 3,
|
|
73
|
+
`all catch blocks return reason: "merge-failed" (found ${mergeFailedReasons}, expected >= 3)`,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// ── Test 4: Non-conflict handler notifies user ──────────────────────────
|
|
77
|
+
|
|
78
|
+
// Each non-conflict block should call ctx.ui.notify with error severity
|
|
79
|
+
const notifyErrorPattern = /Merge failed:.*Resolve and run \/gsd auto to resume/g;
|
|
80
|
+
const notifyCount = [...phasesSrc.matchAll(notifyErrorPattern)].length;
|
|
81
|
+
assertTrue(
|
|
82
|
+
notifyCount >= 3,
|
|
83
|
+
`all catch blocks notify user about merge failure (found ${notifyCount}, expected >= 3)`,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// ── Test 5: logError replaces logWarning for non-conflict merge errors ──
|
|
87
|
+
|
|
88
|
+
// The old code used logWarning — verify logError is used instead
|
|
89
|
+
const logWarningMergePattern = /logWarning\(.*Milestone merge failed with non-conflict error/g;
|
|
90
|
+
const logWarningCount = [...phasesSrc.matchAll(logWarningMergePattern)].length;
|
|
91
|
+
assertTrue(
|
|
92
|
+
logWarningCount === 0,
|
|
93
|
+
"logWarning is no longer used for non-conflict merge errors (replaced by logError)",
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const logErrorMergePattern = /logError\(.*Milestone merge failed with non-conflict error/g;
|
|
97
|
+
const logErrorCount = [...phasesSrc.matchAll(logErrorMergePattern)].length;
|
|
98
|
+
assertTrue(
|
|
99
|
+
logErrorCount >= 3,
|
|
100
|
+
`logError is used for non-conflict merge errors (found ${logErrorCount}, expected >= 3)`,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
report();
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for #2684: preferences.md must be included in both
|
|
3
|
+
* ROOT_STATE_FILES (sync) and copyPlanningArtifacts (initial seed).
|
|
4
|
+
*
|
|
5
|
+
* Without this, post_unit_hooks and all preference-driven config silently
|
|
6
|
+
* stop working inside auto-mode worktrees.
|
|
7
|
+
*/
|
|
8
|
+
import { test } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { readFileSync, mkdtempSync, mkdirSync, writeFileSync, existsSync, rmSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
|
|
14
|
+
test("#2684: preferences.md is NOT in ROOT_STATE_FILES (forward-only sync)", () => {
|
|
15
|
+
const srcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
|
|
16
|
+
const src = readFileSync(srcPath, "utf-8");
|
|
17
|
+
|
|
18
|
+
const constIdx = src.indexOf("ROOT_STATE_FILES");
|
|
19
|
+
assert.ok(constIdx !== -1, "ROOT_STATE_FILES constant exists");
|
|
20
|
+
|
|
21
|
+
const arrayStart = src.indexOf("[", constIdx);
|
|
22
|
+
const arrayEnd = src.indexOf("] as const", arrayStart);
|
|
23
|
+
const block = src.slice(arrayStart, arrayEnd);
|
|
24
|
+
|
|
25
|
+
// preferences.md must NOT be in ROOT_STATE_FILES — it is handled separately
|
|
26
|
+
// in syncGsdStateToWorktree() (forward-only, additive). Including it in
|
|
27
|
+
// ROOT_STATE_FILES would cause syncWorktreeStateBack() to overwrite the
|
|
28
|
+
// authoritative project root copy (#2684).
|
|
29
|
+
const entries = block.split("\n")
|
|
30
|
+
.map(l => l.trim())
|
|
31
|
+
.filter(l => l.startsWith('"') && l.includes(".md"));
|
|
32
|
+
const hasPrefs = entries.some(l => l.includes("preferences.md"));
|
|
33
|
+
assert.ok(
|
|
34
|
+
!hasPrefs,
|
|
35
|
+
"preferences.md must NOT be in ROOT_STATE_FILES (back-sync would overwrite root)",
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("#2684: copyPlanningArtifacts file list includes preferences.md", () => {
|
|
40
|
+
const srcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
|
|
41
|
+
const src = readFileSync(srcPath, "utf-8");
|
|
42
|
+
|
|
43
|
+
// Find the copyPlanningArtifacts function body
|
|
44
|
+
const fnIdx = src.indexOf("function copyPlanningArtifacts");
|
|
45
|
+
assert.ok(fnIdx !== -1, "copyPlanningArtifacts function exists");
|
|
46
|
+
|
|
47
|
+
// Extract function body (up to the next top-level function)
|
|
48
|
+
const fnBody = src.slice(fnIdx, fnIdx + 1500);
|
|
49
|
+
|
|
50
|
+
assert.ok(
|
|
51
|
+
fnBody.includes('"preferences.md"'),
|
|
52
|
+
"preferences.md should be in copyPlanningArtifacts file list",
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("#2684: syncGsdStateToWorktree copies preferences.md", async () => {
|
|
57
|
+
// Functional test: create a mock source and destination, call the sync
|
|
58
|
+
const srcBase = mkdtempSync(join(tmpdir(), "gsd-wt-prefs-src-"));
|
|
59
|
+
const dstBase = mkdtempSync(join(tmpdir(), "gsd-wt-prefs-dst-"));
|
|
60
|
+
const srcGsd = join(srcBase, ".gsd");
|
|
61
|
+
const dstGsd = join(dstBase, ".gsd");
|
|
62
|
+
mkdirSync(srcGsd, { recursive: true });
|
|
63
|
+
mkdirSync(dstGsd, { recursive: true });
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
// Write a preferences.md in source
|
|
67
|
+
writeFileSync(
|
|
68
|
+
join(srcGsd, "preferences.md"),
|
|
69
|
+
"---\nversion: 1\n---\n\npost_unit_hooks:\n - name: notify\n command: echo done\n",
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Import and call syncGsdStateToWorktree
|
|
73
|
+
const { syncGsdStateToWorktree } = await import("../auto-worktree.ts");
|
|
74
|
+
syncGsdStateToWorktree(srcBase, dstBase);
|
|
75
|
+
|
|
76
|
+
// Verify preferences.md was copied
|
|
77
|
+
assert.ok(
|
|
78
|
+
existsSync(join(dstGsd, "preferences.md")),
|
|
79
|
+
"preferences.md should be copied to worktree",
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const content = readFileSync(join(dstGsd, "preferences.md"), "utf-8");
|
|
83
|
+
assert.ok(
|
|
84
|
+
content.includes("post_unit_hooks"),
|
|
85
|
+
"copied preferences.md should contain the hooks config",
|
|
86
|
+
);
|
|
87
|
+
} finally {
|
|
88
|
+
rmSync(srcBase, { recursive: true, force: true });
|
|
89
|
+
rmSync(dstBase, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
@@ -45,7 +45,7 @@ test("getIsolationMode defaults to none when preferences have no isolation setti
|
|
|
45
45
|
// Validate the default via validatePreferences: when no isolation is set,
|
|
46
46
|
// preferences.git.isolation is undefined, and getIsolationMode returns "none".
|
|
47
47
|
// Default changed from "worktree" to "none" so GSD works out of the box
|
|
48
|
-
// without
|
|
48
|
+
// without PREFERENCES.md (#2480).
|
|
49
49
|
const { preferences } = validatePreferences({});
|
|
50
50
|
assert.equal(preferences.git?.isolation, undefined, "no isolation in empty prefs");
|
|
51
51
|
const isolation = preferences.git?.isolation;
|
|
@@ -59,7 +59,7 @@ test("solo mode applies correct defaults", () => {
|
|
|
59
59
|
const result = applyModeDefaults("solo", { mode: "solo" });
|
|
60
60
|
assert.equal(result.git?.auto_push, true);
|
|
61
61
|
assert.equal(result.git?.push_branches, false);
|
|
62
|
-
assert.equal(result.git?.pre_merge_check,
|
|
62
|
+
assert.equal(result.git?.pre_merge_check, "auto");
|
|
63
63
|
assert.equal(result.git?.merge_strategy, "squash");
|
|
64
64
|
assert.equal(result.git?.isolation, "none");
|
|
65
65
|
assert.equal(result.unique_milestone_ids, false);
|