ai/AGENTS.md
2026-03-14 16:03:53 -03:00

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 dashboards
  • ppq-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- or nano-gpt-
  • Private functions/variables: prefix with -- (e.g., ppq--internal)
  • Use lexical-binding: t in file header

Naming Conventions

  • Functions: ppq-function-name or nano-gpt-function-name
  • Variables: ppq-variable-name or nano-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: ppq or nano-gpt

Formatting

  • Indent using 2 spaces (Emacs default for Elisp)
  • Maximum line length: 80 characters
  • Use setq for setting variables, defvar for defining with docstrings
  • Place docstrings on line after defun/defvar opening 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 :type property in defcustom for customization types
  • Use type predicates: numberp, stringp, listp, functionp, bufferp, vectorp
  • 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
  • For async URL operations, check 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: (set-keymap-parent map tabulated-list-mode-map)
  • Define keys using define-key with string sequences (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
  • Enable hl-line-mode for highlighting current line

Async Operations

  • Use url-retrieve for async HTTP requests
  • Define callback functions with status parameter
  • 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 defun and defvar must have a docstring
  • Docstrings start with descriptive verb ("Show", "Fetch", "Copy")
  • Document interactive commands with (interactive) and "Calling from program..."
  • Use ;;;###autoload for user-facing commands

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 (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))