Function: claude-code-ide--cleanup-session
claude-code-ide--cleanup-session is a natively compiled function
defined in claude-code-ide.el.
Signature
(claude-code-ide--cleanup-session SESSION &optional BUFFER-DYING)
Documentation
Clean up all state of SESSION when its Claude instance exits.
Idempotent per session: the sentinel, the terminal's kill-buffer hook and the kill-emacs sweep can all arrive here, and overlapping teardowns of SIBLING sessions must still proceed — a global in-progress flag would silently skip them.
BUFFER-DYING non-nil means we were called from the terminal buffer's
own kill-buffer-hook, so the buffer must NOT be killed again: a
nested kill would destroy it before the backend's remaining kill hooks
run, leaving them to fire on a dead buffer (ghostel's native-process
hook signals wrong-type-argument that way).
Source Code
;; Defined in /nix/store/yjvbhwl7p6zrbw9xlnni7y4hra8sa3pl-emacs-packages-deps/share/emacs/site-lisp/claude-code-ide.el
(defun claude-code-ide--cleanup-session (session &optional buffer-dying)
"Clean up all state of SESSION when its Claude instance exits.
Idempotent per session: the sentinel, the terminal's kill-buffer hook
and the kill-emacs sweep can all arrive here, and overlapping teardowns
of SIBLING sessions must still proceed — a global in-progress flag
would silently skip them.
BUFFER-DYING non-nil means we were called from the terminal buffer's
own `kill-buffer-hook', so the buffer must NOT be killed again: a
nested kill would destroy it before the backend's remaining kill hooks
run, leaving them to fire on a dead buffer (ghostel's native-process
hook signals wrong-type-argument that way)."
(when (and session
(not (claude-code-ide-mcp-session-cleanup-done session)))
(setf (claude-code-ide-mcp-session-cleanup-done session) t)
;; Close this session's open diffs first; their deferred verdicts can
;; never be delivered once the instance is gone, and the ediff
;; buffers/window configs would otherwise be orphaned
(when-let* ((active-diffs (claude-code-ide-mcp-session-active-diffs session)))
(let (tab-names)
(maphash (lambda (tab-name _diff-info) (push tab-name tab-names))
active-diffs)
(dolist (tab-name tab-names)
(condition-case err
(claude-code-ide-mcp--cleanup-diff tab-name session)
(error
(claude-code-ide-debug "Error cleaning up diff %s: %s" tab-name err))))))
;; Stop this instance's MCP server (closes the websocket server,
;; removes its lockfile, deregisters it). Best effort: a failure
;; here must not wedge the session half-cleaned.
(condition-case err
(claude-code-ide-mcp--stop-session session)
(error
(claude-code-ide-debug "Error stopping MCP session during cleanup: %s" err)))
;; Remove global advice and hooks when the last instance is gone.
;; Advice removal ignores the current customization values — they may
;; have changed since installation, and removing uninstalled advice is
;; a harmless no-op.
(when (= (hash-table-count claude-code-ide-mcp--sessions) 0)
(remove-hook 'window-selection-change-functions
#'claude-code-ide--note-window-selection)
(when (boundp 'tab-bar-tab-post-open-functions)
(remove-hook 'tab-bar-tab-post-open-functions
#'claude-code-ide--strip-new-tab-claude-windows))
(dolist (fn '(vterm--window-adjust-process-window-size
eat--adjust-process-window-size))
(when (fboundp fn)
(advice-remove fn #'claude-code-ide--terminal-reflow-filter)))
(when (fboundp 'vterm--filter)
(advice-remove 'vterm--filter #'claude-code-ide--vterm-smart-renderer)))
;; Notify MCP tools server about session end
(claude-code-ide-mcp-server-session-ended
(claude-code-ide-mcp-session-session-id session))
;; Kill this session's own terminal buffer. Never a name lookup: the
;; canonical name could resolve to a sibling instance's buffer.
(let ((buffer (claude-code-ide-mcp-session-buffer session)))
(when (and buffer (buffer-live-p buffer) (not buffer-dying))
(let ((kill-buffer-hook nil) ; Disable hooks to prevent recursion
(kill-buffer-query-functions nil)) ; Don't ask for confirmation
(kill-buffer buffer))
;; The kill above ran with hooks disabled, so the backend never got
;; a chance to terminate the CLI process — do it explicitly. When
;; BUFFER-DYING, the backend's own kill hooks handle the process.
(let ((process (claude-code-ide-mcp-session-process session)))
(when (and process (process-live-p process))
(ignore-errors (delete-process process)))))
;; Repoint the global MRU when it pointed at the killed buffer
(when (eq claude-code-ide--last-accessed-buffer buffer)
(setq claude-code-ide--last-accessed-buffer
(cl-loop for s in (claude-code-ide-mcp--active-sessions)
for b = (claude-code-ide-mcp-session-buffer s)
when (and b (buffer-live-p b)) return b))))
(claude-code-ide-debug "Cleaned up Claude Code session %s"
(claude-code-ide--session-display-name session))))