# Agent Guidelines for nano-gpt This is an Emacs Lisp project for a dashboard application. The codebase consists of: - `dashboard.el` - Main dashboard implementation (180 lines) ## Build/Load Commands ```elisp ;; Load the dashboard in Emacs (load-file "dashboard.el") ;; Byte-compile for faster loading (byte-compile-file "dashboard.el") ``` ## Running Tests This project uses Emacs's built-in ERT testing framework. No external test framework is configured. To run a single test in Emacs: ```elisp ;; Evaluate the test definition, then run: (ert-run-tests-interactively "^test-name$") ``` Or from command line: ```bash emacs --batch --eval "(progn (load-file \"dashboard.el\") (ert \"^test-name$\"))" ``` Run all tests: ```bash emacs --batch --eval "(progn (load-file \"dashboard.el\") (ert t))" ``` ## Linting Commands Emacs has built-in linting tools available: ```elisp ;; Check documentation strings (checkdoc "dashboard.el") ;; Check for undefined variables (elint) (elint-file "dashboard.el") ;; Byte-compile to catch errors (byte-compile-file "dashboard.el") ``` Run linting from command line: ```bash emacs --batch --eval "(progn (load-file \"dashboard.el\") (checkdoc \"dashboard.el\"))" ``` ## Code Style Guidelines ### General Principles - Use CamelCase for function and variable names (Emacs Lisp convention) - Prefix all public functions/variables with a project-specific namespace: `my-dashboard-` - Use kebab-case for keymap names: `my-dashboard-mode-map` ### Naming Conventions - Functions: `my-dashboard-function-name` - Variables: `my-dashboard-variable-name` - Private functions/variables: prefix with `--` (e.g., `my-dashboard--internal`) - Modes: `my-dashboard-foo-mode` - Keymaps: `my-dashboard-foo-mode-map` - Buffer names: `*Dashboard:Name*` ### Formatting - Indent using 2 spaces (Emacs default for Elisp) - Maximum line length: 80 characters - Use `setq` for setting variables, `defvar` for defining variables with docstrings - Place docstrings on the line after `defun`/`defvar` opening paren - Use consistent spacing around operators: `(+ x y)` not `(+x y)` ### Imports and Dependencies - Use `(require 'module)` at the top of files - Common dependencies: `url`, `json`, `tabulated-list`, `org` - Use `use-package` for declarative package management if needed ### Types - Use `:type` property in `defcustom` for customization types - Use type predicates: `numberp`, `stringp`, `listp`, `functionp`, `bufferp` - Check for nil explicitly: `(when var ...)` not `(if var ...)` - Use `eq` for symbol comparison, `equal` for lists/strings ### Error Handling - Use `condition-case` for error catching - Check API error status with `plist-get status :error` - Provide meaningful error messages in docstrings - Handle `:null` values from JSON parsing explicitly ### Keymap Conventions - Use `make-sparse-keymap` for mode keymaps - Set keymap parent to appropriate mode map (e.g., `tabulated-list-mode-map`) - Define keys using `define-key` with key sequences as strings (e.g., "q", "C-x C-f") - Use `setq` with `let` for local keymap creation ### Mode Definition - Use `define-derived-mode` for major modes - Set `tabulated-list-format` and `tabulated-list-entries` - Call `tabulated-list-init-header` and `tabulated-list-print` - Set `inhibit-read-only` before modifying read-only buffers ### Async Operations - Use `url-retrieve` for async HTTP requests - Define callback functions with `status` parameter - Check for errors with `plist-get status :error` - Use `goto-char (point-min)` before parsing buffer content - Use `re-search-forward` to skip HTTP headers in response ### Documentation - Every `defun` and `defvar` should have a docstring - Docstrings start with descriptive verb ("Show", "Fetch", "Copy") - Document interactive commands with `(interactive)` and "Calling from program..." ### Package Management - Use `defcustom` for user-configurable variables with `:type`, `:group`, `:default` - Use `defvar` for internal variables with docstrings - Prefix custom group with project namespace ## Project-Specific Patterns ### Safe List Access ```elisp (defun my-dashboard--aget (alist key) "Safely get value from ALIST for KEY, returns nil if null." (let ((val (cdr (assq key alist)))) (unless (eq val :null) val))) ``` ### Buffer Management ```elisp ;; Get or create buffer (get-buffer-create "*Buffer-Name*") ;; Modify buffer safely (with-current-buffer buf ...) ;; For read-only buffers (let ((inhibit-read-only t)) (erase-buffer) ...) ``` ### Window Configuration ```elisp ;; Save/restore window config (setq my-dashboard--window-config (current-window-configuration)) (set-window-configuration my-dashboard--window-config) ;; Split windows (split-window-right) ; vertical split (split-window-below) ; horizontal split (delete-other-windows) ; maximize current window ``` ### JSON Parsing ```elisp ;; Parse JSON with alist object type (let* ((json (json-parse-string (buffer-substring (point) (point-max)) :object-type 'alist)) (data (cdr (assq 'data json)))) ...) ``` ### Tabulated List Mode ```elisp ;; Format specification: [("Column" width sort-fn) ...] (setq tabulated-list-format [("Name" 30 t) ("ID" 40 t) ("Created" 18 t) ("Ctx" 8 t :right-align t :pad-right 2)]) ;; Entries: (id [col1 col2 col3] ...) (setq tabulated-list-entries '((1 ["Apple" "Red" "Sweet"]) (2 ["Banana" "Yellow" "Sweet"]))) ``` ### Properize Text with Faces ```elisp (propertize text 'font-lock-face face) ;; face can be: '(:foreground "green"), 'bold, etc. ```