Skip to content

joshjob42/agent-shell-rewind

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

agent-shell-rewind

Claude-Code-style time-travel for Emacs agent-shell: snapshot every agent turn, then rewind or fork a session's conversation and code together back to any earlier turn.

Two layers, joined by a tiny index:

  • Code — each turn becomes a jj commit; rewind restores the tree.
  • Conversation — the agent SDK's own session log is truncated to turn N and resumed, so the model faithfully continues as of that turn — no regeneration, and no protocol extension required. (It's essentially what Claude Code's own /rewind does, driven from Emacs.)

Everything is non-destructive (jj undo + the original session file recover the pre-rewind branch), and the resumed shell renders natively via session/load replay. DESIGN.md has the full design + the experiments that validated it.

Status: working end-to-end — conversation + code rewind/fork validated on real sessions.

Requires

  • Emacs + agent-shell (tested v0.57.3) + jj on PATH.
  • The session's project must be a jj repo (jj git init --colocate in a git repo, or M-x agent-shell-rewind-init, or set agent-shell-rewind-auto-init to t).

Use

(add-to-list 'load-path "~/Lab_notebook/agent-shell-rewind")
(require 'agent-shell-rewind)
(agent-shell-rewind-mode 1)   ; snapshot every agent turn, globally

Then in a jj-backed project, each agent turn adds a checkpoint labeled with the prompt that drove it (from the transcript) and anchored to both the jj change (code) and the SDK session leaf-uuid (conversation).

Three verbs

  • goto (agent-shell-rewind-goto-turn) — reset the code to turn N, in place. Conversation untouched. ("undo the files, keep talking")
  • rewind (agent-shell-rewind-rewind-turn) — unwind THIS shell to turn N in place (same buffer/window) — code and conversation — then continue. Non-destructive: the pre-rewind session stays on disk (recoverable via agent-shell-resume-session) and code is jj undo-able. ("take me back and go from there")
  • fork (agent-shell-rewind-fork-turn) — like rewind but into a NEW shell, leaving the original open too. ("explore an alternative without losing this one")

Both rewind and fork restore code (jj) + truncate the SDK session JSONL to turn N + resume with native session/load replay, so the shell looks like a normal agent-shell session.

Other commands

  • agent-shell-rewind-timeline — the table UI. Keys: g/RET goto · w rewind (in place) · f fork (new shell) · d diff · r refresh · q quit.
  • agent-shell-rewind-list — completing-read a turn; jumps the code there.
  • agent-shell-rewind-diffjj diff working copy vs turn N.
  • agent-shell-rewind-init — set up jj for the current project.

agent-shell-rewind-command-map is a prefix map; bind it, e.g.:

(with-eval-after-load 'agent-shell
  (define-key agent-shell-mode-map (kbd "C-c r") agent-shell-rewind-command-map))
;; C-c r  t timeline · l list · g goto(code) · w rewind(in place) · f fork(new shell) · d diff · i init

Note: the key maps are defvars, so on in-place reload use M-x eval-buffer (or restart Emacs) to pick up new bindings.

Everything is a jj operation → undoable with jj undo / jj op restore, and non-destructive (old turns stay in the DAG). Checkpoints live in $XDG_STATE_HOME/agent-shell/rewind/<project-hash>/checkpoints.jsonl.

How it works (one line)

turn-complete event → jj describe @ (labels the turn's tree) → capture change-id → jj new (fresh working copy for next turn) → append {turn, jj_change, jj_op, session_id, leaf_uuid, ...}. goto-turn = jj new <change>.

The join model (Tier-1 + Tier-2)

  • Code → jj (content-addressed, compact, jj undo-able). .gitignore keeps churn out.
  • Conversation → the SDK's own ~/.claude/projects/<enc-cwd>/<session-id>.jsonl (a parentUuid-chained DAG — the same store Claude Code's /rewind truncates). We don't copy it into jj; we truncate it to turn N + resume (proven faithful — the model reconstructs context from the truncated file, no regeneration, no ACP extension).
  • checkpoints.jsonl = the join index (turn ↔ jj_change ↔ leaf_uuid). Rewind/fork = restore code (jj) + truncate session to leaf_uuid + resume. Storage is a few MB/session (append-only logs diff cheaply); no per-checkpoint dirs.

Conversation display on resume (native look & feel)

The resumed shell renders the prior conversation with agent-shell's own rendering, via session/load full-replay — user turns at the Claude> prompt, agent responses below, exactly like a live session. The trick: agent-shell-session-restore-verbosity is read during the async load, so we setq it to full (a let-binding unwinds before the load fires — that was the earlier "flaky") and restore the old value on the new shell's prompt-ready (with a timer safety-net). agent-shell-rewind-restore-verbosity (default full) controls it. Validated: turns 1..N replay natively and verbosity auto-restores.

Next

  • Diffstat column in the timeline, checkpoint retention/pruning, named bookmarks.
  • Optional btrfs "nuke the whole workspace" layer for messy untracked-state projects.
  • A session/load-less fallback (inject the saved transcript at prompt-ready) for agents that don't advertise native replay.

Done

  • Snapshot-per-turn + checkpoint store + goto/restore (both directions) — validated.
  • Turn summaries pulled from the transcript's last ## User section.
  • Timeline UI (agent-shell-rewind-timeline) + prefix keymap.
  • Live end-to-end dogfood on real turn-complete events — real turns snapshotted a jj repo (summaries "go for it" / "check" from the live transcript); goto-turn reset the code back to turn 1 (files reverted, new file vanished) and forward to turn 2. The full event → handler → jj chain confirmed on real data, not mocks.
  • Tier-2 conversation rewind — VALIDATED. Truncated a real session JSONL at turn 2 and resumed it: the model recalled the early turns and correctly had no memory of the later turns we cut. Faithful, no regeneration, no ACP change. fork-turn wires this to jj code-restore for full conversation+code rewind.

Acknowledgements

This package was largely vibecoded in collaboration with Claude Fable 5.

License

MIT — see LICENSE.

About

Claude-Code-style rewind & fork for Emacs agent-shell: time-travel a session's conversation AND code together (jj + SDK session truncate/resume).

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors