register checks via static barrel instead of readdir + dynamic import

Checks keep self-registering on import; src/checks/all.ts imports every
shipped check from the entry's static graph. Keeps the check graph fully
statically resolvable (bundler/loader friendly) and removes the only
dynamic-import discovery in the codebase.
This commit is contained in:
2026-08-10 10:51:33 -04:00
parent 31c2048e57
commit 11eb2dc6ed
2 changed files with 28 additions and 31 deletions

15
src/checks/all.ts Normal file
View File

@@ -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";

View File

@@ -3,11 +3,15 @@
*
* Entry point. Registers `/pygienium-help`, auto-registers one
* `/pygienium-<check>` 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<void> {
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<void> {
// 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.