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
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// parseRoadmap(), parsePlan(), parseSummary() in files.ts.
|
|
10
10
|
|
|
11
11
|
import { readFileSync, existsSync, mkdirSync } from "node:fs";
|
|
12
|
+
import { logWarning } from "./workflow-logger.js";
|
|
12
13
|
import { isClosedStatus } from "./status-guards.js";
|
|
13
14
|
import { join, relative } from "node:path";
|
|
14
15
|
import { createRequire } from "node:module";
|
|
@@ -93,9 +94,7 @@ function loadArtifactContent(
|
|
|
93
94
|
try {
|
|
94
95
|
content = readFileSync(absPath, "utf-8");
|
|
95
96
|
} catch {
|
|
96
|
-
|
|
97
|
-
`markdown-renderer: cannot read file from disk: ${absPath}\n`,
|
|
98
|
-
);
|
|
97
|
+
logWarning("renderer", `cannot read file from disk: ${absPath}`);
|
|
99
98
|
return null;
|
|
100
99
|
}
|
|
101
100
|
|
|
@@ -111,9 +110,7 @@ function loadArtifactContent(
|
|
|
111
110
|
});
|
|
112
111
|
} catch {
|
|
113
112
|
// Non-fatal: we have the content, DB storage is best-effort
|
|
114
|
-
|
|
115
|
-
`markdown-renderer: warning — failed to store disk fallback in DB: ${artifactPath}\n`,
|
|
116
|
-
);
|
|
113
|
+
logWarning("renderer", `failed to store disk fallback in DB: ${artifactPath}`);
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
return content;
|
|
@@ -146,9 +143,7 @@ async function writeAndStore(
|
|
|
146
143
|
});
|
|
147
144
|
} catch {
|
|
148
145
|
// Non-fatal: file is on disk, DB is best-effort
|
|
149
|
-
|
|
150
|
-
`markdown-renderer: warning — failed to update artifact in DB: ${artifactPath}\n`,
|
|
151
|
-
);
|
|
146
|
+
logWarning("renderer", `failed to update artifact in DB: ${artifactPath}`);
|
|
152
147
|
}
|
|
153
148
|
|
|
154
149
|
invalidateCaches();
|
|
@@ -806,7 +801,8 @@ export function detectStaleRenders(basePath: string): StaleEntry[] {
|
|
|
806
801
|
try {
|
|
807
802
|
const m = _require("./parsers-legacy.ts");
|
|
808
803
|
parseRoadmap = m.parseRoadmap; parsePlan = m.parsePlan;
|
|
809
|
-
} catch {
|
|
804
|
+
} catch (e) {
|
|
805
|
+
logWarning("renderer", `parsers-legacy.ts require failed, falling back to .js: ${(e as Error).message}`);
|
|
810
806
|
const m = _require("./parsers-legacy.js");
|
|
811
807
|
parseRoadmap = m.parseRoadmap; parsePlan = m.parsePlan;
|
|
812
808
|
}
|
|
@@ -841,8 +837,8 @@ export function detectStaleRenders(basePath: string): StaleEntry[] {
|
|
|
841
837
|
});
|
|
842
838
|
}
|
|
843
839
|
}
|
|
844
|
-
} catch {
|
|
845
|
-
|
|
840
|
+
} catch (e) {
|
|
841
|
+
logWarning("renderer", `roadmap parse failed: ${(e as Error).message}`);
|
|
846
842
|
}
|
|
847
843
|
}
|
|
848
844
|
|
|
@@ -874,8 +870,8 @@ export function detectStaleRenders(basePath: string): StaleEntry[] {
|
|
|
874
870
|
});
|
|
875
871
|
}
|
|
876
872
|
}
|
|
877
|
-
} catch {
|
|
878
|
-
|
|
873
|
+
} catch (e) {
|
|
874
|
+
logWarning("renderer", `plan parse failed: ${(e as Error).message}`);
|
|
879
875
|
}
|
|
880
876
|
}
|
|
881
877
|
|
|
@@ -1025,9 +1021,7 @@ export async function repairStaleRenders(basePath: string): Promise<number> {
|
|
|
1025
1021
|
}
|
|
1026
1022
|
}
|
|
1027
1023
|
} catch (err) {
|
|
1028
|
-
|
|
1029
|
-
`markdown-renderer: repair failed for ${entry.path}: ${(err as Error).message}\n`,
|
|
1030
|
-
);
|
|
1024
|
+
logWarning("renderer", `repair failed for ${entry.path}: ${(err as Error).message}`);
|
|
1031
1025
|
}
|
|
1032
1026
|
}
|
|
1033
1027
|
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
import { findMilestoneIds } from './guided-flow.js';
|
|
32
32
|
import { parseRoadmap, parsePlan } from './parsers-legacy.js';
|
|
33
33
|
import { parseContextDependsOn } from './files.js';
|
|
34
|
+
import { logWarning } from './workflow-logger.js';
|
|
34
35
|
|
|
35
36
|
// ─── DECISIONS.md Parser ───────────────────────────────────────────────────
|
|
36
37
|
|
|
@@ -712,25 +713,25 @@ export function migrateFromMarkdown(gsdDir: string): {
|
|
|
712
713
|
try {
|
|
713
714
|
decisions = importDecisions(gsdDir);
|
|
714
715
|
} catch (err) {
|
|
715
|
-
|
|
716
|
+
logWarning("migration", `skipping decisions import: ${(err as Error).message}`);
|
|
716
717
|
}
|
|
717
718
|
|
|
718
719
|
try {
|
|
719
720
|
requirements = importRequirements(gsdDir);
|
|
720
721
|
} catch (err) {
|
|
721
|
-
|
|
722
|
+
logWarning("migration", `skipping requirements import: ${(err as Error).message}`);
|
|
722
723
|
}
|
|
723
724
|
|
|
724
725
|
try {
|
|
725
726
|
artifacts = importHierarchyArtifacts(gsdDir);
|
|
726
727
|
} catch (err) {
|
|
727
|
-
|
|
728
|
+
logWarning("migration", `skipping artifacts import: ${(err as Error).message}`);
|
|
728
729
|
}
|
|
729
730
|
|
|
730
731
|
try {
|
|
731
732
|
hierarchy = migrateHierarchyToDb(gsdDir);
|
|
732
733
|
} catch (err) {
|
|
733
|
-
|
|
734
|
+
logWarning("migration", `skipping hierarchy migration: ${(err as Error).message}`);
|
|
734
735
|
}
|
|
735
736
|
});
|
|
736
737
|
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
import { invalidateAllCaches } from "./cache.js";
|
|
22
22
|
import { loadQueueOrder, saveQueueOrder } from "./queue-order.js";
|
|
23
23
|
import { isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
|
|
24
|
+
import { logWarning } from "./workflow-logger.js";
|
|
24
25
|
|
|
25
26
|
// ─── Park ──────────────────────────────────────────────────────────────────
|
|
26
27
|
|
|
@@ -58,7 +59,7 @@ export function parkMilestone(basePath: string, milestoneId: string, reason: str
|
|
|
58
59
|
try {
|
|
59
60
|
updateMilestoneStatus(milestoneId, "parked");
|
|
60
61
|
} catch (err) {
|
|
61
|
-
|
|
62
|
+
logWarning("engine", `parkMilestone DB sync failed for ${milestoneId}: ${(err as Error).message}`);
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
invalidateAllCaches();
|
|
@@ -84,7 +85,7 @@ export function unparkMilestone(basePath: string, milestoneId: string): boolean
|
|
|
84
85
|
try {
|
|
85
86
|
updateMilestoneStatus(milestoneId, "active");
|
|
86
87
|
} catch (err) {
|
|
87
|
-
|
|
88
|
+
logWarning("engine", `unparkMilestone DB sync failed for ${milestoneId}: ${(err as Error).message}`);
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
invalidateAllCaches();
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { randomInt } from "node:crypto";
|
|
9
|
+
import { logWarning } from "./workflow-logger.js";
|
|
9
10
|
import { readdirSync, existsSync } from "node:fs";
|
|
10
11
|
import { milestonesDir } from "./paths.js";
|
|
11
12
|
import { loadQueueOrder, sortByQueueOrder } from "./queue-order.js";
|
|
@@ -128,7 +129,7 @@ export function findMilestoneIds(basePath: string): string[] {
|
|
|
128
129
|
} catch (err) {
|
|
129
130
|
// Log why milestone scanning failed — silent [] here causes infinite loops (#456)
|
|
130
131
|
if (existsSync(dir)) {
|
|
131
|
-
|
|
132
|
+
logWarning("engine", `findMilestoneIds: .gsd/milestones/ exists but readdirSync failed — ${getErrorMessage(err)}`);
|
|
132
133
|
}
|
|
133
134
|
return [];
|
|
134
135
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Maps complexity tiers to models, enforcing downgrade-only semantics.
|
|
3
3
|
// The user's configured model is always the ceiling.
|
|
4
4
|
|
|
5
|
-
import type { ComplexityTier, ClassificationResult } from "./complexity-classifier.js";
|
|
5
|
+
import type { ComplexityTier, ClassificationResult, TaskMetadata } from "./complexity-classifier.js";
|
|
6
6
|
import { tierOrdinal } from "./complexity-classifier.js";
|
|
7
7
|
import type { ResolvedModelConfig } from "./preferences.js";
|
|
8
8
|
|
|
@@ -10,6 +10,7 @@ import type { ResolvedModelConfig } from "./preferences.js";
|
|
|
10
10
|
|
|
11
11
|
export interface DynamicRoutingConfig {
|
|
12
12
|
enabled?: boolean;
|
|
13
|
+
capability_routing?: boolean; // default: false — enable capability profile scoring
|
|
13
14
|
tier_models?: {
|
|
14
15
|
light?: string;
|
|
15
16
|
standard?: string;
|
|
@@ -32,6 +33,25 @@ export interface RoutingDecision {
|
|
|
32
33
|
wasDowngraded: boolean;
|
|
33
34
|
/** Human-readable reason for this decision */
|
|
34
35
|
reason: string;
|
|
36
|
+
/** How the model was selected */
|
|
37
|
+
selectionMethod: "tier-only" | "capability-scored";
|
|
38
|
+
/** Capability scores per eligible model (capability-scored path only) */
|
|
39
|
+
capabilityScores?: Record<string, number>;
|
|
40
|
+
/** Task requirement vector used for scoring */
|
|
41
|
+
taskRequirements?: Partial<Record<string, number>>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ─── Capability Profiles ─────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
/** Seven-dimension capability profile for a model. All values in 0–100 range. */
|
|
47
|
+
export interface ModelCapabilities {
|
|
48
|
+
coding: number;
|
|
49
|
+
debugging: number;
|
|
50
|
+
research: number;
|
|
51
|
+
reasoning: number;
|
|
52
|
+
speed: number;
|
|
53
|
+
longContext: number;
|
|
54
|
+
instruction: number;
|
|
35
55
|
}
|
|
36
56
|
|
|
37
57
|
// ─── Known Model Tiers ───────────────────────────────────────────────────────
|
|
@@ -114,24 +134,207 @@ const MODEL_COST_PER_1K_INPUT: Record<string, number> = {
|
|
|
114
134
|
"deepseek-chat": 0.00014,
|
|
115
135
|
};
|
|
116
136
|
|
|
137
|
+
// ─── Capability Profiles Data Table ──────────────────────────────────────────
|
|
138
|
+
// Per-model capability profiles (0–100 scale). Used for capability-aware
|
|
139
|
+
// model selection within an eligible tier set.
|
|
140
|
+
|
|
141
|
+
export const MODEL_CAPABILITY_PROFILES: Record<string, ModelCapabilities> = {
|
|
142
|
+
"claude-opus-4-6": { coding: 95, debugging: 90, research: 85, reasoning: 95, speed: 30, longContext: 80, instruction: 90 },
|
|
143
|
+
"claude-sonnet-4-6": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
|
|
144
|
+
"claude-haiku-4-5": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
|
|
145
|
+
"gpt-4o": { coding: 80, debugging: 75, research: 70, reasoning: 75, speed: 65, longContext: 70, instruction: 80 },
|
|
146
|
+
"gpt-4o-mini": { coding: 55, debugging: 45, research: 40, reasoning: 45, speed: 90, longContext: 45, instruction: 70 },
|
|
147
|
+
"gemini-2.5-pro": { coding: 75, debugging: 70, research: 85, reasoning: 75, speed: 55, longContext: 90, instruction: 75 },
|
|
148
|
+
"gemini-2.0-flash": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
|
|
149
|
+
"deepseek-chat": { coding: 75, debugging: 65, research: 55, reasoning: 70, speed: 70, longContext: 55, instruction: 65 },
|
|
150
|
+
"o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// ─── Base Task Requirements Data Table ───────────────────────────────────────
|
|
154
|
+
// Per-unit-type base requirement vectors. Weights indicate how important each
|
|
155
|
+
// capability dimension is for this unit type.
|
|
156
|
+
|
|
157
|
+
export const BASE_REQUIREMENTS: Record<string, Partial<Record<keyof ModelCapabilities, number>>> = {
|
|
158
|
+
"execute-task": { coding: 0.9, instruction: 0.7, speed: 0.3 },
|
|
159
|
+
"research-milestone": { research: 0.9, longContext: 0.7, reasoning: 0.5 },
|
|
160
|
+
"research-slice": { research: 0.9, longContext: 0.7, reasoning: 0.5 },
|
|
161
|
+
"plan-milestone": { reasoning: 0.9, coding: 0.5 },
|
|
162
|
+
"plan-slice": { reasoning: 0.9, coding: 0.5 },
|
|
163
|
+
"replan-slice": { reasoning: 0.9, debugging: 0.6, coding: 0.5 },
|
|
164
|
+
"reassess-roadmap": { reasoning: 0.9, research: 0.5 },
|
|
165
|
+
"complete-slice": { instruction: 0.8, speed: 0.7 },
|
|
166
|
+
"run-uat": { instruction: 0.7, speed: 0.8 },
|
|
167
|
+
"discuss-milestone": { reasoning: 0.6, instruction: 0.7 },
|
|
168
|
+
"complete-milestone": { instruction: 0.8, reasoning: 0.5 },
|
|
169
|
+
};
|
|
170
|
+
|
|
117
171
|
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
118
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Score a model's suitability for a task given a requirement vector.
|
|
175
|
+
* Returns a weighted average of capability dimensions (0–100).
|
|
176
|
+
* Returns 50 if requirements are empty (neutral score).
|
|
177
|
+
*/
|
|
178
|
+
export function scoreModel(
|
|
179
|
+
model: ModelCapabilities,
|
|
180
|
+
requirements: Partial<Record<keyof ModelCapabilities, number>>,
|
|
181
|
+
): number {
|
|
182
|
+
let weightedSum = 0;
|
|
183
|
+
let weightSum = 0;
|
|
184
|
+
for (const [dim, weight] of Object.entries(requirements)) {
|
|
185
|
+
const capability = model[dim as keyof ModelCapabilities] ?? 50;
|
|
186
|
+
weightedSum += weight * capability;
|
|
187
|
+
weightSum += weight;
|
|
188
|
+
}
|
|
189
|
+
return weightSum > 0 ? weightedSum / weightSum : 50;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Compute dynamic task requirements from unit type and optional task metadata.
|
|
194
|
+
* Returns a requirement vector refined by task-specific signals.
|
|
195
|
+
*/
|
|
196
|
+
export function computeTaskRequirements(
|
|
197
|
+
unitType: string,
|
|
198
|
+
metadata?: TaskMetadata,
|
|
199
|
+
): Partial<Record<keyof ModelCapabilities, number>> {
|
|
200
|
+
const base = BASE_REQUIREMENTS[unitType] ?? { reasoning: 0.5 };
|
|
201
|
+
if (unitType === "execute-task" && metadata) {
|
|
202
|
+
if (metadata.tags?.some(t => /^(docs?|readme|comment|config|typo|rename)$/i.test(t))) {
|
|
203
|
+
return { ...base, instruction: 0.9, coding: 0.3, speed: 0.7 };
|
|
204
|
+
}
|
|
205
|
+
if (metadata.complexityKeywords?.some(k => k === "concurrency" || k === "compatibility")) {
|
|
206
|
+
return { ...base, debugging: 0.9, reasoning: 0.8 };
|
|
207
|
+
}
|
|
208
|
+
if (metadata.complexityKeywords?.some(k => k === "migration" || k === "architecture")) {
|
|
209
|
+
return { ...base, reasoning: 0.9, coding: 0.8 };
|
|
210
|
+
}
|
|
211
|
+
if ((metadata.fileCount ?? 0) >= 6 || (metadata.estimatedLines ?? 0) >= 500) {
|
|
212
|
+
return { ...base, coding: 0.9, reasoning: 0.7 };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return base;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Score all eligible models against a requirement vector and return them
|
|
220
|
+
* sorted by score descending. Within 2 points: prefer cheaper; equal cost:
|
|
221
|
+
* lexicographic tie-break by model ID.
|
|
222
|
+
*/
|
|
223
|
+
export function scoreEligibleModels(
|
|
224
|
+
eligibleModelIds: string[],
|
|
225
|
+
requirements: Partial<Record<keyof ModelCapabilities, number>>,
|
|
226
|
+
capabilityOverrides?: Record<string, Partial<ModelCapabilities>>,
|
|
227
|
+
): Array<{ modelId: string; score: number }> {
|
|
228
|
+
const scored = eligibleModelIds.map(modelId => {
|
|
229
|
+
const builtin = MODEL_CAPABILITY_PROFILES[modelId];
|
|
230
|
+
const override = capabilityOverrides?.[modelId];
|
|
231
|
+
const profile: ModelCapabilities = builtin
|
|
232
|
+
? override ? { ...builtin, ...override } : builtin
|
|
233
|
+
: { coding: 50, debugging: 50, research: 50, reasoning: 50, speed: 50, longContext: 50, instruction: 50 };
|
|
234
|
+
return { modelId, score: scoreModel(profile, requirements) };
|
|
235
|
+
});
|
|
236
|
+
scored.sort((a, b) => {
|
|
237
|
+
const scoreDiff = b.score - a.score;
|
|
238
|
+
if (Math.abs(scoreDiff) > 2) return scoreDiff;
|
|
239
|
+
const costA = MODEL_COST_PER_1K_INPUT[a.modelId] ?? Infinity;
|
|
240
|
+
const costB = MODEL_COST_PER_1K_INPUT[b.modelId] ?? Infinity;
|
|
241
|
+
if (costA !== costB) return costA - costB;
|
|
242
|
+
return a.modelId.localeCompare(b.modelId);
|
|
243
|
+
});
|
|
244
|
+
return scored;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Return all models eligible for a given tier, sorted cheapest first.
|
|
249
|
+
* If routingConfig.tier_models[tier] is set and available, returns only that
|
|
250
|
+
* model. Otherwise filters availableModelIds by tier from MODEL_CAPABILITY_TIER.
|
|
251
|
+
*/
|
|
252
|
+
export function getEligibleModels(
|
|
253
|
+
tier: ComplexityTier,
|
|
254
|
+
availableModelIds: string[],
|
|
255
|
+
routingConfig: DynamicRoutingConfig,
|
|
256
|
+
): string[] {
|
|
257
|
+
// 1. Check explicit tier_models config
|
|
258
|
+
const explicitModel = routingConfig.tier_models?.[tier];
|
|
259
|
+
if (explicitModel) {
|
|
260
|
+
// Exact match
|
|
261
|
+
if (availableModelIds.includes(explicitModel)) return [explicitModel];
|
|
262
|
+
// Provider-prefix-stripped match
|
|
263
|
+
const match = availableModelIds.find(id => {
|
|
264
|
+
const bareAvail = id.includes("/") ? id.split("/").pop()! : id;
|
|
265
|
+
const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop()! : explicitModel;
|
|
266
|
+
return bareAvail === bareExplicit;
|
|
267
|
+
});
|
|
268
|
+
if (match) return [match];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 2. Auto-detect: filter by tier, sort cheapest first
|
|
272
|
+
return availableModelIds
|
|
273
|
+
.filter(id => getModelTier(id) === tier)
|
|
274
|
+
.sort((a, b) => {
|
|
275
|
+
const costA = getModelCost(a);
|
|
276
|
+
const costB = getModelCost(b);
|
|
277
|
+
return costA - costB;
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Build a fallback chain for a selected model: [selectedModel, ...configuredFallbacks, configuredPrimary]
|
|
283
|
+
* Deduplicates entries while preserving order.
|
|
284
|
+
*/
|
|
285
|
+
function buildFallbackChain(selectedModelId: string, phaseConfig: ResolvedModelConfig): string[] {
|
|
286
|
+
return [
|
|
287
|
+
...phaseConfig.fallbacks.filter(f => f !== selectedModelId),
|
|
288
|
+
phaseConfig.primary,
|
|
289
|
+
].filter(f => f !== selectedModelId);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Load capability overrides from user preferences' modelOverrides section.
|
|
294
|
+
* Returns a map of model ID → partial capability overrides to deep-merge with built-in profiles.
|
|
295
|
+
*
|
|
296
|
+
* Per D-17: partial capability overrides via models.json modelOverrides, deep-merged with defaults.
|
|
297
|
+
*/
|
|
298
|
+
export function loadCapabilityOverrides(
|
|
299
|
+
prefs: { modelOverrides?: Record<string, { capabilities?: Partial<ModelCapabilities> }> },
|
|
300
|
+
): Record<string, Partial<ModelCapabilities>> {
|
|
301
|
+
const result: Record<string, Partial<ModelCapabilities>> = {};
|
|
302
|
+
if (!prefs.modelOverrides) return result;
|
|
303
|
+
for (const [modelId, overrideEntry] of Object.entries(prefs.modelOverrides)) {
|
|
304
|
+
if (overrideEntry.capabilities) {
|
|
305
|
+
result[modelId] = overrideEntry.capabilities;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
|
|
119
311
|
/**
|
|
120
312
|
* Resolve the model to use for a given complexity tier.
|
|
121
313
|
*
|
|
122
314
|
* Downgrade-only: the returned model is always equal to or cheaper than
|
|
123
315
|
* the user's configured primary model. Never upgrades beyond configuration.
|
|
124
316
|
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
317
|
+
* STEP 1: Filter to eligible models for the requested tier.
|
|
318
|
+
* STEP 2: Capability scoring — ranks eligible models by task-capability match
|
|
319
|
+
* when capability_routing is enabled and multiple eligible models exist.
|
|
320
|
+
* STEP 3: Fallback chain assembly.
|
|
321
|
+
*
|
|
322
|
+
* @param classification The complexity classification result
|
|
323
|
+
* @param phaseConfig The user's configured model for this phase (ceiling)
|
|
324
|
+
* @param routingConfig Dynamic routing configuration
|
|
325
|
+
* @param availableModelIds List of available model IDs (from registry)
|
|
326
|
+
* @param unitType The unit type for capability requirement computation (optional)
|
|
327
|
+
* @param taskMetadata Task metadata for refined requirement vectors (optional)
|
|
328
|
+
* @param capabilityOverrides User-provided capability overrides (deep-merged with built-in profiles, optional)
|
|
129
329
|
*/
|
|
130
330
|
export function resolveModelForComplexity(
|
|
131
331
|
classification: ClassificationResult,
|
|
132
332
|
phaseConfig: ResolvedModelConfig | undefined,
|
|
133
333
|
routingConfig: DynamicRoutingConfig,
|
|
134
334
|
availableModelIds: string[],
|
|
335
|
+
unitType?: string,
|
|
336
|
+
taskMetadata?: TaskMetadata,
|
|
337
|
+
capabilityOverrides?: Record<string, Partial<ModelCapabilities>>,
|
|
135
338
|
): RoutingDecision {
|
|
136
339
|
// If no phase config or routing disabled, pass through
|
|
137
340
|
if (!phaseConfig || !routingConfig.enabled) {
|
|
@@ -141,6 +344,7 @@ export function resolveModelForComplexity(
|
|
|
141
344
|
tier: classification.tier,
|
|
142
345
|
wasDowngraded: false,
|
|
143
346
|
reason: "dynamic routing disabled or no phase config",
|
|
347
|
+
selectionMethod: "tier-only",
|
|
144
348
|
};
|
|
145
349
|
}
|
|
146
350
|
|
|
@@ -160,6 +364,7 @@ export function resolveModelForComplexity(
|
|
|
160
364
|
tier: requestedTier,
|
|
161
365
|
wasDowngraded: false,
|
|
162
366
|
reason: `configured model "${configuredPrimary}" is not in the known tier map — honoring explicit config`,
|
|
367
|
+
selectionMethod: "tier-only",
|
|
163
368
|
};
|
|
164
369
|
}
|
|
165
370
|
|
|
@@ -171,18 +376,14 @@ export function resolveModelForComplexity(
|
|
|
171
376
|
tier: requestedTier,
|
|
172
377
|
wasDowngraded: false,
|
|
173
378
|
reason: `tier ${requestedTier} >= configured ${configuredTier}`,
|
|
379
|
+
selectionMethod: "tier-only",
|
|
174
380
|
};
|
|
175
381
|
}
|
|
176
382
|
|
|
177
|
-
//
|
|
178
|
-
const
|
|
179
|
-
requestedTier,
|
|
180
|
-
routingConfig,
|
|
181
|
-
availableModelIds,
|
|
182
|
-
routingConfig.cross_provider !== false,
|
|
183
|
-
);
|
|
383
|
+
// STEP 1: Get all eligible models for the requested tier
|
|
384
|
+
const eligible = getEligibleModels(requestedTier, availableModelIds, routingConfig);
|
|
184
385
|
|
|
185
|
-
if (
|
|
386
|
+
if (eligible.length === 0) {
|
|
186
387
|
// No suitable model found — use configured primary
|
|
187
388
|
return {
|
|
188
389
|
modelId: configuredPrimary,
|
|
@@ -190,14 +391,37 @@ export function resolveModelForComplexity(
|
|
|
190
391
|
tier: requestedTier,
|
|
191
392
|
wasDowngraded: false,
|
|
192
393
|
reason: `no ${requestedTier}-tier model available`,
|
|
394
|
+
selectionMethod: "tier-only",
|
|
193
395
|
};
|
|
194
396
|
}
|
|
195
397
|
|
|
398
|
+
// STEP 2: Capability scoring (when enabled and multiple eligible models exist)
|
|
399
|
+
if (routingConfig.capability_routing !== false && eligible.length > 1 && unitType) {
|
|
400
|
+
const requirements = computeTaskRequirements(unitType, taskMetadata);
|
|
401
|
+
const scored = scoreEligibleModels(eligible, requirements, capabilityOverrides);
|
|
402
|
+
const winner = scored[0];
|
|
403
|
+
if (winner) {
|
|
404
|
+
const capScores: Record<string, number> = {};
|
|
405
|
+
for (const s of scored) capScores[s.modelId] = s.score;
|
|
406
|
+
const fallbacks = buildFallbackChain(winner.modelId, phaseConfig);
|
|
407
|
+
return {
|
|
408
|
+
modelId: winner.modelId,
|
|
409
|
+
fallbacks,
|
|
410
|
+
tier: requestedTier,
|
|
411
|
+
wasDowngraded: true,
|
|
412
|
+
reason: `capability-scored: ${winner.modelId} (${winner.score.toFixed(1)}) for ${unitType}`,
|
|
413
|
+
capabilityScores: capScores,
|
|
414
|
+
taskRequirements: requirements,
|
|
415
|
+
selectionMethod: "capability-scored",
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// STEP 3: Fallback — use first eligible model (cheapest in tier, or single eligible)
|
|
421
|
+
const targetModelId = eligible[0];
|
|
422
|
+
|
|
196
423
|
// Build fallback chain: [downgraded_model, ...configured_fallbacks, configured_primary]
|
|
197
|
-
const fallbacks =
|
|
198
|
-
...phaseConfig.fallbacks.filter(f => f !== targetModelId),
|
|
199
|
-
configuredPrimary,
|
|
200
|
-
].filter(f => f !== targetModelId);
|
|
424
|
+
const fallbacks = buildFallbackChain(targetModelId, phaseConfig);
|
|
201
425
|
|
|
202
426
|
return {
|
|
203
427
|
modelId: targetModelId,
|
|
@@ -205,6 +429,7 @@ export function resolveModelForComplexity(
|
|
|
205
429
|
tier: requestedTier,
|
|
206
430
|
wasDowngraded: true,
|
|
207
431
|
reason: classification.reason,
|
|
432
|
+
selectionMethod: "tier-only",
|
|
208
433
|
};
|
|
209
434
|
}
|
|
210
435
|
|
|
@@ -226,6 +451,7 @@ export function escalateTier(currentTier: ComplexityTier): ComplexityTier | null
|
|
|
226
451
|
export function defaultRoutingConfig(): DynamicRoutingConfig {
|
|
227
452
|
return {
|
|
228
453
|
enabled: true,
|
|
454
|
+
capability_routing: true,
|
|
229
455
|
escalate_on_failure: true,
|
|
230
456
|
budget_pressure: true,
|
|
231
457
|
cross_provider: true,
|
|
@@ -247,8 +473,8 @@ function getModelTier(modelId: string): ComplexityTier {
|
|
|
247
473
|
if (bareId.includes(knownId) || knownId.includes(bareId)) return tier;
|
|
248
474
|
}
|
|
249
475
|
|
|
250
|
-
// Unknown models are assumed
|
|
251
|
-
return "
|
|
476
|
+
// Unknown models are assumed standard (per D-15: avoids silently ignoring user config)
|
|
477
|
+
return "standard";
|
|
252
478
|
}
|
|
253
479
|
|
|
254
480
|
/** Check if a model ID has a known capability tier mapping. (#2192) */
|
|
@@ -261,43 +487,6 @@ function isKnownModel(modelId: string): boolean {
|
|
|
261
487
|
return false;
|
|
262
488
|
}
|
|
263
489
|
|
|
264
|
-
function findModelForTier(
|
|
265
|
-
tier: ComplexityTier,
|
|
266
|
-
config: DynamicRoutingConfig,
|
|
267
|
-
availableModelIds: string[],
|
|
268
|
-
crossProvider: boolean,
|
|
269
|
-
): string | null {
|
|
270
|
-
// 1. Check explicit tier_models config
|
|
271
|
-
const explicitModel = config.tier_models?.[tier];
|
|
272
|
-
if (explicitModel && availableModelIds.includes(explicitModel)) {
|
|
273
|
-
return explicitModel;
|
|
274
|
-
}
|
|
275
|
-
// Also check with provider prefix stripped
|
|
276
|
-
if (explicitModel) {
|
|
277
|
-
const match = availableModelIds.find(id => {
|
|
278
|
-
const bareAvail = id.includes("/") ? id.split("/").pop()! : id;
|
|
279
|
-
const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop()! : explicitModel;
|
|
280
|
-
return bareAvail === bareExplicit;
|
|
281
|
-
});
|
|
282
|
-
if (match) return match;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// 2. Auto-detect: find the cheapest available model in the requested tier
|
|
286
|
-
const candidates = availableModelIds
|
|
287
|
-
.filter(id => {
|
|
288
|
-
const modelTier = getModelTier(id);
|
|
289
|
-
return modelTier === tier;
|
|
290
|
-
})
|
|
291
|
-
.sort((a, b) => {
|
|
292
|
-
if (!crossProvider) return 0;
|
|
293
|
-
const costA = getModelCost(a);
|
|
294
|
-
const costB = getModelCost(b);
|
|
295
|
-
return costA - costB;
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
return candidates[0] ?? null;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
490
|
function getModelCost(modelId: string): number {
|
|
302
491
|
const bareId = modelId.includes("/") ? modelId.split("/").pop()! : modelId;
|
|
303
492
|
|
|
@@ -15,6 +15,7 @@ import { MergeConflictError } from "./git-service.js";
|
|
|
15
15
|
import { removeSessionStatus } from "./session-status-io.js";
|
|
16
16
|
import type { WorkerInfo } from "./parallel-orchestrator.js";
|
|
17
17
|
import { getErrorMessage } from "./error-utils.js";
|
|
18
|
+
import { logWarning } from "./workflow-logger.js";
|
|
18
19
|
|
|
19
20
|
// ─── Types ─────────────────────────────────────────────────────────────────
|
|
20
21
|
|
|
@@ -47,7 +48,8 @@ export function isMilestoneCompleteInWorktreeDb(basePath: string, mid: string):
|
|
|
47
48
|
{ timeout: 3000, encoding: "utf-8" },
|
|
48
49
|
);
|
|
49
50
|
return (result.stdout || "").trim() === "complete";
|
|
50
|
-
} catch {
|
|
51
|
+
} catch (e) {
|
|
52
|
+
logWarning("parallel", `spawnSync milestone completion check failed for ${mid}: ${(e as Error).message}`);
|
|
51
53
|
return false;
|
|
52
54
|
}
|
|
53
55
|
}
|
|
@@ -65,8 +67,8 @@ function discoverDbCompletedMilestones(basePath: string): Set<string> {
|
|
|
65
67
|
completed.add(entry);
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
|
-
} catch {
|
|
69
|
-
|
|
70
|
+
} catch (e) {
|
|
71
|
+
logWarning("parallel", `readdirSync for completed set failed: ${(e as Error).message}`);
|
|
70
72
|
}
|
|
71
73
|
return completed;
|
|
72
74
|
}
|