Skip to content

feat: Codex-style terminal UI (a3s-code TUI)#82

Open
ZhiXiao-Lin wants to merge 11 commits into
mainfrom
feat/code-tui
Open

feat: Codex-style terminal UI (a3s-code TUI)#82
ZhiXiao-Lin wants to merge 11 commits into
mainfrom
feat/code-tui

Conversation

@ZhiXiao-Lin

Copy link
Copy Markdown
Contributor

A Codex-class interactive terminal UI for the agent, in a new crates/code/cli crate (binary a3s-code), built on the a3s-tui TEA framework and driving AgentSession::stream().

Features (all real-model verified against gpt-4o)

  • Streaming markdown answers with syntax-highlighted code
  • Working file edits (HITL confirmation wired) with inline colored diffs + approve/deny modal and /auto approval mode
  • Action log: every tool's command/path/pattern, live stdout streaming, syntax-highlighted file output
  • Esc interrupt · reasoning/thinking · model + token usage · ↑/↓ history · subagent activity · mouse scroll
  • Session resume across process restarts (per-cwd FileSessionStore)
  • cwd context + slash commands (/help /clear /auto /exit)

Verified end-to-end: streaming, tool calls execute (write → file created with approval), session resume restores context. Rendering logic unit-tested (4 tests); every commit clippy + fmt clean.

Build/CI impact: none

crates/code/cli is its own [workspace] (path-deps a3s-code-core + a3s-tui), so it's excluded from a3s-code's workspace and CI — merging adds the code without touching core/SDK builds or release.

Shippability follow-up (separate, needs decisions)

To release as a distributed binary, a3s-tui must be reachable standalone — publish it to crates.io (needs the registry token) or switch the cli to a git dep — then add a binary build to the release pipeline. Until then it runs in the monorepo via a3s code.

claude added 11 commits June 23, 2026 14:53
A new `crates/code/cli` crate (binary `a3s-code`) built on the a3s-tui TEA
framework. It drives an AgentSession via `session.stream()` and renders the
AgentEvent stream as a live chat transcript:

- streaming assistant text (StreamingMarkdown) + working spinner
- tool-call lifecycle lines (ToolStart/ToolEnd)
- HITL: ConfirmationRequired -> approve/deny modal -> confirm_tool_use
- multi-line input, scrollback, slash commands (/clear, /exit), Ctrl+C quit

The async bridge is a self-re-issuing "pump" command that drains the agent's
mpsc event receiver into the synchronous TEA update loop one event at a time.

Own `[workspace]` root with path deps on a3s-code-core (../core) and a3s-tui
(../../tui), so it does not affect a3s-code's main workspace and a standalone
clone of this repo keeps building unchanged. (a3s-tui must be published to
crates.io before this can ship as a release artifact.)

A headless `A3S_CODE_TUI_SMOKE=1` mode exercises the same stream/AgentEvent path
without a TTY. Verified end-to-end against a real model (gpt-4o via the gateway):
"what is 2 + 2?" -> streamed "2 + 2 equals 4." then End.
Next increment on the agent TUI:
- Esc interrupts an in-progress run (session.cancel()), with an "interrupting…"
  note; the stream then closes and the turn finalizes normally.
- ReasoningDelta is rendered live as dimmed "💭 thinking" above the answer and
  cleared when the answer finalizes (useful for reasoning models).
- On completion, show token usage from the End event
  (total / prompt / completion).
- /help slash command; refreshed welcome + status-bar hints (Esc interrupt).

Build + clippy + fmt clean; regression smoke against gpt-4o still streams a
valid answer.
The write/edit tools already emit before/after/file_path in ToolEnd metadata
(edit.rs even notes it's "so frontend can show Monaco diff"), so this is a
TUI-only change — no core change needed.

- render_tool_end: for tool results carrying before/after/file_path, render a
  colored line diff (similar::TextDiff) with +adds/-dels header and green/red
  changed lines (context lines omitted; capped at 80 lines). Other tools keep
  the status + output-head line.
- Adds serde_json + similar deps; unit tests for the diff vs status-line paths.

patch tool doesn't emit before/after, so it falls back to the status line.
- ↑/↓ recall submitted prompts into the input (single-line input only, so
  multi-line editing keeps normal cursor movement); going forward past the
  newest entry returns to a fresh input.
- Render SubagentStart / SubagentEnd as dimmed "↳ subagent <agent>" lines so
  delegated work is visible in the transcript.
- Refreshed /help.

Build + tests + clippy + fmt clean; regression smoke against gpt-4o still
streams correctly.
- Enable mouse support; scroll wheel scrolls the transcript viewport.
- Status bar now shows the model (captured from the first turn's response
  metadata) and cumulative session token usage.

Build + tests + clippy + fmt clean.
Persist the conversation to <cwd>/.a3s/tui-sessions (FileSessionStore, fixed
"tui-default" id, auto-save). On launch, resume that session if it exists and
seed the transcript with the prior user/assistant turns; otherwise start fresh.
Relaunching in the same directory continues the conversation with full agent
context.

Also fixes run_smoke to drain the stream fully and await the stream task, so the
background auto-save completes before exit (the headless probe was exiting too
early to persist).

Verified end-to-end against gpt-4o: run 1 "remember 42" → run 2 (fresh process)
recalls "42" from the persisted session.
Codex-style action log: accumulate the streamed tool-input JSON and show the
tool's primary argument on completion — "✓ bash — npm test", "✓ read —
src/main.rs", "✓ grep — TODO" — instead of just the tool name. The HITL approval
modal now renders args as pretty-printed JSON.

Unit tests for arg-summary extraction and the summary in the result line.
- /auto toggles Codex-style approval mode: while on, tool-confirmation prompts
  are auto-approved (shown as "⚡ auto-approved <tool>") instead of opening the
  modal.
- Welcome screen shows the working directory for context; /help lists /auto.
When a tool returns file/code content (read/edit on a known extension), render
the output as a syntax-highlighted fenced block via a3s-tui's Markdown (syntect),
matching how Codex shows file content. Other tool output stays dimmed. Also make
the headless smoke probe print tool output for debugging.
Render ToolOutputDelta live — the tail of a running tool's stdout is shown
dimmed under the action (like watching a command run in Codex), then cleared
when the tool completes.
File-modifying tools (write/edit/patch) require a confirmation manager;
without one they fail with "requires confirmation but no HITL confirmation
manager is configured" — so the agent could never edit files in the TUI.

Enable ConfirmationPolicy on the session (long timeout so the approve/deny
modal never expires). Now write/edit emit ConfirmationRequired → the TUI modal
(or /auto) approves → the tool runs → the diff renders. Verified end-to-end
against gpt-4o: "create note.txt with hello" → approved → file written, exit 0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants