#!/usr/bin/env node const path = require('path'); const fs = require('fs'); const { pathToFileURL } = require('url'); const { execFileSync } = require('child_process'); const dir = process.env.CLAUDE_PROJECT_DIR || process.cwd(); const BAKED = "/usr/lib/node_modules/@nanonets/graft/dist/claude"; // dist/claude inside @nanonets/graft, resolved from a directory npm searches. function fromPkg(base) { try { const pkg = require.resolve('@nanonets/graft/package.json', { paths: [base] }); return path.join(path.dirname(pkg), 'dist', 'claude'); } catch { return null; } } // The global node_modules directory, as npm reports it. Queried only when needed. function globalRoot() { try { const root = execFileSync('npm', ['root', '-g'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], shell: process.platform === 'win32' }).trim(); return root || null; } catch { return null; /* npm unavailable */ } } // The version of the package that owns a dist/claude directory, or null. function versionOf(distClaude) { try { return JSON.parse(fs.readFileSync(path.join(distClaude, '..', '..', 'package.json'), 'utf8')).version || null; } catch { return null; } } // Compares dotted version numbers. An unreadable version counts as older. function newer(a, b) { if (!a) return false; if (!b) return true; const p = (v) => String(v).split('-')[0].split('.').map((n) => Number(n) || 0); const pa = p(a), pb = p(b); for (let i = 0; i < Math.max(pa.length, pb.length); i++) { const d = (pa[i] || 0) - (pb[i] || 0); if (d !== 0) return d > 0; } return false; } // The highest-versioned entry in `dirs` that contains `name`, or null. function best(dirs, name) { let bestDir = null, bestVer = null; for (const d of dirs) { if (!d || !fs.existsSync(path.join(d, name))) continue; const v = versionOf(d); if (bestDir === null || newer(v, bestVer)) { bestDir = d; bestVer = v; } } return bestDir; } function entry(name) { // Check the cheap candidates first, and run npm only if none of them match. const cheap = [BAKED, fromPkg(dir), fromPkg(path.join(path.dirname(process.execPath), '..', 'lib'))]; const hit = best(cheap, name); if (hit) return path.join(hit, name); const gr = globalRoot(); const global = gr && path.join(gr, '@nanonets', 'graft', 'dist', 'claude'); if (global && fs.existsSync(path.join(global, name))) return path.join(global, name); return path.join(dir, 'dist', 'claude', name); // fallback; the import does nothing when the file is absent } import(pathToFileURL(entry("hooks.js")).href).then((m) => m.main(process.argv[2])).catch(() => { /* graft is unavailable: do nothing */ });