mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-11 22:53:04 +00:00
Two bugs caused the PreToolUse hook to fail after `gitnexus setup` copied it to ~/.claude/hooks/gitnexus/: 1. **Hook: resolveCliPath() only had 2 fallbacks**, both of which fail when the script runs from outside the npm package tree. The relative path resolves to ~/.claude/dist/cli/index.js (wrong), and require.resolve() can't find the package from ~/.claude/hooks/. Fix: 6-step fallback chain — injected constant → relative path → require.resolve → which/where binary discovery → npm root -g → npx. Handles Windows (where/npm.cmd/shell:true) and Unix alike. Splits `where` output with /\r?\n/ to avoid trailing CR on Windows. 2. **Setup: String.replace() silently failed** because the match target omitted the 2-space indentation present in the source file. The hook was copied verbatim without the CLI path injection. Fix: Instead of fragile string matching, prepend a `const GITNEXUS_CLI_PATH = "..."` constant to the top of the copied file. The hook's resolveCliPath() checks this constant first. Tested on Windows 11 with global npm install. The `where gitnexus` fallback correctly resolves to <npm_prefix>/node_modules/gitnexus/ dist/cli/index.js. Closes #108 Closes #132
This commit is contained in:
parent
0561d24efd
commit
644cf69d17
2 changed files with 66 additions and 17 deletions
|
|
@ -103,20 +103,68 @@ function extractPattern(toolName, toolInput) {
|
|||
|
||||
/**
|
||||
* Resolve the gitnexus CLI path.
|
||||
* 1. Relative path (works when script is inside npm package)
|
||||
* 2. require.resolve (works when gitnexus is globally installed)
|
||||
* 3. Fall back to npx (returns empty string)
|
||||
* 1. Injected constant (set by `gitnexus setup` when copying this file)
|
||||
* 2. Relative path (works when script is inside npm package)
|
||||
* 3. require.resolve (works when gitnexus is in Node module path)
|
||||
* 4. Discover from installed binary via which/where
|
||||
* 5. npm root -g fallback
|
||||
* 6. Fall back to npx (returns empty string)
|
||||
*/
|
||||
function resolveCliPath() {
|
||||
let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');
|
||||
if (!fs.existsSync(cliPath)) {
|
||||
try {
|
||||
cliPath = require.resolve('gitnexus/dist/cli/index.js');
|
||||
} catch {
|
||||
cliPath = '';
|
||||
}
|
||||
// 1. Injected absolute path (populated by `gitnexus setup`)
|
||||
if (typeof GITNEXUS_CLI_PATH !== 'undefined' && GITNEXUS_CLI_PATH && fs.existsSync(GITNEXUS_CLI_PATH)) {
|
||||
return GITNEXUS_CLI_PATH;
|
||||
}
|
||||
return cliPath;
|
||||
|
||||
// 2. Relative path (works when running from inside the npm package tree)
|
||||
let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');
|
||||
if (fs.existsSync(cliPath)) return cliPath;
|
||||
|
||||
// 3. require.resolve
|
||||
try {
|
||||
return require.resolve('gitnexus/dist/cli/index.js');
|
||||
} catch { /* continue */ }
|
||||
|
||||
// 4. Discover from installed binary location
|
||||
const isWin = process.platform === 'win32';
|
||||
try {
|
||||
const whichResult = spawnSync(isWin ? 'where' : 'which', ['gitnexus'], {
|
||||
encoding: 'utf-8',
|
||||
timeout: 3000,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
const binPath = (whichResult.stdout || '').split(/\r?\n/)[0].trim();
|
||||
if (binPath) {
|
||||
const binDir = path.dirname(binPath);
|
||||
const candidates = [
|
||||
// npm global on Windows: <prefix>/gitnexus -> <prefix>/node_modules/gitnexus/
|
||||
path.join(binDir, 'node_modules', 'gitnexus', 'dist', 'cli', 'index.js'),
|
||||
// npm global on Unix: <prefix>/bin/gitnexus -> <prefix>/lib/node_modules/gitnexus/
|
||||
path.join(binDir, '..', 'lib', 'node_modules', 'gitnexus', 'dist', 'cli', 'index.js'),
|
||||
];
|
||||
for (const c of candidates) {
|
||||
if (fs.existsSync(c)) return c;
|
||||
}
|
||||
}
|
||||
} catch { /* continue */ }
|
||||
|
||||
// 5. npm root -g fallback (needs shell:true on Windows for .cmd wrapper)
|
||||
try {
|
||||
const npmResult = spawnSync(isWin ? 'npm.cmd' : 'npm', ['root', '-g'], {
|
||||
encoding: 'utf-8',
|
||||
timeout: 5000,
|
||||
shell: isWin,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
const globalRoot = (npmResult.stdout || '').trim();
|
||||
if (globalRoot) {
|
||||
const candidate = path.join(globalRoot, 'gitnexus', 'dist', 'cli', 'index.js');
|
||||
if (fs.existsSync(candidate)) return candidate;
|
||||
}
|
||||
} catch { /* continue */ }
|
||||
|
||||
// 6. Fall back to npx
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -169,15 +169,16 @@ async function installClaudeCodeHooks(result: SetupResult): Promise<void> {
|
|||
const dest = path.join(destHooksDir, 'gitnexus-hook.cjs');
|
||||
try {
|
||||
let content = await fs.readFile(src, 'utf-8');
|
||||
// Inject resolved CLI path so the copied hook can find the CLI
|
||||
// even when it's no longer inside the npm package tree
|
||||
// Inject resolved CLI path as a constant at the top of the hook file.
|
||||
// Previous approach used String.replace() targeting a specific line, but
|
||||
// failed silently due to indentation mismatch (2-space indent in source
|
||||
// vs no indent in the match string). Prepending a constant is robust
|
||||
// regardless of formatting. See #108, #132.
|
||||
const resolvedCli = path.join(__dirname, '..', 'cli', 'index.js');
|
||||
const normalizedCli = path.resolve(resolvedCli).replace(/\\/g, '/');
|
||||
const jsonCli = JSON.stringify(normalizedCli);
|
||||
content = content.replace(
|
||||
"let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');",
|
||||
`let cliPath = ${jsonCli};`,
|
||||
);
|
||||
content = `const GITNEXUS_CLI_PATH = ${jsonCli};
|
||||
` + content;
|
||||
await fs.writeFile(dest, content, 'utf-8');
|
||||
} catch {
|
||||
// Script not found in source — skip
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue