diff --git a/src/checks/all.ts b/src/checks/all.ts new file mode 100644 index 0000000..95e6301 --- /dev/null +++ b/src/checks/all.ts @@ -0,0 +1,15 @@ +/** + * Static barrel for shipped checks. + * + * Every check module self-registers on import (top-level `registerCheck`), so + * importing all of them here — once, statically, from the entry graph — + * registers every check before commands bind. Adding a check means dropping + * the file in and adding one import line here. + */ +import "./comments.js"; +import "./complexity.js"; +import "./dead-code.js"; +import "./deep-modules.js"; +import "./defensive-guards.js"; +import "./scope.js"; +import "./todos.js"; diff --git a/src/index.ts b/src/index.ts index d249f32..59cef3f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,11 +3,15 @@ * * Entry point. Registers `/pygienium-help`, auto-registers one * `/pygienium-` command per registered `CheckDefinition`, plus the - * `all`/`resume`/`status`/`export` commands. Adding a check requires ONLY a new - * file in `src/checks/` plus one `registerCheck(def)` call — no changes here. + * `all`/`resume`/`status`/`export` commands. Adding a check requires a new + * file in `src/checks/` plus one import line in `src/checks/all.ts` — no + * changes here. * - * Check files in `src/checks/` are auto-discovered (every `.ts` except the - * registry barrel), so they self-register at load time before commands bind. + * Check modules self-register on import (top-level `registerCheck`); the + * static barrel `src/checks/all.ts` imports every shipped check so they all + * register before commands bind. A static import list (rather than readdir + + * dynamic import) keeps the whole check graph resolvable up front, which + * matters for bundlers and module-loaders that tag extension graphs. * * Pi loads this file via jiti at runtime (see `pi.extensions` in package.json). * The default export runs once per session; the factory is async so check @@ -16,10 +20,9 @@ * @module pygienium/index */ -import { readdir, readFile } from "node:fs/promises"; -import { dirname, join } from "node:path"; +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; import { homedir } from "node:os"; -import { fileURLToPath } from "node:url"; import type { ExtensionAPI, ExtensionCommandContext, @@ -28,6 +31,7 @@ import type { SessionStartEvent, } from "@earendil-works/pi-coding-agent"; import { Box, Text } from "@earendil-works/pi-tui"; +import "./checks/all.js"; import { registerPygieniumCommands, type PygieniumCtx } from "./commands.js"; import { type SendChatMessage, @@ -243,27 +247,6 @@ function makeStreamForwarder(pi: ExtensionAPI): StreamForwarder { return { onAgentEvent, sendPhaseLine }; } -/** - * Import every `checks/*.ts` module (except the registry barrel) so each check - * file's top-level `registerCheck(def)` call runs before command binding. This - * is what makes adding a check require zero index.ts changes — drop a file, - * it self-registers. - */ -async function loadCheckModules(): Promise { - const dir = join(dirname(fileURLToPath(import.meta.url)), "checks"); - let entries: string[]; - try { - entries = await readdir(dir); - } catch { - return; // no checks dir (e.g. minimal install) - } - for (const entry of entries) { - if (!entry.endsWith(".ts")) continue; - if (entry === "registry.ts" || entry === "load.ts") continue; - await import(`./checks/${entry}`); - } -} - /** * Create a callback to send completion messages to the main chat window. */ @@ -283,9 +266,8 @@ function makeSendChatMessage(pi: ExtensionAPI): SendChatMessage { export default async function pygieniumExtension( pi: ExtensionAPI, ): Promise { - // Self-register every shipped check before wiring commands. - await loadCheckModules(); - + // Checks self-register on import (see ./checks/all.js), which runs before + // this factory — commands below bind against a fully populated registry. const sendChatMessage = makeSendChatMessage(pi); // Register custom message renderer for pygienium progress messages.