-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Overview
Upgrade the Hermes CLI output rendering from basic ANSI text to rich streaming markdown with syntax-highlighted code blocks, colored diffs, content zone attribution, and navigable output blocks. This is a significant refactor of how cli.py handles agent output — moving from raw print() to a managed output buffer.
This brings the Hermes terminal experience to parity with Toad (best markdown rendering), Aider (best diff display), and Claude Code (best context management) — the three CLI agents that set the standard for terminal UX in 2025-2026.
Split from #504 for atomicity. The companion feature (status bar + token tracking) is tracked in #683.
Research Findings
Terminal Agent UX Leaders
Toad CLI — Best markdown rendering:
- High-performance streaming markdown with tables, syntax highlighting
- Notebook-like interactions: navigate/reuse previous conversation blocks
- SVG export of conversation content
Aider — Best diff display:
/diffcommand to preview changes before applying- Git-native atomic commits with descriptive messages
- Color-coded diff output with file headers
Claude Code — Best context management:
/compactwith configurable summary focus- "Document & Clear" workflow (dump to file, clear window, resume)
- Custom slash commands
Key Patterns Missing from Hermes CLI
- Streaming markdown: Output is plain text with basic ANSI. No table rendering, no header sizing, no nested list indentation during streaming.
- Diff highlighting: The
patchtool returns diffs in tool results, but they're not syntax-highlighted or visually distinct. - Content zones: No visual distinction between agent prose, tool calls, tool results, and system messages beyond basic formatting.
- Block navigation: Can't jump back to a specific code block, copy it, or reference it.
Current State in Hermes Agent
Output model (cli.py):
- Agent output goes through
_cprint()which usesprint_formatted_text()with ANSI passthrough - Output is NOT in a scrollable panel — it just prints to stdout above the prompt_toolkit input area
- The application runs with
full_screen=False - Tool progress is displayed via
print()from the agent thread with throttled repainting (0.25s intervals) - Response is wrapped in a simple box:
╭─ ⚕ Hermes ──╮ ... ╰──────╯
The fundamental challenge: Moving to rich rendering requires changing the output from "print and scroll away" to "managed buffer that can be navigated, searched, and re-rendered." This likely means switching to full_screen=True with a scrollable output Window.
Rich library is already imported but used minimally. The banner uses Rich panels and tables. Output is mostly plain text with ANSI codes.
Implementation Plan
Skill vs. Tool Classification
This is a core codebase change requiring a significant refactor of cli.py's output pipeline. Cannot be expressed as a skill.
Architecture Decision: Output Buffer
The key architectural question is how to manage output. Options:
Option A: Rich Console Capture
- Keep
full_screen=False - Use
rich.console.Consolewithrecord=Truefor structured output - Render markdown via
rich.markdown.Markdown - Render diffs via custom
rich.syntax.Syntax - Still prints to stdout, but with much better formatting
- Pro: minimal refactor. Con: no navigation, no scrollback management.
Option B: Full-Screen TUI with Output Panel
- Switch to
full_screen=True(or alternate screen per [UX]: CLI should use alternate screen (fullscreen) mode on launch #639) - Add a scrollable
Windowfor output above the input area - Manage output as a buffer of "blocks" (text, code, diff, tool call)
- Pro: full navigation, search, block management. Con: major refactor, breaks terminal scrollback.
Option C: Hybrid (Recommended)
- Phase 1: Option A (rich formatting via Rich console, no layout change)
- Phase 2: Option B (full TUI overhaul if there's demand)
Phased Rollout
Phase 1: Rich Output Formatting (Option A — minimal refactor)
- Replace
_cprint()ANSI passthrough withRichconsole rendering - Markdown rendering: headers, tables, code blocks with syntax highlighting, lists, blockquotes
- Diff rendering: when
patchtool returns a diff, render with green/red coloring and file headers - Content zone styling:
- Agent prose: default color
- Tool calls: dimmed/italic with emoji prefix (enhance existing system)
- Tool results: distinct background or left-border
- System messages: yellow/amber
- Errors: red with actionable context
- Inline code with distinct background color
- Code blocks with language label and copy hint
Phase 2: Block Indexing & Navigation (requires Option B)
- Assign IDs to output blocks:
[1] code block,[2] diff,[3] table /block <id>command to display a specific block/copy <id>command to copy block content to clipboard/blockscommand to list all blocks with previews- Keyboard shortcuts: Ctrl+Up/Down to jump between blocks
- Full-screen mode with scrollable output panel
- Search within output:
/search <term>or Ctrl+F
Phase 3: Advanced Features
/transcriptcommand: navigable view of full conversation with blocks/exportcommand: save conversation as well-formatted markdown- Split-pane mode: show tool progress in a side panel while output streams
- Subagent monitoring panel: when delegate_task is active, show status
- Side-by-side diff display (togglable from unified)
Pros & Cons
Pros
- Rich markdown makes output dramatically more readable
- Diff highlighting makes code review faster and more accurate
- Content zones reduce cognitive load (instantly see what's agent vs tool vs system)
- Phase 1 is achievable without major refactoring (just improve rendering)
- Block navigation (Phase 2) enables precise interaction with past output
Cons / Risks
- prompt_toolkit layout changes are fragile and hard to test
- Streaming markdown renderer must handle partial content (mid-code-block, mid-table)
- Full-screen mode (Phase 2) breaks terminal scrollback history
- Performance: complex rendering could slow streaming display
- Terminal compatibility: mosh, tmux, SSH may not support all ANSI features
- cli.py is already 3,620 lines — needs modularization before major refactor
Prerequisite Work
- cli.py modularization: Extract layout, callbacks, commands, state into separate modules under
hermes_cli/. The current 3,620-line file is too large for safe refactoring. - Feature: CLI Status Bar & Token/Cost Tracking — Persistent Context Window Visibility #683 (Status Bar) should land first — it's a simpler layout change that validates the approach.
- [UX]: CLI should use alternate screen (fullscreen) mode on launch #639 (Alternate Screen) is a prerequisite for Phase 2 full-screen mode.
Open Questions
- Option A (Rich formatting) or Option B (full TUI) for Phase 1? (Recommend A)
- Should cli.py be split into modules before this work starts?
- How do we handle terminals that don't support true color? Graceful 256/16-color degradation?
- Should block IDs be persistent across scrollback or reset per response?
- Is Textual (Rich's TUI framework) worth evaluating for Phase 2, or stay with prompt_toolkit?
- Should we add a
--plainflag for users who prefer unformatted output?
References
- Toad CLI — Best terminal markdown rendering
- Aider — Git-native diff display in terminal
- Rich library — Python rich text rendering (already a dependency)
- Textual — TUI framework for potential Phase 2
- Existing code: cli.py
_cprint()— current output function to replace - Existing code: banner.py — already uses Rich panels/tables (proves Rich works)
- Split from closed Feature: Enhanced CLI TUI — Rich Markdown Rendering, Diff Previews, Token Tracking & Navigable Output #504
- Depends on: Feature: CLI Status Bar & Token/Cost Tracking — Persistent Context Window Visibility #683 (Status Bar — simpler layout change, validates approach)
- Absorbs: [UX]: CLI should use alternate screen (fullscreen) mode on launch #639 (alternate screen mode — Phase 2), [UX]: /help and /personalities output needs better formatting and spacing #640 (/help formatting — Phase 1)
- Related: [Bug]: Blinking cursor or switching emojis causing prompt lines to flash repeatedly in terminal #464 (blinking cursor — rendering changes must not worsen this)
- Related: [UX]: Async slash commands (/skills search, etc.) need a loading indicator #636 (async loading indicators — integrated into rich output)