Function: claude-code-ide--start-session
claude-code-ide--start-session is a natively compiled function defined
in claude-code-ide.el.
Signature
(claude-code-ide--start-session &optional CONTINUE RESUME)
Documentation
Start a new Claude Code instance for the current project.
If CONTINUE is non-nil, start Claude with the -c (continue) flag. If RESUME is non-nil, start Claude with the -r (resume) flag.
Always creates a new instance; a project may run any number of them concurrently. When the project already has instances (or with a prefix argument), prompts for an optional instance name.
This function handles:
- CLI availability checking
- Dead session cleanup
- New session creation with a per-instance MCP server
- Process and buffer lifecycle management
Source Code
;; Defined in /nix/store/yjvbhwl7p6zrbw9xlnni7y4hra8sa3pl-emacs-packages-deps/share/emacs/site-lisp/claude-code-ide.el
(defun claude-code-ide--start-session (&optional continue resume)
"Start a new Claude Code instance for the current project.
If CONTINUE is non-nil, start Claude with the -c (continue) flag.
If RESUME is non-nil, start Claude with the -r (resume) flag.
Always creates a new instance; a project may run any number of them
concurrently. When the project already has instances (or with a
prefix argument), prompts for an optional instance name.
This function handles:
- CLI availability checking
- Dead session cleanup
- New session creation with a per-instance MCP server
- Process and buffer lifecycle management"
(unless (claude-code-ide--ensure-cli)
(user-error "Claude Code CLI not available. Please install it and ensure it's in PATH"))
;; Clean up any dead sessions first
(claude-code-ide--cleanup-dead-sessions)
;; Ensure the selected terminal backend is available before starting MCP
(claude-code-ide--terminal-ensure-backend)
(let* ((working-dir (claude-code-ide--get-working-directory))
;; Additional instances get an optional name; a prefix argument
;; offers the prompt for the first instance too
(instance-name (when (or (claude-code-ide-mcp--sessions-for-project working-dir)
current-prefix-arg)
(claude-code-ide--read-instance-name working-dir)))
;; A live instance may already own the base name — e.g. two
;; projects sharing a basename both render as *claude-code[proj]*.
;; Uniquify instead of clobbering or refusing.
(buffer-name (let* ((base (claude-code-ide--instance-buffer-name working-dir instance-name))
(existing (get-buffer base)))
(if (and existing (claude-code-ide--buffer-session existing))
(generate-new-buffer-name base)
base)))
(session-id (claude-code-ide--generate-session-id working-dir))
(session nil)
(registered nil))
(condition-case err
(progn
;; Start this instance's MCP server
(setq session (claude-code-ide-mcp-create-session working-dir session-id instance-name))
(setf (claude-code-ide-mcp-session-window-slot session)
(claude-code-ide--assign-window-slot working-dir))
;; Register with the MCP tools server BEFORE spawning the CLI so
;; an early /mcp/<session-id> request finds its context
(claude-code-ide-mcp-server-session-started session-id working-dir nil)
(setq registered t)
(let* ((port (claude-code-ide-mcp-session-port session))
(buffer-and-process (claude-code-ide--create-terminal-session
buffer-name working-dir port continue resume session-id))
(buffer (car buffer-and-process))
(process (cdr buffer-and-process)))
(setf (claude-code-ide-mcp-session-buffer session) buffer
(claude-code-ide-mcp-session-process session) process)
(claude-code-ide-mcp-server-update-session-buffer session-id buffer)
(with-current-buffer buffer
(setq-local claude-code-ide--session session))
;; Install global terminal advice for the first live instance
(claude-code-ide--maybe-install-global-advice)
;; Set up process sentinel to clean up when Claude exits.
;; The ghostel backend stashes its native sentinel on the
;; process so we can chain it here — otherwise ghostel's
;; buffer-local timers and focus-change hook never tear down.
(let ((prev-sentinel (process-get process 'claude-code-ide--ghostel-sentinel)))
(set-process-sentinel process
(lambda (proc event)
(when prev-sentinel
(ignore-errors (funcall prev-sentinel proc event)))
;; Check for abnormal exit with error code
(when (string-match "exited abnormally with code \\([0-9]+\\)" event)
(let ((exit-code (match-string 1 event)))
(claude-code-ide-debug "Claude process exited with code %s, event: %s"
exit-code event)
(message "Claude exited with error code %s" exit-code)))
(when (or (string-match "finished" event)
(string-match "exited" event)
(string-match "killed" event)
(string-match "terminated" event))
(claude-code-ide--cleanup-session session)))))
;; Also add buffer kill hook as a backup. The buffer-dying
;; flag stops cleanup from nested-killing the buffer while
;; the backend's own kill hooks still have to run on it.
(with-current-buffer buffer
(add-hook 'kill-buffer-hook
(lambda ()
(claude-code-ide--cleanup-session session 'buffer-dying))
nil t)
;; Set up terminal keybindings
(claude-code-ide--setup-terminal-keybindings)
;; Add terminal-specific exit hooks
(cond
((eq claude-code-ide-terminal-backend 'vterm)
;; Add vterm exit hook to ensure buffer is killed when process exits
;; vterm runs Claude directly, no shell involved
(add-hook 'vterm-exit-functions
(lambda (&rest _)
(when (buffer-live-p buffer)
(kill-buffer buffer)))
nil t))
((eq claude-code-ide-terminal-backend 'eat)
;; eat uses kill-buffer-on-exit variable
(setq-local eat-kill-buffer-on-exit t))))
;; Stabilization period for terminal layout initialization
(sleep-for claude-code-ide-terminal-initialization-delay)
;; The CLI can die within the stabilization delay (e.g. it
;; failed to exec in the terminal backend's environment), in
;; which case the exit sentinel has already killed the buffer.
;; Displaying the dead buffer would surface only as a cryptic
;; wrong-type-argument, so fail with a real explanation.
(unless (and (buffer-live-p buffer) (process-live-p process))
(error "Claude Code exited immediately after startup. Verify that `claude-code-ide-cli-path' (%s) is executable in the %s backend's environment"
claude-code-ide-cli-path claude-code-ide-terminal-backend))
;; Display the buffer in a side window
(claude-code-ide--display-buffer-in-side-window buffer)
(claude-code-ide-log "Claude Code %sstarted in %s with MCP on port %d%s"
(cond (continue "continued and ")
(resume "resumed and ")
(t ""))
(claude-code-ide--session-display-name session)
port
(if claude-code-ide-cli-debug " (debug mode enabled)" ""))
;; Delayed so the startup message above stays readable first
(run-with-timer 2 nil #'claude-code-ide--maybe-recommend-ghostel)))
((error quit)
;; Session creation failed (or was quit) - tear down only THIS
;; instance; a directory-wide stop would kill sibling instances'
;; servers. Once a terminal buffer exists, the full per-session
;; cleanup also kills it and its process — leaving it around would
;; orphan a running CLI nothing tracks anymore.
(unless (and session (claude-code-ide-mcp-session-cleanup-done session))
(if (and session (claude-code-ide-mcp-session-buffer session))
(claude-code-ide--cleanup-session session)
(when registered
(claude-code-ide-mcp-server-session-ended session-id))
(when session
(claude-code-ide-mcp--stop-session session))))
;; Re-signal the error with improved message
(signal (car err) (cdr err))))))