@tryinget/pi-snapshot-edit
Snapshot-bound occurrence-selector editing tools for Pi agents
Package details
Install @tryinget/pi-snapshot-edit from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@tryinget/pi-snapshot-edit- Package
@tryinget/pi-snapshot-edit- Version
0.6.0- Published
- Oct 10, 2026
- Downloads
- 310/mo · 201/wk
- Author
- tryinget
- License
- SEE LICENSE IN LICENSE
- Types
- extension
- Size
- 95.3 KB
- Dependencies
- 0 dependencies · 4 peers
Pi manifest JSON
{
"extensions": [
"./extensions/snapshot-edit.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
summary: "Snapshot-bound exact-selector editing tools for Pi agents." read_when:
- "Installing or evaluating pi-snapshot-edit."
- "Choosing a fail-closed text editing protocol." system4d: container: "A standalone-friendly Pi extension package." compass: "Make duplicate text deterministic without hiding stale-file conflicts." engine: "raw snapshot read -> exact selector edit -> fresh revision." fog: "Ordinary filesystems cannot provide lock-free compare-and-swap against arbitrary writers."
@tryinget/pi-snapshot-edit
License notice: this package is not standard open source. Its bundled license is an MIT-style license with a restrictive rider that denies rights to named AI companies, related entities, and people acting for them. Review LICENSE before use or redistribution. The project does not describe this restricted license as plain MIT.
pi-snapshot-edit provides Protocol B through namespaced tools and, by default, owns the standard tool names after session_start:
snapshot_read/read— return onerevision:<alias>header followed by raw UTF-8 file text, without line numbers or gutters;snapshot_edit/edit— apply exact replacements and anchored insertions against that immutable full-file snapshot.
Library use (hosts other than Pi)
The protocol's engine is exported for hosts that are not Pi, such as the polis society host:
import { SnapshotEditService } from "@tryinget/pi-snapshot-edit/service";
const service = new SnapshotEditService();
const read = await service.read({ path: "src/example.ts" }, cwd); // read.text starts with "revision:<alias>"
await service.edit({ path: "src/example.ts", base: "amber", edits: [/* Protocol B operations */] }, cwd, signal);
After a reload or resume, a host restores the revisions its transcript already holds; each rehydrates only while its file still has exactly those bytes:
import { revisionsFromMessages, SnapshotEditService } from "@tryinget/pi-snapshot-edit/service";
service.restoreRevisions(revisionsFromMessages(transcriptMessages, cwd));
Only ./service (with TypeScript declarations) and ./package.json are exported; the Pi extension is still loaded from package.json#pi.extensions. The license rider below applies to library use as well.
Protocol B
snapshot_read({"path":"src/example.ts"})
revision:amber
const value = 1;
return value;
return value;
{
"path": "src/example.ts",
"base": "amber",
"edits": [
{
"op": "replace",
"oldText": "return value;",
"occurrence": 2,
"newText": "return value + 1;"
},
{
"op": "insert_after",
"anchorText": "const value = 1;",
"newText": "\nconst increment = 1;"
}
]
}
A selector with exactly one match may omit occurrence. A duplicate selector requires a positive 1-indexed occurrence. Partial-line and multi-line selectors are exact. All selectors resolve against the same immutable base before any mutation.
Two deterministic caller slips are normalized instead of failing: base may carry the rendered revision: header prefix or surrounding whitespace (the bare alias word is canonical), and a missing op is inferred when exactly one of oldText/anchorText is present before schema validation. Ambiguous shapes still fail closed.
Selectors and newText normalize to the file's LF or CRLF style. Unrelated text bytes are unchanged. Missing, invalid, or out-of-range selectors; overlapping replacements; shared insertion points; insertion on a replacement boundary/interior; and no-op edits fail closed. There is no fuzzy matching or automatic rebase.
Failures that say where to look
A failed edit writes nothing and reports every problem of the batch at once, each with what the revision actually holds there, so one corrected retry can succeed:
- an ambiguous or out-of-range selector lists each match with its line and text (
Matches: #1 line 2: "return x;"; #2 line 4: "return x;"), up to eight; - a selector with no exact match names its nearest near miss: a whitespace-only difference (with the revision's exact text), the selector line where the closest candidate diverges, or its longest present prefix. None of these is ever applied;
- conflicting operations name both edits, their lines, and the kind of conflict;
- a revision of another file names the newest revision of the file asked for; a stale revision names the revision the file holds now, if this session issued one (for example after your own edit).
A successful edit previews the changed regions of the new revision, as raw text under one @@ lines A-B @@ header per hunk with two lines of context, instead of the top of the file; details carry changedLines. Regions beyond the 8KB preview cap are counted, and an oversized region shows its leading lines and where it continues.
Read pagination remains capped at 2,000 lines, and the complete serialized result—including the revision header and any truncation notice—is capped at 50KB. Space for framing is reserved before raw lines are added. A single line that cannot fit on that safe page fails explicitly because exact raw pagination cannot split it with a line-offset API. Edit previews contain raw text without gutters when they fit an independent bounded preview; otherwise success returns a non-throwing omission notice.
Safety contract
- A read retains the complete canonical file bytes and binds them to a session-local alias plus SHA-256 digest. A revision is content-addressed: it is valid exactly while its file holds the bytes it names.
- Edits bind the alias to the canonical path and immutable snapshot text. An alias whose bytes are no longer held (evicted, or issued before a reload) rehydrates from the file only when the file's current digest is the recorded one; a file replaced by identical bytes is the same revision.
- Every batch operation resolves and validates before writing.
- Current bytes and identity must still match inside Pi's per-file mutation queue.
- The desired bytes must pass the snapshot-store byte budget and text decoding validation before commit.
- Commit uses a same-directory temporary file, fsync, mode preservation, a best-effort digest/identity recheck from one opened handle, and atomic rename.
- Success creates a fresh revision.
The implementation preserves valid UTF-8 bytes outside edits, UTF-8 BOM, LF/CRLF style, final-newline shape unless explicitly selected, and mode bits. It rejects binary/non-UTF-8 files, bare-CR or mixed-EOL files, hard-linked targets, stale bytes, replaced inodes, cancellation before commit, and byte-identical no-ops.
Revisions survive eviction, reload, and session resume as ledger records (alias, canonical path, digest); after a reload the extension rebuilds the ledger from the session's own read and edit results, and new aliases never reuse an earlier one. /snapshot-edit clear drops the held bytes and the ledger of the running process. The last digest/identity check is best-effort pre-rename detection, not filesystem compare-and-swap: a non-cooperating writer can change the path in the residual window between that check and rename. Atomic rename also cannot preserve every ACL, xattr, sparse-file, open-descriptor, or watcher behavior. Pi's queue cannot exclude non-cooperating processes.
Standard-tool ownership and dogfood
Loading the extension always registers snapshot_read and snapshot_edit. At session_start it also replaces built-in read and edit by default when both built-in owners are visible, while preserving the host's active-tool selection exactly. A deliberate --tools allowlist that omits either built-in leaves the extension namespaced-only instead of aborting startup.
Set PI_SNAPSHOT_EDIT_OVERRIDE to 0, false, off, or no for namespaced-only operation:
PI_SNAPSHOT_EDIT_OVERRIDE=off pi
Legacy explicit enable surfaces remain available and may add read and edit to the active-tool selection:
/snapshot-edit override
PI_SNAPSHOT_EDIT_OVERRIDE=1 pi
pi --snapshot-edit-override
Standard-name ownership requires positively identified built-in owners for both tools and refuses any visible non-built-in read or edit owner. Explicit enablement also fails closed when either built-in owner is absent; only implicit default startup may degrade to namespaced-only operation. Unsupported reads fail closed. Restart with an opt-out value for namespaced-only operation, or disable/uninstall the extension to restore host ownership. Resumed line-coordinate calls and top-level legacy edit calls are rejected with instructions to reread and issue Protocol B. Nested Protocol B oldText remains valid.
Clipboard image lift
Host Ctrl+V writes a TMPDIR/pi-clipboard-<uuid>.<ext> path into the editor. This package does not teach snapshot read to decode images. Instead an input transform lifts those tmpdir placeholders into user-message ImageContent and rewrites the path to a <file name="..."> marker. Snapshot revisions stay UTF-8. Disable with PI_SNAPSHOT_EDIT_IMAGE_LIFT=off. See ADR 0002.
Install and verify
npm ci
npm run check
npm run release:check
pi install /absolute/path/to/pi-extensions/packages/pi-snapshot-edit
release:check packs the exact npm artifact, installs it under isolated Pi and npm roots, then runs provider-free Protocol B smoke checks in three fresh Pi processes, including a restricted --tools allowlist that omits built-in edit. It also checks the packed whitelist, npm audit --omit=dev, and npm publish --dry-run; it never publishes.
Then run /reload and verify with real read/edit calls on a disposable file. Candidate lanes should report this command rather than altering the controller's Pi install.
Evidence and architecture
- ADR 0001 — Snapshot-bound exact-selector editing
- Protocol evaluation results
- Standard override dogfood
- Content-free historical failure baseline
Historical benchmark documents may describe Protocol A line coordinates as an evaluated alternative; they are not current runtime guidance.
Package identity
- npm:
@tryinget/pi-snapshot-edit - monorepo path:
packages/pi-snapshot-edit - release component:
pi-snapshot-edit