Rendering and state flow
renderAndRun()
render(<App><REPL /></App>)
start deferred prefetch work
waitUntilExit()
ink render()
ThemeProvider wraps tree
Ink.createRoot creates terminal renderer
Ink.render updates reconciler container
AppStateProvider
createStore(initialState)
provide store through React context
useAppState(selector)
useSyncExternalStore(store.subscribe, snapshot)
re-render when selected value changes
renderAndRun owns the terminal lifecycle
renderAndRun renders the root element, starts deferred prefetch work, waits until the UI exits, and then performs graceful shutdown. This keeps startup work and terminal cleanup in one lifecycle boundary.
The Ink wrapper sets up the React terminal renderer
The local Ink wrapper adds the theme provider and creates a root. The root creates an Ink instance, exposes render, unmount, clear, and waitUntilExit methods, and ultimately renders through a reconciler container wired to terminal streams.
External reference: Ink terminal UI.
AppState is broad but explicit
AppState includes tool permission context, bridge state, MCP data, plugin data, notifications, elicitation state, thinking state, prompt suggestions, hooks, and more. The default state constructor initializes these pieces so the REPL can assume the shape exists.
The external store keeps UI subscription simple
The store implementation is intentionally small: getState, setState, and subscribe. AppStateProvider creates the store once, then useAppState uses useSyncExternalStore to subscribe to selected values.
store = createStore(initialState)
setState(fn)
state = fn(state)
listeners.forEach(listener)
useAppState(selector)
useSyncExternalStore(
store.subscribe,
() => selector(store.getState())
)
External reference: React useSyncExternalStore.
The App wrapper provides runtime context
The top-level App component wraps children with frame-rate, stats, and AppState providers. Downstream components can then update shared state as model events arrive, MCP connections change, permissions are requested, and prompt suggestions are calculated.
Fresh state reads prevent stale tool context
The REPL constructs ToolUseContext from current store values when a query begins. This is important for long-running terminal sessions because MCP clients, permission context, resources, and app callbacks can change after the REPL component first rendered.