Vendor

CRUSH — Collaborative TUI Shell

Vendored terminal UI framework (github.com/charmbracelet/crush) — Bubble Tea terminal app infrastructure for AI-assisted development.

Go Bubble Tea Charm Ecosystem
Terminal AI assistant (GitHub Copilot CLI) · Warp AI

The Terminal AI TUI Gap

Terminal-based AI assistants need a rich TUI framework — chat history, streaming responses, tool call visualization, markdown rendering. Building this from scratch is significant work. CRUSH (from Charmbracelet) provides the Bubble Tea foundation: composable components, event-driven architecture, and a beautiful terminal UI. The task was to understand, vendor, and extend CRUSH for the Pollen ecosystem.

Most AI interactions in the terminal today are single-turn prompts piped through curl or wrapped in a thin REPL. There is no session history, no streaming visualisation, no structured tool call display. Products like GitHub Copilot CLI and Warp AI have demonstrated that developers want rich terminal-native AI experiences — but building the TUI plumbing from zero is months of work. By vendoring CRUSH, we jump-started with a production-quality Bubble Tea foundation and focused on the Pollen-specific customizations: multi-turn conversation, streaming Markdown rendering, and structured tool call cards.

Component-Based TUI Architecture

CRUSH is a Go Bubble Tea TUI app with a component-based architecture: chat view (message history with Markdown rendering), thinking block (streaming tool call visualization), provider abstraction (OpenAI-compatible API), and conversation management. The TUI uses Bubble Tea's Model/Update/View pattern with Lip Gloss styling and Bubbles components for spinners, text input, and viewport.

The core architecture follows a unidirectional data flow: user keystrokes trigger Update messages that mutate the model, View renders the current state to the terminal buffer. The chat engine maintains a slice of messages (user + assistant), each rendered via a Lip Gloss-styled component tree. Streamed responses arrive via a channel-driven provider abstraction and are incrementally appended to the assistant message buffer, causing View to re-render as tokens arrive. Tool calls are parsed from the stream and displayed as structured cards with status, duration, and output.

type Model struct {
    messages []Message
    provider Provider
}

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        if msg.Type == tea.KeyEnter {
            return m, m.provider.Stream(m.messages)
        }
    }
    return m, nil
}

func (m Model) View() string {
    return lipgloss.JoinVertical(lipgloss.Top, renderMessages(m.messages)...)
}
CRUSH — Collaborative TUI Shell STATUS Connected · Pollen Provider [Ctrl+C Quit] You Explain the mesh architecture of the Pollen system. Thinking ▶ Tool Call get_mesh_topology Status: running... args: { "format": "full", "include_dormant": true } 0.02s elapsed ✓ Tool Result Got mesh topology: 5 nodes, 4 active, 1 dormant, 12 QUIC peers Assistant The Pollen mesh currently has 5 nodes: your MacBook (leaderless), 2 Raspberry Pis, an Android phone, and a NAS. All are connected via QUIC/mTLS. You Show me the running WASM workloads. Thinking You Deploy the STT plugin to the Raspberry Pi. ▼ 6 more NORMAL : /ask /tools /help LIVE •

TUI View ↔ Bubble Tea Model ↔ Provider API ↔ LLM

BUBBLE TEA COMPONENT ARCHITECTURE Solid = sync call · Dashed = event/stream VIEW (LIP GLOSS) Chat View Message History Thinking Block Streaming Viz Input Bar Text · Commands Status Bar Spinner · Progress MODEL (UPDATE) Chat Model Conversation State Config Model Settings · Providers Update Logic Msg Dispatch Commands Effects · Side FX PROVIDER OpenAI Compatible API Abstraction Stream Handler Token Callback Function Calling Tool Dispatch Retry Logic Backoff · Timeout LLM Remote API OpenAI / Anthropic Local Model Ollama · API render msg call stream HTTP SSE

User Input → Streaming Response → Render

CHAT INTERACTION SEQUENCE USER INPUT Text Message Enter Key 1 MODEL UPDATE Dispatch Msg Set Loading 2 PROVIDER API Request Streaming=True 3 LLM STREAM Token-by-token Server-Sent Events 4 Stream → View Update (Bubble Tea Cmd) STREAMING TOKEN RENDER Token Buffer Accumulate Markdown Parse Render Styles View Update Lip Gloss Render Terminal Output Frame Render N Step Flow Stage (floating)

Languages, Frameworks & Ecosystem

Language
Go 1.22+
TUI Framework
Bubble Tea (Model/Update/View) Lip Gloss (styling) Bubbles (components: spinner, viewport, text input)
AI Provider
OpenAI-compatible provider API Streaming chat completions
Rendering
Markdown rendering (Glamour/Goldmark) ANSI terminal output

Key Decisions

ADR-001
Bubble Tea over Textual / Rich
Decision: Go Bubble Tea → static binary TUI
Go compiles to a single static binary with no runtime dependencies — critical for the Pollen mesh where nodes range from macOS to ARM Linux. Bubble Tea's composable Model/Update/View pattern mirrors React's mental model, making it easy to build complex component trees. The Charm ecosystem provides ready-made components (chat, spinner, viewport, text input) that reduce UI plumbing by 60% compared to rolling raw terminal escapes with Textual/Python. Native terminal performance means no GC pauses during streaming token rendering.
ADR-002
Vendor over Dependency
Decision: Full vendored copy → complete code control
Vendoring CRUSH (charmbracelet/crush) gives us complete control over the codebase. We can extend the chat view for custom tool format rendering (structured cards with status, duration, output) without upstream coordination. We can patch Bubble Tea internals for Pollen-specific streaming behaviour. No breaking changes from upstream can stall development — the code is ours. The tradeoff is manual patch integration if we ever want to sync upstream improvements, but the stability of a vendored foundation outweighs this for a production terminal assistant.
ADR-003
Lip Gloss styling over raw terminal codes — composable style definitions, consistent color theming, no escape-sequence management
Decision: Lip Gloss → composable terminal styling
Lip Gloss provides a declarative styling model: styles are composed as Go structs with named fields (foreground, background, alignment, padding, margin, border) rather than concatenated ANSI escape sequences. This eliminates a class of bugs where escape sequences are mismatched, unclosed, or interleaved with text content. Color theming is consistent through a shared lipgloss.Color palette defined once and referenced by name, rather than raw \033[38;5;XXXm codes scattered through rendering code. Composable styles (e.g., baseStyle.Copy().Bold(true).Foreground(accentColor)) reduce UI code by ~40% compared to string-based terminal styling, and make theme switching a single palette swap instead of a codebase-wide find-and-replace.

Key Development Sessions

CRUSH Setup 256 messages
366K tokens
0.12¢
Run CRUSH
Initial setup and exploration of the CRUSH codebase. Cloned the repository, built the binary, explored the CLI entry points, understood the Bubble Tea application lifecycle, and verified the chat loop operates correctly with the default provider configuration.
Tool Execution
22 messages
79K tokens
Explore parallel tool execution
Investigated CRUSH's tool call architecture: how the streaming provider parses tool calls from the response stream, how they are dispatched to local Go functions, and how results are fed back into the conversation context. Mapped the call chain from provider response to tool execution to result display.
Component Analysis
16 messages
111K tokens
Explore TUI styling code
Deep dive into CRUSH's Bubble Tea component structure: the chat view's message rendering pipeline, how Lip Gloss styles are composed and inherited, the viewport component for scrollable message history, and the spinner component for loading states during streaming responses.
Provider Deep Dive
13 messages
74K tokens
Find provider setup code
Analyzed the OpenAI-compatible provider configuration layer: how API base URLs, model names, authentication tokens, and streaming parameters are wired through the config system. Documented the provider interface contract for custom provider integration.
Provider Customization
10 messages
24K tokens
Customize opencode configuration
Adapted CRUSH's provider configuration for the Pollen ecosystem: configured a custom API base URL pointing to the Pollen provider proxy, set the appropriate model identifier, and tested end-to-end streaming chat with tool call integration against a local provider instance.

Project Metrics

5
Sessions
5
Key Decisions
653K
Tokens Consumed
0.17¢
Total Cost

Complete understanding and vendoring of the CRUSH TUI framework. The vendored codebase is integrated into the Pollen ecosystem as the terminal AI assistant interface, providing chat history, streaming response rendering, tool call visualization, and markdown display — all within a native terminal experience. Industry alignment: GitHub Copilot CLI (terminal-based AI coding assistant) and Warp AI (AI-native terminal with inline suggestions).

GitHub Copilot CLI — Terminal AI Assistant Warp AI — AI-Native Terminal