pi-read-guard0.1.10
Pi extension that prevents massive unbudgeted full-file reads, steering the agent to offset/limit or search tools. Zero prompt tokens.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-read-guard
A pi extension that prevents massive unbudgeted full-file reads, steering the agent to offset/limit or search tools.
Enforcement lives in a tool_call hook, so it adds zero tokens to the system prompt.

Rules
readon any file with more thanmaxReadLines(default 200) withoutoffsetorlimitis blocked.- Images, PDFs and any binary file are never blocked (a NUL byte in the first 8 KB, or an image/PDF extension): their newline bytes are noise, and blocking them would break screenshots.
- The model is steered to read a slice with
offset/limit, or to search first (rg,codegraph_search).
Install
From npm:
pi install npm:pi-read-guard
Or from git:
pi install git:github.com/sorinirimies/pi-read-guard
Previews
Recorded from a real pi with only this plugin loaded; a small scripted mock model plays the agent, so you see the real guard reacting to real tool calls.
A full read of a large file is blocked, the agent reads a slice (60 lines instead of 319):

No bypass by spelling: ~/… and @… paths (which pi expands before reading) are blocked just like the plain one:

Commands: status and block count, off / on, and allow <path> for one deliberate full read:

Commands
/read-guard: status and block count/read-guard off/on: disable / enable for this session/read-guard allow <path>: allow one full read of<path>this session
Config (optional)
~/.pi/agent/read-guard.json:
{ "maxReadLines": 200, "ignore": ["*.md", "*.json"] }
Security notes
- A token saver, not a sandbox. It guards pi's
readtool. An agent can still read files throughbash(cat); the guard keeps accidental full-file reads out of your context, nothing more. - Same path as the tool. Paths are resolved exactly like pi's own
read(@file,~/file,file://, Unicode spaces, Windows shell paths, and the macOS filename variants it falls back to), so those spellings can't slip past it. A contract test compares it with pi's realresolveToCwd. - Fails open. pi blocks a tool when a
tool_callhandler throws, so any unexpected error here means "allow". - Constant memory. Line counts are streamed (64 KB chunks, early exit), never the whole file.
- No network access, no shell commands, zero runtime dependencies.
Development
bun install
just check # typecheck + tests + pack check + nushell tests (what CI runs)
just test # bun test only (with coverage)
just coverage # tests + a coverage table
just push-all # push main to every configured remote (origin + the Gitea instances); `just --list` has pull-all, push-tags-all, release-all, the *-force and sync-gitea-* variants
Coverage: every bun test collects coverage (bunfig.toml) and fails below 95% lines / 95% functions. CI shows the table on the run page and uploads lcov.info.
CI scripts are nushell (scripts/), the same ones locally and in GitHub / Gitea Actions.
just --list shows every task.
Demo recordings
The GIFs above live in examples/vhs/generated/ and are stored with Git LFS (git lfs install once). They are recorded with VHS from a real pi that loads only this extension, on synthetic data (examples/vhs/fixture.sh), never from real files, sessions or credentials. The agent is a scripted mock model (examples/vhs/mock-llm.ts), so the recording is repeatable.
just vhs-all # every tape (examples/vhs/*.tape): needs vhs, ttyd, ffmpeg, pi, bun, python3
just vhs-tape overview # one tape
just vhs-list # list the tapes
just demo # try it yourself in a real pi on the same synthetic data
Releases (automatic)
| Workflow | When | What |
|---|---|---|
| CI | push / PR | quality gate on Linux; tests on macOS and Windows |
| Auto-merge library updates | CI finished on a Dependabot PR | patch and minor updates (GitHub Actions) are merged automatically, but only after CI is green on that exact commit; a major update waits for you. Then it starts the nightly workflow so the update ships |
| Nightly Dependency Update | every night (GitHub 02:00 UTC, Gitea 02:30) and after each auto-merge | bun update within ranges, verify on all platforms, commit chore(deps); then build, tag and publish a new patch whenever a library was upgraded or merged, or feat/fix/perf commits are waiting since the last tag |
| Release | tag vX.Y.Z |
validates the tag against package.json, runs the gate, npm publish (idempotent, with provenance), creates the release |
So library updates need no human: they are merged once CI passes, built, versioned and published as a patch. A downgrade, a failing check, or a major update stops the chain and waits for review.
Manual release: just release patch (or minor / major / X.Y.Z) bumps, tags and pushes to GitHub; just release-all patch pushes to every configured remote; just push-release-all pushes an existing bump.
Secrets (repo settings): NPM_TOKEN is an npm granular access token with publish rights and "bypass 2FA" (required for CI publishing). Optional: GH_PAT (lets a tag push trigger the release itself), GITEA_TOKEN (Gitea).
Commits follow Conventional Commits; the changelog is generated by git-cliff.
Related plugins
Four small pi extensions that save tokens without adding anything to the system prompt:
| Plugin | What it does |
|---|---|
| pi-edit-first | Blocks whole-file write rewrites and hand-written project manifests, steering the agent to targeted edit calls and scaffolders |
| pi-read-guard | Blocks full reads of large files, steering the agent to offset/limit or search |
| pi-tokenburn | Live token and cost counter in pi's footer, with charts and budgets |
| pi-context-diet | Drops stale tool results from the context when the prompt cache is already cold, so long sessions cost less |
pi install npm:pi-edit-first
pi install npm:pi-read-guard
pi install npm:pi-tokenburn
pi install npm:pi-context-diet
MIT