pandora-cli-skills 1.1.36 → 1.1.37

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.
@@ -39,6 +39,18 @@ function buildPolymarketPreflightCommand(cliName) {
39
39
  return `${cliName} polymarket preflight`;
40
40
  }
41
41
 
42
+ function buildMirrorDeployRetryCommand(cliName) {
43
+ return `${cliName} mirror deploy --dry-run --plan-file <plan-file>`;
44
+ }
45
+
46
+ function buildMirrorSyncRetryCommand(cliName) {
47
+ return `${cliName} mirror sync once --paper --pandora-market-address <address> --polymarket-market-id <id>`;
48
+ }
49
+
50
+ function buildMcpRestartCommand(cliName) {
51
+ return `${cliName} mcp`;
52
+ }
53
+
42
54
  /**
43
55
  * Build deterministic Next-Best-Action recovery hints for JSON errors.
44
56
  * @param {{cliName?: string}} [options]
@@ -83,6 +95,45 @@ function createErrorRecoveryService(options = {}) {
83
95
  command: buildPolymarketPreflightCommand(cliName),
84
96
  retryable: true,
85
97
  };
98
+ case 'MIRROR_DEPLOY_FAILED':
99
+ case 'MIRROR_GO_FAILED':
100
+ case 'MIRROR_GO_VERIFY_FAILED':
101
+ case 'MIRROR_GO_PREFLIGHT_FAILED':
102
+ return {
103
+ action: 'Re-run mirror deploy/verify in dry-run mode',
104
+ command: buildMirrorDeployRetryCommand(cliName),
105
+ retryable: true,
106
+ };
107
+ case 'MIRROR_SYNC_FAILED':
108
+ case 'MIRROR_GO_SYNC_FAILED':
109
+ case 'MIRROR_SYNC_PREFLIGHT_FAILED':
110
+ case 'MIRROR_SYNC_DAEMON_START_FAILED':
111
+ case 'MIRROR_SYNC_DAEMON_STOP_FAILED':
112
+ case 'MIRROR_SYNC_DAEMON_STATUS_FAILED':
113
+ return {
114
+ action: 'Run a bounded mirror sync iteration to isolate the failing gate',
115
+ command: buildMirrorSyncRetryCommand(cliName),
116
+ retryable: true,
117
+ };
118
+ case 'MCP_EXECUTE_INTENT_REQUIRED':
119
+ return {
120
+ action: 'Retry MCP tools/call with execute intent enabled',
121
+ command: buildMcpRestartCommand(cliName),
122
+ retryable: true,
123
+ };
124
+ case 'MCP_LONG_RUNNING_MODE_BLOCKED':
125
+ return {
126
+ action: 'Switch to a bounded command variant for MCP',
127
+ command: `${cliName} mirror sync once --help`,
128
+ retryable: true,
129
+ };
130
+ case 'MCP_TOOL_FAILED':
131
+ case 'UNKNOWN_TOOL':
132
+ return {
133
+ action: 'Inspect available MCP tools and retry with a supported tool name',
134
+ command: `${cliName} --output json schema`,
135
+ retryable: true,
136
+ };
86
137
  case 'MISSING_REQUIRED_FLAG':
87
138
  case 'MISSING_FLAG_VALUE':
88
139
  case 'INVALID_FLAG_VALUE':
@@ -0,0 +1,580 @@
1
+ function requireDep(deps, name) {
2
+ if (!deps || typeof deps[name] !== 'function') {
3
+ throw new Error(`mirror remaining parser requires deps.${name}()`);
4
+ }
5
+ return deps[name];
6
+ }
7
+
8
+ function parseDefaultIndexerTimeoutMs(deps) {
9
+ const value = deps && Number.isFinite(deps.defaultIndexerTimeoutMs) ? Number(deps.defaultIndexerTimeoutMs) : 60_000;
10
+ return value > 0 ? value : 60_000;
11
+ }
12
+
13
+ /**
14
+ * Creates the mirror browse parser.
15
+ * @param {object} deps
16
+ * @returns {(args: string[]) => object}
17
+ */
18
+ function createParseMirrorBrowseFlags(deps) {
19
+ const CliError = requireDep(deps, 'CliError');
20
+ const requireFlagValue = requireDep(deps, 'requireFlagValue');
21
+ const parseProbabilityPercent = requireDep(deps, 'parseProbabilityPercent');
22
+ const parsePositiveNumber = requireDep(deps, 'parsePositiveNumber');
23
+ const parseDateLikeFlag = requireDep(deps, 'parseDateLikeFlag');
24
+ const parsePositiveInteger = requireDep(deps, 'parsePositiveInteger');
25
+ const parseInteger = requireDep(deps, 'parseInteger');
26
+
27
+ return function parseMirrorBrowseFlags(args) {
28
+ const options = {
29
+ minYesPct: null,
30
+ maxYesPct: null,
31
+ minVolume24h: 0,
32
+ closesAfter: null,
33
+ closesBefore: null,
34
+ questionContains: null,
35
+ limit: 10,
36
+ chainId: null,
37
+ polymarketGammaUrl: null,
38
+ polymarketGammaMockUrl: null,
39
+ polymarketMockUrl: null,
40
+ };
41
+
42
+ for (let i = 0; i < args.length; i += 1) {
43
+ const token = args[i];
44
+ if (token === '--min-yes-pct') {
45
+ options.minYesPct = parseProbabilityPercent(requireFlagValue(args, i, '--min-yes-pct'), '--min-yes-pct');
46
+ i += 1;
47
+ continue;
48
+ }
49
+ if (token === '--max-yes-pct') {
50
+ options.maxYesPct = parseProbabilityPercent(requireFlagValue(args, i, '--max-yes-pct'), '--max-yes-pct');
51
+ i += 1;
52
+ continue;
53
+ }
54
+ if (token === '--min-volume-24h') {
55
+ options.minVolume24h = parsePositiveNumber(requireFlagValue(args, i, '--min-volume-24h'), '--min-volume-24h');
56
+ i += 1;
57
+ continue;
58
+ }
59
+ if (token === '--closes-after') {
60
+ options.closesAfter = parseDateLikeFlag(requireFlagValue(args, i, '--closes-after'), '--closes-after');
61
+ i += 1;
62
+ continue;
63
+ }
64
+ if (token === '--closes-before') {
65
+ options.closesBefore = parseDateLikeFlag(requireFlagValue(args, i, '--closes-before'), '--closes-before');
66
+ i += 1;
67
+ continue;
68
+ }
69
+ if (token === '--question-contains') {
70
+ options.questionContains = requireFlagValue(args, i, '--question-contains');
71
+ i += 1;
72
+ continue;
73
+ }
74
+ if (token === '--limit') {
75
+ options.limit = parsePositiveInteger(requireFlagValue(args, i, '--limit'), '--limit');
76
+ i += 1;
77
+ continue;
78
+ }
79
+ if (token === '--chain-id') {
80
+ options.chainId = parseInteger(requireFlagValue(args, i, '--chain-id'), '--chain-id');
81
+ i += 1;
82
+ continue;
83
+ }
84
+ if (token === '--polymarket-gamma-url') {
85
+ options.polymarketGammaUrl = requireFlagValue(args, i, '--polymarket-gamma-url');
86
+ i += 1;
87
+ continue;
88
+ }
89
+ if (token === '--polymarket-gamma-mock-url') {
90
+ options.polymarketGammaMockUrl = requireFlagValue(args, i, '--polymarket-gamma-mock-url');
91
+ i += 1;
92
+ continue;
93
+ }
94
+ if (token === '--polymarket-mock-url') {
95
+ options.polymarketMockUrl = requireFlagValue(args, i, '--polymarket-mock-url');
96
+ i += 1;
97
+ continue;
98
+ }
99
+ throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror browse: ${token}`);
100
+ }
101
+
102
+ if (options.minYesPct !== null && options.maxYesPct !== null && options.minYesPct > options.maxYesPct) {
103
+ throw new CliError('INVALID_ARGS', '--min-yes-pct cannot be greater than --max-yes-pct.');
104
+ }
105
+
106
+ return options;
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Creates the mirror verify parser.
112
+ * @param {object} deps
113
+ * @returns {(args: string[]) => object}
114
+ */
115
+ function createParseMirrorVerifyFlags(deps) {
116
+ const CliError = requireDep(deps, 'CliError');
117
+ const parseAddressFlag = requireDep(deps, 'parseAddressFlag');
118
+ const requireFlagValue = requireDep(deps, 'requireFlagValue');
119
+
120
+ return function parseMirrorVerifyFlags(args) {
121
+ const options = {
122
+ pandoraMarketAddress: null,
123
+ polymarketMarketId: null,
124
+ polymarketSlug: null,
125
+ includeSimilarity: false,
126
+ withRules: false,
127
+ allowRuleMismatch: false,
128
+ trustDeploy: false,
129
+ manifestFile: null,
130
+ polymarketHost: null,
131
+ polymarketGammaUrl: null,
132
+ polymarketGammaMockUrl: null,
133
+ polymarketMockUrl: null,
134
+ };
135
+
136
+ for (let i = 0; i < args.length; i += 1) {
137
+ const token = args[i];
138
+ if (token === '--pandora-market-address' || token === '--market-address') {
139
+ options.pandoraMarketAddress = parseAddressFlag(requireFlagValue(args, i, token), token);
140
+ i += 1;
141
+ continue;
142
+ }
143
+ if (token === '--polymarket-market-id') {
144
+ options.polymarketMarketId = requireFlagValue(args, i, '--polymarket-market-id');
145
+ i += 1;
146
+ continue;
147
+ }
148
+ if (token === '--polymarket-slug') {
149
+ options.polymarketSlug = requireFlagValue(args, i, '--polymarket-slug');
150
+ i += 1;
151
+ continue;
152
+ }
153
+ if (token === '--include-similarity') {
154
+ options.includeSimilarity = true;
155
+ continue;
156
+ }
157
+ if (token === '--with-rules') {
158
+ options.withRules = true;
159
+ continue;
160
+ }
161
+ if (token === '--allow-rule-mismatch') {
162
+ options.allowRuleMismatch = true;
163
+ continue;
164
+ }
165
+ if (token === '--trust-deploy') {
166
+ options.trustDeploy = true;
167
+ continue;
168
+ }
169
+ if (token === '--manifest-file') {
170
+ options.manifestFile = requireFlagValue(args, i, '--manifest-file');
171
+ i += 1;
172
+ continue;
173
+ }
174
+ if (token === '--polymarket-host') {
175
+ options.polymarketHost = requireFlagValue(args, i, '--polymarket-host');
176
+ i += 1;
177
+ continue;
178
+ }
179
+ if (token === '--polymarket-gamma-url') {
180
+ options.polymarketGammaUrl = requireFlagValue(args, i, '--polymarket-gamma-url');
181
+ i += 1;
182
+ continue;
183
+ }
184
+ if (token === '--polymarket-gamma-mock-url') {
185
+ options.polymarketGammaMockUrl = requireFlagValue(args, i, '--polymarket-gamma-mock-url');
186
+ i += 1;
187
+ continue;
188
+ }
189
+ if (token === '--polymarket-mock-url') {
190
+ options.polymarketMockUrl = requireFlagValue(args, i, '--polymarket-mock-url');
191
+ i += 1;
192
+ continue;
193
+ }
194
+ throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror verify: ${token}`);
195
+ }
196
+
197
+ if (!options.pandoraMarketAddress) {
198
+ throw new CliError('MISSING_REQUIRED_FLAG', 'Missing --pandora-market-address <address> (alias: --market-address).');
199
+ }
200
+ if (!options.polymarketMarketId && !options.polymarketSlug) {
201
+ throw new CliError('MISSING_REQUIRED_FLAG', 'mirror verify requires --polymarket-market-id <id> or --polymarket-slug <slug>.');
202
+ }
203
+
204
+ return options;
205
+ };
206
+ }
207
+
208
+ /**
209
+ * Creates the mirror status parser.
210
+ * @param {object} deps
211
+ * @returns {(args: string[]) => object}
212
+ */
213
+ function createParseMirrorStatusFlags(deps) {
214
+ const CliError = requireDep(deps, 'CliError');
215
+ const parseAddressFlag = requireDep(deps, 'parseAddressFlag');
216
+ const requireFlagValue = requireDep(deps, 'requireFlagValue');
217
+ const parsePositiveInteger = requireDep(deps, 'parsePositiveInteger');
218
+ const parsePositiveNumber = requireDep(deps, 'parsePositiveNumber');
219
+ const defaultIndexerTimeoutMs = parseDefaultIndexerTimeoutMs(deps);
220
+
221
+ return function parseMirrorStatusFlags(args) {
222
+ const options = {
223
+ stateFile: null,
224
+ strategyHash: null,
225
+ withLive: false,
226
+ trustDeploy: false,
227
+ manifestFile: null,
228
+ pandoraMarketAddress: null,
229
+ polymarketMarketId: null,
230
+ polymarketSlug: null,
231
+ driftTriggerBps: 150,
232
+ hedgeTriggerUsdc: 10,
233
+ indexerUrl: null,
234
+ timeoutMs: defaultIndexerTimeoutMs,
235
+ polymarketHost: null,
236
+ polymarketGammaUrl: null,
237
+ polymarketGammaMockUrl: null,
238
+ polymarketMockUrl: null,
239
+ };
240
+
241
+ for (let i = 0; i < args.length; i += 1) {
242
+ const token = args[i];
243
+ if (token === '--state-file') {
244
+ options.stateFile = requireFlagValue(args, i, '--state-file');
245
+ i += 1;
246
+ continue;
247
+ }
248
+ if (token === '--strategy-hash') {
249
+ const value = requireFlagValue(args, i, '--strategy-hash');
250
+ if (!/^[a-f0-9]{16}$/i.test(value)) {
251
+ throw new CliError('INVALID_FLAG_VALUE', '--strategy-hash must be a 16-character hex value.');
252
+ }
253
+ options.strategyHash = value.toLowerCase();
254
+ i += 1;
255
+ continue;
256
+ }
257
+ if (token === '--with-live') {
258
+ options.withLive = true;
259
+ continue;
260
+ }
261
+ if (token === '--trust-deploy') {
262
+ options.trustDeploy = true;
263
+ continue;
264
+ }
265
+ if (token === '--manifest-file') {
266
+ options.manifestFile = requireFlagValue(args, i, '--manifest-file');
267
+ i += 1;
268
+ continue;
269
+ }
270
+ if (token === '--pandora-market-address' || token === '--market-address') {
271
+ options.pandoraMarketAddress = parseAddressFlag(requireFlagValue(args, i, token), token);
272
+ i += 1;
273
+ continue;
274
+ }
275
+ if (token === '--polymarket-market-id') {
276
+ options.polymarketMarketId = requireFlagValue(args, i, '--polymarket-market-id');
277
+ i += 1;
278
+ continue;
279
+ }
280
+ if (token === '--polymarket-slug') {
281
+ options.polymarketSlug = requireFlagValue(args, i, '--polymarket-slug');
282
+ i += 1;
283
+ continue;
284
+ }
285
+ if (token === '--drift-trigger-bps') {
286
+ options.driftTriggerBps = parsePositiveInteger(requireFlagValue(args, i, '--drift-trigger-bps'), '--drift-trigger-bps');
287
+ i += 1;
288
+ continue;
289
+ }
290
+ if (token === '--hedge-trigger-usdc') {
291
+ options.hedgeTriggerUsdc = parsePositiveNumber(requireFlagValue(args, i, '--hedge-trigger-usdc'), '--hedge-trigger-usdc');
292
+ i += 1;
293
+ continue;
294
+ }
295
+ if (token === '--indexer-url') {
296
+ options.indexerUrl = requireFlagValue(args, i, '--indexer-url');
297
+ i += 1;
298
+ continue;
299
+ }
300
+ if (token === '--timeout-ms') {
301
+ options.timeoutMs = parsePositiveInteger(requireFlagValue(args, i, '--timeout-ms'), '--timeout-ms');
302
+ i += 1;
303
+ continue;
304
+ }
305
+ if (token === '--polymarket-host') {
306
+ options.polymarketHost = requireFlagValue(args, i, '--polymarket-host');
307
+ i += 1;
308
+ continue;
309
+ }
310
+ if (token === '--polymarket-gamma-url') {
311
+ options.polymarketGammaUrl = requireFlagValue(args, i, '--polymarket-gamma-url');
312
+ i += 1;
313
+ continue;
314
+ }
315
+ if (token === '--polymarket-gamma-mock-url') {
316
+ options.polymarketGammaMockUrl = requireFlagValue(args, i, '--polymarket-gamma-mock-url');
317
+ i += 1;
318
+ continue;
319
+ }
320
+ if (token === '--polymarket-mock-url') {
321
+ options.polymarketMockUrl = requireFlagValue(args, i, '--polymarket-mock-url');
322
+ i += 1;
323
+ continue;
324
+ }
325
+ throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror status: ${token}`);
326
+ }
327
+
328
+ if (!options.stateFile && !options.strategyHash) {
329
+ throw new CliError('MISSING_REQUIRED_FLAG', 'mirror status requires --state-file <path> or --strategy-hash <hash>.');
330
+ }
331
+
332
+ return options;
333
+ };
334
+ }
335
+
336
+ /**
337
+ * Creates the mirror close parser.
338
+ * @param {object} deps
339
+ * @returns {(args: string[]) => object}
340
+ */
341
+ function createParseMirrorCloseFlags(deps) {
342
+ const CliError = requireDep(deps, 'CliError');
343
+ const parseAddressFlag = requireDep(deps, 'parseAddressFlag');
344
+ const requireFlagValue = requireDep(deps, 'requireFlagValue');
345
+
346
+ return function parseMirrorCloseFlags(args) {
347
+ const options = {
348
+ pandoraMarketAddress: null,
349
+ polymarketMarketId: null,
350
+ polymarketSlug: null,
351
+ execute: false,
352
+ dryRun: false,
353
+ };
354
+
355
+ for (let i = 0; i < args.length; i += 1) {
356
+ const token = args[i];
357
+ if (token === '--pandora-market-address' || token === '--market-address') {
358
+ options.pandoraMarketAddress = parseAddressFlag(requireFlagValue(args, i, token), token);
359
+ i += 1;
360
+ continue;
361
+ }
362
+ if (token === '--polymarket-market-id') {
363
+ options.polymarketMarketId = requireFlagValue(args, i, '--polymarket-market-id');
364
+ i += 1;
365
+ continue;
366
+ }
367
+ if (token === '--polymarket-slug') {
368
+ options.polymarketSlug = requireFlagValue(args, i, '--polymarket-slug');
369
+ i += 1;
370
+ continue;
371
+ }
372
+ if (token === '--dry-run') {
373
+ options.dryRun = true;
374
+ continue;
375
+ }
376
+ if (token === '--execute') {
377
+ options.execute = true;
378
+ continue;
379
+ }
380
+ throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror close: ${token}`);
381
+ }
382
+
383
+ if (!options.pandoraMarketAddress) {
384
+ throw new CliError('MISSING_REQUIRED_FLAG', 'Missing --pandora-market-address <address> (alias: --market-address).');
385
+ }
386
+ if (!options.polymarketMarketId && !options.polymarketSlug) {
387
+ throw new CliError('MISSING_REQUIRED_FLAG', 'mirror close requires --polymarket-market-id <id> or --polymarket-slug <slug>.');
388
+ }
389
+ if (options.dryRun === options.execute) {
390
+ throw new CliError('INVALID_ARGS', 'mirror close requires exactly one mode: --dry-run or --execute.');
391
+ }
392
+
393
+ return options;
394
+ };
395
+ }
396
+
397
+ /**
398
+ * Creates the mirror lp-explain parser.
399
+ * @param {object} deps
400
+ * @returns {(args: string[]) => object}
401
+ */
402
+ function createParseMirrorLpExplainFlags(deps) {
403
+ const CliError = requireDep(deps, 'CliError');
404
+ const requireFlagValue = requireDep(deps, 'requireFlagValue');
405
+ const parsePositiveNumber = requireDep(deps, 'parsePositiveNumber');
406
+ const parseProbabilityPercent = requireDep(deps, 'parseProbabilityPercent');
407
+ const parseNonNegativeInteger = requireDep(deps, 'parseNonNegativeInteger');
408
+
409
+ return function parseMirrorLpExplainFlags(args) {
410
+ const options = {
411
+ liquidityUsdc: null,
412
+ sourceYesPct: null,
413
+ distributionYes: null,
414
+ distributionNo: null,
415
+ };
416
+
417
+ for (let i = 0; i < args.length; i += 1) {
418
+ const token = args[i];
419
+ if (token === '--liquidity-usdc') {
420
+ options.liquidityUsdc = parsePositiveNumber(requireFlagValue(args, i, '--liquidity-usdc'), '--liquidity-usdc');
421
+ i += 1;
422
+ continue;
423
+ }
424
+ if (token === '--source-yes-pct') {
425
+ options.sourceYesPct = parseProbabilityPercent(requireFlagValue(args, i, '--source-yes-pct'), '--source-yes-pct');
426
+ i += 1;
427
+ continue;
428
+ }
429
+ if (token === '--distribution-yes') {
430
+ options.distributionYes = parseNonNegativeInteger(requireFlagValue(args, i, '--distribution-yes'), '--distribution-yes');
431
+ i += 1;
432
+ continue;
433
+ }
434
+ if (token === '--distribution-no') {
435
+ options.distributionNo = parseNonNegativeInteger(requireFlagValue(args, i, '--distribution-no'), '--distribution-no');
436
+ i += 1;
437
+ continue;
438
+ }
439
+ throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror lp-explain: ${token}`);
440
+ }
441
+
442
+ if (options.liquidityUsdc === null) {
443
+ throw new CliError('MISSING_REQUIRED_FLAG', 'mirror lp-explain requires --liquidity-usdc <n>.');
444
+ }
445
+ if (
446
+ (options.distributionYes === null && options.distributionNo !== null) ||
447
+ (options.distributionYes !== null && options.distributionNo === null)
448
+ ) {
449
+ throw new CliError('INVALID_ARGS', 'Provide both --distribution-yes and --distribution-no together.');
450
+ }
451
+ if (
452
+ options.distributionYes !== null &&
453
+ options.distributionNo !== null &&
454
+ options.distributionYes + options.distributionNo !== 1_000_000_000
455
+ ) {
456
+ throw new CliError('INVALID_ARGS', '--distribution-yes + --distribution-no must equal 1000000000.');
457
+ }
458
+
459
+ return options;
460
+ };
461
+ }
462
+
463
+ /**
464
+ * Creates the mirror simulate parser.
465
+ * @param {object} deps
466
+ * @returns {(args: string[]) => object}
467
+ */
468
+ function createParseMirrorSimulateFlags(deps) {
469
+ const CliError = requireDep(deps, 'CliError');
470
+ const requireFlagValue = requireDep(deps, 'requireFlagValue');
471
+ const parsePositiveNumber = requireDep(deps, 'parsePositiveNumber');
472
+ const parseProbabilityPercent = requireDep(deps, 'parseProbabilityPercent');
473
+ const parseNonNegativeInteger = requireDep(deps, 'parseNonNegativeInteger');
474
+ const parsePositiveInteger = requireDep(deps, 'parsePositiveInteger');
475
+ const parseCsvNumberList = requireDep(deps, 'parseCsvNumberList');
476
+
477
+ return function parseMirrorSimulateFlags(args) {
478
+ const options = {
479
+ liquidityUsdc: null,
480
+ sourceYesPct: null,
481
+ targetYesPct: null,
482
+ polymarketYesPct: null,
483
+ distributionYes: null,
484
+ distributionNo: null,
485
+ feeTier: 3000,
486
+ hedgeRatio: 1,
487
+ hedgeCostBps: 35,
488
+ volumeScenarios: null,
489
+ };
490
+
491
+ for (let i = 0; i < args.length; i += 1) {
492
+ const token = args[i];
493
+ if (token === '--liquidity-usdc') {
494
+ options.liquidityUsdc = parsePositiveNumber(requireFlagValue(args, i, '--liquidity-usdc'), '--liquidity-usdc');
495
+ i += 1;
496
+ continue;
497
+ }
498
+ if (token === '--source-yes-pct') {
499
+ options.sourceYesPct = parseProbabilityPercent(requireFlagValue(args, i, '--source-yes-pct'), '--source-yes-pct');
500
+ i += 1;
501
+ continue;
502
+ }
503
+ if (token === '--target-yes-pct') {
504
+ options.targetYesPct = parseProbabilityPercent(requireFlagValue(args, i, '--target-yes-pct'), '--target-yes-pct');
505
+ i += 1;
506
+ continue;
507
+ }
508
+ if (token === '--polymarket-yes-pct') {
509
+ options.polymarketYesPct = parseProbabilityPercent(requireFlagValue(args, i, '--polymarket-yes-pct'), '--polymarket-yes-pct');
510
+ i += 1;
511
+ continue;
512
+ }
513
+ if (token === '--distribution-yes') {
514
+ options.distributionYes = parseNonNegativeInteger(requireFlagValue(args, i, '--distribution-yes'), '--distribution-yes');
515
+ i += 1;
516
+ continue;
517
+ }
518
+ if (token === '--distribution-no') {
519
+ options.distributionNo = parseNonNegativeInteger(requireFlagValue(args, i, '--distribution-no'), '--distribution-no');
520
+ i += 1;
521
+ continue;
522
+ }
523
+ if (token === '--fee-tier') {
524
+ options.feeTier = parsePositiveInteger(requireFlagValue(args, i, '--fee-tier'), '--fee-tier');
525
+ i += 1;
526
+ continue;
527
+ }
528
+ if (token === '--hedge-ratio') {
529
+ options.hedgeRatio = parsePositiveNumber(requireFlagValue(args, i, '--hedge-ratio'), '--hedge-ratio');
530
+ i += 1;
531
+ continue;
532
+ }
533
+ if (token === '--hedge-cost-bps') {
534
+ options.hedgeCostBps = parseNonNegativeInteger(requireFlagValue(args, i, '--hedge-cost-bps'), '--hedge-cost-bps');
535
+ i += 1;
536
+ continue;
537
+ }
538
+ if (token === '--volume-scenarios') {
539
+ options.volumeScenarios = parseCsvNumberList(requireFlagValue(args, i, '--volume-scenarios'), '--volume-scenarios');
540
+ i += 1;
541
+ continue;
542
+ }
543
+ throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror simulate: ${token}`);
544
+ }
545
+
546
+ if (options.liquidityUsdc === null) {
547
+ throw new CliError('MISSING_REQUIRED_FLAG', 'mirror simulate requires --liquidity-usdc <n>.');
548
+ }
549
+ if (
550
+ (options.distributionYes === null && options.distributionNo !== null) ||
551
+ (options.distributionYes !== null && options.distributionNo === null)
552
+ ) {
553
+ throw new CliError('INVALID_ARGS', 'Provide both --distribution-yes and --distribution-no together.');
554
+ }
555
+ if (
556
+ options.distributionYes !== null &&
557
+ options.distributionNo !== null &&
558
+ options.distributionYes + options.distributionNo !== 1_000_000_000
559
+ ) {
560
+ throw new CliError('INVALID_ARGS', '--distribution-yes + --distribution-no must equal 1000000000.');
561
+ }
562
+ if (![500, 3000, 10000].includes(options.feeTier)) {
563
+ throw new CliError('INVALID_FLAG_VALUE', '--fee-tier must be one of 500, 3000, 10000.');
564
+ }
565
+ if (options.hedgeRatio > 5) {
566
+ throw new CliError('INVALID_FLAG_VALUE', '--hedge-ratio must be <= 5.');
567
+ }
568
+
569
+ return options;
570
+ };
571
+ }
572
+
573
+ module.exports = {
574
+ createParseMirrorBrowseFlags,
575
+ createParseMirrorVerifyFlags,
576
+ createParseMirrorStatusFlags,
577
+ createParseMirrorCloseFlags,
578
+ createParseMirrorLpExplainFlags,
579
+ createParseMirrorSimulateFlags,
580
+ };
@@ -462,19 +462,30 @@ function createRunSchemaCommand(deps) {
462
462
  }
463
463
 
464
464
  function runSchemaCommand(args, context) {
465
+ if (Array.isArray(args) && (args.includes('--help') || args.includes('-h'))) {
466
+ if (context.outputMode === 'json') {
467
+ emitSuccess(context.outputMode, 'schema.help', {
468
+ usage: 'pandora --output json schema',
469
+ });
470
+ } else {
471
+ // eslint-disable-next-line no-console
472
+ console.log('Usage: pandora --output json schema');
473
+ // eslint-disable-next-line no-console
474
+ console.log('');
475
+ // eslint-disable-next-line no-console
476
+ console.log('Notes:');
477
+ // eslint-disable-next-line no-console
478
+ console.log(' - schema payload is available only in --output json mode.');
479
+ }
480
+ return;
481
+ }
482
+
465
483
  if (context.outputMode !== 'json') {
466
484
  throw new CliError('INVALID_USAGE', 'The schema command is only supported in --output json mode.', {
467
485
  hints: ['Run `pandora --output json schema`'],
468
486
  });
469
487
  }
470
488
 
471
- if (Array.isArray(args) && (args.includes('--help') || args.includes('-h'))) {
472
- emitSuccess(context.outputMode, 'schema.help', {
473
- usage: 'pandora --output json schema',
474
- });
475
- return;
476
- }
477
-
478
489
  if (Array.isArray(args) && args.length > 0) {
479
490
  throw new CliError('INVALID_ARGS', 'schema does not accept additional flags or positional arguments.', {
480
491
  hints: ['Run `pandora --output json schema` without extra arguments.'],
package/cli/pandora.cjs CHANGED
@@ -19,6 +19,14 @@ const {
19
19
  createParseMirrorSyncFlags,
20
20
  createParseMirrorSyncDaemonSelectorFlags,
21
21
  } = require('./lib/parsers/mirror_sync_flags.cjs');
22
+ const {
23
+ createParseMirrorBrowseFlags,
24
+ createParseMirrorVerifyFlags,
25
+ createParseMirrorStatusFlags,
26
+ createParseMirrorCloseFlags,
27
+ createParseMirrorLpExplainFlags,
28
+ createParseMirrorSimulateFlags,
29
+ } = require('./lib/parsers/mirror_remaining_flags.cjs');
22
30
  const {
23
31
  createParsePolymarketSharedFlags,
24
32
  createParsePolymarketApproveFlags,
@@ -1114,295 +1122,6 @@ function runTargetScript(targetScript, passThroughArgs) {
1114
1122
  process.exit(result.status === null ? 1 : result.status);
1115
1123
  }
1116
1124
 
1117
- function parseMirrorBrowseFlags(args) {
1118
- const options = {
1119
- minYesPct: null,
1120
- maxYesPct: null,
1121
- minVolume24h: 0,
1122
- closesAfter: null,
1123
- closesBefore: null,
1124
- questionContains: null,
1125
- limit: 10,
1126
- chainId: null,
1127
- polymarketGammaUrl: null,
1128
- polymarketGammaMockUrl: null,
1129
- polymarketMockUrl: null,
1130
- };
1131
-
1132
- for (let i = 0; i < args.length; i += 1) {
1133
- const token = args[i];
1134
- if (token === '--min-yes-pct') {
1135
- options.minYesPct = parseProbabilityPercent(requireFlagValue(args, i, '--min-yes-pct'), '--min-yes-pct');
1136
- i += 1;
1137
- continue;
1138
- }
1139
- if (token === '--max-yes-pct') {
1140
- options.maxYesPct = parseProbabilityPercent(requireFlagValue(args, i, '--max-yes-pct'), '--max-yes-pct');
1141
- i += 1;
1142
- continue;
1143
- }
1144
- if (token === '--min-volume-24h') {
1145
- options.minVolume24h = parsePositiveNumber(requireFlagValue(args, i, '--min-volume-24h'), '--min-volume-24h');
1146
- i += 1;
1147
- continue;
1148
- }
1149
- if (token === '--closes-after') {
1150
- options.closesAfter = parseDateLikeFlag(requireFlagValue(args, i, '--closes-after'), '--closes-after');
1151
- i += 1;
1152
- continue;
1153
- }
1154
- if (token === '--closes-before') {
1155
- options.closesBefore = parseDateLikeFlag(requireFlagValue(args, i, '--closes-before'), '--closes-before');
1156
- i += 1;
1157
- continue;
1158
- }
1159
- if (token === '--question-contains') {
1160
- options.questionContains = requireFlagValue(args, i, '--question-contains');
1161
- i += 1;
1162
- continue;
1163
- }
1164
- if (token === '--limit') {
1165
- options.limit = parsePositiveInteger(requireFlagValue(args, i, '--limit'), '--limit');
1166
- i += 1;
1167
- continue;
1168
- }
1169
- if (token === '--chain-id') {
1170
- options.chainId = parseInteger(requireFlagValue(args, i, '--chain-id'), '--chain-id');
1171
- i += 1;
1172
- continue;
1173
- }
1174
- if (token === '--polymarket-gamma-url') {
1175
- options.polymarketGammaUrl = requireFlagValue(args, i, '--polymarket-gamma-url');
1176
- i += 1;
1177
- continue;
1178
- }
1179
- if (token === '--polymarket-gamma-mock-url') {
1180
- options.polymarketGammaMockUrl = requireFlagValue(args, i, '--polymarket-gamma-mock-url');
1181
- i += 1;
1182
- continue;
1183
- }
1184
- if (token === '--polymarket-mock-url') {
1185
- options.polymarketMockUrl = requireFlagValue(args, i, '--polymarket-mock-url');
1186
- i += 1;
1187
- continue;
1188
- }
1189
- throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror browse: ${token}`);
1190
- }
1191
-
1192
- if (options.minYesPct !== null && options.maxYesPct !== null && options.minYesPct > options.maxYesPct) {
1193
- throw new CliError('INVALID_ARGS', '--min-yes-pct cannot be greater than --max-yes-pct.');
1194
- }
1195
-
1196
- return options;
1197
- }
1198
-
1199
- function parseMirrorVerifyFlags(args) {
1200
- const options = {
1201
- pandoraMarketAddress: null,
1202
- polymarketMarketId: null,
1203
- polymarketSlug: null,
1204
- includeSimilarity: false,
1205
- withRules: false,
1206
- allowRuleMismatch: false,
1207
- trustDeploy: false,
1208
- manifestFile: null,
1209
- polymarketHost: null,
1210
- polymarketGammaUrl: null,
1211
- polymarketGammaMockUrl: null,
1212
- polymarketMockUrl: null,
1213
- };
1214
-
1215
- for (let i = 0; i < args.length; i += 1) {
1216
- const token = args[i];
1217
- if (token === '--pandora-market-address' || token === '--market-address') {
1218
- options.pandoraMarketAddress = parseAddressFlag(
1219
- requireFlagValue(args, i, token),
1220
- token,
1221
- );
1222
- i += 1;
1223
- continue;
1224
- }
1225
- if (token === '--polymarket-market-id') {
1226
- options.polymarketMarketId = requireFlagValue(args, i, '--polymarket-market-id');
1227
- i += 1;
1228
- continue;
1229
- }
1230
- if (token === '--polymarket-slug') {
1231
- options.polymarketSlug = requireFlagValue(args, i, '--polymarket-slug');
1232
- i += 1;
1233
- continue;
1234
- }
1235
- if (token === '--include-similarity') {
1236
- options.includeSimilarity = true;
1237
- continue;
1238
- }
1239
- if (token === '--with-rules') {
1240
- options.withRules = true;
1241
- continue;
1242
- }
1243
- if (token === '--allow-rule-mismatch') {
1244
- options.allowRuleMismatch = true;
1245
- continue;
1246
- }
1247
- if (token === '--trust-deploy') {
1248
- options.trustDeploy = true;
1249
- continue;
1250
- }
1251
- if (token === '--manifest-file') {
1252
- options.manifestFile = requireFlagValue(args, i, '--manifest-file');
1253
- i += 1;
1254
- continue;
1255
- }
1256
- if (token === '--polymarket-host') {
1257
- options.polymarketHost = requireFlagValue(args, i, '--polymarket-host');
1258
- i += 1;
1259
- continue;
1260
- }
1261
- if (token === '--polymarket-gamma-url') {
1262
- options.polymarketGammaUrl = requireFlagValue(args, i, '--polymarket-gamma-url');
1263
- i += 1;
1264
- continue;
1265
- }
1266
- if (token === '--polymarket-gamma-mock-url') {
1267
- options.polymarketGammaMockUrl = requireFlagValue(args, i, '--polymarket-gamma-mock-url');
1268
- i += 1;
1269
- continue;
1270
- }
1271
- if (token === '--polymarket-mock-url') {
1272
- options.polymarketMockUrl = requireFlagValue(args, i, '--polymarket-mock-url');
1273
- i += 1;
1274
- continue;
1275
- }
1276
- throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror verify: ${token}`);
1277
- }
1278
-
1279
- if (!options.pandoraMarketAddress) {
1280
- throw new CliError('MISSING_REQUIRED_FLAG', 'Missing --pandora-market-address <address> (alias: --market-address).');
1281
- }
1282
- if (!options.polymarketMarketId && !options.polymarketSlug) {
1283
- throw new CliError('MISSING_REQUIRED_FLAG', 'mirror verify requires --polymarket-market-id <id> or --polymarket-slug <slug>.');
1284
- }
1285
-
1286
- return options;
1287
- }
1288
-
1289
- function parseMirrorStatusFlags(args) {
1290
- const options = {
1291
- stateFile: null,
1292
- strategyHash: null,
1293
- withLive: false,
1294
- trustDeploy: false,
1295
- manifestFile: null,
1296
- pandoraMarketAddress: null,
1297
- polymarketMarketId: null,
1298
- polymarketSlug: null,
1299
- driftTriggerBps: 150,
1300
- hedgeTriggerUsdc: 10,
1301
- indexerUrl: null,
1302
- timeoutMs: DEFAULT_INDEXER_TIMEOUT_MS,
1303
- polymarketHost: null,
1304
- polymarketGammaUrl: null,
1305
- polymarketGammaMockUrl: null,
1306
- polymarketMockUrl: null,
1307
- };
1308
-
1309
- for (let i = 0; i < args.length; i += 1) {
1310
- const token = args[i];
1311
- if (token === '--state-file') {
1312
- options.stateFile = requireFlagValue(args, i, '--state-file');
1313
- i += 1;
1314
- continue;
1315
- }
1316
- if (token === '--strategy-hash') {
1317
- const value = requireFlagValue(args, i, '--strategy-hash');
1318
- if (!/^[a-f0-9]{16}$/i.test(value)) {
1319
- throw new CliError('INVALID_FLAG_VALUE', '--strategy-hash must be a 16-character hex value.');
1320
- }
1321
- options.strategyHash = value.toLowerCase();
1322
- i += 1;
1323
- continue;
1324
- }
1325
- if (token === '--with-live') {
1326
- options.withLive = true;
1327
- continue;
1328
- }
1329
- if (token === '--trust-deploy') {
1330
- options.trustDeploy = true;
1331
- continue;
1332
- }
1333
- if (token === '--manifest-file') {
1334
- options.manifestFile = requireFlagValue(args, i, '--manifest-file');
1335
- i += 1;
1336
- continue;
1337
- }
1338
- if (token === '--pandora-market-address' || token === '--market-address') {
1339
- options.pandoraMarketAddress = parseAddressFlag(
1340
- requireFlagValue(args, i, token),
1341
- token,
1342
- );
1343
- i += 1;
1344
- continue;
1345
- }
1346
- if (token === '--polymarket-market-id') {
1347
- options.polymarketMarketId = requireFlagValue(args, i, '--polymarket-market-id');
1348
- i += 1;
1349
- continue;
1350
- }
1351
- if (token === '--polymarket-slug') {
1352
- options.polymarketSlug = requireFlagValue(args, i, '--polymarket-slug');
1353
- i += 1;
1354
- continue;
1355
- }
1356
- if (token === '--drift-trigger-bps') {
1357
- options.driftTriggerBps = parsePositiveInteger(requireFlagValue(args, i, '--drift-trigger-bps'), '--drift-trigger-bps');
1358
- i += 1;
1359
- continue;
1360
- }
1361
- if (token === '--hedge-trigger-usdc') {
1362
- options.hedgeTriggerUsdc = parsePositiveNumber(requireFlagValue(args, i, '--hedge-trigger-usdc'), '--hedge-trigger-usdc');
1363
- i += 1;
1364
- continue;
1365
- }
1366
- if (token === '--indexer-url') {
1367
- options.indexerUrl = requireFlagValue(args, i, '--indexer-url');
1368
- i += 1;
1369
- continue;
1370
- }
1371
- if (token === '--timeout-ms') {
1372
- options.timeoutMs = parsePositiveInteger(requireFlagValue(args, i, '--timeout-ms'), '--timeout-ms');
1373
- i += 1;
1374
- continue;
1375
- }
1376
- if (token === '--polymarket-host') {
1377
- options.polymarketHost = requireFlagValue(args, i, '--polymarket-host');
1378
- i += 1;
1379
- continue;
1380
- }
1381
- if (token === '--polymarket-gamma-url') {
1382
- options.polymarketGammaUrl = requireFlagValue(args, i, '--polymarket-gamma-url');
1383
- i += 1;
1384
- continue;
1385
- }
1386
- if (token === '--polymarket-gamma-mock-url') {
1387
- options.polymarketGammaMockUrl = requireFlagValue(args, i, '--polymarket-gamma-mock-url');
1388
- i += 1;
1389
- continue;
1390
- }
1391
- if (token === '--polymarket-mock-url') {
1392
- options.polymarketMockUrl = requireFlagValue(args, i, '--polymarket-mock-url');
1393
- i += 1;
1394
- continue;
1395
- }
1396
- throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror status: ${token}`);
1397
- }
1398
-
1399
- if (!options.stateFile && !options.strategyHash) {
1400
- throw new CliError('MISSING_REQUIRED_FLAG', 'mirror status requires --state-file <path> or --strategy-hash <hash>.');
1401
- }
1402
-
1403
- return options;
1404
- }
1405
-
1406
1125
  function buildMirrorSyncStrategy(options) {
1407
1126
  return {
1408
1127
  mode: options.mode,
@@ -1504,222 +1223,6 @@ function buildMirrorSyncDaemonCliArgs(options, shared) {
1504
1223
  return args;
1505
1224
  }
1506
1225
 
1507
- function parseMirrorCloseFlags(args) {
1508
- const options = {
1509
- pandoraMarketAddress: null,
1510
- polymarketMarketId: null,
1511
- polymarketSlug: null,
1512
- execute: false,
1513
- dryRun: false,
1514
- };
1515
-
1516
- for (let i = 0; i < args.length; i += 1) {
1517
- const token = args[i];
1518
- if (token === '--pandora-market-address' || token === '--market-address') {
1519
- options.pandoraMarketAddress = parseAddressFlag(
1520
- requireFlagValue(args, i, token),
1521
- token,
1522
- );
1523
- i += 1;
1524
- continue;
1525
- }
1526
- if (token === '--polymarket-market-id') {
1527
- options.polymarketMarketId = requireFlagValue(args, i, '--polymarket-market-id');
1528
- i += 1;
1529
- continue;
1530
- }
1531
- if (token === '--polymarket-slug') {
1532
- options.polymarketSlug = requireFlagValue(args, i, '--polymarket-slug');
1533
- i += 1;
1534
- continue;
1535
- }
1536
- if (token === '--dry-run') {
1537
- options.dryRun = true;
1538
- continue;
1539
- }
1540
- if (token === '--execute') {
1541
- options.execute = true;
1542
- continue;
1543
- }
1544
- throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror close: ${token}`);
1545
- }
1546
-
1547
- if (!options.pandoraMarketAddress) {
1548
- throw new CliError('MISSING_REQUIRED_FLAG', 'Missing --pandora-market-address <address> (alias: --market-address).');
1549
- }
1550
- if (!options.polymarketMarketId && !options.polymarketSlug) {
1551
- throw new CliError('MISSING_REQUIRED_FLAG', 'mirror close requires --polymarket-market-id <id> or --polymarket-slug <slug>.');
1552
- }
1553
- if (options.dryRun === options.execute) {
1554
- throw new CliError('INVALID_ARGS', 'mirror close requires exactly one mode: --dry-run or --execute.');
1555
- }
1556
-
1557
- return options;
1558
- }
1559
-
1560
- function parseMirrorLpExplainFlags(args) {
1561
- const options = {
1562
- liquidityUsdc: null,
1563
- sourceYesPct: null,
1564
- distributionYes: null,
1565
- distributionNo: null,
1566
- };
1567
-
1568
- for (let i = 0; i < args.length; i += 1) {
1569
- const token = args[i];
1570
- if (token === '--liquidity-usdc') {
1571
- options.liquidityUsdc = parsePositiveNumber(requireFlagValue(args, i, '--liquidity-usdc'), '--liquidity-usdc');
1572
- i += 1;
1573
- continue;
1574
- }
1575
- if (token === '--source-yes-pct') {
1576
- options.sourceYesPct = parseProbabilityPercent(requireFlagValue(args, i, '--source-yes-pct'), '--source-yes-pct');
1577
- i += 1;
1578
- continue;
1579
- }
1580
- if (token === '--distribution-yes') {
1581
- options.distributionYes = parseNonNegativeInteger(
1582
- requireFlagValue(args, i, '--distribution-yes'),
1583
- '--distribution-yes',
1584
- );
1585
- i += 1;
1586
- continue;
1587
- }
1588
- if (token === '--distribution-no') {
1589
- options.distributionNo = parseNonNegativeInteger(
1590
- requireFlagValue(args, i, '--distribution-no'),
1591
- '--distribution-no',
1592
- );
1593
- i += 1;
1594
- continue;
1595
- }
1596
- throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror lp-explain: ${token}`);
1597
- }
1598
-
1599
- if (options.liquidityUsdc === null) {
1600
- throw new CliError('MISSING_REQUIRED_FLAG', 'mirror lp-explain requires --liquidity-usdc <n>.');
1601
- }
1602
- if (
1603
- (options.distributionYes === null && options.distributionNo !== null) ||
1604
- (options.distributionYes !== null && options.distributionNo === null)
1605
- ) {
1606
- throw new CliError('INVALID_ARGS', 'Provide both --distribution-yes and --distribution-no together.');
1607
- }
1608
- if (
1609
- options.distributionYes !== null &&
1610
- options.distributionNo !== null &&
1611
- options.distributionYes + options.distributionNo !== 1_000_000_000
1612
- ) {
1613
- throw new CliError('INVALID_ARGS', '--distribution-yes + --distribution-no must equal 1000000000.');
1614
- }
1615
-
1616
- return options;
1617
- }
1618
-
1619
- function parseMirrorSimulateFlags(args) {
1620
- const options = {
1621
- liquidityUsdc: null,
1622
- sourceYesPct: null,
1623
- targetYesPct: null,
1624
- polymarketYesPct: null,
1625
- distributionYes: null,
1626
- distributionNo: null,
1627
- feeTier: 3000,
1628
- hedgeRatio: 1,
1629
- hedgeCostBps: 35,
1630
- volumeScenarios: null,
1631
- };
1632
-
1633
- for (let i = 0; i < args.length; i += 1) {
1634
- const token = args[i];
1635
- if (token === '--liquidity-usdc') {
1636
- options.liquidityUsdc = parsePositiveNumber(requireFlagValue(args, i, '--liquidity-usdc'), '--liquidity-usdc');
1637
- i += 1;
1638
- continue;
1639
- }
1640
- if (token === '--source-yes-pct') {
1641
- options.sourceYesPct = parseProbabilityPercent(requireFlagValue(args, i, '--source-yes-pct'), '--source-yes-pct');
1642
- i += 1;
1643
- continue;
1644
- }
1645
- if (token === '--target-yes-pct') {
1646
- options.targetYesPct = parseProbabilityPercent(requireFlagValue(args, i, '--target-yes-pct'), '--target-yes-pct');
1647
- i += 1;
1648
- continue;
1649
- }
1650
- if (token === '--polymarket-yes-pct') {
1651
- options.polymarketYesPct = parseProbabilityPercent(
1652
- requireFlagValue(args, i, '--polymarket-yes-pct'),
1653
- '--polymarket-yes-pct',
1654
- );
1655
- i += 1;
1656
- continue;
1657
- }
1658
- if (token === '--distribution-yes') {
1659
- options.distributionYes = parseNonNegativeInteger(
1660
- requireFlagValue(args, i, '--distribution-yes'),
1661
- '--distribution-yes',
1662
- );
1663
- i += 1;
1664
- continue;
1665
- }
1666
- if (token === '--distribution-no') {
1667
- options.distributionNo = parseNonNegativeInteger(
1668
- requireFlagValue(args, i, '--distribution-no'),
1669
- '--distribution-no',
1670
- );
1671
- i += 1;
1672
- continue;
1673
- }
1674
- if (token === '--fee-tier') {
1675
- options.feeTier = parsePositiveInteger(requireFlagValue(args, i, '--fee-tier'), '--fee-tier');
1676
- i += 1;
1677
- continue;
1678
- }
1679
- if (token === '--hedge-ratio') {
1680
- options.hedgeRatio = parsePositiveNumber(requireFlagValue(args, i, '--hedge-ratio'), '--hedge-ratio');
1681
- i += 1;
1682
- continue;
1683
- }
1684
- if (token === '--hedge-cost-bps') {
1685
- options.hedgeCostBps = parseNonNegativeInteger(requireFlagValue(args, i, '--hedge-cost-bps'), '--hedge-cost-bps');
1686
- i += 1;
1687
- continue;
1688
- }
1689
- if (token === '--volume-scenarios') {
1690
- options.volumeScenarios = parseCsvNumberList(requireFlagValue(args, i, '--volume-scenarios'), '--volume-scenarios');
1691
- i += 1;
1692
- continue;
1693
- }
1694
- throw new CliError('UNKNOWN_FLAG', `Unknown flag for mirror simulate: ${token}`);
1695
- }
1696
-
1697
- if (options.liquidityUsdc === null) {
1698
- throw new CliError('MISSING_REQUIRED_FLAG', 'mirror simulate requires --liquidity-usdc <n>.');
1699
- }
1700
- if (
1701
- (options.distributionYes === null && options.distributionNo !== null) ||
1702
- (options.distributionYes !== null && options.distributionNo === null)
1703
- ) {
1704
- throw new CliError('INVALID_ARGS', 'Provide both --distribution-yes and --distribution-no together.');
1705
- }
1706
- if (
1707
- options.distributionYes !== null &&
1708
- options.distributionNo !== null &&
1709
- options.distributionYes + options.distributionNo !== 1_000_000_000
1710
- ) {
1711
- throw new CliError('INVALID_ARGS', '--distribution-yes + --distribution-no must equal 1000000000.');
1712
- }
1713
- if (![500, 3000, 10000].includes(options.feeTier)) {
1714
- throw new CliError('INVALID_FLAG_VALUE', '--fee-tier must be one of 500, 3000, 10000.');
1715
- }
1716
- if (options.hedgeRatio > 5) {
1717
- throw new CliError('INVALID_FLAG_VALUE', '--hedge-ratio must be <= 5.');
1718
- }
1719
-
1720
- return options;
1721
- }
1722
-
1723
1226
  async function buildDoctorReport(options) {
1724
1227
  return getDoctorServiceInstance().buildDoctorReport(options);
1725
1228
  }
@@ -4674,6 +4177,21 @@ const parseMirrorSyncDaemonSelectorFlagsFromModule = createParseMirrorSyncDaemon
4674
4177
  CliError,
4675
4178
  requireFlagValue,
4676
4179
  });
4180
+ const parseMirrorBrowseFlagsFromModule = createParseMirrorBrowseFlags({
4181
+ ...sharedParserDeps,
4182
+ parseDateLikeFlag,
4183
+ });
4184
+ const parseMirrorVerifyFlagsFromModule = createParseMirrorVerifyFlags(sharedParserDeps);
4185
+ const parseMirrorStatusFlagsFromModule = createParseMirrorStatusFlags({
4186
+ ...sharedParserDeps,
4187
+ defaultIndexerTimeoutMs: DEFAULT_INDEXER_TIMEOUT_MS,
4188
+ });
4189
+ const parseMirrorCloseFlagsFromModule = createParseMirrorCloseFlags(sharedParserDeps);
4190
+ const parseMirrorLpExplainFlagsFromModule = createParseMirrorLpExplainFlags(sharedParserDeps);
4191
+ const parseMirrorSimulateFlagsFromModule = createParseMirrorSimulateFlags({
4192
+ ...sharedParserDeps,
4193
+ parseCsvNumberList,
4194
+ });
4677
4195
  const parsePolymarketSharedFlagsFromModule = createParsePolymarketSharedFlags({
4678
4196
  CliError,
4679
4197
  requireFlagValue,
@@ -5251,18 +4769,18 @@ const runMirrorCommand = createRunMirrorCommand({
5251
4769
  maybeLoadIndexerEnv,
5252
4770
  maybeLoadTradeEnv,
5253
4771
  resolveIndexerUrl,
5254
- parseMirrorBrowseFlags,
4772
+ parseMirrorBrowseFlags: parseMirrorBrowseFlagsFromModule,
5255
4773
  parseMirrorPlanFlags: parseMirrorPlanFlagsFromModule,
5256
4774
  parseMirrorDeployFlags: parseMirrorDeployFlagsFromModule,
5257
- parseMirrorVerifyFlags,
5258
- parseMirrorStatusFlags,
4775
+ parseMirrorVerifyFlags: parseMirrorVerifyFlagsFromModule,
4776
+ parseMirrorStatusFlags: parseMirrorStatusFlagsFromModule,
5259
4777
  parseMirrorSyncFlags: parseMirrorSyncFlagsFromModule,
5260
4778
  parseMirrorSyncDaemonSelectorFlags: parseMirrorSyncDaemonSelectorFlagsFromModule,
5261
4779
  parseMirrorGoFlags: parseMirrorGoFlagsFromModule,
5262
- parseMirrorCloseFlags,
5263
- parseMirrorLpExplainFlags,
4780
+ parseMirrorCloseFlags: parseMirrorCloseFlagsFromModule,
4781
+ parseMirrorLpExplainFlags: parseMirrorLpExplainFlagsFromModule,
5264
4782
  parseMirrorHedgeCalcFlags: parseMirrorHedgeCalcFlagsFromModule,
5265
- parseMirrorSimulateFlags,
4783
+ parseMirrorSimulateFlags: parseMirrorSimulateFlagsFromModule,
5266
4784
  buildMirrorPlan,
5267
4785
  deployMirror,
5268
4786
  verifyMirror,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pandora-cli-skills",
3
- "version": "1.1.36",
3
+ "version": "1.1.37",
4
4
  "description": "Pandora CLI & Skills",
5
5
  "main": "cli/pandora.cjs",
6
6
  "bin": {
@@ -1057,6 +1057,12 @@ test('schema command requires --output json mode', () => {
1057
1057
  assert.match(result.output, /only supported in --output json mode/i);
1058
1058
  });
1059
1059
 
1060
+ test('schema --help succeeds in table mode', () => {
1061
+ const result = runCli(['schema', '--help']);
1062
+ assert.equal(result.status, 0);
1063
+ assert.match(String(result.stdout || ''), /Usage:\s+pandora --output json schema/);
1064
+ });
1065
+
1060
1066
  test('schema command returns envelope schema plus command descriptors', () => {
1061
1067
  const result = runCli(['--output', 'json', 'schema']);
1062
1068
  assert.equal(result.status, 0);
@@ -46,6 +46,7 @@ const {
46
46
  const { runMirrorSync } = require('../../cli/lib/mirror_sync_service.cjs');
47
47
  const { createRunMirrorCommand } = require('../../cli/lib/mirror_command_service.cjs');
48
48
  const { resolveForkRuntime } = require('../../cli/lib/fork_runtime_service.cjs');
49
+ const { createErrorRecoveryService } = require('../../cli/lib/error_recovery_service.cjs');
49
50
  const { createParseTradeFlags } = require('../../cli/lib/parsers/trade_flags.cjs');
50
51
  const { createParseWatchFlags } = require('../../cli/lib/parsers/watch_flags.cjs');
51
52
  const { createParseAutopilotFlags } = require('../../cli/lib/parsers/autopilot_flags.cjs');
@@ -1929,3 +1930,74 @@ test('createRunMirrorCommand sync help reports run|once|start usage and daemon s
1929
1930
  assert.match(payload.daemonLifecycle.stop, /--pid-file <path>\|--strategy-hash <hash>/);
1930
1931
  assert.match(payload.daemonLifecycle.status, /--pid-file <path>\|--strategy-hash <hash>/);
1931
1932
  });
1933
+
1934
+ test('error recovery service returns hints for all mapped codes', () => {
1935
+ const recovery = createErrorRecoveryService({ cliName: 'pandora' });
1936
+ const mappedCodes = [
1937
+ 'TRADE_RISK_GUARD',
1938
+ 'ALLOWANCE_READ_FAILED',
1939
+ 'APPROVE_SIMULATION_FAILED',
1940
+ 'APPROVE_EXECUTION_FAILED',
1941
+ 'TRADE_EXECUTION_FAILED',
1942
+ 'POLYMARKET_APPROVE_FAILED',
1943
+ 'POLYMARKET_PROXY_APPROVAL_REQUIRES_MANUAL_EXECUTION',
1944
+ 'POLYMARKET_TRADE_FAILED',
1945
+ 'POLYMARKET_PREFLIGHT_FAILED',
1946
+ 'POLYMARKET_CHECK_FAILED',
1947
+ 'POLYMARKET_MARKET_RESOLUTION_FAILED',
1948
+ 'MIRROR_DEPLOY_FAILED',
1949
+ 'MIRROR_SYNC_FAILED',
1950
+ 'MCP_EXECUTE_INTENT_REQUIRED',
1951
+ 'MCP_LONG_RUNNING_MODE_BLOCKED',
1952
+ 'MCP_TOOL_FAILED',
1953
+ 'UNKNOWN_TOOL',
1954
+ 'MISSING_REQUIRED_FLAG',
1955
+ 'MISSING_FLAG_VALUE',
1956
+ 'INVALID_FLAG_VALUE',
1957
+ 'UNKNOWN_FLAG',
1958
+ 'INVALID_ARGS',
1959
+ 'INVALID_USAGE',
1960
+ 'INVALID_OUTPUT_MODE',
1961
+ 'UNSUPPORTED_OUTPUT_MODE',
1962
+ 'UNKNOWN_COMMAND',
1963
+ ];
1964
+
1965
+ for (const code of mappedCodes) {
1966
+ const result = recovery.getRecoveryForError({ code });
1967
+ assert.equal(Boolean(result), true, `Expected recovery hint for ${code}`);
1968
+ assert.equal(typeof result.action, 'string');
1969
+ assert.equal(result.action.length > 0, true);
1970
+ assert.equal(typeof result.command, 'string');
1971
+ assert.equal(result.command.length > 0, true);
1972
+ assert.equal(typeof result.retryable, 'boolean');
1973
+ }
1974
+ });
1975
+
1976
+ test('error recovery service falls through to null for unmapped codes', () => {
1977
+ const recovery = createErrorRecoveryService({ cliName: 'pandora' });
1978
+ assert.equal(recovery.getRecoveryForError({ code: 'SOME_NEW_CODE' }), null);
1979
+ assert.equal(recovery.getRecoveryForError({ code: '' }), null);
1980
+ assert.equal(recovery.getRecoveryForError(null), null);
1981
+ });
1982
+
1983
+ test('error recovery service builds deterministic command hints for key flows', () => {
1984
+ const recovery = createErrorRecoveryService({ cliName: 'pandora' });
1985
+
1986
+ const tradeRetry = recovery.getRecoveryForError({
1987
+ code: 'TRADE_RISK_GUARD',
1988
+ details: {
1989
+ marketAddress: TEST_MARKET,
1990
+ side: 'no',
1991
+ amountUsdc: 12.5,
1992
+ },
1993
+ });
1994
+ assert.equal(tradeRetry.command.includes(`--market-address ${TEST_MARKET}`), true);
1995
+ assert.equal(tradeRetry.command.includes('--side no'), true);
1996
+ assert.equal(tradeRetry.command.includes('--amount-usdc 12.5'), true);
1997
+
1998
+ const mirrorSyncRetry = recovery.getRecoveryForError({ code: 'MIRROR_SYNC_FAILED' });
1999
+ assert.equal(mirrorSyncRetry.command, 'pandora mirror sync once --paper --pandora-market-address <address> --polymarket-market-id <id>');
2000
+
2001
+ const mcpRetry = recovery.getRecoveryForError({ code: 'MCP_EXECUTE_INTENT_REQUIRED' });
2002
+ assert.equal(mcpRetry.command, 'pandora mcp');
2003
+ });