Skip to content

Commit 08d6ada

Browse files
authored
feat: add Claude Code PostToolUse hooks (dart formatter + newline enforcer) (#995)
1 parent 32bb0b3 commit 08d6ada

File tree

7 files changed

+92
-107
lines changed

7 files changed

+92
-107
lines changed

.claude/hooks/dart_formatter.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env bash
2+
# PostToolUse hook: auto-format .dart files after Write/Edit/MultiEdit.
3+
# Generated files (*.g.dart, *.freezed.dart, *.gr.dart) are skipped.
4+
INPUT=$(cat)
5+
FILE=$(jq -r '(.tool_input.file_path // .tool_input.path // "")' <<< "$INPUT")
6+
7+
[[ -z "$FILE" || ! -f "$FILE" ]] && exit 0
8+
[[ "$FILE" != *.dart ]] && exit 0
9+
[[ "$FILE" == *.g.dart || "$FILE" == *.freezed.dart || "$FILE" == *.gr.dart ]] && exit 0
10+
11+
ROOT=$(git rev-parse --show-toplevel 2>/dev/null) && cd "$ROOT"
12+
dart format "$FILE"

.claude/hooks/env_file_guard.py

Lines changed: 0 additions & 70 deletions
This file was deleted.

.claude/hooks/env_file_guard.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env bash
2+
# PreToolUse hook: block any access to .env files.
3+
INPUT=$(cat)
4+
TOOL=$(jq -r '.tool // ""' <<< "$INPUT")
5+
COMMAND=$(jq -r '.tool_input.command // ""' <<< "$INPUT")
6+
FILE=$(jq -r '(.tool_input.file_path // .tool_input.path // "")' <<< "$INPUT")
7+
8+
if [[ "$TOOL" == "Bash" || "$TOOL" == "Run" ]]; then
9+
if echo "$COMMAND" | grep -qE '\b(cat|less|more|head|tail|bat|view|nano|vi|vim|code|open|type|strings|xxd|hexdump|od|base64)\b' \
10+
&& echo "$COMMAND" | grep -qE '\.env($|\.)'; then
11+
echo "🚫 BLOCKED: reads a .env file — secrets must not be read by the agent." >&2
12+
exit 2
13+
fi
14+
if echo "$COMMAND" | grep -qE '\b(cp|mv|scp|rsync|tar|zip)\b' \
15+
&& echo "$COMMAND" | grep -qE '\.env($|\.)'; then
16+
echo "🚫 BLOCKED: copies/moves a .env file — secrets must not be transferred." >&2
17+
exit 2
18+
fi
19+
if echo "$COMMAND" | grep -qE '\b(find|grep|rg|ag|fd)\b' \
20+
&& echo "$COMMAND" | grep -qE '\.env($|\.)'; then
21+
echo "🚫 BLOCKED: searches for .env files — secret files must not be scanned." >&2
22+
exit 2
23+
fi
24+
fi
25+
26+
if [[ "$TOOL" =~ ^(Read|View|Edit|Write|MultiEdit)$ ]]; then
27+
if echo "$FILE" | grep -qE '\.env($|\.)'; then
28+
echo "🚫 BLOCKED: Cannot access '$FILE' — .env files contain secrets." >&2
29+
exit 2
30+
fi
31+
fi
32+
33+
exit 0

.claude/hooks/md_formatter.py

Lines changed: 0 additions & 34 deletions
This file was deleted.

.claude/hooks/md_formatter.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env bash
2+
# PostToolUse hook: runs markdownlint-cli2 --fix on .md files after Write/Edit/MultiEdit.
3+
INPUT=$(cat)
4+
FILE=$(jq -r '(.tool_input.file_path // .tool_input.path // "")' <<< "$INPUT")
5+
6+
[[ -z "$FILE" || "$FILE" != *.md ]] && exit 0
7+
8+
ROOT=$(git rev-parse --show-toplevel 2>/dev/null) && cd "$ROOT"
9+
npx --yes markdownlint-cli2 --fix "$FILE" >/dev/null 2>&1
10+
exit 0

.claude/hooks/newline_enforcer.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env bash
2+
# PostToolUse hook: ensure text files end with a newline after Write/Edit/MultiEdit.
3+
INPUT=$(cat)
4+
FILE=$(jq -r '(.tool_input.file_path // .tool_input.path // "")' <<< "$INPUT")
5+
6+
[[ -z "$FILE" || ! -f "$FILE" ]] && exit 0
7+
[[ "$FILE" == *.g.dart || "$FILE" == *.freezed.dart || "$FILE" == *.gr.dart ]] && exit 0
8+
9+
case "$FILE" in
10+
*.dart|*.yaml|*.yml|*.json|*.md|*.py|*.sh|*.kt|*.kts|*.swift|*.gradle|*.xml|*.html|*.txt) ;;
11+
*) exit 0 ;;
12+
esac
13+
14+
[[ -n "$(tail -c 1 "$FILE")" ]] && printf '\n' >> "$FILE"
15+
exit 0

.claude/settings.json

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,26 @@
4747
"hooks": [
4848
{
4949
"type": "command",
50-
"command": "python3 .claude/hooks/md_formatter.py"
50+
"command": "ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0; bash \"$ROOT/.claude/hooks/md_formatter.sh\""
51+
}
52+
]
53+
},
54+
{
55+
"matcher": "Write|Edit|MultiEdit",
56+
"hooks": [
57+
{
58+
"type": "command",
59+
"command": "ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0; bash \"$ROOT/.claude/hooks/dart_formatter.sh\"",
60+
"timeout": 30
61+
}
62+
]
63+
},
64+
{
65+
"matcher": "Write|Edit|MultiEdit",
66+
"hooks": [
67+
{
68+
"type": "command",
69+
"command": "ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0; bash \"$ROOT/.claude/hooks/newline_enforcer.sh\""
5170
}
5271
]
5372
}
@@ -58,7 +77,7 @@
5877
"hooks": [
5978
{
6079
"type": "command",
61-
"command": "python3 .claude/hooks/env_file_guard.py"
80+
"command": "ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0; bash \"$ROOT/.claude/hooks/env_file_guard.sh\""
6281
}
6382
]
6483
},
@@ -67,7 +86,7 @@
6786
"hooks": [
6887
{
6988
"type": "command",
70-
"command": "python3 .claude/hooks/env_file_guard.py"
89+
"command": "ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0; bash \"$ROOT/.claude/hooks/env_file_guard.sh\""
7190
}
7291
]
7392
}

0 commit comments

Comments
 (0)