Skip to main content
all posts
essay
~15 min readUpdated

Recap: Resume Claude Code Sessions Across Every Project, Not Just the Folder You're In

Claude Code's /resume only lists sessions for the folder you're standing in, so I built recap, a read-only CLI that shows every session across every repo and hands you the exact command to jump back in.

Hands the full post + a ready prompt to Claude Code or any AI assistant, so it can read and use it.
share

The first time recap earned its place on my machine, I'd just rebooted after a macOS update I'd been putting off for a week, and I was staring at a fresh desktop trying to remember what I'd been in the middle of. I run a lot of Claude Code sessions at once, usually one per repo, sometimes two or three in the same repo on different branches. That morning there were about eleven of them open before the reboot swallowed the lot. Claude Code hadn't lost anything. The transcripts were all still on disk under ~/.claude. What I'd lost was the map: which project, which branch, which conversation, and what I'd told it last.

The built-in answer to "get me back into a session" is /resume, and it's genuinely good. It has one blind spot, though, and that blind spot turns into a wall the moment your work spans more than one repo. /resume only lists sessions for the directory you're currently standing in. If you cd into the wrong folder, the session you actually want isn't in the list. It's not deleted, it's just invisible from where you are. So the honest problem isn't "resume a session." Claude Code already resumes sessions. The problem is "which of my eleven sessions, across eight repos, was the one that mattered, and what's the exact command to get back into it."

recap is my answer to that, and it's deliberately small. It's a read-only command that reads the logs Claude Code already writes, then prints a day-grouped timeline of every recent session across every project on one screen, each with a ready-to-paste cd <path> && claude -r <id> line. That's the whole tool.

What it is not is an agent. It doesn't write code, it doesn't summarize your week with a model and email you a standup, and by default it doesn't touch the network at all. It opens two files, sorts what it finds, and prints. The value is boring on purpose: a quick offline answer to "where did I leave off" that I can actually trust. If that sounds unglamorous, good. It's the most-used thing I've built this year, and nobody but me and a few hundred npm installs will ever run it.

pollAfter a reboot, how do you find the Claude Code session you were in the middle of?

The one thing resume will never do for you

The mechanic is worth being precise about. Claude Code stores your session transcripts on disk and indexes them by project. When you run /resume, it looks at the project that matches your current working directory and lists the sessions for that project. This is a reasonable default. Most of the time you're sitting in the repo you want, and a scoped list is shorter and easier to read than a global one.

It stops being reasonable the second your day looks like mine. I might touch adatepe-cv, claude-code-recap, a client repo I can't name here, and ~/.claude itself in a single afternoon. After a reboot, /resume from my home directory shows me sessions for my home directory, which is almost never the one I want. To find the CV-sidebar session I have to remember it was in adatepe-cv, cd there, and only then can /resume see it. The information I need to run that search is exactly the information I lost when the machine restarted.

So recap has one job, and everything else is in service of it: make every recent session visible from anywhere, then hand you the command to jump back in. It doesn't replace /resume. It stands one level up from it. /resume is "reopen a session in this folder." recap is "show me every folder's sessions at once, ranked by when I last touched them, and print the resume command so I don't have to type a UUID."

Guess the failure mode before you scroll:

your guessYou reboot, open a terminal in your home directory, and run /resume. Where is the session you spent three hours on yesterday in a different repo?

What a recap run actually prints

The default run fills one screen. You pass no flags, it never touches the network, and there's nothing to wait for. It ranks your recent sessions and prints them grouped by day, newest first.

A default recap run, offline and instant

Each row has what I need to make the call. First the time, plus a relative "12m ago" so I know how warm the session is, and a one-line summary to jog my memory. Then the absolute project path, which is where the work actually lives. After that the git branch, the model, and a rough turn count, which together tell me whether this was a quick poke or a long haul, and finally the 8-character session id. And on its own indented line, the exact cd <path> && claude -r <id> command.

That last detail is smaller than it looks and I care about it more than almost anything else. The resume line is indented with spaces, never a tab, so a triple-click selects the whole command and nothing else. You copy it, paste it, and you're back. In a real terminal the list is colored: the day headers run from green for Today to amber for Yesterday, there's a 14-day activity sparkline in the header so you can see the shape of your week, and each project keeps a stable colored dot so the same repo looks the same across runs. When the output is piped, or when NO_COLOR is set, the color turns itself off and you get clean text.

The summary line isn't magic. recap prefers Claude Code's own ai-title for the session if one exists, falls back to the first real user prompt otherwise, and prints (no prompt) if it genuinely can't find one. Slash commands, system reminders, and tool results aren't counted as prompts, because "your first message was /clear" isn't a useful summary. If you want a real one-sentence description instead, that's what --smart is for, and it's off by default for reasons I'll get to.

It only reads the logs Claude Code already writes

The engineering claim I want to make plainly is that recap invents no new state. It's a reader for data Claude Code is already keeping. There are exactly two things it opens, both local, both yours already.

annotatedThe only two files recap opens
~/.claude/history.jsonl
~/.claude/projects/<encoded>/<session-id>.jsonl
  1. A global append-only log of prompt text, timestamp, project path, and session id. recap reads it to get each session's project path and a fallback one-line summary without opening the transcript. Which sessions are recent enough to bother with is decided separately, by ranking the transcript files themselves by modification time.

CLAUDE_CONFIG_DIR is honored, so if your config lives somewhere other than ~/.claude, recap follows it there. The ordering is a two-step thing that's worth knowing about: candidate transcripts are first ranked by file modification time, then re-sorted by the true last timestamp found inside the file. Claude Code touches transcripts on compaction and title writes, so the modification time alone is only an approximation of "when did I last work on this," and reading the real last event fixes the cases where mtime lies. Project paths always come from the cwd and project fields inside the logs, never decoded back from the directory-name encoding, which is lossy and would occasionally hand you a path that doesn't exist.

The turn count is labeled approximate because it is. It's user prompts plus distinct assistant messages, grouped by message id so that streaming chunks of a single reply aren't counted five times. It's a rough sense of session weight, not an audit. I'd rather print an honest "≈ 46 turns" than a precise number that quietly lies to you.

As for what recap writes: nothing. A run creates, modifies, and deletes zero files. It never edits your session data. The only network path in the entire tool is --smart, which is opt-in, and even that only shells out to your local claude CLI once. There's no telemetry, no phone-home, no counter. I read that guarantee back to myself every time I add a feature, because a tool that reads your conversation transcripts has to be boring and legible about exactly what it touches, or it doesn't deserve to run unattended in your shell.

Why it's one Python file with zero dependencies

recap is a single Python file, standard library only. argparse for the flags, json for the logs, glob for finding transcripts, subprocess for --open and --smart, datetime for the day grouping. No pip packages, no node_modules, no build step, no virtual environment to activate.

That's a deliberate constraint, not laziness. The whole point of recap is to answer "what was I doing" when I'm at my most disoriented, right after a reboot or a context switch, when I have the least patience for setup. A tool for that moment can't itself be a project I have to maintain. If recap needed a dependency tree, then some morning I'd run it and get a version conflict or a broken lockfile instead of my session list, and it would've failed at the one job it exists to do. Python 3 is already on every machine I work on. Betting on the standard library means recap runs the day I install it and keeps running after I've forgotten it's there.

It also keeps the tool auditable. The entire thing is one file you can read in a sitting. The same disciplined-tooling instinct I lean on as a working student at BMW while finishing my M.Sc. CS at LMU is why I kept it to one auditable file with nothing behind it. When I claim it reads exactly two paths and writes nothing, you don't have to trust me, you can open recap.py and confirm it. Zero dependencies is partly a privacy stance: there's no transitive package that could grow a postinstall hook or a network call behind my back. So what you read in that one file is exactly what runs on your machine.

The robustness I had to build in so it never crashes on your logs

The naive version of this tool is ten lines: glob the transcripts, json.loads each line, print. It works right up until it meets a real ~/.claude directory, and then it dies. Log files that are being written live aren't always clean. You'll hit a half-flushed line, a line that's valid JSON but is a bare string instead of the object you expected, an empty file, a project directory that was deleted last week but still has a stale transcript. The naive version tracebacks on the first one and prints nothing, which is the worst possible outcome for a tool you reach for when you're already lost.

So a real share of recap is defensive reading, and I think that work is the actual product:

  • A broken or partial JSONL line is skipped, never fatal. If a line won't parse, or parses to something that isn't an object, recap moves to the next line instead of crashing. One corrupt row can't cost you the whole list.
  • An empty or missing config directory prints a clear message, not a stack trace. If ~/.claude/projects doesn't exist, recap tells you so in one plain sentence and exits. A traceback here would be a tiny insult to someone who just wanted to know where they left off.
  • When you bulk-open, recap skips the session it's running in. --open reads CLAUDE_CODE_SESSION_ID and leaves out the conversation you launched recap from, so a re-open of your working set never spawns a duplicate tab for the session you're already in.
  • A deleted project directory can't send you into a dead resume. The plain list still shows the session, since its transcript is real, but --pick refuses with a clear error instead of cd-ing into nothing, and --open skips it and prints "path gone" rather than opening a broken tab.

None of this is clever. All of it is the difference between a tool I trust at 9 a.m. on reboot morning and a tool I stopped opening because it choked on my own logs once.

Reopening a whole working set safely with --open

The single-session case is --pick: recap shows the list, you choose a row, and it cds into that project and execs claude -r for you. No copy-paste, no UUID typing.

The feature I didn't expect to lean on as hard as I do is --open. It takes the sessions currently listed, respecting your --since, --project, and --limit filters, and opens one new terminal tab per session, each already resumed. It's the fastest way back to a five-repo working set after a reboot. On macOS it drives iTerm2 or Terminal through osascript, and --terminal auto|iTerm2|Terminal picks which. --dry-run prints the tabs and commands and opens nothing, which is how you should always look before you leap.

# reboot
# open terminal, cd ~/repos/adatepe-cv
claude   # scroll /resume, is this it?
# no. cd ~/repos/claude-code-recap
claude   # scroll /resume again
# repeat for eight repos, from memory

The work never moved. What changed is that I stopped reconstructing my own working set from memory, one cd at a time, and started reading it off one screen.

The part I'm quietly proud of is the quoting. When --open builds the command for each tab, every path is passed through shlex.quote. That's not decoration. A project folder named ~/repos/my project has a space in it, and an unquoted cd ~/repos/my project breaks. Worse, a folder whose name contains a semicolon or a backtick is, without quoting, a shell injection waiting to run whatever comes after it. shlex.quote turns every path into a single safe shell token, so a directory name can contain a space, a quote, or a semicolon and the generated command still does exactly one thing: change into that directory and resume that session. recap is building shell commands from strings that live in your filesystem, and the only responsible way to do that is to assume those strings are hostile and quote them like it.

--open also refuses to open anything when stdin isn't a TTY unless you pass --yes, which is exactly the case for Claude Code's own Bash tool. That refusal is on purpose. Opening a pile of tabs is a side effect, and side effects shouldn't happen from a non-interactive context by accident.

The flags I actually reach for

Most days I run recap with no flags at all. When I do reach for one, it's almost always to narrow the list.

The flags that earn their keep

--since takes 30m, 24h, 7d, or 2w. --project is a plain substring match against the absolute path, so --project adatepe catches every repo with "adatepe" in the path. --limit defaults to 15. --json gives you the whole thing with full session ids and resume commands, which is how you build your own tooling on top of it. And --smart is the one flag that touches the network: it makes a single call to your local claude CLI with the small, fast haiku model to turn the raw prompts into real one-sentence summaries. It's off by default, it sends only the 8-character id, the title, and the first user prompt for the listed sessions and nothing else, and if the claude CLI isn't on your PATH it's skipped with a warning and you get the offline summaries. I keep it off for the daily case because the offline list is already enough to recognize my own work, and I turn it on when I'm scanning a longer stretch and want the titles sharpened.

find your answer

Which recap flag do you actually need?

Route yourself to the shortest command for what you are trying to do.

Do you want to see sessions, or get back into them?

The honest caveats, stated plainly

I'd be lying if I sold recap as flawless, so here's the other side, out in the open rather than buried.

--open is macOS only right now. The tab opening drives iTerm2 or Terminal through AppleScript via osascript, and that machinery doesn't exist on Linux or Windows. The rest of recap, the listing, the filtering, --json, --pick, and importantly --open --dry-run, works fine on Linux, so you can still see your sessions and print the exact commands to paste. But the "reopen my whole working set in tabs" trick is a macOS thing until I or someone else wires up the equivalent for another terminal. I'm not going to pretend it's cross-platform when it isn't.

The sharper caveat is --claude-flags. Whatever you pass there is handed verbatim to every resumed claude, and that includes --dangerously-skip-permissions. Read this command for exactly what it is:

recap --since 24h --limit 8 --open --yes \
  --claude-flags "--chrome --dangerously-skip-permissions"

That launches up to eight Claude Code sessions, in eight different repos, each with every permission check disabled, each resuming a conversation you may not fully remember. A resumed session carries its old context with it, so whatever it was in the middle of, it can now finish without asking you. That's a real blast radius, multiplied by the number of tabs. recap doesn't stop you, and it shouldn't, but it prints an explicit warning before it opens anything, and --dry-run will show you the full command list first. Use that flag only on repos you fully control, and only after you've read the dry-run output. If you're not sure what a session was doing, resume it without the flag and let it ask. recap makes it cheap to launch a lot of sessions at once, so you're the one who has to decide what's safe to run.

0sessions open before the reboot
0local files recap reads
0dependencies
0files it writes per run

How to install it in one line

There are a few ways in, all live, and they install the same thing. Pick one.

  1. Plugin marketplace, the native path inside Claude Code. The repo is its own marketplace: run /plugin marketplace add noluyorAbi/claude-code-recap, then /plugin install claude-code-recap@noluyorabi-plugins, reload, and invoke it as /claude-code-recap:recap.
  2. npx, if you want it as a plain skill: npx claude-code-recap writes the skill into ~/.claude/skills/recap and you invoke it as /recap. There's no postinstall hook, and it records a sha256 manifest so it refuses to clobber files you've edited unless you pass --force.
  3. curl, the no-Node path: curl -fsSL https://raw.githubusercontent.com/noluyorAbi/claude-code-recap/main/install.sh | sh, same guarantees.
  4. The skills CLI: npx skills add noluyorAbi/claude-code-recap resolves it straight from the repo across every agent it manages.

If you'd rather not install a skill at all, it's one file. Point Python at it and alias it:

alias recap='python3 ~/.claude/skills/recap/recap.py'

It's MIT licensed, the source is one auditable file at github.com/noluyorAbi/claude-code-recap, and the npm package is claude-code-recap.

Was it worth building?

recap is the least impressive thing I've shipped this year, and the one I'd give up last. There's no model inside it. The architecture is nothing you'd draw on a whiteboard, and there's no benchmark I could wave around. It reads two files and prints a list. The first version took an evening. And it's quietly removed an entire small friction from my week: the reboot-morning ritual of cd-ing through eight repos asking each one what it remembered, reconstructing my own working set from memory like I was recovering a lost password.

What building it taught me is that the useful tools are usually the ones that make something already-true visible, rather than the ones that try to be smart. Claude Code was never losing my sessions. The transcripts sat on disk the whole time. The gap was that the built-in index was scoped to one folder, and I work across many, so from any given folder most of my work was invisible. recap didn't give me a new ability. It just stopped hiding the sessions I already had behind whatever folder I happened to be standing in. That's a smaller ambition than most tools have, and it's exactly why I trust it: there's very little in it that can be confidently wrong, because it's not trying to decide anything for you. It shows you your work and hands you the command to run.

If you run Claude Code across more than one repo, I think you'll feel the same relief the first time you reboot and get your whole working set back from one screen. If you want the day-to-day habits behind how I actually drive the model, they're in my Claude Code skills guide. recap's louder sibling, the overnight loop I let commit while I sleep, is Nightshift. And if you're still assembling your kit, the agentic coding tools I reach for is where I sort the rest.

your move

Where should you go next?

Pick what you are trying to solve.

recap lives with everything else I've built on /#projects, the rest of my writing is on /blog, and the work history is on /cv. If you install it and it saves you a bad reboot morning, that's the whole reason it exists. Come say hi.

built by alperenI build the small tools I write about, recap is one Python file that runs every morning.Working student at BMW, M.Sc. CS at LMU Munich, BMW Fastlane Scholar. See the work, or get in touch.Explore my work