feat(chat): add interactive chat mode with Textual TUI and serve backend#20
Merged
Merged
Conversation
Add --chat flag to run-agent.py that launches an interactive Textual TUI for multi-turn conversations with opencode serve. Architecture: - tools/events/chat_loop.py: ChatEventLoop (idle→prompt→idle loop) - Reuses SseClient, StateTracker, emit_event from existing serve stack - Signals state transitions (idle/busy/error/stopped) via queue.Queue - Auto-rejects permissions, syncs session snapshots on idle - tools/run-agent.py: Textual TUI integration - TextualConsoleProxy bridges Rich Console.print() to RichLog.write() - ChatApp: RichLog upper panel + Input lower panel + Footer - QuitScreen: Ctrl+C confirmation modal - _run_chat_mode(): server start → session create → TUI launch - Makefile: chat: target + WRAPPER_ARGS propagation to phases 1-6 - requirements.txt: textual>=0.80.0 - tests/test_chat_mode.py: 22 unit tests (ChatEventLoop, proxy, argparse) - .project/chat-mode-plan.md: implementation plan Tests: 245 passed, 0 failed (excluding pre-existing parity test)
Three fixes for the interactive chat mode: 1. **CSS**: Add explicit height to Input widget so it's visible - RichLog with height:1fr was taking all space, leaving Input at 0 height - Input now gets height:3 so the input area is always visible 2. **Session creation**: Add permission rules to prevent blocking prompts - New _create_chat_session() function with deny-all permission rules - Prevents interactive permission prompts that would block the TUI 3. **Non-blocking initial prompt**: Use create_task instead of await - on_mount no longer blocks on send_prompt - Errors in initial prompt are displayed gracefully in the TUI Tests: 245 passed, 0 failed
- Add _ChatApp Textual TUI with RichLog output, Input widget, bottom-bar
modeline (provider/model, tokens, cost, heartbeat pulse, [SEL] indicator),
and Ctrl+C quit dialog.
- Add ChatEventLoop / ChatState for SSE consumer lifecycle with composite-key
dedup, per-event debug logging, and transcript jsonl persistence.
- Add TextualConsoleProxy bridging consumer thread renderables to main-thread
RichLog writes via single RenderMessage Message subclass + post_message.
- Add render_message_updated with phase-mode parity: suppresses in-progress
messages, renders user dim / assistant bold-blue with token summary.
- Add make chat target: --agent chat, --prompt-file prompts/chat-initial.md
- Add .opencode/agents/chat.md: lazy-loading chat agent with workspace catalog
- Add prompts/chat-initial.md: bootstrap prompt (codecome.yml + finding counts)
- Add terminal-native mouse selection via Ctrl+S toggle:
* Removed _SelectableRichLog subclass (RichLog has no upstream selection
support; implementing all four required pieces — offset metadata,
selection rendering, cache invalidation, get_selection — is high-risk
on this Textual 8.2.6 + Python 3.14 combo).
* Ctrl+S calls action_toggle_mouse_for_select which toggles the driver's
_disable_mouse_support() / _enable_mouse_support(). When on, [SEL] shows
in the modeline and the terminal emulator's native click-drag + clipboard
selection works. Startup tip explains the Option/Alt and Ctrl+S paths.
* Full rationale documented in .project/chat-mode-textual-postmortem.md §12.
- Add cvss_v4 frontmatter to all 4 findings (CC-0001 through CC-0004) for
compliance with the skilling frontmatter checker.
- Hide initial bootstrap prompt: shows '(initializing session…)' (bold cyan)
instead of echoing the full prompts/chat-initial.md content.
- Add .project/chat-mode-textual-postmortem.md: bisection matrix, forbidden
patterns, safe extension recipes, diagnostic toolkit, and changelog.
- Tests: 31 new chat-specific tests (ChatEventLoop, TextualConsoleProxy,
render_and_log parity, transcript lifecycle, argparse). 268 project total.
- Quality gate: make tests passes (pytest 268/268 + frontmatter 4/4).
Co-authored-by: CodeCome Agent <agent@codecome.local>
Coverage Report
Generated by pytest-cov on |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an interactive
make chatcommand that launches a Textual TUI connected to anopencode serveSSE backend, enabling real-time conversational interaction with the CodeCome agent.What's included
Chat mode TUI (
tools/run-agent.py)_ChatApp— Textual app with RichLog output, Input widget, bottom-bar modeline (provider/model, tokens, cost, heartbeat pulse,[SEL]indicator), and Ctrl+C quit dialogChatEventLoop/ChatState(tools/events/chat_loop.py) — SSE consumer lifecycle with composite-key dedup, per-event debug logging, and transcript jsonl persistenceTextualConsoleProxy— bridges consumer-thread renderables to main-thread RichLog writes via a singleRenderMessageMessage subclass +post_messagerender_message_updated— parity with phase mode: suppresses in-progress messages, renders user messages dim / assistant messages bold-blue with token summaryTerminal-native mouse selection (Ctrl+S)
_SelectableRichLogsubclass — RichLog has no upstream selection support; fully reimplementing all four required pieces (offset metadata, selection rendering, cache invalidation,get_selection) is high-risk on this Textual 8.2.6 + Python 3.14 comboCtrl+Stoggles the driver's mouse support off/on, enabling the terminal emulator's native click-drag selection + system clipboard[SEL]indicator in modeline when terminal-select mode is active.project/chat-mode-textual-postmortem.md§12Supporting files
.opencode/agents/chat.md— lazy-loading chat agent definitionprompts/chat-initial.md— bootstrap prompt (readscodecome.yml, lists findings).project/chat-mode-textual-postmortem.md— bisection matrix, forbidden patterns (each silently freezes the main loop), safe extension recipes, diagnostic toolkit, changelogMakefile—chat:target with--agent chat,--prompt-file,--debugflagFixes
cvss_v4frontmatter added to all 4 findings (CC-0001 through CC-0004) for compliance with the skilling frontmatter checker(initializing session…)(bold cyan) instead of echoing the full multi-line prompt fileTesting
make testsquality gate: pytest + frontmatter validation — clean