@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
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/web-launcher.mjs — start dashboard server and open /chat for construct chat --web.
|
|
3
|
-
*
|
|
4
|
-
* Reuses lib/service-manager.mjs startDashboard so the browser surface shares auth,
|
|
5
|
-
* CSRF, and static hosting with the operations dashboard.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { spawn } from 'node:child_process';
|
|
9
|
-
import { startDashboard, readDashboardState } from '../service-manager.mjs';
|
|
10
|
-
|
|
11
|
-
function openBrowser(url) {
|
|
12
|
-
const platform = process.platform;
|
|
13
|
-
let cmd;
|
|
14
|
-
let args;
|
|
15
|
-
if (platform === 'darwin') {
|
|
16
|
-
cmd = 'open';
|
|
17
|
-
args = [url];
|
|
18
|
-
} else if (platform === 'win32') {
|
|
19
|
-
cmd = 'cmd';
|
|
20
|
-
args = ['/c', 'start', '', url];
|
|
21
|
-
} else {
|
|
22
|
-
cmd = 'xdg-open';
|
|
23
|
-
args = [url];
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
spawn(cmd, args, { detached: true, stdio: 'ignore' }).unref();
|
|
27
|
-
} catch {
|
|
28
|
-
/* browser open is best-effort */
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function runWebChat({
|
|
33
|
-
cwd = process.cwd(),
|
|
34
|
-
env = process.env,
|
|
35
|
-
output = process.stdout,
|
|
36
|
-
errorOutput = process.stderr,
|
|
37
|
-
surface = null,
|
|
38
|
-
} = {}) {
|
|
39
|
-
let dash = readDashboardState();
|
|
40
|
-
if (!dash?.port) {
|
|
41
|
-
const result = await startDashboard({ rootDir: cwd, preferredPort: parseInt(env.PORT || env.DASHBOARD_PORT || '4242', 10) });
|
|
42
|
-
if (result.started || result.reused) {
|
|
43
|
-
dash = { port: result.port, url: result.url || `http://127.0.0.1:${result.port}` };
|
|
44
|
-
} else {
|
|
45
|
-
errorOutput.write(`Failed to start dashboard: ${result.reason || 'unknown error'}\n`);
|
|
46
|
-
return 1;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const base = (dash.url || `http://127.0.0.1:${dash.port}`).replace(/\/$/, '');
|
|
51
|
-
const chatUrl = surface === 'desktop'
|
|
52
|
-
? `${base}/chat/?surface=desktop`
|
|
53
|
-
: `${base}/chat/`;
|
|
54
|
-
output.write(`Construct web chat: ${chatUrl}\n`);
|
|
55
|
-
output.write('Press Ctrl-C to stop the dashboard server.\n');
|
|
56
|
-
openBrowser(chatUrl);
|
|
57
|
-
|
|
58
|
-
await new Promise((resolve) => {
|
|
59
|
-
process.once('SIGINT', resolve);
|
|
60
|
-
process.once('SIGTERM', resolve);
|
|
61
|
-
});
|
|
62
|
-
return 0;
|
|
63
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/web-picker-keys.mjs — keyboard routing for the web list picker.
|
|
3
|
-
*
|
|
4
|
-
* Arrow/enter/escape handling shared with Ink via list-picker.mjs; this module
|
|
5
|
-
* adapts DOM KeyboardEvent shapes for tests and the React overlay.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
getPickerSelectedItem,
|
|
10
|
-
getPickerVisibleItems,
|
|
11
|
-
movePickerIndex,
|
|
12
|
-
reducePickerKey,
|
|
13
|
-
} from './list-picker.mjs';
|
|
14
|
-
|
|
15
|
-
export { getPickerSelectedItem, getPickerVisibleItems, movePickerIndex };
|
|
16
|
-
|
|
17
|
-
export function domKeyToPickerKey(event) {
|
|
18
|
-
const key = event?.key || '';
|
|
19
|
-
return {
|
|
20
|
-
char: key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey ? key : undefined,
|
|
21
|
-
key: {
|
|
22
|
-
escape: key === 'Escape',
|
|
23
|
-
return: key === 'Enter',
|
|
24
|
-
upArrow: key === 'ArrowUp',
|
|
25
|
-
downArrow: key === 'ArrowDown',
|
|
26
|
-
backspace: key === 'Backspace',
|
|
27
|
-
delete: key === 'Delete',
|
|
28
|
-
ctrl: event.ctrlKey,
|
|
29
|
-
meta: event.metaKey,
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function isPickerNavigationKey(key) {
|
|
35
|
-
return key === 'ArrowUp' || key === 'ArrowDown' || key === 'Enter' || key === 'Escape';
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function reduceWebPickerKey(state, event) {
|
|
39
|
-
return reducePickerKey(state, domKeyToPickerKey(event));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function commitWebPickerSelection(state) {
|
|
43
|
-
const item = getPickerSelectedItem(state);
|
|
44
|
-
if (!item || item.disabled) return { ok: false, item: null };
|
|
45
|
-
return { ok: true, item };
|
|
46
|
-
}
|
package/lib/chat/web-session.mjs
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/web-session.mjs — owned-loop runtime factory for browser chat.
|
|
3
|
-
*
|
|
4
|
-
* Builds the same driver + session shape as lib/chat/cli.mjs so dashboard SSE
|
|
5
|
-
* and construct chat --web share one configuration path. One runtime instance
|
|
6
|
-
* is kept per conversation id for the lifetime of the server process.
|
|
7
|
-
*/
|
|
8
|
-
import { randomBytes } from 'crypto';
|
|
9
|
-
import { createOwnedLoopDriver } from '../../apps/chat/engine/loop-driver.mjs';
|
|
10
|
-
import { resolveChatModelSelectionAsync, resolveSessionModel } from '../../apps/chat/engine/models.mjs';
|
|
11
|
-
import { loadChatConfig } from './config.mjs';
|
|
12
|
-
import { loadConstructEnv } from '../env-config.mjs';
|
|
13
|
-
import { resolveLayers } from './transparency.mjs';
|
|
14
|
-
import { buildSystemPrompt } from './system-prompt.mjs';
|
|
15
|
-
import { createSessionUsage } from './tui/usage.mjs';
|
|
16
|
-
import { resolveExecutionCapabilityProfile } from '../models/execution-capability-profile.mjs';
|
|
17
|
-
import { compileExecutionPolicy } from '../models/execution-policy.mjs';
|
|
18
|
-
import { resolveDemoGuideForChat } from '../demo-surface.mjs';
|
|
19
|
-
|
|
20
|
-
const runtimes = new Map();
|
|
21
|
-
const pendingPermissions = new Map();
|
|
22
|
-
|
|
23
|
-
function createPermissionHandler({ session, onPermission = null }) {
|
|
24
|
-
return async (req) => {
|
|
25
|
-
const mode = session.permissionMode || 'allow_once';
|
|
26
|
-
if (mode === 'reject') return 'reject';
|
|
27
|
-
if (mode === 'allow_always') return 'allow_always';
|
|
28
|
-
if (mode === 'allow_once') return 'allow';
|
|
29
|
-
if (mode === 'ask') {
|
|
30
|
-
const requestId = req.requestId || `perm-${randomBytes(8).toString('hex')}`;
|
|
31
|
-
onPermission?.({
|
|
32
|
-
type: 'permission',
|
|
33
|
-
requestId,
|
|
34
|
-
toolCall: { title: req.tool || req.toolCall?.title || 'tool', input: req.input ?? null },
|
|
35
|
-
options: ['allow', 'allow_always', 'reject'],
|
|
36
|
-
});
|
|
37
|
-
return new Promise((resolve) => {
|
|
38
|
-
pendingPermissions.set(requestId, { resolve, req, convId: session.convId });
|
|
39
|
-
setTimeout(() => {
|
|
40
|
-
if (pendingPermissions.has(requestId)) {
|
|
41
|
-
pendingPermissions.delete(requestId);
|
|
42
|
-
resolve('reject');
|
|
43
|
-
}
|
|
44
|
-
}, 120_000);
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
return 'allow';
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function resolveWebPermission(requestId, decision) {
|
|
52
|
-
const pending = pendingPermissions.get(requestId);
|
|
53
|
-
if (!pending) return false;
|
|
54
|
-
pendingPermissions.delete(requestId);
|
|
55
|
-
pending.resolve(decision);
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function listPendingPermissions(convId) {
|
|
60
|
-
return [...pendingPermissions.entries()]
|
|
61
|
-
.filter(([, p]) => p.convId === convId)
|
|
62
|
-
.map(([requestId, p]) => ({
|
|
63
|
-
requestId,
|
|
64
|
-
title: p.req?.toolCall?.title || 'tool',
|
|
65
|
-
options: p.req?.options || ['allow', 'reject'],
|
|
66
|
-
}));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function getWebChatRuntime(convId) {
|
|
70
|
-
return runtimes.get(convId) || null;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export async function ensureWebChatRuntime({
|
|
74
|
-
convId,
|
|
75
|
-
cwd = process.cwd(),
|
|
76
|
-
env = process.env,
|
|
77
|
-
onPermission = null,
|
|
78
|
-
} = {}) {
|
|
79
|
-
if (runtimes.has(convId)) return runtimes.get(convId);
|
|
80
|
-
|
|
81
|
-
const effectiveEnv = loadConstructEnv({ rootDir: cwd, env, warn: false });
|
|
82
|
-
const { config } = loadChatConfig({ cwd });
|
|
83
|
-
const layers = resolveLayers({ flags: {}, env: effectiveEnv });
|
|
84
|
-
for (const key of Object.keys(config.layers || {})) {
|
|
85
|
-
if (config.layers[key] === false) layers[key] = false;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
let modelMode = config.modelMode || 'pinned';
|
|
89
|
-
let resolvedModel = config.model || null;
|
|
90
|
-
if (modelMode === 'free-router') {
|
|
91
|
-
const sessionStub = { modelMode: 'free-router', failedModels: new Set() };
|
|
92
|
-
resolvedModel = await resolveSessionModel(sessionStub, { env: effectiveEnv, tier: 'standard' });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const modelResolution = modelMode === 'free-router' && resolvedModel
|
|
96
|
-
? { id: resolvedModel, notice: null }
|
|
97
|
-
: await resolveChatModelSelectionAsync({ env: effectiveEnv, requested: resolvedModel });
|
|
98
|
-
|
|
99
|
-
const demoName = effectiveEnv.CONSTRUCT_DEMO || null;
|
|
100
|
-
const demoPack = demoName ? resolveDemoGuideForChat(demoName, { cwd, repoRoot: cwd }) : null;
|
|
101
|
-
|
|
102
|
-
const session = {
|
|
103
|
-
convId,
|
|
104
|
-
model: modelResolution.id || resolvedModel,
|
|
105
|
-
modelMode,
|
|
106
|
-
permissionMode: config.permissionMode || 'allow_once',
|
|
107
|
-
sandbox: config.sandbox || 'workspace-write',
|
|
108
|
-
layers,
|
|
109
|
-
usage: createSessionUsage(),
|
|
110
|
-
demoGuide: demoPack?.guide || null,
|
|
111
|
-
demoTitle: demoPack?.script?.title || null,
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const handlers = {
|
|
115
|
-
getSandbox: () => session.sandbox,
|
|
116
|
-
getPermissionMode: () => session.permissionMode,
|
|
117
|
-
requestPermission: createPermissionHandler({ session, onPermission }),
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const { createAiSdkAgent } = await import('../../apps/chat/engine/ai-sdk-agent.mjs');
|
|
121
|
-
const driver = createOwnedLoopDriver({
|
|
122
|
-
env: effectiveEnv,
|
|
123
|
-
cwd,
|
|
124
|
-
model: session.model,
|
|
125
|
-
handlers,
|
|
126
|
-
systemPrompt: buildSystemPrompt({
|
|
127
|
-
capabilityTier: compileExecutionPolicy({
|
|
128
|
-
profile: resolveExecutionCapabilityProfile({ model: session.model }),
|
|
129
|
-
}).prompt.systemPromptTier,
|
|
130
|
-
}),
|
|
131
|
-
createAgent: (opts) => createAiSdkAgent(opts),
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
const runtime = { convId, driver, session, layers, cwd, env: effectiveEnv, _started: false };
|
|
135
|
-
runtimes.set(convId, runtime);
|
|
136
|
-
|
|
137
|
-
if (!demoPack?.guide) {
|
|
138
|
-
await driver.start();
|
|
139
|
-
runtime._started = true;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return runtime;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export async function ensureDriverStarted(runtime) {
|
|
146
|
-
if (!runtime || runtime._started) return runtime;
|
|
147
|
-
await runtime.driver.start();
|
|
148
|
-
runtime._started = true;
|
|
149
|
-
return runtime;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export const createWebChatRuntime = ensureWebChatRuntime;
|
|
153
|
-
|
|
154
|
-
export function dropWebChatRuntime(convId) {
|
|
155
|
-
const runtime = runtimes.get(convId);
|
|
156
|
-
if (!runtime) return;
|
|
157
|
-
try { runtime.driver.stop?.(); } catch { /* already stopped */ }
|
|
158
|
-
runtimes.delete(convId);
|
|
159
|
-
}
|
package/lib/config/alias.mjs
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/config/alias.mjs — resolves the user-facing Construct alias.
|
|
3
|
-
*
|
|
4
|
-
* Lookup precedence:
|
|
5
|
-
* 1. CONSTRUCT_ALIAS env var (per-session override)
|
|
6
|
-
* 2. config.json.aliasOverride in the XDG config dir (user-level, lets each
|
|
7
|
-
* user on the same project see their own branding without
|
|
8
|
-
* committing it)
|
|
9
|
-
* 3. construct.config.json.alias (project-level, committed)
|
|
10
|
-
* 4. "Construct" (the default)
|
|
11
|
-
*
|
|
12
|
-
* Pure resolution — no fs writes. The alias flows to the dashboard
|
|
13
|
-
* wordmark (via /api/alias), persona prompts ({{alias}} template at
|
|
14
|
-
* sync time), and CLI banner output.
|
|
15
|
-
*/
|
|
16
|
-
import fs from 'node:fs';
|
|
17
|
-
import os from 'node:os';
|
|
18
|
-
import path from 'node:path';
|
|
19
|
-
import { loadProjectConfig } from './project-config.mjs';
|
|
20
|
-
import { configDir } from './xdg.mjs';
|
|
21
|
-
|
|
22
|
-
export const DEFAULT_ALIAS = 'Construct';
|
|
23
|
-
export const ALIAS_ENV_KEY = 'CONSTRUCT_ALIAS';
|
|
24
|
-
export const ALIAS_TEMPLATE_TOKEN = /\{\{alias\}\}/g;
|
|
25
|
-
|
|
26
|
-
export function resolveAlias({ cwd = process.cwd(), env = process.env, homeDir = os.homedir() } = {}) {
|
|
27
|
-
const envAlias = env?.[ALIAS_ENV_KEY];
|
|
28
|
-
if (typeof envAlias === 'string' && envAlias.trim()) {
|
|
29
|
-
return { value: envAlias.trim(), source: 'env' };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
const userCfgPath = path.join(configDir(homeDir), 'config.json');
|
|
34
|
-
if (fs.existsSync(userCfgPath)) {
|
|
35
|
-
const userCfg = JSON.parse(fs.readFileSync(userCfgPath, 'utf8'));
|
|
36
|
-
const userAlias = userCfg?.aliasOverride;
|
|
37
|
-
if (typeof userAlias === 'string' && userAlias.trim()) {
|
|
38
|
-
return { value: userAlias.trim(), source: 'user' };
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
} catch { /* user file is best-effort */ }
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
const { config, source } = loadProjectConfig(cwd, env);
|
|
45
|
-
if (source === 'file' && typeof config?.alias === 'string' && config.alias.trim()) {
|
|
46
|
-
return { value: config.alias.trim(), source: 'project' };
|
|
47
|
-
}
|
|
48
|
-
} catch { /* project config is best-effort */ }
|
|
49
|
-
|
|
50
|
-
return { value: DEFAULT_ALIAS, source: 'default' };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function applyAliasToTemplate(text, alias) {
|
|
54
|
-
if (typeof text !== 'string') return text;
|
|
55
|
-
const safe = String(alias ?? DEFAULT_ALIAS).trim() || DEFAULT_ALIAS;
|
|
56
|
-
return text.replace(ALIAS_TEMPLATE_TOKEN, safe);
|
|
57
|
-
}
|
package/lib/dashboard-demo.mjs
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/dashboard-demo.mjs — Construct cockpit Playwright demo adapter.
|
|
3
|
-
*
|
|
4
|
-
* Resolves dashboard recording manifests (or legacy script bridge) and delegates
|
|
5
|
-
* to lib/playwright-demo.mjs with dashboard workspace defaults.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import path from 'node:path';
|
|
9
|
-
import { loadDemoRecording } from './demo-recording.mjs';
|
|
10
|
-
import {
|
|
11
|
-
detectPlaywrightDemo,
|
|
12
|
-
recordPlaywrightDemo,
|
|
13
|
-
collectVideoFiles,
|
|
14
|
-
newestVideo,
|
|
15
|
-
finalizeDemoVideo,
|
|
16
|
-
locateFfmpeg,
|
|
17
|
-
transcodeWebmToMp4,
|
|
18
|
-
resolvePlaywrightPackage,
|
|
19
|
-
} from './playwright-demo.mjs';
|
|
20
|
-
|
|
21
|
-
export {
|
|
22
|
-
collectVideoFiles,
|
|
23
|
-
newestVideo,
|
|
24
|
-
finalizeDemoVideo,
|
|
25
|
-
locateFfmpeg,
|
|
26
|
-
transcodeWebmToMp4,
|
|
27
|
-
resolvePlaywrightPackage,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export function dashboardWorkspaceRoot(repoRoot) {
|
|
31
|
-
return path.join(repoRoot, 'apps', 'dashboard');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function detectDashboardDemo({ cwd = process.cwd(), repoRoot } = {}) {
|
|
35
|
-
const root = repoRoot || cwd;
|
|
36
|
-
return detectPlaywrightDemo({ workspace: dashboardWorkspaceRoot(root), repoRoot: root, cwd });
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function recordDashboardDemo(name, {
|
|
40
|
-
cwd = process.cwd(),
|
|
41
|
-
repoRoot,
|
|
42
|
-
outputDir,
|
|
43
|
-
outputPath = null,
|
|
44
|
-
format = 'mp4',
|
|
45
|
-
artifactDir = null,
|
|
46
|
-
artifactFile = null,
|
|
47
|
-
env = process.env,
|
|
48
|
-
} = {}) {
|
|
49
|
-
const root = repoRoot || cwd;
|
|
50
|
-
const recording = loadDemoRecording(name, { cwd, repoRoot: root });
|
|
51
|
-
|
|
52
|
-
if (!recording) {
|
|
53
|
-
const detection = detectDashboardDemo({ cwd, repoRoot: root });
|
|
54
|
-
if (!detection.present) {
|
|
55
|
-
return { ok: false, name, missing: detection.missing, message: detection.message };
|
|
56
|
-
}
|
|
57
|
-
return { ok: false, name, message: `No recording manifest or dashboard demo script for ${name}` };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const outDir = outputDir || path.join(cwd, '.cx', 'demos', 'dashboard');
|
|
61
|
-
return recordPlaywrightDemo(recording, {
|
|
62
|
-
cwd,
|
|
63
|
-
repoRoot: root,
|
|
64
|
-
outputDir: outDir,
|
|
65
|
-
outputPath,
|
|
66
|
-
format,
|
|
67
|
-
artifactDir,
|
|
68
|
-
artifactFile,
|
|
69
|
-
env,
|
|
70
|
-
});
|
|
71
|
-
}
|
package/lib/dashboard-static.mjs
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/dashboard-static.mjs — build and sync the dashboard static bundle.
|
|
3
|
-
*
|
|
4
|
-
* Keeps the Next.js static export at `apps/dashboard/out/` and the served
|
|
5
|
-
* tree at `lib/server/static/` aligned so the HTTP server picks up the
|
|
6
|
-
* current dashboard build without code changes. Pre-2.0 the dashboard
|
|
7
|
-
* lived at `dashboard/dist/`; the rebuild to Next.js (commit 84cd721)
|
|
8
|
-
* moved the source of truth into `apps/dashboard/out/`.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import fs from 'node:fs';
|
|
12
|
-
import path from 'node:path';
|
|
13
|
-
import { spawnSync } from 'node:child_process';
|
|
14
|
-
import { sanitizeNpmSpawnEnv } from './npm-spawn-env.mjs';
|
|
15
|
-
|
|
16
|
-
function walkFiles(rootDir, currentDir = rootDir) {
|
|
17
|
-
if (!fs.existsSync(currentDir)) return [];
|
|
18
|
-
const entries = [];
|
|
19
|
-
for (const name of fs.readdirSync(currentDir)) {
|
|
20
|
-
const fullPath = path.join(currentDir, name);
|
|
21
|
-
const stat = fs.statSync(fullPath);
|
|
22
|
-
if (stat.isDirectory()) {
|
|
23
|
-
entries.push(...walkFiles(rootDir, fullPath));
|
|
24
|
-
} else {
|
|
25
|
-
entries.push(path.relative(rootDir, fullPath));
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return entries.sort();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function readBufferIfExists(filePath) {
|
|
32
|
-
if (!fs.existsSync(filePath)) return null;
|
|
33
|
-
return fs.readFileSync(filePath);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function compareTrees(sourceDir, targetDir) {
|
|
37
|
-
const sourceFiles = walkFiles(sourceDir);
|
|
38
|
-
const targetFiles = walkFiles(targetDir);
|
|
39
|
-
const sourceSet = new Set(sourceFiles);
|
|
40
|
-
const targetSet = new Set(targetFiles);
|
|
41
|
-
const copied = [];
|
|
42
|
-
const removed = [];
|
|
43
|
-
|
|
44
|
-
for (const relPath of sourceFiles) {
|
|
45
|
-
const source = readBufferIfExists(path.join(sourceDir, relPath));
|
|
46
|
-
const target = readBufferIfExists(path.join(targetDir, relPath));
|
|
47
|
-
if (!target || !source.equals(target)) copied.push(relPath);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
for (const relPath of targetFiles) {
|
|
51
|
-
if (!sourceSet.has(relPath)) removed.push(relPath);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
copied,
|
|
56
|
-
removed,
|
|
57
|
-
sourceFiles,
|
|
58
|
-
targetFiles,
|
|
59
|
-
changed: copied.length > 0 || removed.length > 0,
|
|
60
|
-
inSync: copied.length === 0 && removed.length === 0,
|
|
61
|
-
sourceExists: fs.existsSync(sourceDir),
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function ensureParentDir(filePath) {
|
|
66
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function removeEmptyDirs(dirPath, stopAt) {
|
|
70
|
-
let current = dirPath;
|
|
71
|
-
while (current.startsWith(stopAt) && current !== stopAt) {
|
|
72
|
-
if (!fs.existsSync(current)) {
|
|
73
|
-
current = path.dirname(current);
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (fs.readdirSync(current).length > 0) break;
|
|
77
|
-
fs.rmdirSync(current);
|
|
78
|
-
current = path.dirname(current);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function getDashboardPaths({ rootDir = process.cwd() } = {}) {
|
|
83
|
-
return {
|
|
84
|
-
rootDir,
|
|
85
|
-
dashboardDir: path.join(rootDir, 'apps', 'dashboard'),
|
|
86
|
-
sourceDir: path.join(rootDir, 'apps', 'dashboard', 'out'),
|
|
87
|
-
targetDir: path.join(rootDir, 'lib', 'server', 'static'),
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function getDashboardStaticStatus({ rootDir = process.cwd() } = {}) {
|
|
92
|
-
const { sourceDir, targetDir } = getDashboardPaths({ rootDir });
|
|
93
|
-
return compareTrees(sourceDir, targetDir);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function syncDashboardStatic({ rootDir = process.cwd(), check = false } = {}) {
|
|
97
|
-
const { sourceDir, targetDir } = getDashboardPaths({ rootDir });
|
|
98
|
-
if (!fs.existsSync(sourceDir)) {
|
|
99
|
-
throw new Error(`Dashboard build output missing: ${sourceDir}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const status = compareTrees(sourceDir, targetDir);
|
|
103
|
-
if (check || !status.changed) return status;
|
|
104
|
-
|
|
105
|
-
for (const relPath of status.copied) {
|
|
106
|
-
const sourcePath = path.join(sourceDir, relPath);
|
|
107
|
-
const targetPath = path.join(targetDir, relPath);
|
|
108
|
-
ensureParentDir(targetPath);
|
|
109
|
-
fs.copyFileSync(sourcePath, targetPath);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
for (const relPath of status.removed) {
|
|
113
|
-
const targetPath = path.join(targetDir, relPath);
|
|
114
|
-
fs.rmSync(targetPath, { force: true });
|
|
115
|
-
removeEmptyDirs(path.dirname(targetPath), targetDir);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
...status,
|
|
120
|
-
inSync: true,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function buildDashboardStatic({
|
|
125
|
-
rootDir = process.cwd(),
|
|
126
|
-
spawnSyncFn = spawnSync,
|
|
127
|
-
} = {}) {
|
|
128
|
-
const { dashboardDir } = getDashboardPaths({ rootDir });
|
|
129
|
-
const nextCache = path.join(dashboardDir, '.next');
|
|
130
|
-
fs.rmSync(nextCache, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
131
|
-
|
|
132
|
-
const env = sanitizeNpmSpawnEnv(process.env);
|
|
133
|
-
let result;
|
|
134
|
-
for (let attempt = 1; attempt <= 2; attempt += 1) {
|
|
135
|
-
result = spawnSyncFn('npm', ['--prefix', dashboardDir, 'run', 'build'], {
|
|
136
|
-
cwd: rootDir,
|
|
137
|
-
encoding: 'utf8',
|
|
138
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
139
|
-
env,
|
|
140
|
-
});
|
|
141
|
-
if (result.status === 0) break;
|
|
142
|
-
const output = `${result.stdout || ''}\n${result.stderr || ''}`;
|
|
143
|
-
const transientRename = /ENOENT.*rename.*\.next\/(export|server\/pages)\/500\.html/.test(output);
|
|
144
|
-
if (!transientRename) break;
|
|
145
|
-
fs.rmSync(nextCache, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
146
|
-
}
|
|
147
|
-
if (result.status !== 0) {
|
|
148
|
-
throw new Error((result.stderr || result.stdout || 'dashboard build failed').trim());
|
|
149
|
-
}
|
|
150
|
-
return result;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export async function runDashboardStaticCli(args = [], { rootDir = process.cwd() } = {}) {
|
|
154
|
-
const build = args.includes('--build');
|
|
155
|
-
const check = args.includes('--check');
|
|
156
|
-
|
|
157
|
-
if (build) buildDashboardStatic({ rootDir });
|
|
158
|
-
const status = syncDashboardStatic({ rootDir, check });
|
|
159
|
-
|
|
160
|
-
if (check) {
|
|
161
|
-
if (status.changed) {
|
|
162
|
-
console.error('Dashboard static assets are out of date. Run `construct dashboard:sync --build`.');
|
|
163
|
-
return 1;
|
|
164
|
-
}
|
|
165
|
-
console.log('Dashboard static assets are up to date.');
|
|
166
|
-
return 0;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (status.changed) {
|
|
170
|
-
console.log(`Dashboard static synced: ${status.copied.length} updated, ${status.removed.length} removed.`);
|
|
171
|
-
} else {
|
|
172
|
-
console.log('Dashboard static already up to date.');
|
|
173
|
-
}
|
|
174
|
-
return 0;
|
|
175
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/install/desktop-binary-download.mjs — downloads the Construct chat desktop binary.
|
|
3
|
-
*
|
|
4
|
-
* Fetches the platform-matching construct-chat binary from the latest GitHub
|
|
5
|
-
* release and writes it to the bin/ subdir of the XDG state dir. Called by
|
|
6
|
-
* `construct install --scope=user`.
|
|
7
|
-
* Network failures are non-fatal: returns { status: 'skipped', reason } so the
|
|
8
|
-
* rest of setup can proceed without the desktop binary.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import fs from 'node:fs';
|
|
12
|
-
import path from 'node:path';
|
|
13
|
-
|
|
14
|
-
import { stateDir } from '../config/xdg.mjs';
|
|
15
|
-
|
|
16
|
-
const GITHUB_REPO = 'geraldmaron/construct';
|
|
17
|
-
|
|
18
|
-
function assetName() {
|
|
19
|
-
const plat = process.platform;
|
|
20
|
-
const arch = process.arch === 'arm64' ? 'arm64' : 'x64';
|
|
21
|
-
if (plat === 'win32') return 'construct-chat-windows-x64.exe';
|
|
22
|
-
if (plat === 'darwin') return `construct-chat-darwin-${arch}`;
|
|
23
|
-
return `construct-chat-linux-${arch}`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function destBinaryName() {
|
|
27
|
-
return process.platform === 'win32' ? 'construct-chat.exe' : 'construct-chat';
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function fetchRelease(env) {
|
|
31
|
-
const headers = { 'User-Agent': 'construct-installer/1.0' };
|
|
32
|
-
if (env.GITHUB_TOKEN) headers['Authorization'] = `Bearer ${env.GITHUB_TOKEN}`;
|
|
33
|
-
const res = await fetch(`https://api.github.com/repos/${GITHUB_REPO}/releases/latest`, { headers });
|
|
34
|
-
if (!res.ok) throw new Error(`GitHub API ${res.status}: ${await res.text().then((t) => t.slice(0, 120))}`);
|
|
35
|
-
return res.json();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function downloadAsset(url, dest, env) {
|
|
39
|
-
const headers = {
|
|
40
|
-
'User-Agent': 'construct-installer/1.0',
|
|
41
|
-
'Accept': 'application/octet-stream',
|
|
42
|
-
};
|
|
43
|
-
if (env.GITHUB_TOKEN) headers['Authorization'] = `Bearer ${env.GITHUB_TOKEN}`;
|
|
44
|
-
const res = await fetch(url, { headers, redirect: 'follow' });
|
|
45
|
-
if (!res.ok) throw new Error(`Download ${res.status}: ${url}`);
|
|
46
|
-
const buf = await res.arrayBuffer();
|
|
47
|
-
fs.writeFileSync(dest, Buffer.from(buf));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Returns { status: 'downloaded'|'skipped'|'failed' }; 'skipped' and 'failed' are both non-fatal.
|
|
51
|
-
export async function downloadDesktopBinary({ homeDir, env = process.env } = {}) {
|
|
52
|
-
const binDir = path.join(stateDir(homeDir), 'bin');
|
|
53
|
-
const dest = path.join(binDir, destBinaryName());
|
|
54
|
-
const name = assetName();
|
|
55
|
-
|
|
56
|
-
let release;
|
|
57
|
-
try {
|
|
58
|
-
release = await fetchRelease(env);
|
|
59
|
-
} catch (err) {
|
|
60
|
-
return { status: 'skipped', reason: `GitHub API unreachable: ${err.message}` };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const asset = release.assets?.find((a) => a.name === name);
|
|
64
|
-
if (!asset) {
|
|
65
|
-
return {
|
|
66
|
-
status: 'skipped',
|
|
67
|
-
reason: `No desktop binary in ${release.tag_name} for this platform (looked for ${name})`,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
fs.mkdirSync(binDir, { recursive: true });
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
await downloadAsset(asset.browser_download_url, dest, env);
|
|
75
|
-
} catch (err) {
|
|
76
|
-
return { status: 'failed', reason: err.message };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (process.platform !== 'win32') {
|
|
80
|
-
try {
|
|
81
|
-
fs.chmodSync(dest, 0o755);
|
|
82
|
-
} catch {
|
|
83
|
-
/* best effort */
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return { status: 'downloaded', dest, version: release.tag_name };
|
|
88
|
-
}
|