30-second demo · click to play · or see docs/demo.cast in this repo
What is Excise?
Anthropic's own documentation observes that more than two corrections in a
single Claude Code session reliably poisons the trajectory — the agent starts
fighting ghosts of the cut-off path you abandoned. The fix today is
/clear (lose all context) or /compact (lose nuance). Excise is the
missing third option: a single-binary CLI that opens an interactive picker
over the session JSONL on disk, lets you cut the three turns that took the
agent down the dead end, and writes the file back with snapshot-and-undo
safety.
before after
┌──────────────┐ ┌──────────────┐
│ user │ │ user │
│ assistant │ │ assistant │
│ user │ ─▶ │ (excised) │
│ assistant ✗ │ │ (excised) │
│ user │ │ user │
│ assistant ✓ │ │ assistant ✓ │
└──────────────┘ └──────────────┘
The primitive Excise(Session, set<turn_id>) -> Session' preserves four
invariants: (1) removing a tool_use turn also removes its paired
tool_result, (2) removing a tool_result warns about the surviving
owner, (3) ordering and stable ids are preserved, (4) writes are
atomic (snapshot, write-to-tmp, rename).
Table of contents
Install
# via go
go install github.com/SuperMarioYL/excise/cmd/excise@latest
# via homebrew (after the tap is published — see BUILD_SETUP_NEXT_STEPS.md)
brew install supermarioyl/tap/excise
# from source
git clone https://github.com/SuperMarioYL/excise && cd excise
go build -o ./excise ./cmd/excise
Excise ships as a single ~8 MB static binary. No runtime, no daemon, no
network call.
Quick start
# Zero-arg: auto-discover the newest Claude Code session and open the picker
excise
# Render the turn table, no edits
excise list
# Open the picker on a specific session file
excise pick ~/.claude/projects/-home-me-app/SESSION-UUID.jsonl
# Non-interactive cut: remove turns 5-7 and 9
excise cut 5-7,9 ~/.claude/projects/-home-me-app/SESSION-UUID.jsonl
# Same, but for a Cursor session
excise --tool=cursor cut 12-14 "~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
# Restore the most recent snapshot
excise rollback --list
excise rollback <snapshot-id>
In the TUI:
| key |
action |
j / ↓ |
move down |
k / ↑ |
move up |
g / G |
jump to top / bottom |
space / x |
toggle mark on current turn |
d |
mark + move down |
enter |
commit the cut |
q / ctrl+c |
abort without changes |
The header live-updates turns: 42 → 39 tokens: ~18.2k → ~12.4k as you mark.
Commands
excise [path] open the TUI (auto-discover if no path)
excise list [path] print the turn table (no edits)
excise pick [path] open the TUI (alias for the bare command)
excise cut <range> [path] non-interactive; e.g. "5-7,9"
excise rollback --list list every snapshot, newest first
excise rollback <snapshot-id> restore one snapshot
global flags:
--tool auto|claude|cursor transcript format (default: auto)
--session PATH explicit session path
--force proceed despite dependency warnings
--dry-run show the diff but do not write
-y, --yes skip the confirmation prompt
How dependency-aware cutting works
A Claude Code (or Cursor) turn that issues a tool call creates a downstream
tool_result turn. If you cut the tool call but leave its result behind, the
agent's next turn references a tool_use_id that no longer exists and the
model gets confused.
Excise builds a dependency graph from every tool_use.id ↔ tool_result.tool_use_id
edge, computes the transitive closure of your marked set, and (a) pulls every
dependent result into the cut automatically (invariant 1), and (b) warns when
your selection would orphan a tool_result whose owner survives (invariant 2).
Pass --force to override the warning.
Snapshots and rollback
Every commit copies the source file to:
~/.excise/snapshots/<session-id>/<rfc3339-timestamp>.jsonl.gz
and appends a JSON line to ~/.excise/edit_log.jsonl recording what was
removed, when, and why. Snapshots older than 30 days are auto-pruned on the
next commit so the directory stays bounded.
excise rollback <snapshot-id> restores byte-for-byte. The original
destination is read from the edit log; override with --to <path> if you
moved the session file.
| Tool |
Path |
Read |
Write |
| Claude Code |
~/.claude/projects/<dir>/<uuid>.jsonl |
yes |
atomic rewrite of the original |
| Cursor |
~/Library/Application Support/Cursor/User/globalStorage/state.vscdb |
yes (via sqlite3 CLI, read-only) |
side-car .excised.jsonl to avoid corrupting an open db |
| Cursor (fixture export) |
*.jsonl of {"composerId":...,"bubble":{...}} lines |
yes |
atomic rewrite |
sqlite3 is required only for the Cursor sqlite branch. macOS ships it; on
Linux: apt install sqlite3. The Cursor writer deliberately refuses to mutate
state.vscdb while Cursor is running — v0.2 will add a "Cursor must be
closed" guard and a direct write path.
Architecture
┌────────────────────────────┐
│ cmd/excise/main.go │ cobra CLI: excise [pick|list|cut|rollback]
└────────────┬───────────────┘
│
┌─────────┴────────────────────────────┐
▼ ▼
internal/session internal/tui
loader.go Tool / Turn model model.go pure state + token math
claude.go Claude JSONL picker.go hand-rolled stdin driver
cursor.go Cursor sqlite+jsonl bubbletea.go real terminal UI
dependency.go tool-call graph diff.go before/after summary
writer.go WriterFor() dispatch
│
▼
internal/safety
backup.go snapshot + edit_log + rollback
One binary, no daemon, no IPC.
Roadmap
- v0.2 — direct
state.vscdb writes with a "Cursor closed?" guard; support for
multi-composer Cursor windows.
- v0.3 — Codex / Aider / Cline support behind the same primitive.
- v0.4 —
excise grep <regex> to mark by content match.
- v0.5 — a "session debugger" sidecar that lets you inspect tool-call
graphs without cutting anything.
Out of scope (on purpose)
- Web UI or hosted service. CLI/TUI only.
- Automatic poisoned-turn detection. You pick — we do not call an LLM.
- Prompt-cache reconciliation. Editing invalidates the cache; you accept the
cost.
- A Claude Code plugin. We operate on the on-disk file between sessions.
- Cloud sync, account system, team features.
- Cross-tool session portability — that is cli-continues'
niche, not ours.
- Telemetry of any kind, including opt-in.
Every refusal above keeps the demo under 30 seconds.
Contributing
Bug reports and PRs welcome. Please run go test -race ./... before
submitting. The hot path you most likely want to change is
internal/session/claude.go (schema tolerance) or internal/safety/backup.go
(snapshot policy). Anything bigger, open an issue first so we can agree on
scope.
License
MIT © 2026 SuperMarioYL