systemr-cli 1.0.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.
neo/streaming.py ADDED
@@ -0,0 +1,315 @@
1
+ """SSE streaming client for System R chat endpoint.
2
+
3
+ Implements the Server-Sent Events specification:
4
+ https://html.spec.whatwg.org/multipage/server-sent-events.html
5
+
6
+ Features:
7
+ - Multi-line data: field concatenation (spec-compliant)
8
+ - Comment filtering (lines starting with :)
9
+ - id: field tracking for reconnection via Last-Event-Id
10
+ - retry: field acknowledgment
11
+ - Reconnection with configurable max retries
12
+ - HTTPS validation on API URL
13
+ - Configurable timeouts and endpoint paths
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ from dataclasses import dataclass, field
20
+ from typing import Any, AsyncIterator
21
+
22
+ import httpx
23
+ import structlog
24
+
25
+ from neo.config import get_api_url
26
+
27
+ logger = structlog.get_logger(module="streaming")
28
+
29
+ # ── Configurable constants ──────────────────────────────────────────
30
+
31
+ MAX_HISTORY_MESSAGES: int = 20
32
+ STREAM_TIMEOUT_SECONDS: float = 180.0
33
+ CONNECT_TIMEOUT_SECONDS: float = 15.0
34
+ BLOCKING_TIMEOUT_SECONDS: float = 120.0
35
+ CHAT_STREAM_PATH: str = "/api/v1/agent/chat/stream"
36
+ CHAT_BLOCKING_PATH: str = "/api/v1/agent/chat"
37
+ CHAT_CONFIRM_PATH: str = "/api/v1/agent/chat/confirm"
38
+ MAX_RETRIES: int = 1
39
+
40
+
41
+ @dataclass
42
+ class SSEEvent:
43
+ """A single Server-Sent Event from the chat stream.
44
+
45
+ Attributes:
46
+ event: Event type (thinking, action, text_delta, confirmation_required, done, error).
47
+ data: Raw data string (may be multi-line per SSE spec).
48
+ id: Optional event ID for reconnection tracking.
49
+ parsed: Data parsed as JSON dict. Falls back to {"text": data} on parse failure.
50
+ """
51
+
52
+ event: str
53
+ data: str
54
+ id: str | None = None
55
+ parsed: dict[str, Any] = field(default_factory=dict)
56
+
57
+ def __post_init__(self) -> None:
58
+ """Parse data as JSON on construction."""
59
+ if self.data:
60
+ try:
61
+ self.parsed = json.loads(self.data)
62
+ except (json.JSONDecodeError, TypeError):
63
+ self.parsed = {"text": self.data}
64
+
65
+
66
+ @dataclass
67
+ class ChatRequest:
68
+ """Request payload for the chat stream endpoint.
69
+
70
+ Attributes:
71
+ user_input: The user's message (required).
72
+ session_id: Optional session identifier.
73
+ model: Optional model override (e.g., "anthropic.claude-opus-4-6").
74
+ history: Conversation history (truncated to MAX_HISTORY_MESSAGES).
75
+ profile: PROFILE.md content for context injection.
76
+ rules: RULES.md content for rule enforcement.
77
+ research_mode: If True, enriches with news/sentiment data.
78
+ thinking_visible: If True, streams thinking events to the client.
79
+ """
80
+
81
+ user_input: str
82
+ session_id: str | None = None
83
+ model: str | None = None
84
+ history: list[dict[str, str]] = field(default_factory=list)
85
+ profile: str | None = None
86
+ rules: str | None = None
87
+ daily_context: str | None = None
88
+ research_mode: bool = False
89
+ thinking_visible: bool = True
90
+
91
+ def to_dict(self) -> dict[str, Any]:
92
+ """Serialize to API request payload.
93
+
94
+ Returns:
95
+ Dict ready for JSON serialization. Empty optional fields are omitted.
96
+ """
97
+ payload: dict[str, Any] = {"user_input": self.user_input}
98
+ if self.session_id:
99
+ payload["session_id"] = self.session_id
100
+ if self.model:
101
+ payload["model"] = self.model
102
+ if self.history:
103
+ payload["history"] = self.history[-MAX_HISTORY_MESSAGES:]
104
+ # Merge profile + daily context into a single context field
105
+ context_parts = []
106
+ if self.profile:
107
+ context_parts.append(self.profile)
108
+ if self.daily_context:
109
+ context_parts.append(self.daily_context)
110
+ if context_parts:
111
+ payload["context"] = "\n\n---\n\n".join(context_parts)
112
+ if self.rules:
113
+ payload["rules"] = self.rules
114
+ payload["research_mode"] = self.research_mode
115
+ return payload
116
+
117
+
118
+ async def stream_chat(
119
+ request: ChatRequest,
120
+ access_token: str,
121
+ ) -> AsyncIterator[SSEEvent]:
122
+ """Stream chat responses from the backend via SSE.
123
+
124
+ Implements the full SSE parsing specification:
125
+ - Multi-line data: fields are concatenated with newlines
126
+ - Comment lines (starting with :) are ignored
127
+ - id: field tracked for reconnection via Last-Event-Id header
128
+ - retry: field acknowledged
129
+ - Reconnection on stream drop (up to MAX_RETRIES)
130
+
131
+ Args:
132
+ request: The chat request payload.
133
+ access_token: Bearer token for authentication.
134
+
135
+ Yields:
136
+ SSEEvent objects: thinking, action, text_delta, confirmation_required, done, error.
137
+ """
138
+ api_url = get_api_url()
139
+ url = f"{api_url}{CHAT_STREAM_PATH}"
140
+
141
+ headers = {
142
+ "Authorization": f"Bearer {access_token}",
143
+ "Accept": "text/event-stream",
144
+ "Cache-Control": "no-cache",
145
+ }
146
+
147
+ last_event_id: str | None = None
148
+ retries = 0
149
+
150
+ while retries <= MAX_RETRIES:
151
+ try:
152
+ if last_event_id:
153
+ headers["Last-Event-Id"] = last_event_id
154
+
155
+ timeout = httpx.Timeout(
156
+ STREAM_TIMEOUT_SECONDS, connect=CONNECT_TIMEOUT_SECONDS,
157
+ )
158
+ async with httpx.AsyncClient(timeout=timeout) as client:
159
+ async with client.stream(
160
+ "POST", url, json=request.to_dict(), headers=headers,
161
+ ) as response:
162
+ if response.status_code != 200:
163
+ body = await response.aread()
164
+ logger.error(
165
+ "stream_http_error",
166
+ status=response.status_code,
167
+ body=body.decode(errors="replace")[:200],
168
+ )
169
+ yield SSEEvent(
170
+ event="error",
171
+ data=json.dumps({
172
+ "message": (
173
+ f"HTTP {response.status_code}: "
174
+ f"{body.decode(errors='replace')[:200]}"
175
+ ),
176
+ }),
177
+ )
178
+ return
179
+
180
+ # SSE spec-compliant parser
181
+ event_type = ""
182
+ data_lines: list[str] = []
183
+ event_id: str | None = None
184
+
185
+ async for line in response.aiter_lines():
186
+ # Comment lines — ignore (often keep-alives)
187
+ if line.startswith(":"):
188
+ continue
189
+
190
+ if line.startswith("event:"):
191
+ event_type = line[6:].strip()
192
+
193
+ elif line.startswith("data:"):
194
+ # SSE spec: consecutive data: lines concatenated with \n
195
+ data_lines.append(line[5:].strip())
196
+
197
+ elif line.startswith("id:"):
198
+ event_id = line[3:].strip()
199
+ last_event_id = event_id
200
+
201
+ elif line.startswith("retry:"):
202
+ pass # Acknowledged, not used for timing
203
+
204
+ elif line == "":
205
+ # Empty line = dispatch event
206
+ if data_lines:
207
+ data_str = "\n".join(data_lines)
208
+ evt = event_type or "text_delta"
209
+ yield SSEEvent(
210
+ event=evt, data=data_str, id=event_id,
211
+ )
212
+ # Reset for next event
213
+ event_type = ""
214
+ data_lines = []
215
+ event_id = None
216
+
217
+ # Flush any remaining event at stream end
218
+ if data_lines:
219
+ data_str = "\n".join(data_lines)
220
+ evt = event_type or "text_delta"
221
+ yield SSEEvent(
222
+ event=evt, data=data_str, id=event_id,
223
+ )
224
+
225
+ # Stream completed normally
226
+ return
227
+
228
+ except (httpx.ReadTimeout, httpx.RemoteProtocolError, httpx.ReadError):
229
+ retries += 1
230
+ logger.warning("stream_connection_lost", retry=retries)
231
+ if retries > MAX_RETRIES:
232
+ yield SSEEvent(
233
+ event="error",
234
+ data=json.dumps({
235
+ "message": "Stream connection lost. Falling back.",
236
+ }),
237
+ )
238
+ return
239
+
240
+ except httpx.ConnectError:
241
+ logger.error("stream_connect_failed")
242
+ yield SSEEvent(
243
+ event="error",
244
+ data=json.dumps({
245
+ "message": "Cannot connect to System R. Check your network.",
246
+ }),
247
+ )
248
+ return
249
+
250
+
251
+ async def chat_blocking(
252
+ request: ChatRequest,
253
+ access_token: str,
254
+ ) -> dict[str, Any]:
255
+ """Fallback blocking chat call (non-streaming).
256
+
257
+ Args:
258
+ request: The chat request payload.
259
+ access_token: Bearer token for authentication.
260
+
261
+ Returns:
262
+ Response dict from the API.
263
+
264
+ Raises:
265
+ httpx.HTTPStatusError: On non-2xx responses.
266
+ """
267
+ api_url = get_api_url()
268
+ url = f"{api_url}{CHAT_BLOCKING_PATH}"
269
+ headers = {"Authorization": f"Bearer {access_token}"}
270
+
271
+ timeout = httpx.Timeout(
272
+ BLOCKING_TIMEOUT_SECONDS, connect=CONNECT_TIMEOUT_SECONDS,
273
+ )
274
+ async with httpx.AsyncClient(timeout=timeout) as client:
275
+ resp = await client.post(url, json=request.to_dict(), headers=headers)
276
+ resp.raise_for_status()
277
+ return resp.json()
278
+
279
+
280
+ async def send_confirmation(
281
+ confirmation_token: str,
282
+ approved: bool,
283
+ access_token: str,
284
+ ) -> dict[str, Any]:
285
+ """Send confirmation response for a pending trade action.
286
+
287
+ The backend pauses execution when it encounters a trade action
288
+ requiring confirmation. It sends a confirmation_required SSE event
289
+ with a token. The CLI prompts the user, then calls this endpoint
290
+ to approve or deny. The backend then resumes or cancels.
291
+
292
+ Args:
293
+ confirmation_token: Token from the confirmation_required event.
294
+ approved: True to approve, False to deny.
295
+ access_token: Bearer token for authentication.
296
+
297
+ Returns:
298
+ Response dict from the API.
299
+ """
300
+ api_url = get_api_url()
301
+ url = f"{api_url}{CHAT_CONFIRM_PATH}"
302
+ headers = {"Authorization": f"Bearer {access_token}"}
303
+
304
+ payload = {
305
+ "confirmation_token": confirmation_token,
306
+ "approved": approved,
307
+ }
308
+
309
+ timeout = httpx.Timeout(
310
+ BLOCKING_TIMEOUT_SECONDS, connect=CONNECT_TIMEOUT_SECONDS,
311
+ )
312
+ async with httpx.AsyncClient(timeout=timeout) as client:
313
+ resp = await client.post(url, json=payload, headers=headers)
314
+ resp.raise_for_status()
315
+ return resp.json()
neo/types.py ADDED
@@ -0,0 +1,109 @@
1
+ """Financial types for System R CLI.
2
+
3
+ Rule: Decimal for money, int for counts, str for IDs.
4
+
5
+ All financial values in System R use decimal.Decimal to prevent
6
+ floating-point rounding errors that can lose real money. These
7
+ types are the foundation — every module that handles prices,
8
+ risk amounts, or percentages imports from here.
9
+
10
+ Usage:
11
+ from neo.types import Price, RiskAmount, Quantity, Percentage, to_decimal
12
+
13
+ entry = Price("198.50")
14
+ risk = RiskAmount("1046.80")
15
+ shares = Quantity(160)
16
+ risk_pct = Percentage("1.97")
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from decimal import Decimal, ROUND_HALF_UP
22
+ from typing import Union
23
+
24
+ # Type aliases for clarity and type checking.
25
+ # All are Decimal — the alias communicates intent.
26
+ Price = Decimal
27
+ RiskAmount = Decimal
28
+ Percentage = Decimal
29
+
30
+ # Quantity is int — shares, contracts, units. Never fractional.
31
+ Quantity = int
32
+
33
+ # Rounding contexts
34
+ PRICE_PLACES = Decimal("0.01") # $198.50
35
+ RISK_PLACES = Decimal("0.01") # $1046.80
36
+ PCT_PLACES = Decimal("0.01") # 1.97%
37
+ R_MULTIPLE_PLACES = Decimal("0.01") # +2.30R
38
+
39
+
40
+ def to_decimal(value: Union[str, int, float, Decimal]) -> Decimal:
41
+ """Convert any numeric value to Decimal safely.
42
+
43
+ Handles str, int, float, and Decimal inputs.
44
+ Float is converted via string to avoid float representation errors.
45
+
46
+ Args:
47
+ value: The numeric value to convert.
48
+
49
+ Returns:
50
+ A Decimal representation of the value.
51
+
52
+ Raises:
53
+ InvalidOperation: If the value cannot be converted.
54
+ """
55
+ if isinstance(value, Decimal):
56
+ return value
57
+ if isinstance(value, float):
58
+ # Convert float via string to avoid repr issues
59
+ # e.g., float 0.1 -> "0.1" -> Decimal("0.1")
60
+ return Decimal(str(value))
61
+ return Decimal(value)
62
+
63
+
64
+ def round_price(value: Decimal) -> Decimal:
65
+ """Round a price to 2 decimal places (standard for equities).
66
+
67
+ Args:
68
+ value: The price to round.
69
+
70
+ Returns:
71
+ Price rounded to nearest cent.
72
+ """
73
+ return value.quantize(PRICE_PLACES, rounding=ROUND_HALF_UP)
74
+
75
+
76
+ def round_risk(value: Decimal) -> Decimal:
77
+ """Round a risk amount to 2 decimal places.
78
+
79
+ Args:
80
+ value: The risk amount to round.
81
+
82
+ Returns:
83
+ Risk amount rounded to nearest cent.
84
+ """
85
+ return value.quantize(RISK_PLACES, rounding=ROUND_HALF_UP)
86
+
87
+
88
+ def round_pct(value: Decimal) -> Decimal:
89
+ """Round a percentage to 2 decimal places.
90
+
91
+ Args:
92
+ value: The percentage to round.
93
+
94
+ Returns:
95
+ Percentage rounded to 2 decimals (e.g., 1.97).
96
+ """
97
+ return value.quantize(PCT_PLACES, rounding=ROUND_HALF_UP)
98
+
99
+
100
+ def round_r(value: Decimal) -> Decimal:
101
+ """Round an R-multiple to 2 decimal places.
102
+
103
+ Args:
104
+ value: The R-multiple to round.
105
+
106
+ Returns:
107
+ R-multiple rounded to 2 decimals (e.g., 2.30).
108
+ """
109
+ return value.quantize(R_MULTIPLE_PLACES, rounding=ROUND_HALF_UP)
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: systemr-cli
3
+ Version: 1.0.0
4
+ Summary: System R AI — trading operating system for agents
5
+ Author-email: System R AI <ashim@systemr.ai>
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Financial and Insurance Industry
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Topic :: Office/Business :: Financial :: Investment
16
+ Requires-Python: >=3.11
17
+ Requires-Dist: click>=8.0.0
18
+ Requires-Dist: httpx>=0.24.0
19
+ Requires-Dist: prompt-toolkit>=3.0
20
+ Requires-Dist: rich>=13.0.0
21
+ Requires-Dist: structlog>=23.0.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
24
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
25
+ Requires-Dist: pytest>=7.0; extra == 'dev'
26
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # System R CLI
30
+
31
+ Trading operating system in your terminal. Like Claude Code, but for trading.
32
+
33
+ Type natural language. System R runs the full stack — 55 institutional-grade tools, 25 broker adapters, risk engine, G-Score — all from your terminal.
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install systemr-cli
39
+ ```
40
+
41
+ Or from source:
42
+
43
+ ```bash
44
+ git clone https://github.com/System-R-AI/systemr-neo.git
45
+ cd systemr-neo
46
+ pip install -e .
47
+ ```
48
+
49
+ Requires Python 3.11+.
50
+
51
+ ## Quick Start
52
+
53
+ ```bash
54
+ # Set up your profile
55
+ systemr setup
56
+
57
+ # Connect to System R
58
+ systemr login
59
+
60
+ # Start chatting
61
+ systemr chat
62
+ ```
63
+
64
+ That's it. Type in plain English:
65
+
66
+ ```
67
+ you > buy TSLA 2% risk
68
+ ```
69
+
70
+ System R will size the position, check risk, show you the trade card, and ask for confirmation before executing.
71
+
72
+ ## Commands
73
+
74
+ ### Trading
75
+ | Command | Description |
76
+ |---------|-------------|
77
+ | `systemr chat` | Interactive conversation with System R |
78
+ | `systemr size` | Position sizing (G-formula) |
79
+ | `systemr risk` | Pre-trade risk validation |
80
+ | `systemr eval` | System performance analysis (G-Score) |
81
+ | `systemr scan` | Market scanner |
82
+ | `systemr plan` | Trade planning |
83
+
84
+ ### Local
85
+ | Command | Description |
86
+ |---------|-------------|
87
+ | `systemr journal` | Trade journal (add/list/show/edit/export) |
88
+ | `systemr cron` | Scheduled tasks (add/list/remove/run) |
89
+ | `systemr doctor` | Health check and diagnostics |
90
+ | `systemr setup` | Profile and rules wizard |
91
+
92
+ ### Auth
93
+ | Command | Description |
94
+ |---------|-------------|
95
+ | `systemr login` | Connect to System R |
96
+ | `systemr logout` | Disconnect |
97
+ | `systemr whoami` | Show current session |
98
+
99
+ ## Chat Commands
100
+
101
+ Inside `systemr chat`, use slash commands:
102
+
103
+ | Command | Action |
104
+ |---------|--------|
105
+ | `/morning` | Morning briefing (4 parallel agents) |
106
+ | `/eod` | End of day review + journal |
107
+ | `/plan` | Plan today's trades |
108
+ | `/portfolio` | Show open positions |
109
+ | `/risk` | Risk dashboard |
110
+ | `/memory <query>` | Search past memories |
111
+ | `/sessions` | List recent sessions |
112
+ | `/cron` | Manage scheduled tasks |
113
+ | `/permissions` | View/switch safety profile |
114
+ | `/remember <text>` | Save a memory |
115
+ | `/model <name>` | Switch LLM model |
116
+ | `/credits` | Session credit usage |
117
+ | `/help` | All commands |
118
+
119
+ ## Safety
120
+
121
+ Three-tier confirmation protocol:
122
+
123
+ - **AUTO** — Read-only actions (quotes, analysis, calculations)
124
+ - **CONFIRM** — Trade actions (place/cancel/modify order) — show details, y/n
125
+ - **DOUBLE_CONFIRM** — Destructive actions (kill switch) — type "KILL"
126
+
127
+ Permission profiles: `paper` (max safety), `standard` (default), `experienced` (relaxed stops).
128
+
129
+ ## Architecture
130
+
131
+ ```
132
+ ~/.systemr/
133
+ PROFILE.md # Trader identity, risk params
134
+ RULES.md # Hard/soft rules + standing orders
135
+ auth.json # API credentials (chmod 600)
136
+ config.json # Settings, model config
137
+ journal.db # SQLite trade journal
138
+ memory/
139
+ MEMORY.md # Index
140
+ YYYY-MM-DD.md # Daily trading logs
141
+ *.md # Lessons, notes, violations
142
+ sessions/
143
+ last_session.json # Resume state
144
+ cron/
145
+ jobs.json # Scheduled tasks
146
+ runs/ # Run history (JSONL)
147
+ ```
148
+
149
+ All data stays on your machine. The backend (`agents.systemr.ai`) processes requests — your profile, rules, and journal never leave your disk.
150
+
151
+ ## Backend
152
+
153
+ System R CLI connects to `agents.systemr.ai`:
154
+
155
+ - 55 MCP tools (sizing, risk, intelligence, planning, behavioral)
156
+ - 25 broker/exchange adapters
157
+ - 187 domain services
158
+ - Per-agent encryption (AES-128-CBC)
159
+ - Multi-model LLM via AWS Bedrock (Claude, GPT, Nova)
160
+ - Compute credit billing (USDC, SOL, OSR)
161
+
162
+ ## Development
163
+
164
+ ```bash
165
+ git clone https://github.com/System-R-AI/systemr-neo.git
166
+ cd systemr-neo
167
+ pip install -e ".[dev]"
168
+ pytest
169
+ ```
170
+
171
+ ### Rules
172
+
173
+ - Decimal for all money — never float
174
+ - structlog only — zero `print()`
175
+ - Additive-only — never remove/rename
176
+ - Tests for every module
177
+
178
+ ### Stats
179
+
180
+ - 33 source files, ~8,800 lines
181
+ - 206 tests passing
182
+ - Zero bare `print()` statements
183
+ - Python 3.11+
184
+
185
+ ## Links
186
+
187
+ - [systemr.ai](https://systemr.ai) — Documentation
188
+ - [agents.systemr.ai](https://agents.systemr.ai) — API
189
+ - [app.systemr.ai](https://app.systemr.ai) — Portal
190
+
191
+ Built by [Ashim Nandi](https://ashimnandi.com) and Shannon at [System R AI](https://systemr.ai).
@@ -0,0 +1,37 @@
1
+ neo/__init__.py,sha256=tfzk1-_akK5JhODp5y_JU5oWNX0sBADJckoZy1aDJi8,89
2
+ neo/__main__.py,sha256=jgcHytKsLPyX3dO6FyuxJqV_NunRjsR0TbmURtwyEJs,72
3
+ neo/auth.py,sha256=hyfCHiusymLl_ZT-x1hdPLp-BWlIe0PjoxPSEBZRmcc,6056
4
+ neo/cli.py,sha256=c14ISxc9RJWolO-pAsImhKpvX8OIBo4_oqjEx9pdt_o,6379
5
+ neo/client.py,sha256=OcZgXBynuoaUU8B2MLaoPl_t0DgF1TquRDkC1r3_jzI,2102
6
+ neo/config.py,sha256=NBwWUg48JHhLqIOONpJOxwKI279TINSH-cVh4f3WJc0,2137
7
+ neo/confirmation.py,sha256=2elJIZu9pUTyabnrHywolFmXidMa78BNJDjWf2Y4vYw,10628
8
+ neo/credits.py,sha256=VINRtrMDvQLwnFofNarGfVuKkMrvIbfGW7fcbGcYRm0,3211
9
+ neo/cron.py,sha256=RRbvAhY2rng2RyuW5Qfqz9s63Pompq6VbxJIbLqyt2E,9806
10
+ neo/hooks.py,sha256=ifeyuwCrQhlu4mDodymu5fFEK9Cwd9LYEKX2IQOvwBQ,4903
11
+ neo/logging.py,sha256=vU_kWAZs6BQg8QfepRfau0Yeu8AALUdU-oOwH_P8nMs,1707
12
+ neo/model_failover.py,sha256=oxxG0xfoY_5s5plrd0fX9itfvk-avxuWYApPH_yZH1I,5957
13
+ neo/orchestrator.py,sha256=WmOkMtOS3Bqr1nChsPuVQ6KcfF9BZFRcr-n2FKq9JuM,9718
14
+ neo/profile.py,sha256=L9vSsELJwPmqEu3sNoBa3ALZbm9fVRtYlXf49c_nlrA,14985
15
+ neo/store.py,sha256=Ws8ZOa0A9QbKQFI87afgEvxcTkcXjv9FDAyV6W4H_1w,13189
16
+ neo/streaming.py,sha256=ot2jkfq0fj4KPpjkJbQCijCQFFZWIPh_tMWXyaszfVg,11009
17
+ neo/types.py,sha256=9hVIylz_yl9r8dnWg2OmXCpfZiggIR2evi6E067EQ7o,2922
18
+ neo/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ neo/commands/auth_commands.py,sha256=FxhLmYtMvQ8jrMu6VZh7IWYcArJ3pkJYCpDCDbX8ObA,11455
20
+ neo/commands/chat_commands.py,sha256=E0kSUDRoF3pT6qo5vPSy_Zqi-BpBZ_oDA3kf1zVImr0,35003
21
+ neo/commands/cron_commands.py,sha256=RhXRaj-MhYrZierQAzQ3Iv67x4cj1V_RVQwgb8FdYfg,5682
22
+ neo/commands/doctor_command.py,sha256=Ammu2R55fdLrtNMgrnE8W2eR4J2R8lY2YkJHsiIeSk4,6088
23
+ neo/commands/eval_commands.py,sha256=CPyy910ZEl3_eOLeGqesWtnuuwnWiatdilrPbpdhToQ,2385
24
+ neo/commands/journal_commands.py,sha256=rKowVMlkfWT3hU9gQshfnDcyeSikvSecyd5wB-CN7NA,6695
25
+ neo/commands/plan_commands.py,sha256=Qp7bCGnu_1Ti1M5TG6s2SAEy9sSHrXV0Q_TRqNcXV4g,2897
26
+ neo/commands/risk_commands.py,sha256=aU6bNHBbVaDreXAtLOYXBEWgHRIJrucAQuuxmaUJNNE,2416
27
+ neo/commands/scan_commands.py,sha256=nvwT_FpknmgZsnzmL_OcHYDuKaNmycP8Pd-w5EzDiVo,2014
28
+ neo/commands/size_commands.py,sha256=wbhWkDDwEkXvPTTRq-nYTdEwAI7XUQsPDM5N5y3a118,2268
29
+ neo/display/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ neo/display/chat_renderer.py,sha256=FlDDZIingiowYPDmaWkGoBOEV6KZNUGVhmIX7qfTa2k,3681
31
+ neo/display/formatters.py,sha256=lPUsYHVDORa-q0HwfuwOx83npQfPojO1ACsU3lDpIFo,2808
32
+ neo/display/tables.py,sha256=lPFYIeUnJSCNGeuOR3f2GFcEUOg0fKiSf4meOk2p18Q,1625
33
+ neo/display/theme.py,sha256=gBCXAAMZwYQV-Y-hClCsFcSwpVUEV3NQaWSxZOcC8k8,4941
34
+ systemr_cli-1.0.0.dist-info/METADATA,sha256=85BFL54avhpV4oWhX9Ejsm0YHBYJXEPFKXc1OvLeOec,5338
35
+ systemr_cli-1.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
36
+ systemr_cli-1.0.0.dist-info/entry_points.txt,sha256=KBhZic5uYphEuxbD5KCmJU2f770dxHaCFxPK_HGwFkA,58
37
+ systemr_cli-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ neo = neo.cli:cli
3
+ systemr = neo.cli:cli