# AIRI Coding CLI + Chafa Architecture ## Purpose Define a narrow architecture for a terminal-facing AIRI coding CLI that can optionally render an animated avatar through `chafa`, without coupling terminal presentation to the `computer-use-mcp` runtime. This document is a boundary and contract proposal. It is not an implementation plan for a full CLI in this PR. ## Current Status As of 2026-04-26, the main branch does not expose a stable coding runner event stream. A CLI that renders real progress needs a runner contract first. Starting with terminal animation before that contract exists would produce presentation without reliable runtime state. The correct dependency order is: 1. Define a coding runner event contract. 2. Emit deterministic runner events from the coding runner. 3. Build a text-first CLI that consumes those events. 4. Add optional `chafa` avatar rendering as a display adapter. ## Confirmed Decisions 1. Future CLI package location: `packages/airi-cli`. 2. First integration channel: in-process runner callback. 3. JSONL over stdin/file remains a follow-up adapter for replay, fixtures, and tooling interoperability. 4. `chafa` is optional presentation only. ## Hard Boundaries 1. No `chafa` dependency belongs in `services/computer-use-mcp` core runtime. 2. `computer-use-mcp` must remain headless-safe and CI-safe. 3. The CLI consumes runner events; it must not own planning, tool execution, or runtime policy. 4. Missing `chafa` must degrade to plain text rendering. 5. Non-TTY output must support plain text or JSONL output. 6. CLI rendering changes must not be mixed with coding memory or runner runtime refactors. ## Proposed Location Split - Core runtime: `services/computer-use-mcp`. - CLI package: `packages/airi-cli`. - Event contract: initially colocated with the runner that emits it, then exported for CLI consumption. - Text renderer: `packages/airi-cli/src/renderers/text.ts`. - Chafa adapter: `packages/airi-cli/src/renderers/chafa-avatar.ts`. - JSONL adapter: `packages/airi-cli/src/adapters/jsonl.ts`. ## Runner Event Contract Draft Use an append-only event envelope. The first transport should be an in-process callback. JSONL is an adapter over the same envelope. ```ts interface RunnerEventEnvelope { at: string kind: TKind payload: TPayload runId: string seq: number } ``` Contract rules: - `seq` is strictly increasing per `runId`. - `at` is an ISO timestamp. - events are append-only; later events must not rewrite earlier events. - crash and timeout paths must emit deterministic terminal events. ## Minimum Event Kinds - `run_started` - `preflight_started` - `preflight_completed` - `step_started` - `tool_call_started` - `tool_call_completed` - `assistant_message` - `step_timeout` - `report_status` - `run_finished` - `run_crashed` ## Minimum Payloads `run_started`: ```ts interface RunStartedPayload { maxSteps: number stepTimeoutMs: number taskGoal: string workspacePath: string } ``` `step_started`: ```ts interface StepStartedPayload { maxSteps: number stepIndex: number } ``` `tool_call_started`: ```ts interface ToolCallStartedPayload { argsSummary: string toolName: string } ``` `tool_call_completed`: ```ts interface ToolCallCompletedPayload { error?: string ok: boolean status?: string summary: string toolName: string } ``` `assistant_message`: ```ts interface AssistantMessagePayload { text: string } ``` `report_status`: ```ts interface ReportStatusPayload { status: 'blocked' | 'completed' | 'failed' summary?: string } ``` `run_finished`: ```ts interface RunFinishedPayload { error?: string finalStatus: 'blocked' | 'completed' | 'failed' | 'timeout' totalSteps: number } ``` ## CLI Architecture The CLI should have three separable layers. ### 1. Input Adapter Input adapters convert transport-specific input into `RunnerEventEnvelope` events. Initial adapters: - in-process runner callback - stdin JSONL - JSONL file replay ### 2. State Reducer The reducer builds deterministic `CliViewState` from events. Rules: - no terminal I/O in the reducer - no process spawning in the reducer - no animation timing in the reducer - reducer tests should use fixture event streams ### 3. Renderer Adapter Renderer adapters consume `CliViewState`. Initial renderers: - text renderer, always available - JSONL passthrough renderer for tooling - optional `chafa` avatar renderer ## Chafa Adapter The `chafa` adapter should: - probe `chafa` binary availability at startup - disable itself when stdout is not a TTY - convert sprite or frame assets to ANSI frames - pace animation independently from runner event rate - fall back to text-only rendering on failure Initial CLI flags: - `--avatar=chafa|none` - `--no-avatar` - `--events=runner|stdin|jsonl-file` - `--output=pretty|jsonl` ## Testing Strategy 1. Contract tests for event schema and monotonic `seq`. 2. Reducer tests from fixture JSONL streams. 3. Text renderer snapshot tests. 4. Chafa adapter tests with mocked binary probing and mocked child process execution. 5. CI defaults to text mode and does not require `chafa`. ## Delivery Stages ### Stage 1: Contract - define the runner event envelope - add success, failure, and timeout fixture streams - do not render terminal UI yet ### Stage 2: Text CLI - scaffold `packages/airi-cli` - consume runner events - render run, step, tool, error, and final report state in plain text ### Stage 2.5: JSONL Adapter - support stdin JSONL replay - support JSONL file replay - use fixtures for offline demos and tests ### Stage 3: Chafa Renderer - add optional `chafa` renderer - keep plain text as the default fallback - keep CI and headless runs independent from `chafa` ## Acceptance Criteria 1. No `chafa` dependency is added under `services/computer-use-mcp`. 2. CLI works when `chafa` is not installed. 3. CI does not require `chafa`. 4. The reducer is testable from mocked event streams. 5. Terminal output degrades to plain text. 6. Runner behavior is not changed for animation-first UX. ## Review Checklist Layering: - no renderer code in `computer-use-mcp` runtime - no `chafa` dependency in runtime packages - CLI code stays under `packages/airi-cli` Runtime: - runner remains headless-safe - runner emits events without depending on terminal state - failures and timeouts produce deterministic events Fallback: - missing `chafa` does not fail CLI execution - non-TTY mode uses text or JSONL output Scope: - do not mix CLI rendering with coding memory changes - do not mix CLI rendering with desktop/browser runtime changes ## Out of Scope - long-term memory promotion and governance - desktop/browser runtime refactors - terminal animation before a runner event contract exists - changing coding runner completion semantics for display purposes ## Suggested Follow-up PRs - `feat(cli): define coding runner event contract` - `feat(cli): scaffold airi coding cli text renderer` - `feat(cli): add optional terminal AIRI avatar renderer with chafa`