Documentation
¶
Overview ¶
Package agent detects, without any user configuration, whether a process running inside a session is an AI coding agent CLI (claude, codex, opencode, ...) and what it is currently doing: idle, working, or waiting on the user. It never talks to the agent — only to the session's terminal emulator (pkg/screen) — so a detector that gets it wrong degrades a gutter marker, never the session itself.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Detector ¶
type Detector struct {
// contains filtered or unexported fields
}
Detector holds every loaded manifest, keyed by process name, and turns a session's foreground process plus its screen snapshot into a State. It does no I/O of its own — LoadManifests builds it once at startup, and pkg/session calls Evaluate from its drain goroutine, throttled.
func LoadManifests ¶
LoadManifests builds a Detector from the manifests shipped in the binary, overridden or extended by whatever is in overrideDir (typically config.AgentsDir()). A file in overrideDir whose base name matches a built-in manifest (e.g. "claude.yml") replaces it outright — manifests are ordered rule lists, so merging them field by field would be ambiguous about which side's ordering wins; a different base name adds a manifest for another agent. A missing overrideDir is not an error: the built-ins alone are what makes this phase work with zero configuration.
A manifest that fails to parse is skipped, not fatal — one broken file degrades detection for that one agent, never the whole feature. Every skip is returned as a warning for the caller to print to stderr, the same contract pkg/config's Load/Validate use.
func NewDetector ¶
NewDetector builds a Detector from an already-loaded set of manifests, one per process name. Exported mainly for tests; LoadManifests is what pkg/session actually calls.
func (*Detector) Evaluate ¶
Evaluate returns the agent state for a session whose foreground process is processName, given the plain-text tail of its visible screen and its terminal title. StateNone means processName matches no known manifest — this is not an agent session, and the caller must not render a marker for it. A manifest match with no rule firing is StateIdle.
func (*Detector) Name ¶ added in v1.12.0
Name reports the manifest's declared name for processName (e.g. "claude", "codex", "opencode") — false if no manifest matches. The same lookup Evaluate does internally, split out so a caller only interested in which agent this is (pkg/gui's dashboard) does not need a screen tail or title to ask for it.
type Manifest ¶
type Manifest struct {
// Process is the foreground process name (as reported by the OS, e.g.
// Linux's /proc/<pid>/comm) that selects this manifest. Matched
// case-insensitively.
Process string
Rules []Rule
}
Manifest declares how to recognize one agent CLI (by its foreground process name) and how to read its state off the screen it draws — no Go code, so a UI change in the agent is a manifest edit, not a lazyshell release.
type Rule ¶
type Rule struct {
State State
// ScreenPattern, if set, must match the plain-text tail of the visible
// screen. TitlePattern, if set, must match the terminal title (OSC 0/2).
// At least one of the two is set; when both are, the rule only matches
// when both do.
ScreenPattern *regexp.Regexp
TitlePattern *regexp.Regexp
}
Rule is one line of a manifest: if its pattern(s) match, the session is in State. Rules are evaluated in file order and the first match wins, which is what lets a manifest author put its "blocked" rules first — see ValidateBlockedFirst.
type State ¶
type State int
State is the four-value classification herdr popularized, and that this package's design report (RAPPORT_ANALYSE_INTEGRATION_AGENTS_IA.md) settled on: it is the split tmux does not make, between "produced output" and "is waiting for you".
const ( // StateNone means the session's foreground process does not match any // known agent manifest — it is not an agent session at all, and the // gutter must not show a marker for it. Distinct from StateIdle, which // means "this is an agent, and it is not doing anything right now". StateNone State = iota StateIdle StateWorking StateBlocked StateDone )
func ParseState ¶
ParseState is String's inverse: manifest.go uses it to decode a rule's "state:" key, and pkg/hook uses it to validate a line received over an agent's hook socket — the exact same four names in both places, since a hook event and a manifest rule express the same four-value state. StateNone has no valid spelling: neither a manifest rule nor a hook event can declare "this is not an agent session" — that is the zero value for "nothing said otherwise", never something to say out loud.