vicnaum/vics-agent-skills/session-stripper
Claude Code and agent skill by vicnaum/vics-agent-skills
- community
Checking that the SKILL.md file is still in the repository.
Manually trim, compact, persist, and repair Claude Code conversation sessions stored as JSONL files. Use when the user wants to: (1) fix a session hitting 'Prompt is too long' errors, (2) manually compact or strip a session to reduce context, (3) analyze a session's token usage and message chain, (4) strip tool call inputs and results from a session, (5) strip thinking blocks from a session, (6) compact part of a session before a specific message, (7) verify or repair a broken session chain, (8) persist and summarize tool results to reduce context intelligently, (9) perform any JSONL-level surgery on Claude Code sessions. Triggers on mentions of session surgery, session trimming, session stripping, context compaction, session repair, prompt too long, session compact, manual session manipulation, summarize tool results, or persist tool output.
Install
npx vskill@latest install vicnaum/vics-agent-skills/session-stripperSKILL.md
Session Surgery
CLI tool for trimming Claude Code JSONL sessions. Sessions live at ~/.claude/projects/<project-path>/<uuid>.jsonl.
Identify the session FIRST — never guess by mtime
Every mutating command operates on the exact <session.jsonl> path you pass. Getting that path right is the single most important step: strip the wrong file and you silently corrupt a different, possibly active, session.
To strip the CURRENT session, resolve it from `$CLAUDE_CODE_SESSION_ID` — the env var Claude Code exports into every Bash tool call, which matches the real transcript filename:
current (without --quiet) prints the id, path, and whether it exists.
NEVER select the session with `ls -t ... | head -1` (most-recently-modified). In a project folder with more than one session running — increasingly common with parallel/multi-agent work — the newest file is frequently a different live session, and you will strip someone else's work. $CLAUDE_CODE_SESSION_ID is the only reliable source; if it is unset you are not inside a CC session, so ask the user which file to strip rather than guessing.
When the user names a specific session id or file (e.g. "strip session abc123"), use that instead; current is only for "strip this/my session".
Copy-first: mutations never touch the real session file
Mutating commands do not write the session file you pass. They snapshot it to <session>.jsonl.pending, mutate only that copy, and print the apply hint. The original stays byte-identical until you run apply, which swaps the copy in with one atomic rename:
Why (incident 2026-08-05): a running CC CLI flushes appends to its JSONL every 100ms, takes no lock, and --resume silently truncates the chain at the first missing parentUuid. Any in-place rewrite of a live session races those appends — one live compact-range collapsed a 763-message chain to 14. Copy-first makes that impossible at the architecture level: there is no in-place write left to race.
Mechanics:
- Multi-pass strips reuse the same pending copy (
analyze→compact-range→strip-tools→ …), so iterate freely.snapshot --refreshdiscards it and re-snapshots. applyaccepts either path (original or.pending) and refuses to swap when: the original is being written right now (it grew during a 400ms settle check — a live CLI is appending), the original was rewritten since the snapshot (prefix-hash mismatch: the copy no longer descends from it — re-snapshot), or the pending copy fails `verify`. Refusals leave everything in place.- Anything appended to the original after the snapshot is discarded by apply, by design (counted and reported). In the self-strip flow that tail is the stripping turn itself — noise the session is better off without. Don't snapshot a session mid-way through work you care about.
- The one
.bakof the original is made at apply time. While mutating the pending copy no.baks are written — the untouched original is the backup. --forkis unchanged and needs no apply: the fork is a brand-new file no CLI writes to, so it is mutated directly.- A
"Prompt is too long"session can't run anything, so its file is quiescent even if the stuck CLI is still open — strip +applyimmediately, thenclaude -r <id>.
Restarting into the stripped session (respawn)
Stripping shrinks the pending copy on disk, but the running CLI keeps its pre-strip context until it restarts — and for your OWN session the apply must happen while the CLI is dead. The `respawn` skill's watcher does exactly that: it /exits the CLI, waits for the process to die, applies the swap in that dead window, and relaunches:
- In-place strip of the current session: strip (writes the pending copy), then
respawn.sh --swap <session.jsonl>.pending. The watcher runsapplyafter process death; if apply refuses, it relaunches the UNSTRIPPED session and says so in the kickoff prompt (fail-open). - Forked strip (new session id from
--fork): no swap needed —respawn.sh <new-session-id>.
This works the same for a Claude Code background session (claude --bg, or moved there with ← / /background): respawn stops it through the daemon, applies the swap and resumes the same job.
Call respawn.sh as the LAST action, then end the turn (see the respawn skill). Without respawn, the manual steps depend on where the session runs:
- In a terminal:
/exit,applyfrom any shell,claude -r <id>. - In the background (
claude agentslists it, or$CLAUDE_JOB_DIRis set):/exitin an attached terminal only detaches, andclaude -rrefuses while it runs. Runclaude stop <job>, wait a few seconds,apply, thenclaude attach <job>(orclaude --bg --resume <id>).
Quick Reference
Run scripts from <skill-dir>/scripts/:
All commands support --dry-run (report only) and --no-backup (skip backup). Range filtering via --from N --to M (chain positions).
Backups are enumerated (never skipped)
apply (and any command that writes a real session file, e.g. mutating a .pending copy passed explicitly with backups on) writes a fresh backup: <session>.jsonl.bak, then .bak.1, .bak.2, … It never silently reuses or skips a backup just because one already exists. While mutating a pending copy no per-op backups are made — the untouched original is the recovery point; apply then creates the one real .bak at swap time. Pass --no-backup to opt out.
The context gauge resets automatically after stripping
CC's "context left" meter is not a live recount of the conversation — it reads the token counts (`input_tokens` + `cache_read_input_tokens` + `cache_creation_input_tokens`) recorded on the most recent assistant turn in the JSONL. Newer builds (CC 2.1.27x, Sep 2026) read the same three fields from the nested per-call `usage.iterations[]` list instead, so the reset nulls iterations (and zeroes cache_creation.ephemeral_*) on every record it touches — a stale iterations[0].cache_read_input_tokens alone is enough to keep a stripped session, and every fork of it, pinned at the pre-strip percentage. So stripping content alone does NOT move the meter: the stored numbers still describe the pre-strip size, the gauge stays pinned near 100%, and CC blocks new input client-side ("Context limit reached") even though the file is now tiny — the strip looks like it did nothing.
To fix this, strip-tools, strip-thinking, and strip-all now automatically rewrite the stale `usage` counts to the post-strip active-chain content estimate once stripping finishes. Every assistant turn whose recorded context exceeds the estimate is capped down to it (smaller turns are left alone), and the active chain's final assistant turn is pinned to the estimate — even if it recorded 0 (e.g. a blocked "Prompt is too long" turn). The next real turn re-establishes exact counts.
- Opt out with
--no-usage-reset. - Run it standalone with
reset-usageon an already-stripped session whose meter is still stuck (no need to re-strip). It also accepts--fork. - The estimate counts message content and attachments (which CC re-sends every request). It also counts the system prompt + tool schemas when the session recorded them (
prompt_snapshot, CC ≥ 2.1.267). Older sessions don't have that record, so for them the estimate is a floor. Either way, the first live turn corrects it precisely.
The gauge can only lie in your favour. Because the reset writes an estimate into the JSONL, a session that was barely stripped will still show a reassuring number — and then snap back to ~100% on the first real turn, which is the only number that was ever measured. If a strip freed little, the meter dropping is not evidence that it worked. Check whatanalyzesays is left, and treat a real API turn (one with non-zerocache_read/cache_creation) as the only ground truth. Attachments were excluded from this estimate until they were found to be worth six figures of tokens on long sessions — that omission made the gauge read far below the true prompt size.
Preserved thinking: an edit drops every later thinking block
On Claude Fable 5.1, Opus 5.5, Sonnet 5.5 and Haiku 5.5, each thinking block's signature binds it to the history before it. It also binds it to the previous thinking block.
If you edit an earlier turn, the API drops every later thinking block. Claude Code runs the API in drop mode, so the request still succeeds, unbilled. The model just continues without that reasoning.
Safe: removing thinking blocks from the front of the history, as in strip-thinking --from 0 --to N.
Drops every later thinking block:
- clearing tool results or tool inputs
- persisting tools, text, messages or ranges
- dropping attachments
- compacting a range or the whole front
- removing a thinking block from the middle
Every mutating command handles this for you. The code is in lib/preserved_thinking.py, and run_command calls it after each command:
- It compares the live original with the result. It prints how many later thinking blocks the API will drop, their size, and the first chain position.
- Dry runs preview this by running the command silently on a throwaway copy.
- By default it removes those blocks from the result. The file and the context gauge then match what the model actually sees.
--keep-dropped-thinkingkeeps them. They cost no tokens either way.- Older models such as Opus 4.8 don't run the check, and neither does Claude Mythos 5.1. The step stays silent for them.
Practical rule: strip once, and deeply. Each mid-session strip wipes the reasoning built up since the previous one.
What Gets Stripped
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/session-stripper/SKILL.md
Popularity
- Repository stars
- 43
- Repository forks
- 3
- 7-day trend score
- 19
Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 18 Mar 2026, 7 months ago.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/vicnaum/vics-agent-skills/session-stripper)