copilot.el: one Copilot language server shared across every buffer
An unofficial Copilot plugin for Emacs.
At a glance
- What is it?
- copilot.el is an unofficial Emacs plugin for GitHub Copilot that talks JSON-RPC straight to the official copilot-language-server, with a single global process shared by all buffers rather than one server per project. The shared-server choice buys full protocol access and costs you a multiplexer, and the plugin is still versioned below 1.0.
- Who is it for?
- Adopt copilot.el if you already pay for GitHub Copilot, run Emacs 27 or newer, and want ghost text, chat and Next Edit Suggestions in the same client without a second completion stack fighting you. Do not adopt it expecting an LSP client: there is no eglot here, so diagnostics, formatting and code actions from that server are not part of the deal, and the version number is still 0.9.0.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 25 days ago.
- What is it written in?
- Mainly Emacs Lisp, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One global server, and why it skips eglot
The single most consequential design decision in copilot.el is stated in the second paragraph of the README and defended in doc/design.md: the plugin talks to the Copilot language server over JSON-RPC, using jsonrpc.el directly rather than going through an LSP client like eglot.
Two reasons are given. Most of the Copilot protocol is non-standard LSP, so a client that only speaks the standard would miss the interesting parts. And a single global server is shared across all buffers and projects, rather than one server per project root the way an LSP client would spawn. Everything else in the plugin follows from those two sentences.
The benefit is direct. Inline completions, the interactive chat interface and Next Edit Suggestions are all named as powered by the official @github/copilot-language-server, so the plugin is a front end rather than a reimplementation, and the protocol features it needs are not being filtered through a generic client's idea of what LSP is. The cost is equally direct: a general LSP client multiplexes servers for you, and a plain jsonrpc.el connection does not. The repository has a file named copilot-balancer.el sitting next to copilot.el, copilot-chat.el, copilot-menu.el and copilot-nes.el, and the README excerpt does not explain what it contains. A reader who wants to know how one server serves twenty buffers correctly should start there, then read doc/design.md for the reasoning.
What you give up by not using eglot is worth stating too. You are not getting eglot's diagnostics, formatting, code actions or symbol navigation from that same server, because those are eglot features and eglot is not in the path.
M-x copilot-install-server, and the Node.js question
After the package is installed, two interactive commands do the rest. M-x copilot-install-server puts the language server binary in place, and M-x copilot-login authenticates. The README is blunter than most about this sequence: Then run M-x copilot-install-server and M-x copilot-login. That's it.
The install command has a detail worth planning for. @github/copilot-language-server ships precompiled native binaries for macOS on both Apple Silicon and Intel, Linux on x64 and ARM64, and Windows on x64. When npm is not available, copilot-install-server automatically downloads and installs the native binary, so Node.js is not required at all. If you would rather install through npm, Node.js 22 or newer is needed. That is the whole dependency story: the plugin itself is pure Emacs Lisp, and Node.js enters the picture only if you override the default path.
The verification step is M-x copilot-diagnose, and the README explains its most common output: NotAuthorized means you do not have a valid subscription. That is a useful diagnostic precisely because it is unambiguous. Anything else it reports is about the server or the connection; NotAuthorized is about your account.
The remaining prerequisites are four MELPA packages, installed automatically: editorconfig, jsonrpc, compat and track-changes. The project requires Emacs 27 or higher. editorconfig and track-changes are the two that pull this plugin into your editing behaviour in ways you may not expect, since one reads project-level whitespace configuration and the other tracks buffer modifications, and neither is optional.
The completion map is where the real integration work is
The Quick Start config is short, and the keymap inside it is the part that will collide with whatever you already run.
(use-package copilot
:ensure t
:hook (prog-mode . copilot-mode)
:bind (:map copilot-completion-map
("<tab>" . copilot-accept-completion)
("TAB" . copilot-accept-completion)
("C-<tab>" . copilot-accept-completion-by-word)
("C-TAB" . copilot-accept-completion-by-word)
("C-n" . copilot-next-completion)
("C-p" . copilot-previous-completion)))Two keys do two different granularities of acceptance, word at a time or the whole completion, and C-n and C-p step through multiple candidates. The map is named rather than bound globally, which is deliberate: the keys only do anything while the suggestion map is active.
The Doom Emacs section is where the project's experience with real configurations shows through, because it does not stop at the config. It strongly recommends enabling the childframe option in the company module, written as (company +childframe), to prevent overlay conflict. Overlays are the failure mode: Copilot's ghost text and company's popup occupy the same screen region, and without a childframe one of them draws over the other.
The same section then handles Evil, where TAB is bound to insert a literal tab, by defining my/copilot-tab-or-default, a command that calls copilot-accept-completion when copilot-mode is bound and true and otherwise falls through to (evil-insert 1). This is the shape of a workaround, not a configuration, and the README offers it as one. If you run Evil and a completion package and want Copilot, expect to write something like it yourself.
Indentation is configured separately, through a list of mode and column pairs:
(add-to-list 'copilot-indentation-alist '(prog-mode 2))
(add-to-list 'copilot-indentation-alist '(org-mode 2))
(add-to-list 'copilot-indentation-alist '(text-mode 2))
(add-to-list 'copilot-indentation-alist '(clojure-mode 2))
(add-to-list 'copilot-indentation-alist '(emacs-lisp-mode 2))That this is a per-mode list rather than a single setting is a small warning: the plugin guesses indentation from the buffer, and where the guess is wrong you correct it mode by mode.
Six install recipes, and a plugin still numbered 0.9.0
The Installation section offers MELPA, an Emacs 30+ :vc recipe, straight and quelpa for Emacs 27 to 29, a manual load-path approach, Doom Emacs and Spacemacs. That is a lot of surface for one plugin, and reading it as a compatibility statement tells you something useful: the package is distributed on MELPA, on MELPA Stable, on JCS-ELPA and from source, and the source route is spelled out three times because there are three distinct ways Emacs Lisp projects get fetched.
The simplest is a two-line use-package form with :ensure t, or M-x package-install RET copilot RET. The manual route is a load-path entry and a require:
(add-to-list 'load-path "/path/to/copilot.el")
(require 'copilot)Spacemacs users get a github-copilot layer added to dotspacemacs-configuration-layers alongside auto-completion, which is the shortest path of the seven and the one with the fewest documented surprises.
The version numbers are the part to notice. The three most recent releases are v0.7.0 on 2026-06-26, v0.8.0 on 2026-07-04 and v0.9.0 on 2026-07-06. Three releases in eleven days, and all of them below 1.0. A pre-1.0 number in Emacs Lisp usually means the author is not yet promising interface stability, and copilot.el is young enough that the Configuration section is still growing. The README also points at a CHANGELOG.md at the repository root, so the detail behind those three tags is written down even though the release titles on the tag list are bare version strings.
Next Edit Suggestions inherit the shared server's constraints
Three features are named at the top of the README: inline completions as ghost text, an interactive chat interface, and Next Edit Suggestions. The first two are per-buffer concerns. The third is not, and that difference is where the single-global-server design has to do work the README does not describe.
A Next Edit Suggestion is a prediction about where your cursor goes next, which means it has to be right about state that is not in the current buffer: the file you are about to open, the symbol you just changed elsewhere, the test you just broke. The plugin implements it in copilot-nes.el, as a file separate from copilot.el, copilot-chat.el and copilot-menu.el. With one server shared across all buffers and projects, a suggestion computed against buffer A has to be discarded if you switch to buffer B before you accept it, and the README excerpt says nothing about how that invalidation works, whether suggestions survive a buffer switch, or whether they are recomputed lazily or discarded eagerly.
That is a documentation gap rather than a defect report, and it is the kind of gap that only experimentation can close. It is also the feature most worth closing, because a stale Next Edit Suggestion accepted in the wrong place is far more expensive than a wrong ghost text, which you can see before accepting.
The buffer-level controls for completions are documented: copilot-enable-predicates and copilot-disable-predicates decide when completions trigger, and copilot-enable-display-predicates and copilot-disable-display-predicates decide when they are displayed. The README excerpt does not list the equivalent controls for Next Edit Suggestions, so treat the first pair as the model for what to look for.
Unofficial, and the subscription is the real dependency
The repository description is two words: an unofficial Copilot plugin for Emacs. That word carries the practical weight of the whole project.
The README's note is unambiguous. You need access to GitHub Copilot to use this plugin, and the service introduced a free tier in early 2025. So the dependency chain runs through a commercial service and not through the plugin: copilot.el is MIT licensed, it is not the software providing the completions, and what it does is give you a way to talk to the official @github/copilot-language-server. If your Copilot access lapses, the plugin stops returning completions, and M-x copilot-diagnose will say so with NotAuthorized.
The free tier introduced in early 2025 changes the calculus and is the reason this kind of plugin exists in usable form at all, but it is a service decision rather than a guarantee. The MIT licence covers the plugin and its code, not your entitlement to the completions it retrieves, and a rate limit or a policy change on the service side is invisible in the repository.
The funding model is also visible in the README, through a GitHub Sponsors badge pointing at bbatsov, the same author credited in the MELPA and JCS-ELPA badges. Sponsorship is how the maintenance of this plugin is likely to be sustained, which is worth knowing if you depend on it: there is no commercial support contract, and there is a single obvious person behind the badges.
What the repository does contain for its own development is an Eask file, a test directory and a CI workflow at .github/workflows/test.yml, so the plugin is tested through Emacs's own tooling rather than through an external harness. That is the right choice for an Emacs Lisp package and it is also the only test signal available to you, since the suite is not a substitute for running M-x copilot-diagnose against a real account.
Editorial conclusion
Adopt copilot.el if you already pay for GitHub Copilot, run Emacs 27 or newer, and want ghost text, chat and Next Edit Suggestions in the same client without a second completion stack fighting you. Do not adopt it expecting an LSP client: there is no eglot here, so diagnostics, formatting and code actions from that server are not part of the deal, and the version number is still 0.9.0. Verify three things first, in this order. Run M-x copilot-install-server and confirm it took the native binary path rather than the npm path, since only the former avoids a Node.js 22 requirement. Then run M-x copilot-diagnose and confirm you do not get NotAuthorized, which is the cheapest way to find out whether your subscription covers this client. Then read the tab binding against whatever completion package you already run, because the README's own Doom and Spacemacs sections exist mostly to work around that collision. The plugin is a good fit for a single Emacs user on a paid Copilot plan and a poor substitute for a free local completion setup.
Frequently asked questions
How do I install copilot.el in Emacs?
The simplest route is MELPA, either with a two-line use-package form using :ensure t or with M-x package-install RET copilot RET. Emacs 30 and newer can also use a :vc recipe pointing at the GitHub repository, and Emacs 27 to 29 are covered by straight and quelpa recipes. After installing, run M-x copilot-install-server and then M-x copilot-login.
Does copilot.el need Node.js installed?
Not by default. copilot-install-server automatically downloads and installs a precompiled native binary when npm is not available, and binaries are provided for macOS on Apple Silicon and Intel, Linux on x64 and ARM64, and Windows on x64. Node.js 22 or newer is only needed if you choose to install the language server through npm instead.
What does M-x copilot-diagnose tell me?
It is the README's recommended way to verify that everything works after installing the server and logging in. The README calls out one specific output: NotAuthorized means you do not have a valid GitHub Copilot subscription, which is the check to run first if completions never appear.
How do I fix tab conflicts between copilot.el and company or Evil?
For company, the Doom Emacs section strongly recommends enabling the childframe option, written as (company +childframe), to prevent overlay conflict. For Evil, the README shows a custom command that calls copilot-accept-completion when copilot-mode is active and falls back to (evil-insert 1) otherwise, since Evil binds TAB to inserting a literal tab.
Does copilot.el work through eglot?
No, and the choice is deliberate. The plugin talks to the Copilot language server over JSON-RPC using jsonrpc.el directly rather than going through an LSP client, because most of the Copilot protocol is non-standard LSP, and because a single global server is shared across all buffers and projects. The full rationale is in doc/design.md.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/copilot-emacs-copilot-el)