Skip to content

Commit 631e27a

Browse files
fix: fix Claude Code hooks for Windows and add branch name validation
- enrich-context.sh: move backslash normalization into node (bash ${VAR//\//} fails on Git Bash); use additionalContext output format so context surfaces in Claude's conversation; include file/symbol details instead of just counts - guard-git.sh: add branch name validation on git push — blocks branches that don't match the conventional prefix pattern (feat/, fix/, docs/, etc.) - rebuild-graph.sh → update-graph.sh: rename to reflect incremental behavior; fix same backslash normalization bug - settings.json: bump enrich-context timeout to 10s, reference renamed update-graph.sh
1 parent 863e8a4 commit 631e27a

4 files changed

Lines changed: 38 additions & 27 deletions

File tree

.claude/hooks/enrich-context.sh

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,23 @@ set -euo pipefail
88
# Read the tool input from stdin
99
INPUT=$(cat)
1010

11-
# Extract file path based on tool type
12-
# Read tool uses tool_input.file_path, Grep uses tool_input.path
13-
FILE_PATH=$(echo "$INPUT" | node -e "
11+
# Extract file path and convert to relative — all in node to avoid
12+
# bash backslash issues on Windows/Git Bash
13+
REL_PATH=$(printf '%s' "$INPUT" | CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}" node -e "
1414
let d='';
1515
process.stdin.on('data',c=>d+=c);
1616
process.stdin.on('end',()=>{
1717
const o=JSON.parse(d).tool_input||{};
18-
const p=o.file_path||o.path||'';
19-
if(p)process.stdout.write(p);
18+
let p=(o.file_path||o.path||'').replace(/\\\\/g,'/');
19+
if(!p)return;
20+
let dir=(process.env.CLAUDE_PROJECT_DIR||'.').replace(/\\\\/g,'/');
21+
if(p.startsWith(dir))p=p.slice(dir.length+1);
22+
process.stdout.write(p);
2023
});
2124
" 2>/dev/null) || true
2225

2326
# Guard: no file path found
24-
if [ -z "$FILE_PATH" ]; then
27+
if [ -z "$REL_PATH" ]; then
2528
exit 0
2629
fi
2730

@@ -36,15 +39,6 @@ if ! command -v codegraph &>/dev/null && ! command -v npx &>/dev/null; then
3639
exit 0
3740
fi
3841

39-
# Convert absolute path to relative (strip project dir prefix)
40-
REL_PATH="$FILE_PATH"
41-
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
42-
if [[ "$FILE_PATH" == "${PROJECT_DIR}"* ]]; then
43-
REL_PATH="${FILE_PATH#"${PROJECT_DIR}"/}"
44-
fi
45-
# Normalize backslashes to forward slashes (Windows compatibility)
46-
REL_PATH="${REL_PATH//\\//}"
47-
4842
# Run codegraph deps and capture output
4943
DEPS=""
5044
if command -v codegraph &>/dev/null; then
@@ -58,20 +52,24 @@ if [ -z "$DEPS" ] || [ "$DEPS" = "null" ]; then
5852
exit 0
5953
fi
6054

61-
# Output as informational context (never deny)
62-
echo "$DEPS" | node -e "
55+
# Output as additionalContext so it surfaces in Claude's context
56+
printf '%s' "$DEPS" | node -e "
6357
let d='';
6458
process.stdin.on('data',c=>d+=c);
6559
process.stdin.on('end',()=>{
6660
try {
6761
const o=JSON.parse(d);
6862
const r=o.results?.[0]||{};
69-
const imports=(r.imports||[]).length;
70-
const importedBy=(r.importedBy||[]).length;
71-
const defs=(r.definitions||[]).length;
63+
const imports=(r.imports||[]).map(i=>i.file).join(', ');
64+
const importedBy=(r.importedBy||[]).map(i=>i.file).join(', ');
65+
const defs=(r.definitions||[]).map(d=>d.kind+' '+d.name).join(', ');
7266
const file=o.file||'unknown';
67+
let ctx='[codegraph] '+file;
68+
if(imports)ctx+='\n Imports: '+imports;
69+
if(importedBy)ctx+='\n Imported by: '+importedBy;
70+
if(defs)ctx+='\n Defines: '+defs;
7371
console.log(JSON.stringify({
74-
hookSpecificOutput: 'Codegraph context for '+file+':\\n Imports: '+imports+' files\\n Imported by: '+importedBy+' files\\n Definitions: '+defs+' symbols'
72+
hookSpecificOutput: { additionalContext: ctx }
7573
}));
7674
} catch(e) {}
7775
});

.claude/hooks/guard-git.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ if echo "$COMMAND" | grep -qE '^\s*git\s+stash'; then
7474
deny "BLOCKED: 'git stash' hides all working tree changes including other sessions' work. In worktree mode, commit your changes directly instead."
7575
fi
7676

77+
# --- Branch name validation on push ---
78+
79+
if echo "$COMMAND" | grep -qE '^\s*git\s+push'; then
80+
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || true
81+
if [ -n "$BRANCH" ] && [ "$BRANCH" != "main" ] && [ "$BRANCH" != "HEAD" ]; then
82+
PATTERN="^(feat|fix|docs|refactor|test|chore|ci|perf|build|release|dependabot|revert)/"
83+
if [[ ! "$BRANCH" =~ $PATTERN ]]; then
84+
deny "BLOCKED: Branch '$BRANCH' does not match required pattern. Branch names must start with: feat/, fix/, docs/, refactor/, test/, chore/, ci/, perf/, build/, release/, revert/"
85+
fi
86+
fi
87+
fi
88+
7789
# --- Commit validation against edit log ---
7890

7991
if echo "$COMMAND" | grep -qE '^\s*git\s+commit'; then
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
#!/usr/bin/env bash
22
# rebuild-graph.sh — PostToolUse hook for Edit and Write tools
3-
# Incrementally rebuilds the codegraph after source file edits.
3+
# Incrementally updates the codegraph after source file edits.
44
# Always exits 0 (informational only, never blocks).
55

66
set -euo pipefail
77

88
INPUT=$(cat)
99

10-
# Extract file path using node (jq may not be available on Windows)
10+
# Extract file path and normalize backslashes — all in node to avoid
11+
# bash backslash issues on Windows/Git Bash
1112
FILE_PATH=$(echo "$INPUT" | node -e "
1213
let d='';
1314
process.stdin.on('data',c=>d+=c);
1415
process.stdin.on('end',()=>{
15-
const p=JSON.parse(d).tool_input?.file_path||'';
16+
const p=(JSON.parse(d).tool_input?.file_path||'').replace(/\\\\/g,'/');
1617
if(p)process.stdout.write(p);
1718
});
1819
" 2>/dev/null) || true

.claude/settings.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
{
2323
"type": "command",
2424
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/enrich-context.sh\"",
25-
"timeout": 5
25+
"timeout": 10
2626
}
2727
]
2828
},
@@ -32,7 +32,7 @@
3232
{
3333
"type": "command",
3434
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/enrich-context.sh\"",
35-
"timeout": 5
35+
"timeout": 10
3636
}
3737
]
3838
}
@@ -43,7 +43,7 @@
4343
"hooks": [
4444
{
4545
"type": "command",
46-
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/rebuild-graph.sh\"",
46+
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/update-graph.sh\"",
4747
"timeout": 30
4848
},
4949
{

0 commit comments

Comments
 (0)