From 81fbe9ed453a3123ccdd382c7bdb5c12be742f72 Mon Sep 17 00:00:00 2001 From: Jiri Jakes Date: Sat, 14 Mar 2026 16:03:53 -0300 Subject: [PATCH] Update AGENTS.md --- AGENTS.md | 130 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 44 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 66357ff..1d709ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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: -- `dashboard.el` - Main dashboard implementation (180 lines) +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 ```elisp -;; Load the dashboard in Emacs -(load-file "dashboard.el") +;; 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 "dashboard.el") +(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 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: ```elisp @@ -25,77 +33,79 @@ To run a single test in Emacs: Or from command line: ```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: ```bash -emacs --batch --eval "(progn (load-file \"dashboard.el\") (ert t))" +emacs --batch --eval "(progn (load-file \"ai.el\") (ert t))" ``` ## Linting Commands -Emacs has built-in linting tools available: +Emacs has built-in linting tools: ```elisp ;; Check documentation strings -(checkdoc "dashboard.el") +(checkdoc "ppq-status.el") ;; Check for undefined variables (elint) -(elint-file "dashboard.el") +(elint-file "ppq-status.el") ;; Byte-compile to catch errors -(byte-compile-file "dashboard.el") +(byte-compile-file "ppq-status.el") ``` Run linting from command line: ```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 ### 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` +- 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: `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` +- 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 variables with docstrings -- Place docstrings on the line after `defun`/`defvar` opening paren -- Use consistent spacing around operators: `(+ x y)` not `(+x y)` +- 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 the top of files -- Common dependencies: `url`, `json`, `tabulated-list`, `org` -- Use `use-package` for declarative package management if needed +- 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` +- 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 -- 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 - 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") +- 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 @@ -103,18 +113,20 @@ emacs --batch --eval "(progn (load-file \"dashboard.el\") (checkdoc \"dashboard. - 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 with `plist-get status :error` -- Use `goto-char (point-min)` before parsing buffer content -- Use `re-search-forward` to skip HTTP headers in response +- 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` should have a docstring +- 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` @@ -123,9 +135,9 @@ emacs --batch --eval "(progn (load-file \"dashboard.el\") (checkdoc \"dashboard. ## Project-Specific Patterns -### Safe List Access +### Safe List Access (ALIST) ```elisp -(defun my-dashboard--aget (alist key) +(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))) @@ -149,8 +161,8 @@ emacs --batch --eval "(progn (load-file \"dashboard.el\") (checkdoc \"dashboard. ### Window Configuration ```elisp ;; Save/restore window config -(setq my-dashboard--window-config (current-window-configuration)) -(set-window-configuration my-dashboard--window-config) +(setq ppq--window-config (current-window-configuration)) +(set-window-configuration ppq--window-config) ;; Split windows (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 (let* ((json (json-parse-string (buffer-substring (point) (point-max)) :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] ...) (setq tabulated-list-entries - '((1 ["Apple" "Red" "Sweet"]) - (2 ["Banana" "Yellow" "Sweet"]))) + (mapcar + (lambda (item) + (list (get-id item) + (vector (get-name item) (get-id item) ...))) + data)) ``` ### Properize Text with Faces ```elisp (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)) ```