Back to the catalog

sprintable

Sprintable channel for Gemini CLI — hooks-based two-way chat bridge + hosted MCP.

Open source Open in the app JSON README (API)

About

Sprintable channel for Gemini CLI — hooks-based two-way chat bridge + hosted MCP.

Details

Kind
Plugins
Topic
AI, RAG & memory
Publisher
moonklabs
Origin
gemini
Category
ferramentas
Version
0.1.0
Last push
2026-08-14T13:41:20Z
Repository state
ativo
Language
Python
Added
2026-08-30 14:13:39
Updated
2026-08-30 14:13:39
Origin id
moonklabs/sprintable-gemini

README

# sprintable-gemini

Sprintable channel extension for [Gemini CLI](https://github.com/google-gemini/gemini-cli) — real-time two-way chat between a Gemini agent and its Sprintable team, plus the hosted Sprintable MCP toolset.

## Install

```
gemini extensions install https://github.com/moonklabs/sprintable-gemini
```

One command: pulls the extension, prompts for your Sprintable agent API key (stored in your OS keychain, not a plain-text file — see [Settings](#settings)), and installs the bundled MCP server.

## What it does

- **`SessionStart`** hook boots a detached background listener that consumes the Sprintable SSE stream (`GET /api/v2/agent/stream`) for this agent's conversations.
- **`AfterAgent`** hook is the channel's turn-end heart: it posts the agent's last response back to Sprintable, then drains any queued inbound messages by returning `{"decision":"block","reason":"<joined messages>"}` — which Gemini CLI injects as a new user turn (confirmed against the actual `gemini-cli` source: `continueRequest = [{text: continueReason}]`, capped at `MAX_TURNS=100` per call).
- **Idle wake**: if a message arrives while the CLI process isn't actively running a turn, the listener actively resumes it via `gemini -r <session_id> -p "<message>"` instead of waiting for the next organic turn.

## Settings

Declared in `gemini-extension.json`'s `settings[]` — collected interactively on install, delivered to the hook subprocess's environment automatically by Gemini CLI itself (no manual `.env` wiring needed on your end):

| Setting | Env var | Sensitive |
|---|---|---|
| Sprintable Agent API Key | `SPRINTABLE_API_KEY` | yes (OS keychain) |
| Sprintable API URL | `SPRINTABLE_API_URL` | no (defaults to `https://app.sprintable.ai`) |

Reconfigure any time: `gemini extensions config sprintable`.

The bundled hosted-tools MCP server is registered under the key `sprintable-mcp`
in `gemini-extension.json` (not bare `sprintable` — #2577: that name is reserved
for the sibling Claude Code plugin's separate bundled *channel* MCP,
`sprintable-channel`, to avoid `/mcp`-listing confusion when both happen to be
configured on the same machine). The extension's own install/uninstall name
stays `sprintable`, unaffected.

## Uninstall

```
gemini extensions uninstall sprintable
```

This removes the extension registration, but **does not stop the background listener process** the `SessionStart` hook already spawned — Gemini CLI has no uninstall-time hook, so there's nothing to catch this at (confirmed live, QA finding). Kill it manually:

```
pkill -f 'sprintable-gemini.*scripts/listener.py'
```

(Adjust the pattern if you installed to a non-default path — check `ps aux | grep listener.py` first if unsure.)

## Design notes (ground-truthed against gemini-cli's own source, not public docs)

- **Hook payload is snake_case** (`session_id`, `prompt_response`, `stop_hook_active`, ...) — matches Claude Code/codex's convention, not grok's camelCase. Confirmed by reading the bundled `gemini-cli` package directly.
- **`gemini -r <session_id> -p "<msg>"` reliably re-fires `AfterAgent`** on the resumed turn. This is the opposite of how `codex exec resume` behaves (codex's resume never re-fires its `Stop` hook, confirmed live). Because of this, the idle-wake listener here does **not** post the reply itself — it only launches the resume and watches for process success/failure. Doing both (as an early draft of this plugin did, copying a pattern from the sibling `sprintable-codex` plugin where it was the correct fix) caused a real, reproduced double-post, since `AfterAgent`'s `prompt_response` and a would-be `-o json` capture aren't guaranteed byte-identical (streaming/buffering boundary), so a naive dedup-by-content-hash didn't catch it. One posting path only, no dedup shortcut needed.
- **No subdirectory installs.** `gemini extensions install <github-url>` clones the whole repo and only ever looks for `gemini-extension.json` at the clone root (confirmed by reading the install/clone code path); the URL parser itself rejects anything but exactly `owner/repo` (throws on extra path segments). That's why this extension lives in its own repository instead of a subdirectory of `moonklabs/sprintable-agent-plugins` alongside the codex and grok plugins.
- **No per-agent home directory override exists in Gemini CLI** (`~/.gemini` is hardcoded — no `GEMINI_HOME`-equivalent env var, unlike Codex's `CODEX_HOME` or Grok's `GROK_HOME`). If you're running more than one Sprintable-connected Gemini agent on the same machine, they will share the same OS keychain entry and extension settings by default. Use `SPRINTABLE_STATE_DIR` to at least isolate each agent's own queue/log state; there is currently no way to isolate the credential itself per-agent on a shared machine.

## Development / testing note

This extension was built and live-tested on a machine where Gemini CLI had no other real users or live consumers at the time (confirmed test-only environment, not a shared production install) — the live install/uninstall/keychain tests in this PR's evidence touched the real, shared `~/.gemini` directory (Gemini CLI provides no isolated per-agent home directory), which was judged acceptable specifically because of that premise. If Gemini CLI becomes a live, in-use tool on that machine, that premise no longer holds for future testing there.

More