Minimal vanilla Emacs configuration.
This repository is meant to live at ~/.emacs.d.
init.el: handwritten Emacs configurationlisp/: first-party byte-compilable configuration libraries.gitignore: local Emacs state and generated files to keep out of gitscripts/setup.el: fresh-machine dependency bootstrapscripts/compile.el: byte-compilation helper for first-party ELispscripts/host.el: host-level external tool helperscripts/user.el: user shell, editor, PATH, terminal, and MCP client helpertests/init-test.el: ERT tests for the configAGENTS.md: shared project instructions for coding agentsCLAUDE.md: Claude Code adapter that imports the shared agent instructions.cursor/rules/emacs-config.mdc: Cursor project rule for the same shared context
- MELPA, GNU ELPA, and Nongnu ELPA package archives
- Completion and search support: Vertico, Orderless, Marginalia, Consult, Embark, Which-Key, Corfu, and CAPE for fast minibuffer selection, in-buffer completion, project search, and discoverable command prefixes
- Diagnostics and navigation support: consistent Flymake, Xref, Eglot, Consult-Imenu, references, rename, and code-action bindings across language modules
- Project environment support: envrc/direnv integration so repository-local
.envrcfiles can provide toolchain paths and environment variables to Emacs - Project command runner: generic project-root
compilecommands detect common Make, Node, Cargo, and Python project commands - Navigation memory: bookmarks persist under
var/, and registers have first-class keys for saved points, text snippets, and window/frame layouts - Developer notes: Org capture templates under
C-c okeep tasks, notes, decisions, and debug logs in local runtime files undervar/notes/ - Editing hygiene: generated backup/auto-save files under
var/, save-place, auto-revert, long-line protection, delete-selection behavior, and code-buffer whitespace cleanup - Visual undo: Vundo on
C-x uandC-c uexposes the undo history as a navigable tree while preserving built-in undo commands - Platform integration: guarded macOS, GNU/Linux, WSL, and Windows defaults for modifier keys, Dired, external open/reveal commands, terminal clipboards, browser launchers, trash behavior, and shell selection
- Terminal integration: OSC 52 clipboard copy for terminal frames, including
SSH sessions where local OS clipboard tools would run on the wrong host,
plus
with-editorsupport for shells launched from Emacs, optional shell-editor integration, and xterm-compatible mouse support - Workspace ergonomics: Winner history, directional window movement, and project-named tab-bar workspaces without a separate workspace framework
- Dired file management: project-root Dired entry points, Wdired bulk rename, hidden generated files, DWIM copy targets, and recursive copy/delete defaults
- Buffer management: Ibuffer replaces the flat buffer list with grouped development, Dired, Magit, terminal, help, and Emacs internal buffers
- Version-control ergonomics: Ediff uses a single-frame layout, Smerge gets conflict-resolution keys, and diff-hl shows changed lines when installed
- Snippet support: Yasnippet with small repo-owned templates for common test and source skeletons
- Agentic workflow support: project-root Codex, Claude Code, and Cursor Agent launch helpers, project buffer saving, and copyable file/region/project context commands
- MCP endpoint support: external agents can connect to a running Emacs session
through
mcp-server-libandelisp-dev-mcpfor read-only Elisp docs, definitions, variable metadata, Info lookup, and source inspection - Debugging support: optional Dape keybindings for Debug Adapter Protocol sessions across language-specific adapters
- macOS GUI Emacs shell-environment import so Homebrew tools are visible from Emacs.app
- Magit via
C-c g - vterm via
C-c t, plus project-root vterm viaC-c T - Emacs Lisp development support: Helpful, Paredit, Rainbow Delimiters, Aggressive Indent, Eros, Macrostep, Package-Lint, and Flymake integration
- C/C++ support: clangd through Eglot, Corfu completion, clang-format, CMake mode, project build/debug helpers, and modes for linker scripts, assembly, and GDB command files
- Verilog/SystemVerilog support: built-in verilog-mode, project-aware build/lint helpers, optional Verible formatting and language-server support, and Verilator/Icarus lint fallbacks
- SQL support: sql-mode and SQLi connections, query scratch buffers, sqlformat, sqlup-mode, sql-indent, and optional SQL language-server support
- Rust support: rust-mode with tree-sitter upgrade path, rust-analyzer through Eglot, rustfmt-on-save, Cargo helpers, and Cargo.toml support
- JavaScript/TypeScript support: JS, TS, TSX, JSON, TypeScript language server through Eglot, project-local Prettier formatting, node_modules/.bin, and package-manager script helpers
- Markup and data-file support: Markdown/GFM editing and preview hooks, Mermaid diagram rendering commands, YAML with optional language-server support, and JSON helpers for jq and JSON language-server support
- Python support: virtualenv discovery, optional Eglot language-server support, ruff/black formatting, pytest and ruff project commands, Python REPL helpers, and requirements/pyproject support
- Tree-sitter grammar management: inspect configured grammars and install one or all missing grammars from Emacs
init.el is intentionally small. It adds lisp/ to load-path, requires the
first-party modules, and loads custom.el last. Stable behavior lives in normal
libraries:
config-package.el: package archives, priorities, anduse-packageconfig-ui.el: basic interface defaults and Helpfulconfig-editing.el: editing state, generated-file locations, auto-revert, and code-buffer whitespace hygieneconfig-undo.el: Vundo visual undo tree bindings and display defaultsconfig-platform.el: OS-specific host integration guarded bysystem-typeconfig-terminal.el: terminal-frame behavior for SSH, optional CLI editor integration, and terminal input/clipboard detailsconfig-project.el: project root helpersconfig-project-commands.el: generic project-root command runner using compilation buffersconfig-navigation.el: persistent bookmarks and register-based navigationconfig-notes.el: Org capture, agenda files, and local developer notesconfig-workspace.el: window history, directional movement, and tab-bar project workspacesconfig-files.el: Dired, Dired-X, Wdired, and project file-management defaultsconfig-buffers.el: Ibuffer grouping and buffer-list defaultsconfig-completion.el: minibuffer completion, in-buffer completion, project search, command discovery, and action menusconfig-snippets.el: Yasnippet setup and repo-owned snippet directoryconfig-diagnostics.el: diagnostics, code navigation, references, rename, and language-server action bindingsconfig-debug.el: optional Dape/DAP debugging controlssnippets/: first-party snippets loaded byconfig-snippets.elconfig-environment.el: direnv/envrc project environment loadingconfig-tools.el: vterm and Magitconfig-vc.el: Ediff, Smerge, and diff-hl version-control ergonomicsconfig-mcp.el: MCP stdio bridge helpers and Elisp development MCP toolsconfig-agent.el: agent provider registry plus project-root terminal helpersconfig-elisp.el: Emacs Lisp development supportconfig-c.el: C, C++, CMake, compile, format, and debug supportconfig-verilog.el: Verilog/SystemVerilog editing, formatting, lint, and language-server supportconfig-sql.el: SQL editing, formatting, scratch buffers, and SQLi connection helpersconfig-rust.el: Rust, Cargo, rustfmt, rust-analyzer, and Cargo.toml supportconfig-js.el: JavaScript, TypeScript, TSX, JSON, Prettier, package scripts, and TypeScript language-server supportconfig-markup.el: Markdown, Mermaid, YAML, and extra JSON supportconfig-python.el: Python, virtualenvs, pytest, ruff/black, Eglot, and requirements filesconfig-treesit.el: tree-sitter grammar status and install helpers
This config is easiest to use as a set of prefix-driven workbenches. Which-Key will usually show the next command after a short pause, so start with the prefixes and let Emacs remind you of the leaves.
- Use
C-c pfor project work.C-c p ffinds files,C-c p gsearches with ripgrep,C-c p bswitches project buffers,C-c p cruns a detected build, test, lint, or run command throughcompile, andC-c p Crepeats the last project command. - Treat compilation buffers as the normal command output surface. Build, test,
lint, and language helper commands use clickable diagnostics where possible,
and
recompilekeeps the edit-run loop short. - In programming buffers, the local bindings are intentionally similar across
languages:
C-c estarts or reconnects Eglot,C-c fformats,C-c bbuilds or compiles,C-c ttests, andC-c rruns when the language has a natural run command. Verilog/SystemVerilog usesC-c lfor linting, and SQL keeps the traditionalC-c C-*SQLi bindings. - Use
C-c xfor code intelligence across languages: actions, definitions, references, rename, and Imenu. These bindings work even when a language module adds its own local build/test keys. - Use
C-c a ?as the agent control plane.C-c a pcopies project context,C-c a Popens it for review,C-c a a/d/ulaunch Codex, Claude Code, or Cursor Agent from the project root, andC-c a m sstarts the Emacs MCP endpoint for external agents. - Use
C-c Tfor a project-root vterm andC-c tfor a plain vterm. On terminal-heavy machines, runmake user USER_INSTALL=1for PATH and tmux setup. AddUSER_EDITOR_COMMAND='emacs -nw'or your preferred editor command only when you want this repo to manage shell editor variables too. - Use Dired as a file-management buffer, not just a browser.
C-c f popens the project root,C-c f djumps to the current file,.toggles omitted generated files,(toggles long listing details, andC-c C-eswitches to Wdired for bulk renames. - Use
C-c gfor Magit, and use theC-c vSmerge keys when a conflict buffer is active. Ediff and Smerge are tuned to keep merge work inside one coherent frame. - Use
C-c nfor durable navigation with bookmarks and registers,C-c wfor window and tab workspaces, andC-x uwhen you want Vundo's visual undo tree. - Use
C-c ofor local developer notes. Captured tasks, notes, decisions, and debug logs live undervar/notes/, so they are useful on the machine but stay out of the public config repo. - Use
C-c Efor direnv/envrc project environments andC-c lto inspect or install tree-sitter grammars. - On a new machine, after stock Emacs is installed, run
make host,make user,make setup, andmake testin that order. UseHOST_INSTALL=1,USER_INSTALL=1, andUSER_MCP_INSTALL=1only when you want the helpers to modify the host or user environment.
Project-level agent instructions live in AGENTS.md. Keep that file concise,
generic, and safe for a public repository: project layout, verification
commands, edit boundaries, and maintenance workflows belong there; personal
preferences, private paths, credentials, and employer-specific details do not.
Claude Code reads CLAUDE.md, which imports AGENTS.md and adds only thin
Claude-specific notes. Cursor reads .cursor/rules/emacs-config.mdc, an
always-on project rule that points back at the same canonical instructions.
This keeps Codex, Claude, Cursor, and other agents aligned without duplicating
the full guidance in multiple files.
Local personal agent notes should stay untracked. CLAUDE.local.md,
.claude/settings.local.json, and .cursor/rules/*.local.mdc are ignored by
git for that reason.
The C-c a prefix gathers agent-facing workflows in one place. C-c a ?
opens a Transient control surface for launching agents, gathering context,
opening Magit, and jumping into a project terminal. C-c a a launches Codex,
C-c a d launches Claude Code, C-c a u launches Cursor Agent, and C-c a A
prompts for any configured provider. All launchers start in the current project
root and save modified project buffers first when
my/agent-save-project-buffers-before-launch is non-nil.
Use C-c a p to copy generated project context for an agent, or C-c a P to
open that context in a review buffer. The generated context includes the project
root, git branch/status, and project instruction files such as AGENTS.md,
CLAUDE.md, Cursor rules, and README.md.
External agents can also reach into Emacs through MCP. make setup installs
the mcp-server-lib and elisp-dev-mcp packages, installs the local stdio
bridge at ~/.emacs.d/emacs-mcp-stdio.sh, and keeps that generated script out
of git. In the Emacs session you want agents to reach, run M-x my/mcp-start;
it starts the Emacs server needed by the stdio bridge and then enables the MCP
tools. Stop it with M-x my/mcp-stop when done.
The elisp-dev-mcp server is intentionally read-focused. It exposes tools for
Elisp function docs, function definitions, variable metadata without values,
Info node lookup, and source reads from trusted Emacs/package directories. This
config also allows first-party source under lisp/, scripts/, and tests/.
Useful bindings:
C-c a m s: start the Emacs MCP server and enable Elisp toolsC-c a m x: disable Elisp tools and stop the MCP serverC-c a m i: reinstall the stdio bridge scriptC-c a m c: copy the raw stdio command for another MCP clientC-c a m d: describe registered MCP tools/resourcesC-c a m M: show MCP usage metrics
Run the test suite from the repository root:
make testOr invoke Emacs directly:
emacs -Q --batch -l tests/init-test.elList the available Make targets:
make helpShow the host-level commands that install optional external tools for the current operating system:
make hostThe target is a dry run by default. To actually install packages, opt in explicitly:
make host HOST_INSTALL=1The host helper detects macOS, Ubuntu/Debian-like Linux, WSL, and native Windows shells such as Git Bash or MSYS2. It covers tools such as ripgrep, fd, jq, pandoc, direnv, clangd, clang-format, Node-based language servers, Mermaid CLI, pipx-managed Python tools, platform clipboard/open helpers, and agent CLI availability for Codex, Claude Code, and Cursor Agent. It intentionally does not manage project-local virtualenvs, debug adapters, commercial agent authentication, or per-user shell profile settings.
scripts/host.el is standalone batch Elisp. It assumes a working stock Emacs
executable is already installed, but it does not load init.el or third-party
packages, so it is safe to run before make setup.
Show whether user shell settings are configured for terminal Emacs, optional CLI editor workflows, tmux, and external MCP clients:
make userThe target is a dry run by default. To update the detected shell startup file
and ~/.tmux.conf with managed, idempotent blocks, opt in explicitly:
make user USER_INSTALL=1MCP client configuration is also a dry run by default. To register the Emacs Elisp MCP server with supported user-level clients, opt in separately:
make user USER_MCP_INSTALL=1By default, the helper reports/applies configuration for Claude Code, Codex, and Cursor. Override the list or paths when needed:
make user USER_MCP_INSTALL=1 USER_MCP_CLIENTS="claude codex cursor"
make user USER_EMACS_MCP_SCRIPT=~/.emacs.d/emacs-mcp-stdio.sh USER_CURSOR_MCP_FILE=~/.cursor/mcp.jsonThe user helper checks and can configure ~/.local/bin on PATH,
Homebrew/Linuxbrew shell environment loading when brew is installed, and tmux
mouse/clipboard/truecolor settings. It leaves EDITOR, VISUAL, and
GIT_EDITOR unmanaged by default; opt in with USER_EDITOR_COMMAND when you
want those variables managed too. Override the detected files when needed:
make user USER_INSTALL=1 USER_SHELL_FILE=~/.zshrc USER_TMUX_FILE=~/.tmux.conf
make user USER_INSTALL=1 USER_EDITOR_COMMAND='emacs -nw'scripts/user.el is standalone batch Elisp. It does not load init.el or
third-party packages, so it is safe to run before make setup; it only needs a
working Emacs executable.
Freshen local byte-compiled ELisp files:
make compileThe generated .elc files are local artifacts and are ignored by git. Emacs may
load a fresh init.elc faster than source, but the source files remain the
canonical project state.
Remove local runtime files and first-party byte-compiled artifacts while keeping installed packages:
make cleanRemove runtime files plus installed package directories:
make realcleanAfter make realclean, run make setup before expecting the full config to
load with all packages available.
Install stock Emacs through your operating system's normal package path, clone
this repository as ~/.emacs.d, then run setup once:
cd ~/.emacs.d
make host
make user
make setup
make testRun make host HOST_INSTALL=1 first if you want the helper to install
host-level external tools. Run make user USER_INSTALL=1 if you want the helper
to write shell editor/PATH/tmux settings into your user dotfiles, and run
make user USER_MCP_INSTALL=1 if you want user-scoped agent clients to know
how to launch the Emacs MCP stdio bridge. make setup refreshes package
archives, installs the managed package set, installs the local MCP stdio bridge,
and compiles the vterm native module, then freshens local .elc files.
vterm requires a working compiler toolchain and cmake on the machine.
By default, this config does not overwrite EDITOR, VISUAL, or GIT_EDITOR
in your login shell. That keeps ordinary Git commit-message editing predictable
and avoids making emacsclient part of basic shell muscle memory unless you
explicitly ask for it.
with-editor remains enabled for editor requests launched from inside Emacs:
shell, eshell, term, vterm, M-!, and M-& can call back into the
current Emacs session without forcing your outside shell to use emacsclient.
In Git commit buffers, C-c C-c finishes, C-c C-k cancels, and C-x C-c
also finishes the quick editor instead of trying to shut down the whole Emacs
session.
If you do want this repository to manage shell editor variables, opt in with an explicit command:
make user USER_INSTALL=1 USER_EDITOR_COMMAND='emacs -nw'
make user USER_INSTALL=1 USER_EDITOR_COMMAND='emacsclient -t -a ""'The matching Emacs-side knob is my/terminal-editor-command; leave it nil to
avoid changing subprocess editor variables, or set it to the same command you
use in your shell. my/terminal-start-server is also opt-in. Plain
emacsclient edits still finish with C-x # when you intentionally use them.
In interactive terminal frames, xterm-mouse-mode is enabled so scrolling and
point/window selection work in terminals that support mouse escape sequences.
Git commit message buffers get a 72-column body fill, Auto Fill, and Flyspell
when a spelling backend exists. TRAMP defaults prefer SSH, keep remote autosaves
under var/tramp-auto-save/, reduce lockfile friction, and use modest logging
for normal remote editing. Common xterm escape sequences are decoded for
modified arrows, Home, and End so terminal frames can use familiar navigation
keys without GUI-only modifiers.
Terminal Emacs quality depends partly on the terminal outside Emacs:
- Use a capable
TERM, such asxterm-256coloroutside tmux ortmux-256colorinside tmux. AvoidTERM=dumbexcept for intentionally minimal command output. - Local terminal Emacs uses a paired host clipboard integration when one is
available, such as
pbcopy/pbpasteon macOS. That keeps ordinary editing intact: text killed withC-kis the text yanked back withC-y. - Enable clipboard passthrough for OSC 52 in the local terminal emulator. This
is what lets remote
emacs -nwcopy text back to the workstation clipboard. - In tmux, enable mouse and clipboard passthrough, and advertise truecolor when your terminal supports it:
set -g mouse on
set -g set-clipboard on
set -as terminal-features ',xterm-256color:RGB'Older tmux versions may use this truecolor form instead:
set -ga terminal-overrides ',*:Tc'Some language modules use external command-line tools when they are present and fall back gracefully when they are not:
- Platform integration:
openon macOS,xdg-openplus optionalwl-copy/wl-paste,xclip, orxselon Linux, and Windowsexplorer.exe/clip.exe/PowerShell where available - Terminal clipboard: OSC 52 support is built in. Some terminal multiplexers
need clipboard passthrough enabled, such as
set -g set-clipboard onin tmux. - Project search:
rgfrom ripgrep, plusfdorfindfor file discovery - Project environments:
direnv - Agent workflows:
codexor another Codex CLI launch command onPATH - Debugging: language-specific DAP adapters such as
debugpy,codelldb/lldb-dap, or JavaScript debug adapters - Markdown preview:
pandoc,multimarkdown, ormarkdown - Mermaid rendering:
mmdcfrom@mermaid-js/mermaid-cli - JSON filtering/pretty-printing:
jq - JSON/YAML language servers:
vscode-json-language-serverandyaml-language-server - Python tooling:
python3,pytest,rufforblack, and a language server such asbasedpyright-langserver,pyright-langserver, orpylsp - Verilog/SystemVerilog tooling:
verible-verilog-format,verible-verilog-lint,verible-verilog-ls,verilator, andiverilog
On macOS with Homebrew:
brew install aspell cmake direnv fd icarus-verilog jq llvm node pandoc pipx python ripgrep ruff shellcheck uv verilator
brew tap chipsalliance/verible
brew install verible
npm install -g @mermaid-js/mermaid-cli typescript-language-server vscode-langservers-extracted yaml-language-server
pipx install basedpyright
pipx install sqlparseVerible is not a core Homebrew formula; its macOS formula is maintained in the CHIPS Alliance tap.
On Ubuntu/Debian:
sudo apt update
sudo apt install -y aspell build-essential clang-format clangd cmake curl direnv fd-find gdb git iverilog jq libtool-bin lldb nodejs npm pandoc pipx python3 python3-pip python3-venv ripgrep shellcheck verilator wl-clipboard xclip xsel xdg-utils
sudo npm install -g @mermaid-js/mermaid-cli typescript-language-server vscode-langservers-extracted yaml-language-server
pipx install basedpyright
pipx install ruff
pipx install sqlparseVerible package availability varies across Ubuntu/Debian releases. Install it from your distribution, a third-party package source, or upstream release packages when you want Verible formatting, linting, and language-server support.
On Windows with winget from Git Bash or another POSIX-like shell:
winget install --id GNU.Emacs --exact
winget install --id Git.Git --exact
winget install --id LLVM.LLVM --exact
winget install --id Kitware.CMake --exact
winget install --id BurntSushi.ripgrep.MSVC --exact
winget install --id sharkdp.fd --exact
winget install --id jqlang.jq --exact
winget install --id OpenJS.NodeJS.LTS --exact
winget install --id Python.Python.3.13 --exact
winget install --id JohnMacFarlane.Pandoc --exact
npm install -g @mermaid-js/mermaid-cli typescript-language-server vscode-langservers-extracted yaml-language-server
py -m pip install --user pipx
py -m pipx install basedpyright
py -m pipx install ruff
py -m pipx install sqlparsevscode-langservers-extracted provides vscode-json-language-server. The host
helper treats the global npm package batch as optional: if Node/npm is broken or
the available Node version is a poor fit, the rest of make host HOST_INSTALL=1 can continue. Repair Node or install a current LTS Node through
your preferred Node version manager, then rerun the npm install -g line.
Python project tools such as pytest, ruff, and black are usually best kept
inside each project's virtualenv. The config will find them in .venv/bin
automatically when they are installed there.
Clone this repository as ~/.emacs.d, replacing the URL with your own config
repository:
git clone git@github.com:YOUR-USER/emacs-config.git ~/.emacs.dIf your GitHub CLI is configured for HTTPS, use:
git clone https://github.com/YOUR-USER/emacs-config.git ~/.emacs.d