Skip to content

Follow-up to #1660: PostToolUse hook still hardcodes npx gitnexus in 1.6.5 — proposed fix never shipped #1938

Description

@ProductOfAmerica

Summary

#1660 (closed 2026-05-17) proposed using which gitnexus to detect a PATH-installed binary and prefer gitnexus over npx gitnexus in the PostToolUse stale-index hint. That fix never landed in the published package. gitnexus 1.6.5 ships with the same bare hardcode #1660 originally reported.

I can't reopen #1660 (no permissions), so filing this as a follow-up so the maintainer has a fresh thread.

Verbatim repro from a clean install

gitnexus@1.6.5 installed via Volta on Windows 11. The hook source at node_modules/gitnexus/hooks/claude/gitnexus-hook.cjs lines 339-344:

const analyzeCmd = `npx gitnexus analyze${hadEmbeddings ? ' --embeddings' : ''}`;
sendHookResponse(
  'PostToolUse',
  `GitNexus index is stale (last indexed: ${lastCommit ? lastCommit.slice(0, 7) : 'never'}). ` +
    `Run \`${analyzeCmd}\` to update the knowledge graph.`,
);

No spawnSync('which', ...), no command -v, no PATH probe of any kind. Grep across the entire file:

$ grep -nE "which|command -v|onPath|analyzeBin" gitnexus-hook.cjs
$ # zero matches

Confirmed: the #1660 "fix" was either reverted, never merged, or merged into a different code path that doesn't ship to the installed hook.

Impact on Windows

Even if the proposed spawnSync('which', ['gitnexus']) had landed, which is a Unix command. On Windows it isn't installed by default — only available inside Git Bash / MSYS shims. The Node-side spawnSync('which', ...) call would return a non-zero status on stock Windows because which itself isn't found, falling through to the npx gitnexus branch every time even when the bare gitnexus IS on PATH.

So whenever this gets re-attempted, the fix needs to be cross-platform.

Proposed fix (cross-platform)

Option A — pure-Node existence check, no subprocess:

const which = (cmd) => {
  const exts = process.platform === 'win32'
    ? ['', '.cmd', '.bat', '.exe', '.ps1']
    : [''];
  const dirs = (process.env.PATH || '').split(path.delimiter);
  for (const dir of dirs) {
    for (const ext of exts) {
      try {
        if (fs.statSync(path.join(dir, cmd + ext)).isFile()) return true;
      } catch { /* not here, try next */ }
    }
  }
  return false;
};
const analyzeBin = which('gitnexus') ? 'gitnexus' : 'npx gitnexus';
const analyzeCmd = `${analyzeBin} analyze${hadEmbeddings ? ' --embeddings' : ''}`;

Option B — shell out, but with a platform-appropriate probe:

const probe = process.platform === 'win32' ? 'where' : 'command';
const args  = process.platform === 'win32' ? ['gitnexus'] : ['-v', 'gitnexus'];
const onPath = spawnSync(probe, args, { stdio: 'pipe' }).status === 0;
const analyzeBin = onPath ? 'gitnexus' : 'npx gitnexus';

Either resolves the gap on every supported OS. Option A is preferable — no subprocess overhead, no shell-environment dependency.

Why this is more than cosmetic

Same blast radius as #1660 — agents that follow the hint literally invoke npx gitnexus, bypassing user wrappers. And on this machine npx gitnexus hits the npm 11.x arborist null-target crash (see my companion follow-up to #819), so the hint sends users into a known-broken code path with no fallback.

Environment

  • gitnexus: 1.6.5 (installed via volta install gitnexus@1.6.5)
  • Hook file inspected: <volta>/tools/image/packages/gitnexus/node_modules/gitnexus/hooks/claude/gitnexus-hook.cjs
  • Node: 24.13.0
  • npm: 11.x
  • OS: Windows 11
  • Shell context: Claude Code

Cross-references

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions