@vandeepunk/pi-coding-agent 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (595) hide show
  1. package/CHANGELOG.md +2564 -0
  2. package/README.md +555 -0
  3. package/dist/cli/args.d.ts +47 -0
  4. package/dist/cli/args.d.ts.map +1 -0
  5. package/dist/cli/args.js +286 -0
  6. package/dist/cli/args.js.map +1 -0
  7. package/dist/cli/config-selector.d.ts +14 -0
  8. package/dist/cli/config-selector.d.ts.map +1 -0
  9. package/dist/cli/config-selector.js +31 -0
  10. package/dist/cli/config-selector.js.map +1 -0
  11. package/dist/cli/file-processor.d.ts +15 -0
  12. package/dist/cli/file-processor.d.ts.map +1 -0
  13. package/dist/cli/file-processor.js +79 -0
  14. package/dist/cli/file-processor.js.map +1 -0
  15. package/dist/cli/list-models.d.ts +9 -0
  16. package/dist/cli/list-models.d.ts.map +1 -0
  17. package/dist/cli/list-models.js +92 -0
  18. package/dist/cli/list-models.js.map +1 -0
  19. package/dist/cli/session-picker.d.ts +9 -0
  20. package/dist/cli/session-picker.d.ts.map +1 -0
  21. package/dist/cli/session-picker.js +34 -0
  22. package/dist/cli/session-picker.js.map +1 -0
  23. package/dist/cli.d.ts +3 -0
  24. package/dist/cli.d.ts.map +1 -0
  25. package/dist/cli.js +11 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/config.d.ts +68 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +203 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/core/agent-session.d.ts +574 -0
  32. package/dist/core/agent-session.d.ts.map +1 -0
  33. package/dist/core/agent-session.js +2260 -0
  34. package/dist/core/agent-session.js.map +1 -0
  35. package/dist/core/auth-storage.d.ts +102 -0
  36. package/dist/core/auth-storage.d.ts.map +1 -0
  37. package/dist/core/auth-storage.js +282 -0
  38. package/dist/core/auth-storage.js.map +1 -0
  39. package/dist/core/bash-executor.d.ts +47 -0
  40. package/dist/core/bash-executor.d.ts.map +1 -0
  41. package/dist/core/bash-executor.js +212 -0
  42. package/dist/core/bash-executor.js.map +1 -0
  43. package/dist/core/compaction/branch-summarization.d.ts +86 -0
  44. package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
  45. package/dist/core/compaction/branch-summarization.js +242 -0
  46. package/dist/core/compaction/branch-summarization.js.map +1 -0
  47. package/dist/core/compaction/compaction.d.ts +121 -0
  48. package/dist/core/compaction/compaction.d.ts.map +1 -0
  49. package/dist/core/compaction/compaction.js +607 -0
  50. package/dist/core/compaction/compaction.js.map +1 -0
  51. package/dist/core/compaction/index.d.ts +7 -0
  52. package/dist/core/compaction/index.d.ts.map +1 -0
  53. package/dist/core/compaction/index.js +7 -0
  54. package/dist/core/compaction/index.js.map +1 -0
  55. package/dist/core/compaction/utils.d.ts +35 -0
  56. package/dist/core/compaction/utils.d.ts.map +1 -0
  57. package/dist/core/compaction/utils.js +138 -0
  58. package/dist/core/compaction/utils.js.map +1 -0
  59. package/dist/core/defaults.d.ts +3 -0
  60. package/dist/core/defaults.d.ts.map +1 -0
  61. package/dist/core/defaults.js +2 -0
  62. package/dist/core/defaults.js.map +1 -0
  63. package/dist/core/diagnostics.d.ts +15 -0
  64. package/dist/core/diagnostics.d.ts.map +1 -0
  65. package/dist/core/diagnostics.js +2 -0
  66. package/dist/core/diagnostics.js.map +1 -0
  67. package/dist/core/event-bus.d.ts +9 -0
  68. package/dist/core/event-bus.d.ts.map +1 -0
  69. package/dist/core/event-bus.js +25 -0
  70. package/dist/core/event-bus.js.map +1 -0
  71. package/dist/core/exec.d.ts +29 -0
  72. package/dist/core/exec.d.ts.map +1 -0
  73. package/dist/core/exec.js +71 -0
  74. package/dist/core/exec.js.map +1 -0
  75. package/dist/core/export-html/ansi-to-html.d.ts +22 -0
  76. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
  77. package/dist/core/export-html/ansi-to-html.js +249 -0
  78. package/dist/core/export-html/ansi-to-html.js.map +1 -0
  79. package/dist/core/export-html/index.d.ts +34 -0
  80. package/dist/core/export-html/index.d.ts.map +1 -0
  81. package/dist/core/export-html/index.js +222 -0
  82. package/dist/core/export-html/index.js.map +1 -0
  83. package/dist/core/export-html/template.css +909 -0
  84. package/dist/core/export-html/template.html +54 -0
  85. package/dist/core/export-html/template.js +1549 -0
  86. package/dist/core/export-html/tool-renderer.d.ts +35 -0
  87. package/dist/core/export-html/tool-renderer.d.ts.map +1 -0
  88. package/dist/core/export-html/tool-renderer.js +57 -0
  89. package/dist/core/export-html/tool-renderer.js.map +1 -0
  90. package/dist/core/export-html/vendor/highlight.min.js +1213 -0
  91. package/dist/core/export-html/vendor/marked.min.js +6 -0
  92. package/dist/core/extensions/index.d.ts +11 -0
  93. package/dist/core/extensions/index.d.ts.map +1 -0
  94. package/dist/core/extensions/index.js +9 -0
  95. package/dist/core/extensions/index.js.map +1 -0
  96. package/dist/core/extensions/loader.d.ts +25 -0
  97. package/dist/core/extensions/loader.d.ts.map +1 -0
  98. package/dist/core/extensions/loader.js +400 -0
  99. package/dist/core/extensions/loader.js.map +1 -0
  100. package/dist/core/extensions/runner.d.ts +129 -0
  101. package/dist/core/extensions/runner.d.ts.map +1 -0
  102. package/dist/core/extensions/runner.js +576 -0
  103. package/dist/core/extensions/runner.js.map +1 -0
  104. package/dist/core/extensions/types.d.ts +928 -0
  105. package/dist/core/extensions/types.d.ts.map +1 -0
  106. package/dist/core/extensions/types.js +35 -0
  107. package/dist/core/extensions/types.js.map +1 -0
  108. package/dist/core/extensions/wrapper.d.ts +27 -0
  109. package/dist/core/extensions/wrapper.d.ts.map +1 -0
  110. package/dist/core/extensions/wrapper.js +102 -0
  111. package/dist/core/extensions/wrapper.js.map +1 -0
  112. package/dist/core/footer-data-provider.d.ts +32 -0
  113. package/dist/core/footer-data-provider.d.ts.map +1 -0
  114. package/dist/core/footer-data-provider.js +134 -0
  115. package/dist/core/footer-data-provider.js.map +1 -0
  116. package/dist/core/index.d.ts +9 -0
  117. package/dist/core/index.d.ts.map +1 -0
  118. package/dist/core/index.js +9 -0
  119. package/dist/core/index.js.map +1 -0
  120. package/dist/core/keybindings.d.ts +55 -0
  121. package/dist/core/keybindings.d.ts.map +1 -0
  122. package/dist/core/keybindings.js +153 -0
  123. package/dist/core/keybindings.js.map +1 -0
  124. package/dist/core/messages.d.ts +77 -0
  125. package/dist/core/messages.d.ts.map +1 -0
  126. package/dist/core/messages.js +123 -0
  127. package/dist/core/messages.js.map +1 -0
  128. package/dist/core/model-registry.d.ts +100 -0
  129. package/dist/core/model-registry.d.ts.map +1 -0
  130. package/dist/core/model-registry.js +419 -0
  131. package/dist/core/model-registry.js.map +1 -0
  132. package/dist/core/model-resolver.d.ts +76 -0
  133. package/dist/core/model-resolver.d.ts.map +1 -0
  134. package/dist/core/model-resolver.js +313 -0
  135. package/dist/core/model-resolver.js.map +1 -0
  136. package/dist/core/package-manager.d.ts +131 -0
  137. package/dist/core/package-manager.d.ts.map +1 -0
  138. package/dist/core/package-manager.js +1290 -0
  139. package/dist/core/package-manager.js.map +1 -0
  140. package/dist/core/prompt-templates.d.ts +50 -0
  141. package/dist/core/prompt-templates.d.ts.map +1 -0
  142. package/dist/core/prompt-templates.js +251 -0
  143. package/dist/core/prompt-templates.js.map +1 -0
  144. package/dist/core/resolve-config-value.d.ts +17 -0
  145. package/dist/core/resolve-config-value.d.ts.map +1 -0
  146. package/dist/core/resolve-config-value.js +59 -0
  147. package/dist/core/resolve-config-value.js.map +1 -0
  148. package/dist/core/resource-loader.d.ts +184 -0
  149. package/dist/core/resource-loader.d.ts.map +1 -0
  150. package/dist/core/resource-loader.js +673 -0
  151. package/dist/core/resource-loader.js.map +1 -0
  152. package/dist/core/sdk.d.ts +90 -0
  153. package/dist/core/sdk.d.ts.map +1 -0
  154. package/dist/core/sdk.js +234 -0
  155. package/dist/core/sdk.js.map +1 -0
  156. package/dist/core/session-manager.d.ts +323 -0
  157. package/dist/core/session-manager.d.ts.map +1 -0
  158. package/dist/core/session-manager.js +1091 -0
  159. package/dist/core/session-manager.js.map +1 -0
  160. package/dist/core/settings-manager.d.ts +187 -0
  161. package/dist/core/settings-manager.d.ts.map +1 -0
  162. package/dist/core/settings-manager.js +552 -0
  163. package/dist/core/settings-manager.js.map +1 -0
  164. package/dist/core/skills.d.ts +58 -0
  165. package/dist/core/skills.d.ts.map +1 -0
  166. package/dist/core/skills.js +310 -0
  167. package/dist/core/skills.js.map +1 -0
  168. package/dist/core/slash-commands.d.ts +15 -0
  169. package/dist/core/slash-commands.d.ts.map +1 -0
  170. package/dist/core/slash-commands.js +21 -0
  171. package/dist/core/slash-commands.js.map +1 -0
  172. package/dist/core/system-prompt.d.ts +24 -0
  173. package/dist/core/system-prompt.d.ts.map +1 -0
  174. package/dist/core/system-prompt.js +137 -0
  175. package/dist/core/system-prompt.js.map +1 -0
  176. package/dist/core/timings.d.ts +7 -0
  177. package/dist/core/timings.d.ts.map +1 -0
  178. package/dist/core/timings.js +25 -0
  179. package/dist/core/timings.js.map +1 -0
  180. package/dist/core/tools/bash.d.ts +55 -0
  181. package/dist/core/tools/bash.d.ts.map +1 -0
  182. package/dist/core/tools/bash.js +242 -0
  183. package/dist/core/tools/bash.js.map +1 -0
  184. package/dist/core/tools/edit-diff.d.ts +63 -0
  185. package/dist/core/tools/edit-diff.d.ts.map +1 -0
  186. package/dist/core/tools/edit-diff.js +243 -0
  187. package/dist/core/tools/edit-diff.js.map +1 -0
  188. package/dist/core/tools/edit.d.ts +39 -0
  189. package/dist/core/tools/edit.d.ts.map +1 -0
  190. package/dist/core/tools/edit.js +146 -0
  191. package/dist/core/tools/edit.js.map +1 -0
  192. package/dist/core/tools/find.d.ts +39 -0
  193. package/dist/core/tools/find.d.ts.map +1 -0
  194. package/dist/core/tools/find.js +206 -0
  195. package/dist/core/tools/find.js.map +1 -0
  196. package/dist/core/tools/grep.d.ts +45 -0
  197. package/dist/core/tools/grep.d.ts.map +1 -0
  198. package/dist/core/tools/grep.js +239 -0
  199. package/dist/core/tools/grep.js.map +1 -0
  200. package/dist/core/tools/index.d.ts +73 -0
  201. package/dist/core/tools/index.d.ts.map +1 -0
  202. package/dist/core/tools/index.js +61 -0
  203. package/dist/core/tools/index.js.map +1 -0
  204. package/dist/core/tools/ls.d.ts +40 -0
  205. package/dist/core/tools/ls.d.ts.map +1 -0
  206. package/dist/core/tools/ls.js +118 -0
  207. package/dist/core/tools/ls.js.map +1 -0
  208. package/dist/core/tools/path-utils.d.ts +8 -0
  209. package/dist/core/tools/path-utils.d.ts.map +1 -0
  210. package/dist/core/tools/path-utils.js +81 -0
  211. package/dist/core/tools/path-utils.js.map +1 -0
  212. package/dist/core/tools/read.d.ts +39 -0
  213. package/dist/core/tools/read.d.ts.map +1 -0
  214. package/dist/core/tools/read.js +166 -0
  215. package/dist/core/tools/read.js.map +1 -0
  216. package/dist/core/tools/truncate.d.ts +70 -0
  217. package/dist/core/tools/truncate.d.ts.map +1 -0
  218. package/dist/core/tools/truncate.js +205 -0
  219. package/dist/core/tools/truncate.js.map +1 -0
  220. package/dist/core/tools/write.d.ts +29 -0
  221. package/dist/core/tools/write.d.ts.map +1 -0
  222. package/dist/core/tools/write.js +78 -0
  223. package/dist/core/tools/write.js.map +1 -0
  224. package/dist/index.d.ts +27 -0
  225. package/dist/index.d.ts.map +1 -0
  226. package/dist/index.js +42 -0
  227. package/dist/index.js.map +1 -0
  228. package/dist/main.d.ts +8 -0
  229. package/dist/main.d.ts.map +1 -0
  230. package/dist/main.js +623 -0
  231. package/dist/main.js.map +1 -0
  232. package/dist/migrations.d.ts +33 -0
  233. package/dist/migrations.d.ts.map +1 -0
  234. package/dist/migrations.js +261 -0
  235. package/dist/migrations.js.map +1 -0
  236. package/dist/modes/index.d.ts +9 -0
  237. package/dist/modes/index.d.ts.map +1 -0
  238. package/dist/modes/index.js +8 -0
  239. package/dist/modes/index.js.map +1 -0
  240. package/dist/modes/interactive/components/armin.d.ts +34 -0
  241. package/dist/modes/interactive/components/armin.d.ts.map +1 -0
  242. package/dist/modes/interactive/components/armin.js +333 -0
  243. package/dist/modes/interactive/components/armin.js.map +1 -0
  244. package/dist/modes/interactive/components/assistant-message.d.ts +16 -0
  245. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -0
  246. package/dist/modes/interactive/components/assistant-message.js +91 -0
  247. package/dist/modes/interactive/components/assistant-message.js.map +1 -0
  248. package/dist/modes/interactive/components/bash-execution.d.ts +35 -0
  249. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -0
  250. package/dist/modes/interactive/components/bash-execution.js +162 -0
  251. package/dist/modes/interactive/components/bash-execution.js.map +1 -0
  252. package/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
  253. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
  254. package/dist/modes/interactive/components/bordered-loader.js +51 -0
  255. package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
  256. package/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
  257. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
  258. package/dist/modes/interactive/components/branch-summary-message.js +44 -0
  259. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
  260. package/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
  261. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
  262. package/dist/modes/interactive/components/compaction-summary-message.js +45 -0
  263. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
  264. package/dist/modes/interactive/components/config-selector.d.ts +71 -0
  265. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
  266. package/dist/modes/interactive/components/config-selector.js +479 -0
  267. package/dist/modes/interactive/components/config-selector.js.map +1 -0
  268. package/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
  269. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -0
  270. package/dist/modes/interactive/components/countdown-timer.js +33 -0
  271. package/dist/modes/interactive/components/countdown-timer.js.map +1 -0
  272. package/dist/modes/interactive/components/custom-editor.d.ts +21 -0
  273. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -0
  274. package/dist/modes/interactive/components/custom-editor.js +70 -0
  275. package/dist/modes/interactive/components/custom-editor.js.map +1 -0
  276. package/dist/modes/interactive/components/custom-message.d.ts +20 -0
  277. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
  278. package/dist/modes/interactive/components/custom-message.js +79 -0
  279. package/dist/modes/interactive/components/custom-message.js.map +1 -0
  280. package/dist/modes/interactive/components/daxnuts.d.ts +23 -0
  281. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -0
  282. package/dist/modes/interactive/components/daxnuts.js +140 -0
  283. package/dist/modes/interactive/components/daxnuts.js.map +1 -0
  284. package/dist/modes/interactive/components/diff.d.ts +12 -0
  285. package/dist/modes/interactive/components/diff.d.ts.map +1 -0
  286. package/dist/modes/interactive/components/diff.js +133 -0
  287. package/dist/modes/interactive/components/diff.js.map +1 -0
  288. package/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
  289. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -0
  290. package/dist/modes/interactive/components/dynamic-border.js +21 -0
  291. package/dist/modes/interactive/components/dynamic-border.js.map +1 -0
  292. package/dist/modes/interactive/components/extension-editor.d.ts +17 -0
  293. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
  294. package/dist/modes/interactive/components/extension-editor.js +102 -0
  295. package/dist/modes/interactive/components/extension-editor.js.map +1 -0
  296. package/dist/modes/interactive/components/extension-input.d.ts +23 -0
  297. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
  298. package/dist/modes/interactive/components/extension-input.js +61 -0
  299. package/dist/modes/interactive/components/extension-input.js.map +1 -0
  300. package/dist/modes/interactive/components/extension-selector.d.ts +24 -0
  301. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
  302. package/dist/modes/interactive/components/extension-selector.js +78 -0
  303. package/dist/modes/interactive/components/extension-selector.js.map +1 -0
  304. package/dist/modes/interactive/components/footer.d.ts +26 -0
  305. package/dist/modes/interactive/components/footer.d.ts.map +1 -0
  306. package/dist/modes/interactive/components/footer.js +220 -0
  307. package/dist/modes/interactive/components/footer.js.map +1 -0
  308. package/dist/modes/interactive/components/index.d.ts +32 -0
  309. package/dist/modes/interactive/components/index.d.ts.map +1 -0
  310. package/dist/modes/interactive/components/index.js +33 -0
  311. package/dist/modes/interactive/components/index.js.map +1 -0
  312. package/dist/modes/interactive/components/keybinding-hints.d.ts +41 -0
  313. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -0
  314. package/dist/modes/interactive/components/keybinding-hints.js +61 -0
  315. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -0
  316. package/dist/modes/interactive/components/login-dialog.d.ts +42 -0
  317. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -0
  318. package/dist/modes/interactive/components/login-dialog.js +145 -0
  319. package/dist/modes/interactive/components/login-dialog.js.map +1 -0
  320. package/dist/modes/interactive/components/model-selector.d.ts +47 -0
  321. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -0
  322. package/dist/modes/interactive/components/model-selector.js +266 -0
  323. package/dist/modes/interactive/components/model-selector.js.map +1 -0
  324. package/dist/modes/interactive/components/oauth-selector.d.ts +19 -0
  325. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -0
  326. package/dist/modes/interactive/components/oauth-selector.js +97 -0
  327. package/dist/modes/interactive/components/oauth-selector.js.map +1 -0
  328. package/dist/modes/interactive/components/scoped-models-selector.d.ts +49 -0
  329. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -0
  330. package/dist/modes/interactive/components/scoped-models-selector.js +270 -0
  331. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -0
  332. package/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
  333. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -0
  334. package/dist/modes/interactive/components/session-selector-search.js +155 -0
  335. package/dist/modes/interactive/components/session-selector-search.js.map +1 -0
  336. package/dist/modes/interactive/components/session-selector.d.ts +95 -0
  337. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -0
  338. package/dist/modes/interactive/components/session-selector.js +851 -0
  339. package/dist/modes/interactive/components/session-selector.js.map +1 -0
  340. package/dist/modes/interactive/components/settings-selector.d.ts +53 -0
  341. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -0
  342. package/dist/modes/interactive/components/settings-selector.js +277 -0
  343. package/dist/modes/interactive/components/settings-selector.js.map +1 -0
  344. package/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
  345. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -0
  346. package/dist/modes/interactive/components/show-images-selector.js +35 -0
  347. package/dist/modes/interactive/components/show-images-selector.js.map +1 -0
  348. package/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
  349. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
  350. package/dist/modes/interactive/components/skill-invocation-message.js +47 -0
  351. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
  352. package/dist/modes/interactive/components/theme-selector.d.ts +11 -0
  353. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -0
  354. package/dist/modes/interactive/components/theme-selector.js +46 -0
  355. package/dist/modes/interactive/components/theme-selector.js.map +1 -0
  356. package/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
  357. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -0
  358. package/dist/modes/interactive/components/thinking-selector.js +47 -0
  359. package/dist/modes/interactive/components/thinking-selector.js.map +1 -0
  360. package/dist/modes/interactive/components/tool-execution.d.ts +70 -0
  361. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -0
  362. package/dist/modes/interactive/components/tool-execution.js +621 -0
  363. package/dist/modes/interactive/components/tool-execution.js.map +1 -0
  364. package/dist/modes/interactive/components/tree-selector.d.ts +68 -0
  365. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
  366. package/dist/modes/interactive/components/tree-selector.js +934 -0
  367. package/dist/modes/interactive/components/tree-selector.js.map +1 -0
  368. package/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
  369. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -0
  370. package/dist/modes/interactive/components/user-message-selector.js +113 -0
  371. package/dist/modes/interactive/components/user-message-selector.js.map +1 -0
  372. package/dist/modes/interactive/components/user-message.d.ts +8 -0
  373. package/dist/modes/interactive/components/user-message.d.ts.map +1 -0
  374. package/dist/modes/interactive/components/user-message.js +16 -0
  375. package/dist/modes/interactive/components/user-message.js.map +1 -0
  376. package/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
  377. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -0
  378. package/dist/modes/interactive/components/visual-truncate.js +33 -0
  379. package/dist/modes/interactive/components/visual-truncate.js.map +1 -0
  380. package/dist/modes/interactive/interactive-mode.d.ts +313 -0
  381. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -0
  382. package/dist/modes/interactive/interactive-mode.js +3664 -0
  383. package/dist/modes/interactive/interactive-mode.js.map +1 -0
  384. package/dist/modes/interactive/theme/dark.json +85 -0
  385. package/dist/modes/interactive/theme/light.json +84 -0
  386. package/dist/modes/interactive/theme/theme-schema.json +335 -0
  387. package/dist/modes/interactive/theme/theme.d.ts +78 -0
  388. package/dist/modes/interactive/theme/theme.d.ts.map +1 -0
  389. package/dist/modes/interactive/theme/theme.js +944 -0
  390. package/dist/modes/interactive/theme/theme.js.map +1 -0
  391. package/dist/modes/print-mode.d.ts +28 -0
  392. package/dist/modes/print-mode.d.ts.map +1 -0
  393. package/dist/modes/print-mode.js +98 -0
  394. package/dist/modes/print-mode.js.map +1 -0
  395. package/dist/modes/rpc/rpc-client.d.ts +217 -0
  396. package/dist/modes/rpc/rpc-client.d.ts.map +1 -0
  397. package/dist/modes/rpc/rpc-client.js +405 -0
  398. package/dist/modes/rpc/rpc-client.js.map +1 -0
  399. package/dist/modes/rpc/rpc-mode.d.ts +20 -0
  400. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
  401. package/dist/modes/rpc/rpc-mode.js +500 -0
  402. package/dist/modes/rpc/rpc-mode.js.map +1 -0
  403. package/dist/modes/rpc/rpc-types.d.ts +409 -0
  404. package/dist/modes/rpc/rpc-types.d.ts.map +1 -0
  405. package/dist/modes/rpc/rpc-types.js +8 -0
  406. package/dist/modes/rpc/rpc-types.js.map +1 -0
  407. package/dist/utils/changelog.d.ts +21 -0
  408. package/dist/utils/changelog.d.ts.map +1 -0
  409. package/dist/utils/changelog.js +87 -0
  410. package/dist/utils/changelog.js.map +1 -0
  411. package/dist/utils/clipboard-image.d.ts +11 -0
  412. package/dist/utils/clipboard-image.d.ts.map +1 -0
  413. package/dist/utils/clipboard-image.js +162 -0
  414. package/dist/utils/clipboard-image.js.map +1 -0
  415. package/dist/utils/clipboard-native.d.ts +7 -0
  416. package/dist/utils/clipboard-native.d.ts.map +1 -0
  417. package/dist/utils/clipboard-native.js +14 -0
  418. package/dist/utils/clipboard-native.js.map +1 -0
  419. package/dist/utils/clipboard.d.ts +2 -0
  420. package/dist/utils/clipboard.d.ts.map +1 -0
  421. package/dist/utils/clipboard.js +67 -0
  422. package/dist/utils/clipboard.js.map +1 -0
  423. package/dist/utils/frontmatter.d.ts +8 -0
  424. package/dist/utils/frontmatter.d.ts.map +1 -0
  425. package/dist/utils/frontmatter.js +26 -0
  426. package/dist/utils/frontmatter.js.map +1 -0
  427. package/dist/utils/git.d.ts +2 -0
  428. package/dist/utils/git.d.ts.map +1 -0
  429. package/dist/utils/git.js +6 -0
  430. package/dist/utils/git.js.map +1 -0
  431. package/dist/utils/image-convert.d.ts +9 -0
  432. package/dist/utils/image-convert.d.ts.map +1 -0
  433. package/dist/utils/image-convert.js +35 -0
  434. package/dist/utils/image-convert.js.map +1 -0
  435. package/dist/utils/image-resize.d.ts +36 -0
  436. package/dist/utils/image-resize.d.ts.map +1 -0
  437. package/dist/utils/image-resize.js +181 -0
  438. package/dist/utils/image-resize.js.map +1 -0
  439. package/dist/utils/mime.d.ts +2 -0
  440. package/dist/utils/mime.d.ts.map +1 -0
  441. package/dist/utils/mime.js +26 -0
  442. package/dist/utils/mime.js.map +1 -0
  443. package/dist/utils/photon.d.ts +21 -0
  444. package/dist/utils/photon.d.ts.map +1 -0
  445. package/dist/utils/photon.js +121 -0
  446. package/dist/utils/photon.js.map +1 -0
  447. package/dist/utils/shell.d.ts +26 -0
  448. package/dist/utils/shell.d.ts.map +1 -0
  449. package/dist/utils/shell.js +186 -0
  450. package/dist/utils/shell.js.map +1 -0
  451. package/dist/utils/sleep.d.ts +5 -0
  452. package/dist/utils/sleep.d.ts.map +1 -0
  453. package/dist/utils/sleep.js +17 -0
  454. package/dist/utils/sleep.js.map +1 -0
  455. package/dist/utils/tools-manager.d.ts +3 -0
  456. package/dist/utils/tools-manager.d.ts.map +1 -0
  457. package/dist/utils/tools-manager.js +201 -0
  458. package/dist/utils/tools-manager.js.map +1 -0
  459. package/docs/compaction.md +390 -0
  460. package/docs/custom-provider.md +539 -0
  461. package/docs/development.md +69 -0
  462. package/docs/extensions.md +1827 -0
  463. package/docs/images/doom-extension.png +0 -0
  464. package/docs/images/exy.png +0 -0
  465. package/docs/images/interactive-mode.png +0 -0
  466. package/docs/images/tree-view.png +0 -0
  467. package/docs/json.md +79 -0
  468. package/docs/keybindings.md +174 -0
  469. package/docs/models.md +254 -0
  470. package/docs/packages.md +191 -0
  471. package/docs/prompt-templates.md +67 -0
  472. package/docs/providers.md +168 -0
  473. package/docs/rpc.md +1311 -0
  474. package/docs/sdk.md +957 -0
  475. package/docs/session.md +412 -0
  476. package/docs/settings.md +221 -0
  477. package/docs/shell-aliases.md +13 -0
  478. package/docs/skills.md +227 -0
  479. package/docs/terminal-setup.md +70 -0
  480. package/docs/termux.md +127 -0
  481. package/docs/themes.md +295 -0
  482. package/docs/tree.md +219 -0
  483. package/docs/tui.md +887 -0
  484. package/docs/windows.md +17 -0
  485. package/examples/README.md +25 -0
  486. package/examples/extensions/README.md +202 -0
  487. package/examples/extensions/antigravity-image-gen.ts +413 -0
  488. package/examples/extensions/auto-commit-on-exit.ts +49 -0
  489. package/examples/extensions/bash-spawn-hook.ts +30 -0
  490. package/examples/extensions/bookmark.ts +50 -0
  491. package/examples/extensions/claude-rules.ts +86 -0
  492. package/examples/extensions/commands.ts +72 -0
  493. package/examples/extensions/confirm-destructive.ts +59 -0
  494. package/examples/extensions/custom-compaction.ts +114 -0
  495. package/examples/extensions/custom-footer.ts +64 -0
  496. package/examples/extensions/custom-header.ts +73 -0
  497. package/examples/extensions/custom-provider-anthropic/index.ts +604 -0
  498. package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
  499. package/examples/extensions/custom-provider-anthropic/package.json +19 -0
  500. package/examples/extensions/custom-provider-gitlab-duo/index.ts +349 -0
  501. package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
  502. package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
  503. package/examples/extensions/custom-provider-qwen-cli/index.ts +345 -0
  504. package/examples/extensions/custom-provider-qwen-cli/package.json +16 -0
  505. package/examples/extensions/dirty-repo-guard.ts +56 -0
  506. package/examples/extensions/doom-overlay/README.md +46 -0
  507. package/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
  508. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  509. package/examples/extensions/doom-overlay/doom/build.sh +152 -0
  510. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
  511. package/examples/extensions/doom-overlay/doom-component.ts +132 -0
  512. package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
  513. package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
  514. package/examples/extensions/doom-overlay/index.ts +74 -0
  515. package/examples/extensions/doom-overlay/wad-finder.ts +51 -0
  516. package/examples/extensions/dynamic-resources/SKILL.md +8 -0
  517. package/examples/extensions/dynamic-resources/dynamic.json +79 -0
  518. package/examples/extensions/dynamic-resources/dynamic.md +5 -0
  519. package/examples/extensions/dynamic-resources/index.ts +15 -0
  520. package/examples/extensions/event-bus.ts +43 -0
  521. package/examples/extensions/file-trigger.ts +41 -0
  522. package/examples/extensions/git-checkpoint.ts +53 -0
  523. package/examples/extensions/handoff.ts +150 -0
  524. package/examples/extensions/hello.ts +25 -0
  525. package/examples/extensions/inline-bash.ts +94 -0
  526. package/examples/extensions/input-transform.ts +43 -0
  527. package/examples/extensions/interactive-shell.ts +196 -0
  528. package/examples/extensions/mac-system-theme.ts +47 -0
  529. package/examples/extensions/message-renderer.ts +59 -0
  530. package/examples/extensions/minimal-mode.ts +426 -0
  531. package/examples/extensions/modal-editor.ts +85 -0
  532. package/examples/extensions/model-status.ts +31 -0
  533. package/examples/extensions/notify.ts +55 -0
  534. package/examples/extensions/overlay-qa-tests.ts +881 -0
  535. package/examples/extensions/overlay-test.ts +150 -0
  536. package/examples/extensions/permission-gate.ts +34 -0
  537. package/examples/extensions/pirate.ts +47 -0
  538. package/examples/extensions/plan-mode/README.md +65 -0
  539. package/examples/extensions/plan-mode/index.ts +340 -0
  540. package/examples/extensions/plan-mode/utils.ts +168 -0
  541. package/examples/extensions/preset.ts +398 -0
  542. package/examples/extensions/protected-paths.ts +30 -0
  543. package/examples/extensions/qna.ts +119 -0
  544. package/examples/extensions/question.ts +264 -0
  545. package/examples/extensions/questionnaire.ts +427 -0
  546. package/examples/extensions/rainbow-editor.ts +88 -0
  547. package/examples/extensions/rpc-demo.ts +124 -0
  548. package/examples/extensions/sandbox/index.ts +318 -0
  549. package/examples/extensions/sandbox/package-lock.json +92 -0
  550. package/examples/extensions/sandbox/package.json +19 -0
  551. package/examples/extensions/send-user-message.ts +97 -0
  552. package/examples/extensions/session-name.ts +27 -0
  553. package/examples/extensions/shutdown-command.ts +63 -0
  554. package/examples/extensions/snake.ts +343 -0
  555. package/examples/extensions/space-invaders.ts +560 -0
  556. package/examples/extensions/ssh.ts +220 -0
  557. package/examples/extensions/status-line.ts +40 -0
  558. package/examples/extensions/subagent/README.md +172 -0
  559. package/examples/extensions/subagent/agents/planner.md +37 -0
  560. package/examples/extensions/subagent/agents/reviewer.md +35 -0
  561. package/examples/extensions/subagent/agents/scout.md +50 -0
  562. package/examples/extensions/subagent/agents/worker.md +24 -0
  563. package/examples/extensions/subagent/agents.ts +127 -0
  564. package/examples/extensions/subagent/index.ts +963 -0
  565. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
  566. package/examples/extensions/subagent/prompts/implement.md +10 -0
  567. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
  568. package/examples/extensions/summarize.ts +195 -0
  569. package/examples/extensions/system-prompt-header.ts +17 -0
  570. package/examples/extensions/timed-confirm.ts +70 -0
  571. package/examples/extensions/titlebar-spinner.ts +58 -0
  572. package/examples/extensions/todo.ts +299 -0
  573. package/examples/extensions/tool-override.ts +143 -0
  574. package/examples/extensions/tools.ts +146 -0
  575. package/examples/extensions/trigger-compact.ts +40 -0
  576. package/examples/extensions/truncated-tool.ts +192 -0
  577. package/examples/extensions/widget-placement.ts +17 -0
  578. package/examples/extensions/with-deps/index.ts +36 -0
  579. package/examples/extensions/with-deps/package-lock.json +31 -0
  580. package/examples/extensions/with-deps/package.json +22 -0
  581. package/examples/rpc-extension-ui.ts +632 -0
  582. package/examples/sdk/01-minimal.ts +22 -0
  583. package/examples/sdk/02-custom-model.ts +49 -0
  584. package/examples/sdk/03-custom-prompt.ts +55 -0
  585. package/examples/sdk/04-skills.ts +46 -0
  586. package/examples/sdk/05-tools.ts +56 -0
  587. package/examples/sdk/06-extensions.ts +88 -0
  588. package/examples/sdk/07-context-files.ts +40 -0
  589. package/examples/sdk/08-prompt-templates.ts +47 -0
  590. package/examples/sdk/09-api-keys-and-oauth.ts +48 -0
  591. package/examples/sdk/10-settings.ts +38 -0
  592. package/examples/sdk/11-sessions.ts +48 -0
  593. package/examples/sdk/12-full-control.ts +82 -0
  594. package/examples/sdk/README.md +144 -0
  595. package/package.json +97 -0
@@ -0,0 +1,15 @@
1
+ import type { Component } from "@mariozechner/pi-tui";
2
+ /**
3
+ * Dynamic border component that adjusts to viewport width.
4
+ *
5
+ * Note: When used from extensions loaded via jiti, the global `theme` may be undefined
6
+ * because jiti creates a separate module cache. Always pass an explicit color
7
+ * function when using DynamicBorder in components exported for extension use.
8
+ */
9
+ export declare class DynamicBorder implements Component {
10
+ private color;
11
+ constructor(color?: (str: string) => string);
12
+ invalidate(): void;
13
+ render(width: number): string[];
14
+ }
15
+ //# sourceMappingURL=dynamic-border.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-border.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/dynamic-border.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD;;;;;;GAMG;AACH,qBAAa,aAAc,YAAW,SAAS;IAC9C,OAAO,CAAC,KAAK,CAA0B;IAEvC,YAAY,KAAK,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAyC,EAE5E;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9B;CACD","sourcesContent":["import type { Component } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * Dynamic border component that adjusts to viewport width.\n *\n * Note: When used from extensions loaded via jiti, the global `theme` may be undefined\n * because jiti creates a separate module cache. Always pass an explicit color\n * function when using DynamicBorder in components exported for extension use.\n */\nexport class DynamicBorder implements Component {\n\tprivate color: (str: string) => string;\n\n\tconstructor(color: (str: string) => string = (str) => theme.fg(\"border\", str)) {\n\t\tthis.color = color;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(width: number): string[] {\n\t\treturn [this.color(\"─\".repeat(Math.max(1, width)))];\n\t}\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import { theme } from "../theme/theme.js";
2
+ /**
3
+ * Dynamic border component that adjusts to viewport width.
4
+ *
5
+ * Note: When used from extensions loaded via jiti, the global `theme` may be undefined
6
+ * because jiti creates a separate module cache. Always pass an explicit color
7
+ * function when using DynamicBorder in components exported for extension use.
8
+ */
9
+ export class DynamicBorder {
10
+ color;
11
+ constructor(color = (str) => theme.fg("border", str)) {
12
+ this.color = color;
13
+ }
14
+ invalidate() {
15
+ // No cached state to invalidate currently
16
+ }
17
+ render(width) {
18
+ return [this.color("─".repeat(Math.max(1, width)))];
19
+ }
20
+ }
21
+ //# sourceMappingURL=dynamic-border.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-border.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/dynamic-border.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IACjB,KAAK,CAA0B;IAEvC,YAAY,KAAK,GAA4B,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE;QAC9E,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAAA,CACnB;IAED,UAAU,GAAS;QAClB,0CAA0C;IADvB,CAEnB;IAED,MAAM,CAAC,KAAa,EAAY;QAC/B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAAA,CACpD;CACD","sourcesContent":["import type { Component } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * Dynamic border component that adjusts to viewport width.\n *\n * Note: When used from extensions loaded via jiti, the global `theme` may be undefined\n * because jiti creates a separate module cache. Always pass an explicit color\n * function when using DynamicBorder in components exported for extension use.\n */\nexport class DynamicBorder implements Component {\n\tprivate color: (str: string) => string;\n\n\tconstructor(color: (str: string) => string = (str) => theme.fg(\"border\", str)) {\n\t\tthis.color = color;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(width: number): string[] {\n\t\treturn [this.color(\"─\".repeat(Math.max(1, width)))];\n\t}\n}\n"]}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Multi-line editor component for extensions.
3
+ * Supports Ctrl+G for external editor.
4
+ */
5
+ import { Container, type EditorOptions, type TUI } from "@mariozechner/pi-tui";
6
+ import type { KeybindingsManager } from "../../../core/keybindings.js";
7
+ export declare class ExtensionEditorComponent extends Container {
8
+ private editor;
9
+ private onSubmitCallback;
10
+ private onCancelCallback;
11
+ private tui;
12
+ private keybindings;
13
+ constructor(tui: TUI, keybindings: KeybindingsManager, title: string, prefill: string | undefined, onSubmit: (value: string) => void, onCancel: () => void, options?: EditorOptions);
14
+ handleInput(keyData: string): void;
15
+ private openExternalEditor;
16
+ }
17
+ //# sourceMappingURL=extension-editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-editor.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-editor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EACN,SAAS,EAET,KAAK,aAAa,EAIlB,KAAK,GAAG,EACR,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAKvE,qBAAa,wBAAyB,SAAQ,SAAS;IACtD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,WAAW,CAAqB;IAExC,YACC,GAAG,EAAE,GAAG,EACR,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EACjC,QAAQ,EAAE,MAAM,IAAI,EACpB,OAAO,CAAC,EAAE,aAAa,EA6CvB;IAED,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAgBjC;IAED,OAAO,CAAC,kBAAkB;CAiC1B","sourcesContent":["/**\n * Multi-line editor component for extensions.\n * Supports Ctrl+G for external editor.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport {\n\tContainer,\n\tEditor,\n\ttype EditorOptions,\n\tgetEditorKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@mariozechner/pi-tui\";\nimport type { KeybindingsManager } from \"../../../core/keybindings.js\";\nimport { getEditorTheme, theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { appKeyHint, keyHint } from \"./keybinding-hints.js\";\n\nexport class ExtensionEditorComponent extends Container {\n\tprivate editor: Editor;\n\tprivate onSubmitCallback: (value: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate tui: TUI;\n\tprivate keybindings: KeybindingsManager;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tkeybindings: KeybindingsManager,\n\t\ttitle: string,\n\t\tprefill: string | undefined,\n\t\tonSubmit: (value: string) => void,\n\t\tonCancel: () => void,\n\t\toptions?: EditorOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.keybindings = keybindings;\n\t\tthis.onSubmitCallback = onSubmit;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add title\n\t\tthis.addChild(new Text(theme.fg(\"accent\", title), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create editor\n\t\tthis.editor = new Editor(tui, getEditorTheme(), options);\n\t\tif (prefill) {\n\t\t\tthis.editor.setText(prefill);\n\t\t}\n\t\t// Wire up Enter to submit (Shift+Enter for newlines, like the main editor)\n\t\tthis.editor.onSubmit = (text: string) => {\n\t\t\tthis.onSubmitCallback(text);\n\t\t};\n\t\tthis.addChild(this.editor);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add hint\n\t\tconst hasExternalEditor = !!(process.env.VISUAL || process.env.EDITOR);\n\t\tconst hint =\n\t\t\tkeyHint(\"selectConfirm\", \"submit\") +\n\t\t\t\" \" +\n\t\t\tkeyHint(\"newLine\", \"newline\") +\n\t\t\t\" \" +\n\t\t\tkeyHint(\"selectCancel\", \"cancel\") +\n\t\t\t(hasExternalEditor ? ` ${appKeyHint(this.keybindings, \"externalEditor\", \"external editor\")}` : \"\");\n\t\tthis.addChild(new Text(hint, 1, 0));\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\t// Escape or Ctrl+C to cancel\n\t\tif (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t\treturn;\n\t\t}\n\n\t\t// External editor (app keybinding)\n\t\tif (this.keybindings.matches(keyData, \"externalEditor\")) {\n\t\t\tthis.openExternalEditor();\n\t\t\treturn;\n\t\t}\n\n\t\t// Forward to editor\n\t\tthis.editor.handleInput(keyData);\n\t}\n\n\tprivate openExternalEditor(): void {\n\t\tconst editorCmd = process.env.VISUAL || process.env.EDITOR;\n\t\tif (!editorCmd) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst currentText = this.editor.getText();\n\t\tconst tmpFile = path.join(os.tmpdir(), `pi-extension-editor-${Date.now()}.md`);\n\n\t\ttry {\n\t\t\tfs.writeFileSync(tmpFile, currentText, \"utf-8\");\n\t\t\tthis.tui.stop();\n\n\t\t\tconst [editor, ...editorArgs] = editorCmd.split(\" \");\n\t\t\tconst result = spawnSync(editor, [...editorArgs, tmpFile], {\n\t\t\t\tstdio: \"inherit\",\n\t\t\t});\n\n\t\t\tif (result.status === 0) {\n\t\t\t\tconst newContent = fs.readFileSync(tmpFile, \"utf-8\").replace(/\\n$/, \"\");\n\t\t\t\tthis.editor.setText(newContent);\n\t\t\t}\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tfs.unlinkSync(tmpFile);\n\t\t\t} catch {\n\t\t\t\t// Ignore cleanup errors\n\t\t\t}\n\t\t\tthis.tui.start();\n\t\t\t// Force full re-render since external editor uses alternate screen\n\t\t\tthis.tui.requestRender(true);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Multi-line editor component for extensions.
3
+ * Supports Ctrl+G for external editor.
4
+ */
5
+ import { spawnSync } from "node:child_process";
6
+ import * as fs from "node:fs";
7
+ import * as os from "node:os";
8
+ import * as path from "node:path";
9
+ import { Container, Editor, getEditorKeybindings, Spacer, Text, } from "@mariozechner/pi-tui";
10
+ import { getEditorTheme, theme } from "../theme/theme.js";
11
+ import { DynamicBorder } from "./dynamic-border.js";
12
+ import { appKeyHint, keyHint } from "./keybinding-hints.js";
13
+ export class ExtensionEditorComponent extends Container {
14
+ editor;
15
+ onSubmitCallback;
16
+ onCancelCallback;
17
+ tui;
18
+ keybindings;
19
+ constructor(tui, keybindings, title, prefill, onSubmit, onCancel, options) {
20
+ super();
21
+ this.tui = tui;
22
+ this.keybindings = keybindings;
23
+ this.onSubmitCallback = onSubmit;
24
+ this.onCancelCallback = onCancel;
25
+ // Add top border
26
+ this.addChild(new DynamicBorder());
27
+ this.addChild(new Spacer(1));
28
+ // Add title
29
+ this.addChild(new Text(theme.fg("accent", title), 1, 0));
30
+ this.addChild(new Spacer(1));
31
+ // Create editor
32
+ this.editor = new Editor(tui, getEditorTheme(), options);
33
+ if (prefill) {
34
+ this.editor.setText(prefill);
35
+ }
36
+ // Wire up Enter to submit (Shift+Enter for newlines, like the main editor)
37
+ this.editor.onSubmit = (text) => {
38
+ this.onSubmitCallback(text);
39
+ };
40
+ this.addChild(this.editor);
41
+ this.addChild(new Spacer(1));
42
+ // Add hint
43
+ const hasExternalEditor = !!(process.env.VISUAL || process.env.EDITOR);
44
+ const hint = keyHint("selectConfirm", "submit") +
45
+ " " +
46
+ keyHint("newLine", "newline") +
47
+ " " +
48
+ keyHint("selectCancel", "cancel") +
49
+ (hasExternalEditor ? ` ${appKeyHint(this.keybindings, "externalEditor", "external editor")}` : "");
50
+ this.addChild(new Text(hint, 1, 0));
51
+ this.addChild(new Spacer(1));
52
+ // Add bottom border
53
+ this.addChild(new DynamicBorder());
54
+ }
55
+ handleInput(keyData) {
56
+ const kb = getEditorKeybindings();
57
+ // Escape or Ctrl+C to cancel
58
+ if (kb.matches(keyData, "selectCancel")) {
59
+ this.onCancelCallback();
60
+ return;
61
+ }
62
+ // External editor (app keybinding)
63
+ if (this.keybindings.matches(keyData, "externalEditor")) {
64
+ this.openExternalEditor();
65
+ return;
66
+ }
67
+ // Forward to editor
68
+ this.editor.handleInput(keyData);
69
+ }
70
+ openExternalEditor() {
71
+ const editorCmd = process.env.VISUAL || process.env.EDITOR;
72
+ if (!editorCmd) {
73
+ return;
74
+ }
75
+ const currentText = this.editor.getText();
76
+ const tmpFile = path.join(os.tmpdir(), `pi-extension-editor-${Date.now()}.md`);
77
+ try {
78
+ fs.writeFileSync(tmpFile, currentText, "utf-8");
79
+ this.tui.stop();
80
+ const [editor, ...editorArgs] = editorCmd.split(" ");
81
+ const result = spawnSync(editor, [...editorArgs, tmpFile], {
82
+ stdio: "inherit",
83
+ });
84
+ if (result.status === 0) {
85
+ const newContent = fs.readFileSync(tmpFile, "utf-8").replace(/\n$/, "");
86
+ this.editor.setText(newContent);
87
+ }
88
+ }
89
+ finally {
90
+ try {
91
+ fs.unlinkSync(tmpFile);
92
+ }
93
+ catch {
94
+ // Ignore cleanup errors
95
+ }
96
+ this.tui.start();
97
+ // Force full re-render since external editor uses alternate screen
98
+ this.tui.requestRender(true);
99
+ }
100
+ }
101
+ }
102
+ //# sourceMappingURL=extension-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-editor.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-editor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Multi-line editor component for extensions.\n * Supports Ctrl+G for external editor.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport {\n\tContainer,\n\tEditor,\n\ttype EditorOptions,\n\tgetEditorKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@mariozechner/pi-tui\";\nimport type { KeybindingsManager } from \"../../../core/keybindings.js\";\nimport { getEditorTheme, theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { appKeyHint, keyHint } from \"./keybinding-hints.js\";\n\nexport class ExtensionEditorComponent extends Container {\n\tprivate editor: Editor;\n\tprivate onSubmitCallback: (value: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate tui: TUI;\n\tprivate keybindings: KeybindingsManager;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tkeybindings: KeybindingsManager,\n\t\ttitle: string,\n\t\tprefill: string | undefined,\n\t\tonSubmit: (value: string) => void,\n\t\tonCancel: () => void,\n\t\toptions?: EditorOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.keybindings = keybindings;\n\t\tthis.onSubmitCallback = onSubmit;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add title\n\t\tthis.addChild(new Text(theme.fg(\"accent\", title), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create editor\n\t\tthis.editor = new Editor(tui, getEditorTheme(), options);\n\t\tif (prefill) {\n\t\t\tthis.editor.setText(prefill);\n\t\t}\n\t\t// Wire up Enter to submit (Shift+Enter for newlines, like the main editor)\n\t\tthis.editor.onSubmit = (text: string) => {\n\t\t\tthis.onSubmitCallback(text);\n\t\t};\n\t\tthis.addChild(this.editor);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add hint\n\t\tconst hasExternalEditor = !!(process.env.VISUAL || process.env.EDITOR);\n\t\tconst hint =\n\t\t\tkeyHint(\"selectConfirm\", \"submit\") +\n\t\t\t\" \" +\n\t\t\tkeyHint(\"newLine\", \"newline\") +\n\t\t\t\" \" +\n\t\t\tkeyHint(\"selectCancel\", \"cancel\") +\n\t\t\t(hasExternalEditor ? ` ${appKeyHint(this.keybindings, \"externalEditor\", \"external editor\")}` : \"\");\n\t\tthis.addChild(new Text(hint, 1, 0));\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\t// Escape or Ctrl+C to cancel\n\t\tif (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t\treturn;\n\t\t}\n\n\t\t// External editor (app keybinding)\n\t\tif (this.keybindings.matches(keyData, \"externalEditor\")) {\n\t\t\tthis.openExternalEditor();\n\t\t\treturn;\n\t\t}\n\n\t\t// Forward to editor\n\t\tthis.editor.handleInput(keyData);\n\t}\n\n\tprivate openExternalEditor(): void {\n\t\tconst editorCmd = process.env.VISUAL || process.env.EDITOR;\n\t\tif (!editorCmd) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst currentText = this.editor.getText();\n\t\tconst tmpFile = path.join(os.tmpdir(), `pi-extension-editor-${Date.now()}.md`);\n\n\t\ttry {\n\t\t\tfs.writeFileSync(tmpFile, currentText, \"utf-8\");\n\t\t\tthis.tui.stop();\n\n\t\t\tconst [editor, ...editorArgs] = editorCmd.split(\" \");\n\t\t\tconst result = spawnSync(editor, [...editorArgs, tmpFile], {\n\t\t\t\tstdio: \"inherit\",\n\t\t\t});\n\n\t\t\tif (result.status === 0) {\n\t\t\t\tconst newContent = fs.readFileSync(tmpFile, \"utf-8\").replace(/\\n$/, \"\");\n\t\t\t\tthis.editor.setText(newContent);\n\t\t\t}\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tfs.unlinkSync(tmpFile);\n\t\t\t} catch {\n\t\t\t\t// Ignore cleanup errors\n\t\t\t}\n\t\t\tthis.tui.start();\n\t\t\t// Force full re-render since external editor uses alternate screen\n\t\t\tthis.tui.requestRender(true);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Simple text input component for extensions.
3
+ */
4
+ import { Container, type Focusable, type TUI } from "@mariozechner/pi-tui";
5
+ export interface ExtensionInputOptions {
6
+ tui?: TUI;
7
+ timeout?: number;
8
+ }
9
+ export declare class ExtensionInputComponent extends Container implements Focusable {
10
+ private input;
11
+ private onSubmitCallback;
12
+ private onCancelCallback;
13
+ private titleText;
14
+ private baseTitle;
15
+ private countdown;
16
+ private _focused;
17
+ get focused(): boolean;
18
+ set focused(value: boolean);
19
+ constructor(title: string, _placeholder: string | undefined, onSubmit: (value: string) => void, onCancel: () => void, opts?: ExtensionInputOptions);
20
+ handleInput(keyData: string): void;
21
+ dispose(): void;
22
+ }
23
+ //# sourceMappingURL=extension-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-input.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-input.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAA6C,KAAK,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAMtH,MAAM,WAAW,qBAAqB;IACrC,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,uBAAwB,SAAQ,SAAU,YAAW,SAAS;IAC1E,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAA6B;IAG9C,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,YACC,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EACjC,QAAQ,EAAE,MAAM,IAAI,EACpB,IAAI,CAAC,EAAE,qBAAqB,EA8B5B;IAED,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CASjC;IAED,OAAO,IAAI,IAAI,CAEd;CACD","sourcesContent":["/**\n * Simple text input component for extensions.\n */\n\nimport { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { CountdownTimer } from \"./countdown-timer.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\nexport interface ExtensionInputOptions {\n\ttui?: TUI;\n\ttimeout?: number;\n}\n\nexport class ExtensionInputComponent extends Container implements Focusable {\n\tprivate input: Input;\n\tprivate onSubmitCallback: (value: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate titleText: Text;\n\tprivate baseTitle: string;\n\tprivate countdown: CountdownTimer | undefined;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttitle: string,\n\t\t_placeholder: string | undefined,\n\t\tonSubmit: (value: string) => void,\n\t\tonCancel: () => void,\n\t\topts?: ExtensionInputOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.onSubmitCallback = onSubmit;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.baseTitle = title;\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.titleText = new Text(theme.fg(\"accent\", title), 1, 0);\n\t\tthis.addChild(this.titleText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (opts?.timeout && opts.timeout > 0 && opts.tui) {\n\t\t\tthis.countdown = new CountdownTimer(\n\t\t\t\topts.timeout,\n\t\t\t\topts.tui,\n\t\t\t\t(s) => this.titleText.setText(theme.fg(\"accent\", `${this.baseTitle} (${s}s)`)),\n\t\t\t\t() => this.onCancelCallback(),\n\t\t\t);\n\t\t}\n\n\t\tthis.input = new Input();\n\t\tthis.addChild(this.input);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(`${keyHint(\"selectConfirm\", \"submit\")} ${keyHint(\"selectCancel\", \"cancel\")}`, 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"selectConfirm\") || keyData === \"\\n\") {\n\t\t\tthis.onSubmitCallback(this.input.getValue());\n\t\t} else if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t} else {\n\t\t\tthis.input.handleInput(keyData);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.countdown?.dispose();\n\t}\n}\n"]}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Simple text input component for extensions.
3
+ */
4
+ import { Container, getEditorKeybindings, Input, Spacer, Text } from "@mariozechner/pi-tui";
5
+ import { theme } from "../theme/theme.js";
6
+ import { CountdownTimer } from "./countdown-timer.js";
7
+ import { DynamicBorder } from "./dynamic-border.js";
8
+ import { keyHint } from "./keybinding-hints.js";
9
+ export class ExtensionInputComponent extends Container {
10
+ input;
11
+ onSubmitCallback;
12
+ onCancelCallback;
13
+ titleText;
14
+ baseTitle;
15
+ countdown;
16
+ // Focusable implementation - propagate to input for IME cursor positioning
17
+ _focused = false;
18
+ get focused() {
19
+ return this._focused;
20
+ }
21
+ set focused(value) {
22
+ this._focused = value;
23
+ this.input.focused = value;
24
+ }
25
+ constructor(title, _placeholder, onSubmit, onCancel, opts) {
26
+ super();
27
+ this.onSubmitCallback = onSubmit;
28
+ this.onCancelCallback = onCancel;
29
+ this.baseTitle = title;
30
+ this.addChild(new DynamicBorder());
31
+ this.addChild(new Spacer(1));
32
+ this.titleText = new Text(theme.fg("accent", title), 1, 0);
33
+ this.addChild(this.titleText);
34
+ this.addChild(new Spacer(1));
35
+ if (opts?.timeout && opts.timeout > 0 && opts.tui) {
36
+ this.countdown = new CountdownTimer(opts.timeout, opts.tui, (s) => this.titleText.setText(theme.fg("accent", `${this.baseTitle} (${s}s)`)), () => this.onCancelCallback());
37
+ }
38
+ this.input = new Input();
39
+ this.addChild(this.input);
40
+ this.addChild(new Spacer(1));
41
+ this.addChild(new Text(`${keyHint("selectConfirm", "submit")} ${keyHint("selectCancel", "cancel")}`, 1, 0));
42
+ this.addChild(new Spacer(1));
43
+ this.addChild(new DynamicBorder());
44
+ }
45
+ handleInput(keyData) {
46
+ const kb = getEditorKeybindings();
47
+ if (kb.matches(keyData, "selectConfirm") || keyData === "\n") {
48
+ this.onSubmitCallback(this.input.getValue());
49
+ }
50
+ else if (kb.matches(keyData, "selectCancel")) {
51
+ this.onCancelCallback();
52
+ }
53
+ else {
54
+ this.input.handleInput(keyData);
55
+ }
56
+ }
57
+ dispose() {
58
+ this.countdown?.dispose();
59
+ }
60
+ }
61
+ //# sourceMappingURL=extension-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-input.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-input.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Simple text input component for extensions.\n */\n\nimport { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { CountdownTimer } from \"./countdown-timer.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\nexport interface ExtensionInputOptions {\n\ttui?: TUI;\n\ttimeout?: number;\n}\n\nexport class ExtensionInputComponent extends Container implements Focusable {\n\tprivate input: Input;\n\tprivate onSubmitCallback: (value: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate titleText: Text;\n\tprivate baseTitle: string;\n\tprivate countdown: CountdownTimer | undefined;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttitle: string,\n\t\t_placeholder: string | undefined,\n\t\tonSubmit: (value: string) => void,\n\t\tonCancel: () => void,\n\t\topts?: ExtensionInputOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.onSubmitCallback = onSubmit;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.baseTitle = title;\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.titleText = new Text(theme.fg(\"accent\", title), 1, 0);\n\t\tthis.addChild(this.titleText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (opts?.timeout && opts.timeout > 0 && opts.tui) {\n\t\t\tthis.countdown = new CountdownTimer(\n\t\t\t\topts.timeout,\n\t\t\t\topts.tui,\n\t\t\t\t(s) => this.titleText.setText(theme.fg(\"accent\", `${this.baseTitle} (${s}s)`)),\n\t\t\t\t() => this.onCancelCallback(),\n\t\t\t);\n\t\t}\n\n\t\tthis.input = new Input();\n\t\tthis.addChild(this.input);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(`${keyHint(\"selectConfirm\", \"submit\")} ${keyHint(\"selectCancel\", \"cancel\")}`, 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"selectConfirm\") || keyData === \"\\n\") {\n\t\t\tthis.onSubmitCallback(this.input.getValue());\n\t\t} else if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t} else {\n\t\t\tthis.input.handleInput(keyData);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.countdown?.dispose();\n\t}\n}\n"]}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Generic selector component for extensions.
3
+ * Displays a list of string options with keyboard navigation.
4
+ */
5
+ import { Container, type TUI } from "@mariozechner/pi-tui";
6
+ export interface ExtensionSelectorOptions {
7
+ tui?: TUI;
8
+ timeout?: number;
9
+ }
10
+ export declare class ExtensionSelectorComponent extends Container {
11
+ private options;
12
+ private selectedIndex;
13
+ private listContainer;
14
+ private onSelectCallback;
15
+ private onCancelCallback;
16
+ private titleText;
17
+ private baseTitle;
18
+ private countdown;
19
+ constructor(title: string, options: string[], onSelect: (option: string) => void, onCancel: () => void, opts?: ExtensionSelectorOptions);
20
+ private updateList;
21
+ handleInput(keyData: string): void;
22
+ dispose(): void;
23
+ }
24
+ //# sourceMappingURL=extension-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-selector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAsC,KAAK,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAM/F,MAAM,WAAW,wBAAwB;IACxC,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,0BAA2B,SAAQ,SAAS;IACxD,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAA6B;IAE9C,YACC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAClC,QAAQ,EAAE,MAAM,IAAI,EACpB,IAAI,CAAC,EAAE,wBAAwB,EA2C/B;IAED,OAAO,CAAC,UAAU;IAWlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAcjC;IAED,OAAO,IAAI,IAAI,CAEd;CACD","sourcesContent":["/**\n * Generic selector component for extensions.\n * Displays a list of string options with keyboard navigation.\n */\n\nimport { Container, getEditorKeybindings, Spacer, Text, type TUI } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { CountdownTimer } from \"./countdown-timer.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.js\";\n\nexport interface ExtensionSelectorOptions {\n\ttui?: TUI;\n\ttimeout?: number;\n}\n\nexport class ExtensionSelectorComponent extends Container {\n\tprivate options: string[];\n\tprivate selectedIndex = 0;\n\tprivate listContainer: Container;\n\tprivate onSelectCallback: (option: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate titleText: Text;\n\tprivate baseTitle: string;\n\tprivate countdown: CountdownTimer | undefined;\n\n\tconstructor(\n\t\ttitle: string,\n\t\toptions: string[],\n\t\tonSelect: (option: string) => void,\n\t\tonCancel: () => void,\n\t\topts?: ExtensionSelectorOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.options = options;\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.baseTitle = title;\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.titleText = new Text(theme.fg(\"accent\", title), 1, 0);\n\t\tthis.addChild(this.titleText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (opts?.timeout && opts.timeout > 0 && opts.tui) {\n\t\t\tthis.countdown = new CountdownTimer(\n\t\t\t\topts.timeout,\n\t\t\t\topts.tui,\n\t\t\t\t(s) => this.titleText.setText(theme.fg(\"accent\", `${this.baseTitle} (${s}s)`)),\n\t\t\t\t() => this.onCancelCallback(),\n\t\t\t);\n\t\t}\n\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"selectConfirm\", \"select\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"selectCancel\", \"cancel\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\t\tfor (let i = 0; i < this.options.length; i++) {\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst text = isSelected\n\t\t\t\t? theme.fg(\"accent\", \"→ \") + theme.fg(\"accent\", this.options[i])\n\t\t\t\t: ` ${theme.fg(\"text\", this.options[i])}`;\n\t\t\tthis.listContainer.addChild(new Text(text, 1, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"selectUp\") || keyData === \"k\") {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"selectDown\") || keyData === \"j\") {\n\t\t\tthis.selectedIndex = Math.min(this.options.length - 1, this.selectedIndex + 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"selectConfirm\") || keyData === \"\\n\") {\n\t\t\tconst selected = this.options[this.selectedIndex];\n\t\t\tif (selected) this.onSelectCallback(selected);\n\t\t} else if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.countdown?.dispose();\n\t}\n}\n"]}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Generic selector component for extensions.
3
+ * Displays a list of string options with keyboard navigation.
4
+ */
5
+ import { Container, getEditorKeybindings, Spacer, Text } from "@mariozechner/pi-tui";
6
+ import { theme } from "../theme/theme.js";
7
+ import { CountdownTimer } from "./countdown-timer.js";
8
+ import { DynamicBorder } from "./dynamic-border.js";
9
+ import { keyHint, rawKeyHint } from "./keybinding-hints.js";
10
+ export class ExtensionSelectorComponent extends Container {
11
+ options;
12
+ selectedIndex = 0;
13
+ listContainer;
14
+ onSelectCallback;
15
+ onCancelCallback;
16
+ titleText;
17
+ baseTitle;
18
+ countdown;
19
+ constructor(title, options, onSelect, onCancel, opts) {
20
+ super();
21
+ this.options = options;
22
+ this.onSelectCallback = onSelect;
23
+ this.onCancelCallback = onCancel;
24
+ this.baseTitle = title;
25
+ this.addChild(new DynamicBorder());
26
+ this.addChild(new Spacer(1));
27
+ this.titleText = new Text(theme.fg("accent", title), 1, 0);
28
+ this.addChild(this.titleText);
29
+ this.addChild(new Spacer(1));
30
+ if (opts?.timeout && opts.timeout > 0 && opts.tui) {
31
+ this.countdown = new CountdownTimer(opts.timeout, opts.tui, (s) => this.titleText.setText(theme.fg("accent", `${this.baseTitle} (${s}s)`)), () => this.onCancelCallback());
32
+ }
33
+ this.listContainer = new Container();
34
+ this.addChild(this.listContainer);
35
+ this.addChild(new Spacer(1));
36
+ this.addChild(new Text(rawKeyHint("↑↓", "navigate") +
37
+ " " +
38
+ keyHint("selectConfirm", "select") +
39
+ " " +
40
+ keyHint("selectCancel", "cancel"), 1, 0));
41
+ this.addChild(new Spacer(1));
42
+ this.addChild(new DynamicBorder());
43
+ this.updateList();
44
+ }
45
+ updateList() {
46
+ this.listContainer.clear();
47
+ for (let i = 0; i < this.options.length; i++) {
48
+ const isSelected = i === this.selectedIndex;
49
+ const text = isSelected
50
+ ? theme.fg("accent", "→ ") + theme.fg("accent", this.options[i])
51
+ : ` ${theme.fg("text", this.options[i])}`;
52
+ this.listContainer.addChild(new Text(text, 1, 0));
53
+ }
54
+ }
55
+ handleInput(keyData) {
56
+ const kb = getEditorKeybindings();
57
+ if (kb.matches(keyData, "selectUp") || keyData === "k") {
58
+ this.selectedIndex = Math.max(0, this.selectedIndex - 1);
59
+ this.updateList();
60
+ }
61
+ else if (kb.matches(keyData, "selectDown") || keyData === "j") {
62
+ this.selectedIndex = Math.min(this.options.length - 1, this.selectedIndex + 1);
63
+ this.updateList();
64
+ }
65
+ else if (kb.matches(keyData, "selectConfirm") || keyData === "\n") {
66
+ const selected = this.options[this.selectedIndex];
67
+ if (selected)
68
+ this.onSelectCallback(selected);
69
+ }
70
+ else if (kb.matches(keyData, "selectCancel")) {
71
+ this.onCancelCallback();
72
+ }
73
+ }
74
+ dispose() {
75
+ this.countdown?.dispose();
76
+ }
77
+ }
78
+ //# sourceMappingURL=extension-selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-selector.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Generic selector component for extensions.\n * Displays a list of string options with keyboard navigation.\n */\n\nimport { Container, getEditorKeybindings, Spacer, Text, type TUI } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { CountdownTimer } from \"./countdown-timer.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.js\";\n\nexport interface ExtensionSelectorOptions {\n\ttui?: TUI;\n\ttimeout?: number;\n}\n\nexport class ExtensionSelectorComponent extends Container {\n\tprivate options: string[];\n\tprivate selectedIndex = 0;\n\tprivate listContainer: Container;\n\tprivate onSelectCallback: (option: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate titleText: Text;\n\tprivate baseTitle: string;\n\tprivate countdown: CountdownTimer | undefined;\n\n\tconstructor(\n\t\ttitle: string,\n\t\toptions: string[],\n\t\tonSelect: (option: string) => void,\n\t\tonCancel: () => void,\n\t\topts?: ExtensionSelectorOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.options = options;\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.baseTitle = title;\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.titleText = new Text(theme.fg(\"accent\", title), 1, 0);\n\t\tthis.addChild(this.titleText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (opts?.timeout && opts.timeout > 0 && opts.tui) {\n\t\t\tthis.countdown = new CountdownTimer(\n\t\t\t\topts.timeout,\n\t\t\t\topts.tui,\n\t\t\t\t(s) => this.titleText.setText(theme.fg(\"accent\", `${this.baseTitle} (${s}s)`)),\n\t\t\t\t() => this.onCancelCallback(),\n\t\t\t);\n\t\t}\n\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"selectConfirm\", \"select\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"selectCancel\", \"cancel\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\t\tfor (let i = 0; i < this.options.length; i++) {\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst text = isSelected\n\t\t\t\t? theme.fg(\"accent\", \"→ \") + theme.fg(\"accent\", this.options[i])\n\t\t\t\t: ` ${theme.fg(\"text\", this.options[i])}`;\n\t\t\tthis.listContainer.addChild(new Text(text, 1, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"selectUp\") || keyData === \"k\") {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"selectDown\") || keyData === \"j\") {\n\t\t\tthis.selectedIndex = Math.min(this.options.length - 1, this.selectedIndex + 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"selectConfirm\") || keyData === \"\\n\") {\n\t\t\tconst selected = this.options[this.selectedIndex];\n\t\t\tif (selected) this.onSelectCallback(selected);\n\t\t} else if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.countdown?.dispose();\n\t}\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import { type Component } from "@mariozechner/pi-tui";
2
+ import type { AgentSession } from "../../../core/agent-session.js";
3
+ import type { ReadonlyFooterDataProvider } from "../../../core/footer-data-provider.js";
4
+ /**
5
+ * Footer component that shows pwd, token stats, and context usage.
6
+ * Computes token/context stats from session, gets git branch and extension statuses from provider.
7
+ */
8
+ export declare class FooterComponent implements Component {
9
+ private session;
10
+ private footerData;
11
+ private autoCompactEnabled;
12
+ constructor(session: AgentSession, footerData: ReadonlyFooterDataProvider);
13
+ setAutoCompactEnabled(enabled: boolean): void;
14
+ /**
15
+ * No-op: git branch caching now handled by provider.
16
+ * Kept for compatibility with existing call sites in interactive-mode.
17
+ */
18
+ invalidate(): void;
19
+ /**
20
+ * Clean up resources.
21
+ * Git watcher cleanup now handled by provider.
22
+ */
23
+ dispose(): void;
24
+ render(width: number): string[];
25
+ }
26
+ //# sourceMappingURL=footer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,sBAAsB,CAAC;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AA0BxF;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAI/C,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,UAAU;IAJnB,OAAO,CAAC,kBAAkB,CAAQ;IAElC,YACS,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,0BAA0B,EAC3C;IAEJ,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE5C;IAED;;;OAGG;IACH,UAAU,IAAI,IAAI,CAEjB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI,CAEd;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAgL9B;CACD","sourcesContent":["import type { AssistantMessage } from \"@mariozechner/pi-ai\";\nimport { type Component, truncateToWidth, visibleWidth } from \"@mariozechner/pi-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.js\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.js\";\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * Sanitize text for display in a single-line status.\n * Removes newlines, tabs, carriage returns, and other control characters.\n */\nfunction sanitizeStatusText(text: string): string {\n\t// Replace newlines, tabs, carriage returns with space, then collapse multiple spaces\n\treturn text\n\t\t.replace(/[\\r\\n\\t]/g, \" \")\n\t\t.replace(/ +/g, \" \")\n\t\t.trim();\n}\n\n/**\n * Format token counts (similar to web-ui)\n */\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage.\n * Computes token/context stats from session, gets git branch and extension statuses from provider.\n */\nexport class FooterComponent implements Component {\n\tprivate autoCompactEnabled = true;\n\n\tconstructor(\n\t\tprivate session: AgentSession,\n\t\tprivate footerData: ReadonlyFooterDataProvider,\n\t) {}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\trender(width: number): string[] {\n\t\tconst state = this.session.state;\n\n\t\t// Calculate cumulative usage from ALL session entries (not just post-compaction messages)\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const entry of this.session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\ttotalInput += entry.message.usage.input;\n\t\t\t\ttotalOutput += entry.message.usage.output;\n\t\t\t\ttotalCacheRead += entry.message.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\t\ttotalCost += entry.message.usage.cost.total;\n\t\t\t}\n\t\t}\n\n\t\t// Get last assistant message for context percentage calculation (skip aborted messages)\n\t\tconst lastAssistantMessage = state.messages\n\t\t\t.slice()\n\t\t\t.reverse()\n\t\t\t.find((m) => m.role === \"assistant\" && m.stopReason !== \"aborted\") as AssistantMessage | undefined;\n\n\t\t// Calculate context percentage from last message (input + output + cacheRead + cacheWrite)\n\t\tconst contextTokens = lastAssistantMessage\n\t\t\t? lastAssistantMessage.usage.input +\n\t\t\t\tlastAssistantMessage.usage.output +\n\t\t\t\tlastAssistantMessage.usage.cacheRead +\n\t\t\t\tlastAssistantMessage.usage.cacheWrite\n\t\t\t: 0;\n\t\tconst contextWindow = state.model?.contextWindow || 0;\n\t\tconst contextPercentValue = contextWindow > 0 ? (contextTokens / contextWindow) * 100 : 0;\n\t\tconst contextPercent = contextPercentValue.toFixed(1);\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = process.cwd();\n\t\tconst home = process.env.HOME || process.env.USERPROFILE;\n\t\tif (home && pwd.startsWith(home)) {\n\t\t\tpwd = `~${pwd.slice(home.length)}`;\n\t\t}\n\n\t\t// Add git branch if available\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Add session name if set\n\t\tconst sessionName = this.session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tpwd = `${pwd} • ${sessionName}`;\n\t\t}\n\n\t\t// Truncate path if too long to fit width\n\t\tif (pwd.length > width) {\n\t\t\tconst half = Math.floor(width / 2) - 2;\n\t\t\tif (half > 1) {\n\t\t\t\tconst start = pwd.slice(0, half);\n\t\t\t\tconst end = pwd.slice(-(half - 1));\n\t\t\t\tpwd = `${start}...${end}`;\n\t\t\t} else {\n\t\t\t\tpwd = pwd.slice(0, Math.max(1, width));\n\t\t\t}\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);\n\t\tif (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);\n\t\tif (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);\n\t\tif (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);\n\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay = `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = state.model?.id || \"no-model\";\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\t// Truncate statsLeft to fit width (no room for right side)\n\t\t\tconst plainStatsLeft = statsLeft.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n\t\t\tstatsLeft = `${plainStatsLeft.substring(0, width - 3)}...`;\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\n\t\t// Add thinking level indicator if model supports reasoning\n\t\tlet rightSideWithoutProvider = modelName;\n\t\tif (state.model?.reasoning) {\n\t\t\tconst thinkingLevel = state.thinkingLevel || \"off\";\n\t\t\trightSideWithoutProvider =\n\t\t\t\tthinkingLevel === \"off\" ? `${modelName} • thinking off` : `${modelName} • ${thinkingLevel}`;\n\t\t}\n\n\t\t// Prepend the provider in parentheses if there are multiple providers and there's enough room\n\t\tlet rightSide = rightSideWithoutProvider;\n\t\tif (this.footerData.getAvailableProviderCount() > 1 && state.model) {\n\t\t\trightSide = `(${state.model!.provider}) ${rightSideWithoutProvider}`;\n\t\t\tif (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) {\n\t\t\t\t// Too wide, fall back\n\t\t\t\trightSide = rightSideWithoutProvider;\n\t\t\t}\n\t\t}\n\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 3) {\n\t\t\t\t// Truncate to fit (strip ANSI codes for length calculation, then truncate raw string)\n\t\t\t\tconst plainRightSide = rightSide.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n\t\t\t\tconst truncatedPlain = plainRightSide.substring(0, availableForRight);\n\t\t\t\t// For simplicity, just use plain truncated version (loses color, but fits)\n\t\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - truncatedPlain.length);\n\t\t\t\tstatsLine = statsLeft + padding + truncatedPlain;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Apply dim to each part separately. statsLeft may contain color codes (for context %)\n\t\t// that end with a reset, which would clear an outer dim wrapper. So we dim the parts\n\t\t// before and after the colored section independently.\n\t\tconst dimStatsLeft = theme.fg(\"dim\", statsLeft);\n\t\tconst remainder = statsLine.slice(statsLeft.length); // padding + rightSide\n\t\tconst dimRemainder = theme.fg(\"dim\", remainder);\n\n\t\tconst lines = [theme.fg(\"dim\", pwd), dimStatsLeft + dimRemainder];\n\n\t\t// Add extension statuses on a single line, sorted by key alphabetically\n\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\tif (extensionStatuses.size > 0) {\n\t\t\tconst sortedStatuses = Array.from(extensionStatuses.entries())\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([, text]) => sanitizeStatusText(text));\n\t\t\tconst statusLine = sortedStatuses.join(\" \");\n\t\t\t// Truncate to terminal width with dim ellipsis for consistency with footer style\n\t\t\tlines.push(truncateToWidth(statusLine, width, theme.fg(\"dim\", \"...\")));\n\t\t}\n\n\t\treturn lines;\n\t}\n}\n"]}