Lefthook is classified as not overwriting Entire's hooks, so users get the weaker warning and no permanent fix
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 5.1k
- Forks
- 475
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 178
Description
What happened?
cmd/entire/cli/strategy/hook_managers.go tags each detected hook manager with OverwritesHooks, and that flag decides which of two very different messages a user sees at entire enable:
true: "Warning: X detected", plus the exact lines to add to X's own hook files so Entire survives permanently.false: "Note: X detected", plus "if X reinstalls hooks, runentire enableto restore Entire's hooks". A manual step, after the fact, with no signal for when it is needed.
Husky is true. Lefthook is false, and Husky is the only manager flagged true (hook_managers.go:27-46, unchanged from v0.10.0 through v0.10.4 and current main):
checks := []hookManager{
{"Husky", ".husky/", true},
{"pre-commit", ".pre-commit-config.yaml", false},
{"Overcommit", ".overcommit.yml", false},
}
// ... then, in loops:
checks = append(checks, hookManager{"Lefthook", name, false})
checks = append(checks, hookManager{"hk", name, false})
Lefthook does overwrite, by the exact criterion the true branch is written for. That branch's message (hook_managers.go:80) reads:
%s may overwrite hooks installed by Entire on npm install.
The lefthook npm package ships a postinstall that force-installs its hooks:
// lefthook@2.1.10 postinstall.js, line 10
spawnSync(getExePath(), ["install", "-f"], { cwd: process.env.INIT_CWD || process.cwd() });
Verified on Entire 0.10.4 with lefthook 2.1.10, fresh repo, positive control before every negative claim:
| step | Entire wrapper in pre-push |
in commit-msg |
|---|---|---|
control, after entire enable |
PRESENT | PRESENT |
lefthook install |
ABSENT | ABSENT |
entire enable (restore) |
PRESENT | PRESENT |
lefthook install -f (what postinstall runs) |
ABSENT | ABSENT |
entire enable (remedy) |
PRESENT | PRESENT |
So every npm install in a lefthook repo silently removes Entire's git hooks. That is exactly what Husky is flagged true for. Lefthook meets that criterion literally and is flagged false.
#1349's trigger (a lefthook.yml edit plus a commit) also still reproduces; I confirmed that on the issue itself: https://github.com/entireio/cli/issues/1349#issuecomment-5359195831. The reinstall path above is a second, independent trigger that needs no config change.
Why this is more than a one-word change. Flipping the flag alone produces malformed advice. The true branch treats ConfigPath as a directory and concatenates the hook name onto it (hook_managers.go:86-93):
hookDir := m.ConfigPath // ".husky/" for Husky
fmt.Fprintf(&b, " %s%s:\n", hookDir, spec.name)
Lefthook's ConfigPath is a file (lefthook.yml, or any of the {.,}lefthook{,-local}.{yml,yaml,json,toml} variants the detector already enumerates), so the output would read lefthook.ymlpre-push:.
Lefthook also does not take raw hook files the way Husky does, so the advice itself has to be a different shape. The pre-push handler needs the remote name and URL that git passes as $1 and $2, and lefthook scripts receive the hook's $@ while commands do not, so the advice has to name a script under .lefthook/pre-push/, not a commands: entry. Verified on lefthook 2.1.10 with one pre-push config carrying both handlers and a real git push:
| handler | $# |
$* |
|---|---|---|
commands: entry |
0 | (empty) |
scripts: entry |
2 | origin <remote-url> |
The generated .git/hooks/pre-push does forward "$@" to the lefthook binary; it is lefthook that does not pass them on to commands.
Steps to reproduce
- Fresh repo with a
lefthook.ymldeclaringpre-pushandcommit-msgcommands;git commit lefthook install, thenentire enable; confirm.git/hooks/pre-pushand.git/hooks/commit-msgcontain theEntire CLI hooksmarkerlefthook install -f(or anynpm installin the repo)- Both wrappers are gone; lefthook leaves
pre-push.oldandcommit-msg.old entire enablehad printed theNote:variant at step 2, so the user was told this needs a manualentire enableand given no way to know when
Scripted, re-runnable: repro-lefthook-install.sh (survival table) and lefthook-scripts-args.sh (argument table). Happy to paste either.
Entire CLI version
Entire CLI 0.10.4 (Go 1.26.6, darwin/arm64). OverwritesHooks for Lefthook is unchanged at v0.10.0 through v0.10.4 and on current main.
OS and architecture
macOS 26.6.2 (Darwin 25.6.0) arm64
Agent
Claude Code
Additional context
lefthook 2.1.10 (npm package, node_modules/.bin/lefthook). lefthook 2.1.12 is current; not re-tested there, the postinstall behaviour is unchanged as far as I can see from the package.
Suggested direction. Split the message from the flag: keep OverwritesHooks as the severity signal, and let each manager carry its own remediation snippet (Husky: hook files; Lefthook: a script). hk is likely in the same position; I have not tested it. Note hook_managers_test.go:65 currently asserts OverwritesHooks=false for Lefthook, so the fix changes a test's expectation, which is why I would rather agree the shape here first. I am happy to implement whichever shape you prefer, flagging first per CONTRIBUTING rather than opening a PR.
Related: #1349, #1250 (an external hooks backend would make this moot), #610 (hk added to the same detector).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with cmd/entire/cli/strategy/hook_managers.go and hook_managers_test.go, especially the Lefthook detection and enable-message branches. Run the relevant hook manager tests, then agree on the manager-specific remediation shape before changing the classification; done means Lefthook's overwrite behavior is accurately reported and its guidance is valid for the detected configuration paths.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100