Everything that turns the agent core into an interactive terminal chat. main.py at the
repo root is a thin entry point — it wires up skills/MCP discovery at startup and then hands
off to this package for the actual input loop.
- display.py - ANSI colors, the startup banner,
Spinner(the animatedAI ❯ thinking...status line shown while the agent works), the arrow-key selector (select_one, used by the startup online/offline prompt and the/modelspicker), and the block-layout helpers every command prints through (print_heading,print_rows,print_fields,column_width) so padding and color are decided in one place. - session.py -
Session— owns the active model/agent, chat history, and history-trimming rules (trim_history,compact_turn,keep_only_latest_image) that keep the rolling conversation under the model's context budget. - commands.py - one
cmd_*function per slash command (/help,/new,/clear,/purge,/memory,/status,/models— an arrow-key picker, withdefault/infovariants —/networkand/network info,/image,/tools,/skills,/mcp) plushandle_command(), the dispatch tablemain.py's input loop calls.apply_network_mode()lives here too: it is the one place the live mode, the persisted setting, and memory's embedder policy are changed together, and startup calls it for exactly that reason. - turn.py -
run_turn(): builds the message list for one user turn, drivessrc.engine.turn_engine.run_turn(), and renders eachTurnEventas it arrives (spinner label, trace lines, live answer tokens) before folding tool-call intermediates back out of history once the turn completes.
- Write a
cmd_<name>(session, ...)function incommands.pythat prints whatever it needs to. - Add a branch to
handle_command()matching/<name>. - Add a row to
_HELP_SECTIONSunder the group whose subject it acts on (Chat,Models,Network,Memory,Inspect,Session, or a new one) so/helppicks it up automatically. - Print through
display.py's helpers rather than hand-rolledprint()formatting, so the new command lines up with the existing ones (/helpand/network infopad their name and label columns to one width across every block, which only works if all output goes through them).
No other file needs to change — main.py only ever calls handle_command(), never a specific
command function.
Nothing in this package is imported by src/engine/ or the tool/model/skill registries —
only main.py reaches into it. That keeps the agent core and its registries usable headless
(e.g. a future web or API frontend) without dragging in print()/input() terminal code.