Toolchain
Opinionated toolchain enforcement for pi. Transparently rewrites commands to use preferred tools instead of blocking and forcing retries.
pi install @aliou/pi-toolchain Toolchain
Opinionated toolchain enforcement for pi. Transparently rewrites commands to use preferred tools instead of blocking and forcing retries.
Installation
Install via the pi-extensions package:
pi install git:github.com/aliou/pi-extensionsOr selectively in your settings.json:
{
"packages": [
{
"source": "git:github.com/aliou/pi-extensions",
"extensions": ["extensions/toolchain"]
}
]
}Or from npm:
pi install npm:@aliou/pi-toolchainFeatures
Rewriters (transparent, via spawn hook)
These features rewrite commands before shell execution. The agent never sees that the command was changed.
- enforcePackageManager: Rewrites
npm/yarn/buncommands to the selected package manager. Also handlesnpx->pnpm dlx/bunx. - rewritePython: Rewrites
python/python3touv run pythonandpip/pip3touv pip. - gitRebaseEditor: Injects
GIT_EDITOR=trueandGIT_SEQUENCE_EDITOR=:env vars forgit rebasecommands so they run non-interactively.
Blockers (via tool_call hooks)
These features block commands that have no clear rewrite target.
- preventBrew: Blocks all
brewcommands. Homebrew has no reliable 1:1 mapping to Nix. - preventDockerSecrets: Blocks
docker inspectand commondocker execenv-exfiltration commands (env,printenv,/proc/*/environ). - python confirm (part of rewritePython): When python/pip is used outside a uv project (no
pyproject.toml), shows a confirmation dialog. Also blockspoetry/pyenv/virtualenvunconditionally.
Settings Command
Run /toolchain:settings to open an interactive settings UI with two tabs:
- Local: edit project-scoped config (
.pi/extensions/toolchain.json) - Global: edit global config (
~/.pi/agent/extensions/toolchain.json)
Use Tab / Shift+Tab to switch tabs. Boolean settings and the package manager can be toggled directly.
Configuration
Configuration is loaded from two optional JSON files, merged in order (project overrides global):
- Global:
~/.pi/agent/extensions/toolchain.json - Project:
.pi/extensions/toolchain.json
Configuration Schema
{
"enabled": true,
"features": {
"enforcePackageManager": false,
"rewritePython": false,
"preventBrew": false,
"preventDockerSecrets": false,
"gitRebaseEditor": true
},
"packageManager": {
"selected": "pnpm"
}
}All fields are optional. Missing fields use the defaults shown above.
Feature Defaults
| Feature | Default | Description |
|---|---|---|
enforcePackageManager |
false |
Opt-in. User must pick a manager. |
rewritePython |
false |
Opt-in. User must have uv set up. |
preventBrew |
false |
Opt-in. Machine-specific. |
preventDockerSecrets |
false |
Opt-in. Blocks commands that can exfiltrate container env secrets. |
gitRebaseEditor |
true |
On by default. Always safe. |
Examples
Enforce pnpm and block brew:
{
"features": {
"enforcePackageManager": true,
"preventBrew": true
},
"packageManager": {
"selected": "pnpm"
}
}Enable python/uv rewriting:
{
"features": {
"rewritePython": true
}
}How It Works
Rewriters vs Blockers
The extension uses two pi mechanisms:
Spawn hook (
createBashToolwithspawnHook): Rewrites commands before shell execution. The agent sees the output of the rewritten command but doesn't know it was changed. Used for package manager, python/uv, and git rebase.tool_call event hooks: Block commands entirely. The agent sees a block reason and retries with the correct command. Used for brew (no rewrite target) and python outside uv projects (needs confirmation).
Execution Order
- Guardrails
tool_callhooks run first (permission gate, env protection) - Toolchain
tool_callhooks run (brew blocker, python confirm) - If not blocked, toolchain's bash tool runs with spawn hook (rewrites command)
- Shell executes the rewritten command
AST-Based Rewriting
All rewriters use structural shell parsing via @aliou/sh to identify command names in the AST. This avoids false positives where tool names appear in URLs, file paths, or strings. If the parser fails, the command passes through unchanged -- a missed rewrite is safe, a false positive rewrite corrupts the command.
Migration from Guardrails
If you were using preventBrew, preventPython, or enforcePackageManager in your guardrails config:
- Install
@aliou/pi-toolchain - Create
.pi/extensions/toolchain.jsonwith the equivalent config - Remove the deprecated features from your guardrails config
The guardrails extension will continue to honor these features with a deprecation warning until they are removed in a future version.