pandora-cli-skills 1.1.27 → 1.1.28

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.
@@ -55,7 +55,7 @@ Prerequisite: Node.js `>=18`.
55
55
  - Global machine-readable output:
56
56
  - `pandora --output json doctor`
57
57
  - `pandora --output table polls list --limit 10`
58
- - `--output json` is for non-execution commands; `launch`/`clone-bet` stream script output directly.
58
+ - `--output json` is supported for all commands except `launch`/`clone-bet`; those stream script output directly.
59
59
  - Guided setup:
60
60
  - `pandora setup`
61
61
  - `pandora setup --check-usdc-code`
@@ -143,7 +143,8 @@ Prerequisite: Node.js `>=18`.
143
143
  - each scan item includes at minimum `id`, `chainId`, `marketType`, `question`, `marketCloseTimestamp`, and `odds`.
144
144
 
145
145
  ## Phase 1 limitations
146
- - `--expand` and `--with-odds` are supported on both `markets list` and `scan`.
146
+ - `scan` always includes odds; `--with-odds` is accepted for backward compatibility.
147
+ - `--expand` is supported on both `markets list` and `scan`.
147
148
  - `--active|--resolved|--expiring-soon` are client-side filters on fetched list pages.
148
149
  - Odds are indexer-derived and read-only; missing upstream liquidity/price fields can produce partial odds in some environments.
149
150
  - `scan` is indexer-backed only (no direct chain reads); freshness depends on indexer sync state.
@@ -215,7 +216,7 @@ Prerequisite: Node.js `>=18`.
215
216
  - `mirror sync`:
216
217
  - envelope is `ok=true`, `command="mirror.sync"`, with `data.strategyHash`, `data.stateFile`, `data.parameters`, `data.snapshots[]`, and `data.actions[]`.
217
218
  - hedge controls: `--hedge-trigger-usdc`, `--max-hedge-usdc`, `--hedge-ratio <n>` (default `1`), and `--no-hedge` to disable hedge execution while keeping drift rebalancing active.
218
- - trust controls: `--trust-deploy [--manifest-file <path>]`.
219
+ - trust controls: `--trust-deploy`.
219
220
  - streaming: `--stream` emits per-tick logs for `mirror sync run` (table mode streams by default).
220
221
  - rebalance sizing is pool-aware: drift notional scales with `reserveYes + reserveNo`, then bounded by `--max-rebalance-usdc`.
221
222
  - Polymarket resilience: when Polymarket endpoints are unreachable, cached snapshots under `~/.pandora/polymarket` are reused for read paths; live sync blocks execution if source data is cached/stale.
@@ -239,6 +240,24 @@ Prerequisite: Node.js `>=18`.
239
240
  ## Resolve/LP commands
240
241
  - `resolve` and `lp` are active command paths with strict flag validation, runtime preflight checks, and decoded on-chain revert reporting.
241
242
 
243
+ ## Compatibility aliases
244
+ - `--env-file` = `--dotenv-path`
245
+ - `--no-env-file` = `--skip-dotenv`
246
+ - `--amount` = `--amount-usdc` (trade/watch/autopilot paths)
247
+ - `--market-id` = `--condition-id` (polymarket trade)
248
+ - `--force-gate` = `--skip-gate` (deprecated; prefer `--skip-gate`)
249
+
250
+ ## Additional JSON response shapes
251
+ - `doctor`: `{ ok: true, command: "doctor", data: { schemaVersion, generatedAt, env, rpc, codeChecks, polymarket, summary } }`
252
+ - `resolve`:
253
+ - dry-run: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "dry-run", txPlan } }`
254
+ - execute: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "execute", tx } }`
255
+ - `lp`:
256
+ - add/remove: `{ ok: true, command: "lp", data: { schemaVersion, generatedAt, action: "add"|"remove", mode, txPlan, tx? } }`
257
+ - positions: `{ ok: true, command: "lp", data: { schemaVersion, generatedAt, action: "positions", mode: "read", wallet, count, items } }`
258
+ - `polymarket` (`check|preflight|approve|trade`):
259
+ - `{ ok: true, command, data: { schemaVersion, generatedAt, ... } }`
260
+
242
261
  ### Resolve command
243
262
  - Usage:
244
263
  - `pandora [--output table|json] resolve --poll-address <address> --answer yes|no --reason <text> --dry-run|--execute [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>]`
package/SKILL.md CHANGED
@@ -36,7 +36,7 @@ npm link
36
36
 
37
37
  ## CLI ergonomics
38
38
  - Global output mode: `--output table|json` (default `table`)
39
- - `--output json` applies to non-execution commands; `launch`/`clone-bet` stream script output directly.
39
+ - `--output json` is supported for all commands except `launch`/`clone-bet`; those stream script output directly.
40
40
  - Guided setup command: `pandora setup`
41
41
  - Phase 1 discovery command: `pandora scan`
42
42
  - Phase 1 lifecycle filters: `pandora markets list --active|--resolved|--expiring-soon`
@@ -67,6 +67,7 @@ This section mirrors live CLI help output so agent runs can rely on one source o
67
67
 
68
68
  ```text
69
69
  pandora [--output table|json] --version
70
+ pandora [--output table|json] help
70
71
  pandora [--output table|json] init-env [--force] [--dotenv-path <path>] [--example <path>]
71
72
  pandora [--output table|json] doctor [--dotenv-path <path>] [--skip-dotenv] [--check-usdc-code] [--check-polymarket] [--rpc-timeout-ms <ms>]
72
73
  pandora [--output table|json] setup [--force] [--dotenv-path <path>] [--example <path>] [--check-usdc-code] [--check-polymarket] [--rpc-timeout-ms <ms>]
@@ -79,7 +80,7 @@ pandora [--output table|json] events get --id <id> [--type all|liquidity|oracle-
79
80
  pandora [--output table|json] positions list [--wallet <address>] [--market-address <address>] [--chain-id <id>] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--where-json <json>]
80
81
  pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--limit <n>] [--include-events|--no-events] [--with-lp] [--rpc-url <url>]
81
82
  pandora [--output table|json] watch [--wallet <address>] [--market-address <address>] [--side yes|no] [--amount-usdc <amount>] [--iterations <n>] [--interval-ms <ms>] [--chain-id <id>] [--include-events|--no-events] [--yes-pct <0-100>] [--alert-yes-below <0-100>] [--alert-yes-above <0-100>] [--alert-net-liquidity-below <amount>] [--alert-net-liquidity-above <amount>] [--fail-on-alert]
82
- pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--expand] [--with-odds]
83
+ pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--active|--resolved|--expiring-soon] [--expiring-hours <n>] [--expand]
83
84
  pandora [--output table|json] quote [--indexer-url <url>] [--timeout-ms <ms>] --market-address <address> --side yes|no --amount-usdc <amount> [--yes-pct <0-100>] [--slippage-bps <0-10000>]
84
85
  pandora [--output table|json] trade [--indexer-url <url>] [--timeout-ms <ms>] [--dotenv-path <path>] [--skip-dotenv] --market-address <address> --side yes|no --amount-usdc <amount> --dry-run|--execute [--yes-pct <0-100>] [--slippage-bps <0-10000>] [--min-shares-out-raw <uint>] [--max-amount-usdc <amount>] [--min-probability-pct <0-100>] [--max-probability-pct <0-100>] [--allow-unquoted-execute] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>]
85
86
  pandora [--output table|json] history --wallet <address> [--chain-id <id>] [--market-address <address>] [--side yes|no|both] [--status all|open|won|lost|closed] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by timestamp|pnl|entry-price|mark-price] [--order-direction asc|desc] [--include-seed]
@@ -94,21 +95,23 @@ pandora [--output table|json] analyze --market-address <address> [--provider <na
94
95
  pandora [--output table|json] suggest --wallet <address> --risk low|medium|high --budget <amount> [--count <n>] [--include-venues pandora,polymarket]
95
96
  pandora [--output table|json] resolve --poll-address <address> --answer yes|no --reason <text> --dry-run|--execute [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>]
96
97
  pandora [--output table|json] lp add|remove|positions [--market-address <address>] [--wallet <address>] [--amount-usdc <n>] [--lp-tokens <n>] [--dry-run|--execute] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>] [--deadline-seconds <n>] [--indexer-url <url>] [--timeout-ms <ms>]
98
+ pandora launch [--dotenv-path <path>] [--skip-dotenv] [script args...]
99
+ pandora clone-bet [--dotenv-path <path>] [--skip-dotenv] [script args...]
97
100
  ```
98
101
 
99
102
  Mirror subcommand detail:
100
103
 
101
104
  ```text
102
105
  browse --min-yes-pct <n> --max-yes-pct <n> --min-volume-24h <n> [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>]
103
- plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--polymarket-gamma-url <url>]
104
- deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier 500|3000|10000] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--manifest-file <path>]
106
+ plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--polymarket-gamma-url <url>]
107
+ deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier 500|3000|10000] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--min-close-lead-seconds <n>] [--manifest-file <path>]
105
108
  verify --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--trust-deploy] [--manifest-file <path>] [--include-similarity] [--with-rules] [--allow-rule-mismatch]
106
109
  lp-explain --liquidity-usdc <n> [--source-yes-pct <0-100>] [--distribution-yes <parts>] [--distribution-no <parts>]
107
- hedge-calc [--reserve-yes-usdc <n> --reserve-no-usdc <n>] [--excess-yes-usdc <n>] [--excess-no-usdc <n>] [--polymarket-yes-pct <0-100>] [--hedge-ratio <n>] [--hedge-cost-bps <n>] [--volume-scenarios <csv>] [--pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug>]
108
- simulate --liquidity-usdc <n> [--source-yes-pct <0-100>] [--target-yes-pct <0-100>] [--distribution-yes <parts>] [--distribution-no <parts>] [--fee-tier 500|3000|10000] [--volume-scenarios <csv>] [--hedge-ratio <n>] [--polymarket-yes-pct <0-100>]
110
+ hedge-calc [--reserve-yes-usdc <n> --reserve-no-usdc <n>] [--excess-yes-usdc <n>] [--excess-no-usdc <n>] [--polymarket-yes-pct <0-100>] [--hedge-ratio <n>] [--hedge-cost-bps <n>] [--volume-scenarios <csv>] [--pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug>] [--trust-deploy] [--manifest-file <path>]
111
+ simulate --liquidity-usdc <n> [--source-yes-pct <0-100>] [--target-yes-pct <0-100>] [--distribution-yes <parts>] [--distribution-no <parts>] [--fee-tier 500|3000|10000] [--volume-scenarios <csv>] [--hedge-ratio <n>] [--hedge-cost-bps <n>] [--polymarket-yes-pct <0-100>]
109
112
  go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--paper|--dry-run|--execute-live|--execute] [--private-key <hex>] [--funder <address>] [--auto-sync] [--sync-once] [--skip-gate]
110
- sync run|once|start|stop|status --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--dry-run|--execute-live|--execute] [--private-key <hex>] [--funder <address>] [--trust-deploy] [--skip-gate] [--daemon] [--stream] [--interval-ms <ms>] [--drift-trigger-bps <n>] [--hedge-trigger-usdc <n>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--state-file <path>] [--kill-switch-file <path>]
111
- status --state-file <path>|--strategy-hash <hash> [--with-live] [--trust-deploy]
113
+ sync run|once|start|stop|status --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--dry-run|--execute-live|--execute] [--private-key <hex>] [--funder <address>] [--trust-deploy] [--skip-gate] [--daemon] [--stream] [--interval-ms <ms>] [--drift-trigger-bps <n>] [--hedge-trigger-usdc <n>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--min-time-to-close-sec <n>] [--state-file <path>] [--kill-switch-file <path>]
114
+ status --state-file <path>|--strategy-hash <hash> [--with-live] [--pandora-market-address <address>|--market-address <address>] [--polymarket-market-id <id>|--polymarket-slug <slug>]
112
115
  close --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute
113
116
  ```
114
117
 
@@ -189,7 +192,8 @@ pandora --output json suggest --wallet <0x...> --risk medium --budget 50 --inclu
189
192
  - each scan item includes at minimum `id`, `chainId`, `marketType`, `question`, `marketCloseTimestamp`, and `odds`.
190
193
 
191
194
  ## Phase 1 limitations
192
- - `--expand` and `--with-odds` are supported on both `markets list` and `scan`.
195
+ - `scan` always includes odds; `--with-odds` is accepted for backward compatibility.
196
+ - `--expand` is supported on both `markets list` and `scan`.
193
197
  - `--active|--resolved|--expiring-soon` are client-side filters over fetched pages.
194
198
  - Odds are indexer-derived and read-only; missing upstream liquidity/price fields can produce partial odds in some environments.
195
199
  - `scan` is indexer-backed only (no direct chain reads); freshness depends on indexer sync state.
@@ -303,6 +307,13 @@ Mode aliases:
303
307
  - Mirror commands accept either market flag name:
304
308
  - `--pandora-market-address` or `--market-address`
305
309
 
310
+ Common compatibility aliases:
311
+ - `--env-file` = `--dotenv-path`
312
+ - `--no-env-file` = `--skip-dotenv`
313
+ - `--amount` = `--amount-usdc` (trade/watch/autopilot paths)
314
+ - `--market-id` = `--condition-id` (polymarket trade)
315
+ - `--force-gate` = `--skip-gate` (deprecated; use `--skip-gate`)
316
+
306
317
  ## Distribution format (ppb)
307
318
  - Distribution inputs use parts-per-billion (ppb):
308
319
  - `--distribution-yes 580000000` means YES seed weight = `58%`.
@@ -325,6 +336,18 @@ Common structured error codes for automation:
325
336
  Error envelope:
326
337
  - `{ ok: false, error: { code, message, details? } }`
327
338
 
339
+ ## Additional JSON response shapes
340
+ - `doctor`:
341
+ - `{ ok: true, command: "doctor", data: { schemaVersion, generatedAt, env, rpc, codeChecks, polymarket, summary } }`
342
+ - `resolve`:
343
+ - dry-run: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "dry-run", txPlan } }`
344
+ - execute: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "execute", tx } }`
345
+ - `lp`:
346
+ - `lp add|remove`: `{ ok: true, command: "lp", data: { schemaVersion, generatedAt, action: "add"|"remove", mode, txPlan, tx? } }`
347
+ - `lp positions`: `{ ok: true, command: "lp", data: { schemaVersion, generatedAt, action: "positions", mode: "read", wallet, count, items[] } }`
348
+ - `polymarket`:
349
+ - `check|preflight|approve|trade` all return `{ ok: true, command, data }` with `schemaVersion` and `generatedAt`; execute paths include `result`/`tx` blocks.
350
+
328
351
  ## Pandora mainnet deployment reference
329
352
  - PredictionOracle (Factory): `0x259308E7d8557e4Ba192De1aB8Cf7e0E21896442`
330
353
  - PredictionPoll (Implementation): `0xC49c177736107fD8351ed6564136B9ADbE5B1eC3`
@@ -15,10 +15,6 @@ function createCliOutputService(options = {}) {
15
15
  console.log(JSON.stringify(payload, null, 2));
16
16
  }
17
17
 
18
- function emitJsonError(payload) {
19
- console.log(JSON.stringify(payload, null, 2));
20
- }
21
-
22
18
  function formatErrorValue(value) {
23
19
  if (typeof value === 'string') return value;
24
20
  if (value && typeof value.message === 'string') return value.message;
@@ -75,7 +71,7 @@ function createCliOutputService(options = {}) {
75
71
  const envelope = toErrorEnvelope(error);
76
72
 
77
73
  if (outputMode === 'json') {
78
- emitJsonError(envelope);
74
+ emitJson(envelope);
79
75
  } else {
80
76
  console.error(`[${envelope.error.code}] ${envelope.error.message}`);
81
77
  if (envelope.error.details && Array.isArray(envelope.error.details.errors) && envelope.error.details.errors.length) {
@@ -127,4 +123,3 @@ function createCliOutputService(options = {}) {
127
123
  module.exports = {
128
124
  createCliOutputService,
129
125
  };
130
-
@@ -186,29 +186,6 @@ function toFiniteNumber(value) {
186
186
  return numeric;
187
187
  }
188
188
 
189
- function toBigIntOrNull(value) {
190
- if (value === null || value === undefined || value === '') return null;
191
- if (typeof value === 'bigint') return value;
192
- try {
193
- return BigInt(value);
194
- } catch {
195
- return null;
196
- }
197
- }
198
-
199
- function normalizeBigIntForJson(value) {
200
- if (typeof value === 'bigint') return value.toString();
201
- if (Array.isArray(value)) return value.map((item) => normalizeBigIntForJson(item));
202
- if (value && typeof value === 'object') {
203
- const output = {};
204
- for (const [key, nested] of Object.entries(value)) {
205
- output[key] = normalizeBigIntForJson(nested);
206
- }
207
- return output;
208
- }
209
- return value;
210
- }
211
-
212
189
  function normalizeResolveOutcome(answer) {
213
190
  const normalized = String(answer || '').trim().toLowerCase();
214
191
  if (normalized === 'yes') return true;
@@ -18,10 +18,6 @@ function round(value, decimals = 6) {
18
18
  return Math.round(numeric * factor) / factor;
19
19
  }
20
20
 
21
- function clamp(value, min, max) {
22
- return Math.max(min, Math.min(max, value));
23
- }
24
-
25
21
  function createServiceError(code, message, details = undefined) {
26
22
  const err = new Error(message);
27
23
  err.code = code;
@@ -0,0 +1,52 @@
1
+ const SCAN_USAGE =
2
+ 'pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--active|--resolved|--expiring-soon] [--expiring-hours <n>] [--expand]';
3
+
4
+ function createRunScanCommand(deps) {
5
+ const {
6
+ parseIndexerSharedFlags,
7
+ includesHelpFlag,
8
+ emitSuccess,
9
+ commandHelpPayload,
10
+ maybeLoadIndexerEnv,
11
+ resolveIndexerUrl,
12
+ parseMarketsListFlags,
13
+ fetchMarketsListPage,
14
+ buildMarketsEnrichmentContext,
15
+ buildMarketsListPayload,
16
+ renderScanTable,
17
+ } = deps;
18
+
19
+ return async function runScanCommand(args, context) {
20
+ const shared = parseIndexerSharedFlags(args);
21
+ if (includesHelpFlag(shared.rest)) {
22
+ if (context.outputMode === 'json') {
23
+ emitSuccess(context.outputMode, 'scan.help', commandHelpPayload(SCAN_USAGE));
24
+ } else {
25
+ console.log(`Usage: ${SCAN_USAGE}`);
26
+ }
27
+ return;
28
+ }
29
+
30
+ maybeLoadIndexerEnv(shared);
31
+ const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
32
+
33
+ const options = parseMarketsListFlags(shared.rest);
34
+ options.withOdds = true;
35
+
36
+ const { items, pageInfo, unfilteredCount } = await fetchMarketsListPage(indexerUrl, options, shared.timeoutMs);
37
+ const enrichmentContext = await buildMarketsEnrichmentContext(indexerUrl, items, options, shared.timeoutMs);
38
+ const payload = buildMarketsListPayload(indexerUrl, options, items, pageInfo, {
39
+ includeEnrichedItems: true,
40
+ scanMode: true,
41
+ enrichmentContext,
42
+ unfilteredCount,
43
+ });
44
+
45
+ emitSuccess(context.outputMode, 'scan', payload, renderScanTable);
46
+ };
47
+ }
48
+
49
+ module.exports = {
50
+ SCAN_USAGE,
51
+ createRunScanCommand,
52
+ };
package/cli/pandora.cjs CHANGED
@@ -5,6 +5,7 @@ const path = require('path');
5
5
  const { spawnSync } = require('child_process');
6
6
  const { createCommandRouter } = require('./lib/command_router.cjs');
7
7
  const { createCliOutputService } = require('./lib/cli_output_service.cjs');
8
+ const { createRunScanCommand } = require('./lib/scan_command_service.cjs');
8
9
  const {
9
10
  fetchHistory,
10
11
  } = require('./lib/history_service.cjs');
@@ -258,7 +259,7 @@ Usage:
258
259
  pandora [--output table|json] positions list [--wallet <address>] [--market-address <address>] [--chain-id <id>] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--where-json <json>]
259
260
  pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--limit <n>] [--include-events|--no-events] [--with-lp] [--rpc-url <url>]
260
261
  pandora [--output table|json] watch [--wallet <address>] [--market-address <address>] [--side yes|no] [--amount-usdc <amount>] [--iterations <n>] [--interval-ms <ms>] [--chain-id <id>] [--include-events|--no-events] [--yes-pct <0-100>] [--alert-yes-below <0-100>] [--alert-yes-above <0-100>] [--alert-net-liquidity-below <amount>] [--alert-net-liquidity-above <amount>] [--fail-on-alert]
261
- pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--expand] [--with-odds]
262
+ pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--active|--resolved|--expiring-soon] [--expiring-hours <n>] [--expand]
262
263
  pandora [--output table|json] quote [--indexer-url <url>] [--timeout-ms <ms>] --market-address <address> --side yes|no --amount-usdc <amount> [--yes-pct <0-100>] [--slippage-bps <0-10000>]
263
264
  pandora [--output table|json] trade [--indexer-url <url>] [--timeout-ms <ms>] [--dotenv-path <path>] [--skip-dotenv] --market-address <address> --side yes|no --amount-usdc <amount> --dry-run|--execute [--yes-pct <0-100>] [--slippage-bps <0-10000>] [--min-shares-out-raw <uint>] [--max-amount-usdc <amount>] [--min-probability-pct <0-100>] [--max-probability-pct <0-100>] [--allow-unquoted-execute] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>]
264
265
  pandora [--output table|json] history --wallet <address> [--chain-id <id>] [--market-address <address>] [--side yes|no|both] [--status all|open|won|lost|closed] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by timestamp|pnl|entry-price|mark-price] [--order-direction asc|desc] [--include-seed]
@@ -286,7 +287,7 @@ Examples:
286
287
  pandora positions list --wallet 0x1234...
287
288
  pandora portfolio --wallet 0x1234... --chain-id 1 --with-lp
288
289
  pandora watch --market-address 0xabc... --side yes --amount-usdc 10 --iterations 5 --interval-ms 2000
289
- pandora scan --limit 25 --chain-id 1 --with-odds
290
+ pandora scan --active --limit 25 --chain-id 1
290
291
  pandora quote --market-address 0xabc... --side yes --amount-usdc 50
291
292
  pandora trade --dry-run --market-address 0xabc... --side no --amount-usdc 25 --max-amount-usdc 50 --min-probability-pct 20
292
293
  pandora history --wallet 0x1234... --chain-id 1 --limit 50
@@ -560,6 +561,40 @@ function inferRequestedOutputMode(argv) {
560
561
 
561
562
  function expandEqualsStyleFlags(argv) {
562
563
  const expanded = [];
564
+ const unaryBooleanFlags = new Set([
565
+ '--force',
566
+ '--skip-dotenv',
567
+ '--no-env-file',
568
+ '--check-usdc-code',
569
+ '--check-polymarket',
570
+ '--stdin',
571
+ '--active',
572
+ '--resolved',
573
+ '--expiring-soon',
574
+ '--expand',
575
+ '--with-odds',
576
+ '--include-events',
577
+ '--no-events',
578
+ '--fail-on-alert',
579
+ '--dry-run',
580
+ '--execute',
581
+ '--allow-unquoted-execute',
582
+ '--cross-venue-only',
583
+ '--allow-same-venue',
584
+ '--with-rules',
585
+ '--include-similarity',
586
+ '--paper',
587
+ '--execute-live',
588
+ '--trust-deploy',
589
+ '--allow-rule-mismatch',
590
+ '--auto-sync',
591
+ '--sync-once',
592
+ '--skip-gate',
593
+ '--force-gate',
594
+ '--daemon',
595
+ '--stream',
596
+ '--no-hedge',
597
+ ]);
563
598
  for (const token of Array.isArray(argv) ? argv : []) {
564
599
  if (
565
600
  typeof token === 'string' &&
@@ -571,6 +606,17 @@ function expandEqualsStyleFlags(argv) {
571
606
  const flag = token.slice(0, eqIndex);
572
607
  const value = token.slice(eqIndex + 1);
573
608
  if (flag && flag !== '--') {
609
+ const normalizedValue = value.trim().toLowerCase();
610
+ if (
611
+ unaryBooleanFlags.has(flag) &&
612
+ (normalizedValue === 'true' || normalizedValue === 'false')
613
+ ) {
614
+ // Preserve explicit boolean-literal assignments for unary flags so they
615
+ // are handled as invalid/explicit input by command parsers instead of
616
+ // silently flipping behavior (for example, --dry-run=false).
617
+ expanded.push(token);
618
+ continue;
619
+ }
574
620
  expanded.push(flag);
575
621
  if (value.length) expanded.push(value);
576
622
  continue;
@@ -627,13 +673,29 @@ function parseDateLikeFlag(value, flagName) {
627
673
  `${flagName} must be a date/time string (for example: "2026-03-15" or "2026-03-15T18:00:00Z"), not a bare number.`,
628
674
  );
629
675
  }
630
- const parsed = Date.parse(text);
676
+ const isDateOnly = /^\d{4}-\d{2}-\d{2}$/.test(text);
677
+ const isDateTime =
678
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,3})?)?(Z|[+-]\d{2}:\d{2})$/.test(text);
679
+ if (!isDateOnly && !isDateTime) {
680
+ throw new CliError(
681
+ 'INVALID_FLAG_VALUE',
682
+ `${flagName} must be an ISO date/time string (for example: "2026-03-15" or "2026-03-15T18:00:00Z"). Received: "${text}"`,
683
+ );
684
+ }
685
+ const normalized = isDateOnly ? `${text}T00:00:00Z` : text;
686
+ const parsed = Date.parse(normalized);
631
687
  if (Number.isNaN(parsed)) {
632
688
  throw new CliError(
633
689
  'INVALID_FLAG_VALUE',
634
690
  `${flagName} must be a valid date/time string (for example: "2026-03-15" or "2026-03-15T18:00:00Z"). Received: "${text}"`,
635
691
  );
636
692
  }
693
+ if (isDateOnly && new Date(parsed).toISOString().slice(0, 10) !== text) {
694
+ throw new CliError(
695
+ 'INVALID_FLAG_VALUE',
696
+ `${flagName} must be a real calendar date (YYYY-MM-DD). Received: "${text}"`,
697
+ );
698
+ }
637
699
  return text;
638
700
  }
639
701
 
@@ -1429,7 +1491,6 @@ function parseTradeFlags(args) {
1429
1491
  chainId: null,
1430
1492
  rpcUrl: null,
1431
1493
  privateKey: null,
1432
- funder: null,
1433
1494
  usdc: null,
1434
1495
  };
1435
1496
 
@@ -5571,7 +5632,11 @@ function renderMarketsListTable(data) {
5571
5632
  }
5572
5633
 
5573
5634
  function renderScanTable(data) {
5574
- const items = Array.isArray(data.enrichedItems) ? data.enrichedItems : [];
5635
+ const items = Array.isArray(data.enrichedItems)
5636
+ ? data.enrichedItems
5637
+ : Array.isArray(data.items)
5638
+ ? data.items
5639
+ : [];
5575
5640
  if (!items.length) {
5576
5641
  console.log('No markets found.');
5577
5642
  return;
@@ -6976,6 +7041,38 @@ function buildMarketExpansion(item) {
6976
7041
  };
6977
7042
  }
6978
7043
 
7044
+ function normalizeNumericLikeValue(value) {
7045
+ if (typeof value === 'number') {
7046
+ return Number.isFinite(value) ? value : value;
7047
+ }
7048
+ if (typeof value !== 'string') {
7049
+ return value;
7050
+ }
7051
+ const trimmed = value.trim();
7052
+ if (!trimmed.length) {
7053
+ return value;
7054
+ }
7055
+ const numeric = Number(trimmed);
7056
+ if (!Number.isFinite(numeric)) {
7057
+ return value;
7058
+ }
7059
+ return numeric;
7060
+ }
7061
+
7062
+ function normalizeMarketNumericFields(item) {
7063
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
7064
+ return item;
7065
+ }
7066
+ return {
7067
+ ...item,
7068
+ chainId: normalizeNumericLikeValue(item.chainId),
7069
+ marketCloseTimestamp: normalizeNumericLikeValue(item.marketCloseTimestamp),
7070
+ createdAt: normalizeNumericLikeValue(item.createdAt),
7071
+ totalVolume: normalizeNumericLikeValue(item.totalVolume),
7072
+ currentTvl: normalizeNumericLikeValue(item.currentTvl),
7073
+ };
7074
+ }
7075
+
6979
7076
  function enrichMarketItem(item, options, enrichmentContext) {
6980
7077
  const baseItem = item && typeof item === 'object' ? item : { value: item };
6981
7078
  const enriched = { ...baseItem };
@@ -7039,6 +7136,7 @@ function buildEnrichedMarketItems(items, options, enrichmentContext) {
7039
7136
  function buildMarketsListPayload(indexerUrl, options, items, pageInfo, opts = {}) {
7040
7137
  const isScanMode = Boolean(opts.scanMode);
7041
7138
  const enrichmentContext = opts.enrichmentContext || null;
7139
+ const normalizedItems = Array.isArray(items) ? items.map((item) => normalizeMarketNumericFields(item)) : [];
7042
7140
  const filters = { ...(options.where || {}) };
7043
7141
  if (options.lifecycle && options.lifecycle !== 'all') {
7044
7142
  filters.lifecycle = options.lifecycle;
@@ -7050,23 +7148,27 @@ function buildMarketsListPayload(indexerUrl, options, items, pageInfo, opts = {}
7050
7148
  indexerUrl,
7051
7149
  pagination: buildMarketsPagination(options),
7052
7150
  filters,
7053
- count: items.length,
7151
+ count: normalizedItems.length,
7054
7152
  pageInfo,
7055
- items,
7153
+ items: normalizedItems,
7056
7154
  };
7057
7155
 
7058
- if (typeof opts.unfilteredCount === 'number' && opts.unfilteredCount >= items.length && options.lifecycle !== 'all') {
7156
+ if (
7157
+ typeof opts.unfilteredCount === 'number' &&
7158
+ opts.unfilteredCount >= normalizedItems.length &&
7159
+ options.lifecycle !== 'all'
7160
+ ) {
7059
7161
  payload.lifecycle = {
7060
7162
  mode: options.lifecycle,
7061
7163
  expiringHours: options.lifecycle === 'expiring-soon' ? options.expiringSoonHours : null,
7062
7164
  unfilteredCount: opts.unfilteredCount,
7063
- filteredOut: opts.unfilteredCount - items.length,
7165
+ filteredOut: opts.unfilteredCount - normalizedItems.length,
7064
7166
  };
7065
7167
  }
7066
7168
 
7067
7169
  const includeEnrichedItems = Boolean(opts.includeEnrichedItems || options.expand || options.withOdds);
7068
7170
  if (includeEnrichedItems) {
7069
- const enrichedItems = buildEnrichedMarketItems(items, options, enrichmentContext);
7171
+ const enrichedItems = buildEnrichedMarketItems(normalizedItems, options, enrichmentContext);
7070
7172
  payload.enrichment = {
7071
7173
  expand: Boolean(options.expand),
7072
7174
  withOdds: Boolean(options.withOdds),
@@ -7687,7 +7789,7 @@ async function runMarketsCommand(args, context) {
7687
7789
  const responses = await Promise.all(
7688
7790
  ids.map(async (id) => {
7689
7791
  const data = await graphqlRequest(indexerUrl, query, { id }, shared.timeoutMs);
7690
- return { id, item: data.markets || null };
7792
+ return { id, item: normalizeMarketNumericFields(data.markets || null) };
7691
7793
  }),
7692
7794
  );
7693
7795
 
@@ -7724,43 +7826,19 @@ async function runMarketsCommand(args, context) {
7724
7826
  throw new CliError('INVALID_ARGS', 'markets requires a subcommand: list|get');
7725
7827
  }
7726
7828
 
7727
- async function runScanCommand(args, context) {
7728
- const shared = parseIndexerSharedFlags(args);
7729
- if (includesHelpFlag(shared.rest)) {
7730
- if (context.outputMode === 'json') {
7731
- emitSuccess(
7732
- context.outputMode,
7733
- 'scan.help',
7734
- commandHelpPayload(
7735
- 'pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--expand] [--with-odds]',
7736
- ),
7737
- );
7738
- } else {
7739
- console.log(
7740
- 'Usage: pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--expand] [--with-odds]',
7741
- );
7742
- }
7743
- return;
7744
- }
7745
-
7746
- maybeLoadIndexerEnv(shared);
7747
- const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
7748
-
7749
- const options = parseMarketsListFlags(shared.rest);
7750
- options.expand = true;
7751
- options.withOdds = true;
7752
-
7753
- const { items, pageInfo, unfilteredCount } = await fetchMarketsListPage(indexerUrl, options, shared.timeoutMs);
7754
- const enrichmentContext = await buildMarketsEnrichmentContext(indexerUrl, items, options, shared.timeoutMs);
7755
- const payload = buildMarketsListPayload(indexerUrl, options, items, pageInfo, {
7756
- includeEnrichedItems: true,
7757
- scanMode: true,
7758
- enrichmentContext,
7759
- unfilteredCount,
7760
- });
7761
-
7762
- emitSuccess(context.outputMode, 'scan', payload, renderScanTable);
7763
- }
7829
+ const runScanCommand = createRunScanCommand({
7830
+ parseIndexerSharedFlags,
7831
+ includesHelpFlag,
7832
+ emitSuccess,
7833
+ commandHelpPayload,
7834
+ maybeLoadIndexerEnv,
7835
+ resolveIndexerUrl,
7836
+ parseMarketsListFlags,
7837
+ fetchMarketsListPage,
7838
+ buildMarketsEnrichmentContext,
7839
+ buildMarketsListPayload,
7840
+ renderScanTable,
7841
+ });
7764
7842
 
7765
7843
  async function runPollsCommand(args, context) {
7766
7844
  const shared = parseIndexerSharedFlags(args);
@@ -10600,6 +10678,8 @@ async function main() {
10600
10678
  outputMode = parsed.outputMode;
10601
10679
  args = parsed.args;
10602
10680
  } catch (err) {
10681
+ // Keep parse-time failures machine-readable for agent callers even when
10682
+ // --output itself is malformed or missing.
10603
10683
  emitFailure('json', err);
10604
10684
  return;
10605
10685
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pandora-cli-skills",
3
- "version": "1.1.27",
3
+ "version": "1.1.28",
4
4
  "description": "Pandora CLI & Skills",
5
5
  "main": "cli/pandora.cjs",
6
6
  "bin": {