Skip to content

doctor: PreToolUse hook runtime reports exit 127 for a working hook on a binary-only install (checks a synthesised path, not the recorded command) #128

Description

@MongLong0214

Summary

doctor's PreToolUse hook runtime check reports fail … exits 127 for a hook that actually works. It does not execute the command recorded in settings.json; it reconstructs a different command from an assumed install layout. On a verified binary-only install that layout does not exist, so the check fails while the hook is healthy.

The suggested fix does not clear it, which makes the state unrecoverable through the documented path.

Environment

  • commitlore 0.2.1 (aarch64-apple-darwin release asset, SHA256SUMS verified OK)
  • Installed by extracting the release tarball and copying the single binary to ~/.local/bin/commitlore — the pinned/verified path the README documents, not the install.sh one-liner
  • macOS 15 (Darwin 25.3.0), git 2.50.1 (Apple Git-155), no node on PATH
  • Repository with 768 commits; index --rebuild indexed 175 trailers over 173 commits

Reproduce

# 1. verified binary-only install
gh release download v0.2.1 --repo MongLong0214/commitlore \
  --pattern 'commitlore-0.2.1-aarch64-apple-darwin.tar.gz' --pattern SHA256SUMS
grep 'commitlore-0.2.1-aarch64-apple-darwin.tar.gz' SHA256SUMS | shasum -a 256 -c -   # OK
tar xzf commitlore-0.2.1-aarch64-apple-darwin.tar.gz
cp commitlore ~/.local/bin/commitlore

# 2. init
commitlore init

init reports 4/4 steps ran, 1 need(s) attention:

[3/4] claude hook install
        installed the PreToolUse injection hook: <repo>/.claude/settings.json
          matcher: Read|Edit|Write
          command: commitlore inject --hook-input # commitlore-inject-hook

[4/4] doctor --fix (final check)
        fail    PreToolUse hook runtime — the PreToolUse hook exits 127: /bin/bash: /Users/<user>/.local/bin/scripts/commitlore-run.sh: No such file or directory
                fix: commitlore inject install-claude-hook

The hook is not broken

Running the command that settings.json actually contains succeeds:

$ echo '{"tool_name":"Read","tool_input":{"file_path":"<repo>/Package.swift"}}' | commitlore inject --hook-input
{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"commitlore: active records for Package.swift …"}}
$ echo $?
0

Exit 0, valid hookSpecificOutput, records rendered. commitlore also resolves for a non-login /bin/bash, which is how hooks are invoked.

Root cause

Step 3 writes this command:

commitlore inject --hook-input # commitlore-inject-hook

But the doctor check builds a different one. From the shipped binary:

[installedPath("scripts/commitlore-run.sh"), "inject", "--hook-input"]

~/.local/bin/scripts/commitlore-run.sh does not exist on a binary-only install — that directory is absent entirely — hence 127.

So the check verifies a synthesised command rather than the recorded one. The two disagree whenever the install layout differs from the assumed one.

The suggested fix cannot clear it

$ commitlore inject install-claude-hook
PreToolUse injection hook already installed: <repo>/.claude/settings.json (unchanged)
  matcher: Read|Edit|Write
  command: commitlore inject --hook-input # commitlore-inject-hook

It correctly detects the hook is already present and changes nothing, so doctor keeps failing. --force is documented for a foreign hook, which this is not. There is no documented path from this state to a clean doctor.

Why it matters

A false fail on a working hook is worse than a missing check. It trains the operator to ignore doctor output, which is exactly the signal that should stay trustworthy — and the project already treats this class as a defect (--help cites #63/#67 for steps being named rather than absorbed into a success message).

Suggested direction

Read the command out of settings.json and execute that, rather than reconstructing a path from an assumed layout. The recorded command is the thing whose runtime behaviour matters, and it is already marked with # commitlore-inject-hook for identification.

If a wrapper script is genuinely required for some install shapes, then claude hook install and the doctor check disagree about which install shape is supported, and that disagreement is the bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions