Tom Fuertes

Force Every Claude Code Subagent to Background

21 Feb 2026

Claude Code's Task tool spawns subagents in the foreground by default, which blocks your input for 10-30+ seconds per call. Telling it to use run_in_background: true in CLAUDE.md works maybe 70% of the time. A PreToolUse hook works every time.

#!/usr/bin/env bash
# PreToolUse hook (Task): silently inject run_in_background: true
# Keeps the lead unblocked so the user can always type
set -euo pipefail

INPUT=$(cat)

# Already background - pass through
echo "$INPUT" | jq -e '.tool_input.run_in_background == true' &>/dev/null && exit 0

# Silently add run_in_background (pass full input - updatedInput may replace, not merge)
echo "$INPUT" | jq '{
  hookSpecificOutput: {
    hookEventName: "PreToolUse",
    permissionDecision: "allow",
    updatedInput: (.tool_input + { run_in_background: true })
  }
}'

Register it in settings.json with "matcher": "Task" (full config in this gist).

updatedInput changes the tool parameters before the call runs, and Claude never sees the change. No deny-and-retry, no wasted tokens.

updatedInput vs deny

Most hook examples online deny the call and make Claude retry. That makes sense when the fix needs judgment, like secrets in code or a destructive git command. When the fix is always the same parameter, the retry is just a wasted round trip.

Other conventions you could inject the same way:

If a hook would always apply the same fix, use updatedInput. Deny when it needs a decision.

-Tom