gsd-pi 2.41.0 → 2.42.0-dev.eedc83f
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 +92 -29
- package/dist/cli-web-branch.d.ts +6 -0
- package/dist/cli-web-branch.js +17 -0
- package/dist/cli.js +15 -1
- package/dist/onboarding.js +2 -1
- package/dist/resource-loader.js +39 -6
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +52 -4
- package/dist/resources/extensions/gsd/auto/loop.js +89 -1
- package/dist/resources/extensions/gsd/auto/phases.js +28 -10
- package/dist/resources/extensions/gsd/auto/session.js +6 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +8 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +1 -1
- package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
- package/dist/resources/extensions/gsd/auto-start.js +8 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
- package/dist/resources/extensions/gsd/auto.js +64 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +25 -3
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +40 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
- package/dist/resources/extensions/gsd/context-injector.js +74 -0
- package/dist/resources/extensions/gsd/context-store.js +4 -3
- package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
- package/dist/resources/extensions/gsd/custom-verification.js +145 -0
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
- package/dist/resources/extensions/gsd/db-writer.js +5 -2
- package/dist/resources/extensions/gsd/definition-loader.js +352 -0
- package/dist/resources/extensions/gsd/detection.js +20 -1
- package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
- package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -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/doctor.js +11 -1
- package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
- package/dist/resources/extensions/gsd/engine-types.js +8 -0
- package/dist/resources/extensions/gsd/execution-policy.js +8 -0
- package/dist/resources/extensions/gsd/exit-command.js +12 -2
- package/dist/resources/extensions/gsd/export.js +9 -13
- package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
- package/dist/resources/extensions/gsd/files.js +28 -11
- package/dist/resources/extensions/gsd/forensics.js +94 -3
- package/dist/resources/extensions/gsd/git-constants.js +1 -0
- package/dist/resources/extensions/gsd/git-service.js +73 -3
- package/dist/resources/extensions/gsd/graph.js +225 -0
- package/dist/resources/extensions/gsd/gsd-db.js +25 -8
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +7 -3
- package/dist/resources/extensions/gsd/journal.js +85 -0
- package/dist/resources/extensions/gsd/md-importer.js +5 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +3 -2
- package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
- package/dist/resources/extensions/gsd/preferences-types.js +2 -0
- package/dist/resources/extensions/gsd/preferences.js +60 -8
- package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +92 -7
- package/dist/resources/extensions/gsd/rule-registry.js +489 -0
- package/dist/resources/extensions/gsd/rule-types.js +6 -0
- package/dist/resources/extensions/gsd/run-manager.js +134 -0
- package/dist/resources/extensions/gsd/service-tier.js +147 -0
- package/dist/resources/extensions/gsd/session-lock.js +2 -2
- package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
- package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
- package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
- package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
- package/dist/resources/extensions/gsd/worktree-resolver.js +21 -4
- package/dist/resources/extensions/mcp-client/index.js +2 -1
- package/dist/resources/extensions/search-the-web/tool-search.js +3 -3
- package/dist/resources/extensions/subagent/index.js +7 -3
- package/dist/resources/extensions/voice/index.js +4 -4
- package/dist/resources/skills/create-workflow/SKILL.md +103 -0
- package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
- 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 +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 +13 -13
- package/dist/web/standalone/.next/server/chunks/229.js +3 -3
- 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-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.c195dc1fdd2adbea.js +9 -0
- 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/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
- 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 +4 -0
- package/dist/web-mode.js +69 -11
- package/package.json +1 -1
- package/packages/native/src/__tests__/text.test.mjs +33 -0
- 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/discovery-cache.test.js +3 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.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/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.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 +34 -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/discovery-cache.test.ts +4 -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/components/login-dialog.ts +11 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +36 -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/gsd/auto/loop-deps.ts +5 -1
- package/src/resources/extensions/gsd/auto/loop.ts +101 -1
- package/src/resources/extensions/gsd/auto/phases.ts +30 -10
- package/src/resources/extensions/gsd/auto/session.ts +6 -0
- package/src/resources/extensions/gsd/auto/types.ts +4 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +9 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +1 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
- package/src/resources/extensions/gsd/auto-start.ts +8 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
- package/src/resources/extensions/gsd/auto.ts +71 -2
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +25 -4
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +40 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
- package/src/resources/extensions/gsd/context-injector.ts +100 -0
- package/src/resources/extensions/gsd/context-store.ts +4 -3
- package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
- package/src/resources/extensions/gsd/custom-verification.ts +180 -0
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
- package/src/resources/extensions/gsd/db-writer.ts +6 -2
- package/src/resources/extensions/gsd/definition-loader.ts +462 -0
- package/src/resources/extensions/gsd/detection.ts +20 -1
- package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
- package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -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/doctor.ts +12 -1
- package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
- package/src/resources/extensions/gsd/engine-types.ts +71 -0
- package/src/resources/extensions/gsd/execution-policy.ts +43 -0
- package/src/resources/extensions/gsd/exit-command.ts +14 -2
- package/src/resources/extensions/gsd/export.ts +8 -15
- package/src/resources/extensions/gsd/extension-manifest.json +2 -2
- package/src/resources/extensions/gsd/files.ts +29 -12
- package/src/resources/extensions/gsd/forensics.ts +101 -3
- package/src/resources/extensions/gsd/git-constants.ts +1 -0
- package/src/resources/extensions/gsd/git-service.ts +76 -6
- package/src/resources/extensions/gsd/graph.ts +312 -0
- package/src/resources/extensions/gsd/gsd-db.ts +37 -8
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +7 -3
- package/src/resources/extensions/gsd/journal.ts +134 -0
- package/src/resources/extensions/gsd/md-importer.ts +6 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +3 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
- package/src/resources/extensions/gsd/preferences-types.ts +6 -0
- package/src/resources/extensions/gsd/preferences.ts +63 -6
- package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/src/resources/extensions/gsd/prompts/queue.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +95 -7
- package/src/resources/extensions/gsd/rule-registry.ts +599 -0
- package/src/resources/extensions/gsd/rule-types.ts +68 -0
- package/src/resources/extensions/gsd/run-manager.ts +180 -0
- package/src/resources/extensions/gsd/service-tier.ts +184 -0
- package/src/resources/extensions/gsd/session-lock.ts +2 -2
- package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
- package/src/resources/extensions/gsd/templates/decisions.md +2 -2
- package/src/resources/extensions/gsd/tests/activity-log.test.ts +31 -69
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +103 -120
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +12 -1
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +20 -20
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
- package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
- package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
- package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
- 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 +49 -0
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/journal.test.ts +341 -0
- package/src/resources/extensions/gsd/tests/manifest-status.test.ts +73 -82
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +156 -263
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +135 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +203 -106
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
- package/src/resources/extensions/gsd/types.ts +3 -0
- package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
- package/src/resources/extensions/gsd/worktree-resolver.ts +32 -11
- package/src/resources/extensions/mcp-client/index.ts +5 -1
- package/src/resources/extensions/search-the-web/tool-search.ts +3 -3
- package/src/resources/extensions/subagent/index.ts +7 -3
- package/src/resources/extensions/voice/index.ts +4 -4
- package/src/resources/skills/create-workflow/SKILL.md +103 -0
- package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
- package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
- 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/{Ute3pMouVczQyT15qrBBO → JUBX5FUR73jiViQU5a-Cx}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Ute3pMouVczQyT15qrBBO → JUBX5FUR73jiViQU5a-Cx}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// GSD Login Dialog Component — OAuth login flow UI
|
|
2
2
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
3
|
import { getOAuthProviders } from "@gsd/pi-ai/oauth";
|
|
4
|
-
import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from "@gsd/pi-tui";
|
|
4
|
+
import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, truncateToWidth, type TUI } from "@gsd/pi-tui";
|
|
5
5
|
import { execFile } from "child_process";
|
|
6
6
|
import { theme } from "../theme/theme.js";
|
|
7
7
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
@@ -121,21 +121,25 @@ export class LoginDialogComponent extends Container implements Focusable {
|
|
|
121
121
|
showAuth(url: string, instructions?: string): void {
|
|
122
122
|
this.contentContainer.clear();
|
|
123
123
|
this.contentContainer.addChild(new Spacer(1));
|
|
124
|
-
|
|
124
|
+
|
|
125
|
+
// Truncate the visible URL text so it never wraps (which would break
|
|
126
|
+
// the OSC 8 hyperlink). The full URL is still the link target.
|
|
127
|
+
const maxUrlWidth = Math.max(20, this.tui.terminal.columns - 4);
|
|
128
|
+
const displayUrl = truncateToWidth(url, maxUrlWidth);
|
|
129
|
+
const urlLink = `\x1b]8;;${url}\x07${theme.fg("accent", displayUrl)}\x1b]8;;\x07`;
|
|
130
|
+
this.contentContainer.addChild(new Text(urlLink, 1, 0));
|
|
125
131
|
|
|
126
132
|
const clickHint = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
|
|
127
|
-
|
|
128
|
-
this.contentContainer.addChild(new Text(theme.fg("dim", hyperlink), 1, 0));
|
|
133
|
+
this.contentContainer.addChild(new Text(theme.fg("dim", clickHint), 1, 0));
|
|
129
134
|
|
|
130
135
|
if (instructions) {
|
|
131
136
|
this.contentContainer.addChild(new Spacer(1));
|
|
132
137
|
this.contentContainer.addChild(new Text(theme.fg("warning", instructions), 1, 0));
|
|
133
138
|
}
|
|
134
139
|
|
|
135
|
-
//
|
|
136
|
-
// so it treats the URL as a target, not a window title
|
|
140
|
+
// PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.
|
|
137
141
|
if (process.platform === "win32") {
|
|
138
|
-
execFile("
|
|
142
|
+
execFile("powershell", ["-c", `Start-Process '${url.replace(/'/g, "''")}'`], () => {});
|
|
139
143
|
} else {
|
|
140
144
|
const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
141
145
|
execFile(openCmd, [url], () => {});
|
|
@@ -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);
|
|
@@ -2321,23 +2328,34 @@ export class InteractiveMode {
|
|
|
2321
2328
|
}
|
|
2322
2329
|
|
|
2323
2330
|
private handleCtrlZ(): void {
|
|
2331
|
+
// On Windows, SIGTSTP doesn't exist - Ctrl+Z is not supported
|
|
2332
|
+
if (process.platform === "win32") {
|
|
2333
|
+
return;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2324
2336
|
// Ignore SIGINT while suspended so Ctrl+C in the terminal does not
|
|
2325
2337
|
// kill the backgrounded process. The handler is removed on resume.
|
|
2326
2338
|
const ignoreSigint = () => {};
|
|
2327
2339
|
process.on("SIGINT", ignoreSigint);
|
|
2328
2340
|
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
process.
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
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
|
+
});
|
|
2335
2348
|
|
|
2336
|
-
|
|
2337
|
-
|
|
2349
|
+
// Stop the TUI (restore terminal to normal mode)
|
|
2350
|
+
this.ui.stop();
|
|
2338
2351
|
|
|
2339
|
-
|
|
2340
|
-
|
|
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
|
+
}
|
|
2341
2359
|
}
|
|
2342
2360
|
|
|
2343
2361
|
private async handleFollowUp(): Promise<void> {
|
|
@@ -2455,7 +2473,14 @@ export class InteractiveMode {
|
|
|
2455
2473
|
// Determine editor (respect $VISUAL, then $EDITOR)
|
|
2456
2474
|
const editorCmd = process.env.VISUAL || process.env.EDITOR;
|
|
2457
2475
|
if (!editorCmd) {
|
|
2458
|
-
|
|
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);
|
|
2459
2484
|
return;
|
|
2460
2485
|
}
|
|
2461
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
|
}
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
import type { DispatchAction } from "../auto-dispatch.js";
|
|
20
20
|
import type { WorktreeResolver } from "../worktree-resolver.js";
|
|
21
21
|
import type { CmuxLogLevel } from "../../cmux/index.js";
|
|
22
|
+
import type { JournalEntry } from "../journal.js";
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Dependencies injected by the caller (auto.ts startAuto) so autoLoop
|
|
@@ -102,7 +103,7 @@ export interface LoopDeps {
|
|
|
102
103
|
basePath: string,
|
|
103
104
|
milestoneId: string,
|
|
104
105
|
roadmapContent: string,
|
|
105
|
-
) => { pushed: boolean };
|
|
106
|
+
) => { pushed: boolean; codeFilesChanged: boolean };
|
|
106
107
|
teardownAutoWorktree: (basePath: string, milestoneId: string) => void;
|
|
107
108
|
createAutoWorktree: (basePath: string, milestoneId: string) => string;
|
|
108
109
|
captureIntegrationBranch: (
|
|
@@ -285,4 +286,7 @@ export interface LoopDeps {
|
|
|
285
286
|
|
|
286
287
|
// Session manager
|
|
287
288
|
getSessionFile: (ctx: ExtensionContext) => string;
|
|
289
|
+
|
|
290
|
+
// Journal
|
|
291
|
+
emitJournalEvent: (entry: JournalEntry) => void;
|
|
288
292
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
|
|
11
11
|
|
|
12
|
+
import { randomUUID } from "node:crypto";
|
|
12
13
|
import type { AutoSession, SidecarItem } from "./session.js";
|
|
13
14
|
import type { LoopDeps } from "./loop-deps.js";
|
|
14
15
|
import {
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
} from "./phases.js";
|
|
28
29
|
import { debugLog } from "../debug-logger.js";
|
|
29
30
|
import { isInfrastructureError } from "./infra-errors.js";
|
|
31
|
+
import { resolveEngine } from "../engine-resolver.js";
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* Main auto-mode execution loop. Iterates: derive → dispatch → guards →
|
|
@@ -51,6 +53,11 @@ export async function autoLoop(
|
|
|
51
53
|
iteration++;
|
|
52
54
|
debugLog("autoLoop", { phase: "loop-top", iteration });
|
|
53
55
|
|
|
56
|
+
// ── Journal: per-iteration flow grouping ──
|
|
57
|
+
const flowId = randomUUID();
|
|
58
|
+
let seqCounter = 0;
|
|
59
|
+
const nextSeq = () => ++seqCounter;
|
|
60
|
+
|
|
54
61
|
if (iteration > MAX_LOOP_ITERATIONS) {
|
|
55
62
|
debugLog("autoLoop", {
|
|
56
63
|
phase: "exit",
|
|
@@ -84,6 +91,7 @@ export async function autoLoop(
|
|
|
84
91
|
unitType: sidecarItem.unitType,
|
|
85
92
|
unitId: sidecarItem.unitId,
|
|
86
93
|
});
|
|
94
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "sidecar-dequeue", data: { kind: sidecarItem.kind, unitType: sidecarItem.unitType, unitId: sidecarItem.unitId } });
|
|
87
95
|
}
|
|
88
96
|
|
|
89
97
|
const sessionLockBase = deps.lockBase();
|
|
@@ -106,9 +114,100 @@ export async function autoLoop(
|
|
|
106
114
|
}
|
|
107
115
|
}
|
|
108
116
|
|
|
109
|
-
const ic: IterationContext = { ctx, pi, s, deps, prefs, iteration };
|
|
117
|
+
const ic: IterationContext = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
|
|
118
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-start", data: { iteration } });
|
|
110
119
|
let iterData: IterationData;
|
|
111
120
|
|
|
121
|
+
// ── Custom engine path ──────────────────────────────────────────────
|
|
122
|
+
// When activeEngineId is a non-dev value, bypass runPreDispatch and
|
|
123
|
+
// runDispatch entirely — the custom engine drives its own state via
|
|
124
|
+
// GRAPH.yaml. Shares runGuards and runUnitPhase with the dev path.
|
|
125
|
+
// After unit execution, verifies then reconciles via the engine layer.
|
|
126
|
+
//
|
|
127
|
+
// GSD_ENGINE_BYPASS=1 skips the engine layer entirely — falls through
|
|
128
|
+
// to the dev path below.
|
|
129
|
+
if (s.activeEngineId != null && s.activeEngineId !== "dev" && !sidecarItem && process.env.GSD_ENGINE_BYPASS !== "1") {
|
|
130
|
+
debugLog("autoLoop", { phase: "custom-engine-derive", iteration, engineId: s.activeEngineId });
|
|
131
|
+
|
|
132
|
+
const { engine, policy } = resolveEngine({
|
|
133
|
+
activeEngineId: s.activeEngineId,
|
|
134
|
+
activeRunDir: s.activeRunDir,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const engineState = await engine.deriveState(s.basePath);
|
|
138
|
+
if (engineState.isComplete) {
|
|
139
|
+
await deps.stopAuto(ctx, pi, "Workflow complete");
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
debugLog("autoLoop", { phase: "custom-engine-dispatch", iteration });
|
|
144
|
+
const dispatch = await engine.resolveDispatch(engineState, { basePath: s.basePath });
|
|
145
|
+
|
|
146
|
+
if (dispatch.action === "stop") {
|
|
147
|
+
await deps.stopAuto(ctx, pi, dispatch.reason ?? "Engine stopped");
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
if (dispatch.action === "skip") {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// dispatch.action === "dispatch"
|
|
155
|
+
const step = dispatch.step!;
|
|
156
|
+
const gsdState = await deps.deriveState(s.basePath);
|
|
157
|
+
|
|
158
|
+
iterData = {
|
|
159
|
+
unitType: step.unitType,
|
|
160
|
+
unitId: step.unitId,
|
|
161
|
+
prompt: step.prompt,
|
|
162
|
+
finalPrompt: step.prompt,
|
|
163
|
+
pauseAfterUatDispatch: false,
|
|
164
|
+
observabilityIssues: [],
|
|
165
|
+
state: gsdState,
|
|
166
|
+
mid: s.currentMilestoneId ?? "workflow",
|
|
167
|
+
midTitle: "Workflow",
|
|
168
|
+
isRetry: false,
|
|
169
|
+
previousTier: undefined,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// ── Progress widget (mirrors dev path in runDispatch) ──
|
|
173
|
+
deps.updateProgressWidget(ctx, iterData.unitType, iterData.unitId, iterData.state);
|
|
174
|
+
|
|
175
|
+
// ── Guards (shared with dev path) ──
|
|
176
|
+
const guardsResult = await runGuards(ic, s.currentMilestoneId ?? "workflow");
|
|
177
|
+
if (guardsResult.action === "break") break;
|
|
178
|
+
|
|
179
|
+
// ── Unit execution (shared with dev path) ──
|
|
180
|
+
const unitPhaseResult = await runUnitPhase(ic, iterData, loopState);
|
|
181
|
+
if (unitPhaseResult.action === "break") break;
|
|
182
|
+
|
|
183
|
+
// ── Verify first, then reconcile (only mark complete on pass) ──
|
|
184
|
+
debugLog("autoLoop", { phase: "custom-engine-verify", iteration, unitId: iterData.unitId });
|
|
185
|
+
const verifyResult = await policy.verify(iterData.unitType, iterData.unitId, { basePath: s.basePath });
|
|
186
|
+
if (verifyResult === "pause") {
|
|
187
|
+
await deps.pauseAuto(ctx, pi);
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
if (verifyResult === "retry") {
|
|
191
|
+
debugLog("autoLoop", { phase: "custom-engine-verify-retry", iteration, unitId: iterData.unitId });
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Verification passed — mark step complete
|
|
196
|
+
debugLog("autoLoop", { phase: "custom-engine-reconcile", iteration, unitId: iterData.unitId });
|
|
197
|
+
await engine.reconcile(engineState, {
|
|
198
|
+
unitType: iterData.unitType,
|
|
199
|
+
unitId: iterData.unitId,
|
|
200
|
+
startedAt: s.currentUnit?.startedAt ?? Date.now(),
|
|
201
|
+
finishedAt: Date.now(),
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
deps.clearUnitTimeout();
|
|
205
|
+
consecutiveErrors = 0;
|
|
206
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
|
|
207
|
+
debugLog("autoLoop", { phase: "iteration-complete", iteration });
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
112
211
|
if (!sidecarItem) {
|
|
113
212
|
// ── Phase 1: Pre-dispatch ─────────────────────────────────────────
|
|
114
213
|
const preDispatchResult = await runPreDispatch(ic, loopState);
|
|
@@ -153,6 +252,7 @@ export async function autoLoop(
|
|
|
153
252
|
if (finalizeResult.action === "continue") continue;
|
|
154
253
|
|
|
155
254
|
consecutiveErrors = 0; // Iteration completed successfully
|
|
255
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
|
|
156
256
|
debugLog("autoLoop", { phase: "iteration-complete", iteration });
|
|
157
257
|
} catch (loopErr) {
|
|
158
258
|
// ── Blanket catch: absorb unexpected exceptions, apply graduated recovery ──
|
|
@@ -26,6 +26,7 @@ import { runUnit } from "./run-unit.js";
|
|
|
26
26
|
import { debugLog } from "../debug-logger.js";
|
|
27
27
|
import { gsdRoot } from "../paths.js";
|
|
28
28
|
import { atomicWriteSync } from "../atomic-write.js";
|
|
29
|
+
import { PROJECT_FILES } from "../detection.js";
|
|
29
30
|
import { join } from "node:path";
|
|
30
31
|
|
|
31
32
|
// ─── generateMilestoneReport ──────────────────────────────────────────────────
|
|
@@ -192,6 +193,7 @@ export async function runPreDispatch(
|
|
|
192
193
|
|
|
193
194
|
// ── Milestone transition ────────────────────────────────────────────
|
|
194
195
|
if (mid && s.currentMilestoneId && mid !== s.currentMilestoneId) {
|
|
196
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "milestone-transition", data: { from: s.currentMilestoneId, to: mid } });
|
|
195
197
|
ctx.ui.notify(
|
|
196
198
|
`Milestone ${s.currentMilestoneId} complete. Advancing to ${mid}: ${midTitle}.`,
|
|
197
199
|
"info",
|
|
@@ -386,6 +388,7 @@ export async function runPreDispatch(
|
|
|
386
388
|
);
|
|
387
389
|
}
|
|
388
390
|
debugLog("autoLoop", { phase: "exit", reason: "no-active-milestone" });
|
|
391
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "no-active-milestone" } });
|
|
389
392
|
return { action: "break", reason: "no-active-milestone" };
|
|
390
393
|
}
|
|
391
394
|
|
|
@@ -454,6 +457,7 @@ export async function runPreDispatch(
|
|
|
454
457
|
);
|
|
455
458
|
await closeoutAndStop(ctx, pi, s, deps, `Milestone ${mid} complete`);
|
|
456
459
|
debugLog("autoLoop", { phase: "exit", reason: "milestone-complete" });
|
|
460
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "milestone-complete", milestoneId: mid } });
|
|
457
461
|
return { action: "break", reason: "milestone-complete" };
|
|
458
462
|
}
|
|
459
463
|
|
|
@@ -465,6 +469,7 @@ export async function runPreDispatch(
|
|
|
465
469
|
deps.sendDesktopNotification("GSD", blockerMsg, "error", "attention");
|
|
466
470
|
deps.logCmuxEvent(prefs, blockerMsg, "error");
|
|
467
471
|
debugLog("autoLoop", { phase: "exit", reason: "blocked" });
|
|
472
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "blocked", blockers: state.blockers } });
|
|
468
473
|
return { action: "break", reason: "blocked" };
|
|
469
474
|
}
|
|
470
475
|
|
|
@@ -497,6 +502,7 @@ export async function runDispatch(
|
|
|
497
502
|
});
|
|
498
503
|
|
|
499
504
|
if (dispatchResult.action === "stop") {
|
|
505
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "dispatch-stop", rule: dispatchResult.matchedRule, data: { reason: dispatchResult.reason } });
|
|
500
506
|
await closeoutAndStop(ctx, pi, s, deps, dispatchResult.reason);
|
|
501
507
|
debugLog("autoLoop", { phase: "exit", reason: "dispatch-stop" });
|
|
502
508
|
return { action: "break", reason: "dispatch-stop" };
|
|
@@ -508,6 +514,8 @@ export async function runDispatch(
|
|
|
508
514
|
return { action: "continue" };
|
|
509
515
|
}
|
|
510
516
|
|
|
517
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "dispatch-match", rule: dispatchResult.matchedRule, data: { unitType: dispatchResult.unitType, unitId: dispatchResult.unitId } });
|
|
518
|
+
|
|
511
519
|
let unitType = dispatchResult.unitType;
|
|
512
520
|
let unitId = dispatchResult.unitId;
|
|
513
521
|
let prompt = dispatchResult.prompt;
|
|
@@ -600,6 +608,7 @@ export async function runDispatch(
|
|
|
600
608
|
`Pre-dispatch hook${preDispatchResult.firedHooks.length > 1 ? "s" : ""}: ${preDispatchResult.firedHooks.join(", ")}`,
|
|
601
609
|
"info",
|
|
602
610
|
);
|
|
611
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "pre-dispatch-hook", data: { firedHooks: preDispatchResult.firedHooks, action: preDispatchResult.action } });
|
|
603
612
|
}
|
|
604
613
|
if (preDispatchResult.action === "skip") {
|
|
605
614
|
ctx.ui.notify(
|
|
@@ -809,25 +818,27 @@ export async function runUnitPhase(
|
|
|
809
818
|
unitId,
|
|
810
819
|
});
|
|
811
820
|
|
|
812
|
-
// ── Worktree health check (#1833)
|
|
821
|
+
// ── Worktree health check (#1833, #1843) ────────────────────────────
|
|
813
822
|
// Verify the working directory is a valid git checkout with project
|
|
814
823
|
// files before dispatching work. A broken worktree causes agents to
|
|
815
824
|
// hallucinate summaries since they cannot read or write any files.
|
|
825
|
+
// Uses the shared PROJECT_FILES list from detection.ts to support all
|
|
826
|
+
// ecosystems (Rust, Go, Python, Java, etc.), not just JS.
|
|
816
827
|
if (s.basePath && unitType === "execute-task") {
|
|
817
828
|
const gitMarker = join(s.basePath, ".git");
|
|
818
829
|
const hasGit = deps.existsSync(gitMarker);
|
|
819
|
-
const hasPackageJson = deps.existsSync(join(s.basePath, "package.json"));
|
|
820
|
-
const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
|
|
821
830
|
if (!hasGit) {
|
|
822
831
|
const msg = `Worktree health check failed: ${s.basePath} has no .git — refusing to dispatch ${unitType} ${unitId}`;
|
|
823
|
-
debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit
|
|
832
|
+
debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit });
|
|
824
833
|
ctx.ui.notify(msg, "error");
|
|
825
834
|
await deps.stopAuto(ctx, pi, msg);
|
|
826
835
|
return { action: "break", reason: "worktree-invalid" };
|
|
827
836
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
837
|
+
const hasProjectFile = PROJECT_FILES.some((f) => deps.existsSync(join(s.basePath, f)));
|
|
838
|
+
const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
|
|
839
|
+
if (!hasProjectFile && !hasSrcDir) {
|
|
840
|
+
const msg = `Worktree health check failed: ${s.basePath} has no recognized project files — refusing to dispatch ${unitType} ${unitId}`;
|
|
841
|
+
debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasProjectFile, hasSrcDir });
|
|
831
842
|
ctx.ui.notify(msg, "error");
|
|
832
843
|
await deps.stopAuto(ctx, pi, msg);
|
|
833
844
|
return { action: "break", reason: "worktree-invalid" };
|
|
@@ -843,6 +854,8 @@ export async function runUnitPhase(
|
|
|
843
854
|
const previousTier = s.currentUnitRouting?.tier;
|
|
844
855
|
|
|
845
856
|
s.currentUnit = { type: unitType, id: unitId, startedAt: Date.now() };
|
|
857
|
+
const unitStartSeq = ic.nextSeq();
|
|
858
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: unitStartSeq, eventType: "unit-start", data: { unitType, unitId } });
|
|
846
859
|
deps.captureAvailableSkills();
|
|
847
860
|
deps.writeUnitRuntimeRecord(
|
|
848
861
|
s.basePath,
|
|
@@ -988,7 +1001,12 @@ export async function runUnitPhase(
|
|
|
988
1001
|
unitId,
|
|
989
1002
|
prefs,
|
|
990
1003
|
buildSnapshotOpts: () => deps.buildSnapshotOpts(unitType, unitId),
|
|
991
|
-
buildRecoveryContext: () => ({
|
|
1004
|
+
buildRecoveryContext: () => ({
|
|
1005
|
+
basePath: s.basePath,
|
|
1006
|
+
verbose: s.verbose,
|
|
1007
|
+
currentUnitStartedAt: s.currentUnit?.startedAt ?? Date.now(),
|
|
1008
|
+
unitRecoveryCount: s.unitRecoveryCount,
|
|
1009
|
+
}),
|
|
992
1010
|
pauseAuto: deps.pauseAuto,
|
|
993
1011
|
});
|
|
994
1012
|
|
|
@@ -1115,9 +1133,9 @@ export async function runUnitPhase(
|
|
|
1115
1133
|
);
|
|
1116
1134
|
}
|
|
1117
1135
|
|
|
1118
|
-
const
|
|
1136
|
+
const skipArtifactVerification = unitType.startsWith("hook/") || unitType === "custom-step";
|
|
1119
1137
|
const artifactVerified =
|
|
1120
|
-
|
|
1138
|
+
skipArtifactVerification ||
|
|
1121
1139
|
deps.verifyExpectedArtifact(unitType, unitId, s.basePath);
|
|
1122
1140
|
if (artifactVerified) {
|
|
1123
1141
|
s.completedUnits.push({
|
|
@@ -1141,6 +1159,8 @@ export async function runUnitPhase(
|
|
|
1141
1159
|
s.unitRecoveryCount.delete(`${unitType}/${unitId}`);
|
|
1142
1160
|
}
|
|
1143
1161
|
|
|
1162
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "unit-end", data: { unitType, unitId, status: unitResult.status, artifactVerified }, causedBy: { flowId: ic.flowId, seq: unitStartSeq } });
|
|
1163
|
+
|
|
1144
1164
|
return { action: "next", data: { unitStartedAt: s.currentUnit.startedAt } };
|
|
1145
1165
|
}
|
|
1146
1166
|
|