sigrank 0.18.2 → 0.18.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/package.json +1 -1
- package/tools.mjs +220 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigrank",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.4",
|
|
4
4
|
"author": "SunrisesIllNeverSee (https://github.com/SunrisesIllNeverSee)",
|
|
5
5
|
"description": "SigRank MCP server — the yield cascade + live leaderboard as MCP tools any agent can call",
|
|
6
6
|
"mcpName": "io.github.SunrisesIllNeverSee/sigrank-mcp",
|
package/tools.mjs
CHANGED
|
@@ -1362,6 +1362,36 @@ export const TOOLS = [
|
|
|
1362
1362
|
},
|
|
1363
1363
|
},
|
|
1364
1364
|
},
|
|
1365
|
+
{
|
|
1366
|
+
name: "tokscale_breakdown",
|
|
1367
|
+
description:
|
|
1368
|
+
"Show a per-model breakdown of your token usage across all platforms detected by tokscale. Models under the threshold (default 1%) are lumped into 'other' to keep the display clean. Useful for seeing which models you actually use per platform (e.g. claude-opus-4-8 76%, claude-sonnet-4-6 11%, other 0.3%). Returns { platform: [{ model, input, output, cacheRead, cacheCreate, pct }] }.",
|
|
1369
|
+
annotations: {
|
|
1370
|
+
title: "Model breakdown",
|
|
1371
|
+
...ANNOTATIONS.readOnlyHint,
|
|
1372
|
+
...ANNOTATIONS.openWorldHint,
|
|
1373
|
+
},
|
|
1374
|
+
inputSchema: {
|
|
1375
|
+
type: "object",
|
|
1376
|
+
properties: {
|
|
1377
|
+
threshold: {
|
|
1378
|
+
type: "number",
|
|
1379
|
+
description:
|
|
1380
|
+
"Models below this fraction of their platform's total tokens are lumped into 'other'. Default 0.01 (1%). Set 0 to see every model.",
|
|
1381
|
+
default: 0.01,
|
|
1382
|
+
},
|
|
1383
|
+
},
|
|
1384
|
+
},
|
|
1385
|
+
outputSchema: {
|
|
1386
|
+
type: "object",
|
|
1387
|
+
properties: {
|
|
1388
|
+
platforms: {
|
|
1389
|
+
type: "object",
|
|
1390
|
+
description: "Map of platform name → array of { model, input, output, cacheRead, cacheCreate, pct }",
|
|
1391
|
+
},
|
|
1392
|
+
},
|
|
1393
|
+
},
|
|
1394
|
+
},
|
|
1365
1395
|
];
|
|
1366
1396
|
|
|
1367
1397
|
// tokenpull window key → the board's window_type enum.
|
|
@@ -1383,9 +1413,191 @@ const _lastWatchSubmitAt = new Map();
|
|
|
1383
1413
|
// keeps only platforms with real local data, and sorts claude → codex → rest.
|
|
1384
1414
|
// Pass `platforms` to scope it (e.g. ['claude'] for a fast first paint).
|
|
1385
1415
|
export async function pullActivePlatforms({ platforms } = {}, opts = {}) {
|
|
1386
|
-
|
|
1416
|
+
// If the caller specified explicit platforms, use those.
|
|
1417
|
+
if (platforms && platforms.length) {
|
|
1418
|
+
return _pullExplicit(platforms, opts);
|
|
1419
|
+
}
|
|
1420
|
+
// Auto-detect: run tokscale once to discover all active clients, then pull
|
|
1421
|
+
// only those. This is much faster than trying all 17 adapters (most fail
|
|
1422
|
+
// silently on a machine that doesn't have that tool installed).
|
|
1423
|
+
//
|
|
1424
|
+
// Flow: tokscale surfaces ALL clients → map to our platform names →
|
|
1425
|
+
// ccusage is primary for Claude (more accurate) → other platforms use
|
|
1426
|
+
// their adapters. Anything in tokscale NOT in ccusage gets included.
|
|
1427
|
+
const detected = await _tokscaleDetectClients().catch(() => null);
|
|
1428
|
+
if (detected && detected.length > 0) {
|
|
1429
|
+
return _pullExplicit(detected, opts);
|
|
1430
|
+
}
|
|
1431
|
+
// Fallback: tokscale not installed or failed → try all adapters (old behavior)
|
|
1432
|
+
return _pullExplicit(ALL_PLATFORMS, opts);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
/** Run `tokscale models --json` and return the list of our platform names. */
|
|
1436
|
+
async function _tokscaleDetectClients() {
|
|
1437
|
+
const raw = await execFileAsync("tokscale", ["models", "--json"], 60000);
|
|
1438
|
+
const data = JSON.parse(raw);
|
|
1439
|
+
const entries = Array.isArray(data?.entries)
|
|
1440
|
+
? data.entries
|
|
1441
|
+
: Array.isArray(data)
|
|
1442
|
+
? data
|
|
1443
|
+
: [];
|
|
1444
|
+
if (!entries.length) return [];
|
|
1445
|
+
// Complete tokscale client → our platform name mapping.
|
|
1446
|
+
// Tokscale supports 40 clients (as of v3.1.0). We have native adapters for 16;
|
|
1447
|
+
// the rest map to "other" (user-supplied JSON via SIGRANK_OTHER_PATH).
|
|
1448
|
+
// Source: https://github.com/junhoyeo/tokscale
|
|
1449
|
+
const TOKSCALE_CLIENT_MAP = {
|
|
1450
|
+
// Native adapters
|
|
1451
|
+
claude: "claude",
|
|
1452
|
+
codex: "codex",
|
|
1453
|
+
"devin-cli": "devin",
|
|
1454
|
+
"devin-desktop": "devin",
|
|
1455
|
+
gemini: "gemini",
|
|
1456
|
+
amp: "amp",
|
|
1457
|
+
kimi: "kimi",
|
|
1458
|
+
qwen: "qwen",
|
|
1459
|
+
goose: "goose",
|
|
1460
|
+
kilo: "kilo",
|
|
1461
|
+
kilocode: "kilo",
|
|
1462
|
+
hermes: "hermes",
|
|
1463
|
+
droid: "droid",
|
|
1464
|
+
codebuff: "codebuff",
|
|
1465
|
+
copilot: "copilot",
|
|
1466
|
+
opencode: "opencode",
|
|
1467
|
+
openclaw: "openclaw",
|
|
1468
|
+
pi: "pi",
|
|
1469
|
+
// No native adapter yet → "other"
|
|
1470
|
+
cursor: "other",
|
|
1471
|
+
roocode: "other",
|
|
1472
|
+
mux: "other",
|
|
1473
|
+
crush: "other",
|
|
1474
|
+
antigravity: "other",
|
|
1475
|
+
"antigravity-cli": "other",
|
|
1476
|
+
zed: "other",
|
|
1477
|
+
kiro: "other",
|
|
1478
|
+
trae: "other",
|
|
1479
|
+
warp: "other",
|
|
1480
|
+
cline: "other",
|
|
1481
|
+
gjc: "other",
|
|
1482
|
+
grok: "other",
|
|
1483
|
+
jcode: "other",
|
|
1484
|
+
commandcode: "other",
|
|
1485
|
+
micode: "other",
|
|
1486
|
+
junie: "other",
|
|
1487
|
+
zcode: "other",
|
|
1488
|
+
opencodereview: "other",
|
|
1489
|
+
codebuddy: "other",
|
|
1490
|
+
workbuddy: "other",
|
|
1491
|
+
// synthetic = re-attributed from other sources, skip
|
|
1492
|
+
synthetic: null,
|
|
1493
|
+
};
|
|
1494
|
+
const clients = new Set();
|
|
1495
|
+
for (const e of entries) {
|
|
1496
|
+
if (!e || !e.client) continue;
|
|
1497
|
+
if (e.model === "<synthetic>" || e.model === "unknown") continue;
|
|
1498
|
+
const input = Number(e.input) || 0;
|
|
1499
|
+
const output = Number(e.output) || 0;
|
|
1500
|
+
if (input + output === 0) continue;
|
|
1501
|
+
const platform = TOKSCALE_CLIENT_MAP[e.client];
|
|
1502
|
+
if (platform === null) continue; // synthetic → skip
|
|
1503
|
+
clients.add(platform || "other");
|
|
1504
|
+
}
|
|
1505
|
+
return [...clients];
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
/**
|
|
1509
|
+
* Run `tokscale models --json` and return a model-level breakdown per platform.
|
|
1510
|
+
* Models under `threshold` (default 1%) of their platform's total are lumped
|
|
1511
|
+
* into "other" to keep the display clean.
|
|
1512
|
+
*
|
|
1513
|
+
* Returns: { platform: [{ model, input, output, cacheRead, cacheCreate, pct }] }
|
|
1514
|
+
*/
|
|
1515
|
+
export async function tokscaleModelBreakdown(threshold = 0.01) {
|
|
1516
|
+
const raw = await execFileAsync("tokscale", ["models", "--json"], 60000);
|
|
1517
|
+
const data = JSON.parse(raw);
|
|
1518
|
+
const entries = Array.isArray(data?.entries)
|
|
1519
|
+
? data.entries
|
|
1520
|
+
: Array.isArray(data)
|
|
1521
|
+
? data
|
|
1522
|
+
: [];
|
|
1523
|
+
if (!entries.length) return {};
|
|
1524
|
+
|
|
1525
|
+
const TOKSCALE_CLIENT_MAP = {
|
|
1526
|
+
claude: "claude", codex: "codex", "devin-cli": "devin",
|
|
1527
|
+
"devin-desktop": "devin", gemini: "gemini", amp: "amp",
|
|
1528
|
+
kimi: "kimi", qwen: "qwen", goose: "goose", kilo: "kilo",
|
|
1529
|
+
kilocode: "kilo", hermes: "hermes", droid: "droid",
|
|
1530
|
+
codebuff: "codebuff", copilot: "copilot", opencode: "opencode",
|
|
1531
|
+
openclaw: "openclaw", pi: "pi",
|
|
1532
|
+
cursor: "other", roocode: "other", mux: "other", crush: "other",
|
|
1533
|
+
antigravity: "other", "antigravity-cli": "other", zed: "other",
|
|
1534
|
+
kiro: "other", trae: "other", warp: "other", cline: "other",
|
|
1535
|
+
gjc: "other", grok: "other", jcode: "other", commandcode: "other",
|
|
1536
|
+
micode: "other", junie: "other", zcode: "other",
|
|
1537
|
+
opencodereview: "other", codebuddy: "other", workbuddy: "other",
|
|
1538
|
+
synthetic: null,
|
|
1539
|
+
};
|
|
1540
|
+
|
|
1541
|
+
// Group entries by our platform name
|
|
1542
|
+
const byPlatform = {};
|
|
1543
|
+
for (const e of entries) {
|
|
1544
|
+
if (!e || !e.client) continue;
|
|
1545
|
+
if (e.model === "<synthetic>" || e.model === "unknown") continue;
|
|
1546
|
+
const input = Number(e.input) || 0;
|
|
1547
|
+
const output = Number(e.output) || 0;
|
|
1548
|
+
if (input + output === 0) continue;
|
|
1549
|
+
const platform = TOKSCALE_CLIENT_MAP[e.client];
|
|
1550
|
+
if (platform === null) continue;
|
|
1551
|
+
const p = platform || "other";
|
|
1552
|
+
if (!byPlatform[p]) byPlatform[p] = [];
|
|
1553
|
+
byPlatform[p].push({
|
|
1554
|
+
model: e.model,
|
|
1555
|
+
input,
|
|
1556
|
+
output,
|
|
1557
|
+
cacheRead: Number(e.cacheRead) || 0,
|
|
1558
|
+
cacheCreate: Number(e.cacheWrite) || 0,
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// For each platform: sort by total tokens desc, lump small models into "other"
|
|
1563
|
+
const result = {};
|
|
1564
|
+
for (const [platform, models] of Object.entries(byPlatform)) {
|
|
1565
|
+
const total = models.reduce(
|
|
1566
|
+
(s, m) => s + m.input + m.output + m.cacheRead + m.cacheCreate,
|
|
1567
|
+
0,
|
|
1568
|
+
);
|
|
1569
|
+
if (total === 0) continue;
|
|
1570
|
+
const sorted = models.sort(
|
|
1571
|
+
(a, b) =>
|
|
1572
|
+
b.input + b.output + b.cacheRead + b.cacheCreate -
|
|
1573
|
+
(a.input + a.output + a.cacheRead + a.cacheCreate),
|
|
1574
|
+
);
|
|
1575
|
+
const big = [];
|
|
1576
|
+
const small = { model: "other", input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
|
|
1577
|
+
for (const m of sorted) {
|
|
1578
|
+
const t = m.input + m.output + m.cacheRead + m.cacheCreate;
|
|
1579
|
+
if (t / total < threshold) {
|
|
1580
|
+
small.input += m.input;
|
|
1581
|
+
small.output += m.output;
|
|
1582
|
+
small.cacheRead += m.cacheRead;
|
|
1583
|
+
small.cacheCreate += m.cacheCreate;
|
|
1584
|
+
} else {
|
|
1585
|
+
big.push({ ...m, pct: t / total });
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
if (small.input + small.output + small.cacheRead + small.cacheCreate > 0) {
|
|
1589
|
+
const t = small.input + small.output + small.cacheRead + small.cacheCreate;
|
|
1590
|
+
big.push({ ...small, pct: t / total });
|
|
1591
|
+
}
|
|
1592
|
+
result[platform] = big;
|
|
1593
|
+
}
|
|
1594
|
+
return result;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/** Pull a specific set of platforms in parallel, filter to active ones. */
|
|
1598
|
+
async function _pullExplicit(platforms, opts = {}) {
|
|
1387
1599
|
const settled = await Promise.allSettled(
|
|
1388
|
-
|
|
1600
|
+
platforms.map((p) => callTool("tokenpull", { platform: p }, opts)),
|
|
1389
1601
|
);
|
|
1390
1602
|
const active = settled
|
|
1391
1603
|
.filter((r) => r.status === "fulfilled" && r.value)
|
|
@@ -3290,6 +3502,12 @@ export async function callTool(name, args, opts = {}) {
|
|
|
3290
3502
|
};
|
|
3291
3503
|
}
|
|
3292
3504
|
|
|
3505
|
+
if (name === "tokscale_breakdown") {
|
|
3506
|
+
const threshold = Number(args?.threshold ?? 0.01);
|
|
3507
|
+
const platforms = await tokscaleModelBreakdown(threshold);
|
|
3508
|
+
return { platforms };
|
|
3509
|
+
}
|
|
3510
|
+
|
|
3293
3511
|
throw new Error(`Unknown tool: ${name}`);
|
|
3294
3512
|
}
|
|
3295
3513
|
|