systemr-cli 1.0.2__tar.gz → 1.0.4__tar.gz

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 (59) hide show
  1. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/PKG-INFO +1 -1
  2. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/__init__.py +1 -1
  3. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/cli.py +38 -31
  4. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/chat_commands.py +44 -7
  5. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/display/theme.py +6 -3
  6. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/pyproject.toml +1 -1
  7. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/.gitignore +0 -0
  8. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/DESIGN.md +0 -0
  9. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/README.md +0 -0
  10. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/clawhub/systemr-trading/SKILL.md +0 -0
  11. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/clawhub/systemr-trading/scripts/mcp_stdio_proxy.py +0 -0
  12. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/__main__.py +0 -0
  13. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/auth.py +0 -0
  14. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/client.py +0 -0
  15. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/__init__.py +0 -0
  16. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/auth_commands.py +0 -0
  17. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/cron_commands.py +0 -0
  18. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/doctor_command.py +0 -0
  19. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/eval_commands.py +0 -0
  20. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/journal_commands.py +0 -0
  21. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/plan_commands.py +0 -0
  22. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/risk_commands.py +0 -0
  23. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/scan_commands.py +0 -0
  24. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/size_commands.py +0 -0
  25. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/config.py +0 -0
  26. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/confirmation.py +0 -0
  27. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/credits.py +0 -0
  28. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/cron.py +0 -0
  29. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/display/__init__.py +0 -0
  30. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/display/chat_renderer.py +0 -0
  31. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/display/formatters.py +0 -0
  32. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/display/tables.py +0 -0
  33. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/hooks.py +0 -0
  34. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/logging.py +0 -0
  35. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/model_failover.py +0 -0
  36. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/orchestrator.py +0 -0
  37. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/profile.py +0 -0
  38. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/store.py +0 -0
  39. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/streaming.py +0 -0
  40. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/types.py +0 -0
  41. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/__init__.py +0 -0
  42. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_auth.py +0 -0
  43. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_chat_helpers.py +0 -0
  44. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_chat_renderer.py +0 -0
  45. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_cli.py +0 -0
  46. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_config.py +0 -0
  47. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_confirmation.py +0 -0
  48. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_credits.py +0 -0
  49. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_cron.py +0 -0
  50. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_doctor.py +0 -0
  51. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_formatters.py +0 -0
  52. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_hooks.py +0 -0
  53. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_logging.py +0 -0
  54. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_model_failover.py +0 -0
  55. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_orchestrator.py +0 -0
  56. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_profile.py +0 -0
  57. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_store.py +0 -0
  58. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_streaming.py +0 -0
  59. {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: systemr-cli
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: System R AI — trading operating system for agents
5
5
  Author-email: System R AI <ashim@systemr.ai>
6
6
  License-Expression: MIT
@@ -1,3 +1,3 @@
1
1
  """System R CLI — trading operating system in your terminal."""
2
2
 
3
- __version__ = "1.0.2"
3
+ __version__ = "1.0.4"
@@ -53,48 +53,38 @@ def cli(ctx: click.Context) -> None:
53
53
  load_profile_from_config()
54
54
 
55
55
  if ctx.invoked_subcommand is None:
56
- _show_home()
56
+ _show_home_and_chat(ctx)
57
57
 
58
58
 
59
- def _show_home() -> None:
60
- """Home screen — clean, multi-state status display."""
59
+ def _show_home_and_chat(ctx: click.Context) -> None:
60
+ """Home screen — if connected, drop straight into chat. Like Claude Code."""
61
61
  from neo.auth import AuthManager
62
62
  from neo.config import get_api_key
63
- from neo.profile import profile_exists
64
-
65
- print_banner()
66
63
 
67
64
  api_key = get_api_key()
68
65
  auth = AuthManager()
69
66
  token = auth.get_token()
70
67
 
71
- if api_key:
72
- # API key configured (from app.systemr.ai)
73
- console.print(
74
- f" [{GREEN}]{ICON_CONNECTED}[/] [{GREEN}]connected[/] [{GRAY}]via API key ...{api_key[-8:]}[/]"
75
- )
76
- elif token:
77
- # Session token (from systemr login)
68
+ if api_key or token:
69
+ # Connected go straight to chat
70
+ ctx.invoke(chat)
71
+ else:
72
+ # Not connected — show setup instructions
73
+ print_banner()
78
74
  console.print(
79
- f" [{GREEN}]{ICON_CONNECTED}[/] [{GREEN}]connected[/] [{GRAY}]as {token.email}[/]"
75
+ f" [{WHITE}]Get started:[/]"
80
76
  )
81
- else:
82
- # Not connected
77
+ console.print()
83
78
  console.print(
84
- f" [{RED}]{ICON_DISCONNECTED}[/] [{RED}]not connected[/] "
85
- f"[{MUTED}]run[/] [{DIM}]systemr configure --api-key YOUR_KEY[/]"
79
+ f" [{GREEN}]1.[/] [{GRAY}]Get your API key at[/] [{GREEN}]app.systemr.ai[/]"
86
80
  )
87
81
  console.print(
88
- f" [{GRAY}] get your key at[/] [{GREEN}]app.systemr.ai[/]"
82
+ f" [{GREEN}]2.[/] [{GRAY}]Run:[/] [{DIM}]systemr configure --api-key YOUR_KEY[/]"
89
83
  )
90
-
91
- if (api_key or token) and not profile_exists():
92
84
  console.print(
93
- f" [{GRAY}]{ICON_DISCONNECTED}[/] [{GRAY}]no profile yet[/] "
94
- f"[{MUTED}]run[/] [{DIM}]systemr setup[/]"
85
+ f" [{GREEN}]3.[/] [{GRAY}]Run:[/] [{DIM}]systemr chat[/]"
95
86
  )
96
-
97
- console.print()
87
+ console.print()
98
88
 
99
89
 
100
90
  # ── Command registration ────────────────────────────────────────────
@@ -179,13 +169,30 @@ def setup() -> None:
179
169
  console.print(f" [{WHITE}]Welcome. Let's set up your profile.[/]")
180
170
  console.print()
181
171
 
182
- style = click.prompt(click.style(" Trading style", fg="white"))
183
- timeframe = click.prompt(click.style(" Timeframe", fg="white"))
184
- risk_pct = click.prompt(click.style(" Max risk per trade (%)", fg="white"))
185
- daily_loss = click.prompt(click.style(" Max daily loss (%)", fg="white"))
186
- max_pos = click.prompt(click.style(" Max open positions", fg="white"), type=int)
187
- broker = click.prompt(click.style(" Primary broker", fg="white"))
188
172
  name = click.prompt(click.style(" Your name", fg="white"))
173
+ while not name.strip() or len(name.strip()) < 2:
174
+ print_error("Name must be at least 2 characters.")
175
+ name = click.prompt(click.style(" Your name", fg="white"))
176
+
177
+ style = click.prompt(click.style(" Trading style (e.g. momentum, swing, scalp)", fg="white"))
178
+ timeframe = click.prompt(click.style(" Timeframe (e.g. daily, 4h, 1h)", fg="white"))
179
+
180
+ risk_pct = click.prompt(
181
+ click.style(" Max risk per trade (%)", fg="white"),
182
+ type=click.FloatRange(0.1, 10.0),
183
+ default=1.0,
184
+ )
185
+ daily_loss = click.prompt(
186
+ click.style(" Max daily loss (%)", fg="white"),
187
+ type=click.FloatRange(0.5, 20.0),
188
+ default=3.0,
189
+ )
190
+ max_pos = click.prompt(
191
+ click.style(" Max open positions", fg="white"),
192
+ type=click.IntRange(1, 100),
193
+ default=5,
194
+ )
195
+ broker = click.prompt(click.style(" Primary broker", fg="white"))
189
196
 
190
197
  console.print()
191
198
  console.print(f" [{GRAY}]Any hard rules? (comma-separated, or Enter to skip)[/]")
@@ -13,6 +13,7 @@ import asyncio
13
13
  import json
14
14
 
15
15
  import click
16
+ import httpx
16
17
  import structlog
17
18
  from prompt_toolkit import PromptSession
18
19
  from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
@@ -190,11 +191,19 @@ def chat(model_name: str | None, research: bool, resume: bool, pipe_input: str |
190
191
  else:
191
192
  db_session_id = store.create_chat_session()
192
193
 
194
+ from neo.config import get_api_key
193
195
  print_banner()
196
+
197
+ api_key = get_api_key()
198
+ if api_key:
199
+ console.print(f" [{GREEN}]{ICON_SUCCESS}[/] [{GRAY}]connected via API key[/]")
200
+ else:
201
+ console.print(f" [{GREEN}]{ICON_SUCCESS}[/] [{GRAY}]connected as {token.email}[/]")
202
+ if model_name:
203
+ console.print(f" [{DIM}]model: {model_name}[/]")
204
+
194
205
  print_separator()
195
206
  console.print(f" [{DIM}]Type your message. /help for commands. Ctrl+D to exit.[/]")
196
- if model_name:
197
- console.print(f" [{DIM}]Model: {model_name}[/]")
198
207
  console.print()
199
208
 
200
209
  # Session state
@@ -463,10 +472,26 @@ async def _do_chat(
463
472
  })
464
473
 
465
474
  elif event.event == "error":
466
- msg = event.parsed.get("message", event.parsed.get("error", event.data))
467
- render_error(str(msg))
468
- fire_hook(HookEvent.ERROR, context={"message": str(msg)})
469
-
475
+ raw = event.parsed.get("message", event.parsed.get("error", event.data))
476
+ msg = str(raw)
477
+ # Simplify common backend errors for users
478
+ if "Classification backend unavailable" in msg:
479
+ msg = "AI model temporarily unavailable. Try again in a moment."
480
+ elif "AccessDeniedException" in msg:
481
+ msg = "AI model access error. Contact support."
482
+ elif "ThrottlingException" in msg:
483
+ msg = "Rate limited by AI provider. Wait a moment and try again."
484
+ elif len(msg) > 120:
485
+ msg = msg[:120] + "..."
486
+ render_error(msg)
487
+ fire_hook(HookEvent.ERROR, context={"message": msg})
488
+
489
+ except httpx.ConnectError:
490
+ render_error("Cannot connect to System R. Check your internet connection.")
491
+ fire_hook(HookEvent.ERROR, context={"message": "connection_failed"})
492
+ except httpx.ReadTimeout:
493
+ render_error("Response timed out. Try again or use a simpler query.")
494
+ fire_hook(HookEvent.ERROR, context={"message": "read_timeout"})
470
495
  except Exception:
471
496
  # Fall back to blocking
472
497
  try:
@@ -476,8 +501,20 @@ async def _do_chat(
476
501
  collected_text = reply
477
502
  render_assistant_message(reply)
478
503
  credits.add_response(tools=1)
504
+ except httpx.ConnectError:
505
+ render_error("Cannot connect to System R. Check your internet connection.")
506
+ except httpx.HTTPStatusError as exc:
507
+ if exc.response.status_code == 401:
508
+ render_error("Session expired. Run `systemr login` or `systemr configure --api-key`.")
509
+ elif exc.response.status_code == 402:
510
+ render_error("Insufficient credits. Run `systemr pay` to add credits.")
511
+ elif exc.response.status_code == 429:
512
+ render_error("Rate limited. Wait a moment and try again.")
513
+ else:
514
+ render_error(f"Server error ({exc.response.status_code}). Try again.")
479
515
  except Exception as exc:
480
- render_error(f"Chat error: {exc}")
516
+ render_error("Something went wrong. Try again or run `systemr doctor` to check.")
517
+ logger.error("chat_fallback_error", error=str(exc))
481
518
  fire_hook(HookEvent.ERROR, context={"message": str(exc)})
482
519
 
483
520
  return collected_text
@@ -91,13 +91,16 @@ NEO_BANNER = rf"""[{GREEN}]
91
91
  def print_banner() -> None:
92
92
  """Print the System R home screen header.
93
93
 
94
- SYSTEM R AI in brand green bold, version, tagline.
94
+ Atom logo + brand name in bold, version below, tagline.
95
+ Designed to feel substantial on first launch.
95
96
  """
96
97
  from neo import __version__
97
98
  console.print()
98
- console.print(f" [{GREEN}][bold]SYSTEM R AI[/bold][/] [{DIM}]V{__version__}[/]", highlight=False)
99
+ console.print(f" [{GREEN}] [/]", highlight=False)
100
+ console.print(f" [{GREEN}][bold] SYSTEM R[/bold][/] [{WHITE}]AI[/]", highlight=False)
101
+ console.print(f" [{DIM}] v{__version__}[/]", highlight=False)
99
102
  console.print()
100
- console.print(f" [{GRAY}]Trading operating system for agents[/]")
103
+ console.print(f" [{GRAY}] Trading operating system[/]")
101
104
  console.print()
102
105
 
103
106
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "systemr-cli"
7
- version = "1.0.2"
7
+ version = "1.0.4"
8
8
  description = "System R AI — trading operating system for agents"
9
9
  readme = "README.md"
10
10
  license = "MIT"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes