regenerate omp port from pi base via port-to-omp
Checks now self-register through the static barrel (src/checks/all.ts) like the pi base: the ?mtime registry-split workaround is gone, check modules and tests are byte-identical to the base, and registration happens in one module graph instance under omp's extension loader.
This commit is contained in:
15
src/checks/all.ts
Normal file
15
src/checks/all.ts
Normal 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";
|
||||
@@ -20,7 +20,7 @@
|
||||
* @module pygienium/checks/comments
|
||||
*/
|
||||
|
||||
import type { CheckDefinition, CheckScope } from "./registry.js";
|
||||
import { registerCheck, type CheckScope } from "./registry.js";
|
||||
import { scopeRulesMarkdown } from "./scope.js";
|
||||
|
||||
/** Phase-strip phase this check belongs to. */
|
||||
@@ -215,7 +215,7 @@ async function commentsVerify(scope: CheckScope): Promise<string | undefined> {
|
||||
}
|
||||
|
||||
/** The comments hygiene check definition. */
|
||||
export const check = {
|
||||
export const commentsCheck = {
|
||||
name: "comments",
|
||||
label: "Comments",
|
||||
description:
|
||||
@@ -227,7 +227,7 @@ export const check = {
|
||||
buildFixTask: buildCommentsFixTask,
|
||||
gate: commentsGate,
|
||||
verify: commentsVerify,
|
||||
} as const satisfies CheckDefinition;
|
||||
} as const;
|
||||
|
||||
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts`
|
||||
// that exports `check` and registers it — a new check is still one file.
|
||||
// Self-register on import so index.ts auto-discovery picks it up.
|
||||
registerCheck(commentsCheck);
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* @module pygienium/checks/complexity
|
||||
*/
|
||||
|
||||
import type { CheckDefinition, CheckScope } from "./registry.js";
|
||||
import { registerCheck, type CheckScope } from "./registry.js";
|
||||
import { scopeRulesMarkdown } from "./scope.js";
|
||||
|
||||
/** Phase-strip phase this check belongs to. */
|
||||
@@ -299,7 +299,7 @@ async function complexityVerify(
|
||||
}
|
||||
|
||||
/** The excessive complexity check definition. */
|
||||
export const check = {
|
||||
export const complexityCheck = {
|
||||
name: "complexity",
|
||||
label: "Complexity",
|
||||
description:
|
||||
@@ -311,7 +311,7 @@ export const check = {
|
||||
buildFixTask: buildComplexityFixTask,
|
||||
gate: complexityGate,
|
||||
verify: complexityVerify,
|
||||
} as const satisfies CheckDefinition;
|
||||
} as const;
|
||||
|
||||
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts`
|
||||
// that exports `check` and registers it — a new check is still one file.
|
||||
// Self-register on import so index.ts auto-discovery picks it up.
|
||||
registerCheck(complexityCheck);
|
||||
|
||||
@@ -29,7 +29,11 @@ import {
|
||||
rm,
|
||||
} from "node:fs/promises";
|
||||
import { basename, dirname, join, relative, resolve } from "node:path";
|
||||
import type { CheckDefinition, CheckScope } from "./registry.js";
|
||||
import {
|
||||
registerCheck,
|
||||
type CheckDefinition,
|
||||
type CheckScope,
|
||||
} from "./registry.js";
|
||||
import {
|
||||
isScopeSource,
|
||||
SCOPE_EXCLUDE_DIRS,
|
||||
@@ -1126,7 +1130,7 @@ async function deadCodeVerify(scope: CheckScope): Promise<string | undefined> {
|
||||
}
|
||||
|
||||
/** The registered `CheckDefinition` (module self-registers on import). */
|
||||
export const check: CheckDefinition = {
|
||||
export const deadCodeCheck: CheckDefinition = {
|
||||
name: "dead-code",
|
||||
label: "Dead code",
|
||||
description:
|
||||
@@ -1140,5 +1144,5 @@ export const check: CheckDefinition = {
|
||||
verify: deadCodeVerify,
|
||||
};
|
||||
|
||||
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts`
|
||||
// that exports `check` and registers it.
|
||||
// Self-register so `index.ts` auto-discovers this check with zero wiring edits.
|
||||
registerCheck(deadCodeCheck);
|
||||
|
||||
@@ -23,7 +23,11 @@
|
||||
|
||||
import { readdirSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import type { CheckDefinition, CheckScope } from "./registry.js";
|
||||
import {
|
||||
registerCheck,
|
||||
type CheckDefinition,
|
||||
type CheckScope,
|
||||
} from "./registry.js";
|
||||
import { isScopeSource, scopeRulesMarkdown } from "./scope.js";
|
||||
|
||||
/** Output directory for this check's persistent reports. */
|
||||
@@ -168,7 +172,7 @@ function buildDeepFixTask(
|
||||
}
|
||||
|
||||
/** The check definition; registers itself on import. */
|
||||
export const check: CheckDefinition = {
|
||||
const deepModulesCheck: CheckDefinition = {
|
||||
name: "deep-modules",
|
||||
label: "Deep modules",
|
||||
description:
|
||||
@@ -181,5 +185,6 @@ export const check: CheckDefinition = {
|
||||
verify: deepModulesVerify,
|
||||
};
|
||||
|
||||
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts`
|
||||
// that exports `check` and registers it.
|
||||
registerCheck(deepModulesCheck);
|
||||
|
||||
export { deepModulesCheck };
|
||||
|
||||
@@ -43,7 +43,11 @@
|
||||
|
||||
import { readdirSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import type { CheckDefinition, CheckScope } from "./registry.js";
|
||||
import {
|
||||
registerCheck,
|
||||
type CheckDefinition,
|
||||
type CheckScope,
|
||||
} from "./registry.js";
|
||||
import { isScopeSource, scopeRulesMarkdown } from "./scope.js";
|
||||
|
||||
/** Output directory for this check's persistent reports. */
|
||||
@@ -198,7 +202,7 @@ function buildDefensiveGuardsFixTask(
|
||||
}
|
||||
|
||||
/** The check definition; registers itself on import. */
|
||||
export const check: CheckDefinition = {
|
||||
const defensiveGuardsCheck: CheckDefinition = {
|
||||
name: "defensive-guards",
|
||||
label: "Defensive guards",
|
||||
description:
|
||||
@@ -211,5 +215,6 @@ export const check: CheckDefinition = {
|
||||
verify: defensiveGuardsVerify,
|
||||
};
|
||||
|
||||
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts`
|
||||
// that exports `check` and registers it.
|
||||
registerCheck(defensiveGuardsCheck);
|
||||
|
||||
export { defensiveGuardsCheck };
|
||||
|
||||
@@ -39,7 +39,7 @@ export const SCOPE_EXTENSIONS: ReadonlySet<string> = new Set([
|
||||
|
||||
/**
|
||||
* Directories pygienium never descends into — build output, dependency caches,
|
||||
* tooling state, and VCS metadata. When walking the tree with `glob`/`grep`/
|
||||
* tooling state, and VCS metadata. When walking the tree with `find`/`grep`/
|
||||
* `readdir`, skip these by name to avoid wasting tokens on vendored code and
|
||||
* generated artifacts the user can't act on.
|
||||
*/
|
||||
@@ -104,7 +104,7 @@ export function isScopeSource(path: string): boolean {
|
||||
* exactly what to inspect and what to skip — stated once here, not copy-pasted
|
||||
* into each task builder.
|
||||
*
|
||||
* Agents that use `glob`/`grep`/`readdir` for their own file discovery read
|
||||
* Agents that use `find`/`grep`/`readdir` for their own file discovery read
|
||||
* this before exploring, so the exclusion list governs their search too.
|
||||
*/
|
||||
export function scopeRulesMarkdown(): string {
|
||||
@@ -139,7 +139,7 @@ noise the user cannot act on.
|
||||
exists — it is the authoritative source inventory (git-tracked, extension-
|
||||
filtered, exclude-aware). Read its \`fileCounts\` for the quick picture.
|
||||
2. Otherwise enumerate files yourself, applying the rules above.
|
||||
3. When using \`glob\`/\`grep\`, add ignore patterns for the skip directories
|
||||
(e.g. exclude \`**/node_modules/**\` from your scans).
|
||||
3. When using \`find\`/\`grep\`, add prune clauses for the skip directories
|
||||
(e.g. \`find . -type d -name node_modules -prune -o -name '*.ts' -print\`).
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,11 @@ import { readdirSync } from "node:fs";
|
||||
import { readFile, readdir, stat } from "node:fs/promises";
|
||||
import { join, relative } from "node:path";
|
||||
import { loadRunState } from "../run-state.js";
|
||||
import type { CheckDefinition, CheckScope } from "./registry.js";
|
||||
import {
|
||||
registerCheck,
|
||||
type CheckDefinition,
|
||||
type CheckScope,
|
||||
} from "./registry.js";
|
||||
import {
|
||||
isScopeSource,
|
||||
SCOPE_EXCLUDE_DIRS,
|
||||
@@ -541,7 +545,7 @@ async function todosVerify(scope: CheckScope): Promise<string | undefined> {
|
||||
}
|
||||
|
||||
/** The todos check definition; registers itself on import. */
|
||||
export const check: CheckDefinition = {
|
||||
const todosCheck: CheckDefinition = {
|
||||
name: "todos",
|
||||
label: "TODOs & stubs",
|
||||
description:
|
||||
@@ -554,5 +558,6 @@ export const check: CheckDefinition = {
|
||||
verify: todosVerify,
|
||||
};
|
||||
|
||||
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts`
|
||||
// that exports `check` and registers it.
|
||||
registerCheck(todosCheck);
|
||||
|
||||
export { todosCheck };
|
||||
|
||||
52
src/index.ts
52
src/index.ts
@@ -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,7 +31,7 @@ import type {
|
||||
SessionStartEvent,
|
||||
} from "@oh-my-pi/pi-coding-agent";
|
||||
import { Box, Text } from "@oh-my-pi/pi-tui";
|
||||
import { registerCheck, type CheckDefinition } from "./checks/registry.js";
|
||||
import "./checks/all.js";
|
||||
import { registerPygieniumCommands, type PygieniumCtx } from "./commands.js";
|
||||
import {
|
||||
type SendChatMessage,
|
||||
@@ -244,34 +247,6 @@ function makeStreamForwarder(pi: ExtensionAPI): StreamForwarder {
|
||||
return { onAgentEvent, sendPhaseLine };
|
||||
}
|
||||
|
||||
/**
|
||||
* Import every `checks/*.ts` module (except the registry barrel) and register
|
||||
* each file's `check` export. Check files are pure data modules — they no
|
||||
* longer self-register on import, because omp's extension loader cache-busts
|
||||
* lazily imported graph modules with an `?mtime` suffix, which would split
|
||||
* the registry into two module instances (static entry-graph imports resolve
|
||||
* to the clean file, lazy imports to the `?mtime` copy). Registering here —
|
||||
* from the entry's own registry instance — keeps one registry and still makes
|
||||
* adding a check a drop-a-file operation.
|
||||
*/
|
||||
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;
|
||||
const mod = (await import(`./checks/${entry}`)) as {
|
||||
check?: CheckDefinition;
|
||||
};
|
||||
if (mod.check) registerCheck(mod.check);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback to send completion messages to the main chat window.
|
||||
*/
|
||||
@@ -291,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.
|
||||
|
||||
Reference in New Issue
Block a user