@spfunctions/cli 1.4.4 → 1.5.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 +205 -48
- package/dist/cache.d.ts +6 -0
- package/dist/cache.js +31 -0
- package/dist/cache.test.d.ts +1 -0
- package/dist/cache.test.js +73 -0
- package/dist/client.test.d.ts +1 -0
- package/dist/client.test.js +89 -0
- package/dist/commands/agent.js +594 -106
- package/dist/commands/book.d.ts +17 -0
- package/dist/commands/book.js +220 -0
- package/dist/commands/dashboard.d.ts +6 -3
- package/dist/commands/dashboard.js +53 -22
- package/dist/commands/liquidity.d.ts +2 -0
- package/dist/commands/liquidity.js +128 -43
- package/dist/commands/performance.js +9 -2
- package/dist/commands/positions.js +50 -0
- package/dist/commands/scan.d.ts +1 -0
- package/dist/commands/scan.js +66 -15
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +71 -6
- package/dist/commands/telegram.d.ts +15 -0
- package/dist/commands/telegram.js +125 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +9 -0
- package/dist/config.test.d.ts +1 -0
- package/dist/config.test.js +138 -0
- package/dist/index.js +107 -9
- package/dist/polymarket.d.ts +237 -0
- package/dist/polymarket.js +353 -0
- package/dist/polymarket.test.d.ts +1 -0
- package/dist/polymarket.test.js +424 -0
- package/dist/telegram/agent-bridge.d.ts +15 -0
- package/dist/telegram/agent-bridge.js +368 -0
- package/dist/telegram/bot.d.ts +10 -0
- package/dist/telegram/bot.js +297 -0
- package/dist/telegram/commands.d.ts +11 -0
- package/dist/telegram/commands.js +120 -0
- package/dist/telegram/format.d.ts +11 -0
- package/dist/telegram/format.js +51 -0
- package/dist/telegram/format.test.d.ts +1 -0
- package/dist/telegram/format.test.js +73 -0
- package/dist/telegram/poller.d.ts +6 -0
- package/dist/telegram/poller.js +32 -0
- package/dist/topics.d.ts +3 -0
- package/dist/topics.js +65 -7
- package/dist/topics.test.d.ts +1 -0
- package/dist/topics.test.js +131 -0
- package/dist/tui/border.d.ts +33 -0
- package/dist/tui/border.js +87 -0
- package/dist/tui/chart.d.ts +19 -0
- package/dist/tui/chart.js +117 -0
- package/dist/tui/dashboard.d.ts +9 -0
- package/dist/tui/dashboard.js +814 -0
- package/dist/tui/layout.d.ts +16 -0
- package/dist/tui/layout.js +41 -0
- package/dist/tui/screen.d.ts +33 -0
- package/dist/tui/screen.js +102 -0
- package/dist/tui/state.d.ts +40 -0
- package/dist/tui/state.js +36 -0
- package/dist/tui/widgets/commandbar.d.ts +8 -0
- package/dist/tui/widgets/commandbar.js +82 -0
- package/dist/tui/widgets/detail.d.ts +9 -0
- package/dist/tui/widgets/detail.js +151 -0
- package/dist/tui/widgets/edges.d.ts +4 -0
- package/dist/tui/widgets/edges.js +34 -0
- package/dist/tui/widgets/liquidity.d.ts +9 -0
- package/dist/tui/widgets/liquidity.js +142 -0
- package/dist/tui/widgets/orders.d.ts +4 -0
- package/dist/tui/widgets/orders.js +37 -0
- package/dist/tui/widgets/portfolio.d.ts +4 -0
- package/dist/tui/widgets/portfolio.js +59 -0
- package/dist/tui/widgets/signals.d.ts +4 -0
- package/dist/tui/widgets/signals.js +31 -0
- package/dist/tui/widgets/statusbar.d.ts +8 -0
- package/dist/tui/widgets/statusbar.js +72 -0
- package/dist/tui/widgets/thesis.d.ts +4 -0
- package/dist/tui/widgets/thesis.js +66 -0
- package/dist/tui/widgets/trade.d.ts +9 -0
- package/dist/tui/widgets/trade.js +117 -0
- package/dist/tui/widgets/upcoming.d.ts +4 -0
- package/dist/tui/widgets/upcoming.js +41 -0
- package/dist/tui/widgets/whatif.d.ts +7 -0
- package/dist/tui/widgets/whatif.js +113 -0
- package/dist/utils.test.d.ts +1 -0
- package/dist/utils.test.js +111 -0
- package/package.json +6 -2
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* What-if view — scenario analysis with BEFORE/AFTER comparison
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.renderWhatif = renderWhatif;
|
|
7
|
+
const border_js_1 = require("../border.js");
|
|
8
|
+
const HIGHLIGHT_BG = (0, border_js_1.bgRgb)(25, 25, 30);
|
|
9
|
+
function renderWhatif(screen, region, state) {
|
|
10
|
+
(0, border_js_1.drawBorder)(screen, region, 'WHAT-IF SCENARIO');
|
|
11
|
+
const x = region.col + 2;
|
|
12
|
+
const w = region.width - 4;
|
|
13
|
+
let line = 1;
|
|
14
|
+
if (!state.whatifResult) {
|
|
15
|
+
screen.write(region.row + line, x, 'No what-if data. Press "w" from overview to start.', border_js_1.CLR.dim);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const result = state.whatifResult;
|
|
19
|
+
const scenarios = result.scenarios || result.overrides || [];
|
|
20
|
+
const halfW = Math.floor(w / 2) - 2;
|
|
21
|
+
// ── Top: Scenario list with cursor ──
|
|
22
|
+
screen.write(region.row + line, x, 'Scenarios:', border_js_1.CLR.title);
|
|
23
|
+
line++;
|
|
24
|
+
if (scenarios.length === 0) {
|
|
25
|
+
screen.write(region.row + line, x, ' (no scenarios available)', border_js_1.CLR.dim);
|
|
26
|
+
line += 2;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
for (let i = 0; i < scenarios.length; i++) {
|
|
30
|
+
if (line >= region.height - 8)
|
|
31
|
+
break;
|
|
32
|
+
const sc = scenarios[i];
|
|
33
|
+
const selected = state.whatifScenarioIndex === i;
|
|
34
|
+
const marker = selected ? '\u25B8 ' : ' ';
|
|
35
|
+
const name = sc.name || sc.description || sc.nodeId || `Scenario ${i + 1}`;
|
|
36
|
+
const bg = selected ? HIGHLIGHT_BG : '';
|
|
37
|
+
screen.write(region.row + line, x, (0, border_js_1.fit)(`${marker}${name}`, w), selected ? border_js_1.CLR.white : border_js_1.CLR.text, bg);
|
|
38
|
+
line++;
|
|
39
|
+
}
|
|
40
|
+
line++;
|
|
41
|
+
}
|
|
42
|
+
// ── Divider ──
|
|
43
|
+
for (let c = x; c < x + w; c++) {
|
|
44
|
+
screen.write(region.row + line, c, '─', border_js_1.CLR.borderDim);
|
|
45
|
+
}
|
|
46
|
+
line++;
|
|
47
|
+
// ── Bottom: BEFORE vs AFTER comparison ──
|
|
48
|
+
const beforeCol = x;
|
|
49
|
+
const afterCol = x + halfW + 3;
|
|
50
|
+
// Headers
|
|
51
|
+
screen.write(region.row + line, beforeCol, (0, border_js_1.fit)('BEFORE', halfW), border_js_1.CLR.title);
|
|
52
|
+
screen.write(region.row + line, afterCol, (0, border_js_1.fit)('AFTER', halfW), border_js_1.CLR.title);
|
|
53
|
+
line++;
|
|
54
|
+
const before = result.before || result.current || {};
|
|
55
|
+
const after = result.after || result.proposed || {};
|
|
56
|
+
// Confidence
|
|
57
|
+
const confBefore = before.confidence != null ? `${Math.round(before.confidence * 100)}%` : '?%';
|
|
58
|
+
const confAfter = after.confidence != null ? `${Math.round(after.confidence * 100)}%` : '?%';
|
|
59
|
+
const confDelta = (after.confidence ?? 0) - (before.confidence ?? 0);
|
|
60
|
+
const confDeltaStr = confDelta >= 0 ? `+${Math.round(confDelta * 100)}%` : `${Math.round(confDelta * 100)}%`;
|
|
61
|
+
screen.writeStyled(region.row + line, beforeCol, [
|
|
62
|
+
{ text: 'Confidence: ', fg: border_js_1.CLR.dim },
|
|
63
|
+
{ text: confBefore, fg: border_js_1.CLR.text },
|
|
64
|
+
]);
|
|
65
|
+
screen.writeStyled(region.row + line, afterCol, [
|
|
66
|
+
{ text: 'Confidence: ', fg: border_js_1.CLR.dim },
|
|
67
|
+
{ text: confAfter, fg: border_js_1.CLR.text },
|
|
68
|
+
{ text: ` (${confDeltaStr})`, fg: confDelta >= 0 ? border_js_1.CLR.green : border_js_1.CLR.red },
|
|
69
|
+
]);
|
|
70
|
+
line++;
|
|
71
|
+
// Edge changes
|
|
72
|
+
const beforeEdges = before.edges || [];
|
|
73
|
+
const afterEdges = after.edges || [];
|
|
74
|
+
screen.write(region.row + line, beforeCol, 'Edges:', border_js_1.CLR.dim);
|
|
75
|
+
screen.write(region.row + line, afterCol, 'Edges:', border_js_1.CLR.dim);
|
|
76
|
+
line++;
|
|
77
|
+
const maxEdgeRows = Math.min(Math.max(beforeEdges.length, afterEdges.length), region.height - line - 3);
|
|
78
|
+
for (let i = 0; i < maxEdgeRows; i++) {
|
|
79
|
+
const be = beforeEdges[i];
|
|
80
|
+
const ae = afterEdges[i];
|
|
81
|
+
if (be) {
|
|
82
|
+
const name = (be.market || be.ticker || '').slice(0, halfW - 10);
|
|
83
|
+
const edgeStr = `${be.edge >= 0 ? '+' : ''}${be.edge}`;
|
|
84
|
+
screen.writeStyled(region.row + line, beforeCol, [
|
|
85
|
+
{ text: (0, border_js_1.fit)(name, halfW - 8), fg: border_js_1.CLR.text },
|
|
86
|
+
{ text: (0, border_js_1.fit)(edgeStr, 6, 'right'), fg: be.edge >= 0 ? border_js_1.CLR.green : border_js_1.CLR.red },
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
if (ae) {
|
|
90
|
+
const name = (ae.market || ae.ticker || '').slice(0, halfW - 10);
|
|
91
|
+
const edgeStr = `${ae.edge >= 0 ? '+' : ''}${ae.edge}`;
|
|
92
|
+
const delta = ae.edgeDelta ?? (ae.edge - (be?.edge ?? 0));
|
|
93
|
+
const deltaStr = delta >= 0 ? `+${delta}` : `${delta}`;
|
|
94
|
+
screen.writeStyled(region.row + line, afterCol, [
|
|
95
|
+
{ text: (0, border_js_1.fit)(name, halfW - 14), fg: border_js_1.CLR.text },
|
|
96
|
+
{ text: (0, border_js_1.fit)(edgeStr, 6, 'right'), fg: ae.edge >= 0 ? border_js_1.CLR.green : border_js_1.CLR.red },
|
|
97
|
+
{ text: (0, border_js_1.fit)(deltaStr, 6, 'right'), fg: delta >= 0 ? border_js_1.CLR.green : border_js_1.CLR.red },
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
100
|
+
line++;
|
|
101
|
+
}
|
|
102
|
+
// P&L impact summary
|
|
103
|
+
if (line < region.height - 2) {
|
|
104
|
+
line++;
|
|
105
|
+
const pnlImpact = result.pnlImpact ?? result.pnl_impact;
|
|
106
|
+
if (pnlImpact != null) {
|
|
107
|
+
screen.writeStyled(region.row + line, x, [
|
|
108
|
+
{ text: 'P&L Impact: ', fg: border_js_1.CLR.dim },
|
|
109
|
+
{ text: `${pnlImpact >= 0 ? '+' : ''}$${Math.abs(pnlImpact).toFixed(2)}`, fg: pnlImpact >= 0 ? border_js_1.CLR.green : border_js_1.CLR.red },
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const utils_js_1 = require("./utils.js");
|
|
5
|
+
(0, vitest_1.describe)('vol', () => {
|
|
6
|
+
(0, vitest_1.it)('formats millions', () => {
|
|
7
|
+
(0, vitest_1.expect)((0, utils_js_1.vol)(1_234_567)).toBe('1.2M');
|
|
8
|
+
(0, vitest_1.expect)((0, utils_js_1.vol)(5_000_000)).toBe('5.0M');
|
|
9
|
+
});
|
|
10
|
+
(0, vitest_1.it)('formats thousands', () => {
|
|
11
|
+
(0, vitest_1.expect)((0, utils_js_1.vol)(12_345)).toBe('12K');
|
|
12
|
+
(0, vitest_1.expect)((0, utils_js_1.vol)(1_000)).toBe('1K');
|
|
13
|
+
});
|
|
14
|
+
(0, vitest_1.it)('formats small numbers as-is', () => {
|
|
15
|
+
(0, vitest_1.expect)((0, utils_js_1.vol)(500)).toBe('500');
|
|
16
|
+
(0, vitest_1.expect)((0, utils_js_1.vol)(0)).toBe('0');
|
|
17
|
+
});
|
|
18
|
+
(0, vitest_1.it)('handles null/undefined/NaN', () => {
|
|
19
|
+
(0, vitest_1.expect)((0, utils_js_1.vol)(null)).toBe('-');
|
|
20
|
+
(0, vitest_1.expect)((0, utils_js_1.vol)(undefined)).toBe('-');
|
|
21
|
+
(0, vitest_1.expect)((0, utils_js_1.vol)('abc')).toBe('-');
|
|
22
|
+
});
|
|
23
|
+
(0, vitest_1.it)('parses string numbers', () => {
|
|
24
|
+
(0, vitest_1.expect)((0, utils_js_1.vol)('1500000')).toBe('1.5M');
|
|
25
|
+
(0, vitest_1.expect)((0, utils_js_1.vol)('5000')).toBe('5K');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
(0, vitest_1.describe)('cents', () => {
|
|
29
|
+
(0, vitest_1.it)('converts decimal to cents', () => {
|
|
30
|
+
(0, vitest_1.expect)((0, utils_js_1.cents)(0.55)).toBe('55¢');
|
|
31
|
+
(0, vitest_1.expect)((0, utils_js_1.cents)(1.0)).toBe('100¢');
|
|
32
|
+
(0, vitest_1.expect)((0, utils_js_1.cents)(0)).toBe('0¢');
|
|
33
|
+
});
|
|
34
|
+
(0, vitest_1.it)('handles null/undefined', () => {
|
|
35
|
+
(0, vitest_1.expect)((0, utils_js_1.cents)(null)).toBe('-');
|
|
36
|
+
(0, vitest_1.expect)((0, utils_js_1.cents)(undefined)).toBe('-');
|
|
37
|
+
});
|
|
38
|
+
(0, vitest_1.it)('parses strings', () => {
|
|
39
|
+
(0, vitest_1.expect)((0, utils_js_1.cents)('0.42')).toBe('42¢');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
(0, vitest_1.describe)('pct', () => {
|
|
43
|
+
(0, vitest_1.it)('converts decimal to percentage', () => {
|
|
44
|
+
(0, vitest_1.expect)((0, utils_js_1.pct)(0.82)).toBe('82%');
|
|
45
|
+
(0, vitest_1.expect)((0, utils_js_1.pct)(1.0)).toBe('100%');
|
|
46
|
+
(0, vitest_1.expect)((0, utils_js_1.pct)(0)).toBe('0%');
|
|
47
|
+
});
|
|
48
|
+
(0, vitest_1.it)('handles null/undefined', () => {
|
|
49
|
+
(0, vitest_1.expect)((0, utils_js_1.pct)(null)).toBe('-');
|
|
50
|
+
(0, vitest_1.expect)((0, utils_js_1.pct)(undefined)).toBe('-');
|
|
51
|
+
});
|
|
52
|
+
(0, vitest_1.it)('rounds to nearest integer', () => {
|
|
53
|
+
(0, vitest_1.expect)((0, utils_js_1.pct)(0.827)).toBe('83%');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
(0, vitest_1.describe)('delta', () => {
|
|
57
|
+
(0, vitest_1.it)('shows up arrow for positive', () => {
|
|
58
|
+
(0, vitest_1.expect)((0, utils_js_1.delta)(0.10)).toBe('↑ +10%');
|
|
59
|
+
});
|
|
60
|
+
(0, vitest_1.it)('shows down arrow for negative', () => {
|
|
61
|
+
(0, vitest_1.expect)((0, utils_js_1.delta)(-0.05)).toBe('↓ -5%');
|
|
62
|
+
});
|
|
63
|
+
(0, vitest_1.it)('shows right arrow for zero', () => {
|
|
64
|
+
(0, vitest_1.expect)((0, utils_js_1.delta)(0)).toBe('→ 0%');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
(0, vitest_1.describe)('shortDate', () => {
|
|
68
|
+
(0, vitest_1.it)('formats ISO date', () => {
|
|
69
|
+
const result = (0, utils_js_1.shortDate)('2026-03-12T11:13:00Z');
|
|
70
|
+
(0, vitest_1.expect)(result).toMatch(/Mar 12/);
|
|
71
|
+
});
|
|
72
|
+
(0, vitest_1.it)('handles null/undefined', () => {
|
|
73
|
+
(0, vitest_1.expect)((0, utils_js_1.shortDate)(null)).toBe('-');
|
|
74
|
+
(0, vitest_1.expect)((0, utils_js_1.shortDate)(undefined)).toBe('-');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
(0, vitest_1.describe)('pad', () => {
|
|
78
|
+
(0, vitest_1.it)('pads short strings', () => {
|
|
79
|
+
(0, vitest_1.expect)((0, utils_js_1.pad)('hi', 5)).toBe('hi ');
|
|
80
|
+
});
|
|
81
|
+
(0, vitest_1.it)('truncates long strings', () => {
|
|
82
|
+
(0, vitest_1.expect)((0, utils_js_1.pad)('hello world', 5)).toBe('hello');
|
|
83
|
+
});
|
|
84
|
+
(0, vitest_1.it)('returns exact-width strings unchanged', () => {
|
|
85
|
+
(0, vitest_1.expect)((0, utils_js_1.pad)('abc', 3)).toBe('abc');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
(0, vitest_1.describe)('rpad', () => {
|
|
89
|
+
(0, vitest_1.it)('right-aligns short strings', () => {
|
|
90
|
+
(0, vitest_1.expect)((0, utils_js_1.rpad)('hi', 5)).toBe(' hi');
|
|
91
|
+
});
|
|
92
|
+
(0, vitest_1.it)('truncates long strings', () => {
|
|
93
|
+
(0, vitest_1.expect)((0, utils_js_1.rpad)('hello world', 5)).toBe('hello');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
(0, vitest_1.describe)('trunc', () => {
|
|
97
|
+
(0, vitest_1.it)('truncates with ellipsis', () => {
|
|
98
|
+
(0, vitest_1.expect)((0, utils_js_1.trunc)('hello world', 8)).toBe('hello w…');
|
|
99
|
+
});
|
|
100
|
+
(0, vitest_1.it)('returns short strings unchanged', () => {
|
|
101
|
+
(0, vitest_1.expect)((0, utils_js_1.trunc)('hello', 10)).toBe('hello');
|
|
102
|
+
});
|
|
103
|
+
(0, vitest_1.it)('returns exact-length strings unchanged', () => {
|
|
104
|
+
(0, vitest_1.expect)((0, utils_js_1.trunc)('hello', 5)).toBe('hello');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
(0, vitest_1.describe)('shortId', () => {
|
|
108
|
+
(0, vitest_1.it)('returns first 8 chars', () => {
|
|
109
|
+
(0, vitest_1.expect)((0, utils_js_1.shortId)('f582bf76-3113-4208-b0c1-abc')).toBe('f582bf76');
|
|
110
|
+
});
|
|
111
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spfunctions/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
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"
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "node ./node_modules/typescript/bin/tsc",
|
|
10
10
|
"dev": "tsx src/index.ts",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:watch": "vitest",
|
|
11
13
|
"prepublishOnly": "npm run build"
|
|
12
14
|
},
|
|
13
15
|
"dependencies": {
|
|
@@ -15,12 +17,14 @@
|
|
|
15
17
|
"@mariozechner/pi-ai": "^0.57.1",
|
|
16
18
|
"@mariozechner/pi-tui": "^0.57.1",
|
|
17
19
|
"commander": "^12.0.0",
|
|
20
|
+
"grammy": "^1.41.1",
|
|
18
21
|
"kalshi-typescript": "^3.9.0"
|
|
19
22
|
},
|
|
20
23
|
"devDependencies": {
|
|
21
24
|
"@types/node": "^20.0.0",
|
|
22
25
|
"tsx": "^4.0.0",
|
|
23
|
-
"typescript": "^5.0.0"
|
|
26
|
+
"typescript": "^5.0.0",
|
|
27
|
+
"vitest": "^4.1.0"
|
|
24
28
|
},
|
|
25
29
|
"files": [
|
|
26
30
|
"dist"
|