aria-code 0.52.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 (478) hide show
  1. aria_code/__init__.py +42 -0
  2. aria_code/_version.py +20 -0
  3. aria_code/adk_apps/__init__.py +1 -0
  4. aria_code/adk_apps/aria_code_review/__init__.py +1 -0
  5. aria_code/adk_apps/aria_code_review/agent.py +32 -0
  6. aria_code/adk_apps/aria_research/__init__.py +1 -0
  7. aria_code/adk_apps/aria_research/agent.py +31 -0
  8. aria_code/agents/__init__.py +34 -0
  9. aria_code/agents/base.py +318 -0
  10. aria_code/agents/code_review.py +200 -0
  11. aria_code/agents/deep/__init__.py +37 -0
  12. aria_code/agents/deep/calibration_loop.py +144 -0
  13. aria_code/agents/deep/critic.py +125 -0
  14. aria_code/agents/deep/deepen.py +193 -0
  15. aria_code/agents/deep/models.py +149 -0
  16. aria_code/agents/deep/pipeline.py +164 -0
  17. aria_code/agents/deep/quant_fusion.py +192 -0
  18. aria_code/agents/deep/themes.py +95 -0
  19. aria_code/agents/deep/tiers.py +106 -0
  20. aria_code/agents/engineering/__init__.py +14 -0
  21. aria_code/agents/engineering/coder.py +222 -0
  22. aria_code/agents/engineering/tester.py +145 -0
  23. aria_code/agents/financial/__init__.py +21 -0
  24. aria_code/agents/financial/cashflow_burn.py +119 -0
  25. aria_code/agents/financial/catalyst.py +279 -0
  26. aria_code/agents/financial/corporate_finance.py +175 -0
  27. aria_code/agents/financial/debate.py +224 -0
  28. aria_code/agents/financial/earnings.py +303 -0
  29. aria_code/agents/financial/fundamental.py +191 -0
  30. aria_code/agents/financial/macro.py +99 -0
  31. aria_code/agents/financial/news.py +207 -0
  32. aria_code/agents/financial/risk.py +165 -0
  33. aria_code/agents/financial/sector.py +279 -0
  34. aria_code/agents/financial/strategist.py +121 -0
  35. aria_code/agents/financial/stripe_revenue.py +127 -0
  36. aria_code/agents/financial/synthesis.py +274 -0
  37. aria_code/agents/financial/technical.py +258 -0
  38. aria_code/agents/orchestrator.py +44 -0
  39. aria_code/agents/portfolio_agent.py +363 -0
  40. aria_code/agents/realty/__init__.py +62 -0
  41. aria_code/agents/realty/asset_diagnosis.py +152 -0
  42. aria_code/agents/realty/business_match.py +166 -0
  43. aria_code/agents/realty/cashflow_verify.py +209 -0
  44. aria_code/agents/realty/contract_rules.py +210 -0
  45. aria_code/agents/realty/energy_anomaly.py +189 -0
  46. aria_code/agents/realty/exit_settlement.py +208 -0
  47. aria_code/agents/realty/fulfillment_risk.py +206 -0
  48. aria_code/agents/realty/ops_optimize.py +160 -0
  49. aria_code/agents/realty/revenue_share.py +215 -0
  50. aria_code/agents/registry.py +158 -0
  51. aria_code/agents/signal_scheme.py +111 -0
  52. aria_code/agents/sports/__init__.py +0 -0
  53. aria_code/agents/sports/football_agent.py +197 -0
  54. aria_code/agents/supervisor.py +113 -0
  55. aria_code/agents/team.py +492 -0
  56. aria_code/agents/warehouse/__init__.py +25 -0
  57. aria_code/agents/warehouse/contracts.py +26 -0
  58. aria_code/agents/warehouse/dashboard.py +155 -0
  59. aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
  60. aria_code/agents/warehouse/inbound_exceptions.py +25 -0
  61. aria_code/agents/warehouse/inventory_health.py +24 -0
  62. aria_code/agents/warehouse/local_run.py +71 -0
  63. aria_code/agents/warehouse/local_snapshot.py +132 -0
  64. aria_code/agents/warehouse/logistics_cost.py +84 -0
  65. aria_code/agents/warehouse/logistics_sync.py +21 -0
  66. aria_code/agents/warehouse/workflow.py +31 -0
  67. aria_code/aliyun_data_client.py +4 -0
  68. aria_code/apps/README.md +12 -0
  69. aria_code/apps/__init__.py +2 -0
  70. aria_code/apps/channels/README.md +15 -0
  71. aria_code/apps/channels/__init__.py +17 -0
  72. aria_code/apps/channels/intake.py +92 -0
  73. aria_code/apps/channels/registry.py +95 -0
  74. aria_code/apps/channels/tradingview.py +94 -0
  75. aria_code/apps/cli/README.md +13 -0
  76. aria_code/apps/cli/__init__.py +2 -0
  77. aria_code/apps/cli/_optional.py +120 -0
  78. aria_code/apps/cli/bootstrap.py +145 -0
  79. aria_code/apps/cli/broker_render.py +206 -0
  80. aria_code/apps/cli/codegen_paths.py +29 -0
  81. aria_code/apps/cli/commands/__init__.py +16 -0
  82. aria_code/apps/cli/commands/_business_render.py +78 -0
  83. aria_code/apps/cli/commands/_ui.py +71 -0
  84. aria_code/apps/cli/commands/analysis_cmds.py +369 -0
  85. aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
  86. aria_code/apps/cli/commands/auth_cmds.py +239 -0
  87. aria_code/apps/cli/commands/backtest_cmds.py +2543 -0
  88. aria_code/apps/cli/commands/broker_cmds.py +1234 -0
  89. aria_code/apps/cli/commands/business_workflow_cmds.py +319 -0
  90. aria_code/apps/cli/commands/canvas_cmds.py +77 -0
  91. aria_code/apps/cli/commands/catalog.py +87 -0
  92. aria_code/apps/cli/commands/core_cmds.py +2520 -0
  93. aria_code/apps/cli/commands/data_cmds.py +457 -0
  94. aria_code/apps/cli/commands/diagnostic_cmds.py +230 -0
  95. aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
  96. aria_code/apps/cli/commands/file_cmds.py +312 -0
  97. aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
  98. aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
  99. aria_code/apps/cli/commands/market.py +434 -0
  100. aria_code/apps/cli/commands/market_cmds.py +1381 -0
  101. aria_code/apps/cli/commands/market_context.py +425 -0
  102. aria_code/apps/cli/commands/market_render.py +7 -0
  103. aria_code/apps/cli/commands/model_cmds.py +2153 -0
  104. aria_code/apps/cli/commands/ops_cmds.py +721 -0
  105. aria_code/apps/cli/commands/orchestrator_cmds.py +179 -0
  106. aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
  107. aria_code/apps/cli/commands/portfolio_cmds.py +1278 -0
  108. aria_code/apps/cli/commands/report.py +437 -0
  109. aria_code/apps/cli/commands/scaffold_templates.py +617 -0
  110. aria_code/apps/cli/commands/session_cmds.py +236 -0
  111. aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
  112. aria_code/apps/cli/commands/team.py +669 -0
  113. aria_code/apps/cli/commands/team_render.py +8 -0
  114. aria_code/apps/cli/commands/ui_cmds.py +401 -0
  115. aria_code/apps/cli/commands/warehouse_cmds.py +79 -0
  116. aria_code/apps/cli/commands/workflow_cmds.py +480 -0
  117. aria_code/apps/cli/commands/workspace_cmds.py +1494 -0
  118. aria_code/apps/cli/config_paths.py +73 -0
  119. aria_code/apps/cli/config_store.py +78 -0
  120. aria_code/apps/cli/constants.py +88 -0
  121. aria_code/apps/cli/context.py +34 -0
  122. aria_code/apps/cli/deterministic.py +191 -0
  123. aria_code/apps/cli/direct.py +48 -0
  124. aria_code/apps/cli/finance_service_catalog.py +111 -0
  125. aria_code/apps/cli/football_reports.py +231 -0
  126. aria_code/apps/cli/github_app_auth.py +135 -0
  127. aria_code/apps/cli/google_login.py +196 -0
  128. aria_code/apps/cli/handlers/__init__.py +11 -0
  129. aria_code/apps/cli/handlers/broker_handlers.py +122 -0
  130. aria_code/apps/cli/handlers/chart_handlers.py +1309 -0
  131. aria_code/apps/cli/handlers/market_handlers.py +2513 -0
  132. aria_code/apps/cli/handlers/realty_handlers.py +114 -0
  133. aria_code/apps/cli/handlers/sports_handlers.py +22 -0
  134. aria_code/apps/cli/handlers/strategy_advice.py +82 -0
  135. aria_code/apps/cli/helpers.py +302 -0
  136. aria_code/apps/cli/hooks.py +180 -0
  137. aria_code/apps/cli/i18n.py +297 -0
  138. aria_code/apps/cli/intent.py +136 -0
  139. aria_code/apps/cli/intent_router.py +276 -0
  140. aria_code/apps/cli/intent_signals.py +344 -0
  141. aria_code/apps/cli/lifecycle_hooks.py +48 -0
  142. aria_code/apps/cli/local_tool_schemas.py +536 -0
  143. aria_code/apps/cli/main.py +29 -0
  144. aria_code/apps/cli/market_metadata.py +135 -0
  145. aria_code/apps/cli/market_universe.py +487 -0
  146. aria_code/apps/cli/message_processing.py +310 -0
  147. aria_code/apps/cli/model_catalog.py +620 -0
  148. aria_code/apps/cli/pdf_report.py +977 -0
  149. aria_code/apps/cli/plan_mode.py +139 -0
  150. aria_code/apps/cli/plotly_html.py +15 -0
  151. aria_code/apps/cli/prediction_feedback.py +202 -0
  152. aria_code/apps/cli/preflight.py +502 -0
  153. aria_code/apps/cli/project_aria.py +60 -0
  154. aria_code/apps/cli/prompt_assembly.py +59 -0
  155. aria_code/apps/cli/prompts/__init__.py +0 -0
  156. aria_code/apps/cli/prompts/coding.py +53 -0
  157. aria_code/apps/cli/prompts/select.py +142 -0
  158. aria_code/apps/cli/prompts/system_prompts.py +552 -0
  159. aria_code/apps/cli/prompts/ui.py +436 -0
  160. aria_code/apps/cli/provider_endpoints.py +237 -0
  161. aria_code/apps/cli/provider_keys.py +73 -0
  162. aria_code/apps/cli/providers/__init__.py +1 -0
  163. aria_code/apps/cli/providers/base.py +521 -0
  164. aria_code/apps/cli/providers/chat_routing.py +128 -0
  165. aria_code/apps/cli/providers/collaboration.py +98 -0
  166. aria_code/apps/cli/providers/llm/__init__.py +1 -0
  167. aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
  168. aria_code/apps/cli/providers/llm/ollama_stream.py +1594 -0
  169. aria_code/apps/cli/providers/llm/response_cache.py +60 -0
  170. aria_code/apps/cli/providers/llm/sse_stream.py +257 -0
  171. aria_code/apps/cli/providers/runtime_bridge.py +353 -0
  172. aria_code/apps/cli/providers/vertexai_stream.py +316 -0
  173. aria_code/apps/cli/runtime_consumer.py +657 -0
  174. aria_code/apps/cli/sandbox.py +32 -0
  175. aria_code/apps/cli/session_export.py +120 -0
  176. aria_code/apps/cli/session_jsonl.py +208 -0
  177. aria_code/apps/cli/session_store.py +112 -0
  178. aria_code/apps/cli/skills_catalog.py +273 -0
  179. aria_code/apps/cli/todo_tracker.py +190 -0
  180. aria_code/apps/cli/tool_executor.py +823 -0
  181. aria_code/apps/cli/tool_registry.py +34 -0
  182. aria_code/apps/cli/tool_scope.py +106 -0
  183. aria_code/apps/cli/tools/__init__.py +40 -0
  184. aria_code/apps/cli/tools/context.py +46 -0
  185. aria_code/apps/cli/tools/file_tools.py +180 -0
  186. aria_code/apps/cli/tools/market_tools.py +620 -0
  187. aria_code/apps/cli/tools/notebook_tools.py +111 -0
  188. aria_code/apps/cli/tools/system_tools.py +726 -0
  189. aria_code/apps/cli/tools/write_tools.py +845 -0
  190. aria_code/apps/cli/tradingview_bridge.py +529 -0
  191. aria_code/apps/cli/turn_planning.py +137 -0
  192. aria_code/apps/cli/update_check.py +153 -0
  193. aria_code/apps/cli/utils/__init__.py +0 -0
  194. aria_code/apps/cli/utils/market_detect.py +1597 -0
  195. aria_code/apps/daemon/README.md +14 -0
  196. aria_code/apps/vscode/README.md +115 -0
  197. aria_code/apps/vscode/package.json +70 -0
  198. aria_code/aria_cli.py +6743 -0
  199. aria_code/aria_daemon.py +1398 -0
  200. aria_code/aria_feishu_bot.py +1423 -0
  201. aria_code/aria_mcp_server.py +7 -0
  202. aria_code/aria_relay_client.py +4 -0
  203. aria_code/aria_relay_server.py +490 -0
  204. aria_code/aria_telegram_bot.py +202 -0
  205. aria_code/ariarc.py +385 -0
  206. aria_code/artifacts.py +581 -0
  207. aria_code/backtest_engine.py +4 -0
  208. aria_code/backtest_report.py +4 -0
  209. aria_code/brokers/__init__.py +78 -0
  210. aria_code/brokers/automation.py +193 -0
  211. aria_code/brokers/base.py +231 -0
  212. aria_code/brokers/capabilities.py +264 -0
  213. aria_code/brokers/cn/__init__.py +10 -0
  214. aria_code/brokers/cn/easytrader_broker.py +193 -0
  215. aria_code/brokers/cn/futu_broker.py +194 -0
  216. aria_code/brokers/cn/longbridge_broker.py +190 -0
  217. aria_code/brokers/cn/tiger_broker.py +196 -0
  218. aria_code/brokers/cn/xtquant_broker.py +225 -0
  219. aria_code/brokers/config.py +414 -0
  220. aria_code/brokers/intl/__init__.py +5 -0
  221. aria_code/brokers/intl/alpaca_broker.py +183 -0
  222. aria_code/brokers/intl/ibkr_broker.py +215 -0
  223. aria_code/brokers/intl/trading212_broker.py +183 -0
  224. aria_code/brokers/intl/webull_broker.py +156 -0
  225. aria_code/brokers/paper_broker.py +266 -0
  226. aria_code/brokers/planning.py +296 -0
  227. aria_code/brokers/registry.py +213 -0
  228. aria_code/brokers/trading.py +267 -0
  229. aria_code/canva_client.py +4 -0
  230. aria_code/change_store.py +127 -0
  231. aria_code/clients/__init__.py +0 -0
  232. aria_code/clients/aliyun_data_client.py +660 -0
  233. aria_code/clients/aria_relay_client.py +182 -0
  234. aria_code/clients/canva_client.py +284 -0
  235. aria_code/clients/figma_client.py +100 -0
  236. aria_code/clients/football_data_client.py +1671 -0
  237. aria_code/clients/kling_video_client.py +182 -0
  238. aria_code/clients/market_data_client.py +1976 -0
  239. aria_code/clients/openai_image_client.py +242 -0
  240. aria_code/clients/runway_video_client.py +157 -0
  241. aria_code/clients/warehouse_erp_client.py +105 -0
  242. aria_code/cloud_memory.py +181 -0
  243. aria_code/command_safety.py +19 -0
  244. aria_code/computer_use_tools.py +4 -0
  245. aria_code/dashboard_generator.py +579 -0
  246. aria_code/data_analysis_tools.py +4 -0
  247. aria_code/data_cleaner.py +483 -0
  248. aria_code/data_service.py +513 -0
  249. aria_code/datasources/__init__.py +23 -0
  250. aria_code/datasources/base.py +166 -0
  251. aria_code/datasources/router.py +253 -0
  252. aria_code/datasources/sources/__init__.py +15 -0
  253. aria_code/datasources/sources/akshare_source.py +269 -0
  254. aria_code/datasources/sources/alpha_vantage_source.py +203 -0
  255. aria_code/datasources/sources/edgar_source.py +219 -0
  256. aria_code/datasources/sources/finnhub_source.py +198 -0
  257. aria_code/datasources/sources/fred_source.py +223 -0
  258. aria_code/datasources/sources/tushare_source.py +143 -0
  259. aria_code/datasources/sources/web_scraper_source.py +278 -0
  260. aria_code/datasources/sources/world_bank_source.py +206 -0
  261. aria_code/datasources/sources/yfinance_source.py +152 -0
  262. aria_code/demo_player.py +207 -0
  263. aria_code/doctor.py +813 -0
  264. aria_code/domain/__init__.py +0 -0
  265. aria_code/domain/backtest_engine.py +354 -0
  266. aria_code/domain/backtest_report.py +472 -0
  267. aria_code/domain/finance_formulas.py +389 -0
  268. aria_code/domain/portfolio_ledger.py +307 -0
  269. aria_code/domain/strategy_vault.py +421 -0
  270. aria_code/evals/__init__.py +37 -0
  271. aria_code/evals/harness.py +598 -0
  272. aria_code/evals/runner.py +238 -0
  273. aria_code/external_agent_runner.py +115 -0
  274. aria_code/figma_client.py +4 -0
  275. aria_code/file_analysis_tools.py +4 -0
  276. aria_code/finance_formulas.py +4 -0
  277. aria_code/football_data_client.py +4 -0
  278. aria_code/image_gen_tools.py +4 -0
  279. aria_code/intent_classifier.py +386 -0
  280. aria_code/kling_video_client.py +4 -0
  281. aria_code/licensing.py +151 -0
  282. aria_code/local_finance_tools.py +4 -0
  283. aria_code/local_image_provider.py +4 -0
  284. aria_code/local_llm_provider.py +4 -0
  285. aria_code/macro_tools.py +4 -0
  286. aria_code/markdown_pdf.py +289 -0
  287. aria_code/market_data_client.py +4 -0
  288. aria_code/mcp_client.py +639 -0
  289. aria_code/memory_manager.py +246 -0
  290. aria_code/model_capability.py +486 -0
  291. aria_code/notification_tools.py +4 -0
  292. aria_code/openai_image_client.py +4 -0
  293. aria_code/packages/__init__.py +23 -0
  294. aria_code/packages/adk_bridge/__init__.py +11 -0
  295. aria_code/packages/adk_bridge/code_review_tools.py +49 -0
  296. aria_code/packages/adk_bridge/market_tools.py +125 -0
  297. aria_code/packages/aria_agents/__init__.py +5 -0
  298. aria_code/packages/aria_agents/manifest.py +78 -0
  299. aria_code/packages/aria_core/__init__.py +34 -0
  300. aria_code/packages/aria_core/architecture.py +269 -0
  301. aria_code/packages/aria_core/export.py +147 -0
  302. aria_code/packages/aria_core/manifest.py +65 -0
  303. aria_code/packages/aria_core/paths.py +57 -0
  304. aria_code/packages/aria_core/secure_file.py +68 -0
  305. aria_code/packages/aria_infra/__init__.py +15 -0
  306. aria_code/packages/aria_infra/arthera.py +52 -0
  307. aria_code/packages/aria_infra/doctor.py +246 -0
  308. aria_code/packages/aria_infra/product.py +40 -0
  309. aria_code/packages/aria_mcp/__init__.py +25 -0
  310. aria_code/packages/aria_mcp/bridge.py +201 -0
  311. aria_code/packages/aria_mcp/circuit.py +82 -0
  312. aria_code/packages/aria_mcp/config.py +111 -0
  313. aria_code/packages/aria_mcp/installer.py +95 -0
  314. aria_code/packages/aria_mcp/server.py +1550 -0
  315. aria_code/packages/aria_mcp/tools.py +81 -0
  316. aria_code/packages/aria_sdk/__init__.py +19 -0
  317. aria_code/packages/aria_sdk/client.py +396 -0
  318. aria_code/packages/aria_sdk/providers.py +70 -0
  319. aria_code/packages/aria_sdk/streaming.py +79 -0
  320. aria_code/packages/aria_sdk/types.py +86 -0
  321. aria_code/packages/aria_services/__init__.py +92 -0
  322. aria_code/packages/aria_services/cache.py +60 -0
  323. aria_code/packages/aria_services/context.py +356 -0
  324. aria_code/packages/aria_services/context_checkpoints.py +157 -0
  325. aria_code/packages/aria_services/data.py +11 -0
  326. aria_code/packages/aria_services/provider_health.py +268 -0
  327. aria_code/packages/aria_services/references.py +247 -0
  328. aria_code/packages/aria_services/registry.py +249 -0
  329. aria_code/packages/aria_services/research_protocol.py +57 -0
  330. aria_code/packages/aria_services/research_quality.py +186 -0
  331. aria_code/packages/aria_services/settings.py +157 -0
  332. aria_code/packages/aria_services/usage.py +151 -0
  333. aria_code/packages/aria_skills/__init__.py +46 -0
  334. aria_code/packages/aria_skills/catalog.py +149 -0
  335. aria_code/packages/aria_skills/loader.py +455 -0
  336. aria_code/packages/aria_skills/registry.py +84 -0
  337. aria_code/packages/aria_tools/__init__.py +5 -0
  338. aria_code/packages/aria_tools/financial/__init__.py +8 -0
  339. aria_code/packages/aria_tools/financial/_paths.py +130 -0
  340. aria_code/packages/aria_tools/financial/compliance.py +31 -0
  341. aria_code/packages/aria_tools/financial/factors.py +33 -0
  342. aria_code/packages/aria_tools/financial/risk_tools.py +33 -0
  343. aria_code/packages/aria_tools/financial/strategy.py +31 -0
  344. aria_code/packages/aria_tools/registry.py +128 -0
  345. aria_code/packages/quant_engine/__init__.py +34 -0
  346. aria_code/packages/quant_engine/_namespace.py +62 -0
  347. aria_code/packages/quant_engine/backtest/__init__.py +14 -0
  348. aria_code/packages/quant_engine/backtest/core.py +53 -0
  349. aria_code/packages/quant_engine/backtest/engine.py +96 -0
  350. aria_code/packages/quant_engine/portfolio/__init__.py +13 -0
  351. aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
  352. aria_code/packages/quant_engine/sports/__init__.py +80 -0
  353. aria_code/packages/quant_engine/sports/calibrator.py +354 -0
  354. aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
  355. aria_code/packages/quant_engine/sports/elo.py +300 -0
  356. aria_code/packages/quant_engine/sports/form.py +188 -0
  357. aria_code/packages/quant_engine/sports/h2h.py +195 -0
  358. aria_code/packages/quant_engine/sports/ml_model.py +377 -0
  359. aria_code/packages/quant_engine/sports/predictor.py +312 -0
  360. aria_code/packages/quant_engine/sports/tracker.py +665 -0
  361. aria_code/packages/quant_engine/stochastic/__init__.py +35 -0
  362. aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  363. aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
  364. aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  365. aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  366. aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
  367. aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  368. aria_code/packs/__init__.py +85 -0
  369. aria_code/packs/base.py +168 -0
  370. aria_code/packs/finance/__init__.py +250 -0
  371. aria_code/packs/logistics/__init__.py +169 -0
  372. aria_code/packs/payments/__init__.py +139 -0
  373. aria_code/packs/realty/__init__.py +190 -0
  374. aria_code/packs/registry.py +202 -0
  375. aria_code/packs/rules.py +90 -0
  376. aria_code/plan_utils.py +194 -0
  377. aria_code/plugin_loader.py +328 -0
  378. aria_code/portfolio_ledger.py +4 -0
  379. aria_code/preview_server.py +449 -0
  380. aria_code/privacy/__init__.py +5 -0
  381. aria_code/privacy/feedback.py +123 -0
  382. aria_code/project_review.py +278 -0
  383. aria_code/project_review_api.py +235 -0
  384. aria_code/project_review_server.py +18 -0
  385. aria_code/project_tools.py +4 -0
  386. aria_code/providers/__init__.py +30 -0
  387. aria_code/providers/llm/__init__.py +19 -0
  388. aria_code/providers/llm/anthropic.py +184 -0
  389. aria_code/providers/llm/autoconfig.py +213 -0
  390. aria_code/providers/llm/base.py +139 -0
  391. aria_code/providers/llm/custom.py +158 -0
  392. aria_code/providers/llm/ollama.py +129 -0
  393. aria_code/providers/llm/openai_compat.py +313 -0
  394. aria_code/providers/llm/registry.py +444 -0
  395. aria_code/providers/local_image_provider.py +202 -0
  396. aria_code/providers/local_llm_provider.py +589 -0
  397. aria_code/realty_data_tools.py +4 -0
  398. aria_code/report_exporters.py +150 -0
  399. aria_code/report_generator.py +1764 -0
  400. aria_code/runtime/__init__.py +169 -0
  401. aria_code/runtime/acceptance.py +481 -0
  402. aria_code/runtime/agent_loop.py +1520 -0
  403. aria_code/runtime/approval.py +57 -0
  404. aria_code/runtime/budget.py +232 -0
  405. aria_code/runtime/checkpoints.py +432 -0
  406. aria_code/runtime/crash_report.py +174 -0
  407. aria_code/runtime/events.py +113 -0
  408. aria_code/runtime/gateway.py +150 -0
  409. aria_code/runtime/lsp.py +484 -0
  410. aria_code/runtime/orchestrator.py +55 -0
  411. aria_code/runtime/repo_map.py +700 -0
  412. aria_code/runtime/run_state.py +127 -0
  413. aria_code/runtime/run_store.py +450 -0
  414. aria_code/runtime/self_healing.py +422 -0
  415. aria_code/runtime/subagent.py +547 -0
  416. aria_code/runtime/task_ledger.py +86 -0
  417. aria_code/runtime/task_orchestrator.py +90 -0
  418. aria_code/runtime/tool_executor.py +217 -0
  419. aria_code/runtime/tool_policy.py +107 -0
  420. aria_code/runtime/worktrees.py +215 -0
  421. aria_code/runway_video_client.py +4 -0
  422. aria_code/safety/__init__.py +23 -0
  423. aria_code/safety/permissions.py +306 -0
  424. aria_code/safety/service.py +102 -0
  425. aria_code/setup_wizard.py +673 -0
  426. aria_code/spreadsheet_tools.py +4 -0
  427. aria_code/strategy_vault.py +4 -0
  428. aria_code/tools/__init__.py +0 -0
  429. aria_code/tools/artifact_tools.py +252 -0
  430. aria_code/tools/broker_tools.py +92 -0
  431. aria_code/tools/build_quant_engine.py +97 -0
  432. aria_code/tools/code_audit_tools.py +192 -0
  433. aria_code/tools/computer_use_tools.py +504 -0
  434. aria_code/tools/data_analysis_tools.py +809 -0
  435. aria_code/tools/enterprise_finance_tools.py +156 -0
  436. aria_code/tools/extended_tools.py +240 -0
  437. aria_code/tools/file_analysis_tools.py +885 -0
  438. aria_code/tools/image_gen_tools.py +114 -0
  439. aria_code/tools/local_finance_tools.py +3417 -0
  440. aria_code/tools/logistics_tools.py +135 -0
  441. aria_code/tools/macro_tools.py +369 -0
  442. aria_code/tools/notification_tools.py +249 -0
  443. aria_code/tools/project_tools.py +525 -0
  444. aria_code/tools/realty_data_tools.py +659 -0
  445. aria_code/tools/spreadsheet_tools.py +388 -0
  446. aria_code/tools/stripe_tools.py +115 -0
  447. aria_code/ui/__init__.py +105 -0
  448. aria_code/ui/banner.py +508 -0
  449. aria_code/ui/completer.py +501 -0
  450. aria_code/ui/console.py +281 -0
  451. aria_code/ui/image_render.py +243 -0
  452. aria_code/ui/input_box.py +488 -0
  453. aria_code/ui/picker.py +201 -0
  454. aria_code/ui/render/__init__.py +11 -0
  455. aria_code/ui/render/finance.py +1705 -0
  456. aria_code/ui/render/market.py +225 -0
  457. aria_code/ui/render/output.py +775 -0
  458. aria_code/ui/render/responsive.py +75 -0
  459. aria_code/ui/render/team.py +354 -0
  460. aria_code/ui/robot.py +235 -0
  461. aria_code/ui/startup_dashboard.py +109 -0
  462. aria_code/utils/vscode_uri.py +37 -0
  463. aria_code/video_analysis.py +147 -0
  464. aria_code/video_editor.py +271 -0
  465. aria_code/workspace/__init__.py +6 -0
  466. aria_code/workspace/files.py +193 -0
  467. aria_code/workspace/verify.py +113 -0
  468. aria_code/workspace_changes.py +199 -0
  469. aria_code/workspace_context.py +233 -0
  470. aria_code/workspace_service.py +196 -0
  471. aria_code/workspace_tools.py +150 -0
  472. aria_code-0.52.0.dist-info/METADATA +237 -0
  473. aria_code-0.52.0.dist-info/RECORD +478 -0
  474. aria_code-0.52.0.dist-info/WHEEL +5 -0
  475. aria_code-0.52.0.dist-info/entry_points.txt +3 -0
  476. aria_code-0.52.0.dist-info/licenses/LICENSE +201 -0
  477. aria_code-0.52.0.dist-info/licenses/NOTICE +49 -0
  478. aria_code-0.52.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,281 @@
1
+ """Shared Rich console, availability flags, and ESC-key watcher.
2
+
3
+ Import this instead of repeating the try/except blocks everywhere:
4
+
5
+ from ui.console import console, HAS_RICH, HAS_PT, _SYNTAX_THEME
6
+ from ui.console import _EscWatcher, _esc_watcher
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import asyncio
12
+ import os
13
+ import subprocess
14
+ import sys
15
+ import threading
16
+ import time
17
+ from typing import Optional
18
+
19
+ # ── Rich ───────────────────────────────────────────────────────────────────────
20
+
21
+ try:
22
+ from rich.console import Console
23
+ from rich.markdown import Markdown
24
+ from rich.live import Live
25
+ from rich.text import Text
26
+ from rich.status import Status
27
+ from rich.syntax import Syntax
28
+ from rich.panel import Panel
29
+ from rich.rule import Rule
30
+ from rich import box as rich_box
31
+ from rich.theme import Theme
32
+ HAS_RICH = True
33
+ except ImportError:
34
+ HAS_RICH = False
35
+
36
+ # ── prompt_toolkit ─────────────────────────────────────────────────────────────
37
+
38
+ try:
39
+ from prompt_toolkit import PromptSession
40
+ from prompt_toolkit.completion import Completer, Completion
41
+ from prompt_toolkit.formatted_text import HTML
42
+ from prompt_toolkit.history import FileHistory
43
+ from prompt_toolkit.styles import Style as PTStyle
44
+ HAS_PT = True
45
+ except ImportError:
46
+ HAS_PT = False
47
+
48
+ # ── Console singleton ──────────────────────────────────────────────────────────
49
+
50
+ _SYNTAX_THEME: str = "monokai"
51
+
52
+
53
+ def _detect_terminal_theme() -> str:
54
+ """Return a best-effort terminal theme: ``dark`` or ``light``."""
55
+ explicit = os.getenv("ARIA_RICH_THEME", os.getenv("ARIA_INPUT_THEME", "")).strip().lower()
56
+ if explicit in {"dark", "light"}:
57
+ return explicit
58
+ colorfgbg = os.getenv("COLORFGBG", "")
59
+ if colorfgbg:
60
+ try:
61
+ return "dark" if int(colorfgbg.split(";")[-1]) < 8 else "light"
62
+ except ValueError:
63
+ pass
64
+ # macOS appearance is not the Terminal profile appearance. Apple Terminal's
65
+ # default Basic profile is light even when the OS itself is in dark mode;
66
+ # using AppleInterfaceStyle here produced pale dark-theme headings on white.
67
+ if os.getenv("TERM_PROGRAM", "") == "Apple_Terminal":
68
+ return "light"
69
+ # os.uname() is Unix-only — doesn't exist on Windows at all. sys.platform
70
+ # is the portable equivalent and already covers the "Darwin" case here.
71
+ if sys.platform == "darwin":
72
+ try:
73
+ r = subprocess.run(
74
+ ["defaults", "read", "-g", "AppleInterfaceStyle"],
75
+ capture_output=True, text=True, timeout=0.2, check=False,
76
+ )
77
+ return "dark" if (r.returncode == 0 and "dark" in r.stdout.lower()) else "light"
78
+ except Exception:
79
+ pass
80
+ return "dark"
81
+
82
+
83
+ def _build_rich_theme(theme: str) -> "Theme":
84
+ """Build a Markdown palette with enough contrast for the terminal theme."""
85
+ if theme == "light":
86
+ return Theme({
87
+ "markdown.h1": "bold #24292f",
88
+ "markdown.h2": "bold #24292f",
89
+ "markdown.h3": "bold #8a5a00",
90
+ "markdown.h4": "bold #8a5a00",
91
+ "markdown.h5": "bold #8a5a00",
92
+ "markdown.h6": "bold #8a5a00",
93
+ "markdown.heading": "bold #24292f",
94
+ "markdown.code": "bold #8a5a00",
95
+ "markdown.code_inline": "bold #8a5a00",
96
+ "markdown.link": "underline #0969da",
97
+ "markdown.link_url": "underline #57606a",
98
+ "markdown.item.bullet": "bold #8a5a00",
99
+ "markdown.item.number": "bold #8a5a00",
100
+ "markdown.table.header": "bold #8a5a00",
101
+ "markdown.table.border": "#8c959f",
102
+ "markdown.hr": "#8c959f",
103
+ "markdown.strong": "bold #1f2328",
104
+ "markdown.em": "italic #57606a",
105
+ "markdown.block_quote": "#6e7781",
106
+ })
107
+ return Theme({
108
+ # Keep Markdown close to the terminal palette: neutral text, copper
109
+ # accents, no blue/purple headings, and no black inline-code blocks.
110
+ "markdown.h1": "bold #e8e0d4",
111
+ "markdown.h2": "bold #e8e0d4",
112
+ "markdown.h3": "bold #d6ba8e",
113
+ "markdown.h4": "bold #d6ba8e",
114
+ "markdown.h5": "bold #d6ba8e",
115
+ "markdown.h6": "bold #d6ba8e",
116
+ "markdown.heading": "bold #e8e0d4",
117
+ "markdown.code": "bold #c08050",
118
+ "markdown.code_inline": "bold #c08050",
119
+ "markdown.link": "underline #c08050",
120
+ "markdown.link_url": "underline #8f867a",
121
+ "markdown.item.bullet": "bold #d6ba8e",
122
+ "markdown.item.number": "bold #d6ba8e",
123
+ "markdown.table.header": "bold #d6ba8e",
124
+ "markdown.table.border": "#6f675d",
125
+ "markdown.hr": "#6f675d",
126
+ "markdown.strong": "bold #e8e0d4",
127
+ "markdown.em": "italic #c7beb2",
128
+ "markdown.block_quote": "#a8a096",
129
+ })
130
+
131
+
132
+ if HAS_RICH:
133
+ ARIA_RICH_THEME_NAME = _detect_terminal_theme()
134
+ ARIA_RICH_THEME = _build_rich_theme(ARIA_RICH_THEME_NAME)
135
+ console = Console(highlight=False, theme=ARIA_RICH_THEME)
136
+
137
+ def make_markdown(markup: str) -> Markdown:
138
+ """Create Markdown with Aria's low-saturation terminal theme."""
139
+ from ui.render.output import adapt_markdown_for_width
140
+
141
+ markup = adapt_markdown_for_width(markup, getattr(console, "width", 80))
142
+ return Markdown(
143
+ markup,
144
+ code_theme="bw",
145
+ inline_code_theme="bw",
146
+ )
147
+ else:
148
+ class _FallbackConsole:
149
+ def print(self, *a, **kw):
150
+ print(*[str(x) for x in a])
151
+
152
+ def input(self, prompt: str = "") -> str:
153
+ return input(prompt)
154
+
155
+ def status(self, msg: str):
156
+ class _Ctx:
157
+ def __enter__(self):
158
+ print(msg)
159
+ return self
160
+ def __exit__(self, *a):
161
+ pass
162
+ def update(self, msg: str):
163
+ print(msg)
164
+ return _Ctx()
165
+
166
+ console = _FallbackConsole()
167
+
168
+ def make_markdown(markup: str) -> str:
169
+ return markup
170
+
171
+ # ── termios / raw-mode availability ───────────────────────────────────────────
172
+
173
+ try:
174
+ import termios
175
+ import tty
176
+ import select as _select
177
+ _HAS_TERMIOS = True
178
+ except ImportError:
179
+ _HAS_TERMIOS = False
180
+
181
+
182
+ # ── ESC-key watcher ───────────────────────────────────────────────────────────
183
+
184
+ class _EscWatcher:
185
+ """Background thread that watches for ESC key press to cancel streaming."""
186
+
187
+ def __init__(self):
188
+ self._active = False
189
+ self._paused = False
190
+ self._thread: Optional[threading.Thread] = None
191
+ self._old_settings = None
192
+ self._cancel_event: Optional[asyncio.Event] = None
193
+ self._fd: Optional[int] = None
194
+
195
+ def start(self, cancel_event: asyncio.Event):
196
+ if not _HAS_TERMIOS or not sys.stdin.isatty():
197
+ return
198
+ self._cancel_event = cancel_event
199
+ self._fd = sys.stdin.fileno()
200
+ try:
201
+ self._old_settings = termios.tcgetattr(self._fd)
202
+ tty.setcbreak(self._fd)
203
+ except Exception:
204
+ self._old_settings = None
205
+ return
206
+ self._active = True
207
+ self._paused = False
208
+ self._thread = threading.Thread(target=self._run, daemon=True)
209
+ self._thread.start()
210
+
211
+ def pause(self):
212
+ self._paused = True
213
+ if self._old_settings and self._fd is not None:
214
+ try:
215
+ termios.tcsetattr(self._fd, termios.TCSADRAIN, self._old_settings)
216
+ except Exception:
217
+ pass
218
+
219
+ def resume(self):
220
+ if not self._active or not _HAS_TERMIOS or self._fd is None:
221
+ return
222
+ if self._cancel_event and self._cancel_event.is_set():
223
+ return
224
+ try:
225
+ termios.tcflush(self._fd, termios.TCIFLUSH)
226
+ self._old_settings = termios.tcgetattr(self._fd)
227
+ tty.setcbreak(self._fd)
228
+ except Exception:
229
+ return
230
+ self._paused = False
231
+
232
+ def stop(self):
233
+ self._active = False
234
+ self._paused = False
235
+ if self._old_settings and self._fd is not None:
236
+ try:
237
+ termios.tcsetattr(self._fd, termios.TCSADRAIN, self._old_settings)
238
+ except Exception:
239
+ pass
240
+ self._old_settings = None
241
+ if self._thread:
242
+ self._thread.join(timeout=0.3)
243
+ self._thread = None
244
+
245
+ def _run(self):
246
+ fd = self._fd
247
+ try:
248
+ while self._active:
249
+ if self._paused:
250
+ time.sleep(0.1)
251
+ continue
252
+ try:
253
+ ready, _, _ = _select.select([fd], [], [], 0.15)
254
+ except (ValueError, OSError):
255
+ break
256
+ if not self._active or self._paused:
257
+ continue
258
+ if ready:
259
+ try:
260
+ ch = os.read(fd, 1)
261
+ except OSError:
262
+ break
263
+ if ch == b'\x1b':
264
+ try:
265
+ r2, _, _ = _select.select([fd], [], [], 0.05)
266
+ except (ValueError, OSError):
267
+ break
268
+ if r2:
269
+ try:
270
+ os.read(fd, 16)
271
+ except OSError:
272
+ pass
273
+ else:
274
+ if self._cancel_event:
275
+ self._cancel_event.set()
276
+ self._active = False
277
+ except Exception:
278
+ pass
279
+
280
+
281
+ _esc_watcher = _EscWatcher()
@@ -0,0 +1,243 @@
1
+ """Terminal image rendering — show a real PNG in the terminal, with fallback.
2
+
3
+ Three layered techniques, best-first (exactly what chafa/viu/timg do):
4
+
5
+ 1. iTerm2 inline images (OSC 1337) — iTerm2, WezTerm, ghostty
6
+ 2. Kitty graphics protocol — kitty, ghostty
7
+ 3. Half-block + truecolor downscale (``▀``) — any 24-bit colour terminal
8
+
9
+ Layer 3 is the universal floor: it resizes the image and prints one ``▀`` per
10
+ cell, packing two vertical pixels into each character (foreground = top pixel,
11
+ background = bottom pixel). Pixel-art sources render almost perfectly this way.
12
+
13
+ Used for the startup mascot banner, and reusable for ``/vision`` previews,
14
+ ``/screenshot`` echoes, and inline quant charts.
15
+
16
+ CLI: python3 -m ui.image_render <path> [width] [--half|--iterm|--kitty]
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import base64
22
+ import os
23
+ import sys
24
+
25
+ try:
26
+ from PIL import Image
27
+ _HAS_PIL = True
28
+ except Exception: # pragma: no cover - PIL is a hard dep for this module
29
+ _HAS_PIL = False
30
+
31
+
32
+ # ── Terminal capability detection ────────────────────────────────────────────
33
+ def _in_tmux() -> bool:
34
+ # Image protocols need passthrough wrapping inside tmux; play safe and fall
35
+ # back to half-blocks rather than spraying escape bytes the pane won't eat.
36
+ return bool(os.environ.get("TMUX"))
37
+
38
+
39
+ def supports_iterm() -> bool:
40
+ if _in_tmux():
41
+ return False
42
+ if os.environ.get("TERM_PROGRAM") in ("iTerm.app", "WezTerm", "ghostty"):
43
+ return True
44
+ return bool(os.environ.get("ITERM_SESSION_ID"))
45
+
46
+
47
+ def supports_kitty() -> bool:
48
+ if _in_tmux():
49
+ return False
50
+ if os.environ.get("KITTY_WINDOW_ID"):
51
+ return True
52
+ return os.environ.get("TERM") == "xterm-kitty"
53
+
54
+
55
+ def supports_truecolor() -> bool:
56
+ if os.environ.get("COLORTERM") in ("truecolor", "24bit"):
57
+ return True
58
+ # Most modern terminals are truecolor even without advertising it; only the
59
+ # genuinely ancient (TERM=dumb / linux console) are not.
60
+ return os.environ.get("TERM", "") not in ("", "dumb", "linux")
61
+
62
+
63
+ def best_method() -> str:
64
+ """Return the best available render method for the current terminal."""
65
+ if supports_iterm():
66
+ return "iterm"
67
+ if supports_kitty():
68
+ return "kitty"
69
+ if supports_truecolor():
70
+ return "half"
71
+ return "none"
72
+
73
+
74
+ # ── Protocol emitters ────────────────────────────────────────────────────────
75
+ def _iterm_sequence(png_bytes: bytes, cells_wide: int) -> str:
76
+ """iTerm2 OSC 1337 inline image. Width in character cells, height auto."""
77
+ b64 = base64.b64encode(png_bytes).decode()
78
+ return (
79
+ f"\x1b]1337;File=inline=1;width={cells_wide};"
80
+ f"preserveAspectRatio=1:{b64}\x07"
81
+ )
82
+
83
+
84
+ def _kitty_sequence(png_bytes: bytes, cells_wide: int) -> str:
85
+ """Kitty graphics protocol, PNG payload (f=100), chunked at 4096 bytes."""
86
+ b64 = base64.b64encode(png_bytes).decode()
87
+ chunk = 4096
88
+ parts: list[str] = []
89
+ i = 0
90
+ first = True
91
+ while i < len(b64):
92
+ piece = b64[i : i + chunk]
93
+ i += chunk
94
+ more = 1 if i < len(b64) else 0
95
+ if first:
96
+ # a=T transmit+display, f=100 PNG, c=columns to scale into
97
+ ctrl = f"a=T,f=100,c={cells_wide},m={more}"
98
+ first = False
99
+ else:
100
+ ctrl = f"m={more}"
101
+ parts.append(f"\x1b_G{ctrl};{piece}\x1b\\")
102
+ return "".join(parts)
103
+
104
+
105
+ # ── Half-block fallback (universal) ──────────────────────────────────────────
106
+ _UPPER = "▀" # ▀ upper half block
107
+
108
+
109
+ def autocrop(img: "Image.Image", tol: int = 18, pad: int = 1) -> "Image.Image":
110
+ """Trim a uniform border (e.g. the robot's black canvas) so the subject
111
+ fills the frame. Background colour is sampled from the top-left pixel;
112
+ pixels within ``tol`` of it are treated as border. Falls back to the
113
+ original image if nothing distinct is found.
114
+ """
115
+ from PIL import ImageChops, Image as _I
116
+
117
+ rgb = img.convert("RGB")
118
+ bg = _I.new("RGB", rgb.size, rgb.getpixel((0, 0)))
119
+ diff = ImageChops.difference(rgb, bg).convert("L")
120
+ box = diff.point(lambda p: 255 if p > tol else 0).getbbox()
121
+ if not box:
122
+ return img
123
+ l, t, r, b = box
124
+ l, t = max(0, l - pad), max(0, t - pad)
125
+ r, b = min(img.width, r + pad), min(img.height, b + pad)
126
+ return img.crop((l, t, r, b))
127
+
128
+
129
+ def half_block_render(img: "Image.Image", cells_wide: int = 36) -> str:
130
+ """Render a PIL image as ``▀`` half-blocks with 24-bit colour.
131
+
132
+ Each character is 1 pixel wide and 2 pixels tall, so a square source maps to
133
+ ``cells_wide`` columns × ``cells_wide // 2`` rows. Foreground paints the top
134
+ pixel, background the bottom one.
135
+ """
136
+ img = img.convert("RGB")
137
+ w, h = img.size
138
+ # Sample at cells_wide × (2 px per row). Rows chosen to preserve aspect once
139
+ # the 1:2 cell shape is accounted for, so on-screen proportions match.
140
+ px_w = max(1, cells_wide)
141
+ px_h = max(2, round(px_w * h / w))
142
+ if px_h % 2:
143
+ px_h += 1 # even rows so every cell has a top+bottom pixel
144
+ small = img.resize((px_w, px_h), Image.LANCZOS)
145
+ px = small.load()
146
+
147
+ lines: list[str] = []
148
+ for row in range(0, px_h, 2):
149
+ cells: list[str] = []
150
+ for x in range(px_w):
151
+ tr, tg, tb = px[x, row]
152
+ br, bg, bb = px[x, row + 1]
153
+ cells.append(
154
+ f"\x1b[38;2;{tr};{tg};{tb};48;2;{br};{bg};{bb}m{_UPPER}"
155
+ )
156
+ cells.append("\x1b[0m")
157
+ lines.append("".join(cells))
158
+ return "\n".join(lines)
159
+
160
+
161
+ # ── Public entry point ───────────────────────────────────────────────────────
162
+ def render_image(
163
+ path: str,
164
+ cells_wide: int = 36,
165
+ method: str | None = None,
166
+ crop: bool = True,
167
+ ) -> str | None:
168
+ """Return a printable string that draws ``path`` in the terminal.
169
+
170
+ ``method`` forces one of ``iterm`` / ``kitty`` / ``half``; default auto-detects.
171
+ ``crop`` trims a uniform border first so the subject fills the frame.
172
+ Returns ``None`` if the image can't be loaded or no method is usable.
173
+ """
174
+ chosen = method or best_method()
175
+
176
+ # Protocol path (iTerm2/Kitty) only needs the raw PNG bytes — the terminal
177
+ # scales them. PIL is optional here: with it we autocrop so the subject
178
+ # fills the frame; without it we send the file as-is. This means the real
179
+ # image still shows even when Pillow isn't installed in the venv.
180
+ if chosen in ("iterm", "kitty"):
181
+ try:
182
+ if _HAS_PIL:
183
+ import io
184
+
185
+ img = Image.open(path)
186
+ if crop:
187
+ try:
188
+ img = autocrop(img)
189
+ except Exception:
190
+ pass
191
+ buf = io.BytesIO()
192
+ img.convert("RGB").save(buf, format="PNG")
193
+ data = buf.getvalue()
194
+ else:
195
+ with open(path, "rb") as fh:
196
+ data = fh.read()
197
+ if chosen == "iterm":
198
+ return _iterm_sequence(data, cells_wide)
199
+ return _kitty_sequence(data, cells_wide)
200
+ except Exception:
201
+ chosen = "half" # fall through to the universal path
202
+
203
+ # Half-block fallback needs PIL to resize.
204
+ if chosen == "half" and _HAS_PIL:
205
+ try:
206
+ img = Image.open(path)
207
+ if crop:
208
+ try:
209
+ img = autocrop(img)
210
+ except Exception:
211
+ pass
212
+ return half_block_render(img, cells_wide)
213
+ except Exception:
214
+ return None
215
+ return None
216
+
217
+
218
+ def _main(argv: list[str]) -> int:
219
+ args = [a for a in argv if not a.startswith("--")]
220
+ flags = {a for a in argv if a.startswith("--")}
221
+ if not args:
222
+ print("usage: python3 -m ui.image_render <image> [width] [--half|--iterm|--kitty]")
223
+ return 2
224
+ path = args[0]
225
+ width = int(args[1]) if len(args) > 1 and args[1].isdigit() else 36
226
+ method = None
227
+ if "--half" in flags:
228
+ method = "half"
229
+ elif "--iterm" in flags:
230
+ method = "iterm"
231
+ elif "--kitty" in flags:
232
+ method = "kitty"
233
+ out = render_image(path, width, method)
234
+ if out is None:
235
+ print(f"(cannot render {path}; method={method or best_method()})")
236
+ return 1
237
+ sys.stdout.write(out + "\n")
238
+ sys.stdout.flush()
239
+ return 0
240
+
241
+
242
+ if __name__ == "__main__":
243
+ raise SystemExit(_main(sys.argv[1:]))