home-manager/ai-tools/agent-skills/skills/emacs-ecosystem/SKILL.md
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.
npx skillsauth add takeokunn/nixos-configuration Emacs EcosystemInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
<elisp_fundamentals> <concept name="basic_syntax"> <description>S-expressions as code and data (homoiconicity). Prefix notation for all operations.</description> </concept>
<concept name="data_types"> <description>Emacs Lisp data types: symbol, cons cell, list, vector, hash-table, string, number</description> <example> ;; 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>
</concept>
<pattern name="defun">
<description>Define functions with defun</description>
<example>
(defun my-function (arg1 arg2)
"Docstring describing the function."
(+ arg1 arg2))
</example>
</pattern>
<pattern name="let_binding">
<description>Local variable binding with let and let*</description>
<example>
(let ((x 1)
(y 2))
(+ x y))
(let\* ((x 1)
(y (+ x 1))) ; y can reference x
y)
</example>
</pattern>
<pattern name="conditionals">
<description>Conditional forms: if, when, unless, cond, pcase</description>
<example>
(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>
</pattern>
<pattern name="iteration">
<description>Iteration patterns: dolist, dotimes, cl-loop, seq functions</description>
<example>
(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>
</pattern>
<pattern name="lambda">
<description>Anonymous functions with lambda</description>
<example>
(lambda (x) (* x 2))
(mapcar (lambda (x) (\* x 2)) '(1 2 3))
;; Short form (Emacs 28+)
(mapcar (lambda (x) (+ x 1)) list)
</example>
</pattern>
<pattern name="macros">
<description>Define macros with defmacro. Use backquote for templates, comma for evaluation</description>
<example>
(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))))
</example>
</pattern>
</elisp_fundamentals>
<patterns>
<pattern name="init_el_structure">
<description>Modern init.el organization</description>
<example>
;;; 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>
</pattern>
<pattern name="use_package">
<description>Declarative package configuration with use-package keywords</description>
<decision_tree name="when_to_use">
<question>Does the package need lazy loading or declarative configuration?</question>
<if_yes>Use use-package for clean, maintainable configuration</if_yes>
<if_no>Use require for simple packages with no configuration needs</if_no>
</decision_tree>
<example>
(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))
</example>
<note>
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
</note>
</pattern>
<pattern name="keybinding">
<description>Key binding patterns: global-set-key, define-key, use-package :bind</description>
<example>
;; 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>
</pattern>
<pattern name="hooks">
<description>Hook management with add-hook and use-package :hook</description>
<example>
;; 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>
</pattern>
<pattern name="advice">
<description>Modify existing functions with advice-add and advice-remove</description>
<example>
(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>
</pattern>
<pattern name="custom_variables">
<description>Define customizable variables with defgroup and defcustom</description>
<example>
(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>
</pattern>
</patterns>
<tools>
<tool name="package.el">
<description>Built-in package manager for Emacs. Reliable and sufficient for most workflows.</description>
<example>
;; 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>
</tool>
<tool name="use-package">
<description>Built-in since Emacs 29. The standard declarative way to configure packages. No installation needed on Emacs 29+.</description>
<example>
;; 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>
</tool>
<tool name="straight.el">
<description>Functional package manager with Git integration. Still widely used, but elpaca is gaining adoption for reproducible package management.</description>
<example>
;; 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>
</tool>
<tool name="elpaca">
<description>Modern async package manager gaining adoption for reproducible package management. An alternative to straight.el with improved performance.</description>
<example>
;; 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>
</tool>
</tools>
<magit>
<description>Git porcelain for Emacs</description>
<pattern name="basic_usage">
<description>Basic Magit setup with use-package</description>
<example>
(use-package magit
:ensure t
:bind (("C-x g" . magit-status)
("C-x M-g" . magit-dispatch)
("C-c M-g" . magit-file-dispatch)))
</example>
</pattern>
<pattern name="status_buffer">
<description>Magit status buffer keybindings</description>
<example>
;; 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
</example>
</pattern>
<pattern name="configuration">
<description>Magit configuration settings</description>
<example>
(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)
</example>
</pattern>
<pattern name="forge">
<description>GitHub/GitLab integration with Forge</description>
<example>
(use-package forge
:after magit
:ensure t)
</example>
</pattern>
</magit>
<lsp_integration> <decision_tree name="when_to_use"> <question>Do you need LSP features like completion, go-to-definition, and diagnostics?</question> <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>
<pattern name="eglot"> <description>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.</description> <example> (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>
</pattern>
<pattern name="lsp_mode">
<description>Feature-rich LSP client for advanced configurations. Use when eglot does not meet requirements (e.g., DAP integration, custom UI features via lsp-ui).</description>
<example>
(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>
</pattern>
<pattern name="completion">
<description>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.</description>
<example>
;; 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>
</pattern>
</lsp_integration>
<modern_packages> <tool name="vertico"> <description>Vertical completion UI. Part of the current best-practice completion stack: vertico (UI), orderless (matching), marginalia (annotations), consult (commands), embark (actions).</description> <example> (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>
</tool>
<tool name="which_key">
<description>Display available keybindings</description>
<example>
(use-package which-key
:ensure t
:diminish
:init (which-key-mode))
</example>
</tool>
<tool name="treesit">
<description>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.</description>
<example>
(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>
</tool>
</modern_packages>
<context7_integration> <library name="Emacs Docs" id="/websites/emacsdocs" trust="7.5" snippets="6792" />
<usage_pattern> <step order="1"> <action>Resolve library ID (known: /websites/emacsdocs)</action> <tool>Workflow guidance</tool> <output>Step completed</output> </step> <step order="1"> <action>Fetch documentation with specific topic</action> <tool>Workflow guidance</tool> <output>Step completed</output> </step> <examples> <example topic="elisp">Emacs Lisp programming patterns</example> <example topic="use-package">Package configuration patterns</example> <example topic="org-mode">Org mode configuration</example> <example topic="magit">Magit usage and configuration</example> <example topic="hooks">Hook usage patterns</example> </examples> </usage_pattern>
<common_queries> <query topic="keybindings">Key binding patterns</query> <query topic="defun">Function definition</query> <query topic="advice">Advice system usage</query> <query topic="custom">Customization variables</query> </common_queries> </context7_integration>
<best_practices> <practice priority="critical">Enable lexical-binding in all Elisp files: -*- lexical-binding: t; -_-</practice> <practice priority="high">Use #'function-name for function references (enables byte-compiler warnings)</practice> <practice priority="high">Document functions with docstrings</practice> <practice priority="high">Namespace all symbols with package prefix</practice> <practice priority="medium">Prefer seq.el functions for sequence operations</practice> <practice priority="medium">Use pcase for complex pattern matching</practice> <practice priority="medium">Use defcustom for user-configurable options</practice> <practice priority="medium">Use provide at end of file</practice> <practice priority="medium">Prefer :custom over setq in use-package</practice> <practice priority="medium">Use :hook instead of add-hook in use-package</practice> <practice priority="medium">Lazy load packages with :defer, :commands, or :hook</practice> <practice priority="medium">Use native-compilation when available (Emacs 28+)</practice> <practice priority="high">Prefer eglot for LSP (built-in since Emacs 29, recommended default)</practice> <practice priority="high">Use tree-sitter *-ts-mode variants when available (Emacs 29+, improved in 30.2)</practice> <practice priority="high">Use the modern completion stack: vertico, orderless, marginalia, consult, corfu, cape</practice> <practice priority="medium">use-package is built-in since Emacs 29; no need to install it</practice> <practice priority="medium">Use Emacs 30.x as the baseline and defer to the active package set for the exact stable point release</practice> </best_practices>
<anti_patterns> <avoid name="dynamic_binding"> <description>Using dynamic binding when lexical is needed</description> <instead>Add lexical-binding: t to file header</instead> </avoid>
<avoid name="hardcoded_paths"> <description>Hardcoding absolute paths</description> <instead>Use expand-file-name, user-emacs-directory, or locate-user-emacs-file</instead> </avoid> <avoid name="require_at_top"> <description>Requiring packages at top level unconditionally</description> <instead>Use autoload, use-package with :defer, or eval-after-load</instead> </avoid> <avoid name="global_state"> <description>Modifying global state without restoration</description> <instead>Use let-binding or save-excursion/save-restriction</instead> </avoid> <avoid name="lambda_in_hooks"> <description>Adding lambdas to hooks (hard to remove)</description> <instead>Define named functions and add those</instead> </avoid> <avoid name="setq_for_custom"> <description>Using setq for defcustom variables</description> <instead>Use customize-set-variable or :custom in use-package</instead> </avoid> <avoid name="cl_library"> <description>Using deprecated cl library</description> <instead>Use cl-lib with cl- prefixed functions</instead> </avoid> <avoid name="eval_after_load_string"> <description>Using eval-after-load with string</description> <instead>Use with-eval-after-load or use-package :config</instead> </avoid> <avoid name="inhibit_startup"> <description>Complex logic in early-init.el</description> <instead>Keep early-init.el minimal (frame settings, package setup)</instead> </avoid> <avoid name="lsp_mode_when_eglot_suffices"> <description>Using lsp-mode when eglot meets all requirements</description> <instead>Use eglot (built-in since Emacs 29, preferred default). Only use lsp-mode for DAP integration or lsp-ui features not available in eglot.</instead> </avoid> <avoid name="company_mode"> <description>Using company-mode for in-buffer completion</description> <instead>Use corfu + cape, which integrate with Emacs built-in completion-at-point and pair well with eglot and the vertico stack.</instead> </avoid> <avoid name="helm_ivy"> <description>Using helm or ivy/counsel for minibuffer completion</description> <instead>Use the modern completion stack: vertico (UI) + orderless (matching) + marginalia (annotations) + consult (commands) + embark (actions).</instead> </avoid> </anti_patterns> <rules priority="critical"> <rule>Target Emacs 30.1+ features including built-in use-package and native Tree-sitter support; never recommend installing use-package as a separate dependency</rule> <rule>Target Emacs 30.x as the baseline major series and align exact version with the active package set</rule> <rule>Prefer built-in packages (eglot, use-package, treesit) over third-party alternatives</rule> <rule>Use tree-sitter *-ts-mode variants for all languages with grammar support</rule> <rule>Configure eglot as the default LSP client; only suggest lsp-mode when eglot is insufficient</rule> </rules> <rules priority="standard"> <rule>Recommend the modern completion stack (vertico, orderless, marginalia, consult, corfu, cape) over legacy alternatives (helm, ivy, company)</rule> <rule>For reproducible package management, mention elpaca alongside straight.el</rule> </rules> <workflow> <phase name="analyze"> <objective>Understand Emacs Lisp requirements</objective> <step order="1"> <action>1. Check package dependencies and autoloads</action> <tool>Workflow guidance</tool> <output>Step completed</output> </step> <step order="1"> <action>2. Review existing configuration patterns</action> <tool>Workflow guidance</tool> <output>Step completed</output> </step> <step order="1"> <action>3. Identify hook and advice usage</action> <tool>Workflow guidance</tool> <output>Step completed</output> </step> </phase> <phase name="implement"> <objective>Write idiomatic Emacs Lisp code</objective> <step order="1"> <action>1. Use lexical binding</action> <tool>Workflow guidance</tool> <output>Step completed</output> </step> <step order="1"> <action>2. Follow Emacs Lisp conventions</action> <tool>Workflow guidance</tool> <output>Step completed</output> </step> <step order="1"> <action>3. Provide appropriate customization options</action> <tool>Workflow guidance</tool> <output>Step completed</output> </step> </phase> <phase name="validate"> <objective>Verify Emacs Lisp correctness</objective> <step order="1"> <action>1. Byte-compile without warnings</action> <tool>Workflow guidance</tool> <output>Step completed</output> </step> <step order="1"> <action>2. Test in clean Emacs instance</action> <tool>Workflow guidance</tool> <output>Step completed</output> </step> <step order="1"> <action>3. Verify keybindings don't conflict</action> <tool>Workflow guidance</tool> <output>Step completed</output> </step> </phase> </workflow><error_escalation inherits="core-patterns#error_escalation"> <examples> <example severity="low">Byte-compilation warning</example> <example severity="medium">Configuration error on startup</example> <example severity="high">Package conflict or version mismatch</example> <example severity="critical">Emacs becomes unusable</example> </examples> </error_escalation>
<related_agents> <agent name="explore">Locate code patterns and references in this skill domain</agent> <agent name="quality-assurance">Review implementation quality against this skill guidance</agent> <agent name="code-quality">Analyze code complexity and suggest refactoring improvements</agent> </related_agents>
<constraints> <must>Use lexical-binding: t in all files</must> <must>Provide customization via defcustom</must> <must>Follow Emacs Lisp naming conventions</must> <avoid>Dynamic binding without justification</avoid> <avoid>Overriding standard keybindings silently</avoid> <avoid>Blocking operations in hooks</avoid> </constraints><related_skills> <skill name="org-ecosystem">Org-mode document creation, GTD workflow, Babel, export patterns</skill> <skill name="serena-usage">Symbol operations for elisp code navigation</skill> <skill name="context7-usage">Emacs documentation lookup via /websites/emacsdocs</skill> <skill name="investigation-patterns">Debugging package conflicts and performance issues</skill> <skill name="technical-documentation">Creating package documentation and README files</skill> </related_skills>
tools
This skill should be used when the user asks to parse, search, grep, query, filter, or extract headings, sections, tasks, code blocks, links, or tables from Markdown files. Use when working with mdq, jq-style Markdown querying, section extraction, checklist validation, CI task scripts, or documentation automation pipelines.
development
Practical SBCL (Steel Bank Common Lisp) operations guide. Use this skill whenever the user mentions SBCL execution/debugging, --script usage, REPL workflows, backtraces, ASDF loading, save-lisp-and-die, profiling, or SLY-based Common Lisp development.
tools
Context7 MCP documentation retrieval patterns for up-to-date library and API references. Use this skill whenever current library docs, API signatures, version-specific behavior, or migration notes are needed.
development
Patterns for output formats, reflection checkpoints, agent references, and self-evaluation shared across agents and commands.