diff --git a/aai_cli/code_agent/tui.py b/aai_cli/code_agent/tui.py
index 2871038..d5734cf 100644
--- a/aai_cli/code_agent/tui.py
+++ b/aai_cli/code_agent/tui.py
@@ -48,6 +48,7 @@
VOICE_FRAMES,
_spinner_text,
_status_text,
+ copy_note,
voicebar_markup,
)
from aai_cli.code_agent.voice_ui import _VoiceIO, _VoiceLegs
@@ -82,12 +83,11 @@ class CodeAgentApp(_VoiceLegs):
#promptmark {{ width: 3; color: {banner.BRAND_HEX}; content-align: center middle; }}
#prompt {{ border: none; background: #000000; padding: 0; }}
/* Shown in place of the prompt while voice capture is on (Ctrl-V brings the prompt back). */
- #voicebar {{ dock: bottom; height: 3; background: #000000; border: round {banner.BRAND_HEX};
- margin: 1 1; content-align: center middle; display: none; }}
+ #voicebar {{ dock: bottom; height: 3; background: #000000; border: round {banner.BRAND_HEX}; margin: 1 1; content-align: center middle; display: none; }}
/* In normal flow below the 1fr log, so it sits just above the docked prompt bar. */
- #spinner {{ height: 1; background: #000000; padding: 0 2;
- color: {banner.BRAND_HEX}; display: none; }}
- #status {{ dock: bottom; height: 1; background: #000000; padding: 0 1; }}
+ #spinner {{ height: 1; background: #000000; padding: 0 2; color: {banner.BRAND_HEX}; display: none; }}
+ /* Two rows: the mode/cwd/branch/voice line and the dim key-legend below it. */
+ #status {{ dock: bottom; height: 2; background: #000000; padding: 0 1; }}
"""
TITLE = "AssemblyAI Code"
# Ctrl-C quits (in addition to Ctrl-Q); the built-in command palette is removed.
@@ -245,12 +245,10 @@ def _finalize_reply(self, text: str) -> None:
msg.finalize(text)
def action_copy_last(self) -> None:
- """Copy the most recent assistant reply to the system clipboard."""
+ """Copy the most recent assistant reply to the system clipboard, noting the outcome."""
import pyperclip
- if self._last_reply:
- pyperclip.copy(self._last_reply)
- self._note("(copied last reply to clipboard)")
+ self._note(copy_note(self._last_reply, pyperclip.copy))
def action_toggle_output(self) -> None:
"""Ctrl-O: expand/collapse the most recent tool output (a no-op if there's none)."""
@@ -321,6 +319,7 @@ def action_toggle_voice(self) -> None:
self._refresh_status()
self._sync_input_mode() # show/hide the text box vs. the listening affordance
if self._voice_paused:
+ self._voice.cancel() # release the mic now, don't leave a capture running unseen
self.notify("Voice off — type your request")
elif not self._turn_running():
self.notify("Voice on — listening")
diff --git a/aai_cli/code_agent/tui_status.py b/aai_cli/code_agent/tui_status.py
index e163ea0..95fca7a 100644
--- a/aai_cli/code_agent/tui_status.py
+++ b/aai_cli/code_agent/tui_status.py
@@ -7,11 +7,16 @@
from __future__ import annotations
from pathlib import Path
+from typing import TYPE_CHECKING
+import pyperclip
from rich.markup import escape
from aai_cli.ui import theme
+if TYPE_CHECKING:
+ from collections.abc import Callable
+
# Animated meter for the voice bar — a 3-cell block-char pulse (BMP, single-width, no emoji).
# Public: both the `code` and `live` TUIs cycle it for their bar animation.
VOICE_FRAMES = ("▁▃▅", "▃▅▇", "▅▇▆", "▆▇▅", "▇▅▃", "▅▃▁") # pragma: no mutate
@@ -39,6 +44,38 @@ def _spinner_text(elapsed_s: int, frame: str) -> str:
return f"{frame} Working… ({elapsed_s}s)"
+def keyhints_text(*, voice: bool) -> str:
+ """The dim key-legend footer for the `code` TUI — the shortcuts worth surfacing.
+
+ The keyboard chords are otherwise undiscoverable (the app has no Footer widget). The
+ Ctrl-V voice toggle is only listed when the session has a voice front-end. Caret notation
+ (``^Y``) keeps the legend short enough to fit a narrow terminal; the chords are bold so
+ they read against the dim labels.
+ """
+ hints = ["[b]^Y[/b] copy"]
+ if voice:
+ hints.append("[b]^V[/b] voice")
+ hints += ["[b]^O[/b] expand", "[b]esc[/b] interrupt", "[b]^C[/b] quit"]
+ return f"[dim]{' · '.join(hints)}[/dim]"
+
+
+def copy_note(reply: str, copier: Callable[[str], None]) -> str:
+ """Copy ``reply`` to the clipboard via ``copier``, returning the transcript note to show.
+
+ Keeps the Ctrl-Y action a one-liner and handles its two non-happy paths so they can't
+ surprise the user: nothing has been said yet, and a headless/clipboard-less box where
+ ``pyperclip`` raises (an unhandled raise there would tear down the whole TUI). ``copier``
+ is ``pyperclip.copy`` in production, injected so this unit-tests with no real clipboard.
+ """
+ if not reply:
+ return "(nothing to copy yet)"
+ try:
+ copier(reply)
+ except pyperclip.PyperclipException:
+ return "(couldn't copy: no clipboard available)"
+ return "(copied last reply to clipboard)"
+
+
def _abbrev_home(path: Path) -> str:
"""Render ``path`` with the home directory collapsed to ``~``."""
try:
@@ -58,10 +95,12 @@ def _git_branch(start: Path) -> str | None:
def _status_text(cwd: Path, *, auto_approve: bool, voice_state: str | None = None) -> str:
- """The bottom status line: a mode badge, the working directory, git branch, and voice state.
+ """The two-row bottom footer: a status line, and a dim key-legend beneath it.
- ``voice_state`` is ``"on"``/``"off"`` when the session has a voice front-end (so the
- Ctrl-V toggle shows its effect), or ``None`` when voice isn't wired up at all.
+ Row one is a mode badge, the working directory, the git branch, and voice state; row two
+ is :func:`keyhints_text`. ``voice_state`` is ``"on"``/``"off"`` when the session has a
+ voice front-end (so the Ctrl-V toggle shows its effect, and the legend lists it), or
+ ``None`` when voice isn't wired up at all.
"""
mode = "auto" if auto_approve else "manual"
badge = f"[black on #f59e0b] {mode} [/]"
@@ -73,4 +112,4 @@ def _status_text(cwd: Path, *, auto_approve: bool, voice_state: str | None = Non
# A filled/hollow dot (BMP glyphs, like the rest of the UI — no double-width emoji).
glyph, color = ("●", "#22c55e") if voice_state == "on" else ("○", "#6b7280")
parts.append(f"[{color}]{glyph} voice {voice_state}[/]")
- return " ".join(parts)
+ return " ".join(parts) + "\n" + keyhints_text(voice=voice_state is not None)
diff --git a/aai_cli/code_agent/voice_ui.py b/aai_cli/code_agent/voice_ui.py
index f105fa4..c4b6ad2 100644
--- a/aai_cli/code_agent/voice_ui.py
+++ b/aai_cli/code_agent/voice_ui.py
@@ -113,7 +113,10 @@ def _capture_voice_turn(self) -> None:
self._voice_typed = True
self.call_from_thread(self._notice_voice_off, exc.message)
return
- if transcript:
+ # Re-check after listen(): the user may have switched to text (Ctrl-V) or interrupted
+ # (Escape/Ctrl-C) while this capture was blocking, in which case a turn that finalized
+ # in that window must not be submitted behind their back.
+ if transcript and self._voice_active():
self.call_from_thread(self._enter_and_submit, transcript)
def _notice_voice_off(self, detail: str) -> None:
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal.raw b/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal.raw
index 9355a4d..aa9e569 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal.raw
@@ -19,164 +19,164 @@
font-weight: 700;
}
- .terminal-2623049041-matrix {
+ .terminal-2084666923-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2623049041-title {
+ .terminal-2084666923-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2623049041-r1 { fill: #e0e0e0 }
-.terminal-2623049041-r2 { fill: #c5c8c6 }
-.terminal-2623049041-r3 { fill: #614fd2;font-weight: bold }
-.terminal-2623049041-r4 { fill: #939393 }
-.terminal-2623049041-r5 { fill: #614fd2 }
-.terminal-2623049041-r6 { fill: #f59e0b }
-.terminal-2623049041-r7 { fill: #f04438;font-weight: bold }
-.terminal-2623049041-r8 { fill: #e0e0e0;font-weight: bold }
-.terminal-2623049041-r9 { fill: #22c55e;font-weight: bold }
-.terminal-2623049041-r10 { fill: #000000 }
+ .terminal-2084666923-r1 { fill: #e0e0e0 }
+.terminal-2084666923-r2 { fill: #c5c8c6 }
+.terminal-2084666923-r3 { fill: #614fd2;font-weight: bold }
+.terminal-2084666923-r4 { fill: #939393 }
+.terminal-2084666923-r5 { fill: #614fd2 }
+.terminal-2084666923-r6 { fill: #f59e0b }
+.terminal-2084666923-r7 { fill: #f04438;font-weight: bold }
+.terminal-2084666923-r8 { fill: #e0e0e0;font-weight: bold }
+.terminal-2084666923-r9 { fill: #22c55e;font-weight: bold }
+.terminal-2084666923-r10 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-
-
-
-
-
-
-
-
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│⚠ This command deletes files recursively/forcibly.│
-│Run tool execute? rm -rf build/│
-│y approve a auto-approve n reject e expand│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+
+
+
+
+
+
+
+
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│⚠ This command deletes files recursively/forcibly.│
+│Run tool execute? rm -rf build/│
+│y approve a auto-approve n reject e expand│
+╰────────────────────────────────────────────────────────────────────────────────────────────────╯
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal_benign.raw b/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal_benign.raw
index 673aebf..9ebc31b 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal_benign.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal_benign.raw
@@ -19,164 +19,164 @@
font-weight: 700;
}
- .terminal-901522975-matrix {
+ .terminal-1417549561-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-901522975-title {
+ .terminal-1417549561-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-901522975-r1 { fill: #e0e0e0 }
-.terminal-901522975-r2 { fill: #c5c8c6 }
-.terminal-901522975-r3 { fill: #614fd2;font-weight: bold }
-.terminal-901522975-r4 { fill: #939393 }
-.terminal-901522975-r5 { fill: #614fd2 }
-.terminal-901522975-r6 { fill: #f59e0b }
-.terminal-901522975-r7 { fill: #e0e0e0;font-weight: bold }
-.terminal-901522975-r8 { fill: #22c55e;font-weight: bold }
-.terminal-901522975-r9 { fill: #f04438;font-weight: bold }
-.terminal-901522975-r10 { fill: #000000 }
+ .terminal-1417549561-r1 { fill: #e0e0e0 }
+.terminal-1417549561-r2 { fill: #c5c8c6 }
+.terminal-1417549561-r3 { fill: #614fd2;font-weight: bold }
+.terminal-1417549561-r4 { fill: #939393 }
+.terminal-1417549561-r5 { fill: #614fd2 }
+.terminal-1417549561-r6 { fill: #f59e0b }
+.terminal-1417549561-r7 { fill: #e0e0e0;font-weight: bold }
+.terminal-1417549561-r8 { fill: #22c55e;font-weight: bold }
+.terminal-1417549561-r9 { fill: #f04438;font-weight: bold }
+.terminal-1417549561-r10 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-
-
-
-
-
-
-
-
-
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│Run tool execute? ls -la│
-│y approve a auto-approve n reject e expand│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+
+
+
+
+
+
+
+
+
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│Run tool execute? ls -la│
+│y approve a auto-approve n reject e expand│
+╰────────────────────────────────────────────────────────────────────────────────────────────────╯
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal_expanded.raw b/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal_expanded.raw
index f15782d..1754ee9 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal_expanded.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_approval_modal_expanded.raw
@@ -19,164 +19,164 @@
font-weight: 700;
}
- .terminal-1367203125-matrix {
+ .terminal-3998338575-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1367203125-title {
+ .terminal-3998338575-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1367203125-r1 { fill: #e0e0e0 }
-.terminal-1367203125-r2 { fill: #c5c8c6 }
-.terminal-1367203125-r3 { fill: #614fd2;font-weight: bold }
-.terminal-1367203125-r4 { fill: #939393 }
-.terminal-1367203125-r5 { fill: #614fd2 }
-.terminal-1367203125-r6 { fill: #f59e0b }
-.terminal-1367203125-r7 { fill: #e0e0e0;font-weight: bold }
-.terminal-1367203125-r8 { fill: #22c55e;font-weight: bold }
-.terminal-1367203125-r9 { fill: #f04438;font-weight: bold }
-.terminal-1367203125-r10 { fill: #000000 }
+ .terminal-3998338575-r1 { fill: #e0e0e0 }
+.terminal-3998338575-r2 { fill: #c5c8c6 }
+.terminal-3998338575-r3 { fill: #614fd2;font-weight: bold }
+.terminal-3998338575-r4 { fill: #939393 }
+.terminal-3998338575-r5 { fill: #614fd2 }
+.terminal-3998338575-r6 { fill: #f59e0b }
+.terminal-3998338575-r7 { fill: #e0e0e0;font-weight: bold }
+.terminal-3998338575-r8 { fill: #22c55e;font-weight: bold }
+.terminal-3998338575-r9 { fill: #f04438;font-weight: bold }
+.terminal-3998338575-r10 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-
-
-
-
-
-
-
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│Run tool write_file? file_path=app.py│
-│content=PORT = 8080│
-│DEBUG = 1│
-│y approve a auto-approve n reject e expand│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+
+
+
+
+
+
+
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│Run tool write_file? file_path=app.py│
+│content=PORT = 8080│
+│DEBUG = 1│
+│y approve a auto-approve n reject e expand│
+╰────────────────────────────────────────────────────────────────────────────────────────────────╯
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_ask_modal.raw b/tests/__snapshots__/test_tui_snapshots/test_code_ask_modal.raw
index 02bbd4f..3eed1ac 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_ask_modal.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_ask_modal.raw
@@ -19,165 +19,166 @@
font-weight: 700;
}
- .terminal-1386297441-matrix {
+ .terminal-4063177019-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1386297441-title {
+ .terminal-4063177019-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1386297441-r1 { fill: #e0e0e0 }
-.terminal-1386297441-r2 { fill: #c5c8c6 }
-.terminal-1386297441-r3 { fill: #614fd2;font-weight: bold }
-.terminal-1386297441-r4 { fill: #939393 }
-.terminal-1386297441-r5 { fill: #614fd2 }
-.terminal-1386297441-r6 { fill: #3a3f55 }
-.terminal-1386297441-r7 { fill: #e0e0e0;font-weight: bold }
-.terminal-1386297441-r8 { fill: #000000 }
-.terminal-1386297441-r9 { fill: #0178d4 }
-.terminal-1386297441-r10 { fill: #121212 }
-.terminal-1386297441-r11 { fill: #797979 }
+ .terminal-4063177019-r1 { fill: #e0e0e0 }
+.terminal-4063177019-r2 { fill: #c5c8c6 }
+.terminal-4063177019-r3 { fill: #614fd2;font-weight: bold }
+.terminal-4063177019-r4 { fill: #939393 }
+.terminal-4063177019-r5 { fill: #614fd2 }
+.terminal-4063177019-r6 { fill: #3a3f55 }
+.terminal-4063177019-r7 { fill: #e0e0e0;font-weight: bold }
+.terminal-4063177019-r8 { fill: #000000 }
+.terminal-4063177019-r9 { fill: #0178d4 }
+.terminal-4063177019-r10 { fill: #121212 }
+.terminal-4063177019-r11 { fill: #797979 }
+.terminal-4063177019-r12 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-
-
-
-
-
-
-
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│The agent asks: Which port should the dev server use?│
-│▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎│
-│▊Type your answer and press Enter…▎│
-│▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+
+
+
+
+
+
+
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│The agent asks: Which port should the dev server use?│
+│▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎│
+│▊Type your answer and press Enter…▎│
+│▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎│
+╰────────────────────────────────────────────────────────────────────────────────────────────────╯
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_error.raw b/tests/__snapshots__/test_tui_snapshots/test_code_error.raw
index bb805a7..5a40a2d 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_error.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_error.raw
@@ -19,165 +19,166 @@
font-weight: 700;
}
- .terminal-1956613797-matrix {
+ .terminal-2239750680-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1956613797-title {
+ .terminal-2239750680-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1956613797-r1 { fill: #c5c8c6 }
-.terminal-1956613797-r2 { fill: #614fd2;font-weight: bold }
-.terminal-1956613797-r3 { fill: #939393 }
-.terminal-1956613797-r4 { fill: #e0e0e0 }
-.terminal-1956613797-r5 { fill: #614fd2 }
-.terminal-1956613797-r6 { fill: #38bdf8;font-weight: bold }
-.terminal-1956613797-r7 { fill: #f04438 }
-.terminal-1956613797-r8 { fill: #3a3f55 }
-.terminal-1956613797-r9 { fill: #121212 }
-.terminal-1956613797-r10 { fill: #676767 }
-.terminal-1956613797-r11 { fill: #000000 }
+ .terminal-2239750680-r1 { fill: #c5c8c6 }
+.terminal-2239750680-r2 { fill: #614fd2;font-weight: bold }
+.terminal-2239750680-r3 { fill: #939393 }
+.terminal-2239750680-r4 { fill: #e0e0e0 }
+.terminal-2239750680-r5 { fill: #614fd2 }
+.terminal-2239750680-r6 { fill: #38bdf8;font-weight: bold }
+.terminal-2239750680-r7 { fill: #f04438 }
+.terminal-2239750680-r8 { fill: #3a3f55 }
+.terminal-2239750680-r9 { fill: #121212 }
+.terminal-2239750680-r10 { fill: #676767 }
+.terminal-2239750680-r11 { fill: #000000 }
+.terminal-2239750680-r12 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-» deploy to prod
-✗ gateway unreachable: connection refused
-
-
-
-
-
-
-
-
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│>Ask the agent to build something…│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+» deploy to prod
+✗ gateway unreachable: connection refused
+
+
+
+
+
+
+
+
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│>Ask the agent to build something…│
+ manual ~/demo↗ main
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_splash.raw b/tests/__snapshots__/test_tui_snapshots/test_code_splash.raw
index 770b28c..b9553b2 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_splash.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_splash.raw
@@ -19,163 +19,164 @@
font-weight: 700;
}
- .terminal-2744299105-matrix {
+ .terminal-189530595-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2744299105-title {
+ .terminal-189530595-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2744299105-r1 { fill: #c5c8c6 }
-.terminal-2744299105-r2 { fill: #614fd2;font-weight: bold }
-.terminal-2744299105-r3 { fill: #939393 }
-.terminal-2744299105-r4 { fill: #e0e0e0 }
-.terminal-2744299105-r5 { fill: #614fd2 }
-.terminal-2744299105-r6 { fill: #3a3f55 }
-.terminal-2744299105-r7 { fill: #121212 }
-.terminal-2744299105-r8 { fill: #676767 }
-.terminal-2744299105-r9 { fill: #000000 }
+ .terminal-189530595-r1 { fill: #c5c8c6 }
+.terminal-189530595-r2 { fill: #614fd2;font-weight: bold }
+.terminal-189530595-r3 { fill: #939393 }
+.terminal-189530595-r4 { fill: #e0e0e0 }
+.terminal-189530595-r5 { fill: #614fd2 }
+.terminal-189530595-r6 { fill: #3a3f55 }
+.terminal-189530595-r7 { fill: #121212 }
+.terminal-189530595-r8 { fill: #676767 }
+.terminal-189530595-r9 { fill: #000000 }
+.terminal-189530595-r10 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-
-
-
-
-
-
-
-
-
-
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│>Ask the agent to build something…│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+
+
+
+
+
+
+
+
+
+
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│>Ask the agent to build something…│
+ manual ~/demo↗ main
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_status_auto_approve.raw b/tests/__snapshots__/test_tui_snapshots/test_code_status_auto_approve.raw
index 71d048f..3932905 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_status_auto_approve.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_status_auto_approve.raw
@@ -19,163 +19,164 @@
font-weight: 700;
}
- .terminal-2985930204-matrix {
+ .terminal-3300524382-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2985930204-title {
+ .terminal-3300524382-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2985930204-r1 { fill: #c5c8c6 }
-.terminal-2985930204-r2 { fill: #614fd2;font-weight: bold }
-.terminal-2985930204-r3 { fill: #939393 }
-.terminal-2985930204-r4 { fill: #e0e0e0 }
-.terminal-2985930204-r5 { fill: #614fd2 }
-.terminal-2985930204-r6 { fill: #3a3f55 }
-.terminal-2985930204-r7 { fill: #121212 }
-.terminal-2985930204-r8 { fill: #676767 }
-.terminal-2985930204-r9 { fill: #000000 }
+ .terminal-3300524382-r1 { fill: #c5c8c6 }
+.terminal-3300524382-r2 { fill: #614fd2;font-weight: bold }
+.terminal-3300524382-r3 { fill: #939393 }
+.terminal-3300524382-r4 { fill: #e0e0e0 }
+.terminal-3300524382-r5 { fill: #614fd2 }
+.terminal-3300524382-r6 { fill: #3a3f55 }
+.terminal-3300524382-r7 { fill: #121212 }
+.terminal-3300524382-r8 { fill: #676767 }
+.terminal-3300524382-r9 { fill: #000000 }
+.terminal-3300524382-r10 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-
-
-
-
-
-
-
-
-
-
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│>Ask the agent to build something…│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- auto ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+
+
+
+
+
+
+
+
+
+
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│>Ask the agent to build something…│
+ auto ~/demo↗ main
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_streaming_reply.raw b/tests/__snapshots__/test_tui_snapshots/test_code_streaming_reply.raw
index 95ca020..b3ae8b1 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_streaming_reply.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_streaming_reply.raw
@@ -19,164 +19,165 @@
font-weight: 700;
}
- .terminal-773362532-matrix {
+ .terminal-730392279-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-773362532-title {
+ .terminal-730392279-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-773362532-r1 { fill: #c5c8c6 }
-.terminal-773362532-r2 { fill: #614fd2;font-weight: bold }
-.terminal-773362532-r3 { fill: #939393 }
-.terminal-773362532-r4 { fill: #e0e0e0 }
-.terminal-773362532-r5 { fill: #614fd2 }
-.terminal-773362532-r6 { fill: #38bdf8;font-weight: bold }
-.terminal-773362532-r7 { fill: #3a3f55 }
-.terminal-773362532-r8 { fill: #121212 }
-.terminal-773362532-r9 { fill: #676767 }
-.terminal-773362532-r10 { fill: #000000 }
+ .terminal-730392279-r1 { fill: #c5c8c6 }
+.terminal-730392279-r2 { fill: #614fd2;font-weight: bold }
+.terminal-730392279-r3 { fill: #939393 }
+.terminal-730392279-r4 { fill: #e0e0e0 }
+.terminal-730392279-r5 { fill: #614fd2 }
+.terminal-730392279-r6 { fill: #38bdf8;font-weight: bold }
+.terminal-730392279-r7 { fill: #3a3f55 }
+.terminal-730392279-r8 { fill: #121212 }
+.terminal-730392279-r9 { fill: #676767 }
+.terminal-730392279-r10 { fill: #000000 }
+.terminal-730392279-r11 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-» explain the plan
-Here's the plan. First **scaffold** the project, then wire up the tests.
-
-
-
-
-
-
-
-
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│>Ask the agent to build something…│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+» explain the plan
+Here's the plan. First **scaffold** the project, then wire up the tests.
+
+
+
+
+
+
+
+
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│>Ask the agent to build something…│
+ manual ~/demo↗ main
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_tool_output_collapsed.raw b/tests/__snapshots__/test_tui_snapshots/test_code_tool_output_collapsed.raw
index 705dbe0..62998d0 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_tool_output_collapsed.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_tool_output_collapsed.raw
@@ -19,166 +19,167 @@
font-weight: 700;
}
- .terminal-2885406642-matrix {
+ .terminal-246752052-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2885406642-title {
+ .terminal-246752052-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2885406642-r1 { fill: #c5c8c6 }
-.terminal-2885406642-r2 { fill: #614fd2;font-weight: bold }
-.terminal-2885406642-r3 { fill: #939393 }
-.terminal-2885406642-r4 { fill: #e0e0e0 }
-.terminal-2885406642-r5 { fill: #614fd2 }
-.terminal-2885406642-r6 { fill: #38bdf8;font-weight: bold }
-.terminal-2885406642-r7 { fill: #8a8f98 }
-.terminal-2885406642-r8 { fill: #8a8f98;font-style: italic; }
-.terminal-2885406642-r9 { fill: #3a3f55 }
-.terminal-2885406642-r10 { fill: #121212 }
-.terminal-2885406642-r11 { fill: #676767 }
-.terminal-2885406642-r12 { fill: #000000 }
+ .terminal-246752052-r1 { fill: #c5c8c6 }
+.terminal-246752052-r2 { fill: #614fd2;font-weight: bold }
+.terminal-246752052-r3 { fill: #939393 }
+.terminal-246752052-r4 { fill: #e0e0e0 }
+.terminal-246752052-r5 { fill: #614fd2 }
+.terminal-246752052-r6 { fill: #38bdf8;font-weight: bold }
+.terminal-246752052-r7 { fill: #8a8f98 }
+.terminal-246752052-r8 { fill: #8a8f98;font-style: italic; }
+.terminal-246752052-r9 { fill: #3a3f55 }
+.terminal-246752052-r10 { fill: #121212 }
+.terminal-246752052-r11 { fill: #676767 }
+.terminal-246752052-r12 { fill: #000000 }
+.terminal-246752052-r13 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-» run the tests
-→ execute(pytest -q)
- execute: tests/test_module_0.py .... [ 0%]
-tests/test_module_1.py .... [ 10%]
-tests/test_module_2.py .... [ 20%]
-tests/test_module_3.py .... [ 30%] … (+4 more lines) (Ctrl+O to expand)
-
-
-
-
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│>Ask the agent to build something…│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+» run the tests
+→ execute(pytest -q)
+ execute: tests/test_module_0.py .... [ 0%]
+tests/test_module_1.py .... [ 10%]
+tests/test_module_2.py .... [ 20%]
+tests/test_module_3.py .... [ 30%] … (+4 more lines) (Ctrl+O to expand)
+
+
+
+
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│>Ask the agent to build something…│
+ manual ~/demo↗ main
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_tool_output_expanded.raw b/tests/__snapshots__/test_tui_snapshots/test_code_tool_output_expanded.raw
index 8e9a713..1e11c72 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_tool_output_expanded.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_tool_output_expanded.raw
@@ -19,166 +19,167 @@
font-weight: 700;
}
- .terminal-2213969501-matrix {
+ .terminal-2792346064-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2213969501-title {
+ .terminal-2792346064-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2213969501-r1 { fill: #c5c8c6 }
-.terminal-2213969501-r2 { fill: #614fd2;font-weight: bold }
-.terminal-2213969501-r3 { fill: #939393 }
-.terminal-2213969501-r4 { fill: #e0e0e0 }
-.terminal-2213969501-r5 { fill: #614fd2 }
-.terminal-2213969501-r6 { fill: #38bdf8;font-weight: bold }
-.terminal-2213969501-r7 { fill: #8a8f98 }
-.terminal-2213969501-r8 { fill: #8a8f98;font-style: italic; }
-.terminal-2213969501-r9 { fill: #3a3f55 }
-.terminal-2213969501-r10 { fill: #121212 }
-.terminal-2213969501-r11 { fill: #676767 }
-.terminal-2213969501-r12 { fill: #000000 }
+ .terminal-2792346064-r1 { fill: #c5c8c6 }
+.terminal-2792346064-r2 { fill: #614fd2;font-weight: bold }
+.terminal-2792346064-r3 { fill: #939393 }
+.terminal-2792346064-r4 { fill: #e0e0e0 }
+.terminal-2792346064-r5 { fill: #614fd2 }
+.terminal-2792346064-r6 { fill: #38bdf8;font-weight: bold }
+.terminal-2792346064-r7 { fill: #8a8f98 }
+.terminal-2792346064-r8 { fill: #8a8f98;font-style: italic; }
+.terminal-2792346064-r9 { fill: #3a3f55 }
+.terminal-2792346064-r10 { fill: #121212 }
+.terminal-2792346064-r11 { fill: #676767 }
+.terminal-2792346064-r12 { fill: #000000 }
+.terminal-2792346064-r13 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-» run the tests
-→ execute(pytest -q)
- execute: tests/test_module_0.py .... [ 0%]
-tests/test_module_1.py .... [ 10%]
-tests/test_module_2.py .... [ 20%]
-tests/test_module_3.py .... [ 30%]
-tests/test_module_4.py .... [ 40%]
-tests/test_module_5.py .... [ 50%]
-tests/test_module_6.py .... [ 60%]
-tests/test_module_7.py .... [ 70%] (Ctrl+O to collapse)
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│>Ask the agent to build something…│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+» run the tests
+→ execute(pytest -q)
+ execute: tests/test_module_0.py .... [ 0%]
+tests/test_module_1.py .... [ 10%]
+tests/test_module_2.py .... [ 20%]
+tests/test_module_3.py .... [ 30%]
+tests/test_module_4.py .... [ 40%]
+tests/test_module_5.py .... [ 50%]
+tests/test_module_6.py .... [ 60%]
+tests/test_module_7.py .... [ 70%] (Ctrl+O to collapse)
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│>Ask the agent to build something…│
+ manual ~/demo↗ main
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_transcript.raw b/tests/__snapshots__/test_tui_snapshots/test_code_transcript.raw
index 152213d..fb3fe8d 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_transcript.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_transcript.raw
@@ -19,167 +19,168 @@
font-weight: 700;
}
- .terminal-2481357719-matrix {
+ .terminal-1120851722-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-2481357719-title {
+ .terminal-1120851722-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-2481357719-r1 { fill: #c5c8c6 }
-.terminal-2481357719-r2 { fill: #614fd2;font-weight: bold }
-.terminal-2481357719-r3 { fill: #939393 }
-.terminal-2481357719-r4 { fill: #e0e0e0 }
-.terminal-2481357719-r5 { fill: #614fd2 }
-.terminal-2481357719-r6 { fill: #38bdf8;font-weight: bold }
-.terminal-2481357719-r7 { fill: #e0e0e0;font-weight: bold }
-.terminal-2481357719-r8 { fill: #58d1eb }
-.terminal-2481357719-r9 { fill: #8a8f98 }
-.terminal-2481357719-r10 { fill: #3a3f55 }
-.terminal-2481357719-r11 { fill: #121212 }
-.terminal-2481357719-r12 { fill: #676767 }
-.terminal-2481357719-r13 { fill: #000000 }
+ .terminal-1120851722-r1 { fill: #c5c8c6 }
+.terminal-1120851722-r2 { fill: #614fd2;font-weight: bold }
+.terminal-1120851722-r3 { fill: #939393 }
+.terminal-1120851722-r4 { fill: #e0e0e0 }
+.terminal-1120851722-r5 { fill: #614fd2 }
+.terminal-1120851722-r6 { fill: #38bdf8;font-weight: bold }
+.terminal-1120851722-r7 { fill: #e0e0e0;font-weight: bold }
+.terminal-1120851722-r8 { fill: #58d1eb }
+.terminal-1120851722-r9 { fill: #8a8f98 }
+.terminal-1120851722-r10 { fill: #3a3f55 }
+.terminal-1120851722-r11 { fill: #121212 }
+.terminal-1120851722-r12 { fill: #676767 }
+.terminal-1120851722-r13 { fill: #000000 }
+.terminal-1120851722-r14 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-» add a /health endpoint
-Adding a health check:
-
- 1 New route
- 2 A test
-→ write_file(app.py)
- write_file: wrote 8 lines to app.py
-
-
-
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│>Ask the agent to build something…│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+» add a /health endpoint
+Adding a health check:
+
+ 1 New route
+ 2 A test
+→ write_file(app.py)
+ write_file: wrote 8 lines to app.py
+
+
+
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│>Ask the agent to build something…│
+ manual ~/demo↗ main
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_voice_listening.raw b/tests/__snapshots__/test_tui_snapshots/test_code_voice_listening.raw
index 310de20..7b0288b 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_voice_listening.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_voice_listening.raw
@@ -19,161 +19,162 @@
font-weight: 700;
}
- .terminal-1669332747-matrix {
+ .terminal-3917210796-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-1669332747-title {
+ .terminal-3917210796-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-1669332747-r1 { fill: #c5c8c6 }
-.terminal-1669332747-r2 { fill: #614fd2;font-weight: bold }
-.terminal-1669332747-r3 { fill: #939393 }
-.terminal-1669332747-r4 { fill: #e0e0e0 }
-.terminal-1669332747-r5 { fill: #614fd2 }
-.terminal-1669332747-r6 { fill: #000000 }
-.terminal-1669332747-r7 { fill: #22c55e }
+ .terminal-3917210796-r1 { fill: #c5c8c6 }
+.terminal-3917210796-r2 { fill: #614fd2;font-weight: bold }
+.terminal-3917210796-r3 { fill: #939393 }
+.terminal-3917210796-r4 { fill: #e0e0e0 }
+.terminal-3917210796-r5 { fill: #614fd2 }
+.terminal-3917210796-r6 { fill: #000000 }
+.terminal-3917210796-r7 { fill: #22c55e }
+.terminal-3917210796-r8 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-
-
-
-
-
-
-
-
-
-
-
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│▁▃▅ Listening — speak your request (Ctrl-V to type)│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main● voice on
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+
+
+
+
+
+
+
+
+
+
+
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│▁▃▅ Listening — speak your request (Ctrl-V to type)│
+ manual ~/demo↗ main● voice on
+^Y copy · ^V voice · ^O expand · esc interrupt · ^C quit
diff --git a/tests/__snapshots__/test_tui_snapshots/test_code_working_spinner.raw b/tests/__snapshots__/test_tui_snapshots/test_code_working_spinner.raw
index d48171b..6bcc995 100644
--- a/tests/__snapshots__/test_tui_snapshots/test_code_working_spinner.raw
+++ b/tests/__snapshots__/test_tui_snapshots/test_code_working_spinner.raw
@@ -19,164 +19,165 @@
font-weight: 700;
}
- .terminal-3063843344-matrix {
+ .terminal-3227114883-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
- .terminal-3063843344-title {
+ .terminal-3227114883-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
- .terminal-3063843344-r1 { fill: #c5c8c6 }
-.terminal-3063843344-r2 { fill: #614fd2;font-weight: bold }
-.terminal-3063843344-r3 { fill: #939393 }
-.terminal-3063843344-r4 { fill: #e0e0e0 }
-.terminal-3063843344-r5 { fill: #614fd2 }
-.terminal-3063843344-r6 { fill: #38bdf8;font-weight: bold }
-.terminal-3063843344-r7 { fill: #3a3f55 }
-.terminal-3063843344-r8 { fill: #121212 }
-.terminal-3063843344-r9 { fill: #676767 }
-.terminal-3063843344-r10 { fill: #000000 }
+ .terminal-3227114883-r1 { fill: #c5c8c6 }
+.terminal-3227114883-r2 { fill: #614fd2;font-weight: bold }
+.terminal-3227114883-r3 { fill: #939393 }
+.terminal-3227114883-r4 { fill: #e0e0e0 }
+.terminal-3227114883-r5 { fill: #614fd2 }
+.terminal-3227114883-r6 { fill: #38bdf8;font-weight: bold }
+.terminal-3227114883-r7 { fill: #3a3f55 }
+.terminal-3227114883-r8 { fill: #121212 }
+.terminal-3227114883-r9 { fill: #676767 }
+.terminal-3227114883-r10 { fill: #000000 }
+.terminal-3227114883-r11 { fill: #939393;font-weight: bold }
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- AssemblyAI Code
+ AssemblyAI Code
-
-
-
-
- █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
-██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
-███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
-██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
-██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
-╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
-v9.9.9
-
-Thread: default
-
-Ready to code! What would you like to build?
-Tip: approve tools as they run, or pass --auto to skip the prompts.
-
-» build a web scraper
-
-
-
-
-
-
-
-
-
-✶ Working… (7s)
-
-╭────────────────────────────────────────────────────────────────────────────────────────────────╮
-│>Ask the agent to build something…│
-╰────────────────────────────────────────────────────────────────────────────────────────────────╯
- manual ~/demo↗ main
+
+
+
+
+ █████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗ ██████╗ ██╗ ██╗ ██╗
+██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗ ██║ ╚██╗ ██╔╝
+███████║ ███████╗ ███████╗ █████╗ ██╔████╔██║ ██████╔╝ ██║ ╚████╔╝
+██╔══██║ ╚════██║ ╚════██║ ██╔══╝ ██║╚██╔╝██║ ██╔══██╗ ██║ ╚██╔╝
+██║ ██║ ███████║ ███████║ ███████╗ ██║ ╚═╝ ██║ ██████╔╝ ███████╗ ██║
+╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
+v9.9.9
+
+Thread: default
+
+Ready to code! What would you like to build?
+Tip: approve tools as they run, or pass --auto to skip the prompts.
+
+» build a web scraper
+
+
+
+
+
+
+
+
+
+✶ Working… (7s)
+
+╭────────────────────────────────────────────────────────────────────────────────────────────────╮
+│>Ask the agent to build something…│
+ manual ~/demo↗ main
+^Y copy · ^O expand · esc interrupt · ^C quit
diff --git a/tests/test_code_tui_status.py b/tests/test_code_tui_status.py
index fc53dbc..9d6de03 100644
--- a/tests/test_code_tui_status.py
+++ b/tests/test_code_tui_status.py
@@ -8,6 +8,8 @@
from pathlib import Path
+import pyperclip
+
from aai_cli.code_agent import tui_status
from aai_cli.ui import theme
@@ -51,6 +53,56 @@ def test_voicebar_markup_per_phase_carries_label_meter_accent_and_hint() -> None
assert "Speaking" in speaking and "#22c55e" in speaking # green
+def test_copy_note_copies_and_confirms() -> None:
+ # The happy path: the reply is handed to the copier and a confirmation note returned.
+ copied: list[str] = []
+ note = tui_status.copy_note("a reply", copied.append)
+ assert copied == ["a reply"]
+ assert "copied" in note
+
+
+def test_copy_note_when_nothing_to_copy() -> None:
+ # No reply yet: don't touch the clipboard, and tell the user there's nothing to copy.
+ copied: list[str] = []
+ note = tui_status.copy_note("", copied.append)
+ assert copied == [] # copier never called
+ assert "nothing to copy" in note
+
+
+def test_copy_note_degrades_when_no_clipboard() -> None:
+ # A headless/clipboard-less box: pyperclip raises; copy_note must absorb it and return a
+ # note rather than letting the raise propagate (which would tear down the TUI).
+ def _boom(_text: str) -> None:
+ raise pyperclip.PyperclipException("no copy/paste mechanism")
+
+ note = tui_status.copy_note("a reply", _boom)
+ assert "no clipboard available" in note
+
+
+def test_keyhints_lists_shortcuts_and_gates_voice_on_availability() -> None:
+ # The legend always lists copy/expand/interrupt/quit; the Ctrl-V voice toggle appears
+ # only when a voice front-end exists, and the whole line is dim.
+ with_voice = tui_status.keyhints_text(voice=True)
+ assert "copy" in with_voice and "expand" in with_voice and "quit" in with_voice
+ assert "voice" in with_voice # the Ctrl-V hint is listed when voice is available
+ assert with_voice.startswith("[dim]") # rendered as a dim legend
+ without_voice = tui_status.keyhints_text(voice=False)
+ assert "voice" not in without_voice # no Ctrl-V hint without a voice front-end
+ assert "copy" in without_voice and "quit" in without_voice
+
+
+def test_status_text_appends_the_key_legend(tmp_path: Path) -> None:
+ # The footer is two rows: the status info, then the dim key legend beneath it.
+ footer = tui_status._status_text(tmp_path, auto_approve=False)
+ info, _, hints = footer.partition("\n")
+ assert "manual" in info # row one is the status info
+ assert "quit" in hints and "copy" in hints # row two is the key legend
+ assert "voice" not in hints # no voice front-end -> the legend omits the Ctrl-V hint
+ # With a voice front-end the legend's second row gains the Ctrl-V hint.
+ voiced = tui_status._status_text(tmp_path, auto_approve=False, voice_state="on")
+ assert "voice" in voiced.partition("\n")[2]
+
+
def test_status_text_renders_voice_badge(tmp_path: Path) -> None:
# No voice front-end -> no voice badge (the dot glyphs are absent); on/off render the
# state so the Ctrl-V toggle shows. (Asserts on the dots, not the word — the tmp_path name
diff --git a/tests/test_code_tui_voice_switch.py b/tests/test_code_tui_voice_switch.py
new file mode 100644
index 0000000..9687b79
--- /dev/null
+++ b/tests/test_code_tui_voice_switch.py
@@ -0,0 +1,62 @@
+"""Tests for switching between voice and text mode in the `assembly code` TUI.
+
+Switching input mode (Ctrl-V) and interrupting (Escape / Ctrl-C) both have to stop an
+in-flight microphone capture so it neither keeps the mic open behind the text prompt nor
+submits a turn the user no longer wants. These cancel-safety cases are split out of
+test_code_tui_voice.py to keep each file under the 500-line gate, reusing that module's
+app/voice doubles.
+"""
+
+from __future__ import annotations
+
+import threading
+
+import pytest
+
+from aai_cli.code_agent.tui import CodeAgentApp
+from tests.test_code_tui_voice import FakeAgent, FakeVoice, _run, _wait_until
+
+
+def test_toggle_voice_off_cancels_in_flight_capture() -> None:
+ # Switching to text (Ctrl-V) must release the mic now — cancel the blocking listen()
+ # rather than leaving a capture running unseen behind the text prompt.
+ async def go() -> None:
+ voice = FakeVoice()
+ app = CodeAgentApp(agent=FakeAgent([]), voice=voice)
+ app._voice_paused = True # start paused so on_mount doesn't race a capture thread
+ async with app.run_test(size=(100, 30)) as pilot:
+ await pilot.pause()
+ app.action_toggle_voice() # voice on
+ assert voice.cancels == 0 # turning on never cancels
+ app.action_toggle_voice() # voice off -> must cancel the in-flight capture
+ assert voice.cancels == 1
+
+ _run(go())
+
+
+def test_capture_after_switching_to_text_is_not_submitted(monkeypatch: pytest.MonkeyPatch) -> None:
+ # A turn that finalizes in the window between the user pressing Ctrl-V and the capture
+ # unwinding must NOT be submitted — otherwise a spoken phrase lands as a turn after the
+ # user already switched to typing.
+ async def go() -> None:
+ voice = FakeVoice()
+ app = CodeAgentApp(agent=FakeAgent([]), voice=voice)
+ app._voice_paused = True # block the on_mount auto-listen
+ async with app.run_test(size=(100, 30)) as pilot:
+ await pilot.pause()
+ submitted: list[str] = []
+ monkeypatch.setattr(app, "_submit", submitted.append) # spy: _enter_and_submit calls it
+
+ def listen() -> str:
+ voice.listens += 1
+ app._voice_paused = True # user switched to text DURING the capture
+ return "late turn"
+
+ monkeypatch.setattr(voice, "listen", listen)
+ app._voice_paused = False # active when the capture starts
+ thread = threading.Thread(target=app._capture_voice_turn)
+ thread.start()
+ assert await _wait_until(pilot, lambda: not thread.is_alive())
+ assert submitted == [] # the late turn was dropped, not submitted
+
+ _run(go())