From 8eef39eb30f3aca06e81382bef100277082d5f85 Mon Sep 17 00:00:00 2001 From: Edmund Miller Date: Thu, 18 Jun 2026 10:31:02 -0500 Subject: [PATCH] feat(hooks): add build-check Stop/SubagentStop hook When Claude or a subagent finishes, run `make compile` to catch syntax/compilation errors early; on failure, block and feed a parsed error summary back to Claude (falling back to the tail of the output when no error lines match). Synchronous/blocking by design so Claude reacts to breakage before yielding. Written in Bash; matches on hook_event_name Stop/SubagentStop and parses the payload with jq. Stacked on hooks-1-editorconfig. Signed-off-by: Edmund Miller --- .claude/README.md | 7 +++++++ .claude/hooks/check-build.sh | 32 ++++++++++++++++++++++++++++++++ .claude/settings.json | 22 ++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100755 .claude/hooks/check-build.sh diff --git a/.claude/README.md b/.claude/README.md index 24809b7317..ec4c6a5e95 100644 --- a/.claude/README.md +++ b/.claude/README.md @@ -23,6 +23,13 @@ understood on its own. This file documents the hooks that are currently wired up skips silently if it is not present) - **`async: true`**: runs in the background so formatting never blocks the conversation +### 2. Build check (Stop + SubagentStop) +- **Script**: `hooks/check-build.sh` +- **Trigger**: when Claude (or a subagent) finishes responding +- **Action**: runs `make compile` to catch syntax/compilation errors early +- **Failure mode**: blocking — feeds a parsed error summary back to Claude so it + can fix the breakage before yielding + ## Enabling / disabling Hooks are configured in `.claude/settings.json`. Disable one by removing its entry; diff --git a/.claude/hooks/check-build.sh b/.claude/hooks/check-build.sh new file mode 100755 index 0000000000..fd44689062 --- /dev/null +++ b/.claude/hooks/check-build.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# +# Build check hook for Nextflow development. +# +# On Stop / SubagentStop, run `make compile` to catch syntax and compilation +# errors. On failure, block and feed a parsed error summary back to Claude. +# +# Reads the Claude Code hook payload as JSON on stdin (requires `jq`). + +set -uo pipefail + +command -v jq >/dev/null 2>&1 || exit 0 + +input=$(cat) +event=$(jq -r '.hook_event_name // ""' <<<"$input") + +case "$event" in + Stop|SubagentStop) ;; + *) exit 0 ;; +esac + +start=$SECONDS +if out=$(make compile 2>&1); then + jq -n --arg s "$(( SECONDS - start ))" \ + '{suppressOutput: true, systemMessage: ("✓ Build check passed (" + $s + "s)")}' +else + summary=$(printf '%s\n' "$out" | grep -iE 'error|failed|exception' | tail -n 10) + [[ -n "$summary" ]] || summary=$(printf '%s\n' "$out" | tail -n 20) + jq -n --arg r "Build check failed: +$summary" \ + '{decision: "block", reason: $r, stopReason: "Build compilation failed"}' +fi diff --git a/.claude/settings.json b/.claude/settings.json index 5a7d4cf54f..4550c77c28 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -12,6 +12,28 @@ } ] } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-build.sh", + "timeout": 120 + } + ] + } + ], + "SubagentStop": [ + { + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-build.sh", + "timeout": 120 + } + ] + } ] } }