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.