hooks: auto-ignore .devkit/ in host repos on first run - #57
Merged
Conversation
audit-trail.sh writes .devkit/audit.log on every Bash tool call. If the host repo's .gitignore doesn't already cover .devkit/, that file gets tracked on the first `git add .` and then conflicts on every subsequent stash pop / pull / merge because the hook keeps rewriting it. devkit's own .gitignore already has .devkit/, but the plugin can't reach into downstream repos — so every new install is one `git add .` away from this trap. Fix: on first run in a repo (detected via `.devkit/` not yet existing), `git rev-parse --show-toplevel` to find the repo root and append `.devkit/` to the root .gitignore if no matching entry exists. Idempotent (grep -qE '^\.devkit($|/)' skips if already present), worktree-safe (rev-parse handles worktrees), fails silently outside a git repo, and runs only once per repo so there's no per-command fork overhead after the first call. Does not rescue repos that already tracked audit.log before this change — those need a one-time `git rm --cached .devkit/audit.log`.
Review on PR #57 flagged a TOCTOU window in the first-run guard: parallel Claude Code Bash tool calls can both pass the `[[ ! -d .devkit ]]` check and both append `.devkit/` to the host repo's .gitignore, leaving a duplicate line. Harmless to git, but visible noise. Fix: switch the guard to an atomic noclobber init marker. 1. Fast-path check: `[[ -f .devkit/.gitignore-installed ]]` (no fork, just a stat — runs on every hook call but zero overhead after init). 2. Critical section: `( set -C; : > .gitignore-installed )` uses POSIX noclobber in a subshell, so only one process can successfully create the marker. Losers fail the redirection silently and skip the git work; the winner does the rev-parse + grep + append. Also documents the intentional `--show-toplevel` behavior in submodules (reviewer flagged as a potential bug, but it is correct: `.devkit/` is created relative to cwd, so the submodule's own .gitignore is the right file when running inside a submodule). Passes shellcheck.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
hooks/audit-trail.shwrites.devkit/audit.logon every Bash tool call, but the plugin can't reach into downstream repos'.gitignore. Users whose project.gitignoredidn't already cover.devkit/end up tracking the log on their nextgit add ., then hit conflicts on everygit stash pop/pull/mergebecause the hook rewrites it on every subsequent command..devkit/into the host repo's root.gitignoreon first run (detected via.devkit/directory not existing yet), usinggit rev-parse --show-toplevelto find the repo root.grep -qE '^\.devkit($|/)'skips if any matching entry already exists), worktree-safe, silently no-ops outside a git repo, and guards behind[[ ! -d \"\$LOG_DIR\" ]]so the check only forksgitonce per repo — zero per-command overhead thereafter.What this does NOT fix
Repos that already committed
.devkit/audit.logbefore this change..gitignorerules don't apply to already-tracked files — affected users need a one-time:```
git rm --cached .devkit/audit.log && git commit -m "untrack audit log"
```
Test plan
.gitignore: trigger any Bash tool call, confirm.gitignoreis created containing.devkit/and.devkit/audit.logis untracked ingit status..gitignorethat doesn't end in a newline: confirm the appended entry lands on its own line..gitignorealready containing.devkit/: confirm no duplicate entry is appended..gitignorecontaining.devkit(no slash) or.devkit/audit.log: confirm regex matches and nothing is appended..gitignore, not the subdirectory.git rev-parse --show-toplevelresolves correctly and the main repo's.gitignoreis updated..devkit/audit.logand exits 0 without error.