feat: add Gemini CLI + CodeBuddy sessions, polish Usage and Sessions UI#113
feat: add Gemini CLI + CodeBuddy sessions, polish Usage and Sessions UI#113
Conversation
📝 WalkthroughWalkthroughAdds first-class Gemini CLI and CodeBuddy Code session support across CLI discovery/caching, per-source path resolution, session-summary parsing/message extraction, export/trash flows, Web UI/source handling, tests, i18n, install targets, and documentation updates. Changes
Sequence Diagram(s)sequenceDiagram
participant WebUI as Web UI
participant API as Server/API
participant FS as Filesystem
participant Parser as SessionParser
WebUI->>API: GET /session-detail?source=gemini&id=<id>
API->>FS: resolve Gemini tmp dir & read `tmp/<project>/chats/<id>.json`
FS-->>API: raw JSON (header bytes + messages)
API->>Parser: parse header (sessionId, model, timestamps) and messages (strip leading system)
Parser-->>API: normalized session {title, model, messages}
API-->>WebUI: JSON response (sessionDetail with sourceLabel "Gemini CLI")
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
cli.js (2)
3386-3400:⚠️ Potential issue | 🟠 MajorGemini inventory still collides with Codex cache/lookup helpers.
This path now passes
source === "gemini", but the shared inventory helpers still collapse every non-claudesource tocodex. After a Codex browse, a Gemini browse can reuse the cached Codex inventory, and thesessionId -> filePathmap is registered into the Codex store instead of the Gemini store. Please make the shared cache-key / lookup normalization source-aware before routing Gemini through this path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 3386 - 3400, The cache/lookup is collapsing non-claude sources to 'codex', causing Gemini to reuse Codex cache; fix by making the cache key and cache access source-aware: update buildSessionInventoryCacheKey (or call it with the normalizedSource) so the key includes normalizedSource, and ensure getSessionInventoryCache and setSessionInventoryCache are invoked with/expect a source (pass normalizedSource) rather than relying on helpers that collapse non-claude to codex; specifically change listSessionInventoryBySource to use a source-aware cache key and to pass normalizedSource into getSessionInventoryCache/setSessionInventoryCache (or update those helper implementations) so Gemini and Codex maintain separate caches and sessionId->filePath maps.
5444-5494:⚠️ Potential issue | 🔴 CriticalGemini trash entries are still being serialized as Codex entries.
This function now accepts
source === "gemini", but the shared trash helpers it calls still coerce every non-claudesource to Codex semantics. A trashed Gemini session will therefore be indexed undersource: "codex", pick up the wrong labels/provider defaults, and restore into the Codex sessions tree instead of.gemini/tmp/....
🧹 Nitpick comments (4)
web-ui/modules/app.methods.session-browser.mjs (1)
130-133: Source normalization is correct, but duplicated in three places.Please centralize this mapping into one helper to avoid future source-drift bugs.
Refactor sketch
+ normalizeSessionPathSource(source) { + if (source === 'claude' || source === 'gemini' || source === 'all') return source; + return 'codex'; + }, syncSessionPathOptionsForSource(source, nextOptions, mergeWithExisting = false) { - const targetSource = source === 'claude' - ? 'claude' - : (source === 'gemini' ? 'gemini' : (source === 'all' ? 'all' : 'codex')); + const targetSource = this.normalizeSessionPathSource(source); ... }, refreshSessionPathOptions(source) { - const targetSource = source === 'claude' - ? 'claude' - : (source === 'gemini' ? 'gemini' : (source === 'all' ? 'all' : 'codex')); + const targetSource = this.normalizeSessionPathSource(source); ... }, async loadSessionPathOptions(options = {}) { - const source = options.source === 'claude' - ? 'claude' - : (options.source === 'gemini' ? 'gemini' : (options.source === 'all' ? 'all' : 'codex')); + const source = this.normalizeSessionPathSource(options.source); ... },Also applies to: 147-150, 171-174
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web-ui/modules/app.methods.session-browser.mjs` around lines 130 - 133, Duplicate source-to-target mapping appears in multiple places; create a single helper (e.g., normalizeSource or mapSourceToTarget) at module scope that implements the logic (source === 'claude' ? 'claude' : source === 'gemini' ? 'gemini' : source === 'all' ? 'all' : 'codex') and replace the repeated inline logic in the three locations that reference this mapping (the spots that read this.sessionPathOptionsMap[targetSource] and the other two occurrences) to call that helper instead; ensure the helper is imported/defined in the same module scope and used consistently so all accesses use the same normalized targetSource value.tests/e2e/test-sessions.js (2)
6-6: Optional: add explicit path-list assertion for seeded Gemini path.Since
geminiSessionPathis already available from setup, asserting it appears inlist-session-pathsoutput would complete Gemini path-flow coverage.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/test-sessions.js` at line 6, Add an explicit assertion that the seeded Gemini session path (geminiSessionPath) appears in the output of the list-session-paths check: locate where the test already destructures geminiSessionPath from ctx and where other session-path assertions run (the list-session-paths response handling), then add an assertion that the response includes geminiSessionPath (similar to the existing checks for sessionPath and claudeSessionPath) so Gemini path flow is covered.
84-87: Strengthen Gemini export assertion to validate payload semantics.Line [118]-Line [120] only checks existence. Consider also asserting expected text (e.g.,
hello from codexmate) and truncation flag behavior for tighter regression detection.Also applies to: 99-101, 118-120
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/test-sessions.js` around lines 84 - 87, Update the Gemini session assertions to validate message semantics: in the test that calls api('session-detail', { source: 'gemini', sessionId: geminiSessionId }) (variable sessionDetailGemini), keep the Array.isArray check but also assert that at least one message's text includes "hello from codexmate" and that the message object(s) has the expected truncation flag (e.g., isTruncated === true/false as the spec requires). Apply the same stronger checks to the other two similar blocks referenced (the ones around the other sessionDetail variables) so each asserts both the expected substring in message.text and the correct truncation flag on the corresponding message object.cli/session-usage.js (1)
85-87: Avoid exception-driven control flow for missing Gemini parser dependency.At Line [85]-Line [87], an explicit function check is clearer than relying on a thrown
TypeErrorand catch fallback.Suggested refactor
- summary = normalized.source === 'claude' - ? parseClaudeSessionSummary(filePath, summaryOptions) - : (normalized.source === 'gemini' - ? parseGeminiSessionSummary(filePath, summaryOptions) - : parseCodexSessionSummary(filePath, summaryOptions)); + if (normalized.source === 'claude') { + summary = parseClaudeSessionSummary(filePath, summaryOptions); + } else if (normalized.source === 'gemini') { + summary = typeof parseGeminiSessionSummary === 'function' + ? parseGeminiSessionSummary(filePath, summaryOptions) + : null; + } else { + summary = parseCodexSessionSummary(filePath, summaryOptions); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/session-usage.js` around lines 85 - 87, The current code relies on a thrown TypeError to fall back from parseGeminiSessionSummary to parseCodexSessionSummary; change this to an explicit check by testing the Gemini parser's availability (e.g. typeof parseGeminiSessionSummary === 'function' or a hasGeminiParser flag) before calling it, and if it's not available call parseCodexSessionSummary with the same filePath and summaryOptions; update the conditional expression around parseGeminiSessionSummary/parseCodexSessionSummary to use that explicit check instead of relying on exception-driven control flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cli.js`:
- Around line 4129-4130: The current assignment of cwd (const cwd = projectHash
? path.join(getGeminiTmpDir(), projectHash) : '') is synthesizing a temp-hash
path; change it to prefer the real workspace path provided in the session JSON
(use json.projectRoot or json.cwd) and only fall back to the tmp/hash when those
are missing. Update the code that computes cwd (the variable named cwd, and any
logic around projectHash/getGeminiTmpDir()) to use sessionJson.projectRoot ||
sessionJson.cwd || (projectHash ? path.join(getGeminiTmpDir(), projectHash) :
'') so Gemini summaries will use the real project path for filtering and
list-session-paths.
- Around line 4142-4149: The browse/list path currently returns summary objects
with messageCount: 0 and __messageCountExact: false, causing Gemini rows to
always show zero; update the listSessionBrowse() (the browse/list path that
builds these summary objects) to compute and assign the real message count
instead of the hardcoded zero—either call the existing message-count helper
(e.g., whatever function is used elsewhere to hydrate exact counts) or compute
counts inline and set messageCount and __messageCountExact = true for sessions
where counts are available; remove the hardcoded zero assignment in the summary
construction and ensure variable names messageCount and __messageCountExact are
populated from the real count computation.
- Around line 3018-3064: The current gemini branch reads the raw file bytes and
runs matchTokensInText against the serialized JSON, causing false positives;
instead parse the Gemini JSON and scan message-level text like the roleFilter
!== 'all' path: read/JSON.parse the file, iterate rawMessages (using
normalizeGeminiMessageRole, extractGeminiMessageText, extractMessageText), feed
each message to consumeSessionQueryMessage and stop on match, accumulate
snippets via truncateText when state.snippetLimit > 0, set
state.count/state.snippets accordingly, and finally return
buildSessionQueryScanResult; do not run matchTokensInText against the full
serialized JSON blob or limit to an arbitrary 512KB raw slice.
In `@web-ui/logic.sessions.mjs`:
- Line 32: The UI copy for the enabled-query i18n key is stale after adding
Gemini support; update the i18n string used when normalized queries include
'gemini' (the same logic branch that returns normalized === 'codex' ||
normalized === 'claude' || normalized === 'gemini' || normalized === 'all') to
mention Gemini (e.g., “supports Codex/Claude/Gemini” or equivalent), and ensure
any placeholder or tooltip that references enabled-query reflects the new
option.
In `@web-ui/partials/index/layout-header.html`:
- Line 372: The header currently maps sessionFilterSource to labels inline
(using t('sessions.source.all') but falling back to hardcoded names for others),
causing inconsistent labels vs the side rail; update the mapping logic that uses
sessionFilterSource so that all branches use i18n keys (e.g.,
t('sessions.source.claude'), t('sessions.source.gemini'),
t('sessions.source.codex')) instead of hardcoded strings like "Claude Code" or
"Gemini CLI" and ensure the same mapping is used in both places that reference
sessionFilterSource so Gemini is labeled consistently.
---
Outside diff comments:
In `@cli.js`:
- Around line 3386-3400: The cache/lookup is collapsing non-claude sources to
'codex', causing Gemini to reuse Codex cache; fix by making the cache key and
cache access source-aware: update buildSessionInventoryCacheKey (or call it with
the normalizedSource) so the key includes normalizedSource, and ensure
getSessionInventoryCache and setSessionInventoryCache are invoked with/expect a
source (pass normalizedSource) rather than relying on helpers that collapse
non-claude to codex; specifically change listSessionInventoryBySource to use a
source-aware cache key and to pass normalizedSource into
getSessionInventoryCache/setSessionInventoryCache (or update those helper
implementations) so Gemini and Codex maintain separate caches and
sessionId->filePath maps.
---
Nitpick comments:
In `@cli/session-usage.js`:
- Around line 85-87: The current code relies on a thrown TypeError to fall back
from parseGeminiSessionSummary to parseCodexSessionSummary; change this to an
explicit check by testing the Gemini parser's availability (e.g. typeof
parseGeminiSessionSummary === 'function' or a hasGeminiParser flag) before
calling it, and if it's not available call parseCodexSessionSummary with the
same filePath and summaryOptions; update the conditional expression around
parseGeminiSessionSummary/parseCodexSessionSummary to use that explicit check
instead of relying on exception-driven control flow.
In `@tests/e2e/test-sessions.js`:
- Line 6: Add an explicit assertion that the seeded Gemini session path
(geminiSessionPath) appears in the output of the list-session-paths check:
locate where the test already destructures geminiSessionPath from ctx and where
other session-path assertions run (the list-session-paths response handling),
then add an assertion that the response includes geminiSessionPath (similar to
the existing checks for sessionPath and claudeSessionPath) so Gemini path flow
is covered.
- Around line 84-87: Update the Gemini session assertions to validate message
semantics: in the test that calls api('session-detail', { source: 'gemini',
sessionId: geminiSessionId }) (variable sessionDetailGemini), keep the
Array.isArray check but also assert that at least one message's text includes
"hello from codexmate" and that the message object(s) has the expected
truncation flag (e.g., isTruncated === true/false as the spec requires). Apply
the same stronger checks to the other two similar blocks referenced (the ones
around the other sessionDetail variables) so each asserts both the expected
substring in message.text and the correct truncation flag on the corresponding
message object.
In `@web-ui/modules/app.methods.session-browser.mjs`:
- Around line 130-133: Duplicate source-to-target mapping appears in multiple
places; create a single helper (e.g., normalizeSource or mapSourceToTarget) at
module scope that implements the logic (source === 'claude' ? 'claude' : source
=== 'gemini' ? 'gemini' : source === 'all' ? 'all' : 'codex') and replace the
repeated inline logic in the three locations that reference this mapping (the
spots that read this.sessionPathOptionsMap[targetSource] and the other two
occurrences) to call that helper instead; ensure the helper is imported/defined
in the same module scope and used consistently so all accesses use the same
normalized targetSource value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 43901e00-13de-4c77-b270-8da6c71d82aa
📒 Files selected for processing (12)
cli.jscli/session-usage.jstests/e2e/test-sessions.jstests/e2e/test-setup.jstests/unit/session-usage-backend.test.mjsweb-ui/app.jsweb-ui/logic.sessions.mjsweb-ui/modules/app.methods.session-browser.mjsweb-ui/modules/app.methods.session-trash.mjsweb-ui/modules/i18n.dict.mjsweb-ui/partials/index/layout-header.htmlweb-ui/partials/index/panel-sessions.html
📜 Review details
🔇 Additional comments (10)
tests/unit/session-usage-backend.test.mjs (1)
59-61: Good defensive test guard for unintended Gemini parsing.This makes unexpected Gemini summary parsing fail fast in unit tests that should stay on lightweight paths.
web-ui/partials/index/panel-sessions.html (1)
42-42: Gemini source selector is wired correctly.The new option is correctly bound to
sessionFilterSourceand uses i18n.web-ui/modules/i18n.dict.mjs (1)
536-536: i18n additions are complete and consistent.
sessions.source.geminiis added in both locales with matching key structure.Also applies to: 1554-1554
web-ui/app.js (1)
181-182: State initialization for Gemini is complete.All three session-path state maps include
gemini, so per-source loading/caching stays consistent.Also applies to: 187-188, 193-194
web-ui/logic.sessions.mjs (1)
39-39: Source normalization update looks correct.Including
geminiinnormalizeSessionSourcealigns UI normalization with backend-supported source values.web-ui/modules/app.methods.session-trash.mjs (1)
13-15: Gemini trash metadata mapping is correctly integrated.
sourceand fallbacksourceLabelnow handle Gemini consistently with existing Codex/Claude behavior.Also applies to: 21-21
tests/e2e/test-setup.js (2)
208-223: Gemini fixture setup is clean and deterministic.This test data shape is consistent with the Gemini session readers and gives stable E2E behavior.
235-236: Context propagation for Gemini session metadata looks good.Passing both
geminiSessionIdandgeminiSessionPathintoctxis the right wiring for downstream E2E steps.cli/session-usage.js (1)
15-17: Gemini source normalization is correctly handled.Keeping
geminias a first-class source here is the right behavior forlist-sessions-usage.tests/e2e/test-sessions.js (1)
27-31: Gemini session listing coverage is solid.These assertions correctly validate both
source: 'gemini'andsource: 'all'behaviors.Also applies to: 37-37
| const cwd = projectHash ? path.join(getGeminiTmpDir(), projectHash) : ''; | ||
|
|
There was a problem hiding this comment.
Use the real project path in Gemini summaries.
cwd is being synthesized as ~/.gemini/tmp/<project_hash>, but the detail/plain/export readers already look for json.projectRoot / json.cwd. That means Gemini sessions won't participate correctly in path filtering or list-session-paths, because the browser indexes the tmp hash path instead of the workspace path.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cli.js` around lines 4129 - 4130, The current assignment of cwd (const cwd =
projectHash ? path.join(getGeminiTmpDir(), projectHash) : '') is synthesizing a
temp-hash path; change it to prefer the real workspace path provided in the
session JSON (use json.projectRoot or json.cwd) and only fall back to the
tmp/hash when those are missing. Update the code that computes cwd (the variable
named cwd, and any logic around projectHash/getGeminiTmpDir()) to use
sessionJson.projectRoot || sessionJson.cwd || (projectHash ?
path.join(getGeminiTmpDir(), projectHash) : '') so Gemini summaries will use the
real project path for filtering and list-session-paths.
| export function isSessionQueryEnabled(source) { | ||
| const normalized = normalizeSessionSource(source, ''); | ||
| return normalized === 'codex' || normalized === 'claude' || normalized === 'all'; | ||
| return normalized === 'codex' || normalized === 'claude' || normalized === 'gemini' || normalized === 'all'; |
There was a problem hiding this comment.
Query capability changed, but UI copy now appears stale.
With Line 32 enabling Gemini queries, the current placeholder text (“supports Codex/Claude”) becomes inaccurate for Gemini source selection. Please update the enabled-query i18n copy accordingly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web-ui/logic.sessions.mjs` at line 32, The UI copy for the enabled-query i18n
key is stale after adding Gemini support; update the i18n string used when
normalized queries include 'gemini' (the same logic branch that returns
normalized === 'codex' || normalized === 'claude' || normalized === 'gemini' ||
normalized === 'all') to mention Gemini (e.g., “supports Codex/Claude/Gemini” or
equivalent), and ensure any placeholder or tooltip that references enabled-query
reflects the new option.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web-ui/styles/controls-forms.css`:
- Around line 92-121: The current CSS forces .selector-actions and
.sessions-header-actions to never wrap, causing localized button labels to
overflow on narrow screens; add a responsive breakpoint (e.g., max-width
small/compact viewport) that overrides these rules for .selector-actions and
.sessions-header-actions to allow wrapping/reflow: switch .selector-actions to a
wrapping layout (e.g., change grid-auto-flow/auto-columns or use inline-flex
with flex-wrap: wrap), remove or override white-space: nowrap for both
containers, and ensure .btn-tool and .btn-tool-compact keep sensible flex: 0 0
auto but can wrap or break lines; also reduce gaps at that breakpoint to
preserve compact spacing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 291efa14-cbe5-4845-b36f-56a3ac431843
📒 Files selected for processing (7)
tests/unit/session-browser-timeline-regression.test.mjsweb-ui/modules/app.computed.dashboard.mjsweb-ui/modules/app.methods.session-browser.mjsweb-ui/modules/i18n.dict.mjsweb-ui/partials/index/layout-header.htmlweb-ui/partials/index/panel-sessions.htmlweb-ui/styles/controls-forms.css
✅ Files skipped from review due to trivial changes (1)
- web-ui/modules/i18n.dict.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
- web-ui/partials/index/panel-sessions.html
- web-ui/modules/app.methods.session-browser.mjs
📜 Review details
🔇 Additional comments (9)
web-ui/modules/app.computed.dashboard.mjs (1)
77-81: LGTM — Gemini is handled correctly here.The new
geminibranch matches the supported source normalization and uses the existing localized label key, so this stays aligned with the rest of the dashboard.web-ui/partials/index/layout-header.html (2)
217-218: LGTM — this label mapping now covers Gemini.The sidebar source label now matches the supported
sessionFilterSourcevalues and keeps the UI consistent with the new Gemini session source.
372-373: LGTM — the status strip now matches the sidebar.This mirrors the updated source-label mapping, so Gemini is shown consistently in both header locations.
tests/unit/session-browser-timeline-regression.test.mjs (4)
8-10: Good addition: the helper import keeps the new tests isolated.No concerns here.
36-55: Solid regression coverage for session-source changes.This test directly exercises the
event.target.valuepath and verifies the single reload behavior.
57-96: Good coverage for the URL-backed restore path.The test matches the intended
s_sourcebehavior and confirms the reload happens once.
98-140: Good coverage for the cached-source restore path.This validates the localStorage fallback and the expected single reload.
web-ui/styles/controls-forms.css (2)
17-23: Looks good.Keeping the header on one line while adding a controlled gap is the right direction for this compact toolbar.
79-89: Good truncation behavior here.
min-width: 0plus ellipsis/nowrap is the correct flex-item pattern to keep long titles from pushing the actions off-screen.
| .selector-actions { | ||
| display: flex; | ||
| gap: var(--spacing-xs); | ||
| display: inline-grid; | ||
| grid-auto-flow: column; | ||
| grid-auto-columns: max-content; | ||
| column-gap: var(--spacing-xs); | ||
| align-items: center; | ||
| justify-content: end; | ||
| white-space: nowrap; | ||
| flex: 0 0 auto; | ||
| } | ||
|
|
||
| .selector-actions > .btn-tool, | ||
| .selector-actions > .btn-tool-compact { | ||
| flex: 0 0 auto; | ||
| } | ||
|
|
||
| .sessions-header-actions { | ||
| display: inline-flex !important; | ||
| flex-direction: row !important; | ||
| flex-wrap: nowrap !important; | ||
| align-items: center; | ||
| gap: var(--spacing-xs); | ||
| white-space: nowrap; | ||
| } | ||
|
|
||
| .sessions-header-actions > .btn-tool, | ||
| .sessions-header-actions > .btn-tool-compact { | ||
| white-space: nowrap; | ||
| flex: 0 0 auto; | ||
| } |
There was a problem hiding this comment.
Add a responsive fallback for narrow widths.
Forcing both the header and action group to never wrap can make localized button labels overflow horizontally on smaller screens. Please keep a breakpoint that lets the actions reflow, or this layout may regress on compact viewports.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web-ui/styles/controls-forms.css` around lines 92 - 121, The current CSS
forces .selector-actions and .sessions-header-actions to never wrap, causing
localized button labels to overflow on narrow screens; add a responsive
breakpoint (e.g., max-width small/compact viewport) that overrides these rules
for .selector-actions and .sessions-header-actions to allow wrapping/reflow:
switch .selector-actions to a wrapping layout (e.g., change
grid-auto-flow/auto-columns or use inline-flex with flex-wrap: wrap), remove or
override white-space: nowrap for both containers, and ensure .btn-tool and
.btn-tool-compact keep sensible flex: 0 0 auto but can wrap or break lines; also
reduce gaps at that breakpoint to preserve compact spacing.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
README.md (2)
32-60:⚠️ Potential issue | 🟡 MinorMirror the Gemini update in the duplicated session docs.
These bullets are fine, but the later repeated
Sessions Mode/export-sessionsections in this README still say Codex + Claude only. The file now reads as if Gemini is supported in one place and not in another.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@README.md` around lines 32 - 60, Update the duplicated session documentation to mention Gemini wherever Codex and Claude are listed: find the "Sessions Mode" and "export-session" sections and add Gemini to the tool lists and examples so they match the earlier "Session Management" bullets (e.g., change phrases like "Codex + Claude only" to "Codex + Claude + Gemini" and update any example commands or descriptions that enumerate supported providers to include Gemini). Ensure the wording, command examples, and any usage/analytics descriptions mirror the earlier section that already includes Gemini.
166-181:⚠️ Potential issue | 🟡 MinorUpdate the install heading to include Gemini.
The block now includes a Gemini CLI install command, but the heading still says "Claude Code CLI" only. That makes the section title narrower than its contents.
Proposed fix
-### Install Codex CLI / Claude Code CLI (optional) +### Install Codex CLI / Claude Code / Gemini CLI (optional)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@README.md` around lines 166 - 181, Update the section heading "Install Codex CLI / Claude Code CLI (optional)" to reflect all included CLIs (Codex, Claude Code, Gemini, etc.) so the title matches the commands; locate the heading string "Install Codex CLI / Claude Code CLI (optional)" and change it to something inclusive (e.g., "Install Codex, Claude Code, Gemini CLI (optional)"), ensuring it corresponds to the listed command `npm install -g `@google/gemini-cli`` in the block.README.zh.md (1)
32-61:⚠️ Potential issue | 🟡 Minor同步更新后面的重复会话文档。
这里已经写入 Gemini 了,但后面重复的
会话模式/export-session段落仍然只写 Codex + Claude。文档会出现前后不一致,容易误导读者。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@README.zh.md` around lines 32 - 61, Documentation inconsistency: the earlier "会话管理" section includes Gemini but the later duplicated "会话模式" and "export-session" sections only mention Codex + Claude; update those repeated sections ("会话模式" and "export-session") to include Gemini (and any Gemini-specific notes or CLI flags) so the document is consistent with "会话管理" and other references to Gemini in README.zh.md.cli.js (4)
6202-6298:⚠️ Potential issue | 🟠 Major
codexmate export-sessionstill can't reach this Gemini path.
exportSessionData()now handlessource === "gemini", butparseExportSessionArgs()andprintExportSessionUsage()in the same file still only accept/displaycodex|claude. The CLI rejects the new source before this code runs.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 6202 - 6298, The CLI never reaches exportSessionData's "gemini" branch because parseExportSessionArgs and printExportSessionUsage still only accept/display "codex|claude"; update both to include "gemini". In parseExportSessionArgs (the arg parsing/validation function) add "gemini" as an allowed source value and ensure any switch/conditional that maps params.source to internal source strings handles "gemini" the same way as "codex"/"claude". In printExportSessionUsage (the help/usage printer) add "gemini" to the list of supported sources shown to users so the CLI will accept and advertise the new option. Ensure the same canonical label ("gemini") is used so exportSessionData’s source === 'gemini' branch is reachable.
9181-9196:⚠️ Potential issue | 🟠 MajorDon't advertise Gemini usage rows until the usage UI accepts them.
This action now accepts
source=gemini, butweb-ui/logic.sessions.mjs:250-270still hard-filters usage groups tocodex/claude, andweb-ui/logic.sessions.mjs:286-295only totals those two sources. That makes the API surface ahead of the consumer and contradicts the PR note that usage stays Codex/Claude-only.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 9181 - 9196, The CLI case 'list-sessions-usage' currently accepts "gemini" as a valid source; change the validation logic to only allow "codex", "claude", or "all" (remove "gemini" from the allowed set and error text) so the API doesn't advertise Gemini before the UI supports it; update the error message in the result to reflect the allowed values and leave the call to listSessionUsage({ ...usageParams, source: source || 'all' }) unchanged so behavior stays the same for codex/claude/all.
3393-3406:⚠️ Potential issue | 🔴 CriticalGemini inventory cache currently collides with Codex.
listSessionInventoryBySource('gemini', ...)now calls into the shared inventory cache, butbuildSessionInventoryCacheKey()still folds every non-claudesource intocodex. In the commonsource=allflow, the Codex branch populates that key first, so the Gemini branch reads Codex sessions back from cache and Gemini sessions disappear entirely.registerSessionFileLookupEntries()has the same normalization bug, so the dedicated Gemini lookup map never gets populated either.🔧 Suggested fix
function buildSessionInventoryCacheKey(source, limit, options = {}) { - const normalizedSource = source === 'claude' ? 'claude' : 'codex'; + const normalizedSource = source === 'claude' || source === 'gemini' ? source : 'codex'; const normalizedLimit = Number.isFinite(Number(limit)) ? Math.max(1, Math.floor(Number(limit))) : 1; @@ function registerSessionFileLookupEntries(source, sessions = []) { - const normalizedSource = source === 'claude' ? 'claude' : 'codex'; + const normalizedSource = source === 'claude' || source === 'gemini' ? source : 'codex'; const store = g_sessionFileLookupCache[normalizedSource]; if (!(store instanceof Map) || !Array.isArray(sessions)) { return;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 3393 - 3406, The cache key normalization is collapsing every non-`claude` source into `codex`, causing Gemini to read Codex entries; update the logic so the cache and lookup maps use the actual normalized source (preserving 'gemini' and 'codex') instead of always folding to 'codex' — adjust the normalization and callers of buildSessionInventoryCacheKey, getSessionInventoryCache, setSessionInventoryCache, and registerSessionFileLookupEntries to pass/use the true normalizedSource (e.g., keep normalizedSource = source === 'claude' || source === 'gemini' ? source : 'codex' and ensure buildSessionInventoryCacheKey accepts/uses that value rather than re-folding), and confirm listGeminiSessions populates the Gemini-specific lookup map via registerSessionFileLookupEntries.
5451-5500:⚠️ Potential issue | 🔴 CriticalGemini trash entries are still persisted as Codex.
trashSessionData()now allowssource === "gemini", butbuildSessionTrashEntry()still collapses any non-claudesource tocodex. That mislabels the trash entry and makes restore fail becauseresolveSessionRestoreTarget()validates the original Gemini path against the Codex root.🔧 Suggested fix
function buildSessionTrashEntry(summary, options = {}) { - const source = options.source === 'claude' ? 'claude' : 'codex'; + const source = options.source === 'claude' + ? 'claude' + : (options.source === 'gemini' ? 'gemini' : 'codex'); @@ - const sourceLabel = source === 'claude' ? 'Claude Code' : 'Codex'; + const sourceLabel = source === 'claude' + ? 'Claude Code' + : (source === 'gemini' ? 'Gemini CLI' : 'Codex'); @@ function resolveSessionRestoreTarget(entry) { @@ - const root = normalized.source === 'claude' ? getClaudeProjectsDir() : getCodexSessionsDir(); + const root = normalized.source === 'claude' + ? getClaudeProjectsDir() + : (normalized.source === 'gemini' ? getGeminiTmpDir() : getCodexSessionsDir());🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 5451 - 5500, The trash entry is being mislabeled because buildSessionTrashEntry collapses any non-claude source to "codex", so when trashing a Gemini session the entry.source becomes "codex" and restore validation fails; change buildSessionTrashEntry (and any helper it uses) to accept and preserve the original source string (e.g., "gemini") instead of coercing to "codex" so callers like trashSessionData (where buildSessionTrashEntry is called) store entry.source = source; after that, ensure resolveSessionRestoreTarget's validation logic recognizes "gemini" roots (or maps the preserved "gemini" entry back to the correct Gemini root) so restore checks succeed.
♻️ Duplicate comments (4)
web-ui/styles/controls-forms.css (1)
17-23:⚠️ Potential issue | 🟡 MinorAdd a compact-width escape hatch for these nowrap rules.
These overrides keep both the selector header and session actions permanently on one line, so localized labels can still overflow on narrow viewports. Please keep a small-screen breakpoint that lets the actions wrap/reflow instead of locking
nowrapeverywhere.Suggested responsive override
+@media (max-width: 640px) { + .selector-header, + .sessions-header-actions { + flex-wrap: wrap !important; + white-space: normal; + } + + .selector-actions { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + } +}Also applies to: 92-115
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web-ui/styles/controls-forms.css` around lines 17 - 23, The .selector-header rule currently forces flex-wrap: nowrap which prevents localized labels and .session-actions from wrapping on narrow viewports; add a compact-width escape hatch via a small-screen media query (e.g., max-width ~480px) that overrides .selector-header (and the related .session-actions styles around lines 92-115) to set flex-wrap: wrap and relax alignment/gap so actions can reflow (optionally set align-items and gap to more vertical-friendly values) on small screens.cli.js (3)
4135-4145:⚠️ Potential issue | 🟠 MajorPrefer the workspace path in Gemini summaries.
cwdis still synthesized as~/.gemini/tmp/<project_hash>, but the detail/plain/export readers already preferjson.projectRoot/json.cwd. That means path filtering andlist-session-pathswill index the temp bucket instead of the real project path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 4135 - 4145, The returned summary is setting cwd to the temporary path (cwd variable via getGeminiTmpDir() + projectHash) which causes downstream readers to index the temp bucket instead of the real project path; change the summary to prefer the real workspace path when available (use json.projectRoot or json.cwd if present) before falling back to the synthesized tmp path. Locate the object returned in the function that builds the Gemini summary (the cwd variable and the return block with source:'gemini') and make cwd resolve to json.projectRoot || json.cwd || (projectHash ? path.join(getGeminiTmpDir(), projectHash) : '') so the real project root is used when present.
4148-4155:⚠️ Potential issue | 🟠 MajorBrowse rows will still show Gemini sessions as
messageCount: 0.
listSessionBrowse()returns these summary objects directly and never hydrates exact counts afterward, so hardcoding zero here leaves every Gemini row wrong.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 4148 - 4155, The code is overwriting real session summary counts with zeros (messageCount, totalTokens, inputTokens, etc.) causing Gemini rows from listSessionBrowse() to display messageCount: 0; instead, stop hardcoding zeros and copy the corresponding values from the incoming summary object returned by listSessionBrowse() (e.g., use sessionSummary.messageCount, sessionSummary.totalTokens, sessionSummary.inputTokens, sessionSummary.outputTokens, sessionSummary.reasoningOutputTokens) and set __messageCountExact to the correct boolean from the summary (e.g., sessionSummary.__messageCountExact) so Gemini sessions show their actual counts.
3024-3070:⚠️ Potential issue | 🟠 MajorKeep Gemini content search message-based for
roleFilter="all"too.This branch still token-matches against the serialized JSON blob / first 512 KB instead of parsed messages, so metadata can satisfy a content query and later chat content can be missed.
queryScope=content/allshould stay message-based here as well.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 3024 - 3070, The Gemini branch currently skips message-level scanning when state.roleFilter === 'all' and instead token-matches the raw JSON blob; fix it by reusing the message-parsing loop used in the state.roleFilter !== 'all' branch: parse the file into json/messages, iterate rawMessages, skip invalid entries, derive role via normalizeGeminiMessageRole and text via extractMessageText(extractGeminiMessageText(...)), then call consumeSessionQueryMessage(state, { role, text }) for each message (and break when it returns true); preserve snippetLimit behavior (push truncateText(text) when appropriate) and only fall back to raw matchTokensInText if message parsing fails or json.messages is not an array; return buildSessionQueryScanResult(state) as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@cli.js`:
- Around line 6202-6298: The CLI never reaches exportSessionData's "gemini"
branch because parseExportSessionArgs and printExportSessionUsage still only
accept/display "codex|claude"; update both to include "gemini". In
parseExportSessionArgs (the arg parsing/validation function) add "gemini" as an
allowed source value and ensure any switch/conditional that maps params.source
to internal source strings handles "gemini" the same way as "codex"/"claude". In
printExportSessionUsage (the help/usage printer) add "gemini" to the list of
supported sources shown to users so the CLI will accept and advertise the new
option. Ensure the same canonical label ("gemini") is used so
exportSessionData’s source === 'gemini' branch is reachable.
- Around line 9181-9196: The CLI case 'list-sessions-usage' currently accepts
"gemini" as a valid source; change the validation logic to only allow "codex",
"claude", or "all" (remove "gemini" from the allowed set and error text) so the
API doesn't advertise Gemini before the UI supports it; update the error message
in the result to reflect the allowed values and leave the call to
listSessionUsage({ ...usageParams, source: source || 'all' }) unchanged so
behavior stays the same for codex/claude/all.
- Around line 3393-3406: The cache key normalization is collapsing every
non-`claude` source into `codex`, causing Gemini to read Codex entries; update
the logic so the cache and lookup maps use the actual normalized source
(preserving 'gemini' and 'codex') instead of always folding to 'codex' — adjust
the normalization and callers of buildSessionInventoryCacheKey,
getSessionInventoryCache, setSessionInventoryCache, and
registerSessionFileLookupEntries to pass/use the true normalizedSource (e.g.,
keep normalizedSource = source === 'claude' || source === 'gemini' ? source :
'codex' and ensure buildSessionInventoryCacheKey accepts/uses that value rather
than re-folding), and confirm listGeminiSessions populates the Gemini-specific
lookup map via registerSessionFileLookupEntries.
- Around line 5451-5500: The trash entry is being mislabeled because
buildSessionTrashEntry collapses any non-claude source to "codex", so when
trashing a Gemini session the entry.source becomes "codex" and restore
validation fails; change buildSessionTrashEntry (and any helper it uses) to
accept and preserve the original source string (e.g., "gemini") instead of
coercing to "codex" so callers like trashSessionData (where
buildSessionTrashEntry is called) store entry.source = source; after that,
ensure resolveSessionRestoreTarget's validation logic recognizes "gemini" roots
(or maps the preserved "gemini" entry back to the correct Gemini root) so
restore checks succeed.
In `@README.md`:
- Around line 32-60: Update the duplicated session documentation to mention
Gemini wherever Codex and Claude are listed: find the "Sessions Mode" and
"export-session" sections and add Gemini to the tool lists and examples so they
match the earlier "Session Management" bullets (e.g., change phrases like "Codex
+ Claude only" to "Codex + Claude + Gemini" and update any example commands or
descriptions that enumerate supported providers to include Gemini). Ensure the
wording, command examples, and any usage/analytics descriptions mirror the
earlier section that already includes Gemini.
- Around line 166-181: Update the section heading "Install Codex CLI / Claude
Code CLI (optional)" to reflect all included CLIs (Codex, Claude Code, Gemini,
etc.) so the title matches the commands; locate the heading string "Install
Codex CLI / Claude Code CLI (optional)" and change it to something inclusive
(e.g., "Install Codex, Claude Code, Gemini CLI (optional)"), ensuring it
corresponds to the listed command `npm install -g `@google/gemini-cli`` in the
block.
In `@README.zh.md`:
- Around line 32-61: Documentation inconsistency: the earlier "会话管理" section
includes Gemini but the later duplicated "会话模式" and "export-session" sections
only mention Codex + Claude; update those repeated sections ("会话模式" and
"export-session") to include Gemini (and any Gemini-specific notes or CLI flags)
so the document is consistent with "会话管理" and other references to Gemini in
README.zh.md.
---
Duplicate comments:
In `@cli.js`:
- Around line 4135-4145: The returned summary is setting cwd to the temporary
path (cwd variable via getGeminiTmpDir() + projectHash) which causes downstream
readers to index the temp bucket instead of the real project path; change the
summary to prefer the real workspace path when available (use json.projectRoot
or json.cwd if present) before falling back to the synthesized tmp path. Locate
the object returned in the function that builds the Gemini summary (the cwd
variable and the return block with source:'gemini') and make cwd resolve to
json.projectRoot || json.cwd || (projectHash ? path.join(getGeminiTmpDir(),
projectHash) : '') so the real project root is used when present.
- Around line 4148-4155: The code is overwriting real session summary counts
with zeros (messageCount, totalTokens, inputTokens, etc.) causing Gemini rows
from listSessionBrowse() to display messageCount: 0; instead, stop hardcoding
zeros and copy the corresponding values from the incoming summary object
returned by listSessionBrowse() (e.g., use sessionSummary.messageCount,
sessionSummary.totalTokens, sessionSummary.inputTokens,
sessionSummary.outputTokens, sessionSummary.reasoningOutputTokens) and set
__messageCountExact to the correct boolean from the summary (e.g.,
sessionSummary.__messageCountExact) so Gemini sessions show their actual counts.
- Around line 3024-3070: The Gemini branch currently skips message-level
scanning when state.roleFilter === 'all' and instead token-matches the raw JSON
blob; fix it by reusing the message-parsing loop used in the state.roleFilter
!== 'all' branch: parse the file into json/messages, iterate rawMessages, skip
invalid entries, derive role via normalizeGeminiMessageRole and text via
extractMessageText(extractGeminiMessageText(...)), then call
consumeSessionQueryMessage(state, { role, text }) for each message (and break
when it returns true); preserve snippetLimit behavior (push truncateText(text)
when appropriate) and only fall back to raw matchTokensInText if message parsing
fails or json.messages is not an array; return
buildSessionQueryScanResult(state) as before.
In `@web-ui/styles/controls-forms.css`:
- Around line 17-23: The .selector-header rule currently forces flex-wrap:
nowrap which prevents localized labels and .session-actions from wrapping on
narrow viewports; add a compact-width escape hatch via a small-screen media
query (e.g., max-width ~480px) that overrides .selector-header (and the related
.session-actions styles around lines 92-115) to set flex-wrap: wrap and relax
alignment/gap so actions can reflow (optionally set align-items and gap to more
vertical-friendly values) on small screens.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6b593e0f-f72c-485c-b837-8b3ce7b6ce32
📒 Files selected for processing (9)
README.mdREADME.zh.mdcli.jssite/guide/getting-started.mdsite/index.mdtests/unit/run.mjstests/unit/session-header-actions-layout.test.mjsweb-ui/modules/app.methods.install.mjsweb-ui/styles/controls-forms.css
✅ Files skipped from review due to trivial changes (3)
- tests/unit/run.mjs
- site/index.md
- site/guide/getting-started.md
📜 Review details
🧰 Additional context used
🪛 LanguageTool
README.md
[grammar] ~32-~32: Ensure spelling is correct
Context: ...ter/export/delete) with Usage analytics overview - Plugins (Prompt templates): reusable te...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[grammar] ~60-~60: Ensure spelling is correct
Context: ...ied Codex + Claude + Gemini CLI session list - Local session pinning with persistent p...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🔇 Additional comments (3)
tests/unit/session-header-actions-layout.test.mjs (1)
1-75: Looks good.This is a focused contract test for the new sessions header layout rules, and the isolation check is a useful safeguard.
README.zh.md (1)
169-184: LGTM.安装说明和 Gemini CLI 的新增命令是对齐的,这一段文档更新是完整的。
web-ui/modules/app.methods.install.mjs (1)
101-153: Gemini install commands are wired through consistently.The new
geminitarget follows the existing package-manager matrix and registry handling without changing the public API.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
cli.js (2)
5781-5796:⚠️ Potential issue | 🔴 CriticalTrash entries still downcast new sources to Codex.
trashSessionData()now passesgemini/codebuddyhere, butbuildSessionTrashEntry()still normalizes every non-claudesource tocodex. Those trash entries will be listed under the wrong source, and restore will resolve against the Codex root instead of the original Gemini/CodeBuddy location.Suggested fix
function buildSessionTrashEntry(summary, options = {}) { - const source = options.source === 'claude' ? 'claude' : 'codex'; + const source = options.source === 'claude' + ? 'claude' + : (options.source === 'gemini' + ? 'gemini' + : (options.source === 'codebuddy' ? 'codebuddy' : 'codex')); const sessionId = options.sessionId || summary.sessionId || path.basename(options.originalFilePath || summary.filePath || '', '.jsonl'); @@ - const sourceLabel = source === 'claude' ? 'Claude Code' : 'Codex'; + const sourceLabel = source === 'claude' + ? 'Claude Code' + : (source === 'gemini' ? 'Gemini CLI' : (source === 'codebuddy' ? 'CodeBuddy Code' : 'Codex')); @@ - provider: (claudeIndexEntry && typeof claudeIndexEntry.provider === 'string' && claudeIndexEntry.provider.trim()) + provider: (claudeIndexEntry && typeof claudeIndexEntry.provider === 'string' && claudeIndexEntry.provider.trim()) ? claudeIndexEntry.provider.trim() - : (summary.provider || (source === 'claude' ? 'claude' : 'codex')), + : (summary.provider || (source === 'claude' + ? 'claude' + : (source === 'gemini' ? 'gemini' : (source === 'codebuddy' ? 'codebuddy' : 'codex')))),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 5781 - 5796, The trash entries are being normalized to 'codex' incorrectly; update the normalization so that buildSessionTrashEntry preserves the original source values (e.g., 'gemini' or 'codebuddy') instead of downcasting every non-'claude' to 'codex'. Modify buildSessionTrashEntry (and any helper normalizer used by it) to accept and store the source verbatim from the caller (trashSessionData passes source) and ensure restore logic uses that stored source when resolving the root location; remove or change any conditional that maps non-'claude' to 'codex' so the original source field is retained in the trash entry.
3441-3459:⚠️ Potential issue | 🔴 CriticalFix inventory cache key collisions for Gemini/CodeBuddy.
listSessionInventoryBySource()now sendsgeminiandcodebuddythrough the shared inventory cache, butbuildSessionInventoryCacheKey()still normalizes every non-claudesource tocodex. After one source warms the cache, later lookups can return the wrong session set until invalidation.Suggested fix
-function buildSessionInventoryCacheKey(source, limit, options = {}) { - const normalizedSource = source === 'claude' ? 'claude' : 'codex'; +function buildSessionInventoryCacheKey(source, limit, options = {}) { + const normalizedSource = + source === 'claude' || source === 'gemini' || source === 'codebuddy' + ? source + : 'codex';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 3441 - 3459, listSessionInventoryBySource normalizes non-claude sources to 'codex' but then calls buildSessionInventoryCacheKey which still treats non-claude as 'codex', causing gemini/codebuddy cache collisions; fix by creating the cache key from the actual normalizedSource (preserve the real source string like 'gemini' or 'codebuddy') or pass the original source into buildSessionInventoryCacheKey so getSessionInventoryCache and setSessionInventoryCache use a key that includes the true source; update calls in listSessionInventoryBySource to use the corrected key generation and ensure getSessionInventoryCache/setSessionInventoryCache are invoked with that same key.
♻️ Duplicate comments (4)
web-ui/modules/i18n.dict.mjs (1)
1559-1565:⚠️ Potential issue | 🟡 MinorUpdate English query placeholder to include Gemini/CodeBuddy.
Line 1564 still says
Codex/Claudeeven though the enabled sources now include Gemini and CodeBuddy, so EN guidance is outdated.🌐 Suggested i18n fix
- 'sessions.query.placeholder.enabled': 'Search keywords (Codex/Claude, e.g. claude code)', + 'sessions.query.placeholder.enabled': 'Search keywords (Codex/Claude/Gemini/CodeBuddy, e.g. claude code)',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web-ui/modules/i18n.dict.mjs` around lines 1559 - 1565, Update the English i18n entry 'sessions.query.placeholder.enabled' to replace the outdated "Codex/Claude" example with the current sources (e.g., "Gemini/CodeBuddy") so the placeholder reflects available sources; locate the key 'sessions.query.placeholder.enabled' in web-ui/modules/i18n.dict.mjs and change its string to include "Gemini/CodeBuddy" (also consider updating 'sessions.query.placeholder.disabled' if its guidance needs to mention the new sources).cli.js (3)
3071-3117:⚠️ Potential issue | 🟠 MajorKeep Gemini content scans message-based.
When
roleFilter === 'all', this branch matches against raw serialized JSON and only the first 512 KB. Queries can hit metadata likeprojectRoot/modeland miss later messages, soqueryScope=content/allbecomes inaccurate for Gemini.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 3071 - 3117, The Gemini branch currently falls back to scanning raw file text (first ~512KB) when state.roleFilter === 'all', which can match metadata instead of actual messages; update the logic so Gemini sessions are always scanned message-by-message: parse the JSON file (as done in the roleFilter !== 'all' branch), iterate rawMessages and for each entry use normalizeGeminiMessageRole and extractGeminiMessageText and call consumeSessionQueryMessage to drive state (breaking early on match), then return buildSessionQueryScanResult(state); only if JSON parsing fails should you fall back to the byte-read/raw-text token check using matchTokensInText. Ensure you reuse the same symbols (session.source, state.roleFilter, normalizeGeminiMessageRole, extractGeminiMessageText, consumeSessionQueryMessage, matchTokensInText, buildSessionQueryScanResult) so behavior is consistent.
4346-4356:⚠️ Potential issue | 🟠 MajorUse the real workspace path in Gemini summaries.
cwdis still synthesized as~/.gemini/tmp/<project_hash>. The detail/plain/export readers already preferjson.projectRoot/json.cwd, so path filtering andlist-session-pathswill index the temp hash path instead of the actual project path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 4346 - 4356, The returned object currently sets cwd to the synthesized temp path (using projectHash and getGeminiTmpDir()), which causes readers to index the temp hash path; change the logic so the returned cwd uses the real workspace path (e.g., projectRoot or json.cwd if present) and only fall back to the temp path when the real project root is unavailable. Update the code that defines cwd (and the returned object property cwd) to prefer projectRoot/json.cwd first, then projectHash-based getGeminiTmpDir() as a fallback so list-session-paths and detail/plain/export readers see the real project path.
4359-4366:⚠️ Potential issue | 🟠 MajorGemini browse rows still report
messageCount: 0.This summary still hardcodes
messageCountto zero with__messageCountExact: false.listSessionBrowse()returns these summaries directly, so Gemini sessions stay at zero in the browser list even though exact counting exists elsewhere.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli.js` around lines 4359 - 4366, The summary object currently hardcodes messageCount: 0 and __messageCountExact: false; update the code that constructs the browse summary (the object returned by listSessionBrowse / the summary creation block) to use the real message count and mark it exact — e.g., set messageCount to the actual count (from the session summary payload or messages.length) and set __messageCountExact: true (and remove any unconditional zeroing of totalTokens/inputTokens if real values are available). Locate the summary construction near listSessionBrowse and replace the hardcoded zeros/flags with the corresponding fields from the session summary response.
🧹 Nitpick comments (2)
web-ui/modules/app.methods.install.mjs (1)
101-167: Consider generating CLI command rows from a package map to reduce drift risk.The new targets work, but the matrix now has high repetition across package managers. A small data-driven builder will reduce copy/paste errors as more CLIs are added.
♻️ Suggested refactor
+ const targetPackages = { + claude: '@anthropic-ai/claude-code', + codebuddy: '@tencent-ai/codebuddy-code', + gemini: '@google/gemini-cli', + codex: codexPackage + }; const matrix = { claude: { install: '', update: '', uninstall: '' }, codebuddy: { install: '', update: '', uninstall: '' }, gemini: { install: '', update: '', uninstall: '' }, codex: { install: '', update: '', uninstall: '' } }; + const apply = (pm, installVerb, updateVerb, removeVerb) => { + Object.entries(targetPackages).forEach(([target, pkg]) => { + const installPkg = target === 'codex' ? codexInstallPackage : pkg; + matrix[target].install = `${pm} ${installVerb} -g ${installPkg}`; + matrix[target].update = `${pm} ${updateVerb} -g ${pkg}`; + matrix[target].uninstall = `${pm} ${removeVerb} -g ${pkg}`; + }); + };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web-ui/modules/app.methods.install.mjs` around lines 101 - 167, The matrix has repetitive hard-coded command strings; replace it with a data-driven builder: define a package map (e.g., packages = { claude: '@anthropic-ai/claude-code', codebuddy: '@tencent-ai/codebuddy-code', gemini: '@google/gemini-cli', codex: codexPackage }) and a manager-templates map (e.g., templates for 'pnpm','bun','npm' with install/update/uninstall patterns using placeholders), then iterate over Object.keys(packages) to populate matrix[target].install/update/uninstall by plugging packages[target] (or codexInstallPackage for codex.install) into the selected manager templates based on the manager variable; preserve the special case for platform === 'termux' when computing npm update for codex. Use the existing symbols matrix, manager, platform, codexPackage, and codexInstallPackage to locate and replace the current hard-coded blocks.lib/cli-sessions.js (1)
170-171: Use nullish fallback when selecting CodeBuddy content.If
record.messageexists butrecord.message.contentis empty, current logic skips a validrecord.contentfallback.🛠️ Suggested adjustment
- const content = record.message ? record.message.content : record.content; + const content = record?.message?.content ?? record.content;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/cli-sessions.js` around lines 170 - 171, The current selection uses record.message ? record.message.content : record.content which treats an empty/undefined message.content incorrectly; update the logic where content is derived (the lines that set content and call extractMessageText) to prefer record.message.content when it is present and non-null/undefined and otherwise fall back to record.content (i.e., use a nullish fallback for record.message.content), then pass that resulting content into extractMessageText to compute text.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cli.js`:
- Around line 8409-8414: The CLI help lists "gemini" and "codebuddy" as valid
--source values but parseExportSessionArgs() still only validates/handles
"codex" and "claude", causing a validation error; update
parseExportSessionArgs() so its allowedSources/validation logic includes
"gemini" and "codebuddy" and extend any downstream switch/handler that branches
on the source (the same place that treats "codex"/"claude") to handle "gemini"
and "codebuddy" or, alternatively, change the usage text to only list the
sources currently supported—ensure the change is applied around the
parseExportSessionArgs function and any source-specific export handling
functions referenced there.
In `@README.md`:
- Line 60: README has inconsistent "session-source" scope after adding Gemini
and CodeBuddy; update all relevant sections (the top-line list, "Sessions Mode",
and export/source examples) to reflect unified support for Codex, Claude,
Gemini, and CodeBuddy. Search for section headings "Sessions Mode" and any
examples showing "export source" or provider-specific examples and replace
provider-specific language or filters with neutral or inclusive wording (e.g.,
"supported providers: Codex, Claude, Gemini, CodeBuddy" or remove hard-coded
provider filters), and ensure examples/CLI flags match the new unified
session-source semantics throughout the README so all sections are consistent.
---
Outside diff comments:
In `@cli.js`:
- Around line 5781-5796: The trash entries are being normalized to 'codex'
incorrectly; update the normalization so that buildSessionTrashEntry preserves
the original source values (e.g., 'gemini' or 'codebuddy') instead of
downcasting every non-'claude' to 'codex'. Modify buildSessionTrashEntry (and
any helper normalizer used by it) to accept and store the source verbatim from
the caller (trashSessionData passes source) and ensure restore logic uses that
stored source when resolving the root location; remove or change any conditional
that maps non-'claude' to 'codex' so the original source field is retained in
the trash entry.
- Around line 3441-3459: listSessionInventoryBySource normalizes non-claude
sources to 'codex' but then calls buildSessionInventoryCacheKey which still
treats non-claude as 'codex', causing gemini/codebuddy cache collisions; fix by
creating the cache key from the actual normalizedSource (preserve the real
source string like 'gemini' or 'codebuddy') or pass the original source into
buildSessionInventoryCacheKey so getSessionInventoryCache and
setSessionInventoryCache use a key that includes the true source; update calls
in listSessionInventoryBySource to use the corrected key generation and ensure
getSessionInventoryCache/setSessionInventoryCache are invoked with that same
key.
---
Duplicate comments:
In `@cli.js`:
- Around line 3071-3117: The Gemini branch currently falls back to scanning raw
file text (first ~512KB) when state.roleFilter === 'all', which can match
metadata instead of actual messages; update the logic so Gemini sessions are
always scanned message-by-message: parse the JSON file (as done in the
roleFilter !== 'all' branch), iterate rawMessages and for each entry use
normalizeGeminiMessageRole and extractGeminiMessageText and call
consumeSessionQueryMessage to drive state (breaking early on match), then return
buildSessionQueryScanResult(state); only if JSON parsing fails should you fall
back to the byte-read/raw-text token check using matchTokensInText. Ensure you
reuse the same symbols (session.source, state.roleFilter,
normalizeGeminiMessageRole, extractGeminiMessageText,
consumeSessionQueryMessage, matchTokensInText, buildSessionQueryScanResult) so
behavior is consistent.
- Around line 4346-4356: The returned object currently sets cwd to the
synthesized temp path (using projectHash and getGeminiTmpDir()), which causes
readers to index the temp hash path; change the logic so the returned cwd uses
the real workspace path (e.g., projectRoot or json.cwd if present) and only fall
back to the temp path when the real project root is unavailable. Update the code
that defines cwd (and the returned object property cwd) to prefer
projectRoot/json.cwd first, then projectHash-based getGeminiTmpDir() as a
fallback so list-session-paths and detail/plain/export readers see the real
project path.
- Around line 4359-4366: The summary object currently hardcodes messageCount: 0
and __messageCountExact: false; update the code that constructs the browse
summary (the object returned by listSessionBrowse / the summary creation block)
to use the real message count and mark it exact — e.g., set messageCount to the
actual count (from the session summary payload or messages.length) and set
__messageCountExact: true (and remove any unconditional zeroing of
totalTokens/inputTokens if real values are available). Locate the summary
construction near listSessionBrowse and replace the hardcoded zeros/flags with
the corresponding fields from the session summary response.
In `@web-ui/modules/i18n.dict.mjs`:
- Around line 1559-1565: Update the English i18n entry
'sessions.query.placeholder.enabled' to replace the outdated "Codex/Claude"
example with the current sources (e.g., "Gemini/CodeBuddy") so the placeholder
reflects available sources; locate the key 'sessions.query.placeholder.enabled'
in web-ui/modules/i18n.dict.mjs and change its string to include
"Gemini/CodeBuddy" (also consider updating 'sessions.query.placeholder.disabled'
if its guidance needs to mention the new sources).
---
Nitpick comments:
In `@lib/cli-sessions.js`:
- Around line 170-171: The current selection uses record.message ?
record.message.content : record.content which treats an empty/undefined
message.content incorrectly; update the logic where content is derived (the
lines that set content and call extractMessageText) to prefer
record.message.content when it is present and non-null/undefined and otherwise
fall back to record.content (i.e., use a nullish fallback for
record.message.content), then pass that resulting content into
extractMessageText to compute text.
In `@web-ui/modules/app.methods.install.mjs`:
- Around line 101-167: The matrix has repetitive hard-coded command strings;
replace it with a data-driven builder: define a package map (e.g., packages = {
claude: '@anthropic-ai/claude-code', codebuddy: '@tencent-ai/codebuddy-code',
gemini: '@google/gemini-cli', codex: codexPackage }) and a manager-templates map
(e.g., templates for 'pnpm','bun','npm' with install/update/uninstall patterns
using placeholders), then iterate over Object.keys(packages) to populate
matrix[target].install/update/uninstall by plugging packages[target] (or
codexInstallPackage for codex.install) into the selected manager templates based
on the manager variable; preserve the special case for platform === 'termux'
when computing npm update for codex. Use the existing symbols matrix, manager,
platform, codexPackage, and codexInstallPackage to locate and replace the
current hard-coded blocks.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6532094b-f886-40a5-9732-7e1330eb7e59
📒 Files selected for processing (14)
README.mdREADME.zh.mdcli.jscli/session-usage.jslib/cli-sessions.jssite/guide/getting-started.mdsite/index.mdtests/unit/session-usage-backend.test.mjsweb-ui/logic.sessions.mjsweb-ui/modules/app.computed.dashboard.mjsweb-ui/modules/app.methods.install.mjsweb-ui/modules/i18n.dict.mjsweb-ui/partials/index/layout-header.htmlweb-ui/partials/index/panel-sessions.html
✅ Files skipped from review due to trivial changes (2)
- site/guide/getting-started.md
- README.zh.md
🚧 Files skipped from review as they are similar to previous changes (5)
- web-ui/modules/app.computed.dashboard.mjs
- site/index.md
- web-ui/partials/index/layout-header.html
- cli/session-usage.js
- web-ui/partials/index/panel-sessions.html
📜 Review details
🧰 Additional context used
🪛 LanguageTool
README.md
[grammar] ~32-~32: Ensure spelling is correct
Context: ...ter/export/delete) with Usage analytics overview - Plugins (Prompt templates): reusable te...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[grammar] ~60-~60: Ensure spelling is correct
Context: ...e + Gemini CLI + CodeBuddy Code session list - Local session pinning with persisten...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🔇 Additional comments (2)
web-ui/logic.sessions.mjs (1)
30-40: Good source normalization/query-gating update for new providers.Line 32 and Line 39 correctly keep source normalization and query eligibility in sync for
geminiandcodebuddy.tests/unit/session-usage-backend.test.mjs (1)
59-64: Good guardrails for newly wired source parsers.These default throwing stubs are a solid safety net for this test harness and make unexpected gemini/codebuddy fallback parsing fail fast while still allowing explicit overrides via bindings.
| console.log('\n用法: codexmate export-session --source <codex|claude|gemini|codebuddy> (--session-id <ID>|--file <PATH>) [--output <PATH>] [--max-messages <N|all|Infinity>]'); | ||
| console.log('\n示例:'); | ||
| console.log(' codexmate export-session --source codex --session-id 123456'); | ||
| console.log(' codexmate export-session --source claude --file "~/.claude/projects/demo/session.jsonl"'); | ||
| console.log(' codexmate export-session --source codebuddy --file "~/.codebuddy/projects/demo/session.jsonl"'); | ||
| console.log(' codexmate export-session --source codex --session-id 123456 --max-messages=all'); |
There was a problem hiding this comment.
CLI help advertises sources the parser still rejects.
The updated usage text says export-session --source gemini|codebuddy, but parseExportSessionArgs() still only accepts codex or claude. From the CLI, the new sources exit with a validation error instead of exporting.
Suggested fix
- if (normalizedSource && normalizedSource !== 'codex' && normalizedSource !== 'claude') {
- errors.push('参数 --source 仅支持 codex 或 claude');
+ if (
+ normalizedSource
+ && normalizedSource !== 'codex'
+ && normalizedSource !== 'claude'
+ && normalizedSource !== 'gemini'
+ && normalizedSource !== 'codebuddy'
+ ) {
+ errors.push('参数 --source 仅支持 codex、claude、gemini 或 codebuddy');
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cli.js` around lines 8409 - 8414, The CLI help lists "gemini" and "codebuddy"
as valid --source values but parseExportSessionArgs() still only
validates/handles "codex" and "claude", causing a validation error; update
parseExportSessionArgs() so its allowedSources/validation logic includes
"gemini" and "codebuddy" and extend any downstream switch/handler that branches
on the source (the same place that treats "codex"/"claude") to handle "gemini"
and "codebuddy" or, alternatively, change the usage text to only list the
sources currently supported—ensure the change is applied around the
parseExportSessionArgs function and any source-specific export handling
functions referenced there.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
web-ui/partials/index/layout-header.html (1)
217-217:⚠️ Potential issue | 🟡 MinorUse the same i18n mapping here too.
This branch still hardcodes English labels, while the main status strip already uses
t('sessions.source.*'). That leaves the sidebar out of sync for localized builds.Suggested fix
- <span>{{ t('sessions.sourceLabel', { value: (sessionFilterSource === 'all' ? t('sessions.source.all') : (sessionFilterSource === 'claude' ? 'Claude Code' : (sessionFilterSource === 'gemini' ? 'Gemini CLI' : (sessionFilterSource === 'codebuddy' ? 'CodeBuddy Code' : 'Codex')))) }) }}</span> + <span>{{ t('sessions.sourceLabel', { value: (sessionFilterSource === 'all' ? t('sessions.source.all') : (sessionFilterSource === 'claude' ? t('sessions.source.claudeCode') : (sessionFilterSource === 'gemini' ? t('sessions.source.gemini') : (sessionFilterSource === 'codebuddy' ? t('sessions.source.codebuddy') : t('sessions.source.codex'))))) }) }}</span>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web-ui/partials/index/layout-header.html` at line 217, The session source label is hardcoded to English; update the template expression that builds the span text to use the same i18n keys as the status strip (use t('sessions.source.claude'), t('sessions.source.gemini'), t('sessions.source.codebuddy'), t('sessions.source.codex')) instead of literal strings, keeping the outer call t('sessions.sourceLabel', { value: ... }) and the sessionFilterSource variable used to select the correct key so localized builds show the same translations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@web-ui/partials/index/layout-header.html`:
- Line 217: The session source label is hardcoded to English; update the
template expression that builds the span text to use the same i18n keys as the
status strip (use t('sessions.source.claude'), t('sessions.source.gemini'),
t('sessions.source.codebuddy'), t('sessions.source.codex')) instead of literal
strings, keeping the outer call t('sessions.sourceLabel', { value: ... }) and
the sessionFilterSource variable used to select the correct key so localized
builds show the same translations.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8d928817-36c1-406b-865c-d385b3ae9a6a
📒 Files selected for processing (2)
web-ui/modules/i18n.dict.mjsweb-ui/partials/index/layout-header.html
🚧 Files skipped from review as they are similar to previous changes (1)
- web-ui/modules/i18n.dict.mjs
📜 Review details
🔇 Additional comments (1)
web-ui/partials/index/layout-header.html (1)
372-382: LGTM — the current source chip is now localized.The status-strip branch uses the existing source translation keys for all known values and keeps a sensible fallback to Codex.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
tests/unit/session-resume-command.test.mjs (1)
21-47: Add one edge-case assertion for shell-quoting safety.Current cases only use simple IDs. Adding a
sessionIdwith spaces/quotes would lock in the quoting contract for new providers too.Example assertion to add
+ assert.strictEqual( + methods.buildResumeCommand.call({ ...contextBase, sessionResumeWithYolo: true }, { source: 'gemini', sessionId: "gm ' 1" }), + "gemini -r 'gm '\\'' 1'" + );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/unit/session-resume-command.test.mjs` around lines 21 - 47, Add an edge-case assertion to tests for buildResumeCommand to ensure shell-quoting safety: call methods.buildResumeCommand (from createSessionActionMethods) with a context (e.g., contextBase or modified) and a sessionId that contains spaces and/or quotes (e.g., 'id with space' or 'id"with\'quotes') and assert the returned command correctly quotes or escapes the sessionId for the given source(s) (codex, codebuddy, gemini) so quoting behavior is explicit and protected for new providers; update the existing test block around methods.buildResumeCommand usage to include this extra assertion.web-ui/modules/app.methods.session-actions.mjs (1)
141-149: Consider reusing shared source normalization to avoid drift.This method normalizes
sourceinline; using the shared normalizer fromweb-ui/logic.sessions.mjs:35-43would keep source handling consistent as providers evolve.Suggested refactor
- const source = session && session.source ? String(session.source).trim().toLowerCase() : ''; + const source = normalizeSessionSource(session && session.source ? session.source : '', ''); const sessionId = session && session.sessionId ? String(session.sessionId).trim() : ''; const arg = this.quoteResumeArg(sessionId); if (source === 'codebuddy') { return `codebuddy -r ${arg}`; } if (source === 'gemini') { return `gemini -r ${arg}`; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web-ui/modules/app.methods.session-actions.mjs` around lines 141 - 149, The inline normalization of source should be replaced with the shared normalizer exported by logic.sessions.mjs (use the module's exported function for source normalization) instead of repeating String(session.source).trim().toLowerCase(); import that normalizer into app.methods.session-actions.mjs, call it to compute source, then keep the existing sessionId conversion and the call to this.quoteResumeArg(sessionId) and the conditional branches that return `codebuddy -r ${arg}` / `gemini -r ${arg}` unchanged; this ensures consistent provider handling as providers evolve.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/unit/session-resume-command.test.mjs`:
- Around line 21-47: Add an edge-case assertion to tests for buildResumeCommand
to ensure shell-quoting safety: call methods.buildResumeCommand (from
createSessionActionMethods) with a context (e.g., contextBase or modified) and a
sessionId that contains spaces and/or quotes (e.g., 'id with space' or
'id"with\'quotes') and assert the returned command correctly quotes or escapes
the sessionId for the given source(s) (codex, codebuddy, gemini) so quoting
behavior is explicit and protected for new providers; update the existing test
block around methods.buildResumeCommand usage to include this extra assertion.
In `@web-ui/modules/app.methods.session-actions.mjs`:
- Around line 141-149: The inline normalization of source should be replaced
with the shared normalizer exported by logic.sessions.mjs (use the module's
exported function for source normalization) instead of repeating
String(session.source).trim().toLowerCase(); import that normalizer into
app.methods.session-actions.mjs, call it to compute source, then keep the
existing sessionId conversion and the call to this.quoteResumeArg(sessionId) and
the conditional branches that return `codebuddy -r ${arg}` / `gemini -r ${arg}`
unchanged; this ensures consistent provider handling as providers evolve.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 485d300c-6474-4395-9c42-526eac276804
📒 Files selected for processing (2)
tests/unit/session-resume-command.test.mjsweb-ui/modules/app.methods.session-actions.mjs
📜 Review details
🔇 Additional comments (2)
web-ui/modules/app.methods.session-actions.mjs (1)
120-120: Good guard for resume command availability.The source allowlist + non-empty
sessionIdcheck is correct and prevents invalid resume actions in the UI flow.tests/unit/session-resume-command.test.mjs (1)
12-19: Nice coverage for multi-source resume availability.This validates the key gating logic for
codex/codebuddy/geminiand empty-session rejection.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
README.md (1)
276-282:⚠️ Potential issue | 🟡 MinorUpdate “Sessions Mode” wording to include Gemini CLI + CodeBuddy Code.
Right now the “Session Management” section describes a unified Codex/Claude/Gemini CLI/CodeBuddy Code session experience, but “Web UI → Sessions Mode” still says only “Unified Codex + Claude sessions”, which is likely to mislead users. Consider updating that block to match the unified provider list.
🛠️ Proposed fix
### Sessions Mode -- Unified Codex + Claude sessions +- Unified Codex + Claude + Gemini CLI + CodeBuddy Code sessions - Browser / Usage subview switching - Local pin/unpin with persistent storage and pinned-first ordering - Search, filter, export, delete, batch cleanup - Usage view includes 7d / 30d session trends, message trends, source share, and top paths🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@README.md` around lines 276 - 282, Update the "### Sessions Mode" block so its first bullet reflects the full unified provider list instead of only "Unified Codex + Claude sessions"; replace that bullet with a phrase that explicitly includes Gemini CLI and CodeBuddy Code (e.g., "Unified Codex, Claude, Gemini CLI, and CodeBuddy Code sessions") and ensure any related bullets or wording in the same "Sessions Mode" section remain consistent with the Session Management description.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@README.md`:
- Around line 59-73: Update the "Copy resume command" README entry to reflect
that Codex supports an optional --yolo flag: change the Codex example `codex
resume <sessionId>` to include the optional flag (e.g., `codex resume
<sessionId> [--yolo]` or `codex resume <sessionId> (optionally --yolo)`), while
leaving the Gemini and CodeBuddy examples (`gemini -r <sessionId>`, `codebuddy
-r <sessionId>`) unchanged so it’s clear only `codex resume` accepts `--yolo`.
- Around line 32-33: The README has a malformed concatenated bullet where the
sessions and Plugins lines run together; edit the two list items so they are
separate Markdown bullets: ensure the line "- Local Codex/Claude/Gemini
CLI/CodeBuddy Code sessions (list/filter/export/delete) with Usage analytics
overview" is on its own line, followed by a newline and the separate line "-
Plugins (Prompt templates): reusable templates with variables and one-click
copy", removing any duplicated fragment or trailing text that merged the two
entries.
---
Outside diff comments:
In `@README.md`:
- Around line 276-282: Update the "### Sessions Mode" block so its first bullet
reflects the full unified provider list instead of only "Unified Codex + Claude
sessions"; replace that bullet with a phrase that explicitly includes Gemini CLI
and CodeBuddy Code (e.g., "Unified Codex, Claude, Gemini CLI, and CodeBuddy Code
sessions") and ensure any related bullets or wording in the same "Sessions Mode"
section remain consistent with the Session Management description.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e6fcf13c-b385-4869-993b-9e7792418dfa
📒 Files selected for processing (2)
README.mdREADME.zh.md
🚧 Files skipped from review as they are similar to previous changes (1)
- README.zh.md
📜 Review details
🧰 Additional context used
🪛 LanguageTool
README.md
[grammar] ~32-~32: Ensure spelling is correct
Context: ...ter/export/delete) with Usage analytics overview - Plugins (Prompt templates): reusable te...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🔇 Additional comments (1)
README.md (1)
71-71: No issues found. The README correctly documents theexport-sessioncommand flags. Verification confirms--session-idand--filematch the CLI implementation incli.js(lines 8409–8414, 8440–8444), where both flags are explicitly parsed and used in examples.
| - Local Codex/Claude/Gemini CLI/CodeBuddy Code sessions (list/filter/export/delete) with Usage analytics overview | ||
| - Plugins (Prompt templates): reusable templates with variables and one-click copy |
There was a problem hiding this comment.
Fix malformed bullet concatenation around the session/Plugins bullets.
At around line ~32, the sessions bullet appears to include trailing text like with Usage analytics overview - Plugins ... in the same line, which suggests a missing newline or a duplicated fragment. This is consistent with the static-analysis orthography/format hint and can easily render incorrectly in Markdown.
🛠️ Proposed fix
- - Local Codex/Claude/Gemini CLI/CodeBuddy Code sessions (list/filter/export/delete) with Usage analytics overview - Plugins (Prompt templates): reusable templates with variables and one-click copy
+ - Local Codex/Claude/Gemini CLI/CodeBuddy Code sessions (list/filter/export/delete) with Usage analytics overview🧰 Tools
🪛 LanguageTool
[grammar] ~32-~32: Ensure spelling is correct
Context: ...ter/export/delete) with Usage analytics overview - Plugins (Prompt templates): reusable te...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[grammar] ~33-~33: Ensure spelling is correct
Context: ... templates with variables and one-click copy - Task orchestration: plan/queue/run/revi...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@README.md` around lines 32 - 33, The README has a malformed concatenated
bullet where the sessions and Plugins lines run together; edit the two list
items so they are separate Markdown bullets: ensure the line "- Local
Codex/Claude/Gemini CLI/CodeBuddy Code sessions (list/filter/export/delete) with
Usage analytics overview" is on its own line, followed by a newline and the
separate line "- Plugins (Prompt templates): reusable templates with variables
and one-click copy", removing any duplicated fragment or trailing text that
merged the two entries.
| **Session Management** | ||
| - Unified Codex + Claude session list | ||
| - Unified Codex + Claude + Gemini CLI + CodeBuddy Code session list | ||
| - Session locations (local-first, configurable): | ||
| - Codex: `~/.codex/sessions/*.jsonl` (or `$CODEX_HOME/sessions`, `$XDG_CONFIG_HOME/codex/sessions`) | ||
| - Claude: `~/.claude/projects/**/**/*.jsonl` (or `$CLAUDE_HOME/projects`, `$XDG_CONFIG_HOME/claude/projects`) | ||
| - Gemini: `~/.gemini/tmp/*/chats/*.json` (or `$GEMINI_HOME/tmp`, `$XDG_CONFIG_HOME/gemini/tmp`) | ||
| - CodeBuddy: `~/.codebuddy/projects/**/**/*.jsonl` (or `$CODEBUDDY_CODE_HOME_DIR/projects`) | ||
| - Local session pinning with persistent pinned state and pinned-first ordering | ||
| - Keyword/source/cwd filters | ||
| - Keyword/source/cwd/role/time filters, plus shareable filter links | ||
| - Copy resume command (Codex/Gemini/CodeBuddy): `codex resume <sessionId>` / `gemini -r <sessionId>` / `codebuddy -r <sessionId>` | ||
| - Fast search UX: short-lived query result caching to avoid rescanning on each keystroke | ||
| - Usage subview with 7d / 30d session trends, message trends, source share, and top paths | ||
| - Markdown export | ||
| - Markdown export (Web UI + `codexmate export-session`, supports `--session-id` or `--file`) | ||
| - Session-level and message-level delete (supports batch), with a local recycle bin for restore/purge | ||
| - Large-session preview optimization (fast tail preview path) |
There was a problem hiding this comment.
Fix “Copy resume command” entry to include Codex’s optional --yolo.
The “Copy resume command” bullet currently shows:
codex resume <sessionId>gemini -r <sessionId>codebuddy -r <sessionId>
…but your PR objective states Codex supports (optionally --yolo) while Gemini/CodeBuddy do not. Updating the README avoids confusion.
🛠️ Proposed fix
- - Copy resume command (Codex/Gemini/CodeBuddy): `codex resume <sessionId>` / `gemini -r <sessionId>` / `codebuddy -r <sessionId>`
+ - Copy resume command (Codex/Gemini/CodeBuddy): `codex resume <sessionId> [--yolo]` / `gemini -r <sessionId>` / `codebuddy -r <sessionId>`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@README.md` around lines 59 - 73, Update the "Copy resume command" README
entry to reflect that Codex supports an optional --yolo flag: change the Codex
example `codex resume <sessionId>` to include the optional flag (e.g., `codex
resume <sessionId> [--yolo]` or `codex resume <sessionId> (optionally --yolo)`),
while leaving the Gemini and CodeBuddy examples (`gemini -r <sessionId>`,
`codebuddy -r <sessionId>`) unchanged so it’s clear only `codex resume` accepts
`--yolo`.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web-ui/modules/app.computed.session.mjs`:
- Around line 553-561: The computed currently pulls aggregate values from
this.sessionUsageCharts.summary (totalTokens, activeDurationMs, totalDurationMs)
which shows totals across sessions; change it to derive metrics for the current
session using this.activeSession (or the per-session entry in
this.sessionUsageCharts.sessions keyed by this.activeSession.id) so the labels
under usageCurrentSessionStats reflect the active session only; update
references to use activeSession usage fields (or rename the computed if you
intend to keep aggregate data) and ensure formatUsageDuration and
formatCompactUsageSummaryNumber call sites use the per-session values instead of
summary.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 57031b4a-37d8-43b2-a62a-4a61d4be0d9e
📒 Files selected for processing (9)
tests/unit/compact-layout-ui.test.mjstests/unit/web-ui-behavior-parity.test.mjsweb-ui/modules/app.computed.session.mjsweb-ui/modules/i18n.dict.mjsweb-ui/partials/index/panel-usage.htmlweb-ui/styles/responsive.cssweb-ui/styles/sessions-list.cssweb-ui/styles/sessions-toolbar-trash.cssweb-ui/styles/sessions-usage.css
💤 Files with no reviewable changes (1)
- web-ui/styles/responsive.css
✅ Files skipped from review due to trivial changes (5)
- web-ui/partials/index/panel-usage.html
- web-ui/styles/sessions-usage.css
- tests/unit/web-ui-behavior-parity.test.mjs
- web-ui/styles/sessions-list.css
- web-ui/styles/sessions-toolbar-trash.css
🚧 Files skipped from review as they are similar to previous changes (1)
- web-ui/modules/i18n.dict.mjs
📜 Review details
🔇 Additional comments (1)
tests/unit/compact-layout-ui.test.mjs (1)
72-74: Assertions now match the single-line truncation contract.Nice adjustment—these checks align with the current
.session-item-titleCSS behavior (nowrap+ellipsis+hidden) and keep the test intent clear.
| const summary = this.sessionUsageCharts && this.sessionUsageCharts.summary | ||
| ? this.sessionUsageCharts.summary | ||
| : null; | ||
| if (!summary) return null; | ||
| const t = typeof this.t === 'function' ? this.t : null; | ||
| return { | ||
| apiDurationLabel: formatUsageDuration(summary.activeDurationMs || 0, { compact: true, lang: this.lang }), | ||
| totalDurationLabel: formatUsageDuration(summary.totalDurationMs || 0, { compact: true, lang: this.lang }), | ||
| tokenLabel: formatCompactUsageSummaryNumber(summary.totalTokens || 0), |
There was a problem hiding this comment.
usageCurrentSessionStats is using aggregate usage, not the current session.
Line 553 and Line 561 read from this.sessionUsageCharts.summary (totalTokens, durations), which is aggregated across sessionsUsageList. With the current label (当前会话 / usage.currentSession.*), this will display incorrect values when more than one session is included.
Suggested fix (derive from this.activeSession or rename the strip)
usageCurrentSessionStats() {
- const summary = this.sessionUsageCharts && this.sessionUsageCharts.summary
- ? this.sessionUsageCharts.summary
- : null;
- if (!summary) return null;
+ const session = this.activeSession && typeof this.activeSession === 'object'
+ ? this.activeSession
+ : null;
+ if (!session) return null;
const t = typeof this.t === 'function' ? this.t : null;
+ const createdAtMs = Date.parse(session.createdAt || '');
+ const updatedAtMs = Date.parse(session.updatedAt || '');
+ const totalDurationMs = Number.isFinite(createdAtMs) && Number.isFinite(updatedAtMs)
+ ? Math.max(0, updatedAtMs - createdAtMs)
+ : 0;
+ const activeDurationMs = Number.isFinite(Number(session.activeDurationMs))
+ ? Math.max(0, Math.floor(Number(session.activeDurationMs)))
+ : totalDurationMs;
+ const totalTokens = Number.isFinite(Number(session.totalTokens))
+ ? Math.max(0, Math.floor(Number(session.totalTokens)))
+ : 0;
return {
- apiDurationLabel: formatUsageDuration(summary.activeDurationMs || 0, { compact: true, lang: this.lang }),
- totalDurationLabel: formatUsageDuration(summary.totalDurationMs || 0, { compact: true, lang: this.lang }),
- tokenLabel: formatCompactUsageSummaryNumber(summary.totalTokens || 0),
+ apiDurationLabel: formatUsageDuration(activeDurationMs, { compact: true, lang: this.lang }),
+ totalDurationLabel: formatUsageDuration(totalDurationMs, { compact: true, lang: this.lang }),
+ tokenLabel: formatCompactUsageSummaryNumber(totalTokens),
label: t ? t('usage.currentSession.title') : '当前会话',
apiDurationText: t ? t('usage.currentSession.apiDuration') : 'API时长',
totalDurationText: t ? t('usage.currentSession.totalDuration') : '总时长',
tokenText: t ? t('usage.currentSession.tokens') : 'Token'
};
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const summary = this.sessionUsageCharts && this.sessionUsageCharts.summary | |
| ? this.sessionUsageCharts.summary | |
| : null; | |
| if (!summary) return null; | |
| const t = typeof this.t === 'function' ? this.t : null; | |
| return { | |
| apiDurationLabel: formatUsageDuration(summary.activeDurationMs || 0, { compact: true, lang: this.lang }), | |
| totalDurationLabel: formatUsageDuration(summary.totalDurationMs || 0, { compact: true, lang: this.lang }), | |
| tokenLabel: formatCompactUsageSummaryNumber(summary.totalTokens || 0), | |
| const session = this.activeSession && typeof this.activeSession === 'object' | |
| ? this.activeSession | |
| : null; | |
| if (!session) return null; | |
| const t = typeof this.t === 'function' ? this.t : null; | |
| const createdAtMs = Date.parse(session.createdAt || ''); | |
| const updatedAtMs = Date.parse(session.updatedAt || ''); | |
| const totalDurationMs = Number.isFinite(createdAtMs) && Number.isFinite(updatedAtMs) | |
| ? Math.max(0, updatedAtMs - createdAtMs) | |
| : 0; | |
| const activeDurationMs = Number.isFinite(Number(session.activeDurationMs)) | |
| ? Math.max(0, Math.floor(Number(session.activeDurationMs))) | |
| : totalDurationMs; | |
| const totalTokens = Number.isFinite(Number(session.totalTokens)) | |
| ? Math.max(0, Math.floor(Number(session.totalTokens))) | |
| : 0; | |
| return { | |
| apiDurationLabel: formatUsageDuration(activeDurationMs, { compact: true, lang: this.lang }), | |
| totalDurationLabel: formatUsageDuration(totalDurationMs, { compact: true, lang: this.lang }), | |
| tokenLabel: formatCompactUsageSummaryNumber(totalTokens), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web-ui/modules/app.computed.session.mjs` around lines 553 - 561, The computed
currently pulls aggregate values from this.sessionUsageCharts.summary
(totalTokens, activeDurationMs, totalDurationMs) which shows totals across
sessions; change it to derive metrics for the current session using
this.activeSession (or the per-session entry in this.sessionUsageCharts.sessions
keyed by this.activeSession.id) so the labels under usageCurrentSessionStats
reflect the active session only; update references to use activeSession usage
fields (or rename the computed if you intend to keep aggregate data) and ensure
formatUsageDuration and formatCompactUsageSummaryNumber call sites use the
per-session values instead of summary.
Summary
This PR expands the Sessions experience by adding support for Gemini CLI and CodeBuddy Code sessions, and includes documentation + Web UI polish.
Key Features
gemini -r <sessionId>/codebuddy -r <sessionId>resume commandsWeb UI Improvements
Docs
Tests
Summary by CodeRabbit
New Features
Bug Fixes
Tests
Documentation
Style