Skills bubbletea-code-review
Reviews BubbleTea TUI code for proper Elm architecture, model/update/view patterns, and Lipgloss styling. Use when reviewing terminal UI code using charmbracelet/bubbletea.
install
source · Clone the upstream repo
git clone https://github.com/openclaw/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/anderskev/bubbletea-code-review" ~/.claude/skills/clawdbot-skills-bubbletea-code-review && rm -rf "$T"
manifest:
skills/anderskev/bubbletea-code-review/SKILL.mdsource content
BubbleTea Code Review
Quick Reference
| Issue Type | Reference |
|---|---|
| Elm architecture, tea.Cmd as data | references/elm-architecture.md |
| Model state, message handling | references/model-update.md |
| View rendering, Lipgloss styling | references/view-styling.md |
| Component composition, Huh forms | references/composition.md |
| Bubbles components (list, table, etc.) | references/bubbles-components.md |
CRITICAL: Avoid False Positives
Read elm-architecture.md first! The most common review mistake is flagging correct patterns as bugs.
NOT Issues (Do NOT Flag These)
| Pattern | Why It's Correct |
|---|---|
| is returned immediately; runtime executes async |
Value receiver on | Standard BubbleTea pattern; model returned by value |
Nested | Normal component composition |
Helper functions returning | Creates command descriptor, no I/O in Update |
| Commands execute concurrently by runtime |
ACTUAL Issues (DO Flag These)
| Pattern | Why It's Wrong |
|---|---|
in Update | Blocks UI thread |
in Update | Network I/O blocks |
in Update | Freezes UI |
in Update (blocking) | May block indefinitely |
in Update | Blocking call |
Review Checklist
Architecture
- No blocking I/O in Update() (file, network, sleep)
- Helper functions returning
are NOT flagged as blockingtea.Cmd - Commands used for all async operations
Model & Update
- Model is immutable (Update returns new model, not mutates)
- Init returns proper initial command (or nil)
- Update handles all expected message types
- WindowSizeMsg handled for responsive layout
- tea.Batch used for multiple commands
- tea.Quit used correctly for exit
View & Styling
- View is a pure function (no side effects)
- Lipgloss styles defined once, not in View
- Key bindings use key.Matches with help.KeyMap
Components
- Sub-component updates propagated correctly
- Bubbles components initialized with dimensions
- Huh forms embedded via Update loop (not Run())
Critical Patterns
Model Must Be Immutable
// BAD - mutates model func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.items = append(m.items, newItem) // mutation! return m, nil } // GOOD - returns new model func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { newItems := make([]Item, len(m.items)+1) copy(newItems, m.items) newItems[len(m.items)] = newItem m.items = newItems return m, nil }
Commands for Async/IO
// BAD - blocking in Update func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { data, _ := os.ReadFile("config.json") // blocks UI! m.config = parse(data) return m, nil } // GOOD - use commands func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, loadConfigCmd() } func loadConfigCmd() tea.Cmd { return func() tea.Msg { data, err := os.ReadFile("config.json") if err != nil { return errMsg{err} } return configLoadedMsg{parse(data)} } }
Styles Defined Once
// BAD - creates new style each render func (m Model) View() string { style := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("205")) return style.Render("Hello") } // GOOD - define styles at package level or in model var titleStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("205")) func (m Model) View() string { return titleStyle.Render("Hello") }
When to Load References
- First time reviewing BubbleTea → elm-architecture.md (prevents false positives)
- Reviewing Update function logic → model-update.md
- Reviewing View function, styling → view-styling.md
- Reviewing component hierarchy → composition.md
- Using Bubbles components → bubbles-components.md
Review Questions
- Is Update() free of blocking I/O? (NOT: "is the cmd helper blocking?")
- Is the model immutable in Update?
- Are Lipgloss styles defined once, not in View?
- Is WindowSizeMsg handled for resizing?
- Are key bindings documented with help.KeyMap?
- Are Bubbles components sized correctly?