Back to catalogue
ValidationPreToolUse· Bash
Enforce uv for Python dependencies
Forces uv, ends pip/poetry drift
Blocks pip install, pip3 install and poetry add/install commands and redirects the agent to the uv equivalent. Prevents virtual environment fragmentation.
What does the Enforce uv for Python dependencies hook do?
Enforce uv for Python dependencies is a Claude Code PreToolUse hook matching Bash. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. Forces uv, ends pip/poetry drift.
As a PreToolUse hook it runs before the action completes, so it can block or adjust what Claude is about to do. Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.
Use cases
- Keep dependency management consistent on a uv-based project
- Avoid pip/uv conflicts in the same virtualenv
- Enforce team tooling conventions without having to remind the agent
Tags
#validation#uv#pip#poetry#python#package-manager
settings.json fragment
{
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/enforce-uv.mjs",
"type": "command"
}
],
"matcher": "Bash"
}
]
}
}Script · .claude/hooks/enforce-uv.mjs
#!/usr/bin/env node
// @hookstack pre-bash-enforce-uv
// Bloque pip/poetry install et suggère l'équivalent uv (PreToolUse Bash)
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
const BLOCKED = [
{ re: /(^|[;&|\s`])pip\s+install\b/, fix: "uv add" },
{ re: /(^|[;&|\s`])pip3\s+install\b/, fix: "uv add" },
{ re: /(^|[;&|\s`])poetry\s+add\b/, fix: "uv add" },
{ re: /(^|[;&|\s`])poetry\s+install\b/, fix: "uv sync" },
];
export function run(input) {
if (input.tool_name !== "Bash") return null;
const cmd = input.tool_input?.command ?? "";
const hit = BLOCKED.find(({ re }) => re.test(cmd));
if (!hit) return null;
return {
decision: "block",
reason: `Use '${hit.fix}' instead — this project manages dependencies with uv.`,
};
}
/* v8 ignore next 5 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const input = JSON.parse(readFileSync(0, "utf8"));
const result = run(input);
if (result) process.stdout.write(JSON.stringify(result));
}