@spfunctions/cli 1.7.17 → 1.7.19
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/dist/commands/agent.js +4 -4
- package/dist/commands/balance.js +1 -1
- package/dist/commands/book.js +1 -1
- package/dist/commands/edges.js +15 -6
- package/dist/commands/explore.js +2 -1
- package/dist/commands/fills.js +1 -1
- package/dist/commands/list.js +1 -1
- package/dist/commands/login.d.ts +1 -0
- package/dist/commands/login.js +4 -4
- package/dist/commands/positions.js +2 -2
- package/dist/commands/scan.js +1 -1
- package/dist/config.js +2 -2
- package/dist/index.js +26 -6
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +22 -0
- package/package.json +2 -1
package/dist/commands/agent.js
CHANGED
|
@@ -319,7 +319,7 @@ function createFooterBar(piTui) {
|
|
|
319
319
|
const tokStr = this.tokens >= 1000 ? `${(this.tokens / 1000).toFixed(1)}k` : `${this.tokens}`;
|
|
320
320
|
const tokens = C.zinc600(`${tokStr} tok`);
|
|
321
321
|
const exchange = this.exchangeOpen === true ? C.emerald('OPEN') : this.exchangeOpen === false ? C.red('CLOSED') : C.zinc600('...');
|
|
322
|
-
const trading = this.tradingEnabled ? C.amber('
|
|
322
|
+
const trading = this.tradingEnabled ? C.amber('trading') : C.zinc600('read-only');
|
|
323
323
|
const help = C.zinc600('/help');
|
|
324
324
|
const leftText = [model, tokens, exchange, trading].join(sep);
|
|
325
325
|
const lw = visibleWidth(leftText);
|
|
@@ -2207,7 +2207,7 @@ ${topEdges}
|
|
|
2207
2207
|
}
|
|
2208
2208
|
}
|
|
2209
2209
|
if (event.type === 'tool_execution_start') {
|
|
2210
|
-
const toolLine = new MutableLine(C.zinc600(` \
|
|
2210
|
+
const toolLine = new MutableLine(C.zinc600(` \u25B8 ${event.toolName}...`));
|
|
2211
2211
|
toolStartTimes.set(event.toolCallId || event.toolName, Date.now());
|
|
2212
2212
|
toolLines.set(event.toolCallId || event.toolName, toolLine);
|
|
2213
2213
|
chatContainer.addChild(toolLine);
|
|
@@ -2226,7 +2226,7 @@ ${topEdges}
|
|
|
2226
2226
|
line.setText(C.red(` \u2717 ${event.toolName} (${elapsed}s) error`));
|
|
2227
2227
|
}
|
|
2228
2228
|
else {
|
|
2229
|
-
line.setText(C.zinc600(` \
|
|
2229
|
+
line.setText(C.zinc600(` \u25B8 ${event.toolName} `) + C.emerald(`\u2713`) + C.zinc600(` ${elapsed}s`));
|
|
2230
2230
|
}
|
|
2231
2231
|
}
|
|
2232
2232
|
toolStartTimes.delete(key);
|
|
@@ -4016,7 +4016,7 @@ ${ctx.lastEvaluation?.summary ? `Latest eval: ${ctx.lastEvaluation.summary.slice
|
|
|
4016
4016
|
}
|
|
4017
4017
|
}
|
|
4018
4018
|
if (event.type === 'tool_execution_start') {
|
|
4019
|
-
process.stderr.write(` \
|
|
4019
|
+
process.stderr.write(` \u25B8 ${event.toolName}...\n`);
|
|
4020
4020
|
}
|
|
4021
4021
|
if (event.type === 'tool_execution_end') {
|
|
4022
4022
|
const status = event.isError ? '\u2717' : '\u2713';
|
package/dist/commands/balance.js
CHANGED
|
@@ -6,7 +6,7 @@ const utils_js_1 = require("../utils.js");
|
|
|
6
6
|
async function balanceCommand(opts) {
|
|
7
7
|
const result = await (0, kalshi_js_1.getBalance)();
|
|
8
8
|
if (!result)
|
|
9
|
-
throw new Error('Kalshi not configured.
|
|
9
|
+
throw new Error('Kalshi not configured. Run: sf setup --kalshi');
|
|
10
10
|
if (opts.json) {
|
|
11
11
|
console.log(JSON.stringify(result, null, 2));
|
|
12
12
|
return;
|
package/dist/commands/book.js
CHANGED
|
@@ -152,7 +152,7 @@ async function bookCommand(tickers, opts) {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
if (results.length === 0) {
|
|
155
|
-
console.log(
|
|
155
|
+
console.log(`\n ${utils_js_1.c.dim}No markets found. Check the ticker and try again.${utils_js_1.c.reset}\n`);
|
|
156
156
|
return;
|
|
157
157
|
}
|
|
158
158
|
// ── JSON output ──
|
package/dist/commands/edges.js
CHANGED
|
@@ -28,23 +28,28 @@ async function edgesCommand(opts) {
|
|
|
28
28
|
const liqRank = { a: 4, high: 4, b: 3, medium: 3, c: 2, low: 2, d: 1 };
|
|
29
29
|
// ── Step 1: Fetch theses (or single thesis) ────────────────────────────────
|
|
30
30
|
let theses;
|
|
31
|
+
const log = opts.json ? (() => { }) : (msg) => console.log(msg);
|
|
31
32
|
if (opts.thesis) {
|
|
32
33
|
// Single thesis mode — skip listing, fetch context directly
|
|
33
|
-
|
|
34
|
+
log(`${utils_js_1.c.dim}Fetching edges for thesis ${opts.thesis}...${utils_js_1.c.reset}`);
|
|
34
35
|
theses = [{ id: opts.thesis }];
|
|
35
36
|
}
|
|
36
37
|
else {
|
|
37
|
-
|
|
38
|
+
log(`${utils_js_1.c.dim}Fetching theses...${utils_js_1.c.reset}`);
|
|
38
39
|
const data = await client.listTheses();
|
|
39
40
|
const rawTheses = data.theses || data;
|
|
40
41
|
theses = (Array.isArray(rawTheses) ? rawTheses : []).filter((t) => t.status === 'active');
|
|
41
42
|
}
|
|
42
43
|
if (theses.length === 0) {
|
|
43
|
-
|
|
44
|
+
if (opts.json) {
|
|
45
|
+
console.log(JSON.stringify({ edges: [], totalEdges: 0 }));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
(0, utils_js_1.emptyState)('active theses', 'Create one: sf create "your market thesis"');
|
|
44
49
|
return;
|
|
45
50
|
}
|
|
46
51
|
// ── Step 2: Fetch context for each thesis (parallel) ───────────────────────
|
|
47
|
-
|
|
52
|
+
log(`${utils_js_1.c.dim}Fetching edges from ${theses.length} ${theses.length === 1 ? 'thesis' : 'theses'}...${utils_js_1.c.reset}`);
|
|
48
53
|
const allEdges = [];
|
|
49
54
|
const contextPromises = theses.map(async (t) => {
|
|
50
55
|
try {
|
|
@@ -75,7 +80,11 @@ async function edgesCommand(opts) {
|
|
|
75
80
|
}
|
|
76
81
|
}
|
|
77
82
|
if (allEdges.length === 0) {
|
|
78
|
-
|
|
83
|
+
if (opts.json) {
|
|
84
|
+
console.log(JSON.stringify({ edges: [], totalEdges: 0, thesesScanned: theses.length }));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
(0, utils_js_1.emptyState)('edges', `Scanned ${theses.length} theses. Edges appear when thesis price diverges from market price.`);
|
|
79
88
|
return;
|
|
80
89
|
}
|
|
81
90
|
// ── Step 3: Dedupe by marketId — keep highest absolute edge ────────────────
|
|
@@ -100,7 +109,7 @@ async function edgesCommand(opts) {
|
|
|
100
109
|
// ── Step 4: Fetch positions (optional) ─────────────────────────────────────
|
|
101
110
|
let positions = null;
|
|
102
111
|
if ((0, kalshi_js_1.isKalshiConfigured)()) {
|
|
103
|
-
|
|
112
|
+
log(`${utils_js_1.c.dim}Fetching Kalshi positions...${utils_js_1.c.reset}`);
|
|
104
113
|
positions = await (0, kalshi_js_1.getPositions)();
|
|
105
114
|
if (positions) {
|
|
106
115
|
// Enrich with live prices
|
package/dist/commands/explore.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.exploreCommand = exploreCommand;
|
|
14
14
|
const share_js_1 = require("../share.js");
|
|
15
|
+
const utils_js_1 = require("../utils.js");
|
|
15
16
|
const BASE_URL = 'https://simplefunctions.dev';
|
|
16
17
|
async function exploreCommand(slug, opts) {
|
|
17
18
|
if (!slug) {
|
|
@@ -32,7 +33,7 @@ async function exploreCommand(slug, opts) {
|
|
|
32
33
|
}
|
|
33
34
|
console.log('\n Public Theses\n');
|
|
34
35
|
if (theses.length === 0) {
|
|
35
|
-
console.log(
|
|
36
|
+
console.log(` ${utils_js_1.c.dim}No public theses yet. Publish yours: sf publish <id>${utils_js_1.c.reset}\n`);
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
39
|
for (const t of theses) {
|
package/dist/commands/fills.js
CHANGED
|
@@ -6,7 +6,7 @@ const utils_js_1 = require("../utils.js");
|
|
|
6
6
|
async function fillsCommand(opts) {
|
|
7
7
|
const result = await (0, kalshi_js_1.getFills)({ ticker: opts.ticker, limit: 50 });
|
|
8
8
|
if (!result)
|
|
9
|
-
throw new Error('Kalshi not configured.
|
|
9
|
+
throw new Error('Kalshi not configured. Run: sf setup --kalshi');
|
|
10
10
|
if (opts.json) {
|
|
11
11
|
console.log(JSON.stringify(result.fills, null, 2));
|
|
12
12
|
return;
|
package/dist/commands/list.js
CHANGED
|
@@ -11,7 +11,7 @@ async function listCommand(opts) {
|
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
13
|
if (theses.length === 0) {
|
|
14
|
-
|
|
14
|
+
(0, utils_js_1.emptyState)('theses', 'Create one: sf create "your market thesis"');
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
console.log(`\n${utils_js_1.c.bold}` +
|
package/dist/commands/login.d.ts
CHANGED
package/dist/commands/login.js
CHANGED
|
@@ -13,11 +13,11 @@ const utils_js_1 = require("../utils.js");
|
|
|
13
13
|
const SF_API_URL = process.env.SF_API_URL || 'https://simplefunctions.dev';
|
|
14
14
|
async function loginCommand(opts) {
|
|
15
15
|
const apiUrl = opts.apiUrl || (0, config_js_1.loadConfig)().apiUrl || SF_API_URL;
|
|
16
|
-
//
|
|
16
|
+
// If already configured, re-login replaces the existing key
|
|
17
17
|
const fileConfig = (0, config_js_1.loadFileConfig)();
|
|
18
|
-
if (fileConfig.apiKey) {
|
|
19
|
-
console.log(`\n ${utils_js_1.c.dim}Already
|
|
20
|
-
console.log(` ${utils_js_1.c.dim}Run ${utils_js_1.c.cyan}sf
|
|
18
|
+
if (fileConfig.apiKey && !opts.force) {
|
|
19
|
+
console.log(`\n ${utils_js_1.c.dim}Already logged in (${fileConfig.apiKey.slice(0, 12)}...).${utils_js_1.c.reset}`);
|
|
20
|
+
console.log(` ${utils_js_1.c.dim}Run ${utils_js_1.c.cyan}sf login --force${utils_js_1.c.dim} to re-authenticate, or ${utils_js_1.c.cyan}sf logout${utils_js_1.c.dim} to clear.${utils_js_1.c.reset}\n`);
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
// Generate session token
|
|
@@ -180,10 +180,10 @@ async function positionsCommand(opts) {
|
|
|
180
180
|
console.log('');
|
|
181
181
|
}
|
|
182
182
|
else if ((0, kalshi_js_1.isKalshiConfigured)()) {
|
|
183
|
-
console.log(`\n${utils_js_1.c.dim}No open positions
|
|
183
|
+
console.log(`\n ${utils_js_1.c.dim}No open Kalshi positions.${utils_js_1.c.reset}\n`);
|
|
184
184
|
}
|
|
185
185
|
else {
|
|
186
|
-
console.log(`\n${utils_js_1.c.
|
|
186
|
+
console.log(`\n ${utils_js_1.c.dim}Kalshi not configured. Run: ${utils_js_1.c.cyan}sf setup --kalshi${utils_js_1.c.reset}\n`);
|
|
187
187
|
}
|
|
188
188
|
// C) Polymarket positions
|
|
189
189
|
if (polyPositions.length > 0) {
|
package/dist/commands/scan.js
CHANGED
|
@@ -194,7 +194,7 @@ async function keywordScan(query, json, venue = 'all', share) {
|
|
|
194
194
|
console.log('');
|
|
195
195
|
// Conversion hook
|
|
196
196
|
if (!json) {
|
|
197
|
-
console.log(`${utils_js_1.c.dim}Want 24/7 monitoring + edge detection? ${utils_js_1.c.reset}${utils_js_1.c.cyan}sf create${utils_js_1.c.reset}${utils_js_1.c.dim} "
|
|
197
|
+
console.log(`${utils_js_1.c.dim}Want 24/7 monitoring + edge detection? ${utils_js_1.c.reset}${utils_js_1.c.cyan}sf create${utils_js_1.c.reset}${utils_js_1.c.dim} "${query.slice(0, 50)}"${utils_js_1.c.reset}`);
|
|
198
198
|
console.log('');
|
|
199
199
|
}
|
|
200
200
|
}
|
package/dist/config.js
CHANGED
|
@@ -126,11 +126,11 @@ function isConfigured() {
|
|
|
126
126
|
function requireTrading() {
|
|
127
127
|
const config = loadConfig();
|
|
128
128
|
if (!config.tradingEnabled) {
|
|
129
|
-
console.error('\n
|
|
129
|
+
console.error('\n Trading is disabled. Run: sf setup --enable-trading\n');
|
|
130
130
|
process.exit(1);
|
|
131
131
|
}
|
|
132
132
|
if (!config.kalshiKeyId && !process.env.KALSHI_API_KEY_ID) {
|
|
133
|
-
console.error('\n
|
|
133
|
+
console.error('\n Kalshi API key not configured. Run: sf setup --kalshi\n');
|
|
134
134
|
process.exit(1);
|
|
135
135
|
}
|
|
136
136
|
}
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,7 @@ const GROUPED_HELP = `
|
|
|
77
77
|
|
|
78
78
|
\x1b[1mSetup\x1b[22m
|
|
79
79
|
\x1b[36mlogin\x1b[39m Browser login (recommended)
|
|
80
|
+
\x1b[36mlogout\x1b[39m Clear saved credentials
|
|
80
81
|
\x1b[36msetup\x1b[39m Interactive config wizard (power users)
|
|
81
82
|
\x1b[36msetup --check\x1b[39m Show config status
|
|
82
83
|
\x1b[36msetup --polymarket\x1b[39m Configure Polymarket wallet
|
|
@@ -149,7 +150,7 @@ const GROUPED_HELP = `
|
|
|
149
150
|
program
|
|
150
151
|
.name('sf')
|
|
151
152
|
.description('SimpleFunctions CLI — prediction market thesis agent')
|
|
152
|
-
.version('
|
|
153
|
+
.version('1.7.17')
|
|
153
154
|
.option('--api-key <key>', 'API key (or set SF_API_KEY env var)')
|
|
154
155
|
.option('--api-url <url>', 'API base URL (or set SF_API_URL env var)')
|
|
155
156
|
.configureHelp({
|
|
@@ -247,7 +248,7 @@ async function interactiveEntry() {
|
|
|
247
248
|
console.log();
|
|
248
249
|
}
|
|
249
250
|
// ── Pre-action guard: check configuration ────────────────────────────────────
|
|
250
|
-
const NO_CONFIG_COMMANDS = new Set(['setup', 'login', 'help', 'scan', 'explore', 'query', 'context', 'markets', 'milestones', 'forecast', 'settlements', 'balance', 'orders', 'fills', 'schedule', 'announcements', 'history', 'liquidity', 'book', 'prompt', 'sf']);
|
|
251
|
+
const NO_CONFIG_COMMANDS = new Set(['setup', 'login', 'logout', 'help', 'scan', 'explore', 'query', 'context', 'markets', 'watch', 'milestones', 'forecast', 'settlements', 'balance', 'orders', 'fills', 'schedule', 'announcements', 'history', 'liquidity', 'book', 'prompt', 'sf']);
|
|
251
252
|
program.hook('preAction', (thisCommand, actionCommand) => {
|
|
252
253
|
const cmdName = actionCommand.name();
|
|
253
254
|
if (NO_CONFIG_COMMANDS.has(cmdName))
|
|
@@ -259,7 +260,7 @@ program.hook('preAction', (thisCommand, actionCommand) => {
|
|
|
259
260
|
if (!(0, config_js_1.isConfigured)()) {
|
|
260
261
|
const isJson = process.argv.includes('--json');
|
|
261
262
|
if (isJson) {
|
|
262
|
-
console.log(JSON.stringify({ error: 'API key required.
|
|
263
|
+
console.log(JSON.stringify({ error: 'API key required.', code: 'NOT_CONFIGURED', keyUrl: 'https://simplefunctions.dev/dashboard/keys', cli: 'sf login' }));
|
|
263
264
|
}
|
|
264
265
|
else {
|
|
265
266
|
console.log();
|
|
@@ -268,7 +269,8 @@ program.hook('preAction', (thisCommand, actionCommand) => {
|
|
|
268
269
|
console.log(' 2. \x1b[36msf setup\x1b[39m Interactive wizard (2 min)');
|
|
269
270
|
console.log(' 3. \x1b[36msf --api-key KEY\x1b[39m Pass inline');
|
|
270
271
|
console.log();
|
|
271
|
-
console.log(' \x1b[
|
|
272
|
+
console.log(' \x1b[2mGet a key at: \x1b[36mhttps://simplefunctions.dev/dashboard/keys\x1b[22m');
|
|
273
|
+
console.log(' \x1b[2mNo key? Try \x1b[36msf scan "oil"\x1b[22m\x1b[2m — works without login.\x1b[22m');
|
|
272
274
|
}
|
|
273
275
|
console.log();
|
|
274
276
|
process.exit(1);
|
|
@@ -292,9 +294,27 @@ program
|
|
|
292
294
|
program
|
|
293
295
|
.command('login')
|
|
294
296
|
.description('Browser-based login (recommended — no API keys needed)')
|
|
295
|
-
.
|
|
297
|
+
.option('--force', 'Re-login even if already configured')
|
|
298
|
+
.action(async (opts, cmd) => {
|
|
296
299
|
const g = cmd.optsWithGlobals();
|
|
297
|
-
await run(() => (0, login_js_1.loginCommand)({ apiUrl: g.apiUrl }));
|
|
300
|
+
await run(() => (0, login_js_1.loginCommand)({ apiUrl: g.apiUrl, force: opts.force }));
|
|
301
|
+
});
|
|
302
|
+
// ── sf logout ────────────────────────────────────────────────────────────────
|
|
303
|
+
program
|
|
304
|
+
.command('logout')
|
|
305
|
+
.description('Clear saved credentials')
|
|
306
|
+
.action(async () => {
|
|
307
|
+
await run(async () => {
|
|
308
|
+
const { resetConfig, loadFileConfig } = await import('./config.js');
|
|
309
|
+
const cfg = loadFileConfig();
|
|
310
|
+
if (!cfg.apiKey) {
|
|
311
|
+
console.log(`\n ${utils_js_1.c.dim}Not logged in.${utils_js_1.c.reset}\n`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
resetConfig();
|
|
315
|
+
console.log(`\n Logged out. Credentials removed from ~/.sf/config.json`);
|
|
316
|
+
console.log(` ${utils_js_1.c.dim}Run ${utils_js_1.c.cyan}sf login${utils_js_1.c.dim} to re-authenticate.${utils_js_1.c.reset}\n`);
|
|
317
|
+
});
|
|
298
318
|
});
|
|
299
319
|
// ── sf list ──────────────────────────────────────────────────────────────────
|
|
300
320
|
program
|
package/dist/utils.d.ts
CHANGED
|
@@ -34,6 +34,16 @@ export declare function rpad(s: string, width: number): string;
|
|
|
34
34
|
export declare function hr(width?: number): void;
|
|
35
35
|
/** Print an error and exit */
|
|
36
36
|
export declare function die(msg: string): never;
|
|
37
|
+
/**
|
|
38
|
+
* Print an error with an actionable suggestion.
|
|
39
|
+
* Use instead of bare console.error for user-facing errors.
|
|
40
|
+
*/
|
|
41
|
+
export declare function errorWithHint(message: string, hint?: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Print an empty state message with a next-action CTA.
|
|
44
|
+
* Use when a command returns zero results.
|
|
45
|
+
*/
|
|
46
|
+
export declare function emptyState(thing: string, nextAction?: string): void;
|
|
37
47
|
/** Print a section header */
|
|
38
48
|
export declare function header(title: string): void;
|
|
39
49
|
/** Truncate a string with ellipsis */
|
package/dist/utils.js
CHANGED
|
@@ -15,6 +15,8 @@ exports.pad = pad;
|
|
|
15
15
|
exports.rpad = rpad;
|
|
16
16
|
exports.hr = hr;
|
|
17
17
|
exports.die = die;
|
|
18
|
+
exports.errorWithHint = errorWithHint;
|
|
19
|
+
exports.emptyState = emptyState;
|
|
18
20
|
exports.header = header;
|
|
19
21
|
exports.trunc = trunc;
|
|
20
22
|
exports.shortId = shortId;
|
|
@@ -108,6 +110,26 @@ function die(msg) {
|
|
|
108
110
|
console.error(`${exports.c.red}Error:${exports.c.reset} ${msg}`);
|
|
109
111
|
process.exit(1);
|
|
110
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Print an error with an actionable suggestion.
|
|
115
|
+
* Use instead of bare console.error for user-facing errors.
|
|
116
|
+
*/
|
|
117
|
+
function errorWithHint(message, hint) {
|
|
118
|
+
console.error(`\n ${exports.c.red}Error:${exports.c.reset} ${message}`);
|
|
119
|
+
if (hint)
|
|
120
|
+
console.error(` ${exports.c.dim}${hint}${exports.c.reset}`);
|
|
121
|
+
console.error();
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Print an empty state message with a next-action CTA.
|
|
125
|
+
* Use when a command returns zero results.
|
|
126
|
+
*/
|
|
127
|
+
function emptyState(thing, nextAction) {
|
|
128
|
+
console.log(`\n ${exports.c.dim}No ${thing} found.${exports.c.reset}`);
|
|
129
|
+
if (nextAction)
|
|
130
|
+
console.log(` ${exports.c.dim}${nextAction}${exports.c.reset}`);
|
|
131
|
+
console.log();
|
|
132
|
+
}
|
|
111
133
|
/** Print a section header */
|
|
112
134
|
function header(title) {
|
|
113
135
|
console.log(`\n${exports.c.bold}${exports.c.cyan}${title}${exports.c.reset}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spfunctions/cli",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.19",
|
|
4
|
+
"mcpName": "io.github.spfunctions/simplefunctions",
|
|
4
5
|
"description": "Prediction market intelligence CLI. Causal thesis model, 24/7 Kalshi/Polymarket scan, live orderbook, edge detection. Interactive agent mode with tool calling.",
|
|
5
6
|
"bin": {
|
|
6
7
|
"sf": "./dist/index.js"
|