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

FieldTypeDefaultDescription
tracking.human_buffer_secsinteger300Seconds 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_daysinteger365Days to keep turn history before pruning

Projects

Each [[projects]] entry tracks one path:

FieldTypeRequiredDescription
pathstringyesExact path or glob pattern. Supports ~ and *
namestringnoDisplay 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]
FlagDescription
--allShow all-time stats
--project <path>Filter by project path
--agent <name>Filter by agent
--projectsPer-project breakdown
--historyTurn history
--graphASCII activity graph
--dailyDaily breakdown
--format text|json|csvOutput 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]
FlagDescription
--pruneDelete stale turns (open > 2h) and turns beyond retention period
--checkRun 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 hook entries from Claude Code's settings.json and Codex's hooks.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.
  • --purge also 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

AgentStatusGranularity
Claude Code✅ v1Per-prompt (UserPromptSubmit + Stop)
Codex✅ v1Per-prompt (UserPromptSubmit + Stop)
OpenCode✅ v1Per-prompt (user message.updated + session.idle)
Pi⚗️ experimentalPer agent run (before_agent_start + agent_end)

Adding a new agent

  1. Create src/agents/<name>/mod.rs and implement the Agent trait (including is_installed, so suivi init only targets machines that have the agent)
  2. Add hook template files under src/agents/<name>/hooks/
  3. Register it in all_agents() in src/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:

  1. Create src/agents/<name>/mod.rs
  2. Implement the Agent trait (id, display_name, detect, parse_payload, hook_templates)
  3. Add hook template files under src/agents/<name>/hooks/
  4. Register in all_agents() in src/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