= app-cli == purpose Command-line interface built with Clikt that connects to the Correx server to start, manage, and observe sessions. Designed for scripted/headless usage and non-interactive environments. == responsibilities * Start sessions by workflow ID via HTTP POST to the server * Attach to a session's WebSocket stream and relay lifecycle events (session, stage, tool, approval) to stdout * Prompt for approval decisions (approve / reject / steer) when a TTY is available * Support `--json` output for machine parsing and `--quiet` for error-only mode * List sessions, resume/cancel sessions, fetch session events * List inference providers and their health status * Send approval decisions to the server via HTTP == non-responsibilities * Does not maintain persistent state or local event cache * Does not contain domain orchestration logic * Does not perform inference or tool execution * Does not implement the approval engine — only forwards user decisions * Does not run a server == key types === CorrexCli * **kind**: `CliktCommand` (root) * **purpose**: Top-level CLI command. Defines global flags `--json` and `--quiet`. === RunCommand * **kind**: `CliktCommand` (subcommand) * **purpose**: Starts a session for a workflow and streams events to stdout. Supports `--workflow`, `--session`, `--auto-approve`, `--host`, `--port`. === CliWsClient * **kind**: class * **purpose**: Thin Ktor WebSocket client wrapping `HttpClient.webSocket`. Calls `onMessage` callback per frame; returns `false` from callback to close the stream. === SystemExitException * **kind**: exception * **purpose**: Propagates exit codes (0 = success, 1 = session failure, 2 = rejection in non-TTY mode). === RunContext / ApprovalContext * **kind**: internal data classes * **purpose**: Carry session ID, JSON flag, auto-approve flag, TTY status through the message handling loop. == event flow *inbound:* WebSocket frames from server encoded as JSON `ServerMessage` variants. The CLI decodes the `type` field from a raw `JsonObject` (not deserialized via `ProtocolSerializer`). *outbound:* `ApprovalResponsePayload` sent as text frames on the WebSocket session. == integration points * `apps:cli` depends only on external libraries: Clikt (CLI framework) and Ktor client (HTTP + WebSockets) * Talks to the server at `http://{host}:{port}` via REST and `ws://{host}:{port}/sessions/{id}/stream` == invariants * `CliWsClient.sessionStream` breaks out of the frame loop when `onMessage` returns `false` * Auto-approve without a TTY is impossible — if `--auto-approve` is not set and no TTY, all approvals are rejected * Exit code 2 means the user rejected an approval in non-interactive mode * `RunCommand` creates one session and one WebSocket connection per invocation == PlantUML diagram [plantuml, app-cli, "png"] ---- include::../../diagrams/app-cli.puml[] ---- == known issues * JSON output format is hand-constructed in `printLine()` rather than using a serializer — field ordering and escaping may diverge from `ServerMessage` serialization * `--session` (resume) acceptance on the server side is not yet implemented — the parameter is forwarded but the server's response is untested * `resolveDecision` blocks on stdin in TTY mode — no timeout mechanism for user input == open questions * Should session resume reconnect to an existing WebSocket stream or replay from the event store? Currently the parameter is accepted but behavior is undefined. * Should `RunCommand` support multiple sessions concurrently (e.g., `--watch` mode)?