Recipes

Each recipe starts from a real workspace situation and traces the agent’s reasoning through discovery, decision, and action. The goal is not to show tool-call sequences – it is to show how an agent decides what to do with existing tmux state so you can write better prompts and system instructions.

Every recipe uses the same structure:

  • Situation – the developer’s world before the agent acts

  • Prompt – the natural-language sentence that triggers the recipe

  • Discover – what the agent inspects and why

  • Decide – the judgment call that changes the plan

  • Act – the minimum safe action sequence

  • The non-obvious part – the lesson you would miss from reading tool docs alone


Find a running dev server and test against it

Situation. A developer manages a React project with tmuxp. One pane is already running pnpm start with Vite somewhere in the react window. They want to run Playwright e2e tests. The agent does not know which pane has the server, or what port it chose.

Prompt

Run the Playwright tests against my dev server in the myapp session.

Discover

Agent reasoning

list_panes will not help here – it shows metadata like current command and working directory, not terminal content. The dev server printed its URL to the terminal minutes ago, so I need to search terminal content.

The agent calls search_panes with pattern: "Local:" and session_name: "myapp". The response comes back with pane %5 in the react window, matched line: Local: http://localhost:5173/.

Decide

Agent reasoning

The server is alive and its URL is known. I do not need to start anything. I just need an idle pane for running tests.

The agent calls list_panes on the myapp session. Several panes show pane_current_command: zsh – idle shells. It picks %4 in the same window.

Act

The agent calls send_keys in pane %4: PLAYWRIGHT_BASE_URL=http://localhost:5173 pnpm exec playwright test

Then it calls wait_for_text on pane %4 with pattern: "passed|failed|timed out", regex: true, and timeout: 120. Once the wait resolves, it calls capture_pane on %4 with start: -80 to read the test results.

Tip

The agent’s first instinct might be to start a Vite server. But search_panes reveals one is already running. This avoids a port conflict, a wasted pane, and the most common agent mistake: treating tmux like a blank shell.

The non-obvious part

search_panes searches terminal content – what you would see on screen. list_panes searches metadata like current command and working directory. If the agent had used list_panes to find a pane running node, it would know a process exists but not whether it is ready or what URL it chose.


Start a service and wait for it before running dependent work

Situation. The developer is starting fresh in their backend session – no server running yet. They want to run integration tests, but the test suite needs a live API server.

Prompt

Start the API server in my backend session and run the integration tests once it’s ready.

Discover

Agent reasoning

First I need to know what exists in the backend session. If a server is already running, I should reuse it instead of starting a duplicate.

The agent calls list_panes for the backend session. No pane is running a server process. A search_panes call for "listening" returns no matches.

Decide

Agent reasoning

Nothing to reuse. I need a dedicated pane for the server so its output stays separate from the test output.

Act

The agent calls split_window with session_name: "backend" to create a new pane, then calls send_keys in that pane: npm run serve.

The agent calls wait_for_text on the server pane with pattern: "Listening on" and timeout: 30. Once the wait resolves, the agent calls send_keys in the original pane: npm test -- --integration, then wait_for_text with pattern: "passed|failed|error" and regex: true, then capture_pane to read the test results.

Warning

Calling capture_pane immediately after send_keys is a race condition. send_keys returns the moment tmux accepts the keystrokes, not when the command finishes. Always use wait_for_text between them.

The non-obvious part

wait_for_text replaces sleep. The server might start in 2 seconds or 20 – the agent adapts. The anti-pattern is polling with repeated capture_pane calls or hardcoding a sleep duration. The MCP server handles the polling internally with configurable timeout (default 8s) and interval (default 50ms).


Find the failing pane without opening random terminals

Situation. The developer kicked off multiple jobs across panes in a ci session – linting, unit tests, integration tests, type checking. One of them failed, but they stepped away and do not remember which pane.

Prompt

Check my ci session – which jobs failed?

Discover

Agent reasoning

I should not capture every pane and read them all – that is expensive and slow. Instead I will search for common failure indicators across all panes at once.

The agent calls search_panes with pattern: "FAIL|ERROR|error:|Traceback", regex: true, scoped to session_name: "ci".

Decide

Agent reasoning

Two panes matched: %3 has FAIL: test_upload and %6 has error: Type 'string' is not assignable. I will capture context from each.

Act

The agent calls capture_pane on %3 with start: -60, then on %6 with start: -60.

Tip

If the error scrolled off the visible screen, use content_start: -200 (or deeper) when calling search_panes. The content_start parameter makes search reach into scrollback history, not just the visible screen.

The non-obvious part

search_panes checks all panes in a single call – searching 20 panes costs roughly the same as searching 2. An agent that instead calls list_panes then capture_pane on each one individually makes 20+ round trips for the same information. The regex: true parameter is required here because the | in the pattern is a regex alternation, not literal text.


Interrupt a stuck process and recover the pane

Situation. A long-running build is hanging. The developer wants to interrupt it, verify the pane is responsive, and re-run the command.

Prompt

The build in pane %2 is stuck. Kill it and restart.

Or with less specificity — the agent will discover the target:

Prompt

The build in one of my panes is stuck. Kill it and restart.

Or if you’ve built muscle memory in your chats:

Prompt

The build is stuck. Kill it and restart.

Discover

Agent reasoning

I need to send Ctrl-C. This is a tmux key name, not text – so I must use enter: false or tmux will send Ctrl-C followed by Enter, which could confirm a prompt I did not intend to answer.

The agent calls send_keys with keys: "C-c" and enter: false on the target pane.

Decide

Agent reasoning

Did the interrupt work? Some processes ignore SIGINT. I will wait briefly for a shell prompt to reappear. Developers use custom prompts, so I cannot just look for $.

The agent calls wait_for_text with pattern: "[$#>%] *$", regex: true, and timeout: 5.

Agent reasoning

If the wait resolves, the shell is back. If it times out, the process ignored Ctrl-C. I will escalate: try SIGQUIT (C-\ with enter: false), then destroy and replace the pane only as a last resort.

Act

If the wait times out, the agent sends C-\ (also with enter: false). If that also fails, it calls kill_pane on the stuck pane, then split_window to create a replacement, then send_keys to re-run.

Warning

The enter: false parameter is critical. Without it, send_keys sends Ctrl-C then Enter, which could confirm a “really quit?” prompt, submit a partially typed command, or enter a newline into a REPL.

The non-obvious part

Recovery is a two-step decision. Try SIGINT first (Ctrl-C), verify it worked with wait_for_text, escalate to SIGQUIT only if needed. The escalation ladder is: interrupt, verify, escalate signal, destroy. Skipping straight to kill_pane loses the pane’s scrollback history and any partially written output that might explain why it hung.


Re-run a command without mixing old and new output

Situation. The developer wants pytest re-run in tmux, but the candidate pane already has old test output in scrollback. They want only fresh results.

Prompt

Run pytest in the test pane and show me only the fresh output.

Discover

The agent calls list_panes to find the pane by title, cwd, or current command. If more than one pane is plausible, it uses capture_pane with a small range to confirm the target.

Decide

Agent reasoning

The pane is a shell. I should clear it before running so the capture afterwards contains only fresh output. If it were running a watcher or long-lived process, I would not hijack it – I would use a different pane.

Act

The agent calls clear_pane, then send_keys with keys: "pytest", then wait_for_text with pattern: "passed|failed|error" and regex: true, then capture_pane to read the fresh output.

The non-obvious part

clear_pane runs two tmux commands internally (send-keys -R then clear-history) with a brief gap between them. Calling capture_pane immediately after clear_pane may catch partial state. The wait_for_text call after send_keys naturally provides the needed delay, so the sequence clear-send-wait-capture is safe.


Build a workspace the agent can revisit later

Situation. The developer wants a durable project workspace – not just a quick split, but a layout that later prompts can refer to by role (“the server pane”, “the test pane”).

Prompt

Set up a tmux workspace for myproject with editor, server, and test panes.

Discover

Agent reasoning

Before creating anything, I need to check whether a session with this name already exists. Creating a duplicate will fail.

The agent calls list_sessions. No session named myproject exists.

Decide

Agent reasoning

Safe to create. I need three panes: editor, server, tests. I will create the session, split twice, then apply a layout so tmux handles the geometry instead of me calculating sizes.

Act

The agent calls create_session with session_name: "myproject" and start_directory: "/home/dev/myproject". Then split_window twice (with direction: "right" and direction: "below"), followed by select_layout with layout: "main-vertical".

The agent calls set_pane_title on each pane: editor, server, tests.

The agent calls send_keys in the server pane: npm run dev, then wait_for_text for pattern: "ready|listening|Local:" with regex: true and timeout: 30.

Tip

If the session does already exist, the right move is to reuse and extend it, not recreate it. The list_sessions check at the top is what makes that decision possible.

The non-obvious part

Titles and naming are not cosmetic. They reduce future discovery cost. When the agent comes back in a later conversation and the user says “restart the server,” the agent calls list_panes, finds the pane titled server, and acts – no searching, no guessing, no capturing every pane to figure out which one is which. But note: pane IDs are ephemeral across tmux server restarts, so the agent should always re-discover by metadata (session name, pane title, cwd) rather than trusting remembered %N values.