{"_id":"@aaronkyriesenbach/pi-substitute-commands","_rev":"2-bc9758a4b3c101abacae1b4e148fb740","name":"@aaronkyriesenbach/pi-substitute-commands","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@aaronkyriesenbach/pi-substitute-commands","version":"1.0.0","keywords":["pi-package","pi-extension"],"author":"Aaron Ky-Riesenbach","license":"MIT","_id":"@aaronkyriesenbach/pi-substitute-commands@1.0.0","maintainers":[{"name":"aaronkyriesenbach","email":"npm.shimmy035@passmail.net"}],"homepage":"https://github.com/aaronkyriesenbach/pi-packages/tree/master/packages/pi-substitute-commands","bugs":{"url":"https://github.com/aaronkyriesenbach/pi-packages/issues"},"pi":{"extensions":["./extensions/index.ts"]},"dist":{"shasum":"7420167347bfab3dcf28617531ce44b31d0ce42f","tarball":"https://registry.npmjs.org/@aaronkyriesenbach/pi-substitute-commands/-/pi-substitute-commands-1.0.0.tgz","fileCount":5,"integrity":"sha512-GTLymsEVz0HejjBT+7XXWNihlY6tsOw4VHj1JTbq/Ogpzs43K//BLLNH7BA7rDcTfYO+fdlXxu6OskZ+ZU3FUg==","signatures":[{"sig":"MEUCIGg22TE7sbajVRxElGqe+mVp/xh1YEfHpPMjCxDVMZ8UAiEA51myCedn9vn2tQIP77RGoJ5c9PUH82NLtItNTzAvGoA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":18126},"type":"module","shasum":"7420167347bfab3dcf28617531ce44b31d0ce42f","engines":{"node":">=22.19.0"},"scripts":{"lint":"eslint .","test":"vitest run","format":"prettier --write .","typecheck":"tsc --noEmit","test:watch":"vitest","format:check":"prettier --check .","test:coverage":"vitest run --coverage","prepublishOnly":"bun run typecheck && bun run lint && bun run format:check && bun run test:coverage"},"_npmUser":{"name":"aaronkyriesenbach","email":"npm.shimmy035@passmail.net"},"_integrity":"sha512-GTLymsEVz0HejjBT+7XXWNihlY6tsOw4VHj1JTbq/Ogpzs43K//BLLNH7BA7rDcTfYO+fdlXxu6OskZ+ZU3FUg==","repository":{"url":"git+ssh://git@github.com/aaronkyriesenbach/pi-packages.git","type":"git","directory":"packages/pi-substitute-commands"},"_npmVersion":"10.8.3","description":"Pi extension that hard-blocks agent-issued bash tool calls containing disallowed commands and suggests replacements.","directories":{},"_nodeVersion":"24.3.0","dependencies":{"unbash":"^4.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@earendil-works/pi-coding-agent":"^0.80.10"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-substitute-commands_1.0.0_1785962094124_0.11487191798203478","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@aaronkyriesenbach/pi-substitute-commands","version":"1.1.0","description":"Pi extension that hard-blocks agent-issued bash tool calls containing disallowed commands and suggests replacements.","keywords":["pi-package","pi-extension"],"license":"MIT","author":"Aaron Ky-Riesenbach","repository":{"type":"git","url":"git+ssh://git@github.com/aaronkyriesenbach/pi-packages.git","directory":"packages/pi-substitute-commands"},"homepage":"https://github.com/aaronkyriesenbach/pi-packages/tree/master/packages/pi-substitute-commands","bugs":{"url":"https://github.com/aaronkyriesenbach/pi-packages/issues"},"type":"module","publishConfig":{"access":"public"},"pi":{"extensions":["./extensions/index.ts"]},"engines":{"node":">=22.19.0"},"scripts":{"typecheck":"tsc --noEmit","lint":"eslint .","format":"prettier --write .","format:check":"prettier --check .","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"bun run typecheck && bun run lint && bun run format:check && bun run test:coverage"},"dependencies":{"unbash":"^4.0.0"},"peerDependencies":{"@earendil-works/pi-coding-agent":"*"},"devDependencies":{"@earendil-works/pi-coding-agent":"^0.80.10"},"_id":"@aaronkyriesenbach/pi-substitute-commands@1.1.0","_integrity":"sha512-xnNRjKBfZtR/7Dxf1+AEJ844QOfgEadg+wCfG3i6IPexkYLHpIoJUYzwafzzLB5ki5RJommBZIV+TAjd1xljRw==","_nodeVersion":"26.3.0","_npmVersion":"10.8.3","shasum":"3417e2e0d54818b3885473214793d0ed992d9106","dist":{"integrity":"sha512-xnNRjKBfZtR/7Dxf1+AEJ844QOfgEadg+wCfG3i6IPexkYLHpIoJUYzwafzzLB5ki5RJommBZIV+TAjd1xljRw==","shasum":"3417e2e0d54818b3885473214793d0ed992d9106","tarball":"https://registry.npmjs.org/@aaronkyriesenbach/pi-substitute-commands/-/pi-substitute-commands-1.1.0.tgz","fileCount":5,"unpackedSize":18126,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEB55jEfsXm4Z4MUeH4vcyA+u2du2p8/XDNV2V4jptjyAiEA3/sNBVSaMvX50TEpz6UCuHK9Bt4PJwu2w2DdxlQK8ss="}]},"_npmUser":{"name":"aaronkyriesenbach","email":"npm.shimmy035@passmail.net"},"directories":{},"maintainers":[{"name":"aaronkyriesenbach","email":"npm.shimmy035@passmail.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-substitute-commands_1.1.0_1789060015881_0.3209877704414259"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-05T20:34:53.948Z","modified":"2026-09-10T17:06:56.164Z","1.0.0":"2026-08-05T20:34:54.279Z","1.1.0":"2026-09-10T17:06:56.035Z"},"bugs":{"url":"https://github.com/aaronkyriesenbach/pi-packages/issues"},"author":"Aaron Ky-Riesenbach","license":"MIT","homepage":"https://github.com/aaronkyriesenbach/pi-packages/tree/master/packages/pi-substitute-commands","keywords":["pi-package","pi-extension"],"repository":{"type":"git","url":"git+ssh://git@github.com/aaronkyriesenbach/pi-packages.git","directory":"packages/pi-substitute-commands"},"description":"Pi extension that hard-blocks agent-issued bash tool calls containing disallowed commands and suggests replacements.","maintainers":[{"name":"aaronkyriesenbach","email":"npm.shimmy035@passmail.net"}],"readme":"# pi-substitute-commands\n\nA [pi](https://pi.dev) extension that hard-blocks agent-issued `bash` tool\ncalls containing a disallowed command, telling the agent to use the\nrecommended replacement instead. Ships with one Substitution Pair (`find`/\n`grep` family → `fd`/`rg`), structured so future pairs are a small code\nchange.\n\n## Why\n\nAgent instructions frequently say \"use `fd`/`rg` instead of `find`/`grep`\" —\nthey're faster, respect `.gitignore`, and have saner defaults — but a prose\ninstruction is easy for an agent to forget once a command gets wrapped in\n`sudo`, piped through `xargs`, buried inside a `bash -c` string, or spawned\nfrom a `find -exec`. This extension enforces that guidance structurally: it\nparses every `bash` tool call the agent issues, resolves the command down to\nwhat would actually execute, and blocks the call outright if `find`/`grep`/\n`egrep`/`fgrep`/`zgrep` would run — no matter how deeply it's nested inside\nwrappers, subshells, or command substitutions.\n\n## What it blocks\n\nThe check parses the command with [`unbash`](https://www.npmjs.com/package/unbash)\nand looks for a blocked command name in **Command Position** — the leading\nword of a command, or the resolved sub-command of a supported wrapper —\nanywhere in the parsed structure. A few examples:\n\n| Command                                  | Blocked because                               |\n| ---------------------------------------- | --------------------------------------------- |\n| `grep -rn \"TODO\" src/`                   | `grep` in Command Position — use `rg`         |\n| `find . -name \"*.ts\"`                    | `find` in Command Position — use `fd`         |\n| `sudo grep -r \"secret\" /etc`             | `grep` behind a `sudo` passthrough wrapper    |\n| `find . -type f -exec grep -l foo {} \\;` | `grep` inside `find -exec`'s sub-command      |\n| `bash -c \"cat a.txt \\| grep foo\"`        | `grep` inside a `bash -c` nested script       |\n| `echo \"$(grep -c foo file)\"`             | `grep` inside a `$(...)` command substitution |\n\n`git grep`/`git-grep` is exempt — it invokes git's own pattern search, not\nthe standalone `grep` binary. If the command can't be parsed with\nconfidence (a syntax error, or anything `unbash` reports parse errors for),\nthe check **fails open**: the call is allowed through unblocked rather than\nguessed at. The extension only ever inspects the command — it never\nrewrites or mutates it.\n\nResolution follows Wrapper Unwrapping through:\n\n- Passthrough wrappers: `sudo`, `xargs`, `nice`, `nohup`, `env`, `strace`\n- Flag wrappers with a nested script: `bash -c`, `sh -c`, `zsh -c`\n- Exec wrappers: `find -exec`/`-ok`, `fd -x`/`--exec`/`-X`/`--exec-batch`\n\nand recurses into command substitutions (`$(...)` and backticks) and\nsubshells (`(...)`) wherever they appear, so a blocked command nested\nseveral layers deep is still caught.\n\nWhen a call is blocked, the agent sees a reason naming every distinct\ndisallowed command found and its recommended replacement, e.g.:\n\n```\nBlocked: this command uses disallowed command(s): `grep` (use `rg` instead), `find` (use `fd` instead).\n```\n\n## Install\n\n```bash\npi install npm:@aaronkyriesenbach/pi-substitute-commands\n```\n\nOr add it to `.pi/settings.json` / `~/.pi/agent/settings.json`:\n\n```json\n{\n  \"packages\": [\"npm:@aaronkyriesenbach/pi-substitute-commands\"]\n}\n```\n\n## Development\n\n```bash\nbun install\nbun run typecheck\nbun run lint\nbun run format:check\nbun run test:coverage\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}