@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
|
@@ -19,6 +19,7 @@ import { join } from 'node:path';
|
|
|
19
19
|
import { homedir } from 'node:os';
|
|
20
20
|
import { spawnSync } from 'node:child_process';
|
|
21
21
|
import { configDir } from '../config/xdg.mjs';
|
|
22
|
+
import { resolveOpRef } from '../providers/secret-resolver.mjs';
|
|
22
23
|
|
|
23
24
|
// ── Multi-method auth helpers ────────────────────────────────────────────
|
|
24
25
|
|
|
@@ -628,8 +629,7 @@ function resolveCredentialSync(varName, homeDir) {
|
|
|
628
629
|
const opMatch = content.match(opRe);
|
|
629
630
|
if (opMatch) {
|
|
630
631
|
try {
|
|
631
|
-
|
|
632
|
-
if (result.status === 0) return result.stdout.trim();
|
|
632
|
+
return resolveOpRef(opMatch[1]);
|
|
633
633
|
} catch { /* op read failed */ }
|
|
634
634
|
}
|
|
635
635
|
}
|
|
@@ -730,16 +730,15 @@ function resolveCredential(varName, homeDir) {
|
|
|
730
730
|
const opRe = new RegExp(`export\\s+${varName}=["']?\\$\\(op read '([^']+)'\\)["']?`, 'm');
|
|
731
731
|
const opMatch = content.match(opRe);
|
|
732
732
|
if (opMatch) {
|
|
733
|
+
// op read failures often mean the rc-file field name doesn't match the
|
|
734
|
+
// actual 1Password item field; surface them under the debug flag.
|
|
733
735
|
try {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
const errMsg = (result.stderr || '').trim().slice(0, 200);
|
|
739
|
-
if (errMsg && process.env.CONSTRUCT_DEBUG_CREDENTIALS === '1') {
|
|
740
|
-
process.stderr.write(`[credentials] op read failed for ${varName}: ${errMsg}\n`);
|
|
736
|
+
return resolveOpRef(opMatch[1]);
|
|
737
|
+
} catch (err) {
|
|
738
|
+
if (process.env.CONSTRUCT_DEBUG_CREDENTIALS === '1') {
|
|
739
|
+
process.stderr.write(`[credentials] op read failed for ${varName}: ${String(err.message).slice(0, 200)}\n`);
|
|
741
740
|
}
|
|
742
|
-
}
|
|
741
|
+
}
|
|
743
742
|
}
|
|
744
743
|
|
|
745
744
|
// If the value uses command substitution ($(...)), try evaluating via
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
19
19
|
import { dirname, join } from 'node:path';
|
|
20
20
|
import { syncFileStateToSql } from '../storage/sync.mjs';
|
|
21
|
-
import {
|
|
21
|
+
import { resolveActiveScope } from '../scopes/loader.mjs';
|
|
22
22
|
import { ensureCxDir } from '../project-init-shared.mjs';
|
|
23
23
|
|
|
24
24
|
const ROOT = '.cx/knowledge/external/research';
|
|
@@ -64,7 +64,7 @@ export async function addResearchFinding({
|
|
|
64
64
|
throw new Error('addResearchFinding: confidence=confirmed requires at least one source');
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const profile =
|
|
67
|
+
const profile = resolveActiveScope(cwd);
|
|
68
68
|
const now = new Date();
|
|
69
69
|
const expiresAt = new Date(now.getTime() + ttlDays * 24 * 60 * 60 * 1000).toISOString();
|
|
70
70
|
|
package/lib/mcp/server.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* lib/mcp/server.mjs — Construct MCP server: tool registry and request dispatcher.
|
|
4
4
|
*
|
|
5
5
|
* Thin dispatcher only — all tool implementations live in lib/mcp/tools/*.mjs.
|
|
6
|
-
* Registers 52 tools across 8 modules: project, document, storage, skills, workflow, telemetry, memory,
|
|
6
|
+
* Registers 52 tools across 8 modules: project, document, storage, skills, workflow, telemetry, memory, scope.
|
|
7
7
|
* Consumed by Claude Code, OpenCode, and any MCP-compatible host.
|
|
8
8
|
*/
|
|
9
9
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
} from './tools/storage.mjs';
|
|
39
39
|
import {
|
|
40
40
|
listSkills, getSkill, searchSkills, getTemplate, listTemplates,
|
|
41
|
-
agentContract, brokerCheck, orchestrationPolicy, workerRun, listTeams, getTeam,
|
|
41
|
+
agentContract, brokerCheck, orchestrationPolicy, workerRun, listTeams, getTeam, suggestSkillsTool,
|
|
42
42
|
} from './tools/skills.mjs';
|
|
43
43
|
import {
|
|
44
44
|
workflowInit, workflowAddTask, workflowUpdateTask,
|
|
@@ -53,12 +53,15 @@ import {
|
|
|
53
53
|
sessionList, sessionLoad, sessionSearch, sessionSave, rovoSearch,
|
|
54
54
|
} from './tools/memory.mjs';
|
|
55
55
|
import {
|
|
56
|
-
|
|
56
|
+
scopeShow, scopeList, scopeDrafts, scopeHealthTool,
|
|
57
57
|
outcomesSummary, outcomesRecord, knowledgeAdd,
|
|
58
|
-
|
|
58
|
+
scopeCreate, scopeArchive, sandboxList, learningStatus,
|
|
59
59
|
knowledgeGraphAsk,
|
|
60
|
-
} from './tools/
|
|
60
|
+
} from './tools/scope.mjs';
|
|
61
61
|
import { modelResolve, triageRecommend, workflowInvoke, capabilityDescribe, executionResolve, artifactWorkflow } from './tools/embedded-contract.mjs';
|
|
62
|
+
import { authorArtifact } from './tools/artifact-author.mjs';
|
|
63
|
+
import { findTool } from './tools/find-tool.mjs';
|
|
64
|
+
import { recoverToolName, recordToolNameMiss, isGatewayName } from './tool-recovery.mjs';
|
|
62
65
|
|
|
63
66
|
const DEFAULT_ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
64
67
|
const ROOT_DIR = resolve(process.env.CX_TOOLKIT_DIR || DEFAULT_ROOT_DIR);
|
|
@@ -470,11 +473,11 @@ const ALL_TOOL_DEFS = [
|
|
|
470
473
|
},
|
|
471
474
|
{
|
|
472
475
|
name: 'broker_check',
|
|
473
|
-
description: 'Queries the MCP broker policy gate for a pending action without executing it. Agents call this BEFORE attempting a high-risk action so the response (allowed / approvalRequired / reason / source) can be surfaced to the user in the agent\'s voice rather than triggering a denial after the fact. In solo mode the broker is off by default — returns `brokerActive: false` with `allowed: true` so the call is cheap and agents don\'t waste tokens on an inactive gate. Always emits a `tool.called` trace event for audit-trail parity. Reads specialists/
|
|
476
|
+
description: 'Queries the MCP broker policy gate for a pending action without executing it. Agents call this BEFORE attempting a high-risk action so the response (allowed / approvalRequired / reason / source) can be surfaced to the user in the agent\'s voice rather than triggering a denial after the fact. In solo mode the broker is off by default — returns `brokerActive: false` with `allowed: true` so the call is cheap and agents don\'t waste tokens on an inactive gate. Always emits a `tool.called` trace event for audit-trail parity. Reads specialists/org fence rules in team / enterprise mode.',
|
|
474
477
|
inputSchema: {
|
|
475
478
|
type: 'object',
|
|
476
479
|
properties: {
|
|
477
|
-
role: { type: 'string', description: 'Persona name (e.g. `engineer`, `security`). Must match a key in specialists/
|
|
480
|
+
role: { type: 'string', description: 'Persona name (e.g. `engineer`, `security`). Must match a key in specialists/org for team / enterprise mode.' },
|
|
478
481
|
project: { type: 'string', description: 'Optional project scope for the decision.' },
|
|
479
482
|
tool: { type: 'string', description: 'The tool the agent wants to invoke (e.g. `github`, `fs`).' },
|
|
480
483
|
action: { type: 'string', description: 'The action on that tool (e.g. `create_pr`, `edit:lib/foo.mjs`).' },
|
|
@@ -486,7 +489,7 @@ const ALL_TOOL_DEFS = [
|
|
|
486
489
|
},
|
|
487
490
|
{
|
|
488
491
|
name: 'agent_contract',
|
|
489
|
-
description: 'Looks up explicit agent-to-agent service contracts (from specialists/
|
|
492
|
+
description: 'Looks up explicit agent-to-agent service contracts (from specialists/org). Specialists should call this at the start of a handoff to see the expected input shape, preconditions, and what postconditions they must satisfy. Use without args to get all contracts; pass producer/consumer to narrow; pass id for a specific contract.',
|
|
490
493
|
inputSchema: {
|
|
491
494
|
type: 'object',
|
|
492
495
|
properties: {
|
|
@@ -496,6 +499,18 @@ const ALL_TOOL_DEFS = [
|
|
|
496
499
|
},
|
|
497
500
|
},
|
|
498
501
|
},
|
|
502
|
+
{
|
|
503
|
+
name: 'find_tool',
|
|
504
|
+
description: 'Find Construct tools by intent when you do not know the exact name. Describe what you want to do; returns the best-matching tools with their input schemas, ranked by hybrid semantic + lexical relevance. Then invoke a result via the `call` gateway (or directly if it is a flat tool).',
|
|
505
|
+
inputSchema: {
|
|
506
|
+
type: 'object',
|
|
507
|
+
properties: {
|
|
508
|
+
query: { type: 'string', description: 'Natural-language description of the task (e.g. "export a markdown file to pdf").' },
|
|
509
|
+
limit: { type: 'number', description: 'Max tools to return (default 5, max 20).' },
|
|
510
|
+
},
|
|
511
|
+
required: ['query'],
|
|
512
|
+
},
|
|
513
|
+
},
|
|
499
514
|
{
|
|
500
515
|
name: 'get_template',
|
|
501
516
|
description: 'Reads a doc template by name (e.g. "prd", "meta-prd", "prfaq", "evidence-brief", "adr", "runbook"). Resolves .cx/templates/docs/{name}.md first, then templates/docs/{name}.md.',
|
|
@@ -529,14 +544,16 @@ const ALL_TOOL_DEFS = [
|
|
|
529
544
|
inputSchema: { type: 'object', properties: {} },
|
|
530
545
|
},
|
|
531
546
|
{
|
|
532
|
-
name: '
|
|
533
|
-
description: '
|
|
547
|
+
name: 'suggest_skills',
|
|
548
|
+
description: 'Rank skills from the central catalog for a natural-language intent. Optional specialistId filters entitlement metadata.',
|
|
534
549
|
inputSchema: {
|
|
535
550
|
type: 'object',
|
|
536
551
|
properties: {
|
|
537
|
-
|
|
552
|
+
intent: { type: 'string', description: 'Task description or keywords' },
|
|
553
|
+
specialistId: { type: 'string', description: 'Optional cx-* id for entitlement hints' },
|
|
554
|
+
limit: { type: 'number', description: 'Max suggestions (default 5)' },
|
|
538
555
|
},
|
|
539
|
-
required: ['
|
|
556
|
+
required: ['intent'],
|
|
540
557
|
},
|
|
541
558
|
},
|
|
542
559
|
{
|
|
@@ -757,41 +774,43 @@ const ALL_TOOL_DEFS = [
|
|
|
757
774
|
properties: {
|
|
758
775
|
query: { type: 'string', description: 'The user\'s question or the project/repo name (e.g. "project iverson", "hashicorp/project-iverson", "PLAT"). The tool matches this against configured GITHUB_REPOS, JIRA_PROJECTS, LINEAR_TEAMS.' },
|
|
759
776
|
root_dir: { type: 'string', description: 'Data root dir override (default: homedir()). Use CX_DATA_DIR value if set.' },
|
|
777
|
+
team_id: { type: 'string', description: 'Optional team id (from the unified registry). Scopes the fetch to that team\'s declared sources; observations are tagged team:<id>.' },
|
|
778
|
+
target_ids: { type: 'array', items: { type: 'string' }, description: 'Optional source-target ids to restrict to (must belong to team_id). A target outside the team returns a typed OUT_OF_SCOPE error rather than a silent wrong-source fetch.' },
|
|
760
779
|
},
|
|
761
780
|
},
|
|
762
781
|
},
|
|
763
782
|
{
|
|
764
|
-
name: '
|
|
765
|
-
description: 'Return the active Construct
|
|
783
|
+
name: 'scope_show',
|
|
784
|
+
description: 'Return the active Construct scope (id, displayName, roles, departments, intake taxonomy, doc templates). Use when a specialist needs to know which role set, classification taxonomy, or doc templates apply before drafting work.',
|
|
766
785
|
inputSchema: {
|
|
767
786
|
type: 'object',
|
|
768
787
|
properties: {
|
|
769
788
|
cwd: { type: 'string', description: 'Project root (default: server cwd).' },
|
|
770
|
-
id: { type: 'string', description: 'Force a specific
|
|
789
|
+
id: { type: 'string', description: 'Force a specific scope id instead of resolving from config.' },
|
|
771
790
|
},
|
|
772
791
|
},
|
|
773
792
|
},
|
|
774
793
|
{
|
|
775
|
-
name: '
|
|
776
|
-
description: 'List the curated org
|
|
794
|
+
name: 'scope_list',
|
|
795
|
+
description: 'List the curated org scope catalog (rnd, operations, creative, research) with role/department counts. Use to discover which scopes are available before suggesting `construct scope set`.',
|
|
777
796
|
inputSchema: { type: 'object', properties: {} },
|
|
778
797
|
},
|
|
779
798
|
{
|
|
780
|
-
name: '
|
|
781
|
-
description: 'List in-progress draft
|
|
799
|
+
name: 'scope_drafts',
|
|
800
|
+
description: 'List in-progress draft scopes under `.cx/scopes/draft-*` and any user-defined custom scope at `.cx/scope.json`. Use to see what scope work is pending before scaffolding another draft.',
|
|
782
801
|
inputSchema: {
|
|
783
802
|
type: 'object',
|
|
784
803
|
properties: { cwd: { type: 'string', description: 'Project root (default: server cwd).' } },
|
|
785
804
|
},
|
|
786
805
|
},
|
|
787
806
|
{
|
|
788
|
-
name: '
|
|
789
|
-
description: 'Per-
|
|
807
|
+
name: 'scope_health',
|
|
808
|
+
description: 'Per-scope health rollup over a window: observation count, per-role outcome runs and success rates. Use to check whether a scope is producing data before recommending changes or archive.',
|
|
790
809
|
inputSchema: {
|
|
791
810
|
type: 'object',
|
|
792
811
|
properties: {
|
|
793
812
|
cwd: { type: 'string', description: 'Project root (default: server cwd).' },
|
|
794
|
-
id: { type: 'string', description: '
|
|
813
|
+
id: { type: 'string', description: 'Scope id (default: active scope).' },
|
|
795
814
|
window_days: { type: 'number', description: 'Window in days (default 30).' },
|
|
796
815
|
},
|
|
797
816
|
},
|
|
@@ -819,7 +838,7 @@ const ALL_TOOL_DEFS = [
|
|
|
819
838
|
role: { type: 'string', description: 'Specialist id (e.g. cx-engineer, product-manager).' },
|
|
820
839
|
success: { type: 'boolean' },
|
|
821
840
|
intake_id: { type: 'string' },
|
|
822
|
-
profile: { type: 'string', description: 'Override active
|
|
841
|
+
profile: { type: 'string', description: 'Override active scope id stamp.' },
|
|
823
842
|
escalated: { type: 'boolean' },
|
|
824
843
|
duration_ms: { type: 'number' },
|
|
825
844
|
notes: { type: 'string', description: 'Trimmed to 500 chars.' },
|
|
@@ -829,7 +848,7 @@ const ALL_TOOL_DEFS = [
|
|
|
829
848
|
},
|
|
830
849
|
{
|
|
831
850
|
name: 'knowledge_add',
|
|
832
|
-
description: 'Persist a research finding as `.cx/knowledge/external/research/<slug>.md` with research-specific frontmatter (topic, confidence, sources, expiresAt,
|
|
851
|
+
description: 'Persist a research finding as `.cx/knowledge/external/research/<slug>.md` with research-specific frontmatter (topic, confidence, sources, expiresAt, scope). Writes durable state — requires `confirm=true`. `confidence=confirmed` requires at least one source.',
|
|
833
852
|
inputSchema: {
|
|
834
853
|
type: 'object',
|
|
835
854
|
required: ['confirm', 'slug', 'topic', 'body'],
|
|
@@ -858,15 +877,15 @@ const ALL_TOOL_DEFS = [
|
|
|
858
877
|
},
|
|
859
878
|
},
|
|
860
879
|
{
|
|
861
|
-
name: '
|
|
862
|
-
description: 'Scaffold a draft org
|
|
880
|
+
name: 'scope_create',
|
|
881
|
+
description: 'Scaffold a draft org scope under `.cx/scopes/draft-<id>/` (requirements.md + scope.json + persona stubs + department charters). Writes durable state — requires `confirm=true`. For curated catalog work, follow `docs/guides/concepts/scope-lifecycle.md` after creation.',
|
|
863
882
|
inputSchema: {
|
|
864
883
|
type: 'object',
|
|
865
884
|
required: ['confirm', 'id'],
|
|
866
885
|
properties: {
|
|
867
886
|
confirm: { type: 'boolean', description: 'Must be true.' },
|
|
868
887
|
cwd: { type: 'string' },
|
|
869
|
-
id: { type: 'string', description: '
|
|
888
|
+
id: { type: 'string', description: 'Scope id (^[a-z][a-z0-9-]{1,30}$).' },
|
|
870
889
|
display_name: { type: 'string' },
|
|
871
890
|
seed_roles: {
|
|
872
891
|
type: 'array',
|
|
@@ -889,8 +908,8 @@ const ALL_TOOL_DEFS = [
|
|
|
889
908
|
},
|
|
890
909
|
},
|
|
891
910
|
{
|
|
892
|
-
name: '
|
|
893
|
-
description: 'Archive a curated
|
|
911
|
+
name: 'scope_archive',
|
|
912
|
+
description: 'Archive a curated scope: moves `specialists/org/scopes/<id>.json` and its intake table into `archive/scopes/<id>/` with an archive note. Destructive — requires `confirm=true` and a substantive `reason` (>=8 chars). Observations and outcomes are preserved.',
|
|
894
913
|
inputSchema: {
|
|
895
914
|
type: 'object',
|
|
896
915
|
required: ['confirm', 'id', 'reason'],
|
|
@@ -924,7 +943,7 @@ const ALL_TOOL_DEFS = [
|
|
|
924
943
|
},
|
|
925
944
|
{
|
|
926
945
|
name: 'learning_status',
|
|
927
|
-
description: 'One-shot mirror of `npm run learning:status`: active
|
|
946
|
+
description: 'One-shot mirror of `npm run learning:status`: active scope, observation counts (last 24h + total), research finding count, per-role outcome rollup. Use to answer "is Construct learning?" without spawning a shell.',
|
|
928
947
|
inputSchema: {
|
|
929
948
|
type: 'object',
|
|
930
949
|
properties: { cwd: { type: 'string', description: 'Project root (default: server cwd).' } },
|
|
@@ -947,14 +966,13 @@ const ALL_TOOL_DEFS = [
|
|
|
947
966
|
},
|
|
948
967
|
{
|
|
949
968
|
name: 'publish_detect',
|
|
950
|
-
description: 'Detect availability of publish pipeline tooling: Pandoc/Typst export, pandoc-ext/diagram figures, VHS terminal demos
|
|
969
|
+
description: 'Detect availability of publish pipeline tooling: Pandoc/Typst export, pandoc-ext/diagram figures, and VHS terminal demos.',
|
|
951
970
|
inputSchema: {
|
|
952
971
|
type: 'object',
|
|
953
972
|
properties: {
|
|
954
973
|
format: { type: 'string', enum: ['pdf', 'docx', 'doc', 'deck', 'pptx', 'html', 'rtf', 'odt', 'epub', 'tex', 'txt', 'md', 'mdx'], description: 'Export format to probe (default pdf).' },
|
|
955
974
|
figures: { type: 'boolean', description: 'Include figure binaries (default true).' },
|
|
956
975
|
demo: { type: 'string', description: 'When set, require VHS/asciinema for terminal demo.' },
|
|
957
|
-
dashboard_demo: { type: 'string', description: 'When set, require Playwright workspace for dashboard demo.' },
|
|
958
976
|
},
|
|
959
977
|
},
|
|
960
978
|
},
|
|
@@ -969,7 +987,6 @@ const ALL_TOOL_DEFS = [
|
|
|
969
987
|
output_path: { type: 'string', description: 'Optional output path for export.' },
|
|
970
988
|
format: { type: 'string', enum: ['pdf', 'docx', 'doc', 'deck', 'pptx', 'html', 'rtf', 'odt', 'epub', 'tex', 'txt', 'md', 'mdx'], description: 'Target format (default pdf).' },
|
|
971
989
|
demo: { type: 'string', description: 'Terminal tape name to record.' },
|
|
972
|
-
dashboard_demo: { type: 'string', description: 'Dashboard Playwright demo spec basename.' },
|
|
973
990
|
figures: { type: 'boolean', description: 'Render diagrams (default true).' },
|
|
974
991
|
strict: { type: 'boolean', description: 'Fail when toolchain or release gate fails (default true).' },
|
|
975
992
|
source_only: { type: 'boolean', description: 'Write sources only (default false).' },
|
|
@@ -1088,7 +1105,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1088
1105
|
},
|
|
1089
1106
|
{
|
|
1090
1107
|
name: 'workflow_contract_validate',
|
|
1091
|
-
description: 'Validate a producer→consumer handoff against specialists/
|
|
1108
|
+
description: 'Validate a producer→consumer handoff against specialists/org. Required when a specialist hands off to another role: enforces input.mustContain, output schema, disk-artifact postconditions, and binary postconditions per producer (rubber-stamp prevention, post-hoc threat-model prevention, etc.). Self-enforcing: a producer with binary rules MUST pass `packet`, or the call itself is a contract violation.',
|
|
1092
1109
|
inputSchema: {
|
|
1093
1110
|
type: 'object',
|
|
1094
1111
|
required: ['producer', 'consumer'],
|
|
@@ -1151,6 +1168,19 @@ const ALL_TOOL_DEFS = [
|
|
|
1151
1168
|
},
|
|
1152
1169
|
},
|
|
1153
1170
|
},
|
|
1171
|
+
{
|
|
1172
|
+
name: 'author_artifact',
|
|
1173
|
+
description: 'Materialize a typed Construct artifact you have drafted (prd, prd-platform, prd-business, meta-prd, adr, rfc, research-brief, evidence-brief, runbook) to disk and run the release gate. YOU draft the full markdown — start with a single # title and include the type\'s required ## sections (call get_template first for the shape) — and pass it as draft_markdown; the canonical file is written and the gate verdict + errors are returned so you can fix and re-call. This is the Construct author→materialize→validate pass for supported hosts.',
|
|
1174
|
+
inputSchema: {
|
|
1175
|
+
type: 'object',
|
|
1176
|
+
properties: {
|
|
1177
|
+
draft_markdown: { type: 'string', description: 'The complete artifact markdown you authored. Must start with one # title line and contain the required ## sections for the type.' },
|
|
1178
|
+
artifact_type: { type: 'string', description: 'Artifact type (prd, meta-prd, adr, rfc, research-brief, evidence-brief, runbook, …). Defaults to prd; inferred from subject when omitted.' },
|
|
1179
|
+
subject: { type: 'string', description: 'Short subject/title hint (e.g. "OIDC integration") used for the filename and type inference.' },
|
|
1180
|
+
},
|
|
1181
|
+
required: ['draft_markdown'],
|
|
1182
|
+
},
|
|
1183
|
+
},
|
|
1154
1184
|
{
|
|
1155
1185
|
name: 'model_resolve',
|
|
1156
1186
|
description: 'Resolve which model an embedded Construct workflow should use given the host/IDE provider context. Precedence: host model → same-provider-family fallback → Construct tier default → structured config error. Never reads or returns credential values (requiresCredential is a boolean) and never claims unverified provider health. Read-only; performs no writes.',
|
|
@@ -1235,7 +1265,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1235
1265
|
},
|
|
1236
1266
|
{
|
|
1237
1267
|
name: 'orchestration_run',
|
|
1238
|
-
description: 'EXECUTE a real multi-specialist orchestration run
|
|
1268
|
+
description: 'EXECUTE a real multi-specialist orchestration run and return per-specialist output — the executing counterpart to workflow_invoke (which only plans). For MCP hosts with no subagent primitive (VS Code/Copilot, Cursor), this is how you actually run a specialist chain: the engine owns orchestration, this tool is the thin client (ADR-0022). Solo runs execute in-process — no daemon, no port, no token; a remote/team orchestration service is opt-in via CONSTRUCT_ORCHESTRATION_URL. Real specialist output requires the `provider` worker backend (a provider key configured); the default `inline` backend prepares tasks only.',
|
|
1239
1269
|
inputSchema: {
|
|
1240
1270
|
type: 'object',
|
|
1241
1271
|
required: ['request'],
|
|
@@ -1273,17 +1303,14 @@ const ALL_TOOL_DEFS = [
|
|
|
1273
1303
|
// host/model; the long tail is reachable, just not front-loaded.
|
|
1274
1304
|
|
|
1275
1305
|
const CORE_TOOL_NAMES = new Set([
|
|
1276
|
-
'orchestration_policy', 'get_skill', 'search_skills', 'knowledge_search',
|
|
1277
|
-
'memory_search', 'project_context', 'summarize_diff',
|
|
1306
|
+
'orchestration_policy', 'get_skill', 'get_template', 'search_skills', 'knowledge_search',
|
|
1307
|
+
'memory_search', 'project_context', 'summarize_diff', 'find_tool',
|
|
1308
|
+
'author_artifact', 'document_export', 'publish_run', 'artifact_workflow',
|
|
1309
|
+
'workflow_invoke', 'triage_recommend',
|
|
1278
1310
|
]);
|
|
1279
1311
|
|
|
1280
|
-
function firstSentence(text) {
|
|
1281
|
-
const s = String(text || '').replace(/\s+/g, ' ').trim();
|
|
1282
|
-
const end = s.search(/\.(\s|$)/);
|
|
1283
|
-
return (end > 0 ? s.slice(0, end) : s).slice(0, 140);
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
1312
|
const LONG_TAIL_DEFS = ALL_TOOL_DEFS.filter((t) => !CORE_TOOL_NAMES.has(t.name));
|
|
1313
|
+
const KNOWN_TOOL_NAMES = new Set(ALL_TOOL_DEFS.map((t) => t.name));
|
|
1287
1314
|
|
|
1288
1315
|
// One dispatcher for the long tail. `tool` is constrained to an enum of valid
|
|
1289
1316
|
// names (≈1 token each — kills hallucinated names, the key small-model lever),
|
|
@@ -1291,11 +1318,12 @@ const LONG_TAIL_DEFS = ALL_TOOL_DEFS.filter((t) => !CORE_TOOL_NAMES.has(t.name))
|
|
|
1291
1318
|
// schemas. Dispatch reuses the same handlers via dispatchToolByName.
|
|
1292
1319
|
|
|
1293
1320
|
const CONSTRUCT_CALL_TOOL = {
|
|
1294
|
-
name: '
|
|
1321
|
+
name: 'call',
|
|
1295
1322
|
description:
|
|
1296
|
-
'Invoke any non-core Construct tool by name
|
|
1297
|
-
+ '
|
|
1298
|
-
+ LONG_TAIL_DEFS.map((t) =>
|
|
1323
|
+
'Invoke any non-core Construct tool by name: provide `tool` and `args`. '
|
|
1324
|
+
+ 'If you do not know the exact name, call find_tool with a description of your task to get ranked tools and their schemas. '
|
|
1325
|
+
+ `Long-tail tool groups: ${[...new Set(LONG_TAIL_DEFS.map((t) => t.name.split('_')[0]))].sort().map((g) => `${g}_*`).join(', ')}. `
|
|
1326
|
+
+ 'The `tool` value must be a catalog name (constrained by the enum below).',
|
|
1299
1327
|
inputSchema: {
|
|
1300
1328
|
type: 'object',
|
|
1301
1329
|
properties: {
|
|
@@ -1349,7 +1377,7 @@ export async function dispatchToolByName(name, args = {}) {
|
|
|
1349
1377
|
else if (name === 'workflow_contract_validate') result = await workflowContractValidate(args);
|
|
1350
1378
|
else if (name === 'workflow_import_plan') result = workflowImportPlan(args);
|
|
1351
1379
|
else if (name === 'list_teams') result = listTeams(opts);
|
|
1352
|
-
else if (name === '
|
|
1380
|
+
else if (name === 'suggest_skills') result = suggestSkillsTool(args, opts);
|
|
1353
1381
|
else if (name === 'cx_trace_telemetry') { const m = await import('./tools/telemetry.mjs'); result = await m.cxTrace(args, opts); }
|
|
1354
1382
|
else if (name === 'cx_trace') result = await cxTrace(args, opts);
|
|
1355
1383
|
else if (name === 'cx_score') result = await cxScore(args);
|
|
@@ -1367,7 +1395,7 @@ export async function dispatchToolByName(name, args = {}) {
|
|
|
1367
1395
|
else if (name === 'session_usage') result = await sessionUsage(args, opts);
|
|
1368
1396
|
else if (name === 'provider_fetch') {
|
|
1369
1397
|
const { demandFetch } = await import('../embed/demand-fetch.mjs');
|
|
1370
|
-
result = await demandFetch({ query: args.query, rootDir: args.root_dir });
|
|
1398
|
+
result = await demandFetch({ query: args.query, rootDir: args.root_dir, teamId: args.team_id, targetIds: args.target_ids });
|
|
1371
1399
|
}
|
|
1372
1400
|
else if (name === 'knowledge_search') {
|
|
1373
1401
|
const { knowledgeSearch } = await import('../knowledge/search.mjs');
|
|
@@ -1392,7 +1420,6 @@ export async function dispatchToolByName(name, args = {}) {
|
|
|
1392
1420
|
format: args.format || 'pdf',
|
|
1393
1421
|
includeFigures: args.figures !== false,
|
|
1394
1422
|
includeTerminalDemo: Boolean(args.demo),
|
|
1395
|
-
includeDashboardDemo: Boolean(args.dashboard_demo),
|
|
1396
1423
|
});
|
|
1397
1424
|
}
|
|
1398
1425
|
else if (name === 'publish_run') {
|
|
@@ -1403,7 +1430,6 @@ export async function dispatchToolByName(name, args = {}) {
|
|
|
1403
1430
|
format: args.format || 'pdf',
|
|
1404
1431
|
includeFigures: args.figures !== false,
|
|
1405
1432
|
includeTerminalDemo: Boolean(args.demo),
|
|
1406
|
-
includeDashboardDemo: Boolean(args.dashboard_demo),
|
|
1407
1433
|
});
|
|
1408
1434
|
} else {
|
|
1409
1435
|
result = runPublish({
|
|
@@ -1411,7 +1437,6 @@ export async function dispatchToolByName(name, args = {}) {
|
|
|
1411
1437
|
format: args.format || 'pdf',
|
|
1412
1438
|
outputPath: args.output_path,
|
|
1413
1439
|
demos: args.demo ? [args.demo] : [],
|
|
1414
|
-
dashboardDemos: args.dashboard_demo ? [args.dashboard_demo] : [],
|
|
1415
1440
|
figures: args.figures !== false,
|
|
1416
1441
|
strict: args.strict !== false,
|
|
1417
1442
|
gate: args.no_gate !== true,
|
|
@@ -1421,15 +1446,15 @@ export async function dispatchToolByName(name, args = {}) {
|
|
|
1421
1446
|
});
|
|
1422
1447
|
}
|
|
1423
1448
|
}
|
|
1424
|
-
else if (name === '
|
|
1425
|
-
else if (name === '
|
|
1426
|
-
else if (name === '
|
|
1427
|
-
else if (name === '
|
|
1449
|
+
else if (name === 'scope_show') result = scopeShow(args);
|
|
1450
|
+
else if (name === 'scope_list') result = scopeList();
|
|
1451
|
+
else if (name === 'scope_drafts') result = scopeDrafts(args);
|
|
1452
|
+
else if (name === 'scope_health') result = scopeHealthTool(args);
|
|
1428
1453
|
else if (name === 'outcomes_summary') result = outcomesSummary(args);
|
|
1429
1454
|
else if (name === 'outcomes_record') result = outcomesRecord(args);
|
|
1430
1455
|
else if (name === 'knowledge_add') result = await knowledgeAdd(args);
|
|
1431
|
-
else if (name === '
|
|
1432
|
-
else if (name === '
|
|
1456
|
+
else if (name === 'scope_create') result = scopeCreate(args);
|
|
1457
|
+
else if (name === 'scope_archive') result = scopeArchive(args);
|
|
1433
1458
|
else if (name === 'sandbox_list') result = sandboxList();
|
|
1434
1459
|
else if (name === 'learning_status') result = learningStatus(args);
|
|
1435
1460
|
else if (name === 'knowledge_graph_ask') result = knowledgeGraphAsk(args);
|
|
@@ -1439,17 +1464,35 @@ export async function dispatchToolByName(name, args = {}) {
|
|
|
1439
1464
|
else if (name === 'capability_describe') result = capabilityDescribe(args);
|
|
1440
1465
|
else if (name === 'construct_execution_resolve') result = executionResolve(args);
|
|
1441
1466
|
else if (name === 'artifact_workflow') result = artifactWorkflow(args);
|
|
1467
|
+
else if (name === 'author_artifact') result = await authorArtifact(args, opts);
|
|
1468
|
+
else if (name === 'find_tool') result = await findTool(args, { toolDefs: ALL_TOOL_DEFS, env: process.env });
|
|
1442
1469
|
else if (name === 'orchestration_run') { const m = await import('./tools/orchestration-run.mjs'); result = await m.orchestrationRun(args); }
|
|
1443
1470
|
else if (name === 'orchestration_status') { const m = await import('./tools/orchestration-run.mjs'); result = await m.orchestrationStatus(args); }
|
|
1444
|
-
else if (name === '
|
|
1471
|
+
else if (name === 'call') {
|
|
1445
1472
|
const inner = String(args?.tool || '');
|
|
1446
|
-
if (!inner || inner
|
|
1473
|
+
if (!inner || isGatewayName(inner)) result = { error: "call requires a 'tool' name to invoke (e.g. { tool: 'document_export', args: {…} })" };
|
|
1447
1474
|
else result = await dispatchToolByName(inner, args?.args || {});
|
|
1448
1475
|
}
|
|
1449
|
-
else result =
|
|
1476
|
+
else result = await resolveUnknownToolName(name, args);
|
|
1450
1477
|
return result;
|
|
1451
1478
|
}
|
|
1452
1479
|
|
|
1480
|
+
// The agent reached for a name no branch matches. Recover the obvious cases (the
|
|
1481
|
+
// gateway under an alias, a known tool wearing the host prefix) and record every
|
|
1482
|
+
// miss so the discoverability gap is measurable instead of silent.
|
|
1483
|
+
|
|
1484
|
+
async function resolveUnknownToolName(name, args = {}) {
|
|
1485
|
+
const recovered = recoverToolName(name, KNOWN_TOOL_NAMES);
|
|
1486
|
+
recordToolNameMiss(ROOT_DIR, { name, recovered: recovered?.name || (recovered?.gateway ? 'call' : null) });
|
|
1487
|
+
if (recovered?.gateway) {
|
|
1488
|
+
const inner = String(args?.tool || '');
|
|
1489
|
+
if (!inner || isGatewayName(inner)) return { error: "call requires a 'tool' name to invoke (e.g. { tool: 'document_export', args: {…} })" };
|
|
1490
|
+
return dispatchToolByName(inner, args?.args || {});
|
|
1491
|
+
}
|
|
1492
|
+
if (recovered?.name) return dispatchToolByName(recovered.name, args);
|
|
1493
|
+
return { error: `Unknown tool: ${name}. Core tools are flat; invoke any other tool via { name: 'call', arguments: { tool: '<name>', args: {…} } }.` };
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1453
1496
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1454
1497
|
const { name, arguments: args = {} } = request.params;
|
|
1455
1498
|
|
package/lib/mcp/tool-budget.mjs
CHANGED
|
@@ -8,9 +8,7 @@
|
|
|
8
8
|
* 2. which external MCP servers are `enabled` in opencode.json — sync disables
|
|
9
9
|
* the heavy ones for local-capable setups (they cannot be trimmed at runtime).
|
|
10
10
|
* Holds the shared token-sizing helper and the external-server id list both
|
|
11
|
-
* levers reason about
|
|
12
|
-
* Vercel AI SDK tool set to a compiled execution policy's allowed groups and
|
|
13
|
-
* schema cap (lib/models/execution-policy.mjs).
|
|
11
|
+
* levers reason about.
|
|
14
12
|
*/
|
|
15
13
|
|
|
16
14
|
import { getMeasuredHeavyExternalMcpTokenCost } from './external-schema-cost.mjs';
|
|
@@ -59,46 +57,3 @@ export function estimateToolTokens(tools) {
|
|
|
59
57
|
}
|
|
60
58
|
|
|
61
59
|
export { getMeasuredHeavyExternalMcpTokenCost };
|
|
62
|
-
|
|
63
|
-
// Maps the owned loop's agent tool names (apps/chat/engine/tools/registry.mjs)
|
|
64
|
-
// onto the execution-policy tool groups (lib/models/execution-policy.mjs) so a
|
|
65
|
-
// compiled policy can trim the serialized tool surface for a capability-
|
|
66
|
-
// constrained model. construct_tool is the single MCP gateway, so it stands in
|
|
67
|
-
// for the whole 'construct' group. An unrecognized name falls back to
|
|
68
|
-
// 'construct' — a group present in every tier — so an unclassified tool is never
|
|
69
|
-
// silently dropped from a constrained surface.
|
|
70
|
-
|
|
71
|
-
const AGENT_TOOL_GROUPS = Object.freeze({
|
|
72
|
-
read: 'read',
|
|
73
|
-
glob: 'search',
|
|
74
|
-
grep: 'search',
|
|
75
|
-
write: 'edit',
|
|
76
|
-
edit: 'edit',
|
|
77
|
-
shell: 'shell',
|
|
78
|
-
construct_tool: 'construct',
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
export function toolGroupForName(name) {
|
|
82
|
-
return AGENT_TOOL_GROUPS[name] || 'construct';
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Trim a tool set to the compiled policy's envelope: drop any tool whose group is
|
|
86
|
-
// outside allowedToolGroups, then cap the survivors at maxToolSchemas in stable
|
|
87
|
-
// insertion order. A null allowedToolGroups means "no group filter" and an
|
|
88
|
-
// unbounded cap means "no count limit", so the rich/hosted-direct envelope (all
|
|
89
|
-
// groups, cap 32) returns the input set unchanged.
|
|
90
|
-
|
|
91
|
-
export function applyToolBudget(tools, { allowedToolGroups = null, maxToolSchemas = Infinity } = {}) {
|
|
92
|
-
if (!tools || typeof tools !== 'object') return tools;
|
|
93
|
-
const allow = Array.isArray(allowedToolGroups) ? new Set(allowedToolGroups) : null;
|
|
94
|
-
const cap = Number.isFinite(maxToolSchemas) ? Math.max(0, Math.floor(maxToolSchemas)) : Infinity;
|
|
95
|
-
const out = {};
|
|
96
|
-
let kept = 0;
|
|
97
|
-
for (const [name, def] of Object.entries(tools)) {
|
|
98
|
-
if (allow && !allow.has(toolGroupForName(name))) continue;
|
|
99
|
-
if (kept >= cap) break;
|
|
100
|
-
out[name] = def;
|
|
101
|
-
kept += 1;
|
|
102
|
-
}
|
|
103
|
-
return out;
|
|
104
|
-
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/mcp/tool-recovery.mjs — Tolerant dispatch fallback + tool-name-miss telemetry.
|
|
3
|
+
*
|
|
4
|
+
* Hosts and models routinely mis-name Construct tools: the `call` gateway under
|
|
5
|
+
* its former `construct_call` name, a long-tail tool invoked directly, or a name
|
|
6
|
+
* carrying a stray `construct-mcp_` host prefix. Recovering the intent when it is
|
|
7
|
+
* unambiguous keeps work flowing; recording every miss closes a discoverability
|
|
8
|
+
* blind spot — calls rejected at the host's tool-validation layer never reach the
|
|
9
|
+
* server, so without this record those failures stay invisible to Construct.
|
|
10
|
+
*/
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
|
|
14
|
+
const GATEWAY_NAMES = new Set(['call', 'construct_call']);
|
|
15
|
+
|
|
16
|
+
function stripHostPrefix(name) {
|
|
17
|
+
return String(name || '').replace(/^construct[-_]mcp[-_]/i, '');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isGatewayName(name) {
|
|
21
|
+
return GATEWAY_NAMES.has(name) || GATEWAY_NAMES.has(stripHostPrefix(name));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Resolve an unrecognized name to a real target when the intent is unambiguous:
|
|
25
|
+
// a gateway alias, or a known tool wearing the host prefix. Anything else is a
|
|
26
|
+
// genuine miss with no safe recovery.
|
|
27
|
+
|
|
28
|
+
export function recoverToolName(name, knownNames) {
|
|
29
|
+
if (isGatewayName(name)) return { gateway: true };
|
|
30
|
+
const stripped = stripHostPrefix(name);
|
|
31
|
+
if (stripped !== name && knownNames.has(stripped)) return { name: stripped };
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function recordToolNameMiss(rootDir, { name, recovered = null } = {}) {
|
|
36
|
+
try {
|
|
37
|
+
const dir = path.join(rootDir, '.cx', 'observations');
|
|
38
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
39
|
+
const line = `${JSON.stringify({ at: new Date().toISOString(), kind: 'tool-name-miss', name, recovered })}\n`;
|
|
40
|
+
fs.appendFileSync(path.join(dir, 'tool-name-misses.jsonl'), line);
|
|
41
|
+
} catch {
|
|
42
|
+
/* telemetry is best-effort; never break dispatch */
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/mcp/tools/artifact-author.mjs — MCP `author_artifact` tool.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode-reachable author pass: the calling agent is the model and supplies
|
|
5
|
+
* the drafted typed-artifact markdown,
|
|
6
|
+
* which gets materialized to its canonical path and run through the release gate.
|
|
7
|
+
* Returns the path and a structured verdict so the agent can fix and re-call.
|
|
8
|
+
* allowScaffold stays off — a real draft is required, never an empty scaffold.
|
|
9
|
+
*/
|
|
10
|
+
import { runConstructArtifactLoop } from '../../artifact-loop-core.mjs';
|
|
11
|
+
|
|
12
|
+
export async function authorArtifact(args = {}, { ROOT_DIR } = {}) {
|
|
13
|
+
const draftMarkdown = args.draft_markdown || args.draft || '';
|
|
14
|
+
const cwd = args.cwd || process.cwd();
|
|
15
|
+
const result = await runConstructArtifactLoop({
|
|
16
|
+
draftMarkdown,
|
|
17
|
+
artifactType: args.artifact_type,
|
|
18
|
+
text: args.subject || args.text || '',
|
|
19
|
+
cwd,
|
|
20
|
+
rootDir: ROOT_DIR,
|
|
21
|
+
explicit: true,
|
|
22
|
+
allowScaffold: false,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
ok: Boolean(result.ok),
|
|
27
|
+
artifact_type: result.artifactType,
|
|
28
|
+
path: result.relPath,
|
|
29
|
+
written: !result.draftMissing,
|
|
30
|
+
gate: result.validation?.ok ? 'PASS' : 'FAIL',
|
|
31
|
+
errors: result.validation?.errors || [],
|
|
32
|
+
warnings: result.validation?.warnings || [],
|
|
33
|
+
workflow_plan: (result.invokePlan?.selectedRoles || []).map((r) => `cx-${r}`),
|
|
34
|
+
summary: result.summary,
|
|
35
|
+
};
|
|
36
|
+
}
|