LLxprt Code Hooks

July 23, 2026 · View on GitHub

The LLxprt Code hook system allows you to extend and customize the behavior of the CLI by running scripts at key points during execution. Hooks can intercept tool calls, modify LLM requests/responses, enforce security policies, add logging, and much more.

What Are Hooks?

Hooks are external scripts (bash, Python, or any executable) that LLxprt Code calls at specific points in the execution lifecycle. Your hook receives JSON input on stdin, processes it, and returns JSON output on stdout. Based on the output, LLxprt Code can:

  • Block operations (e.g., prevent writes to sensitive directories)
  • Modify tool inputs or LLM requests
  • Add context to responses
  • Log activity for auditing
  • Stop the agent entirely

Coming from Other Tools?

From Gemini CLI (Compatibility)

LLxprt Code's hook system is compatible with Gemini CLI's hook configuration. The key points:

  • Configuration uses the same settings.json format under the hooks key
  • Event names are similar: BeforeTool, AfterTool, BeforeModel, AfterModel
  • Scripts receive JSON on stdin and return JSON on stdout
  • Exit codes have the same semantics (0=success, 1=warning, 2=block)

From Claude Code

If you're used to Claude Code's permission system:

  • Hooks provide more granular control than simple allow/deny rules
  • You can implement custom logic (e.g., allow writes only to specific directories)
  • Hooks can modify inputs, not just approve/deny them
  • Multiple hooks can run for the same event with aggregated results

Security and Risks

Warning

Hooks execute arbitrary code with your user privileges.

By configuring hooks, you are explicitly allowing LLxprt Code to run shell commands on your machine. Malicious or poorly configured hooks can:

  • Exfiltrate data: Read sensitive files (.env, ssh keys) and send them to remote servers.
  • Modify system: Delete files, install malware, or change system settings.
  • Consume resources: Run infinite loops or crash your system.

Project-level hooks (in .llxprt/settings.json) and Extension hooks are particularly risky when opening third-party projects or extensions from untrusted authors. LLxprt Code will warn you when it detects untrusted project hooks (identified by their name and command). Untrusted hooks are not auto-approved — they remain untrusted until you explicitly trust them. It is your responsibility to review these hooks (and any installed extensions) before trusting them.

See Security Considerations for a detailed threat model and mitigation strategies.

Quick Start

1. Enable Hooks

Hooks are disabled by default and require explicit opt-in. Add the following to your user settings.json:

{
  "hooks": {
    "enabled": true
  }
}

There is also an experimental gate (tools.enableHooks) that defaults to true. Both hooks.enabled and tools.enableHooks must be true for hooks to run.

2. Configure a Hook

Add hooks to your settings.json. Hook scripts can live anywhere — the command field accepts any path with ~ expansion. The example keeps the script under LLxprt's config directory ($LLXPRT_CONFIG_HOME if set, otherwise the platform default ~/.config/llxprt-code on Linux / ~/Library/Preferences/llxprt-code on macOS / %APPDATA%\llxprt-code\Config on Windows). The shell snippets spell this out as ${LLXPRT_CONFIG_HOME:-$HOME/.config/llxprt-code}/hooks (the Linux default; swap in the macOS/Windows default for your platform):

{
  "hooks": {
    "BeforeTool": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${LLXPRT_CONFIG_HOME:-$HOME/.config/llxprt-code}/hooks/security-policy.sh"
          }
        ]
      }
    ]
  }
}

3. Write Your Hook Script

Create ${LLXPRT_CONFIG_HOME:-$HOME/.config/llxprt-code}/hooks/security-policy.sh (or any location you prefer):

#!/bin/bash
# Read JSON input from stdin
INPUT=$(cat)

# Parse the tool name
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')

# Parse the path if it's a file operation
PATH_VALUE=$(echo "$INPUT" | jq -r '.tool_input.path // empty')

# Block writes to /etc
if [[ "$TOOL_NAME" == "write_file" && "$PATH_VALUE" == /etc* ]]; then
  echo '{"decision": "deny", "reason": "Writing to /etc is prohibited"}'
  exit 2
fi

# Allow everything else
echo '{"decision": "allow"}'
exit 0

Make it executable:

chmod +x "${LLXPRT_CONFIG_HOME:-$HOME/.config/llxprt-code}/hooks/security-policy.sh"

4. Test It

Now when LLxprt Code tries to write to /etc/passwd, your hook will block it!

Hook Events

LLxprt Code supports these hook events:

EventWhen It FiresCommon Uses
BeforeToolBefore executing any toolSecurity policies, audit logging, input validation
AfterToolAfter a tool completesResult logging, adding context
BeforeModelBefore sending request to LLMRequest modification, prompt injection defense
AfterModelAfter receiving LLM responseResponse filtering, output modification
BeforeToolSelectionBefore tool selection phaseRestricting available tools
SessionStartWhen session beginsSession initialization
SessionEndWhen session endsCleanup, final logging
BeforeAgentBefore agent processes promptPrompt preprocessing
AfterAgentAfter agent completesResponse postprocessing

Hooks in Extensions

Extensions can bundle hooks alongside MCP servers and prompts. When you install an extension that includes hooks, those hooks are loaded automatically when the extension is enabled.

For details on how extension hooks work, including scope precedence and security considerations, see Hooks in Extensions.

Next Steps

Example Use Cases

Security Policy Enforcement

Block operations on sensitive files or directories:

# Deny writes to system directories
if [[ "$PATH_VALUE" == /etc* || "$PATH_VALUE" == /var* ]]; then
  echo '{"decision": "deny", "reason": "System directory access denied"}'
  exit 2
fi

Audit Logging

Log all tool calls for compliance. The recommended location is LLxprt's canonical log/state directory ($LLXPRT_LOG_HOME, falling back to $LLXPRT_CONFIG_HOME, then the platform default ~/.local/state/llxprt-code on Linux / ~/Library/Logs/llxprt-code on macOS / %LOCALAPPDATA%\llxprt-code\Log on Windows); you may also write to a workspace-local .llxprt/audit.log or wherever your team's policy requires:

# Log to LLxprt's canonical log/state directory (or choose your own path)
LOG_DIR="${LLXPRT_LOG_HOME:-${LLXPRT_CONFIG_HOME:-$HOME/.local/state/llxprt-code}}"
mkdir -p "$LOG_DIR"
echo "$(date) - Tool: $TOOL_NAME, Path: $PATH_VALUE" >> "$LOG_DIR/audit.log"
echo '{"decision": "allow"}'
exit 0

Cost Control

Limit expensive model calls by estimating content size:

#!/usr/bin/env python3
import json, sys

input_data = json.load(sys.stdin)
request = input_data.get('llm_request', {})

# Estimate token count from contents (rough approximation: ~4 chars per token)
total_chars = 0
for content in request.get('contents', []):
    for part in content.get('parts', []):
        if isinstance(part, str):
            total_chars += len(part)
        elif isinstance(part, dict) and 'text' in part:
            total_chars += len(part['text'])

estimated_tokens = total_chars // 4

if estimated_tokens > 100000:
    print(json.dumps({
        "continue": False,
        "reason": f"Request exceeds token limit (~{estimated_tokens} tokens)"
    }))
    sys.exit(2)

print(json.dumps({"continue": True}))

Project-Specific Rules

Apply rules based on working directory:

# Only allow certain tools in production directories
CWD=$(echo "$INPUT" | jq -r '.cwd')
if [[ "$CWD" == */production/* && "$TOOL_NAME" != "read_file" ]]; then
  echo '{"decision": "deny", "reason": "Only read operations allowed in production"}'
  exit 2
fi