pi-rust-guardian
Zero-tool, zero-prompt-tax Rust guardrails for Pi: content-addressed Read-Guard, coalesced cargo check diagnostics with rustc help hints, and DAG-persisted rustfmt tracking.
Package details
Install pi-rust-guardian from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-rust-guardian- Package
pi-rust-guardian- Version
1.0.2- Published
- Nov 6, 2026
- Downloads
- 522/mo · 522/wk
- Author
- saldmy
- License
- MIT
- Types
- extension
- Size
- 57.6 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./extensions/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-rust-guardian
Zero-tool, zero-prompt-tax Rust compiler feedback and Read-Guard for the
picoding agent.
pi-rust-guardian attaches guardrails and compiler intelligence directly to Pi's core tools (read, write, edit). It registers 0 extra tools, injects 0 prompt tokens, avoids KV-cache rewinds on local models, and short-circuits Rust compile feedback into a single turn.
pi install npm:pi-rust-guardian
40 fn get_count() -> Result<usize, AppError> {
-41 Ok(count)
+41 count
42 }
[rust-guardian] cargo check --all-targets: 1 error(s) — fix before finishing:
│ error[E0308]: mismatched types
│ --> src/lib.rs:41:5
│ |
│ 41 | count
│ | ^^^^^ expected `Result<usize, AppError>`, found `usize`
│ |
│ help: try wrapping the expression in `Ok`
│ |
│ 41 | Ok(count)
│ | +++ +
The Problem vs. The Fix
| The Classic Agent Failure Mode | How pi-rust-guardian Fixes It |
|---|---|
Silent File TruncationThe agent reads lines 1–80 of a 600-line file (or hits output truncation), then calls write()—silently wiping out lines 81–600. |
Content-Addressed Read-GuardTracks SHA-256 digests and full vs partial read coverage. Targeted edit is allowed, but whole-file write is blocked until the complete file is read. |
3-Turn Compiler LagFixing a compile error takes 3 full turns:edit → bash("cargo check") → edit (fix) |
1-Turn Compiler LoopCoalesces parallel edits and runs cargo check --all-targets in tool_result. Multi-line rustc borrow-checker diagrams and help: consider & hints return on Turn 1. |
| Prompt Bloat & KV-Cache RewindsLSP extensions register 8–13 new tools, adding 3,500–4,100 tokens to Turn 1 and evicting prompt caches when tools toggle. | 0 Tools, 0 Tokens, Append-OnlyPi's 4 core tools stay untouched. State is stored out-of-context in session DAG entries; inference prompt caches (vLLM, llama.cpp) stay intact. |
Formatting DesyncRunning rustfmt mid-turn shifts line numbers for subsequent edits; running it out-of-band triggers stale line errors next turn. |
Deferred agent_end FormattingRuns rustfmt after the turn finishes, tracks child mod files, updates SHA-256 hash chains, and supplies re-read recovery hints if lines wrapped. |
Design Philosophy
1. Hooks Over Tools (4 active tools stays 4 active tools)
Coding models are heavily post-trained on pi’s four core primitives (read, write, edit, bash). Extensions that register 8–13 custom discovery or diagnostic tools (symbol_search, module_report, lsp_diagnostics) add thousands of tokens to the Turn-1 prompt and dilute tool selection—especially for smaller or locally hosted models that were fine-tuned on the standard 4-tool action space. pi-rust-guardian leaves the tool registry untouched and attaches guardrails directly to read, write, and edit.
2. Strict KV-Cache Prefix Integrity
Local inference servers (llama-server, SGLang, vLLM) and cloud prompt caches rely on exact token-prefix matching:
- Extensions that inject ephemeral diagnostic messages via the
contextevent cause tail cache rewinds when those messages are dropped or mutated on subsequent turns. - Extensions that dynamically activate "lazy tools" mid-session mutate the
"tools"array at the top of the system prompt, invalidating the entire KV cache.
pi-rust-guardian never hooks context and never mutates the active tool list. Compiler diagnostics are appended permanently to the edit or write tool_result that triggered them, and internal state is stored out-of-context in toolResult.details and custom session DAG entries.
3. rustc (--all-targets) Over 1-Line LSP Diagnostics
Language server (rust-analyzer) diagnostics are optimized for IDE squiggles and often condense errors into single-line summaries (E0308: mismatched types). For an LLM fixing Rust code, the most valuable signal is rustc’s full multi-line span output—specifically borrow-checker lifetime diagrams and compiler-generated help: consider borrowing here: &value fix suggestions. pi-rust-guardian invokes cargo check --all-targets --message-format=json to surface rich rustc error spans while stripping out warning noise.
Architecture & How It Improves Agent Performance
1. Two-Tier Content-Addressed Read-Guard (readSnapshots + full Coverage)
A naive Read-Guard only tracks a Set<string> of file paths the agent has touched. pi-rust-guardian tracks SHA-256 content digests alongside a boolean full coverage flag (SnapshotEntry { hash, full, formattedSinceRead }):
- Blocks Blind Hallucinated Edits: If the agent attempts to
editorwritean existing.rsfile it has not read in the current session, the call is blocked intool_callbefore disk I/O occurs. - Catches Out-of-Band File Drift: On every
editorwrite, the file’s current SHA-256 hash on disk is compared against the recorded snapshot. If the file was modified outsideread/edit/write(for example viasedorcargo fixinbash, or by the user in an external editor), the hash mismatch is caught and the agent is forced to re-read the file before patching stale lines. - Prevents Partial-Read File Truncation: When a
.rsfile is read withoffset/limitor truncated (details.truncation.truncated === true),pi-rust-guardianrecordsfull: false. Whenfull === false, targetededitcalls are allowed, but full-filewriteoverwrites are blocked until the agent reads the complete file.
2. Compaction-Aware Context Synchronization (syncFromBranch)
When a long session hits the context limit and pi compacts the history, early turns are replaced by a lossy summary while recent turns starting from compactionEntry.firstKeptEntryId remain verbatim in context.
- On
session_start,session_tree, andsession_compact,pi-rust-guardianlocates the latest compaction boundary (firstKeptEntryId). - It scans
assistanttool-call metadata across the branch, but only creditstoolResultsnapshots inside the active post-compaction window. - Automatic Coverage Downgrading: If the agent performed a full
readofsrc/lib.rson Turn 3 (now compacted out of context) and a smallediton Turn 30 (still in the active window),src/lib.rsis downgraded fromfull: truetofull: false. The agent can continue making surgicaleditcalls around the code it recently touched without being forced into an unnecessary re-read, but it is blocked from overwriting the entire file withwritefrom compacted memory.
3. Coalesced, Per-Crate Serialized Compiler Feedback
By running cargo check --all-targets inside tool_result, diagnostics are returned in the same turn as the edit or write rather than requiring a separate bash("cargo check") turn.
- 25ms Parallel Batch Coalescing: In
pi, when an agent updates a struct definition and its callers across 3.rsfiles in a single turn (edit("a.rs"),edit("b.rs"),edit("c.rs")), those edits execute in parallel. Without coalescing,a.rswould triggercargo checkbeforeb.rsandc.rsfinish writing to disk—reporting false errors on an intermediate state and spawning 3 concurrentcargoprocesses that contend ontarget/.cargo-lock. pi-rust-guardianincrements a per-Cargo-root sequence counter (mutationSeqByRoot), waits25ms(BATCH_SETTLE_MS) for sibling writes in the same turn to land on disk, and verifies the sequence counter both before and inside a promise mutex (enqueueCargoTask). Earlier edits in the batch step aside cleanly, andcargo checkruns once on the final edit of the batch.- Unit & Integration Test Verification (
--all-targets): Plaincargo checkskips#[cfg(test)]modules, allowing agents to write broken unit tests without noticing. Passing--all-targetscompileslib,bin, examples, benches, and#[cfg(test)]tests together.
4. High-Signal, Warning-Free rustc Error Extraction
During an active multi-file refactor, a Rust crate often emits dozens of unused_import or dead_code warnings before printing the actual compile error at the bottom of stderr.
extractCargoErrorsparsescargo check --message-format=jsonNDJSON output and extractsmessage.renderedstrictly wheremessage.level === "error".- All compiler warnings are discarded so they never distract the model mid-refactor or push real errors past the 3,000-character / 6-error truncation cap.
- Spanless
rustcsummary messages ("aborting due to N previous errors","For more information about this error...") and Cargo boilerplate stderr lines are stripped, while substantive non-JSON failures (such asbuild.rspanics, linker errors, or brokenCargo.tomlmanifests) are preserved fromstderr.
5. Deferred rustfmt With Recursive Child-Module & Hash-Chain Tracking
Code formatting creates a classic conflict for coding agents: running rustfmt mid-turn shifts line numbers and breaks subsequent edit calls in the same turn, while running rustfmt at the end of a turn alters file bytes behind the model's back.
pi-rust-guardian resolves this in its agent_end pipeline:
- End-of-Turn Execution:
rustfmtonly runs atagent_endafter all tool calls in the turn have completed. - Edition Detection:
detectRustEditioninspects the crate’sCargo.toml(walking up to the workspace rootCargo.tomlifedition.workspace = trueis used, and respecting Cargo’s"2015"default when[package]omitsedition). - Recursive Child
modAwareness: Runningrustfmt src/lib.rsautomatically parsesmod foo;declarations and formatssrc/foo.rson disk. Before runningrustfmt,pi-rust-guardianverifies all tracked snapshots (validPreFmtSnapshots), skips formatting any crate where a sibling file was modified out-of-band (dirtiedCrateRoots), and then advancespreHash -> postHashacross every tracked file whose bytes changed during formatting. - Line-Wrap Recovery Hint: Advancing the snapshot to
postHashallows the agent to edit untouched functions in the next turn without a Read-Guard block. However, ifrustfmtwrapped a long single line into multiple lines and the agent later fails anedit(event.isError) trying to match the old unformatted line,tool_resultdetectsformattedSinceReadand appends a recovery hint explaining thatrustfmtreformatted the file and instructing the agent toreadthe target section.
6. Out-of-Context Session DAG & Symlink-Safe Persistence
- Zero-Token DAG State: Snapshot metadata (
{ hash, full }) is saved insidetoolResult.details.rustGuardian, andrustfmthash transitions (preHash -> postHash) are appended viapi.appendEntry("rust-guardian:fmt", ...). Becausepi’sconvertToLlm()stripsdetailsandcustomentries when projecting messages for the LLM, full state survives/treebranch switches,/fork,/resume, and process restarts at 0 token cost. - Hot
/reloadState: Runtime maps are stored onglobalThis[Symbol.for("pi.rust-guardian.runtime-state")]with schema migration, so running/reloadpreserves active snapshots. - Symlink-Safe Path Canonicalization:
canonicalizePathwalks up to the nearest existing ancestor directory and resolvesrealpathSync.nativebefore re-attaching non-existent child segments, ensuring pre-creationwritepaths and post-creationeditpaths resolve to identical keys inside symlinked directories or worktrees.
Comparison With Other Extensions
| Feature | pi-rust-guardian |
pi-lens |
@piex-dev/lsp |
@narumitw/pi-lsp |
|---|---|---|---|---|
| Extra LLM Tools Registered | 0 | 13 (8 active on Turn 1) | 1 (lsp) |
2 (lsp_diagnostics, lsp_fix) |
| Extra Skills Registered | 0 | 4 | 0 | 0 |
| Turn-1 Prompt Token Overhead | 0 tokens | ~3,500–4,100 tokens | ~300 tokens | ~400 tokens |
| Append-Only KV Cache by Default | Yes | Requires --lens-retain-injections + --no-lazy-tools |
Yes | Yes |
| Diagnostic Backend | cargo check --all-targets (rustc help: spans + unit tests) |
rust-analyzer + clippy + tree-sitter |
rust-analyzer LSP |
rust-analyzer LSP |
| Compiler Warnings Filtered Out | Yes (errors only) | Configurable | Yes | No |
| SHA-256 Read & Partial-Write Guard | Yes (compaction & /tree aware) |
Yes | No | No |
Deferred rustfmt + Hash Chains |
Yes (tracks child mod files) |
Yes (emits re-read nudge next turn) | No | No |
When to Use (and When Not to Use)
Good fit for:
- Rust-focused workflows in
piwhere you want compiler-in-the-loop verification (rustcerror spans, borrow-checkerhelp:hints, and--all-targetstest checking), automaticrustfmt, and Read-Guard protection with zero tool bloat. - Local inference setups (
llama.cpp,SGLang,vLLM) where minimizing Turn-1 prefill tokens and avoiding mid-session KV-cache invalidations directly improves response latency. - Models tuned for
pi’s 4 core tools (as well as frontier models like Claude Opus, GPT/Codex, DeepSeek, and Qwen) that already navigate codebases effectively usingrginbash+read/edit.
Not a fit for:
- Polyglot repositories:
pi-rust-guardianonly intercepts.rsfiles and Cargo workspaces. For multi-language repositories (TypeScript, Python, Go, etc.), usepi-lensor@piex-dev/lsp. - Agent-driven LSP/AST navigation: If you want the model to explicitly invoke tools like
ast_grep_searchorlsp_navigation(go-to-definition / find-references), this extension intentionally does not register any tools. - Uncached monoliths with >60s incremental checks: Because
cargo check --all-targetsruns after.rsedits settle, it works best on crates and workspaces where incremental checks complete in a few seconds.
Installation & Configuration
# Install from npm
pi install npm:pi-rust-guardian
# Or install from GitHub
pi install git:github.com/saldmy/pi-rust-guardian
# Or test for a single session without saving
pi -e npm:pi-rust-guardian
Environment Variables
PI_RUST_GUARDIAN_CARGO_TIMEOUT_MS— Timeout in milliseconds forcargo check --all-targets(default:60000):PI_RUST_GUARDIAN_CARGO_TIMEOUT_MS=120000 pi
Slash Commands
/rust-guardian— Show tracked.rsfiles, short SHA-256 hashes,[FULL]/[PARTIAL]read coverage, and the latestcargo checksummary.
Development & Testing
# Clone the repository
git clone https://github.com/saldmy/pi-rust-guardian.git
cd pi-rust-guardian
# Install dependencies
npm install
# Run type checking
npm run typecheck
# Run unit tests
npm test
License
MIT © 2026 Dmytro Salo