@vike-io/cli 0.14.0 → 0.16.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +32 -14
  3. package/package.json +4 -4
  4. package/skills/vike/SKILL.md +75 -37
  5. package/skills/vike-alerts-setup/SKILL.md +42 -10
  6. package/skills/vike-alerts-webhook/SKILL.md +18 -10
  7. package/skills/vike-candles/SKILL.md +10 -2
  8. package/skills/vike-core/SKILL.md +6 -3
  9. package/skills/vike-daily-brief/SKILL.md +68 -0
  10. package/skills/vike-follow-the-money/SKILL.md +60 -0
  11. package/skills/vike-hl-asset-briefing/SKILL.md +48 -0
  12. package/skills/vike-hl-builder-intel/SKILL.md +39 -0
  13. package/skills/vike-hl-cohort-backtest/SKILL.md +61 -0
  14. package/skills/vike-hl-cohort-positioning/SKILL.md +58 -0
  15. package/skills/vike-hl-liquidation-radar/SKILL.md +49 -0
  16. package/skills/vike-hl-position-match/SKILL.md +4 -3
  17. package/skills/vike-hl-top-traders/SKILL.md +22 -9
  18. package/skills/vike-hl-trader-profile/SKILL.md +65 -0
  19. package/skills/vike-perp-crowding/SKILL.md +49 -0
  20. package/skills/vike-perp-funding/SKILL.md +4 -1
  21. package/skills/vike-perp-overview/SKILL.md +23 -3
  22. package/skills/vike-pm-l2-archive/SKILL.md +50 -0
  23. package/skills/vike-pm-market-diligence/SKILL.md +60 -0
  24. package/skills/vike-polymarket-market-detail/SKILL.md +20 -15
  25. package/skills/vike-polymarket-markets/SKILL.md +2 -1
  26. package/skills/vike-polymarket-smart-money/SKILL.md +1 -0
  27. package/skills/vike-polymarket-trader/SKILL.md +2 -1
  28. package/skills/vike-smart-money-discovery/SKILL.md +4 -3
  29. package/skills/vike-stream/SKILL.md +99 -0
  30. package/skills/vike-token-holder-diligence/SKILL.md +58 -0
  31. package/skills/vike-token-holders/SKILL.md +6 -4
  32. package/skills/vike-token-research/SKILL.md +6 -6
  33. package/skills/vike-token-screener/SKILL.md +3 -3
  34. package/skills/vike-token-search/SKILL.md +2 -1
  35. package/skills/vike-token-smart-money-trend/SKILL.md +57 -0
  36. package/skills/vike-token-transfers/SKILL.md +8 -6
  37. package/skills/vike-wallet-balances/SKILL.md +9 -7
  38. package/skills/vike-wallet-batch/SKILL.md +3 -3
  39. package/skills/vike-wallet-compare/SKILL.md +2 -2
  40. package/skills/vike-wallet-counterparties/SKILL.md +2 -2
  41. package/skills/vike-wallet-discover/SKILL.md +3 -1
  42. package/skills/vike-wallet-ens/SKILL.md +2 -2
  43. package/skills/vike-wallet-label-provenance/SKILL.md +5 -4
  44. package/skills/vike-wallet-pnl-history/SKILL.md +2 -2
  45. package/skills/vike-wallet-portfolio/SKILL.md +10 -8
  46. package/skills/vike-wallet-summary/SKILL.md +6 -6
  47. package/skills/vike-wallet-trace/SKILL.md +4 -3
  48. package/skills/vike-wallet-transactions/SKILL.md +2 -2
  49. package/skills/vike-wallet-who-is/SKILL.md +50 -0
  50. package/skills/vike-wallets-related/SKILL.md +52 -0
  51. package/skills.sh.json +34 -12
  52. package/src/commands/alerts.js +85 -15
  53. package/src/commands/call.js +32 -0
  54. package/src/commands/candles.js +11 -15
  55. package/src/commands/doctor.js +4 -4
  56. package/src/commands/hl.js +274 -0
  57. package/src/commands/perp.js +5 -5
  58. package/src/commands/polymarket.js +105 -5
  59. package/src/commands/search.js +10 -0
  60. package/src/commands/stream.js +308 -0
  61. package/src/commands/token.js +2 -0
  62. package/src/commands/tool-command.js +27 -0
  63. package/src/commands/wallet.js +21 -0
  64. package/src/index.js +10 -1
  65. package/src/mcp-client.js +37 -16
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: vike-wallets-related
3
+ description: Gather evidence on whether two wallets, or a wallet and its possible siblings, belong to the same entity - labels, first funders, shared counterparties and tokens with exchanges and routers removed, direct links, a depth-2 trace - with confidence tiers. Use when the user asks "are these wallets the same person" / "find wallets related to 0x..." / "is this a sybil cluster" / "does B belong to A".
4
+ allowed-tools: Bash(vike-data:*)
5
+ ---
6
+
7
+ # vike-wallets-related
8
+
9
+ Evidence, never proof: no tool exposes a deposit-address owner or a cluster id. Cap the cluster at 10 wallets and ask before widening. About 20-30 credits.
10
+
11
+ ## Chain
12
+
13
+ ```bash
14
+ vike-data labels $A --json # 1 A and B: same entity already?
15
+ vike-data labels $B --json
16
+ vike-data wallet first-funder $A --json # 2 earliest inbound coin and token, sender labelled
17
+ vike-data wallet first-funder $B --json
18
+ vike-data wallet compare $A $B --window 90d --limit 50 --json # 3, up to ~20 s on busy wallets
19
+ vike-data wallet batch <shared counterparties> --json # 2 label them, drop exchanges and routers
20
+ vike-data wallet trace $A --depth 2 --width 10 --window 90d --min-usd 1000 --json # 4 is B in A's neighbourhood?
21
+ ```
22
+
23
+ Add `--chain bsc|base|hyperevm` to every wallet command when the wallets live there (default ethereum); `labels` reads the Ethereum label table, so on other chains use `wallet batch --chain`.
24
+
25
+ ## Siblings mode (only A is known)
26
+
27
+ `wallet counterparties $A --window 90d --limit 50`, label them with `wallet batch`, drop `cex`, `dex`, `market_maker` and contract rows, keep the highest-volume unlabelled or `user` wallets, then `wallet compare $A <candidate>` for the top 3-5, `wallet first-funder` on each, and `wallet trace` once. Stop at 10 wallets.
28
+
29
+ ## Reading
30
+
31
+ - `compare` returns `shared_counterparties` (`wallet`, `a_volume_usd`, `b_volume_usd`, `entity`, `wallet_type`) and `shared_tokens`. The big shared rows are almost always an exchange, a router or USDT/USDC/the native coin: in a live market-maker pair the top shared counterparties were Binance, another wallet of the same firm and Coinbase, the shared tokens ETH, USDT and USDC. **Remove CEX, router and stablecoin overlap before counting anything.**
32
+ - **Direct links**: B appearing as a node of A's trace (an edge with `out_usd` or `in_usd` > 0 between them) is stronger than any overlap.
33
+ - **First funders**: the same non-exchange sender in `first_native_in` or `first_token_in` of both is a link; the same exchange hot wallet is not (everyone withdrawing from it shares it). Native rows hold top-level sends only and history starts per chain (`coverage.history_from`): no match is not evidence against.
34
+
35
+ | Tier | Evidence |
36
+ |---|---|
37
+ | High | the same entity in `labels` / `batch` for both; or a direct transfer between them AND a shared non-CEX counterparty |
38
+ | Medium | several shared non-exchange counterparties AND shared niche tokens (not stablecoins/ETH); one direct link of size; or the same non-exchange first funder |
39
+ | Low / none | overlap only through exchanges, routers, stablecoins; matching ENS style; funding from the same CEX |
40
+
41
+ State the tier and the list of what was removed.
42
+
43
+ ## Anti-patterns
44
+
45
+ - Don't claim "same person" from a shared CEX hot wallet: everyone who uses that exchange shares it.
46
+ - Don't trace through exchanges or past 10 wallets; don't trace at `--min-usd 0`.
47
+ - Don't call two wallets unrelated from one chain: run the commands on the chain where both are active (`wallet balances` shows where the value is).
48
+
49
+ ## Pairs well with
50
+
51
+ - `vike-wallet-who-is`, `vike-wallet-compare`, `vike-wallet-counterparties`, `vike-wallet-trace`, `vike-wallet-batch`
52
+ - `vike-polymarket-trader`: for Polymarket wallets, `polymarket wallet` returns `correlated`, the wallets trading the same markets
package/skills.sh.json CHANGED
@@ -4,35 +4,41 @@
4
4
  "groupings": [
5
5
  {
6
6
  "title": "Quick start",
7
- "description": "Start here. The umbrella skill routes user questions to the right specialized skill below; vike-core covers install and auth.",
7
+ "description": "Start here. The umbrella skill routes user questions to the right specialized skill below; vike-core covers install and auth; vike-daily-brief is a cheap cross-market rundown.",
8
8
  "skills": [
9
9
  "vike",
10
- "vike-core"
10
+ "vike-core",
11
+ "vike-daily-brief"
11
12
  ]
12
13
  },
13
14
  {
14
15
  "title": "Tokens",
15
- "description": "Resolve, screen, and research tokens on Ethereum, BSC and Base. Smart-money inflow (Ethereum), holders, top buyers and sellers.",
16
+ "description": "Resolve, screen, and research tokens on Ethereum, BSC, Base and HyperEVM. Smart-money inflow and its trend over time (Ethereum), holders and holder due diligence, top buyers and sellers.",
16
17
  "skills": [
17
18
  "vike-token-search",
18
19
  "vike-token-research",
19
20
  "vike-token-transfers",
20
21
  "vike-token-holders",
22
+ "vike-token-holder-diligence",
21
23
  "vike-token-screener",
22
- "vike-smart-money-discovery"
24
+ "vike-smart-money-discovery",
25
+ "vike-token-smart-money-trend"
23
26
  ]
24
27
  },
25
28
  {
26
29
  "title": "Wallets",
27
- "description": "Per-wallet profile, flows, counterparty graphs, ENS, batch labelling, label provenance.",
30
+ "description": "Who a wallet is, per-wallet profile, flows, counterparty graphs, incident tracing, relatedness evidence, ENS, batch labelling, label provenance, on Ethereum, BSC, Base and HyperEVM.",
28
31
  "skills": [
32
+ "vike-wallet-who-is",
29
33
  "vike-wallet-summary",
30
34
  "vike-wallet-portfolio",
31
35
  "vike-wallet-discover",
32
36
  "vike-wallet-transactions",
33
37
  "vike-wallet-counterparties",
34
38
  "vike-wallet-trace",
39
+ "vike-follow-the-money",
35
40
  "vike-wallet-compare",
41
+ "vike-wallets-related",
36
42
  "vike-wallet-batch",
37
43
  "vike-wallet-pnl-history",
38
44
  "vike-wallet-balances",
@@ -42,18 +48,25 @@
42
48
  },
43
49
  {
44
50
  "title": "CEX perps",
45
- "description": "Funding-rate arb and per-symbol venue comparison, plus cross-venue perp price gaps and a market snapshot.",
51
+ "description": "Funding-rate arb and per-symbol venue comparison, cross-venue perp price gaps, open interest and long/short lean per venue, and a crowding read that nets open interest against price.",
46
52
  "skills": [
47
53
  "vike-perp-funding",
48
- "vike-perp-overview"
54
+ "vike-perp-overview",
55
+ "vike-perp-crowding"
49
56
  ]
50
57
  },
51
58
  {
52
59
  "title": "Hyperliquid perps",
53
- "description": "Top traders (and checking one address against the leaderboard) and share-card reverse-lookup.",
60
+ "description": "Top traders and leaderboards, one-wallet profiles, wallet-cohort positioning and its backtest method, single-symbol briefings, liquidation risk, builder codes, and share-card reverse-lookup.",
54
61
  "skills": [
55
62
  "vike-hl-top-traders",
56
- "vike-hl-position-match"
63
+ "vike-hl-trader-profile",
64
+ "vike-hl-position-match",
65
+ "vike-hl-cohort-positioning",
66
+ "vike-hl-cohort-backtest",
67
+ "vike-hl-asset-briefing",
68
+ "vike-hl-liquidation-radar",
69
+ "vike-hl-builder-intel"
57
70
  ]
58
71
  },
59
72
  {
@@ -65,21 +78,30 @@
65
78
  },
66
79
  {
67
80
  "title": "Polymarket",
68
- "description": "Prediction-market discovery, single-market deep-dives, trader profiles, and smart-money signals with a market overview.",
81
+ "description": "Prediction-market discovery, single-market deep-dives and due diligence (order book, tape, trades with wallets, holders, on-chain settlement), trader profiles, smart-money signals, and the free L2 Parquet archive.",
69
82
  "skills": [
70
83
  "vike-polymarket-markets",
71
84
  "vike-polymarket-market-detail",
85
+ "vike-pm-market-diligence",
72
86
  "vike-polymarket-trader",
73
- "vike-polymarket-smart-money"
87
+ "vike-polymarket-smart-money",
88
+ "vike-pm-l2-archive"
74
89
  ]
75
90
  },
76
91
  {
77
92
  "title": "Alerts",
78
- "description": "Create, list, edit, delete on-chain transfer alerts (USD thresholds per token); register HTTPS webhooks with HMAC delivery.",
93
+ "description": "Create, list, edit, delete transfer alerts (a token or one wallet, USD thresholds) and Hyperliquid alerts; read what fired; HTTPS webhooks with HMAC delivery.",
79
94
  "skills": [
80
95
  "vike-alerts-setup",
81
96
  "vike-alerts-webhook"
82
97
  ]
98
+ },
99
+ {
100
+ "title": "Live streams",
101
+ "description": "Hyperliquid, HL cohort, on-chain and Polymarket events as they happen over WebSocket, as NDJSON, resumed by cursor.",
102
+ "skills": [
103
+ "vike-stream"
104
+ ]
83
105
  }
84
106
  ]
85
107
  }
@@ -1,11 +1,45 @@
1
1
  import { callTool } from '../mcp-client.js';
2
2
  import { emit, emitError } from '../format.js';
3
3
  import { parseBool, parseInt10 } from '../util.js';
4
+ import { addTool } from './tool-command.js';
5
+
6
+ // What a rule watches beyond token and USD bounds: a transfer alert's address, and the Hyperliquid
7
+ // parameters. Which alert type takes which is the server's to check; it names the fix.
8
+ const TARGET_OPTIONS = [
9
+ ['--address <a>', 'transfer: one wallet to watch (0x + 40 hex), sent or received'],
10
+ ['--address-role <r>', 'transfer: any | from | to (default any; needs --address)'],
11
+ ['--wallet <a>', 'Hyperliquid account (0x + 40 hex): required by hl_wallet and hl_liq_proximity, optional for hl_liquidation'],
12
+ ['--actions <a...>', 'hl_wallet: open close flip increase reduce (default all but reduce)'],
13
+ ['--pnl-cohorts <c...>', 'hl_cohort_fill, hl_liquidation: PnL cohorts, e.g. "3x smart" "2x smart"'],
14
+ ['--size-cohorts <c...>', 'hl_cohort_fill, hl_liquidation: size cohorts, e.g. "4x whale" whale'],
15
+ ['--index <i>', 'hl_cohort_bias (required): smi | wsi | smart_bias | whale_bias | smi_wsi_gap'],
16
+ ['--threshold <n>', 'hl_cohort_bias: level to cross (default 0); hl_liq_proximity: share of the way to liquidation, 0-1 (default 0.8)'],
17
+ ['--band <n>', 'hl_cohort_bias, hl_liq_proximity: hysteresis, > 0 (default 0.1)'],
18
+ ['--direction <d>', 'hl_cohort_bias: up | down | both (default both)'],
19
+ ];
20
+
21
+ // `--pnl-cohorts ''` clears the list: send [] (the server refuses ['']).
22
+ const list = (v) => (Array.isArray(v) && v.every((x) => x === '') ? [] : v);
23
+
24
+ function targetArgs(opts) {
25
+ return {
26
+ address: opts.address,
27
+ address_role: opts.addressRole,
28
+ wallet: opts.wallet,
29
+ actions: list(opts.actions),
30
+ pnl_cohorts: list(opts.pnlCohorts),
31
+ size_cohorts: list(opts.sizeCohorts),
32
+ index: opts.index,
33
+ threshold: opts.threshold,
34
+ band: opts.band,
35
+ direction: opts.direction,
36
+ };
37
+ }
4
38
 
5
39
  export function registerAlerts(program) {
6
40
  const cmd = program
7
41
  .command('alerts')
8
- .description('Manage on-chain transfer alerts');
42
+ .description('Manage alerts: on-chain transfers and Hyperliquid fills, liquidations, cohort bias and liquidation proximity');
9
43
 
10
44
  cmd
11
45
  .command('list')
@@ -38,7 +72,7 @@ export function registerAlerts(program) {
38
72
 
39
73
  cmd
40
74
  .command('channels')
41
- .description('List your enabled notification channels (email/discord/webhook)')
75
+ .description('List your enabled notification channels (email/webhook)')
42
76
  .option('--json', 'Emit JSON')
43
77
  .action(async (opts) => {
44
78
  try {
@@ -47,25 +81,31 @@ export function registerAlerts(program) {
47
81
  } catch (err) { emitError(err, opts); }
48
82
  });
49
83
 
50
- cmd
84
+ const create = cmd
51
85
  .command('create')
52
- .description('Create a new on-chain transfer alert')
86
+ .description('Create an alert: an on-chain transfer alert (default) or a Hyperliquid alert (--type)')
53
87
  .requiredOption('--label <l>', 'Alert label (user-visible name)')
54
- .option('--token <sym>', 'Token ticker, UPPERCASE (e.g. USDC, ETH)')
55
- .option('--chain <c>', 'eth | bsc', 'eth')
56
- .option('--min-usd <n>', 'Minimum transfer USD value')
57
- .option('--max-usd <n>', 'Maximum transfer USD value')
58
- .option('--channel <c...>', 'Notification channel(s): email, discord')
59
- .option('--cooldown <min>', 'Minimum gap between triggers (minutes)', '60')
88
+ .option('--type <t>', 'transfer | hl_wallet | hl_cohort_fill | hl_liquidation | hl_cohort_bias | hl_liq_proximity (default transfer)')
89
+ .option('--token <sym>', 'transfer: token ticker, UPPERCASE (e.g. USDC); Hyperliquid: the coin (BTC, kPEPE), required by hl_cohort_bias')
90
+ // No default chain: a Hyperliquid alert refuses one.
91
+ .option('--chain <c>', 'transfer only: eth | bsc | base | hyperevm (default eth)')
92
+ .option('--min-usd <n>', 'transfer: minimum USD value; Hyperliquid: fill or liquidation floor (hl_cohort_fill: required, >= 10000)')
93
+ .option('--max-usd <n>', 'transfer only: maximum USD value');
94
+ for (const [flags, text] of TARGET_OPTIONS) create.option(flags, text);
95
+ create
96
+ .option('--channel <c...>', 'Notification channel(s): email, webhook (Hyperliquid: webhook, or none and read them with `alerts events`)')
97
+ .option('--cooldown <min>', 'Token-wide transfer alerts: minimum gap between notifications (minutes)', '60')
60
98
  .option('--json', 'Emit JSON')
61
99
  .action(async (opts) => {
62
100
  try {
63
101
  const r = await callTool('alerts_create', {
64
102
  label: opts.label,
103
+ alert_type: opts.type,
65
104
  token_symbol: opts.token,
66
105
  chain: opts.chain,
67
106
  min_value_usd: opts.minUsd,
68
107
  max_value_usd: opts.maxUsd,
108
+ ...targetArgs(opts),
69
109
  channels: opts.channel,
70
110
  cooldown_minutes: parseInt10(opts.cooldown, 60),
71
111
  });
@@ -73,27 +113,29 @@ export function registerAlerts(program) {
73
113
  } catch (err) { emitError(err, opts); }
74
114
  });
75
115
 
76
- cmd
116
+ const edit = cmd
77
117
  .command('edit <alert_id>')
78
- .description('Update an alert (only fields you pass are modified)')
118
+ .description('Update an alert (only fields you pass are modified; \'\' clears an optional one)')
79
119
  .option('--label <l>')
80
120
  .option('--token <sym>')
81
121
  .option('--chain <c>')
82
122
  .option('--min-usd <n>')
83
- .option('--max-usd <n>')
123
+ .option('--max-usd <n>');
124
+ for (const [flags, text] of TARGET_OPTIONS) edit.option(flags, text);
125
+ edit
84
126
  .option('--channel <c...>')
85
127
  .option('--cooldown <min>')
86
128
  .option('--active <bool>', 'Pause/resume (true|false)')
87
129
  .option('--json', 'Emit JSON')
88
130
  .action(async (alertId, opts) => {
89
131
  try {
90
- const payload = { alert_id: alertId };
132
+ const payload = { alert_id: alertId, ...targetArgs(opts) };
91
133
  if (opts.label !== undefined) payload.label = opts.label;
92
134
  if (opts.token !== undefined) payload.token_symbol = opts.token;
93
135
  if (opts.chain !== undefined) payload.chain = opts.chain;
94
136
  if (opts.minUsd !== undefined) payload.min_value_usd = opts.minUsd;
95
137
  if (opts.maxUsd !== undefined) payload.max_value_usd = opts.maxUsd;
96
- if (opts.channel !== undefined) payload.channels = opts.channel;
138
+ if (opts.channel !== undefined) payload.channels = list(opts.channel);
97
139
  if (opts.cooldown !== undefined) payload.cooldown_minutes = opts.cooldown;
98
140
  if (opts.active !== undefined) payload.is_active = parseBool(opts.active, true);
99
141
  const r = await callTool('alerts_edit', payload);
@@ -115,4 +157,32 @@ export function registerAlerts(program) {
115
157
  emit(r, opts);
116
158
  } catch (err) { emitError(err, opts); }
117
159
  });
160
+
161
+ addTool(cmd, 'events', {
162
+ tool: 'alerts_events',
163
+ desc: 'Events your alerts produced, oldest first, with each delivery\'s status (kept 15 days)',
164
+ options: [
165
+ ['--cursor <c>', 'next_cursor from the previous page (an empty page returns the same one)'],
166
+ ['--limit <n>', 'Events per page, 1-500 (default 100)'],
167
+ ['--rule-id <id>', 'Only the events of this alert'],
168
+ ],
169
+ });
170
+
171
+ addTool(cmd, 'resend <delivery_id>', {
172
+ tool: 'alerts_resend_delivery',
173
+ args: ['delivery_id'],
174
+ desc: 'Send one delivery again (its id from an event\'s delivery list in `alerts events`)',
175
+ });
176
+
177
+ addTool(cmd, 'rotate-secret <channel_id>', {
178
+ tool: 'alerts_rotate_webhook_secret',
179
+ args: ['channel_id'],
180
+ desc: 'New signing secret for a webhook channel; the old one keeps signing for 24 hours',
181
+ });
182
+
183
+ addTool(cmd, 'test-webhook <channel_id>', {
184
+ tool: 'alerts_test_webhook',
185
+ args: ['channel_id'],
186
+ desc: 'Send the signed test event to a webhook channel again',
187
+ });
118
188
  }
@@ -0,0 +1,32 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { callTool } from '../mcp-client.js';
3
+ import { emitError } from '../format.js';
4
+
5
+ export function registerCall(program) {
6
+ program
7
+ .command('call <tool>')
8
+ .description('Call any MCP tool by name and print its raw result (see `vike-data schema --pretty` for the tools and their arguments)')
9
+ .option('--args <json>', 'Tool arguments as a JSON object, e.g. \'{"symbol":"BTC"}\'')
10
+ .option('--args-file <path>', 'Read the arguments JSON from a file (- for stdin)')
11
+ .option('--timeout <s>', 'Seconds to wait for the answer', '60')
12
+ .option('--json', 'Errors as JSON on stdout (the result is JSON either way)')
13
+ .action(async (tool, opts) => {
14
+ try {
15
+ const text = (opts.argsFile !== undefined
16
+ ? readFileSync(opts.argsFile === '-' ? 0 : opts.argsFile, 'utf8')
17
+ : (opts.args ?? '{}')).replace(/^/, '').trim(); // a file saved with a UTF-8 BOM
18
+ let args;
19
+ try { args = JSON.parse(text); } catch (err) {
20
+ throw new Error(`arguments are not valid JSON: ${err.message}`);
21
+ }
22
+ if (!args || typeof args !== 'object' || Array.isArray(args)) {
23
+ throw new Error('arguments must be a JSON object, e.g. {"address":"0x..."}');
24
+ }
25
+ const seconds = Number(opts.timeout);
26
+ if (!(seconds > 0)) throw new Error(`--timeout must be a positive number of seconds, got ${opts.timeout}`);
27
+ // Parsed only to check it; the text itself is sent, so numbers above 2^53 are not rounded.
28
+ const r = await callTool(tool, args, { raw: true, argsText: text, timeoutMs: seconds * 1000 });
29
+ process.stdout.write((typeof r === 'string' ? r : JSON.stringify(r)) + '\n');
30
+ } catch (err) { emitError(err, opts); }
31
+ });
32
+ }
@@ -8,26 +8,22 @@ export function registerCandles(program) {
8
8
  .description('Historical OHLCV candles — Hyperliquid markets and Polymarket outcome tokens');
9
9
 
10
10
  cmd
11
- .command('hl <symbol>')
12
- .description('Hyperliquid candles (perp or spot), aggregated from HL executed fills')
13
- .option('--interval <i>', '1m | 5m | 15m | 30m | 1h | 4h | 1d', '1h')
14
- .option('--market <m>', 'perp | spot', 'perp')
15
- .option('--dex <d>', 'Perp DEX (default HL, the core venue; others are HIP-3 builder DEXs)')
11
+ .command('hl [symbol]')
12
+ .description('Hyperliquid candles (perp or spot), aggregated from HL executed fills; --view latest = the newest 1m bar of every symbol')
13
+ .option('--view <v>', 'candles (default; needs <symbol>) | export (the same, limit defaults to 5000) | latest (every symbol, no <symbol>)')
14
+ .option('--interval <i>', '1m | 5m | 15m | 30m | 1h | 4h | 1d (default 1h; latest: 1m only)')
15
+ .option('--market <m>', 'perp | spot (default perp)')
16
+ .option('--dex <d>', 'Perp DEX (default HL, the core venue; others are HIP-3 builder DEXs; latest: omit for every DEX)')
16
17
  .option('--start <t>', 'ISO-8601 UTC or epoch ms (inclusive)')
17
18
  .option('--end <t>', 'ISO-8601 UTC or epoch ms (exclusive)')
18
- .option('--limit <n>', 'Max candles (default 500, max 5000)', '500')
19
+ .option('--limit <n>', 'Max candles (default 500, export 5000; max 5000)')
19
20
  .option('--json', 'Emit JSON')
20
21
  .action(async (symbol, opts) => {
21
22
  try {
22
- const args = {
23
- symbol,
24
- interval: opts.interval,
25
- market: opts.market,
26
- limit: parseInt10(opts.limit, 500),
27
- };
28
- if (opts.dex !== undefined) args.dex = opts.dex;
29
- if (opts.start !== undefined) args.start = opts.start;
30
- if (opts.end !== undefined) args.end = opts.end;
23
+ // Only what was given: the server's defaults stay the defaults. `latest` takes only market, dex and
24
+ // interval, so a symbol, window or limit is not sent with it.
25
+ const args = { view: opts.view, interval: opts.interval, market: opts.market, dex: opts.dex };
26
+ if (opts.view !== 'latest') Object.assign(args, { symbol, start: opts.start, end: opts.end, limit: opts.limit });
31
27
  const r = await callTool('hl_ohlcv', args);
32
28
  emit(r, opts);
33
29
  } catch (err) { emitError(err, opts); }
@@ -8,13 +8,13 @@ export function registerDoctor(program) {
8
8
  .action(async () => {
9
9
  const checks = [];
10
10
 
11
- // 1. Node version
11
+ // 1. Node version: package.json engines is >=22.12.0
12
12
  const nodeVer = process.version;
13
- const major = parseInt(nodeVer.slice(1).split('.')[0], 10);
13
+ const [major, minor] = process.versions.node.split('.').map(Number);
14
14
  checks.push({
15
15
  name: 'Node runtime',
16
- ok: major >= 20,
17
- detail: `${nodeVer} (need >= 20.0.0)`,
16
+ ok: major > 22 || (major === 22 && minor >= 12),
17
+ detail: `${nodeVer} (need >= 22.12.0)`,
18
18
  });
19
19
 
20
20
  // 2. API key