Skip to main content

schwab_cli/
capabilities.rs

1use serde_json::{json, Value};
2
3#[derive(Debug, Clone, serde::Serialize)]
4pub struct CommandSpec {
5    pub path: &'static str,
6    pub description: &'static str,
7    pub http: Option<&'static str>,
8    pub mutation: bool,
9    pub requires_auth: bool,
10}
11
12pub fn all_commands() -> Vec<CommandSpec> {
13    vec![
14        CommandSpec {
15            path: "capabilities",
16            description: "Machine-readable command catalog",
17            http: None,
18            mutation: false,
19            requires_auth: false,
20        },
21        CommandSpec {
22            path: "env schema",
23            description: "Environment variable schema",
24            http: None,
25            mutation: false,
26            requires_auth: false,
27        },
28        CommandSpec {
29            path: "instructions",
30            description: "Agent system prompt and tool-use guidance",
31            http: None,
32            mutation: false,
33            requires_auth: false,
34        },
35        CommandSpec {
36            path: "disclaimer show",
37            description: "Print trading risk disclaimer (experimental software)",
38            http: None,
39            mutation: false,
40            requires_auth: false,
41        },
42        CommandSpec {
43            path: "disclaimer accept",
44            description: "Accept risk disclaimer (required before live trading)",
45            http: None,
46            mutation: true,
47            requires_auth: false,
48        },
49        CommandSpec {
50            path: "disclaimer status",
51            description: "Whether disclaimer was accepted on this machine",
52            http: None,
53            mutation: false,
54            requires_auth: false,
55        },
56        CommandSpec {
57            path: "auth login",
58            description: "OAuth authorization code flow",
59            http: None,
60            mutation: true,
61            requires_auth: false,
62        },
63        CommandSpec {
64            path: "auth status",
65            description: "Inspect stored OAuth tokens",
66            http: None,
67            mutation: false,
68            requires_auth: false,
69        },
70        CommandSpec {
71            path: "auth refresh",
72            description: "Refresh OAuth access token",
73            http: None,
74            mutation: true,
75            requires_auth: false,
76        },
77        CommandSpec {
78            path: "auth logout",
79            description: "Delete stored OAuth tokens",
80            http: None,
81            mutation: true,
82            requires_auth: false,
83        },
84        CommandSpec {
85            path: "accounts numbers",
86            description: "List account numbers and encrypted hash values",
87            http: Some("GET /accounts/accountNumbers"),
88            mutation: false,
89            requires_auth: true,
90        },
91        CommandSpec {
92            path: "accounts list",
93            description: "List linked accounts with balances and positions",
94            http: Some("GET /accounts"),
95            mutation: false,
96            requires_auth: true,
97        },
98        CommandSpec {
99            path: "accounts get",
100            description: "Get one account by account number hash",
101            http: Some("GET /accounts/{accountNumber}"),
102            mutation: false,
103            requires_auth: true,
104        },
105        CommandSpec {
106            path: "portfolio summary",
107            description: "Aggregate portfolio equity and holdings across accounts",
108            http: Some("GET /accounts"),
109            mutation: false,
110            requires_auth: true,
111        },
112        CommandSpec {
113            path: "portfolio buying-power",
114            description: "Cash available for trading on one account (pre-buy check)",
115            http: Some("GET /accounts/{accountNumber}"),
116            mutation: false,
117            requires_auth: true,
118        },
119        CommandSpec {
120            path: "trade buy",
121            description: "Buy equity shares with safety guardrails",
122            http: Some("POST /accounts/{accountNumber}/orders"),
123            mutation: true,
124            requires_auth: true,
125        },
126        CommandSpec {
127            path: "trade sell",
128            description: "Sell equity shares with safety guardrails",
129            http: Some("POST /accounts/{accountNumber}/orders"),
130            mutation: true,
131            requires_auth: true,
132        },
133        CommandSpec {
134            path: "safety show",
135            description: "Show active safety.json limits and path",
136            http: None,
137            mutation: false,
138            requires_auth: false,
139        },
140        CommandSpec {
141            path: "safety init",
142            description: "Create default safety.json in config directory",
143            http: None,
144            mutation: true,
145            requires_auth: false,
146        },
147        CommandSpec {
148            path: "safety path",
149            description: "Print safety.json path",
150            http: None,
151            mutation: false,
152            requires_auth: false,
153        },
154        CommandSpec {
155            path: "plan schema",
156            description: "JSON Schema for trade plan YAML/JSON files",
157            http: None,
158            mutation: false,
159            requires_auth: false,
160        },
161        CommandSpec {
162            path: "plan prompt",
163            description: "LLM workflow and template for generating trade plans",
164            http: None,
165            mutation: false,
166            requires_auth: false,
167        },
168        CommandSpec {
169            path: "plan validate",
170            description: "Validate trade plan structure and safety limits",
171            http: None,
172            mutation: false,
173            requires_auth: true,
174        },
175        CommandSpec {
176            path: "plan show",
177            description: "Display parsed trade plan",
178            http: None,
179            mutation: false,
180            requires_auth: false,
181        },
182        CommandSpec {
183            path: "plan run",
184            description: "Execute trade plan steps sequentially",
185            http: Some("POST /accounts/{accountNumber}/orders"),
186            mutation: true,
187            requires_auth: true,
188        },
189        CommandSpec {
190            path: "orders schema",
191            description: "OrderRequest JSON schema and Schwab order examples",
192            http: None,
193            mutation: false,
194            requires_auth: false,
195        },
196        CommandSpec {
197            path: "orders validate",
198            description: "Validate order JSON shape and safety.json limits",
199            http: None,
200            mutation: false,
201            requires_auth: false,
202        },
203        CommandSpec {
204            path: "orders list",
205            description: "List orders for an account",
206            http: Some("GET /accounts/{accountNumber}/orders"),
207            mutation: false,
208            requires_auth: true,
209        },
210        CommandSpec {
211            path: "orders all",
212            description: "List orders across all accounts",
213            http: Some("GET /orders"),
214            mutation: false,
215            requires_auth: true,
216        },
217        CommandSpec {
218            path: "orders get",
219            description: "Get order by ID",
220            http: Some("GET /accounts/{accountNumber}/orders/{orderId}"),
221            mutation: false,
222            requires_auth: true,
223        },
224        CommandSpec {
225            path: "orders wait",
226            description: "Poll order status until filled or timeout",
227            http: Some("GET /accounts/{accountNumber}/orders/{orderId}"),
228            mutation: false,
229            requires_auth: true,
230        },
231        CommandSpec {
232            path: "orders place",
233            description: "Place a new order",
234            http: Some("POST /accounts/{accountNumber}/orders"),
235            mutation: true,
236            requires_auth: true,
237        },
238        CommandSpec {
239            path: "orders preview",
240            description: "Preview order validation and fees",
241            http: Some("POST /accounts/{accountNumber}/previewOrder"),
242            mutation: false,
243            requires_auth: true,
244        },
245        CommandSpec {
246            path: "orders cancel",
247            description: "Cancel an open order",
248            http: Some("DELETE /accounts/{accountNumber}/orders/{orderId}"),
249            mutation: true,
250            requires_auth: true,
251        },
252        CommandSpec {
253            path: "orders replace",
254            description: "Replace an existing order",
255            http: Some("PUT /accounts/{accountNumber}/orders/{orderId}"),
256            mutation: true,
257            requires_auth: true,
258        },
259        CommandSpec {
260            path: "transactions list",
261            description: "List transactions for an account",
262            http: Some("GET /accounts/{accountNumber}/transactions"),
263            mutation: false,
264            requires_auth: true,
265        },
266        CommandSpec {
267            path: "transactions get",
268            description: "Get transaction by ID",
269            http: Some("GET /accounts/{accountNumber}/transactions/{transactionId}"),
270            mutation: false,
271            requires_auth: true,
272        },
273        CommandSpec {
274            path: "user preference",
275            description: "User preferences and streamer info",
276            http: Some("GET /userPreference"),
277            mutation: false,
278            requires_auth: true,
279        },
280        CommandSpec {
281            path: "market info",
282            description: "Agent dossier: quote + fundamentals + price context + research hints",
283            http: Some("GET /marketdata/v1/{symbol}/quotes + /instruments + /pricehistory"),
284            mutation: false,
285            requires_auth: true,
286        },
287        CommandSpec {
288            path: "market quotes",
289            description: "Get quotes for multiple symbols",
290            http: Some("GET /marketdata/v1/quotes"),
291            mutation: false,
292            requires_auth: true,
293        },
294        CommandSpec {
295            path: "market quote",
296            description: "Get quote for a single symbol",
297            http: Some("GET /marketdata/v1/{symbol}/quotes"),
298            mutation: false,
299            requires_auth: true,
300        },
301        CommandSpec {
302            path: "market history",
303            description: "Price history (OHLCV candles)",
304            http: Some("GET /marketdata/v1/pricehistory"),
305            mutation: false,
306            requires_auth: true,
307        },
308        CommandSpec {
309            path: "market instrument",
310            description: "Instrument search and fundamentals (company info)",
311            http: Some("GET /marketdata/v1/instruments"),
312            mutation: false,
313            requires_auth: true,
314        },
315        CommandSpec {
316            path: "market instrument-by-cusip",
317            description: "Instrument lookup by CUSIP",
318            http: Some("GET /marketdata/v1/instruments/{cusip}"),
319            mutation: false,
320            requires_auth: true,
321        },
322        CommandSpec {
323            path: "market hours",
324            description: "Market hours for one or more markets",
325            http: Some("GET /marketdata/v1/markets"),
326            mutation: false,
327            requires_auth: true,
328        },
329        CommandSpec {
330            path: "market hours-for",
331            description: "Market hours for a single market",
332            http: Some("GET /marketdata/v1/markets/{market}"),
333            mutation: false,
334            requires_auth: true,
335        },
336        CommandSpec {
337            path: "options chain",
338            description: "Option chain for an underlying symbol",
339            http: Some("GET /marketdata/v1/chains"),
340            mutation: false,
341            requires_auth: true,
342        },
343        CommandSpec {
344            path: "options positions",
345            description: "List option positions grouped into spreads",
346            http: Some("GET /accounts"),
347            mutation: false,
348            requires_auth: true,
349        },
350        CommandSpec {
351            path: "options schema",
352            description: "Strategy templates and symbology for options orders",
353            http: None,
354            mutation: false,
355            requires_auth: false,
356        },
357        CommandSpec {
358            path: "options validate",
359            description: "Validate strategy params against safety.json",
360            http: None,
361            mutation: false,
362            requires_auth: false,
363        },
364        CommandSpec {
365            path: "options preview",
366            description: "Preview vertical or iron condor order",
367            http: Some("POST /accounts/{accountNumber}/orders"),
368            mutation: false,
369            requires_auth: true,
370        },
371        CommandSpec {
372            path: "options open",
373            description: "Open vertical spread or iron condor",
374            http: Some("POST /accounts/{accountNumber}/orders"),
375            mutation: true,
376            requires_auth: true,
377        },
378        CommandSpec {
379            path: "options close",
380            description: "Close an option spread by position group id",
381            http: Some("POST /accounts/{accountNumber}/orders"),
382            mutation: true,
383            requires_auth: true,
384        },
385        CommandSpec {
386            path: "agent schema",
387            description: "JSON Schema for rules.yaml",
388            http: None,
389            mutation: false,
390            requires_auth: false,
391        },
392        CommandSpec {
393            path: "agent validate",
394            description: "Validate rules.yaml structure",
395            http: None,
396            mutation: false,
397            requires_auth: false,
398        },
399        CommandSpec {
400            path: "agent status",
401            description: "Show persisted agent state",
402            http: None,
403            mutation: false,
404            requires_auth: false,
405        },
406        CommandSpec {
407            path: "agent run",
408            description: "Run options agent loop from rules.yaml",
409            http: None,
410            mutation: true,
411            requires_auth: true,
412        },
413        CommandSpec {
414            path: "agent stop",
415            description: "Stop background options agent daemon",
416            http: None,
417            mutation: true,
418            requires_auth: false,
419        },
420        CommandSpec {
421            path: "agent close-all",
422            description: "Close all option spreads tracked in agent state (not whole account)",
423            http: Some("POST /accounts/{accountNumber}/orders"),
424            mutation: true,
425            requires_auth: true,
426        },
427        CommandSpec {
428            path: "dashboard",
429            description: "Rich terminal dashboard (agent, rules, positions)",
430            http: None,
431            mutation: false,
432            requires_auth: false,
433        },
434        CommandSpec {
435            path: "watch",
436            description: "Run agent in-process with live TUI (or attach to existing daemon)",
437            http: None,
438            mutation: false,
439            requires_auth: false,
440        },
441        CommandSpec {
442            path: "rules show",
443            description: "Human-readable rules configuration breakdown",
444            http: None,
445            mutation: false,
446            requires_auth: false,
447        },
448        CommandSpec {
449            path: "rules list",
450            description: "List discoverable rules/*.yaml files",
451            http: None,
452            mutation: false,
453            requires_auth: false,
454        },
455    ]
456}
457
458pub fn command_tree() -> Value {
459    json!([
460        { "group": "meta", "commands": ["capabilities", "env schema", "instructions"] },
461        { "group": "auth", "commands": ["login", "status", "refresh", "logout"] },
462        { "group": "accounts", "commands": ["numbers", "list", "get"] },
463        { "group": "portfolio", "commands": ["summary", "buying-power"] },
464        { "group": "trade", "commands": ["buy", "sell"] },
465        { "group": "safety", "commands": ["show", "init", "path"] },
466        { "group": "plan", "commands": ["schema", "prompt", "validate", "show", "run"] },
467        { "group": "market", "commands": ["info", "quotes", "quote", "history", "instrument", "instrument-by-cusip", "hours", "hours-for"] },
468        { "group": "options", "commands": ["chain", "positions", "schema", "validate", "preview", "open", "close"] },
469        { "group": "agent", "commands": ["schema", "validate", "status", "run", "stop", "close-all"] },
470        { "group": "dashboard", "commands": ["dashboard"] },
471        { "group": "watch", "commands": ["watch"] },
472        { "group": "rules", "commands": ["show", "list"] },
473        { "group": "orders", "commands": ["schema", "validate", "list", "all", "get", "wait", "place", "preview", "cancel", "replace"] },
474        { "group": "transactions", "commands": ["list", "get"] },
475        { "group": "user", "commands": ["preference"] }
476    ])
477}
478
479pub fn capabilities_json() -> Value {
480    let commands: Vec<Value> = all_commands()
481        .into_iter()
482        .map(|c| {
483            json!({
484                "path": c.path,
485                "description": c.description,
486                "http": c.http,
487                "mutation": c.mutation,
488                "requires_auth": c.requires_auth,
489            })
490        })
491        .collect();
492
493    json!({
494        "cli": "schwab",
495        "api_product": "Trader API - Individual (Accounts and Trading Production)",
496        "market_data_base_url": schwab_api::MARKET_DATA_BASE_URL,
497        "base_url": schwab_api::TRADER_BASE_URL,
498        "default_mode": "agent",
499        "output_formats": ["pretty", "json", "md"],
500        "mutation_policy": "Auth mutations require --yes in non-interactive mode",
501        "trading_policy": "Trading mutations require --trust --yes in agent mode; safety.json hard limits always enforced",
502        "global_flags": ["--yes", "--trust", "--dry-run", "--json"],
503        "commands": commands,
504    })
505}