/** * Regression test for the /agents roster and its coverage count. * * The page mirrors the app's registry, `AgentSpec.all` in * the-nook/Sources/NookCore/Agents/AgentRegistry.swift. When Muse joined * the registry in 0.10.0 the page kept twelve entries and kept saying * "twelve", so a shipped agent was missing from the one page that lists * them. The registry lives in another repo, so the expected roster is * restated here; when an agent is added there, add it here and on the page. * * FRE-997 restored the count. The page said thirteen while the registry held * eighteen, so the count was dropped rather than corrected (FRE-359 verdict B); * now that all eighteen rows are on the page, the roster check is an equality * check again and the page states eighteen. A subset check would let a shipped * agent go missing behind a passing suite, which is the NOOK-336 failure. * * STATIC SOURCE AUDIT, the same pattern as the downloads regressions. */ import { describe, it, expect } from "bun:test"; import { readFileSync } from "node:fs"; import { join } from "node:path"; const SOURCE = readFileSync(join(import.meta.dir, "agents.tsx"), "utf8"); /** User-facing copy only: comments are not copy and may name the old wording. */ const COPY = SOURCE.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, ""); /** JSX wraps prose across lines; match a sentence, not its indentation. */ const PROSE = COPY.replace(/\s+/g, " "); /** AgentSpec.all display names, in registry order (18 rows). @ The-Nook f4258034, 2026-10-29 */ const REGISTRY = [ "Claude Code", "Codex", "OpenCode", "Pi", "Oh My Pi", "Muse", "Hermes", "OpenClaw", "Z.ai (ZCode)", "Gemini CLI", "Cline", "Qwen Code", "DeepSeek", "Kimi", "GitHub Copilot CLI", "Crush", "Cursor", "Amp" ]; const entries = [...SOURCE.matchAll(/^\s{4}name: "([^"]+)",$/gm)].map((m) => m[1]); describe("Nook /agents roster", () => { it("lists every registry agent, once, in registry order", () => { expect(entries).toEqual(REGISTRY); }); it("states the registry's size", () => { expect(entries.length).toBe(18); expect(COPY).toContain("all eighteen"); expect(COPY).toContain("The 18 agents"); expect(COPY).toContain("eighteen coding agents"); }); it("carries no stale count", () => { expect(COPY.toLowerCase()).not.toContain("thirteen"); expect(COPY).not.toMatch(/\b13 agents\b/); expect(COPY).not.toContain("all thirteen"); }); }); describe("Nook /agents remote coverage", () => { it("says agents can run on remote machines over SSH", () => { expect(PROSE).toContain("remote machines"); expect(PROSE).toContain("remote sessions report into the same grid"); }); it("names the three agents that stay on this Mac", () => { expect(PROSE).toContain("Muse, Cline and Amp run on this Mac only"); }); it("discloses the local-only rows where they are listed", () => { const localOnly = [ ...SOURCE.matchAll(/This Mac only — remote hosts skip it/g) ]; expect(localOnly.length).toBe(3); }); });