@spfunctions/cli 0.1.0
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 +100 -0
- package/dist/client.d.ts +26 -0
- package/dist/client.js +117 -0
- package/dist/commands/context.d.ts +5 -0
- package/dist/commands/context.js +70 -0
- package/dist/commands/create.d.ts +5 -0
- package/dist/commands/create.js +31 -0
- package/dist/commands/evaluate.d.ts +4 -0
- package/dist/commands/evaluate.js +30 -0
- package/dist/commands/get.d.ts +5 -0
- package/dist/commands/get.js +98 -0
- package/dist/commands/list.d.ts +4 -0
- package/dist/commands/list.js +34 -0
- package/dist/commands/scan.d.ts +9 -0
- package/dist/commands/scan.js +198 -0
- package/dist/commands/signal.d.ts +5 -0
- package/dist/commands/signal.js +18 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +127 -0
- package/dist/utils.d.ts +42 -0
- package/dist/utils.js +124 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# SimpleFunctions CLI (`sf`)
|
|
2
|
+
|
|
3
|
+
Prediction market thesis agent CLI. Pure HTTP client — no project dependencies.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g simplefunctions
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Configuration
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
export SF_API_KEY=sf_live_xxx # required
|
|
15
|
+
export SF_API_URL=https://simplefunctions.com # optional, defaults to production
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or pass inline:
|
|
19
|
+
```bash
|
|
20
|
+
sf --api-key sf_live_xxx list
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Commands
|
|
24
|
+
|
|
25
|
+
### `sf list`
|
|
26
|
+
List all theses.
|
|
27
|
+
```
|
|
28
|
+
ID Status Conf Updated Title
|
|
29
|
+
f582bf76 active 82% Mar 12 11:13 Trump cannot exit the Iran war...
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### `sf get <id>`
|
|
33
|
+
Full thesis details: causal tree, edge analysis, positions, last evaluation.
|
|
34
|
+
```bash
|
|
35
|
+
sf get f582bf76
|
|
36
|
+
sf get f582bf76 --json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### `sf context <id>`
|
|
40
|
+
**Primary command for agents.** Returns a compact context snapshot: thesis, confidence, causal tree nodes, top edges, positions, last evaluation summary.
|
|
41
|
+
```bash
|
|
42
|
+
sf context f582bf76
|
|
43
|
+
sf context f582bf76 --json # machine-readable for agent parsing
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### `sf create "thesis text"`
|
|
47
|
+
Create a new thesis. Sync by default (waits for formation agent to complete).
|
|
48
|
+
```bash
|
|
49
|
+
sf create "Trump cannot exit the Iran war gracefully before 2027"
|
|
50
|
+
sf create "..." --async # return immediately
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### `sf signal <id> "content"`
|
|
54
|
+
Inject a signal into the thesis queue. Queued for next monitor cycle.
|
|
55
|
+
```bash
|
|
56
|
+
sf signal f582bf76 "Oil closes at $95 today"
|
|
57
|
+
sf signal f582bf76 "Iran closes Strait of Hormuz" --type news
|
|
58
|
+
sf signal f582bf76 "My read: escalation likely" --type user_note
|
|
59
|
+
```
|
|
60
|
+
Signal types: `news` | `user_note` | `external` (default: `user_note`)
|
|
61
|
+
|
|
62
|
+
### `sf evaluate <id>`
|
|
63
|
+
Trigger a deep evaluation using the heavy model (Claude Opus).
|
|
64
|
+
```bash
|
|
65
|
+
sf evaluate f582bf76
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### `sf scan "keywords"`
|
|
69
|
+
Explore Kalshi markets directly (no auth required).
|
|
70
|
+
```bash
|
|
71
|
+
sf scan "oil recession iran"
|
|
72
|
+
sf scan --series KXWTIMAX
|
|
73
|
+
sf scan --market KXWTIMAX-26DEC31-T140
|
|
74
|
+
sf scan "oil" --json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## For AI Agents (OpenClaw etc.)
|
|
78
|
+
|
|
79
|
+
After `npm install -g simplefunctions` and setting `SF_API_KEY`:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
You can use the sf CLI to interact with SimpleFunctions:
|
|
83
|
+
- sf context <id> --json Get current thesis state (JSON)
|
|
84
|
+
- sf signal <id> "content" Inject an observation note
|
|
85
|
+
- sf list List all theses
|
|
86
|
+
- sf scan "keywords" Explore Kalshi markets
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Agents should call `sf context <id> --json` periodically to get the latest state, then decide whether to inject signals or alert the user.
|
|
90
|
+
|
|
91
|
+
## Local Development
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
cd cli
|
|
95
|
+
npm install
|
|
96
|
+
npm run dev -- list # run without building
|
|
97
|
+
npm run build # compile to dist/
|
|
98
|
+
npm link # install as global 'sf' command
|
|
99
|
+
sf list
|
|
100
|
+
```
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SimpleFunctions HTTP Client
|
|
3
|
+
*
|
|
4
|
+
* Pure fetch-based client. Zero project dependencies.
|
|
5
|
+
* Talks to the SF API (authenticated) and Kalshi public API (no auth).
|
|
6
|
+
*/
|
|
7
|
+
export declare class SFClient {
|
|
8
|
+
private apiKey;
|
|
9
|
+
private baseUrl;
|
|
10
|
+
constructor(apiKey?: string, baseUrl?: string);
|
|
11
|
+
private request;
|
|
12
|
+
listTheses(): Promise<{
|
|
13
|
+
theses: any[];
|
|
14
|
+
}>;
|
|
15
|
+
getThesis(id: string): Promise<any>;
|
|
16
|
+
getContext(id: string): Promise<any>;
|
|
17
|
+
createThesis(rawThesis: string, sync?: boolean): Promise<any>;
|
|
18
|
+
injectSignal(id: string, type: string, content: string, source?: string): Promise<any>;
|
|
19
|
+
evaluate(id: string): Promise<any>;
|
|
20
|
+
updateThesis(id: string, data: Record<string, unknown>): Promise<any>;
|
|
21
|
+
}
|
|
22
|
+
export declare function kalshiFetchAllSeries(): Promise<any[]>;
|
|
23
|
+
export declare function kalshiFetchEvents(seriesTicker: string): Promise<any[]>;
|
|
24
|
+
export declare function kalshiFetchMarket(ticker: string): Promise<any>;
|
|
25
|
+
export declare function kalshiFetchMarketsBySeries(seriesTicker: string): Promise<any[]>;
|
|
26
|
+
export declare function kalshiFetchMarketsByEvent(eventTicker: string): Promise<any[]>;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SimpleFunctions HTTP Client
|
|
4
|
+
*
|
|
5
|
+
* Pure fetch-based client. Zero project dependencies.
|
|
6
|
+
* Talks to the SF API (authenticated) and Kalshi public API (no auth).
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.SFClient = void 0;
|
|
10
|
+
exports.kalshiFetchAllSeries = kalshiFetchAllSeries;
|
|
11
|
+
exports.kalshiFetchEvents = kalshiFetchEvents;
|
|
12
|
+
exports.kalshiFetchMarket = kalshiFetchMarket;
|
|
13
|
+
exports.kalshiFetchMarketsBySeries = kalshiFetchMarketsBySeries;
|
|
14
|
+
exports.kalshiFetchMarketsByEvent = kalshiFetchMarketsByEvent;
|
|
15
|
+
const DEFAULT_API_URL = 'https://simplefunctions.com';
|
|
16
|
+
const KALSHI_BASE = 'https://api.elections.kalshi.com/trade-api/v2';
|
|
17
|
+
// ===== SF API Client =====
|
|
18
|
+
class SFClient {
|
|
19
|
+
apiKey;
|
|
20
|
+
baseUrl;
|
|
21
|
+
constructor(apiKey, baseUrl) {
|
|
22
|
+
this.apiKey = apiKey || process.env.SF_API_KEY || '';
|
|
23
|
+
this.baseUrl = (baseUrl || process.env.SF_API_URL || DEFAULT_API_URL).replace(/\/$/, '');
|
|
24
|
+
if (!this.apiKey) {
|
|
25
|
+
throw new Error('API key required. Set SF_API_KEY or use --api-key');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async request(method, path, body) {
|
|
29
|
+
const url = `${this.baseUrl}${path}`;
|
|
30
|
+
const headers = {
|
|
31
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
};
|
|
34
|
+
const res = await fetch(url, {
|
|
35
|
+
method,
|
|
36
|
+
headers,
|
|
37
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
const text = await res.text();
|
|
41
|
+
throw new Error(`API error ${res.status}: ${text}`);
|
|
42
|
+
}
|
|
43
|
+
return res.json();
|
|
44
|
+
}
|
|
45
|
+
// ── Thesis operations ──
|
|
46
|
+
async listTheses() {
|
|
47
|
+
return this.request('GET', '/api/thesis');
|
|
48
|
+
}
|
|
49
|
+
// GET /api/thesis/:id returns { ...thesisRow, positions: [] } — flat, NOT { thesis, positions }
|
|
50
|
+
async getThesis(id) {
|
|
51
|
+
return this.request('GET', `/api/thesis/${id}`);
|
|
52
|
+
}
|
|
53
|
+
async getContext(id) {
|
|
54
|
+
return this.request('GET', `/api/thesis/${id}/context`);
|
|
55
|
+
}
|
|
56
|
+
async createThesis(rawThesis, sync = true) {
|
|
57
|
+
return this.request('POST', `/api/thesis/create?sync=${sync}`, { rawThesis });
|
|
58
|
+
}
|
|
59
|
+
async injectSignal(id, type, content, source = 'cli') {
|
|
60
|
+
return this.request('POST', `/api/thesis/${id}/signal`, { type, content, source });
|
|
61
|
+
}
|
|
62
|
+
async evaluate(id) {
|
|
63
|
+
return this.request('POST', `/api/thesis/${id}/evaluate`);
|
|
64
|
+
}
|
|
65
|
+
async updateThesis(id, data) {
|
|
66
|
+
return this.request('PATCH', `/api/thesis/${id}`, data);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.SFClient = SFClient;
|
|
70
|
+
// ===== Kalshi Public API (no auth) =====
|
|
71
|
+
async function kalshiGet(path, params) {
|
|
72
|
+
const url = new URL(`${KALSHI_BASE}${path}`);
|
|
73
|
+
if (params) {
|
|
74
|
+
for (const [k, v] of Object.entries(params)) {
|
|
75
|
+
if (v !== undefined && v !== '')
|
|
76
|
+
url.searchParams.set(k, v);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const res = await fetch(url.toString());
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
throw new Error(`Kalshi API ${res.status}: ${await res.text()}`);
|
|
82
|
+
}
|
|
83
|
+
return res.json();
|
|
84
|
+
}
|
|
85
|
+
async function kalshiFetchAllSeries() {
|
|
86
|
+
const data = await kalshiGet('/series', { include_volume: 'true' });
|
|
87
|
+
return data.series || [];
|
|
88
|
+
}
|
|
89
|
+
async function kalshiFetchEvents(seriesTicker) {
|
|
90
|
+
const data = await kalshiGet('/events', {
|
|
91
|
+
series_ticker: seriesTicker,
|
|
92
|
+
status: 'open',
|
|
93
|
+
with_nested_markets: 'true',
|
|
94
|
+
limit: '200',
|
|
95
|
+
});
|
|
96
|
+
return data.events || [];
|
|
97
|
+
}
|
|
98
|
+
async function kalshiFetchMarket(ticker) {
|
|
99
|
+
const data = await kalshiGet(`/markets/${ticker}`);
|
|
100
|
+
return data.market || data;
|
|
101
|
+
}
|
|
102
|
+
async function kalshiFetchMarketsBySeries(seriesTicker) {
|
|
103
|
+
const data = await kalshiGet('/markets', {
|
|
104
|
+
series_ticker: seriesTicker,
|
|
105
|
+
status: 'open',
|
|
106
|
+
limit: '200',
|
|
107
|
+
});
|
|
108
|
+
return data.markets || [];
|
|
109
|
+
}
|
|
110
|
+
async function kalshiFetchMarketsByEvent(eventTicker) {
|
|
111
|
+
const data = await kalshiGet('/markets', {
|
|
112
|
+
event_ticker: eventTicker,
|
|
113
|
+
status: 'open',
|
|
114
|
+
limit: '1000',
|
|
115
|
+
});
|
|
116
|
+
return data.markets || [];
|
|
117
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.contextCommand = contextCommand;
|
|
4
|
+
const client_js_1 = require("../client.js");
|
|
5
|
+
const utils_js_1 = require("../utils.js");
|
|
6
|
+
async function contextCommand(id, opts) {
|
|
7
|
+
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
8
|
+
const ctx = await client.getContext(id);
|
|
9
|
+
if (opts.json) {
|
|
10
|
+
console.log(JSON.stringify(ctx, null, 2));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
// Context API shape:
|
|
14
|
+
// { thesisId, thesis, title, status, confidence (number|null),
|
|
15
|
+
// causalTree: { nodes: FlatNode[] }, edges: Edge[], edgeMeta,
|
|
16
|
+
// lastEvaluation: { summary, confidenceDelta, ... }, updatedAt, ... }
|
|
17
|
+
// Thesis header
|
|
18
|
+
console.log(`\n${utils_js_1.c.bold}Thesis:${utils_js_1.c.reset} ${ctx.thesis || ctx.rawThesis || '(unknown)'}`);
|
|
19
|
+
const confStr = ctx.confidence !== null && ctx.confidence !== undefined ? (0, utils_js_1.pct)(ctx.confidence) : '-';
|
|
20
|
+
const confDelta = ctx.lastEvaluation?.confidenceDelta;
|
|
21
|
+
const deltaStr = confDelta ? ` (${(0, utils_js_1.delta)(confDelta)} since last eval)` : '';
|
|
22
|
+
console.log(`${utils_js_1.c.bold}Confidence:${utils_js_1.c.reset} ${confStr}${deltaStr}`);
|
|
23
|
+
console.log(`${utils_js_1.c.bold}Status:${utils_js_1.c.reset} ${ctx.status}`);
|
|
24
|
+
console.log(`${utils_js_1.c.bold}Last Updated:${utils_js_1.c.reset} ${(0, utils_js_1.shortDate)(ctx.updatedAt)}`);
|
|
25
|
+
// Causal tree nodes (flat array from API)
|
|
26
|
+
const nodes = ctx.causalTree?.nodes;
|
|
27
|
+
if (nodes && nodes.length > 0) {
|
|
28
|
+
(0, utils_js_1.header)('Causal Tree');
|
|
29
|
+
for (const node of nodes) {
|
|
30
|
+
const indent = ' '.repeat((node.depth || 0) + 1);
|
|
31
|
+
const prob = node.probability !== undefined ? (0, utils_js_1.pct)(node.probability) : '-';
|
|
32
|
+
const label = node.label || node.id;
|
|
33
|
+
console.log(`${indent}${utils_js_1.c.cyan}${node.id}${utils_js_1.c.reset} ${(0, utils_js_1.pad)(label, 40)} ${(0, utils_js_1.rpad)(prob, 5)}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Top edges (sorted by absolute edge size)
|
|
37
|
+
const edges = ctx.edges;
|
|
38
|
+
if (edges && edges.length > 0) {
|
|
39
|
+
(0, utils_js_1.header)('Top Edges (by edge size)');
|
|
40
|
+
const sorted = [...edges].sort((a, b) => Math.abs(b.edge ?? b.edgeSize ?? 0) - Math.abs(a.edge ?? a.edgeSize ?? 0));
|
|
41
|
+
for (const edge of sorted.slice(0, 10)) {
|
|
42
|
+
const edgeSize = edge.edge ?? edge.edgeSize ?? 0;
|
|
43
|
+
const edgeColor = edgeSize > 10 ? utils_js_1.c.green : edgeSize > 0 ? utils_js_1.c.yellow : utils_js_1.c.red;
|
|
44
|
+
const mktPrice = edge.marketPrice ?? edge.currentPrice ?? 0;
|
|
45
|
+
const title = edge.market || edge.marketTitle || edge.marketId || '?';
|
|
46
|
+
console.log(` ${(0, utils_js_1.pad)(title, 35)}` +
|
|
47
|
+
` ${(0, utils_js_1.rpad)(mktPrice.toFixed(0) + '¢', 5)}` +
|
|
48
|
+
` ${edgeColor}edge ${edgeSize > 0 ? '+' : ''}${edgeSize.toFixed(1)}${utils_js_1.c.reset}` +
|
|
49
|
+
` ${utils_js_1.c.dim}${edge.venue || ''}${utils_js_1.c.reset}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Last evaluation summary
|
|
53
|
+
if (ctx.lastEvaluation?.summary) {
|
|
54
|
+
(0, utils_js_1.header)('Last Evaluation');
|
|
55
|
+
console.log(` ${utils_js_1.c.dim}${(0, utils_js_1.shortDate)(ctx.lastEvaluation.evaluatedAt)} | model: ${ctx.lastEvaluation.model || ''}${utils_js_1.c.reset}`);
|
|
56
|
+
console.log(` ${ctx.lastEvaluation.summary}`);
|
|
57
|
+
if (ctx.lastEvaluation.positionRecommendations?.length > 0) {
|
|
58
|
+
console.log(`\n ${utils_js_1.c.bold}Position Recommendations:${utils_js_1.c.reset}`);
|
|
59
|
+
for (const pr of ctx.lastEvaluation.positionRecommendations) {
|
|
60
|
+
const recColor = pr.recommendation === 'hold' ? utils_js_1.c.dim : pr.recommendation === 'close' ? utils_js_1.c.red : utils_js_1.c.yellow;
|
|
61
|
+
console.log(` [${(pr.positionId || '').slice(0, 8)}] ${recColor}${pr.recommendation}${utils_js_1.c.reset} — ${pr.reason}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Edge meta (last rescan time)
|
|
66
|
+
if (ctx.edgeMeta?.lastRescanAt) {
|
|
67
|
+
console.log(`\n${utils_js_1.c.dim}Last rescan: ${(0, utils_js_1.shortDate)(ctx.edgeMeta.lastRescanAt)}${utils_js_1.c.reset}`);
|
|
68
|
+
}
|
|
69
|
+
console.log('');
|
|
70
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createCommand = createCommand;
|
|
4
|
+
const client_js_1 = require("../client.js");
|
|
5
|
+
const utils_js_1 = require("../utils.js");
|
|
6
|
+
async function createCommand(thesis, opts) {
|
|
7
|
+
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
8
|
+
const sync = !opts.async;
|
|
9
|
+
if (sync) {
|
|
10
|
+
console.log(`${utils_js_1.c.dim}Creating thesis (sync mode — waiting for formation)...${utils_js_1.c.reset}`);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
console.log(`${utils_js_1.c.dim}Creating thesis (async mode)...${utils_js_1.c.reset}`);
|
|
14
|
+
}
|
|
15
|
+
const result = await client.createThesis(thesis, sync);
|
|
16
|
+
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} ${result.thesis?.id || result.id}`);
|
|
18
|
+
console.log(` ${utils_js_1.c.bold}Status:${utils_js_1.c.reset} ${result.thesis?.status || result.status}`);
|
|
19
|
+
if (result.thesis?.confidence) {
|
|
20
|
+
console.log(` ${utils_js_1.c.bold}Confidence:${utils_js_1.c.reset} ${Math.round(parseFloat(result.thesis.confidence) * 100)}%`);
|
|
21
|
+
}
|
|
22
|
+
if (result.thesis?.causalTree?.nodes) {
|
|
23
|
+
console.log(` ${utils_js_1.c.bold}Nodes:${utils_js_1.c.reset} ${result.thesis.causalTree.nodes.length}`);
|
|
24
|
+
}
|
|
25
|
+
if (result.thesis?.edgeAnalysis?.edges) {
|
|
26
|
+
console.log(` ${utils_js_1.c.bold}Edges:${utils_js_1.c.reset} ${result.thesis.edgeAnalysis.edges.length}`);
|
|
27
|
+
}
|
|
28
|
+
const id = result.thesis?.id || result.id;
|
|
29
|
+
console.log(`\n${utils_js_1.c.dim}View: sf get ${(0, utils_js_1.shortId)(id)}${utils_js_1.c.reset}`);
|
|
30
|
+
console.log(`${utils_js_1.c.dim}Context: sf context ${(0, utils_js_1.shortId)(id)}${utils_js_1.c.reset}`);
|
|
31
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.evaluateCommand = evaluateCommand;
|
|
4
|
+
const client_js_1 = require("../client.js");
|
|
5
|
+
const utils_js_1 = require("../utils.js");
|
|
6
|
+
async function evaluateCommand(id, opts) {
|
|
7
|
+
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
8
|
+
console.log(`${utils_js_1.c.dim}Triggering deep evaluation (heavy model)...${utils_js_1.c.reset}`);
|
|
9
|
+
const result = await client.evaluate(id);
|
|
10
|
+
console.log(`\n${utils_js_1.c.green}✓${utils_js_1.c.reset} Evaluation complete`);
|
|
11
|
+
if (result.evaluation) {
|
|
12
|
+
const ev = result.evaluation;
|
|
13
|
+
if (ev.confidenceDelta !== undefined) {
|
|
14
|
+
const d = ev.confidenceDelta;
|
|
15
|
+
const dColor = d > 0 ? utils_js_1.c.green : d < 0 ? utils_js_1.c.red : utils_js_1.c.dim;
|
|
16
|
+
console.log(` ${utils_js_1.c.bold}Confidence:${utils_js_1.c.reset} ${(0, utils_js_1.pct)(ev.previousConfidence)} → ${(0, utils_js_1.pct)(ev.newConfidence)} ${dColor}(${(0, utils_js_1.delta)(d)})${utils_js_1.c.reset}`);
|
|
17
|
+
}
|
|
18
|
+
if (ev.summary) {
|
|
19
|
+
console.log(`\n ${ev.summary}`);
|
|
20
|
+
}
|
|
21
|
+
if (ev.positionUpdates && ev.positionUpdates.length > 0) {
|
|
22
|
+
console.log(`\n ${utils_js_1.c.bold}Position Recommendations:${utils_js_1.c.reset}`);
|
|
23
|
+
for (const pu of ev.positionUpdates) {
|
|
24
|
+
const recColor = pu.recommendation === 'hold' ? utils_js_1.c.dim : pu.recommendation === 'close' ? utils_js_1.c.red : utils_js_1.c.yellow;
|
|
25
|
+
console.log(` [${pu.positionId.slice(0, 8)}] ${recColor}${pu.recommendation}${utils_js_1.c.reset} — ${pu.reason}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
console.log('');
|
|
30
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCommand = getCommand;
|
|
4
|
+
const client_js_1 = require("../client.js");
|
|
5
|
+
const utils_js_1 = require("../utils.js");
|
|
6
|
+
async function getCommand(id, opts) {
|
|
7
|
+
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
8
|
+
// GET /api/thesis/:id returns { ...thesisRow, positions: [] }
|
|
9
|
+
// Fields are spread at top-level (NOT nested under .thesis)
|
|
10
|
+
const data = await client.getThesis(id);
|
|
11
|
+
if (opts.json) {
|
|
12
|
+
console.log(JSON.stringify(data, null, 2));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
// data is the flat thesis row + positions array
|
|
16
|
+
const t = data; // thesis fields are at top level
|
|
17
|
+
const positions = data.positions || [];
|
|
18
|
+
(0, utils_js_1.header)(`Thesis: ${(t.id || id).slice(0, 8)}`);
|
|
19
|
+
(0, utils_js_1.hr)();
|
|
20
|
+
console.log(`${utils_js_1.c.bold}Status:${utils_js_1.c.reset} ${t.status || '-'}`);
|
|
21
|
+
const conf = t.confidence ? (0, utils_js_1.pct)(parseFloat(t.confidence)) : '-';
|
|
22
|
+
console.log(`${utils_js_1.c.bold}Confidence:${utils_js_1.c.reset} ${conf}`);
|
|
23
|
+
const createdAt = t.createdAt instanceof Date ? t.createdAt.toISOString() : t.createdAt;
|
|
24
|
+
const updatedAt = t.updatedAt instanceof Date ? t.updatedAt.toISOString() : t.updatedAt;
|
|
25
|
+
console.log(`${utils_js_1.c.bold}Created:${utils_js_1.c.reset} ${(0, utils_js_1.shortDate)(createdAt)}`);
|
|
26
|
+
console.log(`${utils_js_1.c.bold}Updated:${utils_js_1.c.reset} ${(0, utils_js_1.shortDate)(updatedAt)}`);
|
|
27
|
+
if (t.title) {
|
|
28
|
+
console.log(`${utils_js_1.c.bold}Title:${utils_js_1.c.reset} ${t.title}`);
|
|
29
|
+
}
|
|
30
|
+
console.log(`${utils_js_1.c.bold}Thesis:${utils_js_1.c.reset} ${t.rawThesis || '-'}`);
|
|
31
|
+
if (t.webhookUrl) {
|
|
32
|
+
console.log(`${utils_js_1.c.bold}Webhook:${utils_js_1.c.reset} ${t.webhookUrl}`);
|
|
33
|
+
}
|
|
34
|
+
// Causal tree
|
|
35
|
+
const causalTree = t.causalTree;
|
|
36
|
+
if (causalTree && causalTree.nodes) {
|
|
37
|
+
(0, utils_js_1.header)('Causal Tree');
|
|
38
|
+
printNodes(causalTree.nodes, 0);
|
|
39
|
+
}
|
|
40
|
+
// Edge analysis
|
|
41
|
+
const edgeAnalysis = t.edgeAnalysis;
|
|
42
|
+
if (edgeAnalysis && edgeAnalysis.edges) {
|
|
43
|
+
(0, utils_js_1.header)('Edge Analysis');
|
|
44
|
+
console.log(`${utils_js_1.c.dim}Analyzed: ${(0, utils_js_1.shortDate)(edgeAnalysis.analyzedAt)}${utils_js_1.c.reset}`);
|
|
45
|
+
if (edgeAnalysis.lastRescanAt) {
|
|
46
|
+
console.log(`${utils_js_1.c.dim}Last rescan: ${(0, utils_js_1.shortDate)(edgeAnalysis.lastRescanAt)}${utils_js_1.c.reset}`);
|
|
47
|
+
}
|
|
48
|
+
for (const edge of edgeAnalysis.edges) {
|
|
49
|
+
const edgeSize = edge.edgeSize ?? 0;
|
|
50
|
+
const edgeColor = edgeSize > 10 ? utils_js_1.c.green : edgeSize > 0 ? utils_js_1.c.yellow : utils_js_1.c.red;
|
|
51
|
+
console.log(` ${edge.marketTitle || edge.marketId}` +
|
|
52
|
+
` ${utils_js_1.c.dim}${edge.venue}${utils_js_1.c.reset}` +
|
|
53
|
+
` price: ${(edge.marketPrice ?? 0).toFixed(0)}¢` +
|
|
54
|
+
` ${edgeColor}edge: ${edgeSize > 0 ? '+' : ''}${edgeSize.toFixed(1)}${utils_js_1.c.reset}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Positions
|
|
58
|
+
if (positions.length > 0) {
|
|
59
|
+
(0, utils_js_1.header)('Positions');
|
|
60
|
+
for (const p of positions) {
|
|
61
|
+
if (!p)
|
|
62
|
+
continue;
|
|
63
|
+
const statusIcon = p.status === 'open' ? utils_js_1.c.green + '●' : utils_js_1.c.dim + '○';
|
|
64
|
+
console.log(` ${statusIcon}${utils_js_1.c.reset} [${(p.id || '?').slice(0, 8)}] "${p.marketTitle || '?'}" ` +
|
|
65
|
+
`${p.direction || '?'}@${p.entryPrice || '?'}→${p.currentPrice || p.entryPrice || '?'} ` +
|
|
66
|
+
`${utils_js_1.c.dim}(${p.venue || '?'})${utils_js_1.c.reset}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Last evaluation
|
|
70
|
+
const lastEval = t.lastEvaluation;
|
|
71
|
+
if (lastEval) {
|
|
72
|
+
(0, utils_js_1.header)('Last Evaluation');
|
|
73
|
+
const evalAt = lastEval.evaluatedAt instanceof Date ? lastEval.evaluatedAt.toISOString() : lastEval.evaluatedAt;
|
|
74
|
+
console.log(`${utils_js_1.c.dim}${(0, utils_js_1.shortDate)(evalAt)} | model: ${lastEval.model || '-'}${utils_js_1.c.reset}`);
|
|
75
|
+
if (lastEval.confidenceDelta !== undefined) {
|
|
76
|
+
const d = lastEval.confidenceDelta;
|
|
77
|
+
const dColor = d > 0 ? utils_js_1.c.green : d < 0 ? utils_js_1.c.red : utils_js_1.c.dim;
|
|
78
|
+
console.log(`Confidence: ${(0, utils_js_1.pct)(lastEval.previousConfidence)} → ${(0, utils_js_1.pct)(lastEval.newConfidence)} ${dColor}(${(0, utils_js_1.delta)(d)})${utils_js_1.c.reset}`);
|
|
79
|
+
}
|
|
80
|
+
if (lastEval.summary) {
|
|
81
|
+
console.log(`\n${lastEval.summary}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
console.log('');
|
|
85
|
+
}
|
|
86
|
+
function printNodes(nodes, depth) {
|
|
87
|
+
for (const node of nodes) {
|
|
88
|
+
if (!node)
|
|
89
|
+
continue;
|
|
90
|
+
const indent = ' '.repeat(depth + 1);
|
|
91
|
+
const prob = node.probability !== undefined ? (0, utils_js_1.pct)(node.probability) : '-';
|
|
92
|
+
const imp = node.importance !== undefined ? ` imp:${node.importance}` : '';
|
|
93
|
+
console.log(`${indent}${utils_js_1.c.cyan}${node.id}${utils_js_1.c.reset} ${node.label} ${utils_js_1.c.dim}(${prob}${imp})${utils_js_1.c.reset}`);
|
|
94
|
+
if (node.children && node.children.length > 0) {
|
|
95
|
+
printNodes(node.children, depth + 1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listCommand = listCommand;
|
|
4
|
+
const client_js_1 = require("../client.js");
|
|
5
|
+
const utils_js_1 = require("../utils.js");
|
|
6
|
+
async function listCommand(opts) {
|
|
7
|
+
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
8
|
+
const { theses } = await client.listTheses();
|
|
9
|
+
if (theses.length === 0) {
|
|
10
|
+
console.log(`${utils_js_1.c.dim}No theses found.${utils_js_1.c.reset}`);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
console.log(`\n${utils_js_1.c.bold}` +
|
|
14
|
+
(0, utils_js_1.pad)('ID', 12) +
|
|
15
|
+
(0, utils_js_1.pad)('Status', 10) +
|
|
16
|
+
(0, utils_js_1.rpad)('Conf', 6) +
|
|
17
|
+
' ' +
|
|
18
|
+
(0, utils_js_1.pad)('Updated', 14) +
|
|
19
|
+
' Title' +
|
|
20
|
+
utils_js_1.c.reset);
|
|
21
|
+
(0, utils_js_1.hr)(90);
|
|
22
|
+
for (const t of theses) {
|
|
23
|
+
const statusColor = t.status === 'active' ? utils_js_1.c.green : t.status === 'forming' ? utils_js_1.c.yellow : utils_js_1.c.dim;
|
|
24
|
+
const conf = t.confidence ? (0, utils_js_1.pct)(parseFloat(t.confidence)) : '-';
|
|
25
|
+
console.log((0, utils_js_1.pad)((0, utils_js_1.shortId)(t.id), 12) +
|
|
26
|
+
statusColor + (0, utils_js_1.pad)(t.status, 10) + utils_js_1.c.reset +
|
|
27
|
+
(0, utils_js_1.rpad)(conf, 6) +
|
|
28
|
+
' ' +
|
|
29
|
+
utils_js_1.c.dim + (0, utils_js_1.pad)((0, utils_js_1.shortDate)(t.updatedAt), 14) + utils_js_1.c.reset +
|
|
30
|
+
' ' +
|
|
31
|
+
(0, utils_js_1.trunc)(t.title || t.rawThesis.slice(0, 60), 50));
|
|
32
|
+
}
|
|
33
|
+
console.log(`\n${utils_js_1.c.dim}${theses.length} thesis(es)${utils_js_1.c.reset}`);
|
|
34
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scanCommand = scanCommand;
|
|
4
|
+
const client_js_1 = require("../client.js");
|
|
5
|
+
const utils_js_1 = require("../utils.js");
|
|
6
|
+
async function scanCommand(query, opts) {
|
|
7
|
+
// Mode 1: --market TICKER — single market detail
|
|
8
|
+
if (opts.market) {
|
|
9
|
+
await showMarket(opts.market.toUpperCase(), opts.json);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
// Mode 2: --series TICKER — list events + markets for a series
|
|
13
|
+
if (opts.series) {
|
|
14
|
+
await showSeries(opts.series.toUpperCase(), opts.json);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// Mode 3: keyword scan across all series
|
|
18
|
+
await keywordScan(query, opts.json);
|
|
19
|
+
}
|
|
20
|
+
async function showMarket(ticker, json) {
|
|
21
|
+
console.log(`${utils_js_1.c.dim}Fetching market ${ticker}...${utils_js_1.c.reset}`);
|
|
22
|
+
const m = await (0, client_js_1.kalshiFetchMarket)(ticker);
|
|
23
|
+
if (json) {
|
|
24
|
+
console.log(JSON.stringify(m, null, 2));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
(0, utils_js_1.header)(`${m.title || m.ticker}`);
|
|
28
|
+
if (m.subtitle)
|
|
29
|
+
console.log(`${utils_js_1.c.dim}${m.subtitle}${utils_js_1.c.reset}`);
|
|
30
|
+
console.log(`Event: ${m.event_ticker} Status: ${m.status}`);
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log(`Yes Ask: ${(0, utils_js_1.cents)(m.yes_ask_dollars)} (size: ${m.yes_ask_size_fp || '-'})`);
|
|
33
|
+
console.log(`Yes Bid: ${(0, utils_js_1.cents)(m.yes_bid_dollars)} (size: ${m.yes_bid_size_fp || '-'})`);
|
|
34
|
+
console.log(`No Ask: ${(0, utils_js_1.cents)(m.no_ask_dollars)}`);
|
|
35
|
+
console.log(`No Bid: ${(0, utils_js_1.cents)(m.no_bid_dollars)}`);
|
|
36
|
+
console.log(`Last: ${(0, utils_js_1.cents)(m.last_price_dollars)}`);
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(`Volume: ${(0, utils_js_1.vol)(m.volume_fp)}`);
|
|
39
|
+
console.log(`Vol 24h: ${(0, utils_js_1.vol)(m.volume_24h_fp)}`);
|
|
40
|
+
console.log(`Open Int: ${(0, utils_js_1.vol)(m.open_interest_fp)}`);
|
|
41
|
+
console.log(`Liquidity: ${(0, utils_js_1.vol)(m.liquidity_dollars)}`);
|
|
42
|
+
console.log('');
|
|
43
|
+
console.log(`Open: ${m.open_time}`);
|
|
44
|
+
console.log(`Close: ${m.close_time}`);
|
|
45
|
+
if (m.rules_primary) {
|
|
46
|
+
console.log(`\nRules: ${m.rules_primary.slice(0, 300)}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function showSeries(seriesTicker, json) {
|
|
50
|
+
console.log(`${utils_js_1.c.dim}Fetching events for series ${seriesTicker}...${utils_js_1.c.reset}`);
|
|
51
|
+
let events = await (0, client_js_1.kalshiFetchEvents)(seriesTicker);
|
|
52
|
+
if (events.length === 0) {
|
|
53
|
+
// Fallback: direct market lookup by series
|
|
54
|
+
const markets = await (0, client_js_1.kalshiFetchMarketsBySeries)(seriesTicker);
|
|
55
|
+
if (json) {
|
|
56
|
+
console.log(JSON.stringify(markets, null, 2));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (markets.length === 0) {
|
|
60
|
+
console.log(`${utils_js_1.c.dim}No open markets found for series ${seriesTicker}.${utils_js_1.c.reset}`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
(0, utils_js_1.header)(`${seriesTicker} — ${markets.length} markets`);
|
|
64
|
+
printMarketsTable(markets);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (json) {
|
|
68
|
+
console.log(JSON.stringify(events, null, 2));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
for (const event of events) {
|
|
72
|
+
(0, utils_js_1.header)(`${event.title || event.event_ticker}`);
|
|
73
|
+
console.log(`${utils_js_1.c.dim}${event.event_ticker} | ${event.category || '-'} | strike: ${event.strike_date || '-'}${utils_js_1.c.reset}`);
|
|
74
|
+
const markets = event.markets && event.markets.length > 0
|
|
75
|
+
? event.markets
|
|
76
|
+
: await (0, client_js_1.kalshiFetchMarketsByEvent)(event.event_ticker);
|
|
77
|
+
printMarketsTable(markets);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function keywordScan(query, json) {
|
|
81
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
82
|
+
console.log(`${utils_js_1.c.dim}Scanning Kalshi for: "${query}"...${utils_js_1.c.reset}`);
|
|
83
|
+
const allSeries = await (0, client_js_1.kalshiFetchAllSeries)();
|
|
84
|
+
// Score each series
|
|
85
|
+
const thesisKeywords = [
|
|
86
|
+
'oil', 'wti', 'gas', 'recession', 'gdp', 'fed', 'inflation',
|
|
87
|
+
'unemployment', 'cpi', 'interest rate', 'congress', 'election',
|
|
88
|
+
'iran', 'hormuz', 'war', 'tariff', 'trade', 'rate',
|
|
89
|
+
];
|
|
90
|
+
const matches = [];
|
|
91
|
+
for (const s of allSeries) {
|
|
92
|
+
const text = `${s.title} ${s.ticker} ${(s.tags || []).join(' ')} ${s.category}`.toLowerCase();
|
|
93
|
+
let score = 0;
|
|
94
|
+
for (const term of terms) {
|
|
95
|
+
if (text.includes(term))
|
|
96
|
+
score += 10;
|
|
97
|
+
}
|
|
98
|
+
for (const kw of thesisKeywords) {
|
|
99
|
+
if (text.includes(kw))
|
|
100
|
+
score += 5;
|
|
101
|
+
}
|
|
102
|
+
const v = parseFloat(s.volume_fp || '0');
|
|
103
|
+
if (v > 1_000_000)
|
|
104
|
+
score += 3;
|
|
105
|
+
else if (v > 100_000)
|
|
106
|
+
score += 1;
|
|
107
|
+
if (score > 0)
|
|
108
|
+
matches.push({ series: s, score });
|
|
109
|
+
}
|
|
110
|
+
matches.sort((a, b) => b.score - a.score);
|
|
111
|
+
const topSeries = matches.slice(0, 15);
|
|
112
|
+
console.log(`\n${utils_js_1.c.bold}Found ${matches.length} relevant series. Top ${topSeries.length}:${utils_js_1.c.reset}\n`);
|
|
113
|
+
for (const { series: s, score } of topSeries) {
|
|
114
|
+
console.log(` ${utils_js_1.c.dim}[${score}]${utils_js_1.c.reset} ${(0, utils_js_1.pad)(s.ticker, 25)} ${s.title}`);
|
|
115
|
+
}
|
|
116
|
+
// Fetch live markets for top 10
|
|
117
|
+
console.log(`\n${utils_js_1.c.dim}Fetching live markets...${utils_js_1.c.reset}\n`);
|
|
118
|
+
const allMarkets = [];
|
|
119
|
+
for (const { series: s } of topSeries.slice(0, 10)) {
|
|
120
|
+
try {
|
|
121
|
+
const events = await (0, client_js_1.kalshiFetchEvents)(s.ticker);
|
|
122
|
+
for (const event of events) {
|
|
123
|
+
const markets = event.markets || [];
|
|
124
|
+
for (const m of markets) {
|
|
125
|
+
if (m.status === 'open' || m.status === 'active') {
|
|
126
|
+
allMarkets.push({
|
|
127
|
+
seriesTicker: s.ticker,
|
|
128
|
+
ticker: m.ticker,
|
|
129
|
+
title: m.title || m.subtitle || '',
|
|
130
|
+
yesAsk: parseFloat(m.yes_ask_dollars || '0'),
|
|
131
|
+
lastPrice: parseFloat(m.last_price_dollars || '0'),
|
|
132
|
+
volume24h: parseFloat(m.volume_24h_fp || '0'),
|
|
133
|
+
liquidity: parseFloat(m.liquidity_dollars || '0'),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
await new Promise(r => setTimeout(r, 150));
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// skip failed series
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
allMarkets.sort((a, b) => b.liquidity - a.liquidity);
|
|
145
|
+
if (json) {
|
|
146
|
+
console.log(JSON.stringify(allMarkets, null, 2));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
(0, utils_js_1.header)(`${allMarkets.length} Live Markets`);
|
|
150
|
+
console.log(utils_js_1.c.bold +
|
|
151
|
+
(0, utils_js_1.pad)('Ticker', 35) +
|
|
152
|
+
(0, utils_js_1.rpad)('Yes', 6) +
|
|
153
|
+
(0, utils_js_1.rpad)('Last', 6) +
|
|
154
|
+
(0, utils_js_1.rpad)('Vol24h', 10) +
|
|
155
|
+
(0, utils_js_1.rpad)('Liq', 10) +
|
|
156
|
+
' Title' +
|
|
157
|
+
utils_js_1.c.reset);
|
|
158
|
+
(0, utils_js_1.hr)(110);
|
|
159
|
+
for (const m of allMarkets.slice(0, 50)) {
|
|
160
|
+
console.log((0, utils_js_1.pad)(m.ticker, 35) +
|
|
161
|
+
(0, utils_js_1.rpad)(`${Math.round(m.yesAsk * 100)}¢`, 6) +
|
|
162
|
+
(0, utils_js_1.rpad)(`${Math.round(m.lastPrice * 100)}¢`, 6) +
|
|
163
|
+
(0, utils_js_1.rpad)((0, utils_js_1.vol)(m.volume24h), 10) +
|
|
164
|
+
(0, utils_js_1.rpad)((0, utils_js_1.vol)(m.liquidity), 10) +
|
|
165
|
+
` ${m.title.slice(0, 55)}`);
|
|
166
|
+
}
|
|
167
|
+
console.log('');
|
|
168
|
+
}
|
|
169
|
+
function printMarketsTable(markets) {
|
|
170
|
+
if (markets.length === 0) {
|
|
171
|
+
console.log(` ${utils_js_1.c.dim}(no markets)${utils_js_1.c.reset}`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
markets.sort((a, b) => {
|
|
175
|
+
const pa = parseFloat(a.yes_ask_dollars || a.last_price_dollars || '0');
|
|
176
|
+
const pb = parseFloat(b.yes_ask_dollars || b.last_price_dollars || '0');
|
|
177
|
+
return pb - pa;
|
|
178
|
+
});
|
|
179
|
+
console.log(' ' + utils_js_1.c.bold +
|
|
180
|
+
(0, utils_js_1.pad)('Ticker', 35) +
|
|
181
|
+
(0, utils_js_1.rpad)('YesAsk', 8) +
|
|
182
|
+
(0, utils_js_1.rpad)('Last', 8) +
|
|
183
|
+
(0, utils_js_1.rpad)('Vol24h', 10) +
|
|
184
|
+
(0, utils_js_1.rpad)('Liq', 12) +
|
|
185
|
+
' Title' +
|
|
186
|
+
utils_js_1.c.reset);
|
|
187
|
+
console.log(' ' + utils_js_1.c.dim + '─'.repeat(100) + utils_js_1.c.reset);
|
|
188
|
+
for (const m of markets) {
|
|
189
|
+
console.log(' ' +
|
|
190
|
+
(0, utils_js_1.pad)(m.ticker || '', 35) +
|
|
191
|
+
(0, utils_js_1.rpad)((0, utils_js_1.cents)(m.yes_ask_dollars), 8) +
|
|
192
|
+
(0, utils_js_1.rpad)((0, utils_js_1.cents)(m.last_price_dollars), 8) +
|
|
193
|
+
(0, utils_js_1.rpad)((0, utils_js_1.vol)(m.volume_24h_fp), 10) +
|
|
194
|
+
(0, utils_js_1.rpad)((0, utils_js_1.vol)(m.liquidity_dollars), 12) +
|
|
195
|
+
` ${(m.title || m.subtitle || '').slice(0, 55)}`);
|
|
196
|
+
}
|
|
197
|
+
console.log('');
|
|
198
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.signalCommand = signalCommand;
|
|
4
|
+
const client_js_1 = require("../client.js");
|
|
5
|
+
const utils_js_1 = require("../utils.js");
|
|
6
|
+
async function signalCommand(id, content, opts) {
|
|
7
|
+
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
8
|
+
const type = opts.type || 'user_note';
|
|
9
|
+
const result = await client.injectSignal(id, type, content, 'cli');
|
|
10
|
+
console.log(`${utils_js_1.c.green}✓${utils_js_1.c.reset} Signal injected`);
|
|
11
|
+
console.log(` ${utils_js_1.c.bold}Type:${utils_js_1.c.reset} ${type}`);
|
|
12
|
+
console.log(` ${utils_js_1.c.bold}Source:${utils_js_1.c.reset} cli`);
|
|
13
|
+
console.log(` ${utils_js_1.c.bold}Content:${utils_js_1.c.reset} ${content}`);
|
|
14
|
+
if (result.signalId) {
|
|
15
|
+
console.log(` ${utils_js_1.c.bold}ID:${utils_js_1.c.reset} ${result.signalId}`);
|
|
16
|
+
}
|
|
17
|
+
console.log(`\n${utils_js_1.c.dim}Signal queued for next monitor cycle.${utils_js_1.c.reset}`);
|
|
18
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SimpleFunctions CLI — sf
|
|
4
|
+
*
|
|
5
|
+
* Prediction market thesis agent CLI.
|
|
6
|
+
* Zero heavy dependencies: commander + native fetch.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* sf list
|
|
10
|
+
* sf get <id>
|
|
11
|
+
* sf context <id> [--json]
|
|
12
|
+
* sf create "thesis text" [--async]
|
|
13
|
+
* sf signal <id> "content" [--type news|user_note|external]
|
|
14
|
+
* sf evaluate <id>
|
|
15
|
+
* sf scan "keywords" [--series TICKER] [--market TICKER] [--json]
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* SimpleFunctions CLI — sf
|
|
5
|
+
*
|
|
6
|
+
* Prediction market thesis agent CLI.
|
|
7
|
+
* Zero heavy dependencies: commander + native fetch.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* sf list
|
|
11
|
+
* sf get <id>
|
|
12
|
+
* sf context <id> [--json]
|
|
13
|
+
* sf create "thesis text" [--async]
|
|
14
|
+
* sf signal <id> "content" [--type news|user_note|external]
|
|
15
|
+
* sf evaluate <id>
|
|
16
|
+
* sf scan "keywords" [--series TICKER] [--market TICKER] [--json]
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
const commander_1 = require("commander");
|
|
20
|
+
const list_js_1 = require("./commands/list.js");
|
|
21
|
+
const get_js_1 = require("./commands/get.js");
|
|
22
|
+
const context_js_1 = require("./commands/context.js");
|
|
23
|
+
const create_js_1 = require("./commands/create.js");
|
|
24
|
+
const signal_js_1 = require("./commands/signal.js");
|
|
25
|
+
const evaluate_js_1 = require("./commands/evaluate.js");
|
|
26
|
+
const scan_js_1 = require("./commands/scan.js");
|
|
27
|
+
const utils_js_1 = require("./utils.js");
|
|
28
|
+
const program = new commander_1.Command();
|
|
29
|
+
program
|
|
30
|
+
.name('sf')
|
|
31
|
+
.description('SimpleFunctions CLI — prediction market thesis agent')
|
|
32
|
+
.version('0.1.0')
|
|
33
|
+
.option('--api-key <key>', 'API key (or set SF_API_KEY env var)')
|
|
34
|
+
.option('--api-url <url>', 'API base URL (or set SF_API_URL env var)');
|
|
35
|
+
// Helper to extract global opts from any command's opts chain
|
|
36
|
+
function globalOpts(opts) {
|
|
37
|
+
// Commander merges parent opts into child opts when using .optsWithGlobals()
|
|
38
|
+
return {
|
|
39
|
+
apiKey: opts.apiKey || opts.parent?.apiKey,
|
|
40
|
+
apiUrl: opts.apiUrl || opts.parent?.apiUrl,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// ── sf list ──────────────────────────────────────────────────────────────────
|
|
44
|
+
program
|
|
45
|
+
.command('list')
|
|
46
|
+
.description('List all theses')
|
|
47
|
+
.action(async (_opts, cmd) => {
|
|
48
|
+
const g = cmd.optsWithGlobals();
|
|
49
|
+
await run(() => (0, list_js_1.listCommand)({ apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
50
|
+
});
|
|
51
|
+
// ── sf get <id> ───────────────────────────────────────────────────────────────
|
|
52
|
+
program
|
|
53
|
+
.command('get <id>')
|
|
54
|
+
.description('Get full thesis details')
|
|
55
|
+
.option('--json', 'Output raw JSON')
|
|
56
|
+
.action(async (id, opts, cmd) => {
|
|
57
|
+
const g = cmd.optsWithGlobals();
|
|
58
|
+
await run(() => (0, get_js_1.getCommand)(id, { json: opts.json, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
59
|
+
});
|
|
60
|
+
// ── sf context <id> ───────────────────────────────────────────────────────────
|
|
61
|
+
program
|
|
62
|
+
.command('context <id>')
|
|
63
|
+
.description('Get thesis context snapshot (primary command for agents)')
|
|
64
|
+
.option('--json', 'Output raw JSON')
|
|
65
|
+
.action(async (id, opts, cmd) => {
|
|
66
|
+
const g = cmd.optsWithGlobals();
|
|
67
|
+
await run(() => (0, context_js_1.contextCommand)(id, { json: opts.json, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
68
|
+
});
|
|
69
|
+
// ── sf create <thesis> ────────────────────────────────────────────────────────
|
|
70
|
+
program
|
|
71
|
+
.command('create <thesis>')
|
|
72
|
+
.description('Create a new thesis (sync by default — waits for formation)')
|
|
73
|
+
.option('--async', 'Async mode — return immediately without waiting')
|
|
74
|
+
.action(async (thesis, opts, cmd) => {
|
|
75
|
+
const g = cmd.optsWithGlobals();
|
|
76
|
+
await run(() => (0, create_js_1.createCommand)(thesis, { async: opts.async, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
77
|
+
});
|
|
78
|
+
// ── sf signal <id> <content> ──────────────────────────────────────────────────
|
|
79
|
+
program
|
|
80
|
+
.command('signal <id> <content>')
|
|
81
|
+
.description('Inject a signal into the thesis queue')
|
|
82
|
+
.option('--type <type>', 'Signal type: news | user_note | external', 'user_note')
|
|
83
|
+
.action(async (id, content, opts, cmd) => {
|
|
84
|
+
const g = cmd.optsWithGlobals();
|
|
85
|
+
await run(() => (0, signal_js_1.signalCommand)(id, content, { type: opts.type, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
86
|
+
});
|
|
87
|
+
// ── sf evaluate <id> ──────────────────────────────────────────────────────────
|
|
88
|
+
program
|
|
89
|
+
.command('evaluate <id>')
|
|
90
|
+
.description('Trigger a deep evaluation (heavy model, force-heavy mode)')
|
|
91
|
+
.action(async (id, _opts, cmd) => {
|
|
92
|
+
const g = cmd.optsWithGlobals();
|
|
93
|
+
await run(() => (0, evaluate_js_1.evaluateCommand)(id, { apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
94
|
+
});
|
|
95
|
+
// ── sf scan [query] ───────────────────────────────────────────────────────────
|
|
96
|
+
program
|
|
97
|
+
.command('scan [query]')
|
|
98
|
+
.description('Explore Kalshi markets (no auth required)')
|
|
99
|
+
.option('--series <ticker>', 'List events + markets for a series (e.g. KXWTIMAX)')
|
|
100
|
+
.option('--market <ticker>', 'Get single market detail (e.g. KXWTIMAX-26DEC31-T140)')
|
|
101
|
+
.option('--json', 'Output raw JSON')
|
|
102
|
+
.action(async (query, opts, cmd) => {
|
|
103
|
+
const g = cmd.optsWithGlobals();
|
|
104
|
+
const q = query || '';
|
|
105
|
+
if (!q && !opts.series && !opts.market) {
|
|
106
|
+
console.error('Usage: sf scan "keywords" OR sf scan --series TICKER OR sf scan --market TICKER');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
await run(() => (0, scan_js_1.scanCommand)(q, {
|
|
110
|
+
series: opts.series,
|
|
111
|
+
market: opts.market,
|
|
112
|
+
json: opts.json,
|
|
113
|
+
apiKey: g.apiKey,
|
|
114
|
+
apiUrl: g.apiUrl,
|
|
115
|
+
}));
|
|
116
|
+
});
|
|
117
|
+
// ── Error wrapper ─────────────────────────────────────────────────────────────
|
|
118
|
+
async function run(fn) {
|
|
119
|
+
try {
|
|
120
|
+
await fn();
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
124
|
+
(0, utils_js_1.die)(msg);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
program.parse();
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI formatting utilities
|
|
3
|
+
*
|
|
4
|
+
* ANSI escape codes only — no chalk dependency.
|
|
5
|
+
*/
|
|
6
|
+
export declare const c: {
|
|
7
|
+
readonly reset: "\u001B[0m";
|
|
8
|
+
readonly bold: "\u001B[1m";
|
|
9
|
+
readonly dim: "\u001B[2m";
|
|
10
|
+
readonly red: "\u001B[31m";
|
|
11
|
+
readonly green: "\u001B[32m";
|
|
12
|
+
readonly yellow: "\u001B[33m";
|
|
13
|
+
readonly blue: "\u001B[34m";
|
|
14
|
+
readonly magenta: "\u001B[35m";
|
|
15
|
+
readonly cyan: "\u001B[36m";
|
|
16
|
+
readonly white: "\u001B[37m";
|
|
17
|
+
readonly gray: "\u001B[90m";
|
|
18
|
+
};
|
|
19
|
+
/** Format a number as volume string: 1234567 → "1.2M", 12345 → "12K" */
|
|
20
|
+
export declare function vol(n: number | string | null | undefined): string;
|
|
21
|
+
/** Format a dollar amount as cents: 0.55 → "55¢" */
|
|
22
|
+
export declare function cents(n: number | string | null | undefined): string;
|
|
23
|
+
/** Format a probability as percentage: 0.82 → "82%" */
|
|
24
|
+
export declare function pct(n: number | string | null | undefined): string;
|
|
25
|
+
/** Format a confidence delta with arrow: +0.10 → "↑ +10%", -0.05 → "↓ -5%" */
|
|
26
|
+
export declare function delta(n: number): string;
|
|
27
|
+
/** Format ISO date string to short form: "2026-03-12T11:13:00Z" → "Mar 12 11:13" */
|
|
28
|
+
export declare function shortDate(iso: string | null | undefined): string;
|
|
29
|
+
/** Pad/truncate a string to exact width */
|
|
30
|
+
export declare function pad(s: string, width: number): string;
|
|
31
|
+
/** Right-align a string to exact width */
|
|
32
|
+
export declare function rpad(s: string, width: number): string;
|
|
33
|
+
/** Print a horizontal rule */
|
|
34
|
+
export declare function hr(width?: number): void;
|
|
35
|
+
/** Print an error and exit */
|
|
36
|
+
export declare function die(msg: string): never;
|
|
37
|
+
/** Print a section header */
|
|
38
|
+
export declare function header(title: string): void;
|
|
39
|
+
/** Truncate a string with ellipsis */
|
|
40
|
+
export declare function trunc(s: string, maxLen: number): string;
|
|
41
|
+
/** Short ID: first 8 chars of a UUID */
|
|
42
|
+
export declare function shortId(id: string): string;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CLI formatting utilities
|
|
4
|
+
*
|
|
5
|
+
* ANSI escape codes only — no chalk dependency.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.c = void 0;
|
|
9
|
+
exports.vol = vol;
|
|
10
|
+
exports.cents = cents;
|
|
11
|
+
exports.pct = pct;
|
|
12
|
+
exports.delta = delta;
|
|
13
|
+
exports.shortDate = shortDate;
|
|
14
|
+
exports.pad = pad;
|
|
15
|
+
exports.rpad = rpad;
|
|
16
|
+
exports.hr = hr;
|
|
17
|
+
exports.die = die;
|
|
18
|
+
exports.header = header;
|
|
19
|
+
exports.trunc = trunc;
|
|
20
|
+
exports.shortId = shortId;
|
|
21
|
+
// ===== ANSI Colors =====
|
|
22
|
+
exports.c = {
|
|
23
|
+
reset: '\x1b[0m',
|
|
24
|
+
bold: '\x1b[1m',
|
|
25
|
+
dim: '\x1b[2m',
|
|
26
|
+
red: '\x1b[31m',
|
|
27
|
+
green: '\x1b[32m',
|
|
28
|
+
yellow: '\x1b[33m',
|
|
29
|
+
blue: '\x1b[34m',
|
|
30
|
+
magenta: '\x1b[35m',
|
|
31
|
+
cyan: '\x1b[36m',
|
|
32
|
+
white: '\x1b[37m',
|
|
33
|
+
gray: '\x1b[90m',
|
|
34
|
+
};
|
|
35
|
+
// ===== Formatting helpers =====
|
|
36
|
+
/** Format a number as volume string: 1234567 → "1.2M", 12345 → "12K" */
|
|
37
|
+
function vol(n) {
|
|
38
|
+
if (n === null || n === undefined)
|
|
39
|
+
return '-';
|
|
40
|
+
const v = typeof n === 'string' ? parseFloat(n) : n;
|
|
41
|
+
if (isNaN(v))
|
|
42
|
+
return '-';
|
|
43
|
+
if (v >= 1_000_000)
|
|
44
|
+
return `${(v / 1_000_000).toFixed(1)}M`;
|
|
45
|
+
if (v >= 1_000)
|
|
46
|
+
return `${(v / 1_000).toFixed(0)}K`;
|
|
47
|
+
return v.toString();
|
|
48
|
+
}
|
|
49
|
+
/** Format a dollar amount as cents: 0.55 → "55¢" */
|
|
50
|
+
function cents(n) {
|
|
51
|
+
if (n === null || n === undefined)
|
|
52
|
+
return '-';
|
|
53
|
+
const v = typeof n === 'string' ? parseFloat(n) : n;
|
|
54
|
+
if (isNaN(v))
|
|
55
|
+
return '-';
|
|
56
|
+
return `${Math.round(v * 100)}¢`;
|
|
57
|
+
}
|
|
58
|
+
/** Format a probability as percentage: 0.82 → "82%" */
|
|
59
|
+
function pct(n) {
|
|
60
|
+
if (n === null || n === undefined)
|
|
61
|
+
return '-';
|
|
62
|
+
const v = typeof n === 'string' ? parseFloat(n) : n;
|
|
63
|
+
if (isNaN(v))
|
|
64
|
+
return '-';
|
|
65
|
+
return `${Math.round(v * 100)}%`;
|
|
66
|
+
}
|
|
67
|
+
/** Format a confidence delta with arrow: +0.10 → "↑ +10%", -0.05 → "↓ -5%" */
|
|
68
|
+
function delta(n) {
|
|
69
|
+
const arrow = n > 0 ? '↑' : n < 0 ? '↓' : '→';
|
|
70
|
+
const sign = n > 0 ? '+' : '';
|
|
71
|
+
return `${arrow} ${sign}${Math.round(n * 100)}%`;
|
|
72
|
+
}
|
|
73
|
+
/** Format ISO date string to short form: "2026-03-12T11:13:00Z" → "Mar 12 11:13" */
|
|
74
|
+
function shortDate(iso) {
|
|
75
|
+
if (!iso)
|
|
76
|
+
return '-';
|
|
77
|
+
try {
|
|
78
|
+
const d = new Date(iso);
|
|
79
|
+
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
80
|
+
const month = months[d.getMonth()];
|
|
81
|
+
const day = d.getDate();
|
|
82
|
+
const hours = d.getHours().toString().padStart(2, '0');
|
|
83
|
+
const mins = d.getMinutes().toString().padStart(2, '0');
|
|
84
|
+
return `${month} ${day} ${hours}:${mins}`;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return iso.slice(0, 16);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Pad/truncate a string to exact width */
|
|
91
|
+
function pad(s, width) {
|
|
92
|
+
if (s.length >= width)
|
|
93
|
+
return s.slice(0, width);
|
|
94
|
+
return s + ' '.repeat(width - s.length);
|
|
95
|
+
}
|
|
96
|
+
/** Right-align a string to exact width */
|
|
97
|
+
function rpad(s, width) {
|
|
98
|
+
if (s.length >= width)
|
|
99
|
+
return s.slice(0, width);
|
|
100
|
+
return ' '.repeat(width - s.length) + s;
|
|
101
|
+
}
|
|
102
|
+
/** Print a horizontal rule */
|
|
103
|
+
function hr(width = 80) {
|
|
104
|
+
console.log(exports.c.dim + '─'.repeat(width) + exports.c.reset);
|
|
105
|
+
}
|
|
106
|
+
/** Print an error and exit */
|
|
107
|
+
function die(msg) {
|
|
108
|
+
console.error(`${exports.c.red}Error:${exports.c.reset} ${msg}`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
/** Print a section header */
|
|
112
|
+
function header(title) {
|
|
113
|
+
console.log(`\n${exports.c.bold}${exports.c.cyan}${title}${exports.c.reset}`);
|
|
114
|
+
}
|
|
115
|
+
/** Truncate a string with ellipsis */
|
|
116
|
+
function trunc(s, maxLen) {
|
|
117
|
+
if (s.length <= maxLen)
|
|
118
|
+
return s;
|
|
119
|
+
return s.slice(0, maxLen - 1) + '…';
|
|
120
|
+
}
|
|
121
|
+
/** Short ID: first 8 chars of a UUID */
|
|
122
|
+
function shortId(id) {
|
|
123
|
+
return id.slice(0, 8);
|
|
124
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@spfunctions/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for SimpleFunctions prediction market thesis agent",
|
|
5
|
+
"bin": {
|
|
6
|
+
"sf": "./dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"dev": "tsx src/index.ts",
|
|
11
|
+
"prepublishOnly": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"commander": "^12.0.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"typescript": "^5.0.0",
|
|
18
|
+
"tsx": "^4.0.0",
|
|
19
|
+
"@types/node": "^20.0.0"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"prediction-markets",
|
|
26
|
+
"thesis-agent",
|
|
27
|
+
"kalshi",
|
|
28
|
+
"polymarket",
|
|
29
|
+
"cli"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/simplefunctions/simplefunctions"
|
|
35
|
+
}
|
|
36
|
+
}
|