branch: externals/ellama
commit aff8c73aaf94d3a25a271ce453bbf57b096c9fbd
Author: Sergey Kostyaev <[email protected]>
Commit: Sergey Kostyaev <[email protected]>
Check README command coverage
Add a check-commands target to compare public Ellama commands with the
README Commands section and include it in check-readme so documentation hooks
catch missing or stale command entries. Use a static Elisp source parser to
avoid load-time false negatives, document the DLP commands it reports, and
refresh the generated Info manual.
---
Makefile | 7 ++-
README.org | 5 ++
ellama.info | 88 +++++++++++++++--------------
scripts/check-commands.sh | 125 +++++++++++++++++++++++++++++++++++++++++
scripts/extract-commands.el | 133 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 315 insertions(+), 43 deletions(-)
diff --git a/Makefile b/Makefile
index 232c72d485..ac3bea29a9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
# Makefile for ellama project
-.PHONY: build test test-detailed test-integration test-srt-integration
docker-build-srt-parity test-srt-integration-linux check-compile-warnings
checkdocs manual format-elisp install-git-hooks refill-news refill-readme
check-elisp check-readme check-news check-custom-variables
+.PHONY: build test test-detailed test-integration test-srt-integration
docker-build-srt-parity test-srt-integration-linux check-compile-warnings
checkdocs manual format-elisp install-git-hooks refill-news refill-readme
check-elisp check-readme check-news check-custom-variables check-commands
SRT_PARITY_DOCKER_IMAGE ?= ellama-srt-parity:latest
SRT_PARITY_DOCKERFILE ?= docker/srt-parity-linux.Dockerfile
@@ -114,9 +114,12 @@ refill-readme:
check-elisp: format-elisp build test check-compile-warnings checkdocs
-check-readme: refill-readme manual check-custom-variables
+check-readme: refill-readme manual check-custom-variables check-commands
check-news: refill-news
check-custom-variables:
./scripts/check-custom-variables.sh
+
+check-commands:
+ ./scripts/check-commands.sh
diff --git a/README.org b/README.org
index 2bafa00c19..ed0d1501e8 100644
--- a/README.org
+++ b/README.org
@@ -253,6 +253,11 @@ More sophisticated configuration example:
needed.
- ~ellama-tools-disable-all~: Disable all enabled tools simultaneously. Use
this
command to reset the system to a minimal state, ensuring no tools are active.
+- ~ellama-tools-dlp-show-incident-stats~: Show a summary buffer with recent DLP
+ incident statistics.
+- ~ellama-tools-dlp-clear-session-bypasses~: Clear session-scoped DLP bypasses.
+- ~ellama-tools-dlp-reset-runtime-state~: Reset in-memory DLP runtime state,
+ including incident logs and detector caches.
* Keymap
diff --git a/ellama.info b/ellama.info
index 37c78db3c7..f364a9248f 100644
--- a/ellama.info
+++ b/ellama.info
@@ -382,6 +382,12 @@ File: ellama.info, Node: Commands, Next: Keymap, Prev:
Installation, Up: Top
• ‘ellama-tools-disable-all’: Disable all enabled tools
simultaneously. Use this command to reset the system to a minimal
state, ensuring no tools are active.
+ • ‘ellama-tools-dlp-show-incident-stats’: Show a summary buffer with
+ recent DLP incident statistics.
+ • ‘ellama-tools-dlp-clear-session-bypasses’: Clear session-scoped DLP
+ bypasses.
+ • ‘ellama-tools-dlp-reset-runtime-state’: Reset in-memory DLP runtime
+ state, including incident logs and detector caches.
File: ellama.info, Node: Keymap, Next: Configuration, Prev: Commands, Up:
Top
@@ -2646,47 +2652,47 @@ Tag Table:
Node: Top1379
Node: Installation3973
Node: Commands8987
-Node: Keymap17925
-Node: Configuration20812
-Node: Session Provider Keys37385
-Node: Session Compaction39246
-Node: Image Input41540
-Node: Task Tool Subagents43685
-Node: Plan-and-Act Agent Loop46859
-Node: Edit Tool Shell Hooks49440
-Node: DLP for Tool Input/Output51733
-Node: SRT Filesystem Policy for Tools67732
-Node: Context Management73401
-Node: Transient Menus for Context Management74469
-Node: Managing the Context76148
-Node: Considerations76923
-Node: Minor modes77516
-Node: ellama-context-header-line-mode79504
-Node: ellama-context-header-line-global-mode80329
-Node: ellama-context-mode-line-mode81049
-Node: ellama-context-mode-line-global-mode81897
-Node: Ellama Session Header Line Mode82601
-Node: Enabling and Disabling83170
-Node: Customization83617
-Node: Ellama Session Mode Line Mode83905
-Node: Enabling and Disabling (1)84490
-Node: Customization (1)84937
-Node: Using Blueprints85231
-Node: Key Components of Ellama Blueprints85871
-Node: Creating and Managing Blueprints86478
-Node: Blueprints files87456
-Node: Variable Management87877
-Node: Keymap and Mode88330
-Node: Transient Menus89266
-Node: Running Blueprints programmatically89812
-Node: MCP Integration90399
-Node: Agent Skills91634
-Node: Directory Structure91997
-Node: Creating a Skill93024
-Node: How it works93399
-Node: Acknowledgments93790
-Node: Contributions94501
-Node: GNU Free Documentation License95175
+Node: Keymap18270
+Node: Configuration21157
+Node: Session Provider Keys37730
+Node: Session Compaction39591
+Node: Image Input41885
+Node: Task Tool Subagents44030
+Node: Plan-and-Act Agent Loop47204
+Node: Edit Tool Shell Hooks49785
+Node: DLP for Tool Input/Output52078
+Node: SRT Filesystem Policy for Tools68077
+Node: Context Management73746
+Node: Transient Menus for Context Management74814
+Node: Managing the Context76493
+Node: Considerations77268
+Node: Minor modes77861
+Node: ellama-context-header-line-mode79849
+Node: ellama-context-header-line-global-mode80674
+Node: ellama-context-mode-line-mode81394
+Node: ellama-context-mode-line-global-mode82242
+Node: Ellama Session Header Line Mode82946
+Node: Enabling and Disabling83515
+Node: Customization83962
+Node: Ellama Session Mode Line Mode84250
+Node: Enabling and Disabling (1)84835
+Node: Customization (1)85282
+Node: Using Blueprints85576
+Node: Key Components of Ellama Blueprints86216
+Node: Creating and Managing Blueprints86823
+Node: Blueprints files87801
+Node: Variable Management88222
+Node: Keymap and Mode88675
+Node: Transient Menus89611
+Node: Running Blueprints programmatically90157
+Node: MCP Integration90744
+Node: Agent Skills91979
+Node: Directory Structure92342
+Node: Creating a Skill93369
+Node: How it works93744
+Node: Acknowledgments94135
+Node: Contributions94846
+Node: GNU Free Documentation License95520
End Tag Table
diff --git a/scripts/check-commands.sh b/scripts/check-commands.sh
new file mode 100755
index 0000000000..45ad008f9f
--- /dev/null
+++ b/scripts/check-commands.sh
@@ -0,0 +1,125 @@
+#!/bin/bash
+# check-commands.sh - Verify interactive command documentation coverage
+#
+# Usage: ./scripts/check-commands.sh [project-dir]
+# If project-dir is omitted, uses the current directory.
+#
+# Performs two checks:
+# 1. Verify all public commands are documented in README.org
+# 2. Verify all documented commands in README.org correspond to real commands
+#
+# Exit code 0 = all checks pass, 1 = discrepancies found
+
+set -euo pipefail
+
+# Determine project root (resolve to absolute path)
+PROJECT_DIR="${1:-$(pwd)}"
+PROJECT_DIR="$(cd "$PROJECT_DIR" && pwd)"
+README_FILE="$PROJECT_DIR/README.org"
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+
+echo "=== Ellama Interactive Command Documentation Checker ==="
+echo "Project directory: $PROJECT_DIR"
+echo "README file: $README_FILE"
+echo ""
+
+# Validate README.org exists
+if [ ! -f "$README_FILE" ]; then
+ echo "ERROR: README.org not found at $README_FILE"
+ exit 1
+fi
+
+# Validate Elisp extraction script exists
+if [ ! -f "$SCRIPT_DIR/extract-commands.el" ]; then
+ echo "ERROR: Elisp extraction script not found at
$SCRIPT_DIR/extract-commands.el"
+ exit 1
+fi
+
+# Create temp files
+CMD_FILE=$(mktemp)
+DOCUMENTED_FILE=$(mktemp)
+TMP_RAW_DOCS=$(mktemp)
+
+trap "rm -f '$CMD_FILE' '$DOCUMENTED_FILE' '$TMP_RAW_DOCS'" EXIT
+
+# --- Step 1: Extract public commands using Emacs batch mode ---
+echo "--- Extracting public commands from .el files ---"
+
+# Use safe Emacs-based extraction to avoid false positives from regex.
+# Pass directory via environment variable to avoid command-line processing
issues
+if ELLAMA_DIR="$PROJECT_DIR" emacs -Q -batch -l
"$SCRIPT_DIR/extract-commands.el" 2>/dev/null > "$CMD_FILE"; then
+ CMD_COUNT=$(wc -l < "$CMD_FILE")
+ echo "Found $CMD_COUNT public commands in .el files."
+else
+ echo "ERROR: Failed to extract public commands."
+ exit 1
+fi
+echo ""
+
+# --- Step 2: Extract documented commands from README.org Commands section ---
+echo "--- Extracting documented commands from README.org Commands section ---"
+
+# Use oq to get Commands section range
+COMMANDS_SECTION_RANGE=$(oq "$README_FILE" ".section('Commands')" 2>&1 | head
-1 || true)
+
+# Extract lines from Commands section
+if [ -n "$COMMANDS_SECTION_RANGE" ]; then
+ # Parse line range
+ START_LINE=$(echo "$COMMANDS_SECTION_RANGE" | sed -n 's/.*lines
\([0-9]*\):.*/\1/p')
+ END_LINE=$(echo "$COMMANDS_SECTION_RANGE" | sed -n 's/.*lines
[0-9]*:\([0-9]*\)).*/\1/p')
+
+ # Extract documented commands
+ sed -n "${START_LINE},${END_LINE}p" "$README_FILE" \
+ | grep -oE '~ellama-[a-zA-Z0-9_-]+~' \
+ | sed 's/~//g' \
+ | sort -u > "$TMP_RAW_DOCS" || true
+else
+ echo "WARNING: Could not locate Commands section with oq, using fallback."
+ sed -n '/^\* Commands$/,/^\* /p' "$README_FILE" \
+ | grep -oE '~ellama-[a-zA-Z0-9_-]*~' \
+ | sed 's/~//g' \
+ | sort -u > "$TMP_RAW_DOCS" || true
+fi
+
+sort -u "$TMP_RAW_DOCS" > "$DOCUMENTED_FILE"
+
+DOCUMENTED_COUNT=$(wc -l < "$DOCUMENTED_FILE")
+echo "Found $DOCUMENTED_COUNT documented commands in README.org."
+echo ""
+
+# --- Step 3: Compare and report ---
+echo "--- Checking coverage ---"
+echo ""
+
+# Check 1: Public commands not documented
+UNDOCUMENTED=$(comm -23 "$CMD_FILE" "$DOCUMENTED_FILE")
+if [ -n "$UNDOCUMENTED" ]; then
+ echo "ERROR: Public commands NOT documented in README.org:"
+ echo "$UNDOCUMENTED"
+ UNDOCUMENTED_FLAG=1
+else
+ echo "OK: All public commands are documented."
+ UNDOCUMENTED_FLAG=0
+fi
+echo ""
+
+# Check 2: Documented commands that don't exist
+EXTRA=$(comm -13 "$CMD_FILE" "$DOCUMENTED_FILE")
+if [ -n "$EXTRA" ]; then
+ echo "ERROR: Commands documented in README.org that do not exist:"
+ echo "$EXTRA"
+ EXTRA_FLAG=1
+else
+ echo "OK: All documented commands correspond to real functions."
+ EXTRA_FLAG=0
+fi
+echo ""
+
+# Exit with error if discrepancies
+if [ "$UNDOCUMENTED_FLAG" -eq 1 ] || [ "$EXTRA_FLAG" -eq 1 ]; then
+ echo "FAIL: Documentation coverage issues detected."
+ exit 1
+fi
+
+echo "PASS: All public commands are properly documented."
+exit 0
diff --git a/scripts/extract-commands.el b/scripts/extract-commands.el
new file mode 100644
index 0000000000..7ec746fa51
--- /dev/null
+++ b/scripts/extract-commands.el
@@ -0,0 +1,133 @@
+;;; extract-commands.el --- Extract documented public commands -*-
lexical-binding: t -*-
+
+;; This script reads Ellama source files as forms. It intentionally does not
+;; load the project, because loading depends on user packages and local runtime
+;; state and can silently miss commands in batch checks.
+
+(require 'cl-lib)
+
+(defconst check-commands--extra-public-commands
+ '(ellama-summarize-webpage
+ ellama-tools-dlp-clear-session-bypasses
+ ellama-tools-dlp-reset-runtime-state
+ ellama-tools-dlp-show-incident-stats)
+ "Interactive public commands without autoload cookies.")
+
+(defconst check-commands--ignored-commands
+ '(ellama-blueprint-chat-with-system-kill-buffer
+ ellama-blueprint-create
+ ellama-blueprint-edit-system-message
+ ellama-blueprint-new
+ ellama-blueprint-remove
+ ellama-blueprint-select
+ ellama-blueprint-select-user-defined-blueprint
+ ellama-blueprint-set-system-kill-buffer
+ ellama-chat-with-system-from-buffer
+ ellama-context-add-file-quote
+ ellama-context-add-info-node-quote
+ ellama-context-add-webpage-quote-eww
+ ellama-context-element-remove-by-name
+ ellama-eval-run-hypothesis-suite-interactive
+ ellama-kill-current-buffer
+ ellama-manual-export
+ ellama-send-buffer-to-new-chat
+ ellama-send-buffer-to-new-chat-then-kill
+ ellama-transient-ask-menu
+ ellama-transient-blueprint-menu
+ ellama-transient-blueprint-mode-menu
+ ellama-transient-code-menu
+ ellama-transient-context-menu
+ ellama-transient-improve-menu
+ ellama-transient-main-menu
+ ellama-transient-make-menu
+ ellama-transient-session-menu
+ ellama-transient-summarize-menu
+ ellama-transient-tools-menu
+ ellama-transient-translate-menu)
+ "Interactive commands intentionally excluded from README Commands coverage.")
+
+(defun check-commands--source-files (dir)
+ "Return Ellama source .el files in DIR."
+ (sort
+ (directory-files dir t "\\`ellama.*\\.el\\'")
+ #'string<))
+
+(defun check-commands--public-command-name-p (name)
+ "Return non-nil when NAME is an Ellama command name worth documenting."
+ (and (symbolp name)
+ (string-prefix-p "ellama-" (symbol-name name))
+ (not (string-prefix-p "ellama--" (symbol-name name)))
+ (not (memq name check-commands--ignored-commands))))
+
+(defun check-commands--interactive-body-p (body)
+ "Return non-nil when BODY starts with an `interactive' form."
+ (when (stringp (car body))
+ (setq body (cdr body)))
+ (while (and (consp (car body))
+ (eq (caar body) 'declare))
+ (setq body (cdr body)))
+ (and (consp (car body))
+ (eq (caar body) 'interactive)))
+
+(defun check-commands--command-form-name (form autoloadp)
+ "Return public command name from FORM when AUTOLOADP marks it as public."
+ (when (consp form)
+ (let ((head (car form))
+ (name (cadr form)))
+ (cond
+ ((memq head '(defun cl-defun))
+ (let ((body (cdddr form)))
+ (when (and autoloadp
+ (check-commands--public-command-name-p name)
+ (check-commands--interactive-body-p body))
+ name)))
+ ((eq head 'transient-define-prefix)
+ (when (and autoloadp
+ (check-commands--public-command-name-p name))
+ name))))))
+
+(defun check-commands--extract-file (file)
+ "Return public command symbols found in FILE."
+ (let (commands)
+ (with-temp-buffer
+ (insert-file-contents file)
+ (emacs-lisp-mode)
+ (goto-char (point-min))
+ (while (not (eobp))
+ (condition-case nil
+ (let* ((comment-start (point))
+ (_ (forward-comment (point-max)))
+ (autoloadp
+ (string-match-p
+ ";;;###autoload"
+ (buffer-substring-no-properties
+ comment-start (point))))
+ (form (unless (eobp)
+ (read (current-buffer))))
+ (name (check-commands--command-form-name form autoloadp)))
+ (when name
+ (push name commands)))
+ (end-of-file
+ (goto-char (point-max)))
+ (invalid-read-syntax
+ (forward-char 1))
+ (error
+ (forward-char 1)))))
+ commands))
+
+(defun check-commands--extract-public (dir)
+ "Extract public command names from Ellama sources in DIR."
+ (let ((commands (copy-sequence check-commands--extra-public-commands)))
+ (dolist (file (check-commands--source-files dir))
+ (setq commands (append (check-commands--extract-file file) commands)))
+ (dolist (command (sort (delete-dups commands)
+ (lambda (left right)
+ (string< (symbol-name left)
+ (symbol-name right)))))
+ (princ (format "%s\n" command)))))
+
+(when (and noninteractive (getenv "ELLAMA_DIR"))
+ (check-commands--extract-public (getenv "ELLAMA_DIR")))
+
+(provide 'extract-commands)
+;;; extract-commands.el ends here