Non-interactive Use: heph exec
heph exec runs one turn and exits. It is the same engine as the session, the
same worktree, gates, and review, without the screen: for scripts, for a quick
question from the shell, for piping something in.
heph exec 'fix the failing test in src/parse.rs'
git diff | heph exec 'review this change and list anything risky'
heph exec --plan 'summarize what this crate does' | less
The prompt is the arguments, joined with spaces. If stdin is not a terminal,
whatever is on it is appended to the prompt as context, so
git diff | heph exec 'review this' gives the model the diff.
What comes out
The model's final text goes to stdout. Everything else, tool calls as they
run, status lines, prompts, the review, goes to stderr, so the text can be
piped or captured on its own. --stream echoes the model's text to stderr as it
streams too, for watching a long answer arrive.
The last stderr line summarizes the turn:
-- turn 01a0c63e on main: EndTurn, 4 rounds, $0.2140; applied src/parse.rs
It ends with ; skills a, b when the turn activated skills, whether you armed
them or the model loaded them.
Skills
--skill NAME activates a skill with the prompt, as /skill does in the
session; repeat it for more, and they go in the order given:
heph exec --skill rust-change --plan 'how are errors typed here'
A name that is not discovered, or that [skills] disabled names, ends the run
before any turn starts, with exit 1. A skill already active on the conversation
--continue names is skipped with a note on stderr, since it is in effect.
--skill needs a prompt to go with. When --continue offers a pending review
and you leave it undecided, the prompt does not run, and the summary line ends
; not delivered: skills a, b.
Exit codes
| Code | Meaning |
|---|---|
| 0 | The turn ended with nothing left to decide: the model finished, and any change was applied. |
| 2 | The turn ended short: a call no flag covered, an interrupt, the model's output limit, or changes waiting for review. The reason is on stderr. |
| 1 | Heph itself failed: bad config, no provider, a database or git error. |
With a terminal
Run from a shell, heph exec has a tty even though it has no screen, and uses
it: permission prompts, ask_user questions, and the review appear as plain
prompts on the terminal, and you answer by typing.
exec in worktree of ref main (mirrors /home/you/project)
cargo test --lib auth
y this call only
a this session: exec(cargo) args(*)
= any exec of cargo, any arguments; only cargo is run,
and each argument is passed to it literally, never
through a shell
A remember: the same, written to project config [permissions] promoted
e edit the pattern before a or A
n deny
N deny with reason
>
The review works the same way: the brief, then Enter for hunk-by-hunk prompts
(a, r, c, e, s, q) or the configured external tool, which opens on
the terminal. Heph reads the tty directly, so this works even when stdout is
piped or stdin carries the prompt's context.
Without a terminal
In a cron job, a CI step, or a pipe with no tty, there is no one to ask, and heph fails closed:
-
A Level 2 call (
shell,web_fetch) that no flag or config pattern covers is denied. The model is told no one is present, and usually explains what it would have needed. The turn continues. -
ask_userreturns an error telling the model to decide or state its assumption. -
Changes the model made stay in the worktree, unreviewed, and the turn exits 2 with the conversation to continue on:
changes await review in the worktree of ref main; run `heph exec --continue main` in a terminal to review them, or pass --accept-edits -
SIGINTends the turn as interrupted.
Three flags widen what runs unasked. They approve tool calls, and nothing else: none of them stands in for review.
| Flag | Approves |
|---|---|
--allow-tools PATTERN | Calls matching the pattern, e.g. --allow-tools 'exec(cargo) args(*)'. Repeatable. |
--yes | Every Level 2 call. |
--yes-really | Level 2 and Level 3. |
--accept-edits | Not a tool flag: applies the turn's changes without review. |
--accept-edits is the one to think about. With it, whatever the model changed
lands in your tree when the gates pass. It is right for a change you will look
at in git diff afterward anyway, and wrong for anything you would want to
reject hunk by hunk.
Picking a review up later
A turn that exited 2 with changes waiting can be finished from a terminal:
heph exec --continue main
With --continue and no prompt, heph offers the pending review and exits when
it is decided. With a prompt as well, the review comes first and the new turn
after. The same review opens in the interactive session:
heph --continue main
See Picking Up Where You Left Off.
The base branch
A conversation starts from a branch you name for it (Branches).
The session asks; exec cannot, so a conversation with no base branch needs
[git] base_branch in the project's config or --base on the command line:
heph exec --base main 'add a test for the parser'
Without either, the run ends before any turn with a message naming both. Once a
conversation has a base branch, --base with another name is refused rather
than moving it; /base in a session does that.
The other flags
--continue NAME runs on a conversation other than main. --profile,
--tier, --model, --plan, --design, --tools, --review, and --quick
mean what they mean in the session (Models and Cost,
Plan Turns, Design Turns).
--implement PATH builds a committed design in a new conversation, or in the
--continue one with its context cleared (Design Turns).
--quick runs the turn on the quick tier; it does not yet cap the turn at one
round.
The global flags go before the subcommand: heph -C DIR exec ...,
heph --data-root DIR exec ....