9d21dfa1ad310b2c65a65d22a5f578c92d8ea9c0 diff --git a/src/server.ts b/src/server.ts index 870fbaa03131ca5ed4e071121babd0b9a2865953..e841bb322c7045de900b0a6b254fbf6858e715f9 100644 --- a/src/server.ts +++ b/src/server.ts @@ -341,10 +341,57 @@ const server = Bun.serve({ "/agents": htmlResponse(AGENTS_INDEX_HTML), "/agents/register": htmlResponse(REGISTER_HTML), "/agents/:name": async (req) => { + const name = req.params.name; + const userRes = await fetch(`${FORGEJO_INTERNAL}/api/v1/users/${encodeURIComponent(name)}`); + if (userRes.status === 404) { + const html = await renderPage({ + title: `${name} — agents — tdd.md`, + bodyMarkdown: `# agents / ${name}\n\n> No agent registered with this name.\n\n[← all agents](/agents) · [register your own →](/agents/register)`, + ogPath: `https://tdd.md/agents/${name}`, + active: "agents", + }); + return htmlResponse(html, 404); + } + const reposRes = await fetch(`${FORGEJO_INTERNAL}/api/v1/users/${encodeURIComponent(name)}/repos?limit=50`); + const repos = reposRes.ok ? ((await reposRes.json()) as { name: string; description: string }[]) : []; + + let body = `# agents / ${name}\n\n`; + if (repos.length === 0) { + body += "> Registered, but no kata attempts yet.\n\n[← all agents](/agents)"; + } else { + const progressByRepo = await Promise.all( + repos.map(async (r) => { + const cRes = await fetch(`${FORGEJO_INTERNAL}/api/v1/repos/${encodeURIComponent(name)}/${encodeURIComponent(r.name)}/commits?limit=50&stat=false`); + const commits = cRes.ok ? ((await cRes.json()) as { commit: { message: string } }[]) : []; + return { repo: r, progress: computeProgress(commits) }; + }), + ); + + const totals: Record = {}; + for (const r of repos) { + try { + const game = await loadGame(r.name); + totals[r.name] = game.steps.length; + } catch { + // unknown kata, no total + } + } + + body += "## attempts\n\n"; + body += "| kata | verified | phases |\n|---|---|---|\n"; + for (const { repo: r, progress } of progressByRepo) { + const total = totals[r.name]; + const verified = progress.verifiedSteps.size; + const counter = total !== undefined ? `${verified} / ${total}` : `${verified} / ?`; + const phases = `red ${progress.redCount} · green ${progress.greenCount} · refactor ${progress.refactorCount}`; + body += `| [${r.name}](/${name}/${r.name}) | ${counter} | ${phases} |\n`; + } + } + const html = await renderPage({ - title: `${req.params.name} — agents — tdd.md`, - bodyMarkdown: `# agents / ${req.params.name}\n\n> Not yet registered or no attempts.\n\nWhen this agent submits a run, their commits and verdicts will appear here.`, - ogPath: `https://tdd.md/agents/${req.params.name}`, + title: `${name} — agents — tdd.md`, + bodyMarkdown: body, + ogPath: `https://tdd.md/agents/${name}`, active: "agents", }); return htmlResponse(html);