Skip to main content

Portfolio-level project tracking for Claude Code

Project description

claude-ledger

Portfolio-level project tracking for Claude Code.

Why This Exists

Claude Code is excellent at per-project memory. Each project gets its own CLAUDE.md, auto-memory files, and todos. Within a single project, context persists well.

But if you work across many projects — 10, 20, 50+ — there's a fundamental gap: Claude starts every session ignorant of what happened in your other projects, even on the same machine. There's no portfolio layer.

This is the problem I hit running 30+ projects with Claude Code as my primary development tool. The symptoms:

  • Constant re-explaining. "Where were we?" became the opening line of every session. Not just for the current project — for the portfolio. Which projects are active? What did I ship yesterday? What's been neglected?
  • Invisible progress. I built a session tracking system but never wired the hooks. Six days of work on a client project went completely untracked. I only discovered this during an audit.
  • Lost cross-project awareness. Pausing one project without realising it blocks three others. Changing shared infrastructure without knowing what depends on it. No cascade warnings.
  • Session amnesia after compaction. Mid-session context compression would wipe portfolio awareness entirely. The project I was currently working on survived, but everything else vanished.

Per-project memory is solved. Portfolio-level intelligence is not. That's what claude-ledger addresses.

How It Fits Into Claude Code's Memory System

Claude Code already has a layered memory system. claude-ledger adds the missing layer:

Layer 1: CLAUDE.md          Per-project instructions, vocabulary, protocols
Layer 2: Auto-memory        Per-project facts, preferences, feedback
Layer 3: Todos/Tasks        Per-project work tracking
─────────────────────────────────────────────────────────
Layer 4: claude-ledger      Cross-project portfolio awareness  ← NEW

Layers 1-3 answer: "What do I need to know about this project?"

Layer 4 answers: "What's happening across all my projects? What's active, what's stale, what depends on what, and what did I do yesterday?"

claude-ledger sits on top of the existing system — it doesn't replace anything. Your CLAUDE.md files, memory, and todos keep working exactly as they do now. The ledger adds the portfolio view that was missing.

How It Works

claude-ledger is a set of Claude Code hooks + a CLI that automatically:

  1. Tracks activity — every file edit and git commit is recorded to a per-project ledger file, without you doing anything
  2. Generates briefings — on session start, Claude gets a portfolio summary with priority tiers and staleness detection
  3. Maps workstreams — groups related projects together and warns when changes might cascade

The key design principle: capture must be fully automatic. Manual triggers have already proven unreliable — if it requires a human to remember to run something, it won't happen. Hooks solve this.

What Claude Sees at Session Start

When you start a Claude Code session, the SessionStart hook generates a briefing like:

Portfolio briefing ready — 32 projects tracked, 7 P1 active, 3 stale.
Read ~/.claude/ledger/_portfolio.md and ~/.claude/ledger/_workstreams.md for full context.

Claude can then read the full briefing — grouped by priority, with staleness warnings and workstream cascade alerts — before you even type your first message.

What Happens During a Session

As you work, hooks fire silently in the background:

  • Edit a file → the ledger records which project was touched
  • Make a git commit → the commit message and SHA are captured to the project's activity log
  • Claude responds → the session summary is stored
  • Session ends → everything is finalised and the ledger repo is committed (giving you time-travel via git log)

You never interact with the ledger directly. It just accumulates.

Quick Start

pip install claude-ledger
claude-ledger init --scan-dirs ~/Code --github-user your-username
claude-ledger scan
claude-ledger bootstrap

That's it. Every Claude Code session now starts with a portfolio briefing, and activity is tracked automatically.

What Gets Created

~/.claude/ledger/
├── ledger.yaml              # Your configuration
├── _portfolio.md            # Auto-generated briefing (Claude reads this at session start)
├── _workstreams.md          # Cross-project dependency map with cascade warnings
├── .git/                    # Time-travel — git log shows portfolio state over time
├── my-project.md            # One ledger file per project
└── another-project.md       #   (YAML frontmatter + activity log)

The whole thing is plain markdown files in ~/.claude/. No database, no daemon, no external service. Git-backed for time-travel. Human-readable. Works offline.

Commands

Command What it does
claude-ledger init Set up ledger directory and install Claude Code hooks
claude-ledger scan Discover projects in your configured directories
claude-ledger bootstrap Create ledger files from scan results
claude-ledger briefing Generate portfolio + workstream briefings
claude-ledger status Quick portfolio summary
claude-ledger uninstall Remove hooks (optionally delete ledger data)

Configuration

After running init, edit ~/.claude/ledger/ledger.yaml:

version: 1

# Where to scan for projects
scan_dirs:
  - ~/Code
  - ~/Projects

# GitHub username for remote repo discovery (optional)
github_user: your-username

# Directories to scan for stray project files
stray_scan_dirs:
  - ~/Downloads

# Days of inactivity before a project is "stale"
stale_days: 7

# Projects to skip during scan/bootstrap
skip_slugs: []
no_track: []

# Group related projects into workstreams (optional)
workstreams:
  backend:
    display_name: "Backend Services"
    members:
      - api-server
      - auth-service
      - worker-queue
  frontend:
    display_name: "Frontend Apps"
    members:
      - web-app
      - mobile-app

Ledger File Format

Each project gets a markdown file with YAML frontmatter:

---
name: My Project
slug: my-project
directory: /Users/you/Code/my-project
repo_url: https://github.com/you/my-project.git
status: active        # active, paused, dormant, archived, completed
priority: P1          # P1, P2, P3 (inferred from activity)
vision: A short description of what this project does
current_phase: building
last_session: '2026-03-20T15:03:39Z'
last_activity: Add user authentication
systems: []
tags: [next.js, typescript, tailwind]
workstreams: [frontend]
---

## Activity Log

### 20 March 2026
- Add user authentication (a1b2c3d)
- Fix login redirect loop (e4f5g6h)

### 18 March 2026
- Initial project scaffold (1234567)

These are just markdown files. Edit them freely — change priorities, update the vision, add notes. The hooks only ever append to the activity log and update timestamps.

How Hooks Work

claude-ledger installs five hooks into your Claude Code settings:

Hook Event What it does
PostToolUse (Edit/Write) File edited Records which project was touched
PostToolUse (Bash) Command run Captures git commit metadata
Stop Response complete Stores session summary
SessionEnd Session closes Finalises activity, commits ledger repo
SessionStart Session opens Generates portfolio briefing

All hooks have tight timeouts (2-5 seconds) and fail silently — they never block your work.

Concurrent Sessions

If you run multiple Claude Code sessions at once (different terminals, different projects), the ledger handles it safely. Each project's ledger file has its own lock file, and session state is isolated per session ID. Two sessions touching the same project won't corrupt each other's data.

Heuristic Inference

When you run bootstrap, claude-ledger infers project metadata from what it finds:

Signal Inference
>=10 commits/30d + CLAUDE.md P1 (high priority)
>=3 commits/30d or has .mcp.json P2 (medium)
Everything else P3 (low)
>=1 commit in 30 days Active
No commits but <90 days old Paused
<365 days old Dormant
Older Archived

You can override any of these in the ledger files — they're just markdown.

Design Decisions

Why plain files, not a database? Markdown files are human-readable, git-compatible, and always available. No MCP dependency, no daemon, no external service. Each file is independently lockable for thread-safe concurrent writes.

Why YAML frontmatter? Structured enough for machines to parse (priority sorting, staleness detection), readable enough for humans to edit. The format is forward-compatible — a future daemon or dashboard could ingest these files directly.

Why hooks, not manual tracking? Because manual triggers don't work. I built a session tracking script and forgot to wire the hooks for 6 days. If it requires a human to remember, it won't happen.

Why the workstreams layer? Because pausing one project can silently block three others. Cascade warnings surface cross-project risks before they cause problems.

Requirements

  • Python >= 3.9
  • Claude Code (for the hooks integration)
  • gh CLI (optional, for GitHub repo discovery)

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_ledger-0.1.0.tar.gz (38.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_ledger-0.1.0-py3-none-any.whl (32.4 kB view details)

Uploaded Python 3

File details

Details for the file claude_ledger-0.1.0.tar.gz.

File metadata

  • Download URL: claude_ledger-0.1.0.tar.gz
  • Upload date:
  • Size: 38.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for claude_ledger-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9bd2dd2fa0b36111af48af4980a700371313b9ac3b6cb12aa7fadbde068b1a83
MD5 ea8a52f0a43e70a672600a4747d3303c
BLAKE2b-256 61e9517b742de347ffc83ba073e9417fe78ccab97ea1666b2f09a72d951c6051

See more details on using hashes here.

File details

Details for the file claude_ledger-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: claude_ledger-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 32.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for claude_ledger-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7850d01c7d053bec2b70f2c0812de2518c108df8a127f2d66d0daa4c11036dd1
MD5 c62cb810708c7dec0d0b85b9de8984ff
BLAKE2b-256 cecaddc264f0c9e6cc65a8d6b5be8b5c5e1aecb7e8dc339c680b011b38db38da

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page