@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/ui/links.mjs
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/ui/links.mjs — shared OSC-8 hyperlink layer for every construct surface.
|
|
3
|
+
*
|
|
4
|
+
* Canonical link authority for CLI output and generated adapter diagnostics.
|
|
5
|
+
* Renders repo-relative paths and URLs as clickable terminal links (OSC-8) when
|
|
6
|
+
* the stream supports them, and always keeps the raw path/URL as the visible
|
|
7
|
+
* label so terminals that ignore OSC-8 (notably macOS Terminal.app) can still
|
|
8
|
+
* Cmd-click the visible text via their own path/URL detection. Falls back to
|
|
9
|
+
* colored text when links are disabled.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { pathToFileURL } from 'node:url';
|
|
15
|
+
|
|
16
|
+
const OSC8_OPEN = '\x1b]8;;';
|
|
17
|
+
const OSC8_MID = '\x07';
|
|
18
|
+
const OSC8_CLOSE = '\x1b]8;;\x07';
|
|
19
|
+
|
|
20
|
+
export const REPO_PATH_PATTERN =
|
|
21
|
+
/(`?)((?:\.cx\/|docs\/|inbox\/|skills\/|rules\/|lib\/|templates\/|specialists\/|tests\/|platforms\/|personas\/|schemas\/)?[\w][\w./-]*\.(?:md|mdx|json|mjs|ts|tsx|yml|yaml)|construct\.config\.json|package\.json|[A-Z][A-Z0-9_]*\.md)(`?)/g;
|
|
22
|
+
|
|
23
|
+
const REPO_PATH =
|
|
24
|
+
/(^|[\s(,])(`?)((?:\.cx\/|docs\/|inbox\/|skills\/|rules\/|lib\/|templates\/|specialists\/|tests\/|platforms\/|personas\/|schemas\/)?[\w][\w./-]*\.(?:md|mdx|json|mjs|ts|tsx|yml|yaml)|construct\.config\.json|package\.json|[A-Z][A-Z0-9_]*\.md)(`?)(?=$|[\s),.:;])/g;
|
|
25
|
+
|
|
26
|
+
const URL_PATTERN = /\bhttps?:\/\/[^\s<>()\][]+[^\s<>()\][.,;:!?'"]/g;
|
|
27
|
+
|
|
28
|
+
export function terminalLinksEnabled(env = process.env, { plain = false, stream = process.stdout } = {}) {
|
|
29
|
+
if (plain || env.NO_COLOR === '1' || env.CX_PLAIN_COPY === '1') return false;
|
|
30
|
+
if (env.CX_LINKS === '0') return false;
|
|
31
|
+
if (stream?.isTTY) return true;
|
|
32
|
+
if (env.TERM_PROGRAM === 'vscode' || env.TERM_PROGRAM === 'cursor' || env.WT_SESSION) return true;
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function fileUriForPath(filePath, { cwd = process.cwd() } = {}) {
|
|
37
|
+
const abs = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
|
|
38
|
+
return pathToFileURL(abs).href;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// OSC-8 wraps a visible label with an invisible href. Terminals that honor the
|
|
42
|
+
// sequence make the label clickable; terminals that ignore it (Terminal.app)
|
|
43
|
+
// simply print the label — so callers must pass a label that is itself a
|
|
44
|
+
// clickable string (a raw path or URL), never a friendly title that hides it.
|
|
45
|
+
|
|
46
|
+
export function formatTerminalLink(label, href, colors, { enabled = true } = {}) {
|
|
47
|
+
const styled = `${colors?.link || ''}${label}${colors?.reset || ''}`;
|
|
48
|
+
if (!enabled || !href) return styled;
|
|
49
|
+
return `${OSC8_OPEN}${href}${OSC8_MID}${styled}${OSC8_CLOSE}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function formatPathLink(relPath, colors, { cwd = process.cwd(), enabled = true } = {}) {
|
|
53
|
+
const display = relPath.replace(/^`/, '').replace(/`$/, '');
|
|
54
|
+
const abs = path.resolve(cwd, display);
|
|
55
|
+
const href = fileUriForPath(abs, { cwd });
|
|
56
|
+
return formatTerminalLink(display, href, colors, { enabled });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// A bare URL is its own visible label, so OSC-8 terminals get one-click and
|
|
60
|
+
// Terminal.app's URL detection still works on the printed text.
|
|
61
|
+
|
|
62
|
+
export function formatUrlLink(url, colors, { enabled = true } = {}) {
|
|
63
|
+
return formatTerminalLink(url, url, colors, { enabled });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// A titled link ([text](href)) must keep the destination visible for terminals
|
|
67
|
+
// that drop OSC-8: render the title as the clickable label, then append the raw
|
|
68
|
+
// destination so it is both readable and Cmd-clickable everywhere.
|
|
69
|
+
|
|
70
|
+
export function formatTitledLink(label, href, colors, { enabled = true, display = href } = {}) {
|
|
71
|
+
if (!label || label === display) {
|
|
72
|
+
return formatTerminalLink(display, href, colors, { enabled });
|
|
73
|
+
}
|
|
74
|
+
const linked = formatTerminalLink(label, href, colors, { enabled });
|
|
75
|
+
const tail = `${colors?.dim || ''} (${display})${colors?.reset || ''}`;
|
|
76
|
+
return `${linked}${tail}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function linkifyRepoPaths(text, colors, { cwd = process.cwd(), enabled = true } = {}) {
|
|
80
|
+
if (!enabled || text == null) return String(text ?? '');
|
|
81
|
+
const source = String(text);
|
|
82
|
+
return source.replace(REPO_PATH_PATTERN, (full, openTick, relPath, closeTick, offset) => {
|
|
83
|
+
if (offset > 0) {
|
|
84
|
+
const prev = source[offset - 1];
|
|
85
|
+
if (!/[\s(,]/.test(prev)) return full;
|
|
86
|
+
}
|
|
87
|
+
return formatPathLink(`${openTick || ''}${relPath}${closeTick || ''}`, colors, { cwd, enabled });
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function applyPathLinks(text, colors, { cwd = process.cwd(), enabled = true } = {}) {
|
|
92
|
+
if (!enabled || !text) return String(text);
|
|
93
|
+
return String(text).replace(REPO_PATH, (match, prefix, openTick, relPath, closeTick) => {
|
|
94
|
+
const linked = formatPathLink(`${openTick || ''}${relPath}${closeTick || ''}`, colors, { cwd, enabled });
|
|
95
|
+
return `${prefix}${linked}`;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function linkifyUrls(text, colors, { enabled = true } = {}) {
|
|
100
|
+
if (!enabled || text == null) return String(text ?? '');
|
|
101
|
+
return String(text).replace(URL_PATTERN, (url) => formatUrlLink(url, colors, { enabled }));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Linkify both repo paths and URLs in one pass; URLs first so a URL containing
|
|
105
|
+
// a path-like suffix is not split by the path matcher.
|
|
106
|
+
|
|
107
|
+
export function applyLinks(text, colors, { cwd = process.cwd(), enabled = true } = {}) {
|
|
108
|
+
if (!enabled || !text) return String(text ?? '');
|
|
109
|
+
return applyPathLinks(linkifyUrls(text, colors, { enabled }), colors, { cwd, enabled });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function writeLinkedLine(output, line, colors, { cwd, enabled, prefix = '' } = {}) {
|
|
113
|
+
const body = applyLinks(line, colors, { cwd, enabled });
|
|
114
|
+
output.write(prefix ? `${prefix}${body}\n` : `${body}\n`);
|
|
115
|
+
}
|
package/lib/ui/theme.mjs
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/ui/theme.mjs — shared semantic color resolution for every construct surface.
|
|
3
|
+
*
|
|
4
|
+
* Surface-neutral palette for the CLI and generated adapter diagnostics.
|
|
5
|
+
* Terminal codes are basic 16-color SGR, which renders on every
|
|
6
|
+
* terminal including macOS Terminal.app; the truecolor guard degrades any 24-bit
|
|
7
|
+
* value so no raw escape leaks where the terminal cannot honor it.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { shouldUseColor } from '../term-format.mjs';
|
|
11
|
+
|
|
12
|
+
const ESC = '[';
|
|
13
|
+
|
|
14
|
+
function resolveTerminalColorScheme(env = process.env, configTheme = null) {
|
|
15
|
+
const explicit = String(configTheme || env.CONSTRUCT_COLOR_SCHEME || '').toLowerCase();
|
|
16
|
+
if (explicit === 'light' || explicit === 'dark') return explicit;
|
|
17
|
+
const termProgram = String(env.TERM_PROGRAM || '').toLowerCase();
|
|
18
|
+
if (termProgram.includes('apple_terminal')) return 'light';
|
|
19
|
+
return 'dark';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function terminalSemantic(scheme) {
|
|
23
|
+
const dark = scheme !== 'light';
|
|
24
|
+
return {
|
|
25
|
+
text: { code: dark ? '97' : '30' },
|
|
26
|
+
muted: { code: dark ? '90' : '90' },
|
|
27
|
+
accent: { code: dark ? '96' : '36' },
|
|
28
|
+
accentAlt: { code: dark ? '95' : '35' },
|
|
29
|
+
ok: { code: dark ? '92' : '32' },
|
|
30
|
+
warn: { code: dark ? '93' : '33' },
|
|
31
|
+
danger: { code: dark ? '91' : '31' },
|
|
32
|
+
badgeFg: { code: dark ? '30' : '97' },
|
|
33
|
+
surface: { code: dark ? '40' : '47' },
|
|
34
|
+
surfaceMuted: { code: dark ? '100' : '47' },
|
|
35
|
+
border: { code: dark ? '90' : '90' },
|
|
36
|
+
brandAccent: { code: dark ? '97' : '30' },
|
|
37
|
+
highlight: { code: dark ? '96' : '36' },
|
|
38
|
+
heading: { code: dark ? '97' : '30' },
|
|
39
|
+
emphasis: { code: dark ? '93' : '33' },
|
|
40
|
+
code: { code: dark ? '92' : '32' },
|
|
41
|
+
link: { code: dark ? '94' : '34' },
|
|
42
|
+
panel: { code: dark ? '90' : '90' },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// COLORTERM=truecolor|24bit advertises 24-bit support. Terminal.app sets neither
|
|
47
|
+
// and tops out at 256 colors, so absent the flag we treat the stream as non-truecolor.
|
|
48
|
+
|
|
49
|
+
export function supportsTrueColor(env = process.env) {
|
|
50
|
+
const flag = String(env.COLORTERM || '').toLowerCase();
|
|
51
|
+
return flag === 'truecolor' || flag === '24bit';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// SGR codes from design-tokens are already basic 16-color (30-37/90-97). A 24-bit
|
|
55
|
+
// sequence (38;2;r;g;b) only survives when the terminal advertises truecolor;
|
|
56
|
+
// otherwise it is dropped to dim so meaning never rides on an unrenderable color.
|
|
57
|
+
|
|
58
|
+
function degradeCode(code, trueColor) {
|
|
59
|
+
if (!code) return code;
|
|
60
|
+
if (/^38;2;/.test(String(code)) && !trueColor) return '2';
|
|
61
|
+
return code;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function resolveUiColors({
|
|
65
|
+
enabled = true,
|
|
66
|
+
stream = process.stdout,
|
|
67
|
+
env = process.env,
|
|
68
|
+
scheme: explicitScheme = null,
|
|
69
|
+
configTheme = null,
|
|
70
|
+
} = {}) {
|
|
71
|
+
const scheme = explicitScheme || resolveTerminalColorScheme(env, configTheme);
|
|
72
|
+
const useColor = shouldUseColor({ enabled, stream, env });
|
|
73
|
+
const trueColor = supportsTrueColor(env);
|
|
74
|
+
const semantic = terminalSemantic(scheme);
|
|
75
|
+
const codes = Object.fromEntries(Object.entries(semantic).map(([k, v]) => [k, degradeCode(v.code, trueColor)]));
|
|
76
|
+
const reset = useColor ? `${ESC}0m` : '';
|
|
77
|
+
const wrap = (code) => (useColor && code ? `${ESC}${code}m` : '');
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
scheme,
|
|
81
|
+
reset,
|
|
82
|
+
bold: wrap('1'),
|
|
83
|
+
dim: wrap('2'),
|
|
84
|
+
underline: wrap('4'),
|
|
85
|
+
text: wrap(codes.text),
|
|
86
|
+
muted: wrap(codes.muted),
|
|
87
|
+
accent: wrap(codes.accent),
|
|
88
|
+
accentAlt: wrap(codes.accentAlt),
|
|
89
|
+
ok: wrap(codes.ok),
|
|
90
|
+
warn: wrap(codes.warn),
|
|
91
|
+
danger: wrap(codes.danger),
|
|
92
|
+
badgeFg: wrap(codes.badgeFg),
|
|
93
|
+
surface: wrap(codes.surface),
|
|
94
|
+
surfaceMuted: wrap(codes.surfaceMuted),
|
|
95
|
+
border: wrap(codes.border),
|
|
96
|
+
brandAccent: wrap(codes.brandAccent),
|
|
97
|
+
highlight: wrap(codes.highlight),
|
|
98
|
+
heading: wrap(codes.heading),
|
|
99
|
+
emphasis: wrap(codes.emphasis),
|
|
100
|
+
code: wrap(codes.code),
|
|
101
|
+
link: wrap(codes.link),
|
|
102
|
+
panel: wrap(codes.panel),
|
|
103
|
+
green: wrap(codes.ok),
|
|
104
|
+
yellow: wrap(codes.warn),
|
|
105
|
+
cyan: wrap(codes.highlight),
|
|
106
|
+
red: wrap(codes.danger),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
import fs from 'node:fs';
|
|
33
|
+
import { loadRegistry } from '../registry/loader.mjs';
|
|
33
34
|
import path from 'node:path';
|
|
34
35
|
import os from 'node:os';
|
|
35
36
|
import { spawnSync } from 'node:child_process';
|
|
@@ -466,7 +467,7 @@ function unmergeSettings(filePath) {
|
|
|
466
467
|
|
|
467
468
|
function constructMcpKeys() {
|
|
468
469
|
try {
|
|
469
|
-
const registry =
|
|
470
|
+
const registry = loadRegistry({ rootDir: PKG_ROOT });
|
|
470
471
|
return Object.keys(registry.mcpServers || {});
|
|
471
472
|
} catch { return []; }
|
|
472
473
|
}
|
package/lib/validator.mjs
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* field-level detail. Backs workflow-state, cli-commands, and MCP tools
|
|
7
7
|
* before they mutate persistent state.
|
|
8
8
|
*/
|
|
9
|
+
import { loadRegistry } from './registry/loader.mjs';
|
|
9
10
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
10
11
|
import { existsSync, readFileSync } from 'node:fs';
|
|
11
12
|
import { join, dirname } from 'node:path';
|
|
@@ -55,19 +56,55 @@ export function validateRegistry(reg, options = {}) {
|
|
|
55
56
|
const errors = [];
|
|
56
57
|
const rootDir = options.rootDir ?? join(__dirname, '..');
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
// Handle unified registry format (v2) - convert specialists from object to array
|
|
60
|
+
// and extract orchestrator from specialists
|
|
61
|
+
let registry = reg;
|
|
62
|
+
if (reg.version === 2 && typeof reg.specialists === 'object' && !Array.isArray(reg.specialists)) {
|
|
63
|
+
const orchestrator = Object.values(reg.specialists || {}).find(s => s.role === 'orchestrator');
|
|
64
|
+
const otherSpecialists = Object.values(reg.specialists || {}).filter(s => s.role !== 'orchestrator');
|
|
65
|
+
registry = {
|
|
66
|
+
version: 1,
|
|
67
|
+
system: 'construct',
|
|
68
|
+
prefix: 'cx',
|
|
69
|
+
orchestrator: orchestrator ? {
|
|
70
|
+
name: orchestrator.name,
|
|
71
|
+
displayName: orchestrator.displayName || orchestrator.description?.split('—')[0].trim(),
|
|
72
|
+
description: orchestrator.description || '',
|
|
73
|
+
promptFile: orchestrator.promptFile,
|
|
74
|
+
modelTier: orchestrator.modelTier || 'standard',
|
|
75
|
+
skills: orchestrator.skills || [],
|
|
76
|
+
} : null,
|
|
77
|
+
specialists: otherSpecialists.map(s => ({
|
|
78
|
+
name: s.name,
|
|
79
|
+
displayName: s.displayName || s.description?.split('—')[0].trim(),
|
|
80
|
+
description: s.description || '',
|
|
81
|
+
promptFile: s.promptFile,
|
|
82
|
+
modelTier: s.modelTier || 'standard',
|
|
83
|
+
skills: s.skills || [],
|
|
84
|
+
team: s.team,
|
|
85
|
+
role: s.role,
|
|
86
|
+
docArtifacts: s.docArtifacts || [],
|
|
87
|
+
subscriptions: s.events || [],
|
|
88
|
+
watchConditions: s.watchConditions || [],
|
|
89
|
+
fence: s.fence || {},
|
|
90
|
+
})),
|
|
91
|
+
mcpServers: reg.mcpServers || {},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!registry.version && registry.version !== 0) errors.push('registry: version is missing');
|
|
96
|
+
if (!registry.system) errors.push('registry: system is missing');
|
|
97
|
+
if (!registry.prefix) errors.push('registry: prefix is missing');
|
|
61
98
|
|
|
62
|
-
if (!Array.isArray(
|
|
99
|
+
if (!Array.isArray(registry.specialists) || registry.specialists.length === 0) {
|
|
63
100
|
errors.push('registry: specialists must be a non-empty array');
|
|
64
101
|
}
|
|
65
|
-
if (!
|
|
102
|
+
if (!registry.orchestrator || typeof registry.orchestrator !== 'object') {
|
|
66
103
|
errors.push('registry: orchestrator must be a non-empty object');
|
|
67
104
|
}
|
|
68
105
|
|
|
69
|
-
const specialists = Array.isArray(
|
|
70
|
-
const orchestrator =
|
|
106
|
+
const specialists = Array.isArray(registry.specialists) ? registry.specialists : [];
|
|
107
|
+
const orchestrator = registry.orchestrator && typeof registry.orchestrator === 'object' ? registry.orchestrator : null;
|
|
71
108
|
|
|
72
109
|
const allIds = new Set();
|
|
73
110
|
const specialistNames = new Set(specialists.map((s) => s?.name).filter(Boolean));
|
|
@@ -218,7 +255,7 @@ export function validateRegistry(reg, options = {}) {
|
|
|
218
255
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
219
256
|
|
|
220
257
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
221
|
-
const reg =
|
|
258
|
+
const reg = loadRegistry();
|
|
222
259
|
const result = validateRegistry(reg);
|
|
223
260
|
if (result.valid) {
|
|
224
261
|
console.log(`✓ Registry valid (${result.summary})`);
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/validators/skill-effectiveness.mjs — Content-depth validation beyond structure.
|
|
3
|
+
*
|
|
4
|
+
* Checks role failure modes, workflow chains, domain boundaries, and resolvable
|
|
5
|
+
* skill references for scope lint and release-gate enforcement.
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
|
|
10
|
+
import { loadRegistry } from '../registry/loader.mjs';
|
|
11
|
+
import { artifactTypes } from '../artifact-manifest.mjs';
|
|
12
|
+
|
|
13
|
+
const GET_SKILL_RE = /get_skill\("([^"]+)"\)/g;
|
|
14
|
+
const CX_SPECIALIST_RE = /\bcx-[a-z][a-z0-9-]+\b/g;
|
|
15
|
+
|
|
16
|
+
function walkSkills(skillsDir, prefix = '') {
|
|
17
|
+
const out = [];
|
|
18
|
+
if (!fs.existsSync(skillsDir)) return out;
|
|
19
|
+
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
20
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
21
|
+
const full = path.join(skillsDir, entry.name);
|
|
22
|
+
if (entry.isDirectory()) out.push(...walkSkills(full, rel));
|
|
23
|
+
else if (entry.name.endsWith('.md') && entry.name !== 'routing.md') {
|
|
24
|
+
out.push({ rel: rel.replace(/\.md$/, ''), full });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseFrontmatter(content) {
|
|
31
|
+
const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
32
|
+
if (!m) return { meta: {}, body: content };
|
|
33
|
+
const meta = {};
|
|
34
|
+
for (const line of m[1].split('\n')) {
|
|
35
|
+
const kv = line.match(/^([a-zA-Z0-9_-]+):\s*(.*)$/);
|
|
36
|
+
if (!kv) continue;
|
|
37
|
+
const [, key, raw] = kv;
|
|
38
|
+
if (raw.startsWith('[')) {
|
|
39
|
+
try { meta[key] = JSON.parse(raw); } catch { meta[key] = raw; }
|
|
40
|
+
} else {
|
|
41
|
+
meta[key] = raw.replace(/^["']|["']$/g, '');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { meta, body: content.slice(m[0].length) };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isRoleSkill(rel) {
|
|
48
|
+
return rel.startsWith('roles/');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isWorkflowSkill(rel) {
|
|
52
|
+
return rel.startsWith('docs/') && rel.endsWith('-workflow');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isDomainSkill(rel) {
|
|
56
|
+
return !isRoleSkill(rel) && !isWorkflowSkill(rel) && rel !== 'routing';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function validateRoleDepth(rel, body, errors) {
|
|
60
|
+
const numberedH3 = (body.match(/^###\s+\d+\./gm) || []).length;
|
|
61
|
+
const numberedList = (body.match(/^\d+\.\s+\*\*/gm) || []).length;
|
|
62
|
+
if (numberedH3 + numberedList < 3) {
|
|
63
|
+
errors.push(`${rel}: role overlay needs ≥3 numbered failure-mode sections (### N. or 1. **Title**)`);
|
|
64
|
+
}
|
|
65
|
+
const hasSymptom = /\*\*Symptom\*\*:|Symptom:/i.test(body);
|
|
66
|
+
const hasCounter = /\*\*Counter-move\*\*:|Counter-move:|Counter:/i.test(body);
|
|
67
|
+
if (!hasSymptom || !hasCounter) {
|
|
68
|
+
errors.push(`${rel}: role overlay missing Symptom/Counter-move pattern`);
|
|
69
|
+
}
|
|
70
|
+
const isBase = !rel.includes('.') && !/^roles\/[^/]+\.[^/]+$/.test(rel);
|
|
71
|
+
if (isBase && !/## Self-check|## Methodology|## Ship Check/i.test(body)) {
|
|
72
|
+
errors.push(`${rel}: base role file should include Self-check or Methodology section`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function countWorkflowSteps(body) {
|
|
77
|
+
const lines = body.split(/\r?\n/);
|
|
78
|
+
let inFence = false;
|
|
79
|
+
let count = 0;
|
|
80
|
+
for (const line of lines) {
|
|
81
|
+
if (/^```/.test(line.trim())) inFence = !inFence;
|
|
82
|
+
if (inFence) continue;
|
|
83
|
+
if (/^\d+\.\s+/.test(line)) count++;
|
|
84
|
+
}
|
|
85
|
+
return count;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function validateWorkflowDepth(rel, meta, body, errors) {
|
|
89
|
+
if (!meta.verificationBar || String(meta.verificationBar).length < 40) {
|
|
90
|
+
errors.push(`${rel}: workflow missing verificationBar (≥40 chars)`);
|
|
91
|
+
}
|
|
92
|
+
if (!meta.artifactType) {
|
|
93
|
+
errors.push(`${rel}: workflow missing artifactType`);
|
|
94
|
+
}
|
|
95
|
+
if (!/construct artifact validate/i.test(body)) {
|
|
96
|
+
errors.push(`${rel}: workflow must mention construct artifact validate`);
|
|
97
|
+
}
|
|
98
|
+
if (countWorkflowSteps(body) < 4) {
|
|
99
|
+
errors.push(`${rel}: workflow needs ≥4 numbered steps`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function validateDomainDepth(rel, body, meta, errors) {
|
|
104
|
+
const stripped = body.replace(/^---[\s\S]*?---/, '').trim();
|
|
105
|
+
if (stripped.length < 400) {
|
|
106
|
+
errors.push(`${rel}: domain skill body too short (<400 chars)`);
|
|
107
|
+
}
|
|
108
|
+
const desc = String(meta.description || '');
|
|
109
|
+
const boundary =
|
|
110
|
+
/when not to use|when to use|do not use|use this skill when|use this skill to|use when:/i.test(body) ||
|
|
111
|
+
/use when|use this skill when|use this skill to|do not use/i.test(desc);
|
|
112
|
+
if (!boundary) {
|
|
113
|
+
errors.push(`${rel}: domain skill missing boundary section (When not to use / When to use)`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function validateReferences(rel, body, skillsSet, registry, errors) {
|
|
118
|
+
for (const m of body.matchAll(GET_SKILL_RE)) {
|
|
119
|
+
const id = m[1];
|
|
120
|
+
if (!skillsSet.has(id)) {
|
|
121
|
+
errors.push(`${rel}: unresolved get_skill("${id}")`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (isWorkflowSkill(rel)) {
|
|
125
|
+
for (const id of body.match(CX_SPECIALIST_RE) || []) {
|
|
126
|
+
const key = id.replace(/^cx-/, '');
|
|
127
|
+
const found = Object.values(registry.specialists || {}).some(
|
|
128
|
+
(s) => s.name === key || `cx-${s.name}` === id,
|
|
129
|
+
);
|
|
130
|
+
if (!found) errors.push(`${rel}: unknown specialist ${id}`);
|
|
131
|
+
}
|
|
132
|
+
const types = artifactTypes();
|
|
133
|
+
for (const m of body.matchAll(/`([a-z][a-z0-9-]*)`/g)) {
|
|
134
|
+
const t = m[1];
|
|
135
|
+
if (t.startsWith('prd') || t === 'rfc' || t === 'adr' || t === 'memo') {
|
|
136
|
+
if (!types.includes(t) && !types.includes(t.replace(/-platform$/, ''))) {
|
|
137
|
+
/* template ids vary — only warn on explicit get_template */
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function validateSkillEffectiveness({ rootDir } = {}) {
|
|
145
|
+
const root = rootDir || path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..');
|
|
146
|
+
const skillsDir = path.join(root, 'skills');
|
|
147
|
+
const errors = [];
|
|
148
|
+
const warnings = [];
|
|
149
|
+
const files = walkSkills(skillsDir);
|
|
150
|
+
const skillsSet = new Set(files.map((f) => f.rel));
|
|
151
|
+
let registry;
|
|
152
|
+
try {
|
|
153
|
+
registry = loadRegistry({ rootDir: root });
|
|
154
|
+
} catch {
|
|
155
|
+
registry = { specialists: {} };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
for (const { rel, full } of files) {
|
|
159
|
+
const raw = fs.readFileSync(full, 'utf8');
|
|
160
|
+
const { meta, body } = parseFrontmatter(raw);
|
|
161
|
+
if (isRoleSkill(rel) && meta.role?.includes('.')) {
|
|
162
|
+
validateRoleDepth(rel, body, errors);
|
|
163
|
+
} else if (isRoleSkill(rel)) {
|
|
164
|
+
if (body.length > 200) validateRoleDepth(rel, body, errors);
|
|
165
|
+
} else if (isWorkflowSkill(rel)) {
|
|
166
|
+
validateWorkflowDepth(rel, meta, body, errors);
|
|
167
|
+
} else if (isDomainSkill(rel)) {
|
|
168
|
+
validateDomainDepth(rel, body, meta, errors);
|
|
169
|
+
}
|
|
170
|
+
validateReferences(rel, body, skillsSet, registry, errors);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return { valid: errors.length === 0, errors, warnings, checked: files.length };
|
|
174
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* description: "<≤1024 chars, includes a 'use when' trigger>"
|
|
9
9
|
* [inputs: [<string>, …] — optional, surfaced by capability discovery]
|
|
10
10
|
* [artifactType: <string> — optional, surfaced by capability discovery]
|
|
11
|
-
* [role/applies_to/inherits/version/
|
|
11
|
+
* [role/applies_to/inherits/version/scopes/cap for role files]
|
|
12
12
|
* ---
|
|
13
13
|
* # <Title>
|
|
14
14
|
* Use when: <trigger condition>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geraldmaron/construct",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "npm@11.5.1",
|
|
6
6
|
"description": "Construct — agent orchestration layer for OpenCode, Claude Code, and other coding surfaces",
|
|
@@ -51,12 +51,15 @@
|
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"mcp": "node lib/mcp/server.mjs",
|
|
54
|
+
"doctor": "node ./bin/construct doctor",
|
|
54
55
|
"test": "node scripts/run-tests.mjs",
|
|
55
56
|
"test:functional": "XDG_CONFIG_HOME= XDG_STATE_HOME= XDG_CACHE_HOME= node --test --test-timeout=120000 --test-concurrency=1 tests/functional/*.functional.test.mjs",
|
|
57
|
+
"test:visual": "node -e \"console.log('visual surface tests removed')\"",
|
|
56
58
|
"test:unit": "node scripts/run-tests.mjs --exclude=tests/functional",
|
|
57
59
|
"lint:js": "eslint bin/construct \"lib/**/*.mjs\" \"scripts/**/*.mjs\" \"tests/**/*.mjs\"",
|
|
58
60
|
"coverage": "c8 --reporter=text-summary --reporter=lcov --src=lib --src=bin node scripts/run-tests.mjs --exclude=tests/functional",
|
|
59
61
|
"docs:init": "node -e \"console.error('docs:init is deprecated — use: construct init --docs-preset=lean|product|full'); process.exit(1)\"",
|
|
62
|
+
"docs:verify": "node ./bin/construct docs:verify",
|
|
60
63
|
"docs:update": "node ./bin/construct docs:update",
|
|
61
64
|
"docs:site": "node ./bin/construct docs:site",
|
|
62
65
|
"docs:dev": "node scripts/npm-run.mjs npm --prefix apps/docs run dev",
|
|
@@ -64,20 +67,22 @@
|
|
|
64
67
|
"lint:comments": "node ./bin/construct lint:comments",
|
|
65
68
|
"lint:contracts": "node ./bin/construct lint:contracts",
|
|
66
69
|
"lint:agents": "node ./bin/construct lint:agents",
|
|
70
|
+
"gates:audit": "node ./bin/construct gates:audit",
|
|
71
|
+
"evals": "node ./bin/construct evals",
|
|
72
|
+
"certify:demos": "node ./bin/construct certify demos",
|
|
67
73
|
"build:sea": "esbuild bin/construct --bundle --platform=node --target=node20 --packages=external --outfile=dist/construct-bundle.cjs 2>&1 || (echo 'esbuild not found — install with: npm install -g esbuild' && exit 1)",
|
|
68
|
-
"build:chat": "esbuild apps/chat/tui/index.jsx --bundle --platform=node --format=esm --packages=external --jsx=automatic --jsx-import-source=react --outfile=apps/chat/dist/tui.mjs",
|
|
69
|
-
"build:chat-desktop": "node scripts/build-chat-desktop.mjs",
|
|
70
|
-
"build:chat-desktop:bundle": "node scripts/npm-run.mjs npm install --prefix apps/chat/desktop --include=dev && node scripts/npm-run.mjs npm --prefix apps/chat/desktop run build",
|
|
71
|
-
"build:dashboard": "node scripts/npm-run.mjs npm install --prefix apps/dashboard && node scripts/npm-run.mjs npm --prefix apps/dashboard run build",
|
|
72
74
|
"examples:deck": "node scripts/generate-deck-examples.mjs",
|
|
73
75
|
"examples:distribution": "node scripts/generate-distribution-examples.mjs",
|
|
74
76
|
"clean:artifacts": "node scripts/clean-artifacts.mjs",
|
|
75
|
-
"lint:
|
|
77
|
+
"lint:scopes": "node scripts/lint-scopes.mjs",
|
|
78
|
+
"catalog:regen": "node scripts/catalog-regen.mjs",
|
|
79
|
+
"catalog:validate": "node scripts/catalog-validate.mjs",
|
|
80
|
+
"docs:sync": "node scripts/docs-sync.mjs",
|
|
76
81
|
"learning:status": "node scripts/learning-status.mjs",
|
|
77
82
|
"lint:templates": "node scripts/lint-commits-pr.mjs",
|
|
78
83
|
"eval:routing": "node scripts/eval/score-intent-classifier.mjs",
|
|
79
84
|
"adapters": "node lib/adapters-sync.mjs",
|
|
80
|
-
"release:check": "node ./bin/construct
|
|
85
|
+
"release:check": "npm run doctor && node ./bin/construct registry:validate --unified && npm run catalog:validate -- --check && npm test && npm run docs:update -- --check && npm run docs:site -- --check && npm run docs:sync -- --check && node ./bin/construct registry:generate-docs --check && npm run docs:verify -- --strict && node scripts/alignment/census.mjs --ratchet && npm run lint:comments && node ./bin/construct certify gate && npm run lint:templates",
|
|
81
86
|
"release:gate": "node --test tests/functional/release-gate.functional.test.mjs",
|
|
82
87
|
"audit:published": "node scripts/audit-published-artifact.mjs",
|
|
83
88
|
"version": "node scripts/sync-construct-version.mjs && git add -f .construct/version",
|
package/personas/construct.md
CHANGED
|
@@ -31,13 +31,22 @@ Tracks: immediate (act directly), focused (one bounded specialist), orchestrated
|
|
|
31
31
|
|
|
32
32
|
Orchestrated dispatches emit a task-packet with `goal`, `intent`, `workCategory`, `riskFlags`, `acceptanceCriteria` before naming specialists (`specialists/contracts.json:construct-to-orchestrator`).
|
|
33
33
|
|
|
34
|
+
Research-shaped requests and artifact-drafting requests are never "answer from memory" work. If the request is asking for current evidence, comparison, standards, or a typed output, route it through the matching execution path first:
|
|
35
|
+
- research / compare / explore / explain external state → call `orchestration_run` with the original request and `workflow_type: "research-synthesis"` unless the user explicitly supplied a raw evidence bundle, in which case use the evidence-ingest path
|
|
36
|
+
- draft or revise a typed artifact/output → use the canonical template for that artifact and route through the matching workflow before writing the final draft; use `workflow_invoke` only when the user is asking for the plan / contract preview rather than execution
|
|
37
|
+
- never claim research was completed unless `orchestration_run`, evidence tools, or a user-supplied evidence bundle actually produced the evidence
|
|
38
|
+
- if evidence is missing or execution is unavailable, say what is missing and ask for the minimal next input instead of inventing a process narrative or conclusion
|
|
39
|
+
|
|
40
|
+
General conversation is still valid for scoping, clarification, and lightweight discussion, but the default for substantive research is evidence-first execution, not free-form synthesis.
|
|
41
|
+
|
|
34
42
|
## Gates and contracts (org-in-a-box) <!-- cx:prio=2 -->
|
|
35
43
|
|
|
36
|
-
`orchestration_policy` returns
|
|
44
|
+
`orchestration_policy` returns four artifacts; honor all four:
|
|
37
45
|
|
|
38
46
|
1. **Gates**. `framingChallenge`, `externalResearch`, `docAuthoring`
|
|
39
47
|
2. **Contract chain**. typed handoffs from `specialists/contracts.json`. Call `agent_contract` with `handoffPacket` from `orchestration_policy`.
|
|
40
48
|
3. **Specialist sequence**. dispatch plan with ordering/parallel markers.
|
|
49
|
+
4. **Team routing**. name `teamRouting.primaryTeam` in the dispatch plan; route through `teamRouting.requiredApprovals` before DONE; if `teamRouting.blockedStatus` is set, stop and escalate along its `escalationPath` rather than proceeding.
|
|
41
50
|
|
|
42
51
|
Before DONE: postconditions met · sources cited · framing logged · ADRs have Rejected alternatives.
|
|
43
52
|
|
|
@@ -48,12 +57,12 @@ Before DONE: postconditions met · sources cited · framing logged · ADRs have
|
|
|
48
57
|
|
|
49
58
|
## Intake surface <!-- cx:prio=3 -->
|
|
50
59
|
|
|
51
|
-
The active profile (`construct
|
|
60
|
+
The active profile (`construct scope show`) sets the intake taxonomy. Session-start surfaces pending intake at `.cx/intake/pending/<id>.json`. Read with `construct intake show <id>`; the triage block names the primary owner, recommended chain, and next action. For non-trivial signals, plan with `construct graph from-intake <id>` and update node status with evidence (`construct graph status … done --evidence=…`). A node cannot reach `done` without an evidence record. Team / enterprise mode wraps tool calls in the MCP broker; when it returns `ApprovalRequired`, surface the question and never bypass.
|
|
52
61
|
|
|
53
62
|
## Action discipline <!-- cx:prio=1 -->
|
|
54
63
|
|
|
55
64
|
- Dispatch, don't solo-plan: 3+ files, 2+ modules, or a new contract → cx-architect owns the plan.
|
|
56
|
-
- Ask or look up, don't speculate:
|
|
65
|
+
- Ask or look up, don't speculate: use the route's `researchExecutionPolicy`. For library/framework/API docs, prefer Context7 when available; otherwise search and fetch official docs directly. For broader research, go to domain-primary sources. Never a fourth round of internal debate.
|
|
57
66
|
- Deliberation cap: two passes. Same decision twice without a new read, tool call, or user input = hand off, query, or ask.
|
|
58
67
|
- Probe before bulk read: check size via `Glob` / `wc -l` or a `limit: 50` probe before `Read` with `limit > 200`.
|
|
59
68
|
- Start-of-task: parallel bootstrap (above) + `cx_trace` before anything mutating.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This file ships as a reference template alongside the Construct npm package. The canonical project-instructions file is `/CLAUDE.md` at your repo root — that's the file Claude Code actually reads at session start. The structure here mirrors that canonical file so users know what shape their own `/CLAUDE.md` should take after `construct init` scaffolds a project.
|
|
4
4
|
|
|
5
|
-
Personas and specialists are defined in `specialists/
|
|
5
|
+
Personas and specialists are defined in `specialists/org` and rendered into Claude Code agents on every `construct sync`. The single user-facing persona is `construct`; the 28 specialists (cx-engineer, cx-security, cx-devil-advocate, …) are routed internally — you address Construct, Construct dispatches.
|
|
6
6
|
|
|
7
7
|
## Critical rules (mirror in your project /CLAUDE.md)
|
|
8
8
|
|