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,256 @@
1
+ """Shorthand filter parser for JSONB structured content filtering.
2
+
3
+ Provides a concise syntax for building structured filters without writing JSON.
4
+ Multiple -w options are combined with AND logic.
5
+
6
+ Syntax Reference:
7
+ field=value Equal (string)
8
+ field:=value Equal (JSON: number, bool, null, array, object)
9
+ field!=value Not equal
10
+ field>value Greater than
11
+ field>=value Greater than or equal
12
+ field<value Less than
13
+ field<=value Less than or equal
14
+ field~=pattern Case-insensitive LIKE (ilike)
15
+ field~~pattern Case-sensitive LIKE
16
+ field?=a,b,c In list (comma-separated)
17
+ field!?=a,b,c Not in list
18
+ field? Key exists
19
+ field!? Key does not exist
20
+ field@=jsonb JSONB contains
21
+
22
+ Nested paths use dot notation:
23
+ data.count>=10
24
+ metadata.user.email~=%@gmail.com%
25
+
26
+ Examples:
27
+ -w status=complete # status equals "complete"
28
+ -w "count:=10" # count equals 10 (number)
29
+ -w "active:=true" # active equals true (boolean)
30
+ -w "data.score>=85" # nested path, gte comparison
31
+ -w "email~=%@example.com%" # case-insensitive pattern match
32
+ -w "status?=pending,review,approved" # status in list
33
+ -w "error?" # error key exists
34
+ -w "deprecated!?" # deprecated key does not exist
35
+
36
+ Usage with Typer:
37
+ from cli.infrastructure.filter_parser import parse_where_filters
38
+
39
+ @app.command()
40
+ def my_command(
41
+ where: Annotated[
42
+ list[str] | None,
43
+ typer.Option("--where", "-w", help="Filter conditions"),
44
+ ] = None,
45
+ ) -> None:
46
+ filter_spec = parse_where_filters(where)
47
+ # filter_spec is ConditionSpec | LogicalSpec | None
48
+ """
49
+
50
+ import json
51
+ import re
52
+ from typing import Any, cast
53
+
54
+ import typer
55
+
56
+ from alloy_runtime_types.dtos.structured_filter import (
57
+ ConditionOperator,
58
+ ConditionSpec,
59
+ LogicalSpec,
60
+ )
61
+
62
+ # Operator patterns ordered by specificity (longest match first)
63
+ # Format: (regex_pattern, literal_str, operator, has_value, value_parser)
64
+ # The regex_pattern is for matching, literal_str is the actual operator string
65
+ OPERATOR_PATTERNS: list[tuple[str, str, str, bool, str]] = [
66
+ # Multi-char operators first (order matters - longer/more specific first)
67
+ (r"!\?=", "!?=", "nin", True, "list"), # not in list
68
+ (r"\?=", "?=", "in", True, "list"), # in list
69
+ (r">=", ">=", "gte", True, "auto"), # greater than or equal
70
+ (r"<=", "<=", "lte", True, "auto"), # less than or equal
71
+ (r"~=", "~=", "ilike", True, "string"), # case-insensitive like
72
+ (r"~~", "~~", "like", True, "string"), # case-sensitive like
73
+ (r"@=", "@=", "contains", True, "json"), # jsonb contains
74
+ (r":=", ":=", "eq", True, "json"), # equal with JSON value
75
+ (r"!=", "!=", "ne", True, "auto"), # not equal
76
+ (r"!\?", "!?", "not_exists", False, "none"), # key does not exist
77
+ (r"\?", "?", "exists", False, "none"), # key exists (must be after ?=)
78
+ # Single-char operators last
79
+ (r">", ">", "gt", True, "auto"), # greater than
80
+ (r"<", "<", "lt", True, "auto"), # less than
81
+ (r"=", "=", "eq", True, "auto"), # equal (default string)
82
+ ]
83
+
84
+ # Compile regex pattern for finding operators
85
+ # Matches field name, then operator, then optional value
86
+ FILTER_REGEX = re.compile(
87
+ r"^([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)" # field path
88
+ r"(" + "|".join(p[0] for p in OPERATOR_PATTERNS) + r")" # operator
89
+ r"(.*)$" # value (may be empty)
90
+ )
91
+
92
+
93
+ def _parse_auto_value(value: str) -> Any:
94
+ """Parse a value with automatic type detection.
95
+
96
+ Tries JSON parsing first, falls back to string.
97
+ """
98
+ if not value:
99
+ return ""
100
+
101
+ # Try to parse as JSON (handles numbers, bools, null)
102
+ try:
103
+ return json.loads(value)
104
+ except json.JSONDecodeError:
105
+ # Return as string
106
+ return value
107
+
108
+
109
+ def _parse_json_value(value: str, field: str) -> Any:
110
+ """Parse a value as JSON, raising error if invalid."""
111
+ try:
112
+ return json.loads(value)
113
+ except json.JSONDecodeError as e:
114
+ raise typer.BadParameter(
115
+ f"Invalid JSON value for field '{field}': {value}. Error: {e}"
116
+ )
117
+
118
+
119
+ def _parse_list_value(value: str) -> list[str]:
120
+ """Parse a comma-separated list of values."""
121
+ if not value:
122
+ return []
123
+ return [v.strip() for v in value.split(",") if v.strip()]
124
+
125
+
126
+ def parse_single_filter(expr: str) -> ConditionSpec:
127
+ """Parse a single filter expression into a ConditionSpec.
128
+
129
+ Args:
130
+ expr: Filter expression (e.g., "status=complete", "count>=10")
131
+
132
+ Returns:
133
+ ConditionSpec for the filter
134
+
135
+ Raises:
136
+ typer.BadParameter: If expression is invalid
137
+ """
138
+ expr = expr.strip()
139
+ if not expr:
140
+ raise typer.BadParameter("Empty filter expression")
141
+
142
+ match = FILTER_REGEX.match(expr)
143
+ if not match:
144
+ raise typer.BadParameter(
145
+ f"Invalid filter syntax: '{expr}'. "
146
+ "Expected format: field<op>value (e.g., status=complete, count>=10)"
147
+ )
148
+
149
+ field = match.group(1)
150
+ op_str = match.group(2)
151
+ raw_value = match.group(3)
152
+
153
+ # Find the operator details by matching the literal string
154
+ op_name = None
155
+ has_value = True
156
+ value_parser = "auto"
157
+
158
+ for _regex_pattern, literal_str, name, needs_value, parser in OPERATOR_PATTERNS:
159
+ if op_str == literal_str:
160
+ op_name = name
161
+ has_value = needs_value
162
+ value_parser = parser
163
+ break
164
+
165
+ if op_name is None:
166
+ raise typer.BadParameter(f"Unknown operator in: '{expr}'")
167
+
168
+ # Validate value presence
169
+ if has_value and not raw_value:
170
+ raise typer.BadParameter(f"Missing value for operator '{op_str}' in: '{expr}'")
171
+
172
+ if not has_value and raw_value:
173
+ raise typer.BadParameter(
174
+ f"Operator '{op_str}' does not take a value, but got: '{raw_value}'"
175
+ )
176
+
177
+ # Parse the value based on type
178
+ value: Any = None
179
+ if has_value:
180
+ if value_parser == "json":
181
+ value = _parse_json_value(raw_value, field)
182
+ elif value_parser == "list":
183
+ value = _parse_list_value(raw_value)
184
+ elif value_parser == "string":
185
+ value = raw_value
186
+ else: # auto
187
+ value = _parse_auto_value(raw_value)
188
+
189
+ # For exists/not_exists, value should be None (or True for consistency)
190
+ if op_name in ("exists", "not_exists"):
191
+ value = True
192
+
193
+ # Cast op_name to the literal type since we've validated it above
194
+ op_literal = cast(ConditionOperator, op_name)
195
+ return ConditionSpec(field=field, op=op_literal, value=value)
196
+
197
+
198
+ def parse_where_filters(
199
+ filters: list[str] | None,
200
+ ) -> ConditionSpec | LogicalSpec | None:
201
+ """Parse multiple -w filter expressions into a filter spec.
202
+
203
+ Multiple filters are combined with AND logic.
204
+
205
+ Args:
206
+ filters: List of filter expressions from repeated -w options
207
+
208
+ Returns:
209
+ - None if no filters provided
210
+ - ConditionSpec if single filter
211
+ - LogicalSpec (AND) if multiple filters
212
+
213
+ Raises:
214
+ typer.BadParameter: If any expression is invalid
215
+
216
+ Examples:
217
+ >>> parse_where_filters(["status=complete"])
218
+ ConditionSpec(field="status", op="eq", value="complete")
219
+
220
+ >>> parse_where_filters(["status=complete", "count>=10"])
221
+ LogicalSpec(op="and", conditions=[...])
222
+ """
223
+ if not filters:
224
+ return None
225
+
226
+ conditions = [parse_single_filter(f) for f in filters]
227
+
228
+ if len(conditions) == 1:
229
+ return conditions[0]
230
+
231
+ # Cast to the expected type for LogicalSpec
232
+ return LogicalSpec(op="and", conditions=list(conditions))
233
+
234
+
235
+ def merge_filter_specs(
236
+ where_spec: ConditionSpec | LogicalSpec | None,
237
+ json_spec: ConditionSpec | LogicalSpec | None,
238
+ ) -> ConditionSpec | LogicalSpec | None:
239
+ """Merge filter specs from -w and --structured-filter options.
240
+
241
+ If both are provided, combines them with AND logic.
242
+
243
+ Args:
244
+ where_spec: Filter spec from -w options
245
+ json_spec: Filter spec from --structured-filter JSON
246
+
247
+ Returns:
248
+ Combined filter spec, or None if both are None
249
+ """
250
+ if where_spec is None:
251
+ return json_spec
252
+ if json_spec is None:
253
+ return where_spec
254
+
255
+ # Both exist - combine with AND
256
+ return LogicalSpec(op="and", conditions=[where_spec, json_spec])
File without changes
@@ -0,0 +1,99 @@
1
+ """Base formatter interface for CLI output.
2
+
3
+ Defines the contract that all output formatters must implement.
4
+ """
5
+
6
+ from abc import ABC, abstractmethod
7
+ from dataclasses import dataclass
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ if TYPE_CHECKING:
11
+ pass
12
+
13
+
14
+ @dataclass
15
+ class ColumnMeta:
16
+ """Metadata about a column for formatters that need layout info.
17
+
18
+ This is a simplified view of ColumnConfig passed to formatters.
19
+ """
20
+
21
+ label: str
22
+ compact_line: int = 1
23
+ compact_style: str | None = None
24
+ width: int | None = None
25
+
26
+
27
+ class Formatter(ABC):
28
+ """Abstract base class for output formatters.
29
+
30
+ Formatters transform data into specific output formats (Rich tables, JSON, YAML, etc.).
31
+ """
32
+
33
+ @abstractmethod
34
+ def format_entity(self, data: dict[str, Any], title: str | None = None) -> Any:
35
+ """Format a single entity's details as key-value pairs.
36
+
37
+ Args:
38
+ data: Dictionary mapping field labels to values
39
+ title: Optional title for the entity display
40
+
41
+ Returns:
42
+ Formatted output (str for JSON, Rich renderable for Rich, etc.)
43
+ """
44
+ pass
45
+
46
+ @abstractmethod
47
+ def format_list(
48
+ self,
49
+ items: list[dict[str, Any]],
50
+ title: str | None = None,
51
+ columns: list[ColumnMeta] | None = None,
52
+ ) -> Any:
53
+ """Format a list of items as a table or similar structure.
54
+
55
+ Args:
56
+ items: List of dictionaries, each representing one row
57
+ title: Optional title for the list display
58
+ columns: Optional column metadata for layout-aware formatters
59
+
60
+ Returns:
61
+ Formatted output (str for JSON, Rich renderable for Rich, etc.)
62
+ """
63
+ pass
64
+
65
+ @abstractmethod
66
+ def format_success(self, message: str) -> str:
67
+ """Format a success message.
68
+
69
+ Args:
70
+ message: Success message text
71
+
72
+ Returns:
73
+ Formatted success message
74
+ """
75
+ pass
76
+
77
+ @abstractmethod
78
+ def format_info(self, message: str) -> str:
79
+ """Format an informational message.
80
+
81
+ Args:
82
+ message: Info message text
83
+
84
+ Returns:
85
+ Formatted info message
86
+ """
87
+ pass
88
+
89
+ @abstractmethod
90
+ def format_warning(self, message: str) -> str:
91
+ """Format a warning message.
92
+
93
+ Args:
94
+ message: Warning message text
95
+
96
+ Returns:
97
+ Formatted warning message
98
+ """
99
+ pass
@@ -0,0 +1,245 @@
1
+ """Compact block formatter for copy-friendly terminal output.
2
+
3
+ Renders list items as 2-line blocks:
4
+ - Line 1 (primary): ID + key metadata (fixed-width, colored)
5
+ - Line 2 (detail): Description/content (dimmed, full-width)
6
+
7
+ No borders = easy copy-paste. Wraps naturally on narrow terminals.
8
+ Inspired by gh (GitHub CLI) and kubectl output styles.
9
+ """
10
+
11
+ from typing import Any
12
+
13
+ from rich.console import Console, Group, RenderableType
14
+ from rich.text import Text
15
+
16
+ from cli.infrastructure.console import get_console
17
+ from cli.infrastructure.formatters.base import ColumnMeta, Formatter
18
+
19
+
20
+ # Default styles for compact format columns
21
+ DEFAULT_STYLES = {
22
+ "id": "cyan",
23
+ "uuid": "cyan",
24
+ "name": "white bold",
25
+ "type": "magenta",
26
+ "model": "green",
27
+ "provider": "green",
28
+ "date": "blue",
29
+ "created": "blue",
30
+ "time": "blue",
31
+ "version": "yellow",
32
+ "visibility": "dim",
33
+ "tags": "dim italic",
34
+ "description": "dim",
35
+ "content": "dim",
36
+ "status": "yellow",
37
+ }
38
+
39
+
40
+ def _infer_style(label: str) -> str:
41
+ """Infer a style based on column label.
42
+
43
+ Args:
44
+ label: Column header label
45
+
46
+ Returns:
47
+ Rich style string
48
+ """
49
+ label_lower = label.lower()
50
+ for key, style in DEFAULT_STYLES.items():
51
+ if key in label_lower:
52
+ return style
53
+ return "white"
54
+
55
+
56
+ class CompactFormatter(Formatter):
57
+ """Formatter that renders items as compact 2-line blocks.
58
+
59
+ Line 1: Primary metadata (ID, type, model, date) - colored, spaced
60
+ Line 2: Detail content (description, content preview) - dimmed, indented
61
+
62
+ Benefits:
63
+ - No borders = double-click to select any field
64
+ - Line 2 wraps naturally on narrow terminals
65
+ - Easy to scan vertically (IDs align, models align)
66
+ """
67
+
68
+ def __init__(self, console: Console | None = None):
69
+ """Initialize compact formatter.
70
+
71
+ Args:
72
+ console: Optional Rich console instance. Uses shared console if not provided.
73
+ """
74
+ self.console = console or get_console()
75
+
76
+ def format_entity(self, data: dict[str, Any], title: str | None = None) -> Text:
77
+ """Format entity details as compact key-value block.
78
+
79
+ For single entities, we use a simple key: value format without borders.
80
+
81
+ Args:
82
+ data: Dictionary mapping field labels to values
83
+ title: Optional title for the entity
84
+
85
+ Returns:
86
+ Rich Text object ready for rendering
87
+ """
88
+ output = Text()
89
+
90
+ if title:
91
+ output.append(f"{title}\n", style="bold")
92
+ output.append("-" * len(title) + "\n", style="dim")
93
+
94
+ for field, value in data.items():
95
+ display_value = "-" if value is None else str(value)
96
+ output.append(f"{field}: ", style="cyan")
97
+ output.append(f"{display_value}\n", style="white")
98
+
99
+ return output
100
+
101
+ def format_list(
102
+ self,
103
+ items: list[dict[str, Any]],
104
+ title: str | None = None,
105
+ columns: list[ColumnMeta] | None = None,
106
+ ) -> Group:
107
+ """Format list of items as compact 2-line blocks.
108
+
109
+ Args:
110
+ items: List of dictionaries representing rows
111
+ title: Optional title for the list
112
+ columns: Column metadata for layout (line assignment, styles)
113
+
114
+ Returns:
115
+ Rich Group of Text objects ready for rendering
116
+ """
117
+ if not items:
118
+ return Group(Text(self.format_info("No items to display")))
119
+
120
+ renderables: list[RenderableType] = []
121
+
122
+ # Add title if provided
123
+ if title:
124
+ title_text = Text()
125
+ title_text.append(f"{title}\n", style="bold")
126
+ renderables.append(title_text)
127
+
128
+ # Build column lookup by label
129
+ col_meta_map: dict[str, ColumnMeta] = {}
130
+ if columns:
131
+ for col in columns:
132
+ col_meta_map[col.label] = col
133
+
134
+ # Separate columns into line 1 (primary) and line 2 (detail)
135
+ line1_labels: list[str] = []
136
+ line2_labels: list[str] = []
137
+ if columns:
138
+ for col in columns:
139
+ if col.compact_line == 2:
140
+ line2_labels.append(col.label)
141
+ else:
142
+ line1_labels.append(col.label)
143
+ else:
144
+ # Fallback: all columns on line 1
145
+ if items:
146
+ line1_labels = list(items[0].keys())
147
+
148
+ # Render each item as a 2-line block
149
+ for item in items:
150
+ block = Text()
151
+
152
+ # Line 1: Primary metadata (spaced columns)
153
+ line1_parts: list[tuple[str, str]] = []
154
+ for label in line1_labels:
155
+ raw_value = item.get(label)
156
+ if raw_value is None:
157
+ value = "-"
158
+ else:
159
+ value = str(raw_value)
160
+
161
+ # Get style from column meta or infer from label
162
+ meta = col_meta_map.get(label)
163
+ if meta and meta.compact_style:
164
+ style: str = meta.compact_style
165
+ else:
166
+ style = _infer_style(label)
167
+
168
+ # Apply width padding if specified
169
+ if meta and meta.width:
170
+ value = f"{value:<{meta.width}}"
171
+
172
+ line1_parts.append((value, style))
173
+
174
+ # Join line 1 parts with double space
175
+ for i, (value, style) in enumerate(line1_parts):
176
+ if i > 0:
177
+ block.append(" ", style="default")
178
+ block.append(value, style=style)
179
+
180
+ block.append("\n")
181
+
182
+ # Line 2: Detail content (indented, dimmed)
183
+ if line2_labels:
184
+ detail_parts: list[tuple[str, str]] = []
185
+ for label in line2_labels:
186
+ raw_value = item.get(label)
187
+ if raw_value is None or raw_value == "-":
188
+ continue
189
+ value = str(raw_value)
190
+
191
+ # Get style - default to dim for detail line
192
+ meta = col_meta_map.get(label)
193
+ if meta and meta.compact_style:
194
+ style = meta.compact_style
195
+ else:
196
+ style = "dim"
197
+
198
+ detail_parts.append((value, style))
199
+
200
+ if detail_parts:
201
+ block.append(" ") # 3-space indent
202
+ for i, (value, style) in enumerate(detail_parts):
203
+ if i > 0:
204
+ block.append(" · ", style="dim")
205
+ block.append(value, style=style)
206
+ block.append("\n")
207
+
208
+ # Add blank line between items
209
+ block.append("\n")
210
+ renderables.append(block)
211
+
212
+ return Group(*renderables)
213
+
214
+ def format_success(self, message: str) -> str:
215
+ """Format success message with checkmark.
216
+
217
+ Args:
218
+ message: Success message text
219
+
220
+ Returns:
221
+ Rich markup string
222
+ """
223
+ return f"[green]✓[/green] {message}"
224
+
225
+ def format_info(self, message: str) -> str:
226
+ """Format info message with info icon.
227
+
228
+ Args:
229
+ message: Info message text
230
+
231
+ Returns:
232
+ Rich markup string
233
+ """
234
+ return f"[blue]ℹ[/blue] {message}"
235
+
236
+ def format_warning(self, message: str) -> str:
237
+ """Format warning message with warning icon.
238
+
239
+ Args:
240
+ message: Warning message text
241
+
242
+ Returns:
243
+ Rich markup string
244
+ """
245
+ return f"[yellow]⚠[/yellow] {message}"
@@ -0,0 +1,84 @@
1
+ """JSON formatter for machine-readable output.
2
+
3
+ Provides JSON output suitable for scripting and automation.
4
+ """
5
+
6
+ import json
7
+ from typing import Any
8
+
9
+ from cli.infrastructure.formatters.base import ColumnMeta, Formatter
10
+
11
+
12
+ class JsonFormatter(Formatter):
13
+ """Formatter that outputs JSON for scripting and automation."""
14
+
15
+ def __init__(self, indent: int = 2):
16
+ """Initialize JSON formatter.
17
+
18
+ Args:
19
+ indent: Number of spaces for JSON indentation
20
+ """
21
+ self.indent = indent
22
+
23
+ def format_entity(self, data: dict[str, Any], title: str | None = None) -> str:
24
+ """Format entity as JSON object.
25
+
26
+ Args:
27
+ data: Dictionary mapping field labels to values
28
+ title: Ignored for JSON output
29
+
30
+ Returns:
31
+ JSON string
32
+ """
33
+ return json.dumps(data, indent=self.indent, default=str)
34
+
35
+ def format_list(
36
+ self,
37
+ items: list[dict[str, Any]],
38
+ title: str | None = None,
39
+ columns: list[ColumnMeta] | None = None,
40
+ ) -> str:
41
+ """Format list as JSON array.
42
+
43
+ Args:
44
+ items: List of dictionaries
45
+ title: Ignored for JSON output
46
+ columns: Ignored for JSON output
47
+
48
+ Returns:
49
+ JSON string
50
+ """
51
+ return json.dumps(items, indent=self.indent, default=str)
52
+
53
+ def format_success(self, message: str) -> str:
54
+ """Format success message as JSON.
55
+
56
+ Args:
57
+ message: Success message text
58
+
59
+ Returns:
60
+ JSON string with status and message
61
+ """
62
+ return json.dumps({"status": "success", "message": message}, indent=self.indent)
63
+
64
+ def format_info(self, message: str) -> str:
65
+ """Format info message as JSON.
66
+
67
+ Args:
68
+ message: Info message text
69
+
70
+ Returns:
71
+ JSON string with status and message
72
+ """
73
+ return json.dumps({"status": "info", "message": message}, indent=self.indent)
74
+
75
+ def format_warning(self, message: str) -> str:
76
+ """Format warning message as JSON.
77
+
78
+ Args:
79
+ message: Warning message text
80
+
81
+ Returns:
82
+ JSON string with status and message
83
+ """
84
+ return json.dumps({"status": "warning", "message": message}, indent=self.indent)