Skip to content
Back to skills

Skill

ASecurity

<!-- tmux-ide-skill-version: 2.6.0 --> tmux-ide is **the open-source workspace for coding agents**, built on tmux. The app (`tmux-ide app`) shows every agent across local and SSH machines with ground-truth working/blocked/done status, and drives the live tmux session. `tmux-ide adopt` adds the same status to plain tmux clients as tmux chrome: a status bar, keys and menus, all additive tmux options with no wrapper process. Around both: notifications when an agent needs a human, and crash-proof...

  • 550 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 7, 2026
ai-agentsrustgoshellbashreactnoderefactoringgitapi

Works with

  • claude code
  • cursor
  • terminal
  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 7, 2026

npx -y skills add wavyrai/tmux-ide --skill skill --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Skill?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Skill
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wavyrai-skill/badge)](https://www.skillsdirectory.com/skills/wavyrai-skill)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# tmux-ide — Claude Code Skill

<!-- tmux-ide-skill-version: 2.6.0 -->

tmux-ide is **the open-source workspace for coding agents**, built on tmux. The
app (`tmux-ide app`) shows every agent across local and SSH machines with
ground-truth working/blocked/done status, and drives the live tmux session.
`tmux-ide adopt` adds the same status to plain tmux clients as tmux chrome: a
status bar, keys and menus, all additive tmux options with no wrapper process.
Around both: notifications when an agent needs a human, and crash-proof restore.
tmux owns every process and pane, so sessions survive tmux-ide. `.tmux-ide/workspace.yml`
is optional.

## When to use

- User mentions tmux, a dock/status bar over sessions, an agent fleet, or session status
- User wants live working/blocked/done status across multiple agents or panes
- **You are an agent and want to report your own status** so the dock/fleet reflects it (the agent contract, below)
- Post-crash recovery — a tmux server died and the user wants their fleet + Claude conversations back
- User wants a git worktree (plus an adopted session) per branch
- User wants to set up a multi-pane dev workspace with `.tmux-ide/workspace.yml`

## The agent contract

**This is the core of tmux-ide.** Detection is two-layer, and an agent that
reports its own state is the authoritative layer — the dock trusts it over any
screen-scraping. If you are an agent running in a tmux pane, self-report by
setting a pane-local tmux option:

```bash
tmux set-option -p @agent_state "<state>:$(date +%s)"   # state = working | blocked | done | idle
```

The value is `<state>:<unix-epoch>`. A `working`/`blocked` report older than ~10
minutes is treated as stale (the detector falls back to Layer 2), so long-running
agents should re-stamp periodically. Two optional companions:

```bash
tmux set-option -p @agent_session_id "<id>"   # your own session id — powers restore --resume-agents
tmux set-option -p @agent_hint claude          # force which agent manifest Layer 2 uses for this pane
```

**Display metadata** — say WHAT you're doing and WHO you are, right in the
fleet UI. Two more optional pane-local options:

```bash
tmux set-option -p @agent_status_text "refactoring auth"  # one-liner, ≤32 chars — shows in the pane chip ("● claude · refactoring auth")
tmux set-option -p @agent_display_name "reviewer"          # your name — replaces the detected kind in sidebar rows & chips
```

Plain text only (control characters are stripped, tabs break the line for your
pane — don't stamp them; anything past 32 chars is ellipsized). Both surface in
`tmux-ide team --json` (per-pane `statusText` / `displayName`), the unified
app's pane chips, and the sidebar agent rows. They follow the SAME staleness
rules as `@agent_state`: they only show while your state stamp is fresh, so
re-stamp `@agent_state` alongside — update the text whenever your focus
changes, and it disappears with a stale/cleared state instead of lying.

**Claude Code users get this for free** — `tmux-ide integration install claude`
writes a POSIX hook into `~/.claude/settings.json` that stamps `@agent_state` on
every lifecycle event (UserPromptSubmit/PreToolUse → working, Notification →
blocked, Stop → done, SessionEnd → idle) and records `@agent_session_id`. It
takes effect for **new** Claude Code sessions; the merge is reversible
(`integration uninstall claude`).

**Session-id capture for other kinds** (what `restore --resume-agents` resumes
from): codex and cursor-agent panes are stamped **automatically** — the chrome
updater reads each CLI's own on-disk session state; opencode gets a plugin via
`tmux-ide integration install opencode`. `tmux-ide integration status` shows
what's active. Kinds without a verified resume story (gemini, aider, copilot, …)
can self-report the id as above.

**How detection layers work:** Layer 1 is the authority above — a fresh
`@agent_state` option is ground truth. When none is present, Layer 2 resolves the
agent from the pane's process tree and reads the visible screen against
evidence-tuned per-agent manifests to infer working/blocked/done. Run
`tmux-ide agent explain <pane>` to see exactly which layer fired for a pane and why.

### Coordinating with other agents

The status bus is shared, so you can work as part of a team — and the teammates
don't have to be Claude Code. As an agent, you can:

```bash
tmux-ide team --json                     # fleet rollup: each session's + window's agent status
tmux-ide agent explain %2 --json         # one specific pane's status + why (per-pane read)
tmux-ide send %2 "do X, then run tests"  # task another pane's agent (by %id, title, role, or @ide_name)
tmux-ide wait output %2 --match "done"   # block until that pane prints something (exit 0 match / 1 timeout)
tmux-ide wait agent-status api --status done   # block until a whole session finishes
tmux-ide events --follow                 # subscribe to the live session-status transition stream
```

`send` types straight into the target agent's prompt (use `--no-enter` to stage
text; pipe stdin for long input — messages over ~150 chars auto-route through a
`.tasks/dispatch/` file). Report your own status with the `@agent_state` contract
above so teammates coordinating on you see the truth. This works across
Claude Code, codex, cursor-agent, aider, or any CLI agent in a pane.

## Fleet control from the CLI

Every command takes `--json` for structured output.

```bash
tmux-ide team --json                      # whole-fleet state: sessions, panes, agent statuses
tmux-ide events --follow                   # stream agent-status transitions (needs an adopted session)
tmux-ide events --json                     # recent transitions as JSON

tmux-ide wait agent-status <session> --status blocked --timeout 60000   # block until a session hits a status
tmux-ide wait output <pane|session> --match "<regex>" --timeout 60000   # block until a pane's output matches

tmux-ide send <target> "<message>"        # send text to a pane (by name/title/role/ID); --to <name>, --no-enter
tmux-ide agent explain <pane> --json       # debug how a pane's agent state was detected

tmux-ide adopt <session>                   # add the dock to an existing session (additive tmux config)
tmux-ide adopt --all                       # adopt every live session
tmux-ide unadopt <session>                 # remove the dock — sessions keep running as plain tmux

tmux-ide restore --dry-run --json          # preview rebuilding the fleet from the last snapshot
tmux-ide restore --resume-agents           # rebuild after a tmux crash; revive agent convos (claude/codex/cursor/opencode)

tmux-ide worktree create <branch> --from <ref>   # git worktree on a new branch + a session in it
tmux-ide worktree open <branch>            # open/switch to an existing worktree's session
tmux-ide worktree list --json              # worktrees joined with their session status
tmux-ide worktree remove <branch> --force  # kill the session + remove the worktree

tmux-ide update --dry-run                  # detect install method (dev checkout vs npm/pnpm/bun) and show/run the update
tmux-ide doctor                            # system + integration health (tmux version, TUI surfaces, skill freshness)
```

## Drive tmux-ide over the socket (agent loops)

The CLI above spawns a process per call. If you are driving the fleet in a
loop — polling status, waiting on agents, reacting to transitions — start the
control server once and keep ONE connection open instead:

```bash
tmux-ide serve &   # local Unix socket at ~/.tmux-ide/control.sock (0600, this user only)
```

**Frame format:** newline-delimited JSON, one object per line. Send
`{"v":1,"id":<any>,"verb":"<verb>","params":{…}}`; you get back
`{"v":1,"id":<same>,"ok":true,"data":…}` or
`{"v":1,"id":<same>,"ok":false,"error":{"code","message"}}`. Responses
correlate by `id` (they may arrive out of order — a long `wait` doesn't block
other verbs on the same connection). After the `subscribe` verb the server
also PUSHES unsolicited `{"v":1,"event":"agent-status","data":{ts,session,from,to}}`
frames the moment its detection tick sees a session change state — no polling.

**Verbs:** `fleet` (the `team --json` payload) · `agents` (per-pane entries,
optional `{session}`) · `send` (`{session,target,message,noEnter?,dir?}`) ·
`wait` (`{kind:"agent-status",session,status,timeoutMs?}` or
`{kind:"output",target,match,timeoutMs?}`; a timeout is an error response with
code `timeout`) · `spawn` (`{kind|command, session?|sessionName, dir?,
placement?, paneId?}` → the new `paneId`) · `restart-agent` / `stop-agent`
(`{paneId, kind|command}`) · `explain` (`{target}`) · `subscribe`.

One-shot from a shell (nc keeps the pipe open for the response):

```bash
printf '{"v":1,"id":1,"verb":"fleet"}\n' | nc -U ~/.tmux-ide/control.sock | head -1
```

A subscribe loop from node:

```js
const net = require("node:net");
const os = require("node:os");
const sock = net.connect(`${os.homedir()}/.tmux-ide/control.sock`);
let buf = "";
sock.on("data", (chunk) => {
  buf += chunk;
  const lines = buf.split("\n");
  buf = lines.pop();
  for (const line of lines.filter(Boolean)) {
    const frame = JSON.parse(line);
    if (frame.event === "agent-status") console.log(frame.data); // react here
  }
});
sock.write('{"v":1,"id":1,"verb":"subscribe"}\n');
```

Right after `subscribe`, the first tick reports every session once with
`from:null` (a snapshot of where the fleet stands); real transitions follow.

**Socket vs CLI:** prefer the socket for anything event-driven or repeated
(subscribe replaces an `events --follow` poll; a server-held `wait` costs no
spawn-per-poll). Prefer the CLI for one-shot reads and anything a human might
re-run — it needs no server. With a server running, `tmux-ide events --follow
--socket` and `tmux-ide wait … --socket` use it automatically and fall back to
polling silently when it's gone. The server is local-only by design: no
network listener, no tokens — filesystem permissions are the auth.

## Keys & surfaces to tell USERS about

Once a session is adopted, the whole UI is a keystroke away. **Lead with the
prefix** — an agent pane can temporarily change key encoding and swallow a
root-table `Alt` bind, but the tmux prefix always reaches tmux. Every surface has
a prefix twin and an `⌥` fast-path (single keystroke when the terminal allows it).
Right-click any pane or the bar opens the actions menu at the pointer.

| Surface                                    | Prefix (always works) | ⌥ fast-path    |
| ------------------------------------------ | --------------------- | -------------- |
| Home cockpit — fleet tree, detail, preview | `prefix h`            | `⌥h`           |
| Switch session                             | `prefix j`            | `⌥p`           |
| Cheat sheet — every key on one page        | `prefix k`            | `⌥k`           |
| Actions menu (or right-click)              | `prefix u`            | `⌥m`           |
| Sidebar — fleet nav column                 | `prefix b`            | `⌥b`           |
| Panels — explorer / changes / config       | `prefix e` `g` `v`    | `⌥e` `⌥g` `⌥,` |

One interaction grammar everywhere: `j`/`k` move, `enter` opens, `/` filters,
`esc` backs out, `?` asks. Bare `tmux-ide` with no project config opens the
**app** (below). `tmux-ide cheatsheet` prints the full sheet.

## The app — `tmux-ide app`

The full-screen app: tmux stays the engine (PTYs, agents, persistence); the app
renders it. Launch `tmux-ide app` (Home) or `tmux-ide app <session>`. Installed
releases include the runtime; `tmux-ide update --tui-binary` re-downloads it.

- **Two surfaces**: `F1` Home (agents across local + SSH machines; `/` search,
  `f` machine filter, `0`/`w`/`a` All/Working/Needs attention, `Enter` opens the
  agent's pane) and `F2` Terminals (the session mirrored live, window tabs, pane
  headers with agent state). There are no Files/Diff/Missions views in 2.9, and
  `app.views` in workspace.yml is validated but not read.
- **Overlays**: `F5` Commands (new window, New agent…, splits, zoom, Appearance…,
  help) · `F6` Sessions · `F7` Attention · `F8`/`F9` session history/tabs ·
  `F10` sidebar · `Ctrl+G` focus sidebar. `Ctrl+K` in Commands lists every key.
- **Terminals**: `Ctrl+O`/`Ctrl+T` next pane/window, `Alt+Arrow` resize, drag
  borders; right-click a pane for select text / rename / split / zoom / close
  (confirmed). Shift+click opens links; Shift+drag selects inside mouse apps.
- `Ctrl+Q` quits — sessions keep running. `tmux-ide app --detachable` hosts the
  app in tmux so `Ctrl+Q` detaches instead.

## .tmux-ide/workspace.yml (optional)

Adopt works on any session. If you'd rather have tmux-ide build the layout, describe
it in `.tmux-ide/workspace.yml` (sessions launched from a config are adopted automatically).

**Setup workflow for a user's project:**

1. Check state: `tmux-ide status --json`
2. Detect the stack: `tmux-ide detect --json`
3. **Present 2-3 layout options as ASCII diagrams** before writing config:

   **Option A — Claude + Dev (recommended)**

   ```
   ┌─────────────────────────────────────┐
   │             Claude                  │  70%
   ├──────────┬──────────┬──────────────┤
   │ Dev Srv  │  Tests   │    Shell     │  30%
   └──────────┴──────────┴──────────────┘
   ```

   **Option B — Dual Claude**

   ```
   ┌─────────────────┬─────────────────┐
   │    Claude 1     │    Claude 2     │  70%
   ├────────┬────────┴───────┬─────────┤
   │Dev Srv │     Tests      │  Shell  │  30%
   └────────┴────────────────┴─────────┘
   ```

   **Option C — Explorer + Claude + Changes (widget panes)**

   ```
   ┌──────────┬───────────────┬─────────┐
   │ Explorer │    Claude     │ Changes │  100%
   │ (widget) │               │ (widget)│
   └──────────┴───────────────┴─────────┘
   ```

   Adapt pane names/commands to the detected stack (`pnpm dev`, `cargo watch`, …).

4. Write it — quick path `tmux-ide detect --write`, or build with the config CLI:
   ```bash
   tmux-ide config add-row --size 70%
   tmux-ide config add-pane --row 0 --title Claude --command claude
   tmux-ide config add-row --size 30%
   tmux-ide config add-pane --row 1 --title "Dev Server" --command "pnpm dev"
   tmux-ide config add-pane --row 1 --title Shell
   tmux-ide validate --json      # always validate after mutations
   ```

**Schema:**

```yaml
version: 1
name: my-app # tmux session name
before: pnpm install # optional pre-launch shell hook
terminal:
  theme: # optional per-session pane colors
    accent: colour75
    border: colour238
  rows:
    - size: 70% # row height percent (rows split evenly if omitted)
      panes:
        - title: Claude # pane border label
          command: claude # command to run (optional)
          size: 50% # pane width percent (optional)
          dir: apps/web # per-pane working directory (optional)
          focus: true # initial focus (optional)
          env: # environment variables (optional)
            PORT: "3000"
    - panes:
        - title: Explorer
          type: explorer # widget pane: explorer | changes | preview | config
          target: src/ # optional widget target path
        - title: Shell
```

Read config with `tmux-ide config --json`; mutate with `config set <dot.path> <value>`,
`add-pane`, `remove-pane`, `add-row`; apply changes to a running session with
`tmux-ide restart`.

Mission runtime wiring is future work for the workspace config model. Do not add
mission or orchestrator runtime fields to `.tmux-ide/workspace.yml` yet.

## Config — ~/.tmux-ide/config.json

The one product-wide config (override path with `TMUX_IDE_CONFIG`). A deep
partial merge over defaults — any block or field you omit falls back:

```jsonc
{
  "keys": {
    "home": "M-h",
    "popup": "M-p",
    "cheatsheet": "M-k",
    "menu": "M-m",
    "sidebar": "M-b",
    "panels": { "explorer": "M-e", "changes": "M-g", "config": "M-," },
  },
  "theme": {
    "accent": "colour75",
    "muted": "colour240",
    "fg": "colour250",
    "status": {
      "blocked": "colour203",
      "working": "colour221",
      "done": "colour111",
      "idle": "colour114",
      "unknown": "colour244",
    },
    "glyphs": { "active": "●", "inactive": "○" },
  },
  "notifications": { "toast": true, "macos": false },
  "restore": { "resumeAgents": false },
  "updates": { "check": true },
  "integrations": { "offer": true },
}
```

One palette + one keymap drive every surface (status bar, chips, menu, cheat
sheet, and the OpenTUI widgets), so re-theming the whole product is a one-file
edit plus a re-adopt.

## Keeping this skill current

This file is managed — installs and `tmux-ide update` (dev checkouts) refresh the
copy under `~/.claude/skills/tmux-ide`. To refresh it manually at any time, run
`tmux-ide skill-sync`. `tmux-ide doctor` reports when the installed copy is stale.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…