integrations

Gemini CLI

Integrate brin with Gemini CLI hooks to automatically scan packages installed by AI agents

Gemini CLI's hooks system lets you intercept and customize agent behavior at specific points in the agentic loop. By using the BeforeTool hook, you can automatically route all package installations through brin for security scanning.

Prerequisites

Setup

1. Create the hooks configuration

Create .gemini/settings.json in your project root:

JSON
{
  "hooks": {
    "BeforeTool": [
      {
        "matcher": "shell|bash|run_shell_command",
        "hooks": [
          {
            "name": "brin-gateway",
            "type": "command",
            "command": "$GEMINI_PROJECT_DIR/.gemini/hooks/brin-gateway.sh",
            "timeout": 5000
          }
        ]
      }
    ]
  }
}

The matcher is a regex that matches shell-related tool names, ensuring the hook only runs for shell commands.

2. Create the hook script

Create .gemini/hooks/brin-gateway.sh:

Bash
#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.input.command // .input.args.command // empty')
 
# Match install commands with packages
if [[ "$cmd" =~ ^(npm\ (i|install|add)|yarn\ add|pnpm\ (add|i)|bun\ (add|i)|pip\ install|cargo\ add)\ (.+)$ ]]; then
  pkg="${BASH_REMATCH[5]}"
  jq -n --arg p "$pkg" '{decision:"block", reason:"Use `brin add \($p)` instead for security scanning."}'
elif [[ "$cmd" =~ ^(npm\ (uninstall|rm)|yarn\ remove|pnpm\ (rm|remove)|bun\ (rm|remove)|pip\ uninstall|cargo\ remove)\ (.+)$ ]]; then
  pkg="${BASH_REMATCH[5]}"
  jq -n --arg p "$pkg" '{decision:"block", reason:"Use `brin remove \($p)` instead."}'
else
  echo '{"decision":"allow"}'
fi

3. Make the script executable

Bash
chmod +x .gemini/hooks/brin-gateway.sh

Important: JSON Output Rules

Gemini CLI has strict requirements for hook output:

  • Only JSON to stdout - Your script must output only valid JSON. No echo or print statements before the JSON.
  • Use stderr for debugging - Write debug output to stderr: echo "debug" >&2
  • Exit code 0 - Return exit code 0 with decision: "block" to deny an action
  • Exit code 2 - For critical system blocks (script failures, security stops)

If stdout contains any non-JSON text, parsing fails and the hook is treated as allowing the action.

Global Configuration

To apply brin hooks to all your projects, add the configuration to your user settings at ~/.gemini/settings.json.

For the hook script, either:

  • Place it at a fixed global path and reference it absolutely
  • Or use $GEMINI_PROJECT_DIR for project-relative paths

Managing Hooks

Gemini CLI provides commands to manage hooks without editing JSON:

Bash
# View all hooks
/hooks panel
 
# Enable/disable all hooks
/hooks enable-all
/hooks disable-all
 
# Toggle individual hooks
/hooks enable brin-gateway
/hooks disable brin-gateway

Troubleshooting

Hook not triggering

  1. Verify the settings file exists at .gemini/settings.json
  2. Check that the script is executable: chmod +x .gemini/hooks/brin-gateway.sh
  3. Verify the matcher regex matches your shell tool name
  4. Check /hooks panel to see if the hook is enabled

JSON parsing errors

If the hook isn't blocking commands, check that your script outputs only JSON:

Bash
# Test the script
echo '{"input":{"command":"npm install express"}}' | .gemini/hooks/brin-gateway.sh

Expected output (no extra text):

JSON
{"decision":"block","reason":"Use `brin add express` instead for security scanning."}

Debugging

Write debug output to stderr (not stdout):

Bash
#!/bin/bash
input=$(cat)
echo "Received: $input" >&2  # Debug to stderr
cmd=$(echo "$input" | jq -r '.input.command // empty')
echo "Command: $cmd" >&2     # Debug to stderr
 
# ... rest of script