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.
What to read next¶
For the principles that recur across these recipes – discover before acting,
wait instead of polling, content vs. metadata, prefer IDs, escalate
gracefully – see the prompting guide. For specific
pitfalls like enter: false and the send_keys/capture_pane race
condition, see gotchas.