Initial commit
This commit is contained in:
119
hooks/post-tool-use/01-track-edits.sh
Executable file
119
hooks/post-tool-use/01-track-edits.sh
Executable file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
CONTEXT_DIR="$(dirname "$0")/../context"
|
||||
LOG_FILE="$CONTEXT_DIR/edit-log.txt"
|
||||
LOCK_FILE="$CONTEXT_DIR/.edit-log.lock"
|
||||
MAX_LOG_LINES=1000
|
||||
LOCK_TIMEOUT=5
|
||||
|
||||
# Create context dir and log if doesn't exist
|
||||
mkdir -p "$CONTEXT_DIR"
|
||||
touch "$LOG_FILE"
|
||||
|
||||
# Acquire lock with timeout
|
||||
acquire_lock() {
|
||||
local count=0
|
||||
while [ $count -lt $LOCK_TIMEOUT ]; do
|
||||
if mkdir "$LOCK_FILE" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
count=$((count + 1))
|
||||
done
|
||||
# Log but don't fail - non-blocking requirement
|
||||
echo "Warning: Could not acquire lock" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# Release lock
|
||||
release_lock() {
|
||||
rmdir "$LOCK_FILE" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Clean up lock on exit
|
||||
trap release_lock EXIT
|
||||
|
||||
# Function to log edit
|
||||
log_edit() {
|
||||
local file_path="$1"
|
||||
local tool_name="$2"
|
||||
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
|
||||
local repo=$(find_repo "$file_path")
|
||||
|
||||
if acquire_lock; then
|
||||
echo "$timestamp | $repo | $tool_name | $file_path" >> "$LOG_FILE"
|
||||
release_lock
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to find repo root
|
||||
find_repo() {
|
||||
local file_path="$1"
|
||||
if [ -z "$file_path" ] || [ "$file_path" = "null" ]; then
|
||||
echo "unknown"
|
||||
return
|
||||
fi
|
||||
|
||||
local dir
|
||||
dir=$(dirname "$file_path" 2>/dev/null || echo "/")
|
||||
while [ "$dir" != "/" ] && [ -n "$dir" ]; do
|
||||
if [ -d "$dir/.git" ]; then
|
||||
basename "$dir"
|
||||
return
|
||||
fi
|
||||
dir=$(dirname "$dir" 2>/dev/null || echo "/")
|
||||
done
|
||||
echo "unknown"
|
||||
}
|
||||
|
||||
# Read tool use event from stdin (with timeout to prevent hanging)
|
||||
if ! read -t 2 -r tool_use_json; then
|
||||
echo '{}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Validate JSON to prevent injection
|
||||
if ! echo "$tool_use_json" | jq empty 2>/dev/null; then
|
||||
echo '{}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Extract tool name and file path from tool use
|
||||
tool_name=$(echo "$tool_use_json" | jq -r '.tool.name // .tool_name // "unknown"' 2>/dev/null || echo "unknown")
|
||||
file_path=""
|
||||
|
||||
case "$tool_name" in
|
||||
"Edit"|"Write")
|
||||
file_path=$(echo "$tool_use_json" | jq -r '.tool.input.file_path // .tool_input.file_path // "null"' 2>/dev/null || echo "null")
|
||||
;;
|
||||
"MultiEdit")
|
||||
# MultiEdit has multiple files - log each
|
||||
echo "$tool_use_json" | jq -r '.tool.input.edits[]?.file_path // .tool_input.edits[]?.file_path // empty' 2>/dev/null | while read -r path; do
|
||||
if [ -n "$path" ] && [ "$path" != "null" ]; then
|
||||
log_edit "$path" "$tool_name"
|
||||
fi
|
||||
done
|
||||
echo '{}'
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Log single edit
|
||||
if [ -n "$file_path" ] && [ "$file_path" != "null" ]; then
|
||||
log_edit "$file_path" "$tool_name"
|
||||
fi
|
||||
|
||||
# Rotate log if too large (with lock)
|
||||
if acquire_lock; then
|
||||
line_count=$(wc -l < "$LOG_FILE" 2>/dev/null || echo "0")
|
||||
if [ "$line_count" -gt "$MAX_LOG_LINES" ]; then
|
||||
tail -n "$MAX_LOG_LINES" "$LOG_FILE" > "$LOG_FILE.tmp"
|
||||
mv "$LOG_FILE.tmp" "$LOG_FILE"
|
||||
fi
|
||||
release_lock
|
||||
fi
|
||||
|
||||
# Return success (non-blocking)
|
||||
echo '{}'
|
||||
Reference in New Issue
Block a user