Skip to content

fix(cli): steer docs, skills, and hooks through a CLI-neutral project-local runner (#1939)#1945

Merged
magyargergo merged 45 commits into
mainfrom
issue-1939
Jun 2, 2026
Merged

fix(cli): steer docs, skills, and hooks through a CLI-neutral project-local runner (#1939)#1945
magyargergo merged 45 commits into
mainfrom
issue-1939

Conversation

@magyargergo

@magyargergo magyargergo commented May 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

Mitigates the npm 11.x arborist node.target is null install crash (#1939) by steering every GitNexus surface — committed skills, generated AGENTS.md/CLAUDE.md, and the editor stale-index hooks — through one CLI-neutral command instead of a hardcoded npx gitnexus.

gitnexus analyze drops a small runner at <repo>/.gitnexus/run.cjs (a copy of the canonical hooks/claude/resolve-analyze-cmd.cjs). Docs reference node .gitnexus/run.cjs <cmd>, which auto-selects a working runner at call time: a global gitnexus binary → pnpm dlx gitnexus@latest (npm 11 + pnpm) → npx gitnexus@latest. Committed docs therefore bake in no package-manager assumption and no per-machine content, and the resolver is cross-platform-correct on Windows.

Fixes #1939.

Problem & approach

The crash happens inside npm/arborist during npx install, before gitnexus runs, so runtime error handling cannot catch it — the only lever is steering installs toward working paths and documenting workarounds.

An earlier revision of this PR hardcoded pnpm --allow-build=… dlx gitnexus@latest <cmd> into committed docs/skills. That assumes pnpm is installed — a worse default for the npm majority just to dodge the npx crash. The final design defers the package-manager choice to call time via the project-local runner, so the committed instruction stays universal:

  • Project-local runner, not a ~/.claude/ helperAGENTS.md is the multi-CLI anchor (Cursor/Codex/Antigravity/…), so a Claude-specific path would be wrong for non-Claude readers. The runner lives next to the index, is gitignored, and is refreshed on every analyze.
  • First run stays universal — the README quickstart and an inline bootstrap note use npx gitnexus analyze (the runner can't exist before the first analyze); the npm-11 escape hatch (npm i -g gitnexus, or pnpm … dlx) is documented there.
  • Windows-safe execution — the runner's exec tail and the version/PATH probes resolve .cmd/.ps1/.exe shims through a shell on Windows (execFileSync does no PATHEXT resolution and Node blocks .cmd without a shell — CVE-2024-27980), and print a diagnostic instead of a silent exit 1.

What changed

  • Resolver / runner. resolve-analyze-cmd.cjs (canonical in hooks/claude/, byte-identical twin in gitnexus-claude-plugin/hooks/, parity-tested) gains buildRunnerArgv + a require.main === module exec tail. gitnexus analyze drops it at .gitnexus/run.cjs; failure to copy is non-fatal (docs carry a bootstrap fallback).
  • Generated docs. ai-context.ts emits node .gitnexus/run.cjs <cmd> (machine-independent, no version churn), kept under the #856 CLAUDE.md token budget; Cross-Repo Group commands route through the runner too.
  • Hooks + setup. The stale-index hooks emit the resolver's recommendation; gitnexus setup fails closed — it skips hook registration with an actionable error when a required helper (or adapter) is missing, rather than registering a hook that MODULE_NOT_FOUNDs on every tool event.
  • Skill steering. All committed skill copies (gitnexus/skills, .claude, gitnexus-claude-plugin, gitnexus-cursor-integration) route through the runner, guarded by skills-steering.test.ts.
  • Startup discipline. The npm-11 npx-crash warning runs from the analyze command (after the heap re-exec guard), not at CLI module load, so gitnexus mcp and other commands pay no startup probe cost.

Review history

This PR was hardened across several three-method tri-reviews (Codex + Compound-Engineering personas + GitNexus swarm). Findings from each round are addressed:

  • CI-blocking antigravity hook MODULE_NOT_FOUND (helper not staged); false-passing stdout-only tests.
  • Startup regression (invocation probe at module load) → moved into analyze, memoized.
  • Version-ref drift → standardized hints on gitnexus@latest; the separate, version-pinned MCP-registration ref in setup.ts is intentional and unchanged (renamed to MCP_PINNED_REF to end the same-name collision).
  • AI-context churn, unified fail-closed helper copy, Windows global-shim presence detection (resolveOnPath recognizes .exe/extensionless shims), hook-budget probe bounds, and repo-wide skill steering with a regression guard.

Latest round (the Windows probe defect). The most recent review (Codex + adversarial, cross-engine) found that the --version probes (probeVersion in the cjs, getNpmMajorVersion in the CLI) spawned npm/pnpm --version via execFileSync without a shell, so on Windows the .cmd shims ENOENT'd, the probe reported a present tool as absent, and the stale-index hook recommended the exact npx crash command this PR exists to avoid (the runner's --allow-build version gate was defeated the same way). Not a regression (the pre-pivot hook was unconditionally npx), but the headline npm-11 → pnpm steering was a no-op on the Windows hook path. Fixed:

  • shell: process.platform === 'win32' on the version probes (mirroring the exec tail), so .cmd shims resolve on Windows.
  • Banner-tolerant version parsing (a Corepack/notice line on stdout no longer defeats the parse).
  • Presence carried separately from version, so a present-but-unparseable pnpm still selects pnpm rather than falling to the npx crash path.
  • resolve-analyze-cmd.cjs (+ twin) brought under the shell-injection and windowsHide source-regression guards; the runner exec-tail's Windows shell branch now runs on the windows-latest CI leg (was previously untested everywhere); --embeddings=N (equals form) now widens the pnpm allow-build set; a broken README troubleshooting anchor and a generated-doc Cannot find module recovery hint were fixed.

Test plan

  • tsc --noEmit clean; prettier + typecheck pass (pre-commit) on every commit.
  • Targeted + consolidated unit/integration suites green locally (resolver, hooks, runner exec-tail, ai-context, setup, antigravity e2e, hooks e2e, skills-steering) — the Windows-only cases self-skip on POSIX.
  • runner-exec-tail.test.ts registered in scripts/cross-platform-tests.ts (SPAWN_CLI) so the Windows shell branch runs on windows-latest.
  • Full CI on all three OS runners (the windows-latest legs are the real validator for the .cmd-shim resolution, which can't be exercised on a POSIX dev host).
  • Windows / npm 11 reporter confirms the runner / pnpm dlx guidance resolves their install crash.

Known Residuals

Deliberately out of scope (tracked for follow-up), not regressions:

  • [P2] Runner exec-tail Windows arg-forwarding. The require.main === module exec tail forwards process.argv through execFileSync with shell: process.platform === 'win32', so on Windows cmd.exe would interpret & | ^ in forwarded args. Safe under the PR's actual usage — generated docs/skills emit fixed subcommands; the group commands (group impact --target <symbol> --repo <path>, group sync <name>) carry argument slots, so the trust boundary is "forwarded args must not contain cmd.exe metacharacters." A deeper fix (explicit .cmd resolution) conflicts with the repo's mandated shell idiom and has no clean in-repo primitive; the source-regression guard now locks the file so shell: true can't be introduced.
  • The cli skill's non-analyze npx subcommands and the no-install-free-universal-command-on-npm-11 tradeoff remain documented as-is.

Post-Deploy Monitoring & Validation

  • Watch: CI vitest on all three OS runners — especially tests / windows-latest and the windows-latest cross-platform-tests job (runner-exec-tail + the Windows probe path), the primary signal for the cross-platform fixes; plus setup-antigravity / hooks-e2e.
  • Healthy signals: green CI on Linux/macOS/Windows; gitnexus mcp starts with no pre-protocol stderr; regenerating AGENTS.md/CLAUDE.md on an unchanged repo produces no diff (no machine/version churn); on npm ≥ 11 with pnpm present, the stale-index hint recommends the pnpm dlx (not npx) form on Windows.
  • Failure signals → mitigation: MODULE_NOT_FOUND / Cannot find module in hook logs → a required helper failed to copy (re-run gitnexus setup); stray stderr before MCP protocol output → a startup probe regressed; doc churn on regenerate → AI-context emitted machine-specific content again.
  • Validation window / owner: the PR author across the first merges, plus the next Windows / npm-11 repro confirmation on Follow-up to #819: npx gitnexus still crashes with "node.target is null" on npm 11.x — clean cache doesn't help #1939. No data migration; no dashboards beyond CI.

Made with Cursor; hardened via automated multi-engine tri-review and remediation.

Prefer global gitnexus or pnpm dlx in hooks and generated AI context, warn
when npm 11.x would use the broken npx path, and document workarounds for
the arborist node.target null failure mode.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel

vercel Bot commented May 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
gitnexus Ready Ready Preview, Comment Jun 2, 2026 7:24am

Request Review

Comment thread gitnexus/test/integration/antigravity-hook-e2e.test.ts Fixed
Comment thread gitnexus/test/integration/hooks-e2e.test.ts Fixed
@github-actions

github-actions Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

CI Report

All checks passed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
✅ Tests success unit tests, 3 platforms
✅ E2E success gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
10861 10850 0 11 677s

✅ All 10850 tests passed

11 test(s) skipped — expand for details
  • COBOL pipeline benchmark > scales with file count
  • C# pipeline benchmark > scales with file count — namespaces spread across the solution
  • C# pipeline benchmark > scales with file count — all types in one (global) namespace bucket
  • C# pipeline benchmark > scales with file count — all types in one (named) namespace bucket
  • Go pipeline benchmark > scales with file count (workers enabled)
  • Go pipeline benchmark — worker pool (issue Worker idle timeout kills long Go scope extraction and surfaces as Napi::Error during analyze #1848) > does not quarantine the large generated Go file on sub-batch idle timeout
  • PHP pipeline benchmark > scales with file count (workers enabled)
  • Ruby pipeline benchmark > scales with file count (workers enabled)
  • Rust pipeline benchmark > scales with file count (workers enabled)
  • run.cjs direct-exec entrypoint (fix(cli): steer docs, skills, and hooks through a CLI-neutral project-local runner (#1939) #1945) > resolves a .cmd shim via the Windows shell branch, passing args and exit code
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 80.36% 37541/46713 79.84% 📈 +0.5 🟢 ████████████████░░░░
Branches 68.93% 23898/34669 68.5% 📈 +0.4 🟢 █████████████░░░░░░░
Functions 85.47% 3891/4552 84.94% 📈 +0.5 🟢 █████████████████░░░
Lines 83.92% 33780/40248 83.36% 📈 +0.6 🟢 ████████████████░░░░

📋 View full run · Generated by CI

@magyargergo magyargergo left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Tri-Review — #1945 fix(cli): steer npm 11 users away from npx install crash

Verdict: not production-ready · Merge state: checks failing · Branch hygiene: clean feature/fix PR (single commit, no merge-from-main).

Methods & engines (read first). This was a two-method, both-Claude review: the GitNexus swarm (risk-architect, test/CI-verifier) + Compound-Engineering personas (correctness, adversarial, maintainability) — five lanes, but all the same engine. The Codex lane (the only independent engine) was dispatched, but its background job never registered/returned, so there is no independent-engine corroboration here; treat cross-lane agreement as "consistent across personas," not independent confirmation. The coordinator independently reproduced the blocking findings.

Headline (blocking — P0): the PR turns CI red on all three OS runners

test/unit/setup-antigravity.test.ts has 3 failing tests (AfterTool emits stale-index hint…, ignores unknown tool names without crashing, does not crash on empty stdin). Reproduced locally: npx vitest run test/unit/setup-antigravity.test.ts3 failed | 14 passed. The antigravity hook's new top-level require('./resolve-analyze-cmd.cjs') (gitnexus-antigravity-hook.cjs:28) is unsatisfied — the source hooks/antigravity/ dir doesn't contain the helper and the test's stageAdapter() doesn't copy it — so the staged adapter crashes with MODULE_NOT_FOUND. The 4 sibling adapter tests that still pass only assert stdout.trim()==='' (setup-antigravity.test.ts:339–452), which a crash also satisfies, so the suite under-reports its own breakage. (See inline comment.)

Other inline findings

  • [P2] Module-load side effect on every CLI start (incl. gitnexus mcp). warnIfNpm11NpxRisk() runs at index.ts:14 top level, spawning which/where (+ npm --version) — up to 3 synchronous 5s-timeout subprocesses — before command dispatch. Reproduced: GITNEXUS_INVOCATION=npx node dist/cli/index.js --version emits the warning. Re-adds load-time work to the mcp path the file header says was optimized, and to the high-frequency augment hook. Adversarial refuted the scarier variant: the write is stderr-only, so JSON-RPC/stdout is not corrupted.
  • [P2] Version pinning is inconsistent and largely illusory. The plugin copy hardcodes gitnexus@latest (gitnexus-claude-plugin/hooks/resolve-analyze-cmd.cjs:8) while the TS/in-repo copies pin gitnexus@<version>; and the in-repo .cjs's package.json read resolves to a non-existent path once setup.ts copies it to ~/.claude/hooks/gitnexus/, so it silently falls back to latest in production too. The "Keep in sync" comment is already false, and a @latest stale-index hint can re-suggest the npx path #1939 steers users away from. (Consistent across risk + correctness + maintainability — same engine.)

Notable, lower-priority (body only)

  • [P2] ai-context.ts:131 bakes machine-local resolution into git-tracked docs. formatAnalyzeCommand() resolves on the analyzing machine and the result is written into the committed AGENTS.md/CLAUDE.md stale-index hint (verified: both tracked; the default regeneration path rewrites that line). Teammates on a different setup get a wrong command, and the pinned version churns the tracked files on every analyze/version-bump — the same volatile-content churn class as #1706 (symbol-count churn in committed AI-context files; now closed). Single-lane (correctness) + coordinator-verified.
  • [P3] The runtime npm-11 warning may never reach its audience. #1939 crashes during npx install (before gitnexus runs); a successful npx install puts gitnexus on PATH, so resolveInvocationMode() returns gitnexus and the warning is gated off (resolve-invocation.ts:94). README/install guidance is the effective channel.
  • Test gaps. No assertion that the Claude install path copies resolve-analyze-cmd.cjs, and that copy is silently swallowed (setup.ts:400–407, empty catch); warnIfNpm11NpxRisk suppression branches (mode≠npx, npm<11, npm=null) untested and the module-level npm11Warned flag (resolve-invocation.ts:23) is never reset (order-dependent); no parity test across the 3 resolver copies; the Windows .cmd/.bat wrapper branch isn't in the cross-platform matrix.
  • [P3] Windows .exe-only shims (Volta/scoop) aren't matched by the .cmd/.bat filter → suboptimal (not broken) hint. [P3] resolve-invocation.ts:13 throws at module load if package.json#version is missing — unreachable in normal tsc packaging (refuted as a live crash) but a behavior change vs. the graceful CJS latest fallback.

Credit (validated by the review)

env-var parsing/precedence correct; getNpmMajorVersion() null-safe (handles garbage/prerelease); CRLF trimmed per line; the hook-test-helpers env-merge is correct and in fact required for forced-mode children; quality / typecheck / lint / format + packaged install smoke all green. The preference chain (global → pnpm dlx → pinned npx) is a sound design.

CI: CI Gate + tests (ubuntu/macos/windows) failing (the P0 above); all other checks pass.

Coverage limit: the lanes and I read the changed-path suites and the new module; the full ~2000-test suite was not run, so an unrelated flake elsewhere isn't excluded — but every test touching the changed code is accounted for, and the 3 failures are the only PR-caused CI failures.


Automated multi-tool digest (two Claude methods; Codex unavailable). Verify each item before acting.

Comment thread gitnexus/hooks/antigravity/gitnexus-antigravity-hook.cjs
Comment thread gitnexus/src/cli/index.ts Outdated
Comment thread gitnexus-claude-plugin/hooks/resolve-analyze-cmd.cjs
magyargergo and others added 8 commits May 31, 2026 09:33
…arden load checks

The antigravity adapter gained a top-level require('./resolve-analyze-cmd.cjs')
but stageAdapter() did not copy it, so the spawned adapter crashed with
MODULE_NOT_FOUND. Three load-sensitive tests failed; four silent-path tests
false-passed on empty stdout.

Stage the helper alongside the other sibling helpers, and assert status===0 and
no MODULE_NOT_FOUND on the four silent-path tests so a non-loading hook can never
pass green again. Force a deterministic invocation mode in the stale-index test
so the emitted analyze command no longer varies by CI-runner PATH.

Co-authored-by: Cursor <cursoragent@cursor.com>
…rce CJS helper

NPX_REF becomes a literal `gitnexus@latest` in resolve-invocation.ts, dropping
the package.json require and the module-load throw (a malformed/absent version
can no longer crash any CLI command at import). The safety this PR delivers is
the install method steered to (global / pnpm dlx), not a pinned gitnexus
version, and the in-repo CJS mirror already degraded to `latest` once copied
outside the package.

Make the two resolve-analyze-cmd.cjs copies byte-identical and add a parity
test that fails on drift. The separate, version-pinned NPX_REF that setup.ts
writes into the MCP server registration is intentional and left unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
…n mode

warnIfNpm11NpxRisk() ran at index.ts module load, so every CLI invocation
(including the `gitnexus mcp` stdio hot path) paid which/where + npm --version
spawns — against the lazy-startup/MCP-stdout discipline (#207, #1383). Move the
call into analyzeCommand, after the ensureHeap() re-exec guard, so it fires once
in the working process and only for `analyze`.

Memoize the PATH-probe-derived invocation mode (the GITNEXUS_INVOCATION override
stays uncached) so repeated callers don't re-probe, and add a test-only reset so
the cache + once-only warning flag don't leak across the unit suite. Covers the
mode!=='npx', npm<11, and npm-absent suppression branches.

Co-authored-by: Cursor <cursoragent@cursor.com>
The winGitnexusWrapper branch only matched .cmd/.bat, so a global gitnexus
installed by Volta or scoop (a .exe or an extensionless shim) was missed and the
hint fell back to pnpm/npx. Accept .exe and treat any non-empty `where` hit as
on-PATH (the emitted hint is `gitnexus analyze` regardless of which shim
resolves it). Mirror the change into both resolve-analyze-cmd.cjs copies so the
TS source and the byte-identical hook mirrors stay in sync.

Add Windows-mocked test cases (.exe-only, extensionless, .cmd preference, CRLF
stripping) and register resolve-invocation.test.ts in cross-platform-tests.ts so
the windows-latest runner exercises the branch.

Co-authored-by: Cursor <cursoragent@cursor.com>
…CLAUDE.md

ai-context baked a machine-resolved command (formatAnalyzeCommand) into
git-tracked AGENTS.md/CLAUDE.md, so the stale-index hint varied per machine and
churned across branches (the #1706 class). Emit the fixed string
`pnpm dlx gitnexus@latest analyze` instead: committed AI-context is the most
authoritative instruction an agent reads, so it must name an install-free,
crash-free method — never `npx`, the npm-11 path #1939 steers away from.

formatAnalyzeCommand stays exported and unit-tested in resolve-invocation.ts
(it still mirrors the two .cjs hook copies); ai-context just no longer calls it.

Co-authored-by: Cursor <cursoragent@cursor.com>
installClaudeCodeHooks copied its four hook helpers in separate try/catch blocks
that silently swallowed failures, while installAntigravityHooks recorded an
error per failed copy. Extract one copyHookHelpers(srcDir, destDir, label,
result) with a single canonical helper list (including resolve-analyze-cmd.cjs)
and the antigravity loop's error-reporting policy, and use it from both paths so
a missing helper surfaces as a setup error instead of a silent runtime crash.

Assert both the Claude and Antigravity install paths co-locate
resolve-analyze-cmd.cjs next to the adapter, and that a failed copy records an
error rather than passing silently.

Co-authored-by: Cursor <cursoragent@cursor.com>
The extracted HOOK_HELPERS/copyHookHelpers block landed between the
installClaudeCodeHooks JSDoc and its function, leaving the doc reading as if it
described the helper list. Move the block above the doc so it documents the
function again. No behavior change.

Co-authored-by: Cursor <cursoragent@cursor.com>
…osture

Tier-2 review found two in-scope gaps in the #1945 follow-up:

- The "mirrors resolve-invocation.ts / test enforces parity" comments overclaimed:
  the parity test only compared the two .cjs copies to each other, so the TS
  source and the CJS hook copies could silently drift (NPX_REF, the per-mode
  command, and the Windows shim regex were hand-edited in all three this PR).
  Add TS<->CJS value parity (NPX_REF + formatAnalyzeCommand for every forced
  mode) and a source-level shim-regex parity check, and make the mirror comments
  accurately describe what is enforced.

- No test locked the R3/R4 startup posture, so re-adding warnIfNpm11NpxRisk()
  (or any resolve-invocation import) at index.ts module scope -- the #207/#1383
  lazy-startup regression -- would pass CI. Add a guard asserting index.ts has
  no module-load invocation probe and the warning is wired into analyzeCommand.

Co-authored-by: Cursor <cursoragent@cursor.com>
PR #1945 carried the gitnexus/pnpm/npx selection in three hand-synced
places — the canonical hook helper, its byte-identical plugin copy, and a
full TypeScript re-implementation in resolve-invocation.ts — kept in lockstep
by per-mode-command and regex-extracted-by-regex parity tests. The TS
formatAnalyzeCommand had no production caller (ai-context emits a fixed
string), and the module memoized + exposed a test-only reset for a "repeated
callers" case that has exactly one caller.

Make hooks/claude/resolve-analyze-cmd.cjs the single source: extract the
Windows-shim line-picking into a pure, exported pickPathMatch() and add an
injectable probe to resolveInvocationMode() so the shipped logic is testable
without spawning or global mocks. resolve-invocation.ts (118 -> 59 lines) now
consumes that cjs via createRequire for resolveInvocationMode/NPX_REF and adds
only the CLI-only npm-version probe and warning; the relative path resolves
identically from src/cli/ (tsx, vitest) and dist/cli/ (shipped, hooks/ is a
published sibling of dist/). Tests exercise the real shipped artifact, the
NPX_REF/mode-command parity scaffolding is dropped (one implementation can't
drift), and parity narrows to the two cjs copies staying byte-identical.

No behavior change: hook stale-index hints and the analyze warning are
byte-identical; the pre-existing setup.ts resolveGitnexusBin is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@magyargergo magyargergo left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Tri-Review — #1945 fix(cli): steer npm 11 users away from npx install crash

Final verdict: production-ready with minor follow-ups. No blocking defects. Two P2 follow-ups (both squarely in the PR's own theme) plus minor test/doc nits. Branch hygiene: clean feature/fix branch. Merge state: CI green on all 3 OS — BLOCKED only on required-review approval.

Methods & engines (read first). Genuine three-method review: Codex (the one independent engine — live this run via the GitHub connector), the GitNexus swarm (risk-architect, security-boundary; the test/CI lane ended mid-investigation, its domain covered by the testing reviewer), and Compound-Engineering personas (correctness, adversarial, maintainability, testing). 2 of the 3 methods are Claude under different prompts, so Claude-only agreement is "consistent across personas," not independent confirmation — only Codex+Claude agreement is weighted strong.

What's solid (validated, not assumed):

  • The refactor's riskiest move — createRequire('../../hooks/claude/resolve-analyze-cmd.cjs') from the CLI (resolve-invocation.ts) — resolves in the published package, confirmed by Codex + 3 Claude lanes: package.json files:["dist","hooks"], bin:"dist/cli/index.js", and ../../ is identical from src/cli/ and dist/cli/.
  • No injection / trust-boundary issue: execFileSync uses fixed argv (no shell), GITNEXUS_INVOCATION is allowlisted, the warning interpolates only a parsed int.
  • The two resolve-analyze-cmd.cjs copies (gitnexus/hooks/claude/ and gitnexus-claude-plugin/hooks/) are byte-identical (Codex: same git blob SHA; enforced by the parity test in gitnexus/test/unit/resolve-invocation.test.ts).
  • The prior review's two P0s are confirmed fixed: the antigravity-hook MODULE_NOT_FOUND (helper now in HOOK_HELPERS) and the index.ts module-load PATH probe (warning moved into analyzeCommand; posture-guard test added).

Inline findings (3):

  1. [P2] Generated guidance still funnels group repos into npx. ai-context.ts:170 (Cross-Repo Groups block) emits npx gitnexus group list/sync/impact, untouched by this PR — so a group-enabled repo's generated AGENTS.md/CLAUDE.md still steers npm-11 users into the exact arborist crash this PR fixes. (Codex — independent engine.)
  2. [P2] Stale-index hook now spawns which/where under a 10s budget. The PostToolUse hint was a pure string (0 spawns); it now calls formatAnalyzeCommand() → 1–2 PATH probes (timeout:5000ms each) on top of git rev-parse (3s), under the hook's timeout:10s. Worst case ≈ 3+5+5 = 13s > 10s → the nudge is killed mid-run. (risk + adversarial.)
  3. [P3] Dead NPX_REF/PKG_VERSION + loose version regex in the e2e tests. Both unused; the cjs always emits @latest, so toContain('npx gitnexus@latest analyze') is exact and stronger than /@\S+/. (maintainability + testing + risk — 4 lanes.)

Lower-priority (body only):

  • [P2, pre-existing] Partial-install crash: copyHookHelpers (setup.ts:334-363) records a copy failure but the caller still registers the hook → deferred MODULE_NOT_FOUND on first fire. Corroborated by Codex + 3 Claude lanes; already listed as a Known Residual in the PR body — confirming it's real, and the new required resolve-analyze-cmd.cjs widens the blast radius by one file. (Pre-existing class; not introduced here.)
  • Design tradeoff: the fixed pnpm dlx gitnexus@latest analyze in generated docs (ai-context.ts:133) strands users without pnpm (no npx fallback shown). Deliberate per #1706, but consider a fallback hint. Relatedly, this repo's own committed CLAUDE.md:61/AGENTS.md:80 still say npx gitnexus analyze — regenerate to dogfood the fix.
  • Test gaps: warnIfNpm11NpxRisk gitnexus-mode branch not exercised (test only sets pnpm); getNpmMajorVersion edge inputs (empty / pre-release) untested; pickPathMatch(isWin:true, gitnexusWrapper:false) (Windows pnpm probe) untested; some e2e tests don't pin GITNEXUS_INVOCATION (host-dependent); copyHookHelpers error-count assertion hardcodes 4.
  • Nits: InvocationResolver TS interface omits the probe param; gitnexus/README.md:279 pnpm dlx gitnexus missing @latest; NPX_REF name covers all three modes, not just npx.

Refuted / not bugs: memoization removal is not a regression (only one caller, analyze.ts:624); the .ps1-only Windows shim edge is unrealistic (npm co-installs a .cmd); the createRequire fail-closed concern doesn't trigger in a correctly-published package.

Coverage: all 18 changed files reviewed; CLI/cjs/test source read in full, golden/fixture blobs skipped. CI green across ubuntu/macos/windows tests, packaged-install smoke, lint/format/typecheck.

Automated multi-tool digest — verify before acting. Two of three methods are the same engine (Claude); only Codex is independent.

Comment thread gitnexus/src/cli/ai-context.ts Outdated
Comment thread gitnexus/hooks/claude/gitnexus-hook.cjs
Comment thread gitnexus/test/integration/hooks-e2e.test.ts Outdated
magyargergo and others added 5 commits May 31, 2026 12:46
The PostToolUse stale-index hint calls formatAnalyzeCommand(), which probes which/where; named PROBE_TIMEOUT_MS=2000 keeps git rev-parse (~3s) + up to two probes well under Claude Code's 10s hook timeout while preserving the machine-correct hint. Byte-identical in the plugin copy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Cross-Repo Groups block in generated AGENTS.md/CLAUDE.md still emitted bare 'npx gitnexus group ...', funneling npm-11 users into the arborist crash; switch to fixed 'pnpm dlx gitnexus@latest group ...'. Export generateGitNexusContent and add a group-branch test asserting no 'npx gitnexus' literal survives.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
README troubleshooting uses gitnexus@latest; the repo's own committed CLAUDE.md/AGENTS.md stale-index hint now matches the generated output (pnpm dlx gitnexus@latest analyze) so the repo dogfoods the fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mode (U4)

Drop dead PKG_VERSION/NPX_REF version-pinned constants; the cjs always emits gitnexus@latest, so assert exact toContain(...) instead of the /@\\S+/ wildcard; pin GITNEXUS_INVOCATION in the --embeddings tests for host-independent determinism.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add coverage for the gitnexus-mode warn suppression, getNpmMajorVersion edge inputs (empty/pre-release/non-numeric), and the Windows non-wrapper pickPathMatch branch; widen the InvocationResolver interface to document the optional probe param.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Follow-up to #819: npx gitnexus still crashes with "node.target is null" on npm 11.x — clean cache doesn't help

2 participants