Measure kairos — the time you are genuinely present — not chronos, the wall-clock time the machine spends grinding.
In the AI era, a freelancer or employee's day is no longer one app in focus. It's interleaved — a Claude Code session here, a Cursor turn there, reviewing an agent's pull request, prompting, waiting, re-prompting. Conventional time trackers can't see this: they key off the frontmost window or a manual start/stop, so they log either machine time (the agent grinding) or nothing useful, and can never answer "how long was I actually engaged with each agent this week?"
Kairos is a local-first daemon that records the time you are genuinely engaged
with your work and attributes each block to the activity driving it — a Claude Code
session, a client meeting, an ad-hoc task. It separates human effort (reading,
thinking, prompting) from AI execution time, and attributes that human time per
agent. Today it tracks Claude Code out of the box; the client model means any agent
is a small kairos-<agent> hook away.
- Per-agent human-time attribution — the headline. Track genuine engagement with each AI agent separately, not the wall-clock the agent ran for.
- Submit-anchored AI attribution — for an AI session, the active window is
[prompt | agent-stops, you-submit]: your thinking/reading time counts, the agent's autonomous execution does not. - Manual activities & meetings — start/stop from the menu bar; explicit-time activities are AFK-immune (a meeting doesn't fragment when you go idle).
- Any terminal command — launch it as
kairos <command>and Kairos records when that terminal is in focus, so editors,ssh, REPLs, and other interactive CLI work are tracked too — not just AI sessions. - Dashboard — a per-source timeline (each agent a translucent area, total overlaid), a paged raw-segments table, and a client → project summary tree.
- Local-first & private — everything stays on disk (SQLite). No cloud, no account, no telemetry. Append-only events guarded by immutability triggers.
- Zero special permissions — attribution is intent-based (which session you submit to / which activity you start), so Kairos never reads window titles and needs no Accessibility or Automation access.
- Native, not Electron — a Swift menu-bar app with a small Rust core; even the
Claude Code plugin is a single native binary (no Node runtime, no shelling out to
jq/python). The app is ~3 MB on disk and ~45 MB resident at idle (per Activity Monitor); the plugin adds nothing resident — it runs only on hook events and exits in milliseconds.
- Download Kairos.app from the latest Release.
- Drag it into Applications and launch it. A Kairos icon appears in the menu bar.
- (Optional) Enable Launch at login from the menu-bar menu to autostart.
No build step, no toolchain — the Release ships a signed, ready-to-run app.
From the menu bar → Configure → General → Command-Line Tool → Install. This
symlinks kairos onto your PATH (one admin prompt), giving you the shell interface:
kairos --help # clients, projects, activities, export, …The CLI also wraps any terminal command — launching it as kairos <command> lets
Kairos track when that terminal is in focus, so its active time is recorded:
kairos vim main.rs # any command you actively work in — focus/blur is captured
kairos ssh prod(See Timesheets to export attributed time.)
Track AI coding sessions automatically. In the app, open Configure → Plugins → Claude Code and click Register (this makes Kairos's bundled plugin known to Claude Code). Then install it — pick a scope:
claude plugin install kairos-claude-code@kairos --scope user # or: project | localThe plugin ships inside the app (a native binary, no download); Claude Code copies it
into its own cache on install, and you manage it with claude plugin update / uninstall.
Each session shows up as a project (its folder name); tag that project's client once in
Configure… and it applies retroactively.
The plugin's job is to split each session into AI-execution time (the agent running) vs human time (you reading, thinking, prompting), using the agent's submit/stop markers. On its own that split is coarse: during the human windows it can't tell whether you were focused on this session or had switched to another window. For precise attribution, launch Claude through the wrapper so the terminal's focus on the session is recorded too:
kairos claude # pairs the plugin's AI/Human split with real focus on this session
kairos --title "Refactor auth" claude # name the activity; shown in the menu/statuslineNow, within the human window, only the time your terminal was genuinely on that
session counts as interaction with it. --title (optional, composes with
--project) names the activity instead of falling back to the project name.
Each session's activity also carries its Claude session id as an external_id, so a
timesheet can pinpoint exactly which session a block of time belonged to — see
Timesheets.
The plugin also ships a status-line command: the current session renders live in
Claude Code's status bar — Activity: <name> [Focused|Gracing|Idle] with total and
today.
Point the statusLine at the plugin binary (inside the app bundle) in your
.claude/settings.json:
"statusLine": {
"type": "command",
"command": "/Applications/Kairos.app/Contents/Resources/plugins/claude-code/bin/kairos-claude-code statusline",
"refreshInterval": 15
}Kairos is local-first: all data lives in a SQLite file on your machine
(~/Library/Application Support/Kairos/kairos.db). There is no cloud, no account, and
no telemetry — nothing leaves your Mac. It needs no Accessibility or Automation
permissions: because attribution is intent-based (which session you submit to, which
activity you start), Kairos never reads window titles or inspects other apps.
M1 + M2: the resident menu-bar daemon, the line-JSON protocol, explicit-bounds and
AI submit-anchored attribution, the kairos CLI, a SwiftUI config window, the Claude
Code plugin, and the Dashboard. Snapshots and the reference Python summarizer are next.
Platform: macOS today; the data plane (the Rust reducer + the web dashboard) is built to port, with a Windows host on the roadmap. See roadmap.
A lean resident daemon samples idle and ingests events from clients over a local socket
into an append-only SQLite log — the sole source of truth. Segments (attributed
active-time blocks) are computed on demand, never stored. Clients are external
processes (the Claude Code plugin, the kairos CLI, anything you write); consumers read
segments out via the SDK to render timesheets. See
Architecture for the full design.
Attributed time is read out over the same socket the daemon serves — segments are computed on demand from the event log, never stored, so a timesheet is always live and exact.
Quick — kairos export:
kairos export --from 0 --to 9999999999 | jq . # client-grouped, attributed segmentsRicher — the Python SDK (sdk/python, dependency-free) speaks the line-JSON protocol
directly. A single segments.get round-trip returns every segment in the range (no
pagination), each carrying the afk/pause-subtracted human seconds and a joined activity:
from kairos_sdk import Kairos
kairos = Kairos() # ~/.kairos/daemon.sock
for s in kairos.segments(from_ts, to_ts, client=1):
print(s.seconds, s.activity.project, s.activity.external_id)Each activity carries an external_id — for Claude Code sessions, the session id —
so a timesheet can pinpoint exactly which session a block of time belonged to (and, via
the activity's transcript_path, what was being done). Any language works: open the
socket, write one segments.get request line, read one response line — see
docs/05-protocol.md.
- Overview & principles
- Architecture
- Data model
- Attribution (the core)
- Protocol
- Daemon design
- Clients
- Consumers & summarizer
- Roadmap
Building from source, the dev/release isolation, the dashboard HMR loop, and the release process are in CONTRIBUTING.md.
MIT — © Flow Jiang.