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.
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/PKG-INFO +1 -1
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/__init__.py +1 -1
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/cli.py +38 -31
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/chat_commands.py +44 -7
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/display/theme.py +6 -3
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/pyproject.toml +1 -1
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/.gitignore +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/DESIGN.md +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/README.md +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/clawhub/systemr-trading/SKILL.md +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/clawhub/systemr-trading/scripts/mcp_stdio_proxy.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/__main__.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/auth.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/client.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/__init__.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/auth_commands.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/cron_commands.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/doctor_command.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/eval_commands.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/journal_commands.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/plan_commands.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/risk_commands.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/scan_commands.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/commands/size_commands.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/config.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/confirmation.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/credits.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/cron.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/display/__init__.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/display/chat_renderer.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/display/formatters.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/display/tables.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/hooks.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/logging.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/model_failover.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/orchestrator.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/profile.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/store.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/streaming.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/neo/types.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/__init__.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_auth.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_chat_helpers.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_chat_renderer.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_cli.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_config.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_confirmation.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_credits.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_cron.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_doctor.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_formatters.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_hooks.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_logging.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_model_failover.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_orchestrator.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_profile.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_store.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_streaming.py +0 -0
- {systemr_cli-1.0.2 → systemr_cli-1.0.4}/tests/test_types.py +0 -0
|
@@ -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
|
-
|
|
56
|
+
_show_home_and_chat(ctx)
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
def
|
|
60
|
-
"""Home screen —
|
|
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
|
-
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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" [{
|
|
75
|
+
f" [{WHITE}]Get started:[/]"
|
|
80
76
|
)
|
|
81
|
-
|
|
82
|
-
# Not connected
|
|
77
|
+
console.print()
|
|
83
78
|
console.print(
|
|
84
|
-
f"
|
|
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"
|
|
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"
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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(
|
|
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
|
-
|
|
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}]
|
|
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
|
|
103
|
+
console.print(f" [{GRAY}] Trading operating system[/]")
|
|
101
104
|
console.print()
|
|
102
105
|
|
|
103
106
|
|
|
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
|
|
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
|
|
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
|
|
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
|