feat(agent-manager): add Gemini CLI agent adapter#70
Open
nnhhoang wants to merge 1 commit intocodeaholicguy:mainfrom
Open
feat(agent-manager): add Gemini CLI agent adapter#70nnhhoang wants to merge 1 commit intocodeaholicguy:mainfrom
nnhhoang wants to merge 1 commit intocodeaholicguy:mainfrom
Conversation
Adds a GeminiCliAdapter alongside the existing Claude Code and Codex
adapters so `ai-devkit agent list` and `ai-devkit agent detail` can
discover and inspect running Gemini CLI sessions. The adapter follows
the canHandle / detectAgents / getConversation contract and registers
in every agent.ts entrypoint that composes an AgentManager.
Process detection reuses the shared `listAgentProcesses('gemini')`
helper. `isGeminiExecutable` falls back to `path.win32.basename` when
it sees a backslash separator so Windows-style command paths still
resolve to a gemini.exe basename on either platform.
Session discovery walks `~/.gemini/tmp/<shortId>/chats/session-*.json`
across every project short-id directory Gemini maintains. Each
session JSON carries its own `projectHash` — sha256 of the project
root that Gemini CLI resolved at write time via its `.git`-bounded
`findProjectRoot` walk. To stay consistent with that, the adapter
enumerates every ancestor of each running process' CWD, hashes each
candidate, and looks up any matching projectHash to populate
`resolvedCwd` on a SessionFile. The shared
`matchProcessesToSessions()` then performs the usual CWD + birthtime
1:1 greedy assignment, and processes without a matching session
fall back to the process-only AgentInfo shape.
`getConversation` parses the single-JSON-per-file layout Gemini uses
(not JSONL): `messages` is an array with `type` of 'user' or 'gemini'
for visible turns; 'thought' and 'tool' entries are hidden by default
and surface as `system` role when `--verbose` is set.
`displayContent` takes priority over `content` when both are present.
Test coverage mirrors the depth of CodexAdapter — 35 unit tests across
initialization, canHandle, detectAgents, discoverSessions (including
the parent-of-cwd git-root case), determineStatus, parseSession, and
getConversation. Also updates the jest mock in the CLI agent command
test so `agent detail` can route `gemini_cli` agents through the new
adapter for conversation rendering.
Owner
|
Did you test the implementation? I checked out the code, but the behavior when running agent list is not as expected; there is no Gemini session even though I already have one running. |
Owner
|
I also suggest that you run this work with dev-lifecycle so that we have the artifact (design, implementation) of this new integration in docs/ai for referencing later. |
| for (let i = messages.length - 1; i >= 0; i--) { | ||
| const entry = messages[i]; | ||
| if (entry?.type !== 'user') continue; | ||
| const text = (entry.displayContent || entry.content || '').trim(); |
Owner
There was a problem hiding this comment.
Gemini CLI writes content: [{text: "..."}] in practice, but the adapter assumed string. Calling .trim() on an array threw ".trim is not a function". Added resolveContent() to normalize both forms. Affects extractSummary, getConversation, and the GeminiMessageEntry type.
codeaholicguy
requested changes
Apr 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a
GeminiCliAdaptersoai-devkit agent listandai-devkit agent detailcan discover and inspect running Gemini CLI sessions. The adapter implements the existingAgentAdaptercontract (canHandle/detectAgents/getConversation) and registers in everyagent.tsentrypoint that composes anAgentManager.Gemini-specific design
Process detection. Gemini CLI ships as a Node script —
bundle/gemini.jswith shebang#!/usr/bin/env node. On POSIX systemsps auxtherefore reports the process asnode /path/to/gemini ...with argv[0] =node, notgemini. Empirically verified on macOS + Volta:The adapter reflects this: it requests the Node process pool via
listAgentProcesses('node')and keeps only entries whose command line references a gemini entrypoint (gemini,gemini.exe,gemini.jsbasename in any token). This is the only place the adapter deviates from the argv[0]-basename pattern, and the rationale is documented in JSDoc on bothdetectAgentsandisGeminiExecutable.Session discovery. Sessions live at
~/.gemini/tmp/<shortId>/chats/session-*.json.<shortId>is opaque (managed by Gemini's project registry), so the adapter iterates every short-id directory and filters by matchingsession.projectHash— sha256 of the project root Gemini resolved at write time via its.git-boundedfindProjectRootwalk. To stay consistent with that walk, the adapter hashes every ancestor of each process CWD as a candidate (candidateProjectRoots) so subdirectory invocations still line up with the session file the Gemini process wrote. Matched sessions populateresolvedCwdon aSessionFile, then the sharedmatchProcessesToSessions()performs the standard CWD + birthtime 1:1 greedy assignment. Unmatched processes fall back to the process-onlyAgentInfoshape.Session parsing. Gemini writes a single JSON object per file (not JSONL) with schema
{ sessionId, projectHash, startTime, lastUpdated, messages[], directories?, kind }. Each message entry hastypeofuser,gemini,thought, ortool— verified against the vendored bundle source. Visible turns map touser/assistantroles;thoughtandtoolentries are hidden by default and surface assystemwhen--verboseis passed.displayContenttakes priority overcontentwhen both are present.Test coverage
37 unit tests:
gemini.jsbundle entrypoint~/.gemini/tmp, empty CWD, projectHash mismatch, malformed JSON skip,session-filename prefix enforcement, parent-of-cwd git-root fallbacklastUpdatedpriority over entry timestampdisplayContentpriority, empty-content skip, missing type, non-array messagesnx run-many -t build test lintpasses — 461 tests across 4 packages, 0 lint errors. Also verified end-to-end by spawning a Node script whose basename resolves togemini, writing a synthetic session JSON, and callingadapter.detectAgents()directly: process is detected, matched to its session viaprojectHash, and mapped to anAgentInfowith status, summary, andsessionFilePathpopulated.CLI wiring
Registered in all four
agent.tsentrypoints (list,detail,stop,focus). Theagent detailroute map also mapsgemini_cli→ the new adapter for conversation rendering.