@spfunctions/cli 1.5.0 → 1.5.2
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 +2 -0
- package/dist/client.js +11 -2
- package/dist/client.test.js +1 -1
- package/dist/commands/agent.js +21 -9
- package/dist/commands/create.d.ts +2 -0
- package/dist/commands/create.js +18 -7
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +4 -0
- package/dist/index.js +14 -3
- package/dist/telegram/agent-bridge.js +81 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Prediction market intelligence CLI. Build causal thesis models, scan Kalshi/Polymarket for mispricings, detect edges, and trade — all from the terminal.
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
5
7
|
## Quick Start
|
|
6
8
|
|
|
7
9
|
```bash
|
package/dist/client.js
CHANGED
|
@@ -37,8 +37,17 @@ class SFClient {
|
|
|
37
37
|
body: body ? JSON.stringify(body) : undefined,
|
|
38
38
|
});
|
|
39
39
|
if (!res.ok) {
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
let errorBody = null;
|
|
41
|
+
try {
|
|
42
|
+
const text = await res.text();
|
|
43
|
+
errorBody = text ? JSON.parse(text) : null;
|
|
44
|
+
}
|
|
45
|
+
catch { /* not JSON */ }
|
|
46
|
+
const err = new Error(errorBody?.error || errorBody?.message || `API error ${res.status}`);
|
|
47
|
+
err.status = res.status;
|
|
48
|
+
err.code = errorBody?.code || `HTTP_${res.status}`;
|
|
49
|
+
err.details = errorBody;
|
|
50
|
+
throw err;
|
|
42
51
|
}
|
|
43
52
|
return res.json();
|
|
44
53
|
}
|
package/dist/client.test.js
CHANGED
|
@@ -66,7 +66,7 @@ vitest_1.vi.stubGlobal('fetch', mockFetch);
|
|
|
66
66
|
status: 404,
|
|
67
67
|
text: () => Promise.resolve('Not found'),
|
|
68
68
|
});
|
|
69
|
-
await (0, vitest_1.expect)(client.getThesis('bad-id')).rejects.toThrow('API error 404
|
|
69
|
+
await (0, vitest_1.expect)(client.getThesis('bad-id')).rejects.toThrow('API error 404');
|
|
70
70
|
});
|
|
71
71
|
(0, vitest_1.it)('getContext calls correct path', async () => {
|
|
72
72
|
mockFetch.mockResolvedValue({
|
package/dist/commands/agent.js
CHANGED
|
@@ -3180,22 +3180,34 @@ async function runPlainTextAgent(params) {
|
|
|
3180
3180
|
.map((n) => ` ${n.id} ${(n.label || '').slice(0, 40)} \u2014 ${Math.round(n.probability * 100)}%`)
|
|
3181
3181
|
.join('\n') || ' (no causal tree)';
|
|
3182
3182
|
const conf = typeof ctx.confidence === 'number' ? Math.round(ctx.confidence * 100) : 0;
|
|
3183
|
-
const systemPrompt = `You are a prediction market trading assistant.
|
|
3183
|
+
const systemPrompt = `You are a prediction market trading assistant. Your job is not to please the user — it is to help them see reality clearly and make correct trading decisions.
|
|
3184
3184
|
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3185
|
+
## Framework
|
|
3186
|
+
Each thesis has a causal tree. Every node is a hypothesis with a probability. Edge = thesis-implied price - market price. Positive edge = market underprices. Contracts with large edge + good liquidity = most tradeable.
|
|
3187
|
+
executableEdge = edge after subtracting bid-ask spread. A big theoretical edge with wide spread may not be worth entering.
|
|
3188
|
+
|
|
3189
|
+
## Rules
|
|
3190
|
+
- Be concise. Use tools for fresh data. Don't guess prices.
|
|
3191
|
+
- If user mentions news, inject_signal immediately.
|
|
3192
|
+
- If user says "evaluate", trigger immediately. Don't confirm.
|
|
3193
|
+
- Don't end with "anything else?"
|
|
3194
|
+
- If an edge is narrowing or disappearing, say so proactively.
|
|
3195
|
+
- Use Chinese if user writes Chinese, English if English.
|
|
3196
|
+
- Prices in cents (¢). P&L in dollars ($). Don't re-convert tool output.
|
|
3197
|
+
- When a trade idea emerges, create_strategy to record it.
|
|
3198
|
+
${config.tradingEnabled ? '- Trading ENABLED. You have place_order and cancel_order tools.' : '- Trading DISABLED. Tell user: sf setup --enable-trading'}
|
|
3189
3199
|
|
|
3190
|
-
|
|
3200
|
+
## Current State
|
|
3201
|
+
Thesis: ${ctx.thesis || ctx.rawThesis || 'N/A'}
|
|
3202
|
+
ID: ${resolvedThesisId} | Confidence: ${conf}% | Status: ${ctx.status}
|
|
3203
|
+
|
|
3204
|
+
Causal nodes:
|
|
3191
3205
|
${nodesSummary}
|
|
3192
3206
|
|
|
3193
3207
|
Top edges:
|
|
3194
3208
|
${edgesSummary}
|
|
3195
3209
|
|
|
3196
|
-
${ctx.lastEvaluation?.summary ? `Latest
|
|
3197
|
-
|
|
3198
|
-
Rules: Be concise. Use tools when needed. Don't ask "anything else?". Prices are in cents (e.g. 35¢). P&L, cost, and balance are in dollars (e.g. $90.66). Tool outputs are pre-formatted with units — do not re-convert.`;
|
|
3210
|
+
${ctx.lastEvaluation?.summary ? `Latest eval: ${ctx.lastEvaluation.summary.slice(0, 300)}` : ''}`;
|
|
3199
3211
|
// ── Create agent ──────────────────────────────────────────────────────────
|
|
3200
3212
|
const agent = new Agent({
|
|
3201
3213
|
initialState: { systemPrompt, model, tools, thinkingLevel: 'off' },
|
package/dist/commands/create.js
CHANGED
|
@@ -6,15 +6,27 @@ const utils_js_1 = require("../utils.js");
|
|
|
6
6
|
async function createCommand(thesis, opts) {
|
|
7
7
|
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
8
8
|
const sync = !opts.async;
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
if (!opts.json) {
|
|
10
|
+
if (sync) {
|
|
11
|
+
console.log(`${utils_js_1.c.dim}Creating thesis (sync mode — waiting for formation)...${utils_js_1.c.reset}`);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
console.log(`${utils_js_1.c.dim}Creating thesis (async mode)...${utils_js_1.c.reset}`);
|
|
15
|
+
}
|
|
14
16
|
}
|
|
15
17
|
const result = await client.createThesis(thesis, sync);
|
|
18
|
+
const id = result.thesis?.id || result.thesisId || result.id || null;
|
|
19
|
+
if (opts.json) {
|
|
20
|
+
console.log(JSON.stringify({ id, status: result.thesis?.status || result.status || 'forming', result }, null, 2));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!id) {
|
|
24
|
+
console.error(`${utils_js_1.c.red}✗${utils_js_1.c.reset} Thesis creation returned no ID.`);
|
|
25
|
+
console.error(`${utils_js_1.c.dim}Response: ${JSON.stringify(result).slice(0, 200)}${utils_js_1.c.reset}`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
16
28
|
console.log(`\n${utils_js_1.c.green}✓${utils_js_1.c.reset} Thesis created`);
|
|
17
|
-
console.log(` ${utils_js_1.c.bold}ID:${utils_js_1.c.reset} ${
|
|
29
|
+
console.log(` ${utils_js_1.c.bold}ID:${utils_js_1.c.reset} ${id}`);
|
|
18
30
|
console.log(` ${utils_js_1.c.bold}Status:${utils_js_1.c.reset} ${result.thesis?.status || result.status}`);
|
|
19
31
|
if (result.thesis?.confidence) {
|
|
20
32
|
console.log(` ${utils_js_1.c.bold}Confidence:${utils_js_1.c.reset} ${Math.round(parseFloat(result.thesis.confidence) * 100)}%`);
|
|
@@ -25,7 +37,6 @@ async function createCommand(thesis, opts) {
|
|
|
25
37
|
if (result.thesis?.edgeAnalysis?.edges) {
|
|
26
38
|
console.log(` ${utils_js_1.c.bold}Edges:${utils_js_1.c.reset} ${result.thesis.edgeAnalysis.edges.length}`);
|
|
27
39
|
}
|
|
28
|
-
const id = result.thesis?.id || result.id;
|
|
29
40
|
console.log(`\n${utils_js_1.c.dim}View: sf get ${(0, utils_js_1.shortId)(id)}${utils_js_1.c.reset}`);
|
|
30
41
|
console.log(`${utils_js_1.c.dim}Context: sf context ${(0, utils_js_1.shortId)(id)}${utils_js_1.c.reset}`);
|
|
31
42
|
}
|
package/dist/commands/list.d.ts
CHANGED
package/dist/commands/list.js
CHANGED
|
@@ -6,6 +6,10 @@ const utils_js_1 = require("../utils.js");
|
|
|
6
6
|
async function listCommand(opts) {
|
|
7
7
|
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
8
8
|
const { theses } = await client.listTheses();
|
|
9
|
+
if (opts.json) {
|
|
10
|
+
console.log(JSON.stringify(theses, null, 2));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
9
13
|
if (theses.length === 0) {
|
|
10
14
|
console.log(`${utils_js_1.c.dim}No theses found.${utils_js_1.c.reset}`);
|
|
11
15
|
return;
|
package/dist/index.js
CHANGED
|
@@ -165,9 +165,10 @@ program
|
|
|
165
165
|
program
|
|
166
166
|
.command('list')
|
|
167
167
|
.description('List all theses')
|
|
168
|
-
.
|
|
168
|
+
.option('--json', 'JSON output')
|
|
169
|
+
.action(async (opts, cmd) => {
|
|
169
170
|
const g = cmd.optsWithGlobals();
|
|
170
|
-
await run(() => (0, list_js_1.listCommand)({ apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
171
|
+
await run(() => (0, list_js_1.listCommand)({ json: opts.json, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
171
172
|
});
|
|
172
173
|
// ── sf get <id> ───────────────────────────────────────────────────────────────
|
|
173
174
|
program
|
|
@@ -192,9 +193,10 @@ program
|
|
|
192
193
|
.command('create <thesis>')
|
|
193
194
|
.description('Create a new thesis (sync by default — waits for formation)')
|
|
194
195
|
.option('--async', 'Async mode — return immediately without waiting')
|
|
196
|
+
.option('--json', 'JSON output')
|
|
195
197
|
.action(async (thesis, opts, cmd) => {
|
|
196
198
|
const g = cmd.optsWithGlobals();
|
|
197
|
-
await run(() => (0, create_js_1.createCommand)(thesis, { async: opts.async, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
199
|
+
await run(() => (0, create_js_1.createCommand)(thesis, { async: opts.async, json: opts.json, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
198
200
|
});
|
|
199
201
|
// ── sf signal <id> <content> ──────────────────────────────────────────────────
|
|
200
202
|
program
|
|
@@ -516,6 +518,15 @@ async function run(fn) {
|
|
|
516
518
|
}
|
|
517
519
|
catch (err) {
|
|
518
520
|
const msg = err instanceof Error ? err.message : String(err);
|
|
521
|
+
// If --json flag is present, output structured JSON error
|
|
522
|
+
if (process.argv.includes('--json')) {
|
|
523
|
+
console.log(JSON.stringify({
|
|
524
|
+
error: msg,
|
|
525
|
+
code: err.code || 'CLI_ERROR',
|
|
526
|
+
status: err.status || 1,
|
|
527
|
+
}));
|
|
528
|
+
process.exit(1);
|
|
529
|
+
}
|
|
519
530
|
(0, utils_js_1.die)(msg);
|
|
520
531
|
}
|
|
521
532
|
}
|
|
@@ -274,6 +274,53 @@ async function buildTools(sfClient, thesisId, latestContext) {
|
|
|
274
274
|
},
|
|
275
275
|
},
|
|
276
276
|
];
|
|
277
|
+
// Trading tools (only if enabled)
|
|
278
|
+
if (config.tradingEnabled) {
|
|
279
|
+
tools.push({
|
|
280
|
+
name: 'place_order',
|
|
281
|
+
label: 'Place Order',
|
|
282
|
+
description: 'Place a buy or sell order on Kalshi. Requires trading to be enabled. Always confirm with user before placing.',
|
|
283
|
+
parameters: Type.Object({
|
|
284
|
+
ticker: Type.String({ description: 'Market ticker (e.g. KXWTIMAX-26DEC31-T135)' }),
|
|
285
|
+
side: Type.String({ description: 'yes or no' }),
|
|
286
|
+
action: Type.String({ description: 'buy or sell' }),
|
|
287
|
+
count: Type.Number({ description: 'Number of contracts' }),
|
|
288
|
+
yes_price: Type.Number({ description: 'Price in cents (1-99)' }),
|
|
289
|
+
}),
|
|
290
|
+
execute: async (_id, p) => {
|
|
291
|
+
try {
|
|
292
|
+
const result = await kalshi.createOrder({
|
|
293
|
+
ticker: p.ticker,
|
|
294
|
+
side: p.side,
|
|
295
|
+
action: p.action,
|
|
296
|
+
type: 'limit',
|
|
297
|
+
count: p.count,
|
|
298
|
+
yes_price: p.yes_price,
|
|
299
|
+
});
|
|
300
|
+
return { content: [{ type: 'text', text: `✓ Order placed: ${(result.order || result).order_id || 'OK'}\n${p.action.toUpperCase()} ${p.count}x ${p.ticker} ${p.side.toUpperCase()} @ ${p.yes_price}¢` }], details: {} };
|
|
301
|
+
}
|
|
302
|
+
catch (err) {
|
|
303
|
+
return { content: [{ type: 'text', text: `✗ Order failed: ${err.message}` }], details: {} };
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
}, {
|
|
307
|
+
name: 'cancel_order',
|
|
308
|
+
label: 'Cancel Order',
|
|
309
|
+
description: 'Cancel a resting order on Kalshi.',
|
|
310
|
+
parameters: Type.Object({
|
|
311
|
+
orderId: Type.String({ description: 'Order ID to cancel' }),
|
|
312
|
+
}),
|
|
313
|
+
execute: async (_id, p) => {
|
|
314
|
+
try {
|
|
315
|
+
await kalshi.cancelOrder(p.orderId);
|
|
316
|
+
return { content: [{ type: 'text', text: `✓ Order ${p.orderId} cancelled.` }], details: {} };
|
|
317
|
+
}
|
|
318
|
+
catch (err) {
|
|
319
|
+
return { content: [{ type: 'text', text: `✗ Cancel failed: ${err.message}` }], details: {} };
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
}
|
|
277
324
|
return tools;
|
|
278
325
|
}
|
|
279
326
|
async function getOrCreateAgent(sfClient, session) {
|
|
@@ -303,17 +350,43 @@ async function getOrCreateAgent(sfClient, session) {
|
|
|
303
350
|
supportsImages: true, supportsTools: true,
|
|
304
351
|
};
|
|
305
352
|
}
|
|
306
|
-
const
|
|
353
|
+
const edgesSummary = (ctx.edges || [])
|
|
354
|
+
.sort((a, b) => Math.abs(b.edge) - Math.abs(a.edge))
|
|
355
|
+
.slice(0, 5)
|
|
356
|
+
.map((e) => ` ${(e.market || '').slice(0, 35)} | ${e.venue || 'kalshi'} | mkt ${e.marketPrice}¢ → thesis ${e.thesisPrice}¢ | edge ${e.edge > 0 ? '+' : ''}${e.edge}`)
|
|
357
|
+
.join('\n') || ' (no edges)';
|
|
358
|
+
const nodesSummary = (ctx.causalTree?.nodes || [])
|
|
359
|
+
.filter((n) => n.depth === 0 || !n.depth)
|
|
360
|
+
.slice(0, 5)
|
|
361
|
+
.map((n) => ` ${n.id} ${(n.label || '').slice(0, 35)} — ${Math.round((n.probability || 0.5) * 100)}%`)
|
|
362
|
+
.join('\n') || ' (no causal tree)';
|
|
363
|
+
const systemPrompt = `You are a prediction market trading assistant on Telegram. Your job is to help the user see reality clearly and make correct trading decisions.
|
|
307
364
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
365
|
+
## Framework
|
|
366
|
+
Edge = thesis price - market price. Positive = market underprices. Negative = overpriced.
|
|
367
|
+
Contracts with large edge + good liquidity = most tradeable.
|
|
311
368
|
|
|
312
|
-
Rules
|
|
313
|
-
- Keep
|
|
314
|
-
- Prices
|
|
369
|
+
## Rules
|
|
370
|
+
- Keep Telegram messages SHORT — bullet points, no walls of text.
|
|
371
|
+
- Prices in cents (¢). P&L in dollars ($). Don't re-convert tool output units.
|
|
372
|
+
- Call tools for fresh data. Never guess prices or P&L from this prompt.
|
|
373
|
+
- If user mentions news, inject_signal immediately. Don't ask "should I?"
|
|
374
|
+
- If user says "evaluate" or "run it", trigger immediately.
|
|
375
|
+
- Don't end with "anything else?" — user will ask.
|
|
315
376
|
- Use Chinese if user writes Chinese, English if English.
|
|
316
|
-
-
|
|
377
|
+
${config.tradingEnabled ? '- Trading ENABLED. You have place_order and cancel_order. ALWAYS confirm before placing.' : '- Trading DISABLED. Tell user: sf setup --enable-trading'}
|
|
378
|
+
|
|
379
|
+
## Current State
|
|
380
|
+
Thesis: ${(ctx.thesis || ctx.rawThesis || 'N/A').slice(0, 200)}
|
|
381
|
+
ID: ${session.thesisId.slice(0, 8)} | Confidence: ${conf}%
|
|
382
|
+
|
|
383
|
+
Causal nodes:
|
|
384
|
+
${nodesSummary}
|
|
385
|
+
|
|
386
|
+
Top edges:
|
|
387
|
+
${edgesSummary}
|
|
388
|
+
|
|
389
|
+
${ctx.lastEvaluation?.summary ? `Latest eval: ${ctx.lastEvaluation.summary.slice(0, 200)}` : ''}`;
|
|
317
390
|
const agent = new Agent({
|
|
318
391
|
initialState: {
|
|
319
392
|
systemPrompt,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spfunctions/cli",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "Prediction market intelligence CLI. Causal thesis model, 24/7 Kalshi/Polymarket scan, live orderbook, edge detection. Interactive agent mode with tool calling.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sf": "./dist/index.js"
|