Skip to content

Instantly share code, notes, and snippets.

@thoroc
Created March 18, 2026 14:23
Show Gist options
  • Select an option

  • Save thoroc/1bd11428d8b536d6420c361d1016db77 to your computer and use it in GitHub Desktop.

Select an option

Save thoroc/1bd11428d8b536d6420c361d1016db77 to your computer and use it in GitHub Desktop.
Agentic harness historic ranking - based on github stars
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Agent Star History</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #0f1117; color: #e0e0e0; padding: 24px; }
h1 { font-size: 1.5rem; margin-bottom: 4px; }
.subtitle { color: #888; font-size: 0.85rem; margin-bottom: 32px; }
.subtitle a { color: #4a9eff; }
.chart-wrap { background: #1a1d27; border-radius: 12px; padding: 24px; margin-bottom: 32px; }
h2 { font-size: 1rem; margin-bottom: 16px; color: #ccc; }
canvas { max-height: 480px; }
</style>
</head>
<body>
<h1>Agent Star History</h1>
<p class="subtitle">Generated 2026-03-18 · Data from <a href="https://ossinsight.io" target="_blank">OSSInsight</a> · Monthly cumulative totals</p>
<div class="chart-wrap">
<h2>Stars over time (log scale)</h2>
<canvas id="stars"></canvas>
</div>
<div class="chart-wrap">
<h2>Ranking over time (rank 1 = most stars)</h2>
<canvas id="ranking"></canvas>
</div>
<script>
const labels = ["2021-07-01","2021-08-01","2021-09-01","2021-10-01","2021-11-01","2021-12-01","2022-01-01","2022-02-01","2022-03-01","2022-04-01","2022-05-01","2022-06-01","2022-07-01","2022-08-01","2022-09-01","2022-10-01","2022-11-01","2022-12-01","2023-01-01","2023-02-01","2023-03-01","2023-04-01","2023-05-01","2023-06-01","2023-07-01","2023-08-01","2023-09-01","2023-10-01","2023-11-01","2023-12-01","2024-01-01","2024-02-01","2024-03-01","2024-04-01","2024-05-01","2024-06-01","2024-07-01","2024-08-01","2024-09-01","2024-10-01","2024-11-01","2024-12-01","2025-01-01","2025-02-01","2025-03-01","2025-04-01","2025-05-01","2025-06-01","2025-07-01","2025-08-01","2025-09-01","2025-10-01","2025-11-01","2025-12-01","2026-01-01","2026-02-01","2026-03-01"];
const rawStars = {"Claude Code":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,4381,6123,7162,9076,12572,18847,21762,23341,24710,25939,28579,33319,36106,37834],"Codex":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,18368,24210,25821,26738,30285,34508,35951,36853,38378,39708,41072,41871],"Continue":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,73,1680,3405,3915,4380,4913,5373,6060,7385,9113,10330,11389,12180,13090,14222,15848,17692,18590,19655,21138,22776,23831,24680,25255,25793,26208,26652,26951,27126,27359,27618,27892,28039,28122],"Crush":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1584,6655,7707,8105,8512,9007,10013,10443,10680],"Deep Agents":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,261,1890,2270,2567,3205,3662,4052,4340,5295],"Gemini CLI":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,26688,38724,42960,45647,46924,48687,50497,52074,53038,53598],"Goose":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,18,65,123,215,300,4169,8263,10107,11076,11958,13152,14609,15510,15955,16442,16948,18046,19621,20212,20637],"iFlow":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,38,634,1161,1393,1603,1852,2036,2147,2207],"KiloCode":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,94,185,509,1119,3788,4558,6364,6798,7197,7548,7989,8397,8556],"MCPJam":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,21,139,347,505,620,699,748,773,813,849,862],"OpenCode":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,574,4739,9476,12893,15189,16693,18562,22907,40843,46693,49252],"OpenClaw":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,90,381,42868,76894,96223],"Pi":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1,null,3,110,351,1452,5729,7321],"Qwen":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,3782,6805,7851,8276,8716,9132,9578,10106,10354],"Trae":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,25,5155,5535,5777,5867,5986,6099,6228,6292,6337],"Warp":[271,324,372,409,515,623,894,1148,1295,4992,5854,6336,6877,7281,7731,8230,9531,10153,10666,11323,12219,12901,13471,14005,14430,14857,15178,15491,16006,16462,16915,17721,18359,18811,19228,20171,20505,20859,21181,21449,21694,22002,22232,22563,23158,23529,23778,24001,24199,24426,24680,24780,24882,24988,25095,25185,25213],"Zed":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,16683,25365,28329,31336,33834,37813,41495,44662,46501,48041,49833,51204,52884,54194,55713,57038,59882,60763,61571,62638,63646,64566,65344,66101,66909,67472,67746]};
const agentNames = ["Claude Code","Codex","Continue","Crush","Deep Agents","Gemini CLI","Goose","iFlow","KiloCode","MCPJam","OpenCode","OpenClaw","Pi","Qwen","Trae","Warp","Zed"];
const palette = ["#e6194b","#3cb44b","#4363d8","#f58231","#911eb4","#42d4f4","#f032e6","#bfef45","#fabed4","#469990","#dcbeff","#9a6324","#fffac8","#800000","#aaffc3","#808000","#ffd8b1"];
// hidden[i] = true when agent i is toggled off
const hidden = new Array(agentNames.length).fill(false);
function dsBase(i) {
return {
label: agentNames[i],
borderColor: palette[i],
backgroundColor: palette[i] + '22',
tension: 0.3,
pointRadius: 3,
};
}
// Recompute rank data for agent[i] given current hidden state
function computeRankDatasets() {
return agentNames.map((name, i) => ({
...dsBase(i),
spanGaps: false,
data: labels.map((_, dateIdx) => {
if (hidden[i]) return null;
const snapshot = agentNames
.filter((_, j) => !hidden[j])
.map(n => ({ n, stars: rawStars[n][dateIdx] }))
.filter(s => s.stars !== null)
.sort((a, b) => b.stars - a.stars);
const rank = snapshot.findIndex(s => s.n === name) + 1;
return rank === 0 ? null : rank;
}),
}));
}
// Toggle agent by dataset index, sync both charts, recompute rankings
function toggleAgent(idx) {
hidden[idx] = !hidden[idx];
// Sync star chart visibility
const sMeta = starChart.getDatasetMeta(idx);
sMeta.hidden = hidden[idx];
starChart.update('none');
// Recompute and apply ranking data
const newRank = computeRankDatasets();
rankChart.data.datasets.forEach((ds, i) => {
ds.data = newRank[i].data;
rankChart.getDatasetMeta(i).hidden = hidden[i];
});
const visibleCount = hidden.filter(h => !h).length;
rankChart.options.scales.y.max = visibleCount || 1;
rankChart.update();
}
const legendClick = (_e, item) => toggleAgent(item.datasetIndex);
const legendLabels = { color: '#ccc', boxWidth: 12, padding: 10 };
const xScale = { ticks: { color: '#888', maxTicksLimit: 14 }, grid: { color: '#333' } };
const starChart = new Chart(document.getElementById('stars'), {
type: 'line',
data: {
labels,
datasets: agentNames.map((name, i) => ({
...dsBase(i),
spanGaps: true,
data: rawStars[name],
})),
},
options: {
responsive: true,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { labels: legendLabels, onClick: legendClick },
tooltip: {
callbacks: {
label: ctx => ctx.parsed.y !== null
? ` ${ctx.dataset.label}: ${ctx.parsed.y.toLocaleString()} ⭐`
: null,
},
},
},
scales: {
x: xScale,
y: {
type: 'logarithmic',
ticks: { color: '#888', callback: v => v >= 1000 ? (v / 1000).toFixed(0) + 'k' : v },
grid: { color: '#333' },
},
},
},
});
const rankChart = new Chart(document.getElementById('ranking'), {
type: 'line',
data: { labels, datasets: computeRankDatasets() },
options: {
responsive: true,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { labels: legendLabels, onClick: legendClick },
tooltip: {
callbacks: {
label: ctx => ctx.parsed.y !== null
? ` ${ctx.dataset.label}: rank #${ctx.parsed.y}`
: null,
},
},
},
scales: {
x: xScale,
y: {
reverse: true,
min: 1,
max: 17,
ticks: { color: '#888', stepSize: 1, callback: v => '#' + v },
grid: { color: '#333' },
},
},
},
});
</script>
</body>
</html>
#!/usr/bin/env bun
/**
* Fetch historical star data from OSSInsight for all open-source agents
* and generate a self-contained HTML file with two charts:
* 1. Stars over time (log scale line chart)
* 2. Ranking over time (bump chart — rank 1 at top)
*
* Data source: https://ossinsight.io (no auth required, rate limits apply)
*
* Usage:
* bun scripts/star-history.ts
* bun scripts/star-history.ts --output star-history.html
*/
// Open-source repos from agent-stars.ts AGENTS list
const OPEN_REPOS: Record<string, string> = {
"Claude Code": "anthropics/claude-code",
Cline: "cline/cline",
Codex: "openai/codex",
Continue: "continuedev/continue",
Crush: "charmbracelet/crush",
"Deep Agents": "langchain-ai/deepagents",
"Gemini CLI": "google-gemini/gemini-cli",
Goose: "block/goose",
iFlow: "iflow-ai/iflow-cli",
KiloCode: "Kilo-Org/kilocode",
Kode: "shareAI-lab/Kode-Agent",
MCPJam: "MCPJam/inspector",
OpenCode: "anomalyco/opencode",
OpenClaw: "openclaw/openclaw",
OpenHands: "All-Hands-AI/OpenHands",
Pi: "badlogic/pi-mono",
Qwen: "QwenLM/qwen-code",
Roo: "RooVetGit/Roo-Code",
Trae: "bytedance/trae-agent",
Warp: "warpdotdev/Warp",
Zed: "zed-industries/zed",
};
// 21-color palette (ColorBrewer + custom, perceptually distinct)
const PALETTE = [
"#e6194b", "#3cb44b", "#4363d8", "#f58231", "#911eb4",
"#42d4f4", "#f032e6", "#bfef45", "#fabed4", "#469990",
"#dcbeff", "#9a6324", "#fffac8", "#800000", "#aaffc3",
"#808000", "#ffd8b1", "#000075", "#a9a9a9", "#ffffff",
"#000000",
];
interface MonthlyPoint {
date: string; // "YYYY-MM-DD"
stars: number;
}
interface AgentSeries {
agent: string;
repo: string;
history: MonthlyPoint[];
}
type OssInsightResponse = {
data: { rows: Array<{ date: string; stargazers: string }> };
};
async function fetchHistory(repo: string): Promise<MonthlyPoint[] | null> {
const url = `https://api.ossinsight.io/v1/repos/${repo}/stargazers/history`;
const res = await fetch(url, {
headers: { "User-Agent": "star-history-script" },
});
if (res.status === 404) return null;
if (!res.ok) {
console.error(` HTTP ${res.status} for ${repo}`);
return null;
}
const json = (await res.json()) as OssInsightResponse;
const rows = json?.data?.rows;
if (!Array.isArray(rows) || rows.length === 0) return null;
return rows.map((r) => ({ date: r.date.slice(0, 10), stars: Number(r.stargazers) }));
}
function buildHtml(series: AgentSeries[]): string {
const allDates = [
...new Set(series.flatMap((s) => s.history.map((p) => p.date))),
].sort();
// Raw stars per agent per date slot — rankings computed live in JS
const rawStars: Record<string, (number | null)[]> = {};
for (const s of series) {
rawStars[s.agent] = allDates.map((d) => {
const p = s.history.find((h) => h.date === d);
return p ? p.stars : null;
});
}
const agentNames = series.map((s) => s.agent);
const palette = series.map((_, i) => PALETTE[i % PALETTE.length]);
const generated = new Date().toISOString().slice(0, 10);
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Agent Star History</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #0f1117; color: #e0e0e0; padding: 24px; }
h1 { font-size: 1.5rem; margin-bottom: 4px; }
.subtitle { color: #888; font-size: 0.85rem; margin-bottom: 32px; }
.subtitle a { color: #4a9eff; }
.chart-wrap { background: #1a1d27; border-radius: 12px; padding: 24px; margin-bottom: 32px; }
h2 { font-size: 1rem; margin-bottom: 16px; color: #ccc; }
canvas { max-height: 480px; }
</style>
</head>
<body>
<h1>Agent Star History</h1>
<p class="subtitle">Generated ${generated} · Data from <a href="https://ossinsight.io" target="_blank">OSSInsight</a> · Monthly cumulative totals</p>
<div class="chart-wrap">
<h2>Stars over time (log scale)</h2>
<canvas id="stars"></canvas>
</div>
<div class="chart-wrap">
<h2>Ranking over time (rank 1 = most stars)</h2>
<canvas id="ranking"></canvas>
</div>
<script>
const labels = ${JSON.stringify(allDates)};
const rawStars = ${JSON.stringify(rawStars)};
const agentNames = ${JSON.stringify(agentNames)};
const palette = ${JSON.stringify(palette)};
// hidden[i] = true when agent i is toggled off
const hidden = new Array(agentNames.length).fill(false);
function dsBase(i) {
return {
label: agentNames[i],
borderColor: palette[i],
backgroundColor: palette[i] + '22',
tension: 0.3,
pointRadius: 3,
};
}
// Recompute rank data for agent[i] given current hidden state
function computeRankDatasets() {
return agentNames.map((name, i) => ({
...dsBase(i),
spanGaps: false,
data: labels.map((_, dateIdx) => {
if (hidden[i]) return null;
const snapshot = agentNames
.filter((_, j) => !hidden[j])
.map(n => ({ n, stars: rawStars[n][dateIdx] }))
.filter(s => s.stars !== null)
.sort((a, b) => b.stars - a.stars);
const rank = snapshot.findIndex(s => s.n === name) + 1;
return rank === 0 ? null : rank;
}),
}));
}
// Toggle agent by dataset index, sync both charts, recompute rankings
function toggleAgent(idx) {
hidden[idx] = !hidden[idx];
// Sync star chart visibility
const sMeta = starChart.getDatasetMeta(idx);
sMeta.hidden = hidden[idx];
starChart.update('none');
// Recompute and apply ranking data
const newRank = computeRankDatasets();
rankChart.data.datasets.forEach((ds, i) => {
ds.data = newRank[i].data;
rankChart.getDatasetMeta(i).hidden = hidden[i];
});
const visibleCount = hidden.filter(h => !h).length;
rankChart.options.scales.y.max = visibleCount || 1;
rankChart.update();
}
const legendClick = (_e, item) => toggleAgent(item.datasetIndex);
const legendLabels = { color: '#ccc', boxWidth: 12, padding: 10 };
const xScale = { ticks: { color: '#888', maxTicksLimit: 14 }, grid: { color: '#333' } };
const starChart = new Chart(document.getElementById('stars'), {
type: 'line',
data: {
labels,
datasets: agentNames.map((name, i) => ({
...dsBase(i),
spanGaps: true,
data: rawStars[name],
})),
},
options: {
responsive: true,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { labels: legendLabels, onClick: legendClick },
tooltip: {
callbacks: {
label: ctx => ctx.parsed.y !== null
? \` \${ctx.dataset.label}: \${ctx.parsed.y.toLocaleString()} ⭐\`
: null,
},
},
},
scales: {
x: xScale,
y: {
type: 'logarithmic',
ticks: { color: '#888', callback: v => v >= 1000 ? (v / 1000).toFixed(0) + 'k' : v },
grid: { color: '#333' },
},
},
},
});
const rankChart = new Chart(document.getElementById('ranking'), {
type: 'line',
data: { labels, datasets: computeRankDatasets() },
options: {
responsive: true,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { labels: legendLabels, onClick: legendClick },
tooltip: {
callbacks: {
label: ctx => ctx.parsed.y !== null
? \` \${ctx.dataset.label}: rank #\${ctx.parsed.y}\`
: null,
},
},
},
scales: {
x: xScale,
y: {
reverse: true,
min: 1,
max: ${series.length},
ticks: { color: '#888', stepSize: 1, callback: v => '#' + v },
grid: { color: '#333' },
},
},
},
});
</script>
</body>
</html>`;
}
async function main(): Promise<void> {
const outputFlagIdx = process.argv.indexOf("--output");
const outputPath: string =
outputFlagIdx !== -1
? (process.argv[outputFlagIdx + 1] ?? "star-history.html")
: "star-history.html";
console.log(`Fetching history for ${Object.keys(OPEN_REPOS).length} repos from OSSInsight...\n`);
const results = await Promise.all(
Object.entries(OPEN_REPOS).map(async ([agent, repo]) => {
process.stdout.write(` ${agent.padEnd(20)} `);
const history = await fetchHistory(repo);
if (!history) {
console.log("no data");
return null;
}
console.log(`${history.length} months (latest: ${history.at(-1)?.stars.toLocaleString()} ⭐)`);
return { agent, repo, history } satisfies AgentSeries;
}),
);
const series = results.filter((r): r is AgentSeries => r !== null);
console.log(`\n${series.length} / ${Object.keys(OPEN_REPOS).length} repos have data.`);
const html = buildHtml(series);
await Bun.write(outputPath, html);
console.log(`\nChart written to ${outputPath}`);
console.log(`Open with: open ${outputPath}`);
}
main().catch((err: unknown) => {
console.error(err);
process.exit(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment