This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Personal Emacs configuration (~4300 lines of elisp) for a single user on macOS. Requires Emacs 29+, currently built with brew install emacs-plus --with-poll --with-native-comp. There is no build system or CI — verification mostly means loading the config in Emacs.
ERT tests live in test/ (currently only for select custom functions). Run a test file with:
emacs -Q --batch -l test/sentence-spacing-tests.el -f ert-run-tests-batch-and-exitTest files stub use-package as a no-op macro so an init/*.el file can be loaded under emacs -Q without package.el — copy that pattern when testing functions from other init modules.
From inside Emacs (these are the workflows documented in init.el's commentary):
M-x restart-emacs— restart to test config changes end-to-end.M-x bug-hunter-init-file— bisect the init files to locate a load error.M-x profiler-start/M-x profiler-report— profile runtime performance.M-x free-keys— find unused keybindings before adding a new one.
A quick syntax/load smoke test from the shell only covers part of the config (see the terminal gotcha below):
emacs --batch -l ~/.emacs.d/init.elearly-init.el handles native-comp settings and frame/GC startup tweaks. init.el then loads modules from init/ via require, in a deliberate order — init-basics → init-package (MELPA available after this) → early utils/keybindings → built-ins → visuals → external packages → init-org → init-mode-line (deliberately last, to override changes from other packages). When adding a module or moving code between modules, respect this ordering.
Terminal gotcha: init.el stops loading early when display-graphic-p is nil (a trick that moves the load pointer to the end of the buffer). Terminal Emacs is only used for quick edits, so everything from init-packages-general onward — including all external packages, org, and mode-line — only loads in GUI sessions. Batch-mode checks hit the same early exit.
Key module facts not obvious from filenames:
init-basics.eldefines the path variables used everywhere (user-emacs-var-directoryetc.). Package/var/etc state lives outside the repo in~/.local/emacs/; the eln-cache lives in~/.eln-cache/(set in early-init.el) to avoid cloud-sync churn.- Packages are managed with
use-package+ MELPA, withuse-package-always-ensure t— a newuse-packageform auto-installs on next start. customize.elis loaded at the end of init but is gitignored (machine-local); don't put config there.init-packages-languages.elpins tree-sitter grammars to exact tags/commits intreesit-language-source-alist. Grammars install to~/.local/emacs/var/treesit/(no-littering's themed dir). The install loop skips already-installed grammars, so after bumping a pin you must delete the corresponding~/.local/emacs/var/treesit/*.dylib(or calltreesit-install-language-grammar) for it to take effect.
Git submodules for self-authored or forked packages, loaded via use-package :load-path. Convention: feature work and bug fixes belong in the fork/package repo itself; the init/*.el files only contain toggles, keybindings, and glue. Don't grow package logic inside init/.
- Every file uses the standard elisp skeleton:
-*- lexical-binding: t; -*-header,;;; Commentary:/;;; Code:sections, and a matching(provide 'feature)+;;; file ends herefooter. notes.mdat the repo root is an untracked scratch file for pending upgrade/maintenance plans — read it for context on in-flight work, but it is not part of the config.