sigrank-mcp 0.7.0 → 0.8.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 +183 -80
- package/cli.mjs +274 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,93 +1,196 @@
|
|
|
1
|
+
# sigrank-mcp
|
|
2
|
+
|
|
3
|
+
**SigRank terminal client + MCP server.** Check the leaderboard, score your token cascade, and publish your rank — directly from your terminal or any MCP-compatible agent (Claude Code, Cursor, Windsurf).
|
|
4
|
+
|
|
5
|
+
Zero external dependencies. Pure Node.js. No account required to read.
|
|
6
|
+
|
|
7
|
+
→ **[signalaf.com](https://signalaf.com)**
|
|
8
|
+
|
|
1
9
|
---
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx sigrank-mcp board
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
No install needed. `npx` runs it directly. Or install globally:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g sigrank-mcp
|
|
21
|
+
sigrank-mcp board
|
|
22
|
+
```
|
|
23
|
+
|
|
7
24
|
---
|
|
8
25
|
|
|
9
|
-
|
|
26
|
+
## Commands
|
|
10
27
|
|
|
11
|
-
|
|
12
|
-
leaderboard into a tool every agent can invoke (distribution moat). Token-only, no auth,
|
|
13
|
-
no transcript content.
|
|
28
|
+
### `board` — Live leaderboard
|
|
14
29
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
| `get_operator(codename)` | one operator's live profile |
|
|
21
|
-
| `submit_paste(text, codename)` | **rank AND publish** in one call: local cascade + card, then POSTs the raw paste to the board's web-paste endpoint (server re-scores authoritatively). `codename` required to publish; omit for preview-only. |
|
|
22
|
-
| `tokenpull(platform?)` | **in-house local reader** (no ccusage/tokscale): scans local logs → the 4 windows (7d/30d/90d/all) each cascaded. **Claude** (native, recursive incl. `subagents/`, dedup `(session,message)` keep-final, verified vs token-dashboard) + **Codex** (reads `~/.codex/sessions`, estimated via `io_ratio` — Beta from the operator's Claude ratio, else Alpha 2.0; verified vs `ccusage codex`). Zero paste, on-device, token-only. |
|
|
23
|
-
| `tokenpull_submit(codename, window?)` | **the zero-paste flow**: `tokenpull` → publish each window's canonical pillars to the board (server re-scores), tagged with `platform`. `codename` required to publish; omit for preview. |
|
|
30
|
+
```bash
|
|
31
|
+
npx sigrank-mcp board # live, refreshes every 30s
|
|
32
|
+
npx sigrank-mcp board --once # print once and exit
|
|
33
|
+
npx sigrank-mcp board --window 7d
|
|
34
|
+
```
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
Open by design; the proprietary threshold cuts / weights stay server-side.
|
|
36
|
+
**Windows:** `7d` · `30d` (default) · `90d` · `all`
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
```
|
|
39
|
+
⊙ SigRank Leaderboard signalaf.com 02:00:49
|
|
40
|
+
window: 30d · 25 operators
|
|
41
|
+
············································································
|
|
42
|
+
# Operator Class SIGNA SNR Depth Tokens 7d Δ
|
|
43
|
+
············································································
|
|
44
|
+
#1 TransVaultOrigin TRANSMITTER 96.4 96.9% 26.1 18.4K —
|
|
45
|
+
#2 OrcaVanguard TRANSMITTER 88.0 88.0% 23.0 16.0K —
|
|
46
|
+
#3 IronLattice TRANSMITTER 84.0 84.0% 21.6 14.8K —
|
|
47
|
+
#4 PrismCartographer ARCH+ 79.3 79.2% 19.2 12.4K —
|
|
48
|
+
#5 MeridianScribe ARCH+ 76.1 76.4% 17.8 11.2K —
|
|
49
|
+
············································································
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### `me` — Your cascade
|
|
55
|
+
|
|
56
|
+
Score your own token usage across all four measurement windows.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx sigrank-mcp me # auto-detects Claude Code
|
|
60
|
+
npx sigrank-mcp me --platform amp # specify platform
|
|
61
|
+
npx sigrank-mcp me --compare # side-by-side pillar comparison
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Supported platforms:** `claude` · `codex` · `amp` · `gemini` · `qwen` · `goose` · `kimi` · `droid` · `hermes` · `kilo` · `copilot` · `codebuff`
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
⊙ SigRank · me signalaf.com
|
|
68
|
+
platform: claude · source: ~/.claude/projects
|
|
69
|
+
|
|
70
|
+
window Υ Yield SNR Leverage 10xDEV Velocity Class
|
|
71
|
+
─────────────────────────────────────────────────────────────────────
|
|
72
|
+
7d 8 231.4 90.1% 1 823.4x 3.26 8.8x TRANSMITTER
|
|
73
|
+
30d 12 847.2 92.3% 2 041.1x 3.31 9.0x TRANSMITTER
|
|
74
|
+
90d 11 204.6 89.7% 1 994.8x 3.30 8.9x TRANSMITTER
|
|
75
|
+
all 18 436.98 90.0% 2 042.2x 3.31 9.0x TRANSMITTER
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
> Token counts are read locally from your platform's log files. No content is ever read or transmitted — only token counts.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### `watch` — Real-time tune meter
|
|
83
|
+
|
|
84
|
+
Live-updating cascade as you work. Useful for actively optimizing a session.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npx sigrank-mcp watch # refreshes every 30s
|
|
88
|
+
npx sigrank-mcp watch --window 7d
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
34
92
|
|
|
35
|
-
|
|
36
|
-
`node test.mjs` → `rank_paste` reproduces canon: **MO§ES `1251211 11296121 128196310 2555179769`
|
|
37
|
-
→ Υ 18436.98 · lev 2042.2 · TRANSMITTER.** ✅ (math is dependency-free, runs without install.)
|
|
93
|
+
### `--version`
|
|
38
94
|
|
|
39
|
-
## Run
|
|
40
95
|
```bash
|
|
41
|
-
|
|
42
|
-
|
|
96
|
+
npx sigrank-mcp --version
|
|
97
|
+
# 0.7.0
|
|
43
98
|
```
|
|
44
|
-
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## MCP server — use inside Claude Code / Cursor / Windsurf
|
|
103
|
+
|
|
104
|
+
Add to your MCP config (`.mcp.json` or equivalent):
|
|
105
|
+
|
|
45
106
|
```json
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
107
|
+
{
|
|
108
|
+
"mcpServers": {
|
|
109
|
+
"sigrank": {
|
|
110
|
+
"command": "npx",
|
|
111
|
+
"args": ["sigrank-mcp"]
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Once wired, your agent can call these tools directly:
|
|
118
|
+
|
|
119
|
+
| Tool | What it does |
|
|
120
|
+
|---|---|
|
|
121
|
+
| `rank_paste(text)` | Paste token counts → Υ Yield + class + insight card |
|
|
122
|
+
| `get_leaderboard()` | Live public leaderboard |
|
|
123
|
+
| `get_operator(codename)` | One operator's live profile |
|
|
124
|
+
| `submit_paste(text, codename)` | Score locally + publish to the board |
|
|
125
|
+
| `tokenpull(platform?)` | Read local logs → 4-window cascade (no paste needed) |
|
|
126
|
+
| `tokenpull_submit(codename, window?)` | Zero-paste publish: read → sign → post |
|
|
127
|
+
|
|
128
|
+
**Example — score a paste inside Claude:**
|
|
129
|
+
```
|
|
130
|
+
rank_paste("1251211 11296121 128196310 2555179769")
|
|
131
|
+
|
|
132
|
+
→ Υ 18436.98 · SNR 90.0% · Leverage 2042.2x · Class: TRANSMITTER
|
|
133
|
+
→ "This operator holds both axes at once: 9.0x generation AND 2,042x memory leverage..."
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## How the math works
|
|
139
|
+
|
|
140
|
+
SigRank ranks operators on **Υ Yield** — a single number that captures how efficiently
|
|
141
|
+
you use your AI platform's token budget:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
Υ = (cache_read × output) / input²
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Four pillars, one score. Higher is better. The cascade collapses into nine class tiers
|
|
148
|
+
from BASE to TRANSMITTER.
|
|
149
|
+
|
|
150
|
+
| Metric | Formula | What it means |
|
|
73
151
|
|---|---|---|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
152
|
+
| **Υ Yield** | `(Cr × O) / I²` | Overall cascade efficiency |
|
|
153
|
+
| **SNR** | `Cr / (I + Cc)` | Signal-to-noise — how much you're pulling vs pushing |
|
|
154
|
+
| **Leverage** | `Cr / I` | Memory amplification — how hard your cache works |
|
|
155
|
+
| **10xDEV** | `log₁₀(Leverage)` | Orders of magnitude above baseline |
|
|
156
|
+
| **Velocity** | `O / I` | Generation rate — output per unit of input |
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Class tiers
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
TRANSMITTER Υ ≥ 10,000 The closed kinetic loop. Both axes held simultaneously.
|
|
164
|
+
ARCH+ Υ ≥ 5,000 High leverage + strong generation.
|
|
165
|
+
ARCH Υ ≥ 2,500 Architectural thinkers. Deep cache, structured output.
|
|
166
|
+
POWER+ Υ ≥ 1,000 Power users with efficient patterns.
|
|
167
|
+
POWER Υ ≥ 500 Consistent, deliberate operators.
|
|
168
|
+
CORE+ Υ ≥ 200 Developing efficiency. Cache awareness emerging.
|
|
169
|
+
CORE Υ ≥ 50 Active operators. Signal accumulating.
|
|
170
|
+
SIGNAL Υ ≥ 10 Early signal. Patterns not yet locked.
|
|
171
|
+
BASE Υ < 10 Baseline. Every operator starts here.
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Privacy
|
|
177
|
+
|
|
178
|
+
- **Token counts only.** No message content is read, logged, or transmitted — ever.
|
|
179
|
+
- **Local by default.** `me` and `watch` read files on your device only. Numbers stay local unless you explicitly publish.
|
|
180
|
+
- **No account required.** Reading the leaderboard is fully anonymous.
|
|
181
|
+
- **Background tooling excluded.** Memory plugins and observers are filtered out automatically.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Links
|
|
186
|
+
|
|
187
|
+
- **Leaderboard:** [signalaf.com](https://signalaf.com)
|
|
188
|
+
- **Agent (publish your own data):** [sigrank-agent on PyPI](https://pypi.org/project/sigrank-agent/)
|
|
189
|
+
- **npm:** [npmjs.com/package/sigrank-mcp](https://www.npmjs.com/package/sigrank-mcp)
|
|
190
|
+
- **Source:** [github.com/SunrisesIllNeverSee/sigrank-mcp](https://github.com/SunrisesIllNeverSee/sigrank-mcp)
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
MIT — © 2026 Deric J. McHenry / Ello Cello LLC
|
package/cli.mjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* cli.mjs — SigRank terminal UI.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Default (no command): full unified view — all platforms, all windows, token pillars,
|
|
5
|
+
* board position, [S] submit prompt.
|
|
5
6
|
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* npx sigrank-mcp full unified view (default)
|
|
6
9
|
* npx sigrank-mcp board live leaderboard, refreshes every 30s
|
|
7
10
|
* npx sigrank-mcp board --window 7d board for a specific window
|
|
8
11
|
* npx sigrank-mcp board --once print once and exit (no live refresh)
|
|
@@ -738,6 +741,272 @@ async function runWatch({ platform = 'claude', window: win = '7d', refresh = 30
|
|
|
738
741
|
}
|
|
739
742
|
}
|
|
740
743
|
|
|
744
|
+
// ── UNIFIED DEFAULT VIEW ──────────────────────────────────────────────────────
|
|
745
|
+
// npx sigrank-mcp (no args) — pulls everything at once:
|
|
746
|
+
// - all platforms in parallel (only shows ones with data)
|
|
747
|
+
// - all 4 windows per platform
|
|
748
|
+
// - token pillars table (transparency layer)
|
|
749
|
+
// - comparison sources (ccusage, tokscale, token-dashboard) if available
|
|
750
|
+
// - live board position
|
|
751
|
+
// - [S] submit [B] board [Q] quit
|
|
752
|
+
|
|
753
|
+
const ALL_PLATFORMS = [
|
|
754
|
+
'claude','codex','amp','gemini','kimi','qwen','goose','kilo',
|
|
755
|
+
'hermes','droid','codebuff','copilot','openclaw','pi',
|
|
756
|
+
]
|
|
757
|
+
|
|
758
|
+
async function runSigRank() {
|
|
759
|
+
write(HIDE_CURSOR)
|
|
760
|
+
const w = termWidth()
|
|
761
|
+
|
|
762
|
+
// ── 1. Pull everything in parallel ────────────────────────────────────────
|
|
763
|
+
writeln()
|
|
764
|
+
writeln(` ${gold('⊙ SigRank')} ${dim('reading all sources…')}`)
|
|
765
|
+
|
|
766
|
+
const { tokenpullAny } = await import('./tokenpull.mjs')
|
|
767
|
+
|
|
768
|
+
const [platformResults, boardData, ccPillars, tdPillars, tsPillars] = await Promise.all([
|
|
769
|
+
// all platforms
|
|
770
|
+
Promise.allSettled(ALL_PLATFORMS.map(p => tokenpullAny(p))),
|
|
771
|
+
// live board
|
|
772
|
+
callTool('get_leaderboard', {}).catch(() => null),
|
|
773
|
+
// ccusage
|
|
774
|
+
Promise.resolve(ccusagePillars('claude')),
|
|
775
|
+
// token-dashboard
|
|
776
|
+
Promise.resolve(tokenDashPillars()),
|
|
777
|
+
// tokscale
|
|
778
|
+
Promise.resolve(tokscalePillars()),
|
|
779
|
+
])
|
|
780
|
+
|
|
781
|
+
// filter to platforms with actual data
|
|
782
|
+
const active = []
|
|
783
|
+
for (let i = 0; i < ALL_PLATFORMS.length; i++) {
|
|
784
|
+
const r = platformResults[i]
|
|
785
|
+
if (r.status !== 'fulfilled') continue
|
|
786
|
+
const d = r.value
|
|
787
|
+
const all = d.windows?.find(w => w.window === 'all')
|
|
788
|
+
if (!all) continue
|
|
789
|
+
const total = (all.pillars.input ?? 0) + (all.pillars.output ?? 0)
|
|
790
|
+
if (total === 0) continue
|
|
791
|
+
active.push(d)
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// clear loading line
|
|
795
|
+
write(CURSOR_UP(2) + ERASE_LINE + CURSOR_UP(1) + ERASE_LINE)
|
|
796
|
+
|
|
797
|
+
// ── 2. Header ─────────────────────────────────────────────────────────────
|
|
798
|
+
const ts = new Date().toLocaleTimeString('en-US', { hour12: false })
|
|
799
|
+
const header = ` ${gold('⊙ SigRank')} ${bold('Operator Dashboard')}`
|
|
800
|
+
const right = dim(`signalaf.com ${ts}`)
|
|
801
|
+
const gap = Math.max(1, w - stripAnsi(header).length - stripAnsi(right).length)
|
|
802
|
+
writeln()
|
|
803
|
+
writeln(`${header}${' '.repeat(gap)}${right}`)
|
|
804
|
+
|
|
805
|
+
const platformSummary = active.map(d => {
|
|
806
|
+
const all = d.windows.find(w => w.window === 'all')
|
|
807
|
+
return `${cyan(d.platform)} ${dim(`${d.files ?? '?'} files · ${(all?.messages ?? 0).toLocaleString()} msgs`)}`
|
|
808
|
+
}).join(' ')
|
|
809
|
+
writeln(` ${dim('Detected:')} ${platformSummary || dim('no local data found')}`)
|
|
810
|
+
writeln(` ${dim('─'.repeat(w - 4))}`)
|
|
811
|
+
|
|
812
|
+
// ── 3. Cascade table — all platforms × all windows ─────────────────────────
|
|
813
|
+
writeln()
|
|
814
|
+
writeln(` ${bold('Your Cascade')}`)
|
|
815
|
+
const CH = [
|
|
816
|
+
padEnd(dim('Platform'), 10),
|
|
817
|
+
padEnd(dim('Window'), 7),
|
|
818
|
+
padStart(dim('Υ Yield'), 10),
|
|
819
|
+
padStart(dim('SNR'), 7),
|
|
820
|
+
padStart(dim('Leverage'), 10),
|
|
821
|
+
padStart(dim('Velocity'), 9),
|
|
822
|
+
padStart(dim('10xDEV'), 8),
|
|
823
|
+
padEnd(dim('Class'), 13),
|
|
824
|
+
]
|
|
825
|
+
writeln(` ${CH.join(' ')}`)
|
|
826
|
+
writeln(` ${dim('·'.repeat(w - 4))}`)
|
|
827
|
+
|
|
828
|
+
const WINS = ['7d', '30d', '90d', 'all']
|
|
829
|
+
for (const d of active) {
|
|
830
|
+
for (const winKey of WINS) {
|
|
831
|
+
const wdata = d.windows?.find(ww => ww.window === winKey)
|
|
832
|
+
if (!wdata) continue
|
|
833
|
+
const p = wdata.pillars
|
|
834
|
+
const cas = cascadeFromPillars(p)
|
|
835
|
+
if (!cas) continue
|
|
836
|
+
const isTop = cas.yield > 10000
|
|
837
|
+
const clsFn = CLASS_COLOR[cas.class] ?? ((s) => s)
|
|
838
|
+
const cols = [
|
|
839
|
+
padEnd(winKey === '7d' ? cyan(d.platform) : dim(d.platform), 10),
|
|
840
|
+
padEnd(dim(winKey), 7),
|
|
841
|
+
padStart(isTop ? gold(fmtYield(cas.yield)) : fmtYield(cas.yield), 10),
|
|
842
|
+
padStart(fmtSNR(cas.snr), 7),
|
|
843
|
+
padStart(cas.leverage != null ? `${fmtLev(cas.leverage)}×` : '—', 10),
|
|
844
|
+
padStart(cas.velocity != null ? cas.velocity.toFixed(2) + 'x' : '—', 9),
|
|
845
|
+
padStart(cas.dev10x != null ? cas.dev10x.toFixed(2) : '—', 8),
|
|
846
|
+
padEnd(clsFn(cas.class), 13),
|
|
847
|
+
]
|
|
848
|
+
writeln(` ${cols.join(' ')}`)
|
|
849
|
+
}
|
|
850
|
+
writeln()
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// ── 4. Token Pillars transparency table ──────────────────────────────────
|
|
854
|
+
writeln(` ${dim('─'.repeat(w - 4))}`)
|
|
855
|
+
writeln()
|
|
856
|
+
writeln(` ${bold('Token Pillars')} ${dim('(all-time · transparency)')}`)
|
|
857
|
+
|
|
858
|
+
// rows: each active platform + comparison sources
|
|
859
|
+
const PCOLS = [
|
|
860
|
+
padEnd(dim('Source'), 14),
|
|
861
|
+
padStart(dim('Input'), 10),
|
|
862
|
+
padStart(dim('Output'), 10),
|
|
863
|
+
padStart(dim('Cache Write'), 12),
|
|
864
|
+
padStart(dim('Cache Read'), 12),
|
|
865
|
+
padStart(dim('Total'), 10),
|
|
866
|
+
]
|
|
867
|
+
writeln(` ${PCOLS.join(' ')}`)
|
|
868
|
+
writeln(` ${dim('·'.repeat(Math.min(w - 4, 74)))}`)
|
|
869
|
+
|
|
870
|
+
const printPillarRow = (label, colorFn, p, note = '') => {
|
|
871
|
+
if (!p) return
|
|
872
|
+
const i = p.input ?? 0
|
|
873
|
+
const o = p.output ?? 0
|
|
874
|
+
const cw = p.cacheCreate ?? 0
|
|
875
|
+
const cr = p.cacheRead ?? 0
|
|
876
|
+
const total = i + o + cw + cr
|
|
877
|
+
const cols = [
|
|
878
|
+
padEnd(colorFn(label), 14),
|
|
879
|
+
padStart(fmtTokens(i), 10),
|
|
880
|
+
padStart(fmtTokens(o), 10),
|
|
881
|
+
padStart(cw > 0 ? fmtTokens(cw) : dim('—'), 12),
|
|
882
|
+
padStart(cr > 0 ? fmtTokens(cr) : dim('—'), 12),
|
|
883
|
+
padStart(fmtTokens(total), 10),
|
|
884
|
+
]
|
|
885
|
+
writeln(` ${cols.join(' ')}${note ? ' ' + dim(note) : ''}`)
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
for (const d of active) {
|
|
889
|
+
const all = d.windows?.find(ww => ww.window === 'all')
|
|
890
|
+
if (all) printPillarRow(d.platform, cyan, all.pillars)
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// comparison sources (dim, only shown if available)
|
|
894
|
+
if (ccPillars?.all) printPillarRow('ccusage', (s) => paint(c.green, s), ccPillars.all, 'ccusage CLI')
|
|
895
|
+
if (tdPillars?.all) printPillarRow('token-dash', (s) => paint(c.magenta, s), tdPillars.all, 'token-dashboard.db')
|
|
896
|
+
if (tsPillars?.all) printPillarRow('tokscale', (s) => paint(c.blue, s), tsPillars.all, 'tokscale_report.json')
|
|
897
|
+
|
|
898
|
+
// ── 5. Board position ─────────────────────────────────────────────────────
|
|
899
|
+
writeln()
|
|
900
|
+
writeln(` ${dim('─'.repeat(w - 4))}`)
|
|
901
|
+
writeln()
|
|
902
|
+
writeln(` ${bold('Board')} ${dim('30d window · signalaf.com')}`)
|
|
903
|
+
|
|
904
|
+
const entries = boardData?.operators ?? boardData?.entries ?? boardData ?? []
|
|
905
|
+
if (Array.isArray(entries) && entries.length > 0) {
|
|
906
|
+
const top5 = entries.slice(0, 5)
|
|
907
|
+
for (const e of top5) {
|
|
908
|
+
const rank = e.rank === 1 ? gold(`#${e.rank}`) : dim(`#${e.rank}`)
|
|
909
|
+
const name = padEnd(trunc(e.codename ?? '—', 20), 20)
|
|
910
|
+
const cls = padEnd(colorClass(e.class_tier ?? '—'), 13)
|
|
911
|
+
const signa = padStart(e.signa_rate != null ? e.signa_rate.toFixed(1) : '—', 7)
|
|
912
|
+
writeln(` ${rank} ${name} ${cls} ${signa}`)
|
|
913
|
+
}
|
|
914
|
+
if (entries.length > 5) writeln(` ${dim(` … ${entries.length - 5} more operators on signalaf.com`)}`)
|
|
915
|
+
} else {
|
|
916
|
+
writeln(` ${dim(' board unavailable')}`)
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// ── 6. Footer / submit prompt ────────────────────────────────────────────
|
|
920
|
+
writeln()
|
|
921
|
+
writeln(` ${dim('─'.repeat(w - 4))}`)
|
|
922
|
+
writeln(` ${dim('[S]')} submit to board ${dim('[B]')} open board in browser ${dim('[Q]')} quit`)
|
|
923
|
+
writeln()
|
|
924
|
+
|
|
925
|
+
// ── 7. Keypress handler ───────────────────────────────────────────────────
|
|
926
|
+
if (process.stdin.isTTY) {
|
|
927
|
+
process.stdin.setRawMode(true)
|
|
928
|
+
process.stdin.resume()
|
|
929
|
+
process.stdin.setEncoding('utf8')
|
|
930
|
+
|
|
931
|
+
await new Promise((resolve) => {
|
|
932
|
+
process.stdin.on('data', async (key) => {
|
|
933
|
+
const k = key.toLowerCase()
|
|
934
|
+
if (k === 'q' || key === '\u0003') { // q or ctrl+c
|
|
935
|
+
resolve()
|
|
936
|
+
} else if (k === 'b') {
|
|
937
|
+
const { execSync: es } = await import('child_process')
|
|
938
|
+
try { es('open https://signalaf.com', { stdio: 'ignore' }) } catch { }
|
|
939
|
+
resolve()
|
|
940
|
+
} else if (k === 's') {
|
|
941
|
+
process.stdin.setRawMode(false)
|
|
942
|
+
write(SHOW_CURSOR)
|
|
943
|
+
process.stdout.write('\n Codename: ')
|
|
944
|
+
let codename = ''
|
|
945
|
+
process.stdin.on('data', async function onData(chunk) {
|
|
946
|
+
if (chunk === '\r' || chunk === '\n') {
|
|
947
|
+
process.stdin.removeListener('data', onData)
|
|
948
|
+
codename = codename.trim()
|
|
949
|
+
if (!codename) { writeln(red(' ✗ codename required')); resolve(); return }
|
|
950
|
+
writeln()
|
|
951
|
+
writeln(` ${dim('Submitting all windows for')} ${cyan(codename)}${dim('…')}`)
|
|
952
|
+
write(HIDE_CURSOR)
|
|
953
|
+
try {
|
|
954
|
+
for (const d of active) {
|
|
955
|
+
const apiBase = DEFAULT_API_BASE
|
|
956
|
+
const WINDOW_TYPE = { '7d': '7d', '30d': '30d', '90d': '90d', 'all': 'all_time' }
|
|
957
|
+
for (const ww of (d.windows ?? [])) {
|
|
958
|
+
const rawPaste = `${ww.pillars.input} ${ww.pillars.output} ${ww.pillars.cacheCreate} ${ww.pillars.cacheRead}`
|
|
959
|
+
const windowType = WINDOW_TYPE[ww.window] || ww.window
|
|
960
|
+
const now = new Date()
|
|
961
|
+
const ddmmyy = `${String(now.getDate()).padStart(2,'0')}${String(now.getMonth()+1).padStart(2,'0')}${String(now.getFullYear()).slice(-2)}`
|
|
962
|
+
const content_hash = `sha256:${codename}|${windowType}|${rawPaste}|${ddmmyy}`
|
|
963
|
+
const res = await fetch(`${apiBase}/api/v1/ingest-paste`, {
|
|
964
|
+
method: 'POST',
|
|
965
|
+
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
|
966
|
+
body: JSON.stringify({
|
|
967
|
+
codename,
|
|
968
|
+
raw_paste: rawPaste,
|
|
969
|
+
window_type: windowType,
|
|
970
|
+
telemetry: { platform: { primary: d.platform } },
|
|
971
|
+
content_hash,
|
|
972
|
+
submitted_ddmmyy: ddmmyy,
|
|
973
|
+
submitted_at: now.toISOString(),
|
|
974
|
+
}),
|
|
975
|
+
}).catch(() => null)
|
|
976
|
+
const ok = res?.ok ?? false
|
|
977
|
+
const status = ok ? green('✓') : red('✗')
|
|
978
|
+
writeln(` ${status} ${d.platform} ${ww.window}${!ok ? dim(` HTTP ${res?.status ?? 'err'}`) : ''}`)
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
writeln()
|
|
982
|
+
writeln(` ${green('✓')} Submitted. Visit ${cyan(`signalaf.com/user/${codename}`)}`)
|
|
983
|
+
} catch (e) {
|
|
984
|
+
writeln(red(` ✗ ${e.message}`))
|
|
985
|
+
}
|
|
986
|
+
resolve()
|
|
987
|
+
} else if (chunk === '\u007f') { // backspace
|
|
988
|
+
if (codename.length > 0) {
|
|
989
|
+
codename = codename.slice(0, -1)
|
|
990
|
+
process.stdout.write('\b \b')
|
|
991
|
+
}
|
|
992
|
+
} else {
|
|
993
|
+
codename += chunk
|
|
994
|
+
process.stdout.write(chunk)
|
|
995
|
+
}
|
|
996
|
+
})
|
|
997
|
+
process.stdin.resume()
|
|
998
|
+
}
|
|
999
|
+
})
|
|
1000
|
+
})
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
write(SHOW_CURSOR)
|
|
1004
|
+
if (process.stdin.isTTY) {
|
|
1005
|
+
process.stdin.setRawMode(false)
|
|
1006
|
+
process.stdin.pause()
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
741
1010
|
// ── HELP ─────────────────────────────────────────────────────────────────────
|
|
742
1011
|
|
|
743
1012
|
function showHelp() {
|
|
@@ -805,7 +1074,10 @@ export async function runCli(argv) {
|
|
|
805
1074
|
} else if (cmd === '--help' || cmd === '-h' || cmd === 'help') {
|
|
806
1075
|
showHelp()
|
|
807
1076
|
} else if (cmd === '--version' || cmd === '-v') {
|
|
808
|
-
writeln('0.
|
|
1077
|
+
writeln('0.8.0')
|
|
1078
|
+
} else if (!cmd || cmd === 'start' || cmd === 'run') {
|
|
1079
|
+
// default: full unified view
|
|
1080
|
+
await runSigRank()
|
|
809
1081
|
} else {
|
|
810
1082
|
// unknown command: show help
|
|
811
1083
|
showHelp()
|