@geraldmaron/construct 1.2.2 → 1.3.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 +7 -10
- package/bin/construct +286 -220
- package/bin/construct-postinstall.mjs +0 -21
- package/commands/work/optimize-prompts.md +1 -1
- package/examples/distribution/sources/adr.md +2 -2
- package/examples/seed-observations/decisions.md +1 -1
- package/lib/artifact-loop-core.mjs +412 -0
- package/lib/artifact-manifest.mjs +6 -0
- package/lib/artifact-release-gate.mjs +115 -49
- package/lib/audit-rules.mjs +2 -4
- package/lib/audit-skills.mjs +32 -20
- package/lib/audit-specialists.mjs +44 -26
- package/lib/auto-docs.mjs +6 -5
- package/lib/brand-prose.mjs +3 -3
- package/lib/brand-tokens.mjs +2 -2
- package/lib/certification/artifact-fixtures.mjs +4 -1
- package/lib/certification/demo-parity.mjs +6 -32
- package/lib/certification/real-llm-scenarios.mjs +58 -13
- package/lib/certification/role-cards.mjs +7 -6
- package/lib/certification/role-overlays.mjs +4 -4
- package/lib/certification/runner.mjs +9 -2
- package/lib/certification/skill-inventory.mjs +34 -22
- package/lib/certification/skill-scenarios.mjs +21 -8
- package/lib/certification/specialist-contracts.mjs +4 -3
- package/lib/certification/specialist-scenarios.mjs +7 -6
- package/lib/certification/status.mjs +5 -4
- package/lib/cli-commands.mjs +56 -57
- package/lib/comment-lint.mjs +1 -5
- package/lib/completions.mjs +20 -2
- package/lib/config/legacy-config-migration.mjs +125 -0
- package/lib/config/schema.mjs +5 -5
- package/lib/config/source-targets.mjs +80 -0
- package/lib/contracts/validate.mjs +119 -51
- package/lib/decisions/golden.mjs +3 -2
- package/lib/decisions/registry.mjs +11 -6
- package/lib/demo-project.mjs +188 -0
- package/lib/demo-recording.mjs +9 -34
- package/lib/demo-script.mjs +2 -2
- package/lib/demo-surface.mjs +13 -131
- package/lib/demo-tour-renderer.mjs +92 -0
- package/lib/demo.mjs +69 -54
- package/lib/diagram-export.mjs +63 -14
- package/lib/doctor/source-checkout.mjs +3 -4
- package/lib/doctor/watchers/consistency.mjs +90 -22
- package/lib/doctor/watchers/credential-parity.mjs +97 -0
- package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
- package/lib/doctor/watchers/service-health.mjs +4 -32
- package/lib/document-export.mjs +73 -9
- package/lib/document-extract.mjs +10 -198
- package/lib/embed/artifact.mjs +2 -3
- package/lib/embed/cli.mjs +1 -1
- package/lib/embed/config.mjs +1 -2
- package/lib/embed/customer-profiles.mjs +1 -1
- package/lib/embed/daemon.mjs +5 -6
- package/lib/embed/demand-fetch.mjs +114 -2
- package/lib/embed/docs-lifecycle.mjs +9 -5
- package/lib/embed/intake-metrics.mjs +2 -2
- package/lib/embed/notifications.mjs +2 -3
- package/lib/embed/recommendation-store.mjs +25 -22
- package/lib/embed/role-framing.mjs +4 -9
- package/lib/embedded-contract/capability.mjs +4 -2
- package/lib/embedded-contract/contract-version.mjs +1 -1
- package/lib/env-config.mjs +2 -2
- package/lib/flavors/loader.mjs +21 -19
- package/lib/graph/build-from-registry.mjs +31 -7
- package/lib/graph/staleness.mjs +1 -1
- package/lib/headhunt.mjs +82 -35
- package/lib/hooks/agent-tracker.mjs +2 -2
- package/lib/hooks/config-protection.mjs +1 -1
- package/lib/hooks/registry-sync.mjs +3 -3
- package/lib/host-capabilities.mjs +5 -5
- package/lib/improvement/controller.mjs +2 -2
- package/lib/init-unified.mjs +19 -21
- package/lib/intake/classify.mjs +26 -0
- package/lib/intake/daemon.mjs +2 -3
- package/lib/intake/prepare.mjs +10 -3
- package/lib/intake/session-prelude.mjs +1 -1
- package/lib/intake/tables/creative.mjs +9 -9
- package/lib/intake/tables/operations.mjs +4 -4
- package/lib/intake/tables/research.mjs +2 -2
- package/lib/integrations/intake-integrations.mjs +9 -10
- package/lib/knowledge/research-store.mjs +2 -2
- package/lib/mcp/server.mjs +104 -61
- package/lib/mcp/tool-budget.mjs +1 -46
- package/lib/mcp/tool-recovery.mjs +44 -0
- package/lib/mcp/tools/artifact-author.mjs +36 -0
- package/lib/mcp/tools/find-tool.mjs +86 -0
- package/lib/mcp/tools/orchestration-run.mjs +107 -69
- package/lib/mcp/tools/project.mjs +4 -3
- package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
- package/lib/mcp/tools/skills.mjs +39 -22
- package/lib/mcp/tools/telemetry.mjs +2 -1
- package/lib/mcp/tools/workflow.mjs +1 -1
- package/lib/mcp-platform-config.mjs +7 -5
- package/lib/migrations/index.mjs +4 -2
- package/lib/migrations/v2-unified-registry.mjs +35 -0
- package/lib/model-router.mjs +203 -36
- package/lib/models/catalog.mjs +25 -9
- package/lib/models/execution-capability-profile.mjs +3 -3
- package/lib/models/execution-policy.mjs +7 -6
- package/lib/models/provider-poll.mjs +18 -4
- package/lib/opencode-config.mjs +56 -1
- package/lib/opencode-runtime-plugin.mjs +13 -10
- package/lib/oracle/artifact-gate.mjs +14 -4
- package/lib/oracle/cli.mjs +2 -0
- package/lib/oracle/dispatch.mjs +18 -1
- package/lib/oracle/execute.mjs +39 -7
- package/lib/oracle/index.mjs +1 -1
- package/lib/oracle/org-graph.mjs +10 -5
- package/lib/oracle/policy.mjs +6 -0
- package/lib/oracle/read-model.mjs +103 -6
- package/lib/oracle/reconcile.mjs +31 -4
- package/lib/oracle/remediation-dispatch.mjs +53 -0
- package/lib/oracle/routing.mjs +5 -0
- package/lib/oracle/synthesize.mjs +80 -3
- package/lib/orchestration/routing-tables.mjs +44 -10
- package/lib/orchestration/runtime.mjs +2 -0
- package/lib/orchestration/worker.mjs +1 -1
- package/lib/orchestration-policy.mjs +351 -62
- package/lib/overrides/resolver.mjs +1 -12
- package/lib/parity.mjs +37 -11
- package/lib/policy/engine.mjs +80 -15
- package/lib/prompt-composer.js +51 -15
- package/lib/prompt-metadata.mjs +3 -2
- package/lib/providers/connection-probe.mjs +58 -0
- package/lib/providers/copilot-auth.mjs +1 -1
- package/lib/providers/credential-bootstrap.mjs +1 -1
- package/lib/providers/op-run.mjs +3 -4
- package/lib/providers/secret-resolver.mjs +31 -0
- package/lib/publish-tooling.mjs +3 -11
- package/lib/publish.mjs +12 -29
- package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
- package/lib/reflect.mjs +2 -2
- package/lib/registry/agent-manifest.mjs +190 -0
- package/lib/registry/assemble.mjs +133 -0
- package/lib/registry/catalog.mjs +171 -0
- package/lib/registry/cli.mjs +590 -6
- package/lib/registry/consolidation.mjs +5 -4
- package/lib/registry/docs-sync.mjs +67 -0
- package/lib/registry/loader.mjs +147 -0
- package/lib/registry/org-io.mjs +76 -0
- package/lib/registry/retired-paths.mjs +71 -0
- package/lib/registry/surface-map.mjs +5 -7
- package/lib/registry/validator.mjs +438 -0
- package/lib/research-execution-policy.mjs +173 -0
- package/lib/roles/catalog.mjs +4 -9
- package/lib/roles/fence.mjs +107 -1
- package/lib/roles/flavor-bindings.mjs +68 -0
- package/lib/roles/gateway.mjs +140 -29
- package/lib/roles/manifest.mjs +22 -13
- package/lib/roles/router.mjs +1 -1
- package/lib/runtime-env.mjs +1 -1
- package/lib/scopes/enrich.mjs +67 -0
- package/lib/scopes/hooks.mjs +12 -0
- package/lib/{profiles → scopes}/lifecycle.mjs +70 -70
- package/lib/scopes/loader.mjs +104 -0
- package/lib/scopes/rebrand.mjs +32 -0
- package/lib/scopes/research-profile.mjs +16 -0
- package/lib/scopes/teams.mjs +98 -0
- package/lib/service-manager.mjs +1 -117
- package/lib/setup-prompts.mjs +1 -1
- package/lib/setup.mjs +17 -28
- package/lib/skills/composition-graph.mjs +98 -0
- package/lib/skills/router.mjs +80 -0
- package/lib/specialist-contracts.mjs +17 -18
- package/lib/specialists/postconditions.mjs +2 -2
- package/lib/specialists/prompt-schema.mjs +6 -5
- package/lib/specialists/roster.mjs +7 -12
- package/lib/specialists/schema.mjs +9 -6
- package/lib/status.mjs +25 -90
- package/lib/storage/file-lock.mjs +6 -3
- package/lib/telemetry/skill-calls.mjs +1 -1
- package/lib/templates/doc-presentation.mjs +63 -0
- package/lib/templates/visual-requirements.mjs +35 -2
- package/lib/term-format.mjs +107 -2
- package/lib/test-env-setup.mjs +21 -0
- package/lib/ui/components.mjs +42 -0
- package/lib/ui/glyphs.mjs +58 -0
- package/lib/ui/links.mjs +115 -0
- package/lib/ui/theme.mjs +108 -0
- package/lib/uninstall/uninstall.mjs +2 -1
- package/lib/validator.mjs +45 -8
- package/lib/validators/skill-effectiveness.mjs +174 -0
- package/lib/validators/skills.mjs +1 -1
- package/package.json +12 -7
- package/personas/construct.md +12 -3
- package/platforms/claude/CLAUDE.md +1 -1
- package/rules/common/beads-hygiene.md +52 -0
- package/rules/common/neurodivergent-output.md +4 -7
- package/rules/common/research.md +2 -0
- package/scripts/sync-specialists.mjs +260 -49
- package/skills/ai/prompt-optimizer.md +3 -3
- package/skills/brand/output-vibe.md +48 -0
- package/skills/docs/adr-workflow.md +3 -0
- package/skills/docs/backlog-proposal-workflow.md +3 -0
- package/skills/docs/codebase-research-workflow.md +5 -2
- package/skills/docs/customer-profile-workflow.md +3 -0
- package/skills/docs/document-ingest-workflow.md +3 -0
- package/skills/docs/evidence-ingest-workflow.md +4 -1
- package/skills/docs/init-project.md +1 -1
- package/skills/docs/prd-workflow.md +1 -1
- package/skills/docs/prfaq-workflow.md +3 -0
- package/skills/docs/product-intelligence-workflow.md +4 -1
- package/skills/docs/product-signal-workflow.md +3 -0
- package/skills/docs/research-workflow.md +13 -6
- package/skills/docs/runbook-workflow.md +3 -0
- package/skills/docs/strategy-workflow.md +3 -0
- package/skills/docs/user-research-workflow.md +6 -3
- package/skills/operating/orchestration-reference.md +1 -1
- package/skills/roles/{engineer.ai.md → ai-engineer.md} +2 -2
- package/skills/roles/architect.ai-systems.md +1 -1
- package/skills/roles/architect.data.md +1 -1
- package/skills/roles/architect.enterprise.md +1 -1
- package/skills/roles/architect.integration.md +1 -1
- package/skills/roles/architect.md +1 -1
- package/skills/roles/architect.platform.md +1 -1
- package/skills/roles/{product-manager.business-strategy.md → business-strategist.md} +3 -3
- package/skills/roles/data-analyst.experiment.md +1 -1
- package/skills/roles/data-analyst.md +1 -1
- package/skills/roles/data-analyst.product-intelligence.md +1 -1
- package/skills/roles/data-analyst.product.md +1 -1
- package/skills/roles/data-analyst.telemetry.md +1 -1
- package/skills/roles/{engineer.data.md → data-engineer.md} +2 -2
- package/skills/roles/data-engineer.pipeline.md +2 -2
- package/skills/roles/data-engineer.vector-retrieval.md +2 -2
- package/skills/roles/data-engineer.warehouse.md +2 -2
- package/skills/roles/debugger.md +1 -1
- package/skills/roles/designer.accessibility.md +1 -1
- package/skills/roles/designer.md +1 -1
- package/skills/roles/{reviewer.devil-advocate.md → devil-advocate.md} +2 -2
- package/skills/roles/{operator.docs.md → docs-keeper.md} +3 -3
- package/skills/roles/engineer.md +3 -7
- package/skills/roles/{reviewer.evaluator.md → evaluator.md} +2 -2
- package/skills/roles/{researcher.explorer.md → explorer.md} +2 -2
- package/skills/roles/{operator.md → operations.md} +2 -5
- package/skills/roles/orchestrator.md +1 -1
- package/skills/roles/{engineer.platform.md → platform-engineer.md} +2 -2
- package/skills/roles/product-manager.ai-product.md +1 -1
- package/skills/roles/product-manager.enterprise.md +1 -1
- package/skills/roles/product-manager.growth.md +1 -2
- package/skills/roles/product-manager.md +1 -2
- package/skills/roles/product-manager.platform.md +1 -1
- package/skills/roles/product-manager.product.md +1 -1
- package/skills/roles/qa.ai-eval.md +1 -1
- package/skills/roles/qa.api-contract.md +1 -1
- package/skills/roles/qa.data-pipeline.md +1 -1
- package/skills/roles/qa.md +1 -1
- package/skills/roles/qa.web-ui.md +1 -1
- package/skills/roles/{operator.release.md → release-manager.md} +3 -3
- package/skills/roles/researcher.md +1 -1
- package/skills/roles/reviewer.md +1 -1
- package/skills/roles/security.ai.md +1 -1
- package/skills/roles/security.appsec.md +1 -1
- package/skills/roles/security.cloud.md +1 -1
- package/skills/roles/security.legal-compliance.md +1 -1
- package/skills/roles/security.md +1 -1
- package/skills/roles/security.privacy.md +1 -1
- package/skills/roles/security.supply-chain.md +1 -1
- package/skills/roles/{operator.sre.md → sre.md} +3 -3
- package/skills/roles/{qa.test-automation.md → test-automation.md} +2 -2
- package/skills/roles/{reviewer.trace.md → trace-reviewer.md} +2 -2
- package/skills/roles/{researcher.ux.md → ux-researcher.md} +2 -2
- package/skills/routing.json +18 -0
- package/skills/routing.md +1 -1
- package/specialists/artifact-manifest.json +2 -1
- package/specialists/audit-enrichments.json +14 -14
- package/specialists/org/contracts/accessibility-to-qa.json +37 -0
- package/specialists/org/contracts/any-to-business-strategist.json +57 -0
- package/specialists/org/contracts/any-to-debugger.json +38 -0
- package/specialists/org/contracts/any-to-designer.json +33 -0
- package/specialists/org/contracts/any-to-docs-keeper.json +34 -0
- package/specialists/org/contracts/any-to-explorer.json +39 -0
- package/specialists/org/contracts/any-to-sre-incident.json +33 -0
- package/specialists/org/contracts/any-to-trace-reviewer.json +32 -0
- package/specialists/org/contracts/architect-to-ai-engineer.json +32 -0
- package/specialists/org/contracts/architect-to-data-engineer.json +52 -0
- package/specialists/org/contracts/architect-to-devil-advocate.json +38 -0
- package/specialists/org/contracts/architect-to-engineer.json +34 -0
- package/specialists/org/contracts/architect-to-evaluator.json +59 -0
- package/specialists/org/contracts/architect-to-legal-compliance.json +55 -0
- package/specialists/org/contracts/architect-to-operations.json +45 -0
- package/specialists/org/contracts/architect-to-platform-engineer.json +42 -0
- package/specialists/org/contracts/business-strategist-to-product-manager.json +39 -0
- package/specialists/org/contracts/construct-to-orchestrator.json +36 -0
- package/specialists/org/contracts/construct-to-rd-lead.json +53 -0
- package/specialists/org/contracts/data-analyst-to-product-manager.json +43 -0
- package/specialists/org/contracts/data-engineer-to-platform-engineer.json +36 -0
- package/specialists/org/contracts/designer-to-accessibility.json +36 -0
- package/specialists/org/contracts/engineer-to-qa.json +34 -0
- package/specialists/org/contracts/engineer-to-reviewer.json +52 -0
- package/specialists/org/contracts/explorer-to-engineer.json +38 -0
- package/specialists/org/contracts/legal-compliance-to-release-manager.json +43 -0
- package/specialists/org/contracts/platform-engineer-to-engineer.json +31 -0
- package/specialists/org/contracts/product-manager-to-architect.json +71 -0
- package/specialists/org/contracts/product-manager-to-data-analyst.json +49 -0
- package/specialists/org/contracts/product-manager-to-ux-researcher.json +51 -0
- package/specialists/org/contracts/qa-to-release-manager.json +42 -0
- package/specialists/org/contracts/qa-to-test-automation.json +42 -0
- package/specialists/org/contracts/rd-lead-to-architect.json +38 -0
- package/specialists/org/contracts/researcher-to-architect.json +40 -0
- package/specialists/org/contracts/researcher-to-product-manager.json +57 -0
- package/specialists/org/contracts/reviewer-to-security.json +41 -0
- package/specialists/org/contracts/sre-to-release-manager.json +36 -0
- package/specialists/org/contracts/test-automation-to-engineer.json +34 -0
- package/specialists/org/contracts/trace-reviewer-to-sre.json +41 -0
- package/specialists/org/contracts/user-to-construct.json +30 -0
- package/specialists/org/groups/engineering-group.json +43 -0
- package/specialists/org/groups/governance-group.json +38 -0
- package/specialists/org/groups/operations-group.json +41 -0
- package/specialists/org/groups/product-group.json +46 -0
- package/specialists/org/groups/quality-group.json +42 -0
- package/specialists/org/groups/strategy-group.json +41 -0
- package/specialists/org/policies/action-approval.json +13 -0
- package/specialists/org/policies/agents-routing.json +13 -0
- package/specialists/org/policies/architecture.json +20 -0
- package/specialists/org/policies/bash-safety.json +13 -0
- package/specialists/org/policies/beads-hygiene.json +13 -0
- package/specialists/org/policies/bootstrap-state.json +13 -0
- package/specialists/org/policies/code-review.json +13 -0
- package/specialists/org/policies/coding-style.json +13 -0
- package/specialists/org/policies/comments.json +13 -0
- package/specialists/org/policies/commit-approval.json +13 -0
- package/specialists/org/policies/contract-preconditions.json +13 -0
- package/specialists/org/policies/deployment.json +20 -0
- package/specialists/org/policies/description.json +14 -0
- package/specialists/org/policies/design-approval.json +19 -0
- package/specialists/org/policies/doc-ownership.json +13 -0
- package/specialists/org/policies/file-path-fence.json +13 -0
- package/specialists/org/policies/framing.json +13 -0
- package/specialists/org/policies/git-workflow.json +13 -0
- package/specialists/org/policies/incident-response.json +15 -0
- package/specialists/org/policies/intake-triage.json +15 -0
- package/specialists/org/policies/neurodivergent-output.json +13 -0
- package/specialists/org/policies/no-fabrication.json +13 -0
- package/specialists/org/policies/patterns.json +13 -0
- package/specialists/org/policies/quality-gate-approval.json +17 -0
- package/specialists/org/policies/release-gates.json +13 -0
- package/specialists/org/policies/research-evidence.json +13 -0
- package/specialists/org/policies/review-before-change.json +13 -0
- package/specialists/org/policies/rollback.json +15 -0
- package/specialists/org/policies/scope-change.json +20 -0
- package/specialists/org/policies/secret-scan.json +13 -0
- package/specialists/org/policies/security-approval.json +17 -0
- package/specialists/org/policies/security.json +13 -0
- package/specialists/org/policies/session-efficiency.json +13 -0
- package/specialists/org/policies/skill-routing.json +13 -0
- package/specialists/org/policies/strategic-prioritization.json +17 -0
- package/specialists/org/policies/testing.json +13 -0
- package/specialists/org/policies/tool-invisibility.json +14 -0
- package/specialists/org/scopes/creative.json +54 -0
- package/specialists/org/scopes/operations.json +51 -0
- package/specialists/org/scopes/research.json +60 -0
- package/specialists/org/scopes/rnd.json +81 -0
- package/specialists/org/specialists/cx-accessibility.json +69 -0
- package/specialists/org/specialists/cx-ai-engineer.json +75 -0
- package/specialists/org/specialists/cx-architect.json +89 -0
- package/specialists/org/specialists/cx-business-strategist.json +72 -0
- package/specialists/org/specialists/cx-data-analyst.json +68 -0
- package/specialists/org/specialists/cx-data-engineer.json +71 -0
- package/specialists/org/specialists/cx-debugger.json +75 -0
- package/specialists/org/specialists/cx-designer.json +85 -0
- package/specialists/org/specialists/cx-devil-advocate.json +71 -0
- package/specialists/org/specialists/cx-docs-keeper.json +82 -0
- package/specialists/org/specialists/cx-engineer.json +106 -0
- package/specialists/org/specialists/cx-evaluator.json +69 -0
- package/specialists/org/specialists/cx-explorer.json +69 -0
- package/specialists/org/specialists/cx-legal-compliance.json +74 -0
- package/specialists/org/specialists/cx-operations.json +72 -0
- package/specialists/org/specialists/cx-oracle.json +46 -0
- package/specialists/org/specialists/cx-orchestrator.json +81 -0
- package/specialists/org/specialists/cx-platform-engineer.json +80 -0
- package/specialists/org/specialists/cx-product-manager.json +102 -0
- package/specialists/org/specialists/cx-qa.json +79 -0
- package/specialists/org/specialists/cx-rd-lead.json +73 -0
- package/specialists/org/specialists/cx-release-manager.json +77 -0
- package/specialists/org/specialists/cx-researcher.json +82 -0
- package/specialists/org/specialists/cx-reviewer.json +71 -0
- package/specialists/org/specialists/cx-security.json +91 -0
- package/specialists/org/specialists/cx-sre.json +94 -0
- package/specialists/org/specialists/cx-test-automation.json +69 -0
- package/specialists/org/specialists/cx-trace-reviewer.json +69 -0
- package/specialists/org/specialists/cx-ux-researcher.json +68 -0
- package/specialists/org/teams/accessibility-team.json +39 -0
- package/specialists/org/teams/design-team.json +40 -0
- package/specialists/org/teams/engineering-team.json +50 -0
- package/specialists/org/teams/governance-team.json +41 -0
- package/specialists/org/teams/operations-team.json +46 -0
- package/specialists/org/teams/product-management-team.json +45 -0
- package/specialists/org/teams/quality-team.json +49 -0
- package/specialists/org/teams/research-team.json +39 -0
- package/specialists/org/teams/strategy-team.json +48 -0
- package/specialists/org/teams/ux-research-team.json +39 -0
- package/specialists/prompts/_shared/validation-contract.md +1 -1
- package/specialists/prompts/_team-template.md +10 -0
- package/specialists/prompts/cx-accessibility.md +1 -0
- package/specialists/prompts/cx-ai-engineer.md +1 -1
- package/specialists/prompts/cx-business-strategist.md +1 -1
- package/specialists/prompts/cx-data-engineer.md +1 -1
- package/specialists/prompts/cx-designer.md +1 -0
- package/specialists/prompts/cx-devil-advocate.md +1 -1
- package/specialists/prompts/cx-docs-keeper.md +1 -1
- package/specialists/prompts/cx-evaluator.md +1 -1
- package/specialists/prompts/cx-explorer.md +1 -1
- package/specialists/prompts/cx-operations.md +1 -1
- package/specialists/prompts/cx-oracle.md +7 -3
- package/specialists/prompts/cx-orchestrator.md +17 -1
- package/specialists/prompts/cx-platform-engineer.md +1 -1
- package/specialists/prompts/cx-product-manager.md +2 -0
- package/specialists/prompts/cx-release-manager.md +1 -1
- package/specialists/prompts/cx-researcher.md +7 -4
- package/specialists/prompts/cx-sre.md +1 -1
- package/specialists/prompts/cx-test-automation.md +2 -2
- package/specialists/prompts/cx-trace-reviewer.md +3 -3
- package/specialists/prompts/cx-ux-researcher.md +2 -1
- package/templates/demos/scripts/architecture-review-adr.json +30 -0
- package/templates/demos/scripts/capability-contract.json +25 -0
- package/templates/demos/scripts/intake-triage.json +25 -0
- package/templates/demos/scripts/profile-doctor-health.json +25 -0
- package/templates/demos/tapes/agentic-platforms-prd.tape +2 -2
- package/templates/demos/tapes/architecture-review-adr.tape +44 -0
- package/templates/demos/tapes/capability-contract.tape +39 -0
- package/templates/demos/tapes/intake-triage.tape +39 -0
- package/templates/demos/tapes/profile-doctor-health.tape +39 -0
- package/templates/distribution/construct-brand.typ +23 -18
- package/templates/distribution/construct-decision.typ +1 -1
- package/templates/distribution/construct-pdf.typ +1 -1
- package/templates/distribution/construct-prd.typ +1 -1
- package/templates/distribution/construct-research.typ +1 -1
- package/templates/distribution/mermaid-puppeteer.json +8 -0
- package/templates/docs/persona-artifact.md +1 -1
- package/templates/docs/prd.md +6 -0
- package/lib/boundary.mjs +0 -127
- package/lib/certification/dashboard-api.mjs +0 -71
- package/lib/chat/cli.mjs +0 -333
- package/lib/chat/command-suggest.mjs +0 -161
- package/lib/chat/commands.mjs +0 -215
- package/lib/chat/config.mjs +0 -142
- package/lib/chat/context-compactor.mjs +0 -250
- package/lib/chat/context-continuation.mjs +0 -253
- package/lib/chat/continuation-source.mjs +0 -58
- package/lib/chat/demo-guide.mjs +0 -61
- package/lib/chat/design-tokens.mjs +0 -91
- package/lib/chat/desktop-binary.mjs +0 -81
- package/lib/chat/desktop-build.mjs +0 -130
- package/lib/chat/desktop-launcher.mjs +0 -133
- package/lib/chat/evidence.mjs +0 -145
- package/lib/chat/export.mjs +0 -74
- package/lib/chat/harness/driver.mjs +0 -91
- package/lib/chat/list-picker.mjs +0 -112
- package/lib/chat/model-picker.mjs +0 -356
- package/lib/chat/openrouter-fallback.mjs +0 -151
- package/lib/chat/permission-prompt.mjs +0 -33
- package/lib/chat/picker-catalog.mjs +0 -45
- package/lib/chat/policy-telemetry.mjs +0 -34
- package/lib/chat/present.mjs +0 -246
- package/lib/chat/session-context.mjs +0 -39
- package/lib/chat/session-persist.mjs +0 -73
- package/lib/chat/session-restore.mjs +0 -71
- package/lib/chat/session-settings.mjs +0 -53
- package/lib/chat/system-prompt.mjs +0 -52
- package/lib/chat/transparency.mjs +0 -93
- package/lib/chat/tui/color-scheme.mjs +0 -42
- package/lib/chat/tui/markdown.mjs +0 -123
- package/lib/chat/tui/presentation.mjs +0 -100
- package/lib/chat/tui/render.mjs +0 -500
- package/lib/chat/tui/turn-block.mjs +0 -284
- package/lib/chat/tui/turn-present.mjs +0 -18
- package/lib/chat/tui/turn-state.mjs +0 -88
- package/lib/chat/tui/usage.mjs +0 -122
- package/lib/chat/web-commands.mjs +0 -146
- package/lib/chat/web-launcher.mjs +0 -63
- package/lib/chat/web-picker-keys.mjs +0 -46
- package/lib/chat/web-session.mjs +0 -159
- package/lib/config/alias.mjs +0 -57
- package/lib/dashboard-demo.mjs +0 -71
- package/lib/dashboard-static.mjs +0 -175
- package/lib/install/desktop-binary-download.mjs +0 -88
- package/lib/profiles/loader.mjs +0 -123
- package/lib/profiles/rebrand.mjs +0 -46
- package/lib/server/auth.mjs +0 -169
- package/lib/server/chat-loop.mjs +0 -622
- package/lib/server/chat.mjs +0 -336
- package/lib/server/cors.mjs +0 -77
- package/lib/server/csrf.mjs +0 -103
- package/lib/server/demo-preview.mjs +0 -63
- package/lib/server/index.mjs +0 -2641
- package/lib/server/insights.mjs +0 -780
- package/lib/server/langfuse-login.mjs +0 -58
- package/lib/server/rate-limit.mjs +0 -93
- package/lib/server/telemetry-login.mjs +0 -100
- package/lib/server/webhook.mjs +0 -512
- package/specialists/contracts.json +0 -1032
- package/specialists/contracts.schema.json +0 -83
- package/specialists/policy-inventory.json +0 -188
- package/specialists/registry.json +0 -1412
- package/specialists/role-manifests.json +0 -217
- package/specialists/teams.json +0 -94
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/mcp/tools/find-tool.mjs — find_tool: intent-driven tool discovery.
|
|
3
|
+
*
|
|
4
|
+
* Ranks the tool catalog against a natural-language query so an agent that knows
|
|
5
|
+
* what it wants to do, but not the exact tool name, gets the right tools with
|
|
6
|
+
* their schemas. Hybrid scoring: normalized BM25 (always, offline) merged with
|
|
7
|
+
* local-embedding cosine when a real semantic model is available. Degraded
|
|
8
|
+
* hashing embeddings are ignored so they cannot pollute the lexical ranking.
|
|
9
|
+
*/
|
|
10
|
+
import { rankByBm25, cosineSimilarity } from '../../storage/embeddings.mjs';
|
|
11
|
+
|
|
12
|
+
let toolVectorCache = null;
|
|
13
|
+
|
|
14
|
+
function buildCorpus(toolDefs) {
|
|
15
|
+
return toolDefs.map((t) => ({
|
|
16
|
+
name: t.name,
|
|
17
|
+
description: t.description || '',
|
|
18
|
+
inputSchema: t.inputSchema,
|
|
19
|
+
text: `${t.name} ${t.description || ''}`,
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function mergeCosine(scoreMap, corpus, query, env) {
|
|
24
|
+
const { embedText, embedBatch } = await import('../../storage/embeddings-engine.mjs');
|
|
25
|
+
const q = await embedText(query, { env });
|
|
26
|
+
|
|
27
|
+
// A degraded result means the real model was unavailable and hashing stood in;
|
|
28
|
+
// those vectors carry no semantic signal, so leave the BM25 ranking unmerged.
|
|
29
|
+
|
|
30
|
+
if (q?.degraded || !q?.embedding) return;
|
|
31
|
+
|
|
32
|
+
// Embed the catalog once per process: the tool set is static for the server's
|
|
33
|
+
// lifetime, so size-equality is a sufficient cache key. If tools ever become
|
|
34
|
+
// dynamic (MCP list_changed), this needs a real version key. embedBatch
|
|
35
|
+
// degrades all-or-nothing, so one degraded vector means the whole batch fell
|
|
36
|
+
// back to hashing — skip the merge rather than mix real and hashed vectors.
|
|
37
|
+
|
|
38
|
+
if (!toolVectorCache || toolVectorCache.size !== corpus.length) {
|
|
39
|
+
const vecs = await embedBatch(corpus.map((c) => c.text), { env });
|
|
40
|
+
if (vecs.some((v) => v?.degraded)) return;
|
|
41
|
+
toolVectorCache = new Map(corpus.map((c, i) => [c.name, vecs[i].embedding]));
|
|
42
|
+
}
|
|
43
|
+
for (const c of corpus) {
|
|
44
|
+
const cos = cosineSimilarity(q.embedding, toolVectorCache.get(c.name) || []);
|
|
45
|
+
if (cos > 0.05) scoreMap.set(c.name, Math.max(scoreMap.get(c.name) || 0, cos));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function findTool(args = {}, { toolDefs = [], env = process.env } = {}) {
|
|
50
|
+
const query = String(args.query || args.intent || '').trim();
|
|
51
|
+
if (!query) return { error: 'find_tool requires a `query` describing what you want to do.' };
|
|
52
|
+
const limit = Math.min(Math.max(Number(args.limit) || 5, 1), 20);
|
|
53
|
+
|
|
54
|
+
const corpus = buildCorpus(toolDefs);
|
|
55
|
+
|
|
56
|
+
// BM25 is unbounded; normalize against its own max so it merges fairly with
|
|
57
|
+
// cosine on [0,1].
|
|
58
|
+
|
|
59
|
+
const bm25 = rankByBm25(corpus, query, { limit: 20 });
|
|
60
|
+
const bm25Max = bm25[0]?.score || 1;
|
|
61
|
+
const scoreMap = new Map();
|
|
62
|
+
for (const d of bm25) scoreMap.set(d.name, Math.min(d.score / bm25Max, 1));
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
await mergeCosine(scoreMap, corpus, query, env);
|
|
66
|
+
} catch {
|
|
67
|
+
/* embeddings unavailable — BM25-only is the offline-safe path */
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const byName = new Map(corpus.map((c) => [c.name, c]));
|
|
71
|
+
const tools = [...scoreMap.entries()]
|
|
72
|
+
.sort((a, b) => b[1] - a[1])
|
|
73
|
+
.slice(0, limit)
|
|
74
|
+
.map(([name, score]) => ({
|
|
75
|
+
name,
|
|
76
|
+
description: byName.get(name).description,
|
|
77
|
+
inputSchema: byName.get(name).inputSchema,
|
|
78
|
+
score: Number(score.toFixed(3)),
|
|
79
|
+
}));
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
query,
|
|
83
|
+
tools,
|
|
84
|
+
note: 'Invoke a result with { name: "call", arguments: { tool: "<name>", args: {…} } }, or call it directly if it is already a flat tool.',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -1,123 +1,161 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/mcp/tools/orchestration-run.mjs — MCP
|
|
2
|
+
* lib/mcp/tools/orchestration-run.mjs — MCP entry for Construct orchestration.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
4
|
+
* Runs a real multi-specialist orchestration so MCP hosts with no subagent
|
|
5
|
+
* primitive (VS Code/Copilot, Cursor) reach orchestrated outcomes through a tool
|
|
6
|
+
* they already expose. The engine is the in-process orchestration runtime
|
|
7
|
+
* (lib/orchestration/runtime.mjs) — solo runs need no daemon, no port, no token
|
|
8
|
+
* (ADR-0022: the engine owns orchestration; ADR-0041 owned loop). When
|
|
9
|
+
* CONSTRUCT_ORCHESTRATION_URL points at a remote/team orchestration service the
|
|
10
|
+
* same call is proxied over HTTP instead, with the bearer token resolved from the
|
|
11
|
+
* Construct env (CONSTRUCT_ORCHESTRATION_TOKEN / CONSTRUCT_DASHBOARD_TOKEN), not
|
|
12
|
+
* from the dashboard server.
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
|
-
import {
|
|
15
|
-
import { getDashboardToken } from '../../server/auth.mjs';
|
|
15
|
+
import { runOrchestration, startRun, getRun, getRuns } from '../../orchestration/runtime.mjs';
|
|
16
16
|
|
|
17
17
|
const TERMINAL = ['completed', 'completed-with-failures', 'cancelled', 'error'];
|
|
18
18
|
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
function shapeRun(run) {
|
|
20
|
+
return {
|
|
21
|
+
runId: run.runId,
|
|
22
|
+
status: run.status,
|
|
23
|
+
executionMode: run.execution?.executionMode,
|
|
24
|
+
degraded: run.execution?.degraded ?? false,
|
|
25
|
+
degradationReason: run.execution?.degradationReason ?? null,
|
|
26
|
+
intent: run.plan?.intent ?? null,
|
|
27
|
+
track: run.plan?.track ?? null,
|
|
28
|
+
suggestedWorkflowType: run.plan?.suggestedWorkflowType ?? null,
|
|
29
|
+
researchExecutionPolicy: run.plan?.researchExecutionPolicy ?? null,
|
|
30
|
+
specialists: run.plan?.specialists ?? [],
|
|
31
|
+
tasks: (run.tasks || []).map((t) => ({
|
|
32
|
+
id: t.id, role: t.role, status: t.status, executor: t.executor,
|
|
33
|
+
output: t.output ?? null, reasoning: t.reasoning ?? null, error: t.error ?? null,
|
|
34
|
+
})),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function toRequest(args) {
|
|
39
|
+
return {
|
|
40
|
+
request: args.request,
|
|
41
|
+
workflowType: args.workflow_type,
|
|
42
|
+
requestedStrategy: args.requested_strategy ?? 'auto',
|
|
43
|
+
host: args.host,
|
|
44
|
+
hostModel: args.host_model,
|
|
45
|
+
hostProvider: args.host_provider,
|
|
46
|
+
fileCount: args.file_count,
|
|
47
|
+
moduleCount: args.module_count,
|
|
48
|
+
};
|
|
23
49
|
}
|
|
24
50
|
|
|
25
|
-
// A
|
|
26
|
-
//
|
|
51
|
+
// A remote orchestration service is opt-in: only when CONSTRUCT_ORCHESTRATION_URL
|
|
52
|
+
// is set (team / enterprise). The token comes from the Construct env, not from
|
|
53
|
+
// the dashboard server, so the in-process default path carries no dashboard coupling.
|
|
54
|
+
|
|
55
|
+
function remoteService(env) {
|
|
56
|
+
const url = env.CONSTRUCT_ORCHESTRATION_URL;
|
|
57
|
+
if (!url) return null;
|
|
58
|
+
const token = env.CONSTRUCT_ORCHESTRATION_TOKEN || env.CONSTRUCT_DASHBOARD_TOKEN || null;
|
|
59
|
+
return { base: url.replace(/\/$/, ''), token };
|
|
60
|
+
}
|
|
27
61
|
|
|
28
62
|
function unreachable(base, detail) {
|
|
29
63
|
return {
|
|
30
|
-
error: `
|
|
31
|
-
|
|
64
|
+
error: `Remote orchestration service not reachable at ${base}: ${detail}. Unset CONSTRUCT_ORCHESTRATION_URL to run in-process, or start the service and retry.`,
|
|
65
|
+
service: base,
|
|
32
66
|
failFast: true,
|
|
33
67
|
};
|
|
34
68
|
}
|
|
35
69
|
|
|
36
|
-
export async function orchestrationRun(args = {}, {
|
|
37
|
-
const {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
export async function orchestrationRun(args = {}, { env = process.env, cwd = process.cwd(), fetchImpl = fetch } = {}) {
|
|
71
|
+
const { request, worker_backend, wait = true, timeout_ms = 120000 } = args;
|
|
72
|
+
if (!request || typeof request !== 'string') {
|
|
73
|
+
return { error: 'Missing "request" string describing the work to orchestrate.' };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const reqObj = toRequest(args);
|
|
77
|
+
const remote = remoteService(env);
|
|
78
|
+
if (remote) return runViaService(remote, reqObj, { wait, timeout_ms, workerBackend: worker_backend, fetchImpl });
|
|
79
|
+
|
|
80
|
+
const opts = { env, cwd, workerBackend: worker_backend, fetchImpl };
|
|
81
|
+
try {
|
|
82
|
+
if (!wait) {
|
|
83
|
+
const planned = await startRun(reqObj, opts);
|
|
84
|
+
return { runId: planned.runId, status: planned.status, executionMode: planned.execution?.executionMode, poll: 'orchestration_status with run_id' };
|
|
85
|
+
}
|
|
86
|
+
return shapeRun(await runOrchestration(reqObj, opts));
|
|
87
|
+
} catch (err) {
|
|
88
|
+
return { error: `Orchestration failed: ${err.message}`, failFast: true };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function orchestrationStatus(args = {}, { env = process.env, cwd = process.cwd(), fetchImpl = fetch } = {}) {
|
|
93
|
+
const { run_id, limit = 20 } = args;
|
|
94
|
+
const remote = remoteService(env);
|
|
95
|
+
if (remote) return statusViaService(remote, { run_id, limit }, { fetchImpl });
|
|
96
|
+
try {
|
|
97
|
+
if (run_id) {
|
|
98
|
+
const run = await getRun(cwd, run_id, { env });
|
|
99
|
+
return run || { error: `No run found with id ${run_id}.` };
|
|
100
|
+
}
|
|
101
|
+
return await getRuns(cwd, { env, limit });
|
|
102
|
+
} catch (err) {
|
|
103
|
+
return { error: `Failed to read orchestration runs: ${err.message}` };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Remote (team / enterprise) HTTP path. Reached only when CONSTRUCT_ORCHESTRATION_URL
|
|
108
|
+
// is set; the wire shape matches the standalone orchestration service's
|
|
109
|
+
// /api/orchestration/runs contract (ADR-0022).
|
|
66
110
|
|
|
111
|
+
async function runViaService(remote, reqObj, { wait, timeout_ms, workerBackend, fetchImpl }) {
|
|
112
|
+
const { base, token } = remote;
|
|
113
|
+
const headers = { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) };
|
|
114
|
+
const body = JSON.stringify({ ...reqObj, workerBackend });
|
|
67
115
|
let started;
|
|
68
116
|
try {
|
|
69
117
|
const res = await fetchImpl(`${base}/api/orchestration/runs`, { method: 'POST', headers, body });
|
|
70
118
|
if (res.status === 401 || res.status === 403) {
|
|
71
|
-
return { error: `
|
|
119
|
+
return { error: `Remote service rejected the request (HTTP ${res.status}). Set CONSTRUCT_ORCHESTRATION_TOKEN (or CONSTRUCT_DASHBOARD_TOKEN).`, service: base, failFast: true };
|
|
72
120
|
}
|
|
73
121
|
const envelope = await res.json();
|
|
74
|
-
if (!res.ok) return { error: `
|
|
122
|
+
if (!res.ok) return { error: `Remote service error (HTTP ${res.status}): ${envelope.error || 'unknown'}`, service: base };
|
|
75
123
|
started = envelope.data;
|
|
76
124
|
} catch (err) {
|
|
77
125
|
return unreachable(base, err.message);
|
|
78
126
|
}
|
|
79
127
|
|
|
80
128
|
if (!wait) {
|
|
81
|
-
return { runId: started.runId, status: started.status, executionMode: started.execution?.executionMode,
|
|
129
|
+
return { runId: started.runId, status: started.status, executionMode: started.execution?.executionMode, service: base, poll: 'orchestration_status with run_id' };
|
|
82
130
|
}
|
|
83
131
|
|
|
84
132
|
const deadline = Date.now() + timeout_ms;
|
|
85
133
|
let run = started;
|
|
86
134
|
while (!TERMINAL.includes(run.status)) {
|
|
87
135
|
if (Date.now() > deadline) {
|
|
88
|
-
return { runId: started.runId, status: run.status, timedOut: true,
|
|
136
|
+
return { runId: started.runId, status: run.status, timedOut: true, service: base, note: 'Run is still executing; poll it with orchestration_status.' };
|
|
89
137
|
}
|
|
90
138
|
await new Promise((r) => setTimeout(r, 250));
|
|
91
139
|
try {
|
|
92
140
|
const res = await fetchImpl(`${base}/api/orchestration/runs/${encodeURIComponent(started.runId)}`, { headers });
|
|
93
141
|
const envelope = await res.json();
|
|
94
|
-
if (!res.ok) return { error: `
|
|
142
|
+
if (!res.ok) return { error: `Remote service error while polling (HTTP ${res.status}): ${envelope.error || 'unknown'}`, runId: started.runId, service: base };
|
|
95
143
|
run = envelope.data;
|
|
96
144
|
} catch (err) {
|
|
97
|
-
return { error: `Lost connection to the
|
|
145
|
+
return { error: `Lost connection to the remote service while polling run ${started.runId}: ${err.message}`, runId: started.runId, service: base, failFast: true };
|
|
98
146
|
}
|
|
99
147
|
}
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
runId: run.runId,
|
|
103
|
-
status: run.status,
|
|
104
|
-
executionMode: run.execution?.executionMode,
|
|
105
|
-
degraded: run.execution?.degraded ?? false,
|
|
106
|
-
degradationReason: run.execution?.degradationReason ?? null,
|
|
107
|
-
tasks: (run.tasks || []).map((t) => ({ id: t.id, role: t.role, status: t.status, executor: t.executor, output: t.output ?? null, reasoning: t.reasoning ?? null, error: t.error ?? null })),
|
|
108
|
-
daemon: base,
|
|
109
|
-
};
|
|
148
|
+
return { ...shapeRun(run), service: base };
|
|
110
149
|
}
|
|
111
150
|
|
|
112
|
-
|
|
113
|
-
const {
|
|
114
|
-
const { base, token } = resolveDaemon(env);
|
|
151
|
+
async function statusViaService(remote, { run_id, limit }, { fetchImpl }) {
|
|
152
|
+
const { base, token } = remote;
|
|
115
153
|
const headers = token ? { Authorization: `Bearer ${token}` } : {};
|
|
116
154
|
const path = run_id ? `/api/orchestration/runs/${encodeURIComponent(run_id)}` : `/api/orchestration/runs?limit=${encodeURIComponent(limit)}`;
|
|
117
155
|
try {
|
|
118
156
|
const res = await fetchImpl(`${base}${path}`, { headers });
|
|
119
157
|
const envelope = await res.json();
|
|
120
|
-
if (!res.ok) return { error: `
|
|
158
|
+
if (!res.ok) return { error: `Remote service error (HTTP ${res.status}): ${envelope.error || 'unknown'}`, service: base };
|
|
121
159
|
return envelope.data;
|
|
122
160
|
} catch (err) {
|
|
123
161
|
return unreachable(base, err.message);
|
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
* All tools are synchronous except where noted. Requires ROOT_DIR and homedir from the caller.
|
|
6
6
|
*/
|
|
7
7
|
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
8
|
+
import { loadRegistry } from '../../registry/loader.mjs';
|
|
8
9
|
import { join, resolve } from 'node:path';
|
|
9
10
|
import { homedir } from 'node:os';
|
|
10
11
|
import { execSync } from 'node:child_process';
|
|
11
12
|
import { loadConstructEnv } from '../../env-config.mjs';
|
|
12
13
|
import { inspectContextState } from '../../context-state.mjs';
|
|
13
14
|
import { loadWorkflow, summarizeWorkflow, inspectWorkflowHealth } from '../../workflow-state.mjs';
|
|
14
|
-
import {
|
|
15
|
+
import { resolveExecutionContractModelMetadata, selectModelTierForWorkCategory } from '../../model-router.mjs';
|
|
15
16
|
import { buildPublicHealthSurface } from '../../status.mjs';
|
|
16
17
|
import { doctorRoot } from '../../config/xdg.mjs';
|
|
17
18
|
|
|
@@ -282,7 +283,7 @@ export function projectContext(args, { ROOT_DIR }) {
|
|
|
282
283
|
const has_context_file = contextInspection.hasFile;
|
|
283
284
|
const workflow = loadWorkflow(cwd);
|
|
284
285
|
const mergedEnv = loadConstructEnv({ rootDir: ROOT_DIR, homeDir: homedir(), env: process.env });
|
|
285
|
-
const registry =
|
|
286
|
+
const registry = loadRegistry({ rootDir: ROOT_DIR });
|
|
286
287
|
const executionContractModel = resolveExecutionContractModelMetadata({
|
|
287
288
|
envValues: mergedEnv,
|
|
288
289
|
registryModels: registry.models ?? {},
|
|
@@ -324,7 +325,7 @@ export function workflowStatus(args, { ROOT_DIR }) {
|
|
|
324
325
|
const workflow = loadWorkflow(cwd);
|
|
325
326
|
const workflowHealth = inspectWorkflowHealth(workflow, { cwd });
|
|
326
327
|
const mergedEnv = loadConstructEnv({ rootDir: ROOT_DIR, homeDir: homedir(), env: process.env });
|
|
327
|
-
const registry =
|
|
328
|
+
const registry = loadRegistry({ rootDir: ROOT_DIR });
|
|
328
329
|
const executionContractModel = resolveExecutionContractModelMetadata({
|
|
329
330
|
envValues: mergedEnv,
|
|
330
331
|
registryModels: registry.models ?? {},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/mcp/tools/
|
|
2
|
+
* lib/mcp/tools/scope.mjs — MCP wrappers for the org-scope + learning surfaces.
|
|
3
3
|
*
|
|
4
|
-
* Bridges PR #67 capabilities (
|
|
4
|
+
* Bridges PR #67 capabilities (scopes, outcomes, sandboxes, knowledge_add,
|
|
5
5
|
* learning status) so subagents talking through the construct-mcp server can
|
|
6
6
|
* read the same state the CLI exposes. Operator-only surfaces (optimize_apply,
|
|
7
7
|
* optimize_rollback) remain CLI-only and are not registered here.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Pattern: each export is a thin schema-validated wrapper around the existing
|
|
10
10
|
* lib/* function the CLI already uses. Errors return `{ error: string }` so
|
|
11
11
|
* the MCP dispatcher hands a structured failure back instead of crashing.
|
|
12
|
-
* Destructive operations (
|
|
12
|
+
* Destructive operations (scope_archive) require `confirm: true`, matching
|
|
13
13
|
* the storage_reset / delete_ingested_artifacts pattern in tools/storage.mjs.
|
|
14
14
|
*/
|
|
15
15
|
import { resolve } from 'node:path';
|
|
@@ -17,17 +17,17 @@ import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
|
17
17
|
import { join } from 'node:path';
|
|
18
18
|
|
|
19
19
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
} from '../../
|
|
20
|
+
loadScope,
|
|
21
|
+
listScopes,
|
|
22
|
+
loadCustomScope,
|
|
23
|
+
resolveActiveScope,
|
|
24
|
+
} from '../../scopes/loader.mjs';
|
|
25
25
|
import {
|
|
26
|
-
|
|
26
|
+
createDraftScope,
|
|
27
27
|
listDrafts,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
} from '../../
|
|
28
|
+
archiveScope,
|
|
29
|
+
scopeHealth,
|
|
30
|
+
} from '../../scopes/lifecycle.mjs';
|
|
31
31
|
import { recordOutcome } from '../../outcomes/record.mjs';
|
|
32
32
|
import { aggregateOutcomes, readSummary } from '../../outcomes/aggregate.mjs';
|
|
33
33
|
import { addResearchFinding } from '../../knowledge/research-store.mjs';
|
|
@@ -38,73 +38,75 @@ function cwdOf(args) {
|
|
|
38
38
|
return args && args.cwd ? resolve(String(args.cwd)) : process.cwd();
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// Read-only: active
|
|
41
|
+
// Read-only: active scope.
|
|
42
42
|
|
|
43
|
-
export function
|
|
43
|
+
export function scopeShow(args = {}) {
|
|
44
44
|
const cwd = cwdOf(args);
|
|
45
|
-
const
|
|
45
|
+
const scope = resolveActiveScope(cwd, args.id ? String(args.id) : null);
|
|
46
46
|
return {
|
|
47
|
-
id:
|
|
48
|
-
displayName:
|
|
49
|
-
tagline:
|
|
50
|
-
custom:
|
|
51
|
-
roles: Array.isArray(
|
|
52
|
-
|
|
53
|
-
?
|
|
47
|
+
id: scope.id,
|
|
48
|
+
displayName: scope.displayName ?? scope.id,
|
|
49
|
+
tagline: scope.tagline ?? null,
|
|
50
|
+
custom: scope.custom === true,
|
|
51
|
+
roles: Array.isArray(scope.roles) ? scope.roles : [],
|
|
52
|
+
teams: Array.isArray(scope.teams)
|
|
53
|
+
? scope.teams.map((t) => ({ id: t.id, name: t.name, roleCount: Array.isArray(t.roles) ? t.roles.length : 0 }))
|
|
54
54
|
: [],
|
|
55
|
+
experimental: scope.experimental === true,
|
|
55
56
|
intake: {
|
|
56
|
-
types:
|
|
57
|
-
stages:
|
|
57
|
+
types: scope.intake?.types ?? [],
|
|
58
|
+
stages: scope.intake?.stages ?? [],
|
|
58
59
|
},
|
|
59
|
-
docTemplates:
|
|
60
|
-
hooks:
|
|
61
|
-
rebrand:
|
|
60
|
+
docTemplates: scope.docTemplates ?? [],
|
|
61
|
+
hooks: scope.hooks ?? null,
|
|
62
|
+
rebrand: scope.rebrand ?? null,
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
// Read-only: catalog summary.
|
|
66
67
|
|
|
67
|
-
export function
|
|
68
|
+
export function scopeList() {
|
|
68
69
|
return {
|
|
69
|
-
|
|
70
|
-
const p =
|
|
70
|
+
scopes: listScopes().map((id) => {
|
|
71
|
+
const p = loadScope(id);
|
|
71
72
|
return p
|
|
72
73
|
? {
|
|
73
74
|
id: p.id,
|
|
74
75
|
displayName: p.displayName ?? p.id,
|
|
75
76
|
tagline: p.tagline ?? null,
|
|
76
77
|
roleCount: Array.isArray(p.roles) ? p.roles.length : 0,
|
|
77
|
-
|
|
78
|
+
teamCount: Array.isArray(p.teams) ? p.teams.length : 0,
|
|
79
|
+
experimental: p.experimental === true,
|
|
78
80
|
}
|
|
79
81
|
: { id, error: 'failed to parse' };
|
|
80
82
|
}),
|
|
81
83
|
};
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
// Read-only: drafts + custom
|
|
86
|
+
// Read-only: drafts + custom scope under .cx/.
|
|
85
87
|
|
|
86
|
-
export function
|
|
88
|
+
export function scopeDrafts(args = {}) {
|
|
87
89
|
const cwd = cwdOf(args);
|
|
88
90
|
const drafts = listDrafts(cwd).map((d) => ({
|
|
89
91
|
id: d.id,
|
|
90
92
|
dir: d.dir,
|
|
91
|
-
|
|
93
|
+
hasScope: d.hasScope,
|
|
92
94
|
hasBrief: d.hasBrief,
|
|
93
95
|
}));
|
|
94
|
-
const custom =
|
|
96
|
+
const custom = loadCustomScope(cwd);
|
|
95
97
|
return {
|
|
96
98
|
drafts,
|
|
97
99
|
custom: custom ? { id: custom.id, displayName: custom.displayName ?? custom.id } : null,
|
|
98
100
|
};
|
|
99
101
|
}
|
|
100
102
|
|
|
101
|
-
// Read-only: per-
|
|
103
|
+
// Read-only: per-scope health rollup.
|
|
102
104
|
|
|
103
|
-
export function
|
|
105
|
+
export function scopeHealthTool(args = {}) {
|
|
104
106
|
const cwd = cwdOf(args);
|
|
105
|
-
const id = args.id ? String(args.id) :
|
|
107
|
+
const id = args.id ? String(args.id) : resolveActiveScope(cwd).id;
|
|
106
108
|
const windowDays = Number.isFinite(args.window_days) ? Number(args.window_days) : 30;
|
|
107
|
-
return { id, windowDays, ...
|
|
109
|
+
return { id, windowDays, ...scopeHealth(cwd, id, { windowDays }) };
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
// Read-only: outcomes rollup. Optional aggregate=true rebuilds _summary.json.
|
|
@@ -129,7 +131,7 @@ export function outcomesRecord(args = {}) {
|
|
|
129
131
|
if (!args.role || typeof args.success !== 'boolean') {
|
|
130
132
|
return { error: 'outcomes_record requires role:string and success:boolean' };
|
|
131
133
|
}
|
|
132
|
-
const profile = args.profile ? String(args.profile) :
|
|
134
|
+
const profile = args.profile ? String(args.profile) : resolveActiveScope(cwd)?.id;
|
|
133
135
|
const file = recordOutcome(cwd, {
|
|
134
136
|
role: String(args.role),
|
|
135
137
|
intakeId: args.intake_id ? String(args.intake_id) : null,
|
|
@@ -167,16 +169,16 @@ export async function knowledgeAdd(args = {}) {
|
|
|
167
169
|
}
|
|
168
170
|
}
|
|
169
171
|
|
|
170
|
-
// Mutating, gated: scaffold a draft
|
|
172
|
+
// Mutating, gated: scaffold a draft scope.
|
|
171
173
|
|
|
172
|
-
export function
|
|
174
|
+
export function scopeCreate(args = {}) {
|
|
173
175
|
if (args.confirm !== true) {
|
|
174
|
-
return { error: '
|
|
176
|
+
return { error: 'scope_create requires confirm=true to scaffold files' };
|
|
175
177
|
}
|
|
176
178
|
const cwd = cwdOf(args);
|
|
177
|
-
if (!args.id) return { error: '
|
|
179
|
+
if (!args.id) return { error: 'scope_create requires id:string' };
|
|
178
180
|
try {
|
|
179
|
-
const res =
|
|
181
|
+
const res = createDraftScope({
|
|
180
182
|
cwd,
|
|
181
183
|
id: String(args.id),
|
|
182
184
|
displayName: args.display_name ? String(args.display_name) : undefined,
|
|
@@ -196,17 +198,17 @@ export function profileCreate(args = {}) {
|
|
|
196
198
|
}
|
|
197
199
|
}
|
|
198
200
|
|
|
199
|
-
// Mutating, gated, destructive: archive a curated
|
|
201
|
+
// Mutating, gated, destructive: archive a curated scope.
|
|
200
202
|
|
|
201
|
-
export function
|
|
203
|
+
export function scopeArchive(args = {}) {
|
|
202
204
|
if (args.confirm !== true) {
|
|
203
|
-
return { error: '
|
|
205
|
+
return { error: 'scope_archive requires confirm=true (destructive: moves files into archive/scopes/)' };
|
|
204
206
|
}
|
|
205
207
|
if (!args.id || !args.reason || String(args.reason).trim().length < 8) {
|
|
206
|
-
return { error: '
|
|
208
|
+
return { error: 'scope_archive requires id:string and reason:string (>=8 chars)' };
|
|
207
209
|
}
|
|
208
210
|
try {
|
|
209
|
-
return { ok: true, ...
|
|
211
|
+
return { ok: true, ...archiveScope({ id: String(args.id), reason: String(args.reason) }) };
|
|
210
212
|
} catch (err) {
|
|
211
213
|
return { error: err.message ?? String(err) };
|
|
212
214
|
}
|
|
@@ -258,11 +260,11 @@ export function learningStatus(args = {}) {
|
|
|
258
260
|
}
|
|
259
261
|
} catch { /* best effort */ }
|
|
260
262
|
|
|
261
|
-
const
|
|
263
|
+
const scope = resolveActiveScope(cwd);
|
|
262
264
|
const outcomes = readSummary(cwd) || { roles: {} };
|
|
263
265
|
|
|
264
266
|
return {
|
|
265
|
-
|
|
267
|
+
scope: { id: scope.id, displayName: scope.displayName ?? scope.id, custom: scope.custom === true },
|
|
266
268
|
observations,
|
|
267
269
|
research: { count: researchCount },
|
|
268
270
|
outcomes,
|