diff --git a/harness/skills.org b/harness/skills.org index 9f443f2..61e8cbf 100644 --- a/harness/skills.org +++ b/harness/skills.org @@ -400,18 +400,8 @@ EXAMPLES: #+begin_src lisp ** Cognitive Tool Registration #+begin_src lisp :tangle (expand-file-name "skills.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/harness")) -(defvar *cognitive-tools* nil "Registry of available Agent capabilities.") - -(defmacro def-cognitive-tool (name arg-list &body body) - "Registers a new cognitive tool (capability) into the global registry." - `(setf (getf *cognitive-tools* ,name) - (list :args ',arg-list - :executor (lambda (args) - (let ,(mapcar (lambda (arg) - (let ((arg-name (if (listp arg) (first arg) arg))) - `(,arg-name (getf args ,(intern (string arg-name) :keyword))))) - arg-list) - ,@body))))) +;; Cognitive tools are registered in *cognitive-tools* (defined in package.lisp) +;; using the def-cognitive-tool macro. #+end_src (def-cognitive-tool :eval "Evaluates raw Common Lisp code in the harness image. Use this for complex calculations or internal state inspection." diff --git a/opencortex.asd b/opencortex.asd index d2fafa5..5f8370f 100644 --- a/opencortex.asd +++ b/opencortex.asd @@ -18,23 +18,7 @@ (:file "harness/perceive") (:file "harness/reason") (:file "harness/act") - (:file "harness/loop") - - (:file "skills/org-skill-policy") - (:file "skills/org-skill-bouncer") - (:file "skills/org-skill-scribe") - (:file "skills/org-skill-gardener") - (:file "skills/org-skill-lisp-utils") - (:file "skills/org-skill-literate-programming") - (:file "skills/org-skill-engineering-standards") - (:file "skills/org-skill-self-edit") - (:file "skills/org-skill-emacs-edit") - (:file "skills/org-skill-tool-permissions") - (:file "skills/org-skill-self-fix") - (:file "skills/org-skill-peripheral-vision") - (:file "skills/org-skill-gateway-manager") - (:file "skills/org-skill-diagnostics") - (:file "skills/org-skill-config-manager")) + (:file "harness/loop")) :build-operation "program-op" :build-pathname "opencortex-server" diff --git a/opencortex.sh b/opencortex.sh index 7d83b44..33c9499 100755 --- a/opencortex.sh +++ b/opencortex.sh @@ -99,6 +99,7 @@ case "$COMMAND" in --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \ --eval "(push (truename \"$OC_DATA_DIR/\") asdf:*central-registry*)" \ --eval '(ql:quickload :opencortex)' \ + --eval '(opencortex:initialize-all-skills)' \ --eval '(opencortex:doctor-main)' ;; diff --git a/skills/org-skill-cli-gateway.org b/skills/org-skill-cli-gateway.org index 9f82723..b496384 100644 --- a/skills/org-skill-cli-gateway.org +++ b/skills/org-skill-cli-gateway.org @@ -13,6 +13,7 @@ The *CLI Gateway* is the primary sensory and actuating interface for human inter * Implementation #+begin_src lisp +(in-package :opencortex) (defvar *cli-port* 9105) (defvar *cli-server-socket* nil) diff --git a/skills/org-skill-config-manager.org b/skills/org-skill-config-manager.org index f77798c..056498c 100644 --- a/skills/org-skill-config-manager.org +++ b/skills/org-skill-config-manager.org @@ -75,15 +75,38 @@ Secrets are appended to `~/.config/opencortex/.env`, while structural metadata i ** Registry Persistence #+begin_src lisp :tangle (expand-file-name "org-skill-config-manager.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) (defvar *providers* nil "Global registry of configured LLM providers.") -#+end_src -#+begin_src lisp :tangle (expand-file-name "org-skill-config-manager.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) +(defun get-oc-config-dir () + "Returns the XDG-compliant config directory for OpenCortex." + (let ((env (uiop:getenv "OC_CONFIG_DIR"))) + (if (and env (> (length env) 0)) + (uiop:ensure-directory-pathname env) + (uiop:merge-pathnames* ".config/opencortex/" (user-homedir-pathname))))) + (defun save-providers () "Persist provider configuration to XDG config directory." (let ((path (merge-pathnames "providers.lisp" (get-oc-config-dir)))) (ensure-directories-exist path) (with-open-file (s path :direction :output :if-exists :supersede) (format s ";;; OpenCortex Provider Metadata~%~s~%" *providers*)))) + +(defun prompt-for (label &optional default) + "Prompts the user for input on the CLI." + (format t "~a~@[ [~a]~]: " label default) + (finish-output) + (let ((input (read-line))) + (if (string= input "") + (or default "") + input))) + +(defun save-secret (provider field val) + "Appends a secret to the XDG .env file." + (let ((env-file (merge-pathnames ".env" (get-oc-config-dir))) + (var-name (format nil "~:@(~a_~a~)" provider field))) + (ensure-directories-exist env-file) + (with-open-file (out env-file :direction :output :if-exists :append :if-does-not-exist :create) + (format out "~a=~a~%" var-name val)) + (setf (uiop:getenv var-name) val))) #+end_src ** Registry API diff --git a/skills/org-skill-emacs-edit.org b/skills/org-skill-emacs-edit.org index 76178ad..ecb2d0b 100644 --- a/skills/org-skill-emacs-edit.org +++ b/skills/org-skill-emacs-edit.org @@ -89,10 +89,10 @@ Converts AST back to org format, preserving structure. (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))) + (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)) "") @@ -200,17 +200,17 @@ Add a new headline to an existing AST. (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))) + (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)) diff --git a/skills/org-skill-homoiconic-memory.org b/skills/org-skill-homoiconic-memory.org index 6ce5f1e..c6b1734 100644 --- a/skills/org-skill-homoiconic-memory.org +++ b/skills/org-skill-homoiconic-memory.org @@ -13,6 +13,7 @@ The *Homoiconic Memory* skill provides the core persistence layer for OpenCortex * Implementation #+begin_src lisp +(in-package :opencortex) (defun memory-org-to-json (source) "Converts Org-mode source to JSON AST." diff --git a/skills/org-skill-llm-gateway.org b/skills/org-skill-llm-gateway.org index aaeb4a2..770f716 100644 --- a/skills/org-skill-llm-gateway.org +++ b/skills/org-skill-llm-gateway.org @@ -49,23 +49,33 @@ The *LLM Gateway* skill provides a unified interface for interacting with multip ** Cognitive Tools #+begin_src lisp :tangle (expand-file-name "org-skill-llm-gateway.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) -(def-cognitive-tool :get-ollama-embedding (text) - (let* ((host (or (uiop:getenv "OLLAMA_HOST") "localhost:11434")) - (url (format nil "http://~a/api/embeddings" host)) - (body (cl-json:encode-json-to-string `((model . "nomic-embed-text") (prompt . ,text))))) - (handler-case - (let* ((response (dex:post url :headers '(("Content-Type" . "application/json")) :content body)) - (json (cl-json:decode-json-from-string response))) - (cdr (assoc :embedding json))) - (error (c) (harness-log "OLLAMA EMBED ERROR: ~a" c) nil)))) +(def-cognitive-tool :get-ollama-embedding + "Generates vector embeddings via Ollama API." + ((:text :type :string :description "Text to embed.")) + :body (lambda (args) + (let ((text (getf args :text))) + (let* ((host (or (uiop:getenv "OLLAMA_HOST") "localhost:11434")) + (url (format nil "http://~a/api/embeddings" host)) + (body (cl-json:encode-json-to-string `((model . "nomic-embed-text") (prompt . ,text))))) + (handler-case + (let* ((response (dex:post url :headers '(("Content-Type" . "application/json")) :content body)) + (json (cl-json:decode-json-from-string response))) + (cdr (assoc :embedding json))) + (error (c) (harness-log "OLLAMA EMBED ERROR: ~a" c) nil)))))) #+end_src #+begin_src lisp :tangle (expand-file-name "org-skill-llm-gateway.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) -(def-cognitive-tool :ask-llm (prompt system-prompt provider model) - (execute-llm-request :prompt prompt - :system-prompt system-prompt - :provider provider - :model model)) +(def-cognitive-tool :ask-llm + "Unified interface for interacting with LLM providers." + ((:prompt :type :string :description "The user prompt") + (:system-prompt :type :string :description "The system prompt (optional)") + (:provider :type :keyword :description "The provider (e.g., :ollama, :openai)") + (:model :type :string :description "The model name")) + :body (lambda (args) + (execute-llm-request :prompt (getf args :prompt) + :system-prompt (getf args :system-prompt) + :provider (getf args :provider) + :model (getf args :model)))) #+end_src ** Skill Registration diff --git a/skills/org-skill-self-edit.org b/skills/org-skill-self-edit.org index 18de3ae..f8cafec 100644 --- a/skills/org-skill-self-edit.org +++ b/skills/org-skill-self-edit.org @@ -37,6 +37,13 @@ Fast paren balancing for syntax errors. (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)) #+end_src ** Parse Target Location @@ -152,8 +159,8 @@ Provide a fixed version of the code as a lisp form.") "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))) + (let* ((code (getf args :code)) + (balanced (self-edit-balance-parens code))) (handler-case (progn (read-from-string balanced) diff --git a/skills/org-skill-shell-actuator.org b/skills/org-skill-shell-actuator.org index bbc6553..4cc95b2 100644 --- a/skills/org-skill-shell-actuator.org +++ b/skills/org-skill-shell-actuator.org @@ -12,7 +12,8 @@ The *Shell Actuator* provides a controlled interface for the OpenCortex to execu * Implementation -#+begin_src lisp +#+begin_src lisp :tangle (expand-file-name "org-skill-shell-actuator.lisp" (concat (or (getenv "INSTALL_DIR") ".") "/skills")) +(in-package :opencortex) (defparameter *allowed-commands* '("ls" "git" "rg" "grep" "date" "echo" "cat" "node" "python3" "sbcl"))