Back to catalogue
ValidationPreToolUse· BashPreToolUseBefore tool execution · can block⚡ blocking
Enforce package managers
Blocks pip, poetry, npm and yarn commands; enforces uv (Python) and pnpm (Node.js). Generates an actionable message with the replacement command.
Use cases
- Standardize package management tooling across a Python+Node monorepo
- Avoid virtual environment conflicts between pip and uv
Providers & tags
Claude Code
#package-manager#uv#pnpm#python#node#validation
settings.json fragment
{
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"command": "node .claude/hooks/enforce-package-managers.mjs",
"type": "command"
}
],
"matcher": "Bash"
}
]
}
}Script · .claude/hooks/enforce-package-managers.mjs
#!/usr/bin/env node
// Bloque npm et yarn, impose pnpm pour ce projet Node.js (PreToolUse Bash)
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
const FORBIDDEN = [
{ pattern: /(^|[;&|\s])npm(\s|$)/, replacement: 'pnpm' },
{ pattern: /(^|[;&|\s])yarn(\s|$)/, replacement: 'pnpm' },
];
export function run(input) {
if (input.tool_name !== 'Bash') return null;
const cmd = input.tool_input?.command ?? '';
// Supprime le contenu des chaînes entre guillemets pour éviter les faux positifs
// quand npm/yarn apparaissent comme valeurs d'arguments texte (ex. git commit -m "...npm...",
// gh pr create --body "...yarn...") tout en continuant à bloquer les vraies invocations
// de gestionnaire de paquets même après des opérateurs shell (&&, ||, ;).
const stripped = cmd
.replace(/"(?:[^"\\]|\\.)*"/g, '""')
.replace(/'(?:[^'\\]|\\.)*'/g, "''");
const hit = FORBIDDEN.find(({ pattern }) => pattern.test(stripped));
return hit
? { decision: 'block', reason: `Utiliser '${hit.replacement}' à la place. Ce projet impose pnpm (pas npm ni yarn).` }
: null;
}
/* 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));
}