[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-93085":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":17,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":20,"hasPages":20,"topics":22,"createdAt":10,"pushedAt":10,"updatedAt":38,"readmeContent":39,"aiSummary":40,"trendingCount":16,"starSnapshotCount":16,"syncStatus":15,"lastSyncTime":41,"discoverSource":42},93085,"ccmux","epilande\u002Fccmux","epilande","🔮 Track all your AI coding agents (Claude Code, Codex, Cursor, ...) in tmux and jump to the one that needs you","",null,"TypeScript",52,3,51,2,0,1,41.91,"MIT License",false,"main",[23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"ai-agents","bun","claude","claude-code","cli","codex","cursor","developer-tools","gemini-cli","opencode","opentui","session-manager","terminal","tmux","tui","2026-07-22 04:02:08","\u003Cdiv align=\"center\">\n  \u003Ch1>ccmux 🔮\u003C\u002Fh1>\n\u003C\u002Fdiv>\n\n\u003Cp align=\"center\">\n  \u003Cstrong>Track all your AI coding agents (Claude Code, Codex, Cursor, ...) in tmux and jump to the one that needs you\u003C\u002Fstrong>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Cimg alt=\"ccmux picker showing live agent sessions grouped by project\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002Fe4c41e9c-9221-47e2-86d4-362dd799651a\" width=\"900\">\n\u003C\u002Fp>\n\n## ❓ Why?\n\nWhen running multiple AI coding agent sessions across tmux panes, it's hard to keep track of which session is idle, which is waiting for permission, and which pane to switch to. `ccmux` solves this with a background daemon that monitors session activity and an interactive TUI that shows live session states at a glance.\n\nIt works with your existing tmux workflow. You don't change how you launch or run your agents; ccmux discovers what's already running in your panes, so as long as you're in tmux with a supported agent, it just works.\n\n**Built-in support for:** Claude Code, Codex, Cursor, OpenCode, Pi, Gemini CLI, plus [custom agent definitions](#-custom-agents) via config.\n\n## ✨ Features\n\n- 🎯 **Live Session States**: Every agent tracked as idle, working, or waiting (permission \u002F plan approval \u002F question), flagged the moment one needs you\n- 🧩 **Multi-Agent**: Claude Code, Codex, Cursor, OpenCode, Pi, Gemini CLI, plus custom agents via config\n- 🔄 **Real-Time**: Background daemon streams state changes instantly over SSE, no polling, no refresh\n- 👁️ **Live Preview**: Split-pane view of the selected session's pane content\n- ⚡ **Act in Place**: Tab into the preview to approve, answer, or type, keys go straight to that pane\n- 📊 **Sidebar Mode**: Compact always-visible session rail docked beside your working panes\n- 🔍 **Fuzzy Search**: Fuzzy-match sessions by project, branch, or path; substring-match any recent prompt, captured pane content, and on-demand live transcripts\n- 📂 **Session Grouping**: Collapsible project groups with reordering and pinning\n- 🌿 **Git & PR Aware**: Branch and worktree detection, open PRs with live CI and review status\n- 📝 **Diff Review**: Press \u003Ckbd>d\u003C\u002Fkbd> to review a session's working-tree diff with [hunk](https:\u002F\u002Fgithub.com\u002Fmodem-dev\u002Fhunk), right in the pane\n- 🤖 **Background Agents & Subagents**: Claude Code background agents get rows too; nested Task agent waiting states surface\n- 🔁 **Session Control**: Spawn, kill, and restart sessions from the TUI; `ccmux invoke` for scripted one-shot agent turns\n- ⌨️ **Keyboard-First, Mouse-Friendly**: Vim keys and number jumps, plus click-to-switch and right-click context actions\n\n## 📦 Installation\n\n### Prerequisites\n\n- [tmux](https:\u002F\u002Fgithub.com\u002Ftmux\u002Ftmux) with active sessions running AI coding agents\n\n### Homebrew\n\n```sh\nbrew install epilande\u002Ftap\u002Fccmux\nccmux setup\n```\n\n### From Source\n\nRequires [Bun](https:\u002F\u002Fbun.sh).\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fepilande\u002Fccmux.git\ncd ccmux\nbun install\nbun link\nccmux setup\n```\n\n`ccmux setup` installs agent hooks for authoritative session matching. ccmux works without it, but it's recommended; see [Session Matching with Hooks](#-session-matching-with-hooks). Bare `ccmux setup` only configures agents whose executable is found on PATH; use `ccmux setup --agent \u003Cname>` to install for a specific agent even if it isn't detected.\n\n## 🚀 Quick Start\n\n1. Start your AI coding sessions in tmux panes as usual\n2. Launch the picker:\n   ```bash\n   ccmux\n   ```\n3. Navigate with \u003Ckbd>j\u003C\u002Fkbd>\u002F\u003Ckbd>k\u003C\u002Fkbd>, press \u003Ckbd>Enter\u003C\u002Fkbd> to switch to a session\n\n> [!TIP]\n> Bind a tmux key so you can pop ccmux open from anywhere (add to `~\u002F.tmux.conf`):\n>\n> ```tmux\n> # Prefix + C-p: open ccmux in a centered popup\n> bind-key C-p display-popup -E -w 80% -h 75% \"ccmux\"\n>\n> # Or skip the prefix entirely (Alt+p from any pane)\n> bind-key -n M-p display-popup -E -w 80% -h 75% \"ccmux\"\n> ```\n>\n> The picker exits after you select a session, so the popup closes itself and drops you straight into that pane. (`display-popup` requires tmux 3.2+.)\n\n## 🎮 Usage\n\n### CLI Commands\n\n| Command                                     | Description                                                                                     |\n| :------------------------------------------ | :---------------------------------------------------------------------------------------------- |\n| `ccmux`                                     | Launch interactive TUI picker (default)                                                         |\n| `ccmux picker`                              | Launch TUI with options (`--preview`, `--icons \u003Cstyle>`)                                        |\n| `ccmux picker --persistent`                 | Dashboard mode (stay open after switching sessions)                                             |\n| `ccmux spawn [agent]`                       | Spawn a new agent session in a tmux pane                                                        |\n| `ccmux invoke [agent] \"prompt\"`             | Run a single agent turn and write the response to stdout ([docs](docs\u002Finvoke.md))               |\n| `ccmux invoke list`                         | List active and recently-finished invocations (`-j` for JSON)                                   |\n| `ccmux invoke cancel \u003Cid>`                  | Cancel a running invocation by id (idempotent)                                                  |\n| `ccmux invoke result \u003Cid>`                  | Print an invocation's full captured output (subprocess agents only)                             |\n| `ccmux show`                                | List all active sessions                                                                        |\n| `ccmux show --json`                         | Output sessions as JSON                                                                         |\n| `ccmux status`                              | Show daemon and session overview                                                                |\n| `ccmux switch \u003Cid>`                         | Switch tmux client to a session's pane                                                          |\n| `ccmux review [id]`                         | Review a session's diff with [hunk](https:\u002F\u002Fgithub.com\u002Fmodem-dev\u002Fhunk) (defaults to cwd)        |\n| `ccmux kill \u003Cid>`                           | Kill a session's process                                                                        |\n| `ccmux restart \u003Cid>`                        | Kill and resume a session                                                                       |\n| `ccmux send \u003Cid> \u003Ctext>`                    | Send text to a session's tmux pane                                                              |\n| `ccmux screen [id]`                         | Capture pane content                                                                            |\n| `ccmux screen --grep \u003Cpattern>`             | Search across all session panes                                                                 |\n| `ccmux dismiss \u003Cid>`                        | Remove a session from tracking                                                                  |\n| `ccmux daemon start\\|stop\\|restart\\|status` | Manage the background daemon                                                                    |\n| `ccmux config set \u003Ckey> \u003Cvalue>`            | Set a preference                                                                                |\n| `ccmux config get \u003Ckey>`                    | Get a single preference value                                                                   |\n| `ccmux config list`                         | List all preferences                                                                            |\n| `ccmux config themes`                       | List built-in themes (marks the active one)                                                     |\n| `ccmux setup`                               | Install hooks for every supported agent found on PATH (Claude + Codex + Cursor + OpenCode + Pi) |\n| `ccmux setup --agent \u003Cname>`                | Limit install\u002Funinstall\u002Fstatus to specific agent(s); forces install even if not found on PATH   |\n| `ccmux setup --status`                      | Report install state without writing anything                                                   |\n| `ccmux setup --uninstall`                   | Remove hooks (preserves user-owned hook entries)                                                |\n| `ccmux debug`                               | Diagnose session tracking discrepancies                                                         |\n| `ccmux sidebar`                             | Launch narrow sidebar TUI (no preview\u002Ffooter)                                                   |\n| `ccmux sidebar --toggle`                    | Smart toggle: spawn\u002Fkill sidebars in every window across all tmux sessions                      |\n\nThe daemon starts automatically the first time you run a ccmux command (picker, show, invoke, etc.). It runs on `127.0.0.1:2269` and provides both a REST API and SSE event stream.\n\n### Preview Pane\n\nPress \u003Ckbd>P\u003C\u002Fkbd> to split the picker and preview the highlighted session's live pane content side by side. Press \u003Ckbd>Tab\u003C\u002Fkbd> to focus the preview and act in place: your keystrokes go straight to that agent's pane, so you can approve a permission, answer a question, or type a follow-up without ever leaving ccmux.\n\nhttps:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F7e0d42b3-4e7b-43b8-8d06-72a2d69dd694\n\n### Diff Review with Hunk\n\n[hunk](https:\u002F\u002Fgithub.com\u002Fmodem-dev\u002Fhunk) is a terminal diff reviewer. With `hunk` on your `PATH`, press \u003Ckbd>d\u003C\u002Fkbd> in the picker to review the selected session's working-tree diff without leaving ccmux: the picker suspends, `hunk diff --watch` takes over the pane in the session's repository root, and the picker resumes when hunk exits. The same action is available from the right-click context menu.\n\nThe review also runs from the CLI:\n\n```bash\nccmux review          # Review the current directory's repository\nccmux review \u003Cid>     # Review a session's repository by id\n```\n\nInstall hunk with `brew install modem-dev\u002Ftap\u002Fhunk`. The \u003Ckbd>d\u003C\u002Fkbd> footer hint and help entry appear only when hunk is detected on `PATH` at launch.\n\n### Sidebar Mode\n\nA compact, always-visible session list that lives alongside your working panes. No preview panel, no footer, just status icons and project names.\n\n\u003Cp align=\"center\">\n  \u003Cimg alt=\"ccmux sidebar alongside working panes\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F742642cf-b90b-445d-bc9a-38b6cfa0ab79\" width=\"900\">\n\u003C\u002Fp>\n\n```bash\nccmux sidebar --toggle                  # Toggle sidebars in all tmux windows\nccmux sidebar --toggle --width 40       # Custom width (default: 30)\nccmux sidebar --toggle --position right # Right side (default: left)\nccmux sidebar --resize --width 30       # Snap every existing sidebar pane to \u003Cwidth>\n```\n\nThe smart toggle fills gaps when some windows are missing sidebars, and kills all sidebars when every window already has one. New windows automatically get a sidebar, and sidebars snap back to their configured width when a window is resized.\n\nConfigure defaults so `--toggle` uses your preferred layout:\n\n```bash\nccmux config set sidebar.width 40\nccmux config set sidebar.position right\n```\n\n**Suggested tmux keybinding** (add to `~\u002F.tmux.conf`):\n\n```tmux\nbind-key S run-shell \"ccmux sidebar --toggle\"\n```\n\n### Search Mode\n\nPress \u003Ckbd>\u002F\u003C\u002Fkbd> to filter the list as you type. ccmux searches several sources at once and highlights why each row matched:\n\n- **Metadata** (project, branch, path) matches fuzzily, so `ccmx` still finds `ccmux`.\n- **Recent prompts, captured pane content, and live transcripts** match by substring, so a content word matches only where it actually appears.\n\nPrompts come from the daemon's in-memory index, which keeps the most recent prompts per session and is tail-bounded after a daemon restart (only recent prompts are re-read from disk). Transcript search closes that gap: it reads each session's transcript file on demand and covers the full session history, including assistant replies (Claude and Codex).\n\nThree toggles control what gets scanned: `searchPaneContent`, `searchPaneLines`, and `searchTranscript` (see [Configuration](#-configuration)).\n\n### Spawning Sessions\n\nLaunch new agent sessions directly from the CLI:\n\n```bash\nccmux spawn                          # Spawn claude (default) in a new tmux window\nccmux spawn codex                    # Spawn a specific agent\nccmux spawn --split                  # Split current pane instead of new window\nccmux spawn --detach                 # Don't switch to the new pane\nccmux spawn --cwd ~\u002Fproj             # Set working directory\nccmux spawn --resume \u003Cid>            # Resume an existing session\nccmux spawn --prompt \"fix the tests\" # Send an initial prompt\n```\n\n### Programmatic Invocation\n\n`ccmux invoke` runs a single agent turn and writes the response to stdout, so you can use real agents in shell pipelines and scripts. See [`docs\u002Finvoke.md`](docs\u002Finvoke.md) for the full reference.\n\n```bash\nccmux invoke claude \"say hi in one word\"\necho \"what is 2 + 2\" | ccmux invoke claude\ngit diff main | ccmux invoke claude \"Review this diff\"\n```\n\nClaude runs interactively in a dedicated tmux session and returns clean text parsed from the transcript JSONL. Codex, Cursor, OpenCode, Pi, and Gemini run as non-interactive subprocesses (`codex exec -o`, `cursor-agent --print`, `opencode run --format json`, `pi -p`, `gemini -p`) and return the agent's clean response text.\n\nFor orchestration, name an invocation with `--id \u003Cid>`, then use `ccmux invoke list`, `ccmux invoke cancel \u003Cid>`, and `ccmux invoke result \u003Cid>` to watch, cancel, or read its full captured output by that id. See [`docs\u002Finvoke.md`](docs\u002Finvoke.md#fire-and-poll---id-list-cancel-result) for the fire-and-poll reference.\n\n### Dispatch Skill\n\nThis repo ships a `dispatch` [Agent Skill](https:\u002F\u002Fagentskills.io) that teaches your coding agent to orchestrate other agents through `ccmux invoke` (firing, fan-out, joining, cancelling, and reading worker output). For Claude Code it installs as a plugin (this repo doubles as a plugin marketplace):\n\n```\n\u002Fplugin marketplace add epilande\u002Fccmux\n\u002Fplugin install ccmux@ccmux\n```\n\nOther skills-capable agents (Codex, Cursor, OpenCode, and others) can use the same skill by copying it into their skills directory. The skill is additive glue for the ccmux CLI, which must be installed and on your `PATH`. See [`plugins\u002Fccmux\u002FREADME.md`](plugins\u002Fccmux\u002FREADME.md) for details.\n\n## ⌨️ Keyboard Controls\n\n| Action                | Key                                                                                | Description                                                                                        |\n| :-------------------- | :--------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- |\n| Navigate              | \u003Ckbd>j\u003C\u002Fkbd> \u002F \u003Ckbd>k\u003C\u002Fkbd> or \u003Ckbd>↑\u003C\u002Fkbd> \u002F \u003Ckbd>↓\u003C\u002Fkbd>                         | Move through session list                                                                          |\n| Jump to first\u002Flast    | \u003Ckbd>g\u003C\u002Fkbd>\u003Ckbd>g\u003C\u002Fkbd> \u002F \u003Ckbd>G\u003C\u002Fkbd>                                            | Go to top \u002F bottom                                                                                 |\n| Jump to session       | \u003Ckbd>1\u003C\u002Fkbd>–\u003Ckbd>9\u003C\u002Fkbd>                                                          | Switch directly to session N                                                                       |\n| Switch to session     | \u003Ckbd>Enter\u003C\u002Fkbd>                                                                   | Switch tmux to the selected pane                                                                   |\n| Search                | \u003Ckbd>\u002F\u003C\u002Fkbd>                                                                       | Enter fuzzy search mode                                                                            |\n| Toggle preview        | \u003Ckbd>P\u003C\u002Fkbd>                                                                       | Show\u002Fhide the preview panel                                                                        |\n| Scroll preview        | \u003Ckbd>Ctrl+D\u003C\u002Fkbd> \u002F \u003Ckbd>Ctrl+U\u003C\u002Fkbd>                                              | Half-page scroll in preview                                                                        |\n| Resize preview        | \u003Ckbd>Alt+H\u003C\u002Fkbd> \u002F \u003Ckbd>Alt+L\u003C\u002Fkbd>                                                | Increase\u002Fdecrease preview width                                                                    |\n| Focus preview         | \u003Ckbd>Tab\u003C\u002Fkbd>                                                                     | Send keys directly to tmux pane                                                                    |\n| Restart session       | \u003Ckbd>r\u003C\u002Fkbd>                                                                       | Kill and resume the selected session                                                               |\n| Reconnect             | \u003Ckbd>R\u003C\u002Fkbd>                                                                       | Reconnect to the daemon SSE stream                                                                 |\n| Kill session          | \u003Ckbd>x\u003C\u002Fkbd>                                                                       | Kill the selected session's process                                                                |\n| Kill all              | \u003Ckbd>X\u003C\u002Fkbd>                                                                       | Kill all tracked sessions                                                                          |\n| Review diff           | \u003Ckbd>d\u003C\u002Fkbd>                                                                       | Review the session's diff with [hunk](https:\u002F\u002Fgithub.com\u002Fmodem-dev\u002Fhunk) (requires `hunk` on PATH) |\n| Collapse\u002Fexpand       | \u003Ckbd>h\u003C\u002Fkbd> \u002F \u003Ckbd>l\u003C\u002Fkbd> or \u003Ckbd>Space\u003C\u002Fkbd>                                    | Toggle group collapsed state                                                                       |\n| Move group            | \u003Ckbd>J\u003C\u002Fkbd> \u002F \u003Ckbd>K\u003C\u002Fkbd>                                                        | Reorder group down \u002F up (persisted)                                                                |\n| Move group top\u002Fbottom | \u003Ckbd>\u003C\u003C\u002Fkbd> \u002F \u003Ckbd>>\u003C\u002Fkbd>                                                        | Pin group to top \u002F bottom                                                                          |\n| Collapse\u002Fexpand all   | \u003Ckbd>z\u003C\u002Fkbd>\u003Ckbd>M\u003C\u002Fkbd> \u002F \u003Ckbd>z\u003C\u002Fkbd>\u003Ckbd>R\u003C\u002Fkbd> or \u003Ckbd>-\u003C\u002Fkbd> \u002F \u003Ckbd>=\u003C\u002Fkbd> | Collapse or expand all groups                                                                      |\n| Hide idle             | \u003Ckbd>f\u003C\u002Fkbd>                                                                       | Toggle hiding idle sessions                                                                        |\n| Cycle prompt          | \u003Ckbd>p\u003C\u002Fkbd>                                                                       | Prompt display: inline → own row → off                                                             |\n| Cycle group-by        | \u003Ckbd>b\u003C\u002Fkbd>                                                                       | Cycle through group-by modes                                                                       |\n| Help                  | \u003Ckbd>?\u003C\u002Fkbd>                                                                       | Show keyboard shortcuts overlay                                                                    |\n| Quit                  | \u003Ckbd>q\u003C\u002Fkbd> \u002F \u003Ckbd>Esc\u003C\u002Fkbd>                                                      | Exit the picker                                                                                    |\n\n\u003Cdetails>\n\u003Csummary>\u003Cstrong>Search mode keys\u003C\u002Fstrong>\u003C\u002Fsummary>\n\n| Action           | Key                                   |\n| :--------------- | :------------------------------------ |\n| Navigate results | \u003Ckbd>Ctrl+N\u003C\u002Fkbd> \u002F \u003Ckbd>Ctrl+P\u003C\u002Fkbd> |\n| Select           | \u003Ckbd>Enter\u003C\u002Fkbd>                      |\n| Cancel           | \u003Ckbd>Esc\u003C\u002Fkbd>                        |\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>\u003Cstrong>Preview focus mode\u003C\u002Fstrong>\u003C\u002Fsummary>\n\nWhen preview is focused (\u003Ckbd>Tab\u003C\u002Fkbd>), keystrokes are forwarded to the tmux pane. These keys still work:\n\n| Action            | Key                                   |\n| :---------------- | :------------------------------------ |\n| Navigate sessions | \u003Ckbd>Ctrl+N\u003C\u002Fkbd> \u002F \u003Ckbd>Ctrl+P\u003C\u002Fkbd> |\n| Resize preview    | \u003Ckbd>Alt+H\u003C\u002Fkbd> \u002F \u003Ckbd>Alt+L\u003C\u002Fkbd>   |\n| Scroll preview    | \u003Ckbd>Ctrl+D\u003C\u002Fkbd> \u002F \u003Ckbd>Ctrl+U\u003C\u002Fkbd> |\n| Exit focus        | \u003Ckbd>Tab\u003C\u002Fkbd> \u002F \u003Ckbd>Esc\u003C\u002Fkbd>       |\n\n\u003C\u002Fdetails>\n\n## ⚙️ Configuration\n\nPreferences are stored in `~\u002F.config\u002Fccmux\u002Fccmux.json` and can be managed with:\n\n```bash\nccmux config set \u003Ckey> \u003Cvalue>\nccmux config get \u003Ckey>\nccmux config list\n```\n\n| Key                          | Values                                                                       | Default            | Description                                                                                                                        |\n| :--------------------------- | :--------------------------------------------------------------------------- | :----------------- | :--------------------------------------------------------------------------------------------------------------------------------- |\n| `iconStyle`                  | `dot`, `emoji`, `nerdfont`, `none`                                           | `dot`              | Status icon style                                                                                                                  |\n| `theme`                      | `catppuccin-*`, `tokyo-night*`, `dracula`, `gruvbox-*`, `nord`, `rose-pine*` | `catppuccin-mocha` | TUI color theme (resolved at launch; see [Theme](#-theme))                                                                         |\n| `showPreview`                | `true`, `false`                                                              | `false`            | Show preview panel on launch                                                                                                       |\n| `previewWidth`               | `20`–`80`                                                                    | `40`               | Preview panel width (percentage)                                                                                                   |\n| `command`                    | any non-blank string                                                         | `claude`           | CLI command used for session restart                                                                                               |\n| `groupBy`                    | `project`, `cwd`, `session`, `window`, `none`                                | `project`          | How sessions are grouped in the TUI                                                                                                |\n| `promptDisplay`              | `inline`, `row2`, `off`                                                      | `inline`           | Prompt display: inline on row 1, its own row, or hidden                                                                            |\n| `backgroundAgents`           | `true`, `false`                                                              | `true`             | Show Claude background agents as rows (daemon restart required)                                                                    |\n| `additionalClaudeConfigDirs` | array of paths                                                               | `[]`               | Additional Claude config dirs to watch (daemon restart required; see [Multiple Claude Config Dirs](#-multiple-claude-config-dirs)) |\n| `searchPaneContent`          | `true`, `false`                                                              | `true`             | Include captured pane content in TUI search                                                                                        |\n| `searchPaneLines`            | `10`–`500`                                                                   | `100`              | Lines of pane content scanned in TUI search                                                                                        |\n| `searchTranscript`           | `true`, `false`                                                              | `true`             | Search live Claude\u002FCodex transcripts (full history + assistant text) via the daemon                                                |\n| `persistent`                 | `true`, `false`                                                              | `false`            | Keep picker open after switching sessions (dashboard mode)                                                                         |\n| `sidebar.width`              | `10`–`80`                                                                    | `30`               | Sidebar pane width in columns                                                                                                      |\n| `sidebar.position`           | `left`, `right`                                                              | `left`             | Which side of the window to place the sidebar                                                                                      |\n\nFor how these search knobs interact, see [Search Mode](#search-mode).\n\n### 📊 Column Configuration\n\nEach session item has up to two rows (`row1`, `row2`), and each row has a `left` and `right` side. Each side is a comma-separated list of field entries. An entry is either `\u003Cfield>` (use the field's default mode) or `\u003Cfield>:\u003Cmode>` (override the mode).\n\n```bash\nccmux config set columns.row1.left  \"index,status:icon,project\"\nccmux config set columns.row1.right \"agent:short,pane,time\"\nccmux config set columns.row2.left  \"prompt\"\nccmux config set columns.row2.right \"branch\"\n```\n\nPass an empty string to clear a side: `ccmux config set columns.row2.left \"\"`.\n\n| Field     | Modes                 | Default mode | Description                              |\n| :-------- | :-------------------- | :----------- | :--------------------------------------- |\n| `index`   | —                     | —            | Row number (1–9)                         |\n| `status`  | `icon`\u002F`short`\u002F`full` | `icon`       | Status badge style                       |\n| `project` | `dirname`\u002F`full`      | `dirname`    | Project path (basename or full)          |\n| `agent`   | `short`\u002F`full`        | `full`       | Agent name (2-char code or full label)   |\n| `version` | —                     | —            | Agent version                            |\n| `pane`    | —                     | —            | Tmux pane target (session:window.pane)   |\n| `time`    | —                     | —            | Relative time since last input           |\n| `prompt`  | —                     | —            | Last user prompt (truncated)             |\n| `cwd`     | —                     | —            | Working directory                        |\n| `branch`  | —                     | —            | Git branch                               |\n| `pr`      | `short`\u002F`full`        | `full`       | Open PRs for the branch (`#25`\u002F`PR #25`) |\n\nDefaults: `row1.left` is `index, status, project` (status badge widens icon→short→full as the terminal grows). `row1.right` cascades by breakpoint: just `pane` below `xs`, then `agent:short, pane` at `xs`, `agent:short, pane, time` at `sm`, and `agent:full, version, pane, time` at `md`+. The `prompt` and `pr` cells are configured on `row2`, but `promptDisplay` (default `inline`, cycled live by \u003Ckbd>p\u003C\u002Fkbd>) controls how they render: `inline` flattens them onto `row1` so each session stays a single line, `row2` gives the prompt its own line with `pr` at the right edge, and `off` hides both. Sessions with no prompt stay single-line in `inline` mode; in `row2` mode the second line still appears when another row-2 field (such as an open PR) has data.\n\nSidebar defaults differ to fit the narrow rail: `row1` is `status, project` with `pr:short, agent:short` on the right (PR stays visible even with the prompt hidden), and `row2` is `prompt` \u002F `time` (a lone `time` never earns the row; it rides along when some other field has data). The 30-col rail has no room to inline, so the sidebar always uses the two-row layout (`inline` behaves like `row2`). Override these under the `sidebar.columns` key in `~\u002F.config\u002Fccmux\u002Fccmux.json` (e.g. `\"sidebar\": { \"columns\": { \"row2\": { \"left\": [\"pane\"] } } }` to bring the pane target back).\n\nThe CLI's comma-separated form sets one mode per entry. To vary the layout by terminal width (responsive cascade), edit `~\u002F.config\u002Fccmux\u002Fccmux.json` directly and use the `default`\u002F`xs`\u002F`sm`\u002F`md`\u002F`lg` keys on either a row side (whole array) or an entry's `mode`.\n\n### 📐 Breakpoints\n\nNamed breakpoints control when responsive column layouts activate. A breakpoint value applies from that terminal width upward until a larger breakpoint overrides it.\n\n| Name | Default width |\n| :--- | :------------ |\n| `xs` | 40            |\n| `sm` | 60            |\n| `md` | 80            |\n| `lg` | 100           |\n\n```bash\nccmux config set breakpoints.sm 55\nccmux config set breakpoints.lg 120\n```\n\n### 🎨 Theme\n\nThe TUI ships 14 built-in palettes across six families, resolved once at launch (no in-TUI toggle).\n\n```bash\nccmux config themes                       # list built-ins, mark the active one\nccmux config set theme tokyo-night        # switch theme\n```\n\n| Theme                  | Background     |\n| :--------------------- | :------------- |\n| `catppuccin-mocha`     | dark (default) |\n| `catppuccin-macchiato` | dark           |\n| `catppuccin-frappe`    | dark           |\n| `catppuccin-latte`     | light          |\n| `tokyo-night`          | dark           |\n| `tokyo-night-storm`    | dark           |\n| `tokyo-night-day`      | light          |\n| `dracula`              | dark           |\n| `gruvbox-dark`         | dark           |\n| `gruvbox-light`        | light          |\n| `nord`                 | dark           |\n| `rose-pine`            | dark           |\n| `rose-pine-moon`       | dark           |\n| `rose-pine-dawn`       | light          |\n\nFor per-key tweaks, set `theme` to an object in `~\u002F.config\u002Fccmux\u002Fccmux.json`: a built-in `base` plus `colors` (the 14 semantic keys) and\u002For `ansi` (the 16 terminal colors used to render the preview), deep-merged over the base.\n\n```json\n{\n  \"theme\": {\n    \"base\": \"catppuccin-mocha\",\n    \"colors\": { \"red\": \"#ff5555\" },\n    \"ansi\": { \"brightBlack\": \"#585b70\" }\n  }\n}\n```\n\nAn unknown base name falls back to the default theme; an invalid hex value or unknown override key is dropped and the base value is kept. Each emits a warning. Run `ccmux config themes` to inspect any problems with the current config.\n\n> [!NOTE]\n> ccmux paints no background fill, so theme colors sit on your terminal's own background. The light palettes (`catppuccin-latte`, `tokyo-night-day`, `gruvbox-light`, `rose-pine-dawn`) assume a light terminal; pair them with a light background. Every other palette assumes a dark one.\n\n### 🗂️ Multiple Claude Config Dirs\n\nClaude Code writes session transcripts to `$CLAUDE_CONFIG_DIR\u002Fprojects` (default `~\u002F.claude\u002Fprojects`), so sessions from a second account (e.g. a personal login launched with `CLAUDE_CONFIG_DIR=~\u002F.claude-personal`) land in a tree ccmux doesn't watch by default. List those dirs in `additionalClaudeConfigDirs` and a single daemon watches every `\u003Cdir>\u002Fprojects` tree:\n\n```bash\nccmux config set additionalClaudeConfigDirs '[\"~\u002F.claude-personal\"]'\nccmux setup --agent claude   # installs hooks into every configured dir\nccmux daemon restart\n```\n\n`~\u002F.claude` is always watched; entries are additional config dirs (`~` paths supported), and a set `CLAUDE_CONFIG_DIR` environment variable is picked up automatically. Sessions are keyed by their globally unique session ID, so the same project opened under two accounts coexists without collision.\n\n> [!NOTE]\n> If you add a dir later, re-run `ccmux setup --agent claude`. The daemon warns at startup about any configured dir still missing hooks.\n\n## 🔗 Session Matching with Hooks\n\nFor reliable session-to-pane mapping (especially with multiple sessions of the same agent in the same project), install hooks:\n\n```bash\nccmux setup                    # Install hooks for every supported agent found on PATH\nccmux setup --agent codex      # Limit to a single agent (installs even if not on PATH)\nccmux setup --status           # Report install state without writing\nccmux setup --uninstall        # Remove hooks\n```\n\nHooks write PID marker files under `~\u002F.config\u002Fccmux\u002Fsession-pids\u002F` whenever a session starts, a turn completes, or the agent asks the user to approve a tool. The daemon picks up the markers in real time via a filesystem watcher. See [`docs\u002Farchitecture.md#hook-lifecycle`](.\u002Fdocs\u002Farchitecture.md#hook-lifecycle) for the full flow (marker writes, chokidar dispatch, per-agent correlation).\n\nGemini CLI is tracked through process detection and terminal pattern matching, so it needs no setup.\n\n### Claude Code\n\nUses Claude's native hooks in `~\u002F.claude\u002Fsettings.json` with three scripts under `~\u002F.claude\u002Fhooks\u002F`:\n\n- `ccmux-session-start.sh`: writes the marker on session create\u002Fresume\n- `ccmux-session-end.sh`: removes the marker\n- `ccmux-state-notify.sh`: updates state on `idle_prompt` \u002F `permission_prompt`\n\n### Codex CLI\n\nUses Codex's native hooks (`~\u002F.codex\u002Fhooks.json` plus the codex hooks feature flag in `~\u002F.codex\u002Fconfig.toml`, which is `[features] codex_hooks = true` pre-0.124 and `[features] hooks = true` on 0.124+; ccmux recognizes either) with three scripts under `~\u002F.codex\u002Fhooks\u002F`:\n\n- `ccmux-session-start.sh`: writes the marker when a Codex session starts\n- `ccmux-stop.sh`: refreshes the marker at the end of every turn\n- `ccmux-permission-request.sh`: marks the session as `waiting_permission` when the user is asked to approve a tool\n\nTool-approval detection (`PermissionRequest`) needs Codex >= 0.122.\n\n### Cursor CLI\n\nUses Cursor's native hooks (`~\u002F.cursor\u002Fhooks.json`) with four scripts under `~\u002F.cursor\u002Fhooks\u002F`:\n\n- `ccmux-session-start.sh`: writes the marker on fresh chat launch\n- `ccmux-session-end.sh`: unlinks the marker when the chat ends\n- `ccmux-before-submit-prompt.sh`: flips state to `working` and records the last prompt (1 KB cap)\n- `ccmux-stop.sh`: refreshes state back to `idle` at turn completion\n\nRequires `cursor-agent` >= 2026.1.16 (when the hooks feature landed).\n\n### OpenCode\n\nUses OpenCode's plugin system rather than shell hooks. `ccmux setup --agent opencode` drops a single auto-discovered JS plugin at `~\u002F.config\u002Fopencode\u002Fplugin\u002Fccmux.js` (honors `$XDG_CONFIG_HOME`). The plugin subscribes to OpenCode's in-process event bus and writes a marker for every session on the server:\n\n- `session.created` \u002F `session.updated`: marker with directory + title\n- `session.status` (busy\u002Fretry\u002Fidle): refreshes state to `working` or `idle`\n- `message.updated` \u002F `message.part.updated`: captures the user's last prompt (1 KB cap) into the marker (parity with Claude\u002FCodex\u002FCursor)\n- `permission.asked` \u002F `permission.replied`: flips state to `waiting_permission` with the pending tool, clears back to `working` on reply\n- `session.deleted`: unlinks the marker\n\nBecause one OpenCode server can host many sessions, the daemon folds all markers sharing a server PID into the single ccmux Session for the tmux pane that hosts the server. Status is worst-of (`waiting > working > idle`); `cwd` and `nativeSessionId` come from the newest-activity marker, while `pendingTool` and the attention indicator come from the newest-waiting marker.\n\n### Pi\n\nUses Pi's extension system rather than shell hooks. `ccmux setup --agent pi` drops a single auto-discovered JS extension at `~\u002F.pi\u002Fagent\u002Fextensions\u002Fccmux.js`. The extension subscribes to Pi's lifecycle events and writes one marker per session:\n\n- `session_start`: marker with the session id, transcript path, and cwd (Pi fires this at launch, so the marker carries full identity immediately)\n- `before_agent_start`: captures the user's last prompt (1 KB cap)\n- `agent_start` \u002F `agent_end`: flips state to `working` \u002F `idle` (these bracket one full user prompt, so the row never flickers mid-response the way per-turn events would)\n- `session_shutdown`: unlinks the marker\n\nPi runs one session per process, so there's no server-style aggregation; the daemon correlates the marker's PID to its tmux pane via process ancestry and links `nativeSessionId`.\n\n### Matching priority (with hooks installed)\n\n1. **Marker file** (authoritative): Direct PID\u002FTTY\u002Fsession-id\u002Ftranscript from the hook, re-verified on every scan so a wrong binding heals itself\n2. **Process start time**: For panes markers don't claim, ccmux correlates session timestamps with agent process start times, matching each same-directory group as a whole within a 10-minute tolerance. When two candidates are too close to call, the session is left unbound rather than guessed.\n\nWithout hooks, the daemon does not use historical session IDs to claim pane ownership. It creates pane-scoped sessions from live process + tmux discovery, then attaches agent log metadata only when it can safely tie a log to the running process.\n\n## 🧩 Custom Agents\n\nThe built-in agents are the happy path: they ship with hook integration for authoritative session matching. If you run an agent ccmux doesn't support out of the box, you can teach it one in `~\u002F.config\u002Fccmux\u002Fccmux.json`. Custom agents fall back to process matching plus terminal pattern scanning (no hooks), so detection is less precise than a built-in, but it gets unsupported agents onto the board.\n\n\u003Cdetails>\n\u003Csummary>\u003Cstrong>Defining a custom agent\u003C\u002Fstrong>\u003C\u002Fsummary>\n\n```json\n{\n  \"agents\": {\n    \"myagent\": {\n      \"processMatch\": \"myagent\",\n      \"terminalRules\": [\n        {\n          \"matchAny\": [\"thinking...\", \"esc to interrupt\"],\n          \"status\": \"working\"\n        },\n        {\n          \"matchAll\": [\"approve?\", \"[y\u002Fn]\"],\n          \"status\": \"waiting\",\n          \"attentionType\": \"permission\",\n          \"pendingTool\": \"Command\"\n        }\n      ],\n      \"resumeCommand\": \"myagent resume {id}\"\n    }\n  }\n}\n```\n\nYou can also override built-in agent settings by using the agent's name as the key (e.g., `\"claude\"`, `\"codex\"`).\n\n| Field                | Required | Description                                          |\n| :------------------- | :------- | :--------------------------------------------------- |\n| `processMatch`       | Yes\\*    | Regex to match the process executable                |\n| `commandPatterns`    | No       | Additional regex patterns to match full commands     |\n| `terminalRules`      | No       | Ordered terminal matching rules                      |\n| `versionCommand`     | No       | Command to get agent version                         |\n| `versionPatterns`    | No       | Regex patterns to extract version from output        |\n| `resumeCommand`      | No       | Command template for restarting (`{id}` placeholder) |\n| `sessionFilePattern` | No       | Regex to extract session ID from log filenames       |\n| `executable`         | No       | Command used to launch the agent (defaults to key)   |\n| `hooks`              | No       | `{ type }` (built-in override only; internal)        |\n\n\\* Required for new agents; optional when overriding built-in agents.\n\nInvoke-related fields (`invokeMode`, `errorRules`, `readyPattern`) are documented in [`docs\u002Finvoke.md`](docs\u002Finvoke.md).\n\nEach `terminalRules` entry must define exactly one matcher:\n\n- `matchAny`: matches when any string is present in the last 30 lines (case-insensitive)\n- `matchAll`: matches only when every string is present in the last 30 lines (case-insensitive)\n\nRules are evaluated top-to-bottom, and the first match wins. This lets you express broad \"working\" prompts and more specific multi-line waiting prompts without detector-specific logic.\n\n\u003C\u002Fdetails>\n\n## 🏗️ Architecture\n\nccmux has three layers: agents running in tmux panes, a background daemon that observes them, and clients (TUI + CLI utilities) that consume daemon state over HTTP\u002FSSE.\n\n\u003Cpicture>\n  \u003Csource media=\"(prefers-color-scheme: dark)\" srcset=\".\u002Fdocs\u002Fsystem-overview-dark.svg\">\n  \u003Cimg alt=\"ccmux system overview\" src=\".\u002Fdocs\u002Fsystem-overview-light.svg\">\n\u003C\u002Fpicture>\n\nThe daemon merges three signals into one session state: log parsing for agents that write JSONL transcripts, terminal pattern matching for agents that don't, and PID marker files written by hook adapters for authoritative session-to-pane mapping. It exposes a local HTTP API with SSE streaming on port 2269. The TUI connects as an SSE client and renders state reactively using Solid.js via the [@opentui\u002Fsolid](https:\u002F\u002Fgithub.com\u002Fanomalyco\u002Fopentui) framework.\n\nFor deeper internals (status detection cascade, session-to-pane binding, hook event lifecycle, PR enrichment, background agents, code map), see [`docs\u002Farchitecture.md`](.\u002Fdocs\u002Farchitecture.md). Per-agent hook quirks and the agent-owned files ccmux reads are in [`docs\u002Fagent-adapters.md`](.\u002Fdocs\u002Fagent-adapters.md).\n\n\u003Cdetails>\n\u003Csummary>\u003Cstrong>Session states\u003C\u002Fstrong>\u003C\u002Fsummary>\n\n| State       | Meaning                                                 |\n| :---------- | :------------------------------------------------------ |\n| **idle**    | Waiting for user input                                  |\n| **working** | Processing (thinking, running tools, subagents)         |\n| **waiting** | Needs attention: permission, plan approval, or question |\n\nThe status machine derives state from JSONL log entries, tracks pending tool IDs for parallel tool calls, and checks process liveness to detect crashed sessions.\n\n\u003C\u002Fdetails>\n\n## 🔧 Development\n\n```bash\nbun install              # Install dependencies\nbun run dev              # Run with --watch\nbun run typecheck        # Type check\nbun test                 # Run tests\nbun run build            # Bundle to dist\u002Findex.js (consumed by the launcher)\n```\n\n### Performance Profiling\n\nSet `CCMUX_PERF=1` to enable performance instrumentation:\n\n```bash\nCCMUX_PERF=1 ccmux picker 2>\u002Ftmp\u002Fccmux-perf.log\n```\n\nThis outputs a startup waterfall and periodic runtime stats (FPS, memo recomputes, active timers) to stderr.\n\n## 📄 License\n\nMIT\n","ccmux 是一个面向开发者的工作流增强工具，用于在 tmux 环境中统一监控和管理多个 AI 编程代理（如 Claude Code、Codex、Cursor、Gemini CLI 等）的实时会话状态。它通过后台守护进程自动发现运行中的代理会话，提供 TUI 界面展示各会话的 idle\u002Fworking\u002Fwaiting 状态、Git 上下文、差分预览与模糊搜索，并支持一键跳转、就地交互与会话控制。无需修改现有启动方式，深度集成 tmux 工作流，适用于多代理并行开发、AI 辅助编程调试及复杂项目上下文切换等场景。","2026-07-11 02:30:51","CREATED_QUERY"]