Pygienium

Code hygiene for omp — isolated 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/); inspired by piolium's sub-agent loops.

Pygienium runs highly-structured hygiene passes over a repo to clean up the common quality issues LLM-generated code accumulates: restating comments, shallow pass-through modules, dead exports/files, and redundant defensive guards. Each check is an isolated, resumable sub-agent run whose progress lands in a single inspectable run-state file.

Install

Pygienium is a local omp extension under ~/.omp/agent/extensions/pygienium/. Omp loads it via omp.extensions in package.json (entry ./src/index.ts).

# from the extension root
bun install      # optional: only needed for typecheck/test dev deps
bun run typecheck
bun test

Omp auto-discovers the extension from this location via the omp.extensions entry in package.json — no settings.json config is needed. On load it emits a TUI notification Pygienium loaded. Run /pygienium-help for available checks and flags. (only when a dialog-capable UI is available).

Configuration

Pygienium reads its chat-rendering style from omp's settings.json (~/.omp/agent/settings.json) under a pygienium key:

{
  "pygienium": {
    "chatStyle": "verbose"
  }
}
Setting Default Values Description
pygienium.chatStyle "verbose" "verbose" | "compact" Chat rendering for sub-agent tool calls. verbose (piolium-style) streams each tool event live as its own chat line ([Comments: Scanning] → bash ... / ← (ok)). compact (ralpi-style) suppresses the per-event stream and shows only the final completion message with its expandable phase tree.

No entry means "verbose" (the default). An unreadable or missing settings.json also falls back to "verbose".

Commands

Every command accepts a [path] target (default: the current directory) and is resumable — progress is persisted to <cwd>/.pygienium/run-state.json.

Command What it does
/pygienium-help Print every command, shipped check, and flag.
/pygienium-<check> [path] [--fix] Run one isolated sub-agent that scans a target, applies fixes with --fix, and emits a findings + changes report.
/pygienium-all [path] [--fix] Run every registered check in sequence under one resumable run-state.
/pygienium-status [path] Show per-check progress, artifact line counts, and errors for the latest run.
/pygienium-resume [path] [--fresh] Resume the latest in-progress/failed/partial run; complete/skipped checks skip unless --fresh.
/pygienium-export [path] [--check=] [--status=] [--out=md|json] Bundle every check's findings.md + changes.md into pygienium/export.{md|json}.

Flags

Flag Scope Description
[path] all check commands Target file or directory to scan (default: current dir).
--fix <check>, all, resume Apply fixes (default: scan-only; emits findings only).
--fresh resume Re-dispatch completed checks too — reset their run-state entries and re-run.
--check= export Comma-separated check names to include in the bundle.
--status= export Comma-separated statuses to include (e.g. complete,failed,skipped).
--out= export Bundle format: md (default) or json.

Checks

The five shipped checks live in src/checks/ and are auto-discovered on load. /pygienium-help lists whichever checks are currently registered, so this table and the live help always agree on the registered set.

Command Check Agent What it fixes
/pygienium-comments comments scanner / fixer Remove low-value/restating comments, tighten verbose ones, keep "why" comments.
/pygienium-deep-modules deep-modules deep-modules Detect shallow modules (pass-throughs, trivial wrappers, re-export barrels) and consolidate the safe ones.
/pygienium-dead-code dead-code scanner / fixer Find unreferenced exports, dead files, obsolete compat shims, migration helpers, and unused dependencies; remove clearly-dead items and flag dynamic ones.
/pygienium-defensive-guards defensive-guards defensive-guards Remove redundant defensive guarding (null checks on non-nullable types, swallowing try/catch, masking fallbacks) while keeping boundary guards (IO, parsing, untrusted input).
/pygienium-todos todos todos / fixer Inventory TODO/FIXME markers and stub implementations; with --fix, convert silent stubs (placeholder returns, empty bodies) into loud failures — never implementing TODOs or deleting markers.

Artifacts

Every check writes its reports under <cwd>/.pygienium/checks/<name>/ (run state lives at <cwd>/.pygienium/run-state.json):

  • findings.md — what the scan found (per-file line refs).
  • changes.md — what the fix phase changed + anything deferred for human review.

/pygienium-export merges every check's artifacts into one .pygienium/export.md (or export.json).

On first run in a git work tree, pygienium appends .pygienium/ to the target repo's .gitignore so a run never stages its own state/artifacts into git (opt out with --no-gitignore).

Adding a check

One file exporting a check definition. No index.ts command-wiring changes. index.ts auto-discovers every checks/*.ts (except the registry barrel) at startup, registers each file's check export, and /pygienium-<name> appears 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.
  2. Edit the name, label, description, the rubric in the scan/fix task builders, and the gate precondition.
  3. Export it as check. Done.
import type { CheckDefinition, CheckScope } from "./registry.js";

export const check = {
  name: "my-check",
  label: "My check",
  description: "What it fixes (shown in /pygienium-help).",
  agentName: "scanner",       // reuse a shipped agent, or add agents/<name>.md
  fixAgentName: "fixer",
  phaseId: "my-check",
  buildScanTask: (_cwd: string, scope: CheckScope) => `# Task: my-check scan\n…`,
  buildFixTask: (_cwd: string, scope: CheckScope, findings: string) => `# Task: my-check fix\n…`,
  gate: (cwd: string) => undefined,
} as const satisfies CheckDefinition;

Reload omp (or /reload) and run /pygienium-help/pygienium-my-check is listed and runnable. A CheckDefinition supplies the task builders and gate; the generic check-runner wires the phases together, so a new check never touches command plumbing.

Architecture

Each check is a "mode" running a fixed phase pipeline:

registerCheck(def)            ← index.ts discovers checks/*.ts `check` exports
        │
        ▼
/pygienium-<check> ─► runCheck(def)        (src/modes/check-runner.ts)
        │
        ├─ Q0 recon (shared, run once per run)   (src/recon.ts)
        │     git state + source-file inventory → .pygienium/recon.json
        ├─ analysis sub-agent  (buildScanTask)    ← scanner/<check> agent
        │     writes .pygienium/checks/<name>/findings.md
        ├─ fix sub-agent       (buildFixTask)     ← fixer, only with --fix
        │     writes .pygienium/checks/<name>/changes.md
        ├─ verify gate         (re-runs check.gate)
        └─ cleanup             (drops transient scratch artifacts)
  • Sub-agents are isolated in-memory AgentSessions scoped to the target cwd (see src/agent-runner.ts), with the agent definition's system prompt and tool allowlist applied. Agent definitions are plain editable markdown in agents/ (frontmatter name + allowedTools, body = system prompt) — tuning a sub-agent never needs TypeScript changes. A scanned project can ship its own agents/*.md at its root: those are loaded as overrides (repo agent wins on name collision), so teams can tune prompts or add project-specific agents without touching the extension.
  • Run-state is a single JSON file at <cwd>/.pygienium/run-state.json (src/run-state.ts): per-check phase progress, captured findings/changes text, and recon status. /pygienium-status, /pygienium-resume, and /pygienium-export are pure reads over it; /pygienium-all shares one RunState across every check so phases accumulate in one record.
  • The registry (src/checks/registry.ts) is the extensibility seam: a module-level Map of CheckDefinitions. index.ts discovers every checks/*.ts file, registers its check export, then iterates the map and binds one /pygienium-<name> command per entry — adding a check is a file with a check export, nothing else.
  • The footer (src/footer.ts) is the piolium-style pipeline-overview status strip: a single static line in the TUI footer (via ui.setStatus(key, text)) listing the full ordered pipeline with the cursor on the current phase and what's to come. For a single check the items are the phases; for /pygienium-all they're the checks (the full todo list), and the per-check footer is suppressed so two overviews never compete over the same status slot. The chat widget (phases.ts) remains the animated detail view (spinner + tool-call tree + completion tree); the footer is the overview — the two never overlap. In print/JSON mode the footer is a no-op.

Layout

pygienium/
├─ src/
│  ├─ index.ts            ← entry: auto-discover checks, bind commands
│  ├─ commands.ts          ← slash-command handlers (thin binders)
│  ├─ help.ts              ← COMMANDS + CLI_FLAGS → /pygienium-help output
│  ├─ agent-runner.ts      ← isolated sub-agent sessions (injectable for tests)
│  ├─ agents.ts            ← markdown agent-definition loader
│  ├─ recon.ts             ← shared Q0 reconnaissance snapshot
│  ├─ run-state.ts         ← persistent, resumable run-state model
│  ├─ status.ts            ← /pygienium-status formatter (pure)
│  ├─ export.ts            ← /pygienium-export gatherer + md/json renderer
│  ├─ phases.ts            ← live chat progress widget + completion-tree helpers
│  ├─ footer.ts            ← pipeline-overview status strip (TUI footer)
│  ├─ modes/check-runner.ts ← the per-check phase pipeline
│  └─ checks/              ← one file per check, exporting `check`
│     ├─ registry.ts       ← CheckDefinition + registerCheck
│     ├─ comments.ts  deep-modules.ts  dead-code.ts
│     ├─ defensive-guards.ts
└─ agents/                 ← scanner.md  fixer.md  deep-modules.md  defensive-guards.md

License

MIT

Description
omp port of pygenium (generated from Mike/pygenium by Gitea Actions)
Readme MIT 329 KiB
Languages
TypeScript 99.7%
Shell 0.3%