1---
2name: charm-crush
3description: Architecture patterns from Crush, charmbracelet's production agentic coding CLI built on bubbletea v2, lipgloss v2, bubbles v2, glamour v2, and ultraviolet. Use when building production bubbletea apps, composing charm TUI components at scale, designing agentic CLI tools, implementing streaming LLM UIs, or asking about crush internals. NOT for individual charm library basics.
4argument-hint: "[pattern or component to look up]"
5---
6
7# Charm Crush - Production Agentic TUI Patterns
8
9Crush is charmbracelet's agentic coding CLI - their answer to Claude Code, built entirely with their own TUI stack. This skill captures the architecture patterns, component composition strategies, and design decisions from a production app with 10+ composed components, real-time LLM streaming, dialog overlays, and a full agent loop.
10
11Source: `github.com/charmbracelet/crush` (FSL-1.1-MIT)
12
13## Architecture Overview
14
15Crush follows a clear layered architecture:
16
17```
18main.go (cobra CLI)
19 -> internal/app/app.go (wiring: DB, config, agents, LSP, MCP, events)
20 -> internal/agent/ (LLM conversations, tool execution, streaming)
21 -> internal/ui/ (bubbletea v2 TUI)
22 -> internal/permission/ (tool approval via pubsub)
23 -> internal/skills/ (Agent Skills open standard)
24 -> internal/pubsub/ (generic typed broker for cross-component messaging)
25 -> internal/session/ (SQLite persistence via sqlc)
26```
27
28### Key Dependencies (go.mod)
29
30| Library | Version | Role |
31|---------|---------|------|
32| `charm.land/bubbletea/v2` | v2.0.2 | TUI framework |
33| `charm.land/lipgloss/v2` | v2.0.2 | Terminal styling |
34| `charm.land/bubbles/v2` | v2.0.0 | Reusable components (textarea, viewport, spinner, help) |
35| `charm.land/glamour/v2` | v2.0.0 | Markdown rendering |
36| `charm.land/fantasy` | v0.16.0 | LLM provider abstraction (Anthropic, OpenAI, Gemini, Bedrock, etc.) |
37| `charmbracelet/ultraviolet` | - | Screen-buffer rendering system |
38| `charm.land/catwalk` | v0.31.0 | Model registry + golden-file testing |
39| `charmbracelet/x/exp/charmtone` | - | Color palette system |
40
41## Decision Tree: How Crush Solves Common Problems
42
43Use $ARGUMENTS to find the relevant pattern, or browse by category:
44
45### TUI Architecture
46- **"How do I structure a large bubbletea app?"** -> Read `references/tui-patterns.md` (Centralized Model pattern)
47- **"How do I handle window resize?"** -> Read `references/tui-patterns.md` (Layout System)
48- **"How do I compose 10+ components?"** -> Read `references/component-composition.md`
49- **"How do I manage focus between panes?"** -> Read `references/tui-patterns.md` (Focus Management)
50- **"How do I render streaming content?"** -> Read `references/tui-patterns.md` (Streaming Rendering)
51- **"How do I do overlays/modals?"** -> Read `references/component-composition.md` (Dialog System)
52
53### Agent Loop
54- **"How does the LLM loop work?"** -> Read `references/agent-loop.md`
55- **"How do I handle tool approval?"** -> Read `references/agent-loop.md` (Permission System)
56- **"How do I stream LLM responses to the TUI?"** -> Read `references/agent-loop.md` (Streaming Bridge)
57
58### Design & Styling
59- **"How do I build a polished terminal UI?"** -> Read `references/design-system.md`
60- **"What colors does charm use?"** -> Read `references/design-system.md` (Charmtone Palette)
61- **"How do I structure styles for a large app?"** -> Read `references/design-system.md` (Styles Struct)
62
63## Core Pattern: The Centralized Model
64
65The single most important architectural decision in Crush. The `UI` struct in `internal/ui/model/ui.go` is the **sole bubbletea model**. Sub-components are NOT bubbletea models - they're stateful structs with imperative methods.
66
67```
68UI (the only tea.Model)
69 |-- Chat (stateful struct, no Update method)
70 |-- textarea (bubbles/v2 textarea.Model)
71 |-- dialog.Overlay (stack of Dialog interfaces)
72 |-- completions (non-standard Update returning bool)
73 |-- attachments (non-standard Update)
74 |-- status, header, pills (render methods on UI)
75```
76
77This means:
78- All message routing happens in one giant `switch msg.(type)` in `UI.Update()`
79- Focus state determines key routing (editor vs chat)
80- Components expose methods like `HandleMouseDown()`, `ScrollBy()`, `SetMessages()` instead of `Update(tea.Msg)`
81- Side effects return `tea.Cmd` from methods, not from Update
82
83### Why This Works
84
85Traditional Elm architecture (each component gets its own Update/View) breaks down at scale because:
861. Message routing becomes a maze of forwarding
872. Shared state requires complex message passing
883. Focus management needs a central coordinator anyway
89
90Crush sidesteps this by making the parent the single source of truth for all state transitions.
91
92## Rendering Pipeline: Ultraviolet Screen Buffer
93
94Crush uses a hybrid rendering approach instead of pure string concatenation:
95
961. `View()` creates an `ultraviolet.ScreenBuffer` sized to terminal dimensions
972. Layout is computed as `image.Rectangle` regions via `ultraviolet/layout.SplitVertical/SplitHorizontal`
983. Components draw into sub-regions: `uv.NewStyledString(str).Draw(scr, rect)`
994. Dialogs draw last (overlay on top of everything)
1005. `canvas.Render()` flattens to a string for bubbletea
101
102This enables:
103- Overlapping content (dialogs, completions popup)
104- Precise cursor positioning across components
105- Screen-based layout math instead of string width guessing
106
107## Communication: Typed Pub/Sub
108
109Agent and TUI run on separate goroutines. They communicate through a generic typed broker (`internal/pubsub`) that publishes events which bubbletea converts to `tea.Msg`. See `references/agent-loop.md` for the full pattern and event types.
110
111## File Map
112
113| What | Where | Read for |
114|------|-------|----------|
115| Main TUI model | `internal/ui/model/ui.go` | Centralized model, Update loop, layout |
116| Chat component | `internal/ui/model/chat.go` | List wrapping, animation, mouse handling |
117| Tool renderers | `internal/ui/chat/*.go` | Per-tool rendering (bash, file, search, etc.) |
118| Dialog system | `internal/ui/dialog/` | Modal overlays, permissions, models picker |
119| List component | `internal/ui/list/list.go` | Lazy-rendered scrollable list |
120| Styles | `internal/ui/styles/styles.go` | Full design system |
121| Agent loop | `internal/agent/agent.go` | LLM streaming, tool execution |
122| Coordinator | `internal/agent/coordinator.go` | Multi-provider setup, model management |
123| Tool definitions | `internal/agent/tools/*.go` | Tool implementations (.go + .md pairs) |
124| Permission system | `internal/permission/permission.go` | Tool approval flow |
125| Skills loader | `internal/skills/skills.go` | SKILL.md parsing and discovery |
126| Pub/sub | `internal/pubsub/` | Typed event broker |
127| App wiring | `internal/app/app.go` | Service initialization |
128| Config | `internal/config/` | crush.json loading, provider config |
129| UI architecture guide | `internal/ui/AGENTS.md` | Charm's own UI development instructions |
130
131## Reference Files
132
133For detailed patterns with code examples from the actual source:
134
135- `references/tui-patterns.md` - Layout system, focus management, key handling, streaming, responsive design
136- `references/agent-loop.md` - Agent loop, fantasy SDK, tool system, permission flow, pubsub bridge
137- `references/design-system.md` - Charmtone colors, Styles struct, icon system, typography
138- `references/component-composition.md` - Interface hierarchy, dialog stack, list composition, chat items