kolu
Docs

awareness

kolu knows what your agents are doing.

kolu watches the foreground process of every terminal. When that process is a coding agent it recognizes — Claude Code, Codex, Grok Build, or OpenCode — it reads that agent’s own on-disk session to derive whether it’s thinking, running a tool, waiting on you, or done. That state paints the tile chrome, the dock row, and the minimap, so a glance across a workspace of twenty terminals tells you who needs you.

There is no agent registry to configure and no per-agent setup. Run a supported agent in any terminal and detection lights up on its own.

One state vocabulary

Every agent folds onto the same small set of states, drawn with the same glyphs everywhere kolu shows agent status. The shape carries the meaning, so the distinction survives a peripheral glance or reduced color sensitivity.

The five agent states and their indicators: thinking is a pulsing accent dot, tool use a pulsing amber dot, awaiting you a pulsing alert dot, working-in-background a spinning teal ring, and waiting a dim dot.ThinkinggeneratingTool userunning toolsAwaiting youblocked on inputWorkingbackground taskWaitingidle, your turn

The awaiting-you hue sits deliberately outside the warm/pending family, so an agent that needs you never reads as a build still churning. When an agent finishes in the background and you haven’t looked yet, its row also gains a small amber unread badge in the corner until you focus it.

How detection works

The pipeline is the same for every agent; only the last step — which on-disk store holds the session — differs.

Detection reads each terminal’s foreground process id, checks whether it’s a known agent CLI, reads that agent’s own session store (a JSONL transcript or a SQLite database), and derives the live state shown on the dock and tile.foregroundpid of the shellknown agent?claude · codex · …session storeJSONL · SQLitelive statedock · tile · minimap

kolu asks each terminal for its foreground process id (tcgetpgrp, POSIX — Linux and macOS alike), matches it against the known agents, and reads that agent’s session from disk — tailing a JSONL transcript, or querying a SQLite database and watching its write-ahead log. No /proc scraping and no argv inspection. Adding a new agent is one small adapter and one line of registration; nothing server-wide changes.

Claude Code

kolu finds a Claude session by checking whether ~/.claude/sessions/<foreground-pid>.json exists, then tails its JSONL transcript to derive state. The tile also shows the session’s title and a running token count.

State Indicator Meaning
Thinking pulsing accent dot an API call is in flight — Claude is generating
Tool use pulsing amber dot Claude is executing tools
Running in background spinning teal ring Claude ended its turn while a dynamic workflow it launched is still running — busy-waiting, not blocked on you
Awaiting input pulsing alert blocked on you — an AskUserQuestion prompt or a tool-permission gate
Waiting dim dot finished responding (or interrupted with Esc) and idle

The awaiting-input state is the subtle one: while a question or permission prompt is pending, the transcript doesn’t reveal it, so kolu reads the rendered screen for the framework’s prompt chrome and buckets it as awaiting you.

What it can’t detect:

  • Plan approval (ExitPlanMode) — its on-screen shape has no arrow-nav footer to anchor on yet; a deliberate follow-up.
  • Streaming progress — only final state transitions, not intermediate tokens.
  • Wrapped invocations — launched via a wrapper (e.g. script -q out.log claude), the foreground pid is the wrapper, so the session lookup misses.
  • Sub-agents — nested spawns aren’t tracked as separate sessions. (When the parent waits on a launched dynamic workflow, kolu does surface that workflow’s name and live sub-agent count.)

Codex

When the foreground process is codex, kolu queries Codex’s threads SQLite database (the highest-numbered ~/.codex/state_<N>.sqlite) for the most recently updated thread whose working directory matches the terminal, and tails that thread’s rollout JSONL for state and context-token count. Live updates ride a watch on the SQLite write-ahead log.

State Indicator Meaning
Thinking pulsing accent dot latest lifecycle event is task_started, no open tool call
Tool use spinning amber a function_call is open that isn’t an awaiting-user tool
Awaiting input pulsing alert every open call is an awaiting-user tool — request_user_input, request_permissions, or request_plugin_install
Waiting dim dot latest lifecycle event is task_complete

The context count is Codex’s own last_token_usage.input_tokens — the number its /status command shows — not a sum that would double-count cache re-reads.

What it can’t detect (yet):

  • Task-progress checklist — Codex has no user-visible todo list to read.
  • Column-schema changes — if an upstream release renames a depended-on column, detection returns no match; set KOLU_CODEX_DB to pin a known-good database while a fix ships.
  • Same-directory disambiguation — with multiple Codex threads in one directory, kolu picks the most recently updated.

Grok

When the foreground process is grok, kolu prefers the entry in ~/.grok/active_sessions.json whose pid matches the terminal; otherwise it takes the most recently updated session under ~/.grok/sessions/. State comes from tailing that session’s events.jsonlphase_changed for thinking / tool use / awaiting you, turn_ended for waiting. An open ask_user_question promotes to awaiting you even mid-tool, so a multiple-choice prompt never reads as “running tools”.

Resume uses grok -c (most recent in the directory) or grok --resume <uuid> (an exact conversation). Context tokens come from signals.json (contextTokensUsed); task progress stays null until Grok’s plan checklist is a stable first-class count. Transcript export reads chat_history.jsonl (unwraps Grok’s <user_query> harness so the HTML shows the human prompt).

OpenCode

When the foreground process is opencode, kolu queries OpenCode’s SQLite database directly at ~/.local/share/opencode/opencode.db for the most recently updated session whose directory matches the terminal. The database’s write-ahead log drives live updates.

State Indicator Meaning
Thinking pulsing accent dot latest assistant message has no time.completed
Tool use spinning amber a running part whose tool is neither question nor plan_exit
Awaiting input pulsing alert every running part is question or plan_exit — blocked on a reply
Waiting dim dot latest assistant message is completed with finish: "stop"

Todo progress comes free from a count over OpenCode’s todo table, and the tile shows the running token count OpenCode pre-sums itself.

What it can’t detect (yet):

  • Same-directory disambiguation — most recently updated session wins.
  • Non-default database location — set KOLU_OPENCODE_DB to override the path.

Adding an agent

Because detection is agent-agnostic, a new CLI doesn’t wait on kolu. Once you’ve run a known agent in any terminal it also surfaces in the command palette — as a launch option in the worktree-naming flow and in the Recent agents group. Teaching kolu to read a brand-new agent’s state is a single adapter that reads its session store and emits the same five states; everything downstream — the dock, the tile border, the minimap, the fleet view — lights up unchanged.