gsd-pi 2.59.0 → 2.60.0-dev.2580e65
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/dist/resources/extensions/ask-user-questions.js +7 -4
- package/dist/resources/extensions/gsd/auto/phases.js +62 -1
- package/dist/resources/extensions/gsd/auto-dashboard.js +21 -8
- package/dist/resources/extensions/gsd/auto-dispatch.js +6 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +57 -3
- package/dist/resources/extensions/gsd/auto-post-unit.js +43 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +49 -20
- package/dist/resources/extensions/gsd/auto-recovery.js +37 -18
- package/dist/resources/extensions/gsd/auto-start.js +9 -5
- package/dist/resources/extensions/gsd/auto-timers.js +11 -5
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +5 -3
- package/dist/resources/extensions/gsd/auto-verification.js +3 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +120 -55
- package/dist/resources/extensions/gsd/auto.js +39 -17
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +6 -3
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +72 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +4 -10
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +2 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +58 -5
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -10
- package/dist/resources/extensions/gsd/captures.js +54 -1
- package/dist/resources/extensions/gsd/commands/catalog.js +2 -0
- package/dist/resources/extensions/gsd/commands-codebase.js +48 -21
- package/dist/resources/extensions/gsd/commands-inspect.js +2 -1
- package/dist/resources/extensions/gsd/commands-maintenance.js +32 -19
- package/dist/resources/extensions/gsd/complexity-classifier.js +9 -5
- package/dist/resources/extensions/gsd/context-masker.js +68 -0
- package/dist/resources/extensions/gsd/custom-verification.js +3 -2
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +7 -0
- package/dist/resources/extensions/gsd/gsd-db.js +35 -15
- package/dist/resources/extensions/gsd/guided-flow.js +19 -9
- package/dist/resources/extensions/gsd/init-wizard.js +12 -0
- package/dist/resources/extensions/gsd/markdown-renderer.js +11 -9
- package/dist/resources/extensions/gsd/md-importer.js +5 -4
- package/dist/resources/extensions/gsd/milestone-actions.js +3 -2
- package/dist/resources/extensions/gsd/milestone-ids.js +2 -1
- package/dist/resources/extensions/gsd/model-router.js +199 -45
- package/dist/resources/extensions/gsd/parallel-merge.js +5 -3
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +26 -14
- package/dist/resources/extensions/gsd/phase-anchor.js +56 -0
- package/dist/resources/extensions/gsd/preferences-types.js +2 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +91 -0
- package/dist/resources/extensions/gsd/preferences.js +15 -3
- package/dist/resources/extensions/gsd/prompt-loader.js +3 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
- package/dist/resources/extensions/gsd/prompts/rethink.md +7 -0
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +6 -1
- package/dist/resources/extensions/gsd/rethink.js +5 -2
- package/dist/resources/extensions/gsd/rule-registry.js +7 -6
- package/dist/resources/extensions/gsd/safe-fs.js +6 -8
- package/dist/resources/extensions/gsd/state.js +1 -1
- package/dist/resources/extensions/gsd/status-guards.js +4 -3
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -2
- package/dist/resources/extensions/gsd/tools/complete-task.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-slice.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-task.js +2 -1
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -1
- package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -1
- package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +2 -1
- package/dist/resources/extensions/gsd/triage-resolution.js +135 -1
- package/dist/resources/extensions/gsd/triage-ui.js +12 -3
- package/dist/resources/extensions/gsd/workflow-events.js +2 -1
- package/dist/resources/extensions/gsd/workflow-logger.js +37 -4
- package/dist/resources/extensions/gsd/workflow-migration.js +14 -12
- package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
- package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +26 -14
- package/dist/resources/extensions/shared/interview-ui.js +3 -1
- package/dist/resources/skills/btw/SKILL.md +42 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/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 +2 -2
- 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/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- 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 +17 -17
- package/dist/web/standalone/.next/server/chunks/2229.js +1 -1
- package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +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 +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +16 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +26 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +6 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +47 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +122 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +30 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +1 -7
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +26 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +7 -1
- package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
- package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +70 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +156 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +7 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +38 -0
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +1 -8
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +7 -3
- package/src/resources/extensions/gsd/auto/phases.ts +70 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +22 -8
- package/src/resources/extensions/gsd/auto-dispatch.ts +7 -3
- package/src/resources/extensions/gsd/auto-model-selection.ts +77 -6
- package/src/resources/extensions/gsd/auto-post-unit.ts +52 -5
- package/src/resources/extensions/gsd/auto-prompts.ts +54 -20
- package/src/resources/extensions/gsd/auto-recovery.ts +38 -18
- package/src/resources/extensions/gsd/auto-start.ts +10 -9
- package/src/resources/extensions/gsd/auto-timers.ts +12 -5
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +6 -2
- package/src/resources/extensions/gsd/auto-verification.ts +3 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +121 -55
- package/src/resources/extensions/gsd/auto.ts +40 -17
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +4 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +80 -2
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +4 -16
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +2 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +61 -4
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -10
- package/src/resources/extensions/gsd/captures.ts +71 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +2 -0
- package/src/resources/extensions/gsd/commands-codebase.ts +52 -20
- package/src/resources/extensions/gsd/commands-inspect.ts +2 -1
- package/src/resources/extensions/gsd/commands-maintenance.ts +28 -19
- package/src/resources/extensions/gsd/complexity-classifier.ts +10 -5
- package/src/resources/extensions/gsd/context-masker.ts +74 -0
- package/src/resources/extensions/gsd/custom-verification.ts +3 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +7 -0
- package/src/resources/extensions/gsd/gsd-db.ts +14 -16
- package/src/resources/extensions/gsd/guided-flow.ts +9 -8
- package/src/resources/extensions/gsd/init-wizard.ts +12 -0
- package/src/resources/extensions/gsd/markdown-renderer.ts +11 -17
- package/src/resources/extensions/gsd/md-importer.ts +5 -4
- package/src/resources/extensions/gsd/milestone-actions.ts +3 -2
- package/src/resources/extensions/gsd/milestone-ids.ts +2 -1
- package/src/resources/extensions/gsd/model-router.ts +245 -56
- package/src/resources/extensions/gsd/parallel-merge.ts +5 -3
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +18 -14
- package/src/resources/extensions/gsd/phase-anchor.ts +71 -0
- package/src/resources/extensions/gsd/preferences-types.ts +22 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +83 -0
- package/src/resources/extensions/gsd/preferences.ts +16 -3
- package/src/resources/extensions/gsd/prompt-loader.ts +3 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
- package/src/resources/extensions/gsd/prompts/rethink.md +7 -0
- package/src/resources/extensions/gsd/prompts/triage-captures.md +6 -1
- package/src/resources/extensions/gsd/rethink.ts +5 -2
- package/src/resources/extensions/gsd/rule-registry.ts +7 -6
- package/src/resources/extensions/gsd/safe-fs.ts +6 -5
- package/src/resources/extensions/gsd/state.ts +1 -1
- package/src/resources/extensions/gsd/status-guards.ts +4 -3
- package/src/resources/extensions/gsd/tests/capability-router.test.ts +347 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +27 -2
- package/src/resources/extensions/gsd/tests/context-masker.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1188 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +841 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +488 -2
- package/src/resources/extensions/gsd/tests/phase-anchor.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +284 -0
- package/src/resources/extensions/gsd/tests/status-guards.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/stop-backtrack.test.ts +216 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -6
- package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -6
- package/src/resources/extensions/gsd/tools/complete-task.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-slice.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-task.ts +2 -3
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +4 -6
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -3
- package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -3
- package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -3
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +2 -3
- package/src/resources/extensions/gsd/triage-resolution.ts +151 -1
- package/src/resources/extensions/gsd/triage-ui.ts +12 -3
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/workflow-events.ts +2 -1
- package/src/resources/extensions/gsd/workflow-logger.ts +52 -5
- package/src/resources/extensions/gsd/workflow-migration.ts +14 -12
- package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
- package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +16 -14
- package/src/resources/extensions/shared/interview-ui.ts +3 -1
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +144 -0
- package/src/resources/skills/btw/SKILL.md +42 -0
- package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- /package/dist/web/standalone/.next/static/{DGvT_c5Vb7Wu3X-fEOVUU → ogyMN7M-3bGGuRY08L5HR}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{DGvT_c5Vb7Wu3X-fEOVUU → ogyMN7M-3bGGuRY08L5HR}/_ssgManifest.js +0 -0
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// Critical invariant: rendered markdown must round-trip through
|
|
9
9
|
// parseRoadmap(), parsePlan(), parseSummary() in files.ts.
|
|
10
10
|
import { readFileSync, existsSync, mkdirSync } from "node:fs";
|
|
11
|
+
import { logWarning } from "./workflow-logger.js";
|
|
11
12
|
import { isClosedStatus } from "./status-guards.js";
|
|
12
13
|
import { join, relative } from "node:path";
|
|
13
14
|
import { createRequire } from "node:module";
|
|
@@ -56,7 +57,7 @@ function loadArtifactContent(artifactPath, absPath, opts) {
|
|
|
56
57
|
content = readFileSync(absPath, "utf-8");
|
|
57
58
|
}
|
|
58
59
|
catch {
|
|
59
|
-
|
|
60
|
+
logWarning("renderer", `cannot read file from disk: ${absPath}`);
|
|
60
61
|
return null;
|
|
61
62
|
}
|
|
62
63
|
// Store in DB for future use (graceful degradation path)
|
|
@@ -72,7 +73,7 @@ function loadArtifactContent(artifactPath, absPath, opts) {
|
|
|
72
73
|
}
|
|
73
74
|
catch {
|
|
74
75
|
// Non-fatal: we have the content, DB storage is best-effort
|
|
75
|
-
|
|
76
|
+
logWarning("renderer", `failed to store disk fallback in DB: ${artifactPath}`);
|
|
76
77
|
}
|
|
77
78
|
return content;
|
|
78
79
|
}
|
|
@@ -93,7 +94,7 @@ async function writeAndStore(absPath, artifactPath, content, opts) {
|
|
|
93
94
|
}
|
|
94
95
|
catch {
|
|
95
96
|
// Non-fatal: file is on disk, DB is best-effort
|
|
96
|
-
|
|
97
|
+
logWarning("renderer", `failed to update artifact in DB: ${artifactPath}`);
|
|
97
98
|
}
|
|
98
99
|
invalidateCaches();
|
|
99
100
|
}
|
|
@@ -613,7 +614,8 @@ export function detectStaleRenders(basePath) {
|
|
|
613
614
|
parseRoadmap = m.parseRoadmap;
|
|
614
615
|
parsePlan = m.parsePlan;
|
|
615
616
|
}
|
|
616
|
-
catch {
|
|
617
|
+
catch (e) {
|
|
618
|
+
logWarning("renderer", `parsers-legacy.ts require failed, falling back to .js: ${e.message}`);
|
|
617
619
|
const m = _require("./parsers-legacy.js");
|
|
618
620
|
parseRoadmap = m.parseRoadmap;
|
|
619
621
|
parsePlan = m.parsePlan;
|
|
@@ -647,8 +649,8 @@ export function detectStaleRenders(basePath) {
|
|
|
647
649
|
}
|
|
648
650
|
}
|
|
649
651
|
}
|
|
650
|
-
catch {
|
|
651
|
-
|
|
652
|
+
catch (e) {
|
|
653
|
+
logWarning("renderer", `roadmap parse failed: ${e.message}`);
|
|
652
654
|
}
|
|
653
655
|
}
|
|
654
656
|
// ── Check plan checkbox state and summaries for each slice ────────
|
|
@@ -679,8 +681,8 @@ export function detectStaleRenders(basePath) {
|
|
|
679
681
|
}
|
|
680
682
|
}
|
|
681
683
|
}
|
|
682
|
-
catch {
|
|
683
|
-
|
|
684
|
+
catch (e) {
|
|
685
|
+
logWarning("renderer", `plan parse failed: ${e.message}`);
|
|
684
686
|
}
|
|
685
687
|
}
|
|
686
688
|
// Check missing task summary files
|
|
@@ -824,7 +826,7 @@ export async function repairStaleRenders(basePath) {
|
|
|
824
826
|
}
|
|
825
827
|
}
|
|
826
828
|
catch (err) {
|
|
827
|
-
|
|
829
|
+
logWarning("renderer", `repair failed for ${entry.path}: ${err.message}`);
|
|
828
830
|
}
|
|
829
831
|
}
|
|
830
832
|
if (repairCount > 0) {
|
|
@@ -10,6 +10,7 @@ import { resolveGsdRootFile, resolveMilestoneFile, resolveSliceFile, resolveTask
|
|
|
10
10
|
import { findMilestoneIds } from './guided-flow.js';
|
|
11
11
|
import { parseRoadmap, parsePlan } from './parsers-legacy.js';
|
|
12
12
|
import { parseContextDependsOn } from './files.js';
|
|
13
|
+
import { logWarning } from './workflow-logger.js';
|
|
13
14
|
// ─── DECISIONS.md Parser ───────────────────────────────────────────────────
|
|
14
15
|
const VALID_MADE_BY = new Set(['human', 'agent', 'collaborative']);
|
|
15
16
|
/**
|
|
@@ -597,25 +598,25 @@ export function migrateFromMarkdown(gsdDir) {
|
|
|
597
598
|
decisions = importDecisions(gsdDir);
|
|
598
599
|
}
|
|
599
600
|
catch (err) {
|
|
600
|
-
|
|
601
|
+
logWarning("migration", `skipping decisions import: ${err.message}`);
|
|
601
602
|
}
|
|
602
603
|
try {
|
|
603
604
|
requirements = importRequirements(gsdDir);
|
|
604
605
|
}
|
|
605
606
|
catch (err) {
|
|
606
|
-
|
|
607
|
+
logWarning("migration", `skipping requirements import: ${err.message}`);
|
|
607
608
|
}
|
|
608
609
|
try {
|
|
609
610
|
artifacts = importHierarchyArtifacts(gsdDir);
|
|
610
611
|
}
|
|
611
612
|
catch (err) {
|
|
612
|
-
|
|
613
|
+
logWarning("migration", `skipping artifacts import: ${err.message}`);
|
|
613
614
|
}
|
|
614
615
|
try {
|
|
615
616
|
hierarchy = migrateHierarchyToDb(gsdDir);
|
|
616
617
|
}
|
|
617
618
|
catch (err) {
|
|
618
|
-
|
|
619
|
+
logWarning("migration", `skipping hierarchy migration: ${err.message}`);
|
|
619
620
|
}
|
|
620
621
|
});
|
|
621
622
|
process.stderr.write(`gsd-migrate: imported ${decisions} decisions, ${requirements} requirements, ${artifacts} artifacts, ${hierarchy.milestones}M/${hierarchy.slices}S/${hierarchy.tasks}T hierarchy\n`);
|
|
@@ -16,6 +16,7 @@ import { resolveMilestonePath, resolveMilestoneFile, buildMilestoneFileName, } f
|
|
|
16
16
|
import { invalidateAllCaches } from "./cache.js";
|
|
17
17
|
import { loadQueueOrder, saveQueueOrder } from "./queue-order.js";
|
|
18
18
|
import { isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
|
|
19
|
+
import { logWarning } from "./workflow-logger.js";
|
|
19
20
|
// ─── Park ──────────────────────────────────────────────────────────────────
|
|
20
21
|
/**
|
|
21
22
|
* Park a milestone — creates a PARKED.md marker file with reason and timestamp.
|
|
@@ -51,7 +52,7 @@ export function parkMilestone(basePath, milestoneId, reason) {
|
|
|
51
52
|
updateMilestoneStatus(milestoneId, "parked");
|
|
52
53
|
}
|
|
53
54
|
catch (err) {
|
|
54
|
-
|
|
55
|
+
logWarning("engine", `parkMilestone DB sync failed for ${milestoneId}: ${err.message}`);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
invalidateAllCaches();
|
|
@@ -76,7 +77,7 @@ export function unparkMilestone(basePath, milestoneId) {
|
|
|
76
77
|
updateMilestoneStatus(milestoneId, "active");
|
|
77
78
|
}
|
|
78
79
|
catch (err) {
|
|
79
|
-
|
|
80
|
+
logWarning("engine", `unparkMilestone DB sync failed for ${milestoneId}: ${err.message}`);
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
invalidateAllCaches();
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Consumed by 15+ modules across the GSD extension. Zero side-effects.
|
|
6
6
|
*/
|
|
7
7
|
import { randomInt } from "node:crypto";
|
|
8
|
+
import { logWarning } from "./workflow-logger.js";
|
|
8
9
|
import { readdirSync, existsSync } from "node:fs";
|
|
9
10
|
import { milestonesDir } from "./paths.js";
|
|
10
11
|
import { loadQueueOrder, sortByQueueOrder } from "./queue-order.js";
|
|
@@ -109,7 +110,7 @@ export function findMilestoneIds(basePath) {
|
|
|
109
110
|
catch (err) {
|
|
110
111
|
// Log why milestone scanning failed — silent [] here causes infinite loops (#456)
|
|
111
112
|
if (existsSync(dir)) {
|
|
112
|
-
|
|
113
|
+
logWarning("engine", `findMilestoneIds: .gsd/milestones/ exists but readdirSync failed — ${getErrorMessage(err)}`);
|
|
113
114
|
}
|
|
114
115
|
return [];
|
|
115
116
|
}
|
|
@@ -76,19 +76,177 @@ const MODEL_COST_PER_1K_INPUT = {
|
|
|
76
76
|
"gemini-2.5-pro": 0.00125,
|
|
77
77
|
"deepseek-chat": 0.00014,
|
|
78
78
|
};
|
|
79
|
+
// ─── Capability Profiles Data Table ──────────────────────────────────────────
|
|
80
|
+
// Per-model capability profiles (0–100 scale). Used for capability-aware
|
|
81
|
+
// model selection within an eligible tier set.
|
|
82
|
+
export const MODEL_CAPABILITY_PROFILES = {
|
|
83
|
+
"claude-opus-4-6": { coding: 95, debugging: 90, research: 85, reasoning: 95, speed: 30, longContext: 80, instruction: 90 },
|
|
84
|
+
"claude-sonnet-4-6": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
|
|
85
|
+
"claude-haiku-4-5": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
|
|
86
|
+
"gpt-4o": { coding: 80, debugging: 75, research: 70, reasoning: 75, speed: 65, longContext: 70, instruction: 80 },
|
|
87
|
+
"gpt-4o-mini": { coding: 55, debugging: 45, research: 40, reasoning: 45, speed: 90, longContext: 45, instruction: 70 },
|
|
88
|
+
"gemini-2.5-pro": { coding: 75, debugging: 70, research: 85, reasoning: 75, speed: 55, longContext: 90, instruction: 75 },
|
|
89
|
+
"gemini-2.0-flash": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
|
|
90
|
+
"deepseek-chat": { coding: 75, debugging: 65, research: 55, reasoning: 70, speed: 70, longContext: 55, instruction: 65 },
|
|
91
|
+
"o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
|
|
92
|
+
};
|
|
93
|
+
// ─── Base Task Requirements Data Table ───────────────────────────────────────
|
|
94
|
+
// Per-unit-type base requirement vectors. Weights indicate how important each
|
|
95
|
+
// capability dimension is for this unit type.
|
|
96
|
+
export const BASE_REQUIREMENTS = {
|
|
97
|
+
"execute-task": { coding: 0.9, instruction: 0.7, speed: 0.3 },
|
|
98
|
+
"research-milestone": { research: 0.9, longContext: 0.7, reasoning: 0.5 },
|
|
99
|
+
"research-slice": { research: 0.9, longContext: 0.7, reasoning: 0.5 },
|
|
100
|
+
"plan-milestone": { reasoning: 0.9, coding: 0.5 },
|
|
101
|
+
"plan-slice": { reasoning: 0.9, coding: 0.5 },
|
|
102
|
+
"replan-slice": { reasoning: 0.9, debugging: 0.6, coding: 0.5 },
|
|
103
|
+
"reassess-roadmap": { reasoning: 0.9, research: 0.5 },
|
|
104
|
+
"complete-slice": { instruction: 0.8, speed: 0.7 },
|
|
105
|
+
"run-uat": { instruction: 0.7, speed: 0.8 },
|
|
106
|
+
"discuss-milestone": { reasoning: 0.6, instruction: 0.7 },
|
|
107
|
+
"complete-milestone": { instruction: 0.8, reasoning: 0.5 },
|
|
108
|
+
};
|
|
79
109
|
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
110
|
+
/**
|
|
111
|
+
* Score a model's suitability for a task given a requirement vector.
|
|
112
|
+
* Returns a weighted average of capability dimensions (0–100).
|
|
113
|
+
* Returns 50 if requirements are empty (neutral score).
|
|
114
|
+
*/
|
|
115
|
+
export function scoreModel(model, requirements) {
|
|
116
|
+
let weightedSum = 0;
|
|
117
|
+
let weightSum = 0;
|
|
118
|
+
for (const [dim, weight] of Object.entries(requirements)) {
|
|
119
|
+
const capability = model[dim] ?? 50;
|
|
120
|
+
weightedSum += weight * capability;
|
|
121
|
+
weightSum += weight;
|
|
122
|
+
}
|
|
123
|
+
return weightSum > 0 ? weightedSum / weightSum : 50;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Compute dynamic task requirements from unit type and optional task metadata.
|
|
127
|
+
* Returns a requirement vector refined by task-specific signals.
|
|
128
|
+
*/
|
|
129
|
+
export function computeTaskRequirements(unitType, metadata) {
|
|
130
|
+
const base = BASE_REQUIREMENTS[unitType] ?? { reasoning: 0.5 };
|
|
131
|
+
if (unitType === "execute-task" && metadata) {
|
|
132
|
+
if (metadata.tags?.some(t => /^(docs?|readme|comment|config|typo|rename)$/i.test(t))) {
|
|
133
|
+
return { ...base, instruction: 0.9, coding: 0.3, speed: 0.7 };
|
|
134
|
+
}
|
|
135
|
+
if (metadata.complexityKeywords?.some(k => k === "concurrency" || k === "compatibility")) {
|
|
136
|
+
return { ...base, debugging: 0.9, reasoning: 0.8 };
|
|
137
|
+
}
|
|
138
|
+
if (metadata.complexityKeywords?.some(k => k === "migration" || k === "architecture")) {
|
|
139
|
+
return { ...base, reasoning: 0.9, coding: 0.8 };
|
|
140
|
+
}
|
|
141
|
+
if ((metadata.fileCount ?? 0) >= 6 || (metadata.estimatedLines ?? 0) >= 500) {
|
|
142
|
+
return { ...base, coding: 0.9, reasoning: 0.7 };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return base;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Score all eligible models against a requirement vector and return them
|
|
149
|
+
* sorted by score descending. Within 2 points: prefer cheaper; equal cost:
|
|
150
|
+
* lexicographic tie-break by model ID.
|
|
151
|
+
*/
|
|
152
|
+
export function scoreEligibleModels(eligibleModelIds, requirements, capabilityOverrides) {
|
|
153
|
+
const scored = eligibleModelIds.map(modelId => {
|
|
154
|
+
const builtin = MODEL_CAPABILITY_PROFILES[modelId];
|
|
155
|
+
const override = capabilityOverrides?.[modelId];
|
|
156
|
+
const profile = builtin
|
|
157
|
+
? override ? { ...builtin, ...override } : builtin
|
|
158
|
+
: { coding: 50, debugging: 50, research: 50, reasoning: 50, speed: 50, longContext: 50, instruction: 50 };
|
|
159
|
+
return { modelId, score: scoreModel(profile, requirements) };
|
|
160
|
+
});
|
|
161
|
+
scored.sort((a, b) => {
|
|
162
|
+
const scoreDiff = b.score - a.score;
|
|
163
|
+
if (Math.abs(scoreDiff) > 2)
|
|
164
|
+
return scoreDiff;
|
|
165
|
+
const costA = MODEL_COST_PER_1K_INPUT[a.modelId] ?? Infinity;
|
|
166
|
+
const costB = MODEL_COST_PER_1K_INPUT[b.modelId] ?? Infinity;
|
|
167
|
+
if (costA !== costB)
|
|
168
|
+
return costA - costB;
|
|
169
|
+
return a.modelId.localeCompare(b.modelId);
|
|
170
|
+
});
|
|
171
|
+
return scored;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Return all models eligible for a given tier, sorted cheapest first.
|
|
175
|
+
* If routingConfig.tier_models[tier] is set and available, returns only that
|
|
176
|
+
* model. Otherwise filters availableModelIds by tier from MODEL_CAPABILITY_TIER.
|
|
177
|
+
*/
|
|
178
|
+
export function getEligibleModels(tier, availableModelIds, routingConfig) {
|
|
179
|
+
// 1. Check explicit tier_models config
|
|
180
|
+
const explicitModel = routingConfig.tier_models?.[tier];
|
|
181
|
+
if (explicitModel) {
|
|
182
|
+
// Exact match
|
|
183
|
+
if (availableModelIds.includes(explicitModel))
|
|
184
|
+
return [explicitModel];
|
|
185
|
+
// Provider-prefix-stripped match
|
|
186
|
+
const match = availableModelIds.find(id => {
|
|
187
|
+
const bareAvail = id.includes("/") ? id.split("/").pop() : id;
|
|
188
|
+
const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop() : explicitModel;
|
|
189
|
+
return bareAvail === bareExplicit;
|
|
190
|
+
});
|
|
191
|
+
if (match)
|
|
192
|
+
return [match];
|
|
193
|
+
}
|
|
194
|
+
// 2. Auto-detect: filter by tier, sort cheapest first
|
|
195
|
+
return availableModelIds
|
|
196
|
+
.filter(id => getModelTier(id) === tier)
|
|
197
|
+
.sort((a, b) => {
|
|
198
|
+
const costA = getModelCost(a);
|
|
199
|
+
const costB = getModelCost(b);
|
|
200
|
+
return costA - costB;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Build a fallback chain for a selected model: [selectedModel, ...configuredFallbacks, configuredPrimary]
|
|
205
|
+
* Deduplicates entries while preserving order.
|
|
206
|
+
*/
|
|
207
|
+
function buildFallbackChain(selectedModelId, phaseConfig) {
|
|
208
|
+
return [
|
|
209
|
+
...phaseConfig.fallbacks.filter(f => f !== selectedModelId),
|
|
210
|
+
phaseConfig.primary,
|
|
211
|
+
].filter(f => f !== selectedModelId);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Load capability overrides from user preferences' modelOverrides section.
|
|
215
|
+
* Returns a map of model ID → partial capability overrides to deep-merge with built-in profiles.
|
|
216
|
+
*
|
|
217
|
+
* Per D-17: partial capability overrides via models.json modelOverrides, deep-merged with defaults.
|
|
218
|
+
*/
|
|
219
|
+
export function loadCapabilityOverrides(prefs) {
|
|
220
|
+
const result = {};
|
|
221
|
+
if (!prefs.modelOverrides)
|
|
222
|
+
return result;
|
|
223
|
+
for (const [modelId, overrideEntry] of Object.entries(prefs.modelOverrides)) {
|
|
224
|
+
if (overrideEntry.capabilities) {
|
|
225
|
+
result[modelId] = overrideEntry.capabilities;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
80
230
|
/**
|
|
81
231
|
* Resolve the model to use for a given complexity tier.
|
|
82
232
|
*
|
|
83
233
|
* Downgrade-only: the returned model is always equal to or cheaper than
|
|
84
234
|
* the user's configured primary model. Never upgrades beyond configuration.
|
|
85
235
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
236
|
+
* STEP 1: Filter to eligible models for the requested tier.
|
|
237
|
+
* STEP 2: Capability scoring — ranks eligible models by task-capability match
|
|
238
|
+
* when capability_routing is enabled and multiple eligible models exist.
|
|
239
|
+
* STEP 3: Fallback chain assembly.
|
|
240
|
+
*
|
|
241
|
+
* @param classification The complexity classification result
|
|
242
|
+
* @param phaseConfig The user's configured model for this phase (ceiling)
|
|
243
|
+
* @param routingConfig Dynamic routing configuration
|
|
244
|
+
* @param availableModelIds List of available model IDs (from registry)
|
|
245
|
+
* @param unitType The unit type for capability requirement computation (optional)
|
|
246
|
+
* @param taskMetadata Task metadata for refined requirement vectors (optional)
|
|
247
|
+
* @param capabilityOverrides User-provided capability overrides (deep-merged with built-in profiles, optional)
|
|
90
248
|
*/
|
|
91
|
-
export function resolveModelForComplexity(classification, phaseConfig, routingConfig, availableModelIds) {
|
|
249
|
+
export function resolveModelForComplexity(classification, phaseConfig, routingConfig, availableModelIds, unitType, taskMetadata, capabilityOverrides) {
|
|
92
250
|
// If no phase config or routing disabled, pass through
|
|
93
251
|
if (!phaseConfig || !routingConfig.enabled) {
|
|
94
252
|
return {
|
|
@@ -97,6 +255,7 @@ export function resolveModelForComplexity(classification, phaseConfig, routingCo
|
|
|
97
255
|
tier: classification.tier,
|
|
98
256
|
wasDowngraded: false,
|
|
99
257
|
reason: "dynamic routing disabled or no phase config",
|
|
258
|
+
selectionMethod: "tier-only",
|
|
100
259
|
};
|
|
101
260
|
}
|
|
102
261
|
const configuredPrimary = phaseConfig.primary;
|
|
@@ -114,6 +273,7 @@ export function resolveModelForComplexity(classification, phaseConfig, routingCo
|
|
|
114
273
|
tier: requestedTier,
|
|
115
274
|
wasDowngraded: false,
|
|
116
275
|
reason: `configured model "${configuredPrimary}" is not in the known tier map — honoring explicit config`,
|
|
276
|
+
selectionMethod: "tier-only",
|
|
117
277
|
};
|
|
118
278
|
}
|
|
119
279
|
// Downgrade-only: if requested tier >= configured tier, no change
|
|
@@ -124,11 +284,12 @@ export function resolveModelForComplexity(classification, phaseConfig, routingCo
|
|
|
124
284
|
tier: requestedTier,
|
|
125
285
|
wasDowngraded: false,
|
|
126
286
|
reason: `tier ${requestedTier} >= configured ${configuredTier}`,
|
|
287
|
+
selectionMethod: "tier-only",
|
|
127
288
|
};
|
|
128
289
|
}
|
|
129
|
-
//
|
|
130
|
-
const
|
|
131
|
-
if (
|
|
290
|
+
// STEP 1: Get all eligible models for the requested tier
|
|
291
|
+
const eligible = getEligibleModels(requestedTier, availableModelIds, routingConfig);
|
|
292
|
+
if (eligible.length === 0) {
|
|
132
293
|
// No suitable model found — use configured primary
|
|
133
294
|
return {
|
|
134
295
|
modelId: configuredPrimary,
|
|
@@ -136,19 +297,42 @@ export function resolveModelForComplexity(classification, phaseConfig, routingCo
|
|
|
136
297
|
tier: requestedTier,
|
|
137
298
|
wasDowngraded: false,
|
|
138
299
|
reason: `no ${requestedTier}-tier model available`,
|
|
300
|
+
selectionMethod: "tier-only",
|
|
139
301
|
};
|
|
140
302
|
}
|
|
303
|
+
// STEP 2: Capability scoring (when enabled and multiple eligible models exist)
|
|
304
|
+
if (routingConfig.capability_routing !== false && eligible.length > 1 && unitType) {
|
|
305
|
+
const requirements = computeTaskRequirements(unitType, taskMetadata);
|
|
306
|
+
const scored = scoreEligibleModels(eligible, requirements, capabilityOverrides);
|
|
307
|
+
const winner = scored[0];
|
|
308
|
+
if (winner) {
|
|
309
|
+
const capScores = {};
|
|
310
|
+
for (const s of scored)
|
|
311
|
+
capScores[s.modelId] = s.score;
|
|
312
|
+
const fallbacks = buildFallbackChain(winner.modelId, phaseConfig);
|
|
313
|
+
return {
|
|
314
|
+
modelId: winner.modelId,
|
|
315
|
+
fallbacks,
|
|
316
|
+
tier: requestedTier,
|
|
317
|
+
wasDowngraded: true,
|
|
318
|
+
reason: `capability-scored: ${winner.modelId} (${winner.score.toFixed(1)}) for ${unitType}`,
|
|
319
|
+
capabilityScores: capScores,
|
|
320
|
+
taskRequirements: requirements,
|
|
321
|
+
selectionMethod: "capability-scored",
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// STEP 3: Fallback — use first eligible model (cheapest in tier, or single eligible)
|
|
326
|
+
const targetModelId = eligible[0];
|
|
141
327
|
// Build fallback chain: [downgraded_model, ...configured_fallbacks, configured_primary]
|
|
142
|
-
const fallbacks =
|
|
143
|
-
...phaseConfig.fallbacks.filter(f => f !== targetModelId),
|
|
144
|
-
configuredPrimary,
|
|
145
|
-
].filter(f => f !== targetModelId);
|
|
328
|
+
const fallbacks = buildFallbackChain(targetModelId, phaseConfig);
|
|
146
329
|
return {
|
|
147
330
|
modelId: targetModelId,
|
|
148
331
|
fallbacks,
|
|
149
332
|
tier: requestedTier,
|
|
150
333
|
wasDowngraded: true,
|
|
151
334
|
reason: classification.reason,
|
|
335
|
+
selectionMethod: "tier-only",
|
|
152
336
|
};
|
|
153
337
|
}
|
|
154
338
|
/**
|
|
@@ -168,6 +352,7 @@ export function escalateTier(currentTier) {
|
|
|
168
352
|
export function defaultRoutingConfig() {
|
|
169
353
|
return {
|
|
170
354
|
enabled: true,
|
|
355
|
+
capability_routing: true,
|
|
171
356
|
escalate_on_failure: true,
|
|
172
357
|
budget_pressure: true,
|
|
173
358
|
cross_provider: true,
|
|
@@ -186,8 +371,8 @@ function getModelTier(modelId) {
|
|
|
186
371
|
if (bareId.includes(knownId) || knownId.includes(bareId))
|
|
187
372
|
return tier;
|
|
188
373
|
}
|
|
189
|
-
// Unknown models are assumed
|
|
190
|
-
return "
|
|
374
|
+
// Unknown models are assumed standard (per D-15: avoids silently ignoring user config)
|
|
375
|
+
return "standard";
|
|
191
376
|
}
|
|
192
377
|
/** Check if a model ID has a known capability tier mapping. (#2192) */
|
|
193
378
|
function isKnownModel(modelId) {
|
|
@@ -200,37 +385,6 @@ function isKnownModel(modelId) {
|
|
|
200
385
|
}
|
|
201
386
|
return false;
|
|
202
387
|
}
|
|
203
|
-
function findModelForTier(tier, config, availableModelIds, crossProvider) {
|
|
204
|
-
// 1. Check explicit tier_models config
|
|
205
|
-
const explicitModel = config.tier_models?.[tier];
|
|
206
|
-
if (explicitModel && availableModelIds.includes(explicitModel)) {
|
|
207
|
-
return explicitModel;
|
|
208
|
-
}
|
|
209
|
-
// Also check with provider prefix stripped
|
|
210
|
-
if (explicitModel) {
|
|
211
|
-
const match = availableModelIds.find(id => {
|
|
212
|
-
const bareAvail = id.includes("/") ? id.split("/").pop() : id;
|
|
213
|
-
const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop() : explicitModel;
|
|
214
|
-
return bareAvail === bareExplicit;
|
|
215
|
-
});
|
|
216
|
-
if (match)
|
|
217
|
-
return match;
|
|
218
|
-
}
|
|
219
|
-
// 2. Auto-detect: find the cheapest available model in the requested tier
|
|
220
|
-
const candidates = availableModelIds
|
|
221
|
-
.filter(id => {
|
|
222
|
-
const modelTier = getModelTier(id);
|
|
223
|
-
return modelTier === tier;
|
|
224
|
-
})
|
|
225
|
-
.sort((a, b) => {
|
|
226
|
-
if (!crossProvider)
|
|
227
|
-
return 0;
|
|
228
|
-
const costA = getModelCost(a);
|
|
229
|
-
const costB = getModelCost(b);
|
|
230
|
-
return costA - costB;
|
|
231
|
-
});
|
|
232
|
-
return candidates[0] ?? null;
|
|
233
|
-
}
|
|
234
388
|
function getModelCost(modelId) {
|
|
235
389
|
const bareId = modelId.includes("/") ? modelId.split("/").pop() : modelId;
|
|
236
390
|
if (MODEL_COST_PER_1K_INPUT[bareId] !== undefined) {
|
|
@@ -13,6 +13,7 @@ import { mergeMilestoneToMain } from "./auto-worktree.js";
|
|
|
13
13
|
import { MergeConflictError } from "./git-service.js";
|
|
14
14
|
import { removeSessionStatus } from "./session-status-io.js";
|
|
15
15
|
import { getErrorMessage } from "./error-utils.js";
|
|
16
|
+
import { logWarning } from "./workflow-logger.js";
|
|
16
17
|
// ─── Merge Queue ───────────────────────────────────────────────────────────
|
|
17
18
|
/**
|
|
18
19
|
* Check whether a milestone is complete by querying its worktree SQLite DB.
|
|
@@ -27,7 +28,8 @@ export function isMilestoneCompleteInWorktreeDb(basePath, mid) {
|
|
|
27
28
|
const result = spawnSync("sqlite3", [dbPath, `SELECT status FROM milestones WHERE id='${mid}' LIMIT 1`], { timeout: 3000, encoding: "utf-8" });
|
|
28
29
|
return (result.stdout || "").trim() === "complete";
|
|
29
30
|
}
|
|
30
|
-
catch {
|
|
31
|
+
catch (e) {
|
|
32
|
+
logWarning("parallel", `spawnSync milestone completion check failed for ${mid}: ${e.message}`);
|
|
31
33
|
return false;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
@@ -45,8 +47,8 @@ function discoverDbCompletedMilestones(basePath) {
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
|
-
catch {
|
|
49
|
-
|
|
50
|
+
catch (e) {
|
|
51
|
+
logWarning("parallel", `readdirSync for completed set failed: ${e.message}`);
|
|
50
52
|
}
|
|
51
53
|
return completed;
|
|
52
54
|
}
|
|
@@ -19,6 +19,7 @@ import { resolveParallelConfig } from "./preferences.js";
|
|
|
19
19
|
import { writeSessionStatus, readAllSessionStatuses, readSessionStatus, removeSessionStatus, sendSignal, cleanupStaleSessions, } from "./session-status-io.js";
|
|
20
20
|
import { analyzeParallelEligibility, } from "./parallel-eligibility.js";
|
|
21
21
|
import { getErrorMessage } from "./error-utils.js";
|
|
22
|
+
import { logWarning } from "./workflow-logger.js";
|
|
22
23
|
// ─── Module State ──────────────────────────────────────────────────────────
|
|
23
24
|
let state = null;
|
|
24
25
|
// ─── Persistence ──────────────────────────────────────────────────────────
|
|
@@ -61,7 +62,9 @@ export function persistState(basePath) {
|
|
|
61
62
|
writeFileSync(tmp, JSON.stringify(persisted, null, 2), "utf-8");
|
|
62
63
|
renameSync(tmp, dest);
|
|
63
64
|
}
|
|
64
|
-
catch {
|
|
65
|
+
catch (e) {
|
|
66
|
+
logWarning("parallel", `persist parallel state failed: ${e.message}`);
|
|
67
|
+
}
|
|
65
68
|
}
|
|
66
69
|
/**
|
|
67
70
|
* Remove the persisted state file.
|
|
@@ -72,7 +75,9 @@ function removeStateFile(basePath) {
|
|
|
72
75
|
if (existsSync(p))
|
|
73
76
|
unlinkSync(p);
|
|
74
77
|
}
|
|
75
|
-
catch {
|
|
78
|
+
catch (e) {
|
|
79
|
+
logWarning("parallel", `clear parallel state file failed: ${e.message}`);
|
|
80
|
+
}
|
|
76
81
|
}
|
|
77
82
|
function isPidAlive(pid) {
|
|
78
83
|
if (!Number.isInteger(pid) || pid <= 0)
|
|
@@ -81,7 +86,8 @@ function isPidAlive(pid) {
|
|
|
81
86
|
process.kill(pid, 0);
|
|
82
87
|
return true;
|
|
83
88
|
}
|
|
84
|
-
catch {
|
|
89
|
+
catch (e) {
|
|
90
|
+
logWarning("parallel", `pid alive check failed for pid ${pid}: ${e.message}`);
|
|
85
91
|
return false;
|
|
86
92
|
}
|
|
87
93
|
}
|
|
@@ -112,7 +118,8 @@ export function restoreState(basePath) {
|
|
|
112
118
|
}
|
|
113
119
|
return persisted;
|
|
114
120
|
}
|
|
115
|
-
catch {
|
|
121
|
+
catch (e) {
|
|
122
|
+
logWarning("parallel", `readParallelState JSON parse failed: ${e.message}`);
|
|
116
123
|
return null;
|
|
117
124
|
}
|
|
118
125
|
}
|
|
@@ -126,8 +133,8 @@ function appendWorkerLog(basePath, milestoneId, chunk) {
|
|
|
126
133
|
mkdirSync(dir, { recursive: true });
|
|
127
134
|
appendFileSync(workerLogPath(basePath, milestoneId), chunk, "utf-8");
|
|
128
135
|
}
|
|
129
|
-
catch {
|
|
130
|
-
|
|
136
|
+
catch (e) {
|
|
137
|
+
logWarning("parallel", `appendFileSync worker log failed for ${milestoneId}: ${e.message}`);
|
|
131
138
|
}
|
|
132
139
|
}
|
|
133
140
|
function restoreRuntimeState(basePath) {
|
|
@@ -331,9 +338,8 @@ export async function startParallel(basePath, milestoneIds, prefs) {
|
|
|
331
338
|
try {
|
|
332
339
|
wtPath = createMilestoneWorktree(basePath, mid);
|
|
333
340
|
}
|
|
334
|
-
catch {
|
|
335
|
-
|
|
336
|
-
// is not available. Fall back to a placeholder path.
|
|
341
|
+
catch (e) {
|
|
342
|
+
logWarning("parallel", `createMilestoneWorktree fallback for ${mid}: ${e.message}`);
|
|
337
343
|
wtPath = worktreePath(basePath, mid);
|
|
338
344
|
}
|
|
339
345
|
const worker = {
|
|
@@ -451,7 +457,8 @@ export function spawnWorker(basePath, milestoneId) {
|
|
|
451
457
|
detached: false,
|
|
452
458
|
});
|
|
453
459
|
}
|
|
454
|
-
catch {
|
|
460
|
+
catch (e) {
|
|
461
|
+
logWarning("parallel", `spawnSync worker failed for ${milestoneId}: ${e.message}`);
|
|
455
462
|
return false;
|
|
456
463
|
}
|
|
457
464
|
// Handle spawn errors (e.g., ENOENT when binary doesn't exist)
|
|
@@ -572,7 +579,8 @@ function resolveGsdBin() {
|
|
|
572
579
|
try {
|
|
573
580
|
thisDir = dirname(fileURLToPath(import.meta.url));
|
|
574
581
|
}
|
|
575
|
-
catch {
|
|
582
|
+
catch (e) {
|
|
583
|
+
logWarning("parallel", `dirname(fileURLToPath) failed: ${e.message}`);
|
|
576
584
|
thisDir = process.cwd();
|
|
577
585
|
}
|
|
578
586
|
const candidates = [
|
|
@@ -599,7 +607,7 @@ function processWorkerLine(basePath, milestoneId, line) {
|
|
|
599
607
|
event = JSON.parse(line);
|
|
600
608
|
}
|
|
601
609
|
catch {
|
|
602
|
-
return; //
|
|
610
|
+
return; // Non-NDJSON lines (progress text, tool output) are expected — silent drop
|
|
603
611
|
}
|
|
604
612
|
const type = String(event.type ?? "");
|
|
605
613
|
// message_end carries usage data with cost
|
|
@@ -684,7 +692,9 @@ export async function stopParallel(basePath, milestoneId) {
|
|
|
684
692
|
process.kill(worker.pid, "SIGTERM");
|
|
685
693
|
}
|
|
686
694
|
}
|
|
687
|
-
catch {
|
|
695
|
+
catch (e) {
|
|
696
|
+
logWarning("parallel", `process.kill SIGTERM failed for pid ${worker.pid}: ${e.message}`);
|
|
697
|
+
}
|
|
688
698
|
}
|
|
689
699
|
// Wait for the headless process to cascade SIGTERM to its RPC child.
|
|
690
700
|
// The headless signal handler calls client.stop() which sends SIGTERM
|
|
@@ -701,7 +711,9 @@ export async function stopParallel(basePath, milestoneId) {
|
|
|
701
711
|
process.kill(worker.pid, "SIGKILL");
|
|
702
712
|
}
|
|
703
713
|
}
|
|
704
|
-
catch {
|
|
714
|
+
catch (e) {
|
|
715
|
+
logWarning("parallel", `process.kill SIGKILL failed for pid ${worker.pid}: ${e.message}`);
|
|
716
|
+
}
|
|
705
717
|
await waitForWorkerExit(worker, 250);
|
|
706
718
|
}
|
|
707
719
|
// Remove stream listeners before releasing the process handle
|