@geraldmaron/construct 1.2.3 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -10
- package/bin/construct +235 -209
- package/bin/construct-postinstall.mjs +0 -21
- package/commands/work/optimize-prompts.md +1 -1
- package/examples/distribution/sources/adr.md +2 -2
- package/examples/seed-observations/decisions.md +1 -1
- package/lib/artifact-loop-core.mjs +412 -0
- package/lib/artifact-manifest.mjs +6 -0
- package/lib/artifact-release-gate.mjs +115 -49
- package/lib/audit-rules.mjs +2 -4
- package/lib/audit-skills.mjs +32 -20
- package/lib/audit-specialists.mjs +44 -26
- package/lib/auto-docs.mjs +6 -5
- package/lib/brand-prose.mjs +3 -3
- package/lib/brand-tokens.mjs +2 -2
- package/lib/certification/artifact-fixtures.mjs +4 -1
- package/lib/certification/demo-parity.mjs +6 -32
- package/lib/certification/real-llm-scenarios.mjs +58 -13
- package/lib/certification/role-cards.mjs +7 -6
- package/lib/certification/role-overlays.mjs +4 -4
- package/lib/certification/runner.mjs +9 -2
- package/lib/certification/skill-inventory.mjs +34 -22
- package/lib/certification/skill-scenarios.mjs +21 -8
- package/lib/certification/specialist-contracts.mjs +4 -3
- package/lib/certification/specialist-scenarios.mjs +7 -6
- package/lib/certification/status.mjs +5 -4
- package/lib/cli-commands.mjs +55 -54
- package/lib/comment-lint.mjs +1 -5
- package/lib/completions.mjs +20 -2
- package/lib/config/legacy-config-migration.mjs +90 -24
- package/lib/config/schema.mjs +5 -5
- package/lib/config/source-targets.mjs +80 -0
- package/lib/contracts/validate.mjs +119 -51
- package/lib/decisions/golden.mjs +3 -2
- package/lib/decisions/registry.mjs +11 -6
- package/lib/demo-project.mjs +188 -0
- package/lib/demo-recording.mjs +9 -34
- package/lib/demo-script.mjs +2 -2
- package/lib/demo-surface.mjs +13 -131
- package/lib/demo-tour-renderer.mjs +92 -0
- package/lib/demo.mjs +69 -54
- package/lib/diagram-export.mjs +63 -14
- package/lib/doctor/cli.mjs +12 -2
- package/lib/doctor/source-checkout.mjs +3 -4
- package/lib/doctor/watchers/consistency.mjs +148 -43
- package/lib/doctor/watchers/credential-parity.mjs +97 -0
- package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
- package/lib/doctor/watchers/service-health.mjs +4 -32
- package/lib/document-export.mjs +73 -9
- package/lib/document-extract.mjs +10 -198
- package/lib/embed/artifact.mjs +2 -3
- package/lib/embed/cli.mjs +1 -1
- package/lib/embed/config.mjs +1 -2
- package/lib/embed/customer-profiles.mjs +1 -1
- package/lib/embed/daemon.mjs +5 -6
- package/lib/embed/demand-fetch.mjs +114 -2
- package/lib/embed/docs-lifecycle.mjs +9 -5
- package/lib/embed/intake-metrics.mjs +2 -2
- package/lib/embed/notifications.mjs +2 -3
- package/lib/embed/recommendation-store.mjs +25 -22
- package/lib/embed/role-framing.mjs +4 -9
- package/lib/embedded-contract/capability.mjs +4 -2
- package/lib/embedded-contract/contract-version.mjs +1 -1
- package/lib/env-config.mjs +2 -2
- package/lib/flavors/loader.mjs +21 -19
- package/lib/graph/build-from-registry.mjs +31 -7
- package/lib/graph/staleness.mjs +1 -1
- package/lib/headhunt.mjs +82 -35
- package/lib/hooks/agent-tracker.mjs +2 -2
- package/lib/hooks/config-protection.mjs +1 -1
- package/lib/hooks/registry-sync.mjs +3 -3
- package/lib/host-capabilities.mjs +5 -5
- package/lib/improvement/controller.mjs +2 -2
- package/lib/init-unified.mjs +19 -21
- package/lib/intake/classify.mjs +26 -0
- package/lib/intake/daemon.mjs +2 -3
- package/lib/intake/prepare.mjs +10 -3
- package/lib/intake/session-prelude.mjs +1 -1
- package/lib/intake/tables/creative.mjs +9 -9
- package/lib/intake/tables/operations.mjs +4 -4
- package/lib/intake/tables/research.mjs +2 -2
- package/lib/integrations/intake-integrations.mjs +9 -10
- package/lib/knowledge/research-store.mjs +2 -2
- package/lib/mcp/server.mjs +104 -61
- package/lib/mcp/tool-budget.mjs +1 -46
- package/lib/mcp/tool-recovery.mjs +44 -0
- package/lib/mcp/tools/artifact-author.mjs +36 -0
- package/lib/mcp/tools/find-tool.mjs +86 -0
- package/lib/mcp/tools/orchestration-run.mjs +107 -69
- package/lib/mcp/tools/project.mjs +4 -3
- package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
- package/lib/mcp/tools/skills.mjs +39 -22
- package/lib/mcp/tools/telemetry.mjs +2 -1
- package/lib/mcp/tools/workflow.mjs +1 -1
- package/lib/mcp-platform-config.mjs +7 -5
- package/lib/migrations/index.mjs +4 -2
- package/lib/migrations/v2-unified-registry.mjs +35 -0
- package/lib/model-router.mjs +203 -36
- package/lib/models/catalog.mjs +25 -9
- package/lib/models/execution-capability-profile.mjs +3 -3
- package/lib/models/execution-policy.mjs +7 -6
- package/lib/models/provider-poll.mjs +18 -4
- package/lib/opencode-config.mjs +56 -1
- package/lib/opencode-runtime-plugin.mjs +13 -10
- package/lib/oracle/artifact-gate.mjs +14 -4
- package/lib/oracle/cli.mjs +2 -0
- package/lib/oracle/dispatch.mjs +18 -1
- package/lib/oracle/execute.mjs +39 -7
- package/lib/oracle/index.mjs +1 -1
- package/lib/oracle/org-graph.mjs +10 -5
- package/lib/oracle/policy.mjs +6 -0
- package/lib/oracle/read-model.mjs +103 -6
- package/lib/oracle/reconcile.mjs +31 -4
- package/lib/oracle/remediation-dispatch.mjs +53 -0
- package/lib/oracle/routing.mjs +5 -0
- package/lib/oracle/synthesize.mjs +80 -3
- package/lib/orchestration/routing-tables.mjs +44 -10
- package/lib/orchestration/runtime.mjs +2 -0
- package/lib/orchestration/worker.mjs +1 -1
- package/lib/orchestration-policy.mjs +351 -62
- package/lib/overrides/resolver.mjs +1 -12
- package/lib/parity.mjs +37 -11
- package/lib/policy/engine.mjs +80 -15
- package/lib/prompt-composer.js +51 -15
- package/lib/prompt-metadata.mjs +3 -2
- package/lib/providers/connection-probe.mjs +58 -0
- package/lib/providers/copilot-auth.mjs +1 -1
- package/lib/providers/credential-bootstrap.mjs +1 -1
- package/lib/providers/op-run.mjs +3 -4
- package/lib/providers/secret-resolver.mjs +31 -0
- package/lib/publish-tooling.mjs +3 -11
- package/lib/publish.mjs +12 -29
- package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
- package/lib/reflect.mjs +2 -2
- package/lib/registry/agent-manifest.mjs +190 -0
- package/lib/registry/assemble.mjs +133 -0
- package/lib/registry/catalog.mjs +171 -0
- package/lib/registry/cli.mjs +590 -6
- package/lib/registry/consolidation.mjs +5 -4
- package/lib/registry/docs-sync.mjs +67 -0
- package/lib/registry/loader.mjs +147 -0
- package/lib/registry/org-io.mjs +76 -0
- package/lib/registry/retired-paths.mjs +71 -0
- package/lib/registry/surface-map.mjs +5 -7
- package/lib/registry/validator.mjs +438 -0
- package/lib/research-execution-policy.mjs +173 -0
- package/lib/roles/catalog.mjs +4 -9
- package/lib/roles/fence.mjs +107 -1
- package/lib/roles/flavor-bindings.mjs +68 -0
- package/lib/roles/gateway.mjs +140 -29
- package/lib/roles/manifest.mjs +22 -13
- package/lib/roles/router.mjs +1 -1
- package/lib/scopes/enrich.mjs +67 -0
- package/lib/scopes/hooks.mjs +12 -0
- package/lib/{profiles → scopes}/lifecycle.mjs +70 -70
- package/lib/scopes/loader.mjs +104 -0
- package/lib/scopes/rebrand.mjs +32 -0
- package/lib/scopes/research-profile.mjs +16 -0
- package/lib/scopes/teams.mjs +98 -0
- package/lib/service-manager.mjs +1 -117
- package/lib/setup-prompts.mjs +1 -1
- package/lib/setup.mjs +6 -26
- package/lib/skills/composition-graph.mjs +98 -0
- package/lib/skills/router.mjs +80 -0
- package/lib/specialist-contracts.mjs +17 -18
- package/lib/specialists/postconditions.mjs +2 -2
- package/lib/specialists/prompt-schema.mjs +6 -5
- package/lib/specialists/roster.mjs +7 -12
- package/lib/specialists/schema.mjs +9 -6
- package/lib/status.mjs +25 -90
- package/lib/storage/file-lock.mjs +6 -3
- package/lib/telemetry/skill-calls.mjs +1 -1
- package/lib/templates/doc-presentation.mjs +63 -0
- package/lib/templates/visual-requirements.mjs +35 -2
- package/lib/term-format.mjs +107 -2
- package/lib/test-env-setup.mjs +21 -0
- package/lib/ui/components.mjs +42 -0
- package/lib/ui/glyphs.mjs +58 -0
- package/lib/ui/links.mjs +115 -0
- package/lib/ui/theme.mjs +108 -0
- package/lib/uninstall/uninstall.mjs +2 -1
- package/lib/validator.mjs +45 -8
- package/lib/validators/skill-effectiveness.mjs +174 -0
- package/lib/validators/skills.mjs +1 -1
- package/package.json +12 -8
- package/personas/construct.md +12 -3
- package/platforms/claude/CLAUDE.md +1 -1
- package/rules/common/beads-hygiene.md +52 -0
- package/rules/common/neurodivergent-output.md +4 -7
- package/rules/common/research.md +2 -0
- package/scripts/sync-specialists.mjs +269 -51
- package/skills/ai/prompt-optimizer.md +3 -3
- package/skills/brand/output-vibe.md +48 -0
- package/skills/docs/adr-workflow.md +3 -0
- package/skills/docs/backlog-proposal-workflow.md +3 -0
- package/skills/docs/codebase-research-workflow.md +5 -2
- package/skills/docs/customer-profile-workflow.md +3 -0
- package/skills/docs/document-ingest-workflow.md +3 -0
- package/skills/docs/evidence-ingest-workflow.md +4 -1
- package/skills/docs/init-project.md +1 -1
- package/skills/docs/prd-workflow.md +1 -1
- package/skills/docs/prfaq-workflow.md +3 -0
- package/skills/docs/product-intelligence-workflow.md +4 -1
- package/skills/docs/product-signal-workflow.md +3 -0
- package/skills/docs/research-workflow.md +13 -6
- package/skills/docs/runbook-workflow.md +3 -0
- package/skills/docs/strategy-workflow.md +3 -0
- package/skills/docs/user-research-workflow.md +6 -3
- package/skills/operating/orchestration-reference.md +1 -1
- package/skills/roles/{engineer.ai.md → ai-engineer.md} +2 -2
- package/skills/roles/architect.ai-systems.md +1 -1
- package/skills/roles/architect.data.md +1 -1
- package/skills/roles/architect.enterprise.md +1 -1
- package/skills/roles/architect.integration.md +1 -1
- package/skills/roles/architect.md +1 -1
- package/skills/roles/architect.platform.md +1 -1
- package/skills/roles/{product-manager.business-strategy.md → business-strategist.md} +3 -3
- package/skills/roles/data-analyst.experiment.md +1 -1
- package/skills/roles/data-analyst.md +1 -1
- package/skills/roles/data-analyst.product-intelligence.md +1 -1
- package/skills/roles/data-analyst.product.md +1 -1
- package/skills/roles/data-analyst.telemetry.md +1 -1
- package/skills/roles/{engineer.data.md → data-engineer.md} +2 -2
- package/skills/roles/data-engineer.pipeline.md +2 -2
- package/skills/roles/data-engineer.vector-retrieval.md +2 -2
- package/skills/roles/data-engineer.warehouse.md +2 -2
- package/skills/roles/debugger.md +1 -1
- package/skills/roles/designer.accessibility.md +1 -1
- package/skills/roles/designer.md +1 -1
- package/skills/roles/{reviewer.devil-advocate.md → devil-advocate.md} +2 -2
- package/skills/roles/{operator.docs.md → docs-keeper.md} +3 -3
- package/skills/roles/engineer.md +3 -7
- package/skills/roles/{reviewer.evaluator.md → evaluator.md} +2 -2
- package/skills/roles/{researcher.explorer.md → explorer.md} +2 -2
- package/skills/roles/{operator.md → operations.md} +2 -5
- package/skills/roles/orchestrator.md +1 -1
- package/skills/roles/{engineer.platform.md → platform-engineer.md} +2 -2
- package/skills/roles/product-manager.ai-product.md +1 -1
- package/skills/roles/product-manager.enterprise.md +1 -1
- package/skills/roles/product-manager.growth.md +1 -2
- package/skills/roles/product-manager.md +1 -2
- package/skills/roles/product-manager.platform.md +1 -1
- package/skills/roles/product-manager.product.md +1 -1
- package/skills/roles/qa.ai-eval.md +1 -1
- package/skills/roles/qa.api-contract.md +1 -1
- package/skills/roles/qa.data-pipeline.md +1 -1
- package/skills/roles/qa.md +1 -1
- package/skills/roles/qa.web-ui.md +1 -1
- package/skills/roles/{operator.release.md → release-manager.md} +3 -3
- package/skills/roles/researcher.md +1 -1
- package/skills/roles/reviewer.md +1 -1
- package/skills/roles/security.ai.md +1 -1
- package/skills/roles/security.appsec.md +1 -1
- package/skills/roles/security.cloud.md +1 -1
- package/skills/roles/security.legal-compliance.md +1 -1
- package/skills/roles/security.md +1 -1
- package/skills/roles/security.privacy.md +1 -1
- package/skills/roles/security.supply-chain.md +1 -1
- package/skills/roles/{operator.sre.md → sre.md} +3 -3
- package/skills/roles/{qa.test-automation.md → test-automation.md} +2 -2
- package/skills/roles/{reviewer.trace.md → trace-reviewer.md} +2 -2
- package/skills/roles/{researcher.ux.md → ux-researcher.md} +2 -2
- package/skills/routing.json +18 -0
- package/skills/routing.md +1 -1
- package/specialists/artifact-manifest.json +2 -1
- package/specialists/audit-enrichments.json +14 -14
- package/specialists/org/contracts/accessibility-to-qa.json +37 -0
- package/specialists/org/contracts/any-to-business-strategist.json +57 -0
- package/specialists/org/contracts/any-to-debugger.json +38 -0
- package/specialists/org/contracts/any-to-designer.json +33 -0
- package/specialists/org/contracts/any-to-docs-keeper.json +34 -0
- package/specialists/org/contracts/any-to-explorer.json +39 -0
- package/specialists/org/contracts/any-to-sre-incident.json +33 -0
- package/specialists/org/contracts/any-to-trace-reviewer.json +32 -0
- package/specialists/org/contracts/architect-to-ai-engineer.json +32 -0
- package/specialists/org/contracts/architect-to-data-engineer.json +52 -0
- package/specialists/org/contracts/architect-to-devil-advocate.json +38 -0
- package/specialists/org/contracts/architect-to-engineer.json +34 -0
- package/specialists/org/contracts/architect-to-evaluator.json +59 -0
- package/specialists/org/contracts/architect-to-legal-compliance.json +55 -0
- package/specialists/org/contracts/architect-to-operations.json +45 -0
- package/specialists/org/contracts/architect-to-platform-engineer.json +42 -0
- package/specialists/org/contracts/business-strategist-to-product-manager.json +39 -0
- package/specialists/org/contracts/construct-to-orchestrator.json +36 -0
- package/specialists/org/contracts/construct-to-rd-lead.json +53 -0
- package/specialists/org/contracts/data-analyst-to-product-manager.json +43 -0
- package/specialists/org/contracts/data-engineer-to-platform-engineer.json +36 -0
- package/specialists/org/contracts/designer-to-accessibility.json +36 -0
- package/specialists/org/contracts/engineer-to-qa.json +34 -0
- package/specialists/org/contracts/engineer-to-reviewer.json +52 -0
- package/specialists/org/contracts/explorer-to-engineer.json +38 -0
- package/specialists/org/contracts/legal-compliance-to-release-manager.json +43 -0
- package/specialists/org/contracts/platform-engineer-to-engineer.json +31 -0
- package/specialists/org/contracts/product-manager-to-architect.json +71 -0
- package/specialists/org/contracts/product-manager-to-data-analyst.json +49 -0
- package/specialists/org/contracts/product-manager-to-ux-researcher.json +51 -0
- package/specialists/org/contracts/qa-to-release-manager.json +42 -0
- package/specialists/org/contracts/qa-to-test-automation.json +42 -0
- package/specialists/org/contracts/rd-lead-to-architect.json +38 -0
- package/specialists/org/contracts/researcher-to-architect.json +40 -0
- package/specialists/org/contracts/researcher-to-product-manager.json +57 -0
- package/specialists/org/contracts/reviewer-to-security.json +41 -0
- package/specialists/org/contracts/sre-to-release-manager.json +36 -0
- package/specialists/org/contracts/test-automation-to-engineer.json +34 -0
- package/specialists/org/contracts/trace-reviewer-to-sre.json +41 -0
- package/specialists/org/contracts/user-to-construct.json +30 -0
- package/specialists/org/groups/engineering-group.json +43 -0
- package/specialists/org/groups/governance-group.json +38 -0
- package/specialists/org/groups/operations-group.json +41 -0
- package/specialists/org/groups/product-group.json +46 -0
- package/specialists/org/groups/quality-group.json +42 -0
- package/specialists/org/groups/strategy-group.json +41 -0
- package/specialists/org/policies/action-approval.json +13 -0
- package/specialists/org/policies/agents-routing.json +13 -0
- package/specialists/org/policies/architecture.json +20 -0
- package/specialists/org/policies/bash-safety.json +13 -0
- package/specialists/org/policies/beads-hygiene.json +13 -0
- package/specialists/org/policies/bootstrap-state.json +13 -0
- package/specialists/org/policies/code-review.json +13 -0
- package/specialists/org/policies/coding-style.json +13 -0
- package/specialists/org/policies/comments.json +13 -0
- package/specialists/org/policies/commit-approval.json +13 -0
- package/specialists/org/policies/contract-preconditions.json +13 -0
- package/specialists/org/policies/deployment.json +20 -0
- package/specialists/org/policies/description.json +14 -0
- package/specialists/org/policies/design-approval.json +19 -0
- package/specialists/org/policies/doc-ownership.json +13 -0
- package/specialists/org/policies/file-path-fence.json +13 -0
- package/specialists/org/policies/framing.json +13 -0
- package/specialists/org/policies/git-workflow.json +13 -0
- package/specialists/org/policies/incident-response.json +15 -0
- package/specialists/org/policies/intake-triage.json +15 -0
- package/specialists/org/policies/neurodivergent-output.json +13 -0
- package/specialists/org/policies/no-fabrication.json +13 -0
- package/specialists/org/policies/patterns.json +13 -0
- package/specialists/org/policies/quality-gate-approval.json +17 -0
- package/specialists/org/policies/release-gates.json +13 -0
- package/specialists/org/policies/research-evidence.json +13 -0
- package/specialists/org/policies/review-before-change.json +13 -0
- package/specialists/org/policies/rollback.json +15 -0
- package/specialists/org/policies/scope-change.json +20 -0
- package/specialists/org/policies/secret-scan.json +13 -0
- package/specialists/org/policies/security-approval.json +17 -0
- package/specialists/org/policies/security.json +13 -0
- package/specialists/org/policies/session-efficiency.json +13 -0
- package/specialists/org/policies/skill-routing.json +13 -0
- package/specialists/org/policies/strategic-prioritization.json +17 -0
- package/specialists/org/policies/testing.json +13 -0
- package/specialists/org/policies/tool-invisibility.json +14 -0
- package/specialists/org/scopes/creative.json +54 -0
- package/specialists/org/scopes/operations.json +51 -0
- package/specialists/org/scopes/research.json +60 -0
- package/specialists/org/scopes/rnd.json +81 -0
- package/specialists/org/specialists/cx-accessibility.json +69 -0
- package/specialists/org/specialists/cx-ai-engineer.json +75 -0
- package/specialists/org/specialists/cx-architect.json +89 -0
- package/specialists/org/specialists/cx-business-strategist.json +72 -0
- package/specialists/org/specialists/cx-data-analyst.json +68 -0
- package/specialists/org/specialists/cx-data-engineer.json +71 -0
- package/specialists/org/specialists/cx-debugger.json +75 -0
- package/specialists/org/specialists/cx-designer.json +85 -0
- package/specialists/org/specialists/cx-devil-advocate.json +71 -0
- package/specialists/org/specialists/cx-docs-keeper.json +82 -0
- package/specialists/org/specialists/cx-engineer.json +106 -0
- package/specialists/org/specialists/cx-evaluator.json +69 -0
- package/specialists/org/specialists/cx-explorer.json +69 -0
- package/specialists/org/specialists/cx-legal-compliance.json +74 -0
- package/specialists/org/specialists/cx-operations.json +72 -0
- package/specialists/org/specialists/cx-oracle.json +46 -0
- package/specialists/org/specialists/cx-orchestrator.json +81 -0
- package/specialists/org/specialists/cx-platform-engineer.json +80 -0
- package/specialists/org/specialists/cx-product-manager.json +102 -0
- package/specialists/org/specialists/cx-qa.json +79 -0
- package/specialists/org/specialists/cx-rd-lead.json +73 -0
- package/specialists/org/specialists/cx-release-manager.json +77 -0
- package/specialists/org/specialists/cx-researcher.json +82 -0
- package/specialists/org/specialists/cx-reviewer.json +71 -0
- package/specialists/org/specialists/cx-security.json +91 -0
- package/specialists/org/specialists/cx-sre.json +94 -0
- package/specialists/org/specialists/cx-test-automation.json +69 -0
- package/specialists/org/specialists/cx-trace-reviewer.json +69 -0
- package/specialists/org/specialists/cx-ux-researcher.json +68 -0
- package/specialists/org/teams/accessibility-team.json +39 -0
- package/specialists/org/teams/design-team.json +40 -0
- package/specialists/org/teams/engineering-team.json +50 -0
- package/specialists/org/teams/governance-team.json +41 -0
- package/specialists/org/teams/operations-team.json +46 -0
- package/specialists/org/teams/product-management-team.json +45 -0
- package/specialists/org/teams/quality-team.json +49 -0
- package/specialists/org/teams/research-team.json +39 -0
- package/specialists/org/teams/strategy-team.json +48 -0
- package/specialists/org/teams/ux-research-team.json +39 -0
- package/specialists/prompts/_shared/validation-contract.md +1 -1
- package/specialists/prompts/_team-template.md +10 -0
- package/specialists/prompts/cx-accessibility.md +1 -0
- package/specialists/prompts/cx-ai-engineer.md +1 -1
- package/specialists/prompts/cx-business-strategist.md +1 -1
- package/specialists/prompts/cx-data-engineer.md +1 -1
- package/specialists/prompts/cx-designer.md +1 -0
- package/specialists/prompts/cx-devil-advocate.md +1 -1
- package/specialists/prompts/cx-docs-keeper.md +1 -1
- package/specialists/prompts/cx-evaluator.md +1 -1
- package/specialists/prompts/cx-explorer.md +1 -1
- package/specialists/prompts/cx-operations.md +1 -1
- package/specialists/prompts/cx-oracle.md +7 -3
- package/specialists/prompts/cx-orchestrator.md +17 -1
- package/specialists/prompts/cx-platform-engineer.md +1 -1
- package/specialists/prompts/cx-product-manager.md +2 -0
- package/specialists/prompts/cx-release-manager.md +1 -1
- package/specialists/prompts/cx-researcher.md +7 -4
- package/specialists/prompts/cx-sre.md +1 -1
- package/specialists/prompts/cx-test-automation.md +2 -2
- package/specialists/prompts/cx-trace-reviewer.md +3 -3
- package/specialists/prompts/cx-ux-researcher.md +2 -1
- package/templates/demos/scripts/architecture-review-adr.json +30 -0
- package/templates/demos/scripts/capability-contract.json +25 -0
- package/templates/demos/scripts/intake-triage.json +25 -0
- package/templates/demos/scripts/profile-doctor-health.json +25 -0
- package/templates/demos/tapes/agentic-platforms-prd.tape +2 -2
- package/templates/demos/tapes/architecture-review-adr.tape +44 -0
- package/templates/demos/tapes/capability-contract.tape +39 -0
- package/templates/demos/tapes/intake-triage.tape +39 -0
- package/templates/demos/tapes/profile-doctor-health.tape +39 -0
- package/templates/distribution/construct-brand.typ +23 -18
- package/templates/distribution/construct-decision.typ +1 -1
- package/templates/distribution/construct-pdf.typ +1 -1
- package/templates/distribution/construct-prd.typ +1 -1
- package/templates/distribution/construct-research.typ +1 -1
- package/templates/distribution/mermaid-puppeteer.json +8 -0
- package/templates/docs/persona-artifact.md +1 -1
- package/templates/docs/prd.md +6 -0
- package/apps/chat/engine/ai-sdk-agent.mjs +0 -183
- package/apps/chat/engine/loop-driver.mjs +0 -211
- package/apps/chat/engine/models.mjs +0 -122
- package/apps/chat/engine/provider-adapters.mjs +0 -171
- package/apps/chat/engine/tools/permission.mjs +0 -54
- package/apps/chat/engine/tools/primitives.mjs +0 -180
- package/apps/chat/engine/tools/registry.mjs +0 -122
- package/apps/chat/engine/turn-controls.mjs +0 -70
- package/lib/boundary.mjs +0 -127
- package/lib/certification/dashboard-api.mjs +0 -71
- package/lib/chat/cli.mjs +0 -333
- package/lib/chat/command-suggest.mjs +0 -161
- package/lib/chat/commands.mjs +0 -215
- package/lib/chat/config.mjs +0 -142
- package/lib/chat/context-compactor.mjs +0 -250
- package/lib/chat/context-continuation.mjs +0 -253
- package/lib/chat/continuation-source.mjs +0 -58
- package/lib/chat/demo-guide.mjs +0 -61
- package/lib/chat/design-tokens.mjs +0 -91
- package/lib/chat/desktop-binary.mjs +0 -81
- package/lib/chat/desktop-build.mjs +0 -130
- package/lib/chat/desktop-launcher.mjs +0 -133
- package/lib/chat/evidence.mjs +0 -145
- package/lib/chat/export.mjs +0 -74
- package/lib/chat/harness/driver.mjs +0 -91
- package/lib/chat/list-picker.mjs +0 -112
- package/lib/chat/model-picker.mjs +0 -356
- package/lib/chat/openrouter-fallback.mjs +0 -151
- package/lib/chat/permission-prompt.mjs +0 -33
- package/lib/chat/picker-catalog.mjs +0 -45
- package/lib/chat/policy-telemetry.mjs +0 -34
- package/lib/chat/present.mjs +0 -246
- package/lib/chat/session-context.mjs +0 -39
- package/lib/chat/session-persist.mjs +0 -73
- package/lib/chat/session-restore.mjs +0 -71
- package/lib/chat/session-settings.mjs +0 -53
- package/lib/chat/system-prompt.mjs +0 -52
- package/lib/chat/transparency.mjs +0 -93
- package/lib/chat/tui/color-scheme.mjs +0 -42
- package/lib/chat/tui/markdown.mjs +0 -123
- package/lib/chat/tui/presentation.mjs +0 -100
- package/lib/chat/tui/render.mjs +0 -500
- package/lib/chat/tui/turn-block.mjs +0 -284
- package/lib/chat/tui/turn-present.mjs +0 -18
- package/lib/chat/tui/turn-state.mjs +0 -88
- package/lib/chat/tui/usage.mjs +0 -122
- package/lib/chat/web-commands.mjs +0 -146
- package/lib/chat/web-launcher.mjs +0 -63
- package/lib/chat/web-picker-keys.mjs +0 -46
- package/lib/chat/web-session.mjs +0 -159
- package/lib/config/alias.mjs +0 -57
- package/lib/dashboard-demo.mjs +0 -71
- package/lib/dashboard-static.mjs +0 -175
- package/lib/install/desktop-binary-download.mjs +0 -88
- package/lib/profiles/loader.mjs +0 -123
- package/lib/profiles/rebrand.mjs +0 -46
- package/lib/server/auth.mjs +0 -169
- package/lib/server/chat-loop.mjs +0 -622
- package/lib/server/chat.mjs +0 -336
- package/lib/server/cors.mjs +0 -77
- package/lib/server/csrf.mjs +0 -103
- package/lib/server/demo-preview.mjs +0 -63
- package/lib/server/index.mjs +0 -2641
- package/lib/server/insights.mjs +0 -780
- package/lib/server/langfuse-login.mjs +0 -58
- package/lib/server/rate-limit.mjs +0 -93
- package/lib/server/telemetry-login.mjs +0 -100
- package/lib/server/webhook.mjs +0 -512
- package/specialists/contracts.json +0 -1032
- package/specialists/contracts.schema.json +0 -83
- package/specialists/policy-inventory.json +0 -188
- package/specialists/registry.json +0 -1412
- package/specialists/role-manifests.json +0 -217
- package/specialists/teams.json +0 -94
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/demo-project.mjs — project-level demo plug-in scaffold (.cx/demos).
|
|
3
|
+
*
|
|
4
|
+
* `construct demo init --from-project` calls scaffoldProjectDemo to seed a
|
|
5
|
+
* project-defined demo from real project signals (no fabricated scenario
|
|
6
|
+
* content): a manifest (<name>.project.json) plus the demo script and terminal
|
|
7
|
+
* tape stub it ships with, all under .cx/demos/. The manifest is
|
|
8
|
+
* validated against schemas/project-demo.schema.json by a hand-rolled checker
|
|
9
|
+
* (Construct stays dependency-free at startup — no ajv).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
13
|
+
import { join, dirname, basename } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { readContextState } from './context-state.mjs';
|
|
16
|
+
|
|
17
|
+
const REPO_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
18
|
+
|
|
19
|
+
export const PROJECT_DEMO_SCHEMA = 'construct/project-demo/1';
|
|
20
|
+
|
|
21
|
+
export function projectDemosDir(cwd) {
|
|
22
|
+
return join(cwd, '.cx', 'demos');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function projectDemoManifestPath(cwd, name) {
|
|
26
|
+
return join(projectDemosDir(cwd), `${name}.project.json`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function projectDemoScriptRel(name) {
|
|
30
|
+
return `.cx/demos/scripts/${name}.json`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function projectDemoTapeRel(name) {
|
|
34
|
+
return `.cx/demos/tapes/${name}.tape`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// The project name is read, never invented: prefer the durable context state,
|
|
38
|
+
// then package.json, and only then the directory basename as a last resort.
|
|
39
|
+
|
|
40
|
+
export function resolveProjectName(cwd) {
|
|
41
|
+
const state = readContextState(cwd);
|
|
42
|
+
if (state?.projectName && typeof state.projectName === 'string') return state.projectName;
|
|
43
|
+
|
|
44
|
+
const pkgPath = join(cwd, 'package.json');
|
|
45
|
+
if (existsSync(pkgPath)) {
|
|
46
|
+
try {
|
|
47
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
48
|
+
if (pkg?.name && typeof pkg.name === 'string') return pkg.name;
|
|
49
|
+
} catch { /* unreadable package.json falls through to the directory name */ }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return basename(cwd) || 'project';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function projectScriptSource(name, projectName) {
|
|
56
|
+
return {
|
|
57
|
+
name,
|
|
58
|
+
title: `${projectName} demo — ${name}`,
|
|
59
|
+
summary: `Project demo for ${projectName}: walk Construct's read-only status and capability surfaces.`,
|
|
60
|
+
systemOverlay: `Demo mode for the ${projectName} project. Run the exact construct CLI commands from each step. Every command here is read-only and secret-free; report exactly what each command emits and do not invent output.`,
|
|
61
|
+
tape: name,
|
|
62
|
+
fallbackSurface: 'tape',
|
|
63
|
+
steps: [
|
|
64
|
+
{
|
|
65
|
+
title: 'Status — system health',
|
|
66
|
+
prompt: `Run construct status to show ${projectName}'s system health and configured credentials.`,
|
|
67
|
+
command: 'node bin/construct status',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
title: 'Capability — what this install can do',
|
|
71
|
+
prompt: 'Run construct capability describe --json to emit the read-only capability contract for this install.',
|
|
72
|
+
command: 'node bin/construct capability describe --json',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function projectTapeSource(name, projectName) {
|
|
79
|
+
return `Require node
|
|
80
|
+
Require vhs
|
|
81
|
+
# construct demo: ${name} — ${projectName} project plug-in
|
|
82
|
+
Output ${projectDemoTapeRel(name)}
|
|
83
|
+
Set FontSize 18
|
|
84
|
+
Set Width 1100
|
|
85
|
+
Set Height 600
|
|
86
|
+
Set TypingSpeed 60ms
|
|
87
|
+
|
|
88
|
+
Type "node bin/construct status"
|
|
89
|
+
Sleep 500ms
|
|
90
|
+
Enter
|
|
91
|
+
Sleep 2s
|
|
92
|
+
|
|
93
|
+
Type "node bin/construct capability describe --json"
|
|
94
|
+
Sleep 500ms
|
|
95
|
+
Enter
|
|
96
|
+
Sleep 2s
|
|
97
|
+
`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function buildProjectDemoManifest(name, { cwd, projectName, now = () => new Date() } = {}) {
|
|
101
|
+
const resolvedProject = projectName || resolveProjectName(cwd);
|
|
102
|
+
return {
|
|
103
|
+
$schema: '../../schemas/project-demo.schema.json',
|
|
104
|
+
schema: PROJECT_DEMO_SCHEMA,
|
|
105
|
+
name,
|
|
106
|
+
title: `${resolvedProject} demo — ${name}`,
|
|
107
|
+
summary: `Project-defined Construct demo for ${resolvedProject}.`,
|
|
108
|
+
project: resolvedProject,
|
|
109
|
+
script: projectDemoScriptRel(name),
|
|
110
|
+
tape: projectDemoTapeRel(name),
|
|
111
|
+
fallbackSurface: 'tape',
|
|
112
|
+
createdAt: now().toISOString(),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// A hand-rolled validator against schemas/project-demo.schema.json. Construct
|
|
117
|
+
// stays dependency-free at startup, so this mirrors the schema's required
|
|
118
|
+
// fields, const, and patterns rather than loading ajv.
|
|
119
|
+
|
|
120
|
+
export function validateProjectDemoManifest(manifest) {
|
|
121
|
+
const errors = [];
|
|
122
|
+
if (!manifest || typeof manifest !== 'object') return { valid: false, errors: ['manifest is not an object'] };
|
|
123
|
+
|
|
124
|
+
if (manifest.schema !== PROJECT_DEMO_SCHEMA) errors.push(`schema must equal ${PROJECT_DEMO_SCHEMA}`);
|
|
125
|
+
if (typeof manifest.name !== 'string' || !/^[a-z0-9][a-z0-9-]*$/.test(manifest.name)) {
|
|
126
|
+
errors.push('name required, lowercase slug ([a-z0-9][a-z0-9-]*)');
|
|
127
|
+
}
|
|
128
|
+
if (typeof manifest.title !== 'string' || !manifest.title) errors.push('title required');
|
|
129
|
+
if (typeof manifest.project !== 'string' || !manifest.project) errors.push('project required');
|
|
130
|
+
if (typeof manifest.script !== 'string' || !/^\.cx\/demos\/scripts\/.+\.json$/.test(manifest.script)) {
|
|
131
|
+
errors.push('script must be a .cx/demos/scripts/*.json path');
|
|
132
|
+
}
|
|
133
|
+
if (manifest.fallbackSurface && !['tape', 'playwright'].includes(manifest.fallbackSurface)) {
|
|
134
|
+
errors.push('fallbackSurface must be tape or playwright');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { valid: errors.length === 0, errors };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function loadProjectDemoManifest(name, { cwd } = {}) {
|
|
141
|
+
const manifestPath = projectDemoManifestPath(cwd, name);
|
|
142
|
+
if (!existsSync(manifestPath)) return { ok: false, errors: [`project demo not found: ${name}`], manifestPath };
|
|
143
|
+
try {
|
|
144
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
145
|
+
const validated = validateProjectDemoManifest(manifest);
|
|
146
|
+
if (!validated.valid) return { ok: false, errors: validated.errors, manifestPath };
|
|
147
|
+
return { ok: true, manifest, manifestPath };
|
|
148
|
+
} catch (err) {
|
|
149
|
+
return { ok: false, errors: [err.message || 'invalid JSON'], manifestPath };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function scaffoldProjectDemo(name, { cwd, projectName, repoRoot = REPO_ROOT, now = () => new Date() } = {}) {
|
|
154
|
+
if (!name) return { ok: false, message: 'A demo name is required: construct demo init <name> --from-project' };
|
|
155
|
+
|
|
156
|
+
const manifestPath = projectDemoManifestPath(cwd, name);
|
|
157
|
+
if (existsSync(manifestPath)) return { ok: false, message: `Project demo already exists: ${manifestPath}` };
|
|
158
|
+
|
|
159
|
+
const resolvedProject = projectName || resolveProjectName(cwd);
|
|
160
|
+
const demosDir = projectDemosDir(cwd);
|
|
161
|
+
const scriptsDir = join(demosDir, 'scripts');
|
|
162
|
+
const tapesDir = join(demosDir, 'tapes');
|
|
163
|
+
mkdirSync(scriptsDir, { recursive: true });
|
|
164
|
+
mkdirSync(tapesDir, { recursive: true });
|
|
165
|
+
|
|
166
|
+
const scriptPath = join(cwd, projectDemoScriptRel(name));
|
|
167
|
+
const tapePath = join(cwd, projectDemoTapeRel(name));
|
|
168
|
+
|
|
169
|
+
const script = projectScriptSource(name, resolvedProject);
|
|
170
|
+
writeFileSync(scriptPath, `${JSON.stringify(script, null, 2)}\n`, 'utf8');
|
|
171
|
+
writeFileSync(tapePath, projectTapeSource(name, resolvedProject), 'utf8');
|
|
172
|
+
|
|
173
|
+
const manifest = buildProjectDemoManifest(name, { cwd, projectName: resolvedProject, now });
|
|
174
|
+
const validated = validateProjectDemoManifest(manifest);
|
|
175
|
+
if (!validated.valid) {
|
|
176
|
+
return { ok: false, message: `Scaffolded manifest failed validation: ${validated.errors.join('; ')}` };
|
|
177
|
+
}
|
|
178
|
+
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
ok: true,
|
|
182
|
+
manifestPath,
|
|
183
|
+
scriptPath,
|
|
184
|
+
tapePath,
|
|
185
|
+
project: resolvedProject,
|
|
186
|
+
message: `Scaffolded project demo ${name} for ${resolvedProject}:\n ${manifestPath}\n ${scriptPath}\n ${tapePath}`,
|
|
187
|
+
};
|
|
188
|
+
}
|
package/lib/demo-recording.mjs
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/demo-recording.mjs — load and validate Playwright recording manifests.
|
|
3
3
|
*
|
|
4
|
-
* Search order: .cx/demos/recordings/ then templates/demos/recordings/.
|
|
5
|
-
* dashboardDemo scripts synthesize a recording manifest when no JSON exists.
|
|
4
|
+
* Search order: .cx/demos/recordings/ then templates/demos/recordings/.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
import fs from 'node:fs';
|
|
9
8
|
import path from 'node:path';
|
|
10
9
|
import { fileURLToPath } from 'node:url';
|
|
11
|
-
import { loadDemoScript } from './demo-script.mjs';
|
|
12
10
|
|
|
13
11
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
14
12
|
const REPO_ROOT = path.resolve(HERE, '..');
|
|
@@ -90,45 +88,22 @@ export function normalizeRecording(raw, name) {
|
|
|
90
88
|
};
|
|
91
89
|
}
|
|
92
90
|
|
|
93
|
-
export function recordingFromDemoScript(name, { cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
|
|
94
|
-
const script = loadDemoScript(name, { cwd, repoRoot });
|
|
95
|
-
if (!script?.dashboardDemo) return null;
|
|
96
|
-
const demoName = script.dashboardDemo;
|
|
97
|
-
const repo = repoRoot || REPO_ROOT;
|
|
98
|
-
return normalizeRecording({
|
|
99
|
-
name: script.name || name,
|
|
100
|
-
title: script.title || name,
|
|
101
|
-
engine: 'playwright',
|
|
102
|
-
workspace: 'apps/dashboard',
|
|
103
|
-
spec: `apps/dashboard/e2e/demo/${demoName}.spec.ts`,
|
|
104
|
-
baseUrl: 'http://127.0.0.1:4242',
|
|
105
|
-
project: 'demo-recording',
|
|
106
|
-
artifactReveal: script.artifactReveal,
|
|
107
|
-
output: { format: 'mp4', path: `.cx/demos/dashboard/${demoName}.mp4` },
|
|
108
|
-
_legacy: true,
|
|
109
|
-
}, name);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
91
|
export function loadDemoRecording(name, { cwd = process.cwd(), repoRoot = REPO_ROOT } = {}) {
|
|
113
92
|
const filePath = resolveDemoRecordingPath(name, { cwd, repoRoot });
|
|
114
|
-
if (filePath)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
93
|
+
if (!filePath) return null;
|
|
94
|
+
try {
|
|
95
|
+
const raw = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
96
|
+
const validated = validateRecording(raw, name);
|
|
97
|
+
if (!validated.ok) return null;
|
|
98
|
+
return { ...validated.recording, sourcePath: filePath };
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
123
101
|
}
|
|
124
|
-
return recordingFromDemoScript(name, { cwd, repoRoot });
|
|
125
102
|
}
|
|
126
103
|
|
|
127
104
|
export function loadDemoRecordingValidated(name, opts = {}) {
|
|
128
105
|
const filePath = resolveDemoRecordingPath(name, opts);
|
|
129
106
|
if (!filePath) {
|
|
130
|
-
const legacy = recordingFromDemoScript(name, opts);
|
|
131
|
-
if (legacy) return { ok: true, recording: legacy };
|
|
132
107
|
return { ok: false, errors: [`recording not found: ${name}`] };
|
|
133
108
|
}
|
|
134
109
|
try {
|
package/lib/demo-script.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/demo-script.mjs — load committed demo scripts
|
|
2
|
+
* lib/demo-script.mjs — load committed demo scripts.
|
|
3
3
|
*
|
|
4
4
|
* Scripts live in templates/demos/scripts/ (shipped) and .cx/demos/scripts/
|
|
5
5
|
* (project overrides). Each script defines steps, system overlay, and optional
|
|
6
|
-
*
|
|
6
|
+
* recording fallbacks.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import fs from 'node:fs';
|
package/lib/demo-surface.mjs
CHANGED
|
@@ -1,119 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/demo-surface.mjs —
|
|
2
|
+
* lib/demo-surface.mjs — demo execution with recording surfaces and fallbacks.
|
|
3
3
|
*
|
|
4
|
-
* Default surface is
|
|
5
|
-
*
|
|
6
|
-
* (no provider, non-interactive), the runner degrades through the script's
|
|
7
|
-
* fallback chain without failing the command.
|
|
4
|
+
* Default surface is VHS tape recording. Users may target Playwright explicitly.
|
|
5
|
+
* When a recorder is unavailable, the runner prints the demo script steps.
|
|
8
6
|
*/
|
|
9
7
|
|
|
10
|
-
import { spawnSync } from 'node:child_process';
|
|
11
|
-
import { hasAnySecret } from './providers/secret-resolver.mjs';
|
|
12
|
-
import { loadConstructEnv } from './env-config.mjs';
|
|
13
8
|
import { loadDemoScript, formatDemoWelcome, createDemoGuide } from './demo-script.mjs';
|
|
14
9
|
import { loadDemoRecording } from './demo-recording.mjs';
|
|
15
|
-
import { recordDashboardDemo } from './dashboard-demo.mjs';
|
|
16
10
|
import { recordPlaywrightDemo } from './playwright-demo.mjs';
|
|
17
11
|
|
|
18
|
-
export const DEMO_SURFACES = ['
|
|
19
|
-
|
|
20
|
-
const PROVIDER_KEYS = ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'OPENROUTER_API_KEY', 'GITHUB_TOKEN'];
|
|
21
|
-
|
|
22
|
-
function which(bin) {
|
|
23
|
-
const result = spawnSync(process.platform === 'win32' ? 'where' : 'which', [bin], { encoding: 'utf8' });
|
|
24
|
-
if (result.status !== 0) return null;
|
|
25
|
-
return result.stdout.trim().split('\n')[0] || null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function detectChatDemoReady({ env = process.env, cwd = process.cwd() } = {}) {
|
|
29
|
-
const effective = loadConstructEnv({ rootDir: cwd, env, warn: false });
|
|
30
|
-
if (hasAnySecret(PROVIDER_KEYS, { env: effective, cwd })) {
|
|
31
|
-
return { ready: true, reason: 'provider configured' };
|
|
32
|
-
}
|
|
33
|
-
if (which('ollama')) {
|
|
34
|
-
return { ready: true, reason: 'local Ollama detected' };
|
|
35
|
-
}
|
|
36
|
-
return {
|
|
37
|
-
ready: false,
|
|
38
|
-
reason: 'No LLM provider configured. Set a key in ~/.construct/config.env, run `construct creds login copilot`, or start Ollama.',
|
|
39
|
-
};
|
|
40
|
-
}
|
|
12
|
+
export const DEMO_SURFACES = ['tape', 'playwright'];
|
|
41
13
|
|
|
42
14
|
export function buildDemoAttemptChain(requestedSurface, { script, recording = null, interactive = true } = {}) {
|
|
43
15
|
const chain = [];
|
|
44
16
|
const push = (surface) => { if (!chain.includes(surface)) chain.push(surface); };
|
|
45
17
|
|
|
46
|
-
if (requestedSurface === '
|
|
47
|
-
push('dashboard');
|
|
48
|
-
push('playwright');
|
|
49
|
-
push('chat');
|
|
50
|
-
push('web');
|
|
51
|
-
} else if (requestedSurface === 'playwright') {
|
|
18
|
+
if (requestedSurface === 'playwright') {
|
|
52
19
|
push('playwright');
|
|
53
|
-
push('dashboard');
|
|
54
|
-
} else if (requestedSurface === 'web') {
|
|
55
|
-
push('web');
|
|
56
|
-
push('chat');
|
|
57
|
-
} else if (requestedSurface === 'tape') {
|
|
58
|
-
push('tape');
|
|
59
|
-
return chain;
|
|
60
20
|
} else {
|
|
61
|
-
push('
|
|
62
|
-
push('web');
|
|
21
|
+
push('tape');
|
|
63
22
|
}
|
|
64
23
|
|
|
65
|
-
if (script?.dashboardDemo && requestedSurface !== 'tape') push('dashboard');
|
|
66
24
|
if (recording?.engine === 'playwright' && requestedSurface !== 'tape') push('playwright');
|
|
67
25
|
push('tape');
|
|
68
|
-
|
|
69
|
-
if (!interactive && recording?.engine === 'playwright') {
|
|
70
|
-
const filtered = chain.filter((s) => s !== 'chat' && s !== 'web');
|
|
71
|
-
return filtered.length ? filtered : ['tape'];
|
|
72
|
-
}
|
|
73
|
-
if (!interactive && chain[0] === 'chat') {
|
|
74
|
-
const filtered = chain.filter((s) => s !== 'chat' && s !== 'web');
|
|
75
|
-
return filtered.length ? filtered : ['tape'];
|
|
76
|
-
}
|
|
77
26
|
return chain;
|
|
78
27
|
}
|
|
79
28
|
|
|
80
|
-
function buildChatArgs(name, opts) {
|
|
81
|
-
const args = [`--demo=${name}`];
|
|
82
|
-
if (opts.web) args.push('--web');
|
|
83
|
-
if (opts.plain) args.push('--plain');
|
|
84
|
-
if (opts.accessible) args.push('--accessible');
|
|
85
|
-
if (opts.free) args.push('--free');
|
|
86
|
-
if (opts.model) args.push(`--model=${opts.model}`);
|
|
87
|
-
return args;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async function tryChatDemo(name, opts, { web = false } = {}) {
|
|
91
|
-
const readiness = detectChatDemoReady({ env: opts.env, cwd: opts.cwd });
|
|
92
|
-
if (!readiness.ready) {
|
|
93
|
-
return { ok: false, surface: web ? 'web' : 'chat', soft: true, message: readiness.reason };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const script = loadDemoScript(name, opts);
|
|
97
|
-
if (!script) {
|
|
98
|
-
return { ok: false, surface: web ? 'web' : 'chat', soft: true, message: `No demo script for ${name}` };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const chatArgs = buildChatArgs(name, { ...opts, web });
|
|
102
|
-
const { runChat } = await import('./chat/cli.mjs');
|
|
103
|
-
const code = await runChat(chatArgs, {
|
|
104
|
-
cwd: opts.cwd,
|
|
105
|
-
env: { ...opts.env, CONSTRUCT_DEMO: name },
|
|
106
|
-
input: opts.input || process.stdin,
|
|
107
|
-
output: opts.output || process.stdout,
|
|
108
|
-
errorOutput: opts.errorOutput || process.stderr,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
if (code === 0) {
|
|
112
|
-
return { ok: true, surface: web ? 'web' : 'chat', message: `Demo ${name} finished in chat` };
|
|
113
|
-
}
|
|
114
|
-
return { ok: false, surface: web ? 'web' : 'chat', soft: true, message: `Chat exited ${code}` };
|
|
115
|
-
}
|
|
116
|
-
|
|
117
29
|
async function tryTapeDemo(name, opts, { sourceOnly = false } = {}) {
|
|
118
30
|
const { runDemoRecord, resolveTapeSource, listProjectTapes } = await import('./demo.mjs');
|
|
119
31
|
const tapeName = loadDemoScript(name, opts)?.tape || name;
|
|
@@ -157,17 +69,6 @@ function tryPlaywrightDemo(name, opts) {
|
|
|
157
69
|
return { ok: false, surface: 'playwright', soft: true, ...result };
|
|
158
70
|
}
|
|
159
71
|
|
|
160
|
-
function tryDashboardDemo(name, opts) {
|
|
161
|
-
const script = loadDemoScript(name, opts);
|
|
162
|
-
const demoName = script?.dashboardDemo;
|
|
163
|
-
if (!demoName) {
|
|
164
|
-
return { ok: false, surface: 'dashboard', soft: true, message: 'No dashboard demo configured' };
|
|
165
|
-
}
|
|
166
|
-
const result = recordDashboardDemo(demoName, { cwd: opts.cwd, repoRoot: opts.repoRoot });
|
|
167
|
-
if (result.ok) return { ok: true, surface: 'dashboard', ...result };
|
|
168
|
-
return { ok: false, surface: 'dashboard', soft: true, ...result };
|
|
169
|
-
}
|
|
170
|
-
|
|
171
72
|
function printScriptFallback(name, opts, notices = []) {
|
|
172
73
|
const script = loadDemoScript(name, opts);
|
|
173
74
|
if (!script) {
|
|
@@ -192,39 +93,31 @@ function printScriptFallback(name, opts, notices = []) {
|
|
|
192
93
|
export async function runDemoGuided(name, {
|
|
193
94
|
cwd = process.cwd(),
|
|
194
95
|
repoRoot,
|
|
195
|
-
surface = '
|
|
96
|
+
surface = 'tape',
|
|
196
97
|
format = 'mp4',
|
|
197
98
|
out = null,
|
|
198
99
|
sourceOnly = false,
|
|
199
100
|
required = false,
|
|
200
|
-
model = null,
|
|
201
|
-
plain = false,
|
|
202
|
-
accessible = false,
|
|
203
|
-
web = false,
|
|
204
|
-
free = false,
|
|
205
|
-
env = process.env,
|
|
206
|
-
input,
|
|
207
101
|
output,
|
|
208
102
|
errorOutput,
|
|
209
103
|
} = {}) {
|
|
104
|
+
const errOut = errorOutput || process.stderr;
|
|
105
|
+
|
|
210
106
|
const opts = {
|
|
211
|
-
cwd, repoRoot, format, out, sourceOnly, required,
|
|
107
|
+
cwd, repoRoot, format, out, sourceOnly, required, output, errorOutput: errOut,
|
|
212
108
|
};
|
|
213
109
|
|
|
214
110
|
const script = loadDemoScript(name, opts);
|
|
215
111
|
const recording = loadDemoRecording(name, opts);
|
|
216
|
-
const requested =
|
|
217
|
-
const interactive = Boolean((
|
|
112
|
+
const requested = DEMO_SURFACES.includes(surface) ? surface : 'tape';
|
|
113
|
+
const interactive = Boolean((output || process.stdout).isTTY);
|
|
218
114
|
const chain = buildDemoAttemptChain(requested, { script, recording, interactive });
|
|
219
115
|
const notices = [];
|
|
220
116
|
|
|
221
117
|
for (const attempt of chain) {
|
|
222
118
|
let result;
|
|
223
|
-
if (attempt === '
|
|
224
|
-
else if (attempt === 'web') result = await tryChatDemo(name, opts, { web: true });
|
|
225
|
-
else if (attempt === 'tape') result = await tryTapeDemo(name, opts);
|
|
119
|
+
if (attempt === 'tape') result = await tryTapeDemo(name, opts);
|
|
226
120
|
else if (attempt === 'playwright') result = tryPlaywrightDemo(name, opts);
|
|
227
|
-
else if (attempt === 'dashboard') result = tryDashboardDemo(name, opts);
|
|
228
121
|
else continue;
|
|
229
122
|
|
|
230
123
|
if (result.ok) {
|
|
@@ -236,14 +129,3 @@ export async function runDemoGuided(name, {
|
|
|
236
129
|
|
|
237
130
|
return printScriptFallback(name, opts, notices);
|
|
238
131
|
}
|
|
239
|
-
|
|
240
|
-
export function resolveDemoGuideForChat(name, { cwd = process.cwd(), repoRoot } = {}) {
|
|
241
|
-
const script = loadDemoScript(name, { cwd, repoRoot });
|
|
242
|
-
if (!script) return null;
|
|
243
|
-
return {
|
|
244
|
-
script,
|
|
245
|
-
guide: createDemoGuide(script),
|
|
246
|
-
welcome: formatDemoWelcome(script),
|
|
247
|
-
systemOverlay: script.systemOverlay,
|
|
248
|
-
};
|
|
249
|
-
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/demo-tour-renderer.mjs — linear, screen-reader-friendly demo tour renderer.
|
|
3
|
+
*
|
|
4
|
+
* Renders a loaded demo script (lib/demo-script.mjs) as a numbered, top-to-bottom
|
|
5
|
+
* walkthrough with no Ink/interactive widgets, so `construct demo tour --accessible`
|
|
6
|
+
* stays WCAG-plain: color is opt-in via the caller's palette and off under
|
|
7
|
+
* NO_COLOR / non-TTY, prose wraps at a readable width, and every step is announced
|
|
8
|
+
* as "Step N of M". Headless runs pass { skipInput: true } to auto-advance without
|
|
9
|
+
* touching stdin; an interactive run without that flag pauses for Enter between
|
|
10
|
+
* steps. This renderer stays independent of host UI code — the tour is built on the demo
|
|
11
|
+
* surface only.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { resolveColors, termWidth, wrapText } from './term-format.mjs';
|
|
15
|
+
|
|
16
|
+
function writeWrapped(output, text, width) {
|
|
17
|
+
output.write(`${wrapText(String(text), width)}\n`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function renderHeader(output, script, colors, width, total) {
|
|
21
|
+
output.write(`${colors.bold}Demo tour: ${script.title}${colors.reset}\n`);
|
|
22
|
+
if (script.summary) writeWrapped(output, `${colors.dim}${script.summary}${colors.reset}`, width);
|
|
23
|
+
output.write(`\n${colors.dim}${total} step${total === 1 ? '' : 's'}. Each step shows the prompt to ask and the command it runs.${colors.reset}\n`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function renderStep(output, step, position, total, colors, width) {
|
|
27
|
+
const title = step.title || step.prompt?.slice(0, 60) || `Step ${position}`;
|
|
28
|
+
output.write(`\n${colors.bold}Step ${position} of ${total}: ${title}${colors.reset}\n`);
|
|
29
|
+
if (step.prompt) {
|
|
30
|
+
output.write(`${colors.dim}Prompt:${colors.reset}\n`);
|
|
31
|
+
writeWrapped(output, ` ${step.prompt}`, width);
|
|
32
|
+
}
|
|
33
|
+
if (step.command) {
|
|
34
|
+
output.write(`${colors.dim}Command:${colors.reset}\n`);
|
|
35
|
+
output.write(` ${step.command}\n`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Pause for Enter only when a real interactive TTY is driving and the caller did
|
|
40
|
+
// not request auto-advance. A single readable() line keeps the wait keyboard-only
|
|
41
|
+
// and free of cursor positioning so screen readers track it as plain output.
|
|
42
|
+
|
|
43
|
+
function waitForEnter(input, output, colors) {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
output.write(`${colors.dim}Press Enter for the next step…${colors.reset}`);
|
|
46
|
+
const onData = () => {
|
|
47
|
+
input.removeListener('data', onData);
|
|
48
|
+
input.pause?.();
|
|
49
|
+
output.write('\n');
|
|
50
|
+
resolve();
|
|
51
|
+
};
|
|
52
|
+
input.resume?.();
|
|
53
|
+
input.once('data', onData);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function renderTour({
|
|
58
|
+
script,
|
|
59
|
+
output = process.stdout,
|
|
60
|
+
input = process.stdin,
|
|
61
|
+
env = process.env,
|
|
62
|
+
accessible = false,
|
|
63
|
+
plain = false,
|
|
64
|
+
skipInput = false,
|
|
65
|
+
color,
|
|
66
|
+
} = {}) {
|
|
67
|
+
if (!script || !Array.isArray(script.steps)) {
|
|
68
|
+
return { ok: false, steps: 0, message: 'No demo script to tour' };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const linear = accessible || plain;
|
|
72
|
+
const useColor = color !== undefined ? color : (!linear);
|
|
73
|
+
const colors = resolveColors({ enabled: useColor, stream: output, env });
|
|
74
|
+
const width = termWidth(output);
|
|
75
|
+
const total = script.steps.length;
|
|
76
|
+
|
|
77
|
+
// Linear and headless contexts never block on input; interactivity requires a
|
|
78
|
+
// TTY on both ends so a piped or CI run advances on its own.
|
|
79
|
+
const interactive = !skipInput && !linear && Boolean(input?.isTTY) && Boolean(output?.isTTY);
|
|
80
|
+
|
|
81
|
+
renderHeader(output, script, colors, width, total);
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i < total; i += 1) {
|
|
84
|
+
renderStep(output, script.steps[i], i + 1, total, colors, width);
|
|
85
|
+
if (interactive && i < total - 1) {
|
|
86
|
+
await waitForEnter(input, output, colors);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
output.write(`\n${colors.bold}Tour complete.${colors.reset} ${colors.dim}Run any step above in OpenCode, or run \`construct demo ${script.name}\` to drive it live.${colors.reset}\n`);
|
|
91
|
+
return { ok: true, steps: total, surface: 'tour', accessible: linear };
|
|
92
|
+
}
|