| name | Emacs Ecosystem |
| description | This skill should be used when the user asks to "write elisp", "emacs config", "init.el", "use-package", ".el file", "emacs lisp", or "magit". Provides comprehensive Emacs ecosystem patterns and best practices. For org-mode, use org-ecosystem skill. |
| version | 2.0.0 |
Provide comprehensive patterns for Emacs Lisp, configuration management, package systems, and major packages including Magit and LSP integration. For org-mode patterns, see org-ecosystem skill.
<elisp_fundamentals>
S-expressions as code and data (homoiconicity). Prefix notation for all operations.
Emacs Lisp data types: symbol, cons cell, list, vector, hash-table, string, number
;; symbol: Named objects
'foo
:keyword
;; cons_cell: Pair
(cons 1 2) ; => (1 . 2)
;; list: Linked cons cells
'(1 2 3)
;; vector: Fixed-size array
[1 2 3]
;; hash-table: Key-value store
(make-hash-table)
;; string: Text
"hello"
;; number: Integer or float
42
3.14
</example>
Define functions with defun
(defun my-function (arg1 arg2)
"Docstring describing the function."
(+ arg1 arg2))
Local variable binding with let and let*
(let ((x 1)
(y 2))
(+ x y))
(let\* ((x 1)
(y (+ x 1))) ; y can reference x
y)
</example>
Conditional forms: if, when, unless, cond, pcase
(if condition
then-form
else-form)
(when condition
body-forms...)
(unless condition
body-forms...)
(cond
(condition1 result1)
(condition2 result2)
(t default-result))
(pcase value
('symbol (handle-symbol))
((pred stringp) (handle-string))
(\_ (handle-default)))
</example>
Iteration patterns: dolist, dotimes, cl-loop, seq functions
(dolist (item list)
(process item))
(dotimes (i 10)
(process i))
(cl-loop for item in list
collect (transform item))
(seq-map #'transform sequence)
(seq-filter #'predicate sequence)
(seq-reduce #'fn sequence initial)
</example>
Anonymous functions with lambda
(lambda (x) (* x 2))
(mapcar (lambda (x) (\* x 2)) '(1 2 3))
;; Short form (Emacs 28+)
(mapcar (lambda (x) (+ x 1)) list)
</example>
Define macros with defmacro. Use backquote for templates, comma for evaluation
(defmacro with-temp-message (msg &rest body)
"Execute BODY with MSG displayed temporarily."
`(let ((message-log-max nil))
(message "%s" ,msg)
(unwind-protect
(progn ,@body)
(message nil))))
Modern init.el organization
;;; init.el --- Emacs configuration -\*- lexical-binding: t; -\_-
;;; Commentary:
;; Personal Emacs configuration
;;; Code:
;; Bootstrap package manager
(require 'package)
(setq package-archives
'(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
(package-initialize)
;; use-package is built-in since Emacs 29; no installation needed
(eval-when-compile
(require 'use-package))
;; Configuration sections...
(provide 'init)
;;; init.el ends here
</example>
Declarative package configuration with use-package keywords
Does the package need lazy loading or declarative configuration?
Use use-package for clean, maintainable configuration
Use require for simple packages with no configuration needs
(use-package corfu
:ensure t
:defer t
:hook (prog-mode . corfu-mode)
:bind (:map corfu-map
("C-n" . corfu-next)
("C-p" . corfu-previous))
:custom
(corfu-auto t)
(corfu-cycle t))
Keywords:
- :ensure - Install package if not present
- :defer - Lazy load (t or seconds)
- :hook - Add to mode hooks
- :bind - Define keybindings
- :custom - Set customizable variables
- :init - Run before package loads
- :config - Run after package loads
- :commands - Autoload commands
- :after - Load after specified packages
- :if/:when/:unless - Conditional loading
Key binding patterns: global-set-key, define-key, use-package :bind
;; Global keybinding
(global-set-key (kbd "C-c l") #'org-store-link)
;; Mode-specific
(define-key emacs-lisp-mode-map (kbd "C-c C-e") #'eval-last-sexp)
;; With use-package
(use-package magit
:bind (("C-x g" . magit-status)
("C-x M-g" . magit-dispatch)))
;; Keymap definition
(defvar my-prefix-map (make-sparse-keymap)
"Keymap for my custom commands.")
(global-set-key (kbd "C-c m") my-prefix-map)
(define-key my-prefix-map (kbd "f") #'find-file)
</example>
Hook management with add-hook and use-package :hook
;; Add function to hook
(add-hook 'prog-mode-hook #'display-line-numbers-mode)
;; Remove function from hook
(remove-hook 'prog-mode-hook #'display-line-numbers-mode)
;; Lambda in hook (discouraged for removability)
(add-hook 'after-save-hook
(lambda () (message "Saved!")))
;; With use-package
(use-package flycheck
:hook (prog-mode . flycheck-mode))
</example>
Modify existing functions with advice-add and advice-remove
(defun my-after-save-message (orig-fun &rest args)
"Show message after save."
(apply orig-fun args)
(message "Buffer saved at %s" (current-time-string)))
(advice-add 'save-buffer :around #'my-after-save-message)
;; Remove advice
(advice-remove 'save-buffer #'my-after-save-message)
</example>
Define customizable variables with defgroup and defcustom
(defgroup my-package nil
"My package customization."
:group 'convenience
:prefix "my-package-")
(defcustom my-package-option t
"Enable my-package option."
:type 'boolean
:group 'my-package)
(defcustom my-package-list '("a" "b")
"List of strings."
:type '(repeat string)
:group 'my-package)
</example>
Built-in package manager for Emacs. Reliable and sufficient for most workflows.
;; Commands:
;; - package-install - Install a package
;; - package-delete - Remove a package
;; - package-refresh-contents - Update package list
;; - package-list-packages - Browse packages
(require 'package)
(setq package-archives
'(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")))
(package-initialize)
;; Install a package
(package-install 'magit)
</example>
Built-in since Emacs 29. The standard declarative way to configure packages. No installation needed on Emacs 29+.
;; use-package is built-in since Emacs 29; just require it
(eval-when-compile
(require 'use-package))
;; Declarative package configuration
(use-package magit
:ensure t
:bind ("C-x g" . magit-status))
</example>
Functional package manager with Git integration. Still widely used, but elpaca is gaining adoption for reproducible package management.
;; Bootstrap
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el"
user-emacs-directory)))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el")
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
;; Use with use-package
(straight-use-package 'use-package)
(setq straight-use-package-by-default t)
;; Install package
(use-package magit
:straight t)
</example>
Modern async package manager gaining adoption for reproducible package management. An alternative to straight.el with improved performance.
;; Bootstrap
(defvar elpaca-installer-version 0.7)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
;; ... (bootstrap code)
;; Use with use-package
(elpaca elpaca-use-package
(elpaca-use-package-mode))
(use-package magit
:ensure t)
</example>
Git porcelain for Emacs
Basic Magit setup with use-package
(use-package magit
:ensure t
:bind (("C-x g" . magit-status)
("C-x M-g" . magit-dispatch)
("C-c M-g" . magit-file-dispatch)))
Magit status buffer keybindings
;; s - Stage file/hunk
;; u - Unstage file/hunk
;; c c - Commit
;; P p - Push
;; F p - Pull
;; b b - Checkout branch
;; b c - Create branch
;; l l - Log current branch
;; d d - Diff
Magit configuration settings
(setq magit-save-repository-buffers 'dontask)
(setq magit-display-buffer-function
#'magit-display-buffer-same-window-except-diff-v1)
(setq magit-diff-refine-hunk 'all)
GitHub/GitLab integration with Forge
(use-package forge
:after magit
:ensure t)
<lsp_integration>
<decision_tree name="when_to_use">
Do you need LSP features like completion, go-to-definition, and diagnostics?
<if_yes>Use eglot (built-in, recommended default). Use lsp-mode only for advanced configurations requiring features beyond eglot.</if_yes>
<if_no>Use basic major modes without LSP overhead</if_no>
</decision_tree>
Built-in LSP client (Emacs 29+). Recommended default for most use cases. Tightly integrated with Emacs core, leveraging built-in completion (completion-at-point), Flymake for diagnostics, and project.el for project management.
(use-package eglot
:ensure nil ; built-in since Emacs 29
:hook ((python-mode . eglot-ensure)
(python-ts-mode . eglot-ensure)
(typescript-ts-mode . eglot-ensure)
(rust-ts-mode . eglot-ensure))
:config
(setq eglot-autoshutdown t)
(setq eglot-events-buffer-size 0)
;; Emacs 30+: improved tree-sitter integration with eglot
(setq eglot-report-progress nil))
;; Custom server configuration
(add-to-list 'eglot-server-programs
'(rust-ts-mode . ("rust-analyzer")))
</example>
Feature-rich LSP client for advanced configurations. Use when eglot does not meet requirements (e.g., DAP integration, custom UI features via lsp-ui).
(use-package lsp-mode
:ensure t
:hook ((python-mode . lsp-deferred)
(typescript-mode . lsp-deferred))
:commands (lsp lsp-deferred)
:custom
(lsp-keymap-prefix "C-c l")
(lsp-idle-delay 0.5)
(lsp-log-io nil)
:config
(lsp-enable-which-key-integration t))
(use-package lsp-ui
:ensure t
:hook (lsp-mode . lsp-ui-mode)
:custom
(lsp-ui-doc-enable t)
(lsp-ui-sideline-enable t))
</example>
LSP completion with corfu (recommended) or company. Corfu works with Emacs built-in completion-at-point and pairs well with eglot. Cape provides additional completion-at-point backends.
;; With corfu + cape (current best practice)
(use-package corfu
:ensure t
:custom
(corfu-auto t)
(corfu-cycle t)
:init
(global-corfu-mode))
(use-package cape
:ensure t
:init
(add-hook 'completion-at-point-functions #'cape-dabbrev)
(add-hook 'completion-at-point-functions #'cape-file))
;; With company (traditional, still maintained)
(use-package company
:ensure t
:hook (after-init . global-company-mode)
:custom
(company-idle-delay 0.2))
</example>
<modern_packages>
Vertical completion UI. Part of the current best-practice completion stack: vertico (UI), orderless (matching), marginalia (annotations), consult (commands), embark (actions).
(use-package vertico
:ensure t
:init (vertico-mode))
(use-package orderless
:ensure t
:custom
(completion-styles '(orderless basic)))
(use-package marginalia
:ensure t
:init (marginalia-mode))
(use-package consult
:ensure t
:bind (("C-s" . consult-line)
("C-x b" . consult-buffer)
("M-g g" . consult-goto-line)))
</example>
Display available keybindings
(use-package which-key
:ensure t
:diminish
:init (which-key-mode))
Native tree-sitter integration (Emacs 29+, improved in Emacs 30). Emacs 30.2 includes enhanced tree-sitter support with better fontification, indentation, and navigation. Use *-ts-mode variants for tree-sitter-backed major modes.
(setq treesit-language-source-alist
'((python "https://github.com/tree-sitter/tree-sitter-python")
(javascript "https://github.com/tree-sitter/tree-sitter-javascript")
(typescript "https://github.com/tree-sitter/tree-sitter-typescript"
"master" "typescript/src")
(tsx "https://github.com/tree-sitter/tree-sitter-typescript"
"master" "tsx/src")))
;; Install grammars
(mapc #'treesit-install-language-grammar
(mapcar #'car treesit-language-source-alist))
;; Remap modes to tree-sitter variants
(setq major-mode-remap-alist
'((python-mode . python-ts-mode)
(javascript-mode . js-ts-mode)
(typescript-mode . typescript-ts-mode)
(css-mode . css-ts-mode)
(json-mode . json-ts-mode)))
;; Emacs 30+: treesit-auto can manage grammar installation
;; and mode remapping automatically
</example>
<context7_integration>
<usage_pattern>
Resolve library ID (known: /websites/emacsdocs)
Workflow guidance
Step completed
Fetch documentation with specific topic
Workflow guidance
Step completed
Emacs Lisp programming patterns
Package configuration patterns
Org mode configuration
Magit usage and configuration
Hook usage patterns
</usage_pattern>
<common_queries>
Key binding patterns
Function definition
Advice system usage
Customization variables
</common_queries>
</context7_integration>
<best_practices>
Enable lexical-binding in all Elisp files: -*- lexical-binding: t; -_-
Use #'function-name for function references (enables byte-compiler warnings)
Document functions with docstrings
Namespace all symbols with package prefix
Prefer seq.el functions for sequence operations
Use pcase for complex pattern matching
Use defcustom for user-configurable options
Use provide at end of file
Prefer :custom over setq in use-package
Use :hook instead of add-hook in use-package
Lazy load packages with :defer, :commands, or :hook
Use native-compilation when available (Emacs 28+)
Prefer eglot for LSP (built-in since Emacs 29, recommended default)
Use tree-sitter *-ts-mode variants when available (Emacs 29+, improved in 30.2)
Use the modern completion stack: vertico, orderless, marginalia, consult, corfu, cape
use-package is built-in since Emacs 29; no need to install it
Use Emacs 30.x as the baseline and defer to the active package set for the exact stable point release
</best_practices>
<anti_patterns>
Using dynamic binding when lexical is needed
Add lexical-binding: t to file header
Hardcoding absolute paths
Use expand-file-name, user-emacs-directory, or locate-user-emacs-file
Requiring packages at top level unconditionally
Use autoload, use-package with :defer, or eval-after-load
Modifying global state without restoration
Use let-binding or save-excursion/save-restriction
Adding lambdas to hooks (hard to remove)
Define named functions and add those
Using setq for defcustom variables
Use customize-set-variable or :custom in use-package
Using deprecated cl library
Use cl-lib with cl- prefixed functions
Using eval-after-load with string
Use with-eval-after-load or use-package :config
Complex logic in early-init.el
Keep early-init.el minimal (frame settings, package setup)
Using lsp-mode when eglot meets all requirements
Use eglot (built-in since Emacs 29, preferred default). Only use lsp-mode for DAP integration or lsp-ui features not available in eglot.
Using company-mode for in-buffer completion
Use corfu + cape, which integrate with Emacs built-in completion-at-point and pair well with eglot and the vertico stack.
Using helm or ivy/counsel for minibuffer completion
Use the modern completion stack: vertico (UI) + orderless (matching) + marginalia (annotations) + consult (commands) + embark (actions).
Target Emacs 30.x as the baseline major series and align exact version with the active package set
Prefer built-in packages (eglot, use-package, treesit) over third-party alternatives
Use tree-sitter *-ts-mode variants for all languages with grammar support
Configure eglot as the default LSP client; only suggest lsp-mode when eglot is insufficient
Recommend the modern completion stack (vertico, orderless, marginalia, consult, corfu, cape) over legacy alternatives (helm, ivy, company)
For reproducible package management, mention elpaca alongside straight.el
Understand Emacs Lisp requirements
1. Check package dependencies and autoloads
Workflow guidance
Step completed
2. Review existing configuration patterns
Workflow guidance
Step completed
3. Identify hook and advice usage
Workflow guidance
Step completed
Write idiomatic Emacs Lisp code
1. Use lexical binding
Workflow guidance
Step completed
2. Follow Emacs Lisp conventions
Workflow guidance
Step completed
3. Provide appropriate customization options
Workflow guidance
Step completed
Verify Emacs Lisp correctness
1. Byte-compile without warnings
Workflow guidance
Step completed
2. Test in clean Emacs instance
Workflow guidance
Step completed
3. Verify keybindings don't conflict
Workflow guidance
Step completed
<error_escalation>
Byte-compilation warning
Fix warning, ensure clean compilation
Configuration error on startup
Debug with --debug-init, fix issue
Package conflict or version mismatch
Stop, present resolution options to user
Emacs becomes unusable
Provide recovery steps, require user action
</error_escalation>
Keep guidance evidence-based and version-aware
Prefer project conventions over generic defaults
<related_agents>
Locate relevant code patterns
Review output consistency
</related_agents>
Use lexical-binding: t in all files
Provide customization via defcustom
Follow Emacs Lisp naming conventions
Dynamic binding without justification
Overriding standard keybindings silently
Blocking operations in hooks
<related_skills>
Org-mode document creation, GTD workflow, Babel, export patterns
Symbol operations for elisp code navigation
Emacs documentation lookup via /websites/emacsdocs
Debugging package conflicts and performance issues
Creating package documentation and README files
</related_skills>