{
"component": "input",
"tier": "tooling",
"loop_stage": "perceive",
"summary": "The input component is the loop's front door for the operator. SelfwareEditor wraps reedline to provide an IDE-like REPL: history-backed, syntax-highlighted, with a Tab-cycling completion menu, Emacs/Vi modes, and keybindings that surface slash commands ('/'), cycle execution mode (Shift+Tab), toggle YOLO (Ctrl+Y), and open an external editor (Ctrl+X). read_line returns a ReadlineResult (Line, Interrupt, Eof, HostCommand). The command_registry catalogs CommandEntry items by CommandCategory and answers is_known_command. On the loop it is the primary perceive source for human intent and the injection point for control signals (mode changes, interrupts) — it turns raw keystrokes into either a task Message or a HostCommand that reshapes the driver.",
"loop_objects": ["SelfwareEditor", "ReadlineResult", "InputConfig", "InputMode", "CommandEntry", "CommandCategory", "Keybindings", "Message", "HostCommand", "SelfwareCompleter", "SelfwarePrompt"],
"context_basis": "Recommendations formed with input read in the context of the full engine (~600k budget framing), where the REPL feeds the agent loop and ExecuteHostCommand sentinels (__toggle_yolo__, __cycle_mode__) route control changes back into the driver.",
"examples": [
{
"id": "input-01",
"title": "Read a line as the loop's perceive entry",
"loop_stage": "perceive",
"pattern": "elicit-intent",
"intent": "Capture the operator's request to seed a turn.",
"how_it_shapes_the_loop": "read_line returns ReadlineResult::Line, which becomes the Message that starts the loop's Planning phase; it is the canonical perceive source for human intent.",
"loop_objects_touched": ["SelfwareEditor", "ReadlineResult", "Message"],
"wiring": {"inputs_from": ["operator keystrokes"], "outputs_to": ["agent Planning"]},
"touch_interaction": {"gesture": "tap", "canvas_action": "Tapping the input node focuses the on-canvas prompt and raises the soft keyboard.", "visual": "The prompt node glows and shows a blinking caret; the typed line renders live."},
"mini_scenario": "The operator types 'refactor the parser' and presses enter; the loop starts a turn from that line.",
"pitfall": "An empty line should be a no-op, not an empty task — filter blanks before starting a turn."
},
{
"id": "input-02",
"title": "Route a HostCommand into loop control",
"loop_stage": "control",
"pattern": "control-injection",
"intent": "Let keybindings change loop behavior without a task.",
"how_it_shapes_the_loop": "Lines wrapped in __sentinels__ return ReadlineResult::HostCommand, which the driver interprets as a control action (mode/YOLO) rather than a Message, mutating loop control directly.",
"loop_objects_touched": ["ReadlineResult", "HostCommand"],
"wiring": {"inputs_from": ["ExecuteHostCommand keybinding"], "outputs_to": ["agent control"]},
"touch_interaction": {"gesture": "double-tap", "canvas_action": "Double-tapping a control keycap emits its host command into the driver node.", "visual": "The command travels as a distinct control-colored pulse, not a normal text bubble."},
"mini_scenario": "Shift+Tab yields '__cycle_mode__'; the loop advances execution mode instead of running a task.",
"pitfall": "Detect sentinels by the __prefix/suffix__ shape — a user literally typing that must be handled deliberately."
},
{
"id": "input-03",
"title": "Complete a command via Tab menu",
"loop_stage": "perceive",
"pattern": "assisted-input",
"intent": "Speed correct command entry with a cycling menu.",
"how_it_shapes_the_loop": "The Tab binding tries hint-complete, inline complete, then opens the columnar menu and cycles — narrowing perceive input to valid commands the loop can act on.",
"loop_objects_touched": ["SelfwareCompleter", "SelfwareEditor", "CommandEntry"],
"wiring": {"inputs_from": ["command_registry names"], "outputs_to": ["read_line"]},
"touch_interaction": {"gesture": "flick", "canvas_action": "Flicking through the completion menu cycles candidate entries; tapping one accepts it.", "visual": "A single-column menu with a ' > ' marker highlights the current pick."},
"mini_scenario": "The operator types 'chec', hits Tab, and the menu offers /checkpoint which they accept.",
"pitfall": "Completion sources must stay in sync with the registry — a stale list offers dead commands."
},
{
"id": "input-04",
"title": "Open the slash-command menu on '/'",
"loop_stage": "perceive",
"pattern": "quick-command",
"intent": "Surface commands the instant the user types '/'.",
"how_it_shapes_the_loop": "The '/' keybinding inserts the char and immediately opens the completion menu, funneling the operator toward registered CommandEntry actions the loop understands.",
"loop_objects_touched": ["SelfwareEditor", "CommandEntry", "SelfwareCompleter"],
"wiring": {"inputs_from": ["command_registry"], "outputs_to": ["read_line"]},
"touch_interaction": {"gesture": "tap", "canvas_action": "Tapping the '/' key on the soft keyboard pops the slash-command overlay.", "visual": "A menu unfurls under the caret listing slash commands by category."},
"mini_scenario": "Typing '/' shows /undo, /timeline, /stats; the operator taps /stats to inspect the loop.",
"pitfall": "The menu must reflect only known commands — is_known_command should back what it offers."
},
{
"id": "input-05",
"title": "Cycle execution mode with Shift+Tab",
"loop_stage": "control",
"pattern": "mode-cycle",
"intent": "Switch how autonomously the loop acts.",
"how_it_shapes_the_loop": "Shift+Tab emits __cycle_mode__ to rotate normal → auto-edit → yolo → daemon, changing whether the loop pauses for confirmations at each act.",
"loop_objects_touched": ["HostCommand", "ReadlineResult"],
"wiring": {"inputs_from": ["Shift+BackTab binding"], "outputs_to": ["agent execution mode"]},
"touch_interaction": {"gesture": "two-finger-rotate", "canvas_action": "Rotating two fingers on the mode node cycles the execution mode ring.", "visual": "A ring badge steps color: grey → amber → red → blue as autonomy rises."},
"mini_scenario": "The operator rotates to auto-edit so file edits apply without a per-edit prompt.",
"pitfall": "Landing on yolo removes all confirmations — the UI must make that transition unmistakable."
},
{
"id": "input-06",
"title": "Toggle YOLO with Ctrl+Y",
"loop_stage": "control",
"pattern": "guard-toggle",
"intent": "Flip confirmation-free execution on or off.",
"how_it_shapes_the_loop": "Ctrl+Y emits __toggle_yolo__; the driver flips the confirmation guard, directly reshaping whether act transitions gate.",
"loop_objects_touched": ["HostCommand", "ReadlineResult"],
"wiring": {"inputs_from": ["Ctrl+Y binding"], "outputs_to": ["agent confirmation guard"]},
"touch_interaction": {"gesture": "double-tap", "canvas_action": "Double-tapping the YOLO toggle flips the guard state.", "visual": "The toggle snaps red-on / grey-off with a warning pulse when enabling."},
"mini_scenario": "The operator hits Ctrl+Y to enable YOLO for a trusted batch, then again to restore prompts.",
"pitfall": "A stuck YOLO state is dangerous — always reflect the live guard state, never a stale label."
},
{
"id": "input-07",
"title": "Handle Ctrl+C as an interrupt signal",
"loop_stage": "control",
"pattern": "cancel-signal",
"intent": "Let the operator abort the current turn.",
"how_it_shapes_the_loop": "Signal::CtrlC maps to ReadlineResult::Interrupt, which the driver treats as a cancel, transitioning a running loop toward a clean stop.",
"loop_objects_touched": ["ReadlineResult", "SelfwareEditor"],
"wiring": {"inputs_from": ["Ctrl+C"], "outputs_to": ["agent cancel"]},
"touch_interaction": {"gesture": "long-press", "canvas_action": "Long-pressing the stop node sends an interrupt to the active turn.", "visual": "A red stop glyph pulses; the running node winds down to a halted state."},
"mini_scenario": "A long run goes astray; the operator presses Ctrl+C and the loop stops before the next act.",
"pitfall": "Interrupt should cancel gracefully at a safe boundary, not corrupt in-flight state."
},
{
"id": "input-08",
"title": "Handle Ctrl+D as end-of-input",
"loop_stage": "control",
"pattern": "session-end",
"intent": "Exit the REPL cleanly.",
"how_it_shapes_the_loop": "Signal::CtrlD maps to ReadlineResult::Eof, ending the perceive stream so the loop tears down the session.",
"loop_objects_touched": ["ReadlineResult", "SelfwareEditor"],
"wiring": {"inputs_from": ["Ctrl+D"], "outputs_to": ["session teardown"]},
"touch_interaction": {"gesture": "flick", "canvas_action": "Flicking the input node down and off-screen ends the session like an EOF.", "visual": "The prompt node slides away and the canvas dims to a closed state."},
"mini_scenario": "The operator presses Ctrl+D on an empty prompt; the loop persists state and exits.",
"pitfall": "Distinguish Eof from Interrupt — Eof ends the session, Interrupt only cancels a turn."
},
{
"id": "input-09",
"title": "Insert a newline with Ctrl+J",
"loop_stage": "perceive",
"pattern": "multiline-input",
"intent": "Compose multi-line prompts without submitting.",
"how_it_shapes_the_loop": "Ctrl+J inserts a newline instead of submitting, letting the operator build a richer Message before the loop consumes it.",
"loop_objects_touched": ["SelfwareEditor", "Message"],
"wiring": {"inputs_from": ["Ctrl+J binding"], "outputs_to": ["read_line buffer"]},
"touch_interaction": {"gesture": "tap", "canvas_action": "Tapping a soft return key adds a line to the prompt without sending it.", "visual": "The prompt box grows to accommodate the new line; the caret drops down."},
"mini_scenario": "The operator pastes a multi-line spec using Ctrl+J between lines, then submits it whole.",
"pitfall": "Only Enter submits — don't let a bare newline key double as submit or prompts fragment."
},
{
"id": "input-10",
"title": "Edit the prompt in an external editor",
"loop_stage": "perceive",
"pattern": "offload-composition",
"intent": "Compose a long prompt in $EDITOR.",
"how_it_shapes_the_loop": "Ctrl+X opens the buffer in VISUAL/EDITOR/vi via a temp file, returning the edited text as the loop's next Message.",
"loop_objects_touched": ["SelfwareEditor", "Message"],
"wiring": {"inputs_from": ["Ctrl+X binding", "$EDITOR"], "outputs_to": ["read_line buffer"]},
"touch_interaction": {"gesture": "long-press", "canvas_action": "Long-pressing the input node opens a full-screen editor sheet for the prompt.", "visual": "The prompt expands to a full editor pane; on close its content returns to the caret."},
"mini_scenario": "The operator hits Ctrl+X, writes a detailed brief in vim, saves, and the loop receives it.",
"pitfall": "The temp file is per-process — clean it up so stale prompt drafts don't leak."
},
{
"id": "input-11",
"title": "Search history with Ctrl+R",
"loop_stage": "perceive",
"pattern": "recall-prior-intent",
"intent": "Reuse a previous command from history.",
"how_it_shapes_the_loop": "FileBackedHistory (max 10k) lets the operator recall past lines, so the loop can be re-seeded with a proven prior Message.",
"loop_objects_touched": ["SelfwareEditor", "InputConfig", "Message"],
"wiring": {"inputs_from": ["history file"], "outputs_to": ["read_line buffer"]},
"touch_interaction": {"gesture": "flick", "canvas_action": "Flicking up on the input node scrolls back through history entries.", "visual": "Prior commands surface as ghosted suggestions; the match highlights as you refine."},
"mini_scenario": "The operator recalls 'run the e2e suite' from last session and re-submits it.",
"pitfall": "Lines beginning with a space are excluded from history — don't rely on those being recallable."
},
{
"id": "input-12",
"title": "Toggle Emacs/Vi input mode",
"loop_stage": "foundation",
"pattern": "modal-editing",
"intent": "Match the operator's preferred editing style.",
"how_it_shapes_the_loop": "toggle_vim_mode rebuilds the editor with the other InputMode; it changes how keystrokes are parsed, a foundation choice that doesn't alter loop control.",
"loop_objects_touched": ["InputMode", "InputConfig", "SelfwareEditor"],
"wiring": {"inputs_from": ["mode toggle"], "outputs_to": ["editor keybindings"]},
"touch_interaction": {"gesture": "double-tap", "canvas_action": "Double-tapping the mode chip switches between Emacs and Vi editing.", "visual": "The chip flips label EMACS/VI; a Vi mode adds a normal/insert indicator."},
"mini_scenario": "A Vi user double-taps to Vi mode and navigates the prompt with hjkl.",
"pitfall": "Rebuilding the editor must preserve history and completers — don't drop them on toggle."
},
{
"id": "input-13",
"title": "Accept an inline hint with Right arrow",
"loop_stage": "perceive",
"pattern": "predictive-accept",
"intent": "Adopt the suggested completion of a line.",
"how_it_shapes_the_loop": "Right arrow completes the history hint if present, else moves the cursor — smoothing perceive so common Messages type themselves.",
"loop_objects_touched": ["SelfwareEditor", "Message"],
"wiring": {"inputs_from": ["DefaultHinter"], "outputs_to": ["read_line buffer"]},
"touch_interaction": {"gesture": "flick", "canvas_action": "Flicking right on the prompt accepts the ghosted hint completion.", "visual": "The dimmed hint solidifies into the line as it is accepted."},
"mini_scenario": "The operator types 'git ' and the hint 'git status' appears; a right-flick accepts it.",
"pitfall": "Right must fall back to cursor-move when there's no hint, or navigation breaks mid-line."
},
{
"id": "input-14",
"title": "Dismiss the menu with Escape",
"loop_stage": "control",
"pattern": "abort-selection",
"intent": "Close a menu without committing a choice.",
"how_it_shapes_the_loop": "Esc maps to ReedlineEvent::Esc, closing the completion menu so the operator resumes free input without dispatching a command.",
"loop_objects_touched": ["SelfwareEditor", "SelfwareCompleter"],
"wiring": {"inputs_from": ["Esc binding"], "outputs_to": ["read_line"]},
"touch_interaction": {"gesture": "tap", "canvas_action": "Tapping outside the menu overlay dismisses it without selecting.", "visual": "The menu collapses and the caret returns to the unaltered line."},
"mini_scenario": "The operator opens the slash menu by accident and taps away to dismiss it.",
"pitfall": "Esc must not clear the typed line — only the menu; losing the buffer frustrates the operator."
},
{
"id": "input-15",
"title": "Catalog commands by CommandCategory",
"loop_stage": "foundation",
"pattern": "registry-index",
"intent": "Organize available commands for discovery.",
"how_it_shapes_the_loop": "command_registry groups CommandEntry by CommandCategory and exposes command_names/description; it is the foundation catalog the perceive layer and palette draw from.",
"loop_objects_touched": ["CommandEntry", "CommandCategory"],
"wiring": {"inputs_from": ["static registry"], "outputs_to": ["completer", "palette", "help"]},
"touch_interaction": {"gesture": "pinch", "canvas_action": "Pinch-out on the registry node fans commands into category clusters.", "visual": "Commands group under category headers, each cluster color-coded."},
"mini_scenario": "The palette lists commands grouped as session, safety, and inspection categories.",
"pitfall": "Every offered command must appear in the registry — completions outside it dispatch nothing."
},
{
"id": "input-16",
"title": "Validate a command before dispatch",
"loop_stage": "verify",
"pattern": "known-command-gate",
"intent": "Reject unknown slash commands cleanly.",
"how_it_shapes_the_loop": "is_known_command gates dispatch so a mistyped '/xyz' is caught at verify rather than becoming a silent no-op or a task.",
"loop_objects_touched": ["CommandEntry", "ReadlineResult"],
"wiring": {"inputs_from": ["read_line line"], "outputs_to": ["command dispatch or error"]},
"touch_interaction": {"gesture": "tap", "canvas_action": "Tapping an unrecognized command flashes the input node red with a 'not a command' hint.", "visual": "The token underlines red; a suggestion of the nearest known command appears."},
"mini_scenario": "The operator types '/undoo'; the gate rejects it and suggests /undo.",
"pitfall": "Don't treat an unknown slash command as a plain task — surface the error instead."
},
{
"id": "input-17",
"title": "Render context in the prompt",
"loop_stage": "perceive",
"pattern": "situated-prompt",
"intent": "Show model, step, and context usage at the prompt.",
"how_it_shapes_the_loop": "set_prompt_full_context updates SelfwarePrompt with model/step/context_pct, so the operator perceives the loop's budget state right where they type.",
"loop_objects_touched": ["SelfwarePrompt", "SelfwareEditor"],
"wiring": {"inputs_from": ["agent (model, step, context %)"], "outputs_to": ["prompt render"]},
"touch_interaction": {"gesture": "tap", "canvas_action": "Tapping the prompt chip expands the model/step/context readout.", "visual": "The prompt shows 'Qwen3.5 · step 4 · 62%'; the percent tints as it climbs."},
"mini_scenario": "At 88% context the prompt turns amber, nudging the operator to compact before continuing.",
"pitfall": "Keep context_pct fresh — a stale prompt understates real budget pressure."
},
{
"id": "input-18",
"title": "Highlight input syntax live",
"loop_stage": "perceive",
"pattern": "syntactic-feedback",
"intent": "Give immediate visual structure to what's typed.",
"how_it_shapes_the_loop": "SelfwareHighlighter colors the line as it's entered, improving the fidelity of the perceive channel so the operator catches mistakes before submitting.",
"loop_objects_touched": ["SelfwareHighlighter", "SelfwareEditor"],
"wiring": {"inputs_from": ["typed line"], "outputs_to": ["prompt render"]},
"touch_interaction": {"gesture": "tap", "canvas_action": "Tapping the input node toggles syntax highlighting on the live line.", "visual": "Commands, paths, and flags render in distinct colors as they're typed."},
"mini_scenario": "A slash command colors differently from a plain task, so the operator sees which they're issuing.",
"pitfall": "Highlighting is cosmetic — never let a color rule change what the command actually does."
},
{
"id": "input-19",
"title": "Seed completer with live tool names",
"loop_stage": "foundation",
"pattern": "context-aware-completion",
"intent": "Complete against the actually-registered tools.",
"how_it_shapes_the_loop": "InputConfig.tool_names populates SelfwareCompleter so completion reflects the loop's real tool set, keeping perceive aligned with act capability.",
"loop_objects_touched": ["InputConfig", "SelfwareCompleter"],
"wiring": {"inputs_from": ["ToolRegistry names"], "outputs_to": ["completer"]},
"touch_interaction": {"gesture": "flick", "canvas_action": "Flicking the completer node refreshes it against the current tool registry.", "visual": "Tool suggestions repopulate; newly registered tools appear in the menu."},
"mini_scenario": "After MCP tools register, the completer offers them so the operator can reference them by name.",
"pitfall": "Rebuild the completer when tools change — a fixed list omits dynamically registered tools."
},
{
"id": "input-20",
"title": "Persist history across sessions",
"loop_stage": "learn",
"pattern": "durable-recall",
"intent": "Keep command history between runs.",
"how_it_shapes_the_loop": "FileBackedHistory writes to the data-local history file, so the learn stage carries prior operator intent forward into future loops.",
"loop_objects_touched": ["InputConfig", "SelfwareEditor"],
"wiring": {"inputs_from": ["prior session lines"], "outputs_to": ["next session history"]},
"touch_interaction": {"gesture": "long-press", "canvas_action": "Long-pressing the history node shows the persisted command log across sessions.", "visual": "A timeline of past commands scrolls, grouped by session date."},
"mini_scenario": "Restarting selfware, the operator flicks up and recalls yesterday's build command instantly.",
"pitfall": "Ensure the history parent dir exists (create_dir_all) or persistence silently fails."
}
]
}