On this page#
- What the integration actually does
- The wiring
- The two things that break it
- Verifying it works
- Team considerations
- FAQ
What the integration actually does#
Claude Code in Emacs should mean one precise thing: an interactive Claude Code process runs in a terminal buffer whose working directory is the repository root. Emacs remains responsible for navigation and editing; Claude Code remains responsible for agentic work. It is not a replacement for Emacs commands, and it does not require a bespoke editor protocol.
Claude Code is an agentic coding tool that reads a codebase, edits files, and runs commands. The Emacs integration supplies the three operating conditions it needs:
- A real pseudo-terminal, or PTY, for interactive input and output.
- The correct repository as its working directory.
- The same executable and environment that work in your normal terminal.
That boundary matters. A thin integration is easier to debug than an Emacs package that intercepts prompts, rewrites output, or maintains a second representation of Claude’s session.
| What people expect | What the integration should actually do | |---|---| | Turn Emacs into a special Claude client | Run the official CLI in an Emacs terminal | | Automatically understand the active buffer | Start from the correct project root | | Apply changes through Emacs APIs | Let Claude Code edit repository files normally | | Share every Emacs environment detail | Inherit an explicit, inspectable process environment |
The terminal is the control plane; the repository is the shared state. When Claude changes a file, Emacs notices the on-disk change and can revert or refresh the buffer. That deliberately boring design follows the operating model described in the official Claude Code documentation.
Photo by cottonbro studio on Pexels.
The wiring#
The reliable setup is a small project-aware command built on Emacs’s terminal mode. It resolves the current project, creates one terminal buffer per repository, and launches Claude Code there. Do not invoke the agent through shell-command, an asynchronous pipe, or a compilation buffer: those interfaces are not interactive terminals.
1. Add a project-aware launcher#
Paste this into your Emacs configuration:
(require 'project)
(require 'term)
(defcustom pairfoundry-claude-command "claude"
"Executable used to start Claude Code."
:type 'string
:group 'tools)
(defun pairfoundry-claude-code ()
"Open Claude Code at the current project root."
(interactive)
(let* ((project (project-current t))
(root (project-root project))
(project-name
(file-name-nondirectory
(directory-file-name root)))
(buffer-name
(format "*claude-code:%s*" project-name))
(existing (get-buffer buffer-name))
(program (executable-find pairfoundry-claude-command)))
(unless program
(user-error
"Claude Code executable not found in Emacs: %s"
pairfoundry-claude-command))
(if (and existing
(get-buffer-process existing)
(process-live-p (get-buffer-process existing)))
(pop-to-buffer existing)
(when existing
(kill-buffer existing))
(let ((default-directory root))
(ansi-term (getenv "SHELL") buffer-name)
(term-send-raw-string
(format "exec %s\n"
(shell-quote-argument program)))))))This configuration makes the project root explicit instead of assuming the active buffer’s directory is safe. That distinction becomes important in monorepos, generated directories, and buffers that are not visiting files.
The launcher does not alter Claude Code’s own behavior. Authentication, permissions, commands, and repository interaction still belong to the official CLI described in the Claude Code overview.
2. Bind it to a key#
The right key is one your team will not confuse with evaluation or compilation. For example:
(global-set-key
(kbd "C-c a c")
#'pairfoundry-claude-code)Run M-x pairfoundry-claude-code if you prefer not to install a global binding. The function will reuse a live session for the same project and replace a dead terminal buffer.
3. Make external file changes visible#
Emacs must notice when Claude Code changes a file behind an existing buffer. Enable automatic reversion globally:
(global-auto-revert-mode 1)
(setq auto-revert-verbose nil)This is safe for unmodified buffers. If you have unsaved edits, Emacs protects them rather than silently replacing them. That protection is useful: two writers editing the same file without coordination is a workflow error, not something configuration should conceal.
For a stronger operating model—repository invariants, handoff rules, and agent boundaries—the Agent Operating Kit provides the layer that this launcher intentionally does not attempt to encode.
The two things that break it#
Two failures account for most broken claude code emacs setups: Emacs cannot see the executable, or Claude Code is launched without a real terminal. The first produces an immediate command error. The second is worse because the process may start, print output, and then fail only when it needs interactive input.
Failure 1: Claude Code executable not found in Emacs: claude#
This means Emacs and your terminal do not have the same process environment. It does not mean Claude Code is incorrectly installed.
Check inside Emacs:
(executable-find "claude")
(getenv "PATH")If the first expression returns nil, fix the environment used to start Emacs. When Emacs runs as a long-lived server, it can retain an old PATH even after your shell configuration changes. Every client connects to that stale server environment; opening another frame does not refresh it.
The clean fix is to restart the Emacs server from an environment where claude resolves. If policy requires an explicit executable, configure it directly:
(setq pairfoundry-claude-command
"/absolute/path/to/claude")Use the actual path from your managed installation. Do not copy somebody else’s path into a team configuration. Installation and authentication remain governed by the official Claude Code documentation.
Failure 2: standard input is not a terminal, frozen prompts, or raw control sequences#
These symptoms mean the CLI was connected to pipes instead of a PTY. Launching Claude Code with shell-command, start-process, or a compilation command can appear successful until it requests confirmation, changes terminal state, or renders an interactive prompt.
The fix is structural: use ansi-term, term, or another genuine terminal emulator. Do not patch over the problem with output filters or automatic newline injection.
This is the failure mode many integrations obscure. A process buffer is not automatically a terminal buffer. If the transport is wrong, prompt parsing is the wrong layer to debug.
Photo by ThisIsEngineering on Pexels.
Verifying it works#
A successful prompt is not sufficient verification. Confirm the executable, repository boundary, terminal transport, file-refresh behavior, and session reuse separately. That takes a few minutes and distinguishes a working integration from one that merely produced plausible output once.
Use this checklist:
- Open a tracked file and run
M-x pairfoundry-claude-code. - In the terminal, confirm the working directory is the intended repository root.
- Evaluate
(get-buffer-process (current-buffer))from the Claude terminal buffer and confirm it returns a live process. - Ask Claude Code to inspect a specific repository file without changing it.
- Ask it to make a small, reversible edit.
- Confirm the corresponding unmodified Emacs buffer refreshes.
- Invoke the launcher again and confirm it returns to the existing project session.
- Open another repository and confirm it receives a separate buffer.
The most dangerous false positive is a healthy session in the wrong directory. Claude Code can still answer questions and edit files, but it is operating against the wrong boundary. Project-root selection is therefore an invariant, not a convenience.
If your agent workflow is still informal, start with the free foundations track. Other packaged workflows are listed in the PairFoundry packs overview.
Team considerations#
For a team, standardize the launcher’s behavior but keep machine-specific paths, authentication, and permissions out of the shared Emacs file. The second developer usually fails at environment inheritance or repository-root assumptions—not at the Lisp itself. A team integration must make those assumptions visible and cheap to diagnose.
Commit the function and keybinding only if Emacs configuration is already shared by the team. Otherwise, publish the snippet beside a short contract:
claudemust resolve inside Emacs, not only inside a login shell.- Sessions start at the project root.
- One live terminal buffer is reused per repository.
- Unsaved Emacs buffers remain the developer’s responsibility.
- Claude Code permissions and authentication follow official policy.
- Developers must verify diffs before accepting agent changes.
Do not commit absolute executable paths. Do not make automatic buffer reversion override modified buffers. Do not add prompt-scraping logic unless the team is prepared to maintain a second interface contract on top of the CLI.
The larger practice is operational wiring: connecting an agent to real repository constraints without hiding the boundary. More patterns in that category live under Wiring It In.
Photo by Al Nahian on Pexels.
Related reading#
- Claude Code frontend-design plugin: what to check before you trust the connection
- Claude Code GitHub integration: what to check before you trust the connection
FAQ#
Should Claude Code receive the current file’s directory or the repository root?#
Use the repository root. The current file’s directory may be a nested package, generated tree, or temporary location. Starting at the root gives the agent the intended project boundary and makes session reuse predictable.
How is this different from an Emacs-specific Claude package?#
This approach runs the official CLI in a project-aware terminal. An Emacs-specific package may add prompt composition, buffer context, diff interfaces, or custom protocol handling. Those features can be valuable, but they also create more integration surface and more failure modes.
What should we do when Claude Code works in Terminal but not Emacs?#
Check (executable-find "claude") and (getenv "PATH") inside Emacs. If the executable is missing, restart the long-lived Emacs process from the correct environment or configure the approved absolute path locally.
How do I back out safely when the integration behaves incorrectly?#
Stop the terminal process, inspect the repository diff, and revert only the unwanted changes through your normal version-control workflow. Do not treat killing the Claude buffer as a rollback; file changes already written to disk remain there.
When should a team not use this setup?#
Do not use it when repository policy forbids agent access, when commands require controls the terminal session cannot enforce, or when developers cannot review resulting diffs. The launcher improves ergonomics; it does not replace permissions, isolation, review, or repository governance.