5d31ed089d
Add comprehensive guide for agents working in this Doom Emacs config: - Build/lint/test commands (doom sync, doom build, doom doctor) - Emacs Lisp code style guidelines - Doom macro reference (use-package!, add-hook!, map!, after!, load!) - Naming conventions and common patterns - Important workflow notes
7.8 KiB
7.8 KiB
AGENTS.md — Doom Emacs Configuration
This is a Doom Emacs 3 personal configuration at ~/.config/doom/.
Repository Structure
doom/
├── init.el # Module declarations (doom! macro)
├── config.el # Main config, loads modular files via load!
├── packages.el # Package declarations (package! macro)
├── core.el # Core settings and super-save
├── custom.el # Emacs custom variables (auto-generated)
├── security.el # Security settings
├── org.el # Org-mode configuration
├── metrics.el # Usage tracking (keyfreq)
├── theme.el # Theme configuration
├── inbox.el # Quick capture inbox
├── ai/
│ ├── agents.el # Claude Code & Gemini CLI configuration
│ └── gptel.el # GPTel (ChatGPT) configuration
├── development/
│ ├── lsp.el # LSP/LSP-UI configuration
│ ├── cpp.el # C++ development settings
│ ├── nix.el # Nix development settings
│ └── misc.el # Miscellaneous dev settings
├── docs/
│ └── doom-macros-reference.md # Doom macro documentation
└── snippets/ # Yasnippet snippets
Doom Commands
Essential Commands
# Sync config (run after ANY change to init.el, packages.el, or package configs)
doom sync
# Rebuild all packages
doom build
# Diagnose issues
doom doctor
# Update all packages
doom sync -u
# Show verbose output
doom -v sync
# Run in batch mode (for CI/testing)
emacs --batch -l ~/.config/doom/config.el --eval "(message \"test\")"
Package Management
# Force sync without prompts
doom sync -!
# Clean up orphaned packages
doom sync --gc
# Install packages only (no rebuild)
doom install
Testing Individual Config Files
# Load a specific config file and check for errors
emacs --batch -l ~/.config/emacs/bin/doom -l config.el -l ai/agents.el --eval "(message \"Loaded successfully\")"
# Check Elisp syntax
emacs --batch --eval "(setq lexical-binding t)" -l ai/agents.el --eval "(message \"Syntax OK\")"
Code Style Guidelines (Emacs Lisp)
File Headers
Every .el file should start with a standard header:
;;; filename.el -*- lexical-binding: t; -*-
;;; Commentary:
;;; Description of what this file does
;;; Code:
Lexical Binding
Always use lexical-binding: t (note the dash, not underscore in the file-local variable).
;;; init.el -*- lexical-binding: t; -*-
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Private functions | +prefix--name |
+org--restart-mode-h |
| Hook functions | +module-hook-function-h |
+org-exclude-agenda-buffers-from-workspace-h |
| Variables | +module-variable-name |
+org--restart-mode-h |
| Package hooks | -module-hook-function-h |
-org-exclude-agenda-buffers-from-workspace-h |
| Doom modules | :module |
:lang, :completion |
| Module flags | +flag |
+orderless, +lsp |
| Constants | SCREAMING-SNAKE |
MAX-HEIGHT |
Keybinding Syntax (map!)
;; Basic keybindings
(map! :n "C-s" #'consult-line)
;; With lambda
(map! :n "M-s" (lambda () (interactive) (consult-line (thing-at-point 'symbol))))
;; Leader key with prefix
(map! :leader
(:prefix ("C" . "claude-code")
:desc "Start Claude" "c" #'claude-code
:desc "Kill Claude" "k" #'claude-code-kill))
;; State modifiers: :n (normal), :v (visual), :i (insert), :e (emacs)
(map! :nv "C-j" #'something)
use-package! vs use-package
- Use
use-package!in Doom configs (macro version) - Use
use-packagewhen referring to the built-in package.el macro
;; Doom configuration
(use-package! org-roam
:after org
:custom
(org-roam-directory "~/org/roam")
:config
(org-roam-db-autosync-mode))
;; Standard Emacs (when needed)
(use-package org
:init
(setq org-todo-keywords '("TODO" "DONE"))
:config
(org-mode 1))
Hook Registration
;; Anonymous function with defun inside
(add-hook! 'org-agenda-finalize-hook
(defun +org-exclude-agenda-buffers-from-workspace-h ()
"Don't associate temporary agenda buffers with current workspace."
(when org-agenda-new-buffers
(persp-remove-buffer org-agenda-new-buffers
(get-current-persp)
nil))))
;; :local makes hook buffer-local
(add-hook! :local 'some-mode-hook
(defun my-local-hook ()
(do-something)))
Conditional Loading
;; After package loads (preferred in Doom)
(after! lsp-ui
(setq lsp-ui-doc-enable t))
;; With feature check
(when (featurep! :system 'macos)
(setq mac-command-modifier 'meta))
;; After multiple packages
(after! (org org-roam)
(require 'org-roam-protocol))
Defcustom vs Defvar
- Use
defcustomfor user-configurable settings that belong incustom.el - Use
defvarorsetqfor internal variables
;; In packages.el (for user customization)
(setq org-directory "~/org/")
;; Not a defcustom unless it belongs in custom.el
Error Handling
;; Use quiet! to suppress output
(quiet! (org-mode-restart))
;; Use fn! or #' for function references
(advice-add #'org-capture :around
(lambda (fun &rest args)
(letf! ((#'+org--restart-mode-h #'ignore))
(apply fun args))))
;; Use ignore when function should do nothing
(defalias '+org--restart-mode-h #'ignore)
Package Recipes (packages.el)
;; Simple package from Melpa
(package! super-save)
;; GitHub recipe with custom files
(package! copilot
:recipe (:host github :repo "copilot-emacs/copilot.el" :files ("*.el")))
;; GitHub with exclusion patterns
(package! eat
:recipe (:host codeberg
:repo "akib/emacs-eat"
:files ("*.el" ("term" "term/*.el") "*.texi"
"*.ti" ("terminfo/e" "terminfo/e/*")
(:exclude ".dir-locals.el" "*-tests.el"))))
;; Disable a package
(package! some-package :disable t)
Common Patterns
Restart/Reload Functions
(defun +org--restart-mode-h ()
"Restart org-mode on buffer switch."
(remove-hook 'doom-switch-buffer-hook #'+org--restart-mode-h 'local)
(cl-delete (current-buffer) org-agenda-new-buffers :test 'eq)
(quiet! (org-mode-restart))
(run-hooks 'find-file-hook))
Advice Around Functions
(advice-add #'org-capture :around
(lambda (fun &rest args)
(letf! ((#'+org--restart-mode-h #'ignore))
(apply fun args))))
Buffer Display Configuration
(add-to-list 'display-buffer-alist
'("^\\*claude"
(display-buffer-in-side-window)
(side . right)
(window-width . 90)))
Environment Variables
;; Unset for subprocess access
(setenv "CLAUDECODE" nil)
;; Set with path
(setenv "PATH" (concat "/usr/local/bin:" (getenv "PATH")))
Doom Module System
The init.el uses the doom! macro to declare enabled modules:
(doom! :category
(module +flag)
:another-category)
Modules referenced in this config:
:completion— Vertico, Corfu:ui— Doom dashboard, treemacs, workspaces:editor— Evil, format+, multiple-cursors:emacs— Dired, ibuffer, undo:term— Eshell, vterm:checkers— Syntax, spell:tools— LSP, magit, direnv, docker:lang— Python, Go, Rust, Org, JavaScript, etc.:config— Default bindings, smartparens
Important Notes
- Run
doom syncafter ANY change toinit.el,packages.el, orconfig.el - Use
doom buildto rebuild if byte-compilation errors occur - This config uses straight.el for package management (via Doom)
- Custom variables are auto-generated to
custom.el— don't edit manually - The
load!macro loads relative todoom-private-dir(this config directory)