pineapples.dev
pineapples.dev
Back to Blog

AI Strategy

Claude Code Session Logs: One Digest From a Week of Work

Published Oct 15, 2026. Updated Oct 15, 2026

Claude Code session logs, git, and Grok sessions become one redacted digest you read before you share it.

Claude Code Session Logs: One Digest From a Week of Work
Git commits, Claude session logs, and Grok session logs pass the redaction gate, then a digest, themes, and ideas. X hooks are optional and stay off unless you set a token. Source: collect.py and gems.py in session-gems.

Claude Code Session Logs: One Digest From a Week of Work

Claude Code session logs are the JSONL files Claude Code writes under ~/.claude/projects. I wrote session-gems to read a week of those files, plus git commit subjects and Grok build sessions, redact every string on the machine, and leave one digest I can read before I share it.

The code is session-gems.

The real problem

The work I do every day is the best content I have, and it is buried in logs. A commit subject names what landed. A Claude Code session keeps the prompt and the reply. A Grok session keeps the other thread. The post is already in those files. It is still in the shape of a log.

I wanted that week in one file I could read on the laptop that already holds it. session-gems is two Python 3 scripts, standard library only, with no install step. collect.py reads. gems.py groups. The license is MIT.

Principles

Every string is redacted before it is stored. collect.py runs redact on a commit subject, a prompt, and an assistant message before those fields enter the digest. The patterns cover private key blocks, JWTs, token prefixes (sk-, sk-ant-, ghp_, github_pat_, xox, AKIA, AIza, ya29, glpat, whsec, and the other prefixes in that expression), Bearer and Basic headers, .env style lines, and assignments to password, secret, token, api_key, auth, cookie, and session_id. They also cover email addresses, phone numbers, database URLs, URLs with embedded credentials, long hex strings, and long base64 or opaque strings. The home directory is rewritten to ~. A long file path is left in place. tests/test_redaction.py checks that the secret material does not survive, and that a long project path does.

A file named like a credential is never opened. safe_open refuses any file whose name matches auth, token, credential, secret, password, cookie, keychain, .env, .pem, .key, id_rsa, or id_ed25519. The test writes auth.json, .env, token.txt, credentials.json, and id_rsa, calls safe_open on each, and expects PermissionError. For Grok build sessions the collector opens only chat_history.jsonl and summary.json.

Git keeps the subject and the shortstat. The command is git log --all --since=N days ago --no-merges --shortstat. Each stored commit is the subject, the date, a 10 character hash, and the shortstat line: files changed, insertions, deletions.

The digest is capped at 2,000,000 bytes. A session keeps at most 25 prompts, each cut at 700 characters, and the last 12 assistant messages, each cut at 300 characters. If the JSON is still larger, the script shortens prompts, assistant text, and the commit list until it fits. The file lands in ./out. Redaction is a pattern list. It does not know which project names are confidential. I read the digest before I share it.

collect.py makes no network calls. gems.py calls the X recent search API only when X_BEARER_TOKEN is set and I pass --x-query. With the flag and no token, it writes a line to stderr and skips that step. Text that comes back from X is cut at 280 characters and is not passed through redact. I read that block before I share it too.

How we actually build it

Clone session-gems and run the two scripts with the Python 3 you already have.

python3 collect.py --days 7
python3 gems.py out/digest-*.json > themes.json

collect.py walks the roots I pass. The default root is ~, the default depth is 4, and the default window is 7 days. It skips Library, node_modules, Applications, Pictures, Music, Movies, venv, .venv, dist, build, and any directory whose name starts with a dot. A directory that contains a .git directory is a repo. A linked worktree, where .git is a file, is skipped, because those commits duplicate the main checkout. A commit hash already seen, from another clone, is stored once. The default commit cap after that is 1,500. Sources default to git, Claude, and Grok.

Claude Code files are the .jsonl transcripts under ~/.claude/projects whose modification time falls inside the window. A line is kept when type is user or assistant. It is dropped when isSidechain or isMeta is set, when the timestamp is older than the window, and when the text starts as a command tag, a system reminder, a caveat line, an interrupted request, a task notification, or a continued-session preamble. The project path comes from cwd on the record.

Grok build sessions live under ~/.grok/sessions. The project folder name is URL encoded. summary.json supplies the title, the timestamps, and the working directory when that field is present. chat_history.jsonl supplies the turns. A user turn wrapped in user_query tags keeps the query. A user blob longer than 4,000 characters with no query tag is skipped, because that blob is injected context.

The script writes ./out/digest-YYYY-MM-DD.json and prints one line: repos scanned, commits kept, Claude sessions, Grok sessions, bytes, and the path.

gems.py reads that file. Commits that share a subject and a calendar day collapse to one row. Four expressions sort lines into themes. Built matches feat, add, build, launch, new, ship, create. Fixed matches fix, bug, resolve, repair, patch, hotfix. Tools matches mcp, agent, claude, grok, codex, cursor, vercel, next.js, expo, react native, lint, cloudflare, docker, proxy. Pain matches limit, fail, error, broken, blank, too long, rate limit, blocked, timeout, flaky. A fifth expression pulls numbers that sit next to a unit such as %, tests, files, or ms. Each list is cut at 200 lines. Repo counts are the 30 names with the most unique commits in that file.

X is a repeatable --x-query. The call is the recent search endpoint, English, retweets excluded. Each hit keeps a status URL, the time, the text, and the like count. A network or quota failure is stored as a short error string so the rest of the JSON still prints.

Git, Claude, and Grok logs pass a redaction gate into a digest
collect.py reads git shortstat, Claude Code JSONL, and Grok chat history, redacts every string, and writes a digest capped at 2,000,000 bytes. gems.py groups themes. X search runs only with X_BEARER_TOKEN, and that text is not redacted. Source: collect.py and gems.py in https://github.com/PineapplesDev/session-gems.

What we learned

I ran it on my laptop, over the last 7 days.

It scanned 177 repos. Git reported 3,000 raw commits. The same commits were sitting in 8 worktrees, so the unique set was 527. That run is why collect.py now skips a linked worktree. A hash that is still reachable from more than one clone is stored once. The session side was 27 Claude Code sessions and 2 Grok sessions. The digest was 824 KB. The run took about 2 minutes.

824 KB is under the 2,000,000 byte cap, which is small enough to read. 3,000 raw commits is a week of duplicated checkouts. The dedupe is what makes the week readable. The themes were the week itself. The pain lines kept returning to the same failure: weekly usage limits killing scheduled agent runs. A scheduled agent run is a workflow with an owner, which is the shape I describe in AI workflow automation.

I am not naming a repo from that run. I am not naming a client, or a result that belongs to a client. The digest can still hold project names the patterns do not recognize. Reading it is the step before anyone else sees it.

When this isn't the right fit

It is the wrong tool when you want a hosted product, a team dashboard, or someone else to turn the digest into posts. It is two scripts on the machine that already has the logs.

It is the wrong tool when the digest will be pasted into another service before anyone reads it. The redaction list is patterns.

Handing tools to an agent in the browser is a different object. That one is WebMCP on pineapples.dev.

Frequently asked questions

What are Claude Code session logs?

Claude Code session logs are the JSONL files under ~/.claude/projects. session-gems keeps user and assistant messages from those files, skips sidechain and meta lines, and stores a capped, redacted copy in a local digest.

Does session-gems upload the logs?

collect.py makes no network calls. gems.py calls the X recent search API only when X_BEARER_TOKEN is set and you pass --x-query. The digest is written to ./out on the machine that ran the script.

What did the first run on the laptop show?

On my laptop, over the last 7 days, the run scanned 177 repos, saw 3,000 raw commits that deduped to 527 unique, read 27 Claude Code sessions and 2 Grok sessions, and wrote an 824 KB digest in about 2 minutes. The same commits had appeared in 8 worktrees. I do not name clients, client repos, or client results from that run.

Where is the code?

The repository is https://github.com/PineapplesDev/session-gems. It is two Python 3 scripts, standard library only, under the MIT license. Tests run with python3 -m unittest discover -s tests.

Written by Anthony Wentzel

Keep reading