Published 2026-11-05
tag(s): #overblown-minor-annoyances #emacs
At work I have instances of Claude running in different repositories, many times for the same overarching task. For example: "load info from flat file in DB1, run transformation and store in DB2, update output step to include new data, enable output feed for a client". These happen in 3 or 4 different repos, depending on specifics. But the branch names are all the same, since they tie to the same user story.
I have a keymap for agent-shell commands, in which I included a little command to
pick another agent buffer using completing-read:[1]
;; init.el
(defun hoagie-switch-to-agent-shell ()
"Switch to an agent-shell-mode buffer."
(interactive)
(switch-to-buffer
(completing-read
"Switch to Agent: "
(mapcar #'buffer-name
(seq-filter (lambda (b)
(eq (buffer-local-value 'major-mode b)
'agent-shell-mode))
(buffer-list)))
nil t)))
;; user-lisp/bindings.el
(defvar-keymap hoagie-agent-shell-keymap
:doc "Keymap for agent-shell."
:name "Agent Shell"
"t" '("transient" . agent-shell-help-menu)
"b" '("switch to buffer" . hoagie-switch-to-agent-shell)
"p" '("previous-permission" . agent-shell-previous-permission-button)
"a" '("DWIM" . agent-shell))
(keymap-set hoagie-shortcut-keymap "a" hoagie-agent-shell-keymap)
(keymap-global-set "C-c a" hoagie-agent-shell-keymap)
This worked OK, except that the buffer names are all too similar: I ended up always requesting
completion just to pop the list to tell them apart. Which made me think I had to adjust
something, the point of having a command for quick switching, over just listing all buffers,
was to make things quicker.
I thought of two ways to solve this: uniquify buffer names more, or give up and display the
buffer list, and then filter by mode or name prefix.
An iterating on the second idea, what if I found a way to filter the buffer list
before popping it? 👀
I started checking out list-buffers, which happens to be a simple wrapper
over list-buffers-noselect. It is easy to filter the list, but it is impossible
to change the list format[2].
Then I remembered that a while back I switched to ibuffer because it is easier to
sort the buffer list by mode, and filter by name, with it. And looking at the function
signature for the command, everything I needed was right there! And it was lot
simpler than I expected:
(defun hoagie-list-buffers-agent-shell ()
"List agent-shell-mode buffers."
(interactive)
(ibuffer nil "*agent-shell-buffers*" '((mode . agent-shell-mode))
nil nil nil '((" " (name 50) " "))))
The first parameter is whether to pop the list in another window (nope!), then the buffer
name, a list of filters (in this case, only major mode), and bunch of other parameters not
relevant for this case (so, no to all). Finally the last parameter, very important, is the
format of the buffer list. Which lets me trivially show only the buffer name, and make the
column very wide.
Now this command is assigned to b in the agent-shell keymap. Early
usage tells me this isn't slower than what I used to do, and I switch to the wrong agent less
often. Win-win. Well, more like, even-win, right?
And because whenever you find a new toy, you inevitably think that it might be good solution
in other cases, I added a similar command to list all open chats in rcirc:
(defun hoagie-rcirc-list-buffers ()
"List rcirc-mode buffers."
(interactive)
(ibuffer nil "*rcirc-buffers*" '((mode . rcirc-mode)) nil nil nil
'((" " (name 50) " "))))
;; clobbers rcirc-browse-url
(keymap-set rcirc-mode-map "C-c C-b" #'hoagie-rcirc-list-buffers)
use-package!