@luan.sh/pi-fileops
Native file operations for Pi, with structured apply_patch diffs
Package details
Install @luan.sh/pi-fileops from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@luan.sh/pi-fileops- Package
@luan.sh/pi-fileops- Version
0.3.8- Published
- Sep 12, 2026
- Downloads
- not available
- Author
- cfcluan
- License
- MIT
- Types
- extension
- Size
- 25.8 MB
- Dependencies
- 2 dependencies · 1 peer
Pi manifest JSON
{
"image": "https://pi.luan.sh/media/previews/pi-fileops-fd6d191a4760.png",
"video": "https://pi.luan.sh/media/previews/pi-fileops-fcf6128b5f4c.mp4",
"extensions": [
"./src/extension.ts",
"./node_modules/@luan.sh/pi-libtui/src/extension.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@luan.sh/pi-fileops
@luan.sh/pi-fileops adds an apply_patch tool to Pi. The model writes a
Codex-style patch (*** Begin Patch ... *** End Patch) and a native Rust
binary applies it: parsing, context matching, filesystem writes, and
partial-failure tracking. The same operation is available as a direct Pi tool
and, when Code Mode is installed, as tools.apply_patch(...) inside exec.
Preview

Install
pi install npm:@luan.sh/pi-fileops
Requires a Rust toolchain (https://rustup.rs). The apply-patch binary builds
itself on first use under Pi's agent directory (native/apply-patch/<version>/).
Set PI_APPLY_PATCH_BIN to use a prebuilt binary.
Optional companion: pi install npm:@luan.sh/pi-code-mode adds the exec
tool and can move apply_patch under it; without it apply_patch is always a
direct tool.
Direct and Code Mode calls
The extension registers apply_patch as an ordinary Pi tool and also registers
a Code Mode execution adapter. Code Mode alone decides which one the model
sees:
- Without Code Mode, or when
apply_patchis not selected in Code Mode'spi-code-mode.toolssetting, the model calls the direct tool with{ "input": "...patch text..." }. - When it is selected there and
execis active, it disappears from the direct tool list and becomestools.apply_patch("...patch text...")insideexec. The adapter accepts only a raw string; any other input is rejected.
The adapter forwards execution to the same tool implementation and reuses this
package's diff presentation for the nested trace. It does not read Code Mode
settings or change the tool hierarchy. A partial failure inside exec is
reported as a thrown error to the script after the result is published.
Patch format
Every input starts and ends with the patch markers. Use one action header per file:
*** Begin Patch
*** Add File: notes/today.txt
+A new file.
*** Update File: src/main.ts
@@
-old line
+new line
*** Delete File: obsolete.txt
*** End Patch
Supported actions are Add File, Update File, Delete File, and Move to.
Put *** Move to: ... immediately after an *** Update File: ... header.
Order multiple hunks for one file from top to bottom. Context and indentation
are literal text. Every line of an Add File body must start with +.
Paths are resolved relative to the Pi session cwd. Absolute paths are passed
through unchanged. A leading @ and surrounding quotes are stripped from
paths for compatibility with Pi path arguments.
The direct tool accepts input; the aliases patchText and patch are
normalized to input before execution.
Execution and results
The Rust binary owns parsing, matching, filesystem mutations, and partial failure tracking. The TypeScript side owns Pi registration, argument normalization, per-file mutation queues, process control, and result shaping.
Before running, the tool takes Pi's file mutation queue for every path the
patch touches (including move targets), so concurrent edits to the same file
are serialized. The binary is spawned with the patch on stdin and
PI_APPLY_PATCH_JSON=1; its final stdout line is the structured result.
A successful result reports changed, created, deleted, and moved files, fuzz, and the committed unified diff. While queued or running, the transcript shows a preview built from the input patch; once complete, it renders the native diff, so line ranges describe the files actually written.
If an early action succeeds and a later one fails, the tool returns a
partial_failure result listing the committed prefix and the failed targets.
The text result tells the model to re-read failed files before retrying and
not to reapply successful actions. A tool_result hook marks direct
partial-failure results as errors. If nothing was committed, the tool throws
with the binary's diagnostic (bounded to 8 KiB).
The direct tool declares a Lark grammar as constrained sampling, so providers
that support native freeform tools receive the raw patch text. Other providers
receive the ordinary { input: string } function schema.
Settings and keybindings
This package has no settings and registers no commands or key-bound actions.
Its only configuration is the PI_APPLY_PATCH_BIN environment variable.
API
The default export of src/extension.ts is the Pi extension. src/index.ts
exports:
createApplyPatchTool()andregisterApplyPatchTool(pi, tool?)for Pi registration.executePatchWithRust({ cwd, patchText, signal?, binary? })for the native execution boundary; it throwsExecutePatchErroron failure.resolveApplyPatchBinary()for the default/override binary lookup.createApplyPatchRunningResult,createApplyPatchSuccessResult, andcreateApplyPatchPartialFailureResultfor building tool results.- Types
ApplyPatchToolDetails,ApplyPatchOperation,ApplyPatchFileResult,ApplyPatchRunningDetails,ApplyPatchSuccessDetails,ApplyPatchPartialFailureDetails, andExecutePatchResult.
Result details are version: 1 and JSON-serializable. Every status carries
input.operations, affectedPaths, files (per-file applied/failed),
counts, progress, and timing.durationMs. success and partial_failure
add result (the native result); partial_failure adds
failure.{message, failedTargets}.
Layout
| Responsibility | File |
|---|---|
| Extension entry: registers tool, result hook, and Code Mode adapter | src/extension.ts |
| Tool definition, argument normalization, mutation queues | src/tools/apply-patch/definition.ts |
| Result shaping and details types | src/tools/apply-patch/result.ts |
| Transcript rendering (input preview, native diff) | src/tools/apply-patch/presentation.ts |
| Spawning the native binary and parsing its JSON | src/executor.ts |
| Binary lookup and first-use build | src/binary.ts |
| TypeScript patch parsing for previews and path resolution | src/patch.ts |
| Lark grammar for freeform providers | src/grammar.ts |
| Code Mode adapter | src/code-mode-adapter.ts |
Shared types and ExecutePatchError |
src/types.ts |
| Public exports | src/index.ts |
Troubleshooting
- Binary fails to build: make sure
cargois installed and onPATH, or setPI_APPLY_PATCH_BINto an executable file. - The tool stays direct: install
@luan.sh/pi-code-mode, selectapply_patchin itstoolssetting, and restart the session. Only Code Mode owns placement. - The tool is missing entirely: check Pi's active tool selection. A strict
--toolslist must includeapply_patchorexec, depending on which path you want to use. - A patch partially failed: preserve the successful edits, read each failed target again, and retry only the failed actions.
- A patch is rejected: check the begin/end markers, action headers, exact
context lines, and the required
+prefix for added-file content.
Develop
Source: https://github.com/luan/agents, directory
harnesses/pi/agent/packages/pi-fileops. Run bun run typecheck and
bun test test in that directory.