sigrank 0.11.4

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/cli.mjs ADDED
@@ -0,0 +1,1394 @@
1
+ /**
2
+ * cli.mjs — SigRank terminal UI.
3
+ *
4
+ * Default (no command): full unified view — all platforms, all windows, token pillars,
5
+ * board position, [S] submit prompt.
6
+ *
7
+ * Commands:
8
+ * npx sigrank full unified view (default)
9
+ * npx sigrank board live leaderboard, refreshes every 30s
10
+ * npx sigrank board --window 7d board for a specific window
11
+ * npx sigrank board --once print once and exit (no live refresh)
12
+ * npx sigrank me your cascade across all 4 windows
13
+ * npx sigrank me --platform amp use a different platform adapter
14
+ * npx sigrank me --compare raw pillar comparison: ccusage vs tokenpull vs token-dashboard
15
+ * npx sigrank watch RT tune meter — local cascade, refreshes
16
+ * npx sigrank watch --window 7d watch a specific window
17
+ *
18
+ * Color palette mirrors the SigRank web dark theme:
19
+ * gold = class TRANSMITTER headline + rank #1
20
+ * cyan = active metrics / your row highlight
21
+ * dim = secondary data, separators
22
+ * red = negative movement / delta
23
+ * green = positive movement
24
+ */
25
+
26
+ import { callTool, DEFAULT_API_BASE } from './tools.mjs'
27
+ import { classify } from './cascade.mjs'
28
+ import { ensureIdentity, keystorePath } from './keystore.mjs'
29
+ import { submitSignedWindow } from './submit.mjs'
30
+ import { execSync } from 'child_process'
31
+ import { existsSync, readFileSync, writeFileSync } from 'fs'
32
+ import os from 'os'
33
+ import path from 'path'
34
+
35
+ // ── ANSI helpers (no chalk dep) ────────────────────────────────────────────
36
+ const ESC = '\x1b['
37
+ const c = {
38
+ reset: `${ESC}0m`,
39
+ bold: `${ESC}1m`,
40
+ dim: `${ESC}2m`,
41
+ gold: `${ESC}33m`,
42
+ boldGold: `${ESC}1;33m`,
43
+ cyan: `${ESC}36m`,
44
+ boldCyan: `${ESC}1;36m`,
45
+ green: `${ESC}32m`,
46
+ red: `${ESC}31m`,
47
+ white: `${ESC}97m`,
48
+ boldWhite:`${ESC}1;97m`,
49
+ magenta: `${ESC}35m`,
50
+ blue: `${ESC}34m`,
51
+ }
52
+ const paint = (color, str) => `${color}${str}${c.reset}`
53
+ const bold = (str) => paint(c.bold, str)
54
+ const dim = (str) => paint(c.dim, str)
55
+ const gold = (str) => paint(c.boldGold, str)
56
+ const cyan = (str) => paint(c.boldCyan, str)
57
+ const green = (str) => paint(c.green, str)
58
+ const red = (str) => paint(c.red, str)
59
+
60
+ // ── Class tier → color ─────────────────────────────────────────────────────
61
+ const CLASS_COLOR = {
62
+ TRANSMITTER: (s) => paint(c.boldGold, s),
63
+ 'ARCH+': (s) => paint(c.boldCyan, s),
64
+ ARCH: (s) => paint(c.cyan, s),
65
+ POWER: (s) => paint(c.boldWhite, s),
66
+ BASE: (s) => paint(c.white, s),
67
+ SEEKER: (s) => paint(c.magenta, s),
68
+ REFINER: (s) => paint(c.blue, s),
69
+ BEARER: (s) => paint(c.dim, s),
70
+ IGNITER: (s) => paint(c.dim, s),
71
+ }
72
+ const colorClass = (cls) => (CLASS_COLOR[cls] ?? ((s) => s))(cls)
73
+
74
+ // ── Terminal utils ──────────────────────────────────────────────────────────
75
+ const CLEAR_SCREEN = `${ESC}2J${ESC}H`
76
+ const CURSOR_UP = (n) => `${ESC}${n}A`
77
+ const ERASE_LINE = `${ESC}2K`
78
+ const HIDE_CURSOR = `${ESC}?25l`
79
+ const SHOW_CURSOR = `${ESC}?25h`
80
+ const termWidth = () => process.stdout.columns || 80
81
+ const write = (s) => process.stdout.write(s)
82
+ const writeln = (s = '') => process.stdout.write(s + '\n')
83
+
84
+ // Right-pad or truncate to exact width (ANSI-escape-aware via strip helper)
85
+ function stripAnsi(s) { return s.replace(/\x1b\[[0-9;]*m/g, '') }
86
+ function padEnd(s, w) {
87
+ const vis = stripAnsi(s).length
88
+ return vis >= w ? s : s + ' '.repeat(w - vis)
89
+ }
90
+ function padStart(s, w) {
91
+ const vis = stripAnsi(s).length
92
+ return vis >= w ? s : ' '.repeat(w - vis) + s
93
+ }
94
+ function trunc(s, w) {
95
+ const stripped = stripAnsi(s)
96
+ if (stripped.length <= w) return s
97
+ // truncate the raw string, not the escape-aware one — safe for plain strings
98
+ return s.slice(0, w - 1) + '…'
99
+ }
100
+
101
+ // ── Number formatters ───────────────────────────────────────────────────────
102
+ const fmtYield = (y) => {
103
+ if (y == null) return '—'
104
+ if (y >= 10000) return `${(y / 1000).toFixed(1)}K`
105
+ if (y >= 1000) return `${(y / 1000).toFixed(2)}K`
106
+ return y.toFixed(1)
107
+ }
108
+ const fmtLev = (l) => {
109
+ if (l == null) return '—'
110
+ if (l >= 1000) return `${(l / 1000).toFixed(1)}K`
111
+ return l.toFixed(0)
112
+ }
113
+ const fmtPct = (n) => n != null ? `${(n * 100).toFixed(0)}%` : '—'
114
+ const fmtSNR = (n) => n != null ? `${(n * 100).toFixed(1)}%` : '—'
115
+ const fmtMove = (n) => {
116
+ if (n == null || n === 0) return dim(' —')
117
+ return n > 0 ? green(`+${n}`) : red(`${n}`)
118
+ }
119
+ const fmtTokens = (n) => {
120
+ if (n == null) return '—'
121
+ if (n >= 1e9) return `${(n / 1e9).toFixed(1)}B`
122
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`
123
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`
124
+ return String(n)
125
+ }
126
+
127
+ // ── Header / footer ─────────────────────────────────────────────────────────
128
+ function renderHeader(title, subtitle = '') {
129
+ const w = termWidth()
130
+ const ts = new Date().toLocaleTimeString('en-US', { hour12: false })
131
+ const right = dim(`signalaf.com ${ts}`)
132
+ const rightVis = stripAnsi(right).length
133
+ const leftVis = stripAnsi(title).length
134
+ const gap = Math.max(1, w - leftVis - rightVis)
135
+ writeln()
136
+ writeln(` ${title}${' '.repeat(gap)}${right}`)
137
+ if (subtitle) writeln(` ${dim(subtitle)}`)
138
+ writeln(` ${dim('─'.repeat(w - 4))}`)
139
+ }
140
+
141
+ function renderFooter(hint = '') {
142
+ const w = termWidth()
143
+ writeln(` ${dim('─'.repeat(w - 4))}`)
144
+ if (hint) writeln(` ${dim(hint)}`)
145
+ writeln()
146
+ }
147
+
148
+ // ── BOARD command ────────────────────────────────────────────────────────────
149
+
150
+ const BOARD_COLS = [
151
+ { key: 'rank', label: '#', w: 4, align: 'r' },
152
+ { key: 'codename', label: 'Operator',w: 20, align: 'l' },
153
+ { key: 'class_tier', label: 'Class', w: 13, align: 'l' },
154
+ { key: 'yield_', label: 'Υ Yield', w: 9, align: 'r' },
155
+ { key: 'compression_ratio', label: 'SNR', w: 6, align: 'r' },
156
+ { key: 'session_depth', label: 'Depth', w: 6, align: 'r' },
157
+ { key: 'token_throughput', label: 'Tokens', w: 8, align: 'r' },
158
+ { key: 'movement_7d', label: '7d Δ', w: 6, align: 'r' },
159
+ ]
160
+
161
+ function renderBoardRow(entry, highlight = false) {
162
+ const rank = entry.rank === 1 ? gold(`#${entry.rank}`) : `#${entry.rank}`
163
+ const name = highlight
164
+ ? cyan(trunc(entry.codename, 19))
165
+ : trunc(entry.codename, 19)
166
+ const cls = colorClass(entry.class_tier ?? '—')
167
+ const yld = entry.yield_ != null ? fmtYield(entry.yield_) : '—'
168
+ const snr = fmtSNR(entry.compression_ratio)
169
+ const dep = entry.session_depth != null ? entry.session_depth.toFixed(1) : '—'
170
+ const tok = fmtTokens(entry.token_throughput)
171
+ const mv = fmtMove(entry.movement_7d)
172
+
173
+ const cols = [
174
+ padStart(rank, 4),
175
+ padEnd(name, 20),
176
+ padEnd(cls, 13),
177
+ padStart(yld, 9),
178
+ padStart(snr, 6),
179
+ padStart(dep, 6),
180
+ padStart(tok, 8),
181
+ padStart(mv, 6),
182
+ ]
183
+ const prefix = highlight ? `${c.boldCyan}▶${c.reset} ` : ' '
184
+ writeln(prefix + cols.join(' '))
185
+ }
186
+
187
+ function renderBoardHeader(window = '30d') {
188
+ renderHeader(
189
+ `${gold('⊙ SigRank')} ${bold('Leaderboard')}`,
190
+ `window: ${window} · sorted by Υ Yield · top 25 operators`
191
+ )
192
+ // column headers
193
+ const headers = BOARD_COLS.map(col =>
194
+ col.align === 'r'
195
+ ? padStart(dim(col.label), col.w)
196
+ : padEnd(dim(col.label), col.w)
197
+ )
198
+ writeln(` ${headers.join(' ')}`)
199
+ writeln(` ${dim('·'.repeat(termWidth() - 4))}`)
200
+ }
201
+
202
+ async function fetchBoard(window = '30d') {
203
+ const res = await fetch(`${DEFAULT_API_BASE}/api/v1/leaderboard?window=${window}&metric=yield_`, {
204
+ headers: { accept: 'application/json' }
205
+ })
206
+ if (!res.ok) throw new Error(`Board API → HTTP ${res.status}`)
207
+ return res.json()
208
+ }
209
+
210
+ async function runBoard({ window = '30d', once = false, refresh = 30 } = {}) {
211
+ let lines = 0
212
+
213
+ const draw = async () => {
214
+ let data
215
+ try { data = await fetchBoard(window) }
216
+ catch (e) {
217
+ writeln(red(` ✗ Could not reach signalaf.com: ${e.message}`))
218
+ return
219
+ }
220
+
221
+ if (!once && lines > 0) {
222
+ // move cursor up and redraw in-place
223
+ write(CURSOR_UP(lines))
224
+ }
225
+
226
+ const out = []
227
+ const push = (s = '') => out.push(s)
228
+
229
+ push()
230
+ const ts = new Date().toLocaleTimeString('en-US', { hour12: false })
231
+ const right = `signalaf.com ${ts}`
232
+ const title = `⊙ SigRank Leaderboard`
233
+ const w = termWidth()
234
+ const gap = Math.max(1, w - 2 - title.length - right.length)
235
+ push(` ${gold('⊙ SigRank')} ${bold('Leaderboard')}${' '.repeat(gap)}${dim(right)}`)
236
+ push(` ${dim(`window: ${data.window ?? window} · ${data.total_operators ?? (data.entries?.length ?? 0)} operators`)}`)
237
+ push(` ${dim('─'.repeat(w - 4))}`)
238
+
239
+ // column header row
240
+ const headers = BOARD_COLS.map(col =>
241
+ col.align === 'r'
242
+ ? padStart(dim(col.label), col.w)
243
+ : padEnd(dim(col.label), col.w)
244
+ ).join(' ')
245
+ push(` ${headers}`)
246
+ push(` ${dim('·'.repeat(w - 4))}`)
247
+
248
+ const entries = data.entries ?? []
249
+ for (const entry of entries) {
250
+ const rank = entry.rank === 1 ? gold(`#${entry.rank}`) : `#${entry.rank}`
251
+ const name = trunc(entry.codename ?? '—', 19)
252
+ const cls = colorClass(entry.class_tier ?? '—')
253
+ const yld = entry.yield_ != null ? fmtYield(entry.yield_) : '—'
254
+ const snr = fmtSNR(entry.compression_ratio)
255
+ const dep = entry.session_depth != null ? entry.session_depth.toFixed(1) : '—'
256
+ const tok = fmtTokens(entry.token_throughput)
257
+ const mv = fmtMove(entry.movement_7d)
258
+ const cols = [
259
+ padStart(rank, 4),
260
+ padEnd(name, 20),
261
+ padEnd(cls, 13),
262
+ padStart(yld, 9),
263
+ padStart(snr, 6),
264
+ padStart(dep, 6),
265
+ padStart(tok, 8),
266
+ padStart(mv, 6),
267
+ ]
268
+ push(` ${cols.join(' ')}`)
269
+ }
270
+
271
+ push(` ${dim('─'.repeat(w - 4))}`)
272
+ if (!once) push(` ${dim(`auto-refresh every ${refresh}s · ctrl+c to exit`)}`)
273
+ push()
274
+
275
+ // write all at once to minimize flicker
276
+ const rendered = out.join('\n')
277
+ write(rendered)
278
+ lines = out.length
279
+ }
280
+
281
+ if (!once) write(HIDE_CURSOR)
282
+ try {
283
+ await draw()
284
+ if (!once) {
285
+ const iv = setInterval(draw, refresh * 1000)
286
+ await new Promise((resolve) => {
287
+ process.on('SIGINT', () => { clearInterval(iv); resolve() })
288
+ })
289
+ }
290
+ } finally {
291
+ if (!once) write(SHOW_CURSOR + '\n')
292
+ }
293
+ }
294
+
295
+ // ── ME command ───────────────────────────────────────────────────────────────
296
+
297
+ async function runMe({ platform = 'claude', compare = false } = {}) {
298
+ if (compare) return runCompare({ platform })
299
+
300
+ write(HIDE_CURSOR)
301
+ writeln(` ${dim('reading local token logs…')}`)
302
+
303
+ let pulled
304
+ try {
305
+ pulled = await callTool('tokenpull', { platform })
306
+ } catch (e) {
307
+ write(SHOW_CURSOR)
308
+ write(CURSOR_UP(1) + ERASE_LINE)
309
+ writeln(red(` ✗ ${e.message}`))
310
+ process.exit(1)
311
+ }
312
+
313
+ // clear the "reading…" line
314
+ write(CURSOR_UP(1) + ERASE_LINE)
315
+
316
+ const w = termWidth()
317
+ writeln()
318
+ writeln(` ${gold('⊙ SigRank')} ${bold('Your Cascade')} ${dim(`platform: ${pulled.platform ?? platform}`)}`)
319
+ if (pulled.estimated) writeln(` ${dim('⚠ estimated values (cache data unavailable for this platform)')}`)
320
+ writeln(` ${dim('─'.repeat(w - 4))}`)
321
+
322
+ // column headers
323
+ const cols_h = [
324
+ padEnd(dim('Window'), 8),
325
+ padStart(dim('Υ Yield'), 10),
326
+ padStart(dim('SNR'), 7),
327
+ padStart(dim('Leverage'), 9),
328
+ padStart(dim('Velocity'), 9),
329
+ padStart(dim('10xDEV'), 8),
330
+ padStart(dim('Class'), 13),
331
+ padStart(dim('Tokens'), 8),
332
+ ]
333
+ writeln(` ${cols_h.join(' ')}`)
334
+ writeln(` ${dim('·'.repeat(w - 4))}`)
335
+
336
+ const windows = pulled.windows ?? []
337
+ for (const win of windows) {
338
+ const cas = win.cascade
339
+ const isAll = win.window === 'all'
340
+ const wLabel = isAll ? bold('all-time') : win.window
341
+ const yVal = cas?.yield != null ? fmtYield(cas.yield) : '—'
342
+ const snrVal = cas?.snr != null ? fmtSNR(cas.snr) : '—'
343
+ const levVal = cas?.leverage != null ? `${fmtLev(cas.leverage)}×` : '—'
344
+ const velVal = cas?.velocity != null ? cas.velocity.toFixed(2) : '—'
345
+ const devVal = cas?.dev10x != null ? cas.dev10x.toFixed(2) : '—'
346
+ const cls = colorClass(cas?.class ?? '—')
347
+ const tok = fmtTokens(win.pillars?.total ?? (
348
+ (win.pillars?.input ?? 0) + (win.pillars?.output ?? 0) +
349
+ (win.pillars?.cacheCreate ?? 0) + (win.pillars?.cacheRead ?? 0)
350
+ ))
351
+
352
+ const row = [
353
+ padEnd(wLabel, 8),
354
+ padStart(isAll ? gold(yVal) : yVal, 10),
355
+ padStart(snrVal, 7),
356
+ padStart(levVal, 9),
357
+ padStart(velVal, 9),
358
+ padStart(devVal, 8),
359
+ padEnd(cls, 13),
360
+ padStart(tok, 8),
361
+ ]
362
+ writeln(` ${row.join(' ')}`)
363
+ }
364
+
365
+ writeln(` ${dim('─'.repeat(w - 4))}`)
366
+
367
+ // card for the best window (all-time if present, else first)
368
+ const best = windows.find(w => w.window === 'all') ?? windows[0]
369
+ if (best?.card) {
370
+ writeln()
371
+ writeln(` ${dim('cascade read:')}`)
372
+ // wrap card text at terminal width
373
+ const cardText = best.card
374
+ const maxW = w - 6
375
+ const words = cardText.split(' ')
376
+ let line = ''
377
+ for (const word of words) {
378
+ if (line.length + word.length + 1 > maxW) {
379
+ writeln(` ${line}`)
380
+ line = word
381
+ } else {
382
+ line = line ? `${line} ${word}` : word
383
+ }
384
+ }
385
+ if (line) writeln(` ${line}`)
386
+ }
387
+
388
+ writeln()
389
+
390
+ // submit hint
391
+ writeln(` ${dim('to publish:')} ${cyan('npx sigrank submit')} ${dim('(sign in first:')} ${cyan('npx sigrank enroll')}${dim(' — get a code at signalaf.com → Settings)')}`)
392
+ writeln()
393
+ write(SHOW_CURSOR)
394
+ }
395
+
396
+ // ── COMPARE command ───────────────────────────────────────────────────────────
397
+ // Side-by-side: ccusage (JSON) vs tokenpull vs token-dashboard (SQLite)
398
+
399
+ function ccusagePillars(platform = 'claude') {
400
+ // ccusage <platform> daily --json → sum by window
401
+ try {
402
+ const cmd = platform === 'claude' ? 'ccusage claude daily --json' : `ccusage ${platform} daily --json`
403
+ const raw = execSync(cmd, { timeout: 15000, stdio: ['ignore', 'pipe', 'ignore'] }).toString()
404
+ const data = JSON.parse(raw)
405
+ const rows = data.daily ?? data // ccusage may return {daily:[...]} or [...]
406
+
407
+ const now = Date.now()
408
+ const cutoff = { '7d': 7, '30d': 30, '90d': 90 }
409
+ const result = {}
410
+
411
+ for (const [win, days] of Object.entries(cutoff)) {
412
+ const since = new Date(now - days * 86400000)
413
+ let input = 0, output = 0, cacheCreate = 0, cacheRead = 0
414
+ for (const row of rows) {
415
+ const d = new Date(row.date ?? row.day ?? row.week ?? '1970-01-01')
416
+ if (d >= since) {
417
+ input += row.inputTokens ?? row.input_tokens ?? 0
418
+ output += row.outputTokens ?? row.output_tokens ?? 0
419
+ cacheCreate += row.cacheCreationTokens ?? row.cache_create_tokens ?? 0
420
+ cacheRead += row.cacheReadTokens ?? row.cache_read_tokens ?? 0
421
+ }
422
+ }
423
+ result[win] = { input, output, cacheCreate, cacheRead }
424
+ }
425
+ // all-time = sum everything
426
+ let input = 0, output = 0, cacheCreate = 0, cacheRead = 0
427
+ for (const row of rows) {
428
+ input += row.inputTokens ?? row.input_tokens ?? 0
429
+ output += row.outputTokens ?? row.output_tokens ?? 0
430
+ cacheCreate += row.cacheCreationTokens ?? row.cache_create_tokens ?? 0
431
+ cacheRead += row.cacheReadTokens ?? row.cache_read_tokens ?? 0
432
+ }
433
+ result['all'] = { input, output, cacheCreate, cacheRead }
434
+ return result
435
+ } catch {
436
+ return null
437
+ }
438
+ }
439
+
440
+ function tokscalePillars(platform = 'claude') {
441
+ // Read tokscale_report.json — all-time only (no timestamps in export)
442
+ const reportPath = path.join(os.homedir(), 'tokscale_report.json')
443
+ if (!existsSync(reportPath)) return null
444
+ try {
445
+ const data = JSON.parse(readFileSync(reportPath, 'utf8'))
446
+ const entries = data.entries ?? []
447
+ const rows = entries.filter(e =>
448
+ e.client === platform &&
449
+ e.model !== '<synthetic>' && e.model !== 'unknown' &&
450
+ (e.input > 0 || e.output > 0)
451
+ )
452
+ if (rows.length === 0) return null
453
+ const p = rows.reduce((acc, e) => ({
454
+ input: acc.input + (e.input ?? 0),
455
+ output: acc.output + (e.output ?? 0),
456
+ cacheCreate: acc.cacheCreate + (e.cacheWrite ?? 0),
457
+ cacheRead: acc.cacheRead + (e.cacheRead ?? 0),
458
+ }), { input: 0, output: 0, cacheCreate: 0, cacheRead: 0 })
459
+ // tokscale export has no timestamps → only all-time available
460
+ return { all: p }
461
+ } catch { return null }
462
+ }
463
+
464
+ function appPillars() {
465
+ // App numbers from screenshots — all-time, per model (no cache fields)
466
+ // Hard-coded from 2026-06-23 screenshot capture (update when re-screenshotted)
467
+ return {
468
+ all: {
469
+ input: 6_378_000, // sum of all models: 5.6M + 102.1K + 92.9K + 130.3K + 418.9K + 33.5K
470
+ output: 38_682_400, // sum: 19.6M + 6.5M + 5.4M + 6.6M + 292.4K + 290.4K
471
+ cacheCreate: null, // not shown in App UI
472
+ cacheRead: null, // not shown in App UI
473
+ },
474
+ _note: 'App UI — all-time, per-model sum from screenshots 2026-06-23. No cache fields. Update when re-screenshotted.',
475
+ _perModel: [
476
+ { model: 'claude-opus-4-8', input: 5_600_000, output: 19_600_000 },
477
+ { model: 'claude-sonnet-4-5',input: 102_100, output: 6_500_000 },
478
+ { model: 'claude-sonnet-4-6',input: 92_900, output: 5_400_000 },
479
+ { model: 'claude-opus-4-7', input: 130_300, output: 6_600_000 },
480
+ { model: 'claude-fable-5', input: 418_900, output: 292_400 },
481
+ { model: 'claude-haiku-4-5', input: 33_500, output: 290_400 },
482
+ ],
483
+ }
484
+ }
485
+
486
+ function tokenDashPillars() {
487
+ const dbPath = path.join(os.homedir(), '.claude', 'token-dashboard.db')
488
+ if (!existsSync(dbPath)) return null
489
+ try {
490
+ const tmpScript = path.join(os.tmpdir(), 'sigrank_td_query.py')
491
+ writeFileSync(tmpScript, `
492
+ import sqlite3, json, sys
493
+ from datetime import datetime, timezone, timedelta
494
+ db = sqlite3.connect(sys.argv[1])
495
+ cf = "(model LIKE '%claude%' OR model LIKE '%fable%' OR model LIKE '%sonnet%' OR model LIKE '%opus%' OR model LIKE '%haiku%')"
496
+ out = {}
497
+ for win, days in [('7d',7),('30d',30),('90d',90)]:
498
+ since = (datetime.now(timezone.utc) - timedelta(days=days)).isoformat()
499
+ r = db.execute(f"SELECT SUM(input_tokens),SUM(output_tokens),SUM(cache_create_5m_tokens+cache_create_1h_tokens),SUM(cache_read_tokens) FROM messages WHERE timestamp>=? AND {cf}",(since,)).fetchone()
500
+ out[win] = {'input':r[0] or 0,'output':r[1] or 0,'cacheCreate':r[2] or 0,'cacheRead':r[3] or 0}
501
+ r = db.execute(f"SELECT SUM(input_tokens),SUM(output_tokens),SUM(cache_create_5m_tokens+cache_create_1h_tokens),SUM(cache_read_tokens) FROM messages WHERE {cf}").fetchone()
502
+ out['all'] = {'input':r[0] or 0,'output':r[1] or 0,'cacheCreate':r[2] or 0,'cacheRead':r[3] or 0}
503
+ print(json.dumps(out))
504
+ `)
505
+ const raw = execSync(`python3 "${tmpScript}" "${dbPath}"`,
506
+ { timeout: 15000, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()
507
+ return JSON.parse(raw)
508
+ } catch {
509
+ return null
510
+ }
511
+ }
512
+
513
+ function fmtDelta(a, b) {
514
+ if (a == null || b == null) return dim(' —')
515
+ const d = b - a
516
+ if (d === 0) return dim(' =')
517
+ const pct = a !== 0 ? `${d > 0 ? '+' : ''}${((d / a) * 100).toFixed(1)}%` : ''
518
+ const abs = `${d > 0 ? '+' : ''}${fmtTokens(Math.abs(d))}`
519
+ const label = `${abs} ${pct}`
520
+ return d > 0 ? green(label) : red(label)
521
+ }
522
+
523
+ // Compute cascade metrics from raw pillars (mirrors bridge.ts computeCascadeMetrics)
524
+ function cascadeFromPillars(p) {
525
+ if (!p) return null
526
+ const i = p.input ?? 0
527
+ const o = p.output ?? 0
528
+ const cw = p.cacheCreate ?? 0
529
+ const cr = p.cacheRead ?? 0
530
+ if (i === 0 && o === 0) return null
531
+ const safeI = Math.max(i, 1)
532
+ const total = i + o + cw + cr
533
+ const velocity = o / safeI
534
+ const leverage = cr / safeI
535
+ const yield_ = leverage * velocity
536
+ const snr = (i + o) > 0 ? o / (i + o) : 0
537
+ // dev10x = log10(T × C × R) — only when all four pillars present
538
+ let dev10x = null
539
+ if (cw > 0 && o > 0 && i > 0 && cr > 0) {
540
+ const T = o / i, C = cw / o, R = cr / cw
541
+ dev10x = Math.log10(T * C * R)
542
+ }
543
+ // efficiency = ((cr+cw+o)/i) / 4.0
544
+ const efficiency = ((cr + cw + o) / safeI) / 4.0
545
+ const cls = classify(yield_, dev10x)
546
+ return { yield: yield_, velocity, leverage, snr, dev10x, efficiency, class: cls, total }
547
+ }
548
+
549
+ async function runCompare({ platform = 'claude' } = {}) {
550
+ write(HIDE_CURSOR)
551
+
552
+ // Pull all five sources in parallel
553
+ writeln(` ${dim('reading all 5 sources…')}`)
554
+ const [ccPillars, tpData, tdPillars, tsPillars, apPillars] = await Promise.all([
555
+ Promise.resolve(ccusagePillars(platform)),
556
+ callTool('tokenpull', { platform }).catch(() => null),
557
+ Promise.resolve(platform === 'claude' ? tokenDashPillars() : null),
558
+ Promise.resolve(tokscalePillars(platform)),
559
+ Promise.resolve(platform === 'claude' ? appPillars() : null),
560
+ ])
561
+ write(CURSOR_UP(1) + ERASE_LINE)
562
+
563
+ const w = termWidth()
564
+ const WINS = ['7d', '30d', '90d', 'all']
565
+ const WIN_LABEL = { '7d': '7d', '30d': '30d', '90d': '90d', 'all': 'all-time' }
566
+
567
+ // build tokenpull pillar lookup
568
+ const tpPillars = {}
569
+ for (const win of (tpData?.windows ?? [])) {
570
+ tpPillars[win.window] = win.pillars
571
+ }
572
+
573
+ // sources: name, color, pillars-by-window, note
574
+ const SOURCES = [
575
+ { name: 'tokenpull', color: cyan, pillars: tpPillars, note: 'JSONL deduped by msg id · canon source' },
576
+ { name: 'ccusage', color: (s) => paint(c.green, s), pillars: ccPillars ?? {}, note: 'ccusage claude subcommand · monthly only' },
577
+ { name: 'token-dash', color: (s) => paint(c.magenta,s), pillars: tdPillars ?? {}, note: 'SQLite — double-counts sessions · use with caution' },
578
+ { name: 'tokscale', color: (s) => paint(c.blue, s), pillars: tsPillars ?? {}, note: 'all-time only · partial export (~5% of opus-4-8)' },
579
+ { name: 'App', color: gold, pillars: apPillars ?? {}, note: 'screenshots 2026-06-23 · no cache fields · update manually' },
580
+ ]
581
+
582
+ writeln()
583
+ writeln(` ${gold('⊙ SigRank')} ${bold('Source Comparison')} ${dim(`platform: ${platform}`)} ${dim('tokenpull · ccusage · token-dash · tokscale')}`)
584
+ writeln(` ${dim('─'.repeat(w - 4))}`)
585
+
586
+ // ── PILLARS TABLE ──────────────────────────────────────────────────────────
587
+ const PILLARS = [
588
+ { key: 'input', label: 'Input' },
589
+ { key: 'output', label: 'Output' },
590
+ { key: 'cacheCreate', label: 'Cache Write' },
591
+ { key: 'cacheRead', label: 'Cache Read' },
592
+ ]
593
+
594
+ for (const { key, label } of PILLARS) {
595
+ writeln()
596
+ writeln(` ${bold(label)}`)
597
+ const hcols = [padEnd(dim('Source'), 14), ...WINS.map(win => padStart(dim(WIN_LABEL[win]), 13))]
598
+ writeln(` ${hcols.join(' ')}`)
599
+ writeln(` ${dim('·'.repeat(Math.min(w - 4, 14 + WINS.length * 15)))}`)
600
+
601
+ for (const src of SOURCES) {
602
+ const vals = WINS.map(win => {
603
+ const p = src.pillars[win]
604
+ const v = p?.[key]
605
+ if (v == null) return padStart(dim('—'), 13)
606
+ return padStart(fmtTokens(v), 13)
607
+ })
608
+ writeln(` ${padEnd(src.color(src.name), 14)} ${vals.join(' ')}`)
609
+ }
610
+ }
611
+
612
+ // ── SIGNATURE TABLE ────────────────────────────────────────────────────────
613
+ writeln()
614
+ writeln(` ${dim('─'.repeat(w - 4))}`)
615
+ writeln(` ${bold('Cascade Signature')} ${dim('per source · all windows where data available')}`)
616
+ writeln()
617
+
618
+ const SIG_METRICS = [
619
+ { key: 'yield', label: 'Υ Yield', fmt: v => fmtYield(v), w: 9 },
620
+ { key: 'velocity', label: 'Vel', fmt: v => v.toFixed(2), w: 6 },
621
+ { key: 'leverage', label: 'Lev', fmt: v => `${fmtLev(v)}×`, w: 7 },
622
+ { key: 'snr', label: 'SNR', fmt: v => fmtSNR(v), w: 6 },
623
+ { key: 'dev10x', label: '10x', fmt: v => v.toFixed(2), w: 5 },
624
+ { key: 'efficiency', label: 'Eff', fmt: v => v.toFixed(1), w: 6 },
625
+ { key: 'class', label: 'Class', fmt: v => colorClass(v), w: 12 },
626
+ ]
627
+
628
+ // header
629
+ const sigHdr = [
630
+ padEnd(dim('Source'), 14),
631
+ padEnd(dim('Window'), 8),
632
+ ...SIG_METRICS.map(m => padStart(dim(m.label), m.w)),
633
+ ]
634
+ writeln(` ${sigHdr.join(' ')}`)
635
+ writeln(` ${dim('·'.repeat(Math.min(w - 4, 80)))}`)
636
+
637
+ for (const src of SOURCES) {
638
+ const availWins = WINS.filter(win => src.pillars[win] != null)
639
+ if (availWins.length === 0) {
640
+ writeln(` ${padEnd(src.color(src.name), 14)} ${dim('no data')}`)
641
+ continue
642
+ }
643
+ let first = true
644
+ for (const win of availWins) {
645
+ const p = src.pillars[win]
646
+ // For tokenpull, use the pre-computed cascade from the tool if available
647
+ let cas
648
+ if (src.name === 'tokenpull') {
649
+ const tpWin = tpData?.windows?.find(ww => ww.window === win)
650
+ cas = tpWin?.cascade ? {
651
+ yield: tpWin.cascade.yield,
652
+ velocity: tpWin.cascade.velocity,
653
+ leverage: tpWin.cascade.leverage,
654
+ snr: tpWin.cascade.snr,
655
+ dev10x: tpWin.cascade.dev10x,
656
+ efficiency: null,
657
+ class: tpWin.cascade.class,
658
+ } : cascadeFromPillars(p)
659
+ } else {
660
+ cas = cascadeFromPillars(p)
661
+ }
662
+
663
+ const srcLabel = first ? src.color(src.name) : ' '.repeat(stripAnsi(src.name).length)
664
+ const winLabel = win === 'all' ? bold('all-time') : win
665
+ const sigCols = SIG_METRICS.map(m => {
666
+ const v = cas?.[m.key]
667
+ return padStart(v != null ? m.fmt(v) : dim('—'), m.w)
668
+ })
669
+ writeln(` ${padEnd(srcLabel, 14)} ${padEnd(winLabel, 8)} ${sigCols.join(' ')}`)
670
+ first = false
671
+ }
672
+ }
673
+
674
+ // ── NOTES ─────────────────────────────────────────────────────────────────
675
+ writeln()
676
+ writeln(` ${dim('─'.repeat(w - 4))}`)
677
+ for (const src of SOURCES) {
678
+ writeln(` ${src.color(src.name.padEnd(12))} ${dim(src.note)}`)
679
+ }
680
+ writeln(` ${dim('Eff = ((cacheRead+cacheWrite+output)/input)/4.0 vs AA baseline')}`)
681
+ writeln(` ${dim('App has no cache fields → Υ/Lev/Eff/10x unavailable from App source')}`)
682
+ writeln()
683
+ write(SHOW_CURSOR)
684
+ }
685
+
686
+ // ── WATCH command ─────────────────────────────────────────────────────────────
687
+
688
+ async function runWatch({ platform = 'claude', window: win = '7d', refresh = 30, submit = false } = {}) {
689
+ let prev = null
690
+ let lines = 0
691
+ let lastSubmit = null
692
+ const id = submit ? ensureIdentity() : null
693
+ const enrolled = !!(id && id.codename && id.operator_id && id.private_key_pkcs8_b64)
694
+
695
+ write(HIDE_CURSOR)
696
+
697
+ const draw = async () => {
698
+ let result
699
+ try {
700
+ result = await callTool('watch_tokenpull', { platform, window: win })
701
+ } catch (e) {
702
+ writeln(red(` ✗ ${e.message}`))
703
+ return
704
+ }
705
+
706
+ const cas = result.cascade
707
+ const changed = prev !== null && prev !== cas?.yield
708
+
709
+ // --submit (D7 §7): publish the verified window on first observation + whenever Υ changes.
710
+ if (submit && enrolled && (prev === null || changed)) {
711
+ try {
712
+ const r = await submitSignedWindow(win, result.pillars, result.messages, id, { platform: result.platform })
713
+ lastSubmit = r.status === 'received' ? green(`✓ submitted · tier=${r.verification_tier || '—'}`) : red(`✗ ${r.reason || r.status}`)
714
+ } catch (e) {
715
+ lastSubmit = red(`✗ ${e.message}`)
716
+ }
717
+ }
718
+
719
+ if (lines > 0) write(CURSOR_UP(lines))
720
+
721
+ const out = []
722
+ const push = (s = '') => out.push(s)
723
+ const w = termWidth()
724
+ const ts = new Date().toLocaleTimeString('en-US', { hour12: false })
725
+
726
+ push()
727
+ push(` ${gold('⊙ SigRank')} ${bold('Watch')} ${dim(`${platform} · window: ${win} · ${ts}`)}`)
728
+ push(` ${dim('─'.repeat(w - 4))}`)
729
+ push()
730
+
731
+ const yDisplay = cas?.yield != null ? fmtYield(cas.yield) : '—'
732
+ const indicator = changed ? green(' ▲ updated') : dim(' · no change')
733
+
734
+ push(` ${bold('Υ Yield')} ${cas?.yield != null ? gold(yDisplay) : '—'}${indicator}`)
735
+ push(` ${bold('SNR')} ${fmtSNR(cas?.snr)}`)
736
+ push(` ${bold('Leverage')} ${cas?.leverage != null ? `${fmtLev(cas.leverage)}×` : '—'}`)
737
+ push(` ${bold('Velocity')} ${cas?.velocity != null ? cas.velocity.toFixed(2) : '—'}`)
738
+ push(` ${bold('10xDEV')} ${cas?.dev10x != null ? cas.dev10x.toFixed(2) : '—'}`)
739
+ push(` ${bold('Class')} ${colorClass(cas?.class ?? '—')}`)
740
+ push()
741
+ if (submit) {
742
+ push(` ${dim('submit:')} ${enrolled ? (lastSubmit || dim('waiting for a change…')) : red('not enrolled — run `npx sigrank enroll`')}`)
743
+ }
744
+ push(` ${dim('─'.repeat(w - 4))}`)
745
+ push(` ${dim(`polling every ${refresh}s · ${submit ? 'auto-submit ON · ' : ''}tokens stay on your machine · ctrl+c to exit`)}`)
746
+ push()
747
+
748
+ write(out.join('\n'))
749
+ lines = out.length
750
+ prev = cas?.yield ?? null
751
+ }
752
+
753
+ try {
754
+ await draw()
755
+ const iv = setInterval(draw, refresh * 1000)
756
+ await new Promise((resolve) => {
757
+ process.on('SIGINT', () => { clearInterval(iv); resolve() })
758
+ })
759
+ } finally {
760
+ write(SHOW_CURSOR + '\n')
761
+ }
762
+ }
763
+
764
+ // ── UNIFIED DEFAULT VIEW ──────────────────────────────────────────────────────
765
+ // npx sigrank (no args) — pulls everything at once:
766
+ // - all platforms in parallel (only shows ones with data)
767
+ // - all 4 windows per platform
768
+ // - token pillars table (transparency layer)
769
+ // - comparison sources (ccusage, tokscale, token-dashboard) if available
770
+ // - live board position
771
+ // - [S] submit [B] board [Q] quit
772
+
773
+ const ALL_PLATFORMS = [
774
+ 'claude','codex','amp','gemini','kimi','qwen','goose','kilo',
775
+ 'hermes','droid','codebuff','copilot','openclaw','pi',
776
+ ]
777
+
778
+ async function runSigRank() {
779
+ write(HIDE_CURSOR)
780
+ const w = termWidth()
781
+
782
+ // ── 1. Show immediate header so screen isn't blank ────────────────────────
783
+ writeln()
784
+ writeln(` ${gold('⊙ SigRank')} ${bold('Operator Dashboard')}`)
785
+ writeln(` ${dim('reading local data…')}`)
786
+
787
+ const { tokenpullAny } = await import('./tokenpull.mjs')
788
+
789
+ // Local sources first (fast) — board fetch with 5s timeout runs in parallel
790
+ const boardPromise = Promise.race([
791
+ callTool('get_leaderboard', {}).catch(() => null),
792
+ new Promise(r => setTimeout(() => r(null), 5000)),
793
+ ])
794
+
795
+ const [platformResults, tdPillars] = await Promise.all([
796
+ Promise.allSettled(ALL_PLATFORMS.map(p => tokenpullAny(p))),
797
+ Promise.resolve(tokenDashPillars()),
798
+ ])
799
+
800
+ // per-platform verifiers: ccusage + tokscale both support multiple platforms
801
+ // build map: platform → { cc, ts }
802
+ const verifierMap = {}
803
+ for (const platform of ALL_PLATFORMS) {
804
+ verifierMap[platform] = {
805
+ cc: ccusagePillars(platform),
806
+ ts: tokscalePillars(platform),
807
+ }
808
+ }
809
+ // keep top-level claude refs for combined section
810
+ const ccPillars = verifierMap['claude']?.cc ?? null
811
+ const tsPillars = verifierMap['claude']?.ts ?? null
812
+
813
+ const boardData = await boardPromise
814
+
815
+ // filter to platforms with actual data
816
+ const active = []
817
+ for (let i = 0; i < ALL_PLATFORMS.length; i++) {
818
+ const r = platformResults[i]
819
+ if (r.status !== 'fulfilled') continue
820
+ const d = r.value
821
+ const all = d.windows?.find(w => w.window === 'all')
822
+ if (!all) continue
823
+ const total = (all.pillars.input ?? 0) + (all.pillars.output ?? 0)
824
+ if (total === 0) continue
825
+ active.push(d)
826
+ }
827
+
828
+ // clear the 3 loading lines (blank + header + "reading…")
829
+ write(CURSOR_UP(3) + ERASE_LINE + CURSOR_UP(1) + ERASE_LINE + CURSOR_UP(1) + ERASE_LINE)
830
+
831
+ // ── 2. Header ─────────────────────────────────────────────────────────────
832
+ const ts = new Date().toLocaleTimeString('en-US', { hour12: false })
833
+ const header = ` ${gold('⊙ SigRank')} ${bold('Operator Dashboard')}`
834
+ const right = dim(`signalaf.com ${ts}`)
835
+ const gap = Math.max(1, w - stripAnsi(header).length - stripAnsi(right).length)
836
+ writeln()
837
+ writeln(`${header}${' '.repeat(gap)}${right}`)
838
+
839
+ const platformSummary = active.map(d => {
840
+ const all = d.windows.find(w => w.window === 'all')
841
+ return `${cyan(d.platform)} ${dim(`${d.files ?? '?'} files · ${(all?.messages ?? 0).toLocaleString()} msgs`)}`
842
+ }).join(' ')
843
+ writeln(` ${dim('Detected:')} ${platformSummary || dim('no local data found')}`)
844
+ writeln(` ${dim('─'.repeat(w - 4))}`)
845
+
846
+ // ── 3. Cascade table — all platforms × all windows ─────────────────────────
847
+ writeln()
848
+ writeln(` ${bold('Your Cascade')}`)
849
+ const CH = [
850
+ padEnd(dim('Platform'), 10),
851
+ padEnd(dim('Win'), 5),
852
+ padStart(dim('Input'), 8),
853
+ padStart(dim('Output'), 8),
854
+ padStart(dim('CacheW'), 8),
855
+ padStart(dim('CacheR'), 9),
856
+ padStart(dim('Υ Yield'), 9),
857
+ padStart(dim('SNR'), 7),
858
+ padStart(dim('Leverage'), 10),
859
+ padStart(dim('Vel'), 6),
860
+ padStart(dim('10x'), 6),
861
+ padEnd(dim('Class'), 13),
862
+ ]
863
+ writeln(` ${CH.join(' ')}`)
864
+ writeln(` ${dim('·'.repeat(Math.min(w - 4, 110)))}`)
865
+
866
+ const WINS = ['7d', '30d', '90d', 'all']
867
+
868
+ // helper to render one platform/combined row
869
+ const renderCascadeRow = (label, labelColorFn, winKey, p, est = false) => {
870
+ const cas = cascadeFromPillars(p)
871
+ if (!cas) return
872
+ const clsFn = CLASS_COLOR[cas.class] ?? ((s) => s)
873
+ const cols = [
874
+ padEnd(labelColorFn(label), 10),
875
+ padEnd(dim(winKey), 5),
876
+ padStart(est ? dim('~') + fmtTokens(p.input) : fmtTokens(p.input), 8),
877
+ padStart(fmtTokens(p.output), 8),
878
+ padStart(p.cacheCreate > 0 ? (est ? dim('~') + fmtTokens(p.cacheCreate) : fmtTokens(p.cacheCreate)) : dim('—'), 8),
879
+ padStart(p.cacheRead > 0 ? fmtTokens(p.cacheRead) : dim('—'), 9),
880
+ padStart(cas.yield != null ? (cas.yield > 10000 ? gold(fmtYield(cas.yield)) : fmtYield(cas.yield)) : '—', 9),
881
+ padStart(fmtSNR(cas.snr), 7),
882
+ padStart(cas.leverage != null ? `${fmtLev(cas.leverage)}×` : '—', 10),
883
+ padStart(cas.velocity != null ? cas.velocity.toFixed(2) : '—', 6),
884
+ padStart(cas.dev10x != null ? cas.dev10x.toFixed(2) : '—', 6),
885
+ padEnd(clsFn(cas.class), 13),
886
+ ]
887
+ writeln(` ${cols.join(' ')}`)
888
+ }
889
+
890
+ // per-platform rows
891
+ for (const d of active) {
892
+ const isFirst = (winKey) => winKey === '7d' || (winKey === '30d' && !d.windows?.find(w => w.window === '7d' && (w.pillars.input + w.pillars.output) > 0))
893
+ for (const winKey of WINS) {
894
+ const wdata = d.windows?.find(ww => ww.window === winKey)
895
+ if (!wdata) continue
896
+ renderCascadeRow(
897
+ d.platform,
898
+ (s) => isFirst(winKey) ? cyan(s) : dim(s),
899
+ winKey,
900
+ wdata.pillars,
901
+ d.estimated === true,
902
+ )
903
+ }
904
+ writeln()
905
+ }
906
+
907
+ // combined row (only when 2+ platforms active)
908
+ if (active.length > 1) {
909
+ const combinedLabel = active.map(d => d.platform).join('+')
910
+ const hasEstimated = active.some(d => d.estimated === true)
911
+ let isFirstWin = true
912
+ for (const winKey of WINS) {
913
+ const combinedP = { input: 0, output: 0, cacheCreate: 0, cacheRead: 0 }
914
+ let hasData = false
915
+ for (const d of active) {
916
+ const wdata = d.windows?.find(ww => ww.window === winKey)
917
+ if (!wdata) continue
918
+ const p = wdata.pillars
919
+ if ((p.input + p.output) === 0) continue
920
+ combinedP.input += p.input ?? 0
921
+ combinedP.output += p.output ?? 0
922
+ combinedP.cacheCreate += p.cacheCreate ?? 0
923
+ combinedP.cacheRead += p.cacheRead ?? 0
924
+ hasData = true
925
+ }
926
+ if (!hasData) continue
927
+ renderCascadeRow(
928
+ combinedLabel,
929
+ (s) => isFirstWin ? bold(cyan(s)) : dim(s),
930
+ winKey,
931
+ combinedP,
932
+ hasEstimated,
933
+ )
934
+ isFirstWin = false
935
+ }
936
+ writeln()
937
+ }
938
+
939
+ // ── 4. Token Pillars — per-platform verification + combined ──────────────
940
+ writeln(` ${dim('─'.repeat(w - 4))}`)
941
+ writeln()
942
+ writeln(` ${bold('Token Pillars')} ${dim('(all-time · per-platform verification)')}`)
943
+
944
+ const PCOLS = [
945
+ padEnd(dim('Source'), 14),
946
+ padStart(dim('Input'), 10),
947
+ padStart(dim('Output'), 10),
948
+ padStart(dim('Cache Write'), 12),
949
+ padStart(dim('Cache Read'), 12),
950
+ padStart(dim('Total'), 10),
951
+ ]
952
+ writeln(` ${PCOLS.join(' ')}`)
953
+ writeln(` ${dim('·'.repeat(Math.min(w - 4, 74)))}`)
954
+
955
+ const printPillarRow = (label, colorFn, p, note = '') => {
956
+ if (!p) return
957
+ const i = p.input ?? 0
958
+ const o = p.output ?? 0
959
+ const cw = p.cacheCreate ?? 0
960
+ const cr = p.cacheRead ?? 0
961
+ const total = i + o + cw + cr
962
+ const cols = [
963
+ padEnd(colorFn(label), 14),
964
+ padStart(fmtTokens(i), 10),
965
+ padStart(fmtTokens(o), 10),
966
+ padStart(cw > 0 ? fmtTokens(cw) : dim('—'), 12),
967
+ padStart(cr > 0 ? fmtTokens(cr) : dim('—'), 12),
968
+ padStart(fmtTokens(total), 10),
969
+ ]
970
+ writeln(` ${cols.join(' ')}${note ? ' ' + dim(note) : ''}`)
971
+ }
972
+
973
+ // ── Per-platform blocks ─────────────────────────────────────────────────
974
+ for (const d of active) {
975
+ const all = d.windows?.find(ww => ww.window === 'all')
976
+ if (!all) continue
977
+ const v = verifierMap[d.platform] ?? {}
978
+ const estMarker = d.estimated ? dim(' ~ estimated') : ''
979
+ printPillarRow(d.platform, cyan, all.pillars, `tokenpull${estMarker}`)
980
+ if (d.estimated) {
981
+ // show the estimation formula inline
982
+ const ratio = d.ioRatio != null ? d.ioRatio.toFixed(3) : '?'
983
+ writeln(` ${dim(` ~ input = output × ioRatio(${ratio}) cacheCreate = uncached − input cacheRead = exact`)}`)
984
+ }
985
+ if (v.cc?.all) printPillarRow(' ccusage', (s) => paint(c.green, s), v.cc.all, 'ccusage CLI')
986
+ if (d.platform === 'claude' && tdPillars?.all)
987
+ printPillarRow(' token-dash', (s) => paint(c.magenta, s), tdPillars.all, 'token-dashboard.db')
988
+ if (v.ts?.all) printPillarRow(' tokscale', (s) => paint(c.blue, s), v.ts.all, 'tokscale_report.json')
989
+ if (d.estimated && (v.cc?.all || v.ts?.all)) {
990
+ writeln(` ${dim(' ^ verifiers show raw uncached input (input_tokens − cached) — different field than estimated input above')}`)
991
+ }
992
+ writeln()
993
+ }
994
+
995
+ // ── Combined (if more than one platform) ───────────────────────────────
996
+ if (active.length > 1) {
997
+ const combined = { input: 0, output: 0, cacheCreate: 0, cacheRead: 0 }
998
+ for (const d of active) {
999
+ const all = d.windows?.find(ww => ww.window === 'all')
1000
+ if (!all) continue
1001
+ combined.input += all.pillars.input ?? 0
1002
+ combined.output += all.pillars.output ?? 0
1003
+ combined.cacheCreate += all.pillars.cacheCreate ?? 0
1004
+ combined.cacheRead += all.pillars.cacheRead ?? 0
1005
+ }
1006
+ // sum verifiers across all active platforms
1007
+ const ccCombined = { input: 0, output: 0, cacheCreate: 0, cacheRead: 0 }
1008
+ const tsCombined = { input: 0, output: 0, cacheCreate: 0, cacheRead: 0 }
1009
+ let hasCc = false, hasTs = false
1010
+ for (const d of active) {
1011
+ const v = verifierMap[d.platform] ?? {}
1012
+ if (v.cc?.all) {
1013
+ ccCombined.input += v.cc.all.input ?? 0
1014
+ ccCombined.output += v.cc.all.output ?? 0
1015
+ ccCombined.cacheCreate += v.cc.all.cacheCreate ?? 0
1016
+ ccCombined.cacheRead += v.cc.all.cacheRead ?? 0
1017
+ hasCc = true
1018
+ }
1019
+ if (v.ts?.all) {
1020
+ tsCombined.input += v.ts.all.input ?? 0
1021
+ tsCombined.output += v.ts.all.output ?? 0
1022
+ tsCombined.cacheCreate += v.ts.all.cacheCreate ?? 0
1023
+ tsCombined.cacheRead += v.ts.all.cacheRead ?? 0
1024
+ hasTs = true
1025
+ }
1026
+ }
1027
+ printPillarRow('combined', (s) => bold(s), combined, active.map(d => d.platform).join('+'))
1028
+ if (hasCc) printPillarRow(' ccusage', (s) => paint(c.green, s), ccCombined, 'ccusage CLI')
1029
+ if (tdPillars?.all) printPillarRow(' token-dash', (s) => paint(c.magenta, s), tdPillars.all,'token-dashboard.db')
1030
+ if (hasTs) printPillarRow(' tokscale', (s) => paint(c.blue, s), tsCombined, 'tokscale_report.json')
1031
+ }
1032
+
1033
+ // ── 5. Board position ─────────────────────────────────────────────────────
1034
+ writeln()
1035
+ writeln(` ${dim('─'.repeat(w - 4))}`)
1036
+ writeln()
1037
+ writeln(` ${bold('Board')} ${dim('30d window · signalaf.com · sorted by Υ Yield')}`)
1038
+
1039
+ const entries = boardData?.operators ?? boardData?.entries ?? boardData ?? []
1040
+ if (Array.isArray(entries) && entries.length > 0) {
1041
+ const top5 = entries.slice(0, 5)
1042
+ // header
1043
+ const BH = [
1044
+ padStart(dim('#'), 4),
1045
+ padEnd(dim('Codename'), 20),
1046
+ padEnd(dim('Class'), 13),
1047
+ padStart(dim('Υ Yield'), 9),
1048
+ padStart(dim('SNR'), 6),
1049
+ padStart(dim('Depth'), 6),
1050
+ padStart(dim('Tokens'),8),
1051
+ padStart(dim('Force'), 6),
1052
+ padStart(dim('Pct'), 5),
1053
+ padStart(dim('7d↕'), 5),
1054
+ ]
1055
+ writeln(` ${BH.join(' ')}`)
1056
+ writeln(` ${dim('·'.repeat(Math.min(w - 4, 92)))}`)
1057
+ for (const e of top5) {
1058
+ const rank = e.rank === 1 ? gold(`#${e.rank}`) : `#${e.rank}`
1059
+ const name = padEnd(trunc(e.codename ?? '—', 20), 20)
1060
+ const cls = padEnd(colorClass(e.class_tier ?? '—'), 13)
1061
+ const yld = padStart(e.yield_ != null ? fmtYield(e.yield_) : '—', 9)
1062
+ const snr = padStart(e.compression_ratio != null ? fmtSNR(e.compression_ratio) : '—', 6)
1063
+ const depth = padStart(e.session_depth != null ? e.session_depth.toFixed(1) : '—', 6)
1064
+ const tok = padStart(e.token_throughput != null ? fmtTokens(e.token_throughput) : '—', 8)
1065
+ const force = padStart(e.signal_force != null ? e.signal_force.toFixed(1) : '—', 6)
1066
+ const pct = padStart(e.percentile != null ? `${e.percentile}%` : '—', 5)
1067
+ const mv7 = padStart(fmtMove(e.movement_7d), 5)
1068
+ writeln(` ${padStart(rank, 4)} ${name} ${cls} ${yld} ${snr} ${depth} ${tok} ${force} ${pct} ${mv7}`)
1069
+ }
1070
+ if (entries.length > 5) writeln(` ${dim(` … ${entries.length - 5} more operators on signalaf.com`)}`)
1071
+ } else {
1072
+ writeln(` ${dim(' board unavailable')}`)
1073
+ }
1074
+
1075
+ // ── 6. Footer / submit prompt ────────────────────────────────────────────
1076
+ writeln()
1077
+ writeln(` ${dim('─'.repeat(w - 4))}`)
1078
+ writeln(` ${dim('[S]')} submit to board ${dim('[B]')} open board in browser ${dim('[Q]')} quit`)
1079
+ writeln()
1080
+
1081
+ // ── 7. Keypress handler ───────────────────────────────────────────────────
1082
+ if (process.stdin.isTTY) {
1083
+ process.stdin.setRawMode(true)
1084
+ process.stdin.resume()
1085
+ process.stdin.setEncoding('utf8')
1086
+
1087
+ await new Promise((resolve) => {
1088
+ process.stdin.on('data', async (key) => {
1089
+ const k = key.toLowerCase()
1090
+ if (k === 'q' || key === '\u0003') { // q or ctrl+c
1091
+ resolve()
1092
+ } else if (k === 'b') {
1093
+ const { execSync: es } = await import('child_process')
1094
+ try { es('open https://signalaf.com', { stdio: 'ignore' }) } catch { }
1095
+ resolve()
1096
+ } else if (k === 's') {
1097
+ const id = ensureIdentity()
1098
+ const enrolled = !!(id.codename && id.operator_id && id.private_key_pkcs8_b64)
1099
+ process.stdin.setRawMode(false)
1100
+ write(SHOW_CURSOR)
1101
+ if (enrolled) {
1102
+ // VERIFIED path (D7 §7): signed submit from the enrolled device. No codename
1103
+ // prompt — it comes from the keystore. Only signed submissions rank on the board.
1104
+ writeln()
1105
+ writeln(` ${dim('Publishing verified runs for')} ${cyan(id.codename)}${dim('…')}`)
1106
+ write(HIDE_CURSOR)
1107
+ try {
1108
+ for (const d of active) {
1109
+ for (const ww of (d.windows ?? [])) {
1110
+ const r = await submitSignedWindow(ww.window, ww.pillars, ww.messages, id, { platform: d.platform })
1111
+ const ok = r.status === 'received'
1112
+ writeln(` ${ok ? green('✓') : red('✗')} ${d.platform} ${ww.window}${ok ? dim(` tier=${r.verification_tier || '—'}`) : dim(` ${r.reason || r.status}`)}`)
1113
+ }
1114
+ }
1115
+ writeln()
1116
+ writeln(` ${green('✓')} Published. Reload ${cyan(`signalaf.com/user/${id.codename}`)}`)
1117
+ } catch (e) {
1118
+ writeln(red(` ✗ ${e.message}`))
1119
+ }
1120
+ resolve()
1121
+ return
1122
+ }
1123
+ // NOT enrolled → the anonymous paste path (preview-only; does NOT rank) + a hint.
1124
+ writeln()
1125
+ writeln(` ${dim('Not enrolled — run')} ${bold('npx sigrank enroll')} ${dim('to rank as a verified operator. Anonymous paste below:')}`)
1126
+ process.stdout.write(' Codename: ')
1127
+ let codename = ''
1128
+ process.stdin.on('data', async function onData(chunk) {
1129
+ if (chunk === '\r' || chunk === '\n') {
1130
+ process.stdin.removeListener('data', onData)
1131
+ codename = codename.trim()
1132
+ if (!codename) { writeln(red(' ✗ codename required')); resolve(); return }
1133
+ writeln()
1134
+ writeln(` ${dim('Submitting all windows for')} ${cyan(codename)}${dim('…')}`)
1135
+ write(HIDE_CURSOR)
1136
+ try {
1137
+ for (const d of active) {
1138
+ const apiBase = DEFAULT_API_BASE
1139
+ const WINDOW_TYPE = { '7d': '7d', '30d': '30d', '90d': '90d', 'all': 'all_time' }
1140
+ for (const ww of (d.windows ?? [])) {
1141
+ const rawPaste = `${ww.pillars.input} ${ww.pillars.output} ${ww.pillars.cacheCreate} ${ww.pillars.cacheRead}`
1142
+ const windowType = WINDOW_TYPE[ww.window] || ww.window
1143
+ const now = new Date()
1144
+ const ddmmyy = `${String(now.getDate()).padStart(2,'0')}${String(now.getMonth()+1).padStart(2,'0')}${String(now.getFullYear()).slice(-2)}`
1145
+ const content_hash = `sha256:${codename}|${windowType}|${rawPaste}|${ddmmyy}`
1146
+ const res = await fetch(`${apiBase}/api/v1/ingest-paste`, {
1147
+ method: 'POST',
1148
+ headers: { 'content-type': 'application/json', accept: 'application/json' },
1149
+ body: JSON.stringify({
1150
+ codename,
1151
+ raw_paste: rawPaste,
1152
+ window_type: windowType,
1153
+ telemetry: { platform: { primary: d.platform } },
1154
+ content_hash,
1155
+ submitted_ddmmyy: ddmmyy,
1156
+ submitted_at: now.toISOString(),
1157
+ }),
1158
+ }).catch(() => null)
1159
+ const ok = res?.ok ?? false
1160
+ const status = ok ? green('✓') : red('✗')
1161
+ writeln(` ${status} ${d.platform} ${ww.window}${!ok ? dim(` HTTP ${res?.status ?? 'err'}`) : ''}`)
1162
+ }
1163
+ }
1164
+ writeln()
1165
+ writeln(` ${green('✓')} Submitted. Visit ${cyan(`signalaf.com/user/${codename}`)}`)
1166
+ } catch (e) {
1167
+ writeln(red(` ✗ ${e.message}`))
1168
+ }
1169
+ resolve()
1170
+ } else if (chunk === '\u007f') { // backspace
1171
+ if (codename.length > 0) {
1172
+ codename = codename.slice(0, -1)
1173
+ process.stdout.write('\b \b')
1174
+ }
1175
+ } else {
1176
+ codename += chunk
1177
+ process.stdout.write(chunk)
1178
+ }
1179
+ })
1180
+ process.stdin.resume()
1181
+ }
1182
+ })
1183
+ })
1184
+ }
1185
+
1186
+ write(SHOW_CURSOR)
1187
+ if (process.stdin.isTTY) {
1188
+ process.stdin.setRawMode(false)
1189
+ process.stdin.pause()
1190
+ }
1191
+ }
1192
+
1193
+ // ── HELP ─────────────────────────────────────────────────────────────────────
1194
+
1195
+ async function showHelp() {
1196
+ const { createRequire } = await import('module')
1197
+ const pkg = createRequire(import.meta.url)('./package.json')
1198
+ writeln()
1199
+ writeln(` ${gold('⊙ SigRank')} ${bold('CLI')} ${dim('v' + pkg.version)}`)
1200
+ writeln()
1201
+ writeln(` ${bold('Default (no args)')}`)
1202
+ writeln(` ${cyan('sigrank')} unified dashboard: cascade + token pillars + board`)
1203
+ writeln()
1204
+ writeln(` ${bold('Commands')}`)
1205
+ writeln(` ${cyan('enroll')} sign in: paste a connect code (get one at signalaf.com → Settings)`)
1206
+ writeln(` ${cyan('submit')} publish your verified runs to the board (sign in first)`)
1207
+ writeln(` ${cyan('board')} live leaderboard (refreshes every 30s)`)
1208
+ writeln(` ${cyan('board --window 7d')} board for a specific window (7d, 30d, 90d, all)`)
1209
+ writeln(` ${cyan('board --once')} print once and exit`)
1210
+ writeln(` ${cyan('me')} single-platform cascade (claude by default)`)
1211
+ writeln(` ${cyan('me --platform codex')} use a different platform adapter`)
1212
+ writeln(` ${cyan('compare')} raw pillar audit: tokenpull vs ccusage vs token-dash vs tokscale`)
1213
+ writeln(` ${cyan('compare --platform codex')} compare for a specific platform`)
1214
+ writeln(` ${cyan('tui')} full tabbed TUI: Dashboard / Compare / Board / Watch`)
1215
+ writeln(` ${cyan('tui --platform codex')} TUI with a different default platform`)
1216
+ writeln(` ${cyan('watch')} live tune meter — re-reads local logs every 30s`)
1217
+ writeln(` ${cyan('watch --window 7d')} watch a specific window`)
1218
+ writeln()
1219
+ writeln(` ${bold('Options')}`)
1220
+ writeln(` ${dim('--window')} 7d · 30d · 90d · all (default: 30d for board, 7d for watch)`)
1221
+ writeln(` ${dim('--platform')} claude · codex · amp · gemini · opencode · goose · …`)
1222
+ writeln(` ${dim('--refresh')} poll interval in seconds (default: 30)`)
1223
+ writeln(` ${dim('--once')} print once and exit (board only)`)
1224
+ writeln()
1225
+ writeln(` ${bold('For AI clients (not typeable)')}`)
1226
+ writeln(` ${dim('In a piped/non-TTY context, sigrank is an MCP stdio server.')}`)
1227
+ writeln(` ${dim('AI clients (Claude, Cursor, …) call its tools automatically — these are')}`)
1228
+ writeln(` ${dim('NOT shell commands. Humans use the commands above.')}`)
1229
+ writeln()
1230
+ writeln(` ${bold('Examples')}`)
1231
+ writeln(` ${dim('sigrank')} # unified dashboard`)
1232
+ writeln(` ${dim('sigrank board')} # live leaderboard`)
1233
+ writeln(` ${dim('sigrank compare')} # pillar audit (claude)`)
1234
+ writeln(` ${dim('sigrank compare --platform codex')}`)
1235
+ writeln(` ${dim('sigrank me --platform codex')}`)
1236
+ writeln(` ${dim('sigrank watch --window 7d --refresh 60')}`)
1237
+ writeln(` ${dim('sigrank board --window all --once')}`)
1238
+ writeln()
1239
+ }
1240
+
1241
+ // ── ENTRY POINT ───────────────────────────────────────────────────────────────
1242
+
1243
+ // ── enroll: bind this device to your operator via a web connect code (D7 §4.5) ──
1244
+ async function runEnroll({ label } = {}) {
1245
+ const id = ensureIdentity()
1246
+ writeln()
1247
+ writeln(` ${gold('⊙ SigRank')} ${bold('Connect this device')}`)
1248
+ writeln(` ${dim(`device ${id.device_id}`)}`)
1249
+ if (id.codename && id.operator_id) {
1250
+ writeln(` ${dim(`already enrolled as ${id.codename} — re-enrolling a bound device is rejected this slice.`)}`)
1251
+ }
1252
+ writeln(` ${dim('Get a code at signalaf.com → Settings → Connect a device.')}`)
1253
+ writeln()
1254
+
1255
+ const { createInterface } = await import('node:readline/promises')
1256
+ const rl = createInterface({ input: process.stdin, output: process.stdout })
1257
+ let code
1258
+ try {
1259
+ code = (await rl.question(' Paste your connect code: ')).trim()
1260
+ } finally {
1261
+ rl.close()
1262
+ }
1263
+ if (!code) {
1264
+ writeln(red(' ✗ No code entered.'))
1265
+ process.exitCode = 1
1266
+ return
1267
+ }
1268
+
1269
+ let out
1270
+ try {
1271
+ out = await callTool('enroll', { code, device_label: typeof label === 'string' ? label : undefined })
1272
+ } catch (e) {
1273
+ writeln(red(` ✗ ${e.message}`))
1274
+ process.exitCode = 1
1275
+ return
1276
+ }
1277
+
1278
+ if (out.status === 'enrolled') {
1279
+ writeln()
1280
+ writeln(` ${green('✓')} Signed in as ${cyan(out.codename || '(operator)')}.`)
1281
+ writeln(` ${dim('Your runs now cascade to the board. Publish with ')}${bold('npx sigrank submit')}${dim(' (one-shot) or ')}${bold('npx sigrank watch --submit')}${dim(' (continuous).')}`)
1282
+ writeln(` ${dim(`identity: ${keystorePath()}`)}`)
1283
+ return
1284
+ }
1285
+
1286
+ const msg =
1287
+ {
1288
+ code_invalid: 'That code is invalid, expired, or already used — generate a fresh one.',
1289
+ device_already_enrolled: 'This device is already enrolled. Need a new key? Click "New key" at signalaf.com → Settings, then paste it here.',
1290
+ bad_request: 'The code or device key was malformed.',
1291
+ rate_limited: 'Too many attempts — wait a few minutes and retry.',
1292
+ persistence_unavailable: 'Enrollment is temporarily unavailable — try again shortly.',
1293
+ }[out.reason] || `Enrollment failed (${out.reason || 'unknown'}).`
1294
+ writeln(red(` ✗ ${msg}`))
1295
+ process.exitCode = 1
1296
+ }
1297
+
1298
+ // ── submit: publish your verified (signed) runs to the board (D7 §5/§8) ─────────
1299
+ async function runSubmit({ platform = 'claude', window } = {}) {
1300
+ const id = ensureIdentity()
1301
+ if (!id.codename || !id.operator_id) {
1302
+ writeln(red(' ✗ This device is not enrolled. Run `npx sigrank enroll` first.'))
1303
+ process.exitCode = 1
1304
+ return
1305
+ }
1306
+ writeln()
1307
+ writeln(` ${gold('⊙ SigRank')} ${bold('Publishing verified runs')} ${dim(`as ${id.codename}`)}`)
1308
+ writeln(` ${dim('reading local token logs + signing…')}`)
1309
+
1310
+ let out
1311
+ try {
1312
+ out = await callTool('submit_verified', { platform, window: typeof window === 'string' ? window : undefined })
1313
+ } catch (e) {
1314
+ writeln(red(` ✗ ${e.message}`))
1315
+ process.exitCode = 1
1316
+ return
1317
+ }
1318
+ if (out.status === 'not_enrolled') {
1319
+ writeln(red(' ✗ Not enrolled — run `npx sigrank enroll`.'))
1320
+ process.exitCode = 1
1321
+ return
1322
+ }
1323
+
1324
+ writeln()
1325
+ for (const w of out.windows || []) {
1326
+ const label = String(w.window).padEnd(8)
1327
+ if (w.status === 'received') {
1328
+ writeln(` ${green('✓')} ${bold(label)} ${dim(`tier=${w.verification_tier || '—'}${w.persisted ? ' · persisted' : ''}`)}`)
1329
+ } else {
1330
+ writeln(` ${red('✗')} ${bold(label)} ${dim(w.reason || w.status || 'failed')}`)
1331
+ }
1332
+ }
1333
+ writeln(` ${dim(`Reload your board row: signalaf.com/user/${id.codename}`)}`)
1334
+ }
1335
+
1336
+ export async function runCli(argv) {
1337
+ const args = argv.slice(2) // strip 'node' + script path
1338
+ const cmd = args[0]
1339
+
1340
+ // parse --key value flags
1341
+ const flags = {}
1342
+ for (let i = 1; i < args.length; i++) {
1343
+ if (args[i].startsWith('--')) {
1344
+ const key = args[i].slice(2)
1345
+ const val = args[i + 1] && !args[i + 1].startsWith('--') ? args[++i] : true
1346
+ flags[key] = val
1347
+ }
1348
+ }
1349
+
1350
+ try {
1351
+ if (cmd === 'board') {
1352
+ await runBoard({
1353
+ window: flags.window ?? '30d',
1354
+ once: flags.once === true || flags.once === 'true',
1355
+ refresh: Number(flags.refresh) || 30,
1356
+ })
1357
+ } else if (cmd === 'me') {
1358
+ await runMe({ platform: flags.platform ?? 'claude', compare: flags.compare === true || flags.compare === 'true' })
1359
+ } else if (cmd === 'compare') {
1360
+ await runCompare({ platform: flags.platform ?? 'claude' })
1361
+ } else if (cmd === 'tui') {
1362
+ const { runTui } = await import('./tui.mjs')
1363
+ await runTui({ platform: flags.platform ?? 'claude', window: flags.window ?? '7d' })
1364
+ } else if (cmd === 'watch') {
1365
+ await runWatch({
1366
+ platform: flags.platform ?? 'claude',
1367
+ window: flags.window ?? '7d',
1368
+ refresh: Number(flags.refresh) || 30,
1369
+ submit: flags.submit === true || flags.submit === 'true',
1370
+ })
1371
+ } else if (cmd === 'enroll') {
1372
+ await runEnroll({ label: flags.label })
1373
+ } else if (cmd === 'submit') {
1374
+ await runSubmit({ platform: flags.platform ?? 'claude', window: flags.window })
1375
+ } else if (cmd === '--help' || cmd === '-h' || cmd === 'help') {
1376
+ await showHelp()
1377
+ } else if (cmd === '--version' || cmd === '-v') {
1378
+ const { createRequire } = await import('module')
1379
+ const req = createRequire(import.meta.url)
1380
+ const pkg = req('./package.json')
1381
+ writeln(pkg.version)
1382
+ } else if (!cmd || cmd === 'start' || cmd === 'run') {
1383
+ // default: full unified view
1384
+ await runSigRank()
1385
+ } else {
1386
+ // unknown command: show help
1387
+ await showHelp()
1388
+ }
1389
+ } catch (e) {
1390
+ write(SHOW_CURSOR)
1391
+ writeln(red(`\n ✗ ${e.message}`))
1392
+ process.exit(1)
1393
+ }
1394
+ }