ECA (Editor Code Assistant) Emacs is an AI-powered pair-programming client for Emacs.
Inspired by lsp-mode’s JSONRPC handling, it connects to an external eca server process to provide interactive chat, code suggestions, context management and more.
It's everything automatic and smooth as UX and re-usability across editors is the main goal of ECA.
For more details about ECA, check ECA server.
- Emacs 28.1 or later
- Custom
ecaserver binary, it's automatically downloaded ifeca-custom-commandisnil- Place it on your
$PATHor customizeeca-custom-command
- Place it on your
- whisper.el for Speech-to-Text support (optional)
M-x package-install eca
(package! eca :recipe (:host github :repo "editor-code-assistant/eca-emacs" :files ("*.el")))- Run
M-x ecato start the eca process and initialize the workspace.
- eca-emacs will check for
eca-custom-command; - if not set, will check for a
ecaon$PATH; - if not found, will download
ecaautomatically and cache it.
- The dedicated chat window
<eca-chat>pops up. - Type your prompt after the
>and press RET. - Attach more context auto completing after the
@.
eca-chat-add-context-at-point: Add the current function or selected lines to chat as context.eca-chat-add-file-context: Add the current file to chat as context.eca-keep-all-suggested-changes: Accept planned edit changes on file.eca-discard-all-suggested-changes: Reject planned edit changes on file.eca-chat-send-prompt: In case you wanna send prompts programatically in elisp.eca-chat-toggle-window: toggle chat window.
eca-extra-args: customize extra args to server to help debug like("--verbose")or("--log-level" "debug").eca-chat-usage-string-format: to customize what shows on mode-line for usage like costs and tokens.eca-chat-use-side-window: customize whether chat window is a side-window or a normal buffer.eca-chat-window-side: customize the chat window side.eca-chat-window-width: customize the chat window width.eca-chat-window-height: customize the chat window height.eca-chat-diff-tool: customize viewing diffs, defaults to ediff.
You can access transient_ menu with commonly commands via M-x eca-transient-menu or by pressing C-c . in eca's windows.
| Feature | key |
|---|---|
| Chat: clear | C-c C-l |
| Chat: reset | C-c C-k |
| Chat: talk | C-c C-t |
| Chat: Select behavior | C-c C-b |
| Chat: Select model | C-c C-m |
| Chat: Go to MCP details | C-c C-, |
| Chat: Accept next tool call | C-c C-a |
| Chat: Reject next tool call | C-c C-r |
| Chat: prev prompt history | C-↑ |
| Chat: next prompt history | C-↓ |
| Chat: go to prev block | C-c ↑ |
| Chat: go to next block | C-c ↓ |
| Chat: go to prev user msg | C-c C-↑ |
| Chat: go to next user msg | C-c C-↓ |
| Chat: toggle expandable content | C-c Tab |
| MCP: Go to chat | C-c C-, |
If you have whisper.el installed you can use the eca-chat-talk
command (or use the C-t keybinding) to talk to the Editor Code
Assistant. This will record audio until you press RET. Then, the
recorded audio will be transcribed to text and placed into the chat
buffer.
We recommend to use the small, it is a good trade-off between
accuracy and transcription speed.
(use-package whisper
:custom
(whisper-model "small"))Calling M-x eca with prefix C-u will ask for what workspaces to start the process.
Check before the server troubleshooting.
-
Verify environment: Check what environment variables are available to Emacs:
M-x eval-expression RET process-environment RET
-
Test ECA manually: Try running ECA from terminal to verify it works:
eca --help
-
Reset ECA: Clear the workspace and restart:
M-x eca-chat-reset M-x eca ; Start fresh
-
Check ECA installation: Verify ECA is available on your PATH or set
eca-custom-command:(setq eca-custom-command "/path/to/your/eca/binary")
-
Enable debug logging: Add extra arguments for debugging:
(setq eca-extra-args '("--verbose" "--log-level" "debug"))
-
Check environment variables: Test if your API keys are available in Emacs:
M-x eval-expression RET (getenv "ANTHROPIC_API_KEY") RET
Install and configure exec-path-from-shell to import your shell environment into Emacs:
(use-package exec-path-from-shell
:init
;; Specify the environment variables ECA needs
(setq exec-path-from-shell-variables
'("ANTHROPIC_API_KEY"
"OPENAI_API_KEY"
"OLLAMA_API_BASE"
"OPENAI_API_URL"
"ANTHROPIC_API_URL"
"ECA_CONFIG"
"XDG_CONFIG_HOME"
"PATH"
"MANPATH"))
;; For macOS and Linux GUI environments
(when (memq window-system '(mac ns x))
(exec-path-from-shell-initialize)))see - this comment
If Flyspell is causing slowdowns during LLM streaming, you can enable spell-checking only while typing and disable it on submit by adding this to your personal Emacs config:
(defun my/eca-chat-flyspell-setup ()
"Enable Flyspell during typing and disable on submit in `eca-chat-mode`."
(when (derived-mode-p 'eca-chat-mode)
;; Disable Flyspell when submitting prompts
(add-hook 'pre-command-hook
(lambda ()
(when (and (memq this-command '(eca-chat--key-pressed-return
eca-chat-send-prompt-at-chat))
flyspell-mode)
(flyspell-mode -1)))
nil t)
;; Re-enable Flyspell when typing
(add-hook 'pre-command-hook
(lambda ()
(when (and (eq this-command 'self-insert-command)
(not flyspell-mode))
(flyspell-mode 1)))
nil t)))
(add-hook 'eca-chat-mode-hook #'my/eca-chat-flyspell-setup)How it works:
Submit (Enter/Return): Disables Flyspell just before sending your prompt or programmatic send, preventing spell-checking overhead during streaming.
Typing: Re-enables Flyspell on any character insertion (self-insert-command), giving you real-time spell checking while composing.
Contributions are very welcome, please open a issue for discussion or pull request.
