feat(agents): add Gemini CLI adapter#34
Conversation
PR Summary by QodoAdd Gemini CLI adapter and include it in the default built-in agent set
AI Description
Diagram
High-Level Assessment
Files changed (6)
|
Code Review by Qodo
Context used✅ Compliance rules (platform):
64 rules✅ Skills:
doc-authoring, doc-authoring-with-logs 1.
|
Adapt Google's Gemini CLI harness into the closed agent set. It mirrors canonical rules to GEMINI.md and renders MCP servers into the shared .gemini/settings.json under the mcpServers key, preserving other settings. Remote transports map to Gemini's field-keyed shape: httpUrl for HTTP, url for SSE. Gemini has no hook or skills mechanism, so those slices are omitted. Registered in ADAPTERS and added to the curated default set.
Add a round-trip suite for the gemini-cli adapter: stdio and remote (httpUrl/url) MCP render → scrape, preservation of unrelated settings keys, mcpServers removal when empty, and claims. Update the agents init-step assertions to the expanded curated default set.
Note GEMINI.md + .gemini/settings.json in the materialization example and add Gemini CLI to the fixed built-in agent set.
Gemini CLI does expose lifecycle hooks and agent skills, so wire up both slices. Hooks live under the `hooks` key of the shared .gemini/settings.json and use Gemini's own event names, so translate the closed agnos vocabulary across the boundary (PreToolUse↔BeforeTool, Stop↔AfterAgent, PreCompact↔ PreCompress, etc.), dropping events with no counterpart. Skills link .gemini/skills to the canonical skills directory. Also stop claiming .gemini/settings.json: it is a shared user file (theme and other settings), so removing the agent must not delete it — key-level teardown happens through render, matching the Claude Code adapter.
Add coverage for the hook event-vocabulary translation and unsupported- event drop, the mcp+hooks shared-settings compose path, and update the claims assertion to the skills dir (settings.json is no longer claimed).
Promote the canonical event set to the union of every event any agent exposes (adds BeforeModel, AfterModel, BeforeToolSelection) and give each adapter a declared `hookEvents` map from canonical names to its own native names. The shared machinery (renderNativeHooks/scrapeNativeHooks) drives render and scrape off that map, replacing the per-adapter drop-set (Codex) and hand-rolled rename table (Gemini). Nothing is dropped from the central registry, so scraping preserves every event even when only one agent supports it; a per-agent render simply omits events that agent can't express. schema.json tracks the widened enum.
`hooks add` now resolves the installed agents and, using the central hookEvents maps, warns which of them do not support the event being added (naming each), so the hook silently not reaching an agent is surfaced up front rather than only at render time.
Rewrite the hooks-map suite around renderNativeHooks/scrapeNativeHooks with identity and rename maps, and add hooks-add tests asserting the unsupported- agent warning fires (naming the agent) and stays silent when all support it.
Re-checking the three agents' current hook docs showed the maps were far from complete: Claude Code exposes 30 events (we mapped 9), Codex 10 (we mapped 7, missing SubagentStart, PermissionRequest, PostCompact), and Gemini's 11 were already covered. Widen the canonical registry to the full union — Claude's 30 names plus Gemini's three model-loop events (BeforeModel, AfterModel, BeforeToolSelection) — so nothing is dropped, and complete each adapter's hookEvents map. Add an identityEventMap helper for the agents that use the canonical names verbatim (Claude Code, Codex). Codex's docs list a handler statusMessage, so render its hooks with messages preserved.
Add a coverage suite enumerating every event each agent documents and asserting the adapter maps it (guarding against a future doc event going unmapped), plus negative checks for events an agent genuinely lacks. Update the codex round-trip to expect the now-preserved statusMessage.
A declaration missing `command` (allowed by the schema) rendered an empty httpUrl/url/command into .gemini/settings.json, producing invalid config. Skip such servers in writeGeminiMcp with a warning naming the server, and omit each field in toGeminiServer unless present, so no empty value is ever written.
61298e7 to
d258ea6
Compare
What
Adapts Gemini CLI (Google's official terminal agent harness) into agnos as the third first-tier CLI harness, and reworks the hooks layer so agnos owns a central hook-event mapping registry that adapters map into, with complete, verified coverage of every event each agent documents and no events ever dropped from the registry.
Gemini CLI adapter
GEMINI.md.gemini/settings.json#mcpServershttpUrlfor HTTP,urlfor SSE.gemini/settings.json#hooks.gemini/skills/mcpandhookssharesettings.jsonvia per-key read-modify-write, so they never clobber each other or user-owned keys. That shared file is deliberately not claimed, matching Claude Code.Central hook-event mapping registry
BeforeModel/AfterModel/BeforeToolSelection).hookEventsmap (canonical → its native name);identityEventMapkeeps the verbatim-naming agents (Claude Code, Codex) terse.renderNativeHooks/scrapeNativeHooks) drives both directions. Nothing is dropped from the registry — scraping preserves every event; a per-agent render omits only events that agent can't express.Verified coverage (against current docs)
SubagentStart,PermissionRequest,PostCompact)Codex's docs also list a handler
statusMessage, so its hooks now render with messages preserved.Side-effect: add-time support warning
hooks add <event>resolves the installed agents and warns which don't support the event, naming each:Tests
httpUrl/url), hooks translation, mcp+hooks shared-file compose, skills, claimsrenderNativeHooks/scrapeNativeHooksround-trips (identity + rename maps);supportsHookEventhooks addwarning fires (naming the agent) and stays silent when all support itschema.jsonFull gate green:
lint(0 errors) →typecheck→test(257 passed) →build. Verified end-to-end with the CLI (add-time warnings, correct per-agent rendering of newly-mapped events likePermissionRequestandWorktreeCreate, andhooks migrateround-tripping without drops).