sigrank 0.0.215 → 0.0.217

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/manifest.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://raw.githubusercontent.com/anthropics/mcpb/main/schemas/mcpb-manifest-v0.4.schema.json",
3
3
  "manifest_version": "0.4",
4
4
  "name": "sigrank",
5
- "version": "0.0.214",
5
+ "version": "0.0.216",
6
6
  "description": "Ranks AI coding operators by token cascade efficiency (Yield = cache_read \u00d7 output / input\u00b2). 23 tools for yield calculation, local token usage pulls, signed submission, and tokscale-powered analytics (market share, developer profiles, model trends, cost analysis, device profiles, MCP usage, competitive intelligence). Privacy-first: runs locally, only four token counts leave the machine.",
7
7
  "author": {
8
8
  "name": "SunrisesIllNeverSee",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigrank",
3
- "version": "0.0.215",
3
+ "version": "0.0.217",
4
4
  "author": "SunrisesIllNeverSee (https://github.com/SunrisesIllNeverSee)",
5
5
  "description": "SigRank MCP server — AI operator evaluation and token cascade efficiency tools. The yield cascade + live leaderboard as MCP tools any agent can call.",
6
6
  "mcpName": "io.github.SunrisesIllNeverSee/sigrank-mcp",
@@ -124,3 +124,66 @@ export function setTheme(name) {
124
124
  export function getThemeNames() {
125
125
  return Object.keys(THEMES);
126
126
  }
127
+
128
+ // ── Platform color map ─────────────────────────────────────────────────────
129
+ // Each platform gets a distinct 256-color code so platforms are visually
130
+ // distinguishable in the cascade table, token composition bars, board, and
131
+ // watch tabs. The colors are chosen for distinguishability on dark terminals
132
+ // (the default theme). For light theme, darker 256-color variants are used.
133
+ // Monochrome theme returns no color (bold only) — colorblind-safe.
134
+ //
135
+ // Color assignments (dark theme 256-color codes):
136
+ // claude = 75 (blue-cyan)
137
+ // codex = 114 (green)
138
+ // amp = 179 (orange)
139
+ // gemini = 129 (purple-magenta)
140
+ // kimi = 213 (pink)
141
+ // qwen = 68 (steel blue)
142
+ // goose = 142 (olive)
143
+ // kilo = 166 (dark orange)
144
+ // hermes = 96 (plum)
145
+ // droid = 167 (salmon)
146
+ // codebuff = 72 (teal-green)
147
+ // copilot = 33 (bright blue)
148
+ // openclaw = 136 (gold-brown)
149
+ // pi = 177 (light purple)
150
+ // omp = 220 (gold — the "oh my pi" platform gets gold)
151
+ // opencode = 60 (slate)
152
+ // devin = 30 (teal)
153
+ // proxy = 245 (gray — synthetic passthrough)
154
+ // other = 245 (gray — catch-all)
155
+
156
+ const PLATFORM_COLORS_DARK = {
157
+ claude: 75, codex: 114, amp: 179, gemini: 129, kimi: 213,
158
+ qwen: 68, goose: 142, kilo: 166, hermes: 96, droid: 167,
159
+ codebuff: 72, copilot: 33, openclaw: 136, pi: 177, omp: 220,
160
+ opencode: 60, devin: 30, proxy: 245, other: 245,
161
+ };
162
+
163
+ // Light theme: darker shades of the same hues for contrast on white
164
+ const PLATFORM_COLORS_LIGHT = {
165
+ claude: 24, codex: 28, amp: 130, gemini: 54, kimi: 90,
166
+ qwen: 19, goose: 100, kilo: 130, hermes: 54, droid: 124,
167
+ codebuff: 23, copilot: 19, openclaw: 130, pi: 90, omp: 130,
168
+ opencode: 19, devin: 23, proxy: 242, other: 242,
169
+ };
170
+
171
+ /**
172
+ * Returns a paint function for the given platform name, theme-aware.
173
+ * Usage: `const pc = platformColor("claude"); pc("claude")` → colored string.
174
+ * Falls back to the theme's cyan for unknown platforms.
175
+ */
176
+ export function platformColor(platform) {
177
+ const themeName = currentTheme.name;
178
+ // Monochrome: no color, just bold (colorblind-safe)
179
+ if (themeName === "monochrome") {
180
+ return (s) => `${currentTheme.bold}${s}${currentTheme.reset}`;
181
+ }
182
+ const map = themeName === "light" ? PLATFORM_COLORS_LIGHT : PLATFORM_COLORS_DARK;
183
+ const code = map[platform];
184
+ if (code == null) {
185
+ // Unknown platform — fall back to cyan
186
+ return (s) => `${currentTheme.cyan}${s}${currentTheme.reset}`;
187
+ }
188
+ return (s) => `${ESC}38;5;${code}m${s}${currentTheme.reset}`;
189
+ }
@@ -54,6 +54,7 @@ export function dashboardEnabledPlatforms({
54
54
  // The `c` object is a live proxy to the current theme — changing the theme
55
55
  // via setTheme() updates all colors. Supports dark/light/high-contrast/monochrome.
56
56
  import * as _themeMod from "./tui-themes.mjs";
57
+ import { platformColor } from "./tui-themes.mjs";
57
58
  const ESC = "\x1b[";
58
59
  // `c` is a live proxy — reads currentTheme at access time so theme switches
59
60
  // take effect immediately. The helper functions below (paint/bold/dim/etc.)
@@ -477,13 +478,18 @@ const TABS = [
477
478
  { key: "6", label: "Connect", short: "N" }, // sign in / switch device — the whole app is the TUI
478
479
  ];
479
480
 
480
- // Three Degrees of Leverage — reference values pulled from signalaf.com/wiki (2026-06-25).
481
- // base = AI avg (modeled 7:2:1 baseline) · field = SigRank avg (wild field median) · top = best operator.
482
- // STATIC for now; goes live (field/top computed from the board) once there's real user volume.
481
+ // Four Degrees of Leverage — reference values matching signalaf.com/wiki/four-degrees.
482
+ // base = AA baseline (7:2:1 modeled reference, static)
483
+ // hcm = Human Center of Mass (median of all real operators)
484
+ // power = Power users (median of top 100 real operators)
485
+ // top = Top Evals (single top real operator)
486
+ // STATIC fallback values match the web app's GoldColumn fallbacks (lib/marketing/top-operator-column.ts).
487
+ // Goes live (hcm/power/top computed from the board) once there's real user volume.
483
488
  const TD = {
484
- base: { yield: 1.57, snr: 0.33, vel: 0.5, lev: 3.2, d10: 0.5 },
485
- field: { yield: 1.51, snr: 0.07, vel: 0.08, lev: 22.3, d10: 1.35 },
486
- top: { yield: 745.4, snr: 0.63, vel: 1.7, lev: 438.6, d10: 2.64 },
489
+ base: { yield: 1.75, snr: 0.33, vel: 0.50, lev: 3.5, d10: 0.54 },
490
+ hcm: { yield: 1.57, snr: 0.33, vel: 0.50, lev: 3.2, d10: 0.50 },
491
+ power: { yield: 1.51, snr: 0.07, vel: 0.08, lev: 22.3, d10: 1.35 },
492
+ top: { yield: 488.65, snr: 0.58, vel: 1.36, lev: 360.2, d10: 2.56 },
487
493
  };
488
494
 
489
495
  function renderTabBar(activeIdx) {
@@ -737,8 +743,8 @@ function renderDashboard(data, status = "", scrollOffset = 0) {
737
743
  // which is what was starving codex's cascade rows after the Your-Read/Three-Degrees panels landed.)
738
744
  const platformCount = active.length;
739
745
  const barLines = 4 + platformCount + (platformCount > 1 ? 1 : 0); // Token Composition: hr + header + platforms + combined + note
740
- // Your Read (hr+header+~3 wins+1 gap = 6) + Three Degrees (blank+hr+header+col-header+5 rows+note = 10) + status
741
- const boardLines = 17;
746
+ // Your Read (hr+header+up to 4 wins+up to 5 gaps+summary = 13) + Four Degrees (blank+hr+header+col-header+5 rows+note = 10) + status
747
+ const boardLines = 20;
742
748
  const sectionsBelow = barLines + boardLines;
743
749
  const maxCascadeRows = Math.max(8, budget - used - sectionsBelow);
744
750
 
@@ -789,7 +795,10 @@ function renderDashboard(data, status = "", scrollOffset = 0) {
789
795
  if (lastPlatform !== null && row.platform !== lastPlatform) emit();
790
796
  renderRow(
791
797
  row.platform,
792
- (s) => (row.winKey === firstWin[row.platform] ? cyan(s) : dim(s)),
798
+ (s) => {
799
+ const pc = platformColor(row.platform);
800
+ return row.winKey === firstWin[row.platform] ? bold(pc(s)) : dim(s);
801
+ },
793
802
  row.winKey,
794
803
  row.pillars,
795
804
  row.estimated,
@@ -843,7 +852,7 @@ function renderDashboard(data, status = "", scrollOffset = 0) {
843
852
  const all = d.windows?.find((w) => w.window === "all");
844
853
  if (!all) continue;
845
854
  emit(
846
- ` ${padEnd(cyan(d.platform), 10)} ${tokenBar(all.pillars, 50)} ${dim(fmtTok((all.pillars.input ?? 0) + (all.pillars.output ?? 0) + (all.pillars.cacheCreate ?? 0) + (all.pillars.cacheRead ?? 0)))}`,
855
+ ` ${padEnd(platformColor(d.platform)(d.platform), 10)} ${tokenBar(all.pillars, 50)} ${dim(fmtTok((all.pillars.input ?? 0) + (all.pillars.output ?? 0) + (all.pillars.cacheCreate ?? 0) + (all.pillars.cacheRead ?? 0)))}`,
847
856
  );
848
857
  }
849
858
  if (active.length > 1) {
@@ -886,9 +895,9 @@ function renderDashboard(data, status = "", scrollOffset = 0) {
886
895
  }
887
896
  }
888
897
 
889
- // ── Your insights + the Three Degrees panel (replaces the old top-3 mini board)
898
+ // ── Your insights + the Four Degrees panel (replaces the old top-3 mini board)
890
899
  // The user's own cascade (the firstWin/combined row computed above) read against the
891
- // Three Degrees reference so the Dashboard says "here's you, here's the field, here's the ceiling."
900
+ // Four Degrees reference so the Dashboard says "here's you, here's the field, here's the ceiling."
892
901
  const you = (() => {
893
902
  // Best available cascade for the user: prefer claude's first-window, else any computed row.
894
903
  for (const d of active) {
@@ -914,24 +923,38 @@ function renderDashboard(data, status = "", scrollOffset = 0) {
914
923
  if (you) {
915
924
  const wins = [],
916
925
  gaps = [];
917
- const chk = (label, val, fieldAvg, top, fmt, tip) => {
926
+ let aboveHcm = 0,
927
+ abovePower = 0,
928
+ totalMetrics = 0;
929
+ // Three-tier comparison: HCM (field median) → Power (top-100 median) → Top (best operator)
930
+ const chk = (label, val, hcm, power, top, fmt, tip) => {
918
931
  if (val == null) return;
919
- if (val >= top * 0.9)
932
+ totalMetrics++;
933
+ if (val >= top * 0.9) {
920
934
  wins.push(`${label} ${fmt(val)} — ${gold("top tier")}`);
921
- else if (val >= fieldAvg)
922
- wins.push(`${label} ${fmt(val)} — above field avg`);
923
- else
935
+ aboveHcm++;
936
+ abovePower++;
937
+ } else if (val >= power) {
938
+ wins.push(`${label} ${fmt(val)} — ${paint(c.boldCyan, "power-user tier")}`);
939
+ aboveHcm++;
940
+ abovePower++;
941
+ } else if (val >= hcm) {
942
+ wins.push(`${label} ${fmt(val)} — above field, ${dim("approaching power-user")}`);
943
+ aboveHcm++;
944
+ } else {
924
945
  gaps.push({
925
946
  label,
926
- txt: `${label} ${fmt(val)} — below field (${fmt(fieldAvg)}): ${tip}`,
947
+ txt: `${label} ${fmt(val)} — below field (${fmt(hcm)}): ${tip}`,
927
948
  val,
928
- fieldAvg,
949
+ hcm,
929
950
  });
951
+ }
930
952
  };
931
953
  chk(
932
954
  "Υ Yield",
933
955
  you.yield,
934
- TD.field.yield,
956
+ TD.hcm.yield,
957
+ TD.power.yield,
935
958
  TD.top.yield,
936
959
  fmtY,
937
960
  "compound more — reuse cache, raise output",
@@ -939,7 +962,8 @@ function renderDashboard(data, status = "", scrollOffset = 0) {
939
962
  chk(
940
963
  "SNR",
941
964
  you.snr,
942
- TD.field.snr,
965
+ TD.hcm.snr,
966
+ TD.power.snr,
943
967
  TD.top.snr,
944
968
  fmtSNR,
945
969
  "tighten prompts — less input per unit output",
@@ -947,7 +971,8 @@ function renderDashboard(data, status = "", scrollOffset = 0) {
947
971
  chk(
948
972
  "Leverage",
949
973
  you.leverage,
950
- TD.field.lev,
974
+ TD.hcm.lev,
975
+ TD.power.lev,
951
976
  TD.top.lev,
952
977
  (v) => fmtLev(v) + "×",
953
978
  "lean on cache-read — amplify prior context",
@@ -955,55 +980,80 @@ function renderDashboard(data, status = "", scrollOffset = 0) {
955
980
  chk(
956
981
  "Velocity",
957
982
  you.velocity,
958
- TD.field.vel,
983
+ TD.hcm.vel,
984
+ TD.power.vel,
959
985
  TD.top.vel,
960
986
  (v) => v.toFixed(2),
961
987
  "more output per input token",
962
988
  );
963
- for (const w of wins.slice(0, 3)) emit(` ${paint(c.green, "▲")} ${w}`);
964
- // weakest gap gets the prescriptive tip
965
- const weakest = gaps.sort(
966
- (a, b) => a.val / a.fieldAvg - b.val / b.fieldAvg,
967
- )[0];
968
- if (weakest) emit(` ${paint(c.magenta, "▽")} ${dim(weakest.txt)}`);
969
- if (!wins.length && !weakest) emit(` ${dim(" building your read…")}`);
989
+ chk(
990
+ "10xDEV",
991
+ you.dev10x,
992
+ TD.hcm.d10,
993
+ TD.power.d10,
994
+ TD.top.d10,
995
+ (v) => v.toFixed(2),
996
+ "raise leverage — every 10× lev adds 1 to 10xDEV",
997
+ );
998
+ for (const w of wins.slice(0, 4)) emit(` ${paint(c.green, "▲")} ${w}`);
999
+ // all gaps shown; weakest gets the prescriptive tip
1000
+ const sortedGaps = gaps.sort(
1001
+ (a, b) => a.val / a.hcm - b.val / b.hcm,
1002
+ );
1003
+ for (let i = 0; i < sortedGaps.length; i++) {
1004
+ const g = sortedGaps[i];
1005
+ if (i === 0) emit(` ${paint(c.magenta, "▽")} ${dim(g.txt)}`);
1006
+ else emit(` ${paint(c.magenta, "▽")} ${dim(g.label + " below field")}`);
1007
+ }
1008
+ if (!wins.length && !sortedGaps.length) emit(` ${dim(" building your read…")}`);
1009
+ // summary line — where you sit across all metrics
1010
+ if (totalMetrics > 0) {
1011
+ const hcmStr = `${aboveHcm}/${totalMetrics}`;
1012
+ const pwrStr = `${abovePower}/${totalMetrics}`;
1013
+ emit(
1014
+ ` ${dim(`above HCM on ${bold(hcmStr)} · above Power on ${bold(pwrStr)}`)}`,
1015
+ );
1016
+ }
970
1017
  } else {
971
1018
  emit(` ${dim(" reading your cascade… (press [R] to refresh)")}`);
972
1019
  }
973
1020
 
974
- // ── Three Degrees — AI avg · SigRank avg · Top (live values from signalaf.com)
1021
+ // ── Four Degrees — AA baseline · Human Center of Mass · Power users · Top Evals
1022
+ // Matches signalaf.com/wiki/four-degrees (expanded from 3 → 4 degrees 2026-07-17).
975
1023
  emit();
976
1024
  emit(` ${hr()}`);
977
1025
  emit(
978
- ` ${bold("Three Degrees")} ${dim("AI avg · SigRank avg · top — signalaf.com/wiki")}`,
1026
+ ` ${bold("Four Degrees")} ${dim("AA baseline · Human Center of Mass · Power users · Top Evals — signalaf.com/wiki")}`,
979
1027
  );
980
1028
  const tdHead = [
981
1029
  padEnd(hdr("Metric"), 10),
982
- padStart(hdr("AI avg"), 9),
983
- padStart(hdr("SigRank avg"), 12),
1030
+ padStart(hdr("AA base"), 9),
1031
+ padStart(hdr("HCM"), 9),
1032
+ padStart(hdr("Power"), 9),
984
1033
  padStart(hdr("Top"), 9),
985
1034
  ];
986
1035
  emit(` ${tdHead.join(" ")}`);
987
1036
  const white = (s) => paint(c.white, s);
988
- const tdRow = (label, b, f, t, fmt) =>
1037
+ const tdRow = (label, b, h, p, t, fmt) =>
989
1038
  emit(
990
- ` ${padEnd(label, 10)} ${padStart(white(fmt(b)), 9)} ${padStart(fmt(f), 12)} ${padStart(gold(fmt(t)), 9)}`,
1039
+ ` ${padEnd(label, 10)} ${padStart(dim(fmt(b)), 9)} ${padStart(white(fmt(h)), 9)} ${padStart(white(fmt(p)), 9)} ${padStart(gold(fmt(t)), 9)}`,
991
1040
  );
992
- tdRow("Υ Yield", TD.base.yield, TD.field.yield, TD.top.yield, fmtY);
993
- tdRow("SNR", TD.base.snr, TD.field.snr, TD.top.snr, fmtSNR);
994
- tdRow("Velocity", TD.base.vel, TD.field.vel, TD.top.vel, (v) =>
1041
+ tdRow("Υ Yield", TD.base.yield, TD.hcm.yield, TD.power.yield, TD.top.yield, fmtY);
1042
+ tdRow("SNR", TD.base.snr, TD.hcm.snr, TD.power.snr, TD.top.snr, fmtSNR);
1043
+ tdRow("Velocity", TD.base.vel, TD.hcm.vel, TD.power.vel, TD.top.vel, (v) =>
995
1044
  v.toFixed(2),
996
1045
  );
997
1046
  tdRow(
998
1047
  "Leverage",
999
1048
  TD.base.lev,
1000
- TD.field.lev,
1049
+ TD.hcm.lev,
1050
+ TD.power.lev,
1001
1051
  TD.top.lev,
1002
1052
  (v) => fmtLev(v) + "×",
1003
1053
  );
1004
- tdRow("10xDEV", TD.base.d10, TD.field.d10, TD.top.d10, (v) => v.toFixed(2));
1054
+ tdRow("10xDEV", TD.base.d10, TD.hcm.d10, TD.power.d10, TD.top.d10, (v) => v.toFixed(2));
1005
1055
  emit(
1006
- ` ${dim("reference values until live user volume calibrates SigRank avg")}`,
1056
+ ` ${dim("reference values until live user volume calibrates HCM · Power · Top")}`,
1007
1057
  );
1008
1058
  }
1009
1059
 
@@ -1526,7 +1576,7 @@ function renderSubmissions(
1526
1576
  const cells = [
1527
1577
  padStart(rk, 4),
1528
1578
  nm,
1529
- padEnd(cyan(trunc(gPlat(e), 10)), 10),
1579
+ padEnd(platformColor(gPlat(e))(trunc(gPlat(e), 10)), 10),
1530
1580
  padEnd(dim(trunc(gWin(e), 6)), 6),
1531
1581
  yld,
1532
1582
  ...(showOpRatio ? [padEnd(dim(fmtOp(gOpRatio(e))), 18)] : []),
@@ -1717,6 +1767,7 @@ function renderWatchInfo(platform, win, refresh) {
1717
1767
  const winLabel = !win || win === "all-windows" ? "all windows" : win;
1718
1768
  const allDefault =
1719
1769
  platLabel === "all active platforms" && winLabel === "all windows";
1770
+ const platColorFn = (!platform || platform === "all") ? cyan : platformColor(platform);
1720
1771
  writeln();
1721
1772
  writeln(
1722
1773
  ` ${bold("Live Watch")} ${dim("the agent that keeps your rank current")}`,
@@ -1724,7 +1775,7 @@ function renderWatchInfo(platform, win, refresh) {
1724
1775
  writeln();
1725
1776
  writeln(` ${dim("What it does")}`);
1726
1777
  writeln(
1727
- ` Re-reads your local token logs for ${cyan(platLabel)} across ${cyan(winLabel)} every`,
1778
+ ` Re-reads your local token logs for ${platColorFn(platLabel)} across ${cyan(winLabel)} every`,
1728
1779
  );
1729
1780
  writeln(
1730
1781
  ` ${gold(refresh + "s")} and recomputes each cascade (Υ Yield · SNR · Leverage · class) live, on`,
@@ -1746,7 +1797,7 @@ function renderWatchInfo(platform, win, refresh) {
1746
1797
  writeln();
1747
1798
  writeln(` ${dim("Settings")}`);
1748
1799
  writeln(
1749
- ` ${dim("Platform")} ${cyan(platLabel)} ${dim("Window")} ${cyan(winLabel)} ${dim("Refresh")} ${gold(refresh + "s")}`,
1800
+ ` ${dim("Platform")} ${platColorFn(platLabel)} ${dim("Window")} ${cyan(winLabel)} ${dim("Refresh")} ${gold(refresh + "s")}`,
1750
1801
  );
1751
1802
  writeln(
1752
1803
  ` ${dim(allDefault ? "watching everything (default)" : "focused — press [P]/[W] to widen back to all")}`,