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,276 @@
1
+ """Presenter for template render output."""
2
+
3
+ import subprocess
4
+ import sys
5
+ import tempfile
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from rich.console import Console
10
+ from rich.panel import Panel
11
+ from rich.table import Table
12
+
13
+ from cli.infrastructure.editor import EditorError, get_editor
14
+ from cli.infrastructure.formatting.fields import format_uuid
15
+ from cli.infrastructure.macro_parser import extract_jinja_syntax
16
+ from cli.infrastructure.output import OutputService
17
+ from alloy_runtime_types.dtos.templates import RenderTemplateResponse
18
+
19
+ # Import pyperclip at module level for easier mocking in tests
20
+ pyperclip: Any | None = None
21
+ try:
22
+ import pyperclip as _pyperclip
23
+ except ImportError:
24
+ pass
25
+ else:
26
+ pyperclip = _pyperclip
27
+
28
+ PYPERCLIP_AVAILABLE = pyperclip is not None
29
+
30
+
31
+ def present_render_result(
32
+ response: RenderTemplateResponse,
33
+ template_name: str,
34
+ output_mode: str = "console",
35
+ output_file: str | None = None,
36
+ print_escaped: bool = False,
37
+ ) -> bool:
38
+ """Present the rendered template result.
39
+
40
+ Args:
41
+ response: Render response from server
42
+ template_name: Name of the rendered template
43
+ output_mode: Output destination - 'console', 'clipboard', 'file', or 'editor'
44
+ output_file: File path for 'file' output mode
45
+ print_escaped: If True, print Jinja syntax found in rendered output
46
+
47
+ Returns:
48
+ True if output was successful, False if output failed
49
+ """
50
+ output = OutputService.get()
51
+
52
+ if output_mode == "clipboard":
53
+ result = _copy_to_clipboard(response.rendered_text, template_name, output)
54
+ elif output_mode == "file":
55
+ result = _save_to_file(response, template_name, output_file, output)
56
+ elif output_mode == "editor":
57
+ result = _open_in_editor(response.rendered_text, template_name, output)
58
+ else:
59
+ result = _display_to_console(response, template_name, output)
60
+
61
+ # Print escaped Jinja syntax after output if requested
62
+ if print_escaped:
63
+ _print_escaped_jinja(response.rendered_text, output)
64
+
65
+ return result
66
+
67
+
68
+ def _print_escaped_jinja(rendered_text: str, output: OutputService) -> None:
69
+ """Print Jinja syntax found in rendered output.
70
+
71
+ Extracts and prints {{ }}, {% %}, and {# #} blocks that remain
72
+ in the rendered output (typically from {% raw %} blocks).
73
+
74
+ Args:
75
+ rendered_text: The rendered template content
76
+ output: Output service for printing
77
+ """
78
+ matches = extract_jinja_syntax(rendered_text)
79
+
80
+ if not matches:
81
+ output.info("No escaped Jinja syntax found in rendered output")
82
+ return
83
+
84
+ # Print to stderr so it doesn't interfere with piped output
85
+ stderr_console = Console(file=sys.stderr, force_terminal=True)
86
+ stderr_console.print(
87
+ f"\n[bold cyan]Escaped Jinja Syntax[/bold cyan] ({len(matches)} found):"
88
+ )
89
+
90
+ for match in matches:
91
+ # Print the full content of each match
92
+ stderr_console.print(match.content)
93
+
94
+ stderr_console.print() # Blank line after
95
+
96
+
97
+ def _display_to_console(
98
+ response: RenderTemplateResponse,
99
+ template_name: str,
100
+ _output: OutputService,
101
+ ) -> bool:
102
+ """Display rendered template to console.
103
+
104
+ Metadata and status messages are printed to stderr for piping support.
105
+ The actual rendered content is printed to stdout without formatting.
106
+ """
107
+ # Create a console for stderr output (for metadata/status)
108
+ stderr_console = Console(file=sys.stderr, force_terminal=True)
109
+
110
+ # Print success message to stderr (directly, not through output service)
111
+ stderr_console.print(
112
+ f"[green]✓[/green] Template '{template_name}' rendered successfully"
113
+ )
114
+
115
+ # Metadata table - print to stderr
116
+ meta_table = Table(show_header=False, box=None, padding=(0, 1))
117
+ meta_table.add_column("Field", style="dim")
118
+ meta_table.add_column("Value")
119
+
120
+ meta_table.add_row("Character Count", f"{response.character_count:,}")
121
+ meta_table.add_row(
122
+ "Version ID", format_uuid(response.template_version_id, short=True)
123
+ )
124
+ meta_table.add_row("Content Hash", response.content_hash[:16] + "...")
125
+ meta_table.add_row("Render Log ID", format_uuid(response.render_log_id, short=True))
126
+
127
+ meta_panel = Panel(meta_table, title="Render Metadata", border_style="blue")
128
+ stderr_console.print(meta_panel)
129
+
130
+ # Print the actual rendered content to stdout (for piping)
131
+ # No formatting, no panel - just the raw content
132
+ print(response.rendered_text, file=sys.stdout)
133
+
134
+ return True
135
+
136
+
137
+ def _save_to_file(
138
+ response: RenderTemplateResponse,
139
+ template_name: str,
140
+ output_file: str | None,
141
+ output: OutputService,
142
+ ) -> bool:
143
+ """Save rendered template to a file.
144
+
145
+ Returns True on success, False on failure.
146
+ """
147
+ if not output_file:
148
+ output.warning("No output file specified")
149
+ return False
150
+
151
+ try:
152
+ file_path = Path(output_file).expanduser().resolve()
153
+
154
+ # Ensure parent directory exists
155
+ file_path.parent.mkdir(parents=True, exist_ok=True)
156
+
157
+ # Write the content to the file
158
+ file_path.write_text(response.rendered_text, encoding="utf-8")
159
+
160
+ output.success(
161
+ f"Template '{template_name}' rendered and saved to {file_path} "
162
+ f"({response.character_count:,} characters)"
163
+ )
164
+ return True
165
+ except PermissionError:
166
+ output.warning(f"Permission denied: Cannot write to '{output_file}'")
167
+ return False
168
+ except OSError as e:
169
+ output.warning(f"Failed to save file: {e}")
170
+ return False
171
+
172
+
173
+ def _copy_to_clipboard(
174
+ rendered_text: str,
175
+ template_name: str,
176
+ output: OutputService,
177
+ ) -> bool:
178
+ """Copy rendered template to clipboard.
179
+
180
+ All output goes to stderr since clipboard mode doesn't output the content to stdout.
181
+
182
+ Returns True on success, False on failure.
183
+ """
184
+ if not PYPERCLIP_AVAILABLE or pyperclip is None:
185
+ output.warning("pyperclip not installed. Install with: pip install pyperclip")
186
+ output.info("Falling back to console output...")
187
+ # Fallback: print to stdout for piping
188
+ print(rendered_text, file=sys.stdout)
189
+ return False
190
+
191
+ try:
192
+ pyperclip.copy(rendered_text)
193
+ output.success(
194
+ f"Template '{template_name}' rendered and copied to clipboard "
195
+ f"({len(rendered_text):,} characters)"
196
+ )
197
+ return True
198
+ except Exception as e:
199
+ output.warning(f"Failed to copy to clipboard: {e}")
200
+ output.info("Falling back to console output...")
201
+ # Fallback: print to stdout for piping
202
+ print(rendered_text, file=sys.stdout)
203
+ return False
204
+
205
+
206
+ def _open_in_editor(
207
+ rendered_text: str,
208
+ template_name: str,
209
+ output: OutputService,
210
+ ) -> bool:
211
+ """Open rendered template in the user's $EDITOR.
212
+
213
+ Creates a temporary file, opens it in the editor, and waits for the editor
214
+ to close. The temporary file is cleaned up afterward.
215
+
216
+ Returns True on success, False on failure.
217
+ """
218
+ try:
219
+ editor = get_editor()
220
+ except EditorError:
221
+ output.warning(
222
+ "No editor found. Set $EDITOR environment variable or install nano/vim."
223
+ )
224
+ output.info("Falling back to console output...")
225
+ print(rendered_text, file=sys.stdout)
226
+ return False
227
+
228
+ # Create temp file with .txt extension
229
+ try:
230
+ with tempfile.NamedTemporaryFile(
231
+ mode="w",
232
+ suffix=".txt",
233
+ delete=False,
234
+ encoding="utf-8",
235
+ ) as f:
236
+ f.write(rendered_text)
237
+ temp_path = Path(f.name)
238
+ except OSError as e:
239
+ output.warning(f"Failed to create temporary file: {e}")
240
+ output.info("Falling back to console output...")
241
+ print(rendered_text, file=sys.stdout)
242
+ return False
243
+
244
+ try:
245
+ # Open editor and wait for it to close
246
+ editor_parts = editor.split()
247
+ cmd = editor_parts + [str(temp_path)]
248
+
249
+ result = subprocess.call(cmd)
250
+
251
+ if result != 0:
252
+ output.warning(f"Editor exited with non-zero status: {result}")
253
+ return False
254
+
255
+ output.success(
256
+ f"Template '{template_name}' rendered and opened in editor "
257
+ f"({len(rendered_text):,} characters)"
258
+ )
259
+ return True
260
+
261
+ except FileNotFoundError:
262
+ output.warning(f"Editor '{editor}' not found")
263
+ output.info("Falling back to console output...")
264
+ print(rendered_text, file=sys.stdout)
265
+ return False
266
+ except OSError as e:
267
+ output.warning(f"Failed to open editor: {e}")
268
+ output.info("Falling back to console output...")
269
+ print(rendered_text, file=sys.stdout)
270
+ return False
271
+ finally:
272
+ # Clean up temp file
273
+ try:
274
+ temp_path.unlink()
275
+ except OSError:
276
+ pass # Best effort cleanup
File without changes
@@ -0,0 +1,199 @@
1
+ """Template update command implementation."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ import typer
7
+
8
+ from cli.commands.flag_utils import (
9
+ parse_tags,
10
+ require_exclusive,
11
+ require_update_flags,
12
+ validate_template_content_type,
13
+ validate_visibility,
14
+ )
15
+ from cli.commands.templates.update.presenter import present_template_update_success
16
+ from cli.infrastructure.command import async_command, authenticated_client
17
+ from cli.infrastructure.file_content import FileContentError, resolve_content
18
+ from alloy_runtime_types.dtos.templates import UpdateTemplateRequest
19
+ from alloy_runtime_types.enums.template_enums import (
20
+ TemplateContentType,
21
+ TemplateVisibility,
22
+ )
23
+ from alloy_runtime_sdk.exceptions.errors import NotFoundError
24
+
25
+
26
+ def templates_update_command(
27
+ template: str = typer.Argument(..., help="Template name or UUID"),
28
+ name: Optional[str] = typer.Option(
29
+ None,
30
+ "-n",
31
+ "--name",
32
+ help="New name",
33
+ ),
34
+ description: Optional[str] = typer.Option(
35
+ None,
36
+ "-d",
37
+ "--description",
38
+ help="New description",
39
+ ),
40
+ content_type: Optional[str] = typer.Option(
41
+ None,
42
+ "--type",
43
+ help="New type: system_instruction, user_message, fragment",
44
+ ),
45
+ visibility: Optional[str] = typer.Option(
46
+ None,
47
+ "-V",
48
+ "--visibility",
49
+ help="New visibility: private, organization, public",
50
+ ),
51
+ content: Optional[str] = typer.Option(
52
+ None,
53
+ "-c",
54
+ "--content",
55
+ help="New content (or @file)",
56
+ ),
57
+ content_file: Optional[Path] = typer.Option(
58
+ None,
59
+ "-f",
60
+ "--file",
61
+ help="Read new content from file",
62
+ exists=True,
63
+ readable=True,
64
+ ),
65
+ tags: Optional[str] = typer.Option(
66
+ None,
67
+ "-t",
68
+ "--tags",
69
+ help="Replace tags (comma-separated)",
70
+ ),
71
+ clear_description: bool = typer.Option(
72
+ False,
73
+ "--clear-description",
74
+ help="Clear description",
75
+ ),
76
+ clear_tags: bool = typer.Option(
77
+ False,
78
+ "--clear-tags",
79
+ help="Remove all tags",
80
+ ),
81
+ ) -> None:
82
+ """Update an existing template.
83
+
84
+ Only specified fields are updated. Content updates only allowed for single-version templates.
85
+
86
+ Examples:
87
+ ai templates update my-template -n new-name
88
+ ai tpl update my-template -d "New description"
89
+ ai tpl update my-template --clear-description
90
+ ai tpl update my-template -f template.jinja2
91
+ ai tpl update my-template -V public -t "new.tag"
92
+ """
93
+ # Validate exclusive flags
94
+ require_exclusive(("--file", content_file), ("--content", content))
95
+ require_exclusive(
96
+ ("--description", description), ("--clear-description", clear_description)
97
+ )
98
+ require_exclusive(("--tags", tags), ("--clear-tags", clear_tags))
99
+
100
+ # Check at least one update flag provided
101
+ require_update_flags(
102
+ ("--name", name),
103
+ ("--description", description),
104
+ ("--type", content_type),
105
+ ("--visibility", visibility),
106
+ ("--content", content),
107
+ ("--file", content_file),
108
+ ("--tags", tags),
109
+ ("--clear-description", clear_description),
110
+ ("--clear-tags", clear_tags),
111
+ )
112
+
113
+ _execute_update(
114
+ template_identifier=template,
115
+ name=name,
116
+ description=description,
117
+ content_type=content_type,
118
+ visibility=visibility,
119
+ content=content,
120
+ content_file=content_file,
121
+ tags=tags,
122
+ clear_description=clear_description,
123
+ clear_tags=clear_tags,
124
+ )
125
+
126
+
127
+ @async_command
128
+ async def _execute_update(
129
+ template_identifier: str,
130
+ name: Optional[str],
131
+ description: Optional[str],
132
+ content_type: Optional[str],
133
+ visibility: Optional[str],
134
+ content: Optional[str],
135
+ content_file: Optional[Path],
136
+ tags: Optional[str],
137
+ clear_description: bool,
138
+ clear_tags: bool,
139
+ ) -> None:
140
+ """Execute the template update operation."""
141
+ # Resolve @file references
142
+ try:
143
+ content = resolve_content(content)
144
+ except FileContentError as e:
145
+ raise typer.BadParameter(str(e))
146
+
147
+ # Get content from file if specified
148
+ template_content: Optional[str] = None
149
+ if content_file is not None:
150
+ template_content = content_file.read_text()
151
+ elif content is not None:
152
+ template_content = content
153
+
154
+ # Validate enums
155
+ content_type_enum: Optional[TemplateContentType] = None
156
+ if content_type is not None:
157
+ content_type_enum = validate_template_content_type(content_type)
158
+
159
+ visibility_enum: Optional[TemplateVisibility] = None
160
+ if visibility is not None:
161
+ visibility_enum = validate_visibility(visibility)
162
+
163
+ # Handle tags
164
+ tag_paths: Optional[list[str]] = None
165
+ if clear_tags:
166
+ tag_paths = []
167
+ elif tags is not None:
168
+ tag_paths = parse_tags(tags)
169
+ if not tag_paths:
170
+ raise typer.BadParameter("--tags must contain at least one tag")
171
+
172
+ # Handle description
173
+ final_description: Optional[str] = description
174
+ if clear_description:
175
+ final_description = ""
176
+
177
+ # Build update request
178
+ request = UpdateTemplateRequest(
179
+ name=name,
180
+ description=final_description
181
+ if (description is not None or clear_description)
182
+ else None,
183
+ content_type=content_type_enum,
184
+ visibility=visibility_enum,
185
+ content=template_content,
186
+ tag_paths=tag_paths,
187
+ )
188
+
189
+ async with authenticated_client() as (_config, client):
190
+ try:
191
+ await client.get_template(template_identifier)
192
+ except NotFoundError:
193
+ raise typer.BadParameter(
194
+ f"Template '{template_identifier}' not found. Use 'ai templates list' to see available templates."
195
+ )
196
+
197
+ response = await client.update_template(template_identifier, request)
198
+
199
+ present_template_update_success(response)
@@ -0,0 +1,94 @@
1
+ """Presenter for template update output."""
2
+
3
+ from rich.panel import Panel
4
+ from rich.table import Table
5
+ from rich.text import Text
6
+
7
+ from cli.infrastructure.formatting.fields import (
8
+ format_datetime,
9
+ format_optional,
10
+ format_uuid,
11
+ )
12
+ from cli.infrastructure.output import OutputService
13
+ from alloy_runtime_types.dtos.templates import UpdateTemplateResponse
14
+
15
+
16
+ def present_template_update_success(response: UpdateTemplateResponse) -> None:
17
+ """Present successful template update.
18
+
19
+ Args:
20
+ response: Template update response from server
21
+ """
22
+ output = OutputService.get()
23
+
24
+ template = response.template
25
+ version = response.version
26
+ dependencies = response.dependencies
27
+
28
+ output.success("Template updated successfully")
29
+
30
+ # Build a table for template display
31
+ table = Table(show_header=False, box=None, padding=(0, 1))
32
+ table.add_column("Field", style="dim")
33
+ table.add_column("Value")
34
+
35
+ # Template info
36
+ table.add_row("ID", format_uuid(template.id, short=False))
37
+ table.add_row("Name", template.name)
38
+ table.add_row("Content Type", template.content_type_id)
39
+ table.add_row("Visibility", template.visibility_id)
40
+ table.add_row("Description", format_optional(template.description))
41
+
42
+ # Version info
43
+ table.add_row("Version", str(version.version))
44
+ table.add_row("Version ID", format_uuid(version.id, short=True))
45
+ table.add_row("Content Length", f"{len(version.content)} chars")
46
+
47
+ # Tags
48
+ if template.tags:
49
+ tag_str = ", ".join(t.tag_path for t in template.tags)
50
+ table.add_row("Tags", tag_str)
51
+ else:
52
+ table.add_row("Tags", "(none)")
53
+
54
+ # Dependencies
55
+ if dependencies:
56
+ dep_str = ", ".join(d.child_template_name for d in dependencies)
57
+ table.add_row("Dependencies", dep_str)
58
+ else:
59
+ table.add_row("Dependencies", "(none)")
60
+
61
+ # Variables
62
+ if version.required_variables:
63
+ vars_str = ", ".join(version.required_variables)
64
+ table.add_row("Required Variables", vars_str)
65
+ else:
66
+ table.add_row("Required Variables", "(none)")
67
+
68
+ # Ownership
69
+ table.add_row(
70
+ "Organization ID",
71
+ format_optional(template.organization_id, lambda x: format_uuid(x, short=True)),
72
+ )
73
+ table.add_row(
74
+ "User ID",
75
+ format_optional(template.user_id, lambda x: format_uuid(x, short=True)),
76
+ )
77
+ table.add_row("Created At", format_datetime(template.created_at))
78
+ table.add_row("Updated At", format_datetime(template.updated_at))
79
+
80
+ panel = Panel(table, title=f"Template: {template.name}", border_style="green")
81
+ output.console.print(panel)
82
+
83
+ # Show content preview
84
+ if version.content:
85
+ content_preview = version.content
86
+ if len(content_preview) > 500:
87
+ content_preview = content_preview[:500] + "\n... (truncated)"
88
+
89
+ content_panel = Panel(
90
+ Text(content_preview, style="dim"),
91
+ title="Content Preview",
92
+ border_style="blue",
93
+ )
94
+ output.console.print(content_panel)
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,116 @@
1
+ """Template version create command implementation."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ import typer
7
+
8
+ from cli.commands.flag_utils import parse_tags, require_one_of
9
+ from cli.commands.templates.version.presenter import present_version_create_success
10
+ from cli.infrastructure.command import async_command, authenticated_client
11
+ from alloy_runtime_types.dtos.templates import CreateTemplateVersionRequest
12
+
13
+
14
+ def templates_version_command(
15
+ template: str = typer.Argument(
16
+ ...,
17
+ help="Template name or UUID",
18
+ ),
19
+ content_file: Optional[Path] = typer.Option(
20
+ None,
21
+ "-f",
22
+ "--file",
23
+ help="Read content from file",
24
+ exists=True,
25
+ readable=True,
26
+ ),
27
+ content: Optional[str] = typer.Option(
28
+ None,
29
+ "-c",
30
+ "--content",
31
+ help="Inline content",
32
+ ),
33
+ tags: Optional[str] = typer.Option(
34
+ None,
35
+ "-t",
36
+ "--tags",
37
+ help="Replace tags (comma-separated)",
38
+ ),
39
+ clear_tags: bool = typer.Option(
40
+ False,
41
+ "--clear-tags",
42
+ help="Remove all tags",
43
+ ),
44
+ ) -> None:
45
+ """Create a new version of a template.
46
+
47
+ Examples:
48
+ ai templates version my-template -c "Updated content"
49
+ ai tpl version my-template -f updated.jinja2
50
+ ai tpl version my-template -c "New content" -t "email,v2"
51
+ ai tpl version my-template -c "New content" --clear-tags
52
+ """
53
+ require_one_of(("--file", content_file), ("--content", content))
54
+
55
+ _execute_version_from_flags(
56
+ template_identifier=template,
57
+ content_file=content_file,
58
+ content=content,
59
+ tags=tags,
60
+ clear_tags=clear_tags,
61
+ )
62
+
63
+
64
+ def _execute_version_from_flags(
65
+ template_identifier: str,
66
+ content_file: Optional[Path],
67
+ content: Optional[str],
68
+ tags: Optional[str],
69
+ clear_tags: bool,
70
+ ) -> None:
71
+ """Build and execute version creation from command flags."""
72
+ # Get content from file or inline
73
+ if content_file:
74
+ template_content = content_file.read_text()
75
+ else:
76
+ template_content = content or ""
77
+
78
+ if not template_content.strip():
79
+ raise typer.BadParameter("Template content cannot be empty")
80
+
81
+ # Parse tags
82
+ tag_paths: list[str] | None = None
83
+ if clear_tags:
84
+ tag_paths = []
85
+ elif tags:
86
+ tag_paths = parse_tags(tags)
87
+
88
+ # Build request
89
+ request = CreateTemplateVersionRequest(
90
+ content=template_content,
91
+ tag_paths=tag_paths,
92
+ )
93
+
94
+ _resolve_and_create_version(template_identifier, request)
95
+
96
+
97
+ @async_command
98
+ async def _resolve_and_create_version(
99
+ template_identifier: str, request: CreateTemplateVersionRequest
100
+ ) -> None:
101
+ """Resolve template identifier and create version."""
102
+ from alloy_runtime_sdk.exceptions.errors import NotFoundError
103
+
104
+ async with authenticated_client() as (_config, client):
105
+ try:
106
+ template_data = await client.get_template(template_identifier)
107
+ except NotFoundError:
108
+ raise typer.BadParameter(
109
+ f"Template '{template_identifier}' not found. Use 'ai templates list' to see available templates."
110
+ )
111
+
112
+ response = await client.create_template_version(
113
+ template_data.template.id, request
114
+ )
115
+
116
+ present_version_create_success(response)