@geraldmaron/construct 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -10
- package/bin/construct +286 -220
- package/bin/construct-postinstall.mjs +0 -21
- package/commands/work/optimize-prompts.md +1 -1
- package/examples/distribution/sources/adr.md +2 -2
- package/examples/seed-observations/decisions.md +1 -1
- package/lib/artifact-loop-core.mjs +412 -0
- package/lib/artifact-manifest.mjs +6 -0
- package/lib/artifact-release-gate.mjs +115 -49
- package/lib/audit-rules.mjs +2 -4
- package/lib/audit-skills.mjs +32 -20
- package/lib/audit-specialists.mjs +44 -26
- package/lib/auto-docs.mjs +6 -5
- package/lib/brand-prose.mjs +3 -3
- package/lib/brand-tokens.mjs +2 -2
- package/lib/certification/artifact-fixtures.mjs +4 -1
- package/lib/certification/demo-parity.mjs +6 -32
- package/lib/certification/real-llm-scenarios.mjs +58 -13
- package/lib/certification/role-cards.mjs +7 -6
- package/lib/certification/role-overlays.mjs +4 -4
- package/lib/certification/runner.mjs +9 -2
- package/lib/certification/skill-inventory.mjs +34 -22
- package/lib/certification/skill-scenarios.mjs +21 -8
- package/lib/certification/specialist-contracts.mjs +4 -3
- package/lib/certification/specialist-scenarios.mjs +7 -6
- package/lib/certification/status.mjs +5 -4
- package/lib/cli-commands.mjs +56 -57
- package/lib/comment-lint.mjs +1 -5
- package/lib/completions.mjs +20 -2
- package/lib/config/legacy-config-migration.mjs +125 -0
- package/lib/config/schema.mjs +5 -5
- package/lib/config/source-targets.mjs +80 -0
- package/lib/contracts/validate.mjs +119 -51
- package/lib/decisions/golden.mjs +3 -2
- package/lib/decisions/registry.mjs +11 -6
- package/lib/demo-project.mjs +188 -0
- package/lib/demo-recording.mjs +9 -34
- package/lib/demo-script.mjs +2 -2
- package/lib/demo-surface.mjs +13 -131
- package/lib/demo-tour-renderer.mjs +92 -0
- package/lib/demo.mjs +69 -54
- package/lib/diagram-export.mjs +63 -14
- package/lib/doctor/source-checkout.mjs +3 -4
- package/lib/doctor/watchers/consistency.mjs +90 -22
- package/lib/doctor/watchers/credential-parity.mjs +97 -0
- package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
- package/lib/doctor/watchers/service-health.mjs +4 -32
- package/lib/document-export.mjs +73 -9
- package/lib/document-extract.mjs +10 -198
- package/lib/embed/artifact.mjs +2 -3
- package/lib/embed/cli.mjs +1 -1
- package/lib/embed/config.mjs +1 -2
- package/lib/embed/customer-profiles.mjs +1 -1
- package/lib/embed/daemon.mjs +5 -6
- package/lib/embed/demand-fetch.mjs +114 -2
- package/lib/embed/docs-lifecycle.mjs +9 -5
- package/lib/embed/intake-metrics.mjs +2 -2
- package/lib/embed/notifications.mjs +2 -3
- package/lib/embed/recommendation-store.mjs +25 -22
- package/lib/embed/role-framing.mjs +4 -9
- package/lib/embedded-contract/capability.mjs +4 -2
- package/lib/embedded-contract/contract-version.mjs +1 -1
- package/lib/env-config.mjs +2 -2
- package/lib/flavors/loader.mjs +21 -19
- package/lib/graph/build-from-registry.mjs +31 -7
- package/lib/graph/staleness.mjs +1 -1
- package/lib/headhunt.mjs +82 -35
- package/lib/hooks/agent-tracker.mjs +2 -2
- package/lib/hooks/config-protection.mjs +1 -1
- package/lib/hooks/registry-sync.mjs +3 -3
- package/lib/host-capabilities.mjs +5 -5
- package/lib/improvement/controller.mjs +2 -2
- package/lib/init-unified.mjs +19 -21
- package/lib/intake/classify.mjs +26 -0
- package/lib/intake/daemon.mjs +2 -3
- package/lib/intake/prepare.mjs +10 -3
- package/lib/intake/session-prelude.mjs +1 -1
- package/lib/intake/tables/creative.mjs +9 -9
- package/lib/intake/tables/operations.mjs +4 -4
- package/lib/intake/tables/research.mjs +2 -2
- package/lib/integrations/intake-integrations.mjs +9 -10
- package/lib/knowledge/research-store.mjs +2 -2
- package/lib/mcp/server.mjs +104 -61
- package/lib/mcp/tool-budget.mjs +1 -46
- package/lib/mcp/tool-recovery.mjs +44 -0
- package/lib/mcp/tools/artifact-author.mjs +36 -0
- package/lib/mcp/tools/find-tool.mjs +86 -0
- package/lib/mcp/tools/orchestration-run.mjs +107 -69
- package/lib/mcp/tools/project.mjs +4 -3
- package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
- package/lib/mcp/tools/skills.mjs +39 -22
- package/lib/mcp/tools/telemetry.mjs +2 -1
- package/lib/mcp/tools/workflow.mjs +1 -1
- package/lib/mcp-platform-config.mjs +7 -5
- package/lib/migrations/index.mjs +4 -2
- package/lib/migrations/v2-unified-registry.mjs +35 -0
- package/lib/model-router.mjs +203 -36
- package/lib/models/catalog.mjs +25 -9
- package/lib/models/execution-capability-profile.mjs +3 -3
- package/lib/models/execution-policy.mjs +7 -6
- package/lib/models/provider-poll.mjs +18 -4
- package/lib/opencode-config.mjs +56 -1
- package/lib/opencode-runtime-plugin.mjs +13 -10
- package/lib/oracle/artifact-gate.mjs +14 -4
- package/lib/oracle/cli.mjs +2 -0
- package/lib/oracle/dispatch.mjs +18 -1
- package/lib/oracle/execute.mjs +39 -7
- package/lib/oracle/index.mjs +1 -1
- package/lib/oracle/org-graph.mjs +10 -5
- package/lib/oracle/policy.mjs +6 -0
- package/lib/oracle/read-model.mjs +103 -6
- package/lib/oracle/reconcile.mjs +31 -4
- package/lib/oracle/remediation-dispatch.mjs +53 -0
- package/lib/oracle/routing.mjs +5 -0
- package/lib/oracle/synthesize.mjs +80 -3
- package/lib/orchestration/routing-tables.mjs +44 -10
- package/lib/orchestration/runtime.mjs +2 -0
- package/lib/orchestration/worker.mjs +1 -1
- package/lib/orchestration-policy.mjs +351 -62
- package/lib/overrides/resolver.mjs +1 -12
- package/lib/parity.mjs +37 -11
- package/lib/policy/engine.mjs +80 -15
- package/lib/prompt-composer.js +51 -15
- package/lib/prompt-metadata.mjs +3 -2
- package/lib/providers/connection-probe.mjs +58 -0
- package/lib/providers/copilot-auth.mjs +1 -1
- package/lib/providers/credential-bootstrap.mjs +1 -1
- package/lib/providers/op-run.mjs +3 -4
- package/lib/providers/secret-resolver.mjs +31 -0
- package/lib/publish-tooling.mjs +3 -11
- package/lib/publish.mjs +12 -29
- package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
- package/lib/reflect.mjs +2 -2
- package/lib/registry/agent-manifest.mjs +190 -0
- package/lib/registry/assemble.mjs +133 -0
- package/lib/registry/catalog.mjs +171 -0
- package/lib/registry/cli.mjs +590 -6
- package/lib/registry/consolidation.mjs +5 -4
- package/lib/registry/docs-sync.mjs +67 -0
- package/lib/registry/loader.mjs +147 -0
- package/lib/registry/org-io.mjs +76 -0
- package/lib/registry/retired-paths.mjs +71 -0
- package/lib/registry/surface-map.mjs +5 -7
- package/lib/registry/validator.mjs +438 -0
- package/lib/research-execution-policy.mjs +173 -0
- package/lib/roles/catalog.mjs +4 -9
- package/lib/roles/fence.mjs +107 -1
- package/lib/roles/flavor-bindings.mjs +68 -0
- package/lib/roles/gateway.mjs +140 -29
- package/lib/roles/manifest.mjs +22 -13
- package/lib/roles/router.mjs +1 -1
- package/lib/runtime-env.mjs +1 -1
- package/lib/scopes/enrich.mjs +67 -0
- package/lib/scopes/hooks.mjs +12 -0
- package/lib/{profiles → scopes}/lifecycle.mjs +70 -70
- package/lib/scopes/loader.mjs +104 -0
- package/lib/scopes/rebrand.mjs +32 -0
- package/lib/scopes/research-profile.mjs +16 -0
- package/lib/scopes/teams.mjs +98 -0
- package/lib/service-manager.mjs +1 -117
- package/lib/setup-prompts.mjs +1 -1
- package/lib/setup.mjs +17 -28
- package/lib/skills/composition-graph.mjs +98 -0
- package/lib/skills/router.mjs +80 -0
- package/lib/specialist-contracts.mjs +17 -18
- package/lib/specialists/postconditions.mjs +2 -2
- package/lib/specialists/prompt-schema.mjs +6 -5
- package/lib/specialists/roster.mjs +7 -12
- package/lib/specialists/schema.mjs +9 -6
- package/lib/status.mjs +25 -90
- package/lib/storage/file-lock.mjs +6 -3
- package/lib/telemetry/skill-calls.mjs +1 -1
- package/lib/templates/doc-presentation.mjs +63 -0
- package/lib/templates/visual-requirements.mjs +35 -2
- package/lib/term-format.mjs +107 -2
- package/lib/test-env-setup.mjs +21 -0
- package/lib/ui/components.mjs +42 -0
- package/lib/ui/glyphs.mjs +58 -0
- package/lib/ui/links.mjs +115 -0
- package/lib/ui/theme.mjs +108 -0
- package/lib/uninstall/uninstall.mjs +2 -1
- package/lib/validator.mjs +45 -8
- package/lib/validators/skill-effectiveness.mjs +174 -0
- package/lib/validators/skills.mjs +1 -1
- package/package.json +12 -7
- package/personas/construct.md +12 -3
- package/platforms/claude/CLAUDE.md +1 -1
- package/rules/common/beads-hygiene.md +52 -0
- package/rules/common/neurodivergent-output.md +4 -7
- package/rules/common/research.md +2 -0
- package/scripts/sync-specialists.mjs +260 -49
- package/skills/ai/prompt-optimizer.md +3 -3
- package/skills/brand/output-vibe.md +48 -0
- package/skills/docs/adr-workflow.md +3 -0
- package/skills/docs/backlog-proposal-workflow.md +3 -0
- package/skills/docs/codebase-research-workflow.md +5 -2
- package/skills/docs/customer-profile-workflow.md +3 -0
- package/skills/docs/document-ingest-workflow.md +3 -0
- package/skills/docs/evidence-ingest-workflow.md +4 -1
- package/skills/docs/init-project.md +1 -1
- package/skills/docs/prd-workflow.md +1 -1
- package/skills/docs/prfaq-workflow.md +3 -0
- package/skills/docs/product-intelligence-workflow.md +4 -1
- package/skills/docs/product-signal-workflow.md +3 -0
- package/skills/docs/research-workflow.md +13 -6
- package/skills/docs/runbook-workflow.md +3 -0
- package/skills/docs/strategy-workflow.md +3 -0
- package/skills/docs/user-research-workflow.md +6 -3
- package/skills/operating/orchestration-reference.md +1 -1
- package/skills/roles/{engineer.ai.md → ai-engineer.md} +2 -2
- package/skills/roles/architect.ai-systems.md +1 -1
- package/skills/roles/architect.data.md +1 -1
- package/skills/roles/architect.enterprise.md +1 -1
- package/skills/roles/architect.integration.md +1 -1
- package/skills/roles/architect.md +1 -1
- package/skills/roles/architect.platform.md +1 -1
- package/skills/roles/{product-manager.business-strategy.md → business-strategist.md} +3 -3
- package/skills/roles/data-analyst.experiment.md +1 -1
- package/skills/roles/data-analyst.md +1 -1
- package/skills/roles/data-analyst.product-intelligence.md +1 -1
- package/skills/roles/data-analyst.product.md +1 -1
- package/skills/roles/data-analyst.telemetry.md +1 -1
- package/skills/roles/{engineer.data.md → data-engineer.md} +2 -2
- package/skills/roles/data-engineer.pipeline.md +2 -2
- package/skills/roles/data-engineer.vector-retrieval.md +2 -2
- package/skills/roles/data-engineer.warehouse.md +2 -2
- package/skills/roles/debugger.md +1 -1
- package/skills/roles/designer.accessibility.md +1 -1
- package/skills/roles/designer.md +1 -1
- package/skills/roles/{reviewer.devil-advocate.md → devil-advocate.md} +2 -2
- package/skills/roles/{operator.docs.md → docs-keeper.md} +3 -3
- package/skills/roles/engineer.md +3 -7
- package/skills/roles/{reviewer.evaluator.md → evaluator.md} +2 -2
- package/skills/roles/{researcher.explorer.md → explorer.md} +2 -2
- package/skills/roles/{operator.md → operations.md} +2 -5
- package/skills/roles/orchestrator.md +1 -1
- package/skills/roles/{engineer.platform.md → platform-engineer.md} +2 -2
- package/skills/roles/product-manager.ai-product.md +1 -1
- package/skills/roles/product-manager.enterprise.md +1 -1
- package/skills/roles/product-manager.growth.md +1 -2
- package/skills/roles/product-manager.md +1 -2
- package/skills/roles/product-manager.platform.md +1 -1
- package/skills/roles/product-manager.product.md +1 -1
- package/skills/roles/qa.ai-eval.md +1 -1
- package/skills/roles/qa.api-contract.md +1 -1
- package/skills/roles/qa.data-pipeline.md +1 -1
- package/skills/roles/qa.md +1 -1
- package/skills/roles/qa.web-ui.md +1 -1
- package/skills/roles/{operator.release.md → release-manager.md} +3 -3
- package/skills/roles/researcher.md +1 -1
- package/skills/roles/reviewer.md +1 -1
- package/skills/roles/security.ai.md +1 -1
- package/skills/roles/security.appsec.md +1 -1
- package/skills/roles/security.cloud.md +1 -1
- package/skills/roles/security.legal-compliance.md +1 -1
- package/skills/roles/security.md +1 -1
- package/skills/roles/security.privacy.md +1 -1
- package/skills/roles/security.supply-chain.md +1 -1
- package/skills/roles/{operator.sre.md → sre.md} +3 -3
- package/skills/roles/{qa.test-automation.md → test-automation.md} +2 -2
- package/skills/roles/{reviewer.trace.md → trace-reviewer.md} +2 -2
- package/skills/roles/{researcher.ux.md → ux-researcher.md} +2 -2
- package/skills/routing.json +18 -0
- package/skills/routing.md +1 -1
- package/specialists/artifact-manifest.json +2 -1
- package/specialists/audit-enrichments.json +14 -14
- package/specialists/org/contracts/accessibility-to-qa.json +37 -0
- package/specialists/org/contracts/any-to-business-strategist.json +57 -0
- package/specialists/org/contracts/any-to-debugger.json +38 -0
- package/specialists/org/contracts/any-to-designer.json +33 -0
- package/specialists/org/contracts/any-to-docs-keeper.json +34 -0
- package/specialists/org/contracts/any-to-explorer.json +39 -0
- package/specialists/org/contracts/any-to-sre-incident.json +33 -0
- package/specialists/org/contracts/any-to-trace-reviewer.json +32 -0
- package/specialists/org/contracts/architect-to-ai-engineer.json +32 -0
- package/specialists/org/contracts/architect-to-data-engineer.json +52 -0
- package/specialists/org/contracts/architect-to-devil-advocate.json +38 -0
- package/specialists/org/contracts/architect-to-engineer.json +34 -0
- package/specialists/org/contracts/architect-to-evaluator.json +59 -0
- package/specialists/org/contracts/architect-to-legal-compliance.json +55 -0
- package/specialists/org/contracts/architect-to-operations.json +45 -0
- package/specialists/org/contracts/architect-to-platform-engineer.json +42 -0
- package/specialists/org/contracts/business-strategist-to-product-manager.json +39 -0
- package/specialists/org/contracts/construct-to-orchestrator.json +36 -0
- package/specialists/org/contracts/construct-to-rd-lead.json +53 -0
- package/specialists/org/contracts/data-analyst-to-product-manager.json +43 -0
- package/specialists/org/contracts/data-engineer-to-platform-engineer.json +36 -0
- package/specialists/org/contracts/designer-to-accessibility.json +36 -0
- package/specialists/org/contracts/engineer-to-qa.json +34 -0
- package/specialists/org/contracts/engineer-to-reviewer.json +52 -0
- package/specialists/org/contracts/explorer-to-engineer.json +38 -0
- package/specialists/org/contracts/legal-compliance-to-release-manager.json +43 -0
- package/specialists/org/contracts/platform-engineer-to-engineer.json +31 -0
- package/specialists/org/contracts/product-manager-to-architect.json +71 -0
- package/specialists/org/contracts/product-manager-to-data-analyst.json +49 -0
- package/specialists/org/contracts/product-manager-to-ux-researcher.json +51 -0
- package/specialists/org/contracts/qa-to-release-manager.json +42 -0
- package/specialists/org/contracts/qa-to-test-automation.json +42 -0
- package/specialists/org/contracts/rd-lead-to-architect.json +38 -0
- package/specialists/org/contracts/researcher-to-architect.json +40 -0
- package/specialists/org/contracts/researcher-to-product-manager.json +57 -0
- package/specialists/org/contracts/reviewer-to-security.json +41 -0
- package/specialists/org/contracts/sre-to-release-manager.json +36 -0
- package/specialists/org/contracts/test-automation-to-engineer.json +34 -0
- package/specialists/org/contracts/trace-reviewer-to-sre.json +41 -0
- package/specialists/org/contracts/user-to-construct.json +30 -0
- package/specialists/org/groups/engineering-group.json +43 -0
- package/specialists/org/groups/governance-group.json +38 -0
- package/specialists/org/groups/operations-group.json +41 -0
- package/specialists/org/groups/product-group.json +46 -0
- package/specialists/org/groups/quality-group.json +42 -0
- package/specialists/org/groups/strategy-group.json +41 -0
- package/specialists/org/policies/action-approval.json +13 -0
- package/specialists/org/policies/agents-routing.json +13 -0
- package/specialists/org/policies/architecture.json +20 -0
- package/specialists/org/policies/bash-safety.json +13 -0
- package/specialists/org/policies/beads-hygiene.json +13 -0
- package/specialists/org/policies/bootstrap-state.json +13 -0
- package/specialists/org/policies/code-review.json +13 -0
- package/specialists/org/policies/coding-style.json +13 -0
- package/specialists/org/policies/comments.json +13 -0
- package/specialists/org/policies/commit-approval.json +13 -0
- package/specialists/org/policies/contract-preconditions.json +13 -0
- package/specialists/org/policies/deployment.json +20 -0
- package/specialists/org/policies/description.json +14 -0
- package/specialists/org/policies/design-approval.json +19 -0
- package/specialists/org/policies/doc-ownership.json +13 -0
- package/specialists/org/policies/file-path-fence.json +13 -0
- package/specialists/org/policies/framing.json +13 -0
- package/specialists/org/policies/git-workflow.json +13 -0
- package/specialists/org/policies/incident-response.json +15 -0
- package/specialists/org/policies/intake-triage.json +15 -0
- package/specialists/org/policies/neurodivergent-output.json +13 -0
- package/specialists/org/policies/no-fabrication.json +13 -0
- package/specialists/org/policies/patterns.json +13 -0
- package/specialists/org/policies/quality-gate-approval.json +17 -0
- package/specialists/org/policies/release-gates.json +13 -0
- package/specialists/org/policies/research-evidence.json +13 -0
- package/specialists/org/policies/review-before-change.json +13 -0
- package/specialists/org/policies/rollback.json +15 -0
- package/specialists/org/policies/scope-change.json +20 -0
- package/specialists/org/policies/secret-scan.json +13 -0
- package/specialists/org/policies/security-approval.json +17 -0
- package/specialists/org/policies/security.json +13 -0
- package/specialists/org/policies/session-efficiency.json +13 -0
- package/specialists/org/policies/skill-routing.json +13 -0
- package/specialists/org/policies/strategic-prioritization.json +17 -0
- package/specialists/org/policies/testing.json +13 -0
- package/specialists/org/policies/tool-invisibility.json +14 -0
- package/specialists/org/scopes/creative.json +54 -0
- package/specialists/org/scopes/operations.json +51 -0
- package/specialists/org/scopes/research.json +60 -0
- package/specialists/org/scopes/rnd.json +81 -0
- package/specialists/org/specialists/cx-accessibility.json +69 -0
- package/specialists/org/specialists/cx-ai-engineer.json +75 -0
- package/specialists/org/specialists/cx-architect.json +89 -0
- package/specialists/org/specialists/cx-business-strategist.json +72 -0
- package/specialists/org/specialists/cx-data-analyst.json +68 -0
- package/specialists/org/specialists/cx-data-engineer.json +71 -0
- package/specialists/org/specialists/cx-debugger.json +75 -0
- package/specialists/org/specialists/cx-designer.json +85 -0
- package/specialists/org/specialists/cx-devil-advocate.json +71 -0
- package/specialists/org/specialists/cx-docs-keeper.json +82 -0
- package/specialists/org/specialists/cx-engineer.json +106 -0
- package/specialists/org/specialists/cx-evaluator.json +69 -0
- package/specialists/org/specialists/cx-explorer.json +69 -0
- package/specialists/org/specialists/cx-legal-compliance.json +74 -0
- package/specialists/org/specialists/cx-operations.json +72 -0
- package/specialists/org/specialists/cx-oracle.json +46 -0
- package/specialists/org/specialists/cx-orchestrator.json +81 -0
- package/specialists/org/specialists/cx-platform-engineer.json +80 -0
- package/specialists/org/specialists/cx-product-manager.json +102 -0
- package/specialists/org/specialists/cx-qa.json +79 -0
- package/specialists/org/specialists/cx-rd-lead.json +73 -0
- package/specialists/org/specialists/cx-release-manager.json +77 -0
- package/specialists/org/specialists/cx-researcher.json +82 -0
- package/specialists/org/specialists/cx-reviewer.json +71 -0
- package/specialists/org/specialists/cx-security.json +91 -0
- package/specialists/org/specialists/cx-sre.json +94 -0
- package/specialists/org/specialists/cx-test-automation.json +69 -0
- package/specialists/org/specialists/cx-trace-reviewer.json +69 -0
- package/specialists/org/specialists/cx-ux-researcher.json +68 -0
- package/specialists/org/teams/accessibility-team.json +39 -0
- package/specialists/org/teams/design-team.json +40 -0
- package/specialists/org/teams/engineering-team.json +50 -0
- package/specialists/org/teams/governance-team.json +41 -0
- package/specialists/org/teams/operations-team.json +46 -0
- package/specialists/org/teams/product-management-team.json +45 -0
- package/specialists/org/teams/quality-team.json +49 -0
- package/specialists/org/teams/research-team.json +39 -0
- package/specialists/org/teams/strategy-team.json +48 -0
- package/specialists/org/teams/ux-research-team.json +39 -0
- package/specialists/prompts/_shared/validation-contract.md +1 -1
- package/specialists/prompts/_team-template.md +10 -0
- package/specialists/prompts/cx-accessibility.md +1 -0
- package/specialists/prompts/cx-ai-engineer.md +1 -1
- package/specialists/prompts/cx-business-strategist.md +1 -1
- package/specialists/prompts/cx-data-engineer.md +1 -1
- package/specialists/prompts/cx-designer.md +1 -0
- package/specialists/prompts/cx-devil-advocate.md +1 -1
- package/specialists/prompts/cx-docs-keeper.md +1 -1
- package/specialists/prompts/cx-evaluator.md +1 -1
- package/specialists/prompts/cx-explorer.md +1 -1
- package/specialists/prompts/cx-operations.md +1 -1
- package/specialists/prompts/cx-oracle.md +7 -3
- package/specialists/prompts/cx-orchestrator.md +17 -1
- package/specialists/prompts/cx-platform-engineer.md +1 -1
- package/specialists/prompts/cx-product-manager.md +2 -0
- package/specialists/prompts/cx-release-manager.md +1 -1
- package/specialists/prompts/cx-researcher.md +7 -4
- package/specialists/prompts/cx-sre.md +1 -1
- package/specialists/prompts/cx-test-automation.md +2 -2
- package/specialists/prompts/cx-trace-reviewer.md +3 -3
- package/specialists/prompts/cx-ux-researcher.md +2 -1
- package/templates/demos/scripts/architecture-review-adr.json +30 -0
- package/templates/demos/scripts/capability-contract.json +25 -0
- package/templates/demos/scripts/intake-triage.json +25 -0
- package/templates/demos/scripts/profile-doctor-health.json +25 -0
- package/templates/demos/tapes/agentic-platforms-prd.tape +2 -2
- package/templates/demos/tapes/architecture-review-adr.tape +44 -0
- package/templates/demos/tapes/capability-contract.tape +39 -0
- package/templates/demos/tapes/intake-triage.tape +39 -0
- package/templates/demos/tapes/profile-doctor-health.tape +39 -0
- package/templates/distribution/construct-brand.typ +23 -18
- package/templates/distribution/construct-decision.typ +1 -1
- package/templates/distribution/construct-pdf.typ +1 -1
- package/templates/distribution/construct-prd.typ +1 -1
- package/templates/distribution/construct-research.typ +1 -1
- package/templates/distribution/mermaid-puppeteer.json +8 -0
- package/templates/docs/persona-artifact.md +1 -1
- package/templates/docs/prd.md +6 -0
- package/lib/boundary.mjs +0 -127
- package/lib/certification/dashboard-api.mjs +0 -71
- package/lib/chat/cli.mjs +0 -333
- package/lib/chat/command-suggest.mjs +0 -161
- package/lib/chat/commands.mjs +0 -215
- package/lib/chat/config.mjs +0 -142
- package/lib/chat/context-compactor.mjs +0 -250
- package/lib/chat/context-continuation.mjs +0 -253
- package/lib/chat/continuation-source.mjs +0 -58
- package/lib/chat/demo-guide.mjs +0 -61
- package/lib/chat/design-tokens.mjs +0 -91
- package/lib/chat/desktop-binary.mjs +0 -81
- package/lib/chat/desktop-build.mjs +0 -130
- package/lib/chat/desktop-launcher.mjs +0 -133
- package/lib/chat/evidence.mjs +0 -145
- package/lib/chat/export.mjs +0 -74
- package/lib/chat/harness/driver.mjs +0 -91
- package/lib/chat/list-picker.mjs +0 -112
- package/lib/chat/model-picker.mjs +0 -356
- package/lib/chat/openrouter-fallback.mjs +0 -151
- package/lib/chat/permission-prompt.mjs +0 -33
- package/lib/chat/picker-catalog.mjs +0 -45
- package/lib/chat/policy-telemetry.mjs +0 -34
- package/lib/chat/present.mjs +0 -246
- package/lib/chat/session-context.mjs +0 -39
- package/lib/chat/session-persist.mjs +0 -73
- package/lib/chat/session-restore.mjs +0 -71
- package/lib/chat/session-settings.mjs +0 -53
- package/lib/chat/system-prompt.mjs +0 -52
- package/lib/chat/transparency.mjs +0 -93
- package/lib/chat/tui/color-scheme.mjs +0 -42
- package/lib/chat/tui/markdown.mjs +0 -123
- package/lib/chat/tui/presentation.mjs +0 -100
- package/lib/chat/tui/render.mjs +0 -500
- package/lib/chat/tui/turn-block.mjs +0 -284
- package/lib/chat/tui/turn-present.mjs +0 -18
- package/lib/chat/tui/turn-state.mjs +0 -88
- package/lib/chat/tui/usage.mjs +0 -122
- package/lib/chat/web-commands.mjs +0 -146
- package/lib/chat/web-launcher.mjs +0 -63
- package/lib/chat/web-picker-keys.mjs +0 -46
- package/lib/chat/web-session.mjs +0 -159
- package/lib/config/alias.mjs +0 -57
- package/lib/dashboard-demo.mjs +0 -71
- package/lib/dashboard-static.mjs +0 -175
- package/lib/install/desktop-binary-download.mjs +0 -88
- package/lib/profiles/loader.mjs +0 -123
- package/lib/profiles/rebrand.mjs +0 -46
- package/lib/server/auth.mjs +0 -169
- package/lib/server/chat-loop.mjs +0 -622
- package/lib/server/chat.mjs +0 -336
- package/lib/server/cors.mjs +0 -77
- package/lib/server/csrf.mjs +0 -103
- package/lib/server/demo-preview.mjs +0 -63
- package/lib/server/index.mjs +0 -2641
- package/lib/server/insights.mjs +0 -780
- package/lib/server/langfuse-login.mjs +0 -58
- package/lib/server/rate-limit.mjs +0 -93
- package/lib/server/telemetry-login.mjs +0 -100
- package/lib/server/webhook.mjs +0 -512
- package/specialists/contracts.json +0 -1032
- package/specialists/contracts.schema.json +0 -83
- package/specialists/policy-inventory.json +0 -188
- package/specialists/registry.json +0 -1412
- package/specialists/role-manifests.json +0 -217
- package/specialists/teams.json +0 -94
package/lib/chat/present.mjs
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/present.mjs — headless formatters for construct chat surfaces.
|
|
3
|
-
*
|
|
4
|
-
* Shared by Ink TUI, linear renderer, and dashboard web cockpit. Keeps route
|
|
5
|
-
* rows, tool grouping, usage ledgers, and SSE overlay serialization identical
|
|
6
|
-
* across surfaces.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { formatTokens, formatUsageFooter } from './tui/usage.mjs';
|
|
10
|
-
|
|
11
|
-
export function summarizeToolCalls(tools = []) {
|
|
12
|
-
const groups = new Map();
|
|
13
|
-
for (const t of tools) {
|
|
14
|
-
const title = t.title || t.kind || 'tool';
|
|
15
|
-
if (!groups.has(title)) {
|
|
16
|
-
groups.set(title, { title, count: 0, status: 'completed', refs: [] });
|
|
17
|
-
}
|
|
18
|
-
const g = groups.get(title);
|
|
19
|
-
g.count += 1;
|
|
20
|
-
if (t.status === 'failed') g.status = 'failed';
|
|
21
|
-
else if (t.status === 'pending' && g.status !== 'failed') g.status = 'pending';
|
|
22
|
-
else if (t.status === 'in_progress' && g.status === 'completed') g.status = 'in_progress';
|
|
23
|
-
const ref = t.input?.path || t.input?.pattern || t.input?.glob || t.input?.name;
|
|
24
|
-
if (ref) {
|
|
25
|
-
const s = String(ref);
|
|
26
|
-
if (!g.refs.includes(s)) g.refs.push(s);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return [...groups.values()];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function summarizeSources(sources = []) {
|
|
33
|
-
if (!sources?.length) {
|
|
34
|
-
return { total: 0, byTool: {}, refs: [] };
|
|
35
|
-
}
|
|
36
|
-
const byTool = {};
|
|
37
|
-
const refs = [];
|
|
38
|
-
for (const s of sources) {
|
|
39
|
-
if (typeof s === 'string') {
|
|
40
|
-
if (!refs.includes(s)) refs.push(s);
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
byTool[s.tool] = (byTool[s.tool] || 0) + 1;
|
|
44
|
-
if (s.ref && !refs.includes(s.ref)) refs.push(s.ref);
|
|
45
|
-
}
|
|
46
|
-
return { total: sources.length, byTool, refs };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function formatRefsInline(refs, { max = 3 } = {}) {
|
|
50
|
-
if (!refs?.length) return '';
|
|
51
|
-
if (refs.length <= max) return refs.join(', ');
|
|
52
|
-
const shown = refs.slice(0, max);
|
|
53
|
-
return `${shown.join(', ')} +${refs.length - max} more`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function formatSourceToolCounts(byTool) {
|
|
57
|
-
const entries = Object.entries(byTool || {});
|
|
58
|
-
if (!entries.length) return '';
|
|
59
|
-
return entries.map(([tool, n]) => `${tool} ${n}`).join(' ');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function contextRows(overlay, { layers = null } = {}) {
|
|
63
|
-
if (!overlay) return [];
|
|
64
|
-
const rows = [];
|
|
65
|
-
if (overlay.intent) rows.push({ label: 'intent', value: overlay.intent });
|
|
66
|
-
if (overlay.workCategory) rows.push({ label: 'category', value: overlay.workCategory });
|
|
67
|
-
if (overlay.track) rows.push({ label: 'track', value: overlay.track });
|
|
68
|
-
if (overlay.specialists?.length && layers?.specialists !== false) {
|
|
69
|
-
rows.push({ label: 'route', value: overlay.specialists.join(' → ') });
|
|
70
|
-
}
|
|
71
|
-
if (overlay.externalResearch?.required) {
|
|
72
|
-
const shape = overlay.externalResearch.shape ? ` (${overlay.externalResearch.shape})` : '';
|
|
73
|
-
rows.push({ label: 'research', value: `required${shape}` });
|
|
74
|
-
}
|
|
75
|
-
return rows;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function splitSourceLines(refs, { limit = 4 } = {}) {
|
|
79
|
-
if (!refs?.length) return { lines: ['none yet'], hidden: 0, total: 0 };
|
|
80
|
-
const shown = refs.slice(0, limit);
|
|
81
|
-
return {
|
|
82
|
-
lines: shown,
|
|
83
|
-
hidden: Math.max(0, refs.length - limit),
|
|
84
|
-
total: refs.length,
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function toolGroupLabel(group) {
|
|
89
|
-
const count = group.count > 1 ? ` ×${group.count}` : '';
|
|
90
|
-
const refs = formatRefsInline(group.refs, { max: 2 });
|
|
91
|
-
return refs ? `${group.title}${count} ${refs}` : `${group.title}${count}`;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function toolStatusGlyph(status) {
|
|
95
|
-
if (status === 'completed') return '✓';
|
|
96
|
-
if (status === 'failed') return '✗';
|
|
97
|
-
if (status === 'in_progress') return '›';
|
|
98
|
-
return '·';
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* @param {Record<string, unknown> | null | undefined} overlay
|
|
103
|
-
* @param {{ layers?: Record<string, boolean> | null }} [options]
|
|
104
|
-
* @returns {{
|
|
105
|
-
* chain: string[],
|
|
106
|
-
* intent: string | null,
|
|
107
|
-
* track: string | null,
|
|
108
|
-
* gates: Array<{ label: string, value: string }>,
|
|
109
|
-
* summary: string | null,
|
|
110
|
-
* chainLine: string | null,
|
|
111
|
-
* } | null}
|
|
112
|
-
*/
|
|
113
|
-
export function formatRouteStrip(overlay, { layers = null } = {}) {
|
|
114
|
-
if (!overlay) return null;
|
|
115
|
-
const showSpecialists = layers?.specialists !== false;
|
|
116
|
-
const chain = showSpecialists && Array.isArray(overlay.specialists)
|
|
117
|
-
? [...overlay.specialists]
|
|
118
|
-
: [];
|
|
119
|
-
const intent = overlay.intent || null;
|
|
120
|
-
const track = overlay.track || null;
|
|
121
|
-
const gates = formatGateRows(overlay);
|
|
122
|
-
const summary = overlay.dispatchSummary || null;
|
|
123
|
-
const chainLine = showSpecialists
|
|
124
|
-
? (chain.length ? chain.join(' → ') : 'direct')
|
|
125
|
-
: null;
|
|
126
|
-
return { chain, intent, track, gates, summary, chainLine };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* @param {Record<string, unknown> | null | undefined} overlay
|
|
131
|
-
* @param {{ layers?: Record<string, boolean> | null }} [options]
|
|
132
|
-
*/
|
|
133
|
-
export function formatRouteLogLine(overlay, { layers = null } = {}) {
|
|
134
|
-
const strip = formatRouteStrip(overlay, { layers });
|
|
135
|
-
if (!strip) return '';
|
|
136
|
-
const parts = [];
|
|
137
|
-
if (strip.intent) parts.push(`intent=${strip.intent}`);
|
|
138
|
-
if (strip.track) parts.push(`track=${strip.track}`);
|
|
139
|
-
if (strip.chainLine) parts.push(strip.chainLine);
|
|
140
|
-
return parts.join(' · ');
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function formatRiskFlags(riskFlags = {}) {
|
|
144
|
-
return Object.entries(riskFlags)
|
|
145
|
-
.filter(([, v]) => v)
|
|
146
|
-
.map(([k]) => k);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function formatContractChain(chain = []) {
|
|
150
|
-
return chain.map((edge) => `${edge.producer} → ${edge.consumer}${edge.stage ? ` (${edge.stage})` : ''}`);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export function formatGateRows(overlay) {
|
|
154
|
-
if (!overlay) return [];
|
|
155
|
-
const rows = [];
|
|
156
|
-
if (overlay.externalResearch?.required) {
|
|
157
|
-
const detail = overlay.externalResearch.shape || overlay.externalResearch.reason || 'yes';
|
|
158
|
-
rows.push({ label: 'research', value: `required (${detail})` });
|
|
159
|
-
}
|
|
160
|
-
if (overlay.framingChallenge?.required) {
|
|
161
|
-
rows.push({ label: 'framing', value: 'challenge required' });
|
|
162
|
-
}
|
|
163
|
-
if (overlay.docAuthoring?.docType) {
|
|
164
|
-
rows.push({ label: 'doc', value: `${overlay.docAuthoring.docType} → ${overlay.docAuthoring.owner || 'unknown'}` });
|
|
165
|
-
}
|
|
166
|
-
if (overlay.artifactReview?.requiredReviewers?.length) {
|
|
167
|
-
rows.push({
|
|
168
|
-
label: 'reviewers',
|
|
169
|
-
value: overlay.artifactReview.requiredReviewers.join(', '),
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
return rows;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function contextMeterBar(used, size, width = 18) {
|
|
176
|
-
const ratio = size > 0 ? Math.max(0, Math.min(1, used / size)) : 0;
|
|
177
|
-
const filled = Math.round(ratio * width);
|
|
178
|
-
return {
|
|
179
|
-
bar: '█'.repeat(filled) + '░'.repeat(Math.max(0, width - filled)),
|
|
180
|
-
ratio,
|
|
181
|
-
percent: `${Math.round(ratio * 100)}%`,
|
|
182
|
-
label: `${formatTokens(used) || '0'}/${formatTokens(size) || '?'}`,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export function sessionUsageSummary(session) {
|
|
187
|
-
const t = session?.usage?.tokens || session?.tokens || {};
|
|
188
|
-
const parts = [];
|
|
189
|
-
if (t.total) parts.push(`${formatTokens(t.total)} tok`);
|
|
190
|
-
const cost = session?.usage?.cost?.amount ?? session?.cost?.amount;
|
|
191
|
-
if (cost > 0) parts.push(`~$${cost.toFixed(cost < 1 ? 3 : 2)}`);
|
|
192
|
-
const turns = session?.usage?.turns ?? session?.turns;
|
|
193
|
-
if (turns) parts.push(`${turns} turn${turns === 1 ? '' : 's'}`);
|
|
194
|
-
return parts.join(' · ') || 'no tokens yet';
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export function formatTurnUsageLine(usage) {
|
|
198
|
-
return formatUsageFooter(usage, {}).replace(/^\[usage\] /, '');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function overlayToSsePayload(overlay) {
|
|
202
|
-
if (!overlay) return null;
|
|
203
|
-
return {
|
|
204
|
-
type: 'overlay',
|
|
205
|
-
intent: overlay.intent || null,
|
|
206
|
-
workCategory: overlay.workCategory || null,
|
|
207
|
-
track: overlay.track || null,
|
|
208
|
-
specialists: overlay.specialists || [],
|
|
209
|
-
externalResearch: overlay.externalResearch || null,
|
|
210
|
-
riskFlags: overlay.riskFlags || null,
|
|
211
|
-
contractChain: overlay.contractChain || [],
|
|
212
|
-
framingChallenge: overlay.framingChallenge || null,
|
|
213
|
-
dispatchSummary: overlay.dispatchSummary || null,
|
|
214
|
-
dispatchReasons: overlay.dispatchReasons || null,
|
|
215
|
-
triggers: overlay.triggers || [],
|
|
216
|
-
docAuthoring: overlay.docAuthoring || null,
|
|
217
|
-
artifactReview: overlay.artifactReview || null,
|
|
218
|
-
sessionTurnIndex: overlay.sessionTurnIndex ?? 0,
|
|
219
|
-
priorIntent: overlay.priorIntent ?? null,
|
|
220
|
-
workingBranch: overlay.workingBranch ?? null,
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export function sessionMetaToSsePayload({ session, layers, workingBranch, oracle, ctx = null }) {
|
|
225
|
-
return {
|
|
226
|
-
type: 'session_meta',
|
|
227
|
-
model: session?.model || null,
|
|
228
|
-
demoLabel: session?.demoGuide ? (session.demoTitle || 'scripted') : null,
|
|
229
|
-
modelMode: session?.modelMode || 'pinned',
|
|
230
|
-
sandbox: session?.sandbox || null,
|
|
231
|
-
permissionMode: session?.permissionMode || null,
|
|
232
|
-
layers: layers || {},
|
|
233
|
-
workingBranch: workingBranch || null,
|
|
234
|
-
ctx,
|
|
235
|
-
oracle: oracle ? {
|
|
236
|
-
visible: oracle.visible,
|
|
237
|
-
summary: oracle.summary,
|
|
238
|
-
topGaps: (oracle.topGaps || []).map((g) => ({ id: g.id, detail: g.detail || g.message || '' })),
|
|
239
|
-
} : null,
|
|
240
|
-
usage: session?.usage ? {
|
|
241
|
-
turns: session.usage.turns,
|
|
242
|
-
tokens: { ...session.usage.tokens },
|
|
243
|
-
cost: session.usage.cost ? { ...session.usage.cost } : null,
|
|
244
|
-
} : null,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/session-context.mjs — session-scoped context for planTurn routing.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import fs from 'node:fs';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import { execSync } from 'node:child_process';
|
|
8
|
-
|
|
9
|
-
export function buildPlanContext({ session, cwd = process.cwd(), turnBlocks = [], text = '' } = {}) {
|
|
10
|
-
const turns = turnBlocks.filter((item) => item.kind === 'turn');
|
|
11
|
-
const lastTurn = turns.length ? turns[turns.length - 1].block : null;
|
|
12
|
-
|
|
13
|
-
let workingBranch = null;
|
|
14
|
-
try {
|
|
15
|
-
workingBranch = execSync('git branch --show-current', { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() || null;
|
|
16
|
-
} catch { /* not a git repo */ }
|
|
17
|
-
|
|
18
|
-
let projectSummary = null;
|
|
19
|
-
const contextPath = path.join(cwd, '.cx', 'context.md');
|
|
20
|
-
try {
|
|
21
|
-
if (fs.existsSync(contextPath)) {
|
|
22
|
-
projectSummary = fs.readFileSync(contextPath, 'utf8').slice(0, 500);
|
|
23
|
-
}
|
|
24
|
-
} catch { /* unreadable */ }
|
|
25
|
-
|
|
26
|
-
const trimmed = String(text).trim();
|
|
27
|
-
const vagueFollowUp = /^(tell me more|what about|continue|go on|explain|elaborate|and\?)/i.test(trimmed);
|
|
28
|
-
const projectQuestion = /\b(what is this project|what('s| is) this (repo|project|codebase)|describe this project)\b/i.test(trimmed);
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
turnIndex: session?.usage?.turns ?? 0,
|
|
32
|
-
priorIntent: lastTurn?.overlay?.intent ?? null,
|
|
33
|
-
priorWorkCategory: lastTurn?.overlay?.workCategory ?? null,
|
|
34
|
-
workingBranch,
|
|
35
|
-
projectSummary,
|
|
36
|
-
vagueFollowUp: vagueFollowUp && (session?.usage?.turns ?? 0) > 0,
|
|
37
|
-
projectQuestion,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/session-persist.mjs — JSONL transcript persistence for construct chat surfaces.
|
|
3
|
-
*
|
|
4
|
-
* Shared by CLI linear mode and dashboard owned-loop SSE so web/desktop resume
|
|
5
|
-
* matches .cx/chat-sessions/ on disk.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import fs from 'node:fs';
|
|
9
|
-
import path from 'node:path';
|
|
10
|
-
import { resolveProjectScopedPath } from '../project-root.mjs';
|
|
11
|
-
import { deserializeBlock, serializeBlock, snapshotTurn } from './tui/turn-block.mjs';
|
|
12
|
-
import { migrateEvidenceVerdict } from './evidence.mjs';
|
|
13
|
-
|
|
14
|
-
export function chatSessionFilePath({ cwd, convId, host = 'construct-web' }) {
|
|
15
|
-
const dir = resolveProjectScopedPath('chat-sessions', { cwd });
|
|
16
|
-
return path.join(dir, `${convId}-${host}.jsonl`);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function createChatPersister({ cwd, sessionId, resumePath = null, host = 'construct' }) {
|
|
20
|
-
try {
|
|
21
|
-
const dir = resolveProjectScopedPath('chat-sessions', { cwd });
|
|
22
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
23
|
-
const file = resumePath || path.join(dir, `${new Date().toISOString().replace(/[:.]/g, '-')}-${host}-${sessionId}.jsonl`);
|
|
24
|
-
if (!resumePath) {
|
|
25
|
-
fs.appendFileSync(file, `${JSON.stringify({ type: 'session_start', host, sessionId, ts: new Date().toISOString() })}\n`);
|
|
26
|
-
}
|
|
27
|
-
const append = (row) => {
|
|
28
|
-
try {
|
|
29
|
-
fs.appendFileSync(file, `${JSON.stringify({ ...row, ts: new Date().toISOString() })}\n`);
|
|
30
|
-
} catch { /* log is best-effort */ }
|
|
31
|
-
};
|
|
32
|
-
return {
|
|
33
|
-
filePath: file,
|
|
34
|
-
event: (event) => append(event),
|
|
35
|
-
transcript: (role, text) => append({ type: 'transcript', role, text }),
|
|
36
|
-
transcriptBlock: (turn) => append(serializeBlock(turn)),
|
|
37
|
-
};
|
|
38
|
-
} catch {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function loadPersistedTurns(filePath) {
|
|
44
|
-
if (!filePath || !fs.existsSync(filePath)) return { turns: [], turnBlocks: [] };
|
|
45
|
-
const turns = [];
|
|
46
|
-
const turnBlocks = [];
|
|
47
|
-
const lines = fs.readFileSync(filePath, 'utf8').split('\n').filter(Boolean);
|
|
48
|
-
for (const line of lines) {
|
|
49
|
-
try {
|
|
50
|
-
const row = JSON.parse(line);
|
|
51
|
-
const block = deserializeBlock(row);
|
|
52
|
-
if (!block) continue;
|
|
53
|
-
turnBlocks.push({ kind: 'turn', block });
|
|
54
|
-
const snap = snapshotTurn(block);
|
|
55
|
-
const evidence = migrateEvidenceVerdict(snap);
|
|
56
|
-
turns.push({
|
|
57
|
-
id: snap.id || `turn-${turns.length + 1}`,
|
|
58
|
-
userText: snap.userText || '',
|
|
59
|
-
assistant: snap.assistant || '',
|
|
60
|
-
thinking: snap.thinking || '',
|
|
61
|
-
tools: snap.tools || [],
|
|
62
|
-
overlay: snap.overlay || null,
|
|
63
|
-
sources: snap.sources || [],
|
|
64
|
-
usage: snap.usage || null,
|
|
65
|
-
evidence,
|
|
66
|
-
unverified: ['insufficient_evidence', 'uncited_evidence', 'partially_verified'].includes(evidence.status),
|
|
67
|
-
working: false,
|
|
68
|
-
system: false,
|
|
69
|
-
});
|
|
70
|
-
} catch { /* skip malformed rows */ }
|
|
71
|
-
}
|
|
72
|
-
return { turns, turnBlocks };
|
|
73
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/session-restore.mjs — resume prior construct chat sessions from jsonl.
|
|
3
|
-
*
|
|
4
|
-
* Session timelines persist under .cx/chat-sessions/*.jsonl. Transcript rows use
|
|
5
|
-
* { type: 'transcript_block', block } for turn fidelity or legacy { type:
|
|
6
|
-
* 'transcript', role, text }; usage events replay into the shared usage
|
|
7
|
-
* accumulator so /usage and the session dock stay truthful after --resume.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import fs from 'node:fs';
|
|
11
|
-
import path from 'node:path';
|
|
12
|
-
import { resolveProjectScopedPath } from '../project-root.mjs';
|
|
13
|
-
import { createSessionUsage, addUsage } from './tui/usage.mjs';
|
|
14
|
-
import { restoreBlocksFromSessionLines, turnBlocksToLegacyTranscript } from './tui/turn-block.mjs';
|
|
15
|
-
import { rehydrateContinuation } from './context-continuation.mjs';
|
|
16
|
-
import { createContinuationResolver } from './continuation-source.mjs';
|
|
17
|
-
|
|
18
|
-
export function sessionDir({ cwd = process.cwd() } = {}) {
|
|
19
|
-
return resolveProjectScopedPath('chat-sessions', { cwd, ensureDir: false });
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function listSessionFiles({ cwd = process.cwd() } = {}) {
|
|
23
|
-
const dir = sessionDir({ cwd });
|
|
24
|
-
if (!dir || !fs.existsSync(dir)) return [];
|
|
25
|
-
return fs.readdirSync(dir)
|
|
26
|
-
.filter((name) => name.endsWith('.jsonl'))
|
|
27
|
-
.map((name) => path.join(dir, name))
|
|
28
|
-
.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function resolveResumePath({ cwd = process.cwd(), resume = null } = {}) {
|
|
32
|
-
if (resume == null || resume === false) return null;
|
|
33
|
-
if (typeof resume === 'string' && resume !== 'true' && resume !== '') {
|
|
34
|
-
const direct = path.resolve(resume);
|
|
35
|
-
return fs.existsSync(direct) ? direct : null;
|
|
36
|
-
}
|
|
37
|
-
return listSessionFiles({ cwd })[0] || null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function restoreFromSession(filePath, { cwd = process.cwd() } = {}) {
|
|
41
|
-
const turnBlocks = [];
|
|
42
|
-
const usage = createSessionUsage();
|
|
43
|
-
if (!filePath || !fs.existsSync(filePath)) {
|
|
44
|
-
return { turnBlocks, transcript: [], usage, sessionFile: null, continuations: [] };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const lines = fs.readFileSync(filePath, 'utf8').split('\n').filter((l) => l.trim());
|
|
48
|
-
for (const line of lines) {
|
|
49
|
-
let event;
|
|
50
|
-
try { event = JSON.parse(line); } catch { continue; }
|
|
51
|
-
if (event.type === 'usage') addUsage(usage, event);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const blocks = restoreBlocksFromSessionLines(lines);
|
|
55
|
-
for (const item of blocks) {
|
|
56
|
-
if (item.kind === 'turn' || item.kind === 'legacy') turnBlocks.push(item);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Resume rehydrates any persisted continuation packet through the one shared
|
|
60
|
-
// resolver (AC3), so a resumed session reconstructs the same reconstructible
|
|
61
|
-
// layers — via the same composer — that the live and export surfaces use.
|
|
62
|
-
const resolver = createContinuationResolver({ cwd });
|
|
63
|
-
const continuations = [];
|
|
64
|
-
for (const item of turnBlocks) {
|
|
65
|
-
const packet = item?.block?.continuation;
|
|
66
|
-
if (packet) continuations.push({ packet, layers: rehydrateContinuation(packet, { resolveSource: resolver.resolveSource }) });
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const transcript = turnBlocksToLegacyTranscript(turnBlocks);
|
|
70
|
-
return { turnBlocks, transcript, usage, sessionFile: filePath, continuations };
|
|
71
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/session-settings.mjs — apply validated chat settings to session + config.
|
|
3
|
-
*
|
|
4
|
-
* Shared by slash commands and Ink list pickers so /set and picker commits stay aligned.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { saveChatConfig, validateSetting } from './config.mjs';
|
|
8
|
-
|
|
9
|
-
export function applySessionSetting(session, layers, key, rawValue, { cwd, hostId = 'construct' } = {}) {
|
|
10
|
-
const result = validateSetting(key, rawValue);
|
|
11
|
-
if (!result.ok) return result;
|
|
12
|
-
|
|
13
|
-
const targetKey = result.key || key;
|
|
14
|
-
if (targetKey.startsWith('layers.')) {
|
|
15
|
-
const layer = targetKey.slice('layers.'.length);
|
|
16
|
-
layers[layer] = result.value;
|
|
17
|
-
session.layers = { ...layers };
|
|
18
|
-
} else if (targetKey === 'thinking') {
|
|
19
|
-
layers.thinking = result.value;
|
|
20
|
-
session.layers = { ...layers };
|
|
21
|
-
session.thinking = result.value;
|
|
22
|
-
} else if (targetKey === 'model') {
|
|
23
|
-
session.model = result.value;
|
|
24
|
-
session.modelMode = 'pinned';
|
|
25
|
-
session.savedModel = result.value;
|
|
26
|
-
session.modelNotice = null;
|
|
27
|
-
} else if (targetKey === 'permissionMode') {
|
|
28
|
-
session.permissionMode = result.value;
|
|
29
|
-
} else if (targetKey === 'sandbox') {
|
|
30
|
-
session.sandbox = result.value;
|
|
31
|
-
} else if (targetKey === 'ui.ascii') {
|
|
32
|
-
session.ui = { ...(session.ui || { ascii: false, inspector: 'auto', theme: 'auto' }), ascii: result.value };
|
|
33
|
-
} else if (targetKey === 'ui.inspector') {
|
|
34
|
-
session.ui = { ...(session.ui || { ascii: false, inspector: 'auto', theme: 'auto' }), inspector: result.value };
|
|
35
|
-
} else if (targetKey === 'ui.theme') {
|
|
36
|
-
session.ui = { ...(session.ui || { ascii: false, inspector: 'auto', theme: 'auto' }), theme: result.value };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
saveChatConfig({
|
|
41
|
-
host: hostId,
|
|
42
|
-
model: session.modelMode === 'pinned' ? session.model : null,
|
|
43
|
-
modelMode: session.modelMode || 'pinned',
|
|
44
|
-
layers,
|
|
45
|
-
thinking: layers?.thinking,
|
|
46
|
-
permissionMode: session.permissionMode,
|
|
47
|
-
sandbox: session.sandbox,
|
|
48
|
-
ui: session.ui,
|
|
49
|
-
}, { cwd });
|
|
50
|
-
} catch { /* persistence is best-effort */ }
|
|
51
|
-
|
|
52
|
-
return { ok: true, key: targetKey, value: result.value };
|
|
53
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/system-prompt.mjs — owned-loop system prompt for construct chat.
|
|
3
|
-
*
|
|
4
|
-
* Keeps a short Construct identity and research discipline without inlining the
|
|
5
|
-
* full orchestrator persona. Turn-scoped policy text is injected per prompt by
|
|
6
|
-
* the engine when an overlay is present.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export const CHAT_SYSTEM_BASE = `You are Construct — a transparent terminal coding agent operating inside a real repository.
|
|
10
|
-
|
|
11
|
-
Rules:
|
|
12
|
-
- Use read, grep, glob, and construct_tool before making load-bearing claims about this project.
|
|
13
|
-
- For competitive, landscape, or research questions: consult repo files (docs/, ADR/, STRATEGY.md) and cite paths.
|
|
14
|
-
- Call construct_tool with orchestration_policy or knowledge_search when routing or prior artifacts matter.
|
|
15
|
-
- Mark any claim you cannot tie to a tool result as [unverified].
|
|
16
|
-
- Prefer structured markdown answers: headings, short paragraphs, compact tables when useful.
|
|
17
|
-
- Keep actions scoped to the user's request; use write/edit/shell only when asked.`;
|
|
18
|
-
|
|
19
|
-
export const CHAT_SYSTEM_SMALL = `You are Construct in a local/small-model profile. Be concise.
|
|
20
|
-
- One tool call at a time when possible; cite file paths for claims.
|
|
21
|
-
- Use construct_tool for orchestration_policy when routing is non-trivial.
|
|
22
|
-
- Mark unverified claims explicitly.`;
|
|
23
|
-
|
|
24
|
-
// Per-turn routing policy belongs in the SYSTEM role, not the user message:
|
|
25
|
-
// otherwise a weak model echoes it back as "the system told me…" in its visible
|
|
26
|
-
// reasoning, and it compounds in the conversation history. These lines carry the
|
|
27
|
-
// substance that improves answer quality (evidence discipline, the resolved
|
|
28
|
-
// route) without exposing internal scaffolding to the user.
|
|
29
|
-
|
|
30
|
-
export function turnPolicyLines(overlay) {
|
|
31
|
-
if (!overlay) return [];
|
|
32
|
-
const lines = [];
|
|
33
|
-
if (overlay.intent || overlay.workCategory) {
|
|
34
|
-
lines.push(`Classified intent: ${overlay.intent || 'unknown'}${overlay.workCategory ? ` (${overlay.workCategory})` : ''}.`);
|
|
35
|
-
}
|
|
36
|
-
if (overlay.specialists?.length) {
|
|
37
|
-
lines.push(`Resolved route (a policy overlay, not separate agents): ${overlay.specialists.join(' -> ')}.`);
|
|
38
|
-
}
|
|
39
|
-
if (overlay.externalResearch?.required) {
|
|
40
|
-
lines.push('Repo evidence is REQUIRED this turn: grep/read the relevant files and cite their paths. Do not write a long essay without it.');
|
|
41
|
-
}
|
|
42
|
-
if (overlay.assumptionsBlocked) {
|
|
43
|
-
lines.push('Do not infer the project’s purpose, architecture, or behavior from file names or prior knowledge — read the files first. Mark any claim you cannot tie to a tool result as [unverified].');
|
|
44
|
-
}
|
|
45
|
-
return lines;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function buildSystemPrompt({ base = CHAT_SYSTEM_BASE, overlay = null, capabilityTier = 'full' } = {}) {
|
|
49
|
-
const effectiveBase = capabilityTier === 'full' ? base : CHAT_SYSTEM_SMALL;
|
|
50
|
-
const lines = turnPolicyLines(overlay);
|
|
51
|
-
return lines.length ? `${effectiveBase}\n\nTurn policy (internal — never restate this to the user):\n${lines.join('\n')}` : effectiveBase;
|
|
52
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/transparency.mjs — the transparency layer for `construct chat`.
|
|
3
|
-
*
|
|
4
|
-
* The terminal's differentiator is visibility into the loop, so this module
|
|
5
|
-
* decides what the user sees and enriches the host stream with Construct's own
|
|
6
|
-
* orchestration knowledge. It resolves which transparency layers are active
|
|
7
|
-
* (thinking, planned path, specialist routing, tool calls, observability),
|
|
8
|
-
* classifies each normalized driver event into a channel, and — before a turn
|
|
9
|
-
* runs — derives the specialist path Construct's policy would route the request
|
|
10
|
-
* through (orchestration-policy.mjs) so the planned route is shown alongside the
|
|
11
|
-
* host's actual execution.
|
|
12
|
-
*
|
|
13
|
-
* Transparency-first by default: every layer is on unless explicitly disabled,
|
|
14
|
-
* matching the surface's promise of maximum visibility with opt-out, not opt-in.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const LAYER_KEYS = ['thinking', 'path', 'specialists', 'tools', 'observability'];
|
|
18
|
-
|
|
19
|
-
export function resolveLayers({ flags = {}, env = process.env } = {}) {
|
|
20
|
-
const layers = Object.fromEntries(LAYER_KEYS.map((k) => [k, true]));
|
|
21
|
-
|
|
22
|
-
if (flags.thinking === false || env.CX_CHAT_THINKING === '0') layers.thinking = false;
|
|
23
|
-
if (flags.path === false) layers.path = false;
|
|
24
|
-
if (flags.specialists === false) layers.specialists = false;
|
|
25
|
-
if (flags.tools === false) layers.tools = false;
|
|
26
|
-
if (flags.observability === false || flags.quiet === true) layers.observability = false;
|
|
27
|
-
|
|
28
|
-
return layers;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const CHANNEL_BY_TYPE = {
|
|
32
|
-
thinking: 'thinking',
|
|
33
|
-
text: 'message',
|
|
34
|
-
plan: 'path',
|
|
35
|
-
tool_call: 'tools',
|
|
36
|
-
tool_update: 'tools',
|
|
37
|
-
usage: 'observability',
|
|
38
|
-
specialist: 'specialists',
|
|
39
|
-
permission: 'permission',
|
|
40
|
-
error: 'error',
|
|
41
|
-
done: 'system',
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export function channelFor(event) {
|
|
45
|
-
return CHANNEL_BY_TYPE[event?.type] || 'system';
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// message, permission, error and system are structural and always shown; the
|
|
49
|
-
// optional layers gate everything else so toggles change visibility without
|
|
50
|
-
// changing the underlying event stream (which still flows to persistence).
|
|
51
|
-
|
|
52
|
-
export function isVisible(event, layers) {
|
|
53
|
-
const channel = channelFor(event);
|
|
54
|
-
if (channel === 'message' || channel === 'permission' || channel === 'error' || channel === 'system') return true;
|
|
55
|
-
return Boolean(layers[channel]);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function planTurn(request, { env = process.env, context = null } = {}) {
|
|
59
|
-
try {
|
|
60
|
-
const { routeRequest } = await import('../orchestration-policy.mjs');
|
|
61
|
-
const route = routeRequest({ request, context: context || {} });
|
|
62
|
-
const projectQuestion = context?.projectQuestion
|
|
63
|
-
|| /\b(what is this project|what('s| is) this (repo|project|codebase)|describe this project)\b/i.test(String(request));
|
|
64
|
-
const assumptionsBlocked = projectQuestion
|
|
65
|
-
|| (context?.vagueFollowUp && !context?.priorIntent);
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
intent: route.intent,
|
|
69
|
-
workCategory: route.workCategory,
|
|
70
|
-
specialists: route.specialists,
|
|
71
|
-
externalResearch: route.externalResearch,
|
|
72
|
-
riskFlags: route.riskFlags,
|
|
73
|
-
track: route.track,
|
|
74
|
-
contractChain: route.contractChain,
|
|
75
|
-
framingChallenge: route.framingChallenge,
|
|
76
|
-
dispatchSummary: route.dispatchSummary,
|
|
77
|
-
dispatchReasons: route.dispatchReasons,
|
|
78
|
-
triggers: route.triggers,
|
|
79
|
-
docAuthoring: route.docAuthoring,
|
|
80
|
-
artifactReview: route.artifactReview,
|
|
81
|
-
sessionTurnIndex: context?.turnIndex ?? 0,
|
|
82
|
-
priorIntent: context?.priorIntent ?? null,
|
|
83
|
-
workingBranch: context?.workingBranch ?? null,
|
|
84
|
-
assumptionsBlocked,
|
|
85
|
-
};
|
|
86
|
-
} catch {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Per-turn policy is carried in the system prompt via lib/chat/system-prompt.mjs
|
|
92
|
-
// `turnPolicyLines`/`buildSystemPrompt`, not prepended to the user message, so a
|
|
93
|
-
// model cannot echo it back as user-visible reasoning.
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/chat/tui/color-scheme.mjs — terminal light/dark detection for construct chat.
|
|
3
|
-
*
|
|
4
|
-
* Resolves auto|light|dark from CX_CHAT_THEME, saved ui.theme, and COLORFGBG
|
|
5
|
-
* (macOS Terminal, iTerm, Linux VTE, Windows Terminal). Falls back to dark when
|
|
6
|
-
* the background cannot be inferred — set CX_CHAT_THEME=light on legacy Windows
|
|
7
|
-
* conhost if needed.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const THEME_VALUES = new Set(['auto', 'light', 'dark']);
|
|
11
|
-
|
|
12
|
-
export function schemeFromColorFgBg(value) {
|
|
13
|
-
const parts = String(value).split(';').map((p) => parseInt(p, 10)).filter((n) => !Number.isNaN(n));
|
|
14
|
-
if (!parts.length) return null;
|
|
15
|
-
const bg = parts.length >= 2 ? parts[1] : parts[parts.length - 1];
|
|
16
|
-
if (bg === 7 || bg === 15) return 'light';
|
|
17
|
-
if (bg >= 0 && bg <= 6) return 'dark';
|
|
18
|
-
if (bg === 8) return 'dark';
|
|
19
|
-
if (bg >= 9 && bg <= 14) return 'light';
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function detectTerminalColorScheme(env = process.env) {
|
|
24
|
-
const fromFgBg = env.COLORFGBG ? schemeFromColorFgBg(env.COLORFGBG) : null;
|
|
25
|
-
if (fromFgBg) return fromFgBg;
|
|
26
|
-
return 'dark';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function resolveTerminalColorScheme(env = process.env, configTheme = null) {
|
|
30
|
-
const envTheme = String(env.CX_CHAT_THEME || env.CONSTRUCT_CHAT_THEME || '').trim().toLowerCase();
|
|
31
|
-
if (envTheme === 'light' || envTheme === 'dark') return envTheme;
|
|
32
|
-
|
|
33
|
-
const saved = String(configTheme || '').trim().toLowerCase();
|
|
34
|
-
if (saved === 'light' || saved === 'dark') return saved;
|
|
35
|
-
|
|
36
|
-
return detectTerminalColorScheme(env);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function normalizeThemeSetting(value) {
|
|
40
|
-
const v = String(value || 'auto').trim().toLowerCase();
|
|
41
|
-
return THEME_VALUES.has(v) ? v : null;
|
|
42
|
-
}
|