# AGENTS.md — org-roam-project Guidance for AI coding agents working in this repository. ## Project overview `org-roam-project` is an Emacs Lisp package that extends [org-roam](https://www.orgroam.com/) with per-project databases using Emacs' built-in `project.el`. Each project gets its own isolated notes directory and SQLite database. The global zettelkasten is never touched. **Language:** Emacs Lisp **Dependencies:** Emacs >= 28.1, org-roam >= 2.2.0 **License:** GPL-3.0-or-later ### Repository layout ``` org-roam-project.el Main package source (~355 lines) org-roam-project-test.el ERT test suite (~430 lines) Makefile Build and test runner flake.nix / shell.nix Nix dev environment ``` ## Build commands ```sh make all # Byte-compile + run tests make compile # Byte-compile org-roam-project.el make test # Run the full ERT test suite in batch mode make clean # Remove .elc files ``` ### Running a single test ```sh emacs --batch \ -l ert \ -l org-roam \ --eval "(add-to-list 'load-path \".\")" \ -l org-roam-project \ -l org-roam-project-test \ --eval "(ert-run-tests-batch-and-exit '\"orp-test-NAME\")" ``` Replace `orp-test-NAME` with the test name (e.g. `orp-test-default-notes-subdir`). The string is an ERT selector regexp matching test names. ### Dev environment The project uses Nix flakes. Run `nix develop` (or let `direnv` activate via `.envrc`) to get an Emacs with org-roam and all dev tools on `$PATH`. ## Code style guidelines ### Lexical binding Every `.el` file MUST have `lexical-binding: t` in the first line: ```elisp ;;; file.el --- Description -*- lexical-binding: t; -*- ``` ### File structure Follow this order in every source file: 1. First line with description and `lexical-binding: t` 2. Copyright / Author / Version / Package-Requires / Keywords / URL 3. License boilerplate (GPL-3.0-or-later) 4. `;;; Commentary:` section 5. `;;; Code:` marker 6. `(require ...)` forms 7. `;;; Section` headers (three semicolons) grouping related code 8. `(provide 'feature)` at the end 9. `;;; file.el ends here` footer ### Namespace prefix All symbols use the `org-roam-project` prefix: - **Public:** `org-roam-project-` (single dash) - **Private:** `org-roam-project--` (double dash) - **Test public:** `orp-test-` - **Test private:** `orp-test--` Never introduce unprefixed top-level symbols. ### Requires / imports - Place all `(require ...)` forms at the top of `;;; Code:`. - Only hard runtime dependencies. No `eval-when-compile` unless truly needed for macros. - **Do not use `cl-lib` in production code.** Use only built-in Emacs Lisp primitives (`let`, `let*`, `when`, `unless`, `cond`, `cons`, `car`, `cdr`, `alist-get`, `dolist`, etc.). `cl-lib` is acceptable in the test file only. ### Formatting - **Line length:** ~80 columns. String literals may slightly exceed this. - **Indentation:** Standard Emacs Lisp indentation (2-space aligned `let` bindings, standard `defun`/`defmacro`/`cond` indentation). - **Parentheses:** Closing parens always on the same line as the last form — never on their own line. - **Blank lines:** One blank line between top-level forms. Section headers (`;;;`) get blank lines before and after. - **Function references:** Always use sharp-quote: `#'function-name`. - **`let` vs `let*`:** Use `let*` only when bindings depend on earlier bindings in the same form. Use plain `let` otherwise. ### Docstrings Every public and private function, macro, and variable must have a docstring. - First line: complete imperative sentence ("Return the context…"). - Arguments referenced in UPPER CASE: `"DIR defaults to…"`. - Cross-reference other symbols with backtick-quote: `` `org-roam-project-init' ``. - Document return values and nil-return conditions explicitly. ### Inline comments - Use `;;` (two semicolons) on a line by itself above the code. - Section headers use `;;;` (three semicolons). - Avoid end-of-line comments in production code (acceptable in tests for brief annotations). ### Defcustom conventions ```elisp (defcustom org-roam-project-XXXX default-value "Docstring." :type 'TYPE :group 'org-roam-project) ``` - Always specify `:type` and `:group`. - If the variable is usable in `.dir-locals.el`, add a `safe-local-variable` declaration with an `;;;###autoload` cookie: ```elisp ;;;###autoload (put 'org-roam-project-XXXX 'safe-local-variable #'stringp) ``` ### Autoloads Place `;;;###autoload` on: - All interactive commands - The minor mode definition (`define-minor-mode`) - `safe-local-variable` `put` forms Never autoload internal (`--`) functions. ### Error handling Two-tier pattern: 1. **Internal functions** return `nil` on failure — never signal errors. Let callers decide behavior. 2. **Interactive commands** call `org-roam-project--require-context` which signals `user-error` (not `error`) with actionable messages. Always use `user-error` for user-facing errors, never bare `error`. ### Macros - Always declare `(declare (indent 0) (debug t))` as the first form. - Accept `&rest body`, splice with `,@body`. - Use `unwind-protect` for cleanup in test macros. ### Dynamic scoping pattern The core architectural pattern: temporarily rebind `org-roam-directory` and `org-roam-db-location` via `let` — never mutate them with `setq`. ```elisp (let ((org-roam-directory (car ctx)) (org-roam-db-location (cdr ctx))) (org-roam-node-find)) ``` ## Testing conventions - **Framework:** ERT (Emacs Regression Testing), built-in. - **Test file:** `org-roam-project-test.el` - **Test names:** `orp-test-` (no double dash). - **Helpers/macros:** `orp-test--` (double dash). - Tests create real temporary Git repos via `git init` so `project.el` recognizes them. Always clean up with `orp-test--cleanup`. - Use `orp-test--with-project` or `orp-test--with-initialized-project` macros to set up and tear down test fixtures. - Test `user-error` conditions with `(should-error ... :type 'user-error)`. - Tests use real org-roam (not mocked) to catch compatibility issues. - `cl-lib` functions (`cl-remove-if`, `cl-find`, `cl-count`, etc.) are allowed in tests. ## Section headers in test file Tests are numbered in groups with decorative Unicode headers: ```elisp ;;; ─── 1. Customisation defaults ─────────────── ``` Maintain this grouping style when adding new test sections.