The Interactive Session
heph with no subcommand opens the interactive session on the project in the
current directory (-C DIR for another). It takes the terminal over until you
leave by pressing Ctrl-D twice.
Conversations
Plain heph creates a fresh conversation every time, initially displayed as
<unnamed>. Opening it or typing a draft starts no turn and calls no model. The
UUID is its identity; empty conversations and drafts survive exit and crash. You
can also start with heph 'fix the tests' or heph fix the tests. The initial
prompt follows the ordinary submission and base-branch choice flow. Until
submission succeeds it is kept as a draft.
The first submission saves a prompt-derived name immediately, before provider
setup or turn creation. It uses up to three non-filler ASCII words, capped at 24
characters each, or a short prompt hash if none remain. Once the prompt is
stored, the title role may improve that name asynchronously. Input, the main
request, and review do not wait for it. Failure or cancellation keeps the local
name; unusable output does not become session-N. /rename NAME takes
precedence over a delayed title, even when choosing the local name. Reopening
retries a pending title once; legacy unnamed conversations with stored input get
a local name first.
heph --continue reopens the most recently active branch conversation.
heph --continue NAME or heph --continue FULL-UUID opens exactly that
existing conversation, with its history, draft, routing, and pending review.
Missing or locked targets error, never create replacements. A startup prompt
cannot accompany --continue; restored drafts are not automatically submitted.
Fresh startup uses configured routing plus flags, not the previous ref's
routing.
heph --continue-branch BRANCH resumes by an exact current or recorded
publication branch in this checkout. One match opens directly; several prompt
with session metadata and cached /summary content so you can choose. Enter or
q cancels. It excludes startup prompts and other startup selectors. See
Resuming by publication branch.
Inside a session, /stats shows the active conversation's full UUID as
Session ID: at the top, even while accounting loads or fails. Use that ID with
heph --continue FULL-UUID or /switch FULL-UUID to reopen it.
/refs lists labels, full UUIDs, activity, tips, and branches; /switch NAME
or /switch FULL-UUID opens one while idle. Names are exact and
case-insensitive; UUID prefixes and <unnamed> are not selectors.
/new <prompt> starts another root with the same immediate, nonblocking naming
flow, inheriting the active routing and base. /fork <prompt> keeps its
naming-before-creation flow (Forking and Switching Conversations).
A long history is rendered one screenful at a time. A resize or sidebar toggle
keeps the source position of a scrolled view; it does not rewrap the entire
conversation. Live appends keep compatible displayed prefixes while replacement
layout runs, and running-tool clocks do not replace the conversation with a
loading pane. A genuinely cold layout may briefly say rendering history while
the input and sidebar remain responsive; unavailable regions keep their
neighbors. more below indicates that newer content follows the view, without
measuring all preceding rows.
Draft durability
Every edit requests an asynchronous save. The input border shows
draft pending, draft saved, or draft save failed; only saved input has
been acknowledged as committed. Unsent intermediate versions coalesce. A crash
can lose unacknowledged edits, with no fixed time bound while SQLite is busy.
Submission waits for the original prompt's save before starting its runner. Ref
changes drain old-ref saves before releasing its lease. You can keep typing
while these operations wait; Enter keeps your input, and Esc cancels the action.
Normal quit freezes input and flushes for up to ten seconds. On save failure,
R retries and Esc cancels. Failed quit also offers X to explicitly leave
without saving pending changes. Reopening restores the last committed draft,
never automatically submits it.
Tool-result cost diagnostics
/stats shows estimated generating-request shares and a separate tool-result
lifetime section. It reports recorded result appearances, replay input,
following output, and their retrospective lifetime estimate. Full results and
later one-line stubs are captured as distinct prepared-request forms.
These are not extra charges or causal measurements. Costs are allocated by result bytes relative to the request's local input estimate, with cache pricing averaged within that request. Images are particularly approximate. Generating and following-output shares overlap; never add them to the provider total. Older requests have no snapshots, and estimated requests may not have reached the provider. There is no forecast of future replay rounds.
Work summaries
/summary reads a cached memory jogger: generation asks for 3-5 short Markdown
bullets, at most 100 words, covering the current goal, key progress or decision,
and the next step or blocker rather than an exhaustive recap. The UI renders
Markdown, including in older cached summaries. Use /summary --refresh to
replace an older, longer summary with the terse format.
It shows the covered turn and age, and warns if the ref has moved since generation. The cache survives reopening and renaming; a new or forked ref gets its own summary. An empty cache says how to request one.
Automatic generation runs after the first completed turn, then after five new
turns or fifteen minutes since the last summary, whichever comes first. These
checks happen only when a turn ends, not while idle. Pending reviews wait.
/summary --refresh regenerates from the stored transcript while idle. Only one
generation runs at a time, and interactive generation never blocks input.
Leaving or switching away cancels it. heph exec also checks the policy and
finishes a due generation before exiting.
Summaries use the summarize model role and record usage on the covered turn.
They do not rewrite history, consume a conversation turn, or change what the
agent remembers. /clear does not erase them. Failed refreshes keep the old
cache; a delayed result is not installed if the ref has moved. Automatic refresh
failures are logged at debug level; requested failures are displayed.
Input is bounded to 80,000 transcript characters, favoring recent turns, with
per-block limits of 12,000 text characters or 1,500 tool characters. Omitted
content is marked. Automatic updates also carry the previous summary when it
belongs to the current chain. Output uses [context] summary_max_tokens, capped
by the selected tier's token limit. This is a model-generated overview, not a
replacement for reviewing the actual changes or test results.
The turn, on screen
You type a prompt and press Enter. From then until the turn ends, the
conversation fills in as things happen:
-
While the model is being waited on, a dim
* ...with moving dots stands where its reply will go: right afterEnter, and again after each tool call while the next round is on its way. -
The model's text streams in as it is written.
-
When the model thinks before answering, the thinking is folded to one line:
* thinking: <its first line> (Right to open).RightorLeftwith the input empty unfolds and folds the nearest thing that can be: the last tool call, or the last reply that has thinking. Thinking is the model's scratch work; heph never treats it as the model's stated reason for a change. -
Each tool call is one line: a mark, the tool, and what it is acting on.
..means the model is still sending the call;>>means it is running, with a clock beside it;okmeans it finished;!!means it failed. A finished call'sokis magenta when you approved it and green when it ran on its own. -
A running command streams its last few lines of output under its line. When it finishes they fold away; unfolding the call shows all of its output and its full result, colored as a diff when it is one.
-
For work with several steps the model keeps a task list with the
todostool. Its line names the step in progress and the count done, and the list shows beneath it,[x]done,[>]in progress,[ ]to come, without unfolding;Leftfolds it. Each call replaces the list, so the newest is the one to read. -
Lines beginning
--are heph speaking:running acceptance gates, a gate that failed, a denied call, and the last line of every turn:turn done,turn interrupted, or why it ended, with the turn's cost.
Messages stand apart with a blank line above and below. Tool calls that follow
one another, and -- lines that follow one another, run together as a list.
The status bar uses one row when everything fits. Otherwise it separates
session information (conversation and branch, mode, held locks, activity, edits,
and queued messages) from usage information (live tokens, cost, cache-hit share,
context window usage, and model). Narrower terminals add rows between complete
fields, not in the middle of a label. Oversized names and lock lists end in
... rather than pushing other fields out of view.
Muted totals like $8.00 / $1.00 / $4.00 show today's global local-date spend,
this active process, and this ref's persisted history. Refresh messages stay in
/stats and the sidebar; missing totals show -. Activity is working 0:41,
running shell 0:12, approve?, asking, review, or done. 1 queued
means a message is waiting for the next turn. /stats shows the full
accounting breakdown at any width. locks: ...
lists the worktree lease, publication ownership when held, and
execution resources, including background jobs. Long lock
lists end in ...; full acquisition and release names remain in scrollback.
context N% is muted gray like cache N%. It shows the current request's size
as a percentage of the running model's context window, rounded to a whole
percent, not the session's cumulative tokens. While waiting for a reply, it is
an estimate; after the reply, it counts the provider's input tokens (including
cached tokens) plus output tokens. It is hidden if the window is unknown or no
request has run since opening, switching, or clearing the conversation. If an
Ollama response omits usage counts, the percentage keeps the pre-request
estimate; token and cost totals are labeled incomplete instead of actual zero.
done means a turn finished and you have not touched the pane since. Your next
keypress or scroll clears it. The same test decides whether a
notification is sent, so "done" on the bar and a
notification elsewhere never both happen.
Dialogs
Dialogs default to 120 content columns, plus four columns for borders and spacing. They are centered with at least two columns of outer margin on each side, shrinking to fit narrower terminals.
Every dialog shows the current conversation name centered in its top border,
with the dialog name on the left and metadata on the right. This includes
permission prompts, confirmations, questions, review, tiers, stats, and help.
Permission levels stay muted gray; review's stage and help's closing hint sit on
the right. Long conversation names are shortened with ... to avoid colliding
with either side. On a very narrow dialog the centered name is hidden if there
is no room; side labels are shortened when necessary too.
Scrolling
New output keeps the conversation scrolled to the bottom. PgUp and PgDn
scroll by ten lines, Shift+Up and Shift+Down by one, the mouse wheel by
three, Home and End (with the input empty) jump to the top and bottom. While
you are scrolled up, a yellow marker in the corner counts the lines below you;
any new output arrives without moving your view, and End brings you back.
The wheel works because heph takes the mouse, which is also why dragging does
not select text as it does at the shell: hold Shift while you drag, and the
terminal selects as usual. When a permission prompt, a question, or a review is
up, the wheel scrolls that instead of the conversation behind it.
Leaving
Ctrl-D opens an exit confirmation. Press Ctrl-D again to leave; any other
key cancels, leaving your input untouched. A half-typed prompt is saved and
comes back next time. If a turn is running, the confirmation warns you; leaving
anyway cancels it, and the next start closes it as interrupted with its work
kept for review.
/quit and /exit leave immediately, without the confirmation. After shutdown,
heph restores your terminal and prints the active session's latest saved
metadata to stderr:
Session ended
ID: 0195e123-4567-7abc-8123-456789abcdef
Name: fix-parser
Base: main
Branch: heph/fix-parser
The ID is the full UUID you can pass to heph --continue. An unnamed session
shows <unnamed>; a base or publication branch not yet chosen shows <unset>.
This summary also appears after leaving with two presses of Ctrl-D, but not on
startup failure or unexpected event-loop termination.
/clear starts the model fresh on the same conversation: when the work so far
is done and the next thing is a new thread, the next turn is sent nothing of the
earlier ones, which makes every round of it cheaper. Live token/cache counters
restart, but daily, active-session, and historical spend do not. The worktree,
uncommitted work, and the history are kept; the screen empties, and the next
heph shows the earlier turns again above a line marking the clear. Use
/clear <prompt> to clear and immediately send the prompt; a refused clear
sends nothing. A running turn or pending review must finish first. /help and
? on an empty input show the key bindings. /commit commits what the last
turns applied (Committing). /plan, /design, and /code
switch plan and design mode on and off (Plan Turns,
Design Turns). Those are the slash commands so far; anything
else answers not a command yet.