6.9 KiB
6.9 KiB
Agent Guidelines for AI (Emacs Lisp)
This is an Emacs Lisp project providing dashboard packages for AI APIs. The codebase consists of:
ai.el- Main package that loads all dashboardsppq-status.el- Dashboard for PayPerQ API (models, balance, topup)nano-gpt-status.el- Dashboard for Nano-GPT API (models, usage, balance, deposit)
Build/Load Commands
;; Load the package in Emacs
(load-file "ai.el")
;; Or load individual dashboards
(load-file "ppq-status.el")
(load-file "nano-gpt-status.el")
;; Byte-compile for faster loading
(byte-compile-file "ai.el")
(byte-compile-file "ppq-status.el")
(byte-compile-file "nano-gpt-status.el")
Running Tests
This project uses Emacs's built-in ERT testing framework. No test files are currently present.
To run a single test in Emacs:
;; Evaluate the test definition, then run:
(ert-run-tests-interactively "^test-name$")
Or from command line:
emacs --batch --eval "(progn (load-file \"ai.el\") (ert \"^test-name$\"))"
Run all tests:
emacs --batch --eval "(progn (load-file \"ai.el\") (ert t))"
Linting Commands
Emacs has built-in linting tools:
;; Check documentation strings
(checkdoc "ppq-status.el")
;; Check for undefined variables (elint)
(elint-file "ppq-status.el")
;; Byte-compile to catch errors
(byte-compile-file "ppq-status.el")
Run linting from command line:
emacs --batch --eval "(progn (load-file \"ppq-status.el\") (checkdoc \"ppq-status.el\"))"
Code Style Guidelines
General Principles
- Use kebab-case for function and variable names (Emacs Lisp convention)
- Prefix all public functions/variables with project namespace:
ppq-ornano-gpt- - Private functions/variables: prefix with
--(e.g.,ppq--internal) - Use
lexical-binding: tin file header
Naming Conventions
- Functions:
ppq-function-nameornano-gpt-function-name - Variables:
ppq-variable-nameornano-gpt-variable-name - Private functions/variables:
ppq--function-name(double dash) - Modes:
ppq-foo-mode - Keymaps:
ppq-foo-mode-map - Buffer names:
*Dashboard:Name* - Custom groups:
ppqornano-gpt
Formatting
- Indent using 2 spaces (Emacs default for Elisp)
- Maximum line length: 80 characters
- Use
setqfor setting variables,defvarfor defining with docstrings - Place docstrings on line after
defun/defvaropening paren - Consistent spacing around operators:
(+ x y)not(+x y)
Imports and Dependencies
- Use
(require 'module)at top of file after header - Common dependencies:
url,json,auth-source,org,tabulated-list,hl-line - Use
(declare-function func "file")for external functions (e.g.,qrencode-string)
Types
- Use
:typeproperty indefcustomfor customization types - Use type predicates:
numberp,stringp,listp,functionp,bufferp,vectorp - Check for nil explicitly:
(when var ...)not(if var ...) - Use
eqfor symbol comparison,equalfor lists/strings
Error Handling
- Use
condition-casefor error catching - For async URL operations, check status with
(plist-get status :error) - Provide meaningful error messages in docstrings
- Handle
:nullvalues from JSON parsing explicitly
Keymap Conventions
- Use
make-sparse-keymapfor mode keymaps - Set keymap parent:
(set-keymap-parent map tabulated-list-mode-map) - Define keys using
define-keywith string sequences (e.g., "q", "C-x C-f") - Use
setqwithletfor local keymap creation
Mode Definition
- Use
define-derived-modefor major modes - Set
tabulated-list-formatandtabulated-list-entries - Call
tabulated-list-init-headerandtabulated-list-print - Set
inhibit-read-onlybefore modifying read-only buffers - Enable
hl-line-modefor highlighting current line
Async Operations
- Use
url-retrievefor async HTTP requests - Define callback functions with
statusparameter - Check for errors:
(unless (plist-get status :error) ...) - Use
(goto-char (point-min))before parsing buffer content - Use
(re-search-forward "^$" nil t)to skip HTTP headers
Documentation
- Every
defunanddefvarmust have a docstring - Docstrings start with descriptive verb ("Show", "Fetch", "Copy")
- Document interactive commands with
(interactive)and "Calling from program..." - Use
;;;###autoloadfor user-facing commands
Package Management
- Use
defcustomfor user-configurable variables with:type,:group,:default - Use
defvarfor internal variables with docstrings - Prefix custom group with project namespace
Project-Specific Patterns
Safe List Access (ALIST)
(defun ppq--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
;; 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
;; Save/restore window config
(setq ppq--window-config (current-window-configuration))
(set-window-configuration ppq--window-config)
;; Split windows
(split-window-right) ; vertical split
(split-window-below) ; horizontal split
(delete-other-windows) ; maximize current window
JSON Parsing
;; Parse JSON with alist object type
(let* ((json (json-parse-string (buffer-substring (point) (point-max))
:object-type 'alist))
(data (ppq--aget json 'data)))
...)
Tabulated List Mode
;; 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
(mapcar
(lambda (item)
(list (get-id item)
(vector (get-name item) (get-id item) ...)))
data))
Properize Text with Faces
(propertize text 'font-lock-face face)
;; face can be: '(:foreground "green"), 'bold, nil, etc.
Auth-Source Integration
(defun ppq--get-api-key ()
"Get API key from auth-source for ppq.ai."
(let ((secret (auth-source-search :host "ppq.ai" :secret t)))
(when secret
(funcall (plist-get (car secret) :secret)))))
URL Request Patterns
;; GET request
(url-retrieve url #'callback nil t t)
;; POST with JSON body
(let ((url-request-method "POST")
(url-request-extra-headers '(("Content-Type" . "application/json")))
(url-request-data (json-encode data)))
(url-retrieve url #'callback nil nil t))
;; POST with auth header
(let ((url-request-method "POST")
(url-request-extra-headers (list (cons "Authorization" (format "Bearer %s" key))))
(url-request-data (json-encode data)))
(url-retrieve url #'callback nil nil t))