gsd-pi 2.42.0 → 2.43.0-dev.5717b75
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 +23 -0
- package/dist/cli.js +18 -3
- package/dist/loader.js +3 -1
- package/dist/resource-loader.js +39 -6
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +52 -4
- package/dist/resources/extensions/async-jobs/await-tool.js +5 -0
- package/dist/resources/extensions/async-jobs/index.js +2 -0
- package/dist/resources/extensions/gsd/auto/phases.js +1 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +3 -16
- package/dist/resources/extensions/gsd/auto-start.js +8 -11
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -5
- package/dist/resources/extensions/gsd/detection.js +19 -0
- package/dist/resources/extensions/gsd/doctor-checks.js +31 -1
- package/dist/resources/extensions/gsd/doctor-providers.js +10 -0
- package/dist/resources/extensions/gsd/forensics.js +84 -0
- package/dist/resources/extensions/gsd/git-constants.js +1 -0
- package/dist/resources/extensions/gsd/git-service.js +1 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +1 -0
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences.js +59 -8
- package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/dist/resources/extensions/gsd/repo-identity.js +46 -5
- package/dist/resources/extensions/gsd/service-tier.js +13 -4
- package/dist/resources/extensions/gsd/session-lock.js +2 -2
- package/dist/resources/extensions/gsd/worktree-resolver.js +2 -2
- package/dist/resources/extensions/gsd/worktree.js +2 -2
- package/dist/resources/extensions/mcp-client/index.js +2 -1
- package/dist/resources/extensions/search-the-web/tool-search.js +3 -3
- 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 +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_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/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 +5 -5
- 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/terminal/input/route.js +1 -1
- 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 +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
- package/dist/web/standalone/.next/server/chunks/229.js +2 -2
- package/dist/web/standalone/.next/server/chunks/471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-f2a7482d42a5614b.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{main-app-2f2ee7b85712c2bd.js → main-app-fdab67f7802d7832.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/dist/web-mode.d.ts +2 -0
- package/dist/web-mode.js +40 -4
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +6 -0
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/src/agent.test.ts +53 -0
- package/packages/pi-agent-core/src/agent.ts +3 -0
- package/packages/pi-agent-core/src/types.ts +6 -0
- package/packages/pi-agent-core/tsconfig.json +1 -1
- package/packages/pi-ai/dist/models.d.ts +5 -3
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +801 -1468
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +1135 -1588
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +60 -2
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/scripts/generate-models.ts +1543 -0
- package/packages/pi-ai/src/models.generated.ts +1140 -1593
- package/packages/pi-ai/src/models.ts +7 -4
- package/packages/pi-ai/src/utils/oauth/github-copilot.ts +74 -2
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +8 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +29 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +60 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +18 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +23 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.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 +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +63 -11
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +20 -6
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -5
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +9 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +30 -10
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +7 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +68 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -2
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +18 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +29 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +3 -0
- package/packages/pi-coding-agent/src/core/package-manager.ts +99 -58
- package/packages/pi-coding-agent/src/core/resource-loader.ts +24 -6
- package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-editor.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +10 -6
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +31 -11
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/async-bash-timeout.test.ts +122 -0
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +40 -4
- package/src/resources/extensions/async-jobs/await-tool.test.ts +47 -0
- package/src/resources/extensions/async-jobs/await-tool.ts +5 -0
- package/src/resources/extensions/async-jobs/index.ts +1 -0
- package/src/resources/extensions/async-jobs/job-manager.ts +2 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -1
- package/src/resources/extensions/gsd/auto/phases.ts +1 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +2 -18
- package/src/resources/extensions/gsd/auto-start.ts +7 -10
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -5
- package/src/resources/extensions/gsd/detection.ts +19 -0
- package/src/resources/extensions/gsd/doctor-checks.ts +32 -1
- package/src/resources/extensions/gsd/doctor-providers.ts +13 -0
- package/src/resources/extensions/gsd/doctor-types.ts +1 -0
- package/src/resources/extensions/gsd/forensics.ts +92 -0
- package/src/resources/extensions/gsd/git-constants.ts +1 -0
- package/src/resources/extensions/gsd/git-service.ts +0 -1
- package/src/resources/extensions/gsd/gitignore.ts +1 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +1 -0
- package/src/resources/extensions/gsd/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences.ts +62 -6
- package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/src/resources/extensions/gsd/repo-identity.ts +48 -5
- package/src/resources/extensions/gsd/service-tier.ts +17 -4
- package/src/resources/extensions/gsd/session-lock.ts +2 -2
- package/src/resources/extensions/gsd/tests/activity-log.test.ts +31 -69
- package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/journal.test.ts +82 -127
- package/src/resources/extensions/gsd/tests/manifest-status.test.ts +73 -82
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +30 -1
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +56 -3
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +156 -263
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -78
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +81 -74
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +1 -2
- package/src/resources/extensions/gsd/worktree-resolver.ts +2 -3
- package/src/resources/extensions/gsd/worktree.ts +2 -2
- package/src/resources/extensions/mcp-client/index.ts +5 -1
- package/src/resources/extensions/search-the-web/tool-search.ts +3 -3
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-e07acdb7dd069836.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/layout-745c6ed5fea5fb06.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-801b53eff6e83579.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-e6255954dccfcf0a.js +0 -1
- /package/dist/web/standalone/.next/static/{QSuuaY2tP8h_zzhHcVxNj → 5ULZcR9XhHFzlAZFiSKRl}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{QSuuaY2tP8h_zzhHcVxNj → 5ULZcR9XhHFzlAZFiSKRl}/_ssgManifest.js +0 -0
|
@@ -1519,6 +1519,13 @@ export class InteractiveMode {
|
|
|
1519
1519
|
options: string[],
|
|
1520
1520
|
opts?: ExtensionUIDialogOptions,
|
|
1521
1521
|
): Promise<string | undefined> {
|
|
1522
|
+
// If a previous selector is still active, dispose it before creating a
|
|
1523
|
+
// new one. This avoids leaking the previous promise and DOM state when
|
|
1524
|
+
// showExtensionSelector is called rapidly.
|
|
1525
|
+
if (this.extensionSelector) {
|
|
1526
|
+
this.hideExtensionSelector();
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1522
1529
|
return new Promise((resolve) => {
|
|
1523
1530
|
if (opts?.signal?.aborted) {
|
|
1524
1531
|
resolve(undefined);
|
|
@@ -2331,18 +2338,24 @@ export class InteractiveMode {
|
|
|
2331
2338
|
const ignoreSigint = () => {};
|
|
2332
2339
|
process.on("SIGINT", ignoreSigint);
|
|
2333
2340
|
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
process.
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2341
|
+
try {
|
|
2342
|
+
// Set up handler to restore TUI when resumed
|
|
2343
|
+
process.once("SIGCONT", () => {
|
|
2344
|
+
process.removeListener("SIGINT", ignoreSigint);
|
|
2345
|
+
this.ui.start();
|
|
2346
|
+
this.ui.requestRender(true);
|
|
2347
|
+
});
|
|
2340
2348
|
|
|
2341
|
-
|
|
2342
|
-
|
|
2349
|
+
// Stop the TUI (restore terminal to normal mode)
|
|
2350
|
+
this.ui.stop();
|
|
2343
2351
|
|
|
2344
|
-
|
|
2345
|
-
|
|
2352
|
+
// Send SIGTSTP to process group (pid=0 means all processes in group)
|
|
2353
|
+
process.kill(0, "SIGTSTP");
|
|
2354
|
+
} catch {
|
|
2355
|
+
// If suspend fails (e.g. SIGTSTP not supported), ensure the
|
|
2356
|
+
// SIGINT listener doesn't leak.
|
|
2357
|
+
process.removeListener("SIGINT", ignoreSigint);
|
|
2358
|
+
}
|
|
2346
2359
|
}
|
|
2347
2360
|
|
|
2348
2361
|
private async handleFollowUp(): Promise<void> {
|
|
@@ -2460,7 +2473,14 @@ export class InteractiveMode {
|
|
|
2460
2473
|
// Determine editor (respect $VISUAL, then $EDITOR)
|
|
2461
2474
|
const editorCmd = process.env.VISUAL || process.env.EDITOR;
|
|
2462
2475
|
if (!editorCmd) {
|
|
2463
|
-
|
|
2476
|
+
let msg = "No editor configured. Set $VISUAL or $EDITOR environment variable.";
|
|
2477
|
+
if (process.env.TERM_PROGRAM === "iTerm.app") {
|
|
2478
|
+
msg +=
|
|
2479
|
+
"\n\nTip: If you meant to open the GSD dashboard (Ctrl+Alt+G), set Left Option Key to" +
|
|
2480
|
+
" \"Esc+\" in iTerm2 → Profiles → Keys. With the default \"Normal\" setting," +
|
|
2481
|
+
" Ctrl+Alt+G sends Ctrl+G instead.";
|
|
2482
|
+
}
|
|
2483
|
+
this.showWarning(msg);
|
|
2464
2484
|
return;
|
|
2465
2485
|
}
|
|
2466
2486
|
|
package/pkg/package.json
CHANGED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* async-bash-timeout.test.ts — Tests for async_bash timeout behavior.
|
|
3
|
+
*
|
|
4
|
+
* Reproduces issue #2186: when an async bash job exceeds its timeout and
|
|
5
|
+
* the child process ignores SIGTERM, the promise hangs indefinitely.
|
|
6
|
+
* The fix adds a SIGKILL fallback and a hard deadline that force-resolves
|
|
7
|
+
* the promise so execution can continue.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import test from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { createAsyncBashTool } from "./async-bash-tool.ts";
|
|
13
|
+
import { AsyncJobManager } from "./job-manager.ts";
|
|
14
|
+
|
|
15
|
+
function getTextFromResult(result: { content: Array<{ type: string; text?: string }> }): string {
|
|
16
|
+
return result.content.map((c) => c.text ?? "").join("\n");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const noopSignal = new AbortController().signal;
|
|
20
|
+
|
|
21
|
+
test("async_bash with timeout resolves even if process ignores SIGTERM", async () => {
|
|
22
|
+
const manager = new AsyncJobManager();
|
|
23
|
+
const tool = createAsyncBashTool(() => manager, () => process.cwd());
|
|
24
|
+
|
|
25
|
+
// Start a job that traps SIGTERM (ignores it), with a 2s timeout.
|
|
26
|
+
// The process installs a SIGTERM trap and sleeps for 60s.
|
|
27
|
+
// Before the fix, this would hang forever because SIGTERM is ignored
|
|
28
|
+
// and the close event never fires.
|
|
29
|
+
const result = await tool.execute(
|
|
30
|
+
"tc-timeout",
|
|
31
|
+
{
|
|
32
|
+
command: "trap '' TERM; sleep 60",
|
|
33
|
+
timeout: 2,
|
|
34
|
+
label: "sigterm-resistant",
|
|
35
|
+
},
|
|
36
|
+
noopSignal,
|
|
37
|
+
() => {},
|
|
38
|
+
undefined as never,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const text = getTextFromResult(result);
|
|
42
|
+
assert.match(text, /sigterm-resistant/);
|
|
43
|
+
|
|
44
|
+
const jobId = text.match(/\*\*(bg_[a-f0-9]+)\*\*/)?.[1];
|
|
45
|
+
assert.ok(jobId, "Should have returned a job ID");
|
|
46
|
+
|
|
47
|
+
// Now await the job — it should resolve within a reasonable time
|
|
48
|
+
// (timeout 2s + SIGKILL grace 5s + buffer = well under 15s)
|
|
49
|
+
const start = Date.now();
|
|
50
|
+
const job = manager.getJob(jobId)!;
|
|
51
|
+
assert.ok(job, "Job should exist");
|
|
52
|
+
|
|
53
|
+
await Promise.race([
|
|
54
|
+
job.promise,
|
|
55
|
+
new Promise<never>((_, reject) => {
|
|
56
|
+
const t = setTimeout(() => reject(new Error(
|
|
57
|
+
`Job promise hung for ${Date.now() - start}ms — ` +
|
|
58
|
+
`this is the bug from issue #2186: timeout hangs indefinitely`,
|
|
59
|
+
)), 15_000);
|
|
60
|
+
if (typeof t === "object" && "unref" in t) t.unref();
|
|
61
|
+
}),
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
const elapsed = Date.now() - start;
|
|
65
|
+
// Should have resolved well within 15s (timeout 2s + kill grace ~5s)
|
|
66
|
+
assert.ok(elapsed < 15_000, `Job took ${elapsed}ms — expected <15s`);
|
|
67
|
+
|
|
68
|
+
// Job should have completed (resolved, not rejected) with timeout message
|
|
69
|
+
assert.ok(
|
|
70
|
+
job.status === "completed" || job.status === "failed",
|
|
71
|
+
`Job status should be completed or failed, got: ${job.status}`,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (job.status === "completed") {
|
|
75
|
+
assert.ok(
|
|
76
|
+
job.resultText?.includes("timed out") || job.resultText?.includes("Timed out"),
|
|
77
|
+
`Result should mention timeout, got: ${job.resultText}`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
manager.shutdown();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("async_bash with timeout resolves normally when process exits on SIGTERM", async () => {
|
|
85
|
+
const manager = new AsyncJobManager();
|
|
86
|
+
const tool = createAsyncBashTool(() => manager, () => process.cwd());
|
|
87
|
+
|
|
88
|
+
// Start a normal sleep that will die on SIGTERM, with a 1s timeout
|
|
89
|
+
const result = await tool.execute(
|
|
90
|
+
"tc-normal-timeout",
|
|
91
|
+
{
|
|
92
|
+
command: "sleep 60",
|
|
93
|
+
timeout: 1,
|
|
94
|
+
label: "normal-timeout",
|
|
95
|
+
},
|
|
96
|
+
noopSignal,
|
|
97
|
+
() => {},
|
|
98
|
+
undefined as never,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const text = getTextFromResult(result);
|
|
102
|
+
const jobId = text.match(/\*\*(bg_[a-f0-9]+)\*\*/)?.[1];
|
|
103
|
+
assert.ok(jobId, "Should have returned a job ID");
|
|
104
|
+
|
|
105
|
+
const job = manager.getJob(jobId)!;
|
|
106
|
+
const start = Date.now();
|
|
107
|
+
|
|
108
|
+
await Promise.race([
|
|
109
|
+
job.promise,
|
|
110
|
+
new Promise<never>((_, reject) => {
|
|
111
|
+
const t = setTimeout(() => reject(new Error("Job hung")), 10_000);
|
|
112
|
+
if (typeof t === "object" && "unref" in t) t.unref();
|
|
113
|
+
}),
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
const elapsed = Date.now() - start;
|
|
117
|
+
assert.ok(elapsed < 5_000, `Expected quick resolution after SIGTERM, took ${elapsed}ms`);
|
|
118
|
+
assert.equal(job.status, "completed");
|
|
119
|
+
assert.ok(job.resultText?.includes("timed out"), `Should mention timeout: ${job.resultText}`);
|
|
120
|
+
|
|
121
|
+
manager.shutdown();
|
|
122
|
+
});
|
|
@@ -109,6 +109,10 @@ function executeBashInBackground(
|
|
|
109
109
|
timeout?: number,
|
|
110
110
|
): Promise<string> {
|
|
111
111
|
return new Promise<string>((resolve, reject) => {
|
|
112
|
+
let settled = false;
|
|
113
|
+
const safeResolve = (value: string) => { if (!settled) { settled = true; resolve(value); } };
|
|
114
|
+
const safeReject = (err: unknown) => { if (!settled) { settled = true; reject(err); } };
|
|
115
|
+
|
|
112
116
|
const { shell, args } = getShellConfig();
|
|
113
117
|
const resolvedCommand = sanitizeCommand(command);
|
|
114
118
|
|
|
@@ -121,11 +125,39 @@ function executeBashInBackground(
|
|
|
121
125
|
|
|
122
126
|
let timedOut = false;
|
|
123
127
|
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
128
|
+
let sigkillHandle: ReturnType<typeof setTimeout> | undefined;
|
|
129
|
+
let hardDeadlineHandle: ReturnType<typeof setTimeout> | undefined;
|
|
130
|
+
|
|
131
|
+
/** Grace period (ms) between SIGTERM and SIGKILL. */
|
|
132
|
+
const SIGKILL_GRACE_MS = 5_000;
|
|
133
|
+
/** Hard deadline (ms) after SIGKILL to force-resolve the promise. */
|
|
134
|
+
const HARD_DEADLINE_MS = 3_000;
|
|
124
135
|
|
|
125
136
|
if (timeout !== undefined && timeout > 0) {
|
|
126
137
|
timeoutHandle = setTimeout(() => {
|
|
127
138
|
timedOut = true;
|
|
128
139
|
if (child.pid) killTree(child.pid);
|
|
140
|
+
|
|
141
|
+
// If the process ignores SIGTERM, escalate to SIGKILL
|
|
142
|
+
sigkillHandle = setTimeout(() => {
|
|
143
|
+
if (child.pid) {
|
|
144
|
+
try { process.kill(-child.pid, "SIGKILL"); } catch { /* ignore */ }
|
|
145
|
+
try { process.kill(child.pid, "SIGKILL"); } catch { /* ignore */ }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Hard deadline: if even SIGKILL doesn't trigger 'close',
|
|
149
|
+
// force-resolve so the job doesn't hang forever (#2186).
|
|
150
|
+
hardDeadlineHandle = setTimeout(() => {
|
|
151
|
+
const output = Buffer.concat(chunks).toString("utf-8");
|
|
152
|
+
safeResolve(
|
|
153
|
+
output
|
|
154
|
+
? `${output}\n\nCommand timed out after ${timeout} seconds (force-killed)`
|
|
155
|
+
: `Command timed out after ${timeout} seconds (force-killed)`,
|
|
156
|
+
);
|
|
157
|
+
}, HARD_DEADLINE_MS);
|
|
158
|
+
if (typeof hardDeadlineHandle === "object" && "unref" in hardDeadlineHandle) hardDeadlineHandle.unref();
|
|
159
|
+
}, SIGKILL_GRACE_MS);
|
|
160
|
+
if (typeof sigkillHandle === "object" && "unref" in sigkillHandle) sigkillHandle.unref();
|
|
129
161
|
}, timeout * 1000);
|
|
130
162
|
}
|
|
131
163
|
|
|
@@ -168,24 +200,28 @@ function executeBashInBackground(
|
|
|
168
200
|
|
|
169
201
|
child.on("error", (err) => {
|
|
170
202
|
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
203
|
+
if (sigkillHandle) clearTimeout(sigkillHandle);
|
|
204
|
+
if (hardDeadlineHandle) clearTimeout(hardDeadlineHandle);
|
|
171
205
|
signal.removeEventListener("abort", onAbort);
|
|
172
|
-
|
|
206
|
+
safeReject(err);
|
|
173
207
|
});
|
|
174
208
|
|
|
175
209
|
child.on("close", (code) => {
|
|
176
210
|
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
211
|
+
if (sigkillHandle) clearTimeout(sigkillHandle);
|
|
212
|
+
if (hardDeadlineHandle) clearTimeout(hardDeadlineHandle);
|
|
177
213
|
signal.removeEventListener("abort", onAbort);
|
|
178
214
|
if (spillStream) spillStream.end();
|
|
179
215
|
|
|
180
216
|
if (signal.aborted) {
|
|
181
217
|
const output = Buffer.concat(chunks).toString("utf-8");
|
|
182
|
-
|
|
218
|
+
safeResolve(output ? `${output}\n\nCommand aborted` : "Command aborted");
|
|
183
219
|
return;
|
|
184
220
|
}
|
|
185
221
|
|
|
186
222
|
if (timedOut) {
|
|
187
223
|
const output = Buffer.concat(chunks).toString("utf-8");
|
|
188
|
-
|
|
224
|
+
safeResolve(output ? `${output}\n\nCommand timed out after ${timeout} seconds` : `Command timed out after ${timeout} seconds`);
|
|
189
225
|
return;
|
|
190
226
|
}
|
|
191
227
|
|
|
@@ -208,7 +244,7 @@ function executeBashInBackground(
|
|
|
208
244
|
text += `\n\nCommand exited with code ${code}`;
|
|
209
245
|
}
|
|
210
246
|
|
|
211
|
-
|
|
247
|
+
safeResolve(text);
|
|
212
248
|
});
|
|
213
249
|
});
|
|
214
250
|
}
|
|
@@ -118,3 +118,50 @@ test("await_job returns not-found message for invalid job IDs", async () => {
|
|
|
118
118
|
|
|
119
119
|
manager.shutdown();
|
|
120
120
|
});
|
|
121
|
+
|
|
122
|
+
test("await_job marks jobs as awaited to suppress follow-up delivery (#2248)", async () => {
|
|
123
|
+
const followUps: string[] = [];
|
|
124
|
+
const manager = new AsyncJobManager({
|
|
125
|
+
onJobComplete: (job) => {
|
|
126
|
+
if (!job.awaited) followUps.push(job.id);
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
const tool = createAwaitTool(() => manager);
|
|
130
|
+
|
|
131
|
+
// Register a job that completes in 50ms
|
|
132
|
+
const jobId = manager.register("bash", "awaited-job", async () => {
|
|
133
|
+
return new Promise<string>((resolve) => setTimeout(() => resolve("result"), 50));
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// await_job consumes the result — should mark as awaited before promise resolves
|
|
137
|
+
await tool.execute("tc7", { jobs: [jobId] }, noopSignal, () => {}, undefined as never);
|
|
138
|
+
|
|
139
|
+
// Give the onJobComplete callback a tick to fire
|
|
140
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
141
|
+
|
|
142
|
+
assert.equal(followUps.length, 0, "onJobComplete should not deliver follow-up for awaited jobs");
|
|
143
|
+
|
|
144
|
+
manager.shutdown();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("unawaited jobs still get follow-up delivery (#2248)", async () => {
|
|
148
|
+
const followUps: string[] = [];
|
|
149
|
+
const manager = new AsyncJobManager({
|
|
150
|
+
onJobComplete: (job) => {
|
|
151
|
+
if (!job.awaited) followUps.push(job.id);
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Register a fire-and-forget job
|
|
156
|
+
const jobId = manager.register("bash", "fire-and-forget", async () => "done");
|
|
157
|
+
const job = manager.getJob(jobId)!;
|
|
158
|
+
await job.promise;
|
|
159
|
+
|
|
160
|
+
// Give the callback a tick
|
|
161
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
162
|
+
|
|
163
|
+
assert.equal(followUps.length, 1, "onJobComplete should deliver follow-up for unawaited jobs");
|
|
164
|
+
assert.equal(followUps[0], jobId);
|
|
165
|
+
|
|
166
|
+
manager.shutdown();
|
|
167
|
+
});
|
|
@@ -66,6 +66,11 @@ export function createAwaitTool(getManager: () => AsyncJobManager): ToolDefiniti
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// Mark all watched jobs as awaited upfront so the onJobComplete
|
|
70
|
+
// callback (which fires synchronously in the promise .then()) knows
|
|
71
|
+
// to suppress the follow-up message.
|
|
72
|
+
for (const j of watched) j.awaited = true;
|
|
73
|
+
|
|
69
74
|
// If all watched jobs are already done, return immediately
|
|
70
75
|
const running = watched.filter((j) => j.status === "running");
|
|
71
76
|
if (running.length === 0) {
|
|
@@ -42,6 +42,7 @@ export default function AsyncJobs(pi: ExtensionAPI) {
|
|
|
42
42
|
|
|
43
43
|
manager = new AsyncJobManager({
|
|
44
44
|
onJobComplete: (job) => {
|
|
45
|
+
if (job.awaited) return;
|
|
45
46
|
const statusEmoji = job.status === "completed" ? "done" : "error";
|
|
46
47
|
const elapsed = ((Date.now() - job.startTime) / 1000).toFixed(1);
|
|
47
48
|
const output = job.status === "completed"
|
|
@@ -261,9 +261,7 @@ export async function runPreDispatch(
|
|
|
261
261
|
|
|
262
262
|
if (mid) {
|
|
263
263
|
if (deps.getIsolationMode() !== "none") {
|
|
264
|
-
deps.captureIntegrationBranch(s.basePath, mid
|
|
265
|
-
commitDocs: prefs?.git?.commit_docs,
|
|
266
|
-
});
|
|
264
|
+
deps.captureIntegrationBranch(s.basePath, mid);
|
|
267
265
|
}
|
|
268
266
|
deps.resolver.enterMilestone(mid, ctx.ui);
|
|
269
267
|
} else {
|
|
@@ -420,8 +420,6 @@ export function buildSkillActivationBlock(params: {
|
|
|
420
420
|
params.sliceTitle,
|
|
421
421
|
params.taskId,
|
|
422
422
|
params.taskTitle,
|
|
423
|
-
...(params.extraContext ?? []),
|
|
424
|
-
params.taskPlanContent ?? undefined,
|
|
425
423
|
);
|
|
426
424
|
|
|
427
425
|
const visibleSkills = (typeof getLoadedSkills === 'function' ? getLoadedSkills() : []).filter(skill => !skill.disableModelInvocation);
|
|
@@ -452,12 +450,6 @@ export function buildSkillActivationBlock(params: {
|
|
|
452
450
|
}
|
|
453
451
|
}
|
|
454
452
|
|
|
455
|
-
for (const skill of visibleSkills) {
|
|
456
|
-
if (skillMatchesContext(skill, contextTokens)) {
|
|
457
|
-
matched.add(normalizeSkillReference(skill.name));
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
453
|
const ordered = [...matched]
|
|
462
454
|
.filter(name => installedNames.has(name) && !avoided.has(name))
|
|
463
455
|
.sort();
|
|
@@ -983,11 +975,7 @@ export async function buildPlanSlicePrompt(
|
|
|
983
975
|
const executorContextConstraints = formatExecutorConstraints();
|
|
984
976
|
|
|
985
977
|
const outputRelPath = relSliceFile(base, mid, sid, "PLAN");
|
|
986
|
-
const
|
|
987
|
-
const commitDocsEnabled = prefs?.preferences?.git?.commit_docs !== false;
|
|
988
|
-
const commitInstruction = commitDocsEnabled
|
|
989
|
-
? `Commit the plan files only: \`git add ${relSlicePath(base, mid, sid)}/ .gsd/DECISIONS.md .gitignore && git commit -m "docs(${sid}): add slice plan"\`. Do not stage .gsd/STATE.md or other runtime files — the system manages those.`
|
|
990
|
-
: "Do not commit — planning docs are not tracked in git for this project.";
|
|
978
|
+
const commitInstruction = "Do not commit — .gsd/ planning docs are managed externally and not tracked in git.";
|
|
991
979
|
return loadPrompt("plan-slice", {
|
|
992
980
|
workingDirectory: base,
|
|
993
981
|
milestoneId: mid, sliceId: sid, sliceTitle: sTitle,
|
|
@@ -1485,11 +1473,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1485
1473
|
// Non-fatal — captures module may not be available
|
|
1486
1474
|
}
|
|
1487
1475
|
|
|
1488
|
-
const
|
|
1489
|
-
const reassessCommitDocsEnabled = reassessPrefs?.preferences?.git?.commit_docs !== false;
|
|
1490
|
-
const reassessCommitInstruction = reassessCommitDocsEnabled
|
|
1491
|
-
? `Commit: \`docs(${mid}): reassess roadmap after ${completedSliceId}\`. Stage only the .gsd/milestones/ files you changed — do not stage .gsd/STATE.md or other runtime files.`
|
|
1492
|
-
: "Do not commit — planning docs are not tracked in git for this project.";
|
|
1476
|
+
const reassessCommitInstruction = "Do not commit — .gsd/ planning docs are managed externally and not tracked in git.";
|
|
1493
1477
|
|
|
1494
1478
|
return loadPrompt("reassess-roadmap", {
|
|
1495
1479
|
workingDirectory: base,
|
|
@@ -167,22 +167,19 @@ export async function bootstrapAutoSession(
|
|
|
167
167
|
// ensureGitignore checks for git-tracked .gsd/ files and skips the
|
|
168
168
|
// ".gsd" pattern if the project intentionally tracks .gsd/ in git.
|
|
169
169
|
const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
|
|
170
|
-
const commitDocs = gitPrefs?.commit_docs;
|
|
171
170
|
const manageGitignore = gitPrefs?.manage_gitignore;
|
|
172
|
-
ensureGitignore(base, {
|
|
171
|
+
ensureGitignore(base, { manageGitignore });
|
|
173
172
|
if (manageGitignore !== false) untrackRuntimeFiles(base);
|
|
174
173
|
|
|
175
174
|
// Bootstrap .gsd/ if it doesn't exist
|
|
176
175
|
const gsdDir = join(base, ".gsd");
|
|
177
176
|
if (!existsSync(gsdDir)) {
|
|
178
177
|
mkdirSync(join(gsdDir, "milestones"), { recursive: true });
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
/* nothing to commit */
|
|
185
|
-
}
|
|
178
|
+
try {
|
|
179
|
+
nativeAddAll(base);
|
|
180
|
+
nativeCommit(base, "chore: init gsd");
|
|
181
|
+
} catch {
|
|
182
|
+
/* nothing to commit */
|
|
186
183
|
}
|
|
187
184
|
}
|
|
188
185
|
|
|
@@ -487,7 +484,7 @@ export async function bootstrapAutoSession(
|
|
|
487
484
|
// Capture integration branch
|
|
488
485
|
if (s.currentMilestoneId) {
|
|
489
486
|
if (getIsolationMode() !== "none") {
|
|
490
|
-
captureIntegrationBranch(base, s.currentMilestoneId
|
|
487
|
+
captureIntegrationBranch(base, s.currentMilestoneId);
|
|
491
488
|
}
|
|
492
489
|
setActiveMilestoneId(base, s.currentMilestoneId);
|
|
493
490
|
}
|
|
@@ -20,21 +20,27 @@ import { saveActivityLog } from "../activity-log.js";
|
|
|
20
20
|
// printed it before the TUI launched. Only re-print on /clear (subsequent sessions).
|
|
21
21
|
let isFirstSession = true;
|
|
22
22
|
|
|
23
|
+
async function syncServiceTierStatus(ctx: ExtensionContext): Promise<void> {
|
|
24
|
+
const { getEffectiveServiceTier, formatServiceTierFooterStatus } = await import("../service-tier.js");
|
|
25
|
+
ctx.ui.setStatus("gsd-fast", formatServiceTierFooterStatus(getEffectiveServiceTier(), ctx.model?.id));
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
export function registerHooks(pi: ExtensionAPI): void {
|
|
24
29
|
pi.on("session_start", async (_event, ctx) => {
|
|
25
30
|
resetWriteGateState();
|
|
26
31
|
resetToolCallLoopGuard();
|
|
32
|
+
await syncServiceTierStatus(ctx);
|
|
27
33
|
if (isFirstSession) {
|
|
28
34
|
isFirstSession = false;
|
|
29
35
|
} else {
|
|
30
36
|
try {
|
|
31
37
|
const gsdBinPath = process.env.GSD_BIN_PATH;
|
|
32
38
|
if (gsdBinPath) {
|
|
33
|
-
const { dirname } = await import(
|
|
39
|
+
const { dirname } = await import("node:path");
|
|
34
40
|
const { printWelcomeScreen } = await import(
|
|
35
|
-
join(dirname(gsdBinPath),
|
|
41
|
+
join(dirname(gsdBinPath), "welcome-screen.js")
|
|
36
42
|
) as { printWelcomeScreen: (opts: { version: string; modelName?: string; provider?: string }) => void };
|
|
37
|
-
printWelcomeScreen({ version: process.env.GSD_VERSION ||
|
|
43
|
+
printWelcomeScreen({ version: process.env.GSD_VERSION || "0.0.0" });
|
|
38
44
|
}
|
|
39
45
|
} catch { /* non-fatal */ }
|
|
40
46
|
}
|
|
@@ -192,8 +198,11 @@ export function registerHooks(pi: ExtensionAPI): void {
|
|
|
192
198
|
markToolEnd(event.toolCallId);
|
|
193
199
|
});
|
|
194
200
|
|
|
201
|
+
pi.on("model_select", async (_event, ctx) => {
|
|
202
|
+
await syncServiceTierStatus(ctx);
|
|
203
|
+
});
|
|
204
|
+
|
|
195
205
|
pi.on("before_provider_request", async (event) => {
|
|
196
|
-
if (!isAutoActive()) return;
|
|
197
206
|
const modelId = event.model?.id;
|
|
198
207
|
if (!modelId) return;
|
|
199
208
|
const { getEffectiveServiceTier, supportsServiceTier } = await import("../service-tier.js");
|
|
@@ -205,4 +214,3 @@ export function registerHooks(pi: ExtensionAPI): void {
|
|
|
205
214
|
return payload;
|
|
206
215
|
});
|
|
207
216
|
}
|
|
208
|
-
|
|
@@ -87,6 +87,18 @@ export const PROJECT_FILES = [
|
|
|
87
87
|
"mix.exs",
|
|
88
88
|
"deno.json",
|
|
89
89
|
"deno.jsonc",
|
|
90
|
+
// .NET
|
|
91
|
+
".sln",
|
|
92
|
+
".csproj",
|
|
93
|
+
"Directory.Build.props",
|
|
94
|
+
// Git submodules
|
|
95
|
+
".gitmodules",
|
|
96
|
+
// Xcode
|
|
97
|
+
"project.yml",
|
|
98
|
+
".xcodeproj",
|
|
99
|
+
".xcworkspace",
|
|
100
|
+
// Docker
|
|
101
|
+
"Dockerfile",
|
|
90
102
|
] as const;
|
|
91
103
|
|
|
92
104
|
const LANGUAGE_MAP: Record<string, string> = {
|
|
@@ -106,6 +118,13 @@ const LANGUAGE_MAP: Record<string, string> = {
|
|
|
106
118
|
"mix.exs": "elixir",
|
|
107
119
|
"deno.json": "typescript/deno",
|
|
108
120
|
"deno.jsonc": "typescript/deno",
|
|
121
|
+
".sln": "dotnet",
|
|
122
|
+
".csproj": "dotnet",
|
|
123
|
+
"Directory.Build.props": "dotnet",
|
|
124
|
+
"project.yml": "swift/xcode",
|
|
125
|
+
".xcodeproj": "swift/xcode",
|
|
126
|
+
".xcworkspace": "swift/xcode",
|
|
127
|
+
"Dockerfile": "docker",
|
|
109
128
|
};
|
|
110
129
|
|
|
111
130
|
const MONOREPO_MARKERS = [
|
|
@@ -2,7 +2,7 @@ import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync, rmSync,
|
|
|
2
2
|
import { basename, dirname, join, sep } from "node:path";
|
|
3
3
|
|
|
4
4
|
import type { DoctorIssue, DoctorIssueCode } from "./doctor-types.js";
|
|
5
|
-
import { readRepoMeta, externalProjectsRoot } from "./repo-identity.js";
|
|
5
|
+
import { readRepoMeta, externalProjectsRoot, cleanNumberedGsdVariants } from "./repo-identity.js";
|
|
6
6
|
import { loadFile, parseRoadmap } from "./files.js";
|
|
7
7
|
import { resolveMilestoneFile, milestonesDir, gsdRoot, resolveGsdRootFile, relGsdRootFile } from "./paths.js";
|
|
8
8
|
import { deriveState, isMilestoneComplete } from "./state.js";
|
|
@@ -776,6 +776,37 @@ export async function checkRuntimeHealth(
|
|
|
776
776
|
// Non-fatal — external state check failed
|
|
777
777
|
}
|
|
778
778
|
|
|
779
|
+
// ── Numbered .gsd collision variants (#2205) ───────────────────────────
|
|
780
|
+
// macOS APFS can create ".gsd 2", ".gsd 3" etc. when a directory blocks
|
|
781
|
+
// symlink creation. These must be removed so the canonical .gsd is used.
|
|
782
|
+
try {
|
|
783
|
+
const variantPattern = /^\.gsd \d+$/;
|
|
784
|
+
const entries = readdirSync(basePath);
|
|
785
|
+
const variants = entries.filter(e => variantPattern.test(e));
|
|
786
|
+
if (variants.length > 0) {
|
|
787
|
+
for (const v of variants) {
|
|
788
|
+
issues.push({
|
|
789
|
+
severity: "warning",
|
|
790
|
+
code: "numbered_gsd_variant",
|
|
791
|
+
scope: "project",
|
|
792
|
+
unitId: "project",
|
|
793
|
+
message: `Found macOS collision variant "${v}" — this can cause GSD state to appear deleted.`,
|
|
794
|
+
file: v,
|
|
795
|
+
fixable: true,
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
if (shouldFix("numbered_gsd_variant")) {
|
|
800
|
+
const removed = cleanNumberedGsdVariants(basePath);
|
|
801
|
+
for (const name of removed) {
|
|
802
|
+
fixesApplied.push(`removed numbered .gsd variant: ${name}`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
} catch {
|
|
807
|
+
// Non-fatal — variant check failed
|
|
808
|
+
}
|
|
809
|
+
|
|
779
810
|
// ── Metrics ledger integrity ───────────────────────────────────────────
|
|
780
811
|
try {
|
|
781
812
|
const metricsPath = join(root, "metrics.json");
|
|
@@ -305,11 +305,24 @@ function checkOptionalProviders(): ProviderCheckResult[] {
|
|
|
305
305
|
const optional = ["brave", "tavily", "jina", "context7"] as const;
|
|
306
306
|
const results: ProviderCheckResult[] = [];
|
|
307
307
|
|
|
308
|
+
// Determine which search providers are configured so we can suppress
|
|
309
|
+
// "not configured" noise for alternative search providers when at least
|
|
310
|
+
// one is already active (e.g. don't warn about missing BRAVE_API_KEY
|
|
311
|
+
// when Tavily is configured).
|
|
312
|
+
const searchProviderIds = ["brave", "tavily"] as const;
|
|
313
|
+
const hasAnySearchProvider = searchProviderIds.some(id => resolveKey(id).found);
|
|
314
|
+
|
|
308
315
|
for (const providerId of optional) {
|
|
309
316
|
const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
|
|
310
317
|
if (!info) continue;
|
|
311
318
|
|
|
312
319
|
const lookup = resolveKey(providerId);
|
|
320
|
+
|
|
321
|
+
// Skip unconfigured search providers when another search provider is active
|
|
322
|
+
if (!lookup.found && hasAnySearchProvider && info.category === "search") {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
|
|
313
326
|
results.push({
|
|
314
327
|
name: providerId,
|
|
315
328
|
label: info.label,
|