@geraldmaron/construct 1.2.3 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (505) hide show
  1. package/README.md +7 -10
  2. package/bin/construct +235 -209
  3. package/bin/construct-postinstall.mjs +0 -21
  4. package/commands/work/optimize-prompts.md +1 -1
  5. package/examples/distribution/sources/adr.md +2 -2
  6. package/examples/seed-observations/decisions.md +1 -1
  7. package/lib/artifact-loop-core.mjs +412 -0
  8. package/lib/artifact-manifest.mjs +6 -0
  9. package/lib/artifact-release-gate.mjs +115 -49
  10. package/lib/audit-rules.mjs +2 -4
  11. package/lib/audit-skills.mjs +32 -20
  12. package/lib/audit-specialists.mjs +44 -26
  13. package/lib/auto-docs.mjs +6 -5
  14. package/lib/brand-prose.mjs +3 -3
  15. package/lib/brand-tokens.mjs +2 -2
  16. package/lib/certification/artifact-fixtures.mjs +4 -1
  17. package/lib/certification/demo-parity.mjs +6 -32
  18. package/lib/certification/real-llm-scenarios.mjs +58 -13
  19. package/lib/certification/role-cards.mjs +7 -6
  20. package/lib/certification/role-overlays.mjs +4 -4
  21. package/lib/certification/runner.mjs +9 -2
  22. package/lib/certification/skill-inventory.mjs +34 -22
  23. package/lib/certification/skill-scenarios.mjs +21 -8
  24. package/lib/certification/specialist-contracts.mjs +4 -3
  25. package/lib/certification/specialist-scenarios.mjs +7 -6
  26. package/lib/certification/status.mjs +5 -4
  27. package/lib/cli-commands.mjs +55 -54
  28. package/lib/comment-lint.mjs +1 -5
  29. package/lib/completions.mjs +20 -2
  30. package/lib/config/legacy-config-migration.mjs +90 -24
  31. package/lib/config/schema.mjs +5 -5
  32. package/lib/config/source-targets.mjs +80 -0
  33. package/lib/contracts/validate.mjs +119 -51
  34. package/lib/decisions/golden.mjs +3 -2
  35. package/lib/decisions/registry.mjs +11 -6
  36. package/lib/demo-project.mjs +188 -0
  37. package/lib/demo-recording.mjs +9 -34
  38. package/lib/demo-script.mjs +2 -2
  39. package/lib/demo-surface.mjs +13 -131
  40. package/lib/demo-tour-renderer.mjs +92 -0
  41. package/lib/demo.mjs +69 -54
  42. package/lib/diagram-export.mjs +63 -14
  43. package/lib/doctor/cli.mjs +12 -2
  44. package/lib/doctor/source-checkout.mjs +3 -4
  45. package/lib/doctor/watchers/consistency.mjs +148 -43
  46. package/lib/doctor/watchers/credential-parity.mjs +97 -0
  47. package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
  48. package/lib/doctor/watchers/service-health.mjs +4 -32
  49. package/lib/document-export.mjs +73 -9
  50. package/lib/document-extract.mjs +10 -198
  51. package/lib/embed/artifact.mjs +2 -3
  52. package/lib/embed/cli.mjs +1 -1
  53. package/lib/embed/config.mjs +1 -2
  54. package/lib/embed/customer-profiles.mjs +1 -1
  55. package/lib/embed/daemon.mjs +5 -6
  56. package/lib/embed/demand-fetch.mjs +114 -2
  57. package/lib/embed/docs-lifecycle.mjs +9 -5
  58. package/lib/embed/intake-metrics.mjs +2 -2
  59. package/lib/embed/notifications.mjs +2 -3
  60. package/lib/embed/recommendation-store.mjs +25 -22
  61. package/lib/embed/role-framing.mjs +4 -9
  62. package/lib/embedded-contract/capability.mjs +4 -2
  63. package/lib/embedded-contract/contract-version.mjs +1 -1
  64. package/lib/env-config.mjs +2 -2
  65. package/lib/flavors/loader.mjs +21 -19
  66. package/lib/graph/build-from-registry.mjs +31 -7
  67. package/lib/graph/staleness.mjs +1 -1
  68. package/lib/headhunt.mjs +82 -35
  69. package/lib/hooks/agent-tracker.mjs +2 -2
  70. package/lib/hooks/config-protection.mjs +1 -1
  71. package/lib/hooks/registry-sync.mjs +3 -3
  72. package/lib/host-capabilities.mjs +5 -5
  73. package/lib/improvement/controller.mjs +2 -2
  74. package/lib/init-unified.mjs +19 -21
  75. package/lib/intake/classify.mjs +26 -0
  76. package/lib/intake/daemon.mjs +2 -3
  77. package/lib/intake/prepare.mjs +10 -3
  78. package/lib/intake/session-prelude.mjs +1 -1
  79. package/lib/intake/tables/creative.mjs +9 -9
  80. package/lib/intake/tables/operations.mjs +4 -4
  81. package/lib/intake/tables/research.mjs +2 -2
  82. package/lib/integrations/intake-integrations.mjs +9 -10
  83. package/lib/knowledge/research-store.mjs +2 -2
  84. package/lib/mcp/server.mjs +104 -61
  85. package/lib/mcp/tool-budget.mjs +1 -46
  86. package/lib/mcp/tool-recovery.mjs +44 -0
  87. package/lib/mcp/tools/artifact-author.mjs +36 -0
  88. package/lib/mcp/tools/find-tool.mjs +86 -0
  89. package/lib/mcp/tools/orchestration-run.mjs +107 -69
  90. package/lib/mcp/tools/project.mjs +4 -3
  91. package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
  92. package/lib/mcp/tools/skills.mjs +39 -22
  93. package/lib/mcp/tools/telemetry.mjs +2 -1
  94. package/lib/mcp/tools/workflow.mjs +1 -1
  95. package/lib/mcp-platform-config.mjs +7 -5
  96. package/lib/migrations/index.mjs +4 -2
  97. package/lib/migrations/v2-unified-registry.mjs +35 -0
  98. package/lib/model-router.mjs +203 -36
  99. package/lib/models/catalog.mjs +25 -9
  100. package/lib/models/execution-capability-profile.mjs +3 -3
  101. package/lib/models/execution-policy.mjs +7 -6
  102. package/lib/models/provider-poll.mjs +18 -4
  103. package/lib/opencode-config.mjs +56 -1
  104. package/lib/opencode-runtime-plugin.mjs +13 -10
  105. package/lib/oracle/artifact-gate.mjs +14 -4
  106. package/lib/oracle/cli.mjs +2 -0
  107. package/lib/oracle/dispatch.mjs +18 -1
  108. package/lib/oracle/execute.mjs +39 -7
  109. package/lib/oracle/index.mjs +1 -1
  110. package/lib/oracle/org-graph.mjs +10 -5
  111. package/lib/oracle/policy.mjs +6 -0
  112. package/lib/oracle/read-model.mjs +103 -6
  113. package/lib/oracle/reconcile.mjs +31 -4
  114. package/lib/oracle/remediation-dispatch.mjs +53 -0
  115. package/lib/oracle/routing.mjs +5 -0
  116. package/lib/oracle/synthesize.mjs +80 -3
  117. package/lib/orchestration/routing-tables.mjs +44 -10
  118. package/lib/orchestration/runtime.mjs +2 -0
  119. package/lib/orchestration/worker.mjs +1 -1
  120. package/lib/orchestration-policy.mjs +351 -62
  121. package/lib/overrides/resolver.mjs +1 -12
  122. package/lib/parity.mjs +37 -11
  123. package/lib/policy/engine.mjs +80 -15
  124. package/lib/prompt-composer.js +51 -15
  125. package/lib/prompt-metadata.mjs +3 -2
  126. package/lib/providers/connection-probe.mjs +58 -0
  127. package/lib/providers/copilot-auth.mjs +1 -1
  128. package/lib/providers/credential-bootstrap.mjs +1 -1
  129. package/lib/providers/op-run.mjs +3 -4
  130. package/lib/providers/secret-resolver.mjs +31 -0
  131. package/lib/publish-tooling.mjs +3 -11
  132. package/lib/publish.mjs +12 -29
  133. package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
  134. package/lib/reflect.mjs +2 -2
  135. package/lib/registry/agent-manifest.mjs +190 -0
  136. package/lib/registry/assemble.mjs +133 -0
  137. package/lib/registry/catalog.mjs +171 -0
  138. package/lib/registry/cli.mjs +590 -6
  139. package/lib/registry/consolidation.mjs +5 -4
  140. package/lib/registry/docs-sync.mjs +67 -0
  141. package/lib/registry/loader.mjs +147 -0
  142. package/lib/registry/org-io.mjs +76 -0
  143. package/lib/registry/retired-paths.mjs +71 -0
  144. package/lib/registry/surface-map.mjs +5 -7
  145. package/lib/registry/validator.mjs +438 -0
  146. package/lib/research-execution-policy.mjs +173 -0
  147. package/lib/roles/catalog.mjs +4 -9
  148. package/lib/roles/fence.mjs +107 -1
  149. package/lib/roles/flavor-bindings.mjs +68 -0
  150. package/lib/roles/gateway.mjs +140 -29
  151. package/lib/roles/manifest.mjs +22 -13
  152. package/lib/roles/router.mjs +1 -1
  153. package/lib/scopes/enrich.mjs +67 -0
  154. package/lib/scopes/hooks.mjs +12 -0
  155. package/lib/{profiles → scopes}/lifecycle.mjs +70 -70
  156. package/lib/scopes/loader.mjs +104 -0
  157. package/lib/scopes/rebrand.mjs +32 -0
  158. package/lib/scopes/research-profile.mjs +16 -0
  159. package/lib/scopes/teams.mjs +98 -0
  160. package/lib/service-manager.mjs +1 -117
  161. package/lib/setup-prompts.mjs +1 -1
  162. package/lib/setup.mjs +6 -26
  163. package/lib/skills/composition-graph.mjs +98 -0
  164. package/lib/skills/router.mjs +80 -0
  165. package/lib/specialist-contracts.mjs +17 -18
  166. package/lib/specialists/postconditions.mjs +2 -2
  167. package/lib/specialists/prompt-schema.mjs +6 -5
  168. package/lib/specialists/roster.mjs +7 -12
  169. package/lib/specialists/schema.mjs +9 -6
  170. package/lib/status.mjs +25 -90
  171. package/lib/storage/file-lock.mjs +6 -3
  172. package/lib/telemetry/skill-calls.mjs +1 -1
  173. package/lib/templates/doc-presentation.mjs +63 -0
  174. package/lib/templates/visual-requirements.mjs +35 -2
  175. package/lib/term-format.mjs +107 -2
  176. package/lib/test-env-setup.mjs +21 -0
  177. package/lib/ui/components.mjs +42 -0
  178. package/lib/ui/glyphs.mjs +58 -0
  179. package/lib/ui/links.mjs +115 -0
  180. package/lib/ui/theme.mjs +108 -0
  181. package/lib/uninstall/uninstall.mjs +2 -1
  182. package/lib/validator.mjs +45 -8
  183. package/lib/validators/skill-effectiveness.mjs +174 -0
  184. package/lib/validators/skills.mjs +1 -1
  185. package/package.json +12 -8
  186. package/personas/construct.md +12 -3
  187. package/platforms/claude/CLAUDE.md +1 -1
  188. package/rules/common/beads-hygiene.md +52 -0
  189. package/rules/common/neurodivergent-output.md +4 -7
  190. package/rules/common/research.md +2 -0
  191. package/scripts/sync-specialists.mjs +269 -51
  192. package/skills/ai/prompt-optimizer.md +3 -3
  193. package/skills/brand/output-vibe.md +48 -0
  194. package/skills/docs/adr-workflow.md +3 -0
  195. package/skills/docs/backlog-proposal-workflow.md +3 -0
  196. package/skills/docs/codebase-research-workflow.md +5 -2
  197. package/skills/docs/customer-profile-workflow.md +3 -0
  198. package/skills/docs/document-ingest-workflow.md +3 -0
  199. package/skills/docs/evidence-ingest-workflow.md +4 -1
  200. package/skills/docs/init-project.md +1 -1
  201. package/skills/docs/prd-workflow.md +1 -1
  202. package/skills/docs/prfaq-workflow.md +3 -0
  203. package/skills/docs/product-intelligence-workflow.md +4 -1
  204. package/skills/docs/product-signal-workflow.md +3 -0
  205. package/skills/docs/research-workflow.md +13 -6
  206. package/skills/docs/runbook-workflow.md +3 -0
  207. package/skills/docs/strategy-workflow.md +3 -0
  208. package/skills/docs/user-research-workflow.md +6 -3
  209. package/skills/operating/orchestration-reference.md +1 -1
  210. package/skills/roles/{engineer.ai.md → ai-engineer.md} +2 -2
  211. package/skills/roles/architect.ai-systems.md +1 -1
  212. package/skills/roles/architect.data.md +1 -1
  213. package/skills/roles/architect.enterprise.md +1 -1
  214. package/skills/roles/architect.integration.md +1 -1
  215. package/skills/roles/architect.md +1 -1
  216. package/skills/roles/architect.platform.md +1 -1
  217. package/skills/roles/{product-manager.business-strategy.md → business-strategist.md} +3 -3
  218. package/skills/roles/data-analyst.experiment.md +1 -1
  219. package/skills/roles/data-analyst.md +1 -1
  220. package/skills/roles/data-analyst.product-intelligence.md +1 -1
  221. package/skills/roles/data-analyst.product.md +1 -1
  222. package/skills/roles/data-analyst.telemetry.md +1 -1
  223. package/skills/roles/{engineer.data.md → data-engineer.md} +2 -2
  224. package/skills/roles/data-engineer.pipeline.md +2 -2
  225. package/skills/roles/data-engineer.vector-retrieval.md +2 -2
  226. package/skills/roles/data-engineer.warehouse.md +2 -2
  227. package/skills/roles/debugger.md +1 -1
  228. package/skills/roles/designer.accessibility.md +1 -1
  229. package/skills/roles/designer.md +1 -1
  230. package/skills/roles/{reviewer.devil-advocate.md → devil-advocate.md} +2 -2
  231. package/skills/roles/{operator.docs.md → docs-keeper.md} +3 -3
  232. package/skills/roles/engineer.md +3 -7
  233. package/skills/roles/{reviewer.evaluator.md → evaluator.md} +2 -2
  234. package/skills/roles/{researcher.explorer.md → explorer.md} +2 -2
  235. package/skills/roles/{operator.md → operations.md} +2 -5
  236. package/skills/roles/orchestrator.md +1 -1
  237. package/skills/roles/{engineer.platform.md → platform-engineer.md} +2 -2
  238. package/skills/roles/product-manager.ai-product.md +1 -1
  239. package/skills/roles/product-manager.enterprise.md +1 -1
  240. package/skills/roles/product-manager.growth.md +1 -2
  241. package/skills/roles/product-manager.md +1 -2
  242. package/skills/roles/product-manager.platform.md +1 -1
  243. package/skills/roles/product-manager.product.md +1 -1
  244. package/skills/roles/qa.ai-eval.md +1 -1
  245. package/skills/roles/qa.api-contract.md +1 -1
  246. package/skills/roles/qa.data-pipeline.md +1 -1
  247. package/skills/roles/qa.md +1 -1
  248. package/skills/roles/qa.web-ui.md +1 -1
  249. package/skills/roles/{operator.release.md → release-manager.md} +3 -3
  250. package/skills/roles/researcher.md +1 -1
  251. package/skills/roles/reviewer.md +1 -1
  252. package/skills/roles/security.ai.md +1 -1
  253. package/skills/roles/security.appsec.md +1 -1
  254. package/skills/roles/security.cloud.md +1 -1
  255. package/skills/roles/security.legal-compliance.md +1 -1
  256. package/skills/roles/security.md +1 -1
  257. package/skills/roles/security.privacy.md +1 -1
  258. package/skills/roles/security.supply-chain.md +1 -1
  259. package/skills/roles/{operator.sre.md → sre.md} +3 -3
  260. package/skills/roles/{qa.test-automation.md → test-automation.md} +2 -2
  261. package/skills/roles/{reviewer.trace.md → trace-reviewer.md} +2 -2
  262. package/skills/roles/{researcher.ux.md → ux-researcher.md} +2 -2
  263. package/skills/routing.json +18 -0
  264. package/skills/routing.md +1 -1
  265. package/specialists/artifact-manifest.json +2 -1
  266. package/specialists/audit-enrichments.json +14 -14
  267. package/specialists/org/contracts/accessibility-to-qa.json +37 -0
  268. package/specialists/org/contracts/any-to-business-strategist.json +57 -0
  269. package/specialists/org/contracts/any-to-debugger.json +38 -0
  270. package/specialists/org/contracts/any-to-designer.json +33 -0
  271. package/specialists/org/contracts/any-to-docs-keeper.json +34 -0
  272. package/specialists/org/contracts/any-to-explorer.json +39 -0
  273. package/specialists/org/contracts/any-to-sre-incident.json +33 -0
  274. package/specialists/org/contracts/any-to-trace-reviewer.json +32 -0
  275. package/specialists/org/contracts/architect-to-ai-engineer.json +32 -0
  276. package/specialists/org/contracts/architect-to-data-engineer.json +52 -0
  277. package/specialists/org/contracts/architect-to-devil-advocate.json +38 -0
  278. package/specialists/org/contracts/architect-to-engineer.json +34 -0
  279. package/specialists/org/contracts/architect-to-evaluator.json +59 -0
  280. package/specialists/org/contracts/architect-to-legal-compliance.json +55 -0
  281. package/specialists/org/contracts/architect-to-operations.json +45 -0
  282. package/specialists/org/contracts/architect-to-platform-engineer.json +42 -0
  283. package/specialists/org/contracts/business-strategist-to-product-manager.json +39 -0
  284. package/specialists/org/contracts/construct-to-orchestrator.json +36 -0
  285. package/specialists/org/contracts/construct-to-rd-lead.json +53 -0
  286. package/specialists/org/contracts/data-analyst-to-product-manager.json +43 -0
  287. package/specialists/org/contracts/data-engineer-to-platform-engineer.json +36 -0
  288. package/specialists/org/contracts/designer-to-accessibility.json +36 -0
  289. package/specialists/org/contracts/engineer-to-qa.json +34 -0
  290. package/specialists/org/contracts/engineer-to-reviewer.json +52 -0
  291. package/specialists/org/contracts/explorer-to-engineer.json +38 -0
  292. package/specialists/org/contracts/legal-compliance-to-release-manager.json +43 -0
  293. package/specialists/org/contracts/platform-engineer-to-engineer.json +31 -0
  294. package/specialists/org/contracts/product-manager-to-architect.json +71 -0
  295. package/specialists/org/contracts/product-manager-to-data-analyst.json +49 -0
  296. package/specialists/org/contracts/product-manager-to-ux-researcher.json +51 -0
  297. package/specialists/org/contracts/qa-to-release-manager.json +42 -0
  298. package/specialists/org/contracts/qa-to-test-automation.json +42 -0
  299. package/specialists/org/contracts/rd-lead-to-architect.json +38 -0
  300. package/specialists/org/contracts/researcher-to-architect.json +40 -0
  301. package/specialists/org/contracts/researcher-to-product-manager.json +57 -0
  302. package/specialists/org/contracts/reviewer-to-security.json +41 -0
  303. package/specialists/org/contracts/sre-to-release-manager.json +36 -0
  304. package/specialists/org/contracts/test-automation-to-engineer.json +34 -0
  305. package/specialists/org/contracts/trace-reviewer-to-sre.json +41 -0
  306. package/specialists/org/contracts/user-to-construct.json +30 -0
  307. package/specialists/org/groups/engineering-group.json +43 -0
  308. package/specialists/org/groups/governance-group.json +38 -0
  309. package/specialists/org/groups/operations-group.json +41 -0
  310. package/specialists/org/groups/product-group.json +46 -0
  311. package/specialists/org/groups/quality-group.json +42 -0
  312. package/specialists/org/groups/strategy-group.json +41 -0
  313. package/specialists/org/policies/action-approval.json +13 -0
  314. package/specialists/org/policies/agents-routing.json +13 -0
  315. package/specialists/org/policies/architecture.json +20 -0
  316. package/specialists/org/policies/bash-safety.json +13 -0
  317. package/specialists/org/policies/beads-hygiene.json +13 -0
  318. package/specialists/org/policies/bootstrap-state.json +13 -0
  319. package/specialists/org/policies/code-review.json +13 -0
  320. package/specialists/org/policies/coding-style.json +13 -0
  321. package/specialists/org/policies/comments.json +13 -0
  322. package/specialists/org/policies/commit-approval.json +13 -0
  323. package/specialists/org/policies/contract-preconditions.json +13 -0
  324. package/specialists/org/policies/deployment.json +20 -0
  325. package/specialists/org/policies/description.json +14 -0
  326. package/specialists/org/policies/design-approval.json +19 -0
  327. package/specialists/org/policies/doc-ownership.json +13 -0
  328. package/specialists/org/policies/file-path-fence.json +13 -0
  329. package/specialists/org/policies/framing.json +13 -0
  330. package/specialists/org/policies/git-workflow.json +13 -0
  331. package/specialists/org/policies/incident-response.json +15 -0
  332. package/specialists/org/policies/intake-triage.json +15 -0
  333. package/specialists/org/policies/neurodivergent-output.json +13 -0
  334. package/specialists/org/policies/no-fabrication.json +13 -0
  335. package/specialists/org/policies/patterns.json +13 -0
  336. package/specialists/org/policies/quality-gate-approval.json +17 -0
  337. package/specialists/org/policies/release-gates.json +13 -0
  338. package/specialists/org/policies/research-evidence.json +13 -0
  339. package/specialists/org/policies/review-before-change.json +13 -0
  340. package/specialists/org/policies/rollback.json +15 -0
  341. package/specialists/org/policies/scope-change.json +20 -0
  342. package/specialists/org/policies/secret-scan.json +13 -0
  343. package/specialists/org/policies/security-approval.json +17 -0
  344. package/specialists/org/policies/security.json +13 -0
  345. package/specialists/org/policies/session-efficiency.json +13 -0
  346. package/specialists/org/policies/skill-routing.json +13 -0
  347. package/specialists/org/policies/strategic-prioritization.json +17 -0
  348. package/specialists/org/policies/testing.json +13 -0
  349. package/specialists/org/policies/tool-invisibility.json +14 -0
  350. package/specialists/org/scopes/creative.json +54 -0
  351. package/specialists/org/scopes/operations.json +51 -0
  352. package/specialists/org/scopes/research.json +60 -0
  353. package/specialists/org/scopes/rnd.json +81 -0
  354. package/specialists/org/specialists/cx-accessibility.json +69 -0
  355. package/specialists/org/specialists/cx-ai-engineer.json +75 -0
  356. package/specialists/org/specialists/cx-architect.json +89 -0
  357. package/specialists/org/specialists/cx-business-strategist.json +72 -0
  358. package/specialists/org/specialists/cx-data-analyst.json +68 -0
  359. package/specialists/org/specialists/cx-data-engineer.json +71 -0
  360. package/specialists/org/specialists/cx-debugger.json +75 -0
  361. package/specialists/org/specialists/cx-designer.json +85 -0
  362. package/specialists/org/specialists/cx-devil-advocate.json +71 -0
  363. package/specialists/org/specialists/cx-docs-keeper.json +82 -0
  364. package/specialists/org/specialists/cx-engineer.json +106 -0
  365. package/specialists/org/specialists/cx-evaluator.json +69 -0
  366. package/specialists/org/specialists/cx-explorer.json +69 -0
  367. package/specialists/org/specialists/cx-legal-compliance.json +74 -0
  368. package/specialists/org/specialists/cx-operations.json +72 -0
  369. package/specialists/org/specialists/cx-oracle.json +46 -0
  370. package/specialists/org/specialists/cx-orchestrator.json +81 -0
  371. package/specialists/org/specialists/cx-platform-engineer.json +80 -0
  372. package/specialists/org/specialists/cx-product-manager.json +102 -0
  373. package/specialists/org/specialists/cx-qa.json +79 -0
  374. package/specialists/org/specialists/cx-rd-lead.json +73 -0
  375. package/specialists/org/specialists/cx-release-manager.json +77 -0
  376. package/specialists/org/specialists/cx-researcher.json +82 -0
  377. package/specialists/org/specialists/cx-reviewer.json +71 -0
  378. package/specialists/org/specialists/cx-security.json +91 -0
  379. package/specialists/org/specialists/cx-sre.json +94 -0
  380. package/specialists/org/specialists/cx-test-automation.json +69 -0
  381. package/specialists/org/specialists/cx-trace-reviewer.json +69 -0
  382. package/specialists/org/specialists/cx-ux-researcher.json +68 -0
  383. package/specialists/org/teams/accessibility-team.json +39 -0
  384. package/specialists/org/teams/design-team.json +40 -0
  385. package/specialists/org/teams/engineering-team.json +50 -0
  386. package/specialists/org/teams/governance-team.json +41 -0
  387. package/specialists/org/teams/operations-team.json +46 -0
  388. package/specialists/org/teams/product-management-team.json +45 -0
  389. package/specialists/org/teams/quality-team.json +49 -0
  390. package/specialists/org/teams/research-team.json +39 -0
  391. package/specialists/org/teams/strategy-team.json +48 -0
  392. package/specialists/org/teams/ux-research-team.json +39 -0
  393. package/specialists/prompts/_shared/validation-contract.md +1 -1
  394. package/specialists/prompts/_team-template.md +10 -0
  395. package/specialists/prompts/cx-accessibility.md +1 -0
  396. package/specialists/prompts/cx-ai-engineer.md +1 -1
  397. package/specialists/prompts/cx-business-strategist.md +1 -1
  398. package/specialists/prompts/cx-data-engineer.md +1 -1
  399. package/specialists/prompts/cx-designer.md +1 -0
  400. package/specialists/prompts/cx-devil-advocate.md +1 -1
  401. package/specialists/prompts/cx-docs-keeper.md +1 -1
  402. package/specialists/prompts/cx-evaluator.md +1 -1
  403. package/specialists/prompts/cx-explorer.md +1 -1
  404. package/specialists/prompts/cx-operations.md +1 -1
  405. package/specialists/prompts/cx-oracle.md +7 -3
  406. package/specialists/prompts/cx-orchestrator.md +17 -1
  407. package/specialists/prompts/cx-platform-engineer.md +1 -1
  408. package/specialists/prompts/cx-product-manager.md +2 -0
  409. package/specialists/prompts/cx-release-manager.md +1 -1
  410. package/specialists/prompts/cx-researcher.md +7 -4
  411. package/specialists/prompts/cx-sre.md +1 -1
  412. package/specialists/prompts/cx-test-automation.md +2 -2
  413. package/specialists/prompts/cx-trace-reviewer.md +3 -3
  414. package/specialists/prompts/cx-ux-researcher.md +2 -1
  415. package/templates/demos/scripts/architecture-review-adr.json +30 -0
  416. package/templates/demos/scripts/capability-contract.json +25 -0
  417. package/templates/demos/scripts/intake-triage.json +25 -0
  418. package/templates/demos/scripts/profile-doctor-health.json +25 -0
  419. package/templates/demos/tapes/agentic-platforms-prd.tape +2 -2
  420. package/templates/demos/tapes/architecture-review-adr.tape +44 -0
  421. package/templates/demos/tapes/capability-contract.tape +39 -0
  422. package/templates/demos/tapes/intake-triage.tape +39 -0
  423. package/templates/demos/tapes/profile-doctor-health.tape +39 -0
  424. package/templates/distribution/construct-brand.typ +23 -18
  425. package/templates/distribution/construct-decision.typ +1 -1
  426. package/templates/distribution/construct-pdf.typ +1 -1
  427. package/templates/distribution/construct-prd.typ +1 -1
  428. package/templates/distribution/construct-research.typ +1 -1
  429. package/templates/distribution/mermaid-puppeteer.json +8 -0
  430. package/templates/docs/persona-artifact.md +1 -1
  431. package/templates/docs/prd.md +6 -0
  432. package/apps/chat/engine/ai-sdk-agent.mjs +0 -183
  433. package/apps/chat/engine/loop-driver.mjs +0 -211
  434. package/apps/chat/engine/models.mjs +0 -122
  435. package/apps/chat/engine/provider-adapters.mjs +0 -171
  436. package/apps/chat/engine/tools/permission.mjs +0 -54
  437. package/apps/chat/engine/tools/primitives.mjs +0 -180
  438. package/apps/chat/engine/tools/registry.mjs +0 -122
  439. package/apps/chat/engine/turn-controls.mjs +0 -70
  440. package/lib/boundary.mjs +0 -127
  441. package/lib/certification/dashboard-api.mjs +0 -71
  442. package/lib/chat/cli.mjs +0 -333
  443. package/lib/chat/command-suggest.mjs +0 -161
  444. package/lib/chat/commands.mjs +0 -215
  445. package/lib/chat/config.mjs +0 -142
  446. package/lib/chat/context-compactor.mjs +0 -250
  447. package/lib/chat/context-continuation.mjs +0 -253
  448. package/lib/chat/continuation-source.mjs +0 -58
  449. package/lib/chat/demo-guide.mjs +0 -61
  450. package/lib/chat/design-tokens.mjs +0 -91
  451. package/lib/chat/desktop-binary.mjs +0 -81
  452. package/lib/chat/desktop-build.mjs +0 -130
  453. package/lib/chat/desktop-launcher.mjs +0 -133
  454. package/lib/chat/evidence.mjs +0 -145
  455. package/lib/chat/export.mjs +0 -74
  456. package/lib/chat/harness/driver.mjs +0 -91
  457. package/lib/chat/list-picker.mjs +0 -112
  458. package/lib/chat/model-picker.mjs +0 -356
  459. package/lib/chat/openrouter-fallback.mjs +0 -151
  460. package/lib/chat/permission-prompt.mjs +0 -33
  461. package/lib/chat/picker-catalog.mjs +0 -45
  462. package/lib/chat/policy-telemetry.mjs +0 -34
  463. package/lib/chat/present.mjs +0 -246
  464. package/lib/chat/session-context.mjs +0 -39
  465. package/lib/chat/session-persist.mjs +0 -73
  466. package/lib/chat/session-restore.mjs +0 -71
  467. package/lib/chat/session-settings.mjs +0 -53
  468. package/lib/chat/system-prompt.mjs +0 -52
  469. package/lib/chat/transparency.mjs +0 -93
  470. package/lib/chat/tui/color-scheme.mjs +0 -42
  471. package/lib/chat/tui/markdown.mjs +0 -123
  472. package/lib/chat/tui/presentation.mjs +0 -100
  473. package/lib/chat/tui/render.mjs +0 -500
  474. package/lib/chat/tui/turn-block.mjs +0 -284
  475. package/lib/chat/tui/turn-present.mjs +0 -18
  476. package/lib/chat/tui/turn-state.mjs +0 -88
  477. package/lib/chat/tui/usage.mjs +0 -122
  478. package/lib/chat/web-commands.mjs +0 -146
  479. package/lib/chat/web-launcher.mjs +0 -63
  480. package/lib/chat/web-picker-keys.mjs +0 -46
  481. package/lib/chat/web-session.mjs +0 -159
  482. package/lib/config/alias.mjs +0 -57
  483. package/lib/dashboard-demo.mjs +0 -71
  484. package/lib/dashboard-static.mjs +0 -175
  485. package/lib/install/desktop-binary-download.mjs +0 -88
  486. package/lib/profiles/loader.mjs +0 -123
  487. package/lib/profiles/rebrand.mjs +0 -46
  488. package/lib/server/auth.mjs +0 -169
  489. package/lib/server/chat-loop.mjs +0 -622
  490. package/lib/server/chat.mjs +0 -336
  491. package/lib/server/cors.mjs +0 -77
  492. package/lib/server/csrf.mjs +0 -103
  493. package/lib/server/demo-preview.mjs +0 -63
  494. package/lib/server/index.mjs +0 -2641
  495. package/lib/server/insights.mjs +0 -780
  496. package/lib/server/langfuse-login.mjs +0 -58
  497. package/lib/server/rate-limit.mjs +0 -93
  498. package/lib/server/telemetry-login.mjs +0 -100
  499. package/lib/server/webhook.mjs +0 -512
  500. package/specialists/contracts.json +0 -1032
  501. package/specialists/contracts.schema.json +0 -83
  502. package/specialists/policy-inventory.json +0 -188
  503. package/specialists/registry.json +0 -1412
  504. package/specialists/role-manifests.json +0 -217
  505. package/specialists/teams.json +0 -94
@@ -1,151 +0,0 @@
1
- /**
2
- * lib/chat/openrouter-fallback.mjs — OpenRouter failure parsing and turn-time fallback.
3
- *
4
- * When a free slug returns 404/429 at call time, walks the live poll for the next
5
- * candidate. When an Ollama tag is pinned but not pulled, falls through to the
6
- * next configured provider for the same turn (mirroring session launch resolution).
7
- */
8
-
9
- import { classifyProviderFailure, resolveValidatedChatModel } from '../model-router.mjs';
10
-
11
- const MAX_FALLBACK_ATTEMPTS = 3;
12
-
13
- export function parseOpenRouterError(error) {
14
- const text = typeof error === 'string' ? error : String(error?.message || error || '');
15
- let raw = text;
16
- try {
17
- const jsonMatch = text.match(/\{[\s\S]*\}/);
18
- if (jsonMatch) {
19
- const body = JSON.parse(jsonMatch[0]);
20
- raw = body?.error?.metadata?.raw || body?.error?.message || text;
21
- }
22
- } catch { /* not JSON */ }
23
-
24
- let summary = raw;
25
- const ollamaMissing = raw.match(/model ['"]([^'"]+)['"] not found/i);
26
- if (ollamaMissing) {
27
- const native = ollamaMissing[1];
28
- summary = `Ollama model '${native}' is not installed locally. Pull it with: ollama pull ${native} (or: construct ollama pull ${native})`;
29
- } else if (/rate[- ]?limit|429|temporarily rate-limited/i.test(raw)) {
30
- summary = raw.replace(/^Provider returned error\.?\s*/i, '').trim() || 'rate-limited upstream';
31
- } else if (/unavailable for free|paid version is available/i.test(raw)) {
32
- summary = 'free tier retired — use a different free model or paid slug';
33
- } else if (/Failed after \d+ attempts/i.test(text)) {
34
- summary = raw || 'provider error after retries';
35
- } else if (/OLLAMA_MODEL_NOT_PULLED|not installed locally/i.test(raw)) {
36
- summary = raw;
37
- }
38
-
39
- return { raw, summary, text };
40
- }
41
-
42
- export function ensureFailedModels(session) {
43
- if (!session.failedModels) session.failedModels = new Set();
44
- return session.failedModels;
45
- }
46
-
47
- export function recordFailedModel(session, modelId) {
48
- if (!modelId) return;
49
- ensureFailedModels(session).add(modelId);
50
- }
51
-
52
- export function getExcludeList(session) {
53
- return [...ensureFailedModels(session)];
54
- }
55
-
56
- export function shouldAttemptFreeFallback(session, modelId) {
57
- if (session?.modelMode === 'free-router') return true;
58
- return typeof modelId === 'string' && /:free$/i.test(modelId);
59
- }
60
-
61
- export function isOllamaNotPulledError(error) {
62
- const text = typeof error === 'string' ? error : String(error?.message || error || '');
63
- return error?.code === 'OLLAMA_MODEL_NOT_PULLED' || /not installed locally/i.test(text);
64
- }
65
-
66
- export async function handleOllamaNotPulledFailure({ session, error, env = process.env, currentModel }) {
67
- if (!isOllamaNotPulledError(error)) return null;
68
- if (typeof currentModel !== 'string' || !currentModel.startsWith('ollama/')) return null;
69
-
70
- recordFailedModel(session, currentModel);
71
- const excludeFamilies = typeof currentModel === 'string' && currentModel.startsWith('ollama/')
72
- ? ['ollama']
73
- : [];
74
- const next = resolveValidatedChatModel({ env, requested: null, excludeFamilies });
75
- if (!next?.id || next.id === currentModel) return null;
76
-
77
- const parsed = parseOpenRouterError(error);
78
- return {
79
- modelId: next.id,
80
- notice: `${parsed.summary} Switched to ${next.id} for this turn.`,
81
- };
82
- }
83
-
84
- export async function handleModelFailure(opts) {
85
- const openRouter = await handleOpenRouterFailure(opts);
86
- if (openRouter) return openRouter;
87
- return handleOllamaNotPulledFailure(opts);
88
- }
89
-
90
- export async function handleOpenRouterFailure({ session, error, env = process.env, currentModel }) {
91
- const parsed = parseOpenRouterError(error);
92
- const classified = classifyProviderFailure({ error: { message: parsed.text || parsed.raw } });
93
- if (!classified?.retryable) return null;
94
- if (!shouldAttemptFreeFallback(session, currentModel)) return null;
95
-
96
- recordFailedModel(session, currentModel);
97
- const exclude = getExcludeList(session);
98
- const { resolveFreeOpenRouterModel } = await import('../../apps/chat/engine/models.mjs');
99
- const next = await resolveFreeOpenRouterModel({ env, tier: 'standard', exclude });
100
- if (!next || next === currentModel) return null;
101
-
102
- const short = (id) => id.replace(/^openrouter\//, '');
103
- return {
104
- modelId: next,
105
- notice: `${short(currentModel)} failed (${parsed.summary}). Switched to ${short(next)}. Retry or /model to pick manually.`,
106
- };
107
- }
108
-
109
- export async function runTurnWithFallback({
110
- driver,
111
- text,
112
- session,
113
- layers,
114
- env,
115
- promptOptions = {},
116
- runTurnInto,
117
- onUpdate = () => {},
118
- }) {
119
- let model = session.model;
120
- let lastState = null;
121
- let lastNotice = null;
122
-
123
- for (let attempt = 0; attempt < MAX_FALLBACK_ATTEMPTS; attempt++) {
124
- lastState = await runTurnInto(
125
- driver,
126
- text,
127
- { ...promptOptions, model, turnOverlay: promptOptions.turnOverlay },
128
- { session, layers, onUpdate },
129
- );
130
-
131
- if (!lastState.error) {
132
- return { state: lastState, model, notice: lastNotice };
133
- }
134
-
135
- const fallback = await handleModelFailure({
136
- session,
137
- error: lastState.error,
138
- env,
139
- currentModel: model,
140
- });
141
- if (!fallback) break;
142
-
143
- model = fallback.modelId;
144
- session.model = model;
145
- lastNotice = fallback.notice;
146
- }
147
-
148
- return { state: lastState, model, notice: lastNotice };
149
- }
150
-
151
- export { MAX_FALLBACK_ATTEMPTS };
@@ -1,33 +0,0 @@
1
- /**
2
- * lib/chat/permission-prompt.mjs — interactive permission decisions for ask mode.
3
- *
4
- * Maps user keystrokes and readline answers to the host-agnostic decision
5
- * vocabulary (allow | allow_always | reject) shared by the permission gate
6
- * (apps/chat/engine/tools/permission.mjs). Both the Ink overlay and the linear
7
- * readline prompt use the same parser so behaviour stays identical.
8
- */
9
-
10
- export function formatPermissionQuestion({ tool = 'tool', input = null } = {}) {
11
- const detail = input && typeof input === 'object'
12
- ? Object.keys(input).slice(0, 3).join(', ')
13
- : '';
14
- const suffix = detail ? ` (${detail})` : '';
15
- return `Allow "${tool}"${suffix}? [y] once [a] always [n] reject`;
16
- }
17
-
18
- export function parsePermissionDecision(raw) {
19
- const s = String(raw ?? '').trim().toLowerCase();
20
- if (['a', 'always', 'allow_always', 'all'].includes(s)) return 'allow_always';
21
- if (['n', 'no', 'reject', 'r', 'deny', '0'].includes(s)) return 'reject';
22
- if (['y', 'yes', 'allow', 'once', '1', ''].includes(s)) return 'allow';
23
- return null;
24
- }
25
-
26
- export function parsePermissionKey(char) {
27
- if (!char) return null;
28
- const c = char.toLowerCase();
29
- if (c === 'a') return 'allow_always';
30
- if (c === 'n' || c === 'r') return 'reject';
31
- if (c === 'y') return 'allow';
32
- return null;
33
- }
@@ -1,45 +0,0 @@
1
- /**
2
- * lib/chat/picker-catalog.mjs — predefined list-picker item sets for construct chat.
3
- */
4
-
5
- import { LAYER_KEYS, PERMISSION_MODES, SANDBOX_LEVELS, INSPECTOR_MODES } from './config.mjs';
6
-
7
- export const PERMISSION_PICKER_ITEMS = Object.freeze([
8
- { id: 'allow', label: 'Allow once', detail: 'y' },
9
- { id: 'allow_always', label: 'Allow always', detail: 'a' },
10
- { id: 'reject', label: 'Reject', detail: 'n' },
11
- ]);
12
-
13
- export const BOOL_PICKER_ITEMS = Object.freeze([
14
- { id: 'on', label: 'on', detail: 'true' },
15
- { id: 'off', label: 'off', detail: 'false' },
16
- ]);
17
-
18
- export function settingKeyPickerItems() {
19
- return [
20
- { id: 'thinking', label: 'thinking', tag: 'bool' },
21
- ...LAYER_KEYS.map((k) => ({ id: k, label: k, tag: 'layer' })),
22
- { id: 'permission', label: 'permission mode', tag: 'enum' },
23
- { id: 'sandbox', label: 'sandbox', tag: 'enum' },
24
- { id: 'inspector', label: 'inspector panel', tag: 'enum' },
25
- { id: 'ascii', label: 'ascii glyphs', tag: 'bool' },
26
- { id: 'theme', label: 'color theme', tag: 'enum' },
27
- { id: 'model', label: 'model', tag: 'model' },
28
- ];
29
- }
30
-
31
- export function enumPickerItems(key) {
32
- if (key === 'permission') return PERMISSION_MODES.map((v) => ({ id: v, label: v }));
33
- if (key === 'sandbox') return SANDBOX_LEVELS.map((v) => ({ id: v, label: v }));
34
- if (key === 'inspector') return INSPECTOR_MODES.map((v) => ({ id: v, label: v }));
35
- if (key === 'theme') return ['auto', 'light', 'dark'].map((v) => ({ id: v, label: v }));
36
- return [];
37
- }
38
-
39
- export function isBoolSetting(key) {
40
- return key === 'thinking' || key === 'ascii' || LAYER_KEYS.includes(key);
41
- }
42
-
43
- export function isEnumSetting(key) {
44
- return key === 'permission' || key === 'sandbox' || key === 'inspector' || key === 'theme';
45
- }
@@ -1,34 +0,0 @@
1
- /**
2
- * lib/chat/policy-telemetry.mjs — bounded sink for execution-policy degraded-mode
3
- * events (construct-6zga.1.2 / construct-rv2x).
4
- *
5
- * When the owned loop compiles a turn's execution policy from a missing, unknown,
6
- * or degraded capability profile, the policy collapses to the conservative
7
- * envelope. The engine records that fact here instead of printing to the chat
8
- * stream so the degradation is observable (dashboard, diagnostics) without
9
- * touching the renderer event union or the live transcript. The buffer is
10
- * in-process and bounded; it is a diagnostic signal, not durable state.
11
- */
12
-
13
- const RECENT = [];
14
- const MAX_EVENTS = 50;
15
-
16
- export function recordPolicyTelemetry(event = {}) {
17
- const entry = {
18
- kind: typeof event.kind === 'string' ? event.kind : 'execution-policy-degraded',
19
- model: event.model ?? null,
20
- capabilityClass: event.capabilityClass ?? 'unknown',
21
- reasons: Array.isArray(event.reasons) ? [...event.reasons] : [],
22
- };
23
- RECENT.push(entry);
24
- if (RECENT.length > MAX_EVENTS) RECENT.shift();
25
- return entry;
26
- }
27
-
28
- export function recentPolicyTelemetry() {
29
- return RECENT.map((e) => ({ ...e, reasons: [...e.reasons] }));
30
- }
31
-
32
- export function clearPolicyTelemetry() {
33
- RECENT.length = 0;
34
- }
@@ -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
- }