Planning hooks crash on git-bash/Windows & Linux — stat -f is BSD-only
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- shell
- Domain
- testing-qa, tooling
Research direction
Start with .claude/hooks/user-prompt-submit.sh, .claude/hooks/session-start.sh, and .claude/hooks/stop.sh, then reproduce with the command in the issue on a GNU system. Update the mtime handling and inspect tests/hooks/test-stop.sh for the deterministic timestamp case. Done means ./scripts/quality.sh passes all 56 tests on git-bash/Linux while macOS behavior remains working.
Written by the indexing model from the issue text.
Description
Environment
- OS: Windows 11 (git-bash) — also affects any GNU coreutils system (Linux). Works fine on macOS, which is why it likely went unnoticed.
- Affected files:
.claude/hooks/user-prompt-submit.sh,.claude/hooks/session-start.sh,.claude/hooks/stop.sh
Summary
The planning hooks silently crash on git-bash / Linux. As a side effect, scripts/quality.sh fails (~17 hook test cases) and the UserPromptSubmit / Stop / SessionStart hooks never run — so the "surface the active plan" feature is effectively dead on Windows/Linux.
Note: the test suite reports 56/56 on macOS but the hooks never execute on GNU systems (Linux/git-bash) — which is why this likely went unnoticed.
Root cause
Each hook reads the file mtime with:
mtime=$(stat -f %m "$progress" 2>/dev/null || stat -c %Y "$progress" 2>/dev/null || echo 0)
stat -f %m is the BSD/macOS syntax (works on macOS). On GNU stat, -f means "display filesystem status" — it does not fail, it prints a non-numeric string. Since it's tried first, the || fallback to stat -c %Y never fires, $mtime gets text, and the next line if (( mtime > newest )) evaluates that text as a variable name. Under set -euo pipefail this throws:
.claude/hooks/user-prompt-submit.sh: line 28: File: unbound variable
and the hook exits 1.
Steps to reproduce (git-bash)
echo '{}' | bash .claude/hooks/user-prompt-submit.sh; echo $? # prints 1, should be 0
./scripts/quality.sh # fails on hook tests
Fix (cross-platform, no macOS regression)
Try GNU first, fall back to BSD, and guard that the result is numeric:
mtime=$(stat -c %Y "$progress" 2>/dev/null || stat -f %m "$progress" 2>/dev/null || echo 0)
[[ "$mtime" =~ ^[0-9]+$ ]] || mtime=0
Apply to all three hooks. On macOS, stat -c fails → falls back to stat -f %m (behavior unchanged); on GNU, stat -c %Y returns the epoch.
Bonus (minor, test robustness)
In tests/hooks/test-stop.sh, the "multi-feature" case uses a plain touch, which can tie on coarse-granularity filesystems (Windows) and make the strict > comparison pick the wrong file. Forcing an explicit time makes it deterministic:
touch -d '2037-01-01 00:00' .claude/plans/features/002-newer/progress.md
Result
After both changes, ./scripts/quality.sh passes 56/56 on git-bash.
- Dominant language
- Shell
- Stars
- 18
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Contributor guide
No contributing guide indexed for this repository
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.
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
astral-sh/python-build-standalone#1293 ·
-
package-update
Difficulty 1/5 Under an hour Newbie friendliness 90/100
oSoWoSo/vOid_Community_repOsitory#185 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
carbon-language/carbon-lang#7821 ·