Skip to main content

thread-keeper

A local-first Python CLI that automatically collects the logs of how you work with AI coding agents (Claude Code, Codex, Cursor), normalizes them into one queryable SQLite store, and opens them in Jupyter — so you own your agent-usage data and can find where your instructions or setup are wasteful or inconsistent.

No web UI, no daemon, no cloud, no network calls. Just: collect → normalize → analyze.

The product name is thread-keeper. The PyPI / install distribution name is thk. The Python import is threadkeeper. How you run the CLI depends on the install path:

  • uv tool install thk / pip install thk → thk or thread-keeper on PATH
  • uvx thk … → one-shot; prefix every invocation with uvx thk
  • clone / uv sync → uv run thread-keeper (or uv run thk)

See docs/PRD.md for the full product spec and docs/contracts/ for the JSON Schemas of every shape that crosses a boundary.

Install

# From PyPI (puts `thk` and `thread-keeper` on PATH):
uv tool install thk
# or: pip install thk

# From a clone (editable):
git clone <this-repo>
cd thread-keeper
uv sync
# Optional: put `thread-keeper` / `thk` on PATH for agent hooks (required
# before install-hooks if you want a bare name; install-hooks also writes an
# absolute path so SessionEnd works after `uv sync` alone).
uv tool install -e .

Requires Python ≥3.11 (pinned to 3.14 in .python-version) and uv.

Quickstart

# Backfill your ENTIRE existing history from Claude Code / Codex / Cursor —
# no "only since install" cutoff.
#
# After `uv tool install thk` (or `pip install thk`):
thk collect --sweep
# One-shot without installing on PATH:
# uvx thk collect --sweep
# From a clone (no tool install):
# uv run thread-keeper collect --sweep

# See what's in the store.
thk status
# uvx thk status
# uv run thread-keeper status

# Optional: write SessionEnd / notify hook configs (backs up existing files
# first). Commands use an absolute path to this install so they work even
# when the CLI is not on PATH. Revert with `uninstall-hooks`.
thk install-hooks
# thk uninstall-hooks
# uvx thk install-hooks
# uv run thread-keeper install-hooks
import threadkeeper as tk

sessions = tk.sessions()             # DataFrame + has_usage / cost_usd / token cols
sessions.loc[sessions["has_usage"]].sort_values("cost_usd", ascending=False)
tk.usage_long().groupby("model")["cost_usd"].sum()
sid = sessions.iloc[0]["id"]
messages = tk.messages(sid)          # DataFrame, one row per normalized event
tk.cost_of(sessions.iloc[0]["usage"])

API note (cost_of / cost_breakdown_of): these return None when usage is missing/empty or every model in the blob is unpriced (unknown ≠ $0; previously empty usage returned 0.0). Prefer sessions["cost_usd"] / filter has_usage, or handle None before arithmetic.

Starter analysis notebooks live in notebooks/ — see that folder's README for setup with Jupyter.

CLI

thk and thread-keeper are the same CLI (both console scripts). Examples below use thread-keeper; substitute thk after a PyPI tool install.

thread-keeper collect --source <claude-code|codex|cursor> --session-id <id> [--transcript <path>]
thread-keeper collect --source <claude-code|cursor> --from-stdin          # Claude Code / Cursor SessionEnd (JSON on stdin)
thread-keeper collect --source codex --from-notify-argv                   # Codex notify (idle-heuristic)
thread-keeper collect --sweep [--force] [--source <...>] [--claude-root <dir>] [--codex-root <dir>] [--cursor-root <dir>] [--host <label>]
thread-keeper install-hooks [--tool <claude-code|cursor|codex> ...]
thread-keeper uninstall-hooks [--tool <claude-code|cursor|codex> ...]
thread-keeper status
  • The fast path is what hooks invoke: either --session-id/--transcript, or --from-stdin (Claude Code / Cursor SessionEnd JSON payload), or --from-notify-argv (Codex notify, no session id — reparses the newest rollout). Always exits 0 (must never block the agent tool).
  • --sweep is the backstop + cold-start backfill: walks all three sources' log dirs and ingests anything changed since the last run, including your entire pre-existing history on a fresh store.
  • --force (with --sweep) re-parses every file, ignoring the change-detection watermark. Use it to backfill a parser/schema change into an existing store — e.g. after an upgrade that adds a per-message field: thread-keeper collect --sweep --force.
  • Source roots (--claude-root/--codex-root/--cursor-root, or the matching THREAD_KEEPER_CLAUDE_ROOT/_CODEX_ROOT/_CURSOR_ROOT env vars) plus --host <label> let you consolidate logs copied in from another laptop into the same store, correctly attributed by origin machine.

Configuration

Setting Flag Env var Default
Data dir — THREAD_KEEPER_DATA_DIR ~/.thread-keeper/
Claude Code root --claude-root THREAD_KEEPER_CLAUDE_ROOT ~/.claude/projects
Codex root --codex-root THREAD_KEEPER_CODEX_ROOT ~/.codex/sessions
Cursor root --cursor-root THREAD_KEEPER_CURSOR_ROOT platform Cursor user dir

The SQLite store lives at <data-dir>/thread-keeper.db; install-hooks backs up any existing hook config it overwrites to <data-dir>/backups/.

Design invariants

  • Offline. No network calls anywhere, ever.
  • Read-only on foreign logs. Cursor's state.vscdb is always snapshot-copied (with its -wal/-shm files) before being read; the original is never opened read-write.
  • Whole-session invariant. A changed file is always re-parsed whole and replaces its prior session — never a partial/appended read.
  • Idempotent. Re-running any collect is a no-op net of row identity; a session rename and its origin host label both survive re-import.

See docs/PRD.md §6 for the full set of design principles.

Development

uv sync
uv run pytest
uv run thread-keeper --help

See CLAUDE.md for how this repo is meant to be extended (spec-driven, one Req at a time, contracts used verbatim).

Credits / prior art

thread-keeper is a from-scratch Python redraw of the offline log-extraction core of Chronicle by @chizhangucb, a JS/Electron "time machine" for AI coding-agent sessions. Chronicle already solved parsing Claude Code, Codex, and Cursor's session logs offline; thread-keeper ports that extraction logic (schema, parsers, pricing tables) to Python and drops everything else (the desktop UI, live streaming, replay, sharing) to leave a lightweight, notebook-first collector. Thanks to @chizhangucb and the Chronicle project for the open-source reference implementation this work stands on.

Metadata

Release files for thk 0.0.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for thk 0.0.4
File Size Uploaded
thk-0.0.4.tar.gz 28.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for thk 0.0.4
File Interpreter ABI Platform
thk-0.0.4-py3-none-any.whl Python 3 none any Details

Total release size: 63.4 kB

Release files / thk-0.0.4.tar.gz

Download URL thk-0.0.4.tar.gz
Size 28.3 kB
Tags Source
SHA-256 checksum
How to use checksums
dc61a2c02ff37a2e960533f52fe880e0d5136e7d5a6209bb2950bb18e62cbbe8
BLAKE2b-256 checksum
How to use checksums
2aff4680fd338e2738c465091232ed9fae659fd87baa5bb02c6087523bb7004d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / thk-0.0.4-py3-none-any.whl

Download URL thk-0.0.4-py3-none-any.whl
Size 35.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
93d0e89f9c0b6321138c6a240971900e8ac7054f5ead221368a9fcbbbadf1eb4
BLAKE2b-256 checksum
How to use checksums
4619cfd1d95c2421b7951e48dc72fc714a146340be79cd946f3162fbd66fd23d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.0.6

2 release files

0.0.5

2 release files

This release

0.0.4 This release

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page