feat(sessions): Batch 1 — full-prompt search, match snippets, junk fold, highlight fix

Merged
GR
4mo ago
+930-148

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-side searchClaudeSessions (500-pool) was dead code — ~314 of 414 sessions were unfindable.

Now, dual-path union:

  • Main-side deep search (rewritten searchClaudeSessions): scans every session's every prompt (from history.jsonl, all accounts), debounced 180ms, stale-response guarded. Prompt text stays in the main process (promptsBySession map rebuilt with the existing 5s session cache) — only small snippets cross IPC.
  • Renderer local filter (unchanged) still covers enrichment-only fields: custom title, branch, PR link, last AI reply, terminal badge.
  • Deep matches beyond the loaded list are appended with lazy enrichment (both bounded by the deep-search result cap, 100), everything re-sorts by recency.

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-words sites 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 sessions row at the bottom (recency order preserved inside). Search always shows everything. Conservative: unknown messageCount and 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:

  • Bounded scan batches (was ~400 concurrent exec greps 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 no gitBranch field).
  • mtime-keyed per-file cache: unchanged transcripts are never re-scanned (a ~ms stat pass 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.
  • Shell-free reads (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

  • New pure module src/session-search.ts (no fs/electron): matchesAllWords / extractSnippet / findPromptMatch / isMinorSession — unit-testable.
  • searchClaudeSessions return shape changed ({sessions, snippets}); it was previously uncalled, so no consumers were affected. IPC handler/preload are passthrough; electron-api.d.ts updated.
  • Keyboard navigation (↑↓/PageUp/PageDown/Enter) switched to the displayed list so indexes stay aligned with the fold.
  • Not covered here (by design): closed VS Code sessions' prompts aren't in 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

  • 10 new unit tests for the pure search/fold helpers (AND semantics, 2-char CJK match, snippet ellipses/whitespace, prompt-index attribution, conservative fold predicate) — 45 total pass.
  • npx tsc --noEmit clean.

🤖 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

    • Search scans all sessions and every user prompt in the main process, unions with the local field filter, and appends deep matches with lazy enrichment (debounced 180ms).
    • Rows show a matched‑prompt snippet (⌕ #N …context…) when the hit isn’t visible; suppressed if it duplicates the first/last lines.
    • Unified high‑contrast search highlight across all react-highlight-words sites.
    • Minor‑session folding: closed, untitled, PR‑less sessions with ≤2 messages collapse into an expandable “minor sessions” row; sticky header while scrolled, an always‑visible fold bar below the list when expanded, folds again on popup show; search always shows everything.
  • Bug Fixes

    • Prevented focus‑triggered list resets by reading the current search from a ref.
    • Fold only after the first active‑session detection so a just‑started session never folds at app start.
    • Deep‑match enrichment no longer silently caps; aligned with append path and bounded by the search result cap (100).
    • Stopped random title/branch dropouts and sped up cold starts: mtime+size‑keyed enrichment cache with serialized scans (batches of 25), shell‑free execFile reads, and an async in‑process 256KB tail for branch; explicit detached HEAD clears stale branches; failures don’t mark a file fresh and retry on the next pass.

Summary by CodeRabbit

  • New Features

    • Search now scans every session and every previously typed user prompt (not limited to the loaded subset).
    • Search results include contextual matched-prompt snippets and improved high-contrast highlighting.
    • Added debounced “deep search” that expands results with lazy enrichment and supports minor-session folding for short one-offs.
  • Bug Fixes

    • Search matching now consistently uses full-text, AND-style word semantics across prompts.
    • Snippets correctly handle ellipses and whitespace normalization for clearer context.
  • Documentation

    • Updated the changelog and README to reflect full coverage, snippet context, and minor-session grouping.
  • Release

    • Version bumped to 1.0.83.