alloy-runtime-cli 0.1.0__py3-none-any.whl

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 (451) hide show
  1. alloy_runtime_cli-0.1.0.dist-info/METADATA +61 -0
  2. alloy_runtime_cli-0.1.0.dist-info/RECORD +451 -0
  3. alloy_runtime_cli-0.1.0.dist-info/WHEEL +5 -0
  4. alloy_runtime_cli-0.1.0.dist-info/entry_points.txt +2 -0
  5. alloy_runtime_cli-0.1.0.dist-info/top_level.txt +1 -0
  6. cli/__init__.py +0 -0
  7. cli/commands/__init__.py +0 -0
  8. cli/commands/admin/__init__.py +0 -0
  9. cli/commands/admin/bootstrap_command.py +118 -0
  10. cli/commands/admin/credentials/__init__.py +0 -0
  11. cli/commands/admin/credentials/create/__init__.py +0 -0
  12. cli/commands/admin/credentials/create/command.py +148 -0
  13. cli/commands/admin/credentials/create/presenter.py +16 -0
  14. cli/commands/admin/credentials/grant/__init__.py +0 -0
  15. cli/commands/admin/credentials/grant/command.py +119 -0
  16. cli/commands/admin/credentials/grant/fields.py +33 -0
  17. cli/commands/admin/credentials/grant/presenter.py +23 -0
  18. cli/commands/agents/__init__.py +0 -0
  19. cli/commands/agents/create/__init__.py +0 -0
  20. cli/commands/agents/create/command.py +475 -0
  21. cli/commands/agents/create/fields.py +64 -0
  22. cli/commands/agents/create/presenter.py +68 -0
  23. cli/commands/agents/delete/__init__.py +0 -0
  24. cli/commands/agents/delete/command.py +47 -0
  25. cli/commands/agents/delete/presenter.py +16 -0
  26. cli/commands/agents/get/command.py +37 -0
  27. cli/commands/agents/get/presenter.py +32 -0
  28. cli/commands/agents/list/__init__.py +1 -0
  29. cli/commands/agents/list/command.py +54 -0
  30. cli/commands/agents/list/presenter.py +82 -0
  31. cli/commands/agents/update/__init__.py +0 -0
  32. cli/commands/agents/update/command.py +435 -0
  33. cli/commands/agents/update/fields.py +40 -0
  34. cli/commands/agents/update/presenter.py +68 -0
  35. cli/commands/audio/__init__.py +0 -0
  36. cli/commands/audio/transcribe/__init__.py +0 -0
  37. cli/commands/audio/transcribe/command.py +144 -0
  38. cli/commands/audio/transcribe/presenter.py +15 -0
  39. cli/commands/auth/__init__.py +0 -0
  40. cli/commands/auth/login/__init__.py +0 -0
  41. cli/commands/auth/login/command.py +80 -0
  42. cli/commands/auth/signup/__init__.py +0 -0
  43. cli/commands/auth/signup/command.py +115 -0
  44. cli/commands/billing/__init__.py +1 -0
  45. cli/commands/billing/costs/__init__.py +1 -0
  46. cli/commands/billing/costs/by_agent/__init__.py +1 -0
  47. cli/commands/billing/costs/by_agent/command.py +57 -0
  48. cli/commands/billing/costs/by_agent/presenter.py +81 -0
  49. cli/commands/billing/costs/by_model/__init__.py +1 -0
  50. cli/commands/billing/costs/by_model/command.py +57 -0
  51. cli/commands/billing/costs/by_model/presenter.py +80 -0
  52. cli/commands/billing/costs/daily/__init__.py +1 -0
  53. cli/commands/billing/costs/daily/command.py +55 -0
  54. cli/commands/billing/costs/daily/presenter.py +75 -0
  55. cli/commands/billing/costs/summary/__init__.py +1 -0
  56. cli/commands/billing/costs/summary/command.py +57 -0
  57. cli/commands/billing/costs/summary/presenter.py +42 -0
  58. cli/commands/billing/projects/__init__.py +1 -0
  59. cli/commands/billing/projects/create/__init__.py +1 -0
  60. cli/commands/billing/projects/create/command.py +60 -0
  61. cli/commands/billing/projects/create/presenter.py +26 -0
  62. cli/commands/billing/projects/get/__init__.py +1 -0
  63. cli/commands/billing/projects/get/command.py +33 -0
  64. cli/commands/billing/projects/get/presenter.py +32 -0
  65. cli/commands/billing/projects/list/__init__.py +1 -0
  66. cli/commands/billing/projects/list/command.py +40 -0
  67. cli/commands/billing/projects/list/presenter.py +57 -0
  68. cli/commands/content/__init__.py +1 -0
  69. cli/commands/content/delete/__init__.py +0 -0
  70. cli/commands/content/delete/command.py +49 -0
  71. cli/commands/content/delete/presenter.py +18 -0
  72. cli/commands/content/edit/__init__.py +1 -0
  73. cli/commands/content/edit/command.py +155 -0
  74. cli/commands/content/edit/editor.py +150 -0
  75. cli/commands/content/edit/presenter.py +146 -0
  76. cli/commands/content/get/__init__.py +1 -0
  77. cli/commands/content/get/command.py +39 -0
  78. cli/commands/content/get/presenter.py +176 -0
  79. cli/commands/content/list/__init__.py +1 -0
  80. cli/commands/content/list/command.py +347 -0
  81. cli/commands/content/list/export_formatters.py +409 -0
  82. cli/commands/content/list/export_handler.py +165 -0
  83. cli/commands/content/list/presenter.py +190 -0
  84. cli/commands/credentials/__init__.py +0 -0
  85. cli/commands/credentials/create/__init__.py +0 -0
  86. cli/commands/credentials/create/command.py +165 -0
  87. cli/commands/credentials/create/fields.py +38 -0
  88. cli/commands/credentials/create/presenter.py +20 -0
  89. cli/commands/credentials/update/__init__.py +0 -0
  90. cli/commands/credentials/update/command.py +53 -0
  91. cli/commands/credentials/update/fields.py +71 -0
  92. cli/commands/credentials/update/presenter.py +16 -0
  93. cli/commands/flag_utils.py +366 -0
  94. cli/commands/generate/__init__.py +0 -0
  95. cli/commands/generate/cancel/__init__.py +1 -0
  96. cli/commands/generate/cancel/command.py +44 -0
  97. cli/commands/generate/cancel/presenter.py +26 -0
  98. cli/commands/generate/status/__init__.py +1 -0
  99. cli/commands/generate/status/command.py +58 -0
  100. cli/commands/generate/status/presenter.py +78 -0
  101. cli/commands/generate/text/__init__.py +0 -0
  102. cli/commands/generate/text/command.py +1325 -0
  103. cli/commands/generate/text/concurrent_renderer.py +355 -0
  104. cli/commands/generate/text/presenter.py +287 -0
  105. cli/commands/generate/text/stream_renderer.py +129 -0
  106. cli/commands/knowledge/__init__.py +0 -0
  107. cli/commands/knowledge/collections/__init__.py +0 -0
  108. cli/commands/knowledge/collections/cluster/__init__.py +0 -0
  109. cli/commands/knowledge/collections/cluster/command.py +64 -0
  110. cli/commands/knowledge/collections/cluster/presenter.py +74 -0
  111. cli/commands/knowledge/collections/cluster_status/__init__.py +0 -0
  112. cli/commands/knowledge/collections/cluster_status/command.py +46 -0
  113. cli/commands/knowledge/collections/cluster_status/presenter.py +10 -0
  114. cli/commands/knowledge/collections/create/__init__.py +0 -0
  115. cli/commands/knowledge/collections/create/command.py +137 -0
  116. cli/commands/knowledge/collections/create/presenter.py +38 -0
  117. cli/commands/knowledge/collections/delete/__init__.py +1 -0
  118. cli/commands/knowledge/collections/delete/command.py +47 -0
  119. cli/commands/knowledge/collections/delete/presenter.py +20 -0
  120. cli/commands/knowledge/collections/get/__init__.py +1 -0
  121. cli/commands/knowledge/collections/get/command.py +30 -0
  122. cli/commands/knowledge/collections/get/presenter.py +44 -0
  123. cli/commands/knowledge/collections/list/__init__.py +1 -0
  124. cli/commands/knowledge/collections/list/command.py +41 -0
  125. cli/commands/knowledge/collections/list/presenter.py +68 -0
  126. cli/commands/knowledge/collections/update/__init__.py +0 -0
  127. cli/commands/knowledge/collections/update/command.py +97 -0
  128. cli/commands/knowledge/collections/update/presenter.py +42 -0
  129. cli/commands/knowledge/documents/__init__.py +0 -0
  130. cli/commands/knowledge/documents/bulk_metadata/__init__.py +0 -0
  131. cli/commands/knowledge/documents/bulk_metadata/command.py +119 -0
  132. cli/commands/knowledge/documents/bulk_metadata/presenter.py +36 -0
  133. cli/commands/knowledge/documents/delete/__init__.py +0 -0
  134. cli/commands/knowledge/documents/delete/command.py +47 -0
  135. cli/commands/knowledge/documents/delete/presenter.py +20 -0
  136. cli/commands/knowledge/documents/get/__init__.py +0 -0
  137. cli/commands/knowledge/documents/get/command.py +39 -0
  138. cli/commands/knowledge/documents/get/presenter.py +78 -0
  139. cli/commands/knowledge/documents/ingest/__init__.py +0 -0
  140. cli/commands/knowledge/documents/ingest/command.py +222 -0
  141. cli/commands/knowledge/documents/ingest/presenter.py +41 -0
  142. cli/commands/knowledge/documents/list/__init__.py +0 -0
  143. cli/commands/knowledge/documents/list/command.py +69 -0
  144. cli/commands/knowledge/documents/list/presenter.py +86 -0
  145. cli/commands/knowledge/documents/reingest/__init__.py +0 -0
  146. cli/commands/knowledge/documents/reingest/command.py +102 -0
  147. cli/commands/knowledge/documents/reingest/presenter.py +70 -0
  148. cli/commands/knowledge/documents/update/__init__.py +0 -0
  149. cli/commands/knowledge/documents/update/command.py +85 -0
  150. cli/commands/knowledge/documents/update/presenter.py +37 -0
  151. cli/commands/knowledge/recover/__init__.py +0 -0
  152. cli/commands/knowledge/recover/command.py +46 -0
  153. cli/commands/knowledge/recover/presenter.py +79 -0
  154. cli/commands/knowledge/search/__init__.py +0 -0
  155. cli/commands/knowledge/search/command.py +218 -0
  156. cli/commands/knowledge/search/presenter.py +111 -0
  157. cli/commands/knowledge/synthesis/__init__.py +0 -0
  158. cli/commands/knowledge/synthesis/create/__init__.py +0 -0
  159. cli/commands/knowledge/synthesis/create/command.py +127 -0
  160. cli/commands/knowledge/synthesis/create/presenter.py +33 -0
  161. cli/commands/knowledge/synthesis/delete/__init__.py +0 -0
  162. cli/commands/knowledge/synthesis/delete/command.py +53 -0
  163. cli/commands/knowledge/synthesis/delete/presenter.py +31 -0
  164. cli/commands/knowledge/synthesis/get/__init__.py +0 -0
  165. cli/commands/knowledge/synthesis/get/command.py +55 -0
  166. cli/commands/knowledge/synthesis/get/presenter.py +114 -0
  167. cli/commands/knowledge/synthesis/list/__init__.py +0 -0
  168. cli/commands/knowledge/synthesis/list/command.py +132 -0
  169. cli/commands/knowledge/synthesis/list/presenter.py +84 -0
  170. cli/commands/knowledge/synthesis/refresh/__init__.py +0 -0
  171. cli/commands/knowledge/synthesis/refresh/command.py +42 -0
  172. cli/commands/knowledge/synthesis/refresh/presenter.py +33 -0
  173. cli/commands/knowledge/synthesis/update/__init__.py +0 -0
  174. cli/commands/knowledge/synthesis/update/command.py +76 -0
  175. cli/commands/knowledge/synthesis/update/presenter.py +41 -0
  176. cli/commands/models/__init__.py +0 -0
  177. cli/commands/models/list/__init__.py +0 -0
  178. cli/commands/models/list/command.py +84 -0
  179. cli/commands/models/list/presenter.py +114 -0
  180. cli/commands/organizations/__init__.py +0 -0
  181. cli/commands/organizations/create/command.py +32 -0
  182. cli/commands/organizations/create/presenter.py +9 -0
  183. cli/commands/pipelines/__init__.py +1 -0
  184. cli/commands/pipelines/approvals/__init__.py +1 -0
  185. cli/commands/pipelines/approvals/decide_command.py +77 -0
  186. cli/commands/pipelines/approvals/get_command.py +44 -0
  187. cli/commands/pipelines/approvals/presenter.py +56 -0
  188. cli/commands/pipelines/costs/__init__.py +1 -0
  189. cli/commands/pipelines/costs/command.py +57 -0
  190. cli/commands/pipelines/costs/daily_command.py +54 -0
  191. cli/commands/pipelines/costs/daily_presenter.py +59 -0
  192. cli/commands/pipelines/costs/presenter.py +37 -0
  193. cli/commands/pipelines/create/__init__.py +1 -0
  194. cli/commands/pipelines/create/command.py +103 -0
  195. cli/commands/pipelines/create/presenter.py +22 -0
  196. cli/commands/pipelines/env_vars/__init__.py +1 -0
  197. cli/commands/pipelines/env_vars/command.py +51 -0
  198. cli/commands/pipelines/env_vars/presenter.py +16 -0
  199. cli/commands/pipelines/execute/__init__.py +1 -0
  200. cli/commands/pipelines/execute/command.py +142 -0
  201. cli/commands/pipelines/execute/presenter.py +47 -0
  202. cli/commands/pipelines/executions/__init__.py +1 -0
  203. cli/commands/pipelines/executions/costs/__init__.py +1 -0
  204. cli/commands/pipelines/executions/costs/command.py +48 -0
  205. cli/commands/pipelines/executions/costs/presenter.py +29 -0
  206. cli/commands/pipelines/executions/costs_by_model/__init__.py +1 -0
  207. cli/commands/pipelines/executions/costs_by_model/command.py +50 -0
  208. cli/commands/pipelines/executions/costs_by_model/presenter.py +78 -0
  209. cli/commands/pipelines/executions/costs_by_step/__init__.py +1 -0
  210. cli/commands/pipelines/executions/costs_by_step/command.py +50 -0
  211. cli/commands/pipelines/executions/costs_by_step/presenter.py +72 -0
  212. cli/commands/pipelines/executions/get_command.py +38 -0
  213. cli/commands/pipelines/executions/list_command.py +123 -0
  214. cli/commands/pipelines/executions/presenter.py +131 -0
  215. cli/commands/pipelines/executions/rerun_command.py +41 -0
  216. cli/commands/pipelines/executions/update/__init__.py +1 -0
  217. cli/commands/pipelines/executions/update/command.py +110 -0
  218. cli/commands/pipelines/executions/update/presenter.py +28 -0
  219. cli/commands/pipelines/get/__init__.py +1 -0
  220. cli/commands/pipelines/get/command.py +33 -0
  221. cli/commands/pipelines/get/presenter.py +48 -0
  222. cli/commands/pipelines/list/__init__.py +1 -0
  223. cli/commands/pipelines/list/command.py +53 -0
  224. cli/commands/pipelines/list/presenter.py +66 -0
  225. cli/commands/pipelines/schedules/__init__.py +1 -0
  226. cli/commands/pipelines/schedules/create_command.py +119 -0
  227. cli/commands/pipelines/schedules/create_presenter.py +35 -0
  228. cli/commands/pipelines/schedules/delete_command.py +52 -0
  229. cli/commands/pipelines/schedules/env_vars_command.py +59 -0
  230. cli/commands/pipelines/schedules/env_vars_presenter.py +16 -0
  231. cli/commands/pipelines/schedules/get_command.py +38 -0
  232. cli/commands/pipelines/schedules/list_command.py +33 -0
  233. cli/commands/pipelines/schedules/once_command.py +90 -0
  234. cli/commands/pipelines/schedules/once_presenter.py +30 -0
  235. cli/commands/pipelines/schedules/presenter.py +104 -0
  236. cli/commands/pipelines/schedules/update_command.py +139 -0
  237. cli/commands/pipelines/schedules/update_presenter.py +29 -0
  238. cli/commands/render/__init__.py +0 -0
  239. cli/commands/render/html_to_image/__init__.py +0 -0
  240. cli/commands/render/html_to_image/command.py +170 -0
  241. cli/commands/schemas/__init__.py +0 -0
  242. cli/commands/schemas/create/__init__.py +0 -0
  243. cli/commands/schemas/create/command.py +122 -0
  244. cli/commands/schemas/create/presenter.py +53 -0
  245. cli/commands/schemas/delete/command.py +45 -0
  246. cli/commands/schemas/delete/presenter.py +9 -0
  247. cli/commands/schemas/get/__init__.py +0 -0
  248. cli/commands/schemas/get/command.py +56 -0
  249. cli/commands/schemas/get/presenter.py +129 -0
  250. cli/commands/schemas/list/__init__.py +0 -0
  251. cli/commands/schemas/list/command.py +64 -0
  252. cli/commands/schemas/list/presenter.py +133 -0
  253. cli/commands/schemas/update/__init__.py +0 -0
  254. cli/commands/schemas/update/command.py +369 -0
  255. cli/commands/schemas/update/presenter.py +53 -0
  256. cli/commands/sessions/__init__.py +1 -0
  257. cli/commands/sessions/delete/__init__.py +1 -0
  258. cli/commands/sessions/delete/command.py +47 -0
  259. cli/commands/sessions/delete/presenter.py +10 -0
  260. cli/commands/sessions/get/__init__.py +1 -0
  261. cli/commands/sessions/get/command.py +42 -0
  262. cli/commands/sessions/get/presenter.py +59 -0
  263. cli/commands/sessions/list/__init__.py +1 -0
  264. cli/commands/sessions/list/command.py +61 -0
  265. cli/commands/sessions/list/presenter.py +68 -0
  266. cli/commands/sessions/messages/__init__.py +1 -0
  267. cli/commands/sessions/messages/command.py +78 -0
  268. cli/commands/sessions/messages/presenter.py +79 -0
  269. cli/commands/shared_flags.py +500 -0
  270. cli/commands/sync/__init__.py +0 -0
  271. cli/commands/sync/command.py +45 -0
  272. cli/commands/sync/presenter.py +49 -0
  273. cli/commands/tags/__init__.py +1 -0
  274. cli/commands/tags/create/__init__.py +1 -0
  275. cli/commands/tags/create/command.py +60 -0
  276. cli/commands/tags/delete/__init__.py +1 -0
  277. cli/commands/tags/delete/command.py +47 -0
  278. cli/commands/tags/delete/presenter.py +10 -0
  279. cli/commands/tags/get/command.py +31 -0
  280. cli/commands/tags/get/presenter.py +23 -0
  281. cli/commands/tags/list/__init__.py +1 -0
  282. cli/commands/tags/list/command.py +52 -0
  283. cli/commands/tags/list/presenter.py +49 -0
  284. cli/commands/tags/update/command.py +64 -0
  285. cli/commands/tags/update/presenter.py +9 -0
  286. cli/commands/templates/__init__.py +0 -0
  287. cli/commands/templates/create/__init__.py +0 -0
  288. cli/commands/templates/create/command.py +152 -0
  289. cli/commands/templates/create/presenter.py +86 -0
  290. cli/commands/templates/delete/__init__.py +0 -0
  291. cli/commands/templates/delete/command.py +47 -0
  292. cli/commands/templates/delete/presenter.py +16 -0
  293. cli/commands/templates/get/__init__.py +0 -0
  294. cli/commands/templates/get/command.py +52 -0
  295. cli/commands/templates/get/presenter.py +233 -0
  296. cli/commands/templates/get_by_version/command.py +32 -0
  297. cli/commands/templates/get_by_version/presenter.py +30 -0
  298. cli/commands/templates/list/__init__.py +1 -0
  299. cli/commands/templates/list/command.py +102 -0
  300. cli/commands/templates/list/presenter.py +93 -0
  301. cli/commands/templates/render/__init__.py +0 -0
  302. cli/commands/templates/render/command.py +115 -0
  303. cli/commands/templates/render/presenter.py +276 -0
  304. cli/commands/templates/update/__init__.py +0 -0
  305. cli/commands/templates/update/command.py +199 -0
  306. cli/commands/templates/update/presenter.py +94 -0
  307. cli/commands/templates/version/__init__.py +1 -0
  308. cli/commands/templates/version/command.py +116 -0
  309. cli/commands/templates/version/presenter.py +100 -0
  310. cli/commands/tool_configs/__init__.py +0 -0
  311. cli/commands/tool_configs/create/__init__.py +0 -0
  312. cli/commands/tool_configs/create/command.py +118 -0
  313. cli/commands/tool_configs/create/presenter.py +53 -0
  314. cli/commands/tool_configs/delete/__init__.py +0 -0
  315. cli/commands/tool_configs/delete/command.py +47 -0
  316. cli/commands/tool_configs/delete/presenter.py +18 -0
  317. cli/commands/tool_configs/get/__init__.py +0 -0
  318. cli/commands/tool_configs/get/command.py +31 -0
  319. cli/commands/tool_configs/get/presenter.py +62 -0
  320. cli/commands/tool_configs/list/__init__.py +0 -0
  321. cli/commands/tool_configs/list/command.py +59 -0
  322. cli/commands/tool_configs/list/presenter.py +60 -0
  323. cli/commands/tool_configs/update/__init__.py +0 -0
  324. cli/commands/tool_configs/update/command.py +128 -0
  325. cli/commands/tool_configs/update/presenter.py +53 -0
  326. cli/commands/tools/__init__.py +1 -0
  327. cli/commands/tools/get/__init__.py +1 -0
  328. cli/commands/tools/get/command.py +42 -0
  329. cli/commands/tools/get/presenter.py +45 -0
  330. cli/commands/tools/list/__init__.py +1 -0
  331. cli/commands/tools/list/command.py +56 -0
  332. cli/commands/tools/list/presenter.py +44 -0
  333. cli/commands/users/__init__.py +0 -0
  334. cli/commands/users/create/command.py +53 -0
  335. cli/commands/users/create/presenter.py +9 -0
  336. cli/commands/whoami/__init__.py +0 -0
  337. cli/commands/whoami/command.py +42 -0
  338. cli/infrastructure/__init__.py +0 -0
  339. cli/infrastructure/auth_storage.py +71 -0
  340. cli/infrastructure/client_factory.py +36 -0
  341. cli/infrastructure/command.py +75 -0
  342. cli/infrastructure/config.py +188 -0
  343. cli/infrastructure/console.py +27 -0
  344. cli/infrastructure/editor.py +138 -0
  345. cli/infrastructure/error_display.py +178 -0
  346. cli/infrastructure/field_extractor.py +360 -0
  347. cli/infrastructure/file_content.py +210 -0
  348. cli/infrastructure/filter_parser.py +256 -0
  349. cli/infrastructure/formatters/__init__.py +0 -0
  350. cli/infrastructure/formatters/base.py +99 -0
  351. cli/infrastructure/formatters/compact_formatter.py +245 -0
  352. cli/infrastructure/formatters/json_formatter.py +84 -0
  353. cli/infrastructure/formatters/lines_formatter.py +102 -0
  354. cli/infrastructure/formatting/__init__.py +0 -0
  355. cli/infrastructure/formatting/fields.py +193 -0
  356. cli/infrastructure/forms/__init__.py +0 -0
  357. cli/infrastructure/forms/agent_picker.py +123 -0
  358. cli/infrastructure/forms/agent_tool_editor.py +384 -0
  359. cli/infrastructure/forms/agent_tools_manager.py +212 -0
  360. cli/infrastructure/forms/base_picker.py +469 -0
  361. cli/infrastructure/forms/components.py +126 -0
  362. cli/infrastructure/forms/json_schema_builder.py +149 -0
  363. cli/infrastructure/forms/model_picker.py +134 -0
  364. cli/infrastructure/forms/parsers.py +173 -0
  365. cli/infrastructure/forms/resolution_modal.py +302 -0
  366. cli/infrastructure/forms/schema_picker.py +137 -0
  367. cli/infrastructure/forms/tag_management_modal.py +103 -0
  368. cli/infrastructure/forms/tag_picker.py +207 -0
  369. cli/infrastructure/forms/template_picker.py +131 -0
  370. cli/infrastructure/forms/tool_config_picker.py +130 -0
  371. cli/infrastructure/forms/tool_picker.py +103 -0
  372. cli/infrastructure/injection/__init__.py +0 -0
  373. cli/infrastructure/injection/parser.py +302 -0
  374. cli/infrastructure/injection/resolver.py +399 -0
  375. cli/infrastructure/kv_parser.py +130 -0
  376. cli/infrastructure/local_storage.py +227 -0
  377. cli/infrastructure/macro_parser.py +215 -0
  378. cli/infrastructure/output.py +192 -0
  379. cli/infrastructure/provider_setup.py +81 -0
  380. cli/infrastructure/renderers/__init__.py +0 -0
  381. cli/infrastructure/renderers/entity_renderer.py +77 -0
  382. cli/infrastructure/renderers/list_renderer.py +114 -0
  383. cli/infrastructure/scope_utils.py +47 -0
  384. cli/infrastructure/spinner.py +101 -0
  385. cli/infrastructure/tui/__init__.py +0 -0
  386. cli/infrastructure/tui/clipboard.py +41 -0
  387. cli/infrastructure/tui/formatters.py +105 -0
  388. cli/infrastructure/tui/preview.py +14 -0
  389. cli/infrastructure/tui/selectable.py +198 -0
  390. cli/infrastructure/validation/__init__.py +0 -0
  391. cli/infrastructure/validation/tag_validation.py +74 -0
  392. cli/main.py +759 -0
  393. cli/tui/__init__.py +0 -0
  394. cli/tui/app.py +199 -0
  395. cli/tui/app_store.py +73 -0
  396. cli/tui/chat/__init__.py +0 -0
  397. cli/tui/chat/commands/__init__.py +0 -0
  398. cli/tui/chat/commands/base.py +65 -0
  399. cli/tui/chat/commands/create_session.py +135 -0
  400. cli/tui/chat/commands/load_session.py +119 -0
  401. cli/tui/chat/commands/regenerate.py +120 -0
  402. cli/tui/chat/commands/reload_session.py +63 -0
  403. cli/tui/chat/commands/send_message.py +190 -0
  404. cli/tui/chat/commands/undo.py +66 -0
  405. cli/tui/chat/editor.py +71 -0
  406. cli/tui/chat/messages.py +223 -0
  407. cli/tui/chat/pane.py +141 -0
  408. cli/tui/chat/renderers/__init__.py +0 -0
  409. cli/tui/chat/renderers/base.py +72 -0
  410. cli/tui/chat/renderers/markdown.py +250 -0
  411. cli/tui/chat/renderers/plain.py +83 -0
  412. cli/tui/chat/screen.py +1155 -0
  413. cli/tui/chat/services/__init__.py +0 -0
  414. cli/tui/chat/services/injection.py +386 -0
  415. cli/tui/chat/services/name_generator.py +256 -0
  416. cli/tui/chat/slash_commands.py +424 -0
  417. cli/tui/chat/store.py +280 -0
  418. cli/tui/chat/types.py +220 -0
  419. cli/tui/chat/widgets/__init__.py +0 -0
  420. cli/tui/chat/widgets/chat_header.py +75 -0
  421. cli/tui/chat/widgets/chat_input.py +362 -0
  422. cli/tui/chat/widgets/injection_popup.py +161 -0
  423. cli/tui/chat/widgets/message_display.py +287 -0
  424. cli/tui/chat/widgets/session_sidebar.py +214 -0
  425. cli/tui/chat/widgets/welcome_screen.py +290 -0
  426. cli/tui/screens/__init__.py +0 -0
  427. cli/tui/screens/agents.py +344 -0
  428. cli/tui/screens/base.py +301 -0
  429. cli/tui/screens/content.py +508 -0
  430. cli/tui/screens/dashboard.py +89 -0
  431. cli/tui/screens/models.py +96 -0
  432. cli/tui/screens/nav_screen.py +186 -0
  433. cli/tui/screens/schemas.py +522 -0
  434. cli/tui/screens/templates.py +734 -0
  435. cli/tui/screens/tool_configs.py +335 -0
  436. cli/tui/styles/__init__.py +0 -0
  437. cli/tui/widgets/__init__.py +0 -0
  438. cli/tui/widgets/agent_create_modal.py +139 -0
  439. cli/tui/widgets/agent_form_modal.py +659 -0
  440. cli/tui/widgets/agent_update_modal.py +299 -0
  441. cli/tui/widgets/base_form_modal.py +77 -0
  442. cli/tui/widgets/confirm_modal.py +75 -0
  443. cli/tui/widgets/help_modal.py +145 -0
  444. cli/tui/widgets/new_session_modal.py +328 -0
  445. cli/tui/widgets/schema_create_modal.py +271 -0
  446. cli/tui/widgets/schema_update_modal.py +188 -0
  447. cli/tui/widgets/status_footer.py +147 -0
  448. cli/tui/widgets/template_create_modal.py +502 -0
  449. cli/tui/widgets/template_update_modal.py +308 -0
  450. cli/tui/widgets/tool_config_create_modal.py +216 -0
  451. cli/tui/widgets/tool_config_update_modal.py +208 -0
@@ -0,0 +1,207 @@
1
+ """Tag picker widget for selecting multiple tags.
2
+
3
+ A multi-select search picker that uses the ApiClient to fetch tags
4
+ and allows selecting multiple tags by their paths. Supports creating
5
+ new tags on-the-fly when the searched tag doesn't exist.
6
+ """
7
+
8
+ from typing import Any
9
+
10
+ from textual.message import Message
11
+ from textual.widgets import OptionList
12
+ from textual.widgets.option_list import Option
13
+
14
+ from alloy_runtime_sdk.api_client.client import ApiClient
15
+
16
+ from cli.infrastructure.forms.base_picker import MultiSelectPicker
17
+ from cli.infrastructure.validation.tag_validation import validate_tag_path
18
+
19
+
20
+ class TagPicker(MultiSelectPicker[dict[str, Any]]):
21
+ """Multi-select tag picker with live API search and on-the-fly tag creation.
22
+
23
+ Uses the ApiClient to fetch tags on each keystroke. Results appear in a
24
+ list below the search input. User can select multiple tags.
25
+
26
+ When searching for a tag that doesn't exist, the picker shows an option
27
+ to create the new tag. The tag will be auto-created on the server when
28
+ the content is saved.
29
+
30
+ Usage:
31
+ yield TagPicker(
32
+ client=server_client,
33
+ placeholder="Search tags...",
34
+ id="tags",
35
+ )
36
+
37
+ # Get selected tag paths
38
+ picker = self.query_one("#tags", TagPicker)
39
+ tag_paths = picker.selected_tags # list[str]
40
+ """
41
+
42
+ class TagsChanged(Message):
43
+ """Posted when selected tags change."""
44
+
45
+ def __init__(self, selected_tags: list[str]) -> None:
46
+ self.selected_tags = selected_tags
47
+ super().__init__()
48
+
49
+ def __init__(
50
+ self,
51
+ client: ApiClient,
52
+ placeholder: str = "Search tags...",
53
+ empty_message: str = "No tags selected (required)",
54
+ **kwargs: Any,
55
+ ) -> None:
56
+ """Initialize the tag picker.
57
+
58
+ Args:
59
+ client: ApiClient instance for API calls
60
+ placeholder: Placeholder text for search input
61
+ empty_message: Message shown when nothing is selected
62
+ """
63
+ super().__init__(
64
+ client=client,
65
+ placeholder=placeholder,
66
+ empty_message=empty_message,
67
+ **kwargs,
68
+ )
69
+ self._last_search_query: str = ""
70
+
71
+ @property
72
+ def selected_tags(self) -> list[str]:
73
+ """Get the list of selected tag paths."""
74
+ return list(self._selected_items.keys())
75
+
76
+ @property
77
+ def selected_tag_items(self) -> list[dict[str, Any]]:
78
+ """Get the full selected tag dicts."""
79
+ return list(self._selected_items.values())
80
+
81
+ async def _fetch_items(self, query: str) -> list[dict[str, Any]]:
82
+ """Fetch tags matching query from the API."""
83
+ self._last_search_query = query
84
+ response = await self._client.list_tags(search=query, limit=50)
85
+ results = [tag.model_dump() for tag in response.tags]
86
+
87
+ # Check if query exactly matches any existing tag
88
+ exact_match_exists = any(tag.get("tag_path", "") == query for tag in results)
89
+
90
+ # If no exact match, check if we can offer to create the tag
91
+ if not exact_match_exists:
92
+ is_valid, _ = validate_tag_path(query)
93
+ if is_valid:
94
+ # Add synthetic "create new tag" option at the top
95
+ create_option = {
96
+ "tag_path": query,
97
+ "display_name": query,
98
+ "_is_new": True,
99
+ }
100
+ results.insert(0, create_option)
101
+
102
+ return results
103
+
104
+ def _format_item(self, item: dict[str, Any]) -> str:
105
+ """Format tag for display in option list."""
106
+ tag_path = item.get("tag_path", "")
107
+ is_new = item.get("_is_new", False)
108
+
109
+ if is_new:
110
+ return f"[cyan][+] Create:[/] {tag_path}"
111
+ else:
112
+ display_name = item.get("display_name", tag_path)
113
+ if display_name and display_name != tag_path:
114
+ return f"{tag_path} ({display_name})"
115
+ return tag_path
116
+
117
+ def _get_item_id(self, item: dict[str, Any]) -> str:
118
+ """Get unique identifier for a tag (its path)."""
119
+ return item.get("tag_path", "")
120
+
121
+ def _get_result_count_message(self, count: int) -> str:
122
+ """Get status message for result count."""
123
+ # Count existing vs new tags
124
+ existing_count = sum(
125
+ 1 for t in self._search_results if not t.get("_is_new", False)
126
+ )
127
+ has_create_option = any(t.get("_is_new", False) for t in self._search_results)
128
+
129
+ if existing_count == 0 and not has_create_option:
130
+ # No results and query is invalid
131
+ is_valid, error = validate_tag_path(self._last_search_query)
132
+ if not is_valid:
133
+ return f"[yellow]{error}[/]"
134
+ return "No tags found"
135
+ elif has_create_option and existing_count == 0:
136
+ return "No existing tags - Enter to create new tag"
137
+ elif has_create_option:
138
+ return f"{existing_count} tag{'s' if existing_count != 1 else ''} found - or create new"
139
+ else:
140
+ return f"{existing_count} tag{'s' if existing_count != 1 else ''} - Enter to toggle"
141
+
142
+ def _update_options(self) -> None:
143
+ """Update the option list with current search results (custom styling for tags)."""
144
+ options = self.query_one("#search-options", OptionList)
145
+ options.clear_options()
146
+
147
+ for i, tag in enumerate(self._search_results):
148
+ tag_path = tag.get("tag_path", "")
149
+ is_new = tag.get("_is_new", False)
150
+
151
+ if is_new:
152
+ # Style "create new tag" option distinctly
153
+ if tag_path in self._selected_items:
154
+ display_text = f"[green]✓[/] [cyan][+] Create:[/] {tag_path}"
155
+ else:
156
+ display_text = f" [cyan][+] Create:[/] {tag_path}"
157
+ else:
158
+ # Existing tag with display name
159
+ display_name = tag.get("display_name", tag_path)
160
+ if display_name and display_name != tag_path:
161
+ display_text = f"{tag_path} ({display_name})"
162
+ else:
163
+ display_text = tag_path
164
+
165
+ # Mark selected tags with checkmark
166
+ if tag_path in self._selected_items:
167
+ display_text = f"[green]✓[/] {display_text}"
168
+ else:
169
+ display_text = f" {display_text}"
170
+
171
+ options.add_option(Option(display_text, id=f"opt-{i}"))
172
+
173
+ if self._search_results:
174
+ options.highlighted = 0
175
+
176
+ def _format_selection_display(self) -> str | None:
177
+ """Format selected tags for display."""
178
+ if not self._selected_items:
179
+ return None
180
+
181
+ # Format tags, marking new ones with (new)
182
+ formatted_tags: list[str] = []
183
+ for tag_path, tag_data in self._selected_items.items():
184
+ if tag_data.get("_is_new", False):
185
+ formatted_tags.append(f"[cyan]{tag_path}[/] [dim](new)[/]")
186
+ else:
187
+ formatted_tags.append(tag_path)
188
+
189
+ paths = ", ".join(formatted_tags)
190
+ return f"[bold green]Selected ({len(self._selected_items)}):[/] {paths}"
191
+
192
+ def _post_selection_changed(self) -> None:
193
+ """Post TagsChanged message with tag paths."""
194
+ self.post_message(self.TagsChanged(self.selected_tags))
195
+
196
+ def set_initial_selection(self, tags: list[dict[str, Any]]) -> None:
197
+ """Pre-populate the picker with existing tag selections.
198
+
199
+ Args:
200
+ tags: List of tag dicts with at least 'tag_path' key
201
+ """
202
+ self._selected_items.clear()
203
+ for tag in tags:
204
+ tag_path = tag.get("tag_path", "")
205
+ if tag_path:
206
+ self._selected_items[tag_path] = tag
207
+ self._update_selected_display()
@@ -0,0 +1,131 @@
1
+ """Template picker component for selecting system instruction templates.
2
+
3
+ A specialized search picker that uses the ApiClient to fetch templates
4
+ and returns the selected template's latest version ID.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from alloy_runtime_sdk.api_client.client import ApiClient
10
+ from alloy_runtime_types.enums.template_enums import TemplateContentType
11
+
12
+ from cli.infrastructure.forms.base_picker import SingleSelectPicker
13
+
14
+
15
+ def _format_template(template: dict[str, Any]) -> str:
16
+ """Format a template for display in the picker."""
17
+ name = template.get("name", "Unknown")
18
+ version = template.get("latest_version_number", "?")
19
+ visibility = template.get("visibility_id", "private")
20
+ content_type = template.get("content_type_id", "")
21
+
22
+ # Short visibility label
23
+ vis_label = {"private": "Private", "organization": "Org", "public": "Public"}.get(
24
+ visibility, visibility
25
+ )
26
+
27
+ # Include content type if not system_instruction (since that's the default filter)
28
+ type_label = ""
29
+ if content_type and content_type != "system_instruction":
30
+ type_label = f" [{content_type}]"
31
+
32
+ return f"{name} (v{version}) - {vis_label}{type_label}"
33
+
34
+
35
+ class TemplatePicker(SingleSelectPicker[dict[str, Any]]):
36
+ """Template picker for selecting system instruction templates.
37
+
38
+ Searches templates via the ApiClient and returns the selected template's
39
+ latest version ID. User sees friendly template names, not UUIDs.
40
+
41
+ Usage:
42
+ yield TemplatePicker(
43
+ client=server_client,
44
+ id="system-template",
45
+ )
46
+
47
+ # Get selected version ID
48
+ picker = self.query_one("#system-template", TemplatePicker)
49
+ version_id = picker.selected_id # UUID string or None
50
+ """
51
+
52
+ def __init__(
53
+ self,
54
+ client: ApiClient,
55
+ content_type: TemplateContentType
56
+ | None = TemplateContentType.SYSTEM_INSTRUCTION,
57
+ placeholder: str = "Search templates (e.g., system prompt default)...",
58
+ empty_message: str = "No template selected (optional)",
59
+ **kwargs: Any,
60
+ ) -> None:
61
+ """Initialize the template picker.
62
+
63
+ Args:
64
+ client: ApiClient instance for API calls
65
+ content_type: Filter by template content type (default: system_instruction)
66
+ placeholder: Placeholder text for search input
67
+ empty_message: Message shown when nothing is selected
68
+ """
69
+ super().__init__(
70
+ client=client,
71
+ placeholder=placeholder,
72
+ empty_message=empty_message,
73
+ **kwargs,
74
+ )
75
+ self._content_type = content_type
76
+
77
+ @property
78
+ def selected_version_id(self) -> str | None:
79
+ """Alias for selected_id - returns the template version UUID."""
80
+ return self.selected_id
81
+
82
+ @property
83
+ def selected_template_name(self) -> str | None:
84
+ """Get the name of the selected template."""
85
+ if self._selected_item is None:
86
+ return None
87
+ return self._selected_item.get("name")
88
+
89
+ async def _fetch_items(self, query: str) -> list[dict[str, Any]]:
90
+ """Fetch templates matching query from the API."""
91
+ response = await self._client.list_templates(
92
+ search=query,
93
+ content_type=self._content_type,
94
+ limit=50,
95
+ )
96
+ return [template.model_dump() for template in response.templates]
97
+
98
+ def _format_item(self, item: dict[str, Any]) -> str:
99
+ """Format template for display in option list."""
100
+ return _format_template(item)
101
+
102
+ def _get_item_id(self, item: dict[str, Any]) -> str:
103
+ """Get unique identifier for a template (latest_version_id)."""
104
+ return str(item.get("latest_version_id", ""))
105
+
106
+ def _is_item_selected(self, item: dict[str, Any]) -> bool:
107
+ """Check if an item is currently selected (by latest_version_id)."""
108
+ if self._selected_item is None:
109
+ return False
110
+ return self._get_item_id(item) == self._get_item_id(self._selected_item)
111
+
112
+ def set_initial_template(self, template: dict[str, Any]) -> None:
113
+ """Pre-populate the picker with an existing template selection.
114
+
115
+ Args:
116
+ template: Template dict with at least 'latest_version_id' and 'name' keys
117
+ """
118
+ self.set_initial_selection(template)
119
+
120
+ def set_initial_template_by_version_id(
121
+ self, version_id: str, name: str = "Selected Template"
122
+ ) -> None:
123
+ """Pre-populate the picker with a template by its version ID.
124
+
125
+ Use this when you only have the version ID and not the full template data.
126
+
127
+ Args:
128
+ version_id: The template version UUID
129
+ name: Display name for the template
130
+ """
131
+ self.set_initial_selection({"latest_version_id": version_id, "name": name})
@@ -0,0 +1,130 @@
1
+ """Tool config picker widget with search and single-select.
2
+
3
+ A search input that uses the ApiClient to fetch tool configs on each keystroke,
4
+ displaying results in a static list below. Filters by tool_id to only show
5
+ configs for a specific tool.
6
+ """
7
+
8
+ from typing import Any
9
+
10
+ from alloy_runtime_sdk.api_client.client import ApiClient
11
+ from alloy_runtime_types.dtos.tool_configs import ToolConfigSummary
12
+
13
+ from cli.infrastructure.forms.base_picker import SingleSelectPicker
14
+
15
+
16
+ class ToolConfigPicker(SingleSelectPicker[ToolConfigSummary]):
17
+ """Single-select tool config picker with live search.
18
+
19
+ Filters tool configs by tool_id to only show configs for the selected tool.
20
+
21
+ Usage:
22
+ yield ToolConfigPicker(
23
+ client=server_client,
24
+ tool_id="rag_search",
25
+ id="tool-config-picker"
26
+ )
27
+
28
+ # Get selected config
29
+ picker = self.query_one("#tool-config-picker", ToolConfigPicker)
30
+ config = picker.selected_config # ToolConfigSummary | None
31
+
32
+ Keybindings:
33
+ - Type to search (fetches from API)
34
+ - Up/Down to navigate results
35
+ - Enter to select/deselect
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ client: ApiClient,
41
+ tool_id: str | None = None,
42
+ placeholder: str = "Type to search saved configs...",
43
+ **kwargs: Any,
44
+ ) -> None:
45
+ """Initialize the tool config picker.
46
+
47
+ Args:
48
+ client: ApiClient instance for API calls
49
+ tool_id: Filter by tool ID (e.g., 'rag_search')
50
+ placeholder: Placeholder text for search input
51
+ """
52
+ super().__init__(
53
+ client=client,
54
+ placeholder=placeholder,
55
+ empty_message="No saved config selected",
56
+ fetch_on_mount=True, # Load configs initially
57
+ **kwargs,
58
+ )
59
+ self._tool_id = tool_id
60
+
61
+ @property
62
+ def tool_id(self) -> str | None:
63
+ """Get the tool ID filter."""
64
+ return self._tool_id
65
+
66
+ @tool_id.setter
67
+ def tool_id(self, value: str | None) -> None:
68
+ """Set the tool ID filter and refresh results."""
69
+ self._tool_id = value
70
+ # Clear current selection if tool changed
71
+ self.clear()
72
+ # Trigger a refresh
73
+ if self._fetch_on_mount:
74
+ self._fetch_initial()
75
+
76
+ @property
77
+ def selected_config(self) -> ToolConfigSummary | None:
78
+ """Get the selected tool config."""
79
+ return self.selected_item
80
+
81
+ @property
82
+ def selected_config_id(self) -> str | None:
83
+ """Get the ID of the selected tool config as string."""
84
+ if self.selected_item:
85
+ return str(self.selected_item.id)
86
+ return None
87
+
88
+ async def _fetch_items(self, query: str) -> list[ToolConfigSummary]:
89
+ """Fetch tool configs matching query from the API."""
90
+ if not self._tool_id:
91
+ # No tool selected yet, return empty list
92
+ return []
93
+
94
+ response = await self._client.list_tool_configs(
95
+ search=query if query else None,
96
+ tool_id=self._tool_id,
97
+ limit=50,
98
+ )
99
+ return list(response.tool_configs)
100
+
101
+ def _format_item(self, item: ToolConfigSummary) -> str:
102
+ """Format tool config for display in option list."""
103
+ desc = ""
104
+ if item.description:
105
+ desc = (
106
+ f" - {item.description[:30]}..."
107
+ if len(item.description) > 30
108
+ else f" - {item.description}"
109
+ )
110
+ return f"{item.name}{desc}"
111
+
112
+ def _get_item_id(self, item: ToolConfigSummary) -> str:
113
+ """Get unique identifier for a tool config."""
114
+ return str(item.id)
115
+
116
+ def _get_result_count_message(self, count: int) -> str:
117
+ """Get status message for result count."""
118
+ if count == 0:
119
+ if not self._tool_id:
120
+ return "Select a tool first"
121
+ return "No saved configs found for this tool"
122
+ return f"{count} config{'s' if count != 1 else ''} found - Enter to select"
123
+
124
+ def set_initial_config(self, config: ToolConfigSummary) -> None:
125
+ """Pre-populate the picker with an existing config selection.
126
+
127
+ Args:
128
+ config: ToolConfigSummary object to pre-select
129
+ """
130
+ self.set_initial_selection(config)
@@ -0,0 +1,103 @@
1
+ """Tool picker widget with search and single-select.
2
+
3
+ A search input that uses the ApiClient to fetch available tools on each keystroke,
4
+ displaying results in a static list below. Users can select a single tool.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from alloy_runtime_sdk.api_client.client import ApiClient
10
+ from alloy_runtime_types.dtos.tools import ToolResponse
11
+
12
+ from cli.infrastructure.forms.base_picker import SingleSelectPicker
13
+
14
+
15
+ class ToolPicker(SingleSelectPicker[ToolResponse]):
16
+ """Single-select tool picker with live search.
17
+
18
+ Uses the ApiClient to fetch tools on each keystroke. Results appear in a
19
+ static box below the search input.
20
+
21
+ Usage:
22
+ yield ToolPicker(
23
+ client=server_client,
24
+ id="tool-picker"
25
+ )
26
+
27
+ # Get selected tool
28
+ picker = self.query_one("#tool-picker", ToolPicker)
29
+ tool = picker.selected_tool # ToolResponse | None
30
+
31
+ Keybindings:
32
+ - Type to search (fetches from API)
33
+ - Up/Down to navigate results
34
+ - Enter to select/deselect
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ client: ApiClient,
40
+ placeholder: str = "Type to search tools (e.g., rag, web, search)...",
41
+ **kwargs: Any,
42
+ ) -> None:
43
+ """Initialize the tool picker.
44
+
45
+ Args:
46
+ client: ApiClient instance for API calls
47
+ placeholder: Placeholder text for search input
48
+ """
49
+ super().__init__(
50
+ client=client,
51
+ placeholder=placeholder,
52
+ empty_message="No tool selected",
53
+ fetch_on_mount=True, # Load all tools initially
54
+ **kwargs,
55
+ )
56
+
57
+ @property
58
+ def selected_tool(self) -> ToolResponse | None:
59
+ """Get the selected tool."""
60
+ return self.selected_item
61
+
62
+ @property
63
+ def selected_tool_id(self) -> str | None:
64
+ """Get the ID of the selected tool."""
65
+ return self.selected_id
66
+
67
+ async def _fetch_items(self, query: str) -> list[ToolResponse]:
68
+ """Fetch tools matching query from the API."""
69
+ response = await self._client.list_tools(
70
+ search=query if query else None,
71
+ limit=50,
72
+ )
73
+ return list(response.tools)
74
+
75
+ def _format_item(self, item: ToolResponse) -> str:
76
+ """Format tool for display in option list."""
77
+ has_config = item.parameter_schema is not None
78
+ config_indicator = " [dim][configurable][/]" if has_config else ""
79
+ # Truncate description if too long
80
+ desc = (
81
+ item.description[:50] + "..."
82
+ if len(item.description) > 50
83
+ else item.description
84
+ )
85
+ return f"{item.id}{config_indicator} - {desc}"
86
+
87
+ def _get_item_id(self, item: ToolResponse) -> str:
88
+ """Get unique identifier for a tool."""
89
+ return item.id
90
+
91
+ def _get_result_count_message(self, count: int) -> str:
92
+ """Get status message for result count."""
93
+ if count == 0:
94
+ return "No tools found"
95
+ return f"{count} tool{'s' if count != 1 else ''} found - Enter to select"
96
+
97
+ def set_initial_tool(self, tool: ToolResponse) -> None:
98
+ """Pre-populate the picker with an existing tool selection.
99
+
100
+ Args:
101
+ tool: ToolResponse object to pre-select
102
+ """
103
+ self.set_initial_selection(tool)
File without changes