@geraldmaron/construct 1.2.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (504) 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/source-checkout.mjs +3 -4
  44. package/lib/doctor/watchers/consistency.mjs +90 -22
  45. package/lib/doctor/watchers/credential-parity.mjs +97 -0
  46. package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
  47. package/lib/doctor/watchers/service-health.mjs +4 -32
  48. package/lib/document-export.mjs +73 -9
  49. package/lib/document-extract.mjs +10 -198
  50. package/lib/embed/artifact.mjs +2 -3
  51. package/lib/embed/cli.mjs +1 -1
  52. package/lib/embed/config.mjs +1 -2
  53. package/lib/embed/customer-profiles.mjs +1 -1
  54. package/lib/embed/daemon.mjs +5 -6
  55. package/lib/embed/demand-fetch.mjs +114 -2
  56. package/lib/embed/docs-lifecycle.mjs +9 -5
  57. package/lib/embed/intake-metrics.mjs +2 -2
  58. package/lib/embed/notifications.mjs +2 -3
  59. package/lib/embed/recommendation-store.mjs +25 -22
  60. package/lib/embed/role-framing.mjs +4 -9
  61. package/lib/embedded-contract/capability.mjs +4 -2
  62. package/lib/embedded-contract/contract-version.mjs +1 -1
  63. package/lib/env-config.mjs +2 -2
  64. package/lib/flavors/loader.mjs +21 -19
  65. package/lib/graph/build-from-registry.mjs +31 -7
  66. package/lib/graph/staleness.mjs +1 -1
  67. package/lib/headhunt.mjs +82 -35
  68. package/lib/hooks/agent-tracker.mjs +2 -2
  69. package/lib/hooks/config-protection.mjs +1 -1
  70. package/lib/hooks/registry-sync.mjs +3 -3
  71. package/lib/host-capabilities.mjs +5 -5
  72. package/lib/improvement/controller.mjs +2 -2
  73. package/lib/init-unified.mjs +19 -21
  74. package/lib/intake/classify.mjs +26 -0
  75. package/lib/intake/daemon.mjs +2 -3
  76. package/lib/intake/prepare.mjs +10 -3
  77. package/lib/intake/session-prelude.mjs +1 -1
  78. package/lib/intake/tables/creative.mjs +9 -9
  79. package/lib/intake/tables/operations.mjs +4 -4
  80. package/lib/intake/tables/research.mjs +2 -2
  81. package/lib/integrations/intake-integrations.mjs +9 -10
  82. package/lib/knowledge/research-store.mjs +2 -2
  83. package/lib/mcp/server.mjs +104 -61
  84. package/lib/mcp/tool-budget.mjs +1 -46
  85. package/lib/mcp/tool-recovery.mjs +44 -0
  86. package/lib/mcp/tools/artifact-author.mjs +36 -0
  87. package/lib/mcp/tools/find-tool.mjs +86 -0
  88. package/lib/mcp/tools/orchestration-run.mjs +107 -69
  89. package/lib/mcp/tools/project.mjs +4 -3
  90. package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
  91. package/lib/mcp/tools/skills.mjs +39 -22
  92. package/lib/mcp/tools/telemetry.mjs +2 -1
  93. package/lib/mcp/tools/workflow.mjs +1 -1
  94. package/lib/mcp-platform-config.mjs +7 -5
  95. package/lib/migrations/index.mjs +4 -2
  96. package/lib/migrations/v2-unified-registry.mjs +35 -0
  97. package/lib/model-router.mjs +203 -36
  98. package/lib/models/catalog.mjs +25 -9
  99. package/lib/models/execution-capability-profile.mjs +3 -3
  100. package/lib/models/execution-policy.mjs +7 -6
  101. package/lib/models/provider-poll.mjs +18 -4
  102. package/lib/opencode-config.mjs +56 -1
  103. package/lib/opencode-runtime-plugin.mjs +13 -10
  104. package/lib/oracle/artifact-gate.mjs +14 -4
  105. package/lib/oracle/cli.mjs +2 -0
  106. package/lib/oracle/dispatch.mjs +18 -1
  107. package/lib/oracle/execute.mjs +39 -7
  108. package/lib/oracle/index.mjs +1 -1
  109. package/lib/oracle/org-graph.mjs +10 -5
  110. package/lib/oracle/policy.mjs +6 -0
  111. package/lib/oracle/read-model.mjs +103 -6
  112. package/lib/oracle/reconcile.mjs +31 -4
  113. package/lib/oracle/remediation-dispatch.mjs +53 -0
  114. package/lib/oracle/routing.mjs +5 -0
  115. package/lib/oracle/synthesize.mjs +80 -3
  116. package/lib/orchestration/routing-tables.mjs +44 -10
  117. package/lib/orchestration/runtime.mjs +2 -0
  118. package/lib/orchestration/worker.mjs +1 -1
  119. package/lib/orchestration-policy.mjs +351 -62
  120. package/lib/overrides/resolver.mjs +1 -12
  121. package/lib/parity.mjs +37 -11
  122. package/lib/policy/engine.mjs +80 -15
  123. package/lib/prompt-composer.js +51 -15
  124. package/lib/prompt-metadata.mjs +3 -2
  125. package/lib/providers/connection-probe.mjs +58 -0
  126. package/lib/providers/copilot-auth.mjs +1 -1
  127. package/lib/providers/credential-bootstrap.mjs +1 -1
  128. package/lib/providers/op-run.mjs +3 -4
  129. package/lib/providers/secret-resolver.mjs +31 -0
  130. package/lib/publish-tooling.mjs +3 -11
  131. package/lib/publish.mjs +12 -29
  132. package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
  133. package/lib/reflect.mjs +2 -2
  134. package/lib/registry/agent-manifest.mjs +190 -0
  135. package/lib/registry/assemble.mjs +133 -0
  136. package/lib/registry/catalog.mjs +171 -0
  137. package/lib/registry/cli.mjs +590 -6
  138. package/lib/registry/consolidation.mjs +5 -4
  139. package/lib/registry/docs-sync.mjs +67 -0
  140. package/lib/registry/loader.mjs +147 -0
  141. package/lib/registry/org-io.mjs +76 -0
  142. package/lib/registry/retired-paths.mjs +71 -0
  143. package/lib/registry/surface-map.mjs +5 -7
  144. package/lib/registry/validator.mjs +438 -0
  145. package/lib/research-execution-policy.mjs +173 -0
  146. package/lib/roles/catalog.mjs +4 -9
  147. package/lib/roles/fence.mjs +107 -1
  148. package/lib/roles/flavor-bindings.mjs +68 -0
  149. package/lib/roles/gateway.mjs +140 -29
  150. package/lib/roles/manifest.mjs +22 -13
  151. package/lib/roles/router.mjs +1 -1
  152. package/lib/scopes/enrich.mjs +67 -0
  153. package/lib/scopes/hooks.mjs +12 -0
  154. package/lib/{profiles → scopes}/lifecycle.mjs +70 -70
  155. package/lib/scopes/loader.mjs +104 -0
  156. package/lib/scopes/rebrand.mjs +32 -0
  157. package/lib/scopes/research-profile.mjs +16 -0
  158. package/lib/scopes/teams.mjs +98 -0
  159. package/lib/service-manager.mjs +1 -117
  160. package/lib/setup-prompts.mjs +1 -1
  161. package/lib/setup.mjs +6 -26
  162. package/lib/skills/composition-graph.mjs +98 -0
  163. package/lib/skills/router.mjs +80 -0
  164. package/lib/specialist-contracts.mjs +17 -18
  165. package/lib/specialists/postconditions.mjs +2 -2
  166. package/lib/specialists/prompt-schema.mjs +6 -5
  167. package/lib/specialists/roster.mjs +7 -12
  168. package/lib/specialists/schema.mjs +9 -6
  169. package/lib/status.mjs +25 -90
  170. package/lib/storage/file-lock.mjs +6 -3
  171. package/lib/telemetry/skill-calls.mjs +1 -1
  172. package/lib/templates/doc-presentation.mjs +63 -0
  173. package/lib/templates/visual-requirements.mjs +35 -2
  174. package/lib/term-format.mjs +107 -2
  175. package/lib/test-env-setup.mjs +21 -0
  176. package/lib/ui/components.mjs +42 -0
  177. package/lib/ui/glyphs.mjs +58 -0
  178. package/lib/ui/links.mjs +115 -0
  179. package/lib/ui/theme.mjs +108 -0
  180. package/lib/uninstall/uninstall.mjs +2 -1
  181. package/lib/validator.mjs +45 -8
  182. package/lib/validators/skill-effectiveness.mjs +174 -0
  183. package/lib/validators/skills.mjs +1 -1
  184. package/package.json +12 -8
  185. package/personas/construct.md +12 -3
  186. package/platforms/claude/CLAUDE.md +1 -1
  187. package/rules/common/beads-hygiene.md +52 -0
  188. package/rules/common/neurodivergent-output.md +4 -7
  189. package/rules/common/research.md +2 -0
  190. package/scripts/sync-specialists.mjs +260 -49
  191. package/skills/ai/prompt-optimizer.md +3 -3
  192. package/skills/brand/output-vibe.md +48 -0
  193. package/skills/docs/adr-workflow.md +3 -0
  194. package/skills/docs/backlog-proposal-workflow.md +3 -0
  195. package/skills/docs/codebase-research-workflow.md +5 -2
  196. package/skills/docs/customer-profile-workflow.md +3 -0
  197. package/skills/docs/document-ingest-workflow.md +3 -0
  198. package/skills/docs/evidence-ingest-workflow.md +4 -1
  199. package/skills/docs/init-project.md +1 -1
  200. package/skills/docs/prd-workflow.md +1 -1
  201. package/skills/docs/prfaq-workflow.md +3 -0
  202. package/skills/docs/product-intelligence-workflow.md +4 -1
  203. package/skills/docs/product-signal-workflow.md +3 -0
  204. package/skills/docs/research-workflow.md +13 -6
  205. package/skills/docs/runbook-workflow.md +3 -0
  206. package/skills/docs/strategy-workflow.md +3 -0
  207. package/skills/docs/user-research-workflow.md +6 -3
  208. package/skills/operating/orchestration-reference.md +1 -1
  209. package/skills/roles/{engineer.ai.md → ai-engineer.md} +2 -2
  210. package/skills/roles/architect.ai-systems.md +1 -1
  211. package/skills/roles/architect.data.md +1 -1
  212. package/skills/roles/architect.enterprise.md +1 -1
  213. package/skills/roles/architect.integration.md +1 -1
  214. package/skills/roles/architect.md +1 -1
  215. package/skills/roles/architect.platform.md +1 -1
  216. package/skills/roles/{product-manager.business-strategy.md → business-strategist.md} +3 -3
  217. package/skills/roles/data-analyst.experiment.md +1 -1
  218. package/skills/roles/data-analyst.md +1 -1
  219. package/skills/roles/data-analyst.product-intelligence.md +1 -1
  220. package/skills/roles/data-analyst.product.md +1 -1
  221. package/skills/roles/data-analyst.telemetry.md +1 -1
  222. package/skills/roles/{engineer.data.md → data-engineer.md} +2 -2
  223. package/skills/roles/data-engineer.pipeline.md +2 -2
  224. package/skills/roles/data-engineer.vector-retrieval.md +2 -2
  225. package/skills/roles/data-engineer.warehouse.md +2 -2
  226. package/skills/roles/debugger.md +1 -1
  227. package/skills/roles/designer.accessibility.md +1 -1
  228. package/skills/roles/designer.md +1 -1
  229. package/skills/roles/{reviewer.devil-advocate.md → devil-advocate.md} +2 -2
  230. package/skills/roles/{operator.docs.md → docs-keeper.md} +3 -3
  231. package/skills/roles/engineer.md +3 -7
  232. package/skills/roles/{reviewer.evaluator.md → evaluator.md} +2 -2
  233. package/skills/roles/{researcher.explorer.md → explorer.md} +2 -2
  234. package/skills/roles/{operator.md → operations.md} +2 -5
  235. package/skills/roles/orchestrator.md +1 -1
  236. package/skills/roles/{engineer.platform.md → platform-engineer.md} +2 -2
  237. package/skills/roles/product-manager.ai-product.md +1 -1
  238. package/skills/roles/product-manager.enterprise.md +1 -1
  239. package/skills/roles/product-manager.growth.md +1 -2
  240. package/skills/roles/product-manager.md +1 -2
  241. package/skills/roles/product-manager.platform.md +1 -1
  242. package/skills/roles/product-manager.product.md +1 -1
  243. package/skills/roles/qa.ai-eval.md +1 -1
  244. package/skills/roles/qa.api-contract.md +1 -1
  245. package/skills/roles/qa.data-pipeline.md +1 -1
  246. package/skills/roles/qa.md +1 -1
  247. package/skills/roles/qa.web-ui.md +1 -1
  248. package/skills/roles/{operator.release.md → release-manager.md} +3 -3
  249. package/skills/roles/researcher.md +1 -1
  250. package/skills/roles/reviewer.md +1 -1
  251. package/skills/roles/security.ai.md +1 -1
  252. package/skills/roles/security.appsec.md +1 -1
  253. package/skills/roles/security.cloud.md +1 -1
  254. package/skills/roles/security.legal-compliance.md +1 -1
  255. package/skills/roles/security.md +1 -1
  256. package/skills/roles/security.privacy.md +1 -1
  257. package/skills/roles/security.supply-chain.md +1 -1
  258. package/skills/roles/{operator.sre.md → sre.md} +3 -3
  259. package/skills/roles/{qa.test-automation.md → test-automation.md} +2 -2
  260. package/skills/roles/{reviewer.trace.md → trace-reviewer.md} +2 -2
  261. package/skills/roles/{researcher.ux.md → ux-researcher.md} +2 -2
  262. package/skills/routing.json +18 -0
  263. package/skills/routing.md +1 -1
  264. package/specialists/artifact-manifest.json +2 -1
  265. package/specialists/audit-enrichments.json +14 -14
  266. package/specialists/org/contracts/accessibility-to-qa.json +37 -0
  267. package/specialists/org/contracts/any-to-business-strategist.json +57 -0
  268. package/specialists/org/contracts/any-to-debugger.json +38 -0
  269. package/specialists/org/contracts/any-to-designer.json +33 -0
  270. package/specialists/org/contracts/any-to-docs-keeper.json +34 -0
  271. package/specialists/org/contracts/any-to-explorer.json +39 -0
  272. package/specialists/org/contracts/any-to-sre-incident.json +33 -0
  273. package/specialists/org/contracts/any-to-trace-reviewer.json +32 -0
  274. package/specialists/org/contracts/architect-to-ai-engineer.json +32 -0
  275. package/specialists/org/contracts/architect-to-data-engineer.json +52 -0
  276. package/specialists/org/contracts/architect-to-devil-advocate.json +38 -0
  277. package/specialists/org/contracts/architect-to-engineer.json +34 -0
  278. package/specialists/org/contracts/architect-to-evaluator.json +59 -0
  279. package/specialists/org/contracts/architect-to-legal-compliance.json +55 -0
  280. package/specialists/org/contracts/architect-to-operations.json +45 -0
  281. package/specialists/org/contracts/architect-to-platform-engineer.json +42 -0
  282. package/specialists/org/contracts/business-strategist-to-product-manager.json +39 -0
  283. package/specialists/org/contracts/construct-to-orchestrator.json +36 -0
  284. package/specialists/org/contracts/construct-to-rd-lead.json +53 -0
  285. package/specialists/org/contracts/data-analyst-to-product-manager.json +43 -0
  286. package/specialists/org/contracts/data-engineer-to-platform-engineer.json +36 -0
  287. package/specialists/org/contracts/designer-to-accessibility.json +36 -0
  288. package/specialists/org/contracts/engineer-to-qa.json +34 -0
  289. package/specialists/org/contracts/engineer-to-reviewer.json +52 -0
  290. package/specialists/org/contracts/explorer-to-engineer.json +38 -0
  291. package/specialists/org/contracts/legal-compliance-to-release-manager.json +43 -0
  292. package/specialists/org/contracts/platform-engineer-to-engineer.json +31 -0
  293. package/specialists/org/contracts/product-manager-to-architect.json +71 -0
  294. package/specialists/org/contracts/product-manager-to-data-analyst.json +49 -0
  295. package/specialists/org/contracts/product-manager-to-ux-researcher.json +51 -0
  296. package/specialists/org/contracts/qa-to-release-manager.json +42 -0
  297. package/specialists/org/contracts/qa-to-test-automation.json +42 -0
  298. package/specialists/org/contracts/rd-lead-to-architect.json +38 -0
  299. package/specialists/org/contracts/researcher-to-architect.json +40 -0
  300. package/specialists/org/contracts/researcher-to-product-manager.json +57 -0
  301. package/specialists/org/contracts/reviewer-to-security.json +41 -0
  302. package/specialists/org/contracts/sre-to-release-manager.json +36 -0
  303. package/specialists/org/contracts/test-automation-to-engineer.json +34 -0
  304. package/specialists/org/contracts/trace-reviewer-to-sre.json +41 -0
  305. package/specialists/org/contracts/user-to-construct.json +30 -0
  306. package/specialists/org/groups/engineering-group.json +43 -0
  307. package/specialists/org/groups/governance-group.json +38 -0
  308. package/specialists/org/groups/operations-group.json +41 -0
  309. package/specialists/org/groups/product-group.json +46 -0
  310. package/specialists/org/groups/quality-group.json +42 -0
  311. package/specialists/org/groups/strategy-group.json +41 -0
  312. package/specialists/org/policies/action-approval.json +13 -0
  313. package/specialists/org/policies/agents-routing.json +13 -0
  314. package/specialists/org/policies/architecture.json +20 -0
  315. package/specialists/org/policies/bash-safety.json +13 -0
  316. package/specialists/org/policies/beads-hygiene.json +13 -0
  317. package/specialists/org/policies/bootstrap-state.json +13 -0
  318. package/specialists/org/policies/code-review.json +13 -0
  319. package/specialists/org/policies/coding-style.json +13 -0
  320. package/specialists/org/policies/comments.json +13 -0
  321. package/specialists/org/policies/commit-approval.json +13 -0
  322. package/specialists/org/policies/contract-preconditions.json +13 -0
  323. package/specialists/org/policies/deployment.json +20 -0
  324. package/specialists/org/policies/description.json +14 -0
  325. package/specialists/org/policies/design-approval.json +19 -0
  326. package/specialists/org/policies/doc-ownership.json +13 -0
  327. package/specialists/org/policies/file-path-fence.json +13 -0
  328. package/specialists/org/policies/framing.json +13 -0
  329. package/specialists/org/policies/git-workflow.json +13 -0
  330. package/specialists/org/policies/incident-response.json +15 -0
  331. package/specialists/org/policies/intake-triage.json +15 -0
  332. package/specialists/org/policies/neurodivergent-output.json +13 -0
  333. package/specialists/org/policies/no-fabrication.json +13 -0
  334. package/specialists/org/policies/patterns.json +13 -0
  335. package/specialists/org/policies/quality-gate-approval.json +17 -0
  336. package/specialists/org/policies/release-gates.json +13 -0
  337. package/specialists/org/policies/research-evidence.json +13 -0
  338. package/specialists/org/policies/review-before-change.json +13 -0
  339. package/specialists/org/policies/rollback.json +15 -0
  340. package/specialists/org/policies/scope-change.json +20 -0
  341. package/specialists/org/policies/secret-scan.json +13 -0
  342. package/specialists/org/policies/security-approval.json +17 -0
  343. package/specialists/org/policies/security.json +13 -0
  344. package/specialists/org/policies/session-efficiency.json +13 -0
  345. package/specialists/org/policies/skill-routing.json +13 -0
  346. package/specialists/org/policies/strategic-prioritization.json +17 -0
  347. package/specialists/org/policies/testing.json +13 -0
  348. package/specialists/org/policies/tool-invisibility.json +14 -0
  349. package/specialists/org/scopes/creative.json +54 -0
  350. package/specialists/org/scopes/operations.json +51 -0
  351. package/specialists/org/scopes/research.json +60 -0
  352. package/specialists/org/scopes/rnd.json +81 -0
  353. package/specialists/org/specialists/cx-accessibility.json +69 -0
  354. package/specialists/org/specialists/cx-ai-engineer.json +75 -0
  355. package/specialists/org/specialists/cx-architect.json +89 -0
  356. package/specialists/org/specialists/cx-business-strategist.json +72 -0
  357. package/specialists/org/specialists/cx-data-analyst.json +68 -0
  358. package/specialists/org/specialists/cx-data-engineer.json +71 -0
  359. package/specialists/org/specialists/cx-debugger.json +75 -0
  360. package/specialists/org/specialists/cx-designer.json +85 -0
  361. package/specialists/org/specialists/cx-devil-advocate.json +71 -0
  362. package/specialists/org/specialists/cx-docs-keeper.json +82 -0
  363. package/specialists/org/specialists/cx-engineer.json +106 -0
  364. package/specialists/org/specialists/cx-evaluator.json +69 -0
  365. package/specialists/org/specialists/cx-explorer.json +69 -0
  366. package/specialists/org/specialists/cx-legal-compliance.json +74 -0
  367. package/specialists/org/specialists/cx-operations.json +72 -0
  368. package/specialists/org/specialists/cx-oracle.json +46 -0
  369. package/specialists/org/specialists/cx-orchestrator.json +81 -0
  370. package/specialists/org/specialists/cx-platform-engineer.json +80 -0
  371. package/specialists/org/specialists/cx-product-manager.json +102 -0
  372. package/specialists/org/specialists/cx-qa.json +79 -0
  373. package/specialists/org/specialists/cx-rd-lead.json +73 -0
  374. package/specialists/org/specialists/cx-release-manager.json +77 -0
  375. package/specialists/org/specialists/cx-researcher.json +82 -0
  376. package/specialists/org/specialists/cx-reviewer.json +71 -0
  377. package/specialists/org/specialists/cx-security.json +91 -0
  378. package/specialists/org/specialists/cx-sre.json +94 -0
  379. package/specialists/org/specialists/cx-test-automation.json +69 -0
  380. package/specialists/org/specialists/cx-trace-reviewer.json +69 -0
  381. package/specialists/org/specialists/cx-ux-researcher.json +68 -0
  382. package/specialists/org/teams/accessibility-team.json +39 -0
  383. package/specialists/org/teams/design-team.json +40 -0
  384. package/specialists/org/teams/engineering-team.json +50 -0
  385. package/specialists/org/teams/governance-team.json +41 -0
  386. package/specialists/org/teams/operations-team.json +46 -0
  387. package/specialists/org/teams/product-management-team.json +45 -0
  388. package/specialists/org/teams/quality-team.json +49 -0
  389. package/specialists/org/teams/research-team.json +39 -0
  390. package/specialists/org/teams/strategy-team.json +48 -0
  391. package/specialists/org/teams/ux-research-team.json +39 -0
  392. package/specialists/prompts/_shared/validation-contract.md +1 -1
  393. package/specialists/prompts/_team-template.md +10 -0
  394. package/specialists/prompts/cx-accessibility.md +1 -0
  395. package/specialists/prompts/cx-ai-engineer.md +1 -1
  396. package/specialists/prompts/cx-business-strategist.md +1 -1
  397. package/specialists/prompts/cx-data-engineer.md +1 -1
  398. package/specialists/prompts/cx-designer.md +1 -0
  399. package/specialists/prompts/cx-devil-advocate.md +1 -1
  400. package/specialists/prompts/cx-docs-keeper.md +1 -1
  401. package/specialists/prompts/cx-evaluator.md +1 -1
  402. package/specialists/prompts/cx-explorer.md +1 -1
  403. package/specialists/prompts/cx-operations.md +1 -1
  404. package/specialists/prompts/cx-oracle.md +7 -3
  405. package/specialists/prompts/cx-orchestrator.md +17 -1
  406. package/specialists/prompts/cx-platform-engineer.md +1 -1
  407. package/specialists/prompts/cx-product-manager.md +2 -0
  408. package/specialists/prompts/cx-release-manager.md +1 -1
  409. package/specialists/prompts/cx-researcher.md +7 -4
  410. package/specialists/prompts/cx-sre.md +1 -1
  411. package/specialists/prompts/cx-test-automation.md +2 -2
  412. package/specialists/prompts/cx-trace-reviewer.md +3 -3
  413. package/specialists/prompts/cx-ux-researcher.md +2 -1
  414. package/templates/demos/scripts/architecture-review-adr.json +30 -0
  415. package/templates/demos/scripts/capability-contract.json +25 -0
  416. package/templates/demos/scripts/intake-triage.json +25 -0
  417. package/templates/demos/scripts/profile-doctor-health.json +25 -0
  418. package/templates/demos/tapes/agentic-platforms-prd.tape +2 -2
  419. package/templates/demos/tapes/architecture-review-adr.tape +44 -0
  420. package/templates/demos/tapes/capability-contract.tape +39 -0
  421. package/templates/demos/tapes/intake-triage.tape +39 -0
  422. package/templates/demos/tapes/profile-doctor-health.tape +39 -0
  423. package/templates/distribution/construct-brand.typ +23 -18
  424. package/templates/distribution/construct-decision.typ +1 -1
  425. package/templates/distribution/construct-pdf.typ +1 -1
  426. package/templates/distribution/construct-prd.typ +1 -1
  427. package/templates/distribution/construct-research.typ +1 -1
  428. package/templates/distribution/mermaid-puppeteer.json +8 -0
  429. package/templates/docs/persona-artifact.md +1 -1
  430. package/templates/docs/prd.md +6 -0
  431. package/apps/chat/engine/ai-sdk-agent.mjs +0 -183
  432. package/apps/chat/engine/loop-driver.mjs +0 -211
  433. package/apps/chat/engine/models.mjs +0 -122
  434. package/apps/chat/engine/provider-adapters.mjs +0 -171
  435. package/apps/chat/engine/tools/permission.mjs +0 -54
  436. package/apps/chat/engine/tools/primitives.mjs +0 -180
  437. package/apps/chat/engine/tools/registry.mjs +0 -122
  438. package/apps/chat/engine/turn-controls.mjs +0 -70
  439. package/lib/boundary.mjs +0 -127
  440. package/lib/certification/dashboard-api.mjs +0 -71
  441. package/lib/chat/cli.mjs +0 -333
  442. package/lib/chat/command-suggest.mjs +0 -161
  443. package/lib/chat/commands.mjs +0 -215
  444. package/lib/chat/config.mjs +0 -142
  445. package/lib/chat/context-compactor.mjs +0 -250
  446. package/lib/chat/context-continuation.mjs +0 -253
  447. package/lib/chat/continuation-source.mjs +0 -58
  448. package/lib/chat/demo-guide.mjs +0 -61
  449. package/lib/chat/design-tokens.mjs +0 -91
  450. package/lib/chat/desktop-binary.mjs +0 -81
  451. package/lib/chat/desktop-build.mjs +0 -130
  452. package/lib/chat/desktop-launcher.mjs +0 -133
  453. package/lib/chat/evidence.mjs +0 -145
  454. package/lib/chat/export.mjs +0 -74
  455. package/lib/chat/harness/driver.mjs +0 -91
  456. package/lib/chat/list-picker.mjs +0 -112
  457. package/lib/chat/model-picker.mjs +0 -356
  458. package/lib/chat/openrouter-fallback.mjs +0 -151
  459. package/lib/chat/permission-prompt.mjs +0 -33
  460. package/lib/chat/picker-catalog.mjs +0 -45
  461. package/lib/chat/policy-telemetry.mjs +0 -34
  462. package/lib/chat/present.mjs +0 -246
  463. package/lib/chat/session-context.mjs +0 -39
  464. package/lib/chat/session-persist.mjs +0 -73
  465. package/lib/chat/session-restore.mjs +0 -71
  466. package/lib/chat/session-settings.mjs +0 -53
  467. package/lib/chat/system-prompt.mjs +0 -52
  468. package/lib/chat/transparency.mjs +0 -93
  469. package/lib/chat/tui/color-scheme.mjs +0 -42
  470. package/lib/chat/tui/markdown.mjs +0 -123
  471. package/lib/chat/tui/presentation.mjs +0 -100
  472. package/lib/chat/tui/render.mjs +0 -500
  473. package/lib/chat/tui/turn-block.mjs +0 -284
  474. package/lib/chat/tui/turn-present.mjs +0 -18
  475. package/lib/chat/tui/turn-state.mjs +0 -88
  476. package/lib/chat/tui/usage.mjs +0 -122
  477. package/lib/chat/web-commands.mjs +0 -146
  478. package/lib/chat/web-launcher.mjs +0 -63
  479. package/lib/chat/web-picker-keys.mjs +0 -46
  480. package/lib/chat/web-session.mjs +0 -159
  481. package/lib/config/alias.mjs +0 -57
  482. package/lib/dashboard-demo.mjs +0 -71
  483. package/lib/dashboard-static.mjs +0 -175
  484. package/lib/install/desktop-binary-download.mjs +0 -88
  485. package/lib/profiles/loader.mjs +0 -123
  486. package/lib/profiles/rebrand.mjs +0 -46
  487. package/lib/server/auth.mjs +0 -169
  488. package/lib/server/chat-loop.mjs +0 -622
  489. package/lib/server/chat.mjs +0 -336
  490. package/lib/server/cors.mjs +0 -77
  491. package/lib/server/csrf.mjs +0 -103
  492. package/lib/server/demo-preview.mjs +0 -63
  493. package/lib/server/index.mjs +0 -2641
  494. package/lib/server/insights.mjs +0 -780
  495. package/lib/server/langfuse-login.mjs +0 -58
  496. package/lib/server/rate-limit.mjs +0 -93
  497. package/lib/server/telemetry-login.mjs +0 -100
  498. package/lib/server/webhook.mjs +0 -512
  499. package/specialists/contracts.json +0 -1032
  500. package/specialists/contracts.schema.json +0 -83
  501. package/specialists/policy-inventory.json +0 -188
  502. package/specialists/registry.json +0 -1412
  503. package/specialists/role-manifests.json +0 -217
  504. 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
- }