Claude History is a VS Code extension that lets you search, browse, and export your Claude Code conversation history — directly inside VS Code.
Claude Code stores all conversations as JSONL files on disk, organized by project. This extension indexes those files using full-text search (FlexSearch), surfaces them in a native VS Code TreeView grouped by project, and renders each conversation in a rich React-powered webview with syntax-highlighted tool cards for every tool Claude used.
Migrated from a standalone Electron desktop app. The core domain logic is 100% platform-agnostic and reusable outside VS Code.
- Conversation browser — sidebar TreeView groups conversations by project, sorted by recency
- Full-text search —
Claude History: SearchQuickPick searches across all conversation content using FlexSearch, with text highlighting in results - Sort options — six sort modes: Most Recent, Oldest First, Most Messages, Fewest Messages, Alphabetical, configurable via
claudeHistory.sortOrder - Date range filter — filter to today, last 7 days, last 30 days, or all time via
claudeHistory.dateRange - Live file watcher — conversations modified since the panel opened show a live badge; the tree auto-refreshes
- Rich conversation viewer — React webview renders messages with specialized tool cards:
Editdiffs (structured patch hunks with old/new highlighting)Bashterminal output (stdout + stderr, with CollapsibleJson for structured output)Read,Write,Glob,Grepfile operationsTaskagent,TodoWrite,TodoReadtask management cards- Generic fallback card for unknown tools
- Export — export any conversation to Markdown, plain text, or JSON via VS Code's native save dialog
- Copy — copy individual message content to clipboard
- Multi-profile support — scan conversations across multiple Claude Code config directories (e.g. default + work)
- Refresh — rebuild the index on demand after new conversations are created
- Multi-assistant support — Codex CLI sessions appear in a separate tree section alongside Claude sessions; toggle providers via
claudeHistory.configureProviders - Codex auto-detection — first-time notification when
~/.codex/sessionsis detected; one-click to enable - Resume sessions —
claudeHistory.resumeProviderSessionopens a terminal and runs the correct resume command for any provider (claude --resume <id>orcodex resume <id>)
The extension is split into two runtimes that cannot share modules directly:
packages/threadbase-core/ ← Private shared package (@threadbase/core)
├── src/session.ts ← ProviderSession, ProviderMessage interfaces
├── src/provider.ts ← AssistantProvider interface
├── src/registry.ts ← ProviderRegistry (Promise.allSettled fan-out + SessionFilter)
└── src/index.ts ← Re-exports all public symbols
VS Code Extension Host (Node.js)
├── src/core/ ← Platform-agnostic domain logic (no Electron, no VS Code)
│ ├── types.ts ← All shared domain types
│ ├── scanner.ts ← JSONL streaming parser with LRU cache
│ ├── indexer.ts ← FlexSearch full-text index
│ ├── formatters.ts ← Markdown / plain-text export formatters
│ ├── worktree-parser.ts ← Git worktree metadata parser
│ ├── execFileNoThrow.ts ← Safe child_process wrapper
│ ├── profiles.ts ← Profile config load/save (storageDir-parameterized)
│ ├── filters.ts ← applyFilters / applySorting with exhaustiveness guard
│ ├── export.ts ← buildExportContent — format-agnostic export builder
│ └── providers/
│ ├── claude.ts ← ClaudeProvider — wraps ConversationScanner; resume via `claude --resume <uuid>`
│ └── codex.ts ← CodexProvider — parses Codex CLI JSONL files; resume via `codex resume <id>`
├── src/services/
│ └── HistoryService.ts ← Orchestrates scanner + indexer + ProviderRegistry; single VS Code entry point
├── src/providers/
│ └── ConversationTreeProvider.ts ← vscode.TreeDataProvider; Claude tree + Codex CLI section
├── src/webview/
│ ├── protocol.ts ← Typed bidirectional message contract (ExtensionMessage / WebviewMessage)
│ └── ConversationPanel.ts ← vscode.WebviewPanel factory; singleton per conversation
└── extension.ts ← activate() / deactivate(); wires everything together
VS Code Webview (sandboxed iframe — no Node.js access)
└── src/webview-ui/
├── index.html ← CSP nonce placeholder
├── main.tsx ← React 18 createRoot entry point
├── App.tsx ← Conversation viewer root; handles all webview ↔ extension messages
├── vscodeApi.ts ← Singleton wrapper for acquireVsCodeApi()
├── types.ts ← Re-exports from src/core/types (type-only, no runtime coupling)
└── components/
├── MessageContent.tsx ← Renders message text with react-markdown + remark-gfm
├── ToolInvocationBadge.tsx ← Inline badge showing tool name + input summary
├── MessageNavigation.tsx ← Prev/Next navigator for jumping between messages
├── ToolResultCard.tsx ← Dispatcher — routes to the correct tool card
└── tool-cards/
├── EditDiffCard.tsx ← Structured patch diff viewer
├── BashTerminalCard.tsx ← stdout/stderr terminal output with CollapsibleJson
├── GrepResultCard.tsx ← Grep match results with file/line context
├── GlobResultCard.tsx ← File glob match list
├── ReadFileCard.tsx ← File read operation card
├── WriteFileCard.tsx ← File write operation card
├── TaskToolCard.tsx ← Task agent / TodoWrite / TodoRead cards
├── CollapsibleJson.tsx ← Expandable JSON viewer (used by BashTerminalCard)
└── GenericToolCard.tsx ← Fallback for unknown tool types
Communication between the two runtimes uses a typed, discriminated-union message protocol defined in src/webview/protocol.ts:
// Extension Host → Webview
type ExtensionMessage =
| { type: 'loadConversation'; conversation: Conversation }
| { type: 'highlightQuery'; query: string }
// Webview → Extension Host
type WebviewMessage =
| { type: 'ready' }
| { type: 'copyMessage'; content: string }
| { type: 'exportConversation'; format: ExportFormat }
| { type: 'openExternal'; url: string }The webview sends ready on mount; the extension host replays the last loaded conversation on reconnect (e.g. when the panel regains focus after being hidden).
All domain logic lives in src/core/ with zero Electron and zero VS Code imports. This was a deliberate architectural boundary: core modules can be tested with plain Vitest (no VS Code test runner needed), ported to other editors, or used in CLI tools without modification.
HistoryService is the only class in the extension host that directly uses ConversationScanner and SearchIndexer. All VS Code commands and providers talk to HistoryService, never to the core modules directly. This keeps the VS Code surface area isolated and makes the domain logic independently testable.
Two separate esbuild invocations produce two bundles:
- Extension host —
platform: 'node',format: 'cjs',external: ['vscode'](VS Code API injected at runtime) - Webview bundle —
platform: 'browser',format: 'esm', bundles React + all dependencies into a singleout/webview/main.js
esbuild was chosen over webpack for its simplicity and zero-config approach. No loader configuration, no plugin ecosystem to manage.
The extension host and webview have fundamentally different compilation targets:
| Extension host | Webview | |
|---|---|---|
| Config | tsconfig.json |
tsconfig.webview.json |
| Module | CommonJS | ESNext |
| Lib | ES2020 | ES2020 + DOM |
| JSX | — | react-jsx |
| Excludes | src/webview-ui/** |
everything except src/webview-ui/** |
A single tsconfig.json cannot correctly typecheck both because DOM types (e.g. window, document) conflict with Node.js globals.
The webview HTML uses a cryptographically random nonce on every load:
<script nonce="${nonce}" src="${scriptUri}"></script>The Content Security Policy restricts scripts to only those carrying the correct nonce, preventing XSS from injected content in conversation messages.
retainContextWhenHidden: true
ConversationPanel sets retainContextWhenHidden: true on the WebviewPanel. This preserves the React component tree and scroll position when the user switches tabs and returns, avoiding a full re-render and re-fetch of the conversation data.
acquireVsCodeApi() may only be called once per webview lifetime. src/webview-ui/vscodeApi.ts wraps it in a module-level singleton and exports a typed postMessage function. All components call this module — never acquireVsCodeApi() directly.
SearchIndexer uses FlexSearch's Document index (not the simpler Index) to support field-level indexing across sessionName, projectName, and preview. This allows searches to be scoped or ranked by field. The index is rebuilt from scratch on each rebuild() call; there is no persistence to disk (index rebuild from JSONL files is fast enough at typical conversation counts).
The AssistantProvider interface decouples the extension from any specific AI tool. Each provider implements scanSessions(), loadSession(), and resumeCommand(). ProviderRegistry fans out getSessions() to all registered providers using Promise.allSettled — a failing provider (disk error, missing directory) is silently skipped rather than aborting the whole scan.
threadbase-core is a private npm package ("private": true) with no VS Code dependency. It is linked via file: in package.json and resolved by both tsc (via paths alias) and Vitest (via resolve.alias) without a build step.
Claude sessions continue to flow through the existing ConversationScanner/SearchIndexer pipeline unchanged. The ClaudeProvider registered in ProviderRegistry is used only for the resumeCommand path; getProviderSessions() is for non-Claude providers (Codex, and future additions).
Claude Code supports multiple config directories via CLAUDE_CONFIG_DIR. src/core/profiles.ts manages a profiles.json file in VS Code's globalStorageUri directory. On first run, a default profile pointing to ~/.claude is created automatically. Additional profiles (e.g. for work accounts) can be added by editing profiles.json directly (UI is planned for a future stage).
- VS Code 1.85.0 or later
- Node.js 18.x or later
- npm 9.x or later
git clone https://github.com/RonenMars/vscode-claude-code-manager
cd vscode-claude-code-manager
npm installnpm run buildThis runs node esbuild.js and produces:
out/extension.js— extension host bundle (~72 kb)out/webview/main.js— React webview bundle (~1.5 mb, includes React + all deps)
npm run watchRebuilds both bundles on file changes. In VS Code, press F5 to launch the extension in a new Extension Development Host window. After making changes, press Ctrl+Shift+F5 (macOS: Shift+Cmd+F5) to reload.
npm testRuns all Vitest unit tests (core modules only — no VS Code runtime required):
npm run test:watch # watch modenpx tsc --noEmit # extension host
npx tsc --noEmit --project tsconfig.webview.json # webview| Command | Title | Description |
|---|---|---|
claudeHistory.search |
Claude History: Search | Opens a QuickPick to full-text search all conversations |
claudeHistory.refresh |
Claude History: Refresh Index | Rescans JSONL files and rebuilds the search index |
claudeHistory.openConversation |
Claude History: Open Conversation | Opens a conversation in the webview panel |
claudeHistory.openWorkspace |
Claude History: Open Workspace Conversations | Filters the tree to the current workspace's conversations |
claudeHistory.setSortOrder |
Claude History: Set Sort Order | QuickPick to change the tree sort order |
claudeHistory.setDateRange |
Claude History: Set Date Range | QuickPick to change the date range filter |
claudeHistory.resumeProviderSession |
Claude History: Resume Provider Session | Opens a terminal and runs the resume command for a session (claude --resume or codex resume) |
claudeHistory.configureProviders |
Claude History: Configure Providers | Multi-select QuickPick to toggle which session providers are shown in the tree |
All commands are also accessible from the activity bar sidebar panel's toolbar.
| Setting | Type | Default | Description |
|---|---|---|---|
claudeHistory.defaultProfileId |
string |
"default" |
Profile ID used for scanning when no explicit profile is selected |
claudeHistory.sortOrder |
string |
"recent" |
Conversation sort order: recent, oldest, messages-desc, messages-asc, alpha |
claudeHistory.dateRange |
string |
"all" |
Date range filter: all, today, week, month, year |
claudeHistory.enabledProviders |
string[] |
["claude"] |
Active session providers shown in the tree. Add "codex" to enable Codex CLI sessions |
claudeHistory.codexSessionsPath |
string |
"" |
Override path to the Codex CLI sessions directory. Leave empty to use the default (~/.codex/sessions/) |
Profiles are managed via profiles.json in VS Code's global storage directory. To find this directory, run Developer: Open Extension Storage from the command palette after the extension has activated.
vscode-claude-code-manager/
├── packages/
│ └── threadbase-core/ ← Private shared package (@threadbase/core)
│ └── src/ ← ProviderSession, AssistantProvider, ProviderRegistry
├── src/
│ ├── core/ ← Platform-agnostic domain logic + tests
│ │ └── providers/ ← ClaudeProvider, CodexProvider
│ ├── services/ ← VS Code orchestration layer
│ ├── providers/ ← VS Code TreeDataProvider
│ ├── webview/ ← Panel factory + typed message protocol
│ ├── webview-ui/ ← React conversation viewer (browser bundle)
│ └── extension.ts ← Extension entry point
├── src/test/
│ └── fixtures/ ← Real JSONL conversation files used in tests
├── out/ ← Build output (git-ignored)
│ ├── extension.js ← Extension host bundle
│ └── webview/
│ └── main.js ← Webview React bundle
├── docs/
│ ├── stage0-architecture.md ← Target architecture spec
│ └── stage0-feature-parity.md ← Feature parity matrix vs. original Electron app
├── esbuild.js ← Build script (extension host + webview)
├── tsconfig.json ← Extension host TypeScript config
├── tsconfig.webview.json ← Webview TypeScript config
└── vitest.config.ts ← Test runner config (core only)
| Layer | Technology |
|---|---|
| Extension host | TypeScript 5.3, Node.js, VS Code API ^1.85.0 |
| Full-text search | FlexSearch 0.7 |
| Webview UI | React 18, TypeScript |
| Markdown rendering | react-markdown + remark-gfm |
| Build | esbuild |
| Tests | Vitest |
This extension is a migration of claude-code-search-history, a standalone Electron desktop app. The VS Code extension covers Stages 0–5 of that migration:
| Stage | Scope | Status |
|---|---|---|
| 0 | Analysis + project bootstrap | ✅ Complete |
| 1 | Core module extraction (types, scanner, indexer, formatters, profiles, filters, export) | ✅ Complete |
| 2 | Extension scaffold (package.json, tsconfigs, esbuild, entry point) | ✅ Complete |
| 3 | HistoryService + ConversationTreeProvider | ✅ Complete |
| 4 | Webview infrastructure (ConversationPanel + message protocol) | ✅ Complete |
| 5 | React conversation viewer (all tool cards + message navigation) | ✅ Complete |
| 6 | Sort options, date filter, text highlighting, live file watcher, task tool cards | ✅ Complete |
| 7+ | Chat terminal (node-pty), worktrees panel, profiles UI | Planned |
Dropped from extension scope (Electron-only):
ChatTerminal— xterm.js + node-pty interactive terminalActiveChatList— live chat session managementWorktreesPanel— git worktree creation UIProfilesPanel— profile management UI
Redesigned for VS Code:
| Electron | VS Code |
|---|---|
ResultsList + SearchBar |
VS Code QuickPick |
FilterPanel |
QuickPick filter step |
Electron IPC (ipcMain / ipcRenderer) |
Typed postMessage protocol |
app.getPath('userData') |
context.globalStorageUri.fsPath |
dialog.showSaveDialog |
vscode.window.showSaveDialog |
electron-store |
context.globalState / vscode.workspace.getConfiguration |
scan-progress IPC push event |
vscode.window.withProgress |
MIT