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
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
// GSD Extension — Unified Rule Registry
|
|
2
|
+
//
|
|
3
|
+
// Holds all dispatch rules and hooks as a flat list of UnifiedRule objects.
|
|
4
|
+
// Provides evaluation methods for each phase (dispatch, post-unit, pre-dispatch)
|
|
5
|
+
// and encapsulates mutable hook state as instance fields.
|
|
6
|
+
//
|
|
7
|
+
// A module-level singleton accessor allows existing code to migrate incrementally.
|
|
8
|
+
import { resolvePostUnitHooks, resolvePreDispatchHooks } from "./preferences.js";
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
// ─── Artifact Path Resolution ──────────────────────────────────────────────
|
|
12
|
+
export function resolveHookArtifactPath(basePath, unitId, artifactName) {
|
|
13
|
+
const parts = unitId.split("/");
|
|
14
|
+
if (parts.length === 3) {
|
|
15
|
+
const [mid, sid, tid] = parts;
|
|
16
|
+
return join(basePath, ".gsd", "milestones", mid, "slices", sid, "tasks", `${tid}-${artifactName}`);
|
|
17
|
+
}
|
|
18
|
+
if (parts.length === 2) {
|
|
19
|
+
const [mid, sid] = parts;
|
|
20
|
+
return join(basePath, ".gsd", "milestones", mid, "slices", sid, artifactName);
|
|
21
|
+
}
|
|
22
|
+
return join(basePath, ".gsd", "milestones", parts[0], artifactName);
|
|
23
|
+
}
|
|
24
|
+
// ─── Dispatch Rule Conversion ──────────────────────────────────────────────
|
|
25
|
+
/**
|
|
26
|
+
* Convert an array of DispatchRule objects to UnifiedRule[] format.
|
|
27
|
+
* Preserves exact array order — dispatch is order-dependent (first-match-wins).
|
|
28
|
+
*/
|
|
29
|
+
export function convertDispatchRules(rules) {
|
|
30
|
+
return rules.map((rule) => ({
|
|
31
|
+
name: rule.name,
|
|
32
|
+
when: "dispatch",
|
|
33
|
+
evaluation: "first-match",
|
|
34
|
+
where: rule.match,
|
|
35
|
+
then: (result) => result,
|
|
36
|
+
description: `Dispatch rule: ${rule.name}`,
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
// ─── RuleRegistry ─────────────────────────────────────────────────────────
|
|
40
|
+
const HOOK_STATE_FILE = "hook-state.json";
|
|
41
|
+
export class RuleRegistry {
|
|
42
|
+
/** Static dispatch rules provided at construction time. */
|
|
43
|
+
dispatchRules;
|
|
44
|
+
// ── Mutable hook state (encapsulated, not module-level) ──────────────
|
|
45
|
+
activeHook = null;
|
|
46
|
+
hookQueue = [];
|
|
47
|
+
cycleCounts = new Map();
|
|
48
|
+
retryPending = false;
|
|
49
|
+
retryTrigger = null;
|
|
50
|
+
constructor(dispatchRules) {
|
|
51
|
+
this.dispatchRules = dispatchRules;
|
|
52
|
+
}
|
|
53
|
+
// ── Core query ───────────────────────────────────────────────────────
|
|
54
|
+
/**
|
|
55
|
+
* Returns all rules: static dispatch rules + dynamically loaded hook rules.
|
|
56
|
+
* Hook rules are loaded fresh from preferences on each call (not cached).
|
|
57
|
+
*/
|
|
58
|
+
listRules() {
|
|
59
|
+
const rules = [...this.dispatchRules];
|
|
60
|
+
// Convert post-unit hooks to unified rules
|
|
61
|
+
const postHooks = resolvePostUnitHooks();
|
|
62
|
+
for (const hook of postHooks) {
|
|
63
|
+
rules.push({
|
|
64
|
+
name: hook.name,
|
|
65
|
+
when: "post-unit",
|
|
66
|
+
evaluation: "all-matching",
|
|
67
|
+
where: (unitType) => hook.after.includes(unitType),
|
|
68
|
+
then: () => hook,
|
|
69
|
+
description: `Post-unit hook: fires after ${hook.after.join(", ")}`,
|
|
70
|
+
lifecycle: {
|
|
71
|
+
artifact: hook.artifact,
|
|
72
|
+
retry_on: hook.retry_on,
|
|
73
|
+
max_cycles: hook.max_cycles,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Convert pre-dispatch hooks to unified rules
|
|
78
|
+
const preHooks = resolvePreDispatchHooks();
|
|
79
|
+
for (const hook of preHooks) {
|
|
80
|
+
rules.push({
|
|
81
|
+
name: hook.name,
|
|
82
|
+
when: "pre-dispatch",
|
|
83
|
+
evaluation: "all-matching",
|
|
84
|
+
where: (unitType) => hook.before.includes(unitType),
|
|
85
|
+
then: () => hook,
|
|
86
|
+
description: `Pre-dispatch hook: fires before ${hook.before.join(", ")}`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return rules;
|
|
90
|
+
}
|
|
91
|
+
// ── Dispatch evaluation (async, first-match-wins) ───────────────────
|
|
92
|
+
/**
|
|
93
|
+
* Iterate dispatch rules in order. First match wins.
|
|
94
|
+
* Returns stop action if no rule matches (unhandled phase).
|
|
95
|
+
*/
|
|
96
|
+
async evaluateDispatch(ctx) {
|
|
97
|
+
for (const rule of this.dispatchRules) {
|
|
98
|
+
const result = await rule.where(ctx);
|
|
99
|
+
if (result) {
|
|
100
|
+
if (result.action !== "skip")
|
|
101
|
+
result.matchedRule = rule.name;
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
action: "stop",
|
|
107
|
+
reason: `Unhandled phase "${ctx.state.phase}" — run /gsd doctor to diagnose.`,
|
|
108
|
+
level: "info",
|
|
109
|
+
matchedRule: "<no-match>",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// ── Post-unit hook evaluation (sync, all-matching with lifecycle) ────
|
|
113
|
+
/**
|
|
114
|
+
* Replicate exact semantics of checkPostUnitHooks from post-unit-hooks.ts:
|
|
115
|
+
* hook-on-hook prevention, idempotency, cycle limits, retry_on, dequeue.
|
|
116
|
+
*/
|
|
117
|
+
evaluatePostUnit(completedUnitType, completedUnitId, basePath) {
|
|
118
|
+
// If we just completed a hook unit, handle its result
|
|
119
|
+
if (this.activeHook) {
|
|
120
|
+
return this._handleHookCompletion(basePath);
|
|
121
|
+
}
|
|
122
|
+
// Don't trigger hooks for other hook units (prevent hook-on-hook chains)
|
|
123
|
+
// Don't trigger hooks for triage units or quick-task units
|
|
124
|
+
if (completedUnitType.startsWith("hook/") ||
|
|
125
|
+
completedUnitType === "triage-captures" ||
|
|
126
|
+
completedUnitType === "quick-task") {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
// Check if any hooks are configured for this unit type
|
|
130
|
+
const hooks = resolvePostUnitHooks().filter(h => h.after.includes(completedUnitType));
|
|
131
|
+
if (hooks.length === 0)
|
|
132
|
+
return null;
|
|
133
|
+
// Build hook queue for this trigger
|
|
134
|
+
this.hookQueue = hooks.map(config => ({
|
|
135
|
+
config,
|
|
136
|
+
triggerUnitType: completedUnitType,
|
|
137
|
+
triggerUnitId: completedUnitId,
|
|
138
|
+
}));
|
|
139
|
+
return this._dequeueNextHook(basePath);
|
|
140
|
+
}
|
|
141
|
+
_dequeueNextHook(basePath) {
|
|
142
|
+
while (this.hookQueue.length > 0) {
|
|
143
|
+
const entry = this.hookQueue.shift();
|
|
144
|
+
const { config, triggerUnitType, triggerUnitId } = entry;
|
|
145
|
+
// Check idempotency — if artifact already exists, skip
|
|
146
|
+
if (config.artifact) {
|
|
147
|
+
const artifactPath = resolveHookArtifactPath(basePath, triggerUnitId, config.artifact);
|
|
148
|
+
if (existsSync(artifactPath))
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
// Check cycle limit
|
|
152
|
+
const cycleKey = `${config.name}/${triggerUnitType}/${triggerUnitId}`;
|
|
153
|
+
const currentCycle = (this.cycleCounts.get(cycleKey) ?? 0) + 1;
|
|
154
|
+
const maxCycles = config.max_cycles ?? 1;
|
|
155
|
+
if (currentCycle > maxCycles)
|
|
156
|
+
continue;
|
|
157
|
+
this.cycleCounts.set(cycleKey, currentCycle);
|
|
158
|
+
this.activeHook = {
|
|
159
|
+
hookName: config.name,
|
|
160
|
+
triggerUnitType,
|
|
161
|
+
triggerUnitId,
|
|
162
|
+
cycle: currentCycle,
|
|
163
|
+
pendingRetry: false,
|
|
164
|
+
};
|
|
165
|
+
// Build prompt with variable substitution
|
|
166
|
+
const [mid, sid, tid] = triggerUnitId.split("/");
|
|
167
|
+
let prompt = config.prompt
|
|
168
|
+
.replace(/\{milestoneId\}/g, mid ?? "")
|
|
169
|
+
.replace(/\{sliceId\}/g, sid ?? "")
|
|
170
|
+
.replace(/\{taskId\}/g, tid ?? "");
|
|
171
|
+
// Inject browser safety instruction
|
|
172
|
+
prompt += "\n\n**Browser tool safety:** Do NOT use `browser_wait_for` with `condition: \"network_idle\"` — it hangs indefinitely when dev servers keep persistent connections (Vite HMR, WebSocket). Use `selector_visible`, `text_visible`, or `delay` instead.";
|
|
173
|
+
return {
|
|
174
|
+
hookName: config.name,
|
|
175
|
+
prompt,
|
|
176
|
+
model: config.model,
|
|
177
|
+
unitType: `hook/${config.name}`,
|
|
178
|
+
unitId: triggerUnitId,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// No more hooks — clear active state
|
|
182
|
+
this.activeHook = null;
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
_handleHookCompletion(basePath) {
|
|
186
|
+
const hook = this.activeHook;
|
|
187
|
+
const hooks = resolvePostUnitHooks();
|
|
188
|
+
const config = hooks.find(h => h.name === hook.hookName);
|
|
189
|
+
// Check if retry was requested via retry_on artifact
|
|
190
|
+
if (config?.retry_on) {
|
|
191
|
+
const retryArtifactPath = resolveHookArtifactPath(basePath, hook.triggerUnitId, config.retry_on);
|
|
192
|
+
if (existsSync(retryArtifactPath)) {
|
|
193
|
+
const cycleKey = `${config.name}/${hook.triggerUnitType}/${hook.triggerUnitId}`;
|
|
194
|
+
const currentCycle = this.cycleCounts.get(cycleKey) ?? 1;
|
|
195
|
+
const maxCycles = config.max_cycles ?? 1;
|
|
196
|
+
if (currentCycle < maxCycles) {
|
|
197
|
+
this.activeHook = null;
|
|
198
|
+
this.hookQueue = [];
|
|
199
|
+
this.retryPending = true;
|
|
200
|
+
this.retryTrigger = {
|
|
201
|
+
unitType: hook.triggerUnitType,
|
|
202
|
+
unitId: hook.triggerUnitId,
|
|
203
|
+
retryArtifact: config.retry_on,
|
|
204
|
+
};
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Hook completed normally — try next hook in queue
|
|
210
|
+
this.activeHook = null;
|
|
211
|
+
return this._dequeueNextHook(basePath);
|
|
212
|
+
}
|
|
213
|
+
// ── Pre-dispatch hook evaluation (sync, all-matching with compose) ──
|
|
214
|
+
/**
|
|
215
|
+
* Replicate exact semantics of runPreDispatchHooks from post-unit-hooks.ts:
|
|
216
|
+
* modify/skip/replace compose semantics.
|
|
217
|
+
*/
|
|
218
|
+
evaluatePreDispatch(unitType, unitId, prompt, basePath) {
|
|
219
|
+
// Don't intercept hook units
|
|
220
|
+
if (unitType.startsWith("hook/")) {
|
|
221
|
+
return { action: "proceed", prompt, firedHooks: [] };
|
|
222
|
+
}
|
|
223
|
+
const hooks = resolvePreDispatchHooks().filter(h => h.before.includes(unitType));
|
|
224
|
+
if (hooks.length === 0) {
|
|
225
|
+
return { action: "proceed", prompt, firedHooks: [] };
|
|
226
|
+
}
|
|
227
|
+
const [mid, sid, tid] = unitId.split("/");
|
|
228
|
+
const substitute = (text) => text
|
|
229
|
+
.replace(/\{milestoneId\}/g, mid ?? "")
|
|
230
|
+
.replace(/\{sliceId\}/g, sid ?? "")
|
|
231
|
+
.replace(/\{taskId\}/g, tid ?? "");
|
|
232
|
+
const firedHooks = [];
|
|
233
|
+
let currentPrompt = prompt;
|
|
234
|
+
for (const hook of hooks) {
|
|
235
|
+
if (hook.action === "skip") {
|
|
236
|
+
if (hook.skip_if) {
|
|
237
|
+
const conditionPath = resolveHookArtifactPath(basePath, unitId, hook.skip_if);
|
|
238
|
+
if (!existsSync(conditionPath))
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
firedHooks.push(hook.name);
|
|
242
|
+
return { action: "skip", firedHooks };
|
|
243
|
+
}
|
|
244
|
+
if (hook.action === "replace") {
|
|
245
|
+
firedHooks.push(hook.name);
|
|
246
|
+
return {
|
|
247
|
+
action: "replace",
|
|
248
|
+
prompt: substitute(hook.prompt ?? ""),
|
|
249
|
+
unitType: hook.unit_type,
|
|
250
|
+
model: hook.model,
|
|
251
|
+
firedHooks,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (hook.action === "modify") {
|
|
255
|
+
firedHooks.push(hook.name);
|
|
256
|
+
if (hook.prepend) {
|
|
257
|
+
currentPrompt = `${substitute(hook.prepend)}\n\n${currentPrompt}`;
|
|
258
|
+
}
|
|
259
|
+
if (hook.append) {
|
|
260
|
+
currentPrompt = `${currentPrompt}\n\n${substitute(hook.append)}`;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
action: "proceed",
|
|
266
|
+
prompt: currentPrompt,
|
|
267
|
+
model: hooks.find(h => h.action === "modify" && h.model)?.model,
|
|
268
|
+
firedHooks,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
// ── State accessors ─────────────────────────────────────────────────
|
|
272
|
+
getActiveHook() {
|
|
273
|
+
return this.activeHook;
|
|
274
|
+
}
|
|
275
|
+
isRetryPending() {
|
|
276
|
+
return this.retryPending;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Returns the trigger unit info for a pending retry, or null.
|
|
280
|
+
* Clears the retry state after reading.
|
|
281
|
+
*/
|
|
282
|
+
consumeRetryTrigger() {
|
|
283
|
+
if (!this.retryPending || !this.retryTrigger)
|
|
284
|
+
return null;
|
|
285
|
+
const trigger = { ...this.retryTrigger };
|
|
286
|
+
this.retryPending = false;
|
|
287
|
+
this.retryTrigger = null;
|
|
288
|
+
return trigger;
|
|
289
|
+
}
|
|
290
|
+
/** Clear all mutable state (activeHook, hookQueue, cycleCounts, retryPending, retryTrigger). */
|
|
291
|
+
resetState() {
|
|
292
|
+
this.activeHook = null;
|
|
293
|
+
this.hookQueue = [];
|
|
294
|
+
this.cycleCounts.clear();
|
|
295
|
+
this.retryPending = false;
|
|
296
|
+
this.retryTrigger = null;
|
|
297
|
+
}
|
|
298
|
+
// ── Persistence ─────────────────────────────────────────────────────
|
|
299
|
+
_hookStatePath(basePath) {
|
|
300
|
+
return join(basePath, ".gsd", HOOK_STATE_FILE);
|
|
301
|
+
}
|
|
302
|
+
/** Persist current hook cycle counts to disk. */
|
|
303
|
+
persistState(basePath) {
|
|
304
|
+
const state = {
|
|
305
|
+
cycleCounts: Object.fromEntries(this.cycleCounts),
|
|
306
|
+
savedAt: new Date().toISOString(),
|
|
307
|
+
};
|
|
308
|
+
try {
|
|
309
|
+
const dir = join(basePath, ".gsd");
|
|
310
|
+
if (!existsSync(dir))
|
|
311
|
+
mkdirSync(dir, { recursive: true });
|
|
312
|
+
writeFileSync(this._hookStatePath(basePath), JSON.stringify(state, null, 2), "utf-8");
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// Non-fatal — state is recreatable from artifacts
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/** Restore hook cycle counts from disk after a crash/restart. */
|
|
319
|
+
restoreState(basePath) {
|
|
320
|
+
try {
|
|
321
|
+
const filePath = this._hookStatePath(basePath);
|
|
322
|
+
if (!existsSync(filePath))
|
|
323
|
+
return;
|
|
324
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
325
|
+
const state = JSON.parse(raw);
|
|
326
|
+
if (state.cycleCounts && typeof state.cycleCounts === "object") {
|
|
327
|
+
this.cycleCounts.clear();
|
|
328
|
+
for (const [key, value] of Object.entries(state.cycleCounts)) {
|
|
329
|
+
if (typeof value === "number") {
|
|
330
|
+
this.cycleCounts.set(key, value);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
// Non-fatal — fresh state is fine
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/** Clear persisted hook state file from disk. */
|
|
340
|
+
clearPersistedState(basePath) {
|
|
341
|
+
try {
|
|
342
|
+
const filePath = this._hookStatePath(basePath);
|
|
343
|
+
if (existsSync(filePath)) {
|
|
344
|
+
writeFileSync(filePath, JSON.stringify({ cycleCounts: {}, savedAt: new Date().toISOString() }, null, 2), "utf-8");
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
// Non-fatal
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// ── Hook status reporting ───────────────────────────────────────────
|
|
352
|
+
/** Get status of all configured hooks for display. */
|
|
353
|
+
getHookStatus() {
|
|
354
|
+
const entries = [];
|
|
355
|
+
const postHooks = resolvePostUnitHooks();
|
|
356
|
+
for (const hook of postHooks) {
|
|
357
|
+
const activeCycles = {};
|
|
358
|
+
for (const [key, count] of this.cycleCounts) {
|
|
359
|
+
if (key.startsWith(`${hook.name}/`)) {
|
|
360
|
+
activeCycles[key] = count;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
entries.push({
|
|
364
|
+
name: hook.name,
|
|
365
|
+
type: "post",
|
|
366
|
+
enabled: hook.enabled !== false,
|
|
367
|
+
targets: hook.after,
|
|
368
|
+
activeCycles,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
const preHooks = resolvePreDispatchHooks();
|
|
372
|
+
for (const hook of preHooks) {
|
|
373
|
+
entries.push({
|
|
374
|
+
name: hook.name,
|
|
375
|
+
type: "pre",
|
|
376
|
+
enabled: hook.enabled !== false,
|
|
377
|
+
targets: hook.before,
|
|
378
|
+
activeCycles: {},
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
return entries;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Manually trigger a specific hook for a unit.
|
|
385
|
+
* Bypasses normal flow — forces hook to run even if artifact exists.
|
|
386
|
+
*/
|
|
387
|
+
triggerHookManually(hookName, unitType, unitId, basePath) {
|
|
388
|
+
const hook = resolvePostUnitHooks().find(h => h.name === hookName);
|
|
389
|
+
if (!hook) {
|
|
390
|
+
console.error(`[triggerHookManually] Hook "${hookName}" not found in post_unit_hooks`);
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
if (!hook.prompt || typeof hook.prompt !== "string" || hook.prompt.trim().length === 0) {
|
|
394
|
+
console.error(`[triggerHookManually] Hook "${hookName}" has empty prompt`);
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
this.activeHook = {
|
|
398
|
+
hookName: hook.name,
|
|
399
|
+
triggerUnitType: unitType,
|
|
400
|
+
triggerUnitId: unitId,
|
|
401
|
+
cycle: 1,
|
|
402
|
+
pendingRetry: false,
|
|
403
|
+
};
|
|
404
|
+
this.hookQueue = [{
|
|
405
|
+
config: hook,
|
|
406
|
+
triggerUnitType: unitType,
|
|
407
|
+
triggerUnitId: unitId,
|
|
408
|
+
}];
|
|
409
|
+
const cycleKey = `${hook.name}/${unitType}/${unitId}`;
|
|
410
|
+
const currentCycle = (this.cycleCounts.get(cycleKey) ?? 0) + 1;
|
|
411
|
+
this.cycleCounts.set(cycleKey, currentCycle);
|
|
412
|
+
this.activeHook.cycle = currentCycle;
|
|
413
|
+
const [mid, sid, tid] = unitId.split("/");
|
|
414
|
+
const prompt = hook.prompt
|
|
415
|
+
.replace(/\{milestoneId\}/g, mid ?? "")
|
|
416
|
+
.replace(/\{sliceId\}/g, sid ?? "")
|
|
417
|
+
.replace(/\{taskId\}/g, tid ?? "");
|
|
418
|
+
return {
|
|
419
|
+
hookName: hook.name,
|
|
420
|
+
prompt,
|
|
421
|
+
model: hook.model,
|
|
422
|
+
unitType: `hook/${hook.name}`,
|
|
423
|
+
unitId,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
/** Format hook status for terminal display. */
|
|
427
|
+
formatHookStatus() {
|
|
428
|
+
const entries = this.getHookStatus();
|
|
429
|
+
if (entries.length === 0) {
|
|
430
|
+
return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/preferences.md";
|
|
431
|
+
}
|
|
432
|
+
const lines = ["Configured Hooks:", ""];
|
|
433
|
+
const postHooks = entries.filter(e => e.type === "post");
|
|
434
|
+
const preHooks = entries.filter(e => e.type === "pre");
|
|
435
|
+
if (postHooks.length > 0) {
|
|
436
|
+
lines.push("Post-Unit Hooks (run after unit completes):");
|
|
437
|
+
for (const hook of postHooks) {
|
|
438
|
+
const status = hook.enabled ? "enabled" : "disabled";
|
|
439
|
+
const cycles = Object.keys(hook.activeCycles).length;
|
|
440
|
+
const cycleInfo = cycles > 0 ? ` (${cycles} active cycle${cycles === 1 ? "" : "s"})` : "";
|
|
441
|
+
lines.push(` ${hook.name} [${status}] → after: ${hook.targets.join(", ")}${cycleInfo}`);
|
|
442
|
+
}
|
|
443
|
+
lines.push("");
|
|
444
|
+
}
|
|
445
|
+
if (preHooks.length > 0) {
|
|
446
|
+
lines.push("Pre-Dispatch Hooks (run before unit dispatches):");
|
|
447
|
+
for (const hook of preHooks) {
|
|
448
|
+
const status = hook.enabled ? "enabled" : "disabled";
|
|
449
|
+
lines.push(` ${hook.name} [${status}] → before: ${hook.targets.join(", ")}`);
|
|
450
|
+
}
|
|
451
|
+
lines.push("");
|
|
452
|
+
}
|
|
453
|
+
return lines.join("\n");
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// ─── Module-level Singleton ─────────────────────────────────────────────────
|
|
457
|
+
let _registry = null;
|
|
458
|
+
/** Get the singleton registry. Throws if not initialized. */
|
|
459
|
+
export function getRegistry() {
|
|
460
|
+
if (!_registry) {
|
|
461
|
+
throw new Error("RuleRegistry not initialized — call initRegistry() or setRegistry() first.");
|
|
462
|
+
}
|
|
463
|
+
return _registry;
|
|
464
|
+
}
|
|
465
|
+
/** Set the singleton registry instance. */
|
|
466
|
+
export function setRegistry(r) {
|
|
467
|
+
_registry = r;
|
|
468
|
+
}
|
|
469
|
+
/** Create and set the singleton registry with the given dispatch rules. */
|
|
470
|
+
export function initRegistry(dispatchRules) {
|
|
471
|
+
const registry = new RuleRegistry(dispatchRules);
|
|
472
|
+
setRegistry(registry);
|
|
473
|
+
return registry;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Get the singleton registry, lazily creating one with empty dispatch rules
|
|
477
|
+
* if not yet initialized. This ensures facade functions work even when
|
|
478
|
+
* the full registry hasn't been set up (e.g. during testing).
|
|
479
|
+
*/
|
|
480
|
+
export function getOrCreateRegistry() {
|
|
481
|
+
if (!_registry) {
|
|
482
|
+
_registry = new RuleRegistry([]);
|
|
483
|
+
}
|
|
484
|
+
return _registry;
|
|
485
|
+
}
|
|
486
|
+
/** Reset the singleton (for testing). */
|
|
487
|
+
export function resetRegistry() {
|
|
488
|
+
_registry = null;
|
|
489
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// GSD Extension — Unified Rule Type Definitions
|
|
2
|
+
//
|
|
3
|
+
// Every dispatch rule and hook is expressed as a `UnifiedRule` with a
|
|
4
|
+
// consistent when/where/then shape. This file defines the type system;
|
|
5
|
+
// the `RuleRegistry` class in rule-registry.ts holds instances at runtime.
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* run-manager.ts — Create and list isolated workflow run directories.
|
|
3
|
+
*
|
|
4
|
+
* Each run lives under `.gsd/workflow-runs/<name>/<timestamp>/` and contains:
|
|
5
|
+
* - DEFINITION.yaml — frozen snapshot of the workflow definition at run-creation time
|
|
6
|
+
* - GRAPH.yaml — initialized step graph with all steps pending
|
|
7
|
+
* - PARAMS.json — (optional) parameter overrides used for this run
|
|
8
|
+
*
|
|
9
|
+
* Observability:
|
|
10
|
+
* - All run state is on disk in human-readable YAML/JSON — inspectable with cat/less.
|
|
11
|
+
* - `listRuns()` returns structured metadata including step counts and overall status.
|
|
12
|
+
* - Timestamp directory names are filesystem-safe (ISO with hyphens replacing colons).
|
|
13
|
+
* - Errors include the full path context for diagnosis.
|
|
14
|
+
*/
|
|
15
|
+
import { mkdirSync, writeFileSync, existsSync, readdirSync, statSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { stringify } from "yaml";
|
|
18
|
+
import { loadDefinition, substituteParams } from "./definition-loader.js";
|
|
19
|
+
import { initializeGraph, writeGraph, readGraph } from "./graph.js";
|
|
20
|
+
// ─── Constants ───────────────────────────────────────────────────────────
|
|
21
|
+
const RUNS_DIR = "workflow-runs";
|
|
22
|
+
const DEFS_DIR = "workflow-defs";
|
|
23
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
24
|
+
/**
|
|
25
|
+
* Generate a filesystem-safe timestamp: `YYYY-MM-DDTHH-MM-SS`.
|
|
26
|
+
* Replaces colons with hyphens so the string is safe as a directory name
|
|
27
|
+
* on all platforms (Windows forbids colons in paths).
|
|
28
|
+
*/
|
|
29
|
+
function makeTimestamp(date = new Date()) {
|
|
30
|
+
return date.toISOString().replace(/:/g, "-").replace(/\.\d{3}Z$/, "");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Derive overall status from a graph's step statuses.
|
|
34
|
+
*/
|
|
35
|
+
function deriveStatus(graph) {
|
|
36
|
+
const hasActive = graph.steps.some((s) => s.status === "active");
|
|
37
|
+
const allDone = graph.steps.every((s) => s.status === "complete" || s.status === "expanded");
|
|
38
|
+
if (allDone)
|
|
39
|
+
return "complete";
|
|
40
|
+
if (hasActive)
|
|
41
|
+
return "running";
|
|
42
|
+
return "pending";
|
|
43
|
+
}
|
|
44
|
+
// ─── Public API ──────────────────────────────────────────────────────────
|
|
45
|
+
/**
|
|
46
|
+
* Create a new isolated run directory for a workflow definition.
|
|
47
|
+
*
|
|
48
|
+
* 1. Loads the definition from `<basePath>/.gsd/workflow-defs/<defName>.yaml`
|
|
49
|
+
* 2. Applies parameter substitution if overrides are provided
|
|
50
|
+
* 3. Creates `<basePath>/.gsd/workflow-runs/<defName>/<timestamp>/`
|
|
51
|
+
* 4. Writes frozen DEFINITION.yaml, initialized GRAPH.yaml, and optional PARAMS.json
|
|
52
|
+
*
|
|
53
|
+
* @param basePath — project root directory
|
|
54
|
+
* @param defName — definition filename (without .yaml extension)
|
|
55
|
+
* @param overrides — optional parameter overrides (merged with definition defaults)
|
|
56
|
+
* @returns Full path to the created run directory
|
|
57
|
+
* @throws Error if the definition file doesn't exist or is invalid
|
|
58
|
+
*/
|
|
59
|
+
export function createRun(basePath, defName, overrides) {
|
|
60
|
+
const defsDir = join(basePath, ".gsd", DEFS_DIR);
|
|
61
|
+
// Load and validate the definition
|
|
62
|
+
const rawDef = loadDefinition(defsDir, defName);
|
|
63
|
+
// Apply parameter substitution if overrides provided
|
|
64
|
+
const def = overrides
|
|
65
|
+
? substituteParams(rawDef, overrides)
|
|
66
|
+
: substituteParams(rawDef); // still resolve default params if any
|
|
67
|
+
// Create the run directory
|
|
68
|
+
const timestamp = makeTimestamp();
|
|
69
|
+
const runDir = join(basePath, ".gsd", RUNS_DIR, defName, timestamp);
|
|
70
|
+
mkdirSync(runDir, { recursive: true });
|
|
71
|
+
// Freeze the definition as DEFINITION.yaml
|
|
72
|
+
writeFileSync(join(runDir, "DEFINITION.yaml"), stringify(def), "utf-8");
|
|
73
|
+
// Initialize and write GRAPH.yaml
|
|
74
|
+
const graph = initializeGraph(def);
|
|
75
|
+
writeGraph(runDir, graph);
|
|
76
|
+
// Write PARAMS.json if overrides were provided
|
|
77
|
+
if (overrides && Object.keys(overrides).length > 0) {
|
|
78
|
+
writeFileSync(join(runDir, "PARAMS.json"), JSON.stringify(overrides, null, 2), "utf-8");
|
|
79
|
+
}
|
|
80
|
+
return runDir;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* List existing workflow runs with metadata.
|
|
84
|
+
*
|
|
85
|
+
* Scans `<basePath>/.gsd/workflow-runs/` for run directories. Each run's
|
|
86
|
+
* GRAPH.yaml is read to derive step counts and overall status.
|
|
87
|
+
*
|
|
88
|
+
* @param basePath — project root directory
|
|
89
|
+
* @param defName — optional filter: only list runs for this definition name
|
|
90
|
+
* @returns Array of run metadata, sorted newest-first within each definition
|
|
91
|
+
*/
|
|
92
|
+
export function listRuns(basePath, defName) {
|
|
93
|
+
const runsRoot = join(basePath, ".gsd", RUNS_DIR);
|
|
94
|
+
if (!existsSync(runsRoot))
|
|
95
|
+
return [];
|
|
96
|
+
const results = [];
|
|
97
|
+
// Get workflow name directories
|
|
98
|
+
const nameDirs = defName ? [defName] : readdirSync(runsRoot).filter((entry) => {
|
|
99
|
+
const full = join(runsRoot, entry);
|
|
100
|
+
return statSync(full).isDirectory();
|
|
101
|
+
});
|
|
102
|
+
for (const name of nameDirs) {
|
|
103
|
+
const nameDir = join(runsRoot, name);
|
|
104
|
+
if (!existsSync(nameDir))
|
|
105
|
+
continue;
|
|
106
|
+
const timestamps = readdirSync(nameDir).filter((entry) => {
|
|
107
|
+
const full = join(nameDir, entry);
|
|
108
|
+
return statSync(full).isDirectory();
|
|
109
|
+
});
|
|
110
|
+
// Sort newest-first (ISO strings sort lexicographically)
|
|
111
|
+
timestamps.sort().reverse();
|
|
112
|
+
for (const ts of timestamps) {
|
|
113
|
+
const runDir = join(nameDir, ts);
|
|
114
|
+
try {
|
|
115
|
+
const graph = readGraph(runDir);
|
|
116
|
+
const total = graph.steps.length;
|
|
117
|
+
const completed = graph.steps.filter((s) => s.status === "complete").length;
|
|
118
|
+
const pending = graph.steps.filter((s) => s.status === "pending").length;
|
|
119
|
+
const active = graph.steps.filter((s) => s.status === "active").length;
|
|
120
|
+
results.push({
|
|
121
|
+
name,
|
|
122
|
+
timestamp: ts,
|
|
123
|
+
runDir,
|
|
124
|
+
steps: { total, completed, pending, active },
|
|
125
|
+
status: deriveStatus(graph),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Skip runs with invalid/missing GRAPH.yaml
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return results;
|
|
134
|
+
}
|