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,384 @@
1
+ """Agent tool editor widget for configuring a single tool instance.
2
+
3
+ Provides a compound widget for editing tool configuration including:
4
+ - Tool selection
5
+ - Alias (defaults to tool_id)
6
+ - Config mode: none, inline JSON, or saved config reference
7
+ - Inline config JSON textarea with parameter_schema display
8
+ - Tool config picker for referencing saved configs
9
+ - Enable/disable toggle
10
+ - LLM instruction input
11
+ """
12
+
13
+ import json
14
+ from datetime import datetime, timezone
15
+ from typing import Any
16
+ from uuid import UUID
17
+
18
+ from textual import on, work
19
+ from textual.app import ComposeResult
20
+ from textual.containers import Horizontal, Vertical
21
+ from textual.message import Message
22
+ from textual.widget import Widget
23
+ from textual.widgets import (
24
+ Button,
25
+ Checkbox,
26
+ Input,
27
+ RadioButton,
28
+ RadioSet,
29
+ Static,
30
+ TextArea,
31
+ )
32
+
33
+ from alloy_runtime_sdk.api_client.client import ApiClient
34
+ from alloy_runtime_types.dtos.agents import AgentToolConfig, AgentToolResponse
35
+ from alloy_runtime_types.dtos.tool_configs import ToolConfigSummary
36
+ from alloy_runtime_types.dtos.tools import ToolResponse
37
+
38
+ from cli.infrastructure.forms.tool_config_picker import ToolConfigPicker
39
+ from cli.infrastructure.forms.tool_picker import ToolPicker
40
+
41
+
42
+ class AgentToolEditor(Widget):
43
+ """Editor widget for a single agent tool configuration.
44
+
45
+ Provides UI for configuring a tool instance with support for:
46
+ - Tool selection from available tools
47
+ - Custom alias (enables multiple instances of same tool)
48
+ - Three config modes: none, inline JSON, saved config reference
49
+ - LLM instruction for per-agent hints
50
+
51
+ Usage:
52
+ editor = AgentToolEditor(client=client, id="tool-editor-1")
53
+ # Get the configured tool
54
+ config = editor.get_tool_config() # AgentToolConfig | None
55
+ """
56
+
57
+ DEFAULT_CLASSES = "tool-editor"
58
+
59
+ class ToolChanged(Message):
60
+ """Posted when tool configuration changes."""
61
+
62
+ def __init__(self, editor: "AgentToolEditor") -> None:
63
+ self.editor = editor
64
+ super().__init__()
65
+
66
+ class RemoveRequested(Message):
67
+ """Posted when user requests removal of this editor."""
68
+
69
+ def __init__(self, editor: "AgentToolEditor") -> None:
70
+ self.editor = editor
71
+ super().__init__()
72
+
73
+ def __init__(
74
+ self,
75
+ client: ApiClient,
76
+ tool_order: int = 0,
77
+ **kwargs: Any,
78
+ ) -> None:
79
+ """Initialize the agent tool editor.
80
+
81
+ Args:
82
+ client: ApiClient instance for API calls
83
+ tool_order: Order of this tool in the agent's tool list
84
+ """
85
+ super().__init__(**kwargs)
86
+ self._client = client
87
+ self._tool_order = tool_order
88
+ self._selected_tool: ToolResponse | None = None
89
+
90
+ def compose(self) -> ComposeResult:
91
+ with Vertical(classes="tool-editor-content"):
92
+ # Header with title and remove button
93
+ with Horizontal(classes="tool-header"):
94
+ yield Static(
95
+ f"Tool #{self._tool_order + 1}",
96
+ classes="tool-title",
97
+ id="tool-title",
98
+ )
99
+ yield Button("Remove", id="remove-btn", variant="error")
100
+
101
+ # Tool selection
102
+ yield Static("Tool *", classes="field-label")
103
+ yield ToolPicker(client=self._client, id="tool-picker")
104
+
105
+ # Alias
106
+ yield Static("Alias (defaults to tool ID)", classes="field-label")
107
+ yield Input(placeholder="e.g., rag-testimonials", id="alias-input")
108
+
109
+ # Config mode selection
110
+ yield Static("Configuration", classes="field-label")
111
+ with RadioSet(id="config-mode-radios"):
112
+ yield RadioButton("No configuration", id="mode-none", value=True)
113
+ yield RadioButton("Inline config (JSON)", id="mode-inline")
114
+ yield RadioButton("Use saved config", id="mode-reference")
115
+
116
+ # Inline config section (hidden by default)
117
+ with Vertical(id="inline-config-section", classes="config-section hidden"):
118
+ yield Static(
119
+ "Parameter Schema (reference):", classes="field-label schema-label"
120
+ )
121
+ yield TextArea(id="schema-display", read_only=True)
122
+ yield Static("Config (JSON):", classes="field-label")
123
+ yield TextArea(id="config-input", language="json")
124
+
125
+ # Tool config reference section (hidden by default)
126
+ with Vertical(
127
+ id="config-reference-section", classes="config-section hidden"
128
+ ):
129
+ yield Static("Select saved config:", classes="field-label")
130
+ yield ToolConfigPicker(
131
+ client=self._client,
132
+ tool_id=None,
133
+ id="tool-config-picker",
134
+ )
135
+
136
+ # Enable toggle
137
+ yield Checkbox("Enabled", value=True, id="enabled-checkbox")
138
+
139
+ # LLM instruction
140
+ yield Static("LLM Instruction (optional)", classes="field-label")
141
+ yield TextArea(
142
+ id="llm-instruction-input",
143
+ )
144
+
145
+ @on(Button.Pressed, "#remove-btn")
146
+ def on_remove_pressed(self, event: Button.Pressed) -> None:
147
+ """Handle remove button press."""
148
+ event.stop()
149
+ self.post_message(self.RemoveRequested(self))
150
+
151
+ @on(ToolPicker.SelectionChanged)
152
+ def on_tool_selected(self, event: ToolPicker.SelectionChanged) -> None:
153
+ """Handle tool selection change."""
154
+ event.stop()
155
+ self._selected_tool = event.selected_item
156
+
157
+ # Update alias placeholder
158
+ alias_input = self.query_one("#alias-input", Input)
159
+ if self._selected_tool:
160
+ alias_input.placeholder = f"Default: {self._selected_tool.id}"
161
+ else:
162
+ alias_input.placeholder = "e.g., rag-testimonials"
163
+
164
+ # Update schema display
165
+ self._update_schema_display()
166
+
167
+ # Update tool config picker filter
168
+ config_picker = self.query_one("#tool-config-picker", ToolConfigPicker)
169
+ config_picker.tool_id = self._selected_tool.id if self._selected_tool else None
170
+
171
+ # Post change event
172
+ self.post_message(self.ToolChanged(self))
173
+
174
+ @on(RadioSet.Changed, "#config-mode-radios")
175
+ def on_config_mode_changed(self, event: RadioSet.Changed) -> None:
176
+ """Handle config mode change."""
177
+ event.stop()
178
+ self._update_config_sections()
179
+ self.post_message(self.ToolChanged(self))
180
+
181
+ def _update_config_sections(self) -> None:
182
+ """Show/hide config sections based on selected mode."""
183
+ radios = self.query_one("#config-mode-radios", RadioSet)
184
+ pressed = radios.pressed_button
185
+
186
+ inline_section = self.query_one("#inline-config-section", Vertical)
187
+ reference_section = self.query_one("#config-reference-section", Vertical)
188
+
189
+ if pressed and pressed.id == "mode-inline":
190
+ inline_section.remove_class("hidden")
191
+ reference_section.add_class("hidden")
192
+ elif pressed and pressed.id == "mode-reference":
193
+ inline_section.add_class("hidden")
194
+ reference_section.remove_class("hidden")
195
+ else:
196
+ inline_section.add_class("hidden")
197
+ reference_section.add_class("hidden")
198
+
199
+ def _update_schema_display(self) -> None:
200
+ """Update the schema display with selected tool's parameter_schema."""
201
+ schema_display = self.query_one("#schema-display", TextArea)
202
+
203
+ if self._selected_tool and self._selected_tool.parameter_schema:
204
+ schema_json = json.dumps(self._selected_tool.parameter_schema, indent=2)
205
+ schema_display.text = schema_json
206
+ else:
207
+ schema_display.text = "(This tool has no configurable parameters)"
208
+
209
+ def get_tool_config(self) -> AgentToolConfig | None:
210
+ """Get the current tool configuration.
211
+
212
+ Returns:
213
+ AgentToolConfig if a tool is selected and valid, None otherwise
214
+ """
215
+ tool_picker = self.query_one("#tool-picker", ToolPicker)
216
+ if not tool_picker.selected_tool:
217
+ return None
218
+
219
+ tool_id = tool_picker.selected_tool.id
220
+ alias_input = self.query_one("#alias-input", Input)
221
+ alias = alias_input.value.strip() or None
222
+
223
+ # Get config mode
224
+ radios = self.query_one("#config-mode-radios", RadioSet)
225
+ pressed = radios.pressed_button
226
+ config_mode = pressed.id if pressed else "mode-none"
227
+
228
+ config: dict[str, Any] | None = None
229
+ tool_config_id: UUID | None = None
230
+
231
+ if config_mode == "mode-inline":
232
+ config_input = self.query_one("#config-input", TextArea)
233
+ config_text = config_input.text.strip()
234
+ if config_text:
235
+ try:
236
+ config = json.loads(config_text)
237
+ except json.JSONDecodeError:
238
+ # Invalid JSON, will be caught by validation
239
+ config = None
240
+ elif config_mode == "mode-reference":
241
+ config_picker = self.query_one("#tool-config-picker", ToolConfigPicker)
242
+ if config_picker.selected_config:
243
+ tool_config_id = config_picker.selected_config.id
244
+
245
+ enabled_checkbox = self.query_one("#enabled-checkbox", Checkbox)
246
+ llm_instruction_input = self.query_one("#llm-instruction-input", TextArea)
247
+ llm_instruction = llm_instruction_input.text.strip() or None
248
+
249
+ return AgentToolConfig(
250
+ tool_id=tool_id,
251
+ tool_alias=alias,
252
+ config=config,
253
+ tool_config_id=tool_config_id,
254
+ is_enabled=enabled_checkbox.value,
255
+ tool_order=self._tool_order,
256
+ llm_instruction=llm_instruction,
257
+ )
258
+
259
+ def validate(self) -> str | None:
260
+ """Validate the current configuration.
261
+
262
+ Returns:
263
+ Error message if invalid, None if valid
264
+ """
265
+ tool_picker = self.query_one("#tool-picker", ToolPicker)
266
+ if not tool_picker.selected_tool:
267
+ return f"Tool #{self._tool_order + 1}: No tool selected"
268
+
269
+ # Get config mode
270
+ radios = self.query_one("#config-mode-radios", RadioSet)
271
+ pressed = radios.pressed_button
272
+ config_mode = pressed.id if pressed else "mode-none"
273
+
274
+ if config_mode == "mode-inline":
275
+ config_input = self.query_one("#config-input", TextArea)
276
+ config_text = config_input.text.strip()
277
+ if config_text:
278
+ try:
279
+ json.loads(config_text)
280
+ except json.JSONDecodeError as e:
281
+ return (
282
+ f"Tool #{self._tool_order + 1}: Invalid JSON config - {e.msg}"
283
+ )
284
+
285
+ return None
286
+
287
+ def get_alias(self) -> str:
288
+ """Get the effective alias for this tool."""
289
+ alias_input = self.query_one("#alias-input", Input)
290
+ alias = alias_input.value.strip()
291
+ if alias:
292
+ return alias
293
+ tool_picker = self.query_one("#tool-picker", ToolPicker)
294
+ if tool_picker.selected_tool:
295
+ return tool_picker.selected_tool.id
296
+ return ""
297
+
298
+ def set_tool_order(self, order: int) -> None:
299
+ """Update the tool order and title."""
300
+ self._tool_order = order
301
+ title = self.query_one("#tool-title", Static)
302
+ title.update(f"Tool #{order + 1}")
303
+
304
+ def populate_from_response(self, tool: AgentToolResponse) -> None:
305
+ """Populate the editor from an AgentToolResponse.
306
+
307
+ Args:
308
+ tool: Existing tool configuration to load
309
+ """
310
+ self._populate_from_response_async(tool)
311
+
312
+ @work(exclusive=True)
313
+ async def _populate_from_response_async(self, tool: AgentToolResponse) -> None:
314
+ """Async population to fetch full tool details."""
315
+ # Fetch full tool details to get parameter_schema
316
+ try:
317
+ tool_response = await self._client.get_tool(tool.tool_id)
318
+ self._selected_tool = tool_response
319
+ except Exception:
320
+ # Create minimal tool response if fetch fails
321
+ self._selected_tool = ToolResponse(
322
+ id=tool.tool_id,
323
+ description="",
324
+ parameter_schema=None,
325
+ )
326
+
327
+ # Set tool picker
328
+ tool_picker = self.query_one("#tool-picker", ToolPicker)
329
+ tool_picker.set_initial_tool(self._selected_tool)
330
+
331
+ # Set alias
332
+ alias_input = self.query_one("#alias-input", Input)
333
+ if tool.tool_alias and tool.tool_alias != tool.tool_id:
334
+ alias_input.value = tool.tool_alias
335
+
336
+ # Set config mode and values
337
+ if tool.config:
338
+ # Inline config mode
339
+ radio = self.query_one("#mode-inline", RadioButton)
340
+ radio.value = True
341
+ config_input = self.query_one("#config-input", TextArea)
342
+ config_input.text = json.dumps(tool.config, indent=2)
343
+ elif tool.tool_config_id:
344
+ # Reference mode
345
+ radio = self.query_one("#mode-reference", RadioButton)
346
+ radio.value = True
347
+ # Fetch and set tool config
348
+ config_picker = self.query_one("#tool-config-picker", ToolConfigPicker)
349
+ config_picker.tool_id = tool.tool_id
350
+ if tool.tool_config_name:
351
+ # Create a minimal summary for display
352
+ summary = ToolConfigSummary(
353
+ id=tool.tool_config_id,
354
+ name=tool.tool_config_name,
355
+ slug="",
356
+ tool_id=tool.tool_id,
357
+ description=None,
358
+ organization_id=None,
359
+ user_id=None,
360
+ created_at=datetime.now(timezone.utc),
361
+ updated_at=datetime.now(timezone.utc),
362
+ )
363
+ config_picker.set_initial_config(summary)
364
+ else:
365
+ # No config mode (default)
366
+ radio = self.query_one("#mode-none", RadioButton)
367
+ radio.value = True
368
+
369
+ # Update visibility
370
+ self._update_config_sections()
371
+ self._update_schema_display()
372
+
373
+ # Set enabled
374
+ enabled_checkbox = self.query_one("#enabled-checkbox", Checkbox)
375
+ enabled_checkbox.value = tool.is_enabled
376
+
377
+ # Set LLM instruction
378
+ if tool.llm_instruction:
379
+ llm_input = self.query_one("#llm-instruction-input", TextArea)
380
+ llm_input.text = tool.llm_instruction
381
+
382
+ # Update order
383
+ self._tool_order = tool.tool_order
384
+ self.set_tool_order(tool.tool_order)
@@ -0,0 +1,212 @@
1
+ """Agent tools manager widget for managing a list of tool configurations.
2
+
3
+ Provides a compound widget for managing multiple tool configurations including:
4
+ - Add tool button
5
+ - List of AgentToolEditor widgets
6
+ - Removal of individual tools
7
+ - Validation of unique aliases
8
+ """
9
+
10
+ from typing import Any
11
+
12
+ from textual import on
13
+ from textual.app import ComposeResult
14
+ from textual.containers import Vertical, VerticalScroll
15
+ from textual.message import Message
16
+ from textual.widget import Widget
17
+ from textual.widgets import Button, Static
18
+
19
+ from alloy_runtime_sdk.api_client.client import ApiClient
20
+ from alloy_runtime_types.dtos.agents import AgentToolConfig, AgentToolResponse
21
+
22
+ from cli.infrastructure.forms.agent_tool_editor import AgentToolEditor
23
+
24
+
25
+ class AgentToolsManager(Widget):
26
+ """Manager widget for a list of agent tool configurations.
27
+
28
+ Provides UI for managing multiple tool instances with:
29
+ - Add tool button
30
+ - List of AgentToolEditor widgets
31
+ - Removal of individual tools
32
+ - Validation of unique aliases
33
+
34
+ Usage:
35
+ manager = AgentToolsManager(client=client, id="tools-manager")
36
+ # Get all configured tools
37
+ tools = manager.configured_tools # list[AgentToolConfig]
38
+ """
39
+
40
+ DEFAULT_CLASSES = "tools-manager"
41
+
42
+ class ToolsChanged(Message):
43
+ """Posted when the tools list changes."""
44
+
45
+ def __init__(self, tools: list[AgentToolConfig]) -> None:
46
+ self.tools = tools
47
+ super().__init__()
48
+
49
+ def __init__(
50
+ self,
51
+ client: ApiClient,
52
+ **kwargs: Any,
53
+ ) -> None:
54
+ """Initialize the agent tools manager.
55
+
56
+ Args:
57
+ client: ApiClient instance for API calls
58
+ """
59
+ super().__init__(**kwargs)
60
+ self._client = client
61
+ self._editor_count = 0
62
+
63
+ def compose(self) -> ComposeResult:
64
+ with Vertical(classes="tools-manager-content"):
65
+ yield Button("+ Add Tool", id="add-tool-btn", variant="primary")
66
+ yield Static("No tools configured", id="empty-message")
67
+ yield VerticalScroll(id="tools-list")
68
+
69
+ @on(Button.Pressed, "#add-tool-btn")
70
+ def on_add_tool_pressed(self, event: Button.Pressed) -> None:
71
+ """Handle add tool button press."""
72
+ event.stop()
73
+ self._add_tool_editor()
74
+
75
+ @on(AgentToolEditor.RemoveRequested)
76
+ def on_tool_remove_requested(self, event: AgentToolEditor.RemoveRequested) -> None:
77
+ """Handle tool removal request."""
78
+ event.stop()
79
+ self._remove_tool_editor(event.editor)
80
+
81
+ @on(AgentToolEditor.ToolChanged)
82
+ def on_tool_changed(self, event: AgentToolEditor.ToolChanged) -> None:
83
+ """Handle tool configuration change."""
84
+ event.stop()
85
+ self.post_message(self.ToolsChanged(self.configured_tools))
86
+
87
+ def _add_tool_editor(self) -> None:
88
+ """Add a new tool editor to the list."""
89
+ tools_list = self.query_one("#tools-list", VerticalScroll)
90
+ editor = AgentToolEditor(
91
+ client=self._client,
92
+ tool_order=self._editor_count,
93
+ id=f"tool-editor-{self._editor_count}",
94
+ )
95
+ tools_list.mount(editor)
96
+ self._editor_count += 1
97
+ self._update_empty_message()
98
+ self.post_message(self.ToolsChanged(self.configured_tools))
99
+
100
+ def _remove_tool_editor(self, editor: AgentToolEditor) -> None:
101
+ """Remove a tool editor from the list."""
102
+ editor.remove()
103
+ self._renumber_editors()
104
+ self._update_empty_message()
105
+ self.post_message(self.ToolsChanged(self.configured_tools))
106
+
107
+ def _renumber_editors(self) -> None:
108
+ """Renumber all editors after removal."""
109
+ tools_list = self.query_one("#tools-list", VerticalScroll)
110
+ for i, editor in enumerate(tools_list.query(AgentToolEditor)):
111
+ editor.set_tool_order(i)
112
+
113
+ def _update_empty_message(self) -> None:
114
+ """Show/hide empty message based on editor count."""
115
+ tools_list = self.query_one("#tools-list", VerticalScroll)
116
+ empty_message = self.query_one("#empty-message", Static)
117
+ editors = list(tools_list.query(AgentToolEditor))
118
+ if editors:
119
+ empty_message.add_class("hidden")
120
+ else:
121
+ empty_message.remove_class("hidden")
122
+
123
+ @property
124
+ def configured_tools(self) -> list[AgentToolConfig]:
125
+ """Get all configured tools.
126
+
127
+ Returns:
128
+ List of AgentToolConfig from all editors with valid selections
129
+ """
130
+ tools_list = self.query_one("#tools-list", VerticalScroll)
131
+ configs: list[AgentToolConfig] = []
132
+ for i, editor in enumerate(tools_list.query(AgentToolEditor)):
133
+ config = editor.get_tool_config()
134
+ if config:
135
+ # Update tool_order to match current position
136
+ config.tool_order = i
137
+ configs.append(config)
138
+ return configs
139
+
140
+ def validate_aliases(self) -> str | None:
141
+ """Validate that all tool aliases are unique.
142
+
143
+ Returns:
144
+ Error message if duplicates found, None if all unique
145
+ """
146
+ tools_list = self.query_one("#tools-list", VerticalScroll)
147
+ aliases: dict[str, int] = {}
148
+
149
+ for i, editor in enumerate(tools_list.query(AgentToolEditor)):
150
+ alias = editor.get_alias()
151
+ if not alias:
152
+ continue
153
+ if alias in aliases:
154
+ return (
155
+ f"Duplicate tool alias '{alias}' found in tools "
156
+ f"#{aliases[alias] + 1} and #{i + 1}"
157
+ )
158
+ aliases[alias] = i
159
+
160
+ return None
161
+
162
+ def validate_all(self) -> str | None:
163
+ """Validate all tool editors.
164
+
165
+ Returns:
166
+ First error message found, or None if all valid
167
+ """
168
+ tools_list = self.query_one("#tools-list", VerticalScroll)
169
+
170
+ # Validate individual editors
171
+ for editor in tools_list.query(AgentToolEditor):
172
+ error = editor.validate()
173
+ if error:
174
+ return error
175
+
176
+ # Validate unique aliases
177
+ return self.validate_aliases()
178
+
179
+ def set_initial_tools(self, tools: list[AgentToolResponse]) -> None:
180
+ """Pre-populate with existing tool configurations.
181
+
182
+ Args:
183
+ tools: List of existing tool configurations to load
184
+ """
185
+ # Sort by tool_order
186
+ sorted_tools = sorted(tools, key=lambda t: t.tool_order)
187
+
188
+ for tool in sorted_tools:
189
+ self._add_tool_editor_with_data(tool)
190
+
191
+ def _add_tool_editor_with_data(self, tool: AgentToolResponse) -> None:
192
+ """Add a tool editor and populate it with existing data."""
193
+ tools_list = self.query_one("#tools-list", VerticalScroll)
194
+ editor = AgentToolEditor(
195
+ client=self._client,
196
+ tool_order=self._editor_count,
197
+ id=f"tool-editor-{self._editor_count}",
198
+ )
199
+ tools_list.mount(editor)
200
+ self._editor_count += 1
201
+ self._update_empty_message()
202
+
203
+ # Populate after mounting
204
+ editor.populate_from_response(tool)
205
+
206
+ def clear(self) -> None:
207
+ """Remove all tool editors."""
208
+ tools_list = self.query_one("#tools-list", VerticalScroll)
209
+ for editor in list(tools_list.query(AgentToolEditor)):
210
+ editor.remove()
211
+ self._editor_count = 0
212
+ self._update_empty_message()