HookStackGitHub
Back to catalogue
ValidationPreToolUse· BashPreToolUseBefore tool execution · can block⚡ blocking

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.

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
// Bloque pip/poetry install et suggère l'équivalent uv (PreToolUse Bash)
import { readFileSync } from 'fs';
import { fileURLToPath } from '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));
}