Hooks are scripts or programs that Gemini CLI executes at specific points in the
agentic loop, allowing you to intercept and customize behavior without modifying
the CLI’s source code.
Note: Hooks are currently an experimental feature.
To use hooks, you must explicitly enable them in your settings.json:
{
"tools": { "enableHooks": true },
"hooks": { "enabled": true }
}
Both of these are needed in this experimental phase.
See writing hooks guide for a tutorial on creating your
first hook and a comprehensive example.
See hooks reference for the technical specification of the I/O
schemas.
See best practices for guidelines on security, performance,
and debugging.
Warning: Hooks execute arbitrary code with your user privileges.
By configuring hooks, you are explicitly allowing Gemini CLI 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 .gemini/settings.json) and Extension hooks are
particularly risky when opening third-party projects or extensions from
untrusted authors. Gemini CLI will warn you the first time it detects a new
project hook (identified by its name and command), but it is your
responsibility to review these hooks (and any installed extensions) before
trusting them.
Note: Extension hooks are subject to a mandatory security warning and
consent flow during extension installation or update if hooks are detected.
You must explicitly approve the installation or update of any extension that
contains hooks.
Hook definitions are configured in settings.json files using the hooks
object. Configuration can be specified at multiple levels with defined
precedence rules.
Hook configurations are applied in the following order of execution (lower
numbers run first):
Project settings:.gemini/settings.json in your project directory
(highest priority)
User settings:~/.gemini/settings.json
System settings:/etc/gemini-cli/settings.json
Extensions: Internal hooks defined by installed extensions (lowest
priority). See Extensions documentation for
details on how extensions define and configure hooks.
If multiple hooks with the identical name and command are discovered
across different configuration layers, Gemini CLI deduplicates them. The hook
from the higher-priority layer (e.g., Project) will be kept, and others will be
ignored.
Within each level, hooks run in the order they are declared in the
configuration.
name (string, recommended): Unique identifier for the hook used in
/hooks enable/disable commands. If omitted, the command path is used as
the identifier.
type (string, required): Hook type - currently only "command" is
supported
command (string, required): Path to the script or command to execute
description (string, optional): Human-readable description shown in
/hooks panel
timeout (number, optional): Timeout in milliseconds (default: 60000)
matcher (string, optional): Pattern to filter when hook runs (event
matchers only)
You can enable or disable all hooks at once using commands:
Terminal window
/hooksenable-all
/hooksdisable-all
These commands provide a shortcut to enable or disable all configured hooks
without managing them individually. The enable-all command removes all hooks
from the hooks.disabled array, while disable-all adds all configured hooks
to the disabled list. Changes take effect immediately without requiring a
restart.
You can enable or disable individual hooks using commands:
Terminal window
/hooksenablehook-name
/hooksdisablehook-name
These commands allow you to control hook execution without editing configuration
files. The hook name should match the name field in your hook configuration.
Changes made via these commands are persisted to your settings. The settings are
saved to workspace scope if available, otherwise to your global user settings
(~/.gemini/settings.json).
To permanently disable hooks, add them to the hooks.disabled array in your
settings.json:
{
"hooks": {
"disabled": ["secret-scanner", "auto-test"]
}
}
Note: The hooks.disabled array uses a UNION merge strategy. Disabled hooks
from all configuration levels (user, project, system) are combined and
deduplicated, meaning a hook disabled at any level remains disabled.