Skip to content

fix: hooks need only bash and node; missing tools fail visibly (GIT-116) - #51

Merged
3 commits merged into
mainfrom
bugfix/GIT-116-hook-portability
Sep 23, 2026
Merged

3 commits merged into
mainfrom
bugfix/GIT-116-hook-portability

Conversation

@nTEG-dev

@nTEG-dev nTEG-dev commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

What

The hooks need only bash and node, and a missing tool is now a visible failure instead of a silent "nothing relevant" (GIT-116).

  • Time bound moved into quick-retrieve (armDeadline: 2.5 s, unref'd, reason on stderr); auto-retrieve-hook.sh calls node directly, with no timeout wrapper.
  • Locating quick-retrieve.js from hooks copied into <repo>/.gitmem/hooks (by init / install-hooks): the lookup searched for a package called gitmem, but it is gitmem-mcp. It now tries the gitmem-mcp bin, npx's cache (how most installs run it, newest first) and the global node_modules.
  • Visible failures: no node, or no module found, is a non-blocking hook error: exit 1 with the reason on stderr (exit 2 would block the prompt). The node check now runs before prompt extraction, which also needed node.
  • session-start.sh:
    • debug log per user in $TMPDIR, never fatal under set -e (an unwritable /tmp log killed the hook);
    • no pgrep → skip the 7 s readiness gate rather than stall;
    • which → command -v;
    • no GNU-only date +%N.
  • resolve-root.sh: pid liveness via kill -0, then /proc (Linux) or ps (macOS), instead of matching kill's English error text; host from bash's $HOSTNAME.
  • credential-guard.sh: the path is JSON-escaped. A quote in it made the block output invalid JSON, which Claude Code treats as allow, so the RED LINE block silently disappeared.
  • CI: a new hooks-macos job runs the suite on macos-latest with a stock PATH (system dirs plus a node-only dir, so no Homebrew). It asserts timeout/gtimeout are absent. Publish depends on it. Found on the first run: macOS 15 ships /usr/bin/jq, so the job reports stock jq rather than asserting its absence; the jq-less path is covered by hooks-without-jq on Linux.

Tool sweep (audit class D, category 6)

Tool Guaranteed on macOS 12+ and Ubuntu? Now
timeout / gtimeout no (macOS) removed; bound inside node
node on the hook's PATH? not always (nvm/volta) required; missing = visible error
npm root -g, which gitmem npm not always on PATH; wrong package name command -v gitmem-mcp, npx cache, then npm root -g (correct name)
pgrep macOS yes; procps yes; slim images no optional; absence skips the wait
which not POSIX command -v (builtin)
hostname yes bash $HOSTNAME first
kill -0 text match locale-dependent /proc or ps
ps macOS yes; slim Linux no used only where /proc is absent
jq no optional (node fallback, since GIT-112)
date +%s%N GNU only (macOS prints a literal N) date +%s
grep -E with \b, \s; sed (no -i); awk; tr; mktemp; id yes (GNU and BSD) unchanged
bash 3.2 constructs clean (verified in the audit) unchanged

Out of scope and noted: display-hook.sh is shipped but not wired anywhere (dead code); /tmp/gitmem-hooks-$SESSION_ID keying belongs to GIT-103.

How to test

bash hooks/tests/test-hooks.sh: +7 portability tests, each using a PATH with the tool removed so they fail on any OS. There are also +4 unit tests: deadline timing, unref, and no hook calling timeout.

Run origin/main hooks This branch
Local, normal PATH 47/53: 6 new tests fail 53/53
Local, stock macOS PATH (system + node-only dir; no timeout/gtimeout/jq/npm) — 53/53
Local, slim PATH (no jq, no ps) — 53/53
CI hooks-without-jq (Linux) / hooks-macos — see checks below

On main the new tests fail as follows: auto-retrieve without timeout returns nothing; no node exits 0 silently; module not found exits 0 silently; the npx cache is not searched; no pgrep stalls about 7 s; the credential block with a quoted path is invalid JSON. The unwritable-log test passes vacuously on main, because main writes to a fixed /tmp path the test can't make unwritable.

Venue: not applicable; hooks only. Unit: 1361/1361.

Checklist

  • Fails on main (6/7 new), passes on the branch, on emulated macOS and slim Linux PATHs
  • macOS CI leg added and gating publish

🤖 Generated with Claude Code

Claude and others added 3 commits September 22, 2026 20:21
auto-retrieve-hook.sh ran `timeout 2.5 node …`. macOS ships no timeout,
the error went to /dev/null, and the hook exited as "nothing relevant", so
auto-retrieve never ran on a stock Mac.

- quick-retrieve bounds its own run (armDeadline, 2.5 s, unref'd); the
  hook calls node directly.
- Hooks copied into <repo>/.gitmem/hooks looked for a package named
  "gitmem"; it is gitmem-mcp. The lookup now tries the gitmem-mcp bin,
  npx's cache (how most installs run it) and the global node_modules.
- A missing node or quick-retrieve module is a visible non-blocking hook
  error (exit 1, reason on stderr), not a silent empty result. The node
  check runs before prompt extraction, which needed node too.
- session-start.sh: debug log per user in $TMPDIR and never fatal under
  set -e; no pgrep skips the 7 s readiness gate instead of stalling;
  `which` -> `command -v`; no GNU-only `date +%N`.
- resolve-root.sh: pid liveness via kill -0, then /proc (Linux) or ps
  (macOS), not kill's English error text; host from bash's $HOSTNAME.
- credential-guard.sh: the path is JSON-escaped. A quote in it made the
  block output invalid JSON, which Claude Code treats as allow.

test-hooks.sh: +7 portability tests using PATHs with the tool removed
(53/53; main fails 6). Unit: +4 (deadline, unref, no timeout call left).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sh depends on it (GIT-116)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…q (GIT-116)

macos-latest (macOS 15) has /usr/bin/jq as a system tool, so asserting its
absence failed the job. The jq-less path stays covered by hooks-without-jq
on Linux; the macOS leg now reports whether stock jq is present.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nTEG-dev nTEG-dev closed this pull request by merging all changes into main in 60a990e Sep 23, 2026
@nTEG-dev
nTEG-dev deleted the bugfix/GIT-116-hook-portability branch September 23, 2026 05:34
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.

1 participant