pi cli extension that controls the entropy of the codebase by enforcing code module boundaries. It helps combat slop generation and code architecture degradation.
AI coding agents produce edits with limited context knowledge (myopia) — their changes may leak implementation details, and break architectural contracts (slop).
Module contracts as guardrails. Each directory can contain a descriptor file that declares:
readonly— files and directories the agent must not touchfrozen— files where no new exports are allowedvisible— the set of exports allowed to be added or modified in that module
The extension intercepts agent write/edit operations and enforces these contracts. Violations are blocked with a clear reason.
- Indexing — On session start, scans the project tree for descriptor files and builds a module index.
- System prompt — Injects a hint so the agent knows to respect descriptor file conventions.
- Gating — On every write/edit, checks:
- Readonly gate — is the target file locked? Fronzen gate — is there any surface change to the target file?
- Export gate — would the change introduce an export not in the
visiblelist? - Import gate (not implemented yet) — would the change introduce an import violating visibility scope?
- System prompt: system-prompt.md
- Currently supported languages: TypeScript/JavaScript, Rust, Java, Go, Kotlin, Scala
pi install npm:@cuzfrog/pi-module-gatesOr load directly for a single session:
pi -e npm:@cuzfrog/pi-module-gatesA module descriptor is a Markdown file (default name: MODULE.md) placed in a directory. You can piggy-back on your module context file for example CONTEXT.md.
---
readonly: [mod.rs]
---
Any prose for the agent to better understand the module.frozen: [mod.rs]Frozen files cannot change their surface size: no new exports or public entries are allowed.
A skill module-freeze-all has been included to auto-freeze modules.
visible:
- greet # equivalent to `path: ./greet`
- sub/mod1/Fooor:
visible:
- path: my_function
modifier: pub(crate) # (optional) demands an exact match| Scenario | Behavior |
|---|---|
visible key absent or no MODULE.md |
Module is unconstrained — exports are not gated. Equivalent to null internally. |
visible: [] |
Module is fully closed — no new exports may be added. Editing existing exports is still allowed. |
| Malformed YAML frontmatter | The module is left unguarded and an info notification is emitted. |
project/
MODULE.md visible: [Foo, Bar]
src/
MODULE.md visible: [Bar, Baz]
app.ts ← checked against `src/MODULE.md` only
A MODULE.md only enforces exports within its immediate directory.
# parent/MODULE.md
visible:
- sub/Tool # type Tool is allowed to be imported from parent
# parent/sub/MODULE.md (before complement pass)
visible:
- Bar # type Bar is allowed to be imported from parent/sub within parent, but not outside parentA MODULE.md semantically gates exposures at the module level it resides.
Add a module-gate entry to .pi/settings.json:
{
"module-gate": {
"moduleDescriptorFileName": "MODULE.md",
"moduleDescriptorReadonly": true,
"sourceRoot": "src/"
}
}| Option | Default | Description |
|---|---|---|
moduleDescriptorFileName |
"MODULE.md" |
File name used for module descriptors (case-insensitive) |
moduleDescriptorReadonly |
true |
When true, descriptor files are readonly. |
sourceRoot |
"src/" |
Directory to scan for descriptor files and enforce gates. Set to "" to scan from project root. |
When no settings file exists or no module-gate key is present, defaults apply.
MIT
Cause Chung (cuzfrog@gmail.com)