An advanced, surgical codebase search, structural symbol navigation, and targeted replacement extension for the Pi Coding Agent.
Instead of exhausting context windows on redundant full-file read/write cycles, pi-find-replace equips your agent with a localized, language-agnostic toolset to query, extract, modify, and track code changes safely.
Standard shell operations (like sed or raw regex) are prone to breaking syntax or failing when target files undergo edits. This extension mitigates those constraints using three core design principles:
- Line-Drift Resilience: Tools like
replace_in_symboledit strictly inside a symbol's structural boundaries. Line-shift metrics are calculated and reported back dynamically, allowing the agent to update its positional memory without needing to re-read the file. - Context Token Economy: Rather than streaming raw file output, results are parsed, trimmed, and annotated with matching line numbers. Output syntax is optimized for LLM consumption, keeping context usage low.
- Transactional Isolation: Features a robust, per-file undo/redo stack. Unlike global git rollbacks, the agent can rollback individual, specific changes inside one target file without affecting its other modifications across the workspace.
No hard requirements. This extension works out of the box on all platforms.
For optimal performance, install ripgrep — a high-performance search engine written in Rust. The extension uses a 3-tier fallback chain:
| Tier | Source | Speed | Setup |
|---|---|---|---|
| 1 | Bundled (@vscode/ripgrep) |
⚡ Fastest | Auto-installed via npm |
| 2 | System rg command |
Fast | Pi's own rg (~/.pi/agent/bin), or brew install ripgrep / apt install ripgrep / choco install ripgrep |
| 3 | Pure JavaScript fallback | Slower | Always available, zero dependencies |
The extension automatically detects which tier is available and uses the fastest option. If you have ripgrep installed (bundled or system), you'll get near-instant search results. Without it, the JavaScript fallback ensures everything still works.
Tier 2 looks for rg by absolute path before trying PATH, so it also works in Pi sub-agent processes that don't inherit the main agent's PATH:
PI_FIND_REPLACE_RG— set this to anrgbinary to force a specific one<agent dir>/bin/rg— the binary Pi downloads, where the agent dir isPI_CODING_AGENT_DIRor~/.pi/agentrgonPATH
Install the extension globally using the Pi CLI package installer:
pi install npm:pi-find-replaceOr install it globally via npm:
npm install -g pi-find-replaceTo load the extension into your Pi coding agent session:
pi --extension pi-find-replace(Alternatively, register pi-find-replace inside your global ~/.pi/agent/config.json under the "extensions" array to load it automatically for every session).
Performs targeted replacements across files matching specific glob paths.
- Capabilities: Handles regex capture groups (
$1,$2), multiline queries (where.matches\n), case preservation (adjusting camelCase, snake_case, or UPPER_CASE dynamically to match the source target), and dry-run reviews. - Output: Returns a detailed report of the affected files, modified match count, and a warning on potential line shifting.
{
"regexQuery": "function oldCode\\((.*?)\\)",
"replaceString": "async function newCode($1)",
"files": ["src/**/*.ts"],
"dryRun": false,
"preserveCase": true,
"multiline": true
}Locates functions, classes, methods, and interface definitions by name. Instead of merely showing the definition signature line, it resolves the entire structural block using multi-language boundary analysis.
- Capabilities: Restricts queries using
scopeandkindfilters. Supports optional surrounding context line counts.
{
"name": "authenticateUser",
"kind": "function",
"scope": "src/",
"contextLines": 2
}- Supported Kinds:
function,class,method,interface,type,variable,constant,enum,any
Performs text or regex replacements scoped strictly inside a named symbol's boundaries. Since it targets a relative scope, this tool protects edits from line-drift occurring elsewhere in the file.
- Output: Executes the replacement, calculates the exact line shift, and displays the newly modified block with corrected absolute line numbers for immediate agent verification.
{
"file": "src/auth.ts",
"symbol": "authenticateUser",
"find": "return false;",
"replace": "return verifyToken(req);",
"isRegex": false
}Generates a structured mapping of all code symbols declared within a single file. Useful for providing the agent with a architectural file overview before implementing changes.
- Output: Returns a list of symbols with nested indentation levels corresponding to scopes, matching the layout structure of VS Code’s Outline view.
{
"file": "src/auth.ts"
}Performs a global workspace search to locate imports, definitions, and invocations of a specific symbol.
- Output: Segregates usages into four logical categories to assist the agent in impact analysis:
- 📞 Calls: Direct invocations of the symbol
- 📦 Imports: Import statements and module requirements
- 🔗 References: Mentions, typings, or scalar usage
- 📝 Definitions: The initial declaration location
{
"name": "authenticateUser",
"scope": "src/",
"excludeFiles": ["**/*.test.ts"]
}A temporary clipboard utility. Allows the agent to store, retrieve, or cut code snippets during multi-step refactoring workflows across different sessions or files.
- Capabilities: Evaluates and reports code block metrics (line count, byte size, and age). Includes a
consumemode which acts as a "cut" by returning the content and immediately wiping it from memory.
{"action": "store", "text": "const tempHelper = () => true;"}
{"action": "get", "consume": true}
{"action": "status"}
{"action": "clear"}Rolls back modifications on a specified file. Unlike git-level resets, undo_file only affects modifications made directly by the extension inside the targeted file.
- Capabilities: Supports rolling back multiple steps sequentially and returns the fully restored code state.
{
"file": "src/auth.ts",
"steps": 1
}Re-applies file edits that were reverted using undo_file.
{
"file": "src/auth.ts",
"steps": 1
}Moves a targeted code block (such as a function or class) from one file to another, or adjusts its position inside the same file.
- Capabilities: Leverages the boundary detection engine to safely slice out the whole block. Integrates with the per-file tracking system so the action can be undone.
{
"symbol": "formatDate",
"from": "src/utils.ts",
"to": "src/helpers.ts",
"position": "after:parseDate"
}- Supported Positions:
top,bottom,after:<symbolName>
The extension uses a cascading search backend that automatically falls back if a tier is unavailable:
- Bundled ripgrep (
@vscode/ripgrep) — Pre-compiled binaries shipped with the npm package. Uses ripgrep's JSON interface (rg --json) for structured, parse-free output. - System ripgrep — Falls back to Pi's managed
rgor the globally installedrgcommand if the bundled binary is missing (e.g. in sub-agent processes that don't carry this package'snode_modules). - Pure JavaScript fallback — A built-in file scanner using Node.js
fswith regex matching. Slower than ripgrep but guarantees functionality on all platforms with zero native dependencies. It is also used for regexes ripgrep's default engine rejects, such as look-ahead/look-behind.
A later tier is tried only when the previous one can't run. "No matches" or a timeout is a final answer.
The JavaScript fallback walks the same files ripgrep would:
- Skipped like ripgrep: hidden files and directories (
.git,.dart_tool,.gradle,.venv, ...), anything matched by.gitignore/.ignorefiles (nested, and from the enclosing repo when searching a subdirectory), binary files (containing a NUL byte), and symlinks. - Also skipped, as a safety net when nothing ignores them:
node_modules,bower_components,dist,build,Pods,Carthage,DerivedData,__pycache__,venv. - Hard limits so a huge tree can't exhaust memory:
- files larger than 4 MB are skipped
- at most 10,000 files are searched
- at most 200,000 directory entries are visited
- about 10 MB of matched text is collected
- a search stops after 30 s
All three tiers produce identical output formats, so the agent sees consistent results regardless of which backend is active.
The extension dynamically switches boundary-tracking strategies based on the file type:
- Brace Matching Strategy: Track matching curly braces, brackets, and parentheses. Default for C, C++, C#, Java, JavaScript, TypeScript, Go, Rust, and JSON.
- Indentation Analysis Strategy: Calculates code scopes by tracking visual indentation steps and line-termination offsets. Default for Python and Ruby.
- Keyword Matching Strategy: Tracks structural ending terminators (
end). Default for Swift, Kotlin, and Scala.
This extension provides multi-language symbol extraction and boundary processing for the following environments:
- Languages: TypeScript, JavaScript, Python, Rust, Go, Java, C#, C/C++, PHP, Ruby, Swift, Kotlin, Scala, and YAML.
This project is licensed under the MIT License.