Skip to content

Instantly share code, notes, and snippets.

@leoafarias
Created March 25, 2026 14:45
Show Gist options
  • Select an option

  • Save leoafarias/3ceb297ea47b05d434f0f6ddecfaa6a7 to your computer and use it in GitHub Desktop.

Select an option

Save leoafarias/3ceb297ea47b05d434f0f6ddecfaa6a7 to your computer and use it in GitHub Desktop.
Hook to enforce native tool
#!/bin/bash
set -euo pipefail
# Read input from stdin
input=$(cat)
# Extract command (jq required)
command -v jq >/dev/null 2>&1 || exit 0
cmd=$(echo "$input" | jq -r '.tool_input.command // empty')
[ -z "$cmd" ] && exit 0
# Strip leading whitespace
cmd="${cmd#"${cmd%%[![:space:]]*}"}"
# Extract first command segment (before any pipe, &&, ||, or ;)
first_segment="${cmd%%|*}"
first_segment="${first_segment%%&&*}"
first_segment="${first_segment%%||*}"
first_segment="${first_segment%%;*}"
# Strip leading env var assignments (e.g., FOO=bar cmd)
clean="$first_segment"
while [[ "$clean" =~ ^[[:space:]]*[A-Za-z_][A-Za-z0-9_]*= ]]; do
clean="${clean#*[[:space:]]}"
done
clean="${clean#"${clean%%[![:space:]]*}"}"
# Get binary name (first word, strip path)
binary="${clean%% *}"
binary="${binary##*/}"
[ -z "$binary" ] && exit 0
# Unwrap bash -c / sh -c wrappers
if [[ "$binary" == "bash" || "$binary" == "sh" || "$binary" == "zsh" ]]; then
if [[ "$clean" =~ -c ]]; then
inner=$(echo "$clean" | sed -E "s/^[^ ]+ +-c +['\"]?//" | sed -E "s/['\"]?\s*$//")
inner_first="${inner%%|*}"
inner_first="${inner_first%%&&*}"
binary="${inner_first%% *}"
binary="${binary##*/}"
[ -z "$binary" ] && exit 0
else
exit 0
fi
fi
# Block and redirect to native tools
deny() {
echo "{\"hookSpecificOutput\": {\"permissionDecision\": \"deny\"}, \"systemMessage\": \"$1\"}" >&2
exit 2
}
case "$binary" in
cat)
deny "Use the Read tool instead of 'cat'. It supports offset and limit parameters for partial file reads." ;;
head)
deny "Use the Read tool with the 'limit' parameter instead of 'head'." ;;
tail)
deny "Use the Read tool with 'offset' and 'limit' parameters instead of 'tail'." ;;
grep|rg)
deny "Use the Grep tool instead of '$binary'. It supports regex, glob filters, context lines (-A/-B/-C), and output modes." ;;
find)
deny "Use the Glob tool instead of 'find'. Use patterns like '**/*.ts' for recursive matching." ;;
ls)
deny "Use the Glob tool instead of 'ls'. Use patterns like 'src/*' or '**/*.py'." ;;
sed)
deny "Use the Edit tool for file modifications instead of 'sed'. Use Read with offset/limit for line extraction." ;;
awk)
deny "Use the Grep tool for pattern matching or Edit tool for transformations instead of 'awk'." ;;
*)
exit 0 ;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment