nansen-cli 1.6.0 → 1.7.0

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.
package/AGENTS.md ADDED
@@ -0,0 +1,222 @@
1
+ # AGENTS.md — Agent Quick Start
2
+
3
+ > **This file is for AI agents (OpenClaw, Claude Code, Cursor, etc.) that need to query Nansen data on behalf of their human.** If you're a human, see [README.md](README.md).
4
+
5
+ ## CLI vs REST API
6
+
7
+ **Prefer the [REST API](https://docs.nansen.ai) for most agent use cases** — no install step, no dependency issues.
8
+
9
+ ```bash
10
+ # Direct API call — no CLI needed
11
+ curl -s -X POST https://api.nansen.ai/api/v1/token-screener \
12
+ -H "apiKey: YOUR_KEY" \
13
+ -H "Content-Type: application/json" \
14
+ -d '{"chain":"solana","pagination":{"page":1,"per_page":5}}' | jq .
15
+ ```
16
+
17
+ Use this CLI guide if the CLI is already installed or your human specifically wants CLI usage. The CLI adds `--pretty`, `--table`, `--fields`, `--stream`, built-in retries, and schema introspection.
18
+
19
+ ## Install & Auth in One Shot
20
+
21
+ ```bash
22
+ # Install
23
+ npm install -g nansen-cli
24
+
25
+ # Auth — pick ONE method:
26
+ # Method 1: Interactive (human needs to paste key)
27
+ nansen login
28
+
29
+ # Method 2: Environment variable (no interaction needed)
30
+ export NANSEN_API_KEY=<key>
31
+
32
+ # Method 3: Config file (write directly — no validation call burned)
33
+ mkdir -p ~/.nansen && echo '{"apiKey":"<key>","baseUrl":"https://api.nansen.ai"}' > ~/.nansen/config.json && chmod 600 ~/.nansen/config.json
34
+ ```
35
+
36
+ **Get an API key:** [app.nansen.ai/api](https://app.nansen.ai/api)
37
+
38
+ ### Auth Priority
39
+
40
+ 1. `NANSEN_API_KEY` env var (highest)
41
+ 2. `~/.nansen/config.json` file
42
+ 3. Prompt (interactive only)
43
+
44
+ ### Common Auth Pitfall
45
+
46
+ `nansen login` validates your key by making a real API call (burns 1 credit). If you already know the key is valid, writing `~/.nansen/config.json` directly is cheaper.
47
+
48
+ ## Verify It Works
49
+
50
+ ```bash
51
+ # This is cheap and fast:
52
+ nansen schema | head -1
53
+ # Expected: {"version":"1.x.x","commands":{...}}
54
+
55
+ # This burns a credit but proves API access:
56
+ nansen token screener --chain solana --limit 1
57
+ ```
58
+
59
+ ## Agent-Optimized Patterns
60
+
61
+ ### 1. Always Use `--fields` to Reduce Token Burn
62
+
63
+ ```bash
64
+ # ❌ Returns everything (huge JSON, wastes agent context)
65
+ nansen smart-money netflow --chain solana
66
+
67
+ # ✅ Only what you need
68
+ nansen smart-money netflow --chain solana --fields token_symbol,net_flow_usd,chain --limit 10
69
+ ```
70
+
71
+ ### 2. Use Schema for Self-Discovery
72
+
73
+ ```bash
74
+ # Don't guess commands — introspect:
75
+ nansen schema --pretty # All commands
76
+ nansen schema smart-money --pretty # One command's options & return fields
77
+ ```
78
+
79
+ ### 3. Parse Errors Programmatically
80
+
81
+ Every response has `success: true/false`. Errors include `code` for routing:
82
+
83
+ | Code | What To Do |
84
+ |------|------------|
85
+ | `CREDITS_EXHAUSTED` | **Stop all calls.** Tell the human. |
86
+ | `RATE_LIMITED` | Wait. Auto-retry handles this. |
87
+ | `UNSUPPORTED_FILTER` | Remove the filter, retry without it. |
88
+ | `UNAUTHORIZED` | Key is wrong. Re-auth. |
89
+ | `INVALID_ADDRESS` | Check address format for the chain. |
90
+
91
+ ### 4. Budget Credits
92
+
93
+ - Most calls cost 1 credit
94
+ - `profiler labels` + `profiler balance` are expensive — batch 3-4 at a time
95
+ - `schema`, `help`, `cache` are free (no API key needed)
96
+ - If you get `CREDITS_EXHAUSTED`, **stop immediately** — don't retry
97
+
98
+ ### 5. Use `--stream` for Large Results
99
+
100
+ ```bash
101
+ # NDJSON mode — process line by line, don't buffer giant arrays
102
+ nansen token dex-trades --chain solana --limit 100 --stream
103
+ ```
104
+
105
+ ## Pagination
106
+
107
+ The CLI exposes `--limit N` which maps to `{page: 1, per_page: N}` in the API request. **There is no `--page` flag** — the CLI always fetches page 1. To access later pages, use the REST API directly.
108
+
109
+ ```bash
110
+ # CLI: page 1 only, up to N results
111
+ nansen smart-money netflow --chain solana --limit 50
112
+
113
+ # REST API: full pagination control
114
+ curl -s -X POST https://api.nansen.ai/api/v1/smart-money/netflow \
115
+ -H "apiKey: $NANSEN_API_KEY" -H "Content-Type: application/json" \
116
+ -d '{"chains":["solana"],"pagination":{"page":2,"per_page":50}}'
117
+ ```
118
+
119
+ **Pagination key inconsistency:** Profiler endpoints (which use the beta API) take `recordsPerPage` instead of `per_page`. The CLI sends the right key automatically based on the command, but if you're calling the REST API directly, check which key each endpoint expects.
120
+
121
+ **Detecting the last page:** The raw API response does not include a `total_pages` or `has_more` field in the CLI-visible output. Reliable heuristic: if the number of results returned is less than your `--limit`, you're on the last page.
122
+
123
+ ```bash
124
+ # Request 50, get 23 back → last page
125
+ nansen token holders --token <addr> --chain solana --limit 50
126
+ # Check: .data.data.length (or .data.results.length) < 50 → done
127
+ ```
128
+
129
+ **`profiler perp-positions` has no pagination** — the API ignores the pagination parameter for this endpoint.
130
+
131
+ ## Output Parsing Gotchas
132
+
133
+ ### Response envelope
134
+
135
+ Every CLI response is wrapped in a standard envelope:
136
+
137
+ ```json
138
+ { "success": true, "data": <raw_api_response> }
139
+ ```
140
+
141
+ Errors follow a different shape:
142
+
143
+ ```json
144
+ { "success": false, "error": "message", "code": "ERROR_CODE", "status": 401, "details": {...} }
145
+ ```
146
+
147
+ ### Raw API response shapes vary by endpoint
148
+
149
+ The `data` field inside the envelope is the raw JSON from the Nansen API. Its internal structure differs across endpoints — there is no single canonical key for the results array:
150
+
151
+ | Shape | Example endpoints |
152
+ |-------|------------------|
153
+ | `data.data` (array) | token screener |
154
+ | `data.results` (array) | entity search |
155
+ | `data.data.results` (array) | most profiler endpoints |
156
+ | `data.netflows` | smart-money netflow |
157
+ | `data.trades` | smart-money dex-trades |
158
+ | `data.holdings` | smart-money holdings |
159
+ | `data.holders` | token holders |
160
+
161
+ When parsing, check for the array at each level. `--table` and `--stream` handle this automatically, but if you're parsing raw JSON with `jq`, probe the shape first:
162
+
163
+ ```bash
164
+ nansen smart-money netflow --chain solana | jq 'keys, .data | keys'
165
+ ```
166
+
167
+ ### `--fields` applies to the entire response tree
168
+
169
+ `--fields token_symbol,net_flow_usd` strips everything except those keys from the **entire** response, including the `success` and `data` wrapper fields. The result will be a bare object containing only the matched keys found anywhere in the tree.
170
+
171
+ ### Client-side vs server-side filtering
172
+
173
+ `token screener --search <term>` is **client-side**: the CLI fetches up to 500 results from the server, then filters locally. Set `--limit` higher than your expected result count when using `--search`.
174
+
175
+ ### Fields absent for some chain/token combinations
176
+
177
+ Some fields are only populated for specific chains or tokens:
178
+ - `smart_money_holders` — absent for tokens without smart money tracking
179
+ - Flow intelligence fields — may all be `0` for illiquid tokens (not an error)
180
+ - Perp endpoints (`--symbol`) only work for Hyperliquid; `--token` is for on-chain tokens
181
+
182
+ ## Chains Quick Reference
183
+
184
+ `ethereum` `solana` `base` `bnb` `arbitrum` `polygon` `optimism` `avalanche` `linea` `scroll` `mantle` `ronin` `sei` `plasma` `sonic` `monad` `hyperevm` `iotaevm`
185
+
186
+ > Run `nansen schema` to get the current chain list (source of truth).
187
+
188
+ ## Troubleshooting
189
+
190
+ ### Quick fixes
191
+
192
+ | Symptom | Fix |
193
+ |---------|-----|
194
+ | `command not found: nansen` | `npm install -g nansen-cli` or `npx nansen-cli` |
195
+ | `UNAUTHORIZED` after login | Check `cat ~/.nansen/config.json` — key may not have saved. Write it directly. |
196
+ | Login hangs or fails | Skip `nansen login`, write config directly (see Install & Auth above) |
197
+ | Huge JSON response | Use `--fields` to select only needed columns |
198
+ | Perp endpoints empty or erroring | Use `--symbol BTC` not `--token`. Perp endpoints are Hyperliquid-only. |
199
+ | JUP DCA returns error | Solana-only endpoint |
200
+ | `UNSUPPORTED_FILTER` on token holders | Not all tokens have smart money data. Remove `--smart-money` and retry. |
201
+ | `CREDITS_EXHAUSTED` | Stop all calls. Check [app.nansen.ai](https://app.nansen.ai). No retry will help. |
202
+
203
+ ### Error codes
204
+
205
+ | Code | What to do |
206
+ |------|------------|
207
+ | `CREDITS_EXHAUSTED` | **Stop all calls.** Tell the human. Check dashboard. |
208
+ | `RATE_LIMITED` | Wait — auto-retry handles this by default. |
209
+ | `UNSUPPORTED_FILTER` | Remove the filter, retry without it. |
210
+ | `UNAUTHORIZED` | Key is wrong or missing. Re-auth. |
211
+ | `INVALID_ADDRESS` | Check address format matches the chain (EVM: `0x...`, Solana: Base58). |
212
+
213
+ ### Known endpoint quirks
214
+
215
+ - **`token holders --smart-money`** — Fails with `UNSUPPORTED_FILTER` for tokens without smart money tracking (e.g., WCT on Optimism). Not all tokens have this data. Do not retry.
216
+ - **`token flow-intelligence`** — May return all-zero flows for tokens without significant smart money activity. Normal, not an error.
217
+ - **`profiler labels` and `profiler balance`** consume credits. Budget ~20 calls per session. Batch calls in groups of 3–4.
218
+ - **`profiler perp-positions`** — No pagination support; the API ignores the pagination parameter.
219
+
220
+ ## For OpenClaw / Skill Users
221
+
222
+ If you installed this as an OpenClaw skill, the `SKILL.md` file has the skill interface. This `AGENTS.md` covers the CLI directly. Both work.
package/README.md CHANGED
@@ -27,7 +27,20 @@ npm link
27
27
 
28
28
  ## Configuration
29
29
 
30
- **Option 1: Interactive login (recommended)**
30
+ ### For AI Agents (Recommended)
31
+
32
+ Use the [AI Agent Setup](https://app.nansen.ai/auth/agent-setup) flow:
33
+
34
+ 1. Your agent will ask you to visit: **[app.nansen.ai/auth/agent-setup](https://app.nansen.ai/auth/agent-setup)**
35
+ 2. Sign in with your Nansen account
36
+ 3. Copy the message shown
37
+ 4. Paste it back to your agent
38
+
39
+ Your agent saves the key and handles everything else automatically.
40
+
41
+ ### Manual Setup
42
+
43
+ **Option 1: Interactive login**
31
44
  ```bash
32
45
  nansen login
33
46
  # Enter your API key when prompted
@@ -192,6 +205,9 @@ nansen cache clear
192
205
 
193
206
  This CLI is built specifically for AI agents. Every design decision prioritizes agent usability.
194
207
 
208
+ **Getting Started:**
209
+ Direct your users to [app.nansen.ai/auth/agent-setup](https://app.nansen.ai/auth/agent-setup) for seamless authentication. See [AI Agent Access](https://docs.nansen.ai/reference/ai-agent-access) for full documentation.
210
+
195
211
  **Why agents love it:**
196
212
  - **Structured Output**: All responses are JSON with consistent schema — no parsing HTML or unstructured text
197
213
  - **Predictable Errors**: Errors include status codes and actionable details agents can handle programmatically
@@ -263,6 +279,8 @@ NANSEN_API_KEY=your-key npm run test:live
263
279
 
264
280
  See [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines.
265
281
 
282
+ **AI agents:** See [AGENTS.md](AGENTS.md) for the agent quick-start (install, auth, patterns, troubleshooting).
283
+
266
284
  **AI contributors:** See [CLAUDE.md](CLAUDE.md) for agent-specific guidance on contributing to this repo.
267
285
 
268
286
  ## API Coverage
package/SKILL.md CHANGED
@@ -12,21 +12,40 @@ compatibility: Requires Node.js 18+. Needs NANSEN_API_KEY environment variable o
12
12
 
13
13
  Command-line interface for the [Nansen API](https://docs.nansen.ai) - onchain analytics for crypto investors and AI agents.
14
14
 
15
+ ## CLI vs Direct API
16
+
17
+ If your agent has HTTP access (curl, fetch), you can skip the CLI and hit the [Nansen REST API](https://docs.nansen.ai) directly with `apiKey` header auth. The CLI is most useful for terminal-native agents (Claude Code, Codex, Cursor) that benefit from `--pretty`, `--table`, `--fields`, built-in retries, and schema introspection.
18
+
15
19
  ## Setup
16
20
 
17
21
  ```bash
18
22
  # Install globally
19
23
  npm install -g nansen-cli
20
24
 
21
- # Authenticate (interactive)
22
- nansen login
25
+ # Authenticate — pick the method that works for your context:
26
+
27
+ # Option A: Non-interactive (best for agents — no prompts, no wasted credits)
28
+ mkdir -p ~/.nansen && echo '{"apiKey":"YOUR_KEY","baseUrl":"https://api.nansen.ai"}' > ~/.nansen/config.json && chmod 600 ~/.nansen/config.json
23
29
 
24
- # Or set environment variable
30
+ # Option B: Environment variable (good for CI/scripts)
25
31
  export NANSEN_API_KEY=your-api-key
32
+
33
+ # Option C: Interactive login (burns 1 credit to validate)
34
+ nansen login
26
35
  ```
27
36
 
28
37
  Get your API key at [app.nansen.ai/api](https://app.nansen.ai/api).
29
38
 
39
+ ### Verify Installation
40
+
41
+ ```bash
42
+ # Free check (no API key needed):
43
+ nansen schema | head -1
44
+
45
+ # Full check (uses 1 credit):
46
+ nansen token screener --chain solana --limit 1
47
+ ```
48
+
30
49
  ## Commands
31
50
 
32
51
  ### Smart Money
@@ -82,7 +101,7 @@ nansen portfolio defi --wallet 0x123...
82
101
 
83
102
  ## Supported Chains
84
103
 
85
- ethereum, solana, base, bnb, arbitrum, polygon, optimism, avalanche, linea, scroll, zksync, mantle, ronin, sei, sonic, monad, hyperevm
104
+ ethereum, solana, base, bnb, arbitrum, polygon, optimism, avalanche, linea, scroll, mantle, ronin, sei, plasma, sonic, monad, hyperevm, iotaevm
86
105
 
87
106
  ## Smart Money Labels
88
107
 
@@ -96,24 +115,9 @@ nansen schema --pretty
96
115
  nansen schema smart-money --pretty
97
116
  ```
98
117
 
99
- ## Known Endpoint Issues
100
-
101
- ### Chain/Token-Specific Limitations
102
- - `token holders --smart-money` — Fails with `UNSUPPORTED_FILTER` for tokens without smart money tracking (e.g., WCT on Optimism). Not all tokens have smart money data. Do not retry.
103
- - `token flow-intelligence` — May return all-zero flows for tokens without significant smart money activity. This is normal, not an error.
104
-
105
- ### Credit Management
106
- - `profiler labels` and `profiler balance` consume credits. Budget ~20 calls per session.
107
- - `Insufficient credits` (403, code `CREDITS_EXHAUSTED`) is a hard stop — no retry will help.
108
- - Check your Nansen dashboard for credit balance: [app.nansen.ai](https://app.nansen.ai).
109
- - Run balance checks in batches of 3-4 to avoid burning credits on rate-limit retries.
118
+ ## Troubleshooting
110
119
 
111
- ### Error Codes to Watch
112
- | Code | Meaning | Action |
113
- |------|---------|--------|
114
- | `UNSUPPORTED_FILTER` | Filter not available for this token/chain | Remove the filter and retry, or skip this token |
115
- | `CREDITS_EXHAUSTED` | API credits depleted | Stop all API calls. Check dashboard. |
116
- | `RATE_LIMITED` | Too many requests (429) | Wait and retry (automatic with default retry) |
120
+ See [AGENTS.md Troubleshooting](AGENTS.md#troubleshooting) for the full troubleshooting guide, including error codes, known endpoint quirks, and pagination gotchas.
117
121
 
118
122
  ## Examples
119
123
 
package/TODO.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  > **Built by agents, for agents.** We prioritize improvements that create the best possible AI agent experience.
4
4
 
5
+ ## P0 - Trading Agent Support
6
+
7
+ ### Wallet Management
8
+ - [ ] `nansen wallet create` — generate a local wallet (per chain), store key securely on disk
9
+ - [ ] `nansen wallet address` — print the wallet address for funding
10
+ - [ ] `nansen wallet balance` — check wallet balance
11
+
12
+ ### Trading Execution
13
+ - [x] `nansen quote` — get a quote for a DEX swap (chain, tokens, amount)
14
+ - [x] `nansen execute` — sign and submit a trade via Nansen API (takes quote-id)
15
+ - [ ] ⚠️ EVM transaction signing — requires thorough security review before production use
16
+
17
+ > These commands are required for `nansen-trading-agent` — the autonomous trading sub-agent.
18
+ > Wallet commands should keep the agent self-contained with zero external dependencies.
19
+
20
+ ---
21
+
5
22
  ## P2 - Nice to Have
6
23
 
7
24
  ### Test Coverage Gaps
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nansen-cli",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Command-line interface for Nansen API - designed for AI agents",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/api.js CHANGED
@@ -320,6 +320,11 @@ function loadConfig() {
320
320
  config = { apiKey: null, baseUrl: 'https://api.nansen.ai' };
321
321
  }
322
322
 
323
+ // Ensure baseUrl default (config file from older versions may omit it)
324
+ if (!config.baseUrl) {
325
+ config.baseUrl = 'https://api.nansen.ai';
326
+ }
327
+
323
328
  // Env vars override individual fields
324
329
  if (process.env.NANSEN_API_KEY) {
325
330
  config.apiKey = process.env.NANSEN_API_KEY;
@@ -489,7 +494,41 @@ export class NansenAPI {
489
494
  } else if (code === ErrorCode.CREDITS_EXHAUSTED) {
490
495
  message = message.replace(/\.+$/, '') + '. No retry will help. Check your Nansen dashboard for credit balance.';
491
496
  } else if (code === ErrorCode.PAYMENT_REQUIRED) {
492
- message = 'Payment required (x402). Sign the paymentRequirements below per https://docs.x402.org and pass the result with --x402-payment-signature <value>.';
497
+ // Try x402 auto-payment with fallback across payment networks
498
+ if (!this.defaultHeaders['Payment-Signature']) {
499
+ try {
500
+ const { createPaymentSignatures } = await import('./x402.js');
501
+ for await (const { signature, network } of createPaymentSignatures(response, url)) {
502
+ const paidResponse = await fetch(url, {
503
+ method: 'POST',
504
+ headers: {
505
+ 'Content-Type': 'application/json',
506
+ 'X-Client-Type': 'nansen-cli',
507
+ 'X-Client-Version': packageVersion,
508
+ 'Payment-Signature': signature,
509
+ ...this.defaultHeaders,
510
+ ...options.headers,
511
+ },
512
+ body: JSON.stringify(NansenAPI.cleanBody(body)),
513
+ });
514
+ if (paidResponse.ok) {
515
+ const chain = network.startsWith('solana:') ? 'Solana' : 'Base';
516
+ console.error(`[x402] Paid via ${chain} USDC`);
517
+ // Check remaining balance and warn if low
518
+ try {
519
+ const { checkX402Balance } = await import('./x402.js');
520
+ const balance = await checkX402Balance(network);
521
+ if (balance !== null && balance < 0.25) {
522
+ console.error(`[x402] Warning: USDC balance low ($${balance.toFixed(2)}). Fund your wallet to avoid interruptions.`);
523
+ }
524
+ } catch { /* balance check is best-effort */ }
525
+ return await paidResponse.json();
526
+ }
527
+ // This payment option was rejected, try next
528
+ }
529
+ } catch { /* x402 auto-pay unavailable, fall through */ }
530
+ }
531
+ message = 'Payment required. To access this endpoint:\n • Set an API key: nansen login --api-key <key> (get one at https://app.nansen.ai/api)\n • Or pay per call: nansen wallet create, fund with USDC on Base or Solana (from $0.01/call, min $0.05 balance)\n • Docs: https://docs.x402.org';
493
532
  const paymentHeader = response.headers.get('payment-required');
494
533
  if (paymentHeader) {
495
534
  try {
package/src/cli.js CHANGED
@@ -4,6 +4,8 @@
4
4
  */
5
5
 
6
6
  import { NansenAPI, NansenError, ErrorCode, saveConfig, deleteConfig, getConfigFile, clearCache, getCacheDir, validateAddress, sleep } from './api.js';
7
+ import { buildWalletCommands } from './wallet.js';
8
+ import { buildTradingCommands } from './trading.js';
7
9
  import fs from 'fs';
8
10
  import { getUpdateNotification, scheduleUpdateCheck } from './update-check.js';
9
11
  import { createRequire } from 'module';
@@ -839,6 +841,9 @@ COMMANDS:
839
841
  login Save your API key (interactive)
840
842
  logout Remove saved API key
841
843
  schema Output JSON schema for all commands (for agent introspection)
844
+ wallet Local wallet management (create, list, show, export, default, delete)
845
+ quote Get a DEX swap quote (chain, tokens, amount)
846
+ execute Sign and broadcast a quoted trade
842
847
  cache Cache management (clear)
843
848
  smart-money Smart Money analytics (netflow, dex-trades, perp-trades, holdings, dcas, historical-holdings)
844
849
  profiler Wallet profiling (balance, labels, transactions, pnl, pnl-summary, search,
@@ -968,10 +973,16 @@ export function buildCommands(deps = {}) {
968
973
 
969
974
  return {
970
975
  'login': async (args, apiInstance, flags, options) => {
971
- log('Nansen CLI Login\n');
972
- log('Get your API key at: https://app.nansen.ai/api\n');
973
-
974
- const apiKey = await promptFn('Enter your API key: ', true);
976
+ // Support non-interactive: nansen login --api-key <key>
977
+ let apiKey = options['api-key'] || options.apiKey;
978
+
979
+ if (!apiKey) {
980
+ log('Nansen CLI Login\n');
981
+ log('Get your API key at: https://app.nansen.ai/api\n');
982
+ log('Tip: For non-interactive use: nansen login --api-key <key>\n');
983
+
984
+ apiKey = await promptFn('Enter your API key: ', true);
985
+ }
975
986
 
976
987
  if (!apiKey || apiKey.trim().length === 0) {
977
988
  log('\n❌ No API key provided');
@@ -1346,7 +1357,7 @@ export function buildCommands(deps = {}) {
1346
1357
  }
1347
1358
 
1348
1359
  // Commands that don't require API authentication
1349
- export const NO_AUTH_COMMANDS = ['login', 'logout', 'help', 'schema', 'cache'];
1360
+ export const NO_AUTH_COMMANDS = ['login', 'logout', 'help', 'schema', 'cache', 'wallet', 'quote', 'execute'];
1350
1361
 
1351
1362
  // Command aliases for convenience
1352
1363
  export const COMMAND_ALIASES = {
@@ -1489,7 +1500,7 @@ export async function runCLI(rawArgs, deps = {}) {
1489
1500
  scheduleUpdateCheck();
1490
1501
  const notify = () => { if (updateNotification) errorOutput(updateNotification); };
1491
1502
 
1492
- const commands = { ...buildCommands(deps), ...commandOverrides };
1503
+ const commands = { ...buildCommands(deps), ...buildWalletCommands(deps), ...buildTradingCommands(deps), ...commandOverrides };
1493
1504
 
1494
1505
  if (flags.version || flags.v) {
1495
1506
  output(VERSION);
@@ -1522,10 +1533,12 @@ export async function runCLI(rawArgs, deps = {}) {
1522
1533
  return { type: 'command-help', command };
1523
1534
  }
1524
1535
  }
1525
- // Fallback to main help
1526
- output(BANNER + HELP);
1527
- notify();
1528
- return { type: 'help' };
1536
+ // Commands with handlers (e.g. quote, execute) show their own usage
1537
+ if (command === 'help' || !commands[command]) {
1538
+ output(BANNER + HELP);
1539
+ notify();
1540
+ return { type: 'help' };
1541
+ }
1529
1542
  }
1530
1543
 
1531
1544
  if (!commands[command]) {