A unified desktop application for browsing conversation histories from multiple AI coding assistants — Claude Code, Codex, Gemini CLI, and OpenCode — all in one place.
AI coding assistants store their session data in different formats (JSONL, JSON, SQLite) scattered across various directories. There's no unified way to browse, search, or compare these conversations. AICoder Session Viewer solves this by normalizing all sources into a single, consistent interface.
Download the latest release from the Releases page.
The app is not signed with an Apple Developer certificate. macOS Gatekeeper may block it with an "app is damaged" warning. To fix this, run:
xattr -cr /Applications/AICoder\ Session\ Viewer.appDownload the .exe / .msi (Windows) or .deb / .AppImage (Linux) from the release page and install normally.
- Multi-tool support — Claude Code, Codex, Gemini CLI, OpenCode
- Unified data model — All session formats normalized on the Rust side before reaching the frontend
- Rich content rendering — Markdown, syntax-highlighted code blocks (via Shiki), collapsible tool calls/thinking blocks, and image attachments with click-to-zoom preview
- Search & navigation — Filter sessions by tool type, realtime title/path search (300ms debounce) that auto-upgrades to full-text content search (message text, thinking, and tool-call params) 1s after typing stops — or immediately on Enter, and search within the current conversation
- Project grouping — Group sessions by project path in a collapsible folder tree; toggle between flat list and grouped view
- Token usage summary — Show per-message and session-level token usage when the source data contains usage metadata
- Subagent drill-down — Expand Claude Code
Agenttool calls and lazy-load their subagent conversation - Resume, new session & scheduled continue — Resume any historical session, start a new session from a project folder, or wait until the reset time and then resume with
continue - YOLO launch mode — Hold Option/Alt or use the context menu to resume/start supported tools with their unattended approval flags
- View as web page — One click opens the session as a self-contained HTML page in your default browser: full Markdown rendering, collapsible thinking/tool blocks, a light/dark toggle, and an asymmetric chat layout (you on the right, the assistant on the left; tool results shown as tool output rather than "user" turns). Crucially, all images are embedded inline as base64 — including images referenced by local file path (e.g. Codex-generated posters) and pasted screenshots — so the single file renders every image with no missing resources when sent to a colleague or shown on a projector (per-image cap 10 MB)
- Export — Export sessions as JSONL, Markdown, or self-contained HTML (same inline-image rendering as above) via save dialog
- Lightweight i18n — Automatically follows the system/browser language, with
VITE_LOCALE=zh|enoverride support - Dark theme — Purpose-built dark UI with per-tool color coding
- Fast & lightweight — Native Tauri app with minimal resource usage; SQLite accessed read-only
| Tool | macOS / Linux | Windows | Format |
|---|---|---|---|
| Claude Code | ~/.claude/projects/{path}/{uuid}.jsonl |
%USERPROFILE%\.claude\projects\... |
JSONL (index in ~/.claude/sessions/*.json) |
| Codex | ~/.codex/sessions/{Y}/{M}/{D}/rollout-*.jsonl |
%USERPROFILE%\.codex\sessions\... |
JSONL |
| Gemini CLI | ~/.gemini/tmp/{project}/chats/session-*.json |
%USERPROFILE%\.gemini\tmp\... |
JSON |
| OpenCode | ~/.local/share/opencode/opencode.db |
%USERPROFILE%\.local\share\opencode\opencode.db |
SQLite |
All four tools use the user home directory (
~/%USERPROFILE%) as root, with identical directory structures across platforms.
If your tool stores data in a non-default location (e.g., a derivative of OpenCode with a different database path), you can override the path per provider via the in-app Settings dialog (gear icon in the sidebar header).
- Click the gear icon in the title bar
- Enter or browse to a custom path for any provider; leave a field empty to use the default
- Click Save — changes take effect immediately, no app restart required
- If a custom path is invalid, that provider is silently skipped and a warning is shown
The configuration is persisted at:
- macOS:
~/Library/Application Support/aicoder-session-viewer/config.json - Linux:
~/.config/aicoder-session-viewer/config.json - Windows:
%APPDATA%\aicoder-session-viewer\config.json
When Claude Code, Codex, Gemini CLI, or OpenCode shows a quota message such as You've hit your limit · resets 1am (Asia/Shanghai) or try again at 1:35 PM, support scheduled continue strategy:
- Infer the reset time from the most recent session messages. The parser currently recognizes phrasings such as
resets ...,try again at ..., andavailable again at .... If no explicit reset time is found, it falls back to the next local01:00. - Add a fixed 5-minute safety buffer on top of the inferred reset time, so the app does not fire exactly at the boundary.
- Open a fresh terminal window or tab.
- Wait until the target time.
- Run a native
resume + promptcommand that restores the session and sendscontinue. - When the target CLI supports unattended execution flags, append them as well so the resumed run does not stall on approval prompts.
Current command shapes for scheduled continue:
- Claude Code:
claude --permission-mode bypassPermissions --resume <session-id> "continue" - Codex:
codex resume --dangerously-bypass-approvals-and-sandbox <session-id> "continue" - Gemini CLI:
gemini --approval-mode yolo --resume <session-id> "continue" - OpenCode:
opencode --session <session-id> --prompt "continue"
This keeps the behavior aligned with each vendor CLI's supported session model instead of depending on terminal input injection hacks. Where supported, the app also opts into unattended approval modes so the resumed run can proceed without stopping for confirmation.
| Shortcut | Scope | Action |
|---|---|---|
Cmd+F / Ctrl+F |
Session detail | Focus the current conversation search box |
Enter |
Conversation search box | Jump to the next match |
Shift+Enter |
Conversation search box | Jump to the previous match |
Esc |
Image preview | Close the full-size image preview |
Hold Option / Alt + click resume |
Session detail | Resume with YOLO / unattended approval flags when supported |
Hold Option / Alt + click new session |
Project tree | Start a new session with YOLO / unattended approval flags when supported |
OpenCode currently ignores YOLO mode because its CLI does not expose an unattended approval flag yet.
| Layer | Technology |
|---|---|
| Desktop framework | Tauri v2 |
| Backend | Rust (serde, chrono, rusqlite, walkdir, thiserror) |
| Frontend | React 19 + TypeScript |
| Styling | Tailwind CSS v4 |
| State management | Zustand |
| Code highlighting | Shiki |
| Markdown | react-markdown + remark-gfm |
- Node.js >= 20
- pnpm >= 10
- Rust >= 1.85
- Tauri v2 system dependencies — see Tauri prerequisites
# Clone the repository
git clone https://github.com/user/aicoder-session-viewer.git
cd aicoder-session-viewer
# Install frontend dependencies
pnpm install
# Start in development mode (compiles Rust + starts Vite dev server)
pnpm tauri dev
# Force Chinese or English UI during development
VITE_LOCALE=zh pnpm tauri dev
VITE_LOCALE=en pnpm tauri dev
# Build for production
pnpm tauri buildaicoder-session-viewer/
├── src-tauri/ # Rust backend
│ └── src/
│ ├── main.rs # Entry point
│ ├── lib.rs # Tauri app setup & plugin registration
│ ├── error.rs # Unified error types (thiserror)
│ ├── models.rs # Shared data models (SessionSummary, Message, ContentBlock...)
│ ├── commands.rs # Tauri IPC commands
│ ├── export.rs # Session export (JSONL / Markdown / self-contained HTML)
│ └── providers/ # Data source implementations
│ ├── mod.rs # SessionProvider trait + ProviderRegistry
│ ├── claude.rs # Claude Code (JSONL)
│ ├── codex.rs # Codex (JSONL)
│ ├── gemini.rs # Gemini CLI (JSON)
│ └── opencode.rs # OpenCode (SQLite)
├── src/ # React frontend
│ ├── App.tsx # Root component
│ ├── App.css # Tailwind imports + theme variables
│ ├── types.ts # TypeScript types (mirrors models.rs)
│ ├── i18n/ # Lightweight Chinese / English locale layer
│ ├── stores/
│ │ └── sessionStore.ts # Zustand store
│ ├── hooks/
│ │ ├── useAltKeyPressed.ts # Detect Option/Alt for YOLO launch shortcuts
│ │ └── useDebounce.ts
│ ├── utils/
│ │ ├── buildProjectTree.ts # Group sessions into folder tree
│ │ ├── format.ts # Shared number/time formatting helpers
│ │ └── sessionSearch.ts # In-session search and shortcut matching
│ └── components/
│ ├── Layout.tsx # Two-column layout + view mode toggle + settings dialog
│ ├── SettingsDialog.tsx # Provider path override dialog
│ ├── common/
│ │ └── YoloHint.tsx # Shared YOLO mode badge
│ ├── Sidebar/
│ │ ├── SearchBar.tsx # Debounced search input
│ │ ├── ToolFilter.tsx # Tool type filter tabs
│ │ ├── SessionList.tsx # Flat session list
│ │ └── ProjectTree.tsx # Grouped project folder tree + new session launcher
│ └── Chat/
│ ├── ChatView.tsx # Session detail + search + resume + web view + export buttons
│ ├── MessageBubble.tsx # Message rendering (all content block types)
│ ├── CodeBlock.tsx # Shiki syntax highlighting
│ └── ToolCallBlock.tsx # Collapsible tool use/result blocks + subagent drill-down
├── index.html
├── package.json
├── vite.config.ts
└── tsconfig.json
┌─────────────────────────────────────────────────────────┐
│ Frontend (React + TypeScript) │
│ ┌──────────────┐ ┌────────────────────────────────┐ │
│ │ Sidebar │ │ ChatView │ │
│ │ - Search │ │ - Message bubbles │ │
│ │ - Filter │ │ - Markdown / Code / Tools │ │
│ │ - List │ │ - Thinking blocks │ │
│ └──────┬───────┘ └───────────────┬────────────────┘ │
│ │ Zustand │ │
│ └──────────┬────────────────┘ │
│ │ invoke() │
├────────────────────┼────────────────────────────────────┤
│ Tauri IPC │ │
├────────────────────┼────────────────────────────────────┤
│ Backend (Rust) ▼ │
│ ┌─────────────────────────┐ │
│ │ ProviderRegistry │ │
│ │ ┌───────┐ ┌───────┐ │ │
│ │ │Claude │ │ Codex │ │ → SessionSummary │
│ │ └───────┘ └───────┘ │ → Session │
│ │ ┌───────┐ ┌────────┐ │ → Message │
│ │ │Gemini │ │OpenCode│ │ → ContentBlock │
│ │ └───────┘ └────────┘ │ │
│ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
All four data sources implement the SessionProvider trait and are normalized into a unified model before being sent to the frontend. If a provider is unavailable (e.g., directory doesn't exist), it is silently skipped without affecting others.
MIT
