Documentation
¶
Index ¶
- Constants
- Variables
- func AttemptPrettyPrint(w io.Writer, chatMessage pub_models.Message, username string, raw bool) error
- func ConfigDirPaths() []string
- func ConfigUpgradeMessage(configFileName string, added []string) string
- func CreateConfigDir(configPath string) error
- func CreateFile[T any](path string, toCreate *T) error
- func DeleteRange[T any](s []T, start, end int) ([]T, error)
- func ExpandUserPath(p string) (string, error)
- func GetClaiCacheDir() (string, error)
- func GetClaiConfigDir() (string, error)
- func GetFirstTokens(prompt []string, n int) []string
- func IsMcpLogAuthChallengeLine(line string) bool
- func IsMcpLogAuthLine(line string) bool
- func IsMcpLogAuthPayloadLine(line string) bool
- func IsMcpLogErrorLine(line string) bool
- func IsTerminalWriter(w io.Writer) bool
- func LoadConfigFromFile[T any](configDirPath, configFileName string, migrationCb func(string) error, dflt *T) (T, error)
- func LoadConfigFromFileCollect[T any](configDirPath, configFileName string, migrationCb func(string) error, dflt *T) (T, []string, error)
- func LoadTheme(configDirPath string) error
- func NewMacroReader(inputs []string) io.Reader
- func NotificationBellEnabled() bool
- func PrepareDisplayMessage(msg pub_models.Message) pub_models.Message
- func PrintMcpErrorBlock(w io.Writer, server string, lines []string, termWidth int) error
- func PrintMcpLogHeader(w io.Writer, server string, termWidth int) error
- func PrintMcpLogLine(w io.Writer, line string, termWidth int) error
- func PrintToolActivity(w io.Writer, call pub_models.Call, content string, termWidth, maxRows int) error
- func Prompt(stdinReplace string, args []string) (string, error)
- func RandomPrefix() string
- func ReadAndUnmarshal[T any](filePath string, config *T) error
- func RehydrateEscapedConfigString(s string) string
- func ReplaceTildeWithHome(s string) (string, error)
- func ResolveConfigDirPath(configDir string, components []string) (string, error)
- func RoleColor(role string) string
- func RollingOutputEnabled() bool
- func RollingOutputWindowCellHeight() int
- func SaveBase64File(prefix, dir, b64JSON, extension string) (string, error)
- func SessionDimensions(w io.Writer) dimensions.Dimensions
- func ShortenedOutput(out string, maxShortenedNewlines int) string
- func SummarizeAsyncToolResult(name, content string) string
- func TableTheme() table.Theme
- func ThemeConfigPath(configDirPath string) string
- func ToolOutputRows() int
- func UnescapeConfigString(toEdit string) string
- func UpdateMessageTerminalMetadata(msg string, line *string, lineCount *int, termWidth int)
- func WriteFile[T any](path string, toWrite *T) error
- func WriteFileAtomic(path string, data []byte, perm os.FileMode) error
- type ActivityViewport
- func (v *ActivityViewport) AppendMcpLogBlock(server, content string, maxRows int)
- func (v *ActivityViewport) AppendReasoning(content string)
- func (v *ActivityViewport) AppendText(content string)
- func (v *ActivityViewport) AppendTool(call pub_models.Call, content string, maxRows int)
- func (v *ActivityViewport) Content() string
- func (v *ActivityViewport) DetachRenderedRegion() int
- func (v *ActivityViewport) FinishReasoning()
- func (v *ActivityViewport) FinishText()
- func (v *ActivityViewport) RemoveTextBlock() int
- func (v *ActivityViewport) Render(w io.Writer) error
- func (v *ActivityViewport) Resize(width, terminalHeight int)
- func (v *ActivityViewport) Rows() []string
- func (v *ActivityViewport) TextBlockActive() bool
- type ContextKey
- type RollingOutputConfig
- type Theme
Constants ¶
const MaxShortenedNewlines = 5
Variables ¶
var Live bool
Live controls whether macro mode switches to interactive stdin after consuming all macro inputs (true, default) or appends trailing "q\n" terminators for graceful auto-exit (false). Set by Setup() based on the --non-interactive flag.
var NoCreateConfig bool
NoCreateConfig is stronger than ReadonlyConfig: it also suppresses config dir and default config file creation, migration callbacks, and the resulting reads. Set for read-only chat subcommands so `clai chat list` can run against a read-only filesystem or a config dir that does not exist yet.
var ReadonlyConfig bool
ReadonlyConfig makes config loads read-only: missing fields are filled in memory, but the file is never rewritten and no upgrade announcement is printed. Set for raw (machine-readable) runs so shell hooks and scripts never mutate the user's configs as a side effect (config migration design, Q5). This is what keeps a precmd hook running `clai -r chat dirv2` from silently migrating the configs before the user's own commands run.
Functions ¶
func AttemptPrettyPrint ¶
func AttemptPrettyPrint(w io.Writer, chatMessage pub_models.Message, username string, raw bool) error
AttemptPrettyPrint by first checking if the glow command is available, and if so, pretty print the chat message. If not found, simply print the message as is. If the message has ReasoningContent, it is rendered with reasoning color before the main content.
If w is nil, os.Stdout is used.
func ConfigDirPaths ¶
func ConfigDirPaths() []string
func ConfigUpgradeMessage ¶ added in v1.10.20
ConfigUpgradeMessage returns the announcement text for fields added to a config file, e.g. "added new field(s) to textConfig.json: stoploss".
func CreateConfigDir ¶
func CreateFile ¶
func DeleteRange ¶
DeleteRange removes elements in [start, end] (inclusive) from the slice and returns the new slice. Panics if indices are invalid. Works for any slice type (Go 1.18+).
func ExpandUserPath ¶ added in v1.11.0
ExpandUserPath expands environment variables ($VAR or ${VAR}) and a leading tilde in p. Unset variables expand to the empty string, matching shell semantics. Paths like ~user are returned unchanged.
func GetClaiCacheDir ¶
GetClaiCacheDir returns the path to the clai cache directory. The directory is located inside the user's cache directory as <UserCacheDir>/clai, unless overridden by CLAI_CACHE_DIR.
func GetClaiConfigDir ¶
GetClaiConfigDir returns the path to the clai configuration directory. The directory is located inside the user's configuration directory as <UserConfigDir>/.clai, unless overridden by CLAI_CONFIG_DIR.
func GetFirstTokens ¶
GetFirstTokens returns the first n tokens of the prompt, or the whole prompt if it has less than n tokens
func IsMcpLogAuthChallengeLine ¶ added in v1.11.2
IsMcpLogAuthChallengeLine reports whether a line is a high-confidence authorization prompt, fit for reclassifying a connect failure's error type rather than only for opening a pre-emptive prompt window. See IsMcpLogAuthLine's doc comment for why a bare "401"/"403" is not enough here (R1-01).
func IsMcpLogAuthLine ¶ added in v1.10.23
IsMcpLogAuthLine reports whether an MCP server stderr line asks the user to act on an auth flow: an imperative prompt, an auth failure, or a URL whose path looks like an auth endpoint.
func IsMcpLogAuthPayloadLine ¶ added in v1.10.23
IsMcpLogAuthPayloadLine reports whether a line following an auth prompt carries its actionable payload: any URL, or a short uppercase user code like "ABCD-1234". Everything else after a prompt is machinery chatter.
func IsMcpLogErrorLine ¶ added in v1.10.21
IsMcpLogErrorLine reports whether an MCP server stderr line looks like an error. The match is case-insensitive substring matching against mcpLogErrorKeywords.
func IsTerminalWriter ¶ added in v1.10.20
IsTerminalWriter reports whether w resolves to a terminal writer.
func LoadConfigFromFile ¶
func LoadConfigFromFileCollect ¶ added in v1.10.20
func LoadConfigFromFileCollect[T any]( configDirPath, configFileName string, migrationCb func(string) error, dflt *T, ) (T, []string, error)
LoadConfigFromFileCollect is LoadConfigFromFile without the stdout announcement. It returns the JSON paths of every field it added to a pre-existing file, e.g. "stoploss" or "stoploss.max-tokens-handover-instructions"; an empty list means the file needed no upgrade (or was freshly created). Callers that must control when the announcement is printed (e.g. the interactive setup wizard, which prints it just before its TUI starts and pads the block with a blank separator line that absorbs the wizard's one-line clear overshoot) use this to announce at the right time.
func LoadTheme ¶
LoadTheme loads (and possibly creates) the theme.json file within the config dir. It is safe to call multiple times. After loading, stores the theme for consumer access.
func NewMacroReader ¶ added in v1.10.17
NewMacroReader creates a reader that yields each input string as a line.
By default (Live=true), after all macro inputs are consumed the reader transparently switches to os.Stdin so the user continues interacting.
When Live is false (--non-interactive), 10 trailing "q\n" lines are appended for graceful termination through nested table loops.
Returns nil when inputs is empty or nil (caller should skip injection).
func NotificationBellEnabled ¶
func NotificationBellEnabled() bool
func PrepareDisplayMessage ¶ added in v1.10.10
func PrepareDisplayMessage(msg pub_models.Message) pub_models.Message
func PrintMcpErrorBlock ¶ added in v1.10.21
PrintMcpErrorBlock prints one elevated MCP server error block below the rolling window. The block is display-only: it never enters the chat history or model context.
func PrintMcpLogHeader ¶ added in v1.10.23
PrintMcpLogHeader prints the rolling-window style block header for one MCP server's log lines.
func PrintMcpLogLine ¶ added in v1.10.23
PrintMcpLogLine prints one styled MCP log line under a block header, following the mcpLogBodyRows styling and wrapping rules.
func PrintToolActivity ¶ added in v1.10.20
func PrintToolActivity(w io.Writer, call pub_models.Call, content string, termWidth, maxRows int) error
PrintToolActivity prints one non-raw tool execution as a header and its result. The model transcript remains independent from this display-only representation.
func Prompt ¶
Prompt returns the prompt by checking all the arguments and stdin. If there is no arguments, but data in stdin, stdin will become the prompt. If there are arguments and data in stdin, all stdinReplace tokens will be substituted with the data in stdin
func RandomPrefix ¶
func RandomPrefix() string
func ReadAndUnmarshal ¶
ReadAndUnmarshal by first finding the file, then attempting to read + unmarshal to T
func RehydrateEscapedConfigString ¶ added in v1.11.1
RehydrateEscapedConfigString cleans up a value corrupted by an older clai setup editor and leaves every other value byte-identical.
The old editor escaped every real newline and tab, so a corrupted value holds literal "\n"/"\t" and no real newline or tab character. A value that already holds a real newline or tab was written by a human or by a current editor, so the guard keeps it. This protects prompts that quote a literal "\n" or "\t" as text, which is why RehydrateEscapedConfigString is not used for the shell context template (architecture/shell-context.md, "Newline encoding").
func ReplaceTildeWithHome ¶
func ResolveConfigDirPath ¶
func RollingOutputEnabled ¶ added in v1.10.20
func RollingOutputEnabled() bool
RollingOutputEnabled reports whether reasoning and tool activity share a rolling terminal viewport.
func RollingOutputWindowCellHeight ¶ added in v1.10.20
func RollingOutputWindowCellHeight() int
RollingOutputWindowCellHeight returns the maximum terminal cells (rows) for the shared rolling activity viewport.
func SaveBase64File ¶ added in v1.10.17
SaveBase64File decodes a base64 string and writes it to dir/prefix_random.extension. Falls back to /tmp on write failure.
func SessionDimensions ¶ added in v1.10.20
func SessionDimensions(w io.Writer) dimensions.Dimensions
SessionDimensions resolves the one terminal-size snapshot for w. The snapshot is bound to the writer's file descriptor, so the observed size matches the file the output is actually written to (R2-02): a terminal yields its live size, and a non-terminal writer, a failed read, or a reported zero size deterministically yields dimensions.Fallback (80x24). No error propagates, matching the legacy silent fallback. A nil writer resolves to os.Stdout.
This is the single width-discovery point of clai. Every width-aware render path either stores the result of this helper for its session (the querier's snapshot) or calls it once at the start of a standalone render operation.
func ShortenedOutput ¶
ShortenedOutput returns a shortened version of the output
func SummarizeAsyncToolResult ¶ added in v1.10.20
SummarizeAsyncToolResult returns the concise interactive representation of async tool output. Raw and redirected output bypass this helper.
func TableTheme ¶ added in v1.10.17
TableTheme returns the table.Theme derived from the current global theme.
func ThemeConfigPath ¶
ThemeConfigPath returns the fully qualified theme.json path.
func ToolOutputRows ¶ added in v1.10.20
func ToolOutputRows() int
ToolOutputRows returns the maximum terminal rows used for non-raw tool output.
func UnescapeConfigString ¶ added in v1.11.1
UnescapeConfigString rehydrates literal "\n" and "\t" sequences into real control characters. Config values written by older clai setup editors carry those literals instead of real newlines (architecture/shell-context.md, "Newline encoding").
func UpdateMessageTerminalMetadata ¶
UpdateMessageTerminalMetadata updates the terminal metadata. Meaning the lineCount, to eventually clear the terminal
func WriteFileAtomic ¶ added in v1.11.1
WriteFileAtomic writes data to path through a temp file in the same directory and a rename, so a concurrent reader sees the old file or the new file, never a prefix (worklog 2026-09-09-conversation-summaries, D31). An existing file keeps its permission bits, like os.WriteFile.
Types ¶
type ActivityViewport ¶ added in v1.10.20
type ActivityViewport struct {
// contains filtered or unexported fields
}
ActivityViewport keeps a bounded, display-only view of streamed reasoning, assistant prose, and tool activity. It never changes model or tool source data.
Storage is logical, not visual: each block keeps the full sanitized content (the source of truth) plus its display policy (kind, header and role colours, 2-space body indentation, and tool output-marker and ERROR: handling). Colours and indentation are re-derived at rewrap time, so a width change rewraps every retained block instead of reconstructing content from already wrapped rows. The active reasoning and text blocks coalesce streamed tokens into one open block; finished blocks keep their logical content until the retention policy evicts them (maxActivityBlocks blocks, each bounded by maxActivityBlockRunes). A terminal-height grow can therefore bring retained content back into view, bounded by retention, not by the last visible row count.
func NewActivityViewport ¶ added in v1.10.20
func NewActivityViewport(width, maxRows, terminalHeight int) *ActivityViewport
NewActivityViewport creates a viewport for all transient model activity. maxRows is the configured rolling-window cap; terminalHeight is the raw terminal height at creation. The effective height is min(maxRows, max(terminalHeight, 1)) from the start, so the viewport never renders taller than the terminal it writes to (R5-01). Resize takes raw terminal dimensions and keeps the same min(cap, terminal height) bound on later dimension changes.
func (*ActivityViewport) AppendMcpLogBlock ¶ added in v1.10.21
func (v *ActivityViewport) AppendMcpLogBlock(server, content string, maxRows int)
AppendMcpLogBlock adds one compacted server-log block to the viewport. The block carries the same body budget as a tool block; lines that match the MCP log error heuristic are styled as errors inside the block.
func (*ActivityViewport) AppendReasoning ¶ added in v1.10.20
func (v *ActivityViewport) AppendReasoning(content string)
AppendReasoning adds a reasoning block. The warm colour applies only to its header; its indented body has the same neutral presentation as tool output. An open assistant-prose block is closed first so the reasoning starts a new block.
func (*ActivityViewport) AppendText ¶ added in v1.10.20
func (v *ActivityViewport) AppendText(content string)
AppendText adds an assistant-prose block to the same viewport. Streamed tokens coalesce into one block until FinishText or RemoveTextBlock closes it.
func (*ActivityViewport) AppendTool ¶ added in v1.10.20
func (v *ActivityViewport) AppendTool(call pub_models.Call, content string, maxRows int)
AppendTool adds a compacted tool block to the same viewport. An open assistant-prose or reasoning block is closed first so the tool starts a new block.
func (*ActivityViewport) Content ¶ added in v1.10.20
func (v *ActivityViewport) Content() string
Content returns the sanitized content of the open reasoning block, or "" when no reasoning block is open.
func (*ActivityViewport) DetachRenderedRegion ¶ added in v1.10.20
func (v *ActivityViewport) DetachRenderedRegion() int
DetachRenderedRegion transfers ownership of the rows already drawn to the caller. The retained activity history will render at the current cursor position on the next Render call.
func (*ActivityViewport) FinishReasoning ¶ added in v1.10.20
func (v *ActivityViewport) FinishReasoning()
FinishReasoning closes the current reasoning block. Its visible rows stay in the activity history, while the next reasoning event starts a new block.
func (*ActivityViewport) FinishText ¶ added in v1.10.20
func (v *ActivityViewport) FinishText()
FinishText closes the active assistant-prose block. Its visible rows stay in the activity history, while the next prose stream starts a new block.
func (*ActivityViewport) RemoveTextBlock ¶ added in v1.10.20
func (v *ActivityViewport) RemoveTextBlock() int
RemoveTextBlock removes the active assistant-prose block from the activity history and returns the number of rows removed. The final answer is removed this way so it prints below the window instead of remaining inside it. The active text block is the last retained block, so its rows are the cache tail.
func (*ActivityViewport) Render ¶ added in v1.10.20
func (v *ActivityViewport) Render(w io.Writer) error
Render redraws the viewport in its existing terminal region. A dirty viewport (after Resize or a partial write) emits one complete atomic frame: move up the previously drawn rows, clear down, and rewrite the full window. The diff path is used only for normal streaming appends: pure appends print below the previously drawn region without clearing, and a changed tail clears from the first changed row down. The whole update lands in one write, so the terminal never paints an intermediate blank frame. The cursor is left below the viewport so subsequent tool activity or answer text follows it. A partial or failed write leaves the viewport dirty; the next Render retries the full frame.
func (*ActivityViewport) Resize ¶ added in v1.10.20
func (v *ActivityViewport) Resize(width, terminalHeight int)
Resize applies new terminal dimensions to the viewport. It mutates state only: it stores the new width, computes the effective height as min(configured cap, terminal height), rewraps all retained blocks at the new width, invalidates the render bookkeeping, and marks the viewport dirty. It never writes to the writer and is a no-op when the supplied dimensions equal the current ones. Resize is invoked only from the serialized session loop, never from a signal callback; mutation and rendering stay on that loop, so no mutex is needed.
func (*ActivityViewport) Rows ¶ added in v1.10.20
func (v *ActivityViewport) Rows() []string
Rows returns the physical terminal rows currently visible in the viewport.
func (*ActivityViewport) TextBlockActive ¶ added in v1.10.20
func (v *ActivityViewport) TextBlockActive() bool
TextBlockActive reports whether an assistant-prose block is currently open.
type RollingOutputConfig ¶ added in v1.10.20
type RollingOutputConfig struct {
Enabled bool `json:"enabled"`
WindowCellHeight int `json:"windowCellHeight"`
}
RollingOutputConfig configures the shared rolling activity viewport that contains streamed reasoning and tool blocks.
type Theme ¶
type Theme struct {
Primary string `json:"primary"`
Secondary string `json:"secondary"`
Breadtext string `json:"breadtext"`
RoleSystem string `json:"roleSystem"`
RoleUser string `json:"roleUser"`
RoleTool string `json:"roleTool"`
RoleReasoning string `json:"roleReasoning"`
RoleOther string `json:"roleOther"`
NotificationBell bool `json:"notificationBell"`
TableItems int `json:"tableItems"`
ToolOutputRows int `json:"toolOutputRows"`
RollingOutput RollingOutputConfig `json:"rollingOutput"`
}
Theme holds ANSI color configuration for terminal output. Values are raw ANSI escape sequences (e.g. "\u001b[38;2;120;140;160m").
This file is loaded from <clai-config-dir>/theme.json on startup. If NO_COLOR is set truthy, all colorization should be disabled.
Keep this config stable; new fields should be appended with defaults. Users can customize their own theme.json.