Inject installed dependency versions
The agent codes against your real versions, not guesses
Injects the exact installed versions from package.json (and pyproject.toml) into every prompt, so the agent stops hallucinating APIs from the wrong major version — the single most common time-sink in vibe coding. Reads one or two files, output is capped to stay token-light.
What does the Inject installed dependency versions hook do?
Inject installed dependency versions is a Claude Code UserPromptSubmit hook. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. The agent codes against your real versions, not guesses.
As a UserPromptSubmit 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
- Accurate API usage
- Fewer version-mismatch bugs
- Faster iteration
Tags
settings.json fragment
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/inject-deps-versions.mjs",
"type": "command"
}
],
"matcher": "*"
}
]
}
}Script · .claude/hooks/inject-deps-versions.mjs
#!/usr/bin/env node
// Injecte les versions réelles des dépendances dans chaque prompt (UserPromptSubmit)
import { readFileSync, existsSync } from 'fs';
import { fileURLToPath } from 'url';
import { join } from 'path';
const MAX_ENTRIES = 60; // borne le coût en tokens
function parsePackageJson(raw) {
let pkg;
try { pkg = JSON.parse(raw); } catch { return []; }
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
return Object.entries(deps).map(([name, version]) => `${name}@${version}`);
}
// Extraction best-effort des dépendances pyproject (PEP 621 [project] et Poetry).
function parsePyproject(raw) {
const out = [];
const block = raw.match(/dependencies\s*=\s*\[([\s\S]*?)\]/);
if (block) {
for (const m of block[1].matchAll(/["']([^"']+)["']/g)) out.push(m[1].trim());
}
return out;
}
export function run({ cwd = process.cwd(), readFile = readFileSync, fileExists = existsSync } = {}) {
const entries = [];
const pkgPath = join(cwd, 'package.json');
if (fileExists(pkgPath)) {
try { entries.push(...parsePackageJson(readFile(pkgPath, 'utf8'))); } catch {}
}
const pyPath = join(cwd, 'pyproject.toml');
if (fileExists(pyPath)) {
try { entries.push(...parsePyproject(readFile(pyPath, 'utf8'))); } catch {}
}
if (entries.length === 0) return null;
const shown = entries.slice(0, MAX_ENTRIES);
const more = entries.length > MAX_ENTRIES ? ` (+${entries.length - MAX_ENTRIES} more)` : '';
return (
'## Installed dependency versions\n' +
'Use these exact versions — do not assume newer/older APIs:\n' +
shown.map((e) => `- ${e}`).join('\n') + more + '\n'
);
}
/* v8 ignore next 4 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const result = run();
if (result) process.stdout.write(result);
}