{
  "id": "gemini-cli",
  "title": "Gemini CLI Cookbook",
  "lede": "Recipes for the parts of Gemini CLI that have no equivalent elsewhere: custom commands that can splice shell output into their own prompt, checkpointing you can rewind, and a plan mode that refuses to touch anything.",
  "updated": "2026-07-12",
  "categories": [
    { "id": "commands", "label": "Custom commands" },
    { "id": "safety", "label": "Undo & approvals" },
    { "id": "headless", "label": "Headless & CI" }
  ],
  "recipes": [
    {
      "id": "shell-injecting-command",
      "title": "Build a command that gathers its own context",
      "category": "commands",
      "difficulty": "intermediate",
      "when": "You keep running the same command, pasting its output back in, and *then* asking the question.",
      "sheetRefs": ["extend", "config"],
      "steps": [
        {
          "text": "Custom commands can run shell with `!{...}` and splice the output straight into the prompt. The command collects its own evidence before the model sees it.",
          "code": {
            "lang": "toml",
            "file": ".gemini/commands/pr/review.toml",
            "text": "description = \"Review the current branch against main.\"\nprompt = \"\"\"\nReview this diff like an owner. Prioritize correctness and\nregressions over style. Cite every finding as file:line.\n\nDiff:\n!{git diff main...HEAD}\n\nRecent commits:\n!{git log --oneline main..HEAD}\n\"\"\""
          }
        },
        {
          "text": "Directory nesting becomes namespacing, so this one is invoked as `/pr:review`. Project commands live in `.gemini/commands/`, personal ones in `~/.gemini/commands/`.",
          "code": {
            "lang": "bash",
            "text": "/pr:review"
          }
        }
      ],
      "why": "This inverts the usual loop. Instead of you fetching context and pasting it in, the command *is* the fetch — so it can't be run against stale output, and everyone who types `/pr:review` gets the same evidence.",
      "links": [
        { "label": "Custom commands", "url": "https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/custom-commands.md" }
      ]
    },
    {
      "id": "args-in-commands",
      "title": "Take an argument and use it in both prose and shell",
      "category": "commands",
      "difficulty": "intermediate",
      "when": "A command should be parameterized — search for *this*, fix *that* — rather than hard-coded.",
      "sheetRefs": ["extend"],
      "steps": [
        {
          "text": "`{{args}}` interpolates in the prompt text and inside `!{...}` blocks. Used in both, it's substituted in both.",
          "code": {
            "lang": "toml",
            "file": ".gemini/commands/find.toml",
            "text": "description = \"Explain every use of a pattern.\"\nprompt = \"\"\"\nSummarize the findings for the pattern `{{args}}`.\nExplain what each call site is actually doing.\n\nSearch results:\n!{grep -rn {{args}} . --include=*.ts}\n\"\"\""
          }
        },
        {
          "text": "Invoke it with the argument.",
          "code": {
            "lang": "bash",
            "text": "/find useLegacyAuth"
          }
        }
      ],
      "why": "Anything reaching `!{...}` runs in a shell, so an argument is untrusted input in a command position. Keep injected args inside a single well-understood command like `grep`, and don't build one that would happily interpolate `; rm -rf .` into something more creative.",
      "links": [
        { "label": "Custom commands", "url": "https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/custom-commands.md" }
      ]
    },
    {
      "id": "checkpoint-restore",
      "title": "Let it try the risky refactor — you can rewind",
      "category": "safety",
      "difficulty": "starter",
      "when": "You want the agent to attempt something ambitious without betting the working tree on it.",
      "sheetRefs": ["session", "config"],
      "steps": [
        {
          "text": "Enable checkpointing in `settings.json`. **The `--checkpointing` flag was removed in 0.11.0** — settings is the only way in now.",
          "code": {
            "lang": "json",
            "file": "~/.gemini/settings.json",
            "text": "{\n  \"checkpointing\": {\n    \"enabled\": true\n  }\n}"
          }
        },
        {
          "text": "Before any file-modifying tool runs, the CLI snapshots the project and the conversation. Undo both with `/restore`.",
          "code": {
            "lang": "bash",
            "text": "/restore"
          }
        }
      ],
      "why": "`/restore` rewinds the **conversation** as well as the files — which is what makes it more than `git checkout .`. You don't end up with a clean tree and an agent still convinced it already made the change.",
      "links": [
        { "label": "Checkpointing", "url": "https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/checkpointing.md" }
      ]
    },
    {
      "id": "plan-before-doing",
      "title": "Get the plan before anything gets written",
      "category": "safety",
      "difficulty": "starter",
      "when": "The change is big enough that you want to argue with the approach before it exists in files.",
      "sheetRefs": ["approval", "flags"],
      "steps": [
        {
          "text": "Plan mode reads and analyzes but won't modify. It's a read-only approval mode, not a promise.",
          "code": {
            "lang": "bash",
            "text": "gemini --approval-mode plan -p \"Analyze telemetry and suggest improvements\""
          }
        },
        {
          "text": "Leave plan mode in plain language once you like the plan.",
          "code": {
            "lang": "text",
            "text": "exit plan mode"
          }
        }
      ],
      "why": "The four approval modes are `default`, `auto_edit`, `yolo`, and `plan`. Reversing them into a workflow — plan to decide, `auto_edit` to grind through the mechanical part — is more useful than picking one and living with it.",
      "links": [
        { "label": "Plan mode", "url": "https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/plan-mode.md" }
      ]
    },
    {
      "id": "headless-json",
      "title": "Run it in a script",
      "category": "headless",
      "difficulty": "starter",
      "when": "You want Gemini in a pipeline and need a parseable answer out the other end.",
      "sheetRefs": ["headless", "flags"],
      "steps": [
        {
          "text": "`-p` runs headless; `--output-format json` makes it machine-readable.",
          "code": {
            "lang": "bash",
            "text": "gemini -p \"Does this repo have a CI workflow? Answer yes or no.\" \\\n  --output-format json | jq -r '.response'"
          }
        },
        {
          "text": "Bound what it can reach when the run is unattended.",
          "code": {
            "lang": "bash",
            "text": "gemini -p \"Summarize the open TODOs.\" \\\n  --approval-mode plan \\\n  --allowed-tools \"read_file,glob,search_file_content\""
          }
        }
      ],
      "why": "Pairing `--approval-mode plan` with an allowlist in CI gives you an agent that can analyze and report but structurally cannot commit — the safe default for anything that runs without a human watching.",
      "links": [
        { "label": "Headless mode", "url": "https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/headless.md" }
      ]
    }
  ]
}
