aria-code 4.1.3__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 (284) hide show
  1. agents/__init__.py +32 -0
  2. agents/base.py +190 -0
  3. agents/deep/__init__.py +37 -0
  4. agents/deep/calibration_loop.py +144 -0
  5. agents/deep/critic.py +125 -0
  6. agents/deep/deepen.py +193 -0
  7. agents/deep/models.py +149 -0
  8. agents/deep/pipeline.py +164 -0
  9. agents/deep/quant_fusion.py +192 -0
  10. agents/deep/themes.py +95 -0
  11. agents/deep/tiers.py +106 -0
  12. agents/financial/__init__.py +10 -0
  13. agents/financial/catalyst.py +279 -0
  14. agents/financial/debate.py +145 -0
  15. agents/financial/earnings.py +303 -0
  16. agents/financial/fundamental.py +159 -0
  17. agents/financial/macro.py +99 -0
  18. agents/financial/news.py +207 -0
  19. agents/financial/risk.py +132 -0
  20. agents/financial/sector.py +279 -0
  21. agents/financial/synthesis.py +274 -0
  22. agents/financial/technical.py +258 -0
  23. agents/portfolio_agent.py +333 -0
  24. agents/realty/__init__.py +62 -0
  25. agents/realty/asset_diagnosis.py +150 -0
  26. agents/realty/business_match.py +165 -0
  27. agents/realty/cashflow_verify.py +208 -0
  28. agents/realty/contract_rules.py +209 -0
  29. agents/realty/energy_anomaly.py +188 -0
  30. agents/realty/exit_settlement.py +207 -0
  31. agents/realty/fulfillment_risk.py +205 -0
  32. agents/realty/ops_optimize.py +159 -0
  33. agents/realty/revenue_share.py +214 -0
  34. agents/registry.py +144 -0
  35. agents/sports/__init__.py +0 -0
  36. agents/sports/football_agent.py +169 -0
  37. agents/team.py +289 -0
  38. aliyun_data_client.py +660 -0
  39. apps/README.md +12 -0
  40. apps/__init__.py +2 -0
  41. apps/channels/README.md +15 -0
  42. apps/cli/README.md +13 -0
  43. apps/cli/__init__.py +2 -0
  44. apps/cli/bootstrap.py +99 -0
  45. apps/cli/codegen_paths.py +29 -0
  46. apps/cli/commands/__init__.py +16 -0
  47. apps/cli/commands/analysis_cmds.py +288 -0
  48. apps/cli/commands/backtest_cmds.py +1887 -0
  49. apps/cli/commands/broker_cmds.py +1154 -0
  50. apps/cli/commands/business_workflow_cmds.py +289 -0
  51. apps/cli/commands/catalog.py +84 -0
  52. apps/cli/commands/data_cmds.py +405 -0
  53. apps/cli/commands/diagnostic_cmds.py +179 -0
  54. apps/cli/commands/diagnostic_ops_cmds.py +696 -0
  55. apps/cli/commands/finance_render.py +12 -0
  56. apps/cli/commands/market.py +399 -0
  57. apps/cli/commands/market_cmds.py +1276 -0
  58. apps/cli/commands/market_context.py +425 -0
  59. apps/cli/commands/market_render.py +7 -0
  60. apps/cli/commands/model_cmds.py +1579 -0
  61. apps/cli/commands/ops_cmds.py +668 -0
  62. apps/cli/commands/portfolio_cmds.py +962 -0
  63. apps/cli/commands/report.py +377 -0
  64. apps/cli/commands/scaffold_templates.py +617 -0
  65. apps/cli/commands/session_cmds.py +179 -0
  66. apps/cli/commands/session_ux_cmds.py +280 -0
  67. apps/cli/commands/team.py +588 -0
  68. apps/cli/commands/team_render.py +8 -0
  69. apps/cli/commands/ui_cmds.py +358 -0
  70. apps/cli/commands/workflow_cmds.py +279 -0
  71. apps/cli/commands/workspace_cmds.py +1414 -0
  72. apps/cli/config_paths.py +70 -0
  73. apps/cli/config_store.py +61 -0
  74. apps/cli/deterministic.py +122 -0
  75. apps/cli/direct.py +48 -0
  76. apps/cli/github_app_auth.py +135 -0
  77. apps/cli/handlers/__init__.py +11 -0
  78. apps/cli/handlers/broker_handlers.py +122 -0
  79. apps/cli/handlers/chart_handlers.py +1309 -0
  80. apps/cli/handlers/market_handlers.py +2509 -0
  81. apps/cli/handlers/realty_handlers.py +114 -0
  82. apps/cli/handlers/strategy_advice.py +82 -0
  83. apps/cli/hooks.py +180 -0
  84. apps/cli/i18n.py +284 -0
  85. apps/cli/intent.py +136 -0
  86. apps/cli/intent_router.py +217 -0
  87. apps/cli/lifecycle_hooks.py +48 -0
  88. apps/cli/main.py +29 -0
  89. apps/cli/market_metadata.py +135 -0
  90. apps/cli/market_universe.py +265 -0
  91. apps/cli/message_processing.py +257 -0
  92. apps/cli/plan_mode.py +139 -0
  93. apps/cli/plotly_html.py +15 -0
  94. apps/cli/prediction_feedback.py +202 -0
  95. apps/cli/preflight.py +497 -0
  96. apps/cli/project_aria.py +60 -0
  97. apps/cli/prompts/__init__.py +0 -0
  98. apps/cli/prompts/coding.py +658 -0
  99. apps/cli/prompts/system_prompts.py +531 -0
  100. apps/cli/prompts/ui.py +434 -0
  101. apps/cli/providers/__init__.py +1 -0
  102. apps/cli/providers/base.py +271 -0
  103. apps/cli/providers/chat_routing.py +80 -0
  104. apps/cli/providers/llm/__init__.py +1 -0
  105. apps/cli/providers/llm/ollama_stream.py +1170 -0
  106. apps/cli/providers/llm/sse_stream.py +216 -0
  107. apps/cli/providers/runtime_bridge.py +185 -0
  108. apps/cli/runtime_consumer.py +489 -0
  109. apps/cli/session_export.py +87 -0
  110. apps/cli/session_jsonl.py +207 -0
  111. apps/cli/session_store.py +112 -0
  112. apps/cli/todo_tracker.py +190 -0
  113. apps/cli/tools/__init__.py +40 -0
  114. apps/cli/tools/context.py +46 -0
  115. apps/cli/tools/file_tools.py +112 -0
  116. apps/cli/tools/market_tools.py +549 -0
  117. apps/cli/tools/notebook_tools.py +111 -0
  118. apps/cli/tools/system_tools.py +669 -0
  119. apps/cli/tools/write_tools.py +715 -0
  120. apps/cli/tradingview_bridge.py +434 -0
  121. apps/cli/update_check.py +152 -0
  122. apps/cli/utils/__init__.py +0 -0
  123. apps/cli/utils/market_detect.py +1578 -0
  124. apps/daemon/README.md +14 -0
  125. apps/vscode/README.md +115 -0
  126. apps/vscode/package.json +70 -0
  127. aria_cli.py +11636 -0
  128. aria_code-4.1.3.dist-info/METADATA +952 -0
  129. aria_code-4.1.3.dist-info/RECORD +284 -0
  130. aria_code-4.1.3.dist-info/WHEEL +5 -0
  131. aria_code-4.1.3.dist-info/entry_points.txt +2 -0
  132. aria_code-4.1.3.dist-info/licenses/LICENSE +121 -0
  133. aria_code-4.1.3.dist-info/top_level.txt +50 -0
  134. aria_daemon.py +1295 -0
  135. aria_feishu_bot.py +1359 -0
  136. aria_relay_client.py +182 -0
  137. aria_relay_server.py +405 -0
  138. aria_telegram_bot.py +202 -0
  139. ariarc.py +328 -0
  140. artifacts.py +491 -0
  141. backtest_report.py +472 -0
  142. brokers/__init__.py +72 -0
  143. brokers/base.py +207 -0
  144. brokers/capabilities.py +264 -0
  145. brokers/cn/__init__.py +10 -0
  146. brokers/cn/easytrader_broker.py +193 -0
  147. brokers/cn/futu_broker.py +194 -0
  148. brokers/cn/longbridge_broker.py +190 -0
  149. brokers/cn/tiger_broker.py +196 -0
  150. brokers/cn/xtquant_broker.py +175 -0
  151. brokers/config.py +364 -0
  152. brokers/intl/__init__.py +5 -0
  153. brokers/intl/alpaca_broker.py +183 -0
  154. brokers/intl/ibkr_broker.py +215 -0
  155. brokers/intl/webull_broker.py +156 -0
  156. brokers/paper_broker.py +259 -0
  157. brokers/planning.py +296 -0
  158. brokers/registry.py +181 -0
  159. brokers/trading.py +237 -0
  160. change_store.py +127 -0
  161. command_safety.py +19 -0
  162. computer_use_tools.py +504 -0
  163. dashboard_generator.py +578 -0
  164. data_analysis_tools.py +808 -0
  165. data_cleaner.py +483 -0
  166. data_service.py +481 -0
  167. datasources/__init__.py +23 -0
  168. datasources/base.py +166 -0
  169. datasources/router.py +221 -0
  170. datasources/sources/__init__.py +15 -0
  171. datasources/sources/akshare_source.py +269 -0
  172. datasources/sources/alpha_vantage_source.py +202 -0
  173. datasources/sources/edgar_source.py +218 -0
  174. datasources/sources/finnhub_source.py +197 -0
  175. datasources/sources/fred_source.py +219 -0
  176. datasources/sources/tushare_source.py +141 -0
  177. datasources/sources/web_scraper_source.py +278 -0
  178. datasources/sources/world_bank_source.py +205 -0
  179. datasources/sources/yfinance_source.py +152 -0
  180. demo_player.py +204 -0
  181. doctor.py +508 -0
  182. file_analysis_tools.py +734 -0
  183. finance_formulas.py +389 -0
  184. football_data_client.py +1670 -0
  185. intent_classifier.py +358 -0
  186. local_finance_tools.py +3221 -0
  187. local_llm_provider.py +552 -0
  188. macro_tools.py +368 -0
  189. market_data_client.py +1899 -0
  190. mcp_client.py +506 -0
  191. memory_manager.py +245 -0
  192. model_capability.py +416 -0
  193. notification_tools.py +248 -0
  194. packages/__init__.py +23 -0
  195. packages/aria_agents/__init__.py +5 -0
  196. packages/aria_agents/manifest.py +69 -0
  197. packages/aria_core/__init__.py +34 -0
  198. packages/aria_core/architecture.py +192 -0
  199. packages/aria_core/export.py +124 -0
  200. packages/aria_core/manifest.py +65 -0
  201. packages/aria_infra/__init__.py +15 -0
  202. packages/aria_infra/arthera.py +52 -0
  203. packages/aria_infra/doctor.py +246 -0
  204. packages/aria_infra/product.py +37 -0
  205. packages/aria_mcp/__init__.py +25 -0
  206. packages/aria_mcp/bridge.py +38 -0
  207. packages/aria_mcp/config.py +97 -0
  208. packages/aria_mcp/tools.py +61 -0
  209. packages/aria_sdk/__init__.py +19 -0
  210. packages/aria_sdk/client.py +396 -0
  211. packages/aria_sdk/providers.py +70 -0
  212. packages/aria_sdk/streaming.py +73 -0
  213. packages/aria_sdk/types.py +86 -0
  214. packages/aria_services/__init__.py +55 -0
  215. packages/aria_services/context.py +258 -0
  216. packages/aria_services/data.py +11 -0
  217. packages/aria_services/provider_health.py +189 -0
  218. packages/aria_services/registry.py +213 -0
  219. packages/aria_services/usage.py +138 -0
  220. packages/aria_skills/__init__.py +5 -0
  221. packages/aria_skills/registry.py +59 -0
  222. packages/aria_tools/__init__.py +5 -0
  223. packages/aria_tools/registry.py +128 -0
  224. packages/quant_engine/__init__.py +6 -0
  225. packages/quant_engine/sports/__init__.py +72 -0
  226. packages/quant_engine/sports/calibrator.py +353 -0
  227. packages/quant_engine/sports/dixon_coles.py +234 -0
  228. packages/quant_engine/sports/elo.py +299 -0
  229. packages/quant_engine/sports/form.py +188 -0
  230. packages/quant_engine/sports/h2h.py +195 -0
  231. packages/quant_engine/sports/ml_model.py +354 -0
  232. packages/quant_engine/sports/predictor.py +311 -0
  233. packages/quant_engine/sports/tracker.py +664 -0
  234. packages/quant_engine/stochastic/__init__.py +27 -0
  235. packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  236. packages/quant_engine/stochastic/ito_calculus.py +477 -0
  237. packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  238. packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  239. packages/quant_engine/stochastic/options_pricing.py +573 -0
  240. packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  241. plan_utils.py +194 -0
  242. plugin_loader.py +328 -0
  243. portfolio_ledger.py +262 -0
  244. privacy/__init__.py +5 -0
  245. privacy/feedback.py +123 -0
  246. project_tools.py +525 -0
  247. providers/__init__.py +30 -0
  248. providers/llm/__init__.py +19 -0
  249. providers/llm/anthropic.py +184 -0
  250. providers/llm/base.py +139 -0
  251. providers/llm/ollama.py +128 -0
  252. providers/llm/openai_compat.py +282 -0
  253. providers/llm/registry.py +358 -0
  254. realty_data_tools.py +659 -0
  255. report_generator.py +1314 -0
  256. runtime/__init__.py +103 -0
  257. runtime/agent_loop.py +1183 -0
  258. runtime/approval.py +51 -0
  259. runtime/events.py +102 -0
  260. runtime/gateway.py +128 -0
  261. runtime/lsp.py +346 -0
  262. runtime/subagent.py +258 -0
  263. runtime/tool_executor.py +104 -0
  264. runtime/tool_policy.py +106 -0
  265. safety/__init__.py +21 -0
  266. safety/permissions.py +275 -0
  267. setup_wizard.py +653 -0
  268. strategy_vault.py +420 -0
  269. ui/__init__.py +100 -0
  270. ui/banner.py +310 -0
  271. ui/completer.py +391 -0
  272. ui/console.py +271 -0
  273. ui/image_render.py +243 -0
  274. ui/input_box.py +376 -0
  275. ui/picker.py +195 -0
  276. ui/render/__init__.py +11 -0
  277. ui/render/finance.py +1480 -0
  278. ui/render/market.py +225 -0
  279. ui/render/output.py +681 -0
  280. ui/render/team.py +346 -0
  281. ui/robot.py +235 -0
  282. workspace/__init__.py +6 -0
  283. workspace/files.py +170 -0
  284. workspace/verify.py +113 -0
ui/banner.py ADDED
@@ -0,0 +1,310 @@
1
+ """Startup banner and status-label rendering for Aria Code.
2
+
3
+ All functions accept only primitive values (strings, dicts, ints, bools)
4
+ so aria_cli.py can do data gathering while this module owns all display.
5
+
6
+ Public surface
7
+ --------------
8
+ render_compact_banner(...) — one-line banner (banner=compact)
9
+ render_full_banner(...) — robot-face + grid panel (banner=full)
10
+ render_try_hints(console) — "try analyze AAPL · /help" line below panel
11
+ privacy_status_label(...)
12
+ control_status_label(...)
13
+ ollama_status_label(...)
14
+ bottom_toolbar_parts(...)
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import os
20
+ import shutil
21
+ from typing import Optional
22
+
23
+
24
+ def _t(key: str, lang: str) -> str:
25
+ """Thin wrapper around i18n.t() — tolerates import failure."""
26
+ try:
27
+ from apps.cli.i18n import t as _translate
28
+ return _translate(key, lang=lang)
29
+ except Exception:
30
+ _fallback = {
31
+ "sharing_on": "sharing on", "local_only": "local-only",
32
+ "network_on": "network on", "network_off": "network off",
33
+ "privacy": "privacy", "ollama_online": "Ollama online",
34
+ "ollama_offline": "Ollama offline", "cloud": "cloud",
35
+ "local_first_agent": "local-first agent",
36
+ "model": "model", "workspace": "workspace",
37
+ "mode": "mode", "status": "status",
38
+ "tools": "tools", "skills": "skills", "quant": "quant",
39
+ "try": "try", "local": "local", "lite": "lite",
40
+ }
41
+ return _fallback.get(key, key)
42
+
43
+
44
+ # ── Status label helpers ───────────────────────────────────────────────────────
45
+
46
+ def privacy_status_label(config: dict, rich: bool = False, lang: str = "") -> str:
47
+ sharing = bool(config.get("data_sharing", False))
48
+ upload = bool(config.get("feedback_upload", False))
49
+ _lang = lang or config.get("ui_lang", "en")
50
+ if sharing and upload:
51
+ label = _t("sharing_on", _lang)
52
+ return _mark("accent", label) if rich else label
53
+ return _t("local_only", _lang)
54
+
55
+
56
+ def control_status_label(config: dict, rich: bool = False, lang: str = "") -> str:
57
+ _lang = lang or config.get("ui_lang", "en")
58
+ permission = config.get("permission_mode", "workspace-write")
59
+ net_key = "network_on" if bool(config.get("network_enabled", True)) else "network_off"
60
+ network = _t(net_key, _lang)
61
+ priv_label = _t("privacy", _lang)
62
+ privacy = privacy_status_label(config, rich=rich, lang=_lang)
63
+ return f"{permission} · {network} · {priv_label} {privacy}"
64
+
65
+
66
+ def ollama_status_label(
67
+ ollama_alive: bool,
68
+ installed_models: set,
69
+ config: dict,
70
+ rich: bool = False,
71
+ lang: str = "",
72
+ ) -> str:
73
+ _lang = lang or config.get("ui_lang", "en")
74
+ count = len(installed_models)
75
+ has_cloud = bool(
76
+ config.get("auth_token")
77
+ or os.getenv("ANTHROPIC_API_KEY")
78
+ or os.getenv("OPENAI_API_KEY")
79
+ or os.getenv("DEEPSEEK_API_KEY")
80
+ )
81
+ cloud_word = _t("cloud", _lang)
82
+ cloud_rich = _mark("muted", f"· {cloud_word} ✓")
83
+ cloud_tag = (
84
+ f" {cloud_rich}" if rich
85
+ else f" · {cloud_word} ✓"
86
+ ) if has_cloud else ""
87
+ model_word = _t("model_singular" if count == 1 else "model_plural", _lang)
88
+ if ollama_alive:
89
+ label = f"{_t('ollama_online', _lang)} · {count} {model_word}"
90
+ return f"{label}{cloud_tag}"
91
+ base = _t("ollama_offline", _lang)
92
+ if has_cloud:
93
+ return (f"{base} {cloud_rich}" if rich else f"{base} · {cloud_word} ✓")
94
+ return base
95
+
96
+
97
+ def bottom_toolbar_parts(
98
+ conversation: list,
99
+ config: dict,
100
+ actual_model: Optional[str],
101
+ get_model_cfg_fn,
102
+ ) -> tuple:
103
+ """Return (model_label, cwd, privacy, est_tokens, max_ctx)."""
104
+ est_tokens = sum(len(m.get("content", "")) for m in conversation) // 3
105
+ mkey = config.get("model", "qwen2.5:7b")
106
+ max_ctx = get_model_cfg_fn(mkey).get("num_ctx", 16384)
107
+ cwd = os.getcwd()
108
+ home = os.path.expanduser("~")
109
+ if cwd.startswith(home):
110
+ cwd = "~" + cwd[len(home):]
111
+ model_label = actual_model or mkey
112
+ if len(model_label) > 28:
113
+ model_label = "…" + model_label[-27:]
114
+ if len(cwd) > 34:
115
+ cwd = "…" + cwd[-33:]
116
+ privacy = "sharing" if bool(config.get("data_sharing", False)) else "local-only"
117
+ return model_label, cwd, privacy, est_tokens, max_ctx
118
+
119
+
120
+ # ── Banner renderers ───────────────────────────────────────────────────────────
121
+
122
+ _MASCOT = "[bold #C08050]◉[/bold #C08050]"
123
+
124
+
125
+ def _is_light_theme() -> bool:
126
+ try:
127
+ from .robot import detect_theme
128
+ return detect_theme() == "light"
129
+ except Exception:
130
+ return False
131
+
132
+
133
+ def _style_tag(style: str, text: str) -> str:
134
+ if style == "dim":
135
+ return f"[dim]{text}[/dim]"
136
+ return f"[{style}]{text}[/{style}]"
137
+
138
+
139
+ def _banner_style(role: str) -> str:
140
+ if _is_light_theme():
141
+ return {
142
+ "primary": "bold #1F2328",
143
+ "muted": "#57606A",
144
+ "subtle": "#6E7781",
145
+ "dim": "#8C959F",
146
+ "accent": "#9A6700",
147
+ }.get(role, "#57606A")
148
+ return {
149
+ "primary": "bold",
150
+ "muted": "dim",
151
+ "subtle": "dim",
152
+ "dim": "dim",
153
+ "accent": "#C08050",
154
+ }.get(role, "dim")
155
+
156
+
157
+ def _mark(role: str, text: str) -> str:
158
+ return _style_tag(_banner_style(role), text)
159
+
160
+
161
+ def _normalize_dim_markup(markup: str) -> str:
162
+ """Make caller-supplied [dim] markup readable in light terminals."""
163
+ if not _is_light_theme():
164
+ return markup
165
+ return markup.replace("[dim]", "[#57606A]").replace("[/dim]", "[/#57606A]")
166
+
167
+
168
+ def render_compact_banner(
169
+ *,
170
+ version: str,
171
+ model_label: str,
172
+ runtime: str, # "cloud" | "local"
173
+ cwd: str,
174
+ control_status_rich: str,
175
+ tool_count: int,
176
+ update_notice: Optional[str] = None,
177
+ console,
178
+ has_rich: bool,
179
+ lang: str = "en",
180
+ ) -> None:
181
+ if not has_rich:
182
+ print(f" Aria Code v{version} {model_label} {cwd}")
183
+ return
184
+ _rt_word = _t("cloud", lang) if runtime == "cloud" else _t("local", lang)
185
+ _rt = _mark("muted", _rt_word)
186
+ _tools_word = _t("tools", lang)
187
+ _mascot = _style_tag(f"bold {_banner_style('accent')}", "◉")
188
+ console.print(
189
+ f" {_mascot} {_mark('primary', 'Aria Code')} {_mark('subtle', f'v{version}')}"
190
+ f" {_mark('dim', '·')} {model_label} {_rt}"
191
+ f" {_mark('dim', '·')} {_mark('muted', cwd)}"
192
+ )
193
+ console.print(_mark("muted", f" {_normalize_dim_markup(control_status_rich)} · {tool_count} {_tools_word} · /help"))
194
+ if update_notice:
195
+ console.print(f" {update_notice}")
196
+
197
+
198
+ def render_full_banner(
199
+ *,
200
+ version: str,
201
+ rt_label: str, # Rich markup: "GPT-OSS 120B [dim]cloud[/dim]"
202
+ cwd: str,
203
+ control_status_rich: str,
204
+ ollama_status_rich: str,
205
+ tool_count: int,
206
+ skill_count: int,
207
+ auto_healed_from: str = "",
208
+ current_id: str = "",
209
+ badge: str = "",
210
+ installed_models: frozenset = frozenset(),
211
+ best_lite_id: str = "", # model ID to suggest when lite badge + not installed
212
+ update_notice: Optional[str] = None,
213
+ console,
214
+ has_rich: bool,
215
+ rich_box,
216
+ lang: str = "en",
217
+ ) -> None:
218
+ _lfa = _t("local_first_agent", lang)
219
+ _model = _t("model", lang)
220
+ _ws = _t("workspace", lang)
221
+ _tools = _t("tools", lang)
222
+ _tip = _t("tip", lang)
223
+ _amatch = _t("auto_matched", lang)
224
+
225
+ if not has_rich:
226
+ print(f"\n Aria Code v{version} {_lfa}")
227
+ print(f" {_model:<10}{rt_label}")
228
+ print(f" {_ws:<10}{cwd}")
229
+ print(f" {control_status_rich}")
230
+ print(f" {ollama_status_rich}")
231
+ print("─" * 60)
232
+ return
233
+
234
+ from rich.table import Table
235
+ from rich.text import Text
236
+
237
+ # Left column: hand-tuned pixel mascot (pure text — works in every terminal,
238
+ # no image deps). Keep it quiet; copper is reserved for state accents.
239
+ from .robot import ROBOT_ROW_COUNT, get_robot_row
240
+
241
+ _face = Text()
242
+ for _idx in range(ROBOT_ROW_COUNT):
243
+ for _style, _text in get_robot_row(2, _idx):
244
+ _face.append(_text, style=_style)
245
+ if _idx < ROBOT_ROW_COUNT - 1:
246
+ _face.append("\n")
247
+
248
+ # Right column: Claude Code-like essentials only. Operational detail is
249
+ # available in the bottom toolbar and slash commands, so startup stays calm.
250
+ _info_lines = [
251
+ f"{_mark('primary', 'Aria Code')} {_mark('subtle', f'v{version} · {_lfa}')}",
252
+ f"{_normalize_dim_markup(rt_label)}",
253
+ f"{_mark('muted', cwd)}",
254
+ ]
255
+ if auto_healed_from:
256
+ _info_lines.append(
257
+ f"{_mark('muted', f'⚙ {_amatch}')} "
258
+ f"[yellow]{auto_healed_from}[/yellow]"
259
+ f" {_mark('dim', '→')} [bold]{current_id}[/bold]"
260
+ )
261
+ if badge == "Fast" and best_lite_id and best_lite_id not in installed_models:
262
+ lite_word = _t("lite", lang)
263
+ _info_lines.append(
264
+ f"[yellow]{_tip}[/yellow] {_mark('muted', f'{lite_word} model — ')}"
265
+ f"[bold]ollama pull {best_lite_id}[/bold] {_mark('muted', f'for full {_tools}')}"
266
+ )
267
+ if update_notice:
268
+ _info_lines.append(update_notice)
269
+
270
+ _info = Text.from_markup("\n".join(_info_lines))
271
+
272
+ _grid = Table.grid(padding=(0, 3))
273
+ _grid.add_column(no_wrap=True, vertical="top")
274
+ _grid.add_column(vertical="middle")
275
+ _grid.add_row(_face, _info)
276
+
277
+ console.print(_grid)
278
+
279
+
280
+ def render_try_hints(console, has_rich: bool, lang: str = "en") -> None:
281
+ """Show natural-language examples that demonstrate LLM-native usage."""
282
+ if not has_rich:
283
+ return
284
+ tcols = shutil.get_terminal_size((80, 24)).columns
285
+ # Hints are natural language sentences — NOT slash commands.
286
+ # The point: users should feel free to just type what they want.
287
+ if lang == "zh":
288
+ hints = [
289
+ (_mark("accent", "宁德时代今天怎么样?"), 19), # 9 CJK×2 + 1
290
+ (_mark("accent", "帮我分析一下持仓风险"), 20), # 10 CJK×2
291
+ (_mark("accent", "生成今日A股晨报看板"), 19), # 9 CJK×2 + "A"×1
292
+ (_mark("subtle", "/help"), 5),
293
+ ]
294
+ else:
295
+ hints = [
296
+ (_mark("accent", "How's NVDA this week?"), 18),
297
+ (_mark("accent", "Analyze my portfolio risk"), 24),
298
+ (_mark("accent", "Generate a morning brief HTML"), 30),
299
+ (_mark("subtle", "/help"), 5),
300
+ ]
301
+ sep = f" {_mark('dim', '·')} "
302
+ parts = []
303
+ used = 8
304
+ for hint_rich, hint_len in hints:
305
+ cost = hint_len + (5 if parts else 0)
306
+ if used + cost <= tcols - 4:
307
+ parts.append(hint_rich)
308
+ used += cost
309
+ _try_word = _t("try", lang)
310
+ console.print(f" {_mark('subtle', _try_word)} " + sep.join(parts) + "\n")