cmux-next: Search All Windows searches as you type across terminals, agent sessions and tabs

Merged
AU
16d ago
+1093-259

Summary

Brings cmux-next's Search All Windows (⌥⌘F) up to parity with classic cmux's global search.

Before: ⌥⌘F first asked for a text argument. Return then showed a fixed page of raw terminal lines (up to 20 per terminal). Claude Code and Codex sessions were mostly not found, because a TUI's terminal history holds redraws, not the conversation. Browser and chat tabs were not searched at all.

After:

  • Live search. ⌥⌘F opens the page with the cursor in the search field. Every keystroke searches again, and an edit cancels the previous search. Classic did this with an 80 ms debounce.
  • What it searches. Every tab on every machine, including workspaces no window shows:
    • every tab's title and workspace name, plus the address for browser tabs;
    • each terminal's last 4,000 history rows and its screen, as before;
    • the transcript of each Claude Code or Codex session running in a terminal on this Mac: ~/.claude/projects/…/<id>.jsonl and ~/.codex/sessions/…/rollout-…-<id>.jsonl, found through the session id the agent hook reports. Classic searched these too.
  • Read once, then searched in memory. All of that is read when the page opens, then searched off the main actor on each keystroke. Typing never reads a daemon or a file.
  • Rows. One row per tab, like classic, ordered with title matches first:
    • the newest matching line, cut to a snippet around the match, with the typed words highlighted;
    • subtitled "Workspace › Tab", plus "· Claude" or "· Codex" for a transcript match;
    • "N matches" when the tab has more than one.
  • Choosing a row shows the tab. For terminal text it also runs Find for the match. Transcript and title matches only show the tab, as in classic.
  • CLI. --arg text=… still works, now as the starting query.

Palette change: PalettePageSpec.queryResults is a new async hook for pages that answer the query themselves. Their rows show in the order the page returns them, not the order the ranker gives the providers' rows.

Not in this PR: browser page text. Classic indexed DOM text, but cmux-next has no cross-engine page-text read yet. Browser tabs are found by title and address.

Testing

  • PaletteQueryResultsTests (new): typed queries reach the page and its rows keep the page's order with highlights; an older query's slow answer never replaces a newer one's.
  • Existing suites run in the same fleet step: CmdPListsOpenItemsTests, PaletteSearchScopeTests, SearchAllWindowsKeyTests, ActionCatalogTests.

Observed / Not run

Pending: fleet step d89548813931cefe35ba7215 (compile plus the suites above) at f2db055c is queued. Not yet run: GUI dogfood in a tagged build.

Changelog

Changed: Search All Windows (⌥⌘F) in cmux NEXT searches as you type across every terminal, Claude Code and Codex session, and tab title.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Brings cmux-next's Search All Windows (⌥⌘F) to classic cmux's global search behavior: it opens a live palette page that searches as you type, instead of first asking for a text argument.

  • Each keystroke searches every terminal's history and screen, every Claude Code and Codex session transcript on this Mac, and every tab's title and workspace name. Reading happens once when the page opens (capped at the newest 4 MB of a transcript, terminals on a disconnected daemon counted as unread), and typing searches that copy off the main actor while the field's spinner shows.
  • Results show one row per tab, title matches first, with the newest matching line snippet, match count, and highlights. Choosing a row shows the tab and runs Find for terminal text; the read copy drops when the page closes.
  • The CLI's --arg text=… still works as the starting query, and the palette gains an optional queryResults hook for pages that answer the query themselves, so an older query's answer never replaces a newer one's.