@north-light/crouter 0.3.170 → 0.3.172

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 (670) hide show
  1. package/dist/api/client.d.ts +37 -11
  2. package/dist/api/client.js +84 -15
  3. package/dist/api/dto/human.d.ts +53 -39
  4. package/dist/api/dto/human.js +2 -3
  5. package/dist/api/dto/inbox.d.ts +0 -1
  6. package/dist/api/dto/nodes.d.ts +12 -0
  7. package/dist/api/dto/review-comments.d.ts +137 -0
  8. package/dist/api/dto/review-comments.js +5 -0
  9. package/dist/api/dto/reviews.d.ts +109 -0
  10. package/dist/api/dto/reviews.js +5 -0
  11. package/dist/api/index.d.ts +2 -0
  12. package/dist/api/index.js +2 -0
  13. package/dist/api/routes.d.ts +17 -4
  14. package/dist/api/routes.js +21 -5
  15. package/dist/build-root.d.ts +1 -16
  16. package/dist/build-root.js +1 -25
  17. package/dist/builtin-memory/00-runtime-base.md +7 -5
  18. package/dist/builtin-memory/04-base-worker.md +1 -1
  19. package/dist/builtin-memory/04-orchestration-kernel.md +7 -7
  20. package/dist/builtin-memory/05-kinds/design/00-base.md +1 -1
  21. package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +1 -1
  22. package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -1
  23. package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -1
  24. package/dist/builtin-memory/05-kinds/plan/00-base.md +1 -1
  25. package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +1 -1
  26. package/dist/builtin-memory/05-kinds/review/00-base.md +1 -1
  27. package/dist/builtin-memory/05-kinds/review/companion/00-base.md +20 -0
  28. package/dist/builtin-memory/05-kinds/spec/00-base.md +3 -7
  29. package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +3 -3
  30. package/dist/builtin-memory/05-kinds/spec/requirements.md +3 -3
  31. package/dist/builtin-memory/design.md +2 -2
  32. package/dist/builtin-memory/insights/capture.md +50 -0
  33. package/dist/builtin-memory/insights/init.md +45 -0
  34. package/dist/builtin-memory/insights/listen.md +9 -0
  35. package/dist/builtin-memory/internal/agent-shaping.md +2 -2
  36. package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
  37. package/dist/builtin-memory/internal/plugins.md +12 -11
  38. package/dist/builtin-memory/internal/storage-tiers.md +2 -2
  39. package/dist/builtin-memory/{planning.md → plan/roadmap.md} +3 -3
  40. package/dist/builtin-memory/plan.md +19 -0
  41. package/dist/builtin-memory/spec/guide.md +38 -0
  42. package/dist/builtin-memory/spec/requirements.md +31 -0
  43. package/dist/builtin-memory/spec/roadmap.md +35 -0
  44. package/dist/builtin-memory/spec.md +10 -86
  45. package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +3 -4
  46. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +49 -5
  47. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +56 -0
  48. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +920 -0
  49. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +221 -156
  50. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +4 -5
  51. package/dist/cli.js +2 -6
  52. package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +7 -35
  53. package/dist/clients/attach/__tests__/attach-keybindings.test.js +32 -16
  54. package/dist/clients/attach/__tests__/context-message.test.js +142 -6
  55. package/dist/clients/attach/__tests__/crtr-output.test.js +29 -45
  56. package/dist/clients/attach/__tests__/diagram.test.js +1 -1
  57. package/dist/clients/attach/__tests__/file-review-focus.test.js +48 -0
  58. package/dist/clients/attach/__tests__/group-activity.test.js +266 -0
  59. package/dist/clients/attach/__tests__/input-controller-extension-command.test.js +32 -0
  60. package/dist/clients/attach/__tests__/session-identity-refresh.test.js +32 -0
  61. package/dist/clients/attach/__tests__/titled-editor-preview.test.js +1 -2
  62. package/dist/clients/attach/chrome/header.d.ts +10 -0
  63. package/dist/clients/attach/chrome/header.js +30 -0
  64. package/dist/clients/attach/chrome/recap.d.ts +7 -0
  65. package/dist/clients/attach/chrome/recap.js +19 -0
  66. package/dist/clients/attach/chrome/status-line.d.ts +3 -0
  67. package/dist/clients/attach/chrome/status-line.js +5 -0
  68. package/dist/clients/attach/chrome/widgets.js +11 -21
  69. package/dist/clients/attach/command.js +11 -7
  70. package/dist/clients/attach/config.d.ts +4 -0
  71. package/dist/clients/attach/config.js +3 -0
  72. package/dist/clients/attach/input/controller.d.ts +19 -0
  73. package/dist/clients/attach/input/controller.js +62 -28
  74. package/dist/clients/attach/input/titled-editor.d.ts +0 -4
  75. package/dist/clients/attach/input/titled-editor.js +2 -10
  76. package/dist/clients/attach/overlays/file-review.d.ts +9 -5
  77. package/dist/clients/attach/overlays/file-review.js +9 -70
  78. package/dist/clients/attach/overlays/help.d.ts +33 -0
  79. package/dist/clients/attach/overlays/help.js +204 -0
  80. package/dist/clients/attach/render/activity-slot.d.ts +4 -0
  81. package/dist/clients/attach/render/activity-slot.js +13 -0
  82. package/dist/clients/attach/render/chat-view.d.ts +117 -12
  83. package/dist/clients/attach/render/chat-view.js +604 -135
  84. package/dist/clients/attach/render/context-message.d.ts +2 -1
  85. package/dist/clients/attach/render/context-message.js +17 -13
  86. package/dist/clients/attach/render/crtr-output.js +1 -3
  87. package/dist/clients/attach/render/diagram.js +2 -2
  88. package/dist/clients/attach/render/edit-diff.d.ts +4 -1
  89. package/dist/clients/attach/render/edit-diff.js +7 -11
  90. package/dist/clients/attach/render/group-activity.d.ts +32 -0
  91. package/dist/clients/attach/render/group-activity.js +206 -0
  92. package/dist/clients/attach/render/group-recap.d.ts +10 -0
  93. package/dist/clients/attach/render/group-recap.js +93 -0
  94. package/dist/clients/attach/render/html-markdown.d.ts +17 -0
  95. package/dist/clients/attach/render/html-markdown.js +228 -0
  96. package/dist/clients/attach/render/measured-container.d.ts +96 -0
  97. package/dist/clients/attach/render/measured-container.js +322 -0
  98. package/dist/clients/attach/render/scroll-animation.d.ts +8 -0
  99. package/dist/clients/attach/render/scroll-animation.js +80 -0
  100. package/dist/clients/attach/render/tool-calls.js +6 -6
  101. package/dist/clients/attach/render/transcript-copy.d.ts +9 -0
  102. package/dist/clients/attach/render/transcript-copy.js +81 -0
  103. package/dist/clients/attach/render/viewport.d.ts +29 -0
  104. package/dist/clients/attach/render/viewport.js +159 -0
  105. package/dist/clients/attach/session/bindings.d.ts +10 -7
  106. package/dist/clients/attach/session/bindings.js +23 -8
  107. package/dist/clients/attach/session/connection.js +4 -5
  108. package/dist/clients/attach/session/context.d.ts +6 -4
  109. package/dist/clients/attach/session/frame.d.ts +34 -0
  110. package/dist/clients/attach/session/frame.js +176 -0
  111. package/dist/clients/attach/session/frames.d.ts +4 -1
  112. package/dist/clients/attach/session/frames.js +14 -1
  113. package/dist/clients/attach/session/identity.js +4 -3
  114. package/dist/clients/attach/session/input-wiring.d.ts +14 -4
  115. package/dist/clients/attach/session/input-wiring.js +15 -13
  116. package/dist/clients/attach/session/invariant.d.ts +9 -0
  117. package/dist/clients/attach/session/invariant.js +15 -0
  118. package/dist/clients/attach/session/keys.d.ts +10 -2
  119. package/dist/clients/attach/session/keys.js +13 -5
  120. package/dist/clients/attach/session/layout.d.ts +10 -8
  121. package/dist/clients/attach/session/layout.js +33 -34
  122. package/dist/clients/attach/session/mouse.d.ts +18 -0
  123. package/dist/clients/attach/session/mouse.js +64 -0
  124. package/dist/clients/attach/session/pane-tag.d.ts +3 -0
  125. package/dist/clients/attach/session/pane-tag.js +51 -8
  126. package/dist/clients/attach/session/reconnect.d.ts +3 -0
  127. package/dist/clients/attach/session/reconnect.js +42 -5
  128. package/dist/clients/attach/session/surface-frame.d.ts +20 -0
  129. package/dist/clients/attach/session/surface-frame.js +73 -0
  130. package/dist/clients/attach/session/surface-host.d.ts +64 -0
  131. package/dist/clients/attach/session/surface-host.js +255 -0
  132. package/dist/clients/attach/session/whip.d.ts +17 -0
  133. package/dist/clients/attach/session/whip.js +334 -0
  134. package/dist/clients/attach/slash/dispatch.d.ts +13 -1
  135. package/dist/clients/attach/slash/dispatch.js +51 -29
  136. package/dist/clients/attach/viewer.js +907 -819
  137. package/dist/clients/conversation/conversation.d.ts +65 -0
  138. package/dist/clients/conversation/conversation.js +318 -0
  139. package/dist/clients/conversation/frames.d.ts +30 -0
  140. package/dist/clients/conversation/frames.js +52 -0
  141. package/dist/clients/conversation/region.d.ts +51 -0
  142. package/dist/clients/conversation/region.js +265 -0
  143. package/dist/clients/conversation/submit.d.ts +38 -0
  144. package/dist/clients/conversation/submit.js +51 -0
  145. package/dist/clients/inbox/__tests__/inbox-controller.test.js +247 -0
  146. package/dist/clients/inbox/__tests__/mount-panel.test.js +776 -0
  147. package/dist/clients/inbox/controller.d.ts +79 -0
  148. package/dist/clients/inbox/controller.js +448 -0
  149. package/dist/clients/inbox/deck-adapter.d.ts +29 -0
  150. package/dist/clients/inbox/deck-adapter.js +62 -0
  151. package/dist/clients/inbox/layout.d.ts +8 -0
  152. package/dist/clients/inbox/layout.js +9 -0
  153. package/dist/clients/inbox/resolve.d.ts +10 -0
  154. package/dist/clients/inbox/resolve.js +34 -0
  155. package/dist/clients/inbox/review/__tests__/editor-roundtrip.test.js +29 -0
  156. package/dist/clients/inbox/review/__tests__/remap.test.js +196 -0
  157. package/dist/clients/inbox/review/__tests__/review-core.test.js +341 -0
  158. package/dist/clients/inbox/review/anchors.d.ts +41 -0
  159. package/dist/clients/inbox/review/anchors.js +89 -0
  160. package/dist/clients/inbox/review/columns.d.ts +22 -0
  161. package/dist/clients/inbox/review/columns.js +40 -0
  162. package/dist/clients/inbox/review/comments-client.d.ts +66 -0
  163. package/dist/clients/inbox/review/comments-client.js +98 -0
  164. package/dist/clients/inbox/review/composition.d.ts +60 -0
  165. package/dist/clients/inbox/review/composition.js +278 -0
  166. package/dist/clients/inbox/review/document-region.d.ts +120 -0
  167. package/dist/clients/inbox/review/document-region.js +902 -0
  168. package/dist/clients/inbox/review/frame.d.ts +45 -0
  169. package/dist/clients/inbox/review/frame.js +215 -0
  170. package/dist/clients/inbox/review/keys.d.ts +67 -0
  171. package/dist/clients/inbox/review/keys.js +130 -0
  172. package/dist/clients/inbox/review/launch.d.ts +10 -0
  173. package/dist/clients/inbox/review/launch.js +129 -0
  174. package/dist/clients/inbox/review/live-source.d.ts +24 -0
  175. package/dist/clients/inbox/review/live-source.js +160 -0
  176. package/dist/clients/inbox/review/nvim.d.ts +13 -0
  177. package/dist/clients/inbox/review/nvim.js +42 -0
  178. package/dist/clients/inbox/review/remap.d.ts +51 -0
  179. package/dist/clients/inbox/review/remap.js +157 -0
  180. package/dist/clients/inbox/review/review-client.d.ts +30 -0
  181. package/dist/clients/inbox/review/review-client.js +107 -0
  182. package/dist/clients/inbox/review/roundtrip.d.ts +5 -0
  183. package/dist/clients/inbox/review/roundtrip.js +36 -0
  184. package/dist/clients/inbox/review/state.d.ts +143 -0
  185. package/dist/clients/inbox/review/state.js +282 -0
  186. package/dist/clients/inbox/review-adapter.d.ts +29 -0
  187. package/dist/clients/inbox/review-adapter.js +92 -0
  188. package/dist/clients/inbox/review-visibility.d.ts +5 -0
  189. package/dist/clients/inbox/review-visibility.js +19 -0
  190. package/dist/clients/inbox/surface.d.ts +19 -0
  191. package/dist/clients/inbox/surface.js +128 -0
  192. package/dist/clients/inbox/tui/ansi.d.ts +66 -0
  193. package/dist/clients/inbox/tui/ansi.js +317 -0
  194. package/dist/clients/inbox/tui/clipboard.d.ts +2 -0
  195. package/dist/clients/inbox/tui/clipboard.js +26 -0
  196. package/dist/clients/inbox/tui/input.d.ts +7 -0
  197. package/dist/clients/inbox/tui/input.js +653 -0
  198. package/dist/clients/inbox/tui/keys.d.ts +28 -0
  199. package/dist/clients/inbox/tui/keys.js +111 -0
  200. package/dist/clients/inbox/tui/panel.d.ts +2 -0
  201. package/dist/clients/inbox/tui/panel.js +217 -0
  202. package/dist/clients/inbox/tui/render.d.ts +37 -0
  203. package/dist/clients/inbox/tui/render.js +476 -0
  204. package/dist/clients/inbox/tui/types.d.ts +57 -0
  205. package/dist/clients/inbox/tui/types.js +1 -0
  206. package/dist/clients/inbox/tui.d.ts +6 -0
  207. package/dist/clients/inbox/tui.js +74 -0
  208. package/dist/clients/surfaces/host.d.ts +66 -0
  209. package/dist/clients/surfaces/host.js +1 -0
  210. package/dist/clients/surfaces/region.d.ts +16 -0
  211. package/dist/clients/surfaces/region.js +1 -0
  212. package/dist/clients/web/__tests__/push-engine.test.js +14 -158
  213. package/dist/clients/web/dev-server.d.ts +3 -3
  214. package/dist/clients/web/dev-server.js +9 -23
  215. package/dist/clients/web/events.js +4 -6
  216. package/dist/clients/web/push-engine.d.ts +2 -66
  217. package/dist/clients/web/push-engine.js +30 -161
  218. package/dist/clients/web/push-registry.d.ts +0 -4
  219. package/dist/clients/web/push-registry.js +2 -7
  220. package/dist/clients/web/server.js +3 -35
  221. package/dist/clients/web/web-client/shared/protocol.d.ts +0 -108
  222. package/dist/clients/web/web-cmd.js +2 -2
  223. package/dist/commands/__tests__/human.test.js +27 -15
  224. package/dist/commands/api-client.js +3 -3
  225. package/dist/commands/canvas-browse.js +1 -1
  226. package/dist/commands/canvas-issue.js +1 -1
  227. package/dist/commands/canvas-use.js +1 -1
  228. package/dist/commands/human/doc.d.ts +2 -0
  229. package/dist/commands/human/doc.js +98 -0
  230. package/dist/commands/human/inbox.d.ts +2 -0
  231. package/dist/commands/human/inbox.js +101 -0
  232. package/dist/commands/human/prompts.d.ts +2 -3
  233. package/dist/commands/human/prompts.js +27 -180
  234. package/dist/commands/human/queue.d.ts +1 -5
  235. package/dist/commands/human/queue.js +54 -216
  236. package/dist/commands/human/review.d.ts +2 -0
  237. package/dist/commands/human/review.js +298 -0
  238. package/dist/commands/human/shared.d.ts +2 -69
  239. package/dist/commands/human/shared.js +1 -181
  240. package/dist/commands/human.js +15 -20
  241. package/dist/commands/memory/__tests__/lint.test.d.ts +1 -0
  242. package/dist/commands/memory/__tests__/lint.test.js +15 -0
  243. package/dist/commands/memory/delete.js +2 -2
  244. package/dist/commands/memory/lint.d.ts +1 -0
  245. package/dist/commands/memory/lint.js +21 -3
  246. package/dist/commands/memory/read.js +4 -4
  247. package/dist/commands/node.js +8 -7
  248. package/dist/commands/pkg/plugin-manage.js +143 -68
  249. package/dist/commands/surface-inspect.js +2 -2
  250. package/dist/commands/surface.js +6 -12
  251. package/dist/commands/sys/__tests__/setup-core.test.js +9 -5
  252. package/dist/commands/sys/__tests__/setup-front-door.test.js +7 -42
  253. package/dist/commands/sys/config.js +29 -3
  254. package/dist/commands/sys/feedback.js +1 -1
  255. package/dist/commands/sys/setup-core.d.ts +12 -11
  256. package/dist/commands/sys/setup-core.js +55 -46
  257. package/dist/commands/sys/setup-wizard.d.ts +4 -1
  258. package/dist/commands/sys/setup-wizard.js +50 -9
  259. package/dist/commands/sys/sync-deps.js +1 -1
  260. package/dist/commands/sys.js +4 -6
  261. package/dist/core/__tests__/broker-sdk-wiring.test.js +6 -1
  262. package/dist/core/__tests__/broker-snapshot-history.test.js +9 -1
  263. package/dist/core/__tests__/dead-node-policy-table.test.js +3 -4
  264. package/dist/core/__tests__/fixtures/fake-engine.d.ts +8 -5
  265. package/dist/core/__tests__/fixtures/fake-engine.js +28 -2
  266. package/dist/core/__tests__/full/broker-provider-retry.test.js +87 -2
  267. package/dist/core/__tests__/full/broker-whip-interrupt.test.d.ts +1 -0
  268. package/dist/core/__tests__/full/broker-whip-interrupt.test.js +39 -0
  269. package/dist/core/__tests__/full/detach-focus.test.js +1 -1
  270. package/dist/core/__tests__/human-deliver-e2e.test.js +64 -64
  271. package/dist/core/__tests__/human-deliver.test.js +71 -345
  272. package/dist/core/__tests__/inline-memory-refs.test.js +2 -12
  273. package/dist/core/__tests__/memory-resolver-precedence.test.js +12 -15
  274. package/dist/core/__tests__/migration.test.js +10 -0
  275. package/dist/core/__tests__/model-pin-durability.test.js +14 -0
  276. package/dist/core/__tests__/phase4-review-migration.test.d.ts +1 -0
  277. package/dist/core/__tests__/phase4-review-migration.test.js +100 -0
  278. package/dist/core/__tests__/phase4-review-store.test.d.ts +1 -0
  279. package/dist/core/__tests__/phase4-review-store.test.js +143 -0
  280. package/dist/core/__tests__/remote-canvas-target.test.js +2 -2
  281. package/dist/core/__tests__/revive.test.js +22 -53
  282. package/dist/core/__tests__/session-cycles.test.js +48 -18
  283. package/dist/core/__tests__/spawn-no-kickoff.test.js +8 -12
  284. package/dist/core/__tests__/stop-guard.test.js +16 -1
  285. package/dist/core/__tests__/tmux-surface.test.js +21 -25
  286. package/dist/core/asset-root.d.ts +7 -0
  287. package/dist/core/asset-root.js +18 -0
  288. package/dist/core/broker-client/client.d.ts +0 -7
  289. package/dist/core/broker-client/client.js +4 -10
  290. package/dist/core/broker-client/transport-relay.d.ts +3 -4
  291. package/dist/core/broker-client/transport-relay.js +3 -4
  292. package/dist/core/canvas/__tests__/attention.test.js +1 -1
  293. package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
  294. package/dist/core/canvas/__tests__/remote-transport.test.d.ts +1 -0
  295. package/dist/core/{view/__tests__/transport-remote.test.js → canvas/__tests__/remote-transport.test.js} +2 -2
  296. package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
  297. package/dist/core/canvas/attention.js +4 -4
  298. package/dist/core/canvas/canvas.d.ts +6 -11
  299. package/dist/core/canvas/canvas.js +11 -33
  300. package/dist/core/canvas/db.js +207 -38
  301. package/dist/core/canvas/extensions.d.ts +1 -1
  302. package/dist/core/canvas/extensions.js +8 -1
  303. package/dist/core/canvas/index.d.ts +0 -1
  304. package/dist/core/canvas/index.js +0 -1
  305. package/dist/core/canvas/nav-model.d.ts +1 -1
  306. package/dist/core/canvas/nav-model.js +1 -1
  307. package/dist/core/canvas/paths.d.ts +5 -0
  308. package/dist/core/canvas/paths.js +16 -0
  309. package/dist/core/canvas/remote-canvas-source.js +2 -2
  310. package/dist/core/canvas/remote-transport.d.ts +7 -0
  311. package/dist/core/{view/transport-remote.js → canvas/remote-transport.js} +3 -4
  312. package/dist/core/canvas/source.js +1 -1
  313. package/dist/core/canvas/status-glyph.js +4 -12
  314. package/dist/core/canvas/types.d.ts +10 -35
  315. package/dist/core/clipboard-text.d.ts +8 -0
  316. package/dist/core/clipboard-text.js +18 -0
  317. package/dist/core/command-manifests/registry.d.ts +1 -3
  318. package/dist/core/command-plugins/bundle.d.ts +29 -0
  319. package/dist/core/command-plugins/bundle.js +198 -0
  320. package/dist/core/command-plugins/discovery.d.ts +0 -8
  321. package/dist/core/command-plugins/discovery.js +2 -39
  322. package/dist/core/command-plugins/transport/http-fetch.d.ts +4 -30
  323. package/dist/core/command-plugins/transport/http-fetch.js +17 -95
  324. package/dist/core/command.d.ts +1 -13
  325. package/dist/core/command.js +1 -16
  326. package/dist/core/config.d.ts +4 -0
  327. package/dist/core/config.js +28 -3
  328. package/dist/core/document-lines.d.ts +4 -0
  329. package/dist/core/document-lines.js +6 -0
  330. package/dist/core/feed/feed.d.ts +2 -0
  331. package/dist/core/feed/feed.js +11 -7
  332. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +1 -0
  333. package/dist/core/human/__tests__/inbox-cancel-worker.js +7 -0
  334. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +1 -0
  335. package/dist/core/human/__tests__/inbox-claim-worker.js +17 -0
  336. package/dist/core/human/__tests__/inbox-core.test.d.ts +1 -0
  337. package/dist/core/human/__tests__/inbox-core.test.js +144 -0
  338. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +1 -0
  339. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +8 -0
  340. package/dist/core/human/__tests__/visible-paths.test.d.ts +1 -0
  341. package/dist/{clients/attach → core/human}/__tests__/visible-paths.test.js +9 -0
  342. package/dist/core/human/claim.d.ts +23 -0
  343. package/dist/core/human/claim.js +67 -0
  344. package/dist/core/human/convention.d.ts +30 -0
  345. package/dist/core/human/convention.js +202 -0
  346. package/dist/core/human/deck-factories.d.ts +8 -0
  347. package/dist/core/human/deck-factories.js +15 -0
  348. package/dist/core/human/deck-schema.d.ts +70 -0
  349. package/dist/core/human/deck-schema.js +92 -0
  350. package/dist/core/human/root.d.ts +7 -0
  351. package/dist/core/human/root.js +37 -0
  352. package/dist/core/human/scan.d.ts +3 -0
  353. package/dist/core/human/scan.js +76 -0
  354. package/dist/core/human/summary.d.ts +8 -0
  355. package/dist/core/human/summary.js +44 -0
  356. package/dist/core/human/tickets.d.ts +49 -0
  357. package/dist/core/human/tickets.js +190 -0
  358. package/dist/core/human/types.d.ts +136 -0
  359. package/dist/core/human/types.js +5 -0
  360. package/dist/{clients/attach → core/human}/visible-paths.d.ts +1 -1
  361. package/dist/{clients/attach → core/human}/visible-paths.js +3 -2
  362. package/dist/core/inspector/chrome.d.ts +6 -0
  363. package/dist/core/inspector/chrome.js +14 -0
  364. package/dist/core/inspector/contract.d.ts +105 -0
  365. package/dist/core/inspector/contract.js +1 -0
  366. package/dist/core/inspector/core.d.ts +4 -5
  367. package/dist/core/inspector/core.js +3 -5
  368. package/dist/core/inspector/host.d.ts +46 -0
  369. package/dist/core/{tui → inspector}/host.js +42 -208
  370. package/dist/core/inspector/text.d.ts +2 -2
  371. package/dist/core/inspector/transport.d.ts +6 -0
  372. package/dist/core/inspector/transport.js +36 -0
  373. package/dist/core/inspector/tui.d.ts +2 -2
  374. package/dist/core/inspector/tui.js +15 -15
  375. package/dist/core/keybindings/__tests__/inbox-affordance.test.js +11 -11
  376. package/dist/core/keybindings/__tests__/resolve.test.js +10 -19
  377. package/dist/core/keybindings/attach-control.d.ts +9 -3
  378. package/dist/core/keybindings/attach-control.js +3 -1
  379. package/dist/core/keybindings/catalog.d.ts +6 -5
  380. package/dist/core/keybindings/catalog.js +61 -133
  381. package/dist/core/keybindings/inbox.d.ts +5 -6
  382. package/dist/core/keybindings/inbox.js +9 -10
  383. package/dist/core/keybindings/index.d.ts +3 -3
  384. package/dist/core/keybindings/index.js +3 -3
  385. package/dist/core/keybindings/persistence.d.ts +5 -1
  386. package/dist/core/keybindings/persistence.js +24 -6
  387. package/dist/core/keybindings/resolve.d.ts +6 -0
  388. package/dist/core/keybindings/resolve.js +10 -3
  389. package/dist/core/memory/inline-ref-grammar.js +2 -3
  390. package/dist/core/memory-resolver.d.ts +27 -29
  391. package/dist/core/memory-resolver.js +45 -39
  392. package/dist/core/model-routes.d.ts +33 -0
  393. package/dist/core/model-routes.js +42 -3
  394. package/dist/core/preview-registry.js +17 -55
  395. package/dist/core/profiles/select.js +3 -3
  396. package/dist/core/{view/remote-canvas-target.d.ts → remote-canvas-target.d.ts} +2 -2
  397. package/dist/core/{view/remote-canvas-target.js → remote-canvas-target.js} +4 -4
  398. package/dist/core/review/__tests__/capture-origin.test.d.ts +1 -0
  399. package/dist/core/review/__tests__/capture-origin.test.js +139 -0
  400. package/dist/core/review/__tests__/comments.test.d.ts +1 -0
  401. package/dist/core/review/__tests__/comments.test.js +98 -0
  402. package/dist/core/review/__tests__/project.test.d.ts +1 -0
  403. package/dist/core/review/__tests__/project.test.js +71 -0
  404. package/dist/core/review/__tests__/remap.test.d.ts +1 -0
  405. package/dist/core/review/__tests__/remap.test.js +159 -0
  406. package/dist/core/review/__tests__/stage-identity.test.d.ts +1 -0
  407. package/dist/core/review/__tests__/stage-identity.test.js +140 -0
  408. package/dist/core/review/birth.d.ts +7 -0
  409. package/dist/core/review/birth.js +23 -0
  410. package/dist/core/review/comments.d.ts +69 -0
  411. package/dist/core/review/comments.js +295 -0
  412. package/dist/core/review/companion.d.ts +35 -0
  413. package/dist/core/review/companion.js +205 -0
  414. package/dist/core/review/document.d.ts +27 -0
  415. package/dist/core/review/document.js +138 -0
  416. package/dist/core/review/project.d.ts +13 -0
  417. package/dist/core/review/project.js +58 -0
  418. package/dist/core/review/realize.d.ts +19 -0
  419. package/dist/core/review/realize.js +217 -0
  420. package/dist/core/review/remap.d.ts +24 -0
  421. package/dist/core/review/remap.js +156 -0
  422. package/dist/core/review/signal.d.ts +5 -0
  423. package/dist/core/review/signal.js +31 -0
  424. package/dist/core/review/stage.d.ts +26 -0
  425. package/dist/core/review/stage.js +184 -0
  426. package/dist/core/review/store.d.ts +46 -0
  427. package/dist/core/review/store.js +163 -0
  428. package/dist/core/review/ticket-filter.d.ts +17 -0
  429. package/dist/core/review/ticket-filter.js +32 -0
  430. package/dist/core/review/types.d.ts +130 -0
  431. package/dist/core/review/types.js +23 -0
  432. package/dist/core/runtime/__tests__/session-visibility.test.d.ts +1 -0
  433. package/dist/core/runtime/__tests__/session-visibility.test.js +119 -0
  434. package/dist/core/runtime/bearings.d.ts +5 -17
  435. package/dist/core/runtime/bearings.js +88 -71
  436. package/dist/core/runtime/broker-protocol.d.ts +31 -8
  437. package/dist/core/runtime/broker.d.ts +15 -1
  438. package/dist/core/runtime/broker.js +573 -141
  439. package/dist/core/runtime/canvas-extensions.d.ts +4 -5
  440. package/dist/core/runtime/canvas-extensions.js +5 -5
  441. package/dist/core/runtime/front-door.js +4 -4
  442. package/dist/core/runtime/headless-pi.d.ts +24 -0
  443. package/dist/core/runtime/headless-pi.js +152 -0
  444. package/dist/core/runtime/kickoff.d.ts +1 -5
  445. package/dist/core/runtime/kickoff.js +1 -5
  446. package/dist/core/runtime/model-registry.d.ts +3 -0
  447. package/dist/core/runtime/model-registry.js +13 -0
  448. package/dist/core/runtime/naming-persist.d.ts +4 -0
  449. package/dist/core/runtime/naming-persist.js +27 -1
  450. package/dist/core/runtime/naming.d.ts +17 -12
  451. package/dist/core/runtime/naming.js +91 -140
  452. package/dist/core/runtime/node-read.js +19 -10
  453. package/dist/core/runtime/nodes.d.ts +9 -14
  454. package/dist/core/runtime/nodes.js +3 -53
  455. package/dist/core/runtime/package-health.d.ts +6 -21
  456. package/dist/core/runtime/package-health.js +12 -123
  457. package/dist/core/runtime/placement-tmux.d.ts +2 -1
  458. package/dist/core/runtime/placement-tmux.js +6 -4
  459. package/dist/core/runtime/placement.d.ts +1 -0
  460. package/dist/core/runtime/placement.js +4 -8
  461. package/dist/core/runtime/promote.js +3 -0
  462. package/dist/core/runtime/recap.d.ts +2 -7
  463. package/dist/core/runtime/recap.js +35 -69
  464. package/dist/core/runtime/revive.js +34 -77
  465. package/dist/core/runtime/session-cycles.d.ts +4 -0
  466. package/dist/core/runtime/session-cycles.js +14 -1
  467. package/dist/core/runtime/session-visibility.d.ts +37 -0
  468. package/dist/core/runtime/session-visibility.js +83 -0
  469. package/dist/core/runtime/spawn.js +10 -22
  470. package/dist/core/runtime/stop-guard.d.ts +1 -1
  471. package/dist/core/runtime/stop-guard.js +18 -13
  472. package/dist/core/runtime/surface-bg.js +1 -1
  473. package/dist/core/runtime/tmux-chrome.d.ts +1 -1
  474. package/dist/core/runtime/tmux-chrome.js +1 -1
  475. package/dist/core/runtime/tmux.d.ts +35 -7
  476. package/dist/core/runtime/tmux.js +439 -62
  477. package/dist/core/runtime/tool-group-summary.d.ts +19 -0
  478. package/dist/core/runtime/tool-group-summary.js +88 -0
  479. package/dist/core/scope.d.ts +2 -7
  480. package/dist/core/scope.js +9 -35
  481. package/dist/core/substrate/schema.d.ts +2 -2
  482. package/dist/core/termrender/code-doc.d.ts +12 -0
  483. package/dist/core/termrender/code-doc.js +92 -0
  484. package/dist/core/termrender/display.d.ts +12 -0
  485. package/dist/core/termrender/display.js +19 -0
  486. package/dist/core/termrender/termrender.d.ts +73 -0
  487. package/dist/core/termrender/termrender.js +795 -0
  488. package/dist/core/termrender/version.d.ts +1 -0
  489. package/dist/core/termrender/version.js +1 -0
  490. package/dist/core/tui/terminal.js +1 -2
  491. package/dist/core/user-settings.d.ts +71 -3
  492. package/dist/core/user-settings.js +83 -1
  493. package/dist/daemon/api/__tests__/inbox.test.js +44 -133
  494. package/dist/daemon/api/handlers/human.d.ts +0 -10
  495. package/dist/daemon/api/handlers/human.js +104 -702
  496. package/dist/daemon/api/handlers/inbox.js +113 -142
  497. package/dist/daemon/api/handlers/review-comments.d.ts +2 -0
  498. package/dist/daemon/api/handlers/review-comments.js +500 -0
  499. package/dist/daemon/api/handlers/reviews.d.ts +2 -0
  500. package/dist/daemon/api/handlers/reviews.js +287 -0
  501. package/dist/daemon/api/map.d.ts +22 -7
  502. package/dist/daemon/api/map.js +159 -11
  503. package/dist/daemon/api/server.js +4 -0
  504. package/dist/daemon/cron-run.js +6 -9
  505. package/dist/daemon/crtrd.js +17 -33
  506. package/dist/daemon/fleet.d.ts +1 -8
  507. package/dist/daemon/fleet.js +4 -30
  508. package/dist/daemon/human/finish.d.ts +14 -0
  509. package/dist/daemon/human/finish.js +115 -0
  510. package/dist/daemon/human/sweep.d.ts +6 -0
  511. package/dist/daemon/human/sweep.js +49 -0
  512. package/dist/daemon/messaging/node-message.d.ts +18 -0
  513. package/dist/daemon/messaging/node-message.js +70 -0
  514. package/dist/daemon/review/comment-notify.d.ts +26 -0
  515. package/dist/daemon/review/comment-notify.js +107 -0
  516. package/dist/daemon/review/deliver.d.ts +7 -0
  517. package/dist/daemon/review/deliver.js +56 -0
  518. package/dist/daemon/review/finish.d.ts +22 -0
  519. package/dist/daemon/review/finish.js +254 -0
  520. package/dist/daemon/review/sweep.d.ts +11 -0
  521. package/dist/daemon/review/sweep.js +120 -0
  522. package/dist/index.d.ts +2 -0
  523. package/dist/index.js +2 -2
  524. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +42 -64
  525. package/dist/pi-extensions/__tests__/canvas-recap.test.d.ts +1 -0
  526. package/dist/pi-extensions/__tests__/canvas-recap.test.js +159 -0
  527. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +16 -38
  528. package/dist/pi-extensions/__tests__/canvas-structured-output.test.d.ts +1 -0
  529. package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +63 -0
  530. package/dist/pi-extensions/canvas-context-intro.d.ts +8 -11
  531. package/dist/pi-extensions/canvas-context-intro.js +50 -104
  532. package/dist/pi-extensions/canvas-recap.d.ts +15 -2
  533. package/dist/pi-extensions/canvas-recap.js +192 -69
  534. package/dist/pi-extensions/canvas-review-boundary.d.ts +59 -0
  535. package/dist/pi-extensions/canvas-review-boundary.js +81 -0
  536. package/dist/pi-extensions/canvas-stophook.d.ts +3 -3
  537. package/dist/pi-extensions/canvas-stophook.js +78 -128
  538. package/dist/pi-extensions/canvas-structured-output.js +52 -8
  539. package/dist/shared/generated-context.d.ts +29 -4
  540. package/dist/shared/generated-context.js +122 -7
  541. package/dist/shared/tool-groups.d.ts +23 -0
  542. package/dist/shared/tool-groups.js +60 -0
  543. package/dist/shared/working-activity.d.ts +8 -0
  544. package/dist/shared/working-activity.js +26 -0
  545. package/dist/types.d.ts +36 -2
  546. package/dist/types.js +51 -0
  547. package/dist/web-client/assets/index-CCPaLeW7.css +2 -0
  548. package/dist/web-client/assets/index-kyOWsnZK.js +86 -0
  549. package/dist/web-client/index.html +2 -2
  550. package/dist/web-client/sw.js +1 -1
  551. package/docs/compat/hearth-crtr-v4.md +2 -2
  552. package/docs/compat/hearth-crtr-v5.md +4 -2
  553. package/docs/public-api.md +3 -3
  554. package/package.json +21 -14
  555. package/runtime.lock.json +4661 -1453
  556. package/scripts/postinstall.mjs +15 -2
  557. package/dist/builtin-pi-packages/pi-mode-switch/README.md +0 -36
  558. package/dist/builtin-pi-packages/pi-mode-switch/bin/mode +0 -36
  559. package/dist/builtin-pi-packages/pi-mode-switch/extensions/index.ts +0 -449
  560. package/dist/builtin-pi-packages/pi-mode-switch/package.json +0 -14
  561. package/dist/builtin-pi-packages/pi-mode-switch/tsconfig.json +0 -9
  562. package/dist/builtin-views/_lib/states.mjs +0 -161
  563. package/dist/builtin-views/canvas/core.mjs +0 -657
  564. package/dist/builtin-views/canvas/text.mjs +0 -58
  565. package/dist/builtin-views/canvas/tui.mjs +0 -168
  566. package/dist/builtin-views/canvas/web.jsx +0 -121
  567. package/dist/builtin-views/chat/core.mjs +0 -684
  568. package/dist/builtin-views/chat/text.mjs +0 -101
  569. package/dist/builtin-views/chat/tui.mjs +0 -360
  570. package/dist/builtin-views/chat/web.jsx +0 -362
  571. package/dist/builtin-views/git-pr/core.mjs +0 -673
  572. package/dist/builtin-views/git-pr/text.mjs +0 -84
  573. package/dist/builtin-views/git-pr/tui.mjs +0 -301
  574. package/dist/builtin-views/git-pr/web.jsx +0 -216
  575. package/dist/builtin-views/inbox/_lib/render.mjs +0 -175
  576. package/dist/builtin-views/inbox/core.mjs +0 -1273
  577. package/dist/builtin-views/inbox/text.mjs +0 -73
  578. package/dist/builtin-views/inbox/tui.mjs +0 -314
  579. package/dist/builtin-views/inbox/web.jsx +0 -188
  580. package/dist/builtin-views/linkedin/core.mjs +0 -906
  581. package/dist/builtin-views/linkedin/text.mjs +0 -69
  582. package/dist/builtin-views/linkedin/tui.mjs +0 -413
  583. package/dist/builtin-views/linkedin/web.jsx +0 -206
  584. package/dist/builtin-views/prompt-review/core.mjs +0 -735
  585. package/dist/builtin-views/prompt-review/text.mjs +0 -15
  586. package/dist/builtin-views/prompt-review/tui.mjs +0 -196
  587. package/dist/builtin-views/prompt-review/web.jsx +0 -484
  588. package/dist/builtin-views/workspace-sidebar/__tests__/core.test.js +0 -50
  589. package/dist/builtin-views/workspace-sidebar/__tests__/core.test.ts +0 -53
  590. package/dist/builtin-views/workspace-sidebar/core.mjs +0 -670
  591. package/dist/builtin-views/workspace-sidebar/text.mjs +0 -53
  592. package/dist/builtin-views/workspace-sidebar/tui.mjs +0 -141
  593. package/dist/builtin-views/workspace-sidebar/web.jsx +0 -109
  594. package/dist/clients/attach/session/mode.d.ts +0 -25
  595. package/dist/clients/attach/session/mode.js +0 -97
  596. package/dist/commands/sys/promptstudio.d.ts +0 -2
  597. package/dist/commands/sys/promptstudio.js +0 -65
  598. package/dist/commands/view-cycle.d.ts +0 -2
  599. package/dist/commands/view-cycle.js +0 -130
  600. package/dist/commands/view-list.d.ts +0 -2
  601. package/dist/commands/view-list.js +0 -66
  602. package/dist/commands/view-new.d.ts +0 -2
  603. package/dist/commands/view-new.js +0 -74
  604. package/dist/commands/view-pick.d.ts +0 -6
  605. package/dist/commands/view-pick.js +0 -129
  606. package/dist/commands/view-run.d.ts +0 -2
  607. package/dist/commands/view-run.js +0 -214
  608. package/dist/commands/view.d.ts +0 -2
  609. package/dist/commands/view.js +0 -31
  610. package/dist/commands/workspace.d.ts +0 -2
  611. package/dist/commands/workspace.js +0 -165
  612. package/dist/core/__tests__/chat-view-reconnect.test.js +0 -105
  613. package/dist/core/__tests__/full/consult-startup-failure.test.js +0 -124
  614. package/dist/core/canvas/__tests__/human-work-outbox.test.js +0 -123
  615. package/dist/core/canvas/human-work-outbox.d.ts +0 -73
  616. package/dist/core/canvas/human-work-outbox.js +0 -261
  617. package/dist/core/command-plugins/store.d.ts +0 -16
  618. package/dist/core/command-plugins/store.js +0 -64
  619. package/dist/core/keybindings/__tests__/bespoke-consumers.test.js +0 -40
  620. package/dist/core/runtime/fault-recovery-nudge.d.ts +0 -3
  621. package/dist/core/runtime/fault-recovery-nudge.js +0 -3
  622. package/dist/core/tui/__tests__/host-keybindings.test.js +0 -113
  623. package/dist/core/tui/host.d.ts +0 -66
  624. package/dist/core/view/__tests__/transport-cache.test.js +0 -62
  625. package/dist/core/view/bridge.d.ts +0 -10
  626. package/dist/core/view/bridge.js +0 -31
  627. package/dist/core/view/chrome.d.ts +0 -9
  628. package/dist/core/view/chrome.js +0 -22
  629. package/dist/core/view/contract.d.ts +0 -205
  630. package/dist/core/view/contract.js +0 -23
  631. package/dist/core/view/loader.d.ts +0 -31
  632. package/dist/core/view/loader.js +0 -188
  633. package/dist/core/view/stream-local.d.ts +0 -3
  634. package/dist/core/view/stream-local.js +0 -231
  635. package/dist/core/view/transport-cache.d.ts +0 -8
  636. package/dist/core/view/transport-cache.js +0 -38
  637. package/dist/core/view/transport-local.d.ts +0 -7
  638. package/dist/core/view/transport-local.js +0 -83
  639. package/dist/core/view/transport-remote.d.ts +0 -2
  640. package/dist/core/view/transport.d.ts +0 -10
  641. package/dist/core/view/transport.js +0 -15
  642. package/dist/pi-extensions/naming-tool.d.ts +0 -24
  643. package/dist/pi-extensions/naming-tool.js +0 -67
  644. package/dist/prompts/view.d.ts +0 -7
  645. package/dist/prompts/view.js +0 -172
  646. package/dist/web/ViewChrome.d.ts +0 -7
  647. package/dist/web/ViewChrome.js +0 -28
  648. package/dist/web/ViewPane.d.ts +0 -39
  649. package/dist/web/ViewPane.js +0 -48
  650. package/dist/web/index.d.ts +0 -8
  651. package/dist/web/index.js +0 -22
  652. package/dist/web/runtime.d.ts +0 -39
  653. package/dist/web/runtime.js +0 -232
  654. package/dist/web/states.d.ts +0 -24
  655. package/dist/web/states.js +0 -24
  656. package/dist/web/transport-http.d.ts +0 -5
  657. package/dist/web/transport-http.js +0 -34
  658. package/dist/web/transport-stream.d.ts +0 -3
  659. package/dist/web/transport-stream.js +0 -204
  660. package/dist/web-client/assets/index-U_NZ66VE.js +0 -79
  661. package/dist/web-client/assets/index-aaeu4adv.css +0 -2
  662. /package/dist/{builtin-views/workspace-sidebar/__tests__/core.test.d.ts → clients/attach/__tests__/file-review-focus.test.d.ts} +0 -0
  663. /package/dist/clients/attach/__tests__/{visible-paths.test.d.ts → group-activity.test.d.ts} +0 -0
  664. /package/dist/{core/__tests__/chat-view-reconnect.test.d.ts → clients/attach/__tests__/input-controller-extension-command.test.d.ts} +0 -0
  665. /package/dist/{core/__tests__/full/consult-startup-failure.test.d.ts → clients/attach/__tests__/session-identity-refresh.test.d.ts} +0 -0
  666. /package/dist/{core/canvas/__tests__/human-work-outbox.test.d.ts → clients/inbox/__tests__/inbox-controller.test.d.ts} +0 -0
  667. /package/dist/{core/keybindings/__tests__/bespoke-consumers.test.d.ts → clients/inbox/__tests__/mount-panel.test.d.ts} +0 -0
  668. /package/dist/{core/tui/__tests__/host-keybindings.test.d.ts → clients/inbox/review/__tests__/editor-roundtrip.test.d.ts} +0 -0
  669. /package/dist/{core/view/__tests__/transport-cache.test.d.ts → clients/inbox/review/__tests__/remap.test.d.ts} +0 -0
  670. /package/dist/{core/view/__tests__/transport-remote.test.d.ts → clients/inbox/review/__tests__/review-core.test.d.ts} +0 -0
@@ -0,0 +1,476 @@
1
+ import stringWidth from 'string-width';
2
+ import { renderMarkdownBlockAwareLines } from '../../../core/termrender/termrender.js';
3
+ import { RESET, BOLD, DIM, ITALIC, GREEN, YELLOW, CYAN, REVERSE, sanitize, singleLine, truncate, hline, wrap, hardWrap, centerHorizontal, clipLine, panLine, visibleWidth, } from './ansi.js';
4
+ /** Cells one h/l press pans by in the card body. */
5
+ const PAN_STEP = 8;
6
+ // ── Renderers ────────────────────────────────────────────────────────────────
7
+ export function renderOverview(state, cols, rows) {
8
+ const lines = [];
9
+ const title = `${BOLD}${CYAN} Decisions ${RESET}`;
10
+ const progress = `${state.responses.size}/${state.interactions.length} answered`;
11
+ lines.push('');
12
+ lines.push(` ${title} ${DIM}${progress}${RESET}`);
13
+ lines.push(` ${DIM}${hline(Math.min(cols - 4, 60))}${RESET}`);
14
+ lines.push('');
15
+ const rowsBuf = [];
16
+ for (let i = 0; i < state.interactions.length; i++) {
17
+ const interaction = state.interactions[i];
18
+ const response = state.responses.get(interaction.id);
19
+ const preAnswered = state.preAnsweredIds.has(interaction.id);
20
+ const icon = response
21
+ ? (preAnswered ? `${DIM}◆${RESET}` : `${GREEN}✓${RESET}`)
22
+ : `${DIM}○${RESET}`;
23
+ const label = singleLine(interaction.title);
24
+ const cursor = i === state.currentIndex ? `${CYAN}▸${RESET} ` : ' ';
25
+ const labelMax = Math.max(10, cols - 16);
26
+ rowsBuf.push({
27
+ line: ` ${cursor}${icon} ${truncate(label, labelMax)}`,
28
+ questionIndex: i,
29
+ });
30
+ if (response) {
31
+ const summary = singleLine(responseSummary(response, interaction));
32
+ const summaryMax = Math.max(10, cols - 10);
33
+ rowsBuf.push({
34
+ line: ` ${DIM}${truncate(summary, summaryMax)}${RESET}`,
35
+ questionIndex: i,
36
+ });
37
+ }
38
+ }
39
+ const reserved = 4 + 3 + 2;
40
+ const available = Math.max(1, rows - reserved);
41
+ let scroll = state.scrollOffset || 0;
42
+ const focusRow = rowsBuf.findIndex((r) => r.questionIndex === state.currentIndex);
43
+ if (focusRow >= 0) {
44
+ if (focusRow < scroll)
45
+ scroll = focusRow;
46
+ if (focusRow >= scroll + available)
47
+ scroll = focusRow - available + 1;
48
+ }
49
+ scroll = Math.max(0, Math.min(scroll, Math.max(0, rowsBuf.length - available)));
50
+ if (scroll > 0) {
51
+ lines.push(` ${DIM}↑ ${scroll} more above${RESET}`);
52
+ }
53
+ else {
54
+ lines.push('');
55
+ }
56
+ const end = Math.min(rowsBuf.length, scroll + available);
57
+ for (let i = scroll; i < end; i++)
58
+ lines.push(rowsBuf[i].line);
59
+ if (end < rowsBuf.length) {
60
+ lines.push(` ${DIM}↓ ${rowsBuf.length - end} more below${RESET}`);
61
+ }
62
+ else {
63
+ lines.push('');
64
+ }
65
+ lines.push(` ${DIM}${hline(Math.min(cols - 4, 60))}${RESET}`);
66
+ lines.push(` ${DIM}enter${RESET} review ${DIM}j/k${RESET} navigate ${DIM}q${RESET} finish`);
67
+ while (lines.length < rows)
68
+ lines.push('');
69
+ // Overview content extends roughly cols-16 wide for option labels; center
70
+ // against a 60-col cap (the divider width) when the terminal is much wider.
71
+ const centered = centerHorizontal(lines.slice(0, rows), cols, Math.min(cols, 60) + 2);
72
+ return centered;
73
+ }
74
+ /**
75
+ * Render a (possibly multiline) input buffer with a visible cursor at
76
+ * `cursor` (a code-point index). A private-use sentinel is inserted at the
77
+ * cursor position, the result is hard-wrapped (so `\n` and width-wrapping
78
+ * behave exactly as they would without a cursor), then the sentinel is
79
+ * swapped for a reverse-video block on whichever wrapped line it landed on.
80
+ * `stringWidth('\uE000') === 1`, so it occupies exactly one column and never
81
+ * throws off the wrap math.
82
+ */
83
+ export function renderInputBuffer(buffer, cursor, maxWidth) {
84
+ const chars = [...buffer];
85
+ const at = Math.max(0, Math.min(cursor, chars.length));
86
+ const withCaret = [...chars.slice(0, at), '\uE000', ...chars.slice(at)].join('');
87
+ const wrapped = hardWrap(withCaret, maxWidth);
88
+ return wrapped.map((line) => (line.includes('\uE000') ? line.replace('\uE000', `${REVERSE} ${RESET}`) : line));
89
+ }
90
+ /** Code-point bounds of every visual row `renderInputBuffer` paints, mirroring
91
+ * `hardWrap` (character-based, lossless) so a cursor index maps to the row the
92
+ * human actually sees. `end` is exclusive of the row's trailing '\n', if any. */
93
+ export function wrappedRowBounds(buffer, width) {
94
+ const chars = [...buffer];
95
+ const w = Math.max(1, width);
96
+ const rows = [];
97
+ let start = 0;
98
+ let rowW = 0;
99
+ for (let i = 0; i < chars.length; i++) {
100
+ const ch = chars[i];
101
+ if (ch === '\n') {
102
+ rows.push({ start, end: i });
103
+ start = i + 1;
104
+ rowW = 0;
105
+ continue;
106
+ }
107
+ const cw = stringWidth(ch);
108
+ if (rowW + cw > w) {
109
+ rows.push({ start, end: i });
110
+ start = i;
111
+ rowW = cw;
112
+ }
113
+ else {
114
+ rowW += cw;
115
+ }
116
+ }
117
+ rows.push({ start, end: chars.length });
118
+ return rows;
119
+ }
120
+ /** Vertical motion by *visual* row — what up/down mean in a wrapped input box,
121
+ * where one long soft-wrapped paragraph spans many rows. Returns the new
122
+ * code-point cursor, unchanged when there is no row in that direction. */
123
+ export function verticalCursor(buffer, cursor, delta, width) {
124
+ const rows = wrappedRowBounds(buffer, width);
125
+ const at = Math.max(0, Math.min(cursor, [...buffer].length));
126
+ let r = 0;
127
+ for (let i = 0; i < rows.length; i++)
128
+ if (rows[i].start <= at)
129
+ r = i;
130
+ const target = rows[r + delta];
131
+ if (target === undefined)
132
+ return at;
133
+ return Math.min(target.start + (at - rows[r].start), target.end);
134
+ }
135
+ /**
136
+ * Build the item-review frame parts (pre/body/post) and derive the body's
137
+ * scroll bounds. Pure: reads state, mutates nothing. Both `renderItemReview`
138
+ * and the pre-render `clampItemReviewScroll` go through this, so the layout
139
+ * math has a single source of truth.
140
+ */
141
+ function buildItemReviewLayout(state, cols, rows) {
142
+ const interaction = state.interactions[state.currentIndex];
143
+ const response = state.responses.get(interaction.id);
144
+ const maxW = Math.min(cols - 4, 120);
145
+ // Diagrams are pictures, not prose: they render at the pane's full width
146
+ // rather than the readability cap. The card stops being centered when one
147
+ // claims that width, so the extra columns are actually available to it.
148
+ const paneW = Math.max(20, cols - 4);
149
+ const mdLines = (md) => renderMarkdownBlockAwareLines(md, maxW, paneW);
150
+ // Pre-body: position, divider, title, subtitle (always visible)
151
+ const preLines = [];
152
+ const pos = `${state.currentIndex + 1}/${state.interactions.length}`;
153
+ preLines.push('');
154
+ preLines.push(` ${BOLD}${CYAN}[${pos}]${RESET}`);
155
+ preLines.push(` ${DIM}${hline(maxW)}${RESET}`);
156
+ preLines.push('');
157
+ for (const line of wrap(sanitize(interaction.title), maxW)) {
158
+ preLines.push(` ${BOLD}${line}${RESET}`);
159
+ }
160
+ // "Previously answered" marker — shown only while the seed is intact (no user
161
+ // override yet). The label comes from preAnswered.label so callers can be
162
+ // domain-specific ("Previously approved", "Carried over from prior pass").
163
+ if (state.preAnsweredIds.has(interaction.id)) {
164
+ const customLabel = interaction.preAnswered !== undefined ? interaction.preAnswered.label : undefined;
165
+ const label = typeof customLabel === 'string' && customLabel.length > 0
166
+ ? customLabel
167
+ : 'Previously answered';
168
+ preLines.push(` ${DIM}${ITALIC}◆ ${sanitize(label)} — press n/p to review, or any option to override${RESET}`);
169
+ }
170
+ // Rendered subtitle and question body are scrollable so long content never
171
+ // overflows the fixed header.
172
+ const bodyLines = [];
173
+ if (interaction.subtitle) {
174
+ bodyLines.push('');
175
+ for (const line of mdLines(interaction.subtitle)) {
176
+ bodyLines.push(` ${line}`);
177
+ }
178
+ }
179
+ if (interaction.body) {
180
+ bodyLines.push('');
181
+ for (const line of mdLines(interaction.body)) {
182
+ bodyLines.push(` ${line}`);
183
+ }
184
+ }
185
+ // Post-body: input buffer or actions, then footer.
186
+ const postLines = [];
187
+ postLines.push('');
188
+ if (state.inputMode) {
189
+ postLines.push(` ${DIM}${hline(maxW)}${RESET}`);
190
+ const label = interaction.freetextLabel !== undefined
191
+ ? interaction.freetextLabel
192
+ : state.inputMode.kind === 'comment' ? 'Comment' : 'Response';
193
+ // Show attached option in comment mode. For single-select the comment
194
+ // qualifies the pick; for multi-select an attached option means the
195
+ // comment is saved as a per-option note (and auto-checks the option).
196
+ let attachedLine;
197
+ if (state.inputMode.kind === 'comment') {
198
+ const attachedId = state.inputMode.selectedOptionId;
199
+ const opts = interaction.options;
200
+ if (opts.length > 0) {
201
+ const attached = attachedId !== undefined
202
+ ? opts.find((o) => o.id === attachedId)
203
+ : undefined;
204
+ const valueText = attached !== undefined
205
+ ? `${CYAN}${truncate(singleLine(attached.label), Math.max(10, maxW - 28))}${RESET}`
206
+ : `${DIM}none (overall)${RESET}`;
207
+ attachedLine = ` ${DIM}attached:${RESET} ${valueText} ${DIM}[tab to cycle]${RESET}`;
208
+ }
209
+ }
210
+ for (const labelLine of wrap(`${singleLine(label)}:`, maxW)) {
211
+ postLines.push(` ${YELLOW}${labelLine}${RESET}`);
212
+ }
213
+ const bufLines = renderInputBuffer(state.inputMode.buffer, state.inputMode.cursor, maxW - 1);
214
+ for (const line of bufLines)
215
+ postLines.push(` ${line}`);
216
+ if (attachedLine !== undefined) {
217
+ postLines.push('');
218
+ postLines.push(attachedLine);
219
+ }
220
+ postLines.push('');
221
+ postLines.push(` ${DIM}enter${RESET} submit ${DIM}^J/⌥⏎${RESET} newline${state.editorAvailable ? ` ${DIM}^O${RESET} editor` : ''} ${DIM}esc${RESET} cancel`);
222
+ }
223
+ else {
224
+ postLines.push(...renderActions(interaction, state.selectedAction, maxW, response));
225
+ }
226
+ // Transient hint (e.g. an empty multi-select Enter that was rejected). Sits
227
+ // just above the footer; cleared on the next keypress.
228
+ if (state.hint !== undefined && state.hint.length > 0) {
229
+ postLines.push('');
230
+ for (const hintLine of wrap(sanitize(state.hint), maxW)) {
231
+ postLines.push(` ${YELLOW}${hintLine}${RESET}`);
232
+ }
233
+ }
234
+ // Derive the scroll window bounds. This builder never writes back into state
235
+ // — the host clamps state.scrollOffset via clampItemReviewScroll before render.
236
+ const reservedRows = preLines.length + postLines.length + 1; // +1 for footer
237
+ const bodyHeight = Math.max(1, rows - reservedRows);
238
+ const maxScroll = Math.max(0, bodyLines.length - bodyHeight);
239
+ const overflows = bodyLines.length > bodyHeight;
240
+ // Horizontal geometry, derived from what is actually on screen: a body row
241
+ // wider than the prose column takes the whole pane (which also drops the
242
+ // centering pad below), and anything past that pans.
243
+ const scroll = Math.max(0, Math.min(state.scrollOffset || 0, maxScroll));
244
+ const visible = visibleItemBodyLines(bodyLines, scroll, bodyHeight);
245
+ let widest = 0;
246
+ for (const line of visible)
247
+ widest = Math.max(widest, visibleWidth(line));
248
+ const bodyBoxW = widest > maxW + 2 ? paneW + 2 : maxW + 2;
249
+ return {
250
+ interaction, preLines, bodyLines, postLines,
251
+ maxW, bodyHeight, maxScroll, overflows,
252
+ bodyBoxW, maxHScroll: Math.max(0, widest - bodyBoxW),
253
+ };
254
+ }
255
+ /** Body rows as they are actually painted: vertical-scroll indicators replace
256
+ * their endpoint rows, so those hidden rows cannot make h/l available. */
257
+ function visibleItemBodyLines(bodyLines, scroll, bodyHeight) {
258
+ if (bodyLines.length <= bodyHeight)
259
+ return bodyLines;
260
+ const visible = bodyLines.slice(scroll, scroll + bodyHeight);
261
+ if (scroll > 0)
262
+ visible[0] = ` ${DIM}↑ ${scroll} more above${RESET}`;
263
+ const remainingBelow = bodyLines.length - (scroll + bodyHeight);
264
+ if (remainingBelow > 0)
265
+ visible[visible.length - 1] = ` ${DIM}↓ ${remainingBelow} more below${RESET}`;
266
+ return visible;
267
+ }
268
+ /**
269
+ * Clamp state.scrollOffset to the current body's scroll bounds. The host runs
270
+ * this as a pre-render step (from the input path) so `renderItemReview` stays a
271
+ * pure function of state — the clamp keeps u/d scrolling responsive without the
272
+ * renderer mutating state mid-frame.
273
+ */
274
+ export function clampItemReviewScroll(state, cols, rows) {
275
+ const { maxScroll, maxHScroll } = buildItemReviewLayout(state, cols, rows);
276
+ state.scrollOffset = Math.max(0, Math.min(state.scrollOffset || 0, maxScroll));
277
+ state.bodyScrollOffsets[state.bodyMode] = state.scrollOffset;
278
+ // Publish the live horizontal reach so the input layer can tell whether h/l
279
+ // mean "pan" on this card, and keep the stored pan inside it across vertical
280
+ // scrolling and resize.
281
+ state.hscrollMax = maxHScroll;
282
+ state.hscrollOffset = Math.max(0, Math.min(state.hscrollOffset || 0, maxHScroll));
283
+ }
284
+ /** Pan the card body horizontally. Clamping happens in the pre-render clamp
285
+ * the host already runs, exactly like vertical over-scroll. */
286
+ export function panItemReview(state, direction) {
287
+ state.hscrollOffset = Math.max(0, (state.hscrollOffset || 0) + direction * PAN_STEP);
288
+ }
289
+ export function renderItemReview(state, cols, rows) {
290
+ const { interaction, preLines, bodyLines, postLines, maxW, bodyHeight, maxScroll, overflows, bodyBoxW, maxHScroll } = buildItemReviewLayout(state, cols, rows);
291
+ // Read-only clamp: renderer never mutates state (clampItemReviewScroll, run
292
+ // pre-render, keeps state.scrollOffset itself in bounds).
293
+ const scroll = Math.max(0, Math.min(state.scrollOffset || 0, maxScroll));
294
+ let visibleBody = visibleItemBodyLines(bodyLines, scroll, bodyHeight);
295
+ // Contain every body row in the card's rectangle: rows that fit are
296
+ // untouched, a diagram wider than the pane pans under h/l with ‹/› marking
297
+ // the content still off-screen.
298
+ const hscroll = Math.max(0, Math.min(state.hscrollOffset || 0, maxHScroll));
299
+ visibleBody = visibleBody.map((line) => panLine(line, hscroll, bodyBoxW));
300
+ // Footer hint — mention scroll keys when body overflows
301
+ const footerParts = interaction.multiSelect === true
302
+ ? [
303
+ `${DIM}n/p${RESET} prev/next`,
304
+ `${DIM}space${RESET} toggle`,
305
+ `${DIM}enter${RESET} confirm`,
306
+ `${DIM}q${RESET} overview`,
307
+ ]
308
+ : [
309
+ `${DIM}n/p${RESET} prev/next`,
310
+ `${DIM}q${RESET} overview`,
311
+ ];
312
+ if (overflows) {
313
+ footerParts.unshift(state.inputMode ? `${DIM}pgup/pgdn${RESET} scroll` : `${DIM}u/d${RESET} scroll`);
314
+ }
315
+ if (maxHScroll > 0 && state.inputMode === null) {
316
+ footerParts.unshift(`${DIM}h/l${RESET} pan`);
317
+ }
318
+ const footer = ` ${footerParts.join(' ')}`;
319
+ // Assemble — pad to fill rows so post-body sits at the bottom
320
+ const lines = [...preLines, ...visibleBody, ...postLines];
321
+ while (lines.length < rows - 1)
322
+ lines.push('');
323
+ lines.push(footer);
324
+ // Final clamp (safety net for very small terminals)
325
+ const clamped = lines.length > rows
326
+ ? [...lines.slice(0, rows - 1), footer]
327
+ : lines;
328
+ // Content occupies the body rectangle (prose column, or the full pane when a
329
+ // diagram claims it) — center the whole block when the terminal is wider,
330
+ // then contain every row so nothing can wrap past the card's rectangle.
331
+ return centerHorizontal(clamped, cols, bodyBoxW).map((line) => clipLine(line, cols));
332
+ }
333
+ /** The option list. Focus is always drawn so j/k and shortcuts show where they land. */
334
+ function renderActions(interaction, selectedAction, maxW, existing) {
335
+ const lines = [];
336
+ const opts = interaction.options;
337
+ // Prefix on first row: " X [s] " — 2 + 1 (cursor) + 1 + 3 ([s]) + 1 = 8 visible cols.
338
+ // Continuation rows align under the label so each option reads as a block.
339
+ const multi = interaction.multiSelect === true;
340
+ const checked = new Set(existing?.selectedOptionIds ?? []);
341
+ const prefixWidth = multi ? 12 : 8;
342
+ const indent = ' '.repeat(prefixWidth);
343
+ const contentMax = Math.max(20, maxW - prefixWidth);
344
+ const optionComments = existing !== undefined ? existing.optionComments : undefined;
345
+ for (let i = 0; i < opts.length; i++) {
346
+ const o = opts[i];
347
+ const cursor = i === selectedAction ? `${CYAN}▸${RESET}` : ' ';
348
+ const sc = o.shortcut === undefined ? ' ' : o.shortcut;
349
+ const keyBadge = `${DIM}[${sc}]${RESET}`;
350
+ const box = multi
351
+ ? (checked.has(o.id) ? `${GREEN}[x]${RESET}` : `${DIM}[ ]${RESET}`) + ' '
352
+ : '';
353
+ const labelLines = wrap(sanitize(o.label), contentMax);
354
+ for (let j = 0; j < labelLines.length; j++) {
355
+ const prefix = j === 0 ? ` ${cursor} ${box}${keyBadge} ` : indent;
356
+ lines.push(`${prefix}${labelLines[j]}`);
357
+ }
358
+ if (o.description) {
359
+ const descLines = wrap(`— ${sanitize(o.description)}`, contentMax);
360
+ for (const dl of descLines) {
361
+ lines.push(`${indent}${DIM}${dl}${RESET}`);
362
+ }
363
+ }
364
+ if (multi && optionComments !== undefined) {
365
+ const note = optionComments[o.id];
366
+ if (typeof note === 'string' && note.length > 0) {
367
+ const noteLines = wrap(`✎ ${sanitize(note)}`, contentMax);
368
+ for (const nl of noteLines) {
369
+ lines.push(`${indent}${YELLOW}${nl}${RESET}`);
370
+ }
371
+ }
372
+ }
373
+ }
374
+ if (interaction.allowFreetext && opts.length > 0) {
375
+ const cursor = opts.length === selectedAction ? `${CYAN}▸${RESET}` : ' ';
376
+ let label;
377
+ if (interaction.freetextLabel !== undefined)
378
+ label = interaction.freetextLabel;
379
+ else if (multi)
380
+ label = 'Add overall comment (c on an option for per-option)';
381
+ else
382
+ label = 'Add comment';
383
+ const ftLines = wrap(sanitize(label), contentMax);
384
+ for (let j = 0; j < ftLines.length; j++) {
385
+ const prefix = j === 0 ? ` ${cursor} ${DIM}[c]${RESET} ` : ' '.repeat(8);
386
+ lines.push(`${prefix}${ftLines[j]}`);
387
+ }
388
+ }
389
+ else if (interaction.allowFreetext && opts.length === 0) {
390
+ const ftLabel = interaction.freetextLabel !== undefined ? interaction.freetextLabel : 'Enter response';
391
+ const ftLines = wrap(sanitize(ftLabel), contentMax);
392
+ for (let j = 0; j < ftLines.length; j++) {
393
+ const prefix = j === 0 ? ` ${DIM}[r]${RESET} ` : ' '.repeat(6);
394
+ lines.push(`${prefix}${ftLines[j]}`);
395
+ }
396
+ }
397
+ if (existing) {
398
+ lines.push('');
399
+ for (const curLine of wrap(`Current: ${responseSummary(existing, interaction)}`, maxW)) {
400
+ lines.push(` ${GREEN}${curLine}${RESET}`);
401
+ }
402
+ }
403
+ return lines;
404
+ }
405
+ export function renderFinal(state, cols, rows) {
406
+ const header = [];
407
+ const footer = [];
408
+ const maxW = Math.min(cols - 4, 60);
409
+ const total = state.interactions.length;
410
+ const answered = state.responses.size;
411
+ header.push('');
412
+ header.push(` ${BOLD}${CYAN} Summary ${RESET}`);
413
+ header.push(` ${DIM}${hline(maxW)}${RESET}`);
414
+ header.push('');
415
+ header.push(` ${answered}/${total} questions answered`);
416
+ header.push('');
417
+ footer.push('');
418
+ footer.push(` ${DIM}${hline(maxW)}${RESET}`);
419
+ if (answered < total) {
420
+ footer.push(` ${YELLOW}${total - answered} unanswered — press p to go back${RESET}`);
421
+ }
422
+ footer.push(` ${DIM}enter${RESET} submit ${DIM}p${RESET} go back`);
423
+ const questionRows = [];
424
+ for (const interaction of state.interactions) {
425
+ const response = state.responses.get(interaction.id);
426
+ const preAnswered = state.preAnsweredIds.has(interaction.id);
427
+ const icon = response
428
+ ? (preAnswered ? `${DIM}◆${RESET}` : `${GREEN}✓${RESET}`)
429
+ : `${YELLOW}○${RESET}`;
430
+ const label = singleLine(interaction.title);
431
+ questionRows.push(` ${icon} ${truncate(label, Math.max(10, maxW - 4))}`);
432
+ if (response) {
433
+ questionRows.push(` ${DIM}${truncate(singleLine(responseSummary(response, interaction)), Math.max(10, maxW - 6))}${RESET}`);
434
+ }
435
+ }
436
+ const available = Math.max(1, rows - header.length - footer.length - 1);
437
+ let visible = questionRows;
438
+ if (questionRows.length > available) {
439
+ visible = [
440
+ ...questionRows.slice(0, available - 1),
441
+ ` ${DIM}… ${questionRows.length - (available - 1)} more rows omitted${RESET}`,
442
+ ];
443
+ }
444
+ const lines = [...header, ...visible, ...footer];
445
+ while (lines.length < rows)
446
+ lines.push('');
447
+ return centerHorizontal(lines.slice(0, rows), cols, maxW + 2);
448
+ }
449
+ export function responseSummary(r, interaction) {
450
+ if (r.selectedOptionIds !== undefined) {
451
+ const oc = r.optionComments;
452
+ const parts = r.selectedOptionIds
453
+ .map((id) => interaction.options.find((o) => o.id === id))
454
+ .filter((o) => o !== undefined)
455
+ .map((o) => {
456
+ const note = oc !== undefined ? oc[o.id] : undefined;
457
+ return typeof note === 'string' && note.length > 0
458
+ ? `${sanitize(o.label)} ("${sanitize(note)}")`
459
+ : sanitize(o.label);
460
+ });
461
+ const picks = parts.length > 0 ? parts.join(', ') : '(none)';
462
+ if (r.freetext)
463
+ return `${picks}: "${sanitize(r.freetext)}"`;
464
+ return picks;
465
+ }
466
+ const opt = r.selectedOptionId
467
+ ? interaction.options.find((o) => o.id === r.selectedOptionId)
468
+ : undefined;
469
+ if (opt && r.freetext)
470
+ return `${sanitize(opt.label)}: "${sanitize(r.freetext)}"`;
471
+ if (opt)
472
+ return sanitize(opt.label);
473
+ if (r.freetext)
474
+ return sanitize(r.freetext);
475
+ return '(empty)';
476
+ }
@@ -0,0 +1,57 @@
1
+ import type { Key } from './keys.js';
2
+ import type { Deck, Interaction, InteractionResponse } from '../../../core/human/types.js';
3
+ export type Phase = 'overview' | 'item-review' | 'final';
4
+ export type InputMode = null | {
5
+ kind: 'comment';
6
+ buffer: string;
7
+ cursor: number;
8
+ selectedOptionId?: string;
9
+ } | {
10
+ kind: 'freetext';
11
+ buffer: string;
12
+ cursor: number;
13
+ };
14
+ export interface TuiState {
15
+ phase: Phase;
16
+ currentIndex: number;
17
+ interactions: Interaction[];
18
+ responses: Map<string, InteractionResponse>;
19
+ preAnsweredIds: Set<string>;
20
+ inputMode: InputMode;
21
+ selectedAction: number;
22
+ bodyMode: 'question';
23
+ scrollOffset: number;
24
+ bodyScrollOffsets: {
25
+ question: number;
26
+ };
27
+ hscrollOffset: number;
28
+ hscrollMax: number;
29
+ wrapWidth: number;
30
+ editorAvailable: boolean;
31
+ hint?: string;
32
+ persist?: () => void;
33
+ }
34
+ export interface MountedPanelOpts {
35
+ deck: Deck;
36
+ progressPath?: string;
37
+ onEditorRequest?: () => void;
38
+ cols: number;
39
+ rows: number;
40
+ onProgress?: (responses: InteractionResponse[]) => void;
41
+ onComplete?: (responses: InteractionResponse[]) => void;
42
+ onExit?: () => void;
43
+ onDirty?: () => void;
44
+ }
45
+ export interface MountedPanel {
46
+ handleKey(input: string, key: Key): void;
47
+ render(): string[];
48
+ handleResize(cols: number, rows: number): string[];
49
+ unmount(): void;
50
+ loadDeck(deck: Deck, opts?: {
51
+ progressPath?: string;
52
+ }): void;
53
+ canAcceptHostKeys(): boolean;
54
+ atDeckTop(): boolean;
55
+ getInputBuffer(): string | undefined;
56
+ setInputBuffer(text: string): void;
57
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { TicketSummary } from '../../core/human/types.js';
2
+ export declare const KIND_ICON: Record<string, string>;
3
+ export declare const KIND_COLOR: Record<string, string>;
4
+ export declare function formatTimeAgo(iso: string): string;
5
+ /** Pure inbox rows; selection and terminal ownership belong to InboxController. */
6
+ export declare function buildInboxLines(items: TicketSummary[], width: number, selectedIndex: number, height?: number): string[];
@@ -0,0 +1,74 @@
1
+ import { RESET, BOLD, DIM, ITALIC, CYAN, RED, GRAY, YELLOW, truncateRow } from './tui/ansi.js';
2
+ function ansiColor(text, color) {
3
+ switch (color) {
4
+ case 'gray': return `${GRAY}${text}${RESET}`;
5
+ case 'cyan': return `${CYAN}${text}${RESET}`;
6
+ case 'red': return `${RED}${text}${RESET}`;
7
+ case 'yellow': return `${YELLOW}${text}${RESET}`;
8
+ default: return text;
9
+ }
10
+ }
11
+ export const KIND_ICON = { notify: '✉', decision: '◆', context: '✎', error: '⚠', review: '▤' };
12
+ export const KIND_COLOR = { notify: 'gray', decision: 'cyan', context: 'cyan', error: 'red', review: 'yellow' };
13
+ export function formatTimeAgo(iso) {
14
+ const minutes = Math.floor((Date.now() - new Date(iso).getTime()) / 60000);
15
+ if (minutes >= 60)
16
+ return `${Math.floor(minutes / 60)}h ago`;
17
+ if (minutes > 0)
18
+ return `${minutes}m ago`;
19
+ return 'just now';
20
+ }
21
+ /** Pure inbox rows; selection and terminal ownership belong to InboxController. */
22
+ export function buildInboxLines(items, width, selectedIndex, height = Number.MAX_SAFE_INTEGER) {
23
+ if (items.length === 0)
24
+ return [` ${DIM}${ITALIC}No pending interactions${RESET}`];
25
+ const contentWidth = width - 4;
26
+ const itemLines = items.map((item, index) => buildItemLines(item, index, selectedIndex, contentWidth));
27
+ const window = visibleWindow(itemLines, selectedIndex, Math.max(1, height - 2));
28
+ const lines = [` ${BOLD}${items.length} pending${RESET}`, ''];
29
+ if (window.start > 0)
30
+ lines.push(` ${DIM}↑ ${window.start} above${RESET}`);
31
+ for (let index = window.start; index < window.end; index++)
32
+ lines.push(...itemLines[index]);
33
+ if (window.end < items.length)
34
+ lines.push(` ${DIM}↓ ${items.length - window.end} below${RESET}`);
35
+ return lines;
36
+ }
37
+ function buildItemLines(item, index, selectedIndex, contentWidth) {
38
+ const kind = item.kind === 'deck' ? item.interactionKind ?? 'decision' : 'review';
39
+ const icon = KIND_ICON[kind] ?? '·';
40
+ // The inbox is a title-first triage surface. Origin details remain available
41
+ // after opening a ticket, rather than consuming the list's scarce row width.
42
+ const age = formatTimeAgo(item.blockedSince);
43
+ const cursor = index === selectedIndex ? `${CYAN}▸${RESET} ` : ' ';
44
+ const titleWidth = Math.max(1, contentWidth - age.length - 6);
45
+ const row = `${cursor}${ansiColor(icon, KIND_COLOR[kind] ?? 'cyan')} ${BOLD}${truncateRow(item.title || `(${item.id.slice(0, 8)})`, titleWidth)}${RESET} ${DIM}${age}${RESET}`;
46
+ const subtitle = item.claim ? `claimed by ${item.claim.owner} · ${item.subtitle}` : item.subtitle;
47
+ return [row, ` ${DIM}${truncateRow(subtitle, contentWidth - 6)}${RESET}`];
48
+ }
49
+ /** Select a contiguous row window that always contains the selected ticket. */
50
+ function visibleWindow(rows, selectedIndex, capacity) {
51
+ let start = Math.max(0, Math.min(selectedIndex, rows.length - 1));
52
+ let end = start + 1;
53
+ let preferBelow = true;
54
+ while (true) {
55
+ const below = end < rows.length ? { start, end: end + 1 } : undefined;
56
+ const above = start > 0 ? { start: start - 1, end } : undefined;
57
+ const preferred = preferBelow ? [below, above] : [above, below];
58
+ const next = preferred.find((candidate) => candidate !== undefined && windowHeight(rows, candidate.start, candidate.end) <= capacity);
59
+ if (next === undefined)
60
+ break;
61
+ start = next.start;
62
+ end = next.end;
63
+ preferBelow = !preferBelow;
64
+ }
65
+ return { start, end };
66
+ }
67
+ function windowHeight(rows, start, end) {
68
+ let height = start > 0 ? 1 : 0;
69
+ for (let index = start; index < end; index++)
70
+ height += rows[index].length;
71
+ if (end < rows.length)
72
+ height++;
73
+ return height;
74
+ }