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,700 @@
1
+ """Repo map — what is defined where, ranked by what the rest of the repo needs.
2
+
3
+ Why this exists
4
+ ---------------
5
+ Before this module, Aria's only way to find anything in an unfamiliar repository
6
+ was to guess a string and grep for it. That works in a 20-file project and
7
+ falls apart in an enterprise one: the model burns four or five rounds probing
8
+ names that do not exist, and a small local model — the configuration this
9
+ project is actually built around — never recovers, because it has no prior about
10
+ what this codebase calls things.
11
+
12
+ A file tree does not fix that; it says where files are, not what is in them.
13
+ What a model needs on the first round is a *symbol* map: the classes and
14
+ functions this repository defines, which file each lives in, and — critically —
15
+ which of them the rest of the repository actually depends on.
16
+
17
+ Ranking is the whole difference
18
+ -------------------------------
19
+ Every real repo has far more symbols than fit in a context window, so an
20
+ unranked dump is just a differently-shaped grep. This module ranks a file by
21
+ how much the *rest of the codebase* reaches into it: for each symbol a file
22
+ defines, how many other files mention that symbol. A widely-referenced module
23
+ scores high; a leaf script nothing imports scores near zero. That ordering is
24
+ what makes a budgeted map useful — the first 8 KB describe the parts of the
25
+ system a change is most likely to touch.
26
+
27
+ Three corrections keep that number honest, each for an observed way the naive
28
+ count lies:
29
+
30
+ - Names under four characters are ignored. ``id``, ``get``, ``run``, ``fn``
31
+ collide with ordinary vocabulary and would rank whichever file happened to
32
+ define them above everything else.
33
+ - A name defined in more than a handful of files contributes nothing. A
34
+ ``main`` or ``setup`` defined in thirty places tells you nothing about which
35
+ of the thirty matters.
36
+ - One symbol's contribution is capped, so a single popular helper cannot
37
+ outvote a file that is broadly depended on.
38
+
39
+ No new dependencies
40
+ -------------------
41
+ Python is parsed with the stdlib ``ast`` — exact, including nesting. Every
42
+ other language is matched by declaration regex. This is deliberately less
43
+ precise than tree-sitter, and the trade is worth it: an approximate map that
44
+ installs everywhere beats an exact one gated behind a native build, because the
45
+ map's job is to point the model at the right file, and ``read_file`` is what
46
+ establishes the truth once it gets there.
47
+ """
48
+
49
+ from __future__ import annotations
50
+
51
+ import ast
52
+ import re
53
+ import time
54
+ from dataclasses import dataclass, field
55
+ from pathlib import Path
56
+ from typing import Dict, Iterable, List, Optional, Sequence, Set, Tuple
57
+
58
+ __all__ = [
59
+ "REPO_MAP_SCHEMAS",
60
+ "REPO_MAP_TOOLS",
61
+ "RepoMap",
62
+ "Symbol",
63
+ "tool_find_symbol",
64
+ "tool_repo_map",
65
+ ]
66
+
67
+
68
+ # Directories that are never the user's own code. Skipped by name at any
69
+ # depth: a single unpruned ``.venv`` or ``node_modules`` outweighs the entire
70
+ # repository and would dominate both the scan time and the ranking.
71
+ SKIP_DIRS = frozenset({
72
+ ".git", ".hg", ".svn", "__pycache__", "node_modules", ".venv", "venv",
73
+ "env", ".env", "site-packages", "dist", "build", "target", ".next",
74
+ ".nuxt", ".cache", ".mypy_cache", ".pytest_cache", ".ruff_cache",
75
+ ".tox", ".gradle", ".idea", ".vscode", "vendor", "coverage",
76
+ "htmlcov", ".terraform", "Pods", "DerivedData", ".dart_tool",
77
+ "bower_components", "jspm_packages", ".serverless", "out",
78
+ })
79
+
80
+ # Extension → language label. Membership here is also what decides whether a
81
+ # file is scanned at all.
82
+ LANGUAGES: Dict[str, str] = {
83
+ ".py": "python", ".pyi": "python",
84
+ ".js": "javascript", ".jsx": "javascript", ".mjs": "javascript", ".cjs": "javascript",
85
+ ".ts": "typescript", ".tsx": "typescript",
86
+ ".go": "go",
87
+ ".rs": "rust",
88
+ ".java": "java",
89
+ ".kt": "kotlin", ".kts": "kotlin",
90
+ ".swift": "swift",
91
+ ".rb": "ruby",
92
+ ".php": "php",
93
+ ".cs": "csharp",
94
+ ".c": "c", ".h": "c",
95
+ ".cc": "cpp", ".cpp": "cpp", ".cxx": "cpp", ".hpp": "cpp",
96
+ ".scala": "scala",
97
+ ".sh": "shell", ".bash": "shell",
98
+ ".sql": "sql",
99
+ ".lua": "lua",
100
+ ".dart": "dart",
101
+ ".ex": "elixir", ".exs": "elixir",
102
+ }
103
+
104
+ # Declaration patterns per language. Each yields (kind, name). These match
105
+ # declarations, not uses — anchored at line start (modulo indentation and
106
+ # modifiers) so a call like ``foo(function() {})`` is not read as a definition.
107
+ _DECL_PATTERNS: Dict[str, Sequence[Tuple[str, re.Pattern]]] = {
108
+ "javascript": (
109
+ ("class", re.compile(r"^\s*(?:export\s+)?(?:default\s+)?class\s+([A-Za-z_$][\w$]*)")),
110
+ ("function", re.compile(r"^\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s*\*?\s*([A-Za-z_$][\w$]*)")),
111
+ ("function", re.compile(r"^\s*(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>")),
112
+ ),
113
+ "go": (
114
+ ("func", re.compile(r"^func\s+(?:\([^)]*\)\s*)?([A-Za-z_][\w]*)")),
115
+ ("type", re.compile(r"^type\s+([A-Za-z_][\w]*)")),
116
+ ),
117
+ "rust": (
118
+ ("fn", re.compile(r"^\s*(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?fn\s+([A-Za-z_][\w]*)")),
119
+ ("struct", re.compile(r"^\s*(?:pub(?:\([^)]*\))?\s+)?struct\s+([A-Za-z_][\w]*)")),
120
+ ("enum", re.compile(r"^\s*(?:pub(?:\([^)]*\))?\s+)?enum\s+([A-Za-z_][\w]*)")),
121
+ ("trait", re.compile(r"^\s*(?:pub(?:\([^)]*\))?\s+)?trait\s+([A-Za-z_][\w]*)")),
122
+ ("impl", re.compile(r"^\s*impl(?:<[^>]*>)?\s+(?:[\w:<>, ]+\s+for\s+)?([A-Za-z_][\w]*)")),
123
+ ),
124
+ "java": (
125
+ ("class", re.compile(r"^\s*(?:public|private|protected|abstract|final|static|\s)*class\s+([A-Za-z_][\w]*)")),
126
+ ("interface", re.compile(r"^\s*(?:public|private|protected|abstract|\s)*interface\s+([A-Za-z_][\w]*)")),
127
+ ("enum", re.compile(r"^\s*(?:public|private|protected|\s)*enum\s+([A-Za-z_][\w]*)")),
128
+ ),
129
+ "ruby": (
130
+ ("class", re.compile(r"^\s*class\s+([A-Za-z_][\w]*)")),
131
+ ("module", re.compile(r"^\s*module\s+([A-Za-z_][\w]*)")),
132
+ ("def", re.compile(r"^\s*def\s+(?:self\.)?([A-Za-z_][\w]*[?!=]?)")),
133
+ ),
134
+ "php": (
135
+ ("class", re.compile(r"^\s*(?:abstract\s+|final\s+)?class\s+([A-Za-z_][\w]*)")),
136
+ ("function", re.compile(r"^\s*(?:public\s+|private\s+|protected\s+|static\s+)*function\s+([A-Za-z_][\w]*)")),
137
+ ),
138
+ "csharp": (
139
+ ("class", re.compile(r"^\s*(?:public|private|protected|internal|abstract|sealed|static|partial|\s)*class\s+([A-Za-z_][\w]*)")),
140
+ ("interface", re.compile(r"^\s*(?:public|private|protected|internal|\s)*interface\s+([A-Za-z_][\w]*)")),
141
+ ("record", re.compile(r"^\s*(?:public|private|protected|internal|\s)*record\s+([A-Za-z_][\w]*)")),
142
+ ),
143
+ "swift": (
144
+ ("class", re.compile(r"^\s*(?:public\s+|private\s+|internal\s+|open\s+|final\s+)*class\s+([A-Za-z_][\w]*)")),
145
+ ("struct", re.compile(r"^\s*(?:public\s+|private\s+|internal\s+)?struct\s+([A-Za-z_][\w]*)")),
146
+ ("protocol", re.compile(r"^\s*(?:public\s+|private\s+|internal\s+)?protocol\s+([A-Za-z_][\w]*)")),
147
+ ("func", re.compile(r"^\s*(?:public\s+|private\s+|internal\s+|static\s+|override\s+|@\w+\s+)*func\s+([A-Za-z_][\w]*)")),
148
+ ),
149
+ "kotlin": (
150
+ ("class", re.compile(r"^\s*(?:public\s+|private\s+|internal\s+|open\s+|data\s+|sealed\s+|abstract\s+)*class\s+([A-Za-z_][\w]*)")),
151
+ ("object", re.compile(r"^\s*(?:public\s+|private\s+|internal\s+)?object\s+([A-Za-z_][\w]*)")),
152
+ ("fun", re.compile(r"^\s*(?:public\s+|private\s+|internal\s+|suspend\s+|override\s+)*fun\s+(?:<[^>]*>\s*)?([A-Za-z_][\w]*)")),
153
+ ),
154
+ "c": (
155
+ ("struct", re.compile(r"^\s*(?:typedef\s+)?struct\s+([A-Za-z_][\w]*)")),
156
+ ("function", re.compile(r"^[A-Za-z_][\w \*]*\s+\*?([A-Za-z_][\w]*)\s*\([^;]*\)\s*\{")),
157
+ ),
158
+ "scala": (
159
+ ("class", re.compile(r"^\s*(?:case\s+)?class\s+([A-Za-z_][\w]*)")),
160
+ ("object", re.compile(r"^\s*(?:case\s+)?object\s+([A-Za-z_][\w]*)")),
161
+ ("trait", re.compile(r"^\s*trait\s+([A-Za-z_][\w]*)")),
162
+ ("def", re.compile(r"^\s*def\s+([A-Za-z_][\w]*)")),
163
+ ),
164
+ "shell": (
165
+ ("function", re.compile(r"^\s*(?:function\s+)?([A-Za-z_][\w]*)\s*\(\s*\)\s*\{")),
166
+ ),
167
+ "sql": (
168
+ ("table", re.compile(r"(?i)^\s*create\s+table\s+(?:if\s+not\s+exists\s+)?[`\"\[]?([A-Za-z_][\w]*)")),
169
+ ("view", re.compile(r"(?i)^\s*create\s+(?:or\s+replace\s+)?view\s+[`\"\[]?([A-Za-z_][\w]*)")),
170
+ ("function", re.compile(r"(?i)^\s*create\s+(?:or\s+replace\s+)?function\s+[`\"\[]?([A-Za-z_][\w]*)")),
171
+ ),
172
+ "lua": (
173
+ ("function", re.compile(r"^\s*(?:local\s+)?function\s+([A-Za-z_][\w.:]*)")),
174
+ ),
175
+ "dart": (
176
+ ("class", re.compile(r"^\s*(?:abstract\s+)?class\s+([A-Za-z_][\w]*)")),
177
+ ),
178
+ "elixir": (
179
+ ("defmodule", re.compile(r"^\s*defmodule\s+([A-Za-z_][\w.]*)")),
180
+ ("def", re.compile(r"^\s*defp?\s+([a-z_][\w]*[?!]?)")),
181
+ ),
182
+ }
183
+ _DECL_PATTERNS["typescript"] = _DECL_PATTERNS["javascript"] + (
184
+ ("interface", re.compile(r"^\s*(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)")),
185
+ ("type", re.compile(r"^\s*(?:export\s+)?type\s+([A-Za-z_$][\w$]*)\s*=")),
186
+ ("enum", re.compile(r"^\s*(?:export\s+)?(?:const\s+)?enum\s+([A-Za-z_$][\w$]*)")),
187
+ )
188
+ _DECL_PATTERNS["cpp"] = _DECL_PATTERNS["c"] + (
189
+ ("class", re.compile(r"^\s*class\s+([A-Za-z_][\w]*)")),
190
+ ("namespace", re.compile(r"^\s*namespace\s+([A-Za-z_][\w]*)")),
191
+ )
192
+
193
+ _IDENTIFIER = re.compile(r"[A-Za-z_][A-Za-z0-9_]*")
194
+
195
+ # Ranking guards — see the module docstring for what each one corrects.
196
+ _MIN_RANKED_NAME = 4
197
+ _MAX_DEFINING_FILES = 5
198
+ _MAX_SYMBOL_CONTRIBUTION = 20
199
+
200
+ # Rendering budget: how many files a map should try to touch, and roughly what
201
+ # one symbol line costs. Together these turn a char budget into a per-file
202
+ # symbol cap, so breadth survives a small budget.
203
+ _TARGET_FILES = 28
204
+ _AVG_SYMBOL_LINE = 46
205
+
206
+
207
+ @dataclass(frozen=True)
208
+ class Symbol:
209
+ """One definition found in one file."""
210
+
211
+ name: str
212
+ kind: str
213
+ line: int
214
+ parent: str = ""
215
+
216
+ @property
217
+ def qualified(self) -> str:
218
+ return f"{self.parent}.{self.name}" if self.parent else self.name
219
+
220
+
221
+ @dataclass
222
+ class FileEntry:
223
+ path: str
224
+ language: str
225
+ symbols: List[Symbol] = field(default_factory=list)
226
+ mtime: float = 0.0
227
+ size: int = 0
228
+ score: float = 0.0
229
+
230
+
231
+ def _python_symbols(source: str) -> List[Symbol]:
232
+ """Exact symbols for Python, including one level of class nesting.
233
+
234
+ Methods are kept with their class as ``parent`` rather than flattened: a
235
+ map that lists ``run`` without saying it is ``AcceptanceGate.run`` sends
236
+ the model to the wrong file about as often as it helps.
237
+ """
238
+ try:
239
+ tree = ast.parse(source)
240
+ except (SyntaxError, ValueError, RecursionError):
241
+ return []
242
+
243
+ out: List[Symbol] = []
244
+
245
+ def _kind(node: ast.AST) -> str:
246
+ if isinstance(node, ast.AsyncFunctionDef):
247
+ return "async def"
248
+ if isinstance(node, ast.FunctionDef):
249
+ return "def"
250
+ return "class"
251
+
252
+ for node in tree.body:
253
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
254
+ out.append(Symbol(name=node.name, kind=_kind(node), line=node.lineno))
255
+ if isinstance(node, ast.ClassDef):
256
+ for child in node.body:
257
+ if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)):
258
+ out.append(Symbol(
259
+ name=child.name, kind=_kind(child),
260
+ line=child.lineno, parent=node.name,
261
+ ))
262
+ elif isinstance(node, (ast.Assign, ast.AnnAssign)):
263
+ # Module-level constants are part of a module's surface: registries
264
+ # like LOCAL_TOOLS and SKIP_DIRS are exactly what a caller looks up.
265
+ targets = node.targets if isinstance(node, ast.Assign) else [node.target]
266
+ for target in targets:
267
+ if isinstance(target, ast.Name) and target.id.isupper() and len(target.id) > 2:
268
+ out.append(Symbol(name=target.id, kind="const", line=node.lineno))
269
+ return out
270
+
271
+
272
+ def _regex_symbols(source: str, language: str) -> List[Symbol]:
273
+ patterns = _DECL_PATTERNS.get(language)
274
+ if not patterns:
275
+ return []
276
+ out: List[Symbol] = []
277
+ seen: Set[Tuple[str, int]] = set()
278
+ for lineno, line in enumerate(source.splitlines(), 1):
279
+ if len(line) > 400: # minified or generated; never a readable declaration
280
+ continue
281
+ for kind, pattern in patterns:
282
+ match = pattern.match(line)
283
+ if match:
284
+ name = match.group(1)
285
+ key = (name, lineno)
286
+ if key not in seen:
287
+ seen.add(key)
288
+ out.append(Symbol(name=name, kind=kind, line=lineno))
289
+ break
290
+ return out
291
+
292
+
293
+ def extract_symbols(source: str, language: str) -> List[Symbol]:
294
+ """Definitions in *source*. Never raises — an unparseable file has none."""
295
+ try:
296
+ if language == "python":
297
+ return _python_symbols(source)
298
+ return _regex_symbols(source, language)
299
+ except Exception:
300
+ return []
301
+
302
+
303
+ class RepoMap:
304
+ """A scanned, ranked symbol index for one repository root."""
305
+
306
+ def __init__(
307
+ self,
308
+ root: str | Path = ".",
309
+ *,
310
+ max_files: int = 4000,
311
+ max_file_bytes: int = 400_000,
312
+ extra_skip_dirs: Iterable[str] = (),
313
+ ) -> None:
314
+ self.root = Path(root).expanduser().resolve()
315
+ self.max_files = max(1, int(max_files))
316
+ self.max_file_bytes = max(1, int(max_file_bytes))
317
+ self.skip_dirs = SKIP_DIRS | {str(d) for d in extra_skip_dirs}
318
+ self.files: Dict[str, FileEntry] = {}
319
+ self.defs: Dict[str, List[Tuple[str, Symbol]]] = {}
320
+ self.refs: Dict[str, Set[str]] = {}
321
+ self.built_at: float = 0.0
322
+ self.truncated: bool = False
323
+ self.scan_seconds: float = 0.0
324
+
325
+ # ── scanning ──────────────────────────────────────────────────────────
326
+
327
+ def _walk(self) -> List[Path]:
328
+ found: List[Path] = []
329
+ stack = [self.root]
330
+ while stack and len(found) < self.max_files:
331
+ current = stack.pop()
332
+ try:
333
+ entries = list(current.iterdir())
334
+ except (OSError, PermissionError):
335
+ continue
336
+ for entry in entries:
337
+ name = entry.name
338
+ if name.startswith(".") and name not in (".github",):
339
+ if entry.is_dir():
340
+ continue
341
+ try:
342
+ if entry.is_dir():
343
+ if name not in self.skip_dirs:
344
+ stack.append(entry)
345
+ continue
346
+ if entry.suffix.lower() in LANGUAGES:
347
+ found.append(entry)
348
+ if len(found) >= self.max_files:
349
+ self.truncated = True
350
+ break
351
+ except OSError:
352
+ continue
353
+ return found
354
+
355
+ def build(self, *, force: bool = False) -> "RepoMap":
356
+ """Scan the tree. Unchanged files keep their cached symbols.
357
+
358
+ Incremental by mtime and size so a rebuild between turns costs a stat
359
+ per file rather than a reparse — the map is only useful if refreshing
360
+ it is cheaper than the grep round it replaces.
361
+ """
362
+ started = time.time()
363
+ paths = self._walk()
364
+ previous = self.files if not force else {}
365
+ files: Dict[str, FileEntry] = {}
366
+
367
+ for path in paths:
368
+ try:
369
+ stat = path.stat()
370
+ except OSError:
371
+ continue
372
+ if stat.st_size > self.max_file_bytes:
373
+ continue
374
+ try:
375
+ rel = str(path.relative_to(self.root))
376
+ except ValueError:
377
+ rel = str(path)
378
+
379
+ cached = previous.get(rel)
380
+ if cached is not None and cached.mtime == stat.st_mtime and cached.size == stat.st_size:
381
+ files[rel] = cached
382
+ continue
383
+
384
+ language = LANGUAGES.get(path.suffix.lower(), "")
385
+ try:
386
+ source = path.read_text(encoding="utf-8", errors="replace")
387
+ except (OSError, UnicodeError):
388
+ continue
389
+ files[rel] = FileEntry(
390
+ path=rel,
391
+ language=language,
392
+ symbols=extract_symbols(source, language),
393
+ mtime=stat.st_mtime,
394
+ size=stat.st_size,
395
+ )
396
+
397
+ self.files = files
398
+ self._index()
399
+ self.built_at = time.time()
400
+ self.scan_seconds = self.built_at - started
401
+ return self
402
+
403
+ def _index(self) -> None:
404
+ """Build the definition table, the reference table, and the ranking."""
405
+ defs: Dict[str, List[Tuple[str, Symbol]]] = {}
406
+ for entry in self.files.values():
407
+ for symbol in entry.symbols:
408
+ defs.setdefault(symbol.name, []).append((entry.path, symbol))
409
+ self.defs = defs
410
+
411
+ # References: identifiers a file mentions that some *other* file
412
+ # defines. Re-reading each file here is what a second pass costs; it
413
+ # is bounded by max_file_bytes and only runs on a real rebuild.
414
+ known = set(defs)
415
+ refs: Dict[str, Set[str]] = {}
416
+ for entry in self.files.values():
417
+ path = self.root / entry.path
418
+ try:
419
+ source = path.read_text(encoding="utf-8", errors="replace")
420
+ except (OSError, UnicodeError):
421
+ continue
422
+ own = {symbol.name for symbol in entry.symbols}
423
+ for token in set(_IDENTIFIER.findall(source)):
424
+ if token in known and token not in own:
425
+ refs.setdefault(token, set()).add(entry.path)
426
+ self.refs = refs
427
+
428
+ for entry in self.files.values():
429
+ entry.score = self._score(entry)
430
+
431
+ def _effective_refs(self, name: str) -> int:
432
+ """Reference count, with the two names that always lie zeroed out.
433
+
434
+ A name under four characters (``get``, ``run``, ``id``) collides with
435
+ ordinary vocabulary, and a name defined in more than a handful of files
436
+ (``main``, ``setup``, ``status``) cannot tell you which of them is the
437
+ one that matters. Both would otherwise dominate every ranking.
438
+ """
439
+ if len(name) < _MIN_RANKED_NAME:
440
+ return 0
441
+ if len(self.defs.get(name, ())) > _MAX_DEFINING_FILES:
442
+ return 0
443
+ return len(self.refs.get(name, ()))
444
+
445
+ def _score(self, entry: FileEntry) -> float:
446
+ total = 0.0
447
+ counted: Set[str] = set()
448
+ for symbol in entry.symbols:
449
+ if symbol.name in counted:
450
+ continue
451
+ counted.add(symbol.name)
452
+ total += min(self._effective_refs(symbol.name), _MAX_SYMBOL_CONTRIBUTION)
453
+ return total
454
+
455
+ # ── querying ──────────────────────────────────────────────────────────
456
+
457
+ def ranked(self, *, focus: Sequence[str] = ()) -> List[FileEntry]:
458
+ """Files, most depended-upon first.
459
+
460
+ ``focus`` promotes paths the caller already knows are relevant (the
461
+ files in the current diff, say) above the global ranking, because what
462
+ the rest of the repo needs and what *this task* needs are different
463
+ questions and the caller often knows the second one.
464
+ """
465
+ wanted = tuple(f for f in focus if f)
466
+
467
+ def _is_focused(entry: FileEntry) -> bool:
468
+ return any(token in entry.path for token in wanted)
469
+
470
+ return sorted(
471
+ (e for e in self.files.values() if e.symbols),
472
+ key=lambda e: (0 if _is_focused(e) else 1, -e.score, e.path),
473
+ )
474
+
475
+ def notable(self, entry: FileEntry, limit: int) -> List[Symbol]:
476
+ """The *limit* symbols in this file worth naming, in source order.
477
+
478
+ Two rules, in order. Top-level definitions come before methods: a class
479
+ name is the handle a caller navigates by, and three of its methods say
480
+ less about the file than the class itself does. Within a level, the
481
+ most-referenced name wins.
482
+
483
+ Listing a file's first N definitions instead — the obvious
484
+ implementation — is what makes a map read like noise: the top of a
485
+ large module is usually private constants and local helpers, so the
486
+ budget gets spent on the exact symbols no outside caller will ever
487
+ ask about.
488
+ """
489
+ if len(entry.symbols) <= limit:
490
+ return list(entry.symbols)
491
+ ranked = sorted(
492
+ entry.symbols,
493
+ key=lambda s: (
494
+ bool(s.parent),
495
+ s.name.startswith("_"),
496
+ -self._effective_refs(s.name),
497
+ s.line,
498
+ ),
499
+ )[:limit]
500
+ return sorted(ranked, key=lambda s: s.line)
501
+
502
+ def render(self, *, budget_chars: int = 8000, focus: Sequence[str] = ()) -> str:
503
+ """The map, as compact text, cut to fit *budget_chars*."""
504
+ if not self.files:
505
+ return "(repo map empty — no source files found)"
506
+
507
+ header = (
508
+ f"# Repo map — {self.root.name}\n"
509
+ f"# {len(self.files)} files indexed, "
510
+ f"{sum(len(e.symbols) for e in self.files.values())} symbols, "
511
+ f"ranked by how much the rest of the repo references them.\n"
512
+ f"# Use read_file for the actual code; this map only says where to look.\n"
513
+ )
514
+ if self.truncated:
515
+ header += f"# (truncated at {self.max_files} files)\n"
516
+
517
+ blocks: List[str] = []
518
+ used = len(header)
519
+ shown = 0
520
+ entries = self.ranked(focus=focus)
521
+
522
+ # Spread the budget across files instead of letting the first one eat
523
+ # it. A map that covers thirty files shallowly is navigable; one that
524
+ # covers a single file exhaustively is a worse `read_file`.
525
+ per_file = max(3, min(12, budget_chars // (_TARGET_FILES * _AVG_SYMBOL_LINE)))
526
+
527
+ for entry in entries:
528
+ lines = [f"\n{entry.path}"]
529
+ listed = self.notable(entry, per_file)
530
+ for symbol in listed:
531
+ lines.append(f" {symbol.line:>5} {symbol.kind} {symbol.qualified}")
532
+ if len(entry.symbols) > len(listed):
533
+ lines.append(f" … +{len(entry.symbols) - len(listed)} more")
534
+ block = "\n".join(lines)
535
+ if used + len(block) > budget_chars:
536
+ break
537
+ blocks.append(block)
538
+ used += len(block)
539
+ shown += 1
540
+
541
+ footer = ""
542
+ if shown < len(entries):
543
+ footer = (
544
+ f"\n\n… {len(entries) - shown} lower-ranked files omitted. "
545
+ "Use find_symbol(name) to locate anything not listed here."
546
+ )
547
+ return header + "".join(blocks) + footer
548
+
549
+ def find_symbol(self, name: str, *, limit: int = 20) -> dict:
550
+ """Where *name* is defined and which files mention it.
551
+
552
+ This is the call that replaces a speculative grep: it answers "does
553
+ this repo have such a thing, and where" in one round instead of three.
554
+ """
555
+ query = (name or "").strip()
556
+ if not query:
557
+ return {"symbol": "", "definitions": [], "referenced_by": [], "error": "empty name"}
558
+
559
+ definitions = [
560
+ {"file": path, "line": symbol.line, "kind": symbol.kind, "name": symbol.qualified}
561
+ for path, symbol in self.defs.get(query, ())
562
+ ]
563
+
564
+ if not definitions:
565
+ # An exact miss is the common case for a half-remembered name, so
566
+ # answer with the near misses rather than an empty result.
567
+ lowered = query.lower()
568
+ near = sorted(
569
+ (n for n in self.defs if lowered in n.lower()),
570
+ key=lambda n: (len(n), n),
571
+ )[:limit]
572
+ return {
573
+ "symbol": query,
574
+ "definitions": [],
575
+ "referenced_by": [],
576
+ "did_you_mean": near,
577
+ }
578
+
579
+ return {
580
+ "symbol": query,
581
+ "definitions": definitions[:limit],
582
+ "referenced_by": sorted(self.refs.get(query, ()))[:limit],
583
+ "reference_count": len(self.refs.get(query, ())),
584
+ }
585
+
586
+ def summary(self) -> dict:
587
+ return {
588
+ "root": str(self.root),
589
+ "files": len(self.files),
590
+ "symbols": sum(len(e.symbols) for e in self.files.values()),
591
+ "truncated": self.truncated,
592
+ "scan_seconds": round(self.scan_seconds, 3),
593
+ }
594
+
595
+
596
+ # ── tool surface ──────────────────────────────────────────────────────────
597
+ # One cached map per root: rebuilding is incremental, so a repeated call in the
598
+ # same session costs a stat per file rather than a full reparse.
599
+
600
+ _CACHE: Dict[str, RepoMap] = {}
601
+
602
+
603
+ def get_repo_map(root: str | Path = ".", *, refresh: bool = True) -> RepoMap:
604
+ key = str(Path(root).expanduser().resolve())
605
+ existing = _CACHE.get(key)
606
+ if existing is None:
607
+ existing = RepoMap(key)
608
+ _CACHE[key] = existing
609
+ return existing.build()
610
+ if refresh:
611
+ existing.build()
612
+ return existing
613
+
614
+
615
+ def clear_cache() -> None:
616
+ _CACHE.clear()
617
+
618
+
619
+ def tool_repo_map(params: dict) -> dict:
620
+ """Model-facing: an overview of what this repository defines."""
621
+ try:
622
+ root = params.get("path") or "."
623
+ budget = int(params.get("budget_chars") or 8000)
624
+ focus = params.get("focus") or []
625
+ if isinstance(focus, str):
626
+ focus = [focus]
627
+ repo = get_repo_map(root)
628
+ return {
629
+ "success": True,
630
+ "data": {
631
+ "map": repo.render(budget_chars=max(500, min(budget, 40000)), focus=focus),
632
+ **repo.summary(),
633
+ },
634
+ }
635
+ except Exception as exc:
636
+ return {"success": False, "error": f"repo_map failed: {exc}"}
637
+
638
+
639
+ def tool_find_symbol(params: dict) -> dict:
640
+ """Model-facing: where one named thing is defined, and who uses it."""
641
+ try:
642
+ name = str(params.get("name") or params.get("symbol") or "").strip()
643
+ if not name:
644
+ return {"success": False, "error": "Missing 'name' parameter"}
645
+ repo = get_repo_map(params.get("path") or ".")
646
+ return {"success": True, "data": repo.find_symbol(name)}
647
+ except Exception as exc:
648
+ return {"success": False, "error": f"find_symbol failed: {exc}"}
649
+
650
+
651
+ REPO_MAP_TOOLS = {
652
+ "repo_map": (tool_repo_map,
653
+ "Ranked map of what this repository defines and where"),
654
+ "find_symbol": (tool_find_symbol,
655
+ "Locate a class/function by name and see which files reference it"),
656
+ }
657
+
658
+ REPO_MAP_SCHEMAS = [
659
+ {
660
+ "name": "repo_map",
661
+ "description": (
662
+ "Get a ranked map of the classes, functions and constants this repository "
663
+ "defines, with file paths and line numbers. Files are ordered by how much "
664
+ "the rest of the codebase references them, so the top of the map is where "
665
+ "a change most likely belongs. CALL THIS FIRST when working in an "
666
+ "unfamiliar repository — it replaces several rounds of guessing filenames "
667
+ "and grepping. Then use read_file on the files it points to."
668
+ ),
669
+ "parameters": {
670
+ "type": "object",
671
+ "properties": {
672
+ "path": {"type": "string", "description": "Repository root (default: current directory)"},
673
+ "budget_chars": {"type": "integer", "description": "Max size of the returned map (default 8000)"},
674
+ "focus": {
675
+ "type": "array",
676
+ "items": {"type": "string"},
677
+ "description": "Path fragments to list first, e.g. files you are already editing",
678
+ },
679
+ },
680
+ "required": [],
681
+ },
682
+ },
683
+ {
684
+ "name": "find_symbol",
685
+ "description": (
686
+ "Find where a class, function or constant is defined and which files "
687
+ "reference it. Use this instead of guessing a path or grepping for a name: "
688
+ "it returns exact file:line definitions, and near-miss suggestions when the "
689
+ "name does not exist, so a wrong guess costs one round instead of four."
690
+ ),
691
+ "parameters": {
692
+ "type": "object",
693
+ "properties": {
694
+ "name": {"type": "string", "description": "Symbol name to look up"},
695
+ "path": {"type": "string", "description": "Repository root (default: current directory)"},
696
+ },
697
+ "required": ["name"],
698
+ },
699
+ },
700
+ ]