Documentation
¶
Overview ¶
Package probes hosts the status-line probe registry (model, cost, context, git, subagents, ...) consumed by the formatter.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // Line0Registry holds probes rendered on the first status line (row 0). // Order: email, project, quota. Line0Registry = []Probe{ &EmailProbe{}, &ProjectProbe{}, &QuotaProbe{}, } // Line1Registry holds probes rendered on the second status line (row 1). // Order: model (includes effort icon inline), git, ctx, cost, time. Line1Registry = []Probe{ &ModelProbe{}, &GitProbe{}, &CtxProbe{}, &CostProbe{}, &TimeProbe{}, } // Line2Registry is empty since Phase 6.9.e: the cache-aggregate row (line 2) // was removed in favour of per-row cache columns + TTL suffix in the table // (T-13). CacheProbe was deleted in Phase 7 (BL-33). Line2Registry = []Probe{} // SubagentRegistry holds the subagent status probe, rendered only in the // subagent status line code-path. SubagentRegistry = []Probe{ &SubagentProbe{}, } )
Probe registries define the display order of probes on each status-line row. Each registry is a fixed-order slice: position in the slice == left-to-right render position for that row.
Row assignment (from Phase 4 concept §A4):
Line0Registry — row 0 of the multiline output: email, project name, quota indicator Line1Registry — row 1 of the multiline output: model, effort, git, ctx, cost, time Line2Registry — row 2 of the multiline output: cache aggregate row (CacheProbe) SubagentRegistry — subagent status probes (one per active subagent)
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// Per-widget toggles (Phase 6 — from config.Widgets). Default false; set
// via ToProbesConfig to mirror the user's config. Phase 4-5 callers that
// do not populate these fields should use Default()-sourced Config to get
// all-true behaviour.
ModelEnabled bool
EffortEnabled bool
CostEnabled bool
ProjectEnabled bool
EmailEnabled bool
TimeEnabled bool
CtxEnabled bool
CacheEnabled bool
QuotaEnabled bool
GitEnabled bool
SubagentEnabled bool
// TableRows is the maximum number of per-turn rows shown in the subagent
// table. Set from config.General.TableRows via ToProbesConfig. When 0 the
// assembler applies its own default (10). Capped at 40 by SetTableRows.
TableRows int
// Per-probe values (Phase 6 — from config.Probes).
// Email is the override address for the Email probe. When empty the probe
// reads the address from the CC session JSONL.
Email string
// Threshold values (Phase 6 — from config.Thresholds).
CostBudgetUSD float64
CtxNoticeRatio float64
CtxWarnRatio float64
CtxCriticalRatio float64
Quota5hNoticeRatio float64
Quota5hWarnRatio float64
Quota5hCriticalRatio float64
Quota7dNoticeRatio float64
Quota7dWarnRatio float64
Quota7dCriticalRatio float64
OrchTTLMinutes int
SubagentGapMinutes int
// IsSubagentContext indicates that the probe is being rendered in a subagent
// display context (not the main orchestrator line). When true, TTL is
// suppressed (T-23). This is a runtime flag, not a config threshold —
// SubagentGapMinutes is the detection threshold (C3 fix).
IsSubagentContext bool
}
Config carries per-invocation configuration flags. It is a lightweight struct (not the full internal/config.Config) scoped to what probes actually need. All XEnabled fields default to false; callers must populate from config.ToProbesConfig.
type CostProbe ¶
type CostProbe struct{}
CostProbe renders the total session cost in USD. Always visible (even at $0.00). Priority P2.
func (*CostProbe) Render ¶
Render formats the header cost from the official Claude Code meter (d.Stdin.Cost.TotalCostUSD), clipped to this session via the /clear baseline, not our table-driven estimate (Phase 7.46).
Rationale: the header must show the single most authoritative figure, even at a small lag, rather than our reconstructed estimate (which carries a known ~10% reasoning-billing gap). But TotalCostUSD is cumulative across /clear, so we subtract st.BaselineCost — the ccTotal captured on the first Reconcile of this session_id — to show only the current session's spend (a /clear starts a new session_id with a fresh baseline). On a session begun from zero baseline=0, so this equals the raw total. The per-turn table keeps our estimate (labelled "~cost"); a small header↔table mismatch is expected. This does not reintroduce the 7.45 dancing-number bug — that came from redistributing the lagging ccTotal across turns, not from a monotonic session-clipped header.
Budget signal: when c.CostBudgetUSD > 0 and the session cost has reached or exceeded it, the figure is painted bold_red (config-honesty, Phase 7.47). A budget of 0 (the default) disables the check and the figure stays plain.
Full: "cost: $<value>" Compact/Minimal: "$<value>"
type CtxProbe ¶
type CtxProbe struct{}
CtxProbe renders the context window usage as a progress bar.
Used tokens = sum of three input-side keys from CurrentUsage: cache_read_input_tokens + cache_creation_input_tokens + input_tokens. Percentage = used / Size * 100 (clamped to [0, 100]).
Display (AnsiEnabled=false / legacy):
Full: "ctx <bar10> <usedK>/<sizeK> (<pct>%)" Compact: "<bar5> <usedK>/<sizeK>" (no %) Minimal: "<usedK>/<sizeK>" (no bar, no %)
Display (AnsiEnabled=true / T-22):
Full: "ctx <bar10> <coloured-usedK>/<sizeK>" (no %; usedK colour by fill) Compact: "<bar5> <usedK>/<sizeK>" (unchanged) Minimal: "<usedK>/<sizeK>" (no colour markers)
Colour rules for usedK (AnsiEnabled=true, > 95% → bold_red; < 50% → green; otherwise marker from ProgressBarColor threshold):
> 95% → {{color:bold_red}}
< 50% → {{color:green}}
50–69% → {{color:yellow}}
70–89% → {{color:orange}}
≥ 90% → {{color:red}}
type Data ¶
type Data struct {
Stdin stdin.Payload // parsed stdin from CC hook
Session *parser.SessionStats // JSONL aggregates; nil = empty session
Subagents []parser.SubagentStats
Git *parser.GitStatus // nil = not in a git repo or detection failed
Now time.Time
TerminalCols int // 0 = detect failed; probes should fall back to 80
SessionID string // CC session id; "" disables hint state persistence (Phase 4.4)
// ExtraCacheEvents lets main inject synthetic alerts (e.g. config load
// errors) that are not derived from session/subagent JSONL data. Phase 6.
ExtraCacheEvents []parser.CacheEvent
// Phase 6.8.a: delta-based cost fields.
// SessionTotal is the cost incurred in this session (ccTotal − BaselineCost).
// Populated by main after cost.Reconcile; zero when state not yet loaded.
SessionTotal float64
// Phase 6.9.a: session duration delta.
// SessionDurMS is TotalAPIDurationMS − BaselineDurMS (resets on /clear).
// Populated by main via cost.SessionDuration; zero when state not yet loaded.
SessionDurMS int64
// LastRequestCost is the cost of the most recent prompt group
// (ccTotal − PromptCost[curGroupID]). Zero when not yet computed.
LastRequestCost float64
// PerTurnCostFn returns the finalized per-turn USD cost for the given UUID.
// Returns (0, false) when the turn has no recorded cost yet.
// Set by main from state.Session.PerTurnCost via cost.PerTurn.
// nil when state not available (graceful degradation: render "—").
PerTurnCostFn func(uuid string) (float64, bool)
// State is the reconciled per-session state. Used by perTurnTable to pass
// to renderer.RenderUnified for per-turn cost column (C1). nil means state
// not available; renderer degrades gracefully (shows "—" for all turns).
State *state.Session
// CommitBadgeCount is the "✓ N committed" badge count to render this refresh
// (Phase 6.95.a). Zero means no badge. Set by main via state.CommitBadgeTick;
// GitProbe renders it (green) in Full/Compact, never in Minimal.
CommitBadgeCount int
// ExtraActive reports that paid extra-usage is in effect (a rate-limit window
// at ≥100% AND hasExtraUsageEnabled). Set by main (Phase 6.95.h) via
// state.ExtraUsageTick. QuotaProbe renders a red "+$X extra usage" block after
// the triggering window's reset timer.
ExtraActive bool
// ExtraUSD is the estimated overage in USD accrued since a rate-limit window
// crossed 100% (SessionTotal − OverageBaseline). The extra block is shown only
// when ExtraActive && ExtraUSD ≥ $0.01.
ExtraUSD float64
// HintStart is the rotating-hint starting index for this session, read by main
// from quota.HintStart on the first render of a new session. The hint widget
// uses it only on that first render; subsequent renders follow per-session
// HintRotation. Account-wide so the opening hint shifts by one each session.
HintStart int
// UpdateHint is the pre-formatted "update available" hint (#7c, Phase 7.46
// Wave B / BL-36), or "" when the running version is current or unknown. main
// builds it from the binary version and the cached price file's latest_version
// via hint.UpdateText; the assembler forwards it to the hint widget.
UpdateHint string
}
Data is the common render snapshot assembled once per invocation and passed to every probe. Populated by the parser (Phase 3) and stdin decoder (Phase 4) before any probe runs.
type EffortProbe ¶
type EffortProbe struct{}
EffortProbe renders the effort level as a Unicode circle icon. All three display levels render the same icon (effort is P0; icon is never dropped).
func (*EffortProbe) MinWidth ¶
func (p *EffortProbe) MinWidth() int
func (*EffortProbe) Name ¶
func (p *EffortProbe) Name() string
func (*EffortProbe) Priority ¶
func (p *EffortProbe) Priority() int
type EmailProbe ¶
type EmailProbe struct{}
EmailProbe renders the user's email address. Zero-state: all data comes from Config passed per-call to Visible and Render.
Visible returns false when EmailEnabled=false or Email is empty.
Display:
Full: full email unchanged. Compact: middle-truncate to 16 runes. Minimal: middle-truncate to 12 runes.
func (*EmailProbe) MinWidth ¶
func (p *EmailProbe) MinWidth() int
func (*EmailProbe) Name ¶
func (p *EmailProbe) Name() string
func (*EmailProbe) Priority ¶
func (p *EmailProbe) Priority() int
type GitProbe ¶
type GitProbe struct{}
GitProbe renders the current git branch and modified-file count. Visible only when d.Git is non-nil (i.e., we are inside a git repository).
Display:
Full/Compact: "⎇ <branch>" (or "⎇ -:<branch>" if Worktree != "") + " ⚠N" if N > 0 Minimal: middle-truncated branch with "…", at least 8 runes in the result
func (*GitProbe) Render ¶
Render formats the git branch status.
Colour markers applied per B3 §5:
branch segment "⎇ <name>" → {{color:cyan}}…{{reset}}
warning segment "⚠N" (when ModifiedCount > 0) → {{color:yellow}}…{{reset}}
Full: "⎇ <branch>" (+ " ⚠N" if N>0), no truncation. Compact: "⎇ <branch12>" (+ " ⚠N" if N>0), branch middle-truncated to 12. Minimal: "⎇ <branch8>", no ⚠N.
type Level ¶
type Level int
Level controls how aggressively a probe compresses its output to fit narrow terminals. Probes must handle all three levels independently.
type ModelProbe ¶
type ModelProbe struct{}
ModelProbe renders the canonical short model name (e.g. "opus-4-7"). It is a P0 probe: always visible when Model.ID is non-empty.
func (*ModelProbe) MinWidth ¶
func (p *ModelProbe) MinWidth() int
func (*ModelProbe) Name ¶
func (p *ModelProbe) Name() string
func (*ModelProbe) Priority ¶
func (p *ModelProbe) Priority() int
func (*ModelProbe) Render ¶
Render returns the canonical short model name, optionally followed by the effort icon (e.g. "sonnet-4-6 ◑"). Effort is appended without a separator so it reads as one visual unit.
When AnsiEnabled, the model name is wrapped in effortColorMarker(effort.Level) instead of the former {{bold}} marker, so the model name shares the same colour semantics as the effort glyph (T-14). An empty marker means the name is plain. All display levels return the same value.
type Probe ¶
type Probe interface {
// Name returns a unique identifier used in logs and registry comments.
Name() string
// Priority returns the display priority: 0 (always visible) to 4 (drop first).
// Matches §A4 priority table in the concept doc.
Priority() int
// Visible reports whether this probe has meaningful data to show.
// Returning false causes the renderer to skip the probe entirely.
Visible(d Data, c Config) bool
// Render produces the display string for the given level.
// Output must be plain text with no embedded ANSI codes; the renderer
// wraps it with Theme colours after calling Render.
Render(d Data, c Config, t renderer.Theme, level Level) string
// MinWidth returns the minimum character width of the Minimal-level output.
// Used by the renderer to decide which probes to drop when space is tight.
MinWidth() int
}
Probe is the single interface every status-line block must implement. Probes are stateless: all state comes from Data and Config.
Render parameter order: (d Data, c Config, t renderer.Theme, level Level) — amendment 2026-05-18, from 3-param to 4-param to pass Config explicitly.
type ProjectProbe ¶
type ProjectProbe struct{}
ProjectProbe renders the basename of the current working directory. Falls back to "?" when Cwd is empty or the basename is empty (e.g. root "/"). Always visible. Priority P2.
func (*ProjectProbe) MinWidth ¶
func (p *ProjectProbe) MinWidth() int
func (*ProjectProbe) Name ¶
func (p *ProjectProbe) Name() string
func (*ProjectProbe) Priority ¶
func (p *ProjectProbe) Priority() int
type QuotaProbe ¶
type QuotaProbe struct{}
QuotaProbe renders quota usage for the 5-hour and 7-day rate-limit windows. Data is sourced from quota.Freshest() (cross-session persistent file) with d.Stdin.RateLimits as a fallback when no snapshot has been stored yet.
Visible only when Config.QuotaEnabled=true AND either quota.Freshest() returns a snapshot OR d.Stdin.RateLimits != nil.
func (*QuotaProbe) MinWidth ¶
func (p *QuotaProbe) MinWidth() int
func (*QuotaProbe) Name ¶
func (p *QuotaProbe) Name() string
func (*QuotaProbe) Priority ¶
func (p *QuotaProbe) Priority() int
func (*QuotaProbe) Render ¶
Render formats the quota blocks.
Data source priority:
- quota.Freshest() — cross-session persistent snapshot (account-wide freshest).
- d.Stdin.RateLimits — current payload fallback when no snapshot exists.
When the snapshot is older than staleDuration (10m), an "as of Xm ago" suffix is appended to signal freshness decay.
Colour markers applied per B3 §5:
- progress bars wrapped in ProgressBarColor(pct, th) + bar + Reset
- ↻ reset countdown wrapped in {{color:yellow}} when time-to-reset < 30m
- percentage value wrapped in {{color:bold_red}} when pct > 95
Display levels:
Full: "5h: <bar10_5h> <reset5h> · 7d: <bar10_7d> <reset7d>" [+ age suffix] Compact: "<bar5_5h> <reset5h> · <bar5_7d> <reset7d>" [+ age suffix] Minimal: "<pct5h>% · <pct7d>%" [+ age suffix]
type SubagentProbe ¶
type SubagentProbe struct{}
SubagentProbe renders one line per active subagent task. It resolves each stdin Task to a SubagentStats entry by matching task.ID to SubagentStats.AgentID. Unmatched tasks produce a fallback line with "?" placeholders and emit slog.Warn.
Visible only when len(d.Stdin.Tasks) > 0.
Display (fields separated by " · "):
Full: "<name> · <model> · <ctx>/<max> · $<cost> · ⏱<time> · <last_tool>" (6 fields) Compact: "<name> · <model> · <ctx>/<max> · $<cost> · ⏱<time>" (5 fields, last_tool dropped) Minimal: "<name> · <model> · <ctx>/<max>" (3 fields)
func (*SubagentProbe) MinWidth ¶
func (p *SubagentProbe) MinWidth() int
func (*SubagentProbe) Name ¶
func (p *SubagentProbe) Name() string
func (*SubagentProbe) Priority ¶
func (p *SubagentProbe) Priority() int
type TimeProbe ¶
type TimeProbe struct{}
TimeProbe renders the total API wall-clock duration in MM:SS format. Priority P1. Returns MM:SS at all levels including Minimal.
func (*TimeProbe) Render ¶
Render formats the elapsed time relative to the session baseline (phase 6.9.a). Uses d.SessionDurMS (TotalAPIDurationMS − BaselineDurMS) so the counter resets on /clear together with the cost counter.
The fallback to raw TotalAPIDurationMS keys on d.State == nil ("state not yet loaded") rather than on durMS <= 0. After /clear the state IS loaded and SessionDurMS is legitimately 0 (baseline == current) — falling back there would resurrect the stale cumulative total (bug: cost reset to $0.00 but time kept 238:17). Negative deltas (transient ccTotal dip) are clamped to 0.
Full: "time: MM:SS" Compact: "MM:SS" Minimal: "MM:SS"