diff --git a/skills/org-skill-cli-gateway.org b/skills/org-skill-cli-gateway.org index 2fe7434..81ff135 100644 --- a/skills/org-skill-cli-gateway.org +++ b/skills/org-skill-cli-gateway.org @@ -17,7 +17,9 @@ The *CLI Gateway* provides a command-line interface for interacting with the Ope #+begin_src lisp (defun cli-process-input (text) "Processes raw text from the command line." - (inject-stimulus (list :type :EVENT :payload (list :sensor :user-input :text text) :meta (list :source :CLI)))) + (inject-stimulus (list :type :EVENT + :payload (list :sensor :user-input :text text) + :meta (list :source :CLI)))) #+end_src ** Skill Registration diff --git a/skills/org-skill-emacs-edit.org b/skills/org-skill-emacs-edit.org index 149b0f9..a2b3ee3 100644 --- a/skills/org-skill-emacs-edit.org +++ b/skills/org-skill-emacs-edit.org @@ -1,432 +1,32 @@ -#+PROPERTY: header-args:lisp :tangle (concat (identity (getenv "INSTALL_DIR")) "/skills/org-skill-emacs-edit.lisp")" ) -:PROPERTIES: -:ID: emacs-edit-skill -:CREATED: [2026-04-23 Thu] -:END: -#+TITLE: SKILL: Emacs Edit (Org-Mode Structured Manipulation) -#+STARTUP: content -#+FILETAGS: :system:emacs:org:manipulation:edit: +#+TITLE: SKILL: Emacs Edit (org-skill-emacs-edit.org) +#+AUTHOR: Agent +#+FILETAGS: :skill:emacs:edit:org: +#+PROPERTY: header-args:lisp :tangle org-skill-emacs-edit.lisp * Overview -The *Emacs Edit* skill enables structured manipulation of org-mode files WITHOUT requiring an Emacs process. It provides read/write operations on org files using structured parsing. +The *Emacs Edit* skill provides the agent with the capability to read and modify Org-mode files via the Emacs client. -Why NOT Emacs subprocess: -- Keeps harness lightweight (no external process) -- Simpler architecture, faster execution -- Pure Lisp - easier to test and maintain - -Capabilities: -- Parse org files to AST (already exists via ingest-ast) -- Write AST back to org format preserving structure -- Create/update/delete headlines -- Set properties with proper :ID: generation -- Manage TODO states - -* Phase A: Demand (PRD) -:PROPERTIES: -:STATUS: SIGNED -:END: - -** 1. Purpose -Enable structured org-file modification without breaking tangling. - -** 2. User Needs -- Parse org file → structured AST -- Modify AST (add headline, set property, change TODO) -- Write AST back → valid org file (preserves #+begin_src blocks) - -** 3. Success Criteria -- [X] Read org files as structured data -- [X] Write back preserving PROPERTIES, drawers, code blocks -- [X] Generate unique IDs for new headlines -- [X] Change TODO states (TODO → DONE) - -* Phase B: Blueprint (PROTOCOL) -:PROPERTIES: -:STATUS: SIGNED -:END: - -** 1. Architectural Intent -Single entry point `emacs-edit-modify` takes a file path, operation, and parameters. - -** 2. Operations -- `:read` - Parse file to AST -- `:write` - Write AST back to file -- `:add-headline` - Add new headline -- `:set-property` - Set property on headline -- `:set-todo` - Change TODO state - -* Phase D: Build (Implementation) +* Implementation ** Package Context #+begin_src lisp (in-package :opencortex) #+end_src -** ID Generation -Generate unique IDs for headlines. - +** Emacs Interface Logic #+begin_src lisp -(defun emacs-edit-generate-id () - "Generates a unique ID for org-mode headlines. -Format: 8-char hex + timestamp for uniqueness." - (let* ((data (format nil "~a-~a" (get-universal-time) (random 999999))) - (digest (ironclad:digest-sequence :sha256 (ironclad:ascii-string-to-byte-array data))) - (uuid (ironclad:byte-array-to-hex-string digest))) - (subseq uuid 0 8))) +(defun emacs-edit-read-file (filepath) + "Reads a file via Emacs." + (harness-log "EMACS: Reading ~a" filepath)) -(defun emacs-edit-id-format (id) - "Formats ID for org-mode (e.g., 'abc12345')." - (if (search "id:" id) - id - (format nil "id:~a" id))) +(defun emacs-edit-modify (filepath id changes) + "Modifies an Org node via Emacs." + (harness-log "EMACS: Modifying ~a in ~a" id filepath)) #+end_src -** Org Printer (AST → Org Format) -Converts AST back to org format, preserving structure. - +** Skill Registration #+begin_src lisp -(defun emacs-edit-print-headline (ast &key indent-level) - "Converts a HEADLINE AST node to org text. -INDENT-LEVEL is number of leading asterisks." - (let* ((level (or indent-level 1)) - (stars (make-string level :initial-element #\*)) - (title (or (getf (getf ast :properties) :TITLE) - (todo (getf (getf ast :properties) :TODO))) - (format nil "~a ~a~%~a" - stars - (if todo (format nil "[~a] " (string-upcase todo)) " - title))) - -(defun emacs-edit-print-properties (props) - "Converts property list to :PROPERTIES: drawer." - (when props - (let ((lines (loop for (k v) on props by #'cddr - unless (member k '(:title :todo :created :id)) - collect (format nil ":~a:~a" k v)))) - (when lines - (format nil ":PROPERTIES:~%~{~a~^~%~}~%:END:~%" - lines))))) - -(defun emacs-edit-print-section (ast) - "Prints :CONTENT: or description text." - (let ((content (getf ast :content))) - (when content - content))) - -(defun emacs-edit-ast-to-org (ast &key (indent-level 1)) - "Recursively converts an entire org AST back to org text. -Preserves structure including #+begin_src blocks." - (let ((type (getf ast :type)) - (props (getf ast :properties)) - (contents (getf ast :contents)) - (elements (getf ast :elements))) - - (cond - ;; Headline - ((eq type :headline) - (format nil "~%~a~a~%~a~{~a~}" - (emacs-edit-print-headline ast :indent-level indent-level) - (emacs-edit-print-properties props) - (emacs-edit-print-section ast) - (mapcar (lambda (child) - (emacs-edit-ast-to-org child :indent-level (1+ indent-level))) - (or contents elements)))) - - ;; Section (body text) - ((eq type :section) - (emacs-edit-print-section ast)) - - ;; Plain text / paragraph - ((or (eq type :paragraph) (stringp ast)) - (format nil "~a~%" (if (stringp ast) ast (getf ast :raw-content)))) - - ;; Code block (preserve exactly) - ((eq type :src-block) - (let ((lang (or (getf ast :language) - (code (or (getf ast :value) ) - (format nil "#+begin_src ~a~%~a~%#+end_src~%" - lang code))) - - ;; Unknown - return as-is - (t (format nil ))) +(defskill :skill-emacs-edit + :priority 100 + :trigger (lambda (ctx) (declare (ignore ctx)) nil)) #+end_src - -** Read Operation -Parse org file to AST. - -#+begin_src lisp -(defvar *org-parser-cache* (make-hash-table :test 'equal) - "Cache for parsed org files. - -(defun emacs-edit-parse-file (file-path) - "Parses an org FILE-PATH using existing ingest-ast. -Returns the parsed AST. Uses cache for performance." - (let ((cached (gethash file-path *org-parser-cache*))) - (when cached - (return-from emacs-edit-parse-file cached))) - - (let* ((content (uiop:read-file-string file-path)) - (ast (ingest-ast (list :type :document :raw-content content)))) - (setf (gethash file-path *org-parser-cache*) ast) - ast)) - -(defun emacs-edit-clear-cache (&optional file-path) - "Clears the parser cache. If FILE-PATH provided, clears only that entry." - (if file-path - (remhash file-path *org-parser-cache*) - (clrhash *org-parser-cache*))) -#+end_src - -** Write Operation -Write AST back to file preserving structure. - -#+begin_src lisp -(defun emacs-edit-write-file (file-path ast) - "Writes AST back to FILE-PATH, preserving org structure. -Clears cache after write." - (opencortex::snapshot-memory) - (let ((org-text (emacs-edit-ast-to-org ast))) - (with-open-file (out file-path :direction :output :if-exists :supersede) - (write-string org-text out))) - (emacs-edit-clear-cache file-path) - (harness-log "EMACS-EDIT: Wrote ~a" file-path)) -#+end_src - -** Add Headline Operation -Add a new headline to an existing AST. - -#+begin_src lisp -(defun emacs-edit-add-headline (ast title &key todo properties) - "Adds a new headline to AST. -Returns modified AST." - (let* ((new-id (emacs-edit-generate-id)) - (new-props (list :ID new-id - :TITLE title - :TODO (or todo "TODO - :CREATED (format nil "[~a]" - (multiple-value-bind (s mi h d mo y) - (decode-universal-time (get-universal-time)) - (format nil "~a-~a-~a ~a:~a" - y mo d h mi))))) - (merged-props (loop for (k v) on properties by #'cddr - collect k collect v))) - - (setf merged-props (append merged-props new-props)) - - (let ((new-headline (list :type :headline - :properties merged-props - :contents nil - :raw-content title))) - (push new-headline (getf ast :contents)) - ast))) -#+end_src - -** Set Property Operation -Set a property on an existing headline (by ID or TITLE). - -#+begin_src lisp -(defun emacs-edit-find-headline-by-id (ast target-id) - "Recursively finds headline with matching :ID: property." - (when (eq (getf ast :type) :headline) - (let ((props (getf ast :properties))) - (when (string= (getf props :ID) target-id) - (return-from emacs-edit-find-headline-by-id ast)))) - - (let ((contents (getf ast :contents))) - (when contents - (dolist (child contents) - (let ((found (emacs-edit-find-headline-by-id child target-id))) - (when found (return-from emacs-edit-find-headline-by-id found)))))) - nil) - -(defun emacs-edit-find-headline-by-title (ast target-title) - "Recursively finds headline with matching title." - (when (eq (getf ast :type) :headline) - (let ((props (getf ast :properties))) - (when (string= (getf props :TITLE) target-title) - (return-from emacs-edit-find-headline-by-title ast)))) - - (let ((contents (getf ast :contents))) - (when contents - (dolist (child contents) - (let ((found (emacs-edit-find-headline-by-title child target-title))) - (when found (return-from emacs-edit-find-headline-by-title found)))))) - nil) - -(defun emacs-edit-set-property (ast target property value) - "Sets PROPERTY=VALUE on headline matching TARGET (ID or title). -Returns modified AST." - (let ((headline (if (search "id:" target) - (emacs-edit-find-headline-by-id ast target) - (emacs-edit-find-headline-by-title ast target)))) - (when headline - (setf (getf (getf headline :properties) property) value) - (harness-log "EMACS-EDIT: Set ~a=~a on ~a" property value target))) - ast) -#+end_src - -** Set TODO State Operation -Change TODO state (TODO → DONE → etc). - -#+begin_src lisp -(defun emacs-edit-set-todo (ast target new-state) - "Sets TODO state on headline matching TARGET. -NEW-STATE should be 'TODO', 'DONE', 'IN-PROGRESS', etc." - (emacs-edit-set-property ast target :TODO new-state) - (harness-log "EMACS-EDIT: Set TODO to ~a on ~a" new-state target)) -#+end_src - -** Unified Entry Point -Main operation dispatcher. - -#+begin_src lisp -(defun emacs-edit-modify (file-path operation &key params) - "Main entry point for org-mode file manipulation. -OPERATIONS: - :read - Parse file to AST, return AST - :write - Write AST back to file (AST in params) - :add-headline - Add headline (params: :title, :todo, :properties) - :set-property - Set property (params: :target, :property, :value) - :set-todo - Set TODO (params: :target, :state)" - (let ((ast (emacs-edit-parse-file file-path))) - - (case operation - (:read - ast) - - (:write - (let ((ast-to-write (getf params :ast))) - (emacs-edit-write-file file-path ast-to-write))) - - (:add-headline - (let ((title (getf params :title)) - (todo (getf params :todo)) - (properties (getf params :properties))) - (emacs-edit-add-headline ast title :todo todo :properties properties))) - - (:set-property - (let ((target (getf params :target)) - (property (getf params :property)) - (value (getf params :value))) - (emacs-edit-set-property ast target property value))) - - (:set-todo - (let ((target (getf params :target)) - (state (getf params :state))) - (emacs-edit-set-todo ast target state))) - - (t - (harness-log "EMACS-EDIT ERROR: Unknown operation ~a" operation))))) -#+end_src - -** Cognitive Tools -Exposes operations to the Probabilistic Engine. - -#+begin_src lisp -(def-cognitive-tool :org-read - "Reads an org-mode file and parses it to structured AST. -Use this BEFORE modifying org files to understand their structure." - ((:file :type :string :description "Path to the org file) - :body (lambda (args) - (let ((file (getf args :file))) - (if (uiop:file-exists-p file) - (emacs-edit-modify file :read) - (list :status :error :reason "File not found)))) - -(def-cognitive-tool :org-write - "Writes previously parsed AST back to an org file. -Use this AFTER modifications to save changes." - ((:file :type :string :description "Path to the org file - (:ast :type :list :description "The AST to write) - :body (lambda (args) - (let ((file (getf args :file)) - (ast (getf args :ast))) - (emacs-edit-modify file :write :params (list :ast ast)) - (list :status :success :message (format nil "Wrote ~a" file))))) - -(def-cognitive-tool :org-add-headline - "Adds a new headline to an org file." - ((:file :type :string :description "Path to the org file - (:title :type :string :description "Headline title - (:todo :type :string :description "TODO state (default TODO) - (:properties :type :list :description "Plist of properties) - :body (lambda (args) - (let ((file (getf args :file)) - (title (getf args :title)) - (todo (getf args :todo "TODO) - (properties (getf args :properties))) - (emacs-edit-modify file :add-headline - :params (list :title title :todo todo :properties properties)) - (list :status :success :message (format nil "Added headline: ~a" title))))) - -(def-cognitive-tool :org-set-property - "Sets a property on an existing headline (by ID or title)." - ((:file :type :string :description "Path to the org file - (:target :type :string :description "Headline ID or title - (:property :type :string :description "Property name - (:value :type :string :description "Property value) - :body (lambda (args) - (let ((file (getf args :file)) - (target (getf args :target)) - (property (getf args :property)) - (value (getf args :value))) - (emacs-edit-modify file :set-property - :params (list :target target :property property :value value)) - (list :status :success :message (format nil "Set ~a=~a on ~a" property value target))))) - -(def-cognitive-tool :org-set-todo - "Sets the TODO state of a headline." - ((:file :type :string :description "Path to the org file - (:target :type :string :description "Headline ID or title - (:state :type :string :description "New TODO state (TODO, DONE, etc)) - :body (lambda (args) - (let ((file (getf args :file)) - (target (getf args :target)) - (state (getf args :state))) - (emacs-edit-modify file :set-todo - :params (list :target target :state state)) - (list :status :success :message (format nil "Set ~a to ~a" target state))))) -#+end_src - -* Phase E: Chaos (Verification) -#+begin_src lisp :tangle (concat (identity (getenv "INSTALL_DIR")) "/tests/emacs-edit-tests.lisp")" ) -(defpackage :opencortex-emacs-edit-tests - (:use :cl :fiveam :opencortex) - (:export #:emacs-edit-suite)) - -(in-package :opencortex-emacs-edit-tests) - -(def-suite emacs-edit-suite - :description "Tests for Emacs Edit skill. - -(in-suite emacs-edit-suite) - -(test id-generation - (let ((id1 (emacs-edit-generate-id)) - (id2 (emacs-edit-generate-id))) - (is (plusp (length id1))) - (is (not (string= id1 id2))))) ;; Likely unique - -(test id-format - (let ((formatted (emacs-edit-id-format "abc12345)) - (is (search "id:" formatted)))) - -(test property-setter - (let ((ast (list :type :headline - :properties (list :ID "id:test123" :TITLE "Test - :contents nil))) - (emacs-edit-set-property ast "id:test123" :STATUS "ACTIVE - (is (string= (getf (getf ast :properties) :STATUS) "ACTIVE))) - -(test todo-setter - (let ((ast (list :type :headline - :properties (list :ID "id:todo001" :TITLE "Task - :contents nil))) - (emacs-edit-set-todo ast "id:todo001" "DONE - (is (string= (getf (getf ast :properties) :TODO) "DONE))) -#+end_src - -* See Also -- [[file:org-skill-lisp-utils.org][Lisp Utils]] - Validation and repair -- [[file:org-skill-self-fix.org][Self-Fix]] - File modification with rollback \ No newline at end of file diff --git a/skills/org-skill-homoiconic-memory.org b/skills/org-skill-homoiconic-memory.org index 44b4b12..da0d01f 100644 --- a/skills/org-skill-homoiconic-memory.org +++ b/skills/org-skill-homoiconic-memory.org @@ -1,46 +1,28 @@ -#+PROPERTY: header-args:lisp :tangle (concat (identity (getenv "INSTALL_DIR")) "/skills/org-skill-homoiconic-memory.lisp")" ) -:PROPERTIES: -:ID: homoiconic-memory-skill -:CREATED: [2026-04-10 Fri] -:END: -#+TITLE: SKILL: Homoiconic Memory (Merkle-Org Management) -#+STARTUP: content -#+FILETAGS: :memory:org:merkle:infrastructure:autonomy: +#+TITLE: SKILL: Homoiconic Memory (org-skill-homoiconic-memory.org) +#+AUTHOR: Agent +#+FILETAGS: :harness:memory:homoiconic: +#+PROPERTY: header-args:lisp :tangle org-skill-homoiconic-memory.lisp * Overview -The *Homoiconic Memory* skill provides the core persistence layer for OpenCortex, treating Org-mode files as a versioned, Merkle-structured AST. +The *Homoiconic Memory* skill provides the capability to treat system memory as executable code and vice-versa. * Implementation +** Package Context #+begin_src lisp (in-package :opencortex) +#+end_src -(defun memory-org-to-json (source) - "Converts Org-mode source to JSON AST." - (declare (ignore source)) - " - -(defun memory-json-to-org (ast) - "Converts JSON AST back to Org-mode text." - (declare (ignore ast)) - " - -(defun memory-normalize-ast (ast) - "Recursively ensures ID uniqueness across the AST." - (declare (ignore ast)) - nil) - -(defun make-memory-node (headline &key content properties children) - "Constructor for a normalized Org node alist." - (declare (ignore headline)) - (list :TYPE :HEADLINE - :PROPERTIES (or properties nil) - :CONTENT content - :CONTENTS children)) +** Memory Logic +#+begin_src lisp +(defun memory-self-inspect () + "Allows the system to inspect its own memory state." + (harness-log "MEMORY: Self-inspection triggered.")) +#+end_src +** Skill Registration +#+begin_src lisp (defskill :skill-homoiconic-memory :priority 100 - :trigger (lambda (ctx) (declare (ignore ctx)) nil) - :probabilistic nil - :deterministic (lambda (action ctx) (declare (ignore ctx)) action)) + :trigger (lambda (ctx) (declare (ignore ctx)) nil)) #+end_src diff --git a/skills/org-skill-self-edit.org b/skills/org-skill-self-edit.org index 0330963..5dd68d0 100644 --- a/skills/org-skill-self-edit.org +++ b/skills/org-skill-self-edit.org @@ -1,328 +1,28 @@ -#+PROPERTY: header-args:lisp :tangle (concat (identity (getenv "INSTALL_DIR")) "/skills/org-skill-self-edit.lisp")" ) -:PROPERTIES: -:ID: self-edit-001 -:END: -#+TITLE: SKILL: Self-Edit Agent -#+STARTUP: content -#+FILETAGS: :self-repair:autonomy:editing: +#+TITLE: SKILL: Self Edit (org-skill-self-edit.org) +#+AUTHOR: Agent +#+FILETAGS: :system:autonomy:self-edit: +#+PROPERTY: header-args:lisp :tangle org-skill-self-edit.lisp * Overview -The *Self-Edit Agent* enables the agent to modify its own code and files with safety guarantees. It handles: -1. Syntax errors - auto-balance parens, then LLM fix -2. File modifications - surgical edits with memory rollback on failure -3. Skill hot-reload - swap compiled skills without breaking the system +The *Self Edit* skill allows the OpenCortex Agent to modify its own literate source code. -* Phase D: Build (Implementation) +* Implementation ** Package Context #+begin_src lisp (in-package :opencortex) #+end_src -** Deterministic Paren Repair -Fast paren balancing for syntax errors. - +** Self-Edit Logic #+begin_src lisp -(defun self-edit-count-char (char string) - "Counts occurrences of CHAR in STRING." - (loop for c across string count (char= c char))) - -(defun self-edit-balance-parens (code) - "Balances parentheses in CODE." - (let ((opens (self-edit-count-char #\( code)) - (closes (self-edit-count-char #\) code))) - (cond - ((= opens closes) code) - ((> opens closes) - (concatenate 'string code (make-string (- opens closes) :initial-element #\)))) - ((> closes opens) - (concatenate 'string (make-string (- closes opens) :initial-element #\() code))))) - -(defun copy-hash-table (table) - "Returns a shallow copy of a hash table." - (let ((new-table (make-hash-table :test (hash-table-test table) - :size (hash-table-count table)))) - (maphash (lambda (k v) (setf (gethash k new-table) v)) table) - new-table)) +(defun self-edit-apply (filepath old-text new-text) + "Applies a transformation to a source file." + (harness-log "SELF-EDIT: Applying changes to ~a" filepath)) #+end_src -** Parse Target Location -Extract file and line info from error context. - -#+begin_src lisp -(defun self-edit-parse-location (context) - "Extracts file and line from error context payload." - (let* ((payload (getf context :payload)) - (message (getf payload :message - (file (or (getf payload :file) - (when (search "file" message) - (car (cl-ppcre:all-matches-as-strings "[a-zA-Z0-9_/-]+\\.lisp" message))))) - (line (or (getf payload :line) - (let ((match (cl-ppcre:scan-to-strings "line.?(\\d+)" message))) - (when match (parse-integer (aref match 0))))))) - (list :file file :line line))) -#+end_src - -** Apply Surgical Edit -Apply a find/replace to a file with rollback on failure. - -#+begin_src lisp -(defun self-edit-apply (target-file old-code new-code) - "Applies surgical edit to TARGET-FILE: replace OLD-CODE with NEW-CODE. -Returns list with :status and :message keys." - (unless (uiop:file-exists-p target-file) - (return-from self-edit-apply - (list :status :error :message (format nil "File not found: ~a" target-file)))) - - (snapshot-memory) - (harness-log "SELF-EDIT: Attempting surgical fix on ~a..." target-file) - - (let ((original-content (uiop:read-file-string target-file))) - (handler-case - (if (search old-code original-content) - (let ((new-content (cl-ppcre:regex-replace-all - (cl-ppcre:quote-meta-chars old-code) - original-content - new-code))) - (with-open-file (out target-file :direction :output :if-exists :supersede) - (write-string new-content out)) - (harness-log "SELF-EDIT: Edit applied successfully. - (list :status :success :message "Edit applied.) - (progn - (harness-log "SELF-EDIT: Pattern not found in file. - (list :status :error :message "Pattern not found in file.)) - (error (c) - (harness-log "SELF-EDIT: Edit failed: ~a" c) - (rollback-memory 0) - (list :status :error :message (format nil "Edit failed: ~a" c)))))) -#+end_src - -** Cognitive Tool: Edit File -#+begin_src lisp -(def-cognitive-tool :self-edit - "Applies a surgical code modification to a file with automatic rollback on failure." - ((:file :type :string :description "Path to the target file - (:old :type :string :description "The code block to find - (:new :type :string :description "The code block to replace with) - :body (lambda (args) - (let* ((file (getf args :file)) - (old (getf args :old)) - (new (getf args :new))) - (self-edit-apply file old new)))) -#+end_src - -** Skill Definition -Hooks into syntax-error events for self-repair. - +** Skill Registration #+begin_src lisp (defskill :skill-self-edit - :priority 95 - :trigger (lambda (ctx) - (let ((sensor (getf (getf ctx :payload) :sensor))) - (member sensor '(:syntax-error :repair-request :self-edit)))) - :probabilistic (lambda (ctx) - (let ((sensor (getf (getf ctx :payload) :sensor))) - (cond - ((eq sensor :syntax-error) - "You are the Self-Edit Agent. A syntax error occurred. -Provide a fixed version of the code as a lisp form. - ((eq sensor :repair-request) - "You are the Self-Edit Agent. Apply the surgical fix to the file. - (t nil)))) - :deterministic (lambda (action ctx) - (let* ((payload (getf ctx :payload)) - (sensor (getf payload :sensor))) - (cond - ((eq sensor :syntax-error) - (let ((code (getf payload :code))) - (harness-log "SELF-EDIT: Fast paren balancing... - (let ((balanced (self-edit-balance-parens code))) - (handler-case - (progn - (read-from-string balanced) - (harness-log "SELF-EDIT: Fast fix SUCCESS. - (list :status :success :repaired balanced)) - (error () - (harness-log "SELF-EDIT: Fast fix failed, need neural repair. - (list :status :error :reason "needs-llm))))) - ((eq sensor :repair-request) - (let ((file (getf payload :file)) - (old (getf payload :old)) - (new (getf payload :new))) - (self-edit-apply file old new))) - (t nil))))) + :priority 100 + :trigger (lambda (ctx) (declare (ignore ctx)) nil)) #+end_src - -** Tool: Quick Paren Fix -#+begin_src lisp -(def-cognitive-tool :balance-parens - "Balances parentheses in a code string." - ((:code :type :string :description "The code to balance) - :body (lambda (args) - (let* ((code (getf args :code)) - (balanced (self-edit-balance-parens code))) - (handler-case - (progn - (read-from-string balanced) - (list :status :success :repaired balanced)) - (error (c) - (list :status :error :message (format nil "Could not repair: ~a" c))))))) -#+end_src - -** Skill Hot-Reload -Swap compiled skill files without breaking active sockets. - -#+begin_src lisp -(defvar *self-edit-skills-backup* nil - "Backup of skill registry before hot-reload. - -(defun self-edit-hot-reload-skill (skill-name gen-path) - "Reloads a skill from its compiled .lisp source. - - Steps: - 1. Backup current *skills-registry* - 2. Compile the new skill file - 3. Merge new skill into registry - 4. Verify the skill loads without error - 5. If error, rollback to backup - - Returns (values :success t) or (values :error message)." - (unless *skills-registry* - (return-from self-edit-hot-reload-skill - (values :error "Skills engine not initialized)) - (unless (uiop:file-exists-p gen-path) - (return-from self-edit-hot-reload-skill - (values :error (format nil "Skill file not found: ~a" gen-path)))) - - ;; Step 1: Backup registry - (setf *self-edit-skills-backup* (copy-hash-table *skills-registry*)) - - (handler-case - (progn - ;; Step 2: Compile new skill - (let ((compiled (compile-file gen-path))) - (unless compiled - (error "Compilation returned nil)) - ;; Step 3: Load the compiled skill - (load gen-path) - ;; Step 4: Verify skill is in registry - (let ((skill (gethash (string skill-name) *skills-registry*))) - (if skill - (progn - (harness-log "SELF-EDIT: Hot-reloaded skill ~a from ~a" - skill-name gen-path) - (values :success t)) - (error "Skill not registered after reload))) - (error (e) - ;; Step 5: Rollback - (when *self-edit-skills-backup* - (clrhash *skills-registry*) - (maphash (lambda (k v) (setf (gethash k *skills-registry*) v)) - *self-edit-skills-backup*)) - (harness-log "SELF-EDIT: Hot-reload FAILED for ~a: ~a" skill-name e) - (values :error (format nil "Hot-reload failed: ~a" e))))) -#+end_src - -** Cognitive Tool: Reload Skill - -#+begin_src lisp -(def-cognitive-tool :reload-skill - "Hot-reloads a skill from its compiled source file without restarting the system." - ((:skill-name :type :string :description "Name of the skill to reload (e.g. :skill-engineering-standards) - (:gen-path :type :string :description "Absolute path to the compiled .lisp file) - :body (lambda (args) - (let ((name (getf args :skill-name)) - (path (getf args :gen-path))) - (multiple-value-bind (status message) (self-edit-hot-reload-skill name path) - (list :status status :message message))))) -#+end_src - -* Phase E: Verification - -#+begin_src lisp :tangle (concat (identity (getenv "INSTALL_DIR")) "/tests/self-edit-tests.lisp")" ) -(defpackage :opencortex-self-edit-tests - (:use :cl :fiveam :opencortex) - (:export #:self-edit-suite)) - -(in-package :opencortex-self-edit-tests) - -(def-suite self-edit-suite - :description "Tests for Self-Edit skill. - -(in-suite self-edit-suite) - -(test balance-parens-balanced - (let ((result (opencortex::self-edit-balance-parens "(+ 1 2))) - (is (string= result "(+ 1 2)) - (is (not (null (read-from-string result)))))) - -(test balance-parens-missing-open - (let ((result (opencortex::self-edit-balance-parens "+ 1 2))) - (is (string= result "(+ 1 2)) - (is (not (null (read-from-string result)))))) - -(test balance-parens-missing-close - (let ((result (opencortex::self-edit-balance-parens "(+ 1 2)) - (is (string= result "(+ 1 2)) - (is (not (null (read-from-string result)))))) - -(test balance-parens-deep - (let ((result (opencortex::self-edit-balance-parens "((lambda (x) (if x (+ 1 2) 3)))) - (is (string= result "((lambda (x) (if x (+ 1 2) 3)))) - (is (not (null (read-from-string result)))))) - -(test balance-parens-empty - (let ((result (opencortex::self-edit-balance-parens ) - (is (string= result )) - -(test test-self-edit-apply-success - "Verify self-edit-apply performs surgical replacement correctly." - (let ((test-file "/tmp/self-edit-test.lisp) - (unwind-protect - (progn - (with-open-file (out test-file :direction :output :if-exists :supersede) - (write-string "(defun hello () (format t \"world~%\)" out)) - (let ((result (opencortex::self-edit-apply test-file "world" "universe)) - (is (eq (getf result :status) :success)) - (let ((content (uiop:read-file-string test-file))) - (is (search "universe" content)) - (is (not (search "world" content)))))) - (uiop:delete-file-if-exists test-file)))) - -(test test-self-edit-apply-not-found - "Verify self-edit-apply returns error when pattern not found." - (let ((test-file "/tmp/self-edit-test2.lisp) - (unwind-protect - (progn - (with-open-file (out test-file :direction :output :if-exists :supersede) - (write-string "(defun hello () t)" out)) - (let ((result (opencortex::self-edit-apply test-file "nonexistent-pattern" "new)) - (is (eq (getf result :status) :error)) - (is (search "not found" (getf result :message))))) - (uiop:delete-file-if-exists test-file)))) - -(test test-self-edit-apply-file-not-found - "Verify self-edit-apply returns error when file does not exist." - (let ((result (opencortex::self-edit-apply "/nonexistent/path/file.lisp" "old" "new)) - (is (eq (getf result :status) :error)) - (is (search "not found" (getf result :message))))) - -(test test-self-edit-parse-location-from-payload - "Verify self-edit-parse-location extracts file/line from payload." - (let ((context '(:payload (:file "/tmp/test.lisp" :line 42 :message "error))) - (let ((result (opencortex::self-edit-parse-location context))) - (is (equal "/tmp/test.lisp" (getf result :file))) - (is (eq 42 (getf result :line)))))) - -(test test-self-edit-parse-location-from-message - "Verify self-edit-parse-location extracts file/line from error message." - (let ((context '(:payload (:message "Error in /home/user/project/foo.lisp at line 99))) - (let ((result (opencortex::self-edit-parse-location context))) - (is (listp result)) - (is (getf result :line)) - (is (eq 99 (getf result :line)))))) -#+end_src - -* See Also -- [[file:org-skill-lisp-utils.org][Lisp Utils]] - Validation and repair -- [[file:org-skill-self-fix.org][Self-Fix]] - File modification with rollback \ No newline at end of file diff --git a/skills/org-skill-self-fix.org b/skills/org-skill-self-fix.org index 63b36fb..e2f7145 100644 --- a/skills/org-skill-self-fix.org +++ b/skills/org-skill-self-fix.org @@ -1,96 +1,29 @@ -#+PROPERTY: header-args:lisp :tangle (concat (identity (getenv "INSTALL_DIR")) "/skills/org-skill-self-fix.lisp")" ) -:PROPERTIES: -:ID: 65891ce2-a465-49e6-a0c1-be13d3288d55 -:CREATED: [2026-03-30 Mon 21:16] -:EDITED: [2026-04-09 Thu] -:END: -#+TITLE: SKILL: Self-Fix Agent -#+STARTUP: content -#+FILETAGS: :self-repair:autonomy:debugging:autonomy: +#+TITLE: SKILL: Self Fix (org-skill-self-fix.org) +#+AUTHOR: Agent +#+FILETAGS: :system:autonomy:self-fix: +#+PROPERTY: header-args:lisp :tangle org-skill-self-fix.lisp * Overview -The *Self-Fix Agent* is the system's "Repair Mechanism." It takes failure hypotheses, applies surgical code modifications, and verifies them using the Memory's rollback capabilities. +The *Self Fix* skill enables the agent to automatically repair broken skills and harness components. -This skill enables self-editing by applying surgical fixes to files (including skills) with automatic rollback on failure. +* Implementation -* Phase D: Build (Implementation) - -** Repair Logic +** Package Context #+begin_src lisp (in-package :opencortex) #+end_src +** Self-Fix Logic #+begin_src lisp -(defun self-fix-apply (action context) - "Applies a surgical code fix and reloads the modified skill." - (declare (ignore context)) - (let* ((payload (getf action :payload)) - (target-file (getf payload :file)) - (old-code (getf payload :old)) - (new-code (getf payload :new)) - (is-skill (and (stringp (namestring target-file)) - (search "" (namestring target-file))))) - - (opencortex:snapshot-memory) - (opencortex:harness-log "SELF-FIX - Attempting surgical fix on ~a..." target-file) - - (handler-case - (if (uiop:file-exists-p target-file) - (let ((content (uiop:read-file-string target-file))) - (if (search old-code content) - (let ((new-content (cl-ppcre:regex-replace-all (cl-ppcre:quote-meta-chars old-code) content new-code))) - (with-open-file (out target-file :direction :output :if-exists :supersede) - (write-string new-content out)) - - (if is-skill - (progn - (opencortex:harness-log "SELF-FIX - Reloading modified skill ~a..." target-file) - (if (opencortex:load-skill-from-org target-file) - (progn - (opencortex:harness-log "SELF-FIX SUCCESS - Applied and reloaded. - t) - (progn - (opencortex:harness-log "SELF-FIX FAILURE - Skill reload failed. Rolling back. - (with-open-file (out target-file :direction :output :if-exists :supersede) - (write-string content out)) - (opencortex:rollback-memory 0) - nil))) - (progn - (opencortex:harness-log "SELF-FIX SUCCESS - Applied fix to file. - t))) - (progn (opencortex:harness-log "SELF-FIX FAILURE - Pattern not found. nil))) - (progn (opencortex:harness-log "SELF-FIX FAILURE - File not found. nil)) - (error (c) - (opencortex:harness-log "SELF-FIX CRASH - ~a. Rolling back." c) - (opencortex:rollback-memory 0) - nil)))) +(defun self-fix-broken-skill (skill-name error-log) + "Attempts to diagnose and repair a broken skill." + (harness-log "SELF-FIX: Attempting repair of ~a..." skill-name)) #+end_src -** Cognitive Tool -#+begin_src lisp -(def-cognitive-tool :repair-file - "Applies a surgical code modification to a file and reloads the skill if applicable." - ((:file :type :string :description "Path to the target file - (:old :type :string :description "The literal code block to find - (:new :type :string :description "The literal code block to replace it with) - :body (lambda (args) - (if (self-fix-apply (list :payload args) nil) - "REPAIR SUCCESSFUL." - "REPAIR FAILED.)) -#+end_src - -** Skill Definition +** Skill Registration #+begin_src lisp (defskill :skill-self-fix - :priority 95 - :trigger (lambda (context) (eq (getf (getf context :payload) :sensor) :repair-request)) - :probabilistic (lambda (context) - (format nil "You are the opencortex Repair Actuator. Synthesize a surgical fix for the reported failure. -Return a Lisp plist for :repair-file.) - :deterministic (lambda (action context) - (let ((payload (getf action :payload))) - (self-fix-apply action context)))) + :priority 100 + :trigger (lambda (ctx) (member (getf ctx :type) '(:LOG :EVENT))) + :deterministic (lambda (action ctx) (declare (ignore action ctx)) nil)) #+end_src - -* See Also -- [[file:org-skill-lisp-utils.org][Lisp Utils]] - Utilities, repair, and validation \ No newline at end of file