Claude Code Manager — a CLI + TUI for everything Claude Code stores under ~/.claude/projects/.
List projects, browse sessions, view / delete / export them, inspect memory
files, see disk-usage stats — without cd-ing into a directory full of
URL-encoded path names.
uv tool install claudecm
ccm # launches the TUI
ccm ls # subcommand mode
ccm stats # disk-usage dashboardRequires Python 3.10+ and uv (or
pipx/pipif you prefer). The PyPI distribution is namedclaudecm(the unqualifiedccmwas already taken). The console command staysccm.
~/.claude/projects/ accumulates fast: every Claude Code session writes a
JSONL file, every project keeps its own memory directory, and the folder names
are a one-way path encoding (/home/q/my_projects/foo →
-home-q-my-projects-foo). Going in with ls and rm is painful.
- One pane to navigate them all — projects on the left, sessions on the right, Claude-style spinner up top.
- Subcommands for scripting —
ccm ls --sort size -n 10,ccm export <project> -f md,ccm stats. - Decodes folder names correctly — by reading the real
cwdfrom inside each session's JSONL, not by replacing-with/and hoping. - Knows about memory and PRs — surfaces
memory/*.md, counts messages by type, picks up custom session titles. - No daemon, no config — operates directly on the on-disk layout Claude Code already uses.
From PyPI (recommended)
uv tool install claudecm # global, exposes `ccm`
# alternatives:
pipx install claudecm
pip install --user claudecmRun without installing (one-off)
uvx --from claudecm ccm lsFrom source (latest main)
git clone https://github.com/QuocTang/ccm
cd ccm
uv tool install . # global
# or for dev work:
uv sync --extra dev
uv run ccm --helpFrom a GitHub commit/branch
uv tool install git+https://github.com/QuocTang/ccmccm # launch TUI (default when no args)
ccm ls # list projects (sorted by recent activity)
ccm ls -s size -n 10 # sort by size, top 10
ccm show <project> # detail for one project
ccm sessions <project> # list sessions of a project
ccm view <project> <sess> # render messages of a session
ccm rm <project> # delete a project directory (with confirm)
ccm rm-session <p> <s> # delete one session
ccm export <p> [<s>] -f md # export to markdown (or -f json | raw)
ccm memory <project> # view memory files
ccm memory <p> --show NAME # print one memory file
ccm memory <p> --rm NAME # delete one memory file
ccm stats # disk usage dashboard
ccm tui # launch TUI explicitly<project> accepts an encoded dir name, the real cwd path, the basename
(e.g. my-project), or any unique substring of either.
<session> accepts the full UUID or a unique prefix (the 8-char head shown
by ccm sessions is usually enough).
| Key | Action |
|---|---|
↑/↓ j/k |
Move cursor |
h/l ←/→ |
Focus projects / sessions pane |
Tab |
Switch panes |
Enter |
Drill in (project → sessions → view) |
m |
Show memory for highlighted project |
d |
Delete focused project / session |
r |
Refresh |
q Ctrl+C |
Quit (or back inside a sub-screen) |
Inside a delete-confirm modal: y / Enter to confirm, n / Esc to cancel.
Three layers, deliberately separated so domain code stays UI-agnostic.
flowchart LR
User([Terminal user])
subgraph ccm
direction TB
CLI["cli/<br/><sub>typer subcommands</sub>"]
TUI["tui/<br/><sub>textual app</sub>"]
CORE["core/<br/><sub>projects · sessions · memory · stats · export</sub>"]
SHARED["palette.py · paths.py<br/><sub>shared theme & path helpers</sub>"]
end
Disk[("~/.claude/projects/<br/>JSONL sessions + memory/")]
User -- "ccm <subcommand>" --> CLI
User -- "ccm (no args)" --> TUI
CLI --> CORE
TUI --> CORE
CLI -.-> SHARED
TUI -.-> SHARED
CORE --> Disk
The lossy folder-name encoding is the one bit that requires care: Claude Code
replaces both / and _ with -, so paths.real_cwd_from_sessions() reads
the actual cwd field from the first session JSONL inside each directory.
The naive - → / replacement is only a fallback for empty projects.
uv sync --extra dev # install + pytest
uv run pytest -q # run all tests
uv run pytest tests/core/test_paths.py -k naive_decode # one test
uv tool install . --force --reinstall # rebuild global `ccm`See CLAUDE.md for architecture notes and the textual / typer
gotchas we hit (lossy path encoding, Widget._size shadowing, markup escaping,
etc).
PRs welcome. Keep the three-layer split (core stays UI-agnostic), run
uv run pytest -q before pushing, and follow the gotchas in CLAUDE.md if you
touch tui/.
MIT © QuocTang