@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
package/lib/publish.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Single compound run: artifact release gate (default), detect tooling, export
|
|
5
5
|
* markdown (pandoc-ext/diagram + Construct PDF template), optional VHS terminal
|
|
6
|
-
* demo, optional Playwright
|
|
6
|
+
* demo, optional Playwright recording. --strict requires toolchain and gate.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import fs from 'node:fs';
|
|
@@ -11,7 +11,6 @@ import path from 'node:path';
|
|
|
11
11
|
import { exportMarkdown, parsePublishFrontmatter, EXPORT_FORMATS } from './document-export.mjs';
|
|
12
12
|
import { detectPublishPipeline } from './publish-tooling.mjs';
|
|
13
13
|
import { runDemoRecord } from './demo.mjs';
|
|
14
|
-
import { recordDashboardDemo } from './dashboard-demo.mjs';
|
|
15
14
|
import { loadDemoRecording } from './demo-recording.mjs';
|
|
16
15
|
import { recordPlaywrightDemo } from './playwright-demo.mjs';
|
|
17
16
|
import { validateArtifactRelease } from './artifact-release-gate.mjs';
|
|
@@ -46,7 +45,6 @@ export function runPublish({
|
|
|
46
45
|
format = 'pdf',
|
|
47
46
|
outputPath,
|
|
48
47
|
demos = [],
|
|
49
|
-
dashboardDemos = [],
|
|
50
48
|
recordings = [],
|
|
51
49
|
figures = true,
|
|
52
50
|
strict = true,
|
|
@@ -62,27 +60,26 @@ export function runPublish({
|
|
|
62
60
|
return { ok: false, message: `Input not found: ${resolvedIn}` };
|
|
63
61
|
}
|
|
64
62
|
|
|
63
|
+
const explicitDemos = demos.length > 0;
|
|
64
|
+
const explicitRecordings = recordings.length > 0;
|
|
65
65
|
let demoNames = demos;
|
|
66
|
-
let dashNames = dashboardDemos;
|
|
67
66
|
let recordingNames = recordings;
|
|
68
|
-
if (!demoNames.length && !
|
|
67
|
+
if (!demoNames.length && !recordingNames.length) {
|
|
69
68
|
const fm = parsePublishFrontmatter(resolvedIn);
|
|
70
69
|
if (fm?.demo) demoNames = [fm.demo];
|
|
71
|
-
if (fm?.dashboardDemo) dashNames = [fm.dashboardDemo];
|
|
72
70
|
if (fm?.recording) recordingNames = [fm.recording];
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
const detection = detectPublishPipeline({
|
|
76
74
|
format,
|
|
77
75
|
includeFigures: figures,
|
|
78
|
-
includeTerminalDemo: demoNames.length > 0 && !sourceOnly,
|
|
79
|
-
includeDashboardDemo: (dashNames.length > 0 || recordingNames.length > 0) && !sourceOnly,
|
|
76
|
+
includeTerminalDemo: explicitDemos && demoNames.length > 0 && !sourceOnly,
|
|
80
77
|
cwd,
|
|
81
78
|
repoRoot,
|
|
82
79
|
env,
|
|
83
80
|
});
|
|
84
81
|
|
|
85
|
-
const ledger = { export: null, demos: [],
|
|
82
|
+
const ledger = { export: null, demos: [], recordings: [], detection, gate: null };
|
|
86
83
|
const resolvedType = artifactType || inferArtifactTypeFromPath(resolvedIn, { rootDir: cwd });
|
|
87
84
|
|
|
88
85
|
if (gate && !sourceOnly && resolvedType) {
|
|
@@ -131,40 +128,33 @@ export function runPublish({
|
|
|
131
128
|
}
|
|
132
129
|
|
|
133
130
|
for (const name of demoNames) {
|
|
131
|
+
const demoRequired = explicitDemos && strict && !sourceOnly;
|
|
134
132
|
const demoResult = runDemoRecord(name, {
|
|
135
133
|
cwd,
|
|
136
134
|
format: 'mp4',
|
|
137
|
-
sourceOnly: sourceOnly || (!
|
|
138
|
-
required:
|
|
135
|
+
sourceOnly: sourceOnly || (!demoRequired && !detection.steps?.terminalDemo?.present),
|
|
136
|
+
required: demoRequired,
|
|
139
137
|
surface: 'tape',
|
|
140
138
|
});
|
|
141
139
|
ledger.demos.push(demoResult);
|
|
142
|
-
if (
|
|
140
|
+
if (demoRequired && !demoResult.ok) {
|
|
143
141
|
return { ok: false, strict: true, ledger, message: demoResult.message };
|
|
144
142
|
}
|
|
145
143
|
}
|
|
146
144
|
|
|
147
|
-
for (const name of dashNames) {
|
|
148
|
-
const dashResult = recordDashboardDemo(name, { cwd, repoRoot, env });
|
|
149
|
-
ledger.dashboardDemos.push(dashResult);
|
|
150
|
-
if (strict && !sourceOnly && !dashResult.ok) {
|
|
151
|
-
return { ok: false, strict: true, ledger, message: dashResult.message };
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
145
|
for (const name of recordingNames) {
|
|
156
146
|
const recording = loadDemoRecording(name, { cwd, repoRoot: repoRoot || cwd });
|
|
157
147
|
if (!recording) {
|
|
158
148
|
const miss = { ok: false, name, message: `Recording not found: ${name}` };
|
|
159
149
|
ledger.recordings.push(miss);
|
|
160
|
-
if (strict && !sourceOnly) {
|
|
150
|
+
if (explicitRecordings && strict && !sourceOnly) {
|
|
161
151
|
return { ok: false, strict: true, ledger, message: miss.message };
|
|
162
152
|
}
|
|
163
153
|
continue;
|
|
164
154
|
}
|
|
165
155
|
const recResult = recordPlaywrightDemo(recording, { cwd, repoRoot: repoRoot || cwd, env });
|
|
166
156
|
ledger.recordings.push(recResult);
|
|
167
|
-
if (strict && !sourceOnly && !recResult.ok) {
|
|
157
|
+
if (explicitRecordings && strict && !sourceOnly && !recResult.ok) {
|
|
168
158
|
return { ok: false, strict: true, ledger, message: recResult.message };
|
|
169
159
|
}
|
|
170
160
|
}
|
|
@@ -191,7 +181,6 @@ Options:
|
|
|
191
181
|
--output=<path> Output path (default: .cx/publish/<name>.<format>)
|
|
192
182
|
--type=<doc-type> Artifact type for release gate (inferred from path when omitted)
|
|
193
183
|
--demo=<name> Terminal VHS tape name (repeatable)
|
|
194
|
-
--dashboard-demo=<name> Playwright demo spec basename (repeatable)
|
|
195
184
|
--recording=<name> Playwright recording manifest (repeatable)
|
|
196
185
|
--no-figures Skip pandoc-ext/diagram filter
|
|
197
186
|
--no-gate Skip artifact release gate (maintainer escape hatch)
|
|
@@ -203,7 +192,6 @@ Options:
|
|
|
203
192
|
Frontmatter (optional):
|
|
204
193
|
publish:
|
|
205
194
|
demo: resource-guard-rails
|
|
206
|
-
dashboardDemo: cockpit-tour
|
|
207
195
|
recording: agentic-platforms-prd
|
|
208
196
|
|
|
209
197
|
Examples:
|
|
@@ -218,7 +206,6 @@ export async function runPublishCli(argv = [], { cwd = process.cwd(), repoRoot }
|
|
|
218
206
|
output: null,
|
|
219
207
|
type: null,
|
|
220
208
|
demos: [],
|
|
221
|
-
dashboardDemos: [],
|
|
222
209
|
figures: true,
|
|
223
210
|
strict: true,
|
|
224
211
|
gate: true,
|
|
@@ -245,8 +232,6 @@ export async function runPublishCli(argv = [], { cwd = process.cwd(), repoRoot }
|
|
|
245
232
|
if (arg.startsWith('--type=')) { options.type = arg.slice(7); continue; }
|
|
246
233
|
if (arg === '--demo') { options.demos.push(argv[++i]); continue; }
|
|
247
234
|
if (arg.startsWith('--demo=')) { options.demos.push(arg.slice(7)); continue; }
|
|
248
|
-
if (arg === '--dashboard-demo') { options.dashboardDemos.push(argv[++i]); continue; }
|
|
249
|
-
if (arg.startsWith('--dashboard-demo=')) { options.dashboardDemos.push(arg.slice(17)); continue; }
|
|
250
235
|
if (!arg.startsWith('--')) options.input = arg;
|
|
251
236
|
}
|
|
252
237
|
|
|
@@ -260,7 +245,6 @@ export async function runPublishCli(argv = [], { cwd = process.cwd(), repoRoot }
|
|
|
260
245
|
format: options.format,
|
|
261
246
|
includeFigures: options.figures,
|
|
262
247
|
includeTerminalDemo: options.demos.length > 0,
|
|
263
|
-
includeDashboardDemo: options.dashboardDemos.length > 0,
|
|
264
248
|
cwd,
|
|
265
249
|
repoRoot,
|
|
266
250
|
});
|
|
@@ -278,7 +262,6 @@ export async function runPublishCli(argv = [], { cwd = process.cwd(), repoRoot }
|
|
|
278
262
|
format: options.format,
|
|
279
263
|
outputPath: options.output,
|
|
280
264
|
demos: options.demos,
|
|
281
|
-
dashboardDemos: options.dashboardDemos,
|
|
282
265
|
figures: options.figures,
|
|
283
266
|
strict: options.strict,
|
|
284
267
|
gate: options.gate,
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import fs from 'node:fs';
|
|
21
|
+
import { loadRegistry } from '../registry/loader.mjs';
|
|
21
22
|
import path from 'node:path';
|
|
22
23
|
import { fileURLToPath } from 'node:url';
|
|
23
24
|
|
|
@@ -34,17 +35,21 @@ function escapeRegExp(value) {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
function registryMcpServers() {
|
|
37
|
-
const
|
|
38
|
-
const bundled = fileURLToPath(new URL('../../specialists/registry.json', import.meta.url));
|
|
39
|
-
for (const src of [installed, bundled]) {
|
|
38
|
+
for (const rootDir of [constructDir(), fileURLToPath(new URL('../..', import.meta.url))]) {
|
|
40
39
|
try {
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
40
|
+
const registry = loadRegistry({ rootDir, skipValidation: true });
|
|
41
|
+
if (registry?.mcpServers && Object.keys(registry.mcpServers).length > 0) {
|
|
42
|
+
return registry.mcpServers;
|
|
43
|
+
}
|
|
44
|
+
} catch { /* try next root */ }
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const templatePath = path.join(constructDir(), 'platforms', 'claude', 'settings.template.json');
|
|
48
|
+
const template = JSON.parse(fs.readFileSync(templatePath, 'utf8'));
|
|
49
|
+
return template.mcpServers || {};
|
|
50
|
+
} catch {
|
|
51
|
+
return {};
|
|
46
52
|
}
|
|
47
|
-
return {};
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
function hasMcpTable(text, id) {
|
package/lib/reflect.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/reflect.mjs — construct reflect command implementation.
|
|
3
3
|
*
|
|
4
|
-
* Captures improvement feedback from
|
|
4
|
+
* Captures improvement feedback from host sessions and updates Construct core
|
|
5
5
|
* knowledge base with actionable insights for continuous self-improvement.
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -259,4 +259,4 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
259
259
|
console.error(`Error: ${err.message}`);
|
|
260
260
|
process.exit(1);
|
|
261
261
|
});
|
|
262
|
-
}
|
|
262
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/registry/agent-manifest.mjs — load, regenerate, and verify the agent execution manifest.
|
|
3
|
+
*
|
|
4
|
+
* The manifest (registry/agent-manifest.json) declares the MCP-first tool surface
|
|
5
|
+
* agents should reach for and the OpenCode-first human entry. The structural
|
|
6
|
+
* facts must stay honest against live sources rather than drifting into
|
|
7
|
+
* hand-maintained copy:
|
|
8
|
+
* - the CORE tool set, which is the CORE_TOOL_NAMES Set literal in
|
|
9
|
+
* lib/mcp/server.mjs (that constant is not exported, so it is parsed from the
|
|
10
|
+
* source text — the single authority for what the MCP ListTools front-loads);
|
|
11
|
+
* - the long-tail dispatcher name `construct_call`;
|
|
12
|
+
* - the OpenCode human conversation entry (the non-interactive CLI remains the admin/headless substrate);
|
|
13
|
+
* - the MCP gap list, which is exactly the capabilities declaring surfaces.mcp.supported=false
|
|
14
|
+
* in registry/capabilities.json, with the CLI fallback taken from surfaces.cli.command.
|
|
15
|
+
*
|
|
16
|
+
* generateAgentManifest reconciles those source-derived facts onto the curated prose
|
|
17
|
+
* (tool `use` strings, rationales, credential guidance, gap reasons) carried by the
|
|
18
|
+
* committed manifest and emits canonical JSON, so the human-owned copy round-trips while
|
|
19
|
+
* any drift in a live-source fact is caught by the --check path in registry:generate-docs.
|
|
20
|
+
* parseCoreToolNames reads the server source and returns the declared set so callers and
|
|
21
|
+
* tests verify the manifest against the server instead of re-stating the list.
|
|
22
|
+
* Read-only by default: generateAgentManifest writes only when asked.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
26
|
+
import { dirname, resolve } from 'node:path';
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
28
|
+
|
|
29
|
+
import { loadCapabilityRegistry } from './validate.mjs';
|
|
30
|
+
|
|
31
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
const REPO_ROOT = resolve(HERE, '..', '..');
|
|
33
|
+
|
|
34
|
+
export const MANIFEST_PATH = resolve(REPO_ROOT, 'registry', 'agent-manifest.json');
|
|
35
|
+
export const MCP_SERVER_PATH = resolve(REPO_ROOT, 'lib', 'mcp', 'server.mjs');
|
|
36
|
+
|
|
37
|
+
export const LONG_TAIL_DISPATCH_TOOL = 'call';
|
|
38
|
+
|
|
39
|
+
export function loadAgentManifest(path = MANIFEST_PATH) {
|
|
40
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// CORE_TOOL_NAMES is a `new Set([...])` literal of single-quoted tool names.
|
|
44
|
+
// Capture the array body between the brackets, then pull each quoted token so the
|
|
45
|
+
// parser tolerates reordering and reflow but fails loudly if the constant is gone.
|
|
46
|
+
|
|
47
|
+
export function parseCoreToolNames(source) {
|
|
48
|
+
const block = source.match(/const\s+CORE_TOOL_NAMES\s*=\s*new\s+Set\(\[([\s\S]*?)\]\)/);
|
|
49
|
+
if (!block) {
|
|
50
|
+
throw new Error('CORE_TOOL_NAMES Set literal not found in MCP server source');
|
|
51
|
+
}
|
|
52
|
+
const names = [...block[1].matchAll(/'([^']+)'/g)].map((m) => m[1]);
|
|
53
|
+
if (names.length === 0) {
|
|
54
|
+
throw new Error('CORE_TOOL_NAMES literal parsed to an empty set');
|
|
55
|
+
}
|
|
56
|
+
return names;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function readCoreToolNames(path = MCP_SERVER_PATH) {
|
|
60
|
+
return parseCoreToolNames(readFileSync(path, 'utf8'));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Structural gate shared by the test and any future generator: the manifest must
|
|
64
|
+
// front-load exactly the live CORE set, expose construct_call as the long-tail
|
|
65
|
+
// door, name OpenCode as the primary human conversation entry, and never mention
|
|
66
|
+
// the removed local-loop subcommand.
|
|
67
|
+
|
|
68
|
+
export function verifyAgentManifest(manifest, { coreToolNames } = {}) {
|
|
69
|
+
const errors = [];
|
|
70
|
+
const core = coreToolNames ?? readCoreToolNames();
|
|
71
|
+
|
|
72
|
+
const tools = manifest?.toolSurface;
|
|
73
|
+
if (!tools || typeof tools !== 'object') {
|
|
74
|
+
errors.push('toolSurface object missing');
|
|
75
|
+
} else {
|
|
76
|
+
const declaredCore = Array.isArray(tools.core) ? tools.core.map((t) => t.name) : null;
|
|
77
|
+
if (!declaredCore) {
|
|
78
|
+
errors.push('toolSurface.core must be an array of tool entries');
|
|
79
|
+
} else {
|
|
80
|
+
const missing = core.filter((n) => !declaredCore.includes(n));
|
|
81
|
+
const extra = declaredCore.filter((n) => !core.includes(n));
|
|
82
|
+
if (missing.length) errors.push(`toolSurface.core missing CORE_TOOL_NAMES: ${missing.join(', ')}`);
|
|
83
|
+
if (extra.length) errors.push(`toolSurface.core lists non-core tools: ${extra.join(', ')}`);
|
|
84
|
+
}
|
|
85
|
+
if (tools.longTail?.tool !== LONG_TAIL_DISPATCH_TOOL) {
|
|
86
|
+
errors.push(`toolSurface.longTail.tool must be ${LONG_TAIL_DISPATCH_TOOL}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const entry = manifest?.humanEntry;
|
|
91
|
+
if (!entry || entry.surface !== 'opencode' || entry.command !== 'opencode') {
|
|
92
|
+
errors.push('humanEntry must name OpenCode as the primary human conversation surface');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const removedLocalLoopCommand = 'construct' + ' ' + 'c' + 'hat';
|
|
96
|
+
if (JSON.stringify(manifest).includes(removedLocalLoopCommand)) {
|
|
97
|
+
errors.push('manifest must not reference the removed local-loop subcommand');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { valid: errors.length === 0, errors };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// The MCP gap list is not editorial: it is exactly the capabilities that declare
|
|
104
|
+
// surfaces.mcp.supported=false. Derive id/name/cliFallback from the registry so a
|
|
105
|
+
// capability flipping its MCP support shows up as drift; keep the human-written reason.
|
|
106
|
+
|
|
107
|
+
function deriveMcpGaps({ rootDir = REPO_ROOT, curatedGaps = [] } = {}) {
|
|
108
|
+
const { capabilities = [] } = loadCapabilityRegistry({ rootDir });
|
|
109
|
+
const reasonById = new Map(curatedGaps.map((g) => [g.id, g.reason]));
|
|
110
|
+
return capabilities
|
|
111
|
+
.filter((cap) => cap?.surfaces?.mcp?.supported === false)
|
|
112
|
+
.map((cap) => {
|
|
113
|
+
const reason = reasonById.get(cap.id);
|
|
114
|
+
if (!reason) {
|
|
115
|
+
throw new Error(`agent-manifest: MCP gap "${cap.id}" has no curated reason in registry/agent-manifest.json`);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
id: cap.id,
|
|
119
|
+
name: cap.name ?? cap.id,
|
|
120
|
+
mcp: 'unsupported',
|
|
121
|
+
cliFallback: cap.surfaces?.cli?.command ?? null,
|
|
122
|
+
reason,
|
|
123
|
+
};
|
|
124
|
+
})
|
|
125
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Reconcile source-of-truth structure onto the curated prose: pull the CORE set from
|
|
129
|
+
// the live server source (preserving each tool's curated `use` by name), pin the
|
|
130
|
+
// long-tail dispatcher and the OpenCode human entry, and derive the MCP gap list from the
|
|
131
|
+
// capability registry. The committed manifest supplies the prose; live sources supply
|
|
132
|
+
// the facts. Emit canonical JSON so a correct file round-trips byte-for-byte.
|
|
133
|
+
|
|
134
|
+
export function generateAgentManifest({ rootDir = REPO_ROOT, write = true } = {}) {
|
|
135
|
+
const manifestPath = resolve(rootDir, 'registry', 'agent-manifest.json');
|
|
136
|
+
const serverPath = resolve(rootDir, 'lib', 'mcp', 'server.mjs');
|
|
137
|
+
const current = loadAgentManifest(manifestPath);
|
|
138
|
+
const coreToolNames = readCoreToolNames(serverPath);
|
|
139
|
+
|
|
140
|
+
const useByName = new Map(
|
|
141
|
+
(current.toolSurface?.core ?? []).map((t) => [t.name, t.use]),
|
|
142
|
+
);
|
|
143
|
+
const core = coreToolNames.map((name) => {
|
|
144
|
+
const use = useByName.get(name);
|
|
145
|
+
if (!use) {
|
|
146
|
+
throw new Error(`agent-manifest: core tool "${name}" has no curated use string in registry/agent-manifest.json`);
|
|
147
|
+
}
|
|
148
|
+
return { name, use };
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const next = {
|
|
152
|
+
$schema: current.$schema,
|
|
153
|
+
version: current.version,
|
|
154
|
+
kind: current.kind,
|
|
155
|
+
description: current.description,
|
|
156
|
+
sources: current.sources,
|
|
157
|
+
toolSurface: {
|
|
158
|
+
...current.toolSurface,
|
|
159
|
+
core,
|
|
160
|
+
longTail: {
|
|
161
|
+
...current.toolSurface?.longTail,
|
|
162
|
+
tool: LONG_TAIL_DISPATCH_TOOL,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
humanEntry: {
|
|
166
|
+
...current.humanEntry,
|
|
167
|
+
surface: 'opencode',
|
|
168
|
+
command: 'opencode',
|
|
169
|
+
subcommand: null,
|
|
170
|
+
},
|
|
171
|
+
credentials: current.credentials,
|
|
172
|
+
mcpGaps: {
|
|
173
|
+
...current.mcpGaps,
|
|
174
|
+
gaps: deriveMcpGaps({ rootDir, curatedGaps: current.mcpGaps?.gaps ?? [] }),
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const content = `${JSON.stringify(next, null, 2)}\n`;
|
|
179
|
+
if (!write) {
|
|
180
|
+
const existing = readFileSync(manifestPath, 'utf8');
|
|
181
|
+
return { path: manifestPath, content, drift: existing !== content };
|
|
182
|
+
}
|
|
183
|
+
writeFileSync(manifestPath, content);
|
|
184
|
+
return { path: manifestPath, content, drift: false };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function checkAgentManifestDrift({ rootDir = REPO_ROOT } = {}) {
|
|
188
|
+
const { drift, content } = generateAgentManifest({ rootDir, write: false });
|
|
189
|
+
return { drift, contentLength: content.length };
|
|
190
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/registry/assemble.mjs — Runtime merge of modular org files into registry v3.
|
|
3
|
+
*
|
|
4
|
+
* Walks specialists/org/{groups,teams,specialists,contracts,policies}, merges
|
|
5
|
+
* .cx/org overlay fragments, and returns the assembled object for validation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
|
|
11
|
+
function readJsonDir(dir) {
|
|
12
|
+
const out = {};
|
|
13
|
+
if (!fs.existsSync(dir)) return out;
|
|
14
|
+
for (const name of fs.readdirSync(dir)) {
|
|
15
|
+
if (!name.endsWith('.json')) continue;
|
|
16
|
+
const id = name.replace(/\.json$/, '');
|
|
17
|
+
const raw = JSON.parse(fs.readFileSync(path.join(dir, name), 'utf8'));
|
|
18
|
+
const entityId = raw.id || id;
|
|
19
|
+
const { id: _drop, ...rest } = raw;
|
|
20
|
+
out[entityId] = { id: entityId, ...rest };
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function mergeOverlaySection(base, overlaySection) {
|
|
26
|
+
if (!overlaySection) return;
|
|
27
|
+
for (const [id, value] of Object.entries(overlaySection)) {
|
|
28
|
+
base[id] = { ...base[id], ...value };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function mergeOrgOverlay(orgRoot, overlayRoot) {
|
|
33
|
+
if (!fs.existsSync(overlayRoot)) return;
|
|
34
|
+
const sections = ['groups', 'teams', 'specialists', 'contracts', 'policies'];
|
|
35
|
+
for (const section of sections) {
|
|
36
|
+
const overlayDir = path.join(overlayRoot, section);
|
|
37
|
+
if (!fs.existsSync(overlayDir)) continue;
|
|
38
|
+
const baseDir = path.join(orgRoot, section === 'groups' ? 'groups' : section);
|
|
39
|
+
const base = readJsonDir(baseDir);
|
|
40
|
+
for (const name of fs.readdirSync(overlayDir)) {
|
|
41
|
+
if (!name.endsWith('.json')) continue;
|
|
42
|
+
const raw = JSON.parse(fs.readFileSync(path.join(overlayDir, name), 'utf8'));
|
|
43
|
+
const entityId = raw.id || name.replace(/\.json$/, '');
|
|
44
|
+
const { id: _drop, ...rest } = raw;
|
|
45
|
+
base[entityId] = { ...(base[entityId] || {}), id: entityId, ...rest };
|
|
46
|
+
}
|
|
47
|
+
for (const [id, value] of Object.entries(base)) {
|
|
48
|
+
writeMerged(baseDir, id, value);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function writeMerged() {
|
|
54
|
+
// overlay merge is in-memory only; no disk write
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function orgDirMtime(rootDir) {
|
|
58
|
+
const orgRoot = path.join(rootDir, 'specialists', 'org');
|
|
59
|
+
if (!fs.existsSync(orgRoot)) return 0;
|
|
60
|
+
let max = 0;
|
|
61
|
+
const walk = (dir) => {
|
|
62
|
+
for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
63
|
+
const p = path.join(dir, ent.name);
|
|
64
|
+
if (ent.isDirectory()) walk(p);
|
|
65
|
+
else if (ent.name.endsWith('.json')) max = Math.max(max, fs.statSync(p).mtimeMs);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
walk(orgRoot);
|
|
69
|
+
const overlay = path.join(rootDir, '.cx', 'org');
|
|
70
|
+
if (fs.existsSync(overlay)) walk(overlay);
|
|
71
|
+
return max;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function assembleRegistry(rootDir) {
|
|
75
|
+
const orgRoot = path.join(rootDir, 'specialists', 'org');
|
|
76
|
+
const overlayRoot = path.join(rootDir, '.cx', 'org');
|
|
77
|
+
|
|
78
|
+
if (!fs.existsSync(orgRoot)) {
|
|
79
|
+
throw new Error(`Modular org not found: ${orgRoot}. Run node scripts/migrate-org-modular.mjs`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const groups = readJsonDir(path.join(orgRoot, 'groups'));
|
|
83
|
+
const squads = readJsonDir(path.join(orgRoot, 'teams'));
|
|
84
|
+
const specialists = readJsonDir(path.join(orgRoot, 'specialists'));
|
|
85
|
+
const contracts = readJsonDir(path.join(orgRoot, 'contracts'));
|
|
86
|
+
const policies = readJsonDir(path.join(orgRoot, 'policies'));
|
|
87
|
+
|
|
88
|
+
if (fs.existsSync(overlayRoot)) {
|
|
89
|
+
for (const [id, val] of Object.entries(readJsonDir(path.join(overlayRoot, 'groups')))) {
|
|
90
|
+
groups[id] = { ...groups[id], ...val };
|
|
91
|
+
}
|
|
92
|
+
for (const [id, val] of Object.entries(readJsonDir(path.join(overlayRoot, 'teams')))) {
|
|
93
|
+
squads[id] = { ...squads[id], ...val };
|
|
94
|
+
}
|
|
95
|
+
for (const [id, val] of Object.entries(readJsonDir(path.join(overlayRoot, 'specialists')))) {
|
|
96
|
+
specialists[id] = { ...specialists[id], ...val };
|
|
97
|
+
}
|
|
98
|
+
mergeOverlaySection(contracts, readJsonDir(path.join(overlayRoot, 'contracts')));
|
|
99
|
+
mergeOverlaySection(policies, readJsonDir(path.join(overlayRoot, 'policies')));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const teams = { ...groups, ...squads };
|
|
103
|
+
|
|
104
|
+
for (const [specId, spec] of Object.entries(specialists)) {
|
|
105
|
+
if (!spec.team && spec.teamId) spec.team = spec.teamId;
|
|
106
|
+
if (!spec.teamId && spec.team) spec.teamId = spec.team;
|
|
107
|
+
specialists[specId] = spec;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
version: 3,
|
|
112
|
+
teams,
|
|
113
|
+
specialists,
|
|
114
|
+
contracts,
|
|
115
|
+
policies,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function listTeamsFromRegistry(registry, { kind } = {}) {
|
|
120
|
+
const entries = Object.entries(registry.teams || {}).map(([id, team]) => ({ id, ...team }));
|
|
121
|
+
if (kind === 'group') return entries.filter((t) => t.kind === 'group');
|
|
122
|
+
if (kind === 'squad') return entries.filter((t) => t.kind === 'squad');
|
|
123
|
+
return entries;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function listGroupsHierarchical(registry) {
|
|
127
|
+
const groups = listTeamsFromRegistry(registry, { kind: 'group' });
|
|
128
|
+
const squads = listTeamsFromRegistry(registry, { kind: 'squad' });
|
|
129
|
+
return groups.map((group) => ({
|
|
130
|
+
...group,
|
|
131
|
+
squads: squads.filter((s) => s.groupId === group.id),
|
|
132
|
+
}));
|
|
133
|
+
}
|