Working in tmux
Heph is built to be left alone. A turn takes minutes; you start one, go and do something else, and come back when the model needs an answer or has finished. tmux is where that way of working lives, and heph is made to fit it: the session takes one pane and stays out of the others, it announces itself when it needs you, and it can be found from anywhere. This chapter is how the pieces are meant to go together. Nothing here is required; heph runs in a bare terminal too.
heph tmux list now selects the current project's live panes, including from
subdirectories. Use heph tmux list --all-projects to see all panes, or
heph tmux list 'auth|login' to match names and saved branch metadata.
--match is the explicit regex spelling. Unknown pane identities appear only
globally, labeled as unknown projects. status and next remain global
attention tools.
One pane per project
Give each project a heph of its own, in a pane or window of that project's tmux session, and leave it running. The conversation persists, so there is no cost to leaving the pane open between prompts, and a heph that is already open is one you can hand something to without ceremony.
A typical window:
┌──────────────────────────────────┬──────────────────────────────────┐
│ $ nvim src/parse.rs │ You: the parser rejects @tests/ │
│ │ fixtures/odd.toml; why? │
│ │ │
│ │ ok view src/parse.rs │
│ │ ok grep quoted │
│ │ >> shell cargo test parse 0:04 │
│ │ | running 12 tests │
├──────────────────────────────────┤ │
│ $ git log --oneline -5 │ │
│ $ │ message ─────────────────────── │
│ │ > │
│ │ main | running shell 0:04 | ... │
└──────────────────────────────────┴──────────────────────────────────┘
Your editor and a shell on the left, heph on the right. The two sides look at different trees: your editor is in your working tree; heph's model works in a worktree, a copy that mirrors your tree as it was when the turn began. Nothing the model does shows up on the left until you accept it in review, and nothing you type on the left is seen by the model until the next turn begins, when the worktree is refreshed from your tree.
That separation is what makes it safe to keep editing while a turn runs. If you change a region the model also changed, review sets the model's hunk aside and keeps yours (Rejections, Comments, and the Next Round).
Looking at the worktree
Sometimes you want to see what the model has built before review opens, or run
something against it. !cmd runs a command in the worktree from inside heph
(Running Commands Yourself); !pwd prints the worktree's path, and
you can cd there in another pane:
$ cd ~/.local/share/heph/wt/6746d59d741e4d0e/01a0c4e6-.../
$ git diff HEAD
It is an ordinary git checkout. git diff HEAD there is the model's change so
far; git log shows your project's history beneath it. Read all you like;
writing there by hand is not a good idea while a turn runs, since the model will
not know.
Tools that take the screen
When review opens in lazygit, or Ctrl-E opens your editor, heph suspends its
screen in the pane and gives the pane to the tool, as vim does for :!. The
tool runs in the same pane, at the pane's size; when it exits, heph redraws.
Nothing else in the window is disturbed. A pane wide enough for lazygit's three
columns is a pane wide enough for heph.
Being reached
The status bar at the bottom of heph's pane shows what the turn is doing. Once
you switch to another window it is out of sight, so heph also sends a
notification whenever it needs you or a wait ends: a
question, a permission prompt, a review opening, a turn finishing. Under tmux
without allow-passthrough, that is a tmux display-message, which appears in
the status line of whichever window you are in. With passthrough, it is your
terminal's own notification.
Nothing is sent while you are looking at the pane. tmux itself knows whether heph's pane is the active pane of an attached client, and heph asks it.
Several hephs
With a heph per project and several projects open, the question becomes "which
one needs me?". Every heph publishes its state as tmux pane variables, and
heph tmux reads them across all sessions. Publication runs on a bounded
background worker; slow or unavailable tmux does not block keyboard input.
Failures retry after five seconds, each subprocess has a one-second deadline,
and exit cleanup waits at most two seconds. Two lines in tmux.conf put the
count of waiting hephs in your status bar and bind a key that jumps to the one
most in need of you.
heph tmux: finding the one that needs you
Put this in tmux.conf (heph tmux conf prints it):
set -g status-interval 5
set -g status-right '#(TMUX="#{socket_path},#{pid},0" heph tmux status) | %H:%M '
bind H run-shell 'TMUX="#{socket_path},#{pid},0" heph tmux next --client "#{client_name}" --pane "#{pane_id}"'
Now the status bar says heph 1 waiting / 1 busy (heph 0 waiting / 0 busy
when no heph is running; the waiting count turns red when anything waits), and
prefix H jumps to the pane most in need of you: a question outranks a
permission prompt, which outranks a review, which outranks a finished turn.
Press again to cycle among equals. The binding identifies the client and pane
that invoked it, so detach/reattach and multiple attached clients do not confuse
the jump. It switches that client, window, and pane, in any session.
Both commands explicitly select the tmux server that invoked them, rather than
relying on environment changes after attaching locally or over SSH. If you used
the older snippet, replace it and reload your configuration with
tmux source-file ~/.tmux.conf (or your configuration's actual path). These
commands also work with older heph versions; they use no new CLI flags.
Counts come from pane variables, without waiting for the session's JSON status
reply. A live heph stays counted while an editor or review tool owns its pane,
and differences in the running session's status schema do not affect counts.
Linked windows count only once, no matter how many sessions or clients see them.
If querying tmux itself fails, the segment says heph status unavailable rather
than silently disappearing or reporting zero activity.
$ heph tmux list
PANE PROJECT REF STATE WAITING SPEND
work:1.1 api-server main permission 1 $0.31
docs:0.0 handbook main done 0 $0.02
heph tmux opens no database and reads no config; it runs from tmux's own
status line in milliseconds. A pane whose heph has exited is ignored even if its
variables were left behind.
The variables
| Variable | Value |
|---|---|
@heph-state | asking, permission, review, paused, done, idle, or working |
@heph-ref | The conversation's human label (<unnamed> when absent) |
@heph-ref-id | Its full UUID, used for switching rather than the label |
@heph-waiting | 1 when the session is waiting on you, else 0 |
@heph-project | The project directory's name |
@heph-sock | The session's control socket |
Changed values are published on state transitions; missing options are repaired
every five seconds, even while idle. Repairs leave existing options untouched.
This avoids periodic tmux redraws that can clear your terminal's text selection,
even in other sessions with no heph pane. Options are unset when heph exits.
Read-only control-socket status queries do not redraw heph's screen. You can use
the variables in your own status line: #{@heph-state} in a pane-format string.
The control socket
Each session listens on $XDG_RUNTIME_DIR/heph/<pid>.sock, mode 0600, and
removes it on exit. It accepts two lines and nothing else:
-
statusanswers with the session's state as JSON: the active conversation, its state, its spend, whether it is waiting. -
switch NAMEasks the session to switch conversations. Switching arrives in a later phase; today the session acknowledges and says so.
heph tmux list uses status to show spend; heph tmux next uses it to check
which conversation is active. There is no way to send a prompt, approve a call,
or read a conversation through the socket.