File Locations and Precedence
Gemini CLI discovers commands from two locations, loaded in a specific order:User commands (global)
Located in
~/.gemini/commands/. These commands are available in any project you are working on.If a command in the project directory has the same name as a command in the user directory, the project command will always be used. This allows projects to override global commands with project-specific versions.
Naming and Namespacing
The name of a command is determined by its file path relative to itscommands directory. Subdirectories are used to create namespaced commands, with the path separator (/ or \) being converted to a colon (:).
- A file at
~/.gemini/commands/test.tomlbecomes the command/test - A file at
<project>/.gemini/commands/git/commit.tomlbecomes the namespaced command/git:commit
TOML File Format
Your command definition files must be written in the TOML format and use the.toml file extension.
Required Fields
The prompt that will be sent to the Gemini model when the command is executed. This can be a single-line or multi-line string.
Optional Fields
A brief, one-line description of what the command does. This text will be displayed next to your command in the
/help menu. If you omit this field, a generic description will be generated from the filename.Handling Arguments
Custom commands support two powerful methods for handling arguments. The CLI automatically chooses the correct method based on the content of your command’sprompt.
Context-Aware Injection with {{args}}
If your prompt contains the special placeholder {{args}}, the CLI will replace that placeholder with the text the user typed after the command name.
The behavior of this injection depends on where it is used:
Raw Injection (Outside Shell Commands)
When used in the main body of the prompt, the arguments are injected exactly as the user typed them. Example (git/fix.toml):
Using Arguments in Shell Commands
When you use{{args}} inside a shell injection block (!{...}), the arguments are automatically shell-escaped before replacement. This allows you to safely pass arguments to shell commands, ensuring the resulting command is syntactically correct and secure while preventing command injection vulnerabilities.
Example (/grep-code.toml):
/grep-code It's complicated:
- The CLI sees
{{args}}used both outside and inside!{...} - Outside: The first
{{args}}is replaced raw withIt's complicated - Inside: The second
{{args}}is replaced with the escaped version (e.g., on Linux:"It\'s complicated") - The command executed is
grep -r "It's complicated" . - The CLI prompts you to confirm this exact, secure command before execution
- The final prompt is sent
Default Argument Handling
If yourprompt does not contain the special placeholder {{args}}, the CLI uses a default behavior for handling arguments.
If you provide arguments to the command (e.g., /mycommand arg1), the CLI will append the full command you typed to the end of the prompt, separated by two newlines. This allows the model to see both the original instructions and the specific arguments you just provided.
If you do not provide any arguments (e.g., /mycommand), the prompt is sent to the model exactly as it is, with nothing appended.
Example (changelog.toml):
This example shows how to create a robust command by defining a role for the model, explaining where to find the user’s input, and specifying the expected format and behavior.
/changelog 1.2.0 added "New feature", the final text sent to the model will be the original prompt followed by two newlines and the command you typed.
Executing Shell Commands with !{...}
You can make your commands dynamic by executing shell commands directly within your prompt and injecting their output. This is ideal for gathering context from your local environment, like reading file content or checking the status of Git.
How it works:
- Inject commands: Use the
!{...}syntax - Argument substitution: If
{{args}}is present inside the block, it is automatically shell-escaped - Robust parsing: The parser correctly handles complex shell commands that include nested braces, such as JSON payloads. The content inside
!{...}must have balanced braces ({and}) - Security check and confirmation: The CLI performs a security check on the final, resolved command (after arguments are escaped and substituted). A dialog will appear showing the exact command(s) to be executed
- Execution and error reporting: The command is executed. If the command fails, the output injected into the prompt will include the error messages (stderr) followed by a status line, e.g.,
[Shell command exited with code 1]
git/commit.toml):
This command gets the staged git diff and uses it to ask the model to write a commit message.
/review FileCommandLoader.ts, the @{docs/best-practices.md} placeholder is replaced by the content of that file, and {{args}} is replaced by the text you provided, before the final prompt is sent to the model.
Example: Pure Function Refactoring Command
Let’s create a global command that asks the model to refactor a piece of code.Create the File and Directories
First, ensure the user commands directory exists, then create arefactor subdirectory for organization and the final TOML file.
Add the Content
Open~/.gemini/commands/refactor/pure.toml in your editor and add the following content. We are including the optional description for best practice.