opencode-package-managers-hook
v0.1.2
Published
OpenCode plugin that rewrites package manager commands to your preferred tools — uv/pipx over pip, pnpm over npm, rbenv over bare gem, and more
Maintainers
Readme
Stop OpenCode from running pip install --break-system-packages or any other bare package manager you'd rather not use. Every ecosystem is independently configurable with three modes: rewrite (silently transform), block (throw an error with guidance), or off (leave it alone).
Supported ecosystems
| Ecosystem | Banned commands | Default rewrite target |
| --- | --- | --- |
| Python | pip, pip3, python -m pip, virtualenv, python -m venv, bare python | uv pip, pipx install (global), uv venv, uv run python |
| Node | npm, npx, yarn | pnpm, pnpm dlx |
| Ruby | bare gem, bare bundle | rbenv exec gem, rbenv exec bundle |
The plugin probes your $PATH at startup — if a preferred tool (e.g. uv, pnpm, rbenv) isn't installed, that ecosystem's rewriter is silently disabled.
Install
opencode plugin opencode-package-managers-hook -gOr manually:
npm install -g opencode-package-managers-hookThen add to your opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-package-managers-hook"]
}Configuration
Pass options via the tuple form in your opencode.json:
{
"plugin": [
["opencode-package-managers-hook", {
"python": {
"mode": "rewrite",
"install": "uv pip install",
"globalInstall": "pipx install",
"venv": "uv venv",
"run": "uv run python",
"stripBreakSystemPackages": true,
"poetryMode": "off"
},
"node": {
"mode": "rewrite",
"target": "pnpm",
"exec": "pnpm dlx",
"rewriteYarn": true
},
"ruby": {
"mode": "block",
"rbenvExec": true
},
"verbose": false
}]
]
}All fields are optional — the defaults shown above apply when omitted.
Per-ecosystem options
Python
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| mode | "rewrite" | "block" | "off" | "rewrite" | Action when pip/pip3 is used |
| install | string | "uv pip install" | Replacement for pip install |
| globalInstall | string | "pipx install" | Replacement for pip install --user and pipx install |
| venv | string | "uv venv" | Replacement for python -m venv / virtualenv |
| run | string | "uv run python" | Replacement for bare python <script> |
| stripBreakSystemPackages | boolean | true | Remove --break-system-packages from any surviving command |
| poetryMode | "rewrite" | "block" | "off" | "off" | Whether to also rewrite poetry commands to uv equivalents |
Node
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| mode | "rewrite" | "block" | "off" | "rewrite" | Action when npm/npx is used |
| target | string | "pnpm" | Tool to rewrite npm commands to |
| exec | string | "pnpm dlx" | Tool to rewrite npx to |
| rewriteYarn | boolean | true | Also rewrite yarn to target |
Ruby
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| mode | "rewrite" | "block" | "off" | "block" | Action when bare gem/bundle is used |
| rbenvExec | boolean | true | Prepend rbenv exec to gem/bundle |
Disabling an ecosystem
Set any ecosystem to false to disable it entirely:
{
"plugin": [
["opencode-package-managers-hook", {
"ruby": false
}]
]
}Blocking instead of rewriting
Use "block" mode to hard-fail with a helpful error message instead of silently rewriting:
{
"plugin": [
["opencode-package-managers-hook", {
"python": { "mode": "block" },
"node": { "mode": "block" }
}]
]
}How it works
- On startup, the plugin checks which preferred tools (
uv,pipx,pnpm,rbenv) exist on your$PATH. - It builds a chain of rewriters for each enabled ecosystem whose tools are present.
- On every
bashtool invocation, thetool.execute.beforehook runs the command through the rewriter chain. - Commands already using preferred tools (
uv,pnpm,bun,rbenv, etc.) are skipped. - In
"rewrite"mode, the command is silently transformed. In"block"mode, an error is thrown with guidance on what to use instead.
Rewrite examples
| Original | Rewritten to |
| --- | --- |
| pip install requests | uv pip install requests |
| pip3 install --user awscli | pipx install awscli |
| pip install --break-system-packages foo | uv pip install foo |
| python -m pip install flask | uv pip install flask |
| python -m venv .venv | uv venv .venv |
| virtualenv env | uv venv env |
| python script.py | uv run python script.py |
| poetry install | uv sync (when poetryMode: "rewrite") |
| poetry add flask | uv add flask (when poetryMode: "rewrite") |
| npm install express | pnpm install express |
| npx create-react-app my-app | pnpm dlx create-react-app my-app |
| yarn add lodash | pnpm add lodash |
| gem install rails | rbenv exec gem install rails (rewrite) or blocked |
| bundle install | rbenv exec bundle install (rewrite) or blocked |
Development
bun install
bun run typecheck
bun run buildReleasing
npm version patch && git push --follow-tagsThe publish workflow handles npm (Trusted Publishing + provenance) and the GitHub Release.
