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
|
@@ -287,9 +287,9 @@ describe('git-service', async () => {
|
|
|
287
287
|
|
|
288
288
|
|
|
289
289
|
const tempDir = mkdtempSync(join(tmpdir(), "gsd-git-service-test-"));
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
290
|
+
runGit(tempDir, ["init", "-b", "main"]);
|
|
291
|
+
runGit(tempDir, ["config", "user.name", "Pi Test"]);
|
|
292
|
+
runGit(tempDir, ["config", "user.email", "pi@example.com"]);
|
|
293
293
|
|
|
294
294
|
// runGit should work on a valid repo
|
|
295
295
|
const branch = runGit(tempDir, ["branch", "--show-current"]);
|
|
@@ -334,13 +334,13 @@ describe('git-service', async () => {
|
|
|
334
334
|
|
|
335
335
|
function initTempRepo(): string {
|
|
336
336
|
const dir = mkdtempSync(join(tmpdir(), "gsd-git-t02-"));
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
337
|
+
runGit(dir, ["init", "-b", "main"]);
|
|
338
|
+
runGit(dir, ["config", "user.name", "Pi Test"]);
|
|
339
|
+
runGit(dir, ["config", "user.email", "pi@example.com"]);
|
|
340
340
|
// Need an initial commit so HEAD exists
|
|
341
341
|
createFile(dir, ".gitkeep", "");
|
|
342
|
-
|
|
343
|
-
|
|
342
|
+
runGit(dir, ["add", "-A"]);
|
|
343
|
+
runGit(dir, ["commit", "-m", "init"]);
|
|
344
344
|
return dir;
|
|
345
345
|
}
|
|
346
346
|
|
|
@@ -577,12 +577,12 @@ describe('git-service', async () => {
|
|
|
577
577
|
|
|
578
578
|
function initBranchTestRepo(): string {
|
|
579
579
|
const dir = mkdtempSync(join(tmpdir(), "gsd-git-t03-"));
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
580
|
+
runGit(dir, ["init", "-b", "main"]);
|
|
581
|
+
runGit(dir, ["config", "user.name", "Pi Test"]);
|
|
582
|
+
runGit(dir, ["config", "user.email", "pi@example.com"]);
|
|
583
583
|
createFile(dir, ".gitkeep", "");
|
|
584
|
-
|
|
585
|
-
|
|
584
|
+
runGit(dir, ["add", "-A"]);
|
|
585
|
+
runGit(dir, ["commit", "-m", "init"]);
|
|
586
586
|
return dir;
|
|
587
587
|
}
|
|
588
588
|
|
|
@@ -618,12 +618,12 @@ describe('git-service', async () => {
|
|
|
618
618
|
{
|
|
619
619
|
// master-only repo
|
|
620
620
|
const repo = mkdtempSync(join(tmpdir(), "gsd-git-t03-master-"));
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
621
|
+
runGit(repo, ["init", "-b", "master"]);
|
|
622
|
+
runGit(repo, ["config", "user.name", "Pi Test"]);
|
|
623
|
+
runGit(repo, ["config", "user.email", "pi@example.com"]);
|
|
624
624
|
createFile(repo, ".gitkeep", "");
|
|
625
|
-
|
|
626
|
-
|
|
625
|
+
runGit(repo, ["add", "-A"]);
|
|
626
|
+
runGit(repo, ["commit", "-m", "init"]);
|
|
627
627
|
|
|
628
628
|
const svc = new GitServiceImpl(repo);
|
|
629
629
|
assert.deepStrictEqual(svc.getMainBranch(), "master", "getMainBranch returns master when only master exists");
|
|
@@ -635,11 +635,11 @@ describe('git-service', async () => {
|
|
|
635
635
|
// S05: Enhanced features — snapshots, pre-merge checks
|
|
636
636
|
// ═══════════════════════════════════════════════════════════════════════
|
|
637
637
|
|
|
638
|
-
// ─── createSnapshot:
|
|
638
|
+
// ─── createSnapshot: default (enabled) ─────────────────────────────────
|
|
639
639
|
|
|
640
|
-
test('createSnapshot: enabled', () => {
|
|
640
|
+
test('createSnapshot: enabled by default when prefs omitted', () => {
|
|
641
641
|
const repo = initBranchTestRepo();
|
|
642
|
-
const svc = new GitServiceImpl(repo
|
|
642
|
+
const svc = new GitServiceImpl(repo);
|
|
643
643
|
|
|
644
644
|
// Create a branch with a commit
|
|
645
645
|
run("git checkout -b gsd/M001/S01", repo);
|
|
@@ -675,6 +675,39 @@ describe('git-service', async () => {
|
|
|
675
675
|
rmSync(repo, { recursive: true, force: true });
|
|
676
676
|
});
|
|
677
677
|
|
|
678
|
+
// ─── runPreMergeCheck: default (auto-detect) ──────────────────────────
|
|
679
|
+
|
|
680
|
+
test('runPreMergeCheck: auto-detects when prefs omitted', () => {
|
|
681
|
+
const repo = initBranchTestRepo();
|
|
682
|
+
createFile(repo, "package.json", JSON.stringify({
|
|
683
|
+
name: "test-default",
|
|
684
|
+
scripts: { test: 'node -e "process.exit(0)"' },
|
|
685
|
+
}));
|
|
686
|
+
run("git add -A", repo);
|
|
687
|
+
run('git commit -m "add package.json"', repo);
|
|
688
|
+
|
|
689
|
+
// No pre_merge_check pref set — should auto-detect and run
|
|
690
|
+
const svc = new GitServiceImpl(repo);
|
|
691
|
+
const result: PreMergeCheckResult = svc.runPreMergeCheck();
|
|
692
|
+
|
|
693
|
+
assert.deepStrictEqual(result.passed, true, "runPreMergeCheck auto-detects and passes when prefs omitted");
|
|
694
|
+
assert.ok(!result.skipped, "runPreMergeCheck is not skipped when prefs omitted and package.json exists");
|
|
695
|
+
|
|
696
|
+
rmSync(repo, { recursive: true, force: true });
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
test('runPreMergeCheck: gracefully skips when prefs omitted and no package.json', () => {
|
|
700
|
+
const repo = initBranchTestRepo();
|
|
701
|
+
// No package.json — auto-detect should skip gracefully
|
|
702
|
+
const svc = new GitServiceImpl(repo);
|
|
703
|
+
const result: PreMergeCheckResult = svc.runPreMergeCheck();
|
|
704
|
+
|
|
705
|
+
assert.deepStrictEqual(result.passed, true, "runPreMergeCheck passes when no package.json (skip)");
|
|
706
|
+
assert.deepStrictEqual(result.skipped, true, "runPreMergeCheck skips when no test runner detected");
|
|
707
|
+
|
|
708
|
+
rmSync(repo, { recursive: true, force: true });
|
|
709
|
+
});
|
|
710
|
+
|
|
678
711
|
// ─── runPreMergeCheck: pass ────────────────────────────────────────────
|
|
679
712
|
|
|
680
713
|
test('runPreMergeCheck: pass', () => {
|
|
@@ -1082,9 +1115,9 @@ describe('git-service', async () => {
|
|
|
1082
1115
|
test('untrackRuntimeFiles', async () => {
|
|
1083
1116
|
const { untrackRuntimeFiles } = await import("../gitignore.ts");
|
|
1084
1117
|
const repo = mkdtempSync(join(tmpdir(), "gsd-untrack-"));
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1118
|
+
runGit(repo, ["init", "-b", "main"]);
|
|
1119
|
+
runGit(repo, ["config", "user.email", "test@test.com"]);
|
|
1120
|
+
runGit(repo, ["config", "user.name", "Test"]);
|
|
1088
1121
|
|
|
1089
1122
|
// Create and track runtime files (simulates pre-.gitignore state)
|
|
1090
1123
|
mkdirSync(join(repo, ".gsd", "activity"), { recursive: true });
|
|
@@ -1095,8 +1128,8 @@ describe('git-service', async () => {
|
|
|
1095
1128
|
writeFileSync(join(repo, ".gsd", "activity", "log.jsonl"), "{}");
|
|
1096
1129
|
writeFileSync(join(repo, ".gsd", "runtime", "data.json"), "{}");
|
|
1097
1130
|
writeFileSync(join(repo, "src.ts"), "code");
|
|
1098
|
-
|
|
1099
|
-
|
|
1131
|
+
runGit(repo, ["add", "-A"]);
|
|
1132
|
+
runGit(repo, ["commit", "-m", "init"]);
|
|
1100
1133
|
|
|
1101
1134
|
// Precondition: runtime files are tracked
|
|
1102
1135
|
const trackedBefore = run("git ls-files .gsd/", repo);
|
|
@@ -1131,18 +1164,19 @@ describe('git-service', async () => {
|
|
|
1131
1164
|
|
|
1132
1165
|
test('smartStage excludes runtime files, allows milestone artifacts', () => {
|
|
1133
1166
|
const repo = mkdtempSync(join(tmpdir(), "gsd-smart-stage-excludes-"));
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1167
|
+
runGit(repo, ["init", "-b", "main"]);
|
|
1168
|
+
runGit(repo, ["config", "user.email", "test@test.com"]);
|
|
1169
|
+
runGit(repo, ["config", "user.name", "Test"]);
|
|
1137
1170
|
writeFileSync(join(repo, "README.md"), "init");
|
|
1138
|
-
|
|
1171
|
+
runGit(repo, ["add", "-A"]);
|
|
1172
|
+
runGit(repo, ["commit", "-m", "init"]);
|
|
1139
1173
|
|
|
1140
1174
|
// Create .gsd/ runtime files + milestone artifacts + a normal source file
|
|
1141
1175
|
mkdirSync(join(repo, ".gsd", "milestones", "M001"), { recursive: true });
|
|
1142
1176
|
mkdirSync(join(repo, ".gsd", "runtime"), { recursive: true });
|
|
1143
1177
|
mkdirSync(join(repo, ".gsd", "activity"), { recursive: true });
|
|
1144
1178
|
writeFileSync(join(repo, ".gsd", "milestones", "M001", "ROADMAP.md"), "# Roadmap");
|
|
1145
|
-
writeFileSync(join(repo, ".gsd", "
|
|
1179
|
+
writeFileSync(join(repo, ".gsd", "PREFERENCES.md"), "---\nversion: 1\n---");
|
|
1146
1180
|
writeFileSync(join(repo, ".gsd", "STATE.md"), "# State");
|
|
1147
1181
|
writeFileSync(join(repo, ".gsd", "runtime", "units.json"), "{}");
|
|
1148
1182
|
writeFileSync(join(repo, ".gsd", "activity", "log.jsonl"), "{}");
|
|
@@ -64,7 +64,7 @@ describe('gsd-db', () => {
|
|
|
64
64
|
// Check schema_version table
|
|
65
65
|
const adapter = _getAdapter()!;
|
|
66
66
|
const version = adapter.prepare('SELECT MAX(version) as version FROM schema_version').get();
|
|
67
|
-
assert.deepStrictEqual(version?.['version'],
|
|
67
|
+
assert.deepStrictEqual(version?.['version'], 14, 'schema version should be 14');
|
|
68
68
|
|
|
69
69
|
// Check tables exist by querying them
|
|
70
70
|
const dRows = adapter.prepare('SELECT count(*) as cnt FROM decisions').get();
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for #2527: idle watchdog stalled-tool detection.
|
|
3
|
+
*
|
|
4
|
+
* Bug 1: When a tool is stalled longer than idle_timeout, the watchdog
|
|
5
|
+
* notifies but falls through to detectWorkingTreeActivity(), which
|
|
6
|
+
* resets lastProgressAt if files were modified earlier. Recovery is
|
|
7
|
+
* never called — the session burns tokens indefinitely.
|
|
8
|
+
*
|
|
9
|
+
* Bug 2: After async recoverTimedOutUnit(), pauseAuto/stopAuto may set
|
|
10
|
+
* s.currentUnit = null, but the next line accesses .startedAt — crash.
|
|
11
|
+
*
|
|
12
|
+
* These tests verify the auto-timers.ts source contains the structural
|
|
13
|
+
* fixes: the stalledToolDetected flag, clearInFlightTools() call, the
|
|
14
|
+
* filesystem-check guard, and the null guard after recovery.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import { test, describe } from "node:test";
|
|
20
|
+
import assert from "node:assert/strict";
|
|
21
|
+
|
|
22
|
+
const TIMERS_SRC = readFileSync(
|
|
23
|
+
join(import.meta.dirname, "..", "auto-timers.ts"),
|
|
24
|
+
"utf-8",
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// ═══ Bug 1: stalledToolDetected flag prevents filesystem-activity override ═══
|
|
28
|
+
|
|
29
|
+
describe("#2527 Bug 1: stalled tool should not be overridden by filesystem activity", () => {
|
|
30
|
+
test("auto-timers.ts imports clearInFlightTools", () => {
|
|
31
|
+
assert.ok(
|
|
32
|
+
TIMERS_SRC.includes("clearInFlightTools"),
|
|
33
|
+
"clearInFlightTools must be imported from auto-tool-tracking",
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("auto-timers.ts declares stalledToolDetected flag", () => {
|
|
38
|
+
assert.ok(
|
|
39
|
+
TIMERS_SRC.includes("stalledToolDetected"),
|
|
40
|
+
"stalledToolDetected flag must exist in idle watchdog",
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("stalled tool sets flag to true", () => {
|
|
45
|
+
// The flag must be set before the filesystem check
|
|
46
|
+
const flagSet = TIMERS_SRC.indexOf("stalledToolDetected = true");
|
|
47
|
+
assert.ok(flagSet > -1, "stalledToolDetected must be set to true when tool is stalled");
|
|
48
|
+
|
|
49
|
+
const notify = TIMERS_SRC.indexOf("Stalled tool detected:");
|
|
50
|
+
assert.ok(flagSet < notify, "flag must be set before the stall notification");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("stalled tool calls clearInFlightTools", () => {
|
|
54
|
+
// clearInFlightTools() must be called when tool is stalled, so subsequent
|
|
55
|
+
// watchdog ticks don't re-detect the same stale entries
|
|
56
|
+
const clearCall = TIMERS_SRC.indexOf("clearInFlightTools()");
|
|
57
|
+
assert.ok(clearCall > -1, "clearInFlightTools() must be called when tool is stalled");
|
|
58
|
+
|
|
59
|
+
const flagSet = TIMERS_SRC.indexOf("stalledToolDetected = true");
|
|
60
|
+
assert.ok(
|
|
61
|
+
Math.abs(clearCall - flagSet) < 200,
|
|
62
|
+
"clearInFlightTools() should be near stalledToolDetected = true",
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("filesystem-activity check is guarded by stalledToolDetected", () => {
|
|
67
|
+
// The detectWorkingTreeActivity check must be skipped when stalledToolDetected is true
|
|
68
|
+
assert.ok(
|
|
69
|
+
TIMERS_SRC.includes("!stalledToolDetected && detectWorkingTreeActivity"),
|
|
70
|
+
"detectWorkingTreeActivity must be guarded by !stalledToolDetected",
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("control flow: stalled tool → skip filesystem check → reach recovery", () => {
|
|
75
|
+
// Verify the structural ordering: flag declaration → stall block → guarded fs check → recovery
|
|
76
|
+
const flagDecl = TIMERS_SRC.indexOf("let stalledToolDetected = false");
|
|
77
|
+
const stallBlock = TIMERS_SRC.indexOf("stalledToolDetected = true");
|
|
78
|
+
const fsGuard = TIMERS_SRC.indexOf("!stalledToolDetected && detectWorkingTreeActivity");
|
|
79
|
+
const recovery = TIMERS_SRC.indexOf("recoverTimedOutUnit(ctx, pi, unitType, unitId, \"idle\"");
|
|
80
|
+
|
|
81
|
+
assert.ok(flagDecl > -1, "flag declaration must exist");
|
|
82
|
+
assert.ok(flagDecl < stallBlock, "flag declared before stall block");
|
|
83
|
+
assert.ok(stallBlock < fsGuard, "stall block before filesystem guard");
|
|
84
|
+
assert.ok(fsGuard < recovery, "filesystem guard before recovery call");
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// ═══ Bug 2: null guard after async recoverTimedOutUnit ═══════════════════════
|
|
89
|
+
|
|
90
|
+
describe("#2527 Bug 2: null guard after async recovery prevents crash", () => {
|
|
91
|
+
test("idle watchdog has null guard after recoverTimedOutUnit", () => {
|
|
92
|
+
// Find the idle recovery call
|
|
93
|
+
const idleRecovery = TIMERS_SRC.indexOf(
|
|
94
|
+
'recoverTimedOutUnit(ctx, pi, unitType, unitId, "idle"',
|
|
95
|
+
);
|
|
96
|
+
assert.ok(idleRecovery > -1, "idle recovery call must exist");
|
|
97
|
+
|
|
98
|
+
// The null guard must appear between the recovery call and the next
|
|
99
|
+
// writeUnitRuntimeRecord that accesses s.currentUnit.startedAt
|
|
100
|
+
const afterRecovery = TIMERS_SRC.slice(idleRecovery, idleRecovery + 400);
|
|
101
|
+
assert.ok(
|
|
102
|
+
afterRecovery.includes("if (!s.currentUnit) return"),
|
|
103
|
+
"null guard for s.currentUnit must exist after idle recoverTimedOutUnit",
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("null guard is between recovery and writeUnitRuntimeRecord", () => {
|
|
108
|
+
const idleRecovery = TIMERS_SRC.indexOf(
|
|
109
|
+
'recoverTimedOutUnit(ctx, pi, unitType, unitId, "idle"',
|
|
110
|
+
);
|
|
111
|
+
const afterRecovery = TIMERS_SRC.slice(idleRecovery);
|
|
112
|
+
|
|
113
|
+
const recoveredReturn = afterRecovery.indexOf('if (recovery === "recovered") return');
|
|
114
|
+
const nullGuard = afterRecovery.indexOf("if (!s.currentUnit) return");
|
|
115
|
+
const writeRecord = afterRecovery.indexOf("writeUnitRuntimeRecord(s.basePath");
|
|
116
|
+
|
|
117
|
+
assert.ok(recoveredReturn > -1, "recovered return must exist");
|
|
118
|
+
assert.ok(nullGuard > -1, "null guard must exist");
|
|
119
|
+
assert.ok(writeRecord > -1, "writeUnitRuntimeRecord must exist after recovery");
|
|
120
|
+
assert.ok(
|
|
121
|
+
recoveredReturn < nullGuard && nullGuard < writeRecord,
|
|
122
|
+
"order must be: recovered-return → null-guard → writeUnitRuntimeRecord",
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -123,7 +123,7 @@ test("init-wizard: v2 .gsd/ preferences detected", (t) => {
|
|
|
123
123
|
const dir = makeTempDir("prefs-detect");
|
|
124
124
|
try {
|
|
125
125
|
mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
|
|
126
|
-
writeFileSync(join(dir, ".gsd", "
|
|
126
|
+
writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), "---\nversion: 1\nmode: solo\n---\n", "utf-8");
|
|
127
127
|
|
|
128
128
|
const detection = detectProjectState(dir);
|
|
129
129
|
assert.ok(detection.v2);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for #2676: idle watchdog must exempt user-interactive tools
|
|
3
|
+
* (ask_user_questions, secure_env_collect) from stall detection.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, test, beforeEach } from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import {
|
|
8
|
+
markToolStart,
|
|
9
|
+
markToolEnd,
|
|
10
|
+
hasInteractiveToolInFlight,
|
|
11
|
+
getInFlightToolCount,
|
|
12
|
+
getOldestInFlightToolStart,
|
|
13
|
+
getOldestInFlightToolAgeMs,
|
|
14
|
+
clearInFlightTools,
|
|
15
|
+
} from "../auto-tool-tracking.ts";
|
|
16
|
+
|
|
17
|
+
// These tests call the tracking module directly (bypassing the auto.ts
|
|
18
|
+
// wrapper which guards on s.active) so we always pass isActive=true.
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
clearInFlightTools();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("hasInteractiveToolInFlight", () => {
|
|
25
|
+
test("returns false when no tools are in-flight", () => {
|
|
26
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("returns false when only non-interactive tools are in-flight", () => {
|
|
30
|
+
markToolStart("call-1", true, "bash");
|
|
31
|
+
markToolStart("call-2", true, "read");
|
|
32
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("returns true when ask_user_questions is in-flight", () => {
|
|
36
|
+
markToolStart("call-1", true, "bash");
|
|
37
|
+
markToolStart("call-2", true, "ask_user_questions");
|
|
38
|
+
assert.equal(hasInteractiveToolInFlight(), true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("returns true when secure_env_collect is in-flight", () => {
|
|
42
|
+
markToolStart("call-1", true, "secure_env_collect");
|
|
43
|
+
assert.equal(hasInteractiveToolInFlight(), true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("returns false after interactive tool completes", () => {
|
|
47
|
+
markToolStart("call-1", true, "ask_user_questions");
|
|
48
|
+
assert.equal(hasInteractiveToolInFlight(), true);
|
|
49
|
+
markToolEnd("call-1");
|
|
50
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("returns true if one of multiple tools is interactive", () => {
|
|
54
|
+
markToolStart("call-1", true, "bash");
|
|
55
|
+
markToolStart("call-2", true, "edit");
|
|
56
|
+
markToolStart("call-3", true, "ask_user_questions");
|
|
57
|
+
markToolStart("call-4", true, "write");
|
|
58
|
+
assert.equal(hasInteractiveToolInFlight(), true);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("toolName tracking in markToolStart", () => {
|
|
63
|
+
test("defaults toolName to 'unknown' when not provided", () => {
|
|
64
|
+
markToolStart("call-1", true);
|
|
65
|
+
// unknown tool should not be treated as interactive
|
|
66
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
67
|
+
assert.equal(getInFlightToolCount(), 1);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("no-ops when isActive is false", () => {
|
|
71
|
+
markToolStart("call-1", false, "ask_user_questions");
|
|
72
|
+
assert.equal(getInFlightToolCount(), 0);
|
|
73
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("existing tracking behavior preserved with toolName", () => {
|
|
78
|
+
test("getInFlightToolCount tracks correctly", () => {
|
|
79
|
+
assert.equal(getInFlightToolCount(), 0);
|
|
80
|
+
markToolStart("call-1", true, "bash");
|
|
81
|
+
assert.equal(getInFlightToolCount(), 1);
|
|
82
|
+
markToolStart("call-2", true, "ask_user_questions");
|
|
83
|
+
assert.equal(getInFlightToolCount(), 2);
|
|
84
|
+
markToolEnd("call-1");
|
|
85
|
+
assert.equal(getInFlightToolCount(), 1);
|
|
86
|
+
markToolEnd("call-2");
|
|
87
|
+
assert.equal(getInFlightToolCount(), 0);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("getOldestInFlightToolStart returns correct timestamp", () => {
|
|
91
|
+
assert.equal(getOldestInFlightToolStart(), undefined);
|
|
92
|
+
const before = Date.now();
|
|
93
|
+
markToolStart("call-1", true, "bash");
|
|
94
|
+
const after = Date.now();
|
|
95
|
+
const oldest = getOldestInFlightToolStart();
|
|
96
|
+
assert.ok(oldest !== undefined);
|
|
97
|
+
assert.ok(oldest! >= before && oldest! <= after);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("getOldestInFlightToolAgeMs returns 0 with no tools", () => {
|
|
101
|
+
assert.equal(getOldestInFlightToolAgeMs(), 0);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("getOldestInFlightToolAgeMs returns positive value with tools", () => {
|
|
105
|
+
markToolStart("call-1", true, "read");
|
|
106
|
+
const age = getOldestInFlightToolAgeMs();
|
|
107
|
+
assert.ok(age >= 0, `age should be non-negative, got ${age}`);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("clearInFlightTools resets all state", () => {
|
|
111
|
+
markToolStart("call-1", true, "ask_user_questions");
|
|
112
|
+
markToolStart("call-2", true, "bash");
|
|
113
|
+
assert.equal(getInFlightToolCount(), 2);
|
|
114
|
+
assert.equal(hasInteractiveToolInFlight(), true);
|
|
115
|
+
clearInFlightTools();
|
|
116
|
+
assert.equal(getInFlightToolCount(), 0);
|
|
117
|
+
assert.equal(hasInteractiveToolInFlight(), false);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -189,7 +189,22 @@ test("getAllKeyStatuses detects empty keys as not configured", () => {
|
|
|
189
189
|
const statuses = getAllKeyStatuses(auth);
|
|
190
190
|
const groq = statuses.find((s) => s.provider.id === "groq");
|
|
191
191
|
assert.equal(groq?.configured, false);
|
|
192
|
-
|
|
192
|
+
// Empty-key entries are filtered out, so provider appears unconfigured
|
|
193
|
+
assert.equal(groq?.source, "none");
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("getAllKeyStatuses finds valid keys even when empty-key entry exists at index 0", () => {
|
|
197
|
+
const auth = makeAuth({
|
|
198
|
+
groq: [
|
|
199
|
+
{ type: "api_key", key: "" },
|
|
200
|
+
{ type: "api_key", key: "gsk-real-key" },
|
|
201
|
+
],
|
|
202
|
+
});
|
|
203
|
+
const statuses = getAllKeyStatuses(auth);
|
|
204
|
+
const groq = statuses.find((s) => s.provider.id === "groq");
|
|
205
|
+
assert.equal(groq?.configured, true);
|
|
206
|
+
assert.equal(groq?.source, "auth.json");
|
|
207
|
+
assert.equal(groq?.credentialCount, 1); // only the valid key counts
|
|
193
208
|
});
|
|
194
209
|
|
|
195
210
|
test("getAllKeyStatuses detects env var keys", () => {
|
|
@@ -363,7 +363,7 @@ test('md-importer: schema v1→v2 migration', () => {
|
|
|
363
363
|
openDatabase(':memory:');
|
|
364
364
|
const adapter = _getAdapter();
|
|
365
365
|
const version = adapter?.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
366
|
-
assert.deepStrictEqual(version?.v,
|
|
366
|
+
assert.deepStrictEqual(version?.v, 14, 'new DB should be at schema version 14');
|
|
367
367
|
|
|
368
368
|
// Artifacts table should exist
|
|
369
369
|
const tableCheck = adapter?.prepare("SELECT count(*) as c FROM sqlite_master WHERE type='table' AND name='artifacts'").get();
|
|
@@ -323,9 +323,9 @@ test('memory-store: schema includes memories table', () => {
|
|
|
323
323
|
const viewCount = adapter.prepare('SELECT count(*) as cnt FROM active_memories').get();
|
|
324
324
|
assert.deepStrictEqual(viewCount?.['cnt'], 0, 'active_memories view should exist');
|
|
325
325
|
|
|
326
|
-
// Verify schema version is
|
|
326
|
+
// Verify schema version is 14 (after indexes + slice_dependencies)
|
|
327
327
|
const version = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
328
|
-
assert.deepStrictEqual(version?.['v'],
|
|
328
|
+
assert.deepStrictEqual(version?.['v'], 14, 'schema version should be 14');
|
|
329
329
|
|
|
330
330
|
closeDatabase();
|
|
331
331
|
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* milestone-report-path.test.ts — Regression test for milestone report path resolution.
|
|
3
|
+
*
|
|
4
|
+
* When running in a worktree, milestone reports must be written to the
|
|
5
|
+
* original project root (originalBasePath), not the worktree path (basePath).
|
|
6
|
+
*
|
|
7
|
+
* Covers: _resolveReportBasePath from auto/phases.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, test } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
|
|
13
|
+
import { _resolveReportBasePath } from "../auto/phases.ts";
|
|
14
|
+
|
|
15
|
+
describe("_resolveReportBasePath", () => {
|
|
16
|
+
test("uses originalBasePath when set (worktree scenario)", () => {
|
|
17
|
+
const session = {
|
|
18
|
+
originalBasePath: "/projects/my-app",
|
|
19
|
+
basePath: "/projects/my-app/.claude/worktrees/agent-abc123",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
assert.equal(_resolveReportBasePath(session), "/projects/my-app");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("falls back to basePath when originalBasePath is empty", () => {
|
|
26
|
+
const session = {
|
|
27
|
+
originalBasePath: "",
|
|
28
|
+
basePath: "/projects/my-app",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
assert.equal(_resolveReportBasePath(session), "/projects/my-app");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("falls back to basePath when originalBasePath is undefined", () => {
|
|
35
|
+
const session = {
|
|
36
|
+
originalBasePath: undefined as unknown as string,
|
|
37
|
+
basePath: "/projects/my-app",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
assert.equal(_resolveReportBasePath(session), "/projects/my-app");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("uses originalBasePath even when basePath differs", () => {
|
|
44
|
+
const session = {
|
|
45
|
+
originalBasePath: "/home/user/repo",
|
|
46
|
+
basePath: "/tmp/worktree-xyz",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
assert.equal(_resolveReportBasePath(session), "/home/user/repo");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Uses the writeRunnerPreferences pattern from doctor-git.test.ts:
|
|
9
9
|
* PROJECT_PREFERENCES_PATH is a module-level constant frozen at import
|
|
10
10
|
* time, so process.chdir() won't redirect preference loading. We write
|
|
11
|
-
* prefs to the runner's cwd .gsd/
|
|
11
|
+
* prefs to the runner's cwd .gsd/PREFERENCES.md and clean up in finally.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
|
|
@@ -24,7 +24,7 @@ import assert from 'node:assert/strict';
|
|
|
24
24
|
|
|
25
25
|
// --- Preferences helpers (same pattern as doctor-git.test.ts K001) ---
|
|
26
26
|
|
|
27
|
-
const RUNNER_PREFS_PATH = join(process.cwd(), ".gsd", "
|
|
27
|
+
const RUNNER_PREFS_PATH = join(process.cwd(), ".gsd", "PREFERENCES.md");
|
|
28
28
|
|
|
29
29
|
function writeRunnerPreferences(isolation: "none" | "worktree" | "branch"): void {
|
|
30
30
|
mkdirSync(join(process.cwd(), ".gsd"), { recursive: true });
|
|
@@ -72,12 +72,12 @@ try {
|
|
|
72
72
|
|
|
73
73
|
// Test 4: shouldUseWorktreeIsolation returns false for no prefs (default: none)
|
|
74
74
|
// Worktree isolation requires explicit opt-in — default is "none" so GSD
|
|
75
|
-
// works out of the box without
|
|
75
|
+
// works out of the box without PREFERENCES.md (#2480).
|
|
76
76
|
// Skip if global prefs exist — they override the default and this test
|
|
77
|
-
// cannot control ~/.gsd/
|
|
77
|
+
// cannot control ~/.gsd/PREFERENCES.md.
|
|
78
78
|
|
|
79
79
|
test('shouldUseWorktreeIsolation returns false for no prefs (default: none)', () => {
|
|
80
|
-
const globalPrefsExist = existsSync(join(homedir(), ".gsd", "
|
|
80
|
+
const globalPrefsExist = existsSync(join(homedir(), ".gsd", "PREFERENCES.md"))
|
|
81
81
|
|| existsSync(join(homedir(), ".gsd", "PREFERENCES.md"));
|
|
82
82
|
if (!globalPrefsExist) {
|
|
83
83
|
try {
|
|
@@ -91,9 +91,9 @@ test('shouldUseWorktreeIsolation returns false for no prefs (default: none)', ()
|
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
// Test 5: getIsolationMode returns "none" when no
|
|
94
|
+
// Test 5: getIsolationMode returns "none" when no PREFERENCES.md exists (#2480)
|
|
95
95
|
test('getIsolationMode returns "none" with no prefs (default)', () => {
|
|
96
|
-
const globalPrefsExist = existsSync(join(homedir(), ".gsd", "
|
|
96
|
+
const globalPrefsExist = existsSync(join(homedir(), ".gsd", "PREFERENCES.md"))
|
|
97
97
|
|| existsSync(join(homedir(), ".gsd", "PREFERENCES.md"));
|
|
98
98
|
if (!globalPrefsExist) {
|
|
99
99
|
try {
|