--- name: repo-local-git-hooks description: Detect and repair cases where repository-local Git hooks are bypassed by a global core.hooksPath, especially on macOS machines managed by nix-darwin/home-manager that inject global gitleaks/git-hooks paths. Use when CI catches lint/format/test issues that pre-commit or pre-push should have caught, when .git/hooks exists but Git ignores it, when installing pre-commit refuses because core.hooksPath is set, or when a repo needs local pre-commit/pre-push hooks to override global hooks. --- # Repo Local Git Hooks Use this skill when a repo expects local hooks but Git is using a global `core.hooksPath`. The common symptom is: `.git/hooks/pre-commit` exists, yet commits/pushes run only a global Nix/gitleaks hook or no repo-specific lint at all. ## Audit First Run the bundled audit script from the target repo: ```bash path/to/repo-local-git-hooks/scripts/audit_git_hooks.sh ``` Or inspect manually: ```bash git config --show-origin --get-all core.hooksPath || true git rev-parse --git-common-dir git rev-parse --git-path hooks ``` Interpretation: - `git rev-parse --git-path hooks` must match `$(git rev-parse --git-common-dir)/hooks` when repo-local `.git/hooks` should be effective. - If `core.hooksPath` comes from `~/.config/git/config`, `/etc/gitconfig`, or a `/nix/store/...-git-hooks` path, this is a global Git config override. In Eotel's setup that normally comes from nix-darwin/home-manager, not from the repo's `devenv`. - `devenv` can install tools or configure project hooks when the repo explicitly uses `git-hooks.hooks`, but a global `core.hooksPath` visible via `git config --show-origin` is outside the repo and can bypass `.git/hooks`. ## Fix Pattern Prefer the repo's own hook installer. Typical names: ```bash just install-hooks just pre-commit-install ``` The installer should be idempotent and should: ```bash git config --local --unset-all core.hooksPath || true GIT_CONFIG_GLOBAL=/dev/null uvx pre-commit install GIT_CONFIG_GLOBAL=/dev/null uvx pre-commit install --hook-type pre-push git config --local core.hooksPath "$(git rev-parse --git-common-dir)/hooks" ``` For `prek`, use the same pattern but run `prek install` while `GIT_CONFIG_GLOBAL=/dev/null` if it refuses to install with `core.hooksPath` set. ## Agent Workflow 1. Confirm the failure mode with the audit script or the manual commands above. 2. Check whether the repo already has an install recipe (`just --list`, `rg "install-hooks|pre-commit-install"`). 3. Patch that recipe so it writes repo-local `core.hooksPath` after installing hooks. 4. If the repo has a Claude/Codex session-start hook, make it verify the effective hook path, not just the existence of `.git/hooks/pre-commit`. 5. Run the actual hooks after the fix: ```bash .git/hooks/pre-commit uvx pre-commit run --hook-stage pre-push --all-files ``` If `pre-commit` is unavailable, use the repo's tool (`prek`, `lefthook`, etc.) and still verify `git rev-parse --git-path hooks`. ## Session Hook Guard Existence checks are insufficient. A correct guard checks both hook files and effective path: ```bash EXPECTED_HOOKS_DIR="$(git rev-parse --git-common-dir)/hooks" EFFECTIVE_HOOKS_DIR="$(git rev-parse --git-path hooks)" if [ "$EFFECTIVE_HOOKS_DIR" != "$EXPECTED_HOOKS_DIR" ]; then just install-hooks fi ``` Also verify generated hook files contain the expected launcher marker, such as `File generated by pre-commit`, when the repo uses pre-commit. ## Resource - `scripts/audit_git_hooks.sh`: deterministic audit/fix helper for this exact failure mode.