@hiennguyen9874/pi-multi-edit0.1.3
Pi coding-agent extension: exact string multi-edit tool with fuzzy matching and diff previews.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-multi-edit
A Pi coding-agent extension that registers an edit tool for exact string replacement in files, with fuzzy matching, diff rendering, and TUI preview support.
Features
- Exact string replacement — each
edits[].old_stringmust match exactly, including whitespace and newlines - Fuzzy matching — configurable similarity-based fallback with smart quote / Unicode normalization
replace_all— replace every occurrence when desired- Diff previews — async diff computation shown in the TUI before tool execution
- Unified patch output — standard unified patch in tool result details
- Line ending preservation — auto-detects and preserves
\nvs\r\n - BOM preservation — strips BOM for matching, restores on write
- Configurable settings — global (
~/.pi-agent/settings.json) and project (.pi/settings.json) - Pluggable file operations — override read/write/access for remote editing (e.g. SSH)
- Legacy argument aliases — supports
path,oldText,newText,old_str,new_str,change_all, and camel-case edit items - Multi-edit support — atomically applies small, non-overlapping replacements and warns above five per call
- macOS path variants — handles NFD unicode normalization, curly quotes, and narrow no-break spaces in screenshot filenames
Install
As a Pi package:
pi install npm:@hiennguyen9874/pi-multi-edit
Or as an npm dependency:
npm install @hiennguyen9874/pi-multi-edit
Development
npm install
npm test
npm run test:watch
Tool input
{
"file_path": "src/example.ts",
"edits": [
{
"old_string": "first text to replace",
"new_string": "first replacement"
},
{
"old_string": "second text to replace",
"new_string": "second replacement"
}
]
}
Each call accepts one or more edits; five or fewer is recommended, and larger valid batches return a warning. Each old_string and new_string is limited to 4,000 characters, and all edit text combined is limited to 10,000 characters. Edits are matched against the original file, must be unique and non-overlapping, and are written atomically only after every edit succeeds.
replace_all remains available when the array contains exactly one edit. Legacy top-level and camel-case arguments are also accepted:
{
"path": "src/example.ts",
"oldText": "text to replace",
"newText": "replacement text",
"change_all": false
}
Configuration
Settings are loaded from both global and project-level config files. Project settings override global ones.
~/.pi-agent/settings.json (global) and .pi/settings.json (project)
{
"edit": {
"fuzzyMatch": true,
"fuzzyThreshold": 0.95
}
}
Or using the piEdit namespace:
{
"piEdit": {
"fuzzyMatch": false,
"fuzzyThreshold": 0.9
}
}
| Key | Type | Default | Description |
|---|---|---|---|
fuzzyMatch |
boolean |
true |
Enable fuzzy matching fallback when exact match fails |
fuzzyThreshold |
number |
0.95 |
Similarity threshold for fuzzy matching (0–1) |
Extension API
createEditToolDefinition(cwd, options?) — ToolDefinition
Creates the full tool definition with TUI rendering, suitable for pi.registerTool().
import { createEditToolDefinition } from "@hiennguyen9874/pi-multi-edit";
const definition = createEditToolDefinition(process.cwd(), {
matching: { allowFuzzy: false, fuzzyThreshold: 0.9 },
operations: {
readFile: async (path) => { /* custom read */ },
writeFile: async (path, content) => { /* custom write */ },
access: async (path) => { /* custom access check */ },
},
});
createEditTool(cwd, options?) — AgentTool
Wraps the tool definition into an AgentTool for the core runtime.
Default extension
import editExtension from "@hiennguyen9874/pi-multi-edit";
// Registers the edit tool on session_start
pi.registerExtension(editExtension);
How matching works
- Exact match —
old_stringis searched literally in the file content - Fuzzy match (if enabled) — when exact match fails:
- Normalizes smart quotes (
""→",''→'), Unicode dashes, special spaces (NFKC + custom) - Tries substring match on normalized content
- Falls back to line-window similarity with relative indent depth awareness
- Rejects match if ambiguity is high (multiple candidates above threshold, or no dominant winner)
- Normalizes smart quotes (
replace_all— finds all occurrences and replaces them, preserving unchanged lines in normalized mode
Architecture
| File | Purpose |
|---|---|
src/index.ts |
Extension entrypoint — tool schema, execution, TUI rendering, argument normalization |
src/edit-diff.ts |
Core matching engine — fuzzy search, normalization, replacement application, diff/patch generation |
src/settings.ts |
Loads fuzzy match settings from global and project config files |
src/paths.ts |
Path normalization, resolution, canonicalization, cloud sync helpers |
src/path-utils.ts |
CWD-relative path resolution with macOS-specific fallbacks |
src/render-utils.ts |
TUI rendering helpers — path display, hyperlinks, theme-aware formatting |
src/tool-definition-wrapper.ts |
Wraps ToolDefinition into AgentTool for the core runtime |
src/child-process.ts |
Cross-platform process spawning with idle-timer-based child process waiting |
License
MIT