ui

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 9, 2026 License: MIT Imports: 38 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var SlashCommands = []SlashCommand{
	{
		Name:        "/help",
		Description: "Show available commands and usage information",
		Category:    "Info",
		Aliases:     []string{"/h", "/?"},
	},
	{
		Name:        "/tools",
		Description: "List all available MCP tools",
		Category:    "Info",
		Aliases:     []string{"/t"},
	},
	{
		Name:        "/servers",
		Description: "Show connected MCP servers",
		Category:    "Info",
		Aliases:     []string{"/s"},
	},

	{
		Name:        "/clear",
		Description: "Clear conversation and start fresh",
		Category:    "System",
		Aliases:     []string{"/c", "/cls"},
	},
	{
		Name:        "/usage",
		Description: "Show token usage statistics",
		Category:    "Info",
		Aliases:     []string{"/u"},
	},
	{
		Name:        "/reset-usage",
		Description: "Reset usage statistics",
		Category:    "System",
		Aliases:     []string{"/ru"},
	},
	{
		Name:        "/clear-queue",
		Description: "Clear all queued messages",
		Category:    "System",
		Aliases:     []string{"/cq"},
	},
	{
		Name:        "/compact",
		Description: "Summarise older messages to free context space",
		Category:    "System",
		Aliases:     []string{"/co"},
	},
	{
		Name:        "/model",
		Description: "Switch to a different model",
		Category:    "System",
		Aliases:     []string{"/m"},
	},
	{
		Name:        "/thinking",
		Description: "Set thinking/reasoning level (off, minimal, low, medium, high)",
		Category:    "System",
		Aliases:     []string{"/think"},
		Complete: func(prefix string) []string {
			levels := models.ThinkingLevels()
			var matches []string
			for _, l := range levels {
				s := string(l)
				if prefix == "" || strings.HasPrefix(s, strings.ToLower(prefix)) {
					matches = append(matches, s)
				}
			}
			return matches
		},
	},
	{
		Name:        "/quit",
		Description: "Exit the application",
		Category:    "System",
		Aliases:     []string{"/q", "/exit"},
	},

	{
		Name:        "/tree",
		Description: "Navigate session tree (switch branches)",
		Category:    "Navigation",
	},
	{
		Name:        "/fork",
		Description: "Branch from an earlier message",
		Category:    "Navigation",
	},
	{
		Name:        "/new",
		Description: "Start a new session",
		Category:    "Navigation",
		Aliases:     []string{"/n"},
	},
	{
		Name:        "/name",
		Description: "Set a display name for this session",
		Category:    "Navigation",
	},
	{
		Name:        "/session",
		Description: "Show session info and statistics",
		Category:    "Info",
	},
}

SlashCommands provides the global registry of all available slash commands in the application. Commands are organized by category (Info, System) and include their primary names, descriptions, and alternative aliases.

Functions

func AdaptiveColor

func AdaptiveColor(light, dark string) color.Color

AdaptiveColor picks between a light-mode and dark-mode hex color string based on the detected terminal background. This replaces the old lipgloss.AdaptiveColor{Light: ..., Dark: ...} pattern from v1.

func ApplyGradient

func ApplyGradient(text string, colorA, colorB color.Color) string

ApplyGradient applies a color gradient from colorA to colorB across the text. Uses ~8 color stops for performance rather than per-character coloring.

func BaseStyle

func BaseStyle() lipgloss.Style

BaseStyle returns a new, empty lipgloss style that can be customized with additional styling methods. This serves as the foundation for building more complex styled components.

func CreateBadge

func CreateBadge(text string, c color.Color) string

CreateBadge generates a styled badge or label with inverted colors (text on colored background) for highlighting important tags, statuses, or categories.

func CreateGradientText

func CreateGradientText(text string, startColor, endColor color.Color) string

CreateGradientText creates styled text with a gradient effect between two colors.

func CreateProgressBar

func CreateProgressBar(width int, percentage float64, theme Theme) string

CreateProgressBar generates a visual progress bar with filled and empty segments based on the percentage complete. The bar uses Unicode block characters for smooth appearance and theme colors to indicate progress.

func CreateSeparator

func CreateSeparator(width int, char string, c color.Color) string

CreateSeparator generates a horizontal separator line with the specified width, character, and color. Useful for visually dividing sections of content in the UI.

func ExtractAtPrefix added in v0.4.0

func ExtractAtPrefix(line string, cursorCol int) (hasAt bool, prefix string, startIdx int)

ExtractAtPrefix checks the current line for an @-file trigger at cursorCol. It returns:

  • hasAt: true if a valid @ trigger was found
  • prefix: the text after @ (possibly empty) that the user has typed so far
  • startIdx: byte offset of the @ character in the line

The @ must appear at the start of the line or after whitespace. Quoted paths are supported: @"path with spaces" — the returned prefix strips quotes.

func FormatCompactLine

func FormatCompactLine(symbol, label, content string, symbolColor, labelColor, contentColor color.Color) string

FormatCompactLine assembles a complete compact mode message line with consistent spacing and styling. Combines a symbol, fixed-width label, and content with their respective colors to create a uniform appearance across all message types.

func GetAllCommandNames

func GetAllCommandNames() []string

GetAllCommandNames returns a complete list of all command names and their aliases. This is useful for command completion, validation, and help display. The returned slice contains both primary command names and all alternative aliases.

func GetMarkdownRenderer

func GetMarkdownRenderer(width int) *glamour.TermRenderer

GetMarkdownRenderer creates and returns a configured glamour.TermRenderer for rendering markdown content with syntax highlighting and proper formatting. The renderer is customized with our theme colors and adapted to the specified width.

func IsDarkBackground

func IsDarkBackground() bool

IsDarkBackground returns the cached terminal background detection result.

func ProcessFileAttachments added in v0.4.0

func ProcessFileAttachments(text string, cwd string) string

ProcessFileAttachments scans the user's input text for @file references, reads each referenced file, and returns the text with @tokens replaced by XML-wrapped file content. Non-file @ tokens (like email addresses) are left unchanged.

Returns the original text unchanged if no valid @file references are found.

func SetTheme

func SetTheme(theme Theme)

SetTheme updates the global UI theme, affecting all subsequent rendering operations. This allows runtime theme switching for different visual preferences.

func StyleCard

func StyleCard(width int, theme Theme) lipgloss.Style

StyleCard creates a lipgloss style for card-like containers with rounded borders, padding, and appropriate width. Used for grouping related content in a visually distinct box.

func StyleCompactContent

func StyleCompactContent(c color.Color) lipgloss.Style

StyleCompactContent creates a simple lipgloss style for message content in compact mode, applying only color without additional formatting.

func StyleCompactLabel

func StyleCompactLabel(c color.Color) lipgloss.Style

StyleCompactLabel creates a lipgloss style for message labels in compact mode with fixed width for alignment and bold colored text for readability.

func StyleCompactSymbol

func StyleCompactSymbol(symbol string, c color.Color) lipgloss.Style

StyleCompactSymbol creates a lipgloss style for message type indicators in compact mode, using bold colored text to distinguish different message categories.

func StyleError

func StyleError(theme Theme) lipgloss.Style

StyleError creates a lipgloss style for error messages using red colors with bold text to ensure visibility of problems or failures.

func StyleHeader

func StyleHeader(theme Theme) lipgloss.Style

StyleHeader creates a lipgloss style for primary headers using the theme's primary color with bold text for emphasis and hierarchy.

func StyleInfo

func StyleInfo(theme Theme) lipgloss.Style

StyleInfo creates a lipgloss style for informational messages using blue colors with bold text for general notifications and status updates.

func StyleMuted

func StyleMuted(theme Theme) lipgloss.Style

StyleMuted creates a lipgloss style for de-emphasized text using muted colors and italic formatting, suitable for supplementary or less important information.

func StyleSubheader

func StyleSubheader(theme Theme) lipgloss.Style

StyleSubheader creates a lipgloss style for secondary headers using the theme's secondary color with bold text, providing visual hierarchy below primary headers.

func StyleSuccess

func StyleSuccess(theme Theme) lipgloss.Style

StyleSuccess creates a lipgloss style for success messages using green colors with bold text to indicate successful operations or positive outcomes.

func StyleWarning

func StyleWarning(theme Theme) lipgloss.Style

StyleWarning creates a lipgloss style for warning messages using yellow/amber colors with bold text to draw attention to potential issues or cautions.

func WithAlign

func WithAlign(align lipgloss.Position) renderingOption

WithAlign returns a renderingOption that sets the horizontal alignment of the block content within its container. The align parameter accepts lipgloss.Left, lipgloss.Center, or lipgloss.Right positions.

func WithBackground

func WithBackground(c color.Color) renderingOption

WithBackground returns a renderingOption that sets the background color for the entire block. The color parameter accepts any color.Color value, typically a lipgloss hex color (e.g. lipgloss.Color("#1e1e2e")).

func WithBorderColor

func WithBorderColor(c color.Color) renderingOption

WithBorderColor returns a renderingOption that sets the border color for the block. The color parameter uses lipgloss.AdaptiveColor to support both light and dark terminal themes automatically.

func WithFullWidth

func WithFullWidth() renderingOption

WithFullWidth returns a renderingOption that configures the block renderer to expand to the full available width of its container. When enabled, the block will fill the entire horizontal space rather than sizing to its content.

func WithMarginBottom

func WithMarginBottom(margin int) renderingOption

WithMarginBottom returns a renderingOption that sets the bottom margin for the block. The margin is specified in number of lines and adds vertical space below the block.

func WithMarginTop

func WithMarginTop(margin int) renderingOption

WithMarginTop returns a renderingOption that sets the top margin for the block. The margin is specified in number of lines and adds vertical space above the block.

func WithNoBorder

func WithNoBorder() renderingOption

WithNoBorder returns a renderingOption that disables all borders on the block, rendering content with only padding.

func WithPaddingBottom

func WithPaddingBottom(padding int) renderingOption

WithPaddingBottom returns a renderingOption that sets the bottom padding for the block content. The padding is specified in number of lines and adds vertical space between the content and the bottom border.

func WithPaddingLeft

func WithPaddingLeft(padding int) renderingOption

WithPaddingLeft returns a renderingOption that sets the left padding for the block content. The padding is specified in number of characters and adds horizontal space between the left border and the content.

func WithPaddingRight

func WithPaddingRight(padding int) renderingOption

WithPaddingRight returns a renderingOption that sets the right padding for the block content. The padding is specified in number of characters and adds horizontal space between the content and the right border.

func WithPaddingTop

func WithPaddingTop(padding int) renderingOption

WithPaddingTop returns a renderingOption that sets the top padding for the block content. The padding is specified in number of lines and adds vertical space between the top border and the content.

func WithWidth

func WithWidth(width int) renderingOption

WithWidth returns a renderingOption that sets a specific width for the block in characters. This overrides the default container width and allows precise control over the block's horizontal dimensions.

Types

type AgentInterface

type AgentInterface interface {
	GetLoadingMessage() string
	GetTools() []any                // Using any to avoid importing tool types
	GetLoadedServerNames() []string // Add this method for debug config
	GetMCPToolCount() int           // Tools loaded from external MCP servers
	GetExtensionToolCount() int     // Tools registered by extensions
}

AgentInterface defines the minimal interface required from the agent package to avoid circular dependencies while still accessing necessary agent functionality.

type AppController

type AppController interface {
	// Run queues or immediately starts a new agent step with the given prompt.
	// Returns the current queue depth: 0 means the prompt started immediately
	// (or the app is closed), >0 means it was queued. The caller must update
	// UI state (e.g. queueCount) based on the return value — Run does NOT
	// send events to the program to avoid deadlocking when called from
	// within Update().
	Run(prompt string) int
	// CancelCurrentStep cancels any in-progress agent step.
	CancelCurrentStep()
	// QueueLength returns the number of prompts currently waiting in the queue.
	QueueLength() int
	// ClearQueue discards all queued prompts. The caller must update UI state
	// (e.g. queueCount) — ClearQueue does NOT send events to the program to
	// avoid deadlocking when called from within Update().
	ClearQueue()
	// ClearMessages clears the conversation history.
	ClearMessages()
	// CompactConversation summarises older messages to free context space.
	// Runs asynchronously; results are delivered via CompactCompleteEvent or
	// CompactErrorEvent sent through the registered tea.Program. Returns an
	// error synchronously if compaction cannot be started (e.g. agent is busy).
	// customInstructions is optional text appended to the summary prompt.
	CompactConversation(customInstructions string) error
	// GetTreeSession returns the tree session manager, or nil if tree sessions
	// are not enabled. Used by slash commands like /tree, /fork, /session.
	GetTreeSession() *session.TreeManager
	// SendEvent sends a tea.Msg to the program asynchronously. Safe to call
	// from any goroutine. Used by extension command goroutines to deliver
	// results back to the TUI without going through tea.Cmd (which can stall
	// when the goroutine blocks on interactive prompts).
	SendEvent(tea.Msg)
	// AddContextMessage adds a user-role message to the conversation history
	// without triggering an LLM response. Used by the ! shell command prefix
	// to inject command output into context so the LLM can reference it in
	// subsequent turns.
	AddContextMessage(text string)
	// RunWithFiles queues a multimodal prompt (text + images) for execution.
	// Behaves like Run but includes file parts (e.g. clipboard images)
	// alongside the text. Returns the current queue depth (0 = started
	// immediately, >0 = queued).
	RunWithFiles(prompt string, files []fantasy.FilePart) int
}

AppController is the interface the parent TUI model uses to interact with the app layer. It is satisfied by *app.App once that is created (TAS-4). Using an interface here keeps model.go compilable before app.App exists, and makes the parent model easily testable with a mock.

type AppModel

type AppModel struct {
	// contains filtered or unexported fields
}

AppModel is the root Bubble Tea model for the interactive TUI. It owns the state machine, routes events to child components, and manages the overall layout. It holds a reference to the app layer (AppController) for triggering agent work and queue operations.

Layout (stacked, no alt screen):

┌─ [custom header] (optional, from extension) ──────┐
├─ stream region (variable height) ─────────────────┤
│                                                    │
├─ separator line (with optional queue count) ───────┤
│  [above widgets]                                   │
│  queued  How do I fix the build?                   │
│  queued  Also check the tests                      │
├─ input region (fixed height from textarea) ────────┤
│  [below widgets]                                   │
│ Tokens: 23.4K (12%) | Cost: $0.00  provider·model │
├─ [custom footer] (optional, from extension) ──────┤
└────────────────────────────────────────────────────┘

The status bar is always present (1 line) to avoid layout shifts that occurred when usage info appeared/disappeared conditionally.

Completed responses are emitted above the BT-managed region via tea.Println() before the model resets for the next interaction.

func NewAppModel

func NewAppModel(appCtrl AppController, opts AppModelOptions) *AppModel

NewAppModel creates a new AppModel. The appCtrl parameter must not be nil. opts provides display configuration; zero values are valid (uses defaults).

To use with the concrete *app.App type, pass it directly — *app.App satisfies AppController once the app layer is implemented (TAS-4).

NewAppModel constructs all child components (InputComponent, StreamComponent) using the provided options.

func (*AppModel) Init

func (m *AppModel) Init() tea.Cmd

Init implements tea.Model. Initialises child components. Startup info is printed to stdout before the program starts via PrintStartupInfo().

func (*AppModel) PrintStartupInfo

func (m *AppModel) PrintStartupInfo()

PrintStartupInfo prints the startup banner (model name, context, skills, tool counts) to stdout. Call this before program.Run() so the messages are visible above the Bubble Tea managed region.

All startup information is rendered inside a single system message block.

func (*AppModel) Update

func (m *AppModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model. It is the heart of the state machine: it routes incoming messages to children and handles state transitions.

func (*AppModel) View

func (m *AppModel) View() tea.View

View implements tea.Model. It renders the stacked layout: stream region + separator + [queued messages] + input region + status bar. The status bar is always present (1 line) to avoid layout shifts. When the tree selector is active, it replaces the stream region.

type AppModelOptions

type AppModelOptions struct {
	// CompactMode selects the compact renderer for message formatting.
	CompactMode bool

	// ModelName is the display name of the model (e.g. "claude-sonnet-4-5").
	ModelName string

	// ProviderName is the LLM provider (e.g. "anthropic", "openai").
	// Used for the startup "Model loaded" message.
	ProviderName string

	// LoadingMessage is an optional informational message from the agent
	// (e.g. GPU fallback info). Displayed at startup when non-empty.
	LoadingMessage string

	// Cwd is the working directory for @file autocomplete and path resolution.
	// If empty, @file features are disabled.
	Cwd string

	// Width is the initial terminal width in columns.
	Width int

	// Height is the initial terminal height in rows.
	Height int

	// ServerNames holds loaded MCP server names for the /servers command.
	ServerNames []string

	// ToolNames holds available tool names for the /tools command.
	ToolNames []string

	// UsageTracker provides token usage statistics for /usage and /reset-usage.
	// May be nil if usage tracking is unavailable for the current model.
	UsageTracker *UsageTracker

	// ExtensionCommands are slash commands registered by extensions. They
	// appear in autocomplete, /help, and are dispatched when submitted.
	ExtensionCommands []ExtensionCommand

	// ContextPaths lists absolute paths of loaded context files (e.g.
	// AGENTS.md). Displayed in the [Context] startup section.
	ContextPaths []string

	// SkillItems lists loaded skills for the [Skills] startup section.
	SkillItems []SkillItem

	// MCPToolCount is the number of tools loaded from external MCP servers.
	MCPToolCount int

	// ExtensionToolCount is the number of tools registered by extensions.
	ExtensionToolCount int

	// GetWidgets returns current extension widgets for a given placement
	// ("above" or "below"). Called during View() to render persistent
	// extension widgets. May be nil if no extensions are loaded.
	GetWidgets func(placement string) []WidgetData

	// GetHeader returns the current custom header set by an extension, or
	// nil if no header is active. Called during View() to render a
	// persistent header above the stream region. May be nil.
	GetHeader func() *WidgetData

	// GetFooter returns the current custom footer set by an extension, or
	// nil if no footer is active. Called during View() to render a
	// persistent footer below the status bar. May be nil.
	GetFooter func() *WidgetData

	// GetToolRenderer returns the extension-provided tool renderer for a
	// specific tool name, or nil if no custom renderer is registered.
	// Called during tool result rendering to check for custom formatting.
	// May be nil if no extensions are loaded.
	GetToolRenderer func(toolName string) *ToolRendererData

	// GetEditorInterceptor returns the current editor interceptor set by
	// an extension, or nil if none is active. Called during Update() to
	// intercept key events and during View() to wrap input rendering.
	// May be nil if no extensions are loaded.
	GetEditorInterceptor func() *EditorInterceptor

	// GetUIVisibility returns the current UI visibility overrides set by
	// an extension, or nil if none have been set (show everything).
	// Called during View() and PrintStartupInfo() to conditionally hide
	// built-in chrome elements. May be nil if no extensions are loaded.
	GetUIVisibility func() *UIVisibility

	// GetStatusBarEntries returns extension-provided status bar entries,
	// sorted by priority. Called during renderStatusBar() to inject
	// extension entries alongside the built-in model/usage display.
	// May be nil if no extensions are loaded.
	GetStatusBarEntries func() []StatusBarEntryData

	// EmitBeforeFork, if non-nil, is called before branching to a
	// different session tree entry. Returns (cancelled, reason) where
	// cancelled=true means the fork should be aborted. May be nil if
	// no extensions are loaded.
	EmitBeforeFork func(targetID string, isUserMsg bool, userText string) (bool, string)

	// EmitBeforeSessionSwitch, if non-nil, is called before switching
	// to a new session branch (e.g. /new, /clear). Returns (cancelled,
	// reason). May be nil if no extensions are loaded.
	EmitBeforeSessionSwitch func(reason string) (bool, string)

	// GetGlobalShortcuts, if non-nil, returns extension-registered global
	// keyboard shortcuts. Keys are binding strings (e.g., "ctrl+p").
	// Handlers are called in a goroutine to avoid blocking the TUI event
	// loop. May be nil if no extensions are loaded.
	GetGlobalShortcuts func() map[string]func()

	// GetExtensionCommands, if non-nil, returns the current extension
	// commands. Called on WidgetUpdateEvent to refresh the command list
	// after an extension hot-reload. May be nil if no extensions loaded.
	GetExtensionCommands func() []ExtensionCommand

	// SetModel changes the active model at runtime. The model string uses
	// "provider/model" format (e.g. "anthropic/claude-sonnet-4-5-20250929").
	// Returns an error if the model string is invalid or the provider cannot
	// be created. May be nil if model switching is not supported.
	SetModel func(modelString string) error

	// EmitModelChange fires the OnModelChange extension event after a
	// successful model switch. Parameters are (newModel, previousModel, source).
	// May be nil if extensions are not loaded.
	EmitModelChange func(newModel, previousModel, source string)

	// ThinkingLevel is the initial thinking level (e.g. "off", "medium").
	ThinkingLevel string
	// IsReasoningModel is true when the current model supports reasoning.
	IsReasoningModel bool
	// SetThinkingLevel changes the thinking level on the agent/provider.
	SetThinkingLevel func(level string) error
}

AppModelOptions holds configuration passed to NewAppModel.

type CLI

type CLI struct {
	// contains filtered or unexported fields
}

CLI manages the command-line interface for KIT, providing message rendering, user input handling, and display management. It supports both standard and compact display modes, handles streaming responses, tracks token usage, and manages the overall conversation flow between the user and AI assistants.

func NewCLI

func NewCLI(debug bool, compact bool) (*CLI, error)

NewCLI creates and initializes a new CLI instance with the specified display modes. The debug parameter enables debug message rendering, while compact enables a more condensed display format. Returns an initialized CLI ready for interaction or an error if initialization fails.

func SetupCLI

func SetupCLI(opts *CLISetupOptions) (*CLI, error)

SetupCLI creates, configures, and initializes a CLI instance with the provided options. It sets up model display, usage tracking for supported providers, and shows initial loading information. Returns nil in quiet mode or an initialized CLI instance ready for user interaction.

func (*CLI) DisplayAssistantMessage

func (c *CLI) DisplayAssistantMessage(message string) error

DisplayAssistantMessage renders and displays an AI assistant's response message with appropriate formatting. This method delegates to DisplayAssistantMessageWithModel with an empty model name for backward compatibility.

func (*CLI) DisplayAssistantMessageWithModel

func (c *CLI) DisplayAssistantMessageWithModel(message, modelName string) error

DisplayAssistantMessageWithModel renders and displays an AI assistant's response with the specified model name shown in the message header. The message is formatted according to the current display mode and includes timestamp information.

func (*CLI) DisplayCancellation

func (c *CLI) DisplayCancellation()

DisplayCancellation displays a system message indicating that the current AI generation has been cancelled by the user (typically via ESC key).

func (*CLI) DisplayDebugConfig

func (c *CLI) DisplayDebugConfig(config map[string]any)

DisplayDebugConfig renders and displays configuration settings in a formatted debug message. The config parameter should contain key-value pairs representing configuration options that will be displayed for debugging purposes.

func (*CLI) DisplayDebugMessage

func (c *CLI) DisplayDebugMessage(message string)

DisplayDebugMessage renders and displays a debug message if debug mode is enabled. Debug messages are formatted distinctively and only shown when the CLI is initialized with debug=true.

func (*CLI) DisplayError

func (c *CLI) DisplayError(err error)

DisplayError renders and displays an error message with distinctive formatting to ensure visibility. The error is timestamped and styled according to the current display mode's error theme.

func (*CLI) DisplayExtensionBlock

func (c *CLI) DisplayExtensionBlock(text, borderColor, subtitle string)

DisplayExtensionBlock renders a custom styled block with the given border color and optional subtitle. Used by extensions via ctx.PrintBlock.

func (*CLI) DisplayInfo

func (c *CLI) DisplayInfo(message string)

DisplayInfo renders and displays an informational system message. These messages are typically used for status updates, notifications, or other non-error system communications to the user.

func (*CLI) DisplayToolCallMessage

func (c *CLI) DisplayToolCallMessage(toolName, toolArgs string)

DisplayToolCallMessage is a no-op retained for backward compatibility. Tool calls are now rendered as part of the unified tool block in DisplayToolMessage, which combines the invocation header with the execution result.

func (*CLI) DisplayToolMessage

func (c *CLI) DisplayToolMessage(toolName, toolArgs, toolResult string, isError bool)

DisplayToolMessage renders and displays the complete result of a tool execution, including the tool name, arguments, and result. The isError parameter determines whether the result should be displayed as an error or success message.

func (*CLI) DisplayUsageAfterResponse

func (c *CLI) DisplayUsageAfterResponse()

DisplayUsageAfterResponse renders and displays token usage information immediately following an AI response. This provides real-time feedback about the cost and token consumption of each interaction.

func (*CLI) DisplayUserMessage

func (c *CLI) DisplayUserMessage(message string)

DisplayUserMessage renders and displays a user's message with appropriate formatting based on the current display mode (standard or compact). The message is timestamped and styled according to the active theme.

func (*CLI) GetDebugLogger

func (c *CLI) GetDebugLogger() *CLIDebugLogger

GetDebugLogger returns a CLIDebugLogger instance that routes debug output through the CLI's rendering system for consistent message formatting and display.

func (*CLI) GetUsageTracker

func (c *CLI) GetUsageTracker() *UsageTracker

GetUsageTracker returns the usage tracker attached to this CLI, or nil if no tracker has been configured. Callers that need a usage-tracker-agnostic handle can assign the returned *UsageTracker wherever an app.UsageUpdater is expected — *UsageTracker satisfies that interface.

func (*CLI) SetModelName

func (c *CLI) SetModelName(modelName string)

SetModelName updates the current AI model name being used in the conversation. This name is displayed in message headers to indicate which model is responding.

func (*CLI) SetUsageTracker

func (c *CLI) SetUsageTracker(tracker *UsageTracker)

SetUsageTracker attaches a usage tracker to the CLI for monitoring token consumption and costs. The tracker will be automatically updated with the current display width for proper rendering.

func (*CLI) ShowSpinner

func (c *CLI) ShowSpinner(action func() error) error

ShowSpinner displays an animated spinner while executing the provided action function. The spinner automatically stops when the action completes. Returns any error returned by the action function.

func (*CLI) UpdateUsageFromResponse

func (c *CLI) UpdateUsageFromResponse(response *fantasy.Response, inputText string)

UpdateUsageFromResponse records token usage using metadata from the fantasy response when available. Falls back to text-based estimation if the metadata is missing or appears unreliable. This provides more accurate usage tracking when providers supply token count information.

type CLIDebugLogger

type CLIDebugLogger struct {
	// contains filtered or unexported fields
}

CLIDebugLogger implements the tools.DebugLogger interface using CLI rendering. It provides debug logging functionality that integrates with the CLI's display system, ensuring debug messages are properly formatted and displayed alongside other conversation content.

func NewCLIDebugLogger

func NewCLIDebugLogger(cli *CLI) *CLIDebugLogger

NewCLIDebugLogger creates and returns a new CLIDebugLogger instance that routes debug output through the provided CLI instance. The logger will respect the CLI's debug mode setting and display format preferences.

func (*CLIDebugLogger) IsDebugEnabled

func (l *CLIDebugLogger) IsDebugEnabled() bool

IsDebugEnabled checks whether debug logging is currently active. Returns true if the CLI instance exists and has debug mode enabled, allowing callers to conditionally perform expensive debug operations only when necessary.

func (*CLIDebugLogger) LogDebug

func (l *CLIDebugLogger) LogDebug(message string)

LogDebug processes and displays a debug message through the CLI's rendering system. Messages are formatted with appropriate emojis and tags based on their content type (DEBUG, POOL, etc.) and only displayed when debug mode is enabled. The method handles multi-line debug output and connection pool status messages with context-aware formatting.

type CLIEventHandler

type CLIEventHandler struct {
	// contains filtered or unexported fields
}

CLIEventHandler routes app-layer events to CLI display methods for non-interactive modes (--prompt and script). It supports two display strategies depending on whether streaming is active:

Streaming mode (StreamChunkEvents arrive):

  • Chunks are printed directly to stdout as they arrive, giving the user real-time feedback identical to the interactive TUI.
  • At flush boundaries (tool calls, step completion) a trailing newline is printed and the streamed flag prevents double-rendering.

Non-streaming mode (no StreamChunkEvents):

  • The complete response arrives via ResponseCompleteEvent or StepCompleteEvent and is rendered through the formatted CLI display.

func NewCLIEventHandler

func NewCLIEventHandler(cli *CLI, modelName string) *CLIEventHandler

NewCLIEventHandler creates a handler that routes app events to the given CLI. modelName is shown in assistant message headers.

func (*CLIEventHandler) Cleanup

func (h *CLIEventHandler) Cleanup()

Cleanup ensures any active spinner is stopped. Must be called after the agent step finishes (whether successfully or not).

func (*CLIEventHandler) Handle

func (h *CLIEventHandler) Handle(msg tea.Msg)

Handle processes a single app event and renders it via the CLI. This is the callback passed to app.RunOnceWithDisplay.

type CLISetupOptions

type CLISetupOptions struct {
	Agent          AgentInterface
	ModelString    string
	Debug          bool
	Compact        bool
	Quiet          bool
	ShowDebug      bool   // Whether to show debug config
	ProviderAPIKey string // For OAuth detection
}

CLISetupOptions encapsulates all configuration parameters needed to initialize and set up a CLI instance, including display preferences, model information, and debugging settings.

type CompactRenderer

type CompactRenderer struct {
	// contains filtered or unexported fields
}

CompactRenderer handles rendering messages in a space-efficient compact format, optimized for terminals with limited vertical space. It displays messages with minimal decorations while maintaining readability and essential information.

func NewCompactRenderer

func NewCompactRenderer(width int, debug bool) *CompactRenderer

NewCompactRenderer creates and initializes a new CompactRenderer with the specified terminal width and debug mode setting. The width parameter determines line wrapping, while debug enables additional diagnostic output in rendered messages.

func (*CompactRenderer) RenderAssistantMessage

func (r *CompactRenderer) RenderAssistantMessage(content string, timestamp time.Time, modelName string) UIMessage

RenderAssistantMessage renders an AI assistant's response in compact format with a distinctive symbol (<) and the model name as label. Empty content is displayed as "(no output)". Returns a UIMessage with formatted content and metadata.

func (*CompactRenderer) RenderDebugConfigMessage

func (r *CompactRenderer) RenderDebugConfigMessage(config map[string]any, timestamp time.Time) UIMessage

RenderDebugConfigMessage renders configuration settings in compact format for debugging purposes. Config entries are displayed as key=value pairs separated by commas, truncated if necessary to fit on a single line.

func (*CompactRenderer) RenderDebugMessage

func (r *CompactRenderer) RenderDebugMessage(message string, timestamp time.Time) UIMessage

RenderDebugMessage renders diagnostic information in compact format when debug mode is enabled. Messages are truncated if they exceed the available width to maintain single-line display.

func (*CompactRenderer) RenderErrorMessage

func (r *CompactRenderer) RenderErrorMessage(errorMsg string, timestamp time.Time) UIMessage

RenderErrorMessage renders an error notification in compact format with a distinctive error symbol (!) and styling to ensure visibility. The error content is displayed in a single line with appropriate color highlighting.

func (*CompactRenderer) RenderSystemMessage

func (r *CompactRenderer) RenderSystemMessage(content string, timestamp time.Time) UIMessage

RenderSystemMessage renders a system notification or informational message in compact format with a distinctive symbol (*) and "System" label. Content is formatted to fit on a single line for minimal space usage.

func (*CompactRenderer) RenderToolCallMessage

func (r *CompactRenderer) RenderToolCallMessage(toolName, toolArgs string, timestamp time.Time) UIMessage

RenderToolCallMessage renders a tool call notification in compact format, showing the tool being executed with its arguments in a single line. The tool name is highlighted and arguments are displayed in a muted color for visual distinction.

func (*CompactRenderer) RenderToolMessage

func (r *CompactRenderer) RenderToolMessage(toolName, toolArgs, toolResult string, isError bool) UIMessage

RenderToolMessage renders a unified tool block in compact format, combining the tool invocation header (icon + display name + params) with the execution result body. Status is indicated by icon: checkmark for success, cross for error.

func (*CompactRenderer) RenderUserMessage

func (r *CompactRenderer) RenderUserMessage(content string, timestamp time.Time) UIMessage

RenderUserMessage renders a user's input message in compact format with a distinctive symbol (>) and label. The content is formatted to preserve structure while minimizing vertical space usage. Returns a UIMessage with formatted content and metadata.

func (*CompactRenderer) SetWidth

func (r *CompactRenderer) SetWidth(width int)

SetWidth updates the terminal width for the renderer, affecting how content is wrapped and formatted in subsequent render operations.

type EditorInterceptor added in v0.2.0

type EditorInterceptor struct {
	// HandleKey intercepts key presses before the built-in editor.
	HandleKey func(key string, currentText string) EditorKeyAction
	// Render wraps the built-in editor's rendered output.
	Render func(width int, defaultContent string) string
}

EditorInterceptor is the UI-layer representation of an extension editor interceptor. It decouples the UI package from the extensions package. The CLI layer converts the extension EditorConfig to this type.

type EditorKeyAction added in v0.2.0

type EditorKeyAction struct {
	// Type determines the action taken.
	Type EditorKeyActionType
	// RemappedKey is the target key name for EditorKeyRemap.
	RemappedKey string
	// SubmitText is the text to submit for EditorKeySubmit.
	SubmitText string
}

EditorKeyAction is the UI-layer equivalent of extensions.EditorKeyAction.

type EditorKeyActionType added in v0.2.0

type EditorKeyActionType string

EditorKeyActionType defines the outcome of an editor key interception. Mirrors extensions.EditorKeyActionType for package decoupling.

const (
	// EditorKeyPassthrough lets the built-in editor handle the key normally.
	EditorKeyPassthrough EditorKeyActionType = "passthrough"
	// EditorKeyConsumed means the extension handled the key.
	EditorKeyConsumed EditorKeyActionType = "consumed"
	// EditorKeyRemap transforms the key into a different key.
	EditorKeyRemap EditorKeyActionType = "remap"
	// EditorKeySubmit forces immediate text submission.
	EditorKeySubmit EditorKeyActionType = "submit"
)

type ExtensionCommand

type ExtensionCommand struct {
	Name        string
	Description string
	Execute     func(args string) (string, error)
	Complete    func(prefix string) []string // optional argument tab-completion
}

ExtensionCommand is a slash command registered by an extension. Unlike built-in SlashCommands whose execution is hardcoded in handleSlashCommand, extension commands carry their own Execute callback.

func FindExtensionCommand

func FindExtensionCommand(name string, cmds []ExtensionCommand) *ExtensionCommand

FindExtensionCommand looks up an extension command by name from the given slice. Returns a pointer to the matching command, or nil if not found.

type FileSuggestion added in v0.4.0

type FileSuggestion struct {
	// RelPath is the path relative to the search base (e.g. "cmd/kit/main.go").
	RelPath string
	// IsDir is true when the entry is a directory.
	IsDir bool
	// Score is the fuzzy match score (higher is better).
	Score int
}

FileSuggestion represents a single file or directory suggestion for the @ autocomplete popup.

func GetFileSuggestions added in v0.4.0

func GetFileSuggestions(prefix string, cwd string) []FileSuggestion

GetFileSuggestions returns file/directory suggestions matching the given prefix. It tries `git ls-files` first (fast, respects .gitignore), then falls back to a simple directory walk.

If prefix contains a path separator the search is scoped to that subdirectory. For example, prefix "cmd/k" searches inside "cmd/" for entries matching "k".

type FlatNode

type FlatNode struct {
	Entry    any    // the underlying entry
	ID       string // entry ID
	ParentID string
	Depth    int    // indentation level
	IsLast   bool   // last child at this depth
	Prefix   string // computed prefix string (├─, └─, etc.)
	Label    string // user-defined label, if any
}

FlatNode is a tree entry flattened for list rendering with indentation info.

type FuzzyMatch

type FuzzyMatch struct {
	Command *SlashCommand
	Score   int
}

FuzzyMatch represents the result of a fuzzy string matching operation, containing the matched command and its relevance score. Higher scores indicate better matches.

func FuzzyMatchCommands

func FuzzyMatchCommands(query string, commands []SlashCommand) []FuzzyMatch

FuzzyMatchCommands performs fuzzy string matching on the provided slash commands based on the query string. Returns a slice of matches sorted by relevance score in descending order. An empty query returns all commands with zero scores.

type ImageAttachment added in v0.7.0

type ImageAttachment struct {
	// Data is the raw image bytes (PNG, JPEG, etc.).
	Data []byte
	// MediaType is the MIME type (e.g. "image/png", "image/jpeg").
	MediaType string
}

ImageAttachment holds a clipboard image that will be sent alongside the user's text prompt to the LLM. The data is raw image bytes; MediaType is a MIME type like "image/png".

type InputComponent

type InputComponent struct {
	// contains filtered or unexported fields
}

InputComponent is the interactive text input field for the parent AppModel. It wraps the slash command autocomplete popup and delegates slash command execution to the AppController. On submit it returns a submitMsg tea.Cmd instead of tea.Quit — lifecycle is entirely managed by the parent.

Slash commands handled locally (not forwarded to app layer):

  • /quit, /q, /exit → tea.Quit
  • /clear, /cls, /c → appCtrl.ClearMessages() then clear the textarea

/clear-queue is forwarded to the parent via submitMsg so the parent can update queueCount directly (calling ClearQueue from within Update would require prog.Send which deadlocks).

All other input is returned via submitMsg for the parent to forward to app.Run().

func NewInputComponent

func NewInputComponent(width int, title string, appCtrl AppController) *InputComponent

NewInputComponent creates a new InputComponent with the given width, title, and optional AppController. If appCtrl is nil the component still works but /clear and /clear-queue are no-ops.

func (*InputComponent) ClearPendingImages added in v0.7.0

func (s *InputComponent) ClearPendingImages() []ImageAttachment

ClearPendingImages removes all pending image attachments and returns them. Used by the parent model when consuming images for submission.

func (*InputComponent) Init

func (s *InputComponent) Init() tea.Cmd

Init implements tea.Model. Starts the cursor blink animation.

func (*InputComponent) PendingImageCount added in v0.7.0

func (s *InputComponent) PendingImageCount() int

PendingImageCount returns the number of images currently attached.

func (*InputComponent) SetCwd added in v0.4.0

func (s *InputComponent) SetCwd(cwd string)

SetCwd sets the working directory used for @file autocomplete suggestions and path resolution. Should be called by the parent after construction.

func (*InputComponent) Update

func (s *InputComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model. Handles keyboard input, popup navigation, and slash command execution. Returns submitMsg via a tea.Cmd when the user submits text — it does NOT return tea.Quit (parent owns lifecycle).

func (*InputComponent) View

func (s *InputComponent) View() tea.View

View implements tea.Model. Renders the title, textarea, autocomplete popup (if visible), and help text.

type MessageRenderer

type MessageRenderer struct {
	// contains filtered or unexported fields
}

MessageRenderer handles the formatting and rendering of different message types with consistent styling, markdown support, and appropriate visual hierarchies for the standard (non-compact) display mode.

func NewMessageRenderer

func NewMessageRenderer(width int, debug bool) *MessageRenderer

NewMessageRenderer creates and initializes a new MessageRenderer with the specified terminal width and debug mode setting. The width parameter determines line wrapping and layout calculations.

func (*MessageRenderer) RenderAssistantMessage

func (r *MessageRenderer) RenderAssistantMessage(content string, timestamp time.Time, modelName string) UIMessage

RenderAssistantMessage renders an AI assistant's response with left-aligned formatting, including the model name, timestamp, and markdown-rendered content. Empty responses are displayed with a special "Finished without output" message. The message features a colored left border for visual distinction.

func (*MessageRenderer) RenderDebugConfigMessage

func (r *MessageRenderer) RenderDebugConfigMessage(config map[string]any, timestamp time.Time) UIMessage

RenderDebugConfigMessage renders configuration settings in a formatted debug display with key-value pairs shown in a structured layout. Used to display runtime configuration for debugging purposes with a distinctive icon and border styling.

func (*MessageRenderer) RenderDebugMessage

func (r *MessageRenderer) RenderDebugMessage(message string, timestamp time.Time) UIMessage

RenderDebugMessage renders diagnostic and debugging information with special formatting including a debug icon, colored border, and structured layout. Debug messages are only displayed when debug mode is enabled and help developers troubleshoot issues.

func (*MessageRenderer) RenderErrorMessage

func (r *MessageRenderer) RenderErrorMessage(errorMsg string, timestamp time.Time) UIMessage

RenderErrorMessage renders error notifications with distinctive red coloring and bold text to ensure visibility. Error messages include timestamp information and are displayed with an error-colored border for immediate recognition.

func (*MessageRenderer) RenderSystemMessage

func (r *MessageRenderer) RenderSystemMessage(content string, timestamp time.Time) UIMessage

RenderSystemMessage renders KIT system messages such as help text, command outputs, and informational notifications. These messages are displayed with a distinctive system color border and "KIT System" label to differentiate them from user and AI content.

func (*MessageRenderer) RenderToolCallMessage

func (r *MessageRenderer) RenderToolCallMessage(toolName, toolArgs string, timestamp time.Time) UIMessage

RenderToolCallMessage renders a notification that a tool is being executed, showing the tool name, formatted arguments (if any), and execution timestamp. The message uses tool-specific coloring to distinguish it from regular conversation messages.

func (*MessageRenderer) RenderToolMessage

func (r *MessageRenderer) RenderToolMessage(toolName, toolArgs, toolResult string, isError bool) UIMessage

RenderToolMessage renders a unified tool block combining the tool invocation header (icon + display name + params) with the execution result body. The border color indicates status: green for success, red for error. This replaces the previous two-block approach (separate call + result blocks).

func (*MessageRenderer) RenderUserMessage

func (r *MessageRenderer) RenderUserMessage(content string, timestamp time.Time) UIMessage

RenderUserMessage renders a user's input message with distinctive right-aligned formatting, including the system username, timestamp, and markdown-rendered content. The message is displayed with a colored right border for visual distinction.

func (*MessageRenderer) SetWidth

func (r *MessageRenderer) SetWidth(width int)

SetWidth updates the terminal width for the renderer, affecting how content is wrapped and formatted in subsequent render operations.

type MessageType

type MessageType int

MessageType represents different categories of messages displayed in the UI, each with distinct visual styling and formatting rules.

const (
	UserMessage MessageType = iota
	AssistantMessage
	ToolMessage
	ToolCallMessage // New type for showing tool calls in progress
	SystemMessage   // New type for KIT system messages (help, tools, etc.)
	ErrorMessage    // New type for error messages
)

type ModelEntry added in v0.6.0

type ModelEntry struct {
	Provider     string
	ModelID      string
	Name         string // human-friendly name (e.g. "Claude Haiku 4.5")
	ContextLimit int
	Reasoning    bool
}

ModelEntry holds display metadata for a single model in the selector.

type ModelSelectedMsg added in v0.6.0

type ModelSelectedMsg struct {
	ModelString string // "provider/model-id"
}

ModelSelectedMsg is sent when the user selects a model from the selector.

type ModelSelectorCancelledMsg added in v0.6.0

type ModelSelectorCancelledMsg struct{}

ModelSelectorCancelledMsg is sent when the user cancels the selector.

type ModelSelectorComponent added in v0.6.0

type ModelSelectorComponent struct {
	// contains filtered or unexported fields
}

ModelSelectorComponent is a full-screen Bubble Tea component that displays a filterable list of available models. It follows the same pattern as TreeSelectorComponent: inline text search, scrolling list, and custom messages for result delivery.

func NewModelSelector added in v0.6.0

func NewModelSelector(currentModel string, width, height int) *ModelSelectorComponent

NewModelSelector creates a model selector populated from the global registry, filtered to only providers with configured API keys.

func (*ModelSelectorComponent) Init added in v0.6.0

func (ms *ModelSelectorComponent) Init() tea.Cmd

Init implements tea.Model.

func (*ModelSelectorComponent) IsActive added in v0.6.0

func (ms *ModelSelectorComponent) IsActive() bool

IsActive returns whether the selector is still accepting input.

func (*ModelSelectorComponent) Update added in v0.6.0

func (ms *ModelSelectorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model.

func (*ModelSelectorComponent) View added in v0.6.0

func (ms *ModelSelectorComponent) View() tea.View

View implements tea.Model.

type Renderer added in v0.2.0

type Renderer interface {
	RenderUserMessage(content string, timestamp time.Time) UIMessage
	RenderAssistantMessage(content string, timestamp time.Time, modelName string) UIMessage
	RenderToolMessage(toolName, toolArgs, toolResult string, isError bool) UIMessage
	RenderSystemMessage(content string, timestamp time.Time) UIMessage
	RenderErrorMessage(errorMsg string, timestamp time.Time) UIMessage
	RenderDebugMessage(message string, timestamp time.Time) UIMessage
	RenderDebugConfigMessage(config map[string]any, timestamp time.Time) UIMessage
	SetWidth(width int)
}

Renderer is the interface satisfied by both MessageRenderer and CompactRenderer. It allows model.go and cli.go to call rendering methods without branching on compact mode.

type SessionStats

type SessionStats struct {
	TotalInputTokens      int
	TotalOutputTokens     int
	TotalCacheReadTokens  int
	TotalCacheWriteTokens int
	TotalCost             float64
	RequestCount          int
}

SessionStats aggregates token usage and cost information across all requests in a session, providing totals and request counts for usage analysis and cost tracking.

type SkillItem

type SkillItem struct {
	Name   string // Skill name (e.g. "btca-cli").
	Path   string // Absolute path to the skill file.
	Source string // "project" or "user" (global).
}

SkillItem holds display metadata about a loaded skill for the startup [Skills] section. Built by the CLI layer from the SDK's []*kit.Skill.

type SlashCommand

type SlashCommand struct {
	Name        string
	Description string
	Aliases     []string
	Category    string                       // e.g., "Navigation", "System", "Info"
	Complete    func(prefix string) []string // optional argument tab-completion
}

SlashCommand represents a user-invokable slash command with its metadata. Commands can have multiple aliases and are organized by category for better discoverability and help display.

func GetCommandByName

func GetCommandByName(name string) *SlashCommand

GetCommandByName looks up a slash command by its primary name or any of its aliases. Returns a pointer to the matching SlashCommand, or nil if no command matches the provided name.

type SlashCommandInput

type SlashCommandInput struct {
	// contains filtered or unexported fields
}

SlashCommandInput provides an interactive text input field with intelligent slash command autocomplete functionality. It displays a popup menu of matching commands as the user types, supporting fuzzy matching and keyboard navigation.

func NewSlashCommandInput

func NewSlashCommandInput(width int, title string) *SlashCommandInput

NewSlashCommandInput creates and initializes a new slash command input field with the specified width and title. The input supports multi-line text entry, command autocomplete, and is styled to match the application's theme.

func (*SlashCommandInput) Cancelled

func (s *SlashCommandInput) Cancelled() bool

Cancelled returns true if the user cancelled the input operation (e.g., by pressing ESC or Ctrl+C) without submitting any text.

func (*SlashCommandInput) Init

func (s *SlashCommandInput) Init() tea.Cmd

Init implements the tea.Model interface, returning the initial command to start the cursor blinking animation for the text input field.

func (*SlashCommandInput) RenderedLines

func (s *SlashCommandInput) RenderedLines() int

RenderedLines returns the total number of terminal lines used by the last rendered view, including the title, input area, popup, and help text. This is used for proper screen clearing when the input is dismissed.

func (*SlashCommandInput) Update

func (s *SlashCommandInput) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements the tea.Model interface, handling keyboard input for text entry, command selection, and navigation. Manages the autocomplete popup display and processes submission or cancellation actions.

func (*SlashCommandInput) Value

func (s *SlashCommandInput) Value() string

Value returns the final text value entered by the user after submission. This will be empty if the input was cancelled.

func (*SlashCommandInput) View

func (s *SlashCommandInput) View() tea.View

View implements the tea.Model interface, rendering the complete input field including the title, text area, autocomplete popup (when active), and help text. The view adapts based on whether single or multi-line input is detected.

type Spinner

type Spinner struct {
	// contains filtered or unexported fields
}

Spinner provides an animated loading indicator that displays while long-running operations are in progress. It writes directly to stderr using a goroutine-based animation loop, avoiding Bubble Tea's terminal capability queries that can leak escape sequences (mode 2026 DECRPM).

The KITT-style frames are generated by knightRiderFrames() in stream.go (same package) and use the active theme colors.

func NewSpinner

func NewSpinner() *Spinner

NewSpinner creates a new animated KITT-style spinner using theme colors.

func (*Spinner) Start

func (s *Spinner) Start()

Start begins the spinner animation in a separate goroutine. The spinner will continue animating until Stop is called.

func (*Spinner) Stop

func (s *Spinner) Stop()

Stop halts the spinner animation and blocks until the animation goroutine has exited and the line is cleared. Safe to call multiple times.

type StatusBarEntryData added in v0.3.0

type StatusBarEntryData struct {
	Key      string // unique identifier (e.g. "myext:git-branch")
	Text     string // rendered content shown in the status bar
	Priority int    // lower = further left; built-in entries use 100-110
}

StatusBarEntryData represents a keyed extension entry in the TUI status bar. Multiple entries from different extensions coexist, ordered by Priority (lower values render further left).

type StreamComponent

type StreamComponent struct {
	// contains filtered or unexported fields
}

StreamComponent is the Bubble Tea child model responsible for the stream region: it renders a KITT-style spinner when the agent is working, and displays live text as StreamChunkEvents arrive. The spinner remains visible alongside streaming text until the step completes and Reset() is called.

Tool calls, tool results, user messages, and other non-streaming content are printed immediately by the parent AppModel via tea.Println(). The StreamComponent only handles the live streaming text and spinner display.

Lifecycle is managed entirely by the parent AppModel:

  • Parent calls Reset() between agent steps to clear state.
  • Parent emits completed responses above the BT region via tea.Println() then calls Reset(); StreamComponent never calls tea.Quit.

Events handled:

  • app.SpinnerEvent{Show:true} → start spinner tick loop
  • app.StreamChunkEvent → append text
  • app.ToolExecutionEvent → show execution label on spinner

func NewStreamComponent

func NewStreamComponent(compactMode bool, width int, modelName string) *StreamComponent

NewStreamComponent creates a new StreamComponent ready to be embedded in AppModel.

func (*StreamComponent) GetRenderedContent

func (s *StreamComponent) GetRenderedContent() string

GetRenderedContent returns the rendered assistant message from the accumulated streaming text. Returns empty string if no text has been accumulated. Used by the parent AppModel to flush content via tea.Println() before resetting.

func (*StreamComponent) HasReasoning added in v0.7.0

func (s *StreamComponent) HasReasoning() bool

HasReasoning returns true if any reasoning content has been accumulated.

func (*StreamComponent) Init

func (s *StreamComponent) Init() tea.Cmd

Init implements tea.Model. No startup command needed; SpinnerEvent drives the ticker.

func (*StreamComponent) Reset

func (s *StreamComponent) Reset()

Reset clears all accumulated state so the component is ready for the next agent step. Called by AppModel after a step completes or errors.

func (*StreamComponent) SetHeight

func (s *StreamComponent) SetHeight(h int)

SetHeight constrains the stream region render height. When height > 0, the render output is clamped to that many lines (trailing lines are discarded). A value of 0 means unconstrained.

func (*StreamComponent) SetThinkingVisible added in v0.7.0

func (s *StreamComponent) SetThinkingVisible(visible bool)

SetThinkingVisible sets whether reasoning blocks are shown or collapsed.

func (*StreamComponent) SpinnerView

func (s *StreamComponent) SpinnerView() string

SpinnerView returns the rendered spinner line for the parent to embed in the status bar. Returns "" when the spinner is not active.

func (*StreamComponent) Update

func (s *StreamComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model. Routes app-layer events and internal tick msgs.

func (*StreamComponent) View

func (s *StreamComponent) View() tea.View

View implements tea.Model. Renders the current stream region content.

type Theme

type Theme struct {
	Primary     color.Color
	Secondary   color.Color
	Success     color.Color
	Warning     color.Color
	Error       color.Color
	Info        color.Color
	Text        color.Color
	Muted       color.Color
	VeryMuted   color.Color
	Background  color.Color
	Border      color.Color
	MutedBorder color.Color
	System      color.Color
	Tool        color.Color
	Accent      color.Color
	Highlight   color.Color

	// Diff block backgrounds
	DiffInsertBg  color.Color // Green-tinted bg for added lines
	DiffDeleteBg  color.Color // Red-tinted bg for removed lines
	DiffEqualBg   color.Color // Neutral bg for context lines
	DiffMissingBg color.Color // Empty-cell bg when sides are uneven

	// Code/output block backgrounds
	CodeBg   color.Color // Background for code blocks (Read tool)
	GutterBg color.Color // Line-number gutter background
	WriteBg  color.Color // Green-tinted bg for Write tool content
}

Theme defines a comprehensive color scheme for the application's UI, supporting both light and dark terminal modes through adaptive colors. It includes semantic colors for different message types and UI elements, based on the Catppuccin color palette.

func DefaultTheme

func DefaultTheme() Theme

DefaultTheme creates and returns the default KIT theme based on the Catppuccin Mocha (dark) and Latte (light) color palettes. This theme provides a cohesive, pleasant visual experience with carefully selected colors for different UI elements.

func GetTheme

func GetTheme() Theme

GetTheme returns the currently active UI theme. The theme controls all color and styling decisions throughout the application's interface.

type ToolApprovalInput

type ToolApprovalInput struct {
	// contains filtered or unexported fields
}

func NewToolApprovalInput

func NewToolApprovalInput(toolName, toolArgs string, width int) *ToolApprovalInput

func (*ToolApprovalInput) Init

func (t *ToolApprovalInput) Init() tea.Cmd

func (*ToolApprovalInput) Update

func (t *ToolApprovalInput) Update(msg tea.Msg) (tea.Model, tea.Cmd)

func (*ToolApprovalInput) View

func (t *ToolApprovalInput) View() tea.View

type ToolRendererData added in v0.2.0

type ToolRendererData struct {
	// DisplayName, if non-empty, replaces the auto-capitalized tool name
	// in the header line.
	DisplayName string

	// BorderColor, if non-empty, overrides the default success/error border
	// color. Hex string (e.g. "#89b4fa").
	BorderColor string

	// Background, if non-empty, sets a background color for the tool block.
	// Hex string (e.g. "#1e1e2e").
	Background string

	// BodyMarkdown, when true, renders the RenderBody output as markdown
	// via glamour. Ignored when RenderBody is nil or returns empty.
	BodyMarkdown bool

	// RenderHeader, if non-nil, replaces the default parameter formatting
	// in the tool header line. Receives the JSON-encoded arguments and max
	// width. Return a short summary string, or empty to fall back to default.
	RenderHeader func(toolArgs string, width int) string

	// RenderBody, if non-nil, replaces the default tool result body. Receives
	// the result text, error flag, and available width. Return the full styled
	// body content, or empty to fall back to builtin/default renderer.
	RenderBody func(toolResult string, isError bool, width int) string
}

ToolRendererData holds extension-provided rendering functions for a specific tool. The UI layer uses this to override the default tool header/body rendering without depending on the extensions package directly.

type TreeCancelledMsg

type TreeCancelledMsg struct{}

TreeCancelledMsg is sent when the user cancels the tree selector (ESC).

type TreeFilterMode

type TreeFilterMode int

TreeFilterMode controls which entries are visible in the tree selector.

const (
	TreeFilterDefault   TreeFilterMode = iota // hide settings entries
	TreeFilterNoTools                         // hide tool results
	TreeFilterUserOnly                        // show only user messages
	TreeFilterLabelOnly                       // show only labeled entries
	TreeFilterAll                             // show everything
)

func (TreeFilterMode) String

func (m TreeFilterMode) String() string

type TreeNodeSelectedMsg

type TreeNodeSelectedMsg struct {
	// ID is the entry ID of the selected node.
	ID string
	// Entry is the underlying entry object.
	Entry any
	// IsUser is true if the selected entry is a user message.
	IsUser bool
	// UserText is the user message text (only set when IsUser is true).
	UserText string
}

TreeNodeSelectedMsg is sent when the user selects a node in the tree selector.

type TreeSelectorComponent

type TreeSelectorComponent struct {
	// contains filtered or unexported fields
}

TreeSelectorComponent is a Bubble Tea component that renders the session tree as an ASCII art list with navigation and selection.

func NewTreeSelector

func NewTreeSelector(tm *session.TreeManager, width, height int) *TreeSelectorComponent

NewTreeSelector creates a tree selector from a TreeManager.

func (*TreeSelectorComponent) Init

func (ts *TreeSelectorComponent) Init() tea.Cmd

Init implements tea.Model.

func (*TreeSelectorComponent) IsActive

func (ts *TreeSelectorComponent) IsActive() bool

IsActive returns whether the tree selector is still accepting input.

func (*TreeSelectorComponent) Update

func (ts *TreeSelectorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model.

func (*TreeSelectorComponent) View

func (ts *TreeSelectorComponent) View() tea.View

View implements tea.Model.

type UIMessage

type UIMessage struct {
	ID        string
	Type      MessageType
	Position  int
	Height    int
	Content   string
	Timestamp time.Time
	Streaming bool
}

UIMessage encapsulates a fully rendered message ready for display in the UI, including its formatted content, display metrics, and metadata. Messages can be static or streaming (progressively updated).

type UIVisibility added in v0.3.0

type UIVisibility struct {
	HideStartupMessage bool // Hide the "Model loaded..." startup block
	HideStatusBar      bool // Hide the "provider · model  Tokens: ..." line
	HideSeparator      bool // Hide the "────────" divider between stream and input
	HideInputHint      bool // Hide the "enter submit · ctrl+j..." hint below input
}

UIVisibility controls which built-in TUI chrome elements are visible. The zero value shows everything (backward compatible).

type UsageStats

type UsageStats struct {
	InputTokens      int
	OutputTokens     int
	CacheReadTokens  int
	CacheWriteTokens int
	InputCost        float64
	OutputCost       float64
	CacheReadCost    float64
	CacheWriteCost   float64
	TotalCost        float64
}

UsageStats encapsulates detailed token usage and cost breakdown for a single LLM request/response cycle, including input, output, and cache token counts along with their associated costs.

type UsageTracker

type UsageTracker struct {
	// contains filtered or unexported fields
}

UsageTracker monitors and accumulates token usage statistics and associated costs for LLM interactions throughout a session. It provides real-time usage information and supports both estimated and actual token counts. OAuth users see $0 costs.

func CreateUsageTracker

func CreateUsageTracker(modelString, providerAPIKey string) *UsageTracker

CreateUsageTracker creates a UsageTracker for the given model string and provider API key. It returns nil when usage tracking is unavailable (e.g. ollama or unrecognised models). This is used by the interactive TUI path which doesn't go through SetupCLI.

func NewUsageTracker

func NewUsageTracker(modelInfo *models.ModelInfo, provider string, width int, isOAuth bool) *UsageTracker

NewUsageTracker creates and initializes a new UsageTracker for the specified model. The tracker uses model-specific pricing information to calculate costs, unless OAuth credentials are being used (in which case costs are shown as $0). Width determines the display formatting.

func (*UsageTracker) EstimateAndUpdateUsage

func (ut *UsageTracker) EstimateAndUpdateUsage(inputText, outputText string)

EstimateAndUpdateUsage estimates token counts from raw text strings and updates the usage statistics. This method is used when actual token counts are not available from the API response. The estimated values also serve as the context utilization approximation since they represent a single API call.

func (*UsageTracker) GetLastRequestStats

func (ut *UsageTracker) GetLastRequestStats() *UsageStats

GetLastRequestStats returns a copy of the usage statistics from the most recent request, or nil if no requests have been made. The returned copy is safe to use without additional synchronization.

func (*UsageTracker) GetSessionStats

func (ut *UsageTracker) GetSessionStats() SessionStats

GetSessionStats returns a copy of the cumulative session statistics including total token counts, costs, and request count. The returned copy is safe to use without additional synchronization.

func (*UsageTracker) RenderUsageInfo

func (ut *UsageTracker) RenderUsageInfo() string

RenderUsageInfo generates a formatted string displaying current usage statistics including token counts, context utilization percentage, and costs. The display adapts colors based on usage levels and formats large numbers with K/M suffixes for readability.

func (*UsageTracker) Reset

func (ut *UsageTracker) Reset()

Reset clears all accumulated usage statistics, resetting both session totals and last request information to their initial empty state. This is typically used when starting a new conversation or clearing usage history.

func (*UsageTracker) SetContextTokens

func (ut *UsageTracker) SetContextTokens(tokens int)

SetContextTokens records the approximate current context window utilization. This should be set from the final API call's input + output tokens (i.e. FinalResponse.Usage) rather than the aggregate TotalUsage, because TotalUsage sums across all tool-calling steps and overstates the actual window fill level.

func (*UsageTracker) SetWidth

func (ut *UsageTracker) SetWidth(width int)

SetWidth updates the terminal width used for formatting usage information display. This should be called when the terminal is resized to ensure proper text wrapping and alignment.

func (*UsageTracker) UpdateUsage

func (ut *UsageTracker) UpdateUsage(inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens int)

UpdateUsage records new token usage data and calculates associated costs based on the model's pricing. Updates both the last request statistics and cumulative session totals. For OAuth users, costs are recorded as $0 while still tracking token counts.

type WidgetData added in v0.2.0

type WidgetData struct {
	// Text is the content to display.
	Text string
	// Markdown, when true, renders Text as styled markdown.
	Markdown bool
	// BorderColor is a hex color (e.g. "#a6e3a1") for the left border.
	// Empty uses the theme's default accent color.
	BorderColor string
	// NoBorder disables the left border entirely.
	NoBorder bool
}

WidgetData is the UI-layer representation of an extension widget. It decouples the UI package from the extensions package. The CLI layer converts extension WidgetConfig values to WidgetData for rendering.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL