npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pi-edit

v1.1.0

Published

Claude Code-style editing discipline for Pi — read-before-write, TOCTOU protection, bash steering, post-edit diagnostics, schema-error recovery

Readme

pi-edit

Claude Code-style editing discipline for Pi.

Brings Claude Code's editing safety optimizations to Pi without removing what Pi already does better (edits[] multi-edit, Unicode fuzzy match, file-mutation queue, pluggable Operations, TUI diff previews).

What It Does

Pi-edit adds five safety layers via event hooks (no tool overrides):

  1. Read-before-write enforcement — tracks which files the model has read; blocks edit on unread files (so oldText always matches current contents) and blocks write to an existing unread file (so it can't clobber content the model has never seen). Writes that create new files are always allowed.

  2. TOCTOU protection — captures file mtime + size at read time; blocks edits when the file changed on disk since last read (external edits, linters, concurrent processes). For hot files (modified within ~2.5s of the read — the only case where an mtime+size comparison can miss a same-tick rewrite), a sha256 content hash is also recorded and the edit gate hash-confirms instead.

  3. Bash steering — inspects bash commands for file-mutating patterns (sed -i, awk > file, echo > file, perl -i, tee file, cp/mv/truncate/dd of= in command position) and blocks them with a hint to use edit or write instead. Read-only inspection (sed -n 'Np', awk filters without redirect) is allowed.

  4. Post-edit diagnostics — after each successful edit or write, runs a fast linter (eslint for TS/JS, ruff for Python) and appends findings to the tool output so the model can fix issues before continuing. Skipped entirely (and cached) when the linter isn't installed in the project.

  5. Schema-error recovery hints — watches for Pi schema validation failures (e.g. wrong parameter names like file or file_path instead of path) and steers a corrective message to the model. The failed tool result itself is left untouched.

Installation

# Via npm (recommended)
pi install npm:pi-edit

# Via GitHub URL (fallback)
pi install https://github.com/yeshao/pi-edit

Flags

| Flag | Description | |------|-------------| | --pi-edit-no-readguard | Disable read-before-write + TOCTOU | | --pi-edit-no-bashguard | Disable sed/awk steering | | --pi-edit-no-diagnostics | Disable post-edit lint/typecheck feedback | | --pi-edit-no-telemetry | Disable telemetry event logging |

How It Works

Read-before-write

Model calls read("file.txt")
  → pi-edit records: mtime, size, fullRead flag
    (+ content hash if the file was modified within the last ~2.5s)

Model calls edit("file.txt", edits=[...]) or write("file.txt", ...)
  → pi-edit checks: was this file read? has it changed on disk?
  → if unread or stale → block with helpful message
  → if clean → allow

Paths are normalized the same way Pi's own tools normalize them (~ expansion, leading-@ strip, unicode-space normalization), so the guard tracks the same file Pi actually reads and writes.

TOCTOU detection

  • Fresh: mtime and size match the recorded read (or the recorded hash matches) → edit allowed.
  • Changed: mtime, size, or hash differs → file was modified since last read → block and require re-read.
  • Unreadable: file can't be stat'd/hashed (I/O error) → fail closed, require re-read.

mtime+size alone can miss exactly one case: a rewrite landing within the filesystem's timestamp granularity (HFS+ 1s, FAT 2s) of the recorded mtime with the same byte size. That's only possible when the file's mtime is near "now" at read time, so only those hot reads pay for a content hash — cold files (the common case when exploring a repo) are tracked with a single stat call, no extra file reads.

Bash steering

Two detection strategies:

  1. Inherently mutating patterns — blocks sed -i/--in-place, perl -i, awk with a redirect, and dd of=, truncate, cp, mv, install in command position (start of command or after ;, &, |, $(). Subcommands and arguments never match: npm install, git mv, and grep truncate are all allowed. Option detection requires a real option token, so hyphenated filenames (my-file.txt) don't look like -i.
  2. Redirect target analysis — extracts >, >>, and tee targets, strips quoted strings to avoid false positives (e.g. echo "a > b"), and blocks writes outside the project directory. Targets that expand at run time (~/…, $VAR, $(cmd)) can't be verified statically and are blocked (fail closed). Scratch spaces (/tmp, /var/tmp) and /dev, /proc, /sys are permitted.

Post-edit diagnostics

Model calls edit("src/foo.ts", ...)
  → edit succeeds
  → pi-edit runs eslint on src/foo.ts
  → if issues found → appended to tool result:
     [diagnostics for src/foo.ts — fix before continuing]
     3:10  error  'x' is defined but never used  @typescript-eslint/no-unused-vars

Supported: eslint for .ts/.tsx/.js/.jsx, ruff for .py.

  • eslint is only invoked when a local install exists (node_modules/.bin/eslint, walking up from cwd); the answer is cached per project, so projects without eslint pay nothing per edit and never see npx errors as fake findings.
  • Findings are taken from stdout only — tool noise on stderr is never surfaced.
  • Debounced at 250ms per file; shared in-flight runs; 30s timeout ceiling; the run is tied to Pi's abort signal so Esc interrupts it.

Schema-error recovery

Pi validates tool arguments before extensions see the call, and a validation failure bypasses tool execution (and the tool_result hook) entirely. pi-edit therefore watches tool_execution_end: when a read/edit/write fails with a schema-validation error, it steers a corrective message to the model, e.g.:

[pi-edit] The edit call failed schema validation because of a wrong parameter name.
Pi's edit tool takes 'path' (not 'file' or 'file_path'). Retry with: edit path=src/foo.ts

The failed tool result stays an error — pi-edit never pretends a failed call succeeded.

Telemetry

pi-edit records a structured, append-only event log at every decision point in its five safety layers so you can measure how often each guard fires, how often users opt out via flags, and whether diagnostics actually catch anything.

Local only. Nothing is ever sent over the network. Events are written to ~/.pi-edit/events.jsonl (one JSON object per line, UTF-8). Set $PI_EDIT_TELEMETRY_PATH to override the location (useful for tests). Disable logging entirely with the --pi-edit-no-telemetry flag.

What is logged. Each event carries a timestamp, a per-session id, the event name, and event-specific fields:

| Event | Meaning | |-------|---------| | session_start | Extension instantiated (includes which guards are off) | | read_tracked | A read was recorded for gating | | read_untracked | A read event arrived with no usable path key | | edit_gate_decision | An edit/write was allowed or blocked (and why) | | partial_read_warning | An edit after a partial read | | bash_gate_decision | A bash command was allowed or blocked | | diagnostics_run | A post-edit lint run completed (outcome, duration, findings) | | schema_recovery_hint | A schema-validation recovery hint was steered |

Redaction. File paths are logged relative to the project cwd when they fall inside it, and as absolute paths only when they fall outside (mirroring the existing inside/outside-project logic). For bash-steering events, only the first 120 characters of the command are logged — shell commands can contain inline secrets. File contents and hashes are never logged.

Rotation. When events.jsonl exceeds 5 MiB it is renamed to events.jsonl.1 (overwriting any previous .1) and a fresh file is started. One backup generation is kept; there is no compression or time-based rotation.

Summarize. Run npm run stats to print a plain-text summary (block rates, flag opt-out rates, diagnostics hit rate, schema-recovery counts, partial-read warnings). Pass --json for machine-readable output, or point at a custom file as the first argument.

Partial-read warnings

If the model edits a file it only saw partially (read with offset/limit, or a long file Pi truncated — detected via the read result's truncation details), pi-edit logs a warning that the model may lack full-file context. Debounced to once per minute per file, and suppressed in TUI mode (raw console output would corrupt the interface).

Known behaviors under parallel tool execution

Pi executes tool calls in parallel batches by default. Two consequences, both self-healing:

  • read(f) and edit(f) issued in the same assistant message: the edit gate runs before the read executes, so the edit is blocked with "read it first" and succeeds on the retry.
  • Two edits to the same file in one batch: their post-edit baseline refreshes race, which can record a stale baseline and cause one false "changed on disk" block on the next edit.

Dependencies

None — runs entirely via Pi's event hooks and Node.js built-ins.

Project Structure

pi-edit/
├── package.json          # Pi extension manifest
├── tsconfig.json
├── vitest.config.ts
├── README.md
├── src/
│   └── index.ts          # Extension entry (all five safety layers + telemetry)
├── scripts/
│   └── pi-edit-stats.mjs # summarize ~/.pi-edit/events.jsonl (`npm run stats`)
└── tests/
    ├── classify-bash.test.ts  # bash-steering + pure-helper unit tests
    └── telemetry.test.ts      # telemetry, redaction, rotation, stats tests

Development

# Install dependencies
npm install

# Type check
npm run typecheck

# Run tests
npm test

License

MIT