pandora-cli-skills 1.1.38 → 1.1.39

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/README.md CHANGED
@@ -49,6 +49,20 @@ pandora --output json trade --dry-run \
49
49
  pandora stream prices --indexer-url https://pandoraindexer.up.railway.app/ --interval-ms 1000
50
50
  ```
51
51
 
52
+ ### Sports Quickstart
53
+
54
+ ```bash
55
+ # list upcoming soccer events
56
+ pandora --output json sports events list --competition <id-or-slug> --limit 5
57
+
58
+ # compute trimmed-median consensus for one event
59
+ pandora --output json sports consensus --event-id <event-id> --trim-percent 20
60
+
61
+ # build conservative create + resolve plans
62
+ pandora --output json sports create plan --event-id <event-id> --selection home
63
+ pandora --output json sports resolve plan --event-id <event-id> --poll-address <0x...>
64
+ ```
65
+
52
66
  ## Fork Mode Notes
53
67
 
54
68
  - Runtime marker is included in payloads: `data.runtime.mode = "fork" | "live"`.
@@ -51,6 +51,16 @@ Prerequisite: Node.js `>=18`.
51
51
  - `npm run dry-run:clone`
52
52
  - `node cli/pandora.cjs help`
53
53
 
54
+ ## Quickstart (Sports)
55
+ - List soccer events:
56
+ - `pandora --output json sports events list --competition <id-or-slug> --limit 5`
57
+ - Compute sportsbook consensus:
58
+ - `pandora --output json sports consensus --event-id <event-id> --trim-percent 20`
59
+ - Build creation plan:
60
+ - `pandora --output json sports create plan --event-id <event-id> --selection home`
61
+ - Build manual resolve recommendation:
62
+ - `pandora --output json sports resolve plan --event-id <event-id> --poll-address <0x...>`
63
+
54
64
  ## New CLI capabilities
55
65
  - Global machine-readable output:
56
66
  - `pandora --output json doctor`
package/SKILL.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: pandora-cli-skills
3
3
  summary: Canonical skill and operator guide for Pandora CLI including mirror, polymarket, resolve, and LP flows.
4
- version: 1.1.37
4
+ version: 1.1.39
5
5
  ---
6
6
 
7
7
  # Pandora CLI & Skills
@@ -83,6 +83,7 @@ pandora [--output table|json] doctor [--dotenv-path <path>] [--skip-dotenv] [--c
83
83
  pandora [--output table|json] setup [--force] [--dotenv-path <path>] [--example <path>] [--check-usdc-code] [--check-polymarket] [--rpc-timeout-ms <ms>]
84
84
  pandora [--output table|json] markets list [--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] [--with-odds]
85
85
  pandora [--output table|json] markets get [--id <id> ...] [--stdin]
86
+ pandora [--output table|json] sports books list|events list|events live|odds snapshot|consensus|create plan|create run|sync once|sync run|sync start|sync stop|sync status|resolve plan [flags]
86
87
  pandora [--output table|json] polls list [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--status <int>] [--category <int>] [--question-contains <text>] [--where-json <json>]
87
88
  pandora [--output table|json] polls get --id <id>
88
89
  pandora [--output table|json] events list [--type all|liquidity|oracle-fee|claim] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-direction asc|desc] [--chain-id <id>] [--wallet <address>] [--market-address <address>] [--poll-address <address>] [--tx-hash <hash>]
@@ -144,6 +145,81 @@ preflight [--fork] [--fork-rpc-url <url>] [--fork-chain-id <id>] [--rpc-url <url
144
145
  trade --condition-id <id>|--slug <slug>|--token-id <id> --token yes|no --amount-usdc <n> --dry-run|--execute [--side buy|sell] [--polymarket-host <url>] [--polymarket-mock-url <url>] [--timeout-ms <ms>] [--fork] [--fork-rpc-url <url>] [--fork-chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
145
146
  ```
146
147
 
148
+ ## Sports command matrix
149
+ | Command | Purpose | Primary flags |
150
+ | --- | --- | --- |
151
+ | `sports books list` | Show sportsbook provider health and active book preference list. | `--provider`, `--book-priority`, `--timeout-ms` |
152
+ | `sports events list` | List normalized soccer events. | `--competition`, `--kickoff-after`, `--kickoff-before`, `--limit` |
153
+ | `sports events live` | List only live/in-play events. | `--competition`, `--limit`, `--provider` |
154
+ | `sports odds snapshot` | Fetch event odds snapshot plus consensus context. | `--event-id`, `--trim-percent`, `--min-tier1-books`, `--min-total-books` |
155
+ | `sports consensus` | Compute trimmed-median consensus from live or offline checks. | `--event-id` or `--checks-json`, `--trim-percent`, `--book-priority` |
156
+ | `sports create plan` | Build conservative creation plan and safety gates. | `--event-id`, `--selection`, `--market-type`, `--creation-window-open-min`, `--creation-window-close-min` |
157
+ | `sports create run` | Dry-run or execute creation path. | `--event-id`, `--dry-run/--execute`, `--liquidity-usdc`, `--chain-id`, `--rpc-url` |
158
+ | `sports sync once|run|start|stop|status` | Evaluate and operate sports sync runtime state. | `--event-id` (required for `once|run|start`), `--risk-profile`, `--state-file`, `--paper/--execute-live` |
159
+ | `sports resolve plan` | Build manual-final resolution recommendation. | `--event-id` or `--checks-json/--checks-file`, `--poll-address`, `--settle-delay-ms`, `--consecutive-checks-required` |
160
+
161
+ ## Sports consensus policy
162
+ - Odds are normalized to implied probability from decimal/American/fractional inputs.
163
+ - Consensus method is `trimmed-median` (v1), with default `--trim-percent 20`.
164
+ - Conservative coverage inputs default to `--min-tier1-books 3` and `--min-total-books 6`.
165
+ - Confidence can degrade when coverage policy is not satisfied.
166
+ - Consensus payload includes:
167
+ - `method`
168
+ - `tier1Coverage`
169
+ - `totalBooks`
170
+ - `includedBooks`
171
+ - `excludedBooks`
172
+ - `outliers`
173
+ - `consensusYesPct`
174
+ - `consensusNoPct`
175
+
176
+ ## Sports timing policy
177
+ - Creation planning defaults:
178
+ - `--creation-window-open-min 1440` (24h before kickoff)
179
+ - `--creation-window-close-min 90` (90m before kickoff)
180
+ - Core timing module defaults (spec-level fallbacks):
181
+ - creation open lead `7d`, creation close lead `15m`
182
+ - assumed event duration `3h`
183
+ - resolve open delay `30m`, resolve target delay `2h`, resolve close delay `48h`
184
+ - Resolve plan safety defaults:
185
+ - `--settle-delay-ms 600000` (10m)
186
+ - `--consecutive-checks-required 2`
187
+
188
+ ## Manual resolve workflow (sports)
189
+ 1. Build resolve plan:
190
+ - `pandora --output json sports resolve plan --event-id <id> --poll-address <0x...>`
191
+ 2. Confirm plan safety:
192
+ - require `safeToResolve=true`
193
+ - read `recommendedAnswer`, `stableWindowStartAt`, and diagnostics
194
+ 3. Execute resolution:
195
+ - run `recommendedCommand` when present
196
+ - or run manual command: `pandora resolve --poll-address <0x...> --answer yes|no|invalid --reason "<text>" --execute`
197
+ 4. If unsafe:
198
+ - continue collecting checks (`--checks-json`/`--checks-file`) and rerun until safety gates pass.
199
+
200
+ ## Sports sync risk defaults
201
+ - Conservative (`--risk-profile conservative`):
202
+ - `maxDataAgeMs=120000`
203
+ - `minCoverageRatio=0.70`
204
+ - `maxCoverageDropRatio=0.25`
205
+ - `maxSpreadJumpBps=150`
206
+ - `maxConsecutiveFailures=3`
207
+ - `maxConsecutiveGateFailures=2`
208
+ - Balanced:
209
+ - `maxDataAgeMs=150000`
210
+ - `minCoverageRatio=0.60`
211
+ - `maxCoverageDropRatio=0.30`
212
+ - `maxSpreadJumpBps=200`
213
+ - `maxConsecutiveFailures=4`
214
+ - `maxConsecutiveGateFailures=3`
215
+ - Aggressive:
216
+ - `maxDataAgeMs=180000`
217
+ - `minCoverageRatio=0.50`
218
+ - `maxCoverageDropRatio=0.40`
219
+ - `maxSpreadJumpBps=250`
220
+ - `maxConsecutiveFailures=5`
221
+ - `maxConsecutiveGateFailures=4`
222
+
147
223
  ## Read-only indexer commands
148
224
  Indexer URL resolution order:
149
225
  1. `--indexer-url`
@@ -18,6 +18,7 @@ function createCommandRouter(deps = {}) {
18
18
  runSetup,
19
19
  runMarketsCommand,
20
20
  runScanCommand,
21
+ runSportsCommand,
21
22
  runQuoteCommand,
22
23
  runTradeCommand,
23
24
  runPollsCommand,
@@ -65,6 +66,7 @@ function createCommandRouter(deps = {}) {
65
66
  requireFn('runSetup', runSetup);
66
67
  requireFn('runMarketsCommand', runMarketsCommand);
67
68
  requireFn('runScanCommand', runScanCommand);
69
+ requireFn('runSportsCommand', runSportsCommand);
68
70
  requireFn('runQuoteCommand', runQuoteCommand);
69
71
  requireFn('runTradeCommand', runTradeCommand);
70
72
  requireFn('runPollsCommand', runPollsCommand);
@@ -153,6 +155,7 @@ function createCommandRouter(deps = {}) {
153
155
  },
154
156
  markets: async (handlerArgs, handlerContext) => runMarketsCommand(handlerArgs, handlerContext),
155
157
  scan: async (handlerArgs, handlerContext) => runScanCommand(handlerArgs, handlerContext),
158
+ sports: async (handlerArgs, handlerContext) => runSportsCommand(handlerArgs, handlerContext),
156
159
  quote: async (handlerArgs, handlerContext) => runQuoteCommand(handlerArgs, handlerContext),
157
160
  trade: async (handlerArgs, handlerContext) => runTradeCommand(handlerArgs, handlerContext),
158
161
  polls: async (handlerArgs, handlerContext) => runPollsCommand(handlerArgs, handlerContext),
@@ -51,6 +51,38 @@ function buildMcpRestartCommand(cliName) {
51
51
  return `${cliName} mcp`;
52
52
  }
53
53
 
54
+ function buildMcpBoundedCommand(cliName, details) {
55
+ const toolName = cleanToken(details && details.toolName, '');
56
+ if (toolName.startsWith('sports.sync.')) {
57
+ return `${cliName} sports sync once --help`;
58
+ }
59
+ if (toolName.startsWith('mirror.sync.')) {
60
+ return `${cliName} mirror sync once --help`;
61
+ }
62
+ if (toolName.startsWith('autopilot.')) {
63
+ return `${cliName} autopilot once --help`;
64
+ }
65
+ if (toolName.startsWith('watch')) {
66
+ return `${cliName} watch --help`;
67
+ }
68
+ return `${cliName} help`;
69
+ }
70
+
71
+ function buildSportsConsensusRetryCommand(cliName, details) {
72
+ const eventId = cleanToken(details && details.eventId, '<event-id>');
73
+ return `${cliName} sports consensus --event-id ${eventId}`;
74
+ }
75
+
76
+ function buildSportsCreatePlanCommand(cliName, details) {
77
+ const eventId = cleanToken(details && details.eventId, '<event-id>');
78
+ return `${cliName} sports create plan --event-id ${eventId}`;
79
+ }
80
+
81
+ function buildSportsResolvePlanRetryCommand(cliName, details) {
82
+ const eventId = cleanToken(details && details.eventId, '<event-id>');
83
+ return `${cliName} sports resolve plan --event-id ${eventId}`;
84
+ }
85
+
54
86
  /**
55
87
  * Build deterministic Next-Best-Action recovery hints for JSON errors.
56
88
  * @param {{cliName?: string}} [options]
@@ -124,7 +156,7 @@ function createErrorRecoveryService(options = {}) {
124
156
  case 'MCP_LONG_RUNNING_MODE_BLOCKED':
125
157
  return {
126
158
  action: 'Switch to a bounded command variant for MCP',
127
- command: `${cliName} mirror sync once --help`,
159
+ command: buildMcpBoundedCommand(cliName, details),
128
160
  retryable: true,
129
161
  };
130
162
  case 'MCP_TOOL_FAILED':
@@ -134,6 +166,46 @@ function createErrorRecoveryService(options = {}) {
134
166
  command: `${cliName} --output json schema`,
135
167
  retryable: true,
136
168
  };
169
+ case 'SPORTS_PROVIDER_NOT_CONFIGURED':
170
+ case 'SPORTS_PROVIDER_FETCH_MISSING':
171
+ case 'SPORTS_LIST_COMPETITIONS_FAILED':
172
+ case 'SPORTS_LIST_EVENTS_FAILED':
173
+ case 'SPORTS_GET_EVENT_ODDS_FAILED':
174
+ case 'SPORTS_GET_EVENT_STATUS_FAILED':
175
+ return {
176
+ action: 'Retry sportsbook query with explicit provider settings',
177
+ command: `${cliName} sports books list --provider auto`,
178
+ retryable: true,
179
+ };
180
+ case 'SPORTS_PROVIDER_TIMEOUT':
181
+ case 'SPORTS_PROVIDER_REQUEST_FAILED':
182
+ case 'SPORTS_PROVIDER_HTTP_ERROR':
183
+ case 'SPORTS_PROVIDER_INVALID_JSON':
184
+ return {
185
+ action: 'Retry sportsbook read path with backup provider',
186
+ command: `${cliName} sports events list --provider backup --limit 10`,
187
+ retryable: true,
188
+ };
189
+ case 'SPORTS_CONSENSUS_FAILED':
190
+ case 'SPORTS_CONSENSUS_UNAVAILABLE':
191
+ return {
192
+ action: 'Re-run consensus for the target event',
193
+ command: buildSportsConsensusRetryCommand(cliName, details),
194
+ retryable: true,
195
+ };
196
+ case 'SPORTS_CREATE_BLOCKED':
197
+ case 'SPORTS_CREATE_FAILED':
198
+ return {
199
+ action: 'Rebuild creation plan and adjust timing/coverage inputs',
200
+ command: buildSportsCreatePlanCommand(cliName, details),
201
+ retryable: true,
202
+ };
203
+ case 'SPORTS_RESOLVE_PLAN_UNSAFE':
204
+ return {
205
+ action: 'Wait for stable final status and retry resolve plan',
206
+ command: buildSportsResolvePlanRetryCommand(cliName, details),
207
+ retryable: true,
208
+ };
137
209
  case 'MISSING_REQUIRED_FLAG':
138
210
  case 'MISSING_FLAG_VALUE':
139
211
  case 'INVALID_FLAG_VALUE':
@@ -175,6 +175,54 @@ const TOOL_DEFINITIONS = [
175
175
  { name: 'markets.list', command: ['markets', 'list'], description: 'List Pandora markets with filters.' },
176
176
  { name: 'markets.get', command: ['markets', 'get'], description: 'Get one or more markets by id.' },
177
177
  { name: 'scan', command: ['scan'], description: 'Scan markets with lifecycle filters.' },
178
+ { name: 'sports.books.list', command: ['sports', 'books', 'list'], description: 'List sportsbook provider health and configured book priorities.' },
179
+ { name: 'sports.events.list', command: ['sports', 'events', 'list'], description: 'List normalized soccer events from sportsbook providers.' },
180
+ { name: 'sports.events.live', command: ['sports', 'events', 'live'], description: 'List currently-live soccer events from sportsbook providers.' },
181
+ { name: 'sports.odds.snapshot', command: ['sports', 'odds', 'snapshot'], description: 'Get normalized event odds snapshot and consensus.' },
182
+ { name: 'sports.consensus', command: ['sports', 'consensus'], description: 'Compute majority-book trimmed-median consensus for one event.' },
183
+ { name: 'sports.create.plan', command: ['sports', 'create', 'plan'], description: 'Build conservative market creation plan from sportsbook consensus.' },
184
+ {
185
+ name: 'sports.create.run',
186
+ command: ['sports', 'create', 'run'],
187
+ description: 'Execute or dry-run sports market creation.',
188
+ mutating: true,
189
+ safeFlags: ['--dry-run', '--paper'],
190
+ executeFlags: ['--execute'],
191
+ },
192
+ {
193
+ name: 'sports.sync.once',
194
+ command: ['sports', 'sync', 'once'],
195
+ description: 'Run one sports sync evaluation tick.',
196
+ mutating: true,
197
+ safeFlags: ['--paper', '--dry-run'],
198
+ executeFlags: ['--execute-live', '--execute'],
199
+ },
200
+ {
201
+ name: 'sports.sync.run',
202
+ command: ['sports', 'sync', 'run'],
203
+ description: 'Continuous sports sync loop (blocked in MCP v1).',
204
+ longRunningBlocked: true,
205
+ mutating: true,
206
+ safeFlags: ['--paper', '--dry-run'],
207
+ executeFlags: ['--execute-live', '--execute'],
208
+ },
209
+ {
210
+ name: 'sports.sync.start',
211
+ command: ['sports', 'sync', 'start'],
212
+ description: 'Start detached sports sync runtime (blocked in MCP v1).',
213
+ longRunningBlocked: true,
214
+ mutating: true,
215
+ safeFlags: ['--paper', '--dry-run'],
216
+ executeFlags: ['--execute-live', '--execute'],
217
+ },
218
+ {
219
+ name: 'sports.sync.stop',
220
+ command: ['sports', 'sync', 'stop'],
221
+ description: 'Stop sports sync runtime.',
222
+ mutating: true,
223
+ },
224
+ { name: 'sports.sync.status', command: ['sports', 'sync', 'status'], description: 'Inspect sports sync runtime status.' },
225
+ { name: 'sports.resolve.plan', command: ['sports', 'resolve', 'plan'], description: 'Build manual-final resolution recommendation.' },
178
226
  { name: 'quote', command: ['quote'], description: 'Build YES/NO quote estimates.' },
179
227
  {
180
228
  name: 'trade',
@@ -391,6 +439,7 @@ function createMcpToolRegistry() {
391
439
  );
392
440
  blocked.code = 'MCP_LONG_RUNNING_MODE_BLOCKED';
393
441
  blocked.details = {
442
+ toolName: definition.name,
394
443
  hints: ['Use the non-long-running variant (for example, *.once) or call the CLI directly outside MCP.'],
395
444
  };
396
445
  throw blocked;