gsd-pi 2.60.0 → 2.61.0-dev.7aed0bf
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 +15 -7
- 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 +58 -9
- package/dist/resources/extensions/gsd/auto-post-unit.js +3 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +36 -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 +2 -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 +7 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -10
- 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 +8 -4
- package/dist/resources/extensions/gsd/custom-verification.js +3 -2
- package/dist/resources/extensions/gsd/gsd-db.js +33 -13
- 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 +156 -121
- 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/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +45 -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/rethink.md +1 -1
- 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/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 +11 -4
- 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/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/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/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +7 -3
- package/src/resources/extensions/gsd/auto/phases.ts +17 -7
- 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 -15
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +37 -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 +2 -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 +8 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -10
- 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 +9 -4
- package/src/resources/extensions/gsd/custom-verification.ts +3 -2
- package/src/resources/extensions/gsd/gsd-db.ts +12 -14
- 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 +199 -173
- 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/preferences-types.ts +13 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +45 -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/rethink.md +1 -1
- 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/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/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 +403 -3
- 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/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 +11 -4
- 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/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/{qdTGLxuqgx_Md-LpUsbMx → b7FOoMHaUb3FPoLNbxar4}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{qdTGLxuqgx_Md-LpUsbMx → b7FOoMHaUb3FPoLNbxar4}/_ssgManifest.js +0 -0
|
@@ -103,6 +103,7 @@ export const KNOWN_PREFERENCE_KEYS = new Set<string>([
|
|
|
103
103
|
"stale_commit_threshold_minutes",
|
|
104
104
|
"context_management",
|
|
105
105
|
"experimental",
|
|
106
|
+
"codebase",
|
|
106
107
|
]);
|
|
107
108
|
|
|
108
109
|
/** Canonical list of all dispatch unit types. */
|
|
@@ -211,6 +212,16 @@ export interface ExperimentalPreferences {
|
|
|
211
212
|
rtk?: boolean;
|
|
212
213
|
}
|
|
213
214
|
|
|
215
|
+
/** Configuration for the codebase map generator (/gsd codebase). */
|
|
216
|
+
export interface CodebaseMapPreferences {
|
|
217
|
+
/** Additional directory/file patterns to exclude (e.g. ["docs/", "fixtures/"]). Merged with built-in defaults. */
|
|
218
|
+
exclude_patterns?: string[];
|
|
219
|
+
/** Max files to include in the map. Default: 500. */
|
|
220
|
+
max_files?: number;
|
|
221
|
+
/** Files-per-directory threshold before collapsing to a summary line. Default: 20. */
|
|
222
|
+
collapse_threshold?: number;
|
|
223
|
+
}
|
|
224
|
+
|
|
214
225
|
export interface GSDPreferences {
|
|
215
226
|
version?: number;
|
|
216
227
|
mode?: WorkflowMode;
|
|
@@ -275,6 +286,8 @@ export interface GSDPreferences {
|
|
|
275
286
|
* See the preferences reference for details on each feature.
|
|
276
287
|
*/
|
|
277
288
|
experimental?: ExperimentalPreferences;
|
|
289
|
+
/** Configuration for the codebase map generator (/gsd codebase). */
|
|
290
|
+
codebase?: CodebaseMapPreferences;
|
|
278
291
|
}
|
|
279
292
|
|
|
280
293
|
export interface LoadedGSDPreferences {
|
|
@@ -857,5 +857,50 @@ export function validatePreferences(preferences: GSDPreferences): {
|
|
|
857
857
|
}
|
|
858
858
|
}
|
|
859
859
|
|
|
860
|
+
// ─── Codebase Map ──────────────────────────────────────────────────
|
|
861
|
+
if (preferences.codebase !== undefined) {
|
|
862
|
+
if (typeof preferences.codebase === "object" && preferences.codebase !== null) {
|
|
863
|
+
const cb = preferences.codebase as Record<string, unknown>;
|
|
864
|
+
const validCb: import("./preferences-types.js").CodebaseMapPreferences = {};
|
|
865
|
+
|
|
866
|
+
if (cb.exclude_patterns !== undefined) {
|
|
867
|
+
if (Array.isArray(cb.exclude_patterns) && cb.exclude_patterns.every((p: unknown) => typeof p === "string")) {
|
|
868
|
+
validCb.exclude_patterns = cb.exclude_patterns as string[];
|
|
869
|
+
} else {
|
|
870
|
+
errors.push("codebase.exclude_patterns must be an array of strings");
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (cb.max_files !== undefined) {
|
|
874
|
+
const mf = typeof cb.max_files === "number" ? cb.max_files : Number(cb.max_files);
|
|
875
|
+
if (Number.isFinite(mf) && mf >= 1) {
|
|
876
|
+
validCb.max_files = Math.floor(mf);
|
|
877
|
+
} else {
|
|
878
|
+
errors.push("codebase.max_files must be a positive integer");
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
if (cb.collapse_threshold !== undefined) {
|
|
882
|
+
const ct = typeof cb.collapse_threshold === "number" ? cb.collapse_threshold : Number(cb.collapse_threshold);
|
|
883
|
+
if (Number.isFinite(ct) && ct >= 1) {
|
|
884
|
+
validCb.collapse_threshold = Math.floor(ct);
|
|
885
|
+
} else {
|
|
886
|
+
errors.push("codebase.collapse_threshold must be a positive integer");
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
const knownCbKeys = new Set(["exclude_patterns", "max_files", "collapse_threshold"]);
|
|
891
|
+
for (const key of Object.keys(cb)) {
|
|
892
|
+
if (!knownCbKeys.has(key)) {
|
|
893
|
+
warnings.push(`unknown codebase key "${key}" — ignored`);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (Object.keys(validCb).length > 0) {
|
|
898
|
+
validated.codebase = validCb;
|
|
899
|
+
}
|
|
900
|
+
} else {
|
|
901
|
+
errors.push("codebase must be an object");
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
860
905
|
return { preferences: validated, errors, warnings };
|
|
861
906
|
}
|
|
@@ -19,6 +19,7 @@ import { parse as parseYaml } from "yaml";
|
|
|
19
19
|
import type { PostUnitHookConfig, PreDispatchHookConfig, TokenProfile } from "./types.js";
|
|
20
20
|
import type { DynamicRoutingConfig } from "./model-router.js";
|
|
21
21
|
import { normalizeStringArray } from "../shared/format-utils.js";
|
|
22
|
+
import { logWarning } from "./workflow-logger.js";
|
|
22
23
|
import { resolveProfileDefaults as _resolveProfileDefaults } from "./preferences-models.js";
|
|
23
24
|
|
|
24
25
|
import {
|
|
@@ -48,6 +49,7 @@ export type {
|
|
|
48
49
|
AutoSupervisorConfig,
|
|
49
50
|
RemoteQuestionsConfig,
|
|
50
51
|
CmuxPreferences,
|
|
52
|
+
CodebaseMapPreferences,
|
|
51
53
|
GSDPreferences,
|
|
52
54
|
LoadedGSDPreferences,
|
|
53
55
|
SkillResolution,
|
|
@@ -237,7 +239,7 @@ function parseFrontmatterBlock(frontmatter: string): GSDPreferences {
|
|
|
237
239
|
}
|
|
238
240
|
return parsed as GSDPreferences;
|
|
239
241
|
} catch (e) {
|
|
240
|
-
|
|
242
|
+
logWarning("guided", `YAML parse error in frontmatter block: ${(e as Error).message}`);
|
|
241
243
|
return {} as GSDPreferences;
|
|
242
244
|
}
|
|
243
245
|
}
|
|
@@ -296,8 +298,8 @@ function parseHeadingListFormat(content: string): GSDPreferences {
|
|
|
296
298
|
}
|
|
297
299
|
|
|
298
300
|
typed[targetSection] = value;
|
|
299
|
-
} catch {
|
|
300
|
-
|
|
301
|
+
} catch (e) {
|
|
302
|
+
logWarning("guided", `preferences section parse failed: ${(e as Error).message}`);
|
|
301
303
|
}
|
|
302
304
|
}
|
|
303
305
|
|
|
@@ -371,6 +373,17 @@ function mergePreferences(base: GSDPreferences, override: GSDPreferences): GSDPr
|
|
|
371
373
|
service_tier: override.service_tier ?? base.service_tier,
|
|
372
374
|
forensics_dedup: override.forensics_dedup ?? base.forensics_dedup,
|
|
373
375
|
show_token_cost: override.show_token_cost ?? base.show_token_cost,
|
|
376
|
+
codebase: (base.codebase || override.codebase)
|
|
377
|
+
? {
|
|
378
|
+
...(base.codebase ?? {}),
|
|
379
|
+
...(override.codebase ?? {}),
|
|
380
|
+
// Merge exclude_patterns arrays rather than overriding
|
|
381
|
+
exclude_patterns: [
|
|
382
|
+
...((base.codebase?.exclude_patterns) ?? []),
|
|
383
|
+
...((override.codebase?.exclude_patterns) ?? []),
|
|
384
|
+
].filter(Boolean),
|
|
385
|
+
}
|
|
386
|
+
: undefined,
|
|
374
387
|
};
|
|
375
388
|
}
|
|
376
389
|
|
|
@@ -22,6 +22,7 @@ import { GSDError, GSD_PARSE_ERROR } from "./errors.js";
|
|
|
22
22
|
import { join, dirname } from "node:path";
|
|
23
23
|
import { fileURLToPath } from "node:url";
|
|
24
24
|
import { homedir } from "node:os";
|
|
25
|
+
import { logWarning } from "./workflow-logger.js";
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Resolve the GSD extension directory.
|
|
@@ -72,7 +73,7 @@ function warmCache(): void {
|
|
|
72
73
|
// prompts/ may not exist in test environments — lazy loading still works.
|
|
73
74
|
// Emit a diagnostic when running outside tests so wrong-path bugs are visible.
|
|
74
75
|
if (!process.env.VITEST && !process.env.NODE_TEST) {
|
|
75
|
-
|
|
76
|
+
logWarning("prompt", `warmCache: prompts dir not found: ${promptsDir}`);
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
|
|
@@ -87,7 +88,7 @@ function warmCache(): void {
|
|
|
87
88
|
} catch {
|
|
88
89
|
// templates/ may not exist in test environments — lazy loading still works.
|
|
89
90
|
if (!process.env.VITEST && !process.env.NODE_TEST) {
|
|
90
|
-
|
|
91
|
+
logWarning("prompt", `warmCache: templates dir not found: ${templatesDir}`);
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
}
|
|
@@ -48,7 +48,7 @@ Remove the `{ID}-PARKED.md` file from the milestone directory to reactivate it.
|
|
|
48
48
|
### Skip a slice
|
|
49
49
|
Mark a slice as skipped so auto-mode advances past it without executing. Use the `gsd_skip_slice` tool:
|
|
50
50
|
```
|
|
51
|
-
gsd_skip_slice({
|
|
51
|
+
gsd_skip_slice({ milestoneId: "M003", sliceId: "S02", reason: "Descoped — feature moved to M005" })
|
|
52
52
|
```
|
|
53
53
|
Skipped slices are treated as closed by the state machine (like "complete" but distinct). Use when a slice is no longer needed or has been superseded. The slice data is preserved for reference.
|
|
54
54
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// A module-level singleton accessor allows existing code to migrate incrementally.
|
|
8
8
|
|
|
9
|
+
import { logWarning } from "./workflow-logger.js";
|
|
9
10
|
import type { UnifiedRule, RulePhase } from "./rule-types.js";
|
|
10
11
|
import type { DispatchAction, DispatchContext, DispatchRule } from "./auto-dispatch.js";
|
|
11
12
|
import type {
|
|
@@ -387,8 +388,8 @@ export class RuleRegistry {
|
|
|
387
388
|
const dir = join(basePath, ".gsd");
|
|
388
389
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
389
390
|
writeFileSync(this._hookStatePath(basePath), JSON.stringify(state, null, 2), "utf-8");
|
|
390
|
-
} catch {
|
|
391
|
-
|
|
391
|
+
} catch (e) {
|
|
392
|
+
logWarning("registry", `failed to persist hook state: ${(e as Error).message}`);
|
|
392
393
|
}
|
|
393
394
|
}
|
|
394
395
|
|
|
@@ -407,8 +408,8 @@ export class RuleRegistry {
|
|
|
407
408
|
}
|
|
408
409
|
}
|
|
409
410
|
}
|
|
410
|
-
} catch {
|
|
411
|
-
|
|
411
|
+
} catch (e) {
|
|
412
|
+
logWarning("registry", `failed to restore hook state: ${(e as Error).message}`);
|
|
412
413
|
}
|
|
413
414
|
}
|
|
414
415
|
|
|
@@ -423,8 +424,8 @@ export class RuleRegistry {
|
|
|
423
424
|
"utf-8",
|
|
424
425
|
);
|
|
425
426
|
}
|
|
426
|
-
} catch {
|
|
427
|
-
|
|
427
|
+
} catch (e) {
|
|
428
|
+
logWarning("registry", `failed to clear hook state: ${(e as Error).message}`);
|
|
428
429
|
}
|
|
429
430
|
}
|
|
430
431
|
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, cpSync, type CopySyncOptions } from "node:fs"
|
|
2
2
|
import { dirname } from "node:path"
|
|
3
|
+
import { logWarning } from "./workflow-logger.js"
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Safely creates a directory. Returns true if successful, false on error.
|
|
6
|
-
* Logs
|
|
7
|
+
* Logs warnings via workflow-logger on failure.
|
|
7
8
|
*/
|
|
8
9
|
export function safeMkdir(dirPath: string): boolean {
|
|
9
10
|
try {
|
|
10
11
|
mkdirSync(dirPath, { recursive: true })
|
|
11
12
|
return true
|
|
12
13
|
} catch (err) {
|
|
13
|
-
|
|
14
|
+
logWarning("fs", `mkdir failed: ${dirPath}: ${(err as Error).message}`)
|
|
14
15
|
return false
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Safely copies src to dst. Returns true if successful, false if src doesn't exist or copy fails.
|
|
20
|
-
* Logs
|
|
21
|
+
* Logs warnings via workflow-logger on failure.
|
|
21
22
|
*/
|
|
22
23
|
export function safeCopy(src: string, dst: string, opts?: CopySyncOptions): boolean {
|
|
23
24
|
if (!existsSync(src)) return false
|
|
@@ -25,7 +26,7 @@ export function safeCopy(src: string, dst: string, opts?: CopySyncOptions): bool
|
|
|
25
26
|
cpSync(src, dst, opts)
|
|
26
27
|
return true
|
|
27
28
|
} catch (err) {
|
|
28
|
-
|
|
29
|
+
logWarning("fs", `copy failed: ${src} → ${dst}: ${(err as Error).message}`)
|
|
29
30
|
return false
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -41,7 +42,7 @@ export function safeCopyRecursive(src: string, dst: string, opts?: Omit<CopySync
|
|
|
41
42
|
cpSync(src, dst, { ...opts, recursive: true })
|
|
42
43
|
return true
|
|
43
44
|
} catch (err) {
|
|
44
|
-
|
|
45
|
+
logWarning("fs", `recursive copy failed: ${src} → ${dst}: ${(err as Error).message}`)
|
|
45
46
|
return false
|
|
46
47
|
}
|
|
47
48
|
}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
// GSD Extension — Capability-Aware Router Tests
|
|
2
|
+
// Tests for new capability scoring functions and data tables (Plan 01-01)
|
|
3
|
+
|
|
4
|
+
import { describe, test } from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
scoreModel,
|
|
9
|
+
computeTaskRequirements,
|
|
10
|
+
scoreEligibleModels,
|
|
11
|
+
getEligibleModels,
|
|
12
|
+
resolveModelForComplexity,
|
|
13
|
+
MODEL_CAPABILITY_PROFILES,
|
|
14
|
+
BASE_REQUIREMENTS,
|
|
15
|
+
defaultRoutingConfig,
|
|
16
|
+
} from "../model-router.js";
|
|
17
|
+
import type { ModelCapabilities, DynamicRoutingConfig, RoutingDecision } from "../model-router.js";
|
|
18
|
+
|
|
19
|
+
// ─── scoreModel ──────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
describe("scoreModel", () => {
|
|
22
|
+
const sonnetProfile: ModelCapabilities = {
|
|
23
|
+
coding: 85, debugging: 80, research: 75, reasoning: 80,
|
|
24
|
+
speed: 60, longContext: 75, instruction: 85,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
test("produces correct weighted average for single dimension", () => {
|
|
28
|
+
// Only coding weight 1.0 → result should be the coding score
|
|
29
|
+
const score = scoreModel(sonnetProfile, { coding: 1.0 });
|
|
30
|
+
assert.equal(score, 85);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("produces correct weighted average for two dimensions (coding 0.9, instruction 0.7)", () => {
|
|
34
|
+
// (0.9*85 + 0.7*85) / (0.9+0.7) = (76.5+59.5)/1.6 = 136/1.6 = 85.0
|
|
35
|
+
const score = scoreModel(sonnetProfile, { coding: 0.9, instruction: 0.7 });
|
|
36
|
+
assert.ok(Math.abs(score - 85.0) < 0.01, `Expected ~85.0, got ${score}`);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("returns 50 when requirements is empty", () => {
|
|
40
|
+
const score = scoreModel(sonnetProfile, {});
|
|
41
|
+
assert.equal(score, 50);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("uses 50 as fallback for unknown dimension in requirements", () => {
|
|
45
|
+
// 'unknown' dimension not in profile → treated as 50
|
|
46
|
+
const score = scoreModel(sonnetProfile, { coding: 0.5, unknown: 1.0 } as any);
|
|
47
|
+
// (0.5*85 + 1.0*50) / (0.5+1.0) = (42.5+50)/1.5 = 92.5/1.5 = 61.67
|
|
48
|
+
assert.ok(score > 61 && score < 62, `Expected ~61.67, got ${score}`);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ─── computeTaskRequirements ─────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
describe("computeTaskRequirements", () => {
|
|
55
|
+
test("execute-task with no metadata returns base requirements", () => {
|
|
56
|
+
const req = computeTaskRequirements("execute-task", undefined);
|
|
57
|
+
assert.deepStrictEqual(req, { coding: 0.9, instruction: 0.7, speed: 0.3 });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("execute-task with docs tag returns docs-adjusted requirements", () => {
|
|
61
|
+
const req = computeTaskRequirements("execute-task", { tags: ["docs"] });
|
|
62
|
+
assert.equal(req.instruction, 0.9);
|
|
63
|
+
assert.equal(req.coding, 0.3);
|
|
64
|
+
assert.equal(req.speed, 0.7);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("execute-task with readme tag returns docs-adjusted requirements", () => {
|
|
68
|
+
const req = computeTaskRequirements("execute-task", { tags: ["readme"] });
|
|
69
|
+
assert.equal(req.instruction, 0.9);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("execute-task with concurrency keyword boosts debugging and reasoning", () => {
|
|
73
|
+
const req = computeTaskRequirements("execute-task", { complexityKeywords: ["concurrency"] });
|
|
74
|
+
assert.equal(req.debugging, 0.9);
|
|
75
|
+
assert.equal(req.reasoning, 0.8);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("execute-task with compatibility keyword boosts debugging and reasoning", () => {
|
|
79
|
+
const req = computeTaskRequirements("execute-task", { complexityKeywords: ["compatibility"] });
|
|
80
|
+
assert.equal(req.debugging, 0.9);
|
|
81
|
+
assert.equal(req.reasoning, 0.8);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("execute-task with migration keyword boosts reasoning and coding", () => {
|
|
85
|
+
const req = computeTaskRequirements("execute-task", { complexityKeywords: ["migration"] });
|
|
86
|
+
assert.equal(req.reasoning, 0.9);
|
|
87
|
+
assert.equal(req.coding, 0.8);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("execute-task with architecture keyword boosts reasoning and coding", () => {
|
|
91
|
+
const req = computeTaskRequirements("execute-task", { complexityKeywords: ["architecture"] });
|
|
92
|
+
assert.equal(req.reasoning, 0.9);
|
|
93
|
+
assert.equal(req.coding, 0.8);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("execute-task with fileCount >= 6 boosts coding and reasoning", () => {
|
|
97
|
+
const req = computeTaskRequirements("execute-task", { fileCount: 8 });
|
|
98
|
+
assert.equal(req.coding, 0.9);
|
|
99
|
+
assert.equal(req.reasoning, 0.7);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("execute-task with fileCount exactly 6 triggers large-file boost", () => {
|
|
103
|
+
const req = computeTaskRequirements("execute-task", { fileCount: 6 });
|
|
104
|
+
assert.equal(req.coding, 0.9);
|
|
105
|
+
assert.equal(req.reasoning, 0.7);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("execute-task with estimatedLines >= 500 boosts coding and reasoning", () => {
|
|
109
|
+
const req = computeTaskRequirements("execute-task", { estimatedLines: 500 });
|
|
110
|
+
assert.equal(req.coding, 0.9);
|
|
111
|
+
assert.equal(req.reasoning, 0.7);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("research-milestone with no metadata returns base requirements", () => {
|
|
115
|
+
const req = computeTaskRequirements("research-milestone", undefined);
|
|
116
|
+
assert.deepStrictEqual(req, { research: 0.9, longContext: 0.7, reasoning: 0.5 });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("unknown unit type returns default reasoning requirement", () => {
|
|
120
|
+
const req = computeTaskRequirements("unknown-type", undefined);
|
|
121
|
+
assert.deepStrictEqual(req, { reasoning: 0.5 });
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ─── MODEL_CAPABILITY_PROFILES ───────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
describe("MODEL_CAPABILITY_PROFILES", () => {
|
|
128
|
+
test("contains all 9 required models", () => {
|
|
129
|
+
const required = [
|
|
130
|
+
"claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5",
|
|
131
|
+
"gpt-4o", "gpt-4o-mini", "gemini-2.5-pro", "gemini-2.0-flash",
|
|
132
|
+
"deepseek-chat", "o3",
|
|
133
|
+
];
|
|
134
|
+
for (const model of required) {
|
|
135
|
+
assert.ok(MODEL_CAPABILITY_PROFILES[model], `Missing profile for ${model}`);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("each profile has all 7 capability dimensions", () => {
|
|
140
|
+
const dims: Array<keyof ModelCapabilities> = [
|
|
141
|
+
"coding", "debugging", "research", "reasoning",
|
|
142
|
+
"speed", "longContext", "instruction",
|
|
143
|
+
];
|
|
144
|
+
for (const [modelId, profile] of Object.entries(MODEL_CAPABILITY_PROFILES)) {
|
|
145
|
+
for (const dim of dims) {
|
|
146
|
+
assert.ok(profile[dim] !== undefined, `${modelId} missing dimension ${dim}`);
|
|
147
|
+
assert.ok(profile[dim] >= 0 && profile[dim] <= 100, `${modelId}.${dim} out of range`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("claude-opus-4-6 has high reasoning and coding", () => {
|
|
153
|
+
const opus = MODEL_CAPABILITY_PROFILES["claude-opus-4-6"];
|
|
154
|
+
assert.ok(opus.reasoning >= 90, `Expected reasoning >= 90, got ${opus.reasoning}`);
|
|
155
|
+
assert.ok(opus.coding >= 90, `Expected coding >= 90, got ${opus.coding}`);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("claude-haiku-4-5 has high speed but lower reasoning", () => {
|
|
159
|
+
const haiku = MODEL_CAPABILITY_PROFILES["claude-haiku-4-5"];
|
|
160
|
+
assert.ok(haiku.speed >= 90, `Expected speed >= 90, got ${haiku.speed}`);
|
|
161
|
+
assert.ok(haiku.reasoning < 70, `Expected reasoning < 70, got ${haiku.reasoning}`);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// ─── BASE_REQUIREMENTS ───────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
describe("BASE_REQUIREMENTS", () => {
|
|
168
|
+
test("contains all 11 unit types", () => {
|
|
169
|
+
const required = [
|
|
170
|
+
"execute-task", "research-milestone", "research-slice",
|
|
171
|
+
"plan-milestone", "plan-slice", "replan-slice",
|
|
172
|
+
"reassess-roadmap", "complete-slice", "run-uat",
|
|
173
|
+
"discuss-milestone", "complete-milestone",
|
|
174
|
+
];
|
|
175
|
+
for (const unitType of required) {
|
|
176
|
+
assert.ok(BASE_REQUIREMENTS[unitType], `Missing requirements for ${unitType}`);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// ─── scoreEligibleModels ─────────────────────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
describe("scoreEligibleModels", () => {
|
|
184
|
+
test("returns array sorted by score descending", () => {
|
|
185
|
+
const requirements = { research: 0.9, longContext: 0.7, reasoning: 0.5 };
|
|
186
|
+
const results = scoreEligibleModels(["claude-sonnet-4-6", "gpt-4o"], requirements);
|
|
187
|
+
assert.ok(results.length === 2);
|
|
188
|
+
assert.ok(results[0].score >= results[1].score, "Should be sorted descending by score");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("returns single model when only one eligible", () => {
|
|
192
|
+
const requirements = { coding: 0.9 };
|
|
193
|
+
const results = scoreEligibleModels(["claude-sonnet-4-6"], requirements);
|
|
194
|
+
assert.equal(results.length, 1);
|
|
195
|
+
assert.equal(results[0].modelId, "claude-sonnet-4-6");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("models without profiles get uniform 50s score", () => {
|
|
199
|
+
const requirements = { coding: 1.0 };
|
|
200
|
+
const results = scoreEligibleModels(["unknown-model-xyz"], requirements);
|
|
201
|
+
assert.equal(results[0].score, 50);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("when two models score within 2 points, prefers cheaper model", () => {
|
|
205
|
+
// gemini-2.0-flash is cheaper than gpt-4o-mini ($0.0001 vs $0.00015)
|
|
206
|
+
// Use a requirement that causes similar scores for both
|
|
207
|
+
const requirements = { speed: 1.0 };
|
|
208
|
+
const results = scoreEligibleModels(["gpt-4o-mini", "gemini-2.0-flash"], requirements);
|
|
209
|
+
// Both are high-speed: gpt-4o-mini=90, gemini-2.0-flash=95 — scores differ by 5, not within 2
|
|
210
|
+
// So top should be gemini-2.0-flash by score
|
|
211
|
+
assert.equal(results[0].modelId, "gemini-2.0-flash");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("tie-breaks by lexicographic model ID when cost and score are equal", () => {
|
|
215
|
+
// Use models without cost entries — both get Infinity cost
|
|
216
|
+
const requirements = { coding: 1.0 };
|
|
217
|
+
const results = scoreEligibleModels(["model-z", "model-a"], requirements);
|
|
218
|
+
// Both unknown → score=50, cost=Infinity → tiebreak by ID
|
|
219
|
+
assert.equal(results[0].modelId, "model-a");
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("scoreEligibleModels respects capabilityOverrides", () => {
|
|
223
|
+
const requirements = { coding: 1.0 };
|
|
224
|
+
// Override claude-sonnet-4-6's coding to 30 (worse)
|
|
225
|
+
const results = scoreEligibleModels(
|
|
226
|
+
["claude-sonnet-4-6", "gpt-4o"],
|
|
227
|
+
requirements,
|
|
228
|
+
{ "claude-sonnet-4-6": { coding: 30 } },
|
|
229
|
+
);
|
|
230
|
+
// gpt-4o coding=80 should beat overridden sonnet coding=30
|
|
231
|
+
assert.equal(results[0].modelId, "gpt-4o");
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// ─── getEligibleModels ───────────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
describe("getEligibleModels", () => {
|
|
238
|
+
const MODELS = [
|
|
239
|
+
"claude-opus-4-6", // heavy
|
|
240
|
+
"claude-sonnet-4-6", // standard
|
|
241
|
+
"claude-haiku-4-5", // light
|
|
242
|
+
"gpt-4o-mini", // light
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
test("returns light-tier models sorted by cost when no explicit config", () => {
|
|
246
|
+
const config: DynamicRoutingConfig = defaultRoutingConfig();
|
|
247
|
+
const result = getEligibleModels("light", MODELS, config);
|
|
248
|
+
assert.ok(result.length >= 1);
|
|
249
|
+
// All results should be light-tier
|
|
250
|
+
for (const id of result) {
|
|
251
|
+
assert.ok(
|
|
252
|
+
["claude-haiku-4-5", "gpt-4o-mini"].includes(id),
|
|
253
|
+
`Expected light-tier model, got ${id}`,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("returns explicit tier_models when configured and available", () => {
|
|
259
|
+
const config: DynamicRoutingConfig = {
|
|
260
|
+
...defaultRoutingConfig(),
|
|
261
|
+
tier_models: { light: "gpt-4o-mini" },
|
|
262
|
+
};
|
|
263
|
+
const result = getEligibleModels("light", MODELS, config);
|
|
264
|
+
assert.deepStrictEqual(result, ["gpt-4o-mini"]);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("returns empty array when no eligible models for tier", () => {
|
|
268
|
+
const config: DynamicRoutingConfig = defaultRoutingConfig();
|
|
269
|
+
// Only heavy model available, requesting light
|
|
270
|
+
const result = getEligibleModels("light", ["claude-opus-4-6"], config);
|
|
271
|
+
assert.equal(result.length, 0);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// ─── DynamicRoutingConfig extension ─────────────────────────────────────────
|
|
276
|
+
|
|
277
|
+
describe("DynamicRoutingConfig.capability_routing", () => {
|
|
278
|
+
test("defaultRoutingConfig includes capability_routing: true", () => {
|
|
279
|
+
const config = defaultRoutingConfig();
|
|
280
|
+
assert.equal(config.capability_routing, true);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// ─── RoutingDecision.selectionMethod ─────────────────────────────────────────
|
|
285
|
+
|
|
286
|
+
describe("RoutingDecision.selectionMethod", () => {
|
|
287
|
+
const MODELS = ["claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5", "gpt-4o-mini"];
|
|
288
|
+
|
|
289
|
+
function makeClassification(tier: "light" | "standard" | "heavy") {
|
|
290
|
+
return { tier, reason: "test", downgraded: false };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
test("returns selectionMethod: tier-only when routing is disabled", () => {
|
|
294
|
+
const config = { ...defaultRoutingConfig(), enabled: false };
|
|
295
|
+
const result: RoutingDecision = resolveModelForComplexity(
|
|
296
|
+
makeClassification("light"),
|
|
297
|
+
{ primary: "claude-opus-4-6", fallbacks: [] },
|
|
298
|
+
config,
|
|
299
|
+
MODELS,
|
|
300
|
+
);
|
|
301
|
+
assert.equal(result.selectionMethod, "tier-only");
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("returns selectionMethod: tier-only for no phase config passthrough", () => {
|
|
305
|
+
const config = { ...defaultRoutingConfig(), enabled: true };
|
|
306
|
+
const result: RoutingDecision = resolveModelForComplexity(
|
|
307
|
+
makeClassification("light"),
|
|
308
|
+
undefined,
|
|
309
|
+
config,
|
|
310
|
+
MODELS,
|
|
311
|
+
);
|
|
312
|
+
assert.equal(result.selectionMethod, "tier-only");
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("returns selectionMethod: tier-only for unknown model passthrough", () => {
|
|
316
|
+
const config = { ...defaultRoutingConfig(), enabled: true };
|
|
317
|
+
const result: RoutingDecision = resolveModelForComplexity(
|
|
318
|
+
makeClassification("light"),
|
|
319
|
+
{ primary: "custom-provider/my-model-v3", fallbacks: [] },
|
|
320
|
+
config,
|
|
321
|
+
["custom-provider/my-model-v3", ...MODELS],
|
|
322
|
+
);
|
|
323
|
+
assert.equal(result.selectionMethod, "tier-only");
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test("returns selectionMethod: tier-only for no-downgrade passthrough", () => {
|
|
327
|
+
const config = { ...defaultRoutingConfig(), enabled: true };
|
|
328
|
+
const result: RoutingDecision = resolveModelForComplexity(
|
|
329
|
+
makeClassification("heavy"),
|
|
330
|
+
{ primary: "claude-opus-4-6", fallbacks: [] },
|
|
331
|
+
config,
|
|
332
|
+
MODELS,
|
|
333
|
+
);
|
|
334
|
+
assert.equal(result.selectionMethod, "tier-only");
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("returns selectionMethod: tier-only when downgraded", () => {
|
|
338
|
+
const config = { ...defaultRoutingConfig(), enabled: true };
|
|
339
|
+
const result: RoutingDecision = resolveModelForComplexity(
|
|
340
|
+
makeClassification("light"),
|
|
341
|
+
{ primary: "claude-opus-4-6", fallbacks: [] },
|
|
342
|
+
config,
|
|
343
|
+
MODELS,
|
|
344
|
+
);
|
|
345
|
+
assert.equal(result.selectionMethod, "tier-only");
|
|
346
|
+
});
|
|
347
|
+
});
|
|
@@ -486,3 +486,66 @@ test("getCodebaseMapStats: reads total file count from header for accuracy with
|
|
|
486
486
|
cleanup(base);
|
|
487
487
|
}
|
|
488
488
|
});
|
|
489
|
+
|
|
490
|
+
// ─── excludePatterns from options ────────────────────────────────────────
|
|
491
|
+
|
|
492
|
+
test("generateCodebaseMap: custom excludePatterns filters additional directories", () => {
|
|
493
|
+
const base = makeTmpRepo();
|
|
494
|
+
try {
|
|
495
|
+
addFile(base, "src/main.ts");
|
|
496
|
+
addFile(base, "src/utils.ts");
|
|
497
|
+
addFile(base, ".cache-data/data/index.lance");
|
|
498
|
+
addFile(base, "docs/guide.md");
|
|
499
|
+
|
|
500
|
+
const result = generateCodebaseMap(base, {
|
|
501
|
+
excludePatterns: [".cache-data/", "docs/"],
|
|
502
|
+
});
|
|
503
|
+
assert.ok(result.content.includes("`src/main.ts`"));
|
|
504
|
+
assert.ok(result.content.includes("`src/utils.ts`"));
|
|
505
|
+
assert.ok(!result.content.includes(".cache-data"));
|
|
506
|
+
assert.ok(!result.content.includes("guide.md"));
|
|
507
|
+
assert.equal(result.fileCount, 2);
|
|
508
|
+
} finally {
|
|
509
|
+
cleanup(base);
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
test("generateCodebaseMap: collapseThreshold option overrides default", () => {
|
|
514
|
+
const base = makeTmpRepo();
|
|
515
|
+
try {
|
|
516
|
+
// Create 10 files in one directory — below default threshold (20)
|
|
517
|
+
// but above a custom threshold of 5
|
|
518
|
+
for (let i = 0; i < 10; i++) {
|
|
519
|
+
addFile(base, `src/comp${i}.ts`);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// With default threshold (20), files should NOT collapse
|
|
523
|
+
const expanded = generateCodebaseMap(base);
|
|
524
|
+
assert.ok(expanded.content.includes("`src/comp0.ts`"));
|
|
525
|
+
|
|
526
|
+
// With custom threshold (5), files SHOULD collapse
|
|
527
|
+
const collapsed = generateCodebaseMap(base, { collapseThreshold: 5 });
|
|
528
|
+
assert.ok(collapsed.content.includes("10 files"));
|
|
529
|
+
assert.ok(!collapsed.content.includes("`src/comp0.ts`\n"));
|
|
530
|
+
} finally {
|
|
531
|
+
cleanup(base);
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
test("updateCodebaseMap: respects excludePatterns option", () => {
|
|
536
|
+
const base = makeTmpRepo();
|
|
537
|
+
try {
|
|
538
|
+
addFile(base, "src/main.ts");
|
|
539
|
+
addFile(base, "vendor-extra/lib.js");
|
|
540
|
+
|
|
541
|
+
const initial = generateCodebaseMap(base);
|
|
542
|
+
writeCodebaseMap(base, initial.content);
|
|
543
|
+
|
|
544
|
+
// Update with exclusion should remove vendor-extra files
|
|
545
|
+
const result = updateCodebaseMap(base, { excludePatterns: ["vendor-extra/"] });
|
|
546
|
+
assert.ok(result.content.includes("`src/main.ts`"));
|
|
547
|
+
assert.ok(!result.content.includes("vendor-extra"));
|
|
548
|
+
} finally {
|
|
549
|
+
cleanup(base);
|
|
550
|
+
}
|
|
551
|
+
});
|