@oh-my-pi/pi-tui 18.2.4 → 18.2.6

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 (748) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +43 -0
  3. package/dist/types/app-keybindings.d.ts +395 -0
  4. package/dist/types/apps/autoresearch-dashboard.d.ts +43 -0
  5. package/dist/types/apps/autoresearch-data.d.ts +15 -0
  6. package/dist/types/apps/cleanse-board.d.ts +66 -0
  7. package/dist/types/apps/cleanse-picker.d.ts +23 -0
  8. package/dist/types/apps/debug/log-formatting.d.ts +6 -0
  9. package/dist/types/apps/debug/log-viewer.d.ts +81 -0
  10. package/dist/types/apps/debug/protocol-probe.d.ts +42 -0
  11. package/dist/types/apps/debug/raw-sse-buffer.d.ts +58 -0
  12. package/dist/types/apps/debug/raw-sse.d.ts +26 -0
  13. package/dist/types/apps/debug/terminal-info.d.ts +34 -0
  14. package/dist/types/apps/debug/viewer-frame.d.ts +52 -0
  15. package/dist/types/apps/git/avatar.d.ts +9 -0
  16. package/dist/types/apps/git/colors.d.ts +49 -0
  17. package/dist/types/apps/git/diff-pane.d.ts +188 -0
  18. package/dist/types/apps/git/git-tui.d.ts +66 -0
  19. package/dist/types/apps/git/sidebar.d.ts +149 -0
  20. package/dist/types/apps/git/state.d.ts +117 -0
  21. package/dist/types/apps/if-bench-board.d.ts +75 -0
  22. package/dist/types/apps/live-visualizer.d.ts +33 -0
  23. package/dist/types/apps/ps-data.d.ts +51 -0
  24. package/dist/types/apps/ps-top.d.ts +33 -0
  25. package/dist/types/apps/session-picker.d.ts +26 -0
  26. package/dist/types/apps/setup-model-picker.d.ts +13 -0
  27. package/dist/types/apps/standalone-picker.d.ts +42 -0
  28. package/dist/types/autocomplete.d.ts +4 -0
  29. package/dist/types/chat/advisor-message.d.ts +11 -0
  30. package/dist/types/chat/assistant-message.d.ts +106 -0
  31. package/dist/types/chat/background-tan-message.d.ts +9 -0
  32. package/dist/types/chat/bash-execution.d.ts +47 -0
  33. package/dist/types/chat/cache-invalidation-marker.d.ts +50 -0
  34. package/dist/types/chat/chat-transcript-builder.d.ts +50 -0
  35. package/dist/types/chat/collab-prompt-message.d.ts +10 -0
  36. package/dist/types/chat/compaction-summary-message.d.ts +54 -0
  37. package/dist/types/chat/custom-message.d.ts +10 -0
  38. package/dist/types/chat/display-preferences.d.ts +13 -0
  39. package/dist/types/chat/eval-execution.d.ts +29 -0
  40. package/dist/types/chat/execution-shared.d.ts +51 -0
  41. package/dist/types/chat/extension-types.d.ts +28 -0
  42. package/dist/types/chat/hook-message.d.ts +10 -0
  43. package/dist/types/chat/image-loading.d.ts +46 -0
  44. package/dist/types/chat/late-diagnostics-message.d.ts +21 -0
  45. package/dist/types/chat/messages.d.ts +189 -0
  46. package/dist/types/chat/reaction.d.ts +30 -0
  47. package/dist/types/chat/read-target.d.ts +4 -0
  48. package/dist/types/chat/read-tool-group.d.ts +67 -0
  49. package/dist/types/chat/served-model-marker.d.ts +47 -0
  50. package/dist/types/chat/skill-message.d.ts +33 -0
  51. package/dist/types/chat/skill-title-input.d.ts +14 -0
  52. package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +12 -0
  53. package/dist/types/chat/thinking-display.d.ts +12 -0
  54. package/dist/types/chat/todo-reminder.d.ts +13 -0
  55. package/dist/types/chat/tool-execution.d.ts +138 -0
  56. package/dist/types/chat/transcript-browser.d.ts +93 -0
  57. package/dist/types/chat/transcript-entry.d.ts +45 -0
  58. package/dist/types/chat/transcript-outline.d.ts +86 -0
  59. package/dist/types/chat/transcript-render-helpers.d.ts +92 -0
  60. package/dist/types/chat/ttsr-notification.d.ts +23 -0
  61. package/dist/types/chat/user-message.d.ts +52 -0
  62. package/dist/types/chrome/chat-block.d.ts +64 -0
  63. package/dist/types/chrome/collab-qrcode.d.ts +28 -0
  64. package/dist/types/chrome/context-thresholds.d.ts +10 -0
  65. package/dist/types/chrome/countdown-timer.d.ts +14 -0
  66. package/dist/types/chrome/diff.d.ts +14 -0
  67. package/dist/types/chrome/dynamic-border.d.ts +16 -0
  68. package/dist/types/chrome/error-block.d.ts +17 -0
  69. package/dist/types/chrome/form-theme.d.ts +3 -0
  70. package/dist/types/chrome/format.d.ts +12 -0
  71. package/dist/types/chrome/index.d.ts +20 -0
  72. package/dist/types/chrome/keybinding-hints.d.ts +40 -0
  73. package/dist/types/chrome/live-board.d.ts +23 -0
  74. package/dist/types/chrome/local-date.d.ts +4 -0
  75. package/dist/types/chrome/message-divider.d.ts +22 -0
  76. package/dist/types/chrome/message-frame.d.ts +52 -0
  77. package/dist/types/chrome/message-notice.d.ts +35 -0
  78. package/dist/types/chrome/overlay-box.d.ts +74 -0
  79. package/dist/types/chrome/qrcode.d.ts +48 -0
  80. package/dist/types/chrome/segment-track.d.ts +27 -0
  81. package/dist/types/chrome/select-list-mouse-routing.d.ts +4 -0
  82. package/dist/types/chrome/selector-helpers.d.ts +52 -0
  83. package/dist/types/chrome/shared.d.ts +5 -0
  84. package/dist/types/chrome/streaming-panel.d.ts +19 -0
  85. package/dist/types/chrome/tool-activity.d.ts +17 -0
  86. package/dist/types/chrome/transcript-container.d.ts +129 -0
  87. package/dist/types/chrome/transcript-status.d.ts +12 -0
  88. package/dist/types/chrome/visual-truncate.d.ts +19 -0
  89. package/dist/types/components/disclosure.d.ts +42 -0
  90. package/dist/types/components/form.d.ts +130 -0
  91. package/dist/types/components/key-value-list.d.ts +24 -0
  92. package/dist/types/components/layout/geometry.d.ts +73 -0
  93. package/dist/types/components/layout/row.d.ts +55 -0
  94. package/dist/types/components/layout/split-pane.d.ts +80 -0
  95. package/dist/types/components/layout/stack.d.ts +50 -0
  96. package/dist/types/components/menu-selection.d.ts +71 -0
  97. package/dist/types/components/metric.d.ts +44 -0
  98. package/dist/types/components/progress-bar.d.ts +36 -0
  99. package/dist/types/components/scroll-view.d.ts +53 -19
  100. package/dist/types/components/scroll-viewport.d.ts +46 -0
  101. package/dist/types/components/section.d.ts +19 -0
  102. package/dist/types/components/select-list.d.ts +46 -2
  103. package/dist/types/components/settings-list.d.ts +20 -0
  104. package/dist/types/components/table.d.ts +32 -0
  105. package/dist/types/components/tree-view.d.ts +130 -0
  106. package/dist/types/components/wizard-step.d.ts +60 -0
  107. package/dist/types/hotkeys-markdown.d.ts +7 -0
  108. package/dist/types/index.d.ts +17 -0
  109. package/dist/types/keybinding-matchers.d.ts +35 -0
  110. package/dist/types/keys.d.ts +2 -2
  111. package/dist/types/lang-from-path.d.ts +9 -0
  112. package/dist/types/overlays/advisor-config.d.ts +114 -0
  113. package/dist/types/overlays/agent-activity.d.ts +41 -0
  114. package/dist/types/overlays/agent-hub-projection.d.ts +33 -0
  115. package/dist/types/overlays/agent-hub-renderer.d.ts +53 -0
  116. package/dist/types/overlays/agent-hub-types.d.ts +99 -0
  117. package/dist/types/overlays/agent-hub.d.ts +126 -0
  118. package/dist/types/overlays/agent-transcript-viewer.d.ts +71 -0
  119. package/dist/types/overlays/agents-hub.d.ts +72 -0
  120. package/dist/types/overlays/ask-dialog.d.ts +88 -0
  121. package/dist/types/overlays/bordered-loader.d.ts +11 -0
  122. package/dist/types/overlays/btw-history-panel.d.ts +29 -0
  123. package/dist/types/overlays/btw-history.d.ts +17 -0
  124. package/dist/types/overlays/btw-panel.d.ts +26 -0
  125. package/dist/types/overlays/cleanse-panel.d.ts +36 -0
  126. package/dist/types/overlays/codex-reset-fireworks.d.ts +52 -0
  127. package/dist/types/overlays/composer-shape-preview.d.ts +47 -0
  128. package/dist/types/overlays/composer-shape-registry.d.ts +50 -0
  129. package/dist/types/overlays/copy-selector.d.ts +48 -0
  130. package/dist/types/overlays/copy-targets.d.ts +63 -0
  131. package/dist/types/overlays/error-banner.d.ts +13 -0
  132. package/dist/types/overlays/extensions/display-text.d.ts +10 -0
  133. package/dist/types/overlays/extensions/extension-dashboard.d.ts +63 -0
  134. package/dist/types/overlays/extensions/extension-list.d.ts +54 -0
  135. package/dist/types/overlays/extensions/inspector-model.d.ts +109 -0
  136. package/dist/types/overlays/extensions/inspector-panel.d.ts +17 -0
  137. package/dist/types/overlays/extensions/live-tool-session.d.ts +28 -0
  138. package/dist/types/overlays/extensions/mcp-runtime.d.ts +107 -0
  139. package/dist/types/overlays/extensions/state-manager.d.ts +42 -0
  140. package/dist/types/overlays/extensions/types.d.ts +165 -0
  141. package/dist/types/overlays/history-search.d.ts +16 -0
  142. package/dist/types/overlays/hook-editor.d.ts +44 -0
  143. package/dist/types/overlays/hook-input.d.ts +20 -0
  144. package/dist/types/overlays/hook-selector.d.ts +71 -0
  145. package/dist/types/overlays/hub-frame.d.ts +77 -0
  146. package/dist/types/overlays/login-dialog.d.ts +43 -0
  147. package/dist/types/overlays/logout-account-selector.d.ts +15 -0
  148. package/dist/types/overlays/mcp-add-wizard.d.ts +87 -0
  149. package/dist/types/overlays/model-browser.d.ts +213 -0
  150. package/dist/types/overlays/model-hub.d.ts +72 -0
  151. package/dist/types/overlays/model-picker.d.ts +74 -0
  152. package/dist/types/overlays/model-selector.d.ts +52 -0
  153. package/dist/types/overlays/move-overlay.d.ts +39 -0
  154. package/dist/types/overlays/oauth-selector.d.ts +41 -0
  155. package/dist/types/overlays/omfg-panel.d.ts +20 -0
  156. package/dist/types/overlays/pause-screen.d.ts +43 -0
  157. package/dist/types/overlays/plan-review-overlay.d.ts +89 -0
  158. package/dist/types/overlays/plan-save-overlay.d.ts +19 -0
  159. package/dist/types/overlays/plan-toc.d.ts +41 -0
  160. package/dist/types/overlays/plugin-selector.d.ts +28 -0
  161. package/dist/types/overlays/plugin-settings.d.ts +180 -0
  162. package/dist/types/overlays/queue-mode-selector.d.ts +11 -0
  163. package/dist/types/overlays/reset-usage-selector.d.ts +23 -0
  164. package/dist/types/overlays/rewind-selector.d.ts +61 -0
  165. package/dist/types/overlays/running-subagent-badge.d.ts +6 -0
  166. package/dist/types/overlays/session-account-selector.d.ts +24 -0
  167. package/dist/types/overlays/session-info-overlay.d.ts +17 -0
  168. package/dist/types/overlays/session-observer-registry.d.ts +77 -0
  169. package/dist/types/overlays/session-selector.d.ts +131 -0
  170. package/dist/types/overlays/settings-defs.d.ts +119 -0
  171. package/dist/types/overlays/settings-selector.d.ts +73 -0
  172. package/dist/types/overlays/show-images-selector.d.ts +11 -0
  173. package/dist/types/overlays/snapcompact-shape-preview.d.ts +31 -0
  174. package/dist/types/overlays/theme-selector.d.ts +12 -0
  175. package/dist/types/overlays/thinking-selector.d.ts +12 -0
  176. package/dist/types/overlays/tiny-title-download-progress.d.ts +21 -0
  177. package/dist/types/overlays/tree-selector.d.ts +92 -0
  178. package/dist/types/overlays/usage-dashboard.d.ts +94 -0
  179. package/dist/types/overlays/usage-display.d.ts +5 -0
  180. package/dist/types/overlays/usage-row.d.ts +17 -0
  181. package/dist/types/prompt/attachment-chips.d.ts +19 -0
  182. package/dist/types/prompt/composer-attachments.d.ts +71 -0
  183. package/dist/types/prompt/composer-cache.d.ts +37 -0
  184. package/dist/types/prompt/composer.d.ts +159 -0
  185. package/dist/types/prompt/custom-editor.d.ts +261 -0
  186. package/dist/types/prompt/editor-top-gap.d.ts +18 -0
  187. package/dist/types/prompt/emoji-autocomplete.d.ts +16 -0
  188. package/dist/types/prompt/github-ref-autocomplete.d.ts +35 -0
  189. package/dist/types/prompt/gradient-highlight.d.ts +32 -0
  190. package/dist/types/prompt/image-format.d.ts +4 -0
  191. package/dist/types/prompt/image-references.d.ts +26 -0
  192. package/dist/types/prompt/interactive-context-helpers.d.ts +43 -0
  193. package/dist/types/prompt/internal-url-autocomplete.d.ts +56 -0
  194. package/dist/types/prompt/macos-spelling.d.ts +44 -0
  195. package/dist/types/prompt/magic-keyword-boundary.d.ts +9 -0
  196. package/dist/types/prompt/magic-keywords.d.ts +26 -0
  197. package/dist/types/prompt/markdown-prose.d.ts +15 -0
  198. package/dist/types/prompt/model-mention-autocomplete.d.ts +25 -0
  199. package/dist/types/prompt/model-mention-syntax.d.ts +24 -0
  200. package/dist/types/prompt/orchestrate.d.ts +13 -0
  201. package/dist/types/prompt/prompt-action-autocomplete.d.ts +56 -0
  202. package/dist/types/prompt/queue-input.d.ts +8 -0
  203. package/dist/types/prompt/skill-tokens.d.ts +13 -0
  204. package/dist/types/prompt/tools-markdown.d.ts +12 -0
  205. package/dist/types/prompt/ultrathink.d.ts +13 -0
  206. package/dist/types/prompt/usage-amounts.d.ts +26 -0
  207. package/dist/types/prompt/video.d.ts +18 -0
  208. package/dist/types/prompt/welcome.d.ts +83 -0
  209. package/dist/types/prompt/workflow.d.ts +13 -0
  210. package/dist/types/render/code-cell.d.ts +51 -0
  211. package/dist/types/render/file-list.d.ts +22 -0
  212. package/dist/types/render/hyperlink.d.ts +80 -0
  213. package/dist/types/render/index.d.ts +16 -0
  214. package/dist/types/render/output-block.d.ts +54 -0
  215. package/dist/types/render/output-pane.d.ts +80 -0
  216. package/dist/types/render/render-utils.d.ts +312 -0
  217. package/dist/types/render/sixel.d.ts +21 -0
  218. package/dist/types/render/status-line.d.ts +23 -0
  219. package/dist/types/render/tool-card.d.ts +71 -0
  220. package/dist/types/render/tree-list.d.ts +29 -0
  221. package/dist/types/render/types.d.ts +17 -0
  222. package/dist/types/render/utils.d.ts +43 -0
  223. package/dist/types/render/width-aware-text.d.ts +23 -0
  224. package/dist/types/setup/lazy.d.ts +3 -0
  225. package/dist/types/setup/scenes/composer.d.ts +3 -0
  226. package/dist/types/setup/scenes/glyph.d.ts +3 -0
  227. package/dist/types/setup/scenes/model.d.ts +3 -0
  228. package/dist/types/setup/scenes/outro.d.ts +2 -0
  229. package/dist/types/setup/scenes/providers.d.ts +3 -0
  230. package/dist/types/setup/scenes/sign-in.d.ts +21 -0
  231. package/dist/types/setup/scenes/splash.d.ts +11 -0
  232. package/dist/types/setup/scenes/theme.d.ts +3 -0
  233. package/dist/types/setup/scenes/types.d.ts +110 -0
  234. package/dist/types/setup/scenes/web-search.d.ts +22 -0
  235. package/dist/types/setup/setup-version.d.ts +11 -0
  236. package/dist/types/setup/startup-splash.d.ts +9 -0
  237. package/dist/types/setup/wizard-overlay.d.ts +16 -0
  238. package/dist/types/setup/wizard.d.ts +25 -0
  239. package/dist/types/status-line/component.d.ts +182 -0
  240. package/dist/types/status-line/context-usage.d.ts +156 -0
  241. package/dist/types/status-line/footer.d.ts +30 -0
  242. package/dist/types/status-line/git-utils.d.ts +22 -0
  243. package/dist/types/status-line/host.d.ts +129 -0
  244. package/dist/types/status-line/index.d.ts +6 -0
  245. package/dist/types/status-line/loop.d.ts +18 -0
  246. package/dist/types/status-line/metrics.d.ts +21 -0
  247. package/dist/types/status-line/presets.d.ts +3 -0
  248. package/dist/types/status-line/schema.d.ts +15 -0
  249. package/dist/types/status-line/segments.d.ts +5 -0
  250. package/dist/types/status-line/separators.d.ts +3 -0
  251. package/dist/types/status-line/types.d.ts +236 -0
  252. package/dist/types/stdin-buffer.d.ts +4 -0
  253. package/dist/types/theme/color.d.ts +24 -0
  254. package/dist/types/theme/defaults/index.d.ts +9489 -0
  255. package/dist/types/theme/index.d.ts +4 -0
  256. package/dist/types/theme/loader.d.ts +23 -0
  257. package/dist/types/theme/mermaid-cache.d.ts +26 -0
  258. package/dist/types/theme/schema-validation.d.ts +3 -0
  259. package/dist/types/theme/schema.d.ts +25 -0
  260. package/dist/types/theme/session-color.d.ts +43 -0
  261. package/dist/types/theme/shimmer.d.ts +44 -0
  262. package/dist/types/theme/symbols.d.ts +34 -0
  263. package/dist/types/theme/theme-class.d.ts +299 -0
  264. package/dist/types/theme/theme.d.ts +111 -0
  265. package/dist/types/theme/tui-adapters.d.ts +30 -0
  266. package/dist/types/thinking.d.ts +165 -0
  267. package/dist/types/tools/agent-tree.d.ts +36 -0
  268. package/dist/types/tools/ask.d.ts +67 -0
  269. package/dist/types/tools/ast-edit.d.ts +53 -0
  270. package/dist/types/tools/ast-grep.d.ts +52 -0
  271. package/dist/types/tools/autoresearch.d.ts +156 -0
  272. package/dist/types/tools/bash-interactive.d.ts +28 -0
  273. package/dist/types/tools/bash.d.ts +103 -0
  274. package/dist/types/tools/conflict-detect.d.ts +43 -0
  275. package/dist/types/tools/debug.d.ts +60 -0
  276. package/dist/types/tools/default-renderer.d.ts +24 -0
  277. package/dist/types/tools/edit.d.ts +158 -0
  278. package/dist/types/tools/eval-format/code-mode-declarations.d.ts +5 -0
  279. package/dist/types/tools/eval-format/index.d.ts +5 -0
  280. package/dist/types/tools/eval-format/javascript.d.ts +2 -0
  281. package/dist/types/tools/eval-format/python.d.ts +2 -0
  282. package/dist/types/tools/eval.d.ts +94 -0
  283. package/dist/types/tools/fetch.d.ts +42 -0
  284. package/dist/types/tools/gh-format.d.ts +8 -0
  285. package/dist/types/tools/github.d.ts +144 -0
  286. package/dist/types/tools/glob.d.ts +44 -0
  287. package/dist/types/tools/goal.d.ts +42 -0
  288. package/dist/types/tools/grep.d.ts +64 -0
  289. package/dist/types/tools/grouped-file-output.d.ts +71 -0
  290. package/dist/types/tools/hashline-format.d.ts +28 -0
  291. package/dist/types/tools/hub.d.ts +340 -0
  292. package/dist/types/tools/index.d.ts +4 -0
  293. package/dist/types/tools/json-tree.d.ts +59 -0
  294. package/dist/types/tools/line-ranges.d.ts +29 -0
  295. package/dist/types/tools/list-limit.d.ts +15 -0
  296. package/dist/types/tools/lsp.d.ts +72 -0
  297. package/dist/types/tools/match-line-format.d.ts +11 -0
  298. package/dist/types/tools/mcp.d.ts +81 -0
  299. package/dist/types/tools/memory.d.ts +67 -0
  300. package/dist/types/tools/output-meta.d.ts +151 -0
  301. package/dist/types/tools/read.d.ts +139 -0
  302. package/dist/types/tools/renderer.d.ts +98 -0
  303. package/dist/types/tools/report-tool-issue.d.ts +8 -0
  304. package/dist/types/tools/resolve.d.ts +47 -0
  305. package/dist/types/tools/streaming-output.d.ts +282 -0
  306. package/dist/types/tools/subprocess.d.ts +24 -0
  307. package/dist/types/tools/task-repair-args.d.ts +53 -0
  308. package/dist/types/tools/task-yield-assembly.d.ts +20 -0
  309. package/dist/types/tools/task.d.ts +425 -0
  310. package/dist/types/tools/terminal-output.d.ts +8 -0
  311. package/dist/types/tools/think.d.ts +13 -0
  312. package/dist/types/tools/todo.d.ts +118 -0
  313. package/dist/types/tools/tool-errors.d.ts +7 -0
  314. package/dist/types/tools/vibe.d.ts +120 -0
  315. package/dist/types/tools/web-search.d.ts +195 -0
  316. package/dist/types/tools/write.d.ts +76 -0
  317. package/dist/types/tools/xd-url.d.ts +13 -0
  318. package/dist/types/tools/xdev.d.ts +52 -0
  319. package/dist/types/tui.d.ts +18 -0
  320. package/dist/types/utils.d.ts +2 -0
  321. package/package.json +37 -3
  322. package/src/app-keybindings.ts +760 -0
  323. package/src/apps/autoresearch-dashboard.ts +500 -0
  324. package/src/apps/autoresearch-data.ts +64 -0
  325. package/src/apps/cleanse-board.ts +341 -0
  326. package/src/apps/cleanse-picker.ts +57 -0
  327. package/src/apps/debug/log-formatting.ts +53 -0
  328. package/src/apps/debug/log-viewer.ts +868 -0
  329. package/src/apps/debug/protocol-probe.ts +267 -0
  330. package/src/apps/debug/raw-sse-buffer.ts +449 -0
  331. package/src/apps/debug/raw-sse.ts +282 -0
  332. package/src/apps/debug/terminal-info.ts +115 -0
  333. package/src/apps/debug/viewer-frame.ts +174 -0
  334. package/src/apps/git/avatar.ts +52 -0
  335. package/src/apps/git/colors.ts +109 -0
  336. package/src/apps/git/diff-pane.ts +1512 -0
  337. package/src/apps/git/git-tui.ts +889 -0
  338. package/src/apps/git/sidebar.ts +1204 -0
  339. package/src/apps/git/state.ts +113 -0
  340. package/src/apps/if-bench-board.ts +324 -0
  341. package/src/apps/live-visualizer.ts +238 -0
  342. package/src/apps/ps-data.ts +116 -0
  343. package/src/apps/ps-top.ts +422 -0
  344. package/src/apps/session-picker.ts +95 -0
  345. package/src/apps/setup-model-picker.ts +21 -0
  346. package/src/apps/standalone-picker.ts +111 -0
  347. package/src/autocomplete.ts +9 -13
  348. package/src/chat/advisor-message.ts +175 -0
  349. package/src/chat/assistant-message.ts +1214 -0
  350. package/src/chat/background-tan-message.ts +35 -0
  351. package/src/chat/bash-execution.ts +394 -0
  352. package/src/chat/cache-invalidation-marker.ts +94 -0
  353. package/src/chat/chat-transcript-builder.ts +593 -0
  354. package/src/chat/collab-prompt-message.ts +34 -0
  355. package/src/chat/compaction-summary-message.ts +261 -0
  356. package/src/chat/custom-message.ts +23 -0
  357. package/src/chat/display-preferences.ts +24 -0
  358. package/src/chat/eval-execution.ts +172 -0
  359. package/src/chat/execution-shared.ts +113 -0
  360. package/src/chat/extension-types.ts +46 -0
  361. package/src/chat/hook-message.ts +20 -0
  362. package/src/chat/image-loading.ts +122 -0
  363. package/src/chat/late-diagnostics-message.ts +92 -0
  364. package/src/chat/messages.ts +305 -0
  365. package/src/chat/reaction.ts +65 -0
  366. package/src/chat/read-target.ts +7 -0
  367. package/src/chat/read-tool-group.ts +873 -0
  368. package/src/chat/served-model-marker.ts +91 -0
  369. package/src/chat/skill-message.ts +220 -0
  370. package/src/chat/skill-title-input.ts +41 -0
  371. package/src/chat/stripped-tool-calls-placeholder.ts +35 -0
  372. package/src/chat/thinking-display.ts +312 -0
  373. package/src/chat/todo-reminder.ts +33 -0
  374. package/src/chat/tool-execution.ts +1395 -0
  375. package/src/chat/transcript-browser.ts +268 -0
  376. package/src/chat/transcript-entry.ts +96 -0
  377. package/src/chat/transcript-outline.ts +260 -0
  378. package/src/chat/transcript-render-helpers.ts +315 -0
  379. package/src/chat/ttsr-notification.ts +123 -0
  380. package/src/chat/user-message.ts +287 -0
  381. package/src/chrome/chat-block.ts +110 -0
  382. package/src/chrome/collab-qrcode.ts +74 -0
  383. package/src/chrome/context-thresholds.ts +85 -0
  384. package/src/chrome/countdown-timer.ts +74 -0
  385. package/src/chrome/diff.ts +266 -0
  386. package/src/chrome/dynamic-border.ts +36 -0
  387. package/src/chrome/error-block.ts +63 -0
  388. package/src/chrome/form-theme.ts +10 -0
  389. package/src/chrome/format.ts +66 -0
  390. package/src/chrome/index.ts +21 -0
  391. package/src/chrome/keybinding-hints.ts +55 -0
  392. package/src/chrome/live-board.ts +123 -0
  393. package/src/chrome/local-date.ts +20 -0
  394. package/src/chrome/message-divider.ts +51 -0
  395. package/src/chrome/message-frame.ts +164 -0
  396. package/src/chrome/message-notice.ts +103 -0
  397. package/src/chrome/overlay-box.ts +301 -0
  398. package/src/chrome/qrcode.ts +539 -0
  399. package/src/chrome/segment-track.ts +86 -0
  400. package/src/chrome/select-list-mouse-routing.ts +11 -0
  401. package/src/chrome/selector-helpers.ts +119 -0
  402. package/src/chrome/shared.ts +27 -0
  403. package/src/chrome/streaming-panel.ts +72 -0
  404. package/src/chrome/tool-activity.ts +44 -0
  405. package/src/chrome/transcript-container.ts +902 -0
  406. package/src/chrome/transcript-status.ts +27 -0
  407. package/src/chrome/visual-truncate.ts +70 -0
  408. package/src/components/disclosure.ts +235 -0
  409. package/src/components/editor.ts +10 -12
  410. package/src/components/form.ts +566 -0
  411. package/src/components/input.ts +6 -27
  412. package/src/components/key-value-list.ts +88 -0
  413. package/src/components/layout/geometry.ts +202 -0
  414. package/src/components/layout/row.ts +283 -0
  415. package/src/components/layout/split-pane.ts +349 -0
  416. package/src/components/layout/stack.ts +261 -0
  417. package/src/components/markdown.ts +16 -12
  418. package/src/components/menu-selection.ts +248 -0
  419. package/src/components/metric.ts +132 -0
  420. package/src/components/progress-bar.ts +140 -0
  421. package/src/components/scroll-view.ts +372 -70
  422. package/src/components/scroll-viewport.ts +154 -0
  423. package/src/components/section.ts +77 -0
  424. package/src/components/select-list.ts +238 -159
  425. package/src/components/settings-list.ts +103 -80
  426. package/src/components/table.ts +127 -0
  427. package/src/components/tree-view.ts +481 -0
  428. package/src/components/wizard-step.ts +229 -0
  429. package/src/deccara.ts +1 -3
  430. package/src/hotkeys-markdown.ts +100 -0
  431. package/src/index.ts +18 -0
  432. package/src/keybinding-matchers.ts +99 -0
  433. package/src/keys.ts +2 -2
  434. package/src/lang-from-path.ts +252 -0
  435. package/src/overlays/advisor-config.ts +761 -0
  436. package/src/overlays/agent-activity.ts +113 -0
  437. package/src/overlays/agent-hub-projection.ts +250 -0
  438. package/src/overlays/agent-hub-renderer.ts +281 -0
  439. package/src/overlays/agent-hub-types.ts +74 -0
  440. package/src/overlays/agent-hub.ts +1562 -0
  441. package/src/overlays/agent-transcript-viewer.ts +669 -0
  442. package/src/overlays/agents-hub.ts +1203 -0
  443. package/src/overlays/ask-dialog.ts +1202 -0
  444. package/src/overlays/bordered-loader.ts +41 -0
  445. package/src/overlays/btw-history-panel.ts +591 -0
  446. package/src/overlays/btw-history.ts +33 -0
  447. package/src/overlays/btw-panel.ts +167 -0
  448. package/src/overlays/cleanse-panel.ts +167 -0
  449. package/src/overlays/codex-reset-fireworks.ts +375 -0
  450. package/src/overlays/composer-shape-preview.ts +137 -0
  451. package/src/overlays/composer-shape-registry.ts +82 -0
  452. package/src/overlays/copy-selector.ts +618 -0
  453. package/src/overlays/copy-targets.ts +210 -0
  454. package/src/overlays/error-banner.ts +39 -0
  455. package/src/overlays/extensions/display-text.ts +39 -0
  456. package/src/overlays/extensions/extension-dashboard.ts +645 -0
  457. package/src/overlays/extensions/extension-list.ts +667 -0
  458. package/src/overlays/extensions/inspector-model.ts +484 -0
  459. package/src/overlays/extensions/inspector-panel.ts +669 -0
  460. package/src/overlays/extensions/live-tool-session.ts +67 -0
  461. package/src/overlays/extensions/mcp-runtime.ts +289 -0
  462. package/src/overlays/extensions/state-manager.ts +337 -0
  463. package/src/overlays/extensions/types.ts +208 -0
  464. package/src/overlays/history-search.ts +268 -0
  465. package/src/overlays/hook-editor.ts +272 -0
  466. package/src/overlays/hook-input.ts +84 -0
  467. package/src/overlays/hook-selector.ts +635 -0
  468. package/src/overlays/hub-frame.ts +262 -0
  469. package/src/overlays/login-dialog.ts +231 -0
  470. package/src/overlays/logout-account-selector.ts +115 -0
  471. package/src/overlays/mcp-add-wizard.ts +1436 -0
  472. package/src/overlays/model-browser.ts +1176 -0
  473. package/src/overlays/model-hub.ts +2279 -0
  474. package/src/overlays/model-picker.ts +285 -0
  475. package/src/overlays/model-selector.ts +107 -0
  476. package/src/overlays/move-overlay.ts +197 -0
  477. package/src/overlays/oauth-selector.ts +446 -0
  478. package/src/overlays/omfg-panel.ts +136 -0
  479. package/src/overlays/pause-screen.ts +198 -0
  480. package/src/overlays/plan-review-overlay.ts +1227 -0
  481. package/src/overlays/plan-save-overlay.ts +74 -0
  482. package/src/overlays/plan-toc.ts +139 -0
  483. package/src/overlays/plugin-selector.ts +97 -0
  484. package/src/overlays/plugin-settings.ts +839 -0
  485. package/src/overlays/queue-mode-selector.ts +55 -0
  486. package/src/overlays/reset-usage-selector.ts +170 -0
  487. package/src/overlays/rewind-selector.ts +499 -0
  488. package/src/overlays/running-subagent-badge.ts +19 -0
  489. package/src/overlays/session-account-selector.ts +68 -0
  490. package/src/overlays/session-info-overlay.ts +96 -0
  491. package/src/overlays/session-observer-registry.ts +289 -0
  492. package/src/overlays/session-selector.ts +1137 -0
  493. package/src/overlays/settings-defs.ts +315 -0
  494. package/src/overlays/settings-selector.ts +1415 -0
  495. package/src/overlays/show-images-selector.ts +44 -0
  496. package/src/overlays/snapcompact-shape-preview-doc.md +14 -0
  497. package/src/overlays/snapcompact-shape-preview.ts +192 -0
  498. package/src/overlays/theme-selector.ts +62 -0
  499. package/src/overlays/thinking-selector.ts +51 -0
  500. package/src/overlays/tiny-title-download-progress.ts +102 -0
  501. package/src/overlays/tree-selector.ts +1121 -0
  502. package/src/overlays/usage-dashboard.ts +629 -0
  503. package/src/overlays/usage-display.ts +45 -0
  504. package/src/overlays/usage-row.ts +118 -0
  505. package/src/prompt/attachment-chips.ts +201 -0
  506. package/src/prompt/composer-attachments.ts +280 -0
  507. package/src/prompt/composer-cache.ts +304 -0
  508. package/src/prompt/composer.ts +934 -0
  509. package/src/prompt/custom-editor.ts +1392 -0
  510. package/src/prompt/data/emojis.json +1 -0
  511. package/src/prompt/editor-top-gap.ts +28 -0
  512. package/src/prompt/emoji-autocomplete.ts +285 -0
  513. package/src/prompt/github-ref-autocomplete.ts +75 -0
  514. package/src/prompt/gradient-highlight.ts +101 -0
  515. package/src/prompt/image-format.ts +28 -0
  516. package/src/prompt/image-references.ts +102 -0
  517. package/src/prompt/interactive-context-helpers.ts +126 -0
  518. package/src/prompt/internal-url-autocomplete.ts +155 -0
  519. package/src/prompt/macos-spelling.ts +456 -0
  520. package/src/prompt/magic-keyword-boundary.ts +23 -0
  521. package/src/prompt/magic-keywords.ts +42 -0
  522. package/src/prompt/markdown-prose.ts +245 -0
  523. package/src/prompt/model-mention-autocomplete.ts +94 -0
  524. package/src/prompt/model-mention-syntax.ts +41 -0
  525. package/src/prompt/orchestrate.ts +26 -0
  526. package/src/prompt/prompt-action-autocomplete.ts +337 -0
  527. package/src/prompt/queue-input.ts +132 -0
  528. package/src/prompt/skill-tokens.ts +36 -0
  529. package/src/prompt/tips.txt +27 -0
  530. package/src/prompt/tools-markdown.ts +29 -0
  531. package/src/prompt/ultrathink.ts +26 -0
  532. package/src/prompt/usage-amounts.ts +99 -0
  533. package/src/prompt/video.ts +47 -0
  534. package/src/prompt/welcome.ts +605 -0
  535. package/src/prompt/workflow.ts +26 -0
  536. package/src/render/code-cell.ts +244 -0
  537. package/src/render/file-list.ts +58 -0
  538. package/src/render/hyperlink.ts +192 -0
  539. package/src/render/index.ts +17 -0
  540. package/src/render/output-block.ts +267 -0
  541. package/src/render/output-pane.ts +299 -0
  542. package/src/render/render-utils.ts +1182 -0
  543. package/src/render/sixel.ts +69 -0
  544. package/src/render/status-line.ts +55 -0
  545. package/src/render/tool-card.ts +272 -0
  546. package/src/render/tree-list.ts +188 -0
  547. package/src/render/types.ts +19 -0
  548. package/src/render/utils.ts +109 -0
  549. package/src/render/width-aware-text.ts +60 -0
  550. package/src/setup/lazy.ts +17 -0
  551. package/src/setup/scenes/composer.ts +127 -0
  552. package/src/setup/scenes/glyph.ts +120 -0
  553. package/src/setup/scenes/model.ts +138 -0
  554. package/src/setup/scenes/outro.ts +24 -0
  555. package/src/setup/scenes/providers.ts +107 -0
  556. package/src/setup/scenes/sign-in.ts +373 -0
  557. package/src/setup/scenes/splash.ts +192 -0
  558. package/src/setup/scenes/theme.ts +343 -0
  559. package/src/setup/scenes/types.ts +111 -0
  560. package/src/setup/scenes/web-search.ts +165 -0
  561. package/src/setup/setup-version.ts +11 -0
  562. package/src/setup/startup-splash.ts +107 -0
  563. package/src/setup/wizard-overlay.ts +319 -0
  564. package/src/setup/wizard.ts +101 -0
  565. package/src/status-line/component.ts +3003 -0
  566. package/src/status-line/context-usage.ts +688 -0
  567. package/src/status-line/footer.ts +323 -0
  568. package/src/status-line/git-utils.ts +42 -0
  569. package/src/status-line/host.ts +97 -0
  570. package/src/status-line/index.ts +6 -0
  571. package/src/status-line/loop.ts +28 -0
  572. package/src/status-line/metrics.ts +94 -0
  573. package/src/status-line/presets.ts +107 -0
  574. package/src/status-line/schema.ts +57 -0
  575. package/src/status-line/segments.ts +934 -0
  576. package/src/status-line/separators.ts +55 -0
  577. package/src/status-line/types.ts +227 -0
  578. package/src/stdin-buffer.ts +4 -2
  579. package/src/theme/color.ts +95 -0
  580. package/src/theme/dark.json +95 -0
  581. package/src/theme/defaults/alabaster.json +93 -0
  582. package/src/theme/defaults/amethyst.json +96 -0
  583. package/src/theme/defaults/anthracite.json +93 -0
  584. package/src/theme/defaults/basalt.json +91 -0
  585. package/src/theme/defaults/birch.json +95 -0
  586. package/src/theme/defaults/dark-abyss.json +91 -0
  587. package/src/theme/defaults/dark-arctic.json +104 -0
  588. package/src/theme/defaults/dark-aurora.json +95 -0
  589. package/src/theme/defaults/dark-catppuccin.json +107 -0
  590. package/src/theme/defaults/dark-cavern.json +91 -0
  591. package/src/theme/defaults/dark-celestial.json +106 -0
  592. package/src/theme/defaults/dark-copper.json +95 -0
  593. package/src/theme/defaults/dark-cosmos.json +90 -0
  594. package/src/theme/defaults/dark-cyberpunk.json +102 -0
  595. package/src/theme/defaults/dark-dracula.json +98 -0
  596. package/src/theme/defaults/dark-eclipse.json +91 -0
  597. package/src/theme/defaults/dark-ember.json +95 -0
  598. package/src/theme/defaults/dark-equinox.json +90 -0
  599. package/src/theme/defaults/dark-forest.json +96 -0
  600. package/src/theme/defaults/dark-github.json +105 -0
  601. package/src/theme/defaults/dark-gruvbox.json +112 -0
  602. package/src/theme/defaults/dark-lavender.json +95 -0
  603. package/src/theme/defaults/dark-lunar.json +89 -0
  604. package/src/theme/defaults/dark-midnight.json +95 -0
  605. package/src/theme/defaults/dark-monochrome.json +94 -0
  606. package/src/theme/defaults/dark-monokai.json +98 -0
  607. package/src/theme/defaults/dark-nebula.json +90 -0
  608. package/src/theme/defaults/dark-nord.json +98 -0
  609. package/src/theme/defaults/dark-ocean.json +101 -0
  610. package/src/theme/defaults/dark-one.json +100 -0
  611. package/src/theme/defaults/dark-poimandres.json +115 -0
  612. package/src/theme/defaults/dark-rainforest.json +91 -0
  613. package/src/theme/defaults/dark-reef.json +91 -0
  614. package/src/theme/defaults/dark-retro.json +92 -0
  615. package/src/theme/defaults/dark-rose-pine.json +96 -0
  616. package/src/theme/defaults/dark-sakura.json +95 -0
  617. package/src/theme/defaults/dark-slate.json +95 -0
  618. package/src/theme/defaults/dark-solarized.json +97 -0
  619. package/src/theme/defaults/dark-solstice.json +90 -0
  620. package/src/theme/defaults/dark-starfall.json +91 -0
  621. package/src/theme/defaults/dark-sunset.json +99 -0
  622. package/src/theme/defaults/dark-swamp.json +90 -0
  623. package/src/theme/defaults/dark-synthwave.json +103 -0
  624. package/src/theme/defaults/dark-taiga.json +91 -0
  625. package/src/theme/defaults/dark-terminal.json +95 -0
  626. package/src/theme/defaults/dark-tokyo-night.json +101 -0
  627. package/src/theme/defaults/dark-tundra.json +91 -0
  628. package/src/theme/defaults/dark-twilight.json +91 -0
  629. package/src/theme/defaults/dark-volcanic.json +91 -0
  630. package/src/theme/defaults/graphite.json +92 -0
  631. package/src/theme/defaults/index.ts +201 -0
  632. package/src/theme/defaults/light-arctic.json +107 -0
  633. package/src/theme/defaults/light-aurora-day.json +91 -0
  634. package/src/theme/defaults/light-canyon.json +91 -0
  635. package/src/theme/defaults/light-catppuccin.json +106 -0
  636. package/src/theme/defaults/light-cirrus.json +90 -0
  637. package/src/theme/defaults/light-coral.json +95 -0
  638. package/src/theme/defaults/light-cyberpunk.json +96 -0
  639. package/src/theme/defaults/light-dawn.json +90 -0
  640. package/src/theme/defaults/light-dunes.json +91 -0
  641. package/src/theme/defaults/light-eucalyptus.json +95 -0
  642. package/src/theme/defaults/light-forest.json +100 -0
  643. package/src/theme/defaults/light-frost.json +95 -0
  644. package/src/theme/defaults/light-github.json +115 -0
  645. package/src/theme/defaults/light-glacier.json +91 -0
  646. package/src/theme/defaults/light-gruvbox.json +108 -0
  647. package/src/theme/defaults/light-haze.json +90 -0
  648. package/src/theme/defaults/light-honeycomb.json +95 -0
  649. package/src/theme/defaults/light-lagoon.json +91 -0
  650. package/src/theme/defaults/light-lavender.json +95 -0
  651. package/src/theme/defaults/light-meadow.json +91 -0
  652. package/src/theme/defaults/light-mint.json +95 -0
  653. package/src/theme/defaults/light-monochrome.json +101 -0
  654. package/src/theme/defaults/light-ocean.json +99 -0
  655. package/src/theme/defaults/light-one.json +99 -0
  656. package/src/theme/defaults/light-opal.json +91 -0
  657. package/src/theme/defaults/light-orchard.json +91 -0
  658. package/src/theme/defaults/light-paper.json +95 -0
  659. package/src/theme/defaults/light-poimandres.json +115 -0
  660. package/src/theme/defaults/light-prism.json +90 -0
  661. package/src/theme/defaults/light-retro.json +98 -0
  662. package/src/theme/defaults/light-sand.json +95 -0
  663. package/src/theme/defaults/light-savanna.json +91 -0
  664. package/src/theme/defaults/light-solarized.json +102 -0
  665. package/src/theme/defaults/light-soleil.json +90 -0
  666. package/src/theme/defaults/light-sunset.json +99 -0
  667. package/src/theme/defaults/light-synthwave.json +98 -0
  668. package/src/theme/defaults/light-tokyo-night.json +111 -0
  669. package/src/theme/defaults/light-wetland.json +91 -0
  670. package/src/theme/defaults/light-zenith.json +89 -0
  671. package/src/theme/defaults/limestone.json +94 -0
  672. package/src/theme/defaults/mahogany.json +97 -0
  673. package/src/theme/defaults/marble.json +93 -0
  674. package/src/theme/defaults/obsidian.json +91 -0
  675. package/src/theme/defaults/onyx.json +91 -0
  676. package/src/theme/defaults/pearl.json +93 -0
  677. package/src/theme/defaults/porcelain.json +91 -0
  678. package/src/theme/defaults/quartz.json +96 -0
  679. package/src/theme/defaults/sandstone.json +95 -0
  680. package/src/theme/defaults/titanium.json +90 -0
  681. package/src/theme/index.ts +5 -0
  682. package/src/theme/light.json +93 -0
  683. package/src/theme/loader.ts +200 -0
  684. package/src/theme/mermaid-cache.ts +92 -0
  685. package/src/theme/schema-validation.ts +119 -0
  686. package/src/theme/schema.ts +168 -0
  687. package/src/theme/session-color.ts +253 -0
  688. package/src/theme/shimmer.ts +307 -0
  689. package/src/theme/symbols.ts +1433 -0
  690. package/src/theme/theme-class.ts +787 -0
  691. package/src/theme/theme-schema.json +463 -0
  692. package/src/theme/theme.ts +810 -0
  693. package/src/theme/tui-adapters.ts +339 -0
  694. package/src/thinking.ts +380 -0
  695. package/src/tools/agent-tree.ts +128 -0
  696. package/src/tools/ask.ts +482 -0
  697. package/src/tools/ast-edit.ts +210 -0
  698. package/src/tools/ast-grep.ts +171 -0
  699. package/src/tools/autoresearch.ts +287 -0
  700. package/src/tools/bash-interactive.ts +295 -0
  701. package/src/tools/bash.ts +468 -0
  702. package/src/tools/conflict-detect.ts +77 -0
  703. package/src/tools/debug.ts +167 -0
  704. package/src/tools/default-renderer.ts +172 -0
  705. package/src/tools/edit.ts +1258 -0
  706. package/src/tools/eval-format/code-mode-declarations.ts +61 -0
  707. package/src/tools/eval-format/index.ts +16 -0
  708. package/src/tools/eval-format/javascript.ts +952 -0
  709. package/src/tools/eval-format/python.ts +544 -0
  710. package/src/tools/eval.ts +835 -0
  711. package/src/tools/fetch.ts +226 -0
  712. package/src/tools/gh-format.ts +18 -0
  713. package/src/tools/github.ts +686 -0
  714. package/src/tools/glob.ts +213 -0
  715. package/src/tools/goal.ts +153 -0
  716. package/src/tools/grep.ts +385 -0
  717. package/src/tools/grouped-file-output.ts +211 -0
  718. package/src/tools/hashline-format.ts +59 -0
  719. package/src/tools/hub.ts +1488 -0
  720. package/src/tools/index.ts +71 -0
  721. package/src/tools/json-tree.ts +388 -0
  722. package/src/tools/line-ranges.ts +98 -0
  723. package/src/tools/list-limit.ts +43 -0
  724. package/src/tools/lsp.ts +702 -0
  725. package/src/tools/match-line-format.ts +20 -0
  726. package/src/tools/mcp.ts +309 -0
  727. package/src/tools/memory.ts +220 -0
  728. package/src/tools/output-meta.ts +367 -0
  729. package/src/tools/read.ts +628 -0
  730. package/src/tools/renderer.ts +107 -0
  731. package/src/tools/report-tool-issue.ts +13 -0
  732. package/src/tools/resolve.ts +139 -0
  733. package/src/tools/streaming-output.ts +1576 -0
  734. package/src/tools/subprocess.ts +39 -0
  735. package/src/tools/task-repair-args.ts +118 -0
  736. package/src/tools/task-yield-assembly.ts +147 -0
  737. package/src/tools/task.ts +2110 -0
  738. package/src/tools/terminal-output.ts +153 -0
  739. package/src/tools/think.ts +28 -0
  740. package/src/tools/todo.ts +580 -0
  741. package/src/tools/tool-errors.ts +15 -0
  742. package/src/tools/vibe.ts +420 -0
  743. package/src/tools/web-search.ts +409 -0
  744. package/src/tools/write.ts +511 -0
  745. package/src/tools/xd-url.ts +28 -0
  746. package/src/tools/xdev.ts +201 -0
  747. package/src/tui.ts +57 -4
  748. package/src/utils.ts +8 -0
@@ -0,0 +1,628 @@
1
+ import type { SummaryResult } from "@oh-my-pi/pi-natives";
2
+ import { formatNumberedLine } from "./hashline-format";
3
+ import { LINE_RANGE_CHUNK_SOURCE, parseLineRanges } from "./line-ranges";
4
+ import * as path from "node:path";
5
+ import type { Component } from "../tui";
6
+ import { Text } from "../components/text";
7
+ import type { RenderResultOptions, ToolRenderer } from "./renderer";
8
+ import { getLanguageFromPath } from "../lang-from-path";
9
+ import type { Theme } from "../theme/theme";
10
+ import { fileHyperlink, renderCodeCell, renderMarkdownCell, renderStatusLine } from "../render";
11
+ import { markFramedBlockComponent } from "../render/output-block";
12
+ import { framedToolCard } from "../render/tool-card";
13
+ import { type ReadUrlToolDetails, renderReadUrlCall, renderReadUrlResult } from "./fetch";
14
+ import { formatFullOutputReference, formatStyledTruncationWarning, stripOutputNotice } from "./output-meta";
15
+ import { formatBytes, sanitizeDisplayLines, shortenPath, wrapBrackets } from "../render/render-utils";
16
+
17
+ import type { OutputMeta } from "./output-meta";
18
+ import type { TruncationResult } from "./streaming-output";
19
+
20
+ /** Read result metadata retains truncation statistics, not a second copy of the body. */
21
+ export type ReadTruncationStats = Omit<TruncationResult, "content">;
22
+
23
+ /** Display metadata for file and URL reads. */
24
+ export interface ReadToolDetails {
25
+ kind?: "file" | "url";
26
+ /** Filesystem hyperlink target resolved by the executing tool. */
27
+ displayTarget?: string;
28
+ truncation?: ReadTruncationStats;
29
+ isDirectory?: boolean;
30
+ resolvedPath?: string;
31
+ suffixResolution?: { from: string; to: string };
32
+ url?: string;
33
+ finalUrl?: string;
34
+ contentType?: string;
35
+ method?: string;
36
+ notes?: string[];
37
+ meta?: OutputMeta;
38
+ /** Full on-disk byte size recorded before applying a file range. */
39
+ fileSize?: number;
40
+ /** Full source line count when the read reached EOF and the count is exact. */
41
+ totalLines?: number;
42
+ /** Raw text + start line for user-visible TUI rendering, set when content is text-like.
43
+ * Mirrors the same lines the model receives but without hashline/line-number prefixes,
44
+ * so the TUI can render the file content with its own gutter without re-parsing the formatted text. */
45
+ displayContent?: {
46
+ text: string;
47
+ startLine: number;
48
+ lineNumbers?: Array<number | null>;
49
+ };
50
+ summary?: { lines: number; elidedSpans: number; elidedLines: number };
51
+ /** Number of unresolved git conflicts surfaced by this read (TUI uses for inline `⚠ N` badge). */
52
+ conflictCount?: number;
53
+ /** Paths recovered from a delimited read argument; used only by the TUI to render one call as multiple read rows. */
54
+ displayReadTargets?: string[];
55
+ /**
56
+ * Resolved filesystem link target for each {@link displayReadTargets} entry, aligned by index; `null` when a
57
+ * delimited part has no linkable fs path. Lets the TUI hyperlink each grouped row the same way a standalone read row is.
58
+ */
59
+ displayReadTargetLinks?: Array<string | null>;
60
+ }
61
+
62
+ // Parsing also recognizes incomplete counts to explain their errors; path splitting
63
+ // only peels complete selectors (not a trailing `+` or `L`).
64
+ const RANGE_SELECTOR_CHUNK = `${LINE_RANGE_CHUNK_SOURCE}(?<=[\\d.-])`;
65
+ const RANGE_LIST_SRC = `${RANGE_SELECTOR_CHUNK}(?:,${RANGE_SELECTOR_CHUNK})*`;
66
+ // A tail selector: `-N` reads the last N lines. Keep in sync with TAIL_SELECTOR_RE.
67
+ const TAIL_CHUNK_SRC = String.raw`-\d+`;
68
+ const FILE_LINE_RANGE_RE = new RegExp(`^(?:${RANGE_LIST_SRC}|${TAIL_CHUNK_SRC}|raw|conflicts|img)$`, "i");
69
+ const FILE_LINE_RANGE_ONLY_RE = new RegExp(`^(?:${RANGE_LIST_SRC}|${TAIL_CHUNK_SRC})$`, "i");
70
+ const FILE_RAW_ONLY_RE = /^raw$/i;
71
+ // Permissive selector chunk for internal URLs — accepts well-formed selectors
72
+ // plus common malformed shapes (e.g. `:-N-M`) so the read tool peels the entire
73
+ // selector chain off before dispatching to a protocol handler.
74
+ const INTERNAL_URL_SELECTOR_PART_RE = new RegExp(
75
+ String.raw`^(?:raw|conflicts|img|${RANGE_LIST_SRC}|-\d+(?:[-+]\d+)?)$`,
76
+ "i",
77
+ );
78
+ // Schemes whose host grammar is identifier-shaped, so any trailing
79
+ // `:<selector-chunk>` is unambiguously a read-tool selector. `mcp://` is
80
+ // excluded because mcp resource URIs may legitimately contain colons. `ssh://`
81
+ // is included despite an optional `:port`; `splitInternalUrlSel` skips the peel
82
+ // for an `ssh://host:port` that has no `/path`, so the port colon is never
83
+ // mistaken for a selector (a real ssh selector trails the `/path`, e.g.
84
+ // `ssh://h/f:1-5`).
85
+ const INTERNAL_SCHEMES_WITH_SELECTORS: Record<string, true> = {
86
+ agent: true,
87
+ artifact: true,
88
+ issue: true,
89
+ history: true,
90
+ local: true,
91
+ memory: true,
92
+ omp: true,
93
+ pr: true,
94
+ rule: true,
95
+ security: true,
96
+ skill: true,
97
+ ssh: true,
98
+ vault: true,
99
+ };
100
+ // Schemes whose resource URIs are server-defined and may legitimately end
101
+ // with selector-shaped tails (e.g. `:raw`, `:conflicts`, `:1-50`, `/:raw`).
102
+ // `McpProtocolHandler` resolves by exact URI match (`r.uri === uri`), so
103
+ // peeling syntactically can make valid resources unreachable. Keep these
104
+ // schemes opaque; selector support for them needs a resolver-aware path that
105
+ // tries the exact URI before interpreting any suffix as a read selector.
106
+ const OPAQUE_RESOURCE_SCHEMES: ReadonlySet<string> = new Set(["mcp"]);
107
+ const INTERNAL_URL_SCHEME_RE = /^([a-z][a-z0-9+.-]*):\/\//i;
108
+ /** Split a filesystem path from its trailing read selector. */
109
+ export function splitPathAndSel(rawPath: string): { path: string; sel?: string } {
110
+ const colon = rawPath.lastIndexOf(":");
111
+ if (colon <= 0) return { path: rawPath };
112
+
113
+ const candidate = rawPath.slice(colon + 1);
114
+ if (!FILE_LINE_RANGE_RE.test(candidate)) return { path: rawPath };
115
+
116
+ let basePath = rawPath.slice(0, colon);
117
+ let sel = candidate;
118
+
119
+ // Allow a compound trailing selector: `path:1-50:raw`, `path:raw:1-50`, or
120
+ // `path:raw:-60`. The two chunks must be one line-range (or tail) plus one
121
+ // `raw`, in either order.
122
+ const innerColon = basePath.lastIndexOf(":");
123
+ if (innerColon > 0) {
124
+ const innerCandidate = basePath.slice(innerColon + 1);
125
+ const innerIsRaw = FILE_RAW_ONLY_RE.test(innerCandidate);
126
+ const outerIsRaw = FILE_RAW_ONLY_RE.test(candidate);
127
+ const innerIsRange = FILE_LINE_RANGE_ONLY_RE.test(innerCandidate);
128
+ const outerIsRange = FILE_LINE_RANGE_ONLY_RE.test(candidate);
129
+ if ((innerIsRaw && outerIsRange) || (innerIsRange && outerIsRaw)) {
130
+ sel = `${innerCandidate}:${candidate}`;
131
+ basePath = basePath.slice(0, innerColon);
132
+ }
133
+ }
134
+
135
+ return { path: basePath, sel };
136
+ }
137
+
138
+ /**
139
+ * Variant of {@link splitPathAndSel} for internal URLs (`scheme://...`).
140
+ *
141
+ * The filesystem-path splitter is intentionally conservative: it refuses to
142
+ * peel a trailing `:<chunk>` unless that chunk matches the strict selector
143
+ * grammar. That rule is right for filesystem paths (a file named `a:1-50` is
144
+ * legal) but wrong for internal URLs, where any trailing `:<chunk>` after the
145
+ * scheme is unambiguously a read-tool selector — even if malformed (e.g.
146
+ * `artifact://3:raw:-100-5`).
147
+ *
148
+ * This function iteratively peels selector-shaped chunks (well-formed plus
149
+ * common malformed shapes like `:-N-M`) so the rest of the read tool can pass a
150
+ * clean URL to the protocol handler and surface selector errors via parseSel
151
+ * instead of as misleading "host invalid" errors from the handler. Schemes
152
+ * whose resource URIs may legitimately contain colons (`mcp://`) are skipped.
153
+ *
154
+ * Falls back to the input unchanged when nothing matches.
155
+ */
156
+
157
+ export function splitInternalUrlSel(rawPath: string): { path: string; sel?: string } {
158
+ const schemeMatch = rawPath.match(INTERNAL_URL_SCHEME_RE);
159
+ if (!schemeMatch) return { path: rawPath };
160
+ const scheme = schemeMatch[1].toLowerCase();
161
+ // Opaque schemes (mcp://, etc.) carry server-defined resource URIs that may
162
+ // legitimately end in selector-shaped tails. Forward verbatim — see
163
+ // OPAQUE_RESOURCE_SCHEMES.
164
+ if (OPAQUE_RESOURCE_SCHEMES.has(scheme)) return { path: rawPath };
165
+ if (!INTERNAL_SCHEMES_WITH_SELECTORS[scheme]) return { path: rawPath };
166
+
167
+ const schemeEnd = schemeMatch[0].length;
168
+ // ssh:// authority carries an optional `:port`; with no `/path` after the
169
+ // authority, a trailing `:NNNN` is the port, not a read selector
170
+ // (e.g. ssh://host:2222). Other schemes' authority-trailing selectors
171
+ // (artifact://5:1-50) still peel, so this guard is ssh-specific.
172
+ if (scheme === "ssh" && rawPath.indexOf("/", schemeEnd) === -1) {
173
+ return { path: rawPath };
174
+ }
175
+ let path = rawPath;
176
+ const chunks: string[] = [];
177
+ while (true) {
178
+ const colon = path.lastIndexOf(":");
179
+ // Stop before crossing into the scheme separator `://`.
180
+ if (colon < schemeEnd) break;
181
+ const tail = path.slice(colon + 1);
182
+ if (!INTERNAL_URL_SELECTOR_PART_RE.test(tail)) break;
183
+ chunks.unshift(tail);
184
+ path = path.slice(0, colon);
185
+ }
186
+ if (chunks.length === 0) return { path: rawPath };
187
+ return { path, sel: chunks.join(":") };
188
+ }
189
+
190
+ /** Recognize HTTP URLs and www-prefixed external read targets. */
191
+ export function isReadableUrlPath(value: string): boolean {
192
+ return /^https?:\/\/?/i.test(value) || /^www\./i.test(value);
193
+ }
194
+
195
+ /** Return the first selected line when every range has valid one-based bounds. */
196
+ export function readSelectorRangeStart(selector: string): number | undefined {
197
+ try {
198
+ const start = parseLineRanges(selector)?.[0].startLine;
199
+ return start !== undefined && Number.isFinite(start) ? start : undefined;
200
+ } catch {
201
+ return undefined;
202
+ }
203
+ }
204
+
205
+ // =============================================================================
206
+ // TUI Renderer
207
+ // =============================================================================
208
+
209
+ export interface ReadRenderArgs {
210
+ path?: unknown;
211
+ file_path?: unknown;
212
+ // Legacy fields from old schema — tolerated for in-flight tool calls during transition
213
+ offset?: number;
214
+ limit?: number;
215
+ raw?: boolean;
216
+ }
217
+
218
+ const INTERNAL_URL_LIKE_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
219
+
220
+ function splitReadRenderPath(rawPath: string): { path: string; sel?: string } {
221
+ if (INTERNAL_URL_LIKE_RE.test(rawPath)) {
222
+ const internal = splitInternalUrlSel(rawPath);
223
+ if (internal.sel) return internal;
224
+ }
225
+ return splitPathAndSel(rawPath);
226
+ }
227
+
228
+ function firstReadSelectorLine(sel: string | undefined): number | undefined {
229
+ if (!sel) return undefined;
230
+ const range = sel.split(":").find(chunk => chunk.toLowerCase() !== "raw");
231
+ return range ? readSelectorRangeStart(range) : undefined;
232
+ }
233
+
234
+ /** Absolute fs path the read result actually resolved to, used as the OSC 8 link
235
+ * target when the structured `resolvedPath` isn't set (the common plain-file and
236
+ * image reads only record the path in `meta.source`). URL/internal sources are
237
+ * not fs paths, so only `type: "path"` qualifies. */
238
+ export function readSourceFsPath(details: ReadToolDetails | undefined): string | undefined {
239
+ const source = details?.meta?.source;
240
+ return source?.type === "path" && typeof source.value === "string" ? source.value : undefined;
241
+ }
242
+
243
+ function formatReadPathLink(
244
+ rawPath: string,
245
+ options: {
246
+ resolvedPath?: string;
247
+ sourcePath?: string;
248
+ suffixResolution?: { from: string; to: string };
249
+ offset?: number;
250
+ fallbackLabel?: string;
251
+ },
252
+ ): string {
253
+ const split = splitReadRenderPath(rawPath);
254
+ const basePath = split.path || rawPath;
255
+ const selectorSuffix = split.sel ? `:${split.sel}` : "";
256
+ const plainDisplayPath = options.suffixResolution
257
+ ? shortenPath(options.suffixResolution.to)
258
+ : shortenPath(basePath || options.resolvedPath || options.fallbackLabel || rawPath);
259
+ const absoluteInputPath = path.isAbsolute(basePath) ? basePath : undefined;
260
+ const target = options.resolvedPath ?? options.sourcePath ?? absoluteInputPath;
261
+ const line = firstReadSelectorLine(split.sel) ?? options.offset;
262
+ const linkOptions = line !== undefined ? { line } : undefined;
263
+ const linkedPath = target ? fileHyperlink(target, plainDisplayPath, linkOptions) : plainDisplayPath;
264
+ return `${linkedPath}${selectorSuffix}`;
265
+ }
266
+
267
+ /** Render file, image, and URL reads in the transcript. */
268
+ export const readToolRenderer = {
269
+ renderCall(args: ReadRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
270
+ const rawPath =
271
+ typeof args.file_path === "string" ? args.file_path : typeof args.path === "string" ? args.path : "";
272
+ if (isReadableUrlPath(rawPath)) {
273
+ return renderReadUrlCall({ path: rawPath, raw: args.raw }, _options, uiTheme);
274
+ }
275
+
276
+ const offset = args.offset;
277
+ const limit = args.limit;
278
+
279
+ let pathDisplay = formatReadPathLink(rawPath, { offset, fallbackLabel: "…" }) || "…";
280
+ if (offset !== undefined || limit !== undefined) {
281
+ const startLine = offset ?? 1;
282
+ const endLine = limit !== undefined ? startLine + limit - 1 : "";
283
+ pathDisplay += `:${startLine}${endLine ? `-${endLine}` : ""}`;
284
+ }
285
+
286
+ const text = renderStatusLine({ icon: "pending", title: "Read", description: pathDisplay }, uiTheme);
287
+ return new Text(text, 0, 0);
288
+ },
289
+
290
+ renderResult(
291
+ result: { content: Array<{ type: string; text?: string }>; details?: ReadToolDetails; isError?: boolean },
292
+ options: RenderResultOptions,
293
+ uiTheme: Theme,
294
+ args?: ReadRenderArgs,
295
+ ): Component {
296
+ const urlDetails = result.details as ReadUrlToolDetails | undefined;
297
+ const baseRawPathForKind =
298
+ typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
299
+ if (urlDetails?.kind === "url" || isReadableUrlPath(baseRawPathForKind)) {
300
+ return renderReadUrlResult(
301
+ result as {
302
+ content: Array<{ type: string; text?: string }>;
303
+ details?: ReadUrlToolDetails;
304
+ isError?: boolean;
305
+ },
306
+ options,
307
+ uiTheme,
308
+ );
309
+ }
310
+
311
+ if (result.isError) {
312
+ const rawErrorText = result.content?.find(c => c.type === "text")?.text ?? "";
313
+ const errorText = (rawErrorText || "Unknown error").replace(/^Error:\s*/, "");
314
+ const rawPath =
315
+ typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
316
+ const filePath =
317
+ formatReadPathLink(rawPath, {
318
+ offset: args?.offset,
319
+ sourcePath: result.details?.displayTarget ?? readSourceFsPath(result.details),
320
+ }) || shortenPath(rawPath);
321
+ let title = filePath ? `Read ${filePath}` : "Read";
322
+ if (args?.offset !== undefined || args?.limit !== undefined) {
323
+ const startLine = args.offset ?? 1;
324
+ const endLine = args.limit !== undefined ? startLine + args.limit - 1 : "";
325
+ title += `:${startLine}${endLine ? `-${endLine}` : ""}`;
326
+ }
327
+ const header = renderStatusLine({ icon: "error", title }, uiTheme);
328
+ const errorLines = sanitizeDisplayLines(errorText).map(line => uiTheme.fg("error", line));
329
+ return framedToolCard(uiTheme, () => ({
330
+ header,
331
+ phase: "error",
332
+ sections: [{ content: errorLines }],
333
+ }));
334
+ }
335
+ const details = result.details;
336
+ const rawText = result.content?.find(c => c.type === "text")?.text ?? "";
337
+ // Prefer structured `displayContent` from details when available so the TUI
338
+ // shows clean file content (no model-only hashline anchors) without parsing the formatted text.
339
+ // Fall back to the raw text, but strip the LLM-facing notice so it doesn't
340
+ // echo next to the styled warning line below.
341
+ const contentText = details?.displayContent?.text ?? stripOutputNotice(rawText, details?.meta);
342
+ const imageContent = result.content?.find(c => c.type === "image");
343
+ const rawPath =
344
+ typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
345
+ const renderPath = splitReadRenderPath(rawPath);
346
+ const lang = getLanguageFromPath(renderPath.path);
347
+
348
+ const warningLines: string[] = [];
349
+ const truncation = details?.meta?.truncation;
350
+ const fallback = details?.truncation;
351
+ if (details?.resolvedPath) {
352
+ warningLines.push(uiTheme.fg("dim", wrapBrackets(`Resolved path: ${details.resolvedPath}`, uiTheme)));
353
+ }
354
+ if (truncation) {
355
+ if (fallback?.firstLineExceedsLimit) {
356
+ let warning = `First line exceeds ${formatBytes(fallback.outputBytes ?? fallback.totalBytes)} limit`;
357
+ if (truncation.artifactId) {
358
+ warning += `. ${formatFullOutputReference(truncation.artifactId)}`;
359
+ }
360
+ warningLines.push(uiTheme.fg("warning", wrapBrackets(warning, uiTheme)));
361
+ } else {
362
+ const warning = formatStyledTruncationWarning(details?.meta, uiTheme);
363
+ if (warning) warningLines.push(warning);
364
+ }
365
+ }
366
+
367
+ if (imageContent) {
368
+ const suffix = details?.suffixResolution;
369
+ const displayPath = formatReadPathLink(rawPath, {
370
+ resolvedPath: details?.resolvedPath,
371
+ sourcePath: details?.displayTarget ?? readSourceFsPath(details),
372
+ suffixResolution: suffix,
373
+ fallbackLabel: "image",
374
+ });
375
+ const correction = suffix ? ` ${uiTheme.fg("dim", `(corrected from ${shortenPath(suffix.from)})`)}` : "";
376
+ const header = renderStatusLine(
377
+ { icon: suffix ? "warning" : "success", title: "Read", description: `${displayPath}${correction}` },
378
+ uiTheme,
379
+ );
380
+ const detailLines = contentText
381
+ ? sanitizeDisplayLines(contentText).map(line => uiTheme.fg("toolOutput", line))
382
+ : [];
383
+ const lines = [...detailLines, ...warningLines];
384
+ return framedToolCard(uiTheme, () => ({
385
+ header,
386
+ phase: "success",
387
+ sections: [
388
+ {
389
+ label: uiTheme.fg("toolTitle", "Details"),
390
+ content: lines.length > 0 ? lines : [uiTheme.fg("dim", "(image)")],
391
+ },
392
+ ],
393
+ }));
394
+ }
395
+
396
+ const suffix = details?.suffixResolution;
397
+ // resolvedPath is the absolute fs path when a read resolved/corrected the
398
+ // input (suffix match, internal URL, archive/sqlite/notebook); plain file
399
+ // reads only record the absolute path in meta.source, so fall back to that
400
+ // to keep the title clickable.
401
+ const displayPath = formatReadPathLink(rawPath, {
402
+ resolvedPath: details?.resolvedPath,
403
+ sourcePath: details?.displayTarget ?? readSourceFsPath(details),
404
+ suffixResolution: suffix,
405
+ offset: args?.offset,
406
+ });
407
+ const correction = suffix ? ` ${uiTheme.fg("dim", `(corrected from ${shortenPath(suffix.from)})`)}` : "";
408
+ let title = displayPath ? `Read ${displayPath}${correction}` : "Read";
409
+ if (args?.offset !== undefined || args?.limit !== undefined) {
410
+ const startLine = args.offset ?? 1;
411
+ const endLine = args.limit !== undefined ? startLine + args.limit - 1 : "";
412
+ title += `:${startLine}${endLine ? `-${endLine}` : ""}`;
413
+ }
414
+ if (details?.summary) {
415
+ title += ` (summary: ${details.summary.elidedSpans} elided span${details.summary.elidedSpans === 1 ? "" : "s"})`;
416
+ }
417
+ if (details?.conflictCount && details.conflictCount > 0) {
418
+ const n = details.conflictCount;
419
+ title += ` ${uiTheme.fg("warning", `(⚠ ${n} conflict${n === 1 ? "" : "s"})`)}`;
420
+ }
421
+ const rawRequested =
422
+ args?.raw === true || renderPath.sel?.split(":").some(chunk => chunk.toLowerCase() === "raw") === true;
423
+ const isMarkdown = details?.contentType === "text/markdown" && !rawRequested;
424
+ let cachedWidth: number | undefined;
425
+ let cachedExpanded: boolean | undefined;
426
+ let cachedLines: string[] | undefined;
427
+ return markFramedBlockComponent({
428
+ render: (width: number) => {
429
+ const expanded = options.expanded;
430
+ if (cachedLines && cachedWidth === width && cachedExpanded === expanded) return cachedLines;
431
+ cachedLines = isMarkdown
432
+ ? renderMarkdownCell(
433
+ {
434
+ content: contentText,
435
+ title,
436
+ status: "complete",
437
+ output: warningLines.length > 0 ? warningLines.join("\n") : undefined,
438
+ expanded,
439
+ width,
440
+ },
441
+ uiTheme,
442
+ )
443
+ : renderCodeCell(
444
+ {
445
+ code: contentText,
446
+ language: lang,
447
+ title,
448
+ status: "complete",
449
+ output: warningLines.length > 0 ? warningLines.join("\n") : undefined,
450
+ expanded,
451
+ codeStartLine: details?.displayContent?.startLine,
452
+ codeLineNumbers: details?.displayContent?.lineNumbers,
453
+ width,
454
+ },
455
+ uiTheme,
456
+ );
457
+ cachedWidth = width;
458
+ cachedExpanded = expanded;
459
+ return cachedLines;
460
+ },
461
+ invalidate: () => {
462
+ cachedWidth = undefined;
463
+ cachedExpanded = undefined;
464
+ cachedLines = undefined;
465
+ },
466
+ });
467
+ },
468
+ mergeCallAndResult: true,
469
+ } satisfies ToolRenderer<ReadRenderArgs, ReadToolDetails>;
470
+
471
+ /** Inclusive line range describing one elided span in a structural summary. */
472
+ export interface ElidedRange {
473
+ start: number;
474
+ end: number;
475
+ }
476
+
477
+ const BRACE_PAIRS: Record<string, string> = { "{": "}", "(": ")", "[": "]" };
478
+ const BRACE_TAIL_TRAILING_RE = /^[;,)\]}]*$/;
479
+
480
+ /**
481
+ * Decide whether the kept lines surrounding an elided range collapse to a
482
+ * single brace-pair line in the rendered summary. Returns true when the head
483
+ * line ends with `{` / `(` / `[` and the tail line is the matching closer
484
+ * (optionally followed by terminating punctuation like `;`, `,`, or further
485
+ * closers — e.g. `};`, `})`, `]);`).
486
+ */
487
+ export function canMergeBracePair(headLine: string, tailLine: string): boolean {
488
+ const head = headLine.trimEnd();
489
+ const tail = tailLine.trim();
490
+ const opener = head.slice(-1);
491
+ const closer = BRACE_PAIRS[opener];
492
+ if (!closer) return false;
493
+ if (!tail.startsWith(closer)) return false;
494
+ return BRACE_TAIL_TRAILING_RE.test(tail.slice(closer.length));
495
+ }
496
+
497
+ /** Format one summary line with the selected line-prefix mode. */
498
+ export function formatSingleLine(
499
+ line: number,
500
+ text: string,
501
+ shouldAddHashLines: boolean,
502
+ shouldAddLineNumbers: boolean,
503
+ ): string {
504
+ if (shouldAddHashLines) return formatNumberedLine(line, text);
505
+ if (shouldAddLineNumbers) return `${line}|${text}`;
506
+ return text;
507
+ }
508
+
509
+ /** Format the boundary lines around an elided brace-pair body. */
510
+ export function formatMergedBraceLine(
511
+ startLine: number,
512
+ endLine: number,
513
+ headText: string,
514
+ tailText: string,
515
+ shouldAddHashLines: boolean,
516
+ shouldAddLineNumbers: boolean,
517
+ ): { model: string; display: string } {
518
+ const merged = `${headText.trimEnd()} … ${tailText.trim()}`;
519
+ if (shouldAddHashLines) {
520
+ return { model: `${startLine}-${endLine}:${merged}`, display: merged };
521
+ }
522
+ if (shouldAddLineNumbers) {
523
+ return { model: `${startLine}-${endLine}|${merged}`, display: merged };
524
+ }
525
+ return { model: merged, display: merged };
526
+ }
527
+
528
+ /** Format a structural summary using the supplied file-display preferences. */
529
+ export function formatReadSummary(
530
+ displayMode: { hashLines: boolean; lineNumbers: boolean },
531
+ summary: SummaryResult,
532
+ ): {
533
+ text: string;
534
+ displayText: string;
535
+ elidedRanges: ElidedRange[];
536
+ elidedLines: number;
537
+ } {
538
+ const shouldAddHashLines = displayMode.hashLines;
539
+ const shouldAddLineNumbers = shouldAddHashLines ? false : displayMode.lineNumbers;
540
+
541
+ // Flatten segments into per-line units so we can merge a kept-head /
542
+ // elided / kept-tail sandwich into a single brace-pair line when the
543
+ // boundary lines look like `… {` and `}` (or matching variants).
544
+ type Unit =
545
+ | { kind: "line"; line: number; text: string }
546
+ | { kind: "elided"; startLine: number; endLine: number }
547
+ | {
548
+ kind: "merged";
549
+ startLine: number;
550
+ endLine: number;
551
+ headText: string;
552
+ tailText: string;
553
+ };
554
+
555
+ const raw: Unit[] = [];
556
+ for (const segment of summary.segments) {
557
+ if (segment.kind === "elided") {
558
+ raw.push({ kind: "elided", startLine: segment.startLine, endLine: segment.endLine });
559
+ continue;
560
+ }
561
+ const text = segment.text ?? "";
562
+ if (text.length === 0) continue;
563
+ const lines = text.split("\n");
564
+ for (let i = 0; i < lines.length; i++) {
565
+ raw.push({ kind: "line", line: segment.startLine + i, text: lines[i] });
566
+ }
567
+ }
568
+
569
+ const units: Unit[] = [];
570
+ let i = 0;
571
+ while (i < raw.length) {
572
+ const cur = raw[i];
573
+ if (cur.kind === "elided") {
574
+ const prev = units.length > 0 ? units[units.length - 1] : null;
575
+ const next = i + 1 < raw.length ? raw[i + 1] : null;
576
+ if (prev?.kind === "line" && next?.kind === "line" && canMergeBracePair(prev.text, next.text)) {
577
+ units.pop();
578
+ units.push({
579
+ kind: "merged",
580
+ startLine: prev.line,
581
+ endLine: next.line,
582
+ headText: prev.text,
583
+ tailText: next.text,
584
+ });
585
+ i += 2;
586
+ continue;
587
+ }
588
+ }
589
+ units.push(cur);
590
+ i++;
591
+ }
592
+
593
+ const modelParts: string[] = [];
594
+ const displayParts: string[] = [];
595
+ const elidedRanges: ElidedRange[] = [];
596
+ let elidedLines = 0;
597
+ for (const unit of units) {
598
+ if (unit.kind === "elided") {
599
+ modelParts.push("…");
600
+ displayParts.push("…");
601
+ elidedRanges.push({ start: unit.startLine, end: unit.endLine });
602
+ elidedLines += unit.endLine - unit.startLine + 1;
603
+ continue;
604
+ }
605
+ if (unit.kind === "merged") {
606
+ const formatted = formatMergedBraceLine(
607
+ unit.startLine,
608
+ unit.endLine,
609
+ unit.headText,
610
+ unit.tailText,
611
+ shouldAddHashLines,
612
+ shouldAddLineNumbers,
613
+ );
614
+ modelParts.push(formatted.model);
615
+ displayParts.push(formatted.display);
616
+ // Suggest the full brace range so re-reading shows both braces
617
+ // plus the elided body in one shot.
618
+ elidedRanges.push({ start: unit.startLine, end: unit.endLine });
619
+ // Merged brace pair encloses (start+1)..(end-1) as elided.
620
+ elidedLines += Math.max(0, unit.endLine - unit.startLine - 1);
621
+ continue;
622
+ }
623
+ modelParts.push(formatSingleLine(unit.line, unit.text, shouldAddHashLines, shouldAddLineNumbers));
624
+ displayParts.push(unit.text);
625
+ }
626
+
627
+ return { text: modelParts.join("\n"), displayText: displayParts.join("\n"), elidedRanges, elidedLines };
628
+ }