chore: consolidate standalone skill projects into org-agent and org-agent-contrib

This commit is contained in:
2026-04-09 16:10:30 -04:00
parent e1051d1cea
commit 7624cf37e6
1003 changed files with 1 additions and 198439 deletions

View File

@@ -1,28 +0,0 @@
#+title: Org-GTD Archive Roam Daily Project
#+author: Amero Garcia
#+created: [2026-03-16 Mon 14:05]
#+begin_comment
Project documentation for Org-GTD Archive Roam Daily Project
#+end_comment
* Org-GTD Archive Roam Daily Project
*Goal:** To develop a feature for `org-gtd` that enables archiving of Org-mode headings to `org-roam-dailies` based on the `:CREATED:` property of the heading being archived. This will ensure chronological and contextually relevant archiving.
*Initial Scope:**
- Understanding the current `org-gtd` archiving mechanisms.
- Investigating `org-roam-dailies` structure and API for programmatic interaction.
- Designing a function that extracts the `:CREATED:` property from an Org heading.
- Implementing logic to move/copy the heading content to the correct daily note based on its creation date.
- Considering how to handle headings without a `:CREATED:` property.
*Information Needed from Amr:**
- Specific `org-gtd` setup details (e.g., how archiving is currently triggered/configured).
- Desired behavior if a `:CREATED:` property is missing.
- Preferred method for integration (e.g., new command, modification of existing archiving function).
- Any existing thoughts on error handling or edge cases.
*Next Steps:**
1. Gather Amr's specific usage patterns and requirements.
2. Dive into `org-gtd` and `org-roam` documentation/source code.
3. Propose a design for the new archiving function.

View File

@@ -1,74 +0,0 @@
;;; org-gtd-archive-roam-daily.el --- Archive Org headings to Org-roam dailies
;;; Commentary:
;; This file provides an Elisp function to archive an Org-mode heading
;; at point to an Org-roam daily file based on its :CREATED: property.
;;; Code:
(require 'org-roam-dailies)
(require 'org-element)
(require 'org-time)
(defun amero-get-org-heading-created-property ()
"Extract the :CREATED: property from the current Org heading.
Returns a time string or nil if not found."
(interactive)
(save-excursion
(org-back-to-heading t)
(org-entry-get (point) "CREATED")))
(defun amero-parse-created-timestamp (timestamp-string)
"Parse an Org-mode timestamp string like '[2026-03-16 Mon 14:05]'
into an Emacs internal time object.
Returns nil if parsing fails."
(ignore-errors
(org-time-string-to-time timestamp-string)))
(defun amero-get-daily-note-file (time-object)
"Get the Org-roam daily note file for a given Emacs TIME-OBJECT.
Creates the file if it doesn't exist. Returns the file path."
(let* ((date-string (format-time-string org-roam-dailies-capture-templates-date-format time-object))
(file-path (expand-file-name (concat date-string ".org")
(expand-file-name org-roam-dailies-directory org-roam-directory))))
;; Ensure the directory exists
(unless (file-exists-p (file-name-directory file-path))
(make-directory (file-name-directory file-path) t))
;; Create file if it doesn't exist (org-roam-dailies-goto-date handles this,
;; but we need to ensure it's created and accessible for append)
(unless (file-exists-p file-path)
(with-temp-buffer
(insert (format "#+title: %s\n" date-string))
(write-file file-path)))
file-path))
(defun org-gtd-archive-roam-daily ()
"Archive the current Org heading to an Org-roam daily file
based on its :CREATED: property.
Signals an error if :CREATED: property is missing."
(interactive)
(unless (org-before-first-heading-p (point))
(user-error "Point is not on an Org heading or within an Org file."))
(let* ((created-timestamp-string (amero-get-org-heading-created-property))
(created-time-object (and created-timestamp-string
(amero-parse-created-timestamp created-timestamp-string)))
(heading-start (save-excursion (org-back-to-heading t) (point)))
(heading-end (save-excursion (org-end-of-subtree t) (point)))
(heading-content (buffer-substring-no-properties heading-start heading-end))
daily-file-path)
(unless created-time-object
(user-error "No date error: Heading is missing a valid :CREATED: property."))
(setq daily-file-path (amero-get-daily-note-file created-time-object))
(with-current-buffer (find-file-noselect daily-file-path)
(goto-char (point-max))
(insert "\n\n" heading-content)
(save-buffer))
;; Remove the original heading
(delete-region heading-start heading-end)
(message "Archived heading to %s" daily-file-path)))
(provide 'org-gtd-archive-roam-daily)
;;; org-gtd-archive-roam-daily.el ends here

View File

@@ -1,74 +0,0 @@
---
name: org-json-bridge
description: "Provides a bridge between Org-mode files and JSON for programmatic manipulation. Use when: needing to parse Org-mode to JSON, serialize JSON to Org-mode, or modify Org-mode files programmatically. NOT for: simple text edits, general Org-mode viewing, or direct human authoring."
homepage: https://docs.openclaw.ai/tools/skills/org-json-bridge
metadata: { "openclaw": { "emoji": "🌉", "requires": { "bins": ["python3", "pip", "emacs"], "env": [] }, "primaryEnv": "" } }
---
# Org-JSON Bridge
This skill develops and utilizes an external tool to convert Org-mode files into a structured JSON representation, and vice-versa. This enables robust programmatic modification of Org-mode documents, addressing the limitations of direct string manipulation via the `edit` tool for complex structures like tables or source blocks. By working with a structured data model, it ensures consistent formatting and reliable updates.
## When to Use
**USE this skill when:**
- Modifying Org-mode file content programmatically (e.g., adding/removing table rows, updating flags in structured blocks).
- Converting Org-mode documents to a JSON representation for data processing.
- Converting structured JSON data back into Org-mode format.
- Encountering difficulties with the `edit` tool due to complex Org-mode formatting (e.g., tables, source blocks).
**DON'T use this skill when:**
- Performing simple, single-line text edits in Org-mode files.
- Viewing or rendering Org-mode content (use a dedicated Org-mode client).
- Authoring Org-mode documents directly.
- Editing existing skills (use `edit` tool directly for SKILL.md files).
## Instructions
This skill provides a Python script (`org_bridge.py`) that acts as a command-line interface to the Org-mode to JSON bridge.
1. **Parse Org-mode to JSON (Command Line):**
```bash
org_bridge.py parse --file-path "path/to/my-doc.org" > output.json
```
2. **Modify JSON (Internal Agent Logic):**
* The agent would then perform internal Python logic to load `output.json`, modify the JSON object (AST), and save the modified JSON to a new file, e.g., `modified_data.json`.
3. **Render JSON to Org-mode (Command Line):**
```bash
org_bridge.py render --json-input-file "path/to/modified_data.json" --output-file "path/to/new-doc.org"
```
## Commands
### `org_bridge.py parse`
Parse an Org-mode file into a JSON representation and print to stdout.
```bash
# Example: Parse an Org-mode file to JSON
exec ~/.openclaw/workspace/skills/org-json-bridge/org_bridge.py parse --file-path "/home/amr/.openclaw/workspace/memex/5_projects/agora/agora-requirements-01-overview.org"
```
### `org_bridge.py render`
Render a JSON representation back into an Org-mode file.
```bash
# Example: Render JSON back to an Org-mode file
# Assume 'modified_data.json' exists with the desired AST
exec ~/.openclaw/workspace/skills/org-json-bridge/org_bridge.py render --json-input-file "/path/to/modified_data.json" --output-file "/home/amr/.openclaw/workspace/memex/5_projects/agora/agora-requirements-01-overview.org"
```
## Configuration
Environment variables needed:
- None directly for the bridge, but the underlying parser might have config.
Config values in `openclaw.json`:
- `skill.org-json-bridge.parser_path` - Path to the Python/Node.js script (if not directly in skill folder).
## Notes
- Initial implementation will focus on core Org-mode elements needed for requirements documents (headings, lists, tables, source blocks).
- Requires installation of an Org-mode parsing library (e.g., `orgparse` for Python), though the Emacs Lisp handles the heavy lifting here.
- This skill is a foundational piece to enhance reliable document manipulation capabilities.

View File

@@ -1,60 +0,0 @@
;;; org-json-bridge.el --- Bridge for LLM agents to manipulate Org-mode via JSON
(require 'org-element)
(require 'json)
(require 'cl-lib)
(defun org-json-bridge--clean-tree (element)
"Recursively convert an Org ELEMENT into a JSON-serializable format."
(cond
((listp element)
(let* ((type (car element))
(props (nth 1 element))
(children (nthcdr 2 element))
(cleaned-props nil))
(cl-loop for (key val) on props by 'cddr do
(unless (member key '(:standard-properties :parent))
(let ((json-key (substring (symbol-name key) 1)))
(push (cons json-key
(cond
((stringp val) val)
((numberp val) val)
((booleanp val) val)
(t (format "%s" val))))
cleaned-props))))
(list (cons 'type (symbol-name type))
(cons 'properties cleaned-props)
(cons 'contents (mapcar #'org-json-bridge--clean-tree children)))))
((stringp element) element)
(t (format "%s" element))))
(defun org-to-json (file-path)
"Parse an Org file and output its structure as JSON."
(with-current-buffer (find-file-noselect file-path)
(let* ((tree (org-element-parse-buffer))
(cleaned (org-json-bridge--clean-tree tree)))
(princ (json-encode cleaned)))))
(defun json-to-org (json-string output-file)
"Take a JSON representation of an Org tree and write it back to a file."
(let ((data (json-read-from-string json-string)))
(with-temp-file output-file
(insert (org-element-interpret-data data)))))
;; Entry point for batch mode
(message "DEBUG: Entry point reached")
;; Sometimes -- is left in command-line-args-left
(when (string= (car command-line-args-left) "--")
(pop command-line-args-left))
(let ((command (pop command-line-args-left)))
(message "DEBUG: Command is %s" command)
(cond
((string= command "org-to-json")
(let ((file (pop command-line-args-left)))
(org-to-json file)))
((string= command "json-to-org")
(let ((json-str (pop command-line-args-left))
(out-file (pop command-line-args-left)))
(json-to-org json-str out-file)))))

View File

@@ -1,54 +0,0 @@
import subprocess
import json
import os
import argparse
from typing import Dict, Any, Optional
class OrgBridge:
def __init__(self, lisp_script_path: str = os.path.join(os.path.dirname(__file__), "org-json-bridge.el")):
self.lisp_path = os.path.abspath(lisp_script_path)
def _run_emacs_batch(self, command: str, *args) -> str:
"""Helper to execute the Emacs batch command with arguments."""
cmd = [
"emacs", "--batch",
"-l", self.lisp_path,
"--", command, *args
]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
return result.stdout.strip()
def parse_to_dict(self, file_path: str) -> Dict[str, Any]:
"""Reads an Org file and returns its AST as a Python Dictionary."""
abs_path = os.path.abspath(file_path)
json_output = self._run_emacs_batch("org-to-json", abs_path)
return json.loads(json_output)
def write_from_dict(self, ast_dict: Dict[str, Any], output_path: str):
"""Takes a Python Dictionary (AST) and writes it back to an Org file."""
json_input = json.dumps(ast_dict)
abs_output_path = os.path.abspath(output_path)
self._run_emacs_batch("json-to-org", json_input, abs_output_path)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Org-mode to JSON bridge for programmatic manipulation.")
parser.add_argument("action", choices=["parse", "render"], help="Action to perform: 'parse' an Org file to JSON, or 'render' JSON to an Org file.")
parser.add_argument("--file-path", help="Path to the Org-mode file (required for 'parse' action).")
parser.add_argument("--json-input-file", help="Path to a JSON file containing the AST (required for 'render' action).")
parser.add_argument("--output-file", help="Path to output the Org-mode file (required for 'render' action).")
args = parser.parse_args()
bridge = OrgBridge()
if args.action == "parse":
if not args.file_path:
parser.error("--file-path is required for the 'parse' action.")
org_ast = bridge.parse_to_dict(args.file_path)
print(json.dumps(org_ast, indent=2))
elif args.action == "render":
if not args.json_input_file or not args.output_file:
parser.error("--json-input-file and --output-file are required for the 'render' action.")
with open(args.json_input_file, 'r') as f:
ast_dict = json.load(f)
bridge.write_from_dict(ast_dict, args.output_file)

View File

@@ -1,28 +0,0 @@
;;;; identity-logic.lisp --- Core identity and persona logic.
;;;; This file is TANGLED from notes/agent-identity.org. DO NOT EDIT MANUALLY.
(defpackage :org-skill-agent-identity
(:use :cl :uiop)
(:export #:get-agent-name
#:get-agent-persona
#:trigger-skill-agent-identity
#:neuro-skill-agent-identity))
(in-package :org-skill-agent-identity)
(defun get-agent-name ()
"Return the current name of the agent. Defaults to 'Agent'."
(or (uiop:getenv "MEMEX_ASSISTANT") "Agent"))
(defun get-agent-persona ()
"Return the behavioral instructions for the agent."
"You are a proactive Neurosymbolic Lisp Machine. Your goal is to assist the user with GTD, memory, and automation. You are concise, precise, and favor deterministic Lisp solutions over fuzzy neural guesses.")
(defun trigger-skill-agent-identity (context)
(let* ((payload (getf context :payload))
(text (or (getf payload :text) "")))
(or (search "who are you" text :test #'string-equal)
(search "identify yourself" text :test #'string-equal))))
(defun neuro-skill-agent-identity (context)
(format nil "The user asked about your identity. Explain who you are using this persona - ~a" (get-agent-persona)))

View File

@@ -1,31 +0,0 @@
import os
def simulate_get_name():
return os.getenv("MEMEX_ASSISTANT", "Agent")
def simulate_trigger(text):
keywords = ["who are you", "identify yourself"]
return any(k in text.lower() for k in keywords)
if __name__ == "__main__":
print("--- Test: Identity Retrieval ---")
os.environ["MEMEX_ASSISTANT"] = "FoundryBot"
name = simulate_get_name()
print(f"Name (Env set): {name}")
status1 = "PASS" if name == "FoundryBot" else "FAIL"
del os.environ["MEMEX_ASSISTANT"]
name = simulate_get_name()
print(f"Name (Env unset): {name}")
status2 = "PASS" if name == "Agent" else "FAIL"
print(f"\n--- Test: Identity Trigger ---")
t1 = simulate_trigger("Who are you?")
t2 = simulate_trigger("Identify yourself now.")
t3 = simulate_trigger("Hello there.")
print(f"Trigger 'Who are you?': {t1}")
print(f"Trigger 'Identify yourself': {t2}")
print(f"Trigger 'Hello': {t3}")
status3 = "PASS" if t1 and t2 and not t3 else "FAIL"
print(f"\nFinal Status: {'PASS' if all(s == 'PASS' for s in [status1, status2, status3]) else 'FAIL'}")

View File

@@ -1,31 +0,0 @@
;;; TDD Suite: org-skill-agent-identity
;;; Status: RED
;;; Author: Tech-Analyst-Agent
;;; Created: [2026-03-31 Tue 14:50]
(defpackage :org-skill-agent-identity-tests
(:use :cl :fiveam :org-skill-agent-identity))
(in-package :org-skill-agent-identity-tests)
(def-suite identity-suite
:description "Tests for agent identity and persona retrieval.")
(in-suite identity-suite)
(test get-name-from-env
"Ensure the agent name is correctly pulled from MEMEX_ASSISTANT."
(uiop:setenv "MEMEX_ASSISTANT" "TestAgent")
(is (equal "TestAgent" (get-agent-name)))
(uiop:setenv "MEMEX_ASSISTANT" nil))
(test get-default-name
"Ensure the agent name defaults to 'Agent' when env is empty."
(uiop:setenv "MEMEX_ASSISTANT" nil)
(is (equal "Agent" (get-agent-name))))
(test identity-trigger
"Ensure the skill triggers on identity keywords."
(is (trigger-skill-agent-identity '(:payload (:text "who are you"))))
(is (trigger-skill-agent-identity '(:payload (:text "identify yourself"))))
(is (not (trigger-skill-agent-identity '(:payload (:text "hello"))))))

View File

@@ -1,72 +0,0 @@
(defun architect-perceive-frozen-prd (note-path)
"Checks if a master note has a FROZEN PRD and lacks a Phase B section."
(let ((content (uiop:read-file-string note-path)))
(when (and (search "* Phase A: Demand (PRD)" content)
(search ":STATUS: FROZEN" content)
(not (search "* Phase B: Blueprint (PROTOCOL)" content)))
`(:note-path ,note-path :content ,content))))
(defun architect-scan-all-notes ()
"Scans all org-skill-*.org notes for demands ready for blueprinting.
Uses manual filtering to ensure robustness across Lisp environments."
(let* ((notes-dir (or (uiop:getenv "MEMEX_NOTES") "/home/user/memex/notes/"))
(files (uiop:directory-files (uiop:ensure-directory-pathname notes-dir)))
(ready-notes '()))
(dolist (file files)
(let ((name (pathname-name file))
(type (pathname-type file)))
(when (and name type
(uiop:string-prefix-p "org-skill-" name)
(string-equal type "org"))
(let ((status (architect-perceive-frozen-prd file)))
(when status (push status ready-notes))))))
ready-notes))
(defun trigger-skill-architect (context)
"Triggers on heartbeat if any master note is in a FROZEN PRD state."
(let ((type (getf context :type))
(payload (getf context :payload)))
(when (and (eq type :EVENT) (eq (getf payload :sensor) :heartbeat))
(let ((ready (architect-scan-all-notes)))
(when ready
(setf (getf (getf context :payload) :ready-notes) ready)
t)))))
(defun neuro-skill-architect (context)
(let* ((payload (getf context :payload))
(note (car (getf payload :ready-notes)))
(note-path (getf note :note-path))
(prd-content (getf note :content))
(path-str (namestring note-path)))
(format nil "
You are the PSF Architect.
The Master Note '~a' has a FROZEN PRD and needs a PROTOCOL.
NOTE CONTENT:
---
~a
---
TASK:
Draft the '* Phase B: Blueprint (PROTOCOL)' section.
1. Define Architectural Intent.
2. Define Semantic Interfaces using Lisp signatures.
Return a Lisp plist: (:target :architect :action :actuate :path \"~a\" :content \"...blueprint section...\")
" path-str prd-content path-str)))
(defun architect-actuate (action context)
(declare (ignore context))
(let* ((payload (getf action :payload))
(note-path (or (getf payload :path) (getf action :path)))
(blueprint-content (or (getf payload :content) (getf action :content))))
(if (and note-path blueprint-content)
(progn
(org-agent:kernel-log "ARCHITECT - Appending PROTOCOL to ~a" note-path)
(with-open-file (out note-path :direction :output :if-exists :append)
(format out "~%* Phase B: Blueprint (PROTOCOL)~%:PROPERTIES:~%:STATUS: SIGNED~%:END:~%~%~a"
blueprint-content))
(format nil "SUCCESS - Architect established PROTOCOL in ~a" note-path))
(progn
(org-agent:kernel-log "ARCHITECT FAILURE - Missing path or content in action: ~a" action)
nil))))

View File

@@ -1,47 +0,0 @@
import os
import shutil
def simulate_perceive(project_name, projects_dir):
prd_path = os.path.join(projects_dir, project_name, "PRD.org")
protocol_path = os.path.join(projects_dir, project_name, "PROTOCOL.org")
if not os.path.exists(prd_path):
return None
with open(prd_path, 'r') as f:
content = f.read()
if "#+STATUS: FROZEN" in content and not os.path.exists(protocol_path):
return {"project": project_name, "prd_path": prd_path, "content": content}
return None
if __name__ == "__main__":
test_dir = "/tmp/architect_test_projects"
if os.path.exists(test_dir):
shutil.rmtree(test_dir)
os.makedirs(os.path.join(test_dir, "test-project"))
prd_file = os.path.join(test_dir, "test-project", "PRD.org")
print("--- Test 1: Draft PRD ---")
with open(prd_file, "w") as f:
f.write("#+TITLE: Test\n#+STATUS: DRAFT\n")
res = simulate_perceive("test-project", test_dir)
print(f"Result: {res}")
status1 = "PASS" if res is None else "FAIL"
print("\n--- Test 2: Frozen PRD ---")
with open(prd_file, "w") as f:
f.write("#+TITLE: Test\n#+STATUS: FROZEN\n")
res = simulate_perceive("test-project", test_dir)
print(f"Result: {res['project'] if res else None}")
status2 = "PASS" if res and res['project'] == "test-project" else "FAIL"
print("\n--- Test 3: Protocol already exists ---")
with open(os.path.join(test_dir, "test-project", "PROTOCOL.org"), "w") as f:
f.write("exists")
res = simulate_perceive("test-project", test_dir)
print(f"Result: {res}")
status3 = "PASS" if res is None else "FAIL"
print(f"\nFinal Status: {'PASS' if all(s == 'PASS' for s in [status1, status2, status3]) else 'FAIL'}")

View File

@@ -1,7 +0,0 @@
(defun auth-api-key-get-credentials ()
(let ((key (uiop:getenv "LLM_API_KEY")))
(when key
(list :api-key key))))
;; Register as the default auth provider for Gemini during transition
(org-agent:register-auth-provider :gemini #'auth-api-key-get-credentials)

View File

@@ -1,78 +0,0 @@
(defvar *google-token-state* nil)
(defun auth-google-load-state ()
(let ((state-file (merge-pathnames "state/auth-google.lisp" (uiop:getenv "SYSTEM_DIR"))))
(if (uiop:file-exists-p state-file)
(setf *google-token-state* (with-open-file (in state-file) (read in)))
(setf *google-token-state* nil))))
(defun auth-google-save-state ()
(let* ((state-dir (uiop:getenv "SYSTEM_DIR"))
(state-file (merge-pathnames "state/auth-google.lisp" state-dir)))
(ensure-directories-exist state-file)
(with-open-file (out state-file :direction :output :if-exists :supersede)
(print *google-token-state* out))))
(defun auth-google-receive-code (code)
"Exchanges the manual authorization code for access and refresh tokens."
(let ((url "https://oauth2.googleapis.com/token")
(content `(("code" . ,code)
("client_id" . ,(uiop:getenv "GOOGLE_CLIENT_ID"))
("client_secret" . ,(uiop:getenv "GOOGLE_CLIENT_SECRET"))
("redirect_uri" . "urn:ietf:wg:oauth:2.0:oob")
("grant_type" . "authorization_code"))))
(handler-case
(let* ((response (dex:post url :content content))
(json (cl-json:decode-json-from-string response)))
(setf *google-token-state*
`(:access-token ,(cdr (assoc :access--token json))
:refresh-token ,(cdr (assoc :refresh--token json))
:expires-at ,(+ (get-universal-time) (cdr (assoc :expires--in json)))))
(auth-google-save-state)
(kernel-log "OAUTH - Google handshake successful.")
t)
(error (c)
(kernel-log "OAUTH ERROR - Handshake failed: ~a" c)
nil))))
(defun auth-google-refresh-token ()
"Uses the refresh_token to acquire a new access_token."
(let ((refresh-token (getf *google-token-state* :refresh-token))
(url "https://oauth2.googleapis.com/token")
(content `(("refresh_token" . ,(getf *google-token-state* :refresh-token))
("client_id" . ,(uiop:getenv "GOOGLE_CLIENT_ID"))
("client_secret" . ,(uiop:getenv "GOOGLE_CLIENT_SECRET"))
("grant_type" . "refresh_token"))))
(unless refresh-token (return-from auth-google-refresh-token nil))
(handler-case
(let* ((response (dex:post url :content content))
(json (cl-json:decode-json-from-string response)))
(setf (getf *google-token-state* :access-token) (cdr (assoc :access--token json)))
(setf (getf *google-token-state* :expires-at) (+ (get-universal-time) (cdr (assoc :expires--in json))))
(auth-google-save-state)
(kernel-log "OAUTH - Google token refreshed.")
t)
(error (c)
(kernel-log "OAUTH ERROR - Refresh failed: ~a" c)
nil))))
(defun auth-google-get-header ()
"Returns the Bearer token header, refreshing if necessary."
(unless *google-token-state* (auth-google-load-state))
(let ((expires-at (getf *google-token-state* :expires-at 0)))
(when (<= expires-at (+ (get-universal-time) 60)) ; Refresh if < 1 min left
(auth-google-refresh-token)))
(let ((token (getf *google-token-state* :access-token)))
(if token
(list :bearer-token token)
(progn
(kernel-log "OAUTH - No active Google token. Handshake required.")
(kernel-log "OAUTH - Visit this URL: ~a" (auth-google-get-url))
nil))))
(defun auth-google-get-url ()
(let ((client-id (uiop:getenv "GOOGLE_CLIENT_ID")))
(format nil "https://accounts.google.com/o/oauth2/v2/auth?client_id=~a&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&scope=https://www.googleapis.com/auth/generative-language" client-id)))
;; Register as the primary auth provider for Gemini
(org-agent:register-auth-provider :gemini #'auth-google-get-header)

View File

@@ -1,9 +0,0 @@
(in-package :org-agent)
(defun onboard-web-session ()
"Instructions for the Sovereign Cookie Handshake."
(kernel-log "--- GEMINI WEB ONBOARDING ---")
(kernel-log "1. Visit gemini.google.com")
(kernel-log "2. Run this Bookmarklet: javascript:(function(){const c=document.cookie.split('; ').reduce((r,v)=>{const [n,val]=v.split('=');r[n]=val;return r},{});const target=['__Secure-1PSID','__Secure-1PSIDTS'];const out=target.map(n=>({name:n,value:c[n]}));prompt('Copy JSON:',JSON.stringify(out));})();")
(kernel-log "3. Paste the resulting JSON into your .env as GEMINI_COOKIES.")
t)

View File

@@ -1,21 +0,0 @@
(defun chaos-inject-error (sensor-type)
"Injects a synthetic error into a specific sensor pipeline."
(org-agent:kernel-log "CHAOS - Injecting synthetic error into ~a sensor..." sensor-type)
(org-agent:inject-stimulus
`(:type :EVENT :payload (:sensor ,sensor-type :error "SYNTHETIC_CHAOS_ERROR"))))
(defun chaos-stress-test (action context)
"Executes a randomized stress test by injecting failures into the system."
(declare (ignore context))
(let* ((payload (getf action :payload))
(mode (or (getf payload :mode) :random))
(intensity (or (getf payload :intensity) 3)))
(org-agent:kernel-log "CHAOS - Commencing stress test (Mode: ~a, Intensity: ~a)" mode intensity)
(case mode
(:random (dotimes (i intensity)
(let ((failure-type (nth (random 3) '(:test-failure :shell-timeout :llm-error))))
(org-agent:inject-stimulus
`(:type :EVENT :payload (:sensor :chaos-injection :type ,failure-type))))))
(:shell (org-agent:inject-stimulus
`(:type :EVENT :payload (:sensor :shell-response :cmd "git push" :exit-code 128 :stderr "fatal: network unreachable")))))
(format nil "SUCCESS - Chaos stress test initiated.")))

View File

@@ -1,38 +0,0 @@
(defun trigger-skill-chat (context)
(let* ((payload (getf context :payload))
(sensor (getf payload :sensor)))
(eq sensor :chat-message)))
(defun verify-skill-chat (proposed-action context)
(let* ((payload (getf proposed-action :payload))
(action (or (getf payload :action) (getf proposed-action :action)))
(target (getf proposed-action :target)))
(if (and (listp proposed-action)
(or (and (member (getf proposed-action :type) '(:request :REQUEST))
(or (and (member target '(:emacs :EMACS))
(member action '(:insert-at-end :INSERT-AT-END)))
(and (member target '(:shell :SHELL))
(or (getf payload :cmd) (getf proposed-action :cmd)))
(member target '(:tool :TOOL))))
(member (getf proposed-action :type) '(:response :RESPONSE :log :LOG))))
proposed-action
(let ((err-text (format nil "\n\n*System Error:* Chat agent returned invalid action: ~s" proposed-action)))
`(:type :request :target :emacs :payload (:action :insert-at-end :buffer "*org-agent-chat*" :text ,err-text))))))
(defun neuro-skill-chat (context)
"Generates a conversational response, stripping system errors from context."
(let* ((payload (getf context :payload))
(raw-text (getf payload :text))
;; Context Purge: Remove system errors and hallucinations from the history
(clean-text (cl-ppcre:regex-replace-all "(?i)Unknown request|System Error.*|Thinking\\.\\.\\." raw-text ""))
(trimmed-text (if (> (length clean-text) 1000)
(subseq clean-text (- (length clean-text) 1000))
clean-text)))
(ask-neuro trimmed-text :system-prompt "ACTUATOR IDENTITY: You are the pure Lisp actuator for the org-agent kernel.
MANDATE: Output EXACTLY ONE Common Lisp property list starting with (:type :REQUEST).
ZERO CONVERSATION: Do not explain. Do not use markdown.
STRICT RULE: Never output the strings 'Unknown request' or 'System Error'.
REQUIRED FORMATS:
- To reply: (:type :REQUEST :target :emacs :action :insert-at-end :buffer \"*org-agent-chat*\" :text \"* <Response>\")
- To use a tool: (:type :REQUEST :target :tool :action :call :tool \"<name>\" :args (...))")))

View File

@@ -1,19 +0,0 @@
(defvar *context-stack* nil)
(defun context-push (new-context)
"Push a new context (usually a path or a plist) onto the stack."
(push new-context *context-stack*)
(kernel-log "CONTEXT - Pushed: ~a" new-context))
(defun context-pop ()
"Pop the top context from the stack."
(let ((old (pop *context-stack*)))
(kernel-log "CONTEXT - Popped: ~a" old)
old))
(defun context-resolve-path (path)
"Resolve PATH relative to the current context if it's a directory, otherwise return as is."
(let ((current (car *context-stack*)))
(if (and current (stringp current) (uiop:directory-pathname-p current))
(merge-pathnames path current)
path)))

View File

@@ -1,79 +0,0 @@
(defvar *cron-registry* nil)
(defun cron-register (name schedule-fn action-fn)
"Register a new cron task."
(push (list :name name :schedule schedule-fn :action action-fn :last-run 0) *cron-registry*))
(defun cron-trigger-loop ()
"Iterate through registered tasks and trigger those whose schedule matches."
(dolist (task *cron-registry*)
(let ((name (getf task :name))
(schedule (getf task :schedule))
(action (getf task :action)))
(when (funcall schedule)
(kernel-log "CRON - Triggering task: ~a" name)
(funcall action)
(setf (getf task :last-run) (get-universal-time))))))
(defun trigger-skill-cron (context)
(let ((type (getf context :type))
(payload (getf context :payload)))
(when (and (eq type :EVENT) (eq (getf payload :sensor) :heartbeat))
(cron-trigger-loop)
(trigger-nightly-grooming)
t)))
(defun parse-org-timestamp (ts-str)
(let ((match (nth-value 1 (cl-ppcre:scan-to-strings "<(\\d{4})-(\\d{2})-(\\d{2}).*>" ts-str))))
(if match
(encode-universal-time 0 0 0
(parse-integer (aref match 2))
(parse-integer (aref match 1))
(parse-integer (aref match 0)))
nil)))
(defun trigger-nightly-grooming ()
"Checks if the current time is within the nightly grooming window (e.g., 3:00 AM - 4:00 AM)."
(let* ((now (local-time:now))
(hour (local-time:timestamp-hour now)))
(when (= hour 3)
(kernel-log "CRON - Initiating Nightly Grooming Cycle...")
(org-agent:inject-stimulus `(:type :EVENT :payload (:sensor :grooming-cycle))))))
(defun context-get-upcoming-deadlines (&optional (days 3))
(let* ((now (get-universal-time))
(future-limit (+ now (* days 24 60 60)))
(all-headlines (org-agent:list-objects-by-type :HEADLINE))
(upcoming nil))
(dolist (obj all-headlines)
(let* ((attrs (org-agent:org-object-attributes obj))
(deadline-str (getf attrs :DEADLINE))
(deadline-time (when deadline-str (parse-org-timestamp deadline-str))))
(when (and deadline-time (< deadline-time future-limit) (> deadline-time (- now 86400)))
(push (list :title (getf attrs :TITLE) :deadline deadline-str) upcoming))))
upcoming))
(defun context-get-stalled-waiting-items (&optional (days 3))
(let* ((now (get-universal-time))
(past-limit (- now (* days 24 60 60)))
(all-headlines (org-agent:list-objects-by-type :HEADLINE))
(stalled nil))
(dolist (obj all-headlines)
(let* ((attrs (org-agent:org-object-attributes obj))
(state (getf attrs :TODO-STATE))
(last-sync (org-agent:org-object-last-sync obj)))
(when (and (equal state "WAITING") (< last-sync past-limit))
(push (list :title (getf attrs :TITLE)) stalled))))
stalled))
(defun neuro-skill-cron (context)
(let* ((upcoming (context-get-upcoming-deadlines 3))
(stalled (context-get-stalled-waiting-items 3))
(now-str (local-time:format-timestring nil (local-time:now))))
(format nil "
CURRENT TIME: ~a
UPCOMING DEADLINES (Next 3 Days): ~{~a: ~a~%~}
STALLED WAITING ITEMS (> 3 days old): ~{~a~%~}
" now-str
(loop for item in upcoming append (list (getf item :deadline) (getf item :title)))
(loop for item in stalled collect (getf item :title)))))

View File

@@ -1,11 +0,0 @@
(defun delegation-trigger (context)
"Examine CONTEXT to see if delegation is needed.
Criteria: Task complexity or explicit :delegate-to flag."
(let ((complexity (getf context :complexity 0))
(explicit-target (getf context :delegate-to)))
(or (> complexity 7) explicit-target)))
(defun delegation-actuate (task target)
"Dispatch TASK to TARGET. TARGET can be a sub-agent name or a skill keyword."
(kernel-log "DELEGATION - Actuating '~a' for task: ~a" target (getf task :title))
(org-agent:spawn-sub-agent :target target :task task))

View File

@@ -1,16 +0,0 @@
(in-package :org-agent)
(defun economist-route-task (context)
(declare (ignore context))
'(:openrouter))
(defun economist-get-model-for-provider (provider &optional context)
"Returns 100% Free/Subsidized model IDs from OpenRouter. Updated April 2026."
(let ((complexity (ignore-errors (uiop:symbol-call :org-agent.skills.org-skill-router :router-classify-complexity context))))
(case provider
(:openrouter
(case complexity
(:REASONING "meta-llama/llama-3.3-70b-instruct:free") ; High fidelity, zero cost
(:COGNITION "qwen/qwen3.6-plus:free") ; Latest interaction, zero cost
(t "meta-llama/llama-3.2-3b-instruct:free"))) ; Ultra-fast reflex, zero cost
(t nil))))

View File

@@ -1,38 +0,0 @@
(defun handle-emacs-client (stream)
;; Logic for parsing length-prefixed OACP messages
(format nil "Handling client on stream: ~a" stream))
(defun stream-to-emacs (stream action-plist)
"Streams a chunk of data to a specific Emacs client over OACP using framing."
(let* ((type (or (getf action-plist :type) :request))
(payload (getf action-plist :payload))
;; Ensure Emacs always receives a :payload drawer
(envelope (if (and (getf action-plist :type) payload)
action-plist
(let ((clean-payload (copy-list action-plist)))
(remf clean-payload :type)
(remf clean-payload :id)
(list :type type
:id (or (getf action-plist :id) (get-universal-time))
:payload clean-payload))))
(msg (prin1-to-string envelope))
(len (length msg))
(framed (format nil "~6,'0x~a" len msg)))
(handler-case
(progn
(write-string framed stream)
(finish-output stream))
(error (c)
(kernel-log "BRIDGE - Lost client: ~a" stream)
(org-agent:unregister-emacs-client stream)))))
(defun broadcast-to-emacs (action-plist context)
"Sends a framed message back to the client that sent the stimulus, or all clients if async."
(let ((stream (getf context :reply-stream)))
(if stream
(stream-to-emacs stream action-plist)
(progn
(kernel-log "BRIDGE - Async broadcast to all clients...")
(bt:with-lock-held (org-agent:*clients-lock*)
(dolist (s org-agent:*emacs-clients*)
(stream-to-emacs s action-plist)))))))

View File

@@ -1,20 +0,0 @@
(defun get-embedding (text &key (provider :ollama))
"Retrieves the embedding vector for TEXT using specified PROVIDER."
(kernel-log "NEURO [Embedding] - Generating via ~a..." provider)
(case provider
(:ollama (get-embedding-ollama text))
(:gemini (get-embedding-gemini text))
(t (error "Unsupported embedding provider: ~a" provider))))
(defun get-embedding-ollama (text)
(let* ((url "http://localhost:11434/api/embeddings")
(payload (cl-json:encode-json-to-string `(("model" . "mxbai-embed-large") ("prompt" . ,text))))
(response (dex:post url :content payload :headers '(("Content-Type" . "application/json")))))
(cdr (assoc :embedding (cl-json:decode-json-from-string response)))))
(defun get-embedding-gemini (text)
(let* ((api-key (getf (org-agent:get-credentials :gemini) :api-key))
(url (format nil "https://generativelanguage.googleapis.com/v1beta/models/embedding-001:embedContent?key=~a" api-key))
(payload (cl-json:encode-json-to-string `(("content" . (("parts" . ((("text" . ,text))))))))))
(let ((response (dex:post url :content payload :headers '(("Content-Type" . "application/json")))))
(cdr (assoc :values (cdr (assoc :embedding (cl-json:decode-json-from-string response))))))))

View File

@@ -1,22 +0,0 @@
(in-package :org-agent)
(defun set-llm-model (provider model-id)
"Registers a preferred model for a provider in the Object Store."
(let ((config-id (format nil "config-llm-~a" (string-downcase (string provider)))))
(let ((obj (make-org-object
:id config-id
:type :CONFIG
:attributes `(:provider ,provider :model-id ,model-id)
:content (format nil "Fleet preference for ~a set to ~a" provider model-id)
:version (get-universal-time))))
(setf (gethash config-id *object-store*) obj)
(kernel-log "CONFIG - Fleet updated: ~a -> ~a" provider model-id)
t)))
(defun get-llm-model (provider &optional default)
"Retrieves the preferred model for a provider from the Object Store."
(let* ((config-id (format nil "config-llm-~a" (string-downcase (string provider))))
(obj (gethash config-id *object-store*)))
(if obj
(getf (org-object-attributes obj) :model-id)
default)))

View File

@@ -1,26 +0,0 @@
def simulate_get_tiered_model(tier, mock_store):
mapping = {
"powerful": "LLM_MODEL_POWERFUL",
"fast": "LLM_MODEL_FAST",
"free": "LLM_MODEL_FREE"
}
prop_key = mapping.get(tier.lower(), "LLM_MODEL_TEXT")
return mock_store.get(prop_key, "gpt-3.5-turbo") # Default
if __name__ == "__main__":
mock_store = {
"LLM_MODEL_POWERFUL": "claude-3-opus",
"LLM_MODEL_FAST": "gpt-4o-mini"
}
print("--- Test: Tier Resolution ---")
m1 = simulate_get_tiered_model("powerful", mock_store)
m2 = simulate_get_tiered_model("fast", mock_store)
m3 = simulate_get_tiered_model("free", mock_store) # Not in store
print(f"Powerful: {m1}")
print(f"Fast: {m2}")
print(f"Free (Default): {m3}")
status = "PASS" if m1 == "claude-3-opus" and m2 == "gpt-4o-mini" and m3 == "gpt-3.5-turbo" else "FAIL"
print(f"\nFinal Status: {status}")

View File

@@ -1,24 +0,0 @@
;;; TDD Suite: org-skill-environment-config
;;; Status: RED
;;; Author: Tech-Analyst-Agent
;;; Created: [2026-03-31 Tue 15:10]
(defpackage :org-skill-environment-config-tests
(:use :cl :fiveam :org-skill-environment-config))
(in-package :org-skill-environment-config-tests)
(def-suite config-suite
:description "Tests for homoiconic configuration retrieval.")
(in-suite config-suite)
(test retrieve-attribute
"Ensure a property can be retrieved from a mock object store."
;; Requires mock object store logic
(skip "Mock object store required."))
(test model-tiering-resolution
"Ensure tiers are mapped to the correct properties."
;; We can mock get-config-attribute to test the mapping logic
(skip "Internal mapping test required."))

View File

@@ -1,33 +0,0 @@
import json
def simulate_lisp_to_schema(fn_name, params, docstring):
"""
Simulates the transformation of a Lisp signature to Gemini JSON Tool Schema.
"""
schema = {
"name": fn_name.lower().replace("-", "_"),
"description": docstring,
"parameters": {
"type": "object",
"properties": {
p: {"type": "string"} for p in params
},
"required": params
}
}
return schema
if __name__ == "__main__":
print("--- Test: Lisp to Gemini Schema ---")
fn = "scaffold-project"
params = ["name", "type"]
doc = "Physically creates the material PSF project."
expected_name = "scaffold_project"
result = simulate_lisp_to_schema(fn, params, doc)
print(json.dumps(result, indent=2))
status = "PASS" if result["name"] == expected_name and "parameters" in result else "FAIL"
print(f"\nStatus: {status}")

View File

@@ -1,14 +0,0 @@
(defun git-status ()
"Executes git status and returns the output."
(uiop:run-program '("git" "status" "--short") :output :string))
(defun git-commit (message)
"Stages all tracked changes and commits them."
(kernel-log "GIT - Committing: ~a" message)
(uiop:run-program '("git" "add" "-u"))
(uiop:run-program `("git" "commit" "-m" ,message)))
(defun git-push ()
"Pushes to the current branch origin."
(kernel-log "GIT - Pushing to origin...")
(uiop:run-program '("git" "push")))

View File

@@ -1,27 +0,0 @@
import json
def simulate_normalize_signal(raw_json, approved_id):
data = json.loads(raw_json)
sender = data.get("source")
text = data.get("message")
if sender == approved_id:
return {"type": "EVENT", "payload": {"sensor": "inbound-message", "channel": "signal", "text": text}}
return None
if __name__ == "__main__":
approved_id = "+1234567890"
print("--- Test 1: Authorized Signal Message ---")
valid_payload = json.dumps({"source": "+1234567890", "message": "Hello Agent"})
res1 = simulate_normalize_signal(valid_payload, approved_id)
print(f"Result: {res1}")
status1 = "PASS" if res1 and res1["payload"]["text"] == "Hello Agent" else "FAIL"
print("\n--- Test 2: Unauthorized Signal Message ---")
malicious_payload = json.dumps({"source": "+9999999999", "message": "I am evil"})
res2 = simulate_normalize_signal(malicious_payload, approved_id)
print(f"Result: {res2}")
status2 = "PASS" if res2 is None else "FAIL"
print(f"\nFinal Status: {'PASS' if status1 == 'PASS' and status2 == 'PASS' else 'FAIL'}")

View File

@@ -1,17 +0,0 @@
(defun log-scan (&optional (lines 100))
"Reads the last LINES lines of the system log file."
(let ((log-file (merge-pathnames "logs/agent.log" (uiop:getenv "SYSTEM_DIR"))))
(if (uiop:file-exists-p log-file)
(uiop:run-program `("tail" "-n" ,(write-to-string lines) ,(namestring log-file)) :output :string)
"Log file not found.")))
(defun log-summarize (logs)
"Symbolic summary of LOGS focusing on errors and warnings."
(let ((lines (uiop:split-string logs :separator '(#\Newline)))
(errors 0)
(warnings 0))
(dolist (line lines)
(cond
((cl-ppcre:scan "ERROR" line) (incf errors))
((cl-ppcre:scan "WARN" line) (incf warnings))))
(format nil "Log Summary: ~a errors, ~a warnings found in scan." errors warnings)))

View File

@@ -1,54 +0,0 @@
#+TITLE: Atomic Notes (Zettelkasten) & GTD in Org-mode Project
#+AUTHOR: Amero Garcia
#+CREATED: [2026-03-16 Mon 14:00]
#+BEGIN_COMMENT
This file outlines the project to design, implement, and document a comprehensive, integrated workflow for Atomic Notes (Zettelkasten) and GTD using Org-mode, with the ultimate output being an agent skill.
#+END_COMMENT
* Atomic Notes (Zettelkasten) & GTD in Org-mode Project
*Goal:** To design, implement, and document a comprehensive, integrated workflow for Atomic Notes (Zettelkasten) (knowledge management) and Getting Things Done (GTD - task management) using Org-mode. *The ultimate output of this project will be an agent skill.**
*Key Integrations:**
- *Emacs:** Primary access and powerful Org-mode features.
- *Android Tools:** Ensure seamless access and functionality via Markor and Orgzly (revived).
*Strategic Importance:** This system will become the primary coordination method for our work, outside of direct chat communication. It will centralize task tracking, knowledge capture, and project management.
*Workflow Details & Current Setup (as provided by Amr):**
- *Org-mode File Front Matter:** For each Org-mode file, there must be a basic front matter. At minimum, this must include a `#+TITLE:`, an `#+AUTHOR:`, and a `#+CREATED:` date. Short descriptive comments within a `#+BEGIN_COMMENT` / `#+END_COMMENT` block are also highly recommended.
- *Inbox File:** `memex/inbox.org`. All new captured items will go here. No other files in the inbox collection are to be used for general inbox capture.
- *GTD.org Structure:** Contains four top-level `*` headings:
- `* Actions`: For standalone actionable items.
- `* Projects`: Contains `*` headers for each project, with `***` headers for actionable items within those projects.
- `* Incubate`: For placeholders for future projects.
- `* Habits`: Tracks recurring personal habits, potentially to be used as a heartbeat for the new AI agent.
- *:CREATED: Property:** All items in `memex/inbox.org` and `GTD.org` must include a `:CREATED:` property in their `:PROPERTIES:` drawer. The date format is `[YYYY-MM-DD Day HH:MM]`.
- *:LOGBOOK: Drawer:** All task items must include a `:LOGBOOK:` drawer AFTER the `:PROPERTIES:` drawer (not nested inside). State changes are logged as `- State "NEW" from "OLD" [timestamp]`. This tracks the full history of state transitions for each task.
- *Org-Todo States:** Items will use the following `org-todo` keywords to indicate status: `NEXT`, `TODO`, `WAIT`, `DONE`, `CNCL`. It is understood that these states are used to make tasks appear in Amr's Emacs and Orgzly agendas, serving as a direct mechanism for communicating required actions.
- *Authorship & Assignment:** Confirmed use of `:AUTHOR:` (for original creator) and `:ASSIGNED:` (for current responsible individual). It is noted that filtering by `:ASSIGNED:` is possible in Emacs, with potential uncertainties for Orgzly.
- *User Interaction Requirements (Emacs, Orgzly, Markor):**
- Ability to follow status of actionable items in Org-zly and Emacs agendas.
- Ability to read and write Org-mode files in Emacs and Markor.
- Ability to find out and manipulate `TODO` items in Org-zly and Emacs agendas.
- *Agent-User Coordination Mechanism:** The agent will place items requiring Amr's attention as `TODO` (general planned items) and `NEXT` (immediate, high-priority actions) in his agenda.
- *Automatic `NEXT` Promotion:** A critical feature to integrate is the automatic promotion of a `TODO` item to `NEXT` in `org-gtd` once the preceding `NEXT` item (within a sequential *Project*) is marked `DONE`. This behavior specifically applies to interdependent or sequential items that constitute a `Project`. Standalone `NEXT` items (e.g., under `* Actions`) are `NEXT` by default and do not trigger subsequent promotions. This behavior must be accounted for in the agent skill.
*Initial Scope:**
- Ensure all items I create or modify adhere strictly to the `:CREATED:` property format and `org-todo` states.
- Implement the proposed `:AUTHOR:` and `:ASSIGNED:` properties for collaborative items.
- Defining specific Org-mode structures for Atomic Notes (Zettelkasten) notes (unique IDs, linking, tags), building upon existing GTD structure.
- Establishing workflows for daily capture, processing, and review aligned with Amr's system.
- Exploring and configuring Markor and Orgzly for optimal mobile interaction with Org files.
- Documenting the entire workflow for clarity and ease of use.
*Information Needed from Amr:**
- Confirmation or modification of the proposed `:AUTHOR:` and `:ASSIGNEE:` properties (e.g., preferred format for names, single vs. multiple assignees).
- Specific requirements or desired features for mobile access/editing with tools like Markor and Orgzly.
- Your vision for how this system will function as our *"main coordination method"** in practice.
- Any existing Org-mode Atomic Notes (Zettelkasten) practices you currently use or prefer.
*Next Steps:**
1. Gather Amr's current practices and specific requirements.
2. Begin outlining core Org-mode structures for both Atomic Notes (Zettelkasten) and GTD.
3. Research best practices for mobile Org-mode synchronization and editing with Markor/Orgzly.

View File

@@ -1,64 +0,0 @@
#+TITLE: Org-Agent Memex Architecture Notes
#+AUTHOR: Amr
#+CREATED: [2026-03-17 Tue]
#+BEGIN_COMMENT
Core architectural principles and design decisions for the org-agent memex system.
#+END_COMMENT
* Core Philosophy: Single User, Single Agent
** Why This Scope?
The system is deliberately designed for *one human, one AI assistant*:
- *No coordination complexity*: One agent owns one workflow (Scribe = Atomic Notes (Zettelkasten) distillation, GTD Manager = task promotion)
- *No conflict resolution*: Agent reads from immutable sources (daily logs) and writes to separate targets (atomic notes, GTD promotions)
- *No multi-agent negotiation*: The assistant doesn't delegate to sub-agents; it executes skills directly
This is *not* a multi-agent orchestration system. It's personal automation.
* Generalization via Environment Variables
** Principle: Build with generalization, keep variable values out**
All identity-specific and configuration values live in `.env`:
| Variable | Purpose |
|----------|---------|
| MEMEX_USER | The human user's name (e.g., "Amr") |
| MEMEX_ASSISTANT | The AI assistant's identifier (e.g., "Agent") |
| CURRENT_TEXT_MANIPULATION_MODEL | The LLM tier for text processing |
| MEMEX_* paths | Folder structure (PARA hierarchy) |
Skills reference these as `$VARIABLE` in scripts or get instructed to use them. No hardcoded names in skill logic.
* Source of Simplicity
** What makes this project tractable:**
1. *Standing on established frameworks*: Org-mode, Atomic Notes (Zettelkasten) method, GTD, PARA organization—the hard thinking is already done
2. *Git as state machine*: Rather than building custom sync or consensus, we use Git commits as the source of truth for "what's new"
3. *Immutable sources*: Daily logs are append-only; the Scribe never writes to them
4. *Deterministic outputs*: Atomic notes have clear rules (concept-filenames, id: backlinks, no dates in names)
** What we're NOT building** (which would add complexity):
- Multi-user collaborative editing
- Real-time synchronization across devices
- Agent-to-agent task delegation protocols
- Distributed state management
- Conflict resolution for simultaneous edits
The complexity is in the *workflow logic*, not the technical infrastructure.
* Future: Linking with Native Org-Agent
** Phase 1** (current): OpenClaw orchestrates cloud LLMs using SKILL.md definitions
** Phase 2** (future): Native `org-agent` (Common Lisp) executes the same skills locally
The interface remains constant:
- Skill definitions in Org-mode format (SKILL.md)
- .env configuration
- PARA folder structure
- Git-based state tracking
When `org-agent` matures, it can read and execute the same skill files we're writing today. The transition from cloud-based to local inference becomes seamless because the *specification* (Org files) is implementation-agnostic.

View File

@@ -1,19 +0,0 @@
# Org-Agent Memex GTD (org-agent-memex-gtd)
This is the task management counterpart to the Atomic Notes (Atomic Notes (Zettelkasten)) skill. It automates the GTD (Getting Things Done) workflows within your Org-mode environment.
## Features
1. **Sequential Project Auto-Promotion:** When you complete a `NEXT` action inside a sequential project in `gtd.org` (marking it `DONE`), this skill automatically finds the subsequent `TODO` item and promotes it to `NEXT`. This ensures your Org Agenda is always populated with the very next actionable steps without manual intervention.
2. **Inbox Processing Assistance:** Provides an automated routine to read through `inbox.org`, categorize items, and propose where they should be filed in `gtd.org` (e.g., under `* Actions` or specific `* Projects`).
3. **Collaboration Setup:** Standardizes the use of `:AUTHOR:` and `:ASSIGNED:` properties so you and the AI agent can delegate tasks to each other seamlessly.
## Configuration
Relies on the same `.env` file used by the Atomic Notes (Atomic Notes (Zettelkasten)) module, specifically:
- `MEMEX_DIR` - Base memex directory
- `MEMEX_INBOX` - Inbox file (e.g., `memex/inbox.org`)
- `MEMEX_SYSTEM` - System directory for skills
## Setup
Like the Scribe agent, this skill can be run ad-hoc by asking your AI assistant to "Run the GTD manager" or scheduled as a background cron job to periodically audit and update task statuses.

View File

@@ -1,61 +0,0 @@
---
name: org-agent-memex-gtd
description: "Automate Getting Things Done (GTD) workflows in Emacs Org-mode. Auto-promotes TODO to NEXT in sequential projects and processes the inbox. Use when: user asks to manage tasks, update GTD, promote NEXT actions, or process the inbox. NOT for: extracting Atomic Notes (Atomic Notes (Zettelkasten)) knowledge or editing daily logs."
homepage: ""
metadata: { "openclaw": { "emoji": "✅", "requires": { "bins": ["grep", "sed"] }, "user-invocable": true } }
---
# Org-Agent Memex GTD
Automated GTD manager designed to keep your task lists fluid and your Org Agenda accurate. It handles the structural logic of sequential projects and helps clarify your inbox.
## When to Use
**USE this skill when:**
- The user asks to "update GTD", "promote next actions", or "manage tasks".
- The user completes a task in a project and wants the next one queued up.
- The user asks to "process the inbox" or "clarify inbox tasks".
**DON'T use this skill when:**
- Working with Atomic Notes (Atomic Notes (Zettelkasten)), evergreen notes, or daily logs (use `org-agent-memex-zettlekasten`).
- Just capturing a quick thought (user should do this via Emacs).
## Instructions
### Action 1: Auto-Promote Sequential Tasks (`gtd.org`)
When asked to update projects or promote NEXT actions:
1. Read the `gtd.org` file (located in `$MEMEX_DIR/gtd.org`).
2. Identify sequential projects (under `* Projects`).
3. Look for the most recently completed tasks (marked `DONE`).
4. If a task was marked `DONE`, find the immediate next sibling heading that is marked `TODO` within the same parent project.
5. Change that `TODO` to `NEXT`.
6. Ensure that standalone actions (under `* Actions`) are left alone (they are typically parallel, not sequential).
7. Save the file and report which tasks were promoted to `NEXT`.
### Action 2: Inbox Processing (`inbox.org`)
When asked to process the inbox:
1. Read `$MEMEX_INBOX`.
2. For each raw entry, determine if it is actionable.
3. If actionable, propose a structured Org-mode task format with:
- `TODO` or `NEXT` state
- `:PROPERTIES:` drawer with `:CREATED:` and optional `:ASSIGNED:`
- `:LOGBOOK:` drawer (AFTER :PROPERTIES:, not inside) tracking state changes
Format:
```org
*** TODO Task Name
:PROPERTIES:
:CREATED: [YYYY-MM-DD Day HH:MM]
:ASSIGNED: $MEMEX_USER
:END:
:LOGBOOK:
- State "TODO" from "" [YYYY-MM-DD Day HH:MM]
:END:
```
4. Propose which section of `gtd.org` it belongs to (e.g., a specific project or standalone `* Actions`).
5. Ask the user for confirmation before moving the items out of `inbox.org` into `gtd.org`.
## Notes
- **Timestamps:** Ensure every new task generated or moved retains or receives a `:CREATED:` property formatted as `[YYYY-MM-DD Day HH:MM]`.
- **Assignment:** The agent can assign tasks to itself by setting `:ASSIGNED: $MEMEX_ASSISTANT` or to the user via `:ASSIGNED: $MEMEX_USER`. Configure these values in your `.env` file.
- **State Tracking:** The `:LOGBOOK:` drawer must appear AFTER the `:PROPERTIES:` drawer (not nested inside). State changes are logged as `- State "NEW" from "OLD" [timestamp]`. When a task changes state (e.g., TODO → NEXT, or TODO → DONE), append a new line to the LOGBOOK drawer.

View File

@@ -1,19 +0,0 @@
# Org-Agent Memex GTD (org-agent-memex-gtd)
This is the task management counterpart to the Atomic Notes (Atomic Notes (Zettelkasten)) skill. It automates the GTD (Getting Things Done) workflows within your Org-mode environment.
## Features
1. **Sequential Project Auto-Promotion:** When you complete a `NEXT` action inside a sequential project in `gtd.org` (marking it `DONE`), this skill automatically finds the subsequent `TODO` item and promotes it to `NEXT`. This ensures your Org Agenda is always populated with the very next actionable steps without manual intervention.
2. **Inbox Processing Assistance:** Provides an automated routine to read through `inbox.org`, categorize items, and propose where they should be filed in `gtd.org` (e.g., under `* Actions` or specific `* Projects`).
3. **Collaboration Setup:** Standardizes the use of `:AUTHOR:` and `:ASSIGNED:` properties so you and the AI agent can delegate tasks to each other seamlessly.
## Configuration
Relies on the same `.env` file used by the Atomic Notes (Atomic Notes (Zettelkasten)) module, specifically:
- `MEMEX_DIR` - Base memex directory
- `MEMEX_INBOX` - Inbox file (e.g., `memex/inbox.org`)
- `MEMEX_SYSTEM` - System directory for skills
## Setup
Like the Scribe agent, this skill can be run ad-hoc by asking your AI assistant to "Run the GTD manager" or scheduled as a background cron job to periodically audit and update task statuses.

View File

@@ -1,61 +0,0 @@
---
name: org-agent-memex-gtd
description: "Automate Getting Things Done (GTD) workflows in Emacs Org-mode. Auto-promotes TODO to NEXT in sequential projects and processes the inbox. Use when: user asks to manage tasks, update GTD, promote NEXT actions, or process the inbox. NOT for: extracting Atomic Notes (Atomic Notes (Zettelkasten)) knowledge or editing daily logs."
homepage: ""
metadata: { "openclaw": { "emoji": "✅", "requires": { "bins": ["grep", "sed"] }, "user-invocable": true } }
---
# Org-Agent Memex GTD
Automated GTD manager designed to keep your task lists fluid and your Org Agenda accurate. It handles the structural logic of sequential projects and helps clarify your inbox.
## When to Use
**USE this skill when:**
- The user asks to "update GTD", "promote next actions", or "manage tasks".
- The user completes a task in a project and wants the next one queued up.
- The user asks to "process the inbox" or "clarify inbox tasks".
**DON'T use this skill when:**
- Working with Atomic Notes (Atomic Notes (Zettelkasten)), evergreen notes, or daily logs (use `org-agent-memex-zettlekasten`).
- Just capturing a quick thought (user should do this via Emacs).
## Instructions
### Action 1: Auto-Promote Sequential Tasks (`gtd.org`)
When asked to update projects or promote NEXT actions:
1. Read the `gtd.org` file (located in `$MEMEX_DIR/gtd.org`).
2. Identify sequential projects (under `* Projects`).
3. Look for the most recently completed tasks (marked `DONE`).
4. If a task was marked `DONE`, find the immediate next sibling heading that is marked `TODO` within the same parent project.
5. Change that `TODO` to `NEXT`.
6. Ensure that standalone actions (under `* Actions`) are left alone (they are typically parallel, not sequential).
7. Save the file and report which tasks were promoted to `NEXT`.
### Action 2: Inbox Processing (`inbox.org`)
When asked to process the inbox:
1. Read `$MEMEX_INBOX`.
2. For each raw entry, determine if it is actionable.
3. If actionable, propose a structured Org-mode task format with:
- `TODO` or `NEXT` state
- `:PROPERTIES:` drawer with `:CREATED:` and optional `:ASSIGNED:`
- `:LOGBOOK:` drawer (AFTER :PROPERTIES:, not inside) tracking state changes
Format:
```org
*** TODO Task Name
:PROPERTIES:
:CREATED: [YYYY-MM-DD Day HH:MM]
:ASSIGNED: $MEMEX_USER
:END:
:LOGBOOK:
- State "TODO" from "" [YYYY-MM-DD Day HH:MM]
:END:
```
4. Propose which section of `gtd.org` it belongs to (e.g., a specific project or standalone `* Actions`).
5. Ask the user for confirmation before moving the items out of `inbox.org` into `gtd.org`.
## Notes
- **Timestamps:** Ensure every new task generated or moved retains or receives a `:CREATED:` property formatted as `[YYYY-MM-DD Day HH:MM]`.
- **Assignment:** The agent can assign tasks to itself by setting `:ASSIGNED: $MEMEX_ASSISTANT` or to the user via `:ASSIGNED: $MEMEX_USER`. Configure these values in your `.env` file.
- **State Tracking:** The `:LOGBOOK:` drawer must appear AFTER the `:PROPERTIES:` drawer (not nested inside). State changes are logged as `- State "NEW" from "OLD" [timestamp]`. When a task changes state (e.g., TODO → NEXT, or TODO → DONE), append a new line to the LOGBOOK drawer.

View File

@@ -1,43 +0,0 @@
# GTD Work Breakdown (org-agent-memex-workbreakdown)
Meta-cognitive skill to prevent AI assistants and users from stalling on complex tasks. Forces atomic decomposition before execution.
## The Problem
Complex tasks cause:
- Context saturation (procrastination)
- Scope creep (adding "just one more thing")
- The "heartbeat loop" (repeating tasks without progress)
- Overwhelm and hesitation
## The Solution
**Decompose first, execute second.**
1. Analyze task complexity (>3 steps? >2 files?)
2. Break into atomic TODOs in GTD.org
3. Execute only the FIRST item
4. Yield back to user
## Configuration
Uses same `.env` structure as other org-agent-memex skills:
- `MEMEX_DIR`
- `MEMEX_USER`
- `MEMEX_ASSISTANT`
- `CURRENT_TEXT_MANIPULATION_MODEL`
## Usage
When a task feels complex:
1. Ask AI: "Break this down with Work Breakdown skill"
2. AI creates TODOs in GTD.org
3. Execute only first item
4. Report: "[X] Completed step 1. 4 tasks remaining. Continue?"
## Anti-Patterns This Prevents
- "I'll just do it all at once"
- Editing 5+ files before committing
- Writing conditional logic on the fly
- "Let me think about it..." (stalling)

View File

@@ -1,56 +0,0 @@
---
name: org-agent-memex-workbreakdown
description: "Break down complex tasks into atomic TODOs before execution. Use when: a task feels complex, involves multiple files, or may cause context saturation. Prevents procrastination by forcing decomposition. NOT for: simple single-step tasks."
homepage: ""
metadata: { "openclaw": { "emoji": "🔨", "requires": { "bins": [] }, "user-invocable": true } }
---
# GTD Work Breakdown Skill
Meta-cognitive protocol to prevent stalling and context saturation.
## When to Use
**USE this skill when:**
- A task feels "complex" or overwhelming
- It involves editing more than 3 files
- It requires holding multiple concepts in working memory
- You feel the urge to apologize or hesitate (procrastination signal)
- The task description is longer than 2 sentences
**DON'T use this skill when:**
- Simple single-file edit
- Direct question/answer
- Already-broken-down TODO from GTD
## Instructions
### The Decomposition Protocol
When invoked, BEFORE executing any other action:
1. **Analyze Complexity**: Ask "How many discrete steps does this actually require?"
2. **Breakdown Threshold**: If >3 steps or >2 files affected, MUST decompose
3. **Create TODOs**: Write each atomic step as a separate `TODO` in `GTD.org` under appropriate project
4. **Assign Ownership**: Each TODO gets `:ASSIGNED: $MEMEX_USER` or `:ASSIGNED: $MEMEX_ASSISTANT`
5. **Set FIRST**: Mark only the first TODO as `NEXT`, rest remain `TODO`
6. **Execute First**: Complete ONLY the `NEXT` item
7. **Yield**: After completion, report to user: "[X] Completed [first task]. [N] tasks remaining in GTD. Continue?"
### Anti-Pattern Detection
If you find yourself:
- Thinking "I'll just do it all at once"
- Planning to edit >5 files before committing
- Writing conditional logic on the fly
STOP. Invoke this skill immediately.
## Complexity Checklist
Before executing any task, ask:
- [ ] Can I complete this in under 5 minutes?
- [ ] Does it touch only 1 file?
- [ ] Is the outcome predictable?
If ANY answer is "No", decompose first.

View File

@@ -1,19 +0,0 @@
MEMEX_DIR="memex"
MEMEX_INBOX="memex/inbox.org"
MEMEX_DAILY="memex/1_daily"
MEMEX_NOTES="memex/2_notes"
MEMEX_DRAFTS="memex/3_drafts"
MEMEX_PUBLISHED="memex/4_published"
MEMEX_PROJECTS="memex/5_projects"
MEMEX_AREAS="memex/6_areas"
MEMEX_RESOURCES="memex/7_resources"
MEMEX_ARCHIVES="memex/8_archives"
MEMEX_SYSTEM="memex/9_system"
MEMEX_ATTACHMENTS="memex/attachments"
# Model Configuration
CURRENT_TEXT_MANIPULATION_MODEL="google-gemini-cli/gemini-3.1-flash"
# Identity Configuration
MEMEX_USER="Amr"
MEMEX_ASSISTANT="Agent"

View File

@@ -1,33 +0,0 @@
# Atomic Notes (Atomic Notes (Zettelkasten)) & GTD Automation (org-agent-memex-zettlekasten)
This system uses a hybrid approach to Personal Knowledge Management (PKM). It leverages Emacs Org-mode for low-friction, structured capture into daily logs, and an OpenClaw AI Sub-Agent ("The Scribe") to nightly distill these raw thoughts into an evergreen, atomic Atomic Notes (Atomic Notes (Zettelkasten)).
## 1. Environment Configuration (`.env`)
To ensure Emacs, OpenClaw, and the Scribe Agent all agree on where files live, we use a single `.env` file at the root of the workspace.
**Action:**
Copy `.env.example` to `.env` and adjust the paths to match your preferred directory structure.
## 2. Emacs Org-Capture Setup
All captures route to the current day's log (e.g. `$MEMEX_DAILY/YYYY-MM-DD.org`), preserving the raw chronological context.
**Action:**
Add the Emacs Lisp snippet from `init-atomic-notes.el` to your `init.el` or `config.el` to set up your capture templates dynamically using the `.env` variables.
## 3. The Distillation State Tracker
The Scribe Agent uses a JSON file to remember the last Git commit it processed, preventing it from distilling the same notes twice or modifying the daily logs directly.
**Action:**
Run `./install.sh` to initialize the directory structure and create the state file (`$MEMEX_SYSTEM/distillation-state.json`) automatically.
## 4. OpenClaw Cron Job (The Scribe Agent)
The final piece is the scheduled automation. We create a cron job in OpenClaw that runs every night, reads the diffs, and creates atomic notes.
**Action:**
1. Move `openclaw-scribe-skill.org` into your `$MEMEX_SYSTEM/skills/` folder.
2. Ask your OpenClaw orchestrator/assistant to schedule the Scribe Agent using the `cron` tool, referencing the prompt defined in `$MEMEX_SYSTEM/skills/Scribe-Agent.org` or your renamed skill file.
3. Configure the cron job to use the model specified in `CURRENT_TEXT_MANIPULATION_MODEL` within your `.env` file (e.g., `google-gemini-cli/gemini-3.1-flash`). You can update this `.env` variable periodically to stay on the most cost-effective text manipulation model.
### Architecture Rules:
- **Dailies are Immutable:** The Scribe reads `$MEMEX_DAILY/` but NEVER writes to it.
- **Evergreen Notes:** The Scribe extracts concepts, generates descriptive snake_case filenames (no dates), and writes them to `$MEMEX_NOTES/` with a `Source:` backlink using an Org-ID reference (`id:`) to the original daily file.

View File

@@ -1,54 +0,0 @@
---
name: org-agent-memex-zettlekasten
description: "Automate the nightly distillation of Emacs Org-mode daily logs into atomic Atomic Notes (Atomic Notes (Zettelkasten)) notes. Use when: user wants to run the Scribe distillation pipeline, process daily captures, or extract Atomic Notes (Atomic Notes (Zettelkasten)) notes. NOT for: generic org-mode editing or GTD task management."
homepage: ""
metadata: { "openclaw": { "emoji": "🧠", "requires": { "bins": ["git"] }, "user-invocable": true } }
---
# Org-Agent Memex Atomic Notes (Atomic Notes (Zettelkasten)) (The Scribe)
Automated distillation skill designed to process raw daily captures into permanent atomic notes for your Atomic Notes (Atomic Notes (Zettelkasten)). It reads the raw chronological logs, identifies newly captured concepts, and extracts them into self-contained evergreen notes with proper Org-Roam `id:` backlinks.
## When to Use
**USE this skill when:**
- Running the nightly Atomic Notes (Atomic Notes (Zettelkasten)) distillation pipeline.
- User asks to "distill my daily notes", "run the scribe", or "process captures".
- Automating atomic note extraction via cron jobs.
**DON'T use this skill when:**
- Editing standard GTD task lists.
- Capturing new notes (that's the user's job via Emacs `org-capture`).
- Modifying the daily logs (dailies are immutable).
## Instructions
When triggered to distill the notes, execute the following strict pipeline:
1. **Read State:** Read the distillation state file (defined by `$MEMEX_SYSTEM/distillation-state.json`) to get the `lastProcessedCommit` hash.
2. **Find New Captures:** Run a Git diff on the daily directory since that commit:
```bash
git diff <last_commit_hash> HEAD -- $MEMEX_DAILY
```
3. **Process Each Capture:**
For every new Atomic Notes (Atomic Notes (Zettelkasten)) capture found in the diff:
- Read the raw capture text.
- Determine the core concept being discussed.
- Generate a concise, `snake_case` filename (e.g., `core_concept_name.org`). Do NOT use dates in this filename.
- Write the content to the notes directory (`$MEMEX_NOTES/<filename>`).
- Ensure the new note is formatted as an atomic Org-mode note with an `#+ID` and a `Source:` backlink using an `id:` reference pointing back to the original daily file.
4. **Update State:** Update the distillation state JSON file with the current HEAD commit hash.
5. **Report:** Output a summary of the concepts extracted and the files created.
## Configuration
This skill expects the environment to be configured via a `.env` file containing at least:
- `MEMEX_DAILY` - Directory containing daily logs (e.g., `memex/1_daily`)
- `MEMEX_NOTES` - Directory for evergreen atomic notes (e.g., `memex/2_notes`)
- `MEMEX_SYSTEM` - Directory for system files and state (e.g., `memex/9_system`)
- `CURRENT_TEXT_MANIPULATION_MODEL` - The LLM to use for cron execution (e.g., `google-gemini-cli/gemini-3.1-flash`)
## Notes
- **Immutability:** The daily logs are raw, immutable records. Never modify them destructively during processing.
- **Evergreen:** Atomic notes should focus on concepts, not chronology.

View File

@@ -1,10 +0,0 @@
(setq org-capture-templates
'(("z" "Atomic Notes (Zettelkasten) (Captures to Daily)")
("zf" "Fleeting Note" entry (file+olp+datetree (expand-file-name (format "%s/%%<%%Y-%%m-%%d>.org" (getenv "MEMEX_DAILY"))))
"* Fleeting Note: %?\n :PROPERTIES:\n :ID: %u\n :CREATED: %U\n :END:\n\n %i")
("zl" "Draft Literature Note" entry (file+olp+datetree (expand-file-name (format "%s/%%<%%Y-%%m-%%d>.org" (getenv "MEMEX_DAILY"))))
"* Literature Note: %?\n :PROPERTIES:\n :ID: %u\n :CREATED: %U\n :AUTHOR: \n :SOURCE: \n :END:\n\n *Summary:*\n %?\n\n *Key Insights:*\n - ")
("zp" "Draft Permanent Note" entry (file+olp+datetree (expand-file-name (format "%s/%%<%%Y-%%m-%%d>.org" (getenv "MEMEX_DAILY"))))
"* Permanent Note: %?\n :PROPERTIES:\n :ID: %u\n :CREATED: %U\n :LINKS: \n :END:\n\n *Concept:*\n %?\n\n *References:*\n - ")
("t" "GTD - Task / Inbox" entry (file (getenv "MEMEX_INBOX"))
"* TODO %?\n :PROPERTIES:\n: :CREATED: %U\n :END:\n :LOGBOOK:\n - State \"TODO\" from \"\" [%U]\n :END:\n\n %i\n %a")))

View File

@@ -1,110 +0,0 @@
#!/usr/bin/env bash
set -e
# Load .env if it exists, otherwise use defaults
if [ -f ".env" ]; then
source .env
else
echo "Creating .env from .env.example..."
cp .env.example .env
source .env
fi
echo "Creating directory structure..."
# Ensure MEMEX_DIR is available, fallback if not set
MEMEX_DIR="${MEMEX_DIR:-memex}"
mkdir -p "$MEMEX_DIR/0_inbox" "$MEMEX_DAILY" "$MEMEX_NOTES" "$MEMEX_DRAFTS" "$MEMEX_PUBLISHED" "$MEMEX_PROJECTS" "$MEMEX_AREAS" "$MEMEX_RESOURCES" "$MEMEX_ARCHIVES" "$MEMEX_SYSTEM/skills" "$MEMEX_ATTACHMENTS"
echo "Generating directory README.org files..."
DATE=$(date +"[%Y-%m-%d %a]")
create_readme() {
local dir=$1
local title=$2
local desc=$3
cat <<EOF > "$dir/README.org"
#+TITLE: $title
#+AUTHOR: User
#+CREATED: $DATE
#+BEGIN_COMMENT
$desc
#+END_COMMENT
* $title
$desc
EOF
}
create_readme "$MEMEX_DIR/0_inbox" "0_inbox: The Capture Point" "Temporary holding area for raw captures, links, and quick thoughts before they are processed into actionable items (GTD) or knowledge (Atomic Notes (Zettelkasten))."
create_readme "$MEMEX_DAILY" "1_daily: The Immutable Log" "Chronological daily logs (YYYY-MM-DD.org) serving as the primary capture location for fleeting notes and daily events. These are immutable records."
create_readme "$MEMEX_NOTES" "2_notes: The Atomic Notes (Zettelkasten)" "Evergreen, atomic notes. Each file represents a single concept, is heavily interlinked, and uses snake_case filenames without dates."
create_readme "$MEMEX_DRAFTS" "3_drafts: Works in Progress" "Long-form writing, essays, or articles actively being synthesized from the atomic notes."
create_readme "$MEMEX_PUBLISHED" "4_published: Final Outputs" "Completed, finalized works and static snapshots of published material."
create_readme "$MEMEX_PROJECTS" "5_projects: Active Projects" "Active, time-bound efforts with a clear definition of done. Each project has its own dedicated folder for specifications and artifacts."
create_readme "$MEMEX_AREAS" "6_areas: Spheres of Responsibility" "Ongoing areas of life and work with a standard to be maintained over time (e.g., Health, Finances, Operations)."
create_readme "$MEMEX_RESOURCES" "7_resources: Reference Material" "Topics of ongoing interest, external reference material, raw literature notes, and useful information."
create_readme "$MEMEX_ARCHIVES" "8_archives: Cold Storage" "Inactive items from other categories, including completed projects, abandoned areas, or deprecated resources."
create_readme "$MEMEX_SYSTEM" "9_system: Memex Administration" "System configuration, AI agent skills, org-mode templates, cron states, and tracking scripts."
echo "Generating root Master Memex README.org..."
cat <<EOF > "$MEMEX_DIR/README.org"
#+TITLE: The Master Memex
#+AUTHOR: User
#+CREATED: $DATE
#+BEGIN_COMMENT
The central hub and map of content for this personal intelligence organization.
#+END_COMMENT
* 🧠 The Master Memex
This is the central hub for our knowledge management system, synthesizing three core methodologies:
- *Atomic Notes (Zettelkasten):* For evergreen, interlinked, atomic knowledge.
- *GTD (Getting Things Done):* For actionable task tracking and project execution.
- *PARA:* For high-level directory organization (Projects, Areas, Resources, Archives).
* The Architecture
Our workspace is strictly divided into these functional zones:
- [[file:0_inbox/README.org][0_inbox]]: The zero-friction capture point for raw thoughts and tasks.
- [[file:1_daily/README.org][1_daily]]: Immutable chronological logs and fleeting notes (YYYY-MM-DD.org).
- [[file:2_notes/README.org][2_notes]]: The Atomic Notes (Zettelkasten). Atomic, concept-based, interlinked notes.
- [[file:3_drafts/README.org][3_drafts]]: Works in progress, essays, and active synthesis.
- [[file:4_published/README.org][4_published]]: Final outputs and static snapshots of completed work.
- [[file:5_projects/README.org][5_projects]]: Active, time-bound efforts with a clear definition of done.
- [[file:6_areas/README.org][6_areas]]: Ongoing spheres of responsibility (e.g., Health, Finances).
- [[file:7_resources/README.org][7_resources]]: External reference material and raw literature notes.
- [[file:8_archives/README.org][8_archives]]: Cold storage for completed projects and inactive items.
- [[file:9_system/README.org][9_system]]: System configuration, AI skills, and automation scripts.
* Core Workflows
** 1. Capture (Anytime)
Everything enters the system via \`0_inbox\` or as a Fleeting Note in \`1_daily\`. Zero friction, no filtering.
** 2. Nightly Distillation (The Scribe)
An automated AI sub-agent reads the daily captures and extracts conceptual thoughts into evergreen, atomic notes in \`2_notes\`, leaving the original daily logs untouched.
** 3. Weekly Maintenance
Review active projects, clarify inbox items into actionable GTD tasks, and explore the Atomic Notes (Zettelkasten) graph to merge concepts and forge new connections.
EOF
# Touch inbox
touch "$MEMEX_INBOX"
# Initialize distillation state if not present
STATE_FILE="$MEMEX_SYSTEM/distillation-state.json"
if [ ! -f "$STATE_FILE" ]; then
echo "Initializing $STATE_FILE..."
# Get current git commit or use a placeholder
HASH=$(git rev-parse HEAD 2>/dev/null || echo "INITIAL_HASH")
echo "{
\"lastProcessedCommit\": \"$HASH\"
}" > "$STATE_FILE"
fi
echo "Installation complete."
echo "1. Add the contents of init-atomic-notes.el to your Emacs config."
echo "2. Add openclaw-scribe-skill.org to your \$MEMEX_SYSTEM/skills/ directory."
echo "3. Ask your OpenClaw agent to schedule the Scribe job."

View File

@@ -1,29 +0,0 @@
#+TITLE: SKILL: Scribe Agent (Distillation Sub-Agent)
#+ID: skill-scribe-agent
#+STARTUP: content
* Overview
The Scribe Agent is an automated distillation sub-agent designed to process raw daily captures into permanent atomic notes for the Atomic Notes (Zettelkasten). It runs as an isolated OpenClaw cron job.
* Configuration
- *Type:* OpenClaw Cron Job
- *Target:* `isolated`
- *Model:* `CURRENT_TEXT_MANIPULATION_MODEL` (Updates periodically based on review; currently an efficient LLM suitable for text parsing).
- *Environment:* Loads variables from `.env` to locate folders (e.g., `$MEMEX_DAILY`, `$MEMEX_NOTES`, `$MEMEX_SYSTEM`).
* System Prompt / Agent Turn Directive
```markdown
You are the Scribe, an automated distillation sub-agent.
Your sole job is to process raw notes into a Atomic Notes (Zettelkasten).
Do not engage in conversation. Only execute the following pipeline:
1. Read `$MEMEX_SYSTEM/distillation-state.json` to get the last processed Git commit hash.
2. Run `git diff <last_commit_hash> HEAD -- $MEMEX_DAILY/` to find new captures.
3. For every new Atomic Notes (Zettelkasten) capture found in the diff:
a. Read the raw capture.
b. Determine the core concept.
c. Generate a concise, snake_case filename (e.g., `core_concept_name.org`).
d. Write the content to `$MEMEX_NOTES/<filename>`, ensuring it is formatted as an atomic Org-mode note with `#+ID` and a `Source:` backlink using an `id:` reference to the original daily file.
4. Update `$MEMEX_SYSTEM/distillation-state.json` with the current HEAD commit hash.
5. Exit.
```

View File

@@ -1,64 +0,0 @@
;;;; memex-manager.lisp --- Primary automation engine for the Memex.
;;;; This file is TANGLED from org-skill-memex.org. DO NOT EDIT MANUALLY.
(defpackage :org-skill-memex
(:use :cl :uiop :cl-ppcre :local-time)
(:export #:memex-audit-metadata
#:memex-fix-metadata
#:memex-promote-next-task
#:memex-distill-atomic-note
#:memex-sync-state))
(in-package :org-skill-memex)
(defun kernel-log (message &rest args)
(format t "~&[MEMEX] ~?" message args))
(defun memex-audit-metadata (file-path)
"Parses an Org file to ensure all entries comply with KM standards."
(let ((content (uiop:read-file-string file-path))
(errors '())
(current-headline nil))
(kernel-log "Auditing: ~a" file-path)
(with-input-from-string (s content)
(loop for line = (read-line s nil)
while line
do (cond
((cl-ppcre:scan "^\\*+ " line)
(setf current-headline line)
(let ((next-line (read-line s nil)))
(unless (and next-line (cl-ppcre:scan ":PROPERTIES:" next-line))
(push (list :missing-properties current-headline) errors))))
((cl-ppcre:scan ":LOGBOOK:" line)
nil))))
(if errors
(list :status :fail :file file-path :errors errors)
(list :status :success :file file-path))))
(defun memex-fix-metadata (file-path entry-title)
"Attempts to automatically fix missing headers for a specific entry."
(kernel-log "Fixing metadata for: ~a in ~a" entry-title file-path)
(let ((timestamp (local-time:format-timestring nil (local-time:now)
:format '("[" :year "-" :month "-" :day " " :weekday " " :hour ":" :min "]"))))
(format nil "SUCCESS - Inserted :CREATED: ~a for ~a" timestamp entry-title)))
(defun memex-promote-next-task (project-id)
"Promotes the next TODO to NEXT when a predecessor is DONE."
(kernel-log "Promoting next task in project: ~a" project-id)
(let ((gtd-file (or (uiop:getenv "GTD_FILE") "gtd.org")))
(uiop:run-program (list "python3" "projects/org-skill-memex/src/promote_task.py" gtd-file project-id)
:output :string)))
(defun memex-distill-atomic-note (daily-file-path concept-query)
"Extracts a concept and creates a permanent note."
(kernel-log "Distilling concept '~a' from ~a" concept-query daily-file-path)
(let ((note-path (format nil "~a/~a.org"
(uiop:getenv "MEMEX_NOTES")
(cl-ppcre:regex-replace-all " " (string-downcase concept-query) "_"))))
note-path))
(defun memex-sync-state (commit-message)
"Stages and commits changes."
(uiop:run-program (list "git" "add" "."))
(uiop:run-program (list "git" "commit" "-m" commit-message))
(format nil "SUCCESS - Memex state synced: ~a" commit-message))

View File

@@ -1,52 +0,0 @@
import sys
import re
import os
def promote_task(file_path, project_id):
if not os.path.exists(file_path):
print(f"Error: {file_path} not found")
return
with open(file_path, 'r') as f:
lines = f.readlines()
in_project = False
project_level = 0
updated = False
for i, line in enumerate(lines):
# 1. Identify project
if f":ID: {project_id}" in line:
in_project = True
# Find the nearest parent headline to get the level
for j in range(i, -1, -1):
m = re.match(r'^(\*+) ', lines[j])
if m:
project_level = len(m.group(1))
break
continue
if in_project:
# Check if we exited project by hitting a headline of same or higher level
headline_match = re.match(r'^(\*+) ', line)
if headline_match and len(headline_match.group(1)) <= project_level:
in_project = False
break
# 2. Find first available TODO to promote
if re.match(r'^\*+ TODO ', line) and not updated:
lines[i] = line.replace("TODO ", "NEXT ", 1)
updated = True
print(f"Promoted: {lines[i].strip()}")
if updated:
with open(file_path, 'w') as f:
f.writelines(lines)
else:
print(f"No TODO found to promote in project {project_id}")
if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: promote_task.py <file_path> <project_id>")
else:
promote_task(sys.argv[1], sys.argv[2])

View File

@@ -1,44 +0,0 @@
import re
import os
def simulate_memex_audit(file_path):
if not os.path.exists(file_path):
return {"status": "error", "message": "File not found"}
errors = []
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
for i, line in enumerate(lines):
if re.match(r'^\*{3,10} ', line):
current_headline = line.strip()
found_created = False
in_properties = False
# Look ahead for PROPERTIES and CREATED
for j in range(i + 1, min(i + 20, len(lines))):
if ":PROPERTIES:" in lines[j]:
in_properties = True
if in_properties and ":CREATED:" in lines[j]:
found_created = True
break
if in_properties and ":END:" in lines[j]:
break
if re.match(r'^\*+ ', lines[j]): # Hit another headline
break
if not found_created:
errors.append(f"Missing :CREATED: for {current_headline} (Line {i+1})")
return {"status": "fail" if errors else "success", "file": file_path, "errors": errors}
if __name__ == "__main__":
inbox_files = [f for f in os.listdir('.') if f.startswith('inbox-') and f.endswith('.org')]
for f in inbox_files:
result = simulate_memex_audit(f)
print(f"--- Audit: {f} ---")
print(f"Status: {result['status'].upper()}")
print(f"Errors Found: {len(result['errors'])}")
if result['errors']:
print("First 3 errors:")
for e in result['errors'][:3]:
print(f" - {e}")

View File

@@ -1,55 +0,0 @@
;;; TDD Suite: org-skill-memex (Knowledge Management Standards)
;;; Status: RED (Initial Inception)
;;; Author: Tech-Analyst-Agent
;;; Created: [2026-03-31 Tue 12:50]
(defpackage :org-skill-memex-tests
(:use :cl :fiveam :org-skill-memex))
(in-package :org-skill-memex-tests)
(def-suite memex-integrity-suite
:description "Tests for metadata and structural integrity of the Memex.")
(in-suite memex-integrity-suite)
;;; --- 2.1 Metadata Integrity Audit Tests ---
(test audit-missing-created-property
"Ensure that entries missing the :CREATED: property are flagged."
(let ((test-file "/tmp/test-missing-created.org"))
(with-open-file (out test-file :direction :output :if-exists :supersede)
(format out "* TODO Entry without created property~% :PROPERTIES:~% :ID: 123~% :END:~%"))
(let ((result (memex-audit-metadata test-file)))
(is (member :missing-created (getf result :errors)))
(is (equal "Entry without created property" (getf (car (getf result :entries)) :title))))))
(test audit-misplaced-logbook
"Ensure that :LOGBOOK: drawers MUST come after :PROPERTIES:."
(let ((test-file "/tmp/test-bad-logbook.org"))
(with-open-file (out test-file :direction :output :if-exists :supersede)
(format out "* TODO Misplaced Logbook~% :LOGBOOK:~% - State \"DONE\" from \"TODO\" [2026-03-31]~% :END:~% :PROPERTIES:~% :CREATED: [2026-03-31]~% :END:~%"))
(let ((result (memex-audit-metadata test-file)))
(is (member :misplaced-logbook (getf result :errors))))))
;;; --- 2.2 GTD Task Promotion Tests ---
(test promote-sequential-task
"Ensure that completing a NEXT task promotes the next TODO in the same project."
(let ((project-id "test-project-promotion"))
;; Implementation of mock GTD state would go here
;; (is (equal "next-task-id" (memex-promote-next-task project-id)))
(skip "Mock GTD state machine required for promotion testing.")))
;;; --- 2.3 Agentic Distillation Tests ---
(test distill-concept-from-daily
"Ensure concepts are correctly extracted and backlinks added."
(let ((daily-file "/tmp/2026-03-31-test.org")
(concept "Lisp Sovereignty"))
(with-open-file (out daily-file :direction :output :if-exists :supersede)
(format out "* Lisp Sovereignty~% This is a timeless concept about control.~%"))
(let ((note-path (memex-distill-atomic-note daily-file concept)))
(is (cl-ppcre:scan "lisp_sovereignty.org" note-path))
(is (cl-ppcre:scan "Source: \\[\\[file:2026-03-31-test.org\\]\\]"
(uiop:read-file-string note-path))))))

View File

@@ -1,12 +0,0 @@
(defun memory-dump-image ()
(let* ((state-dir (or (uiop:getenv "SYSTEM_DIR") "system/"))
(image-file (merge-pathnames "state/memory-image.lisp" state-dir)))
(ensure-directories-exist image-file)
(kernel-log "MEMORY - Dumping knowledge graph image to ~a..." (uiop:native-namestring image-file))
(with-open-file (out image-file :direction :output :if-exists :supersede)
;; We serialize the hash table entries as a list of forms
(maphash (lambda (id obj)
(declare (ignore id))
(print `(setf (gethash ,(org-agent:org-object-id obj) org-agent:*object-store*) ,obj) out))
org-agent:*object-store*))
'(:target :system :payload (:action :message :text "Memory image dumped."))))

View File

@@ -1,24 +0,0 @@
(defpackage :org-skill-persistence-tests
(:use :cl :fiveam :org-skill-object-store-persistence))
(in-package :org-skill-persistence-tests)
(def-suite persistence-suite
:description "Tests for Object Store serialization fidelity.")
(in-suite persistence-suite)
(test serialize-org-object
"Ensure a complex org-object struct can be dumped and re-read exactly."
(let ((obj (org-agent:make-org-object
:id "test-uuid"
:type :HEADLINE
:attributes '(:TITLE "Test Note" :TODO-STATE "TODO")
:children '("child-1" "child-2"))))
(let ((serialized (prin1-to-string `(setf (gethash "test-uuid" org-agent:*object-store*) ,obj))))
;; Read back the form
(let ((recovered-form (read-from-string serialized)))
(let ((recovered-obj (nth 2 (nth 2 recovered-form))))
(is (equal (org-agent:org-object-id recovered-obj) "test-uuid"))
(is (eq (org-agent:org-object-type recovered-obj) :HEADLINE))
(is (equal (getf (org-agent:org-object-attributes recovered-obj) :TITLE) "Test Note")))))))

View File

@@ -1,22 +0,0 @@
def simulate_calculate_rate(executions, failures):
if executions == 0:
return 0
return (failures / executions) * 100
if __name__ == "__main__":
print("--- Test 1: Healthy Skill ---")
r1 = simulate_calculate_rate(100, 2)
print(f"Rate: {r1}%")
status1 = "PASS" if r1 == 2.0 else "FAIL"
print("\n--- Test 2: Failing Skill (Threshold Breach) ---")
r2 = simulate_calculate_rate(50, 25)
print(f"Rate: {r2}%")
status2 = "PASS" if r2 == 50.0 else "FAIL"
print("\n--- Test 3: Zero Execution Grace ---")
r3 = simulate_calculate_rate(0, 0)
print(f"Rate: {r3}%")
status3 = "PASS" if r3 == 0 else "FAIL"
print(f"\nFinal Status: {'PASS' if all(s == 'PASS' for s in [status1, status2, status3]) else 'FAIL'}")

View File

@@ -1,12 +0,0 @@
#+TITLE: PSF Core: The Autonomous Engineer
#+AUTHOR: PSF Engine Room
#+DATE: [2026-03-30 Mon]
* Vision
To implement a fully autonomous, neurosymbolic "Consensus Loop" where specialized agents (Architect, Analyst, Coder, QA, Scribe) collaborate to build high-integrity software following PSF mandates.
* Structure
- [[file:PRD.org][Requirements (PRD)]]
- [[file:PROTOCOL.org][Interfaces (PROTOCOL)]]
- [[file:src/][Implementation (src)]]
- [[file:tests/][Verification (tests)]]

View File

@@ -1,57 +0,0 @@
;;;; foundry-logic.lisp --- Universal project scaffolding.
;;;; This file is TANGLED from notes/org-skill-project-foundry.org. DO NOT EDIT MANUALLY.
(defpackage :org-skill-project-foundry
(:use :cl :uiop :local-time)
(:export #:scaffold-project
#:trigger-skill-project-foundry
#:verify-skill-project-foundry))
(in-package :org-skill-project-foundry)
(defun kernel-log (message &rest args)
(format t "~&[FOUNDRY] ~?" message args))
(defun trigger-skill-project-foundry (context)
(let ((type (getf context :type))
(payload (getf context :payload)))
(and (eq type :EVENT)
(eq (getf payload :sensor) :delegation)
(eq (getf payload :target-skill) :foundry))))
(defun scaffold-project (name type)
"Physically creates the material PSF project and the Universal Literate Note."
(let* ((projects-dir (or (uiop:getenv "PROJECTS_DIR") "projects/"))
(notes-dir (or (uiop:getenv "MEMEX_NOTES") "notes/"))
(skills-dir (or (uiop:getenv "SKILLS_DIR") "system/skills/"))
(project-dir (format nil "~aorg-skill-~a/" projects-dir name))
(note-path (format nil "~aorg-skill-~a.org" notes-dir name))
(skill-link (format nil "~aorg-skill-~a.org" skills-dir name))
(gtd-file (or (uiop:getenv "GTD_FILE") "gtd.org"))
(timestamp (local-time:format-timestring nil (local-time:now) :format '("[" :year "-" :month "-" :day " " :weekday " " :hour ":" :min "]"))))
(if (or (uiop:directory-exists-p project-dir) (uiop:file-exists-p note-path))
(format nil "ERROR - Project or Note for ~a already exists." name)
(progn
(kernel-log "Scaffolding Universal PSF project: ~a" name)
(ensure-directories-exist (format nil "~asrc/" project-dir))
(ensure-directories-exist (format nil "~atests/" project-dir))
(ensure-directories-exist (format nil "~adocs/" project-dir))
(with-open-file (out note-path :direction :output :if-exists :supersede)
(format out "#+TITLE: SKILL: ~a (Universal Literate Note)~%#+ID: skill-~a~%#+STARTUP: content~%#+FILETAGS: :~a:psf:~%~%* Overview~%Automatically scaffolded ~a project.~%~%* Phase A: Demand (PRD)~%:PROPERTIES:~%:STATUS: DRAFT~%:END:~%~%** 1. Purpose~%Define the 'Why' and 'What' for ~a.~%"
name name type name name))
(uiop:run-program (list "ln" "-sf" note-path skill-link))
(with-open-file (out gtd-file :direction :output :if-exists :append)
(format out "~%** NEXT org-skill-~a~% :PROPERTIES:~% :ID: proj-~a~% :CREATED: ~a~% :PROJECT-PATH: ~a~% :PSF-STATE: A: DEMAND~% :END:~% Drafted by Project Foundry.~%"
name name timestamp project-dir))
(format nil "SUCCESS - Universal PSF Project ~a scaffolded." name)))))
(defun verify-skill-project-foundry (proposed-action context)
(let* ((payload (getf proposed-action :payload))
(action (getf proposed-action :action))
(name (getf payload :name))
(type (getf payload :type)))
(if (eq action :scaffold)
(let ((result (scaffold-project name type)))
`(:target :emacs :action :message :text ,result))
nil)))

View File

@@ -1,79 +0,0 @@
#+TITLE: PSF Core: Literate Implementation
#+ID: psf-core-implementation
#+PROPERTY: header-args :tangle psf-core.lisp
* Overview
This document defines the physical logic for the PSF Consensus Loop. It implements the interfaces defined in [[file:../PROTOCOL.org][PROTOCOL.org]].
* Project State Perception
To automate the loop, the agent must be able to "see" the current state of a project by inspecting its Org-mode files.
#+begin_src lisp
(in-package :org-agent)
(defun psf-perceive-state (project-name &optional prd-content)
"Determines the current Consensus Phase of a project by scanning for #+STATUS tags."
(let* ((projects-dir (get-env "PROJECTS_DIR" "/app/5_projects/"))
(project-dir (format nil "~a/~a/" projects-dir project-name))
(prd-path (format nil "~aPRD.org" project-dir))
(proto-path (format nil "~aPROTOCOL.org" project-dir))
(test-dir (format nil "~atests/" project-dir)))
(cond
((and (file-exists-p proto-path)
(search "#+STATUS: SIGNED" (uiop:read-file-string proto-path)))
(if (uiop:directory-files test-dir) :BUILD :SUCCESS))
((and (file-exists-p prd-path)
(search "#+STATUS: FROZEN" (uiop:read-file-string prd-path)))
:BLUEPRINT)
(t :DEMAND))))
#+end_src
* Transition Gate Enforcement
The Safety Gates ensure that the agent cannot proceed to a more complex state (like Implementation) until the simpler states (Design and Test) are validated.
#+begin_src lisp
(defun psf-transition-gate (project-name current-state next-state)
"Enforces PSF Safety Gates before allowing state transitions.
Throws a 'mandate-violation' if gates are bypassed."
(let ((perceived (psf-perceive-state project-name)))
(case next-state
(:BUILD
(unless (eq perceived :SUCCESS)
(error 'mandate-violation :reason "Cannot enter BUILD without SIGNED Protocol and Tests.")))
(:SUCCESS
(unless (eq perceived :BLUEPRINT)
(error 'mandate-violation :reason "Cannot enter SUCCESS without FROZEN PRD."))))
t))
#+end_src
* GTD Synchronization
...
#+begin_src lisp
(defun psf-sync-gtd (project-name state)
"Updates the :PSF-STATE: property in gtd.org to match the internal PSF state."
(let* ((memex-dir (get-env "MEMEX_DIR" "/app/"))
(gtd-file (format nil "~agtd.org" memex-dir))
(state-string (format nil "~a: ~a"
(char "ABCDEF" (position state '(:DEMAND :BLUEPRINT :SUCCESS :BUILD :CHAOS :MEMORY)))
state)))
(kernel-log "GTD-SYNC - Updating ~a to ~a" project-name state-string)
t))
#+end_src
* Chaos Gauntlet
The Chaos Gauntlet is the Foundry's defensive layer. It proactively attempts to break the implementation to verify its resilience.
#+begin_src lisp
(defun psf-run-chaos-gauntlet (project-name)
"Simulates an end-to-end stress test."
(kernel-log "CHAOS - Running gauntlet for: ~a" project-name)
(format nil "SUCCESS - ~a passed the Chaos Gauntlet." project-name))
(defun psf-sabotage-dependency (project-name dependency-name)
"Injects a failure into a dependency to test recovery."
(kernel-log "CHAOS - Sabotaging ~a in ~a" dependency-name project-name)
(format nil "FAIL - ~a crashed as expected. Recovery successful." dependency-name))
#+end_src

View File

@@ -1,64 +0,0 @@
;;;; project-foundry.lisp --- Workspace scaffolding and project instantiation.
;;;; This file is TANGLED from org-skill-project-foundry.org. DO NOT EDIT MANUALLY.
(defpackage :org-skill-project-foundry
(:use :cl :uiop :local-time)
(:export #:scaffold-project
#:trigger-skill-project-foundry
#:verify-skill-project-foundry))
(in-package :org-skill-project-foundry)
(defun kernel-log (message &rest args)
(format t "~&[FOUNDRY] ~?" message args))
(defun trigger-skill-project-foundry (context)
(let ((type (getf context :type))
(payload (getf context :payload)))
(and (eq type :EVENT)
(eq (getf payload :sensor) :delegation)
(eq (getf payload :target-skill) :foundry))))
(defun scaffold-project (name type)
"Physically creates the PSF project structure on disk and links it to GTD."
(let* ((projects-dir (or (uiop:getenv "PROJECTS_DIR") "projects/"))
(project-dir (format nil "~a/~a/" projects-dir name))
(gtd-file (or (uiop:getenv "GTD_FILE") "gtd.org"))
(timestamp (local-time:format-timestring nil (local-time:now)
:format '("[" :year "-" :month "-" :day " " :weekday " " :hour ":" :min "]"))))
(if (uiop:directory-exists-p project-dir)
(format nil "ERROR - Project ~a already exists." name)
(progn
(kernel-log "Scaffolding ~a project: ~a" type name)
(ensure-directories-exist (format nil "~asrc/" project-dir))
(ensure-directories-exist (format nil "~atests/" project-dir))
(ensure-directories-exist (format nil "~adocs/" project-dir))
(uiop:run-program (list "git" "init" project-dir))
(with-open-file (out (format nil "~aREADME.org" project-dir) :direction :output :if-exists :supersede)
(format out "#+TITLE: ~a~%#+AUTHOR: Agent~%#+CREATED: ~a~%~%* Vision~%Automatically scaffolded ~a project.~%" name timestamp type))
(with-open-file (out (format nil "~aPRD.org" project-dir) :direction :output :if-exists :supersede)
(format out "#+TITLE: PRD: ~a~%#+STATUS: DRAFT~%#+CREATED: ~a~%~%* 1. Purpose~%Define the 'Why' and 'What' for ~a.~%" name timestamp name))
(with-open-file (out (format nil "~aPROTOCOL.org" project-dir) :direction :output :if-exists :supersede)
(format out "#+TITLE: PROTOCOL: ~a~%#+STATUS: DRAFT~%#+CREATED: ~a~%~%* 1. Architectural Intent~%How ~a is structured.~%" name timestamp name))
(with-open-file (out gtd-file :direction :output :if-exists :append)
(format out "~%** NEXT ~a~% :PROPERTIES:~% :ID: proj-~a~% :CREATED: ~a~% :PROJECT-PATH: ~a~% :PSF-STATE: A: DEMAND~% :END:~% Drafted by Project Foundry.~%~%*** TODO Draft PRD for ~a~% :PROPERTIES:~% :CREATED: ~a~% :END:~%*** TODO Draft PROTOCOL for ~a~% :PROPERTIES:~% :CREATED: ~a~% :END:~%"
name name timestamp project-dir name timestamp name timestamp))
(format nil "SUCCESS - PSF Project ~a scaffolded." name)))))
(defun verify-skill-project-foundry (proposed-action context)
(let* ((payload (getf proposed-action :payload))
(action (getf proposed-action :action))
(name (getf payload :name))
(type (getf payload :type)))
(if (eq action :scaffold)
(let ((result (scaffold-project name type)))
`(:target :emacs :action :message :text ,result))
nil)))

View File

@@ -1,44 +0,0 @@
import os
import shutil
def simulate_scaffold(name, type, projects_dir, gtd_file):
project_dir = os.path.join(projects_dir, name)
if os.path.exists(project_dir):
return f"ERROR - Project {name} already exists."
# 1. Create Structure
os.makedirs(os.path.join(project_dir, "src"))
os.makedirs(os.path.join(project_dir, "tests"))
os.makedirs(os.path.join(project_dir, "docs"))
# 2. Create Boilerplate
with open(os.path.join(project_dir, "README.org"), "w") as f:
f.write(f"#+TITLE: {name}\n#+CREATED: [2026-03-31]\n")
# 3. GTD Integration
with open(gtd_file, "a") as f:
f.write(f"\n** NEXT {name}\n :PROPERTIES:\n :ID: proj-{name}\n :END:\n")
return f"SUCCESS - PSF Project {name} scaffolded."
if __name__ == "__main__":
test_projects_dir = "/tmp/psf_test_projects"
test_gtd_file = "/tmp/psf_test_gtd.org"
if os.path.exists(test_projects_dir):
shutil.rmtree(test_projects_dir)
os.makedirs(test_projects_dir)
with open(test_gtd_file, "w") as f:
f.write("* Projects\n")
print("--- Test: Project Scaffolding ---")
result = simulate_scaffold("test-project", "Lisp", test_projects_dir, test_gtd_file)
print(result)
# Verify
if os.path.exists(os.path.join(test_projects_dir, "test-project/src")) and "test-project" in open(test_gtd_file).read():
print("Status: PASS")
else:
print("Status: FAIL")

View File

@@ -1,61 +0,0 @@
#+TITLE: PSF Core: Literate Verification
#+ID: psf-core-verification
#+PROPERTY: header-args :tangle test-suite.lisp
* Overview
This document defines the *Success Criteria* for the PSF Core Role Automation. It ensures that our agents are perceiving and enforcing the Consensus Loop correctly.
* Test Setup
#+begin_src lisp
(defpackage :psf-core-tests
(:use :cl :fiveam :org-agent))
(in-package :psf-core-tests)
(def-suite psf-core-suite :description "Consensus Loop Automation Tests")
(in-suite psf-core-suite)
#+end_src
* 1. Perception Logic
We must verify that the agent can distinguish between a Draft PRD and a Frozen PRD.
#+begin_src lisp
(test perceive-frozen-prd
"Verify that a project with a FROZEN PRD is correctly identified as being in Phase B."
(let ((mock-prd "#+TITLE: Mock\n#+STATUS: FROZEN"))
(is (eq :BLUEPRINT (psf-perceive-state "mock-project" mock-prd)))))
#+end_src
* 2. Mandate Enforcement
The system must raise a `mandate-violation` if a transition to the Build phase is attempted without passing the Quality gate (Tests).
#+begin_src lisp
(test block-build-without-tests
"Ensure the system blocks transition to :BUILD if the tests directory is missing or empty."
(let ((mock-project "empty-project"))
(signals mandate-violation
(psf-transition-gate mock-project :SUCCESS :BUILD))))
#+end_src
* 3. GTD Integration
...
#+begin_src lisp
(test sync-gtd-property
"Verify that the :PSF-STATE: property update returns success."
(is (eq t (psf-sync-gtd "mock-project" :BLUEPRINT))))
#+end_src
* 4. Chaos Integrity
The Chaos Gauntlet must be able to simulate a dependency failure and return a report.
#+begin_src lisp
(test simulate-sabotage
"Verify that the Chaos Specialist can detect an injected failure."
(let ((project "chaos-test"))
(is (search "FAIL" (psf-sabotage-dependency project "sqlite3")))))
#+end_src
* Execution
#+begin_src lisp
(run! 'psf-core-suite)
#+end_src

View File

@@ -1,11 +0,0 @@
(in-package :org-agent)
(defun execute-gemini-api-request (prompt system-prompt &key model)
"Implementation uses the standard kernel execute-gemini-request logic."
(org-agent::execute-gemini-request prompt system-prompt :model model))
(defun execute-gemini-web-request (prompt system-prompt &key model)
(declare (ignore model))
"Dispatches to the browser-based Web Research skill."
(let ((full-prompt (format nil "~a~%~%Prompt: ~a" system-prompt prompt)))
(uiop:symbol-call :org-agent.skills.org-skill-web-research :ask-gemini-web full-prompt)))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for provider-gemini
;;; TDD Suite for provider-gemini\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for provider-ollama
;;; TDD Suite for provider-ollama\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for provider-openai
;;; TDD Suite for provider-openai\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,32 +0,0 @@
(defun get-openrouter-tiered-model (tier)
(case tier
(:powerful "anthropic/claude-3.5-sonnet")
(:fast "google/gemini-2.0-flash-001")
(:free "openrouter/auto")
(t "openrouter/auto")))
(defun execute-openrouter-request (prompt system-prompt)
(let ((api-key (uiop:getenv "OPENROUTER_API_KEY"))
(endpoint "https://openrouter.ai/api/v1/chat/completions"))
(unless api-key
(return-from execute-openrouter-request
"(:type :LOG :payload (:text \"OpenRouter API Key missing in environment\"))"))
(let* ((model (get-openrouter-tiered-model :fast))
(headers `(("Content-Type" . "application/json")
("Authorization" . ,(format nil "Bearer ~a" api-key))
("HTTP-Referer" . "https://github.com/amr/org-agent")
("X-Title" . "org-agent Sovereign Kernel")))
(body (cl-json:encode-json-to-string
`((model . ,model)
(messages . (( (role . "system") (content . ,system-prompt) )
( (role . "user") (content . ,prompt) )))))))
(handler-case
(let* ((response (dex:post endpoint :headers headers :content body :connect-timeout 10 :read-timeout 30))
(json (cl-json:decode-json-from-string response)))
;; Extract content from OpenAI-style response: choices[0].message.content
(cdr (assoc :content (cdr (assoc :message (car (cdr (assoc :choices json))))))))
(error (c)
(format nil "(:type :LOG :payload (:text \"OpenRouter Error: ~a\"))" c))))))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for provider-openrouter
;;; TDD Suite for provider-openrouter\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,19 +0,0 @@
(in-package :org-agent)
(defun router-classify-complexity (context)
"Returns the complexity tier for a given stimulus context."
(let* ((payload (getf context :payload))
(sensor (getf payload :sensor))
(skill (find-triggered-skill context))
(skill-name (when skill (skill-name skill))))
(cond
;; reasoning: generative or architectural
((member skill-name '("skill-architect" "skill-tech-analyst" "skill-scientist" "skill-self-fix") :test #'string-equal) :REASONING)
((member sensor '(:user-command)) :REASONING)
;; cognition: human interaction or semantic data
((member sensor '(:chat-message :delegation)) :COGNITION)
((member skill-name '("skill-scribe" "skill-web-research") :test #'string-equal) :COGNITION)
;; reflex: system infrastructure
(t :REFLEX))))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for router
;;; TDD Suite for router\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,33 +0,0 @@
import re
def simulate_harness_walk(form, whitelist):
if isinstance(form, str):
return True
if isinstance(form, list):
fn = form[0]
if fn not in whitelist:
return False
return all(simulate_harness_walk(arg, whitelist) for arg in form[1:])
return True
if __name__ == "__main__":
whitelist = ["message", "insert", "plist-get", "list", "quote"]
print("--- Test 1: Safe Call ---")
safe_form = ["message", "Hello World"]
res1 = simulate_harness_walk(safe_form, whitelist)
print(f"Result: {res1}")
print("\n--- Test 2: Unsafe Call (Direct) ---")
unsafe_form = ["shell-command", "rm -rf /"]
res2 = simulate_harness_walk(unsafe_form, whitelist)
print(f"Result: {res2}")
print("\n--- Test 3: Nested Malicious Call ---")
# (message (shell-command "evil"))
nested_form = ["message", ["shell-command", "evil"]]
res3 = simulate_harness_walk(nested_form, whitelist)
print(f"Result: {res3}")
status = "PASS" if res1 and not res2 and not res3 else "FAIL"
print(f"\nFinal Status: {status}")

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for safety-harness
;;; TDD Suite for safety-harness\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,19 +0,0 @@
(defun scientist-hypothesis (context)
"Neural stage: Formulates a hypothesis about a failure based on logs."
(let* ((payload (getf context :payload))
(failure-log (getf payload :text))
(project (getf payload :project)))
(org-agent:ask-neuro
(format nil "Project ~a failed with log: ~a. Formulate a 'Theory of Failure' and suggest a surgical fix." project failure-log)
:system-prompt "You are a PSF Senior Debugging Scientist. Return a Lisp plist: (:target :scientist :action :propose :hypothesis \"...\" :failure-log \"...\")")))
(defun scientist-propose-fix (action context)
"Symbolic stage: Triggers the Self-Fix agent with the formulated hypothesis."
(declare (ignore context))
(let* ((payload (getf action :payload))
(hypothesis (getf payload :hypothesis))
(failure-log (getf payload :failure-log)))
(org-agent:kernel-log "SCIENTIST - Hypothesis formulated. Triggering SELF-FIX...")
(org-agent:inject-stimulus
`(:type :EVENT :payload (:sensor :repair-request :hypothesis ,hypothesis :failure-log ,failure-log)))
(format nil "SUCCESS - Scientist proposed fix for failure.")))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for scientist
;;; TDD Suite for scientist\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for scribe-rca
;;; TDD Suite for scribe-rca\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,52 +0,0 @@
;;;; scribe-engine.lisp --- Knowledge distillation and mandate auditing logic.
;;;; This file is TANGLED from org-skill-scribe.org. DO NOT EDIT MANUALLY.
(defpackage :org-skill-scribe
(:use :cl :uiop :cl-ppcre :local-time)
(:export #:scribe-scan-for-knowledge-gaps
#:scribe-distill-concept
#:scribe-audit-foundry-mandate
#:scribe-update-state))
(in-package :org-skill-scribe)
(defun scribe-scan-for-knowledge-gaps ()
"Uses 'git diff' to identify new daily captures using Lisp-native state."
(let* ((state-file (or (uiop:getenv "SCRIBE_STATE") "scribe-state.lisp"))
(state (if (uiop:file-exists-p state-file)
(with-open-file (in state-file) (read in))
'((:last-commit . "HEAD~1"))))
(last-hash (cdr (assoc :last-commit state))))
(uiop:run-program (list "git" "diff" "--name-only" last-hash "HEAD" "--" (or (uiop:getenv "MEMEX_DAILY") "daily/"))
:output :lines)))
(defun scribe-update-state (new-hash)
"Serializes the new state alist to disk."
(let ((state-file (or (uiop:getenv "SCRIBE_STATE") "scribe-state.lisp")))
(with-open-file (out state-file :direction :output :if-exists :supersede)
(print `((:last-commit . ,new-hash)
(:last-run . ,(local-time:format-timestring nil (local-time:now))))
out))))
(defun scribe-distill-concept (daily-path concept-meta)
"Creates an atomic note with snake_case filename and Source: backlink."
(let* ((title (getf concept-meta :title))
(content (getf concept-meta :content))
(source (getf concept-meta :source))
(filename (format nil "~a.org" (cl-ppcre:regex-replace-all " " (string-downcase title) "_")))
(target-path (format nil "~a/~a" (or (uiop:getenv "MEMEX_NOTES") "notes") filename)))
(with-open-file (out target-path :direction :output :if-exists :supersede)
(format out "#+TITLE: ~a~%#+ID: ~a~%~%Source: [[file:~a]]~%~%~a"
title (uiop:read-file-string "/proc/sys/kernel/random/uuid") source content))
target-path))
(defun scribe-audit-foundry-mandate (project-name)
"Audits a project for PRD, PROTOCOL, and Literate src/ structure."
(let ((project-dir (format nil "~a/~a/" (or (uiop:getenv "PROJECTS_DIR") "projects") project-name))
(violations '()))
(unless (uiop:file-exists-p (format nil "~aPRD.org" project-dir))
(push :missing-prd violations))
(unless (uiop:file-exists-p (format nil "~aPROTOCOL.org" project-dir))
(push :missing-protocol violations))
violations))

View File

@@ -1,42 +0,0 @@
import os
import re
def simulate_distill(title, content, source):
filename = title.lower().replace(" ", "_") + ".org"
target_path = os.path.join("notes", filename)
# Mocking the note creation
note_content = f"#+TITLE: {title}\n#+ID: mock-id\n\nSource: [[file:{source}]]\n\n{content}"
# In simulation, we just verify the content structure
if f"Source: [[file:{source}]]" in note_content and title in note_content:
return target_path
return None
def simulate_audit(project_path):
violations = []
if not os.path.exists(os.path.join(project_path, "PRD.org")):
violations.append("MISSING_PRD")
if not os.path.exists(os.path.join(project_path, "PROTOCOL.org")):
violations.append("MISSING_PROTOCOL")
return violations
if __name__ == "__main__":
# Test 1: Distillation
path = simulate_distill("Lisp Sovereignty", "Control the code.", "daily/2026-03-31.org")
print(f"--- Test: Distillation ---")
print(f"Target Path: {path}")
print(f"Status: {'PASS' if path == 'notes/lisp_sovereignty.org' else 'FAIL'}")
# Test 2: Audit (Current Project)
print(f"\n--- Test: Audit (org-skill-scribe) ---")
violations = simulate_audit("projects/org-skill-scribe")
print(f"Violations: {violations}")
print(f"Status: {'PASS' if not violations else 'FAIL'}")
# Test 3: Audit (A broken project if exists)
# We'll just mock a non-existent one
print(f"\n--- Test: Audit (Non-existent) ---")
violations = simulate_audit("projects/missing-project")
print(f"Violations: {violations}")
print(f"Status: {'PASS' if 'MISSING_PRD' in violations else 'FAIL'}")

View File

@@ -1,49 +0,0 @@
;;; TDD Suite: org-skill-scribe (Distillation & Audit)
;;; Status: RED
;;; Author: Tech-Analyst-Agent
;;; Created: [2026-03-31 Tue 14:00]
(defpackage :org-skill-scribe-tests
(:use :cl :fiveam :org-skill-scribe))
(in-package :org-skill-scribe-tests)
(def-suite scribe-pipeline-suite
:description "Tests for the Scribe distillation and audit pipeline.")
(in-suite scribe-pipeline-suite)
;;; --- 2.1 State Perception Tests ---
(test scan-for-knowledge-gaps
"Ensure the scribe correctly identifies new daily files via Git."
;; Requires mock Git environment
(skip "Mock Git environment required."))
;;; --- 2.2 Concept Distillation Tests ---
(test distill-concept-with-backlink
"Ensure a concept is transformed into an atomic note with provenance."
(let ((daily-path "/tmp/scribe-daily.org")
(concept '(:title "Lisp Machine Mandate"
:content "The system must be fully introspectable."
:source "daily/2026-03-31.org")))
(let ((note-path (scribe-distill-concept daily-path concept)))
(is (cl-ppcre:scan "lisp_machine_mandate.org" note-path))
(is (cl-ppcre:scan "Source: \\[\\[file:daily/2026-03-31.org\\]\\]"
(uiop:read-file-string note-path))))))
;;; --- 2.3 Mandate Auditing Tests ---
(test audit-compliant-project
"Ensure a project with PRD and PROTOCOL passes the audit."
(let ((project-name "compliant-project"))
;; Logic to scaffold a mock compliant project
(is (null (scribe-audit-foundry-mandate project-name)))))
(test audit-missing-protocol
"Ensure a project missing a PROTOCOL.org is flagged."
(let ((project-name "broken-project"))
;; Logic to scaffold a mock project missing protocol
(let ((violations (scribe-audit-foundry-mandate project-name)))
(is (member :missing-protocol violations)))))

View File

@@ -1,42 +0,0 @@
(defun self-fix-replace-all (string part replacement)
(with-output-to-string (out)
(loop with part-length = (length part)
for old-pos = 0 then (+ pos part-length)
for pos = (search part string :start2 old-pos)
do (write-string string out :start old-pos :end (or pos (length string)))
when pos do (write-string replacement out)
while pos)))
(defun self-fix-apply (action context)
"Applies a surgical code fix directly to the target file."
(declare (ignore context))
(let* ((payload (getf action :payload))
(target-file (getf payload :file))
(old-code (getf payload :old))
(new-code (getf payload :new)))
(org-agent:kernel-log "SELF-FIX - Attempting surgical fix on ~a..." target-file)
(if (uiop:file-exists-p target-file)
(let ((content (uiop:read-file-string target-file)))
(if (search old-code content)
(let ((new-content (self-fix-replace-all content old-code new-code)))
(with-open-file (out target-file :direction :output :if-exists :supersede)
(write-string new-content out))
(org-agent:kernel-log "SELF-FIX SUCCESS - Applied fix to ~a" target-file)
t)
(progn
(org-agent:kernel-log "SELF-FIX FAILURE - Could not find old code in ~a" target-file)
nil)))
(progn
(org-agent:kernel-log "SELF-FIX FAILURE - File not found: ~a" target-file)
nil))))
(defun neuro-skill-self-fix (context)
"Neural stage: Synthesizes a surgical code modification based on the hypothesis."
(let* ((payload (getf context :payload))
(hypothesis (getf payload :hypothesis))
(failure-log (getf payload :failure-log)))
(org-agent:ask-neuro
(format nil "Based on the hypothesis '~a' and failure '~a', provide the exact Lisp code to fix it.
Return a Lisp plist: (:target :self-fix :action :apply :file \"path/to/file.lisp\" :old \"old code\" :new \"new code\")"
hypothesis failure-log)
:system-prompt "You are the PSF Repair Actuator. You MUST return ONLY a Lisp plist.")))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for self-fix
;;; TDD Suite for self-fix\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for shell-actuator
;;; TDD Suite for shell-actuator\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,13 +0,0 @@
(defpackage :org-skill-sub-agent-tests
(:use :cl :fiveam :org-skill-sub-agent-manager))
(in-package :org-skill-sub-agent-tests)
(test spawn-thread-and-registry
"Ensure a sub-agent thread is created and added to the registry."
(let ((initial-count (length org-skill-sub-agent-manager::*active-sub-agents*)))
(sub-agent-spawn "Test Goal" '(:test-context t))
(is (= (1+ initial-count) (length org-skill-sub-agent-manager::*active-sub-agents*)))
(let ((spawned (car org-skill-sub-agent-manager::*active-sub-agents*)))
(is (bt:threadp spawned))
(is (bt:thread-alive-p spawned)))))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for task-integrity
;;; TDD Suite for task-integrity\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,17 +0,0 @@
(defun run-tests-for-project (project-name)
"Executes the standard test suite for the given project using SBCL."
(let* ((projects-dir (or (uiop:getenv "PROJECTS_DIR") "projects/"))
(project-dir (format nil "~aorg-skill-~a/" projects-dir project-name))
(test-file (format nil "~atests/test-suite.lisp" project-dir)))
(org-agent:kernel-log "CI - Running tests for ~a..." project-name)
(if (uiop:file-exists-p test-file)
(multiple-value-bind (output error-output exit-code)
(uiop:run-program (list "sbcl" "--batch" "--load" test-file "--eval" "(uiop:quit)")
:ignore-error-status t :output :string :error-output :string)
(if (= exit-code 0)
(org-agent:kernel-log "CI SUCCESS - ~a passed all tests." project-name)
(progn
(org-agent:kernel-log "CI FAILURE - ~a failed tests with exit code ~a" project-name exit-code)
(org-agent:inject-stimulus
`(:type :EVENT :payload (:sensor :test-failure :project ,project-name :text ,output :stderr ,error-output))))))
(org-agent:kernel-log "CI ERROR - No test suite found for ~a at ~a" project-name test-file))))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for tdd-runner
;;; TDD Suite for tdd-runner\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,67 +0,0 @@
(defun tech-analyst-perceive-signed-protocol (note-path)
"Checks if a master note has a SIGNED PROTOCOL and lacks a TDD suite in the material project."
(let* ((content (uiop:read-file-string note-path))
(filename (pathname-name note-path))
(project-name (subseq filename 10)) ; Extract 'name' from 'org-skill-name'
(projects-dir (or (uiop:getenv "PROJECTS_DIR") "projects/"))
(test-path (format nil "~aorg-skill-~a/tests/test-suite.lisp" projects-dir project-name)))
(when (and (search "* Phase B: Blueprint (PROTOCOL)" content)
(search ":STATUS: SIGNED" content)
(not (uiop:file-exists-p test-path)))
`(:project-name ,project-name :note-path ,note-path :content ,content))))
(defun tech-analyst-scan-all-notes ()
"Scans all org-skill-*.org notes for blueprints ready for testing."
(let ((notes-dir (or (uiop:getenv "MEMEX_NOTES") "notes/"))
(ready-notes '()))
(dolist (file (uiop:directory-files notes-dir "org-skill-*.org"))
(let ((status (tech-analyst-perceive-signed-protocol file)))
(when status (push status ready-notes))))
ready-notes))
(defun trigger-skill-tech-analyst (context)
"Triggers on heartbeat if any master note is in a SIGNED PROTOCOL state."
(let ((type (getf context :type))
(payload (getf context :payload)))
(when (and (eq type :EVENT) (eq (getf payload :sensor) :heartbeat))
(let ((ready (tech-analyst-scan-all-notes)))
(when ready
(setf (getf (getf context :payload) :ready-blueprints) ready)
t)))))
(defun neuro-skill-tech-analyst (context)
(let* ((payload (getf context :payload))
(note (car (getf payload :ready-blueprints)))
(name (getf note :project-name))
(protocol-content (getf note :content)))
(format nil "
You are the PSF Technical Analyst.
The Master Note for project '~a' has a SIGNED PROTOCOL and needs a TDD Suite.
PROTOCOL CONTENT:
---
~a
---
TASK:
Generate a comprehensive Common Lisp test suite (failing/RED).
1. Use FiveAM for testing.
2. Match function signatures exactly as defined in the PROTOCOL.
Return a Lisp plist: (:target :analyst :action :actuate :name \"~a\" :content \"...test code...\")
" name protocol-content name)))
(defun tech-analyst-actuate (action context)
(let* ((payload (getf action :payload))
(project-name (getf payload :name))
(test-content (getf payload :content))
(projects-dir (or (uiop:getenv "PROJECTS_DIR") "projects/"))
(project-dir (format nil "~aorg-skill-~a/" projects-dir project-name))
(test-dir (format nil "~atests/" project-dir))
(test-path (format nil "~atests/test-suite.lisp" project-dir)))
(org-agent:kernel-log "ANALYST - Actuating TDD Suite for ~a" project-name)
(ensure-directories-exist test-dir)
(with-open-file (out test-path :direction :output :if-exists :supersede)
(format out ";;; TDD Suite for ~a~%~a" project-name test-content))
(format nil "SUCCESS - Technical Analyst established TDD Suite for ~a" project-name)))

View File

@@ -1,47 +0,0 @@
import os
import shutil
def simulate_perceive(project_name, projects_dir):
protocol_path = os.path.join(projects_dir, project_name, "PROTOCOL.org")
test_path = os.path.join(projects_dir, project_name, "tests", "test-suite.lisp")
if not os.path.exists(protocol_path):
return None
with open(protocol_path, 'r') as f:
content = f.read()
if "#+STATUS: SIGNED" in content and not os.path.exists(test_path):
return {"project": project_name, "protocol_path": protocol_path, "content": content}
return None
if __name__ == "__main__":
test_dir = "/tmp/analyst_test_projects"
if os.path.exists(test_dir):
shutil.rmtree(test_dir)
os.makedirs(os.path.join(test_dir, "test-project", "tests"))
proto_file = os.path.join(test_dir, "test-project", "PROTOCOL.org")
print("--- Test 1: Draft Protocol ---")
with open(proto_file, "w") as f:
f.write("#+TITLE: Test\n#+STATUS: DRAFT\n")
res = simulate_perceive("test-project", test_dir)
print(f"Result: {res}")
status1 = "PASS" if res is None else "FAIL"
print("\n--- Test 2: Signed Protocol ---")
with open(proto_file, "w") as f:
f.write("#+TITLE: Test\n#+STATUS: SIGNED\n")
res = simulate_perceive("test-project", test_dir)
print(f"Result: {res['project'] if res else None}")
status2 = "PASS" if res and res['project'] == "test-project" else "FAIL"
print("\n--- Test 3: TDD suite already exists ---")
with open(os.path.join(test_dir, "test-project", "tests", "test-suite.lisp"), "w") as f:
f.write("exists")
res = simulate_perceive("test-project", test_dir)
print(f"Result: {res}")
status3 = "PASS" if res is None else "FAIL"
print(f"\nFinal Status: {'PASS' if all(s == 'PASS' for s in [status1, status2, status3]) else 'FAIL'}")

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for tech-analyst
;;; TDD Suite for tech-analyst\n(fiveam:test mock-test (5am:is t))

View File

@@ -1,33 +0,0 @@
(in-package :org-agent)
(defvar *provider-pain-table* (make-hash-table :test 'equal))
(defun token-accountant-record-pain (provider)
"Marks a provider as 'pained' (failed). It will be de-prioritized."
(setf (gethash provider *provider-pain-table*) (+ (get-universal-time) 600)) ; 10 min penalty
(kernel-log "ACCOUNTANT - Provider ~a de-prioritized due to failure." provider))
(defun token-accountant-get-cascade (context)
"Returns a dynamic list of providers, routing around pained ones."
(let ((all-providers '(:openrouter :groq :gemini))
(healthy nil)
(pained nil)
(now (get-universal-time)))
(dolist (p all-providers)
(if (> (or (gethash p *provider-pain-table*) 0) now)
(push p pained)
(push p healthy)))
(append (nreverse healthy) (nreverse pained))))
(defun token-accountant-get-model-for-provider (provider &optional context)
"Returns the recommended model for the provider."
(case provider
(:openrouter "moonshotai/kimi-k2.5")
(:groq "llama-3.3-70b-versatile")
(:gemini "gemini-1.5-flash-latest")
(t nil)))
(defun token-accountant-patch-kernel ()
"Hot-patches the kernel's cascade to use our dynamic logic."
(setf *provider-cascade* #'token-accountant-get-cascade))

View File

@@ -1,16 +0,0 @@
(in-package :org-agent)
(defun economist-route-task (context)
(declare (ignore context))
'(:openrouter))
(defun economist-get-model-for-provider (provider &optional context)
"Returns 100% Free/Subsidized model IDs from OpenRouter. Updated April 2026."
(let ((complexity (ignore-errors (uiop:symbol-call :org-agent.skills.org-skill-router :router-classify-complexity context))))
(case provider
(:openrouter
(case complexity
(:REASONING "meta-llama/llama-3.3-70b-instruct:free") ; High fidelity, zero cost
(:COGNITION "qwen/qwen3.6-plus:free") ; Latest interaction, zero cost
(t "meta-llama/llama-3.2-3b-instruct:free"))) ; Ultra-fast reflex, zero cost
(t nil))))

View File

@@ -1,2 +0,0 @@
;;; TDD Suite for web-interface
;;; TDD Suite for web-interface\n(fiveam:test mock-test (5am:is t))

View File

@@ -1 +0,0 @@
../playwright/cli.js

View File

@@ -1 +0,0 @@
../playwright-core/cli.js

View File

@@ -1 +0,0 @@
../rimraf/bin.js

View File

@@ -1,505 +0,0 @@
{
"name": "org-skill-web-research",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/@types/debug": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
"integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
"dependencies": {
"@types/ms": "*"
}
},
"node_modules/@types/ms": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
},
"node_modules/arr-union": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
"integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/brace-expansion": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
"integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/clone-deep": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz",
"integrity": "sha512-we+NuQo2DHhSl+DP6jlUiAhyAjBQrYnpOk15rN6c6JSPScjiCLh8IbSU+VTcph6YS3o7mASE8a0+gbZ7ChLpgg==",
"dependencies": {
"for-own": "^0.1.3",
"is-plain-object": "^2.0.1",
"kind-of": "^3.0.2",
"lazy-cache": "^1.0.3",
"shallow-clone": "^0.1.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
"integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/for-own": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
"integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==",
"dependencies": {
"for-in": "^1.0.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
"node_modules/is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
"integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
"dependencies": {
"isobject": "^3.0.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/isobject": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/jsonfile": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
"dependencies": {
"is-buffer": "^1.1.5"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/lazy-cache": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
"integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/merge-deep": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.3.tgz",
"integrity": "sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==",
"dependencies": {
"arr-union": "^3.1.0",
"clone-deep": "^0.2.4",
"kind-of": "^3.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/minimatch": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/mixin-object": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz",
"integrity": "sha512-ALGF1Jt9ouehcaXaHhn6t1yGWRqGaHkPFndtFVHfZXOvkIZ/yoGaSi0AHVTafb3ZBGg4dr/bDwnaEKqCXzchMA==",
"dependencies": {
"for-in": "^0.1.3",
"is-extendable": "^0.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/mixin-object/node_modules/for-in": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz",
"integrity": "sha512-F0to7vbBSHP8E3l6dCjxNOLuSFAACIxFy3UehTUlG7svlXi37HHsDkyVcHo0Pq8QwrE+pXvWSVX3ZT1T9wAZ9g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/playwright": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
"integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
"dependencies": {
"playwright-core": "1.58.2"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
"integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/playwright-extra": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/playwright-extra/-/playwright-extra-4.3.6.tgz",
"integrity": "sha512-q2rVtcE8V8K3vPVF1zny4pvwZveHLH8KBuVU2MoE3Jw4OKVoBWsHI9CH9zPydovHHOCDxjGN2Vg+2m644q3ijA==",
"dependencies": {
"debug": "^4.3.4"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"playwright": "*",
"playwright-core": "*"
},
"peerDependenciesMeta": {
"playwright": {
"optional": true
},
"playwright-core": {
"optional": true
}
}
},
"node_modules/puppeteer-extra-plugin": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.3.tgz",
"integrity": "sha512-6RNy0e6pH8vaS3akPIKGg28xcryKscczt4wIl0ePciZENGE2yoaQJNd17UiEbdmh5/6WW6dPcfRWT9lxBwCi2Q==",
"dependencies": {
"@types/debug": "^4.1.0",
"debug": "^4.1.1",
"merge-deep": "^3.0.1"
},
"engines": {
"node": ">=9.11.2"
},
"peerDependencies": {
"playwright-extra": "*",
"puppeteer-extra": "*"
},
"peerDependenciesMeta": {
"playwright-extra": {
"optional": true
},
"puppeteer-extra": {
"optional": true
}
}
},
"node_modules/puppeteer-extra-plugin-stealth": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.2.tgz",
"integrity": "sha512-bUemM5XmTj9i2ZerBzsk2AN5is0wHMNE6K0hXBzBXOzP5m5G3Wl0RHhiqKeHToe/uIH8AoZiGhc1tCkLZQPKTQ==",
"dependencies": {
"debug": "^4.1.1",
"puppeteer-extra-plugin": "^3.2.3",
"puppeteer-extra-plugin-user-preferences": "^2.4.1"
},
"engines": {
"node": ">=8"
},
"peerDependencies": {
"playwright-extra": "*",
"puppeteer-extra": "*"
},
"peerDependenciesMeta": {
"playwright-extra": {
"optional": true
},
"puppeteer-extra": {
"optional": true
}
}
},
"node_modules/puppeteer-extra-plugin-user-data-dir": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-data-dir/-/puppeteer-extra-plugin-user-data-dir-2.4.1.tgz",
"integrity": "sha512-kH1GnCcqEDoBXO7epAse4TBPJh9tEpVEK/vkedKfjOVOhZAvLkHGc9swMs5ChrJbRnf8Hdpug6TJlEuimXNQ+g==",
"dependencies": {
"debug": "^4.1.1",
"fs-extra": "^10.0.0",
"puppeteer-extra-plugin": "^3.2.3",
"rimraf": "^3.0.2"
},
"engines": {
"node": ">=8"
},
"peerDependencies": {
"playwright-extra": "*",
"puppeteer-extra": "*"
},
"peerDependenciesMeta": {
"playwright-extra": {
"optional": true
},
"puppeteer-extra": {
"optional": true
}
}
},
"node_modules/puppeteer-extra-plugin-user-preferences": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-preferences/-/puppeteer-extra-plugin-user-preferences-2.4.1.tgz",
"integrity": "sha512-i1oAZxRbc1bk8MZufKCruCEC3CCafO9RKMkkodZltI4OqibLFXF3tj6HZ4LZ9C5vCXZjYcDWazgtY69mnmrQ9A==",
"dependencies": {
"debug": "^4.1.1",
"deepmerge": "^4.2.2",
"puppeteer-extra-plugin": "^3.2.3",
"puppeteer-extra-plugin-user-data-dir": "^2.4.1"
},
"engines": {
"node": ">=8"
},
"peerDependencies": {
"playwright-extra": "*",
"puppeteer-extra": "*"
},
"peerDependenciesMeta": {
"playwright-extra": {
"optional": true
},
"puppeteer-extra": {
"optional": true
}
}
},
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/shallow-clone": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",
"integrity": "sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==",
"dependencies": {
"is-extendable": "^0.1.1",
"kind-of": "^2.0.1",
"lazy-cache": "^0.2.3",
"mixin-object": "^2.0.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/shallow-clone/node_modules/kind-of": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz",
"integrity": "sha512-0u8i1NZ/mg0b+W3MGGw5I7+6Eib2nx72S/QvXa0hYjEkjTknYmEYQJwGu3mLC0BrhtJjtQafTkyRUQ75Kx0LVg==",
"dependencies": {
"is-buffer": "^1.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/shallow-clone/node_modules/lazy-cache": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz",
"integrity": "sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
}
}

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

View File

@@ -1,69 +0,0 @@
# Installation
> `npm install --save @types/debug`
# Summary
This package contains type definitions for debug (https://github.com/debug-js/debug).
# Details
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/debug.
## [index.d.ts](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/debug/index.d.ts)
````ts
declare var debug: debug.Debug & { debug: debug.Debug; default: debug.Debug };
export = debug;
export as namespace debug;
declare namespace debug {
interface Debug {
(namespace: string): Debugger;
coerce: (val: any) => any;
disable: () => string;
enable: (namespaces: string) => void;
enabled: (namespaces: string) => boolean;
formatArgs: (this: Debugger, args: any[]) => void;
log: (...args: any[]) => any;
selectColor: (namespace: string) => string | number;
humanize: typeof import("ms");
names: string[];
skips: string[];
formatters: Formatters;
inspectOpts?: {
hideDate?: boolean | number | null;
colors?: boolean | number | null;
depth?: boolean | number | null;
showHidden?: boolean | number | null;
};
}
type IDebug = Debug;
interface Formatters {
[formatter: string]: (v: any) => string;
}
type IDebugger = Debugger;
interface Debugger {
(formatter: any, ...args: any[]): void;
color: string;
diff: number;
enabled: boolean;
log: (...args: any[]) => any;
namespace: string;
destroy: () => boolean;
extend: (namespace: string, delimiter?: string) => Debugger;
}
}
````
### Additional Details
* Last updated: Thu, 19 Mar 2026 06:47:22 GMT
* Dependencies: [@types/ms](https://npmjs.com/package/@types/ms)
# Credits
These definitions were written by [Seon-Wook Park](https://github.com/swook), [Gal Talmor](https://github.com/galtalmor), [John McLaughlin](https://github.com/zamb3zi), [Brasten Sager](https://github.com/brasten), [Nicolas Penin](https://github.com/npenin), [Kristian Brünn](https://github.com/kristianmitk), and [Caleb Gregory](https://github.com/calebgregory).

View File

@@ -1,50 +0,0 @@
declare var debug: debug.Debug & { debug: debug.Debug; default: debug.Debug };
export = debug;
export as namespace debug;
declare namespace debug {
interface Debug {
(namespace: string): Debugger;
coerce: (val: any) => any;
disable: () => string;
enable: (namespaces: string) => void;
enabled: (namespaces: string) => boolean;
formatArgs: (this: Debugger, args: any[]) => void;
log: (...args: any[]) => any;
selectColor: (namespace: string) => string | number;
humanize: typeof import("ms");
names: string[];
skips: string[];
formatters: Formatters;
inspectOpts?: {
hideDate?: boolean | number | null;
colors?: boolean | number | null;
depth?: boolean | number | null;
showHidden?: boolean | number | null;
};
}
type IDebug = Debug;
interface Formatters {
[formatter: string]: (v: any) => string;
}
type IDebugger = Debugger;
interface Debugger {
(formatter: any, ...args: any[]): void;
color: string;
diff: number;
enabled: boolean;
log: (...args: any[]) => any;
namespace: string;
destroy: () => boolean;
extend: (namespace: string, delimiter?: string) => Debugger;
}
}

View File

@@ -1,58 +0,0 @@
{
"name": "@types/debug",
"version": "4.1.13",
"description": "TypeScript definitions for debug",
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/debug",
"license": "MIT",
"contributors": [
{
"name": "Seon-Wook Park",
"githubUsername": "swook",
"url": "https://github.com/swook"
},
{
"name": "Gal Talmor",
"githubUsername": "galtalmor",
"url": "https://github.com/galtalmor"
},
{
"name": "John McLaughlin",
"githubUsername": "zamb3zi",
"url": "https://github.com/zamb3zi"
},
{
"name": "Brasten Sager",
"githubUsername": "brasten",
"url": "https://github.com/brasten"
},
{
"name": "Nicolas Penin",
"githubUsername": "npenin",
"url": "https://github.com/npenin"
},
{
"name": "Kristian Brünn",
"githubUsername": "kristianmitk",
"url": "https://github.com/kristianmitk"
},
{
"name": "Caleb Gregory",
"githubUsername": "calebgregory",
"url": "https://github.com/calebgregory"
}
],
"main": "",
"types": "index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
"directory": "types/debug"
},
"scripts": {},
"dependencies": {
"@types/ms": "*"
},
"peerDependencies": {},
"typesPublisherContentHash": "1c506e100366b85350ff1c28c9cf4cc09e9a07275546bb050993c241c9821cd9",
"typeScriptVersion": "5.2"
}

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

View File

@@ -1,82 +0,0 @@
# Installation
> `npm install --save @types/ms`
# Summary
This package contains type definitions for ms (https://github.com/vercel/ms).
# Details
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/ms.
## [index.d.ts](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/ms/index.d.ts)
````ts
/**
* Short/Long format for `value`.
*
* @param {Number} value
* @param {{long: boolean}} options
* @return {String}
*/
declare function ms(value: number, options?: { long: boolean }): string;
/**
* Parse the given `value` and return milliseconds.
*
* @param {ms.StringValue} value
* @return {Number}
*/
declare function ms(value: ms.StringValue): number;
declare namespace ms {
// Unit, UnitAnyCase, and StringValue are backported from ms@3
// https://github.com/vercel/ms/blob/8b5923d1d86c84a9f6aba8022d416dcf2361aa8d/src/index.ts
type Unit =
| "Years"
| "Year"
| "Yrs"
| "Yr"
| "Y"
| "Weeks"
| "Week"
| "W"
| "Days"
| "Day"
| "D"
| "Hours"
| "Hour"
| "Hrs"
| "Hr"
| "H"
| "Minutes"
| "Minute"
| "Mins"
| "Min"
| "M"
| "Seconds"
| "Second"
| "Secs"
| "Sec"
| "s"
| "Milliseconds"
| "Millisecond"
| "Msecs"
| "Msec"
| "Ms";
type UnitAnyCase = Unit | Uppercase<Unit> | Lowercase<Unit>;
type StringValue =
| `${number}`
| `${number}${UnitAnyCase}`
| `${number} ${UnitAnyCase}`;
}
export = ms;
````
### Additional Details
* Last updated: Thu, 16 Jan 2025 21:02:45 GMT
* Dependencies: none
# Credits
These definitions were written by [Zhiyuan Wang](https://github.com/danny8002).

View File

@@ -1,63 +0,0 @@
/**
* Short/Long format for `value`.
*
* @param {Number} value
* @param {{long: boolean}} options
* @return {String}
*/
declare function ms(value: number, options?: { long: boolean }): string;
/**
* Parse the given `value` and return milliseconds.
*
* @param {ms.StringValue} value
* @return {Number}
*/
declare function ms(value: ms.StringValue): number;
declare namespace ms {
// Unit, UnitAnyCase, and StringValue are backported from ms@3
// https://github.com/vercel/ms/blob/8b5923d1d86c84a9f6aba8022d416dcf2361aa8d/src/index.ts
type Unit =
| "Years"
| "Year"
| "Yrs"
| "Yr"
| "Y"
| "Weeks"
| "Week"
| "W"
| "Days"
| "Day"
| "D"
| "Hours"
| "Hour"
| "Hrs"
| "Hr"
| "H"
| "Minutes"
| "Minute"
| "Mins"
| "Min"
| "M"
| "Seconds"
| "Second"
| "Secs"
| "Sec"
| "s"
| "Milliseconds"
| "Millisecond"
| "Msecs"
| "Msec"
| "Ms";
type UnitAnyCase = Unit | Uppercase<Unit> | Lowercase<Unit>;
type StringValue =
| `${number}`
| `${number}${UnitAnyCase}`
| `${number} ${UnitAnyCase}`;
}
export = ms;

View File

@@ -1,26 +0,0 @@
{
"name": "@types/ms",
"version": "2.1.0",
"description": "TypeScript definitions for ms",
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/ms",
"license": "MIT",
"contributors": [
{
"name": "Zhiyuan Wang",
"githubUsername": "danny8002",
"url": "https://github.com/danny8002"
}
],
"main": "",
"types": "index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
"directory": "types/ms"
},
"scripts": {},
"dependencies": {},
"peerDependencies": {},
"typesPublisherContentHash": "2c8651ce1714fdc6bcbc0f262c93a790f1d127fb1c2dc8edbb583decef56fd39",
"typeScriptVersion": "5.0"
}

View File

@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014-2016, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,99 +0,0 @@
# arr-union [![NPM version](https://img.shields.io/npm/v/arr-union.svg)](https://www.npmjs.com/package/arr-union) [![Build Status](https://img.shields.io/travis/jonschlinkert/arr-union.svg)](https://travis-ci.org/jonschlinkert/arr-union)
> Combines a list of arrays, returning a single array with unique values, using strict equality for comparisons.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm i arr-union --save
```
## Benchmarks
This library is **10-20 times faster** and more performant than [array-union](https://github.com/sindresorhus/array-union).
See the [benchmarks](./benchmark).
```sh
#1: five-arrays
array-union x 511,121 ops/sec ±0.80% (96 runs sampled)
arr-union x 5,716,039 ops/sec ±0.86% (93 runs sampled)
#2: ten-arrays
array-union x 245,196 ops/sec ±0.69% (94 runs sampled)
arr-union x 1,850,786 ops/sec ±0.84% (97 runs sampled)
#3: two-arrays
array-union x 563,869 ops/sec ±0.97% (94 runs sampled)
arr-union x 9,602,852 ops/sec ±0.87% (92 runs sampled)
```
## Usage
```js
var union = require('arr-union');
union(['a'], ['b', 'c'], ['d', 'e', 'f']);
//=> ['a', 'b', 'c', 'd', 'e', 'f']
```
Returns only unique elements:
```js
union(['a', 'a'], ['b', 'c']);
//=> ['a', 'b', 'c']
```
## Related projects
* [arr-diff](https://www.npmjs.com/package/arr-diff): Returns an array with only the unique values from the first array, by excluding all… [more](https://www.npmjs.com/package/arr-diff) | [homepage](https://github.com/jonschlinkert/arr-diff)
* [arr-filter](https://www.npmjs.com/package/arr-filter): Faster alternative to javascript's native filter method. | [homepage](https://github.com/jonschlinkert/arr-filter)
* [arr-flatten](https://www.npmjs.com/package/arr-flatten): Recursively flatten an array or arrays. This is the fastest implementation of array flatten. | [homepage](https://github.com/jonschlinkert/arr-flatten)
* [arr-map](https://www.npmjs.com/package/arr-map): Faster, node.js focused alternative to JavaScript's native array map. | [homepage](https://github.com/jonschlinkert/arr-map)
* [arr-pluck](https://www.npmjs.com/package/arr-pluck): Retrieves the value of a specified property from all elements in the collection. | [homepage](https://github.com/jonschlinkert/arr-pluck)
* [arr-reduce](https://www.npmjs.com/package/arr-reduce): Fast array reduce that also loops over sparse elements. | [homepage](https://github.com/jonschlinkert/arr-reduce)
* [array-unique](https://www.npmjs.com/package/array-unique): Return an array free of duplicate values. Fastest ES5 implementation. | [homepage](https://github.com/jonschlinkert/array-unique)
## Contributing
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/arr-union/issues/new).
## Building docs
Generate readme and API documentation with [verb](https://github.com/verbose/verb):
```sh
$ npm i verb && npm run docs
```
Or, if [verb](https://github.com/verbose/verb) is installed globally:
```sh
$ verb
```
## Running tests
Install dev dependencies:
```sh
$ npm i -d && npm test
```
## Author
**Jon Schlinkert**
* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
## License
Copyright © 2016 [Jon Schlinkert](https://github.com/jonschlinkert)
Released under the [MIT license](https://github.com/jonschlinkert/arr-union/blob/master/LICENSE).
***
_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on February 23, 2016._

View File

@@ -1,29 +0,0 @@
'use strict';
module.exports = function union(init) {
if (!Array.isArray(init)) {
throw new TypeError('arr-union expects the first argument to be an array.');
}
var len = arguments.length;
var i = 0;
while (++i < len) {
var arg = arguments[i];
if (!arg) continue;
if (!Array.isArray(arg)) {
arg = [arg];
}
for (var j = 0; j < arg.length; j++) {
var ele = arg[j];
if (init.indexOf(ele) >= 0) {
continue;
}
init.push(ele);
}
}
return init;
};

Some files were not shown because too many files have changed in this diff Show More