Skip to content

Commit fe9560f

Browse files
committed
Refine AI hook pipeline and tighten gh permissions
- session-start.sh: use session_id from hook input instead of generating a custom one, with fallback to timestamp-pid - post-edit-record.sh, stop-format.sh: use ${TMPDIR:-/tmp} instead of hardcoded /tmp to respect contributor's environment - stop-format.sh: skip formatting and tests entirely when no files were edited this turn (no wasted Docker containers on read-only turns) - settings.json: replace broad Bash(gh pr:*) and Bash(gh api:*) with granular read-only allows (view, list, diff, checks, status, search); write operations (create, edit, comment, merge) prompt by default
1 parent 871e484 commit fe9560f

4 files changed

Lines changed: 46 additions & 29 deletions

File tree

.claude/hooks/post-edit-record.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
88

99
# Only track .php files, skip vendored/generated paths
1010
if [[ "$FILE" == *.php ]] && [[ "$FILE" != vendor/* ]] && [[ "$FILE" != */vendor/* ]] && [[ "$FILE" != coverage/* ]] && [[ "$FILE" != */coverage/* ]]; then
11-
TRACKER="/tmp/claude-edited-php-files-${CLAUDE_HOOK_SESSION_ID:-default}"
11+
TRACKER="${TMPDIR:-/tmp}/claude-edited-php-files-${CLAUDE_HOOK_SESSION_ID:-default}"
1212
echo "$FILE" >> "$TRACKER"
1313
sort -u "$TRACKER" -o "$TRACKER"
1414
fi

.claude/hooks/session-start.sh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
# No network calls, no installs, no docker run - just detect and report.
44
cd "$CLAUDE_PROJECT_DIR" || exit 0
55

6-
# Generate a stable session ID and persist via CLAUDE_ENV_FILE
6+
# Use session_id from hook input and persist via CLAUDE_ENV_FILE
77
# so the edit tracker and stop hook share the same file path
8-
SESSION_ID="$(date +%s)-$$"
8+
INPUT=$(cat)
9+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty')
10+
if [[ -z "$SESSION_ID" ]]; then
11+
SESSION_ID="$(date +%s)-$$"
12+
fi
913
if [[ -n "$CLAUDE_ENV_FILE" ]]; then
1014
echo "export CLAUDE_HOOK_SESSION_ID='$SESSION_ID'" >> "$CLAUDE_ENV_FILE"
1115
fi

.claude/hooks/stop-format.sh

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,37 @@
11
#!/bin/bash
22
# Stop hook: batch-format PHP files touched this turn, run test suite, report issues.
3-
# Tests are fast (~2s) so we include them every turn - failures are reported as context.
3+
# Tests are fast (~2s) so we include them when files were edited.
4+
# Failures are reported as context, not blocking.
45
cd "$CLAUDE_PROJECT_DIR" || exit 0
56

6-
TRACKER="/tmp/claude-edited-php-files-${CLAUDE_HOOK_SESSION_ID:-default}"
7+
TRACKER="${TMPDIR:-/tmp}/claude-edited-php-files-${CLAUDE_HOOK_SESSION_ID:-default}"
8+
9+
# Skip entirely if no files were edited this turn
10+
[[ -f "$TRACKER" ]] || exit 0
11+
12+
files=$(cat "$TRACKER")
13+
rm -f "$TRACKER"
14+
[[ -n "$files" ]] || exit 0
715

816
ctx=""
917

10-
# Batch php-cs-fixer autocorrect on tracked files (if any were edited)
11-
if [[ -f "$TRACKER" ]]; then
12-
files=$(cat "$TRACKER")
13-
rm -f "$TRACKER"
14-
15-
if [[ -n "$files" ]]; then
16-
if docker image inspect chartmogulphp82 &>/dev/null 2>&1; then
17-
file_args=$(echo "$files" | tr '\n' ' ')
18-
docker run --rm -w /src -v "$(pwd):/src" -v "$HOME/.composer/cache:/root/.composer/cache" chartmogulphp82 \
19-
bash -c "vendor/bin/php-cs-fixer fix --no-interaction --quiet $file_args" 2>/dev/null || true
20-
lint_out=$(docker run --rm -w /src -v "$(pwd):/src" -v "$HOME/.composer/cache:/root/.composer/cache" chartmogulphp82 \
21-
bash -c "vendor/bin/php-cs-fixer fix --dry-run --no-interaction $file_args 2>&1")
22-
elif [[ -x vendor/bin/php-cs-fixer ]]; then
23-
echo "$files" | xargs vendor/bin/php-cs-fixer fix --no-interaction --quiet 2>/dev/null || true
24-
lint_out=$(echo "$files" | xargs vendor/bin/php-cs-fixer fix --dry-run --no-interaction 2>&1)
25-
fi
26-
offenses=$(echo "$lint_out" | grep -E "^\s+\d+\)" | head -20)
27-
if [[ -n "$offenses" ]]; then
28-
ctx+="php-cs-fixer offenses remaining after autocorrect:\n$offenses\n"
29-
fi
30-
fi
18+
# Batch php-cs-fixer autocorrect on tracked files
19+
if docker image inspect chartmogulphp82 &>/dev/null 2>&1; then
20+
file_args=$(echo "$files" | tr '\n' ' ')
21+
docker run --rm -w /src -v "$(pwd):/src" -v "$HOME/.composer/cache:/root/.composer/cache" chartmogulphp82 \
22+
bash -c "vendor/bin/php-cs-fixer fix --no-interaction --quiet $file_args" 2>/dev/null || true
23+
lint_out=$(docker run --rm -w /src -v "$(pwd):/src" -v "$HOME/.composer/cache:/root/.composer/cache" chartmogulphp82 \
24+
bash -c "vendor/bin/php-cs-fixer fix --dry-run --no-interaction $file_args 2>&1")
25+
elif [[ -x vendor/bin/php-cs-fixer ]]; then
26+
echo "$files" | xargs vendor/bin/php-cs-fixer fix --no-interaction --quiet 2>/dev/null || true
27+
lint_out=$(echo "$files" | xargs vendor/bin/php-cs-fixer fix --dry-run --no-interaction 2>&1)
28+
fi
29+
offenses=$(echo "$lint_out" | grep -E "^\s+\d+\)" | head -20)
30+
if [[ -n "$offenses" ]]; then
31+
ctx+="php-cs-fixer offenses remaining after autocorrect:\n$offenses\n"
3132
fi
3233

33-
# Always run tests - edits to tests or src both matter (~2s)
34+
# Run tests - only when files were edited this turn (~2s)
3435
if docker image inspect chartmogulphp82 &>/dev/null 2>&1; then
3536
test_out=$(docker run --rm -w /src -v "$(pwd):/src" -v "$HOME/.composer/cache:/root/.composer/cache" chartmogulphp82 \
3637
phpunit 2>&1)

.claude/settings.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,20 @@
2222
"Bash(git pull:*)",
2323
"Bash(git cherry-pick:*)",
2424
"Bash(git reset:*)",
25-
"Bash(gh pr:*)",
26-
"Bash(gh api:*)"
25+
"Bash(gh pr view:*)",
26+
"Bash(gh pr list:*)",
27+
"Bash(gh pr diff:*)",
28+
"Bash(gh pr checks:*)",
29+
"Bash(gh pr status:*)",
30+
"Bash(gh run view:*)",
31+
"Bash(gh run list:*)",
32+
"Bash(gh run watch:*)",
33+
"Bash(gh issue view:*)",
34+
"Bash(gh issue list:*)",
35+
"Bash(gh release view:*)",
36+
"Bash(gh release list:*)",
37+
"Bash(gh repo view:*)",
38+
"Bash(gh search:*)"
2739
],
2840
"deny": [
2941
"Read(./.env*)"

0 commit comments

Comments
 (0)