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
|
@@ -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
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import test from "node:test";
|
|
1
|
+
import test, { describe } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
|
|
4
|
-
import { classifyUnitComplexity, tierLabel, tierOrdinal } from "../complexity-classifier.js";
|
|
4
|
+
import { classifyUnitComplexity, tierLabel, tierOrdinal, extractTaskMetadata } from "../complexity-classifier.js";
|
|
5
5
|
import type { ComplexityTier, TaskMetadata } from "../complexity-classifier.js";
|
|
6
6
|
|
|
7
7
|
// ─── tierLabel ───────────────────────────────────────────────────────────────
|
|
@@ -179,3 +179,28 @@ test("execute-task with few code blocks stays standard", () => {
|
|
|
179
179
|
const result = classifyUnitComplexity("execute-task", "M001/S01/T01", "/tmp/fake", undefined, metadata);
|
|
180
180
|
assert.equal(result.tier, "standard");
|
|
181
181
|
});
|
|
182
|
+
|
|
183
|
+
// ─── ClassificationResult taskMetadata passthrough ───────────────────────────
|
|
184
|
+
|
|
185
|
+
describe("ClassificationResult taskMetadata", () => {
|
|
186
|
+
test("classifyUnitComplexity for execute-task returns result with taskMetadata populated", () => {
|
|
187
|
+
const metadata: TaskMetadata = { fileCount: 3, tags: ["docs"] };
|
|
188
|
+
const result = classifyUnitComplexity("execute-task", "M001/S01/T01", "/tmp/fake", undefined, metadata);
|
|
189
|
+
assert.ok(result.taskMetadata !== undefined, "taskMetadata should be populated for execute-task");
|
|
190
|
+
assert.equal(result.taskMetadata!.tags?.[0], "docs");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("classifyUnitComplexity for hook/xyz returns result with taskMetadata undefined", () => {
|
|
194
|
+
const result = classifyUnitComplexity("hook/verify", "M001/S01/T01", "/tmp/fake");
|
|
195
|
+
assert.equal(result.taskMetadata, undefined, "taskMetadata should be undefined for hook units");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("classifyUnitComplexity for plan-slice returns result with taskMetadata undefined", () => {
|
|
199
|
+
const result = classifyUnitComplexity("plan-slice", "M001/S01", "/tmp/fake");
|
|
200
|
+
assert.equal(result.taskMetadata, undefined, "taskMetadata should be undefined for plan-slice");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("extractTaskMetadata is importable as a named export and is a function", () => {
|
|
204
|
+
assert.equal(typeof extractTaskMetadata, "function", "extractTaskMetadata should be a callable function");
|
|
205
|
+
});
|
|
206
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import { createObservationMask } from "../context-masker.js";
|
|
5
|
+
|
|
6
|
+
// These helpers produce messages in the pi-ai LLM payload format
|
|
7
|
+
// (post-convertToLlm, pre-provider), which is what before_provider_request sees.
|
|
8
|
+
|
|
9
|
+
function userMsg(content: string) {
|
|
10
|
+
return { role: "user", content: [{ type: "text", text: content }] };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function assistantMsg(content: string) {
|
|
14
|
+
return { role: "assistant", content: [{ type: "text", text: content }] };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** toolResult in pi-ai format: role "toolResult", content as TextContent[] */
|
|
18
|
+
function toolResult(text: string) {
|
|
19
|
+
return { role: "toolResult", content: [{ type: "text", text }], toolCallId: "toolu_test", toolName: "Read", isError: false };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** bashExecution after convertToLlm: becomes a user message with "Ran `cmd`" prefix */
|
|
23
|
+
function bashResult(text: string) {
|
|
24
|
+
return { role: "user", content: [{ type: "text", text: `Ran \`echo test\`\n\`\`\`\n${text}\n\`\`\`` }] };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const MASK_TEXT = "[result masked — within summarized history]";
|
|
28
|
+
|
|
29
|
+
test("masks nothing when message count is within keepRecentTurns", () => {
|
|
30
|
+
const mask = createObservationMask(8);
|
|
31
|
+
const messages = [
|
|
32
|
+
userMsg("hello"),
|
|
33
|
+
assistantMsg("hi"),
|
|
34
|
+
toolResult("file contents"),
|
|
35
|
+
];
|
|
36
|
+
const result = mask(messages as any);
|
|
37
|
+
assert.equal(result.length, 3);
|
|
38
|
+
assert.deepEqual((result[2].content as any)[0].text, "file contents");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("masks tool results older than keepRecentTurns", () => {
|
|
42
|
+
const mask = createObservationMask(2);
|
|
43
|
+
const messages = [
|
|
44
|
+
userMsg("turn 1"),
|
|
45
|
+
toolResult("old tool output"),
|
|
46
|
+
assistantMsg("response 1"),
|
|
47
|
+
userMsg("turn 2"),
|
|
48
|
+
toolResult("newer tool output"),
|
|
49
|
+
assistantMsg("response 2"),
|
|
50
|
+
userMsg("turn 3"),
|
|
51
|
+
toolResult("newest tool output"),
|
|
52
|
+
assistantMsg("response 3"),
|
|
53
|
+
];
|
|
54
|
+
const result = mask(messages as any);
|
|
55
|
+
// Old tool result (before boundary) should be masked
|
|
56
|
+
assert.equal((result[1].content as any)[0].text, MASK_TEXT);
|
|
57
|
+
// Recent tool results (within keep window) should be preserved
|
|
58
|
+
assert.equal((result[4].content as any)[0].text, "newer tool output");
|
|
59
|
+
assert.equal((result[7].content as any)[0].text, "newest tool output");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("never masks assistant messages", () => {
|
|
63
|
+
const mask = createObservationMask(1);
|
|
64
|
+
const messages = [
|
|
65
|
+
userMsg("turn 1"),
|
|
66
|
+
assistantMsg("old reasoning"),
|
|
67
|
+
userMsg("turn 2"),
|
|
68
|
+
assistantMsg("new reasoning"),
|
|
69
|
+
];
|
|
70
|
+
const result = mask(messages as any);
|
|
71
|
+
assert.equal((result[1].content as any)[0].text, "old reasoning");
|
|
72
|
+
assert.equal((result[3].content as any)[0].text, "new reasoning");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("never masks user messages", () => {
|
|
76
|
+
const mask = createObservationMask(1);
|
|
77
|
+
const messages = [
|
|
78
|
+
userMsg("old user message"),
|
|
79
|
+
assistantMsg("response"),
|
|
80
|
+
userMsg("new user message"),
|
|
81
|
+
assistantMsg("response"),
|
|
82
|
+
];
|
|
83
|
+
const result = mask(messages as any);
|
|
84
|
+
assert.equal((result[0].content as any)[0].text, "old user message");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("masks bash result user messages", () => {
|
|
88
|
+
const mask = createObservationMask(1);
|
|
89
|
+
const messages = [
|
|
90
|
+
userMsg("turn 1"),
|
|
91
|
+
bashResult("huge log output"),
|
|
92
|
+
assistantMsg("response 1"),
|
|
93
|
+
userMsg("turn 2"),
|
|
94
|
+
assistantMsg("response 2"),
|
|
95
|
+
];
|
|
96
|
+
const result = mask(messages as any);
|
|
97
|
+
assert.equal((result[1].content as any)[0].text, MASK_TEXT);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("returns same array length", () => {
|
|
101
|
+
const mask = createObservationMask(1);
|
|
102
|
+
const messages = [
|
|
103
|
+
userMsg("a"), toolResult("b"), assistantMsg("c"),
|
|
104
|
+
userMsg("d"), toolResult("e"), assistantMsg("f"),
|
|
105
|
+
];
|
|
106
|
+
const result = mask(messages as any);
|
|
107
|
+
assert.equal(result.length, messages.length);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("masks toolResult by role, not by type field", () => {
|
|
111
|
+
const mask = createObservationMask(1);
|
|
112
|
+
const messages = [
|
|
113
|
+
userMsg("turn 1"),
|
|
114
|
+
// This is the actual pi-ai format: role "toolResult", no type field
|
|
115
|
+
{ role: "toolResult", content: [{ type: "text", text: "old result" }], toolCallId: "t1", toolName: "Read", isError: false },
|
|
116
|
+
assistantMsg("response 1"),
|
|
117
|
+
userMsg("turn 2"),
|
|
118
|
+
assistantMsg("response 2"),
|
|
119
|
+
];
|
|
120
|
+
const result = mask(messages as any);
|
|
121
|
+
assert.equal((result[1].content as any)[0].text, MASK_TEXT);
|
|
122
|
+
});
|
|
@@ -94,11 +94,11 @@ const dynamicToolsSrc = readFileSync(
|
|
|
94
94
|
"utf-8",
|
|
95
95
|
);
|
|
96
96
|
|
|
97
|
-
// ensureDbOpen should
|
|
98
|
-
// Check that the catch block
|
|
97
|
+
// ensureDbOpen should surface diagnostic context, not just boolean false
|
|
98
|
+
// Check that the catch block logs error details via workflow-logger
|
|
99
99
|
assertTrue(
|
|
100
|
-
dynamicToolsSrc.includes("
|
|
101
|
-
"ensureDbOpen catch block surfaces diagnostic information
|
|
100
|
+
dynamicToolsSrc.includes("ensureDbOpen failed") && dynamicToolsSrc.includes("logWarning"),
|
|
101
|
+
"ensureDbOpen catch block surfaces diagnostic information via logWarning instead of bare false (#2517)",
|
|
102
102
|
);
|
|
103
103
|
|
|
104
104
|
// ── Part 3: post-unit does NOT artifact-retry on db_unavailable ──────────
|