Install the Gemini CLI plugin
The cardinal-gemini-plugin puts your Google Gemini CLI sessions on the Agent Outcomes dashboard, matching the Claude Code, Codex CLI, and Cursor plugins:
- Telemetry — Gemini CLI ships a native OpenTelemetry exporter; the plugin points it directly at Cardinal ingest so
gemini_cli.token.usage,gemini_cli.tool.call.*,gemini_cli.api.request.*, and session/config/agent/compression events arrive without any hook code. Plugin-owned hooks emit the same Cardinal event contract as the sibling plugins on top, so sessions are attributed to engineer, branch, PR, and initiative with per-turn token and tool usage. - MCP tools — a managed
cardinalMCP server entry, installed as a Gemini CLI extension bundle under~/.gemini/extensions/cardinal/, exposing whichever tools your org has integrations for. Pass--telemetry-onlyto skip this side.
Sessions in a git repo receive the initiative branch-naming convention at start, and org spend-limit policies are enforced in-session — quiet context at notify, a visible message at warn, a stopped turn at block. Verdicts are cached locally, so the spend gate never adds a network hop to your prompt path.
Reach out to support@cardinalhq.io for support or to ask questions not answered in our documentation.
Requirements
- Gemini CLI 0.26.0 or later, where hooks are on by default. Earlier versions need hooks explicitly enabled in Gemini CLI’s settings.
- Python 3.11+ on your
PATH. - A Cardinal account on
app.cardinalhq.io, or a self-hosted Cardinal UI your operator has prepared (see Connect AI clients).
1. Install
Gemini CLI doesn’t have a plugin marketplace, so the install is a git clone + run:
git clone https://github.com/cardinalhq/cardinal-gemini-plugin.git ~/workspace/cardinal-gemini-plugin2. Connect
python3 ~/workspace/cardinal-gemini-plugin/plugins/cardinal-gemini-plugin/scripts/cardinal-connectThe connect script prints a URL like https://app.cardinalhq.io/connect?code=ABCD-EFGH. Open it in your browser, sign in if you aren’t already, pick the org to connect, and click Approve. The plugin picks up your consent within a few seconds and writes:
| File | What gets written |
|---|---|
~/.gemini/extensions/cardinal/gemini-extension.json | Extension manifest with the mcpServers.cardinal entry, tagged cardinalManaged: true. |
~/.gemini/extensions/cardinal/hooks/hooks.json | Managed Cardinal hook entries for SessionStart, BeforeAgent, AfterModel, AfterTool, PreCompress, and SessionEnd. |
~/.gemini/extensions/cardinal/GEMINI.md | Context file loaded into the model context by Gemini CLI. |
~/.gemini/settings.json | Managed telemetry block pointing Gemini CLI’s native OTLP exporter at Cardinal ingest. |
~/.gemini/cardinal.json | Non-secret connection state for status/disconnect. |
~/.gemini/cardinal-secrets.json | Your minted keys, written mode 0600. |
For self-hosted Cardinal UI, add --host:
python3 scripts/cardinal-connect --host https://ui.example.internalWith --host set, nothing touches Cardinal Cloud: your Cardinal UI runs the consent flow, mints the keys, and hands the plugin your endpoints. The telemetry side requires your operator to have set MAESTRO_INGEST_ENDPOINT on the Cardinal UI deployment (see Environment variables); without it the connect bundle omits telemetry and sessions never reach the dashboard.
Then restart Gemini CLI so it reloads MCP, hooks, and extensions.
3. Verify
python3 scripts/cardinal-statusYou’ll see the connected org, both endpoints, key prefixes, and a reachability probe against each side. Run a session in any git repo and it appears on the Outcomes dashboard within a few minutes.
Variants
python3 scripts/cardinal-connect --telemetry-only # Outcomes dashboard only; skip the MCP tools
python3 scripts/cardinal-connect --rotate # Mint fresh keys, overwrite an existing connection
python3 scripts/cardinal-connect --no-extension # Skip the extension bundle (MCP + GEMINI.md), keep telemetry
python3 scripts/cardinal-connect --host https://… # Point at a self-hosted Cardinal UI
python3 scripts/cardinal-connect --dry-run # Walk the consent flow, write nothingGetting attributed correctly
Sessions are attributed to an initiative by branch name: <type>/<kebab-name>, where the type prefix is one of feat, fix, refactor, infra, chore, research, or spike (for example feat/outcomes-observability → initiative outcomes-observability, type feature). Sessions on main/master/develop/trunk are treated as research/scoping work. The plugin surfaces this convention to Gemini at the start of every session so branches it cuts for you classify cleanly.
Upgrading from before 0.17.0
Plugin versions before 0.17.0 wrote a hook registration that Gemini CLI ignored, so their hooks never ran. Re-run cardinal-connect to rewrite it (no new consent needed), then restart Gemini CLI. cardinal-status reports an old registration or a Gemini CLI version too old to load hooks.
Pull requests and decisions
Each prompt’s branch state includes the branch’s pull request when gh is installed and signed in and a PR exists. Network work runs in the background, so the hooks don’t slow your prompts.
Decision capture is off by default:
python3 scripts/cardinal-decision onWhile it’s on, each prompt tells Gemini to record material choices with cardinal-decision record …, and each decision is sent tagged with the session, repo, branch, and PR. python3 scripts/cardinal-decision off turns it off. With Gemini CLI’s sandbox enabled, the record command can’t write ~/.gemini; it exits with a message telling you to allow that path or run without the sandbox.
Storyboard associations
From version 0.20.4, the plugin tells Gemini CLI how Cardinal’s storyboards relate to your work, and records where a storyboard was written. See Associations for the model: a storyboard is about something someone declared, or written from a checkout.
- At session start, with Cardinal connected, the plugin gives Gemini this session’s id and shows storyboards about or written from the work in this repository, including after a merge. Gemini HTML-escapes
<and>in what it receives, so the block arrives as<cardinal-storyboards>; it is still delimited. It adds thecardinal-storyboardcommand (context,discover). - Edited files are recorded after
write_fileandreplacesucceed. - Context filled in automatically by a
BeforeToolhook, for thecardinalserver’s storyboard tools only. - No lookup before an edit. That is Claude Code only.
- Upgrading. Re-run
cardinal-connectso theBeforeTooland edit-recording hooks are registered, then restart Gemini CLI. - Opt out.
CARDINAL_STORYBOARD_SESSION_START=0drops the session id line and the discovery block;CARDINAL_STORYBOARD_DISCOVERY=0drops the discovery block only;CARDINAL_STORYBOARD_CONTEXT=0stops Gemini CLI from filling insession_idandcontext. - What it sends. Repository, branch, commit, pull request number and ticket key from the branch name, and (on
main) recently merged pull request numbers and your recent local branch names, to your own Cardinal. Files edited are repository-relative paths, visible to members of your org and never on a public link. See Associations.
Privacy
The plugin captures tool names, bash command lines, and file paths so Cardinal can tell which repo and service a session worked on. The contents of your prompts are never captured. Per-turn token counts come straight from Gemini CLI’s AfterModel hook payload — no transcript scraping — and are attributed to the model id the hook reports. Hooks don’t receive cached-token counts, so reported cost is an upper bound.
From version 0.18.0, the result of every tool call (Gemini’s own tools such as run_shell_command and read_file, and any MCP server, failed calls included) is kept locally as storyboard evidence, in ~/.cardinal/evidence/ on your machine, with credentials scrubbed, and deleted after 14 days. Cardinal’s own tools are skipped: their results are already witnessed. A call that touches something sensitive, such as a .env file, a private key or a secret-printing command, is withheld: only a stub saying why is kept. The plugin never uploads the spool; a result leaves your machine only when you cite it in an Investigation Storyboard and the plugin’s scripts/cardinal-evidence promotes it. To opt out, set CARDINAL_EVIDENCE_CAPTURE=0 in the environment Gemini CLI starts with, or create the file ~/.cardinal/evidence/disabled.
Payload-shape capture
The hooks follow Gemini CLI’s published hook payload types, but haven’t been observed widely in the wild. If your sessions look sparse, set CARDINAL_GEMINI_DEBUG_PAYLOADS=1 before starting Gemini CLI — raw hook payloads land under ~/.gemini/cardinal/telemetry/debug/<Event>-<ts>.json. Share those with the plugin maintainers so the parity spec can be locked to real key names.
Disconnect
python3 scripts/cardinal-disconnect # Remove everything the plugin added
python3 scripts/cardinal-disconnect --force # Skip the reachability probe on the revoke stepDisconnect revokes the keys with Cardinal and removes only the plugin-owned settings, extension bundle, and local state; everything else in your Gemini CLI config is left as it was.