How Gemini CLI Commands Are Structured Using TOML: A Complete Guide

Gemini CLI commands are structured as individual TOML files placed in the .gemini/commands/ directory, with each file containing a description field and a multiline prompt field that supports runtime placeholders like {{args}}.

The EveryInc/compound-engineering-plugin repository provides a complete implementation for converting Claude Code commands into Gemini CLI's TOML-based format. Understanding this structure is essential for developers migrating existing AI-assisted workflows or building custom command suites for Google's Gemini CLI.

Understanding the TOML File Structure for Gemini CLI Commands

Required Fields

Each Gemini CLI command TOML file must contain two specific fields according to the specification in docs/specs/gemini.md (lines 30-38):

  • description: A concise, one-line string summary displayed when users run /help
  • prompt: A multiline string (using TOML's triple-quote syntax) containing the actual instructions sent to the model

The prompt field supports template variables that expand at runtime. The most common placeholder is {{args}}, which injects user-provided arguments into the prompt context.

File Path Conventions and Namespaces

The physical location of each TOML file determines its invocation syntax. Files reside under .gemini/commands/ and use directory nesting to create command namespaces:


.gemini/commands/git/commit.toml    →  /git:commit
.gemini/commands/workflows/plan.toml → /workflows:plan

As implemented in src/converters/claude-to-gemini.ts (lines 36-38), the resolveCommandPath function converts colon-separated command names into directory paths by splitting on the delimiter and joining with forward slashes.

How Commands Are Converted from Claude Code to Gemini TOML

The conversion pipeline in src/converters/claude-to-gemini.ts handles the transformation of Claude Code command definitions into Gemini-compatible TOML files through three key functions:

Name Resolution and Path Mapping

The resolveCommandPath function normalizes command names to filesystem-safe paths. It processes colon-separated identifiers (e.g., git:commit) into array segments (["git", "commit"]), then joins them for the final file location.

TOML Serialization

The toToml function (lines 44-53) constructs the final TOML content:

function toToml(description: string, prompt: string): string {
  // Escapes backslashes and quotes for TOML safety
  const escapedPrompt = prompt
    .replace(/\\/g, '\\\\')
    .replace(/"/g, '\\"')
  
  return `description = "${description}"
prompt = """
${escapedPrompt}
"""
`
}

This implementation uses TOML's triple-quoted literal strings for the prompt field to preserve formatting and newlines while escaping internal quotes and backslashes to prevent parsing errors.

File System Writing

The writeGeminiBundle function in src/targets/gemini.ts (lines 21-24) persists each command to disk:

if (bundle.commands.length > 0) {
  for (const command of bundle.commands) {
    await writeText(
      path.join(paths.commandsDir, `${command.name}.toml`),
      command.content + "\n"
    )
  }
}

This ensures each command receives a .toml extension and resides in the correct subdirectory based on its namespace.

Practical Examples of Gemini CLI TOML Commands

Example TOML Command File

Here is a complete example of a generated command file located at .gemini/commands/git/commit.toml:

description = "Create a git commit with a good message"
prompt = """
Look at the current git diff and create a commit with a descriptive message.

User request: {{args}}
"""

The description appears when users list available commands, while the prompt contains the full instructions sent to the Gemini model, including the {{args}} placeholder that injects user input.

Conversion Implementation Example

The following TypeScript demonstrates how the plugin converts a Claude Code command definition into the Gemini TOML structure:

// src/converters/claude-to-gemini.ts
function convertCommand(command: ClaudeCommand, usedNames: Set<string>): GeminiCommand {
  const commandPath = resolveCommandPath(command.name)   // ["git","commit"]
  const pathKey = commandPath.join("/")                // "git/commit"
  uniqueName(pathKey, usedNames)                       // track for deduplication

  const description = command.description ?? `Converted from Claude command ${command.name}`
  const prompt = command.argumentHint
    ? `${transformContentForGemini(command.body.trim())}\n\nUser request: {{args}}`
    : transformContentForGemini(command.body.trim())

  const content = toToml(description, prompt)          // serialises to TOML
  return { name: pathKey, content }
}

This function handles name resolution, description fallback logic, prompt transformation with optional argument injection, and final TOML serialization.

Summary

  • Gemini CLI commands use TOML files stored in .gemini/commands/ with a description string and multiline prompt field.
  • File paths define command namespaces: subdirectories create colon-separated command names (e.g., git/commit.toml becomes /git:commit).
  • The conversion pipeline in src/converters/claude-to-gemini.ts uses resolveCommandPath for name mapping and toToml for serialization.
  • Runtime placeholders like {{args}} in the prompt field allow dynamic user input injection when commands execute.

Frequently Asked Questions

What fields are required in a Gemini CLI TOML command file?

Every TOML command file must contain exactly two top-level fields: description, which is a single-line string displayed in help menus, and prompt, which is a multiline string containing the instructions sent to the Gemini model. According to the specification in docs/specs/gemini.md, these fields are mandatory for the CLI to recognize and execute the command.

How does the file path determine the command name?

The Gemini CLI derives the invocation syntax from the relative path under .gemini/commands/. Directory separators become namespace colons, and the filename (minus extension) becomes the final command segment. As implemented in src/converters/claude-to-gemini.ts, the resolveCommandPath function splits colon-separated names and joins them with forward slashes, meaning git:commit maps to .gemini/commands/git/commit.toml.

Can I use variables or placeholders in the prompt field?

Yes, the prompt field supports template variables that expand at runtime. The most common placeholder is {{args}}, which injects any arguments the user provides after the command invocation. During conversion in src/converters/claude-to-gemini.ts, the pipeline appends \n\nUser request: {{args}} to prompts when the original Claude command accepts arguments, ensuring user input reaches the model context.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →