Words typed or pasted while /new opens belong to the new chat. Backspace
edits those words. Enter during the opening leaves them unsent in the new
chat's composer; press Enter after it opens to send them.
Escape cancels the opening. If you typed words meanwhile, they return to the
chat you were leaving, unsent, with
opening cancelled; your words are back in this chat, unsent.
Typing when no chat is opening works as usual.
Closing the terminal or quitting during opening also keeps those words, including an unfinished paste. They return unsent on the next start.
/quit closes the current chat when another chat is open. The next chat
appears immediately while closing finishes in the background. The keyboard
waits for that close for at most one second; keys and pastes during that wait
are discarded, so they cannot send the next chat's draft. The window keeps
drawing and resizing throughout.
If closing fails, it says
this chat could not be saved; its unsaved turns stay on this computer for the next start.
A close still running after that second says
this chat is still finishing in the background.
A slow close alone does not mean saving failed. An unanswered final saving
reply also keeps the finishing notice.
If that background close later fails, the active chat shows the closed chat's
name followed by the same saving-failure sentence, once.
If writing on this computer fails and the transcript location is known, the restored terminal says
codeaf could not save this chat; unsaved turns are kept in <transcript path> and return on the next start if this computer still holds the chat.
The path names the transcript on the computer running the chat, including over
--host. When the location is unknown, it says
codeaf could not save this chat; unsaved turns stay on this computer and return on the next start if this computer still holds the chat.
The command exits with a failure status. One sentence stays visible after
the full-screen chat closes, including when error output goes to a log. On the
next start, retained turns are published if this computer still holds the chat.
If another computer has continued it, take the chat back to see its newest history;
local accounting alone does not create a separate chat.
A computer removed from your paired devices keeps saving this chat locally. Quitting stays quiet and does not try a final relay save; the removal notice already explains that the chat stays on this computer until you pair it again.
The screen closes after at most one second while a slow close finishes. The
launcher keeps its connection open for the final publish and release before it exits.
A successful quit with nothing waiting stays quiet. While a sync save waits, the restored terminal says saving this chat to your other devices…. After about three seconds, a confirmed local save adds still saving — ctrl+c leaves it saved here; without that confirmation, it says still saving on this computer — ctrl+c leaves the written turns here.
The launcher waits up to 30 seconds for the final saving reply. If only upload is outstanding, the successful receipt is saved here; it reaches your other devices when the sync service at 127.0.0.1:8901 answers, using your service's host. Ctrl+c during this restored-terminal wait exits promptly with the same receipt when local saving is confirmed. Without confirmation, it says saving on this computer has not finished; its written turns stay here and exits unsuccessfully. Only a failed local write says codeaf could not save this chat.
A chat held here, or one never shared, sends with one Enter using its local hold. Sending does not wait for the sync service to answer. An established outage does not add another network wait to every send.
A copy with no current hold gets one fresh check, bounded to five seconds.
It sends, offers Continue this chat here?, or explains why the words remain
unsent. If you change chats meanwhile, it says
chat changed before sending; your words are still unsent.
If you edit the draft meanwhile, it says
draft changed before sending; press enter to send your current words.
alt+e walks this conversation's thinking rung; /effort opens the ladder and
/effort <rung> sets it. The models-and-cost page explains the rungs.
enter sends the message you have typed.
shift+enter opens a new line without sending in home's message box, its ask-here
pane, and conversations, including task rooms and while an answer is running.
In conversations, alt+enter and ctrl+j also open a line. On home, alt+enter
keeps its task-composer action.
On home, even a blank line hides the placeholder and moves the cursor onto the new line. Left/right move through the draft; up/down move between its lines, then return to list navigation at the top or bottom. Deleting the entire draft brings the placeholder back.
Shift+enter requires a terminal that distinguishes it from plain enter. If your
terminal sends plain enter instead, it has the ordinary send behavior; use
alt+enter or ctrl+j in a conversation as a fallback.
ctrl+shift+enter while a turn is running stops the answer and sends what you
have typed — see "Interrupt and say something new in one key" below. At rest it
does nothing.
cmd+enter while a turn is running holds what you have typed for the answer
after this one. It is the secondary choice for when you do not want to change the
work already under way. At rest it does nothing at all. On Linux and Windows that key
is called super+enter — see "What cmd+enter is called on your keyboard" below.
ctrl+enter queues non-empty words only while this conversation's turn is
running, from its own composer, and only when the draft is not a /command
and has no live send-door tag. With no turn running, on the new-chat start page,
over a /command, or with a live /task tag, it is
plain enter: send, start the chat, or run the command at once. An empty box
also takes enter's ordinary action. It works wherever the terminal sends the
chord, including modifyOtherKeys terminals; the hint and tip advertise it only
after the terminal says it can distinguish keys. Terminals that cannot send it
send plain enter on many keyboards or ctrl+j (a newline) on some.
The queue carries words alone: mid-turn with a picture or a picked harness on
the tray it refuses in one line and changes nothing. Queued messages sit above
the box behind a dim reply arrow ↳. Only messages queued from this window can
be taken back before their turns start — see "The queued messages" below.
What enter does depends on what is in the box:
- Text in the box: it is sent.
- An empty box with pictures in the attachment tray: it is still sent. A message of pictures and no words is a message.
- An empty box with a tool row selected: that row opens instead of anything being sent.
- An empty box with nothing selected: nothing happens.
While a turn is running, plain enter steers: it stops the model's current
reply where it is, keeps what has arrived, and sends your words into the same turn.
See "Typing while an answer is still coming" below. ctrl+enter instead queues a
fresh turn to run after the current one; commands and an empty box take plain
enter's action. Until its turn
starts, the queued message sits above the box behind a reply arrow ↳, dim, one row
each, and nothing is written under them. If queueing fails, codeaf notes
follow-up failed: <error>.
Typing is never blocked. The box works normally while an answer streams.
enter while a turn is running steers. Your words appear at once as a line of your
own in the transcript. A short dim clause beneath says where they landed, such as
stopped the reply here, kept running as job 3, took this instead of the question,
or waiting for the running step.
If text or reasoning is streaming, codeaf cancels that one model request, keeps the partial answer it actually received, and continues the same turn with your words as the next user message. An incomplete tool call is dropped because a provider cannot accept a tool call with no result.
If a short tool is running, codeaf lets it finish and lands your words at that
boundary. If a bash command has already been running for 3 seconds, codeaf keeps it
alive as a job and lands your steer immediately. The clause names the job and
jobs output N shows its output. A command that is still YOUNGER than 3 seconds when
you steer is given those few seconds to finish on its own; if it is still running when
they are up, it is kept alive as a job then. So a bash command holds your correction
for about three seconds at most — never for its own ending and never for the
background-after clock. That is the bound on the handoff, not on the answer: the step
may hold other tools as well, and the model's reply then takes as long as the model
takes.
The exact words stop, kill it, cancel, abort, ctrl-c and their tiny variants
stop the command instead, at any age — a stop is not asked to wait out those three
seconds, because they exist to let a short command finish and that is the one thing you
have just said you do not want. If you then type something else, the model reads both
lines in the order you sent them; the stopped command is not restarted by the second
one.
| What you do | What happens |
|---|---|
enter |
stops the current generation and sends the words into this turn |
ctrl+enter |
queues the message to run after this turn — see "The queued messages" below |
cmd+enter |
holds the message for an ordinary turn after this answer |
esc |
stops the answer and clears both waiting-message queues |
→ over an empty box |
steers the oldest waiting words into the running answer |
click → steers it in |
the same, with the pointer |
ctrl+shift+enter instead of enter |
stops the answer and sends the sentence in one key — see below |
↑ over an empty box |
takes the newest parked message back into the box to edit; with none parked, walk your history. A queued message is not reached this way — click its row |
| click the block | takes that message back into the box to edit |
cmd+enter again |
holds the edited sentence again |
Attachments in the tray go with the held message, and come back on the tray if you take
it back. /-commands are not held: a slash command is something you said to this
surface rather than to the model, and it runs at once.
Limits. esc with nothing waiting is exactly the plain interrupt it always was.
With messages waiting, it clears both the editable parked queue and the session's
follow-up queue. Each nonempty queue says what was dropped — 1 waiting message dropped
or N waiting messages dropped for parked messages, and the corresponding queued
word for follow-ups. Replacing the conversation with /new or from the welcome box also
drops parked messages. Inside a task room enter steers the node instead and nothing
is held; that is the room's own key (see the room section below). Your line lands on the
task's page as a └ elbow where you said it, with a short · delivered clause that
fades away.
Why it goes straight in. Plain enter is the gesture people expect to act now.
The current generation is itself made into a legal boundary: codeaf keeps its partial
assistant message without incomplete tool calls, writes your user message after it,
and asks the model again. A steer that races with a turn already sealing still lifts
to the follow-up queue, so the words are never dropped.
ctrl+enter queues non-empty words from this conversation's own box while its
turn is running and has no live send-door tag. With no turn running, on the
new-chat start page, over a /command, with a live /task tag, or with an
empty box, it takes plain enter's action. It works whenever
the terminal sends it; hints and tips require the terminal's key-support reply.
Terminals that cannot send it deliver plain enter or, on some, a newline.
Each queued message is drawn above the box behind a dim reply arrow ↳, wrapping
within the frame. Nothing is written under the block. Its words reach the
transcript as an ordinary sent line only when its turn begins. Slash words made
plain with Backspace stay plain there.
Taking one back before it runs. Only messages you queued from this window
light under the pointer and take a click. Rows admitted from another window or
a steer that fell through are drawn the same but cannot be taken back here.
A click asks the session to remove that message; another click while the answer
is pending asks nothing. ↑ belongs to parked messages and history.
A successful take-back returns the words, pasted documents and plain slash words to this conversation's composer, even if a task room has the keyboard. An empty box receives the original draft. In a non-empty box the returned message is appended on a new line; the existing draft and tray stay, and paste chips are renumbered to avoid collisions. Multiple take-backs keep click order. If you replaced the conversation before the answer, its words stay reachable in ↑ history.
The queue carries words alone. With a picture or a picked harness on the tray,
it refuses: ctrl+enter queues words alone — take the pictures or the shape of work off first.
The draft and tray stay; the queue hint is absent. Send pictures with enter.
Limits. A message whose turn already started cannot be restored: a false
answer leaves it to run. esc drops the queue and says N queued messages dropped;
a drain never restarts work you stopped.
enter while a turn is running sends what you have typed into that turn. The
current provider request is stopped, what it already streamed remains in the
transcript, and no second turn starts.
It is the same question, not a new one. Your sentence is added to the transcript of the turn that is running, as your own words, and the model reads it at its next step — after everything it has already done about your original question. That is what makes it a correction rather than a restart: "no, the other file" arrives while the work is still going, and the work carries on from there.
The correction appears on the page where you sent it, between the work already shown and
the next tool row. It is flush left in your own column, with a dim └ marking that it
continues the same question. It stays in that position after the turn finishes and when
the conversation is reopened from disk.
While it waits, the row says where it landed. Next to your words, in dim, one of:
stopped the reply here when the reply in flight was cut for it, stopped the running command when your words plainly told a long command to stop, kept bash running as job 3
(or …as jobs 3, 4) when a long command was moved to the background so your correction
could land, took this instead of the question when a question was open and your
sentence answered it instead, and waiting for the running step when a short tool is
being allowed to finish first. The clause goes when the model is actually given the words; the position of
the line is what says where they went from then on.
This is the key for the moment you are watching an answer go the wrong way and you do not want to pay for stopping it. The three keys, side by side:
| Key | What happens to the answer | What happens to your sentence |
|---|---|---|
enter |
current generation stops; partial kept | goes into the same turn now |
cmd+enter |
keeps going | waits above the box until the answer finishes |
ctrl+shift+enter |
stopped, and what it said is kept | opens the next turn |
Over an empty box enter does nothing, unless a waiting message offers the →
shortcut. At rest, enter sends an ordinary new turn.
The → shortcut for a message that is already waiting. If you pressed cmd+enter and
your message is sitting above the box, → over an empty box sends that message in
instead of leaving it to wait. It is the front of the queue that goes, and the dim line
under the block says so: → steers it in. You can click those words instead. With
anything typed in the box, → is the caret key it always is — the shortcut only exists
where → had nothing else to mean.
A message with pictures is left waiting. Only words steer; pictures take their
own durable attachment path. When one is at the front of the queue, the line does
not offer → steers it in.
The line that teaches it. While a turn is running and you have words in the box, the right end of the row under the message box reads exactly:
enter steers it in · ctrl+enter queue · esc interrupt
That is the form when no command can be kept. A running foreground command that can be
kept adds ctrl+g backgrounds immediately before esc interrupt; a ! command cannot
be kept. The ctrl+enter queue clause appears only on a terminal that can send that
chord and has replied that it can distinguish keys. cmd+enter still waits and
ctrl+shift+enter still stops and sends, but the
one-line slot advertises neither — they are on the keys sheet (?).
A sentence sent into a running turn is accepted only while the turn can still make a boundary. A steer normally creates that boundary by stopping the current generation. If the turn was already sealing, there is no request left to stop.
Nothing is dropped and nothing pretends. When that happens your sentence is lifted out and simply becomes the next message: an ordinary one, waiting for a turn of its own. The screen says that is what happened, and the reply arrives in the turn that follows. You do not have to type it again, and you do not have to check.
The fall-through message becomes an ordinary next turn. The surface removes its temporary steer clause and draws the normal user line when that next turn begins.
cmd+enter reaches a program only where your terminal can tell it apart from a plain
enter — the kitty keyboard protocol, xterm's modifyOtherKeys, or win32-input. Ghostty,
kitty, WezTerm and recent iTerm2 profiles all do; a plain Terminal.app does not.
Where it cannot be spelled, the key arrives as ordinary enter and therefore
steers. Queueing needs the same terminal support (ctrl+enter has it on exactly
the same terminals) — on a plain terminal the follow-up queue is not available at all.
On those terminals codeaf never advertises the chord. The line under the box still
begins enter steers it in, because plain enter works everywhere, and ends with the
stop clause that works in the current state.
What works everywhere instead. enter steers, esc stops, and a message that
missed its moment can simply be typed again; the queued block, where a follow-up
waits, is clickable on every terminal.
It is one keystroke with two names, and codeaf spells it for the keyboard you are on.
| Where you are | What /help and this page's key sheet draw |
What the terminal sends |
|---|---|---|
| macOS | ⌘enter |
super+enter or meta+enter |
| Linux, Windows, WSL | super+enter |
super+enter or meta+enter |
super is the key with the diamond, the Windows logo, or the command symbol on it,
depending on whose keyboard it is. codeaf binds both names the terminals send, because
which one arrives is a fact about the road the bytes took rather than about your hand.
This page writes it cmd+enter throughout, which is the name it was chosen under; the key
sheet substitutes the spelling for your platform at the moment it draws, exactly as it
does for alt+/opt.
ctrl+r appears twice on the /help sheet, and the two are not the same key doing two
jobs at random — each is scoped, and each row says its scope:
ctrl+rover a draft spells the draft out: codeaf says back what it takes the sentence to mean, andenteradds that to what you are saying. It is offered only while the hint slot saysctrl+r spell it out, and it does nothing at all otherwise.ctrl+rin/filesreveals the folder a landed file is in, withctrl+ybeside it to copy the file somewhere.ctrl+rin/modelfetches the newest model list; the picker's placeholder saysctrl+r refresh. It is not on the/helpsheet — the placeholder is where it is named.
/files is a full-screen list with its own keyboard, and the model picker takes every key
while it is open, so none of them contend for one keystroke on one screen.
? over an empty box opens /help: every command and every chord, written into the
transcript where you can scroll it. A row that is wider than the window wraps under the
column its sentence already starts in, so the next line does not sit at the left edge as
if it were another key.
On a place — home, activity, memory, spend, settings, automations — ? draws the
map instead, which is what alt+. draws: that place's own keys, in the cells the foot
was already using. One meaning, two screens: show me the keys for where I am standing.
It never eats a question mark you are typing. The binding wants the box EMPTY. With
anything at all in the draft the key is an ordinary ? and goes into your sentence; and
every overlay, filter box, picker, panel and question on this surface takes the keyboard
before this binding is read, so a ? typed into one of those reaches it and nothing else.
/? is an alias of /help as well, for fingers that arrived from elsewhere.
/help is a column. The key sits on the left and the sentence starts in a fixed
column. When the window is too narrow for the sentence, the next line starts in
that same column. It does not start at the left edge, which would look like a
second key. The same is true of a line that was already indented under the
column above it.
Stop it and tell it something different at the same time — interrupt and say something new in one key
ctrl+shift+enter while a turn is running stops the answer and sends what is in the box,
as one gesture. Unlike a steer, it ends the whole turn and starts your sentence as a
new one after the stop finishes.
What happens, in order:
- Your sentence goes onto the waiting queue exactly as
cmd+enterwould put it there. - The turn is interrupted: everything it already said is kept, and the note
interruptedis added, exactly asescdoes it. - When that turn has actually finished stopping, your message opens the next turn.
Nothing is sent into the turn you stopped. The transcript reads in the order it
happened: the partial answer, the interrupted note, then your message.
The box is cleared the moment you press it, and the draft file it came from is done with — from your side you have said the thing. Attachments in the tray go with it.
What it does not do. A /-command is run at once and the turn is left running —
a slash command is something you said to codeaf rather than to the model, so there is
nothing to interrupt for. The same is true of a live /task tag, of a picked
harness, and of a refusal. The rule is simple: the turn is stopped only if the key
actually queued a message.
With an empty box it does nothing at all — not even a plain interrupt. Use esc for
that. With nothing running it also does nothing: enter already sends.
It is not the queue key. ctrl+enter is the chord that queues a message for after
the running turn; this one means "instead of that". One key does not do both.
Where it does not exist. Inside a task room there is nothing for it to mean —
enter in a room steers the node there and then, with no queue to jump, and a room's way
of ending work is x and a card that asks first. The chord is ignored there.
Terminals that cannot send it. ctrl+shift+enter reaches a program only where the terminal
can tell it apart from a plain enter — the kitty keyboard protocol, xterm's
modifyOtherKeys, or win32-input. Where it cannot, the key arrives as an ordinary enter
and your message steers instead. Use esc to stop the whole turn, then send the next
message normally.
The line that teaches it. While a turn is running and you have typed something, the right end of the row under the message box reads exactly:
enter steers it in · esc interrupt
On a terminal that can send ctrl+enter it reads
enter steers it in · ctrl+enter queue · esc interrupt. The row does not name
ctrl+shift+enter, even where the chord works (it did until 2026-09-30, in the slot the
queue key now has); the keys sheet (?) lists it. A foreground command that can be kept
inserts ctrl+g backgrounds before the final stop clause.
A picture on the tray is a message even when the box has no words. It cannot steer, so
that form reads enter waits · esc interrupt. With neither words nor a picture, the line is simply
esc interrupt, unless a command can be kept, when it is
ctrl+g backgrounds · esc interrupt.
No. Every road ends in an answer.
If you pressed enter, codeaf stops the current model request, keeps its partial
reply, draws your words immediately, and continues the same turn from them.
If you pressed cmd+enter, the message is held above the box for the next turn.
It is still yours: edit it with ↑, click it, or let it go when the answer finishes.
Pressing → over that waiting message steers it in instead.
If there was no step left — the turn's last request had already gone out, or it finished a moment later — your words are not dropped and not pretended about. They become an ordinary message waiting for a turn of its own, the screen says so, and codeaf answers them in the next turn. You see your line, a pause, and then a reply. You do not have to type it again.
The same is true of a task or a background job that finishes in that window: its note lands and codeaf speaks about it rather than leaving it sitting there.
The one thing that is not answered is a message you queued with ctrl+enter for a
turn you then interrupted. A drain never restarts a turn you stopped, so those
are dropped — press enter again to send it.
A message waiting above the box stays with its conversation when you open Home with
space space, /home, or alt+1, and still sends when that answer ends. Its pictures
and pasted documents stay with it. A connection that holds one conversation
at a time returns the waiting words and pictures to the box and tray when switching ends
the old conversation. esc stops the answer and drops waiting messages.
From Home, alt+3 (chats on the bar) goes straight back to the conversation you
were in, and alt+k chooses one.
Press esc or ctrl+c. While a turn is running, both do the same thing: the turn
is stopped and everything it already said is kept.
What happens:
- The session is told to stop, and a note
stoppedis added to the conversation. - The screen stops on the key: every spinner goes, and every call that was running keeps the time it ran until you stopped it.
- Any queued follow-ups are dropped, and codeaf says so —
1 queued message dropped, orN queued messages dropped. - The status word becomes
stopping, theninterrupted, andinterruptedstays as the status word until the next turn starts. - If a message of yours was waiting for that answer, it is not dropped: it sends
immediately as the next turn. That is the whole difference
escmakes while something is waiting.
The words codeaf uses for one stop. They are five slots and one key press, so they
are worth reading together: stopping is the status word while the turn is being let go,
stopping · detaching in 7s is that same word once the 10-second bound is counting down,
interrupted is the status word once it is over, · stopped is the note left in the
conversation, and ▸ stopped by you at 40s is the chip a stopped turn collapses to. A
turn that never let go leaves a sixth: detached — the turn was let go of and nothing is waiting for it. If
you are looking for the word interrupted anywhere else on the screen, that is where it
is — the status line, and only after the turn has truly ended.
What the screen says. While a turn runs, the right end of the row under the
message box ends with esc interrupt — for example
enter steers it in · ctrl+enter queue · esc interrupt while you have typed something
on a terminal that can send ctrl+enter. A foreground command that can be kept inserts ctrl+g backgrounds immediately before the stop clause. When a
message of yours is already waiting for the answer to finish, the last clause becomes
esc stops and drops. On the very first frame of a session the conversation carries the note
esc interrupts · ctrl+c quits · ? for help.
Stopping it and saying something new at once. ctrl+shift+enter does both in one key —
see "Interrupt and say something new in one key" above. esc on its own stops without
sending anything you have not already committed with enter.
Limits. Interrupting does nothing at all when no turn is running. esc reaches
the interrupt last: a history recall is cancelled first, rewind is armed on the way
past, and any open list or overlay takes the key before the message box sees it. So
esc while the command list or the @ list is open closes that list and does
not interrupt.
Mid-turn ctrl+c only ever interrupts — that press never leaves. It is spent on
the model. The NEXT press is read at rest, and at rest ctrl+c is the way out — so the
two-tap people make mid-turn, press it again harder because the first did not seem to
land, stops the answer and then quits. Nothing you typed is lost when it does: the box
and anything waiting for an answer are written to disk on the way out. See "Quitting
codeaf — how do I exit, close it, or why did ctrl+c not quit" below.
When you stop an answer with esc or ctrl+c, its unfinished words fold behind
▸ stopped by you. Reopening the conversation keeps that chip. Press ctrl+e
to open it and read the words that arrived before you stopped. They stay
unfinished work, not a completed answer. After reopening, the chip may leave out
how long the answer ran, because that time is not saved.
Esc is not stopping it — how long does a stop take, why the turn is still finishing, how long stopping takes, and what happens if it will not stop or will not let go
I pressed escape and it is still running. That is this section: escape is not being ignored, the turn is being let go of, and if it will not let go codeaf ends it for you after ten seconds.
esc cancels the turn on the keystroke, but the turn does not close on the keystroke. A
bash call whose command left something holding its output waits up to three seconds
before the pipes are forced shut, and a jobs kill spends two seconds on a polite signal
and two more on the one that is not polite. For those seconds the status line reads
stopping rather than interrupted, and that is the honest word: the work is being let
go rather than gone.
That window is bounded at 10 seconds and the screen says so. The status line reads
stopping · detaching in 7s, counting down from the moment you pressed the key. If the
engine lets go inside the window — which is what almost always happens, because the longest
ordinary wait is about four seconds — the countdown simply disappears and the status word
becomes interrupted.
Nothing moves in that window and nothing new is drawn. The spinners are already gone from the status line and from every tool row. Any consent question, account offer or harness offer that was open is taken down on the key, because each was about work that is now over. Whatever the model says while the turn winds down is not shown — a sentence it was still speaking stops where it was, and a call it was half-way through asking for never becomes a row. Two things do still land, because neither can draw anything new: a call that was already on screen reports its own result if it returns in that moment, and what the turn spent is still counted.
No key makes it stop harder, because the second stage is a clock and not a key. A
second esc inside half a second is the rewind's door and ctrl+c at rest is the way
out, so neither is free — and you do not need one. The esc you already pressed started the
10-second window, and when it runs out codeaf stops waiting on its own.
What happens at 10 seconds. codeaf detaches from the turn: the waits codeaf holds are
ended and whatever request was still open to the model is aborted. A wait that ignores
being cancelled — a command whose output a grandchild is still holding, say — may run on
behind the detached turn; what detaching guarantees is that NOTHING IS WAITING FOR IT any
more, not that it is already gone. The conversation gets the note detached — the turn was let go of and nothing is waiting for it, and the box is
yours again — you can type the next thing straight away. If the turn had spent anything,
the note names it, for example detached — the turn was let go of and nothing is waiting for it — it spent $0.04; under half a cent it says it spent under a cent rather than a
figure, and a turn that spent nothing says nothing about money at all.
Nothing is silently orphaned. A turn that had to be detached is written into the
conversation's own journal file as abandoned, with the tokens and the money it had spent
by then, so a turn nobody waited for is never a turn nobody can account for. What it cost
is already on your spending page either way: codeaf counts money as each request is
answered rather than when a turn ends.
You should almost never see this. Ten seconds is well above the longest ordinary
letting-go, so the deadline only fires on a turn that was genuinely not going to end. If
something is wedged even further down, ctrl+c at rest still quits and takes the whole
process with it — but you no longer have to reach for that just to get your prompt back.
To exit or close codeaf, press ctrl+c once. That is the whole gesture: no second press,
no window to beat, nothing to confirm — with one exception, a running automation, below.
ctrl+c, once. With nothing running, the press that lands is the way out: codeaf
writes your draft to disk and exits.
If ctrl+c did not quit, a turn was running. Mid-turn that key is the interrupt —
the same thing esc does — and the press is spent on the model. Press it again once the
answer has stopped and codeaf leaves.
An automation running does not hold it up. Closing codeaf pauses automations, and a run it cut off runs again when a window is next open (Quitting while an automation is running).
Nothing you typed is lost when you exit or close it. The unsent sentence in the box goes to disk, with any message that was still waiting for an answer folded in underneath it, and the next launch puts them back in the box. See "What quitting saves and closes" below.
Conversations and tasks are NOT named first (a first press used to name them; that
warning is gone). A conversation this machine's codeaf service is running keeps
working after the window closes — its tasks, its questions and its journal are there
when you open the same workspace again — and one running inside this terminal
(--no-host, or a host that could not be reached) stops with it. The task column
(alt+l) and /status say what is running before you go.
Closing one tab still asks. ctrl+w on a conversation with work running raises a card
that names that work — a task and a job running — and waits for an answer.
The quit gesture is one ctrl+c, but where the press lands changes what it does.
Mid-turn it is only the interrupt. While an answer is streaming, ctrl+c is the same
key esc is: it stops the turn, and that press does not leave. The next one, at rest,
does — so the two-tap people make mid-turn stops the model once and then quits.
It works over everything. ctrl+c is read above every picker, panel, room, mode
and paste bracket — leaving is never modal. Pressing it with the model picker or the
settings panel up does not close them: it leaves codeaf, with the panel still up. esc
is the key that closes the thing in front of you.
What quitting does. Your unsent draft is written to disk first, with any message still waiting for an answer folded in underneath it. Then this terminal comes off every conversation it holds — the ones behind the screen as well, which already wrote their own boxes to disk when you switched away from them. Nothing is lost that was typed.
Leaving a window is not ending the work. A hosted conversation is detached: the
window goes and the conversation keeps its turn, its tasks, its questions and its journal,
and opening the same workspace again rejoins it. A conversation running inside this
terminal has nowhere else to run, so it is interrupted and closed here. /close and a stop
you type are still the person ending something on purpose, wherever the conversation is
running.
Limits.
/quitcloses one conversation, not the program. It is typed out on purpose, so it is not asked twice — but what it closes is the conversation in front, and codeaf stays up with the previous one forward when this terminal is holding another. It leaves only when that was the last one, an automation running or not.ctrl+ccloses everything./exitand/qare the same.- A real signal —
kill -INT,kill -TERM,kill -HUP, a closed terminal window, or^Con a terminal that is not in raw mode — also leaves through the same clean exit: draft written, session closed, status 0. A second signal ends the process at once with status128 + the signal's number. The screen is handed back on a bounded best effort first; if that could not finish, the terminal may needresetafterwards. - Nothing else asks you to confirm leaving, and a signal never asks. A
ctrl+cstruck by accident at rest ends the session; what you had typed comes back at the next launch.
Quitting while a task is running — what happens to tasks and background work when the session closes
A task running on another paired computer keeps running there when you close this chat. Reopening follows its saved attempt and brings its result home once; it never starts that attempt again automatically. See Tasks on another computer.
A hosted conversation keeps working. Closing the window, ctrl+c, kill -HUP
and a terminal that went away all detach: the task goes on running in this machine's codeaf
service, its questions stay waiting for you, and you rejoin it by opening the same
workspace again.
A conversation running inside this terminal stops with it. That is --no-host, and a
launch where no host could be reached. Every task the session is running is stopped when it
closes and codeaf waits for all of them before it leaves; a task that was only just admitted
is stopped before it opens anything — no worktree, no log, no model call for a session that
has left. Nothing can start in it afterwards: this session has closed; nothing new starts in it. Background jobs go the same way, a moment later.
Either way, what a task wrote is on its branch and stays there; a stop is an interruption
and never a finding about the work. A headless codeaf chat --once leaves by the closing
road: the turn stops, the session closes, and the process exits cleanly.
Keys — what all the keys do, the keyboard keys, keys on the keyboard, key bindings and keyboard shortcuts
This page is about the keys you press. Every key, chord and keyboard shortcut codeaf listens for is on this page, in the tables below.
If you came here looking for a different kind of key, it is somewhere else:
- an API key for a model — see Models and cost and Connected accounts
- a device key or a pairing code for reaching another machine — see Reaching a machine with a pairing code
- an ssh key — that is your own ssh setup, and codeaf runs your
sshunchanged; see Running on another machine - the architecture map —
/atlas, orcodeaf atlasfrom the shell. It takes the whole frame while it is up;?inside shows its keys, andescorqcloses the map or its map picker and gives the frame back
These apply with no overlay up, no room open, and no mode on.
| Chord | What it does |
|---|---|
enter |
Send the message. Empty box with attachments still sends; empty box with a tool row selected opens that row |
ctrl+enter |
Queue non-empty words from this conversation's composer while its turn runs. Commands, idle sends, the start page and an empty box take plain enter's action. Any decoded chord works; hints need the terminal's reply |
shift+enter |
Open a new line without sending, on home and in conversations |
alt+enter |
Open a new line in a conversation |
ctrl+j |
Same as alt+enter |
esc |
In order: cancel a history recall, then arm rewind, then interrupt the running turn — and send any message that was waiting for it |
esc esc |
Two presses inside a short window open the quick inline rewind mode. /rewind opens the full timeline instead |
ctrl+c |
Turn running: interrupt, and nothing else. Nothing running: quit codeaf, on that press — unless an automation's run is in hand and this is the last codeaf window, when the first press says so and a second within ten seconds quits |
ctrl+g |
A foreground command that can be kept: send that command to the background. Otherwise: close the task column, or bring it back. On a frame under 100 columns with no roster raised and no command to keep, it does nothing |
enter while a turn runs |
Stop the current generation, keep its partial reply, and steer the words into the same turn |
cmd+enter while a turn runs |
Hold the message above the box until the answer finishes. Empty box: nothing. Nothing running: nothing |
ctrl+shift+enter while a turn runs |
Stop the answer and send what you have typed, as one gesture. Empty box: nothing. Nothing running: nothing |
→ over an empty box, a message waiting |
Send that waiting message into the running answer. With text in the box it is the caret key |
↑ over an empty box |
takes the newest parked message back into the box to edit; with none parked, walk your history. A queued message is not reached this way — click its row |
The enter family, shortest first: enter sends or steers, cmd+enter holds it for the
next answer, ctrl+enter queues it for after the current turn, ctrl+shift+enter stops
the whole turn and sends, and shift+enter, alt+enter or ctrl+j opens a line.
Neither ctrl+shift+enter nor cmd+enter opens a line — use shift+enter for that.
Both need a terminal that can tell them apart from plain enter; where it cannot, the
key arrives as ordinary enter and the message steers instead. A decoded ctrl+enter
is accepted wherever the terminal sends it, including modifyOtherKeys without a kitty
reply. Its hint and tip still require the terminal's key-support reply. A terminal that
cannot send it delivers plain enter on many keyboards, or a newline on some.
| Chord | What it does |
|---|---|
ctrl+o |
Selected landed card: open its output. Selected proposal: open its brief. Inside a task's page: open or fold the long instruction at the top. Otherwise: open or fold the live caption's tool rows; before a live caption exists, open or fold the N earlier tool calls fallback. It never opens a ▸ worked chip — that is ctrl+e |
ctrl+b |
Enter copy mode — freeze the view so you can read and copy. Not on home, where it moves the caret |
ctrl+s |
Hand the pointer to your terminal so you can drag-select. Toggles; any other key takes it back |
ctrl+, |
Open the settings panel |
alt+e |
Walk this conversation's thinking rung one step: auto → low → medium → high → xhigh → max, and back to auto. Works with a sentence half typed. On home it walks the rung of the next conversation instead — the effort word after the model’s colon on home’s seam; on the other places it does nothing |
alt+a |
Walk what this conversation runs without asking one stop: asks → guardian → YOLO → asks. Never lands on refuses. Works with a sentence half typed; over --host it says the far machine's rules decide. On home it walks the gate of the next conversation — the ◇ cell on the rule above home's box — and that pin is spent by the conversation that uses it; on the other places it does nothing |
ctrl+. |
Open the activity page (/history) — every task this machine has run, across every project and every session; type to filter it. It opens on a machine that has run nothing too, and the page says what tasks are |
space space |
On an empty box: open home (/home) — every project and conversation on the machine the session runs on, and an empty home on a fresh one. Does nothing when the box has words in it |
ctrl+l |
Jump back to the live edge of the conversation |
alt+v (opt+v) |
Open the conversations view (/wall) on Chats: all saved, non-deleted conversations, with live or saved previews. Select cards to create a team. Press again or esc to close it. Its own keys are on the Conversations and teams page |
ctrl+t |
Start a new chat — the same start page the + at the end of the tab strip opens. Nothing is created until you send the first message, esc comes back, and the conversation you were in keeps its draft, its attachments and its work. On a home row it starts the fresh chat in that row's own folder, while clicking a projects row selects the folder for the next message |
ctrl+w |
Close this tab — the same thing the ✕ on it does. Selects the last-used remaining tab, or Home if none remain. Drafts are kept, and the conversation keeps running; a tab with work in it asks keep running / stop work / cancel first |
alt+t (opt+t) |
Give the keyboard to the task roster. Press again or esc to take it back |
alt+l (opt+l) |
Close the column on the right, or bring it back, in every chat: the same column holds the Tasks and, in a chat in a team, the Traffic. The key is named at the right of the column's header. Under 100 columns it lays the column over the body, and a second press takes it off. Remembered for the next session |
alt+m |
In a team that has a manager: go to the manager. Over --host against an older codeaf on the far machine it says managers are not available there |
ctrl+g |
A foreground command that can be kept takes the key first. Otherwise it does what alt+l does: close the column, or bring it back; the column stands even with no tasks in it. Remembered for the next session |
ctrl+e |
Empty box: open or close the running conversation’s compact steps first; otherwise the newest ▸ worked chip onto its outline of captions — the latest completed turn's out here, the newest completed work inside a task's page — or the most recent thinking block when there is no chip. A caption is a short status line per step; its tool rows are one expand further. Otherwise: go to end of line |
pgup / pgdown |
Scroll one page — the height of the view minus one, never less than one row |
tab |
Take the highlighted slash command on Home or in a conversation; otherwise open or commit path completion over a command's path argument — and over an empty box with no completion showing, go back to the last conversation. Does nothing when this terminal holds only one |
| Chord | What it does |
|---|---|
up |
While browsing history, recall the previous whole entry. Otherwise move the caret up inside a multi-line message; at the first line, walk back through history; over an empty box with no history, select the previous tool row; otherwise scroll up one row |
down |
The mirror of up |
left |
Empty box: step back a level — close the room, else clear the selection. Two left presses inside about 600ms go home to the live edge. Non-empty box: move the caret left |
right |
Empty box: go into the next running task's room. Non-empty box: move the caret right |
ctrl+f |
Move the caret right, always. Never navigation |
alt+left / alt+b / ctrl+left |
Jump a word left. option+← arrives as one of the first two on a Mac. Does nothing over an empty box — the plain arrows keep their navigation meaning |
alt+right / alt+f / ctrl+right |
Jump a word right, under the same three names |
super+left / meta+left / ctrl+a |
Start of the line. cmd+← arrives as one of these on a Mac. Both super and meta are bound because a modified arrow and a modified letter arrive under different ones |
super+right / meta+right |
End of the line — one of the two spellings cmd+→ can arrive as |
home / ctrl+a |
Start of the current line |
end |
End of the current line, always |
ctrl+e |
End of the line — unless the box is empty, where it opens or closes the running conversation’s compact steps, or the newest ▸ worked chip onto its caption outline (the latest completed turn's out here, the newest completed work inside a task's page), falling through to the most recent thinking block when there is no chip |
shift+← / shift+→ |
Select a character at a time, the way shift does in any text field. Does nothing over an empty box |
shift+↑ / shift+↓ |
Select a line at a time |
shift+home / shift+end |
Select to the start or end of the line |
alt+shift+← / alt+shift+→ |
Select a word at a time. ctrl+shift+← / ctrl+shift+→ are the same |
cmd+a |
Select the whole message. ctrl+a cannot be this — it is the start of the line, here and in every other box |
ctrl+z |
Undo — take back what you just typed, a word at a time |
ctrl+shift+z |
Redo, on a terminal that can tell it from ctrl+z |
| any printing key | Types the character — over a selection it replaces it |
home, end, up and down work on the logical line — the run between newlines —
not on the row your terminal wrapped it onto. up enters history when the
caret is on the first logical line. Once browsing, up and down move whole
entries, including multiline prompts, in both the conversation and task rooms.
down past the newest entry restores your draft and caret; esc restores them
immediately. The conversation hint says ↑ older · ↓ newer · esc restore draft · enter send.
Narrow terminals shorten this to ↑↓ history · esc draft or ↑↓ history.
A word jump crosses the same boundary the word kill deletes: spaces first, then
the run of non-spaces, so alt+left then alt+backspace always deletes exactly
the word it just crossed.
How do I select text in the message box — highlight what I typed, drag to select in the input bar, the input box acts like normal text
Just drag inside the box. Put the pointer on a character of what you have typed,
sweep to another with the left button down, and the run between them highlights — the
same highlight a sweep over the conversation wears. On release that text is on your
clipboard, and the status line says what landed: copied · 10 chars, copied · 1 word, copied · 2 lines.
The highlight stays after you let go, because it is a live selection and not a
flash: type and the typed character replaces it, backspace or delete removes it
whole, a paste drops in over it. Any arrow key, any word jump, any line jump puts the
selection down again.
Double-click takes the word, triple-click takes the whole line. A word is what a
person means by one — a path, a hash, a flag, go.mod, a URL are each one word — the
same boundary a double-click uses in the conversation above.
It works in the boxes you type a message into: the message box in a conversation and the box at the foot of home. Sweeping out of the box carries the selection to the end of what is in it, rather than stopping at the edge.
With the keyboard, shift with any motion key selects: shift+←/shift+→ by a
character, shift+↑/shift+↓ by a line, alt+shift+←/alt+shift+→ by a word,
shift+home/shift+end to a line's ends, and cmd+a takes the whole message. Those
shift chords are the message box's; on the places shift+←→↑↓ belong to the place —
the time window on activity and spend, nothing elsewhere — so there the pointer is how
you select.
ctrl+z undoes, ctrl+shift+z redoes. They work in the message box and in every
other box on the surface — home's box, the errand pane, and every filter and search box
on every place and panel.
A step is a word, not a keystroke. Typing runs together into one step and the step
breaks where a word does, so one ctrl+z takes back the last word rather than the last
letter. A run of backspaces is one step too, a paste is a step of its own, and switching
from typing to deleting starts a new one. That is what makes ctrl+u, ctrl+w and
alt+backspace recoverable: each of those kills is one step, and one ctrl+z brings it
back.
Sixty-four steps back, and no further. A very large draft keeps fewer, because the history is bounded by how much text it holds as well as by how many steps.
Sending clears it. Once a message has gone, ctrl+z will not pull it back into the
box — ↑ walks the history of what you sent, and that is where a sent message lives.
ctrl+shift+z needs a terminal that can report it. Shift is invisible on a control
byte: a terminal that has not taken the keyboard-disambiguation protocol sends the same
thing for ctrl+z and ctrl+shift+z, so on that terminal the redo arrives as a second
undo. Ghostty, kitty and WezTerm report it; older terminals do not. There is no second
redo chord — ctrl+y already copies a path on home and in /files, and taking a working
key away would be the worse trade.
ctrl+z does not suspend codeaf. In an ordinary shell that chord stops the program
and hands you back the prompt; codeaf runs the terminal in raw mode, so the key arrives
as an ordinary keystroke and is the undo instead. To leave, press ctrl+c.
A click anywhere on the message box puts the caret under the pointer: on the letter you aimed at, at the row's end when you click past the end of a line, and at the start of the text when you click on the prompt's side of it. It works on a wrapped, multi-line draft — the row you click is the row the caret lands on.
It is the ordinary text-field gesture, and home's box at the foot of the screen answers
it too — the one place with a box (the Places page, Typing on a place). Holding the button
down and sweeping selects instead, and releasing copies what is lit (see "how do I
select text in the message box" above). While a picker's
filter box is standing in the box's place — the model picker, /resume,
/files — or while the composer layer is up, a click does not
move that box's caret; those are typed at and filtered, not edited by pointer.
On a Mac, option+← / option+→ are the word jumps and cmd+← / cmd+→ are
the line's ends, in every box codeaf has. They work in the message box, in
home's box at the foot of the screen, in the errand pane, and in every filter and
search box on every place and panel.
They work because of what the terminal sends, and a Mac terminal sends them one of two ways — codeaf answers both:
- iTerm2's Natural Text Editing key mappings (the preset most people have)
send
esc bforoption+←,esc fforoption+→, the byte0x01forcmd+←and0x05forcmd+→. Those reach codeaf asalt+b,alt+f,ctrl+aandctrl+e, and all four are bound. This works with the option key set to Normal — the mappings do the work, so nothing has to be turned on. - Terminals that keep option a modifier — Ghostty, Kitty, WezTerm, and iTerm2
with Settings → Profiles → Keys → Left Option Key set to Esc+ — send
alt+left/alt+rightinstead, andmeta+left/meta+rightfor thecmdarrows. Those are bound too.
If option+← still does nothing, the profile has neither: set Left Option
Key to Esc+, or load Settings → Profiles → Keys → Presets → Natural Text
Editing. Either one is enough, and you only need one.
ctrl+← and ctrl+→ are bound but will never arrive on a Mac. macOS takes
them for Mission Control's "Move left/right a space" before any terminal sees the
keystroke. They are the Windows and Linux spelling of the word jump and they work
there. On a Mac, to get them you would have to turn those two shortcuts off in
System Settings → Keyboard → Keyboard Shortcuts → Mission Control — there is
nothing codeaf can do about it from inside.
ctrl+a and ctrl+e are the spellings that work on every terminal there is,
and they are the same two jumps. If you would rather not depend on any of the
above, those are the keys.
The caret keys are one vocabulary and every box on the surface answers it: the
message box, home's box at the foot of the screen, the errand pane on home,
the something else… box on a question's own answer row, the settings filter and
its value editor, the task page's filter, the rewind search, and every filterable
overlay — the model picker, /resume, /files, the connect key box, the
connections panel — and the Memories tab's filter and line editor.
| Chord | Everywhere |
|---|---|
alt+left / alt+b / ctrl+left |
A word back |
alt+right / alt+f / ctrl+right |
A word forward |
super+left / meta+left / ctrl+a |
Start of the line |
super+right / meta+right |
End of the line |
alt+backspace / ctrl+backspace |
Delete the word behind the caret. ctrl+w does it too in every filter and search box — but not in the message box, where it closes the tab |
ctrl+u |
Delete to the start of the line |
This did not used to be true: until this wave the jumps were bound in the message
box alone, so option+← moved a word in a conversation and did nothing at all in
home's box — which is the first box most people type into. home and end are
the exception and stay with the box that owns them: on the settings panel, the
task page and the rewind sheet they move the list, not the caret.
ctrl+e acts on the row under the cursor on home: on a conversation it asks to delete
it — the confirmation Permanently deleting a conversation from Home describes, and
nothing is deleted until you confirm. The card's legend says ctrl+e delete. A row of the
automations panel takes no ctrl+e: nothing is paused or deleted from home itself.
cmd+→ arrives as ctrl+e on a Mac, so reaching for the end of a sentence used to act on
whatever the cursor was resting on.
It reads the caret now. With the caret somewhere before the end of what you
typed, ctrl+e moves it to the end of the line and leaves the row alone; from the
end of the line — and over an empty box — it is the row's key the legend names.
So one press never reaches the row.
It used to put a conversation away in an archive, and ctrl+e on that dimmed row brought
it back. Since 2026-10-07 it asks to delete instead, and enter on a dimmed row is what
reopens a closed conversation.
A click on the box puts the caret under the pointer: on the letter you aimed at, at the row's end when you click past the end of a line, and at the start of the text when you click on the prompt's side of it. It works on a wrapped, multi-line draft.
It answers on home as well as in the conversation — home is the one place with a box at its foot (the Places page, Typing on a place); the filters on tasks and memory are rows of their own bodies.
Two things it does not do. With nothing typed there is no caret to place, so
the click falls through to the place underneath — the row is carrying a dim
sentence rather than a draft. And while a picker's filter box is standing in the
box's place — the model picker, /resume, /files — or while
the composer layer is up, a click does not move that box's caret; those are typed
at and filtered, not edited by pointer.
| Chord | What it does |
|---|---|
backspace |
Delete the character behind the caret. Over an empty box with attachments, it removes the last attached picture instead — and its [image #n] token with it |
delete |
Delete the character in front of the caret |
ctrl+u |
Delete to the start of this line — not the whole message, and not the whole box. From the middle of abcdef it leaves def |
ctrl+k |
Delete to the end of this line — the other half of the pair. It stops at the newline rather than joining the next line onto this one, so a second press on an emptied line does nothing. This chord used to open the conversation switcher, which is now alt+k |
super+backspace |
Same as ctrl+u (Mac cmd+delete) |
alt+backspace |
Delete the word behind the caret. This is the word kill in the message box |
ctrl+backspace |
Same as alt+backspace |
ctrl+w |
Not a deletion here. It closes the tab in front and keeps its draft — see Close the tab you are in below. It still deletes a word in every filter and search box, and on the switcher it closes the tab under the cursor |
ctrl+h |
Deliberately not bound — some terminals send plain backspace as ctrl+h |
The kills above work the same way in every box codeaf has, not only the
message box: home's own box, the errand pane beside it, the model picker, the
sessions roster, the deliverables list, the connect key box and panel, the memory
place's line editor, the settings filter and its value editor, the rewind search, and the task
page's filter. In those boxes ctrl+w is a word kill too —
they are the whole screen while they are up, and no tab could be closed from
inside one.
ctrl+w closes the tab in front, and it is exactly the ✕ on that tab.
ctrl+t opens a tab and ctrl+w shuts one, which is what those two keys do in
a browser.
Your unsent sentence, caret and attachments stay with the conversation. The
conversation remains in alt+k and Home, and reopening restores its tab and draft.
Closing the active tab selects the most recently used remaining open tab. With none left, the window goes Home with the current session behind it. Closing an inactive tab does not switch the current conversation.
Conversations keep working when another tab is selected, over every door: the ordinary
engine-backed chat, --host and --at each hold one connection per conversation, and
codeaf chat --no-host holds them in this process. Selecting another tab ends nothing and
sends no Stop or Close to either side of the switch. Closing the final tab only opens Home
and leaves that conversation behind it.
On the new chat page it closes that page, exactly as esc does: the page
comes down, the conversation you were in comes back with its draft and its work,
and the first message you had half typed is parked for the next time you open it.
Ending a conversation for good is a different act, and this key is not it:
Stop on a task's page ends that work, and /quit closes the conversation in
front. See Close a tab from the switcher below, which is the same
gesture aimed at a row on the alt+k card instead of at the tab in front.
The trade is that ctrl+w no longer deletes a word in the message box.
alt+backspace and ctrl+backspace still do, and one of the two reaches
codeaf on every terminal.
cmd+delete is bound. codeaf answers it under the name super+backspace, and it
deletes to the start of the line, exactly as ctrl+u does. When it does nothing
at all, the key never reached codeaf: your terminal decides whether cmd
combinations are sent to the program at all, and several do not send them.
| Terminal | Does cmd+delete reach codeaf? |
|---|---|
| Ghostty | Yes |
| Kitty | Yes |
| WezTerm | Yes |
| iTerm2 | Yes with the Natural Text Editing preset, which maps ⌘⌫ to the byte 0x15 — that reaches codeaf as ctrl+u, the same deletion. Load it at Settings → Profiles → Keys → Presets. Without a mapping iTerm2 does not forward cmd+delete at all; you can also add one by hand at Key Mappings: ⌘⌫, action Send Escape Sequence, [127;9u |
| Terminal.app | No, and it cannot be made to. It does not speak the keyboard protocol that carries modifiers like cmd |
Anything over ssh or tmux |
Only if the outer terminal is one of the first three, and tmux is passing the protocol through |
ctrl+u is the spelling that works everywhere, on every terminal on every
machine, and it is the same deletion. If cmd+delete does nothing where you are
sitting, that is the key to use instead — nothing is missing and there is nothing
to turn on inside codeaf.
The same is true of alt+backspace and ctrl+backspace for the word kill, and
between the two of them every terminal sends one — which is what makes it safe
for ctrl+w, their old third spelling, to close the tab instead. On iTerm2's
Natural Text Editing preset
opt+⌫ is mapped to esc del, which arrives as alt+backspace and kills a word.
codeaf does not detect what your terminal sends and cannot tell you which of
these it will deliver; the only test is pressing it.
It is one line marked › with no border, and it holds newlines, so it is a small
multi-line editor rather than a single-line field.
- It shows at most 6 rows at once, further capped by your terminal height minus two, and never fewer than one row. It never takes more than 6 rows from the conversation no matter how large the paste.
- A longer message scrolls inside the box, following the caret. The rows scrolled
past are marked with an ellipsis in the same two cells the
›occupies, so nothing shifts under your caret. - Continuation rows are indented to sit under the text. Soft wrapping breaks at the last space before the edge, and mid-word only when the line offers no space.
Inside a task's room the box wears a segment in front of its › , naming the work
your words are going to: the task's state glyph and its name, on the same tinted
background a selected row wears, in the hue that task's state is drawn in everywhere
else — accent while it runs and again while it is your call, muted once it is done, the
bad hue when something broke, dim when it is queued or you stopped it. It is there
whether the box is empty or full, which is the point: the placeholder that used to say
this disappeared the moment you started typing. The name is cut to at most 18 cells; on
a frame too narrow to spend the cells, the segment is dropped and the placeholder goes
back to naming the task itself. There is no segment at all in the main conversation.
A slash command you type into the box is highlighted as you type it — /task,
/compact, /clear get a tinted background behind the word, so a real command looks
different from ordinary text and a typo like /tsak does not. The highlight is drawn in
your sent message too. It adds no characters and no cells; see "Slash commands are drawn
as chips" in the commands page for the whole of it.
A key chord is never given that background. Where codeaf names a key — the hint slot
on the legend, the /help sheet, the opening esc interrupts · ctrl+c quits · ? for help — the
chord is drawn one tier brighter than the words around it and nothing else changes. A
tinted background always means a slash command and only ever that, so the two marks
never have to be told apart. See "Why is one word in a line brighter than the rest" on
the screen page.
Pasted text lands as one edit with its newlines intact — it never submits line by line. Bracketed paste is on. CRLF and bare CR become LF at the door.
Inside an open paste bracket, every key is text: enter and ctrl+j become a
newline, tab becomes a tab, everything else contributes its text. Nothing between
the brackets can submit, interrupt, or answer a question. ctrl+c is the one
exception and still works — it leaves codeaf without closing the bracket. A bracket that goes quiet for 2 seconds is treated as
abandoned, flushed, and the keyboard handed back.
A paste while copy mode is up is declined — nothing happens, and your clipboard still holds the text.
Type what you want and press ctrl+r. codeaf reads the sentence sitting in the box
and writes, in dim text under it, what it takes that sentence to mean:
› build me a login page
taking it to mean —
- email and password, and the form says which field is wrong
- a session that survives a refresh
- I'll pick a cookie session unless you say
enter add it to what you're saying · esc leave it
Three kinds of thing go into that block, and each is handled differently. What you
said is your requirement and is never repeated, improved or reworded — it is already in
your box and it stays there untouched. What anyone would obviously want is supplied
outright, as a concrete line, without asking you about it. What is genuinely yours to
decide gets a sensible default with a flag on it — I'll pick … unless you say — and
only where a wrong guess would waste the work; the rest takes a default in silence.
It is one small model call on your cheap tier, made only when you press the chord. It never runs on its own, it never sends anything, and nothing about it goes into the conversation. It is added to what this session has spent, the way the session's own name and other calls codeaf makes for itself are, and it does not count as a turn.
If it fails or takes longer than 10 seconds you get nothing — no error line, no note. The hint under the box simply comes back, and your draft has not been touched.
The taking it to mean — block is not part of your message. It is drawn below the
box precisely so it does not look like it is. Two keys:
enteradds it to what you're saying. The lines are appended to your draft after a blank line, as plain text. From that moment they are your words like any others — edit them, delete the ones you disagree with, keep typing. Nothing has been sent: the nextentersends the message, and it goes as one ordinary message.escleaves it. The block goes, your draft is byte for byte the characters it was, and nothing is remembered anywhere.
So a line you did not want is a line you press esc on, or delete after adding. There is
no setting to turn off, because there is nothing running until your finger is on the
chord.
Any key that changes the draft dismisses the block as well. It was about the sentence
you had, not the one you now have, and an expansion of an older sentence must never be
added to a newer one. Press ctrl+r again for the new one.
The hint appears at the right end of the rule above the message box only while your draft
looks like something to build and still has room to grow: it contains a making word —
build, create, make, write, design, add, implement, set up, generate,
draft, put together — and is under 120 characters with no bullet list in it.
A detailed draft gets no hint, and that is the point rather than a limit. If you have
already spelled out what you want, there is nothing here for you, and a dim line under
every draft on the screen is a line people learn to stop seeing. The same goes for a draft
that is empty, one that starts with /, and one where you have typed the list yourself.
The word list is a courtesy for teaching you the chord, not a rule about what can be
spelled out — the chord works wherever the hint is drawn, and nowhere else. Pressed where
the hint is absent, ctrl+r does nothing at all.
It shares the slot with a live /task tag's line, enter sizes this task, which
wins whenever both would show: a tag changes what enter does, and the slot names that
first.
The hint never moves the message box. It rides a line that is on the frame either way, so a draft that starts looking like something to build changes one word at the end of a rule and nothing else. While the call is out that same slot turns a small spinner in front of the words; when the answer lands the block appears under the box, and the box itself has still not moved.
The box belongs to whoever it is talking to. The conversation has one unsent
sentence, and every task page you open has its own. Opening a task's page, pressing
esc to come back, or clicking straight from one task to another never moves a word
from one of them to another.
What each one keeps: the text, where the caret is in it, its compact [paste 1 · 42 lines] chips with the documents behind them, and anything on its tray. So a
half-written message for the model is exactly as you left it when you come back from a
task, caret included, and a correction you started typing at a task is still there when
you open that task again.
enter sends the box you are looking at, and clears only that one. Steering a task
empties that task's box and leaves the conversation's sentence and every other task's
alone. If steering is refused and you answer the question with [m] — send it to the
main conversation instead — the words that go are the ones you typed at the task, and
your unsent sentence for the model is still in the box you come back to.
All of it is kept on disk, per recipient, and comes back after a crash or a restart: the conversation's sentence with its caret, and each task page's own line with its documents and tray. A task page's line is never restored into the conversation, and never into a different conversation's task that happens to have the same number. The section below is the whole of how that is written down.
Before this, there was one box and one set of words in it: typing a message for the
model, clicking a task and pressing enter sent that message to the task, with nothing
on the screen looking any different at any point.
The half-written message survives closing the window, a crash, /new, and a session
that has moved on. There is nothing to press; it is automatic.
- Every recipient's box is kept, each to itself. Quitting while a task's page is open writes the sentence you had for the model and the correction you were typing at the worker; the next launch puts each one back where it was typed. Nothing typed at a task ever comes back in a box pointed at the model.
- It is written 300ms after you stop typing, and again synchronously on quit before anything else happens.
- Anything still waiting for an answer is folded in on quit. A message you parked
with
enterwhile a turn was running (see "Typing while the model is still answering") is written into the draft file underneath your unsent sentence, each on its own line, so it comes back the next time you open codeaf here instead of vanishing with the session. - It is cleared only when you send it, or queue it as a follow-up.
/newdoes not clear it. - A box holding only blank lines or spaces is not a draft, and nothing is written
for it — the file is removed instead. Blank lines are what
ctrl+jandalt+enterleave behind. Unsupported modified-enter chords can arrive as plain enter, or asctrl+jon some terminals; only the latter opens a blank line, and nothing on the frame draws them. One kept on disk used to be adopted by the next window in the directory, which then opened with a box that looked empty, was not, and refusedspace spacefor home. - The file is keyed by the chat and this window, and is written with mode 0600. Copied chats have separate keys across projects, and the same chat keeps its identity when its copy moves to another folder.
- At startup, codeaf restores that chat's newest saved draft from a window whose process is no longer running. Another chat's saved words are never adopted. Older plain drafts without a recorded chat can still be recovered for older conversations in the same project. Files belonging to a process that is still alive are never touched, and older drafts are left where they are. A process id that cannot be checked is treated as still alive.
- CR and CRLF in a restored draft are normalised to LF.
- No draft is kept at all if codeaf was started without a draft file.
If sending from an older window opens Continue this chat here?, choosing continue here puts your exact draft in the arrived chat's message box, including spaces, newlines, pasted text and attachments. This also works when the chat reopens in a different folder. It stays unsent until you press enter again. Choosing leave it there, or a move that fails, keeps the draft too.
Everything the box is holding is written to one record per conversation, and the plain file you can read sits beside it.
- One record, and a plain file beside it.
draft-<id>-<n>-<pid>.jsonis the record and is what a restore reads: every box's words and caret, the documents behind its[paste 1 · 42 lines]chips, its tray, each stamped with the machine, the project and the conversation it belongs to.draft-<id>-<n>-<pid>.txtis your sentence in plain text, exactly as it always was — a build that has never heard of the record still reads and writes it, and it is what a restore falls back on where there is no record. - A sent or cleared box stays empty. The record is the answer even when it has nothing to say. It is written before the plain file, so a crash between the two can leave that file still holding the sentence you just sent; the record is believed instead, and the stale file is cleared away when it is found. An empty record is left behind saying "this box is empty" rather than deleted, and goes when the conversation is closed.
- Nothing is dropped to make it fit. There is no size limit and nothing is truncated: a pasted log of any size is written down with the sentence it belongs to.
- A correction sent to a task is in the record too, before it is sent.
enterin a task's page writes the words, the caret, the pasted blocks and the name that correction was sent under into that page's slot, and the correction crosses only once that write has landed — so a message waiting for an answer does survive closing the window. The next launch puts it back on that page under the same name, so asking again is a repeat rather than a second correction. If the record cannot be written the correction is not sent at all:task 7 was not corrected — the draft could not be saved first, and your words are back on its page. See I sent a correction and the window closed on the tasks page. - What comes back is what you left, and what is wrong with it is said. An attachment
whose file was deleted meanwhile is still on the tray, and the conversation says
a restored draft still names a file that is gone · shot.png;enternames it again if you send it anyway. A[paste 1 · 42 lines]tag restored from a plain file with no record behind it keeps its place in your sentence, and the conversation saysa pasted block could not be restored · its tag is still in the draft. Nothing is edited out of your words for you. - And
enterwill not send that message. With a compact tag whose text is gone, the conversation and a task page both refuse witha pasted block could not be restored · the words were not sent, and your whole line stays in the box: sending would hand the model the tag instead of the document, and deleting the tag would send a different message from the one on your screen. Paste the block again, or delete the tag. - If it cannot be written, you are told:
this draft could not be saved, with the reason. A record codeaf cannot read — a later build's, or a half-finished write — is never overwritten either; it is moved aside as<name>.json.unreadable-<number>and the new one written in its place. - A task page's line follows its conversation, not the window. Open that conversation tomorrow in a different window and its lines come with it; open a different conversation and they are neither shown to you nor deleted.
- Only the newest record for a conversation is read. If two windows each left one — two crashes — the later one wins outright, so a box you emptied is not refilled by the earlier one. The earlier record is left on disk untouched rather than merged or deleted; nothing in codeaf offers it back to you.
up, with the caret on the first line of the message box, walks back through the
prompts you typed before, newest first. down walks forward again toward your live
draft. esc cancels the walk and restores your own sentence exactly as you left it.
- A message waiting for the answer is read first. With an empty box and something
waiting above it,
uptakes that message back into the box to be edited instead of walking your history. Only once nothing is waiting doesupwalk the history again. - Your live draft is stashed on the way in, and comes back on
escor on walking forward past the newest entry. - Walking past the oldest entry stays put rather than emptying the box.
- The list is built once per walk, in two passes: everything you typed in this directory first, then everything you typed anywhere. Duplicates are dropped. Each pass goes 200 entries deep.
Slash commands are recalled too. Every non-empty line you press enter on is
remembered, commands included, and a command you pick out of the command list is
remembered as well.
With history not wired up (--no-history), up takes nothing and keeps its other
meanings.
What is the chip above the message box — the thinking chip, alt+e, /effort, and how to make this one chat think harder
The line above the message box — the legend — names how hard the model will think about
your next turn, immediately after the model that will be doing the thinking:
The effort follows a colon with no space: model:effort. The model stays bold and
bright; effort keeps its own styling. The old six-dot badge is no longer shown.
─ glm-5.3-flash (deepinfra):high · ◇ asks ──── $0.27 66.8k/1.3M · 5% idle ─
› what changed in the relay this week
The word is one of the five rungs of the effort ladder — low, medium, high,
xhigh, max — or auto, and it is what will actually happen, not what somebody
chose: it is the rung the next turn will ask for, whichever setting decided it. See
Making the model think harder, deeper, or less on the "Models and cost" page for the
whole ladder and for what each rung asks the provider for.
A conversation nobody has dialled reads auto, which is what a shipped install
says on every fresh conversation. See What auto means beside the model below.
The same cell is on the rule above the box on home, where it
says how hard the conversation you are about to start will think, and alt+e or a press
walks it there too. A rung set there is carried onto the conversation enter opens and
lasts as long as this window does. The rule above home's box on the Places
page has all four cells of that rule.
alt+e walks it. Each press moves one rung up, and off the top it comes back to
auto: auto → low → medium → high → xhigh → max → auto. It works with a sentence half
typed — it is a chord, it carries no text of its own, and it leaves your draft and your
caret exactly where they were. Ordinary letters keep typing.
Pressing the rung walks it too, one step per press — the same six stops, auto
included — which is the same gesture as pressing a task's thinking row inside that task. It brightens under the pointer over
exactly its own cells first, and the press never moves the caret in your draft. It does
not open a list: the list is /effort.
/effort opens the ladder: six rows — auto first, then the five rungs cheapest
first — with the row you are on marked. ↑/↓ walk it, enter applies, esc closes,
and alt+e moves the cursor down a row while the list is up. While the list is up
every key belongs to it — a plain letter does not type into the message box
underneath. Typing /effort again puts the list away. /thinking and /think are the
same command. The six rows read:
auto the model decides — the shipped setting
low answers quickly and barely deliberates
medium a short think before it answers
high thinks before it answers
xhigh a deeper pass, and it takes the time that costs
max the deepest pass there is
/effort <rung> sets one outright — /effort max, /effort low — without opening
anything. /effort auto clears this conversation's rung, and /effort off is the same
thing under its older name. A word that is none of the six changes nothing and prints
them all.
Until 2026-09-09 the rung was a chip at the right end of the tray row above the box, and clicking that opened the ladder. The rung is on the legend now, beside the model it is about, and the click walks it.
What it changes and what it does not:
- It sets this conversation's rung. It is sticky — kept in this session's own
meta.json— so it is still there after you close codeaf and come back. - The rung reaches the work this conversation hands out: task workers start at it too.
- It does not change other conversations. The default for those is the thinking
row in
/settings, which ships atauto(the provider default). autois on the legend, it is the ladder's top row, and it is a stop on the wheel. With thinking atautoand no more specific level chosen — which is what a shipped install is — the cell readsauto, it is pressable, andalt+ewalks it ontolow. One more press pastmaxbrings it back toauto.- It works on a
--hostconversation. The rung is set on the engine machine, where the conversation lives, and the word on your legend is the one that machine resolved. An engine too old to know the ladder says so at the door and there is then no rung on the line, no press, and no chord — rather than a knob that does nothing.
When the rung will not move. A thinking level dialled onto the model itself — the
model picker's ctrl+t, or --reasoning at launch — beats this conversation's rung. Press
alt+e there and codeaf says so in a note, naming the model and pointing at ctrl+t:
thinking stays low · the level set on <model> decides this conversation — ctrl+t in
/model changes it. Clear that level and the rung moves again.
The rung is dim, like the rest of that line. It brightens for about two seconds after it
changes — the cell takes a lit ground and its effort word goes cyan — so you can see the new word
without looking away from what you are typing, and then it goes quiet again. Walking it
back onto auto flashes the same way and writes one line: thinking · auto · the model
decides.
The approvals chip above the message box — alt+a, turning YOLO on inside a chat, stop asking me for this conversation
After the thinking rung, the legend names what this conversation runs without asking:
─ glm-5.3-flash (deepinfra):high · ◇ asks ─── $0.27 66.8k/1.3M · 5% idle ─
› what changed in the relay this week
The cell is the permissions panel's own mark for "a whole tool" and one word, and it is there at every posture — a control you cannot see until you have used it is not a control. The word is what is in force, whichever setting decided it:
| word | what it means |
|---|---|
asks |
every call the rules say to ask about is asked about |
guardian |
a small model answers the plainly safe ones first; you get the rest |
YOLO |
every tool runs without asking — the posture --yolo opens. Painted in the warning hue for as long as it is true |
refuses |
every call the rules do not name is refused. Also painted as a warning |
alt+a walks it: asks → guardian → YOLO → asks. Three stops, each more autonomy than
the last, and the wheel never lands on refuses — a press past YOLO that refused every
call would break the session you are in. It works with a sentence half typed and leaves
your draft and caret where they were. Pressing the cell walks it too, one stop per
press; it brightens under the pointer over exactly its own cells first.
Why not ctrl+y or shift+tab: ctrl+y copies a path on home and in /files, and one
chord means one thing on this surface; shift+tab walks backwards through the fields of
every question card, and on some terminals arrives as a plain tab.
Inside a task's page the cell reads ◇ on its own — a task runs every tool without
asking and has no wheel — and alt+a there says so instead of moving anything (the task
page's "The line above the box on a task's page"). On home, the same
◇ cell sits on the rule above home's box and
says what the conversation you are about to start will run without asking — the settings
rows' answer, or YOLO if this process was started with --yolo. alt+a or a press walks
it there on the same wheel, the pin is carried onto the conversation enter opens, and it
is spent by that conversation: back on home the cell says the rows' word again, so an
open gate is never quietly the default for the one after. The rule above home's box on
the Places page has the whole rule.
What it changes and what it does not:
- It sets this conversation's posture, live — the very next tool call is decided under
it. It is sticky, kept in this session's own
meta.json, so it is still there after you close codeaf and/resume.codeaf resume --yolooutranks the saved word for that launch. - It does not change other conversations. Their answer is the tool approvals
row and the AI approval screening row on
/settings' Permissions category, unless they have a saved posture of their own. - Neither floor moves. Dangerous shell commands and anything sent in your name are
asked about at every stop,
YOLOincluded, exactly as under--yolo. - It works on a
--hostconversation. The posture is set on the engine machine, where the gate is, and the word on your legend is the one that machine resolved. An engine too old to have the door says so when the connection opens: the cell is then a reading of the far machine's own row, andalt+aand the press both answer: what runs without asking is decided on the machine the conversation runs on — its engine has no dial for this window · change it in that machine's /settings.
The cell flashes for about two seconds after it changes and writes one line —
approvals · YOLO · every tool runs without asking · dangerous commands still ask — and
then settles. While the welcome box is on screen there is no legend and no badge either —
the cell arrives with the legend the moment the greeting goes (the first keystroke, or on the
very first conversation the first message). The status line carries the old
YOLO badge only on a frame whose legend has no cell, over an engine with no approvals
door, and only while the gate is open.
auto on the line above the message box means nobody has asked this conversation to
think any particular amount. codeaf sends no reasoning field on the request at all, and
the model thinks however it thinks — its own published default. It is not "think as little
as possible": that is a different request, and low is the rung for it.
It is what a fresh install says. The thinking row in /settings ships at auto,
so until you dial something — this conversation with alt+e, /effort or a press on the
cell; one model with the picker's ctrl+t; one task with alt+e on it; or the machine
itself in /settings — every conversation reads auto.
Home also keeps model:auto on the seam and alt+e effort in the bottom row.
This applies to local engine connections and --host alike: an unset default is
auto, not a missing control. Press the effort word or use alt+e to change the
next conversation's effort.
To put it back to auto: keep pressing alt+e or the cell — the wheel's stop after
max is auto — or type /effort auto (or /effort off, the older name for the same
thing), or open /effort and pick the top row. The typed word and the top row do it in
one move from any rung; the wheel gets there by walking. Until 2026-09-15 the wheel had
five stops and could not reach auto at all, which left the state a fresh install starts
at as the one thing the control in front of you could not say.
Clearing it does not always change the word on the line, and codeaf says why in a note either way:
- Nothing else is set: the cell reads
autoand the note is thinking · auto · the model decides. - The thinking row in
/settingsis set on this machine: a cleared conversation falls back to that row, so the cell keeps its word and the note is thinking · auto for this chat · <rung> · the thinking row in /settings decides now. - A level is dialled onto the model itself (
ctrl+tin/model, or--reasoningat launch): that level beats every rung here, the cell keeps saying it, and the note names the model and points atctrl+t.
Until 2026-09-09 there was no cell at all on a conversation nobody had dialled, which
on a shipped install meant every conversation — so the dial was invisible to anyone who
had not already found it. Both roads say auto now, the local one and --host. The cell
is missing in exactly one case: a session with no dial behind it, which is a --host
connection to an engine too old to know the ladder. There is then no rung, no press and no
chord, and /effort says how hard this conversation thinks is unavailable — this session
has no dial onto it.
There are three ways in.
- Drag a file in, or paste one. Drop a screenshot on the terminal — or copy a
file in Finder or your file manager and press
cmd+v/ctrl+shift+v— and codeaf attaches it. See "Dragging or pasting a screenshot in" below, which is the way most people do this. /attach <path>. A picture handed to it is a picture.~becomes your home directory, a relative path is resolved against the conversation's directory — or against your own machine's working directory over--host— an absolute path is left alone, and a quoted or backslash-escaped path is read as the one path it is. (/image <path>was a second word for this until 2026-09-22 and is gone.)- The
@completion. An image row in the list is taggedimg. Choosing it removes the half-typed@tokenfrom your sentence and puts the file in the tray, instead of typing a path.
Typing out an @ path to an image by hand does not attach — attaching happens when
you choose the completion row.
What is accepted: .png, .jpg, .jpeg, .webp and .gif, case-insensitive.
Those five are exactly what codeaf will send. The ceiling is 10 MB per picture,
checked against the file's size first and again against the bytes actually read. The
same path attached twice is one chip.
You do not always have to attach. A file already on the machine can be looked
at without the tray: ask codeaf to look at it by path and it uses its view_image
tool, which opens the picture with the looking model — the same one model that
answers every picture question here, whether you attached the file or not.
Attaching is for a picture you are handing over as part of what you are saying.
The tray. Attached pictures sit in a one-row tray directly above the message box,
one dim chip each, drawn as ▣ #1 name.png × — * in place of the square and
x for removal on an ASCII terminal. The number is the picture's place in the message and the number [image #1]
in your sentence refers to. The message box stays the sentence. backspace over an
empty box drops the last chip, and clicking a chip removes that one — and takes its
[image #n] out of your sentence, counting the ones behind it down so the numbers
stay true. Returning to Home preserves your unsent prompt, cursor and attachments in
that conversation. Reopening it restores them. Home starts with an empty box and tray;
a new conversation receives only what you type or attach there. Queued messages keep
their own attachments.
Drag a picture onto the terminal, or paste one you copied as a file, and codeaf
attaches it. What the terminal actually hands over is the file's path as pasted
text — /var/folders/.../Screenshot 2026-08-21 at 5.21.40 PM.png, usually with its
spaces backslashed, sometimes quoted, sometimes as a file:// URL, and sometimes with
raw spaces or %20 escapes. codeaf reads every shape, including the narrow no-break
space in a macOS screenshot name, and reads several files dropped at once, separated by
spaces or by newlines.
Your sentence gets [image #1], not the path. The picture goes on the tray and a
short token takes its place in the message box, numbered in the order the pictures
were attached. It is ordinary text: type around it, delete it, move it. And it is
what you say out loud — "what font is image #1", "compare image #1 with image #2" —
because the token goes to codeaf inside your message, in the position you left it,
and the picture itself travels with it. codeaf is told that [image #1] marks the
first picture in the message, so the number you read is the picture it is looking at.
A picture attached by /attach or the @ completion gets its token too, appended
to the end of your sentence when you press enter, so "image 2" means the same thing
whichever way the picture got there.
It is all or nothing, on purpose. A paste is treated as attachments only when one
complete terminal reading of it names real files on this machine. A sentence that
mentions a .png, a diff, a stack trace, a log — all of it goes into the message box as
the text it plainly is, which is what pasting has always done.
A paste over a line that starts with / is left as text too, so /attach and
/export still take a path.
Raw image data on the clipboard is not read. Copying a picture out of a browser or
a screenshot tool — as pixels rather than as a file — pastes nothing here. Save it to
a file first, then drag that in, or use /attach <path>.
| Situation | Exact text |
|---|---|
/attach with no path |
opens the file browser rather than refusing |
| Not one of the five types | it is attached as a file, not refused |
| Missing file | no such file: <path as typed> |
| Already in the tray | <basename> is already attached |
| Dragged or pasted in over the ceiling | <basename> is over the 10MB image limit |
| Unreadable when you send | could not read <basename> |
| Over the ceiling when you send | <basename> is over the 10MB image limit |
A dragged or pasted picture is measured at the moment you drop it, and one over the
ceiling is refused there rather than attached and refused later. Nothing is lost when
that happens: the path stays in your message box as the text it arrived as, so you can
still ask codeaf to look at the file where it lies. Pressing enter on that retained
absolute path repeats the attachment refusal; it is not treated as an unknown slash
command and the path remains in the box.
A picture that is dragged in but does not exist on this machine stays in the message
box unchanged and says <basename> is not on this machine. The path is still there to
edit or retry; no chip means no picture will be sent.
A refusal keeps your pictures. The tray is emptied while the message is in flight; if sending fails, the chips are put back, in front of anything attached in the meantime, without duplicating.
enter with a full tray sends. The files are read at the moment you press enter.
The line in the conversation becomes your sentence — tokens and all — plus the file
names in dim square brackets, numbered to match:
› what is wrong with this [image #1] [#1 chart.png]. A message with pictures and no
words is still a message; it goes out as its tokens alone.
codeaf really sees the picture. It does not receive the path and go and open it:
the bytes travel inside the message as the picture itself, base64-encoded, beside your
words, which is why the path is rooted on your local machine even on a remote
session. If the model you are talking to cannot see, the picture is shown to a model
that can and its answer comes back prefixed [vision: <model>]; if nothing available
can see, the message is refused before anything is sent and your pictures stay on the
tray. Once the message is sent, the transcript keeps the numbered marker and draws a
compact control for each picture under your line.
A command with a full tray is still a command: /attach adds a second picture rather
than sending the first.
Yes. After sending, each image has a compact filename row in tray order, with preview
and open original actions. The numbered [#1 shot.png] marker remains above it.
Images start collapsed; your words are never folded with them.
Click preview to show a low-resolution terminal view, and collapse to close it. Only one attached picture per message expands at a time. alt+i toggles the last visible image in chat or a task page. The terminal preview is at most 20 rows; it is not suitable for reading screenshot text.
Click the expanded picture itself or [open original], or use alt+o, for the
full-quality image in your system's
viewer. The phone-width detail sheet also accepts o. In --host sessions,
engine-owned files are fetched to this machine first. In a plain SSH login, the
action explains how to use the local client and gives the original path instead
of opening a viewer on the server.
The original action works even when the terminal cannot draw pictures. If a requested
preview is missing, unsupported or unreadable, it says Preview unavailable · open
the original. Replaying a conversation starts its attachments collapsed again.
Type @ and codeaf offers one list under the message box. The first row is the
words team, chat and file. Under them: teams, then conversations,
then tasks, then files and folders. It opens on the bare @; no letter is
needed first. It closes on esc, on committing, or when the token stops being one.
team, chat and file are presses. The word under the pointer takes a
background, and the hint says only teams · click, only conversations · click or
only files · click. A press types @team:, @chat: or @file: and keeps only
that section. Press the same word again and the prefix comes off. Home's list has
the same prefixes; its first-row words are not buttons, so type the prefix there.
A command's path argument (/image , /export , /attach ) stays a file list
and has no prefix row.
Folders have a trailing slash (internal/tui3/) and a folder note, like a
picture's img note. Choosing one puts its path into the sentence. Use /folder
to choose a folder as a place.
The prefix words, teams and tabs arrive from memory; recent conversations and
files arrive as they are read. Recents are read once per opening when no read is
pending: a new @ token or the next letter after esc. Openings during a read
share one follow-up; the older answer cannot settle their search. Letters, caret
moves and automatic closes start no read. Removing the token ends it. After esc,
a space or punctuation keeps even a bare @ closed. A prefixed multi-word chat
search waits with looking… for its fresh answer.
An unread catalog says looking…; a read with no match says no matches, or
no team matches, no conversation matches or no file matches for a prefix.
Arrivals select the best match unless you chose a row with arrows since the query
changed; that choice survives while offered. A changed query returns to the best
match. On home, clicking inserts the row.
A bare @ ends at its first space: ask @ben to fix stays a sentence and Enter
sends it whole. @ is no token. An @ in the middle of a word, such as an email
address or a Go doc link, never opens the list.
Only @team:, @chat: and @file: searches may hold spaces. The token walks back
from the caret over at most three spaces, never past a newline. @chat:who is
finds who is kim jong il; @file:internal tui3 finds internal/tui3/;
@team:har bor finds harbor. These searches work in a conversation, on home
and on the new-chat page.
Teams, conversations and files match every word in any order. A word may match a prefix, substring or subsequence; only the last word, still being typed, may match by its letters in order. Finished words match whole. Tasks keep their own scorer for the whole query rather than matching each word separately.
A prefixed search of several words that matches nothing closes only after its
catalog has been read; before then the list stays up and starts the read. One word
that matches nothing still says so. A space or punctuation after a chosen mention
keeps the list closed: @side-chat, and @side-chat is a chat are writing on.
The conversations are every tab open in this window, including other projects'
tabs, except the one you are typing in. Only the window's own front conversation
is absent for being unnamed with nothing sent, even if its draft names its tab.
Held, restored and side tabs are offered without that check. Then come the twenty
most recent in this project, read once per opening when no read is pending;
openings during a read share one follow-up. Older conversations and other
projects' saved conversations require /resume unless they are already open here.
Home and the new-chat page leave no eligible conversation off: their sentences start a
new conversation, so the one behind them can be a reference too.
The bare @ keeps eight teams and eight conversations. @chat: and @team:
keep up to thirty-two rows and scroll. File hits are capped at 32. The list draws
8 rows, or 14 when a team, conversation or task is present. Task rows are capped
at 8 and searched to a pool of 40.
The @ file list walks the conversation's workspace, or your own machine's
working directory over --host. Home's list walks the pinned target, otherwise
this window's file root. On a local window that is the workspace, where the next
conversation opens and what project: names while the list is up. Over --host,
the unpinned list offers this machine's files, as a conversation's list does;
the foot and sent sentence use the far machine's workspace. The row selected
before opening the list does not choose the file root.
Moving that target with alt+p or /project replaces the catalog and walks again. Home offers files and folders, never tasks:
a task pointer is minted when a conversation sends and home has none yet.
Skipped: .git, vendor, node_modules, every dot-directory, every dot-file and
every symlink. Unreadable directories are skipped rather than fatal. The walk is
capped at 10,000 files, with paths relative to its root and forward slashes.
It runs once per conversation; a file created part-way through will not appear.
With its list closed, home keeps its completed walk across arrows over other
projects and reuses it when the list next opens in the same folder.
File ranking puts a prefix above a substring above a subsequence. The whole path and base name are both tried at each tier, with the base name a hair below the path. Inside a tier the earlier match wins, then the shorter path.
- A file: the path replaces what you typed after the
@, and the@stays. Nothing is read at this point.@internal/session/agent.gois sent exactly as it stands, and codeaf's read tool resolves it if it wants to. - A folder: the same thing, with the trailing slash kept —
@internal/tui3/. - An image: the whole half-typed
@tokenis removed and the file is attached instead. - A task: the task's name goes in after the
@. The pointer block is minted when you send, not here. - A team: the
@comes out and a coloured dot plus the team's slug goes in,●harbor, in that team's colour. On a screen that draws no colour the dot is*. - A conversation:
@and its handle, or a short slug of its title when it has no handle. The row's note is the full title. A conversation in no team is still on the list. - Under a command's path argument: the path replaces the argument whole, with no
@in front, and an image is written into the line like any other file.
Every @<slug> that names a task codeaf already knows about grows
a pointer-block footnote after the message, one block per task, in token order,
deduplicated. A team mark and a chat mark do something else, on the engine: the
model is handed a short digest of that team or that conversation, and your
transcript keeps the words you typed. The digest is members, handles, states and
recent traffic for a team, and the title, the state and an excerpt of the last
reply for a chat. It is never the whole transcript. Mentioning a conversation does
not message it and does not start its turn. Unknown tokens are left alone in
silence. A task slug pasted whole and sent in the same beat resolves against the
snapshot already in memory and never touches the disk, so it may stay plain text.
The entry remembered for ↑ is the sentence as you typed it, before the task
footnote.
The honest limit: /attach (and its /upload alias) and /export get path
completion. That is the whole list. Any other command that takes a path gets no
completion at all, and says nothing about it. Over --host, completion still walks the
machine you are sitting at: /attach sends those local bytes across.
These two lists are not modal — you keep typing into the same box and the list follows what you type. Only these keys are taken from you:
| Chord | What it does |
|---|---|
up / ctrl+p |
Move the list cursor up |
down / ctrl+n |
Move the list cursor down |
esc |
Close the list. For the command list it also seals that word — the list does not reopen on the next letter of it. It does not interrupt a running turn |
enter |
Command list: take the highlighted command. At the start of an otherwise empty box that runs it; anywhere else it replaces just that word with the command's name and runs nothing. If nothing matched, the line is sent as typed. @ list: insert the highlighted team, conversation, task or file; if nothing is picked, the line is sent |
tab |
Command list on Home or in a conversation: take the highlighted command, as Enter does. Mid-sentence it only completes the word; a finished send tag is not submitted. No match leaves the draft alone. Otherwise opens or commits path completion over /attach or /export ; with an empty conversation box it goes back to the last conversation |
enter, with an argument completion open |
Closes the list and runs the line as typed. Your path is never swapped for the top-ranked row |
Model picker — opened by /model with no argument, by clicking the model name
in the status row, or by enter on the your model row of the settings panel's
Models category (the same list and the same keys, drawn in the panel's place):
esc close · enter switch to the highlighted model and close the list · ctrl+t cycle the reasoning
effort · ctrl+r fetch the newest model list (/model only — not the settings panel's
rows) · tab and → open the providers under the model the cursor is on and move the
cursor into them, tab and ← close them and put it back on the model ·
up/ctrl+p, down/ctrl+n, pgup, pgdown walk the list · the mouse wheel
walks the list too, three rows a notch, clamped at both ends, in /model, settings
slots and roles, home's draft list and the task composer's list. The page beneath
stays put until the list closes ·
backspace, delete, ctrl+u, ctrl+w, left/ctrl+b, right/ctrl+f,
home/ctrl+a, end/ctrl+e edit the filter · alt+s orders the list by the next
column and alt+shift+s turns that column round · anything else types into it.
Every model list opens on the model it holds, highlighted and on screen. /model
opens on the model in use, so enter with nothing typed confirms; settings slots and
roles, a task's model word, home's draft, alt+o and lane lists and chips follow the
same rule. A refresh puts the cursor back on the held model with the filter kept,
and ctrl+u emptying the box returns there too. If that model is absent, the cursor
uses the first selectable row past unavailable notices. /model <query> and typing
a filter start the narrowed list at its first row. The model in use is bold in the
accent with no band of its own; the cursor supplies the band, and a pointer hovering
another row lifts it too.
Every model picker column is two rungs — its own direction, then reversed — so alt+s walks model ↓,
model ↑, via ↓, via ↑, and so on back round to the name, skipping any column this list
published nothing in; alt+shift+s retraces it. The list is always sorted and the sorted
column always wears ↓ or ↑ in the heading. Inside an open provider fold the same key
sorts the PROVIDERS, and the two tables keep their own orders. It is a chord and not a bare s for the reason the activity page gives:
s is one of the commonest letters a filter starts with, and the list a person was
narrowing would re-sort instead.
→ and ← are the providers' keys unless you are in the middle of typing, in which case
they move the caret through the filter text. "In the middle of typing" means within 0.6
seconds of the last change to the box; every keystroke pushes that out again, so they stay
the caret's for as long as you keep typing and become the tree's the moment you stop. They
are also always the tree's at the very end and the very start of the text, where there is no
character to step over — so an empty box, which is most of this list's life, never waits.
To get the caret back without waiting, press any key that edits — a letter, backspace,
ctrl+w, or a paste — or ctrl+b/ctrl+f, which are ←/→'s understudies, are never the tree's, and
so move the caret without changing a letter of what you typed. Walking the list with
↑/↓ does not count as editing, so reading a filtered list never takes the arrows back
from the tree. tab always opens and closes whatever the box is doing.
With the providers open, enter on one of them pins it instead of switching model.
Its placeholder reads exactly filter by name · ctrl+r refresh — the keys are on the FOOT, because
a placeholder vanishes under the first typed character and the foot does not. The hint slot
follows the cursor: → hosts · alt+s sort · enter switch · ctrl+t effort · esc on a model, ← back · alt+s sort · enter choose · esc inside its
providers, and enter unpin · ← back · esc on the provider already pinned, where enter takes
the pin off — with tab providers and tab back in place of the arrows while you are
mid-typing and the arrow would step over a character instead. The foot always names whichever
of the two actually works at that moment. Typing while a fold is open filters that model's providers;
only a query none of them match falls through to filtering the model list.
Sessions roster — opened by /resume: the same key map, except enter opens the
selected session. Its placeholder reads filter · ↑↓ · enter open · esc cancel.
The empty screen's greeting: it takes only two keys, and only over an empty message
box — up/down walk the recent sessions listed under it and enter opens the selected
one. Every other key dismisses the greeting and then does whatever it normally does; the
first letter you type lands in the box, which is drawn inside the greeting until then.
Both pickers are modal: while one is up, every chord except ctrl+c belongs to it.
ctrl+c does not close the picker — it quits codeaf, with the picker still up.
An approval question: 1 allow once · 2 always — not drawn and inert when it
would do nothing · 3 deny · c answer in words · esc later, which folds the
question to the chip and answers nothing. It is not modal: every other key belongs
to your message box, and a key it does read also stops the countdown. A key pressed
in the first quarter-second is dropped, so a question landing under a moving hand is not
answered by a keystroke aimed at your sentence. ctrl+c is handed back to the message
box, where it quits codeaf. On the second
beat of "always" for a bash command, 1–9 pick a shape and esc goes back — and
while that beat is up the digits are the shapes', not the answers'.
alt+y raises the newest question you put off, from any page.
A task proposal is answered on that same block, in that same grammar: 1 start
it · 2 no · c answer in words · esc later, which folds it to the chip and
answers nothing. enter over an empty box takes the answer marked ▸ — the one
the clock is about to take — and enter with words in the box sends them as a
correction, which starts the corrected work. ctrl+e over an empty box opens the
brief in the conversation. Bare letters are ordinary answer text: typing no is a
correction and does NOT decline, because the answers are on the row with their
keys. Any key the question reads also stops the countdown, and deleting the draft
does not restart it.
An automation card is answered on that same block: 1 Save · 2 Save and run it now,
where it is offered · 0 Don't save · o Change… · esc later — see Keys on
/automations and on an automation's card below.
A slow provider's offer, raised on the status line when a provider you pinned has gone
quiet: the row reads coreweave is slow · switch to auto? (y) and y, over an empty
box only, fetches this answer from somewhere else. There is no key to decline — the
question takes itself down when an answer starts arriving — and with anything typed in the
box a y is a y.
A connect offer: 1 connect · 2 not now · esc later. It is on the question
block, so everything else falls through to the message box. A service connected by a
KEY has no 1 at all — a bare yes to one of those is read as a decline — and asks for
the key in the message box itself, masked: enter sends it, 2 is the way out, and
esc puts the question off with what you typed still in the box.
A harness offer: 1 run it · 2 not now · esc later. It is on the question
block, so everything else falls through to the message box rather than doing nothing, and a
digit is the offer's only over an empty box. enter and y no longer answer it, and esc
is no longer the no.
A finished harness design: 1 save it · 2 change it, which walks into the design's
own room · 3 drop it · esc later. The page in the conversation carries no answers of
its own, and the same three digits answer it from inside the design's room. It was enter,
e and esc on the card and ctrl+k/ctrl+x in the room; all five are gone.
The steer guard, raised when you press enter in a room whose node is not
listening: r revive and send · m send to main · esc cancel and keep your words ·
ctrl+c handed back to the door, where it quits · everything else does nothing.
Its row reads
[r] revive and send · [m] send to main · [esc] cancel. When the task is still
running — refused mid-check, or while its work lands — the guard offers no r: its row
reads [m] send to main · [esc] cancel, because reviving live work would duplicate it.
Conversations view (alt+v and /wall open the saved library, ▦ All on the strip or
under the box opens the tabs in the view you are in; the keys below are the same either way): the
arrows move the focus · enter opens · space picks · x closes a view (the work keeps
running) · m its teams · s a new team · e the shown team's settings · tab or 1 to
9 show a team · / filters · ? lists every key, and each of its rows is a button. The
team switcher under the strip's team chip takes ↑ ↓ enter esc. The whole map is on
the Conversations and teams page.
Teams page (alt+2, /teams): the overview owns its keyboard. Member cards and
interaction links lead into Chats. pgup / pgdown scroll the interaction table.
On its controls: ↑ ↓ walk, ← → cross between the rail and the pane, enter presses,
s the team's card · c close · w open on the conversations view · n new team (inside
the chosen team) · o Organize · m Move into… another team · space pick a team for a
move of several · p the members card · M a manager · r reopen · d delete a closed
team · u Undo a close or a move · esc cancels a drag or a move's question. In the Move
into… picker, typing filters, ↑ ↓ walk, enter moves, esc cancels. The whole map is on
the teams page page.
Settings panel (ctrl+,): esc backs out one layer at a time — search, then an
open account or device rename, then the panel · left/shift+tab and right/tab change tab ·
up/ctrl+p, down/ctrl+n, pgup, pgdown, home, end walk · enter activates,
and space activates too — except while a search is on, when it goes into the box, so
a phrase like shell command can be written · on the Devices tab without a search, n renames this device and r removes the selected computer · backspace, ctrl+u, ctrl+w edit the
search · anything else types into it.
Sessions place (ctrl+., or /history, or the one dim door line at the bottom of the task
column, ctrl+. earlier): esc closes it (or clears the filter first, if one is being typed),
and ctrl+. closes it either way · up/ctrl+p, down/ctrl+n move, stepping
over the running and completed section words · pgup/pgdown move twelve · home/end
first and last · enter opens the row · backspace, ctrl+w and ctrl+u edit the filter
· 1 and 2 answer the row under the cursor, but only while the pane beside the list is
drawing those two answers for it — a frame at least 110 columns wide, over a task of this
conversation's that is waiting on you; everywhere else a digit is typed · every other
printable key, the space included, types into the filter, which narrows
every section at once and is drawn on the control row at the top of the list, as
⌕ port. alt+s sorts by the next column — age, name, state, files, cost — and
alt+shift+s turns the column you are on round; a press on one of the two column labels at
the right of that control row sorts by it. → opens the row's
verbs, and this place has one: s stop it, over a task this conversation is holding that is
still queued or running. Its foot is assembled from what is true of the row under the
cursor — enter open its room · → verbs: stop it · alt+s sort · type to filter over a task this
window is running, enter go inside it on a task another conversation ran, which has no room to open,
and one of three clauses on a task another codeaf window is running:
enter go to that conversation when this terminal is holding that conversation, which
switches to it standing in that task's room; enter read it as it runs when the engine is
running it and this window can join, which opens that task's own live transcript, read-only;
and enter where it is running when nothing here can reach it, which opens the card that
says which window has it — and
esc clear the filter in place of type to filter while you are typing one — the one fact
the control row itself cannot show is that esc now means the filter and not the page. On a machine that has run nothing at all,
where the body is teaching what tasks are, the foot drops to tab next place · esc alone:
there is no row to filter, none to open and no verb to press. Clicking a row moves the cursor
to it and the pane follows; clicking the same row again opens it. On a frame too narrow for
the pane there is nothing for a first click to show, so one click opens as it always did. A
click on one of the pane's own answers presses that answer and opens nothing. The wheel
walks the cursor. The tasks pages describe what is on it.
Inside an old task's card (enter on a completed task's row): esc or ← backs out to the
list · ctrl+. closes the whole page · ↑/↓ (also k/j) scroll · pgup/pgdown and
space move a screenful · home/end the ends · m puts that task's name in your
message box and closes the page. Its foot reads
m puts it in your message · ↑↓ scroll; esc back is in the head's right corner instead,
said once. Clicking its head row or its foot goes
back to the list; its body is read.
Inside the card over another window's task (enter on a row noted another window):
the same keys, minus the mention. Its foot reads ↑↓ scroll — with esc back beside it
where the head has no room for its corner — and m does nothing: nothing has landed for a
@ name to point at. esc or ← backs out to the list.
Rewind timeline (/rewind, or tab from inside the quick esc esc mode): esc
clears the search first and closes the page second · up/ctrl+p, down/ctrl+n move ·
pgup/pgdown move twelve · home/end the oldest point and the newest · enter
places the pick, and enter again on that same point does the rewind · backspace,
ctrl+u and ctrl+w edit the search · every other printable key types into the search,
which reaches your messages, the model's replies and the tool calls' arguments. Its head
reads ⟲ rewind — pick where the conversation goes back to and its foot
⟲ drops 2 turns — everything below the pick is let go above
esc close · ↑↓ move · enter picks the point — which becomes
esc close · ↑↓ move · enter again rewinds here once a pick is placed, and
esc clear the search · ↑↓ move · enter picks the point while you are typing one.
Clicking a row places the pick; clicking the placed point rewinds; the wheel walks the
cursor. The sessions and rewind page describes what the cut does.
Inside the quick rewind mode (esc esc): ↑/↓ walk turns · ←/→ step inside
one · enter cuts · tab lifts you onto the rewind timeline with the cut you had
chosen · esc leaves with nothing changed.
Connections panel: esc · up/ctrl+p · down/ctrl+n · pgup · pgdown ·
enter. Its filter placeholder reads filter · ↑↓ · enter connect · esc close.
Harness panel: esc · up/ctrl+p · down/ctrl+n · pgup · pgdown ·
enter.
Thinking ladder (/effort, /thinking, /think): esc closes ·
up/ctrl+p, down/ctrl+n walk the six rows — auto and the five rungs · alt+e
moves down one · enter applies the row under the cursor. Clicking a row applies it;
clicking either of the two sentences around them does nothing. Its foot reads
↑↓ · enter apply · esc · alt+e next rung. Pressing the effort word after the colon above the message box
does not open this list — it walks the rung one step.
Permissions panel: esc — which drops an armed confirmation first, then closes ·
up/ctrl+p · down/ctrl+n · pgup · pgdown · enter or d to drop the
line under the cursor. Press it twice; the first press arms it.
Status deck sheet (narrow terminals): esc or q close · up/k, down/j
move · enter activate. Its foot reads esc close · ↑↓ move.
Phone tool detail sheet: esc, q, left and enter all close · up/k,
down/j scroll · pgup/ctrl+b, pgdown/ctrl+f/space page · home/g top ·
end/G bottom · ctrl+o lifts the line cap. Its foot reads exactly
esc close · ↑↓ scroll, or esc close · ↑↓ scroll · tap … for the rest.
Crew panel (/crew): up/k/ctrl+p, down/j/ctrl+n, home, end walk the six
rows · enter changes the row · esc goes back one level and closes · ? lists every key ·
z undoes the last change while the bottom edge offers z undo. On the models row
←/→ step all, open, price, custom, and enter steps forward like → until
price or custom, where it opens the ceilings or the checklist. On the providers row ←/→ walk the
chips and space turns the one under the cursor off or on — the foot reads
enter change · space toggle · esc close · ? keys there and nowhere else — and space on
the + opens /connect, whose esc comes back to this row; enter opens the providers list, whose foot reads
space or enter toggle · esc back. On the cap row a digit starts the figure. A click on
a row is enter, a click on a provider chip toggles it, and the wheel walks. The models
page has every key (Crew panel keys).
All of these are modal: while one is up, every chord except ctrl+c belongs to it.
ctrl+c does not close the panel — it quits codeaf, with the panel still up.
tab, pressed in a conversation with an empty message box, goes to the conversation you
were in before this one. Press it again and you are back. It is cd -.
It does nothing at all when there is nowhere to go: one conversation open, or none this
terminal has been in before. The bottom row no longer advertises tab, but the key still
works. alt+k opens the card of conversations whenever there is another to switch to —
see Switch between open conversations.
It works while either conversation is running, over every door: the one you leave keeps streaming into its own transcript and is all there when you come back.
On a place, tab is the next place instead. Home, AI teams, activity, memory,
automations, spend and settings form the circle it walks; shift+tab walks it back. That is
the same key doing the same kind of thing — going to the next thing of the kind you are
looking at — and it is the only meaning tab has while a place is up. See Places.
Everything else that wants tab gets it first, and that is the whole rule rather than a
claim that tab is free. In order: a paste bracket makes it a literal tab; the task roster
eats it while it holds the keyboard (esc gives the keyboard back first); a box that has
taken the whole keyboard on a place keeps it — the errand pane on home, the value being
edited in settings; the rewind timeline and the inline rewind lift with it; and path
completion takes it over /attach or /export . Then, on a place, it is the next place.
Only in a conversation, with none of those claiming it and the box empty, is it the way back.
Two claims on tab were withdrawn when the places arrived, and both moved to a key that
points the way they go: the settings panel changed its own section with tab, and now
uses ← and → alone; the memory panel changed shelf with tab, and the Memories tab
that replaced it has no shelf key at all — → opens a group and ← folds it, as on the
Activity tab.
The welcome box is the one exception worth naming: tab does not dismiss it. Every
other key does — that is the box's contract — but switching away is the opposite of
starting work here, so the box is still standing when you come back.
Switch to another conversation without going home — alt+k, the conversation switcher, switch between my open chats, alt tab between conversations, ctrl+k does not switch any more
Press alt+k to choose a conversation without leaving the one you are reading.
On a Mac that is opt+k. It was ctrl+k until this build, and that chord is now an edit
in the message box — delete to the end of the line — so the switcher moved one modifier
across. If opt+k types a ˚ into your message instead of opening the card, your terminal
is composing accents with Option: see Why option+left or cmd+left does nothing above,
which is the same setting.
Press it again, use the arrows, or scroll to move the highlight. The list stays open
while you read its names; a pause never switches chats or dismisses the list.
Moving the pointer over a row highlights that row without changing the keyboard
selection or switching chats. A small dot marks the pointer even without color.
The “more conversations” control also highlights; headings and borders do not.
An outside click dismisses the card without activating anything behind it.
Enter opens the highlighted row; clicking a row opens that conversation. Escape or
a click outside the card cancels. A click outside the list acts on nothing.
This holds even when the quick switch setting is on. That setting applies only to
ctrl+tab on terminals that can send it. Ordinary terminals do not report modifier-key
releases, so opening a chat waits for an explicit choice rather than guessing when you
released Ctrl.
╭──────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ open 3 of 12 · enter open · esc cancel · ↑↓ choose · ctrl+w close tab │
│ │
│> 1 ? harness dry run on one publi… asking you something codeaf 4m │
│ 2 ◐ openrouter price scrape 2 tasks running research 1d │
│ 3 ○ Refactor the rail scope model you are here codeaf 40m │
│ → show closed │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────╯
It works from the first session: on a fresh launch you hold one conversation, the card has that one row, and the fold has the rest of the machine in it.
Everything else on the machine is behind the fold at the foot — → show closed opens
it and ← hide closed puts it away again. Most rows down there are conversations this terminal is not
holding at all; the exception is one whose tab you closed, which is still held and still
running and is behind the fold because it is not on the row any more. Taking either kind
puts it in front of you and gives it a tab, exactly as enter on home does, and the one
you are in keeps running — over the ordinary engine socket, over --host, over --at and
under codeaf chat --no-host alike. Each conversation holds its own connection, so
opening a second, third or fourth closes nothing and cancels nothing.
The tabs above a conversation are the same journey with a mouse. The conversations
this window has been in are drawn there, the one you are in bright and underlined;
clicking one switches to it. The Chats ▾ control that used to sit at the row's right
end is deleted — the legend under the box names this key instead — and what is left there
is a dim +3 counting the tabs the row could not spell, which does nothing when pressed. The
× on a tab dismisses its view, and ctrl+w is that × on
the tab you are in. See Conversation tabs and
Closing a tab on the screen page.
| Key | What it does |
|---|---|
alt+k (opt+k) |
Open the list; each further press moves the highlight without switching |
ctrl+tab |
Switch immediately when quick switch is on, otherwise browse; requires a terminal that sends it |
alt+shift+k (opt+K) |
Walk the list backwards. Every terminal sends it, unlike the ctrl+shift+tab beside it, which needs a terminal that can spell the alias |
tab / ↓ |
Down one — cursor only, without switching, and the card stops fading |
shift+tab / ↑ |
Up one. Both wrap round at the ends |
1…9 |
On the holding card, go to that row outright — the number is drawn on the rows that have one. While the card is fading, digits are typing and land in your message |
enter / click a row |
Open that conversation |
→ |
Open the fold — every other conversation on this machine |
← |
Fold them away again |
ctrl+w |
Close the tab of the conversation under the cursor. A row that is not the conversation you are in closes at once with no question, says tab closed · <the conversation's name>, and leaves the list for the fold — so a second press closes the next tab. Only the conversation you are in raises keep running / stop work / cancel when it is working. A row below the fold has no tab to close, so nothing happens and nothing is said. See Closing a tab on the screen page |
esc |
Take it all back: the card goes and you are in the conversation you started from, however many presses ago that was |
| any other key | While the card is fading, it is typing — the card goes and the key lands in your message. On the holding card it puts the card away and is swallowed |
It works in a conversation and on every place — home, activity, memory, spend, settings, automations — because it is drawn over the screen rather than being a screen of its own. Nothing under it moves by a cell while the card is up.
Taking a row from a place leaves that place. The place comes down and you are looking
at the conversation you chose — the same arrival home's own enter on a conversation row
makes, and the same one ctrl+tab makes where quick switch is on. That holds for a row
below the fold too, which is opened beside the others and then walked into. The one case
that does not move you is a door that refuses: the place stays up with the refusal on its
own line, so you can read it.
It does nothing on a machine with one conversation on it — a first run, and nothing
else — and says so by not being there: no card, and the keys row under the box does not
name it. Everywhere else that row reads alt+e effort · alt+a approvals · alt+k chats · / commands · space space home — opt in place of alt on a Mac. Effort and approvals appear only when the session
has those controls. As the frame narrows, controls give way from the left, keeping
/ commands · space space home, then / commands on its own.
Taking a row is never refused for having too many open. The card draws the first twelve rows and hands a digit to the first nine; past that the cursor is the way, and home is the page that shows every conversation you have. Past twelve open, a quiet conversation left alone for fifteen minutes may be let go of — see How many conversations can one terminal hold on the home page. Work, a question, a draft or news you have not seen keeps it.
Six columns — number, glyph, name, one clause, project, clock — which is what makes it
scan: the names all start in the same cell, so you read straight down them. 3 of 12 is
how many tabs are on the row above, of how many conversations this machine has.
The name column takes whatever room the frame has spare, up to about sixty cells, so a
wide terminal shows long names whole instead of spending the width on nothing new. It
keeps two cells of air before the clause, so a name that fills its column never runs into
the words beside it. A narrow frame gives up the project first, then shortens the clause,
then drops the clock, then the clause — the name is the last thing cut, and it is cut with
a … rather than repeated anywhere else.
The foot is one instruction: → show closed, and ← hide closed once the fold is
open. It is there only while there is something behind the fold.
With the fold open, one dim closed marks where the tabs stop. Everything above that
word has a tab on the row; everything below it does not. It is spaced the way the head's
open is — a blank line above it and a blank below, so it reads as a heading over the rows
under it. On a card too short for that it gives up the blanks, then the word itself: the
rows you asked for are what a short card spends its lines on.
On a terminal of about twenty rows or fewer, → can change only the foot. The card
gets a handful of lines there, and if every one of them is already a tab then the closed
rows are below the scroll line rather than missing — the head's 3 of 7 still counts them,
and ↓ walks down to them. A taller frame draws them straight away. It is not a row: the cursor
skips it and a click on it does nothing. It is not drawn at all while the list is only
tabs.
The ✕ on a row does not mean the tab is closed. It means that row will not open, and
it is always beside the reason: open in another window, or that folder is gone. A
conversation whose tab you closed keeps its ordinary mark — ○, or ◐ if work is turning
in it — because nothing about it is wrong: it is still held and still running, and it is
below the closed word only because it is not on the tab row any more.
A long name is never read out twice. The card used to re-wrap the highlighted row's name in a block under the list; it does not any more. The name gets the room on its own row instead.
Why the switcher lists conversations in that order — tab order, where the cursor starts, a closed tab
The rows are in the same order as the tabs above them — the leftmost tab is the first
row, so 2 on the card is the second tab on the row. The strip's order is the order you
first entered each conversation and it never re-sorts itself, which means the card does not
either: a conversation stays at the number you last saw it at. The one you are in wears
you are here wherever its tab is, rather than being drawn at the end.
The cursor still opens on the conversation tab would go to — the one you were in
before this one — which is usually not the first row. That is what keeps the common
journey two keys: alt+k, enter, and you are back where you just were. ↑↓ from there
walk the list in the order you see it.
The list above the fold IS the tab row. Close a tab — with its ×, with ctrl+w in
the conversation, or with ctrl+w on this card — and the row leaves the list at the same
moment the tab leaves the strip. The conversation is not closed: it is still held, still
running, and behind the fold, where enter brings it and its tab back.
The cursor never opens on you are here, so alt+k enter always lands somewhere.
Every row says what changed since you last looked, not what the conversation is about:
| The row says | What happened |
|---|---|
asking you something |
it is waiting on you — a question or an approval |
working |
something is turning in it — a reply, a task node, or a background command — and it wears ◐ |
3 tasks running |
that many pieces of work are turning in it right now |
it finished while you were away |
a turn ended in there after you left |
nothing new |
it has been quiet since you left it |
you are here |
the conversation you are sitting in |
open in another window |
another terminal is holding it; enter will refuse |
that folder is gone |
the project directory it worked in is not there any more |
| (nothing) | quiet — nothing has happened in it since you left |
Then the project it is in and how long ago you left it. That is what makes the switcher double as the catch-up: after twenty minutes in one chat, one key says what the other seven did.
The mark on each row is the mark on that conversation's tab, taken from the same reading.
That includes you are here: work in the conversation in front wears ◐, and a question
there wears ?, without replacing the note that says where you are.
The marks, and the one that is not about tabs: ○ quiet, ◐ working, ? waiting on
you, and ✕ this row will not open — which is the last two clauses in the table above
and nothing to do with a tab being closed. The ✕ a person presses on a tab closes it; the
✕ on a card row is the card refusing, and the clause beside it always says why. A
conversation whose tab you closed is ○ like any other, because nothing is wrong with it.
The card is frozen the moment it opens. A conversation that finishes a turn while you are looking at the card does not re-rank the list under your finger.
The quick switch setting controls ctrl+tab, where the terminal can send that
chord. It is on by default. Each press switches immediately and shows a brief receipt;
Escape returns to where the burst started. Typing dismisses the receipt and goes into
the new chat. An arrow turns the receipt into a list that waits for Enter.
Turn it off under /settings, interface, quick switch to make ctrl+tab browse too.
alt+k always browses, regardless of this setting. It never moves the underlying
chat until Enter, a numbered shortcut, or a row click chooses one. Key releases are not
available consistently across terminals, so there is no release-to-commit behavior.
The switcher was ctrl+k and is now alt+k, because ctrl+k is kill-to-the-end-of-the-line
and the message box wanted it back. ctrl+u deletes to the start of the line here and
always has; ctrl+k is the other half of that pair in every shell on your machine, and it
could not be that while it opened a card. Every other ctrl+<letter> on this surface is
already spent, so the switcher moved rather than the edit.
alt+k arrives as escape-then-k, which every terminal sends with no setting to turn on
and no protocol to negotiate — except on a Mac, where Option composes accents unless
you tell the terminal otherwise and opt+k types ˚. That is the one thing this chord costs
that the old one did not, and it is the same setting every other alt+ chord in codeaf
needs: Why option+left or cmd+left does nothing has the menu path for your terminal.
Nothing else wants alt+k — no window manager, no emulator, and no other key in codeaf.
ctrl+tab is bound — but only on terminals that can send it, and many cannot.
ctrl+tab has no distinct encoding in an ordinary terminal: it arrives as a plain tab and
is indistinguishable from it. Only a terminal that speaks the kitty keyboard protocol sends
it as itself, and codeaf asks yours on every frame — where the answer is yes, ctrl+tab
opens the switcher and ctrl+shift+tab walks it back. Where the answer is no, the chord is
not bound and is never named, because a key codeaf tells you about is a key that works.
Two popular terminals — WezTerm and Windows Terminal — also spend ctrl+tab on their own
tabs by default, so it would never reach codeaf there.
alt+tab is not available at any price: the window manager takes it on Windows and on most
Linux desktops.
alt+shift+k is the reverse, and the move made it better. It arrives as
escape-then-K, a different byte from escape-then-k, so every terminal can send it.
The old ctrl+shift+k could not be told apart from ctrl+k and was bound only where the
terminal answered the keyboard query, which was about half of them. With the card down,
alt+shift+k opens the ring at its far end — the last row, which is the rightmost tab —
and waits for your choice. shift+tab still walks the card back once it is up, on
every terminal, and that is the key most hands reach for.
ctrl+w closes the tab under the cursor, which is what the card's own legend
says: enter open · esc cancel · ↑↓ choose · ctrl+w close tab. An inactive row leaves
the card open with tab closed · <title> and drops off the list, behind the fold with
everything else this window is not showing — so pressing it again closes the next tab
rather than the same one, and several go in a row. Its conversation, its work and its
draft are untouched; → reaches it and enter brings it back.
Tab closing does not archive the saved conversation. Home shows up to three
recently closed tabs as dimmed rows, and enter on one reopens it. Home has no archive key
any more: ctrl+e, or → then x delete, on a conversation's row asks to delete it
permanently, and nothing goes until you confirm.
On the row marked you are here, the card closes and the window selects the most
recently used remaining tab, or Home when none remain. This is the same action as
ctrl+w in the conversation and the tab's ×, and it asks the same question where that
conversation is working. The work keeps running over every door; selecting another tab
ends nothing. Closing the final tab opens Home, leaving that conversation behind it.
ctrl+w on a row below the fold does nothing, and says nothing. There is no tab down
there to close — whether this terminal never opened that conversation, or you closed its
tab a moment ago and this is where it went — so the key has already got what it was pressed
for. Nothing is dismissed, nothing joins the ctrl+shift+t stack, and the card
stays exactly as it was.
What actually ends things: Stop on a task's page ends that work, and /quit closes
the conversation in front — leaving codeaf when it was the last one this terminal held.
ctrl+w is never about making room — it is about what you want on the row. A quiet
conversation left alone can be let go of on its own; this key only takes its tab down.
tab on an empty message box has not changed: it goes to the conversation you were in
before this one, in one key, with no card. Use tab to flick between two and alt+k when
there are more.
The legend above the box names every door that would act: tab last appears once a
second conversation is open, alt+k chats whenever there is anywhere at all to go. On a
place the switcher is named on the map (alt+.) instead, because a place's foot is four
fixed clauses the design sets word for word.
On macOS every alt+ below is drawn opt+ — alt+enter is opt+enter, alt+p is
opt+p, alt+o is opt+o. Same key, same chord, named the way the keycap names it.
alt+enter with something typed into home's box opens the composer layer: the page
behind dims, the box stays where it is, and the three facts a task needs appear under it.
It opens from home alone — only home starts things. The places page has the layer in full;
these are its keys.
| Key | What it does |
|---|---|
alt+enter |
first press opens the layer; second press sends the task off |
alt+p |
move the task to the next project codeaf knows, and round again |
alt+o |
open the model list for the execution slot — what the work runs on |
a digit, or . |
type the spend cap; the figure changes as you type |
backspace |
take one character off the cap |
enter |
talk about it instead — an ordinary conversation carrying the same sentence |
esc |
back to the place you were on, sentence still in the box |
alt+o changes a model only inside this task layer. On home, /model or a press on
the model name opens the draft's model list instead. alt+p also works on home to move
the draft to the next project; other places bind neither chord.
While the layer is up it has the whole keyboard: tab does not walk to the next place, and
letters do not reach the composer — what you typed is already written and is on the screen
above you. Inside the model list alt+o opens, the keys are the model picker's own — type
to filter, ↑↓ to walk, enter to use it, esc to go back to the layer.
Press the space bar twice with an empty message box. That is the way back to home from
inside a conversation, and /home opens it too.
There is also a number: alt+1 (opt+1 on a Mac). Home is the first of the eight words on
the top line, home chats AI teams activity memories automations spend settings, and
alt+1 opens it. The other numbers do not follow the bar's order — they kept the
destinations they had before the bar was regrouped: alt+2 AI teams, alt+3 chats,
alt+4 activity, alt+5 spend, alt+6 settings, alt+7 automations, alt+8 memories. So
memories is the fifth word on the bar and alt+8, and automations, the sixth, is alt+7.
alt+. draws every place with its number, and /help lists them the same way, digit by
digit. alt+3 is chats, the way back to the conversation in front (a new chat when none
is open); it is not a room, so tab steps over it. Hold
alt and press the digit. On macOS codeaf draws the modifier as opt+, after the name on
that keycap; it is the same key and the same chord, and on Linux and on Windows it is drawn
alt+. It arrives in every terminal codeaf runs in, which is why the numbers are on alt
rather than on ctrl.
ctrl+1 … ctrl+8 are a second spelling, on the terminals that can send them. ctrl
and a digit has no encoding in the forty-year-old scheme most terminals speak, so it is not
the first spelling and never will be — but a terminal running the kitty keyboard protocol
sends exactly the keys that scheme cannot spell, and it tells codeaf it does. Where that
report arrives, ctrl+1 … ctrl+8 jump to the same eight destinations; ctrl+. draws the same
map, and the map's own line says alt+1…8 or ctrl+1…8 go to a place so you can see it is
live. Where it does not, those chords do nothing and are never advertised. kitty, ghostty,
WezTerm, foot and Windows Terminal are the usual ones that report it. On a Mac this is the
way in that needs no setting at all — see "Why my option key types ¡ ™ £ instead of
jumping" on the screen page.
The numbers work from a conversation as well as from a place. They are the one class of
place key that does: tab belongs to the composer's path completion while you are typing,
and the rest of the place grammar — → for the row's verbs, alt+<letter> for how a place
is shown, shift+←→↑↓ for its time window — is about the room you are standing in. Every
number opens its room whatever is in it: a place with nothing of its own to draw spends the
frame saying what it is for, and none of the eight is ever a key that does nothing. On
such a page the line under the box names only the way out — tab next place · esc — on
activity, on automations and on memory alike: a foot that offered enter or type to filter over a body with no rows would be naming a key with nothing to act on.
There is no ctrl+<letter> chord for home: every one this surface could use is already
taken, and ctrl+. is the activity page (/history) from a conversation — while a place is
up that same ctrl+. draws the map, on the terminals that can send it, because a place
takes the whole frame and never reaches the conversation's keys. esc was not available either: on an idle conversation it
already arms rewind and already clears messages waiting from the turn, and a third
meaning on one key in that state is how a surface stops being predictable.
The first space types itself. The second one, finding a box that still shows nothing
with that space behind the cursor, takes the whole draft away and opens home — so a leading
space you actually wanted is never eaten (space then x leaves x). It does nothing when
the box has words in it, and it is not a paste: text pasted with two leading spaces is two
spaces. A machine with one conversation, or none, opens an empty home; so does a session
over --host, where what opens is the far machine's home.
It answers from every place as well as from a conversation. Wherever a place is
up, the two spaces are read against that place's own filter — activity and memory —
and open home just as they do from a draft; on spend and automations, which have nothing to
type into, two bare spaces open it and any key between them disarms it. On home itself the
door is a no-op: the page is already open, and two spaces type into home's own filter. It also does not answer from under a layer that owns the
keyboard: on the settings panel space is the drawn verb on a row (activate),
memory's line editor keeps every key while it is open, and inside a task's
record — the room the roster opens on enter — space pages the card the way
pgdown and ctrl+f do, so the door yields there and the key scrolls.
A box that looks empty and is not still answers it. Blank lines left by ctrl+j,
alt+enter, or a modified-enter chord delivered as ctrl+j by some terminals,
draw nothing on the frame — and the gesture reads the box the same way the frame does, so
two spaces open home and the blank lines go with the draft. The rule in one sentence:
wherever the foot advertises space space home, two spaces open it.
It works while a turn is running; the answer keeps streaming underneath and esc puts you
back in it.
When the box is empty, the keys row under the box says so:
/ commands · space space home, after any effort, approvals and chats hints. Clicking
space space home opens home; that clause vanishes as soon as you type. A tip never takes
its place: a conversation's tip covers the project at the row's right end instead.
The door does not ask what the machine holds. It is open on a machine with only this
conversation and on one with none, from the first minute, and starting a second
conversation with /new changes nothing about it. It used to be shut until the launch
found somewhere else to go, and that rule is gone (the home page, space space does
nothing).
Once it is open, home is seven panels in one, two or three columns (the home page has what each holds), and its keys are a small grammar:
| Key | On home |
|---|---|
↑ / ↓ (ctrl+p / ctrl+n) |
walk the field, from one panel into the next, and stop at both ends: ↑ off the top row stays there and does not climb onto the tab bar (reach the bar with a click, tab, or a place's chord) |
← / → |
never cross to another column: → opens the row's verbs on a strip under it and ← closes them, and on an ask here row → hands its pane the keyboard. With something typed they move the caret |
| a digit, or a question's own key | answers the one row of needs you that is drawing its answers, from anywhere on home, with no cursor move — the row under the cursor when it can take one, the top answerable row otherwise. A question's chips are 1 allow once 2 always 3 deny; a landing in unread offers 1 accept 2 not right, its own [a]/[n] being letters and letters always type on home |
enter |
acts on the row under the cursor: a conversation opens, a since you left line opens its record, file or place, an automations row opens /automations, a fold line opens or shuts its panel. projects and spend rows are not stops, so the cursor never reaches them; a click on a project picks the folder for the next message |
pgup / pgdown |
jump a screenful |
tab |
the next place on the bar |
alt+enter |
with something typed: opens the composer layer, and a second press sends it off as a task (Keys in the composer layer). It is not a spelling of ask here |
ctrl+enter |
with something typed: asks it here at once, without walking to the ask here row — only on a terminal that can tell it from a plain enter, so the foot names ↑ ask here instead |
esc |
clears the box if anything is in it, and closes home otherwise |
alt+. |
the map |
backspace, ctrl+u, ctrl+w, ctrl+b, ctrl+f |
edit the box |
| anything else | goes into the box, which searches the whole machine and offers to start a new conversation at the same time |
Opening home puts the cursor on the chat you were in before this one, so space space
then enter is a switch back; a window with only one conversation opens on its own row,
which says here. The panel holding the cursor marks its heading with the cursor's ground,
which is how you tell which panel your arrows are in. alt+g and alt+q are unbound on
home — there is no list left to group or thin — and so is the answer strip above the box:
the answers are on the needs you row itself.
← and → never cross home's columns. Every row the cursor can stand on is in the
field at the left; the rail at the right — projects, spend and the quiet panels — is
read and clicked, never walked, so there is no column to cross to. (The arrows used to
cross into the neighbouring column; that ended on 2026-09-17.) → opens the row's
verbs on a strip drawn directly under that row, pushing the rest of the list down by
its own height, and while that strip is drawn its letters are the verbs and the box is
asleep — a question's own answer keys and words; x delete, c copy name,
n new in project and o open folder on a conversation; s stop it on work this window
is running. An automations row has no verbs on home; its verbs are on /automations.
esc or ← closes it, enter still opens the row, and walking off the row closes it too.
A row with no verbs draws no strip: on an ask here row → hands the errand's pane the
keyboard instead, and on a panel's fold line (N more) enter opens the panel and shows
the rest; on N fewer it folds them again. While something is typed the two arrows move
the caret in the box instead.
A letter always types, unless the verb strip that names it is on screen — that visible
strip is the one state where a printable key is a verb, and it is why it has to be drawn.
Everywhere else "make me a site" comes out whole wherever the cursor is resting. The row's
actions otherwise ride chords, which can never begin a word, and they work from any column:
ctrl+e asks to delete the conversation — the same confirmation → then x delete
opens, and nothing is deleted until you confirm. Up to three closed tabs stay dimmed on
Home; typing finds older ones, and enter on a dimmed row reopens it. ctrl+o opens
its folder and ctrl+y copies its path. ctrl+t on a conversation's row
starts a new one in that row's folder; a click on a row of the projects panel instead
picks the folder for the next message sent from home. ctrl+x on a tasks row this
window holds asks to stop that task. An automations row takes enter, which opens
/automations — where → then p pauses one and d deletes it — and ctrl+o, which
opens the folder it runs in; nothing else. Each chord acts on the row under
your pointer when there is one, the cursor's row otherwise. alt+e on home walks the
thinking rung of the next conversation, never a row's; the machine's own default is the
thinking row of /settings (alt+e — how hard the thing you are looking at thinks).
With the mouse: a click puts the cursor on a row and a second click on that row opens it. The wheel walks the list three rows a turn, and the places on the top line are controls: clicking a place's word, or the cell either side of it, goes there, and clicking the air around the words does nothing.
Under 60 columns those two clicks are one. At phone width home is an inbox and a
row's card is a full-frame sheet, so a tap selects and opens in one gesture; the sheet's
top row reads ‹ back and esc or a tap on it returns to the list with the cursor where
it was. The hint line becomes a bar of at most three wide targets — open · new · ask here, or ‹ back · open · more on a sheet — and mouse motion is ignored, because
there is no hover on glass. Home's own page has the whole shape.
The box row reads › type to search or start something new — the promise itself, both
readings of what you type: a search of everything home shows, or the first message of a
new conversation. (Until 2026-09-17 the box said › say what you want done and the
promise opened the foot.) The rule above it is a legend on home and nowhere else, and
it says what the box is a draft for:
─ glm-5.3-flash:auto · ◇ asks ───
— the model, a colon and effort, then approvals at the left. The project the next
conversation opens in is project: <path> at the right end of the keys row under the box
(it stood at the rule's right until 2026-09-22). The arrow and effort badge are gone. A long
project path keeps its root and truncates on the right. The chords that change them are on the line
under the box, with home's own keys, because the lowest line is for keys on home as in a
conversation: alt+p project walks all projects in the projects panel's order, alt+e effort
cycles auto → low → medium → high → xhigh → max → auto, and alt+a approvals
walks asks → guardian → YOLO → asks. Pressing the project or approvals cell does the
same. The project choice survives starting a conversation and returning home for the
lifetime of this window; the seam alone shows it, with no footer announcement.
/model, or pressing the model name, opens home's model list; alt+o no longer
does. The model is always bold and bright cyan on both home's and a conversation's seam.
alt+k chats opens the conversation switcher and is absent when there is nowhere to go.
On a Mac these clauses read opt+p project · opt+e effort · opt+a approvals · opt+k chats.
opt+y raises the newest pending question, and opt+w toggles the folder browser
preview. These exchanged roles with approvals and projects respectively.
With all controls available the resting foot is
alt+p project · alt+e effort · alt+a approvals · alt+k chats · / commands.
The project and approvals hints are absent where those controls cannot act. ctrl+o
still opens the selected row's folder and tab still moves to the next place, but neither
has a hint in home's bottom row. esc still closes home; alt+. draws the whole map.
Every ordinary grid row keeps the same list keys as the cursor walks. A fold or action
row names its own keys, with the available draft controls before esc. For example:
enter starts a new conversation and sends this · ↑ ask here · ↑↑ pick a match · alt+p project · alt+e effort · alt+a approvals · esc clear.
Typing a slash command changes the first clause to enter runs this command.
With nothing typed home is the panels, hanging from the top. While anything is typed
it is one list, a drop-up: the action row — start a new conversation: "…" — is the LAST
row of the list, with ask here: "…" directly above it, both directly above the box, and
the matches rise above the pair best one first; the cursor starts on the action row, so
one ↑ reaches ask here and a second lands on the strongest match. Clearing the box puts
the panels back. On a frame 136 columns or wider a card about the match under the cursor
stands to the right of the list while you type; at rest there is no card.
Home is modal like the panels above: while it is up, every chord except ctrl+c belongs
to it. ctrl+c does not close home — it quits codeaf, with home still up.
On an ask here row — the ? row an errand leaves on home — the line under the box
reads ↑↓ move · enter or → answer this ask here · esc close, or
↑↓ move · enter answer this ask here · esc clear while something is typed, when → moves
the caret instead. enter or → hands the keyboard to the exchange's pane; tab on the
row is the next place, not a way in. The pane reads
enter sends a follow-up · tab or esc back to the list, and tab or esc there hands the
keyboard back. The way back is that line's last clause and the one it keeps at sixty
columns (Asking from home).
On every place, ↑ from the first row of the page lands the cursor on the tab bar,
the eight place words on the top line after the codeaf wordmark. The chat tab strip is
not on a place. It is the row under that top line only while a conversation is in front.
The word you are standing in wears the cursor's
band there instead of its usual mark, and five keys mean something on that row:
| Chord | What it does while the cursor is on the bar |
|---|---|
← / → |
walk one word along, wrapping round from either end. Nothing opens |
enter |
go into the place under the cursor |
↓ |
the same — go into the place under the cursor |
esc |
back into the page, on the row you walked up from. It does not close the place |
↑ |
nothing. The bar is on the top line, and there is nothing above it |
Everything else means exactly what it means everywhere else: tab and shift+tab are the
next and previous place, alt+1 … alt+8 jump, alt+. draws the map, and any printable
key goes into the composer — taking the cursor back down into the page with it, because
somebody who has started typing has stopped looking at the bar.
← and → are not the row's keys up here. On a row they open that row's verb strip
and its folds; the bar is not a row of any page's list, so both arrows are the walk along
the words and nothing else.
The first ↓ back off the bar lands where you left. ↑ onto the bar does not move the
page's own cursor, so walking up and straight back down costs nothing.
And it works only where a bar is drawn. Home on a phone-shaped frame and a task's record
card draw something else in those cells, so ↑ there is the walk it has always been — no
key does anything that is not on the screen.
The places page has the same thing with the pointer's half beside it: How do I move between the tabs with the arrow keys.
On the spend place (/spend, or alt+5) two things lead to the money limits, and neither
one is an editor on that page — the page answers what did it cost, and the Spending tab
of /settings is the one place what may it spend is set.
enteron the first line. The page's top line is a dim pointer —today $3.42 of $500 · /budget sets the limits— andenteron it opens the Spending tab.→thenb.→on any row of the page opens that row's verb strip, and this place's strip is one letter:b the limits. Pressingbwhile the strip is drawn opens the same tab.escor←closes the strip, and walking off the row closes it too.
b is a verb only while the strip naming it is on screen. That is the rule everywhere
on this surface: a printable key belongs to the message box unless a drawn strip has
claimed it, so there is no bare b anywhere else that means "the limits". The door that
works from wherever you are standing is the command — /budget, also /limits — and
that is the door a refused turn names, because your refused message is still in the box
and every letter you type there goes into it.
The third door on the same subject is the money segment of the status line: press
$0.14 and the Spending tab opens. It also brightens under the pointer, and takes the warm
ink once this conversation has spent four fifths of its own per conversation limit.
On /automations (alt+7, or the automations heading on home): ↑/↓
(ctrl+p/ctrl+n) walk the list and pgup/pgdown page it · enter opens a row's
history, where enter on a run opens that run's own transcript and esc goes back to the
list · esc on the list leaves the place · tab is the next place. → draws the row's
verbs, and their letters work only while that strip is drawn:
| Key | Verb |
|---|---|
r |
run now — check now on a watch, say it now on a reminder |
s |
stop the run |
p |
pause, or resume on a paused one |
e |
edit — leaves the place with its /automations edit … line in the message box |
d |
delete — d again deletes it and its history · esc keeps it |
o |
open where it was asked — the conversation that made it |
A verb that cannot work on the row is not drawn: s only while a run is in hand and r
only while none is, no pause on a finished one, no o with no conversation behind it. A
click on a row opens it; the wheel walks. Home's automations panel has none of these
keys — its rows open this place — and ctrl+e, ctrl+x and alt+e do nothing to one.
On an automation's card — wants to remind you, wants to schedule work,
wants to watch for something — 1 Save · 2 Save and run it now (2 Save and check it
now on a watch; no 2 on a reminder or on work kept in a separate worktree) · 0 Don't
save · o Change…, which asks for your correction in words · esc later, which puts the
card off until alt+y raises it. A card raised by a typed /automations add or edit —
save this automation?, save this change? — has only 1 and 0; a change I raise
has 1, 0 and o. In home's ask here pane the same keys answer it while the pane has
the keyboard, and esc there hands it back to the list.
Automations says what each verb and each answer does.
When Run tasks on names a computer or a proposal already names one,
ctrl+d computer on that proposal over an empty draft cycles this computer and eligible paired computers. Accepting sends the
choice shown by runs on <name>. When a remote task cannot reach its computer
or was interrupted, its card and page offer h run it here; click the card
action or press h over an empty draft to start fresh local work. The old
attempt is abandoned and its late result never lands. See Tasks on another
computer for limits and the exact recovery sentences.
Opening a task's room gives the keyboard to that room. For a task that accepts
notes, type one and press enter to send it; you do not need to press alt+t again first.
Use alt+t when you want to navigate the roster again.
While the task roster holds the keyboard (alt+t, opt+t): esc gives the keyboard
back · up/down move over the needs-you band, the group headings and the tasks ·
enter opens a task's room, opens or folds a group on its heading, and opens a band row's
thing · left/right switch the column between Tasks and Traffic in a chat in a team ·
x stops the task under the cursor · alt+w widens the column and narrows it again. Its
hint reads exactly ↑↓ move · enter open · x stop · alt+w wide · esc, led by
←→ tasks/traffic · in a chat in a team. On a row whose work is still running or still
queued the hint gains one more clause before esc, alt+e think harder, which moves that
task's thinking rung. On an ordinary finished task, it saves the rung for when you
continue; it does not restart work or rewrite the last attempt.
Widen is a chord and not the bare letter w. It used to be w, and w was read
before the message box: a sentence typed while the roster still held the keyboard came out
as riting the port and orktree. Every bare letter on this surface is either a key on a
modal page with no message box, or an answer to a question drawn on screen, pressed over an
empty box, and widening a column is neither, so it took a chord. The bare w still works
on the full-frame roster (alt+t under 100 columns, where the column is drawn over the
whole frame and there is no message box on screen). The column's own footer says
alt+w widen · click seam or alt+w narrow · click seam, and clicking the seam (the
column's two leftmost cells) does the same thing with the pointer. Both the offer and the
handle exist only from 120 columns up, which is the only frame that lends the wider
column; narrower than that those two cells belong to the row under them and open its task.
Folding is the headings' and nothing else's. Queued, Waiting and Done start folded
to one heading line; enter on the heading, or a press on it, opens the group, and it stays
open for the session. Running never folds. A task row has no fold of its own: what it
used to fold away (the merge word, the price, the branch) is on the hint line when the
pointer is on it. ← and → no longer fold anything; in a chat in a team they switch the
column's two words, and elsewhere they do nothing.
The walk stops at this conversation's last job, after its last task. The roster holds
this conversation's work, then the jobs section under it, so ↓ walks both and clamps at
the bottom rather than carrying on into the project's record. Old tasks from earlier
sessions are on the activity page, reached from the column's own ctrl+. earlier line, from
ctrl+. or from /history; enter on an earlier row there goes inside that task's
card. In a directory whose earlier sessions ran tasks but where this conversation has
run none and started no jobs, alt+t falls through: there is nothing on the column to
put a cursor on. A session that has only started a server still has the jobs section, so
alt+t takes it.
The column's other lines take no cursor. Its automations section, and the two +
rows (+ /task, + /automations), are the pointer's; the walk skips them. The jobs
section does take the cursor: the label, then every job row the column actually drew. The
keyboard equivalents of the pointer's lines are the commands themselves: /automations (or
alt+7) opens the automations place, and typing /task or /automations is exactly what
pressing its + row puts in the box. enter on the jobs label toggles the section;
enter on a job row opens that job's page.
Under 60 columns the TASKS PAGE this column reaches is a thumb's, not a keyboard's: the
column itself is unchanged, and its own keys are the ones above. The page's rows become
two-line cards a tap opens, its foot is a ‹ back bar in place of the key legend
enter open its room · alt+s sort · type to filter, and the strip that opens it is one full-width door
(▸ 3 tasks · 1 running) rather than a row of chips. Mouse motion is ignored: a tap opens
in one gesture. The tasks page describes the phone flow in full.
alt+l closes the column, and opens it again. It works from the message box, from
inside a room, and while the roster holds the keyboard: it is the one key here you do not
have to ask for the roster first to use. It is the same key in every chat, whichever word
is in front, and the column's header names it at its right (opt+l on a Mac). Closing it
hands the keyboard back to the box. The choice is written to your profile as
ui.task_column, so the next session opens the way you left it, and alt+t counts as
asking for the column back. ctrl+g does the same while no foreground command can be kept;
while one can, ctrl+g backgrounds the command instead.
A closed column leaves a two-column edge down the right of the frame with a ❮ in it,
drawn in ink, and clicking anywhere on that edge opens the column again. Clicking the
alt+l at the right of the column's header while it stands closes it, so the pointer can
go both ways. alt+l works with no tasks at all: the column stands with only its header,
its + /task door — and + /automations under it where this window can keep
automations — and the ctrl+. earlier door under them if earlier sessions ran anything,
and either way an empty column is still a column to close. Under 100
columns there is no column beside the conversation, and alt+l lays it over the body
instead. On the untouched empty screen there is no column yet; there the key is a first
keystroke like any other: the greeting goes and the key then closes the column it would
just have raised, so a second press brings it back (The empty screen page).
With a room open: esc leaves the room, though a history recall walk is
cancelled first · enter steers the node (see What steering a task looks like on its
page below) · ctrl+b freezes the room's own rows for
copying, not the conversation's · pgup/pgdown page · up/down walk your history,
and scroll the page one row only when there is no history to walk. left is
deliberately not taken here — it falls through to the message box's
back-navigation.
Inside a program's room — a task handed to senior-dev — ctrl+y turns the page
between the actions it took and its raw calls to its model, ctrl+o opens its whole brief
as the page (scroll it like the page) and closes it again, and the box sends nothing.
While it works the keys row under the box reads
/stop · x with empty input · esc main · ctrl+y calls, ending ctrl+y actions while the
calls are showing and ctrl+o close brief while the brief is; once it has ended the row
is that last clause alone.
up and down in a room mean what they mean in the message box, in the same order:
inside a multi-line message they move the caret; on the first line — or over an empty box
— they walk your own history, newest first; and only with nothing to walk do they scroll
the page. It is the same history the main conversation walks, and a line you steered
into a task goes into it, so ↑ brings back the last thing you said to the task and
you can edit it and send it again. A line the steer guard refused is not remembered. On
an adaptive run's page ↑/↓ over an empty box walk the graph's chips and the fuel
gate's answers instead; type something and they walk the history from there.
What steering a task looks like on its page. Your line is drawn where you said it, under whatever the task had already done, as an elbow:
└ the config lives under etc/ · delivered
The └ says it is a correction to the work already running and not a new question, so
the page's turn count does not move — a task's page is one question, the instruction it
was given, with your corrections hanging off it. The clause after your words is what the
sending did, and it is there for a few seconds and then gone: · delivered ordinarily,
and · it was waiting on its pieces — your line wakes it when the task had handed its
work out and was parked on the reports, because then your line is what starts it moving
again. The elbow itself stays.
A task's page reopened later reads your corrections as corrections. Leave the room
and come back, or open the task tomorrow, and every line you steered into it comes back
as a └ elbow in the place you said it — with no clause, because what happened to those
words is news and the elbow's position is the whole of the record. They used to come back
as fresh questions with a ›, which made yesterday's correction read as a second
instruction and made the page count turns nobody opened.
esc in a room never interrupts and never stops. Out in the conversation esc
interrupts the running turn; inside a room the first esc leaves the room and the next
one interrupts. Ending the task itself is x and its card. The legend's left end always
names what the next esc does: room · esc/←← main, and room · esc your line back
while a history walk is on. The keys row under the box reads x stop while there
is work here to stop and ↑↓ history during a walk — it never reads esc interrupt
inside a room, because in here that is not what the key does.
A click inside the room's page does not leave it. A press that lands on nothing —
a blank row, the gap beside a paragraph, the slack under a short transcript — does
nothing at all, exactly as it does in the conversation. Leaving is esc and ←, and
the pinned header at the top of the page names both: esc/← main. That header row is
also a button — press it anywhere along its width and you are back in the conversation
The Stop that ends the work is not on that row at all: it is at the right end of the
quiet facts row underneath, so a press aimed at leaving can never end a task.
Inside a harness design's room, a finished design is answered by the same three digits
the page in the conversation uses: 1 save it · 2 change it · 3 drop it, with esc
for later. There is no second row of chords in the room. It used to have one — ctrl+k
saved the design and ctrl+x dropped it — and that row is deleted along with the
enter/e/esc grammar that answered the card in the feed. Every other key goes to the
message box, which is where you say what you want changed instead. The saved-shapes pages
describe the answers in full.
x asks to stop the work. It is taken on the roster's focused row and inside a
room or an adaptive run's page, and only over an empty message box — the moment there
is a sentence in the box it is the letter x. When the roster does not hold the keyboard
and exactly one stoppable task row is visible, x takes that row straight away, because
there is nothing to choose between; with two or more rows it does nothing until alt+t
gives the roster a cursor to aim with. It never stops anything by itself: it raises a
card, and the card is answered below.
The tasks pages describe what rooms and the roster are for.
A program's task page — senior-dev's — opens on the actions it took, each under the step
of its process. ctrl+y turns it to the raw calls it made to its model: what it sent,
what the model answered, which model it was, and the call in flight. ctrl+y again turns
it back. It works in the program's room, whichever door opened it — its row, its card, or
the activity page — and the key row names it: ctrl+y calls over the actions, ctrl+y actions
over the calls. Every page opens on the actions.
It is a chord, so it never costs a character: the room's box keeps what you typed. It is not bound on any other task's page.
x raises one card above the message box:
? Stop this task?
Its work halts; the branch it wrote on is kept.
1 stop it
2 keep going
enter take it · esc keep going · ←→ choose
The head is the question and nothing else; the sentence under it is the promise — what stopping does not take away.
On an adaptive run's page the head reads Stop this run? and the promise is
In-flight nodes halt; partial results stay. A harness being designed is a task, so
x on its row reaches it like any other — and the promise says what is actually true
of it: The page it is writing is dropped; nothing was saved. It has no branch and
wrote no files, so the reassurance about a kept branch would be pointing at nothing.
The cursor opens on keep going. left/right move it, enter takes the
answer under it, esc is keep going, and every other key does nothing while the
card is up. A click on either answer's row is that answer, and a click anywhere else
does nothing rather than falling through to the box.
1 and 2 move the cursor; they do not answer. The digit beside an answer walks
the cursor onto that answer and lights its row. enter is still what decides, which is
this card's whole rule said in the digits' own grammar.
Your half-typed message is safe under this card and cannot be sent by it. enter
belongs to the card while it is up; the sentence in the box is exactly where you left
it once you have answered.
There is no bypass key and no "don't ask me again". Stopping cannot be undone —
the worker's turn ends where it stands — so the card is always asked, and pressing
x again while it is up is a keystroke the card swallows.
esc never stops anything. It closes the card, then a room, then a page, in that
order.
Nothing is thrown away by stopping: see the tasks page for what a stopped task and a stopped run keep.
A landing that reads your call is a question, and it is put to you on the question
block above the message box — the same block every other decision in codeaf arrives on —
so the letters below work wherever you are standing: the conversation, the task's own room,
the /tasks page. Any landing that is your call, at any depth — a task you asked for,
or a part of one it handed out itself:
| Key | What it does |
|---|---|
a |
the ask's own yes — take the work; on a card whose branch clashed with yours it reads resolve it, which spends one more merge round and takes nothing as done |
n |
the ask's own no — not right, or drop it on a conflict; the task becomes incomplete and keeps its branch, and its dependents still fail because it did not finish |
s |
tell it — opens the task's own page with the message box pointed at it. What you type is sent as a correction; it never answers the question by itself, so "looks good" typed there does not become an accept |
esc |
later — the question folds to the chip in the status line and nothing is answered |
d you decide is not on a landing's row. The three answers and the way out are the
whole of it, and no key on this surface ever does something that is not drawn on the screen
in front of you. Where a landing draws each answer on a line of its own — the one road that
does, because its [a] moves files of yours — d you decide is on it. The lasting
choice is task.settle in /settings under Tasks, and it is the right place for it: a
letter that hands one landing over changes nothing about the next one.
While task.settle is auto the reason row also reads codeaf is deciding; the answers
stay drawn, and pressing one yourself is how you take the question back.
The words on a and n change with the question and the keys never do. A card whose
branch clashed with yours reads a resolve it · n drop it; one the check did not pass
reads a accept anyway. There is always a third column, s tell it.
check again is not offered. codeaf retries a check that never answered by itself,
on another model, before the card ever appears — so there is nothing left for you to
spend a round on. l does nothing here now.
A key that is not drawn does nothing. If codeaf has no way to spend an answer — no
merge round behind resolve it, for instance — that chip is absent rather than present
and failing, and its letter is absent with it.
They are held to the same rule x is. The card must be the selected one — walk to
it with ↑/↓, which steps through tool calls, proposals and landed cards — and the
message box must be empty, with no panel, picker or copy mode up. A letter typed into a
sentence stays a letter, always.
Once answered the letters go away and the receipt every question leaves takes their place,
✓ <the card's head> → accept · you · 14:02 · c change: the answer in the card's own
words (accept, not right, resolve it, drop it), who decided and when (the questions
page has the shape). The task's report then leads you took this as done or
incomplete — you said it is not finished. d decides nothing yet: the answers stay drawn
and the reason row reads codeaf is deciding. The same columns are clickable on the card.
See the tasks page for what each answer does to the work.
Inside the task's room the same keys need no selection. The room is the task, so
a, n, s and d over an empty message box answer it directly, the answers row stands
at the foot of the page where this task has finished — say it to main would otherwise be,
and the hint slot names the same letters while the question stands.
The room and the card are one question: answer in either and both show the receipt.
And the roster's row answers them too. With the roster holding the keyboard (alt+t)
and the cursor on a row that is your call, the hint slot names the same letters in place
of the move keys, and they answer that row's landing without opening its room. Same card,
same answers, same receipt — the column, the card and the room cannot disagree, because
there is one card behind all three.
codeaf owns the pointer by default, using all-motion tracking so hover works.
Only the left button acts. A press is resolved in this order:
- The quick rewind mode (
escesc), which takes every press on the conversation while it is up: a click on any transcript row moves the cut to the nearest point at or above it, and a click on the⟲ rewind hereline does the rewind. - The settings panel, the task page, home, the rewind timeline, the status deck, or the phone tool sheet — each takes every press inside its frame, padding included. On the task page a press on a row puts the cursor there and the record pane beside the list follows it, and a second press on the same row opens it — on a frame too narrow for that pane one press opens, as it always did; a press on a section word or on empty padding does nothing. Inside an old task's record card, the head row and the foot go back to the list and its body is read. On automations and spend a press on a row opens it on the first press too — an automation's row opens its history; on memory it opens a line's card, or opens or folds a group. On home a click puts the cursor on a row and a second click opens it. On the rewind timeline a click places the pick and a click on the point already placed does the rewind.
- An approval question block, then a connect offer, then a harness offer.
- The harness panel, the permissions panel, the connections panel — a press on a row acts, and a press anywhere else closes the list.
- The row above the message box: an attachment chip or a picked harness's chip removes it. Neither moves the caret in your draft. The thinking rung is not on that row any more — it is on the legend above it, where a press walks it one step.
- The jump-to-latest chip.
- A stop target: the confirmation card's two answers while it is up, and the
Stopat the right end of a room's facts row — the second row of its header, under the breadcrumbs. On a phone-width terminalStop's hit box is three rows tall, because a finger is about that wide. - A room's pinned header, which is the pointer's way back to the conversation.
The whole row answers, both ends of it, because the row says
esc/← mainand a row that named the exits and did nothing when pressed would be dead. The dim family lines under it are facts, not doors, and do nothing. - Task strip chips, then the rail column, then a proposal's choices row. On a wide
terminal the strip chip the roster's cursor is on carries a
✕of its own, and pressing it asks to stop that work instead of opening its room. When the column is closed, the two-column edge it leaves at the right of the frame answers here too — a press anywhere on it opens the column again;alt+ldoes that too. Within the column, its own lines are asked before its task rows: the header's words (the other word brings its view to the front, and thealt+lat the right closes the column), a needs-you band row (it opens its thing), a group heading (it opens or folds the group), a Traffic row, a+ /taskor+ /automationsrow (it types that command into your message box), and a row in theautomationssection (it opens/automationswith the cursor already on that automation). Whatever lights under the pointer is exactly what a press there does. - The figures on the status row: the money figure (
$0.14) and the cache beside it, which open the Spending tab of/settings; and the context meter (66.8k/1.3M · 5%) and the compaction forecast, which print/status. Each brightens under the pointer over its own cells to say it is a door. The model's name is on the line above the message box and opens the model picker, and the thinking rung beside it walks one rung up the ladder. A press elsewhere on the status row falls through — the rest of it is readings, not controls. On a narrow terminal the whole two-row deck answers. - A message of yours waiting for the answer to finish, in the block above the
box — a click anywhere along its line takes that message back into the box to be
edited, and the block loses it. The whole line answers, because nothing shares it.
On the dim line under it, only the words
→ steers it inanswer; a press on the rest does nothing. - The body: an inline task link inside prose, which is the one mouse-only target
on the surface; a cut markdown table's foot; a waiting sign-in, where a click
copies its link; a thinking block, clickable over its whole height; a tool row,
which opens its expansion, except that hovering a running foreground command reveals
click to backgroundin its right-hand slot and only those words keep that command; or the full-frame sheet on a narrow terminal; theN earlier tool callsfold; the… N more linesfoot, which lifts the cap; a spawn card, which opens the node's room, or its brief if there is no node yet; and a landed card, which opens its full context. The answers to a landing that is your call are not on that card: they are on the question block above the message box, where each ofa accept,n not rightands tell itis its own target and a press between them does nothing.
A file path is a different kind of target. Everything numbered above is a click
codeaf itself answers. A real file path — in a reply, in a note, on a read/edit/
write row, under a picture — is a terminal hyperlink, so your terminal answers it,
usually on cmd+click (ctrl+click on Linux). The underline is how you can tell it
works. See "click a file path to open it" on the "what is on the screen" page for which
terminals open one and what is deliberately not linked.
A click on empty space does nothing, anywhere — there is no empty-space gesture on
this surface, and that includes inside a room: a press on a blank row of a task's page
is not the way out and never closes it. The way out of a room is esc, ←, or a press
on the pinned header that names them. A click in copy mode acts on nothing, because
the rows there are a frozen snapshot.
Hover lights whatever the pointer is on, at the size of the thing rather than the size of its row: a row that is one target — a tool call, a roster row, a parked message, a room's header — takes a background band across the width, and something that shares its line — a strip chip, a picture on the tray, one answer of a card, a task reference in a reply — lights only its own cells, leaving its neighbours dark. Anything that answers to nothing does not react. On home it does one thing more: the preview on the right becomes the row you are pointing at, and returns to the cursor's row when you point somewhere else (see the home page). There is no hover in copy mode, on the linear/screen-reader tier, or in the phone tool sheet. The screen page says what lights, under "When a row brightens under the pointer".
The wheel usually moves three rows per notch on the surface that owns the frame.
The context chooser answers first, then the conversation switcher (one row a notch),
copy mode, questions, the crew panel, and /model. Next come the task composer's
model list, a settings slot or role's model list, and home's draft model list: each
walks its own cursor, clamped at both ends, with the page beneath unchanged.
With those lists closed, the page navigation row answers before settings, the task page, home and the other places. Next come the rewind timeline, the status deck, the phone tool sheet, conversation tabs, an open command menu, the fullscreen roster, the task column under the pointer, an open question room or task room, and otherwise the conversation. Each list whose window follows its cursor stops at its own ends. A task's decision sheet or move menu absorbs the wheel, and an open design wall handles it before the context chooser.
On the settings panel, the task page, home, the rewind timeline and the fullscreen roster the wheel walks the cursor rather than a scroll offset of its own, because on those the window follows the cursor.
The task column on the right scrolls under the pointer and leaves the conversation
beside it where it is. It moves the column's own window (the header and the needs-you band
stay pinned at the top) unless the column is holding the keyboard (alt+t),
in which case the window is already following the cursor and the wheel walks that instead.
Reaching the bottom re-arms sticking, so new replies follow along again. Scrolling up drops out of it.
pgup and pgdown move the height of the view minus one row, never fewer than one.
The jump-to-latest chip is one dim chip at the left edge reading ↓ latest · ctrl+l
— v latest · ctrl+l on the linear tier — and it appears only when you are parked
away from the live edge. It is drawn into the first row of the frame's existing
breathing gap, so it never takes a row of its own; on a window too short to have a
gap it is not drawn at all, though ctrl+l still works. It is dim normally and
accent-coloured under the pointer. Clicking it, or pressing ctrl+l, rejoins the live
edge — the room's edge if a room is open. It is not shown while copy mode, a room, or
the fullscreen roster is up.
Selecting text with your mouse — drag to copy, select a word with the mouse, why did copying take the whole line instead of the words I dragged over
Just drag. Put the pointer on a character, sweep to another with the left button
down, and the cells between them highlight — from where you pressed to the end of
that line, every line between in full, and the last line up to where you are, exactly
as your terminal would select it. The moment you release, that text is on your
clipboard — stripped of colours and the drawn left rails, exactly as copy mode
strips a yank. There is nothing further to press: no ctrl+c, no key at all —
releasing the button IS the copy. The highlight stays lit for the few seconds the
status line says what landed — copied · 14 chars for a span inside one line,
copied · 3 lines across several — so you can see exactly what you got. The write
goes over OSC 52, so it works over ssh and through tmux. What is highlighted is
exactly what is copied, to the character.
Double-click takes the word, triple-click takes the line. A word is what a
person means by one: a path, a hash, a flag, go.mod and a URL are each one word, a
bracket or a quote ends one, and the full stop closing a sentence is left behind.
The status line says copied · 1 word or copied · 1 line. Two quick clicks on a
tool call or a fold are still two clicks — open, then shut — because a button acts on
every click; only text is taken by a double-click.
Copying took the whole line instead of the words you dragged over? Two things do that,
and neither is a fault. A third click in quick succession takes the whole row — that is
what triple-click is, and a double-click with a stray third click becomes one.
And a sweep that leaves the row it started on is a stream, not a box: it takes your
anchor to the end of that row, every row between in full, and the last row up to the
pointer, exactly as your terminal would. To take a few words and nothing else, sweep
sideways and stay on the one row — the status line then says copied · 14 chars rather
than copied · 2 lines, which is how you tell the two apart.
A wide glyph is never split. A sweep that starts or ends inside a CJK character or an emoji takes the whole glyph, and the highlight covers both of its cells.
The message box answers the same sweep, and so does the box at the foot of every place. The one difference is that a selection there is live — you can type over it — so it stays lit until you move the caret rather than fading with the status line. See "how do I select text in the message box" above.
The sweep is drawn in the same background copy mode's selection wears — the strongest of the three this screen draws, a shade above the one under the pointer. It is the same claim ("these rows are what a copy would take"), so it is the same paint; it used to be drawn at the pointer's own quieter step, which said a sweep in progress was a shadow rather than a selection.
The selection covers every cell it takes — a code span or a chip included. An inline
code span wears a background of its own (the raised plane that makes it read as code),
and so does a chip, but under a sweep they are part of what is being claimed: the
highlight paints over them, in the sweep's colour, their text keeping its own colour
and weight. Every terminal's own selection replaces the background it sweeps over, and
this one reads the same. The clipboard is unchanged — what is highlighted is still
exactly what is copied — so a sweep across a row with code spans in it copies the
row you saw lit, to the character.
The selection sticks to the text, not to the screen. A task's page streams and follows its live edge, so rows can scroll while your button is down; the highlight rides the rows you swept, the copy is those rows wherever they moved to, and a click opens the row you actually pressed even when it has shifted. A row that scrolls clean off the screen before you release is no click at all.
A click is unchanged in feel: press and release in place and it lands where it always did. (Under the hood the body's click now fires on release, the way every button in every GUI does, which is what lets a drag never trigger the thing it started on — a sweep that begins on a thinking block does not collapse it.)
A click is allowed to wobble. A hand is not a vice, so a press that drifts up to two columns sideways or one row up or down before you let go is still a click, and it lands on the row you pressed rather than the row you drifted onto. Past that it is a sweep. The cost of the tolerance is one gesture: you cannot select exactly two adjacent rows by dragging down exactly one row — sweep past them and come back, or sweep sideways within a single row to select that one row.
codeaf still owns the pointer by default — that is what makes wheel scrolling, clickable paths and pressable rows work — and there is no scrollback to fall back on, because codeaf runs in the alternate screen. Two further doors remain for when you want your terminal's own selection:
ctrl+s hands the pointer back so a drag selects text the way it does everywhere
else. The /select command does the same thing. Your next keystroke takes the
pointer back — there is no mode to leave. ctrl+s is a toggle, and it is the one
key excepted from that automatic handback.
The hover highlight is dropped along with the pointer, so no band is left lit under a
pointer that has moved on. While the pointer is out, the row under the message box
reads exactly drag to select · any key ends it.
If you have turned the ui.mouse setting off, your terminal already has every drag
permanently, and /select says exactly:
your terminal already has the pointer — drag to select.
Five things stop a click, and only one of them is a setting.
You pressed ctrl+s. That hands the pointer to your terminal so you can drag-select,
and it is a toggle — it is the one key excepted from the automatic handback, so if you
press it and then only touch the mouse, nothing codeaf draws will answer a click until you
press a key or press ctrl+s again. While the pointer is out, the row under the message
box reads exactly drag to select · any key ends it. That line is how you tell this apart
from everything else here.
The ui.mouse setting is off. It is on by default — /settings, the General category, the row labelled mouse interaction. With it off, codeaf never asks your terminal to report
the pointer at all: no hover, no click, no wheel, and your terminal keeps drag-select
permanently. /select then says your terminal already has the pointer — drag to select.
Your click drifted. A press that moves more than two columns or more than one row before you release is a sweep, not a click: it copies the rows it crossed and the status line says what was copied instead of opening anything. See "selecting text with your mouse" above.
There is nothing under the pointer. A click on empty space does nothing anywhere on this surface, including the air around the place words on the top line and the blank rows of a task's page. A click in copy mode acts on nothing at all, because those rows are a frozen snapshot.
The terminal is too narrow for the word you are aiming at. The top line folds its
trailing places into more ▾ as the frame narrows; click more ▾ and pick the place from
its menu. tab, shift+tab and alt+1…alt+8 still go everywhere.
A file path is your terminal's click, not codeaf's — usually cmd+click (ctrl+click on Linux). If a plain click on a path does nothing, that is why.
Copy mode: taking text out of the conversation — ctrl+b, freeze the screen, esc to leave, and ctrl+b on home does nothing of the kind
ctrl+b freezes the view and hands the keyboard to a reader, so you can pull text out
of a surface that runs in the alternate screen where your terminal's own selection is
gone. The /copy command does the same. Inside a room, ctrl+b freezes the room's
rows rather than the conversation's.
On home ctrl+b is not copy mode. It moves the caret in home's box one cell to the
left, as ← does, and home's rows are never frozen. Nothing can be copied off the home
screen this way: a title or a path on home is on a row enter opens, and inside that
conversation the rows can be frozen. (For one build on 2026-09-22 the key froze home's
own screen; it was taken out the same day.)
Freezing looks like nothing when nothing is moving — the rows stay where they are on
purpose. What tells you the freeze is on is the keys row, which reads exactly
↑↓ move · v select · a block · enter copy · esc, the highlighted cursor row, and the status word
COPY. esc always leaves.
| Chord | What it does |
|---|---|
up/k, down/j |
Move |
pgup, pgdown |
Page |
home, end |
Jump to top or bottom |
v |
Drop or lift the mark |
a |
Take the block under the cursor |
enter, y |
Copy the selection, or the cursor's line when no range is marked |
esc, ctrl+b, q |
Leave |
Copy mode takes every other key too, and does nothing with them.
a asks the narrower question first — a run of fenced code rows. Press a again
to widen to the whole answer around it. A blank row belongs to nothing, and a there
does nothing.
Copying stays in copy mode and lifts the mark, so a second copy cannot copy the same span by accident.
The status line reads exactly COPY, or COPY · N lines when more than one line is
selected. After copying it reads COPY · clipboard sent until you move or change
the selection. This confirms that codeaf sent the clipboard write; your terminal
must permit clipboard access. The row under the box reads
↑↓ move · v select · a block · enter copy · esc.
Narrow terminals keep enter copy visible while dropping the other clauses.
Freezing snapshots the rows both painted and plain. The conversation underneath keeps streaming, and none of it moves the rows you are reading. Leaving rejoins the live edge — the room's edge if a room was frozen.
What comes out is the plain text with the left rail lifted — the stem under an
expanded tool call, the hairline beside a fenced block or a blockquote — along with
the indent in front of it and any trailing padding. The one-off marks › and ·
are deliberately kept, because they say who was speaking.
How it reaches your clipboard: OSC 52, written in band, so it works over ssh and
inside a container with no display. When TERM starts with screen or tmux it is
wrapped in tmux's DCS passthrough with every ESC doubled. It uses the clipboard
selection, not the primary one.
Refusals while copy mode is up:
- A click does nothing.
- A paste is declined, and your clipboard keeps the text.
- There is no hover.
- The selection highlight is a background, and a terminal below ANSI256 gets no
highlight at all. Read the span off the
COPY · N linescount instead. It is the strongest of the three backgrounds this screen draws — a shade above the one under the pointer and the one under a chosen row — because a selection is held open and runs across many lines at once, and you are looking for both of its ends.
An image drawn in an expansion copies as what it is on screen — rows of ▀, with
the colour stripped, which is no use to anybody. Take the dim line under it instead:
it is the picture's whole absolute path. It is also a link you can click, the same as
every other real file path on this screen — see "click a file path to open it" on the
"what is on the screen" page. What copy mode gives you is the plain path, with the link
stripped off it.
Three chords carry unrelated meanings. Which one you get depends on where you are.
A fourth, alt+e, carries one meaning on several surfaces — move the thinking rung of
the thing you are looking at — and its own section below has the table.
ctrl+. — two meanings, and the two screens can never both be up:
| Where you are | What it does |
|---|---|
| in a conversation | every task this project has run — the same list /history opens |
| on a place | draws the key map, exactly as alt+. (opt+.) does — only on terminals that report they can send ctrl+<digit> |
A place takes the whole frame, so while one is up the conversation's keys are not
under it at all. Where your terminal has not reported that it can send ctrl+., the place
reading simply does not exist and the chord does nothing there.
ctrl+t — three meanings:
| Where | What it does |
|---|---|
| Message box, or the task roster holding the keyboard | Start a new chat — the start page the tab strip's + opens. The roster gives the keyboard back on the way |
| Home | Start a fresh conversation in the folder of the conversation row under the cursor — a click on a projects row picks the folder for the next message instead |
| Model picker only | Cycle the reasoning effort |
It used to hand the keyboard to the task roster everywhere. That is alt+t (opt+t) now —
the same letter under the other modifier, beside the roster's own alt+w widen chord. On
macOS, opt+t types † instead unless your terminal is set to send Option as Meta — see
getting started, "Option as meta, on macOS", for the setting and where it lives.
ctrl+o — five meanings:
| Where | What it does |
|---|---|
| A landed card is selected | Open its output |
| A proposal is selected | Open its brief |
| Phone tool detail sheet | Lift the line cap |
| Inside a task's page, with a long instruction at the top | Open the rest of it, and press again to fold it back |
| Nothing selected | Fold or unfold this turn's tool cluster |
Inside a task's page the first thing ctrl+o reaches is the instruction the task was
given, where that instruction is longer than three lines: the page shows the first three
and one dim line reading ▸ …14 more lines · ctrl+o, and the chord opens the whole of it.
Pressing it again folds it back, and the line then reads ▾ …14 fewer · ctrl+o. A short
instruction has no such line and the chord falls through to the fold below. See tasks,
"The long brief at the top of a task's page".
ctrl+o also folds and unfolds a task page's own tool cluster where there is one, and
scrolling up at the top of the page opens that fold as well; the fold line there reads
N earlier tool calls · scroll up or ctrl+o.
ctrl+o is a chord, so it never costs you a character: you can press it with half a
sentence in the box and go on typing into the same words.
Two more chords surprise people:
ctrl+bis copy mode, not emacs "left". The alternate screen took your terminal's selection away, and copy mode is what buys it back.ctrl+emeans two things depending on whether the box is empty: end of line when there is text, open the most recent thinking block when there is not.ctrl+wcloses a tab wherever you press it: in a conversation the tab in front, and while the switcher is up the tab of the conversation under the cursor — the card has taken the whole keyboard by then. Neither one deletes the conversation;ctrl+eon its row on home asks to. In the message box the word kill it used to be isalt+backspace.ctrl+kmeans one thing now, and it is an edit: delete to the end of the line, in the message box and in every filter box. It opened the conversation switcher until that card moved toalt+k, and it saved a harness design from inside that design's room until the room's approval row was deleted. Neither is bound any more.
And over an empty box, left and right are navigation rather than caret
movement. ctrl+f never is — it always moves the caret right.
alt+e moves one step up the thinking ladder — low, medium, high, xhigh, max —
and it moves the rung of the thing you are looking at. One chord, three scopes:
| Where you are | What moves |
|---|---|
| The message box, typing or empty | This conversation's rung — the one on the legend above the box, beside the model, see The thinking chip above the message box |
The task roster holds the keyboard (alt+t) and the cursor is on a task |
That task's rung |
| You are inside a task's page | That task's rung |
| Home, whatever row the cursor is on | The next conversation's rung — the effort word after the model's colon on the rule above home's box, carried onto the conversation enter opens |
Everywhere else it does nothing at all: the other places, a program's page, and the
automations — an automation has no thinking rung. No conversation row on home is a scope:
a conversation's rung belongs to the window that conversation is open in, where the rung
on the legend above its message box moves it — by this chord, by a press on it, or by
/effort.
The machine's own default is not one of the scopes. Home once had a card about the
machine, and this chord moved the install's rung from it; there is no machine card now. To
change how hard this machine thinks by default, open /settings and walk to the
thinking row, which is the setting both roads always wrote.
It climbs, and off the top it comes back to auto. Each press goes one rung up. This
conversation's rung, a task's rung, home's next-conversation rung, and the level ctrl+t
dials onto one model in /model all come back to auto off the top — the surface is the
only door that sets any of them, so it has to be the door that clears them, and clearing
hands the work back to whatever stands over it. /effort auto and the top row of
/effort clear the conversation in one move instead of walking to it. (This
conversation's rung wrapped from max back to low until 2026-09-15, which left auto —
the state a fresh install is in — reachable only by name.)
The rung reads as a quiet clause where the thing already states its facts — that is
what thinking high says. A task's is under Task setup (or Next run setup after it
settles) in the expanded task page, as Thinking · high, and in the compact header and
roster figures when there is room; there a rung nobody has dialled says nothing, which is
not the same as low. The next conversation's is on the rule above home's box. The
machine's own is the thinking row of /settings. No card on home says it: home's cards
carry no thinking clause.
On a task it lands on the next call, not this one. A worker already running keeps the
rung it started with, so the line codeaf writes says so: task 7 · thinking · high · its next call takes it. On an ordinary task that has finished, it instead saves
the choice for the next continuation. The completed attempt stays unchanged.
Where it is named. The roster's hint line ends alt+e think harder · esc on a task
whose rung can move, and drops the clause where the key would not work; the keys row under
the message box, and home's, read alt+e effort. A window with nowhere to write the
setting does not offer it.
These do nothing in the v3 chat. If you expect one of them, here is the straight answer:
| Chord | Status |
|---|---|
shift+enter |
Bound: opens a new line in the home and conversation message boxes, even while an answer runs |
ctrl+shift+enter |
Bound, in one state: while a turn is running with something typed, it stops the answer and sends that message. It does not open a new line — use shift+enter. Over an empty box, or with nothing running, it does nothing |
cmd+enter |
Bound, in one state: while a turn is running with something typed, it holds that message above the box for the next turn. It does not open a new line. Over an empty box, or with nothing running, it does nothing. Needs a terminal that can spell it |
ctrl+d |
Not bound |
ctrl+v |
Not bound. It used to change effort; use alt+e (opt+e on macOS) now. Pasting remains the terminal’s shortcut and arrives as pasted text |
ctrl+k |
Bound, in every box: delete from the caret to the end of the line, the pair to ctrl+u. It does not eat the newline. It was the conversation switcher until that moved to alt+k (opt+k on a Mac) to give this letter back to the message box |
alt+k |
The switcher: the card of every conversation this terminal has open. On a Mac it is opt+k, and it needs "use option as meta" turned on in your terminal — see "Why alt+k and not ctrl+k or ctrl+tab" |
ctrl+r |
Bound. In the message box it is spell it out — see "Make my prompt better" above — in the /files list it opens the folder a file is in, and in the /model picker it fetches the newest model list. Nowhere else |
alt+e |
Bound, on three surfaces: it moves how hard the thing you are looking at thinks — this conversation from the message box, a task, or on home the next conversation. An automation has no rung to move. The machine's own default is the thinking row of /settings and is not on this chord. See "The thinking chip above the message box" and "alt+e — how hard the thing you are looking at thinks". Anywhere else it does nothing. On macOS it is shown as opt+e; the terminal must send Option as Alt/Meta, as for the other Option shortcuts |
ctrl+x |
Bound in two places: inside the paste editor it discards that paste (ctrl+x discards this paste); and on a tasks row of home that this window holds it asks to stop that task (ctrl+x stop it on the alt+. map; the foot under a field row is the resting sentence and does not name it). It stops no automation — that is → then s on /automations — and it no longer drops a harness design. Not bound anywhere else |
ctrl+y |
Bound on a program's task page: it turns the page between the actions the program took and its raw model calls; on a Home project row and in the /files list it copies the path under the cursor. Nowhere else |
ctrl+z |
Bound: undo in every box, with ctrl+shift+z to redo — see "Undo what I typed" |
ctrl+<digit> |
Bound as a second spelling of the place keys, on the terminals that report they can send it. ctrl and a digit has no encoding in the scheme most terminals speak, which is why alt+1 … alt+8 (opt+1 … opt+8 on a Mac) are the first spelling and always will be, but a terminal running the kitty keyboard protocol sends it and says so, and where that report arrives ctrl+1 … ctrl+8 reach the same seven places and the chats destination. The map's line says alt+1…8 or ctrl+1…8 go to a place exactly when the alias is live. Where the terminal has said nothing, the chord does nothing and is never drawn |
ctrl+. |
Two meanings, on two screens that cannot both be up. In a conversation it is every task this project has run (/history); while a place is up it draws the key map, on the terminals that can send ctrl+<digit> |
alt+<letter> |
Bound only where a place says so, and only on that place — and no place binds one today (the old memory place's alt+s shelf walk is gone). alt+b and alt+f are the word jumps inside every box and are never taken by a place. Every other alt+<letter> does nothing |
shift+← shift+→ shift+↑ shift+↓ |
The time window of a place that has one: shift+←→ moves it by its own length, shift+↑↓ changes how coarse it is. Two places have one — activity (when it ran) and spend (which days) — and each draws the same control on its head row, shift+← aug 12 – aug 25 → with shift+↑ coarser beside it. Anywhere else, on a terminal too narrow to draw the control, and (for the zoom alone) on a line with no room for its clause, they do nothing |
ctrl+h |
Deliberately not bound, because some terminals send plain backspace as ctrl+h |
A key that is not bound falls through to "does this key carry text". If it carries text it types; if it does not, nothing happens.
While a foreground bash command is running, ctrl+g hands it to the background
instead of waiting for it. Nothing is killed and nothing is run again: the
command keeps going as a job, the row gains a dim job 3 beside its other
trailing marks, and the turn carries straight on. Read it as "go on" — let the
command run and get on with the work.
This is the key for the moment you realise go test ./... is going to take nine
minutes. The alternatives are esc, which stops the turn and throws the run
away, and waiting.
Afterwards it is an ordinary job: ask codeaf to list them, tail one, or kill one, and it is killed with everything else when the session closes. The command's log file is named in the call's own result, and on the job's page — not on the row.
The key is absent whenever it cannot work, and absent means it does nothing as a background gesture rather than telling you it cannot. The same absence rule governs the row's pointer offer:
- nothing is running
- the running call is not
bash— areador awritehas no process to hand over - the running
bashasked for the background already, so it was a job from the start - the row has already been sent away
- the call is being replayed or is still forming
- this session is over
--host, where there is no door for the local surface to hand that process over
When more than one command is running at once, ctrl+g takes the one that has
been running longest, which is the one you are waiting on.
While a command can be kept, this meaning takes precedence over hiding or restoring the
task column, so the column stays where it was. With no such command the key does what
alt+l does: it closes the column or brings it back. ctrl+b is copy mode and esc interrupts; neither changes.
The ui.mouse and ui.timestamps settings are read at boot and again at the end of
every turn — not the moment you change them. The same is true of the approval posture
and the consent countdown.
So if you turn the mouse off, or turn timestamps on, mid-turn, the change takes hold one turn later. Nothing is wrong; the surface has not re-read the setting yet.
This holds however the row was changed — in the panel, or by asking codeaf to do it with
change_setting. The row is written straight away and the transcript says so; the screen
picks it up when the turn ends.
The background shell commands after row is different. Its key is
bash.background_after_seconds, and the session engine and the visible countdown arm
from it together at launch. A change lands on the next session, not at the end of the
current turn. Because this row controls how hard the machine may be worked,
change_setting refuses it; open /settings, choose the Tasks category, and change the row
yourself.
With ui.mouse off, every drag belongs to your terminal permanently, and ctrl+s has
nothing to hand over.
The compact conversation keeps thinking behind its work disclosure. Open that with
ctrl+e, then click the thought block to inspect it. Inside the opened work, or on
a task page, models that stream their working get a dim italic block headed
⠿ thinking · N tok · ctrl+e.
While it streams, only the last 3 wrapped lines show, each painted a step further
along a fade so the newest reads brightest. The moment the turn says anything that is
not reasoning, the block collapses to one row reading
thought for Ns · N tok · ctrl+e.
Click anywhere on the thought block to open it. ctrl+e over an empty message box
opens the most recent thought only when no whole-work disclosure takes priority.
Opening latches your choice during streaming. An opened live block shows the
whole buffer, not the 3-line window. Completion still folds the whole turn’s work.
An expanded block is capped at 200 rows, and says how much is held back. The token
count is an estimate at 4 bytes per token. Before enough text arrives to estimate one
token, the count is absent: thinking · ctrl+e, or thought for Ns · ctrl+e after
the block collapses. The label and disclosure stay available.
Reasoning is never written to the session file. A resumed conversation shows the answers, not the thinking.
The chat loop watches for a model that keeps calling tools without putting any visible
words between the calls. After 6 consecutive tool-using replies with no visible assistant
text, it adds a note beginning [silent]. If the silence continues, a stronger note
arrives at 12, and that one is the last — one silent run is mentioned twice and no more.
Each note asks the model to write what it has learned, what it will check next, and why
before making another call.
A [silent] note is not an accusation of being stuck. It is about the record rather
than the work: it says the reasoning between steps is being lost. It spends none of the
loop guard's warning limit, and no number of [silent] notes will end a turn.
The first note is advice; the second says the tools are about to be held, and the section below says what that means.
That request matters for reasoning models because their streamed thinking is shown on the screen but is not put into the next request. Of the model's prose, only visible assistant text becomes part of the conversation the following step can read.
Three things reset the count — and with it the hold, if one was on: a visible note, a
successful edit or write, and a successful shell command that left the working folder
different from how it found it. That last one is why a commit-and-push run is not scolded
for being quiet — landing work is work, whatever verb it is spelled with. Each rung is
issued once in one silent stretch; after a reset, a later silent stretch begins again at 6.
Because the note it asked for twice was never written. The [silent] note at 6 replies is
advice. The one at 12 is the last, and it says outright from here your tool calls are held. From that point the loop stops running a reply that carries only tool calls: each
call is answered with [held] Nothing was run this step… in place of its result, nothing
reaches your files or your shell, and the answer says what to write and that the next call
runs as soon as it is there.
A rule the loop can enforce is not a suggestion. The reasoning between steps is not saved anywhere — a task's room, its checker, its parent and you all read what was written down — so past a certain amount of silence codeaf stops asking and starts holding.
Writing anything visible clears it immediately and the loop is back to normal, at the first rung again. A reply that carries both a note and tool calls was never held in the first place, so a model that writes as it works never sees any of this.
After 3 held replies the turn stops. If it keeps sending only tool calls, the turn ends with this line rather than arguing forever:
stopped here · would not write its notes down, so what this turn worked out is not on the record
Nothing is handed to a task — the same model under the same rule would be as quiet — and whatever the turn had already saved is on disk where it left it.
When the turn that stopped belongs to a task worker, the task says so. Its row on the
rail reads incomplete · would not write its notes down, drawn dim rather than in the
bad colour — nothing was found wrong with the work — with branch kept beside it as a fact
of its own. How tasks run has it under "What the words under a stopped task mean".
The loop also notices command variants that keep returning information already seen. After
5 consecutive tool rounds in which every result contains no fresh line, a [stuck]
note says: the last 5 rounds read nothing new; what you are looking for is already in the transcript. A fresh line, a successful write, or a command that changed the working folder
resets that count.
Four signals say the turn is not moving: the same call three times in a row, the same
failure three times, the same argument refused twice, and five rounds that read
nothing new. They share one warning limit. After two [stuck] notes, a third such signal
ends the turn instead of adding a third ineffective note.
In an interactive conversation, codeaf uses the same checkpoint hand-off as any other
overlong turn and moves the remains to a watched task. If that hand-off cannot be made —
for example inside a task or without a consent surface — it ends the turn with
this turn is going in circles · stopping here with anything remaining left undone.
Two things soften that limit. [silent] notes spend none of it, so a quiet turn cannot
be ended for being quiet; being held for not writing its notes down is a separate road with
its own ending, described in the section above. And getting something done gives one
spent note back: a batch
that wrote a file, or ran a shell command that changed the working folder, steps the count
down by one — unless it was the very call the turn has already been warned about, because
writing the same file seven times is the loop and not the way out of it. It is a step down
and not a wipe: a turn that keeps looping still reaches the limit, it just takes longer to
get there.
Flush-left text is said to you: your messages and codeaf's trailing answer. Text with a two-column gutter is work done on your behalf: thinking, tool calls and their details or results, and assistant text that was followed by another call. Below 60 columns the gutter disappears and the dim treatment carries the same distinction.
Indented reply text is also greyer than the answer. Opened narration renders markdown structure in quiet ink, so headings, lists and code remain readable. See "Why is part of the reply grey, and where is the actual answer" on the screen page.
This is the answer to "how do I see what codeaf did?", "what did codeaf just do", "show me the work behind that answer", "see the tool calls it ran", and "what happened during that turn" — the finished work is folded, and one gesture opens it.
When a successful turn has work and a trailing answer, the finished work collapses to
one indented chip between your message and the answer, such as
▸ worked 47s · thought 6s · 6 tool calls · ctrl+e. Its figures are the whole turn's
elapsed time, the thinking block's time when there was one, and the real call count.
There is a blank row between the chip and the answer under it. Detailed time,
token and spend receipts appear only when you open the work.
A turn you stopped with esc says so instead. Its unfinished work collapses
into a stopped-work chip. Completed replies and explicitly addressed updates stay
outside it; a human update interrupted mid-stream is labeled interrupted.
The retained audience and interruption state survive reopening the conversation.
Click the chip or press ctrl+e over an empty message box to open or close it. There is
no transcript cursor, so the key first chooses the running conversation’s compact
steps, then the latest completed turn’s work. The running view shows up to three
wrapped rows of recent captions, with the newest live step shimmering. Opening
shows the existing outline: click a caption to inspect its calls, or click the
whole-work door to return to the compact steps. Completion collapses work opened
during the turn. A caption longer than three rows ends with an ellipsis in the
compact view; open the work to read its full text.
Confirmed replies stay visible even when later work follows them. An important
interim result, blocker, decision or requested update also stays visible when
codeaf explicitly addresses it to you with a leading [update] marker. The
interface removes the marker and renders the message normally, including when
that same response calls tools. Routine tool narration has no marker and folds
with the work; you do not need to type any marker yourself. Team exchanges
and compaction details stay inside the work disclosure; opening it preserves
reasoning, notes and receipts alongside the step captions.
Questions, approval prompts and text-only answers are never hidden — nor is a second message you sent into a running turn, which
ends the chip above it and starts a new one. Fold state belongs to this window; resumed
sessions derive fresh closed chips from their saved entries.
The same rule applies inside tasks and nested task transcripts. Current work
uses one compact activity window of at most three rows. Earlier steps in that
turn stay behind its disclosure, including across a correction or an interim
reply. When work finishes, settled operations collapse into work chips around
your messages and confirmed replies. ctrl+e or a click opens the details;
ui.work = open opens them by default. No journal detail is deleted.
Set ui.work to open to keep completed work visible. fold is the default. The row
live-applies on the next render; work stays indented in either mode.
- Slash commands — what
/attach,/export,/select,/copy,/model,/resume,/permissionsand the rest do: the commands page. - The status line, the legend under the box, and the layout: the screen page.
- Tasks, rooms, proposals and the roster: the tasks pages.
- Rewind, which
escescopens quick and/rewindopens whole: the sessions and rewind page. - Automations — reminders, scheduled work and watches, their schedules and what their runs came to: the automations page. Their keys are above, in Keys on /automations and on an automation's card.
The + at the right of the conversation tabs opens a New chat start page. Type a first message or choose a recent conversation. Opening the page creates nothing. esc or its tab's close control returns to your previous chat or task with its draft. The first message creates the conversation. A draft parked on the start page stays for this window's lifetime. See Starting a new chat with the + plus button beside the tabs on the screen page.
ctrl+shift+t puts the last tab you shut back, and pressing it again walks
further back through the ones before it, newest first. It is the third key of the
same grammar: ctrl+t opens a tab, ctrl+w shuts one, ctrl+shift+t undoes the
shutting.
A held conversation returns with its unsent sentence, caret, attachments and reading
state — including one you left with keep running, which comes back with everything it
did while its tab was off the row. A remembered one is resumed on a connection of its own,
which ends nothing that is already open. It never creates a new conversation.
If reopening fails, the reason is shown and the entry stays first in line for retry.
It works from home, which is where shutting your last tab leaves you — that is the press this key most often undoes.
What it will not do:
- A tab you already brought back yourself is skipped. Reopening a conversation
from
alt+kor from home takes it off the list, so the key moves on to the one under it rather than spending a press on the tab in front of you. - The same conversation is never on the list twice. Shut it, reopen it, shut it again, and it is still one entry.
- The New chat page is not on the list. Nothing was created there, so there is
nothing to come back to — and the first message you had half typed was parked
when the page closed.
ctrl+topens the page again and hands it straight back. - With nothing shut, the key does nothing at all, and it never types into your message box.
The window remembers the last 32 tabs you shut, which is as many as the tab row itself remembers, and it remembers them only for as long as the window is open.
Your terminal has to be able to send the key. codeaf answers the event spelled
ctrl+shift+t. Whether it arrives distinctly depends on the terminal and its
keyboard configuration. A terminal that collapses it to ctrl+t sends that instead, and you get a
new chat — the plain chord is never read as a reopen, because a key that opened a
tab on one terminal and reopened another on the next is a key nobody could predict.
There is nothing to turn on inside codeaf, and the only test is pressing it.
Ctrl+W offers Keep running, Stop work and Cancel while an account asks for typed
input, just as it does during tool permission. alt+k opens the chats card and ctrl+t
opens another conversation without answering the question. Cancel leaves the
pending input untouched. Stop work cancels this conversation's pending question;
reopening it does not restart the work.
alt+y (opt+y on a Mac) brings the newest open question back from any page and
returns to its conversation. It leaves approvals alone. alt+a (opt+a) now cycles
approvals; the two shortcuts exchanged roles. A question with no answer remains open
when you put it off with esc.
Press alt+k (opt+k on macOS) to open the chats menu from a conversation.
It lists the same open tabs as Home, in the tab strip's order. Select one and press
Enter. Closed conversations are behind the menu's fold; Home also keeps up to
three recently closed conversations dimmed below the open list.
Press alt+e (opt+e on a Mac) or click the effort word beside the model above
the message box. It cycles auto, low, medium, high, xhigh, max, then
back to auto, without changing your draft. /effort high sets a rung directly.
This changes the current conversation's effort; it does not move other chats or
a task that has already started.
With an empty message box, press x when exactly one stoppable task row is
visible. With more than one, press alt+t (opt+t on a Mac), select the task
in the roster, and press x. Neither route opens the task room. The stop card
opens on keep going: choose stop it and press Enter to confirm, or click
that answer. Escape cancels the card. A half-typed message is preserved; with
text in the box, an ordinary x types the letter instead.
The successful saved here receipt requires the chat engine to acknowledge that its local transcript and captured work finished saving. A network error alone, or an unanswered closing call, does not prove that. If local closing still has no receipt after about three seconds, it says still saving on this computer — ctrl+c leaves the written turns here. If you leave before local saving is confirmed, codeaf says saving on this computer has not finished; its written turns stay here and exits unsuccessfully. A confirmed local save that is still uploading uses the successful sync-service receipt instead. A failed local write keeps the codeaf could not save this chat sentence.
The new saved-here receipt is enabled only when both the window and engine support it. An older engine's successful close stays successful without a receipt. An older window receives the ordinary successful reply when the new engine saved locally but upload is pending. A local write error remains a failure.
When an older engine has not answered before the closing wait ends, the window
keeps the earlier codeaf is still saving this chat in the background; unsaved turns stay on this computer.
message. A forced interrupt keeps the earlier exit status 130. New progress and
saved-here words require both builds to support the receipt.
A running hosted chat detaches and keeps working. Its successful departure does not require a local-close receipt. If another idle chat closed locally and its upload failed, quitting still reports that chat as saved here and succeeds. A failure writing this computer's saved chat or its kept copies remains a failure, including when an upload failed too.