@geraldmaron/construct 1.2.3 → 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 +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/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/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 +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/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
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/design-tokens.mjs — semantic chat tokens derived from brand primitives.
|
|
3
|
-
*
|
|
4
|
-
* Maps lib/brand-tokens.mjs ink ramp to dark/light chat surfaces for web CSS vars
|
|
5
|
-
* and terminal semantic colours. Single source for cockpit theming across linear
|
|
6
|
-
* and web/desktop surfaces.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { INK, FONTS, STATUS } from '../brand-tokens.mjs';
|
|
10
|
-
|
|
11
|
-
export const CHAT_DARK = Object.freeze({
|
|
12
|
-
bg: '#000000',
|
|
13
|
-
surface: '#0b0b0c',
|
|
14
|
-
border: INK.muted,
|
|
15
|
-
text: '#ededed',
|
|
16
|
-
muted: '#9a9a9e',
|
|
17
|
-
accent: '#ffffff',
|
|
18
|
-
accentAlt: INK.hairlineStrong,
|
|
19
|
-
...STATUS,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
export const CHAT_LIGHT = Object.freeze({
|
|
23
|
-
bg: '#f8f9fb',
|
|
24
|
-
surface: INK.surfaceAlt,
|
|
25
|
-
border: INK.hairlineStrong,
|
|
26
|
-
text: INK.ink,
|
|
27
|
-
muted: INK.muted,
|
|
28
|
-
accent: INK.inkStrong,
|
|
29
|
-
accentAlt: INK.muted,
|
|
30
|
-
ok: '#15803d',
|
|
31
|
-
warn: '#a16207',
|
|
32
|
-
danger: STATUS.danger,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const TERMINAL_DARK = Object.freeze({
|
|
36
|
-
text: { ink: 'white', code: '37' },
|
|
37
|
-
muted: { ink: 'gray', code: '90' },
|
|
38
|
-
accent: { ink: 'whiteBright', code: '97' },
|
|
39
|
-
accentAlt: { ink: 'gray', code: '90' },
|
|
40
|
-
brandAccent: { ink: 'whiteBright', code: '97' },
|
|
41
|
-
surface: { ink: 'gray', code: '90' },
|
|
42
|
-
surfaceMuted: { ink: 'gray', code: '90' },
|
|
43
|
-
border: { ink: 'gray', code: '90' },
|
|
44
|
-
ok: { ink: 'green', code: '32' },
|
|
45
|
-
warn: { ink: 'yellow', code: '33' },
|
|
46
|
-
danger: { ink: 'red', code: '31' },
|
|
47
|
-
badgeFg: { ink: 'black', code: '30' },
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
const TERMINAL_LIGHT = Object.freeze({
|
|
51
|
-
text: { ink: 'black', code: '30' },
|
|
52
|
-
muted: { ink: 'gray', code: '90' },
|
|
53
|
-
accent: { ink: 'black', code: '30' },
|
|
54
|
-
accentAlt: { ink: 'gray', code: '90' },
|
|
55
|
-
brandAccent: { ink: 'black', code: '30' },
|
|
56
|
-
surface: { ink: 'gray', code: '90' },
|
|
57
|
-
surfaceMuted: { ink: 'gray', code: '90' },
|
|
58
|
-
border: { ink: 'gray', code: '90' },
|
|
59
|
-
ok: { ink: 'green', code: '32' },
|
|
60
|
-
warn: { ink: 'rgb(161,98,7)', code: '33' },
|
|
61
|
-
danger: { ink: 'red', code: '31' },
|
|
62
|
-
badgeFg: { ink: 'white', code: '97' },
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
export function chatPalette(scheme = 'dark') {
|
|
66
|
-
return scheme === 'light' ? CHAT_LIGHT : CHAT_DARK;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function chatCssVars(scheme = 'dark') {
|
|
70
|
-
const p = chatPalette(scheme);
|
|
71
|
-
return {
|
|
72
|
-
'--cx-chat-bg': p.bg,
|
|
73
|
-
'--cx-chat-surface': p.surface,
|
|
74
|
-
'--cx-chat-border': p.border,
|
|
75
|
-
'--cx-chat-text': p.text,
|
|
76
|
-
'--cx-chat-muted': p.muted,
|
|
77
|
-
'--cx-chat-accent': p.accent,
|
|
78
|
-
'--cx-chat-accent-alt': p.accentAlt,
|
|
79
|
-
'--cx-chat-warn': p.warn,
|
|
80
|
-
'--cx-chat-danger': p.danger,
|
|
81
|
-
'--cx-chat-ok': p.ok,
|
|
82
|
-
'--cx-chat-font': FONTS.sans,
|
|
83
|
-
'--cx-chat-mono': FONTS.mono,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function chatTerminalSemantic(scheme = 'dark') {
|
|
88
|
-
return scheme === 'light' ? TERMINAL_LIGHT : TERMINAL_DARK;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export { FONTS, INK, STATUS };
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/desktop-binary.mjs — resolve the Construct chat desktop window binary.
|
|
3
|
-
*
|
|
4
|
-
* Search order: CONSTRUCT_CHAT_DESKTOP_BIN, repo dev build, the bin/ subdir of
|
|
5
|
-
* the XDG state dir, then packaged sidecar next to construct.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import fs from 'node:fs';
|
|
9
|
-
import os from 'node:os';
|
|
10
|
-
import path from 'node:path';
|
|
11
|
-
import { fileURLToPath } from 'node:url';
|
|
12
|
-
|
|
13
|
-
import { stateDir } from '../config/xdg.mjs';
|
|
14
|
-
|
|
15
|
-
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
-
const REPO_ROOT = path.resolve(HERE, '..', '..');
|
|
17
|
-
|
|
18
|
-
function binaryName() {
|
|
19
|
-
return process.platform === 'win32' ? 'construct-chat.exe' : 'construct-chat';
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function isReadableFile(filePath) {
|
|
23
|
-
try {
|
|
24
|
-
const stat = fs.statSync(filePath);
|
|
25
|
-
if (!stat.isFile()) return false;
|
|
26
|
-
fs.accessSync(filePath, fs.constants.R_OK);
|
|
27
|
-
return true;
|
|
28
|
-
} catch {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function isExecutable(filePath) {
|
|
34
|
-
try {
|
|
35
|
-
fs.accessSync(filePath, fs.constants.X_OK);
|
|
36
|
-
return fs.statSync(filePath).isFile();
|
|
37
|
-
} catch {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function isLaunchableBinary(filePath) {
|
|
43
|
-
if (!filePath) return false;
|
|
44
|
-
if (isExecutable(filePath)) return true;
|
|
45
|
-
return isReadableFile(filePath) && /\.(mjs|cjs|js)$/i.test(filePath);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function desktopBinaryCandidates({ repoRoot = REPO_ROOT, homeDir = os.homedir(), env = process.env } = {}) {
|
|
49
|
-
const name = binaryName();
|
|
50
|
-
const tauriRoot = path.join(repoRoot, 'apps', 'chat', 'desktop', 'src-tauri', 'target');
|
|
51
|
-
return [
|
|
52
|
-
env.CONSTRUCT_CHAT_DESKTOP_BIN,
|
|
53
|
-
path.join(tauriRoot, 'release', name),
|
|
54
|
-
path.join(tauriRoot, 'debug', name),
|
|
55
|
-
path.join(stateDir(homeDir), 'bin', name),
|
|
56
|
-
path.join(repoRoot, 'apps', 'chat', 'desktop', 'bin', name),
|
|
57
|
-
].filter(Boolean);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function resolveDesktopBinary(options = {}) {
|
|
61
|
-
for (const candidate of desktopBinaryCandidates(options)) {
|
|
62
|
-
if (isLaunchableBinary(candidate)) return candidate;
|
|
63
|
-
}
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function desktopBinaryInstallHint({ repoRoot = REPO_ROOT } = {}) {
|
|
68
|
-
return [
|
|
69
|
-
'Construct chat desktop window is not installed.',
|
|
70
|
-
'Install it with: construct install --scope=user',
|
|
71
|
-
`Or set CONSTRUCT_CHAT_DESKTOP_BIN to your construct-chat binary.`,
|
|
72
|
-
`Dev build: npm run build:chat-desktop (output: ${path.join(repoRoot, 'apps', 'chat', 'desktop', 'src-tauri', 'target', 'release', binaryName())})`,
|
|
73
|
-
].join('\n');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function hasGuiDisplay(env = process.env) {
|
|
77
|
-
if (env.CX_CHAT_NO_DISPLAY === '1') return false;
|
|
78
|
-
if (process.platform === 'win32') return true;
|
|
79
|
-
if (process.platform === 'darwin') return true;
|
|
80
|
-
return Boolean(env.DISPLAY || env.WAYLAND_DISPLAY);
|
|
81
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/desktop-build.mjs — build the Construct chat desktop window from source.
|
|
3
|
-
*
|
|
4
|
-
* The native window is a Tauri (Rust) app at apps/chat/desktop/src-tauri, so a
|
|
5
|
-
* build requires the Rust toolchain. Absent cargo, the rustup installer runs
|
|
6
|
-
* first, then `cargo build --release` produces the binary at
|
|
7
|
-
* target/release/construct-chat — exactly where resolveDesktopBinary looks.
|
|
8
|
-
* Mirrors the build-chat-desktop job in .github/workflows/release.yml for
|
|
9
|
-
* local, low-touch first-run setup.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { spawnSync } from 'node:child_process';
|
|
13
|
-
import fs from 'node:fs';
|
|
14
|
-
import os from 'node:os';
|
|
15
|
-
import path from 'node:path';
|
|
16
|
-
import { fileURLToPath } from 'node:url';
|
|
17
|
-
import { resolveDesktopBinary } from './desktop-binary.mjs';
|
|
18
|
-
|
|
19
|
-
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
-
const REPO_ROOT = path.resolve(HERE, '..', '..');
|
|
21
|
-
|
|
22
|
-
function srcTauriDir(repoRoot = REPO_ROOT) {
|
|
23
|
-
return path.join(repoRoot, 'apps', 'chat', 'desktop', 'src-tauri');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function hasDesktopSource(repoRoot = REPO_ROOT) {
|
|
27
|
-
try {
|
|
28
|
-
return fs.statSync(path.join(srcTauriDir(repoRoot), 'Cargo.toml')).isFile();
|
|
29
|
-
} catch {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// rustup installs to ~/.cargo/bin, absent from a fresh shell's PATH until
|
|
35
|
-
// ~/.cargo/env is sourced; prepend it for every child process here.
|
|
36
|
-
|
|
37
|
-
export function cargoEnv(env = process.env, homeDir = os.homedir()) {
|
|
38
|
-
const cargoBin = path.join(homeDir, '.cargo', 'bin');
|
|
39
|
-
const sep = process.platform === 'win32' ? ';' : ':';
|
|
40
|
-
const currentPath = env.PATH || '';
|
|
41
|
-
const parts = currentPath.split(sep);
|
|
42
|
-
if (!parts.includes(cargoBin)) {
|
|
43
|
-
return { ...env, PATH: `${cargoBin}${sep}${currentPath}` };
|
|
44
|
-
}
|
|
45
|
-
return { ...env };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function hasCargo(env = process.env, homeDir = os.homedir()) {
|
|
49
|
-
const probe = spawnSync('cargo', ['--version'], {
|
|
50
|
-
env: cargoEnv(env, homeDir),
|
|
51
|
-
stdio: 'ignore',
|
|
52
|
-
});
|
|
53
|
-
return probe.status === 0;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function ensureRustToolchain({
|
|
57
|
-
env = process.env,
|
|
58
|
-
homeDir = os.homedir(),
|
|
59
|
-
output = process.stdout,
|
|
60
|
-
errorOutput = process.stderr,
|
|
61
|
-
} = {}) {
|
|
62
|
-
if (hasCargo(env, homeDir)) return { ok: true };
|
|
63
|
-
|
|
64
|
-
if (process.platform === 'win32') {
|
|
65
|
-
return {
|
|
66
|
-
ok: false,
|
|
67
|
-
reason: 'Rust toolchain not found. Install it from https://rustup.rs and re-run.',
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
output.write('Installing the Rust toolchain (one-time setup for the Construct Chat window)…\n');
|
|
72
|
-
const installer = spawnSync(
|
|
73
|
-
'sh',
|
|
74
|
-
['-c', "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal"],
|
|
75
|
-
{ env, stdio: 'inherit' },
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
if (installer.status !== 0) {
|
|
79
|
-
return {
|
|
80
|
-
ok: false,
|
|
81
|
-
reason: 'Rust toolchain install failed. Install it manually from https://rustup.rs and re-run.',
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (!hasCargo(env, homeDir)) {
|
|
86
|
-
return {
|
|
87
|
-
ok: false,
|
|
88
|
-
reason: 'Rust installed but cargo is still not resolvable on PATH (~/.cargo/bin).',
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return { ok: true };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function buildDesktopBinary({
|
|
96
|
-
repoRoot = REPO_ROOT,
|
|
97
|
-
env = process.env,
|
|
98
|
-
homeDir = os.homedir(),
|
|
99
|
-
output = process.stdout,
|
|
100
|
-
errorOutput = process.stderr,
|
|
101
|
-
} = {}) {
|
|
102
|
-
if (!hasDesktopSource(repoRoot)) {
|
|
103
|
-
return { ok: false, reason: `Tauri source not found at ${srcTauriDir(repoRoot)}` };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const toolchain = ensureRustToolchain({ env, homeDir, output, errorOutput });
|
|
107
|
-
if (!toolchain.ok) return { ok: false, reason: toolchain.reason };
|
|
108
|
-
|
|
109
|
-
output.write('Building the Construct Chat desktop window (cargo build --release)…\n');
|
|
110
|
-
const build = spawnSync('cargo', ['build', '--release'], {
|
|
111
|
-
cwd: srcTauriDir(repoRoot),
|
|
112
|
-
env: cargoEnv(env, homeDir),
|
|
113
|
-
stdio: 'inherit',
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
if (build.status !== 0) {
|
|
117
|
-
return { ok: false, reason: 'cargo build --release failed (see output above).' };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const binaryPath = resolveDesktopBinary({ repoRoot, homeDir });
|
|
121
|
-
if (!binaryPath) {
|
|
122
|
-
return {
|
|
123
|
-
ok: false,
|
|
124
|
-
reason: 'Build reported success but no binary resolved at target/release/construct-chat.',
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
output.write(`Built Construct Chat window: ${binaryPath}\n`);
|
|
129
|
-
return { ok: true, binaryPath };
|
|
130
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/desktop-launcher.mjs — start dashboard and open Construct chat in the native window.
|
|
3
|
-
*
|
|
4
|
-
* Launch order when the construct-chat binary is missing: build it from the Tauri
|
|
5
|
-
* source on disk (dev/branch path), else download the prebuilt release asset, else
|
|
6
|
-
* fail loudly with remediation. Chrome app-mode is never an implicit substitute —
|
|
7
|
-
* the native window is the contract, and `construct chat --web` is the explicit
|
|
8
|
-
* browser path.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { spawn } from 'node:child_process';
|
|
12
|
-
import os from 'node:os';
|
|
13
|
-
import { startDashboard, readDashboardState } from '../service-manager.mjs';
|
|
14
|
-
import { hasGuiDisplay, resolveDesktopBinary } from './desktop-binary.mjs';
|
|
15
|
-
import { buildDesktopBinary, hasDesktopSource } from './desktop-build.mjs';
|
|
16
|
-
|
|
17
|
-
async function ensureDashboard({ cwd, env, errorOutput }) {
|
|
18
|
-
let dash = readDashboardState();
|
|
19
|
-
if (dash?.port) return dash;
|
|
20
|
-
|
|
21
|
-
const result = await startDashboard({
|
|
22
|
-
rootDir: cwd,
|
|
23
|
-
preferredPort: parseInt(env.PORT || env.DASHBOARD_PORT || '4242', 10),
|
|
24
|
-
});
|
|
25
|
-
if (result.started || result.reused) {
|
|
26
|
-
return { port: result.port, url: result.url || `http://127.0.0.1:${result.port}` };
|
|
27
|
-
}
|
|
28
|
-
errorOutput.write(`Failed to start dashboard: ${result.reason || 'unknown error'}\n`);
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function waitForChild(child) {
|
|
33
|
-
return new Promise((resolve) => {
|
|
34
|
-
let settled = false;
|
|
35
|
-
const finish = (code) => {
|
|
36
|
-
if (settled) return;
|
|
37
|
-
settled = true;
|
|
38
|
-
resolve(code ?? 0);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
if (child) {
|
|
42
|
-
child.on('exit', (code) => finish(code));
|
|
43
|
-
child.on('error', () => finish(1));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const onSignal = () => {
|
|
47
|
-
try { child?.kill('SIGTERM'); } catch { /* already exited */ }
|
|
48
|
-
finish(0);
|
|
49
|
-
};
|
|
50
|
-
process.once('SIGINT', onSignal);
|
|
51
|
-
process.once('SIGTERM', onSignal);
|
|
52
|
-
|
|
53
|
-
if (!child) finish(0);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function spawnDesktopBinary(bin, chatUrl, env) {
|
|
58
|
-
if (/\.(mjs|cjs|js)$/i.test(bin)) {
|
|
59
|
-
return spawn(process.execPath, [bin, '--url', chatUrl], {
|
|
60
|
-
env: { ...env, CONSTRUCT_CHAT_URL: chatUrl },
|
|
61
|
-
stdio: 'ignore',
|
|
62
|
-
detached: false,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
return spawn(bin, ['--url', chatUrl], {
|
|
66
|
-
env: { ...env, CONSTRUCT_CHAT_URL: chatUrl },
|
|
67
|
-
stdio: 'ignore',
|
|
68
|
-
detached: false,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async function tryDownloadBinary(env) {
|
|
73
|
-
try {
|
|
74
|
-
const { downloadDesktopBinary } = await import('../install/desktop-binary-download.mjs');
|
|
75
|
-
const result = await downloadDesktopBinary({ homeDir: os.homedir(), env });
|
|
76
|
-
if (result.status === 'downloaded') return resolveDesktopBinary();
|
|
77
|
-
} catch {
|
|
78
|
-
/* fall through to loud failure below */
|
|
79
|
-
}
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function resolveBinaryToLaunch({ cwd, env, output, errorOutput }) {
|
|
84
|
-
const existing = resolveDesktopBinary({ repoRoot: cwd, env });
|
|
85
|
-
if (existing) return existing;
|
|
86
|
-
|
|
87
|
-
if (hasDesktopSource(cwd)) {
|
|
88
|
-
const built = buildDesktopBinary({ repoRoot: cwd, env, output, errorOutput });
|
|
89
|
-
if (built.ok) return built.binaryPath;
|
|
90
|
-
errorOutput.write(`Could not build the Construct Chat window: ${built.reason}\n`);
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export async function runDesktopChat({
|
|
98
|
-
cwd = process.cwd(),
|
|
99
|
-
env = process.env,
|
|
100
|
-
output = process.stdout,
|
|
101
|
-
errorOutput = process.stderr,
|
|
102
|
-
binaryPath = null,
|
|
103
|
-
dashOverride = null,
|
|
104
|
-
} = {}) {
|
|
105
|
-
if (!hasGuiDisplay(env)) {
|
|
106
|
-
errorOutput.write('No graphical display available. Use `construct chat --plain` for terminal mode.\n');
|
|
107
|
-
return 1;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
let bin = binaryPath || resolveBinaryToLaunch({ cwd, env, output, errorOutput });
|
|
111
|
-
|
|
112
|
-
if (!bin && !hasDesktopSource(cwd)) {
|
|
113
|
-
bin = await tryDownloadBinary(env);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (!bin) {
|
|
117
|
-
errorOutput.write(
|
|
118
|
-
'The Construct Chat native window is unavailable.\n'
|
|
119
|
-
+ 'Build it with: npm run build:chat-desktop (requires the Rust toolchain from https://rustup.rs)\n'
|
|
120
|
-
+ 'Or open chat in a browser tab with: construct chat --web\n',
|
|
121
|
-
);
|
|
122
|
-
return 1;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const dash = dashOverride || await ensureDashboard({ cwd, env, errorOutput });
|
|
126
|
-
if (!dash) return 1;
|
|
127
|
-
|
|
128
|
-
const base = (dash.url || `http://127.0.0.1:${dash.port}`).replace(/\/$/, '');
|
|
129
|
-
const chatUrl = `${base}/chat/?surface=desktop`;
|
|
130
|
-
|
|
131
|
-
const child = spawnDesktopBinary(bin, chatUrl, env);
|
|
132
|
-
return waitForChild(child);
|
|
133
|
-
}
|
package/lib/chat/evidence.mjs
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
/** Per-turn evidence provenance for chat surfaces. */
|
|
2
|
-
|
|
3
|
-
export const EVIDENCE_SCHEMA_VERSION = 1;
|
|
4
|
-
|
|
5
|
-
const REPO_EVIDENCE_TOOLS = new Set(['read', 'grep', 'glob']);
|
|
6
|
-
const GLOB_META_RE = /[*?\[\]{}]/;
|
|
7
|
-
|
|
8
|
-
function freeze(value) {
|
|
9
|
-
if (Array.isArray(value)) value.forEach(freeze);
|
|
10
|
-
else if (value && typeof value === 'object') Object.values(value).forEach(freeze);
|
|
11
|
-
return Object.freeze(value);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function concreteTarget(value) {
|
|
15
|
-
if (typeof value !== 'string' || !value.trim() || GLOB_META_RE.test(value)) return null;
|
|
16
|
-
return value;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function toolResult(tool = {}) {
|
|
20
|
-
const result = tool.content ?? tool.result;
|
|
21
|
-
return result && typeof result === 'object' ? result : null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function concreteTargets(tool = {}) {
|
|
25
|
-
const result = toolResult(tool);
|
|
26
|
-
const name = String(tool.title || tool.kind || '');
|
|
27
|
-
if (!result) return [];
|
|
28
|
-
if (name === 'read') return [concreteTarget(result.path || tool.input?.path)].filter(Boolean);
|
|
29
|
-
|
|
30
|
-
// grep/glob requests describe a search, not a source. Only the concrete files
|
|
31
|
-
// returned by a successful result become evidence records.
|
|
32
|
-
const matches = Array.isArray(result.matches) ? result.matches : [];
|
|
33
|
-
return [...new Set(matches.map((match) => concreteTarget(typeof match === 'string' ? match : match?.file || match?.path)).filter(Boolean))];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function evidenceTarget(tool = {}) {
|
|
37
|
-
return concreteTargets(tool)[0] || null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function isSuccessfulEvidenceTool(tool = {}) {
|
|
41
|
-
if (!REPO_EVIDENCE_TOOLS.has(String(tool.title || tool.kind || ''))) return false;
|
|
42
|
-
if (tool.status !== 'completed') return false;
|
|
43
|
-
const result = toolResult(tool);
|
|
44
|
-
return Boolean(result && result.ok !== false && result.denied !== true && !result.error && concreteTargets(tool).length);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function evidenceRecords(tools = [], { turnId = null } = {}) {
|
|
48
|
-
const records = [];
|
|
49
|
-
for (const tool of tools) {
|
|
50
|
-
if (!isSuccessfulEvidenceTool(tool)) continue;
|
|
51
|
-
for (const target of concreteTargets(tool)) {
|
|
52
|
-
records.push(freeze({
|
|
53
|
-
schemaVersion: EVIDENCE_SCHEMA_VERSION,
|
|
54
|
-
recordId: `${turnId || 'turn'}:${String(tool.id || '')}:${target}`,
|
|
55
|
-
turnId: turnId ? String(turnId) : null,
|
|
56
|
-
toolId: String(tool.id || ''),
|
|
57
|
-
tool: String(tool.title || tool.kind),
|
|
58
|
-
requestedTarget: String(tool.input?.path || tool.input?.glob || tool.input?.pattern || ''),
|
|
59
|
-
target,
|
|
60
|
-
sourceId: `repo:${target}`,
|
|
61
|
-
completion: 'completed',
|
|
62
|
-
result: 'success',
|
|
63
|
-
}));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return freeze(records);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function escapeRegExp(value) {
|
|
70
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function citationMatches(text, record) {
|
|
74
|
-
const target = String(record?.target || '');
|
|
75
|
-
const sourceId = String(record?.sourceId || '');
|
|
76
|
-
if (!target) return false;
|
|
77
|
-
const escapedTarget = escapeRegExp(target);
|
|
78
|
-
const targetPattern = new RegExp(`(^|[^A-Za-z0-9_./-])${escapedTarget}(?=$|[^A-Za-z0-9_./-])`);
|
|
79
|
-
const sourcePattern = sourceId
|
|
80
|
-
? new RegExp(`\\[source:\\s*${escapeRegExp(sourceId)}\\s*\\]`, 'i')
|
|
81
|
-
: null;
|
|
82
|
-
return targetPattern.test(text) || Boolean(sourcePattern?.test(text));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function verdict({ status, records = [], citations = [], reasonCodes = [] }) {
|
|
86
|
-
return freeze({
|
|
87
|
-
schemaVersion: EVIDENCE_SCHEMA_VERSION,
|
|
88
|
-
status,
|
|
89
|
-
records: [...records],
|
|
90
|
-
citations: [...new Set(citations)],
|
|
91
|
-
reasonCodes: [...reasonCodes],
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function deriveEvidenceVerdict({ id = null, overlay = null, tools = [], assistant = '', evidenceVisible = true, migration = false } = {}) {
|
|
96
|
-
const required = overlay?.assumptionsBlocked || overlay?.externalResearch?.required;
|
|
97
|
-
const migrationCode = migration ? ['legacy_snapshot_without_evidence'] : [];
|
|
98
|
-
if (!required) return verdict({ status: 'not_applicable', reasonCodes: ['evidence_not_required', ...migrationCode] });
|
|
99
|
-
if (!evidenceVisible) return verdict({ status: 'insufficient_evidence', reasonCodes: ['evidence_layer_hidden', ...migrationCode] });
|
|
100
|
-
|
|
101
|
-
const records = evidenceRecords(tools, { turnId: id });
|
|
102
|
-
if (!records.length) return verdict({ status: 'insufficient_evidence', records, reasonCodes: ['no_successful_concrete_evidence', ...migrationCode] });
|
|
103
|
-
const text = String(assistant || '');
|
|
104
|
-
const citations = records.filter((record) => citationMatches(text, record)).map((record) => record.target);
|
|
105
|
-
if (!citations.length) return verdict({ status: 'uncited_evidence', records, citations, reasonCodes: ['evidence_not_cited', ...migrationCode] });
|
|
106
|
-
return verdict({
|
|
107
|
-
status: citations.length === records.length ? 'verified' : 'partially_verified',
|
|
108
|
-
records,
|
|
109
|
-
citations,
|
|
110
|
-
reasonCodes: [citations.length === records.length ? 'all_recorded_evidence_cited' : 'some_recorded_evidence_uncited', ...migrationCode],
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function migrateEvidenceVerdict(turn = {}) {
|
|
115
|
-
if (turn.evidence?.schemaVersion === EVIDENCE_SCHEMA_VERSION) return turn.evidence;
|
|
116
|
-
if (turn.evidence?.status) {
|
|
117
|
-
const records = (turn.evidence.records || []).map((record, index) => freeze({
|
|
118
|
-
schemaVersion: EVIDENCE_SCHEMA_VERSION,
|
|
119
|
-
recordId: record.recordId || `${turn.id || 'turn'}:${record.toolId || ''}:${record.target || index}`,
|
|
120
|
-
turnId: record.turnId || turn.id || null,
|
|
121
|
-
toolId: record.toolId || '',
|
|
122
|
-
tool: record.tool || 'unknown',
|
|
123
|
-
requestedTarget: record.requestedTarget || record.target || '',
|
|
124
|
-
target: record.target || '',
|
|
125
|
-
sourceId: record.sourceId || `repo:${record.target || ''}`,
|
|
126
|
-
completion: record.completion || 'completed',
|
|
127
|
-
result: record.result || 'success',
|
|
128
|
-
})).filter((record) => record.target);
|
|
129
|
-
return verdict({
|
|
130
|
-
status: turn.evidence.status,
|
|
131
|
-
records,
|
|
132
|
-
citations: turn.evidence.citations || [],
|
|
133
|
-
reasonCodes: [...(turn.evidence.reasonCodes || []), 'legacy_evidence_unversioned'],
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
return deriveEvidenceVerdict({ ...turn, migration: true });
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function evidenceNotice(verdict) {
|
|
140
|
-
if (!verdict || verdict.status === 'not_applicable' || verdict.status === 'verified') return null;
|
|
141
|
-
if (verdict.reasonCodes?.includes('evidence_layer_hidden')) return 'Evidence tools were hidden for this turn, so no source was verified';
|
|
142
|
-
if (verdict.status === 'uncited_evidence') return 'Evidence was read but the answer does not cite its recorded paths — treat claims as unverified';
|
|
143
|
-
if (verdict.status === 'partially_verified') return 'Only some recorded evidence paths are cited — treat uncited claims as unverified';
|
|
144
|
-
return 'Answer produced without successful recorded repo evidence — treat as unverified';
|
|
145
|
-
}
|
package/lib/chat/export.mjs
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/export.mjs — plain markdown export for construct chat turns.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import fs from 'node:fs';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import { resolveProjectScopedPath } from '../project-root.mjs';
|
|
8
|
-
import { summarizeSources } from './tui/turn-present.mjs';
|
|
9
|
-
import { rehydrateContinuation } from './context-continuation.mjs';
|
|
10
|
-
import { createContinuationResolver } from './continuation-source.mjs';
|
|
11
|
-
|
|
12
|
-
function exportDir({ cwd }) {
|
|
13
|
-
const base = resolveProjectScopedPath('chat-sessions', { cwd, ensureDir: true });
|
|
14
|
-
const dir = path.join(base, 'exports');
|
|
15
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
16
|
-
return dir;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Render a persisted continuation packet by rehydrating it through the one shared
|
|
20
|
-
// resolver (AC3): reconstructible layers re-derive via the same composer a live
|
|
21
|
-
// turn runs, elided layers report as summarized, and the budget accounting stays
|
|
22
|
-
// re-verifiable in the export.
|
|
23
|
-
|
|
24
|
-
function continuationToMarkdown(packet, { cwd }) {
|
|
25
|
-
if (!packet || typeof packet !== 'object') return '';
|
|
26
|
-
const resolver = createContinuationResolver({ cwd });
|
|
27
|
-
const layers = rehydrateContinuation(packet, { resolveSource: resolver.resolveSource });
|
|
28
|
-
const budget = packet.budget || {};
|
|
29
|
-
const lines = ['### context continuation', '',
|
|
30
|
-
`compacted to fit ~${budget.triggerTokens ?? 'n/a'} tokens — retained ${budget.retainedTokens ?? 0}, summarized ${budget.elidedTokens ?? 0}, referenced ${budget.referencedCount ?? 0}`];
|
|
31
|
-
if (packet.blocker) {
|
|
32
|
-
lines.push('', `blocker: ${packet.blocker.reason} (required ${packet.blocker.requiredTokens} > budget ${packet.blocker.budgetTokens})`);
|
|
33
|
-
}
|
|
34
|
-
const reconstructed = layers.filter((l) => l.sourceId || l.elided);
|
|
35
|
-
if (reconstructed.length) {
|
|
36
|
-
lines.push('', 'layers on rehydrate:');
|
|
37
|
-
for (const layer of reconstructed) {
|
|
38
|
-
if (layer.elided) lines.push(`- ${layer.kind}: summarized (elided)`);
|
|
39
|
-
else lines.push(`- ${layer.kind}: ${layer.resolved ? `re-derived from ${layer.sourceId}` : `unresolved (${layer.sourceId})`}`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return lines.join('\n');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function turnToMarkdown(turn, { cwd = process.cwd() } = {}) {
|
|
46
|
-
const lines = [`## you`, '', turn.userText || '', ''];
|
|
47
|
-
const evidence = turn.evidence || null;
|
|
48
|
-
if (evidence) lines.push(`evidence: ${evidence.status}${evidence.reasonCodes?.length ? ` (${evidence.reasonCodes.join(', ')})` : ''}`, '');
|
|
49
|
-
const src = summarizeSources(evidence?.records?.map((record) => ({ tool: record.tool, ref: record.target })) || turn.sources || []);
|
|
50
|
-
if (src.total) {
|
|
51
|
-
lines.push(`sources: ${src.refs.join(', ')}`, '');
|
|
52
|
-
}
|
|
53
|
-
lines.push('## construct', '', turn.assistant || '(no answer)', '');
|
|
54
|
-
if (turn.continuation) {
|
|
55
|
-
const cont = continuationToMarkdown(turn.continuation, { cwd });
|
|
56
|
-
if (cont) lines.push('', cont, '');
|
|
57
|
-
}
|
|
58
|
-
return lines.join('\n');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function exportTurns(turnBlocks, { scope = 'last', cwd = process.cwd() } = {}) {
|
|
62
|
-
const turns = turnBlocks.filter((item) => item.kind === 'turn').map((item) => item.block);
|
|
63
|
-
if (!turns.length) return { ok: false, error: 'no turns to export' };
|
|
64
|
-
|
|
65
|
-
let selected = turns;
|
|
66
|
-
if (scope === 'last') selected = [turns[turns.length - 1]];
|
|
67
|
-
else if (scope === 'turn' && turns.length) selected = [turns[turns.length - 1]];
|
|
68
|
-
|
|
69
|
-
const body = selected.map((turn) => turnToMarkdown(turn, { cwd })).join('\n---\n\n');
|
|
70
|
-
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
71
|
-
const file = path.join(exportDir({ cwd }), `${stamp}-${scope}-answer.md`);
|
|
72
|
-
fs.writeFileSync(file, `${body}\n`, 'utf8');
|
|
73
|
-
return { ok: true, path: file, count: selected.length };
|
|
74
|
-
}
|