Go hook-only integration that nudges Python-related commands in AI coding
agents toward uv.
Tested:
- Windows + Codex CLI.
Implemented but not yet verified:
- OpenCode hook installation and command rewriting.
- macOS and Linux behavior.
This repository should be treated as tested only for the Windows + Codex CLI path until the other targets are validated.
Install these tools and make sure they are available on PATH:
uv.codexfor the tested Codex CLI path.
Go 1.22 or newer is only required when building from source.
The hook installer does not install Codex, OpenCode, Python, Go, or uv.
The rewrite rules intentionally follow uv project boundaries:
pyproject.tomlanduv.lockmark uv-managed projects.poetry.lock,[tool.poetry],pdm.lock, and[tool.pdm]mark projects managed by other tools; commands in those projects are left unchanged.- Python tool commands such as
ruff,pytest, andtyuseuv runinside uv projects, anduvx/uv tool runoutside projects.
Install the latest stable GitHub release into a user-level binary directory.
macOS:
curl -LsSf https://uv-python-hook.jinxiao2010.uk/install.sh | shWindows PowerShell:
powershell -ExecutionPolicy ByPass -c "irm https://uv-python-hook.jinxiao2010.uk/install.ps1 | iex"The installer fetches the latest stable release from GitHub, downloads the
matching archive for your OS and CPU architecture, verifies it against
checksums.txt, and installs the binary.
Default install locations:
- macOS:
$HOME/.local/bin/uv-python-hook - Windows:
$HOME\.local\bin\uv-python-hook.exe
The installer also tries to make the command available on PATH:
- macOS: appends a small
uv-python-hookblock to the detected shell profile (~/.zshrc,~/.bashrc, or~/.config/fish/config.fish). - Windows: appends the install directory to the user
Pathenvironment variable.
Installer environment variables:
UV_PYTHON_HOOK_INSTALL_DIR: install to a custom directory.UV_PYTHON_HOOK_NO_MODIFY_PATH=1: install the binary without editing PATH.UV_PYTHON_HOOK_REPO: override the GitHub repository, useful for forks and release testing.
To remove the binary:
rm ~/.local/bin/uv-python-hookRemove-Item "$HOME\.local\bin\uv-python-hook.exe"From the repository root.
For release-style builds, use CGO_ENABLED=0. This project is a pure Go CLI
with no C dependencies, so disabling CGO makes the binary easier to distribute
across machines without requiring a matching system C runtime. It is not
strictly required for local development, but it is the recommended default for
published binaries. If future dependencies require CGO, remove this setting.
Windows PowerShell:
New-Item -ItemType Directory -Force .\bin | Out-Null
$env:CGO_ENABLED = "0"
go build -buildvcs=false -trimpath -ldflags="-s -w" -o .\bin\uv-python-hook.exe .\cmd\uv-python-hookLinux or macOS:
mkdir -p ./bin
CGO_ENABLED=0 go build -buildvcs=false -trimpath -ldflags="-s -w" -o ./bin/uv-python-hook ./cmd/uv-python-hookCommon cross-compile targets from PowerShell:
New-Item -ItemType Directory -Force .\bin | Out-Null
$env:CGO_ENABLED = "0"
$env:GOOS = "linux"; $env:GOARCH = "amd64"
go build -buildvcs=false -trimpath -ldflags="-s -w" -o .\bin\uv-python-hook-linux-amd64 .\cmd\uv-python-hook
$env:GOOS = "linux"; $env:GOARCH = "arm64"
go build -buildvcs=false -trimpath -ldflags="-s -w" -o .\bin\uv-python-hook-linux-arm64 .\cmd\uv-python-hook
$env:GOOS = "darwin"; $env:GOARCH = "amd64"
go build -buildvcs=false -trimpath -ldflags="-s -w" -o .\bin\uv-python-hook-darwin-amd64 .\cmd\uv-python-hook
$env:GOOS = "darwin"; $env:GOARCH = "arm64"
go build -buildvcs=false -trimpath -ldflags="-s -w" -o .\bin\uv-python-hook-darwin-arm64 .\cmd\uv-python-hook
Remove-Item Env:\GOOS, Env:\GOARCHCommon cross-compile targets from Linux or macOS shells:
mkdir -p ./bin
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -buildvcs=false -trimpath -ldflags="-s -w" -o ./bin/uv-python-hook-linux-amd64 ./cmd/uv-python-hook
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -buildvcs=false -trimpath -ldflags="-s -w" -o ./bin/uv-python-hook-linux-arm64 ./cmd/uv-python-hook
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -buildvcs=false -trimpath -ldflags="-s -w" -o ./bin/uv-python-hook-darwin-amd64 ./cmd/uv-python-hook
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -buildvcs=false -trimpath -ldflags="-s -w" -o ./bin/uv-python-hook-darwin-arm64 ./cmd/uv-python-hookTo install the command into your Go binary directory instead:
$env:CGO_ENABLED = "0"
go install -buildvcs=false .\cmd\uv-python-hook-buildvcs=false avoids Go VCS stamping failures in checkouts where Git marks
the directory as unsafe or unavailable.
After building or installing, confirm the command is reachable:
uv-python-hook doctor
uv-python-hook --versionIf you built into .\bin, either add that directory to PATH or run the binary
by path:
.\bin\uv-python-hook.exe doctorReleases are automated with GoReleaser and GitHub Actions.
- Pushes to
maincreate a stable SemVer tag such asv1.2.3, then publish a GitHub release. - Pushes to
devcreate a prerelease tag such asv1.2.3-beta.1, then publish a GitHub prerelease. - Version bumps follow Conventional Commits:
feat:increments minor.fix:and other changes increment patch.BREAKING CHANGE:or a!marker, such asfeat!:increments major.
GoReleaser builds Linux, Windows, and macOS binaries for amd64 and arm64.
It also uploads install.sh and install.ps1 as release assets. The release
build injects the release version into the binary, so:
uv-python-hook --versionprints the released version and build metadata.
Run the full Go test suite:
go test ./...If your Go cache is not writable in the current environment, point it to a local cache directory:
$env:GOCACHE = "$PWD\.tmp-go-cache"
go test ./...You can also test a rewrite without installing hooks:
uv-python-hook rewrite-command "python app.py"
uv-python-hook rewrite-command --target opencode "python app.py"
uv-python-hook rewrite-command "pip install -r requirements.txt"
uv-python-hook detect-projectBy default, install uses user scope and auto-detects installed targets. It
installs the Codex hook only when codex is found on PATH, and installs the
OpenCode plugin only when opencode is found on PATH:
uv-python-hook installTo force a specific target for the current user:
uv-python-hook install --user --targets codexFor a project-local install, run this inside the target repository and specify
--project:
uv-python-hook install --project --targets codexUser installs write Codex hook entries to:
%USERPROFILE%\.codex\hooks.json
Project installs write Codex hook entries to:
.\.codex\hooks.json
The Codex hook runs before shell tool execution. When it sees a Python-related
command, it denies the original command and suggests the equivalent uv
command. Codex suggestions force a temporary uv cache by default, so standalone
tools use uv tool run instead of the uvx shorthand.
Examples:
python app.py->uv --cache-dir <temp>\uv-python-agent-hooks\uv-cache run app.pypy app.py->uv --cache-dir <temp>\uv-python-agent-hooks\uv-cache run app.pypytest -qinside a uv project ->uv --cache-dir <temp>\uv-python-agent-hooks\uv-cache run pytest -qpytest -qoutside a project ->uv --cache-dir <temp>\uv-python-agent-hooks\uv-cache tool run pytest -qpip install requests->uv --cache-dir <temp>\uv-python-agent-hooks\uv-cache pip install requestspython -m pip install requests->uv --cache-dir <temp>\uv-python-agent-hooks\uv-cache pip install requestspython -m venv->uv --cache-dir <temp>\uv-python-agent-hooks\uv-cache venv .venvpython -m venv venv->uv --cache-dir <temp>\uv-python-agent-hooks\uv-cache venv venv
For requirements installs, the hook checks the nearest pyproject.toml or
uv.lock:
- If the project has uv-syncable dependency metadata,
pip install -r requirements.txtbecomesuv --cache-dir <temp>\uv-python-agent-hooks\uv-cache sync. - If
[project]metadata is incomplete, it falls back touv --cache-dir <temp>\uv-python-agent-hooks\uv-cache pip install -r requirements.txt. - If the project is managed by Poetry or PDM, the command is left unchanged.
Run doctor after installation to inspect tool availability and hook paths:
uv-python-hook doctorBy default, uninstall also uses user scope and auto-detects targets. It
removes existing hook files even if the corresponding command is no longer on
PATH:
uv-python-hook uninstallTo force a specific target:
uv-python-hook uninstall --user --targets codexFor a project-local install:
uv-python-hook uninstall --project --targets codexInstall the OpenCode plugin explicitly:
uv-python-hook install --user --targets opencodeFor sandboxed OpenCode environments, prefer a project-local install so the plugin is available from the working directory:
uv-python-hook install --project --targets opencodeOpenCode rewrites Python-related commands directly. Unlike the Codex target, it
does not force uv to use the hook's temporary cache by default, so standalone
tools use the uvx shorthand:
python app.py->uv run app.pypytest -qinside a uv project ->uv run pytest -qpytest -qoutside a project ->uvx pytest -qpip install requests->uv pip install requests
Set UV_PYTHON_AGENT_HOOKS_CACHE_MODE=on to force the same temporary cache mode
used by Codex.
Codex hooks force UV_CACHE_DIR to a dedicated temporary cache by default:
%TEMP%\uv-python-agent-hooks\uv-cache
OpenCode hooks leave uv cache behavior unchanged by default. Control this
with:
$env:UV_PYTHON_AGENT_HOOKS_CACHE_MODE = "auto" # codex on, opencode off
$env:UV_PYTHON_AGENT_HOOKS_CACHE_MODE = "on" # force temp cache
$env:UV_PYTHON_AGENT_HOOKS_CACHE_MODE = "off" # never force temp cacheOverride it with:
$env:UV_PYTHON_AGENT_HOOKS_CACHE_DIR = "D:\path\to\uv-cache"By default, venv rewrites preserve an explicit path:
python -m venv -> uv venv .venv
python -m venv venv -> uv venv venv
Set UV_PYTHON_AGENT_HOOKS_FORCE_DOT_VENV=on to force the venv target path to
.venv even when the original command names another path:
$env:UV_PYTHON_AGENT_HOOKS_FORCE_DOT_VENV = "on"With the switch enabled:
python -m venv venv -> uv venv .venv
python -m venv --python 3.12 venv -> uv venv --python 3.12 .venv
virtualenv env -> uv venv .venv
uv-python-hook install
uv-python-hook uninstall
uv-python-hook install --project --targets codex
uv-python-hook uninstall --project --targets codex
uv-python-hook doctor
uv-python-hook detect-project
uv-python-hook rewrite-command "pip install -r requirements.txt"
uv-python-hook rewrite-command --target opencode "python app.py"Copyright 2026 uv Python Agent Hooks contributors.
Licensed under the Apache License, Version 2.0. See LICENSE.