@geraldmaron/construct 1.1.1 → 1.2.1
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 -19
- package/bin/construct +487 -87
- 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 +258 -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/artifact-workflow.mjs +252 -0
- package/lib/audit-skills.mjs +4 -0
- package/lib/audit-specialists.mjs +285 -0
- package/lib/audit-trail.mjs +1 -1
- package/lib/auto-docs.mjs +90 -19
- 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 +246 -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 +146 -15
- package/lib/cli-service-inventory.mjs +42 -0
- package/lib/comment-lint.mjs +6 -4
- package/lib/config/intake-policy.mjs +209 -0
- package/lib/config/project-config.mjs +11 -2
- package/lib/config/schema.mjs +86 -6
- package/lib/config/source-targets.mjs +311 -0
- 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 +598 -140
- package/lib/diagram-export.mjs +192 -0
- package/lib/diagram.mjs +13 -11
- package/lib/docs-verify.mjs +25 -7
- package/lib/doctor/index.mjs +3 -1
- package/lib/doctor/project-adapters.mjs +44 -0
- package/lib/doctor/source-checkout.mjs +16 -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/document-export.mjs +384 -38
- package/lib/document-extract.mjs +44 -4
- package/lib/document-ingest.mjs +29 -20
- package/lib/embed/auto-sources.mjs +44 -0
- package/lib/embed/cli.mjs +4 -3
- package/lib/embed/daemon.mjs +18 -46
- package/lib/embed/demand-fetch.mjs +50 -19
- package/lib/embed/inbox.mjs +6 -10
- package/lib/embed/worker.mjs +4 -3
- package/lib/embedded-contract/index.mjs +11 -0
- 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/export-branding.mjs +32 -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/health-check.mjs +90 -76
- package/lib/hooks/artifact-release-gate.mjs +43 -0
- 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 +18 -16
- 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 +50 -119
- 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/constants.mjs +29 -0
- package/lib/intake/daemon.mjs +2 -0
- package/lib/intake/git-queue.mjs +9 -8
- package/lib/intake/intake-config.mjs +52 -105
- package/lib/intake/legacy-paths.mjs +5 -0
- 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 +103 -10
- 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/embedded-contract.mjs +14 -0
- 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 +373 -92
- 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/playwright-demo.mjs +254 -0
- package/lib/project-init-shared.mjs +4 -1
- package/lib/prompt-composer.js +7 -3
- 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 +259 -9
- package/lib/server/rate-limit.mjs +5 -3
- package/lib/server/webhook.mjs +3 -2
- package/lib/service-manager.mjs +60 -8
- package/lib/setup.mjs +70 -7
- 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/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 +28 -14
- package/personas/construct.md +2 -2
- 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 +41 -28
- 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 +7 -2
- 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 +489 -0
- package/specialists/artifact-manifest.schema.json +83 -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-reference.docx +0 -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 +8 -8
- 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/bin/construct
CHANGED
|
@@ -12,10 +12,10 @@ import os from 'node:os';
|
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import { spawnSync } from 'node:child_process';
|
|
14
14
|
|
|
15
|
-
import { CLI_COMMANDS,
|
|
15
|
+
import { CLI_COMMANDS, CATEGORY_ORDER, formatCommandHelp } from '../lib/cli-commands.mjs';
|
|
16
16
|
import { buildStatus, formatStatusReport } from '../lib/status.mjs';
|
|
17
17
|
import { validateRegistry } from '../lib/validator.mjs';
|
|
18
|
-
import { readCurrentModels, readOpenRouterApiKeyFromOpenCodeConfig, applyToEnv, resetEnv,
|
|
18
|
+
import { readCurrentModels, readOpenRouterApiKeyFromOpenCodeConfig, applyToEnv, resetEnv, setModelWithTierInference } from '../lib/model-router.mjs';
|
|
19
19
|
import { pollFreeModels, topForTier, selectForTier } from '../lib/model-free-selector.mjs';
|
|
20
20
|
import { cmdMcpList, cmdMcpAdd, cmdMcpRemove, cmdMcpInfo } from '../lib/mcp-manager.mjs';
|
|
21
21
|
import { cmdOllama } from '../lib/ollama-manager.mjs';
|
|
@@ -26,7 +26,8 @@ import { runUpdate } from '../lib/update.mjs';
|
|
|
26
26
|
import { runUpgrade } from '../lib/upgrade.mjs';
|
|
27
27
|
import { runUninstall } from '../lib/uninstall/uninstall.mjs';
|
|
28
28
|
import { maybeFirstInvocationProbe } from '../lib/install/first-invocation.mjs';
|
|
29
|
-
import { loadConstructEnv, getUserEnvPath
|
|
29
|
+
import { loadConstructEnv, getUserEnvPath } from '../lib/env-config.mjs';
|
|
30
|
+
import { ensureConstructCredentials } from '../lib/providers/credential-bootstrap.mjs';
|
|
30
31
|
import {
|
|
31
32
|
loadProjectConfig,
|
|
32
33
|
writeProjectConfig,
|
|
@@ -39,30 +40,30 @@ import {
|
|
|
39
40
|
import { validateProjectConfig } from '../lib/config/schema.mjs';
|
|
40
41
|
import {
|
|
41
42
|
DEPLOYMENT_MODES,
|
|
42
|
-
DEFAULT_DEPLOYMENT_MODE,
|
|
43
43
|
DEPLOYMENT_MODE_ENV_KEY,
|
|
44
44
|
describeDeploymentMode,
|
|
45
|
-
describeResourceLine,
|
|
46
45
|
getDeploymentMode,
|
|
47
|
-
isValidDeploymentMode,
|
|
48
46
|
resolveResourceMode,
|
|
49
47
|
} from '../lib/deployment-mode.mjs';
|
|
50
48
|
import { runDistillCli } from '../lib/distill.mjs';
|
|
51
49
|
import { runIngestCli } from '../lib/document-ingest.mjs';
|
|
52
50
|
import { runInferCli } from '../lib/schema-infer.mjs';
|
|
53
|
-
import {
|
|
51
|
+
import { generateCompletions, getCompletionScript } from '../lib/completions.mjs';
|
|
54
52
|
import { runHeadhunt, listTeamTemplates } from '../lib/headhunt.mjs';
|
|
55
53
|
import {
|
|
56
54
|
getLockStatus,
|
|
57
55
|
cleanupStaleLock,
|
|
58
56
|
cleanupStaleQueue,
|
|
59
57
|
readQueue,
|
|
60
|
-
formatStatus,
|
|
61
58
|
} from '../lib/beads-lock.mjs';
|
|
62
59
|
import { runBd, getHumanStatus, acquireMergeSlot, releaseMergeSlot } from '../lib/beads-client.mjs';
|
|
63
60
|
import { auditSkills, runAuditSkillsCli } from '../lib/audit-skills.mjs';
|
|
61
|
+
import { auditSpecialists, runAuditSpecialistsCli } from '../lib/audit-specialists.mjs';
|
|
62
|
+
import { runCapabilityLedgerAuditCli } from '../lib/capability-ledger.mjs';
|
|
63
|
+
import { runCorpusInventoryAuditCli } from '../lib/test-corpus-inventory.mjs';
|
|
64
|
+
import { runArtifactValidateCli } from '../lib/artifact-release-gate.mjs';
|
|
64
65
|
import { runTeamReviewCli } from '../lib/telemetry/team-rollup.mjs';
|
|
65
|
-
import {
|
|
66
|
+
import { startDashboard, startServices } from '../lib/service-manager.mjs';
|
|
66
67
|
import { readCostLog, summarizeCostData, formatCostReport, clearCostLog } from '../lib/cost.mjs';
|
|
67
68
|
import { readEfficiencyLog, summarizeEfficiencyData, formatEfficiencyReport } from '../lib/efficiency.mjs';
|
|
68
69
|
import { resolveColors } from '../lib/term-format.mjs';
|
|
@@ -74,6 +75,7 @@ import { runPressureRelease } from '../lib/runtime-pressure.mjs';
|
|
|
74
75
|
|
|
75
76
|
const ROOT_DIR = path.resolve(import.meta.dirname, '..');
|
|
76
77
|
const HOME = os.homedir();
|
|
78
|
+
ensureConstructCredentials({ env: process.env, cwd: ROOT_DIR, home: HOME });
|
|
77
79
|
const ENV = loadConstructEnv({ rootDir: ROOT_DIR, homeDir: HOME, env: process.env });
|
|
78
80
|
for (const [key, value] of Object.entries(ENV)) {
|
|
79
81
|
if (!(key in process.env)) process.env[key] = value;
|
|
@@ -170,41 +172,51 @@ function runNodeScript(scriptPath, args = [], extraEnv = {}, { exitOnError = tru
|
|
|
170
172
|
}
|
|
171
173
|
|
|
172
174
|
async function cmdRegistryStatus(args = []) {
|
|
173
|
-
const {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (!existsSync(matrixPath)) {
|
|
178
|
-
errorln('Capability matrix not found. Run "construct sync" or ensure tests/registry/capability-matrix.json exists.');
|
|
179
|
-
process.exit(1);
|
|
180
|
-
}
|
|
175
|
+
const { runRegistryStatus } = await import('../lib/registry/cli.mjs');
|
|
176
|
+
const code = await runRegistryStatus(args, { rootDir: ROOT_DIR, println, errorln });
|
|
177
|
+
if (code) process.exit(code);
|
|
178
|
+
}
|
|
181
179
|
|
|
182
|
-
|
|
183
|
-
const
|
|
180
|
+
async function cmdRegistryValidate(args = []) {
|
|
181
|
+
const { runRegistryValidate } = await import('../lib/registry/cli.mjs');
|
|
182
|
+
const code = await runRegistryValidate(args, { rootDir: ROOT_DIR, println, errorln });
|
|
183
|
+
if (code) process.exit(code);
|
|
184
|
+
}
|
|
184
185
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
async function cmdRegistryGenerateDocs(args = []) {
|
|
187
|
+
const { runRegistryGenerateDocs } = await import('../lib/registry/cli.mjs');
|
|
188
|
+
const code = await runRegistryGenerateDocs(args, { rootDir: ROOT_DIR, println, errorln });
|
|
189
|
+
if (code) process.exit(code);
|
|
190
|
+
}
|
|
189
191
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
192
|
+
async function cmdRulesUsage(args = []) {
|
|
193
|
+
const { summarizeRuleCalls } = await import('../lib/telemetry/rule-calls.mjs');
|
|
194
|
+
const { summarizeHookCalls } = await import('../lib/telemetry/hook-calls.mjs');
|
|
195
|
+
const sub = args[0] || 'usage';
|
|
196
|
+
const sinceFlag = args.find((a) => a.startsWith('--since='));
|
|
197
|
+
const since = sinceFlag ? sinceFlag.split('=')[1] : '30d';
|
|
193
198
|
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
println(`
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
for (const [
|
|
201
|
-
const icon = status.quality_score === null ? '⚪' : status.quality_score >= 0.9 ? '🟢' : status.quality_score >= 0.7 ? '🟡' : '🔴';
|
|
202
|
-
const score = status.quality_score !== null ? `${(status.quality_score * 100).toFixed(0)}%` : 'N/A';
|
|
203
|
-
const date = status.last_validated ? new Date(status.last_validated).toLocaleDateString() : 'never';
|
|
204
|
-
println(` ${icon} ${name.padEnd(10)} | Score: ${score.padEnd(5)} | Validated: ${date}`);
|
|
205
|
-
}
|
|
199
|
+
if (sub === 'usage') {
|
|
200
|
+
const rules = summarizeRuleCalls({ since });
|
|
201
|
+
const hooks = summarizeHookCalls({ since });
|
|
202
|
+
println(`Rule references — last ${since} (${rules.total} events)\n`);
|
|
203
|
+
const ruleRows = Object.entries(rules.rules).sort((a, b) => b[1] - a[1]);
|
|
204
|
+
if (ruleRows.length === 0) println(' No rule reference events in the window.');
|
|
205
|
+
for (const [path, count] of ruleRows) println(` ${path.padEnd(50)} refs=${count}`);
|
|
206
206
|
println('');
|
|
207
|
+
println(`Hook fires — last ${since} (${hooks.totalEvents ?? 0} events)\n`);
|
|
208
|
+
const hookEntries = Object.entries(hooks.hooks ?? {}).sort((a, b) => b[1].calls - a[1].calls);
|
|
209
|
+
if (!hookEntries.length) {
|
|
210
|
+
println(' No hook events in window.');
|
|
211
|
+
} else {
|
|
212
|
+
for (const [id, s] of hookEntries) {
|
|
213
|
+
println(` ${id.padEnd(40)} calls=${s.calls} blocked=${s.blocked ?? 0} errors=${s.errors ?? 0}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return;
|
|
207
217
|
}
|
|
218
|
+
errorln(`Unknown rules subcommand: ${sub}. Available: usage`);
|
|
219
|
+
process.exit(1);
|
|
208
220
|
}
|
|
209
221
|
|
|
210
222
|
async function cmdStatus() {
|
|
@@ -255,22 +267,6 @@ async function cmdStatus() {
|
|
|
255
267
|
} catch { /* best-effort surface; never block status */ }
|
|
256
268
|
}
|
|
257
269
|
|
|
258
|
-
async function cmdShow() {
|
|
259
|
-
const status = await buildStatus({ rootDir: ROOT_DIR, cwd: process.cwd(), homeDir: HOME, env: process.env });
|
|
260
|
-
const dashboard = readDashboardState(HOME);
|
|
261
|
-
println('');
|
|
262
|
-
println('Construct Services');
|
|
263
|
-
println('══════════════════');
|
|
264
|
-
println('');
|
|
265
|
-
if (dashboard) println(` • Managed dashboard PID ${dashboard.pid} on ${dashboard.url}`);
|
|
266
|
-
for (const service of status.system.services) {
|
|
267
|
-
const icon = service.status === 'healthy' ? '✓' : service.status === 'degraded' ? '⚠' : '✗';
|
|
268
|
-
const note = service.note ? ` (${service.note})` : '';
|
|
269
|
-
println(` ${icon} ${service.name.padEnd(28)} ${service.url}${note}`);
|
|
270
|
-
}
|
|
271
|
-
println('');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
270
|
async function cmdSync(args) {
|
|
275
271
|
const { startOpLog } = await import('../lib/op-log.mjs');
|
|
276
272
|
const opLog = startOpLog('sync', { homeDir: HOME });
|
|
@@ -504,15 +500,19 @@ async function cmdDoctor() {
|
|
|
504
500
|
|
|
505
501
|
const checks = [];
|
|
506
502
|
const add = (label, pass, optional = false) => checks.push({ label, pass, optional });
|
|
503
|
+
const { isConstructSourceCheckout } = await import('../lib/doctor/source-checkout.mjs');
|
|
504
|
+
const isSourceCheckout = isConstructSourceCheckout(ROOT_DIR);
|
|
507
505
|
add('registry.json exists', fs.existsSync(path.join(ROOT_DIR, 'specialists', 'registry.json')));
|
|
508
506
|
add('sync-specialists.mjs exists', fs.existsSync(path.join(ROOT_DIR, 'scripts', 'sync-specialists.mjs')));
|
|
509
507
|
|
|
510
|
-
// Dashboard static build output
|
|
511
|
-
// emits a static export into lib/server/static/ via its postbuild script;
|
|
512
|
-
// an empty static dir means lib/server/index.mjs will serve 404s.
|
|
508
|
+
// Dashboard static build output is absent from published npm installs.
|
|
513
509
|
|
|
514
|
-
|
|
515
|
-
|
|
510
|
+
if (isSourceCheckout) {
|
|
511
|
+
const dashboardIndex = path.join(ROOT_DIR, 'lib', 'server', 'static', 'index.html');
|
|
512
|
+
add('Dashboard built (lib/server/static/index.html)', fs.existsSync(dashboardIndex));
|
|
513
|
+
} else {
|
|
514
|
+
add('Dashboard built (skipped — published package)', true, true);
|
|
515
|
+
}
|
|
516
516
|
|
|
517
517
|
// Project .gitignore must cover .cx/. Auto-fix via --fix-gitignore.
|
|
518
518
|
// Only checks when cwd looks like a Construct project (has .cx/ or
|
|
@@ -677,6 +677,14 @@ async function cmdDoctor() {
|
|
|
677
677
|
|
|
678
678
|
add('Node.js 20+ (recommended)', Number.parseInt(process.versions.node.split('.')[0], 10) >= 20);
|
|
679
679
|
add('npm available', true);
|
|
680
|
+
const npmDevdir = process.env.npm_config_devdir ?? process.env.NPM_CONFIG_DEVDIR;
|
|
681
|
+
if (npmDevdir) {
|
|
682
|
+
add(
|
|
683
|
+
'npm env: npm_config_devdir set (Cursor sandbox) — npm 11.2+ warns on every `npm run`. Unset: unset npm_config_devdir NPM_CONFIG_DEVDIR. Or run scripts directly: node scripts/... / construct ...',
|
|
684
|
+
false,
|
|
685
|
+
true,
|
|
686
|
+
);
|
|
687
|
+
}
|
|
680
688
|
const constructOnPath = spawnSync('zsh', ['-lc', 'command -v construct'], { encoding: 'utf8', env: process.env });
|
|
681
689
|
const constructPath = constructOnPath.status === 0 ? constructOnPath.stdout.trim() : '';
|
|
682
690
|
const constructVersion = constructPath
|
|
@@ -854,6 +862,21 @@ async function cmdDoctor() {
|
|
|
854
862
|
add('Headhunt classifier agents exist in registry', classifierOrphans.length === 0);
|
|
855
863
|
const skillAudit = auditSkills({ rootDir: ROOT_DIR, silent: true });
|
|
856
864
|
add('No declared skills missing on disk', skillAudit.pass, false);
|
|
865
|
+
if (isSourceCheckout) {
|
|
866
|
+
const specialistAudit = auditSpecialists({ rootDir: ROOT_DIR, silent: true });
|
|
867
|
+
add('Specialist/skill audit cross-checks', specialistAudit.pass, false);
|
|
868
|
+
|
|
869
|
+
// The catalog is the public-service source of truth; doctor joins it to runtime dispatch
|
|
870
|
+
// and the generated reference so a command that is unroutable or undocumented surfaces here.
|
|
871
|
+
|
|
872
|
+
const { buildCliServiceInventory } = await import('../lib/cli-service-inventory.mjs');
|
|
873
|
+
const cliDrift = buildCliServiceInventory({ rootDir: ROOT_DIR })
|
|
874
|
+
.filter((entry) => !entry.runnable || !entry.documented);
|
|
875
|
+
add('Public CLI dispatch/reference parity', cliDrift.length === 0, false);
|
|
876
|
+
} else {
|
|
877
|
+
add('Specialist/skill audit cross-checks (skipped — published package)', true, true);
|
|
878
|
+
add('Public CLI dispatch/reference parity (skipped — published package)', true, true);
|
|
879
|
+
}
|
|
857
880
|
const { lintResearchRepo } = await import('../lib/research-lint.mjs');
|
|
858
881
|
const researchLintResults = lintResearchRepo({ rootDir: process.cwd() });
|
|
859
882
|
add('Research artifacts meet minimum evidence structure', researchLintResults.every((entry) => entry.errors.length === 0), true);
|
|
@@ -916,6 +939,14 @@ async function cmdDoctor() {
|
|
|
916
939
|
add('Cross-surface adapter parity', false);
|
|
917
940
|
}
|
|
918
941
|
|
|
942
|
+
try {
|
|
943
|
+
const { checkProjectAdaptersForDoctor } = await import('../lib/doctor/project-adapters.mjs');
|
|
944
|
+
const projectAdapters = checkProjectAdaptersForDoctor({ rootDir: ROOT_DIR, projectDir: process.cwd() });
|
|
945
|
+
add(projectAdapters.label, projectAdapters.ok, projectAdapters.warning);
|
|
946
|
+
} catch {
|
|
947
|
+
add('Project adapters', false, true);
|
|
948
|
+
}
|
|
949
|
+
|
|
919
950
|
try {
|
|
920
951
|
const { validateSkills } = await import('../lib/validators/skills.mjs');
|
|
921
952
|
const skillsRoot = path.join(ROOT_DIR, 'skills');
|
|
@@ -928,6 +959,28 @@ async function cmdDoctor() {
|
|
|
928
959
|
add('Skill structure', false, true);
|
|
929
960
|
}
|
|
930
961
|
|
|
962
|
+
try {
|
|
963
|
+
const { lintWorkflowSkillVerification } = await import('../lib/registry/skill-verification.mjs');
|
|
964
|
+
const wf = lintWorkflowSkillVerification(path.join(ROOT_DIR, 'skills'));
|
|
965
|
+
const wfLabel = wf.warnings.length
|
|
966
|
+
? `Workflow skill verification (${wf.checked} checked, ${wf.warnings.length} missing bars)`
|
|
967
|
+
: `Workflow skill verification (${wf.checked} checked)`;
|
|
968
|
+
add(wfLabel, wf.valid && wf.warnings.length === 0, true);
|
|
969
|
+
} catch {
|
|
970
|
+
add('Workflow skill verification', false, true);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
try {
|
|
974
|
+
const { validateCapabilityRegistry } = await import('../lib/registry/validate.mjs');
|
|
975
|
+
const reg = validateCapabilityRegistry({ rootDir: ROOT_DIR });
|
|
976
|
+
const regLabel = reg.warnings.length
|
|
977
|
+
? `Capability registry (${reg.count} entries, ${reg.warnings.length} warnings)`
|
|
978
|
+
: `Capability registry (${reg.count} entries)`;
|
|
979
|
+
add(regLabel, reg.valid, reg.errors.length > 0);
|
|
980
|
+
} catch {
|
|
981
|
+
add('Capability registry', false, true);
|
|
982
|
+
}
|
|
983
|
+
|
|
931
984
|
try {
|
|
932
985
|
const { describeBreakers, STATES } = await import('../lib/providers/circuit-breaker.mjs');
|
|
933
986
|
const breakers = describeBreakers();
|
|
@@ -1036,7 +1089,7 @@ async function cmdDoctor() {
|
|
|
1036
1089
|
}
|
|
1037
1090
|
|
|
1038
1091
|
// Agent contracts integrity
|
|
1039
|
-
const { getAllContracts
|
|
1092
|
+
const { getAllContracts } = await import('../lib/specialist-contracts.mjs');
|
|
1040
1093
|
const contracts = getAllContracts();
|
|
1041
1094
|
add('Agent contracts loaded', contracts.length > 0);
|
|
1042
1095
|
if (contracts.length > 0) {
|
|
@@ -1242,7 +1295,7 @@ async function cmdDev(args = []) {
|
|
|
1242
1295
|
opLog.event('selection', { services: selected ? [...selected] : 'all' });
|
|
1243
1296
|
|
|
1244
1297
|
// Auto-magic health check — catch missing prerequisites before starting services
|
|
1245
|
-
const { quickHealthCheck,
|
|
1298
|
+
const { quickHealthCheck, resolveCredentials } = await import('../lib/health-check.mjs');
|
|
1246
1299
|
|
|
1247
1300
|
// Resolve credentials from env, .env files, and 1Password before starting services
|
|
1248
1301
|
const resolvedCreds = await resolveCredentials({ homeDir: HOME });
|
|
@@ -1524,17 +1577,19 @@ async function cmdExport(args) {
|
|
|
1524
1577
|
const toFlag = args.find((a) => a.startsWith('--to='))?.split('=')[1];
|
|
1525
1578
|
const outFlag = args.find((a) => a.startsWith('--output='))?.split('=')[1];
|
|
1526
1579
|
const detectOnly = args.includes('--detect');
|
|
1580
|
+
const figures = !args.includes('--no-figures');
|
|
1581
|
+
const branding = args.includes('--plain') || args.includes('--no-brand') ? 'plain' : 'construct';
|
|
1527
1582
|
|
|
1528
1583
|
const { detect, exportMarkdown, EXPORT_FORMATS } = await import('../lib/document-export.mjs');
|
|
1529
1584
|
|
|
1530
1585
|
if (detectOnly) {
|
|
1531
1586
|
const format = toFlag || 'pdf';
|
|
1532
|
-
println(JSON.stringify(detect(format), null, 2));
|
|
1587
|
+
println(JSON.stringify(detect(format, process.env, { branding }), null, 2));
|
|
1533
1588
|
return;
|
|
1534
1589
|
}
|
|
1535
1590
|
|
|
1536
1591
|
if (!inputPath) {
|
|
1537
|
-
errorln('Usage: construct export <markdown-file> --to=<pdf|docx|html> [--output=<path>] [--detect]');
|
|
1592
|
+
errorln('Usage: construct export <markdown-file> --to=<pdf|docx|html> [--output=<path>] [--figures|--no-figures] [--plain] [--detect]');
|
|
1538
1593
|
process.exit(1);
|
|
1539
1594
|
}
|
|
1540
1595
|
if (!toFlag) {
|
|
@@ -1544,7 +1599,14 @@ async function cmdExport(args) {
|
|
|
1544
1599
|
|
|
1545
1600
|
const resolvedIn = path.resolve(process.cwd(), inputPath);
|
|
1546
1601
|
const resolvedOut = outFlag ? path.resolve(process.cwd(), outFlag) : null;
|
|
1547
|
-
const result = exportMarkdown({
|
|
1602
|
+
const result = exportMarkdown({
|
|
1603
|
+
inputPath: resolvedIn,
|
|
1604
|
+
outputPath: resolvedOut,
|
|
1605
|
+
format: toFlag,
|
|
1606
|
+
figures: toFlag === 'pdf' ? figures : false,
|
|
1607
|
+
branding,
|
|
1608
|
+
repoRoot: ROOT_DIR,
|
|
1609
|
+
});
|
|
1548
1610
|
if (!result.ok) {
|
|
1549
1611
|
errorln(result.message);
|
|
1550
1612
|
process.exit(result.missing?.length ? 2 : 1);
|
|
@@ -1552,6 +1614,36 @@ async function cmdExport(args) {
|
|
|
1552
1614
|
ok(result.message);
|
|
1553
1615
|
}
|
|
1554
1616
|
|
|
1617
|
+
async function cmdPublish(args) {
|
|
1618
|
+
const { runPublishCli } = await import('../lib/publish.mjs');
|
|
1619
|
+
const { exitCode } = await runPublishCli(args, { cwd: process.cwd(), repoRoot: ROOT_DIR });
|
|
1620
|
+
process.exit(exitCode ?? 0);
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
async function cmdTools(args) {
|
|
1624
|
+
const [sub, ...rest] = args;
|
|
1625
|
+
if (sub !== 'detect') {
|
|
1626
|
+
errorln('Usage: construct tools detect [--json] [--figures] [--no-figures] [--demo=NAME] [--dashboard-demo=NAME]');
|
|
1627
|
+
process.exit(1);
|
|
1628
|
+
}
|
|
1629
|
+
const { detectPublishPipeline, formatToolsDetectReport } = await import('../lib/publish-tooling.mjs');
|
|
1630
|
+
const json = rest.includes('--json');
|
|
1631
|
+
const figures = !rest.includes('--no-figures');
|
|
1632
|
+
const demos = rest.filter((a) => a.startsWith('--demo=')).map((a) => a.slice(7));
|
|
1633
|
+
const dashboardDemos = rest.filter((a) => a.startsWith('--dashboard-demo=')).map((a) => a.slice(17));
|
|
1634
|
+
const detection = detectPublishPipeline({
|
|
1635
|
+
format: 'pdf',
|
|
1636
|
+
includeFigures: figures,
|
|
1637
|
+
includeTerminalDemo: demos.length > 0,
|
|
1638
|
+
includeDashboardDemo: dashboardDemos.length > 0,
|
|
1639
|
+
cwd: process.cwd(),
|
|
1640
|
+
repoRoot: ROOT_DIR,
|
|
1641
|
+
});
|
|
1642
|
+
if (json) println(formatToolsDetectReport(detection, { json: true }));
|
|
1643
|
+
else println(formatToolsDetectReport(detection));
|
|
1644
|
+
process.exit(detection.present ? 0 : 2);
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1555
1647
|
async function cmdInfer(args) {
|
|
1556
1648
|
try {
|
|
1557
1649
|
const result = await runInferCli(args, { cwd: process.cwd() });
|
|
@@ -1667,7 +1759,7 @@ async function cmdPrune(args) {
|
|
|
1667
1759
|
println(`.cx/ now at ${Math.round(after.totalCxBytes / 1024 / 1024)}MB / ${Math.round(after.totalCxCap / 1024 / 1024)}MB cap (${Math.round(after.totalCxUsageRatio * 100)}%).`);
|
|
1668
1760
|
}
|
|
1669
1761
|
|
|
1670
|
-
async function
|
|
1762
|
+
async function _cmdCosts(args) {
|
|
1671
1763
|
const sub = args[0] || 'status';
|
|
1672
1764
|
const {
|
|
1673
1765
|
getDailySpend,
|
|
@@ -1882,7 +1974,7 @@ async function cmdOverrides(args) {
|
|
|
1882
1974
|
process.exit(1);
|
|
1883
1975
|
}
|
|
1884
1976
|
|
|
1885
|
-
async function
|
|
1977
|
+
async function _cmdPricing(args) {
|
|
1886
1978
|
const sub = args[0] || 'status';
|
|
1887
1979
|
const {
|
|
1888
1980
|
describePricingCatalogFreshness,
|
|
@@ -2080,7 +2172,7 @@ async function cmdEmbeddedTriage(args) {
|
|
|
2080
2172
|
input = fs.readFileSync(0, 'utf8');
|
|
2081
2173
|
}
|
|
2082
2174
|
if (!input.trim() && !ingestion?.error) {
|
|
2083
|
-
//
|
|
2175
|
+
// Embedded-contract verb: an embedder that pipes empty/no input
|
|
2084
2176
|
// must still get a parseable, versioned envelope — not a bare exit. Emit a
|
|
2085
2177
|
// typed error envelope on stdout so the contract holds, and exit non-zero so
|
|
2086
2178
|
// shell callers still see failure.
|
|
@@ -2764,12 +2856,25 @@ async function cmdIntakeConfig(args) {
|
|
|
2764
2856
|
const cwd = process.cwd();
|
|
2765
2857
|
|
|
2766
2858
|
const action = args[0];
|
|
2859
|
+
if (action === 'migrate') {
|
|
2860
|
+
const { migrateLegacyIntakeConfig } = await import('../lib/intake/intake-config.mjs');
|
|
2861
|
+
const result = migrateLegacyIntakeConfig(cwd);
|
|
2862
|
+
if (!result.migrated) {
|
|
2863
|
+
info(result.reason ?? 'nothing to migrate');
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2866
|
+
info('Migrated .cx/intake-config.json → construct.config.json intakePolicy');
|
|
2867
|
+
println(JSON.stringify(result.policy, null, 2));
|
|
2868
|
+
return;
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2767
2871
|
if (!action || action === 'show') {
|
|
2768
2872
|
const cfg = loadIntakeConfig(cwd, process.env);
|
|
2769
2873
|
const desc = describeIntakeDepth(cfg.maxDepth);
|
|
2770
2874
|
println(`Intake config (root: ${cwd})`);
|
|
2771
2875
|
println('');
|
|
2772
2876
|
println(` includeProjectInbox: ${cfg.includeProjectInbox ? 'on' : 'off'} (.cx/inbox)`);
|
|
2877
|
+
println(` includeRootInbox: ${(cfg.includeRootInbox ?? cfg.includeArchetypeInbox) ? 'on' : 'off'} (inbox/)`);
|
|
2773
2878
|
println(` includeDocsIntake: ${cfg.includeDocsIntake ? 'on' : 'off'} (docs/intake)`);
|
|
2774
2879
|
println(` maxDepth: ${cfg.maxDepth} — ${desc.label}`);
|
|
2775
2880
|
println(` ${desc.detail}`);
|
|
@@ -2804,6 +2909,9 @@ async function cmdIntakeConfig(args) {
|
|
|
2804
2909
|
const docsFlag = tail.find((a) => a.startsWith('--include-docs-intake='));
|
|
2805
2910
|
if (docsFlag) patch.includeDocsIntake = /^(on|true|1|yes)$/i.test(docsFlag.split('=')[1] || '');
|
|
2806
2911
|
|
|
2912
|
+
const rootFlag = tail.find((a) => a.startsWith('--include-root-inbox='));
|
|
2913
|
+
if (rootFlag) patch.includeRootInbox = /^(on|true|1|yes)$/i.test(rootFlag.split('=')[1] || '');
|
|
2914
|
+
|
|
2807
2915
|
const addFlags = tail.filter((a) => a.startsWith('--add-dir='));
|
|
2808
2916
|
const removeFlags = tail.filter((a) => a.startsWith('--remove-dir='));
|
|
2809
2917
|
|
|
@@ -2830,6 +2938,123 @@ async function cmdIntakeConfig(args) {
|
|
|
2830
2938
|
}
|
|
2831
2939
|
}
|
|
2832
2940
|
|
|
2941
|
+
async function cmdSources(args) {
|
|
2942
|
+
const {
|
|
2943
|
+
validateSourceTargets,
|
|
2944
|
+
normalizeConfigTarget,
|
|
2945
|
+
resolveEffectiveSourceTargetsFromConfig,
|
|
2946
|
+
legacyEnvSourceTargets,
|
|
2947
|
+
SOURCE_PROVIDERS,
|
|
2948
|
+
} = await import('../lib/config/source-targets.mjs');
|
|
2949
|
+
const {
|
|
2950
|
+
loadProjectConfig,
|
|
2951
|
+
writeProjectConfig,
|
|
2952
|
+
findProjectConfigPath,
|
|
2953
|
+
PROJECT_CONFIG_FILENAME,
|
|
2954
|
+
} = await import('../lib/config/project-config.mjs');
|
|
2955
|
+
|
|
2956
|
+
const cwd = process.cwd();
|
|
2957
|
+
const action = args[0] ?? 'list';
|
|
2958
|
+
|
|
2959
|
+
if (action === 'list' || action === 'show') {
|
|
2960
|
+
const { config, path: cfgPath } = loadProjectConfig(cwd, process.env);
|
|
2961
|
+
const configTargets = (config.sources?.targets ?? []).map(normalizeConfigTarget);
|
|
2962
|
+
const envTargets = legacyEnvSourceTargets(process.env);
|
|
2963
|
+
const effective = resolveEffectiveSourceTargetsFromConfig(config, process.env);
|
|
2964
|
+
println(`Source targets (config: ${cfgPath ?? 'default'})`);
|
|
2965
|
+
println('');
|
|
2966
|
+
if (configTargets.length === 0) println(' (no targets in construct.config.json)');
|
|
2967
|
+
for (const t of configTargets) {
|
|
2968
|
+
println(` ${t.id} · ${t.provider} · ${JSON.stringify(t.selector)}`);
|
|
2969
|
+
}
|
|
2970
|
+
if (envTargets.length) {
|
|
2971
|
+
println('');
|
|
2972
|
+
println('Legacy env targets (merged at runtime):');
|
|
2973
|
+
for (const t of envTargets) {
|
|
2974
|
+
println(` ${t.id} · ${t.provenance} · ${t.provider} · ${JSON.stringify(t.selector)}`);
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
println('');
|
|
2978
|
+
println(`Effective targets: ${effective.length}`);
|
|
2979
|
+
return;
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
if (action === 'validate') {
|
|
2983
|
+
const loaded = loadProjectConfig(cwd, process.env);
|
|
2984
|
+
const targetErrors = validateSourceTargets(loaded.raw?.sources?.targets ?? loaded.config?.sources?.targets ?? []);
|
|
2985
|
+
const all = [...(loaded.errors ?? []), ...targetErrors];
|
|
2986
|
+
if (!all.length) {
|
|
2987
|
+
info(`${loaded.path ?? PROJECT_CONFIG_FILENAME}: source targets valid`);
|
|
2988
|
+
return;
|
|
2989
|
+
}
|
|
2990
|
+
errorln(`Source target validation failed (${all.length}):`);
|
|
2991
|
+
for (const e of all) errorln(` - ${e}`);
|
|
2992
|
+
process.exit(1);
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
if (action === 'add') {
|
|
2996
|
+
const provider = args[1];
|
|
2997
|
+
const id = args[2];
|
|
2998
|
+
const selectorRaw = args[3];
|
|
2999
|
+
if (!provider || !id || !selectorRaw) {
|
|
3000
|
+
errorln('Usage: construct sources add <provider> <id> <selector-json>');
|
|
3001
|
+
process.exit(1);
|
|
3002
|
+
}
|
|
3003
|
+
if (!SOURCE_PROVIDERS.includes(provider)) {
|
|
3004
|
+
errorln(`Unknown provider: ${provider}. Expected: ${SOURCE_PROVIDERS.join(', ')}`);
|
|
3005
|
+
process.exit(1);
|
|
3006
|
+
}
|
|
3007
|
+
let selector;
|
|
3008
|
+
try { selector = JSON.parse(selectorRaw); } catch (err) {
|
|
3009
|
+
errorln(`Invalid selector JSON: ${err.message}`);
|
|
3010
|
+
process.exit(1);
|
|
3011
|
+
}
|
|
3012
|
+
const target = normalizeConfigTarget({ id, provider, selector });
|
|
3013
|
+
const targetErrors = validateSourceTargets([target]);
|
|
3014
|
+
if (targetErrors.length) {
|
|
3015
|
+
for (const e of targetErrors) errorln(` - ${e}`);
|
|
3016
|
+
process.exit(1);
|
|
3017
|
+
}
|
|
3018
|
+
const cfgPath = findProjectConfigPath(cwd) || path.join(cwd, PROJECT_CONFIG_FILENAME);
|
|
3019
|
+
const { config } = loadProjectConfig(cwd, process.env);
|
|
3020
|
+
const existing = config.sources?.targets ?? [];
|
|
3021
|
+
if (existing.some((t) => t.id === id)) {
|
|
3022
|
+
errorln(`Target id already exists: ${id}`);
|
|
3023
|
+
process.exit(1);
|
|
3024
|
+
}
|
|
3025
|
+
const next = {
|
|
3026
|
+
...config,
|
|
3027
|
+
sources: { ...(config.sources ?? {}), targets: [...existing, target] },
|
|
3028
|
+
};
|
|
3029
|
+
writeProjectConfig(cfgPath, next);
|
|
3030
|
+
info(`added source target ${id}`);
|
|
3031
|
+
return;
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
if (action === 'remove') {
|
|
3035
|
+
const id = args[1];
|
|
3036
|
+
if (!id) {
|
|
3037
|
+
errorln('Usage: construct sources remove <id>');
|
|
3038
|
+
process.exit(1);
|
|
3039
|
+
}
|
|
3040
|
+
const cfgPath = findProjectConfigPath(cwd) || path.join(cwd, PROJECT_CONFIG_FILENAME);
|
|
3041
|
+
const { config } = loadProjectConfig(cwd, process.env);
|
|
3042
|
+
const existing = config.sources?.targets ?? [];
|
|
3043
|
+
const nextTargets = existing.filter((t) => t.id !== id);
|
|
3044
|
+
if (nextTargets.length === existing.length) {
|
|
3045
|
+
errorln(`No target with id: ${id}`);
|
|
3046
|
+
process.exit(1);
|
|
3047
|
+
}
|
|
3048
|
+
const next = { ...config, sources: { ...(config.sources ?? {}), targets: nextTargets } };
|
|
3049
|
+
writeProjectConfig(cfgPath, next);
|
|
3050
|
+
info(`removed source target ${id}`);
|
|
3051
|
+
return;
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
errorln(`Unknown sources action: ${action}. Available: list, add, remove, validate`);
|
|
3055
|
+
process.exit(1);
|
|
3056
|
+
}
|
|
3057
|
+
|
|
2833
3058
|
async function cmdIntakeMetrics() {
|
|
2834
3059
|
try {
|
|
2835
3060
|
const { computeIntakeMetrics, pendingAge } = await import('../lib/embed/intake-metrics.mjs');
|
|
@@ -3078,7 +3303,7 @@ async function cmdWorkspace(args) {
|
|
|
3078
3303
|
const sub = args[0];
|
|
3079
3304
|
if (!sub || sub === '--help' || sub === '-h') { printWorkspaceHelp(); return; }
|
|
3080
3305
|
|
|
3081
|
-
const { createWorkspace, getWorkspace, listWorkspaces
|
|
3306
|
+
const { createWorkspace, getWorkspace, listWorkspaces } = await import('../lib/embed/workspaces.mjs');
|
|
3082
3307
|
|
|
3083
3308
|
if (sub === 'list') {
|
|
3084
3309
|
const workspaces = listWorkspaces();
|
|
@@ -3192,7 +3417,6 @@ async function cmdRecommendations(args) {
|
|
|
3192
3417
|
const reasonFlag = args.find(a => a.startsWith('--reason='))?.split('=').slice(1).join('=');
|
|
3193
3418
|
const suppressDaysFlag = args.find(a => a.startsWith('--suppress-days='))?.split('=')[1];
|
|
3194
3419
|
try {
|
|
3195
|
-
const { dedupKey: computeKey } = await import('../lib/embed/recommendation-store.mjs');
|
|
3196
3420
|
const key = `${recType}::${recTitle.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 60)}`;
|
|
3197
3421
|
const { dismissRecommendation } = await import('../lib/embed/recommendation-store.mjs');
|
|
3198
3422
|
dismissRecommendation(key, {
|
|
@@ -3602,6 +3826,7 @@ async function cmdModels(args) {
|
|
|
3602
3826
|
return;
|
|
3603
3827
|
}
|
|
3604
3828
|
if (tier && setModel) {
|
|
3829
|
+
const registry = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, 'specialists', 'registry.json'), 'utf8'));
|
|
3605
3830
|
const preferFree = args.includes('--prefer-free');
|
|
3606
3831
|
const preferFreeSameFamily = args.includes('--prefer-free-same-family');
|
|
3607
3832
|
const inferred = setModelWithTierInference(envPath, tier, setModel, registry.models ?? {}, { preferFree, preferFreeSameFamily });
|
|
@@ -3647,7 +3872,7 @@ async function cmdModels(args) {
|
|
|
3647
3872
|
return;
|
|
3648
3873
|
}
|
|
3649
3874
|
if (args.includes('--cheapest')) {
|
|
3650
|
-
const { selectCheapestProvider,
|
|
3875
|
+
const { selectCheapestProvider, formatCheapestProviderMessage, isCheapestProviderEnabled } =
|
|
3651
3876
|
await import('../lib/model-cheapest-provider.mjs');
|
|
3652
3877
|
const cheapestTier = args.find((arg) => arg.startsWith('--tier='))?.split('=')[1] ?? 'standard';
|
|
3653
3878
|
const verbose = args.includes('--verbose');
|
|
@@ -3659,7 +3884,7 @@ async function cmdModels(args) {
|
|
|
3659
3884
|
return;
|
|
3660
3885
|
}
|
|
3661
3886
|
if (args.includes('--apply-cheapest')) {
|
|
3662
|
-
const {
|
|
3887
|
+
const { setCheapestProviderPreference } =
|
|
3663
3888
|
await import('../lib/model-cheapest-provider.mjs');
|
|
3664
3889
|
const allTiers = args.includes('--all-tiers') || !args.find((arg) => arg.startsWith('--tier='));
|
|
3665
3890
|
const tierArg = args.find((arg) => arg.startsWith('--tier='))?.split('=')[1];
|
|
@@ -3684,6 +3909,22 @@ async function cmdModels(args) {
|
|
|
3684
3909
|
await cmdSync([]);
|
|
3685
3910
|
return;
|
|
3686
3911
|
}
|
|
3912
|
+
if (args.includes('--list') || args[0] === 'list') {
|
|
3913
|
+
const { getProviderModelCatalog } = await import('../lib/model-router.mjs');
|
|
3914
|
+
const { listChatModels } = await import('../apps/chat/engine/models.mjs');
|
|
3915
|
+
const catalog = getProviderModelCatalog({ env: process.env, cwd: process.cwd() });
|
|
3916
|
+
const models = listChatModels({ env: process.env, cwd: process.cwd() });
|
|
3917
|
+
if (args.includes('--json')) {
|
|
3918
|
+
println(JSON.stringify({ catalog, models }, null, 2));
|
|
3919
|
+
return;
|
|
3920
|
+
}
|
|
3921
|
+
println('Visible models (filtered by construct.config.json models.visibility):');
|
|
3922
|
+
for (const model of models) {
|
|
3923
|
+
const flag = model.available ? 'ok' : '—';
|
|
3924
|
+
println(` ${flag} ${model.id}${model.configured ? '' : ' (provider not configured)'}`);
|
|
3925
|
+
}
|
|
3926
|
+
return;
|
|
3927
|
+
}
|
|
3687
3928
|
const registry = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, 'specialists', 'registry.json'), 'utf8'));
|
|
3688
3929
|
const result = readCurrentModels(envPath, registry.models ?? {});
|
|
3689
3930
|
const nothingSelected = ['reasoning', 'standard', 'fast'].every((t) => !result[t]);
|
|
@@ -3937,10 +4178,21 @@ async function cmdCleanup() {
|
|
|
3937
4178
|
println(` ! ${err.step}: ${err.error}`);
|
|
3938
4179
|
}
|
|
3939
4180
|
}
|
|
4181
|
+
|
|
4182
|
+
// Reclaim orphaned Postgres containers/volumes from deleted home namespaces.
|
|
4183
|
+
const { reclaimOrphanedDockerResources, formatReclaim } = await import('../lib/maintenance/docker-reclaim.mjs');
|
|
4184
|
+
const reclaim = reclaimOrphanedDockerResources({ homeDir: HOME, env: process.env, dryRun });
|
|
4185
|
+
if (!quiet) {
|
|
4186
|
+
const line = formatReclaim(reclaim);
|
|
4187
|
+
if (line) println(` ${line}`);
|
|
4188
|
+
for (const entry of reclaim.errors ?? []) {
|
|
4189
|
+
println(` ! docker reclaim ${entry.name}: ${entry.error}`);
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
3940
4192
|
}
|
|
3941
4193
|
}
|
|
3942
4194
|
|
|
3943
|
-
async function
|
|
4195
|
+
async function _cmdCost(args) {
|
|
3944
4196
|
const jsonOutput = args.includes('--json');
|
|
3945
4197
|
const resetFlag = args.includes('--reset');
|
|
3946
4198
|
const daysArg = args.find((a) => a.startsWith('--days='));
|
|
@@ -4191,8 +4443,21 @@ async function cmdDocsCheck(args) {
|
|
|
4191
4443
|
}
|
|
4192
4444
|
}
|
|
4193
4445
|
|
|
4194
|
-
async function cmdDocsSite() {
|
|
4195
|
-
const
|
|
4446
|
+
async function cmdDocsSite(args = []) {
|
|
4447
|
+
const check = args.includes('--check');
|
|
4448
|
+
const { buildFumadocsReference, checkFumadocsReferenceDrift } = await import('../lib/auto-docs.mjs');
|
|
4449
|
+
if (check) {
|
|
4450
|
+
const { drift } = checkFumadocsReferenceDrift({ rootDir: ROOT_DIR });
|
|
4451
|
+
if (drift.length === 0) {
|
|
4452
|
+
ok('docs/reference/ is up to date.');
|
|
4453
|
+
return;
|
|
4454
|
+
}
|
|
4455
|
+
println('');
|
|
4456
|
+
println('Generated reference docs are out of date. Run `construct docs:site` to regenerate:');
|
|
4457
|
+
for (const f of drift) println(` ✗ ${f}`);
|
|
4458
|
+
process.exitCode = 1;
|
|
4459
|
+
return;
|
|
4460
|
+
}
|
|
4196
4461
|
const { written } = buildFumadocsReference({ rootDir: ROOT_DIR });
|
|
4197
4462
|
if (written.length === 0) {
|
|
4198
4463
|
ok('docs/reference/ already up to date.');
|
|
@@ -4340,7 +4605,6 @@ async function cmdCiPreview(args) {
|
|
|
4340
4605
|
const { readFileSync, existsSync } = await import('node:fs');
|
|
4341
4606
|
const { join } = await import('node:path');
|
|
4342
4607
|
const { spawnSync: spSync } = await import('node:child_process');
|
|
4343
|
-
const YAML = await import('node:fs');
|
|
4344
4608
|
|
|
4345
4609
|
const listOnly = args.includes('--list');
|
|
4346
4610
|
const fullMode = args.includes('--full');
|
|
@@ -4838,7 +5102,7 @@ async function cmdTags(args) {
|
|
|
4838
5102
|
|
|
4839
5103
|
async function cmdScheduler(args) {
|
|
4840
5104
|
const sub = args[0];
|
|
4841
|
-
const {
|
|
5105
|
+
const { runJobOnce, listJobs } = await import('../lib/scheduler/index.mjs');
|
|
4842
5106
|
|
|
4843
5107
|
if (!sub || sub === 'list') {
|
|
4844
5108
|
const jobs = listJobs();
|
|
@@ -4876,18 +5140,30 @@ async function cmdScheduler(args) {
|
|
|
4876
5140
|
|
|
4877
5141
|
async function cmdCreds(args) {
|
|
4878
5142
|
const sub = args[0];
|
|
4879
|
-
const {
|
|
5143
|
+
const { writeCreds, deleteCreds, listCreds, checkCredsFileMode, credsFilePath } =
|
|
4880
5144
|
await import('../lib/providers/creds.mjs');
|
|
4881
5145
|
const { createInterface } = await import('node:readline');
|
|
4882
5146
|
|
|
4883
5147
|
if (!sub || sub === 'list') {
|
|
4884
5148
|
const entries = listCreds();
|
|
4885
|
-
if (entries.length === 0) {
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
println(
|
|
5149
|
+
if (entries.length === 0) {
|
|
5150
|
+
println('No provider credentials stored in the rotation store.');
|
|
5151
|
+
} else {
|
|
5152
|
+
println('Provider credentials\n');
|
|
5153
|
+
println(' PROVIDER ACCOUNT ROTATED_AT NEXT_ROTATION');
|
|
5154
|
+
for (const e of entries) {
|
|
5155
|
+
println(` ${e.provider.padEnd(26)} ${(e.account || '—').padEnd(27)} ${(e.rotatedAt || '—').padEnd(23)} ${e.nextRotationDue || '—'}`);
|
|
5156
|
+
}
|
|
4890
5157
|
}
|
|
5158
|
+
try {
|
|
5159
|
+
const { getProviderModelCatalog } = await import('../lib/model-router.mjs');
|
|
5160
|
+
const { providers } = getProviderModelCatalog({ env: process.env });
|
|
5161
|
+
println('\nLLM provider readiness (op:// references resolve at call time)\n');
|
|
5162
|
+
for (const p of providers) {
|
|
5163
|
+
println(` ${p.configured ? '✓' : '·'} ${p.id}`);
|
|
5164
|
+
}
|
|
5165
|
+
println('\n Set keys as values or op:// refs in ~/.construct/config.env; run `construct creds login copilot` for GitHub Copilot.');
|
|
5166
|
+
} catch { /* readiness view is informational */ }
|
|
4891
5167
|
return;
|
|
4892
5168
|
}
|
|
4893
5169
|
|
|
@@ -4926,7 +5202,68 @@ async function cmdCreds(args) {
|
|
|
4926
5202
|
return;
|
|
4927
5203
|
}
|
|
4928
5204
|
|
|
4929
|
-
|
|
5205
|
+
if (sub === 'login') {
|
|
5206
|
+
const provider = args[1] || 'copilot';
|
|
5207
|
+
if (provider !== 'copilot' && provider !== 'github-copilot') {
|
|
5208
|
+
errorln(`\`creds login\` currently supports only \`copilot\`. For API-key providers use \`construct creds set ${provider}\`.`);
|
|
5209
|
+
process.exit(1);
|
|
5210
|
+
}
|
|
5211
|
+
const copilot = await import('../lib/providers/copilot-auth.mjs');
|
|
5212
|
+
if (copilot.hasStoredCredential() && !args.includes('--force')) {
|
|
5213
|
+
try {
|
|
5214
|
+
await copilot.getCopilotToken();
|
|
5215
|
+
println('GitHub Copilot is already authenticated and the session token exchanged successfully.');
|
|
5216
|
+
println('Re-run with --force to authenticate a different account.');
|
|
5217
|
+
return;
|
|
5218
|
+
} catch {
|
|
5219
|
+
println('A stored Copilot credential exists but the session exchange failed; re-authenticating.');
|
|
5220
|
+
}
|
|
5221
|
+
}
|
|
5222
|
+
let device;
|
|
5223
|
+
try {
|
|
5224
|
+
device = await copilot.requestDeviceCode({});
|
|
5225
|
+
} catch (err) {
|
|
5226
|
+
errorln(err.message);
|
|
5227
|
+
process.exit(1);
|
|
5228
|
+
}
|
|
5229
|
+
println('\nGitHub Copilot sign-in');
|
|
5230
|
+
println(` 1. Open: ${device.verificationUri}`);
|
|
5231
|
+
println(` 2. Enter code: ${device.userCode}`);
|
|
5232
|
+
println('\nWaiting for authorization...');
|
|
5233
|
+
let token;
|
|
5234
|
+
try {
|
|
5235
|
+
token = await copilot.pollForAccessToken({
|
|
5236
|
+
deviceCode: device.deviceCode,
|
|
5237
|
+
interval: device.interval,
|
|
5238
|
+
expiresIn: device.expiresIn,
|
|
5239
|
+
onPending: () => process.stdout.write('.'),
|
|
5240
|
+
});
|
|
5241
|
+
process.stdout.write('\n');
|
|
5242
|
+
} catch (err) {
|
|
5243
|
+
process.stdout.write('\n');
|
|
5244
|
+
errorln(err.message);
|
|
5245
|
+
process.exit(1);
|
|
5246
|
+
}
|
|
5247
|
+
copilot.persistOAuth({ accessToken: token.accessToken, refreshToken: token.refreshToken, expiresAt: token.expiresAt, user: null });
|
|
5248
|
+
try {
|
|
5249
|
+
await copilot.getCopilotToken();
|
|
5250
|
+
println('GitHub Copilot authenticated. The session token exchanged successfully.');
|
|
5251
|
+
println('Credentials stored in ~/.construct/auth/github-copilot.json and ~/.config/github-copilot/apps.json.');
|
|
5252
|
+
try {
|
|
5253
|
+
const models = await copilot.listCopilotModels({});
|
|
5254
|
+
if (models.length) {
|
|
5255
|
+
println('\nModels available to your account (use as github-copilot/<id>):');
|
|
5256
|
+
for (const id of models) println(` - github-copilot/${id}`);
|
|
5257
|
+
}
|
|
5258
|
+
} catch { /* model listing is informational; auth already succeeded */ }
|
|
5259
|
+
} catch (err) {
|
|
5260
|
+
errorln(`Authenticated with GitHub, but the Copilot token exchange failed: ${err.message}`);
|
|
5261
|
+
process.exit(1);
|
|
5262
|
+
}
|
|
5263
|
+
return;
|
|
5264
|
+
}
|
|
5265
|
+
|
|
5266
|
+
errorln(`Unknown creds subcommand: ${sub}. Available: list, login, set, rotate, revoke, test`);
|
|
4930
5267
|
process.exit(1);
|
|
4931
5268
|
}
|
|
4932
5269
|
|
|
@@ -5149,7 +5486,7 @@ async function cmdTelemetryQuery(args) {
|
|
|
5149
5486
|
println(' errors [--agent=<name>] [--since=24h] error span summary');
|
|
5150
5487
|
println(' trace <traceId> full span tree');
|
|
5151
5488
|
println('');
|
|
5152
|
-
println('For Langfuse/Honeycomb/Grafana Tempo/Datadog, see docs/concepts/observability.
|
|
5489
|
+
println('For Langfuse/Honeycomb/Grafana Tempo/Datadog, see docs/concepts/observability.md');
|
|
5153
5490
|
}
|
|
5154
5491
|
|
|
5155
5492
|
async function cmdHook(args) {
|
|
@@ -5215,6 +5552,7 @@ const handlers = new Map([
|
|
|
5215
5552
|
['status', cmdStatus],
|
|
5216
5553
|
['install', cmdInstall],
|
|
5217
5554
|
['config', cmdConfig],
|
|
5555
|
+
['sources', cmdSources],
|
|
5218
5556
|
['intake', cmdIntake],
|
|
5219
5557
|
['recommendations', cmdRecommendations],
|
|
5220
5558
|
['integrations', cmdIntegrations],
|
|
@@ -5233,13 +5571,18 @@ const handlers = new Map([
|
|
|
5233
5571
|
['distill', cmdDistill],
|
|
5234
5572
|
['ingest', cmdIngest],
|
|
5235
5573
|
['export', cmdExport],
|
|
5574
|
+
['publish', cmdPublish],
|
|
5575
|
+
['tools', cmdTools],
|
|
5236
5576
|
['infer', cmdInfer],
|
|
5237
5577
|
['search', cmdSearch],
|
|
5238
5578
|
['storage', cmdStorage],
|
|
5239
5579
|
['registry:status', cmdRegistryStatus],
|
|
5580
|
+
['registry:validate', cmdRegistryValidate],
|
|
5581
|
+
['registry:generate-docs', cmdRegistryGenerateDocs],
|
|
5582
|
+
['rules', cmdRulesUsage],
|
|
5240
5583
|
// Pricing / cost readouts are stubbed out: the ledger writes, model-pricing
|
|
5241
5584
|
// catalog, and per-turn accounting still run, but no CLI surface exposes
|
|
5242
|
-
// them to the user. Handlers (
|
|
5585
|
+
// them to the user. Handlers (_cmdPricing, _cmdCosts, _cmdCost) remain in this
|
|
5243
5586
|
// file so the OTel + dashboard wiring (planned in Workstream J) can rewire
|
|
5244
5587
|
// the surface without re-implementing the logic.
|
|
5245
5588
|
['overrides', cmdOverrides],
|
|
@@ -5262,7 +5605,7 @@ const handlers = new Map([
|
|
|
5262
5605
|
if (sub === 'verify') return cmdDocsVerify(rest);
|
|
5263
5606
|
if (sub === 'update') return cmdDocsUpdate(rest);
|
|
5264
5607
|
if (sub === 'check') return cmdDocsCheck(rest);
|
|
5265
|
-
if (sub === 'site') return cmdDocsSite();
|
|
5608
|
+
if (sub === 'site') return cmdDocsSite(rest);
|
|
5266
5609
|
if (sub === 'reconcile') return cmdDocsReconcile(rest);
|
|
5267
5610
|
errorln(`Unknown docs subcommand: ${sub}. Available: check, verify, update, site, reconcile`);
|
|
5268
5611
|
process.exit(1);
|
|
@@ -5277,6 +5620,10 @@ const handlers = new Map([
|
|
|
5277
5620
|
const { runAcpServer } = await import('../lib/acp/server.mjs');
|
|
5278
5621
|
runAcpServer({ input: process.stdin, output: process.stdout, env: process.env, defaultCwd: process.cwd() });
|
|
5279
5622
|
}],
|
|
5623
|
+
['chat', async (args) => {
|
|
5624
|
+
const { runChat } = await import('../lib/chat/cli.mjs');
|
|
5625
|
+
process.exitCode = await runChat(args, { env: process.env, cwd: process.cwd() });
|
|
5626
|
+
}],
|
|
5280
5627
|
['beads:stats', async (args) => {
|
|
5281
5628
|
const { getContentionStats, getHumanStatus } = await import('../lib/beads-optimistic.mjs');
|
|
5282
5629
|
const jsonOutput = args.includes('--json');
|
|
@@ -5438,11 +5785,40 @@ const handlers = new Map([
|
|
|
5438
5785
|
['audit', async (args) => {
|
|
5439
5786
|
const sub = args[0];
|
|
5440
5787
|
if (sub === 'skills') return runAuditSkillsCli(args.slice(1));
|
|
5788
|
+
if (sub === 'specialists') return runAuditSpecialistsCli(args.slice(1));
|
|
5789
|
+
if (sub === 'tests') {
|
|
5790
|
+
const rest = args.slice(1);
|
|
5791
|
+
if (rest.includes('--corpus')) return runCorpusInventoryAuditCli(rest.filter((arg) => arg !== '--corpus'), { rootDir: ROOT_DIR });
|
|
5792
|
+
return runCapabilityLedgerAuditCli(rest, { rootDir: ROOT_DIR });
|
|
5793
|
+
}
|
|
5441
5794
|
if (sub === 'trail' || !sub) {
|
|
5442
5795
|
const { runAuditTrailCli } = await import('../lib/audit-trail.mjs');
|
|
5443
5796
|
return runAuditTrailCli(args.slice(sub === 'trail' ? 1 : 0));
|
|
5444
5797
|
}
|
|
5445
|
-
errorln(`Unknown audit subcommand: ${sub}. Available: skills, trail`);
|
|
5798
|
+
errorln(`Unknown audit subcommand: ${sub}. Available: skills, specialists, tests, trail`);
|
|
5799
|
+
process.exit(1);
|
|
5800
|
+
}],
|
|
5801
|
+
['artifact', async (args) => {
|
|
5802
|
+
const sub = args[0];
|
|
5803
|
+
if (sub === 'validate') return runArtifactValidateCli(args.slice(1));
|
|
5804
|
+
if (sub === 'workflow') {
|
|
5805
|
+
const rest = args.slice(1);
|
|
5806
|
+
const request = rest.filter((arg) => !arg.startsWith('--')).join(' ');
|
|
5807
|
+
const read = (name) => rest.find((arg) => arg.startsWith(`${name}=`))?.slice(name.length + 1);
|
|
5808
|
+
const { runArtifactWorkflow } = await import('../lib/artifact-workflow.mjs');
|
|
5809
|
+
const result = runArtifactWorkflow({
|
|
5810
|
+
input: request,
|
|
5811
|
+
artifactType: read('--type'),
|
|
5812
|
+
filePath: read('--file'),
|
|
5813
|
+
format: read('--to'),
|
|
5814
|
+
outputPath: read('--output'),
|
|
5815
|
+
branding: rest.includes('--plain') ? 'plain' : undefined,
|
|
5816
|
+
approvalMode: rest.includes('--apply') ? 'allow-durable-write' : (read('--approval-mode') || 'proposal-only'),
|
|
5817
|
+
}, { cwd: process.cwd(), rootDir: ROOT_DIR });
|
|
5818
|
+
println(JSON.stringify(result, null, 2));
|
|
5819
|
+
return;
|
|
5820
|
+
}
|
|
5821
|
+
errorln(`Unknown artifact subcommand: ${sub || '(none)'}. Available: validate, workflow`);
|
|
5446
5822
|
process.exit(1);
|
|
5447
5823
|
}],
|
|
5448
5824
|
['doc', async (args) => {
|
|
@@ -5890,6 +6266,30 @@ const handlers = new Map([
|
|
|
5890
6266
|
const { runEmbedCli } = await import('../lib/embed/cli.mjs');
|
|
5891
6267
|
return runEmbedCli(args, { rootDir: new URL('..', import.meta.url).pathname });
|
|
5892
6268
|
}],
|
|
6269
|
+
['oracle', async (args) => {
|
|
6270
|
+
const { runOracleCli } = await import('../lib/oracle/cli.mjs');
|
|
6271
|
+
return runOracleCli(args, { rootDir: ROOT_DIR, projectDir: process.cwd(), homeDir: HOME });
|
|
6272
|
+
}],
|
|
6273
|
+
['certify', async (args) => {
|
|
6274
|
+
const { runCertificationCli } = await import('../lib/certification/cli.mjs');
|
|
6275
|
+
const code = await runCertificationCli(args, { projectDir: process.cwd(), repoRoot: ROOT_DIR });
|
|
6276
|
+
if (typeof code === 'number' && code !== 0) process.exit(code);
|
|
6277
|
+
}],
|
|
6278
|
+
['improvement', async (args) => {
|
|
6279
|
+
const { runImprovementCli } = await import('../lib/improvement/cli.mjs');
|
|
6280
|
+
const code = await runImprovementCli(args, { projectDir: process.cwd() });
|
|
6281
|
+
if (typeof code === 'number' && code !== 0) process.exit(code);
|
|
6282
|
+
}],
|
|
6283
|
+
['matrix', async (args) => {
|
|
6284
|
+
const { runGraphCli } = await import('../lib/graph/cli.mjs');
|
|
6285
|
+
const code = runGraphCli(args, { rootDir: ROOT_DIR, projectDir: process.cwd() });
|
|
6286
|
+
if (typeof code === 'number' && code !== 0) process.exit(code);
|
|
6287
|
+
}],
|
|
6288
|
+
['impact', async (args) => {
|
|
6289
|
+
const { runImpactCli } = await import('../lib/graph/impact-cli.mjs');
|
|
6290
|
+
const code = await runImpactCli(args, { rootDir: ROOT_DIR, projectDir: process.cwd() });
|
|
6291
|
+
if (typeof code === 'number' && code !== 0) process.exit(code);
|
|
6292
|
+
}],
|
|
5893
6293
|
['reflect', async (args) => {
|
|
5894
6294
|
const { runReflectCli } = await import('../lib/reflect.mjs');
|
|
5895
6295
|
return runReflectCli(args);
|