feat(sessions): Batch 1 — full-prompt search, match snippets, junk fold, highlight fix#132
Conversation
- Search ALL sessions x ALL user prompts main-side (fixes #131); matches beyond the loaded 100 append with lazy enrichment - Matched-prompt snippet line (prompt #N + context) when the hit isn't visible in the row's first/last lines - Unified high-contrast amber search highlight (9 sites) - Fold minor sessions (<=2 msgs, untitled, no PR, closed) into an expandable row - New pure module session-search.ts + 10 unit tests (45 total) - Plan doc: docs/session-finding-plan-zh-tw.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughSession search now scans all sessions and prompts in the main process, returns contextual snippets, and merges deep results into the switcher. The UI adds consistent highlighting, minor-session folding, debounced search coordination, lazy enrichment, and updated documentation and release metadata. ChangesSession search experience
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant SwitcherUI
participant ElectronAPI
participant searchClaudeSessions
participant historyjsonl
SwitcherUI->>ElectronAPI: request debounced search query
ElectronAPI->>searchClaudeSessions: searchClaudeSessions(query)
searchClaudeSessions->>historyjsonl: read sessions and prompt text
historyjsonl-->>searchClaudeSessions: session and prompt data
searchClaudeSessions-->>ElectronAPI: matched sessions and snippets
ElectronAPI-->>SwitcherUI: deep search results
SwitcherUI->>SwitcherUI: merge, enrich, fold, and highlight results
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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
🧹 Nitpick comments (2)
src/electron-api.d.ts (1)
133-139: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTyped payload is correct; consider sharing the shape instead of triple-declaring it.
The
snippetsrecord shape ({ snippet, promptIndex, isLastPrompt }) is duplicated here, asSessionSearchMatchinclaude-session-utility.ts, and inline inswitcher-ui.tsx'ssearchSnippetsstate. A type-only import (erased at compile time, so it's safe across the main/renderer boundary) would give a single source of truth and catch drift at compile time instead of silently at runtime.♻️ Share the type instead of duplicating it
+import type { SessionSearchMatch } from './claude-session-utility'; + searchClaudeSessions: (query: string) => Promise<{ sessions: any[]; - snippets: Record< - string, - { snippet: string; promptIndex: number; isLastPrompt: boolean } - >; + snippets: Record<string, SessionSearchMatch>; }>;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/electron-api.d.ts` around lines 133 - 139, Reuse the existing SessionSearchMatch type from claude-session-utility.ts for the snippets record value in searchClaudeSessions, and update switcher-ui.tsx’s searchSnippets state to use the same shared type. Remove the duplicated inline `{ snippet, promptIndex, isLastPrompt }` declarations while preserving the current payload shape.src/claude-session-utility.ts (1)
215-239: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy liftFull-corpus scan+lowercase runs synchronously on the main process for every search.
searchClaudeSessionsrebuildstarget(projectName + project + all prompts joined) and lowercases it for every session on every call — this is exactly the workload issue#131wants to unlock (searching beyond the ~100 loaded sessions across all accounts/history). Since the function is synchronous and likely invoked viaipcMain.handle, this blocks the Electron main process for the full scan duration on each debounced keystroke. With large multi-account histories (the target use case), this could introduce noticeable UI jank/freezes app-wide (window drag, other IPC, status updates), not just in the switcher.Two independent, low-cost mitigations:
- Cache pre-lowercased prompt text alongside
promptsBySession(built once per cache refresh) instead of re-lowering the same megabytes of text on every keystroke-triggered search.- If profiling on real large histories shows meaningful main-thread blocking, consider offloading the scan to a
worker_threadsworker or chunking it withsetImmediatebetween session batches.Please verify actual latency against realistic account sizes/history volumes before deciding whether (2) is necessary; (1) is cheap and worth doing regardless.
⚡ Minimal mitigation: cache lowercased prompts once per rebuild
-let promptsBySession: Map<string, string[]> = new Map(); +let promptsBySession: Map<string, string[]> = new Map(); +let promptsBySessionLower: Map<string, string[]> = new Map();Populate
promptsBySessionLoweralongsidepromptsBySessioninreadClaudeSessions, then insearchClaudeSessionsbuildtargetfrom the pre-lowered arrays instead of calling.toLowerCase()on the joined string each time.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/claude-session-utility.ts` around lines 215 - 239, Cache lowercased prompt text during the cache rebuild in readClaudeSessions by maintaining a promptsBySessionLower structure alongside promptsBySession. Update searchClaudeSessions to use the pre-lowered prompt array when constructing its search target, avoiding repeated lowercasing of joined prompts on every query; preserve the existing project-name/project matching and result behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/session-finding-plan-zh-tw.md`:
- Line 125: Update the D3 `/pin` row in the Batch 3 table to include a third
cell matching the `項 | 內容 | 備註` header. Preserve the existing content in the
first two cells and add the appropriate notes cell, even if it is empty.
In `@src/claude-session-utility.ts`:
- Line 215: Run Prettier on searchClaudeSessions and the template-literal
assignment around the reported lines so both follow the project’s print-width
and line-break rules. Ensure any multiline arrays or objects in the affected
code use trailing commas.
In `@src/session-search.test.ts`:
- Line 12: Format the affected code in the session-search test using the
project’s Prettier configuration, including wrapping long string literals and
multi-argument calls and adding required trailing commas. Apply the changes to
the test cases around the target declaration and the referenced sections,
without altering their behavior.
In `@src/session-search.ts`:
- Around line 26-57: Format the return object in findPromptMatch so it fits the
configured print width, wrapping the snippet property as needed, and add the
required trailing comma to the object literal. Keep the matching logic
unchanged.
In `@src/switcher-ui.tsx`:
- Around line 411-429: Cap the non-duplicate deep matches appended by
applySearchFilter to MAX_APPENDED_DEEP_MATCHES before merging and sorting,
preserving the existing base results and ordering. In scheduleDeepSearch,
replace the appended-result literal 30 with the same MAX_APPENDED_DEEP_MATCHES
constant so display and enrichment limits remain synchronized.
---
Nitpick comments:
In `@src/claude-session-utility.ts`:
- Around line 215-239: Cache lowercased prompt text during the cache rebuild in
readClaudeSessions by maintaining a promptsBySessionLower structure alongside
promptsBySession. Update searchClaudeSessions to use the pre-lowered prompt
array when constructing its search target, avoiding repeated lowercasing of
joined prompts on every query; preserve the existing project-name/project
matching and result behavior.
In `@src/electron-api.d.ts`:
- Around line 133-139: Reuse the existing SessionSearchMatch type from
claude-session-utility.ts for the snippets record value in searchClaudeSessions,
and update switcher-ui.tsx’s searchSnippets state to use the same shared type.
Remove the duplicated inline `{ snippet, promptIndex, isLastPrompt }`
declarations while preserving the current payload shape.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: f9209980-4fc6-4fe1-bf29-2c30c5200fe7
📒 Files selected for processing (9)
CHANGELOG.mdREADME.mddocs/session-finding-plan-zh-tw.mdpackage.jsonsrc/claude-session-utility.tssrc/electron-api.d.tssrc/session-search.test.tssrc/session-search.tssrc/switcher-ui.tsx
There was a problem hiding this comment.
All reported issues were addressed across 9 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
- Read search value via ref in fetchClaudeSessions (stale closure could clear the filtered list on window focus) [cubic P1] - Gate minor-session folding on first active-detection completion so a just-started session never folds at app start [cubic P2] - Un-cap deep-match enrichment (was 30) to match the uncapped append; both now bounded by the search result cap of 100 [CodeRabbit Major, resolved toward no-silent-caps] - Prettier: new files wholesale; changed lines only in claude-session-utility.ts / switcher-ui.tsx - Docs: plan status header reflects PR-1 in flight; D3 table row third cell [cubic P2 / CodeRabbit Minor] Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add docs/session-finding-plan.md (full English translation, same status/decisions/gotchas) - Untrack the zh-TW copy (stays as a local working copy; note added pointing at the English canonical version) - CHANGELOG plan reference updated Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/switcher-ui.tsx (1)
793-797: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
onSessionStatusesUpdatedstill usesfilterSessionsLocally— deep matches vanish during status updates.This PR updated all other search-aware
setSessionscall sites (lines 509, 550, 592) to useapplySearchFilter, but missed this one. When a session status update arrives during an active search,filterSessionsLocallyfilters out deep-matched sessions (matched by prompt text in the main process, not by local enrichment fields). Deep matches disappear and don't reappear until the user types again, undermining the PR's core objective.🔧 Use `applySearchFilter` for consistency
setSessions((prev: any[]) => { const updated = updateSessions(prev); const search = sessionSearchRef2.current; - return search.trim() ? filterSessionsLocally(updated, search) : updated; + return search.trim() ? applySearchFilter(updated, search) : updated; });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/switcher-ui.tsx` around lines 793 - 797, Update the search-aware setSessions callback in onSessionStatusesUpdated to use applySearchFilter instead of filterSessionsLocally, preserving the existing updateSessions flow and current search value handling so deep-matched sessions remain visible during status updates.
🧹 Nitpick comments (1)
src/switcher-ui.tsx (1)
1604-1611: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winFold/expand rows lack keyboard interaction support.
Both the expand row (line 1604) and the collapse row (line 1371) only have
onClickhandlers. Keyboard users cannot toggle minor-session folding. Consider addingtabIndex={0},role="button", and anonKeyDownhandler for Enter/Space.⌨️ Add keyboard support to the expand row
{!isSearchingSessions && !minorsExpanded && minorSessions.length > 0 && ( <div + tabIndex={0} + role="button" + aria-label={`Expand ${minorSessions.length} minor sessions`} onClick={() => setMinorsExpanded(true)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + setMinorsExpanded(true); + } + }} style={{ padding: '6px 10px 8px 24px', color: '`#777`', fontSize: '12px', cursor: 'pointer' }} > ▸ {minorSessions.length} minor sessions (≤2 msgs, untitled) </div> )}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/switcher-ui.tsx` around lines 1604 - 1611, Update the minor-session expand row near the isSearchingSessions/minorsExpanded conditional to add keyboard accessibility with tabIndex={0}, role="button", and an onKeyDown handler that triggers setMinorsExpanded(true) for Enter and Space. Apply the same interaction support to the collapse row's existing toggle near the minorsExpanded collapse control, preserving their current click behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/switcher-ui.tsx`:
- Around line 454-456: Apply Prettier formatting to the session-loading code
around the loaded and appended declarations, and the related code at the second
flagged location, so it matches the repository’s formatting rules and passes
prettier/prettier ESLint checks. Preserve the existing behavior and logic.
---
Outside diff comments:
In `@src/switcher-ui.tsx`:
- Around line 793-797: Update the search-aware setSessions callback in
onSessionStatusesUpdated to use applySearchFilter instead of
filterSessionsLocally, preserving the existing updateSessions flow and current
search value handling so deep-matched sessions remain visible during status
updates.
---
Nitpick comments:
In `@src/switcher-ui.tsx`:
- Around line 1604-1611: Update the minor-session expand row near the
isSearchingSessions/minorsExpanded conditional to add keyboard accessibility
with tabIndex={0}, role="button", and an onKeyDown handler that triggers
setMinorsExpanded(true) for Enter and Space. Apply the same interaction support
to the collapse row's existing toggle near the minorsExpanded collapse control,
preserving their current click behavior.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0a3b1b12-3367-43a8-ab3b-1b9af4599271
📒 Files selected for processing (6)
CHANGELOG.mddocs/session-finding-plan.mdsrc/claude-session-utility.tssrc/session-search.test.tssrc/session-search.tssrc/switcher-ui.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- src/session-search.test.ts
- src/session-search.ts
- src/claude-session-utility.ts
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pre-existing bug surfaced during Batch 1 testing: - Enrichment spawned ~400 concurrent exec greps (100 sessions x 4); the biggest transcripts regularly blew the silent 3s timeout (errors resolve to ''), so their title/branch vanished at random. Now batched 10 sessions at a time; timeout 3s -> 5s. Same batching applied to loadLastAssistantResponses (100 concurrent tails). - Branch was read from the last 5 transcript lines; an active session's tail is often tool output with no gitBranch field (measured on a live 26MB session: tail -5 hit 0, tail -20 hit 12). Window widened to 50 lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Fixes the cubic finding (entry-time TTL stamp: any scan slower than the 5s TTL wrote an already-stale cache, so every popup interaction triggered another multi-second full rescan) and the 8-10s cold path the user measured (issue #134): - Per-file {mtimeMs, size} scan state: transcripts unchanged since their last scan are never re-grepped; a ~ms statSync pass replaces the wall-clock TTL entirely - titles/branches/prLinks become persistent accumulator maps; concurrent callers serialize through a promise queue and hit the accumulated cache (correct across different session sets) - File state recorded after a session's scan, so a failed pass retries; invalidateSessionCache clears the state (+ prLinks, which it previously missed) - Scan batches 10 -> 25 (both enrichment and last-assistant reads) for a faster first cold pass Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/claude-session-utility.ts`:
- Line 1737: Format the changed enrichment block in loadSessionEnrichment,
including the referenced lines, with the project’s Prettier configuration so all
prettier/prettier lint errors are resolved. Limit formatting changes to this
block and preserve its behavior.
- Around line 1744-1749: Update execPromise to preserve command failures
distinctly from a successful empty stdout, including timeouts and spawn errors,
and propagate that failure to the enrichment scan. In the scan logic around
enrichedFileState.set, only mark the transcript fresh after a successful
command; leave failed scans eligible for retry.
- Around line 1743-1780: Replace the shell-based execPromise grep/tail pipeline
in scan with shell-free filesystem reads and in-process parsing of each
jsonlPath, preserving the latest matching title, AI-title, branch, and PR-link
results. Remove interpolated transcript paths from command execution, and ensure
read/parse failures do not update enrichedFileState as successfully scanned so
they are retried on the next scan.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 751e4d4d-27a3-4489-ab32-24ec1b495eb3
📒 Files selected for processing (3)
CHANGELOG.mdsrc/claude-session-utility.tssrc/switcher-ui.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- CHANGELOG.md
- src/switcher-ui.tsx
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Expanded minors header now sticks to the top of the scroll area so collapsing never requires scrolling back to the boundary row; each fresh popup show starts folded again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/switcher-ui.tsx (1)
798-802: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
onSessionStatusesUpdatedstill usesfilterSessionsLocally— deep matches dropped during search.All other search-mode
setSessionscall sites (lines 512-514, 555, 597) were updated to useapplySearchFilter, but this one was missed. When a session status changes viafs.watchwhile the user is searching,filterSessionsLocallyfilters by enrichment fields only, dropping deep matches that were found by prompt text in the main process. This causes deep-matched sessions to disappear from the list mid-search.🔧 Use `applySearchFilter` to preserve deep matches
setSessions((prev: any[]) => { const updated = updateSessions(prev); const search = sessionSearchRef2.current; - return search.trim() ? filterSessionsLocally(updated, search) : updated; + return search.trim() ? applySearchFilter(updated, search) : updated; });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/switcher-ui.tsx` around lines 798 - 802, Update the search branch in the onSessionStatusesUpdated setSessions callback to use applySearchFilter instead of filterSessionsLocally, preserving deep prompt-text matches when session statuses change during an active search; leave the non-search updated path unchanged.
🧹 Nitpick comments (1)
src/switcher-ui.tsx (1)
1295-1302: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winUse
allSessionsRef.currentinstead ofallSessionsfor consistency.Line 1300 passes
allSessions(React state) toapplySearchFilter, while the debounced callback at line 451 and the initial load at line 513 useallSessionsRef.current. If an async callback (e.g.,detectActiveSessions) updatesallSessionsRef.currentbetween renders, the synchronousonChangehandler would use a staleallSessionssnapshot. The ref-mirroring pattern exists precisely for this scenario.Based on learnings,
allSessionsRefis intentionally kept in sync withsetAllSessionsto ensure async IPC callbacks read the latest state; using the ref here is the safer choice.♻️ Use ref for consistency with other call sites
onChange={(e) => { const val = e.target.value; setSessionSearchValue(val); sessionSearchRef2.current = val; scheduleDeepSearch(val); - setSessions(applySearchFilter(allSessions, val)); + setSessions(applySearchFilter(allSessionsRef.current, val)); setSelectedSessionIndex(0); }}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/switcher-ui.tsx` around lines 1295 - 1302, Update the session search onChange handler to pass allSessionsRef.current to applySearchFilter instead of the potentially stale allSessions state value, while preserving the existing search, scheduling, and selection-reset behavior.Source: Learnings
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/switcher-ui.tsx`:
- Around line 1381-1392: Reformat the fold-header div’s inline style object in
the relevant fold-header render block to match Prettier’s expected indentation,
including all properties from padding through backgroundColor. Do not change the
style values or behavior.
---
Outside diff comments:
In `@src/switcher-ui.tsx`:
- Around line 798-802: Update the search branch in the onSessionStatusesUpdated
setSessions callback to use applySearchFilter instead of filterSessionsLocally,
preserving deep prompt-text matches when session statuses change during an
active search; leave the non-search updated path unchanged.
---
Nitpick comments:
In `@src/switcher-ui.tsx`:
- Around line 1295-1302: Update the session search onChange handler to pass
allSessionsRef.current to applySearchFilter instead of the potentially stale
allSessions state value, while preserving the existing search, scheduling, and
selection-reset behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
Sticky header only pins while scrolled inside the minors zone; the new bar sits outside the scroll area so collapsing is one click from any scroll position. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
All reported issues were addressed across 1 file (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Review round: exec -> execFile (grep args, no shell interpolation); branch tail via in-process 256KB read (no spawn); failures resolve null and skip the fresh-mark so they retry next pass; fold controls get role=button + tabIndex + Enter/Space; fold styles extracted to consts (fixes flagged prettier indents). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Review round: readTailUtf8 -> fs.promises (no sync IO on the main event loop; joins the per-file Promise.all); an explicit gitBranch HEAD in the tail now clears the stale branch, while a tail with no gitBranch line keeps the old value (usually transient tool output). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
Session-finding Batch 1 — "search & noise" (plan:
docs/session-finding-plan.md, included in this PR). Fixes #131.1. Full search: ALL sessions × ALL user prompts (fixes #131)
Before: the search box only filtered the ~100 loaded sessions' visible fields (
filterSessionsLocally), and the main-sidesearchClaudeSessions(500-pool) was dead code — ~314 of 414 sessions were unfindable.Now, dual-path union:
searchClaudeSessions): scans every session's every prompt (fromhistory.jsonl, all accounts), debounced 180ms, stale-response guarded. Prompt text stays in the main process (promptsBySessionmap rebuilt with the existing 5s session cache) — only small snippets cross IPC.2. Matched-prompt snippet line
When the hit is in a middle prompt (invisible in the row), the row shows
⌕ #N …±40 chars context…so you can see why it matched. Suppressed when it would duplicate the visible first/last lines (respects the first/last/both display-mode setting).3. High-contrast search highlight
All 9
react-highlight-wordssites now share one amber-background/dark-text style (SEARCH_HIGHLIGHT_STYLE). The old per-field translucent styles were near-invisible on colored text.4. Minor-session folding
Closed, untitled, PR-less sessions with ≤2 messages collapse into an expandable
▸ N minor sessionsrow at the bottom (recency order preserved inside). Search always shows everything. Conservative: unknownmessageCountand active sessions never fold; folding waits for the first active-session detection. Fold UX: sticky boundary header + an always-visible fold bar below the list while expanded; folding resets on each popup show; fold controls are keyboard-accessible. Manual per-session hide arrives with the pins PR (Batch 1 PR-2).5. Added during live testing: enrichment dropout + perf fixes (pre-existing bugs, #134)
Testing surfaced that custom titles / branches vanished at random and the cold path took 8–10s. Fixed here since search quality depends on these fields:
execgreps starving the biggest transcripts past a silent timeout) and a 50-line→tail-read branch window (an active session's tail is often tool output with nogitBranchfield).statpass replaces the 5s wall-clock TTL, whose entry-time stamp made any >5s scan stale on arrival); concurrent callers serialize onto one accumulated cache. Cold start measured 8–10s → ~4s, and it's now once per app launch.execFile+ in-process tail): no interpolated paths near a shell; timed-out/failed reads don't mark the transcript scanned — they retry next pass.Implementation notes
src/session-search.ts(no fs/electron):matchesAllWords/extractSnippet/findPromptMatch/isMinorSession— unit-testable.searchClaudeSessionsreturn shape changed ({sessions, snippets}); it was previously uncalled, so no consumers were affected. IPC handler/preload are passthrough;electron-api.d.tsupdated.history.jsonl— full transcript search is Batch 2 (FTS5, plan §5.2).Known trade-off (side effect)
Custom titles/branches take slightly longer to first-paint after app launch: the enrichment cold pass is now one bounded scan per launch (~4s measured for ~100 transcripts / ~900MB corpus). The old code often looked faster because its unbounded 400-process burst raced ahead — but it randomly dropped titles/branches (silent timeouts) and, due to the TTL bug, kept rescanning forever. After the one-time scan, updates are incremental (a ~ms stat pass; only changed transcripts are re-read). Eliminating the remaining cold scan is tracked in #134 (persist the cache to disk, or let the Batch-2 FTS index subsume it).
Tests
npx tsc --noEmitclean.🤖 On behalf of @grimmerk — generated with Claude Code
Summary by cubic
Adds full‑prompt search across all sessions with clearer matches and less noise in the Sessions switcher, plus safer and faster metadata enrichment. Fixes #131 and #134.
New Features
react-highlight-wordssites.Bug Fixes
execFilereads, and an async in‑process 256KB tail for branch; explicit detachedHEADclears stale branches; failures don’t mark a file fresh and retry on the next pass.Written for commit 0ce8447. Summary will update on new commits.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Release