pi-agent-edit

An agent-built edit tool for Pi: lossless tolerant matching, actionable ambiguity recovery, stale-read guards, and recoverable file writes.

Packages

Package details

extension

Install pi-agent-edit from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-agent-edit
Package
pi-agent-edit
Version
0.2.12
Published
Nov 1, 2026
Downloads
1,534/mo · 385/wk
Author
bjstan1979
License
MIT
Types
extension
Size
180.6 KB
Dependencies
0 dependencies · 2 peers
Pi manifest JSON
{
  "image": "https://cdn.jsdelivr.net/npm/pi-agent-edit@0.2.12/assets/preview.png",
  "extensions": [
    "./index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-agent-edit

An edit tool built by an agent, for agents.

A replacement for Pi's built-in edit tool, developed and iterated by AI agents under human direction and review. Instead of making an agent re-read a file after every mismatch, it provides actionable recovery hints while keeping untouched file content intact.

简体中文 · Changelog · MIT license

Built by agents

AI models participating in development, using the names supplied by the maintainer:

  • claude-opus-5.5
  • gemini-4-Argon
  • gpt-6.1-sol

Agents helped implement, test, review, and refine the tool they themselves use to edit code. These are development credits, not endorsements by the model providers. Human review and tests still matter: agent-built does not mean infallible.

The extension runs locally. It does not call these models or require its own API key or model subscription.

Install

Published on npm, and listed in the Pi package gallery:

pi install npm:pi-agent-edit

Or install for the current project only:

pi install -l npm:pi-agent-edit

Try it for one invocation without saving an installation:

pi -e npm:pi-agent-edit

Before publication, use a local source checkout instead: pi install ./pi-agent-edit.

Restart Pi or run /reload after installing. This package intentionally registers edit and a thin wrapper around the official read tool, shadowing the built-ins; shadowing notices are expected. The read wrapper binds version tracking to the actual bytes read, without a second disk read. Avoid loading another replacement for either tool, and remove any manually copied version before installing from npm.

To uninstall, run pi remove npm:pi-agent-edit, then restart or /reload to restore the built-in tools (provided no other override remains).

Compatibility: tested with Pi 1.3.0 (@earendil-works/pi-coding-agent). Use a current Pi release. Tool overriding, argument formats, and host APIs can differ in older releases or third-party forks; compatibility with every Pi version is not guaranteed. Pi supplies the host peer dependencies; there is no separate build step.

Why another edit tool?

Agents rarely get every copied string exactly right. They encounter indentation differences, repeated blocks, copied line-number prefixes, and files changed by another process. This tool makes those cases recoverable without silently normalizing unrelated content.

  • Lossless tolerant matching. Tries exact text, trailing-whitespace tolerance, indentation tolerance, then relaxed whitespace/Unicode matching. Normalization locates the match; it does not rewrite the whole file. Unchanged lines in tolerant replacements retain their original content.
  • Actionable ambiguity errors. Shows context for up to five occurrences, attempts unique whole-line oldText suggestions within six extra lines and 400 characters, and lists up to 12 matching line numbers. Retry with a listed nearLine without re-reading while the file hash stays unchanged. Same-line duplicates require distinguishing context, not a line-number tie-break.
  • Useful mismatch feedback. Suggests similar numbered regions and line-by-line differences. Detects some likely already-applied edits without treating them as success.
  • Agent-friendly input. Handles copied line-number prefixes, camelCase/snake_case argument aliases, integer strings for line numbers, and smart relative re-indentation.
  • More than search and replace. Supports line-range replacement/deletion, insertion, replaceAll, and dryRun.
  • Stale-read guards. Line-number edits require a sufficiently fresh read/write view. Session changes and compaction clear that view; hash-bound ambiguity offers are separately invalidated by content changes.
  • Safer writes. All edits resolve against the original file and are applied all-or-nothing. Uses temporary-file/fsync/rename writes where possible, detects concurrent content changes, and uses recoverable in-place writes when necessary. Preserves BOM, CRLF, permissions, ownership where possible, symlinks, and hard-link identity via in-place writing.
  • Readable results. Compact numbered diffs support the next edit without another read. JSON and whitespace-sensitive file checks provide warnings, not a substitute for parsing or tests.
  • Encoding guard. Rejects non-UTF-8 files rather than corrupting their non-ASCII bytes.

Usage

The agent calls edit; you do not need to run a separate command for each change. The original text-replacement format remains available:

{
  "path": "src/app.ts",
  "edits": [
    { "oldText": "foo(1)", "newText": "foo(2)" }
  ]
}

Additional locators and options:

{
  "path": "src/app.ts",
  "dryRun": true,
  "edits": [
    { "oldText": "x = 1", "newText": "x = 2", "nearLine": 120 },
    { "oldText": "oldName", "newText": "newName", "replaceAll": true },
    { "startLine": 10, "endLine": 12, "newText": "replacement" },
    { "afterLine": 0, "newText": "import x from 'x';" }
  ]
}

These illustrate independent edit forms: an actual batch must not contain overlapping edits. Every edit targets the file before the call, not the output of earlier edits in the same batch. Use exactly one locator per edit. endLine requires startLine; nearLine and replaceAll only apply to oldText. Empty newText deletes a matched region or line range. afterLine: 0 inserts at the beginning; line insertion uses line-oriented newline semantics.

Continuing with line numbers

  • A successful tracked read establishes the exact file version consumed by the tool, including when using offset/limit. This is a version guard, not proof that every line was displayed: verify your intended coordinates.
  • A successful write establishes a view from its input content, so a newly created file can immediately be edited by line number. Files created with shell commands need a read; session changes and compaction also clear views. Failed tool calls grant no view: if a host's write + then_run writes the file but its follow-up command fails, read it before editing by line number.
  • After an edit changes line counts, only new-file coordinates actually printed in the latest successful edit report can be used without another read. Deleted old-file lines, omitted hunks, truncated middles, and ranges crossing unseen gaps do not qualify. A dryRun grants no new coordinates.
  • An external content change invalidates these views. Use a fresh read or an oldText locator; preserving size and modification time does not bypass the content hash.

Recovering from repeated text

If oldText matches several places, the error offers numbered snippets and, when available, a unique larger oldText. Either:

  1. Copy the unique suggestion and preserve the extra context unchanged in newText.
  2. Keep oldText and use a listed matching line as nearLine while the file remains unchanged.
  3. Set replaceAll: true if every occurrence should change.

nearLine cannot distinguish matches that start on the same line. If the file changes after an error, its hash-bound line offers no longer authorize a stale-line retry.

Safety and limitations

  • Tolerant matching is not a semantic code transformation. Review the diff and run your compiler/tests, especially for Python, YAML, and Makefiles.
  • Concurrent-change detection is best-effort, not a file lock. POSIX does not provide atomic compare-and-rename; a race remains between the final check and rename.
  • In-place fallback is recoverable on ordinary I/O failure, not atomic. A forced termination or power loss can interrupt it before rollback. Recovery backups normally exist only during the call; rollback failure reports the retained backup path.
  • Network filesystems and special mounts can have different rename/fsync guarantees. Unsupported directory fsync is skipped.
  • Non-UTF-8 files are deliberately unsupported. JSON/whitespace diagnostics are warnings and heuristics, not full syntax validation.

Development

From a source checkout with Pi's peer dependencies available:

npm install
npm test
npm run typecheck
npm pack --dry-run

The same three commands run in CI (.github/workflows/pi-agent-edit.yml) on Node 22 and 24 for any change under pi-agent-edit/. Node 20 is not tested because the Pi peer dependency requires Node >= 22.19.0 (node:fs named export globSync).

The pure editing engine and writer can be tested independently of Pi. Extension integration tests need the host peer dependencies. Ownership-sensitive tests skip when not run as root. Tests and development configuration are intentionally excluded from the npm tarball.

License

MIT. Maintained by bjstan1979.