@spfunctions/cli 1.7.16 → 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 +305 -72
- package/dist/commands/balance.js +1 -1
- package/dist/commands/book.js +1 -1
- package/dist/commands/context.d.ts +1 -0
- package/dist/commands/context.js +5 -0
- package/dist/commands/edges.d.ts +1 -0
- package/dist/commands/edges.js +28 -13
- package/dist/commands/explore.d.ts +1 -0
- package/dist/commands/explore.js +7 -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/markets.d.ts +1 -0
- package/dist/commands/markets.js +5 -0
- package/dist/commands/positions.js +2 -2
- package/dist/commands/query.d.ts +1 -0
- package/dist/commands/query.js +6 -1
- package/dist/commands/scan.d.ts +1 -0
- package/dist/commands/scan.js +8 -3
- package/dist/commands/watch.d.ts +19 -0
- package/dist/commands/watch.js +157 -0
- package/dist/config.js +2 -2
- package/dist/index.js +54 -10
- package/dist/share.d.ts +4 -0
- package/dist/share.js +27 -0
- package/dist/skills/loader.d.ts +19 -0
- package/dist/skills/loader.js +86 -0
- package/dist/types/output.d.ts +412 -0
- package/dist/types/output.js +9 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +22 -0
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ const create_js_1 = require("./commands/create.js");
|
|
|
29
29
|
const signal_js_1 = require("./commands/signal.js");
|
|
30
30
|
const evaluate_js_1 = require("./commands/evaluate.js");
|
|
31
31
|
const scan_js_1 = require("./commands/scan.js");
|
|
32
|
+
const watch_js_1 = require("./commands/watch.js");
|
|
32
33
|
const positions_js_1 = require("./commands/positions.js");
|
|
33
34
|
const edges_js_1 = require("./commands/edges.js");
|
|
34
35
|
const agent_js_1 = require("./commands/agent.js");
|
|
@@ -76,6 +77,7 @@ const GROUPED_HELP = `
|
|
|
76
77
|
|
|
77
78
|
\x1b[1mSetup\x1b[22m
|
|
78
79
|
\x1b[36mlogin\x1b[39m Browser login (recommended)
|
|
80
|
+
\x1b[36mlogout\x1b[39m Clear saved credentials
|
|
79
81
|
\x1b[36msetup\x1b[39m Interactive config wizard (power users)
|
|
80
82
|
\x1b[36msetup --check\x1b[39m Show config status
|
|
81
83
|
\x1b[36msetup --polymarket\x1b[39m Configure Polymarket wallet
|
|
@@ -99,12 +101,18 @@ const GROUPED_HELP = `
|
|
|
99
101
|
\x1b[36mscan\x1b[39m "keywords" Search Kalshi + Polymarket
|
|
100
102
|
\x1b[36mscan\x1b[39m --series TICKER Browse a Kalshi series
|
|
101
103
|
\x1b[36medges\x1b[39m [--json] Top edges across all theses
|
|
104
|
+
\x1b[36mwatch\x1b[39m [query] Watch for market changes (e.g. sf watch "gold")
|
|
102
105
|
\x1b[36mwhatif\x1b[39m <id> What-if scenario analysis
|
|
103
106
|
\x1b[36mliquidity\x1b[39m [topic] Orderbook liquidity scanner
|
|
104
107
|
\x1b[36mbook\x1b[39m <ticker> [ticker2...] Orderbook depth for specific markets
|
|
105
108
|
\x1b[36mexplore\x1b[39m [slug] Browse public theses
|
|
106
109
|
\x1b[36mforecast\x1b[39m <event> Market distribution (P50/P75/P90)
|
|
107
110
|
|
|
111
|
+
\x1b[1mShare\x1b[22m \x1b[2m(any command with --share generates a short URL)\x1b[22m
|
|
112
|
+
\x1b[36mscan\x1b[39m "gold" --share Share scan results
|
|
113
|
+
\x1b[36mquery\x1b[39m "fed rate" --share Share query results
|
|
114
|
+
\x1b[36mcontext\x1b[39m --share Share global context snapshot
|
|
115
|
+
|
|
108
116
|
\x1b[1mPortfolio\x1b[22m
|
|
109
117
|
\x1b[36mpositions\x1b[39m Kalshi + Polymarket positions
|
|
110
118
|
\x1b[36mbalance\x1b[39m Account balance
|
|
@@ -142,7 +150,7 @@ const GROUPED_HELP = `
|
|
|
142
150
|
program
|
|
143
151
|
.name('sf')
|
|
144
152
|
.description('SimpleFunctions CLI — prediction market thesis agent')
|
|
145
|
-
.version('
|
|
153
|
+
.version('1.7.17')
|
|
146
154
|
.option('--api-key <key>', 'API key (or set SF_API_KEY env var)')
|
|
147
155
|
.option('--api-url <url>', 'API base URL (or set SF_API_URL env var)')
|
|
148
156
|
.configureHelp({
|
|
@@ -240,7 +248,7 @@ async function interactiveEntry() {
|
|
|
240
248
|
console.log();
|
|
241
249
|
}
|
|
242
250
|
// ── Pre-action guard: check configuration ────────────────────────────────────
|
|
243
|
-
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']);
|
|
244
252
|
program.hook('preAction', (thisCommand, actionCommand) => {
|
|
245
253
|
const cmdName = actionCommand.name();
|
|
246
254
|
if (NO_CONFIG_COMMANDS.has(cmdName))
|
|
@@ -252,7 +260,7 @@ program.hook('preAction', (thisCommand, actionCommand) => {
|
|
|
252
260
|
if (!(0, config_js_1.isConfigured)()) {
|
|
253
261
|
const isJson = process.argv.includes('--json');
|
|
254
262
|
if (isJson) {
|
|
255
|
-
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' }));
|
|
256
264
|
}
|
|
257
265
|
else {
|
|
258
266
|
console.log();
|
|
@@ -261,7 +269,8 @@ program.hook('preAction', (thisCommand, actionCommand) => {
|
|
|
261
269
|
console.log(' 2. \x1b[36msf setup\x1b[39m Interactive wizard (2 min)');
|
|
262
270
|
console.log(' 3. \x1b[36msf --api-key KEY\x1b[39m Pass inline');
|
|
263
271
|
console.log();
|
|
264
|
-
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');
|
|
265
274
|
}
|
|
266
275
|
console.log();
|
|
267
276
|
process.exit(1);
|
|
@@ -285,9 +294,27 @@ program
|
|
|
285
294
|
program
|
|
286
295
|
.command('login')
|
|
287
296
|
.description('Browser-based login (recommended — no API keys needed)')
|
|
288
|
-
.
|
|
297
|
+
.option('--force', 'Re-login even if already configured')
|
|
298
|
+
.action(async (opts, cmd) => {
|
|
289
299
|
const g = cmd.optsWithGlobals();
|
|
290
|
-
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
|
+
});
|
|
291
318
|
});
|
|
292
319
|
// ── sf list ──────────────────────────────────────────────────────────────────
|
|
293
320
|
program
|
|
@@ -312,9 +339,10 @@ program
|
|
|
312
339
|
.command('context [id]')
|
|
313
340
|
.description('Context snapshot. With ID: thesis-specific. Without: global market snapshot (no auth)')
|
|
314
341
|
.option('--json', 'Output raw JSON')
|
|
342
|
+
.option('--share', 'Share output via short URL')
|
|
315
343
|
.action(async (id, opts, cmd) => {
|
|
316
344
|
const g = cmd.optsWithGlobals();
|
|
317
|
-
await run(() => (0, context_js_1.contextCommand)(id, { json: opts.json, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
345
|
+
await run(() => (0, context_js_1.contextCommand)(id, { json: opts.json, share: opts.share, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
318
346
|
});
|
|
319
347
|
// ── sf create <thesis> ────────────────────────────────────────────────────────
|
|
320
348
|
program
|
|
@@ -376,6 +404,7 @@ program
|
|
|
376
404
|
.option('--market <ticker>', 'Get single market detail (e.g. KXWTIMAX-26DEC31-T140)')
|
|
377
405
|
.option('--venue <venue>', 'Filter by venue: kalshi, polymarket, or all (default: all)')
|
|
378
406
|
.option('--json', 'Output raw JSON')
|
|
407
|
+
.option('--share', 'Share output via short URL')
|
|
379
408
|
.action(async (query, opts, cmd) => {
|
|
380
409
|
const g = cmd.optsWithGlobals();
|
|
381
410
|
const q = query || '';
|
|
@@ -388,15 +417,26 @@ program
|
|
|
388
417
|
market: opts.market,
|
|
389
418
|
venue: opts.venue,
|
|
390
419
|
json: opts.json,
|
|
420
|
+
share: opts.share,
|
|
391
421
|
apiKey: g.apiKey,
|
|
392
422
|
apiUrl: g.apiUrl,
|
|
393
423
|
}));
|
|
394
424
|
});
|
|
425
|
+
// ── sf watch [query] ─────────────────────────────────────────────────────────
|
|
426
|
+
program
|
|
427
|
+
.command('watch [query]')
|
|
428
|
+
.description('Watch for market changes (e.g. sf watch "gold")')
|
|
429
|
+
.option('--interval <seconds>', 'Poll interval in seconds (default: 60)', '60')
|
|
430
|
+
.option('--json', 'JSON change events (for piping)')
|
|
431
|
+
.action(async (query, opts) => {
|
|
432
|
+
await run(() => (0, watch_js_1.watchCommand)(query, { interval: opts.interval, json: opts.json }));
|
|
433
|
+
});
|
|
395
434
|
// ── sf edges ──────────────────────────────────────────────────────────────────
|
|
396
435
|
program
|
|
397
436
|
.command('edges')
|
|
398
437
|
.description('Top edges across all theses — what to trade now')
|
|
399
438
|
.option('--json', 'JSON output for agents')
|
|
439
|
+
.option('--share', 'Share output via short URL')
|
|
400
440
|
.option('--limit <n>', 'Max edges to show', '20')
|
|
401
441
|
.option('--thesis <id>', 'Filter to a single thesis')
|
|
402
442
|
.option('--min-edge <cents>', 'Minimum absolute edge size in cents')
|
|
@@ -406,6 +446,7 @@ program
|
|
|
406
446
|
const g = cmd.optsWithGlobals();
|
|
407
447
|
await run(() => (0, edges_js_1.edgesCommand)({
|
|
408
448
|
json: opts.json,
|
|
449
|
+
share: opts.share,
|
|
409
450
|
limit: opts.limit,
|
|
410
451
|
thesis: opts.thesis,
|
|
411
452
|
minEdge: opts.minEdge,
|
|
@@ -465,8 +506,9 @@ program
|
|
|
465
506
|
.command('explore [slug]')
|
|
466
507
|
.description('Browse public theses (no auth required)')
|
|
467
508
|
.option('--json', 'JSON output')
|
|
509
|
+
.option('--share', 'Share output via short URL')
|
|
468
510
|
.action(async (slug, opts) => {
|
|
469
|
-
await run(() => (0, explore_js_1.exploreCommand)(slug, { json: opts.json }));
|
|
511
|
+
await run(() => (0, explore_js_1.exploreCommand)(slug, { json: opts.json, share: opts.share }));
|
|
470
512
|
});
|
|
471
513
|
// ── sf dashboard ──────────────────────────────────────────────────────────────
|
|
472
514
|
program
|
|
@@ -702,17 +744,19 @@ program
|
|
|
702
744
|
.command('markets')
|
|
703
745
|
.description('Traditional market snapshot — SPY, VIX, Treasury, Gold, Oil (no auth)')
|
|
704
746
|
.option('--json', 'JSON output')
|
|
747
|
+
.option('--share', 'Share output via short URL')
|
|
705
748
|
.action(async (opts) => {
|
|
706
|
-
await run(() => (0, markets_js_1.marketsCommand)({ json: opts.json }));
|
|
749
|
+
await run(() => (0, markets_js_1.marketsCommand)({ json: opts.json, share: opts.share }));
|
|
707
750
|
});
|
|
708
751
|
// ── sf query "question" ──────────────────────────────────────────────────────
|
|
709
752
|
program
|
|
710
753
|
.command('query <question>')
|
|
711
754
|
.description('LLM-enhanced prediction market knowledge search (no auth required)')
|
|
712
755
|
.option('--json', 'JSON output')
|
|
756
|
+
.option('--share', 'Share output via short URL')
|
|
713
757
|
.option('--limit <n>', 'Max results per category (default 10)', '10')
|
|
714
758
|
.action(async (question, opts) => {
|
|
715
|
-
await run(() => (0, query_js_1.queryCommand)(question, { json: opts.json, limit: opts.limit }));
|
|
759
|
+
await run(() => (0, query_js_1.queryCommand)(question, { json: opts.json, share: opts.share, limit: opts.limit }));
|
|
716
760
|
});
|
|
717
761
|
// ── sf telegram ──────────────────────────────────────────────────────────────
|
|
718
762
|
program
|
package/dist/share.d.ts
ADDED
package/dist/share.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* --share helper: POST CLI output to /api/share and print the URL.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.shareOutput = shareOutput;
|
|
7
|
+
const SF_API_URL = process.env.SF_API_URL || 'https://simplefunctions.dev';
|
|
8
|
+
async function shareOutput(command, args, data) {
|
|
9
|
+
try {
|
|
10
|
+
const res = await fetch(`${SF_API_URL}/api/share`, {
|
|
11
|
+
method: 'POST',
|
|
12
|
+
headers: { 'Content-Type': 'application/json' },
|
|
13
|
+
body: JSON.stringify({ command, args, data }),
|
|
14
|
+
});
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
console.error(`Share failed: ${res.status}`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const { url } = await res.json();
|
|
20
|
+
console.log();
|
|
21
|
+
console.log(` \x1b[36m${url}\x1b[39m`);
|
|
22
|
+
console.log();
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
console.error(`Share failed: ${err.message}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill loader — reads .md files from the skills directory,
|
|
3
|
+
* parses frontmatter, and makes them available as slash commands.
|
|
4
|
+
*/
|
|
5
|
+
export interface Skill {
|
|
6
|
+
name: string;
|
|
7
|
+
trigger: string;
|
|
8
|
+
description: string;
|
|
9
|
+
author: string;
|
|
10
|
+
version: string;
|
|
11
|
+
category: string;
|
|
12
|
+
tags: string[];
|
|
13
|
+
toolsUsed: string[];
|
|
14
|
+
estimatedTime: string;
|
|
15
|
+
auto?: string;
|
|
16
|
+
prompt: string;
|
|
17
|
+
raw: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function loadSkills(): Skill[];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Skill loader — reads .md files from the skills directory,
|
|
4
|
+
* parses frontmatter, and makes them available as slash commands.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.loadSkills = loadSkills;
|
|
8
|
+
const fs_1 = require("fs");
|
|
9
|
+
const path_1 = require("path");
|
|
10
|
+
function loadSkills() {
|
|
11
|
+
const skills = [];
|
|
12
|
+
// Built-in skills from cli/src/skills/
|
|
13
|
+
// __dirname works in CJS; resolve to src/skills regardless of whether running from dist/ or src/
|
|
14
|
+
const thisDir = __dirname;
|
|
15
|
+
const skillsDir = thisDir.includes('dist')
|
|
16
|
+
? (0, path_1.join)(thisDir, '../../src/skills') // running from dist/skills/
|
|
17
|
+
: thisDir; // running from src/skills/
|
|
18
|
+
let files = [];
|
|
19
|
+
try {
|
|
20
|
+
files = (0, fs_1.readdirSync)(skillsDir).filter(f => f.endsWith('.md'));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return skills;
|
|
24
|
+
}
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
try {
|
|
27
|
+
const raw = (0, fs_1.readFileSync)((0, path_1.join)(skillsDir, file), 'utf-8');
|
|
28
|
+
const skill = parseSkillFile(raw);
|
|
29
|
+
if (skill)
|
|
30
|
+
skills.push(skill);
|
|
31
|
+
}
|
|
32
|
+
catch { }
|
|
33
|
+
}
|
|
34
|
+
// User skills from ~/.sf/skills/ (future extensibility)
|
|
35
|
+
try {
|
|
36
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
37
|
+
const userDir = (0, path_1.join)(home, '.sf', 'skills');
|
|
38
|
+
const userFiles = (0, fs_1.readdirSync)(userDir).filter(f => f.endsWith('.md'));
|
|
39
|
+
for (const file of userFiles) {
|
|
40
|
+
try {
|
|
41
|
+
const raw = (0, fs_1.readFileSync)((0, path_1.join)(userDir, file), 'utf-8');
|
|
42
|
+
const skill = parseSkillFile(raw);
|
|
43
|
+
if (skill)
|
|
44
|
+
skills.push(skill);
|
|
45
|
+
}
|
|
46
|
+
catch { }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch { }
|
|
50
|
+
return skills;
|
|
51
|
+
}
|
|
52
|
+
function parseSkillFile(raw) {
|
|
53
|
+
// Parse YAML frontmatter between --- delimiters
|
|
54
|
+
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
55
|
+
if (!match)
|
|
56
|
+
return null;
|
|
57
|
+
const frontmatter = match[1];
|
|
58
|
+
const body = match[2].trim();
|
|
59
|
+
const meta = {};
|
|
60
|
+
for (const line of frontmatter.split('\n')) {
|
|
61
|
+
const m = line.match(/^(\w[\w_]*)\s*:\s*(.+)$/);
|
|
62
|
+
if (m)
|
|
63
|
+
meta[m[1]] = m[2].trim();
|
|
64
|
+
}
|
|
65
|
+
if (!meta.name || !meta.trigger)
|
|
66
|
+
return null;
|
|
67
|
+
// Extract the "Instructions" section as the prompt (everything after ## Instructions)
|
|
68
|
+
const instructionsMatch = body.match(/## Instructions\n([\s\S]*)$/i);
|
|
69
|
+
const prompt = instructionsMatch ? instructionsMatch[1].trim() : body;
|
|
70
|
+
// Parse array fields: [a, b, c]
|
|
71
|
+
const parseArray = (s) => (s || '').replace(/[\[\]]/g, '').split(',').map(x => x.trim()).filter(Boolean);
|
|
72
|
+
return {
|
|
73
|
+
name: meta.name,
|
|
74
|
+
trigger: meta.trigger,
|
|
75
|
+
description: meta.description || '',
|
|
76
|
+
author: meta.author || 'unknown',
|
|
77
|
+
version: meta.version || '1.0.0',
|
|
78
|
+
category: meta.category || 'general',
|
|
79
|
+
tags: parseArray(meta.tags),
|
|
80
|
+
toolsUsed: parseArray(meta.tools_used),
|
|
81
|
+
estimatedTime: meta.estimated_time || '',
|
|
82
|
+
auto: meta.auto,
|
|
83
|
+
prompt,
|
|
84
|
+
raw,
|
|
85
|
+
};
|
|
86
|
+
}
|