@geraldmaron/construct 1.2.3 → 1.3.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 +7 -10
- package/bin/construct +235 -209
- 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 +55 -54
- package/lib/comment-lint.mjs +1 -5
- package/lib/completions.mjs +20 -2
- package/lib/config/legacy-config-migration.mjs +90 -24
- 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/cli.mjs +12 -2
- package/lib/doctor/source-checkout.mjs +3 -4
- package/lib/doctor/watchers/consistency.mjs +148 -43
- 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/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 +6 -26
- 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 -8
- 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 +269 -51
- 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/apps/chat/engine/ai-sdk-agent.mjs +0 -183
- package/apps/chat/engine/loop-driver.mjs +0 -211
- package/apps/chat/engine/models.mjs +0 -122
- package/apps/chat/engine/provider-adapters.mjs +0 -171
- package/apps/chat/engine/tools/permission.mjs +0 -54
- package/apps/chat/engine/tools/primitives.mjs +0 -180
- package/apps/chat/engine/tools/registry.mjs +0 -122
- package/apps/chat/engine/turn-controls.mjs +0 -70
- 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
|
@@ -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,
|
package/lib/mcp/tools/skills.mjs
CHANGED
|
@@ -19,6 +19,9 @@ import { buildTaskPacketFromIntent } from '../../workflow-state.mjs';
|
|
|
19
19
|
import { logSkillCall } from '../../telemetry/skill-calls.mjs';
|
|
20
20
|
import { templateMetadata } from '../../artifact-manifest.mjs';
|
|
21
21
|
import { buildSpecialistCatalog } from '../../specialists/roster.mjs';
|
|
22
|
+
import { loadRegistry } from '../../registry/loader.mjs';
|
|
23
|
+
import { listGroupsHierarchical } from '../../registry/assemble.mjs';
|
|
24
|
+
import { suggestSkills } from '../../skills/router.mjs';
|
|
22
25
|
|
|
23
26
|
export function listSkills({ ROOT_DIR }) {
|
|
24
27
|
const skillsDir = join(ROOT_DIR, 'skills');
|
|
@@ -48,9 +51,22 @@ export function listSkills({ ROOT_DIR }) {
|
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
export function getSkill(args, { ROOT_DIR }) {
|
|
51
|
-
const { path: skillPath } = args;
|
|
54
|
+
const { path: skillPath, specialistId, agentId } = args;
|
|
52
55
|
if (!skillPath) return { error: 'Missing path argument' };
|
|
53
56
|
|
|
57
|
+
const strict = process.env.CONSTRUCT_STRICT_SKILLS === '1';
|
|
58
|
+
const specId = specialistId || agentId;
|
|
59
|
+
let entitlementWarning = null;
|
|
60
|
+
if (specId) {
|
|
61
|
+
const registry = loadRegistry({ rootDir: ROOT_DIR, skipValidation: true });
|
|
62
|
+
const id = String(specId).startsWith('cx-') ? specId : `cx-${specId}`;
|
|
63
|
+
const entitled = new Set(registry.specialists?.[id]?.skills || []);
|
|
64
|
+
if (entitled.size > 0 && !entitled.has(skillPath)) {
|
|
65
|
+
entitlementWarning = `Skill ${skillPath} is not in ${id} entitlement list`;
|
|
66
|
+
if (strict) return { error: entitlementWarning };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
54
70
|
// Primary: flat file form skills/<path>.md
|
|
55
71
|
// Fallback: directory form skills/<path>/SKILL.md (one minor-release grace period)
|
|
56
72
|
const candidates = [
|
|
@@ -73,7 +89,7 @@ export function getSkill(args, { ROOT_DIR }) {
|
|
|
73
89
|
sessionId: args.sessionId,
|
|
74
90
|
tokensReturned: Math.ceil(content.length / 4),
|
|
75
91
|
});
|
|
76
|
-
return { content };
|
|
92
|
+
return { content, ...(entitlementWarning ? { warning: entitlementWarning } : {}) };
|
|
77
93
|
}
|
|
78
94
|
|
|
79
95
|
export function searchSkills(args, { ROOT_DIR }) {
|
|
@@ -434,29 +450,30 @@ export async function workerRun(args = {}) {
|
|
|
434
450
|
return { ...result, ...(evidence ? { evidence } : {}) };
|
|
435
451
|
}
|
|
436
452
|
|
|
437
|
-
export function
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
453
|
+
export function suggestSkillsTool(args, { ROOT_DIR, cwd } = {}) {
|
|
454
|
+
const intent = args?.intent || args?.query || '';
|
|
455
|
+
return suggestSkills({
|
|
456
|
+
intent,
|
|
457
|
+
specialistId: args?.specialistId || args?.agentId,
|
|
458
|
+
limit: args?.limit || 5,
|
|
459
|
+
rootDir: ROOT_DIR,
|
|
460
|
+
cwd: cwd || ROOT_DIR,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export function listTeams({ ROOT_DIR } = {}) {
|
|
465
|
+
const registry = loadRegistry({ rootDir: ROOT_DIR, skipValidation: true });
|
|
441
466
|
return {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
description: t.description,
|
|
445
|
-
members: t.members,
|
|
446
|
-
focus: t.focus,
|
|
447
|
-
skills: t.skills,
|
|
448
|
-
promotionGates: t.promotionGates,
|
|
449
|
-
})),
|
|
467
|
+
groups: listGroupsHierarchical(registry),
|
|
468
|
+
teams: Object.entries(registry.teams || {}).map(([id, t]) => ({ id, ...t })),
|
|
450
469
|
};
|
|
451
470
|
}
|
|
452
471
|
|
|
453
|
-
export function getTeam(args, { ROOT_DIR }) {
|
|
454
|
-
const
|
|
472
|
+
export function getTeam(args, { ROOT_DIR } = {}) {
|
|
473
|
+
const name = args?.name || args?.id;
|
|
455
474
|
if (!name) return { error: 'Missing name argument' };
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
if (!team) return { error: `Team not found: ${name}`, available: Object.keys(templates) };
|
|
461
|
-
return { name, ...team };
|
|
475
|
+
const registry = loadRegistry({ rootDir: ROOT_DIR, skipValidation: true });
|
|
476
|
+
const team = registry.teams?.[name];
|
|
477
|
+
if (!team) return { error: `Team not found: ${name}`, available: Object.keys(registry.teams || {}) };
|
|
478
|
+
return { id: name, ...team };
|
|
462
479
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* adapter when configured; local JSONL remains available by default.
|
|
7
7
|
*/
|
|
8
8
|
import { join, resolve } from 'node:path';
|
|
9
|
+
import { loadRegistry } from '../../registry/loader.mjs';
|
|
9
10
|
import { homedir } from 'node:os';
|
|
10
11
|
import { readFileSync, existsSync } from 'node:fs';
|
|
11
12
|
import { createTelemetryClient } from '../../telemetry/client.mjs';
|
|
@@ -71,7 +72,7 @@ function resolveSessionContext() {
|
|
|
71
72
|
|
|
72
73
|
export async function cxTrace(args, { ROOT_DIR }) {
|
|
73
74
|
const ctx = resolveSessionContext();
|
|
74
|
-
const registry =
|
|
75
|
+
const registry = loadRegistry({ rootDir: ROOT_DIR });
|
|
75
76
|
const registryModels = registry.models ?? {};
|
|
76
77
|
const currentModels = readCurrentModels(join(ROOT_DIR, '.env'), registryModels, process.env);
|
|
77
78
|
const route = typeof args.input === 'string' ? routeRequest({ request: args.input }) : null;
|
|
@@ -97,7 +97,7 @@ export function workflowValidate(args) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
|
-
* Validate a producer→consumer handoff against specialists/
|
|
100
|
+
* Validate a producer→consumer handoff against specialists/org.
|
|
101
101
|
*
|
|
102
102
|
* Args:
|
|
103
103
|
* producer — agent or persona name producing the artifact
|
|
@@ -14,16 +14,16 @@ export function getOpenCodeMcpId(id) {
|
|
|
14
14
|
return id;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
function resolveTemplateString(value, resolvedValues) {
|
|
18
|
-
return value.replace(/__([A-Z0-9_]+)__/g, (_, name) => resolvedValues[name] ??
|
|
17
|
+
function resolveTemplateString(value, resolvedValues, fallback = (name) => `__${name}__`) {
|
|
18
|
+
return value.replace(/__([A-Z0-9_]+)__/g, (_, name) => resolvedValues[name] ?? fallback(name));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function resolveTemplateObject(input, resolvedValues) {
|
|
21
|
+
function resolveTemplateObject(input, resolvedValues, fallback) {
|
|
22
22
|
if (!input || typeof input !== "object" || Array.isArray(input)) return {};
|
|
23
23
|
return Object.fromEntries(
|
|
24
24
|
Object.entries(input).map(([key, value]) => [
|
|
25
25
|
key,
|
|
26
|
-
typeof value === "string" ? resolveTemplateString(value, resolvedValues) : value,
|
|
26
|
+
typeof value === "string" ? resolveTemplateString(value, resolvedValues, fallback) : value,
|
|
27
27
|
]),
|
|
28
28
|
);
|
|
29
29
|
}
|
|
@@ -45,7 +45,9 @@ function buildLocalEnvironment(mcpDef, resolvedValues) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function buildRemoteHeaders(mcpDef, resolvedValues) {
|
|
48
|
-
return stripUnresolvedValues(
|
|
48
|
+
return stripUnresolvedValues(
|
|
49
|
+
resolveTemplateObject(mcpDef.headers ?? {}, resolvedValues, (name) => `{env:${name}}`),
|
|
50
|
+
);
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
function withMemoryDefaults(id, values) {
|