Track · Analyze · Improve
Local-first analytics for AI coding assistants — one machine, or a whole team
Install → · Team Mode → · Security model → · CLI →
Every screenshot and recording in this README is produced from a synthetic demo fleet (
packages/server/scripts/seed-demo.ts) — real numbers, fictional names. See Reproducing the media.
agentistics reads what your AI coding assistants already write to disk and turns it into tokens, costs, sessions and activity you can actually reason about. Nothing is uploaded: on a single machine it never opens an outbound connection at all, and in Team Mode a machine sends computed metrics only — never chat.
It tracks six harnesses side by side:
| Harness | Read from | Tokens & cost | Agent metrics | Notes |
|---|---|---|---|---|
| Claude Code | ~/.claude/ | ✅ | ✅ | The deepest source — agent invocations, git line counts, workflow runs |
| Codex CLI | ~/.codex/sessions/ | ✅ | — | |
| Gemini CLI | ~/.gemini/tmp/ | — | — | Local files carry no token data |
| GitHub Copilot CLI | ~/.copilot/session-state/ | ✅ | — | MCP tool calls counted |
Antigravity (agy) | ~/.gemini/antigravity-cli/ | ✅ | — | Decoded from its own protobuf blobs; edit deltas for lines changed |
| Kimi Code | ~/.kimi-code/sessions/ | ✅ | — | Routes to other providers; priced by the routed model |
A metric a harness genuinely cannot produce renders as N/A, never as a confident 0 —
HARNESS_CAPABILITIES in @agentistics/core is the single source of truth for which is which.
curl -fsSL https://agentop.openvibes.tech/cli | bashThen just run it:
agentopBare agentop on a terminal opens the control center — one full-screen application in the
terminal's alternate buffer, so it adds nothing to your scrollback.
Its tabs are Services (start / stop / restart this machine, a central, or the Docker machine;
connect to or leave a central; enable a boot service), Setup, Logs, Commands, Help
and Contribute. Screens change with ←/→, panes with tab, and the footer always names the
keys that work in the current focus.
Open the dashboard at http://localhost:47292 (the API + MCP endpoint stays on 47291).
Download the latest .msi or .exe from the Releases page.
On first launch agentistics detects your Claude Code data path automatically (Windows native or WSL).
SmartScreen warning? "More info → Run anyway". The binary is not code-signed yet.
git clone https://github.com/blpsoares/agentistics.git
cd agentistics && bun install
bun run dev # API (47291) + UI dev server (47292)| Script | What it does |
|---|---|
bun run dev | API + Vite dev server in parallel |
bun run watch | OTel daemon only |
bun run watch:cli | Terminal TUI |
bun test | Unit tests |
bun run build:binary | Full build → release/agentop |
agentop tui is a live dashboard that needs no browser — Overview, Projects, Sessions, Costs and
Harnesses, switched with the digits or tab.
| Page | Route | What it answers |
|---|---|---|
| Home | / | KPIs, activity over time, heatmap, hourly usage, highlights |
| Costs | /costs | Spend by model and date, budget and forecast, cache efficiency |
| Projects | /projects | Per-project breakdown and comparison |
| Repositories | /repositories | Grouped by git remote, so one repo unifies across people, paths and machines |
| Actions | /repositories/actions | Sessions produced by CI runners |
| Tags | /tags | Saved, named groupings — "what did Client X cost this month" |
| Tools | /tools | Tool-call ranking and token attribution |
| Compare | /compare | Every harness side by side |
| Custom | /custom | Drag-and-drop layout builder |
Metrics group by normalized git remote (host/org/repo), independent of where the repo is
checked out or which machine produced the work. A repo's detail page carries Overview, Members,
Sessions, an Actions tab when it has CI runs, and a Dynamic Workflows tab that renders each
multi-agent Workflow run as a phase-by-phase timeline.
A tag is a saved grouping of sources — repositories, projects, machines, teams or accounts — optionally pinned to a date window. Tag responses are aggregate-only: counts and sums, never session rows or transcripts, with any key the viewer cannot see collapsed into an "other" bucket.
The whole dashboard is responsive and installs as a PWA.
One machine runs as a central and aggregates usage from many members. Members push computed metrics only — raw chat is never stored centrally (it is fetched on demand over a reverse WebSocket, from the machine that holds it).
Every machine has a role: solo (local only, the default), central (the aggregator, a Docker service on port 48080), or member. A machine may be a member of several centrals at once, with different sharing rules for each.
# host a central
agentop central init && agentop central up
# join one
agentop member connect --endpoint http://<central-host>:48080 --token <token>A central is not a shared password. First boot prints a one-time setup token and asks you to create the owner account; everyone else is invited. Accounts carry argon2id passwords, optional TOTP two-factor, and a role. A team is a scope key, not a label — being in a team is seeing it.
Step-up authentication gates escalation, not paperwork: editing an account, deleting a team and changing a password ask for a second proof. Enrolling a machine, a token or a repository does not — a prompt people meet daily is a prompt they clear without reading.
Each connection carries its own rules across two dimensions (repositories and projects) in one of two modes: denylist (share everything except these) or allowlist (share only these). The rules never travel: the central is told nothing about them beyond a per-dimension count.
When several of your machines share one account, they tell each other what they withhold over a sealed machine-to-machine channel (X25519 → HKDF → AES-256-GCM, sender and recipient bound into the authenticated header, keys pinned on first sight and every new pin announced). That is what makes two things possible:
- A warning at the point of decision: before you start sharing a repository, the picker names the sibling machines that withhold it. It warns, never blocks — and it always says that an absent warning is not proof, because a machine knows only what its siblings announced.
- A proposal: a sibling can offer its rules, and applying one may only ever narrow what this machine shares. Never replace, never widen.
- Presence is WebSocket-authoritative — online while the socket is live, offline within ~8s.
- Auto-reconciliation: if the central's database is wiped, a token is rotated or an endpoint changes, a member notices and re-pushes its full history. A revoked machine resets itself to solo.
- GitHub Actions: an ephemeral runner pushes its metrics with
agentop ci-push, authenticated by keyless GitHub OIDC against a registered-repos allowlist. The central stamps the repository itself, so a runner cannot mis-report which repo it ran for. See docs/github-actions.md.
→ docs/architecture.md · docs/DEPLOY.md · docs/security.md
A central can be published behind a tunnel. AGENTISTICS_EXPOSURE selects a profile —
local | lan | public — and public permanently revokes every route that touches the host
(shell, local chat, raw transcripts, MCP admin) and requires a second factor of every owner. The
profile is the only thing that decides a capability; no opt-in re-enables host power on public.
agentop doctor --exposed # run this BEFORE opening a tunnelA check that could not be verified reports fail, never a reassuring pass.
→ docs/exposure.md · SECURITY.md
| Command | Purpose |
|---|---|
(bare) / start | The control center |
setup | First-run wizard (solo / central / member) |
server | Web + API + MCP + daemon. --central runs a central natively (no Docker), --bg detaches |
restart | Bounce a mode; --rebuild rebuilds first (a full, cacheless rebuild — --cache opts out) |
status | Services + health |
tui | The live terminal dashboard |
watch | The OTel daemon only |
central | up / init / down / logs / status / restart / pull |
member | connect / leave / status / list |
ci-push | One-shot push of a CI runner's metrics |
autostart | Start a mode with the system (systemd user service) |
doctor | Exposure preflight; --exposed checks against the strict public bar |
setup-token | Reissue the one-time owner setup token |
reset-password | The way back in for a locked-out last owner |
upgrade · check-update | Update, or print a notice when one is due |
→ Full reference:docs/cli.md
Costs are computed per session from a pricing table with three layered sources, merged in order of trust: the built-in table (compiled in — the floor), the LiteLLM community dataset, and the vendors' own pages. A source that fails or returns junk costs freshness, never the ability to price anything; community rows that imply a unit change or sit more than tenfold from the built-in figure are dropped.
Settings → Pricing lists every model this machine has actually used, each with its origin
(official / community / builtin) — so a model nobody can price is visible rather than silently
guessed at.
Claude Code deletes session transcripts older than cleanupPeriodDays (30 by default) on every
startup. agentistics asks once, on first run, what to do about that:
| Mode | What it keeps |
|---|---|
consolidate(recommended) | One small JSON of computed metrics per session — survives the cleanup, no chat duplicated |
full | Additionally mirrors the raw transcripts, chat included. Grows without bound |
off | Nothing — ~/.claude only |
Revived sessions appear in lists and agent metrics but never inflate aggregate totals.
Nay is a chat panel that answers questions about your own data by calling the MCP tools.
It runs claude --print under the hood, so it spends your Claude subscription quota.
The MCP server exposes the same analytics as structured tools (summary, harnesses, projects, sessions, costs, layout building, PDF export) and registers itself at user scope on first start — so any Claude Code session can use them, not just Nay.
claude mcp list # should show "agentistics"- OpenTelemetry export for Grafana, Datadog or any OTLP collector — docs/opentelemetry.md
- Live sessions — which assistants are running right now, detected from host processes. When
detection is impossible (not Linux, no
/proc, a container that cannot see the host) it says so instead of rendering an honest-looking zero - PDF export of any combination of sections, period and theme
- PT-BR + EN, light and dark, throughout — CLI, TUI and web
| Doc | Contents |
|---|---|
| docs/cli.md | Every agentop command, flag and example |
| docs/architecture.md | Structure, request lifecycle, build pipeline |
| docs/security.md | Threat model, trust boundaries, the limits of each control |
| docs/exposure.md | Publishing a central safely |
| docs/DEPLOY.md | Central deployment, central.sh, env vars |
| docs/github-actions.md | CI ingest, OIDC, repo-bound tokens |
| docs/harness-contract.md | What each metric must MEAN across harnesses |
| docs/data-sources.md | Data sources, JSONL parsing, SessionMeta |
| docs/metrics.md | Pricing table, cost formula, streak, cache |
| docs/nay.md · docs/mcp.md | The chat assistant and the MCP tools |
The GIFs above are recorded from a demo fleet, never from a real machine — a published recording cannot be taken back, and a real project name or repository in one is a leak.
# 1. a pseudonymized fleet, derived from this machine's computed metrics# (never from transcripts, so chat cannot reach it by construction)
bun run packages/server/scripts/seed-demo.ts --split 3 --force
# 2. run them
HOME=~/.agentistics-demo-home-1 PORT=47391 ./release/agentop server &# 3. record
packages/server/scripts/record-all.sh # terminal → .cast + .gif
bun run packages/server/scripts/record-web.ts # web → .gifTerminal recordings are asciinema casts (casts/*.cast, embeddable as a
real player with selectable text) rendered to GIF with agg.
See CONTRIBUTING.md. Everything in this project is in English — code, comments, commits, docs.
Made with ♥ for the vibe coding community











