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- Server is already automatically downloaded for UX reasons unless you set
eca-custom-command
- Server is already automatically downloaded for UX reasons unless you set
- 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
@.
Server / process
eca: Starts eca server/session + open chateca-stop: Stop eca server/sessioneca-restart: Restart eca server/sessioneca-workspaces: print worskpace folders of current sessioneca-open-global-config: Open ECA global config file
Chat
eca-chat-toggle-window: Open/close chateca-chat-select: Select a chat for existing sessioneca-chat-new: Start a new chat for current session/workspace folders.eca-chat-rename: Rename current chateca-chat-clear: Clear chat content visually onlyeca-chat-reset: Deletes current chat and start a new oneeca-chat-select-model: Change chat modeleca-chat-select-behavior: Change chat behavioreca-chat-cycle-behavior: Change chat behavior to next availableeca-chat-add-context-to-system-prompt: Add file/dirs to system prompt checking multiple modes with range supporteca-chat-add-context-to-user-prompt: Add file/dirs to user prompt checking multiple modes with range supporteca-chat-add-filepath-to-user-prompt: Add filepath mention only to user prompt checking multiple modes with range supporteca-chat-drop-context-from-system-prompt: Drop a context from system prompteca-chat-send-prompt: Send a prompt in chat interactivelyeca-chat-send-prompt-at-chatOpen chat and send any prompt written thereeca-chat-clear-prompt: Clear written prompt in chateca-chat-repeat-prompt: Repeat a previously sent prompteca-chat-stop-prompt: Stop a running prompt in chateca-chat-tool-call-accept-all: Acceppt all pending tool calls in chateca-chat-tool-call-accept-next: Acceppt next pending tool call in chateca-chat-tool-call-reject-next: Reject next pending tool call in chateca-chat-timeline: Show user prompt history as a timelineeca-chat-talk: Use whisper.el to send a prompt via voice.
Server / process
eca-custom-command: Theecaserver command; when nil ECA auto-downloads or usesecafrom$PATH.eca-server-download-method: Method to download server (curlorurl-retrieve, Emacs built-in way).eca-server-download-url: Custom URL to download the ECA server archive.eca-server-install-path: Path where the downloaded ECA server binary is installed.eca-server-version-file-path: Path to the file storing the downloaded ECA server version.eca-unzip-script: Script/command template used to unzip the downloaded ECA server archive.eca-extra-args: Extra args to pass to the ECA server, e.g.("--verbose")or("--log-level" "debug").eca-min-gc-cons-threshold: Temporary GC threshold used while processing heavy server messages.
Core / session
eca-before-initialize-hook: Functions called before an ECA session is initialized.eca-after-initialize-hook: Functions called after an ECA session is initialized.eca-find-root-for-buffer-function: Function used to determine the workspace root for the current buffer.
Chat
eca-chat-mode-hook: Hooks to run after enteringeca-chat-mode.eca-chat-use-side-window: Whether the chat buffer is displayed in a side window or a normal window.eca-chat-window-side: On which side (left,right,top,bottom) the chat side window appears.eca-chat-window-width: Width of the chat side window when on the left or right.eca-chat-window-height: Height of the chat side window when on the top or bottom.eca-chat-focus-on-open: Whether to focus the chat window when it opens.eca-chat-auto-add-repomap: Whether to automatically include repoMap context when opening ECA.eca-chat-auto-add-cursor: Whether to automatically track the cursor position and add it as context.eca-chat-cursor-context-debounce: Seconds to debounce updates when tracking cursor context.eca-chat-prompt-separator: Separator string between the chat content and the prompt area.eca-chat-prompt-prefix: Prompt prefix string shown before user input.eca-chat-prompt-prefix-loading: Prompt prefix string while a request is in progress.eca-chat-context-prefix: Prefix used for context references in the chat buffer (default@).eca-chat-filepath-prefix: Prefix used for file path references in the chat buffer (default#).eca-chat-expandable-block-open-symbol: Symbol used for expandable blocks in open state.eca-chat-expandable-block-close-symbol: Symbol used for expandable blocks in closed state.eca-chat-mcp-tool-call-loading-symbol: Symbol used for MCP tool calls while loading.eca-chat-mcp-tool-call-error-symbol: Symbol used for MCP tool calls when they fail.eca-chat-mcp-tool-call-success-symbol: Symbol used for MCP tool calls when they succeed.eca-chat-expand-pending-approval-tools: Whether to auto-expand tool calls that are pending approval.eca-chat-shrink-called-tools: Whether to auto-shrink tool calls after they have been executed.eca-chat-custom-model: Override the model used for chat (nil = server default).eca-chat-custom-behavior: Override the chat behavior (nil = server default).eca-chat-usage-string-format: Controls what usage information (tokens/costs/limits) is shown in the mode-line.eca-chat-diff-tool: How to show file diffs from chat (smergeorediff).eca-chat-tool-call-prepare-throttle: Throttle strategy fortoolCallPrepareevents (allorsmart).eca-chat-tool-call-prepare-update-interval: When usingsmartthrottle, process every NthtoolCallPrepareupdate.eca-chat-tool-call-approval-content-size: Face height used for tool call approval UI text.
Completion
eca-completion-idle-delay: Idle delay before triggering inline completion (0 = immediate, nil = disabled).
Rewrite
eca-rewrite-prompt-prefix: Text automatically prefixed to rewrite prompts.eca-rewrite-finish-prefix: Prefix text shown in the buffer when a rewrite finishes.eca-rewrite-diff-tool: Diff tool for rewrite overlays (simple-difforediff).eca-rewrite-finished-action: Action to take when a rewrite finishes (show-overlay-actions,accept,diff,merge).eca-rewrite-on-finished-hook: Hook run after a rewrite finishes, receiving the overlay as argument.
MCP
eca-mcp-details-position-params: Display parameters for the MCP details side window.
API / requests
eca-api-response-timeout: Maximum time to wait (seconds) for synchronous API responses.eca-api-request-while-no-input-may-block: If non-nil,eca-api-request-while-no-inputmay block even whennon-essentialis set.
UI / misc
eca-buttons-allow-mouse: Whether ECA buttons can be clicked with the mouse.
You can access the transient menu with common commands via M-x eca-transient-menu or by pressing C-c . in ECA 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-S-b |
| Chat: cycle behavior | C-c C-b |
| Chat: select model | C-c C-m |
| Chat: new chat | C-c C-n |
| Chat: select chat | C-c C-f |
| Chat: repeat last prompt | C-c C-p |
| Chat: clear prompt | C-c C-d |
| Chat: timeline | C-c C-h |
| Chat: send prompt at chat buffer | C-c C-RET |
| Chat: accept all pending tool calls | C-c C-a |
| Chat: accept next pending tool call | C-c C-S-a |
| Chat: accept all tool calls and remember | C-c C-s |
| Chat: reject next pending tool call | C-c C-r |
| Chat: rename chat | C-c C-S-r |
| Chat: prev prompt history | C-↑ |
| Chat: next prompt history | C-↓ |
| Chat: go to prev tool / diff / reason block | C-c ↑ |
| Chat: go to next tool / diff / reason block | C-c ↓ |
| Chat: go to prev user message | C-c C-↑ |
| Chat: go to next user message | C-c C-↓ |
| Chat: toggle expandable content at point | C-c Tab |
| Chat: open transient menu | C-c . |
| Chat: go to MCP details | C-c C-, |
| MCP: go to chat (from MCP details buffer) | C-c C-, |
| MCP: open transient menu (from MCP details) | C-c . |
Check detailed features here.
Select a text and call eca-rewrite, after rewrite is finish, call any action on the overlay.
Enable eca-completion-mode and call eca-complete.
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" "server"))
-
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.
