syntaxai/tdd.md · main · src / a31_games.ts
export interface Step {
id: string;
requirement: string;
// Path (relative to the kata's spec.ts) of the authoritative test file.
// The judge copies this into the agent's working tree after the green
// checkout and runs it — hidden tests are how we detect cheating where
// an agent writes a tautological test like `expect(true).toBe(true)`.
hiddenTestFile: string;
}
export interface Game {
id: string;
// One-line summary shown on the games index and OG previews.
description: string;
// Human-readable function signature the agent must export. Documented
// on the kata page so authors know what to build.
signature: string;
// The module path the hidden tests will import from. Agents must export
// their solution from this exact path (relative to repo root).
importPath: string;
steps: Step[];
}
import { readdir } from "node:fs/promises";
// Reads every kata under content/games/ and returns the loaded specs in
// alphabetical order. Used to build the games index and sitemap without
// hard-coding individual kata ids.
export async function listGames(): Promise<Game[]> {
let entries;
try {
entries = await readdir("./content/games", { withFileTypes: true });
} catch {
return [];
}
const ids = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
const games: Game[] = [];
for (const id of ids) {
try {
games.push(await loadGame(id));
} catch {
// skip katas that fail to load (missing spec.ts, etc.)
}
}
return games;
}
export async function loadGame(id: string): Promise<Game> {
const file = Bun.file(`./content/games/${id}/spec.ts`);
if (!(await file.exists())) {
throw new Error(`unknown game: ${id}`);
}
const mod = await import(`../content/games/${id}/spec.ts`);
return mod.spec as Game;
}