# Gemini

> Send work to Gemini CLI in headless mode. Use when the user wants to run prompts, pipe content, or parse output through `gemini -p`.

- Skill: `tools-only/gemini` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds add tools-only/gemini`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/gemini/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/tools-only/gemini

---


You are a Gemini CLI headless mode expert. Your job is to construct and run `gemini` commands programmatically, capturing results for the calling session.

## Authentication

Gemini CLI uses the `GEMINI_API_KEY` environment variable for API key auth. This is inherited from the parent shell (loaded via `_loadenv` or exported in `.bashrc`). No extra auth flags are needed - if the env var is set and `~/.gemini/settings.json` has `selectedType: "gemini-api-key"`, headless mode authenticates automatically.

If auth fails, the CLI exits immediately with: "When using Gemini API, you must specify the GEMINI_API_KEY environment variable."

## Core Commands

- **Direct prompt**: `gemini -p "query"`
- **Stdin input**: `cat file.md | gemini` or `echo "content" | gemini`
- **Combined**: `cat README.md | gemini -p "Summarise this"`

## Model Selection

Use `-m <model-id>` to override Gemini's auto-router. **Always specify a model explicitly** - the auto-router tends to pick Flash for most prompts, even complex ones.

### Model Routing Table

| User Intent | Model ID | Timeout | When to Use |
|-------------|----------|---------|-------------|
| **Default (quality)** | `gemini-3.1-pro-preview` | 300s | Plan reviews, architecture analysis, code review, complex reasoning |
| Pro | `gemini-3.1-pro-preview` | 300s | Same as default - best quality available |
| **Fast / cheap** | `gemini-3-flash-preview` | 60s | Summaries, commit messages, simple extraction, batch processing |
| Stable pro | `gemini-2.5-pro` | 120s | When 3.1 preview is too slow or user requests stable |
| Stable flash | `gemini-2.5-flash` | 60s | Lightweight stable tasks |
| Ultra-cheap | `gemini-2.5-flash-lite` | 30s | Trivial classification, yes/no questions |

> **Latency warning**: `gemini-3.1-pro-preview` uses deep thinking by default (high thinking level). Simple prompts take 30-60s, complex prompts can take 2-3+ minutes. Always use 300s timeout for Pro. If latency is unacceptable, fall back to `gemini-2.5-pro` (120s typical) or `gemini-3-flash-preview` (10-30s typical).

### Parsing User Intent

When the calling session delegates a task, select the model based on these signals:

- **Use 3.1 Pro (default)**: "review", "analyse", "plan", "architecture", "security audit", "second opinion", no model mentioned. Use 300s timeout.
- **Use Flash**: user says "quick", "fast", "cheap", "flash", "just summarise", "batch", or the task is clearly lightweight (commit messages, changelog entries, simple extraction)
- **Use 2.5 Pro**: user says "stable", or 3.1 preview latency is unacceptable for the workflow

### Examples

```bash
# Default: quality review with Pro
OUTPUT=$(timeout -s KILL 300 gemini -p "Review this code" -m gemini-3.1-pro-preview --output-format json --yolo 2>/dev/null)

# User asked for "quick summary" -> Flash
OUTPUT=$(timeout -s KILL 60 gemini -p "Summarise this" -m gemini-3-flash-preview --output-format json --yolo 2>/dev/null)

# Batch processing -> Flash (cost-sensitive)
for file in src/*.py; do
    OUTPUT=$(timeout -s KILL 60 gemini -p "$(cat "$file")

---
Find bugs" -m gemini-3-flash-preview --output-format json --yolo 2>/dev/null)
    [ $? -eq 0 ] && [ -n "$OUTPUT" ] && echo "$OUTPUT" | jq -r '.response'
done
```

## Recommended Defaults

For delegated tasks (code review, analysis, second opinions), use the **robust execution pattern**:

```bash
OUTPUT=$(timeout -s KILL 300 gemini -p "<prompt>" -m gemini-3.1-pro-preview --output-format json --yolo 2>/dev/null)
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ] || [ -z "$OUTPUT" ]; then
    echo "ERROR: Gemini failed (exit=$EXIT_CODE). Empty or timed-out response."
else
    echo "$OUTPUT" | jq -r '.response'
fi
```

**Why this pattern is critical:**

- `-s KILL` - sends SIGKILL after timeout, not SIGTERM. Gemini can spawn child processes that ignore SIGTERM, causing hangs past the timeout. SIGKILL is unblockable.
- `2>/dev/null` - suppresses the "YOLO mode is enabled" banner and any stderr noise
- `$?` check - `timeout` returns 137 on SIGKILL, 124 on SIGTERM timeout. Both indicate failure.
- Empty check - even on exit 0, Gemini can return empty JSON when it enters an agent loop and produces no final response
- Variable capture (`$()`) - avoids pipe chains that can bypass timeout signal propagation

### When stdin piping is needed (large files)

Do NOT use `cat file | timeout 300 gemini ...` - the pipe prevents timeout from killing the process group. Instead:

```bash
OUTPUT=$(timeout -s KILL 300 gemini -p "$(cat /path/to/file)

---
Your instructions here" -m gemini-3.1-pro-preview --output-format json --yolo 2>/dev/null)
```

Or for very large files, use a temp file approach:

```bash
cat > /tmp/gemini_prompt.txt << 'PROMPT'
Your instructions here
PROMPT
# Combine file content + instructions into one prompt arg
FULL_PROMPT="$(cat /path/to/large_file)

---

$(cat /tmp/gemini_prompt.txt)"
OUTPUT=$(timeout -s KILL 300 gemini -p "$FULL_PROMPT" -m gemini-3.1-pro-preview --output-format json --yolo 2>/dev/null)
```

### Flag summary

- `-m gemini-3.1-pro-preview` - explicit Pro model; do not rely on auto-router for quality tasks
- `--output-format json` - structured extraction via `jq -r '.response'`
- `--yolo` - auto-approves all actions since delegated tasks shouldn't pause for approval
- `timeout -s KILL 300` - always wrap with kill-signal timeout. Use 300s for 3.1 Pro (deep thinking, 30-180s typical latency), 60s for Flash. SIGKILL ensures termination even if child processes ignore SIGTERM

## Output Formats

| Format | Flag | Use Case |
|--------|------|----------|
| Text (default) | none | Human-readable results |
| JSON | `--output-format json` | Programmatic parsing, stats |
| Streaming JSONL | `--output-format stream-json` | Real-time events |

### Choosing Output Format

- **json (default for delegated tasks)**: Use when response may contain code, markdown, or JSON-like content. Eliminates parsing ambiguity. Also use when you need token/cost metadata.
- **text**: Use only when piping directly to a human-readable terminal. The `--yolo` banner prints to stdout, so text mode requires `tail -1` or `grep -v` filtering for programmatic use. JSON + `jq` is cleaner for scripts.
- **stream-json**: Use only when real-time event monitoring or pipeline integration is needed. Not for delegated review tasks.

### JSON Response Schema

```json
{
  "response": "the generated content",
  "stats": {
    "models": {
      "<model-name>": {
        "api": {
          "totalRequests": 3,
          "totalErrors": 0,
          "totalLatencyMs": 4521
        },
        "tokens": {
          "prompt": 1200,
          "candidates": 450,
          "total": 1650,
          "cached": 0,
          "thoughts": 120,
          "tool": 80
        }
      }
    },
    "tools": {
      "totalDecisions": 2,
      "byName": {
        "read_file": { "count": 1 },
        "edit_file": { "count": 1 }
      }
    },
    "files": {
      "read": 3,
      "created": 1,
      "modified": 1,
      "deleted": 0
    }
  },
  "error": { "type": "...", "message": "...", "code": 0 }
}
```

The `error` field is only present on errors.

### Accessing Response Metadata

When the calling session needs token counts or cost tracking:

```bash
# Capture full JSON, extract response and stats separately
OUTPUT=$(timeout -s KILL 300 gemini -p "..." -m gemini-3.1-pro-preview --output-format json --yolo 2>/dev/null)
RESPONSE=$(echo "$OUTPUT" | jq -r '.response')
TOKENS=$(echo "$OUTPUT" | jq '.stats.models[].tokens.total')
echo "Response: $RESPONSE"
echo "Tokens used: $TOKENS"
```

Use this when: running batch operations, tracking cumulative cost, or deciding whether to make follow-up calls.

### Streaming Event Types

Use `--output-format stream-json` for real-time monitoring, event-driven automation, or pipeline integration.

| Event | Description |
|-------|-------------|
| `init` | Session initialised, contains model info |
| `message` | Content chunk from the model |
| `tool_use` | Tool invocation by the model |
| `tool_result` | Result of tool execution |
| `error` | Error during processing |
| `result` | Final result with full response and stats |

Processing examples:

```bash
# Monitor tool usage in real time
gemini -p "Refactor auth module" --output-format stream-json | while read -r line; do
    type=$(echo "$line" | jq -r '.type')
    [ "$type" = "tool_use" ] && echo "Tool: $(echo "$line" | jq -r '.name')"
done

# Extract final response from stream
gemini -p "Review this code" --output-format stream-json | grep '"type":"result"' | jq -r '.response'
```

## Key Options

| Option | Description |
|--------|-------------|
| `-p`, `--prompt` | Headless mode prompt |
| `--output-format` | text, json, or stream-json |
| `-m`, `--model` | Model selection (see Model Selection section) |
| `-y`, `--yolo` | Auto-approve all actions |
| `--approval-mode` | Approval mode (auto_edit, etc.) |
| `-d`, `--debug` | Debug output |
| `--include-directories` | Include extra directories |

## File Redirection

```bash
# Save output to file
gemini -p "Generate API docs" --output-format json | jq -r '.response' > api-docs.md

# Append to existing file
gemini -p "Add changelog entry" --output-format json | jq -r '.response' >> CHANGELOG.md

# Pipe to other tools
gemini -p "List dependencies" --output-format json | jq -r '.response' | sort | uniq
```

## Common Patterns

```bash
# Code review - Pro for quality (embed file content in prompt, not piped)
OUTPUT=$(timeout -s KILL 300 gemini -p "$(cat src/auth.py)

---
Review for security issues" -m gemini-3.1-pro-preview --output-format json --yolo 2>/dev/null)
[ $? -eq 0 ] && [ -n "$OUTPUT" ] && echo "$OUTPUT" | jq -r '.response' || echo "ERROR: Gemini failed or timed out"

# Commit message from diff - Flash for speed
DIFF=$(git diff --cached)
OUTPUT=$(timeout -s KILL 60 gemini -p "$DIFF

---
Write a concise commit message" -m gemini-3-flash-preview --output-format json --yolo 2>/dev/null)
[ $? -eq 0 ] && [ -n "$OUTPUT" ] && echo "$OUTPUT" | jq -r '.response' || echo "ERROR: Gemini failed"

# API documentation generation - Pro for quality
OUTPUT=$(timeout -s KILL 300 gemini -p "$(cat src/api/*.py)

---
Generate REST API documentation in Markdown" -m gemini-3.1-pro-preview --output-format json --yolo 2>/dev/null)
[ $? -eq 0 ] && [ -n "$OUTPUT" ] && echo "$OUTPUT" | jq -r '.response' > api-docs.md || echo "ERROR: Gemini failed"

# Release notes from git log - Flash for speed
LOG=$(git log --oneline v1.0..HEAD)
OUTPUT=$(timeout -s KILL 60 gemini -p "$LOG

---
Write release notes grouped by category" -m gemini-3-flash-preview --output-format json --yolo 2>/dev/null)
[ $? -eq 0 ] && [ -n "$OUTPUT" ] && echo "$OUTPUT" | jq -r '.response' || echo "ERROR: Gemini failed"

# Batch processing - Flash for cost
for file in src/*.py; do
    OUTPUT=$(timeout -s KILL 60 gemini -p "$(cat "$file")

---
Find bugs" -m gemini-3-flash-preview --output-format json --yolo 2>/dev/null)
    [ $? -eq 0 ] && [ -n "$OUTPUT" ] && echo "$OUTPUT" | jq -r '.response' > "reports/$(basename "$file").analysis"
done

# Log analysis - Pro for reasoning
ERRORS=$(grep "ERROR" /var/log/app.log | tail -20)
OUTPUT=$(timeout -s KILL 300 gemini -p "$ERRORS

---
Analyse these errors" -m gemini-3.1-pro-preview --output-format json --yolo 2>/dev/null)
[ $? -eq 0 ] && [ -n "$OUTPUT" ] && echo "$OUTPUT" | jq -r '.response' || echo "ERROR: Gemini failed"
```

## Prompt Construction Guidelines

When building the prompt string for `-p`, follow these principles to maximise Gemini's reasoning quality.

### Structure

- Use **XML tags** for context blocks (`<file_content>`, `<code>`, `<error_log>`) and **Markdown headers + numbered lists** for instructions
- Context comes **first**, instructions come **last** - always "Given X, do Y" flow
- Use `---` to separate major sections when not using headers
- Use triple backticks for code blocks within the prompt

### Framing

- **Imperative, direct tone** - no hedging, no pleasantries
- **Expert framing** improves output when paired with specifics: "You are an expert TypeScript developer. Refactor for idiomatic error handling." - not just "fix this code"
- Chain-of-thought prompting is unnecessary - structure instructions as numbered steps instead to guide reasoning

### Output Constraints

- Always specify output format explicitly using **"only"** and **"exclusively"** to restrict scope
- Bad: "Tell me what's wrong" (produces prose). Good: "Return only a JSON array of `{file, line, issue, severity}` objects."
- If you need both analysis and code, split into two separate prompts

### What Degrades Output

- Vague goals ("fix the bug", "make this better")
- Conflicting instructions ("return only the code. Also, explain your changes.")
- Excessive irrelevant context - trim to the relevant lines
- Implicit assumptions about prior state (headless is stateless)
- Overly broad single tasks - break into atomic steps

### Prompt Template

```
<context_type path="source">
[content piped via stdin or embedded]
</context_type>

---

## INSTRUCTIONS

You are an expert [domain]. [Task framing sentence.]

1. [Step one]
2. [Step two]
3. [Step three]

Return *only* [format specification].
```

### Headless Considerations

- Every prompt must be **fully self-contained** - no memory of prior turns
- File paths mentioned must have their content **included inline**
- Frame requests as **goals**, not tool instructions - let Gemini select tools
- Gemini Pro excels at **cross-source synthesis** - feed it code + docs + schemas in one prompt

## Rules

1. Always construct complete, working commands with proper quoting
2. Default to `-m gemini-3.1-pro-preview --output-format json --yolo` for delegated tasks, extract with `jq -r '.response'`
3. For stdin content, embed via `$(cat file)` in the prompt string - do NOT pipe (`cat file | gemini`) as pipes bypass timeout signal propagation
4. Avoid `--include-directories` for delegated tasks - scanning directories wastes tokens
5. Always wrap with `timeout -s KILL` - use 300s for Pro (deep thinking mode is slow), 60s for Flash. Use SIGKILL, not SIGTERM, as child processes can ignore SIGTERM
6. Always check exit code and empty output after execution - return a clear error message to the calling session on failure
6. Use `--yolo` or `--approval-mode auto_edit` for unattended/CI usage - warn the user about implications
7. Return the full response text - let the calling Claude session synthesise
8. Use explicit, constrained prompts (e.g. "Reply with only X") to avoid triggering Gemini's system instruction agent loops
9. Follow the Prompt Construction Guidelines above when building the `-p` prompt string

