# Agent Detection The glyphs on the dock and the border chips on each pane come from a **two-layer** detector. The whole point is trust: when an agent can tell you the truth, tmux-ide believes it; when it can't, tmux-ide reasons transparently and lets you correct it. The four states are **blocked**, **working**, **done**, and **idle** (plus **unknown** when there's no signal at all). ## Layer 1 — authoritative [#layer-1--authoritative] The authority layer is ground truth: an agent stamps its own pane with a tmux option and the detector trusts it. ### The Claude Code integration [#the-claude-code-integration] ```bash tmux-ide integration install claude ``` Claude Code has a first-class hooks system. This command writes a small POSIX hook script and registers it in `~/.claude/settings.json` for the lifecycle events that map to agent states: | Claude event | Reported state | | -------------------------------- | -------------- | | `UserPromptSubmit`, `PreToolUse` | working | | `Notification` | blocked | | `Stop` | done | | `SessionEnd` | idle | Each hook invocation stamps the current pane: ``` @agent_state ":" @agent_session_id the Claude session id (used by restore --resume-agents) ``` The merge is surgical and reversible: entries are tagged by the hook-script path, a one-time backup is written next to `settings.json`, and uninstall removes exactly the tmux-ide entries. Hooks are read at session start, so the integration takes effect for **new** Claude Code sessions. ### The self-report contract [#the-self-report-contract] You don't need the integration to join the authority layer. **Any** agent — or any script — can report status by writing the same pane option: ```bash tmux set-option -p @agent_state "working:$(date +%s)" ``` The value is `:`. A `working` or `blocked` report older than 10 minutes is treated as stale and the detector falls back to Layer 2, so keep long-running agents stamping periodically. ## Layer 2 — the evidence-tuned fallback [#layer-2--the-evidence-tuned-fallback] When there's no fresh authority option, the detector resolves the agent from the **process tree** (which command is actually running in the pane) and then reads the pane's visible screen against **evidence-tuned manifests** — per-agent rule sets that recognize the tells of working, blocked, and done from the last lines of output. This layer is deterministic and inspectable, not a black box — and you can override it. ## Debug it: `agent explain` [#debug-it-agent-explain] To see exactly how a pane was classified, ask: ```bash tmux-ide agent explain %3 # a pane id tmux-ide agent explain %3 --json ``` It's read-only — it captures and inspects, never sends keys — and prints everything the detector reasoned over: * the pane's command and pid; * the `@agent_state` authority option (raw value, parsed verdict, staleness); * the `@agent_hint` override option; * which manifest resolved and by which path (hint / fast / tree); * each state's rule and whether it matched; * the winning classification; * the bottom screen lines it judged. ## Overrides [#overrides] If the fallback ever guesses wrong for a particular tool, pin the manifest with the `@agent_hint` pane option — `agent explain` reports whether a hint is applied and which manifest it forced. For authoritative control, prefer the [self-report contract](#the-self-report-contract) above: a fresh `@agent_state` always wins. ## Per-pane agents in `team --json` [#per-pane-agents-in-team---json] Whatever the detector resolves is exposed programmatically. `tmux-ide team --json` carries a per-pane `agents[]` array on each session — one entry per pane that classified to a real agent, flat across the session's windows: | Field | What it is | | ------------- | ------------------------------------------------------------------- | | `paneId` | tmux pane id, e.g. `%5` | | `windowIndex` | the window (tab) the pane lives in | | `session` | owning session name (repeated per entry so a flat list stays keyed) | | `kind` | resolved agent — the manifest id (`claude`, `codex`, …) | | `state` | final status (`blocked` / `working` / `done` / `idle`) | | `confidence` | the manifest's evidence confidence | | `since` | authority-state epoch stamp, or `null` for a scraped/tracked pane | | `title` | `pane_title` | | `command` | `pane_current_command` — the immediate process (often node/bun/sh) | | `dir` | `pane_current_path` — the pane's working directory | This is the same data the unified app reads: the sidebar's agents section and the Terminal surface's per-pane chips both render from it. See [Home, sidebar & panels](/docs/app-surfaces#the-terminal-surface). ## See also [#see-also] * [The dock & keys](/docs/the-dock) — where the glyphs and chips appear * [Notifications & events](/docs/notifications-events) — reacting to state transitions # App surfaces ## Home [#home] Home is the configless front door. It shows live tmux session count, aggregate agent status, the selected session, theme control, and the action to open Terminals. It intentionally has no persistent workspace sidebar. When no tmux server exists, Home stays useful and offers a new-session action. The session list updates as ordinary tmux sessions appear or disappear. ## Terminals [#terminals] Terminals is a visual client for the selected tmux session: * the left sidebar lists the current session and detected agents; * the window strip reflects real tmux windows and creates new ones; * every pane has a title, state, and relevant controls; * terminal content is mirrored as terminal cells, including indexed and truecolor output; * pane clicks and agent rows navigate to the canonical tmux pane; * splitting, resizing, renaming, creation, and closing are daemon-owned tmux mutations, not a parallel layout model. The UI retains terminal content through quiet periods, resize, theme switching, daemon replacement, and viewer reattachment. tmux remains the source of truth. ## Commands and notifications [#commands-and-notifications] `F5` or `Ctrl+P` opens the command surface. Transient action notes, such as a successful split, dismiss automatically and do not become permanent chrome. ## Project-defined views [#project-defined-views] WorkspaceConfigV1 can order full-panel views for `home`, `terminals`, `files`, `diff`, and `missions`, or compose them with split and tab layouts. Files, changes, dialogs, settings, palettes, and rich previews are loaded as optional features so the resident terminal path stays small. The Missions panel is presentational in this beta. `harnesses`, `agents`, and `missions` workspace blocks are validated, but there is no public CLI command that dispatches those mission defaults yet. `tmux-ide web` is also explicitly unavailable; the daemon remains renderer-neutral for future clients. # CLI reference This page follows the public surface printed by `tmux-ide --help` in `2.9.0-beta.8`. `--json` is available on the scripting commands that explicitly list it below; interactive commands do not emit JSON. ## Open tmux-ide [#open-tmux-ide] | Command | What it does | | --------------------------- | ------------------------------------------------------------------------------------------------------ | | `tmux-ide` | Open the visual app when no workspace config is present; launch the project layout when one is present | | `tmux-ide ` | Open or launch from a particular directory | | `tmux-ide start [path]` | Explicitly launch the declarative project layout | | `tmux-ide app [session]` | Open Home, or bypass Home and open a live session | | `tmux-ide app --detachable` | Host the app in `_tmux-ide-app`; `Ctrl+Q` detaches and the viewer survives | | `tmux-ide app --hosted` | Alias for `--detachable` | | `tmux-ide team [--json]` | Open the classic fleet cockpit, or print fleet state as JSON | | `tmux-ide --team` | Force the classic fleet cockpit for a bare invocation | `app.detachable` in `~/.tmux-ide/config.json` makes detachable mode the default. ## Sessions and recovery [#sessions-and-recovery] | Command | What it does | | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | `tmux-ide init [--template ] [--json]` | Scaffold `.tmux-ide/workspace.yml`; auto-detect the stack unless a template is named | | `tmux-ide stop [session] [--json]` | Kill the resolved project session or the named session | | `tmux-ide restart [session] [--json]` | Stop and relaunch the session | | `tmux-ide attach [session] [--json]` | Reattach to a running session | | `tmux-ide ls [--json]` | List tmux sessions | | `tmux-ide status [session] [--json]` | Show session status | | `tmux-ide restore [--dry-run] [--run-commands] [--resume-agents] [--json]` | Rebuild the fleet from the last snapshot | Restore does not replay recorded commands unless `--run-commands` is present. `--resume-agents` uses captured conversation IDs and each supported agent's native resume command. See [Restore & resume](/docs/restore-resume). ## Inspect and edit a workspace [#inspect-and-edit-a-workspace] | Command | What it does | | ----------------------------------------------------------------------------------- | ------------------------------------------------------ | | `tmux-ide setup` | Open the interactive setup wizard | | `tmux-ide setup --edit` | Open the config tree editor | | `tmux-ide settings` | Open the standalone config manager | | `tmux-ide inspect [path] [--json]` | Show the effective config and live runtime state | | `tmux-ide doctor [--json]` | Check the local installation and requirements | | `tmux-ide validate [path] [--json]` | Validate the resolved workspace config | | `tmux-ide detect [path] [--json]` | Detect the project stack and show a suggested layout | | `tmux-ide detect [path] --write` | Write the detected layout to `.tmux-ide/workspace.yml` | | `tmux-ide migrate [path] --dry-run [--json]` | Preview migration from legacy `ide.yml` | | `tmux-ide migrate [path] --write [--json]` | Create `.tmux-ide/workspace.yml` from `ide.yml` | | `tmux-ide config [--json]` | Print the resolved launch projection | | `tmux-ide config set ` | Set a workspace value by dot path | | `tmux-ide config add-pane --row --title [--command ] [--size ]` | Add a pane | | `tmux-ide config remove-pane --row --pane ` | Remove a pane | | `tmux-ide config add-row [--size ]` | Add a row containing a shell pane | Row and pane indexes are zero-based. Config mutations preserve WorkspaceConfigV1 and refuse legacy-only fields rather than silently losing them. ## Agent awareness and coordination [#agent-awareness-and-coordination] | Command | What it does | | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | | `tmux-ide integration install claude` | Install Claude Code lifecycle hooks and refresh the bundled Claude skill | | `tmux-ide integration install opencode` | Install session-ID capture for future opencode sessions | | `tmux-ide integration uninstall ` | Remove only tmux-ide's integration entries | | `tmux-ide integration status [--json]` | Show discovered agents, integration state, and resume-ID capture | | `tmux-ide agent explain [--json]` | Explain exactly how an agent state was classified | | `tmux-ide send [--no-enter] [--json]` | Type into a pane selected by ID, title, semantic name, or supported legacy role | | `tmux-ide send --to [--no-enter] [--json]` | Equivalent explicit target form | | `tmux-ide events [--follow] [--json]` | Read recent agent-state transitions or follow them | | `tmux-ide events --follow --socket[=]` | Follow pushed events from a running control server | | `tmux-ide wait agent-status --status [--timeout ] [--json]` | Wait for a session status; exit 0 on match and 1 on timeout | | `tmux-ide wait output --match [--timeout ] [--json]` | Wait until visible pane output matches | | `tmux-ide serve [socket-path]` | Run the foreground, owner-only local NDJSON control socket | `serve` defaults to `~/.tmux-ide/control.sock`. The wait commands use it when requested and otherwise fall back to direct polling. ## Adopt ordinary tmux sessions [#adopt-ordinary-tmux-sessions] | Command | What it does | | -------------------------------------------- | -------------------------------------------------------- | | `tmux-ide adopt ` | Add tmux-ide's server-side status bar and agent tracking | | `tmux-ide adopt --all` | Adopt every live non-internal session | | `tmux-ide unadopt ` | Remove tmux-ide chrome without killing the session | | `tmux-ide switcher [--client ]` | Open the compact session picker | | `tmux-ide cheatsheet` | Print the complete key sheet | | `tmux-ide menu [--client ]` | Open the contextual tmux actions menu | | `tmux-ide popup ` | Open a widget in a tmux popup | | `tmux-ide sidebar-toggle [--session ]` | Toggle the app navigation column | See [The dock & keys](/docs/the-dock) for the matching tmux bindings. ## Worktrees [#worktrees] | Command | What it does | | -------------------------------------------------------------------------------- | ------------------------------------------------------------- | | `tmux-ide worktree create [--from ] [--dir ] [--no-session]` | Add a worktree and, by default, open an adopted session in it | | `tmux-ide worktree open ` | Open or switch to the worktree's session | | `tmux-ide worktree list [--json]` | Join git worktrees with their live session status | | `tmux-ide worktree remove [--force]` | Remove the worktree, then kill only its derived session | Removal refuses a dirty worktree unless `--force` is explicit. ## Rich pane content [#rich-pane-content] | Command | What it does | | --------------------------------- | ------------------------------------------------------------- | | `tmux-ide show ` | Select Markdown or raster-image rendering from the file type | | `tmux-ide widget markdown [file]` | Render Markdown from a file or stdin | | `tmux-ide widget image ` | Render PNG, JPEG, GIF, WebP, or AVIF content | | `tmux-ide widget card [file]` | Render the structured card JSON contract from a file or stdin | File-backed widgets refresh when their source changes. `Ctrl+C` restores the ordinary terminal surface. ## Updates [#updates] | Command | What it does | | --------------------------------------- | -------------------------------------------------------------------- | | `tmux-ide update [--dry-run]` | Update according to the detected install method, or print the plan | | `tmux-ide update --tui-binary [--json]` | Download and verify the matching compiled OpenTUI runtime | | `tmux-ide update --manifests [--json]` | Refresh the agent-detection manifest pack; local overrides still win | | `tmux-ide skill-sync [--json]` | Refresh the managed Claude Code skill in `~/.claude/skills/tmux-ide` | ## Daemon and server entry points [#daemon-and-server-entry-points] These are integration surfaces for process hosts, not the normal way to open the app. | Command | What it does | | ------------------------------------------- | ------------------------------------------------------------------------ | | `tmux-ide --headless [--port ] [--json]` | Own the canonical daemon in the foreground without opening tmux or a TUI | | `tmux-ide command-center [--port ]` | Start the command-center HTTP API (default port 4000) | | `tmux-ide server [--port ]` | Deprecated loopback-only PTY server; use `--headless` | The headless owner publishes `~/.tmux-ide/daemon.json` with owner-only permissions. A host should wait until both `/identity` matches that record and `/health` reports a compatible protocol before considering the child ready. Stop it with `SIGINT` or `SIGTERM`; do not wrap it in a second daemonizer. ## Global flags [#global-flags] | Flag | Meaning | | ----------------- | -------------------------------------------------------------- | | `-h`, `--help` | Print built-in help | | `-v`, `--version` | Print the installed version | | `--verbose` | Log tmux commands; `TMUX_IDE_DEBUG=1` is equivalent | | `--json` | Request structured output where the command lists JSON support | | `--headless` | Run only the canonical daemon in this process | Command-specific flags are documented with their command above. # Workspace Layouts ## Overview [#overview] `.tmux-ide/workspace.yml` is **optional**. The OpenTUI discovers ordinary tmux sessions without it. Use a workspace file when you want tmux-ide to build a repeatable layout with named panes, commands, working directories, and environment variables. Legacy `ide.yml` files are still supported through a compatibility adapter. Preview migration with `tmux-ide migrate --dry-run`; write the new file with `tmux-ide migrate --write`. Scaffold one from your detected stack: ```bash tmux-ide init # or: tmux-ide detect --write ``` ## Minimal example [#minimal-example] ```yaml version: 1 name: my-app # tmux session name terminal: rows: - size: 70% # row height panes: - title: Claude command: claude focus: true - title: Shell - panes: - title: Dev Server command: pnpm dev - title: Tests command: pnpm test ``` ## Fields [#fields] ### Top level [#top-level] | Field | Required | Notes | | ----------- | -------- | ------------------------------------------------------------ | | `version` | yes | Must be `1` | | `name` | no | tmux session name; falls back to the project directory | | `before` | no | Pre-launch shell hook | | `terminal` | no | Repeatable tmux rows, panes, and per-session colors | | `app` | no | Ordered full-panel or composite app views | | `harnesses` | no | Named agent adapter, command, and environment profiles | | `agents` | no | Named harness, model, and role profiles | | `missions` | no | Validated mission defaults; runtime consumption is not wired | ### Rows [#rows] ```yaml terminal: rows: - size: 70% # optional row height (percent); rows split evenly if omitted panes: [...] # at least one pane ``` ### Panes [#panes] | Field | Type | Notes | | --------- | ------- | --------------------------------------------- | | `id` | string | Stable semantic pane ID; unique when provided | | `title` | string | Pane border label | | `command` | string | Command to run in the pane | | `size` | percent | Pane width such as `50%` | | `dir` | string | Per-pane working directory | | `focus` | boolean | Initial focus | | `env` | map | String or numeric environment values | | `type` | string | Render a supported widget instead of a shell | | `target` | string | Target path for widgets that accept one | ### Widget panes [#widget-panes] Set `type` to render a built-in TUI widget in a pane instead of a shell: ```yaml panes: - title: Explorer type: explorer target: src/ - title: Changes type: changes ``` The launch resolver currently implements `explorer`, `changes`, `preview`, `config`, and `sidebar`. The setup wizard is a CLI surface (`tmux-ide setup`), not a workspace pane type. Explorer, changes, and config are also available as floating panels (`prefix e` / `g` / `v`, or `⌥e` / `⌥g` / `⌥,`) from an adopted session — see [Home, sidebar & panels](/docs/app-surfaces). ## App views [#app-views] `app.views` controls the ordered views shown by the unified app. A simple view selects one of `home`, `terminals`, `files`, `diff`, or `missions`: ```yaml app: views: - id: home title: Home panel: home - id: terminals title: Terminals panel: terminals ``` Composite views use a recursive `layout` of `panel`, `split`, and `tabs` nodes. Node IDs must be unique within the view. Splits contain two to four children; tabs contain one to eight. When supplied, split `weights` must have one entry per child and a tab's `active` value must name one of its children. ## Agent profiles and mission defaults [#agent-profiles-and-mission-defaults] Harness commands may be a shell string or an executable/argv array. Agent roles are `manager`, `implementer`, `reviewer`, `researcher`, or `validator`: ```yaml harnesses: claude: adapter: claude command: [claude, --dangerously-skip-permissions] agents: lead: harness: claude role: manager reviewer: harness: claude model: sonnet role: reviewer missions: manager: lead reviewer: reviewer isolation: worktree max_concurrent_tasks: 3 ``` Agent and mission references are validated. In the current release these blocks are declarative defaults only: the app can show its Missions panel, but the CLI does not yet launch or dispatch a mission from this workspace data. ### Per-session theme [#per-session-theme] The `terminal.theme` block sets colors for **this session's** panes: ```yaml terminal: theme: accent: colour75 border: colour238 bg: colour235 fg: colour248 ``` For the product-wide palette that colors the dock, chips, panels, and widgets, use `~/.tmux-ide/config.json` instead — see [Theming & config](/docs/theming). ## The global config & in-app settings [#the-global-config--in-app-settings] `.tmux-ide/workspace.yml` describes one project's layout. Product-wide behavior lives in `~/.tmux-ide/config.json` (override the path with `TMUX_IDE_CONFIG`) — the same file that holds the shared theme. Two things worth knowing for 2.9: * **`app.frontDoor`** controls the default entry point. Bare `tmux-ide` launches the unified visual app by default (`true`); set it to `false` only when you deliberately need the retained classic entry path. * **`app.detachable`** makes plain `tmux-ide app` run hosted: the app lives in a tmux session of its own and your terminal attaches to it, so the cockpit survives the terminal and reattaches from anywhere (`^q` detaches instead of quitting). Default `false` — same as passing `--detachable` every time. * **Settings are editable in the app.** Inside `tmux-ide app`, press **F5** and type "settings" for a palette command per setting — no JSON required. Edits write back to `~/.tmux-ide/config.json` atomically: | Setting | Config field | | -------------- | ---------------------------------------- | | Accent color | `theme.accent` | | Notifications | `notifications` (channels & transitions) | | Quiet hours | `notifications.quietHours` | | Update cadence | `updater`, `updates` | | Crash restore | `restore` | Keyboard shortcuts have a read-only viewer, and a guarded "reset to defaults" removes your overrides so the built-in defaults take over. See [Theming & config](/docs/theming) for the full palette. ## Editing config programmatically [#editing-config-programmatically] Use the typed `config` subcommands to mutate workspace fields, then validate the result with structured output: ```bash tmux-ide config set name "my-app" tmux-ide config add-pane --row 0 --title "Claude" --command "claude" tmux-ide validate --json ``` See the [CLI reference](/docs/commands) for the full `config` surface, and [Templates](/docs/templates) for ready-made starting points. ## Legacy compatibility [#legacy-compatibility] The legacy `team`, pane `role`/`task` metadata, `sidebar`, and `orchestrator` blocks are not represented in WorkspaceConfigV1. The migration command reports those fields as diagnostics instead of silently dropping them. Use the typed `harnesses`, `agents`, and `missions` fields above for declarative profiles. Do not copy the legacy `orchestrator` runtime block into `.tmux-ide/workspace.yml`. # Contributing ## Local Setup [#local-setup] Requirements: * Node.js 18 or newer * pnpm 10 or newer * tmux 3.0 or newer for manual session smoke tests Install dependencies from the repo root: ```bash pnpm install --frozen-lockfile ``` ## Main Commands [#main-commands] Run these from the repository root: ```bash pnpm test pnpm typecheck pnpm build pnpm docs:build pnpm pack:check pnpm check ``` What they do: * `pnpm test` runs the Node CLI test suite (uses `--experimental-strip-types` on `.ts` files) * `pnpm typecheck` runs `tsc --noEmit` to verify types without emitting output * `pnpm build` compiles TypeScript to `dist/` via `tsc` * `pnpm docs:build` validates the docs site production build * `pnpm pack:check` verifies the published npm package can be packed cleanly * `pnpm check` runs the full default pre-push and pre-release check path `npm publish` is guarded by `prepublishOnly`, so publishing runs `pnpm check` automatically before npm actually publishes the package. ## Manual Smoke Tests [#manual-smoke-tests] If tmux is available locally, run a manual smoke test: ```bash node bin/cli.js init node bin/cli.js inspect --json node bin/cli.js ``` Then in a second shell: ```bash node bin/cli.js status --json node bin/cli.js stop --json ``` ## CI [#ci] GitHub Actions validates: * the CLI test suite on Node 18, 20, and 22 * the docs production build * package packing with `npm pack --dry-run` That keeps the release path close to the local `pnpm check` workflow while still exercising multiple Node versions in CI. ## Release Workflow [#release-workflow] Before publishing a release: 1. Update `CHANGELOG.md` under `Unreleased`. 2. Confirm the version in `package.json`. 3. Run `pnpm check`. 4. Do the manual smoke test if the release includes CLI behavior changes. 5. Move `Unreleased` notes into the final version entry. 6. Tag and publish the release. The repository root also includes: * `CONTRIBUTING.md` for contributor setup * `RELEASE.md` for the release checklist * `CHANGELOG.md` for release notes * `SECURITY.md` for vulnerability reporting ## Pull Request Expectations [#pull-request-expectations] * Keep CLI behavior changes covered by tests. * Update docs when command behavior or output changes. * Prefer focused pull requests over mixed refactors. * Run `pnpm check` before opening or updating a pull request. # OpenTUI demo Animated tmux-ide OpenTUI tour showing Home, Terminals, and Commands This is not a hand-built browser imitation. The artifact renders the production `ApplicationShellView`, pane surfaces, theme tokens, terminal adapter contract, and command palette against a deterministic in-memory tmux fixture. It cycles through Home, an agent-aware four-pane workspace, and Commands. That boundary follows the useful lesson from tmuxy's demo: keep the product UI and replace only the external tmux authority with deterministic demo data. tmux-ide's release client is a native terminal renderer, so the checked-in demo is an animated SVG capture rather than a second DOM implementation. Regenerate it from a checkout: ```bash pnpm demo:tui ``` The command writes `docs/public/tui-demo.svg`. It requires the same Bun and OpenTUI development dependencies used by renderer tests; installed users do not need them. With reduced motion enabled, the SVG shows the Terminals frame without animation. # Getting started ## Requirements [#requirements] * tmux 3.0 or newer; 3.2+ recommended * Node.js 20 or newer * macOS arm64/x64 or Linux arm64/x64 Installed releases use a compiled OpenTUI runtime and do not require Bun. Development checkouts use Bun to build the runtime and can opt into live source with `TMUX_IDE_TUI_SOURCE=1`. ## Install [#install] ```bash npm install -g tmux-ide@beta tmux-ide app ``` If the exact-version runtime is not already installed, the first explicit app launch downloads it, verifies its release metadata and SHA-256 digest, and caches it under `~/.tmux-ide/bin`. The daemon starts only after a runnable TUI has been selected. If the download was interrupted: ```bash tmux-ide update --tui-binary tmux-ide app ``` ## Open a session [#open-a-session] Home lists the tmux sessions you already own. Select one with the mouse or keyboard, or name it directly: ```bash tmux new-session -s work tmux-ide app work ``` If there are no sessions, press `N` on Home to create one. Closing tmux-ide does not kill the tmux session. ## Controls [#controls] | Key | Action | | ---------------- | ----------------------------------------------------- | | `F1` | Home | | `F2` | Terminals | | `F3`, `F4`, `F6` | Files, Diff, Missions when those views are configured | | `F5`, `Ctrl+P` | Commands | | `F8`, `Ctrl+Tab` | Cycle focus inside a composite view | | `F12` | Performance HUD | | `Ctrl+O` | Cycle panes | | `Ctrl+T` | Cycle windows | | `Ctrl+G` | Return to Home | | `Ctrl+E` | Toggle the Files editor | | `Meta+Arrow` | Resize the focused pane | | `Ctrl+Q` | Quit, or detach a detachable viewer | Pane headers and window tabs also expose mouse controls for the supported actions. ## Diagnose an install [#diagnose-an-install] ```bash tmux-ide doctor --json tmux-ide inspect --json ``` When reporting an issue, include the output plus the shortest sequence that reproduces it. # tmux-ide tmux-ide is a mouse-friendly OpenTUI application around ordinary tmux sessions. It adds Home, clickable window and pane chrome, agent indicators, memorable names, and direct controls. tmux remains the authority for processes, PTYs, topology, persistence, and SSH. ```bash npm install -g tmux-ide@beta tmux-ide app ``` ## The current app [#the-current-app] The default app starts with two core views and adds project-defined views when they are present in `.tmux-ide/workspace.yml`: * **Home** discovers live sessions and provides a clean first-run state. * **Terminals** renders the selected tmux session with window tabs, pane headers, agent awareness, and terminal content. * Optional **Files**, **Diff**, and **Missions** panels can appear through `app.views`; mission profiles are validated but not dispatched by the CLI yet. * **Commands** exposes contextual actions and settings. * Mouse and keyboard control pane focus, windows, splitting, resizing, renaming, creation, and confirmed closing. * `tmux-ide web` is intentionally unavailable in this beta; the canonical daemon API remains renderer-neutral. ## Why tmux stays underneath [#why-tmux-stays-underneath] tmux has years of coverage for shells, terminal modes, resizes, detach/attach, disconnects, and SSH. tmux-ide does not duplicate that multiplexer. If the app or daemon stops, your sessions remain ordinary tmux sessions. ```mermaid flowchart LR T[tmux\nPTYs · processes · topology · persistence] D[daemon\ndiscovery · lifecycle · agent state · pane streams] U[OpenTUI\nHome · Terminals · chrome · input] T <--> D D <--> U ``` Continue with [Getting started](/docs/getting-started) or read the [2.9 release cut](/docs/release-2-9-0-beta-1). The [OpenTUI demo](/docs/demo) is generated from the production component tree. # Multi-Agent Teams tmux-ide doesn't just watch one agent — it lets a **team of agents work together**, and they don't have to be the same tool. A Claude Code lead, a codex pane, a cursor-agent pane, and an `aider` pane can share one fleet and coordinate through tmux-ide's primitives. Nothing here is aspirational: it's the same `send`, `wait`, `events`, and `team` commands documented elsewhere, pointed at each other. Because every agent is just a process in a tmux pane, the coordination layer is **agent-agnostic**. Claude Code reports its status automatically (via the integration hooks); any other agent either self-reports with a one-line pane option or is recognized by the fallback detector. ## A mixed fleet [#a-mixed-fleet] ``` mixed-fleet ●working [ ⌂ home ^b h ] [ ⧉ switch ^b j ] ┌──────────────────────────────┬──────────────────────────────┐ │ Lead (claude) %0 │ API (codex) %2 │ │ claude · working ●working │ codex · done ●done │ ├──────────────────────────────┼──────────────────────────────┤ │ UI (cursor-agent) %3 │ Shell %4 │ │ cursor · blocked ●blocked │ $ │ └──────────────────────────────┴──────────────────────────────┘ ``` One glance at the dock tells the lead (human or agent): the API worker is `done`, the UI worker is `blocked` and needs attention. That glyph is the handoff signal. ## The primitives [#the-primitives] ### 1. A shared status bus [#1-a-shared-status-bus] Every agent publishes its state, and the whole fleet can read it. The fleet rollup is one JSON call, transitions stream as they happen, and any single pane's verdict is one command: ```bash tmux-ide team --json # fleet rollup: each session's + window's agent status tmux-ide events --follow # live stream of session-status transitions (JSONL) tmux-ide agent explain %2 --json # one pane's status (+ exactly why it was classified that way) ``` (`team --json` and `events` report at session/window granularity; use `agent explain ` — or the per-pane border chips — for an individual pane.) Any agent joins the bus by self-reporting — no integration required: ```bash tmux set-option -p @agent_state "working:$(date +%s)" # working | blocked | done | idle ``` Claude Code does this automatically once you run `tmux-ide integration install claude`. For codex, cursor-agent, or any other tool, either drop that one line into the agent's own lifecycle or rely on the fallback detector (process-tree + screen manifests). See [Agent detection](/docs/agent-detection). ### 2. Direct messaging [#2-direct-messaging] `tmux-ide send` types a message straight into another agent's prompt — so agent A can literally hand agent B a task. Target a pane by ID (`%2`), title, role, or `@ide_name`: ```bash tmux-ide send %2 "Implement POST /login per the spec in docs/auth.md, then run the tests" tmux-ide send API --no-enter "draft: " # stage text without submitting echo "long instructions…" | tmux-ide send %2 # pipe from stdin ``` Long messages (over \~150 characters) are automatically written to a dispatch file and the target is told to read it — no paste-mode issues in any agent TUI. ### 3. Synchronization [#3-synchronization] Block until a teammate reaches a state or produces output. Exit codes make it scriptable (`0` = matched, `1` = timed out): ```bash tmux-ide wait output %2 --match "tests passed" --timeout 300000 # wait on a pane tmux-ide wait agent-status api --status done --timeout 600000 # wait on a session ``` ### 4. Observation & isolation [#4-observation--isolation] ```bash tmux-ide agent explain %3 # why is the UI pane blocked? see exactly how it was classified tmux-ide worktree create feature/login # give a teammate an isolated checkout + session ``` ## Worked example: a Claude Code lead dispatching to codex [#worked-example-a-claude-code-lead-dispatching-to-codex] Picture a Claude Code agent running as the **lead** in pane `%0`, with a codex worker in `%2`. The lead can drive the whole loop from its own shell: ```bash # 1. Read the fleet rollup, then check the codex pane specifically tmux-ide team --json | jq '.projects[].sessions[] | {name, status}' tmux-ide agent explain %2 --json | jq -r .classification # 2. Hand the codex pane a task tmux-ide send %2 "Add a /health endpoint returning {ok:true}, then run: npm test" # 3. Block until codex reports the tests passing tmux-ide wait output %2 --match "Tests:.*passed" --timeout 300000 \ && echo "codex finished — reviewing its diff" # 4. Re-read that pane's verdict before assigning the next task tmux-ide agent explain %2 --json | jq -r .classification ``` Step 2 puts text into codex's prompt exactly as if the lead had typed it. Step 3 watches codex's own output. Steps 1 and 4 read the shared bus. None of it is Claude-specific — swap `%2` for a cursor-agent or `aider` pane and the same commands hold. When the unit you're waiting on is a whole session — say a worktree running one agent — `tmux-ide wait agent-status --status done` is the session-level twin of the pane-level `wait output`. ## The lead-agent pattern [#the-lead-agent-pattern] The pattern that ties it together: * A **lead** — a human watching the dock, or a lead agent — assigns work with `send` and worktrees. * **Teammates** report status (automatically for Claude Code, via the pane option for anything else). * The **`done`/`blocked` glyph and the toast** are the handoff signal: the lead reacts the moment a teammate finishes or gets stuck, instead of polling. Because the status bus, messaging, and waits are all plain CLI with `--json` and exit codes, you can wire the same pattern into scripts, a lead agent's tool calls, or a Makefile — whatever is driving the fleet. ## See also [#see-also] * [Agent detection](/docs/agent-detection) — the status every teammate publishes * [Notifications & events](/docs/notifications-events) — the toast/stream side of the handoff * [Worktrees](/docs/worktrees) — an isolated checkout per teammate * [CLI reference](/docs/commands) — `send`, `wait`, `team`, `events` in full # Notifications & Events The reason to know your fleet's status is to act on it. tmux-ide surfaces every agent-status transition three ways: **toasts** for humans, an **event stream** for scripts, and **wait** primitives for coordination. ## Toasts — the who-needs-me loop [#toasts--the-who-needs-me-loop] When an agent anywhere in the fleet goes **blocked** or **done**, tmux-ide fires a toast. Because the chrome lives server-side, the toast appears on **every client attached to the session** — the laptop and the phone both light up. Notifications are configured in `~/.tmux-ide/config.json`: ```json { "notifications": { "toast": true, "macos": false } } ``` * `toast` (default `true`) — the in-tmux toast. * `macos` (default `false`) — also post a native macOS notification. The macOS helper is bundled with tmux-ide—there is nothing else to install. Its banners use the tmux-ide app icon, follow the system's light/dark icon appearance, and jump to the session that needs you when clicked. On first use, macOS may show its permission card instead of the agent banner. Choose **Options → Allow**; subsequent agent notifications then appear normally. See [Theming & config](/docs/theming) for the full file. ## Per-pane chips [#per-pane-chips] Beyond fleet-wide toasts, each pane's border chip continuously reflects that pane's agent, e.g. `claude · working`. It's the ambient version of the same signal — no interruption, always visible. ## The event stream [#the-event-stream] Every status transition is also an append-only JSONL event. Follow it live or snapshot it: ```bash tmux-ide events --follow # stream transitions as they happen tmux-ide events --json # print recent events as JSON ``` `events` needs an adopted session (that's where the status tracking runs). Pipe the stream anywhere — a log, a webhook relay, a status bar of your own. ## Coordination primitives [#coordination-primitives] Two `wait` commands block until a condition is met, so you can script the fleet. Both exit `0` on match and `1` on timeout. ### Wait for a status [#wait-for-a-status] ```bash tmux-ide wait agent-status work --status blocked --timeout 300000 ``` Blocks until the `work` session reaches the given agent status (`blocked` | `working` | `done` | `idle` | `unknown`). Handy for "notify me when this run needs input" or gating a script on an agent finishing. ### Wait for output [#wait-for-output] ```bash tmux-ide wait output %2 --match "Listening on" --timeout 60000 tmux-ide wait output web --match "\berror\b" ``` Blocks until a pane's (or session's) visible output matches a regex — a portable way to wait on a dev server booting, a build finishing, or an error appearing. ## See also [#see-also] * [Agent detection](/docs/agent-detection) — where the transitions come from * [The dock & keys](/docs/the-dock) — the always-on status surface # OpenTUI 2.9 beta.8 The current package is `2.9.0-beta.8`. It ships an OpenTUI client around ordinary tmux sessions. tmux owns PTYs, processes, topology, persistence, and SSH; the daemon publishes semantic workspace state and pane streams; OpenTUI owns presentation and input. ## Install [#install] ```bash npm install -g tmux-ide@beta tmux-ide app ``` Pass a session name to bypass Home: ```bash tmux-ide app work ``` ## New in beta.8 [#new-in-beta8] * Pane headers show recent input and pane reads, with external tmux activity distinguished from attributed agent activity. Failed and disconnected states have explicit labels. * Appearance offers 22 Gloomberb themes alongside System, Dark, and Light. Open Appearance, type to filter, and use the arrow keys to preview. Enter saves the selection; Escape restores the previous theme. Theme changes leave terminal truecolor output intact. * Resize, zoom, and retained scrollback fixes include corrected pane geometry and finer mouse scrolling. The TUI keeps wheel momentum in scrollback when returning to live output. ## Included [#included] * Home, Terminals, and contextual Commands. * Configless discovery and creation of ordinary tmux sessions. * Agent state in the sidebar, window tabs, and pane chrome. * Agent-row navigation to the correct pane. * Mouse and keyboard pane/window selection and control. * Pane splitting, resizing, renaming, creation, and confirmed closing. * Memorable names for newly created panes and windows. * Indexed and truecolor terminal cells. * Retained content through quiet panes, resize, theme changes, daemon replacement, and viewer reattachment. * A downloadable, checksummed runtime for macOS and Linux on arm64 and x64. * Detachable app hosting with `tmux-ide app --detachable`. * Optional workspace-defined Files, Diff, Missions, split, and tab views. * Rich Markdown, raster image, and structured card rendering in panes. ## Deliberately bounded [#deliberately-bounded] `tmux-ide web` fails with an explicit message instead of starting an unsupported web client. The native desktop client is still under development. Mission profiles and the Missions panel exist, but public CLI mission dispatch from workspace defaults is not wired in this beta. The npm package includes a patched tmux server for compatible Apple Silicon macOS hosts. Its native grid extension preserves hidden terminal backing during frozen scrollback reflow. Existing system-tmux servers remain in compatibility mode; installing the package does not restart them or migrate running sessions. Other hosts use system tmux. Terminal mouse events are row based, so this TUI does not provide pixel-level macOS scrolling. ## Reporting issues [#reporting-issues] Open a [GitHub issue](https://github.com/wavyrai/tmux-ide/issues) with your OS, architecture, tmux version, `tmux-ide doctor --json`, and the shortest reproduction sequence. # Restore & Resume A tmux server death shouldn't cost you an afternoon. While tmux-ide is running, the chrome updater continuously writes a snapshot of your fleet to `~/.tmux-ide/snapshot.json`. If the server dies, `tmux-ide restore` rebuilds everything it recorded. ## What comes back [#what-comes-back] ```bash tmux-ide restore ``` For each session in the last snapshot, restore rebuilds: * every **window**, * the **split layout** of each window, * per-pane **working directories**, * per-pane **titles**, and * **re-adopts** the sessions that were adopted (the dock returns). If a session already maps to a registry project **with a project config**, restore relaunches from that config instead of a raw rebuild — the config is the source of truth. ## Safety first [#safety-first] Restore never clobbers work in progress: * An **already-live session** with the same name is skipped, never overwritten. * Recorded pane **commands are not auto-run**. By default a rebuilt agent pane is a plain shell in the right directory with its title restored — you decide what to relaunch. Pass `--run-commands` to replay the recorded commands. Preview the plan without touching tmux: ```bash tmux-ide restore --dry-run tmux-ide restore --dry-run --json ``` ## Resume agent conversations [#resume-agent-conversations] The strongest form of recovery brings the **conversations** back, not just the panes. An agent pane that carries a recorded `@agent_session_id` relaunches, under `--resume-agents`, with its kind's native resume invocation — reviving the actual conversation instead of opening a fresh shell. ```bash tmux-ide restore --resume-agents ``` You can make this the default in `~/.tmux-ide/config.json`: ```json { "restore": { "resumeAgents": true } } ``` ### Support per agent [#support-per-agent] Two things have to be true for a pane to resume: tmux-ide must know the kind's **resume invocation**, and the pane's **session id** must have been captured while the agent ran. Honest status of both: | Agent | Resume invocation | Session-id capture | | ------------------------- | ---------------------------- | ---------------------------------------------------------------------------- | | Claude Code | `claude --resume ` | automatic once `tmux-ide integration install claude` is set up (hooks) | | Codex CLI | `codex resume ` | **automatic** — the updater reads the session's own rollout file | | Cursor CLI | `cursor-agent --resume ` | **automatic** — the updater reads the CLI's chat store | | opencode | `opencode --session ` | automatic once `tmux-ide integration install opencode` is set up (plugin) | | Copilot CLI | `copilot --resume=` | not automatic (no stable capture surface yet) — self-report works, see below | | gemini, aider, goose, amp | — | no verified native resume invocation | Check what's active on your machine: ```bash tmux-ide integration status ``` Any agent not listed can still join: the agent contract is open. An agent that writes its own id resumes like the rest (given a known invocation for its kind): ```bash tmux set-option -p @agent_session_id ``` Capture never overwrites: an id stamped by an integration (or by the agent itself) is left alone. Codex/Cursor capture runs inside the chrome updater, so it is active whenever any session is adopted. ## Snapshot cadence [#snapshot-cadence] How often the snapshot is written is part of the updater config in `~/.tmux-ide/config.json`: ```json { "updater": { "tickMs": 2000, "snapshotEvery": 15 } } ``` A snapshot is written every `snapshotEvery` ticks (default: every 15 ticks of 2s ≈ every 30 seconds). See [Theming & config](/docs/theming). ## See also [#see-also] * [Agent detection](/docs/agent-detection) — how `@agent_session_id` is recorded * [Worktrees](/docs/worktrees) — isolated sessions per branch, restored too # Templates `tmux-ide init --template ` copies a shipped template into `.tmux-ide/workspace.yml`, replaces its example name with the current project directory, validates it as WorkspaceConfigV1, and then writes it. ## Shipped names [#shipped-names] | Template | Initial layout | | --------------------- | ---------------------------------------------------------------------------------------- | | `default` | Two Claude panes; Changes, Dev Server, and Shell below | | `nextjs` | Three Claude panes; Next.js, Changes, and Shell below | | `convex` | Three Claude panes; Next.js, Convex, and Shell below | | `vite` | Two Claude panes; Vite, Changes, and Shell below | | `go` | Two Claude panes; Go, Changes, and Shell below | | `python` | Two Claude panes; Server, Changes, and Shell below | | `agent-team` | Lead and two teammate panes; Changes, Dev Server, and Shell below | | `agent-team-nextjs` | Lead, Frontend, and Backend agents; Next.js, Changes, and Shell below | | `agent-team-monorepo` | Lead plus agents rooted in `apps/web` and `apps/api`; matching dev panes and Shell below | | `missions` | Lead, Frontend, and Backend above Validator, Researcher, and Shell | Run, for example: ```bash tmux-ide init --template nextjs tmux-ide validate --json tmux-ide start ``` ## Representative generated file [#representative-generated-file] The shipped `default` template currently produces this structure (the `name` is rewritten for your directory): ```yaml name: my-project version: 1 terminal: rows: - size: 70% panes: - id: agent-1 title: Claude 1 command: claude - id: agent-2 title: Claude 2 command: claude - panes: - id: changes title: Changes type: changes - id: dev title: Dev Server - id: shell title: Shell focus: true ``` Stable pane `id` values are included so the daemon can preserve semantic identity independently of display titles. ## Additional team scaffolding [#additional-team-scaffolding] The `agent-team`, `agent-team-nextjs`, and `agent-team-monorepo` templates also copy the bundled skill files and create, when missing: * `.tmux-ide/library/architecture.md` * `.tmux-ide/library/learnings.md` * `.tasks/validation-contract.md` * `AGENTS.md` The `missions` template creates the same files plus `.tmux-ide/skills/`. Existing files are not overwritten. These templates create agent-ready pane layouts and project context. They do not add a legacy `orchestrator` block or automatically dispatch work. See [Workspace layouts](/docs/configuration#agent-profiles-and-mission-defaults) for the current declarative agent and mission fields. ## Automatic detection [#automatic-detection] Without `--template`, `tmux-ide init` detects the stack. Recognized frameworks produce a suggested layout; otherwise it falls back to `default`: ```bash tmux-ide detect --json tmux-ide init ``` Use `tmux-ide detect --write` when you want detection to write directly. Both commands refuse to overwrite an existing workspace config. # The Dock & Keys `tmux-ide adopt ` adds a **dock** — a native tmux chrome row — to any existing session. It's the always-on surface: a fleet of tabs, live agent-status glyphs, and triggers into the rest of the app. Everything it draws is a tmux option, so it renders on the server and shows up on every client (including SSH), and `unadopt` reverts it completely. ## What's on the bar [#whats-on-the-bar] ``` ┌ web ● ─┬ api ○ ─┬ infra ◍ ─┐ [ ⌂ home ^b h ] [ ? keys ^b k ] [ ⧉ switch ^b j ] ``` * **Fleet tabs** — one per session in your fleet. Click a tab to switch to it. * **Agent-status glyphs** — each tab carries a glyph tinted by the session's agent state: blocked, working, done, or idle. One glance across the bar tells you where the fleet stands. See [Agent detection](/docs/agent-detection). * **Triggers** — clickable `[ ⌂ home ]`, `[ ? keys ]`, and `[ ⧉ switch ]` open the home cockpit, the cheat sheet, and the session switcher. Each advertises its **prefix twin** (`^b h`, `^b k`, `^b j`) — the key form that works everywhere (see below). Per-pane, the border chip reads the agent for that specific pane, e.g. `claude · working`. ## Adopt, unadopt, and safety [#adopt-unadopt-and-safety] ```bash tmux-ide adopt work # add the chrome to one session tmux-ide adopt --all # adopt every live (non-internal) session tmux-ide unadopt work # remove the chrome; session keeps running ``` Adoption is **purely additive tmux configuration**. There is no wrapper process between you and tmux. If tmux-ide crashes or you uninstall it, adopted sessions keep running as ordinary tmux — you just lose the decoration. That's the whole point: no lock-in, no risk. ## One interaction grammar [#one-interaction-grammar] Every surface tmux-ide draws speaks the same five keys: | Key | Action | | --------- | -------------------------- | | `j` / `k` | Move down / up | | `enter` | Open / confirm | | `/` | Filter | | `esc` | Back out / close | | `?` | Show keys for this surface | Learn it once; it works in the home cockpit, the sidebar, and every panel. ## The keys [#the-keys] Once a session is adopted, the whole app is a couple of keystrokes away. Every surface has two bindings — reach for the **prefix twin** first; the `⌥` key is a faster shortcut when it's available. | Action | Prefix (always works) | Alt fast-path | | -------------- | --------------------- | ------------- | | Home cockpit | `prefix h` | `⌥h` | | Switch session | `prefix j` | `⌥p` | | Cheat sheet | `prefix k` | `⌥k` | | Actions menu | `prefix u` | `⌥m` | | Sidebar | `prefix b` | `⌥b` | | File explorer | `prefix e` | `⌥e` | | Git changes | `prefix g` | `⌥g` | | Config editor | `prefix v` | `⌥,` | `prefix` is your tmux prefix (`C-b` unless you've changed it), so `prefix h` means "press `C-b`, release, then `h`". ### Why prefix-first [#why-prefix-first] The prefix twins are the **reliable** path: they work under every keyboard protocol. The `⌥` (root-table) binds are a genuine one-keystroke fast path, but an agent pane can temporarily switch how the terminal encodes keys (the kitty keyboard protocol), which can swallow a root-table `Alt` bind before tmux ever sees it. tmux-ide registers kitty-encoded fallbacks for the `⌥` keys, but coverage varies by terminal — the prefix is immune by construction, so lead with it. Both forms come from the same entries in `~/.tmux-ide/config.json` (`keys.*`): rebind the fast-path key and its prefix twin follows. See [Theming & config](/docs/theming). On a fresh install, a first-run welcome card names the core keys once. Reprint the full sheet any time: ```bash tmux-ide cheatsheet ``` ## The actions menu [#the-actions-menu] Right-click any pane or the status bar to open a native tmux menu **at the pointer** — it opens on button *release*. The same menu is on `prefix u` (or `⌥m`). Either way the action set is identical wherever you invoke it, so you never have to remember which surface owns a command. ```bash tmux-ide menu [--client N] ``` ## See also [#see-also] * [Home, sidebar & panels](/docs/app-surfaces) — the floating surfaces the dock triggers * [Agent detection](/docs/agent-detection) — what the glyphs mean and where they come from * [Theming & config](/docs/theming) — rebind every key and recolor every glyph # Appearance The OpenTUI has one appearance authority. Home, application chrome, sidebars, tabs, pane headers, status bars, dialogs, and overlays all consume the same semantic token snapshot. Choose **light**, **dark**, or **system** from the command palette's Settings group. A switch publishes one new appearance generation and repaints every app-owned surface atomically. Terminal content is different: applications inside tmux own their ANSI colors. tmux-ide preserves those indexed and truecolor cells rather than recoloring them to match the application chrome. That means a dark-only program can remain dark while the surrounding tmux-ide chrome is light; it is the program's chosen palette, not mixed application theming. Fresh sessions advertise: ```text TERM=tmux-256color COLORTERM=truecolor COLORFGBG=15;0 ``` `NO_COLOR` is removed for newly launched shells. Programs that were already running retain the environment with which they started. # Worktrees When you want two agents working two branches at once without stepping on each other, `tmux-ide worktree` gives each its own git worktree **and** its own adopted session inside it. One command, isolated checkouts, no branch-switching churn. ## Create [#create] ```bash tmux-ide worktree create fix/login ``` This adds a git worktree on a new `fix/login` branch and opens a session inside it — already adopted, so the dock and agent detection are on from the first keystroke. Options: | Flag | Effect | | -------------- | ---------------------------------------------------------- | | `--from ` | Base the new branch on `` instead of the current head | | `--dir ` | Override where the worktree is checked out | | `--no-session` | Create the worktree only; don't open a session | ## Open, list, remove [#open-list-remove] ```bash tmux-ide worktree open fix/login # open (or switch to) its session tmux-ide worktree list [--json] # worktrees joined with session status tmux-ide worktree remove fix/login [--force] # kill the session + remove the worktree ``` `remove` refuses to discard a dirty worktree unless you pass `--force`. ## Where worktrees live [#where-worktrees-live] By default each worktree is checked out into a sibling `-worktrees` directory next to the repo. Override the base globally in `~/.tmux-ide/config.json`: ```json { "worktrees": { "dir": "~/wt" } } ``` An empty value keeps the sibling-directory default; a relative path is resolved against the repo. See [Theming & config](/docs/theming). ## See also [#see-also] * [Restore & resume](/docs/restore-resume) — worktree sessions are snapshotted too * [The dock & keys](/docs/the-dock) — worktree sessions arrive pre-adopted