Update AGENTS.md

This commit is contained in:
Jiri Jakes 2026-03-14 16:03:53 -03:00
parent 18a7d1f126
commit 81fbe9ed45

130
AGENTS.md
View file

@ -1,21 +1,29 @@
# Agent Guidelines for nano-gpt # Agent Guidelines for AI (Emacs Lisp)
This is an Emacs Lisp project for a dashboard application. The codebase consists of: This is an Emacs Lisp project providing dashboard packages for AI APIs. The codebase consists of:
- `dashboard.el` - Main dashboard implementation (180 lines) - `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 ## Build/Load Commands
```elisp ```elisp
;; Load the dashboard in Emacs ;; Load the package in Emacs
(load-file "dashboard.el") (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 for faster loading
(byte-compile-file "dashboard.el") (byte-compile-file "ai.el")
(byte-compile-file "ppq-status.el")
(byte-compile-file "nano-gpt-status.el")
``` ```
## Running Tests ## Running Tests
This project uses Emacs's built-in ERT testing framework. No external test framework is configured. This project uses Emacs's built-in ERT testing framework. No test files are currently present.
To run a single test in Emacs: To run a single test in Emacs:
```elisp ```elisp
@ -25,77 +33,79 @@ To run a single test in Emacs:
Or from command line: Or from command line:
```bash ```bash
emacs --batch --eval "(progn (load-file \"dashboard.el\") (ert \"^test-name$\"))" emacs --batch --eval "(progn (load-file \"ai.el\") (ert \"^test-name$\"))"
``` ```
Run all tests: Run all tests:
```bash ```bash
emacs --batch --eval "(progn (load-file \"dashboard.el\") (ert t))" emacs --batch --eval "(progn (load-file \"ai.el\") (ert t))"
``` ```
## Linting Commands ## Linting Commands
Emacs has built-in linting tools available: Emacs has built-in linting tools:
```elisp ```elisp
;; Check documentation strings ;; Check documentation strings
(checkdoc "dashboard.el") (checkdoc "ppq-status.el")
;; Check for undefined variables (elint) ;; Check for undefined variables (elint)
(elint-file "dashboard.el") (elint-file "ppq-status.el")
;; Byte-compile to catch errors ;; Byte-compile to catch errors
(byte-compile-file "dashboard.el") (byte-compile-file "ppq-status.el")
``` ```
Run linting from command line: Run linting from command line:
```bash ```bash
emacs --batch --eval "(progn (load-file \"dashboard.el\") (checkdoc \"dashboard.el\"))" emacs --batch --eval "(progn (load-file \"ppq-status.el\") (checkdoc \"ppq-status.el\"))"
``` ```
## Code Style Guidelines ## Code Style Guidelines
### General Principles ### General Principles
- Use CamelCase for function and variable names (Emacs Lisp convention) - Use kebab-case for function and variable names (Emacs Lisp convention)
- Prefix all public functions/variables with a project-specific namespace: `my-dashboard-` - Prefix all public functions/variables with project namespace: `ppq-` or `nano-gpt-`
- Use kebab-case for keymap names: `my-dashboard-mode-map` - Private functions/variables: prefix with `--` (e.g., `ppq--internal`)
- Use `lexical-binding: t` in file header
### Naming Conventions ### Naming Conventions
- Functions: `my-dashboard-function-name` - Functions: `ppq-function-name` or `nano-gpt-function-name`
- Variables: `my-dashboard-variable-name` - Variables: `ppq-variable-name` or `nano-gpt-variable-name`
- Private functions/variables: prefix with `--` (e.g., `my-dashboard--internal`) - Private functions/variables: `ppq--function-name` (double dash)
- Modes: `my-dashboard-foo-mode` - Modes: `ppq-foo-mode`
- Keymaps: `my-dashboard-foo-mode-map` - Keymaps: `ppq-foo-mode-map`
- Buffer names: `*Dashboard:Name*` - Buffer names: `*Dashboard:Name*`
- Custom groups: `ppq` or `nano-gpt`
### Formatting ### Formatting
- Indent using 2 spaces (Emacs default for Elisp) - Indent using 2 spaces (Emacs default for Elisp)
- Maximum line length: 80 characters - Maximum line length: 80 characters
- Use `setq` for setting variables, `defvar` for defining variables with docstrings - Use `setq` for setting variables, `defvar` for defining with docstrings
- Place docstrings on the line after `defun`/`defvar` opening paren - Place docstrings on line after `defun`/`defvar` opening paren
- Use consistent spacing around operators: `(+ x y)` not `(+x y)` - Consistent spacing around operators: `(+ x y)` not `(+x y)`
### Imports and Dependencies ### Imports and Dependencies
- Use `(require 'module)` at the top of files - Use `(require 'module)` at top of file after header
- Common dependencies: `url`, `json`, `tabulated-list`, `org` - Common dependencies: `url`, `json`, `auth-source`, `org`, `tabulated-list`, `hl-line`
- Use `use-package` for declarative package management if needed - Use `(declare-function func "file")` for external functions (e.g., `qrencode-string`)
### Types ### Types
- Use `:type` property in `defcustom` for customization types - Use `:type` property in `defcustom` for customization types
- Use type predicates: `numberp`, `stringp`, `listp`, `functionp`, `bufferp` - Use type predicates: `numberp`, `stringp`, `listp`, `functionp`, `bufferp`, `vectorp`
- Check for nil explicitly: `(when var ...)` not `(if var ...)` - Check for nil explicitly: `(when var ...)` not `(if var ...)`
- Use `eq` for symbol comparison, `equal` for lists/strings - Use `eq` for symbol comparison, `equal` for lists/strings
### Error Handling ### Error Handling
- Use `condition-case` for error catching - Use `condition-case` for error catching
- Check API error status with `plist-get status :error` - For async URL operations, check status with `(plist-get status :error)`
- Provide meaningful error messages in docstrings - Provide meaningful error messages in docstrings
- Handle `:null` values from JSON parsing explicitly - Handle `:null` values from JSON parsing explicitly
### Keymap Conventions ### Keymap Conventions
- Use `make-sparse-keymap` for mode keymaps - Use `make-sparse-keymap` for mode keymaps
- Set keymap parent to appropriate mode map (e.g., `tabulated-list-mode-map`) - Set keymap parent: `(set-keymap-parent map tabulated-list-mode-map)`
- Define keys using `define-key` with key sequences as strings (e.g., "q", "C-x C-f") - Define keys using `define-key` with string sequences (e.g., "q", "C-x C-f")
- Use `setq` with `let` for local keymap creation - Use `setq` with `let` for local keymap creation
### Mode Definition ### Mode Definition
@ -103,18 +113,20 @@ emacs --batch --eval "(progn (load-file \"dashboard.el\") (checkdoc \"dashboard.
- Set `tabulated-list-format` and `tabulated-list-entries` - Set `tabulated-list-format` and `tabulated-list-entries`
- Call `tabulated-list-init-header` and `tabulated-list-print` - Call `tabulated-list-init-header` and `tabulated-list-print`
- Set `inhibit-read-only` before modifying read-only buffers - Set `inhibit-read-only` before modifying read-only buffers
- Enable `hl-line-mode` for highlighting current line
### Async Operations ### Async Operations
- Use `url-retrieve` for async HTTP requests - Use `url-retrieve` for async HTTP requests
- Define callback functions with `status` parameter - Define callback functions with `status` parameter
- Check for errors with `plist-get status :error` - Check for errors: `(unless (plist-get status :error) ...)`
- Use `goto-char (point-min)` before parsing buffer content - Use `(goto-char (point-min))` before parsing buffer content
- Use `re-search-forward` to skip HTTP headers in response - Use `(re-search-forward "^$" nil t)` to skip HTTP headers
### Documentation ### Documentation
- Every `defun` and `defvar` should have a docstring - Every `defun` and `defvar` must have a docstring
- Docstrings start with descriptive verb ("Show", "Fetch", "Copy") - Docstrings start with descriptive verb ("Show", "Fetch", "Copy")
- Document interactive commands with `(interactive)` and "Calling from program..." - Document interactive commands with `(interactive)` and "Calling from program..."
- Use `;;;###autoload` for user-facing commands
### Package Management ### Package Management
- Use `defcustom` for user-configurable variables with `:type`, `:group`, `:default` - Use `defcustom` for user-configurable variables with `:type`, `:group`, `:default`
@ -123,9 +135,9 @@ emacs --batch --eval "(progn (load-file \"dashboard.el\") (checkdoc \"dashboard.
## Project-Specific Patterns ## Project-Specific Patterns
### Safe List Access ### Safe List Access (ALIST)
```elisp ```elisp
(defun my-dashboard--aget (alist key) (defun ppq--aget (alist key)
"Safely get value from ALIST for KEY, returns nil if null." "Safely get value from ALIST for KEY, returns nil if null."
(let ((val (cdr (assq key alist)))) (let ((val (cdr (assq key alist))))
(unless (eq val :null) val))) (unless (eq val :null) val)))
@ -149,8 +161,8 @@ emacs --batch --eval "(progn (load-file \"dashboard.el\") (checkdoc \"dashboard.
### Window Configuration ### Window Configuration
```elisp ```elisp
;; Save/restore window config ;; Save/restore window config
(setq my-dashboard--window-config (current-window-configuration)) (setq ppq--window-config (current-window-configuration))
(set-window-configuration my-dashboard--window-config) (set-window-configuration ppq--window-config)
;; Split windows ;; Split windows
(split-window-right) ; vertical split (split-window-right) ; vertical split
@ -163,7 +175,7 @@ emacs --batch --eval "(progn (load-file \"dashboard.el\") (checkdoc \"dashboard.
;; Parse JSON with alist object type ;; Parse JSON with alist object type
(let* ((json (json-parse-string (buffer-substring (point) (point-max)) (let* ((json (json-parse-string (buffer-substring (point) (point-max))
:object-type 'alist)) :object-type 'alist))
(data (cdr (assq 'data json)))) (data (ppq--aget json 'data)))
...) ...)
``` ```
@ -178,12 +190,42 @@ emacs --batch --eval "(progn (load-file \"dashboard.el\") (checkdoc \"dashboard.
;; Entries: (id [col1 col2 col3] ...) ;; Entries: (id [col1 col2 col3] ...)
(setq tabulated-list-entries (setq tabulated-list-entries
'((1 ["Apple" "Red" "Sweet"]) (mapcar
(2 ["Banana" "Yellow" "Sweet"]))) (lambda (item)
(list (get-id item)
(vector (get-name item) (get-id item) ...)))
data))
``` ```
### Properize Text with Faces ### Properize Text with Faces
```elisp ```elisp
(propertize text 'font-lock-face face) (propertize text 'font-lock-face face)
;; face can be: '(:foreground "green"), 'bold, etc. ;; face can be: '(:foreground "green"), 'bold, nil, etc.
```
### Auth-Source Integration
```elisp
(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
```elisp
;; 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))
``` ```