@sammorrowdrums/mcpi 0.70.8 → 0.85.0-bootstrap.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (888) hide show
  1. package/CHANGELOG.md +1621 -0
  2. package/LICENSE +21 -0
  3. package/README.md +196 -141
  4. package/dist/bun/cli.d.ts.map +1 -1
  5. package/dist/bun/cli.js +4 -0
  6. package/dist/bun/cli.js.map +1 -1
  7. package/dist/bun/register-bedrock.d.ts.map +1 -1
  8. package/dist/bun/register-bedrock.js +1 -1
  9. package/dist/bun/register-bedrock.js.map +1 -1
  10. package/dist/bun/restore-sandbox-env.d.ts +17 -0
  11. package/dist/bun/restore-sandbox-env.d.ts.map +1 -0
  12. package/dist/bun/restore-sandbox-env.js +36 -0
  13. package/dist/bun/restore-sandbox-env.js.map +1 -0
  14. package/dist/cli/args.d.ts +9 -2
  15. package/dist/cli/args.d.ts.map +1 -1
  16. package/dist/cli/args.js +116 -16
  17. package/dist/cli/args.js.map +1 -1
  18. package/dist/cli/auth-check.d.ts +19 -0
  19. package/dist/cli/auth-check.d.ts.map +1 -0
  20. package/dist/cli/auth-check.js +50 -0
  21. package/dist/cli/auth-check.js.map +1 -0
  22. package/dist/cli/auth-command.d.ts +24 -0
  23. package/dist/cli/auth-command.d.ts.map +1 -0
  24. package/dist/cli/auth-command.js +103 -0
  25. package/dist/cli/auth-command.js.map +1 -0
  26. package/dist/cli/config-selector.d.ts +6 -4
  27. package/dist/cli/config-selector.d.ts.map +1 -1
  28. package/dist/cli/config-selector.js +4 -4
  29. package/dist/cli/config-selector.js.map +1 -1
  30. package/dist/cli/credential-print.d.ts +13 -0
  31. package/dist/cli/credential-print.d.ts.map +1 -0
  32. package/dist/cli/credential-print.js +76 -0
  33. package/dist/cli/credential-print.js.map +1 -0
  34. package/dist/cli/experimental/auth.d.ts +16 -0
  35. package/dist/cli/experimental/auth.d.ts.map +1 -0
  36. package/dist/cli/experimental/auth.js +13 -0
  37. package/dist/cli/experimental/auth.js.map +1 -0
  38. package/dist/cli/experimental/cli.d.ts +6 -0
  39. package/dist/cli/experimental/cli.d.ts.map +1 -0
  40. package/dist/cli/experimental/cli.js +5 -0
  41. package/dist/cli/experimental/cli.js.map +1 -0
  42. package/dist/cli/experimental/command-options.d.ts +17 -0
  43. package/dist/cli/experimental/command-options.d.ts.map +1 -0
  44. package/dist/cli/experimental/command-options.js +35 -0
  45. package/dist/cli/experimental/command-options.js.map +1 -0
  46. package/dist/cli/experimental/command.d.ts +63 -0
  47. package/dist/cli/experimental/command.d.ts.map +1 -0
  48. package/dist/cli/experimental/command.js +130 -0
  49. package/dist/cli/experimental/command.js.map +1 -0
  50. package/dist/cli/experimental/commands/client.d.ts +13 -0
  51. package/dist/cli/experimental/commands/client.d.ts.map +1 -0
  52. package/dist/cli/experimental/commands/client.js +25 -0
  53. package/dist/cli/experimental/commands/client.js.map +1 -0
  54. package/dist/cli/experimental/commands/mcpi.d.ts +15 -0
  55. package/dist/cli/experimental/commands/mcpi.d.ts.map +1 -0
  56. package/dist/cli/experimental/commands/mcpi.js +28 -0
  57. package/dist/cli/experimental/commands/mcpi.js.map +1 -0
  58. package/dist/cli/experimental/commands/server.d.ts +13 -0
  59. package/dist/cli/experimental/commands/server.d.ts.map +1 -0
  60. package/dist/cli/experimental/commands/server.js +25 -0
  61. package/dist/cli/experimental/commands/server.js.map +1 -0
  62. package/dist/cli/experimental/transport-address.d.ts +10 -0
  63. package/dist/cli/experimental/transport-address.d.ts.map +1 -0
  64. package/dist/cli/experimental/transport-address.js +38 -0
  65. package/dist/cli/experimental/transport-address.js.map +1 -0
  66. package/dist/cli/file-processor.d.ts.map +1 -1
  67. package/dist/cli/file-processor.js +13 -26
  68. package/dist/cli/file-processor.js.map +1 -1
  69. package/dist/cli/initial-message.d.ts +1 -1
  70. package/dist/cli/initial-message.d.ts.map +1 -1
  71. package/dist/cli/initial-message.js.map +1 -1
  72. package/dist/cli/list-models.d.ts +2 -2
  73. package/dist/cli/list-models.d.ts.map +1 -1
  74. package/dist/cli/list-models.js +4 -4
  75. package/dist/cli/list-models.js.map +1 -1
  76. package/dist/cli/project-trust.d.ts +10 -0
  77. package/dist/cli/project-trust.d.ts.map +1 -0
  78. package/dist/cli/project-trust.js +48 -0
  79. package/dist/cli/project-trust.js.map +1 -0
  80. package/dist/cli/session-picker.d.ts +3 -2
  81. package/dist/cli/session-picker.d.ts.map +1 -1
  82. package/dist/cli/session-picker.js +5 -4
  83. package/dist/cli/session-picker.js.map +1 -1
  84. package/dist/cli/startup-ui.d.ts +20 -0
  85. package/dist/cli/startup-ui.d.ts.map +1 -0
  86. package/dist/cli/startup-ui.js +184 -0
  87. package/dist/cli/startup-ui.js.map +1 -0
  88. package/dist/cli.d.ts.map +1 -1
  89. package/dist/cli.js +6 -3
  90. package/dist/cli.js.map +1 -1
  91. package/dist/client/index.d.ts +3 -0
  92. package/dist/client/index.d.ts.map +1 -0
  93. package/dist/client/index.js +3 -0
  94. package/dist/client/index.js.map +1 -0
  95. package/dist/client/remote-session.d.ts +53 -0
  96. package/dist/client/remote-session.d.ts.map +1 -0
  97. package/dist/client/remote-session.js +340 -0
  98. package/dist/client/remote-session.js.map +1 -0
  99. package/dist/client/transcript.d.ts +12 -0
  100. package/dist/client/transcript.d.ts.map +1 -0
  101. package/dist/client/transcript.js +98 -0
  102. package/dist/client/transcript.js.map +1 -0
  103. package/dist/config.d.ts +34 -12
  104. package/dist/config.d.ts.map +1 -1
  105. package/dist/config.js +312 -61
  106. package/dist/config.js.map +1 -1
  107. package/dist/core/agent-session-runtime.d.ts +11 -9
  108. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  109. package/dist/core/agent-session-runtime.js +16 -7
  110. package/dist/core/agent-session-runtime.js.map +1 -1
  111. package/dist/core/agent-session-services.d.ts +13 -12
  112. package/dist/core/agent-session-services.d.ts.map +1 -1
  113. package/dist/core/agent-session-services.js +34 -14
  114. package/dist/core/agent-session-services.js.map +1 -1
  115. package/dist/core/agent-session.d.ts +110 -53
  116. package/dist/core/agent-session.d.ts.map +1 -1
  117. package/dist/core/agent-session.js +662 -488
  118. package/dist/core/agent-session.js.map +1 -1
  119. package/dist/core/auth-guidance.d.ts.map +1 -1
  120. package/dist/core/auth-guidance.js.map +1 -1
  121. package/dist/core/auth-storage.d.ts +35 -104
  122. package/dist/core/auth-storage.d.ts.map +1 -1
  123. package/dist/core/auth-storage.js +272 -286
  124. package/dist/core/auth-storage.js.map +1 -1
  125. package/dist/core/bash-executor.d.ts +1 -1
  126. package/dist/core/bash-executor.d.ts.map +1 -1
  127. package/dist/core/bash-executor.js +11 -8
  128. package/dist/core/bash-executor.js.map +1 -1
  129. package/dist/core/cache-stats.d.ts +49 -0
  130. package/dist/core/cache-stats.d.ts.map +1 -0
  131. package/dist/core/cache-stats.js +101 -0
  132. package/dist/core/cache-stats.js.map +1 -0
  133. package/dist/core/compaction/branch-summarization.d.ts +16 -6
  134. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  135. package/dist/core/compaction/branch-summarization.js +13 -10
  136. package/dist/core/compaction/branch-summarization.js.map +1 -1
  137. package/dist/core/compaction/compaction.d.ts +25 -9
  138. package/dist/core/compaction/compaction.d.ts.map +1 -1
  139. package/dist/core/compaction/compaction.js +163 -133
  140. package/dist/core/compaction/compaction.js.map +1 -1
  141. package/dist/core/compaction/index.d.ts +3 -3
  142. package/dist/core/compaction/index.d.ts.map +1 -1
  143. package/dist/core/compaction/index.js.map +1 -1
  144. package/dist/core/compaction/utils.d.ts +3 -3
  145. package/dist/core/compaction/utils.d.ts.map +1 -1
  146. package/dist/core/compaction/utils.js +7 -18
  147. package/dist/core/compaction/utils.js.map +1 -1
  148. package/dist/core/defaults.d.ts +1 -1
  149. package/dist/core/defaults.d.ts.map +1 -1
  150. package/dist/core/defaults.js.map +1 -1
  151. package/dist/core/env-overlay.d.ts +21 -0
  152. package/dist/core/env-overlay.d.ts.map +1 -0
  153. package/dist/core/env-overlay.js +25 -0
  154. package/dist/core/env-overlay.js.map +1 -0
  155. package/dist/core/exec.d.ts +8 -1
  156. package/dist/core/exec.d.ts.map +1 -1
  157. package/dist/core/exec.js +7 -1
  158. package/dist/core/exec.js.map +1 -1
  159. package/dist/core/experimental.d.ts +6 -0
  160. package/dist/core/experimental.d.ts.map +1 -0
  161. package/dist/core/experimental.js +8 -0
  162. package/dist/core/experimental.js.map +1 -0
  163. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  164. package/dist/core/export-html/ansi-to-html.js +1 -1
  165. package/dist/core/export-html/ansi-to-html.js.map +1 -1
  166. package/dist/core/export-html/index.d.ts +2 -2
  167. package/dist/core/export-html/index.d.ts.map +1 -1
  168. package/dist/core/export-html/index.js +8 -6
  169. package/dist/core/export-html/index.js.map +1 -1
  170. package/dist/core/export-html/template.css +53 -4
  171. package/dist/core/export-html/template.js +129 -35
  172. package/dist/core/export-html/tool-renderer.d.ts +2 -8
  173. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  174. package/dist/core/export-html/tool-renderer.js +15 -2
  175. package/dist/core/export-html/tool-renderer.js.map +1 -1
  176. package/dist/core/export-html/vendor/marked.min.js +75 -3
  177. package/dist/core/extensions/index.d.ts +8 -8
  178. package/dist/core/extensions/index.d.ts.map +1 -1
  179. package/dist/core/extensions/index.js.map +1 -1
  180. package/dist/core/extensions/loader.d.ts +5 -7
  181. package/dist/core/extensions/loader.d.ts.map +1 -1
  182. package/dist/core/extensions/loader.js +161 -79
  183. package/dist/core/extensions/loader.js.map +1 -1
  184. package/dist/core/extensions/runner.d.ts +32 -12
  185. package/dist/core/extensions/runner.d.ts.map +1 -1
  186. package/dist/core/extensions/runner.js +194 -3
  187. package/dist/core/extensions/runner.js.map +1 -1
  188. package/dist/core/extensions/types.d.ts +217 -59
  189. package/dist/core/extensions/types.d.ts.map +1 -1
  190. package/dist/core/extensions/types.js.map +1 -1
  191. package/dist/core/extensions/wrapper.d.ts +3 -3
  192. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  193. package/dist/core/extensions/wrapper.js +22 -3
  194. package/dist/core/extensions/wrapper.js.map +1 -1
  195. package/dist/core/footer-data-provider.d.ts +12 -0
  196. package/dist/core/footer-data-provider.d.ts.map +1 -1
  197. package/dist/core/footer-data-provider.js +30 -2
  198. package/dist/core/footer-data-provider.js.map +1 -1
  199. package/dist/core/http-dispatcher.d.ts +22 -0
  200. package/dist/core/http-dispatcher.d.ts.map +1 -0
  201. package/dist/core/http-dispatcher.js +96 -0
  202. package/dist/core/http-dispatcher.js.map +1 -0
  203. package/dist/core/index.d.ts +9 -8
  204. package/dist/core/index.d.ts.map +1 -1
  205. package/dist/core/index.js +1 -0
  206. package/dist/core/index.js.map +1 -1
  207. package/dist/core/keybindings.d.ts +79 -10
  208. package/dist/core/keybindings.d.ts.map +1 -1
  209. package/dist/core/keybindings.js +11 -10
  210. package/dist/core/keybindings.js.map +1 -1
  211. package/dist/core/messages.d.ts +2 -2
  212. package/dist/core/messages.d.ts.map +1 -1
  213. package/dist/core/messages.js.map +1 -1
  214. package/dist/core/model-config.d.ts +571 -0
  215. package/dist/core/model-config.d.ts.map +1 -0
  216. package/dist/core/model-config.js +254 -0
  217. package/dist/core/model-config.js.map +1 -0
  218. package/dist/core/model-registry.d.ts +23 -110
  219. package/dist/core/model-registry.d.ts.map +1 -1
  220. package/dist/core/model-registry.js +65 -616
  221. package/dist/core/model-registry.js.map +1 -1
  222. package/dist/core/model-resolver.d.ts +20 -7
  223. package/dist/core/model-resolver.d.ts.map +1 -1
  224. package/dist/core/model-resolver.js +139 -43
  225. package/dist/core/model-resolver.js.map +1 -1
  226. package/dist/core/model-runtime.d.ts +100 -0
  227. package/dist/core/model-runtime.d.ts.map +1 -0
  228. package/dist/core/model-runtime.js +602 -0
  229. package/dist/core/model-runtime.js.map +1 -0
  230. package/dist/core/models-store.d.ts +22 -0
  231. package/dist/core/models-store.d.ts.map +1 -0
  232. package/dist/core/models-store.js +113 -0
  233. package/dist/core/models-store.js.map +1 -0
  234. package/dist/core/output-guard.d.ts +1 -0
  235. package/dist/core/output-guard.d.ts.map +1 -1
  236. package/dist/core/output-guard.js +52 -22
  237. package/dist/core/output-guard.js.map +1 -1
  238. package/dist/core/package-manager.d.ts +21 -4
  239. package/dist/core/package-manager.d.ts.map +1 -1
  240. package/dist/core/package-manager.js +428 -191
  241. package/dist/core/package-manager.js.map +1 -1
  242. package/dist/core/pi-manifest.d.ts +8 -0
  243. package/dist/core/pi-manifest.d.ts.map +1 -0
  244. package/dist/core/pi-manifest.js +25 -0
  245. package/dist/core/pi-manifest.js.map +1 -0
  246. package/dist/core/project-trust.d.ts +15 -0
  247. package/dist/core/project-trust.d.ts.map +1 -0
  248. package/dist/core/project-trust.js +59 -0
  249. package/dist/core/project-trust.js.map +1 -0
  250. package/dist/core/prompt-templates.d.ts +4 -2
  251. package/dist/core/prompt-templates.d.ts.map +1 -1
  252. package/dist/core/prompt-templates.js +36 -50
  253. package/dist/core/prompt-templates.js.map +1 -1
  254. package/dist/core/provider-attribution.d.ts +4 -0
  255. package/dist/core/provider-attribution.d.ts.map +1 -0
  256. package/dist/core/provider-attribution.js +72 -0
  257. package/dist/core/provider-attribution.js.map +1 -0
  258. package/dist/core/provider-composer.d.ts +58 -0
  259. package/dist/core/provider-composer.d.ts.map +1 -0
  260. package/dist/core/provider-composer.js +396 -0
  261. package/dist/core/provider-composer.js.map +1 -0
  262. package/dist/core/radius.d.ts +2 -0
  263. package/dist/core/radius.d.ts.map +1 -0
  264. package/dist/core/radius.js +2 -0
  265. package/dist/core/radius.js.map +1 -0
  266. package/dist/core/remote-catalog-provider.d.ts +17 -0
  267. package/dist/core/remote-catalog-provider.d.ts.map +1 -0
  268. package/dist/core/remote-catalog-provider.js +137 -0
  269. package/dist/core/remote-catalog-provider.js.map +1 -0
  270. package/dist/core/resolve-config-value.d.ts +13 -6
  271. package/dist/core/resolve-config-value.d.ts.map +1 -1
  272. package/dist/core/resolve-config-value.js +143 -22
  273. package/dist/core/resolve-config-value.js.map +1 -1
  274. package/dist/core/resource-loader.d.ts +40 -12
  275. package/dist/core/resource-loader.d.ts.map +1 -1
  276. package/dist/core/resource-loader.js +236 -87
  277. package/dist/core/resource-loader.js.map +1 -1
  278. package/dist/core/runtime-credentials.d.ts +15 -0
  279. package/dist/core/runtime-credentials.d.ts.map +1 -0
  280. package/dist/core/runtime-credentials.js +39 -0
  281. package/dist/core/runtime-credentials.js.map +1 -0
  282. package/dist/core/sdk.d.ts +26 -25
  283. package/dist/core/sdk.d.ts.map +1 -1
  284. package/dist/core/sdk.js +56 -49
  285. package/dist/core/sdk.js.map +1 -1
  286. package/dist/core/session-manager.d.ts +42 -19
  287. package/dist/core/session-manager.d.ts.map +1 -1
  288. package/dist/core/session-manager.js +454 -223
  289. package/dist/core/session-manager.js.map +1 -1
  290. package/dist/core/settings-manager.d.ts +77 -8
  291. package/dist/core/settings-manager.d.ts.map +1 -1
  292. package/dist/core/settings-manager.js +255 -67
  293. package/dist/core/settings-manager.js.map +1 -1
  294. package/dist/core/skills.d.ts +2 -2
  295. package/dist/core/skills.d.ts.map +1 -1
  296. package/dist/core/skills.js +12 -34
  297. package/dist/core/skills.js.map +1 -1
  298. package/dist/core/slash-commands.d.ts +2 -1
  299. package/dist/core/slash-commands.d.ts.map +1 -1
  300. package/dist/core/slash-commands.js +4 -3
  301. package/dist/core/slash-commands.js.map +1 -1
  302. package/dist/core/source-info.d.ts +1 -1
  303. package/dist/core/source-info.d.ts.map +1 -1
  304. package/dist/core/source-info.js.map +1 -1
  305. package/dist/core/system-prompt.d.ts +1 -1
  306. package/dist/core/system-prompt.d.ts.map +1 -1
  307. package/dist/core/system-prompt.js +15 -25
  308. package/dist/core/system-prompt.js.map +1 -1
  309. package/dist/core/telemetry.d.ts +7 -1
  310. package/dist/core/telemetry.d.ts.map +1 -1
  311. package/dist/core/telemetry.js +7 -1
  312. package/dist/core/telemetry.js.map +1 -1
  313. package/dist/core/timings.d.ts +5 -3
  314. package/dist/core/timings.d.ts.map +1 -1
  315. package/dist/core/timings.js +26 -16
  316. package/dist/core/timings.js.map +1 -1
  317. package/dist/core/tools/bash.d.ts +11 -5
  318. package/dist/core/tools/bash.d.ts.map +1 -1
  319. package/dist/core/tools/bash.js +224 -185
  320. package/dist/core/tools/bash.js.map +1 -1
  321. package/dist/core/tools/edit-diff.d.ts +26 -5
  322. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  323. package/dist/core/tools/edit-diff.js +110 -23
  324. package/dist/core/tools/edit-diff.js.map +1 -1
  325. package/dist/core/tools/edit.d.ts +11 -5
  326. package/dist/core/tools/edit.d.ts.map +1 -1
  327. package/dist/core/tools/edit.js +67 -100
  328. package/dist/core/tools/edit.js.map +1 -1
  329. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  330. package/dist/core/tools/file-mutation-queue.js +27 -12
  331. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  332. package/dist/core/tools/find.d.ts +10 -3
  333. package/dist/core/tools/find.d.ts.map +1 -1
  334. package/dist/core/tools/find.js +41 -37
  335. package/dist/core/tools/find.js.map +1 -1
  336. package/dist/core/tools/grep.d.ts +7 -3
  337. package/dist/core/tools/grep.d.ts.map +1 -1
  338. package/dist/core/tools/grep.js +12 -8
  339. package/dist/core/tools/grep.js.map +1 -1
  340. package/dist/core/tools/index.d.ts +18 -18
  341. package/dist/core/tools/index.d.ts.map +1 -1
  342. package/dist/core/tools/index.js.map +1 -1
  343. package/dist/core/tools/ls.d.ts +7 -3
  344. package/dist/core/tools/ls.d.ts.map +1 -1
  345. package/dist/core/tools/ls.js +17 -15
  346. package/dist/core/tools/ls.js.map +1 -1
  347. package/dist/core/tools/output-accumulator.d.ts +52 -0
  348. package/dist/core/tools/output-accumulator.d.ts.map +1 -0
  349. package/dist/core/tools/output-accumulator.js +184 -0
  350. package/dist/core/tools/output-accumulator.js.map +1 -0
  351. package/dist/core/tools/path-utils.d.ts +2 -0
  352. package/dist/core/tools/path-utils.d.ts.map +1 -1
  353. package/dist/core/tools/path-utils.js +39 -21
  354. package/dist/core/tools/path-utils.js.map +1 -1
  355. package/dist/core/tools/read.d.ts +7 -3
  356. package/dist/core/tools/read.d.ts.map +1 -1
  357. package/dist/core/tools/read.js +101 -51
  358. package/dist/core/tools/read.js.map +1 -1
  359. package/dist/core/tools/render-utils.d.ts +5 -2
  360. package/dist/core/tools/render-utils.d.ts.map +1 -1
  361. package/dist/core/tools/render-utils.js +18 -2
  362. package/dist/core/tools/render-utils.js.map +1 -1
  363. package/dist/core/tools/tool-definition-wrapper.d.ts +2 -2
  364. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  365. package/dist/core/tools/tool-definition-wrapper.js +3 -3
  366. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  367. package/dist/core/tools/truncate.d.ts.map +1 -1
  368. package/dist/core/tools/truncate.js +12 -2
  369. package/dist/core/tools/truncate.js.map +1 -1
  370. package/dist/core/tools/write.d.ts +6 -2
  371. package/dist/core/tools/write.d.ts.map +1 -1
  372. package/dist/core/tools/write.js +35 -45
  373. package/dist/core/tools/write.js.map +1 -1
  374. package/dist/core/trust-manager.d.ts +36 -0
  375. package/dist/core/trust-manager.d.ts.map +1 -0
  376. package/dist/core/trust-manager.js +202 -0
  377. package/dist/core/trust-manager.js.map +1 -0
  378. package/dist/core/usage-totals.d.ts +19 -0
  379. package/dist/core/usage-totals.d.ts.map +1 -0
  380. package/dist/core/usage-totals.js +52 -0
  381. package/dist/core/usage-totals.js.map +1 -0
  382. package/dist/extensions/index.d.ts +3 -0
  383. package/dist/extensions/index.d.ts.map +1 -0
  384. package/dist/extensions/index.js +3 -0
  385. package/dist/extensions/index.js.map +1 -0
  386. package/dist/extensions/llama/client.d.ts +61 -0
  387. package/dist/extensions/llama/client.d.ts.map +1 -0
  388. package/dist/extensions/llama/client.js +302 -0
  389. package/dist/extensions/llama/client.js.map +1 -0
  390. package/dist/extensions/llama/huggingface.d.ts +23 -0
  391. package/dist/extensions/llama/huggingface.d.ts.map +1 -0
  392. package/dist/extensions/llama/huggingface.js +141 -0
  393. package/dist/extensions/llama/huggingface.js.map +1 -0
  394. package/dist/extensions/llama/index.d.ts +3 -0
  395. package/dist/extensions/llama/index.d.ts.map +1 -0
  396. package/dist/extensions/llama/index.js +217 -0
  397. package/dist/extensions/llama/index.js.map +1 -0
  398. package/dist/extensions/llama/provider.d.ts +10 -0
  399. package/dist/extensions/llama/provider.d.ts.map +1 -0
  400. package/dist/extensions/llama/provider.js +121 -0
  401. package/dist/extensions/llama/provider.js.map +1 -0
  402. package/dist/extensions/llama/ui.d.ts +42 -0
  403. package/dist/extensions/llama/ui.d.ts.map +1 -0
  404. package/dist/extensions/llama/ui.js +416 -0
  405. package/dist/extensions/llama/ui.js.map +1 -0
  406. package/dist/index.d.ts +34 -27
  407. package/dist/index.d.ts.map +1 -1
  408. package/dist/index.js +11 -5
  409. package/dist/index.js.map +1 -1
  410. package/dist/main.d.ts +2 -2
  411. package/dist/main.d.ts.map +1 -1
  412. package/dist/main.js +311 -101
  413. package/dist/main.js.map +1 -1
  414. package/dist/migrations.d.ts +5 -5
  415. package/dist/migrations.d.ts.map +1 -1
  416. package/dist/migrations.js +10 -10
  417. package/dist/migrations.js.map +1 -1
  418. package/dist/modes/index.d.ts +6 -5
  419. package/dist/modes/index.d.ts.map +1 -1
  420. package/dist/modes/index.js.map +1 -1
  421. package/dist/modes/interactive/components/armin.d.ts +1 -1
  422. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  423. package/dist/modes/interactive/components/armin.js.map +1 -1
  424. package/dist/modes/interactive/components/assistant-message.d.ts +8 -3
  425. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  426. package/dist/modes/interactive/components/assistant-message.js +55 -26
  427. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  428. package/dist/modes/interactive/components/bash-execution.d.ts +2 -2
  429. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  430. package/dist/modes/interactive/components/bash-execution.js +4 -4
  431. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  432. package/dist/modes/interactive/components/bordered-loader.d.ts +2 -2
  433. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  434. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  435. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  436. package/dist/modes/interactive/components/branch-summary-message.d.ts +2 -2
  437. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  438. package/dist/modes/interactive/components/branch-summary-message.js +1 -1
  439. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  440. package/dist/modes/interactive/components/compaction-summary-message.d.ts +2 -2
  441. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  442. package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
  443. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  444. package/dist/modes/interactive/components/config-selector.d.ts +37 -6
  445. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  446. package/dist/modes/interactive/components/config-selector.js +318 -34
  447. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  448. package/dist/modes/interactive/components/countdown-timer.d.ts +3 -3
  449. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  450. package/dist/modes/interactive/components/countdown-timer.js +2 -2
  451. package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  452. package/dist/modes/interactive/components/custom-editor.d.ts +2 -2
  453. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  454. package/dist/modes/interactive/components/custom-editor.js +9 -2
  455. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  456. package/dist/modes/interactive/components/custom-entry.d.ts +19 -0
  457. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -0
  458. package/dist/modes/interactive/components/custom-entry.js +52 -0
  459. package/dist/modes/interactive/components/custom-entry.js.map +1 -0
  460. package/dist/modes/interactive/components/custom-message.d.ts +6 -4
  461. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  462. package/dist/modes/interactive/components/custom-message.js +11 -3
  463. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  464. package/dist/modes/interactive/components/daxnuts.d.ts +1 -1
  465. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  466. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  467. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  468. package/dist/modes/interactive/components/diff.js.map +1 -1
  469. package/dist/modes/interactive/components/dynamic-border.d.ts +1 -1
  470. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  471. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  472. package/dist/modes/interactive/components/extension-editor.d.ts +5 -4
  473. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  474. package/dist/modes/interactive/components/extension-editor.js +19 -32
  475. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  476. package/dist/modes/interactive/components/extension-input.d.ts +1 -1
  477. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  478. package/dist/modes/interactive/components/extension-input.js +1 -1
  479. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  480. package/dist/modes/interactive/components/extension-selector.d.ts +3 -1
  481. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  482. package/dist/modes/interactive/components/extension-selector.js +7 -2
  483. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  484. package/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
  485. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -0
  486. package/dist/modes/interactive/components/first-time-setup.js +103 -0
  487. package/dist/modes/interactive/components/first-time-setup.js.map +1 -0
  488. package/dist/modes/interactive/components/footer.d.ts +9 -4
  489. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  490. package/dist/modes/interactive/components/footer.js +52 -31
  491. package/dist/modes/interactive/components/footer.js.map +1 -1
  492. package/dist/modes/interactive/components/index.d.ts +33 -31
  493. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  494. package/dist/modes/interactive/components/index.js +2 -0
  495. package/dist/modes/interactive/components/index.js.map +1 -1
  496. package/dist/modes/interactive/components/keybinding-hints.d.ts +6 -1
  497. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  498. package/dist/modes/interactive/components/keybinding-hints.js +20 -6
  499. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  500. package/dist/modes/interactive/components/login-dialog.d.ts +12 -6
  501. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  502. package/dist/modes/interactive/components/login-dialog.js +46 -17
  503. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  504. package/dist/modes/interactive/components/markdown-transform.d.ts +3 -0
  505. package/dist/modes/interactive/components/markdown-transform.d.ts.map +1 -0
  506. package/dist/modes/interactive/components/markdown-transform.js +19 -0
  507. package/dist/modes/interactive/components/markdown-transform.js.map +1 -0
  508. package/dist/modes/interactive/components/mermaid.d.ts +11 -0
  509. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
  510. package/dist/modes/interactive/components/mermaid.js +75 -0
  511. package/dist/modes/interactive/components/mermaid.js.map +1 -0
  512. package/dist/modes/interactive/components/model-selector.d.ts +13 -6
  513. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  514. package/dist/modes/interactive/components/model-selector.js +92 -44
  515. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  516. package/dist/modes/interactive/components/oauth-selector.d.ts +7 -4
  517. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  518. package/dist/modes/interactive/components/oauth-selector.js +32 -28
  519. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  520. package/dist/modes/interactive/components/scoped-models-selector.d.ts +5 -1
  521. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  522. package/dist/modes/interactive/components/scoped-models-selector.js +52 -14
  523. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  524. package/dist/modes/interactive/components/session-selector-search.d.ts +1 -1
  525. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  526. package/dist/modes/interactive/components/session-selector-search.js +1 -1
  527. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  528. package/dist/modes/interactive/components/session-selector.d.ts +4 -5
  529. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  530. package/dist/modes/interactive/components/session-selector.js +18 -16
  531. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  532. package/dist/modes/interactive/components/settings-selector.d.ts +23 -2
  533. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  534. package/dist/modes/interactive/components/settings-selector.js +322 -22
  535. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  536. package/dist/modes/interactive/components/show-images-selector.d.ts +1 -1
  537. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  538. package/dist/modes/interactive/components/show-images-selector.js +1 -1
  539. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  540. package/dist/modes/interactive/components/skill-invocation-message.d.ts +2 -2
  541. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  542. package/dist/modes/interactive/components/skill-invocation-message.js +1 -1
  543. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  544. package/dist/modes/interactive/components/status-indicator.d.ts +28 -0
  545. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -0
  546. package/dist/modes/interactive/components/status-indicator.js +60 -0
  547. package/dist/modes/interactive/components/status-indicator.js.map +1 -0
  548. package/dist/modes/interactive/components/theme-selector.d.ts +1 -1
  549. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  550. package/dist/modes/interactive/components/theme-selector.js +1 -1
  551. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  552. package/dist/modes/interactive/components/thinking-selector.d.ts +2 -2
  553. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  554. package/dist/modes/interactive/components/thinking-selector.js +3 -2
  555. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  556. package/dist/modes/interactive/components/tool-execution.d.ts +2 -2
  557. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  558. package/dist/modes/interactive/components/tool-execution.js +33 -2
  559. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  560. package/dist/modes/interactive/components/tree-selector.d.ts +7 -2
  561. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  562. package/dist/modes/interactive/components/tree-selector.js +179 -26
  563. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  564. package/dist/modes/interactive/components/trust-selector.d.ts +23 -0
  565. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -0
  566. package/dist/modes/interactive/components/trust-selector.js +91 -0
  567. package/dist/modes/interactive/components/trust-selector.js.map +1 -0
  568. package/dist/modes/interactive/components/user-message-selector.d.ts +1 -1
  569. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  570. package/dist/modes/interactive/components/user-message-selector.js +1 -1
  571. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  572. package/dist/modes/interactive/components/user-message.d.ts +9 -3
  573. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  574. package/dist/modes/interactive/components/user-message.js +26 -6
  575. package/dist/modes/interactive/components/user-message.js.map +1 -1
  576. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  577. package/dist/modes/interactive/components/visual-truncate.js +1 -1
  578. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  579. package/dist/modes/interactive/external-editor.d.ts +12 -0
  580. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  581. package/dist/modes/interactive/external-editor.js +37 -0
  582. package/dist/modes/interactive/external-editor.js.map +1 -0
  583. package/dist/modes/interactive/interactive-mode.d.ts +104 -26
  584. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  585. package/dist/modes/interactive/interactive-mode.js +1565 -692
  586. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  587. package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
  588. package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
  589. package/dist/modes/interactive/model-catalog-refresh.js +34 -0
  590. package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
  591. package/dist/modes/interactive/model-search.d.ts +12 -0
  592. package/dist/modes/interactive/model-search.d.ts.map +1 -0
  593. package/dist/modes/interactive/model-search.js +15 -0
  594. package/dist/modes/interactive/model-search.js.map +1 -0
  595. package/dist/modes/interactive/theme/dark.json +10 -5
  596. package/dist/modes/interactive/theme/light.json +10 -5
  597. package/dist/modes/interactive/theme/theme-controller.d.ts +40 -0
  598. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -0
  599. package/dist/modes/interactive/theme/theme-controller.js +125 -0
  600. package/dist/modes/interactive/theme/theme-controller.js.map +1 -0
  601. package/dist/modes/interactive/theme/theme-schema.json +22 -5
  602. package/dist/modes/interactive/theme/theme.d.ts +48 -7
  603. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  604. package/dist/modes/interactive/theme/theme.js +194 -76
  605. package/dist/modes/interactive/theme/theme.js.map +1 -1
  606. package/dist/modes/json-event.d.ts +31 -0
  607. package/dist/modes/json-event.d.ts.map +1 -0
  608. package/dist/modes/json-event.js +15 -0
  609. package/dist/modes/json-event.js.map +1 -0
  610. package/dist/modes/print-mode.d.ts +3 -3
  611. package/dist/modes/print-mode.d.ts.map +1 -1
  612. package/dist/modes/print-mode.js +16 -5
  613. package/dist/modes/print-mode.js.map +1 -1
  614. package/dist/modes/rpc/rpc-client.d.ts +33 -10
  615. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  616. package/dist/modes/rpc/rpc-client.js +89 -11
  617. package/dist/modes/rpc/rpc-client.js.map +1 -1
  618. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  619. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  620. package/dist/modes/rpc/rpc-mode.js +69 -9
  621. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  622. package/dist/modes/rpc/rpc-types.d.ts +43 -5
  623. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  624. package/dist/modes/rpc/rpc-types.js.map +1 -1
  625. package/dist/package-manager-cli.d.ts +6 -2
  626. package/dist/package-manager-cli.d.ts.map +1 -1
  627. package/dist/package-manager-cli.js +527 -25
  628. package/dist/package-manager-cli.js.map +1 -1
  629. package/dist/path-migration.d.ts +13 -0
  630. package/dist/path-migration.d.ts.map +1 -0
  631. package/dist/path-migration.js +52 -0
  632. package/dist/path-migration.js.map +1 -0
  633. package/dist/rpc-entry.d.ts +3 -0
  634. package/dist/rpc-entry.d.ts.map +1 -0
  635. package/dist/rpc-entry.js +11 -0
  636. package/dist/rpc-entry.js.map +1 -0
  637. package/dist/server/create-harness.d.ts +26 -0
  638. package/dist/server/create-harness.d.ts.map +1 -0
  639. package/dist/server/create-harness.js +106 -0
  640. package/dist/server/create-harness.js.map +1 -0
  641. package/dist/utils/abort.d.ts +5 -0
  642. package/dist/utils/abort.d.ts.map +1 -0
  643. package/dist/utils/abort.js +48 -0
  644. package/dist/utils/abort.js.map +1 -0
  645. package/dist/utils/ansi.d.ts +2 -0
  646. package/dist/utils/ansi.d.ts.map +1 -0
  647. package/dist/utils/ansi.js +52 -0
  648. package/dist/utils/ansi.js.map +1 -0
  649. package/dist/utils/changelog.d.ts +2 -1
  650. package/dist/utils/changelog.d.ts.map +1 -1
  651. package/dist/utils/changelog.js +85 -0
  652. package/dist/utils/changelog.js.map +1 -1
  653. package/dist/utils/child-process.d.ts +12 -5
  654. package/dist/utils/child-process.d.ts.map +1 -1
  655. package/dist/utils/child-process.js +33 -5
  656. package/dist/utils/child-process.js.map +1 -1
  657. package/dist/utils/clipboard-image.d.ts.map +1 -1
  658. package/dist/utils/clipboard-image.js +4 -4
  659. package/dist/utils/clipboard-image.js.map +1 -1
  660. package/dist/utils/clipboard-native.d.ts +4 -1
  661. package/dist/utils/clipboard-native.d.ts.map +1 -1
  662. package/dist/utils/clipboard-native.js +14 -8
  663. package/dist/utils/clipboard-native.js.map +1 -1
  664. package/dist/utils/clipboard.d.ts +2 -0
  665. package/dist/utils/clipboard.d.ts.map +1 -1
  666. package/dist/utils/clipboard.js +129 -46
  667. package/dist/utils/clipboard.js.map +1 -1
  668. package/dist/utils/deprecation.d.ts +4 -0
  669. package/dist/utils/deprecation.d.ts.map +1 -0
  670. package/dist/utils/deprecation.js +13 -0
  671. package/dist/utils/deprecation.js.map +1 -0
  672. package/dist/utils/exif-orientation.d.ts +1 -1
  673. package/dist/utils/exif-orientation.d.ts.map +1 -1
  674. package/dist/utils/exif-orientation.js.map +1 -1
  675. package/dist/utils/git.d.ts.map +1 -1
  676. package/dist/utils/git.js +54 -22
  677. package/dist/utils/git.js.map +1 -1
  678. package/dist/utils/html.d.ts +7 -0
  679. package/dist/utils/html.d.ts.map +1 -0
  680. package/dist/utils/html.js +40 -0
  681. package/dist/utils/html.js.map +1 -0
  682. package/dist/utils/image-convert.d.ts +1 -0
  683. package/dist/utils/image-convert.d.ts.map +1 -1
  684. package/dist/utils/image-convert.js +21 -15
  685. package/dist/utils/image-convert.js.map +1 -1
  686. package/dist/utils/image-process.d.ts +18 -0
  687. package/dist/utils/image-process.d.ts.map +1 -0
  688. package/dist/utils/image-process.js +83 -0
  689. package/dist/utils/image-process.js.map +1 -0
  690. package/dist/utils/image-resize-core.d.ts +30 -0
  691. package/dist/utils/image-resize-core.d.ts.map +1 -0
  692. package/dist/utils/image-resize-core.js +124 -0
  693. package/dist/utils/image-resize-core.js.map +1 -0
  694. package/dist/utils/image-resize-worker.d.ts +2 -0
  695. package/dist/utils/image-resize-worker.d.ts.map +1 -0
  696. package/dist/utils/image-resize-worker.js +31 -0
  697. package/dist/utils/image-resize-worker.js.map +1 -0
  698. package/dist/utils/image-resize.d.ts +7 -27
  699. package/dist/utils/image-resize.d.ts.map +1 -1
  700. package/dist/utils/image-resize.js +75 -115
  701. package/dist/utils/image-resize.js.map +1 -1
  702. package/dist/utils/json.d.ts +3 -0
  703. package/dist/utils/json.d.ts.map +1 -0
  704. package/dist/utils/json.js +7 -0
  705. package/dist/utils/json.js.map +1 -0
  706. package/dist/utils/management-http.d.ts +23 -0
  707. package/dist/utils/management-http.d.ts.map +1 -0
  708. package/dist/utils/management-http.js +50 -0
  709. package/dist/utils/management-http.js.map +1 -0
  710. package/dist/utils/mime.d.ts +1 -0
  711. package/dist/utils/mime.d.ts.map +1 -1
  712. package/dist/utils/mime.js +100 -16
  713. package/dist/utils/mime.js.map +1 -1
  714. package/dist/utils/open-browser.d.ts +9 -0
  715. package/dist/utils/open-browser.d.ts.map +1 -0
  716. package/dist/utils/open-browser.js +22 -0
  717. package/dist/utils/open-browser.js.map +1 -0
  718. package/dist/utils/paths.d.ts +28 -1
  719. package/dist/utils/paths.d.ts.map +1 -1
  720. package/dist/utils/paths.js +97 -2
  721. package/dist/utils/paths.js.map +1 -1
  722. package/dist/utils/pi-user-agent.d.ts +2 -0
  723. package/dist/utils/pi-user-agent.d.ts.map +1 -0
  724. package/dist/utils/pi-user-agent.js +5 -0
  725. package/dist/utils/pi-user-agent.js.map +1 -0
  726. package/dist/utils/shell.d.ts +1 -0
  727. package/dist/utils/shell.d.ts.map +1 -1
  728. package/dist/utils/shell.js +18 -6
  729. package/dist/utils/shell.js.map +1 -1
  730. package/dist/utils/syntax-highlight.d.ts +12 -0
  731. package/dist/utils/syntax-highlight.d.ts.map +1 -0
  732. package/dist/utils/syntax-highlight.js +118 -0
  733. package/dist/utils/syntax-highlight.js.map +1 -0
  734. package/dist/utils/tool-result-images.d.ts +19 -0
  735. package/dist/utils/tool-result-images.d.ts.map +1 -0
  736. package/dist/utils/tool-result-images.js +45 -0
  737. package/dist/utils/tool-result-images.js.map +1 -0
  738. package/dist/utils/tools-manager.d.ts +10 -1
  739. package/dist/utils/tools-manager.d.ts.map +1 -1
  740. package/dist/utils/tools-manager.js +105 -36
  741. package/dist/utils/tools-manager.js.map +1 -1
  742. package/dist/utils/version-check.d.ts +19 -0
  743. package/dist/utils/version-check.d.ts.map +1 -0
  744. package/dist/utils/version-check.js +85 -0
  745. package/dist/utils/version-check.js.map +1 -0
  746. package/dist/utils/windows-self-update.d.ts +3 -0
  747. package/dist/utils/windows-self-update.d.ts.map +1 -0
  748. package/dist/utils/windows-self-update.js +77 -0
  749. package/dist/utils/windows-self-update.js.map +1 -0
  750. package/docs/compaction.md +39 -32
  751. package/docs/containerization.md +113 -0
  752. package/docs/custom-provider.md +212 -75
  753. package/docs/development.md +9 -9
  754. package/docs/docs.json +156 -0
  755. package/docs/environment-variables.md +186 -0
  756. package/docs/extensions.md +652 -140
  757. package/docs/index.md +81 -0
  758. package/docs/json.md +26 -15
  759. package/docs/keybindings.md +60 -21
  760. package/docs/llama-cpp.md +99 -0
  761. package/docs/models.md +168 -34
  762. package/docs/packages.md +46 -36
  763. package/docs/prompt-templates.md +13 -5
  764. package/docs/providers.md +147 -26
  765. package/docs/quickstart.md +167 -0
  766. package/docs/rpc.md +218 -36
  767. package/docs/sdk.md +190 -125
  768. package/docs/security.md +59 -0
  769. package/docs/{session.md → session-format.md} +48 -22
  770. package/docs/sessions.md +145 -0
  771. package/docs/settings.md +130 -25
  772. package/docs/shell-aliases.md +2 -2
  773. package/docs/skills.md +13 -14
  774. package/docs/terminal-setup.md +73 -16
  775. package/docs/termux.md +10 -10
  776. package/docs/themes.md +41 -16
  777. package/docs/tmux.md +6 -4
  778. package/docs/tui.md +85 -61
  779. package/docs/usage.md +302 -0
  780. package/docs/windows.md +2 -2
  781. package/examples/README.md +1 -1
  782. package/examples/extensions/README.md +12 -9
  783. package/examples/extensions/bash-spawn-hook.ts +2 -2
  784. package/examples/extensions/border-status-editor.ts +145 -0
  785. package/examples/extensions/built-in-tool-renderer.ts +2 -2
  786. package/examples/extensions/claude-rules.ts +1 -1
  787. package/examples/extensions/commands.ts +1 -1
  788. package/examples/extensions/custom-compaction.ts +6 -16
  789. package/examples/extensions/custom-footer.ts +1 -1
  790. package/examples/extensions/custom-header.ts +7 -7
  791. package/examples/extensions/custom-provider-anthropic/index.ts +15 -8
  792. package/examples/extensions/custom-provider-anthropic/package-lock.json +4 -4
  793. package/examples/extensions/custom-provider-anthropic/package.json +3 -3
  794. package/examples/extensions/custom-provider-gitlab-duo/index.ts +65 -9
  795. package/examples/extensions/custom-provider-gitlab-duo/package.json +2 -2
  796. package/examples/extensions/custom-provider-gitlab-duo/test.ts +2 -2
  797. package/examples/extensions/doom-overlay/README.md +1 -1
  798. package/examples/extensions/doom-overlay/doom-component.ts +4 -4
  799. package/examples/extensions/doom-overlay/doom-keys.ts +1 -1
  800. package/examples/extensions/doom-overlay/index.ts +5 -5
  801. package/examples/extensions/doom-overlay/wad-finder.ts +7 -3
  802. package/examples/extensions/dynamic-resources/dynamic.json +1 -1
  803. package/examples/extensions/entry-renderer.ts +41 -0
  804. package/examples/extensions/git-merge-and-resolve.ts +115 -0
  805. package/examples/extensions/github-issue-autocomplete.ts +2 -2
  806. package/examples/extensions/gondolin/index.ts +531 -0
  807. package/examples/extensions/gondolin/package-lock.json +185 -0
  808. package/examples/extensions/gondolin/package.json +19 -0
  809. package/examples/extensions/handoff.ts +50 -14
  810. package/examples/extensions/hidden-thinking-label.ts +1 -1
  811. package/examples/extensions/inline-bash.ts +2 -2
  812. package/examples/extensions/input-transform-streaming.ts +39 -0
  813. package/examples/extensions/input-transform.ts +2 -2
  814. package/examples/extensions/interactive-shell.ts +2 -2
  815. package/examples/extensions/kimi-deferred-tools.ts +61 -0
  816. package/examples/extensions/mac-system-theme.ts +2 -2
  817. package/examples/extensions/message-renderer.ts +3 -3
  818. package/examples/extensions/minimal-mode.ts +2 -2
  819. package/examples/extensions/modal-editor.ts +2 -2
  820. package/examples/extensions/model-status.ts +1 -1
  821. package/examples/extensions/notify.ts +3 -3
  822. package/examples/extensions/overlay-qa-tests.ts +242 -140
  823. package/examples/extensions/overlay-test.ts +9 -6
  824. package/examples/extensions/pirate.ts +1 -1
  825. package/examples/extensions/plan-mode/README.md +3 -2
  826. package/examples/extensions/plan-mode/index.ts +90 -40
  827. package/examples/extensions/preset.ts +16 -10
  828. package/examples/extensions/project-trust.ts +64 -0
  829. package/examples/extensions/prompt-customizer.ts +1 -1
  830. package/examples/extensions/provider-payload.ts +5 -5
  831. package/examples/extensions/qna.ts +4 -8
  832. package/examples/extensions/question.ts +41 -19
  833. package/examples/extensions/questionnaire.ts +50 -29
  834. package/examples/extensions/rainbow-editor.ts +1 -1
  835. package/examples/extensions/reload-runtime.ts +2 -2
  836. package/examples/extensions/rpc-demo.ts +1 -1
  837. package/examples/extensions/sandbox/index.ts +9 -9
  838. package/examples/extensions/sandbox/package-lock.json +7 -7
  839. package/examples/extensions/sandbox/package.json +3 -3
  840. package/examples/extensions/shutdown-command.ts +5 -5
  841. package/examples/extensions/snake.ts +2 -2
  842. package/examples/extensions/space-invaders.ts +2 -2
  843. package/examples/extensions/ssh.ts +2 -2
  844. package/examples/extensions/structured-output.ts +1 -1
  845. package/examples/extensions/subagent/README.md +18 -13
  846. package/examples/extensions/subagent/agents.ts +42 -11
  847. package/examples/extensions/subagent/index.ts +76 -30
  848. package/examples/extensions/summarize.ts +12 -19
  849. package/examples/extensions/tic-tac-toe.ts +2 -2
  850. package/examples/extensions/titlebar-spinner.ts +3 -3
  851. package/examples/extensions/todo.ts +2 -2
  852. package/examples/extensions/tool-override.ts +1 -1
  853. package/examples/extensions/tools.ts +7 -2
  854. package/examples/extensions/truncated-tool.ts +2 -2
  855. package/examples/extensions/with-deps/package-lock.json +4 -4
  856. package/examples/extensions/with-deps/package.json +4 -4
  857. package/examples/extensions/working-indicator.ts +4 -4
  858. package/examples/extensions/working-message-test.ts +1 -1
  859. package/examples/rpc-extension-ui.ts +11 -2
  860. package/examples/sdk/01-minimal.ts +15 -11
  861. package/examples/sdk/02-custom-model.ts +18 -18
  862. package/examples/sdk/03-custom-prompt.ts +25 -17
  863. package/examples/sdk/04-skills.ts +3 -3
  864. package/examples/sdk/05-tools.ts +8 -4
  865. package/examples/sdk/06-extensions.ts +13 -9
  866. package/examples/sdk/07-context-files.ts +2 -2
  867. package/examples/sdk/08-prompt-templates.ts +3 -3
  868. package/examples/sdk/09-api-keys-and-oauth.ts +19 -33
  869. package/examples/sdk/10-settings.ts +4 -4
  870. package/examples/sdk/11-sessions.ts +4 -0
  871. package/examples/sdk/12-full-control.ts +22 -21
  872. package/examples/sdk/README.md +23 -30
  873. package/npm-shrinkwrap.json +1818 -0
  874. package/package.json +117 -99
  875. package/dist/modes/interactive/assets/clankolas.png +0 -0
  876. package/dist/modes/interactive/components/auth-selector-status.d.ts +0 -20
  877. package/dist/modes/interactive/components/auth-selector-status.d.ts.map +0 -1
  878. package/dist/modes/interactive/components/auth-selector-status.js +0 -23
  879. package/dist/modes/interactive/components/auth-selector-status.js.map +0 -1
  880. package/dist/modes/interactive/components/earendil-announcement.d.ts +0 -5
  881. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +0 -1
  882. package/dist/modes/interactive/components/earendil-announcement.js +0 -40
  883. package/dist/modes/interactive/components/earendil-announcement.js.map +0 -1
  884. package/docs/images/exy.png +0 -0
  885. package/docs/tree.md +0 -233
  886. package/examples/extensions/antigravity-image-gen.ts +0 -418
  887. package/examples/extensions/custom-provider-qwen-cli/index.ts +0 -345
  888. package/examples/extensions/custom-provider-qwen-cli/package.json +0 -16
@@ -0,0 +1,75 @@
1
+ import { Marked } from "@sammorrowdrums/mcpi-tui";
2
+ import { render } from "grok-mermaid";
3
+ const markdownParser = new Marked();
4
+ function isMermaid(token) {
5
+ return token.type === "code" && token.lang?.trim().split(/\s+/, 1)[0]?.toLowerCase() === "mermaid";
6
+ }
7
+ function codeSpan(line) {
8
+ // Encode each diagram row as inline code (` ... `) so Markdown preserves its spacing and
9
+ // box-drawing characters. Use a non-breaking space for blank rows because an
10
+ // empty code span has no visible height.
11
+ const content = line || "\u00a0";
12
+ // CommonMark code spans use matching backtick delimiters, so choose one
13
+ // longer than any backtick run in the content (``hel`lo`` -> <code>hel`lo</code>).
14
+ // If the content starts or ends with a backtick, separating it from the
15
+ // delimiter with a space keeps that backtick as content; CommonMark removes
16
+ // the padding when rendering (`` `edge` `` -> <code>`edge`</code>).
17
+ // Mermaid labels can preserve backticks, for example:
18
+ // `┌──────────────┐ ┌──────────────┐`
19
+ // ```│ plain ` tick ├───▶│ two `` ticks │```
20
+ // `└──────────────┘ └──────────────┘`
21
+ const longestBacktickRun = Math.max(0, ...Array.from(content.matchAll(/`+/g), (match) => match[0].length));
22
+ const fence = "`".repeat(longestBacktickRun + 1);
23
+ const padding = content.startsWith("`") || content.endsWith("`") ? " " : "";
24
+ return `${fence}${padding}${content}${padding}${fence}`;
25
+ }
26
+ function styleSpan(span, theme) {
27
+ switch (span.cls) {
28
+ case "border":
29
+ return theme.fg("borderMuted", span.text);
30
+ case "text":
31
+ return theme.fg("text", span.text);
32
+ case "edge":
33
+ return theme.fg("accent", span.text);
34
+ case "edgeLabel":
35
+ return theme.fg("muted", span.text);
36
+ case "title":
37
+ return theme.fg("accent", theme.bold(span.text));
38
+ case "none":
39
+ return span.text;
40
+ }
41
+ }
42
+ function themedLines(art, theme) {
43
+ return art.styled.map((row) => row.map((span) => styleSpan(span, theme)).join(""));
44
+ }
45
+ /** Create a transformer that replaces top-level Mermaid code blocks with Unicode terminal diagrams. */
46
+ export function createMermaidMarkdownTransformer(options) {
47
+ return (markdown, context) => {
48
+ const mode = options.getMode();
49
+ if (mode === "off" ||
50
+ context.messageType === "assistant-thinking" ||
51
+ (context.isStreaming && mode !== "streaming")) {
52
+ return markdown;
53
+ }
54
+ return markdownParser
55
+ .lexer(markdown)
56
+ .map((token) => {
57
+ if (!isMermaid(token))
58
+ return token.raw;
59
+ const art = render(token.text);
60
+ if (!art || art.width > context.availableWidth)
61
+ return token.raw;
62
+ if (!context.isStreaming && art.warnings.length > 0) {
63
+ const suffix = art.warnings.length > 1 ? ` (+${art.warnings.length - 1} more)` : "";
64
+ const warning = `Mermaid diagram not rendered: ${art.warnings[0]}${suffix}`;
65
+ const styledWarning = options.theme ? options.theme.fg("warning", warning) : warning;
66
+ return `${token.raw}\n${codeSpan(styledWarning)} \n`;
67
+ }
68
+ const lines = options.theme ? themedLines(art, options.theme) : art.plain;
69
+ // Markdown hard breaks keep every diagram row on its own line.
70
+ return `${lines.map(codeSpan).join(" \n")}\n`;
71
+ })
72
+ .join("");
73
+ };
74
+ }
75
+ //# sourceMappingURL=mermaid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mermaid.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/mermaid.ts"],"names":[],"mappings":"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","sourcesContent":["import { Marked, type Token } from \"@sammorrowdrums/mcpi-tui\";\nimport { type MermaidArt, render, type Span } from \"grok-mermaid\";\nimport type { MarkdownTransformer } from \"../../../core/extensions/types.ts\";\nimport type { MermaidRenderingMode } from \"../../../core/settings-manager.ts\";\nimport type { Theme } from \"../theme/theme.ts\";\n\nconst markdownParser = new Marked();\n\ninterface MermaidTransformerOptions {\n\tgetMode: () => MermaidRenderingMode;\n\ttheme?: Theme;\n}\n\nfunction isMermaid(token: Token): token is Token & { type: \"code\"; text: string; lang?: string } {\n\treturn token.type === \"code\" && token.lang?.trim().split(/\\s+/, 1)[0]?.toLowerCase() === \"mermaid\";\n}\n\nfunction codeSpan(line: string): string {\n\t// Encode each diagram row as inline code (` ... `) so Markdown preserves its spacing and\n\t// box-drawing characters. Use a non-breaking space for blank rows because an\n\t// empty code span has no visible height.\n\tconst content = line || \"\\u00a0\";\n\t// CommonMark code spans use matching backtick delimiters, so choose one\n\t// longer than any backtick run in the content (``hel`lo`` -> <code>hel`lo</code>).\n\t// If the content starts or ends with a backtick, separating it from the\n\t// delimiter with a space keeps that backtick as content; CommonMark removes\n\t// the padding when rendering (`` `edge` `` -> <code>`edge`</code>).\n\t// Mermaid labels can preserve backticks, for example:\n\t// `┌──────────────┐ ┌──────────────┐`\n\t// ```│ plain ` tick ├───▶│ two `` ticks │```\n\t// `└──────────────┘ └──────────────┘`\n\tconst longestBacktickRun = Math.max(0, ...Array.from(content.matchAll(/`+/g), (match) => match[0].length));\n\tconst fence = \"`\".repeat(longestBacktickRun + 1);\n\tconst padding = content.startsWith(\"`\") || content.endsWith(\"`\") ? \" \" : \"\";\n\treturn `${fence}${padding}${content}${padding}${fence}`;\n}\n\nfunction styleSpan(span: Span, theme: Theme): string {\n\tswitch (span.cls) {\n\t\tcase \"border\":\n\t\t\treturn theme.fg(\"borderMuted\", span.text);\n\t\tcase \"text\":\n\t\t\treturn theme.fg(\"text\", span.text);\n\t\tcase \"edge\":\n\t\t\treturn theme.fg(\"accent\", span.text);\n\t\tcase \"edgeLabel\":\n\t\t\treturn theme.fg(\"muted\", span.text);\n\t\tcase \"title\":\n\t\t\treturn theme.fg(\"accent\", theme.bold(span.text));\n\t\tcase \"none\":\n\t\t\treturn span.text;\n\t}\n}\n\nfunction themedLines(art: MermaidArt, theme: Theme): string[] {\n\treturn art.styled.map((row) => row.map((span) => styleSpan(span, theme)).join(\"\"));\n}\n\n/** Create a transformer that replaces top-level Mermaid code blocks with Unicode terminal diagrams. */\nexport function createMermaidMarkdownTransformer(options: MermaidTransformerOptions): MarkdownTransformer {\n\treturn (markdown, context) => {\n\t\tconst mode = options.getMode();\n\t\tif (\n\t\t\tmode === \"off\" ||\n\t\t\tcontext.messageType === \"assistant-thinking\" ||\n\t\t\t(context.isStreaming && mode !== \"streaming\")\n\t\t) {\n\t\t\treturn markdown;\n\t\t}\n\n\t\treturn markdownParser\n\t\t\t.lexer(markdown)\n\t\t\t.map((token) => {\n\t\t\t\tif (!isMermaid(token)) return token.raw;\n\t\t\t\tconst art = render(token.text);\n\t\t\t\tif (!art || art.width > context.availableWidth) return token.raw;\n\t\t\t\tif (!context.isStreaming && art.warnings.length > 0) {\n\t\t\t\t\tconst suffix = art.warnings.length > 1 ? ` (+${art.warnings.length - 1} more)` : \"\";\n\t\t\t\t\tconst warning = `Mermaid diagram not rendered: ${art.warnings[0]}${suffix}`;\n\t\t\t\t\tconst styledWarning = options.theme ? options.theme.fg(\"warning\", warning) : warning;\n\t\t\t\t\treturn `${token.raw}\\n${codeSpan(styledWarning)} \\n`;\n\t\t\t\t}\n\t\t\t\tconst lines = options.theme ? themedLines(art, options.theme) : art.plain;\n\t\t\t\t// Markdown hard breaks keep every diagram row on its own line.\n\t\t\t\treturn `${lines.map(codeSpan).join(\" \\n\")}\\n`;\n\t\t\t})\n\t\t\t.join(\"\");\n\t};\n}\n"]}
@@ -1,7 +1,7 @@
1
- import { Container, type Focusable, Input, type TUI } from "@mariozechner/pi-tui";
2
1
  import { type Model } from "@sammorrowdrums/mcpi-ai";
3
- import type { ModelRegistry } from "../../../core/model-registry.js";
4
- import type { SettingsManager } from "../../../core/settings-manager.js";
2
+ import { Container, type Focusable, Input, type TUI } from "@sammorrowdrums/mcpi-tui";
3
+ import type { ModelRuntime } from "../../../core/model-runtime.ts";
4
+ import type { SettingsManager } from "../../../core/settings-manager.ts";
5
5
  interface ScopedModelItem {
6
6
  model: Model<any>;
7
7
  thinkingLevel?: string;
@@ -22,17 +22,24 @@ export declare class ModelSelectorComponent extends Container implements Focusab
22
22
  private selectedIndex;
23
23
  private currentModel?;
24
24
  private settingsManager;
25
- private modelRegistry;
25
+ private modelRuntime;
26
26
  private onSelectCallback;
27
27
  private onCancelCallback;
28
28
  private errorMessage?;
29
+ private refreshStatusMessage;
30
+ private refreshStatusSuccess;
29
31
  private tui;
30
32
  private scopedModels;
31
33
  private scope;
32
34
  private scopeText?;
33
35
  private scopeHintText?;
34
- constructor(tui: TUI, currentModel: Model<any> | undefined, settingsManager: SettingsManager, modelRegistry: ModelRegistry, scopedModels: ReadonlyArray<ScopedModelItem>, onSelect: (model: Model<any>) => void, onCancel: () => void, initialSearchInput?: string);
35
- private loadModels;
36
+ private readonly refreshAbortController;
37
+ private refreshTimeout?;
38
+ private closed;
39
+ constructor(tui: TUI, currentModel: Model<any> | undefined, settingsManager: SettingsManager, modelRuntime: ModelRuntime, scopedModels: ReadonlyArray<ScopedModelItem>, onSelect: (model: Model<any>) => void, onCancel: () => void, initialSearchInput?: string);
40
+ private loadModelsFromSnapshot;
41
+ private refreshModels;
42
+ dispose(): void;
36
43
  private sortModels;
37
44
  private getScopeText;
38
45
  private getScopeHintText;
@@ -1 +1 @@
1
- {"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAGL,KAAK,GAAG,EACR,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,KAAK,EAAkB,MAAM,yBAAyB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAWzE,UAAU,eAAe;IACxB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAU,YAAW,SAAS;IACzE,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IACD,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,cAAc,CAAmB;IACzC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,SAAS,CAAC,CAAO;IACzB,OAAO,CAAC,aAAa,CAAC,CAAO;IAE7B,YACC,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,EACpC,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,aAAa,CAAC,eAAe,CAAC,EAC5C,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EACrC,QAAQ,EAAE,MAAM,IAAI,EACpB,kBAAkB,CAAC,EAAE,MAAM,EA+D3B;YAEa,UAAU;IA8CxB,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,UAAU;IAyDlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAwCjC;IAED,OAAO,CAAC,YAAY;IAMpB,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@mariozechner/pi-tui\";\nimport { type Model, modelsAreEqual } from \"@sammorrowdrums/mcpi-ai\";\nimport type { ModelRegistry } from \"../../../core/model-registry.js\";\nimport type { SettingsManager } from \"../../../core/settings-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput 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.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate settingsManager: SettingsManager;\n\tprivate modelRegistry: ModelRegistry;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tsettingsManager: SettingsManager,\n\t\tmodelRegistry: ModelRegistry,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.modelRegistry = modelRegistry;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\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 hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Load models and do initial render\n\t\tthis.loadModels().then(() => {\n\t\t\tif (initialSearchInput) {\n\t\t\t\tthis.filterModels(initialSearchInput);\n\t\t\t} else {\n\t\t\t\tthis.updateList();\n\t\t\t}\n\t\t\t// Request re-render after models are loaded\n\t\t\tthis.tui.requestRender();\n\t\t});\n\t}\n\n\tprivate async loadModels(): Promise<void> {\n\t\tlet models: ModelItem[];\n\n\t\t// Refresh to pick up any changes to models.json\n\t\tthis.modelRegistry.refresh();\n\n\t\t// Check for models.json errors\n\t\tconst loadError = this.modelRegistry.getError();\n\t\tif (loadError) {\n\t\t\tthis.errorMessage = loadError;\n\t\t}\n\n\t\t// Load available models (built-in models still work even if models.json failed)\n\t\ttry {\n\t\t\tconst availableModels = await this.modelRegistry.getAvailable();\n\t\t\tmodels = availableModels.map((model: Model<any>) => ({\n\t\t\t\tprovider: model.provider,\n\t\t\t\tid: model.id,\n\t\t\t\tmodel,\n\t\t\t}));\n\t\t} catch (error) {\n\t\t\tthis.allModels = [];\n\t\t\tthis.scopedModelItems = [];\n\t\t\tthis.activeModels = [];\n\t\t\tthis.filteredModels = [];\n\t\t\tthis.errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRegistry.find(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(\n\t\t\t\t\tthis.activeModels,\n\t\t\t\t\tquery,\n\t\t\t\t\t({ id, provider }) => `${id} ${provider} ${provider}/${id} ${provider} ${id}`,\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\t// Save as new default\n\t\tthis.settingsManager.setDefaultModelAndProvider(model.provider, model.id);\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
1
+ {"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAkB,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAGL,KAAK,GAAG,EACR,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAazE,UAAU,eAAe;IACxB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAU,YAAW,SAAS;IACzE,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IACD,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,cAAc,CAAmB;IACzC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,oBAAoB,CAAgC;IAC5D,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,SAAS,CAAC,CAAO;IACzB,OAAO,CAAC,aAAa,CAAC,CAAO;IAC7B,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAyB;IAChE,OAAO,CAAC,cAAc,CAAC,CAAgC;IACvD,OAAO,CAAC,MAAM,CAAS;IAEvB,YACC,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,EACpC,eAAe,EAAE,eAAe,EAChC,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,aAAa,CAAC,eAAe,CAAC,EAC5C,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EACrC,QAAQ,EAAE,MAAM,IAAI,EACpB,kBAAkB,CAAC,EAAE,MAAM,EA2D3B;IAED,OAAO,CAAC,sBAAsB;YAuBhB,aAAa;IAwC3B,OAAO,IAAI,IAAI,CAKd;IAED,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,UAAU;IA+DlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAyCjC;IAED,OAAO,CAAC,YAAY;IAOpB,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import { type Model, modelsAreEqual } from \"@sammorrowdrums/mcpi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@sammorrowdrums/mcpi-tui\";\nimport type { ModelRuntime } from \"../../../core/model-runtime.ts\";\nimport type { SettingsManager } from \"../../../core/settings-manager.ts\";\nimport { refreshModelCatalogs } from \"../model-catalog-refresh.ts\";\nimport { getModelSelectorSearchText } from \"../model-search.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint } from \"./keybinding-hints.ts\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput 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.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate settingsManager: SettingsManager;\n\tprivate modelRuntime: ModelRuntime;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate refreshStatusMessage = \"Refreshing model catalogs…\";\n\tprivate refreshStatusSuccess = false;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\tprivate readonly refreshAbortController = new AbortController();\n\tprivate refreshTimeout?: ReturnType<typeof setTimeout>;\n\tprivate closed = false;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tsettingsManager: SettingsManager,\n\t\tmodelRuntime: ModelRuntime,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.modelRuntime = modelRuntime;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\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 hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Render the current snapshot immediately, then refresh in the background.\n\t\tthis.loadModelsFromSnapshot();\n\t\tif (initialSearchInput) this.filterModels(initialSearchInput);\n\t\telse this.updateList();\n\t\tthis.tui.requestRender();\n\t\tvoid this.refreshModels();\n\t}\n\n\tprivate loadModelsFromSnapshot(): void {\n\t\tconst models = this.modelRuntime.getAvailableSnapshot().map((model: Model<any>) => ({\n\t\t\tprovider: model.provider,\n\t\t\tid: model.id,\n\t\t\tmodel,\n\t\t}));\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRuntime.getModel(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate async refreshModels(): Promise<void> {\n\t\tconst timeoutMs = 15_000;\n\t\tlet timedOut = false;\n\t\tthis.refreshTimeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tthis.refreshAbortController.abort();\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tconst result = await refreshModelCatalogs(this.modelRuntime, this.refreshAbortController.signal);\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tif (result.aborted && timedOut) {\n\t\t\t\tthis.errorMessage = \"Model refresh timed out; showing cached models.\";\n\t\t\t} else if (result.errors.size === 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.keys().next().value}; showing cached models.`;\n\t\t\t} else if (result.errors.size > 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.size} model catalogs (${[...result.errors.keys()].join(\", \")}); showing cached models.`;\n\t\t\t} else {\n\t\t\t\tthis.errorMessage = this.modelRuntime.getError();\n\t\t\t\tif (!this.errorMessage) {\n\t\t\t\t\tthis.refreshStatusMessage = \"Model catalogs refreshed.\";\n\t\t\t\t\tthis.refreshStatusSuccess = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.loadModelsFromSnapshot();\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t\tthis.tui.requestRender();\n\t\t} catch (error) {\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tthis.errorMessage = timedOut\n\t\t\t\t? \"Model refresh timed out; showing cached models.\"\n\t\t\t\t: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`;\n\t\t\tthis.updateList();\n\t\t\tthis.tui.requestRender();\n\t\t} finally {\n\t\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\tthis.refreshAbortController.abort();\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(this.activeModels, query, ({ id, provider, model }) =>\n\t\t\t\t\tgetModelSelectorSearchText({ id, provider, name: model.name }),\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\t// When filtering by a query, move the selector to the top row so the best\n\t\t// match is highlighted. When the query is cleared, keep the current position\n\t\t// clamped to the (restored) list length.\n\t\tthis.selectedIndex = query ? 0 : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t\tif (this.refreshStatusMessage) {\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(this.refreshStatusSuccess ? \"success\" : \"muted\", ` ${this.refreshStatusMessage}`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.dispose();\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\tthis.dispose();\n\t\t// Save as new default\n\t\tthis.settingsManager.setDefaultModelAndProvider(model.provider, model.id);\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
@@ -1,5 +1,7 @@
1
- import { Container, fuzzyFilter, getKeybindings, Input, Spacer, Text, } from "@mariozechner/pi-tui";
2
1
  import { modelsAreEqual } from "@sammorrowdrums/mcpi-ai";
2
+ import { Container, fuzzyFilter, getKeybindings, Input, Spacer, Text, } from "@sammorrowdrums/mcpi-tui";
3
+ import { refreshModelCatalogs } from "../model-catalog-refresh.js";
4
+ import { getModelSelectorSearchText } from "../model-search.js";
3
5
  import { theme } from "../theme/theme.js";
4
6
  import { DynamicBorder } from "./dynamic-border.js";
5
7
  import { keyHint } from "./keybinding-hints.js";
@@ -25,21 +27,26 @@ export class ModelSelectorComponent extends Container {
25
27
  selectedIndex = 0;
26
28
  currentModel;
27
29
  settingsManager;
28
- modelRegistry;
30
+ modelRuntime;
29
31
  onSelectCallback;
30
32
  onCancelCallback;
31
33
  errorMessage;
34
+ refreshStatusMessage = "Refreshing model catalogs…";
35
+ refreshStatusSuccess = false;
32
36
  tui;
33
37
  scopedModels;
34
38
  scope = "all";
35
39
  scopeText;
36
40
  scopeHintText;
37
- constructor(tui, currentModel, settingsManager, modelRegistry, scopedModels, onSelect, onCancel, initialSearchInput) {
41
+ refreshAbortController = new AbortController();
42
+ refreshTimeout;
43
+ closed = false;
44
+ constructor(tui, currentModel, settingsManager, modelRuntime, scopedModels, onSelect, onCancel, initialSearchInput) {
38
45
  super();
39
46
  this.tui = tui;
40
47
  this.currentModel = currentModel;
41
48
  this.settingsManager = settingsManager;
42
- this.modelRegistry = modelRegistry;
49
+ this.modelRuntime = modelRuntime;
43
50
  this.scopedModels = scopedModels;
44
51
  this.scope = scopedModels.length > 0 ? "scoped" : "all";
45
52
  this.onSelectCallback = onSelect;
@@ -78,47 +85,24 @@ export class ModelSelectorComponent extends Container {
78
85
  this.addChild(new Spacer(1));
79
86
  // Add bottom border
80
87
  this.addChild(new DynamicBorder());
81
- // Load models and do initial render
82
- this.loadModels().then(() => {
83
- if (initialSearchInput) {
84
- this.filterModels(initialSearchInput);
85
- }
86
- else {
87
- this.updateList();
88
- }
89
- // Request re-render after models are loaded
90
- this.tui.requestRender();
91
- });
88
+ // Render the current snapshot immediately, then refresh in the background.
89
+ this.loadModelsFromSnapshot();
90
+ if (initialSearchInput)
91
+ this.filterModels(initialSearchInput);
92
+ else
93
+ this.updateList();
94
+ this.tui.requestRender();
95
+ void this.refreshModels();
92
96
  }
93
- async loadModels() {
94
- let models;
95
- // Refresh to pick up any changes to models.json
96
- this.modelRegistry.refresh();
97
- // Check for models.json errors
98
- const loadError = this.modelRegistry.getError();
99
- if (loadError) {
100
- this.errorMessage = loadError;
101
- }
102
- // Load available models (built-in models still work even if models.json failed)
103
- try {
104
- const availableModels = await this.modelRegistry.getAvailable();
105
- models = availableModels.map((model) => ({
106
- provider: model.provider,
107
- id: model.id,
108
- model,
109
- }));
110
- }
111
- catch (error) {
112
- this.allModels = [];
113
- this.scopedModelItems = [];
114
- this.activeModels = [];
115
- this.filteredModels = [];
116
- this.errorMessage = error instanceof Error ? error.message : String(error);
117
- return;
118
- }
97
+ loadModelsFromSnapshot() {
98
+ const models = this.modelRuntime.getAvailableSnapshot().map((model) => ({
99
+ provider: model.provider,
100
+ id: model.id,
101
+ model,
102
+ }));
119
103
  this.allModels = this.sortModels(models);
120
104
  this.scopedModels = this.scopedModels.map((scoped) => {
121
- const refreshed = this.modelRegistry.find(scoped.model.provider, scoped.model.id);
105
+ const refreshed = this.modelRuntime.getModel(scoped.model.provider, scoped.model.id);
122
106
  return refreshed ? { ...scoped, model: refreshed } : scoped;
123
107
  });
124
108
  this.scopedModelItems = this.scopedModels.map((scoped) => ({
@@ -132,6 +116,61 @@ export class ModelSelectorComponent extends Container {
132
116
  this.selectedIndex =
133
117
  currentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));
134
118
  }
119
+ async refreshModels() {
120
+ const timeoutMs = 15_000;
121
+ let timedOut = false;
122
+ this.refreshTimeout = setTimeout(() => {
123
+ timedOut = true;
124
+ this.refreshAbortController.abort();
125
+ }, timeoutMs);
126
+ try {
127
+ const result = await refreshModelCatalogs(this.modelRuntime, this.refreshAbortController.signal);
128
+ if (this.closed)
129
+ return;
130
+ this.refreshStatusMessage = "";
131
+ if (result.aborted && timedOut) {
132
+ this.errorMessage = "Model refresh timed out; showing cached models.";
133
+ }
134
+ else if (result.errors.size === 1) {
135
+ this.errorMessage = `Could not refresh ${result.errors.keys().next().value}; showing cached models.`;
136
+ }
137
+ else if (result.errors.size > 1) {
138
+ this.errorMessage = `Could not refresh ${result.errors.size} model catalogs (${[...result.errors.keys()].join(", ")}); showing cached models.`;
139
+ }
140
+ else {
141
+ this.errorMessage = this.modelRuntime.getError();
142
+ if (!this.errorMessage) {
143
+ this.refreshStatusMessage = "Model catalogs refreshed.";
144
+ this.refreshStatusSuccess = true;
145
+ }
146
+ }
147
+ this.loadModelsFromSnapshot();
148
+ this.filterModels(this.searchInput.getValue());
149
+ this.tui.requestRender();
150
+ }
151
+ catch (error) {
152
+ if (this.closed)
153
+ return;
154
+ this.refreshStatusMessage = "";
155
+ this.errorMessage = timedOut
156
+ ? "Model refresh timed out; showing cached models."
157
+ : `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`;
158
+ this.updateList();
159
+ this.tui.requestRender();
160
+ }
161
+ finally {
162
+ if (this.refreshTimeout)
163
+ clearTimeout(this.refreshTimeout);
164
+ }
165
+ }
166
+ dispose() {
167
+ if (this.closed)
168
+ return;
169
+ this.closed = true;
170
+ if (this.refreshTimeout)
171
+ clearTimeout(this.refreshTimeout);
172
+ this.refreshAbortController.abort();
173
+ }
135
174
  sortModels(models) {
136
175
  const sorted = [...models];
137
176
  // Sort: current model first, then by provider
@@ -168,9 +207,12 @@ export class ModelSelectorComponent extends Container {
168
207
  }
169
208
  filterModels(query) {
170
209
  this.filteredModels = query
171
- ? fuzzyFilter(this.activeModels, query, ({ id, provider }) => `${id} ${provider} ${provider}/${id} ${provider} ${id}`)
210
+ ? fuzzyFilter(this.activeModels, query, ({ id, provider, model }) => getModelSelectorSearchText({ id, provider, name: model.name }))
172
211
  : this.activeModels;
173
- this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));
212
+ // When filtering by a query, move the selector to the top row so the best
213
+ // match is highlighted. When the query is cleared, keep the current position
214
+ // clamped to the (restored) list length.
215
+ this.selectedIndex = query ? 0 : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));
174
216
  this.updateList();
175
217
  }
176
218
  updateList() {
@@ -222,6 +264,10 @@ export class ModelSelectorComponent extends Container {
222
264
  this.listContainer.addChild(new Spacer(1));
223
265
  this.listContainer.addChild(new Text(theme.fg("muted", ` Model Name: ${selected.model.name}`), 0, 0));
224
266
  }
267
+ if (this.refreshStatusMessage) {
268
+ this.listContainer.addChild(new Spacer(1));
269
+ this.listContainer.addChild(new Text(theme.fg(this.refreshStatusSuccess ? "success" : "muted", ` ${this.refreshStatusMessage}`), 0, 0));
270
+ }
225
271
  }
226
272
  handleInput(keyData) {
227
273
  const kb = getKeybindings();
@@ -258,6 +304,7 @@ export class ModelSelectorComponent extends Container {
258
304
  }
259
305
  // Escape or Ctrl+C
260
306
  else if (kb.matches(keyData, "tui.select.cancel")) {
307
+ this.dispose();
261
308
  this.onCancelCallback();
262
309
  }
263
310
  // Pass everything else to search input
@@ -267,6 +314,7 @@ export class ModelSelectorComponent extends Container {
267
314
  }
268
315
  }
269
316
  handleSelect(model) {
317
+ this.dispose();
270
318
  // Save as new default
271
319
  this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
272
320
  this.onSelectCallback(model);
@@ -1 +1 @@
1
- {"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@mariozechner/pi-tui\";\nimport { type Model, modelsAreEqual } from \"@sammorrowdrums/mcpi-ai\";\nimport type { ModelRegistry } from \"../../../core/model-registry.js\";\nimport type { SettingsManager } from \"../../../core/settings-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput 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.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate settingsManager: SettingsManager;\n\tprivate modelRegistry: ModelRegistry;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tsettingsManager: SettingsManager,\n\t\tmodelRegistry: ModelRegistry,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.modelRegistry = modelRegistry;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\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 hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Load models and do initial render\n\t\tthis.loadModels().then(() => {\n\t\t\tif (initialSearchInput) {\n\t\t\t\tthis.filterModels(initialSearchInput);\n\t\t\t} else {\n\t\t\t\tthis.updateList();\n\t\t\t}\n\t\t\t// Request re-render after models are loaded\n\t\t\tthis.tui.requestRender();\n\t\t});\n\t}\n\n\tprivate async loadModels(): Promise<void> {\n\t\tlet models: ModelItem[];\n\n\t\t// Refresh to pick up any changes to models.json\n\t\tthis.modelRegistry.refresh();\n\n\t\t// Check for models.json errors\n\t\tconst loadError = this.modelRegistry.getError();\n\t\tif (loadError) {\n\t\t\tthis.errorMessage = loadError;\n\t\t}\n\n\t\t// Load available models (built-in models still work even if models.json failed)\n\t\ttry {\n\t\t\tconst availableModels = await this.modelRegistry.getAvailable();\n\t\t\tmodels = availableModels.map((model: Model<any>) => ({\n\t\t\t\tprovider: model.provider,\n\t\t\t\tid: model.id,\n\t\t\t\tmodel,\n\t\t\t}));\n\t\t} catch (error) {\n\t\t\tthis.allModels = [];\n\t\t\tthis.scopedModelItems = [];\n\t\t\tthis.activeModels = [];\n\t\t\tthis.filteredModels = [];\n\t\t\tthis.errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRegistry.find(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(\n\t\t\t\t\tthis.activeModels,\n\t\t\t\t\tquery,\n\t\t\t\t\t({ id, provider }) => `${id} ${provider} ${provider}/${id} ${provider} ${id}`,\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\t// Save as new default\n\t\tthis.settingsManager.setDefaultModelAndProvider(model.provider, model.id);\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
1
+ {"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Model, modelsAreEqual } from \"@sammorrowdrums/mcpi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@sammorrowdrums/mcpi-tui\";\nimport type { ModelRuntime } from \"../../../core/model-runtime.ts\";\nimport type { SettingsManager } from \"../../../core/settings-manager.ts\";\nimport { refreshModelCatalogs } from \"../model-catalog-refresh.ts\";\nimport { getModelSelectorSearchText } from \"../model-search.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint } from \"./keybinding-hints.ts\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput 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.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate settingsManager: SettingsManager;\n\tprivate modelRuntime: ModelRuntime;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate refreshStatusMessage = \"Refreshing model catalogs…\";\n\tprivate refreshStatusSuccess = false;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\tprivate readonly refreshAbortController = new AbortController();\n\tprivate refreshTimeout?: ReturnType<typeof setTimeout>;\n\tprivate closed = false;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tsettingsManager: SettingsManager,\n\t\tmodelRuntime: ModelRuntime,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.modelRuntime = modelRuntime;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\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 hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Render the current snapshot immediately, then refresh in the background.\n\t\tthis.loadModelsFromSnapshot();\n\t\tif (initialSearchInput) this.filterModels(initialSearchInput);\n\t\telse this.updateList();\n\t\tthis.tui.requestRender();\n\t\tvoid this.refreshModels();\n\t}\n\n\tprivate loadModelsFromSnapshot(): void {\n\t\tconst models = this.modelRuntime.getAvailableSnapshot().map((model: Model<any>) => ({\n\t\t\tprovider: model.provider,\n\t\t\tid: model.id,\n\t\t\tmodel,\n\t\t}));\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRuntime.getModel(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate async refreshModels(): Promise<void> {\n\t\tconst timeoutMs = 15_000;\n\t\tlet timedOut = false;\n\t\tthis.refreshTimeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tthis.refreshAbortController.abort();\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tconst result = await refreshModelCatalogs(this.modelRuntime, this.refreshAbortController.signal);\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tif (result.aborted && timedOut) {\n\t\t\t\tthis.errorMessage = \"Model refresh timed out; showing cached models.\";\n\t\t\t} else if (result.errors.size === 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.keys().next().value}; showing cached models.`;\n\t\t\t} else if (result.errors.size > 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.size} model catalogs (${[...result.errors.keys()].join(\", \")}); showing cached models.`;\n\t\t\t} else {\n\t\t\t\tthis.errorMessage = this.modelRuntime.getError();\n\t\t\t\tif (!this.errorMessage) {\n\t\t\t\t\tthis.refreshStatusMessage = \"Model catalogs refreshed.\";\n\t\t\t\t\tthis.refreshStatusSuccess = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.loadModelsFromSnapshot();\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t\tthis.tui.requestRender();\n\t\t} catch (error) {\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tthis.errorMessage = timedOut\n\t\t\t\t? \"Model refresh timed out; showing cached models.\"\n\t\t\t\t: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`;\n\t\t\tthis.updateList();\n\t\t\tthis.tui.requestRender();\n\t\t} finally {\n\t\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\tthis.refreshAbortController.abort();\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(this.activeModels, query, ({ id, provider, model }) =>\n\t\t\t\t\tgetModelSelectorSearchText({ id, provider, name: model.name }),\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\t// When filtering by a query, move the selector to the top row so the best\n\t\t// match is highlighted. When the query is cleared, keep the current position\n\t\t// clamped to the (restored) list length.\n\t\tthis.selectedIndex = query ? 0 : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t\tif (this.refreshStatusMessage) {\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(this.refreshStatusSuccess ? \"success\" : \"muted\", ` ${this.refreshStatusMessage}`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.dispose();\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\tthis.dispose();\n\t\t// Save as new default\n\t\tthis.settingsManager.setDefaultModelAndProvider(model.provider, model.id);\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
@@ -1,10 +1,13 @@
1
- import { Container, type Focusable } from "@mariozechner/pi-tui";
2
- import type { AuthStorage } from "../../../core/auth-storage.js";
1
+ import type { ApiKeyAuth, AuthCheck, OAuthAuth } from "@sammorrowdrums/mcpi-ai";
2
+ import { Container, type Focusable } from "@sammorrowdrums/mcpi-tui";
3
3
  export type AuthSelectorProvider = {
4
4
  id: string;
5
5
  name: string;
6
6
  authType: "oauth" | "api_key";
7
+ method?: ApiKeyAuth | OAuthAuth;
8
+ status?: AuthCheck;
7
9
  };
10
+ export declare function formatAuthSelectorProviderType(authType: AuthSelectorProvider["authType"]): string;
8
11
  /**
9
12
  * Component that renders an auth provider selector
10
13
  */
@@ -18,10 +21,10 @@ export declare class OAuthSelectorComponent extends Container implements Focusab
18
21
  private filteredProviders;
19
22
  private selectedIndex;
20
23
  private mode;
21
- private authStorage;
22
24
  private onSelectCallback;
23
25
  private onCancelCallback;
24
- constructor(mode: "login" | "logout", authStorage: AuthStorage, providers: AuthSelectorProvider[], onSelect: (providerId: string) => void, onCancel: () => void);
26
+ private showAuthTypeLabels;
27
+ constructor(mode: "login" | "logout", providers: AuthSelectorProvider[], onSelect: (providerId: string, authType: AuthSelectorProvider["authType"]) => void, onCancel: () => void, initialSearchInput?: string);
25
28
  private filterProviders;
26
29
  private updateList;
27
30
  private formatStatusIndicator;
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/oauth-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAMd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAKjE,MAAM,MAAM,oBAAoB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAU,YAAW,SAAS;IACzE,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,iBAAiB,CAAyB;IAClD,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,gBAAgB,CAAa;IAErC,YACC,IAAI,EAAE,OAAO,GAAG,QAAQ,EACxB,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,oBAAoB,EAAE,EACjC,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,EACtC,QAAQ,EAAE,MAAM,IAAI,EAyCpB;IAED,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,UAAU;IA+ClB,OAAO,CAAC,qBAAqB;IA0B7B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CA8BjC;CACD","sourcesContent":["import {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tTruncatedText,\n} from \"@mariozechner/pi-tui\";\nimport type { AuthStorage } from \"../../../core/auth-storage.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { getAuthSelectorIndicator } from \"./auth-selector-status.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\nexport type AuthSelectorProvider = {\n\tid: string;\n\tname: string;\n\tauthType: \"oauth\" | \"api_key\";\n};\n\n/**\n * Component that renders an auth provider selector\n */\nexport class OAuthSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to search 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.searchInput.focused = value;\n\t}\n\n\tprivate listContainer: Container;\n\tprivate allProviders: AuthSelectorProvider[];\n\tprivate filteredProviders: AuthSelectorProvider[];\n\tprivate selectedIndex: number = 0;\n\tprivate mode: \"login\" | \"logout\";\n\tprivate authStorage: AuthStorage;\n\tprivate onSelectCallback: (providerId: string) => void;\n\tprivate onCancelCallback: () => void;\n\n\tconstructor(\n\t\tmode: \"login\" | \"logout\",\n\t\tauthStorage: AuthStorage,\n\t\tproviders: AuthSelectorProvider[],\n\t\tonSelect: (providerId: string) => void,\n\t\tonCancel: () => void,\n\t) {\n\t\tsuper();\n\n\t\tthis.mode = mode;\n\t\tthis.authStorage = authStorage;\n\t\tthis.allProviders = providers;\n\t\tthis.filteredProviders = providers;\n\t\tthis.onSelectCallback = onSelect;\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\tconst title = mode === \"login\" ? \"Select provider to configure:\" : \"Select provider to logout:\";\n\t\tthis.addChild(new TruncatedText(theme.fg(\"accent\", theme.bold(title)), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.searchInput = new Input();\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\tconst selectedProvider = this.filteredProviders[this.selectedIndex];\n\t\t\tif (selectedProvider) {\n\t\t\t\tthis.onSelectCallback(selectedProvider.id);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Initial render\n\t\tthis.filterProviders(\"\");\n\t}\n\n\tprivate filterProviders(query: string): void {\n\t\tthis.filteredProviders = query\n\t\t\t? fuzzyFilter(this.allProviders, query, (provider) => `${provider.name} ${provider.id} ${provider.authType}`)\n\t\t\t: this.allProviders;\n\t\tthis.selectedIndex = Math.max(0, Math.min(this.selectedIndex, Math.max(0, this.filteredProviders.length - 1)));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 8;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredProviders.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredProviders.length);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst provider = this.filteredProviders[i];\n\t\t\tif (!provider) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\tconst statusIndicator = this.formatStatusIndicator(provider);\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst text = theme.fg(\"accent\", provider.name);\n\t\t\t\tline = prefix + text + statusIndicator;\n\t\t\t} else {\n\t\t\t\tconst text = ` ${theme.fg(\"text\", provider.name)}`;\n\t\t\t\tline = text + statusIndicator;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new TruncatedText(line, 1, 0));\n\t\t}\n\n\t\tif (startIndex > 0 || endIndex < this.filteredProviders.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredProviders.length})`);\n\t\t\tthis.listContainer.addChild(new TruncatedText(scrollInfo, 1, 0));\n\t\t}\n\n\t\t// Show \"no providers\" if empty\n\t\tif (this.filteredProviders.length === 0) {\n\t\t\tconst message =\n\t\t\t\tthis.allProviders.length === 0\n\t\t\t\t\t? this.mode === \"login\"\n\t\t\t\t\t\t? \"No providers available\"\n\t\t\t\t\t\t: \"No providers logged in. Use /login first.\"\n\t\t\t\t\t: \"No matching providers\";\n\t\t\tthis.listContainer.addChild(new TruncatedText(theme.fg(\"muted\", ` ${message}`), 1, 0));\n\t\t}\n\t}\n\n\tprivate formatStatusIndicator(provider: AuthSelectorProvider): string {\n\t\tconst status = provider.authType === \"api_key\" ? this.authStorage.getAuthStatus(provider.id) : undefined;\n\t\tconst indicator = getAuthSelectorIndicator(provider.authType, this.authStorage.get(provider.id), status);\n\n\t\tif (indicator.kind === \"configured\") {\n\t\t\treturn theme.fg(\"success\", ` ✓ ${indicator.label}`);\n\t\t}\n\n\t\tif (indicator.kind === \"configured-other\") {\n\t\t\treturn theme.fg(\"muted\", \" • \") + theme.fg(\"warning\", indicator.label);\n\t\t}\n\n\t\tconst base = theme.fg(\"muted\", \" • unconfigured\");\n\t\tif (indicator.kind === \"environment\") {\n\t\t\treturn base + theme.fg(\"success\", ` · env: ${indicator.label}`);\n\t\t}\n\t\tif (indicator.kind === \"runtime\") {\n\t\t\treturn base + theme.fg(\"success\", \" · runtime API key\");\n\t\t}\n\t\tif (indicator.kind === \"fallback\") {\n\t\t\treturn base + theme.fg(\"success\", \" · custom API key\");\n\t\t}\n\n\t\treturn base;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\t// Up arrow\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredProviders.length === 0) return;\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredProviders.length === 0) return;\n\t\t\tthis.selectedIndex = Math.min(this.filteredProviders.length - 1, this.selectedIndex + 1);\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedProvider = this.filteredProviders[this.selectedIndex];\n\t\t\tif (selectedProvider) {\n\t\t\t\tthis.onSelectCallback(selectedProvider.id);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterProviders(this.searchInput.getValue());\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"oauth-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/oauth-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAMd,MAAM,0BAA0B,CAAC;AAIlC,MAAM,MAAM,oBAAoB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,oBAAoB,CAAC,UAAU,CAAC,GAAG,MAAM,CAEjG;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAU,YAAW,SAAS;IACzE,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,iBAAiB,CAAyB;IAClD,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,gBAAgB,CAA2E;IACnG,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,kBAAkB,CAAU;IAEpC,YACC,IAAI,EAAE,OAAO,GAAG,QAAQ,EACxB,SAAS,EAAE,oBAAoB,EAAE,EACjC,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,CAAC,UAAU,CAAC,KAAK,IAAI,EAClF,QAAQ,EAAE,MAAM,IAAI,EACpB,kBAAkB,CAAC,EAAE,MAAM,EA4C3B;IAED,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,UAAU;IAkDlB,OAAO,CAAC,qBAAqB;IAmB7B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CA8BjC;CACD","sourcesContent":["import type { ApiKeyAuth, AuthCheck, OAuthAuth } from \"@sammorrowdrums/mcpi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tTruncatedText,\n} from \"@sammorrowdrums/mcpi-tui\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\n\nexport type AuthSelectorProvider = {\n\tid: string;\n\tname: string;\n\tauthType: \"oauth\" | \"api_key\";\n\tmethod?: ApiKeyAuth | OAuthAuth;\n\tstatus?: AuthCheck;\n};\n\nexport function formatAuthSelectorProviderType(authType: AuthSelectorProvider[\"authType\"]): string {\n\treturn authType === \"oauth\" ? \"subscription\" : \"API key\";\n}\n\n/**\n * Component that renders an auth provider selector\n */\nexport class OAuthSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to search 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.searchInput.focused = value;\n\t}\n\n\tprivate listContainer: Container;\n\tprivate allProviders: AuthSelectorProvider[];\n\tprivate filteredProviders: AuthSelectorProvider[];\n\tprivate selectedIndex: number = 0;\n\tprivate mode: \"login\" | \"logout\";\n\tprivate onSelectCallback: (providerId: string, authType: AuthSelectorProvider[\"authType\"]) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate showAuthTypeLabels: boolean;\n\n\tconstructor(\n\t\tmode: \"login\" | \"logout\",\n\t\tproviders: AuthSelectorProvider[],\n\t\tonSelect: (providerId: string, authType: AuthSelectorProvider[\"authType\"]) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.mode = mode;\n\t\tthis.allProviders = providers;\n\t\tthis.filteredProviders = providers;\n\t\tthis.showAuthTypeLabels = new Set(providers.map((provider) => provider.authType)).size > 1;\n\t\tthis.onSelectCallback = onSelect;\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\tconst title = mode === \"login\" ? \"Select provider to configure:\" : \"Select provider to logout:\";\n\t\tthis.addChild(new TruncatedText(theme.fg(\"accent\", theme.bold(title)), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\tconst selectedProvider = this.filteredProviders[this.selectedIndex];\n\t\t\tif (selectedProvider) {\n\t\t\t\tthis.onSelectCallback(selectedProvider.id, selectedProvider.authType);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Initial render\n\t\tthis.filterProviders(initialSearchInput ?? \"\");\n\t}\n\n\tprivate filterProviders(query: string): void {\n\t\tthis.filteredProviders = query\n\t\t\t? fuzzyFilter(\n\t\t\t\t\tthis.allProviders,\n\t\t\t\t\tquery,\n\t\t\t\t\t(provider) => `${provider.name} ${provider.id} ${provider.authType} ${provider.method?.name ?? \"\"}`,\n\t\t\t\t)\n\t\t\t: this.allProviders;\n\t\tthis.selectedIndex = Math.max(0, Math.min(this.selectedIndex, Math.max(0, this.filteredProviders.length - 1)));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 8;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredProviders.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredProviders.length);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst provider = this.filteredProviders[i];\n\t\t\tif (!provider) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\tconst statusIndicator = this.formatStatusIndicator(provider);\n\t\t\tconst authTypeLabel = this.showAuthTypeLabels\n\t\t\t\t? theme.fg(\"muted\", ` [${formatAuthSelectorProviderType(provider.authType)}]`)\n\t\t\t\t: \"\";\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst text = theme.fg(\"accent\", provider.name);\n\t\t\t\tline = prefix + text + authTypeLabel + statusIndicator;\n\t\t\t} else {\n\t\t\t\tconst text = ` ${theme.fg(\"text\", provider.name)}`;\n\t\t\t\tline = text + authTypeLabel + statusIndicator;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new TruncatedText(line, 1, 0));\n\t\t}\n\n\t\tif (startIndex > 0 || endIndex < this.filteredProviders.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredProviders.length})`);\n\t\t\tthis.listContainer.addChild(new TruncatedText(scrollInfo, 1, 0));\n\t\t}\n\n\t\t// Show \"no providers\" if empty\n\t\tif (this.filteredProviders.length === 0) {\n\t\t\tconst message =\n\t\t\t\tthis.allProviders.length === 0\n\t\t\t\t\t? this.mode === \"login\"\n\t\t\t\t\t\t? \"No providers available\"\n\t\t\t\t\t\t: \"No providers logged in. Use /login first.\"\n\t\t\t\t\t: \"No matching providers\";\n\t\t\tthis.listContainer.addChild(new TruncatedText(theme.fg(\"muted\", ` ${message}`), 1, 0));\n\t\t}\n\t}\n\n\tprivate formatStatusIndicator(provider: AuthSelectorProvider): string {\n\t\tif (!provider.status) return theme.fg(\"muted\", \" • unconfigured\");\n\t\tif (provider.status.type !== provider.authType) {\n\t\t\tconst label = provider.status.type === \"oauth\" ? \"subscription configured\" : \"API key configured\";\n\t\t\treturn theme.fg(\"muted\", \" • \") + theme.fg(\"warning\", label);\n\t\t}\n\t\tif (\n\t\t\t!provider.status.source ||\n\t\t\tprovider.status.source === \"OAuth\" ||\n\t\t\tprovider.status.source === \"stored credential\"\n\t\t) {\n\t\t\treturn theme.fg(\"success\", \" configured\");\n\t\t}\n\t\tconst source = /^[A-Z][A-Z0-9_]*(?:, [A-Z][A-Z0-9_]*)*$/.test(provider.status.source)\n\t\t\t? `env: ${provider.status.source}`\n\t\t\t: provider.status.source;\n\t\treturn theme.fg(\"success\", ` ${source}`);\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\t// Up arrow\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredProviders.length === 0) return;\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredProviders.length === 0) return;\n\t\t\tthis.selectedIndex = Math.min(this.filteredProviders.length - 1, this.selectedIndex + 1);\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedProvider = this.filteredProviders[this.selectedIndex];\n\t\t\tif (selectedProvider) {\n\t\t\t\tthis.onSelectCallback(selectedProvider.id, selectedProvider.authType);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterProviders(this.searchInput.getValue());\n\t\t}\n\t}\n}\n"]}