Files
doom/AGENTS.md
T
tomatocream 5d31ed089d docs: add AGENTS.md for agentic coding guidelines
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
2026-04-04 14:45:00 +08:00

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-package when 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 defcustom for user-configurable settings that belong in custom.el
  • Use defvar or setq for 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

  1. Run doom sync after ANY change to init.el, packages.el, or config.el
  2. Use doom build to rebuild if byte-compilation errors occur
  3. This config uses straight.el for package management (via Doom)
  4. Custom variables are auto-generated to custom.el — don't edit manually
  5. The load! macro loads relative to doom-private-dir (this config directory)