use serde_json::{json, Value};
#[derive(Debug, Clone, serde::Serialize)]
pub struct CommandSpec {
pub path: &'static str,
pub description: &'static str,
pub http: Option<&'static str>,
pub mutation: bool,
pub requires_auth: bool,
}
pub fn all_commands() -> Vec<CommandSpec> {
vec![
CommandSpec {
path: "capabilities",
description: "Machine-readable command catalog",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "env schema",
description: "Environment variable schema",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "instructions",
description: "Agent system prompt and tool-use guidance",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "disclaimer show",
description: "Print trading risk disclaimer (experimental software)",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "disclaimer accept",
description: "Accept risk disclaimer (required before live trading)",
http: None,
mutation: true,
requires_auth: false,
},
CommandSpec {
path: "disclaimer status",
description: "Whether disclaimer was accepted on this machine",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "auth login",
description: "OAuth authorization code flow",
http: None,
mutation: true,
requires_auth: false,
},
CommandSpec {
path: "auth status",
description: "Inspect stored OAuth tokens",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "auth refresh",
description: "Refresh OAuth access token",
http: None,
mutation: true,
requires_auth: false,
},
CommandSpec {
path: "auth logout",
description: "Delete stored OAuth tokens",
http: None,
mutation: true,
requires_auth: false,
},
CommandSpec {
path: "accounts numbers",
description: "List account numbers and encrypted hash values",
http: Some("GET /accounts/accountNumbers"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "accounts list",
description: "List linked accounts with balances and positions",
http: Some("GET /accounts"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "accounts get",
description: "Get one account by account number hash",
http: Some("GET /accounts/{accountNumber}"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "portfolio summary",
description: "Aggregate portfolio equity and holdings across accounts",
http: Some("GET /accounts"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "portfolio buying-power",
description: "Cash available for trading on one account (pre-buy check)",
http: Some("GET /accounts/{accountNumber}"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "trade buy",
description: "Buy equity shares with safety guardrails",
http: Some("POST /accounts/{accountNumber}/orders"),
mutation: true,
requires_auth: true,
},
CommandSpec {
path: "trade sell",
description: "Sell equity shares with safety guardrails",
http: Some("POST /accounts/{accountNumber}/orders"),
mutation: true,
requires_auth: true,
},
CommandSpec {
path: "safety show",
description: "Show active safety.json limits and path",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "safety init",
description: "Create default safety.json in config directory",
http: None,
mutation: true,
requires_auth: false,
},
CommandSpec {
path: "safety path",
description: "Print safety.json path",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "plan schema",
description: "JSON Schema for trade plan YAML/JSON files",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "plan prompt",
description: "LLM workflow and template for generating trade plans",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "plan validate",
description: "Validate trade plan structure and safety limits",
http: None,
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "plan show",
description: "Display parsed trade plan",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "plan run",
description: "Execute trade plan steps sequentially",
http: Some("POST /accounts/{accountNumber}/orders"),
mutation: true,
requires_auth: true,
},
CommandSpec {
path: "orders schema",
description: "OrderRequest JSON schema and Schwab order examples",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "orders validate",
description: "Validate order JSON shape and safety.json limits",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "orders list",
description: "List orders for an account",
http: Some("GET /accounts/{accountNumber}/orders"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "orders all",
description: "List orders across all accounts",
http: Some("GET /orders"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "orders get",
description: "Get order by ID",
http: Some("GET /accounts/{accountNumber}/orders/{orderId}"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "orders wait",
description: "Poll order status until filled or timeout",
http: Some("GET /accounts/{accountNumber}/orders/{orderId}"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "orders place",
description: "Place a new order",
http: Some("POST /accounts/{accountNumber}/orders"),
mutation: true,
requires_auth: true,
},
CommandSpec {
path: "orders preview",
description: "Preview order validation and fees",
http: Some("POST /accounts/{accountNumber}/previewOrder"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "orders cancel",
description: "Cancel an open order",
http: Some("DELETE /accounts/{accountNumber}/orders/{orderId}"),
mutation: true,
requires_auth: true,
},
CommandSpec {
path: "orders replace",
description: "Replace an existing order",
http: Some("PUT /accounts/{accountNumber}/orders/{orderId}"),
mutation: true,
requires_auth: true,
},
CommandSpec {
path: "transactions list",
description: "List transactions for an account",
http: Some("GET /accounts/{accountNumber}/transactions"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "transactions get",
description: "Get transaction by ID",
http: Some("GET /accounts/{accountNumber}/transactions/{transactionId}"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "user preference",
description: "User preferences and streamer info",
http: Some("GET /userPreference"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "market info",
description: "Agent dossier: quote + fundamentals + price context + research hints",
http: Some("GET /marketdata/v1/{symbol}/quotes + /instruments + /pricehistory"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "market quotes",
description: "Get quotes for multiple symbols",
http: Some("GET /marketdata/v1/quotes"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "market quote",
description: "Get quote for a single symbol",
http: Some("GET /marketdata/v1/{symbol}/quotes"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "market history",
description: "Price history (OHLCV candles)",
http: Some("GET /marketdata/v1/pricehistory"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "market instrument",
description: "Instrument search and fundamentals (company info)",
http: Some("GET /marketdata/v1/instruments"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "market instrument-by-cusip",
description: "Instrument lookup by CUSIP",
http: Some("GET /marketdata/v1/instruments/{cusip}"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "market hours",
description: "Market hours for one or more markets",
http: Some("GET /marketdata/v1/markets"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "market hours-for",
description: "Market hours for a single market",
http: Some("GET /marketdata/v1/markets/{market}"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "options chain",
description: "Option chain for an underlying symbol",
http: Some("GET /marketdata/v1/chains"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "options positions",
description: "List option positions grouped into spreads",
http: Some("GET /accounts"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "options schema",
description: "Strategy templates and symbology for options orders",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "options validate",
description: "Validate strategy params against safety.json",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "options preview",
description: "Preview vertical or iron condor order",
http: Some("POST /accounts/{accountNumber}/orders"),
mutation: false,
requires_auth: true,
},
CommandSpec {
path: "options open",
description: "Open vertical spread or iron condor",
http: Some("POST /accounts/{accountNumber}/orders"),
mutation: true,
requires_auth: true,
},
CommandSpec {
path: "options close",
description: "Close an option spread by position group id",
http: Some("POST /accounts/{accountNumber}/orders"),
mutation: true,
requires_auth: true,
},
CommandSpec {
path: "agent schema",
description: "JSON Schema for rules.yaml",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "agent validate",
description: "Validate rules.yaml structure",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "agent status",
description: "Show persisted agent state",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "agent run",
description: "Run options agent loop from rules.yaml",
http: None,
mutation: true,
requires_auth: true,
},
CommandSpec {
path: "agent stop",
description: "Stop background options agent daemon",
http: None,
mutation: true,
requires_auth: false,
},
CommandSpec {
path: "agent close-all",
description: "Close all option spreads tracked in agent state (not whole account)",
http: Some("POST /accounts/{accountNumber}/orders"),
mutation: true,
requires_auth: true,
},
CommandSpec {
path: "dashboard",
description: "Rich terminal dashboard (agent, rules, positions)",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "watch",
description: "Run agent in-process with live TUI (or attach to existing daemon)",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "rules show",
description: "Human-readable rules configuration breakdown",
http: None,
mutation: false,
requires_auth: false,
},
CommandSpec {
path: "rules list",
description: "List discoverable rules/*.yaml files",
http: None,
mutation: false,
requires_auth: false,
},
]
}
pub fn command_tree() -> Value {
json!([
{ "group": "meta", "commands": ["capabilities", "env schema", "instructions"] },
{ "group": "auth", "commands": ["login", "status", "refresh", "logout"] },
{ "group": "accounts", "commands": ["numbers", "list", "get"] },
{ "group": "portfolio", "commands": ["summary", "buying-power"] },
{ "group": "trade", "commands": ["buy", "sell"] },
{ "group": "safety", "commands": ["show", "init", "path"] },
{ "group": "plan", "commands": ["schema", "prompt", "validate", "show", "run"] },
{ "group": "market", "commands": ["info", "quotes", "quote", "history", "instrument", "instrument-by-cusip", "hours", "hours-for"] },
{ "group": "options", "commands": ["chain", "positions", "schema", "validate", "preview", "open", "close"] },
{ "group": "agent", "commands": ["schema", "validate", "status", "run", "stop", "close-all"] },
{ "group": "dashboard", "commands": ["dashboard"] },
{ "group": "watch", "commands": ["watch"] },
{ "group": "rules", "commands": ["show", "list"] },
{ "group": "orders", "commands": ["schema", "validate", "list", "all", "get", "wait", "place", "preview", "cancel", "replace"] },
{ "group": "transactions", "commands": ["list", "get"] },
{ "group": "user", "commands": ["preference"] }
])
}
pub fn capabilities_json() -> Value {
let commands: Vec<Value> = all_commands()
.into_iter()
.map(|c| {
json!({
"path": c.path,
"description": c.description,
"http": c.http,
"mutation": c.mutation,
"requires_auth": c.requires_auth,
})
})
.collect();
json!({
"cli": "schwab",
"api_product": "Trader API - Individual (Accounts and Trading Production)",
"market_data_base_url": schwab_api::MARKET_DATA_BASE_URL,
"base_url": schwab_api::TRADER_BASE_URL,
"default_mode": "agent",
"output_formats": ["pretty", "json", "md"],
"mutation_policy": "Auth mutations require --yes in non-interactive mode",
"trading_policy": "Trading mutations require --trust --yes in agent mode; safety.json hard limits always enforced",
"global_flags": ["--yes", "--trust", "--dry-run", "--json"],
"commands": commands,
})
}