ui

package
v0.70.4 Latest Latest
Warning

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

Go to latest
Published: May 20, 2026 License: MIT Imports: 46 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	LoadThemePreference         = prefs.LoadThemePreference
	SaveThemePreference         = prefs.SaveThemePreference
	LoadModelPreference         = prefs.LoadModelPreference
	SaveModelPreference         = prefs.SaveModelPreference
	LoadThinkingLevelPreference = prefs.LoadThinkingLevelPreference
	SaveThinkingLevelPreference = prefs.SaveThinkingLevelPreference
)

Re-export from prefs package

View Source
var (
	GetTheme                = style.GetTheme
	SetTheme                = style.SetTheme
	DefaultTheme            = style.DefaultTheme
	ApplyTheme              = style.ApplyTheme
	ApplyThemeWithoutSave   = style.ApplyThemeWithoutSave
	ListThemes              = style.ListThemes
	RegisterThemeFromConfig = style.RegisterThemeFromConfig
	KitBanner               = style.KitBanner
	AdaptiveColor           = style.AdaptiveColor
	IsDarkBackground        = style.IsDarkBackground
)
View Source
var ProcessFileAttachments = fileutil.ProcessFileAttachments

Re-export functions and types from fileutil package

Functions

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 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 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 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 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 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.

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()
	// ReloadMessagesFromTree clears the in-memory message store and reloads
	// it from the tree session's current branch. Unlike ClearMessages, this
	// does NOT reset the tree session's leaf pointer. Used after Branch() to
	// sync the store with the new branch position.
	ReloadMessagesFromTree()
	// 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
	// SwitchTreeSession replaces the active tree session with a new one,
	// closing the old session. Used by /new to create a completely fresh session.
	SwitchTreeSession(ts *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 []kit.LLMFilePart) int
	// Steer injects a steering message into the currently running agent
	// turn. If the agent is busy, the message is delivered between steps
	// (after current tool finishes, before next LLM call). If idle, the
	// message starts executing immediately. Returns 0 if started
	// immediately, >0 if injected/pending.
	Steer(prompt string) int
	// SteerWithFiles injects a steering message with optional file
	// attachments (e.g. pasted images) into the currently running agent
	// turn. Behaves like Steer but includes file parts alongside the text.
	SteerWithFiles(prompt string, files []kit.LLMFilePart) 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 (alt screen):

┌─ [custom header] (optional, from extension) ──────┐
├─ scroll region (variable height, ScrollList) ─────┤
│  (completed messages + live streaming text)        │
├─ 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.

All messages (completed and streaming) are rendered via the ScrollList viewport. The alt screen owns the full terminal.

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) AddStartupMessageToScrollList added in v0.33.0

func (m *AppModel) AddStartupMessageToScrollList()

AddStartupMessageToScrollList adds the logo and startup info as the first messages in the ScrollList. This is the only place startup information is rendered — nothing is printed to stdout.

func (*AppModel) Init

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

Init implements tea.Model. Initialises child components.

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 {
	// 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

	// GetToolNames, if non-nil, returns the current tool names. Called on
	// MCPToolsReadyEvent to refresh the tool list after background MCP tool
	// loading completes. May be nil if dynamic tool refresh is not needed.
	GetToolNames func() []string

	// GetMCPToolCount, if non-nil, returns the current MCP tool count.
	// Called on MCPToolsReadyEvent to refresh the startup info bar.
	// May be nil if dynamic tool refresh is not needed.
	GetMCPToolCount func() int

	// 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 []commands.ExtensionCommand

	// PromptTemplates are user-defined prompt templates loaded from ~/.kit/prompts/,
	// .kit/prompts/, or explicit --prompt-template paths. They appear in autocomplete
	// and are expanded when submitted (e.g., /review → full prompt text).
	PromptTemplates []*prompts.PromptTemplate

	// GetPromptTemplates, if non-nil, returns the current prompt templates.
	// Called on ContentReloadEvent to refresh the template list after a file
	// watcher detects changes. May be nil if prompt hot-reload is not needed.
	GetPromptTemplates func() []*prompts.PromptTemplate

	// MCPPrompts are prompts discovered from MCP servers at startup.
	// They appear in autocomplete as /<server>:<prompt> commands.
	MCPPrompts []MCPPromptInfo

	// GetMCPPrompts, if non-nil, returns the current MCP prompts.
	// Called on MCPToolsReadyEvent to refresh after background loading.
	GetMCPPrompts func() []MCPPromptInfo

	// ExpandMCPPrompt, if non-nil, lazily expands an MCP prompt by
	// calling the MCP server's GetPrompt. Called asynchronously when the
	// user invokes an MCP prompt slash command.
	ExpandMCPPrompt func(serverName, promptName string, args map[string]string) (*MCPPromptExpandResult, error)

	// 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

	// GetSkillItems, if non-nil, returns the current skill items.
	// Called on ContentReloadEvent to refresh the skill list after a file
	// watcher detects changes. May be nil if skill hot-reload is not needed.
	GetSkillItems func() []SkillItem

	// ExtensionItems lists loaded extensions for the [Extensions] startup
	// section. Each entry shows the filename of an extension that was
	// discovered and loaded (global, project-local, or explicit).
	ExtensionItems []ExtensionItem

	// GetExtensionItems, if non-nil, returns the current extension items.
	// Called on extension hot-reload to refresh the list. May be nil if no
	// extensions are loaded.
	GetExtensionItems func() []ExtensionItem

	// 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() 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() []commands.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)

	// SwitchSession opens a session by JSONL file path, replacing the
	// active tree session and reloading messages. Called when the user
	// picks a session from /resume. May be nil if session switching is
	// not supported.
	SwitchSession func(path string) error

	// ShowSessionPicker, when true, opens the session picker immediately
	// on startup (used by --resume flag).
	ShowSessionPicker bool

	// StartupExtensionMessages are messages captured during extension
	// initialization. They are displayed in the ScrollList at startup.
	StartupExtensionMessages []string

	// ReloadExtensions hot-reloads all extensions from disk. Called by
	// the /reload-ext command and the automatic file watcher. May be nil
	// if no extensions are loaded.
	ReloadExtensions func() error

	// 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

	// GetMCPResources, if non-nil, returns FileSuggestion entries for all
	// MCP resources available from connected servers. Used by the @
	// autocomplete popup to merge resource suggestions with local files.
	GetMCPResources func() []FileSuggestion

	// MCPResourceReader, if non-nil, reads an MCP resource by server name
	// and URI. Used at submit time to resolve @mcp:server:uri tokens.
	MCPResourceReader fileutil.MCPResourceReader
}

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 handles streaming responses, tracks token usage, and manages the overall conversation flow between the user and AI assistants.

func NewCLI

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

NewCLI creates and initializes a new CLI instance. The debug parameter enables debug message rendering. 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) 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) 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) 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) 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.

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
	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 CancelTimerExpiredMsg added in v0.34.0

type CancelTimerExpiredMsg = core.CancelTimerExpiredMsg

Re-export from core package

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 = commands.ExtensionCommand

Re-export from commands package

type ExtensionItem added in v0.69.0

type ExtensionItem struct {
	Name   string // Extension display name (filename without .go extension).
	Path   string // Absolute path to the extension's .go file.
	Source string // "project" or "user" (global).
}

ExtensionItem holds display metadata about a loaded extension for the startup [Extensions] section. Built by the CLI layer from the SDK's []kit.ExtensionInfo.

type FileAttachmentResult added in v0.51.0

type FileAttachmentResult = fileutil.FileAttachmentResult

Re-export types from fileutil

type FilePart added in v0.51.0

type FilePart = fileutil.FilePart

Re-export types from fileutil

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")
	// or a display name for MCP resources (e.g. "mcp:server/resource-name").
	RelPath string
	// IsDir is true when the entry is a directory.
	IsDir bool
	// Score is the fuzzy match score (higher is better).
	Score int
	// IsMCPResource is true for MCP resource entries.
	IsMCPResource bool
	// MCPServerName is the MCP server name (set when IsMCPResource is true).
	MCPServerName string
	// MCPResourceURI is the MCP resource URI (set when IsMCPResource is true).
	MCPResourceURI string
	// MCPMIMEType is the MIME type hint from the MCP server.
	MCPMIMEType string
}

FileSuggestion represents a single file, directory, or MCP resource 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 *commands.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 []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 = core.ImageAttachment

Re-export from core package

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, appCtrl AppController) *InputComponent

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

func (*InputComponent) Clear added in v0.57.0

func (s *InputComponent) Clear() bool

Clear clears the textarea content and resets related state. Returns true if there was content to clear, false if the input was already empty.

func (*InputComponent) ClearPendingImages added in v0.7.0

func (s *InputComponent) ClearPendingImages() []core.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) RenderPopupCentered added in v0.33.0

func (s *InputComponent) RenderPopupCentered(termWidth, termHeight int) string

renderPopup renders the autocomplete popup for slash command suggestions. When rendered inline (not centered), returns the styled popup content. RenderPopupCentered renders the popup as a centered overlay.

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) SetMCPResourceProvider added in v0.51.0

func (s *InputComponent) SetMCPResourceProvider(fn func() []FileSuggestion)

SetMCPResourceProvider sets a callback that returns MCP resource suggestions for the @ autocomplete popup. 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 textarea, autocomplete popup (if visible), and help text.

type MCPPromptArgInfo added in v0.51.0

type MCPPromptArgInfo struct {
	Name        string
	Description string
	Required    bool
}

MCPPromptArgInfo describes an argument for an MCP prompt.

type MCPPromptExpandResult added in v0.51.0

type MCPPromptExpandResult struct {
	Messages []MCPPromptMessageInfo
}

MCPPromptExpandResult is the result of lazily expanding an MCP prompt.

type MCPPromptInfo added in v0.51.0

type MCPPromptInfo struct {
	Name        string             // Prompt name on the MCP server.
	Description string             // Human-readable description.
	Arguments   []MCPPromptArgInfo // Expected arguments.
	ServerName  string             // Owning MCP server name.
}

MCPPromptInfo describes an MCP prompt for display in the TUI (autocomplete, help). This is a pure UI type — it carries no MCP client dependencies.

type MCPPromptMessageInfo added in v0.51.0

type MCPPromptMessageInfo struct {
	Role      string // "user" or "assistant"
	Content   string
	FileParts []kit.LLMFilePart
}

MCPPromptMessageInfo is a single message from an expanded MCP prompt.

type MCPResourceReader added in v0.51.0

type MCPResourceReader = fileutil.MCPResourceReader

Re-export types from fileutil

type MarkdownThemeColors added in v0.15.0

type MarkdownThemeColors = style.MarkdownThemeColors

Re-export from style package

type MessageItem added in v0.33.0

type MessageItem interface {
	// Render returns the styled content for this message at the given width.
	// Implementations should cache the result to avoid re-rendering.
	Render(width int) string

	// Height returns the number of lines this message occupies when rendered.
	Height() int

	// ID returns a unique identifier for this message (for tracking).
	ID() string
}

MessageItem is the interface all scrollback messages must implement. This allows lazy rendering - messages are only rendered when visible.

type MessageRenderer

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

MessageRenderer handles the formatting and rendering of different message types

func (*MessageRenderer) RenderAssistantMessage

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

RenderAssistantMessage renders an AI assistant's response

func (*MessageRenderer) RenderCustomMessage added in v0.58.1

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

RenderCustomMessage renders a message with a custom alert label (e.g. "Help"). Content is rendered as markdown.

func (*MessageRenderer) RenderDebugConfigMessage

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

RenderDebugConfigMessage renders configuration settings

func (*MessageRenderer) RenderDebugMessage

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

RenderDebugMessage renders diagnostic and debugging information

func (*MessageRenderer) RenderErrorMessage

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

RenderErrorMessage renders error notifications

func (*MessageRenderer) RenderReasoningBlock added in v0.32.0

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

RenderReasoningBlock renders a reasoning/thinking block with the same styling as live streaming: muted italic text with margin. This is used when resuming sessions to display saved reasoning content.

func (*MessageRenderer) RenderSystemMessage

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

RenderSystemMessage renders KIT system messages using herald Note alert

func (*MessageRenderer) RenderToolMessage

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

RenderToolMessage renders a unified tool block

func (*MessageRenderer) RenderUserMessage

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

RenderUserMessage renders a user's input message with a colored left border.

func (*MessageRenderer) SetWidth

func (r *MessageRenderer) SetWidth(width int)

SetWidth updates the terminal width for the renderer

func (*MessageRenderer) UpdateTheme added in v0.31.0

func (r *MessageRenderer) UpdateTheme()

UpdateTheme refreshes the renderer's typography instance with colors from the current theme. This is called when the user changes themes via /theme.

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
	SystemMessage
	ErrorMessage
)

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 Bubble Tea component that displays a filterable list of available models as a centered overlay popup. It delegates rendering and keyboard navigation to PopupList and converts results into the ModelSelectedMsg / ModelSelectorCancelledMsg messages expected by AppModel.

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) RenderOverlay added in v0.35.0

func (ms *ModelSelectorComponent) RenderOverlay(termWidth, termHeight int) string

RenderOverlay returns the popup as a centered overlay string, ready to be composited on top of the main content via overlayContent().

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 — not used for overlay rendering. Use RenderOverlay for the centered overlay approach.

type PopupItem added in v0.35.0

type PopupItem struct {
	Label       string // primary display text
	Description string // secondary text (shown right of label)
	Active      bool   // true → render checkmark indicator
	Meta        any    // opaque data returned on selection
}

PopupItem represents a single entry in a PopupList. The component renders Label as the primary text and Description as secondary text to its right. The Active flag renders a checkmark to indicate the currently-active item (e.g. the current model). Meta is opaque caller data returned on selection.

type PopupList added in v0.35.0

type PopupList struct {
	// Title shown at the top of the popup.
	Title string
	// Subtitle shown below the title (dimmed).
	Subtitle string
	// FooterHint overrides the default keyboard-hint footer.
	FooterHint string

	// FilterFunc is called with (query, allItems) and should return the
	// filtered+scored subset. When nil, a default substring match is used.
	FilterFunc func(query string, items []PopupItem) []PopupItem
	// contains filtered or unexported fields
}

PopupList is a generic, themed, scrollable fuzzy-find popup list. It is rendered as a centered overlay on top of the normal TUI layout and can be reused by any feature that needs a selection popup (slash commands, model selector, session picker, extension-provided lists, etc.).

The caller is responsible for:

  • Building the initial item list
  • Providing a fuzzy-filter callback (or nil for substring matching)
  • Handling the result when the user selects or cancels

Navigation: up/down to move, enter to select, esc to cancel, type to filter.

func NewPopupList added in v0.35.0

func NewPopupList(title string, items []PopupItem, width, height int) *PopupList

NewPopupList creates a new popup list with the given items and dimensions.

func (*PopupList) HandleKey added in v0.35.0

func (p *PopupList) HandleKey(keyName, keyText string) PopupResult

HandleKey processes a single key event and returns the result. The caller should inspect PopupResult to decide whether to re-render, close the popup, or act on a selection.

keyName is the Bubble Tea key string (e.g. "up", "down", "enter", "esc"). keyText is the printable text for character keys (e.g. "a", "1").

func (*PopupList) IsSearching added in v0.35.0

func (p *PopupList) IsSearching() bool

IsSearching returns true when the search input is non-empty.

func (*PopupList) Render added in v0.35.0

func (p *PopupList) Render() string

Render returns the styled popup content (bordered box) ready to be placed as a centered overlay via lipgloss.Place + overlayContent.

func (*PopupList) RenderCentered added in v0.35.0

func (p *PopupList) RenderCentered(termWidth, termHeight int) string

RenderCentered returns the popup placed at the center of a termWidth×termHeight canvas, ready to be composed with overlayContent().

func (*PopupList) SelectedItem added in v0.35.0

func (p *PopupList) SelectedItem() *PopupItem

SelectedItem returns the item under the cursor, or nil if the list is empty.

func (*PopupList) SetSize added in v0.35.0

func (p *PopupList) SetSize(width, height int)

SetSize updates the popup dimensions (e.g. on window resize).

type PopupResult added in v0.35.0

type PopupResult struct {
	// Selected is non-nil when the user pressed Enter on an item.
	Selected *PopupItem
	// Cancelled is true when the user pressed Esc with no search text.
	Cancelled bool
	// Changed is true when the search or cursor moved (caller should re-render).
	Changed bool
}

PopupResult is returned by HandleKey to tell the caller what happened.

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
	RenderReasoningBlock(content string, timestamp time.Time) UIMessage
	RenderToolMessage(toolName, toolArgs, toolResult string, isError bool) UIMessage
	RenderSystemMessage(content string, timestamp time.Time) UIMessage
	RenderCustomMessage(content, label 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)
	UpdateTheme()
}

Renderer is the interface satisfied by MessageRenderer. It allows model.go and cli.go to call rendering methods uniformly.

type ScrollList added in v0.33.0

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

ScrollList manages a viewport over a list of MessageItems. It handles offset-based scrolling, lazy rendering, and character-level text selection (crush-style). Only visible items are rendered on each View() call.

func NewScrollList added in v0.33.0

func NewScrollList(width, height int) *ScrollList

NewScrollList creates a new ScrollList with the given dimensions.

func (*ScrollList) AtBottom added in v0.33.0

func (s *ScrollList) AtBottom() bool

AtBottom returns true if the viewport is at the bottom of the list.

func (*ScrollList) AtTop added in v0.33.0

func (s *ScrollList) AtTop() bool

AtTop returns true if the viewport is at the top of the list.

func (*ScrollList) ClearSelection added in v0.33.0

func (s *ScrollList) ClearSelection()

ClearSelection clears the current text selection.

func (*ScrollList) ExtractSelectedText added in v0.34.0

func (s *ScrollList) ExtractSelectedText() string

ExtractSelectedText returns the plain text content of the current selection by walking through selected items and extracting text at the character level using the ultraviolet cell buffer (ANSI-aware).

func (*ScrollList) GotoBottom added in v0.33.0

func (s *ScrollList) GotoBottom()

GotoBottom scrolls to the end of the list. Uses cached heights and walks backwards from the end to avoid rendering every item in the list.

func (*ScrollList) GotoTop added in v0.33.0

func (s *ScrollList) GotoTop()

GotoTop scrolls to the beginning of the list.

func (*ScrollList) HandleMouseDown added in v0.33.0

func (s *ScrollList) HandleMouseDown(x, y int) bool

HandleMouseDown handles mouse button press. Detects single, double, and triple clicks for character, word, and line selection respectively. Returns true if the click was handled.

func (*ScrollList) HandleMouseDrag added in v0.33.0

func (s *ScrollList) HandleMouseDrag(x, y int) bool

HandleMouseDrag handles mouse motion while button is held. Updates the selection endpoint for character-level precision. Returns true if selection was updated.

Defensively disables auto-scroll on every drag update — even if the MouseDown handler missed (e.g. click landed in viewport padding), any active drag means the user is selecting and the viewport must not jump.

func (*ScrollList) HandleMouseUp added in v0.33.0

func (s *ScrollList) HandleMouseUp() bool

HandleMouseUp handles mouse button release. Returns true if there was an active selection.

func (*ScrollList) HasSelection added in v0.33.0

func (s *ScrollList) HasSelection() bool

HasSelection returns true if there is a non-empty active selection.

func (*ScrollList) InvalidateItemHeight added in v0.62.0

func (s *ScrollList) InvalidateItemHeight(id string)

InvalidateItemHeight removes the cached height for the given item ID, forcing a re-render on the next height query. Call this after mutating an item's content (e.g. AppendChunk on a streaming message).

func (*ScrollList) IsMouseDown added in v0.68.0

func (s *ScrollList) IsMouseDown() bool

IsMouseDown reports whether the user currently has the mouse button held (i.e. a selection drag is in progress). Used by the parent model to avoid re-enabling auto-scroll during streaming while the user is selecting.

func (*ScrollList) ItemGap added in v0.33.0

func (s *ScrollList) ItemGap() int

ItemGap returns the current gap between items.

func (*ScrollList) ScrollBy added in v0.33.0

func (s *ScrollList) ScrollBy(lines int)

ScrollBy scrolls the viewport by the given number of lines. Positive = scroll down, negative = scroll up.

func (*ScrollList) ScrollPercent added in v0.33.0

func (s *ScrollList) ScrollPercent() float64

ScrollPercent returns the current scroll position as a percentage (0.0-1.0). 0.0 = at top, 1.0 = at bottom. Useful for scroll indicators.

func (*ScrollList) SetHeight added in v0.33.0

func (s *ScrollList) SetHeight(height int)

SetHeight updates the viewport height. Called when the terminal is resized.

func (*ScrollList) SetItemGap added in v0.33.0

func (s *ScrollList) SetItemGap(gap int)

SetItemGap sets the number of blank lines between items (0 = no gap).

func (*ScrollList) SetItems added in v0.33.0

func (s *ScrollList) SetItems(items []MessageItem)

SetItems replaces the items in the scroll list. If auto-scroll is enabled, the viewport will scroll to the bottom to show the latest content — EXCEPT when the user is actively selecting text (mouse button held), in which case the scroll position is locked so the highlighted content stays under the cursor. The pending bottom-scroll is deferred to MouseUp.

func (*ScrollList) SetWidth added in v0.33.0

func (s *ScrollList) SetWidth(width int)

SetWidth updates the viewport width. Called when the terminal is resized. This invalidates the height cache since rendered heights are width-dependent.

func (*ScrollList) View added in v0.33.0

func (s *ScrollList) View() string

View renders the visible portion of the scrollback. Only items that fit within the viewport height are rendered. ALWAYS returns exactly s.height lines (padded with empty lines if needed) to ensure the input/footer stay fixed at the bottom.

When an active selection exists, character-level highlighting is applied using ultraviolet ScreenBuffer for ANSI-aware cell manipulation.

type SessionDeletedMsg added in v0.16.0

type SessionDeletedMsg struct {
	Name string
}

SessionDeletedMsg is sent after a session is deleted so the parent can react (e.g. print a message).

type SessionFilterMode added in v0.16.0

type SessionFilterMode int

SessionFilterMode controls filtering of the session list.

const (
	SessionFilterAll   SessionFilterMode = iota // show all sessions
	SessionFilterNamed                          // only named sessions
)

func (SessionFilterMode) String added in v0.16.0

func (m SessionFilterMode) String() string

type SessionScopeMode added in v0.16.0

type SessionScopeMode int

SessionScopeMode controls which sessions are shown.

const (
	SessionScopeCwd SessionScopeMode = iota // current folder only
	SessionScopeAll                         // all sessions across projects
)

func (SessionScopeMode) String added in v0.16.0

func (m SessionScopeMode) String() string

type SessionSelectedMsg added in v0.16.0

type SessionSelectedMsg struct {
	Path string // absolute path to the JSONL session file
}

SessionSelectedMsg is sent when the user selects a session from the picker.

type SessionSelectorCancelledMsg added in v0.16.0

type SessionSelectorCancelledMsg struct{}

SessionSelectorCancelledMsg is sent when the user cancels the picker.

type SessionSelectorComponent added in v0.16.0

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

SessionSelectorComponent is a full-screen Bubble Tea component that lets the user browse and select from available sessions. Modeled after pi's session picker: right-aligned metadata, background-highlighted selection, scope/filter toggles, and inline search.

func NewSessionSelector added in v0.16.0

func NewSessionSelector(cwd string, width, height int) *SessionSelectorComponent

NewSessionSelector creates a session selector. It loads sessions for the current working directory and all sessions across projects. If cwd is empty, only "All" scope is available.

func (*SessionSelectorComponent) Init added in v0.16.0

func (ss *SessionSelectorComponent) Init() tea.Cmd

Init implements tea.Model.

func (*SessionSelectorComponent) IsActive added in v0.16.0

func (ss *SessionSelectorComponent) IsActive() bool

IsActive returns whether the selector is still accepting input.

func (*SessionSelectorComponent) SetCurrentPath added in v0.16.0

func (ss *SessionSelectorComponent) SetCurrentPath(path string)

SetCurrentPath sets the currently active session path so the picker can highlight it in the list.

func (*SessionSelectorComponent) Update added in v0.16.0

func (ss *SessionSelectorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model.

func (*SessionSelectorComponent) View added in v0.16.0

func (ss *SessionSelectorComponent) View() tea.View

View implements tea.Model.

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 ShellCommandMsg added in v0.34.0

type ShellCommandMsg = core.ShellCommandMsg

Re-export from core package

type ShellCommandResultMsg added in v0.34.0

type ShellCommandResultMsg = core.ShellCommandResultMsg

Re-export from core package

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).
	Description string // Short summary used in autocomplete and help.
}

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 = commands.SlashCommand

Re-export from commands package

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 added to the ScrollList by the parent AppModel. 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.
  • Content is displayed via StreamingMessageItem in the ScrollList.
  • 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(width int, modelName string) *StreamComponent

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

func (*StreamComponent) ConsumeOverflow added in v0.31.0

func (s *StreamComponent) ConsumeOverflow() string

ConsumeOverflow is a no-op in alt screen mode. Overflow is handled by the ScrollList viewport. Retained to satisfy streamComponentIface.

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 stream content before resetting.

This commits any pending chunks first so the output includes all received content, not just what has been flushed by the tick.

func (*StreamComponent) HasReasoning added in v0.7.0

func (s *StreamComponent) HasReasoning() bool

HasReasoning returns true if any reasoning content has been accumulated (committed or pending).

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) UpdateTheme added in v0.31.0

func (s *StreamComponent) UpdateTheme()

UpdateTheme refreshes the component's typography instance and spinner animation frames with colors from the current theme. This is called when the user changes themes via /theme.

func (*StreamComponent) View

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

View implements tea.Model. Returns an empty view since rendering is handled by StreamingMessageItem in the ScrollList. Retained to satisfy tea.Model.

type StreamingBashOutputItem added in v0.33.0

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

StreamingBashOutputItem represents live bash command output.

func NewStreamingBashOutputItem added in v0.33.0

func NewStreamingBashOutputItem(id string, command string) *StreamingBashOutputItem

NewStreamingBashOutputItem creates a new streaming bash output item.

func (*StreamingBashOutputItem) AppendStderr added in v0.33.0

func (m *StreamingBashOutputItem) AppendStderr(line string)

AppendStderr adds a stderr line to the output.

func (*StreamingBashOutputItem) AppendStdout added in v0.33.0

func (m *StreamingBashOutputItem) AppendStdout(line string)

AppendStdout adds a stdout line to the output.

func (*StreamingBashOutputItem) Height added in v0.33.0

func (m *StreamingBashOutputItem) Height() int

func (*StreamingBashOutputItem) ID added in v0.33.0

func (*StreamingBashOutputItem) MarkComplete added in v0.33.0

func (m *StreamingBashOutputItem) MarkComplete()

MarkComplete marks the bash output as complete.

func (*StreamingBashOutputItem) Render added in v0.33.0

func (m *StreamingBashOutputItem) Render(width int) string

type StreamingMessageItem added in v0.33.0

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

StreamingMessageItem represents actively streaming assistant or reasoning text. It accumulates content chunks and re-renders on each update for live display.

func NewStreamingMessageItem added in v0.33.0

func NewStreamingMessageItem(id, role string, modelName string) *StreamingMessageItem

NewStreamingMessageItem creates a new streaming message item.

func (*StreamingMessageItem) AppendChunk added in v0.33.0

func (s *StreamingMessageItem) AppendChunk(chunk string)

AppendChunk adds a content chunk and invalidates the render cache.

func (*StreamingMessageItem) Height added in v0.33.0

func (s *StreamingMessageItem) Height() int

Height returns the number of lines.

func (*StreamingMessageItem) ID added in v0.33.0

func (s *StreamingMessageItem) ID() string

ID returns the unique identifier.

func (*StreamingMessageItem) MarkComplete added in v0.33.0

func (s *StreamingMessageItem) MarkComplete()

MarkComplete marks the streaming message as complete and freezes the duration.

func (*StreamingMessageItem) Render added in v0.33.0

func (s *StreamingMessageItem) Render(width int) string

Render renders the streaming message with live content.

type SubmitMsg added in v0.34.0

type SubmitMsg = core.SubmitMsg

Re-export from core package

type TextMessageItem added in v0.33.0

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

TextMessageItem represents a completed text message (user or assistant) in the scrollback. It uses pre-rendered styled content from MessageRenderer.

func NewStyledMessageItem added in v0.33.0

func NewStyledMessageItem(id string, role string, rawContent string, preRendered string) *TextMessageItem

NewStyledMessageItem creates a message item with pre-rendered styled content. This is the preferred way to create messages when you have styled content from MessageRenderer.

func (*TextMessageItem) Height added in v0.33.0

func (m *TextMessageItem) Height() int

func (*TextMessageItem) ID added in v0.33.0

func (m *TextMessageItem) ID() string

func (*TextMessageItem) Render added in v0.33.0

func (m *TextMessageItem) Render(width int) string

type Theme

type Theme = style.Theme

Re-export from style package

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 = core.TreeCancelledMsg

Re-export from core package

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 = core.TreeNodeSelectedMsg

Re-export from core package

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 NewTreeSelectorForFork added in v0.34.0

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

NewTreeSelectorForFork creates a tree selector for the /fork command. It shows only user messages (flat list) matching Pi's fork behavior.

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.

The value should include ALL token categories from the last API call:

InputTokens + CacheReadTokens + CacheCreationTokens + OutputTokens

With Anthropic prompt caching, InputTokens can be near-zero while CacheReadTokens holds the bulk of the context. All four must be summed to get the true context window fill level.

OutputTokens is included because the assistant's output becomes part of the context on the next turn.

Use FinalResponse.Usage (last step only) rather than aggregate TotalUsage, because TotalUsage sums across all tool-calling steps and overstates the actual window fill level.

The value is set unconditionally (not max-only) so that context shrinks correctly after compaction.

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) UpdateModelInfo added in v0.23.0

func (ut *UsageTracker) UpdateModelInfo(modelInfo *models.ModelInfo, provider string, isOAuth bool)

UpdateModelInfo updates the model information and OAuth status when the model is switched mid-session. This ensures token costs and context limits are calculated correctly for the new model.

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
Package render provides pure rendering functions for message blocks.
Package render provides pure rendering functions for message blocks.
Package selection provides character-level text selection for terminal UIs.
Package selection provides character-level text selection for terminal UIs.

Jump to

Keyboard shortcuts

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