@jmcombs/pi-better-toolsy
Drop-in replacements for Pi's built-in ls, read, grep, find, edit, and write tools — adds .gitignore awareness, path-traversal protection, and $ injection-safe edits.
Package details
Install @jmcombs/pi-better-toolsy from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@jmcombs/pi-better-toolsy- Package
@jmcombs/pi-better-toolsy- Version
1.2.0- Published
- Jul 9, 2026
- Downloads
- 264/mo · 43/wk
- Author
- jmcombs
- License
- MIT
- Types
- extension
- Size
- 43.2 KB
- Dependencies
- 0 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
],
"image": "https://raw.githubusercontent.com/jmcombs/pi-extensions/main/assets/better-toolsy/preview.png"
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@jmcombs/pi-better-toolsy
Drop-in replacements for Pi's built-in ls, read, grep, find, edit, and write tools — transparently adds .gitignore awareness, path-traversal protection, and injection-safe edits without changing how the agent works. It also normalizes shell-unsafe gh PR/issue/release bodies so Markdown backticks don't get command-substituted.
Quick Start
pi install npm:@jmcombs/pi-better-toolsy
That's it. The agent uses its normal tool names and your implementations run automatically.
How It Works
Pi exposes six built-in file tools that the LLM is trained to call by name. This extension overrides all six with Node.js implementations that add correctness and safety guarantees the originals lack.
| Tool | What's added |
|---|---|
ls |
.gitignore filtering, dotfile suppression, directories listed as name/ |
read |
50 KB size guard, accurate line-number prefixes when using offset |
grep |
Relative paths in all results, ripgrep fast-path with Node.js fallback, ignoreCase / literal / context flags, .gitignore filtering |
find |
.gitignore filtering, path-based gitignore patterns (dist/**), reliable result cap |
edit |
$-injection-safe replacement (slice arithmetic, not String.replace), uniqueness validation, multiple edits in one call |
write |
Path-traversal guard, automatic parent directory creation |
Because the overrides use the same tool names, the agent's trained behavior is completely unchanged — it calls grep, edit, etc. as always, and the improvements are invisible.
Path Safety
Every tool resolves user-supplied paths through safeResolve(), which blocks directory traversal attacks (../../etc/passwd). Paths are always resolved relative to the current working directory.
.gitignore Awareness
ls, grep, and find load .gitignore at the search root and skip ignored entries. Path-based patterns (dist/**, packages/*/node_modules/) are matched against the full relative path, not just the basename, so nested ignores work correctly.
$ Injection Safety
The built-in edit tool uses String.prototype.replace, which silently expands special $-patterns in replacement text ($&, $`, $', $$). This is especially dangerous in TypeScript source files full of template literals and $-prefixed identifiers. The override uses slice arithmetic so replacement text is always treated as a literal string.
Multi-Edit Support
The edit tool accepts an array of { oldText, newText } pairs and applies them in sequence within a single call. Each edit is validated for uniqueness before any change is written to disk.
Shell-Safe gh Bodies
When a model writes gh pr create --body "## Notes about \ci.yml` built by $(date)", bash performs command substitution on the backticks and $(…)**inside the double quotes**, silently garbling the PR body. Atool_callhook rewrites the--body, --title, and --notesvalues ofgh pr, gh issue, and gh releasecommands to single-quoted form (which disables all expansion) before the built-inbash` tool runs — so the body lands verbatim on the first try instead of relying on the model noticing and re-issuing an edit.
It only acts when the value is double-quoted and contains an unescaped backtick or $, and it is deliberately fail-safe: any parsing ambiguity (e.g. nested quotes inside $(…)) leaves the command untouched. Generic -n/-b/-t flags on non-gh commands (grep -n, sort -n) are never affected. Each rewrite surfaces an in-session 🔧 bt notification naming the flags it normalized (the same signature the other tools carry), and is also logged for non-interactive runs.
Development
This package lives in the pi-extensions monorepo.
# From the repo root
npm ci
npm run check
To test changes locally against a real Pi session:
pi -e ./packages/better-toolsy
License
MIT © Jeremy Combs
