Introduction
suivi tracks time spent working with AI coding agents across multiple projects.
It hooks into Claude Code, Codex, Pi, OpenCode, and other CLI-based agents to measure how much time you spend on each project — broken down by agent, model, and day. Shows both wall-clock time (attention) and agent time (machine effort).
How it works
suivi registers hooks in your agent's config. When you submit a prompt, suivi hook pre fires. When the agent finishes responding, suivi hook stop fires. The time between them — plus a configurable human buffer — is attributed to the project matching your current working directory.
Run multiple sessions in parallel and suivi handles it correctly: wall-clock time deduplicates overlapping sessions, agent time adds them up.
Install
Homebrew
brew install hefgi/tap/suivi
Cargo
cargo install suivi
Requires Rust 1.85+.
Pre-built binaries
Each release ships static binaries for macOS (Intel and Apple Silicon) and Linux (x86_64 and aarch64, musl — no glibc dependency):
# Example: Apple Silicon
curl -sSL https://github.com/hefgi/suivi/releases/download/v0.2.0/suivi-v0.2.0-aarch64-apple-darwin.tar.gz \
| tar -xz && mv suivi /usr/local/bin/
Browse all assets on the releases page.
Setup
After installing, run:
suivi init
This creates your config at ~/.config/suivi/config.toml, detects installed agents, and registers hooks automatically.
PATH requirements
suivi must be on the PATH that your shell and agent hooks use. On macOS, agent hooks run via /bin/sh which does not source ~/.zshrc or ~/.bashrc.
Homebrew install handles this automatically — the binary lands in /opt/homebrew/bin which is always on the default PATH.
cargo install places the binary in ~/.cargo/bin. You must ensure this is on your PATH:
# Add to ~/.zshrc or ~/.bash_profile
export PATH="$HOME/.cargo/bin:$PATH"
Then symlink to a system-wide location so hooks work:
ln -sf ~/.cargo/bin/suivi /opt/homebrew/bin/suivi # macOS (Apple Silicon)
# or
sudo ln -sf ~/.cargo/bin/suivi /usr/local/bin/suivi # Linux / macOS Intel
Quick start
# 1. Install
brew install hefgi/tap/suivi
# 2. Add your projects and register hooks
suivi init
# 3. Work normally — suivi tracks in the background
# 4. See where your time went
suivi stats
suivi stats --projects
suivi stats --graph
Configuration
suivi reads its configuration from ~/.config/suivi/config.toml
($XDG_CONFIG_HOME/suivi/config.toml if set).
Example
[tracking]
human_buffer_secs = 300 # seconds budgeted for reading/writing prompts (default: 300)
retention_days = 365 # how long to keep turn history (default: 365)
[[projects]]
path = "~/code/myapp"
name = "My App" # optional; defaults to the directory basename
[[projects]]
path = "~/work/client-*" # globs expand to one project per matched directory
Fields
| Field | Type | Default | Description |
|---|---|---|---|
tracking.human_buffer_secs | integer | 300 | Seconds of human time budgeted before and after each AI turn (writing the prompt, reading the response). Feeds the wall-clock padding and the effective-duration estimate |
tracking.retention_days | integer | 365 | Days to keep turn history before pruning |
Projects
Each [[projects]] entry tracks one path:
| Field | Type | Required | Description |
|---|---|---|---|
path | string | yes | Exact path or glob pattern. Supports ~ and * |
name | string | no | Display name shown in suivi stats. Defaults to the directory basename |
Path matching
suivi matches a turn to a project by finding the deepest tracked path that is an ancestor of the turn's working directory. If two projects both match, the more specific (longer) path wins.
Globs are re-expanded on every hook call — new directories matching a pattern are picked up automatically. Only directories count; stray files matching a glob are ignored.
Legacy format
Configs written by early versions (buffer_mins, [[projects]] paths = [...])
are still read transparently, but new configs should use the format above.
Creating the config
Run suivi init to create the config interactively.
Commands
suivi init
Initialize suivi: create the config file and install agent hooks.
suivi init
Interactive wizard that prompts for project paths to track, then installs hooks for all detected agents.
If the config already exists, the wizard exits early and directs you to suivi doctor.
suivi status
Show hook installation health and recent activity.
suivi status
Output:
Hooks
Claude Code Ok
Codex Missing
OpenCode Ok
Pi (experimental) Ok (experimental)
Recent activity
12 turns recorded in the last 7 days.
claude-code 10 turns
codex 2 turns
suivi stats
Show time analytics.
suivi stats [OPTIONS]
| Flag | Description |
|---|---|
--all | Show all-time stats |
--project <path> | Filter by project path |
--agent <name> | Filter by agent |
--projects | Per-project breakdown |
--history | Turn history |
--graph | ASCII activity graph |
--daily | Daily breakdown |
--format text|json|csv | Output format (default: text) |
suivi stats # summary
suivi stats --graph # daily graph — last 30 days
suivi stats --daily # day-by-day breakdown
suivi stats --history # recent turns (last 30 days by default)
suivi stats --projects # cross-project comparison table
suivi stats --project <path> # drill into one project
suivi stats --agent <name> # drill into one agent
suivi stats --all --format json # full JSON export
--projects output
Projects
Project Wall-clock Agent time Sessions
--------------------------------------------------------------------
tracker-code 1h 12m 2h 05m ×4
my-app 45m 58m ×1
(untracked) 12m 18m ×2
--graph output
Activity graph
2026-05-28 ████████████░░░░░░░░░░░░░░░░░░ 42m
2026-05-29 ██████████████████████░░░░░░░░ 1h 12m
2026-05-30 ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ < 1m
2026-06-01 ██████░░░░░░░░░░░░░░░░░░░░░░░░ 18m
2026-06-02 ████████████████████████████░░ 1h 32m
--daily output
Daily breakdown
Date Wall-clock Agent time Turns
--------------------------------------------------
2026-05-28 42m 58m 8
2026-05-29 1h 12m 2h 05m 14
2026-06-02 1h 32m 2h 48m 21
--history output
History
2026-06-02 09:14 claude-code 12m tracker-code claude-opus-4-5
2026-06-02 10:31 claude-code 8m tracker-code claude-opus-4-5
2026-06-02 14:05 codex 5m my-app o4-mini
--format json
suivi stats --all --format json
[
{
"window": "All time",
"turns": 42,
"wall_clock_secs": 7320.0,
"agent_secs": 14400.0
}
]
--format csv
suivi stats --all --format csv
window,turns,wall_clock_secs,agent_secs
All time,42,7320,14400
suivi doctor
Database maintenance.
suivi doctor [--prune] [--check]
| Flag | Description |
|---|---|
--prune | Delete stale turns (open > 2h) and turns beyond retention period |
--check | Run SQLite PRAGMA integrity_check |
Without flags, shows a summary of stale and beyond-retention turn counts.
suivi uninstall
Remove suivi's hooks from all agent configs — the inverse of suivi init.
suivi uninstall [--purge]
- Strips
suivi hookentries from Claude Code'ssettings.jsonand Codex'shooks.json, preserving any other hooks; deletes the Pi/OpenCode plugin files (only if they actually reference suivi). - Without
--purge, your config, database, and logs are left in place and their paths are printed. --purgealso deletes suivi's config, database, and log directories.
Run this before deleting the binary — otherwise every agent prompt keeps
invoking a suivi command that no longer exists.
suivi hook pre / stop
Internal commands called by agent hooks. Not intended for direct use.
suivi hook pre --agent <id>
suivi hook stop --agent <id>
Both read JSON from stdin and exit 0 always (errors are silent).
Supported agents
| Agent | Status | Granularity |
|---|---|---|
| Claude Code | ✅ v1 | Per-prompt (UserPromptSubmit + Stop) |
| Codex | ✅ v1 | Per-prompt (UserPromptSubmit + Stop) |
| OpenCode | ✅ v1 | Per-prompt (user message.updated + session.idle) |
| Pi | ⚗️ experimental | Per agent run (before_agent_start + agent_end) |
Adding a new agent
- Create
src/agents/<name>/mod.rsand implement theAgenttrait (includingis_installed, sosuivi initonly targets machines that have the agent) - Add hook template files under
src/agents/<name>/hooks/ - Register it in
all_agents()insrc/agents/mod.rs
The agent must provide a session_id in its hook payload — this is required for correct per-session tracking.
See Contributing for the full workflow.
How time is measured
Wall-clock time
Attention time: each completed turn becomes an interval
[started − buffer, ended + buffer]; overlapping intervals across all
sessions are merged and the non-overlapping ranges summed.
Five parallel 1-minute sessions = 1 minute wall-clock.
Agent time
Machine effort: the sum of each completed turn's real duration (prompt submitted → response finished).
Five parallel 1-minute sessions = 5 minutes of agent time — summing across parallel sessions is intentional, because it measures what the agents did rather than what you attended to.
Effective duration
Each turn also stores an engaged-time estimate (effective_duration_secs),
combining gap-corrected human think time with agent time:
if gap_to_next_turn < buffer × 2:
effective = gap + agent_duration
else:
effective = buffer + agent_duration + buffer
The default buffer is 5 minutes each side — time budgeted for writing the prompt and reading the response. If you re-prompt within 10 minutes (2 × buffer), the actual gap is used instead.
Effective duration is not currently shown in the headline views (wall-clock and agent time are), but it is stored and included in JSON/CSV exports.
Configure the buffer in ~/.config/suivi/config.toml:
[tracking]
human_buffer_secs = 300 # default: 5 minutes each side
Interrupted turns
If an agent run never produces a Stop event (you interrupted it, or it crashed), the next prompt in the same session closes the turn at that moment, charging the elapsed time.
Implementation detail
suivi hook stop derives the agent duration from the turn's own timestamps
(no agent sends a duration field) and writes the best-guess effective
duration immediately. When suivi hook pre fires for the next prompt in the
same session, it corrects the previous turn's effective duration if the gap
was shorter than 2 × buffer. No background process required.
Contributing
Issues and PRs are welcome. Check the open issues for ideas.
Adding a new agent
Adding support for a new agent takes ~30 lines:
- Create
src/agents/<name>/mod.rs - Implement the
Agenttrait (id,display_name,detect,parse_payload,hook_templates) - Add hook template files under
src/agents/<name>/hooks/ - Register in
all_agents()insrc/agents/mod.rs
The agent must provide a session_id in its hook payload — this is required for correct per-session tracking.
Dev workflow
cargo build
cargo test
cargo clippy -- -D warnings
cargo fmt