|
|
Puts the follow-ups you retype constantly ("continue", "yes", "run the
tests") into the bottom frame row of pi's own pane, using the sl0p zellij
fork's OSC 5577 pane-status-bar protocol. Click one or press alt+N and it is
sent as a real user message.
╰ alt+N · ready ──[continue]─[yes]─[no]─[explain]───╯
WHY THE THIRD DESIGN. v1 drew its own button row and enabled terminal mouse
reporting inside pi. It worked and cost too much: mouse reporting is
per-terminal, not per-region, the wheel is just another button in that
protocol (SGR bit 64), and the DECSET modes only select how much MOTION is
reported -- there is no bit that selects which buttons. So pi's pane traded
native wheel scrolling and click-drag selection for clickable buttons, and
because pi renders inline into the main screen it cannot scroll the
emulator's scrollback back to compensate. v2 moved the buttons to a second
pane to confine the tracking; that restored scrolling but cost a pane, a
picker process and a unix socket. OSC 5577 removes the tradeoff instead of
relocating it: zellij draws the buttons in the frame it already owns,
hit-tests them itself, and reports clicks on our stdin. No mouse reporting
anywhere, no extra pane, and the frame row does not shrink the content area
so nothing resizes the PTY. v2 is parked in attic/ (not globbed by
install.sh) as the only non-zellij path, should one be wanted.
ANSWERS come from DEFAULTS until <cwd>/.pi-answers.md exists, after which
the file is authoritative. It is SEEDED with the defaults on first write,
which makes "the file wins" and "you still get the defaults" one rule
instead of a merge order to remember -- and it means deleting a line
genuinely removes a default, which an additive model could not express.
/answers add|rm persists in one step; hand-edit + /answers reload also works.
Labels are backtick-delimited in the markdown so a body may contain the
em-dash separator (as "no -- don't do that" does).
/answers list renders a table via appendEntry + registerEntryRenderer, so it
is scrollable and copyable in the transcript but never enters LLM context.
BUTTON IDS ARE DERIVED FROM THE LABEL (ans-ship), not positional. With a
mutable answer list, positional ids are a live bug: change the answers and
an in-flight click for a vanished button resolves to whatever now occupies
that index and fires the wrong prompt. Label-derived ids fail the allow-list
instead.
BUTTONS EXIST ONLY BETWEEN TURNS. While a run is in flight they are
withdrawn -- so a click cannot queue a second answer behind the one already
working -- and the status text stays up to advertise that alt+N still does.
Withdrawing sends `buttons` with an empty payload, NOT `clear`: the fork's
buttons verb assigns parse_status_bar_buttons(payload), which filter_maps
away every field lacking an id:label, while `clear` is the one verb that
would also wipe the status text. Restoring keys off agent_settled as much as
agent_end, since agent_end never fires on the abort/error paths -- an escape
cancel or a provider blowup would otherwise leave the frame permanently bare.
SECURITY. These are in-band escapes, so anything that writes to the pane (a
cat of a hostile file) can declare buttons and draw a misleading bar. A
click report is a hint that a user pressed something, not proof we put it
there: ids are validated against what we actually declared, and the action
is deliberately non-destructive -- it sends a prompt, it does not deploy.
Click reports arrive interleaved with ordinary stdin and are stripped, or
they would be typed into the editor; pi-tui's StdinBuffer holds an ESC ]
sequence until its ST/BEL terminator, so we always see whole reports.
Caps match the fork exactly (8 buttons, 512-char status, 32-char ids), so we
never rely on zellij silently dropping our excess. Answers past the 8th stay
on alt+N and the status line says so.
|