claude-pager
On <2026-06-03 Wed> I somehow bumped into idea to use emacsclient as EDITOR in Claude Code.
Turns out CC respects EDITOR env variable, either from environment or configured in ~/.claude/settings.json:
{"env": {"EDITOR": "emacsclient", "VISUAL": "emacsclient"}}
Even if [Feature Request] Allow configuring external editor for Ctrl-G prompt editing · Issue #18990 · anthropics/claude-code is still in open.
In that issue I spotted this project: gradigit/claude-pager. Fast C transcript pager for Claude Code: no blank Ctrl-G screen, clickable links/files, queued prompt composer. And its sister editor: gradigit/turbodraft.
Tried both. Turbodraft - I don't need another text editor rather than Emacs, so skipping it. But claude-pager looks useful, so I forked it.
First, I wired Emacs into claude-pager; so that it renders transcripts in both CC screen when it waits for the prompt + adds it to Emacs temporary buffer.
This mostly solves clunkiness of copying from claude terminal into Emacs and back - now I press {C-g} in claude and continue editing in Emacs.
Related to Agent SDK billing and parting ways with agent-shell.
This is how my env block looks like in ~/.claude/settings.json.
{
"env": {
"EDITOR": "/Users/pavel/.velppa/claude-pager/bin/claude-pager-open",
"CLAUDE_PAGER_EDITOR": "/Users/pavel/.velppa/claude-pager/emacs/claude-emacs-prompt",
"CLAUDE_PAGER_EDITOR_TYPE": "gui"
}
}
{C-g} in claude opens claude-pager-open that generates transcript and pastes it into both terminal and to the temp file to edit in Emacs, then runs claude-emacs-prompt shell file that calls elisp file which loads into the buffer, assigns keybindings etc.
Setup involves adding hooks:
SessionStart hook:
26: "command": "/Users/pavel/.velppa/claude-pager/shim/save-session-transcript.sh"
Then I rewrote the binary from C to Zig. Rewrite used Superpowers to develop a spec first, then launch a series of agents. It took 4+ hours for CC to finish rewrite, it was ready in the morning. It used all extra usage (42 EUR) in fast mode in first 20 minutes.
7000 loc in C => ??? in Zig.
Now I understand the C well.
Another interesting project from the same author:
- gradigit/confetti - Lightweight confetti animation for macOS — fire colorful confetti from your screen corners
elpa-find-file function
While researching internals of agent-shell I needed to quickly visit Elisp files by name. Here's the helper, which takes the file name and finds it within installed packages.
Examples:
- (elpa-find-file "agent-shell.el")
- (elpa-find-file "avy.el")
(defun elpa-find-file (pattern)
"Open first file matching PATTERN in the elpa directory.
Error if multiple versions of the same package match."
(interactive "sFile: ")
(let* ((elpa-dir (expand-file-name "elpa" user-emacs-directory))
(files (thread-first
"rg --files %s | grep -F \"%s\" | grep -v '\\.elc$'"
(format (shell-quote-argument elpa-dir) pattern)
shell-command-to-string
split-string))
(pkgs (mapcar (lambda (f)
(let ((dir (cadr (split-string f "/elpa/"))))
(replace-regexp-in-string
"-[0-9].*?/.*\\'" "" dir)))
files))
(dups (seq-filter (lambda (p)
(> (cl-count p pkgs :test #'string=) 1))
(seq-uniq pkgs))))
(when dups
(user-error "Multiple versions found for: %s — clean up %s"
(string-join dups ", ") elpa-dir))
(unless files
(user-error "file %s not found" pattern))
(find-file (cl-first files))))
Support to jumping to a :line:column
Okay, almost immediately I wanted something like this - (elpa-find-file "agent-shell.el:4616"), which is essentially what visit-source.el does. But, it turned out better not integrate visit-source, but implement parsing of line directly in the function.
So, version 2:
(defun elpa-find-file (pattern)
"Open file matching PATTERN in the elpa directory.
PATTERN may include a trailing :LINE or :LINE:COL.
Error if multiple versions of the same package match."
(interactive "sFile: ")
(let* ((parts (split-string pattern ":"))
(name (car parts))
(line (and (nth 1 parts) (string-to-number (nth 1 parts))))
(col (and (nth 2 parts) (string-to-number (nth 2 parts))))
(elpa-dir (expand-file-name "elpa" user-emacs-directory))
(files (thread-first
"rg --files %s | grep -F \"%s\" | grep -v '\\.elc$'"
(format (shell-quote-argument elpa-dir) name)
shell-command-to-string
split-string))
(pkgs (mapcar (lambda (f)
(let ((dir (cadr (split-string f "/elpa/"))))
(replace-regexp-in-string
"-[0-9].*?/.*\\'" "" dir)))
files))
(dups (seq-filter (lambda (p)
(> (cl-count p pkgs :test #'string=) 1))
(seq-uniq pkgs))))
(when dups
(user-error "Multiple versions found for: %s — clean up %s"
(string-join dups ", ") elpa-dir))
(unless files
(user-error "file %s not found" name))
(find-file (cl-first files))
(when line
(goto-char (point-min))
(forward-line (1- line))
(when col (forward-char (1- col))))))