@spfunctions/cli 1.7.14 → 1.7.17
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 +633 -183
- 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 +13 -7
- package/dist/commands/explore.d.ts +1 -0
- package/dist/commands/explore.js +5 -0
- package/dist/commands/markets.d.ts +1 -0
- package/dist/commands/markets.js +5 -0
- 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 +7 -2
- package/dist/commands/watch.d.ts +19 -0
- package/dist/commands/watch.js +157 -0
- package/dist/index.js +28 -4
- 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/package.json +1 -1
package/dist/commands/context.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.contextCommand = contextCommand;
|
|
|
4
4
|
const client_js_1 = require("../client.js");
|
|
5
5
|
const kalshi_js_1 = require("../kalshi.js");
|
|
6
6
|
const utils_js_1 = require("../utils.js");
|
|
7
|
+
const share_js_1 = require("../share.js");
|
|
7
8
|
const SF_API_URL = process.env.SF_API_URL || 'https://simplefunctions.dev';
|
|
8
9
|
async function contextCommand(id, opts) {
|
|
9
10
|
// ── Mode 1: No thesis ID → global market intelligence ─────────────────────
|
|
@@ -14,6 +15,10 @@ async function contextCommand(id, opts) {
|
|
|
14
15
|
return;
|
|
15
16
|
}
|
|
16
17
|
const data = await res.json();
|
|
18
|
+
if (opts.share) {
|
|
19
|
+
await (0, share_js_1.shareOutput)('context', '', data);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
17
22
|
if (opts.json) {
|
|
18
23
|
console.log(JSON.stringify(data, null, 2));
|
|
19
24
|
return;
|
package/dist/commands/edges.d.ts
CHANGED
package/dist/commands/edges.js
CHANGED
|
@@ -17,6 +17,7 @@ exports.edgesCommand = edgesCommand;
|
|
|
17
17
|
const client_js_1 = require("../client.js");
|
|
18
18
|
const kalshi_js_1 = require("../kalshi.js");
|
|
19
19
|
const utils_js_1 = require("../utils.js");
|
|
20
|
+
const share_js_1 = require("../share.js");
|
|
20
21
|
async function edgesCommand(opts) {
|
|
21
22
|
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
22
23
|
const limit = parseInt(opts.limit || '20');
|
|
@@ -141,14 +142,19 @@ async function edgesCommand(opts) {
|
|
|
141
142
|
}
|
|
142
143
|
// Apply limit
|
|
143
144
|
const display = merged.slice(0, limit);
|
|
144
|
-
// ── Step 6: JSON output
|
|
145
|
+
// ── Step 6: JSON / share output ────────────────────────────────────────────
|
|
146
|
+
const outputData = {
|
|
147
|
+
totalEdges: merged.length,
|
|
148
|
+
displayed: display.length,
|
|
149
|
+
thesesScanned: theses.length,
|
|
150
|
+
edges: display,
|
|
151
|
+
};
|
|
152
|
+
if (opts.share) {
|
|
153
|
+
await (0, share_js_1.shareOutput)('edges', '', outputData);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
145
156
|
if (opts.json) {
|
|
146
|
-
console.log(JSON.stringify(
|
|
147
|
-
totalEdges: merged.length,
|
|
148
|
-
displayed: display.length,
|
|
149
|
-
thesesScanned: theses.length,
|
|
150
|
-
edges: display,
|
|
151
|
-
}, null, 2));
|
|
157
|
+
console.log(JSON.stringify(outputData, null, 2));
|
|
152
158
|
return;
|
|
153
159
|
}
|
|
154
160
|
// ── Step 6: Pretty output ──────────────────────────────────────────────────
|
package/dist/commands/explore.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.exploreCommand = exploreCommand;
|
|
14
|
+
const share_js_1 = require("../share.js");
|
|
14
15
|
const BASE_URL = 'https://simplefunctions.dev';
|
|
15
16
|
async function exploreCommand(slug, opts) {
|
|
16
17
|
if (!slug) {
|
|
@@ -21,6 +22,10 @@ async function exploreCommand(slug, opts) {
|
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
24
|
const { theses } = await res.json();
|
|
25
|
+
if (opts?.share) {
|
|
26
|
+
await (0, share_js_1.shareOutput)('explore', '', { theses });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
24
29
|
if (opts?.json) {
|
|
25
30
|
console.log(JSON.stringify(theses, null, 2));
|
|
26
31
|
return;
|
package/dist/commands/markets.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.marketsCommand = marketsCommand;
|
|
10
|
+
const share_js_1 = require("../share.js");
|
|
10
11
|
const BASE_URL = 'https://simplefunctions.dev';
|
|
11
12
|
async function marketsCommand(opts) {
|
|
12
13
|
const res = await fetch(`${BASE_URL}/api/public/markets`);
|
|
@@ -15,6 +16,10 @@ async function marketsCommand(opts) {
|
|
|
15
16
|
return;
|
|
16
17
|
}
|
|
17
18
|
const data = await res.json();
|
|
19
|
+
if (opts?.share) {
|
|
20
|
+
await (0, share_js_1.shareOutput)('markets', '', data);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
18
23
|
if (opts?.json) {
|
|
19
24
|
console.log(JSON.stringify(data, null, 2));
|
|
20
25
|
return;
|
package/dist/commands/query.d.ts
CHANGED
package/dist/commands/query.js
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.queryCommand = queryCommand;
|
|
14
|
-
const
|
|
14
|
+
const share_js_1 = require("../share.js");
|
|
15
|
+
const BASE_URL = process.env.SF_API_URL || 'https://simplefunctions.dev';
|
|
15
16
|
async function queryCommand(q, opts) {
|
|
16
17
|
const limit = opts?.limit || '10';
|
|
17
18
|
const url = `${BASE_URL}/api/public/query?q=${encodeURIComponent(q)}&limit=${limit}`;
|
|
@@ -27,6 +28,10 @@ async function queryCommand(q, opts) {
|
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
29
30
|
const data = await res.json();
|
|
31
|
+
if (opts?.share) {
|
|
32
|
+
await (0, share_js_1.shareOutput)('query', q, data);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
30
35
|
if (opts?.json) {
|
|
31
36
|
console.log(JSON.stringify(data, null, 2));
|
|
32
37
|
return;
|
package/dist/commands/scan.d.ts
CHANGED
package/dist/commands/scan.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.scanCommand = scanCommand;
|
|
|
4
4
|
const client_js_1 = require("../client.js");
|
|
5
5
|
const polymarket_js_1 = require("../polymarket.js");
|
|
6
6
|
const utils_js_1 = require("../utils.js");
|
|
7
|
+
const share_js_1 = require("../share.js");
|
|
7
8
|
// SF API base — scan goes through the server which holds the proxy key
|
|
8
9
|
const SF_API_URL = process.env.SF_API_URL || 'https://simplefunctions.dev';
|
|
9
10
|
async function scanCommand(query, opts) {
|
|
@@ -18,7 +19,7 @@ async function scanCommand(query, opts) {
|
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
20
21
|
// Mode 3: keyword scan across all series + Polymarket
|
|
21
|
-
await keywordScan(query, opts.json, opts.venue || 'all');
|
|
22
|
+
await keywordScan(query, opts.json, opts.venue || 'all', opts.share);
|
|
22
23
|
}
|
|
23
24
|
async function showMarket(ticker, json) {
|
|
24
25
|
console.log(`${utils_js_1.c.dim}Fetching market ${ticker}...${utils_js_1.c.reset}`);
|
|
@@ -80,7 +81,7 @@ async function showSeries(seriesTicker, json) {
|
|
|
80
81
|
printMarketsTable(markets);
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
|
-
async function keywordScan(query, json, venue = 'all') {
|
|
84
|
+
async function keywordScan(query, json, venue = 'all', share) {
|
|
84
85
|
// ── Polymarket search (runs in parallel with Kalshi) ───────────────────────
|
|
85
86
|
const polyPromise = (venue === 'kalshi')
|
|
86
87
|
? Promise.resolve([])
|
|
@@ -154,6 +155,10 @@ async function keywordScan(query, json, venue = 'all') {
|
|
|
154
155
|
}
|
|
155
156
|
const combined = [...allMarkets, ...polyMarkets];
|
|
156
157
|
combined.sort((a, b) => b.liquidity - a.liquidity);
|
|
158
|
+
if (share) {
|
|
159
|
+
await (0, share_js_1.shareOutput)('scan', query, combined);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
157
162
|
if (json) {
|
|
158
163
|
console.log(JSON.stringify(combined, null, 2));
|
|
159
164
|
return;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sf watch
|
|
3
|
+
*
|
|
4
|
+
* Push-mode monitoring. Polls GET /api/changes?since= for server-side
|
|
5
|
+
* detected market changes. No client-side diffing — the server handles
|
|
6
|
+
* change detection every 15 minutes via the scan-prices cron.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* sf watch "gold" — Watch gold-related changes
|
|
10
|
+
* sf watch "iran oil" — Watch iran/oil changes
|
|
11
|
+
* sf watch — Watch all market changes
|
|
12
|
+
* sf watch --json — JSON change events for piping
|
|
13
|
+
*/
|
|
14
|
+
interface WatchOpts {
|
|
15
|
+
interval?: string;
|
|
16
|
+
json?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function watchCommand(query: string | undefined, opts: WatchOpts): Promise<void>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* sf watch
|
|
4
|
+
*
|
|
5
|
+
* Push-mode monitoring. Polls GET /api/changes?since= for server-side
|
|
6
|
+
* detected market changes. No client-side diffing — the server handles
|
|
7
|
+
* change detection every 15 minutes via the scan-prices cron.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* sf watch "gold" — Watch gold-related changes
|
|
11
|
+
* sf watch "iran oil" — Watch iran/oil changes
|
|
12
|
+
* sf watch — Watch all market changes
|
|
13
|
+
* sf watch --json — JSON change events for piping
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.watchCommand = watchCommand;
|
|
17
|
+
const utils_js_1 = require("../utils.js");
|
|
18
|
+
const SF_API_URL = process.env.SF_API_URL || 'https://simplefunctions.dev';
|
|
19
|
+
async function watchCommand(query, opts) {
|
|
20
|
+
const intervalSec = parseInt(opts.interval || '60');
|
|
21
|
+
const intervalMs = intervalSec * 1000;
|
|
22
|
+
const isJson = opts.json || false;
|
|
23
|
+
if (!isJson) {
|
|
24
|
+
console.log();
|
|
25
|
+
console.log(` ${utils_js_1.c.bold}sf watch${utils_js_1.c.reset}${query ? ` "${query}"` : ''}`);
|
|
26
|
+
console.log(` ${utils_js_1.c.dim}Polling /api/changes every ${intervalSec}s · Ctrl+C to stop${utils_js_1.c.reset}`);
|
|
27
|
+
console.log();
|
|
28
|
+
}
|
|
29
|
+
let lastSeen = new Date().toISOString();
|
|
30
|
+
// Handle Ctrl+C gracefully
|
|
31
|
+
process.on('SIGINT', () => {
|
|
32
|
+
if (!isJson) {
|
|
33
|
+
console.log(`\n ${utils_js_1.c.dim}Watch stopped.${utils_js_1.c.reset}\n`);
|
|
34
|
+
}
|
|
35
|
+
process.exit(0);
|
|
36
|
+
});
|
|
37
|
+
// First poll: show recent changes from last hour
|
|
38
|
+
try {
|
|
39
|
+
const initialSince = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
|
40
|
+
const changes = await fetchChanges(initialSince, query);
|
|
41
|
+
if (changes.length > 0) {
|
|
42
|
+
if (!isJson) {
|
|
43
|
+
console.log(` ${utils_js_1.c.dim}${ts()} Recent changes (last hour):${utils_js_1.c.reset}`);
|
|
44
|
+
}
|
|
45
|
+
const deduped = dedupeByTitle(changes);
|
|
46
|
+
for (const change of deduped) {
|
|
47
|
+
if (isJson) {
|
|
48
|
+
console.log(JSON.stringify(change));
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
printChange(change);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
lastSeen = changes[0].detectedAt;
|
|
55
|
+
}
|
|
56
|
+
else if (!isJson) {
|
|
57
|
+
console.log(` ${utils_js_1.c.dim}${ts()} No recent changes. Watching...${utils_js_1.c.reset}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (!isJson) {
|
|
62
|
+
console.error(` ${utils_js_1.c.dim}${ts()} Error: ${err.message}${utils_js_1.c.reset}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Polling loop
|
|
66
|
+
while (true) {
|
|
67
|
+
await sleep(intervalMs);
|
|
68
|
+
try {
|
|
69
|
+
const changes = await fetchChanges(lastSeen, query);
|
|
70
|
+
const deduped = isJson ? changes : dedupeByTitle(changes);
|
|
71
|
+
for (const change of deduped) {
|
|
72
|
+
if (isJson) {
|
|
73
|
+
console.log(JSON.stringify(change));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
printChange(change);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (changes.length > 0) {
|
|
80
|
+
lastSeen = changes[0].detectedAt;
|
|
81
|
+
}
|
|
82
|
+
else if (!isJson) {
|
|
83
|
+
process.stdout.write(` ${utils_js_1.c.dim}${ts()} · no new changes${utils_js_1.c.reset}\r`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
if (!isJson) {
|
|
88
|
+
console.error(` ${utils_js_1.c.dim}${ts()} Error: ${err.message}${utils_js_1.c.reset}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Group same-title same-type changes into one line (e.g. 12 FaZe CEO contracts → 1 line)
|
|
94
|
+
function dedupeByTitle(changes) {
|
|
95
|
+
const groups = new Map();
|
|
96
|
+
for (const c of changes) {
|
|
97
|
+
const key = `${c.changeType}:${c.title}`;
|
|
98
|
+
const existing = groups.get(key);
|
|
99
|
+
if (existing) {
|
|
100
|
+
existing.count++;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
groups.set(key, { count: 1, representative: c });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return Array.from(groups.values()).map(({ count, representative }) => {
|
|
107
|
+
if (count > 1) {
|
|
108
|
+
return { ...representative, title: `${representative.title} (${count} contracts)` };
|
|
109
|
+
}
|
|
110
|
+
return representative;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
async function fetchChanges(since, query) {
|
|
114
|
+
let url = `${SF_API_URL}/api/changes?since=${encodeURIComponent(since)}&limit=50`;
|
|
115
|
+
if (query)
|
|
116
|
+
url += `&q=${encodeURIComponent(query)}`;
|
|
117
|
+
const res = await fetch(url);
|
|
118
|
+
if (!res.ok)
|
|
119
|
+
return [];
|
|
120
|
+
const data = await res.json();
|
|
121
|
+
return data.changes || [];
|
|
122
|
+
}
|
|
123
|
+
function printChange(change) {
|
|
124
|
+
const time = ts();
|
|
125
|
+
const venue = change.venue === 'kalshi' ? `${utils_js_1.c.cyan}K${utils_js_1.c.reset}` :
|
|
126
|
+
change.venue === 'polymarket' ? `\x1b[35mP\x1b[39m` :
|
|
127
|
+
change.venue === 'traditional' ? `${utils_js_1.c.dim}$${utils_js_1.c.reset}` : '·';
|
|
128
|
+
switch (change.changeType) {
|
|
129
|
+
case 'new_contract':
|
|
130
|
+
console.log(` ${time} ${venue} ${utils_js_1.c.green}+ NEW${utils_js_1.c.reset} ${change.afterPrice}¢ ${change.title}`);
|
|
131
|
+
break;
|
|
132
|
+
case 'removed_contract':
|
|
133
|
+
console.log(` ${time} ${venue} ${utils_js_1.c.red}− GONE${utils_js_1.c.reset} ${change.title}`);
|
|
134
|
+
break;
|
|
135
|
+
case 'price_move': {
|
|
136
|
+
if (change.venue === 'traditional') {
|
|
137
|
+
const sign = (change.delta || 0) > 0 ? '+' : '';
|
|
138
|
+
const color = (change.delta || 0) > 0 ? utils_js_1.c.green : utils_js_1.c.red;
|
|
139
|
+
const beforeDollars = ((change.beforePrice || 0) / 100).toFixed(0);
|
|
140
|
+
const afterDollars = ((change.afterPrice || 0) / 100).toFixed(0);
|
|
141
|
+
console.log(` ${time} ${venue} ${color}${sign}${change.delta}¢${utils_js_1.c.reset} $${beforeDollars}→$${afterDollars} ${change.title}`);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
const sign = (change.delta || 0) > 0 ? '+' : '';
|
|
145
|
+
const color = (change.delta || 0) > 0 ? utils_js_1.c.green : utils_js_1.c.red;
|
|
146
|
+
console.log(` ${time} ${venue} ${color}${sign}${change.delta}¢${utils_js_1.c.reset} ${change.beforePrice}→${change.afterPrice}¢ ${change.title}`);
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function ts() {
|
|
153
|
+
return new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
154
|
+
}
|
|
155
|
+
function sleep(ms) {
|
|
156
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
157
|
+
}
|
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");
|
|
@@ -99,12 +100,18 @@ const GROUPED_HELP = `
|
|
|
99
100
|
\x1b[36mscan\x1b[39m "keywords" Search Kalshi + Polymarket
|
|
100
101
|
\x1b[36mscan\x1b[39m --series TICKER Browse a Kalshi series
|
|
101
102
|
\x1b[36medges\x1b[39m [--json] Top edges across all theses
|
|
103
|
+
\x1b[36mwatch\x1b[39m [query] Watch for market changes (e.g. sf watch "gold")
|
|
102
104
|
\x1b[36mwhatif\x1b[39m <id> What-if scenario analysis
|
|
103
105
|
\x1b[36mliquidity\x1b[39m [topic] Orderbook liquidity scanner
|
|
104
106
|
\x1b[36mbook\x1b[39m <ticker> [ticker2...] Orderbook depth for specific markets
|
|
105
107
|
\x1b[36mexplore\x1b[39m [slug] Browse public theses
|
|
106
108
|
\x1b[36mforecast\x1b[39m <event> Market distribution (P50/P75/P90)
|
|
107
109
|
|
|
110
|
+
\x1b[1mShare\x1b[22m \x1b[2m(any command with --share generates a short URL)\x1b[22m
|
|
111
|
+
\x1b[36mscan\x1b[39m "gold" --share Share scan results
|
|
112
|
+
\x1b[36mquery\x1b[39m "fed rate" --share Share query results
|
|
113
|
+
\x1b[36mcontext\x1b[39m --share Share global context snapshot
|
|
114
|
+
|
|
108
115
|
\x1b[1mPortfolio\x1b[22m
|
|
109
116
|
\x1b[36mpositions\x1b[39m Kalshi + Polymarket positions
|
|
110
117
|
\x1b[36mbalance\x1b[39m Account balance
|
|
@@ -312,9 +319,10 @@ program
|
|
|
312
319
|
.command('context [id]')
|
|
313
320
|
.description('Context snapshot. With ID: thesis-specific. Without: global market snapshot (no auth)')
|
|
314
321
|
.option('--json', 'Output raw JSON')
|
|
322
|
+
.option('--share', 'Share output via short URL')
|
|
315
323
|
.action(async (id, opts, cmd) => {
|
|
316
324
|
const g = cmd.optsWithGlobals();
|
|
317
|
-
await run(() => (0, context_js_1.contextCommand)(id, { json: opts.json, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
325
|
+
await run(() => (0, context_js_1.contextCommand)(id, { json: opts.json, share: opts.share, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
318
326
|
});
|
|
319
327
|
// ── sf create <thesis> ────────────────────────────────────────────────────────
|
|
320
328
|
program
|
|
@@ -376,6 +384,7 @@ program
|
|
|
376
384
|
.option('--market <ticker>', 'Get single market detail (e.g. KXWTIMAX-26DEC31-T140)')
|
|
377
385
|
.option('--venue <venue>', 'Filter by venue: kalshi, polymarket, or all (default: all)')
|
|
378
386
|
.option('--json', 'Output raw JSON')
|
|
387
|
+
.option('--share', 'Share output via short URL')
|
|
379
388
|
.action(async (query, opts, cmd) => {
|
|
380
389
|
const g = cmd.optsWithGlobals();
|
|
381
390
|
const q = query || '';
|
|
@@ -388,15 +397,26 @@ program
|
|
|
388
397
|
market: opts.market,
|
|
389
398
|
venue: opts.venue,
|
|
390
399
|
json: opts.json,
|
|
400
|
+
share: opts.share,
|
|
391
401
|
apiKey: g.apiKey,
|
|
392
402
|
apiUrl: g.apiUrl,
|
|
393
403
|
}));
|
|
394
404
|
});
|
|
405
|
+
// ── sf watch [query] ─────────────────────────────────────────────────────────
|
|
406
|
+
program
|
|
407
|
+
.command('watch [query]')
|
|
408
|
+
.description('Watch for market changes (e.g. sf watch "gold")')
|
|
409
|
+
.option('--interval <seconds>', 'Poll interval in seconds (default: 60)', '60')
|
|
410
|
+
.option('--json', 'JSON change events (for piping)')
|
|
411
|
+
.action(async (query, opts) => {
|
|
412
|
+
await run(() => (0, watch_js_1.watchCommand)(query, { interval: opts.interval, json: opts.json }));
|
|
413
|
+
});
|
|
395
414
|
// ── sf edges ──────────────────────────────────────────────────────────────────
|
|
396
415
|
program
|
|
397
416
|
.command('edges')
|
|
398
417
|
.description('Top edges across all theses — what to trade now')
|
|
399
418
|
.option('--json', 'JSON output for agents')
|
|
419
|
+
.option('--share', 'Share output via short URL')
|
|
400
420
|
.option('--limit <n>', 'Max edges to show', '20')
|
|
401
421
|
.option('--thesis <id>', 'Filter to a single thesis')
|
|
402
422
|
.option('--min-edge <cents>', 'Minimum absolute edge size in cents')
|
|
@@ -406,6 +426,7 @@ program
|
|
|
406
426
|
const g = cmd.optsWithGlobals();
|
|
407
427
|
await run(() => (0, edges_js_1.edgesCommand)({
|
|
408
428
|
json: opts.json,
|
|
429
|
+
share: opts.share,
|
|
409
430
|
limit: opts.limit,
|
|
410
431
|
thesis: opts.thesis,
|
|
411
432
|
minEdge: opts.minEdge,
|
|
@@ -465,8 +486,9 @@ program
|
|
|
465
486
|
.command('explore [slug]')
|
|
466
487
|
.description('Browse public theses (no auth required)')
|
|
467
488
|
.option('--json', 'JSON output')
|
|
489
|
+
.option('--share', 'Share output via short URL')
|
|
468
490
|
.action(async (slug, opts) => {
|
|
469
|
-
await run(() => (0, explore_js_1.exploreCommand)(slug, { json: opts.json }));
|
|
491
|
+
await run(() => (0, explore_js_1.exploreCommand)(slug, { json: opts.json, share: opts.share }));
|
|
470
492
|
});
|
|
471
493
|
// ── sf dashboard ──────────────────────────────────────────────────────────────
|
|
472
494
|
program
|
|
@@ -702,17 +724,19 @@ program
|
|
|
702
724
|
.command('markets')
|
|
703
725
|
.description('Traditional market snapshot — SPY, VIX, Treasury, Gold, Oil (no auth)')
|
|
704
726
|
.option('--json', 'JSON output')
|
|
727
|
+
.option('--share', 'Share output via short URL')
|
|
705
728
|
.action(async (opts) => {
|
|
706
|
-
await run(() => (0, markets_js_1.marketsCommand)({ json: opts.json }));
|
|
729
|
+
await run(() => (0, markets_js_1.marketsCommand)({ json: opts.json, share: opts.share }));
|
|
707
730
|
});
|
|
708
731
|
// ── sf query "question" ──────────────────────────────────────────────────────
|
|
709
732
|
program
|
|
710
733
|
.command('query <question>')
|
|
711
734
|
.description('LLM-enhanced prediction market knowledge search (no auth required)')
|
|
712
735
|
.option('--json', 'JSON output')
|
|
736
|
+
.option('--share', 'Share output via short URL')
|
|
713
737
|
.option('--limit <n>', 'Max results per category (default 10)', '10')
|
|
714
738
|
.action(async (question, opts) => {
|
|
715
|
-
await run(() => (0, query_js_1.queryCommand)(question, { json: opts.json, limit: opts.limit }));
|
|
739
|
+
await run(() => (0, query_js_1.queryCommand)(question, { json: opts.json, share: opts.share, limit: opts.limit }));
|
|
716
740
|
});
|
|
717
741
|
// ── sf telegram ──────────────────────────────────────────────────────────────
|
|
718
742
|
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
|
+
}
|