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
|
@@ -1,524 +1,86 @@
|
|
|
1
|
-
// GSD Extension — Hook Engine
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// GSD Extension — Hook Engine Facade
|
|
2
|
+
//
|
|
3
|
+
// Thin facade over RuleRegistry. All mutable state and logic lives in the
|
|
4
|
+
// registry instance; these exported functions delegate through getOrCreateRegistry()
|
|
5
|
+
// so existing call-sites and tests work without modification.
|
|
4
6
|
|
|
5
7
|
import type {
|
|
6
|
-
PostUnitHookConfig,
|
|
7
|
-
PreDispatchHookConfig,
|
|
8
8
|
HookExecutionState,
|
|
9
9
|
HookDispatchResult,
|
|
10
10
|
PreDispatchResult,
|
|
11
|
-
PersistedHookState,
|
|
12
11
|
HookStatusEntry,
|
|
13
12
|
} from "./types.js";
|
|
14
|
-
import {
|
|
15
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
16
|
-
import { join } from "node:path";
|
|
13
|
+
import { getOrCreateRegistry, resolveHookArtifactPath } from "./rule-registry.js";
|
|
17
14
|
|
|
18
|
-
//
|
|
15
|
+
// Re-export resolveHookArtifactPath so existing importers still work.
|
|
16
|
+
export { resolveHookArtifactPath } from "./rule-registry.js";
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
let activeHook: HookExecutionState | null = null;
|
|
18
|
+
// ─── Post-Unit Hooks ───────────────────────────────────────────────────────
|
|
22
19
|
|
|
23
|
-
/** Queue of hooks remaining for the current trigger unit. */
|
|
24
|
-
let hookQueue: Array<{
|
|
25
|
-
config: PostUnitHookConfig;
|
|
26
|
-
triggerUnitType: string;
|
|
27
|
-
triggerUnitId: string;
|
|
28
|
-
}> = [];
|
|
29
|
-
|
|
30
|
-
/** Cycle counts per hook+trigger, keyed as "hookName/triggerUnitType/triggerUnitId". */
|
|
31
|
-
const cycleCounts = new Map<string, number>();
|
|
32
|
-
|
|
33
|
-
/** Set when a hook completes with retry_on artifact present — signals caller to re-run trigger. */
|
|
34
|
-
let retryPending = false;
|
|
35
|
-
|
|
36
|
-
/** Stores the trigger unit info for pending retries so caller knows what to re-run. */
|
|
37
|
-
let retryTrigger: { unitType: string; unitId: string; retryArtifact: string } | null = null;
|
|
38
|
-
|
|
39
|
-
// ─── Public API ────────────────────────────────────────────────────────────
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Called after a unit completes. Returns the next hook unit to dispatch,
|
|
43
|
-
* or null if no hooks apply (normal dispatch should proceed).
|
|
44
|
-
*
|
|
45
|
-
* Call flow:
|
|
46
|
-
* 1. A core unit (e.g. execute-task) completes → handleAgentEnd calls this
|
|
47
|
-
* 2. If hooks match, returns first hook to dispatch. Caller sends the prompt.
|
|
48
|
-
* 3. Hook unit completes → handleAgentEnd calls this again (activeHook is set)
|
|
49
|
-
* 4. Checks retry_on / next hook / done → returns next action or null
|
|
50
|
-
*/
|
|
51
20
|
export function checkPostUnitHooks(
|
|
52
21
|
completedUnitType: string,
|
|
53
22
|
completedUnitId: string,
|
|
54
23
|
basePath: string,
|
|
55
24
|
): HookDispatchResult | null {
|
|
56
|
-
|
|
57
|
-
if (activeHook) {
|
|
58
|
-
return handleHookCompletion(basePath);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Don't trigger hooks for other hook units (prevent hook-on-hook chains)
|
|
62
|
-
// Don't trigger hooks for triage units (prevent hook-on-triage chains)
|
|
63
|
-
// Don't trigger hooks for quick-task units (lightweight one-offs from captures)
|
|
64
|
-
if (completedUnitType.startsWith("hook/") || completedUnitType === "triage-captures" || completedUnitType === "quick-task") return null;
|
|
65
|
-
|
|
66
|
-
// Check if any hooks are configured for this unit type
|
|
67
|
-
const hooks = resolvePostUnitHooks().filter(h =>
|
|
68
|
-
h.after.includes(completedUnitType),
|
|
69
|
-
);
|
|
70
|
-
if (hooks.length === 0) return null;
|
|
71
|
-
|
|
72
|
-
// Build hook queue for this trigger
|
|
73
|
-
hookQueue = hooks.map(config => ({
|
|
74
|
-
config,
|
|
75
|
-
triggerUnitType: completedUnitType,
|
|
76
|
-
triggerUnitId: completedUnitId,
|
|
77
|
-
}));
|
|
78
|
-
|
|
79
|
-
return dequeueNextHook(basePath);
|
|
25
|
+
return getOrCreateRegistry().evaluatePostUnit(completedUnitType, completedUnitId, basePath);
|
|
80
26
|
}
|
|
81
27
|
|
|
82
|
-
/**
|
|
83
|
-
* Returns whether a hook is currently active (for progress display).
|
|
84
|
-
*/
|
|
85
28
|
export function getActiveHook(): HookExecutionState | null {
|
|
86
|
-
return
|
|
29
|
+
return getOrCreateRegistry().getActiveHook();
|
|
87
30
|
}
|
|
88
31
|
|
|
89
|
-
/**
|
|
90
|
-
* Returns true if a retry of the trigger unit was requested by a hook.
|
|
91
|
-
* Caller should re-dispatch the original trigger unit, then hooks will
|
|
92
|
-
* fire again on its next completion.
|
|
93
|
-
*/
|
|
94
32
|
export function isRetryPending(): boolean {
|
|
95
|
-
return
|
|
33
|
+
return getOrCreateRegistry().isRetryPending();
|
|
96
34
|
}
|
|
97
35
|
|
|
98
|
-
/**
|
|
99
|
-
* Returns the trigger unit info for a pending retry, or null.
|
|
100
|
-
* Clears the retry state after reading.
|
|
101
|
-
*/
|
|
102
36
|
export function consumeRetryTrigger(): { unitType: string; unitId: string; retryArtifact: string } | null {
|
|
103
|
-
|
|
104
|
-
const trigger = { ...retryTrigger };
|
|
105
|
-
retryPending = false;
|
|
106
|
-
retryTrigger = null;
|
|
107
|
-
return trigger;
|
|
37
|
+
return getOrCreateRegistry().consumeRetryTrigger();
|
|
108
38
|
}
|
|
109
39
|
|
|
110
|
-
/**
|
|
111
|
-
* Reset all hook state. Called on auto-mode start/stop.
|
|
112
|
-
*/
|
|
113
40
|
export function resetHookState(): void {
|
|
114
|
-
|
|
115
|
-
hookQueue = [];
|
|
116
|
-
cycleCounts.clear();
|
|
117
|
-
retryPending = false;
|
|
118
|
-
retryTrigger = null;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// ─── Internal ──────────────────────────────────────────────────────────────
|
|
122
|
-
|
|
123
|
-
function dequeueNextHook(basePath: string): HookDispatchResult | null {
|
|
124
|
-
while (hookQueue.length > 0) {
|
|
125
|
-
const entry = hookQueue.shift()!;
|
|
126
|
-
const { config, triggerUnitType, triggerUnitId } = entry;
|
|
127
|
-
|
|
128
|
-
// Check idempotency — if artifact already exists, skip this hook
|
|
129
|
-
if (config.artifact) {
|
|
130
|
-
const artifactPath = resolveHookArtifactPath(basePath, triggerUnitId, config.artifact);
|
|
131
|
-
if (existsSync(artifactPath)) continue;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Check cycle limit
|
|
135
|
-
const cycleKey = `${config.name}/${triggerUnitType}/${triggerUnitId}`;
|
|
136
|
-
const currentCycle = (cycleCounts.get(cycleKey) ?? 0) + 1;
|
|
137
|
-
const maxCycles = config.max_cycles ?? 1;
|
|
138
|
-
if (currentCycle > maxCycles) continue;
|
|
139
|
-
|
|
140
|
-
cycleCounts.set(cycleKey, currentCycle);
|
|
141
|
-
|
|
142
|
-
activeHook = {
|
|
143
|
-
hookName: config.name,
|
|
144
|
-
triggerUnitType,
|
|
145
|
-
triggerUnitId,
|
|
146
|
-
cycle: currentCycle,
|
|
147
|
-
pendingRetry: false,
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
// Build the prompt with variable substitution
|
|
151
|
-
const [mid, sid, tid] = triggerUnitId.split("/");
|
|
152
|
-
let prompt = config.prompt
|
|
153
|
-
.replace(/\{milestoneId\}/g, mid ?? "")
|
|
154
|
-
.replace(/\{sliceId\}/g, sid ?? "")
|
|
155
|
-
.replace(/\{taskId\}/g, tid ?? "");
|
|
156
|
-
|
|
157
|
-
// Inject browser safety instruction for hooks that may use browser tools (#1345).
|
|
158
|
-
// Vite HMR and other persistent connections prevent networkidle from resolving.
|
|
159
|
-
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.";
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
hookName: config.name,
|
|
163
|
-
prompt,
|
|
164
|
-
model: config.model,
|
|
165
|
-
unitType: `hook/${config.name}`,
|
|
166
|
-
unitId: triggerUnitId,
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// No more hooks — clear active state and return null for normal dispatch
|
|
171
|
-
activeHook = null;
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function handleHookCompletion(basePath: string): HookDispatchResult | null {
|
|
176
|
-
const hook = activeHook!;
|
|
177
|
-
const hooks = resolvePostUnitHooks();
|
|
178
|
-
const config = hooks.find(h => h.name === hook.hookName);
|
|
179
|
-
|
|
180
|
-
// Check if retry was requested via retry_on artifact
|
|
181
|
-
if (config?.retry_on) {
|
|
182
|
-
const retryArtifactPath = resolveHookArtifactPath(basePath, hook.triggerUnitId, config.retry_on);
|
|
183
|
-
if (existsSync(retryArtifactPath)) {
|
|
184
|
-
// Check cycle limit before allowing retry
|
|
185
|
-
const cycleKey = `${config.name}/${hook.triggerUnitType}/${hook.triggerUnitId}`;
|
|
186
|
-
const currentCycle = cycleCounts.get(cycleKey) ?? 1;
|
|
187
|
-
const maxCycles = config.max_cycles ?? 1;
|
|
188
|
-
|
|
189
|
-
if (currentCycle < maxCycles) {
|
|
190
|
-
// Signal retry — caller will re-dispatch the trigger unit
|
|
191
|
-
activeHook = null;
|
|
192
|
-
hookQueue = [];
|
|
193
|
-
retryPending = true;
|
|
194
|
-
retryTrigger = { unitType: hook.triggerUnitType, unitId: hook.triggerUnitId, retryArtifact: config.retry_on };
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
// Max cycles reached — fall through to normal completion
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Hook completed normally — try next hook in queue
|
|
202
|
-
activeHook = null;
|
|
203
|
-
return dequeueNextHook(basePath);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Resolve the path where a hook artifact is expected to be written.
|
|
208
|
-
* Uses the trigger unit's directory context:
|
|
209
|
-
* - Task-level (M001/S01/T01): .gsd/milestones/M001/slices/S01/tasks/T01-{artifact}
|
|
210
|
-
* - Slice-level (M001/S01): .gsd/milestones/M001/slices/S01/{artifact}
|
|
211
|
-
* - Milestone-level (M001): .gsd/milestones/M001/{artifact}
|
|
212
|
-
*/
|
|
213
|
-
export function resolveHookArtifactPath(basePath: string, unitId: string, artifactName: string): string {
|
|
214
|
-
const parts = unitId.split("/");
|
|
215
|
-
if (parts.length === 3) {
|
|
216
|
-
const [mid, sid, tid] = parts;
|
|
217
|
-
return join(basePath, ".gsd", "milestones", mid, "slices", sid, "tasks", `${tid}-${artifactName}`);
|
|
218
|
-
}
|
|
219
|
-
if (parts.length === 2) {
|
|
220
|
-
const [mid, sid] = parts;
|
|
221
|
-
return join(basePath, ".gsd", "milestones", mid, "slices", sid, artifactName);
|
|
222
|
-
}
|
|
223
|
-
return join(basePath, ".gsd", "milestones", parts[0], artifactName);
|
|
41
|
+
getOrCreateRegistry().resetState();
|
|
224
42
|
}
|
|
225
43
|
|
|
226
|
-
//
|
|
227
|
-
// Phase 2: Pre-Dispatch Hooks
|
|
228
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
44
|
+
// ─── Pre-Dispatch Hooks ────────────────────────────────────────────────────
|
|
229
45
|
|
|
230
|
-
/**
|
|
231
|
-
* Run pre-dispatch hooks for a unit about to be dispatched.
|
|
232
|
-
* Returns a result indicating whether the unit should proceed (with optional
|
|
233
|
-
* prompt modifications), be skipped, or be replaced entirely.
|
|
234
|
-
*
|
|
235
|
-
* Multiple hooks can fire for the same unit type. They compose:
|
|
236
|
-
* - "modify" hooks stack (all prepend/append applied in order)
|
|
237
|
-
* - "skip" short-circuits (first matching skip wins)
|
|
238
|
-
* - "replace" short-circuits (first matching replace wins)
|
|
239
|
-
* - Skip/replace hooks take precedence over modify hooks
|
|
240
|
-
*/
|
|
241
46
|
export function runPreDispatchHooks(
|
|
242
47
|
unitType: string,
|
|
243
48
|
unitId: string,
|
|
244
49
|
prompt: string,
|
|
245
50
|
basePath: string,
|
|
246
51
|
): PreDispatchResult {
|
|
247
|
-
|
|
248
|
-
if (unitType.startsWith("hook/")) {
|
|
249
|
-
return { action: "proceed", prompt, firedHooks: [] };
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const hooks = resolvePreDispatchHooks().filter(h =>
|
|
253
|
-
h.before.includes(unitType),
|
|
254
|
-
);
|
|
255
|
-
if (hooks.length === 0) {
|
|
256
|
-
return { action: "proceed", prompt, firedHooks: [] };
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const [mid, sid, tid] = unitId.split("/");
|
|
260
|
-
const substitute = (text: string): string =>
|
|
261
|
-
text
|
|
262
|
-
.replace(/\{milestoneId\}/g, mid ?? "")
|
|
263
|
-
.replace(/\{sliceId\}/g, sid ?? "")
|
|
264
|
-
.replace(/\{taskId\}/g, tid ?? "");
|
|
265
|
-
|
|
266
|
-
const firedHooks: string[] = [];
|
|
267
|
-
let currentPrompt = prompt;
|
|
268
|
-
|
|
269
|
-
for (const hook of hooks) {
|
|
270
|
-
if (hook.action === "skip") {
|
|
271
|
-
// Check optional skip condition
|
|
272
|
-
if (hook.skip_if) {
|
|
273
|
-
const conditionPath = resolveHookArtifactPath(basePath, unitId, hook.skip_if);
|
|
274
|
-
if (!existsSync(conditionPath)) continue; // Condition not met, don't skip
|
|
275
|
-
}
|
|
276
|
-
firedHooks.push(hook.name);
|
|
277
|
-
return { action: "skip", firedHooks };
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (hook.action === "replace") {
|
|
281
|
-
firedHooks.push(hook.name);
|
|
282
|
-
return {
|
|
283
|
-
action: "replace",
|
|
284
|
-
prompt: substitute(hook.prompt ?? ""),
|
|
285
|
-
unitType: hook.unit_type,
|
|
286
|
-
model: hook.model,
|
|
287
|
-
firedHooks,
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (hook.action === "modify") {
|
|
292
|
-
firedHooks.push(hook.name);
|
|
293
|
-
if (hook.prepend) {
|
|
294
|
-
currentPrompt = `${substitute(hook.prepend)}\n\n${currentPrompt}`;
|
|
295
|
-
}
|
|
296
|
-
if (hook.append) {
|
|
297
|
-
currentPrompt = `${currentPrompt}\n\n${substitute(hook.append)}`;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return {
|
|
303
|
-
action: "proceed",
|
|
304
|
-
prompt: currentPrompt,
|
|
305
|
-
model: hooks.find(h => h.action === "modify" && h.model)?.model,
|
|
306
|
-
firedHooks,
|
|
307
|
-
};
|
|
52
|
+
return getOrCreateRegistry().evaluatePreDispatch(unitType, unitId, prompt, basePath);
|
|
308
53
|
}
|
|
309
54
|
|
|
310
|
-
//
|
|
311
|
-
// Phase 3: Hook State Persistence
|
|
312
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
55
|
+
// ─── State Persistence ─────────────────────────────────────────────────────
|
|
313
56
|
|
|
314
|
-
const HOOK_STATE_FILE = "hook-state.json";
|
|
315
|
-
|
|
316
|
-
function hookStatePath(basePath: string): string {
|
|
317
|
-
return join(basePath, ".gsd", HOOK_STATE_FILE);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Persist current hook cycle counts to disk so they survive crashes/restarts.
|
|
322
|
-
* Called after each hook dispatch and on auto-mode pause.
|
|
323
|
-
*/
|
|
324
57
|
export function persistHookState(basePath: string): void {
|
|
325
|
-
|
|
326
|
-
cycleCounts: Object.fromEntries(cycleCounts),
|
|
327
|
-
savedAt: new Date().toISOString(),
|
|
328
|
-
};
|
|
329
|
-
try {
|
|
330
|
-
const dir = join(basePath, ".gsd");
|
|
331
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
332
|
-
writeFileSync(hookStatePath(basePath), JSON.stringify(state, null, 2), "utf-8");
|
|
333
|
-
} catch {
|
|
334
|
-
// Non-fatal — state is recreatable from artifacts
|
|
335
|
-
}
|
|
58
|
+
getOrCreateRegistry().persistState(basePath);
|
|
336
59
|
}
|
|
337
60
|
|
|
338
|
-
/**
|
|
339
|
-
* Restore hook cycle counts from disk after a crash/restart.
|
|
340
|
-
* Called during auto-mode resume.
|
|
341
|
-
*/
|
|
342
61
|
export function restoreHookState(basePath: string): void {
|
|
343
|
-
|
|
344
|
-
const filePath = hookStatePath(basePath);
|
|
345
|
-
if (!existsSync(filePath)) return;
|
|
346
|
-
const raw = readFileSync(filePath, "utf-8");
|
|
347
|
-
const state: PersistedHookState = JSON.parse(raw);
|
|
348
|
-
if (state.cycleCounts && typeof state.cycleCounts === "object") {
|
|
349
|
-
cycleCounts.clear();
|
|
350
|
-
for (const [key, value] of Object.entries(state.cycleCounts)) {
|
|
351
|
-
if (typeof value === "number") {
|
|
352
|
-
cycleCounts.set(key, value);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
} catch {
|
|
357
|
-
// Non-fatal — fresh state is fine
|
|
358
|
-
}
|
|
62
|
+
getOrCreateRegistry().restoreState(basePath);
|
|
359
63
|
}
|
|
360
64
|
|
|
361
|
-
/**
|
|
362
|
-
* Clear persisted hook state file from disk.
|
|
363
|
-
* Called on clean auto-mode stop.
|
|
364
|
-
*/
|
|
365
65
|
export function clearPersistedHookState(basePath: string): void {
|
|
366
|
-
|
|
367
|
-
const filePath = hookStatePath(basePath);
|
|
368
|
-
if (existsSync(filePath)) {
|
|
369
|
-
writeFileSync(filePath, JSON.stringify({ cycleCounts: {}, savedAt: new Date().toISOString() }, null, 2), "utf-8");
|
|
370
|
-
}
|
|
371
|
-
} catch {
|
|
372
|
-
// Non-fatal
|
|
373
|
-
}
|
|
66
|
+
getOrCreateRegistry().clearPersistedState(basePath);
|
|
374
67
|
}
|
|
375
68
|
|
|
376
|
-
//
|
|
377
|
-
// Phase 3: Hook Status Reporting
|
|
378
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
69
|
+
// ─── Status & Manual Trigger ───────────────────────────────────────────────
|
|
379
70
|
|
|
380
|
-
/**
|
|
381
|
-
* Get status of all configured hooks for display by /gsd hooks.
|
|
382
|
-
*/
|
|
383
71
|
export function getHookStatus(): HookStatusEntry[] {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
// Post-unit hooks
|
|
387
|
-
const postHooks = resolvePostUnitHooks();
|
|
388
|
-
for (const hook of postHooks) {
|
|
389
|
-
const activeCycles: Record<string, number> = {};
|
|
390
|
-
for (const [key, count] of cycleCounts) {
|
|
391
|
-
if (key.startsWith(`${hook.name}/`)) {
|
|
392
|
-
activeCycles[key] = count;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
entries.push({
|
|
396
|
-
name: hook.name,
|
|
397
|
-
type: "post",
|
|
398
|
-
enabled: hook.enabled !== false,
|
|
399
|
-
targets: hook.after,
|
|
400
|
-
activeCycles,
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Pre-dispatch hooks
|
|
405
|
-
const preHooks = resolvePreDispatchHooks();
|
|
406
|
-
for (const hook of preHooks) {
|
|
407
|
-
entries.push({
|
|
408
|
-
name: hook.name,
|
|
409
|
-
type: "pre",
|
|
410
|
-
enabled: hook.enabled !== false,
|
|
411
|
-
targets: hook.before,
|
|
412
|
-
activeCycles: {},
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
return entries;
|
|
72
|
+
return getOrCreateRegistry().getHookStatus();
|
|
417
73
|
}
|
|
418
74
|
|
|
419
|
-
/**
|
|
420
|
-
* Manually trigger a specific hook for a unit.
|
|
421
|
-
* This bypasses the normal flow and forces the hook to run even if its artifact exists.
|
|
422
|
-
*
|
|
423
|
-
* @param hookName - The name of the hook to trigger (e.g., "code-review")
|
|
424
|
-
* @param unitType - The type of unit that triggered the hook (e.g., "execute-task")
|
|
425
|
-
* @param unitId - The unit ID (e.g., "M001/S01/T01")
|
|
426
|
-
* @param basePath - The project base path
|
|
427
|
-
* @returns The hook dispatch result or null if hook not found
|
|
428
|
-
*/
|
|
429
75
|
export function triggerHookManually(
|
|
430
76
|
hookName: string,
|
|
431
77
|
unitType: string,
|
|
432
78
|
unitId: string,
|
|
433
79
|
basePath: string,
|
|
434
80
|
): HookDispatchResult | null {
|
|
435
|
-
|
|
436
|
-
const hook = resolvePostUnitHooks().find(h => h.name === hookName);
|
|
437
|
-
if (!hook) {
|
|
438
|
-
console.error(`[triggerHookManually] Hook "${hookName}" not found in post_unit_hooks`);
|
|
439
|
-
return null;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
if (!hook.prompt || typeof hook.prompt !== 'string' || hook.prompt.trim().length === 0) {
|
|
443
|
-
console.error(`[triggerHookManually] Hook "${hookName}" has empty prompt`);
|
|
444
|
-
return null;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Reset any active hook state to allow manual triggering
|
|
448
|
-
activeHook = {
|
|
449
|
-
hookName: hook.name,
|
|
450
|
-
triggerUnitType: unitType,
|
|
451
|
-
triggerUnitId: unitId,
|
|
452
|
-
cycle: 1,
|
|
453
|
-
pendingRetry: false,
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
// Build the hook queue with just this hook
|
|
457
|
-
hookQueue = [{
|
|
458
|
-
config: hook,
|
|
459
|
-
triggerUnitType: unitType,
|
|
460
|
-
triggerUnitId: unitId,
|
|
461
|
-
}];
|
|
462
|
-
|
|
463
|
-
// Set the cycle count for this specific hook+trigger
|
|
464
|
-
const cycleKey = `${hook.name}/${unitType}/${unitId}`;
|
|
465
|
-
const currentCycle = (cycleCounts.get(cycleKey) ?? 0) + 1;
|
|
466
|
-
cycleCounts.set(cycleKey, currentCycle);
|
|
467
|
-
|
|
468
|
-
// Update active hook with the cycle count
|
|
469
|
-
activeHook.cycle = currentCycle;
|
|
470
|
-
|
|
471
|
-
// Build the prompt with variable substitution
|
|
472
|
-
const [mid, sid, tid] = unitId.split("/");
|
|
473
|
-
const prompt = hook.prompt
|
|
474
|
-
.replace(/\{milestoneId\}/g, mid ?? "")
|
|
475
|
-
.replace(/\{sliceId\}/g, sid ?? "")
|
|
476
|
-
.replace(/\{taskId\}/g, tid ?? "");
|
|
477
|
-
|
|
478
|
-
console.log(`[triggerHookManually] Built prompt for ${hookName}, length: ${prompt.length}`);
|
|
479
|
-
|
|
480
|
-
return {
|
|
481
|
-
hookName: hook.name,
|
|
482
|
-
prompt,
|
|
483
|
-
model: hook.model,
|
|
484
|
-
unitType: `hook/${hook.name}`,
|
|
485
|
-
unitId,
|
|
486
|
-
};
|
|
81
|
+
return getOrCreateRegistry().triggerHookManually(hookName, unitType, unitId, basePath);
|
|
487
82
|
}
|
|
488
83
|
|
|
489
|
-
/**
|
|
490
|
-
* Format hook status for terminal display.
|
|
491
|
-
*/
|
|
492
84
|
export function formatHookStatus(): string {
|
|
493
|
-
|
|
494
|
-
if (entries.length === 0) {
|
|
495
|
-
return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/preferences.md";
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
const lines: string[] = ["Configured Hooks:", ""];
|
|
499
|
-
|
|
500
|
-
const postHooks = entries.filter(e => e.type === "post");
|
|
501
|
-
const preHooks = entries.filter(e => e.type === "pre");
|
|
502
|
-
|
|
503
|
-
if (postHooks.length > 0) {
|
|
504
|
-
lines.push("Post-Unit Hooks (run after unit completes):");
|
|
505
|
-
for (const hook of postHooks) {
|
|
506
|
-
const status = hook.enabled ? "enabled" : "disabled";
|
|
507
|
-
const cycles = Object.keys(hook.activeCycles).length;
|
|
508
|
-
const cycleInfo = cycles > 0 ? ` (${cycles} active cycle${cycles === 1 ? "" : "s"})` : "";
|
|
509
|
-
lines.push(` ${hook.name} [${status}] → after: ${hook.targets.join(", ")}${cycleInfo}`);
|
|
510
|
-
}
|
|
511
|
-
lines.push("");
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
if (preHooks.length > 0) {
|
|
515
|
-
lines.push("Pre-Dispatch Hooks (run before unit dispatches):");
|
|
516
|
-
for (const hook of preHooks) {
|
|
517
|
-
const status = hook.enabled ? "enabled" : "disabled";
|
|
518
|
-
lines.push(` ${hook.name} [${status}] → before: ${hook.targets.join(", ")}`);
|
|
519
|
-
}
|
|
520
|
-
lines.push("");
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
return lines.join("\n");
|
|
85
|
+
return getOrCreateRegistry().formatHookStatus();
|
|
524
86
|
}
|
|
@@ -88,6 +88,8 @@ export const KNOWN_PREFERENCE_KEYS = new Set<string>([
|
|
|
88
88
|
"widget_mode",
|
|
89
89
|
"reactive_execution",
|
|
90
90
|
"github",
|
|
91
|
+
"service_tier",
|
|
92
|
+
"forensics_dedup",
|
|
91
93
|
]);
|
|
92
94
|
|
|
93
95
|
/** Canonical list of all dispatch unit types. */
|
|
@@ -220,6 +222,10 @@ export interface GSDPreferences {
|
|
|
220
222
|
reactive_execution?: ReactiveExecutionConfig;
|
|
221
223
|
/** GitHub sync configuration. Opt-in: syncs GSD events to GitHub Issues, Milestones, and PRs. */
|
|
222
224
|
github?: GitHubSyncConfig;
|
|
225
|
+
/** OpenAI service tier preference. "priority" = 2x cost, faster. "flex" = 0.5x cost, slower. Only affects gpt-5.4 models. */
|
|
226
|
+
service_tier?: "priority" | "flex";
|
|
227
|
+
/** Opt-in: search existing issues and PRs before filing from /gsd forensics. Uses additional AI tokens. */
|
|
228
|
+
forensics_dedup?: boolean;
|
|
223
229
|
}
|
|
224
230
|
|
|
225
231
|
export interface LoadedGSDPreferences {
|
|
@@ -200,12 +200,22 @@ function loadPreferencesFile(path: string, scope: "global" | "project"): LoadedG
|
|
|
200
200
|
export function parsePreferencesMarkdown(content: string): GSDPreferences | null {
|
|
201
201
|
// Use indexOf instead of [\s\S]*? regex to avoid backtracking (#468)
|
|
202
202
|
const startMarker = content.startsWith('---\r\n') ? '---\r\n' : '---\n';
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
203
|
+
if (content.startsWith(startMarker)) {
|
|
204
|
+
const searchStart = startMarker.length;
|
|
205
|
+
const endIdx = content.indexOf('\n---', searchStart);
|
|
206
|
+
if (endIdx === -1) return null;
|
|
207
|
+
const block = content.slice(searchStart, endIdx);
|
|
208
|
+
return parseFrontmatterBlock(block.replace(/\r/g, ''));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Fallback: heading+list format (e.g. "## Git\n- isolation: none") (#2036)
|
|
212
|
+
// GSD agents may write preferences files without frontmatter delimiters.
|
|
213
|
+
if (/^##\s+\w/m.test(content)) {
|
|
214
|
+
return parseHeadingListFormat(content);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
console.warn("[parsePreferencesMarkdown] preferences.md exists but uses an unrecognized format — skipping.");
|
|
218
|
+
return null;
|
|
209
219
|
}
|
|
210
220
|
|
|
211
221
|
function parseFrontmatterBlock(frontmatter: string): GSDPreferences {
|
|
@@ -221,6 +231,51 @@ function parseFrontmatterBlock(frontmatter: string): GSDPreferences {
|
|
|
221
231
|
}
|
|
222
232
|
}
|
|
223
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Parse heading+list format into a nested object, then cast to GSDPreferences.
|
|
236
|
+
* Handles markdown like:
|
|
237
|
+
* ## Git
|
|
238
|
+
* - isolation: none
|
|
239
|
+
* - commit_docs: true
|
|
240
|
+
* ## Models
|
|
241
|
+
* - planner: sonnet
|
|
242
|
+
*/
|
|
243
|
+
function parseHeadingListFormat(content: string): GSDPreferences {
|
|
244
|
+
const result: Record<string, Record<string, string>> = {};
|
|
245
|
+
let currentSection: string | null = null;
|
|
246
|
+
|
|
247
|
+
for (const rawLine of content.split('\n')) {
|
|
248
|
+
const line = rawLine.replace(/\r$/, '');
|
|
249
|
+
const headingMatch = line.match(/^##\s+(.+)$/);
|
|
250
|
+
if (headingMatch) {
|
|
251
|
+
currentSection = headingMatch[1].trim().toLowerCase().replace(/\s+/g, '_');
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (currentSection) {
|
|
255
|
+
const itemMatch = line.match(/^-\s+([^:]+):\s*(.*)$/);
|
|
256
|
+
if (itemMatch) {
|
|
257
|
+
if (!result[currentSection]) result[currentSection] = {};
|
|
258
|
+
const value = itemMatch[2].trim();
|
|
259
|
+
// Coerce "true"/"false" strings and numbers
|
|
260
|
+
result[currentSection][itemMatch[1].trim()] = value;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Convert string values to appropriate types via YAML parser for each section
|
|
266
|
+
const typed: Record<string, unknown> = {};
|
|
267
|
+
for (const [section, entries] of Object.entries(result)) {
|
|
268
|
+
const yamlLines = Object.entries(entries).map(([k, v]) => `${k}: ${v}`).join('\n');
|
|
269
|
+
try {
|
|
270
|
+
typed[section] = parseYaml(yamlLines);
|
|
271
|
+
} catch {
|
|
272
|
+
typed[section] = entries;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return typed as GSDPreferences;
|
|
277
|
+
}
|
|
278
|
+
|
|
224
279
|
// ─── Merging ────────────────────────────────────────────────────────────────
|
|
225
280
|
|
|
226
281
|
/**
|
|
@@ -285,6 +340,8 @@ function mergePreferences(base: GSDPreferences, override: GSDPreferences): GSDPr
|
|
|
285
340
|
github: (base.github || override.github)
|
|
286
341
|
? { ...(base.github ?? {}), ...(override.github ?? {}) } as import("../github-sync/types.js").GitHubSyncConfig
|
|
287
342
|
: undefined,
|
|
343
|
+
service_tier: override.service_tier ?? base.service_tier,
|
|
344
|
+
forensics_dedup: override.forensics_dedup ?? base.forensics_dedup,
|
|
288
345
|
};
|
|
289
346
|
}
|
|
290
347
|
|