Files
passepartout/org/programming-lisp.org
Amr Gharbeia c227877302 v0.8.3: TUI stabilization — box calls, package fixes, sandbox, configure
Bug fixes:
- Fix box() calls: set color-pair before box, pass ACS default chtype integers
- Fix markdown functions: move to passepartout.channel-tui package where
  Croatoan is imported; use add-attributes/remove-attributes instead of
  :bold/:underline kwargs to add-string; call theme-color in gate-trace-lines
  to convert theme keys to Croatoan colors
- Fix sandbox: remove dex:get/dex:post from restricted symbols
  (blocked neuro-provider from loading)
- Export *log-lock* from passepartout (was unbound in jailed skill packages)
- Fix configure: always deploy to XDG, skip cp when source==dest
- Fix bash crash handler format string (~~ escaping)
- Revert test reorder in 28 files (caused package leakage in skill loader)

Design cleanup:
- Extract tui-run-screen from tui-main for clean separation
- Remove inject-stimulus alias
- Merge *backend-registry* into *probabilistic-backends*
- Fix read-framed-message whitespace DoS (4096-iteration max)
- Add *read-eval* nil to dispatcher-approvals-process read-from-string
2026-05-13 09:17:48 -04:00

14 KiB

SKILL: Utils Lisp (org-skill-utils-lisp.org)

Architectural Intent: The Lisp Surgeon's Toolkit

When the agent needs to modify its own code — fix a bug, add a feature, refactor a skill — it reaches for Utils Lisp. This skill provides every operation needed to read, validate, modify, and write Lisp code from within Lisp itself.

This is possible only because Lisp is homoiconic: code is data. The agent can parse a function definition from a string, extract its body, wrap it in a new form, inject a new expression, and validate the result — all using the same data structures that the Lisp runtime uses to execute the code.

The skill has four layers:

  1. Validation — three-phase gate: structural (paren balance) → syntactic (reader safety) → semantic (dangerous forms)
  2. Evaluation — sandboxed eval in a jailed package with *read-eval* nil
  3. Structural surgery — extract, inject, wrap, slurp — surgical code transformations without regex
  4. Formatting — auto-indentation via Emacs batch mode

Contract

  1. (lisp-structural-check code): returns (values T nil) if parentheses balanced, (values nil error-msg) if reader errors detected.
  2. (lisp-syntactic-check code): alias for lisp-structural-check.
  3. (lisp-semantic-check code): returns (values T nil) if no unsafe forms (eval, load, run-program) found; (values nil reason) if blocked.
  4. (lisp-validate code &key strict): unified gate — returns (:status :success) or (:status :error :reason ...).
  5. (lisp-eval code-string): sandboxed eval with captured output. Returns (:status :success :result ...) or (:status :error ...).
  6. (lisp-extract code fn-name): extracts a single defun from code.
  7. (lisp-list-definitions code): returns list of defined symbol names.
  8. (lisp-inject code target new-form): injects a form into a function body.
  9. (lisp-slurp code target form): appends a form to a function body.

Implementation

Package Context

(in-package :passepartout)

Structural Validation

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun lisp-structural-check (code)
  "Checks if parentheses are balanced and the code is readable."
  (handler-case
      (let ((*read-eval* nil))
        (with-input-from-string (s code)
          (loop for form = (read s nil :eof) until (eq form :eof)))
        (values t nil))
    (error (c)
      (values nil (format nil "Reader Error: ~a" c)))))

Syntactic Validation

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun lisp-syntactic-check (code)
  "Checks for valid Lisp syntax beyond just balanced parentheses."
  (lisp-structural-check code))

Semantic Validation (Safety)

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun lisp-semantic-check (code)
  "Checks for potentially unsafe forms."
  (let ((unsafe-tokens '("eval" "load" "uiop:run-program" "sb-ext:run-program" "cl-user::eval")))
    (loop for token in unsafe-tokens
          when (search token (string-downcase code))
          do (return-from lisp-semantic-check (values nil (format nil "Unsafe form detected: ~a" token))))
    (values t nil)))

Unified Validation Gate

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun lisp-validate (code &key (strict t))
  "Unified validation gate for Lisp code."
  (multiple-value-bind (struct-ok struct-err) (lisp-structural-check code)
    (unless struct-ok
      (return-from lisp-validate (list :status :error :reason struct-err)))
    (when strict
      (multiple-value-bind (sem-ok sem-err) (lisp-semantic-check code)
        (unless sem-ok
          (return-from lisp-validate (list :status :error :reason sem-err)))))
    (list :status :success)))

Evaluation (REPL)

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun lisp-eval (code-string &key (package :passepartout))
  "Evaluates a Lisp string and captures its output/results."
  (let ((out (make-string-output-stream))
        (err (make-string-output-stream)))
    (handler-case
        (let* ((*standard-output* out)
               (*error-output* err)
               (*package* (or (find-package package) (find-package :passepartout)))
               (result (with-input-from-string (s code-string)
                         (let ((last-val nil))
                           (loop for form = (read s nil :eof) until (eq form :eof)
                                 do (setf last-val (eval form)))
                           last-val))))
          (list :status :success
                :result (format nil "~a" result)
                :output (get-output-stream-string out)
                :error (get-output-stream-string err)))
      (error (c)
        (list :status :error
              :reason (format nil "~a" c)
              :output (get-output-stream-string out)
              :error (get-output-stream-string err))))))

Formatting (Emacs Batch)

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun lisp-format (code-string)
  "Attempts to format Lisp code using Emacs batch mode if available."
  (handler-case
      (let ((tmp-file "/tmp/oc-format-temp.lisp"))
        (uiop:with-output-file (s tmp-file :if-exists :supersede)
          (format s "~a" code-string))
        (multiple-value-bind (out err code)
            (uiop:run-program (list "emacs" "--batch" tmp-file 
                                   "--eval" "(indent-region (point-min) (point-max))"
                                   "--eval" "(princ (buffer-string))")
                             :output :string :error-output :string :ignore-error-status t)
          (if (= code 0)
              out
              (progn
                (log-message "FORMAT ERROR: ~a" err)
                code-string))))
    (error (c)
      (log-message "FORMAT EXCEPTION: ~a" c)
      code-string)))

Structural Extraction (AST)

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun lisp-extract (code function-name)
  "Extracts the definition of a specific function from a code string."
  (let ((*read-eval* nil))
    (with-input-from-string (s code)
      (loop for form = (read s nil :eof) until (eq form :eof)
            when (and (listp form) 
                      (symbolp (car form))
                      (member (symbol-name (car form)) '("DEFUN" "DEFMACRO" "DEFMETHOD") :test #'string-equal)
                      (symbolp (second form))
                      (string-equal (symbol-name (second form)) function-name))
            do (return-from lisp-extract (format nil "~s" form))))
    nil))

Structural Wrapping (AST)

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun lisp-wrap (code target-name wrapper-symbol)
  "Wraps a specific form in a wrapper form (e.g., wrap in a let)."
  (let ((*read-eval* nil) (results nil))
    (with-input-from-string (s code)
      (loop for form = (read s nil :eof) until (eq form :eof)
            do (if (and (listp form) 
                        (symbolp (second form))
                        (string-equal (symbol-name (second form)) target-name))
                   (push (list wrapper-symbol form) results)
                   (push form results))))
    (format nil "~{~s~^~%~%~}" (nreverse results))))

List Definitions

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun lisp-list-definitions (code)
  "Returns a list of names for all top-level definitions (defun, defmacro, etc.)."
  (let ((*read-eval* nil) (names nil))
    (with-input-from-string (s code)
      (loop for form = (read s nil :eof) until (eq form :eof)
            when (and (listp form) 
                      (symbolp (car form))
                      (member (symbol-name (car form)) 
                              '("DEFUN" "DEFMACRO" "DEFMETHOD" "DEFVAR" "DEFPARAMETER")
                              :test #'string-equal)
                      (symbolp (second form)))
            do (push (second form) names)))
    (nreverse names)))

Structural Injection (AST)

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun lisp-inject (code target-name new-form-string)
  "Injects a new form into the body of a targeted definition."
  (let ((*read-eval* nil)
        (new-form (read-from-string new-form-string))
        (results nil))
    (with-input-from-string (s code)
      (loop for form = (read s nil :eof) until (eq form :eof)
            do (if (and (listp form) 
                        (symbolp (car form))
                        (member (symbol-name (car form)) '("DEFUN" "DEFMACRO" "DEFMETHOD") :test #'string-equal)
                        (symbolp (second form))
                        (string-equal (symbol-name (second form)) target-name))
                   (push (append form (list new-form)) results)
                   (push form results))))
    (format nil "~{~s~^~%~%~}" (nreverse results))))

Structural Slurp (AST)

;; REPL-VERIFIED: 2026-05-03T13:00:00

(defun lisp-slurp (code target-name form-to-slurp-string)
  "Adds a form to the end of a named list or definition (Paredit slurp)."
  (let ((*read-eval* nil)
        (to-slurp (read-from-string form-to-slurp-string))
        (results nil))
    (with-input-from-string (s code)
      (loop for form = (read s nil :eof) until (eq form :eof)
            do (if (and (listp form) 
                        (symbolp (second form))
                        (string-equal (symbol-name (second form)) target-name))
                   (push (append form (list to-slurp)) results)
                   (push form results))))
    (format nil "~{~s~^~%~%~}" (nreverse results))))

Skill Registration

(defskill :passepartout-programming-lisp
  :priority 400
  :trigger (lambda (ctx) (declare (ignore ctx)) nil))

Plist Keywords Normalize (relocated from core-reason)

Lisp keywords are case-sensitive. The LLM might produce :payload or :PAYLOAD depending on the model. This function normalizes keyword keys to uppercase.

(defun plist-keywords-normalize (plist)
  (when (listp plist)
    (loop for (k v) on plist by #'cddr
          collect (if (and (symbolp k) (not (keywordp k)))
                       (intern (string k) :keyword)
                       k)
          collect v)))

Test Suite

Tests for the Lisp Validator structural, syntactic, and semantic gates.

(defpackage :passepartout-utils-lisp-tests
  (:use :cl :fiveam :passepartout)
  (:export #:utils-lisp-suite))

(in-package :passepartout-utils-lisp-tests)

(def-suite utils-lisp-suite
  :description "Tests for the Lisp Validator structural, syntactic, and semantic gates")

(in-suite utils-lisp-suite)

(test structural-balanced
  "Contract 1: balanced code returns T."
  (is (eq t (passepartout:lisp-structural-check "(+ 1 2)"))))

(test structural-unbalanced-open
  "Contract 1: missing close paren returns nil + error."
  (multiple-value-bind (ok reason) (passepartout:lisp-structural-check "(+ 1 2")
    (is (null ok))
    (is (search "Reader Error" reason))))

(test structural-unbalanced-close
  "Contract 1: extra close paren returns nil + error."
  (multiple-value-bind (ok reason) (passepartout:lisp-structural-check "+ 1 2)")
    (is (null ok))
    (is (search "Reader Error" reason))))

(test syntactic-valid
  "Contract 2: valid syntax passes syntactic check."
  (is (eq t (passepartout:lisp-syntactic-check "(+ 1 2)"))))

(test semantic-safe
  "Contract 3: safe code passes semantic check."
  (is (eq t (passepartout:lisp-semantic-check "(+ 1 2)"))))

(test semantic-blocked-eval
  "Contract 3: eval forms are blocked by semantic check."
  (multiple-value-bind (ok reason) (passepartout:lisp-semantic-check "(eval '(+ 1 2))")
    (is (null ok))
    (is (search "Unsafe" reason))))

(test unified-success
  "Contract 4: valid code returns :success via lisp-validate."
  (let ((result (passepartout:lisp-validate "(+ 1 2)" :strict t)))
    (is (eq (getf result :status) :success))))

(test unified-failure
  "Contract 4: invalid code returns :error via lisp-validate."
  (let ((result (passepartout:lisp-validate "(+ 1 2" :strict nil)))
    (is (eq (getf result :status) :error))))

(test eval-basic
  "Contract 5: lisp-eval returns :success with captured result."
  (let ((result (passepartout:lisp-eval "(+ 1 2)")))
    (is (eq (getf result :status) :success))
    (is (string= (getf result :result) "3"))))

(test structural-extract
  "Contract 6: lisp-extract finds and returns a named function."
  (let* ((code "(defun hello () (print \"hi\")) (defun bye () (print \"bye\"))")
         (extracted (passepartout:lisp-extract code "hello")))
    (is (not (null extracted)))
    (let ((form (read-from-string extracted)))
      (is (eq (car form) 'DEFUN))
      (is (eq (second form) 'HELLO)))))

(test list-definitions
  "Contract 7: lisp-list-definitions returns all defined names."
  (let ((code "(defun foo () t) (defmacro bar () nil) (defparameter *baz* 10)"))
    (let ((names (passepartout:lisp-list-definitions code)))
      (is (member 'FOO names))
      (is (member 'BAR names))
      (is (member '*BAZ* names)))))

(test structural-inject
  "Contract 8: lisp-inject adds a form to a function body."
  (let* ((code "(defun my-fun (x) (print x))")
         (injected (passepartout:lisp-inject code "my-fun" "(finish-output)")))
    (let ((form (read-from-string injected)))
      (is (equal (last form) '((FINISH-OUTPUT)))))))

(test structural-slurp
  "Contract 9: lisp-slurp appends a form to a function body."
  (let* ((code "(defun work () (step-1))")
         (slurped (passepartout:lisp-slurp code "work" "(step-2)")))
    (let ((form (read-from-string slurped)))
      (is (equal (last form) '((STEP-2)))))))