@carter-mcalister/pi-mise-toolchain
v0.6.2
Published
Mise-driven toolchain enforcement for Pi
Downloads
66
Maintainers
Readme
@carter-mcalister/pi-mise-toolchain
Opinionated, mise-driven toolchain enforcement for Pi. Transparently rewrites commands to use preferred tools instead of blocking and forcing retries.
Installation
pi install npm:@carter-mcalister/pi-mise-toolchainOr install directly from this monorepo during local development:
pi install /absolute/path/to/pi-packages/packages/pi-mise-toolchainFeatures
Rewriters (transparent, via spawn hook)
These features rewrite commands before shell execution. By default the agent does not see that the command was changed, but you can enable optional rewrite notifications in the config.
- enforcePackageManager: Rewrites
npm/yarn/buncommands to the package manager derived from the nearestmise.toml. Also handlesnpx->pnpm dlx/bunx. Activates only when exactly one ofbun,pnpm, ornpmis declared in[tools]. - rewritePython: Rewrites
python/python3touv run pythonandpip/pip3touv pip. Activates whenuvis declared in the nearestmise.toml[tools]table. - gitRebaseEditor: Injects
GIT_EDITOR=trueandGIT_SEQUENCE_EDITOR=:env vars forgit rebasecommands so they run non-interactively.
Blockers (via tool_call hooks)
Blockers are still used when a rewrite is not safe to apply automatically.
- python confirm (part of
rewritePython): When python/pip is used outside a uv project (nopyproject.toml), shows a confirmation dialog. Also blockspoetry/pyenv/virtualenvunconditionally.
Dangerous command presets such as brew and Docker secret blocking now live in @aliou/pi-guardrails.
Settings Command
Run /toolchain:settings to open an interactive settings UI with two tabs:
- Global: edit global extension config (
~/.pi/agent/extensions/toolchain.json) - Memory: edit session-only overrides (not persisted)
Use Tab / Shift+Tab to switch tabs. The settings UI controls extension-owned settings such as gitRebaseEditor and bash.sourceMode.
Package-manager and Python rewrites are derived from the nearest mise.toml and are not editable in the settings UI.
Configuration
Configuration is split by responsibility:
- Global JSON config:
~/.pi/agent/extensions/toolchain.json - Memory config: session-only overrides via
/toolchain:settings - Project toolchain config: nearest
mise.toml
Global JSON Configuration Schema
{
"enabled": true,
"features": {
"gitRebaseEditor": "rewrite"
},
"bash": {
"sourceMode": "override-bash"
},
"ui": {
"showRewriteNotifications": false
}
}All fields are optional. Missing fields use the defaults shown above.
Project mise.toml Rules
The nearest mise.toml controls project-level rewrites:
- If
[tools]declaresuv, Python rewrites are enabled. - If
[tools]declares exactly one ofbun,pnpm, ornpm, package-manager rewrites are enabled and that manager is selected. - If
[tools]declares zero or multiple supported package managers, package-manager rewrites are disabled.
Defaults
| Setting | Default | Description |
| --- | --- | --- |
| features.gitRebaseEditor | "rewrite" | On by default. Injects non-interactive env vars for git rebase. |
| bash.sourceMode | "override-bash" | Select how rewrite hooks reach bash at runtime. Only matters when at least one rewrite is active. |
| ui.showRewriteNotifications | false | Show a visible Pi notification each time a rewrite happens. |
| Python rewrites | derived from mise.toml | Enabled when uv is declared in [tools]. |
| Package-manager rewrites | derived from mise.toml | Enabled only when exactly one of bun, pnpm, or npm is declared in [tools]. |
Examples
Use composed bash integration with an external bash composer:
{
"bash": {
"sourceMode": "composed-bash"
}
}Enable rewrite notifications in global config:
{
"ui": {
"showRewriteNotifications": true
}
}Enable project-level rewrites through mise.toml:
[tools]
uv = "latest"
bun = "1.3.12"How It Works
Rewriters vs Blockers
The extension uses two Pi mechanisms:
- Spawn hook (
createBashToolwithspawnHook): rewrites commands before shell execution. The agent sees the original command in the tool call UI but gets the output of the rewritten command. - tool_call event hooks: block commands entirely. The agent sees a block reason and retries with the correct command. Used for commands that have no safe rewrite target.
- Optional rewrite notifications: when
ui.showRewriteNotificationsis enabled, a warning-level Pi notification is shown before the rewritten command runs. The message is prefixed with[override-bash]or[composed-bash]for debug clarity.
Execution Order
- Guardrails
tool_callhooks run first (permission gate, env protection) - Toolchain
tool_callhooks run (blockers, optional rewrite notifications) - If not blocked and at least one feature is in
rewritemode, runtime routing depends onbash.sourceMode:override-bash: toolchain registers its own bash tool with spawn hookcomposed-bash: toolchain waits for an external composer to request and compose its spawn hook
- 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.
Bash Source Mode
bash.sourceMode controls how rewrite hooks attach to bash at runtime.
override-bash(default): toolchain registers bash when rewrite is active.composed-bash: toolchain contributes its rewrite hook to an external bash composer.
Important:
- Source mode matters only when at least one feature is set to
"rewrite". - Features set to
"block"are unaffected. - In
override-bashmode, Pi core still uses first-wins tool registration. If another extension earlier in load order already registeredbash, toolchain cannot replace it. - In
composed-bashmode, rewrites run only if an external composer emitsad:bash:spawn-hook:requestand collects contributors. - If no composer exists, rewrites will not run in
composed-bashmode by design.
Migration from Guardrails
If you were using preventBrew, preventPython, or enforcePackageManager in your guardrails config:
- Install
@carter-mcalister/pi-mise-toolchain - Add the project tool choices to
mise.toml(uvfor Python, exactly one ofbun/pnpm/npmfor package-manager rewrites) - Optionally configure extension-owned settings in
~/.pi/agent/extensions/toolchain.json - 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.
Credits
This package is a maintained fork of @aliou/pi-toolchain.
Credit to Aliou for the original project, design, and implementation this fork builds on.
Development
mise install
bun install
mise run check