Skip to content

Commit bf06cca

Browse files
committed
fix: resolve merge conflicts with main
2 parents 263c39b + 3101226 commit bf06cca

10 files changed

Lines changed: 646 additions & 28 deletions

File tree

.claude/hooks/snapshot-pre-bash.sh

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,27 @@ if [ -z "$COMMAND" ]; then
2323
exit 0
2424
fi
2525

26-
# Skip read-only commands that can never write files — reduces snapshot overhead
27-
# for the most common Bash calls (ls, cat, grep, git log, git status, etc.).
26+
# Skip commands that can NEVER write files — reduces snapshot overhead for the
27+
# most common read-only Bash calls. Only include commands that have no
28+
# write-capable flags/modes at all. Notably absent:
29+
# - echo, printf — write files via shell redirections (echo … > file)
30+
# - find — can write via -exec sed -i, -exec cp, -delete, etc.
31+
# - awk — can write via redirection or getline
32+
# - node -e/-p — can write files via the Node.js fs module
2833
# sed is intentionally NOT in this list because `sed -i` modifies files in-place.
29-
if echo "$COMMAND" | grep -qE '^\s*(ls|cat|head|tail|grep|find|git\s+(log|status|diff|show|branch|remote|fetch|rev-parse|stash\s+list|ls-files|blame|describe|tag|config\s+--get)|gh\s+(pr|issue|repo)\s+(view|list|status)|echo|printf|pwd|which|node\s+-e|node\s+-p|npx\s+--version|wc|sort|uniq|awk)\b'; then
34+
if echo "$COMMAND" | grep -qE '^\s*(ls|cat|head|tail|grep|git\s+(log|status|diff|show|branch|remote|fetch|rev-parse|stash\s+list|ls-files|blame|describe|tag|config\s+--get)|gh\s+(pr|issue|repo)\s+(view|list|status)|pwd|which|npx\s+--version|wc|sort|uniq)\b'; then
3035
exit 0
3136
fi
3237

3338
# Resolve the project root (worktree-aware — each worktree has its own .claude/)
3439
PROJECT_DIR=$(git rev-parse --show-toplevel 2>/dev/null) || PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
3540

36-
# Key the snapshot file to the project root so parallel worktrees don't collide.
37-
# Use a simple hash of the path — just enough to be unique per worktree.
41+
# Key the snapshot file to (project root, command) so concurrent Bash calls
42+
# within the same session don't overwrite each other's baseline.
43+
# Claude Code can issue multiple Bash tool calls in parallel; using just the
44+
# project hash would mean call B's pre-hook overwrites call A's snapshot before
45+
# A's post-hook runs, silently dropping A's file writes from session-edits.log.
46+
# Including a hash of the command makes each concurrent call use a distinct file.
3847
PROJECT_HASH=$(echo "$PROJECT_DIR" | node -e "
3948
const crypto = require('crypto');
4049
let d='';
@@ -44,7 +53,16 @@ PROJECT_HASH=$(echo "$PROJECT_DIR" | node -e "
4453
});
4554
" 2>/dev/null) || PROJECT_HASH="default"
4655

47-
SNAPSHOT_FILE="/tmp/claude-bash-snapshot-${PROJECT_HASH}.txt"
56+
CMD_HASH=$(echo "$COMMAND" | node -e "
57+
const crypto = require('crypto');
58+
let d='';
59+
process.stdin.on('data',c=>d+=c);
60+
process.stdin.on('end',()=>{
61+
process.stdout.write(crypto.createHash('sha1').update(d.trim()).digest('hex').slice(0,8));
62+
});
63+
" 2>/dev/null) || CMD_HASH="default"
64+
65+
SNAPSHOT_FILE="/tmp/claude-bash-snapshot-${PROJECT_HASH}-${CMD_HASH}.txt"
4866

4967
# Capture current git status --porcelain.
5068
# Lines look like: "XY filename" or "XY orig -> dest" (rename).

.claude/hooks/track-bash-writes.sh

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ fi
2727
# Resolve the project root (worktree-aware — each worktree has its own .claude/)
2828
PROJECT_DIR=$(git rev-parse --show-toplevel 2>/dev/null) || PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
2929

30-
# Reproduce the same project hash used by snapshot-pre-bash.sh
30+
# Reproduce the same snapshot path used by snapshot-pre-bash.sh.
31+
# The snapshot is keyed by (project root, command) so concurrent Bash calls
32+
# within the same session each get a distinct file — preventing parallel calls
33+
# from overwriting each other's baseline.
3134
PROJECT_HASH=$(echo "$PROJECT_DIR" | node -e "
3235
const crypto = require('crypto');
3336
let d='';
@@ -37,7 +40,16 @@ PROJECT_HASH=$(echo "$PROJECT_DIR" | node -e "
3740
});
3841
" 2>/dev/null) || PROJECT_HASH="default"
3942

40-
SNAPSHOT_FILE="/tmp/claude-bash-snapshot-${PROJECT_HASH}.txt"
43+
CMD_HASH=$(echo "$COMMAND" | node -e "
44+
const crypto = require('crypto');
45+
let d='';
46+
process.stdin.on('data',c=>d+=c);
47+
process.stdin.on('end',()=>{
48+
process.stdout.write(crypto.createHash('sha1').update(d.trim()).digest('hex').slice(0,8));
49+
});
50+
" 2>/dev/null) || CMD_HASH="default"
51+
52+
SNAPSHOT_FILE="/tmp/claude-bash-snapshot-${PROJECT_HASH}-${CMD_HASH}.txt"
4153

4254
# If there is no snapshot (hook was not installed yet, or the pre-hook was
4355
# skipped for a read-only command) we have no baseline — exit cleanly.

0 commit comments

Comments
 (0)