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:
2026-08-10 11:15:41 -04:00
parent a40cdcd9e3
commit 3d142ed217
23 changed files with 117 additions and 115 deletions

View File

@@ -2,8 +2,7 @@
Code hygiene for [omp](https://github.com/oh-my-pi) — isolated Code hygiene for [omp](https://github.com/oh-my-pi) — isolated
sub-agent checks that scan a target, apply fixes, and emit a findings + changes sub-agent checks that scan a target, apply fixes, and emit a findings + changes
report. Port of the pi extension (was `~/.pi/agent/extensions/pygienium/`); report. Inspired by piolium's sub-agent loops.
inspired by piolium's sub-agent loops.
Pygienium runs **highly-structured hygiene passes** over a repo to clean up the Pygienium runs **highly-structured hygiene passes** over a repo to clean up the
common quality issues LLM-generated code accumulates: restating comments, common quality issues LLM-generated code accumulates: restating comments,
@@ -70,9 +69,9 @@ resumable — progress is persisted to `<cwd>/.pygienium/run-state.json`.
## Checks ## Checks
The five shipped checks live in [`src/checks/`](./src/checks/) and are The five shipped checks live in [`src/checks/`](./src/checks/) and self-register
auto-discovered on load. `/pygienium-help` lists whichever checks are currently on load. `/pygienium-help` lists whichever checks are currently registered, so
registered, so this table and the live help always agree on the registered set. this table and the live help always agree on the registered set.
| Command | Check | Agent | What it fixes | | Command | Check | Agent | What it fixes |
| --- | --- | --- | --- | | --- | --- | --- | --- |
@@ -99,27 +98,20 @@ git (opt out with `--no-gitignore`).
## Adding a check ## Adding a check
One file exporting a `check` definition. **No `index.ts` command-wiring changes.** One file + one `registerCheck()` call. **No `index.ts` command-wiring changes.**
`index.ts` auto-discovers every `checks/*.ts` (except the registry barrel) at `index.ts` auto-discovers every `checks/*.ts` (except the registry barrel) at
startup, registers each file's `check` export, and `/pygienium-<name>` appears startup, so a new file self-registers and `/pygienium-<name>` appears
automatically. automatically.
Check files are **pure data modules** — they export a definition and never
import the registry at runtime. Registration happens in `index.ts` from the
entry's own registry instance, which keeps a single registry even under omp's
extension loader (it cache-busts lazily imported graph modules with an
`?mtime` suffix, which would otherwise split the registry into two module
instances).
1. Create `src/checks/<name>.ts` from the template below. 1. Create `src/checks/<name>.ts` from the template below.
2. Edit the `name`, `label`, `description`, the rubric in the scan/fix task 2. Edit the `name`, `label`, `description`, the rubric in the scan/fix task
builders, and the `gate` precondition. builders, and the `gate` precondition.
3. Export it as `check`. Done. 3. Keep the trailing `registerCheck(<name>Check)`. Done.
```ts ```ts
import type { CheckDefinition, CheckScope } from "./registry.js"; import { registerCheck, type CheckScope } from "./registry.js";
export const check = { export const myCheck = {
name: "my-check", name: "my-check",
label: "My check", label: "My check",
description: "What it fixes (shown in /pygienium-help).", description: "What it fixes (shown in /pygienium-help).",
@@ -129,7 +121,9 @@ export const check = {
buildScanTask: (_cwd: string, scope: CheckScope) => `# Task: my-check scan\n…`, buildScanTask: (_cwd: string, scope: CheckScope) => `# Task: my-check scan\n…`,
buildFixTask: (_cwd: string, scope: CheckScope, findings: string) => `# Task: my-check fix\n…`, buildFixTask: (_cwd: string, scope: CheckScope, findings: string) => `# Task: my-check fix\n…`,
gate: (cwd: string) => undefined, gate: (cwd: string) => undefined,
} as const satisfies CheckDefinition; } as const;
registerCheck(myCheck);
``` ```
Reload omp (or `/reload`) and run `/pygienium-help``/pygienium-my-check` is Reload omp (or `/reload`) and run `/pygienium-help``/pygienium-my-check` is
@@ -142,7 +136,7 @@ touches command plumbing.
Each check is a "mode" running a fixed phase pipeline: Each check is a "mode" running a fixed phase pipeline:
``` ```
registerCheck(def) ← index.ts discovers checks/*.ts `check` exports registerCheck(def) ← checks/*.ts self-register on load
/pygienium-<check> ─► runCheck(def) (src/modes/check-runner.ts) /pygienium-<check> ─► runCheck(def) (src/modes/check-runner.ts)
@@ -171,10 +165,9 @@ registerCheck(def) ← index.ts discovers checks/*.ts `check` exports
`/pygienium-export` are pure reads over it; `/pygienium-all` shares one `/pygienium-export` are pure reads over it; `/pygienium-all` shares one
`RunState` across every check so phases accumulate in one record. `RunState` across every check so phases accumulate in one record.
- **The registry** (`src/checks/registry.ts`) is the extensibility seam: a - **The registry** (`src/checks/registry.ts`) is the extensibility seam: a
module-level `Map` of `CheckDefinition`s. `index.ts` discovers every module-level `Map` of `CheckDefinition`s. `index.ts` iterates it and binds
`checks/*.ts` file, registers its `check` export, then iterates the map and one `/pygienium-<name>` command per entry, so adding a check is a file +
binds one `/pygienium-<name>` command per entry — adding a check is a file one `registerCheck()` line.
with a `check` export, nothing else.
- **The footer** (`src/footer.ts`) is the piolium-style pipeline-overview - **The footer** (`src/footer.ts`) is the piolium-style pipeline-overview
status strip: a single static line in the TUI footer (via status strip: a single static line in the TUI footer (via
`ui.setStatus(key, text)`) listing the full ordered pipeline with the cursor `ui.setStatus(key, text)`) listing the full ordered pipeline with the cursor
@@ -202,7 +195,7 @@ pygienium/
│ ├─ phases.ts ← live chat progress widget + completion-tree helpers │ ├─ phases.ts ← live chat progress widget + completion-tree helpers
│ ├─ footer.ts ← pipeline-overview status strip (TUI footer) │ ├─ footer.ts ← pipeline-overview status strip (TUI footer)
│ ├─ modes/check-runner.ts ← the per-check phase pipeline │ ├─ modes/check-runner.ts ← the per-check phase pipeline
│ └─ checks/ ← one file per check, exporting `check` │ └─ checks/ ← one file per check, self-registering
│ ├─ registry.ts ← CheckDefinition + registerCheck │ ├─ registry.ts ← CheckDefinition + registerCheck
│ ├─ comments.ts deep-modules.ts dead-code.ts │ ├─ comments.ts deep-modules.ts dead-code.ts
│ ├─ defensive-guards.ts │ ├─ defensive-guards.ts

View File

@@ -1,5 +1,5 @@
{ {
"name": "@mikefreno/omp-pygenium", "name": "@mikefreno/omp-pygienium",
"version": "0.1.0", "version": "0.1.0",
"description": "Code hygiene extension for omp (port of the pi extension) — isolated sub-agent checks that scan a target, apply fixes, and emit a findings+changes report.", "description": "Code hygiene extension for omp (port of the pi extension) — isolated sub-agent checks that scan a target, apply fixes, and emit a findings+changes report.",
"keywords": [ "keywords": [
@@ -30,4 +30,4 @@
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"test": "bun test" "test": "bun test"
} }
} }

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

@@ -20,7 +20,7 @@
* @module pygienium/checks/comments * @module pygienium/checks/comments
*/ */
import type { CheckDefinition, CheckScope } from "./registry.js"; import { registerCheck, type CheckScope } from "./registry.js";
import { scopeRulesMarkdown } from "./scope.js"; import { scopeRulesMarkdown } from "./scope.js";
/** Phase-strip phase this check belongs to. */ /** Phase-strip phase this check belongs to. */
@@ -215,7 +215,7 @@ async function commentsVerify(scope: CheckScope): Promise<string | undefined> {
} }
/** The comments hygiene check definition. */ /** The comments hygiene check definition. */
export const check = { export const commentsCheck = {
name: "comments", name: "comments",
label: "Comments", label: "Comments",
description: description:
@@ -227,7 +227,7 @@ export const check = {
buildFixTask: buildCommentsFixTask, buildFixTask: buildCommentsFixTask,
gate: commentsGate, gate: commentsGate,
verify: commentsVerify, verify: commentsVerify,
} as const satisfies CheckDefinition; } as const;
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts` // Self-register on import so index.ts auto-discovery picks it up.
// that exports `check` and registers it — a new check is still one file. registerCheck(commentsCheck);

View File

@@ -21,7 +21,7 @@
* @module pygienium/checks/complexity * @module pygienium/checks/complexity
*/ */
import type { CheckDefinition, CheckScope } from "./registry.js"; import { registerCheck, type CheckScope } from "./registry.js";
import { scopeRulesMarkdown } from "./scope.js"; import { scopeRulesMarkdown } from "./scope.js";
/** Phase-strip phase this check belongs to. */ /** Phase-strip phase this check belongs to. */
@@ -299,7 +299,7 @@ async function complexityVerify(
} }
/** The excessive complexity check definition. */ /** The excessive complexity check definition. */
export const check = { export const complexityCheck = {
name: "complexity", name: "complexity",
label: "Complexity", label: "Complexity",
description: description:
@@ -311,7 +311,7 @@ export const check = {
buildFixTask: buildComplexityFixTask, buildFixTask: buildComplexityFixTask,
gate: complexityGate, gate: complexityGate,
verify: complexityVerify, verify: complexityVerify,
} as const satisfies CheckDefinition; } as const;
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts` // Self-register on import so index.ts auto-discovery picks it up.
// that exports `check` and registers it — a new check is still one file. registerCheck(complexityCheck);

View File

@@ -29,7 +29,11 @@ import {
rm, rm,
} from "node:fs/promises"; } from "node:fs/promises";
import { basename, dirname, join, relative, resolve } from "node:path"; 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 { import {
isScopeSource, isScopeSource,
SCOPE_EXCLUDE_DIRS, SCOPE_EXCLUDE_DIRS,
@@ -1126,7 +1130,7 @@ async function deadCodeVerify(scope: CheckScope): Promise<string | undefined> {
} }
/** The registered `CheckDefinition` (module self-registers on import). */ /** The registered `CheckDefinition` (module self-registers on import). */
export const check: CheckDefinition = { export const deadCodeCheck: CheckDefinition = {
name: "dead-code", name: "dead-code",
label: "Dead code", label: "Dead code",
description: description:
@@ -1140,5 +1144,5 @@ export const check: CheckDefinition = {
verify: deadCodeVerify, verify: deadCodeVerify,
}; };
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts` // Self-register so `index.ts` auto-discovers this check with zero wiring edits.
// that exports `check` and registers it. registerCheck(deadCodeCheck);

View File

@@ -23,7 +23,11 @@
import { readdirSync } from "node:fs"; import { readdirSync } from "node:fs";
import { join } from "node:path"; 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"; import { isScopeSource, scopeRulesMarkdown } from "./scope.js";
/** Output directory for this check's persistent reports. */ /** Output directory for this check's persistent reports. */
@@ -168,7 +172,7 @@ function buildDeepFixTask(
} }
/** The check definition; registers itself on import. */ /** The check definition; registers itself on import. */
export const check: CheckDefinition = { const deepModulesCheck: CheckDefinition = {
name: "deep-modules", name: "deep-modules",
label: "Deep modules", label: "Deep modules",
description: description:
@@ -181,5 +185,6 @@ export const check: CheckDefinition = {
verify: deepModulesVerify, verify: deepModulesVerify,
}; };
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts` registerCheck(deepModulesCheck);
// that exports `check` and registers it.
export { deepModulesCheck };

View File

@@ -43,7 +43,11 @@
import { readdirSync } from "node:fs"; import { readdirSync } from "node:fs";
import { join } from "node:path"; 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"; import { isScopeSource, scopeRulesMarkdown } from "./scope.js";
/** Output directory for this check's persistent reports. */ /** Output directory for this check's persistent reports. */
@@ -198,7 +202,7 @@ function buildDefensiveGuardsFixTask(
} }
/** The check definition; registers itself on import. */ /** The check definition; registers itself on import. */
export const check: CheckDefinition = { const defensiveGuardsCheck: CheckDefinition = {
name: "defensive-guards", name: "defensive-guards",
label: "Defensive guards", label: "Defensive guards",
description: description:
@@ -211,5 +215,6 @@ export const check: CheckDefinition = {
verify: defensiveGuardsVerify, verify: defensiveGuardsVerify,
}; };
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts` registerCheck(defensiveGuardsCheck);
// that exports `check` and registers it.
export { defensiveGuardsCheck };

View File

@@ -39,7 +39,7 @@ export const SCOPE_EXTENSIONS: ReadonlySet<string> = new Set([
/** /**
* Directories pygienium never descends into — build output, dependency caches, * 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 * `readdir`, skip these by name to avoid wasting tokens on vendored code and
* generated artifacts the user can't act on. * 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 * exactly what to inspect and what to skip — stated once here, not copy-pasted
* into each task builder. * 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. * this before exploring, so the exclusion list governs their search too.
*/ */
export function scopeRulesMarkdown(): string { export function scopeRulesMarkdown(): string {
@@ -139,7 +139,7 @@ noise the user cannot act on.
exists — it is the authoritative source inventory (git-tracked, extension- exists — it is the authoritative source inventory (git-tracked, extension-
filtered, exclude-aware). Read its \`fileCounts\` for the quick picture. filtered, exclude-aware). Read its \`fileCounts\` for the quick picture.
2. Otherwise enumerate files yourself, applying the rules above. 2. Otherwise enumerate files yourself, applying the rules above.
3. When using \`glob\`/\`grep\`, add ignore patterns for the skip directories 3. When using \`find\`/\`grep\`, add prune clauses for the skip directories
(e.g. exclude \`**/node_modules/**\` from your scans). (e.g. \`find . -type d -name node_modules -prune -o -name '*.ts' -print\`).
`; `;
} }

View File

@@ -40,7 +40,11 @@ import { readdirSync } from "node:fs";
import { readFile, readdir, stat } from "node:fs/promises"; import { readFile, readdir, stat } from "node:fs/promises";
import { join, relative } from "node:path"; import { join, relative } from "node:path";
import { loadRunState } from "../run-state.js"; import { loadRunState } from "../run-state.js";
import type { CheckDefinition, CheckScope } from "./registry.js"; import {
registerCheck,
type CheckDefinition,
type CheckScope,
} from "./registry.js";
import { import {
isScopeSource, isScopeSource,
SCOPE_EXCLUDE_DIRS, SCOPE_EXCLUDE_DIRS,
@@ -541,7 +545,7 @@ async function todosVerify(scope: CheckScope): Promise<string | undefined> {
} }
/** The todos check definition; registers itself on import. */ /** The todos check definition; registers itself on import. */
export const check: CheckDefinition = { const todosCheck: CheckDefinition = {
name: "todos", name: "todos",
label: "TODOs & stubs", label: "TODOs & stubs",
description: description:
@@ -554,5 +558,6 @@ export const check: CheckDefinition = {
verify: todosVerify, verify: todosVerify,
}; };
// No self-registration here: `index.ts` auto-discovers every `checks/*.ts` registerCheck(todosCheck);
// that exports `check` and registers it.
export { todosCheck };

View File

@@ -3,11 +3,15 @@
* *
* Entry point. Registers `/pygienium-help`, auto-registers one * Entry point. Registers `/pygienium-help`, auto-registers one
* `/pygienium-<check>` command per registered `CheckDefinition`, plus the * `/pygienium-<check>` command per registered `CheckDefinition`, plus the
* `all`/`resume`/`status`/`export` commands. Adding a check requires ONLY a new * `all`/`resume`/`status`/`export` commands. Adding a check requires a new
* file in `src/checks/` plus one `registerCheck(def)` call — no changes here. * 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 * Check modules self-register on import (top-level `registerCheck`); the
* registry barrel), so they self-register at load time before commands bind. * 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). * 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 * The default export runs once per session; the factory is async so check
@@ -16,10 +20,9 @@
* @module pygienium/index * @module pygienium/index
*/ */
import { readdir, readFile } from "node:fs/promises"; import { readFile } from "node:fs/promises";
import { dirname, join } from "node:path"; import { join } from "node:path";
import { homedir } from "node:os"; import { homedir } from "node:os";
import { fileURLToPath } from "node:url";
import type { import type {
ExtensionAPI, ExtensionAPI,
ExtensionCommandContext, ExtensionCommandContext,
@@ -28,7 +31,7 @@ import type {
SessionStartEvent, SessionStartEvent,
} from "@oh-my-pi/pi-coding-agent"; } from "@oh-my-pi/pi-coding-agent";
import { Box, Text } from "@oh-my-pi/pi-tui"; 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 { registerPygieniumCommands, type PygieniumCtx } from "./commands.js";
import { import {
type SendChatMessage, type SendChatMessage,
@@ -244,34 +247,6 @@ function makeStreamForwarder(pi: ExtensionAPI): StreamForwarder {
return { onAgentEvent, sendPhaseLine }; 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. * 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( export default async function pygieniumExtension(
pi: ExtensionAPI, pi: ExtensionAPI,
): Promise<void> { ): Promise<void> {
// Self-register every shipped check before wiring commands. // Checks self-register on import (see ./checks/all.js), which runs before
await loadCheckModules(); // this factory — commands below bind against a fully populated registry.
const sendChatMessage = makeSendChatMessage(pi); const sendChatMessage = makeSendChatMessage(pi);
// Register custom message renderer for pygienium progress messages. // Register custom message renderer for pygienium progress messages.

View File

@@ -43,7 +43,7 @@ function fakeCheck(name: string): CheckDefinition {
} }
function stubCtx(cwd: string): PygieniumCtx { function stubCtx(cwd: string): PygieniumCtx {
return { cwd, hasUI: false, ui: undefined } as PygieniumCtx; return { cwd, mode: "print", hasUI: false, ui: undefined } as PygieniumCtx;
} }
/** Capture process.stdout.write lines for the duration of `fn`. */ /** Capture process.stdout.write lines for the duration of `fn`. */

View File

@@ -65,7 +65,7 @@ function fakeCheck(name: string): CheckDefinition {
} }
function stubCtx(cwd: string): PygieniumCtx { function stubCtx(cwd: string): PygieniumCtx {
return { cwd, hasUI: false, ui: undefined } as PygieniumCtx; return { cwd, mode: "print", hasUI: false, ui: undefined } as PygieniumCtx;
} }
/** Tracker: records dispatched agent tasks then delegates to the fake runner. */ /** Tracker: records dispatched agent tasks then delegates to the fake runner. */

View File

@@ -41,7 +41,7 @@ function smokeCheck(): CheckDefinition {
} }
function stubCtx(cwd: string): PygieniumCtx { function stubCtx(cwd: string): PygieniumCtx {
return { cwd, hasUI: false, ui: undefined } as PygieniumCtx; return { cwd, mode: "print", hasUI: false, ui: undefined } as PygieniumCtx;
} }
describe("check-runner integration", () => { describe("check-runner integration", () => {

View File

@@ -27,7 +27,7 @@ import {
import { handleCheckCommand, type PygieniumCtx } from "../src/commands.js"; import { handleCheckCommand, type PygieniumCtx } from "../src/commands.js";
import { loadRunState } from "../src/run-state.js"; import { loadRunState } from "../src/run-state.js";
import { import {
check as commentsCheck, commentsCheck,
findingsPath, findingsPath,
changesPath, changesPath,
} from "../src/checks/comments.js"; } from "../src/checks/comments.js";
@@ -48,6 +48,7 @@ const FILENAME = "sample.ts";
function stubCtx(cwd: string): PygieniumCtx { function stubCtx(cwd: string): PygieniumCtx {
return { return {
cwd, cwd,
mode: "print",
hasUI: false, hasUI: false,
ui: undefined, ui: undefined,
} as PygieniumCtx; } as PygieniumCtx;

View File

@@ -19,7 +19,7 @@ import * as fs from "node:fs/promises";
import * as path from "node:path"; import * as path from "node:path";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { import {
check as complexityCheck, complexityCheck,
buildComplexityScanTask, buildComplexityScanTask,
buildComplexityFixTask, buildComplexityFixTask,
} from "../src/checks/complexity.js"; } from "../src/checks/complexity.js";

View File

@@ -22,7 +22,7 @@ import {
getCheck, getCheck,
} from "../src/checks/registry.js"; } from "../src/checks/registry.js";
import { import {
check as deadCodeCheck, deadCodeCheck,
detectDeadCode, detectDeadCode,
findingsPath, findingsPath,
changesPath, changesPath,
@@ -39,7 +39,7 @@ import { handleCheckCommand, type PygieniumCtx } from "../src/commands.js";
import { loadRunState } from "../src/run-state.js"; import { loadRunState } from "../src/run-state.js";
function stubCtx(cwd: string): PygieniumCtx { function stubCtx(cwd: string): PygieniumCtx {
return { cwd, hasUI: false, ui: undefined } as PygieniumCtx; return { cwd, mode: "print", hasUI: false, ui: undefined } as PygieniumCtx;
} }
async function writeFixture(root: string): Promise<void> { async function writeFixture(root: string): Promise<void> {

View File

@@ -30,11 +30,11 @@ import { loadRunState } from "../src/run-state.js";
import { import {
findingsPath, findingsPath,
changesPath, changesPath,
check as deepModulesCheck, deepModulesCheck,
} from "../src/checks/deep-modules.js"; } from "../src/checks/deep-modules.js";
function stubCtx(cwd: string): PygieniumCtx { function stubCtx(cwd: string): PygieniumCtx {
return { cwd, hasUI: false, ui: undefined } as PygieniumCtx; return { cwd, mode: "print", hasUI: false, ui: undefined } as PygieniumCtx;
} }
/** Drop a pass-through wrapper module that forwards a single lib call. */ /** Drop a pass-through wrapper module that forwards a single lib call. */

View File

@@ -35,11 +35,11 @@ import { loadRunState } from "../src/run-state.js";
import { import {
findingsPath, findingsPath,
changesPath, changesPath,
check as defensiveGuardsCheck, defensiveGuardsCheck,
} from "../src/checks/defensive-guards.js"; } from "../src/checks/defensive-guards.js";
function stubCtx(cwd: string): PygieniumCtx { function stubCtx(cwd: string): PygieniumCtx {
return { cwd, hasUI: false, ui: undefined } as PygieniumCtx; return { cwd, mode: "print", hasUI: false, ui: undefined } as PygieniumCtx;
} }
/** /**

View File

@@ -30,7 +30,7 @@ import { handleCheckCommand, type PygieniumCtx } from "../src/commands.js";
import { loadRunState } from "../src/run-state.js"; import { loadRunState } from "../src/run-state.js";
function stubCtx(cwd: string): PygieniumCtx { function stubCtx(cwd: string): PygieniumCtx {
return { cwd, hasUI: false, ui: undefined } as PygieniumCtx; return { cwd, mode: "print", hasUI: false, ui: undefined } as PygieniumCtx;
} }
/** Capture process.stdout.write lines for the duration of `fn`. */ /** Capture process.stdout.write lines for the duration of `fn`. */

View File

@@ -79,7 +79,7 @@ function fakeCheck(name: string): CheckDefinition {
} }
function stubCtx(cwd: string): PygieniumCtx { function stubCtx(cwd: string): PygieniumCtx {
return { cwd, hasUI: false, ui: undefined } as PygieniumCtx; return { cwd, mode: "print", hasUI: false, ui: undefined } as PygieniumCtx;
} }
/** Tracker: records dispatched agent tasks then delegates to the fake runner. */ /** Tracker: records dispatched agent tasks then delegates to the fake runner. */

View File

@@ -32,14 +32,14 @@ import { loadRunState } from "../src/run-state.js";
import { import {
findingsPath, findingsPath,
changesPath, changesPath,
check as todosCheck, todosCheck,
detectTodoStubs, detectTodoStubs,
todosPriorCounts, todosPriorCounts,
buildTodosScanTask, buildTodosScanTask,
} from "../src/checks/todos.js"; } from "../src/checks/todos.js";
function stubCtx(cwd: string): PygieniumCtx { function stubCtx(cwd: string): PygieniumCtx {
return { cwd, hasUI: false, ui: undefined } as PygieniumCtx; return { cwd, mode: "print", hasUI: false, ui: undefined } as PygieniumCtx;
} }
/** /**

View File

@@ -29,10 +29,10 @@ import {
} from "../src/agent-runner.js"; } from "../src/agent-runner.js";
import { handleCheckCommand, type PygieniumCtx } from "../src/commands.js"; import { handleCheckCommand, type PygieniumCtx } from "../src/commands.js";
import { loadRunState } from "../src/run-state.js"; import { loadRunState } from "../src/run-state.js";
import { check as complexityCheck } from "../src/checks/complexity.js"; import { complexityCheck } from "../src/checks/complexity.js";
import { check as deadCodeCheck } from "../src/checks/dead-code.js"; import { deadCodeCheck } from "../src/checks/dead-code.js";
import { check as deepModulesCheck } from "../src/checks/deep-modules.js"; import { deepModulesCheck } from "../src/checks/deep-modules.js";
import { check as defensiveGuardsCheck } from "../src/checks/defensive-guards.js"; import { defensiveGuardsCheck } from "../src/checks/defensive-guards.js";
/** Agent runner that simulates the MagniFluo bug: ok, empty, no writes. */ /** Agent runner that simulates the MagniFluo bug: ok, empty, no writes. */
const noopRunner: AgentRunner = async () => ({ const noopRunner: AgentRunner = async () => ({
@@ -41,7 +41,7 @@ const noopRunner: AgentRunner = async () => ({
}); });
function stubCtx(cwd: string): PygieniumCtx { function stubCtx(cwd: string): PygieniumCtx {
return { cwd, hasUI: false, ui: undefined } as PygieniumCtx; return { cwd, mode: "print", hasUI: false, ui: undefined } as PygieniumCtx;
} }
describe("verify hooks fail loudly on empty agent output", () => { describe("verify hooks fail loudly on empty agent output", () => {