gsd-pi 2.41.0 → 2.42.0-dev.1df898f
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 +18 -3
- package/dist/loader.js +3 -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/async-jobs/await-tool.js +5 -0
- package/dist/resources/extensions/async-jobs/index.js +2 -0
- package/dist/resources/extensions/gsd/auto/loop.js +89 -1
- package/dist/resources/extensions/gsd/auto/phases.js +29 -13
- 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 +3 -16
- package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
- package/dist/resources/extensions/gsd/auto-start.js +16 -14
- 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 +6 -2
- 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/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/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 +9 -9
- 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 +9 -9
- 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/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 +5 -2
- package/src/resources/extensions/gsd/auto/loop.ts +101 -1
- package/src/resources/extensions/gsd/auto/phases.ts +31 -13
- 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 +2 -18
- package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
- package/src/resources/extensions/gsd/auto-start.ts +15 -13
- 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 +5 -5
- package/src/resources/extensions/gsd/gitignore.ts +1 -1
- 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 +44 -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-activation.test.ts +56 -3
- 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 +79 -5
- 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 -12
- 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/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 → qw8qDHXOTLUXBq1vEknSz}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Ute3pMouVczQyT15qrBBO → qw8qDHXOTLUXBq1vEknSz}/_ssgManifest.js +0 -0
|
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
5
5
|
import { loadRegistry } from "../workflow-templates.js";
|
|
6
|
+
import { resolveProjectRoot } from "../worktree.js";
|
|
6
7
|
|
|
7
8
|
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
8
9
|
|
|
@@ -14,7 +15,7 @@ export interface GsdCommandDefinition {
|
|
|
14
15
|
type CompletionMap = Record<string, readonly GsdCommandDefinition[]>;
|
|
15
16
|
|
|
16
17
|
export const GSD_COMMAND_DESCRIPTION =
|
|
17
|
-
"GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update";
|
|
18
|
+
"GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update|fast";
|
|
18
19
|
|
|
19
20
|
export const TOP_LEVEL_SUBCOMMANDS: readonly GsdCommandDefinition[] = [
|
|
20
21
|
{ cmd: "help", desc: "Categorized command reference with descriptions" },
|
|
@@ -64,6 +65,8 @@ export const TOP_LEVEL_SUBCOMMANDS: readonly GsdCommandDefinition[] = [
|
|
|
64
65
|
{ cmd: "start", desc: "Start a workflow template (bugfix, spike, feature, etc.)" },
|
|
65
66
|
{ cmd: "templates", desc: "List available workflow templates" },
|
|
66
67
|
{ cmd: "extensions", desc: "Manage extensions (list, enable, disable, info)" },
|
|
68
|
+
{ cmd: "fast", desc: "Toggle OpenAI service tier (on/off/flex/status)" },
|
|
69
|
+
{ cmd: "workflow", desc: "Custom workflow lifecycle (new, run, list, validate, pause, resume)" },
|
|
67
70
|
];
|
|
68
71
|
|
|
69
72
|
const NESTED_COMPLETIONS: CompletionMap = {
|
|
@@ -176,6 +179,12 @@ const NESTED_COMPLETIONS: CompletionMap = {
|
|
|
176
179
|
{ cmd: "disable", desc: "Disable an extension" },
|
|
177
180
|
{ cmd: "info", desc: "Show extension details" },
|
|
178
181
|
],
|
|
182
|
+
fast: [
|
|
183
|
+
{ cmd: "on", desc: "Priority tier (2x cost, faster)" },
|
|
184
|
+
{ cmd: "off", desc: "Disable service tier" },
|
|
185
|
+
{ cmd: "flex", desc: "Flex tier (0.5x cost, slower)" },
|
|
186
|
+
{ cmd: "status", desc: "Show current service tier setting" },
|
|
187
|
+
],
|
|
179
188
|
doctor: [
|
|
180
189
|
{ cmd: "fix", desc: "Auto-fix detected issues" },
|
|
181
190
|
{ cmd: "heal", desc: "AI-driven deep healing" },
|
|
@@ -199,6 +208,14 @@ const NESTED_COMPLETIONS: CompletionMap = {
|
|
|
199
208
|
{ cmd: "ok", desc: "Model was appropriate for this task" },
|
|
200
209
|
{ cmd: "under", desc: "Model was underqualified for this task" },
|
|
201
210
|
],
|
|
211
|
+
workflow: [
|
|
212
|
+
{ cmd: "new", desc: "Create a new workflow definition (via skill)" },
|
|
213
|
+
{ cmd: "run", desc: "Create a run and start auto-mode" },
|
|
214
|
+
{ cmd: "list", desc: "List workflow runs" },
|
|
215
|
+
{ cmd: "validate", desc: "Validate a workflow definition YAML" },
|
|
216
|
+
{ cmd: "pause", desc: "Pause custom workflow auto-mode" },
|
|
217
|
+
{ cmd: "resume", desc: "Resume paused custom workflow auto-mode" },
|
|
218
|
+
],
|
|
202
219
|
};
|
|
203
220
|
|
|
204
221
|
function filterOptions(
|
|
@@ -302,6 +319,28 @@ export function getGsdArgumentCompletions(prefix: string) {
|
|
|
302
319
|
return [{ value: "undo --force", label: "--force", description: "Skip confirmation prompt" }];
|
|
303
320
|
}
|
|
304
321
|
|
|
322
|
+
// Workflow definition-name completion for `workflow run <name>` and `workflow validate <name>`
|
|
323
|
+
if (command === "workflow" && (subcommand === "run" || subcommand === "validate") && parts.length <= 3) {
|
|
324
|
+
try {
|
|
325
|
+
const defsDir = join(resolveProjectRoot(process.cwd()), ".gsd", "workflow-defs");
|
|
326
|
+
if (existsSync(defsDir)) {
|
|
327
|
+
return readdirSync(defsDir)
|
|
328
|
+
.filter((f) => f.endsWith(".yaml") && f.startsWith(third))
|
|
329
|
+
.map((f) => {
|
|
330
|
+
const name = f.replace(/\.yaml$/, "");
|
|
331
|
+
return {
|
|
332
|
+
value: `workflow ${subcommand} ${name}`,
|
|
333
|
+
label: name,
|
|
334
|
+
description: `Workflow definition: ${name}`,
|
|
335
|
+
};
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
} catch {
|
|
339
|
+
// ignore filesystem errors during completion
|
|
340
|
+
}
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
|
|
305
344
|
const nested = NESTED_COMPLETIONS[command];
|
|
306
345
|
if (nested && parts.length <= 2) {
|
|
307
346
|
return filterOptions(subcommand, nested, command);
|
|
@@ -52,6 +52,7 @@ export function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
52
52
|
" /gsd keys API key manager [list|add|remove|test|rotate|doctor]",
|
|
53
53
|
" /gsd hooks Show post-unit hook configuration",
|
|
54
54
|
" /gsd extensions Manage extensions [list|enable|disable|info]",
|
|
55
|
+
" /gsd fast Toggle OpenAI service tier [on|off|flex|status]",
|
|
55
56
|
"",
|
|
56
57
|
"MAINTENANCE",
|
|
57
58
|
" /gsd doctor Diagnose and repair .gsd/ state [audit|fix|heal] [scope]",
|
|
@@ -172,6 +172,11 @@ Examples:
|
|
|
172
172
|
await handleUpdate(ctx);
|
|
173
173
|
return true;
|
|
174
174
|
}
|
|
175
|
+
if (trimmed === "fast" || trimmed.startsWith("fast ")) {
|
|
176
|
+
const { handleFast } = await import("../../service-tier.js");
|
|
177
|
+
await handleFast(trimmed.replace(/^fast\s*/, "").trim(), ctx);
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
175
180
|
if (trimmed === "extensions" || trimmed.startsWith("extensions ")) {
|
|
176
181
|
const { handleExtensions } = await import("../../commands-extensions.js");
|
|
177
182
|
await handleExtensions(trimmed.replace(/^extensions\s*/, "").trim(), ctx);
|
|
@@ -2,6 +2,7 @@ import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent
|
|
|
2
2
|
|
|
3
3
|
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { parse as parseYaml } from "yaml";
|
|
5
6
|
|
|
6
7
|
import { handleQuick } from "../../quick.js";
|
|
7
8
|
import { showDiscuss, showHeadlessMilestoneCreation, showQueue } from "../../guided-flow.js";
|
|
@@ -13,8 +14,171 @@ import { loadEffectiveGSDPreferences } from "../../preferences.js";
|
|
|
13
14
|
import { nextMilestoneId } from "../../milestone-ids.js";
|
|
14
15
|
import { findMilestoneIds } from "../../guided-flow.js";
|
|
15
16
|
import { projectRoot } from "../context.js";
|
|
17
|
+
import { createRun, listRuns } from "../../run-manager.js";
|
|
18
|
+
import {
|
|
19
|
+
setActiveEngineId,
|
|
20
|
+
setActiveRunDir,
|
|
21
|
+
startAuto,
|
|
22
|
+
pauseAuto,
|
|
23
|
+
isAutoActive,
|
|
24
|
+
getActiveEngineId,
|
|
25
|
+
} from "../../auto.js";
|
|
26
|
+
import { validateDefinition } from "../../definition-loader.js";
|
|
27
|
+
|
|
28
|
+
// ─── Custom Workflow Subcommands ─────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
const WORKFLOW_USAGE = [
|
|
31
|
+
"Usage: /gsd workflow <subcommand>",
|
|
32
|
+
"",
|
|
33
|
+
" new — Create a new workflow definition (via skill)",
|
|
34
|
+
" run <name> [k=v] — Create a run and start auto-mode",
|
|
35
|
+
" list [name] — List workflow runs (optionally filtered by name)",
|
|
36
|
+
" validate <name> — Validate a workflow definition YAML",
|
|
37
|
+
" pause — Pause custom workflow auto-mode",
|
|
38
|
+
" resume — Resume paused custom workflow auto-mode",
|
|
39
|
+
].join("\n");
|
|
40
|
+
|
|
41
|
+
async function handleCustomWorkflow(
|
|
42
|
+
sub: string,
|
|
43
|
+
ctx: ExtensionCommandContext,
|
|
44
|
+
pi: ExtensionAPI,
|
|
45
|
+
): Promise<boolean> {
|
|
46
|
+
// Bare `/gsd workflow` — show usage
|
|
47
|
+
if (!sub) {
|
|
48
|
+
ctx.ui.notify(WORKFLOW_USAGE, "info");
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── new ──
|
|
53
|
+
if (sub === "new") {
|
|
54
|
+
ctx.ui.notify("Use the create-workflow skill: /skill create-workflow", "info");
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── run <name> [param=value ...] ──
|
|
59
|
+
if (sub === "run" || sub.startsWith("run ")) {
|
|
60
|
+
const args = sub.slice("run".length).trim();
|
|
61
|
+
if (!args) {
|
|
62
|
+
ctx.ui.notify("Usage: /gsd workflow run <name> [param=value ...]", "warning");
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
const parts = args.split(/\s+/);
|
|
66
|
+
const defName = parts[0];
|
|
67
|
+
const overrides: Record<string, string> = {};
|
|
68
|
+
for (let i = 1; i < parts.length; i++) {
|
|
69
|
+
const eqIdx = parts[i].indexOf("=");
|
|
70
|
+
if (eqIdx > 0) {
|
|
71
|
+
overrides[parts[i].slice(0, eqIdx)] = parts[i].slice(eqIdx + 1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const base = projectRoot();
|
|
76
|
+
const runDir = createRun(base, defName, Object.keys(overrides).length > 0 ? overrides : undefined);
|
|
77
|
+
setActiveEngineId("custom");
|
|
78
|
+
setActiveRunDir(runDir);
|
|
79
|
+
ctx.ui.notify(`Created workflow run: ${defName}\nRun dir: ${runDir}`, "info");
|
|
80
|
+
await startAuto(ctx, pi, base, false);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
// Clean up engine state so a failed workflow run doesn't pollute the next /gsd auto
|
|
83
|
+
setActiveEngineId(null);
|
|
84
|
+
setActiveRunDir(null);
|
|
85
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
86
|
+
ctx.ui.notify(`Failed to run workflow "${defName}": ${msg}`, "error");
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ── list [name] ──
|
|
92
|
+
if (sub === "list" || sub.startsWith("list ")) {
|
|
93
|
+
const filterName = sub.slice("list".length).trim() || undefined;
|
|
94
|
+
const base = projectRoot();
|
|
95
|
+
const runs = listRuns(base, filterName);
|
|
96
|
+
if (runs.length === 0) {
|
|
97
|
+
ctx.ui.notify("No workflow runs found.", "info");
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
const lines = runs.map((r) => {
|
|
101
|
+
const stepInfo = `${r.steps.completed}/${r.steps.total} steps`;
|
|
102
|
+
return `• ${r.name} [${r.timestamp}] — ${r.status} (${stepInfo})`;
|
|
103
|
+
});
|
|
104
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ── validate <name> ──
|
|
109
|
+
if (sub === "validate" || sub.startsWith("validate ")) {
|
|
110
|
+
const defName = sub.slice("validate".length).trim();
|
|
111
|
+
if (!defName) {
|
|
112
|
+
ctx.ui.notify("Usage: /gsd workflow validate <name>", "warning");
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
const base = projectRoot();
|
|
116
|
+
const defPath = join(base, ".gsd", "workflow-defs", `${defName}.yaml`);
|
|
117
|
+
if (!existsSync(defPath)) {
|
|
118
|
+
ctx.ui.notify(`Definition not found: ${defPath}`, "error");
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const raw = readFileSync(defPath, "utf-8");
|
|
123
|
+
const parsed = parseYaml(raw);
|
|
124
|
+
const result = validateDefinition(parsed);
|
|
125
|
+
if (result.valid) {
|
|
126
|
+
ctx.ui.notify(`✓ "${defName}" is a valid workflow definition.`, "info");
|
|
127
|
+
} else {
|
|
128
|
+
ctx.ui.notify(`✗ "${defName}" has errors:\n - ${result.errors.join("\n - ")}`, "error");
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
132
|
+
ctx.ui.notify(`Failed to validate "${defName}": ${msg}`, "error");
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── pause ──
|
|
138
|
+
if (sub === "pause") {
|
|
139
|
+
const engineId = getActiveEngineId();
|
|
140
|
+
if (engineId === "dev" || engineId === null) {
|
|
141
|
+
ctx.ui.notify("No custom workflow is running. Use /gsd pause for dev workflow.", "warning");
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
if (!isAutoActive()) {
|
|
145
|
+
ctx.ui.notify("Auto-mode is not active.", "warning");
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
await pauseAuto(ctx, pi);
|
|
149
|
+
ctx.ui.notify("Custom workflow paused.", "info");
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ── resume ──
|
|
154
|
+
if (sub === "resume") {
|
|
155
|
+
const engineId = getActiveEngineId();
|
|
156
|
+
if (engineId === "dev" || engineId === null) {
|
|
157
|
+
ctx.ui.notify("No custom workflow to resume. Use /gsd auto for dev workflow.", "warning");
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
await startAuto(ctx, pi, projectRoot(), false);
|
|
162
|
+
ctx.ui.notify("Custom workflow resumed.", "info");
|
|
163
|
+
} catch (err) {
|
|
164
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
165
|
+
ctx.ui.notify(`Failed to resume workflow: ${msg}`, "error");
|
|
166
|
+
}
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Unknown subcommand — show usage
|
|
171
|
+
ctx.ui.notify(`Unknown workflow subcommand: "${sub}"\n\n${WORKFLOW_USAGE}`, "warning");
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
16
174
|
|
|
17
175
|
export async function handleWorkflowCommand(trimmed: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<boolean> {
|
|
176
|
+
// ── Custom workflow commands (`/gsd workflow ...`) ──
|
|
177
|
+
if (trimmed === "workflow" || trimmed.startsWith("workflow ")) {
|
|
178
|
+
const sub = trimmed.slice("workflow".length).trim();
|
|
179
|
+
return handleCustomWorkflow(sub, ctx, pi);
|
|
180
|
+
}
|
|
181
|
+
|
|
18
182
|
if (trimmed === "queue") {
|
|
19
183
|
await showQueue(ctx, pi, projectRoot());
|
|
20
184
|
return true;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* context-injector.ts — Inject prior step artifacts as context into step prompts.
|
|
3
|
+
*
|
|
4
|
+
* Reads the frozen DEFINITION.yaml from a run directory, finds the current step's
|
|
5
|
+
* `contextFrom` references, locates each referenced step's `produces` artifacts
|
|
6
|
+
* on disk, reads their content (truncated to 10k chars), and prepends formatted
|
|
7
|
+
* context blocks to the step prompt.
|
|
8
|
+
*
|
|
9
|
+
* Observability:
|
|
10
|
+
* - Truncation is logged via console.warn when it occurs, preventing silent overflow.
|
|
11
|
+
* - Missing artifact files are skipped silently (the step may not have produced them yet).
|
|
12
|
+
* - Unknown step IDs in contextFrom produce a console.warn for diagnosis.
|
|
13
|
+
* - The frozen DEFINITION.yaml on disk is the single source of truth for contextFrom config.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
17
|
+
import { join, resolve, sep } from "node:path";
|
|
18
|
+
import type { StepDefinition } from "./definition-loader.js";
|
|
19
|
+
import { readFrozenDefinition } from "./custom-workflow-engine.js";
|
|
20
|
+
|
|
21
|
+
/** Maximum characters per artifact to prevent context window blowout. */
|
|
22
|
+
const MAX_CONTEXT_CHARS = 10_000;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Inject context from prior step artifacts into a step's prompt.
|
|
26
|
+
*
|
|
27
|
+
* Reads the frozen DEFINITION.yaml from `runDir`, finds the step matching
|
|
28
|
+
* `stepId`, and for each step ID in its `contextFrom` array, looks up that
|
|
29
|
+
* step's `produces` paths, reads them from disk (relative to `runDir`),
|
|
30
|
+
* truncates to MAX_CONTEXT_CHARS, and prepends as labeled context blocks.
|
|
31
|
+
*
|
|
32
|
+
* @param runDir — absolute path to the workflow run directory
|
|
33
|
+
* @param stepId — the step ID whose prompt to enrich
|
|
34
|
+
* @param prompt — the original step prompt
|
|
35
|
+
* @returns The prompt with context blocks prepended, or unchanged if no context applies
|
|
36
|
+
* @throws Error if DEFINITION.yaml is missing or unreadable
|
|
37
|
+
*/
|
|
38
|
+
export function injectContext(
|
|
39
|
+
runDir: string,
|
|
40
|
+
stepId: string,
|
|
41
|
+
prompt: string,
|
|
42
|
+
): string {
|
|
43
|
+
const def = readFrozenDefinition(runDir);
|
|
44
|
+
|
|
45
|
+
const step = def.steps.find((s: StepDefinition) => s.id === stepId);
|
|
46
|
+
if (!step || !step.contextFrom || step.contextFrom.length === 0) {
|
|
47
|
+
return prompt;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const contextBlocks: string[] = [];
|
|
51
|
+
|
|
52
|
+
for (const refStepId of step.contextFrom) {
|
|
53
|
+
const refStep = def.steps.find((s: StepDefinition) => s.id === refStepId);
|
|
54
|
+
if (!refStep) {
|
|
55
|
+
console.warn(
|
|
56
|
+
`context-injector: step "${stepId}" references unknown step "${refStepId}" in contextFrom — skipping`,
|
|
57
|
+
);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!refStep.produces || refStep.produces.length === 0) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const relPath of refStep.produces) {
|
|
66
|
+
const absPath = resolve(runDir, relPath);
|
|
67
|
+
// Path traversal guard: ensure resolved path stays within runDir
|
|
68
|
+
if (!absPath.startsWith(resolve(runDir) + sep) && absPath !== resolve(runDir)) {
|
|
69
|
+
console.warn(
|
|
70
|
+
`context-injector: artifact path "${relPath}" resolves outside runDir — skipping`,
|
|
71
|
+
);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (!existsSync(absPath)) {
|
|
75
|
+
// Artifact not yet produced or optional — skip silently
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let content = readFileSync(absPath, "utf-8");
|
|
80
|
+
|
|
81
|
+
if (content.length > MAX_CONTEXT_CHARS) {
|
|
82
|
+
console.warn(
|
|
83
|
+
`context-injector: truncating artifact "${relPath}" from step "${refStepId}" ` +
|
|
84
|
+
`(${content.length} chars → ${MAX_CONTEXT_CHARS} chars)`,
|
|
85
|
+
);
|
|
86
|
+
content = content.slice(0, MAX_CONTEXT_CHARS) + "\n...[truncated]";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
contextBlocks.push(
|
|
90
|
+
`--- Context from step "${refStepId}" (file: ${relPath}) ---\n${content}\n---`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (contextBlocks.length === 0) {
|
|
96
|
+
return prompt;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return contextBlocks.join("\n\n") + "\n\n" + prompt;
|
|
100
|
+
}
|
|
@@ -57,6 +57,7 @@ export function queryDecisions(opts?: DecisionQueryOpts): Decision[] {
|
|
|
57
57
|
choice: row['choice'] as string,
|
|
58
58
|
rationale: row['rationale'] as string,
|
|
59
59
|
revisable: row['revisable'] as string,
|
|
60
|
+
made_by: (row['made_by'] as string as import('./types.js').DecisionMadeBy) ?? 'agent',
|
|
60
61
|
superseded_by: null,
|
|
61
62
|
}));
|
|
62
63
|
} catch {
|
|
@@ -121,10 +122,10 @@ export function queryRequirements(opts?: RequirementQueryOpts): Requirement[] {
|
|
|
121
122
|
export function formatDecisionsForPrompt(decisions: Decision[]): string {
|
|
122
123
|
if (decisions.length === 0) return '';
|
|
123
124
|
|
|
124
|
-
const header = '| # | When | Scope | Decision | Choice | Rationale | Revisable? |';
|
|
125
|
-
const separator = '
|
|
125
|
+
const header = '| # | When | Scope | Decision | Choice | Rationale | Revisable? | Made By |';
|
|
126
|
+
const separator = '|---|------|-------|----------|--------|-----------|------------|---------|';
|
|
126
127
|
const rows = decisions.map(d =>
|
|
127
|
-
`| ${d.id} | ${d.when_context} | ${d.scope} | ${d.decision} | ${d.choice} | ${d.rationale} | ${d.revisable} |`,
|
|
128
|
+
`| ${d.id} | ${d.when_context} | ${d.scope} | ${d.decision} | ${d.choice} | ${d.rationale} | ${d.revisable} | ${d.made_by ?? 'agent'} |`,
|
|
128
129
|
);
|
|
129
130
|
|
|
130
131
|
return [header, separator, ...rows].join('\n');
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* custom-execution-policy.ts — ExecutionPolicy for custom workflows.
|
|
3
|
+
*
|
|
4
|
+
* Delegates verification to the step-level verification module which reads
|
|
5
|
+
* the frozen DEFINITION.yaml and dispatches to the appropriate policy handler.
|
|
6
|
+
*
|
|
7
|
+
* Observability:
|
|
8
|
+
* - verify() returns the outcome from runCustomVerification() — four policies
|
|
9
|
+
* are supported: content-heuristic, shell-command, prompt-verify, human-review.
|
|
10
|
+
* - selectModel() returns null — defers to loop defaults.
|
|
11
|
+
* - recover() returns retry — simple default recovery strategy.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ExecutionPolicy } from "./execution-policy.js";
|
|
15
|
+
import type { RecoveryAction, CloseoutResult } from "./engine-types.js";
|
|
16
|
+
import { runCustomVerification } from "./custom-verification.js";
|
|
17
|
+
|
|
18
|
+
export class CustomExecutionPolicy implements ExecutionPolicy {
|
|
19
|
+
private readonly runDir: string;
|
|
20
|
+
|
|
21
|
+
constructor(runDir: string) {
|
|
22
|
+
this.runDir = runDir;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** No workspace preparation needed for custom workflows. */
|
|
26
|
+
async prepareWorkspace(_basePath: string, _milestoneId: string): Promise<void> {
|
|
27
|
+
// No-op — custom workflows don't need worktree setup
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Defer model selection to loop defaults. */
|
|
31
|
+
async selectModel(
|
|
32
|
+
_unitType: string,
|
|
33
|
+
_unitId: string,
|
|
34
|
+
_context: { basePath: string },
|
|
35
|
+
): Promise<{ tier: string; modelDowngraded: boolean } | null> {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Verify step output by dispatching to the step's configured verification policy.
|
|
41
|
+
*
|
|
42
|
+
* Extracts the step ID from unitId (format: "<workflowName>/<stepId>")
|
|
43
|
+
* and calls runCustomVerification() which reads the frozen DEFINITION.yaml
|
|
44
|
+
* to determine which policy to apply.
|
|
45
|
+
*/
|
|
46
|
+
async verify(
|
|
47
|
+
_unitType: string,
|
|
48
|
+
unitId: string,
|
|
49
|
+
_context: { basePath: string },
|
|
50
|
+
): Promise<"continue" | "retry" | "pause"> {
|
|
51
|
+
const parts = unitId.split("/");
|
|
52
|
+
const stepId = parts[parts.length - 1];
|
|
53
|
+
return runCustomVerification(this.runDir, stepId);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Default recovery: retry the step. */
|
|
57
|
+
async recover(
|
|
58
|
+
_unitType: string,
|
|
59
|
+
_unitId: string,
|
|
60
|
+
_context: { basePath: string },
|
|
61
|
+
): Promise<RecoveryAction> {
|
|
62
|
+
return { outcome: "retry", reason: "Default retry" };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** No-op closeout — no commits or artifact capture. */
|
|
66
|
+
async closeout(
|
|
67
|
+
_unitType: string,
|
|
68
|
+
_unitId: string,
|
|
69
|
+
_context: { basePath: string; startedAt: number },
|
|
70
|
+
): Promise<CloseoutResult> {
|
|
71
|
+
return { committed: false, artifacts: [] };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* custom-verification.ts — Step verification for custom workflows.
|
|
3
|
+
*
|
|
4
|
+
* Reads the frozen DEFINITION.yaml from a run directory, finds the step's
|
|
5
|
+
* `verify` policy, and dispatches to the appropriate handler. Four policies:
|
|
6
|
+
*
|
|
7
|
+
* - content-heuristic: file existence + optional minSize + optional pattern match
|
|
8
|
+
* - shell-command: spawnSync with 30s timeout, exit 0 → continue, else retry
|
|
9
|
+
* - prompt-verify: always "pause" (defers to agent)
|
|
10
|
+
* - human-review: always "pause" (waits for manual inspection)
|
|
11
|
+
* - (no policy): returns "continue" (passthrough)
|
|
12
|
+
*
|
|
13
|
+
* Observability:
|
|
14
|
+
* - Return value is the typed verification outcome ("continue" | "retry" | "pause").
|
|
15
|
+
* - shell-command captures stderr from spawnSync — callers can inspect on retry.
|
|
16
|
+
* - content-heuristic logs the specific failure (missing file, below minSize, pattern mismatch).
|
|
17
|
+
* - The frozen DEFINITION.yaml on disk is the single source of truth for step policies.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { readFileSync, existsSync, statSync } from "node:fs";
|
|
21
|
+
import { join, resolve, sep } from "node:path";
|
|
22
|
+
import { spawnSync } from "node:child_process";
|
|
23
|
+
import type { StepDefinition, VerifyPolicy } from "./definition-loader.js";
|
|
24
|
+
import { readFrozenDefinition } from "./custom-workflow-engine.js";
|
|
25
|
+
|
|
26
|
+
/** Verification outcome type — matches ExecutionPolicy.verify() return type. */
|
|
27
|
+
export type VerificationOutcome = "continue" | "retry" | "pause";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Run custom verification for a specific step in a workflow run.
|
|
31
|
+
*
|
|
32
|
+
* Reads the frozen DEFINITION.yaml from `runDir`, finds the step with the
|
|
33
|
+
* given `stepId`, and dispatches to the appropriate verification handler
|
|
34
|
+
* based on the step's `verify.policy` field.
|
|
35
|
+
*
|
|
36
|
+
* @param runDir — absolute path to the workflow run directory
|
|
37
|
+
* @param stepId — the step ID to verify (e.g. "step-1")
|
|
38
|
+
* @returns "continue" if verification passes, "retry" if it should retry, "pause" if it needs review
|
|
39
|
+
* @throws Error if DEFINITION.yaml is missing or unreadable
|
|
40
|
+
*/
|
|
41
|
+
export function runCustomVerification(
|
|
42
|
+
runDir: string,
|
|
43
|
+
stepId: string,
|
|
44
|
+
): VerificationOutcome {
|
|
45
|
+
const def = readFrozenDefinition(runDir);
|
|
46
|
+
|
|
47
|
+
const step = def.steps.find((s: StepDefinition) => s.id === stepId);
|
|
48
|
+
if (!step) {
|
|
49
|
+
// Step not found in definition — nothing to verify, continue
|
|
50
|
+
return "continue";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!step.verify) {
|
|
54
|
+
// No verification policy configured — passthrough
|
|
55
|
+
return "continue";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return dispatchPolicy(runDir, step, step.verify);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Dispatch to the correct policy handler.
|
|
63
|
+
*/
|
|
64
|
+
function dispatchPolicy(
|
|
65
|
+
runDir: string,
|
|
66
|
+
step: StepDefinition,
|
|
67
|
+
verify: VerifyPolicy,
|
|
68
|
+
): VerificationOutcome {
|
|
69
|
+
switch (verify.policy) {
|
|
70
|
+
case "content-heuristic":
|
|
71
|
+
return handleContentHeuristic(runDir, step, verify);
|
|
72
|
+
case "shell-command":
|
|
73
|
+
return handleShellCommand(runDir, verify);
|
|
74
|
+
case "prompt-verify":
|
|
75
|
+
return "pause";
|
|
76
|
+
case "human-review":
|
|
77
|
+
return "pause";
|
|
78
|
+
default:
|
|
79
|
+
// Unknown policy — safe default is pause
|
|
80
|
+
return "pause";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* content-heuristic handler.
|
|
86
|
+
*
|
|
87
|
+
* For each path in the step's `produces` array:
|
|
88
|
+
* 1. Check that the file exists (resolved relative to runDir)
|
|
89
|
+
* 2. If `minSize` is set, check that file size >= minSize bytes
|
|
90
|
+
* 3. If `pattern` is set, check that file content matches the regex
|
|
91
|
+
*
|
|
92
|
+
* Returns "continue" if all checks pass, "pause" if any fail.
|
|
93
|
+
* If `produces` is empty or undefined, returns "continue" (nothing to check).
|
|
94
|
+
*/
|
|
95
|
+
function handleContentHeuristic(
|
|
96
|
+
runDir: string,
|
|
97
|
+
step: StepDefinition,
|
|
98
|
+
verify: { policy: "content-heuristic"; minSize?: number; pattern?: string },
|
|
99
|
+
): VerificationOutcome {
|
|
100
|
+
const produces = step.produces;
|
|
101
|
+
if (!produces || produces.length === 0) {
|
|
102
|
+
return "continue";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const relPath of produces) {
|
|
106
|
+
const absPath = resolve(runDir, relPath);
|
|
107
|
+
// Path traversal guard
|
|
108
|
+
if (!absPath.startsWith(resolve(runDir) + sep) && absPath !== resolve(runDir)) {
|
|
109
|
+
return "pause";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 1. File existence
|
|
113
|
+
if (!existsSync(absPath)) {
|
|
114
|
+
return "pause";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 2. Minimum size check
|
|
118
|
+
if (verify.minSize !== undefined) {
|
|
119
|
+
const stat = statSync(absPath);
|
|
120
|
+
if (stat.size < verify.minSize) {
|
|
121
|
+
return "pause";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 3. Pattern match check (with timeout guard against ReDoS)
|
|
126
|
+
if (verify.pattern !== undefined) {
|
|
127
|
+
const content = readFileSync(absPath, "utf-8");
|
|
128
|
+
try {
|
|
129
|
+
if (!new RegExp(verify.pattern).test(content)) {
|
|
130
|
+
return "pause";
|
|
131
|
+
}
|
|
132
|
+
} catch {
|
|
133
|
+
// Invalid regex at runtime — treat as verification failure
|
|
134
|
+
return "pause";
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return "continue";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* shell-command handler.
|
|
144
|
+
*
|
|
145
|
+
* Runs the command via `sh -c` with cwd set to the run directory
|
|
146
|
+
* and a 30-second timeout. Returns "continue" if exit code 0,
|
|
147
|
+
* "retry" otherwise (including timeout/signal kills).
|
|
148
|
+
*
|
|
149
|
+
* SECURITY: The command string comes from a frozen DEFINITION.yaml written
|
|
150
|
+
* at run-creation time. The trust boundary is the workflow definition author.
|
|
151
|
+
* Commands run with the same privileges as the GSD process. Only use
|
|
152
|
+
* shell-command verification with definitions you trust.
|
|
153
|
+
*/
|
|
154
|
+
function handleShellCommand(
|
|
155
|
+
runDir: string,
|
|
156
|
+
verify: { policy: "shell-command"; command: string },
|
|
157
|
+
): VerificationOutcome {
|
|
158
|
+
// Guard: reject commands containing shell expansion patterns that suggest injection
|
|
159
|
+
const dangerousPatterns = /\$\(|`|;\s*(rm|curl|wget|nc|bash|sh|eval)\b/;
|
|
160
|
+
if (dangerousPatterns.test(verify.command)) {
|
|
161
|
+
console.warn(
|
|
162
|
+
`custom-verification: shell-command contains suspicious pattern, skipping: ${verify.command}`,
|
|
163
|
+
);
|
|
164
|
+
return "pause";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const result = spawnSync("sh", ["-c", verify.command], {
|
|
168
|
+
cwd: runDir,
|
|
169
|
+
timeout: 30_000,
|
|
170
|
+
encoding: "utf-8",
|
|
171
|
+
stdio: "pipe",
|
|
172
|
+
env: { ...process.env, PATH: process.env.PATH },
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (result.status === 0) {
|
|
176
|
+
return "continue";
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return "retry";
|
|
180
|
+
}
|