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))))