@geraldmaron/construct 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -18
- package/bin/construct +392 -91
- package/bin/construct-postinstall.mjs +30 -2
- package/examples/distribution/README.md +42 -0
- package/examples/distribution/manifest.json +53 -0
- package/examples/distribution/sources/adr.md +84 -0
- package/examples/distribution/sources/deck-one-pager.md +65 -0
- package/examples/distribution/sources/prd-platform.md +161 -0
- package/examples/distribution/sources/research-brief.md +80 -0
- package/examples/distribution/sources/rfc-platform.md +82 -0
- package/examples/distribution/sources/runbook.md +103 -0
- package/examples/distribution/sources/strategy.md +88 -0
- package/lib/adapters-sync.mjs +67 -0
- package/lib/artifact-gate-notice.mjs +38 -0
- package/lib/artifact-manifest.mjs +112 -0
- package/lib/artifact-release-gate.mjs +171 -0
- package/lib/artifact-reviewers.mjs +68 -0
- package/lib/artifact-type-from-path.mjs +79 -0
- package/lib/audit-skills.mjs +4 -0
- package/lib/audit-specialists.mjs +285 -0
- package/lib/audit-trail.mjs +77 -10
- package/lib/auto-docs.mjs +87 -18
- package/lib/beads/drift.mjs +27 -6
- package/lib/beads-client.mjs +49 -121
- package/lib/beads-optimistic.mjs +5 -12
- package/lib/brand-fonts.mjs +93 -0
- package/lib/brand-prose.mjs +214 -0
- package/lib/brand-tokens.mjs +92 -0
- package/lib/bridges/copilot-proxy.mjs +13 -26
- package/lib/capability-ledger.mjs +156 -0
- package/lib/certification/artifact-fixtures.mjs +132 -0
- package/lib/certification/artifact-gates.mjs +63 -0
- package/lib/certification/artifact-provenance.mjs +97 -0
- package/lib/certification/canonical-scenarios.mjs +78 -0
- package/lib/certification/cli.mjs +191 -0
- package/lib/certification/dashboard-api.mjs +71 -0
- package/lib/certification/demo-parity.mjs +116 -0
- package/lib/certification/document-io-fixtures.mjs +246 -0
- package/lib/certification/document-workflow.mjs +97 -0
- package/lib/certification/eval-bridge.mjs +77 -0
- package/lib/certification/model-routing.mjs +149 -0
- package/lib/certification/prompt-budget.mjs +119 -0
- package/lib/certification/rc-gate.mjs +206 -0
- package/lib/certification/real-llm-scenarios.mjs +303 -0
- package/lib/certification/role-cards.mjs +113 -0
- package/lib/certification/role-overlays.mjs +117 -0
- package/lib/certification/run.mjs +122 -0
- package/lib/certification/runner.mjs +323 -0
- package/lib/certification/scenarios.mjs +51 -0
- package/lib/certification/skill-inventory.mjs +289 -0
- package/lib/certification/skill-scenarios.mjs +147 -0
- package/lib/certification/specialist-contracts.mjs +85 -0
- package/lib/certification/specialist-scenarios.mjs +175 -0
- package/lib/certification/stale-impact.mjs +146 -0
- package/lib/certification/status.mjs +252 -0
- package/lib/certification/store.mjs +77 -0
- package/lib/chat/cli.mjs +333 -0
- package/lib/chat/command-suggest.mjs +161 -0
- package/lib/chat/commands.mjs +215 -0
- package/lib/chat/config.mjs +142 -0
- package/lib/chat/context-compactor.mjs +250 -0
- package/lib/chat/context-continuation.mjs +253 -0
- package/lib/chat/continuation-source.mjs +58 -0
- package/lib/chat/demo-guide.mjs +61 -0
- package/lib/chat/design-tokens.mjs +91 -0
- package/lib/chat/desktop-binary.mjs +79 -0
- package/lib/chat/desktop-build.mjs +130 -0
- package/lib/chat/desktop-launcher.mjs +133 -0
- package/lib/chat/evidence.mjs +145 -0
- package/lib/chat/export.mjs +74 -0
- package/lib/chat/harness/driver.mjs +91 -0
- package/lib/chat/list-picker.mjs +112 -0
- package/lib/chat/model-picker.mjs +356 -0
- package/lib/chat/openrouter-fallback.mjs +151 -0
- package/lib/chat/permission-prompt.mjs +33 -0
- package/lib/chat/picker-catalog.mjs +45 -0
- package/lib/chat/policy-telemetry.mjs +34 -0
- package/lib/chat/present.mjs +245 -0
- package/lib/chat/session-context.mjs +39 -0
- package/lib/chat/session-persist.mjs +73 -0
- package/lib/chat/session-restore.mjs +71 -0
- package/lib/chat/session-settings.mjs +53 -0
- package/lib/chat/system-prompt.mjs +52 -0
- package/lib/chat/transparency.mjs +93 -0
- package/lib/chat/tui/color-scheme.mjs +42 -0
- package/lib/chat/tui/markdown.mjs +123 -0
- package/lib/chat/tui/presentation.mjs +100 -0
- package/lib/chat/tui/render.mjs +500 -0
- package/lib/chat/tui/turn-block.mjs +284 -0
- package/lib/chat/tui/turn-present.mjs +18 -0
- package/lib/chat/tui/turn-state.mjs +88 -0
- package/lib/chat/tui/usage.mjs +122 -0
- package/lib/chat/web-commands.mjs +146 -0
- package/lib/chat/web-launcher.mjs +63 -0
- package/lib/chat/web-picker-keys.mjs +46 -0
- package/lib/chat/web-session.mjs +159 -0
- package/lib/cli-commands.mjs +232 -11
- package/lib/comment-lint.mjs +6 -4
- package/lib/config/schema.mjs +36 -5
- package/lib/contract-schemas/decision.json +50 -0
- package/lib/contract-schemas/implementation.json +51 -0
- package/lib/contract-schemas/review-report.json +32 -0
- package/lib/contract-schemas/test-report.json +43 -0
- package/lib/contracts/construct-handoff.mjs +60 -0
- package/lib/contracts/validate.mjs +32 -3
- package/lib/contracts/violation-log.mjs +58 -5
- package/lib/dashboard-demo.mjs +71 -0
- package/lib/dashboard-static.mjs +19 -5
- package/lib/decisions/registry.mjs +5 -3
- package/lib/deck-export-pptx.mjs +1152 -0
- package/lib/demo-recording.mjs +142 -0
- package/lib/demo-script.mjs +114 -0
- package/lib/demo-surface.mjs +249 -0
- package/lib/demo.mjs +703 -0
- package/lib/diagram-export.mjs +192 -0
- package/lib/diagram.mjs +302 -0
- package/lib/docs-verify.mjs +25 -7
- package/lib/doctor/index.mjs +4 -2
- package/lib/doctor/project-adapters.mjs +44 -0
- package/lib/doctor/watchers/cx-budget.mjs +98 -0
- package/lib/doctor/watchers/graph-staleness.mjs +52 -0
- package/lib/doctor/watchers/process-pressure.mjs +4 -3
- package/lib/doctor/watchers/service-health.mjs +1 -1
- package/lib/document-export.mjs +360 -36
- package/lib/document-extract.mjs +88 -0
- package/lib/document-ingest.mjs +32 -18
- package/lib/embed/cli.mjs +2 -2
- package/lib/embed/daemon.mjs +18 -46
- package/lib/embed/semantic.mjs +5 -2
- package/lib/embed/worker.mjs +2 -2
- package/lib/embedded-contract/model-resolve.mjs +10 -9
- package/lib/env-config.mjs +26 -0
- package/lib/evals/dataset.mjs +137 -0
- package/lib/evals/gates.mjs +175 -0
- package/lib/graph/build-co-change.mjs +54 -0
- package/lib/graph/build-from-registry.mjs +136 -0
- package/lib/graph/build-import-graph.mjs +153 -0
- package/lib/graph/cli.mjs +110 -0
- package/lib/graph/impact-cli.mjs +89 -0
- package/lib/graph/impact.mjs +108 -0
- package/lib/graph/staleness.mjs +44 -0
- package/lib/graph/store.mjs +172 -0
- package/lib/handoffs/cleanup.mjs +1 -1
- package/lib/health-check.mjs +90 -76
- package/lib/hooks/artifact-release-gate.mjs +43 -0
- package/lib/hooks/audit-trail.mjs +14 -28
- package/lib/hooks/brand-prose-lint.mjs +38 -0
- package/lib/hooks/graph-impact-advisory.mjs +62 -0
- package/lib/hooks/session-optimize.mjs +84 -207
- package/lib/hooks/session-start.mjs +4 -5
- package/lib/host-disposition.mjs +7 -0
- package/lib/improvement/cli.mjs +189 -0
- package/lib/improvement/controller.mjs +137 -0
- package/lib/improvement/proposal.mjs +120 -0
- package/lib/improvement/specialist-loop.mjs +192 -0
- package/lib/improvement/store.mjs +89 -0
- package/lib/improvement/surface.mjs +219 -0
- package/lib/ingest-tooling.mjs +97 -0
- package/lib/init/doc-lanes.mjs +165 -0
- package/lib/init-docs.mjs +31 -178
- package/lib/init-unified.mjs +27 -97
- package/lib/init-update-guide.mjs +102 -0
- package/lib/init-update.mjs +32 -1
- package/lib/init.mjs +8 -0
- package/lib/install/desktop-binary-download.mjs +85 -0
- package/lib/install/legacy-global-cleanup.mjs +189 -0
- package/lib/intake/daemon.mjs +2 -0
- package/lib/intake/git-queue.mjs +9 -8
- package/lib/intake/queue.mjs +2 -1
- package/lib/intake/session-prelude.mjs +90 -1
- package/lib/libreoffice-export.mjs +97 -0
- package/lib/logging/rotate.mjs +9 -0
- package/lib/maintenance/docker-reclaim.mjs +206 -0
- package/lib/mcp/external-schema-cost.mjs +74 -0
- package/lib/mcp/server.mjs +84 -9
- package/lib/mcp/stdio-mcp-probe.mjs +188 -0
- package/lib/mcp/tool-budget.mjs +55 -4
- package/lib/mcp/tools/document.mjs +18 -4
- package/lib/mcp/tools/skills.mjs +32 -3
- package/lib/mcp/tools/workflow.mjs +13 -1
- package/lib/model-free-selector.mjs +18 -0
- package/lib/model-registry.mjs +13 -229
- package/lib/model-router.mjs +425 -93
- package/lib/models/behavior-matrix.mjs +289 -0
- package/lib/models/catalog.mjs +209 -0
- package/lib/models/execution-capability-profile.mjs +196 -0
- package/lib/models/execution-policy.mjs +307 -0
- package/lib/models/provider-poll.mjs +383 -0
- package/lib/npm-spawn-env.mjs +17 -0
- package/lib/ollama/installed-models.mjs +129 -0
- package/lib/oracle/actions.mjs +187 -0
- package/lib/oracle/artifact-gate.mjs +99 -0
- package/lib/oracle/cli.mjs +204 -0
- package/lib/oracle/daemon-entry.mjs +14 -0
- package/lib/oracle/dispatch.mjs +81 -0
- package/lib/oracle/execute.mjs +143 -0
- package/lib/oracle/gaps.mjs +76 -0
- package/lib/oracle/index.mjs +84 -0
- package/lib/oracle/issues.mjs +164 -0
- package/lib/oracle/org-graph.mjs +170 -0
- package/lib/oracle/policy.mjs +80 -0
- package/lib/oracle/read-model.mjs +398 -0
- package/lib/oracle/reconcile.mjs +191 -0
- package/lib/oracle/routing.mjs +89 -0
- package/lib/oracle/synthesize.mjs +384 -0
- package/lib/oracle/verdicts.mjs +51 -0
- package/lib/orchestration/worker.mjs +88 -27
- package/lib/orchestration-policy.mjs +50 -0
- package/lib/parity.mjs +96 -2
- package/lib/persona-sections.mjs +66 -0
- package/lib/playwright-demo.mjs +252 -0
- package/lib/project-init-shared.mjs +4 -1
- package/lib/prompt-composer.js +9 -4
- package/lib/prompt-validation-contract.mjs +24 -0
- package/lib/provider-capabilities.js +57 -11
- package/lib/providers/contract/adapters/confluence/index.mjs +181 -0
- package/lib/providers/contract/adapters/git/index.mjs +115 -0
- package/lib/providers/contract/adapters/github/index.mjs +166 -0
- package/lib/providers/contract/adapters/jira/index.mjs +187 -0
- package/lib/providers/contract/adapters/slack/index.mjs +175 -0
- package/lib/providers/contract/contract-tests.mjs +57 -0
- package/lib/providers/contract/errors.mjs +48 -0
- package/lib/providers/contract/interface.mjs +50 -0
- package/lib/providers/contract/registry.mjs +102 -0
- package/lib/providers/copilot-auth.mjs +297 -0
- package/lib/providers/credential-bootstrap.mjs +178 -0
- package/lib/providers/credential-catalog.mjs +46 -0
- package/lib/providers/credential-sources.mjs +63 -0
- package/lib/providers/creds.mjs +5 -2
- package/lib/providers/op-run.mjs +59 -0
- package/lib/providers/secret-resolver.mjs +159 -0
- package/lib/publish-template.mjs +163 -0
- package/lib/publish-tooling.mjs +119 -0
- package/lib/publish.mjs +305 -0
- package/lib/registry/cli.mjs +82 -0
- package/lib/registry/consolidation.mjs +147 -0
- package/lib/registry/generate-docs.mjs +75 -0
- package/lib/registry/skill-verification.mjs +57 -0
- package/lib/registry/surface-map.mjs +76 -0
- package/lib/registry/validate.mjs +135 -0
- package/lib/resources/budget.mjs +82 -0
- package/lib/resources/process-budget.mjs +45 -0
- package/lib/rules-delivery.mjs +9 -2
- package/lib/rules-read.mjs +26 -0
- package/lib/runtime-env.mjs +23 -0
- package/lib/runtime-pressure.mjs +51 -7
- package/lib/schema-infer.mjs +13 -2
- package/lib/server/chat-loop.mjs +622 -0
- package/lib/server/demo-preview.mjs +63 -0
- package/lib/server/index.mjs +229 -8
- package/lib/server/langfuse-login.mjs +3 -3
- package/lib/service-manager.mjs +61 -9
- package/lib/session-store.mjs +1 -1
- package/lib/setup.mjs +102 -2
- package/lib/specialists/prompt-schema.mjs +19 -10
- package/lib/specialists/roster.mjs +43 -0
- package/lib/specialists/scaffold.mjs +56 -0
- package/lib/storage/backend.mjs +6 -6
- package/lib/storage/embeddings-local.mjs +6 -3
- package/lib/storage/file-lock.mjs +18 -11
- package/lib/storage/hybrid-query.mjs +7 -4
- package/lib/storage/state-source.mjs +5 -5
- package/lib/storage/sync.mjs +2 -3
- package/lib/telemetry/rule-calls.mjs +52 -0
- package/lib/template-registry.mjs +2 -2
- package/lib/templates/visual-requirements.mjs +27 -51
- package/lib/test-corpus-inventory.mjs +313 -0
- package/lib/uninstall/uninstall.mjs +19 -7
- package/lib/update.mjs +12 -0
- package/lib/upgrade.mjs +14 -0
- package/lib/wireframe.mjs +20 -14
- package/lib/worker/run.mjs +17 -4
- package/lib/worker/trace.mjs +11 -3
- package/package.json +29 -13
- package/personas/construct.md +13 -13
- package/platforms/claude/settings.template.json +36 -0
- package/rules/common/patterns.md +1 -1
- package/rules/common/release-gates.md +4 -3
- package/scripts/sync-specialists.mjs +187 -60
- package/skills/devops/data-engineering.md +1 -1
- package/skills/docs/adr-workflow.md +1 -0
- package/skills/docs/backlog-proposal-workflow.md +1 -0
- package/skills/docs/codebase-research-workflow.md +40 -0
- package/skills/docs/customer-profile-workflow.md +1 -0
- package/skills/docs/document-ingest-workflow.md +1 -0
- package/skills/docs/evidence-ingest-workflow.md +1 -0
- package/skills/docs/init-docs.md +2 -2
- package/skills/docs/init-project.md +8 -3
- package/skills/docs/prd-workflow.md +24 -1
- package/skills/docs/prfaq-workflow.md +1 -0
- package/skills/docs/product-intelligence-workflow.md +1 -0
- package/skills/docs/product-signal-workflow.md +1 -0
- package/skills/docs/research-workflow.md +54 -37
- package/skills/docs/runbook-workflow.md +1 -0
- package/skills/docs/strategy-workflow.md +1 -0
- package/skills/docs/user-research-workflow.md +40 -0
- package/skills/operating/orchestration-reference.md +1 -1
- package/skills/roles/architect.md +5 -0
- package/skills/roles/operator.docs.md +4 -0
- package/skills/routing.md +4 -2
- package/specialists/artifact-manifest.json +480 -0
- package/specialists/artifact-manifest.schema.json +53 -0
- package/specialists/audit-enrichments.json +454 -0
- package/specialists/contracts.json +37 -25
- package/specialists/prompts/_shared/validation-contract.md +26 -0
- package/specialists/prompts/cx-accessibility.md +21 -2
- package/specialists/prompts/cx-ai-engineer.md +24 -1
- package/specialists/prompts/cx-architect.md +4 -1
- package/specialists/prompts/cx-business-strategist.md +23 -2
- package/specialists/prompts/cx-data-analyst.md +22 -1
- package/specialists/prompts/cx-data-engineer.md +23 -2
- package/specialists/prompts/cx-debugger.md +21 -2
- package/specialists/prompts/cx-designer.md +26 -3
- package/specialists/prompts/cx-devil-advocate.md +19 -2
- package/specialists/prompts/cx-docs-keeper.md +28 -1
- package/specialists/prompts/cx-engineer.md +22 -1
- package/specialists/prompts/cx-evaluator.md +18 -1
- package/specialists/prompts/cx-explorer.md +21 -2
- package/specialists/prompts/cx-legal-compliance.md +21 -2
- package/specialists/prompts/cx-operations.md +21 -2
- package/specialists/prompts/cx-oracle.md +94 -0
- package/specialists/prompts/cx-orchestrator.md +20 -1
- package/specialists/prompts/cx-platform-engineer.md +25 -2
- package/specialists/prompts/cx-product-manager.md +32 -1
- package/specialists/prompts/cx-qa.md +24 -2
- package/specialists/prompts/cx-rd-lead.md +23 -2
- package/specialists/prompts/cx-release-manager.md +23 -2
- package/specialists/prompts/cx-researcher.md +22 -2
- package/specialists/prompts/cx-reviewer.md +18 -1
- package/specialists/prompts/cx-security.md +22 -2
- package/specialists/prompts/cx-sre.md +30 -3
- package/specialists/prompts/cx-test-automation.md +7 -1
- package/specialists/prompts/cx-trace-reviewer.md +22 -3
- package/specialists/prompts/cx-ux-researcher.md +21 -2
- package/specialists/registry.json +52 -173
- package/specialists/tone-profiles.json +42 -0
- package/templates/demos/playwright/demo-recording.config.mjs +47 -0
- package/templates/demos/recordings/agentic-platforms-prd.json +19 -0
- package/templates/demos/scripts/agentic-platforms-prd.json +44 -0
- package/templates/demos/specs/_helpers/scroll-artifact.ts +89 -0
- package/templates/demos/tapes/agentic-platforms-prd.tape +49 -0
- package/templates/demos/tapes/resource-guard-rails.tape +49 -0
- package/templates/demos/vhs/construct-cockpit.json +24 -0
- package/templates/distribution/construct-brand.typ +446 -0
- package/templates/distribution/construct-decision.typ +38 -0
- package/templates/distribution/construct-deck.html +95 -0
- package/templates/distribution/construct-pdf.typ +38 -0
- package/templates/distribution/construct-prd.typ +38 -0
- package/templates/distribution/construct-research.typ +38 -0
- package/templates/distribution/construct-web.html +92 -0
- package/templates/distribution/fonts/Geist-Bold.ttf +0 -0
- package/templates/distribution/fonts/Geist-Medium.ttf +0 -0
- package/templates/distribution/fonts/Geist-Regular.ttf +0 -0
- package/templates/distribution/fonts/Geist-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/GeistMono-Medium.ttf +0 -0
- package/templates/distribution/fonts/GeistMono-Regular.ttf +0 -0
- package/templates/distribution/fonts/GeistMono-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/IBMPlexMono-Regular.otf +0 -0
- package/templates/distribution/fonts/JetBrainsMono-Medium.ttf +0 -0
- package/templates/distribution/fonts/JetBrainsMono-Regular.ttf +0 -0
- package/templates/distribution/fonts/JetBrainsMono-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-Bold.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-Medium.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-Regular.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/README.md +36 -0
- package/templates/distribution/fonts/SpaceGrotesk-Variable.ttf +0 -0
- package/templates/distribution/fonts/handwritten/Caveat.ttf +0 -0
- package/templates/distribution/fonts/legacy/IBMPlexMono-Regular.otf +0 -0
- package/templates/distribution/fonts/legacy/Inter-Medium.otf +0 -0
- package/templates/distribution/fonts/legacy/Inter-Regular.otf +0 -0
- package/templates/distribution/fonts/legacy/Inter-SemiBold.otf +0 -0
- package/templates/distribution/fonts/legacy/InterDisplay-SemiBold.otf +0 -0
- package/templates/distribution/fonts/legacy/SourceSerif4-Regular.otf +0 -0
- package/templates/distribution/fonts/legacy/SourceSerif4-Semibold.otf +0 -0
- package/templates/distribution/icons/activity.svg +15 -0
- package/templates/distribution/icons/alert-triangle.svg +17 -0
- package/templates/distribution/icons/book-open.svg +16 -0
- package/templates/distribution/icons/bot.svg +20 -0
- package/templates/distribution/icons/brain.svg +22 -0
- package/templates/distribution/icons/circle-check.svg +16 -0
- package/templates/distribution/icons/clipboard-check.svg +17 -0
- package/templates/distribution/icons/cpu.svg +28 -0
- package/templates/distribution/icons/database.svg +17 -0
- package/templates/distribution/icons/eye.svg +16 -0
- package/templates/distribution/icons/file-text.svg +19 -0
- package/templates/distribution/icons/gauge.svg +16 -0
- package/templates/distribution/icons/git-branch.svg +17 -0
- package/templates/distribution/icons/key.svg +17 -0
- package/templates/distribution/icons/layers.svg +17 -0
- package/templates/distribution/icons/list-checks.svg +19 -0
- package/templates/distribution/icons/lock.svg +16 -0
- package/templates/distribution/icons/message-square.svg +15 -0
- package/templates/distribution/icons/network.svg +19 -0
- package/templates/distribution/icons/route.svg +17 -0
- package/templates/distribution/icons/scale.svg +19 -0
- package/templates/distribution/icons/search.svg +16 -0
- package/templates/distribution/icons/send.svg +16 -0
- package/templates/distribution/icons/server.svg +18 -0
- package/templates/distribution/icons/shield-check.svg +16 -0
- package/templates/distribution/icons/users.svg +18 -0
- package/templates/distribution/icons/webhook.svg +17 -0
- package/templates/distribution/icons/workflow.svg +17 -0
- package/templates/distribution/icons/wrench.svg +15 -0
- package/templates/distribution/run.mjs +18 -1
- package/templates/docs/adr.md +7 -0
- package/templates/docs/construct_guide.md +10 -10
- package/templates/docs/customer-profile.md +4 -0
- package/templates/docs/one-pager.md +4 -0
- package/templates/docs/postmortem.md +31 -0
- package/templates/docs/prd-platform.md +19 -0
- package/templates/docs/prd.md +15 -0
- package/templates/docs/prfaq.md +7 -0
- package/templates/docs/qa-strategy.md +103 -0
- package/templates/docs/research-brief.md +8 -0
- package/templates/docs/rfc-platform.md +12 -0
- package/templates/docs/test-plan.md +7 -0
- package/lib/ingest/chunker.mjs +0 -94
- package/lib/ingest/pipeline.mjs +0 -53
- package/lib/ingest/store.mjs +0 -82
- package/lib/mode-commands.mjs +0 -122
- package/lib/policy/unified-gates.mjs +0 -96
- package/lib/profiles/validate-custom.mjs +0 -114
- package/lib/services/telemetry-backend.mjs +0 -177
- package/lib/storage/fusion.mjs +0 -95
package/lib/mcp/tool-budget.mjs
CHANGED
|
@@ -8,15 +8,20 @@
|
|
|
8
8
|
* 2. which external MCP servers are `enabled` in opencode.json — sync disables
|
|
9
9
|
* the heavy ones for local-capable setups (they cannot be trimmed at runtime).
|
|
10
10
|
* Holds the shared token-sizing helper and the external-server id list both
|
|
11
|
-
* levers reason about
|
|
11
|
+
* levers reason about, plus the owned-loop tool-budget filter that trims the
|
|
12
|
+
* Vercel AI SDK tool set to a compiled execution policy's allowed groups and
|
|
13
|
+
* schema cap (lib/models/execution-policy.mjs).
|
|
12
14
|
*/
|
|
13
15
|
|
|
16
|
+
import { getMeasuredHeavyExternalMcpTokenCost } from './external-schema-cost.mjs';
|
|
17
|
+
|
|
14
18
|
const TOKENS_PER_CHAR = 0.25;
|
|
15
19
|
|
|
16
20
|
// Heavy external MCP servers Construct registers globally. On a local-capable
|
|
17
|
-
// setup these
|
|
18
|
-
//
|
|
19
|
-
//
|
|
21
|
+
// setup these serialize a measured ~37k tokens of tool schema into every agent's
|
|
22
|
+
// window (github alone ~30k as of 2026-06-22; see tests/fixtures/mcp-tool-schemas/
|
|
23
|
+
// and scripts/measure-external-mcp-schemas.mjs) and cannot be filtered per request,
|
|
24
|
+
// so sync disables them in opencode.json. construct-mcp's own knowledge_search /
|
|
20
25
|
// memory_search cover the search/memory cases.
|
|
21
26
|
|
|
22
27
|
export const HEAVY_EXTERNAL_MCP_IDS = ['context7', 'github', 'memory', 'sequential-thinking', 'playwright'];
|
|
@@ -29,6 +34,7 @@ export const LOCAL_SURFACE_MODES = ['auto', 'on', 'off'];
|
|
|
29
34
|
|
|
30
35
|
export function isLocalModel(model) {
|
|
31
36
|
const m = (model || "").toLowerCase();
|
|
37
|
+
if (m.startsWith("local/")) return true;
|
|
32
38
|
return m.includes("ollama") || m.includes("localhost") || m.includes("127.0.0.1");
|
|
33
39
|
}
|
|
34
40
|
|
|
@@ -51,3 +57,48 @@ export function estimateToolTokens(tools) {
|
|
|
51
57
|
const arr = Array.isArray(tools) ? tools : Object.values(tools);
|
|
52
58
|
return Math.round(JSON.stringify(arr).length * TOKENS_PER_CHAR);
|
|
53
59
|
}
|
|
60
|
+
|
|
61
|
+
export { getMeasuredHeavyExternalMcpTokenCost };
|
|
62
|
+
|
|
63
|
+
// Maps the owned loop's agent tool names (apps/chat/engine/tools/registry.mjs)
|
|
64
|
+
// onto the execution-policy tool groups (lib/models/execution-policy.mjs) so a
|
|
65
|
+
// compiled policy can trim the serialized tool surface for a capability-
|
|
66
|
+
// constrained model. construct_tool is the single MCP gateway, so it stands in
|
|
67
|
+
// for the whole 'construct' group. An unrecognized name falls back to
|
|
68
|
+
// 'construct' — a group present in every tier — so an unclassified tool is never
|
|
69
|
+
// silently dropped from a constrained surface.
|
|
70
|
+
|
|
71
|
+
const AGENT_TOOL_GROUPS = Object.freeze({
|
|
72
|
+
read: 'read',
|
|
73
|
+
glob: 'search',
|
|
74
|
+
grep: 'search',
|
|
75
|
+
write: 'edit',
|
|
76
|
+
edit: 'edit',
|
|
77
|
+
shell: 'shell',
|
|
78
|
+
construct_tool: 'construct',
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export function toolGroupForName(name) {
|
|
82
|
+
return AGENT_TOOL_GROUPS[name] || 'construct';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Trim a tool set to the compiled policy's envelope: drop any tool whose group is
|
|
86
|
+
// outside allowedToolGroups, then cap the survivors at maxToolSchemas in stable
|
|
87
|
+
// insertion order. A null allowedToolGroups means "no group filter" and an
|
|
88
|
+
// unbounded cap means "no count limit", so the rich/hosted-direct envelope (all
|
|
89
|
+
// groups, cap 32) returns the input set unchanged.
|
|
90
|
+
|
|
91
|
+
export function applyToolBudget(tools, { allowedToolGroups = null, maxToolSchemas = Infinity } = {}) {
|
|
92
|
+
if (!tools || typeof tools !== 'object') return tools;
|
|
93
|
+
const allow = Array.isArray(allowedToolGroups) ? new Set(allowedToolGroups) : null;
|
|
94
|
+
const cap = Number.isFinite(maxToolSchemas) ? Math.max(0, Math.floor(maxToolSchemas)) : Infinity;
|
|
95
|
+
const out = {};
|
|
96
|
+
let kept = 0;
|
|
97
|
+
for (const [name, def] of Object.entries(tools)) {
|
|
98
|
+
if (allow && !allow.has(toolGroupForName(name))) continue;
|
|
99
|
+
if (kept >= cap) break;
|
|
100
|
+
out[name] = def;
|
|
101
|
+
kept += 1;
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/mcp/tools/document.mjs — Document-handling MCP tools: extract text, ingest, infer schema, list schema artifacts.
|
|
3
3
|
*
|
|
4
|
-
* All functions are async-safe;
|
|
4
|
+
* All functions are async-safe; listSchemaArtifactsTool is synchronous.
|
|
5
5
|
* Depends on lib/document-extract.mjs, lib/document-ingest.mjs, lib/schema-infer.mjs, lib/schema-artifact.mjs.
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync } from 'node:fs';
|
|
8
8
|
import { resolve } from 'node:path';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
extractDocumentTextAsync,
|
|
11
|
+
extractDocumentTextNodeNative,
|
|
12
|
+
} from '../../document-extract.mjs';
|
|
10
13
|
import { ingestDocuments } from '../../document-ingest.mjs';
|
|
11
14
|
import { inferDocumentSchema, inferUnifiedSchema } from '../../schema-infer.mjs';
|
|
12
15
|
import { writeSchemaArtifact, listSchemaArtifacts, SCHEMA_DIR } from '../../schema-artifact.mjs';
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
async function extractViaIngestPipeline(filePath, { maxChars }) {
|
|
18
|
+
try {
|
|
19
|
+
return await extractDocumentTextNodeNative(filePath, { maxChars });
|
|
20
|
+
} catch (err) {
|
|
21
|
+
if (err.code === 'OFFICE_REQUIRES_DOCLING' || err.code === 'PDF_REQUIRES_DOCLING') {
|
|
22
|
+
return extractDocumentTextAsync(filePath, { maxChars });
|
|
23
|
+
}
|
|
24
|
+
throw err;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function extractDocumentText(args) {
|
|
15
29
|
const filePath = resolve(String(args.file_path || ''));
|
|
16
30
|
const maxChars = Number.isFinite(Number(args.max_chars)) && Number(args.max_chars) > 0
|
|
17
31
|
? Math.min(Number(args.max_chars), 200_000)
|
|
@@ -22,7 +36,7 @@ export function extractDocumentText(args) {
|
|
|
22
36
|
}
|
|
23
37
|
|
|
24
38
|
try {
|
|
25
|
-
const extracted =
|
|
39
|
+
const extracted = await extractViaIngestPipeline(filePath, { maxChars });
|
|
26
40
|
return {
|
|
27
41
|
file_path: extracted.filePath,
|
|
28
42
|
extension: extracted.extension,
|
package/lib/mcp/tools/skills.mjs
CHANGED
|
@@ -8,9 +8,17 @@
|
|
|
8
8
|
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import { execFileSync } from 'node:child_process';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
buildConstructToOrchestratorPacket,
|
|
13
|
+
routeRequest,
|
|
14
|
+
routeRequestVerified,
|
|
15
|
+
requiresExecutiveApproval,
|
|
16
|
+
TERMINAL_STATES,
|
|
17
|
+
} from '../../orchestration-policy.mjs';
|
|
12
18
|
import { buildTaskPacketFromIntent } from '../../workflow-state.mjs';
|
|
13
19
|
import { logSkillCall } from '../../telemetry/skill-calls.mjs';
|
|
20
|
+
import { templateMetadata } from '../../artifact-manifest.mjs';
|
|
21
|
+
import { buildSpecialistCatalog } from '../../specialists/roster.mjs';
|
|
14
22
|
|
|
15
23
|
export function listSkills({ ROOT_DIR }) {
|
|
16
24
|
const skillsDir = join(ROOT_DIR, 'skills');
|
|
@@ -120,14 +128,21 @@ export function getTemplate(args, { ROOT_DIR }) {
|
|
|
120
128
|
];
|
|
121
129
|
for (const { source, path: p } of candidates) {
|
|
122
130
|
if (existsSync(p)) {
|
|
123
|
-
|
|
131
|
+
const meta = templateMetadata(safe, { cwd, rootDir: ROOT_DIR });
|
|
132
|
+
return { source, path: p, content: readFileSync(p, 'utf8'), metadata: meta };
|
|
124
133
|
}
|
|
125
134
|
}
|
|
126
135
|
return { error: `Template not found: ${safe}`, available: listTemplatesRaw({ ROOT_DIR }) };
|
|
127
136
|
}
|
|
128
137
|
|
|
129
138
|
export function listTemplates({ ROOT_DIR }) {
|
|
130
|
-
|
|
139
|
+
const raw = listTemplatesRaw({ ROOT_DIR });
|
|
140
|
+
const cwd = process.cwd();
|
|
141
|
+
const metadata = {};
|
|
142
|
+
for (const name of [...new Set([...raw.shipped, ...raw.overridden])]) {
|
|
143
|
+
metadata[name] = templateMetadata(name, { cwd, rootDir: ROOT_DIR });
|
|
144
|
+
}
|
|
145
|
+
return { ...raw, metadata };
|
|
131
146
|
}
|
|
132
147
|
|
|
133
148
|
export async function agentContract(args = {}) {
|
|
@@ -171,6 +186,18 @@ export async function orchestrationPolicy(args) {
|
|
|
171
186
|
const draftTask = route.track !== 'immediate'
|
|
172
187
|
? buildTaskPacketFromIntent(args?.request || '', { fileCount: args?.fileCount, moduleCount: args?.moduleCount })
|
|
173
188
|
: null;
|
|
189
|
+
const handoffPacket = route.track !== 'immediate'
|
|
190
|
+
? buildConstructToOrchestratorPacket({
|
|
191
|
+
request: args?.request || '',
|
|
192
|
+
route,
|
|
193
|
+
goal: args?.goal,
|
|
194
|
+
acceptanceCriteria: args?.acceptanceCriteria,
|
|
195
|
+
fileCount: args?.fileCount,
|
|
196
|
+
moduleCount: args?.moduleCount,
|
|
197
|
+
introducesContract: args?.introducesContract,
|
|
198
|
+
explicitDrive: args?.explicitDrive,
|
|
199
|
+
})
|
|
200
|
+
: null;
|
|
174
201
|
|
|
175
202
|
// Per-specialist context packets: when the caller supplied a candidate
|
|
176
203
|
// artifact list, build the role-filtered bundle each specialist should
|
|
@@ -249,9 +276,11 @@ export async function orchestrationPolicy(args) {
|
|
|
249
276
|
|
|
250
277
|
return {
|
|
251
278
|
...route,
|
|
279
|
+
specialistCatalog: buildSpecialistCatalog(),
|
|
252
280
|
approvalRequired,
|
|
253
281
|
terminalStates: TERMINAL_STATES,
|
|
254
282
|
draftTask,
|
|
283
|
+
handoffPacket,
|
|
255
284
|
...(contextPackets ? { contextPackets } : {}),
|
|
256
285
|
...(taskGraph ? { taskGraph } : {}),
|
|
257
286
|
...(intakeError ? { intakeError } : {}),
|
|
@@ -115,11 +115,23 @@ export function workflowValidate(args) {
|
|
|
115
115
|
*/
|
|
116
116
|
export async function workflowContractValidate(args) {
|
|
117
117
|
const { validateHandoff } = await import('../../contracts/validate.mjs');
|
|
118
|
+
const { enrichConstructOrchestratorHandoff } = await import('../../contracts/construct-handoff.mjs');
|
|
119
|
+
const contractId = args.id
|
|
120
|
+
?? (args.producer === 'construct' && args.consumer === 'cx-orchestrator' ? 'construct-to-orchestrator' : null);
|
|
121
|
+
let artifact = args.artifact;
|
|
122
|
+
if (contractId === 'construct-to-orchestrator' && artifact && typeof artifact === 'object') {
|
|
123
|
+
artifact = enrichConstructOrchestratorHandoff(artifact, {
|
|
124
|
+
request: args.request,
|
|
125
|
+
fileCount: args.fileCount,
|
|
126
|
+
moduleCount: args.moduleCount,
|
|
127
|
+
introducesContract: args.introducesContract,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
118
130
|
return validateHandoff({
|
|
119
131
|
producer: args.producer,
|
|
120
132
|
consumer: args.consumer,
|
|
121
133
|
id: args.id,
|
|
122
|
-
artifact
|
|
134
|
+
artifact,
|
|
123
135
|
packet: args.packet,
|
|
124
136
|
enforcement: args.enforcement || 'block',
|
|
125
137
|
});
|
|
@@ -62,6 +62,7 @@ export async function pollFreeModels(apiKey) {
|
|
|
62
62
|
(m) =>
|
|
63
63
|
m?.pricing?.prompt === "0" && m?.pricing?.completion === "0"
|
|
64
64
|
)
|
|
65
|
+
.filter(isTextOutputModel)
|
|
65
66
|
.map((m) => ({
|
|
66
67
|
id: `openrouter/${m.id}`,
|
|
67
68
|
name: m.name ?? m.id,
|
|
@@ -72,6 +73,23 @@ export async function pollFreeModels(apiKey) {
|
|
|
72
73
|
return models.sort((a, b) => score(b, "standard") - score(a, "standard"));
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
// A coding/chat tier must emit text; image-generation models (output modality
|
|
77
|
+
// "image") are never valid tier defaults. OpenRouter reports modalities under
|
|
78
|
+
// `architecture`; absent metadata is treated as text so unknown models are not
|
|
79
|
+
// dropped. Vision models (image INPUT, text OUTPUT) remain eligible.
|
|
80
|
+
|
|
81
|
+
export function isTextOutputModel(model = {}) {
|
|
82
|
+
const arch = model.architecture || {};
|
|
83
|
+
let output = arch.output_modalities;
|
|
84
|
+
if (!Array.isArray(output) && typeof arch.modality === "string" && arch.modality.includes("->")) {
|
|
85
|
+
output = arch.modality.split("->")[1].split("+").map((s) => s.trim());
|
|
86
|
+
}
|
|
87
|
+
if (Array.isArray(output) && output.length) {
|
|
88
|
+
return output.includes("text") && !output.includes("image");
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
75
93
|
/**
|
|
76
94
|
* Score a model for a given tier (higher is better).
|
|
77
95
|
* @param {object} model – Model object as returned by `pollFreeModels`.
|
package/lib/model-registry.mjs
CHANGED
|
@@ -1,233 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/model-registry.mjs —
|
|
2
|
+
* lib/model-registry.mjs — compatibility re-exports for model tier resolution.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 3. Simple env-only overrides for emergencies
|
|
8
|
-
* 4. Clear error messages when config is missing
|
|
4
|
+
* Canonical implementation lives in lib/model-router.mjs. This module keeps
|
|
5
|
+
* the legacy import path stable while CONSTRUCT_MODEL_* env aliases remain
|
|
6
|
+
* supported for one release cycle via normalizeEnvAssignments in the router.
|
|
9
7
|
*/
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* 1. Environment variables (CX_MODEL_*): Emergency overrides
|
|
21
|
-
* 2. User config (~/.construct/config.env): Personal defaults
|
|
22
|
-
* 3. Registry (specialists/registry.json): Project recommendations
|
|
23
|
-
*
|
|
24
|
-
* Construct ships with NO implicit model defaults (ADR-0027,
|
|
25
|
-
* tests/model-router-no-defaults.test.mjs). When none of the three sources
|
|
26
|
-
* resolves a tier, that tier returns null with source 'not configured'.
|
|
27
|
-
* Callers must surface a clear remediation hint rather than silently
|
|
28
|
-
* substituting a vendor-specific default.
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
const ENV_PREFIX = 'CX_MODEL';
|
|
32
|
-
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
// Core resolution function
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
|
|
37
|
-
export function resolveModelTiers(options = {}) {
|
|
38
|
-
const {
|
|
39
|
-
env = process.env,
|
|
40
|
-
registryPath = null,
|
|
41
|
-
strict = false, // If true, require all tiers to be configured
|
|
42
|
-
} = options;
|
|
43
|
-
|
|
44
|
-
const result = {
|
|
45
|
-
reasoning: null,
|
|
46
|
-
standard: null,
|
|
47
|
-
fast: null,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const sources = {};
|
|
51
|
-
const errors = [];
|
|
52
|
-
|
|
53
|
-
for (const tier of ['reasoning', 'standard', 'fast']) {
|
|
54
|
-
const envKey = `${ENV_PREFIX}_${tier.toUpperCase()}`;
|
|
55
|
-
|
|
56
|
-
// Environment variable (highest priority)
|
|
57
|
-
if (env[envKey]) {
|
|
58
|
-
result[tier] = env[envKey];
|
|
59
|
-
sources[tier] = 'env';
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// User config (read from env which was loaded by env-config)
|
|
64
|
-
const configKey = `CONSTRUCT_MODEL_${tier.toUpperCase()}`;
|
|
65
|
-
if (env[configKey]) {
|
|
66
|
-
result[tier] = env[configKey];
|
|
67
|
-
sources[tier] = 'config';
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Registry
|
|
72
|
-
if (registryPath && fs.existsSync(registryPath)) {
|
|
73
|
-
try {
|
|
74
|
-
const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
75
|
-
const registryModel = registry.models?.[tier]?.primary;
|
|
76
|
-
if (registryModel) {
|
|
77
|
-
result[tier] = registryModel;
|
|
78
|
-
sources[tier] = 'registry';
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
} catch (error) {
|
|
82
|
-
errors.push(`Failed to read registry: ${error.message}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// No source resolved this tier — leave null + 'not configured' so callers
|
|
87
|
-
// can surface a clear remediation hint. Aligns with the wired path
|
|
88
|
-
// (lib/model-router.mjs:resolveTierAssignments) and the no-defaults
|
|
89
|
-
// contract locked in by tests/model-router-no-defaults.test.mjs.
|
|
90
|
-
|
|
91
|
-
result[tier] = null;
|
|
92
|
-
sources[tier] = 'not configured';
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const configured = Object.values(result).filter(Boolean).length;
|
|
96
|
-
|
|
97
|
-
if (strict && configured < 3) {
|
|
98
|
-
const missing = Object.entries(result)
|
|
99
|
-
.filter(([, v]) => !v)
|
|
100
|
-
.map(([k]) => k);
|
|
101
|
-
errors.push(`Missing configuration for tiers: ${missing.join(', ')}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
models: result,
|
|
106
|
-
sources,
|
|
107
|
-
configured,
|
|
108
|
-
complete: configured === 3,
|
|
109
|
-
errors: errors.length > 0 ? errors : null,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ---------------------------------------------------------------------------
|
|
114
|
-
// Quick access helpers
|
|
115
|
-
// ---------------------------------------------------------------------------
|
|
116
|
-
|
|
117
|
-
export function getModelForTier(tier, options = {}) {
|
|
118
|
-
const resolved = resolveModelTiers(options);
|
|
119
|
-
return resolved.models[tier];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function getModelSource(tier, options = {}) {
|
|
123
|
-
const resolved = resolveModelTiers(options);
|
|
124
|
-
return resolved.sources[tier] || 'unknown';
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
// Configuration helpers
|
|
129
|
-
// ---------------------------------------------------------------------------
|
|
130
|
-
|
|
131
|
-
export function validateModelTiers(options = {}) {
|
|
132
|
-
const resolved = resolveModelTiers({ ...options, strict: true });
|
|
133
|
-
const unconfigured = Object.entries(resolved.sources)
|
|
134
|
-
.filter(([, source]) => source === 'not configured')
|
|
135
|
-
.map(([tier]) => tier);
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
valid: resolved.complete && !resolved.errors,
|
|
139
|
-
errors: resolved.errors,
|
|
140
|
-
warnings: unconfigured.length > 0
|
|
141
|
-
? [`Unconfigured tier${unconfigured.length === 1 ? '' : 's'}: ${unconfigured.join(', ')}. Run 'construct models --apply' or set CX_MODEL_<TIER>.`]
|
|
142
|
-
: [],
|
|
143
|
-
resolution: resolved,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function formatModelStatus(options = {}) {
|
|
148
|
-
const resolved = resolveModelTiers(options);
|
|
149
|
-
|
|
150
|
-
let output = 'Model Configuration:\n\n';
|
|
151
|
-
|
|
152
|
-
for (const tier of ['reasoning', 'standard', 'fast']) {
|
|
153
|
-
const model = resolved.models[tier];
|
|
154
|
-
const source = resolved.sources[tier];
|
|
155
|
-
const icon = source === 'not configured' ? '⚠' : '✓';
|
|
156
|
-
const display = model ?? '(not configured)';
|
|
157
|
-
|
|
158
|
-
output += `${icon} ${tier.padEnd(10)} ${display}\n`;
|
|
159
|
-
output += ` Source: ${source}\n\n`;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const unconfigured = Object.entries(resolved.sources)
|
|
163
|
-
.filter(([, source]) => source === 'not configured')
|
|
164
|
-
.map(([tier]) => tier);
|
|
165
|
-
if (unconfigured.length > 0) {
|
|
166
|
-
output += `\nUnconfigured: ${unconfigured.join(', ')}. To configure:\n`;
|
|
167
|
-
output += ' construct models --apply (interactive, picks from available providers)\n';
|
|
168
|
-
output += ' export CX_MODEL_REASONING=... (env override)\n';
|
|
169
|
-
output += ' export CX_MODEL_STANDARD=...\n';
|
|
170
|
-
output += ' export CX_MODEL_FAST=...\n';
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return output;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// ---------------------------------------------------------------------------
|
|
177
|
-
// Backwards compatibility
|
|
178
|
-
// ---------------------------------------------------------------------------
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Legacy wrapper for code that expects the old interface.
|
|
182
|
-
* New code should use resolveModelTiers() directly.
|
|
183
|
-
*/
|
|
184
|
-
export function readCurrentModels(envPath, registryModels = {}) {
|
|
185
|
-
// Maintains backwards compatibility with existing code
|
|
186
|
-
// while using the new resolution logic internally
|
|
187
|
-
|
|
188
|
-
const env = {};
|
|
189
|
-
|
|
190
|
-
// Load from env file if provided
|
|
191
|
-
if (envPath && fs.existsSync(envPath)) {
|
|
192
|
-
const content = fs.readFileSync(envPath, 'utf8');
|
|
193
|
-
for (const line of content.split('\n')) {
|
|
194
|
-
const match = line.match(/^([^=]+)=(.*)$/);
|
|
195
|
-
if (match) {
|
|
196
|
-
env[match[1].trim()] = match[2].trim();
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Merge with process.env
|
|
202
|
-
Object.assign(env, process.env);
|
|
203
|
-
|
|
204
|
-
const resolved = resolveModelTiers({ env });
|
|
205
|
-
|
|
206
|
-
// Return in legacy format
|
|
207
|
-
return {
|
|
208
|
-
reasoning: resolved.models.reasoning,
|
|
209
|
-
standard: resolved.models.standard,
|
|
210
|
-
fast: resolved.models.fast,
|
|
211
|
-
// Legacy code expects these fields
|
|
212
|
-
_resolved: resolved,
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export function setTierModel(tier, model, envPath) {
|
|
217
|
-
// Simplified interface for setting a model
|
|
218
|
-
// In practice, this should update the user config file
|
|
219
|
-
|
|
220
|
-
if (!['reasoning', 'standard', 'fast'].includes(tier)) {
|
|
221
|
-
throw new Error(`Invalid tier: ${tier}. Must be reasoning, standard, or fast.`);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const envKey = `${ENV_PREFIX}_${tier.toUpperCase()}`;
|
|
225
|
-
|
|
226
|
-
return {
|
|
227
|
-
success: true,
|
|
228
|
-
tier,
|
|
229
|
-
model,
|
|
230
|
-
envKey,
|
|
231
|
-
instruction: `Add to your shell profile: export ${envKey}="${model}"`,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
9
|
+
export {
|
|
10
|
+
resolveModelTiers,
|
|
11
|
+
getModelForTier,
|
|
12
|
+
getModelSource,
|
|
13
|
+
validateModelTiers,
|
|
14
|
+
formatModelStatus,
|
|
15
|
+
readCurrentModels,
|
|
16
|
+
setTierModel,
|
|
17
|
+
} from './model-router.mjs';
|