sigrank-mcp 0.6.5 → 0.6.7
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 +181 -106
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
import { callTool, DEFAULT_API_BASE } from './tools.mjs'
|
|
24
24
|
import { execSync } from 'child_process'
|
|
25
|
-
import { existsSync } from 'fs'
|
|
25
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
|
26
26
|
import os from 'os'
|
|
27
27
|
import path from 'path'
|
|
28
28
|
|
|
@@ -431,35 +431,73 @@ function ccusagePillars(platform = 'claude') {
|
|
|
431
431
|
}
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
function tokscalePillars() {
|
|
435
|
+
// Read tokscale_report.json — claude client only, all-time only (no timestamps in export)
|
|
436
|
+
const reportPath = path.join(os.homedir(), 'tokscale_report.json')
|
|
437
|
+
if (!existsSync(reportPath)) return null
|
|
438
|
+
try {
|
|
439
|
+
const data = JSON.parse(readFileSync(reportPath, 'utf8'))
|
|
440
|
+
const entries = data.entries ?? []
|
|
441
|
+
const claude = entries.filter(e =>
|
|
442
|
+
e.client === 'claude' &&
|
|
443
|
+
e.model !== '<synthetic>' && e.model !== 'unknown' &&
|
|
444
|
+
e.provider !== 'unknown'
|
|
445
|
+
)
|
|
446
|
+
const p = claude.reduce((acc, e) => ({
|
|
447
|
+
input: acc.input + (e.input ?? 0),
|
|
448
|
+
output: acc.output + (e.output ?? 0),
|
|
449
|
+
cacheCreate: acc.cacheCreate + (e.cacheWrite ?? 0),
|
|
450
|
+
cacheRead: acc.cacheRead + (e.cacheRead ?? 0),
|
|
451
|
+
}), { input: 0, output: 0, cacheCreate: 0, cacheRead: 0 })
|
|
452
|
+
// tokscale export has no timestamps → only all-time available
|
|
453
|
+
return { all: p }
|
|
454
|
+
} catch { return null }
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function appPillars() {
|
|
458
|
+
// App numbers from screenshots — all-time, per model (no cache fields)
|
|
459
|
+
// Hard-coded from 2026-06-23 screenshot capture (update when re-screenshotted)
|
|
460
|
+
return {
|
|
461
|
+
all: {
|
|
462
|
+
input: 6_378_000, // sum of all models: 5.6M + 102.1K + 92.9K + 130.3K + 418.9K + 33.5K
|
|
463
|
+
output: 38_682_400, // sum: 19.6M + 6.5M + 5.4M + 6.6M + 292.4K + 290.4K
|
|
464
|
+
cacheCreate: null, // not shown in App UI
|
|
465
|
+
cacheRead: null, // not shown in App UI
|
|
466
|
+
},
|
|
467
|
+
_note: 'App UI — all-time, per-model sum from screenshots 2026-06-23. No cache fields. Update when re-screenshotted.',
|
|
468
|
+
_perModel: [
|
|
469
|
+
{ model: 'claude-opus-4-8', input: 5_600_000, output: 19_600_000 },
|
|
470
|
+
{ model: 'claude-sonnet-4-5',input: 102_100, output: 6_500_000 },
|
|
471
|
+
{ model: 'claude-sonnet-4-6',input: 92_900, output: 5_400_000 },
|
|
472
|
+
{ model: 'claude-opus-4-7', input: 130_300, output: 6_600_000 },
|
|
473
|
+
{ model: 'claude-fable-5', input: 418_900, output: 292_400 },
|
|
474
|
+
{ model: 'claude-haiku-4-5', input: 33_500, output: 290_400 },
|
|
475
|
+
],
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
434
479
|
function tokenDashPillars() {
|
|
435
|
-
// query token-dashboard SQLite directly
|
|
436
480
|
const dbPath = path.join(os.homedir(), '.claude', 'token-dashboard.db')
|
|
437
481
|
if (!existsSync(dbPath)) return null
|
|
438
482
|
try {
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
r = db.execute('SELECT SUM(input_tokens),SUM(output_tokens),SUM(cache_create_5m_tokens+cache_create_1h_tokens),SUM(cache_read_tokens) FROM messages WHERE ${claudeFilter}').fetchone()
|
|
458
|
-
print(json.dumps({'input':r[0] or 0,'output':r[1] or 0,'cacheCreate':r[2] or 0,'cacheRead':r[3] or 0}))
|
|
459
|
-
"`
|
|
460
|
-
const raw = execSync(cmd, { timeout: 10000, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()
|
|
461
|
-
result['all'] = JSON.parse(raw)
|
|
462
|
-
return result
|
|
483
|
+
const tmpScript = path.join(os.tmpdir(), 'sigrank_td_query.py')
|
|
484
|
+
writeFileSync(tmpScript, `
|
|
485
|
+
import sqlite3, json, sys
|
|
486
|
+
from datetime import datetime, timezone, timedelta
|
|
487
|
+
db = sqlite3.connect(sys.argv[1])
|
|
488
|
+
cf = "(model LIKE '%claude%' OR model LIKE '%fable%' OR model LIKE '%sonnet%' OR model LIKE '%opus%' OR model LIKE '%haiku%')"
|
|
489
|
+
out = {}
|
|
490
|
+
for win, days in [('7d',7),('30d',30),('90d',90)]:
|
|
491
|
+
since = (datetime.now(timezone.utc) - timedelta(days=days)).isoformat()
|
|
492
|
+
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()
|
|
493
|
+
out[win] = {'input':r[0] or 0,'output':r[1] or 0,'cacheCreate':r[2] or 0,'cacheRead':r[3] or 0}
|
|
494
|
+
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()
|
|
495
|
+
out['all'] = {'input':r[0] or 0,'output':r[1] or 0,'cacheCreate':r[2] or 0,'cacheRead':r[3] or 0}
|
|
496
|
+
print(json.dumps(out))
|
|
497
|
+
`)
|
|
498
|
+
const raw = execSync(`python3 "${tmpScript}" "${dbPath}"`,
|
|
499
|
+
{ timeout: 15000, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()
|
|
500
|
+
return JSON.parse(raw)
|
|
463
501
|
} catch {
|
|
464
502
|
return null
|
|
465
503
|
}
|
|
@@ -475,19 +513,44 @@ function fmtDelta(a, b) {
|
|
|
475
513
|
return d > 0 ? green(label) : red(label)
|
|
476
514
|
}
|
|
477
515
|
|
|
516
|
+
// Compute cascade metrics from raw pillars (mirrors bridge.ts computeCascadeMetrics)
|
|
517
|
+
function cascadeFromPillars(p) {
|
|
518
|
+
if (!p) return null
|
|
519
|
+
const i = p.input ?? 0
|
|
520
|
+
const o = p.output ?? 0
|
|
521
|
+
const cw = p.cacheCreate ?? 0
|
|
522
|
+
const cr = p.cacheRead ?? 0
|
|
523
|
+
if (i === 0 && o === 0) return null
|
|
524
|
+
const safeI = Math.max(i, 1)
|
|
525
|
+
const total = i + o + cw + cr
|
|
526
|
+
const velocity = o / safeI
|
|
527
|
+
const leverage = cr / safeI
|
|
528
|
+
const yield_ = leverage * velocity
|
|
529
|
+
const snr = (i + o) > 0 ? o / (i + o) : 0
|
|
530
|
+
// dev10x = log10(T × C × R) — only when all four pillars present
|
|
531
|
+
let dev10x = null
|
|
532
|
+
if (cw > 0 && o > 0 && i > 0 && cr > 0) {
|
|
533
|
+
const T = o / i, C = cw / o, R = cr / cw
|
|
534
|
+
dev10x = Math.log10(T * C * R)
|
|
535
|
+
}
|
|
536
|
+
// efficiency = ((cr+cw+o)/i) / 4.0
|
|
537
|
+
const efficiency = ((cr + cw + o) / safeI) / 4.0
|
|
538
|
+
const cls = yield_ > 500 ? 'TRANSMITTER' : yield_ > 400 ? 'ARCH+' : yield_ > 300 ? 'ARCH' : yield_ > 150 ? 'POWER' : 'BASE'
|
|
539
|
+
return { yield: yield_, velocity, leverage, snr, dev10x, efficiency, class: cls, total }
|
|
540
|
+
}
|
|
541
|
+
|
|
478
542
|
async function runCompare({ platform = 'claude' } = {}) {
|
|
479
543
|
write(HIDE_CURSOR)
|
|
480
|
-
writeln(` ${dim('reading ccusage…')}`)
|
|
481
|
-
const ccPillars = ccusagePillars(platform)
|
|
482
|
-
write(CURSOR_UP(1) + ERASE_LINE)
|
|
483
|
-
|
|
484
|
-
writeln(` ${dim('reading tokenpull…')}`)
|
|
485
|
-
let tpData
|
|
486
|
-
try { tpData = await callTool('tokenpull', { platform }) } catch { tpData = null }
|
|
487
|
-
write(CURSOR_UP(1) + ERASE_LINE)
|
|
488
544
|
|
|
489
|
-
|
|
490
|
-
|
|
545
|
+
// Pull all five sources in parallel
|
|
546
|
+
writeln(` ${dim('reading all 5 sources…')}`)
|
|
547
|
+
const [ccPillars, tpData, tdPillars, tsPillars, apPillars] = await Promise.all([
|
|
548
|
+
Promise.resolve(ccusagePillars(platform)),
|
|
549
|
+
callTool('tokenpull', { platform }).catch(() => null),
|
|
550
|
+
Promise.resolve(tokenDashPillars()),
|
|
551
|
+
Promise.resolve(tokscalePillars()),
|
|
552
|
+
Promise.resolve(appPillars()),
|
|
553
|
+
])
|
|
491
554
|
write(CURSOR_UP(1) + ERASE_LINE)
|
|
492
555
|
|
|
493
556
|
const w = termWidth()
|
|
@@ -500,103 +563,115 @@ async function runCompare({ platform = 'claude' } = {}) {
|
|
|
500
563
|
tpPillars[win.window] = win.pillars
|
|
501
564
|
}
|
|
502
565
|
|
|
566
|
+
// sources: name, color, pillars-by-window, note
|
|
567
|
+
const SOURCES = [
|
|
568
|
+
{ name: 'tokenpull', color: cyan, pillars: tpPillars, note: 'JSONL deduped by msg id · canon source' },
|
|
569
|
+
{ name: 'ccusage', color: (s) => paint(c.green, s), pillars: ccPillars ?? {}, note: 'ccusage claude subcommand · monthly only' },
|
|
570
|
+
{ name: 'token-dash', color: (s) => paint(c.magenta,s), pillars: tdPillars ?? {}, note: 'SQLite — double-counts sessions · use with caution' },
|
|
571
|
+
{ name: 'tokscale', color: (s) => paint(c.blue, s), pillars: tsPillars ?? {}, note: 'all-time only · partial export (~5% of opus-4-8)' },
|
|
572
|
+
{ name: 'App', color: gold, pillars: apPillars ?? {}, note: 'screenshots 2026-06-23 · no cache fields · update manually' },
|
|
573
|
+
]
|
|
574
|
+
|
|
503
575
|
writeln()
|
|
504
|
-
writeln(` ${gold('⊙ SigRank')} ${bold('Source Comparison')} ${dim(`platform: ${platform} · claude
|
|
576
|
+
writeln(` ${gold('⊙ SigRank')} ${bold('5-Source Comparison')} ${dim(`platform: ${platform} · claude only`)}`)
|
|
505
577
|
writeln(` ${dim('─'.repeat(w - 4))}`)
|
|
506
578
|
|
|
579
|
+
// ── PILLARS TABLE ──────────────────────────────────────────────────────────
|
|
507
580
|
const PILLARS = [
|
|
508
581
|
{ key: 'input', label: 'Input' },
|
|
509
582
|
{ key: 'output', label: 'Output' },
|
|
510
|
-
{ key: 'cacheCreate', label: 'Cache
|
|
583
|
+
{ key: 'cacheCreate', label: 'Cache Write' },
|
|
511
584
|
{ key: 'cacheRead', label: 'Cache Read' },
|
|
512
585
|
]
|
|
513
586
|
|
|
514
587
|
for (const { key, label } of PILLARS) {
|
|
515
588
|
writeln()
|
|
516
589
|
writeln(` ${bold(label)}`)
|
|
517
|
-
|
|
518
|
-
// header row
|
|
519
|
-
const hcols = [
|
|
520
|
-
padEnd(dim('Source'), 16),
|
|
521
|
-
...WINS.map(win => padStart(dim(WIN_LABEL[win]), 14))
|
|
522
|
-
]
|
|
590
|
+
const hcols = [padEnd(dim('Source'), 14), ...WINS.map(win => padStart(dim(WIN_LABEL[win]), 13))]
|
|
523
591
|
writeln(` ${hcols.join(' ')}`)
|
|
524
|
-
writeln(` ${dim('·'.repeat(Math.min(w - 4,
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
// tokenpull row
|
|
535
|
-
if (tpData) {
|
|
536
|
-
const cols = [padEnd(cyan('tokenpull'), 16), ...WINS.map(win => padStart(fmtTokens(tpPillars[win]?.[key] ?? 0), 14))]
|
|
537
|
-
writeln(` ${cols.join(' ')}`)
|
|
538
|
-
} else {
|
|
539
|
-
writeln(` ${padEnd(dim('tokenpull'), 16)} ${dim('not found')}`)
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// token-dashboard row
|
|
543
|
-
if (tdPillars) {
|
|
544
|
-
const cols = [padEnd(cyan('token-dash'), 16), ...WINS.map(win => padStart(fmtTokens(tdPillars[win]?.[key] ?? 0), 14))]
|
|
545
|
-
writeln(` ${cols.join(' ')}`)
|
|
546
|
-
} else {
|
|
547
|
-
writeln(` ${padEnd(dim('token-dash'), 16)} ${dim('not found — run: python3 ~/token-dashboard/cli.py scan')}`)
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// delta row: tokenpull vs token-dash (the two most comparable)
|
|
551
|
-
if (tpData && tdPillars) {
|
|
552
|
-
const cols = [
|
|
553
|
-
padEnd(dim('Δ tp→td'), 16),
|
|
554
|
-
...WINS.map(win => {
|
|
555
|
-
const a = tpPillars[win]?.[key] ?? 0
|
|
556
|
-
const b = tdPillars[win]?.[key] ?? 0
|
|
557
|
-
return padStart(fmtDelta(a, b), 14)
|
|
558
|
-
})
|
|
559
|
-
]
|
|
560
|
-
writeln(` ${cols.join(' ')}`)
|
|
592
|
+
writeln(` ${dim('·'.repeat(Math.min(w - 4, 14 + WINS.length * 15)))}`)
|
|
593
|
+
|
|
594
|
+
for (const src of SOURCES) {
|
|
595
|
+
const vals = WINS.map(win => {
|
|
596
|
+
const p = src.pillars[win]
|
|
597
|
+
const v = p?.[key]
|
|
598
|
+
if (v == null) return padStart(dim('—'), 13)
|
|
599
|
+
return padStart(fmtTokens(v), 13)
|
|
600
|
+
})
|
|
601
|
+
writeln(` ${padEnd(src.color(src.name), 14)} ${vals.join(' ')}`)
|
|
561
602
|
}
|
|
562
603
|
}
|
|
563
604
|
|
|
564
|
-
//
|
|
605
|
+
// ── SIGNATURE TABLE ────────────────────────────────────────────────────────
|
|
565
606
|
writeln()
|
|
566
607
|
writeln(` ${dim('─'.repeat(w - 4))}`)
|
|
567
|
-
writeln(` ${bold('
|
|
608
|
+
writeln(` ${bold('Cascade Signature')} ${dim('per source · all windows where data available')}`)
|
|
568
609
|
writeln()
|
|
569
610
|
|
|
570
|
-
const
|
|
571
|
-
{ key: 'yield',
|
|
572
|
-
{ key: '
|
|
573
|
-
{ key: 'leverage',
|
|
574
|
-
{ key: '
|
|
575
|
-
{ key: 'dev10x',
|
|
576
|
-
{ key: '
|
|
611
|
+
const SIG_METRICS = [
|
|
612
|
+
{ key: 'yield', label: 'Υ Yield', fmt: v => fmtYield(v), w: 9 },
|
|
613
|
+
{ key: 'velocity', label: 'Vel', fmt: v => v.toFixed(2), w: 6 },
|
|
614
|
+
{ key: 'leverage', label: 'Lev', fmt: v => `${fmtLev(v)}×`, w: 7 },
|
|
615
|
+
{ key: 'snr', label: 'SNR', fmt: v => fmtSNR(v), w: 6 },
|
|
616
|
+
{ key: 'dev10x', label: '10x', fmt: v => v.toFixed(2), w: 5 },
|
|
617
|
+
{ key: 'efficiency', label: 'Eff', fmt: v => v.toFixed(1), w: 6 },
|
|
618
|
+
{ key: 'class', label: 'Class', fmt: v => colorClass(v), w: 12 },
|
|
577
619
|
]
|
|
578
620
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
621
|
+
// header
|
|
622
|
+
const sigHdr = [
|
|
623
|
+
padEnd(dim('Source'), 14),
|
|
624
|
+
padEnd(dim('Window'), 8),
|
|
625
|
+
...SIG_METRICS.map(m => padStart(dim(m.label), m.w)),
|
|
626
|
+
]
|
|
627
|
+
writeln(` ${sigHdr.join(' ')}`)
|
|
628
|
+
writeln(` ${dim('·'.repeat(Math.min(w - 4, 80)))}`)
|
|
629
|
+
|
|
630
|
+
for (const src of SOURCES) {
|
|
631
|
+
const availWins = WINS.filter(win => src.pillars[win] != null)
|
|
632
|
+
if (availWins.length === 0) {
|
|
633
|
+
writeln(` ${padEnd(src.color(src.name), 14)} ${dim('no data')}`)
|
|
634
|
+
continue
|
|
635
|
+
}
|
|
636
|
+
let first = true
|
|
637
|
+
for (const win of availWins) {
|
|
638
|
+
const p = src.pillars[win]
|
|
639
|
+
// For tokenpull, use the pre-computed cascade from the tool if available
|
|
640
|
+
let cas
|
|
641
|
+
if (src.name === 'tokenpull') {
|
|
642
|
+
const tpWin = tpData?.windows?.find(ww => ww.window === win)
|
|
643
|
+
cas = tpWin?.cascade ? {
|
|
644
|
+
yield: tpWin.cascade.yield,
|
|
645
|
+
velocity: tpWin.cascade.velocity,
|
|
646
|
+
leverage: tpWin.cascade.leverage,
|
|
647
|
+
snr: tpWin.cascade.snr,
|
|
648
|
+
dev10x: tpWin.cascade.dev10x,
|
|
649
|
+
efficiency: null,
|
|
650
|
+
class: tpWin.cascade.class,
|
|
651
|
+
} : cascadeFromPillars(p)
|
|
652
|
+
} else {
|
|
653
|
+
cas = cascadeFromPillars(p)
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const srcLabel = first ? src.color(src.name) : ' '.repeat(stripAnsi(src.name).length)
|
|
657
|
+
const winLabel = win === 'all' ? bold('all-time') : win
|
|
658
|
+
const sigCols = SIG_METRICS.map(m => {
|
|
659
|
+
const v = cas?.[m.key]
|
|
660
|
+
return padStart(v != null ? m.fmt(v) : dim('—'), m.w)
|
|
590
661
|
})
|
|
591
|
-
|
|
592
|
-
|
|
662
|
+
writeln(` ${padEnd(srcLabel, 14)} ${padEnd(winLabel, 8)} ${sigCols.join(' ')}`)
|
|
663
|
+
first = false
|
|
664
|
+
}
|
|
593
665
|
}
|
|
594
666
|
|
|
595
|
-
//
|
|
667
|
+
// ── NOTES ─────────────────────────────────────────────────────────────────
|
|
596
668
|
writeln()
|
|
597
669
|
writeln(` ${dim('─'.repeat(w - 4))}`)
|
|
598
|
-
|
|
599
|
-
|
|
670
|
+
for (const src of SOURCES) {
|
|
671
|
+
writeln(` ${src.color(src.name.padEnd(12))} ${dim(src.note)}`)
|
|
672
|
+
}
|
|
673
|
+
writeln(` ${dim('Eff = ((cacheRead+cacheWrite+output)/input)/4.0 vs AA baseline')}`)
|
|
674
|
+
writeln(` ${dim('App has no cache fields → Υ/Lev/Eff/10x unavailable from App source')}`)
|
|
600
675
|
writeln()
|
|
601
676
|
write(SHOW_CURSOR)
|
|
602
677
|
}
|