AI-agent-native CLI for Google Gemini Gems — chat, image generation, deep
research, Gem CRUD. No API key. Uses browser cookies via the gemini-webapi
library. 3-10x faster than browser automation on native Linux (~4s per chat).
gemini-cli -p "prompt" → Direct chat (no Gem, fastest)
gemini-cli <gem-id> "prompt" → Chat with a Gem
gemini-cli <gem-id> -c sess.json --new "msg" → Multi-turn conversation
gemini-cli <gem-id> --img "description" → Image generation (Imagen)
gemini-cli <gem-id> -m pro --deep-research "q" → Deep research
gemini-cli --create-gem "Name" -p "instructions" → Create a Gem
gemini-cli --delete-gem <id> → Delete a Gem
gemini-cli --list-gems → List all your Gems
Looking for the browser-based alternative? See hermes-gem-pw — drives a real Chromium via Playwright/CDP. Works on WSL2, supports knowledge upload at Gem creation. Slower (~15-40s) but universally reliable.
gemini-webapi is a Python library that calls Google's internal Gemini API
directly over HTTP. On native Linux, curl_cffi enables fast, reliable
requests (~4s chat). This is 3-10x faster than browser automation.
Platform compatibility:
- Native Linux: Full support. All features work. ~4s chat.
- WSL2:
curl_cffihangs on POST. Use hermes-gem-pw instead. - macOS: Untested (likely works, curl_cffi has native macOS support).
| Feature | Command | Speed |
|---|---|---|
| Direct chat (no Gem) | gemini-cli -p "prompt" |
~4s |
| Gem chat | gemini-cli <id> "prompt" |
~4s |
| Multi-turn conversation | gemini-cli <id> -c sess.json --new "msg" |
~4-30s |
| Image generation | gemini-cli <id> --img "description" |
~10s |
| Deep research | gemini-cli <id> -m pro --deep-research "q" |
~5-10min |
| File upload (PDF, TXT) | gemini-cli <id> -f file.pdf "summarize" |
~5s |
| Image upload | gemini-cli <id> -i photo.png "describe" |
~5s |
| Gem CRUD | --create-gem / --delete-gem / --list-gems |
<1s |
| Model selection | -m flash / pro / lite |
— |
| Extended thinking | --thinking extended |
adds ~30-60s |
| Temporary chat | --temporary |
ephemeral, not in history |
| Thinking traces | --show-thoughts |
adds thoughts to output |
| Video generation | --save-videos DIR |
Veo 3, 3/day Pro |
| Canvas artifact | --extract-canvas FILE |
saves html block |
| Fetch latest | --fetch-latest c_xxx |
fast latest turn |
| Deep research status | --deep-research-status c_xxx |
is the report in the chat yet? |
| Auth diagnostics | --doctor |
which cookie jar is live + capability probe |
| Streaming output | --stream |
real-time tokens |
A cookie pair can be chat-valid while being read-invalid: StreamGenerate answers
(so chat "works") while every SNlM0e-gated call — Gem CRUD, --list-gems,
--list-chats, --account-status — fails. Two root causes, both fixed:
- Stale cache shadowed the live browser session.
resolve_auth()returned~/.gemini-cli/auth.jsonwithout validating it, so Gem CRUD died withAUTH_EXPIRED: no SNlM0e in /app HTMLand--list-gemsreturnedPermission denied … UNAUTHENTICATEDeven though the browser session was fine. Candidates are now probed once per run with a cheap/apprequest and the first live one wins (falling back to the old behaviour only if none is live). - Mixed cookie jars. The browser-shaped batchexecute client took the full jar
from Firefox and then overwrote
__Secure-1PSIDTSwith the cache's value — a jar assembled from two sessions that the server treats as signed out. The jar is now taken whole from one source that owns the selected1PSID.
Also fixed: --list-gems/--gem-info fall back to the browser-shaped LIST_BOTS
RPC; --deep-research-status takes a CHAT id (there is no research-task entity to
look up) and reports whether the report has landed; the pointer's model field says
default instead of None when no -m was given; an unresolvable Gem id fails once
with a clear message instead of retrying into a longer hang.
- A chat-valid cookie pair is not a valid session for reads. StreamGenerate
accepts the pair even when the session is degraded, so chat keeps working while
Gem CRUD /
--list-gems/--list-chatsfail.--doctortells you which source is live;resolve_auth()now prefers it automatically. - Never assemble a cookie jar from two sessions (1PSID from one source plus 1PSIDTS from another is treated as signed out).
gemini_webapi's deep-research entry points have noon_statushook — passing it raisesTypeError, which the fallback chain swallowed, so every DR run silently degraded to a plaingenerate_contentcall.- An unresolvable Gem id hangs rather than 404s; unresolved ids now get one capped 60s attempt with a message naming both causes (typo vs. inaccessible shared Gem).
- Tests are offline:
python3 -m pytest tests/ -q(network + cookie stores stubbed).
git clone https://github.com/lesterppo/hermes-gem-cli.git
cd hermes-gem-cli
./install.shOr one-liner:
curl -fsSL https://raw.githubusercontent.com/lesterppo/hermes-gem-cli/main/install.sh | bash# One-time: cache auth tokens from browser
gemini-cli --init
# Direct chat (fastest, no Gem needed)
gemini-cli -p "What is the capital of France?"
# Chat with a Gem
gemini-cli abc123def456 "Explain quantum computing in one sentence"
# Multi-turn (Gem remembers context)
gemini-cli abc123def456 -c /tmp/session.json --new "My name is Alex"
gemini-cli abc123def456 -c /tmp/session.json "What is my name?"
# Image generation
gemini-cli abc123def456 --img "a sunset over mountains"
# Deep research (Pro model recommended)
gemini-cli abc123def456 -m pro --deep-research "Latest CRISPR advancements"
# Create + delete a Gem
gemini-cli --create-gem "MyBot" -p "You are a helpful assistant"
gemini-cli --delete-gem abc123def456- Token-efficient: stdout is always compact JSON (~60-80 chars), full response on disk
- AI-agent-native: structured JSON output, clear error codes, subprocess-safe
- URL-first: paste any shared Gem URL, ID extracted automatically
- 5-tier auth: env vars → cached file → browser scan → retry → login fallback
Success:
{"ok":true,"f":"./out.md","s":1234,"b":2,"imgs":1,"model":"flash","gem":"GemName","dr":true,"c":"c_xxx","t":3}Error:
{"ok":false,"err":"RATE_LIMIT","msg":"...","retry_after_s":30,"retry":true}- hermes-gem-pw — CDP/browser Gemini CLI. Drives real Chromium via Playwright. Works on WSL2 (where webapi hangs). Supports knowledge upload at Gem creation (PDF, GitHub repo, folder). Slower (~15-40s) but universally reliable.
- Python 3.10+
- Firefox or Chrome signed into gemini.google.com
pip install gemini-webapi browser-cookie3 loguru- Native Linux (or macOS — untested). For WSL2, use gem-pw.
Runs entirely on your machine. Auth tokens cached locally at
~/.gemini-cli/auth.json. No data sent anywhere except Google's Gemini API.
MIT