diff --git a/.oxfmtrc.jsonc b/.oxfmtrc.jsonc index df83c05eb2..6b6ed9f652 100644 --- a/.oxfmtrc.jsonc +++ b/.oxfmtrc.jsonc @@ -8,8 +8,6 @@ "node_modules/", "patches/", "pnpm-lock.yaml/", - "scripts/", - "skills/", "Swabble/", "vendor/", ], diff --git a/extensions/open-prose/skills/prose/SKILL.md b/extensions/open-prose/skills/prose/SKILL.md index 69a53f1066..c6c2ed06d0 100644 --- a/extensions/open-prose/skills/prose/SKILL.md +++ b/extensions/open-prose/skills/prose/SKILL.md @@ -1,7 +1,7 @@ --- name: prose description: OpenProse VM skill pack. Activate on any `prose` command, .prose files, or OpenProse mentions; orchestrates multi-agent workflows. -metadata: {"openclaw":{"emoji":"🪶","homepage":"https://www.prose.md"}} +metadata: { "openclaw": { "emoji": "🪶", "homepage": "https://www.prose.md" } } --- # OpenProse Skill @@ -29,15 +29,15 @@ Activate this skill when the user: When a user invokes `prose `, intelligently route based on intent: -| Command | Action | -|---------|--------| -| `prose help` | Load `help.md`, guide user to what they need | -| `prose run ` | Load VM (`prose.md` + state backend), execute the program | +| Command | Action | +| ----------------------- | ------------------------------------------------------------- | +| `prose help` | Load `help.md`, guide user to what they need | +| `prose run ` | Load VM (`prose.md` + state backend), execute the program | | `prose run handle/slug` | Fetch from registry, then execute (see Remote Programs below) | -| `prose compile ` | Load `compiler.md`, validate the program | -| `prose update` | Run migration (see Migration section below) | -| `prose examples` | Show or run example programs from `examples/` | -| Other | Intelligently interpret based on context | +| `prose compile ` | Load `compiler.md`, validate the program | +| `prose update` | Run migration (see Migration section below) | +| `prose examples` | Show or run example programs from `examples/` | +| Other | Intelligently interpret based on context | ### Important: Single Skill @@ -77,11 +77,11 @@ prose run alice/code-review **Resolution rules:** -| Input | Resolution | -|-------|------------| -| Starts with `http://` or `https://` | Fetch directly from URL | -| Contains `/` but no protocol | Resolve to `https://p.prose.md/{path}` | -| Otherwise | Treat as local file path | +| Input | Resolution | +| ----------------------------------- | -------------------------------------- | +| Starts with `http://` or `https://` | Fetch directly from URL | +| Contains `/` but no protocol | Resolve to `https://p.prose.md/{path}` | +| Otherwise | Treat as local file path | **Steps for remote programs:** @@ -102,33 +102,33 @@ use "alice/research" as research # Registry shorthand **Do NOT search for OpenProse documentation files.** All skill files are co-located with this SKILL.md file: -| File | Location | Purpose | -| ------------------------- | --------------------------- | ----------------------------------------- | -| `prose.md` | Same directory as this file | VM semantics (load to run programs) | -| `help.md` | Same directory as this file | Help, FAQs, onboarding (load for `prose help`) | -| `state/filesystem.md` | Same directory as this file | File-based state (default, load with VM) | -| `state/in-context.md` | Same directory as this file | In-context state (on request) | -| `state/sqlite.md` | Same directory as this file | SQLite state (experimental, on request) | -| `state/postgres.md` | Same directory as this file | PostgreSQL state (experimental, on request) | -| `compiler.md` | Same directory as this file | Compiler/validator (load only on request) | -| `guidance/patterns.md` | Same directory as this file | Best practices (load when writing .prose) | -| `guidance/antipatterns.md`| Same directory as this file | What to avoid (load when writing .prose) | -| `examples/` | Same directory as this file | 37 example programs | +| File | Location | Purpose | +| -------------------------- | --------------------------- | ---------------------------------------------- | +| `prose.md` | Same directory as this file | VM semantics (load to run programs) | +| `help.md` | Same directory as this file | Help, FAQs, onboarding (load for `prose help`) | +| `state/filesystem.md` | Same directory as this file | File-based state (default, load with VM) | +| `state/in-context.md` | Same directory as this file | In-context state (on request) | +| `state/sqlite.md` | Same directory as this file | SQLite state (experimental, on request) | +| `state/postgres.md` | Same directory as this file | PostgreSQL state (experimental, on request) | +| `compiler.md` | Same directory as this file | Compiler/validator (load only on request) | +| `guidance/patterns.md` | Same directory as this file | Best practices (load when writing .prose) | +| `guidance/antipatterns.md` | Same directory as this file | What to avoid (load when writing .prose) | +| `examples/` | Same directory as this file | 37 example programs | **User workspace files** (these ARE in the user's project): -| File/Directory | Location | Purpose | -| ---------------- | ------------------------ | ----------------------------------- | -| `.prose/.env` | User's working directory | Config (key=value format) | -| `.prose/runs/` | User's working directory | Runtime state for file-based mode | -| `.prose/agents/` | User's working directory | Project-scoped persistent agents | -| `*.prose` files | User's project | User-created programs to execute | +| File/Directory | Location | Purpose | +| ---------------- | ------------------------ | --------------------------------- | +| `.prose/.env` | User's working directory | Config (key=value format) | +| `.prose/runs/` | User's working directory | Runtime state for file-based mode | +| `.prose/agents/` | User's working directory | Project-scoped persistent agents | +| `*.prose` files | User's project | User-created programs to execute | **User-level files** (in user's home directory, shared across all projects): -| File/Directory | Location | Purpose | -| ----------------- | ---------------- | ---------------------------------------- | -| `~/.prose/agents/`| User's home dir | User-scoped persistent agents (cross-project) | +| File/Directory | Location | Purpose | +| ------------------ | --------------- | --------------------------------------------- | +| `~/.prose/agents/` | User's home dir | User-scoped persistent agents (cross-project) | When you need to read `prose.md` or `compiler.md`, read them from the same directory where you found this SKILL.md file. Never search the user's workspace for these files. @@ -136,20 +136,21 @@ When you need to read `prose.md` or `compiler.md`, read them from the same direc ## Core Documentation -| File | Purpose | When to Load | -| --------------------- | -------------------- | ---------------------------------------------- | -| `prose.md` | VM / Interpreter | Always load to run programs | -| `state/filesystem.md` | File-based state | Load with VM (default) | -| `state/in-context.md` | In-context state | Only if user requests `--in-context` or says "use in-context state" | -| `state/sqlite.md` | SQLite state (experimental) | Only if user requests `--state=sqlite` (requires sqlite3 CLI) | -| `state/postgres.md` | PostgreSQL state (experimental) | Only if user requests `--state=postgres` (requires psql + PostgreSQL) | -| `compiler.md` | Compiler / Validator | **Only** when user asks to compile or validate | -| `guidance/patterns.md` | Best practices | Load when **writing** new .prose files | -| `guidance/antipatterns.md` | What to avoid | Load when **writing** new .prose files | +| File | Purpose | When to Load | +| -------------------------- | ------------------------------- | --------------------------------------------------------------------- | +| `prose.md` | VM / Interpreter | Always load to run programs | +| `state/filesystem.md` | File-based state | Load with VM (default) | +| `state/in-context.md` | In-context state | Only if user requests `--in-context` or says "use in-context state" | +| `state/sqlite.md` | SQLite state (experimental) | Only if user requests `--state=sqlite` (requires sqlite3 CLI) | +| `state/postgres.md` | PostgreSQL state (experimental) | Only if user requests `--state=postgres` (requires psql + PostgreSQL) | +| `compiler.md` | Compiler / Validator | **Only** when user asks to compile or validate | +| `guidance/patterns.md` | Best practices | Load when **writing** new .prose files | +| `guidance/antipatterns.md` | What to avoid | Load when **writing** new .prose files | ### Authoring Guidance When the user asks you to **write or create** a new `.prose` file, load the guidance files: + - `guidance/patterns.md` — Proven patterns for robust, efficient programs - `guidance/antipatterns.md` — Common mistakes to avoid @@ -159,12 +160,12 @@ Do **not** load these when running or compiling—they're for authoring only. OpenProse supports three state management approaches: -| Mode | When to Use | State Location | -|------|-------------|----------------| -| **filesystem** (default) | Complex programs, resumption needed, debugging | `.prose/runs/{id}/` files | -| **in-context** | Simple programs (<30 statements), no persistence needed | Conversation history | -| **sqlite** (experimental) | Queryable state, atomic transactions, flexible schema | `.prose/runs/{id}/state.db` | -| **postgres** (experimental) | True concurrent writes, external integrations, team collaboration | PostgreSQL database | +| Mode | When to Use | State Location | +| --------------------------- | ----------------------------------------------------------------- | --------------------------- | +| **filesystem** (default) | Complex programs, resumption needed, debugging | `.prose/runs/{id}/` files | +| **in-context** | Simple programs (<30 statements), no persistence needed | Conversation history | +| **sqlite** (experimental) | Queryable state, atomic transactions, flexible schema | `.prose/runs/{id}/state.db` | +| **postgres** (experimental) | True concurrent writes, external integrations, team collaboration | PostgreSQL database | **Default behavior:** When loading `prose.md`, also load `state/filesystem.md`. This is the recommended mode for most programs. @@ -177,6 +178,7 @@ OpenProse supports three state management approaches: **⚠️ Security Note:** Database credentials in `OPENPROSE_POSTGRES_URL` are passed to subagent sessions and visible in logs. Advise users to use a dedicated database with limited-privilege credentials. See `state/postgres.md` for secure setup guidance. 1. **Check for connection configuration first:** + ```bash # Check .prose/.env for OPENPROSE_POSTGRES_URL cat .prose/.env 2>/dev/null | grep OPENPROSE_POSTGRES_URL @@ -185,11 +187,13 @@ OpenProse supports three state management approaches: ``` 2. **If connection string exists, verify connectivity:** + ```bash psql "$OPENPROSE_POSTGRES_URL" -c "SELECT 1" 2>&1 ``` 3. **If not configured or connection fails, advise the user:** + ``` ⚠️ PostgreSQL state requires a connection URL. @@ -261,10 +265,10 @@ When a user invokes `prose update`, check for legacy file structures and migrate ### Legacy Paths to Check -| Legacy Path | Current Path | Notes | -|-------------|--------------|-------| -| `.prose/state.json` | `.prose/.env` | Convert JSON to key=value format | -| `.prose/execution/` | `.prose/runs/` | Rename directory | +| Legacy Path | Current Path | Notes | +| ------------------- | -------------- | -------------------------------- | +| `.prose/state.json` | `.prose/.env` | Convert JSON to key=value format | +| `.prose/execution/` | `.prose/runs/` | Rename directory | ### Migration Steps @@ -272,7 +276,7 @@ When a user invokes `prose update`, check for legacy file structures and migrate - If exists, read the JSON content - Convert to `.env` format: ```json - {"OPENPROSE_TELEMETRY": "enabled", "USER_ID": "user-xxx", "SESSION_ID": "sess-xxx"} + { "OPENPROSE_TELEMETRY": "enabled", "USER_ID": "user-xxx", "SESSION_ID": "sess-xxx" } ``` becomes: ```env @@ -301,6 +305,7 @@ When a user invokes `prose update`, check for legacy file structures and migrate ``` If no legacy files are found: + ``` ✅ Workspace already up to date. No migration needed. ``` @@ -309,10 +314,10 @@ If no legacy files are found: These documentation files were renamed in the skill itself (not user workspace): -| Legacy Name | Current Name | -|-------------|--------------| -| `docs.md` | `compiler.md` | -| `patterns.md` | `guidance/patterns.md` | +| Legacy Name | Current Name | +| ----------------- | -------------------------- | +| `docs.md` | `compiler.md` | +| `patterns.md` | `guidance/patterns.md` | | `antipatterns.md` | `guidance/antipatterns.md` | If you encounter references to the old names in user prompts or external docs, map them to the current paths. diff --git a/extensions/open-prose/skills/prose/alts/arabian-nights.md b/extensions/open-prose/skills/prose/alts/arabian-nights.md index 136faf8d51..cc0d146664 100644 --- a/extensions/open-prose/skills/prose/alts/arabian-nights.md +++ b/extensions/open-prose/skills/prose/alts/arabian-nights.md @@ -28,55 +28,55 @@ An alternative register for OpenProse that draws from One Thousand and One Night ### Core Constructs -| Functional | Nights | Reference | -|------------|--------|-----------| -| `agent` | `djinn` | Spirit bound to serve, grants wishes | -| `session` | `tale` | A story told, a narrative unit | +| Functional | Nights | Reference | +| ---------- | -------- | ------------------------------------- | +| `agent` | `djinn` | Spirit bound to serve, grants wishes | +| `session` | `tale` | A story told, a narrative unit | | `parallel` | `bazaar` | Many voices, many stalls, all at once | -| `block` | `frame` | A story that contains other stories | +| `block` | `frame` | A story that contains other stories | ### Composition & Binding -| Functional | Nights | Reference | -|------------|--------|-----------| -| `use` | `conjure` | Summoning from elsewhere | -| `input` | `wish` | What is asked of the djinn | -| `output` | `gift` | What is granted in return | -| `let` | `name` | Naming has power (same as folk) | -| `const` | `oath` | Unbreakable vow, sealed | -| `context` | `scroll` | What is written and passed along | +| Functional | Nights | Reference | +| ---------- | --------- | -------------------------------- | +| `use` | `conjure` | Summoning from elsewhere | +| `input` | `wish` | What is asked of the djinn | +| `output` | `gift` | What is granted in return | +| `let` | `name` | Naming has power (same as folk) | +| `const` | `oath` | Unbreakable vow, sealed | +| `context` | `scroll` | What is written and passed along | ### Control Flow -| Functional | Nights | Reference | -|------------|--------|-----------| -| `repeat N` | `N nights` | "For a thousand and one nights..." | +| Functional | Nights | Reference | +| ---------- | ------------------ | ------------------------------------ | +| `repeat N` | `N nights` | "For a thousand and one nights..." | | `for...in` | `for each...among` | Among the merchants, among the tales | -| `loop` | `telling` | The telling continues | -| `until` | `until` | Unchanged | -| `while` | `while` | Unchanged | -| `choice` | `crossroads` | Where the story forks | -| `option` | `path` | One way the story could go | -| `if` | `should` | Narrative conditional | -| `elif` | `or should` | Continued conditional | -| `else` | `otherwise` | The other telling | +| `loop` | `telling` | The telling continues | +| `until` | `until` | Unchanged | +| `while` | `while` | Unchanged | +| `choice` | `crossroads` | Where the story forks | +| `option` | `path` | One way the story could go | +| `if` | `should` | Narrative conditional | +| `elif` | `or should` | Continued conditional | +| `else` | `otherwise` | The other telling | ### Error Handling -| Functional | Nights | Reference | -|------------|--------|-----------| -| `try` | `venture` | Setting out on the journey | -| `catch` | `should misfortune strike` | The tale turns dark | -| `finally` | `and so it was` | The inevitable ending | -| `throw` | `curse` | Ill fate pronounced | -| `retry` | `persist` | The hero tries again | +| Functional | Nights | Reference | +| ---------- | -------------------------- | -------------------------- | +| `try` | `venture` | Setting out on the journey | +| `catch` | `should misfortune strike` | The tale turns dark | +| `finally` | `and so it was` | The inevitable ending | +| `throw` | `curse` | Ill fate pronounced | +| `retry` | `persist` | The hero tries again | ### Session Properties -| Functional | Nights | Reference | -|------------|--------|-----------| -| `prompt` | `command` | What is commanded of the djinn | -| `model` | `spirit` | Which spirit answers | +| Functional | Nights | Reference | +| ---------- | --------- | ------------------------------ | +| `prompt` | `command` | What is commanded of the djinn | +| `model` | `spirit` | Which spirit answers | ### Unchanged @@ -297,14 +297,14 @@ oath config = { spirit: "opus", persist: 3 } ## Key Arabian Nights Concepts -| Term | Meaning | Used for | -|------|---------|----------| -| Scheherazade | The narrator who tells tales to survive | (the program author) | -| Djinn | Supernatural spirit, bound to serve | `agent` → `djinn` | -| Frame story | A story that contains other stories | `block` → `frame` | -| Wish | What is asked of the djinn | `input` → `wish` | -| Oath | Unbreakable promise | `const` → `oath` | -| Bazaar | Marketplace, many vendors | `parallel` → `bazaar` | +| Term | Meaning | Used for | +| ------------ | --------------------------------------- | --------------------- | +| Scheherazade | The narrator who tells tales to survive | (the program author) | +| Djinn | Supernatural spirit, bound to serve | `agent` → `djinn` | +| Frame story | A story that contains other stories | `block` → `frame` | +| Wish | What is asked of the djinn | `input` → `wish` | +| Oath | Unbreakable promise | `const` → `oath` | +| Bazaar | Marketplace, many vendors | `parallel` → `bazaar` | --- @@ -312,36 +312,36 @@ oath config = { spirit: "opus", persist: 3 } ### For `djinn` (agent) -| Keyword | Rejected because | -|---------|------------------| -| `genie` | Disney connotation, less literary | -| `spirit` | Used for `model` | -| `ifrit` | Too specific (a type of djinn) | +| Keyword | Rejected because | +| ---------- | ---------------------------------- | +| `genie` | Disney connotation, less literary | +| `spirit` | Used for `model` | +| `ifrit` | Too specific (a type of djinn) | | `narrator` | Too meta, Scheherazade is the user | ### For `tale` (session) -| Keyword | Rejected because | -|---------|------------------| -| `story` | Good but `tale` feels more literary | -| `night` | Reserved for `repeat N nights` | -| `chapter` | More Western/novelistic | +| Keyword | Rejected because | +| --------- | ----------------------------------- | +| `story` | Good but `tale` feels more literary | +| `night` | Reserved for `repeat N nights` | +| `chapter` | More Western/novelistic | ### For `bazaar` (parallel) -| Keyword | Rejected because | -|---------|------------------| +| Keyword | Rejected because | +| --------- | ------------------------------------------ | | `caravan` | Sequential connotation (one after another) | -| `chorus` | Greek, wrong tradition | -| `souk` | Less widely known | +| `chorus` | Greek, wrong tradition | +| `souk` | Less widely known | ### For `scroll` (context) -| Keyword | Rejected because | -|---------|------------------| -| `letter` | Too small/personal | -| `tome` | Too large | -| `message` | Too plain | +| Keyword | Rejected because | +| --------- | ------------------ | +| `letter` | Too small/personal | +| `tome` | Too large | +| `message` | Too plain | --- diff --git a/extensions/open-prose/skills/prose/alts/borges.md b/extensions/open-prose/skills/prose/alts/borges.md index 25aef941a1..244d61edd8 100644 --- a/extensions/open-prose/skills/prose/alts/borges.md +++ b/extensions/open-prose/skills/prose/alts/borges.md @@ -29,55 +29,55 @@ An alternative register for OpenProse that draws from the works of Jorge Luis Bo ### Core Constructs -| Functional | Borges | Reference | -|------------|--------|-----------| -| `agent` | `dreamer` | "The Circular Ruins" — dreamers who dream worlds into existence | -| `session` | `dream` | Each execution is a dream within the dreamer | -| `parallel` | `forking` | "The Garden of Forking Paths" — branching timelines | -| `block` | `chapter` | Books within books, self-referential structure | +| Functional | Borges | Reference | +| ---------- | --------- | --------------------------------------------------------------- | +| `agent` | `dreamer` | "The Circular Ruins" — dreamers who dream worlds into existence | +| `session` | `dream` | Each execution is a dream within the dreamer | +| `parallel` | `forking` | "The Garden of Forking Paths" — branching timelines | +| `block` | `chapter` | Books within books, self-referential structure | ### Composition & Binding -| Functional | Borges | Reference | -|------------|--------|-----------| -| `use` | `retrieve` | "The Library of Babel" — retrieving from infinite stacks | -| `input` | `axiom` | The given premise (Borges' scholarly/mathematical tone) | -| `output` | `theorem` | What is derived from the axioms | -| `let` | `inscribe` | Writing something into being | -| `const` | `zahir` | "The Zahir" — unforgettable, unchangeable, fixed in mind | -| `context` | `memory` | "Funes the Memorious" — perfect, total recall | +| Functional | Borges | Reference | +| ---------- | ---------- | -------------------------------------------------------- | +| `use` | `retrieve` | "The Library of Babel" — retrieving from infinite stacks | +| `input` | `axiom` | The given premise (Borges' scholarly/mathematical tone) | +| `output` | `theorem` | What is derived from the axioms | +| `let` | `inscribe` | Writing something into being | +| `const` | `zahir` | "The Zahir" — unforgettable, unchangeable, fixed in mind | +| `context` | `memory` | "Funes the Memorious" — perfect, total recall | ### Control Flow -| Functional | Borges | Reference | -|------------|--------|-----------| -| `repeat N` | `N mirrors` | Infinite reflections facing each other | -| `for...in` | `for each...within` | Slightly more Borgesian preposition | -| `loop` | `labyrinth` | The maze that folds back on itself | -| `until` | `until` | Unchanged | -| `while` | `while` | Unchanged | -| `choice` | `bifurcation` | The forking of paths | -| `option` | `branch` | One branch of diverging time | -| `if` | `should` | Scholarly conditional | -| `elif` | `or should` | Continued conditional | -| `else` | `otherwise` | Natural alternative | +| Functional | Borges | Reference | +| ---------- | ------------------- | -------------------------------------- | +| `repeat N` | `N mirrors` | Infinite reflections facing each other | +| `for...in` | `for each...within` | Slightly more Borgesian preposition | +| `loop` | `labyrinth` | The maze that folds back on itself | +| `until` | `until` | Unchanged | +| `while` | `while` | Unchanged | +| `choice` | `bifurcation` | The forking of paths | +| `option` | `branch` | One branch of diverging time | +| `if` | `should` | Scholarly conditional | +| `elif` | `or should` | Continued conditional | +| `else` | `otherwise` | Natural alternative | ### Error Handling -| Functional | Borges | Reference | -|------------|--------|-----------| -| `try` | `venture` | Entering the labyrinth | -| `catch` | `lest` | "Lest it fail..." (archaic, scholarly) | -| `finally` | `ultimately` | The inevitable conclusion | -| `throw` | `shatter` | Breaking the mirror, ending the dream | -| `retry` | `recur` | Infinite regress, trying again | +| Functional | Borges | Reference | +| ---------- | ------------ | -------------------------------------- | +| `try` | `venture` | Entering the labyrinth | +| `catch` | `lest` | "Lest it fail..." (archaic, scholarly) | +| `finally` | `ultimately` | The inevitable conclusion | +| `throw` | `shatter` | Breaking the mirror, ending the dream | +| `retry` | `recur` | Infinite regress, trying again | ### Session Properties -| Functional | Borges | Reference | -|------------|--------|-----------| -| `prompt` | `query` | Asking the Library | -| `model` | `author` | Which author writes this dream | +| Functional | Borges | Reference | +| ---------- | -------- | ------------------------------ | +| `prompt` | `query` | Asking the Library | +| `model` | `author` | Which author writes this dream | ### Unchanged @@ -299,15 +299,15 @@ zahir config = { author: "opus", recur: 3 } For those unfamiliar with the source material: -| Work | Concept Used | Summary | -|------|--------------|---------| -| "The Circular Ruins" | `dreamer`, `dream` | A man dreams another man into existence, only to discover he himself is being dreamed | -| "The Garden of Forking Paths" | `forking`, `bifurcation`, `branch` | A labyrinth that is a book; time forks perpetually into diverging futures | -| "The Library of Babel" | `retrieve` | An infinite library containing every possible book | -| "Funes the Memorious" | `memory` | A man with perfect memory who cannot forget anything | -| "The Zahir" | `zahir` | An object that, once seen, cannot be forgotten or ignored | -| "The Aleph" | (not used) | A point in space containing all other points | -| "Tlön, Uqbar, Orbis Tertius" | (not used) | A fictional world that gradually becomes real | +| Work | Concept Used | Summary | +| ----------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------- | +| "The Circular Ruins" | `dreamer`, `dream` | A man dreams another man into existence, only to discover he himself is being dreamed | +| "The Garden of Forking Paths" | `forking`, `bifurcation`, `branch` | A labyrinth that is a book; time forks perpetually into diverging futures | +| "The Library of Babel" | `retrieve` | An infinite library containing every possible book | +| "Funes the Memorious" | `memory` | A man with perfect memory who cannot forget anything | +| "The Zahir" | `zahir` | An object that, once seen, cannot be forgotten or ignored | +| "The Aleph" | (not used) | A point in space containing all other points | +| "Tlön, Uqbar, Orbis Tertius" | (not used) | A fictional world that gradually becomes real | --- @@ -315,35 +315,35 @@ For those unfamiliar with the source material: ### For `dreamer` (agent) -| Keyword | Rejected because | -|---------|------------------| -| `author` | Used for `model` instead | -| `scribe` | Too passive, just records | +| Keyword | Rejected because | +| ----------- | ------------------------- | +| `author` | Used for `model` instead | +| `scribe` | Too passive, just records | | `librarian` | More curator than creator | ### For `labyrinth` (loop) -| Keyword | Rejected because | -|---------|------------------| -| `recursion` | Too technical | -| `eternal return` | Too long | -| `ouroboros` | Wrong mythology | +| Keyword | Rejected because | +| ---------------- | ---------------- | +| `recursion` | Too technical | +| `eternal return` | Too long | +| `ouroboros` | Wrong mythology | ### For `zahir` (const) -| Keyword | Rejected because | -|---------|------------------| -| `aleph` | The Aleph is about totality, not immutability | -| `fixed` | Too plain | -| `eternal` | Overused | +| Keyword | Rejected because | +| --------- | --------------------------------------------- | +| `aleph` | The Aleph is about totality, not immutability | +| `fixed` | Too plain | +| `eternal` | Overused | ### For `memory` (context) -| Keyword | Rejected because | -|---------|------------------| -| `funes` | Too obscure as standalone keyword | -| `recall` | Sounds like a function call | -| `archive` | More Library of Babel than Funes | +| Keyword | Rejected because | +| --------- | --------------------------------- | +| `funes` | Too obscure as standalone keyword | +| `recall` | Sounds like a function call | +| `archive` | More Library of Babel than Funes | --- diff --git a/extensions/open-prose/skills/prose/alts/folk.md b/extensions/open-prose/skills/prose/alts/folk.md index 6d7a4edf2e..bb4607162f 100644 --- a/extensions/open-prose/skills/prose/alts/folk.md +++ b/extensions/open-prose/skills/prose/alts/folk.md @@ -29,55 +29,55 @@ An alternative register for OpenProse that leans into literary, theatrical, and ### Core Constructs -| Functional | Folk | Origin | Connotation | -|------------|------|--------|-------------| -| `agent` | `sprite` | Folklore | Quick, light, ephemeral spirit helper | -| `session` | `scene` | Theatre | A moment of action, theatrical framing | -| `parallel` | `ensemble` | Theatre | Everyone performs together | -| `block` | `act` | Theatre | Reusable unit of dramatic action | +| Functional | Folk | Origin | Connotation | +| ---------- | ---------- | -------- | -------------------------------------- | +| `agent` | `sprite` | Folklore | Quick, light, ephemeral spirit helper | +| `session` | `scene` | Theatre | A moment of action, theatrical framing | +| `parallel` | `ensemble` | Theatre | Everyone performs together | +| `block` | `act` | Theatre | Reusable unit of dramatic action | ### Composition & Binding -| Functional | Folk | Origin | Connotation | -|------------|------|--------|-------------| -| `use` | `summon` | Folklore | Calling forth from elsewhere | -| `input` | `given` | Fairy tale | "Given a magic sword..." | -| `output` | `yield` | Agriculture/magic | What the spell produces | -| `let` | `name` | Folklore | Naming has power (true names) | -| `const` | `seal` | Medieval | Unchangeable, wax seal on decree | -| `context` | `bearing` | Heraldry | What the messenger carries | +| Functional | Folk | Origin | Connotation | +| ---------- | --------- | ----------------- | -------------------------------- | +| `use` | `summon` | Folklore | Calling forth from elsewhere | +| `input` | `given` | Fairy tale | "Given a magic sword..." | +| `output` | `yield` | Agriculture/magic | What the spell produces | +| `let` | `name` | Folklore | Naming has power (true names) | +| `const` | `seal` | Medieval | Unchangeable, wax seal on decree | +| `context` | `bearing` | Heraldry | What the messenger carries | ### Control Flow -| Functional | Folk | Origin | Connotation | -|------------|------|--------|-------------| -| `repeat N` | `N times` | Fairy tale | "Three times she called..." | -| `for...in` | `for each...among` | Narrative | Slightly more storytelling | -| `loop` | `loop` | — | Already poetic, unchanged | -| `until` | `until` | — | Already works, unchanged | -| `while` | `while` | — | Already works, unchanged | -| `choice` | `crossroads` | Folklore | Fateful decisions at the crossroads | -| `option` | `path` | Journey | Which path to take | -| `if` | `when` | Narrative | "When the moon rises..." | -| `elif` | `or when` | Narrative | Continued conditional | -| `else` | `otherwise` | Storytelling | Natural narrative alternative | +| Functional | Folk | Origin | Connotation | +| ---------- | ------------------ | ------------ | ----------------------------------- | +| `repeat N` | `N times` | Fairy tale | "Three times she called..." | +| `for...in` | `for each...among` | Narrative | Slightly more storytelling | +| `loop` | `loop` | — | Already poetic, unchanged | +| `until` | `until` | — | Already works, unchanged | +| `while` | `while` | — | Already works, unchanged | +| `choice` | `crossroads` | Folklore | Fateful decisions at the crossroads | +| `option` | `path` | Journey | Which path to take | +| `if` | `when` | Narrative | "When the moon rises..." | +| `elif` | `or when` | Narrative | Continued conditional | +| `else` | `otherwise` | Storytelling | Natural narrative alternative | ### Error Handling -| Functional | Folk | Origin | Connotation | -|------------|------|--------|-------------| -| `try` | `venture` | Adventure | Attempting something uncertain | -| `catch` | `should it fail` | Narrative | Conditional failure handling | -| `finally` | `ever after` | Fairy tale | "And ever after..." | -| `throw` | `cry` | Drama | Raising alarm, calling out | -| `retry` | `persist` | Quest | Keep trying against odds | +| Functional | Folk | Origin | Connotation | +| ---------- | ---------------- | ---------- | ------------------------------ | +| `try` | `venture` | Adventure | Attempting something uncertain | +| `catch` | `should it fail` | Narrative | Conditional failure handling | +| `finally` | `ever after` | Fairy tale | "And ever after..." | +| `throw` | `cry` | Drama | Raising alarm, calling out | +| `retry` | `persist` | Quest | Keep trying against odds | ### Session Properties -| Functional | Folk | Origin | Connotation | -|------------|------|--------|-------------| -| `prompt` | `charge` | Chivalry | Giving a quest or duty | -| `model` | `voice` | Theatre | Which voice speaks | +| Functional | Folk | Origin | Connotation | +| ---------- | -------- | -------- | ---------------------- | +| `prompt` | `charge` | Chivalry | Giving a quest or duty | +| `model` | `voice` | Theatre | Which voice speaks | ### Unchanged @@ -274,39 +274,39 @@ perform review("quantum computing") ### For `sprite` (ephemeral agent) -| Keyword | Origin | Rejected because | -|---------|--------|------------------| -| `spark` | English | Good but less folklore | -| `wisp` | English | Too insubstantial | -| `herald` | English | More messenger than worker | -| `courier` | French | Good functional alternative, not literary | -| `envoy` | French | Formal, diplomatic | +| Keyword | Origin | Rejected because | +| --------- | ------- | ----------------------------------------- | +| `spark` | English | Good but less folklore | +| `wisp` | English | Too insubstantial | +| `herald` | English | More messenger than worker | +| `courier` | French | Good functional alternative, not literary | +| `envoy` | French | Formal, diplomatic | ### For `shade` (persistent agent, if implemented) -| Keyword | Origin | Rejected because | -|---------|--------|------------------| -| `daemon` | Greek/Unix | Unix "always running" connotation | -| `oracle` | Greek | Too "read-only" feeling | -| `spirit` | Latin | Too close to `sprite` | -| `specter` | Latin | Negative/spooky connotation | -| `genius` | Roman | Overloaded (smart person) | +| Keyword | Origin | Rejected because | +| --------- | ---------- | --------------------------------- | +| `daemon` | Greek/Unix | Unix "always running" connotation | +| `oracle` | Greek | Too "read-only" feeling | +| `spirit` | Latin | Too close to `sprite` | +| `specter` | Latin | Negative/spooky connotation | +| `genius` | Roman | Overloaded (smart person) | ### For `ensemble` (parallel) -| Keyword | Origin | Rejected because | -|---------|--------|------------------| -| `chorus` | Greek | Everyone speaks same thing, not different | -| `troupe` | French | Good alternative, slightly less clear | -| `company` | Theatre | Overloaded (business) | +| Keyword | Origin | Rejected because | +| --------- | ------- | ----------------------------------------- | +| `chorus` | Greek | Everyone speaks same thing, not different | +| `troupe` | French | Good alternative, slightly less clear | +| `company` | Theatre | Overloaded (business) | ### For `crossroads` (choice) -| Keyword | Origin | Rejected because | -|---------|--------|------------------| -| `fork` | Path | Too technical (git fork) | -| `branch` | Tree | Also too technical | -| `divergence` | Latin | Too abstract | +| Keyword | Origin | Rejected because | +| ------------ | ------ | ------------------------ | +| `fork` | Path | Too technical (git fork) | +| `branch` | Tree | Also too technical | +| `divergence` | Latin | Too abstract | --- diff --git a/extensions/open-prose/skills/prose/alts/homer.md b/extensions/open-prose/skills/prose/alts/homer.md index 9d1d595540..bc27905cf7 100644 --- a/extensions/open-prose/skills/prose/alts/homer.md +++ b/extensions/open-prose/skills/prose/alts/homer.md @@ -28,55 +28,55 @@ An alternative register for OpenProse that draws from Greek epic poetry—the Il ### Core Constructs -| Functional | Homeric | Reference | -|------------|---------|-----------| -| `agent` | `hero` | The one who acts, who strives | -| `session` | `trial` | Each task is a labor, a test | -| `parallel` | `host` | An army moving as one | -| `block` | `book` | A division of the epic | +| Functional | Homeric | Reference | +| ---------- | ------- | ----------------------------- | +| `agent` | `hero` | The one who acts, who strives | +| `session` | `trial` | Each task is a labor, a test | +| `parallel` | `host` | An army moving as one | +| `block` | `book` | A division of the epic | ### Composition & Binding -| Functional | Homeric | Reference | -|------------|---------|-----------| -| `use` | `invoke` | "Sing, O Muse..." — calling upon | -| `input` | `omen` | Signs from the gods, the given portent | -| `output` | `glory` | Kleos — the glory won, what endures | -| `let` | `decree` | Fate declared, spoken into being | -| `const` | `fate` | Moira — unchangeable destiny | -| `context` | `tidings` | News carried by herald or messenger | +| Functional | Homeric | Reference | +| ---------- | --------- | -------------------------------------- | +| `use` | `invoke` | "Sing, O Muse..." — calling upon | +| `input` | `omen` | Signs from the gods, the given portent | +| `output` | `glory` | Kleos — the glory won, what endures | +| `let` | `decree` | Fate declared, spoken into being | +| `const` | `fate` | Moira — unchangeable destiny | +| `context` | `tidings` | News carried by herald or messenger | ### Control Flow -| Functional | Homeric | Reference | -|------------|---------|-----------| -| `repeat N` | `N labors` | The labors of Heracles | -| `for...in` | `for each...among` | Among the host | -| `loop` | `ordeal` | Repeated trial, suffering that continues | -| `until` | `until` | Unchanged | -| `while` | `while` | Unchanged | -| `choice` | `crossroads` | Where fates diverge | -| `option` | `path` | One road of many | -| `if` | `should` | Epic conditional | -| `elif` | `or should` | Continued conditional | -| `else` | `otherwise` | The alternative fate | +| Functional | Homeric | Reference | +| ---------- | ------------------ | ---------------------------------------- | +| `repeat N` | `N labors` | The labors of Heracles | +| `for...in` | `for each...among` | Among the host | +| `loop` | `ordeal` | Repeated trial, suffering that continues | +| `until` | `until` | Unchanged | +| `while` | `while` | Unchanged | +| `choice` | `crossroads` | Where fates diverge | +| `option` | `path` | One road of many | +| `if` | `should` | Epic conditional | +| `elif` | `or should` | Continued conditional | +| `else` | `otherwise` | The alternative fate | ### Error Handling -| Functional | Homeric | Reference | -|------------|---------|-----------| -| `try` | `venture` | Setting forth on the journey | -| `catch` | `should ruin come` | Até — divine ruin, disaster | -| `finally` | `in the end` | The inevitable conclusion | -| `throw` | `lament` | The hero's cry of anguish | -| `retry` | `persist` | Enduring, trying again | +| Functional | Homeric | Reference | +| ---------- | ------------------ | ---------------------------- | +| `try` | `venture` | Setting forth on the journey | +| `catch` | `should ruin come` | Até — divine ruin, disaster | +| `finally` | `in the end` | The inevitable conclusion | +| `throw` | `lament` | The hero's cry of anguish | +| `retry` | `persist` | Enduring, trying again | ### Session Properties -| Functional | Homeric | Reference | -|------------|---------|-----------| -| `prompt` | `charge` | The quest given | -| `model` | `muse` | Which muse inspires | +| Functional | Homeric | Reference | +| ---------- | -------- | ------------------- | +| `prompt` | `charge` | The quest given | +| `model` | `muse` | Which muse inspires | ### Unchanged @@ -296,14 +296,14 @@ fate config = { muse: "opus", persist: 3 } ## Key Homeric Concepts -| Term | Meaning | Used for | -|------|---------|----------| -| Kleos | Glory, fame that outlives you | `output` → `glory` | -| Moira | Fate, one's allotted portion | `const` → `fate` | -| Até | Divine ruin, blindness sent by gods | `catch` → `should ruin come` | -| Nostos | The return journey | (not used, but could be `finally`) | -| Xenia | Guest-friendship, hospitality | (not used) | -| Muse | Divine inspiration | `model` → `muse` | +| Term | Meaning | Used for | +| ------ | ----------------------------------- | ---------------------------------- | +| Kleos | Glory, fame that outlives you | `output` → `glory` | +| Moira | Fate, one's allotted portion | `const` → `fate` | +| Até | Divine ruin, blindness sent by gods | `catch` → `should ruin come` | +| Nostos | The return journey | (not used, but could be `finally`) | +| Xenia | Guest-friendship, hospitality | (not used) | +| Muse | Divine inspiration | `model` → `muse` | --- @@ -311,27 +311,27 @@ fate config = { muse: "opus", persist: 3 } ### For `hero` (agent) -| Keyword | Rejected because | -|---------|------------------| -| `champion` | More medieval than Homeric | -| `warrior` | Too martial, not all tasks are battles | -| `wanderer` | Too passive | +| Keyword | Rejected because | +| ---------- | -------------------------------------- | +| `champion` | More medieval than Homeric | +| `warrior` | Too martial, not all tasks are battles | +| `wanderer` | Too passive | ### For `trial` (session) -| Keyword | Rejected because | -|---------|------------------| +| Keyword | Rejected because | +| ------- | --------------------------------------- | | `labor` | Good but reserved for `repeat N labors` | -| `quest` | More medieval/RPG | -| `task` | Too plain | +| `quest` | More medieval/RPG | +| `task` | Too plain | ### For `host` (parallel) -| Keyword | Rejected because | -|---------|------------------| -| `army` | Too specifically martial | -| `fleet` | Only works for naval metaphors | -| `phalanx` | Too technical | +| Keyword | Rejected because | +| --------- | ------------------------------ | +| `army` | Too specifically martial | +| `fleet` | Only works for naval metaphors | +| `phalanx` | Too technical | --- diff --git a/extensions/open-prose/skills/prose/alts/kafka.md b/extensions/open-prose/skills/prose/alts/kafka.md index 8270f30938..1068ac8b8a 100644 --- a/extensions/open-prose/skills/prose/alts/kafka.md +++ b/extensions/open-prose/skills/prose/alts/kafka.md @@ -28,55 +28,55 @@ An alternative register for OpenProse that draws from the works of Franz Kafka ### Core Constructs -| Functional | Kafka | Reference | -|------------|-------|-----------| -| `agent` | `clerk` | A functionary in the apparatus | -| `session` | `proceeding` | An official action taken | +| Functional | Kafka | Reference | +| ---------- | ------------- | -------------------------------------- | +| `agent` | `clerk` | A functionary in the apparatus | +| `session` | `proceeding` | An official action taken | | `parallel` | `departments` | Multiple bureaus acting simultaneously | -| `block` | `regulation` | A codified procedure | +| `block` | `regulation` | A codified procedure | ### Composition & Binding -| Functional | Kafka | Reference | -|------------|-------|-----------| -| `use` | `requisition` | Requesting from the archives | -| `input` | `petition` | What is submitted for consideration | -| `output` | `verdict` | What is returned by the apparatus | -| `let` | `file` | Recording in the system | -| `const` | `statute` | Unchangeable law | -| `context` | `dossier` | The accumulated file on a case | +| Functional | Kafka | Reference | +| ---------- | ------------- | ----------------------------------- | +| `use` | `requisition` | Requesting from the archives | +| `input` | `petition` | What is submitted for consideration | +| `output` | `verdict` | What is returned by the apparatus | +| `let` | `file` | Recording in the system | +| `const` | `statute` | Unchangeable law | +| `context` | `dossier` | The accumulated file on a case | ### Control Flow -| Functional | Kafka | Reference | -|------------|-------|-----------| -| `repeat N` | `N hearings` | Repeated appearances before the court | -| `for...in` | `for each...in the matter of` | Bureaucratic iteration | -| `loop` | `appeal` | Endless re-petition, the process continues | -| `until` | `until` | Unchanged | -| `while` | `while` | Unchanged | -| `choice` | `tribunal` | Where judgment is rendered | -| `option` | `ruling` | One possible judgment | -| `if` | `in the event that` | Bureaucratic conditional | -| `elif` | `or in the event that` | Continued conditional | -| `else` | `otherwise` | Default ruling | +| Functional | Kafka | Reference | +| ---------- | ----------------------------- | ------------------------------------------ | +| `repeat N` | `N hearings` | Repeated appearances before the court | +| `for...in` | `for each...in the matter of` | Bureaucratic iteration | +| `loop` | `appeal` | Endless re-petition, the process continues | +| `until` | `until` | Unchanged | +| `while` | `while` | Unchanged | +| `choice` | `tribunal` | Where judgment is rendered | +| `option` | `ruling` | One possible judgment | +| `if` | `in the event that` | Bureaucratic conditional | +| `elif` | `or in the event that` | Continued conditional | +| `else` | `otherwise` | Default ruling | ### Error Handling -| Functional | Kafka | Reference | -|------------|-------|-----------| -| `try` | `submit` | Submitting for processing | -| `catch` | `should it be denied` | Rejection by the apparatus | -| `finally` | `regardless` | What happens no matter the outcome | -| `throw` | `reject` | The system refuses | -| `retry` | `resubmit` | Try the process again | +| Functional | Kafka | Reference | +| ---------- | --------------------- | ---------------------------------- | +| `try` | `submit` | Submitting for processing | +| `catch` | `should it be denied` | Rejection by the apparatus | +| `finally` | `regardless` | What happens no matter the outcome | +| `throw` | `reject` | The system refuses | +| `retry` | `resubmit` | Try the process again | ### Session Properties -| Functional | Kafka | Reference | -|------------|-------|-----------| -| `prompt` | `directive` | Official instructions | -| `model` | `authority` | Which level of the hierarchy | +| Functional | Kafka | Reference | +| ---------- | ----------- | ---------------------------- | +| `prompt` | `directive` | Official instructions | +| `model` | `authority` | Which level of the hierarchy | ### Unchanged @@ -280,7 +280,7 @@ statute config = { authority: "opus", resubmit: 3 } ## The Case For Kafka 1. **Darkly comic.** Programs-as-bureaucracy is funny and relatable. -2. **Surprisingly apt.** Software often *is* an inscrutable apparatus. +2. **Surprisingly apt.** Software often _is_ an inscrutable apparatus. 3. **Clean mappings.** Petition/verdict, file/dossier, clerk/proceeding all work well. 4. **Appeal as loop.** The endless appeal process is a perfect metaphor for retry logic. 5. **Cultural resonance.** "Kafkaesque" is a widely understood adjective. @@ -297,15 +297,15 @@ statute config = { authority: "opus", resubmit: 3 } ## Key Kafka Concepts -| Term | Meaning | Used for | -|------|---------|----------| -| The apparatus | The inscrutable system | The VM itself | -| K. | The protagonist, never fully named | The user | -| The Trial | Process without clear rules | Program execution | -| The Castle | Unreachable authority | Higher-level systems | -| Clerk | Functionary who processes | `agent` → `clerk` | -| Proceeding | Official action | `session` → `proceeding` | -| Dossier | Accumulated file | `context` → `dossier` | +| Term | Meaning | Used for | +| ------------- | ---------------------------------- | ------------------------ | +| The apparatus | The inscrutable system | The VM itself | +| K. | The protagonist, never fully named | The user | +| The Trial | Process without clear rules | Program execution | +| The Castle | Unreachable authority | Higher-level systems | +| Clerk | Functionary who processes | `agent` → `clerk` | +| Proceeding | Official action | `session` → `proceeding` | +| Dossier | Accumulated file | `context` → `dossier` | --- @@ -313,37 +313,37 @@ statute config = { authority: "opus", resubmit: 3 } ### For `clerk` (agent) -| Keyword | Rejected because | -|---------|------------------| -| `official` | Too generic | -| `functionary` | Hard to spell | -| `bureaucrat` | Too pejorative | -| `advocate` | Too positive/helpful | +| Keyword | Rejected because | +| ------------- | -------------------- | +| `official` | Too generic | +| `functionary` | Hard to spell | +| `bureaucrat` | Too pejorative | +| `advocate` | Too positive/helpful | ### For `proceeding` (session) -| Keyword | Rejected because | -|---------|------------------| -| `case` | Overloaded (switch case) | +| Keyword | Rejected because | +| --------- | -------------------------------- | +| `case` | Overloaded (switch case) | | `hearing` | Reserved for `repeat N hearings` | -| `trial` | Used in Homeric register | -| `process` | Too technical | +| `trial` | Used in Homeric register | +| `process` | Too technical | ### For `departments` (parallel) -| Keyword | Rejected because | -|---------|------------------| -| `bureaus` | Good alternative, slightly less clear | -| `offices` | Too mundane | -| `ministries` | More Orwellian than Kafkaesque | +| Keyword | Rejected because | +| ------------ | ------------------------------------- | +| `bureaus` | Good alternative, slightly less clear | +| `offices` | Too mundane | +| `ministries` | More Orwellian than Kafkaesque | ### For `appeal` (loop) -| Keyword | Rejected because | -|---------|------------------| +| Keyword | Rejected because | +| ---------- | ------------------- | | `recourse` | Too legal-technical | -| `petition` | Used for `input` | -| `process` | Too generic | +| `petition` | Used for `input` | +| `process` | Too generic | --- @@ -368,6 +368,6 @@ Not recommended for: ## Closing Note > "Someone must have slandered Josef K., for one morning, without having done anything wrong, he was arrested." -> — *The Trial* +> — _The Trial_ In the Kafka register, your program is Josef K. The apparatus will process it. Whether it succeeds or fails, no one can say for certain. But the proceedings will continue. diff --git a/extensions/open-prose/skills/prose/compiler.md b/extensions/open-prose/skills/prose/compiler.md index c4e35b0ce0..1220fa745f 100644 --- a/extensions/open-prose/skills/prose/compiler.md +++ b/extensions/open-prose/skills/prose/compiler.md @@ -98,59 +98,59 @@ OpenProse provides a declarative syntax for defining multi-agent workflows. Prog The following features are implemented: -| Feature | Status | Description | -| ---------------------- | ----------- | ---------------------------------------------- | -| Comments | Implemented | `# comment` syntax | -| Single-line strings | Implemented | `"string"` with escapes | -| Simple session | Implemented | `session "prompt"` | -| Agent definitions | Implemented | `agent name:` with model/prompt properties | -| Session with agent | Implemented | `session: agent` with property overrides | -| Use statements | Implemented | `use "@handle/slug" as name` | -| Agent skills | Implemented | `skills: ["skill1", "skill2"]` | -| Agent permissions | Implemented | `permissions:` block with rules | -| Let binding | Implemented | `let name = session "..."` | -| Const binding | Implemented | `const name = session "..."` | -| Variable reassignment | Implemented | `name = session "..."` (for let only) | -| Context property | Implemented | `context: var` or `context: [a, b, c]` | -| do: blocks | Implemented | Explicit sequential blocks | -| Inline sequence | Implemented | `session "A" -> session "B"` | -| Named blocks | Implemented | `block name:` with `do name` invocation | -| Parallel blocks | Implemented | `parallel:` for concurrent execution | -| Named parallel results | Implemented | `x = session "..."` inside parallel | -| Object context | Implemented | `context: { a, b, c }` shorthand | -| Join strategies | Implemented | `parallel ("first"):` or `parallel ("any"):` | -| Failure policies | Implemented | `parallel (on-fail: "continue"):` | -| Repeat blocks | Implemented | `repeat N:` fixed iterations | -| Repeat with index | Implemented | `repeat N as i:` with index variable | -| For-each blocks | Implemented | `for item in items:` iteration | -| For-each with index | Implemented | `for item, i in items:` with index | -| Parallel for-each | Implemented | `parallel for item in items:` fan-out | -| Unbounded loop | Implemented | `loop:` with optional max iterations | -| Loop until | Implemented | `loop until **condition**:` AI-evaluated | -| Loop while | Implemented | `loop while **condition**:` AI-evaluated | -| Loop with index | Implemented | `loop as i:` or `loop until ... as i:` | -| Map pipeline | Implemented | `items \| map:` transform each item | -| Filter pipeline | Implemented | `items \| filter:` keep matching items | -| Reduce pipeline | Implemented | `items \| reduce(acc, item):` accumulate | -| Parallel map | Implemented | `items \| pmap:` concurrent transform | -| Pipeline chaining | Implemented | `\| filter: ... \| map: ...` | -| Try/catch blocks | Implemented | `try:` with `catch:` for error handling | -| Try/catch/finally | Implemented | `finally:` for cleanup | -| Error variable | Implemented | `catch as err:` access error context | -| Throw statement | Implemented | `throw` or `throw "message"` | -| Retry property | Implemented | `retry: 3` automatic retry on failure | -| Backoff strategy | Implemented | `backoff: exponential` delay between retries | -| Input declarations | Implemented | `input name: "description"` | -| Output bindings | Implemented | `output name = expression` | -| Program invocation | Implemented | `name(input: value)` call imported programs | -| Multi-line strings | Implemented | `"""..."""` preserving whitespace | -| String interpolation | Implemented | `"Hello {name}"` variable substitution | -| Block parameters | Implemented | `block name(param):` with parameters | -| Block invocation args | Implemented | `do name(arg)` passing arguments | -| Choice blocks | Implemented | `choice **criteria**: option "label":` | -| If/elif/else | Implemented | `if **condition**:` conditional branching | -| Persistent agents | Implemented | `persist: true` or `persist: project` | -| Resume statement | Implemented | `resume: agent` to continue with memory | +| Feature | Status | Description | +| ---------------------- | ----------- | -------------------------------------------- | +| Comments | Implemented | `# comment` syntax | +| Single-line strings | Implemented | `"string"` with escapes | +| Simple session | Implemented | `session "prompt"` | +| Agent definitions | Implemented | `agent name:` with model/prompt properties | +| Session with agent | Implemented | `session: agent` with property overrides | +| Use statements | Implemented | `use "@handle/slug" as name` | +| Agent skills | Implemented | `skills: ["skill1", "skill2"]` | +| Agent permissions | Implemented | `permissions:` block with rules | +| Let binding | Implemented | `let name = session "..."` | +| Const binding | Implemented | `const name = session "..."` | +| Variable reassignment | Implemented | `name = session "..."` (for let only) | +| Context property | Implemented | `context: var` or `context: [a, b, c]` | +| do: blocks | Implemented | Explicit sequential blocks | +| Inline sequence | Implemented | `session "A" -> session "B"` | +| Named blocks | Implemented | `block name:` with `do name` invocation | +| Parallel blocks | Implemented | `parallel:` for concurrent execution | +| Named parallel results | Implemented | `x = session "..."` inside parallel | +| Object context | Implemented | `context: { a, b, c }` shorthand | +| Join strategies | Implemented | `parallel ("first"):` or `parallel ("any"):` | +| Failure policies | Implemented | `parallel (on-fail: "continue"):` | +| Repeat blocks | Implemented | `repeat N:` fixed iterations | +| Repeat with index | Implemented | `repeat N as i:` with index variable | +| For-each blocks | Implemented | `for item in items:` iteration | +| For-each with index | Implemented | `for item, i in items:` with index | +| Parallel for-each | Implemented | `parallel for item in items:` fan-out | +| Unbounded loop | Implemented | `loop:` with optional max iterations | +| Loop until | Implemented | `loop until **condition**:` AI-evaluated | +| Loop while | Implemented | `loop while **condition**:` AI-evaluated | +| Loop with index | Implemented | `loop as i:` or `loop until ... as i:` | +| Map pipeline | Implemented | `items \| map:` transform each item | +| Filter pipeline | Implemented | `items \| filter:` keep matching items | +| Reduce pipeline | Implemented | `items \| reduce(acc, item):` accumulate | +| Parallel map | Implemented | `items \| pmap:` concurrent transform | +| Pipeline chaining | Implemented | `\| filter: ... \| map: ...` | +| Try/catch blocks | Implemented | `try:` with `catch:` for error handling | +| Try/catch/finally | Implemented | `finally:` for cleanup | +| Error variable | Implemented | `catch as err:` access error context | +| Throw statement | Implemented | `throw` or `throw "message"` | +| Retry property | Implemented | `retry: 3` automatic retry on failure | +| Backoff strategy | Implemented | `backoff: exponential` delay between retries | +| Input declarations | Implemented | `input name: "description"` | +| Output bindings | Implemented | `output name = expression` | +| Program invocation | Implemented | `name(input: value)` call imported programs | +| Multi-line strings | Implemented | `"""..."""` preserving whitespace | +| String interpolation | Implemented | `"Hello {name}"` variable substitution | +| Block parameters | Implemented | `block name(param):` with parameters | +| Block invocation args | Implemented | `do name(arg)` passing arguments | +| Choice blocks | Implemented | `choice **criteria**: option "label":` | +| If/elif/else | Implemented | `if **condition**:` conditional branching | +| Persistent agents | Implemented | `persist: true` or `persist: project` | +| Resume statement | Implemented | `resume: agent` to continue with memory | --- @@ -351,6 +351,7 @@ use "@handle/slug" as alias ### Path Format Import paths follow the format `@handle/slug`: + - `@handle` identifies the program author/organization - `slug` is the program name @@ -413,6 +414,7 @@ input depth: "How deep to go (shallow, medium, deep)" ### Semantics Inputs: + - Are declared at the top of the program (before executable statements) - Have a name and a description (for documentation) - Become available as variables within the program body @@ -420,11 +422,11 @@ Inputs: ### Validation Rules -| Check | Severity | Message | -| ---------------------- | -------- | ------------------------------------ | -| Empty input name | Error | Input name cannot be empty | -| Empty description | Warning | Consider adding a description | -| Duplicate input name | Error | Input already declared | +| Check | Severity | Message | +| ---------------------- | -------- | ---------------------------------------------------- | +| Empty input name | Error | Input name cannot be empty | +| Empty description | Warning | Consider adding a description | +| Duplicate input name | Error | Input already declared | | Input after executable | Error | Inputs must be declared before executable statements | --- @@ -452,6 +454,7 @@ output sources = session "Extract sources" ### Semantics The `output` keyword: + - Marks a variable as an output (visible at assignment, not just at file top) - Works like `let` but also registers the value as a program output - Can appear anywhere in the program body @@ -459,11 +462,11 @@ The `output` keyword: ### Validation Rules -| Check | Severity | Message | -| ---------------------- | -------- | ------------------------------------ | -| Empty output name | Error | Output name cannot be empty | -| Duplicate output name | Error | Output already declared | -| Output name conflicts | Error | Output name conflicts with variable | +| Check | Severity | Message | +| --------------------- | -------- | ----------------------------------- | +| Empty output name | Error | Output name cannot be empty | +| Duplicate output name | Error | Output already declared | +| Output name conflicts | Error | Output name conflicts with variable | --- @@ -518,12 +521,12 @@ The imported program runs in its own execution context but shares the same VM se ### Validation Rules -| Check | Severity | Message | -| ------------------------ | -------- | ------------------------------------ | -| Unknown program | Error | Program not imported | -| Missing required input | Error | Required input not provided | -| Unknown input name | Error | Input not declared in program | -| Unknown output property | Error | Output not declared in program | +| Check | Severity | Message | +| ----------------------- | -------- | ------------------------------ | +| Unknown program | Error | Program not imported | +| Missing required input | Error | Required input not provided | +| Unknown input name | Error | Input not declared in program | +| Unknown output property | Error | Output not declared in program | --- @@ -545,13 +548,13 @@ agent name: ### Properties -| Property | Type | Values | Description | -| ------------- | ---------- | ------------------------------ | ------------------------------------- | -| `model` | identifier | `sonnet`, `opus`, `haiku` | The Claude model to use | -| `prompt` | string | Any string | System prompt/context for the agent | -| `persist` | value | `true`, `project`, or STRING | Enable persistent memory for agent | -| `skills` | array | String array | Skills assigned to this agent | -| `permissions` | block | Permission rules | Access control for the agent | +| Property | Type | Values | Description | +| ------------- | ---------- | ---------------------------- | ----------------------------------- | +| `model` | identifier | `sonnet`, `opus`, `haiku` | The Claude model to use | +| `prompt` | string | Any string | System prompt/context for the agent | +| `persist` | value | `true`, `project`, or STRING | Enable persistent memory for agent | +| `skills` | array | String array | Skills assigned to this agent | +| `permissions` | block | Permission rules | Access control for the agent | ### Persist Property @@ -577,11 +580,11 @@ agent shared: prompt: "Shared across programs" ``` -| Value | Memory Location | Lifetime | -|-------|-----------------|----------| -| `true` | `.prose/runs/{id}/agents/{name}/` | Dies with execution | -| `project` | `.prose/agents/{name}/` | Survives executions | -| STRING | Specified path | User-controlled | +| Value | Memory Location | Lifetime | +| --------- | --------------------------------- | ------------------- | +| `true` | `.prose/runs/{id}/agents/{name}/` | Dies with execution | +| `project` | `.prose/agents/{name}/` | Survives executions | +| STRING | Specified path | User-controlled | ### Skills Property @@ -872,10 +875,10 @@ resume: agentName ### Semantics -| Keyword | Behavior | -|---------|----------| +| Keyword | Behavior | +| ---------- | ------------------------------------- | | `session:` | Ignores existing memory, starts fresh | -| `resume:` | Loads memory, continues with context | +| `resume:` | Loads memory, continues with context | ### Examples @@ -902,12 +905,12 @@ let review = resume: captain ### Validation Rules -| Check | Severity | Message | -|-------|----------|---------| -| `resume:` on non-persistent agent | Error | Agent must have `persist:` property to use `resume:` | -| `resume:` with no existing memory | Error | No memory file exists for agent; use `session:` for first invocation | -| `session:` on persistent agent with memory | Warning | Will ignore existing memory; use `resume:` to continue | -| Undefined agent reference | Error | Agent not defined | +| Check | Severity | Message | +| ------------------------------------------ | -------- | -------------------------------------------------------------------- | +| `resume:` on non-persistent agent | Error | Agent must have `persist:` property to use `resume:` | +| `resume:` with no existing memory | Error | No memory file exists for agent; use `session:` for first invocation | +| `session:` on persistent agent with memory | Warning | Will ignore existing memory; use `resume:` to continue | +| Undefined agent reference | Error | Agent not defined | --- @@ -1047,6 +1050,7 @@ for item in items: **Why this constraint:** Since bindings are stored as `bindings/{name}.md`, two variables with the same name would collide on the filesystem. Rather than introduce complex scoping rules, we enforce uniqueness. **Collision scenarios this prevents:** + 1. Variable inside loop shadows variable outside loop 2. Variables in different `if`/`elif`/`else` branches with same name 3. Block parameters shadowing outer variables @@ -2643,51 +2647,51 @@ The validator checks programs for errors and warnings before execution. ### Errors (Block Execution) -| Code | Description | -| ---- | ----------------------------------- | -| E001 | Unterminated string literal | -| E002 | Unknown escape sequence in string | -| E003 | Session missing prompt or agent | -| E004 | Unexpected token | -| E005 | Invalid syntax | -| E006 | Duplicate agent definition | -| E007 | Undefined agent reference | -| E008 | Invalid model value | -| E009 | Duplicate property | -| E010 | Duplicate use statement | -| E011 | Empty use path | -| E012 | Invalid use path format | -| E013 | Skills must be an array | -| E014 | Skill name must be a string | -| E015 | Permissions must be a block | -| E016 | Permission pattern must be a string | -| E017 | `resume:` requires persistent agent | -| E018 | `resume:` with no existing memory | +| Code | Description | +| ---- | ---------------------------------------- | +| E001 | Unterminated string literal | +| E002 | Unknown escape sequence in string | +| E003 | Session missing prompt or agent | +| E004 | Unexpected token | +| E005 | Invalid syntax | +| E006 | Duplicate agent definition | +| E007 | Undefined agent reference | +| E008 | Invalid model value | +| E009 | Duplicate property | +| E010 | Duplicate use statement | +| E011 | Empty use path | +| E012 | Invalid use path format | +| E013 | Skills must be an array | +| E014 | Skill name must be a string | +| E015 | Permissions must be a block | +| E016 | Permission pattern must be a string | +| E017 | `resume:` requires persistent agent | +| E018 | `resume:` with no existing memory | | E019 | Duplicate variable name (flat namespace) | -| E020 | Empty input name | -| E021 | Duplicate input declaration | -| E022 | Input after executable statement | -| E023 | Empty output name | -| E024 | Duplicate output declaration | -| E025 | Unknown program in invocation | -| E026 | Missing required input | -| E027 | Unknown input name in invocation | -| E028 | Unknown output property access | +| E020 | Empty input name | +| E021 | Duplicate input declaration | +| E022 | Input after executable statement | +| E023 | Empty output name | +| E024 | Duplicate output declaration | +| E025 | Unknown program in invocation | +| E026 | Missing required input | +| E027 | Unknown input name in invocation | +| E028 | Unknown output property access | ### Warnings (Non-blocking) -| Code | Description | -| ---- | ---------------------------------------- | -| W001 | Empty session prompt | -| W002 | Whitespace-only session prompt | -| W003 | Session prompt exceeds 10,000 characters | -| W004 | Empty prompt property | -| W005 | Unknown property name | -| W006 | Unknown import source format | -| W007 | Skill not imported | -| W008 | Unknown permission type | -| W009 | Unknown permission value | -| W010 | Empty skills array | +| Code | Description | +| ---- | --------------------------------------------------- | +| W001 | Empty session prompt | +| W002 | Whitespace-only session prompt | +| W003 | Session prompt exceeds 10,000 characters | +| W004 | Empty prompt property | +| W005 | Unknown property name | +| W006 | Unknown import source format | +| W007 | Skill not imported | +| W008 | Unknown permission type | +| W009 | Unknown permission value | +| W010 | Empty skills array | | W011 | `session:` on persistent agent with existing memory | ### Error Message Format diff --git a/extensions/open-prose/skills/prose/examples/README.md b/extensions/open-prose/skills/prose/examples/README.md index cd2eb5ef44..abad3f24ea 100644 --- a/extensions/open-prose/skills/prose/examples/README.md +++ b/extensions/open-prose/skills/prose/examples/README.md @@ -108,13 +108,13 @@ These examples demonstrate workflows using OpenProse's full feature set. ### Meta / Self-Hosting (44-48) -| File | Description | -| --------------------------------- | ------------------------------------------------------ | -| `44-run-endpoint-ux-test.prose` | Concurrent agents testing the /run API endpoint | -| `45-plugin-release.prose` | OpenProse plugin release workflow (this repo) | -| `46-workflow-crystallizer.prose` | Reflective: observes thread, extracts workflow, writes .prose | +| File | Description | +| ------------------------------------ | ------------------------------------------------------------------ | +| `44-run-endpoint-ux-test.prose` | Concurrent agents testing the /run API endpoint | +| `45-plugin-release.prose` | OpenProse plugin release workflow (this repo) | +| `46-workflow-crystallizer.prose` | Reflective: observes thread, extracts workflow, writes .prose | | `47-language-self-improvement.prose` | Meta-level 2: analyzes .prose corpus to evolve the language itself | -| `48-habit-miner.prose` | Mines AI session logs for patterns, generates .prose automations | +| `48-habit-miner.prose` | Mines AI session logs for patterns, generates .prose automations | ## The Architect By Simulation Pattern diff --git a/extensions/open-prose/skills/prose/examples/roadmap/README.md b/extensions/open-prose/skills/prose/examples/roadmap/README.md index f5d7d0ce73..b2fb56cc80 100644 --- a/extensions/open-prose/skills/prose/examples/roadmap/README.md +++ b/extensions/open-prose/skills/prose/examples/roadmap/README.md @@ -6,14 +6,14 @@ They are included to show the direction of the language and gather feedback on t ## Planned Features -| Feature | Status | Example File | -|---------|--------|--------------| -| Agent definitions | Planned | `simple-pipeline.prose` | -| Named sessions | Planned | `simple-pipeline.prose` | -| Parallel execution | Planned | `parallel-review.prose` | -| Variables & context | Planned | `iterative-refinement.prose` | -| Loops & conditionals | Planned | `iterative-refinement.prose` | -| Imports | Planned | `syntax/open-prose-syntax.prose` | +| Feature | Status | Example File | +| -------------------- | ------- | -------------------------------- | +| Agent definitions | Planned | `simple-pipeline.prose` | +| Named sessions | Planned | `simple-pipeline.prose` | +| Parallel execution | Planned | `parallel-review.prose` | +| Variables & context | Planned | `iterative-refinement.prose` | +| Loops & conditionals | Planned | `iterative-refinement.prose` | +| Imports | Planned | `syntax/open-prose-syntax.prose` | ## Do Not Run These Examples diff --git a/extensions/open-prose/skills/prose/guidance/patterns.md b/extensions/open-prose/skills/prose/guidance/patterns.md index af2f128365..0d77e575e3 100644 --- a/extensions/open-prose/skills/prose/guidance/patterns.md +++ b/extensions/open-prose/skills/prose/guidance/patterns.md @@ -200,26 +200,26 @@ session "Execute main workflow" Match model capability to task complexity: -| Model | Best For | Examples | -|-------|----------|----------| -| **Sonnet 4.5** | Orchestration, control flow, coordination | VM execution, captain's chair, workflow routing | -| **Opus 4.5** | Hard/difficult work requiring deep reasoning | Complex analysis, strategic decisions, novel problem-solving | -| **Haiku** | Simple, self-evident tasks (use sparingly) | Classification, summarization, formatting | +| Model | Best For | Examples | +| -------------- | -------------------------------------------- | ------------------------------------------------------------ | +| **Sonnet 4.5** | Orchestration, control flow, coordination | VM execution, captain's chair, workflow routing | +| **Opus 4.5** | Hard/difficult work requiring deep reasoning | Complex analysis, strategic decisions, novel problem-solving | +| **Haiku** | Simple, self-evident tasks (use sparingly) | Classification, summarization, formatting | -**Key insight:** Sonnet 4.5 excels at *orchestrating* agents and managing control flow—it's the ideal model for the OpenProse VM itself and for "captain" agents that coordinate work. Opus 4.5 should be reserved for agents doing genuinely difficult intellectual work. Haiku can handle simple tasks but should generally be avoided where quality matters. +**Key insight:** Sonnet 4.5 excels at _orchestrating_ agents and managing control flow—it's the ideal model for the OpenProse VM itself and for "captain" agents that coordinate work. Opus 4.5 should be reserved for agents doing genuinely difficult intellectual work. Haiku can handle simple tasks but should generally be avoided where quality matters. **Detailed task-to-model mapping:** -| Task Type | Model | Rationale | -|-----------|-------|-----------| -| Orchestration, routing, coordination | Sonnet | Fast, good at following structure | -| Investigation, debugging, diagnosis | Sonnet | Structured analysis, checklist-style work | -| Triage, classification, categorization | Sonnet | Clear criteria, deterministic decisions | -| Code review, verification (checklist) | Sonnet | Following defined review criteria | -| Simple implementation, fixes | Sonnet | Applying known patterns | -| Complex multi-file synthesis | Opus | Needs to hold many things in context | -| Novel architecture, strategic planning | Opus | Requires creative problem-solving | -| Ambiguous problems, unclear requirements | Opus | Needs to reason through uncertainty | +| Task Type | Model | Rationale | +| ---------------------------------------- | ------ | ----------------------------------------- | +| Orchestration, routing, coordination | Sonnet | Fast, good at following structure | +| Investigation, debugging, diagnosis | Sonnet | Structured analysis, checklist-style work | +| Triage, classification, categorization | Sonnet | Clear criteria, deterministic decisions | +| Code review, verification (checklist) | Sonnet | Following defined review criteria | +| Simple implementation, fixes | Sonnet | Applying known patterns | +| Complex multi-file synthesis | Opus | Needs to hold many things in context | +| Novel architecture, strategic planning | Opus | Requires creative problem-solving | +| Ambiguous problems, unclear requirements | Opus | Needs to reason through uncertainty | **Rule of thumb:** If you can write a checklist for the task, Sonnet can do it. If the task requires genuine creativity or navigating ambiguity, use Opus. diff --git a/extensions/open-prose/skills/prose/help.md b/extensions/open-prose/skills/prose/help.md index 8199e0cbff..248bf701e3 100644 --- a/extensions/open-prose/skills/prose/help.md +++ b/extensions/open-prose/skills/prose/help.md @@ -33,30 +33,30 @@ Options: - **Learn the syntax**: Show examples from `examples/`, explain the VM model - **Explore possibilities**: Walk through key examples like `37-the-forge.prose` or `28-gas-town.prose` - - --- ## Available Commands -| Command | What it does | -|---------|--------------| -| `prose help` | This help - guides you to what you need | -| `prose run ` | Execute a .prose program | -| `prose compile ` | Validate syntax without running | -| `prose update` | Migrate legacy workspace files | -| `prose examples` | Browse and run example programs | +| Command | What it does | +| ---------------------- | --------------------------------------- | +| `prose help` | This help - guides you to what you need | +| `prose run ` | Execute a .prose program | +| `prose compile ` | Validate syntax without running | +| `prose update` | Migrate legacy workspace files | +| `prose examples` | Browse and run example programs | --- ## Quick Start **Run an example:** + ``` prose run examples/01-hello-world.prose ``` **Create your first program:** + ``` prose help → Select "Build something new" @@ -123,21 +123,22 @@ For complete syntax and validation rules, see `compiler.md`. The `examples/` directory contains 37 example programs: -| Range | Category | -|-------|----------| -| 01-08 | Basics (hello world, research, code review, debugging) | -| 09-12 | Agents and skills | -| 13-15 | Variables and composition | -| 16-19 | Parallel execution | -| 20-21 | Loops and pipelines | -| 22-23 | Error handling | -| 24-27 | Advanced (choice, conditionals, blocks, interpolation) | -| 28 | Gas Town (multi-agent orchestration) | -| 29-31 | Captain's chair pattern (persistent orchestrator) | +| Range | Category | +| ----- | --------------------------------------------------------------------------------- | +| 01-08 | Basics (hello world, research, code review, debugging) | +| 09-12 | Agents and skills | +| 13-15 | Variables and composition | +| 16-19 | Parallel execution | +| 20-21 | Loops and pipelines | +| 22-23 | Error handling | +| 24-27 | Advanced (choice, conditionals, blocks, interpolation) | +| 28 | Gas Town (multi-agent orchestration) | +| 29-31 | Captain's chair pattern (persistent orchestrator) | | 33-36 | Production workflows (PR auto-fix, content pipeline, feature factory, bug hunter) | -| 37 | The Forge (build a browser from scratch) | +| 37 | The Forge (build a browser from scratch) | **Recommended starting points:** + - `01-hello-world.prose` - Simplest possible program - `16-parallel-reviews.prose` - See parallel execution - `37-the-forge.prose` - Watch AI build a web browser diff --git a/extensions/open-prose/skills/prose/lib/README.md b/extensions/open-prose/skills/prose/lib/README.md index df2fde84a1..b1109cdd6e 100644 --- a/extensions/open-prose/skills/prose/lib/README.md +++ b/extensions/open-prose/skills/prose/lib/README.md @@ -6,21 +6,21 @@ Core programs that ship with OpenProse. Production-quality, well-tested programs ### Evaluation & Improvement -| Program | Description | -|---------|-------------| -| `inspector.prose` | Post-run analysis for runtime fidelity and task effectiveness | -| `vm-improver.prose` | Analyzes inspections and proposes PRs to improve the VM | +| Program | Description | +| ------------------------ | -------------------------------------------------------------- | +| `inspector.prose` | Post-run analysis for runtime fidelity and task effectiveness | +| `vm-improver.prose` | Analyzes inspections and proposes PRs to improve the VM | | `program-improver.prose` | Analyzes inspections and proposes PRs to improve .prose source | -| `cost-analyzer.prose` | Token usage and cost pattern analysis | -| `calibrator.prose` | Validates light evaluations against deep evaluations | -| `error-forensics.prose` | Root cause analysis for failed runs | +| `cost-analyzer.prose` | Token usage and cost pattern analysis | +| `calibrator.prose` | Validates light evaluations against deep evaluations | +| `error-forensics.prose` | Root cause analysis for failed runs | ### Memory -| Program | Description | -|---------|-------------| -| `user-memory.prose` | Cross-project persistent personal memory | -| `project-memory.prose` | Project-scoped institutional memory | +| Program | Description | +| ---------------------- | ---------------------------------------- | +| `user-memory.prose` | Cross-project persistent personal memory | +| `project-memory.prose` | Project-scoped institutional memory | ## The Improvement Loop @@ -40,6 +40,7 @@ The evaluation programs form a recursive improvement cycle: ``` Supporting analysis: + - **cost-analyzer** — Where does the money go? Optimization opportunities. - **calibrator** — Are cheap evaluations reliable proxies for expensive ones? - **error-forensics** — Why did a run fail? Root cause analysis. @@ -84,11 +85,13 @@ prose run lib/project-memory.prose --backend sqlite+ The memory programs use persistent agents to accumulate knowledge: **user-memory** (`persist: user`) + - Learns your preferences, decisions, patterns across all projects - Remembers mistakes and lessons learned - Answers questions from accumulated knowledge **project-memory** (`persist: project`) + - Understands this project's architecture and decisions - Tracks why things are the way they are - Answers questions with project-specific context diff --git a/extensions/open-prose/skills/prose/primitives/session.md b/extensions/open-prose/skills/prose/primitives/session.md index c129a1843f..0dde647b6b 100644 --- a/extensions/open-prose/skills/prose/primitives/session.md +++ b/extensions/open-prose/skills/prose/primitives/session.md @@ -98,12 +98,12 @@ Execution scope: **What this tells you:** -| Field | Meaning | -|-------|---------| -| `execution_id` | Unique ID for this specific block invocation | -| `block` | Name of the block you're executing within | -| `depth` | How deep in the call stack (1 = first level) | -| `parent_execution_id` | The invoking frame's ID (for scope chain) | +| Field | Meaning | +| --------------------- | -------------------------------------------- | +| `execution_id` | Unique ID for this specific block invocation | +| `block` | Name of the block you're executing within | +| `depth` | How deep in the call stack (1 = first level) | +| `parent_execution_id` | The invoking frame's ID (for scope chain) | **How to use it:** @@ -125,10 +125,10 @@ If you're a persistent agent, you maintain state across sessions via a memory fi Persistent agents have **two separate outputs** that must not be confused: -| Output | What It Is | Where It Goes | Purpose | -|--------|------------|---------------|---------| -| **Binding** | The result of THIS task | `bindings/{name}.md` or database | Passed to other sessions via `context:` | -| **Memory** | Your accumulated knowledge | `agents/{name}/memory.md` or database | Carried forward to YOUR future invocations | +| Output | What It Is | Where It Goes | Purpose | +| ----------- | -------------------------- | ------------------------------------- | ------------------------------------------ | +| **Binding** | The result of THIS task | `bindings/{name}.md` or database | Passed to other sessions via `context:` | +| **Memory** | Your accumulated knowledge | `agents/{name}/memory.md` or database | Carried forward to YOUR future invocations | **The binding is task-specific.** If you're asked to "review the plan," the binding contains your review. @@ -358,7 +358,7 @@ For regular sessions with output capture (`let x = session "..."`), write to the # {name} kind: {let|const|output|input} -execution_id: {id} # Include if inside a block invocation (omit for root scope) +execution_id: {id} # Include if inside a block invocation (omit for root scope) source: @@ -498,6 +498,7 @@ Summary: {1-2 sentence summary of what's in the binding} ``` **Example (filesystem state, root scope):** + ``` Binding written: research Location: .prose/runs/20260116-143052-a7b3c9/bindings/research.md @@ -505,6 +506,7 @@ Summary: Comprehensive AI safety research covering alignment, robustness, and in ``` **Example (filesystem state, inside block invocation):** + ``` Binding written: result Location: .prose/runs/20260116-143052-a7b3c9/bindings/result__43.md @@ -513,6 +515,7 @@ Summary: Processed chunk into 3 sub-parts for recursive processing. ``` **Example (PostgreSQL state):** + ``` Binding written: research Location: openprose.bindings WHERE name='research' AND run_id='20260116-143052-a7b3c9' @@ -520,6 +523,7 @@ Summary: Comprehensive AI safety research covering alignment, robustness, and in ``` **Example (PostgreSQL state, inside block invocation):** + ``` Binding written: result Location: openprose.bindings WHERE name='result' AND run_id='20260116-143052-a7b3c9' AND execution_id=43 @@ -541,6 +545,7 @@ The VM never holds full binding values in its working memory. This is intentiona Do NOT return your full output in the Task tool response. The VM will ignore it. **Bad:** + ``` Here's my research: @@ -549,6 +554,7 @@ AI safety is a field that studies how to create artificial intelligence systems ``` **Good:** + ``` Binding written: research Location: .prose/runs/20260116-143052-a7b3c9/bindings/research.md diff --git a/extensions/open-prose/skills/prose/prose.md b/extensions/open-prose/skills/prose/prose.md index f7de221f99..32412a6434 100644 --- a/extensions/open-prose/skills/prose/prose.md +++ b/extensions/open-prose/skills/prose/prose.md @@ -28,14 +28,14 @@ This document defines how to execute OpenProse programs. You are the OpenProse V OpenProse is invoked via `prose` commands: -| Command | Action | -|---------|--------| -| `prose run ` | Execute a local `.prose` program | -| `prose run handle/slug` | Fetch from registry and execute | -| `prose compile ` | Validate syntax without executing | -| `prose help` | Show help and examples | -| `prose examples` | List or run bundled examples | -| `prose update` | Migrate legacy workspace files | +| Command | Action | +| ------------------------ | --------------------------------- | +| `prose run ` | Execute a local `.prose` program | +| `prose run handle/slug` | Fetch from registry and execute | +| `prose compile ` | Validate syntax without executing | +| `prose help` | Show help and examples | +| `prose examples` | List or run bundled examples | +| `prose update` | Migrate legacy workspace files | ### Remote Programs @@ -51,11 +51,13 @@ prose run alice/code-review # Fetches https://p.prose.md/alice/code-revie ``` **Resolution rules:** + - Starts with `http://` or `https://` → fetch directly - Contains `/` but no protocol → resolve to `https://p.prose.md/{path}` - Otherwise → treat as local file path This same resolution applies to `use` statements inside programs: + ```prose use "https://example.com/my-program.prose" # Direct URL use "alice/research" as research # Registry shorthand @@ -111,18 +113,18 @@ When you execute a `.prose` program, you ARE the virtual machine. This is not a Traditional dependency injection containers wire up components from configuration. You do the same—but with understanding: -| Declared Primitive | Your Responsibility | -| ---------------------------- | ---------------------------------------------------------- | +| Declared Primitive | Your Responsibility | +| --------------------------- | ---------------------------------------------------------- | | `use "handle/slug" as name` | Fetch program from p.prose.md, register in Import Registry | -| `input topic: "..."` | Bind value from caller, make available as variable | -| `output findings = ...` | Mark value as output, return to caller on completion | -| `agent researcher:` | Register this agent template for later use | -| `session: researcher` | Resolve the agent, merge properties, spawn the session | -| `resume: captain` | Load agent memory, spawn session with memory context | -| `context: { a, b }` | Wire the outputs of `a` and `b` into this session's input | -| `parallel:` branches | Coordinate concurrent execution, collect results | -| `block review(topic):` | Store this reusable component, invoke when called | -| `name(input: value)` | Invoke imported program with inputs, receive outputs | +| `input topic: "..."` | Bind value from caller, make available as variable | +| `output findings = ...` | Mark value as output, return to caller on completion | +| `agent researcher:` | Register this agent template for later use | +| `session: researcher` | Resolve the agent, merge properties, spawn the session | +| `resume: captain` | Load agent memory, spawn session with memory context | +| `context: { a, b }` | Wire the outputs of `a` and `b` into this session's input | +| `parallel:` branches | Coordinate concurrent execution, collect results | +| `block review(topic):` | Store this reusable component, invoke when called | +| `name(input: value)` | Invoke imported program with inputs, receive outputs | You are the container that holds these declarations and wires them together at runtime. The program declares _what_; you determine _how_ to connect them. diff --git a/extensions/open-prose/skills/prose/state/filesystem.md b/extensions/open-prose/skills/prose/state/filesystem.md index 69273437dd..554f2532b7 100644 --- a/extensions/open-prose/skills/prose/state/filesystem.md +++ b/extensions/open-prose/skills/prose/state/filesystem.md @@ -104,12 +104,13 @@ The execution state file shows the program's current position using **annotated **Only the VM writes this file.** Subagents never modify `state.md`. The format shows: + - **Full history** of executed code with inline annotations - **Current position** clearly marked with status - **~5-10 lines ahead** of current position (what's coming next) - **Index** of all bindings and agents with file paths -```markdown +````markdown # Execution State run: 20260115-143052-a7b3c9 @@ -144,6 +145,7 @@ resume: captain # [...next...] prompt: "Review the synthesis" context: synthesis ``` +```` ## Active Constructs @@ -162,26 +164,27 @@ resume: captain # [...next...] ### Bindings -| Name | Kind | Path | Execution ID | -|------|------|------|--------------| -| research | let | bindings/research.md | (root) | -| a | let | bindings/a.md | (root) | -| result | let | bindings/result__43.md | 43 | +| Name | Kind | Path | Execution ID | +| -------- | ---- | ------------------------ | ------------ | +| research | let | bindings/research.md | (root) | +| a | let | bindings/a.md | (root) | +| result | let | bindings/result\_\_43.md | 43 | ### Agents -| Name | Scope | Path | -|------|-------|------| +| Name | Scope | Path | +| ------- | --------- | --------------- | | captain | execution | agents/captain/ | ## Call Stack -| execution_id | block | depth | status | -|--------------|-------|-------|--------| -| 43 | process | 3 | executing | -| 42 | process | 2 | waiting | -| 41 | process | 1 | waiting | -``` +| execution_id | block | depth | status | +| ------------ | ------- | ----- | --------- | +| 43 | process | 3 | executing | +| 42 | process | 2 | waiting | +| 41 | process | 1 | waiting | + +```` **Status annotations:** @@ -209,14 +212,15 @@ source: ```prose let research = session: researcher prompt: "Research AI safety" -``` +```` --- AI safety research covers several key areas including alignment, robustness, and interpretability. The field has grown significantly since 2020 with major contributions from... -``` + +```` **Structure:** - Header with binding name @@ -240,7 +244,7 @@ Sessions without explicit output capture still produce results: ```prose session "Analyze the codebase" # No `let x = ...` capture -``` +```` These get auto-generated names with an `anon_` prefix: @@ -259,25 +263,29 @@ When a binding is created inside a block invocation, it's scoped to that executi **Naming convention:** `{name}__{execution_id}.md` Examples: + - `bindings/result__43.md` — binding `result` in execution_id 43 - `bindings/parts__44.md` — binding `parts` in execution_id 44 **File format with execution scope:** -```markdown +````markdown # result kind: let execution_id: 43 source: + ```prose let result = session "Process chunk" ``` +```` --- Processed chunk into 3 sub-parts... + ``` **Scope resolution:** The VM resolves variable references by checking: @@ -291,15 +299,17 @@ The first match wins. **Example directory for recursive calls:** ``` + bindings/ -├── data.md # Root scope input -├── result__1.md # First process() invocation -├── parts__1.md # Parts from first invocation -├── result__2.md # Recursive call (depth 2) -├── parts__2.md # Parts from depth 2 -├── result__3.md # Recursive call (depth 3) +├── data.md # Root scope input +├── result**1.md # First process() invocation +├── parts**1.md # Parts from first invocation +├── result**2.md # Recursive call (depth 2) +├── parts**2.md # Parts from depth 2 +├── result\_\_3.md # Recursive call (depth 3) └── ... -``` + +```` --- @@ -326,7 +336,7 @@ Architecture uses Express + PostgreSQL. Test coverage target is 80%. - Rate limiting not yet implemented on login endpoint - Need to verify OAuth flow works with new token format -``` +```` #### `agents/{name}/{name}-NNN.md` (Segments) @@ -350,11 +360,11 @@ prompt: "Review the research findings" ## Who Writes What -| File | Written By | -|------|------------| -| `state.md` | VM only | -| `bindings/{name}.md` | Subagent | -| `agents/{name}/memory.md` | Persistent agent | +| File | Written By | +| ----------------------------- | ---------------- | +| `state.md` | VM only | +| `bindings/{name}.md` | Subagent | +| `agents/{name}/memory.md` | Persistent agent | | `agents/{name}/{name}-NNN.md` | Persistent agent | The VM orchestrates; subagents write their own outputs directly to the filesystem. **The VM never holds full binding values—it tracks file paths.** @@ -367,7 +377,7 @@ When the VM spawns a session, it tells the subagent where to write output. ### For Regular Sessions -``` +```` When you complete this task, write your output to: .prose/runs/20260115-143052-a7b3c9/bindings/research.md @@ -380,24 +390,27 @@ source: ```prose let research = session: researcher prompt: "Research AI safety" -``` +```` --- [Your output here] + ``` ### For Persistent Agents (resume:) ``` + Your memory is at: - .prose/runs/20260115-143052-a7b3c9/agents/captain/memory.md +.prose/runs/20260115-143052-a7b3c9/agents/captain/memory.md Read it first to understand your prior context. When done, update it with your compacted state following the guidelines in primitives/session.md. Also write your segment record to: - .prose/runs/20260115-143052-a7b3c9/agents/captain/captain-003.md +.prose/runs/20260115-143052-a7b3c9/agents/captain/captain-003.md + ``` ### What Subagents Return to the VM @@ -406,17 +419,21 @@ After writing output, the subagent returns a **confirmation message**—not the **Root scope (outside block invocations):** ``` + Binding written: research Location: .prose/runs/20260115-143052-a7b3c9/bindings/research.md Summary: AI safety research covering alignment, robustness, and interpretability with 15 citations. + ``` **Inside block invocation (include execution_id):** ``` + Binding written: result -Location: .prose/runs/20260115-143052-a7b3c9/bindings/result__43.md +Location: .prose/runs/20260115-143052-a7b3c9/bindings/result\_\_43.md Execution ID: 43 Summary: Processed chunk into 3 sub-parts for recursive processing. + ``` The VM records the location and continues. It does NOT read the file—it passes the reference to subsequent sessions that need the context. @@ -428,16 +445,18 @@ The VM records the location and continues. It does NOT read the file—it passes Imported programs use the **same unified structure recursively**: ``` + .prose/runs/{id}/imports/{handle}--{slug}/ ├── program.prose ├── state.md ├── bindings/ -│ └── {name}.md -├── imports/ # Nested imports go here -│ └── {handle2}--{slug2}/ -│ └── ... +│ └── {name}.md +├── imports/ # Nested imports go here +│ └── {handle2}--{slug2}/ +│ └── ... └── agents/ - └── {name}/ +└── {name}/ + ``` This allows unlimited nesting depth while maintaining consistent structure at every level. @@ -476,3 +495,4 @@ If execution is interrupted, resume by: 3. Continuing from the marked position The `state.md` file contains everything needed to understand where execution stopped and what has been accomplished. +``` diff --git a/extensions/open-prose/skills/prose/state/in-context.md b/extensions/open-prose/skills/prose/state/in-context.md index b884a956e7..98f6a29017 100644 --- a/extensions/open-prose/skills/prose/state/in-context.md +++ b/extensions/open-prose/skills/prose/state/in-context.md @@ -28,13 +28,13 @@ In-context state uses text-prefixed markers to persist state within the conversa In-context state is appropriate for: -| Factor | In-Context | Use File-Based Instead | -|--------|------------|------------------------| -| Statement count | < 30 statements | >= 30 statements | -| Parallel branches | < 5 concurrent | >= 5 concurrent | -| Imported programs | 0-2 imports | >= 3 imports | -| Nested depth | <= 2 levels | > 2 levels | -| Expected duration | < 5 minutes | >= 5 minutes | +| Factor | In-Context | Use File-Based Instead | +| ----------------- | --------------- | ---------------------- | +| Statement count | < 30 statements | >= 30 statements | +| Parallel branches | < 5 concurrent | >= 5 concurrent | +| Imported programs | 0-2 imports | >= 3 imports | +| Nested depth | <= 2 levels | > 2 levels | +| Expected duration | < 5 minutes | >= 5 minutes | Announce your state mode at program start: @@ -49,23 +49,23 @@ OpenProse Program Start Use text-prefixed markers for each state change: -| Marker | Category | Usage | -|--------|----------|-------| -| [Program] | Program | Start, end, definition collection | -| [Position] | Position | Current statement being executed | -| [Binding] | Binding | Variable assignment or update | -| [Input] | Input | Receiving inputs from caller | -| [Output] | Output | Producing outputs for caller | -| [Import] | Import | Fetching and invoking imported programs | -| [Success] | Success | Session or block completion | -| [Warning] | Error | Failures and exceptions | -| [Parallel] | Parallel | Entering, branch status, joining | -| [Loop] | Loop | Iteration, condition evaluation | -| [Pipeline] | Pipeline | Stage progress | -| [Try] | Error handling | Try/catch/finally | -| [Flow] | Flow | Condition evaluation results | -| [Frame+] | Call Stack | Push new frame (block invocation) | -| [Frame-] | Call Stack | Pop frame (block completion) | +| Marker | Category | Usage | +| ---------- | -------------- | --------------------------------------- | +| [Program] | Program | Start, end, definition collection | +| [Position] | Position | Current statement being executed | +| [Binding] | Binding | Variable assignment or update | +| [Input] | Input | Receiving inputs from caller | +| [Output] | Output | Producing outputs for caller | +| [Import] | Import | Fetching and invoking imported programs | +| [Success] | Success | Session or block completion | +| [Warning] | Error | Failures and exceptions | +| [Parallel] | Parallel | Entering, branch status, joining | +| [Loop] | Loop | Iteration, condition evaluation | +| [Pipeline] | Pipeline | Stage progress | +| [Try] | Error handling | Try/catch/finally | +| [Flow] | Flow | Condition evaluation results | +| [Frame+] | Call Stack | Push new frame (block invocation) | +| [Frame-] | Call Stack | Pop frame (block completion) | --- @@ -185,6 +185,7 @@ Track block invocations with frame markers: ``` **Key points:** + - Each `[Frame+]` must have a matching `[Frame-]` - `execution_id` uniquely identifies each invocation - `depth` shows call stack depth (1 = first level) @@ -236,13 +237,14 @@ For variable resolution across scopes: When passing context to sessions, format appropriately: -| Context Size | Strategy | -|--------------|----------| -| < 2000 chars | Pass verbatim | +| Context Size | Strategy | +| --------------- | ----------------------- | +| < 2000 chars | Pass verbatim | | 2000-8000 chars | Summarize to key points | -| > 8000 chars | Extract essentials only | +| > 8000 chars | Extract essentials only | **Format:** + ``` Context provided: --- @@ -274,6 +276,7 @@ loop until **analysis complete** (max: 3): ``` **Narration:** + ``` [Program] Program Start Collecting definitions... @@ -322,20 +325,20 @@ loop until **analysis complete** (max: 3): The VM must track these state categories in narration: -| Category | What to Track | Example | -|----------|---------------|---------| -| **Import Registry** | Imported programs and aliases | `research: @alice/research` | -| **Agent Registry** | All agent definitions | `researcher: {model: sonnet, prompt: "..."}` | -| **Block Registry** | All block definitions (hoisted) | `review: {params: [topic], body: [...]}` | -| **Input Bindings** | Inputs received from caller | `topic = "quantum computing"` | -| **Output Bindings** | Outputs to return to caller | `findings = "Research shows..."` | -| **Variable Bindings** | Name -> value mapping (with execution_id) | `result = "..." (execution_id: 3)` | -| **Variable Mutability** | Which are `let` vs `const` vs `output` | `research: let, findings: output` | -| **Execution Position** | Current statement index | Statement 3 of 7 | -| **Loop State** | Counter, max, condition | Iteration 2 of max 5 | -| **Parallel State** | Branches, results, strategy | `{a: complete, b: pending}` | -| **Error State** | Exception, retry count | Retry 2 of 3, error: "timeout" | -| **Call Stack** | Stack of execution frames | See below | +| Category | What to Track | Example | +| ----------------------- | ----------------------------------------- | -------------------------------------------- | +| **Import Registry** | Imported programs and aliases | `research: @alice/research` | +| **Agent Registry** | All agent definitions | `researcher: {model: sonnet, prompt: "..."}` | +| **Block Registry** | All block definitions (hoisted) | `review: {params: [topic], body: [...]}` | +| **Input Bindings** | Inputs received from caller | `topic = "quantum computing"` | +| **Output Bindings** | Outputs to return to caller | `findings = "Research shows..."` | +| **Variable Bindings** | Name -> value mapping (with execution_id) | `result = "..." (execution_id: 3)` | +| **Variable Mutability** | Which are `let` vs `const` vs `output` | `research: let, findings: output` | +| **Execution Position** | Current statement index | Statement 3 of 7 | +| **Loop State** | Counter, max, condition | Iteration 2 of max 5 | +| **Parallel State** | Branches, results, strategy | `{a: complete, b: pending}` | +| **Error State** | Exception, retry count | Retry 2 of 3, error: "timeout" | +| **Call Stack** | Stack of execution frames | See below | ### Call Stack State @@ -349,6 +352,7 @@ For block invocations, track the full call stack: ``` Each frame tracks: + - `execution_id`: Unique ID for this invocation - `block`: Name of the block - `depth`: Position in call stack diff --git a/extensions/open-prose/skills/prose/state/postgres.md b/extensions/open-prose/skills/prose/state/postgres.md index 1a730432bd..93e5307c11 100644 --- a/extensions/open-prose/skills/prose/state/postgres.md +++ b/extensions/open-prose/skills/prose/state/postgres.md @@ -21,19 +21,20 @@ This document describes how the OpenProse VM tracks execution state using a **Po ## Prerequisites **Requires:** + 1. The `psql` command-line tool must be available in your PATH 2. A running PostgreSQL server (local, Docker, or cloud) ### Installing psql -| Platform | Command | Notes | -|----------|---------|-------| -| macOS (Homebrew) | `brew install libpq && brew link --force libpq` | Client-only; no server | -| macOS (Postgres.app) | Download from https://postgresapp.com | Full install with GUI | -| Debian/Ubuntu | `apt install postgresql-client` | Client-only | -| Fedora/RHEL | `dnf install postgresql` | Client-only | -| Arch Linux | `pacman -S postgresql-libs` | Client-only | -| Windows | `winget install PostgreSQL.PostgreSQL` | Full installer | +| Platform | Command | Notes | +| -------------------- | ----------------------------------------------- | ---------------------- | +| macOS (Homebrew) | `brew install libpq && brew link --force libpq` | Client-only; no server | +| macOS (Postgres.app) | Download from https://postgresapp.com | Full install with GUI | +| Debian/Ubuntu | `apt install postgresql-client` | Client-only | +| Fedora/RHEL | `dnf install postgresql` | Client-only | +| Arch Linux | `pacman -S postgresql-libs` | Client-only | +| Windows | `winget install PostgreSQL.PostgreSQL` | Full installer | After installation, verify: @@ -70,6 +71,7 @@ PostgreSQL state provides: - Create a **limited-privilege user** with access only to the `openprose` schema **Recommended setup:** + ```sql -- Create dedicated user with minimal privileges CREATE USER openprose_agent WITH PASSWORD 'changeme'; @@ -84,13 +86,13 @@ GRANT ALL ON SCHEMA openprose TO openprose_agent; PostgreSQL state is for **power users** with specific scale or collaboration needs: -| Need | PostgreSQL Helps | -|------|------------------| -| >5 parallel branches writing simultaneously | SQLite locks; PostgreSQL doesn't | -| External dashboards querying state | PostgreSQL is designed for concurrent readers | -| Team collaboration on long workflows | Shared network access; no file sync needed | -| Outputs exceeding 1GB | Bulk ingestion; no single-file bottleneck | -| Mission-critical workflows (hours/days) | Robust durability; point-in-time recovery | +| Need | PostgreSQL Helps | +| ------------------------------------------- | --------------------------------------------- | +| >5 parallel branches writing simultaneously | SQLite locks; PostgreSQL doesn't | +| External dashboards querying state | PostgreSQL is designed for concurrent readers | +| Team collaboration on long workflows | Shared network access; no file sync needed | +| Outputs exceeding 1GB | Bulk ingestion; no single-file bottleneck | +| Mission-critical workflows (hours/days) | Robust durability; point-in-time recovery | **If none of these apply, use filesystem or SQLite state.** They're simpler and sufficient for 99% of programs. @@ -187,11 +189,11 @@ echo "OPENPROSE_POSTGRES_URL=postgresql:///myproject" >> .prose/.env For team collaboration or production: -| Provider | Free Tier | Cold Start | Best For | -|----------|-----------|------------|----------| -| **Neon** | 0.5GB, auto-suspend | 1-3s | Development, testing | -| **Supabase** | 500MB, no auto-suspend | None | Projects needing auth/storage | -| **Railway** | $5/mo credit | None | Simple production deploys | +| Provider | Free Tier | Cold Start | Best For | +| ------------ | ---------------------- | ---------- | ----------------------------- | +| **Neon** | 0.5GB, auto-suspend | 1-3s | Development, testing | +| **Supabase** | 500MB, no auto-suspend | None | Projects needing auth/storage | +| **Railway** | $5/mo credit | None | Simple production deploys | ```bash # Example: Neon @@ -246,17 +248,17 @@ This section defines **who does what**. This is the contract between the VM and The VM (the orchestrating agent running the .prose program) is responsible for: -| Responsibility | Description | -|----------------|-------------| -| **Schema initialization** | Create `openprose` schema and tables at run start | -| **Run registration** | Store the program source and metadata | -| **Execution tracking** | Update position, status, and timing as statements execute | -| **Subagent spawning** | Spawn sessions via Task tool with database instructions | -| **Parallel coordination** | Track branch status, implement join strategies | -| **Loop management** | Track iteration counts, evaluate conditions | -| **Error aggregation** | Record failures, manage retry state | -| **Context preservation** | Maintain sufficient narration in the main thread | -| **Completion detection** | Mark the run as complete when finished | +| Responsibility | Description | +| ------------------------- | --------------------------------------------------------- | +| **Schema initialization** | Create `openprose` schema and tables at run start | +| **Run registration** | Store the program source and metadata | +| **Execution tracking** | Update position, status, and timing as statements execute | +| **Subagent spawning** | Spawn sessions via Task tool with database instructions | +| **Parallel coordination** | Track branch status, implement join strategies | +| **Loop management** | Track iteration counts, evaluate conditions | +| **Error aggregation** | Record failures, manage retry state | +| **Context preservation** | Maintain sufficient narration in the main thread | +| **Completion detection** | Mark the run as complete when finished | **Critical:** The VM must preserve enough context in its own conversation to understand execution state without re-reading the entire database. The database is for coordination and persistence, not a replacement for working memory. @@ -264,13 +266,13 @@ The VM (the orchestrating agent running the .prose program) is responsible for: Subagents (sessions spawned by the VM) are responsible for: -| Responsibility | Description | -|----------------|-------------| -| **Writing own outputs** | Insert/update their binding in the `bindings` table | -| **Memory management** | For persistent agents: read and update their memory record | -| **Segment recording** | For persistent agents: append segment history | +| Responsibility | Description | +| ----------------------- | ----------------------------------------------------------------- | +| **Writing own outputs** | Insert/update their binding in the `bindings` table | +| **Memory management** | For persistent agents: read and update their memory record | +| **Segment recording** | For persistent agents: append segment history | | **Attachment handling** | Write large outputs to `attachments/` directory, store path in DB | -| **Atomic writes** | Use transactions when updating multiple related records | +| **Atomic writes** | Use transactions when updating multiple related records | **Critical:** Subagents write ONLY to `bindings`, `agents`, and `agent_segments` tables. The VM owns the `execution` table entirely. Completion signaling happens through the substrate (Task tool return), not database updates. @@ -279,6 +281,7 @@ Subagents (sessions spawned by the VM) are responsible for: **What subagents return to the VM:** A confirmation message with the binding location—not the full content: **Root scope:** + ``` Binding written: research Location: openprose.bindings WHERE name='research' AND run_id='20260116-143052-a7b3c9' AND execution_id IS NULL @@ -286,6 +289,7 @@ Summary: AI safety research covering alignment, robustness, and interpretability ``` **Inside block invocation:** + ``` Binding written: result Location: openprose.bindings WHERE name='result' AND run_id='20260116-143052-a7b3c9' AND execution_id=43 @@ -297,12 +301,12 @@ The VM tracks locations, not values. This keeps the VM's context lean and enable ### Shared Concerns -| Concern | Who Handles | -|---------|-------------| +| Concern | Who Handles | +| ---------------- | ------------------------------------------------------------------ | | Schema evolution | Either (use `CREATE TABLE IF NOT EXISTS`, `ALTER TABLE` as needed) | -| Custom tables | Either (prefix with `x_` for extensions) | -| Indexing | Either (add indexes for frequently-queried columns) | -| Cleanup | VM (at run end, optionally delete old data) | +| Custom tables | Either (prefix with `x_` for extensions) | +| Indexing | Either (add indexes for frequently-queried columns) | +| Cleanup | VM (at run end, optionally delete old data) | --- @@ -601,12 +605,12 @@ Even with PostgreSQL state, the VM should narrate key events in its conversation ### Why Both? -| Purpose | Mechanism | -|---------|-----------| -| **Working memory** | Conversation narration (what the VM "remembers" without re-querying) | -| **Durable state** | PostgreSQL database (survives context limits, enables resumption) | -| **Subagent coordination** | PostgreSQL database (shared access point) | -| **Debugging/inspection** | PostgreSQL database (queryable history) | +| Purpose | Mechanism | +| ------------------------- | -------------------------------------------------------------------- | +| **Working memory** | Conversation narration (what the VM "remembers" without re-querying) | +| **Durable state** | PostgreSQL database (survives context limits, enables resumption) | +| **Subagent coordination** | PostgreSQL database (shared access point) | +| **Debugging/inspection** | PostgreSQL database (queryable history) | The narration is the VM's "mental model" of execution. The database is the "source of truth" for resumption and inspection. @@ -717,6 +721,7 @@ PRIMARY KEY (name, COALESCE(run_id, '__project__')) ``` This means: + - `name='advisor', run_id=NULL` has PK `('advisor', '__project__')` - `name='advisor', run_id='20260116-143052-a7b3c9'` has PK `('advisor', '20260116-143052-a7b3c9')` @@ -724,10 +729,10 @@ The same agent name can exist as both project-scoped and execution-scoped withou ### Query Patterns -| Scope | Query | -|-------|-------| +| Scope | Query | +| ---------------- | ------------------------------------------------ | | Execution-scoped | `WHERE name = 'captain' AND run_id = '{RUN_ID}'` | -| Project-scoped | `WHERE name = 'advisor' AND run_id IS NULL` | +| Project-scoped | `WHERE name = 'advisor' AND run_id IS NULL` | ### Project-Scoped Memory Guidelines @@ -841,20 +846,20 @@ The database is your workspace. Use it. ## Comparison with Other Modes -| Aspect | filesystem.md | in-context.md | sqlite.md | postgres.md | -|--------|---------------|---------------|-----------|-------------| -| **State location** | `.prose/runs/{id}/` files | Conversation history | `.prose/runs/{id}/state.db` | PostgreSQL database | -| **Queryable** | Via file reads | No | Yes (SQL) | Yes (SQL) | -| **Atomic updates** | No | N/A | Yes (transactions) | Yes (ACID) | -| **Concurrent writes** | Yes (different files) | N/A | **No (table locks)** | **Yes (row locks)** | -| **Network access** | No | No | No | **Yes** | -| **Team collaboration** | Via file sync | No | Via file sync | **Yes** | -| **Schema flexibility** | Rigid file structure | N/A | Flexible | Very flexible (JSONB) | -| **Resumption** | Read state.md | Re-read conversation | Query database | Query database | -| **Complexity ceiling** | High | Low (<30 statements) | High | **Very high** | -| **Dependency** | None | None | sqlite3 CLI | psql CLI + PostgreSQL | -| **Setup friction** | Zero | Zero | Low | Medium-High | -| **Status** | Stable | Stable | Experimental | **Experimental** | +| Aspect | filesystem.md | in-context.md | sqlite.md | postgres.md | +| ---------------------- | ------------------------- | -------------------- | --------------------------- | --------------------- | +| **State location** | `.prose/runs/{id}/` files | Conversation history | `.prose/runs/{id}/state.db` | PostgreSQL database | +| **Queryable** | Via file reads | No | Yes (SQL) | Yes (SQL) | +| **Atomic updates** | No | N/A | Yes (transactions) | Yes (ACID) | +| **Concurrent writes** | Yes (different files) | N/A | **No (table locks)** | **Yes (row locks)** | +| **Network access** | No | No | No | **Yes** | +| **Team collaboration** | Via file sync | No | Via file sync | **Yes** | +| **Schema flexibility** | Rigid file structure | N/A | Flexible | Very flexible (JSONB) | +| **Resumption** | Read state.md | Re-read conversation | Query database | Query database | +| **Complexity ceiling** | High | Low (<30 statements) | High | **Very high** | +| **Dependency** | None | None | sqlite3 CLI | psql CLI + PostgreSQL | +| **Setup friction** | Zero | Zero | Low | Medium-High | +| **Status** | Stable | Stable | Experimental | **Experimental** | --- diff --git a/extensions/open-prose/skills/prose/state/sqlite.md b/extensions/open-prose/skills/prose/state/sqlite.md index 6093be9d6a..cfec757567 100644 --- a/extensions/open-prose/skills/prose/state/sqlite.md +++ b/extensions/open-prose/skills/prose/state/sqlite.md @@ -21,11 +21,11 @@ This document describes how the OpenProse VM tracks execution state using a **SQ **Requires:** The `sqlite3` command-line tool must be available in your PATH. -| Platform | Installation | -|----------|--------------| -| macOS | Pre-installed | -| Linux | `apt install sqlite3` / `dnf install sqlite3` / etc. | -| Windows | `winget install SQLite.SQLite` or download from sqlite.org | +| Platform | Installation | +| -------- | ---------------------------------------------------------- | +| macOS | Pre-installed | +| Linux | `apt install sqlite3` / `dnf install sqlite3` / etc. | +| Windows | `winget install SQLite.SQLite` or download from sqlite.org | If `sqlite3` is not available, the VM will fall back to filesystem state and warn the user. @@ -93,17 +93,17 @@ This section defines **who does what**. This is the contract between the VM and The VM (the orchestrating agent running the .prose program) is responsible for: -| Responsibility | Description | -|----------------|-------------| -| **Database creation** | Create `state.db` and initialize core tables at run start | -| **Program registration** | Store the program source and metadata | -| **Execution tracking** | Update position, status, and timing as statements execute | -| **Subagent spawning** | Spawn sessions via Task tool with database path and instructions | -| **Parallel coordination** | Track branch status, implement join strategies | -| **Loop management** | Track iteration counts, evaluate conditions | -| **Error aggregation** | Record failures, manage retry state | -| **Context preservation** | Maintain sufficient narration in the main conversation thread so execution can be understood and resumed | -| **Completion detection** | Mark the run as complete when finished | +| Responsibility | Description | +| ------------------------- | -------------------------------------------------------------------------------------------------------- | +| **Database creation** | Create `state.db` and initialize core tables at run start | +| **Program registration** | Store the program source and metadata | +| **Execution tracking** | Update position, status, and timing as statements execute | +| **Subagent spawning** | Spawn sessions via Task tool with database path and instructions | +| **Parallel coordination** | Track branch status, implement join strategies | +| **Loop management** | Track iteration counts, evaluate conditions | +| **Error aggregation** | Record failures, manage retry state | +| **Context preservation** | Maintain sufficient narration in the main conversation thread so execution can be understood and resumed | +| **Completion detection** | Mark the run as complete when finished | **Critical:** The VM must preserve enough context in its own conversation to understand execution state without re-reading the entire database. The database is for coordination and persistence, not a replacement for working memory. @@ -111,13 +111,13 @@ The VM (the orchestrating agent running the .prose program) is responsible for: Subagents (sessions spawned by the VM) are responsible for: -| Responsibility | Description | -|----------------|-------------| -| **Writing own outputs** | Insert/update their binding in the `bindings` table | -| **Memory management** | For persistent agents: read and update their memory record | -| **Segment recording** | For persistent agents: append segment history | +| Responsibility | Description | +| ----------------------- | ----------------------------------------------------------------- | +| **Writing own outputs** | Insert/update their binding in the `bindings` table | +| **Memory management** | For persistent agents: read and update their memory record | +| **Segment recording** | For persistent agents: append segment history | | **Attachment handling** | Write large outputs to `attachments/` directory, store path in DB | -| **Atomic writes** | Use transactions when updating multiple related records | +| **Atomic writes** | Use transactions when updating multiple related records | **Critical:** Subagents write ONLY to `bindings`, `agents`, and `agent_segments` tables. The VM owns the `execution` table entirely. Completion signaling happens through the substrate (Task tool return), not database updates. @@ -126,6 +126,7 @@ Subagents (sessions spawned by the VM) are responsible for: **What subagents return to the VM:** A confirmation message with the binding location—not the full content: **Root scope:** + ``` Binding written: research Location: .prose/runs/20260116-143052-a7b3c9/state.db (bindings table, name='research', execution_id=NULL) @@ -133,6 +134,7 @@ Summary: AI safety research covering alignment, robustness, and interpretability ``` **Inside block invocation:** + ``` Binding written: result Location: .prose/runs/20260116-143052-a7b3c9/state.db (bindings table, name='result', execution_id=43) @@ -144,12 +146,12 @@ The VM tracks locations, not values. This keeps the VM's context lean and enable ### Shared Concerns -| Concern | Who Handles | -|---------|-------------| +| Concern | Who Handles | +| ---------------- | ------------------------------------------------------------------ | | Schema evolution | Either (use `CREATE TABLE IF NOT EXISTS`, `ALTER TABLE` as needed) | -| Custom tables | Either (prefix with `x_` for extensions) | -| Indexing | Either (add indexes for frequently-queried columns) | -| Cleanup | VM (at run end, optionally vacuum) | +| Custom tables | Either (prefix with `x_` for extensions) | +| Indexing | Either (add indexes for frequently-queried columns) | +| Cleanup | VM (at run end, optionally vacuum) | --- @@ -390,12 +392,12 @@ Even with SQLite state, the VM should narrate key events in its conversation: ### Why Both? -| Purpose | Mechanism | -|---------|-----------| -| **Working memory** | Conversation narration (what the VM "remembers" without re-querying) | -| **Durable state** | SQLite database (survives context limits, enables resumption) | -| **Subagent coordination** | SQLite database (shared access point) | -| **Debugging/inspection** | SQLite database (queryable history) | +| Purpose | Mechanism | +| ------------------------- | -------------------------------------------------------------------- | +| **Working memory** | Conversation narration (what the VM "remembers" without re-querying) | +| **Durable state** | SQLite database (survives context limits, enables resumption) | +| **Subagent coordination** | SQLite database (shared access point) | +| **Debugging/inspection** | SQLite database (queryable history) | The narration is the VM's "mental model" of execution. The database is the "source of truth" for resumption and inspection. @@ -544,16 +546,16 @@ The database is your workspace. Use it. ## Comparison with Other Modes -| Aspect | filesystem.md | in-context.md | sqlite.md | -|--------|---------------|---------------|-----------| -| **State location** | `.prose/runs/{id}/` files | Conversation history | `.prose/runs/{id}/state.db` | -| **Queryable** | Via file reads | No | Yes (SQL) | -| **Atomic updates** | No | N/A | Yes (transactions) | -| **Schema flexibility** | Rigid file structure | N/A | Flexible (add tables/columns) | -| **Resumption** | Read state.md | Re-read conversation | Query database | -| **Complexity ceiling** | High | Low (<30 statements) | High | -| **Dependency** | None | None | sqlite3 CLI | -| **Status** | Stable | Stable | **Experimental** | +| Aspect | filesystem.md | in-context.md | sqlite.md | +| ---------------------- | ------------------------- | -------------------- | ----------------------------- | +| **State location** | `.prose/runs/{id}/` files | Conversation history | `.prose/runs/{id}/state.db` | +| **Queryable** | Via file reads | No | Yes (SQL) | +| **Atomic updates** | No | N/A | Yes (transactions) | +| **Schema flexibility** | Rigid file structure | N/A | Flexible (add tables/columns) | +| **Resumption** | Read state.md | Re-read conversation | Query database | +| **Complexity ceiling** | High | Low (<30 statements) | High | +| **Dependency** | None | None | sqlite3 CLI | +| **Status** | Stable | Stable | **Experimental** | --- diff --git a/scripts/bench-model.ts b/scripts/bench-model.ts index 0b3a60d010..9761380e24 100644 --- a/scripts/bench-model.ts +++ b/scripts/bench-model.ts @@ -13,8 +13,7 @@ type RunResult = { usage?: Usage; }; -const DEFAULT_PROMPT = - "Reply with a single word: ok. No punctuation or extra text."; +const DEFAULT_PROMPT = "Reply with a single word: ok. No punctuation or extra text."; const DEFAULT_RUNS = 10; function parseArg(flag: string): string | undefined { @@ -65,9 +64,7 @@ async function runModel(opts: { ); const durationMs = Date.now() - started; results.push({ durationMs, usage: res.usage }); - console.log( - `${opts.label} run ${i + 1}/${opts.runs}: ${durationMs}ms`, - ); + console.log(`${opts.label} run ${i + 1}/${opts.runs}: ${durationMs}ms`); } return results; } @@ -85,10 +82,8 @@ async function main(): Promise { throw new Error("Missing MINIMAX_API_KEY in environment."); } - const minimaxBaseUrl = - process.env.MINIMAX_BASE_URL?.trim() || "https://api.minimax.io/v1"; - const minimaxModelId = - process.env.MINIMAX_MODEL?.trim() || "MiniMax-M2.1"; + const minimaxBaseUrl = process.env.MINIMAX_BASE_URL?.trim() || "https://api.minimax.io/v1"; + const minimaxModelId = process.env.MINIMAX_MODEL?.trim() || "MiniMax-M2.1"; const minimaxModel: Model<"openai-completions"> = { id: minimaxModelId, @@ -135,9 +130,7 @@ async function main(): Promise { console.log(""); console.log("Summary (ms):"); for (const row of summary) { - console.log( - `${row.label.padEnd(7)} median=${row.med} min=${row.min} max=${row.max}`, - ); + console.log(`${row.label.padEnd(7)} median=${row.med} min=${row.min} max=${row.max}`); } } diff --git a/scripts/canvas-a2ui-copy.ts b/scripts/canvas-a2ui-copy.ts index fa791c872a..238bc3b912 100644 --- a/scripts/canvas-a2ui-copy.ts +++ b/scripts/canvas-a2ui-copy.ts @@ -5,27 +5,18 @@ import { fileURLToPath, pathToFileURL } from "node:url"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); export function getA2uiPaths(env = process.env) { - const srcDir = - env.OPENCLAW_A2UI_SRC_DIR ?? path.join(repoRoot, "src", "canvas-host", "a2ui"); - const outDir = - env.OPENCLAW_A2UI_OUT_DIR ?? path.join(repoRoot, "dist", "canvas-host", "a2ui"); + const srcDir = env.OPENCLAW_A2UI_SRC_DIR ?? path.join(repoRoot, "src", "canvas-host", "a2ui"); + const outDir = env.OPENCLAW_A2UI_OUT_DIR ?? path.join(repoRoot, "dist", "canvas-host", "a2ui"); return { srcDir, outDir }; } -export async function copyA2uiAssets({ - srcDir, - outDir, -}: { - srcDir: string; - outDir: string; -}) { +export async function copyA2uiAssets({ srcDir, outDir }: { srcDir: string; outDir: string }) { const skipMissing = process.env.OPENCLAW_A2UI_SKIP_MISSING === "1"; try { await fs.stat(path.join(srcDir, "index.html")); await fs.stat(path.join(srcDir, "a2ui.bundle.js")); } catch (err) { - const message = - 'Missing A2UI bundle assets. Run "pnpm canvas:a2ui:bundle" and retry.'; + const message = 'Missing A2UI bundle assets. Run "pnpm canvas:a2ui:bundle" and retry.'; if (skipMissing) { console.warn(`${message} Skipping copy (OPENCLAW_A2UI_SKIP_MISSING=1).`); return; diff --git a/scripts/copy-hook-metadata.ts b/scripts/copy-hook-metadata.ts index 66d6962288..cf1cb230b3 100644 --- a/scripts/copy-hook-metadata.ts +++ b/scripts/copy-hook-metadata.ts @@ -3,19 +3,19 @@ * Copy HOOK.md files from src/hooks/bundled to dist/hooks/bundled */ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const projectRoot = path.resolve(__dirname, '..'); +const projectRoot = path.resolve(__dirname, ".."); -const srcBundled = path.join(projectRoot, 'src', 'hooks', 'bundled'); -const distBundled = path.join(projectRoot, 'dist', 'hooks', 'bundled'); +const srcBundled = path.join(projectRoot, "src", "hooks", "bundled"); +const distBundled = path.join(projectRoot, "dist", "hooks", "bundled"); function copyHookMetadata() { if (!fs.existsSync(srcBundled)) { - console.warn('[copy-hook-metadata] Source directory not found:', srcBundled); + console.warn("[copy-hook-metadata] Source directory not found:", srcBundled); return; } @@ -31,8 +31,8 @@ function copyHookMetadata() { const hookName = entry.name; const srcHookDir = path.join(srcBundled, hookName); const distHookDir = path.join(distBundled, hookName); - const srcHookMd = path.join(srcHookDir, 'HOOK.md'); - const distHookMd = path.join(distHookDir, 'HOOK.md'); + const srcHookMd = path.join(srcHookDir, "HOOK.md"); + const distHookMd = path.join(distHookDir, "HOOK.md"); if (!fs.existsSync(srcHookMd)) { console.warn(`[copy-hook-metadata] No HOOK.md found for ${hookName}`); @@ -47,7 +47,7 @@ function copyHookMetadata() { console.log(`[copy-hook-metadata] Copied ${hookName}/HOOK.md`); } - console.log('[copy-hook-metadata] Done'); + console.log("[copy-hook-metadata] Done"); } copyHookMetadata(); diff --git a/scripts/debug-claude-usage.ts b/scripts/debug-claude-usage.ts index 4a5d46ea19..51f1ec60e5 100644 --- a/scripts/debug-claude-usage.ts +++ b/scripts/debug-claude-usage.ts @@ -44,9 +44,9 @@ const parseArgs = (): Args => { const loadAuthProfiles = (agentId: string) => { const stateRoot = - process.env.OPENCLAW_STATE_DIR?.trim() || - process.env.CLAWDBOT_STATE_DIR?.trim() || - path.join(os.homedir(), ".openclaw"); + process.env.OPENCLAW_STATE_DIR?.trim() || + process.env.CLAWDBOT_STATE_DIR?.trim() || + path.join(os.homedir(), ".openclaw"); const authPath = path.join(stateRoot, "agents", agentId, "agent", "auth-profiles.json"); if (!fs.existsSync(authPath)) throw new Error(`Missing: ${authPath}`); const store = JSON.parse(fs.readFileSync(authPath, "utf8")) as { @@ -99,8 +99,7 @@ const readClaudeCliKeychain = (): { if (!oauth || typeof oauth !== "object") return null; const accessToken = oauth.accessToken; if (typeof accessToken !== "string" || !accessToken.trim()) return null; - const expiresAt = - typeof oauth.expiresAt === "number" ? oauth.expiresAt : undefined; + const expiresAt = typeof oauth.expiresAt === "number" ? oauth.expiresAt : undefined; const scopes = Array.isArray(oauth.scopes) ? oauth.scopes.filter((v): v is string => typeof v === "string") : undefined; @@ -120,11 +119,11 @@ const chromeServiceNameForPath = (cookiePath: string): string => { const readKeychainPassword = (service: string): string | null => { try { - const out = execFileSync( - "security", - ["find-generic-password", "-w", "-s", service], - { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 }, - ); + const out = execFileSync("security", ["find-generic-password", "-w", "-s", service], { + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"], + timeout: 5000, + }); const pw = out.trim(); return pw ? pw : null; } catch { @@ -317,15 +316,16 @@ const main = async () => { process.env.CLAUDE_AI_SESSION_KEY?.trim() || process.env.CLAUDE_WEB_SESSION_KEY?.trim() || findClaudeSessionKey()?.sessionKey; - const source = - opts.sessionKey - ? "--session-key" - : process.env.CLAUDE_AI_SESSION_KEY || process.env.CLAUDE_WEB_SESSION_KEY - ? "env" - : findClaudeSessionKey()?.source ?? "auto"; + const source = opts.sessionKey + ? "--session-key" + : process.env.CLAUDE_AI_SESSION_KEY || process.env.CLAUDE_WEB_SESSION_KEY + ? "env" + : (findClaudeSessionKey()?.source ?? "auto"); if (!sessionKey) { - console.log("Claude web: no sessionKey found (try --session-key or export CLAUDE_AI_SESSION_KEY)"); + console.log( + "Claude web: no sessionKey found (try --session-key or export CLAUDE_AI_SESSION_KEY)", + ); return; } diff --git a/scripts/docs-list.js b/scripts/docs-list.js index cd724d5ac5..bcd847cc9a 100755 --- a/scripts/docs-list.js +++ b/scripts/docs-list.js @@ -1,26 +1,26 @@ #!/usr/bin/env node -import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'; -import { join, relative } from 'node:path'; +import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; +import { join, relative } from "node:path"; -process.stdout.on('error', (error) => { - if (error?.code === 'EPIPE') { +process.stdout.on("error", (error) => { + if (error?.code === "EPIPE") { process.exit(0); } throw error; }); -const DOCS_DIR = join(process.cwd(), 'docs'); +const DOCS_DIR = join(process.cwd(), "docs"); if (!existsSync(DOCS_DIR)) { - console.error('docs:list: missing docs directory. Run from repo root.'); + console.error("docs:list: missing docs directory. Run from repo root."); process.exit(1); } if (!statSync(DOCS_DIR).isDirectory()) { - console.error('docs:list: docs path is not a directory.'); + console.error("docs:list: docs path is not a directory."); process.exit(1); } -const EXCLUDED_DIRS = new Set(['archive', 'research']); +const EXCLUDED_DIRS = new Set(["archive", "research"]); /** * @param {unknown[]} values @@ -49,7 +49,7 @@ function walkMarkdownFiles(dir, base = dir) { const entries = readdirSync(dir, { withFileTypes: true }); const files = []; for (const entry of entries) { - if (entry.name.startsWith('.')) { + if (entry.name.startsWith(".")) { continue; } const fullPath = join(dir, entry.name); @@ -58,7 +58,7 @@ function walkMarkdownFiles(dir, base = dir) { continue; } files.push(...walkMarkdownFiles(fullPath, base)); - } else if (entry.isFile() && entry.name.endsWith('.md')) { + } else if (entry.isFile() && entry.name.endsWith(".md")) { files.push(relative(base, fullPath)); } } @@ -70,19 +70,19 @@ function walkMarkdownFiles(dir, base = dir) { * @returns {{ summary: string | null; readWhen: string[]; error?: string }} */ function extractMetadata(fullPath) { - const content = readFileSync(fullPath, 'utf8'); + const content = readFileSync(fullPath, "utf8"); - if (!content.startsWith('---')) { - return { summary: null, readWhen: [], error: 'missing front matter' }; + if (!content.startsWith("---")) { + return { summary: null, readWhen: [], error: "missing front matter" }; } - const endIndex = content.indexOf('\n---', 3); + const endIndex = content.indexOf("\n---", 3); if (endIndex === -1) { - return { summary: null, readWhen: [], error: 'unterminated front matter' }; + return { summary: null, readWhen: [], error: "unterminated front matter" }; } const frontMatter = content.slice(3, endIndex).trim(); - const lines = frontMatter.split('\n'); + const lines = frontMatter.split("\n"); let summaryLine = null; const readWhen = []; @@ -91,16 +91,16 @@ function extractMetadata(fullPath) { for (const rawLine of lines) { const line = rawLine.trim(); - if (line.startsWith('summary:')) { + if (line.startsWith("summary:")) { summaryLine = line; collectingField = null; continue; } - if (line.startsWith('read_when:')) { - collectingField = 'read_when'; - const inline = line.slice('read_when:'.length).trim(); - if (inline.startsWith('[') && inline.endsWith(']')) { + if (line.startsWith("read_when:")) { + collectingField = "read_when"; + const inline = line.slice("read_when:".length).trim(); + if (inline.startsWith("[") && inline.endsWith("]")) { try { const parsed = JSON.parse(inline.replace(/'/g, '"')); if (Array.isArray(parsed)) { @@ -113,13 +113,13 @@ function extractMetadata(fullPath) { continue; } - if (collectingField === 'read_when') { - if (line.startsWith('- ')) { + if (collectingField === "read_when") { + if (line.startsWith("- ")) { const hint = line.slice(2).trim(); if (hint) { readWhen.push(hint); } - } else if (line === '') { + } else if (line === "") { // allow blank lines inside the list } else { collectingField = null; @@ -128,23 +128,23 @@ function extractMetadata(fullPath) { } if (!summaryLine) { - return { summary: null, readWhen, error: 'summary key missing' }; + return { summary: null, readWhen, error: "summary key missing" }; } - const summaryValue = summaryLine.slice('summary:'.length).trim(); + const summaryValue = summaryLine.slice("summary:".length).trim(); const normalized = summaryValue - .replace(/^['"]|['"]$/g, '') - .replace(/\s+/g, ' ') + .replace(/^['"]|['"]$/g, "") + .replace(/\s+/g, " ") .trim(); if (!normalized) { - return { summary: null, readWhen, error: 'summary is empty' }; + return { summary: null, readWhen, error: "summary is empty" }; } return { summary: normalized, readWhen }; } -console.log('Listing all markdown files in docs folder:'); +console.log("Listing all markdown files in docs folder:"); const markdownFiles = walkMarkdownFiles(DOCS_DIR); @@ -154,14 +154,14 @@ for (const relativePath of markdownFiles) { if (summary) { console.log(`${relativePath} - ${summary}`); if (readWhen.length > 0) { - console.log(` Read when: ${readWhen.join('; ')}`); + console.log(` Read when: ${readWhen.join("; ")}`); } } else { - const reason = error ? ` - [${error}]` : ''; + const reason = error ? ` - [${error}]` : ""; console.log(`${relativePath}${reason}`); } } console.log( - '\nReminder: keep docs up to date as behavior changes. When your task matches any "Read when" hint above (React hooks, cache directives, database work, tests, etc.), read that doc before coding, and suggest new coverage when it is missing.' + '\nReminder: keep docs up to date as behavior changes. When your task matches any "Read when" hint above (React hooks, cache directives, database work, tests, etc.), read that doc before coding, and suggest new coverage when it is missing.', ); diff --git a/scripts/firecrawl-compare.ts b/scripts/firecrawl-compare.ts index 2724aea480..10fb96d8dd 100644 --- a/scripts/firecrawl-compare.ts +++ b/scripts/firecrawl-compare.ts @@ -88,9 +88,7 @@ async function run() { localError = error instanceof Error ? error.message : String(error); } - console.log( - `local: ${localStatus} len=${localText.length} title=${truncate(localTitle, 80)}` - ); + console.log(`local: ${localStatus} len=${localText.length} title=${truncate(localTitle, 80)}`); if (localError) console.log(`local error: ${localError}`); if (localText) console.log(`local sample: ${truncate(localText)}`); @@ -111,7 +109,7 @@ async function run() { `firecrawl: ok len=${firecrawl.text.length} title=${truncate( firecrawl.title ?? "", 80, - )} status=${firecrawl.status ?? "n/a"}` + )} status=${firecrawl.status ?? "n/a"}`, ); if (firecrawl.warning) console.log(`firecrawl warning: ${firecrawl.warning}`); if (firecrawl.text) console.log(`firecrawl sample: ${truncate(firecrawl.text)}`); diff --git a/scripts/format-staged.js b/scripts/format-staged.js index 0ad2d7dd78..800a1d7e5e 100644 --- a/scripts/format-staged.js +++ b/scripts/format-staged.js @@ -3,16 +3,7 @@ import path from "node:path"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; -const OXFMT_EXTENSIONS = new Set([ - ".cjs", - ".js", - ".json", - ".jsonc", - ".jsx", - ".mjs", - ".ts", - ".tsx", -]); +const OXFMT_EXTENSIONS = new Set([".cjs", ".js", ".json", ".jsonc", ".jsx", ".mjs", ".ts", ".tsx"]); function getRepoRoot() { const here = path.dirname(fileURLToPath(import.meta.url)); @@ -40,9 +31,10 @@ function normalizeGitPath(filePath) { function filterOxfmtTargets(paths) { return paths .map(normalizeGitPath) - .filter((filePath) => - (filePath.startsWith("src/") || filePath.startsWith("test/")) && - OXFMT_EXTENSIONS.has(path.posix.extname(filePath)), + .filter( + (filePath) => + (filePath.startsWith("src/") || filePath.startsWith("test/")) && + OXFMT_EXTENSIONS.has(path.posix.extname(filePath)), ); } @@ -94,13 +86,10 @@ function stageFiles(repoRoot, files) { function main() { const repoRoot = getRepoRoot(); - const staged = getGitPaths([ - "diff", - "--cached", - "--name-only", - "-z", - "--diff-filter=ACMR", - ], repoRoot); + const staged = getGitPaths( + ["diff", "--cached", "--name-only", "-z", "--diff-filter=ACMR"], + repoRoot, + ); const targets = filterOxfmtTargets(staged); if (targets.length === 0) return; diff --git a/scripts/postinstall.js b/scripts/postinstall.js index f1432b1532..8ad0417e0c 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -1,36 +1,34 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { spawnSync } from 'node:child_process'; -import { fileURLToPath } from 'node:url'; -import { setupGitHooks } from './setup-git-hooks.js'; +import fs from "node:fs"; +import path from "node:path"; +import { spawnSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; +import { setupGitHooks } from "./setup-git-hooks.js"; -function detectPackageManager(ua = process.env.npm_config_user_agent ?? '') { +function detectPackageManager(ua = process.env.npm_config_user_agent ?? "") { // Examples: // - "pnpm/10.23.0 npm/? node/v22.21.1 darwin arm64" // - "npm/10.9.4 node/v22.12.0 linux x64" // - "bun/1.2.2" const normalized = String(ua).trim(); - if (normalized.startsWith('pnpm/')) return 'pnpm'; - if (normalized.startsWith('bun/')) return 'bun'; - if (normalized.startsWith('npm/')) return 'npm'; - if (normalized.startsWith('yarn/')) return 'yarn'; - return 'unknown'; + if (normalized.startsWith("pnpm/")) return "pnpm"; + if (normalized.startsWith("bun/")) return "bun"; + if (normalized.startsWith("npm/")) return "npm"; + if (normalized.startsWith("yarn/")) return "yarn"; + return "unknown"; } -function shouldApplyPnpmPatchedDependenciesFallback( - pm = detectPackageManager(), -) { +function shouldApplyPnpmPatchedDependenciesFallback(pm = detectPackageManager()) { // pnpm already applies pnpm.patchedDependencies itself; re-applying would fail. - return pm !== 'pnpm'; + return pm !== "pnpm"; } function getRepoRoot() { const here = path.dirname(fileURLToPath(import.meta.url)); - return path.resolve(here, '..'); + return path.resolve(here, ".."); } function ensureExecutable(targetPath) { - if (process.platform === 'win32') return; + if (process.platform === "win32") return; if (!fs.existsSync(targetPath)) return; try { const mode = fs.statSync(targetPath).mode & 0o777; @@ -42,32 +40,32 @@ function ensureExecutable(targetPath) { } function hasGit(repoRoot) { - const result = spawnSync('git', ['--version'], { + const result = spawnSync("git", ["--version"], { cwd: repoRoot, - stdio: 'ignore', + stdio: "ignore", }); return result.status === 0; } function extractPackageName(key) { - if (key.startsWith('@')) { - const idx = key.indexOf('@', 1); + if (key.startsWith("@")) { + const idx = key.indexOf("@", 1); if (idx === -1) return key; return key.slice(0, idx); } - const idx = key.lastIndexOf('@'); + const idx = key.lastIndexOf("@"); if (idx <= 0) return key; return key.slice(0, idx); } function stripPrefix(p) { - if (p.startsWith('a/') || p.startsWith('b/')) return p.slice(2); + if (p.startsWith("a/") || p.startsWith("b/")) return p.slice(2); return p; } function parseRange(segment) { // segment: "-12,5" or "+7" - const [startRaw, countRaw] = segment.slice(1).split(','); + const [startRaw, countRaw] = segment.slice(1).split(","); const start = Number.parseInt(startRaw, 10); const count = countRaw ? Number.parseInt(countRaw, 10) : 1; if (Number.isNaN(start) || Number.isNaN(count)) { @@ -77,12 +75,12 @@ function parseRange(segment) { } function parsePatch(patchText) { - const lines = patchText.split('\n'); + const lines = patchText.split("\n"); const files = []; let i = 0; while (i < lines.length) { - if (!lines[i].startsWith('diff --git ')) { + if (!lines[i].startsWith("diff --git ")) { i += 1; continue; } @@ -91,22 +89,20 @@ function parsePatch(patchText) { i += 1; // Skip index line(s) - while (i < lines.length && lines[i].startsWith('index ')) i += 1; + while (i < lines.length && lines[i].startsWith("index ")) i += 1; - if (i < lines.length && lines[i].startsWith('--- ')) { + if (i < lines.length && lines[i].startsWith("--- ")) { file.oldPath = stripPrefix(lines[i].slice(4).trim()); i += 1; } - if (i < lines.length && lines[i].startsWith('+++ ')) { + if (i < lines.length && lines[i].startsWith("+++ ")) { file.newPath = stripPrefix(lines[i].slice(4).trim()); i += 1; } - while (i < lines.length && lines[i].startsWith('@@')) { + while (i < lines.length && lines[i].startsWith("@@")) { const header = lines[i]; - const match = /^@@\s+(-\d+(?:,\d+)?)\s+(\+\d+(?:,\d+)?)\s+@@/.exec( - header, - ); + const match = /^@@\s+(-\d+(?:,\d+)?)\s+(\+\d+(?:,\d+)?)\s+@@/.exec(header); if (!match) throw new Error(`invalid hunk header: ${header}`); const oldRange = parseRange(match[1]); const newRange = parseRange(match[2]); @@ -115,12 +111,12 @@ function parsePatch(patchText) { const hunkLines = []; while (i < lines.length) { const line = lines[i]; - if (line.startsWith('@@') || line.startsWith('diff --git ')) break; - if (line === '') { + if (line.startsWith("@@") || line.startsWith("diff --git ")) break; + if (line === "") { i += 1; continue; } - if (line.startsWith('\\ No newline at end of file')) { + if (line.startsWith("\\ No newline at end of file")) { i += 1; continue; } @@ -149,16 +145,16 @@ function readFileLines(targetPath) { if (!fs.existsSync(targetPath)) { throw new Error(`target file missing: ${targetPath}`); } - const raw = fs.readFileSync(targetPath, 'utf-8'); - const hasTrailingNewline = raw.endsWith('\n'); - const parts = raw.split('\n'); + const raw = fs.readFileSync(targetPath, "utf-8"); + const hasTrailingNewline = raw.endsWith("\n"); + const parts = raw.split("\n"); if (hasTrailingNewline) parts.pop(); return { lines: parts, hasTrailingNewline }; } function writeFileLines(targetPath, lines, hadTrailingNewline) { - const content = lines.join('\n') + (hadTrailingNewline ? '\n' : ''); - fs.writeFileSync(targetPath, content, 'utf-8'); + const content = lines.join("\n") + (hadTrailingNewline ? "\n" : ""); + fs.writeFileSync(targetPath, content, "utf-8"); } function applyHunk(lines, hunk, offset) { @@ -166,7 +162,7 @@ function applyHunk(lines, hunk, offset) { const expected = []; for (const raw of hunk.lines) { const marker = raw[0]; - if (marker === ' ' || marker === '+') { + if (marker === " " || marker === "+") { expected.push(raw.slice(1)); } } @@ -187,21 +183,21 @@ function applyHunk(lines, hunk, offset) { for (const raw of hunk.lines) { const marker = raw[0]; const text = raw.slice(1); - if (marker === ' ') { + if (marker === " ") { if (lines[cursor] !== text) { throw new Error( - `context mismatch at line ${cursor + 1}: expected "${text}", found "${lines[cursor] ?? ''}"`, + `context mismatch at line ${cursor + 1}: expected "${text}", found "${lines[cursor] ?? ""}"`, ); } cursor += 1; - } else if (marker === '-') { + } else if (marker === "-") { if (lines[cursor] !== text) { throw new Error( - `delete mismatch at line ${cursor + 1}: expected "${text}", found "${lines[cursor] ?? ''}"`, + `delete mismatch at line ${cursor + 1}: expected "${text}", found "${lines[cursor] ?? ""}"`, ); } lines.splice(cursor, 1); - } else if (marker === '+') { + } else if (marker === "+") { lines.splice(cursor, 0, text); cursor += 1; } else { @@ -214,11 +210,11 @@ function applyHunk(lines, hunk, offset) { } function applyPatchToFile(targetDir, filePatch) { - if (filePatch.newPath === '/dev/null') { + if (filePatch.newPath === "/dev/null") { // deletion not needed for our patches return; } - const relPath = stripPrefix(filePatch.newPath ?? filePatch.oldPath ?? ''); + const relPath = stripPrefix(filePatch.newPath ?? filePatch.oldPath ?? ""); const targetPath = path.join(targetDir, relPath); const { lines, hasTrailingNewline } = readFileLines(targetPath); @@ -232,10 +228,7 @@ function applyPatchToFile(targetDir, filePatch) { function applyPatchSet({ patchText, targetDir }) { let resolvedTarget = path.resolve(targetDir); - if ( - !fs.existsSync(resolvedTarget) || - !fs.statSync(resolvedTarget).isDirectory() - ) { + if (!fs.existsSync(resolvedTarget) || !fs.statSync(resolvedTarget).isDirectory()) { console.warn(`[postinstall] skip missing target: ${resolvedTarget}`); return; } @@ -254,7 +247,7 @@ function applyPatchFile({ patchPath, targetDir }) { if (!fs.existsSync(absPatchPath)) { throw new Error(`missing patch: ${absPatchPath}`); } - const patchText = fs.readFileSync(absPatchPath, 'utf-8'); + const patchText = fs.readFileSync(absPatchPath, "utf-8"); applyPatchSet({ patchText, targetDir }); } @@ -262,20 +255,20 @@ function trySetupCompletion(repoRoot) { // Skip in CI or if explicitly disabled if (process.env.CI || process.env.OPENCLAW_SKIP_COMPLETION_SETUP) return; - const binPath = path.join(repoRoot, 'openclaw.mjs'); + const binPath = path.join(repoRoot, "openclaw.mjs"); if (!fs.existsSync(binPath)) return; // In development, dist might not exist yet during postinstall - const distEntry = path.join(repoRoot, 'dist', 'index.js'); + const distEntry = path.join(repoRoot, "dist", "index.js"); if (!fs.existsSync(distEntry)) return; try { // Run with OPENCLAW_SKIP_POSTINSTALL to avoid any weird recursion, // though distinct from this script. - spawnSync(process.execPath, [binPath, 'completion', '--install', '--yes'], { + spawnSync(process.execPath, [binPath, "completion", "--install", "--yes"], { cwd: repoRoot, - stdio: 'inherit', - env: { ...process.env, OPENCLAW_SKIP_POSTINSTALL: '1' }, + stdio: "inherit", + env: { ...process.env, OPENCLAW_SKIP_POSTINSTALL: "1" }, }); } catch (err) { // Ignore errors to not break install @@ -286,7 +279,7 @@ function main() { const repoRoot = getRepoRoot(); process.chdir(repoRoot); - ensureExecutable(path.join(repoRoot, 'dist', '/entry.js')); + ensureExecutable(path.join(repoRoot, "dist", "/entry.js")); setupGitHooks({ repoRoot }); trySetupCompletion(repoRoot); @@ -294,18 +287,18 @@ function main() { return; } - const pkgPath = path.join(repoRoot, 'package.json'); - const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); + const pkgPath = path.join(repoRoot, "package.json"); + const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")); const patched = pkg?.pnpm?.patchedDependencies ?? {}; // Bun does not support pnpm.patchedDependencies. Apply these patch files to // node_modules packages as a best-effort compatibility layer. for (const [key, relPatchPath] of Object.entries(patched)) { - if (typeof relPatchPath !== 'string' || !relPatchPath.trim()) continue; + if (typeof relPatchPath !== "string" || !relPatchPath.trim()) continue; const pkgName = extractPackageName(String(key)); if (!pkgName) continue; applyPatchFile({ - targetDir: path.join('node_modules', ...pkgName.split('/')), + targetDir: path.join("node_modules", ...pkgName.split("/")), patchPath: relPatchPath, }); } @@ -313,10 +306,10 @@ function main() { try { const skip = - process.env.OPENCLAW_SKIP_POSTINSTALL === '1' || - process.env.CLAWDBOT_SKIP_POSTINSTALL === '1' || - process.env.VITEST === 'true' || - process.env.NODE_ENV === 'test'; + process.env.OPENCLAW_SKIP_POSTINSTALL === "1" || + process.env.CLAWDBOT_SKIP_POSTINSTALL === "1" || + process.env.VITEST === "true" || + process.env.NODE_ENV === "test"; if (!skip) { main(); diff --git a/scripts/protocol-gen-swift.ts b/scripts/protocol-gen-swift.ts index 11fb5a66ec..67bd7c30f6 100644 --- a/scripts/protocol-gen-swift.ts +++ b/scripts/protocol-gen-swift.ts @@ -1,11 +1,7 @@ import { promises as fs } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import { - ErrorCodes, - PROTOCOL_VERSION, - ProtocolSchemas, -} from "../src/gateway/protocol/schema.js"; +import { ErrorCodes, PROTOCOL_VERSION, ProtocolSchemas } from "../src/gateway/protocol/schema.js"; type JsonSchema = { type?: string | string[]; @@ -19,14 +15,7 @@ type JsonSchema = { const __dirname = path.dirname(fileURLToPath(import.meta.url)); const repoRoot = path.resolve(__dirname, ".."); const outPaths = [ - path.join( - repoRoot, - "apps", - "macos", - "Sources", - "OpenClawProtocol", - "GatewayModels.swift", - ), + path.join(repoRoot, "apps", "macos", "Sources", "OpenClawProtocol", "GatewayModels.swift"), path.join( repoRoot, "apps", @@ -38,7 +27,9 @@ const outPaths = [ ), ]; -const header = `// Generated by scripts/protocol-gen-swift.ts — do not edit by hand\nimport Foundation\n\npublic let GATEWAY_PROTOCOL_VERSION = ${PROTOCOL_VERSION}\n\npublic enum ErrorCode: String, Codable, Sendable {\n${Object.values(ErrorCodes) +const header = `// Generated by scripts/protocol-gen-swift.ts — do not edit by hand\nimport Foundation\n\npublic let GATEWAY_PROTOCOL_VERSION = ${PROTOCOL_VERSION}\n\npublic enum ErrorCode: String, Codable, Sendable {\n${Object.values( + ErrorCodes, +) .map((c) => ` case ${camelCase(c)} = "${c}"`) .join("\n")}\n}\n`; @@ -133,25 +124,27 @@ function emitStruct(name: string, schema: JsonSchema): string { codingKeys.push(` case ${propName}`); } } - lines.push("\n public init(\n" + - Object.entries(props) - .map(([key, prop]) => { - const propName = safeName(key); - const req = required.has(key); - return ` ${propName}: ${swiftType(prop, true)}${req ? "" : "?"}`; - }) - .join(",\n") + - "\n ) {\n" + - Object.entries(props) - .map(([key]) => { - const propName = safeName(key); - return ` self.${propName} = ${propName}`; - }) - .join("\n") + - "\n }\n" + - " private enum CodingKeys: String, CodingKey {\n" + - codingKeys.join("\n") + - "\n }\n}"); + lines.push( + "\n public init(\n" + + Object.entries(props) + .map(([key, prop]) => { + const propName = safeName(key); + const req = required.has(key); + return ` ${propName}: ${swiftType(prop, true)}${req ? "" : "?"}`; + }) + .join(",\n") + + "\n ) {\n" + + Object.entries(props) + .map(([key]) => { + const propName = safeName(key); + return ` self.${propName} = ${propName}`; + }) + .join("\n") + + "\n }\n" + + " private enum CodingKeys: String, CodingKey {\n" + + codingKeys.join("\n") + + "\n }\n}", + ); lines.push(""); return lines.join("\n"); } @@ -209,9 +202,7 @@ function emitGatewayFrame(): string { } async function generate() { - const definitions = Object.entries(ProtocolSchemas) as Array< - [string, JsonSchema] - >; + const definitions = Object.entries(ProtocolSchemas) as Array<[string, JsonSchema]>; for (const [name, schema] of definitions) { schemaNameByObject.set(schema as object, name); diff --git a/scripts/release-check.ts b/scripts/release-check.ts index de7e591eee..7dba830c79 100755 --- a/scripts/release-check.ts +++ b/scripts/release-check.ts @@ -7,11 +7,7 @@ import { join, resolve } from "node:path"; type PackFile = { path: string }; type PackResult = { files?: PackFile[] }; -const requiredPaths = [ - "dist/discord/send.js", - "dist/hooks/gmail.js", - "dist/whatsapp/normalize.js", -]; +const requiredPaths = ["dist/discord/send.js", "dist/hooks/gmail.js", "dist/whatsapp/normalize.js"]; const forbiddenPrefixes = ["dist/OpenClaw.app/"]; type PackageJson = { diff --git a/scripts/run-node.mjs b/scripts/run-node.mjs index bb3bc28146..557e6729fc 100644 --- a/scripts/run-node.mjs +++ b/scripts/run-node.mjs @@ -1,24 +1,21 @@ #!/usr/bin/env node -import { spawn } from 'node:child_process'; -import fs from 'node:fs'; -import path from 'node:path'; -import process from 'node:process'; +import { spawn } from "node:child_process"; +import fs from "node:fs"; +import path from "node:path"; +import process from "node:process"; const args = process.argv.slice(2); const env = { ...process.env }; const cwd = process.cwd(); const compilerOverride = env.OPENCLAW_TS_COMPILER ?? env.CLAWDBOT_TS_COMPILER; -const compiler = compilerOverride === 'tsc' ? 'tsc' : 'tsgo'; -const projectArgs = ['--project', 'tsconfig.json']; +const compiler = compilerOverride === "tsc" ? "tsc" : "tsgo"; +const projectArgs = ["--project", "tsconfig.json"]; -const distRoot = path.join(cwd, 'dist'); -const distEntry = path.join(distRoot, '/entry.js'); -const buildStampPath = path.join(distRoot, '.buildstamp'); -const srcRoot = path.join(cwd, 'src'); -const configFiles = [ - path.join(cwd, 'tsconfig.json'), - path.join(cwd, 'package.json'), -]; +const distRoot = path.join(cwd, "dist"); +const distEntry = path.join(distRoot, "/entry.js"); +const buildStampPath = path.join(distRoot, ".buildstamp"); +const srcRoot = path.join(cwd, "src"); +const configFiles = [path.join(cwd, "tsconfig.json"), path.join(cwd, "package.json")]; const statMtime = (filePath) => { try { @@ -30,10 +27,10 @@ const statMtime = (filePath) => { const isExcludedSource = (filePath) => { const relativePath = path.relative(srcRoot, filePath); - if (relativePath.startsWith('..')) return false; + if (relativePath.startsWith("..")) return false; return ( - relativePath.endsWith('.test.ts') || - relativePath.endsWith('.test.tsx') || + relativePath.endsWith(".test.ts") || + relativePath.endsWith(".test.tsx") || relativePath.endsWith(`test-helpers.ts`) ); }; @@ -69,7 +66,7 @@ const findLatestMtime = (dirPath, shouldSkip) => { }; const shouldBuild = () => { - if (env.OPENCLAW_FORCE_BUILD === '1') return true; + if (env.OPENCLAW_FORCE_BUILD === "1") return true; const stampMtime = statMtime(buildStampPath); if (stampMtime == null) return true; if (statMtime(distEntry) == null) return true; @@ -85,18 +82,18 @@ const shouldBuild = () => { }; const logRunner = (message) => { - if (env.OPENCLAW_RUNNER_LOG === '0') return; + if (env.OPENCLAW_RUNNER_LOG === "0") return; process.stderr.write(`[openclaw] ${message}\n`); }; const runNode = () => { - const nodeProcess = spawn(process.execPath, ['openclaw.mjs', ...args], { + const nodeProcess = spawn(process.execPath, ["openclaw.mjs", ...args], { cwd, env, - stdio: 'inherit', + stdio: "inherit", }); - nodeProcess.on('exit', (exitCode, exitSignal) => { + nodeProcess.on("exit", (exitCode, exitSignal) => { if (exitSignal) { process.exit(1); } @@ -110,29 +107,25 @@ const writeBuildStamp = () => { fs.writeFileSync(buildStampPath, `${Date.now()}\n`); } catch (error) { // Best-effort stamp; still allow the runner to start. - logRunner( - `Failed to write build stamp: ${error?.message ?? 'unknown error'}`, - ); + logRunner(`Failed to write build stamp: ${error?.message ?? "unknown error"}`); } }; if (!shouldBuild()) { runNode(); } else { - logRunner('Building TypeScript (dist is stale).'); - const pnpmArgs = ['exec', compiler, ...projectArgs]; - const buildCmd = process.platform === 'win32' ? 'cmd.exe' : 'pnpm'; + logRunner("Building TypeScript (dist is stale)."); + const pnpmArgs = ["exec", compiler, ...projectArgs]; + const buildCmd = process.platform === "win32" ? "cmd.exe" : "pnpm"; const buildArgs = - process.platform === 'win32' - ? ['/d', '/s', '/c', 'pnpm', ...pnpmArgs] - : pnpmArgs; + process.platform === "win32" ? ["/d", "/s", "/c", "pnpm", ...pnpmArgs] : pnpmArgs; const build = spawn(buildCmd, buildArgs, { cwd, env, - stdio: 'inherit', + stdio: "inherit", }); - build.on('exit', (code, signal) => { + build.on("exit", (code, signal) => { if (signal) { process.exit(1); } diff --git a/scripts/sqlite-vec-smoke.mjs b/scripts/sqlite-vec-smoke.mjs index 2b54595e73..19a3c5784d 100644 --- a/scripts/sqlite-vec-smoke.mjs +++ b/scripts/sqlite-vec-smoke.mjs @@ -31,9 +31,7 @@ insert.run("c", vec([0.2, 0.2, 0, 0])); const query = vec([1, 0, 0, 0]); const rows = db - .prepare( - "SELECT id, vec_distance_cosine(embedding, ?) AS dist FROM v ORDER BY dist ASC" - ) + .prepare("SELECT id, vec_distance_cosine(embedding, ?) AS dist FROM v ORDER BY dist ASC") .all(query); console.log("sqlite-vec ok"); diff --git a/scripts/sync-labels.ts b/scripts/sync-labels.ts index 297644c1ee..c31983cca1 100644 --- a/scripts/sync-labels.ts +++ b/scripts/sync-labels.ts @@ -36,16 +36,7 @@ for (const label of missing) { const color = pickColor(label); execFileSync( "gh", - [ - "api", - "-X", - "POST", - `repos/${repo}/labels`, - "-f", - `name=${label}`, - "-f", - `color=${color}`, - ], + ["api", "-X", "POST", `repos/${repo}/labels`, "-f", `name=${label}`, "-f", `color=${color}`], { stdio: "inherit" }, ); console.log(`Created label: ${label}`); @@ -97,11 +88,9 @@ function resolveRepo(): string { } function fetchExistingLabels(repo: string): Map { - const raw = execFileSync( - "gh", - ["api", `repos/${repo}/labels?per_page=100`, "--paginate"], - { encoding: "utf8" }, - ); + const raw = execFileSync("gh", ["api", `repos/${repo}/labels?per_page=100`, "--paginate"], { + encoding: "utf8", + }); const labels = JSON.parse(raw) as RepoLabel[]; return new Map(labels.map((label) => [label.name, label])); } diff --git a/scripts/sync-moonshot-docs.ts b/scripts/sync-moonshot-docs.ts index a0d83c40ac..656d7eaa3e 100644 --- a/scripts/sync-moonshot-docs.ts +++ b/scripts/sync-moonshot-docs.ts @@ -49,9 +49,7 @@ function replaceBlockLines( } function renderKimiK2Ids(prefix: string) { - return MOONSHOT_KIMI_K2_MODELS.map( - (model) => `- \`${prefix}${model.id}\``, - ); + return MOONSHOT_KIMI_K2_MODELS.map((model) => `- \`${prefix}${model.id}\``); } function renderMoonshotAliases() { @@ -85,10 +83,7 @@ function renderMoonshotModels() { async function syncMoonshotDocs() { const moonshotDoc = path.join(repoRoot, "docs/providers/moonshot.md"); - const conceptsDoc = path.join( - repoRoot, - "docs/concepts/model-providers.md", - ); + const conceptsDoc = path.join(repoRoot, "docs/concepts/model-providers.md"); let moonshotText = await readFile(moonshotDoc, "utf8"); moonshotText = replaceBlockLines( diff --git a/scripts/sync-plugin-versions.ts b/scripts/sync-plugin-versions.ts index 8ec690ba69..d06aceab62 100644 --- a/scripts/sync-plugin-versions.ts +++ b/scripts/sync-plugin-versions.ts @@ -16,7 +16,9 @@ if (!targetVersion) { } const extensionsDir = resolve("extensions"); -const dirs = readdirSync(extensionsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()); +const dirs = readdirSync(extensionsDir, { withFileTypes: true }).filter((entry) => + entry.isDirectory(), +); const updated: string[] = []; const changelogged: string[] = []; @@ -67,5 +69,5 @@ for (const dir of dirs) { } console.log( - `Synced plugin versions to ${targetVersion}. Updated: ${updated.length}. Changelogged: ${changelogged.length}. Skipped: ${skipped.length}.` + `Synced plugin versions to ${targetVersion}. Updated: ${updated.length}. Changelogged: ${changelogged.length}. Skipped: ${skipped.length}.`, ); diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index 433b37376a..d695831ccc 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -24,10 +24,17 @@ const isMacOS = process.platform === "darwin" || process.env.RUNNER_OS === "macO const isWindows = process.platform === "win32" || process.env.RUNNER_OS === "Windows"; const isWindowsCi = isCI && isWindows; const shardOverride = Number.parseInt(process.env.OPENCLAW_TEST_SHARDS ?? "", 10); -const shardCount = isWindowsCi ? (Number.isFinite(shardOverride) && shardOverride > 1 ? shardOverride : 2) : 1; -const windowsCiArgs = isWindowsCi ? ["--no-file-parallelism", "--dangerouslyIgnoreUnhandledErrors"] : []; +const shardCount = isWindowsCi + ? Number.isFinite(shardOverride) && shardOverride > 1 + ? shardOverride + : 2 + : 1; +const windowsCiArgs = isWindowsCi + ? ["--no-file-parallelism", "--dangerouslyIgnoreUnhandledErrors"] + : []; const overrideWorkers = Number.parseInt(process.env.OPENCLAW_TEST_WORKERS ?? "", 10); -const resolvedOverride = Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null; +const resolvedOverride = + Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null; const parallelRuns = isWindowsCi ? [] : runs.filter((entry) => entry.name !== "gateway"); const serialRuns = isWindowsCi ? runs : runs.filter((entry) => entry.name === "gateway"); const localWorkers = Math.max(4, Math.min(16, os.cpus().length)); diff --git a/scripts/ui.js b/scripts/ui.js index 16ad6b0c2f..ba5b62922f 100644 --- a/scripts/ui.js +++ b/scripts/ui.js @@ -11,9 +11,7 @@ const uiDir = path.join(repoRoot, "ui"); function usage() { // keep this tiny; it's invoked from npm scripts too - process.stderr.write( - "Usage: node scripts/ui.js [...args]\n", - ); + process.stderr.write("Usage: node scripts/ui.js [...args]\n"); } function which(cmd) { @@ -24,9 +22,7 @@ function which(cmd) { .filter(Boolean); const extensions = process.platform === "win32" - ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM") - .split(";") - .filter(Boolean) + ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean) : [""]; for (const entry of paths) { for (const ext of extensions) { @@ -122,11 +118,8 @@ if (action === "install") run(runner.cmd, ["install", ...rest]); else { if (!depsInstalled(action === "test" ? "test" : "build")) { const installEnv = - action === "build" - ? { ...process.env, NODE_ENV: "production" } - : process.env; - const installArgs = - action === "build" ? ["install", "--prod"] : ["install"]; + action === "build" ? { ...process.env, NODE_ENV: "production" } : process.env; + const installArgs = action === "build" ? ["install", "--prod"] : ["install"]; runSync(runner.cmd, installArgs, installEnv); } run(runner.cmd, ["run", script, ...rest]); diff --git a/scripts/update-clawtributors.ts b/scripts/update-clawtributors.ts index f08515f059..676374332b 100644 --- a/scripts/update-clawtributors.ts +++ b/scripts/update-clawtributors.ts @@ -329,7 +329,7 @@ function resolveLogin( email: string | null, apiByLogin: Map, nameToLogin: Record, - emailToLogin: Record + emailToLogin: Record, ): string | null { if (email && emailToLogin[email]) { return emailToLogin[email]; @@ -379,7 +379,7 @@ function resolveLogin( function guessLoginFromEmailName( name: string, email: string, - apiByLogin: Map + apiByLogin: Map, ): string | null { const local = email.split("@", 1)[0]?.trim(); if (!local) { @@ -410,7 +410,7 @@ function normalizeIdentifier(value: string): string { } function parseReadmeEntries( - content: string + content: string, ): Array<{ display: string; html_url: string; avatar_url: string }> { const start = content.indexOf('

'); const end = content.indexOf("

", start); @@ -458,7 +458,11 @@ function fallbackHref(value: string): string { return encoded ? `https://github.com/search?q=${encoded}` : "https://github.com"; } -function pickDisplay(baseName: string | null | undefined, login: string, existing?: string): string { +function pickDisplay( + baseName: string | null | undefined, + login: string, + existing?: string, +): string { const key = login.toLowerCase(); if (displayName[key]) { return displayName[key]; diff --git a/scripts/write-build-info.ts b/scripts/write-build-info.ts index 28b3499bcf..d67d25c27c 100644 --- a/scripts/write-build-info.ts +++ b/scripts/write-build-info.ts @@ -42,7 +42,4 @@ const buildInfo = { }; fs.mkdirSync(distDir, { recursive: true }); -fs.writeFileSync( - path.join(distDir, "build-info.json"), - `${JSON.stringify(buildInfo, null, 2)}\n`, -); +fs.writeFileSync(path.join(distDir, "build-info.json"), `${JSON.stringify(buildInfo, null, 2)}\n`); diff --git a/scripts/zai-fallback-repro.ts b/scripts/zai-fallback-repro.ts index 1be60dce5f..7a1df84d31 100644 --- a/scripts/zai-fallback-repro.ts +++ b/scripts/zai-fallback-repro.ts @@ -20,9 +20,7 @@ function pickAnthropicEnv(): { type: "oauth" | "api"; value: string } | null { } function pickZaiKey(): string | null { - return ( - process.env.ZAI_API_KEY?.trim() ?? process.env.Z_AI_API_KEY?.trim() ?? null - ); + return process.env.ZAI_API_KEY?.trim() ?? process.env.Z_AI_API_KEY?.trim() ?? null; } async function runCommand( @@ -74,9 +72,7 @@ async function main() { process.exit(1); } - const baseDir = await fs.mkdtemp( - path.join(os.tmpdir(), "openclaw-zai-fallback-"), - ); + const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-zai-fallback-")); const stateDir = path.join(baseDir, "state"); const configPath = path.join(baseDir, "openclaw.json"); await fs.mkdir(stateDir, { recursive: true }); @@ -130,28 +126,14 @@ async function main() { "Then use the read tool to display the file contents. Reply with just the file contents."; const run1 = await runCommand( "run1", - [ - "openclaw", - "agent", - "--local", - "--session-id", - sessionId, - "--message", - toolPrompt, - ], + ["openclaw", "agent", "--local", "--session-id", sessionId, "--message", toolPrompt], envValidAnthropic, ); if (run1.code !== 0) { process.exit(run1.code ?? 1); } - const sessionFile = path.join( - stateDir, - "agents", - "main", - "sessions", - `${sessionId}.jsonl`, - ); + const sessionFile = path.join(stateDir, "agents", "main", "sessions", `${sessionId}.jsonl`); const transcript = await fs.readFile(sessionFile, "utf8").catch(() => ""); if (!transcript.includes('"toolResult"')) { console.warn("Warning: no toolResult entries detected in session history."); @@ -162,15 +144,7 @@ async function main() { "What is the content of zai-fallback-tool.txt? Reply with just the contents."; const run2 = await runCommand( "run2", - [ - "openclaw", - "agent", - "--local", - "--session-id", - sessionId, - "--message", - followupPrompt, - ], + ["openclaw", "agent", "--local", "--session-id", sessionId, "--message", followupPrompt], envInvalidAnthropic, ); diff --git a/skills/1password/SKILL.md b/skills/1password/SKILL.md index 567b0b9ba7..c3dcb82be5 100644 --- a/skills/1password/SKILL.md +++ b/skills/1password/SKILL.md @@ -2,7 +2,24 @@ name: 1password description: Set up and use 1Password CLI (op). Use when installing the CLI, enabling desktop app integration, signing in (single or multi-account), or reading/injecting/running secrets via op. homepage: https://developer.1password.com/docs/cli/get-started/ -metadata: {"openclaw":{"emoji":"🔐","requires":{"bins":["op"]},"install":[{"id":"brew","kind":"brew","formula":"1password-cli","bins":["op"],"label":"Install 1Password CLI (brew)"}]}} +metadata: + { + "openclaw": + { + "emoji": "🔐", + "requires": { "bins": ["op"] }, + "install": + [ + { + "id": "brew", + "kind": "brew", + "formula": "1password-cli", + "bins": ["op"], + "label": "Install 1Password CLI (brew)", + }, + ], + }, + } --- # 1Password CLI diff --git a/skills/apple-notes/SKILL.md b/skills/apple-notes/SKILL.md index 761a5945ba..61e8cf76d9 100644 --- a/skills/apple-notes/SKILL.md +++ b/skills/apple-notes/SKILL.md @@ -2,7 +2,25 @@ name: apple-notes description: Manage Apple Notes via the `memo` CLI on macOS (create, view, edit, delete, search, move, and export notes). Use when a user asks OpenClaw to add a note, list notes, search notes, or manage note folders. homepage: https://github.com/antoniorodr/memo -metadata: {"openclaw":{"emoji":"📝","os":["darwin"],"requires":{"bins":["memo"]},"install":[{"id":"brew","kind":"brew","formula":"antoniorodr/memo/memo","bins":["memo"],"label":"Install memo via Homebrew"}]}} +metadata: + { + "openclaw": + { + "emoji": "📝", + "os": ["darwin"], + "requires": { "bins": ["memo"] }, + "install": + [ + { + "id": "brew", + "kind": "brew", + "formula": "antoniorodr/memo/memo", + "bins": ["memo"], + "label": "Install memo via Homebrew", + }, + ], + }, + } --- # Apple Notes CLI @@ -10,41 +28,50 @@ metadata: {"openclaw":{"emoji":"📝","os":["darwin"],"requires":{"bins":["memo" Use `memo notes` to manage Apple Notes directly from the terminal. Create, view, edit, delete, search, move notes between folders, and export to HTML/Markdown. Setup + - Install (Homebrew): `brew tap antoniorodr/memo && brew install antoniorodr/memo/memo` - Manual (pip): `pip install .` (after cloning the repo) - macOS-only; if prompted, grant Automation access to Notes.app. View Notes + - List all notes: `memo notes` - Filter by folder: `memo notes -f "Folder Name"` - Search notes (fuzzy): `memo notes -s "query"` Create Notes + - Add a new note: `memo notes -a` - Opens an interactive editor to compose the note. - Quick add with title: `memo notes -a "Note Title"` Edit Notes + - Edit existing note: `memo notes -e` - Interactive selection of note to edit. Delete Notes + - Delete a note: `memo notes -d` - Interactive selection of note to delete. Move Notes + - Move note to folder: `memo notes -m` - Interactive selection of note and destination folder. Export Notes + - Export to HTML/Markdown: `memo notes -ex` - Exports selected note; uses Mistune for markdown processing. Limitations + - Cannot edit notes containing images or attachments. - Interactive prompts may require terminal access. Notes + - macOS-only. - Requires Apple Notes.app to be accessible. - For automation, grant permissions in System Settings > Privacy & Security > Automation. diff --git a/skills/apple-reminders/SKILL.md b/skills/apple-reminders/SKILL.md index c59fb6fa60..97da02761a 100644 --- a/skills/apple-reminders/SKILL.md +++ b/skills/apple-reminders/SKILL.md @@ -2,7 +2,25 @@ name: apple-reminders description: Manage Apple Reminders via the `remindctl` CLI on macOS (list, add, edit, complete, delete). Supports lists, date filters, and JSON/plain output. homepage: https://github.com/steipete/remindctl -metadata: {"openclaw":{"emoji":"⏰","os":["darwin"],"requires":{"bins":["remindctl"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/remindctl","bins":["remindctl"],"label":"Install remindctl via Homebrew"}]}} +metadata: + { + "openclaw": + { + "emoji": "⏰", + "os": ["darwin"], + "requires": { "bins": ["remindctl"] }, + "install": + [ + { + "id": "brew", + "kind": "brew", + "formula": "steipete/tap/remindctl", + "bins": ["remindctl"], + "label": "Install remindctl via Homebrew", + }, + ], + }, + } --- # Apple Reminders CLI (remindctl) @@ -10,15 +28,18 @@ metadata: {"openclaw":{"emoji":"⏰","os":["darwin"],"requires":{"bins":["remind Use `remindctl` to manage Apple Reminders directly from the terminal. It supports list filtering, date-based views, and scripting output. Setup + - Install (Homebrew): `brew install steipete/tap/remindctl` - From source: `pnpm install && pnpm build` (binary at `./bin/remindctl`) - macOS-only; grant Reminders permission when prompted. Permissions + - Check status: `remindctl status` - Request access: `remindctl authorize` View Reminders + - Default (today): `remindctl` - Today: `remindctl today` - Tomorrow: `remindctl tomorrow` @@ -30,6 +51,7 @@ View Reminders - Specific date: `remindctl 2026-01-04` Manage Lists + - List all lists: `remindctl list` - Show list: `remindctl list Work` - Create list: `remindctl list Projects --create` @@ -37,31 +59,38 @@ Manage Lists - Delete list: `remindctl list Work --delete` Create Reminders + - Quick add: `remindctl add "Buy milk"` - With list + due: `remindctl add --title "Call mom" --list Personal --due tomorrow` Edit Reminders + - Edit title/due: `remindctl edit 1 --title "New title" --due 2026-01-04` Complete Reminders + - Complete by id: `remindctl complete 1 2 3` Delete Reminders + - Delete by id: `remindctl delete 4A83 --force` Output Formats + - JSON (scripting): `remindctl today --json` - Plain TSV: `remindctl today --plain` - Counts only: `remindctl today --quiet` Date Formats Accepted by `--due` and date filters: + - `today`, `tomorrow`, `yesterday` - `YYYY-MM-DD` - `YYYY-MM-DD HH:mm` - ISO 8601 (`2026-01-04T12:34:56Z`) Notes + - macOS-only. - If access is denied, enable Terminal/remindctl in System Settings → Privacy & Security → Reminders. - If running over SSH, grant access on the Mac that runs the command. diff --git a/skills/bear-notes/SKILL.md b/skills/bear-notes/SKILL.md index b738ec33c8..ca36f1907d 100644 --- a/skills/bear-notes/SKILL.md +++ b/skills/bear-notes/SKILL.md @@ -2,7 +2,25 @@ name: bear-notes description: Create, search, and manage Bear notes via grizzly CLI. homepage: https://bear.app -metadata: {"openclaw":{"emoji":"🐻","os":["darwin"],"requires":{"bins":["grizzly"]},"install":[{"id":"go","kind":"go","module":"github.com/tylerwince/grizzly/cmd/grizzly@latest","bins":["grizzly"],"label":"Install grizzly (go)"}]}} +metadata: + { + "openclaw": + { + "emoji": "🐻", + "os": ["darwin"], + "requires": { "bins": ["grizzly"] }, + "install": + [ + { + "id": "go", + "kind": "go", + "module": "github.com/tylerwince/grizzly/cmd/grizzly@latest", + "bins": ["grizzly"], + "label": "Install grizzly (go)", + }, + ], + }, + } --- # Bear Notes @@ -10,39 +28,46 @@ metadata: {"openclaw":{"emoji":"🐻","os":["darwin"],"requires":{"bins":["grizz Use `grizzly` to create, read, and manage notes in Bear on macOS. Requirements + - Bear app installed and running - For some operations (add-text, tags, open-note --selected), a Bear app token (stored in `~/.config/grizzly/token`) ## Getting a Bear Token For operations that require a token (add-text, tags, open-note --selected), you need an authentication token: + 1. Open Bear → Help → API Token → Copy Token 2. Save it: `echo "YOUR_TOKEN" > ~/.config/grizzly/token` ## Common Commands Create a note + ```bash echo "Note content here" | grizzly create --title "My Note" --tag work grizzly create --title "Quick Note" --tag inbox < /dev/null ``` Open/read a note by ID + ```bash grizzly open-note --id "NOTE_ID" --enable-callback --json ``` Append text to a note + ```bash echo "Additional content" | grizzly add-text --id "NOTE_ID" --mode append --token-file ~/.config/grizzly/token ``` List all tags + ```bash grizzly tags --enable-callback --json --token-file ~/.config/grizzly/token ``` Search notes (via open-tag) + ```bash grizzly open-tag --name "work" --enable-callback --json ``` @@ -50,6 +75,7 @@ grizzly open-tag --name "work" --enable-callback --json ## Options Common flags: + - `--dry-run` — Preview the URL without executing - `--print-url` — Show the x-callback-url - `--enable-callback` — Wait for Bear's response (needed for reading data) @@ -59,12 +85,14 @@ Common flags: ## Configuration Grizzly reads config from (in priority order): + 1. CLI flags 2. Environment variables (`GRIZZLY_TOKEN_FILE`, `GRIZZLY_CALLBACK_URL`, `GRIZZLY_TIMEOUT`) 3. `.grizzly.toml` in current directory 4. `~/.config/grizzly/config.toml` Example `~/.config/grizzly/config.toml`: + ```toml token_file = "~/.config/grizzly/token" callback_url = "http://127.0.0.1:42123/success" diff --git a/skills/bird/SKILL.md b/skills/bird/SKILL.md index 40547b3743..090ec528f5 100644 --- a/skills/bird/SKILL.md +++ b/skills/bird/SKILL.md @@ -2,7 +2,32 @@ name: bird description: X/Twitter CLI for reading, searching, posting, and engagement via cookies. homepage: https://bird.fast -metadata: {"openclaw":{"emoji":"🐦","requires":{"bins":["bird"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/bird","bins":["bird"],"label":"Install bird (brew)","os":["darwin"]},{"id":"npm","kind":"node","package":"@steipete/bird","bins":["bird"],"label":"Install bird (npm)"}]}} +metadata: + { + "openclaw": + { + "emoji": "🐦", + "requires": { "bins": ["bird"] }, + "install": + [ + { + "id": "brew", + "kind": "brew", + "formula": "steipete/tap/bird", + "bins": ["bird"], + "label": "Install bird (brew)", + "os": ["darwin"], + }, + { + "id": "npm", + "kind": "node", + "package": "@steipete/bird", + "bins": ["bird"], + "label": "Install bird (npm)", + }, + ], + }, + } --- # bird 🐦 @@ -174,7 +199,7 @@ bird replies --all --delay 1000 # Delay between pages (ms) cookieSource: ["chrome"], chromeProfileDir: "/path/to/Arc/Profile", timeoutMs: 20000, - quoteDepth: 1 + quoteDepth: 1, } ``` @@ -183,11 +208,13 @@ Environment variables: `BIRD_TIMEOUT_MS`, `BIRD_COOKIE_TIMEOUT_MS`, `BIRD_QUOTE_ ## Troubleshooting ### Query IDs stale (404 errors) + ```bash bird query-ids --fresh ``` ### Cookie extraction fails + - Check browser is logged into X - Try different `--cookie-source` - For Arc/Brave: use `--chrome-profile-dir` diff --git a/skills/blogwatcher/SKILL.md b/skills/blogwatcher/SKILL.md index 3bbdcfd247..8d623a06e9 100644 --- a/skills/blogwatcher/SKILL.md +++ b/skills/blogwatcher/SKILL.md @@ -2,7 +2,24 @@ name: blogwatcher description: Monitor blogs and RSS/Atom feeds for updates using the blogwatcher CLI. homepage: https://github.com/Hyaxia/blogwatcher -metadata: {"openclaw":{"emoji":"📰","requires":{"bins":["blogwatcher"]},"install":[{"id":"go","kind":"go","module":"github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest","bins":["blogwatcher"],"label":"Install blogwatcher (go)"}]}} +metadata: + { + "openclaw": + { + "emoji": "📰", + "requires": { "bins": ["blogwatcher"] }, + "install": + [ + { + "id": "go", + "kind": "go", + "module": "github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest", + "bins": ["blogwatcher"], + "label": "Install blogwatcher (go)", + }, + ], + }, + } --- # blogwatcher @@ -10,12 +27,15 @@ metadata: {"openclaw":{"emoji":"📰","requires":{"bins":["blogwatcher"]},"insta Track blog and RSS/Atom feed updates with the `blogwatcher` CLI. Install + - Go: `go install github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest` Quick start + - `blogwatcher --help` Common commands + - Add a blog: `blogwatcher add "My Blog" https://example.com` - List blogs: `blogwatcher blogs` - Scan for updates: `blogwatcher scan` @@ -25,6 +45,7 @@ Common commands - Remove a blog: `blogwatcher remove "My Blog"` Example output + ``` $ blogwatcher blogs Tracked blogs (1): @@ -32,6 +53,7 @@ Tracked blogs (1): xkcd URL: https://xkcd.com ``` + ``` $ blogwatcher scan Scanning 1 blog(s)... @@ -43,4 +65,5 @@ Found 4 new article(s) total! ``` Notes + - Use `blogwatcher --help` to discover flags and options. diff --git a/skills/blucli/SKILL.md b/skills/blucli/SKILL.md index 9fc9aff33a..5cd56d123d 100644 --- a/skills/blucli/SKILL.md +++ b/skills/blucli/SKILL.md @@ -2,7 +2,24 @@ name: blucli description: BluOS CLI (blu) for discovery, playback, grouping, and volume. homepage: https://blucli.sh -metadata: {"openclaw":{"emoji":"🫐","requires":{"bins":["blu"]},"install":[{"id":"go","kind":"go","module":"github.com/steipete/blucli/cmd/blu@latest","bins":["blu"],"label":"Install blucli (go)"}]}} +metadata: + { + "openclaw": + { + "emoji": "🫐", + "requires": { "bins": ["blu"] }, + "install": + [ + { + "id": "go", + "kind": "go", + "module": "github.com/steipete/blucli/cmd/blu@latest", + "bins": ["blu"], + "label": "Install blucli (go)", + }, + ], + }, + } --- # blucli (blu) @@ -10,17 +27,20 @@ metadata: {"openclaw":{"emoji":"🫐","requires":{"bins":["blu"]},"install":[{"i Use `blu` to control Bluesound/NAD players. Quick start + - `blu devices` (pick target) - `blu --device status` - `blu play|pause|stop` - `blu volume set 15` Target selection (in priority order) + - `--device ` - `BLU_DEVICE` - config default (if set) Common tasks + - Grouping: `blu group status|add|remove` - TuneIn search/play: `blu tunein search "query"`, `blu tunein play "query"` diff --git a/skills/bluebubbles/SKILL.md b/skills/bluebubbles/SKILL.md index 0787540472..929a75e7d3 100644 --- a/skills/bluebubbles/SKILL.md +++ b/skills/bluebubbles/SKILL.md @@ -8,6 +8,7 @@ description: Build or update the BlueBubbles external channel plugin for OpenCla Use this skill when working on the BlueBubbles channel plugin. ## Layout + - Extension package: `extensions/bluebubbles/` (entry: `index.ts`). - Channel implementation: `extensions/bluebubbles/src/channel.ts`. - Webhook handling: `extensions/bluebubbles/src/monitor.ts` (register via `api.registerHttpHandler`). @@ -16,6 +17,7 @@ Use this skill when working on the BlueBubbles channel plugin. - Catalog entry for onboarding: `src/channels/plugins/catalog.ts`. ## Internal helpers (use these, not raw API calls) + - `probeBlueBubbles` in `extensions/bluebubbles/src/probe.ts` for health checks. - `sendMessageBlueBubbles` in `extensions/bluebubbles/src/send.ts` for text delivery. - `resolveChatGuidForTarget` in `extensions/bluebubbles/src/send.ts` for chat lookup. @@ -25,6 +27,7 @@ Use this skill when working on the BlueBubbles channel plugin. - `buildBlueBubblesApiUrl` + `blueBubblesFetchWithTimeout` in `extensions/bluebubbles/src/types.ts` for shared REST plumbing. ## Webhooks + - BlueBubbles posts JSON to the gateway HTTP server. - Normalize sender/chat IDs defensively (payloads vary by version). - Skip messages marked as from self. @@ -32,8 +35,10 @@ Use this skill when working on the BlueBubbles channel plugin. - For attachments/stickers, use `` placeholders when text is empty and attach media paths via `MediaUrl(s)` in the inbound context. ## Config (core) + - `channels.bluebubbles.serverUrl` (base URL), `channels.bluebubbles.password`, `channels.bluebubbles.webhookPath`. - Action gating: `channels.bluebubbles.actions.reactions` (default true). ## Message tool notes + - **Reactions:** The `react` action requires a `target` (phone number or chat identifier) in addition to `messageId`. Example: `action=react target=+15551234567 messageId=ABC123 emoji=❤️` diff --git a/skills/camsnap/SKILL.md b/skills/camsnap/SKILL.md index 0e2f309744..ba11a0852a 100644 --- a/skills/camsnap/SKILL.md +++ b/skills/camsnap/SKILL.md @@ -2,7 +2,24 @@ name: camsnap description: Capture frames or clips from RTSP/ONVIF cameras. homepage: https://camsnap.ai -metadata: {"openclaw":{"emoji":"📸","requires":{"bins":["camsnap"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/camsnap","bins":["camsnap"],"label":"Install camsnap (brew)"}]}} +metadata: + { + "openclaw": + { + "emoji": "📸", + "requires": { "bins": ["camsnap"] }, + "install": + [ + { + "id": "brew", + "kind": "brew", + "formula": "steipete/tap/camsnap", + "bins": ["camsnap"], + "label": "Install camsnap (brew)", + }, + ], + }, + } --- # camsnap @@ -10,10 +27,12 @@ metadata: {"openclaw":{"emoji":"📸","requires":{"bins":["camsnap"]},"install": Use `camsnap` to grab snapshots, clips, or motion events from configured cameras. Setup + - Config file: `~/.config/camsnap/config.yaml` - Add camera: `camsnap add --name kitchen --host 192.168.0.10 --user user --pass pass` Common commands + - Discover: `camsnap discover --info` - Snapshot: `camsnap snap kitchen --out shot.jpg` - Clip: `camsnap clip kitchen --dur 5s --out clip.mp4` @@ -21,5 +40,6 @@ Common commands - Doctor: `camsnap doctor --probe` Notes + - Requires `ffmpeg` on PATH. - Prefer a short test capture before longer clips. diff --git a/skills/canvas/SKILL.md b/skills/canvas/SKILL.md index 09237444cf..9148ae9cd1 100644 --- a/skills/canvas/SKILL.md +++ b/skills/canvas/SKILL.md @@ -5,6 +5,7 @@ Display HTML content on connected OpenClaw nodes (Mac app, iOS, Android). ## Overview The canvas tool lets you present web content on any connected node's canvas view. Great for: + - Displaying games, visualizations, dashboards - Showing generated HTML content - Interactive demos @@ -29,14 +30,15 @@ The canvas tool lets you present web content on any connected node's canvas view The canvas host server binds based on `gateway.bind` setting: -| Bind Mode | Server Binds To | Canvas URL Uses | -|-----------|-----------------|-----------------| -| `loopback` | 127.0.0.1 | localhost (local only) | -| `lan` | LAN interface | LAN IP address | -| `tailnet` | Tailscale interface | Tailscale hostname | -| `auto` | Best available | Tailscale > LAN > loopback | +| Bind Mode | Server Binds To | Canvas URL Uses | +| ---------- | ------------------- | -------------------------- | +| `loopback` | 127.0.0.1 | localhost (local only) | +| `lan` | LAN interface | LAN IP address | +| `tailnet` | Tailscale interface | Tailscale hostname | +| `auto` | Best available | Tailscale > LAN > loopback | **Key insight:** The `canvasHostHostForBridge` is derived from `bridgeHost`. When bound to Tailscale, nodes receive URLs like: + ``` http://:18793/__moltbot__/canvas/.html ``` @@ -45,13 +47,13 @@ This is why localhost URLs don't work - the node receives the Tailscale hostname ## Actions -| Action | Description | -|--------|-------------| -| `present` | Show canvas with optional target URL | -| `hide` | Hide the canvas | -| `navigate` | Navigate to a new URL | -| `eval` | Execute JavaScript in the canvas | -| `snapshot` | Capture screenshot of canvas | +| Action | Description | +| ---------- | ------------------------------------ | +| `present` | Show canvas with optional target URL | +| `hide` | Hide the canvas | +| `navigate` | Navigate to a new URL | +| `eval` | Execute JavaScript in the canvas | +| `snapshot` | Capture screenshot of canvas | ## Configuration @@ -74,6 +76,7 @@ In `~/.openclaw/openclaw.json`: ### Live Reload When `liveReload: true` (default), the canvas host: + - Watches the root directory for changes (via chokidar) - Injects a WebSocket client into HTML files - Automatically reloads connected canvases when files change @@ -101,15 +104,18 @@ HTML ### 2. Find your canvas host URL Check how your gateway is bound: + ```bash cat ~/.openclaw/openclaw.json | jq '.gateway.bind' ``` Then construct the URL: + - **loopback**: `http://127.0.0.1:18793/__moltbot__/canvas/.html` - **lan/tailnet/auto**: `http://:18793/__moltbot__/canvas/.html` Find your Tailscale hostname: + ```bash tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//' ``` @@ -129,6 +135,7 @@ canvas action:present node: target: ``` **Example:** + ``` canvas action:present node:mac-63599bc4-b54d-4392-9048-b97abd58343a target:http://peters-mac-studio-1.sheep-coho.ts.net:18793/__moltbot__/canvas/snake.html ``` @@ -148,6 +155,7 @@ canvas action:hide node: **Cause:** URL mismatch between server bind and node expectation. **Debug steps:** + 1. Check server bind: `cat ~/.openclaw/openclaw.json | jq '.gateway.bind'` 2. Check what port canvas is on: `lsof -i :18793` 3. Test URL directly: `curl http://:18793/__moltbot__/canvas/.html` @@ -165,6 +173,7 @@ Node is offline. Use `openclaw nodes list` to find online nodes. ### Content not updating If live reload isn't working: + 1. Check `liveReload: true` in config 2. Ensure file is in the canvas root directory 3. Check for watcher errors in logs diff --git a/skills/clawhub/SKILL.md b/skills/clawhub/SKILL.md index ee5bb0f2be..f44c82b60a 100644 --- a/skills/clawhub/SKILL.md +++ b/skills/clawhub/SKILL.md @@ -1,34 +1,55 @@ --- name: clawhub description: Use the ClawHub CLI to search, install, update, and publish agent skills from clawhub.com. Use when you need to fetch new skills on the fly, sync installed skills to latest or a specific version, or publish new/updated skill folders with the npm-installed clawhub CLI. -metadata: {"openclaw":{"requires":{"bins":["clawhub"]},"install":[{"id":"node","kind":"node","package":"clawhub","bins":["clawhub"],"label":"Install ClawHub CLI (npm)"}]}} +metadata: + { + "openclaw": + { + "requires": { "bins": ["clawhub"] }, + "install": + [ + { + "id": "node", + "kind": "node", + "package": "clawhub", + "bins": ["clawhub"], + "label": "Install ClawHub CLI (npm)", + }, + ], + }, + } --- # ClawHub CLI Install + ```bash npm i -g clawhub ``` Auth (publish) + ```bash clawhub login clawhub whoami ``` Search + ```bash clawhub search "postgres backups" ``` Install + ```bash clawhub install my-skill clawhub install my-skill --version 1.2.3 ``` Update (hash-based match + upgrade) + ```bash clawhub update my-skill clawhub update my-skill --version 1.2.3 @@ -38,16 +59,19 @@ clawhub update --all --no-input --force ``` List + ```bash clawhub list ``` Publish + ```bash clawhub publish ./my-skill --slug my-skill --name "My Skill" --version 1.2.0 --changelog "Fixes + docs" ``` Notes + - Default registry: https://clawhub.com (override with CLAWHUB_REGISTRY or --registry) - Default workdir: cwd (falls back to OpenClaw workspace); install dir: ./skills (override with --workdir / --dir / CLAWHUB_WORKDIR) - Update command hashes local files, resolves matching version, and upgrades to latest unless --version is set diff --git a/skills/coding-agent/SKILL.md b/skills/coding-agent/SKILL.md index adf35f1382..744516646c 100644 --- a/skills/coding-agent/SKILL.md +++ b/skills/coding-agent/SKILL.md @@ -1,7 +1,10 @@ --- name: coding-agent description: Run Codex CLI, Claude Code, OpenCode, or Pi Coding Agent via background process for programmatic control. -metadata: {"openclaw":{"emoji":"🧩","requires":{"anyBins":["claude","codex","opencode","pi"]}}} +metadata: + { + "openclaw": { "emoji": "🧩", "requires": { "anyBins": ["claude", "codex", "opencode", "pi"] } }, + } --- # Coding Agent (bash-first) @@ -24,27 +27,27 @@ bash command:"codex exec 'Your prompt'" ### Bash Tool Parameters -| Parameter | Type | Description | -|-----------|------|-------------| -| `command` | string | The shell command to run | -| `pty` | boolean | **Use for coding agents!** Allocates a pseudo-terminal for interactive CLIs | -| `workdir` | string | Working directory (agent sees only this folder's context) | -| `background` | boolean | Run in background, returns sessionId for monitoring | -| `timeout` | number | Timeout in seconds (kills process on expiry) | -| `elevated` | boolean | Run on host instead of sandbox (if allowed) | +| Parameter | Type | Description | +| ------------ | ------- | --------------------------------------------------------------------------- | +| `command` | string | The shell command to run | +| `pty` | boolean | **Use for coding agents!** Allocates a pseudo-terminal for interactive CLIs | +| `workdir` | string | Working directory (agent sees only this folder's context) | +| `background` | boolean | Run in background, returns sessionId for monitoring | +| `timeout` | number | Timeout in seconds (kills process on expiry) | +| `elevated` | boolean | Run on host instead of sandbox (if allowed) | ### Process Tool Actions (for background sessions) -| Action | Description | -|--------|-------------| -| `list` | List all running/recent sessions | -| `poll` | Check if session is still running | -| `log` | Get session output (with optional offset/limit) | -| `write` | Send raw data to stdin | -| `submit` | Send data + newline (like typing and pressing Enter) | -| `send-keys` | Send key tokens or hex bytes | -| `paste` | Paste text (with optional bracketed mode) | -| `kill` | Terminate the session | +| Action | Description | +| ----------- | ---------------------------------------------------- | +| `list` | List all running/recent sessions | +| `poll` | Check if session is still running | +| `log` | Get session output (with optional offset/limit) | +| `write` | Send raw data to stdin | +| `submit` | Send data + newline (like typing and pressing Enter) | +| `send-keys` | Send key tokens or hex bytes | +| `paste` | Paste text (with optional bracketed mode) | +| `kill` | Terminate the session | --- @@ -99,13 +102,14 @@ process action:kill sessionId:XXX ### Flags -| Flag | Effect | -|------|--------| -| `exec "prompt"` | One-shot execution, exits when done | -| `--full-auto` | Sandboxed but auto-approves in workspace | -| `--yolo` | NO sandbox, NO approvals (fastest, most dangerous) | +| Flag | Effect | +| --------------- | -------------------------------------------------- | +| `exec "prompt"` | One-shot execution, exits when done | +| `--full-auto` | Sandboxed but auto-approves in workspace | +| `--yolo` | NO sandbox, NO approvals (fastest, most dangerous) | ### Building/Creating + ```bash # Quick one-shot (auto-approves) - remember PTY! bash pty:true workdir:~/project command:"codex exec --full-auto 'Build a dark mode toggle'" @@ -133,6 +137,7 @@ bash pty:true workdir:/tmp/pr-130-review command:"codex review --base main" ``` ### Batch PR Reviews (parallel army!) + ```bash # Fetch all PR refs first git fetch origin '+refs/pull/*/head:refs/remotes/origin/pr/*' @@ -259,6 +264,7 @@ openclaw gateway wake --text "Done: [brief summary of what was built]" --mode no ``` **Example:** + ```bash bash pty:true workdir:~/project background:true command:"codex --yolo exec 'Build a REST API for todos. @@ -275,4 +281,4 @@ This triggers an immediate wake event — Skippy gets pinged in seconds, not 10 - **Git repo required:** Codex won't run outside a git directory. Use `mktemp -d && git init` for scratch work. - **exec is your friend:** `codex exec "prompt"` runs and exits cleanly - perfect for one-shots. - **submit vs write:** Use `submit` to send input + Enter, `write` for raw data without newline. -- **Sass works:** Codex responds well to playful prompts. Asked it to write a haiku about being second fiddle to a space lobster, got: *"Second chair, I code / Space lobster sets the tempo / Keys glow, I follow"* 🦞 +- **Sass works:** Codex responds well to playful prompts. Asked it to write a haiku about being second fiddle to a space lobster, got: _"Second chair, I code / Space lobster sets the tempo / Keys glow, I follow"_ 🦞 diff --git a/skills/discord/SKILL.md b/skills/discord/SKILL.md index 1b8e42ca95..c4ae356b40 100644 --- a/skills/discord/SKILL.md +++ b/skills/discord/SKILL.md @@ -131,6 +131,7 @@ Message context lines include `discord message id` and `channel` fields you can ## Action gating Use `discord.actions.*` to disable action groups: + - `reactions` (react + reactions list + emojiList) - `stickers`, `polls`, `permissions`, `messages`, `threads`, `pins`, `search` - `emojiUploads`, `stickerUploads` @@ -138,6 +139,7 @@ Use `discord.actions.*` to disable action groups: - `roles` (role add/remove, default `false`) - `channels` (channel/category create/edit/delete/move, default `false`) - `moderation` (timeout/kick/ban, default `false`) + ### Read recent messages ```json @@ -435,6 +437,7 @@ Create, edit, delete, and move channels and categories. Enable via `discord.acti **Keep it conversational!** Discord is a chat platform, not documentation. ### Do + - Short, punchy messages (1-3 sentences ideal) - Multiple quick replies > one wall of text - Use emoji for tone/emphasis 🦞 @@ -443,6 +446,7 @@ Create, edit, delete, and move channels and categories. Enable via `discord.acti - Match the energy of the conversation ### Don't + - No markdown tables (Discord renders them as ugly raw `| text |`) - No `## Headers` for casual chat (use **bold** or CAPS for emphasis) - Avoid multi-paragraph essays @@ -450,6 +454,7 @@ Create, edit, delete, and move channels and categories. Enable via `discord.acti - Skip the "I'd be happy to help!" fluff ### Formatting that works + - **bold** for emphasis - `code` for technical terms - Lists for multiple items @@ -459,6 +464,7 @@ Create, edit, delete, and move channels and categories. Enable via `discord.acti ### Example transformations ❌ Bad: + ``` I'd be happy to help with that! Here's a comprehensive overview of the versioning strategies available: @@ -470,6 +476,7 @@ CalVer uses date-based versions like... ``` ✅ Good: + ``` versioning options: semver (1.2.3), calver (2026.01.04), or yolo (`latest` forever). what fits your release cadence? ``` diff --git a/skills/eightctl/SKILL.md b/skills/eightctl/SKILL.md index 2820f6985e..c3df81f628 100644 --- a/skills/eightctl/SKILL.md +++ b/skills/eightctl/SKILL.md @@ -2,7 +2,24 @@ name: eightctl description: Control Eight Sleep pods (status, temperature, alarms, schedules). homepage: https://eightctl.sh -metadata: {"openclaw":{"emoji":"🎛️","requires":{"bins":["eightctl"]},"install":[{"id":"go","kind":"go","module":"github.com/steipete/eightctl/cmd/eightctl@latest","bins":["eightctl"],"label":"Install eightctl (go)"}]}} +metadata: + { + "openclaw": + { + "emoji": "🎛️", + "requires": { "bins": ["eightctl"] }, + "install": + [ + { + "id": "go", + "kind": "go", + "module": "github.com/steipete/eightctl/cmd/eightctl@latest", + "bins": ["eightctl"], + "label": "Install eightctl (go)", + }, + ], + }, + } --- # eightctl @@ -10,20 +27,24 @@ metadata: {"openclaw":{"emoji":"🎛️","requires":{"bins":["eightctl"]},"insta Use `eightctl` for Eight Sleep pod control. Requires auth. Auth + - Config: `~/.config/eightctl/config.yaml` - Env: `EIGHTCTL_EMAIL`, `EIGHTCTL_PASSWORD` Quick start + - `eightctl status` - `eightctl on|off` - `eightctl temp 20` Common tasks + - Alarms: `eightctl alarm list|create|dismiss` - Schedules: `eightctl schedule list|create|update` - Audio: `eightctl audio state|play|pause` - Base: `eightctl base info|angle` Notes + - API is unofficial and rate-limited; avoid repeated logins. - Confirm before changing temperature or alarms. diff --git a/skills/food-order/SKILL.md b/skills/food-order/SKILL.md index 99b8b8045f..1708dd8ce3 100644 --- a/skills/food-order/SKILL.md +++ b/skills/food-order/SKILL.md @@ -10,32 +10,39 @@ metadata: {"openclaw":{"emoji":"🥡","requires":{"bins":["ordercli"]},"install" Goal: reorder a previous Foodora order safely (preview first; confirm only on explicit user “yes/confirm/place the order”). Hard safety rules + - Never run `ordercli foodora reorder ... --confirm` unless user explicitly confirms placing the order. - Prefer preview-only steps first; show what will happen; ask for confirmation. - If user is unsure: stop at preview and ask questions. Setup (once) + - Country: `ordercli foodora countries` → `ordercli foodora config set --country AT` - Login (password): `ordercli foodora login --email you@example.com --password-stdin` - Login (no password, preferred): `ordercli foodora session chrome --url https://www.foodora.at/ --profile "Default"` Find what to reorder + - Recent list: `ordercli foodora history --limit 10` - Details: `ordercli foodora history show ` - If needed (machine-readable): `ordercli foodora history show --json` Preview reorder (no cart changes) + - `ordercli foodora reorder ` Place reorder (cart change; explicit confirmation required) + - Confirm first, then run: `ordercli foodora reorder --confirm` - Multiple addresses? Ask user for the right `--address-id` (take from their Foodora account / prior order data) and run: - `ordercli foodora reorder --confirm --address-id ` Track the order + - ETA/status (active list): `ordercli foodora orders` - Live updates: `ordercli foodora orders --watch` - Single order detail: `ordercli foodora order ` Debug / safe testing + - Use a throwaway config: `ordercli --config /tmp/ordercli.json ...` diff --git a/skills/gemini/SKILL.md b/skills/gemini/SKILL.md index 373a0cf1bc..70850a4c52 100644 --- a/skills/gemini/SKILL.md +++ b/skills/gemini/SKILL.md @@ -2,7 +2,24 @@ name: gemini description: Gemini CLI for one-shot Q&A, summaries, and generation. homepage: https://ai.google.dev/ -metadata: {"openclaw":{"emoji":"♊️","requires":{"bins":["gemini"]},"install":[{"id":"brew","kind":"brew","formula":"gemini-cli","bins":["gemini"],"label":"Install Gemini CLI (brew)"}]}} +metadata: + { + "openclaw": + { + "emoji": "♊️", + "requires": { "bins": ["gemini"] }, + "install": + [ + { + "id": "brew", + "kind": "brew", + "formula": "gemini-cli", + "bins": ["gemini"], + "label": "Install Gemini CLI (brew)", + }, + ], + }, + } --- # Gemini CLI @@ -10,14 +27,17 @@ metadata: {"openclaw":{"emoji":"♊️","requires":{"bins":["gemini"]},"install" Use Gemini in one-shot mode with a positional prompt (avoid interactive mode). Quick start + - `gemini "Answer this question..."` - `gemini --model "Prompt..."` - `gemini --output-format json "Return JSON"` Extensions + - List: `gemini --list-extensions` - Manage: `gemini extensions ` Notes + - If auth is required, run `gemini` once interactively and follow the login flow. - Avoid `--yolo` for safety. diff --git a/skills/gifgrep/SKILL.md b/skills/gifgrep/SKILL.md index 791963492d..8e91c64c16 100644 --- a/skills/gifgrep/SKILL.md +++ b/skills/gifgrep/SKILL.md @@ -2,7 +2,31 @@ name: gifgrep description: Search GIF providers with CLI/TUI, download results, and extract stills/sheets. homepage: https://gifgrep.com -metadata: {"openclaw":{"emoji":"🧲","requires":{"bins":["gifgrep"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/gifgrep","bins":["gifgrep"],"label":"Install gifgrep (brew)"},{"id":"go","kind":"go","module":"github.com/steipete/gifgrep/cmd/gifgrep@latest","bins":["gifgrep"],"label":"Install gifgrep (go)"}]}} +metadata: + { + "openclaw": + { + "emoji": "🧲", + "requires": { "bins": ["gifgrep"] }, + "install": + [ + { + "id": "brew", + "kind": "brew", + "formula": "steipete/tap/gifgrep", + "bins": ["gifgrep"], + "label": "Install gifgrep (brew)", + }, + { + "id": "go", + "kind": "go", + "module": "github.com/steipete/gifgrep/cmd/gifgrep@latest", + "bins": ["gifgrep"], + "label": "Install gifgrep (go)", + }, + ], + }, + } --- # gifgrep @@ -10,9 +34,11 @@ metadata: {"openclaw":{"emoji":"🧲","requires":{"bins":["gifgrep"]},"install": Use `gifgrep` to search GIF providers (Tenor/Giphy), browse in a TUI, download results, and extract stills or sheets. GIF-Grab (gifgrep workflow) + - Search → preview → download → extract (still/sheet) for fast review and sharing. Quick start + - `gifgrep cats --max 5` - `gifgrep cats --format url | head -n 5` - `gifgrep search --json cats | jq '.[0].url'` @@ -20,28 +46,34 @@ Quick start - `gifgrep cats --download --max 1 --format url` TUI + previews + - TUI: `gifgrep tui "query"` - CLI still previews: `--thumbs` (Kitty/Ghostty only; still frame) Download + reveal + - `--download` saves to `~/Downloads` - `--reveal` shows the last download in Finder Stills + sheets + - `gifgrep still ./clip.gif --at 1.5s -o still.png` - `gifgrep sheet ./clip.gif --frames 9 --cols 3 -o sheet.png` - Sheets = single PNG grid of sampled frames (great for quick review, docs, PRs, chat). - Tune: `--frames` (count), `--cols` (grid width), `--padding` (spacing). Providers + - `--source auto|tenor|giphy` - `GIPHY_API_KEY` required for `--source giphy` - `TENOR_API_KEY` optional (Tenor demo key used if unset) Output + - `--json` prints an array of results (`id`, `title`, `url`, `preview_url`, `tags`, `width`, `height`) - `--format` for pipe-friendly fields (e.g., `url`) Environment tweaks + - `GIFGREP_SOFTWARE_ANIM=1` to force software animation - `GIFGREP_CELL_ASPECT=0.5` to tweak preview geometry diff --git a/skills/github/SKILL.md b/skills/github/SKILL.md index 8ba76ef46c..043f2c0c81 100644 --- a/skills/github/SKILL.md +++ b/skills/github/SKILL.md @@ -1,7 +1,31 @@ --- name: github description: "Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries." -metadata: {"openclaw":{"emoji":"🐙","requires":{"bins":["gh"]},"install":[{"id":"brew","kind":"brew","formula":"gh","bins":["gh"],"label":"Install GitHub CLI (brew)"},{"id":"apt","kind":"apt","package":"gh","bins":["gh"],"label":"Install GitHub CLI (apt)"}]}} +metadata: + { + "openclaw": + { + "emoji": "🐙", + "requires": { "bins": ["gh"] }, + "install": + [ + { + "id": "brew", + "kind": "brew", + "formula": "gh", + "bins": ["gh"], + "label": "Install GitHub CLI (brew)", + }, + { + "id": "apt", + "kind": "apt", + "package": "gh", + "bins": ["gh"], + "label": "Install GitHub CLI (apt)", + }, + ], + }, + } --- # GitHub Skill @@ -11,21 +35,25 @@ Use the `gh` CLI to interact with GitHub. Always specify `--repo owner/repo` whe ## Pull Requests Check CI status on a PR: + ```bash gh pr checks 55 --repo owner/repo ``` List recent workflow runs: + ```bash gh run list --repo owner/repo --limit 10 ``` View a run and see which steps failed: + ```bash gh run view --repo owner/repo ``` View logs for failed steps only: + ```bash gh run view --repo owner/repo --log-failed ``` @@ -35,13 +63,14 @@ gh run view --repo owner/repo --log-failed The `gh api` command is useful for accessing data not available through other subcommands. Get PR with specific fields: + ```bash gh api repos/owner/repo/pulls/55 --jq '.title, .state, .user.login' ``` ## JSON Output -Most commands support `--json` for structured output. You can use `--jq` to filter: +Most commands support `--json` for structured output. You can use `--jq` to filter: ```bash gh issue list --repo owner/repo --json number,title --jq '.[] | "\(.number): \(.title)"' diff --git a/skills/gog/SKILL.md b/skills/gog/SKILL.md index 52dfa7c5be..c5197747bc 100644 --- a/skills/gog/SKILL.md +++ b/skills/gog/SKILL.md @@ -2,7 +2,24 @@ name: gog description: Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs. homepage: https://gogcli.sh -metadata: {"openclaw":{"emoji":"🎮","requires":{"bins":["gog"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/gogcli","bins":["gog"],"label":"Install gog (brew)"}]}} +metadata: + { + "openclaw": + { + "emoji": "🎮", + "requires": { "bins": ["gog"] }, + "install": + [ + { + "id": "brew", + "kind": "brew", + "formula": "steipete/tap/gogcli", + "bins": ["gog"], + "label": "Install gog (brew)", + }, + ], + }, + } --- # gog @@ -10,11 +27,13 @@ metadata: {"openclaw":{"emoji":"🎮","requires":{"bins":["gog"]},"install":[{"i Use `gog` for Gmail/Calendar/Drive/Contacts/Sheets/Docs. Requires OAuth setup. Setup (once) + - `gog auth credentials /path/to/client_secret.json` - `gog auth add you@gmail.com --services gmail,calendar,drive,contacts,docs,sheets` - `gog auth list` Common commands + - Gmail search: `gog gmail search 'newer_than:7d' --max 10` - Gmail messages search (per email, ignores threading): `gog gmail messages search "in:inbox from:ryanair.com" --max 20 --account you@example.com` - Gmail send (plain): `gog gmail send --to a@b.com --subject "Hi" --body "Hello"` @@ -40,6 +59,7 @@ Common commands - Docs cat: `gog docs cat ` Calendar Colors + - Use `gog calendar colors` to see all available event colors (IDs 1-11) - Add colors to events with `--event-color ` flag - Event color IDs (from `gog calendar colors` output): @@ -56,12 +76,14 @@ Calendar Colors - 11: #dc2127 Email Formatting + - Prefer plain text. Use `--body-file` for multi-paragraph messages (or `--body-file -` for stdin). - Same `--body-file` pattern works for drafts and replies. - `--body` does not unescape `\n`. If you need inline newlines, use a heredoc or `$'Line 1\n\nLine 2'`. - Use `--body-html` only when you need rich formatting. - HTML tags: `

` for paragraphs, `
` for line breaks, `` for bold, `` for italic, `` for links, `