@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/demo.mjs
CHANGED
|
@@ -2,19 +2,23 @@
|
|
|
2
2
|
* lib/demo.mjs — `construct demo` terminal recordings via VHS `.tape` files.
|
|
3
3
|
*
|
|
4
4
|
* Shipped tapes live in templates/demos/tapes/; project overrides in
|
|
5
|
-
* .cx/demos/tapes/. Built-in scaffolds via `construct demo init --from
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* .cx/demos/tapes/. Built-in scaffolds via `construct demo init --from=`;
|
|
6
|
+
* `--from-project` scaffolds a project demo plug-in (manifest + script + tape)
|
|
7
|
+
* under .cx/demos/ seeded from real project signals (see lib/demo-project.mjs).
|
|
8
|
+
* Default run mode writes or records a VHS tape; use `tour` for a linear
|
|
9
|
+
* guided walkthrough and `--surface=playwright` for recording manifests.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
11
13
|
import { spawnSync } from 'node:child_process';
|
|
12
14
|
import { join, resolve, dirname, basename, relative } from 'node:path';
|
|
13
15
|
import { fileURLToPath } from 'node:url';
|
|
14
|
-
import { listDemoScripts } from './demo-script.mjs';
|
|
16
|
+
import { listDemoScripts, loadDemoScript } from './demo-script.mjs';
|
|
15
17
|
import { DEMO_SURFACES } from './demo-surface.mjs';
|
|
18
|
+
import { renderTour } from './demo-tour-renderer.mjs';
|
|
16
19
|
import { listDemoRecordings, loadDemoRecording } from './demo-recording.mjs';
|
|
17
20
|
import { recordPlaywrightDemo } from './playwright-demo.mjs';
|
|
21
|
+
import { scaffoldProjectDemo } from './demo-project.mjs';
|
|
18
22
|
|
|
19
23
|
const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
20
24
|
|
|
@@ -219,7 +223,7 @@ export function initConstructCockpitRecording(name, { cwd, repoRoot = REPO_ROOT
|
|
|
219
223
|
...reference,
|
|
220
224
|
name,
|
|
221
225
|
title: reference.title,
|
|
222
|
-
output: { format: 'mp4', path: `.cx/demos
|
|
226
|
+
output: { format: 'mp4', path: `.cx/demos/${name}.mp4` },
|
|
223
227
|
_reference: 'agentic-platforms-prd',
|
|
224
228
|
};
|
|
225
229
|
delete recording.sourcePath;
|
|
@@ -228,7 +232,7 @@ export function initConstructCockpitRecording(name, { cwd, repoRoot = REPO_ROOT
|
|
|
228
232
|
return {
|
|
229
233
|
ok: true,
|
|
230
234
|
recordingPath: recPath,
|
|
231
|
-
message: `Scaffolded Construct cockpit recording ${recPath}
|
|
235
|
+
message: `Scaffolded Construct cockpit recording ${recPath}`,
|
|
232
236
|
};
|
|
233
237
|
}
|
|
234
238
|
|
|
@@ -306,10 +310,6 @@ export function listProjectTapes({ cwd = process.cwd(), repoRoot = REPO_ROOT } =
|
|
|
306
310
|
}
|
|
307
311
|
|
|
308
312
|
export function resolveTapeSource(name, { cwd, repoRoot = REPO_ROOT, artifactPath } = {}) {
|
|
309
|
-
if (name.startsWith('dashboard:')) {
|
|
310
|
-
return { kind: 'dashboard', name: name.slice('dashboard:'.length) };
|
|
311
|
-
}
|
|
312
|
-
|
|
313
313
|
const tapePath = resolveTapeFile(name, { cwd, repoRoot });
|
|
314
314
|
if (tapePath) {
|
|
315
315
|
let source = readFileSync(tapePath, 'utf8');
|
|
@@ -402,10 +402,6 @@ export function runDemoRecord(name, {
|
|
|
402
402
|
required = false,
|
|
403
403
|
surface = null,
|
|
404
404
|
} = {}) {
|
|
405
|
-
if (name.startsWith('dashboard:')) {
|
|
406
|
-
return { ok: false, name, message: 'Use recordDashboardDemo for dashboard: demos' };
|
|
407
|
-
}
|
|
408
|
-
|
|
409
405
|
const recording = loadDemoRecording(name, { cwd, repoRoot });
|
|
410
406
|
const usePlaywright = surface !== 'tape'
|
|
411
407
|
&& (surface === 'playwright' || (recording && recording.engine === 'playwright'));
|
|
@@ -506,28 +502,28 @@ export function printHelp() {
|
|
|
506
502
|
Commands:
|
|
507
503
|
list List tapes, recordings, and demo scripts
|
|
508
504
|
init <name> [--from=T] Scaffold tape or Playwright recording
|
|
505
|
+
init <name> --from-project Scaffold a project demo plug-in under .cx/demos/
|
|
509
506
|
record <name> Record VHS tape or Playwright demo manifest
|
|
510
|
-
|
|
507
|
+
tour [name] Linear, screen-reader-friendly guided tour
|
|
508
|
+
<name> Record or print the demo (default: tape)
|
|
511
509
|
|
|
512
|
-
Default run uses
|
|
513
|
-
Falls back to web chat, VHS tape, Playwright, dashboard, or printed steps.
|
|
510
|
+
Default run uses VHS tape templates and falls back to printed script steps.
|
|
514
511
|
|
|
515
512
|
Options:
|
|
516
513
|
--surface=<s> ${surfaces}
|
|
517
|
-
--
|
|
518
|
-
--
|
|
519
|
-
--plain Linear chat renderer
|
|
520
|
-
--free OpenRouter free-router mode
|
|
514
|
+
--accessible Screen-reader-friendly linear renderer (tour)
|
|
515
|
+
--skip-input Tour: auto-advance without waiting for Enter (headless/CI)
|
|
521
516
|
--format <f> gif (default) | mp4 | webm
|
|
522
517
|
--out <path> Output path (recording)
|
|
518
|
+
--from-project init: scaffold a project demo plug-in under .cx/demos/
|
|
523
519
|
--source-only Tape: write .tape only; skip recording
|
|
524
520
|
-h, --help Show this message
|
|
525
521
|
|
|
526
522
|
Examples:
|
|
527
523
|
construct demo agentic-platforms-prd
|
|
524
|
+
construct demo tour agentic-platforms-prd --accessible
|
|
528
525
|
construct demo record agentic-platforms-prd --format mp4
|
|
529
526
|
construct demo init marketing --from=nextjs-static
|
|
530
|
-
construct demo dashboard:cockpit-tour
|
|
531
527
|
`);
|
|
532
528
|
}
|
|
533
529
|
|
|
@@ -539,22 +535,19 @@ function parseArgs(argv) {
|
|
|
539
535
|
from: 'quickstart',
|
|
540
536
|
command: null,
|
|
541
537
|
name: null,
|
|
542
|
-
surface: '
|
|
543
|
-
model: null,
|
|
544
|
-
plain: false,
|
|
538
|
+
surface: 'tape',
|
|
545
539
|
accessible: false,
|
|
546
|
-
|
|
547
|
-
|
|
540
|
+
skipInput: false,
|
|
541
|
+
fromProject: false,
|
|
548
542
|
};
|
|
549
543
|
const positional = [];
|
|
550
544
|
for (let i = 0; i < argv.length; i += 1) {
|
|
551
545
|
const arg = argv[i];
|
|
552
546
|
if (arg === '--help' || arg === '-h') { options.help = true; continue; }
|
|
547
|
+
if (arg === '--from-project') { options.fromProject = true; continue; }
|
|
553
548
|
if (arg === '--source-only') { options.sourceOnly = true; continue; }
|
|
554
|
-
if (arg === '--plain') { options.plain = true; continue; }
|
|
555
549
|
if (arg === '--accessible') { options.accessible = true; continue; }
|
|
556
|
-
if (arg === '--
|
|
557
|
-
if (arg === '--free') { options.free = true; continue; }
|
|
550
|
+
if (arg === '--skip-input') { options.skipInput = true; continue; }
|
|
558
551
|
if (arg === '--format') { options.format = argv[++i]; continue; }
|
|
559
552
|
if (arg.startsWith('--format=')) { options.format = arg.slice(9); continue; }
|
|
560
553
|
if (arg === '--out') { options.out = argv[++i]; continue; }
|
|
@@ -563,13 +556,12 @@ function parseArgs(argv) {
|
|
|
563
556
|
if (arg.startsWith('--from=')) { options.from = arg.slice(7); continue; }
|
|
564
557
|
if (arg === '--surface') { options.surface = argv[++i]; continue; }
|
|
565
558
|
if (arg.startsWith('--surface=')) { options.surface = arg.slice(10); continue; }
|
|
566
|
-
if (arg === '--model') { options.model = argv[++i]; continue; }
|
|
567
|
-
if (arg.startsWith('--model=')) { options.model = arg.slice(8); continue; }
|
|
568
559
|
positional.push(arg);
|
|
569
560
|
}
|
|
570
561
|
if (positional[0] === 'list') options.command = 'list';
|
|
571
562
|
else if (positional[0] === 'init') { options.command = 'init'; options.name = positional[1]; }
|
|
572
563
|
else if (positional[0] === 'record') { options.command = 'record'; options.name = positional[1]; }
|
|
564
|
+
else if (positional[0] === 'tour') { options.command = 'tour'; options.name = positional[1] || null; }
|
|
573
565
|
else options.name = positional[0] || null;
|
|
574
566
|
return options;
|
|
575
567
|
}
|
|
@@ -591,7 +583,7 @@ export async function runDemoCli(argv = [], { cwd = process.cwd(), repoRoot } =
|
|
|
591
583
|
console.log('\nPlaywright recordings (.cx/demos/recordings/ overrides templates/demos/recordings/):');
|
|
592
584
|
if (!recordings.length) console.log(' (none — run `construct demo init <name> --from=nextjs-static`)');
|
|
593
585
|
else for (const r of recordings) console.log(` ${r}`);
|
|
594
|
-
console.log('\nDemo scripts (
|
|
586
|
+
console.log('\nDemo scripts (tour steps; templates/demos/scripts/):');
|
|
595
587
|
if (!scripts.length) console.log(' (none)');
|
|
596
588
|
else for (const s of scripts) console.log(` ${s}`);
|
|
597
589
|
console.log(`\nScaffold templates: ${RECORDING_INIT_TEMPLATES.join(', ')}`);
|
|
@@ -599,8 +591,21 @@ export async function runDemoCli(argv = [], { cwd = process.cwd(), repoRoot } =
|
|
|
599
591
|
}
|
|
600
592
|
|
|
601
593
|
if (options.command === 'init') {
|
|
594
|
+
if (options.fromProject) {
|
|
595
|
+
if (!options.name) {
|
|
596
|
+
console.error('Usage: construct demo init <name> --from-project');
|
|
597
|
+
process.exit(1);
|
|
598
|
+
}
|
|
599
|
+
const result = scaffoldProjectDemo(options.name, { cwd, repoRoot });
|
|
600
|
+
if (!result.ok) {
|
|
601
|
+
console.error(result.message);
|
|
602
|
+
process.exit(1);
|
|
603
|
+
}
|
|
604
|
+
console.log(result.message);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
602
607
|
if (!options.name) {
|
|
603
|
-
console.error(`Usage: construct demo init <name> [--from=${RECORDING_INIT_TEMPLATES.join('|')}]`);
|
|
608
|
+
console.error(`Usage: construct demo init <name> [--from=${RECORDING_INIT_TEMPLATES.join('|')}] | --from-project`);
|
|
604
609
|
process.exit(1);
|
|
605
610
|
}
|
|
606
611
|
const result = initProjectRecording(options.name, { cwd, from: options.from, repoRoot });
|
|
@@ -612,28 +617,45 @@ export async function runDemoCli(argv = [], { cwd = process.cwd(), repoRoot } =
|
|
|
612
617
|
return;
|
|
613
618
|
}
|
|
614
619
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
const
|
|
623
|
-
|
|
620
|
+
if (options.command === 'tour') {
|
|
621
|
+
const scripts = listDemoScripts({ cwd, repoRoot });
|
|
622
|
+
const tourName = options.name || scripts[0] || null;
|
|
623
|
+
if (!tourName) {
|
|
624
|
+
console.error('No demo scripts found. Add one under templates/demos/scripts/ or .cx/demos/scripts/.');
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
const script = loadDemoScript(tourName, { cwd, repoRoot });
|
|
628
|
+
if (!script) {
|
|
629
|
+
console.error(`Unknown demo: ${tourName}. Run \`construct demo list\` to see available scripts.`);
|
|
630
|
+
process.exit(1);
|
|
631
|
+
}
|
|
632
|
+
// A non-TTY stdout (pipe, file, CI) advances on its own even without --skip-input.
|
|
633
|
+
const skipInput = options.skipInput || !process.stdout.isTTY;
|
|
634
|
+
const result = await renderTour({
|
|
635
|
+
script,
|
|
636
|
+
output: process.stdout,
|
|
637
|
+
input: process.stdin,
|
|
638
|
+
env: process.env,
|
|
639
|
+
accessible: options.accessible,
|
|
640
|
+
skipInput,
|
|
641
|
+
});
|
|
624
642
|
if (!result.ok) {
|
|
625
643
|
console.error(result.message);
|
|
626
|
-
process.exit(
|
|
644
|
+
process.exit(1);
|
|
627
645
|
}
|
|
628
|
-
console.log(result.message);
|
|
629
|
-
for (const v of result.videos || []) console.log(` ${v}`);
|
|
630
646
|
return;
|
|
631
647
|
}
|
|
632
648
|
|
|
649
|
+
const demoName = options.command === 'record' ? options.name : options.name;
|
|
650
|
+
if (!demoName) {
|
|
651
|
+
printHelp();
|
|
652
|
+
process.exit(1);
|
|
653
|
+
}
|
|
654
|
+
|
|
633
655
|
const preferPlaywright = options.surface === 'playwright'
|
|
634
656
|
|| (options.command === 'record' && loadDemoRecording(demoName, { cwd, repoRoot })?.engine === 'playwright');
|
|
635
657
|
const useTape = options.surface === 'tape'
|
|
636
|
-
|| (options.command === 'record' && !preferPlaywright
|
|
658
|
+
|| (options.command === 'record' && !preferPlaywright);
|
|
637
659
|
if (useTape || (options.command === 'record' && preferPlaywright)) {
|
|
638
660
|
const result = runDemoRecord(demoName, {
|
|
639
661
|
cwd,
|
|
@@ -669,11 +691,7 @@ export async function runDemoCli(argv = [], { cwd = process.cwd(), repoRoot } =
|
|
|
669
691
|
format: options.format,
|
|
670
692
|
out: options.out,
|
|
671
693
|
sourceOnly: options.sourceOnly,
|
|
672
|
-
model: options.model,
|
|
673
|
-
plain: options.plain,
|
|
674
694
|
accessible: options.accessible,
|
|
675
|
-
web: options.web,
|
|
676
|
-
free: options.free,
|
|
677
695
|
});
|
|
678
696
|
|
|
679
697
|
if (!result.ok) {
|
|
@@ -684,9 +702,6 @@ export async function runDemoCli(argv = [], { cwd = process.cwd(), repoRoot } =
|
|
|
684
702
|
if (result.notices?.length) {
|
|
685
703
|
for (const n of result.notices) console.error(`note: ${n}`);
|
|
686
704
|
}
|
|
687
|
-
if (result.surface === 'chat' || result.surface === 'web') {
|
|
688
|
-
return;
|
|
689
|
-
}
|
|
690
705
|
if (result.artifactPath) {
|
|
691
706
|
console.log(`Demo (${demoName}) [${result.surface}] recorded to:`);
|
|
692
707
|
console.log(` ${result.artifactPath}`);
|
package/lib/diagram-export.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
14
14
|
|
|
15
15
|
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
16
16
|
const ICON_DIR = path.join(REPO_ROOT, 'templates', 'distribution', 'icons');
|
|
17
|
+
const MERMAID_PPTR_CONFIG = path.join(REPO_ROOT, 'templates', 'distribution', 'mermaid-puppeteer.json');
|
|
17
18
|
|
|
18
19
|
// Monochrome diagram palette, mirroring the ink ramp in construct-brand.typ so
|
|
19
20
|
// embedded diagrams and surrounding body share one design language.
|
|
@@ -36,9 +37,9 @@ export const DISTRIBUTION_D2_SCALE = '0.72';
|
|
|
36
37
|
export const DISTRIBUTION_D2_SKETCH = '1';
|
|
37
38
|
export const DISTRIBUTION_D2_FONT_SIZE = '14';
|
|
38
39
|
export const DISTRIBUTION_MERMAID_MIME = 'image/png';
|
|
39
|
-
export const DISTRIBUTION_MERMAID_WIDTH = '
|
|
40
|
-
export const DISTRIBUTION_MERMAID_SCALE = '
|
|
41
|
-
export const DISTRIBUTION_FIGURE_MAX_WIDTH = '
|
|
40
|
+
export const DISTRIBUTION_MERMAID_WIDTH = '2400';
|
|
41
|
+
export const DISTRIBUTION_MERMAID_SCALE = '2';
|
|
42
|
+
export const DISTRIBUTION_FIGURE_MAX_WIDTH = '92%';
|
|
42
43
|
|
|
43
44
|
// Handwritten font for Mermaid labels so its hand-drawn look matches D2 sketch
|
|
44
45
|
// mode. Caveat ships bundled (templates/distribution/fonts/handwritten) and is
|
|
@@ -51,6 +52,9 @@ const MERMAID_INIT = `%%{init: {
|
|
|
51
52
|
'theme': 'base',
|
|
52
53
|
'look': 'handDrawn',
|
|
53
54
|
'fontFamily': '${MERMAID_FONT}',
|
|
55
|
+
'flowchart': {
|
|
56
|
+
'htmlLabels': false
|
|
57
|
+
},
|
|
54
58
|
'themeVariables': {
|
|
55
59
|
'fontFamily': '${MERMAID_FONT}',
|
|
56
60
|
'fontSize': '19px',
|
|
@@ -86,17 +90,8 @@ const MERMAID_INIT = `%%{init: {
|
|
|
86
90
|
}
|
|
87
91
|
}}%%`;
|
|
88
92
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
92
|
-
'/usr/bin/google-chrome',
|
|
93
|
-
'/usr/bin/chromium',
|
|
94
|
-
'/usr/bin/chromium-browser',
|
|
95
|
-
];
|
|
96
|
-
|
|
97
|
-
export function resolvePuppeteerExecutable(env = process.env) {
|
|
98
|
-
if (env.PUPPETEER_EXECUTABLE_PATH) return env.PUPPETEER_EXECUTABLE_PATH;
|
|
99
|
-
for (const candidate of CHROME_CANDIDATES) {
|
|
93
|
+
function firstExistingPath(paths = []) {
|
|
94
|
+
for (const candidate of paths) {
|
|
100
95
|
try {
|
|
101
96
|
if (fs.existsSync(candidate)) return candidate;
|
|
102
97
|
} catch {
|
|
@@ -106,6 +101,59 @@ export function resolvePuppeteerExecutable(env = process.env) {
|
|
|
106
101
|
return null;
|
|
107
102
|
}
|
|
108
103
|
|
|
104
|
+
function playwrightChromeCandidates(home = process.env.HOME || '') {
|
|
105
|
+
if (!home) return [];
|
|
106
|
+
const cacheRoot = path.join(home, 'Library', 'Caches', 'ms-playwright');
|
|
107
|
+
let entries = [];
|
|
108
|
+
try {
|
|
109
|
+
entries = fs.readdirSync(cacheRoot, { withFileTypes: true });
|
|
110
|
+
} catch {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
entries = entries
|
|
114
|
+
.filter((entry) => entry.isDirectory())
|
|
115
|
+
.sort((a, b) => {
|
|
116
|
+
const aHeadless = a.name.startsWith('chromium_headless_shell-') ? 0 : 1;
|
|
117
|
+
const bHeadless = b.name.startsWith('chromium_headless_shell-') ? 0 : 1;
|
|
118
|
+
if (aHeadless !== bHeadless) return aHeadless - bHeadless;
|
|
119
|
+
const aVer = Number((a.name.match(/-(\d+)/) || [])[1] || 0);
|
|
120
|
+
const bVer = Number((b.name.match(/-(\d+)/) || [])[1] || 0);
|
|
121
|
+
return bVer - aVer;
|
|
122
|
+
});
|
|
123
|
+
const candidates = [];
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
const base = path.join(cacheRoot, entry.name);
|
|
126
|
+
if (entry.name.startsWith('chromium_headless_shell-')) {
|
|
127
|
+
candidates.push(path.join(base, 'chrome-headless-shell-mac-arm64', 'chrome-headless-shell'));
|
|
128
|
+
candidates.push(path.join(base, 'chrome-headless-shell-mac', 'chrome-headless-shell'));
|
|
129
|
+
}
|
|
130
|
+
if (entry.name.startsWith('chromium-')) {
|
|
131
|
+
candidates.push(path.join(base, 'chrome-mac-arm64', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'));
|
|
132
|
+
candidates.push(path.join(base, 'chrome-mac-arm64', 'chrome'));
|
|
133
|
+
candidates.push(path.join(base, 'chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'));
|
|
134
|
+
candidates.push(path.join(base, 'chrome-mac', 'chrome'));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return candidates;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function chromeCandidates(env = process.env) {
|
|
141
|
+
return [
|
|
142
|
+
...playwrightChromeCandidates(env.HOME),
|
|
143
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
144
|
+
'/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
|
|
145
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
146
|
+
'/usr/bin/google-chrome',
|
|
147
|
+
'/usr/bin/chromium',
|
|
148
|
+
'/usr/bin/chromium-browser',
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function resolvePuppeteerExecutable(env = process.env) {
|
|
153
|
+
if (env.PUPPETEER_EXECUTABLE_PATH) return env.PUPPETEER_EXECUTABLE_PATH;
|
|
154
|
+
return firstExistingPath(chromeCandidates(env));
|
|
155
|
+
}
|
|
156
|
+
|
|
109
157
|
export function injectMermaidBrandTheme(code) {
|
|
110
158
|
const body = String(code || '').trim();
|
|
111
159
|
if (!body) return body;
|
|
@@ -170,6 +218,7 @@ export function buildDistributionDiagramEnv(baseEnv = process.env) {
|
|
|
170
218
|
CONSTRUCT_MERMAID_WIDTH: DISTRIBUTION_MERMAID_WIDTH,
|
|
171
219
|
CONSTRUCT_MERMAID_SCALE: DISTRIBUTION_MERMAID_SCALE,
|
|
172
220
|
};
|
|
221
|
+
if (fs.existsSync(MERMAID_PPTR_CONFIG)) env.CONSTRUCT_MERMAID_PPTR_CONFIG = MERMAID_PPTR_CONFIG;
|
|
173
222
|
const chrome = resolvePuppeteerExecutable(baseEnv);
|
|
174
223
|
if (chrome) env.PUPPETEER_EXECUTABLE_PATH = chrome;
|
|
175
224
|
return env;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* lib/doctor/source-checkout.mjs — distinguish Construct source tree vs npm install.
|
|
3
3
|
*
|
|
4
4
|
* `isConstructPackageRepo` is true for both checkouts and consumer installs.
|
|
5
|
-
* Source-only doctor checks (
|
|
6
|
-
*
|
|
5
|
+
* Source-only doctor checks (certification role cards) should run only when this
|
|
6
|
+
* returns true.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import fs from 'node:fs';
|
|
@@ -11,6 +11,5 @@ import path from 'node:path';
|
|
|
11
11
|
|
|
12
12
|
export function isConstructSourceCheckout(rootDir) {
|
|
13
13
|
const root = path.resolve(rootDir);
|
|
14
|
-
return fs.existsSync(path.join(root, '
|
|
15
|
-
|| fs.existsSync(path.join(root, 'tests', 'certification', 'specialists'));
|
|
14
|
+
return fs.existsSync(path.join(root, 'tests', 'certification', 'specialists'));
|
|
16
15
|
}
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* 2. MCP registration ↔ impl: every `name === '<tool>'` branch in the MCP
|
|
8
8
|
* dispatch resolves to an imported function, and every exported tool
|
|
9
9
|
* function in lib/mcp/tools/*.mjs is wired into the dispatch.
|
|
10
|
-
* 3. Roles ↔ registry: every persona key in specialists/
|
|
11
|
-
* resolves to a persona or agent name in specialists/
|
|
10
|
+
* 3. Roles ↔ registry: every persona key in specialists/org
|
|
11
|
+
* resolves to a persona or agent name in specialists/org.
|
|
12
12
|
* 4. Persona promptFiles: every registry.personas[].promptFile and
|
|
13
13
|
* registry.agents[].promptFile exists on disk.
|
|
14
|
-
* 5. Contracts ↔ schemas: specialists/
|
|
14
|
+
* 5. Contracts ↔ schemas: specialists/org passes validateContractsFile
|
|
15
15
|
* (cross-file refs, producer/consumer names, schema paths).
|
|
16
16
|
*
|
|
17
17
|
* Each violation produces an audit record. Blocking severity escalates to a
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
23
|
+
import { loadRegistry } from '../../registry/loader.mjs';
|
|
23
24
|
import { join, dirname, resolve } from 'node:path';
|
|
24
25
|
import os from 'node:os';
|
|
25
26
|
import { fileURLToPath } from 'node:url';
|
|
@@ -89,6 +90,10 @@ export async function runAllChecks({ repoRoot = REPO_ROOT } = {}) {
|
|
|
89
90
|
const hosts = checkHostSurfaces({ repoRoot });
|
|
90
91
|
collect(hosts, findings, passed, 'host-surfaces');
|
|
91
92
|
|
|
93
|
+
const { runCredentialParityChecks } = await import('./credential-parity.mjs');
|
|
94
|
+
const credentials = await runCredentialParityChecks({ rootDir: repoRoot });
|
|
95
|
+
collect(credentials, findings, passed, 'credential-parity');
|
|
96
|
+
|
|
92
97
|
return { findings, passed };
|
|
93
98
|
}
|
|
94
99
|
|
|
@@ -219,22 +224,75 @@ function camelToSnake(s) {
|
|
|
219
224
|
return s.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
|
|
220
225
|
}
|
|
221
226
|
|
|
227
|
+
function readRegistrySnapshot(repoRoot) {
|
|
228
|
+
const registryPath = join(repoRoot, 'specialists', 'org');
|
|
229
|
+
if (!existsSync(registryPath)) return null;
|
|
230
|
+
const stat = statSync(registryPath);
|
|
231
|
+
if (stat.isDirectory()) return loadRegistry({ rootDir: repoRoot });
|
|
232
|
+
return JSON.parse(readFileSync(registryPath, 'utf8'));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function personaKeysFromRegistry(registry) {
|
|
236
|
+
const keys = new Set();
|
|
237
|
+
|
|
238
|
+
for (const [specId, spec] of Object.entries(registry?.specialists || {})) {
|
|
239
|
+
if (specId) keys.add(specId.replace(/^cx-/, ''));
|
|
240
|
+
if (spec?.name) keys.add(String(spec.name).replace(/^cx-/, ''));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (Array.isArray(registry?.specialists)) {
|
|
244
|
+
for (const spec of registry.specialists) {
|
|
245
|
+
if (spec?.id) keys.add(String(spec.id).replace(/^cx-/, ''));
|
|
246
|
+
if (spec?.name) keys.add(String(spec.name).replace(/^cx-/, ''));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (registry?.orchestrator?.id) keys.add(String(registry.orchestrator.id).replace(/^cx-/, ''));
|
|
251
|
+
if (registry?.orchestrator?.name) keys.add(String(registry.orchestrator.name).replace(/^cx-/, ''));
|
|
252
|
+
|
|
253
|
+
return [...keys].sort();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function personaOwnersFromRegistry(registry) {
|
|
257
|
+
const owners = new Map();
|
|
258
|
+
|
|
259
|
+
function addOwner(rawId, owner) {
|
|
260
|
+
if (!rawId) return;
|
|
261
|
+
const personaId = String(rawId).replace(/^cx-/, '');
|
|
262
|
+
if (!owners.has(personaId)) owners.set(personaId, new Set());
|
|
263
|
+
owners.get(personaId).add(owner);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
for (const [specId, spec] of Object.entries(registry?.specialists || {})) {
|
|
267
|
+
addOwner(specId, `specialist:${specId}`);
|
|
268
|
+
addOwner(spec?.name, `specialist:${specId}:name`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (Array.isArray(registry?.specialists)) {
|
|
272
|
+
for (const spec of registry.specialists) {
|
|
273
|
+
const owner = `specialist:${spec?.id || spec?.name || 'unknown'}`;
|
|
274
|
+
addOwner(spec?.id, owner);
|
|
275
|
+
addOwner(spec?.name, `${owner}:name`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
addOwner(registry?.orchestrator?.id, 'orchestrator:id');
|
|
280
|
+
addOwner(registry?.orchestrator?.name, 'orchestrator:name');
|
|
281
|
+
return owners;
|
|
282
|
+
}
|
|
283
|
+
|
|
222
284
|
// ── Check 3: roles ↔ registry ──────────────────────────────────────────────
|
|
223
285
|
|
|
224
286
|
function checkRolesDrift({ repoRoot }) {
|
|
225
287
|
const violations = [];
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (!existsSync(manifestsPath) || !existsSync(registryPath)) {
|
|
230
|
-
return { summary: 'role manifests or registry missing', violations: [] };
|
|
288
|
+
if (!existsSync(join(repoRoot, 'specialists', 'org'))) {
|
|
289
|
+
return { summary: 'registry missing', violations: [] };
|
|
231
290
|
}
|
|
232
291
|
|
|
233
|
-
const
|
|
234
|
-
const registry = JSON.parse(readFileSync(registryPath, 'utf8'));
|
|
292
|
+
const registry = readRegistrySnapshot(repoRoot);
|
|
235
293
|
|
|
236
294
|
const known = new Set();
|
|
237
|
-
for (const s of registry.specialists ||
|
|
295
|
+
for (const s of Object.values(registry.specialists || {})) {
|
|
238
296
|
if (s?.name) { known.add(s.name); known.add(`cx-${s.name}`); }
|
|
239
297
|
}
|
|
240
298
|
if (registry.orchestrator?.name) {
|
|
@@ -242,35 +300,43 @@ function checkRolesDrift({ repoRoot }) {
|
|
|
242
300
|
known.add(`cx-${registry.orchestrator.name}`);
|
|
243
301
|
}
|
|
244
302
|
|
|
245
|
-
const personaKeys =
|
|
303
|
+
const personaKeys = personaKeysFromRegistry(registry);
|
|
304
|
+
const personaOwners = personaOwnersFromRegistry(registry);
|
|
246
305
|
for (const key of personaKeys) {
|
|
247
|
-
if (key === 'version' || key === 'description' || key === 'schemaVersion') continue;
|
|
248
306
|
if (!known.has(key) && !known.has(`cx-${key}`)) {
|
|
249
307
|
violations.push({
|
|
250
308
|
category: 'roles-drift',
|
|
251
309
|
severity: 'warning',
|
|
252
310
|
target: key,
|
|
253
|
-
summary: `role
|
|
311
|
+
summary: `role manifest '${key}' does not resolve to a registry persona/agent`,
|
|
254
312
|
});
|
|
255
313
|
}
|
|
256
314
|
}
|
|
257
315
|
|
|
258
|
-
|
|
316
|
+
for (const [personaId, owners] of personaOwners.entries()) {
|
|
317
|
+
if (owners.size <= 1) continue;
|
|
318
|
+
violations.push({
|
|
319
|
+
category: 'roles-drift',
|
|
320
|
+
severity: 'warning',
|
|
321
|
+
target: personaId,
|
|
322
|
+
summary: `role manifest '${personaId}' is ambiguous after normalization: ${[...owners].join(', ')}`,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return { summary: `roles: ${personaKeys.length} personas, ${violations.length} drift`, violations };
|
|
259
327
|
}
|
|
260
328
|
|
|
261
329
|
// ── Check 4: persona promptFile existence ──────────────────────────────────
|
|
262
330
|
|
|
263
331
|
function checkPersonaPrompts({ repoRoot }) {
|
|
264
332
|
const violations = [];
|
|
265
|
-
const
|
|
266
|
-
if (!
|
|
267
|
-
|
|
268
|
-
const registry = JSON.parse(readFileSync(registryPath, 'utf8'));
|
|
333
|
+
const registry = readRegistrySnapshot(repoRoot);
|
|
334
|
+
if (!registry) return { summary: 'registry missing', violations: [] };
|
|
269
335
|
let checked = 0;
|
|
270
336
|
|
|
271
337
|
for (const list of [
|
|
272
338
|
registry.orchestrator ? [registry.orchestrator] : [],
|
|
273
|
-
registry.specialists ||
|
|
339
|
+
Object.values(registry.specialists || {}),
|
|
274
340
|
]) {
|
|
275
341
|
for (const entry of list) {
|
|
276
342
|
const ref = entry?.promptFile;
|
|
@@ -297,7 +363,9 @@ async function checkContractsDrift({ repoRoot }) {
|
|
|
297
363
|
const violations = [];
|
|
298
364
|
try {
|
|
299
365
|
const { validateContractsFile } = await import('../../contracts/validate.mjs');
|
|
300
|
-
const
|
|
366
|
+
const legacyContractsPath = join(repoRoot, 'specialists', 'contracts.json');
|
|
367
|
+
const opts = existsSync(legacyContractsPath) ? { contractsPath: legacyContractsPath, repoRoot } : { repoRoot };
|
|
368
|
+
const result = validateContractsFile(opts);
|
|
301
369
|
if (!result.ok) {
|
|
302
370
|
for (const err of result.errors) {
|
|
303
371
|
violations.push({
|
|
@@ -327,7 +395,7 @@ function getVSCodeSettingsDir(homeDir) {
|
|
|
327
395
|
return join(appData, 'Code', 'User');
|
|
328
396
|
}
|
|
329
397
|
|
|
330
|
-
function checkHostSurfaces({ repoRoot }) {
|
|
398
|
+
function checkHostSurfaces({ repoRoot: _repoRoot }) {
|
|
331
399
|
const violations = [];
|
|
332
400
|
const homeDir = os.homedir();
|
|
333
401
|
const surfaces = [
|