원클릭으로
add-component
Scaffold a new Bubble Tea TUI component following lstk's UI patterns. Use when adding a new reusable UI element.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Scaffold a new Bubble Tea TUI component following lstk's UI patterns. Use when adding a new reusable UI element.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Create a GitHub pull request following lstk conventions with proper title, description, and ticket references.
Scaffold a new CLI subcommand following lstk patterns. Use when adding a new command to the CLI.
Add a new output event type to the event/sink system. Use when adding a new kind of event for domain-to-UI communication.
Review a PR against lstk architectural patterns and coding conventions. Use when asked to review a pull request.
| name | add-component |
| description | Scaffold a new Bubble Tea TUI component following lstk's UI patterns. Use when adding a new reusable UI element. |
| argument-hint | <component-name> |
| allowed-tools | Read, Write, Edit, Glob, Grep |
Scaffold a new Bubble Tea component named $ARGUMENTS.
Read these files first — they are the source of truth:
internal/ui/components/spinner.go — stateful component with Update/View patterninternal/ui/components/error_display.go — simple show/hide component driven by eventsinternal/ui/components/header.go — pure presentational component (View only)internal/ui/styles/styles.go — all style definitionsinternal/ui/app.go — how components are composed in the main App modelCreate internal/ui/components/<name>.go with:
A struct holding component state:
type <Name> struct {
// Private fields for internal state
visible bool
}
A constructor:
func New<Name>() <Name> {
return <Name>{}
}
State mutation methods that return a new copy (value receiver pattern, like Spinner):
func (c <Name>) Show(...) <Name> {
c.visible = true
// set fields
return c
}
A View method:
func (c <Name>) View() string {
if !c.visible {
return ""
}
// Render using styles from internal/ui/styles
}
An Update method (only if the component handles tea.Msg internally, like Spinner):
func (c <Name>) Update(msg tea.Msg) (<Name>, tea.Cmd) {
// Must be non-blocking
}
In internal/ui/styles/styles.go, add new style variables with semantic names:
var (
<Name>Style = lipgloss.NewStyle().
Foreground(lipgloss.Color("..."))
)
Use existing palette constants (NimboDarkColor, NimboMidColor, NimboLightColor) or standard ANSI color codes. Name styles after what they represent, not how they look.
In internal/ui/app.go:
App structNewApp()Update() to drive the componentView() at the appropriate positionCreate internal/ui/components/<name>_test.go with tests covering:
Follow the existing test patterns in spinner_test.go or error_display_test.go.
If the component is driven by a new domain event, use /add-event to create the event type first. The component should consume the event — not define it. Events live in internal/output/, not in internal/ui/components/.
If the component needs messages that are purely UI concerns (not domain events), define them in the component file with the ...Msg suffix:
type <Name>DoneMsg struct{}
These are for internal component coordination only and should not appear in internal/output/.
Update() blocking — no network calls, no file I/O, no channel waitsinternal/output/events.gointernal/output for event types and internal/ui/styles for styling)