@spfunctions/cli 1.6.1 → 1.7.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/dist/client.d.ts CHANGED
@@ -33,3 +33,6 @@ export declare function kalshiFetchEvents(seriesTicker: string): Promise<any[]>;
33
33
  export declare function kalshiFetchMarket(ticker: string): Promise<any>;
34
34
  export declare function kalshiFetchMarketsBySeries(seriesTicker: string): Promise<any[]>;
35
35
  export declare function kalshiFetchMarketsByEvent(eventTicker: string): Promise<any[]>;
36
+ export declare function fetchGlobalContext(): Promise<any>;
37
+ export declare function fetchQuery(q: string): Promise<any>;
38
+ export declare function fetchTraditionalMarkets(): Promise<any>;
package/dist/client.js CHANGED
@@ -12,6 +12,9 @@ exports.kalshiFetchEvents = kalshiFetchEvents;
12
12
  exports.kalshiFetchMarket = kalshiFetchMarket;
13
13
  exports.kalshiFetchMarketsBySeries = kalshiFetchMarketsBySeries;
14
14
  exports.kalshiFetchMarketsByEvent = kalshiFetchMarketsByEvent;
15
+ exports.fetchGlobalContext = fetchGlobalContext;
16
+ exports.fetchQuery = fetchQuery;
17
+ exports.fetchTraditionalMarkets = fetchTraditionalMarkets;
15
18
  const DEFAULT_API_URL = 'https://simplefunctions.dev';
16
19
  const KALSHI_BASE = 'https://api.elections.kalshi.com/trade-api/v2';
17
20
  // ===== SF API Client =====
@@ -154,3 +157,21 @@ async function kalshiFetchMarketsByEvent(eventTicker) {
154
157
  });
155
158
  return data.markets || [];
156
159
  }
160
+ // ===== Public Endpoints (no auth) =====
161
+ const SF_PUBLIC_BASE = 'https://simplefunctions.dev';
162
+ async function fetchGlobalContext() {
163
+ const res = await fetch(`${SF_PUBLIC_BASE}/api/public/context`);
164
+ if (!res.ok)
165
+ throw new Error(`Context API error: ${res.status}`);
166
+ return res.json();
167
+ }
168
+ async function fetchQuery(q) {
169
+ const res = await fetch(`${SF_PUBLIC_BASE}/api/public/query?q=${encodeURIComponent(q)}`);
170
+ if (!res.ok)
171
+ throw new Error(`Query API error: ${res.status}`);
172
+ return res.json();
173
+ }
174
+ async function fetchTraditionalMarkets() {
175
+ const ctx = await fetchGlobalContext();
176
+ return ctx.traditionalMarkets || [];
177
+ }
@@ -746,6 +746,30 @@ async function agentCommand(thesisId, opts) {
746
746
  };
747
747
  },
748
748
  },
749
+ {
750
+ name: 'global_context',
751
+ label: 'Market Snapshot',
752
+ description: 'Global market snapshot — top movers, expiring contracts, milestones, liquidity, signals. No thesis needed.',
753
+ parameters: emptyParams,
754
+ execute: async () => {
755
+ const { fetchGlobalContext } = await import('../client.js');
756
+ const data = await fetchGlobalContext();
757
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }], details: {} };
758
+ },
759
+ },
760
+ {
761
+ name: 'query',
762
+ label: 'Query',
763
+ description: 'LLM-enhanced prediction market knowledge search. Ask any question about prediction markets, macro, geopolitics. Returns structured answer with live market prices, thesis data, and key factors.',
764
+ parameters: Type.Object({
765
+ q: Type.String({ description: 'Natural language query (e.g. "iran oil prices", "fed rate cut 2026")' }),
766
+ }),
767
+ execute: async (_toolCallId, params) => {
768
+ const { fetchQuery } = await import('../client.js');
769
+ const data = await fetchQuery(params.q);
770
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }], details: {} };
771
+ },
772
+ },
749
773
  {
750
774
  name: 'inject_signal',
751
775
  label: 'Inject Signal',
@@ -2540,6 +2564,26 @@ async function runPlainTextAgent(params) {
2540
2564
  return { content: [{ type: 'text', text: JSON.stringify(ctx, null, 2) }], details: {} };
2541
2565
  },
2542
2566
  },
2567
+ {
2568
+ name: 'global_context', label: 'Market Snapshot',
2569
+ description: 'Global market snapshot — top movers, expiring contracts, milestones, liquidity, signals. No thesis needed.',
2570
+ parameters: emptyParams,
2571
+ execute: async () => {
2572
+ const { fetchGlobalContext } = await import('../client.js');
2573
+ const data = await fetchGlobalContext();
2574
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }], details: {} };
2575
+ },
2576
+ },
2577
+ {
2578
+ name: 'query', label: 'Query',
2579
+ description: 'LLM-enhanced prediction market knowledge search. Returns structured answer with live prices, thesis data, key factors.',
2580
+ parameters: Type.Object({ q: Type.String({ description: 'Natural language query' }) }),
2581
+ execute: async (_id, p) => {
2582
+ const { fetchQuery } = await import('../client.js');
2583
+ const data = await fetchQuery(p.q);
2584
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }], details: {} };
2585
+ },
2586
+ },
2543
2587
  {
2544
2588
  name: 'inject_signal', label: 'Inject Signal',
2545
2589
  description: 'Inject a signal into the thesis',
@@ -1,4 +1,4 @@
1
- export declare function contextCommand(id: string, opts: {
1
+ export declare function contextCommand(id: string | undefined, opts: {
2
2
  json?: boolean;
3
3
  apiKey?: string;
4
4
  apiUrl?: string;
@@ -5,6 +5,64 @@ 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
7
  async function contextCommand(id, opts) {
8
+ // No thesis ID → global market snapshot
9
+ if (!id) {
10
+ const res = await fetch('https://simplefunctions.dev/api/public/context');
11
+ if (!res.ok) {
12
+ console.error(` Error: ${res.status} ${await res.text()}`);
13
+ return;
14
+ }
15
+ const data = await res.json();
16
+ if (opts.json) {
17
+ console.log(JSON.stringify(data, null, 2));
18
+ return;
19
+ }
20
+ console.log(`\n${utils_js_1.c.bold}Market Snapshot${utils_js_1.c.reset} ${(0, utils_js_1.shortDate)(data.snapshotAt)}\n`);
21
+ if (data.edges?.length > 0) {
22
+ console.log(`${utils_js_1.c.bold}Thesis Edges${utils_js_1.c.reset}`);
23
+ for (const m of data.edges.slice(0, 10)) {
24
+ const venue = m.venue === 'kalshi' ? `${utils_js_1.c.cyan}K${utils_js_1.c.reset}` : `${utils_js_1.c.magenta}P${utils_js_1.c.reset}`;
25
+ const edgeStr = m.edge > 0 ? `${utils_js_1.c.green}+${m.edge}¢${utils_js_1.c.reset}` : `${utils_js_1.c.red}${m.edge}¢${utils_js_1.c.reset}`;
26
+ console.log(` ${venue} ${String(m.price).padStart(3)}¢ edge ${edgeStr.padStart(14)} ${m.title.slice(0, 50)}`);
27
+ }
28
+ console.log();
29
+ }
30
+ if (data.movers?.length > 0) {
31
+ console.log(`${utils_js_1.c.bold}Movers (24h)${utils_js_1.c.reset}`);
32
+ for (const m of data.movers.slice(0, 8)) {
33
+ const venue = m.venue === 'kalshi' ? `${utils_js_1.c.cyan}K${utils_js_1.c.reset}` : `${utils_js_1.c.magenta}P${utils_js_1.c.reset}`;
34
+ const ch = m.change24h > 0 ? `${utils_js_1.c.green}+${m.change24h}¢${utils_js_1.c.reset}` : `${utils_js_1.c.red}${m.change24h}¢${utils_js_1.c.reset}`;
35
+ console.log(` ${venue} ${String(m.price).padStart(3)}¢ ${ch.padStart(16)} ${m.title.slice(0, 55)}`);
36
+ }
37
+ console.log();
38
+ }
39
+ if (data.milestones?.length > 0) {
40
+ console.log(`${utils_js_1.c.bold}Upcoming (48h)${utils_js_1.c.reset}`);
41
+ for (const m of data.milestones.slice(0, 8)) {
42
+ console.log(` ${utils_js_1.c.dim}${String(m.hoursUntil).padStart(3)}h${utils_js_1.c.reset} ${m.title.slice(0, 55)} ${utils_js_1.c.dim}${m.category}${utils_js_1.c.reset}`);
43
+ }
44
+ console.log();
45
+ }
46
+ if (data.liquid?.length > 0) {
47
+ console.log(`${utils_js_1.c.bold}Liquid${utils_js_1.c.reset}`);
48
+ for (const m of data.liquid.slice(0, 8)) {
49
+ const venue = m.venue === 'kalshi' ? `${utils_js_1.c.cyan}K${utils_js_1.c.reset}` : `${utils_js_1.c.magenta}P${utils_js_1.c.reset}`;
50
+ console.log(` ${venue} ${String(m.price).padStart(3)}¢ spread ${m.spread}¢ ${m.title.slice(0, 55)}`);
51
+ }
52
+ console.log();
53
+ }
54
+ if (data.signals?.length > 0) {
55
+ console.log(`${utils_js_1.c.bold}Recent Signals${utils_js_1.c.reset}`);
56
+ for (const s of data.signals.slice(0, 5)) {
57
+ const d = s.confidenceDelta > 0 ? `${utils_js_1.c.green}▲${s.confidenceDelta}${utils_js_1.c.reset}` : s.confidenceDelta < 0 ? `${utils_js_1.c.red}▼${s.confidenceDelta}${utils_js_1.c.reset}` : `${s.confidenceDelta}`;
58
+ console.log(` ${d} ${s.confidence}% ${s.summary.slice(0, 60)}`);
59
+ console.log(` ${utils_js_1.c.dim}${s.thesisTitle.slice(0, 40)} ${(0, utils_js_1.shortDate)(s.evaluatedAt)}${utils_js_1.c.reset}`);
60
+ }
61
+ console.log();
62
+ }
63
+ console.log(` ${utils_js_1.c.dim}Create a thesis for focused context: sf create "your market view"${utils_js_1.c.reset}\n`);
64
+ return;
65
+ }
8
66
  const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
9
67
  const ctx = await client.getContext(id);
10
68
  if (opts.json) {
@@ -62,11 +62,18 @@ async function queryCommand(q, opts) {
62
62
  }
63
63
  console.log();
64
64
  }
65
- // Thesis
66
- if (data.thesis) {
65
+ // Theses
66
+ if (data.theses?.length > 0) {
67
+ console.log(` \x1b[1mTheses\x1b[22m`);
68
+ for (const t of data.theses.slice(0, 3)) {
69
+ console.log(` ${t.confidence || '?'}% ${String(t.edges || 0).padStart(2)} edges ${t.title.slice(0, 50)}`);
70
+ }
71
+ console.log();
72
+ }
73
+ else if (data.thesis) {
74
+ // Backward compat
67
75
  const t = data.thesis;
68
76
  console.log(` \x1b[1mThesis\x1b[22m`);
69
- console.log(` ${t.title.slice(0, 60)}`);
70
77
  console.log(` ${t.confidence}% confidence ${t.edges} edges /thesis/${t.slug}`);
71
78
  console.log();
72
79
  }
@@ -5,6 +5,9 @@ exports.sellCommand = sellCommand;
5
5
  const kalshi_js_1 = require("../kalshi.js");
6
6
  const config_js_1 = require("../config.js");
7
7
  const utils_js_1 = require("../utils.js");
8
+ const fs_1 = require("fs");
9
+ const path_1 = require("path");
10
+ const os_1 = require("os");
8
11
  async function buyCommand(ticker, qty, opts) {
9
12
  (0, config_js_1.requireTrading)();
10
13
  await executeOrder(ticker, qty, 'buy', opts);
@@ -62,6 +65,27 @@ async function executeOrder(ticker, qty, action, opts) {
62
65
  if (order.fill_count_fp)
63
66
  console.log(` Filled: ${order.fill_count_fp}/${order.initial_count_fp || quantity}`);
64
67
  console.log();
68
+ // Auto-log trade to journal
69
+ try {
70
+ const journalDir = (0, path_1.join)((0, os_1.homedir)(), '.sf');
71
+ if (!(0, fs_1.existsSync)(journalDir))
72
+ (0, fs_1.mkdirSync)(journalDir, { recursive: true });
73
+ const entry = JSON.stringify({
74
+ ts: new Date().toISOString(),
75
+ action,
76
+ ticker,
77
+ side,
78
+ quantity,
79
+ price: priceCents || null,
80
+ type: orderType,
81
+ orderId: order.order_id || null,
82
+ status: order.status || null,
83
+ filled: order.fill_count_fp || null,
84
+ }) + '\n';
85
+ (0, fs_1.appendFileSync)((0, path_1.join)(journalDir, 'trade-journal.jsonl'), entry);
86
+ console.log(` ${utils_js_1.c.dim}Trade logged to ~/.sf/trade-journal.jsonl${utils_js_1.c.reset}`);
87
+ }
88
+ catch { /* journal logging is best-effort */ }
65
89
  }
66
90
  catch (err) {
67
91
  const msg = err.message || String(err);
package/dist/index.js CHANGED
@@ -77,7 +77,7 @@ const GROUPED_HELP = `
77
77
  \x1b[1mThesis\x1b[22m
78
78
  \x1b[36mlist\x1b[39m List all theses
79
79
  \x1b[36mget\x1b[39m <id> Full thesis details
80
- \x1b[36mcontext\x1b[39m <id> [--json] Thesis snapshot \x1b[2m(primary for agents)\x1b[22m
80
+ \x1b[36mcontext\x1b[39m [id] [--json] Market snapshot (no id) or thesis context (with id)
81
81
  \x1b[36mcreate\x1b[39m "thesis" Create a new thesis
82
82
  \x1b[36msignal\x1b[39m <id> "content" Inject a signal
83
83
  \x1b[36mevaluate\x1b[39m <id> Trigger deep evaluation
@@ -221,7 +221,7 @@ async function interactiveEntry() {
221
221
  console.log();
222
222
  }
223
223
  // ── Pre-action guard: check configuration ────────────────────────────────────
224
- const NO_CONFIG_COMMANDS = new Set(['setup', 'help', 'scan', 'explore', 'query', 'milestones', 'forecast', 'settlements', 'balance', 'orders', 'fills', 'schedule', 'announcements', 'history', 'liquidity', 'book', 'prompt', 'sf']);
224
+ const NO_CONFIG_COMMANDS = new Set(['setup', 'help', 'scan', 'explore', 'query', 'context', 'milestones', 'forecast', 'settlements', 'balance', 'orders', 'fills', 'schedule', 'announcements', 'history', 'liquidity', 'book', 'prompt', 'sf']);
225
225
  program.hook('preAction', (thisCommand, actionCommand) => {
226
226
  const cmdName = actionCommand.name();
227
227
  if (NO_CONFIG_COMMANDS.has(cmdName))
@@ -279,10 +279,10 @@ program
279
279
  const g = cmd.optsWithGlobals();
280
280
  await run(() => (0, get_js_1.getCommand)(id, { json: opts.json, apiKey: g.apiKey, apiUrl: g.apiUrl }));
281
281
  });
282
- // ── sf context <id> ───────────────────────────────────────────────────────────
282
+ // ── sf context [id] ───────────────────────────────────────────────────────────
283
283
  program
284
- .command('context <id>')
285
- .description('Get thesis context snapshot (primary command for agents)')
284
+ .command('context [id]')
285
+ .description('Context snapshot. With ID: thesis-specific. Without: global market snapshot (no auth)')
286
286
  .option('--json', 'Output raw JSON')
287
287
  .action(async (id, opts, cmd) => {
288
288
  const g = cmd.optsWithGlobals();
@@ -38,6 +38,26 @@ async function buildTools(sfClient, thesisId, latestContext) {
38
38
  return { content: [{ type: 'text', text: JSON.stringify(ctx, null, 2) }], details: {} };
39
39
  },
40
40
  },
41
+ {
42
+ name: 'global_context', label: 'Snapshot',
43
+ description: 'Global market snapshot — movers, expiring, milestones, liquidity, signals. No thesis needed.',
44
+ parameters: emptyParams,
45
+ execute: async () => {
46
+ const { fetchGlobalContext } = await import('../client.js');
47
+ const data = await fetchGlobalContext();
48
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }], details: {} };
49
+ },
50
+ },
51
+ {
52
+ name: 'query', label: 'Query',
53
+ description: 'LLM-enhanced prediction market search. Returns answer, live prices, key factors.',
54
+ parameters: Type.Object({ q: Type.String({ description: 'Question' }) }),
55
+ execute: async (_id, p) => {
56
+ const { fetchQuery } = await import('../client.js');
57
+ const data = await fetchQuery(p.q);
58
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }], details: {} };
59
+ },
60
+ },
41
61
  {
42
62
  name: 'inject_signal', label: 'Signal',
43
63
  description: 'Inject a signal (news, note, observation) into the thesis',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spfunctions/cli",
3
- "version": "1.6.1",
3
+ "version": "1.7.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"