sigrank-mcp 0.6.5 → 0.6.6

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.
Files changed (2) hide show
  1. package/cli.mjs +163 -81
  2. 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 } from 'fs'
26
26
  import os from 'os'
27
27
  import path from 'path'
28
28
 
@@ -431,6 +431,51 @@ 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
480
  // query token-dashboard SQLite directly
436
481
  const dbPath = path.join(os.homedir(), '.claude', 'token-dashboard.db')
@@ -475,19 +520,44 @@ function fmtDelta(a, b) {
475
520
  return d > 0 ? green(label) : red(label)
476
521
  }
477
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 = yield_ > 500 ? 'TRANSMITTER' : yield_ > 400 ? 'ARCH+' : yield_ > 300 ? 'ARCH' : yield_ > 150 ? 'POWER' : 'BASE'
546
+ return { yield: yield_, velocity, leverage, snr, dev10x, efficiency, class: cls, total }
547
+ }
548
+
478
549
  async function runCompare({ platform = 'claude' } = {}) {
479
550
  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
551
 
489
- writeln(` ${dim('reading token-dashboard…')}`)
490
- const tdPillars = tokenDashPillars()
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(tokenDashPillars()),
558
+ Promise.resolve(tokscalePillars()),
559
+ Promise.resolve(appPillars()),
560
+ ])
491
561
  write(CURSOR_UP(1) + ERASE_LINE)
492
562
 
493
563
  const w = termWidth()
@@ -500,103 +570,115 @@ async function runCompare({ platform = 'claude' } = {}) {
500
570
  tpPillars[win.window] = win.pillars
501
571
  }
502
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
+
503
582
  writeln()
504
- writeln(` ${gold('⊙ SigRank')} ${bold('Source Comparison')} ${dim(`platform: ${platform} · claude tokens only`)}`)
583
+ writeln(` ${gold('⊙ SigRank')} ${bold('5-Source Comparison')} ${dim(`platform: ${platform} · claude only`)}`)
505
584
  writeln(` ${dim('─'.repeat(w - 4))}`)
506
585
 
586
+ // ── PILLARS TABLE ──────────────────────────────────────────────────────────
507
587
  const PILLARS = [
508
588
  { key: 'input', label: 'Input' },
509
589
  { key: 'output', label: 'Output' },
510
- { key: 'cacheCreate', label: 'Cache Create' },
590
+ { key: 'cacheCreate', label: 'Cache Write' },
511
591
  { key: 'cacheRead', label: 'Cache Read' },
512
592
  ]
513
593
 
514
594
  for (const { key, label } of PILLARS) {
515
595
  writeln()
516
596
  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
- ]
597
+ const hcols = [padEnd(dim('Source'), 14), ...WINS.map(win => padStart(dim(WIN_LABEL[win]), 13))]
523
598
  writeln(` ${hcols.join(' ')}`)
524
- writeln(` ${dim('·'.repeat(Math.min(w - 4, 16 + WINS.length * 16)))}`)
525
-
526
- // ccusage row
527
- if (ccPillars) {
528
- const cols = [padEnd(cyan('ccusage'), 16), ...WINS.map(win => padStart(fmtTokens(ccPillars[win]?.[key] ?? 0), 14))]
529
- writeln(` ${cols.join(' ')}`)
530
- } else {
531
- writeln(` ${padEnd(dim('ccusage'), 16)} ${dim('not found')}`)
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(' ')}`)
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(' ')}`)
561
609
  }
562
610
  }
563
611
 
564
- // SigRank cascade section
612
+ // ── SIGNATURE TABLE ────────────────────────────────────────────────────────
565
613
  writeln()
566
614
  writeln(` ${dim('─'.repeat(w - 4))}`)
567
- writeln(` ${bold('SigRank Cascade')} ${dim('(tokenpull cascade math)')}`)
615
+ writeln(` ${bold('Cascade Signature')} ${dim('per source · all windows where data available')}`)
568
616
  writeln()
569
617
 
570
- const CASCADE_ROWS = [
571
- { key: 'yield', label: 'Υ Yield', fmt: v => fmtYield(v) },
572
- { key: 'snr', label: 'SNR', fmt: v => fmtSNR(v) },
573
- { key: 'leverage', label: 'Leverage', fmt: v => `${fmtLev(v)}×` },
574
- { key: 'velocity', label: 'Velocity', fmt: v => v.toFixed(2) },
575
- { key: 'dev10x', label: '10xDEV', fmt: v => v.toFixed(2) },
576
- { key: 'class', label: 'Class', fmt: v => colorClass(v) },
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 },
577
626
  ]
578
627
 
579
- const hcols = [padEnd(dim('Metric'), 12), ...WINS.map(win => padStart(dim(WIN_LABEL[win]), 12))]
580
- writeln(` ${hcols.join(' ')}`)
581
- writeln(` ${dim('·'.repeat(Math.min(w - 4, 12 + WINS.length * 14)))}`)
582
-
583
- for (const { key, label, fmt } of CASCADE_ROWS) {
584
- const cols = [
585
- padEnd(dim(label), 12),
586
- ...WINS.map(win => {
587
- const cas = tpPillars[win] ? (tpData?.windows?.find(w => w.window === win)?.cascade) : null
588
- const v = cas?.[key]
589
- return padStart(v != null ? fmt(v) : dim('—'), 12)
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)
590
668
  })
591
- ]
592
- writeln(` ${cols.join(' ')}`)
669
+ writeln(` ${padEnd(srcLabel, 14)} ${padEnd(winLabel, 8)} ${sigCols.join(' ')}`)
670
+ first = false
671
+ }
593
672
  }
594
673
 
595
- // estimated rank hint
674
+ // ── NOTES ─────────────────────────────────────────────────────────────────
596
675
  writeln()
597
676
  writeln(` ${dim('─'.repeat(w - 4))}`)
598
- writeln(` ${dim('note: token-dash has longer history (SQLite); tokenpull reads live JSONL only')}`)
599
- writeln(` ${dim('note: ccusage = primary pillar source (all agents); tokenpull = claude-only cascade input')}`)
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')}`)
600
682
  writeln()
601
683
  write(SHOW_CURSOR)
602
684
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigrank-mcp",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
4
4
  "description": "SigRank MCP server — the yield cascade + live leaderboard as MCP tools any agent can call",
5
5
  "type": "module",
6
6
  "bin": {