# Agent GUI Adapters for CLI LLMs

Session-derived notes for building a local GUI wrapper around different LLM agents.

## Goal pattern

Build a local-first browser UI that can talk to multiple agent backends through a shared protocol:

- `echo` adapter for smoke tests and offline development
- `cli` adapter for running external agent CLIs in one-shot mode
- future adapters for streaming / structured event protocols

## Useful design choices

- Keep the UI and backend separate, with `http://127.0.0.1:<port>` and `/health`.
- Normalize all backend outputs into a small event model:
  - `user_message`
  - `status`
  - `assistant_message`
  - `error`
- Store sessions in JSONL so the UI can reload history without a database.
- Represent avatar state as a small finite state machine (`idle`, `thinking`, `tool`, `speaking`, `error`).

## CLI adapter rules

- Store commands as an argv list, not a shell string.
- Replace placeholders like `{prompt}` and `{session_id}` before execution.
- Never use `shell=True` for user prompts.
- Default timeout should be generous enough for code agents, but configurable per agent.

## Claude Code working command

The GUI can call Claude Code directly in print mode using a command shaped like:

```json
["claude", "-p", "--output-format", "text", "--permission-mode", "default", "{prompt}"]
```

This was verified against the local CLI and returned a one-word response successfully.

## Verification pattern

- Use an echo adapter for deterministic tests.
- Add a `python -c` CLI smoke test to verify argv substitution.
- Smoke-test the live server with `curl /health` and `curl /api/chat`.
- Open the browser UI and confirm the message renders and the avatar changes state.

## Pitfalls

- Do not assume every CLI agent supports interactive TTY behavior in a browser wrapper.
- Do not keep Claude Code or similar tools in a disabled example state if the tested print-mode command works locally.
- Do not couple the GUI to a single agent implementation; keep the adapter config-driven.
- Do not hide the raw stdout/stderr of the agent, because debugging agent wrappers depends on seeing both.
