Documentation
¶
Index ¶
- Constants
- func FormatChars(chars int) string
- func FormatContextStr(totalTokens, contextLimit int) string
- func FormatContextStrWithProcs(totalTokens, contextLimit, running int) string
- func FormatContextStrWithProcs2(totalTokens, contextLimit, ephemeral, persistent int) string
- func FormatContextUsed(used, window int) string
- func FormatDuration(d time.Duration) string
- func FormatShellDisplay(cmd, workingDir, workspaceRoot string) string
- func FormatTokens(n int) string
- func FormatToolArgs(args map[string]any) string
- func GetResultSummary(result any) string
- func HistoryPathForWorkspace(home, workspaceDir string) string
- func ImageLabel(n int) string
- func LegacyHistoryPath(home string) string
- func LoadHistory(path string) ([]string, error)
- func LoadWorkspaceHistory(home, workspaceDir string) ([]string, string)
- func MakePrompt(text string) string
- func SaveHistory(path string, history []string) error
- func ShortenBlockMessage(blockMsg string) string
- func SingleLine(s string, maxChars int) string
- func Transcript(card *report.Report, ans *CardAnswer) string
- func TranscriptWidth(card *report.Report, ans *CardAnswer, width int) string
- type CardAnswer
- type InputModel
- func (m InputModel) Cancelled() bool
- func (m InputModel) CardAnswerPicked() *CardAnswer
- func (m InputModel) Init() tea.Cmd
- func (m InputModel) PastedImages() []string
- func (m *InputModel) SetCompletionBaseDir(dir string)
- func (m *InputModel) SetImagePasteHandler(fn func() (string, error))
- func (m *InputModel) SetReport(rep *report.Report)
- func (m *InputModel) SetStagedImages(paths []string)
- func (m *InputModel) SetWakePoll(fn func() (int, string))
- func (m InputModel) Submitted() bool
- func (m InputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)
- func (m InputModel) Value() string
- func (m InputModel) View() string
- func (m InputModel) WakeFired() bool
- type JSONOutput
- type JSONStats
- type TerminalTitle
- type Writer
- func (w *Writer) ActivePlan(planText string)
- func (w *Writer) Agent(msg string)
- func (w *Writer) Assistant(msg string)
- func (w *Writer) Debug(msg string)
- func (w *Writer) Divider(statsMsg string)
- func (w *Writer) Error(msg string)
- func (w *Writer) Info(msg string)
- func (w *Writer) IsHeadless() bool
- func (w *Writer) IsJSONMode() bool
- func (w *Writer) IsVerbose() bool
- func (w *Writer) Report(rep *report.Report)
- func (w *Writer) SetColorOutput(stdout io.Writer)
- func (w *Writer) SetHeadless(headless bool)
- func (w *Writer) SetJSONContent(content string)
- func (w *Writer) SetJSONMode(jsonMode bool)
- func (w *Writer) SetMarkdownMode(mode string)
- func (w *Writer) SetPromptWatcher(fn func() bool)
- func (w *Writer) SetQuiet(quiet bool)
- func (w *Writer) SetStderr(stderr io.Writer)
- func (w *Writer) SetStderrIsTerminal(isTTY bool)
- func (w *Writer) SetStdout(stdout io.Writer)
- func (w *Writer) SetStdoutIsTerminal(isTTY bool)
- func (w *Writer) SetVerbose(lines int)
- func (w *Writer) StartupInfo(msg string)
- func (w *Writer) Steering(msg string)
- func (w *Writer) Thinking(context, msg string)
- func (w *Writer) Tool(name, msg string)
- func (w *Writer) ToolCall(name, argsDisplay, context string)
- func (w *Writer) ToolContext(context string)
- func (w *Writer) ToolProgress(dot string)
- func (w *Writer) ToolResult(summary, duration string)
- func (w *Writer) VerboseLines() int
- func (w *Writer) VerboseOutput(output string) bool
- func (w *Writer) Warn(msg string)
- func (w *Writer) WriteJSONOutput(stats *JSONStats)
Constants ¶
const ( DefaultTitleRunningIcon = "⏳" DefaultTitleAskingIcon = "💬" DefaultTitleWaitingIcon = "💤" )
Default icons for the three states TerminalTitle shows. The hourglass means a turn is running, the speech balloon means something is waiting for you to answer it — a Question, a path or MCP confirmation, the pause prompt — and the sleep symbol means nothing is running and the composer is open. All three are configurable under ui.terminal_title in config.yaml, and any of them can be set to the empty string for no icon.
Variables ¶
This section is empty.
Functions ¶
func FormatChars ¶
FormatChars formats character count in a human-readable way (e.g., "1.5k")
func FormatContextStr ¶
FormatContextStr formats context usage for display. It shows what is used and the share of the window in use, e.g. "8.4k 1%": the raw count on its own never said whether anything needed doing about it.
func FormatContextStrWithProcs ¶
FormatContextStrWithProcs adds the background-process count to the context status: "8.4k 1% ★2" when running > 0, the plain base otherwise. Only running processes count — finished ones already surface through the inbox — and zero keeps the header exactly as before. It counts every running process; use FormatContextStrWithProcs2 to split ephemeral from persistent.
func FormatContextStrWithProcs2 ¶
FormatContextStrWithProcs2 splits the count: blue "★N" for ephemeral processes (die at turn end), red "★N!" for persistent ones (survive, need stopping). Either zero hides its star; both zero keep the header exactly as before.
func FormatContextUsed ¶
FormatContextUsed reports how much of the window a turn's peak context reached, e.g. "8.4k / 1.0M (1%)". The used count and the window share the same k/M scale (FormatTokens), and the share rounds the way FormatContextStr rounds it. No configured window (window <= 0) leaves the bare used count, mirroring how the per-step status drops the percent.
func FormatDuration ¶
FormatDuration formats a duration in a human-readable way, omitting zero values
func FormatShellDisplay ¶
FormatShellDisplay renders shell command and working directory relative to workspace.
func FormatTokens ¶
FormatTokens renders a token count on a k/M scale: "8.4k", "128k", "1.0M". The M scale starts just under a million so 999999 rounds to "1.0M" rather than an unreadable "1000k".
func FormatToolArgs ¶
FormatToolArgs formats tool arguments for compact display. Keys are sorted so the terminal line is deterministic.
func GetResultSummary ¶
GetResultSummary extracts meaningful size info from tool result
func HistoryPathForWorkspace ¶ added in v0.4.2
HistoryPathForWorkspace returns the input history file for one workspace directory. The path is under ~/.kvit-coder/history/ keyed by the sha256 of the absolute workspace path (same scheme as permission stores), so every directory gets its own arrow-up history and listing the directory shows every stored workspace. An empty workspaceDir resolves against the current working directory; when that also fails the legacy global path is returned so the caller still has somewhere to read and write.
func ImageLabel ¶
ImageLabel returns the in-text label for the nth staged image (1-based), e.g. "[image1]".
func LegacyHistoryPath ¶ added in v0.4.2
LegacyHistoryPath is the pre-per-directory input history file: a single global file in the user's home. It is kept as the seed for per-directory histories, not as the live path.
func LoadHistory ¶
LoadHistory loads history from a file
func LoadWorkspaceHistory ¶ added in v0.4.2
LoadWorkspaceHistory loads the history for one workspace directory. When the per-directory file does not exist yet but the legacy global file does, the global entries seed the workspace so a first run in a new directory starts from the familiar history and then diverges on its own.
func MakePrompt ¶
MakePrompt creates a colored prompt with white text on gray background
func SaveHistory ¶
SaveHistory saves history to a file
func ShortenBlockMessage ¶
ShortenBlockMessage creates a user-friendly short summary of a block message. The full message (with diff) is still sent to the LLM.
func SingleLine ¶
SingleLine collapses a message to one display line: newlines, carriage returns and tabs become spaces, runs of whitespace fold to one, and the result is trimmed and truncated to maxChars (with an ellipsis) when maxChars is positive.
func Transcript ¶
func Transcript(card *report.Report, ans *CardAnswer) string
Transcript returns the scrollback copy of a report with the same colors as the card: headline, block lines, dim fields, bright options and muted instructions. Details are always expanded, so nothing the card kept folded is lost from the history.
func TranscriptWidth ¶
func TranscriptWidth(card *report.Report, ans *CardAnswer, width int) string
TranscriptWidth is Transcript wrapped to an explicit width, so tests and callers that know the terminal can pin it.
Types ¶
type CardAnswer ¶
type CardAnswer struct {
// BlockID and OptionID say what was answered, for the record.
BlockID string
OptionID string
// Effect is one of report.EffectDispatch, EffectCollect or EffectResolve.
Effect string
// Prompt is the text the next turn runs, for a dispatch. Empty otherwise.
Prompt string
// Number is the digit the user pressed (1-based, as the card showed it),
// and Label is the option's label. Both are recorded so a scrollback copy
// of the report can say what was picked without looking the report up
// again.
Number int
Label string
}
CardAnswer is what the user picked from the report card. The driver turns it into the next turn, or into nothing at all when the effect is "resolve".
func (*CardAnswer) Describe ¶
func (a *CardAnswer) Describe() string
Describe returns the scrollback line for what the user picked, e.g. "[picked 1) Preserve hidden positions — filtered/preserve (dispatch, will run as next turn)]". It returns "" when there is nothing to describe.
type InputModel ¶
type InputModel struct {
// contains filtered or unexported fields
}
InputModel wraps the textarea component for multi-line input
func NewInputModel ¶
func NewInputModel(prompt string, history []string) InputModel
NewInputModel creates a new input model with the given prompt
func (InputModel) Cancelled ¶
func (m InputModel) Cancelled() bool
Cancelled returns whether the input was cancelled
func (InputModel) CardAnswerPicked ¶
func (m InputModel) CardAnswerPicked() *CardAnswer
CardAnswerPicked returns what the user chose from the card, or nil.
func (InputModel) PastedImages ¶
func (m InputModel) PastedImages() []string
PastedImages returns the images staged by the paste key plus any seeded with SetStagedImages, in label order.
func (*InputModel) SetCompletionBaseDir ¶
func (m *InputModel) SetCompletionBaseDir(dir string)
SetCompletionBaseDir tells path completion which directory relative paths resolve against (the workspace root). Empty falls back to the working directory at completion time.
func (*InputModel) SetImagePasteHandler ¶
func (m *InputModel) SetImagePasteHandler(fn func() (string, error))
SetImagePasteHandler installs the clipboard-image stager behind the paste key. Passing nil disables image paste.
func (*InputModel) SetReport ¶
func (m *InputModel) SetReport(rep *report.Report)
SetReport installs the report the last turn ended with. Passing nil, which is what happens after any turn that did not end with one, leaves the composer exactly as it was before reports existed.
func (*InputModel) SetStagedImages ¶
func (m *InputModel) SetStagedImages(paths []string)
SetStagedImages seeds the composer with images staged before it opened (:image, :paste). They take the first labels, so a paste below continues the numbering instead of restarting at [image1].
func (*InputModel) SetWakePoll ¶
func (m *InputModel) SetWakePoll(fn func() (int, string))
SetWakePoll installs the inbox waiter behind the composer. A nil poll disables wake.
func (InputModel) Submitted ¶
func (m InputModel) Submitted() bool
Submitted returns whether the input was submitted
func (InputModel) WakeFired ¶
func (m InputModel) WakeFired() bool
WakeFired reports whether the composer submitted itself on inbox activity.
type JSONOutput ¶
type JSONOutput struct {
Content string `json:"content"` // The final LLM response
Stats *JSONStats `json:"stats,omitempty"` // Statistics (tokens, cost, etc.)
// Report is the structured report the turn ended with, when it ended with
// one. A consumer that wants the status or the blocks reads it here rather
// than parsing Content, which holds the same report as text.
Report *report.Report `json:"report,omitempty"`
}
JSONOutput represents the structured output for --json mode
type JSONStats ¶
type JSONStats struct {
Session string `json:"session,omitempty"`
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
CacheReadTokens int `json:"cache_read_tokens,omitempty"`
TotalCost float64 `json:"total_cost_usd,omitempty"`
CacheDiscount float64 `json:"cache_discount_usd,omitempty"`
// The turn's peak context, and the window it was measured against, so a
// consumer of the document can tell a small turn from a near-full one.
ContextUsed int `json:"context_used"`
ContextWindow int `json:"context_window"`
DurationMs int64 `json:"duration_ms"`
Steps int `json:"steps"`
}
JSONStats represents statistics in JSON output
type TerminalTitle ¶
type TerminalTitle struct {
// contains filtered or unexported fields
}
TerminalTitle sets the terminal's window title, which a tabbed terminal such as the one in VS Code also uses as the tab label. It shows an icon for what is happening — a turn is running, a prompt is waiting for you to answer it, or nothing is running — followed by the session's title, so a window that is not on screen still says whether the agent is still working or has stopped to ask something.
Two processes write it. The front end owns it between turns and around a turn; the agent process borrows it while a prompt of its own is waiting, because the front end is blocked on the agent and cannot see the prompt. They never write at the same time: the front end is inside cmd.Run while the agent has it.
A terminal takes its title from the escape sequence ESC ] 0 ; text BEL, which sets the window title and the icon name in one go and is understood by every terminal in common use. It is a control sequence rather than output: writing it while a turn is printing changes nothing on screen.
Whether the terminal then shows that title is up to the terminal. VS Code in particular ignores it until "terminal.integrated.tabs.title" is set to "${sequence}", and tmux until "set -g set-titles on" is set.
Nothing is written unless the output is a terminal, so a piped or redirected run stays byte-clean.
func NewTerminalTitle ¶
func NewTerminalTitle(out io.Writer) *TerminalTitle
NewTerminalTitle returns a title setter writing to out, which the front end passes as os.Stdout. When out is a file it has to be a terminal, and not a dumb one, or the setter is inert — that is what keeps the escape sequence out of a redirected stream. Setting KVIT_CODER_NO_TITLE in the environment makes it inert too. Anything that is not a file (a buffer in a test) is written to as asked.
func (*TerminalTitle) Asking ¶
func (t *TerminalTitle) Asking(session string)
Asking shows that the turn has stopped to put something to you and is waiting for the answer.
func (*TerminalTitle) Clear ¶
func (t *TerminalTitle) Clear()
Clear empties the title on the way out. There is no way to ask a terminal what its title was before, so the empty title is what a program that has finished leaves behind: shells that set a title from their prompt put their own back at the next prompt.
func (*TerminalTitle) Disable ¶
func (t *TerminalTitle) Disable()
Disable stops the title being touched at all, for ui.terminal_title.enabled set to false.
func (*TerminalTitle) Running ¶
func (t *TerminalTitle) Running(session string)
Running shows that a turn is under way in the named session.
func (*TerminalTitle) SetIcons ¶
func (t *TerminalTitle) SetIcons(running, waiting, asking string)
SetIcons replaces the three state icons. An empty string means that state gets no icon, leaving the session title on its own.
func (*TerminalTitle) Waiting ¶
func (t *TerminalTitle) Waiting(session string)
Waiting shows that the turn has finished and the composer is open.
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer provides formatted output with consistent prefixes and optional colors.
func NewWriter ¶
NewWriter creates a new Writer with the specified verbosity level. verboseLines: 0 = not verbose, >0 = show tool output up to this many lines (half top, half bottom if truncated)
func (*Writer) ActivePlan ¶
ActivePlan renders a plan from the <active_plan> format string.
func (*Writer) Assistant ¶
Assistant prints an assistant message in white. In headless mode, this goes to stdout (the final answer). In JSON mode, it stores the content to be output later with WriteJSONOutput.
The message is markdown, and on a terminal it is styled as such (headings, lists, code, tables) instead of printed verbatim. Piped output, JSON mode, and NO_COLOR/TERM=dumb stay raw markdown, so scripts and logs see exactly what the model wrote. Rendering never fails the turn: on any error the raw message goes through.
func (*Writer) Divider ¶
Divider separates the step progress from the final report. It carries the turn timing that used to print as a standalone "[39s: 39s llm + ...]" line after the report, so nothing needs to print after the report itself. A blank line leads so the divider never crowds the last step line. It goes to the progress stream (stderr when headless), keeping stdout clean for the final answer. Quiet and JSON modes print nothing.
func (*Writer) Info ¶
Info prints an info message in gray, indented like other step lines. Multi-line messages keep every continuation line indented.
func (*Writer) IsHeadless ¶
IsHeadless returns true if headless mode is enabled.
func (*Writer) IsJSONMode ¶
IsJSONMode returns true if JSON mode is enabled.
func (*Writer) Report ¶
Report prints the structured report a turn ended with. It goes where the final answer goes — stdout when headless — but never through the markdown renderer: the card is already laid out, and styling it as Markdown would reflow the very columns that make it readable.
On a terminal the lines are wrapped to its width, with continuations hanging under the field they belong to. Piped output is left as it was written, the same rule the markdown renderer follows, so a log or a script sees the layout the agent produced rather than one shaped by whoever ran it.
In JSON mode nothing is printed; the report rides in the JSON document instead, as both the rendered text and the structure behind it.
func (*Writer) SetColorOutput ¶
SetColorOutput sets the output for colored text (used by fatih/color package). This should be called when you want colored output to go somewhere other than os.Stdout.
func (*Writer) SetHeadless ¶
SetHeadless enables headless mode where progress goes to stderr and final answer to stdout.
func (*Writer) SetJSONContent ¶
SetJSONContent sets the content to be output in JSON mode
func (*Writer) SetJSONMode ¶
SetJSONMode enables or disables JSON output mode.
func (*Writer) SetMarkdownMode ¶
SetMarkdownMode selects when the final answer is styled as markdown: "auto" (the default), "always", or "never". Empty and unknown values are auto.
func (*Writer) SetPromptWatcher ¶
SetPromptWatcher installs a function reporting whether a question is waiting for an answer. Progress indicators redraw the line they sit on, so one drawn while a question is on screen erases it; with this set they hold off until the question has been answered.
func (*Writer) SetQuiet ¶
SetQuiet enables or disables quiet mode (suppresses all output except Assistant messages).
func (*Writer) SetStderrIsTerminal ¶
SetStderrIsTerminal overrides the detection of whether stderr can be redrawn in place, so a test can exercise both paths without a pseudo-terminal.
func (*Writer) SetStdoutIsTerminal ¶
SetStdoutIsTerminal overrides the detection of whether the final answer goes to a terminal, so a test can exercise the styled and raw markdown paths without a pseudo-terminal.
func (*Writer) SetVerbose ¶
SetVerbose sets the verbose lines level.
func (*Writer) StartupInfo ¶
StartupInfo prints startup information in brown.
func (*Writer) Steering ¶
Steering prints a steering prompt in bright green, indented like Info. It marks the moment typing steering becomes available, so it stands out from the gray progress lines around it. Layout and wrapping match Info.
func (*Writer) Thinking ¶
Thinking prints one bullet-led step line per model turn: the bullet, the brownish context share with the blue background-process count ("★2") when any are running, and the message in full white. An empty message still prints "● (ctx)" so every step shows its status. Continuation lines of a multi-line message stay indented and wrapped.
func (*Writer) Tool ¶
Tool prints a tool execution message with [tool:name] prefix (unused, kept for compatibility).
func (*Writer) ToolCall ¶
ToolCall prints a compact gray tool-call line indented under the step header. The context share lives on the header already, so the context argument is accepted for compatibility and ignored. Success summaries are not shown (see ToolResult); only the call itself appears here.
func (*Writer) ToolContext ¶
ToolContext is kept for compatibility. The context share already leads every step header, so printing it again per tool would repeat the status on each call line.
func (*Writer) ToolProgress ¶
ToolProgress prints a progress indicator for a long wait: one mark per second, folded into "*" per finished minute (see maxDotsPerLine). Dots (".") are a tool running; colons (":") are the model thinking. Both waits share the indented row under the last tool-call line, so the mark is what tells them apart: a colon row under a Read line is the next model call, not the Read.
func (*Writer) ToolResult ¶
ToolResult finishes a tool line. A slow tool keeps its "...2s" duration in gray; the per-result size summary ("34 lines, 5.2k chars") is intentionally not shown, so step output stays one header plus its calls. Error and pending summaries still print as indented single lines.
func (*Writer) VerboseLines ¶
VerboseLines returns the max lines to show in verbose mode.
func (*Writer) VerboseOutput ¶
VerboseOutput prints tool output in verbose mode with truncation if needed. Returns true if output was printed.
func (*Writer) Warn ¶
Warn prints a warning as one indented line in dim brown. The message is collapsed to a single line and truncated, so a warning never breaks the step layout.
func (*Writer) WriteJSONOutput ¶
WriteJSONOutput outputs the final JSON result to stdout