Skip to content

feat(chat): add interactive chat mode with Textual TUI and serve backend#20

Merged
pruiz merged 3 commits into
masterfrom
fix/harness-phase5-permission-graceful-completion
May 22, 2026
Merged

feat(chat): add interactive chat mode with Textual TUI and serve backend#20
pruiz merged 3 commits into
masterfrom
fix/harness-phase5-permission-graceful-completion

Conversation

@pruiz
Copy link
Copy Markdown
Owner

@pruiz pruiz commented May 22, 2026

Summary

Adds an interactive make chat command that launches a Textual TUI connected to an opencode serve SSE 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 dialog
  • ChatEventLoop / ChatState (tools/events/chat_loop.py) — SSE consumer lifecycle with composite-key dedup, per-event debug logging, and transcript jsonl persistence
  • TextualConsoleProxy — bridges consumer-thread renderables to main-thread RichLog writes via a single RenderMessage Message subclass + post_message
  • render_message_updated — parity with phase mode: suppresses in-progress messages, renders user messages dim / assistant messages bold-blue with token summary

Terminal-native mouse selection (Ctrl+S)

  • Removed _SelectableRichLog subclass — 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 combo
  • Ctrl+S toggles 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
  • Startup tip explaining both paths (Option/Alt-drag bypass, Ctrl+S toggle)
  • Full rationale in .project/chat-mode-textual-postmortem.md §12

Supporting files

  • .opencode/agents/chat.md — lazy-loading chat agent definition
  • prompts/chat-initial.md — bootstrap prompt (reads codecome.yml, lists findings)
  • .project/chat-mode-textual-postmortem.md — bisection matrix, forbidden patterns (each silently freezes the main loop), safe extension recipes, diagnostic toolkit, changelog
  • Makefilechat: target with --agent chat, --prompt-file, --debug flag

Fixes

  • cvss_v4 frontmatter added to all 4 findings (CC-0001 through CC-0004) for compliance with the skilling frontmatter checker
  • Initial bootstrap prompt hidden — shows (initializing session…) (bold cyan) instead of echoing the full multi-line prompt file

Testing

  • 31 new chat-specific tests (ChatEventLoop, TextualConsoleProxy, render_and_log parity, transcript lifecycle, argparse)
  • 268 project total — all pass
  • make tests quality gate: pytest + frontmatter validation — clean

pruiz and others added 3 commits May 23, 2026 00:37
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>
@github-actions
Copy link
Copy Markdown

Coverage Report

Metric Value
Line Coverage 59.7%
Lines Covered 0 / 0

Download detailed HTML coverage reports per OS/Python from the workflow artifacts.

Generated by pytest-cov on 2026-05-22T22:55:43.861Z

@pruiz pruiz merged commit 62eff6e into master May 22, 2026
6 checks passed
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.

1 participant