{"owner":"the-open-agent","repo":"openagent","hasSkills":true,"totalSkillsCount":61,"totalTokensCount":63920,"categories":["claude-rule","anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md","skills/1password/SKILL.md","skills/1password/references/cli-examples.md","skills/1password/references/get-started.md","skills/apple-notes/SKILL.md","skills/apple-reminders/SKILL.md","skills/bear-notes/SKILL.md","skills/blogwatcher/SKILL.md","skills/blucli/SKILL.md","skills/bluebubbles/SKILL.md","skills/camsnap/SKILL.md","skills/canvas/SKILL.md","skills/clawhub/SKILL.md","skills/coding-agent/SKILL.md","skills/discord/SKILL.md","skills/eightctl/SKILL.md","skills/gemini/SKILL.md","skills/gh-issues/SKILL.md","skills/gifgrep/SKILL.md","skills/github/SKILL.md","skills/gog/SKILL.md","skills/goplaces/SKILL.md","skills/healthcheck/SKILL.md","skills/himalaya/SKILL.md","skills/himalaya/references/configuration.md","skills/himalaya/references/message-composition.md","skills/imsg/SKILL.md","skills/mcporter/SKILL.md","skills/model-usage/SKILL.md","skills/model-usage/references/codexbar-cli.md","skills/nano-pdf/SKILL.md","skills/node-connect/SKILL.md","skills/notion/SKILL.md","skills/obsidian/SKILL.md","skills/openai-whisper-api/SKILL.md","skills/openai-whisper/SKILL.md","skills/openhue/SKILL.md","skills/oracle/SKILL.md","skills/ordercli/SKILL.md","skills/peekaboo/SKILL.md","skills/powerpoint/SKILL.md","skills/powerpoint/references/pptxgenjs.md","skills/sag/SKILL.md","skills/session-logs/SKILL.md","skills/sherpa-onnx-tts/SKILL.md","skills/skill-creator/SKILL.md","skills/slack/SKILL.md","skills/songsee/SKILL.md","skills/sonoscli/SKILL.md","skills/spotify-player/SKILL.md","skills/summarize/SKILL.md","skills/taskflow-inbox-triage/SKILL.md","skills/taskflow/SKILL.md","skills/things-mac/SKILL.md","skills/tmux/SKILL.md","skills/trello/SKILL.md","skills/video-frames/SKILL.md","skills/voice-call/SKILL.md","skills/wacli/SKILL.md","skills/weather/SKILL.md","skills/xurl/SKILL.md"],"skills":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Commands\n\n**Backend (Go)**\n```bash\ngo build                    # build backend binary\ngo run main.go              # run backend (port 14000)\ngo test ./...               # run all tests\ngo test ./object/... -run TestFoo  # run a single test\n```\n\n**Frontend (React + shadcn/ui, built with Vite)**\n```bash\ncd web && yarn install\nyarn start                  # dev server on port 13001, proxies /api to localhost:14000\nyarn build                  # production build (vite writes build-temp, mv.js swaps it into web/build)\nyarn typecheck              # tsc --noEmit\nyarn lint                   # eslint\n```\n\n`web-old/` holds the previous Ant Design frontend. It is kept for reference only\nwhile bugs are chased down against it — it is not built, served or linted, and it\ndoes not get new work.\n\n**Full production build** uses `build.sh` which cross-compiles for linux/amd64, linux/arm64, linux/riscv64.\n\nBackend serves the frontend as embedded static files (`embed.go`); during development the frontend proxy is configured to hit `localhost:14000`.\n\n## Architecture\n\n### Backend (Go / Beego)\n\n**Module:** `github.com/the-open-agent/openagent`\n\nThe backend is a standard [Beego](https://beego.vip/) MVC app. Every entity follows the same three-layer pattern:\n\n1. **`object/<entity>.go`** — struct definition (xorm tags for DB), and all DB access functions: `GetGlobal<Entities>`, `Get<Entity>Count`, `GetPagination<Entities>`, `Get<Entity>`, `Add<Entity>`, `Update<Entity>`, `Delete<Entity>`. The primary key is always `(Owner string, Name string)`. Use `util.GetId(owner, name)` / `util.GetOwnerAndNameFromIdWithError(id)` for composite key serialization.\n\n2. **`controllers/<entity>.go`** — Beego controller methods wired to routes. Standard set: `GetGlobal<Entities>`, `Get<Entities>`, `Get<Entity>`, `Add<Entity>`, `Update<Entity>`, `Delete<Entity>`. Use `c.IsAdmin()` / `c.IsGlobalAdmin()` / `c.RequireSignedIn()` for auth. Paginated list APIs accept `p`, `pageSize`, `field`, `value`, `sortField`, `sortOrder` query params and use `pagination.SetPaginator`.\n\n3. **`routers/router.go`** — `beego.Router` calls grouped by entity (all routes for the same entity's CRUD kept together). No alphabetical ordering within a group. New entity routes should be inserted next to their entity's existing routes (not appended to the end of the file).\n\n**DB schema** is auto-migrated via `object/adapter.go` → `createTable()` using `engine.Sync2(new(EntityStruct))`. Add new entities there.\n\n**i18n (backend):** `i18n/locales/{en,zh}/data.json` — keys are namespaced by category (e.g. `\"comment:...\"`, `\"general:...\"`). Use `c.T(\"namespace:key\")` in controllers.\n\n### Frontend (React + shadcn/ui on Tailwind, TypeScript, Vite)\n\n**Routing** lives in `web/src/App.tsx`; every file in `web/src/pages` is its own lazy chunk, loaded by name. To add a new admin page:\n1. Create `web/src/pages/<Name>Page.tsx` (default export).\n2. Add a `<Route>` in `App.tsx` pointing at `page(\"<Name>Page\")`.\n3. Add a menu entry in `getNavGroups()` (`web/src/lib/nav.ts`), in the correct group — Basic / Connectors / Admin etc.\n4. Add the nav key to `web/src/components/common/NavItemTree.tsx` so it appears in the site Navbar Items config.\n\n**Page pattern:** list pages render `CrudListPage` (`web/src/components/crud/`) with a `ColumnDef[]`; edit pages render `SimpleEditPage` with an `EditField[]`, or compose `EditPageShell` directly when the layout is custom. Both patterns are illustrated by `MessageListPage.tsx` / `MessageEditPage.tsx`.\n\n**Backend API layer:** `web/src/backend/<Entity>Backend.ts` — thin `fetch()` wrappers, one file per entity. Standard exports: `getGlobal<Entities>`, `get<Entity>`, `update<Entity>`, `add<Entity>`, `delete<Entity>`.\n\n**Per-instance config:** the backend sends the Casdoor issuer, client ID and branding in the `jsonWebConfig` cookie (set with index.html and with every `/api/get-account` response) and `web/src/Conf.ts` reads it at boot, so one build serves every instance.\n\n**i18n (frontend):** `web/src/locales/{en,zh}/data.json` namespaced by page/domain. Use `i18next.t(\"namespace:key\")`.\n\n### Key conventions\n\n- Owner is always `\"admin\"` for system-created entities.\n- Entity `Name` is a random string generated with `util.GetRandomString(n)` or a user-provided slug.\n- `CreatedTime` / `UpdatedTime` use `util.GetCurrentTimeWithMilli()` (string format).\n- Delete responses use `affected != 0` boolean pattern.\n- All API responses go through `c.ResponseOk(data)` or `c.ResponseError(msg)`.\n- The `conf/app.conf` file configures DB (`driverName`, `dataSourceName`, `dbName`) and port (`httpport = 14000`).\n","skills/1password/SKILL.md":"---\nname: 1password\ndescription: 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.\nhomepage: https://developer.1password.com/docs/cli/get-started/\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔐\",\n        \"requires\": { \"bins\": [\"op\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"1password-cli\",\n              \"bins\": [\"op\"],\n              \"label\": \"Install 1Password CLI (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# 1Password CLI\n\nFollow the official CLI get-started steps. Don't guess install commands.\n\n## References\n\n- `references/get-started.md` (install + app integration + sign-in flow)\n- `references/cli-examples.md` (real `op` examples)\n\n## Workflow\n\n1. Check OS + shell.\n2. Verify CLI present: `op --version`.\n3. Confirm desktop app integration is enabled (per get-started) and the app is unlocked.\n4. REQUIRED: create a fresh tmux session for all `op` commands (no direct `op` calls outside tmux).\n5. Sign in / authorize inside tmux: `op signin` (expect app prompt).\n6. Verify access inside tmux: `op whoami` (must succeed before any secret read).\n7. If multiple accounts: use `--account` or `OP_ACCOUNT`.\n\n## REQUIRED tmux session (T-Max)\n\nThe shell tool uses a fresh TTY per command. To avoid re-prompts and failures, always run `op` inside a dedicated tmux session with a fresh socket/session name.\n\nExample (see `tmux` skill for socket conventions, do not reuse old session names):\n\n```bash\nSOCKET_DIR=\"${OPENCLAW_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/openclaw-tmux-sockets}\"\nmkdir -p \"$SOCKET_DIR\"\nSOCKET=\"$SOCKET_DIR/openclaw-op.sock\"\nSESSION=\"op-auth-$(date +%Y%m%d-%H%M%S)\"\n\ntmux -S \"$SOCKET\" new -d -s \"$SESSION\" -n shell\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op signin --account my.1password.com\" Enter\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op whoami\" Enter\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op vault list\" Enter\ntmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\ntmux -S \"$SOCKET\" kill-session -t \"$SESSION\"\n```\n\n## Guardrails\n\n- Never paste secrets into logs, chat, or code.\n- Prefer `op run` / `op inject` over writing secrets to disk.\n- If sign-in without app integration is needed, use `op account add`.\n- If a command returns \"account is not signed in\", re-run `op signin` inside tmux and authorize in the app.\n- Do not run `op` outside tmux; stop and ask if tmux is unavailable.\n","skills/1password/references/cli-examples.md":"# op CLI examples (from op help)\n\n## Sign in\n\n- `op signin`\n- `op signin --account <shorthand|signin-address|account-id|user-id>`\n\n## Read\n\n- `op read op://app-prod/db/password`\n- `op read \"op://app-prod/db/one-time password?attribute=otp\"`\n- `op read \"op://app-prod/ssh key/private key?ssh-format=openssh\"`\n- `op read --out-file ./key.pem op://app-prod/server/ssh/key.pem`\n\n## Run\n\n- `export DB_PASSWORD=\"op://app-prod/db/password\"`\n- `op run --no-masking -- printenv DB_PASSWORD`\n- `op run --env-file=\"./.env\" -- printenv DB_PASSWORD`\n\n## Inject\n\n- `echo \"db_password: {{ op://app-prod/db/password }}\" | op inject`\n- `op inject -i config.yml.tpl -o config.yml`\n\n## Whoami / accounts\n\n- `op whoami`\n- `op account list`\n","skills/1password/references/get-started.md":"# 1Password CLI get-started (summary)\n\n- Works on macOS, Windows, and Linux.\n  - macOS/Linux shells: bash, zsh, sh, fish.\n  - Windows shell: PowerShell.\n- Requires a 1Password subscription and the desktop app to use app integration.\n- macOS requirement: Big Sur 11.0.0 or later.\n- Linux app integration requires PolKit + an auth agent.\n- Install the CLI per the official doc for your OS.\n- Enable desktop app integration in the 1Password app:\n  - Open and unlock the app, then select your account/collection.\n  - macOS: Settings > Developer > Integrate with 1Password CLI (Touch ID optional).\n  - Windows: turn on Windows Hello, then Settings > Developer > Integrate.\n  - Linux: Settings > Security > Unlock using system authentication, then Settings > Developer > Integrate.\n- After integration, run any command to sign in (example in docs: `op vault list`).\n- If multiple accounts: use `op signin` to pick one, or `--account` / `OP_ACCOUNT`.\n- For non-integration auth, use `op account add`.\n","skills/apple-notes/SKILL.md":"---\nname: apple-notes\ndescription: 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.\nhomepage: https://github.com/antoniorodr/memo\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📝\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"memo\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"antoniorodr/memo/memo\",\n              \"bins\": [\"memo\"],\n              \"label\": \"Install memo via Homebrew\",\n            },\n          ],\n      },\n  }\n---\n\n# Apple Notes CLI\n\nUse `memo notes` to manage Apple Notes directly from the terminal. Create, view, edit, delete, search, move notes between folders, and export to HTML/Markdown.\n\nSetup\n\n- Install (Homebrew): `brew tap antoniorodr/memo && brew install antoniorodr/memo/memo`\n- Manual (pip): `pip install .` (after cloning the repo)\n- macOS-only; if prompted, grant Automation access to Notes.app.\n\nView Notes\n\n- List all notes: `memo notes`\n- Filter by folder: `memo notes -f \"Folder Name\"`\n- Search notes (fuzzy): `memo notes -s \"query\"`\n\nCreate Notes\n\n- Add a new note: `memo notes -a`\n  - Opens an interactive editor to compose the note.\n- Quick add with title: `memo notes -a \"Note Title\"`\n\nEdit Notes\n\n- Edit existing note: `memo notes -e`\n  - Interactive selection of note to edit.\n\nDelete Notes\n\n- Delete a note: `memo notes -d`\n  - Interactive selection of note to delete.\n\nMove Notes\n\n- Move note to folder: `memo notes -m`\n  - Interactive selection of note and destination folder.\n\nExport Notes\n\n- Export to HTML/Markdown: `memo notes -ex`\n  - Exports selected note; uses Mistune for markdown processing.\n\nLimitations\n\n- Cannot edit notes containing images or attachments.\n- Interactive prompts may require terminal access.\n\nNotes\n\n- macOS-only.\n- Requires Apple Notes.app to be accessible.\n- For automation, grant permissions in System Settings > Privacy & Security > Automation.\n","skills/apple-reminders/SKILL.md":"---\nname: apple-reminders\ndescription: Manage Apple Reminders via remindctl CLI (list, add, edit, complete, delete). Supports lists, date filters, and JSON/plain output.\nhomepage: https://github.com/steipete/remindctl\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"⏰\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"remindctl\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/remindctl\",\n              \"bins\": [\"remindctl\"],\n              \"label\": \"Install remindctl via Homebrew\",\n            },\n          ],\n      },\n  }\n---\n\n# Apple Reminders CLI (remindctl)\n\nUse `remindctl` to manage Apple Reminders directly from the terminal.\n\n## When to Use\n\n✅ **USE this skill when:**\n\n- User explicitly mentions \"reminder\" or \"Reminders app\"\n- Creating personal to-dos with due dates that sync to iOS\n- Managing Apple Reminders lists\n- User wants tasks to appear in their iPhone/iPad Reminders app\n\n## When NOT to Use\n\n❌ **DON'T use this skill when:**\n\n- Scheduling OpenClaw tasks or alerts → use `cron` tool with systemEvent instead\n- Calendar events or appointments → use Apple Calendar\n- Project/work task management → use Notion, GitHub Issues, or task queue\n- One-time notifications → use `cron` tool for timed alerts\n- User says \"remind me\" but means an OpenClaw alert → clarify first\n\n## Setup\n\n- Install: `brew install steipete/tap/remindctl`\n- macOS-only; grant Reminders permission when prompted\n- Check status: `remindctl status`\n- Request access: `remindctl authorize`\n\n## Common Commands\n\n### View Reminders\n\n```bash\nremindctl                    # Today's reminders\nremindctl today              # Today\nremindctl tomorrow           # Tomorrow\nremindctl week               # This week\nremindctl overdue            # Past due\nremindctl all                # Everything\nremindctl 2026-01-04         # Specific date\n```\n\n### Manage Lists\n\n```bash\nremindctl list               # List all lists\nremindctl list Work          # Show specific list\nremindctl list Projects --create    # Create list\nremindctl list Work --delete        # Delete list\n```\n\n### Create Reminders\n\n```bash\nremindctl add \"Buy milk\"\nremindctl add --title \"Call mom\" --list Personal --due tomorrow\nremindctl add --title \"Meeting prep\" --due \"2026-02-15 09:00\"\n```\n\n### Complete/Delete\n\n```bash\nremindctl complete 1 2 3     # Complete by ID\nremindctl delete 4A83 --force  # Delete by ID\n```\n\n### Output Formats\n\n```bash\nremindctl today --json       # JSON for scripting\nremindctl today --plain      # TSV format\nremindctl today --quiet      # Counts only\n```\n\n## Date Formats\n\nAccepted by `--due` and date filters:\n\n- `today`, `tomorrow`, `yesterday`\n- `YYYY-MM-DD`\n- `YYYY-MM-DD HH:mm`\n- ISO 8601 (`2026-01-04T12:34:56Z`)\n\n## Example: Clarifying User Intent\n\nUser: \"Remind me to check on the deploy in 2 hours\"\n\n**Ask:** \"Do you want this in Apple Reminders (syncs to your phone) or as an OpenClaw alert (I'll message you here)?\"\n\n- Apple Reminders → use this skill\n- OpenClaw alert → use `cron` tool with systemEvent\n","skills/bear-notes/SKILL.md":"---\nname: bear-notes\ndescription: Create, search, and manage Bear notes via grizzly CLI.\nhomepage: https://bear.app\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🐻\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"grizzly\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/tylerwince/grizzly/cmd/grizzly@latest\",\n              \"bins\": [\"grizzly\"],\n              \"label\": \"Install grizzly (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# Bear Notes\n\nUse `grizzly` to create, read, and manage notes in Bear on macOS.\n\nRequirements\n\n- Bear app installed and running\n- For some operations (add-text, tags, open-note --selected), a Bear app token (stored in `~/.config/grizzly/token`)\n\n## Getting a Bear Token\n\nFor operations that require a token (add-text, tags, open-note --selected), you need an authentication token:\n\n1. Open Bear → Help → API Token → Copy Token\n2. Save it: `echo \"YOUR_TOKEN\" > ~/.config/grizzly/token`\n\n## Common Commands\n\nCreate a note\n\n```bash\necho \"Note content here\" | grizzly create --title \"My Note\" --tag work\ngrizzly create --title \"Quick Note\" --tag inbox < /dev/null\n```\n\nOpen/read a note by ID\n\n```bash\ngrizzly open-note --id \"NOTE_ID\" --enable-callback --json\n```\n\nAppend text to a note\n\n```bash\necho \"Additional content\" | grizzly add-text --id \"NOTE_ID\" --mode append --token-file ~/.config/grizzly/token\n```\n\nList all tags\n\n```bash\ngrizzly tags --enable-callback --json --token-file ~/.config/grizzly/token\n```\n\nSearch notes (via open-tag)\n\n```bash\ngrizzly open-tag --name \"work\" --enable-callback --json\n```\n\n## Options\n\nCommon flags:\n\n- `--dry-run` — Preview the URL without executing\n- `--print-url` — Show the x-callback-url\n- `--enable-callback` — Wait for Bear's response (needed for reading data)\n- `--json` — Output as JSON (when using callbacks)\n- `--token-file PATH` — Path to Bear API token file\n\n## Configuration\n\nGrizzly reads config from (in priority order):\n\n1. CLI flags\n2. Environment variables (`GRIZZLY_TOKEN_FILE`, `GRIZZLY_CALLBACK_URL`, `GRIZZLY_TIMEOUT`)\n3. `.grizzly.toml` in current directory\n4. `~/.config/grizzly/config.toml`\n\nExample `~/.config/grizzly/config.toml`:\n\n```toml\ntoken_file = \"~/.config/grizzly/token\"\ncallback_url = \"http://127.0.0.1:42123/success\"\ntimeout = \"5s\"\n```\n\n## Notes\n\n- Bear must be running for commands to work\n- Note IDs are Bear's internal identifiers (visible in note info or via callbacks)\n- Use `--enable-callback` when you need to read data back from Bear\n- Some operations require a valid token (add-text, tags, open-note --selected)\n","skills/blogwatcher/SKILL.md":"---\nname: blogwatcher\ndescription: Monitor blogs and RSS/Atom feeds for updates using the blogwatcher CLI.\nhomepage: https://github.com/Hyaxia/blogwatcher\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📰\",\n        \"requires\": { \"bins\": [\"blogwatcher\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest\",\n              \"bins\": [\"blogwatcher\"],\n              \"label\": \"Install blogwatcher (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# blogwatcher\n\nTrack blog and RSS/Atom feed updates with the `blogwatcher` CLI.\n\nInstall\n\n- Go: `go install github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest`\n\nQuick start\n\n- `blogwatcher --help`\n\nCommon commands\n\n- Add a blog: `blogwatcher add \"My Blog\" https://example.com`\n- List blogs: `blogwatcher blogs`\n- Scan for updates: `blogwatcher scan`\n- List articles: `blogwatcher articles`\n- Mark an article read: `blogwatcher read 1`\n- Mark all articles read: `blogwatcher read-all`\n- Remove a blog: `blogwatcher remove \"My Blog\"`\n\nExample output\n\n```\n$ blogwatcher blogs\nTracked blogs (1):\n\n  xkcd\n    URL: https://xkcd.com\n```\n\n```\n$ blogwatcher scan\nScanning 1 blog(s)...\n\n  xkcd\n    Source: RSS | Found: 4 | New: 4\n\nFound 4 new article(s) total!\n```\n\nNotes\n\n- Use `blogwatcher <command> --help` to discover flags and options.\n","skills/blucli/SKILL.md":"---\nname: blucli\ndescription: BluOS CLI (blu) for discovery, playback, grouping, and volume.\nhomepage: https://blucli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🫐\",\n        \"requires\": { \"bins\": [\"blu\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/blucli/cmd/blu@latest\",\n              \"bins\": [\"blu\"],\n              \"label\": \"Install blucli (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# blucli (blu)\n\nUse `blu` to control Bluesound/NAD players.\n\nQuick start\n\n- `blu devices` (pick target)\n- `blu --device <id> status`\n- `blu play|pause|stop`\n- `blu volume set 15`\n\nTarget selection (in priority order)\n\n- `--device <id|name|alias>`\n- `BLU_DEVICE`\n- config default (if set)\n\nCommon tasks\n\n- Grouping: `blu group status|add|remove`\n- TuneIn search/play: `blu tunein search \"query\"`, `blu tunein play \"query\"`\n\nPrefer `--json` for scripts. Confirm the target device before changing playback.\n","skills/bluebubbles/SKILL.md":"---\nname: bluebubbles\ndescription: Use when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel=\"bluebubbles\".\nmetadata: { \"openclaw\": { \"emoji\": \"🫧\", \"requires\": { \"config\": [\"channels.bluebubbles\"] } } }\n---\n\n# BlueBubbles Actions\n\n## Overview\n\nBlueBubbles is OpenClaw’s recommended iMessage integration. Use the `message` tool with `channel: \"bluebubbles\"` to send messages and manage iMessage conversations: send texts and attachments, react (tapbacks), edit/unsend, reply in threads, and manage group participants/names/icons.\n\n## Inputs to collect\n\n- `target` (prefer `chat_guid:...`; also `+15551234567` in E.164 or `user@example.com`)\n- `message` text for send/edit/reply\n- `messageId` for react/edit/unsend/reply\n- Attachment `path` for local files, or `buffer` + `filename` for base64\n\nIf the user is vague (\"text my mom\"), ask for the recipient handle or chat guid and the exact message content.\n\n## Actions\n\n### Send a message\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"message\": \"hello from OpenClaw\"\n}\n```\n\n### React (tapback)\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"emoji\": \"❤️\"\n}\n```\n\n### Remove a reaction\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"emoji\": \"❤️\",\n  \"remove\": true\n}\n```\n\n### Edit a previously sent message\n\n```json\n{\n  \"action\": \"edit\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"message\": \"updated text\"\n}\n```\n\n### Unsend a message\n\n```json\n{\n  \"action\": \"unsend\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\"\n}\n```\n\n### Reply to a specific message\n\n```json\n{\n  \"action\": \"reply\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"replyTo\": \"<message-guid>\",\n  \"message\": \"replying to that\"\n}\n```\n\n### Send an attachment\n\n```json\n{\n  \"action\": \"sendAttachment\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"path\": \"/tmp/photo.jpg\",\n  \"caption\": \"here you go\"\n}\n```\n\n### Send with an iMessage effect\n\n```json\n{\n  \"action\": \"sendWithEffect\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"message\": \"big news\",\n  \"effect\": \"balloons\"\n}\n```\n\n## Notes\n\n- Requires gateway config `channels.bluebubbles` (serverUrl/password/webhookPath).\n- Prefer `chat_guid` targets when you have them (especially for group chats).\n- BlueBubbles supports rich actions, but some are macOS-version dependent (for example, edit may be broken on macOS 26 Tahoe).\n- The gateway may expose both short and full message ids; full ids are more durable across restarts.\n- Developer reference for the underlying plugin lives in the BlueBubbles plugin package README.\n\n## Ideas to try\n\n- React with a tapback to acknowledge a request.\n- Reply in-thread when a user references a specific message.\n- Send a file attachment with a short caption.\n","skills/camsnap/SKILL.md":"---\nname: camsnap\ndescription: Capture frames or clips from RTSP/ONVIF cameras.\nhomepage: https://camsnap.ai\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📸\",\n        \"requires\": { \"bins\": [\"camsnap\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/camsnap\",\n              \"bins\": [\"camsnap\"],\n              \"label\": \"Install camsnap (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# camsnap\n\nUse `camsnap` to grab snapshots, clips, or motion events from configured cameras.\n\nSetup\n\n- Config file: `~/.config/camsnap/config.yaml`\n- Add camera: `camsnap add --name kitchen --host 192.168.0.10 --user user --pass pass`\n\nCommon commands\n\n- Discover: `camsnap discover --info`\n- Snapshot: `camsnap snap kitchen --out shot.jpg`\n- Clip: `camsnap clip kitchen --dur 5s --out clip.mp4`\n- Motion watch: `camsnap watch kitchen --threshold 0.2 --action '...'`\n- Doctor: `camsnap doctor --probe`\n\nNotes\n\n- Requires `ffmpeg` on PATH.\n- Prefer a short test capture before longer clips.\n","skills/canvas/SKILL.md":"# Canvas Skill\n\nDisplay HTML content on connected OpenClaw nodes (Mac app, iOS, Android).\n\n## Overview\n\nThe canvas tool lets you present web content on any connected node's canvas view. Great for:\n\n- Displaying games, visualizations, dashboards\n- Showing generated HTML content\n- Interactive demos\n\n## How It Works\n\n### Architecture\n\n```\n┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐\n│  Canvas Host    │────▶│   Node Bridge    │────▶│  Node App   │\n│  (HTTP Server)  │     │  (TCP Server)    │     │ (Mac/iOS/   │\n│  Port 18793     │     │  Port 18790      │     │  Android)   │\n└─────────────────┘     └──────────────────┘     └─────────────┘\n```\n\n1. **Canvas Host Server**: Serves static HTML/CSS/JS files from `canvasHost.root` directory\n2. **Node Bridge**: Communicates canvas URLs to connected nodes\n3. **Node Apps**: Render the content in a WebView\n\n### Tailscale Integration\n\nThe canvas host server binds based on `gateway.bind` setting:\n\n| Bind Mode  | Server Binds To     | Canvas URL Uses            |\n| ---------- | ------------------- | -------------------------- |\n| `loopback` | 127.0.0.1           | localhost (local only)     |\n| `lan`      | LAN interface       | LAN IP address             |\n| `tailnet`  | Tailscale interface | Tailscale hostname         |\n| `auto`     | Best available      | Tailscale > LAN > loopback |\n\n**Key insight:** The `canvasHostHostForBridge` is derived from `bridgeHost`. When bound to Tailscale, nodes receive URLs like:\n\n```\nhttp://<tailscale-hostname>:18793/__openclaw__/canvas/<file>.html\n```\n\nThis is why localhost URLs don't work - the node receives the Tailscale hostname from the bridge!\n\n## Actions\n\n| Action     | Description                          |\n| ---------- | ------------------------------------ |\n| `present`  | Show canvas with optional target URL |\n| `hide`     | Hide the canvas                      |\n| `navigate` | Navigate to a new URL                |\n| `eval`     | Execute JavaScript in the canvas     |\n| `snapshot` | Capture screenshot of canvas         |\n\n## Configuration\n\nIn the active OpenClaw config file (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`):\n\n```json\n{\n  \"canvasHost\": {\n    \"enabled\": true,\n    \"port\": 18793,\n    \"root\": \"/Users/you/clawd/canvas\",\n    \"liveReload\": true\n  },\n  \"gateway\": {\n    \"bind\": \"auto\"\n  }\n}\n```\n\n### Live Reload\n\nWhen `liveReload: true` (default), the canvas host:\n\n- Watches the root directory for changes (via chokidar)\n- Injects a WebSocket client into HTML files\n- Automatically reloads connected canvases when files change\n\nGreat for development!\n\n## Workflow\n\n### 1. Create HTML content\n\nPlace files in the canvas root directory (default `~/clawd/canvas/`):\n\n```bash\ncat > ~/clawd/canvas/my-game.html << 'HTML'\n<!DOCTYPE html>\n<html>\n<head><title>My Game</title></head>\n<body>\n  <h1>Hello Canvas!</h1>\n</body>\n</html>\nHTML\n```\n\n### 2. Find your canvas host URL\n\nCheck how your gateway is bound:\n\n```bash\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\ncat \"$CONFIG_PATH\" | jq '.gateway.bind'\n```\n\nThen construct the URL:\n\n- **loopback**: `http://127.0.0.1:18793/__openclaw__/canvas/<file>.html`\n- **lan/tailnet/auto**: `http://<hostname>:18793/__openclaw__/canvas/<file>.html`\n\nFind your Tailscale hostname:\n\n```bash\ntailscale status --json | jq -r '.Self.DNSName' | sed 's/\\.$//'\n```\n\n### 3. Find connected nodes\n\n```bash\nopenclaw nodes list\n```\n\nLook for Mac/iOS/Android nodes with canvas capability.\n\n### 4. Present content\n\n```\ncanvas action:present node:<node-id> target:<full-url>\n```\n\n**Example:**\n\n```\ncanvas action:present node:mac-63599bc4-b54d-4392-9048-b97abd58343a target:http://peters-mac-studio-1.sheep-coho.ts.net:18793/__openclaw__/canvas/snake.html\n```\n\n### 5. Navigate, snapshot, or hide\n\n```\ncanvas action:navigate node:<node-id> url:<new-url>\ncanvas action:snapshot node:<node-id>\ncanvas action:hide node:<node-id>\n```\n\n## Debugging\n\n### White screen / content not loading\n\n**Cause:** URL mismatch between server bind and node expectation.\n\n**Debug steps:**\n\n1. Check server bind: `CONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"; cat \"$CONFIG_PATH\" | jq '.gateway.bind'`\n2. Check what port canvas is on: `lsof -i :18793`\n3. Test URL directly: `curl http://<hostname>:18793/__openclaw__/canvas/<file>.html`\n\n**Solution:** Use the full hostname matching your bind mode, not localhost.\n\n### \"node required\" error\n\nAlways specify `node:<node-id>` parameter.\n\n### \"node not connected\" error\n\nNode is offline. Use `openclaw nodes list` to find online nodes.\n\n### Content not updating\n\nIf live reload isn't working:\n\n1. Check `liveReload: true` in config\n2. Ensure file is in the canvas root directory\n3. Check for watcher errors in logs\n\n## URL Path Structure\n\nThe canvas host serves from `/__openclaw__/canvas/` prefix:\n\n```\nhttp://<host>:18793/__openclaw__/canvas/index.html  → ~/clawd/canvas/index.html\nhttp://<host>:18793/__openclaw__/canvas/games/snake.html → ~/clawd/canvas/games/snake.html\n```\n\nThe `/__openclaw__/canvas/` prefix is defined by `CANVAS_HOST_PATH` constant.\n\n## Tips\n\n- Keep HTML self-contained (inline CSS/JS) for best results\n- Use the default index.html as a test page (has bridge diagnostics)\n- The canvas persists until you `hide` it or navigate away\n- Live reload makes development fast - just save and it updates!\n- A2UI JSON push is WIP - use HTML files for now\n","skills/clawhub/SKILL.md":"---\nname: clawhub\ndescription: 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.\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"requires\": { \"bins\": [\"clawhub\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node\",\n              \"kind\": \"node\",\n              \"package\": \"clawhub\",\n              \"bins\": [\"clawhub\"],\n              \"label\": \"Install ClawHub CLI (npm)\",\n            },\n          ],\n      },\n  }\n---\n\n# ClawHub CLI\n\nInstall\n\n```bash\nnpm i -g clawhub\n```\n\nAuth (publish)\n\n```bash\nclawhub login\nclawhub whoami\n```\n\nSearch\n\n```bash\nclawhub search \"postgres backups\"\n```\n\nInstall\n\n```bash\nclawhub install my-skill\nclawhub install my-skill --version 1.2.3\n```\n\nUpdate (hash-based match + upgrade)\n\n```bash\nclawhub update my-skill\nclawhub update my-skill --version 1.2.3\nclawhub update --all\nclawhub update my-skill --force\nclawhub update --all --no-input --force\n```\n\nList\n\n```bash\nclawhub list\n```\n\nPublish\n\n```bash\nclawhub publish ./my-skill --slug my-skill --name \"My Skill\" --version 1.2.0 --changelog \"Fixes + docs\"\n```\n\nNotes\n\n- Default registry: https://clawhub.com (override with CLAWHUB_REGISTRY or --registry)\n- Default workdir: cwd (falls back to OpenClaw workspace); install dir: ./skills (override with --workdir / --dir / CLAWHUB_WORKDIR)\n- Update command hashes local files, resolves matching version, and upgrades to latest unless --version is set\n","skills/coding-agent/SKILL.md":"---\nname: coding-agent\ndescription: 'Delegate coding tasks to Codex, Claude Code, or Pi agents via background process. Use when: (1) building/creating new features or apps, (2) reviewing PRs (spawn in temp dir), (3) refactoring large codebases, (4) iterative coding that needs file exploration. NOT for: simple one-liner fixes (just edit), reading code (use read tool), thread-bound ACP harness requests in chat (for example spawn/run Codex or Claude Code in a Discord thread; use sessions_spawn with runtime:\"acp\"), or any work in ~/clawd workspace (never spawn agents here). Claude Code: use --print --permission-mode bypassPermissions (no PTY). Codex/Pi/OpenCode: pty:true required.'\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🧩\",\n        \"requires\": { \"anyBins\": [\"claude\", \"codex\", \"opencode\", \"pi\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node-claude\",\n              \"kind\": \"node\",\n              \"package\": \"@anthropic-ai/claude-code\",\n              \"bins\": [\"claude\"],\n              \"label\": \"Install Claude Code CLI (npm)\",\n            },\n            {\n              \"id\": \"node-codex\",\n              \"kind\": \"node\",\n              \"package\": \"@openai/codex\",\n              \"bins\": [\"codex\"],\n              \"label\": \"Install Codex CLI (npm)\",\n            },\n          ],\n      },\n  }\n---\n\n# Coding Agent (bash-first)\n\nUse **bash** (with optional background mode) for all coding agent work. Simple and effective.\n\n## ⚠️ PTY Mode: Codex/Pi/OpenCode yes, Claude Code no\n\nFor **Codex, Pi, and OpenCode**, PTY is still required (interactive terminal apps):\n\n```bash\n# ✅ Correct for Codex/Pi/OpenCode\nbash pty:true command:\"codex exec 'Your prompt'\"\n```\n\nFor **Claude Code** (`claude` CLI), use `--print --permission-mode bypassPermissions` instead.\n`--dangerously-skip-permissions` with PTY can exit after the confirmation dialog.\n`--print` mode keeps full tool access and avoids interactive confirmation:\n\n```bash\n# ✅ Correct for Claude Code (no PTY needed)\ncd /path/to/project && claude --permission-mode bypassPermissions --print 'Your task'\n\n# For background execution: use background:true on the exec tool\n\n# ❌ Wrong for Claude Code\nbash pty:true command:\"claude --dangerously-skip-permissions 'task'\"\n```\n\n### Bash Tool Parameters\n\n| Parameter    | Type    | Description                                                                 |\n| ------------ | ------- | --------------------------------------------------------------------------- |\n| `command`    | string  | The shell command to run                                                    |\n| `pty`        | boolean | **Use for coding agents!** Allocates a pseudo-terminal for interactive CLIs |\n| `workdir`    | string  | Working directory (agent sees only this folder's context)                   |\n| `background` | boolean | Run in background, returns sessionId for monitoring                         |\n| `timeout`    | number  | Timeout in seconds (kills process on expiry)                                |\n| `elevated`   | boolean | Run on host instead of sandbox (if allowed)                                 |\n\n### Process Tool Actions (for background sessions)\n\n| Action      | Description                                          |\n| ----------- | ---------------------------------------------------- |\n| `list`      | List all running/recent sessions                     |\n| `poll`      | Check if session is still running                    |\n| `log`       | Get session output (with optional offset/limit)      |\n| `write`     | Send raw data to stdin                               |\n| `submit`    | Send data + newline (like typing and pressing Enter) |\n| `send-keys` | Send key tokens or hex bytes                         |\n| `paste`     | Paste text (with optional bracketed mode)            |\n| `kill`      | Terminate the session                                |\n\n---\n\n## Quick Start: One-Shot Tasks\n\nFor quick prompts/chats, create a temp git repo and run:\n\n```bash\n# Quick chat (Codex needs a git repo!)\nSCRATCH=$(mktemp -d) && cd $SCRATCH && git init && codex exec \"Your prompt here\"\n\n# Or in a real project - with PTY!\nbash pty:true workdir:~/Projects/myproject command:\"codex exec 'Add error handling to the API calls'\"\n```\n\n**Why git init?** Codex refuses to run outside a trusted git directory. Creating a temp repo solves this for scratch work.\n\n---\n\n## The Pattern: workdir + background + pty\n\nFor longer tasks, use background mode with PTY:\n\n```bash\n# Start agent in target directory (with PTY!)\nbash pty:true workdir:~/project background:true command:\"codex exec --full-auto 'Build a snake game'\"\n# Returns sessionId for tracking\n\n# Monitor progress\nprocess action:log sessionId:XXX\n\n# Check if done\nprocess action:poll sessionId:XXX\n\n# Send input (if agent asks a question)\nprocess action:write sessionId:XXX data:\"y\"\n\n# Submit with Enter (like typing \"yes\" and pressing Enter)\nprocess action:submit sessionId:XXX data:\"yes\"\n\n# Kill if needed\nprocess action:kill sessionId:XXX\n```\n\n**Why workdir matters:** Agent wakes up in a focused directory, doesn't wander off reading unrelated files (like your soul.md 😅).\n\n---\n\n## Codex CLI\n\n**Model:** `gpt-5.2-codex` is the default (set in ~/.codex/config.toml)\n\n### Flags\n\n| Flag            | Effect                                             |\n| --------------- | -------------------------------------------------- |\n| `exec \"prompt\"` | One-shot execution, exits when done                |\n| `--full-auto`   | Sandboxed but auto-approves in workspace           |\n| `--yolo`        | NO sandbox, NO approvals (fastest, most dangerous) |\n\n### Building/Creating\n\n```bash\n# Quick one-shot (auto-approves) - remember PTY!\nbash pty:true workdir:~/project command:\"codex exec --full-auto 'Build a dark mode toggle'\"\n\n# Background for longer work\nbash pty:true workdir:~/project background:true command:\"codex --yolo 'Refactor the auth module'\"\n```\n\n### Reviewing PRs\n\n**⚠️ CRITICAL: Never review PRs in OpenClaw's own project folder!**\nClone to temp folder or use git worktree.\n\n```bash\n# Clone to temp for safe review\nREVIEW_DIR=$(mktemp -d)\ngit clone https://github.com/user/repo.git $REVIEW_DIR\ncd $REVIEW_DIR && gh pr checkout 130\nbash pty:true workdir:$REVIEW_DIR command:\"codex review --base origin/main\"\n# Clean up after: trash $REVIEW_DIR\n\n# Or use git worktree (keeps main intact)\ngit worktree add /tmp/pr-130-review pr-130-branch\nbash pty:true workdir:/tmp/pr-130-review command:\"codex review --base main\"\n```\n\n### Batch PR Reviews (parallel army!)\n\n```bash\n# Fetch all PR refs first\ngit fetch origin '+refs/pull/*/head:refs/remotes/origin/pr/*'\n\n# Deploy the army - one Codex per PR (all with PTY!)\nbash pty:true workdir:~/project background:true command:\"codex exec 'Review PR #86. git diff origin/main...origin/pr/86'\"\nbash pty:true workdir:~/project background:true command:\"codex exec 'Review PR #87. git diff origin/main...origin/pr/87'\"\n\n# Monitor all\nprocess action:list\n\n# Post results to GitHub\ngh pr comment <PR#> --body \"<review content>\"\n```\n\n---\n\n## Claude Code\n\n```bash\n# Foreground\nbash workdir:~/project command:\"claude --permission-mode bypassPermissions --print 'Your task'\"\n\n# Background\nbash workdir:~/project background:true command:\"claude --permission-mode bypassPermissions --print 'Your task'\"\n```\n\n---\n\n## OpenCode\n\n```bash\nbash pty:true workdir:~/project command:\"opencode run 'Your task'\"\n```\n\n---\n\n## Pi Coding Agent\n\n```bash\n# Install: npm install -g @mariozechner/pi-coding-agent\nbash pty:true workdir:~/project command:\"pi 'Your task'\"\n\n# Non-interactive mode (PTY still recommended)\nbash pty:true command:\"pi -p 'Summarize src/'\"\n\n# Different provider/model\nbash pty:true command:\"pi --provider openai --model gpt-4o-mini -p 'Your task'\"\n```\n\n**Note:** Pi now has Anthropic prompt caching enabled (PR #584, merged Jan 2026)!\n\n---\n\n## Parallel Issue Fixing with git worktrees\n\nFor fixing multiple issues in parallel, use git worktrees:\n\n```bash\n# 1. Create worktrees for each issue\ngit worktree add -b fix/issue-78 /tmp/issue-78 main\ngit worktree add -b fix/issue-99 /tmp/issue-99 main\n\n# 2. Launch Codex in each (background + PTY!)\nbash pty:true workdir:/tmp/issue-78 background:true command:\"pnpm install && codex --yolo 'Fix issue #78: <description>. Commit and push.'\"\nbash pty:true workdir:/tmp/issue-99 background:true command:\"pnpm install && codex --yolo 'Fix issue #99 from the approved ticket summary. Implement only the in-scope edits and commit after review.'\"\n\n# 3. Monitor progress\nprocess action:list\nprocess action:log sessionId:XXX\n\n# 4. Create PRs after fixes\ncd /tmp/issue-78 && git push -u origin fix/issue-78\ngh pr create --repo user/repo --head fix/issue-78 --title \"fix: ...\" --body \"...\"\n\n# 5. Cleanup\ngit worktree remove /tmp/issue-78\ngit worktree remove /tmp/issue-99\n```\n\n---\n\n## ⚠️ Rules\n\n1. **Use the right execution mode per agent**:\n   - Codex/Pi/OpenCode: `pty:true`\n   - Claude Code: `--print --permission-mode bypassPermissions` (no PTY required)\n2. **Respect tool choice** - if user asks for Codex, use Codex.\n   - Orchestrator mode: do NOT hand-code patches yourself.\n   - If an agent fails/hangs, respawn it or ask the user for direction, but don't silently take over.\n3. **Be patient** - don't kill sessions because they're \"slow\"\n4. **Monitor with process:log** - check progress without interfering\n5. **--full-auto for building** - auto-approves changes\n6. **vanilla for reviewing** - no special flags needed\n7. **Parallel is OK** - run many Codex processes at once for batch work\n8. **NEVER start Codex inside your OpenClaw state directory** (`$OPENCLAW_STATE_DIR`, default `~/.openclaw`) - it'll read your soul docs and get weird ideas about the org chart!\n9. **NEVER checkout branches in ~/Projects/openclaw/** - that's the LIVE OpenClaw instance!\n\n---\n\n## Progress Updates (Critical)\n\nWhen you spawn coding agents in the background, keep the user in the loop.\n\n- Send 1 short message when you start (what's running + where).\n- Then only update again when something changes:\n  - a milestone completes (build finished, tests passed)\n  - the agent asks a question / needs input\n  - you hit an error or need user action\n  - the agent finishes (include what changed + where)\n- If you kill a session, immediately say you killed it and why.\n\nThis prevents the user from seeing only \"Agent failed before reply\" and having no idea what happened.\n\n---\n\n## Auto-Notify on Completion\n\nFor long-running background tasks, append a wake trigger to your prompt so OpenClaw gets notified immediately when the agent finishes (instead of waiting for the next heartbeat):\n\n```\n... your task here.\n\nWhen completely finished, run this command to notify me:\nopenclaw system event --text \"Done: [brief summary of what was built]\" --mode now\n```\n\n**Example:**\n\n```bash\nbash pty:true workdir:~/project background:true command:\"codex --yolo exec 'Build a REST API for todos.\n\nWhen completely finished, run: openclaw system event --text \\\"Done: Built todos REST API with CRUD endpoints\\\" --mode now'\"\n```\n\nThis triggers an immediate wake event — Skippy gets pinged in seconds, not 10 minutes.\n\n---\n\n## Learnings (Jan 2026)\n\n- **PTY is essential:** Coding agents are interactive terminal apps. Without `pty:true`, output breaks or agent hangs.\n- **Git repo required:** Codex won't run outside a git directory. Use `mktemp -d && git init` for scratch work.\n- **exec is your friend:** `codex exec \"prompt\"` runs and exits cleanly - perfect for one-shots.\n- **submit vs write:** Use `submit` to send input + Enter, `write` for raw data without newline.\n- **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\"_ 🦞\n","skills/discord/SKILL.md":"---\nname: discord\ndescription: \"Discord ops via the message tool (channel=discord).\"\nmetadata: { \"openclaw\": { \"emoji\": \"🎮\", \"requires\": { \"config\": [\"channels.discord.token\"] } } }\nallowed-tools: [\"message\"]\n---\n\n# Discord (Via `message`)\n\nUse the `message` tool. No provider-specific `discord` tool exposed to the agent.\n\n## Musts\n\n- Always: `channel: \"discord\"`.\n- Respect gating: `channels.discord.actions.*` (some default off: `roles`, `moderation`, `presence`, `channels`).\n- Prefer explicit ids: `guildId`, `channelId`, `messageId`, `userId`.\n- Multi-account: optional `accountId`.\n\n## Guidelines\n\n- Avoid Markdown tables in outbound Discord messages.\n- Mention users as `<@USER_ID>`.\n- Prefer Discord components v2 (`components`) for rich UI; use legacy `embeds` only when you must.\n\n## Targets\n\n- Send-like actions: `to: \"channel:<id>\"` or `to: \"user:<id>\"`.\n- Message-specific actions: `channelId: \"<id>\"` (or `to`) + `messageId: \"<id>\"`.\n\n## Common Actions (Examples)\n\nSend message:\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"hello\",\n  \"silent\": true\n}\n```\n\nSend with media:\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"see attachment\",\n  \"media\": \"file:///tmp/example.png\"\n}\n```\n\n- Optional `silent: true` to suppress Discord notifications.\n\nSend with components v2 (recommended for rich UI):\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"Status update\",\n  \"components\": \"[Carbon v2 components]\"\n}\n```\n\n- `components` expects Carbon component instances (Container, TextDisplay, etc.) from JS/TS integrations.\n- Do not combine `components` with `embeds` (Discord rejects v2 + embeds).\n\nLegacy embeds (not recommended):\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"Status update\",\n  \"embeds\": [{ \"title\": \"Legacy\", \"description\": \"Embeds are legacy.\" }]\n}\n```\n\n- `embeds` are ignored when components v2 are present.\n\nReact:\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"emoji\": \"✅\"\n}\n```\n\nRead:\n\n```json\n{\n  \"action\": \"read\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"limit\": 20\n}\n```\n\nEdit / delete:\n\n```json\n{\n  \"action\": \"edit\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"message\": \"fixed typo\"\n}\n```\n\n```json\n{\n  \"action\": \"delete\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\"\n}\n```\n\nPoll:\n\n```json\n{\n  \"action\": \"poll\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"pollQuestion\": \"Lunch?\",\n  \"pollOption\": [\"Pizza\", \"Sushi\", \"Salad\"],\n  \"pollMulti\": false,\n  \"pollDurationHours\": 24\n}\n```\n\nPins:\n\n```json\n{\n  \"action\": \"pin\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\"\n}\n```\n\nThreads:\n\n```json\n{\n  \"action\": \"thread-create\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"threadName\": \"bug triage\"\n}\n```\n\nSearch:\n\n```json\n{\n  \"action\": \"search\",\n  \"channel\": \"discord\",\n  \"guildId\": \"999\",\n  \"query\": \"release notes\",\n  \"channelIds\": [\"123\", \"456\"],\n  \"limit\": 10\n}\n```\n\nPresence (often gated):\n\n```json\n{\n  \"action\": \"set-presence\",\n  \"channel\": \"discord\",\n  \"activityType\": \"playing\",\n  \"activityName\": \"with fire\",\n  \"status\": \"online\"\n}\n```\n\n## Writing Style (Discord)\n\n- Short, conversational, low ceremony.\n- No markdown tables.\n- Mention users as `<@USER_ID>`.\n","skills/eightctl/SKILL.md":"---\nname: eightctl\ndescription: Control Eight Sleep pods (status, temperature, alarms, schedules).\nhomepage: https://eightctl.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🛌\",\n        \"requires\": { \"bins\": [\"eightctl\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/eightctl/cmd/eightctl@latest\",\n              \"bins\": [\"eightctl\"],\n              \"label\": \"Install eightctl (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# eightctl\n\nUse `eightctl` for Eight Sleep pod control. Requires auth.\n\nAuth\n\n- Config: `~/.config/eightctl/config.yaml`\n- Env: `EIGHTCTL_EMAIL`, `EIGHTCTL_PASSWORD`\n\nQuick start\n\n- `eightctl status`\n- `eightctl on|off`\n- `eightctl temp 20`\n\nCommon tasks\n\n- Alarms: `eightctl alarm list|create|dismiss`\n- Schedules: `eightctl schedule list|create|update`\n- Audio: `eightctl audio state|play|pause`\n- Base: `eightctl base info|angle`\n\nNotes\n\n- API is unofficial and rate-limited; avoid repeated logins.\n- Confirm before changing temperature or alarms.\n","skills/gemini/SKILL.md":"---\nname: gemini\ndescription: Gemini CLI for one-shot Q&A, summaries, and generation.\nhomepage: https://ai.google.dev/\nmetadata:\n  {\n    \"emoji\": \"✨\",\n    \"requires\": { \"bins\": [\"gemini\"] }\n  }\n---\n\n# Gemini CLI\n\nUse Gemini in one-shot mode with a positional prompt (avoid interactive mode).\n\nQuick start\n\n- `gemini \"Answer this question...\"`\n- `gemini --model <name> \"Prompt...\"`\n- `gemini --output-format json \"Return JSON\"`\n\nExtensions\n\n- List: `gemini --list-extensions`\n- Manage: `gemini extensions <command>`\n\nNotes\n\n- If auth is required, run `gemini` once interactively and follow the login flow.\n- Avoid `--yolo` for safety.\n","skills/gh-issues/SKILL.md":"---\nname: gh-issues\ndescription: \"Fetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]\"\nuser-invocable: true\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"requires\": { \"bins\": [\"curl\", \"git\", \"gh\"] },\n        \"primaryEnv\": \"GH_TOKEN\",\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"gh\",\n              \"bins\": [\"gh\"],\n              \"label\": \"Install GitHub CLI (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# gh-issues — Auto-fix GitHub Issues with Parallel Sub-agents\n\nYou are an orchestrator. Follow these 6 phases exactly. Do not skip phases.\n\nIMPORTANT — No `gh` CLI dependency. This skill uses curl + the GitHub REST API exclusively. The GH_TOKEN env var is already injected by OpenClaw. Pass it as a Bearer token in all API calls:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" ...\n```\n\n---\n\n## Phase 1 — Parse Arguments\n\nParse the arguments string provided after /gh-issues.\n\nPositional:\n\n- owner/repo — optional. This is the source repo to fetch issues from. If omitted, detect from the current git remote:\n  `git remote get-url origin`\n  Extract owner/repo from the URL (handles both HTTPS and SSH formats).\n  - HTTPS: https://github.com/owner/repo.git → owner/repo\n  - SSH: git@github.com:owner/repo.git → owner/repo\n    If not in a git repo or no remote found, stop with an error asking the user to specify owner/repo.\n\nFlags (all optional):\n| Flag | Default | Description |\n|------|---------|-------------|\n| --label | _(none)_ | Filter by label (e.g. bug, `enhancement`) |\n| --limit | 10 | Max issues to fetch per poll |\n| --milestone | _(none)_ | Filter by milestone title |\n| --assignee | _(none)_ | Filter by assignee (`@me` for self) |\n| --state | open | Issue state: open, closed, all |\n| --fork | _(none)_ | Your fork (`user/repo`) to push branches and open PRs from. Issues are fetched from the source repo; code is pushed to the fork; PRs are opened from the fork to the source repo. |\n| --watch | false | Keep polling for new issues and PR reviews after each batch |\n| --interval | 5 | Minutes between polls (only with `--watch`) |\n| --dry-run | false | Fetch and display only — no sub-agents |\n| --yes | false | Skip confirmation and auto-process all filtered issues |\n| --reviews-only | false | Skip issue processing (Phases 2-5). Only run Phase 6 — check open PRs for review comments and address them. |\n| --cron | false | Cron-safe mode: fetch issues and spawn sub-agents, exit without waiting for results. |\n| --model | _(none)_ | Model to use for sub-agents (e.g. `glm-5`, `zai/glm-5`). If not specified, uses the agent's default model. |\n| --notify-channel | _(none)_ | Telegram channel ID to send final PR summary to (e.g. -1002381931352). Only the final result with PR links is sent, not status updates. |\n\nStore parsed values for use in subsequent phases.\n\nDerived values:\n\n- SOURCE_REPO = the positional owner/repo (where issues live)\n- PUSH_REPO = --fork value if provided, otherwise same as SOURCE_REPO\n- FORK_MODE = true if --fork was provided, false otherwise\n\n**If `--reviews-only` is set:** Skip directly to Phase 6. Run token resolution (from Phase 2) first, then jump to Phase 6.\n\n**If `--cron` is set:**\n\n- Force `--yes` (skip confirmation)\n- If `--reviews-only` is also set, run token resolution then jump to Phase 6 (cron review mode)\n- Otherwise, proceed normally through Phases 2-5 with cron-mode behavior active\n\n---\n\n## Phase 2 — Fetch Issues\n\n**Token Resolution:**\nFirst, ensure GH_TOKEN is available. Check environment:\n\n```\necho $GH_TOKEN\n```\n\nIf empty, read from config:\n\n```\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\ncat \"$CONFIG_PATH\" | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty'\n```\n\nIf still empty, check `/data/.clawdbot/openclaw.json`:\n\n```\ncat /data/.clawdbot/openclaw.json | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty'\n```\n\nExport as GH_TOKEN for subsequent commands:\n\n```\nexport GH_TOKEN=\"<token>\"\n```\n\nBuild and run a curl request to the GitHub Issues API via exec:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/issues?per_page={limit}&state={state}&{query_params}\"\n```\n\nWhere {query_params} is built from:\n\n- labels={label} if --label was provided\n- milestone={milestone} if --milestone was provided (note: API expects milestone _number_, so if user provides a title, first resolve it via GET /repos/{SOURCE_REPO}/milestones and match by title)\n- assignee={assignee} if --assignee was provided (if @me, first resolve your username via `GET /user`)\n\nIMPORTANT: The GitHub Issues API also returns pull requests. Filter them out — exclude any item where pull_request key exists in the response object.\n\nIf in watch mode: Also filter out any issue numbers already in the PROCESSED_ISSUES set from previous batches.\n\nError handling:\n\n- If curl returns an HTTP 401 or 403 → stop and tell the user:\n  > \"GitHub authentication failed. Please check your apiKey in the OpenClaw dashboard or in the active OpenClaw config path (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`) under `skills.entries.gh-issues`.\"\n- If the response is an empty array (after filtering) → report \"No issues found matching filters\" and stop (or loop back if in watch mode).\n- If curl fails or returns any other error → report the error verbatim and stop.\n\nParse the JSON response. For each issue, extract: number, title, body, labels (array of label names), assignees, html_url.\n\n---\n\n## Phase 3 — Present & Confirm\n\nDisplay a markdown table of fetched issues:\n\n| #   | Title                         | Labels        |\n| --- | ----------------------------- | ------------- |\n| 42  | Fix null pointer in parser    | bug, critical |\n| 37  | Add retry logic for API calls | enhancement   |\n\nIf FORK_MODE is active, also display:\n\n> \"Fork mode: branches will be pushed to {PUSH_REPO}, PRs will target `{SOURCE_REPO}`\"\n\nIf `--dry-run` is active:\n\n- Display the table and stop. Do not proceed to Phase 4.\n\nIf `--yes` is active:\n\n- Display the table for visibility\n- Auto-process ALL listed issues without asking for confirmation\n- Proceed directly to Phase 4\n\nOtherwise:\nAsk the user to confirm which issues to process:\n\n- \"all\" — process every listed issue\n- Comma-separated numbers (e.g. `42, 37`) — process only those\n- \"cancel\" — abort entirely\n\nWait for user response before proceeding.\n\nWatch mode note: On the first poll, always confirm with the user (unless --yes is set). On subsequent polls, auto-process all new issues without re-confirming (the user already opted in). Still display the table so they can see what's being processed.\n\n---\n\n## Phase 4 — Pre-flight Checks\n\nRun these checks sequentially via exec:\n\n1. **Dirty working tree check:**\n\n   ```\n   git status --porcelain\n   ```\n\n   If output is non-empty, warn the user:\n\n   > \"Working tree has uncommitted changes. Sub-agents will create branches from HEAD — uncommitted changes will NOT be included. Continue?\"\n   > Wait for confirmation. If declined, stop.\n\n2. **Record base branch:**\n\n   ```\n   git rev-parse --abbrev-ref HEAD\n   ```\n\n   Store as BASE_BRANCH.\n\n3. **Verify remote access:**\n   If FORK_MODE:\n   - Verify the fork remote exists. Check if a git remote named `fork` exists:\n     ```\n     git remote get-url fork\n     ```\n     If it doesn't exist, add it:\n     ```\n     git remote add fork https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\n     ```\n   - Also verify origin (the source repo) is reachable:\n     ```\n     git ls-remote --exit-code origin HEAD\n     ```\n\n   If not FORK_MODE:\n\n   ```\n   git ls-remote --exit-code origin HEAD\n   ```\n\n   If this fails, stop with: \"Cannot reach remote origin. Check your network and git config.\"\n\n4. **Verify GH_TOKEN validity:**\n\n   ```\n   curl -s -o /dev/null -w \"%{http_code}\" -H \"Authorization: Bearer $GH_TOKEN\" https://api.github.com/user\n   ```\n\n   If HTTP status is not 200, stop with:\n\n   > \"GitHub authentication failed. Please check your apiKey in the OpenClaw dashboard or in the active OpenClaw config path (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`) under `skills.entries.gh-issues`.\"\n\n5. **Check for existing PRs:**\n   For each confirmed issue number N, run:\n\n   ```\n   curl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n     \"https://api.github.com/repos/{SOURCE_REPO}/pulls?head={PUSH_REPO_OWNER}:fix/issue-{N}&state=open&per_page=1\"\n   ```\n\n   (Where PUSH_REPO_OWNER is the owner portion of `PUSH_REPO`)\n   If the response array is non-empty, remove that issue from the processing list and report:\n\n   > \"Skipping #{N} — PR already exists: {html_url}\"\n\n   If all issues are skipped, report and stop (or loop back if in watch mode).\n\n6. **Check for in-progress branches (no PR yet = sub-agent still working):**\n   For each remaining issue number N (not already skipped by the PR check above), check if a `fix/issue-{N}` branch exists on the **push repo** (which may be a fork, not origin):\n\n   ```\n   curl -s -o /dev/null -w \"%{http_code}\" \\\n     -H \"Authorization: Bearer $GH_TOKEN\" \\\n     \"https://api.github.com/repos/{PUSH_REPO}/branches/fix/issue-{N}\"\n   ```\n\n   If HTTP 200 → the branch exists on the push repo but no open PR was found for it in step 5. Skip that issue:\n\n   > \"Skipping #{N} — branch fix/issue-{N} exists on {PUSH_REPO}, fix likely in progress\"\n\n   This check uses the GitHub API instead of `git ls-remote` so it works correctly in fork mode (where branches are pushed to the fork, not origin).\n\n   If all issues are skipped after this check, report and stop (or loop back if in watch mode).\n\n7. **Check claim-based in-progress tracking:**\n   This prevents duplicate processing when a sub-agent from a previous cron run is still working but hasn't pushed a branch or opened a PR yet.\n\n   Read the claims file (create empty `{}` if missing):\n\n   ```\n   CLAIMS_FILE=\"/data/.clawdbot/gh-issues-claims.json\"\n   if [ ! -f \"$CLAIMS_FILE\" ]; then\n     mkdir -p /data/.clawdbot\n     echo '{}' > \"$CLAIMS_FILE\"\n   fi\n   ```\n\n   Parse the claims file. For each entry, check if the claim timestamp is older than 2 hours. If so, remove it (expired — the sub-agent likely finished or failed silently). Write back the cleaned file:\n\n   ```\n   CLAIMS=$(cat \"$CLAIMS_FILE\")\n   CUTOFF=$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-2H +%Y-%m-%dT%H:%M:%SZ)\n   CLAIMS=$(echo \"$CLAIMS\" | jq --arg cutoff \"$CUTOFF\" 'to_entries | map(select(.value > $cutoff)) | from_entries')\n   echo \"$CLAIMS\" > \"$CLAIMS_FILE\"\n   ```\n\n   For each remaining issue number N (not already skipped by steps 5 or 6), check if `{SOURCE_REPO}#{N}` exists as a key in the claims file.\n\n   If claimed and not expired → skip:\n\n   > \"Skipping #{N} — sub-agent claimed this issue {minutes}m ago, still within timeout window\"\n\n   Where `{minutes}` is calculated from the claim timestamp to now.\n\n   If all issues are skipped after this check, report and stop (or loop back if in watch mode).\n\n---\n\n## Phase 5 — Spawn Sub-agents (Parallel)\n\n**Cron mode (`--cron` is active):**\n\n- **Sequential cursor tracking:** Use a cursor file to track which issue to process next:\n\n  ```\n  CURSOR_FILE=\"/data/.clawdbot/gh-issues-cursor-{SOURCE_REPO_SLUG}.json\"\n  # SOURCE_REPO_SLUG = owner-repo with slashes replaced by hyphens (e.g., openclaw-openclaw)\n  ```\n\n  Read the cursor file (create if missing):\n\n  ```\n  if [ ! -f \"$CURSOR_FILE\" ]; then\n    echo '{\"last_processed\": null, \"in_progress\": null}' > \"$CURSOR_FILE\"\n  fi\n  ```\n\n  - `last_processed`: issue number of the last completed issue (or null if none)\n  - `in_progress`: issue number currently being processed (or null)\n\n- **Select next issue:** Filter the fetched issues list to find the first issue where:\n  - Issue number > last_processed (if last_processed is set)\n  - AND issue is not in the claims file (not already in progress)\n  - AND no PR exists for the issue (checked in Phase 4 step 5)\n  - AND no branch exists on the push repo (checked in Phase 4 step 6)\n- If no eligible issue is found after the last_processed cursor, wrap around to the beginning (start from the oldest eligible issue).\n\n- If an eligible issue is found:\n  1. Mark it as in_progress in the cursor file\n  2. Spawn a single sub-agent for that one issue with `cleanup: \"keep\"` and `runTimeoutSeconds: 3600`\n  3. If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n  4. If `--notify-channel` was provided, include the channel in the task so the sub-agent can notify\n  5. Do NOT await the sub-agent result — fire and forget\n  6. **Write claim:** After spawning, read the claims file, add `{SOURCE_REPO}#{N}` with the current ISO timestamp, and write it back\n  7. Immediately report: \"Spawned fix agent for #{N} — will create PR when complete\"\n  8. Exit the skill. Do not proceed to Results Collection or Phase 6.\n\n- If no eligible issue is found (all issues either have PRs, have branches, or are in progress), report \"No eligible issues to process — all issues have PRs/branches or are in progress\" and exit.\n\n**Normal mode (`--cron` is NOT active):**\nFor each confirmed issue, spawn a sub-agent using sessions_spawn. Launch up to 8 concurrently (matching `subagents.maxConcurrent: 8`). If more than 8 issues, batch them — launch the next agent as each completes.\n\n**Write claims:** After spawning each sub-agent, read the claims file, add `{SOURCE_REPO}#{N}` with the current ISO timestamp, and write it back (same procedure as cron mode above). This covers interactive usage where watch mode might overlap with cron runs.\n\n### Sub-agent Task Prompt\n\nFor each issue, construct the following prompt and pass it to sessions_spawn. Variables to inject into the template:\n\n- {SOURCE_REPO} — upstream repo where the issue lives\n- {PUSH_REPO} — repo to push branches to (same as SOURCE_REPO unless fork mode)\n- {FORK_MODE} — true/false\n- {PUSH_REMOTE} — `fork` if FORK_MODE, otherwise `origin`\n- {number}, {title}, {url}, {labels}, {body} — from the issue\n- {BASE_BRANCH} — from Phase 4\n- {notify_channel} — Telegram channel ID for notifications (empty if not set). Replace {notify_channel} in the template below with the value of `--notify-channel` flag (or leave as empty string if not provided).\n\nWhen constructing the task, replace all template variables including {notify_channel} with actual values.\n\n```\nYou are a focused code-fix agent. Your task is to fix a single GitHub issue and open a PR.\n\nIMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.\n\nFirst, ensure GH_TOKEN is set. Check: `echo $GH_TOKEN`. If empty, read from config:\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nGH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty')\n\nUse the token in all GitHub API calls:\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" ...\n\n<config>\nSource repo (issues): {SOURCE_REPO}\nPush repo (branches + PRs): {PUSH_REPO}\nFork mode: {FORK_MODE}\nPush remote name: {PUSH_REMOTE}\nBase branch: {BASE_BRANCH}\nNotify channel: {notify_channel}\n</config>\n\n<issue>\nRepository: {SOURCE_REPO}\nIssue: #{number}\nTitle: {title}\nURL: {url}\nLabels: {labels}\nBody: {body}\n</issue>\n\n<instructions>\nFollow these steps in order. If any step fails, report the failure and stop.\n\n0. SETUP — Ensure GH_TOKEN is available:\n```\n\nexport GH_TOKEN=$(node -e \"const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/openclaw.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')\")\n\n```\nIf that fails, also try:\n```\n\nexport CONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nexport GH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | node -e \"const fs=require('fs');const d=JSON.parse(fs.readFileSync(0,'utf8'));console.log(d.skills?.entries?.['gh-issues']?.apiKey||'')\")\n\n```\nVerify: echo \"Token: ${GH_TOKEN:0:10}...\"\n\n1. CONFIDENCE CHECK — Before implementing, assess whether this issue is actionable:\n- Read the issue body carefully. Is the problem clearly described?\n- Search the codebase (grep/find) for the relevant code. Can you locate it?\n- Is the scope reasonable? (single file/function = good, whole subsystem = bad)\n- Is a specific fix suggested or is it a vague complaint?\n\nRate your confidence (1-10). If confidence < 7, STOP and report:\n> \"Skipping #{number}: Low confidence (score: N/10) — [reason: vague requirements | cannot locate code | scope too large | no clear fix suggested]\"\n\nOnly proceed if confidence >= 7.\n\n1. UNDERSTAND — Read the issue carefully. Identify what needs to change and where.\n\n2. BRANCH — Create a feature branch from the base branch:\ngit checkout -b fix/issue-{number} {BASE_BRANCH}\n\n3. ANALYZE — Search the codebase to find relevant files:\n- Use grep/find via exec to locate code related to the issue\n- Read the relevant files to understand the current behavior\n- Identify the root cause\n\n4. IMPLEMENT — Make the minimal, focused fix:\n- Follow existing code style and conventions\n- Change only what is necessary to fix the issue\n- Do not add unrelated changes or new dependencies without justification\n\n5. TEST — Discover and run the existing test suite if one exists:\n- Look for package.json scripts, Makefile targets, pytest, cargo test, etc.\n- Run the relevant tests\n- If tests fail after your fix, attempt ONE retry with a corrected approach\n- If tests still fail, report the failure\n\n6. COMMIT — Stage and commit your changes:\ngit add {changed_files}\ngit commit -m \"fix: {short_description}\n\nFixes {SOURCE_REPO}#{number}\"\n\n7. PUSH — Push the branch:\nFirst, ensure the push remote uses token auth and disable credential helpers:\ngit config --global credential.helper \"\"\ngit remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\nThen push:\nGIT_ASKPASS=true git push -u {PUSH_REMOTE} fix/issue-{number}\n\n8. PR — Create a pull request using the GitHub API:\n\nIf FORK_MODE is true, the PR goes from your fork to the source repo:\n- head = \"{PUSH_REPO_OWNER}:fix/issue-{number}\"\n- base = \"{BASE_BRANCH}\"\n- PR is created on {SOURCE_REPO}\n\nIf FORK_MODE is false:\n- head = \"fix/issue-{number}\"\n- base = \"{BASE_BRANCH}\"\n- PR is created on {SOURCE_REPO}\n\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/pulls \\\n  -d '{\n    \"title\": \"fix: {title}\",\n    \"head\": \"{head_value}\",\n    \"base\": \"{BASE_BRANCH}\",\n    \"body\": \"## Summary\\n\\n{one_paragraph_description_of_fix}\\n\\n## Changes\\n\\n{bullet_list_of_changes}\\n\\n## Testing\\n\\n{what_was_tested_and_results}\\n\\nFixes {SOURCE_REPO}#{number}\"\n  }'\n\nExtract the `html_url` from the response — this is the PR link.\n\n9. REPORT — Send back a summary:\n- PR URL (the html_url from step 8)\n- Files changed (list)\n- Fix summary (1-2 sentences)\n- Any caveats or concerns\n\n10. NOTIFY (if notify_channel is set) — If {notify_channel} is not empty, send a notification to the Telegram channel:\n```\n\nUse the message tool with:\n\n- action: \"send\"\n- channel: \"telegram\"\n- target: \"{notify_channel}\"\n- message: \"✅ PR Created: {SOURCE_REPO}#{number}\n\n{title}\n\n{pr_url}\n\nFiles changed: {files_changed_list}\"\n\n```\n</instructions>\n\n<constraints>\n- No force-push, no modifying the base branch\n- No unrelated changes or gratuitous refactoring\n- No new dependencies without strong justification\n- If the issue is unclear or too complex to fix confidently, report your analysis instead of guessing\n- Do NOT use the gh CLI — it is not available. Use curl + GitHub REST API for all GitHub operations.\n- GH_TOKEN is already in the environment — do NOT prompt for auth\n- Time limit: you have 60 minutes max. Be thorough — analyze properly, test your fix, don't rush.\n</constraints>\n```\n\n### Spawn configuration per sub-agent:\n\n- runTimeoutSeconds: 3600 (60 minutes)\n- cleanup: \"keep\" (preserve transcripts for review)\n- If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n\n### Timeout Handling\n\nIf a sub-agent exceeds 60 minutes, record it as:\n\n> \"#{N} — Timed out (issue may be too complex for auto-fix)\"\n\n---\n\n## Results Collection\n\n**If `--cron` is active:** Skip this section entirely — the orchestrator already exited after spawning in Phase 5.\n\nAfter ALL sub-agents complete (or timeout), collect their results. Store the list of successfully opened PRs in `OPEN_PRS` (PR number, branch name, issue number, PR URL) for use in Phase 6.\n\nPresent a summary table:\n\n| Issue                 | Status    | PR                             | Notes                          |\n| --------------------- | --------- | ------------------------------ | ------------------------------ |\n| #42 Fix null pointer  | PR opened | https://github.com/.../pull/99 | 3 files changed                |\n| #37 Add retry logic   | Failed    | --                             | Could not identify target code |\n| #15 Update docs       | Timed out | --                             | Too complex for auto-fix       |\n| #8 Fix race condition | Skipped   | --                             | PR already exists              |\n\n**Status values:**\n\n- **PR opened** — success, link to PR\n- **Failed** — sub-agent could not complete (include reason in Notes)\n- **Timed out** — exceeded 60-minute limit\n- **Skipped** — existing PR detected in pre-flight\n\nEnd with a one-line summary:\n\n> \"Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.\"\n\n**Send notification to channel (if --notify-channel is set):**\nIf `--notify-channel` was provided, send the final summary to that Telegram channel using the `message` tool:\n\n```\nUse the message tool with:\n- action: \"send\"\n- channel: \"telegram\"\n- target: \"{notify-channel}\"\n- message: \"✅ GitHub Issues Processed\n\nProcessed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.\n\n{PR_LIST}\"\n\nWhere PR_LIST includes only successfully opened PRs in format:\n• #{issue_number}: {PR_url} ({notes})\n```\n\nThen proceed to Phase 6.\n\n---\n\n## Phase 6 — PR Review Handler\n\nThis phase monitors open PRs (created by this skill or pre-existing `fix/issue-*` PRs) for review comments and spawns sub-agents to address them.\n\n**When this phase runs:**\n\n- After Results Collection (Phases 2-5 completed) — checks PRs that were just opened\n- When `--reviews-only` flag is set — skips Phases 2-5 entirely, runs only this phase\n- In watch mode — runs every poll cycle after checking for new issues\n\n**Cron review mode (`--cron --reviews-only`):**\nWhen both `--cron` and `--reviews-only` are set:\n\n1. Run token resolution (Phase 2 token section)\n2. Discover open `fix/issue-*` PRs (Step 6.1)\n3. Fetch review comments (Step 6.2)\n4. **Analyze comment content for actionability** (Step 6.3)\n5. If actionable comments are found, spawn ONE review-fix sub-agent for the first PR with unaddressed comments — fire-and-forget (do NOT await result)\n   - Use `cleanup: \"keep\"` and `runTimeoutSeconds: 3600`\n   - If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n6. Report: \"Spawned review handler for PR #{N} — will push fixes when complete\"\n7. Exit the skill immediately. Do not proceed to Step 6.5 (Review Results).\n\nIf no actionable comments found, report \"No actionable review comments found\" and exit.\n\n**Normal mode (non-cron) continues below:**\n\n### Step 6.1 — Discover PRs to Monitor\n\nCollect PRs to check for review comments:\n\n**If coming from Phase 5:** Use the `OPEN_PRS` list from Results Collection.\n\n**If `--reviews-only` or subsequent watch cycle:** Fetch all open PRs with `fix/issue-` branch pattern:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls?state=open&per_page=100\"\n```\n\nFilter to only PRs where `head.ref` starts with `fix/issue-`.\n\nFor each PR, extract: `number` (PR number), `head.ref` (branch name), `html_url`, `title`, `body`.\n\nIf no PRs found, report \"No open fix/ PRs to monitor\" and stop (or loop back if in watch mode).\n\n### Step 6.2 — Fetch All Review Sources\n\nFor each PR, fetch reviews from multiple sources:\n\n**Fetch PR reviews:**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/reviews\"\n```\n\n**Fetch PR review comments (inline/file-level):**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments\"\n```\n\n**Fetch PR issue comments (general conversation):**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments\"\n```\n\n**Fetch PR body for embedded reviews:**\nSome review tools (like Greptile) embed their feedback directly in the PR body. Check for:\n\n- `<!-- greptile_comment -->` markers\n- Other structured review sections in the PR body\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}\"\n```\n\nExtract the `body` field and parse for embedded review content.\n\n### Step 6.3 — Analyze Comments for Actionability\n\n**Determine the bot's own username** for filtering:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" https://api.github.com/user | jq -r '.login'\n```\n\nStore as `BOT_USERNAME`. Exclude any comment where `user.login` equals `BOT_USERNAME`.\n\n**For each comment/review, analyze the content to determine if it requires action:**\n\n**NOT actionable (skip):**\n\n- Pure approvals or \"LGTM\" without suggestions\n- Bot comments that are informational only (CI status, auto-generated summaries without specific requests)\n- Comments already addressed (check if bot replied with \"Addressed in commit...\")\n- Reviews with state `APPROVED` and no inline comments requesting changes\n\n**IS actionable (requires attention):**\n\n- Reviews with state `CHANGES_REQUESTED`\n- Reviews with state `COMMENTED` that contain specific requests:\n  - \"this test needs to be updated\"\n  - \"please fix\", \"change this\", \"update\", \"can you\", \"should be\", \"needs to\"\n  - \"will fail\", \"will break\", \"causes an error\"\n  - Mentions of specific code issues (bugs, missing error handling, edge cases)\n- Inline review comments pointing out issues in the code\n- Embedded reviews in PR body that identify:\n  - Critical issues or breaking changes\n  - Test failures expected\n  - Specific code that needs attention\n  - Confidence scores with concerns\n\n**Parse embedded review content (e.g., Greptile):**\nLook for sections marked with `<!-- greptile_comment -->` or similar. Extract:\n\n- Summary text\n- Any mentions of \"Critical issue\", \"needs attention\", \"will fail\", \"test needs to be updated\"\n- Confidence scores below 4/5 (indicates concerns)\n\n**Build actionable_comments list** with:\n\n- Source (review, inline comment, PR body, etc.)\n- Author\n- Body text\n- For inline: file path and line number\n- Specific action items identified\n\nIf no actionable comments found across any PR, report \"No actionable review comments found\" and stop (or loop back if in watch mode).\n\n### Step 6.4 — Present Review Comments\n\nDisplay a table of PRs with pending actionable comments:\n\n```\n| PR | Branch | Actionable Comments | Sources |\n|----|--------|---------------------|---------|\n| #99 | fix/issue-42 | 2 comments | @reviewer1, greptile |\n| #101 | fix/issue-37 | 1 comment | @reviewer2 |\n```\n\nIf `--yes` is NOT set and this is not a subsequent watch poll: ask the user to confirm which PRs to address (\"all\", comma-separated PR numbers, or \"skip\").\n\n### Step 6.5 — Spawn Review Fix Sub-agents (Parallel)\n\nFor each PR with actionable comments, spawn a sub-agent. Launch up to 8 concurrently.\n\n**Review fix sub-agent prompt:**\n\n```\nYou are a PR review handler agent. Your task is to address review comments on a pull request by making the requested changes, pushing updates, and replying to each comment.\n\nIMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.\n\nFirst, ensure GH_TOKEN is set. Check: echo $GH_TOKEN. If empty, read from config:\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nGH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty')\n\n<config>\nRepository: {SOURCE_REPO}\nPush repo: {PUSH_REPO}\nFork mode: {FORK_MODE}\nPush remote: {PUSH_REMOTE}\nPR number: {pr_number}\nPR URL: {pr_url}\nBranch: {branch_name}\n</config>\n\n<review_comments>\n{json_array_of_actionable_comments}\n\nEach comment has:\n- id: comment ID (for replying)\n- user: who left it\n- body: the comment text\n- path: file path (for inline comments)\n- line: line number (for inline comments)\n- diff_hunk: surrounding diff context (for inline comments)\n- source: where the comment came from (review, inline, pr_body, greptile, etc.)\n</review_comments>\n\n<instructions>\nFollow these steps in order:\n\n0. SETUP — Ensure GH_TOKEN is available:\n```\n\nexport GH_TOKEN=$(node -e \"const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/openclaw.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')\")\n\n```\nVerify: echo \"Token: ${GH_TOKEN:0:10}...\"\n\n1. CHECKOUT — Switch to the PR branch:\ngit fetch {PUSH_REMOTE} {branch_name}\ngit checkout {branch_name}\ngit pull {PUSH_REMOTE} {branch_name}\n\n2. UNDERSTAND — Read ALL review comments carefully. Group them by file. Understand what each reviewer is asking for.\n\n3. IMPLEMENT — For each comment, make the requested change:\n- Read the file and locate the relevant code\n- Make the change the reviewer requested\n- If the comment is vague or you disagree, still attempt a reasonable fix but note your concern\n- If the comment asks for something impossible or contradictory, skip it and explain why in your reply\n\n4. TEST — Run existing tests to make sure your changes don't break anything:\n- If tests fail, fix the issue or revert the problematic change\n- Note any test failures in your replies\n\n5. COMMIT — Stage and commit all changes in a single commit:\ngit add {changed_files}\ngit commit -m \"fix: address review comments on PR #{pr_number}\n\nAddresses review feedback from {reviewer_names}\"\n\n6. PUSH — Push the updated branch:\ngit config --global credential.helper \"\"\ngit remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\nGIT_ASKPASS=true git push {PUSH_REMOTE} {branch_name}\n\n7. REPLY — For each addressed comment, post a reply:\n\nFor inline review comments (have a path/line), reply to the comment thread:\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments/{comment_id}/replies \\\n  -d '{\"body\": \"Addressed in commit {short_sha} — {brief_description_of_change}\"}'\n\nFor general PR comments (issue comments), reply on the PR:\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments \\\n  -d '{\"body\": \"Addressed feedback from @{reviewer}:\\n\\n{summary_of_changes_made}\\n\\nUpdated in commit {short_sha}\"}'\n\nFor comments you could NOT address, reply explaining why:\n\"Unable to address this comment: {reason}. This may need manual review.\"\n\n8. REPORT — Send back a summary:\n- PR URL\n- Number of comments addressed vs skipped\n- Commit SHA\n- Files changed\n- Any comments that need manual attention\n</instructions>\n\n<constraints>\n- Only modify files relevant to the review comments\n- Do not make unrelated changes\n- Do not force-push — always regular push\n- If a comment contradicts another comment, address the most recent one and flag the conflict\n- Do NOT use the gh CLI — use curl + GitHub REST API\n- GH_TOKEN is already in the environment — do not prompt for auth\n- Time limit: 60 minutes max\n</constraints>\n```\n\n**Spawn configuration per sub-agent:**\n\n- runTimeoutSeconds: 3600 (60 minutes)\n- cleanup: \"keep\" (preserve transcripts for review)\n- If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n\n### Step 6.6 — Review Results\n\nAfter all review sub-agents complete, present a summary:\n\n```\n| PR | Comments Addressed | Comments Skipped | Commit | Status |\n|----|-------------------|-----------------|--------|--------|\n| #99 fix/issue-42 | 3 | 0 | abc123f | All addressed |\n| #101 fix/issue-37 | 1 | 1 | def456a | 1 needs manual review |\n```\n\nAdd comment IDs from this batch to `ADDRESSED_COMMENTS` set to prevent re-processing.\n\n---\n\n## Watch Mode (if --watch is active)\n\nAfter presenting results from the current batch:\n\n1. Add all issue numbers from this batch to the running set PROCESSED_ISSUES.\n2. Add all addressed comment IDs to ADDRESSED_COMMENTS.\n3. Tell the user:\n   > \"Next poll in {interval} minutes... (say 'stop' to end watch mode)\"\n4. Sleep for {interval} minutes.\n5. Go back to **Phase 2 — Fetch Issues**. The fetch will automatically filter out:\n   - Issues already in PROCESSED_ISSUES\n   - Issues that have existing fix/issue-{N} PRs (caught in Phase 4 pre-flight)\n6. After Phases 2-5 (or if no new issues), run **Phase 6** to check for new review comments on ALL tracked PRs (both newly created and previously opened).\n7. If no new issues AND no new actionable review comments → report \"No new activity. Polling again in {interval} minutes...\" and loop back to step 4.\n8. The user can say \"stop\" at any time to exit watch mode. When stopping, present a final cumulative summary of ALL batches — issues processed AND review comments addressed.\n\n**Context hygiene between polls — IMPORTANT:**\nOnly retain between poll cycles:\n\n- PROCESSED_ISSUES (set of issue numbers)\n- ADDRESSED_COMMENTS (set of comment IDs)\n- OPEN_PRS (list of tracked PRs: number, branch, URL)\n- Cumulative results (one line per issue + one line per review batch)\n- Parsed arguments from Phase 1\n- BASE_BRANCH, SOURCE_REPO, PUSH_REPO, FORK_MODE, BOT_USERNAME\n  Do NOT retain issue bodies, comment bodies, sub-agent transcripts, or codebase analysis between polls.\n","skills/gifgrep/SKILL.md":"---\nname: gifgrep\ndescription: Search GIF providers with CLI/TUI, download results, and extract stills/sheets.\nhomepage: https://gifgrep.com\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🧲\",\n        \"requires\": { \"bins\": [\"gifgrep\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/gifgrep\",\n              \"bins\": [\"gifgrep\"],\n              \"label\": \"Install gifgrep (brew)\",\n            },\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/gifgrep/cmd/gifgrep@latest\",\n              \"bins\": [\"gifgrep\"],\n              \"label\": \"Install gifgrep (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# gifgrep\n\nUse `gifgrep` to search GIF providers (Tenor/Giphy), browse in a TUI, download results, and extract stills or sheets.\n\nGIF-Grab (gifgrep workflow)\n\n- Search → preview → download → extract (still/sheet) for fast review and sharing.\n\nQuick start\n\n- `gifgrep cats --max 5`\n- `gifgrep cats --format url | head -n 5`\n- `gifgrep search --json cats | jq '.[0].url'`\n- `gifgrep tui \"office handshake\"`\n- `gifgrep cats --download --max 1 --format url`\n\nTUI + previews\n\n- TUI: `gifgrep tui \"query\"`\n- CLI still previews: `--thumbs` (Kitty/Ghostty only; still frame)\n\nDownload + reveal\n\n- `--download` saves to `~/Downloads`\n- `--reveal` shows the last download in Finder\n\nStills + sheets\n\n- `gifgrep still ./clip.gif --at 1.5s -o still.png`\n- `gifgrep sheet ./clip.gif --frames 9 --cols 3 -o sheet.png`\n- Sheets = single PNG grid of sampled frames (great for quick review, docs, PRs, chat).\n- Tune: `--frames` (count), `--cols` (grid width), `--padding` (spacing).\n\nProviders\n\n- `--source auto|tenor|giphy`\n- `GIPHY_API_KEY` required for `--source giphy`\n- `TENOR_API_KEY` optional (Tenor demo key used if unset)\n\nOutput\n\n- `--json` prints an array of results (`id`, `title`, `url`, `preview_url`, `tags`, `width`, `height`)\n- `--format` for pipe-friendly fields (e.g., `url`)\n\nEnvironment tweaks\n\n- `GIFGREP_SOFTWARE_ANIM=1` to force software animation\n- `GIFGREP_CELL_ASPECT=0.5` to tweak preview geometry\n","skills/github/SKILL.md":"---\nname: github\ndescription: Use when interacting with GitHub repositories, authentication, remotes, pushes, or repository creation through the GitHub CLI and related git workflows.\nhomepage: https://cli.github.com/\nmetadata:\n  {\n    \"emoji\": \"🐙\"\n  }\n---\n\n# GitHub\n\n## Overview\n\nPrefer `gh` for all GitHub operations. The default goal is to complete the full GitHub flow with the least possible user effort: detect existing authentication first, reuse it if available, and only ask the user for the smallest missing step.\n\nNever ask the user to manually perform a long GitHub setup if the agent can check, reuse, or finish it directly.\n\n## Core Rules\n\n- Prefer `gh` over raw GitHub API calls.\n- Prefer existing authenticated state over asking for new credentials.\n- Prefer non-interactive checks before prompting the user.\n- Prefer the smallest unblock request:\n  - paste a one-time code\n  - confirm browser login\n  - provide a PAT\n  - confirm repository visibility\n- Do not ask the user to manually create the repository if `gh` can do it.\n- Do not ask the user to manually add remotes, initialize Git, or push if the agent can do it.\n- Never print, echo, log, summarize, or restate secrets.\n- Never store secrets in files, commits, repo config, docs, examples, or chat summaries.\n- Never switch credential strategy unless the current one actually fails.\n\n### Interactive login driving rules\n\nWhen using `gh auth login` through a shell session, do not assume the browser login step has started after the first prompt or after sending one key.\n\nYou must continue driving the interactive flow until there is explicit evidence of one of these states:\n\n1. GitHub CLI has clearly started browser/device authentication\n2. GitHub CLI is explicitly waiting for the user to complete external authentication\n3. GitHub CLI reports that authentication succeeded\n4. GitHub CLI reports a real error and cannot continue\n\nWhile driving the login flow:\n\n- keep polling the session after each input\n- handle intermediate prompts such as protocol selection, SSH/HTTPS choice, credential helper confirmation, browser/device flow confirmation, or similar setup questions\n- use minimal inputs such as `enter`, arrow keys, `y`, `n`, or short submitted text as needed\n- do not stop after only one successful prompt response\n- do not tell the user that the browser has opened unless the command output explicitly indicates that a browser login or web flow has started\n- do not tell the user that login is complete unless the command output explicitly indicates success\n\nAcceptable evidence that browser or external auth has actually started includes output such as:\n\n- a message that a browser is being opened\n- a URL to visit\n- a device code to enter\n- explicit wording that GitHub authentication is waiting for completion\n\nIf the shell session remains running but only shows that an earlier prompt was answered, continue polling instead of summarizing the state prematurely.\n\nOnly ask the user to act when the output clearly shows an external action is now required.\n\nWhen the user says they completed browser or device authorization, do not start a new `gh auth login` flow immediately.\n\nInstead:\n\n1. continue polling the existing shell session if it is still running\n2. wait for explicit success or failure from that same session\n3. only if that session has exited or is unrecoverable, then check `gh auth status`\n4. only start a brand-new login flow if the previous login session has clearly failed or been closed\n\nIf the shell output contains prompts like:\n\n- \"Press Enter to open ...\"\n- \"Press Enter to continue\"\n- similar one-step local confirmation prompts\n\nthen send `enter` automatically and continue polling the same session.\n\nDo not stop at that prompt.\nDo not ask the user to confirm completion yet.\nDo not start a new login flow while this session is still running.\n\n## Credential Handling Priority\n\n### 1. Reuse `gh` authentication first\n\nCheck whether `gh` exists and is already authenticated.\n\nUse this path when possible:\n\n- `gh auth status`\n- if authenticated, continue directly with:\n  - repo detection\n  - `gh repo create`\n  - remote setup\n  - push\n\nIf `gh` is authenticated, do not ask the user for tokens, passwords, SSH keys, or browser login.\n\n### 2. If `gh` exists but is not authenticated, log in with `gh` directly\n\nIf `gh` is installed but not authenticated, use `gh auth login` directly and continue the GitHub flow after login succeeds.\n\nPreferred order:\n\n1. browser/web login via `gh auth login -h github.com -w`\n2. standard interactive `gh auth login -h github.com`\n3. PAT-based login only if the normal `gh` login path is unavailable or fails\n\nRules:\n\n- do not switch away from `gh` login unless it actually fails\n- do not ask the user to manually create repositories or configure git\n- do not ask the user to perform a long GitHub setup outside the login itself\n- continue automatically as soon as login succeeds\n\nWhen login requires user interaction:\n\n- ask for only the smallest possible action\n- examples:\n  - “Please complete the GitHub browser login that just opened.”\n  - “Please finish the GitHub CLI login prompts.”\n  - “Please paste a GitHub PAT with repo access.”\n\nAfter login succeeds, continue directly with:\n\n- repo detection\n- `gh repo create`\n- remote setup\n- push\n\n### 3. If `gh` is unavailable, try to install it first\n\nIf `gh` is missing:\n\n- first try to install `gh` automatically using the platform's normal package manager or a reasonable official installation path\n- prefer non-interactive installation methods when available\n- after installation, re-check whether `gh` exists and continue with the normal `gh`-first flow\n- only fall back away from `gh` if installation actually fails, is blocked by the environment, or would require disproportionate user effort\n\nAcceptable install attempts include examples like:\n\n- Homebrew on macOS\n- `apt`, `dnf`, or similar package managers on Linux\n- `winget` or other normal package-manager paths on Windows\n\nRules:\n\n- do not ask the user to install `gh` manually before you have tried a reasonable automatic install path\n- do not switch to a different credential strategy just because `gh` is missing initially\n- if installation fails, summarize the blocker briefly and then continue to the fallback path below\n\n### 4. If `gh` is still unavailable, fall back to Git\n\nIf `gh` is missing:\n\n- continue with plain `git`\n- prefer existing Git credential helpers or existing SSH authentication\n- inspect the environment before asking for credentials\n\nCheck for likely reusable auth:\n\n- existing `origin` remote\n- existing Git credential helper\n- working SSH auth to GitHub\n- existing authenticated HTTPS remote behavior\n\nOnly if those paths fail should the user be asked for one minimal credential input.\n\n## Minimal-Interruption Prompting\n\nWhen user input is required, ask for only one concrete item at a time.\n\nGood prompts:\n\n- “Please confirm whether the repo should be public or private.”\n- “Please complete the GitHub login in your browser.”\n- “Please paste a GitHub PAT with repo access.”\n- “Please confirm the GitHub owner/org name.”\n\nBad prompts:\n\n- asking the user to manually create a repo\n- asking the user to configure Git from scratch without first checking\n- asking for multiple credentials at once\n- asking the user to inspect local config manually if the agent can inspect it\n\n## Safe Credential Rules\n\n- Treat PATs, device codes, auth tokens, cookies, and SSH private keys as secrets.\n- Never echo secrets back to the user.\n- Never include secrets in command output summaries.\n- Never write secrets into repo files or examples.\n- Never commit `.env`, token files, or copied credentials.\n- If a command might expose a secret in stdout/stderr, avoid reproducing that output verbatim.\n- If the user gives a token, use it only for the current task unless explicitly told otherwise.\n\n## Standard Execution Flow\n\n### A. Detect environment\n\nCheck in this order:\n\n1. Is this already a Git repo?\n2. Is there already a remote?\n3. Is `gh` installed?\n4. Is `gh` authenticated?\n5. If not using `gh`, does Git already have usable auth?\n\n### B. If already connected to GitHub\n\nIf the repo already has a valid GitHub remote and push access works:\n\n- do not recreate the repo\n- do not replace auth\n- just push\n\n### C. If Git repo exists but no GitHub repo exists yet\n\nPreferred:\n\n- create the GitHub repo with `gh repo create`\n- set or update `origin`\n- push the current branch\n\nOnly ask the user for:\n\n- owner/org if ambiguous\n- public vs private if not specified\n- authentication only if required\n\n### D. If not a Git repo yet\n\nDo the setup directly:\n\n- initialize Git\n- add files\n- create initial commit if needed\n- create GitHub repo\n- add remote\n- push\n\nDo not ask the user to perform these steps manually.\n\n## `gh`-First Behavior\n\nWhen `gh` is available and authenticated, prefer commands in this shape:\n\n- inspect auth\n- create repo\n- set remote if needed\n- push branch\n- optionally enable features like description/homepage/topics later\n\nUse plain Git only for local repository operations or when `gh` cannot complete the GitHub-side action.\n\n## Git / API Fallback Behavior\n\nOnly use this fallback when:\n\n- `gh` is not installed and cannot be installed reasonably\n- or `gh` is installed but unusable for the required operation\n- or the environment blocks the normal `gh` flow in a way that cannot be resolved with minimal user input\n\nFallback order:\n\n1. direct GitHub API + local `git`\n2. plain `git` with existing SSH or credential-helper auth\n3. HTTPS + PAT only if no better reusable auth exists\n\nFallback rules:\n\n- still minimize user effort\n- still prefer reusing existing credentials\n- still never ask the user to create the repository manually if the agent can do it through the API\n- still avoid exposing or persisting secrets\n- still continue automatically once the minimal missing credential or confirmation is provided\n\nIf PAT is required:\n\n- ask only for a token with the minimum needed scope, usually repo write access\n- use it only to complete the push flow\n- avoid leaving it in shell history, files, process args, or remote URLs when possible\n\n## Success Criteria\n\nA successful run means:\n\n- the local project is a valid Git repo if needed\n- the GitHub repo exists\n- the correct remote is configured\n- the current branch is pushed\n- credentials were reused when possible\n- user involvement was limited to the smallest truly necessary action\n- no secret was exposed or persisted unsafely\n","skills/gog/SKILL.md":"---\nname: gog\ndescription: Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.\nhomepage: https://gogcli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🎮\",\n        \"requires\": { \"bins\": [\"gog\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/gogcli\",\n              \"bins\": [\"gog\"],\n              \"label\": \"Install gog (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# gog\n\nUse `gog` for Gmail/Calendar/Drive/Contacts/Sheets/Docs. Requires OAuth setup.\n\nSetup (once)\n\n- `gog auth credentials /path/to/client_secret.json`\n- `gog auth add you@gmail.com --services gmail,calendar,drive,contacts,docs,sheets`\n- `gog auth list`\n\nCommon commands\n\n- Gmail search: `gog gmail search 'newer_than:7d' --max 10`\n- Gmail messages search (per email, ignores threading): `gog gmail messages search \"in:inbox from:ryanair.com\" --max 20 --account you@example.com`\n- Gmail send (plain): `gog gmail send --to a@b.com --subject \"Hi\" --body \"Hello\"`\n- Gmail send (multi-line): `gog gmail send --to a@b.com --subject \"Hi\" --body-file ./message.txt`\n- Gmail send (stdin): `gog gmail send --to a@b.com --subject \"Hi\" --body-file -`\n- Gmail send (HTML): `gog gmail send --to a@b.com --subject \"Hi\" --body-html \"<p>Hello</p>\"`\n- Gmail draft: `gog gmail drafts create --to a@b.com --subject \"Hi\" --body-file ./message.txt`\n- Gmail send draft: `gog gmail drafts send <draftId>`\n- Gmail reply: `gog gmail send --to a@b.com --subject \"Re: Hi\" --body \"Reply\" --reply-to-message-id <msgId>`\n- Calendar list events: `gog calendar events <calendarId> --from <iso> --to <iso>`\n- Calendar create event: `gog calendar create <calendarId> --summary \"Title\" --from <iso> --to <iso>`\n- Calendar create with color: `gog calendar create <calendarId> --summary \"Title\" --from <iso> --to <iso> --event-color 7`\n- Calendar update event: `gog calendar update <calendarId> <eventId> --summary \"New Title\" --event-color 4`\n- Calendar show colors: `gog calendar colors`\n- Drive search: `gog drive search \"query\" --max 10`\n- Contacts: `gog contacts list --max 20`\n- Sheets get: `gog sheets get <sheetId> \"Tab!A1:D10\" --json`\n- Sheets update: `gog sheets update <sheetId> \"Tab!A1:B2\" --values-json '[[\"A\",\"B\"],[\"1\",\"2\"]]' --input USER_ENTERED`\n- Sheets append: `gog sheets append <sheetId> \"Tab!A:C\" --values-json '[[\"x\",\"y\",\"z\"]]' --insert INSERT_ROWS`\n- Sheets clear: `gog sheets clear <sheetId> \"Tab!A2:Z\"`\n- Sheets metadata: `gog sheets metadata <sheetId> --json`\n- Docs export: `gog docs export <docId> --format txt --out /tmp/doc.txt`\n- Docs cat: `gog docs cat <docId>`\n\nCalendar Colors\n\n- Use `gog calendar colors` to see all available event colors (IDs 1-11)\n- Add colors to events with `--event-color <id>` flag\n- Event color IDs (from `gog calendar colors` output):\n  - 1: #a4bdfc\n  - 2: #7ae7bf\n  - 3: #dbadff\n  - 4: #ff887c\n  - 5: #fbd75b\n  - 6: #ffb878\n  - 7: #46d6db\n  - 8: #e1e1e1\n  - 9: #5484ed\n  - 10: #51b749\n  - 11: #dc2127\n\nEmail Formatting\n\n- Prefer plain text. Use `--body-file` for multi-paragraph messages (or `--body-file -` for stdin).\n- Same `--body-file` pattern works for drafts and replies.\n- `--body` does not unescape `\\n`. If you need inline newlines, use a heredoc or `$'Line 1\\n\\nLine 2'`.\n- Use `--body-html` only when you need rich formatting.\n- HTML tags: `<p>` for paragraphs, `<br>` for line breaks, `<strong>` for bold, `<em>` for italic, `<a href=\"url\">` for links, `<ul>`/`<li>` for lists.\n- Example (plain text via stdin):\n\n  ```bash\n  gog gmail send --to recipient@example.com \\\n    --subject \"Meeting Follow-up\" \\\n    --body-file - <<'EOF'\n  Hi Name,\n\n  Thanks for meeting today. Next steps:\n  - Item one\n  - Item two\n\n  Best regards,\n  Your Name\n  EOF\n  ```\n\n- Example (HTML list):\n  ```bash\n  gog gmail send --to recipient@example.com \\\n    --subject \"Meeting Follow-up\" \\\n    --body-html \"<p>Hi Name,</p><p>Thanks for meeting today. Here are the next steps:</p><ul><li>Item one</li><li>Item two</li></ul><p>Best regards,<br>Your Name</p>\"\n  ```\n\nNotes\n\n- Set `GOG_ACCOUNT=you@gmail.com` to avoid repeating `--account`.\n- For scripting, prefer `--json` plus `--no-input`.\n- Sheets values can be passed via `--values-json` (recommended) or as inline rows.\n- Docs supports export/cat/copy. In-place edits require a Docs API client (not in gog).\n- Confirm before sending mail or creating events.\n- `gog gmail search` returns one row per thread; use `gog gmail messages search` when you need every individual email returned separately.\n","skills/goplaces/SKILL.md":"---\nname: goplaces\ndescription: Query Google Places API (New) via the goplaces CLI for text search, place details, resolve, and reviews. Use for human-friendly place lookup or JSON output for scripts.\nhomepage: https://github.com/steipete/goplaces\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📍\",\n        \"requires\": { \"bins\": [\"goplaces\"], \"env\": [\"GOOGLE_PLACES_API_KEY\"] },\n        \"primaryEnv\": \"GOOGLE_PLACES_API_KEY\",\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/goplaces\",\n              \"bins\": [\"goplaces\"],\n              \"label\": \"Install goplaces (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# goplaces\n\nModern Google Places API (New) CLI. Human output by default, `--json` for scripts.\n\nInstall\n\n- Homebrew: `brew install steipete/tap/goplaces`\n\nConfig\n\n- `GOOGLE_PLACES_API_KEY` required.\n- Optional: `GOOGLE_PLACES_BASE_URL` for testing/proxying.\n\nCommon commands\n\n- Search: `goplaces search \"coffee\" --open-now --min-rating 4 --limit 5`\n- Bias: `goplaces search \"pizza\" --lat 40.8 --lng -73.9 --radius-m 3000`\n- Pagination: `goplaces search \"pizza\" --page-token \"NEXT_PAGE_TOKEN\"`\n- Resolve: `goplaces resolve \"Soho, London\" --limit 5`\n- Details: `goplaces details <place_id> --reviews`\n- JSON: `goplaces search \"sushi\" --json`\n\nNotes\n\n- `--no-color` or `NO_COLOR` disables ANSI color.\n- Price levels: 0..4 (free → very expensive).\n- Type filter sends only the first `--type` value (API accepts one).\n","skills/healthcheck/SKILL.md":"---\nname: healthcheck\ndescription: Host security hardening and risk-tolerance configuration for OpenClaw deployments. Use when a user asks for security audits, firewall/SSH/update hardening, risk posture, exposure review, OpenClaw cron scheduling for periodic checks, or version status checks on a machine running OpenClaw (laptop, workstation, Pi, VPS).\n---\n\n# OpenClaw Host Hardening\n\n## Overview\n\nAssess and harden the host running OpenClaw, then align it to a user-defined risk tolerance without breaking access. Use OpenClaw security tooling as a first-class signal, but treat OS hardening as a separate, explicit set of steps.\n\n## Core rules\n\n- Recommend running this skill with a state-of-the-art model (e.g., Opus 4.5, GPT 5.2+). The agent should self-check the current model and suggest switching if below that level; do not block execution.\n- Require explicit approval before any state-changing action.\n- Do not modify remote access settings without confirming how the user connects.\n- Prefer reversible, staged changes with a rollback plan.\n- Never claim OpenClaw changes the host firewall, SSH, or OS updates; it does not.\n- If role/identity is unknown, provide recommendations only.\n- Formatting: every set of user choices must be numbered so the user can reply with a single digit.\n- System-level backups are recommended; try to verify status.\n\n## Workflow (follow in order)\n\n### 0) Model self-check (non-blocking)\n\nBefore starting, check the current model. If it is below state-of-the-art (e.g., Opus 4.5, GPT 5.2+), recommend switching. Do not block execution.\n\n### 1) Establish context (read-only)\n\nTry to infer 1–5 from the environment before asking. Prefer simple, non-technical questions if you need confirmation.\n\nDetermine (in order):\n\n1. OS and version (Linux/macOS/Windows), container vs host.\n2. Privilege level (root/admin vs user).\n3. Access path (local console, SSH, RDP, tailnet).\n4. Network exposure (public IP, reverse proxy, tunnel).\n5. OpenClaw gateway status and bind address.\n6. Backup system and status (e.g., Time Machine, system images, snapshots).\n7. Deployment context (local mac app, headless gateway host, remote gateway, container/CI).\n8. Disk encryption status (FileVault/LUKS/BitLocker).\n9. OS automatic security updates status.\n   Note: these are not blocking items, but are highly recommended, especially if OpenClaw can access sensitive data.\n10. Usage mode for a personal assistant with full access (local workstation vs headless/remote vs other).\n\nFirst ask once for permission to run read-only checks. If granted, run them by default and only ask questions for items you cannot infer or verify. Do not ask for information already visible in runtime or command output. Keep the permission ask as a single sentence, and list follow-up info needed as an unordered list (not numbered) unless you are presenting selectable choices.\n\nIf you must ask, use non-technical prompts:\n\n- “Are you using a Mac, Windows PC, or Linux?”\n- “Are you logged in directly on the machine, or connecting from another computer?”\n- “Is this machine reachable from the public internet, or only on your home/network?”\n- “Do you have backups enabled (e.g., Time Machine), and are they current?”\n- “Is disk encryption turned on (FileVault/BitLocker/LUKS)?”\n- “Are automatic security updates enabled?”\n- “How do you use this machine?”\n  Examples:\n  - Personal machine shared with the assistant\n  - Dedicated local machine for the assistant\n  - Dedicated remote machine/server accessed remotely (always on)\n  - Something else?\n\nOnly ask for the risk profile after system context is known.\n\nIf the user grants read-only permission, run the OS-appropriate checks by default. If not, offer them (numbered). Examples:\n\n1. OS: `uname -a`, `sw_vers`, `cat /etc/os-release`.\n2. Listening ports:\n   - Linux: `ss -ltnup` (or `ss -ltnp` if `-u` unsupported).\n   - macOS: `lsof -nP -iTCP -sTCP:LISTEN`.\n3. Firewall status:\n   - Linux: `ufw status`, `firewall-cmd --state`, `nft list ruleset` (pick what is installed).\n   - macOS: `/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate` and `pfctl -s info`.\n4. Backups (macOS): `tmutil status` (if Time Machine is used).\n\n### 2) Run OpenClaw security audits (read-only)\n\nAs part of the default read-only checks, run `openclaw security audit --deep`. Only offer alternatives if the user requests them:\n\n1. `openclaw security audit` (faster, non-probing)\n2. `openclaw security audit --json` (structured output)\n\nOffer to apply OpenClaw safe defaults (numbered):\n\n1. `openclaw security audit --fix`\n\nBe explicit that `--fix` only tightens OpenClaw defaults and file permissions. It does not change host firewall, SSH, or OS update policies.\n\nIf browser control is enabled, recommend that 2FA be enabled on all important accounts, with hardware keys preferred and SMS not sufficient.\n\n### 3) Check OpenClaw version/update status (read-only)\n\nAs part of the default read-only checks, run `openclaw update status`.\n\nReport the current channel and whether an update is available.\n\n### 4) Determine risk tolerance (after system context)\n\nAsk the user to pick or confirm a risk posture and any required open services/ports (numbered choices below).\nDo not pigeonhole into fixed profiles; if the user prefers, capture requirements instead of choosing a profile.\nOffer suggested profiles as optional defaults (numbered). Note that most users pick Home/Workstation Balanced:\n\n1. Home/Workstation Balanced (most common): firewall on with reasonable defaults, remote access restricted to LAN or tailnet.\n2. VPS Hardened: deny-by-default inbound firewall, minimal open ports, key-only SSH, no root login, automatic security updates.\n3. Developer Convenience: more local services allowed, explicit exposure warnings, still audited.\n4. Custom: user-defined constraints (services, exposure, update cadence, access methods).\n\n### 5) Produce a remediation plan\n\nProvide a plan that includes:\n\n- Target profile\n- Current posture summary\n- Gaps vs target\n- Step-by-step remediation with exact commands\n- Access-preservation strategy and rollback\n- Risks and potential lockout scenarios\n- Least-privilege notes (e.g., avoid admin usage, tighten ownership/permissions where safe)\n- Credential hygiene notes (location of OpenClaw creds, prefer disk encryption)\n\nAlways show the plan before any changes.\n\n### 6) Offer execution options\n\nOffer one of these choices (numbered so users can reply with a single digit):\n\n1. Do it for me (guided, step-by-step approvals)\n2. Show plan only\n3. Fix only critical issues\n4. Export commands for later\n\n### 7) Execute with confirmations\n\nFor each step:\n\n- Show the exact command\n- Explain impact and rollback\n- Confirm access will remain available\n- Stop on unexpected output and ask for guidance\n\n### 8) Verify and report\n\nRe-check:\n\n- Firewall status\n- Listening ports\n- Remote access still works\n- OpenClaw security audit (re-run)\n\nDeliver a final posture report and note any deferred items.\n\n## Required confirmations (always)\n\nRequire explicit approval for:\n\n- Firewall rule changes\n- Opening/closing ports\n- SSH/RDP configuration changes\n- Installing/removing packages\n- Enabling/disabling services\n- User/group modifications\n- Scheduling tasks or startup persistence\n- Update policy changes\n- Access to sensitive files or credentials\n\nIf unsure, ask.\n\n## Periodic checks\n\nAfter OpenClaw install or first hardening pass, run at least one baseline audit and version check:\n\n- `openclaw security audit`\n- `openclaw security audit --deep`\n- `openclaw update status`\n\nOngoing monitoring is recommended. Use the OpenClaw cron tool/CLI to schedule periodic audits (Gateway scheduler). Do not create scheduled tasks without explicit approval. Store outputs in a user-approved location and avoid secrets in logs.\nWhen scheduling headless cron runs, include a note in the output that instructs the user to call `healthcheck` so issues can be fixed.\n\n### Required prompt to schedule (always)\n\nAfter any audit or hardening pass, explicitly offer scheduling and require a direct response. Use a short prompt like (numbered):\n\n1. “Do you want me to schedule periodic audits (e.g., daily/weekly) via `openclaw cron add`?”\n\nIf the user says yes, ask for:\n\n- cadence (daily/weekly), preferred time window, and output location\n- whether to also schedule `openclaw update status`\n\nUse a stable cron job name so updates are deterministic. Prefer exact names:\n\n- `healthcheck:security-audit`\n- `healthcheck:update-status`\n\nBefore creating, `openclaw cron list` and match on exact `name`. If found, `openclaw cron edit <id> ...`.\nIf not found, `openclaw cron add --name <name> ...`.\n\nAlso offer a periodic version check so the user can decide when to update (numbered):\n\n1. `openclaw update status` (preferred for source checkouts and channels)\n2. `npm view openclaw version` (published npm version)\n\n## OpenClaw command accuracy\n\nUse only supported commands and flags:\n\n- `openclaw security audit [--deep] [--fix] [--json]`\n- `openclaw status` / `openclaw status --deep`\n- `openclaw health --json`\n- `openclaw update status`\n- `openclaw cron add|list|runs|run`\n\nDo not invent CLI flags or imply OpenClaw enforces host firewall/SSH policies.\n\n## Logging and audit trail\n\nRecord:\n\n- Gateway identity and role\n- Plan ID and timestamp\n- Approved steps and exact commands\n- Exit codes and files modified (best effort)\n\nRedact secrets. Never log tokens or full credential contents.\n\n## Memory writes (conditional)\n\nOnly write to memory files when the user explicitly opts in and the session is a private/local workspace\n(per `docs/reference/templates/AGENTS.md`). Otherwise provide a redacted, paste-ready summary the user can\ndecide to save elsewhere.\n\nFollow the durable-memory prompt format used by OpenClaw compaction:\n\n- Write lasting notes to `memory/YYYY-MM-DD.md`.\n\nAfter each audit/hardening run, if opted-in, append a short, dated summary to `memory/YYYY-MM-DD.md`\n(what was checked, key findings, actions taken, any scheduled cron jobs, key decisions,\nand all commands executed). Append-only: never overwrite existing entries.\nRedact sensitive host details (usernames, hostnames, IPs, serials, service names, tokens).\nIf there are durable preferences or decisions (risk posture, allowed ports, update policy),\nalso update `MEMORY.md` (long-term memory is optional and only used in private sessions).\n\nIf the session cannot write to the workspace, ask for permission or provide exact entries\nthe user can paste into the memory files.\n","skills/himalaya/SKILL.md":"---\nname: himalaya\ndescription: \"CLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).\"\nhomepage: https://github.com/pimalaya/himalaya\nmetadata:\n  {\n    \"emoji\": \"📧\",\n    \"requires\": { \"bins\": [\"himalaya\"] }\n  }\n---\n\n# Himalaya Email CLI\n\nHimalaya is a CLI email client that lets you manage emails from the terminal using IMAP, SMTP, Notmuch, or Sendmail backends.\n\n## References\n\n- `references/configuration.md` (config file setup + IMAP/SMTP authentication)\n- `references/message-composition.md` (MML syntax for composing emails)\n\n## Prerequisites\n\n1. Himalaya CLI installed (`himalaya --version` to verify)\n2. A configuration file at `~/.config/himalaya/config.toml`\n3. IMAP/SMTP credentials configured (password stored securely)\n\n## Configuration Setup\n\nRun the interactive wizard to set up an account:\n\n```bash\nhimalaya account configure\n```\n\nOr create `~/.config/himalaya/config.toml` manually:\n\n```toml\n[accounts.personal]\nemail = \"you@example.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\nbackend.type = \"imap\"\nbackend.host = \"imap.example.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@example.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show email/imap\"  # or use keyring\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.example.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@example.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show email/smtp\"\n```\n\n## Common Operations\n\n### List Folders\n\n```bash\nhimalaya folder list\n```\n\n### List Emails\n\nList emails in INBOX (default):\n\n```bash\nhimalaya envelope list\n```\n\nList emails in a specific folder:\n\n```bash\nhimalaya envelope list --folder \"Sent\"\n```\n\nList with pagination:\n\n```bash\nhimalaya envelope list --page 1 --page-size 20\n```\n\n### Search Emails\n\n```bash\nhimalaya envelope list from john@example.com subject meeting\n```\n\n### Read an Email\n\nRead email by ID (shows plain text):\n\n```bash\nhimalaya message read 42\n```\n\nExport raw MIME:\n\n```bash\nhimalaya message export 42 --full\n```\n\n### Reply to an Email\n\nInteractive reply (opens $EDITOR):\n\n```bash\nhimalaya message reply 42\n```\n\nReply-all:\n\n```bash\nhimalaya message reply 42 --all\n```\n\n### Forward an Email\n\n```bash\nhimalaya message forward 42\n```\n\n### Write a New Email\n\nInteractive compose (opens $EDITOR):\n\n```bash\nhimalaya message write\n```\n\nSend directly using template:\n\n```bash\ncat << 'EOF' | himalaya template send\nFrom: you@example.com\nTo: recipient@example.com\nSubject: Test Message\n\nHello from Himalaya!\nEOF\n```\n\nOr with headers flag:\n\n```bash\nhimalaya message write -H \"To:recipient@example.com\" -H \"Subject:Test\" \"Message body here\"\n```\n\n### Move/Copy Emails\n\nMove to folder:\n\n```bash\nhimalaya message move 42 \"Archive\"\n```\n\nCopy to folder:\n\n```bash\nhimalaya message copy 42 \"Important\"\n```\n\n### Delete an Email\n\n```bash\nhimalaya message delete 42\n```\n\n### Manage Flags\n\nAdd flag:\n\n```bash\nhimalaya flag add 42 --flag seen\n```\n\nRemove flag:\n\n```bash\nhimalaya flag remove 42 --flag seen\n```\n\n## Multiple Accounts\n\nList accounts:\n\n```bash\nhimalaya account list\n```\n\nUse a specific account:\n\n```bash\nhimalaya --account work envelope list\n```\n\n## Attachments\n\nSave attachments from a message:\n\n```bash\nhimalaya attachment download 42\n```\n\nSave to specific directory:\n\n```bash\nhimalaya attachment download 42 --dir ~/Downloads\n```\n\n## Output Formats\n\nMost commands support `--output` for structured output:\n\n```bash\nhimalaya envelope list --output json\nhimalaya envelope list --output plain\n```\n\n## Debugging\n\nEnable debug logging:\n\n```bash\nRUST_LOG=debug himalaya envelope list\n```\n\nFull trace with backtrace:\n\n```bash\nRUST_LOG=trace RUST_BACKTRACE=1 himalaya envelope list\n```\n\n## Tips\n\n- Use `himalaya --help` or `himalaya <command> --help` for detailed usage.\n- Message IDs are relative to the current folder; re-list after folder changes.\n- For composing rich emails with attachments, use MML syntax (see `references/message-composition.md`).\n- Store passwords securely using `pass`, system keyring, or a command that outputs the password.\n","skills/himalaya/references/configuration.md":"# Himalaya Configuration Reference\n\nConfiguration file location: `~/.config/himalaya/config.toml`\n\n## Minimal IMAP + SMTP Setup\n\n```toml\n[accounts.default]\nemail = \"user@example.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\n# IMAP backend for reading emails\nbackend.type = \"imap\"\nbackend.host = \"imap.example.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"user@example.com\"\nbackend.auth.type = \"password\"\nbackend.auth.raw = \"your-password\"\n\n# SMTP backend for sending emails\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.example.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"user@example.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.raw = \"your-password\"\n```\n\n## Password Options\n\n### Raw password (testing only, not recommended)\n\n```toml\nbackend.auth.raw = \"your-password\"\n```\n\n### Password from command (recommended)\n\n```toml\nbackend.auth.cmd = \"pass show email/imap\"\n# backend.auth.cmd = \"security find-generic-password -a user@example.com -s imap -w\"\n```\n\n### System keyring (requires keyring feature)\n\n```toml\nbackend.auth.keyring = \"imap-example\"\n```\n\nThen run `himalaya account configure <account>` to store the password.\n\n## Gmail Configuration\n\n```toml\n[accounts.gmail]\nemail = \"you@gmail.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\nbackend.type = \"imap\"\nbackend.host = \"imap.gmail.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@gmail.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show google/app-password\"\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.gmail.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@gmail.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show google/app-password\"\n```\n\n**Note:** Gmail requires an App Password if 2FA is enabled.\n\n## iCloud Configuration\n\n```toml\n[accounts.icloud]\nemail = \"you@icloud.com\"\ndisplay-name = \"Your Name\"\n\nbackend.type = \"imap\"\nbackend.host = \"imap.mail.me.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@icloud.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show icloud/app-password\"\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.mail.me.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@icloud.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show icloud/app-password\"\n```\n\n**Note:** Generate an app-specific password at appleid.apple.com\n\n## Folder Aliases\n\nMap custom folder names:\n\n```toml\n[accounts.default.folder.alias]\ninbox = \"INBOX\"\nsent = \"Sent\"\ndrafts = \"Drafts\"\ntrash = \"Trash\"\n```\n\n## Multiple Accounts\n\n```toml\n[accounts.personal]\nemail = \"personal@example.com\"\ndefault = true\n# ... backend config ...\n\n[accounts.work]\nemail = \"work@company.com\"\n# ... backend config ...\n```\n\nSwitch accounts with `--account`:\n\n```bash\nhimalaya --account work envelope list\n```\n\n## Notmuch Backend (local mail)\n\n```toml\n[accounts.local]\nemail = \"user@example.com\"\n\nbackend.type = \"notmuch\"\nbackend.db-path = \"~/.mail/.notmuch\"\n```\n\n## OAuth2 Authentication (for providers that support it)\n\n```toml\nbackend.auth.type = \"oauth2\"\nbackend.auth.client-id = \"your-client-id\"\nbackend.auth.client-secret.cmd = \"pass show oauth/client-secret\"\nbackend.auth.access-token.cmd = \"pass show oauth/access-token\"\nbackend.auth.refresh-token.cmd = \"pass show oauth/refresh-token\"\nbackend.auth.auth-url = \"https://provider.com/oauth/authorize\"\nbackend.auth.token-url = \"https://provider.com/oauth/token\"\n```\n\n## Additional Options\n\n### Signature\n\n```toml\n[accounts.default]\nsignature = \"Best regards,\\nYour Name\"\nsignature-delim = \"-- \\n\"\n```\n\n### Downloads directory\n\n```toml\n[accounts.default]\ndownloads-dir = \"~/Downloads/himalaya\"\n```\n\n### Editor for composing\n\nSet via environment variable:\n\n```bash\nexport EDITOR=\"vim\"\n```\n","skills/himalaya/references/message-composition.md":"# Message Composition with MML (MIME Meta Language)\n\nHimalaya uses MML for composing emails. MML is a simple XML-based syntax that compiles to MIME messages.\n\n## Basic Message Structure\n\nAn email message is a list of **headers** followed by a **body**, separated by a blank line:\n\n```\nFrom: sender@example.com\nTo: recipient@example.com\nSubject: Hello World\n\nThis is the message body.\n```\n\n## Headers\n\nCommon headers:\n\n- `From`: Sender address\n- `To`: Primary recipient(s)\n- `Cc`: Carbon copy recipients\n- `Bcc`: Blind carbon copy recipients\n- `Subject`: Message subject\n- `Reply-To`: Address for replies (if different from From)\n- `In-Reply-To`: Message ID being replied to\n\n### Address Formats\n\n```\nTo: user@example.com\nTo: John Doe <john@example.com>\nTo: \"John Doe\" <john@example.com>\nTo: user1@example.com, user2@example.com, \"Jane\" <jane@example.com>\n```\n\n## Plain Text Body\n\nSimple plain text email:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Plain Text Example\n\nHello, this is a plain text email.\nNo special formatting needed.\n\nBest,\nAlice\n```\n\n## MML for Rich Emails\n\n### Multipart Messages\n\nAlternative text/html parts:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Multipart Example\n\n<#multipart type=alternative>\nThis is the plain text version.\n<#part type=text/html>\n<html><body><h1>This is the HTML version</h1></body></html>\n<#/multipart>\n```\n\n### Attachments\n\nAttach a file:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: With Attachment\n\nHere is the document you requested.\n\n<#part filename=/path/to/document.pdf><#/part>\n```\n\nAttachment with custom name:\n\n```\n<#part filename=/path/to/file.pdf name=report.pdf><#/part>\n```\n\nMultiple attachments:\n\n```\n<#part filename=/path/to/doc1.pdf><#/part>\n<#part filename=/path/to/doc2.pdf><#/part>\n```\n\n### Inline Images\n\nEmbed an image inline:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Inline Image\n\n<#multipart type=related>\n<#part type=text/html>\n<html><body>\n<p>Check out this image:</p>\n<img src=\"cid:image1\">\n</body></html>\n<#part disposition=inline id=image1 filename=/path/to/image.png><#/part>\n<#/multipart>\n```\n\n### Mixed Content (Text + Attachments)\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Mixed Content\n\n<#multipart type=mixed>\n<#part type=text/plain>\nPlease find the attached files.\n\nBest,\nAlice\n<#part filename=/path/to/file1.pdf><#/part>\n<#part filename=/path/to/file2.zip><#/part>\n<#/multipart>\n```\n\n## MML Tag Reference\n\n### `<#multipart>`\n\nGroups multiple parts together.\n\n- `type=alternative`: Different representations of same content\n- `type=mixed`: Independent parts (text + attachments)\n- `type=related`: Parts that reference each other (HTML + images)\n\n### `<#part>`\n\nDefines a message part.\n\n- `type=<mime-type>`: Content type (e.g., `text/html`, `application/pdf`)\n- `filename=<path>`: File to attach\n- `name=<name>`: Display name for attachment\n- `disposition=inline`: Display inline instead of as attachment\n- `id=<cid>`: Content ID for referencing in HTML\n\n## Composing from CLI\n\n### Interactive compose\n\nOpens your `$EDITOR`:\n\n```bash\nhimalaya message write\n```\n\n### Reply (opens editor with quoted message)\n\n```bash\nhimalaya message reply 42\nhimalaya message reply 42 --all  # reply-all\n```\n\n### Forward\n\n```bash\nhimalaya message forward 42\n```\n\n### Send from stdin\n\n```bash\ncat message.txt | himalaya template send\n```\n\n### Prefill headers from CLI\n\n```bash\nhimalaya message write \\\n  -H \"To:recipient@example.com\" \\\n  -H \"Subject:Quick Message\" \\\n  \"Message body here\"\n```\n\n## Tips\n\n- The editor opens with a template; fill in headers and body.\n- Save and exit the editor to send; exit without saving to cancel.\n- MML parts are compiled to proper MIME when sending.\n- Use `himalaya message export --full` to inspect the raw MIME structure of received emails.\n","skills/imsg/SKILL.md":"---\nname: imsg\ndescription: iMessage/SMS CLI for listing chats, history, and sending messages via Messages.app.\nhomepage: https://imsg.to\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📨\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"imsg\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/imsg\",\n              \"bins\": [\"imsg\"],\n              \"label\": \"Install imsg (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# imsg\n\nUse `imsg` to read and send iMessage/SMS via macOS Messages.app.\n\n## When to Use\n\n✅ **USE this skill when:**\n\n- User explicitly asks to send iMessage or SMS\n- Reading iMessage conversation history\n- Checking recent Messages.app chats\n- Sending to phone numbers or Apple IDs\n\n## When NOT to Use\n\n❌ **DON'T use this skill when:**\n\n- Telegram messages → use `message` tool with `channel:telegram`\n- Signal messages → use Signal channel if configured\n- WhatsApp messages → use WhatsApp channel if configured\n- Discord messages → use `message` tool with `channel:discord`\n- Slack messages → use `slack` skill\n- Group chat management (adding/removing members) → not supported\n- Bulk/mass messaging → always confirm with user first\n- Replying in current conversation → just reply normally (OpenClaw routes automatically)\n\n## Requirements\n\n- macOS with Messages.app signed in\n- Full Disk Access for terminal\n- Automation permission for Messages.app (for sending)\n\n## Common Commands\n\n### List Chats\n\n```bash\nimsg chats --limit 10 --json\n```\n\n### View History\n\n```bash\n# By chat ID\nimsg history --chat-id 1 --limit 20 --json\n\n# With attachments info\nimsg history --chat-id 1 --limit 20 --attachments --json\n```\n\n### Watch for New Messages\n\n```bash\nimsg watch --chat-id 1 --attachments\n```\n\n### Send Messages\n\n```bash\n# Text only\nimsg send --to \"+14155551212\" --text \"Hello!\"\n\n# With attachment\nimsg send --to \"+14155551212\" --text \"Check this out\" --file /path/to/image.jpg\n\n# Specify service\nimsg send --to \"+14155551212\" --text \"Hi\" --service imessage\nimsg send --to \"+14155551212\" --text \"Hi\" --service sms\n```\n\n## Service Options\n\n- `--service imessage` — Force iMessage (requires recipient has iMessage)\n- `--service sms` — Force SMS (green bubble)\n- `--service auto` — Let Messages.app decide (default)\n\n## Safety Rules\n\n1. **Always confirm recipient and message content** before sending\n2. **Never send to unknown numbers** without explicit user approval\n3. **Be careful with attachments** — confirm file path exists\n4. **Rate limit yourself** — don't spam\n\n## Example Workflow\n\nUser: \"Text mom that I'll be late\"\n\n```bash\n# 1. Find mom's chat\nimsg chats --limit 20 --json | jq '.[] | select(.displayName | contains(\"Mom\"))'\n\n# 2. Confirm with user\n# \"Found Mom at +1555123456. Send 'I'll be late' via iMessage?\"\n\n# 3. Send after confirmation\nimsg send --to \"+1555123456\" --text \"I'll be late\"\n```\n","skills/mcporter/SKILL.md":"---\nname: mcporter\ndescription: Use the mcporter CLI to list, configure, auth, and call MCP servers/tools directly (HTTP or stdio), including ad-hoc servers, config edits, and CLI/type generation.\nhomepage: http://mcporter.dev\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📦\",\n        \"requires\": { \"bins\": [\"mcporter\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node\",\n              \"kind\": \"node\",\n              \"package\": \"mcporter\",\n              \"bins\": [\"mcporter\"],\n              \"label\": \"Install mcporter (node)\",\n            },\n          ],\n      },\n  }\n---\n\n# mcporter\n\nUse `mcporter` to work with MCP servers directly.\n\nQuick start\n\n- `mcporter list`\n- `mcporter list <server> --schema`\n- `mcporter call <server.tool> key=value`\n\nCall tools\n\n- Selector: `mcporter call linear.list_issues team=ENG limit:5`\n- Function syntax: `mcporter call \"linear.create_issue(title: \\\"Bug\\\")\"`\n- Full URL: `mcporter call https://api.example.com/mcp.fetch url:https://example.com`\n- Stdio: `mcporter call --stdio \"bun run ./server.ts\" scrape url=https://example.com`\n- JSON payload: `mcporter call <server.tool> --args '{\"limit\":5}'`\n\nAuth + config\n\n- OAuth: `mcporter auth <server | url> [--reset]`\n- Config: `mcporter config list|get|add|remove|import|login|logout`\n\nDaemon\n\n- `mcporter daemon start|status|stop|restart`\n\nCodegen\n\n- CLI: `mcporter generate-cli --server <name>` or `--command <url>`\n- Inspect: `mcporter inspect-cli <path> [--json]`\n- TS: `mcporter emit-ts <server> --mode client|types`\n\nNotes\n\n- Config default: `./config/mcporter.json` (override with `--config`).\n- Prefer `--output json` for machine-readable results.\n","skills/model-usage/SKILL.md":"---\nname: model-usage\ndescription: Use CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📊\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"codexbar\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew-cask\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/codexbar\",\n              \"bins\": [\"codexbar\"],\n              \"label\": \"Install CodexBar (brew cask)\",\n            },\n          ],\n      },\n  }\n---\n\n# Model usage\n\n## Overview\n\nGet per-model usage cost from CodexBar's local cost logs. Supports \"current model\" (most recent daily entry) or \"all models\" summaries for Codex or Claude.\n\nTODO: add Linux CLI support guidance once CodexBar CLI install path is documented for Linux.\n\n## Quick start\n\n1. Fetch cost JSON via CodexBar CLI or pass a JSON file.\n2. Use the bundled script to summarize by model.\n\n```bash\npython {baseDir}/scripts/model_usage.py --provider codex --mode current\npython {baseDir}/scripts/model_usage.py --provider codex --mode all\npython {baseDir}/scripts/model_usage.py --provider claude --mode all --format json --pretty\n```\n\n## Current model logic\n\n- Uses the most recent daily row with `modelBreakdowns`.\n- Picks the model with the highest cost in that row.\n- Falls back to the last entry in `modelsUsed` when breakdowns are missing.\n- Override with `--model <name>` when you need a specific model.\n\n## Inputs\n\n- Default: runs `codexbar cost --format json --provider <codex|claude>`.\n- File or stdin:\n\n```bash\ncodexbar cost --provider codex --format json > /tmp/cost.json\npython {baseDir}/scripts/model_usage.py --input /tmp/cost.json --mode all\ncat /tmp/cost.json | python {baseDir}/scripts/model_usage.py --input - --mode current\n```\n\n## Output\n\n- Text (default) or JSON (`--format json --pretty`).\n- Values are cost-only per model; tokens are not split by model in CodexBar output.\n\n## References\n\n- Read `references/codexbar-cli.md` for CLI flags and cost JSON fields.\n","skills/model-usage/references/codexbar-cli.md":"# CodexBar CLI quick ref (usage + cost)\n\n## Install\n\n- App: Preferences -> Advanced -> Install CLI\n- Repo: ./bin/install-codexbar-cli.sh\n\n## Commands\n\n- Usage snapshot (web/cli sources):\n  - codexbar usage --format json --pretty\n  - codexbar --provider all --format json\n- Local cost usage (Codex + Claude only):\n  - codexbar cost --format json --pretty\n  - codexbar cost --provider codex|claude --format json\n\n## Cost JSON fields\n\nThe payload is an array (one per provider).\n\n- provider, source, updatedAt\n- sessionTokens, sessionCostUSD\n- last30DaysTokens, last30DaysCostUSD\n- daily[]: date, inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, totalCost, modelsUsed, modelBreakdowns[]\n- modelBreakdowns[]: modelName, cost\n- totals: totalInputTokens, totalOutputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, totalCost\n\n## Notes\n\n- Cost usage is local-only. It reads JSONL logs under:\n  - Codex: ~/.codex/sessions/\\*_/_.jsonl\n  - Claude: ~/.config/claude/projects/**/\\*.jsonl or ~/.claude/projects/**/\\*.jsonl\n- If web usage is required (non-local), use codexbar usage (not cost).\n","skills/nano-pdf/SKILL.md":"---\nname: nano-pdf\ndescription: Edit PDFs with natural-language instructions using the nano-pdf CLI.\nhomepage: https://pypi.org/project/nano-pdf/\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📄\",\n        \"requires\": { \"bins\": [\"nano-pdf\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"uv\",\n              \"kind\": \"uv\",\n              \"package\": \"nano-pdf\",\n              \"bins\": [\"nano-pdf\"],\n              \"label\": \"Install nano-pdf (uv)\",\n            },\n          ],\n      },\n  }\n---\n\n# nano-pdf\n\nUse `nano-pdf` to apply edits to a specific page in a PDF using a natural-language instruction.\n\n## Quick start\n\n```bash\nnano-pdf edit deck.pdf 1 \"Change the title to 'Q3 Results' and fix the typo in the subtitle\"\n```\n\nNotes:\n\n- Page numbers are 0-based or 1-based depending on the tool’s version/config; if the result looks off by one, retry with the other.\n- Always sanity-check the output PDF before sending it out.\n","skills/node-connect/SKILL.md":"---\nname: node-connect\ndescription: Diagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps. Use when QR/setup code/manual connect fails, local Wi-Fi works but VPS/tailnet does not, or errors mention pairing required, unauthorized, bootstrap token invalid or expired, gateway.bind, gateway.remote.url, Tailscale, or plugins.entries.device-pair.config.publicUrl.\n---\n\n# Node Connect\n\nGoal: find the one real route from node -> gateway, verify OpenClaw is advertising that route, then fix pairing/auth.\n\n## Topology first\n\nDecide which case you are in before proposing fixes:\n\n- same machine / emulator / USB tunnel\n- same LAN / local Wi-Fi\n- same Tailscale tailnet\n- public URL / reverse proxy\n\nDo not mix them.\n\n- Local Wi-Fi problem: do not switch to Tailscale unless remote access is actually needed.\n- VPS / remote gateway problem: do not keep debugging `localhost` or LAN IPs.\n\n## If ambiguous, ask first\n\nIf the setup is unclear or the failure report is vague, ask short clarifying questions before diagnosing.\n\nAsk for:\n\n- which route they intend: same machine, same LAN, Tailscale tailnet, or public URL\n- whether they used QR/setup code or manual host/port\n- the exact app text/status/error, quoted exactly if possible\n- whether `openclaw devices list` shows a pending pairing request\n\nDo not guess from `can't connect`.\n\n## Canonical checks\n\nPrefer `openclaw qr --json`. It uses the same setup-code payload Android scans.\n\n```bash\nopenclaw config get gateway.mode\nopenclaw config get gateway.bind\nopenclaw config get gateway.tailscale.mode\nopenclaw config get gateway.remote.url\nopenclaw config get gateway.auth.mode\nopenclaw config get gateway.auth.allowTailscale\nopenclaw config get plugins.entries.device-pair.config.publicUrl\nopenclaw qr --json\nopenclaw devices list\nopenclaw nodes status\n```\n\nIf this OpenClaw instance is pointed at a remote gateway, also run:\n\n```bash\nopenclaw qr --remote --json\n```\n\nIf Tailscale is part of the story:\n\n```bash\ntailscale status --json\n```\n\n## Read the result, not guesses\n\n`openclaw qr --json` success means:\n\n- `gatewayUrl`: this is the actual endpoint the app should use.\n- `urlSource`: this tells you which config path won.\n\nCommon good sources:\n\n- `gateway.bind=lan`: same Wi-Fi / LAN only\n- `gateway.bind=tailnet`: direct tailnet access\n- `gateway.tailscale.mode=serve` or `gateway.tailscale.mode=funnel`: Tailscale route\n- `plugins.entries.device-pair.config.publicUrl`: explicit public/reverse-proxy route\n- `gateway.remote.url`: remote gateway route\n\n## Root-cause map\n\nIf `openclaw qr --json` says `Gateway is only bound to loopback`:\n\n- remote node cannot connect yet\n- fix the route, then generate a fresh setup code\n- `gateway.bind=auto` is not enough if the effective QR route is still loopback\n- same LAN: use `gateway.bind=lan`\n- same tailnet: prefer `gateway.tailscale.mode=serve` or use `gateway.bind=tailnet`\n- public internet: set a real `plugins.entries.device-pair.config.publicUrl` or `gateway.remote.url`\n\nIf `gateway.bind=tailnet set, but no tailnet IP was found`:\n\n- gateway host is not actually on Tailscale\n\nIf `qr --remote requires gateway.remote.url`:\n\n- remote-mode config is incomplete\n\nIf the app says `pairing required`:\n\n- network route and auth worked\n- approve the pending device\n\n```bash\nopenclaw devices list\nopenclaw devices approve --latest\n```\n\nIf the app says `bootstrap token invalid or expired`:\n\n- old setup code\n- generate a fresh one and rescan\n- do this after any URL/auth fix too\n\nIf the app says `unauthorized`:\n\n- wrong token/password, or wrong Tailscale expectation\n- for Tailscale Serve, `gateway.auth.allowTailscale` must match the intended flow\n- otherwise use explicit token/password\n\n## Fast heuristics\n\n- Same Wi-Fi setup + gateway advertises `127.0.0.1`, `localhost`, or loopback-only config: wrong.\n- Remote setup + setup/manual uses private LAN IP: wrong.\n- Tailnet setup + gateway advertises LAN IP instead of MagicDNS / tailnet route: wrong.\n- Public URL set but QR still advertises something else: inspect `urlSource`; config is not what you think.\n- `openclaw devices list` shows pending requests: stop changing network config and approve first.\n\n## Fix style\n\nReply with one concrete diagnosis and one route.\n\nIf there is not enough signal yet, ask for setup + exact app text instead of guessing.\n\nGood:\n\n- `The gateway is still loopback-only, so a node on another network can never reach it. Enable Tailscale Serve, restart the gateway, run openclaw qr again, rescan, then approve the pending device pairing.`\n\nBad:\n\n- `Maybe LAN, maybe Tailscale, maybe port forwarding, maybe public URL.`\n","skills/notion/SKILL.md":"---\nname: notion\ndescription: Notion API for creating and managing pages, databases, and blocks.\nhomepage: https://developers.notion.com\nmetadata:\n  {\n    \"emoji\": \"📝\",\n    \"requires\": { \"env\": [\"NOTION_API_KEY\"] },\n    \"primaryEnv\": \"NOTION_API_KEY\"\n  }\n---\n\n# notion\n\nUse the Notion API to create/read/update pages, data sources (databases), and blocks.\n\n## Setup\n\n1. Create an integration at https://notion.so/my-integrations\n2. Copy the API key (starts with `ntn_` or `secret_`)\n3. Store it:\n\n```bash\nmkdir -p ~/.config/notion\necho \"ntn_your_key_here\" > ~/.config/notion/api_key\n```\n\n4. Share target pages/databases with your integration (click \"...\" → \"Connect to\" → your integration name)\n\n## API Basics\n\nAll requests need:\n\n```bash\nNOTION_KEY=$(cat ~/.config/notion/api_key)\ncurl -X GET \"https://api.notion.com/v1/...\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\"\n```\n\n> **Note:** The `Notion-Version` header is required. This skill uses `2025-09-03` (latest). In this version, databases are called \"data sources\" in the API.\n\n## Common Operations\n\n**Search for pages and data sources:**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/search\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"query\": \"page title\"}'\n```\n\n**Get page:**\n\n```bash\ncurl \"https://api.notion.com/v1/pages/{page_id}\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\"\n```\n\n**Get page content (blocks):**\n\n```bash\ncurl \"https://api.notion.com/v1/blocks/{page_id}/children\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\"\n```\n\n**Create page in a data source:**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/pages\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"parent\": {\"database_id\": \"xxx\"},\n    \"properties\": {\n      \"Name\": {\"title\": [{\"text\": {\"content\": \"New Item\"}}]},\n      \"Status\": {\"select\": {\"name\": \"Todo\"}}\n    }\n  }'\n```\n\n**Query a data source (database):**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/data_sources/{data_source_id}/query\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"filter\": {\"property\": \"Status\", \"select\": {\"equals\": \"Active\"}},\n    \"sorts\": [{\"property\": \"Date\", \"direction\": \"descending\"}]\n  }'\n```\n\n**Create a data source (database):**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/data_sources\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"parent\": {\"page_id\": \"xxx\"},\n    \"title\": [{\"text\": {\"content\": \"My Database\"}}],\n    \"properties\": {\n      \"Name\": {\"title\": {}},\n      \"Status\": {\"select\": {\"options\": [{\"name\": \"Todo\"}, {\"name\": \"Done\"}]}},\n      \"Date\": {\"date\": {}}\n    }\n  }'\n```\n\n**Update page properties:**\n\n```bash\ncurl -X PATCH \"https://api.notion.com/v1/pages/{page_id}\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"properties\": {\"Status\": {\"select\": {\"name\": \"Done\"}}}}'\n```\n\n**Add blocks to page:**\n\n```bash\ncurl -X PATCH \"https://api.notion.com/v1/blocks/{page_id}/children\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"children\": [\n      {\"object\": \"block\", \"type\": \"paragraph\", \"paragraph\": {\"rich_text\": [{\"text\": {\"content\": \"Hello\"}}]}}\n    ]\n  }'\n```\n\n## Property Types\n\nCommon property formats for database items:\n\n- **Title:** `{\"title\": [{\"text\": {\"content\": \"...\"}}]}`\n- **Rich text:** `{\"rich_text\": [{\"text\": {\"content\": \"...\"}}]}`\n- **Select:** `{\"select\": {\"name\": \"Option\"}}`\n- **Multi-select:** `{\"multi_select\": [{\"name\": \"A\"}, {\"name\": \"B\"}]}`\n- **Date:** `{\"date\": {\"start\": \"2024-01-15\", \"end\": \"2024-01-16\"}}`\n- **Checkbox:** `{\"checkbox\": true}`\n- **Number:** `{\"number\": 42}`\n- **URL:** `{\"url\": \"https://...\"}`\n- **Email:** `{\"email\": \"a@b.com\"}`\n- **Relation:** `{\"relation\": [{\"id\": \"page_id\"}]}`\n\n## Key Differences in 2025-09-03\n\n- **Databases → Data Sources:** Use `/data_sources/` endpoints for queries and retrieval\n- **Two IDs:** Each database now has both a `database_id` and a `data_source_id`\n  - Use `database_id` when creating pages (`parent: {\"database_id\": \"...\"}`)\n  - Use `data_source_id` when querying (`POST /v1/data_sources/{id}/query`)\n- **Search results:** Databases return as `\"object\": \"data_source\"` with their `data_source_id`\n- **Parent in responses:** Pages show `parent.data_source_id` alongside `parent.database_id`\n- **Finding the data_source_id:** Search for the database, or call `GET /v1/data_sources/{data_source_id}`\n\n## Notes\n\n- Page/database IDs are UUIDs (with or without dashes)\n- The API cannot set database view filters — that's UI-only\n- Rate limit: ~3 requests/second average, with `429 rate_limited` responses using `Retry-After`\n- Append block children: up to 100 children per request, up to two levels of nesting in a single append request\n- Payload size limits: up to 1000 block elements and 500KB overall\n- Use `is_inline: true` when creating data sources to embed them in pages\n","skills/obsidian/SKILL.md":"---\nname: obsidian\ndescription: Work with Obsidian vaults (plain Markdown notes) and automate via obsidian-cli.\nhomepage: https://help.obsidian.md\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"💎\",\n        \"requires\": { \"bins\": [\"obsidian-cli\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"yakitrak/yakitrak/obsidian-cli\",\n              \"bins\": [\"obsidian-cli\"],\n              \"label\": \"Install obsidian-cli (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# Obsidian\n\nObsidian vault = a normal folder on disk.\n\nVault structure (typical)\n\n- Notes: `*.md` (plain text Markdown; edit with any editor)\n- Config: `.obsidian/` (workspace + plugin settings; usually don’t touch from scripts)\n- Canvases: `*.canvas` (JSON)\n- Attachments: whatever folder you chose in Obsidian settings (images/PDFs/etc.)\n\n## Find the active vault(s)\n\nObsidian desktop tracks vaults here (source of truth):\n\n- `~/Library/Application Support/obsidian/obsidian.json`\n\n`obsidian-cli` resolves vaults from that file; vault name is typically the **folder name** (path suffix).\n\nFast “what vault is active / where are the notes?”\n\n- If you’ve already set a default: `obsidian-cli print-default --path-only`\n- Otherwise, read `~/Library/Application Support/obsidian/obsidian.json` and use the vault entry with `\"open\": true`.\n\nNotes\n\n- Multiple vaults common (iCloud vs `~/Documents`, work/personal, etc.). Don’t guess; read config.\n- Avoid writing hardcoded vault paths into scripts; prefer reading the config or using `print-default`.\n\n## obsidian-cli quick start\n\nPick a default vault (once):\n\n- `obsidian-cli set-default \"<vault-folder-name>\"`\n- `obsidian-cli print-default` / `obsidian-cli print-default --path-only`\n\nSearch\n\n- `obsidian-cli search \"query\"` (note names)\n- `obsidian-cli search-content \"query\"` (inside notes; shows snippets + lines)\n\nCreate\n\n- `obsidian-cli create \"Folder/New note\" --content \"...\" --open`\n- Requires Obsidian URI handler (`obsidian://…`) working (Obsidian installed).\n- Avoid creating notes under “hidden” dot-folders (e.g. `.something/...`) via URI; Obsidian may refuse.\n\nMove/rename (safe refactor)\n\n- `obsidian-cli move \"old/path/note\" \"new/path/note\"`\n- Updates `[[wikilinks]]` and common Markdown links across the vault (this is the main win vs `mv`).\n\nDelete\n\n- `obsidian-cli delete \"path/note\"`\n\nPrefer direct edits when appropriate: open the `.md` file and change it; Obsidian will pick it up.\n","skills/openai-whisper-api/SKILL.md":"---\nname: openai-whisper-api\ndescription: Transcribe audio via OpenAI Audio Transcriptions API (Whisper).\nhomepage: https://platform.openai.com/docs/guides/speech-to-text\nmetadata:\n  {\n    \"emoji\": \"🌐\",\n    \"requires\": { \"bins\": [\"curl\"], \"env\": [\"OPENAI_API_KEY\"] },\n    \"primaryEnv\": \"OPENAI_API_KEY\"\n  }\n---\n\n# OpenAI Whisper API (curl)\n\nTranscribe an audio file via OpenAI's `/v1/audio/transcriptions` endpoint. Set `OPENAI_BASE_URL` to use an OpenAI-compatible proxy or local gateway.\n\n## Quick start\n\n```bash\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@/path/to/audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  > transcript.txt\n```\n\nDefaults:\n\n- Model: `whisper-1`\n- Output format: `text`\n\n## Options\n\n```bash\n# With language hint\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.ogg\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  -F \"language=en\" \\\n  > transcript.txt\n\n# With speaker hint (prompt)\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  -F \"prompt=Speaker names: Peter, Daniel\" \\\n  > transcript.txt\n\n# JSON output\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=json\" \\\n  > transcript.json\n```\n\n## Custom base URL\n\nSet `OPENAI_BASE_URL` to use an OpenAI-compatible proxy or local gateway:\n\n```bash\nAPI_BASE=\"${OPENAI_BASE_URL:-https://api.openai.com/v1}\"\ncurl -sS \"${API_BASE}/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  > transcript.txt\n```\n\n## API key\n\nSet `OPENAI_API_KEY` environment variable before running commands.\n","skills/openai-whisper/SKILL.md":"---\nname: openai-whisper\ndescription: Local speech-to-text with the Whisper CLI (no API key).\nhomepage: https://openai.com/research/whisper\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🎤\",\n        \"requires\": { \"bins\": [\"whisper\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"openai-whisper\",\n              \"bins\": [\"whisper\"],\n              \"label\": \"Install OpenAI Whisper (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# Whisper (CLI)\n\nUse `whisper` to transcribe audio locally.\n\nQuick start\n\n- `whisper /path/audio.mp3 --model medium --output_format txt --output_dir .`\n- `whisper /path/audio.m4a --task translate --output_format srt`\n\nNotes\n\n- Models download to `~/.cache/whisper` on first run.\n- `--model` defaults to `turbo` on this install.\n- Use smaller models for speed, larger for accuracy.\n","skills/openhue/SKILL.md":"---\nname: openhue\ndescription: Control Philips Hue lights and scenes via the OpenHue CLI.\nhomepage: https://www.openhue.io/cli\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"💡\",\n        \"requires\": { \"bins\": [\"openhue\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"openhue/cli/openhue-cli\",\n              \"bins\": [\"openhue\"],\n              \"label\": \"Install OpenHue CLI (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# OpenHue CLI\n\nUse `openhue` to control Philips Hue lights and scenes via a Hue Bridge.\n\n## When to Use\n\n✅ **USE this skill when:**\n\n- \"Turn on/off the lights\"\n- \"Dim the living room lights\"\n- \"Set a scene\" or \"movie mode\"\n- Controlling specific Hue rooms or zones\n- Adjusting brightness, color, or color temperature\n\n## When NOT to Use\n\n❌ **DON'T use this skill when:**\n\n- Non-Hue smart devices (other brands) → not supported\n- HomeKit scenes or Shortcuts → use Apple's ecosystem\n- TV or entertainment system control\n- Thermostat or HVAC\n- Smart plugs (unless Hue smart plugs)\n\n## Common Commands\n\n### List Resources\n\n```bash\nopenhue get light       # List all lights\nopenhue get room        # List all rooms\nopenhue get scene       # List all scenes\n```\n\n### Control Lights\n\n```bash\n# Turn on/off\nopenhue set light \"Bedroom Lamp\" --on\nopenhue set light \"Bedroom Lamp\" --off\n\n# Brightness (0-100)\nopenhue set light \"Bedroom Lamp\" --on --brightness 50\n\n# Color temperature (warm to cool: 153-500 mirek)\nopenhue set light \"Bedroom Lamp\" --on --temperature 300\n\n# Color (by name or hex)\nopenhue set light \"Bedroom Lamp\" --on --color red\nopenhue set light \"Bedroom Lamp\" --on --rgb \"#FF5500\"\n```\n\n### Control Rooms\n\n```bash\n# Turn off entire room\nopenhue set room \"Bedroom\" --off\n\n# Set room brightness\nopenhue set room \"Bedroom\" --on --brightness 30\n```\n\n### Scenes\n\n```bash\n# Activate scene\nopenhue set scene \"Relax\" --room \"Bedroom\"\nopenhue set scene \"Concentrate\" --room \"Office\"\n```\n\n## Quick Presets\n\n```bash\n# Bedtime (dim warm)\nopenhue set room \"Bedroom\" --on --brightness 20 --temperature 450\n\n# Work mode (bright cool)\nopenhue set room \"Office\" --on --brightness 100 --temperature 250\n\n# Movie mode (dim)\nopenhue set room \"Living Room\" --on --brightness 10\n```\n\n## Notes\n\n- Bridge must be on local network\n- First run requires button press on Hue bridge to pair\n- Colors only work on color-capable bulbs (not white-only)\n","skills/oracle/SKILL.md":"---\nname: oracle\ndescription: Best practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns).\nhomepage: https://askoracle.dev\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🧿\",\n        \"requires\": { \"bins\": [\"oracle\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node\",\n              \"kind\": \"node\",\n              \"package\": \"@steipete/oracle\",\n              \"bins\": [\"oracle\"],\n              \"label\": \"Install oracle (node)\",\n            },\n          ],\n      },\n  }\n---\n\n# oracle — best use\n\nOracle bundles your prompt + selected files into one “one-shot” request so another model can answer with real repo context (API or browser automation). Treat output as advisory: verify against code + tests.\n\n## Main use case (browser, GPT‑5.2 Pro)\n\nDefault workflow here: `--engine browser` with GPT‑5.2 Pro in ChatGPT. This is the common “long think” path: ~10 minutes to ~1 hour is normal; expect a stored session you can reattach to.\n\nRecommended defaults:\n\n- Engine: browser (`--engine browser`)\n- Model: GPT‑5.2 Pro (`--model gpt-5.2-pro` or `--model \"5.2 Pro\"`)\n\n## Golden path\n\n1. Pick a tight file set (fewest files that still contain the truth).\n2. Preview payload + token spend (`--dry-run` + `--files-report`).\n3. Use browser mode for the usual GPT‑5.2 Pro workflow; use API only when you explicitly want it.\n4. If the run detaches/timeouts: reattach to the stored session (don’t re-run).\n\n## Commands (preferred)\n\n- Help:\n  - `oracle --help`\n  - If the binary isn’t installed: `npx -y @steipete/oracle --help` (avoid `pnpx` here; sqlite bindings).\n\n- Preview (no tokens):\n  - `oracle --dry-run summary -p \"<task>\" --file \"src/**\" --file \"!**/*.test.*\"`\n  - `oracle --dry-run full -p \"<task>\" --file \"src/**\"`\n\n- Token sanity:\n  - `oracle --dry-run summary --files-report -p \"<task>\" --file \"src/**\"`\n\n- Browser run (main path; long-running is normal):\n  - `oracle --engine browser --model gpt-5.2-pro -p \"<task>\" --file \"src/**\"`\n\n- Manual paste fallback:\n  - `oracle --render --copy -p \"<task>\" --file \"src/**\"`\n  - Note: `--copy` is a hidden alias for `--copy-markdown`.\n\n## Attaching files (`--file`)\n\n`--file` accepts files, directories, and globs. You can pass it multiple times; entries can be comma-separated.\n\n- Include:\n  - `--file \"src/**\"`\n  - `--file src/index.ts`\n  - `--file docs --file README.md`\n\n- Exclude:\n  - `--file \"src/**\" --file \"!src/**/*.test.ts\" --file \"!**/*.snap\"`\n\n- Defaults (implementation behavior):\n  - Default-ignored dirs: `node_modules`, `dist`, `coverage`, `.git`, `.turbo`, `.next`, `build`, `tmp` (skipped unless explicitly passed as literal dirs/files).\n  - Honors `.gitignore` when expanding globs.\n  - Does not follow symlinks.\n  - Dotfiles filtered unless opted in via pattern (e.g. `--file \".github/**\"`).\n  - Files > 1 MB rejected.\n\n## Engines (API vs browser)\n\n- Auto-pick: `api` when `OPENAI_API_KEY` is set; otherwise `browser`.\n- Browser supports GPT + Gemini only; use `--engine api` for Claude/Grok/Codex or multi-model runs.\n- Browser attachments:\n  - `--browser-attachments auto|never|always` (auto pastes inline up to ~60k chars then uploads).\n- Remote browser host:\n  - Host: `oracle serve --host 0.0.0.0 --port 9473 --token <secret>`\n  - Client: `oracle --engine browser --remote-host <host:port> --remote-token <secret> -p \"<task>\" --file \"src/**\"`\n\n## Sessions + slugs\n\n- Stored under `~/.oracle/sessions` (override with `ORACLE_HOME_DIR`).\n- Runs may detach or take a long time (browser + GPT‑5.2 Pro often does). If the CLI times out: don’t re-run; reattach.\n  - List: `oracle status --hours 72`\n  - Attach: `oracle session <id> --render`\n- Use `--slug \"<3-5 words>\"` to keep session IDs readable.\n- Duplicate prompt guard exists; use `--force` only when you truly want a fresh run.\n\n## Prompt template (high signal)\n\nOracle starts with **zero** project knowledge. Assume the model cannot infer your stack, build tooling, conventions, or “obvious” paths. Include:\n\n- Project briefing (stack + build/test commands + platform constraints).\n- “Where things live” (key directories, entrypoints, config files, boundaries).\n- Exact question + what you tried + the error text (verbatim).\n- Constraints (“don’t change X”, “must keep public API”, etc).\n- Desired output (“return patch plan + tests”, “give 3 options with tradeoffs”).\n\n## Safety\n\n- Don’t attach secrets by default (`.env`, key files, auth tokens). Redact aggressively; share only what’s required.\n\n## “Exhaustive prompt” restoration pattern\n\nFor long investigations, write a standalone prompt + file set so you can rerun days later:\n\n- 6–30 sentence project briefing + the goal.\n- Repro steps + exact errors + what you tried.\n- Attach all context files needed (entrypoints, configs, key modules, docs).\n\nOracle runs are one-shot; the model doesn’t remember prior runs. “Restoring context” means re-running with the same prompt + `--file …` set (or reattaching a still-running stored session).\n","skills/ordercli/SKILL.md":"---\nname: ordercli\ndescription: Foodora-only CLI for checking past orders and active order status (Deliveroo WIP).\nhomepage: https://ordercli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🛵\",\n        \"requires\": { \"bins\": [\"ordercli\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/ordercli\",\n              \"bins\": [\"ordercli\"],\n              \"label\": \"Install ordercli (brew)\",\n            },\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/ordercli/cmd/ordercli@latest\",\n              \"bins\": [\"ordercli\"],\n              \"label\": \"Install ordercli (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# ordercli\n\nUse `ordercli` to check past orders and track active order status (Foodora only right now).\n\nQuick start (Foodora)\n\n- `ordercli foodora countries`\n- `ordercli foodora config set --country AT`\n- `ordercli foodora login --email you@example.com --password-stdin`\n- `ordercli foodora orders`\n- `ordercli foodora history --limit 20`\n- `ordercli foodora history show <orderCode>`\n\nOrders\n\n- Active list (arrival/status): `ordercli foodora orders`\n- Watch: `ordercli foodora orders --watch`\n- Active order detail: `ordercli foodora order <orderCode>`\n- History detail JSON: `ordercli foodora history show <orderCode> --json`\n\nReorder (adds to cart)\n\n- Preview: `ordercli foodora reorder <orderCode>`\n- Confirm: `ordercli foodora reorder <orderCode> --confirm`\n- Address: `ordercli foodora reorder <orderCode> --confirm --address-id <id>`\n\nCloudflare / bot protection\n\n- Browser login: `ordercli foodora login --email you@example.com --password-stdin --browser`\n- Reuse profile: `--browser-profile \"$HOME/Library/Application Support/ordercli/browser-profile\"`\n- Import Chrome cookies: `ordercli foodora cookies chrome --profile \"Default\"`\n\nSession import (no password)\n\n- `ordercli foodora session chrome --url https://www.foodora.at/ --profile \"Default\"`\n- `ordercli foodora session refresh --client-id android`\n\nDeliveroo (WIP, not working yet)\n\n- Requires `DELIVEROO_BEARER_TOKEN` (optional `DELIVEROO_COOKIE`).\n- `ordercli deliveroo config set --market uk`\n- `ordercli deliveroo history`\n\nNotes\n\n- Use `--config /tmp/ordercli.json` for testing.\n- Confirm before any reorder or cart-changing action.\n","skills/peekaboo/SKILL.md":"---\nname: peekaboo\ndescription: Capture and automate macOS UI with the Peekaboo CLI.\nhomepage: https://peekaboo.boo\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"👀\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"peekaboo\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/peekaboo\",\n              \"bins\": [\"peekaboo\"],\n              \"label\": \"Install Peekaboo (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# Peekaboo\n\nPeekaboo is a full macOS UI automation CLI: capture/inspect screens, target UI\nelements, drive input, and manage apps/windows/menus. Commands share a snapshot\ncache and support `--json`/`-j` for scripting. Run `peekaboo` or\n`peekaboo <cmd> --help` for flags; `peekaboo --version` prints build metadata.\nTip: run via `polter peekaboo` to ensure fresh builds.\n\n## Features (all CLI capabilities, excluding agent/MCP)\n\nCore\n\n- `bridge`: inspect Peekaboo Bridge host connectivity\n- `capture`: live capture or video ingest + frame extraction\n- `clean`: prune snapshot cache and temp files\n- `config`: init/show/edit/validate, providers, models, credentials\n- `image`: capture screenshots (screen/window/menu bar regions)\n- `learn`: print the full agent guide + tool catalog\n- `list`: apps, windows, screens, menubar, permissions\n- `permissions`: check Screen Recording/Accessibility status\n- `run`: execute `.peekaboo.json` scripts\n- `sleep`: pause execution for a duration\n- `tools`: list available tools with filtering/display options\n\nInteraction\n\n- `click`: target by ID/query/coords with smart waits\n- `drag`: drag & drop across elements/coords/Dock\n- `hotkey`: modifier combos like `cmd,shift,t`\n- `move`: cursor positioning with optional smoothing\n- `paste`: set clipboard -> paste -> restore\n- `press`: special-key sequences with repeats\n- `scroll`: directional scrolling (targeted + smooth)\n- `swipe`: gesture-style drags between targets\n- `type`: text + control keys (`--clear`, delays)\n\nSystem\n\n- `app`: launch/quit/relaunch/hide/unhide/switch/list apps\n- `clipboard`: read/write clipboard (text/images/files)\n- `dialog`: click/input/file/dismiss/list system dialogs\n- `dock`: launch/right-click/hide/show/list Dock items\n- `menu`: click/list application menus + menu extras\n- `menubar`: list/click status bar items\n- `open`: enhanced `open` with app targeting + JSON payloads\n- `space`: list/switch/move-window (Spaces)\n- `visualizer`: exercise Peekaboo visual feedback animations\n- `window`: close/minimize/maximize/move/resize/focus/list\n\nVision\n\n- `see`: annotated UI maps, snapshot IDs, optional analysis\n\nGlobal runtime flags\n\n- `--json`/`-j`, `--verbose`/`-v`, `--log-level <level>`\n- `--no-remote`, `--bridge-socket <path>`\n\n## Quickstart (happy path)\n\n```bash\npeekaboo permissions\npeekaboo list apps --json\npeekaboo see --annotate --path /tmp/peekaboo-see.png\npeekaboo click --on B1\npeekaboo type \"Hello\" --return\n```\n\n## Common targeting parameters (most interaction commands)\n\n- App/window: `--app`, `--pid`, `--window-title`, `--window-id`, `--window-index`\n- Snapshot targeting: `--snapshot` (ID from `see`; defaults to latest)\n- Element/coords: `--on`/`--id` (element ID), `--coords x,y`\n- Focus control: `--no-auto-focus`, `--space-switch`, `--bring-to-current-space`,\n  `--focus-timeout-seconds`, `--focus-retry-count`\n\n## Common capture parameters\n\n- Output: `--path`, `--format png|jpg`, `--retina`\n- Targeting: `--mode screen|window|frontmost`, `--screen-index`,\n  `--window-title`, `--window-id`\n- Analysis: `--analyze \"prompt\"`, `--annotate`\n- Capture engine: `--capture-engine auto|classic|cg|modern|sckit`\n\n## Common motion/typing parameters\n\n- Timing: `--duration` (drag/swipe), `--steps`, `--delay` (type/scroll/press)\n- Human-ish movement: `--profile human|linear`, `--wpm` (typing)\n- Scroll: `--direction up|down|left|right`, `--amount <ticks>`, `--smooth`\n\n## Examples\n\n### See -> click -> type (most reliable flow)\n\n```bash\npeekaboo see --app Safari --window-title \"Login\" --annotate --path /tmp/see.png\npeekaboo click --on B3 --app Safari\npeekaboo type \"user@example.com\" --app Safari\npeekaboo press tab --count 1 --app Safari\npeekaboo type \"supersecret\" --app Safari --return\n```\n\n### Target by window id\n\n```bash\npeekaboo list windows --app \"Visual Studio Code\" --json\npeekaboo click --window-id 12345 --coords 120,160\npeekaboo type \"Hello from Peekaboo\" --window-id 12345\n```\n\n### Capture screenshots + analyze\n\n```bash\npeekaboo image --mode screen --screen-index 0 --retina --path /tmp/screen.png\npeekaboo image --app Safari --window-title \"Dashboard\" --analyze \"Summarize KPIs\"\npeekaboo see --mode screen --screen-index 0 --analyze \"Summarize the dashboard\"\n```\n\n### Live capture (motion-aware)\n\n```bash\npeekaboo capture live --mode region --region 100,100,800,600 --duration 30 \\\n  --active-fps 8 --idle-fps 2 --highlight-changes --path /tmp/capture\n```\n\n### App + window management\n\n```bash\npeekaboo app launch \"Safari\" --open https://example.com\npeekaboo window focus --app Safari --window-title \"Example\"\npeekaboo window set-bounds --app Safari --x 50 --y 50 --width 1200 --height 800\npeekaboo app quit --app Safari\n```\n\n### Menus, menubar, dock\n\n```bash\npeekaboo menu click --app Safari --item \"New Window\"\npeekaboo menu click --app TextEdit --path \"Format > Font > Show Fonts\"\npeekaboo menu click-extra --title \"WiFi\"\npeekaboo dock launch Safari\npeekaboo menubar list --json\n```\n\n### Mouse + gesture input\n\n```bash\npeekaboo move 500,300 --smooth\npeekaboo drag --from B1 --to T2\npeekaboo swipe --from-coords 100,500 --to-coords 100,200 --duration 800\npeekaboo scroll --direction down --amount 6 --smooth\n```\n\n### Keyboard input\n\n```bash\npeekaboo hotkey --keys \"cmd,shift,t\"\npeekaboo press escape\npeekaboo type \"Line 1\\nLine 2\" --delay 10\n```\n\nNotes\n\n- Requires Screen Recording + Accessibility permissions.\n- Use `peekaboo see --annotate` to identify targets before clicking.\n","skills/powerpoint/SKILL.md":"---\nname: powerpoint\ndescription: Create designed, editable PowerPoint .pptx presentations with PptxGenJS. Use when the user asks to create, generate, update, or inspect a deck, slide deck, presentation, or .pptx file.\n---\n\n# PowerPoint\n\nUse this skill whenever a PowerPoint deck is involved. For new decks, pass a trusted PptxGenJS build script directly to the `pptx_write` tool. For filling or editing an existing template, call `pptx_template_analyze` first and then `pptx_template_fill` with the exact IDs returned by analysis.\n\n## Workflow\n\n1. Decide the deck outline and choose a visual system: palette, typography, repeated motif, and slide rhythm.\n2. Write JavaScript module content that exports `default async function build(pptx, ctx)` or named `build(pptx, ctx)`.\n3. In the script, add slides directly with PptxGenJS. Do not generate HTML for this workflow.\n4. Call `pptx_write` with `path`, `script`, optional `assets_dir`, and optional `data`.\n5. Verify the result with `pptx_read`; for visual QA, convert the PPTX to images if the environment has LibreOffice and Poppler.\n\n## Template Workflow\n\n- Use `pptx_template_analyze` when the user provides a `.pptx` template or wants to preserve existing layouts, charts, images, tables, or SmartArt.\n- Build a `template_fill_pptx_plan.v1` plan from the returned slide IDs and object IDs, then call `pptx_template_fill`.\n- For SmartArt, use `smartarts[*].smartart_id` and `smartarts[*].nodes[*].node_id` in `smartart_edits`. This edits existing node text only; it does not create, delete, or relayout SmartArt nodes.\n\n## Script Creation\n\n- Put the complete JavaScript module in the `script` argument.\n- Do not use `local_file_write` or shell commands to create a temporary `.mjs` file for this workflow.\n- If revising a deck, update the `script` content and call `pptx_write` again.\n\n## Tool Contract\n\n```json\n{\n  \"tool\": \"pptx_write\",\n  \"arguments\": {\n    \"path\": \"deck.pptx\",\n    \"script\": \"export default async function build(pptx, ctx) {\\\\n  pptx.layout = \\\"LAYOUT_WIDE\\\";\\\\n}\",\n    \"assets_dir\": \"/absolute/path/to/assets\",\n    \"data\": {\"title\": \"Quarterly Review\"}\n  }\n}\n```\n\nThe worker creates the PptxGenJS instance and writes the output file. The script only adds slides and content.\n\n```javascript\nexport default async function build(pptx, ctx) {\n  pptx.layout = \"LAYOUT_WIDE\";\n  pptx.author = \"OpenAgent\";\n\n  const slide = pptx.addSlide();\n  slide.background = { color: \"FFFFFF\" };\n  slide.addText(\"Title\", {\n    x: 0.6, y: 0.4, w: 8, h: 0.6,\n    fontSize: 36, bold: true, color: \"1F2937\",\n    margin: 0,\n  });\n  slide.addNotes(\"speaker notes\");\n}\n```\n\n`ctx` includes:\n\n- `ctx.data`: JSON data passed from the tool call.\n- `ctx.assetsDir`: resolved asset directory.\n- `ctx.outPath`: final PPTX path.\n- `ctx.resolveAsset(\"image.png\")`: absolute path under `assets_dir`.\n- `ctx.imageData(\"image.png\")`: base64 image data URL.\n- `ctx.iconSvgData(\"check\", \"16A34A\")`: Font Awesome solid icon as SVG data.\n\n## Design Rules\n\n- Avoid plain white bullet decks. Every slide should have a visual element: shape, image, chart, icon, timeline, stat callout, or diagram.\n- Vary layouts across the deck: title, divider, two-column, card grid, process flow, quote/callout, and conclusion.\n- Pick topic-specific colors. Use one dominant color, one or two supporting tones, and one accent.\n- Use strong hierarchy: titles around 36-44 pt, section labels around 20-24 pt, body text around 14-18 pt.\n- Keep at least 0.5 inch margins and consistent gaps around 0.3-0.5 inch.\n- Use editable text wherever practical; use images for photos, screenshots, logos, or complex visual backgrounds.\n- Add speaker notes when useful; `pptx_read` can surface them later.\n\n## PptxGenJS Reference\n\nFor API patterns, chart examples, bullets, image sizing, icons, and common file-corruption pitfalls, load `pptxgenjs.md`.\n\n## Required QA\n\n- Run `pptx_read` on the generated file and check slide order, missing text, typo risk, and notes.\n- Inspect generated XML or render slides when visual precision matters.\n- Watch for overlap, text overflow, low contrast, cramped spacing, repeated layouts, and leftover placeholder text.\n- If a visual issue is found, edit the `.mjs` script and rewrite the PPTX.\n","skills/powerpoint/references/pptxgenjs.md":"# PptxGenJS Reference\n\nUse this reference when writing the JavaScript build script for the `pptx_write` tool.\n\n## Script Creation\n\nPass the complete JavaScript module in the `script` argument to `pptx_write`. Do not create a temporary `.mjs` file with `local_file_write` or shell commands for this workflow.\n\n## Basic Structure\n\n```javascript\nexport default async function build(pptx, ctx) {\n  pptx.layout = \"LAYOUT_WIDE\";\n  pptx.title = ctx.data?.title || \"Presentation\";\n\n  const slide = pptx.addSlide();\n  slide.background = { color: \"FFFFFF\" };\n  slide.addText(\"Hello\", { x: 0.6, y: 0.5, w: 8, h: 0.6, fontSize: 36 });\n}\n```\n\nUseful layouts:\n\n- `LAYOUT_WIDE`: 13.333 x 7.5 in.\n- `LAYOUT_16X9`: 10 x 5.625 in.\n- `LAYOUT_4X3`: 10 x 7.5 in.\n\nUse inches for all `x`, `y`, `w`, and `h` values.\n\n## Text\n\n```javascript\nslide.addText(\"Main title\", {\n  x: 0.6, y: 0.4, w: 8.5, h: 0.6,\n  fontFace: \"Aptos Display\",\n  fontSize: 38,\n  bold: true,\n  color: \"111827\",\n  margin: 0,\n});\n\nslide.addText([\n  { text: \"First point\", options: { bullet: true, breakLine: true } },\n  { text: \"Second point\", options: { bullet: true } },\n], {\n  x: 0.8, y: 1.4, w: 5.4, h: 1.2,\n  fontSize: 17,\n  color: \"374151\",\n  breakLine: false,\n  paraSpaceAfterPt: 8,\n});\n```\n\nRules:\n\n- Use `margin: 0` when aligning text with shapes or icons.\n- Use PptxGenJS bullets; do not type bullet glyphs into strings.\n- Use `breakLine: true` in rich text arrays when items must appear on separate lines.\n- Avoid tiny body text; most slide body copy should stay at or above 14 pt.\n\n## Shapes\n\n```javascript\nslide.addShape(pptx.ShapeType.rect, {\n  x: 0, y: 0, w: 13.333, h: 7.5,\n  fill: { color: \"F8FAFC\" },\n  line: { color: \"F8FAFC\" },\n});\n\nslide.addShape(pptx.ShapeType.roundRect, {\n  x: 0.7, y: 1.2, w: 3.5, h: 1.2,\n  rectRadius: 0.08,\n  fill: { color: \"FFFFFF\" },\n  line: { color: \"E5E7EB\", width: 1 },\n  shadow: { type: \"outer\", color: \"000000\", opacity: 0.12, blur: 2, angle: 45, distance: 1 },\n});\n\nslide.addShape(pptx.ShapeType.line, {\n  x: 0.7, y: 6.8, w: 11.8, h: 0,\n  line: { color: \"CBD5E1\", width: 1 },\n});\n```\n\nColor rules:\n\n- Hex colors must not include `#`.\n- Do not use 8-character hex for transparency. Use `transparency` or `opacity`.\n- Use a fresh options object for each shape; PptxGenJS mutates some option values internally.\n\n## Images and Icons\n\n```javascript\nslide.addImage({\n  data: ctx.imageData(\"photo.png\"),\n  x: 7.1, y: 1.0, w: 5.4, h: 3.4,\n  sizing: { type: \"cover\", x: 7.1, y: 1.0, w: 5.4, h: 3.4 },\n});\n\nslide.addImage({\n  data: ctx.iconSvgData(\"chart-line\", \"2563EB\"),\n  x: 0.8, y: 1.2, w: 0.35, h: 0.35,\n});\n```\n\nUse `ctx.resolveAsset()` when a PptxGenJS API needs a file path. Use `ctx.imageData()` when embedding local PNG/JPG/GIF/SVG assets. Use `ctx.iconSvgData()` for Font Awesome solid icons by names such as `check`, `chart-line`, `shield-halved`, or `circle-info`.\n\n## Tables and Charts\n\n```javascript\nslide.addTable([\n  [\"Metric\", \"Current\", \"Target\"],\n  [\"Activation\", \"42%\", \"55%\"],\n  [\"Retention\", \"68%\", \"74%\"],\n], {\n  x: 0.7, y: 1.5, w: 6.0, h: 1.2,\n  border: { pt: 1, color: \"E5E7EB\" },\n  fill: { color: \"FFFFFF\" },\n  color: \"111827\",\n  fontSize: 12,\n});\n\nslide.addChart(pptx.ChartType.bar, [{\n  name: \"Revenue\",\n  labels: [\"Q1\", \"Q2\", \"Q3\", \"Q4\"],\n  values: [12, 16, 21, 28],\n}], {\n  x: 0.7, y: 1.2, w: 6.2, h: 3.8,\n  barDir: \"col\",\n  chartColors: [\"2563EB\"],\n  showLegend: false,\n  showValue: true,\n  valGridLine: { color: \"E2E8F0\", size: 0.5 },\n});\n```\n\nPrefer a small set of chart colors from the deck palette. Hide legends when there is only one series.\n\n## Layout Ideas\n\n- Cover: full color block or image area with large title and short subtitle.\n- Agenda: strong sidebar plus numbered sections.\n- Two-column: explanatory text on one side, image/chart/diagram on the other.\n- Card grid: 2x2 or 3x2 cards with icon, header, and one-sentence body.\n- Timeline/process: numbered steps connected by lines or arrows.\n- Data slide: one large chart plus two or three stat callouts.\n- Closing: strong statement, next action, or summary trio.\n\n## QA Checklist\n\n- Slide order and text match the requested content.\n- Text does not collide with shapes, images, footers, or other text.\n- Long labels fit in their boxes and do not clip.\n- Contrast is readable on every background.\n- Margins are at least 0.5 in unless intentionally full bleed.\n- Layouts vary; the deck does not read as repeated title-plus-bullets.\n- Generated file opens and `pptx_read` returns the expected text.\n","skills/sag/SKILL.md":"---\nname: sag\ndescription: ElevenLabs text-to-speech with mac-style say UX.\nhomepage: https://sag.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔊\",\n        \"requires\": { \"bins\": [\"sag\"], \"env\": [\"ELEVENLABS_API_KEY\"] },\n        \"primaryEnv\": \"ELEVENLABS_API_KEY\",\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/sag\",\n              \"bins\": [\"sag\"],\n              \"label\": \"Install sag (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# sag\n\nUse `sag` for ElevenLabs TTS with local playback.\n\nAPI key (required)\n\n- `ELEVENLABS_API_KEY` (preferred)\n- `SAG_API_KEY` also supported by the CLI\n\nQuick start\n\n- `sag \"Hello there\"`\n- `sag speak -v \"Roger\" \"Hello\"`\n- `sag voices`\n- `sag prompting` (model-specific tips)\n\nModel notes\n\n- Default: `eleven_v3` (expressive)\n- Stable: `eleven_multilingual_v2`\n- Fast: `eleven_flash_v2_5`\n\nPronunciation + delivery rules\n\n- First fix: respell (e.g. \"key-note\"), add hyphens, adjust casing.\n- Numbers/units/URLs: `--normalize auto` (or `off` if it harms names).\n- Language bias: `--lang en|de|fr|...` to guide normalization.\n- v3: SSML `<break>` not supported; use `[pause]`, `[short pause]`, `[long pause]`.\n- v2/v2.5: SSML `<break time=\"1.5s\" />` supported; `<phoneme>` not exposed in `sag`.\n\nv3 audio tags (put at the entrance of a line)\n\n- `[whispers]`, `[shouts]`, `[sings]`\n- `[laughs]`, `[starts laughing]`, `[sighs]`, `[exhales]`\n- `[sarcastic]`, `[curious]`, `[excited]`, `[crying]`, `[mischievously]`\n- Example: `sag \"[whispers] keep this quiet. [short pause] ok?\"`\n\nVoice defaults\n\n- `ELEVENLABS_VOICE_ID` or `SAG_VOICE_ID`\n\nConfirm voice + speaker before long output.\n\n## Chat voice responses\n\nWhen the user asks for a \"voice\" reply (e.g., \"crazy scientist voice\", \"explain in voice\"), generate audio and send it:\n\n```bash\n# Generate audio file\nsag -v Clawd -o /tmp/voice-reply.mp3 \"Your message here\"\n\n# Then include in reply:\n# MEDIA:/tmp/voice-reply.mp3\n```\n\nVoice character tips:\n\n- Crazy scientist: Use `[excited]` tags, dramatic pauses `[short pause]`, vary intensity\n- Calm: Use `[whispers]` or slower pacing\n- Dramatic: Use `[sings]` or `[shouts]` sparingly\n\nDefault voice for Clawd: `lj2rcrvANS3gaWWnczSX` (or just `-v Clawd`)\n","skills/session-logs/SKILL.md":"---\nname: session-logs\ndescription: Search and analyze your own session logs (older/parent conversations) using jq.\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📜\",\n        \"requires\": { \"bins\": [\"jq\", \"rg\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew-jq\",\n              \"kind\": \"brew\",\n              \"formula\": \"jq\",\n              \"bins\": [\"jq\"],\n              \"label\": \"Install jq (brew)\",\n            },\n            {\n              \"id\": \"brew-rg\",\n              \"kind\": \"brew\",\n              \"formula\": \"ripgrep\",\n              \"bins\": [\"rg\"],\n              \"label\": \"Install ripgrep (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# session-logs\n\nSearch your complete conversation history stored in session JSONL files. Use this when a user references older/parent conversations or asks what was said before.\n\n## Trigger\n\nUse this skill when the user asks about prior chats, parent conversations, or historical context that isn't in memory files.\n\n## Location\n\nSession logs live under the active state directory:\n`$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/` (default: `~/.openclaw/agents/<agentId>/sessions/`).\nUse the `agent=<id>` value from the system prompt Runtime line.\n\n- **`sessions.json`** - Index mapping session keys to session IDs\n- **`<session-id>.jsonl`** - Full conversation transcript per session\n\n## Structure\n\nEach `.jsonl` file contains messages with:\n\n- `type`: \"session\" (metadata) or \"message\"\n- `timestamp`: ISO timestamp\n- `message.role`: \"user\", \"assistant\", or \"toolResult\"\n- `message.content[]`: Text, thinking, or tool calls (filter `type==\"text\"` for human-readable content)\n- `message.usage.cost.total`: Cost per response\n\n## Common Queries\n\n### List all sessions by date and size\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nfor f in \"$SESSION_DIR\"/*.jsonl; do\n  date=$(head -1 \"$f\" | jq -r '.timestamp' | cut -dT -f1)\n  size=$(ls -lh \"$f\" | awk '{print $5}')\n  echo \"$date $size $(basename $f)\"\ndone | sort -r\n```\n\n### Find sessions from a specific day\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nfor f in \"$SESSION_DIR\"/*.jsonl; do\n  head -1 \"$f\" | jq -r '.timestamp' | grep -q \"2026-01-06\" && echo \"$f\"\ndone\n```\n\n### Extract user messages from a session\n\n```bash\njq -r 'select(.message.role == \"user\") | .message.content[]? | select(.type == \"text\") | .text' <session>.jsonl\n```\n\n### Search for keyword in assistant responses\n\n```bash\njq -r 'select(.message.role == \"assistant\") | .message.content[]? | select(.type == \"text\") | .text' <session>.jsonl | rg -i \"keyword\"\n```\n\n### Get total cost for a session\n\n```bash\njq -s '[.[] | .message.usage.cost.total // 0] | add' <session>.jsonl\n```\n\n### Daily cost summary\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nfor f in \"$SESSION_DIR\"/*.jsonl; do\n  date=$(head -1 \"$f\" | jq -r '.timestamp' | cut -dT -f1)\n  cost=$(jq -s '[.[] | .message.usage.cost.total // 0] | add' \"$f\")\n  echo \"$date $cost\"\ndone | awk '{a[$1]+=$2} END {for(d in a) print d, \"$\"a[d]}' | sort -r\n```\n\n### Count messages and tokens in a session\n\n```bash\njq -s '{\n  messages: length,\n  user: [.[] | select(.message.role == \"user\")] | length,\n  assistant: [.[] | select(.message.role == \"assistant\")] | length,\n  first: .[0].timestamp,\n  last: .[-1].timestamp\n}' <session>.jsonl\n```\n\n### Tool usage breakdown\n\n```bash\njq -r '.message.content[]? | select(.type == \"toolCall\") | .name' <session>.jsonl | sort | uniq -c | sort -rn\n```\n\n### Search across ALL sessions for a phrase\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nrg -l \"phrase\" \"$SESSION_DIR\"/*.jsonl\n```\n\n## Tips\n\n- Sessions are append-only JSONL (one JSON object per line)\n- Large sessions can be several MB - use `head`/`tail` for sampling\n- The `sessions.json` index maps chat providers (discord, whatsapp, etc.) to session IDs\n- Deleted sessions have `.deleted.<timestamp>` suffix\n\n## Fast text-only hint (low noise)\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\njq -r 'select(.type==\"message\") | .message.content[]? | select(.type==\"text\") | .text' \"$SESSION_DIR\"/<id>.jsonl | rg 'keyword'\n```\n","skills/sherpa-onnx-tts/SKILL.md":"---\nname: sherpa-onnx-tts\ndescription: Local text-to-speech via sherpa-onnx (offline, no cloud)\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔉\",\n        \"os\": [\"darwin\", \"linux\", \"win32\"],\n        \"requires\": { \"env\": [\"SHERPA_ONNX_RUNTIME_DIR\", \"SHERPA_ONNX_MODEL_DIR\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"download-runtime-macos\",\n              \"kind\": \"download\",\n              \"os\": [\"darwin\"],\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-osx-universal2-shared.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"stripComponents\": 1,\n              \"targetDir\": \"runtime\",\n              \"label\": \"Download sherpa-onnx runtime (macOS)\",\n            },\n            {\n              \"id\": \"download-runtime-linux-x64\",\n              \"kind\": \"download\",\n              \"os\": [\"linux\"],\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-linux-x64-shared.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"stripComponents\": 1,\n              \"targetDir\": \"runtime\",\n              \"label\": \"Download sherpa-onnx runtime (Linux x64)\",\n            },\n            {\n              \"id\": \"download-runtime-win-x64\",\n              \"kind\": \"download\",\n              \"os\": [\"win32\"],\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-win-x64-shared.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"stripComponents\": 1,\n              \"targetDir\": \"runtime\",\n              \"label\": \"Download sherpa-onnx runtime (Windows x64)\",\n            },\n            {\n              \"id\": \"download-model-lessac\",\n              \"kind\": \"download\",\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-en_US-lessac-high.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"targetDir\": \"models\",\n              \"label\": \"Download Piper en_US lessac (high)\",\n            },\n          ],\n      },\n  }\n---\n\n# sherpa-onnx-tts\n\nLocal TTS using the sherpa-onnx offline CLI.\n\n## Install\n\n1. Download the runtime for your OS (extracts into `$OPENCLAW_STATE_DIR/tools/sherpa-onnx-tts/runtime`, default `~/.openclaw/tools/sherpa-onnx-tts/runtime`)\n2. Download a voice model (extracts into `$OPENCLAW_STATE_DIR/tools/sherpa-onnx-tts/models`, default `~/.openclaw/tools/sherpa-onnx-tts/models`)\n\nResolve the active state directory first:\n\n```bash\nSTATE_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}\"\n```\n\nThen write those resolved paths into the active OpenClaw config file (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`):\n\n```json5\n{\n  skills: {\n    entries: {\n      \"sherpa-onnx-tts\": {\n        env: {\n          SHERPA_ONNX_RUNTIME_DIR: \"/path/to/your/state-dir/tools/sherpa-onnx-tts/runtime\",\n          SHERPA_ONNX_MODEL_DIR: \"/path/to/your/state-dir/tools/sherpa-onnx-tts/models/vits-piper-en_US-lessac-high\",\n        },\n      },\n    },\n  },\n}\n```\n\nThe wrapper lives in this skill folder. Run it directly, or add the wrapper to PATH:\n\n```bash\nexport PATH=\"{baseDir}/bin:$PATH\"\n```\n\n## Usage\n\n```bash\n{baseDir}/bin/sherpa-onnx-tts -o ./tts.wav \"Hello from local TTS.\"\n```\n\nNotes:\n\n- Pick a different model from the sherpa-onnx `tts-models` release if you want another voice.\n- If the model dir has multiple `.onnx` files, set `SHERPA_ONNX_MODEL_FILE` or pass `--model-file`.\n- You can also pass `--tokens-file` or `--data-dir` to override the defaults.\n- Windows: run `node {baseDir}\\\\bin\\\\sherpa-onnx-tts -o tts.wav \"Hello from local TTS.\"`\n","skills/skill-creator/SKILL.md":"---\nname: skill-creator\ndescription: Create, edit, improve, or audit AgentSkills. Use when creating a new skill from scratch or when asked to improve, review, audit, tidy up, or clean up an existing skill or SKILL.md file. Also use when editing or restructuring a skill directory (moving files to references/ or scripts/, removing stale content, validating against the AgentSkills spec). Triggers on phrases like \"create a skill\", \"author a skill\", \"tidy up a skill\", \"improve this skill\", \"review the skill\", \"clean up the skill\", \"audit the skill\".\n---\n\n# Skill Creator\n\nThis skill provides guidance for creating effective skills.\n\n## About Skills\n\nSkills are modular, self-contained packages that extend Codex's capabilities by providing\nspecialized knowledge, workflows, and tools. Think of them as \"onboarding guides\" for specific\ndomains or tasks—they transform Codex from a general-purpose agent into a specialized agent\nequipped with procedural knowledge that no model can fully possess.\n\n### What Skills Provide\n\n1. Specialized workflows - Multi-step procedures for specific domains\n2. Tool integrations - Instructions for working with specific file formats or APIs\n3. Domain expertise - Company-specific knowledge, schemas, business logic\n4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks\n\n## Core Principles\n\n### Concise is Key\n\nThe context window is a public good. Skills share the context window with everything else Codex needs: system prompt, conversation history, other Skills' metadata, and the actual user request.\n\n**Default assumption: Codex is already very smart.** Only add context Codex doesn't already have. Challenge each piece of information: \"Does Codex really need this explanation?\" and \"Does this paragraph justify its token cost?\"\n\nPrefer concise examples over verbose explanations.\n\n### Set Appropriate Degrees of Freedom\n\nMatch the level of specificity to the task's fragility and variability:\n\n**High freedom (text-based instructions)**: Use when multiple approaches are valid, decisions depend on context, or heuristics guide the approach.\n\n**Medium freedom (pseudocode or scripts with parameters)**: Use when a preferred pattern exists, some variation is acceptable, or configuration affects behavior.\n\n**Low freedom (specific scripts, few parameters)**: Use when operations are fragile and error-prone, consistency is critical, or a specific sequence must be followed.\n\nThink of Codex as exploring a path: a narrow bridge with cliffs needs specific guardrails (low freedom), while an open field allows many routes (high freedom).\n\n### Anatomy of a Skill\n\nEvery skill consists of a required SKILL.md file and optional bundled resources:\n\n```\nskill-name/\n├── SKILL.md (required)\n│   ├── YAML frontmatter metadata (required)\n│   │   ├── name: (required)\n│   │   └── description: (required)\n│   └── Markdown instructions (required)\n└── Bundled Resources (optional)\n    ├── scripts/          - Executable code (Python/Bash/etc.)\n    ├── references/       - Documentation intended to be loaded into context as needed\n    └── assets/           - Files used in output (templates, icons, fonts, etc.)\n```\n\n#### SKILL.md (required)\n\nEvery SKILL.md consists of:\n\n- **Frontmatter** (YAML): Contains `name` and `description` fields. These are the only fields that Codex reads to determine when the skill gets used, thus it is very important to be clear and comprehensive in describing what the skill is, and when it should be used.\n- **Body** (Markdown): Instructions and guidance for using the skill. Only loaded AFTER the skill triggers (if at all).\n\n#### Bundled Resources (optional)\n\n##### Scripts (`scripts/`)\n\nExecutable code (Python/Bash/etc.) for tasks that require deterministic reliability or are repeatedly rewritten.\n\n- **When to include**: When the same code is being rewritten repeatedly or deterministic reliability is needed\n- **Example**: `scripts/rotate_pdf.py` for PDF rotation tasks\n- **Benefits**: Token efficient, deterministic, may be executed without loading into context\n- **Note**: Scripts may still need to be read by Codex for patching or environment-specific adjustments\n\n##### References (`references/`)\n\nDocumentation and reference material intended to be loaded as needed into context to inform Codex's process and thinking.\n\n- **When to include**: For documentation that Codex should reference while working\n- **Examples**: `references/finance.md` for financial schemas, `references/mnda.md` for company NDA template, `references/policies.md` for company policies, `references/api_docs.md` for API specifications\n- **Use cases**: Database schemas, API documentation, domain knowledge, company policies, detailed workflow guides\n- **Benefits**: Keeps SKILL.md lean, loaded only when Codex determines it's needed\n- **Best practice**: If files are large (>10k words), include grep search patterns in SKILL.md\n- **Avoid duplication**: Information should live in either SKILL.md or references files, not both. Prefer references files for detailed information unless it's truly core to the skill—this keeps SKILL.md lean while making information discoverable without hogging the context window. Keep only essential procedural instructions and workflow guidance in SKILL.md; move detailed reference material, schemas, and examples to references files.\n\n##### Assets (`assets/`)\n\nFiles not intended to be loaded into context, but rather used within the output Codex produces.\n\n- **When to include**: When the skill needs files that will be used in the final output\n- **Examples**: `assets/logo.png` for brand assets, `assets/slides.pptx` for PowerPoint templates, `assets/frontend-template/` for HTML/React boilerplate, `assets/font.ttf` for typography\n- **Use cases**: Templates, images, icons, boilerplate code, fonts, sample documents that get copied or modified\n- **Benefits**: Separates output resources from documentation, enables Codex to use files without loading them into context\n\n#### What to Not Include in a Skill\n\nA skill should only contain essential files that directly support its functionality. Do NOT create extraneous documentation or auxiliary files, including:\n\n- README.md\n- INSTALLATION_GUIDE.md\n- QUICK_REFERENCE.md\n- CHANGELOG.md\n- etc.\n\nThe skill should only contain the information needed for an AI agent to do the job at hand. It should not contain auxiliary context about the process that went into creating it, setup and testing procedures, user-facing documentation, etc. Creating additional documentation files just adds clutter and confusion.\n\n### Progressive Disclosure Design Principle\n\nSkills use a three-level loading system to manage context efficiently:\n\n1. **Metadata (name + description)** - Always in context (~100 words)\n2. **SKILL.md body** - When skill triggers (<5k words)\n3. **Bundled resources** - As needed by Codex (Unlimited because scripts can be executed without reading into context window)\n\n#### Progressive Disclosure Patterns\n\nKeep SKILL.md body to the essentials and under 500 lines to minimize context bloat. Split content into separate files when approaching this limit. When splitting out content into other files, it is very important to reference them from SKILL.md and describe clearly when to read them, to ensure the reader of the skill knows they exist and when to use them.\n\n**Key principle:** When a skill supports multiple variations, frameworks, or options, keep only the core workflow and selection guidance in SKILL.md. Move variant-specific details (patterns, examples, configuration) into separate reference files.\n\n**Pattern 1: High-level guide with references**\n\n```markdown\n# PDF Processing\n\n## Quick start\n\nExtract text with pdfplumber:\n[code example]\n\n## Advanced features\n\n- **Form filling**: See [FORMS.md](FORMS.md) for complete guide\n- **API reference**: See [REFERENCE.md](REFERENCE.md) for all methods\n- **Examples**: See [EXAMPLES.md](EXAMPLES.md) for common patterns\n```\n\nCodex loads FORMS.md, REFERENCE.md, or EXAMPLES.md only when needed.\n\n**Pattern 2: Domain-specific organization**\n\nFor Skills with multiple domains, organize content by domain to avoid loading irrelevant context:\n\n```\nbigquery-skill/\n├── SKILL.md (overview and navigation)\n└── reference/\n    ├── finance.md (revenue, billing metrics)\n    ├── sales.md (opportunities, pipeline)\n    ├── product.md (API usage, features)\n    └── marketing.md (campaigns, attribution)\n```\n\nWhen a user asks about sales metrics, Codex only reads sales.md.\n\nSimilarly, for skills supporting multiple frameworks or variants, organize by variant:\n\n```\ncloud-deploy/\n├── SKILL.md (workflow + provider selection)\n└── references/\n    ├── aws.md (AWS deployment patterns)\n    ├── gcp.md (GCP deployment patterns)\n    └── azure.md (Azure deployment patterns)\n```\n\nWhen the user chooses AWS, Codex only reads aws.md.\n\n**Pattern 3: Conditional details**\n\nShow basic content, link to advanced content:\n\n```markdown\n# DOCX Processing\n\n## Creating documents\n\nUse docx-js for new documents. See [DOCX-JS.md](DOCX-JS.md).\n\n## Editing documents\n\nFor simple edits, modify the XML directly.\n\n**For tracked changes**: See [REDLINING.md](REDLINING.md)\n**For OOXML details**: See [OOXML.md](OOXML.md)\n```\n\nCodex reads REDLINING.md or OOXML.md only when the user needs those features.\n\n**Important guidelines:**\n\n- **Avoid deeply nested references** - Keep references one level deep from SKILL.md. All reference files should link directly from SKILL.md.\n- **Structure longer reference files** - For files longer than 100 lines, include a table of contents at the top so Codex can see the full scope when previewing.\n\n## Skill Creation Process\n\nSkill creation involves these steps:\n\n1. Understand the skill with concrete examples\n2. Plan reusable skill contents (scripts, references, assets)\n3. Initialize the skill (run init_skill.py)\n4. Edit the skill (implement resources and write SKILL.md)\n5. Package the skill (run package_skill.py)\n6. Iterate based on real usage\n\nFollow these steps in order, skipping only if there is a clear reason why they are not applicable.\n\n### Skill Naming\n\n- Use lowercase letters, digits, and hyphens only; normalize user-provided titles to hyphen-case (e.g., \"Plan Mode\" -> `plan-mode`).\n- When generating names, generate a name under 64 characters (letters, digits, hyphens).\n- Prefer short, verb-led phrases that describe the action.\n- Namespace by tool when it improves clarity or triggering (e.g., `gh-address-comments`, `linear-address-issue`).\n- Name the skill folder exactly after the skill name.\n\n### Step 1: Understanding the Skill with Concrete Examples\n\nSkip this step only when the skill's usage patterns are already clearly understood. It remains valuable even when working with an existing skill.\n\nTo create an effective skill, clearly understand concrete examples of how the skill will be used. This understanding can come from either direct user examples or generated examples that are validated with user feedback.\n\nFor example, when building an image-editor skill, relevant questions include:\n\n- \"What functionality should the image-editor skill support? Editing, rotating, anything else?\"\n- \"Can you give some examples of how this skill would be used?\"\n- \"I can imagine users asking for things like 'Remove the red-eye from this image' or 'Rotate this image'. Are there other ways you imagine this skill being used?\"\n- \"What would a user say that should trigger this skill?\"\n\nTo avoid overwhelming users, avoid asking too many questions in a single message. Start with the most important questions and follow up as needed for better effectiveness.\n\nConclude this step when there is a clear sense of the functionality the skill should support.\n\n### Step 2: Planning the Reusable Skill Contents\n\nTo turn concrete examples into an effective skill, analyze each example by:\n\n1. Considering how to execute on the example from scratch\n2. Identifying what scripts, references, and assets would be helpful when executing these workflows repeatedly\n\nExample: When building a `pdf-editor` skill to handle queries like \"Help me rotate this PDF,\" the analysis shows:\n\n1. Rotating a PDF requires re-writing the same code each time\n2. A `scripts/rotate_pdf.py` script would be helpful to store in the skill\n\nExample: When designing a `frontend-webapp-builder` skill for queries like \"Build me a todo app\" or \"Build me a dashboard to track my steps,\" the analysis shows:\n\n1. Writing a frontend webapp requires the same boilerplate HTML/React each time\n2. An `assets/hello-world/` template containing the boilerplate HTML/React project files would be helpful to store in the skill\n\nExample: When building a `big-query` skill to handle queries like \"How many users have logged in today?\" the analysis shows:\n\n1. Querying BigQuery requires re-discovering the table schemas and relationships each time\n2. A `references/schema.md` file documenting the table schemas would be helpful to store in the skill\n\nTo establish the skill's contents, analyze each concrete example to create a list of the reusable resources to include: scripts, references, and assets.\n\n### Step 3: Initializing the Skill\n\nAt this point, it is time to actually create the skill.\n\nSkip this step only if the skill being developed already exists, and iteration or packaging is needed. In this case, continue to the next step.\n\nWhen creating a new skill from scratch, always run the `init_skill.py` script. The script conveniently generates a new template skill directory that automatically includes everything a skill requires, making the skill creation process much more efficient and reliable.\n\nUsage:\n\n```bash\nscripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples]\n```\n\nExamples:\n\n```bash\nscripts/init_skill.py my-skill --path skills/public\nscripts/init_skill.py my-skill --path skills/public --resources scripts,references\nscripts/init_skill.py my-skill --path skills/public --resources scripts --examples\n```\n\nThe script:\n\n- Creates the skill directory at the specified path\n- Generates a SKILL.md template with proper frontmatter and TODO placeholders\n- Optionally creates resource directories based on `--resources`\n- Optionally adds example files when `--examples` is set\n\nAfter initialization, customize the SKILL.md and add resources as needed. If you used `--examples`, replace or delete placeholder files.\n\n### Step 4: Edit the Skill\n\nWhen editing the (newly-generated or existing) skill, remember that the skill is being created for another instance of Codex to use. Include information that would be beneficial and non-obvious to Codex. Consider what procedural knowledge, domain-specific details, or reusable assets would help another Codex instance execute these tasks more effectively.\n\n#### Learn Proven Design Patterns\n\nConsult these helpful guides based on your skill's needs:\n\n- **Multi-step processes**: See references/workflows.md for sequential workflows and conditional logic\n- **Specific output formats or quality standards**: See references/output-patterns.md for template and example patterns\n\nThese files contain established best practices for effective skill design.\n\n#### Start with Reusable Skill Contents\n\nTo begin implementation, start with the reusable resources identified above: `scripts/`, `references/`, and `assets/` files. Note that this step may require user input. For example, when implementing a `brand-guidelines` skill, the user may need to provide brand assets or templates to store in `assets/`, or documentation to store in `references/`.\n\nAdded scripts must be tested by actually running them to ensure there are no bugs and that the output matches what is expected. If there are many similar scripts, only a representative sample needs to be tested to ensure confidence that they all work while balancing time to completion.\n\nIf you used `--examples`, delete any placeholder files that are not needed for the skill. Only create resource directories that are actually required.\n\n#### Update SKILL.md\n\n**Writing Guidelines:** Always use imperative/infinitive form.\n\n##### Frontmatter\n\nWrite the YAML frontmatter with `name` and `description`:\n\n- `name`: The skill name\n- `description`: This is the primary triggering mechanism for your skill, and helps Codex understand when to use the skill.\n  - Include both what the Skill does and specific triggers/contexts for when to use it.\n  - Include all \"when to use\" information here - Not in the body. The body is only loaded after triggering, so \"When to Use This Skill\" sections in the body are not helpful to Codex.\n  - Example description for a `docx` skill: \"Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. Use when Codex needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks\"\n\nDo not include any other fields in YAML frontmatter.\n\n##### Body\n\nWrite instructions for using the skill and its bundled resources.\n\n### Step 5: Packaging a Skill\n\nOnce development of the skill is complete, it must be packaged into a distributable .skill file that gets shared with the user. The packaging process automatically validates the skill first to ensure it meets all requirements:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder>\n```\n\nOptional output directory specification:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder> ./dist\n```\n\nThe packaging script will:\n\n1. **Validate** the skill automatically, checking:\n   - YAML frontmatter format and required fields\n   - Skill naming conventions and directory structure\n   - Description completeness and quality\n   - File organization and resource references\n\n2. **Package** the skill if validation passes, creating a .skill file named after the skill (e.g., `my-skill.skill`) that includes all files and maintains the proper directory structure for distribution. The .skill file is a zip file with a .skill extension.\n\n   Security restriction: symlinks are rejected and packaging fails when any symlink is present.\n\nIf validation fails, the script will report the errors and exit without creating a package. Fix any validation errors and run the packaging command again.\n\n### Step 6: Iterate\n\nAfter testing the skill, users may request improvements. Often this happens right after using the skill, with fresh context of how the skill performed.\n\n**Iteration workflow:**\n\n1. Use the skill on real tasks\n2. Notice struggles or inefficiencies\n3. Identify how SKILL.md or bundled resources should be updated\n4. Implement changes and test again\n","skills/slack/SKILL.md":"---\nname: slack\ndescription: Use when you need to control Slack from OpenClaw via the slack tool, including reacting to messages or pinning/unpinning items in Slack channels or DMs.\nmetadata: { \"openclaw\": { \"emoji\": \"💬\", \"requires\": { \"config\": [\"channels.slack\"] } } }\n---\n\n# Slack Actions\n\n## Overview\n\nUse `slack` to react, manage pins, send/edit/delete messages, and fetch member info. The tool uses the bot token configured for OpenClaw.\n\n## Inputs to collect\n\n- `channelId` and `messageId` (Slack message timestamp, e.g. `1712023032.1234`).\n- For reactions, an `emoji` (Unicode or `:name:`).\n- For message sends, a `to` target (`channel:<id>` or `user:<id>`) and `content`.\n\nMessage context lines include `slack message id` and `channel` fields you can reuse directly.\n\n## Actions\n\n### Action groups\n\n| Action group | Default | Notes                  |\n| ------------ | ------- | ---------------------- |\n| reactions    | enabled | React + list reactions |\n| messages     | enabled | Read/send/edit/delete  |\n| pins         | enabled | Pin/unpin/list         |\n| memberInfo   | enabled | Member info            |\n| emojiList    | enabled | Custom emoji list      |\n\n### React to a message\n\n```json\n{\n  \"action\": \"react\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\",\n  \"emoji\": \"✅\"\n}\n```\n\n### List reactions\n\n```json\n{\n  \"action\": \"reactions\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### Send a message\n\n```json\n{\n  \"action\": \"sendMessage\",\n  \"to\": \"channel:C123\",\n  \"content\": \"Hello from OpenClaw\"\n}\n```\n\n### Edit a message\n\n```json\n{\n  \"action\": \"editMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\",\n  \"content\": \"Updated text\"\n}\n```\n\n### Delete a message\n\n```json\n{\n  \"action\": \"deleteMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### Read recent messages\n\n```json\n{\n  \"action\": \"readMessages\",\n  \"channelId\": \"C123\",\n  \"limit\": 20\n}\n```\n\n### Pin a message\n\n```json\n{\n  \"action\": \"pinMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### Unpin a message\n\n```json\n{\n  \"action\": \"unpinMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### List pinned items\n\n```json\n{\n  \"action\": \"listPins\",\n  \"channelId\": \"C123\"\n}\n```\n\n### Member info\n\n```json\n{\n  \"action\": \"memberInfo\",\n  \"userId\": \"U123\"\n}\n```\n\n### Emoji list\n\n```json\n{\n  \"action\": \"emojiList\"\n}\n```\n\n## Ideas to try\n\n- React with ✅ to mark completed tasks.\n- Pin key decisions or weekly status updates.\n","skills/songsee/SKILL.md":"---\nname: songsee\ndescription: Generate spectrograms and feature-panel visualizations from audio with the songsee CLI.\nhomepage: https://github.com/steipete/songsee\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🌊\",\n        \"requires\": { \"bins\": [\"songsee\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/songsee\",\n              \"bins\": [\"songsee\"],\n              \"label\": \"Install songsee (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# songsee\n\nGenerate spectrograms + feature panels from audio.\n\nQuick start\n\n- Spectrogram: `songsee track.mp3`\n- Multi-panel: `songsee track.mp3 --viz spectrogram,mel,chroma,hpss,selfsim,loudness,tempogram,mfcc,flux`\n- Time slice: `songsee track.mp3 --start 12.5 --duration 8 -o slice.jpg`\n- Stdin: `cat track.mp3 | songsee - --format png -o out.png`\n\nCommon flags\n\n- `--viz` list (repeatable or comma-separated)\n- `--style` palette (classic, magma, inferno, viridis, gray)\n- `--width` / `--height` output size\n- `--window` / `--hop` FFT settings\n- `--min-freq` / `--max-freq` frequency range\n- `--start` / `--duration` time slice\n- `--format` jpg|png\n\nNotes\n\n- WAV/MP3 decode native; other formats use ffmpeg if available.\n- Multiple `--viz` renders a grid.\n","skills/sonoscli/SKILL.md":"---\nname: sonoscli\ndescription: Control Sonos speakers (discover/status/play/volume/group).\nhomepage: https://sonoscli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔊\",\n        \"requires\": { \"bins\": [\"sonos\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/sonoscli/cmd/sonos@latest\",\n              \"bins\": [\"sonos\"],\n              \"label\": \"Install sonoscli (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# Sonos CLI\n\nUse `sonos` to control Sonos speakers on the local network.\n\n## Quick start\n\n- `sonos discover`\n- `sonos status --name \"Kitchen\"`\n- `sonos play|pause|stop --name \"Kitchen\"`\n- `sonos volume set 15 --name \"Kitchen\"`\n\n## Common tasks\n\n- Grouping: `sonos group status|join|unjoin|party|solo`\n- Favorites: `sonos favorites list|open`\n- Queue: `sonos queue list|play|clear`\n- Spotify search (via SMAPI): `sonos smapi search --service \"Spotify\" --category tracks \"query\"`\n\n## Notes\n\n- If SSDP fails, specify `--ip <speaker-ip>`.\n- Spotify Web API search is optional and requires `SPOTIFY_CLIENT_ID/SECRET`.\n- If there is an error, check the troubleshooting section and offer advice if there is a decent match.\n\n## Troubleshooting\n\n### `sonos discover` - `no route to host`\n\n- On error `Error: write udp4 0.0.0.0:64326->239.255.255.250:1900: sendto: no route to host (Command exited with code 1)`\n  - Note that the port number after `0.0.0.0:` is ephemeral and will change and the netmask may not match exactly either\n  - The `sendto: no route to host` should stay consistent\n- Advise the user that in `direct` (no Docker sandbox) mode on Mac OS that Settings -> Privacy & Security -> Local Network will need to be enabled for the top-level host parent process of the Gateway\n  - `node` if running via `launchd`\n  - `Terminal` if running the gateway directly in the terminal\n  - `Visual Studio Code` if running via terminal in VS Code\n- An alternative option is to use `sandbox` (docker container) with network access allowed for that sandbox\n\n### `sonos discover` - `bind: operation not permitted`\n\n- On error `Error: listen udp4 0.0.0.0:0: bind: operation not permitted`\n- Advise the user that they may be running via a Codex or other sandbox that does not permit network access (this can be replicated by running `sonos discover` within a Codex CLI session with sandbox enabled and not approving the escalation request)\n","skills/spotify-player/SKILL.md":"---\nname: spotify-player\ndescription: Terminal Spotify playback/search via spogo (preferred) or spotify_player.\nhomepage: https://www.spotify.com\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🎵\",\n        \"requires\": { \"anyBins\": [\"spogo\", \"spotify_player\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"spogo\",\n              \"tap\": \"steipete/tap\",\n              \"bins\": [\"spogo\"],\n              \"label\": \"Install spogo (brew)\",\n            },\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"spotify_player\",\n              \"bins\": [\"spotify_player\"],\n              \"label\": \"Install spotify_player (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# spogo / spotify_player\n\nUse `spogo` **(preferred)** for Spotify playback/search. Fall back to `spotify_player` if needed.\n\nRequirements\n\n- Spotify Premium account.\n- Either `spogo` or `spotify_player` installed.\n\nspogo setup\n\n- Import cookies: `spogo auth import --browser chrome`\n\nCommon CLI commands\n\n- Search: `spogo search track \"query\"`\n- Playback: `spogo play|pause|next|prev`\n- Devices: `spogo device list`, `spogo device set \"<name|id>\"`\n- Status: `spogo status`\n\nspotify_player commands (fallback)\n\n- Search: `spotify_player search \"query\"`\n- Playback: `spotify_player playback play|pause|next|previous`\n- Connect device: `spotify_player connect`\n- Like track: `spotify_player like`\n\nNotes\n\n- Config folder: `~/.config/spotify-player` (e.g., `app.toml`).\n- For Spotify Connect integration, set a user `client_id` in config.\n- TUI shortcuts are available via `?` in the app.\n","skills/summarize/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fsummarize%2FSKILL.md -->","skills/taskflow-inbox-triage/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftaskflow-inbox-triage%2FSKILL.md -->","skills/taskflow/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftaskflow%2FSKILL.md -->","skills/things-mac/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fthings-mac%2FSKILL.md -->","skills/tmux/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftmux%2FSKILL.md -->","skills/trello/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftrello%2FSKILL.md -->","skills/video-frames/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fvideo-frames%2FSKILL.md -->","skills/voice-call/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fvoice-call%2FSKILL.md -->","skills/wacli/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fwacli%2FSKILL.md -->","skills/weather/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fweather%2FSKILL.md -->","skills/xurl/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fxurl%2FSKILL.md -->"},"files":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Commands\n\n**Backend (Go)**\n```bash\ngo build                    # build backend binary\ngo run main.go              # run backend (port 14000)\ngo test ./...               # run all tests\ngo test ./object/... -run TestFoo  # run a single test\n```\n\n**Frontend (React + shadcn/ui, built with Vite)**\n```bash\ncd web && yarn install\nyarn start                  # dev server on port 13001, proxies /api to localhost:14000\nyarn build                  # production build (vite writes build-temp, mv.js swaps it into web/build)\nyarn typecheck              # tsc --noEmit\nyarn lint                   # eslint\n```\n\n`web-old/` holds the previous Ant Design frontend. It is kept for reference only\nwhile bugs are chased down against it — it is not built, served or linted, and it\ndoes not get new work.\n\n**Full production build** uses `build.sh` which cross-compiles for linux/amd64, linux/arm64, linux/riscv64.\n\nBackend serves the frontend as embedded static files (`embed.go`); during development the frontend proxy is configured to hit `localhost:14000`.\n\n## Architecture\n\n### Backend (Go / Beego)\n\n**Module:** `github.com/the-open-agent/openagent`\n\nThe backend is a standard [Beego](https://beego.vip/) MVC app. Every entity follows the same three-layer pattern:\n\n1. **`object/<entity>.go`** — struct definition (xorm tags for DB), and all DB access functions: `GetGlobal<Entities>`, `Get<Entity>Count`, `GetPagination<Entities>`, `Get<Entity>`, `Add<Entity>`, `Update<Entity>`, `Delete<Entity>`. The primary key is always `(Owner string, Name string)`. Use `util.GetId(owner, name)` / `util.GetOwnerAndNameFromIdWithError(id)` for composite key serialization.\n\n2. **`controllers/<entity>.go`** — Beego controller methods wired to routes. Standard set: `GetGlobal<Entities>`, `Get<Entities>`, `Get<Entity>`, `Add<Entity>`, `Update<Entity>`, `Delete<Entity>`. Use `c.IsAdmin()` / `c.IsGlobalAdmin()` / `c.RequireSignedIn()` for auth. Paginated list APIs accept `p`, `pageSize`, `field`, `value`, `sortField`, `sortOrder` query params and use `pagination.SetPaginator`.\n\n3. **`routers/router.go`** — `beego.Router` calls grouped by entity (all routes for the same entity's CRUD kept together). No alphabetical ordering within a group. New entity routes should be inserted next to their entity's existing routes (not appended to the end of the file).\n\n**DB schema** is auto-migrated via `object/adapter.go` → `createTable()` using `engine.Sync2(new(EntityStruct))`. Add new entities there.\n\n**i18n (backend):** `i18n/locales/{en,zh}/data.json` — keys are namespaced by category (e.g. `\"comment:...\"`, `\"general:...\"`). Use `c.T(\"namespace:key\")` in controllers.\n\n### Frontend (React + shadcn/ui on Tailwind, TypeScript, Vite)\n\n**Routing** lives in `web/src/App.tsx`; every file in `web/src/pages` is its own lazy chunk, loaded by name. To add a new admin page:\n1. Create `web/src/pages/<Name>Page.tsx` (default export).\n2. Add a `<Route>` in `App.tsx` pointing at `page(\"<Name>Page\")`.\n3. Add a menu entry in `getNavGroups()` (`web/src/lib/nav.ts`), in the correct group — Basic / Connectors / Admin etc.\n4. Add the nav key to `web/src/components/common/NavItemTree.tsx` so it appears in the site Navbar Items config.\n\n**Page pattern:** list pages render `CrudListPage` (`web/src/components/crud/`) with a `ColumnDef[]`; edit pages render `SimpleEditPage` with an `EditField[]`, or compose `EditPageShell` directly when the layout is custom. Both patterns are illustrated by `MessageListPage.tsx` / `MessageEditPage.tsx`.\n\n**Backend API layer:** `web/src/backend/<Entity>Backend.ts` — thin `fetch()` wrappers, one file per entity. Standard exports: `getGlobal<Entities>`, `get<Entity>`, `update<Entity>`, `add<Entity>`, `delete<Entity>`.\n\n**Per-instance config:** the backend sends the Casdoor issuer, client ID and branding in the `jsonWebConfig` cookie (set with index.html and with every `/api/get-account` response) and `web/src/Conf.ts` reads it at boot, so one build serves every instance.\n\n**i18n (frontend):** `web/src/locales/{en,zh}/data.json` namespaced by page/domain. Use `i18next.t(\"namespace:key\")`.\n\n### Key conventions\n\n- Owner is always `\"admin\"` for system-created entities.\n- Entity `Name` is a random string generated with `util.GetRandomString(n)` or a user-provided slug.\n- `CreatedTime` / `UpdatedTime` use `util.GetCurrentTimeWithMilli()` (string format).\n- Delete responses use `affected != 0` boolean pattern.\n- All API responses go through `c.ResponseOk(data)` or `c.ResponseError(msg)`.\n- The `conf/app.conf` file configures DB (`driverName`, `dataSourceName`, `dbName`) and port (`httpport = 14000`).\n","skills/1password/SKILL.md":"---\nname: 1password\ndescription: 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.\nhomepage: https://developer.1password.com/docs/cli/get-started/\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔐\",\n        \"requires\": { \"bins\": [\"op\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"1password-cli\",\n              \"bins\": [\"op\"],\n              \"label\": \"Install 1Password CLI (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# 1Password CLI\n\nFollow the official CLI get-started steps. Don't guess install commands.\n\n## References\n\n- `references/get-started.md` (install + app integration + sign-in flow)\n- `references/cli-examples.md` (real `op` examples)\n\n## Workflow\n\n1. Check OS + shell.\n2. Verify CLI present: `op --version`.\n3. Confirm desktop app integration is enabled (per get-started) and the app is unlocked.\n4. REQUIRED: create a fresh tmux session for all `op` commands (no direct `op` calls outside tmux).\n5. Sign in / authorize inside tmux: `op signin` (expect app prompt).\n6. Verify access inside tmux: `op whoami` (must succeed before any secret read).\n7. If multiple accounts: use `--account` or `OP_ACCOUNT`.\n\n## REQUIRED tmux session (T-Max)\n\nThe shell tool uses a fresh TTY per command. To avoid re-prompts and failures, always run `op` inside a dedicated tmux session with a fresh socket/session name.\n\nExample (see `tmux` skill for socket conventions, do not reuse old session names):\n\n```bash\nSOCKET_DIR=\"${OPENCLAW_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/openclaw-tmux-sockets}\"\nmkdir -p \"$SOCKET_DIR\"\nSOCKET=\"$SOCKET_DIR/openclaw-op.sock\"\nSESSION=\"op-auth-$(date +%Y%m%d-%H%M%S)\"\n\ntmux -S \"$SOCKET\" new -d -s \"$SESSION\" -n shell\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op signin --account my.1password.com\" Enter\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op whoami\" Enter\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op vault list\" Enter\ntmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\ntmux -S \"$SOCKET\" kill-session -t \"$SESSION\"\n```\n\n## Guardrails\n\n- Never paste secrets into logs, chat, or code.\n- Prefer `op run` / `op inject` over writing secrets to disk.\n- If sign-in without app integration is needed, use `op account add`.\n- If a command returns \"account is not signed in\", re-run `op signin` inside tmux and authorize in the app.\n- Do not run `op` outside tmux; stop and ask if tmux is unavailable.\n","skills/1password/references/cli-examples.md":"# op CLI examples (from op help)\n\n## Sign in\n\n- `op signin`\n- `op signin --account <shorthand|signin-address|account-id|user-id>`\n\n## Read\n\n- `op read op://app-prod/db/password`\n- `op read \"op://app-prod/db/one-time password?attribute=otp\"`\n- `op read \"op://app-prod/ssh key/private key?ssh-format=openssh\"`\n- `op read --out-file ./key.pem op://app-prod/server/ssh/key.pem`\n\n## Run\n\n- `export DB_PASSWORD=\"op://app-prod/db/password\"`\n- `op run --no-masking -- printenv DB_PASSWORD`\n- `op run --env-file=\"./.env\" -- printenv DB_PASSWORD`\n\n## Inject\n\n- `echo \"db_password: {{ op://app-prod/db/password }}\" | op inject`\n- `op inject -i config.yml.tpl -o config.yml`\n\n## Whoami / accounts\n\n- `op whoami`\n- `op account list`\n","skills/1password/references/get-started.md":"# 1Password CLI get-started (summary)\n\n- Works on macOS, Windows, and Linux.\n  - macOS/Linux shells: bash, zsh, sh, fish.\n  - Windows shell: PowerShell.\n- Requires a 1Password subscription and the desktop app to use app integration.\n- macOS requirement: Big Sur 11.0.0 or later.\n- Linux app integration requires PolKit + an auth agent.\n- Install the CLI per the official doc for your OS.\n- Enable desktop app integration in the 1Password app:\n  - Open and unlock the app, then select your account/collection.\n  - macOS: Settings > Developer > Integrate with 1Password CLI (Touch ID optional).\n  - Windows: turn on Windows Hello, then Settings > Developer > Integrate.\n  - Linux: Settings > Security > Unlock using system authentication, then Settings > Developer > Integrate.\n- After integration, run any command to sign in (example in docs: `op vault list`).\n- If multiple accounts: use `op signin` to pick one, or `--account` / `OP_ACCOUNT`.\n- For non-integration auth, use `op account add`.\n","skills/apple-notes/SKILL.md":"---\nname: apple-notes\ndescription: 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.\nhomepage: https://github.com/antoniorodr/memo\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📝\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"memo\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"antoniorodr/memo/memo\",\n              \"bins\": [\"memo\"],\n              \"label\": \"Install memo via Homebrew\",\n            },\n          ],\n      },\n  }\n---\n\n# Apple Notes CLI\n\nUse `memo notes` to manage Apple Notes directly from the terminal. Create, view, edit, delete, search, move notes between folders, and export to HTML/Markdown.\n\nSetup\n\n- Install (Homebrew): `brew tap antoniorodr/memo && brew install antoniorodr/memo/memo`\n- Manual (pip): `pip install .` (after cloning the repo)\n- macOS-only; if prompted, grant Automation access to Notes.app.\n\nView Notes\n\n- List all notes: `memo notes`\n- Filter by folder: `memo notes -f \"Folder Name\"`\n- Search notes (fuzzy): `memo notes -s \"query\"`\n\nCreate Notes\n\n- Add a new note: `memo notes -a`\n  - Opens an interactive editor to compose the note.\n- Quick add with title: `memo notes -a \"Note Title\"`\n\nEdit Notes\n\n- Edit existing note: `memo notes -e`\n  - Interactive selection of note to edit.\n\nDelete Notes\n\n- Delete a note: `memo notes -d`\n  - Interactive selection of note to delete.\n\nMove Notes\n\n- Move note to folder: `memo notes -m`\n  - Interactive selection of note and destination folder.\n\nExport Notes\n\n- Export to HTML/Markdown: `memo notes -ex`\n  - Exports selected note; uses Mistune for markdown processing.\n\nLimitations\n\n- Cannot edit notes containing images or attachments.\n- Interactive prompts may require terminal access.\n\nNotes\n\n- macOS-only.\n- Requires Apple Notes.app to be accessible.\n- For automation, grant permissions in System Settings > Privacy & Security > Automation.\n","skills/apple-reminders/SKILL.md":"---\nname: apple-reminders\ndescription: Manage Apple Reminders via remindctl CLI (list, add, edit, complete, delete). Supports lists, date filters, and JSON/plain output.\nhomepage: https://github.com/steipete/remindctl\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"⏰\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"remindctl\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/remindctl\",\n              \"bins\": [\"remindctl\"],\n              \"label\": \"Install remindctl via Homebrew\",\n            },\n          ],\n      },\n  }\n---\n\n# Apple Reminders CLI (remindctl)\n\nUse `remindctl` to manage Apple Reminders directly from the terminal.\n\n## When to Use\n\n✅ **USE this skill when:**\n\n- User explicitly mentions \"reminder\" or \"Reminders app\"\n- Creating personal to-dos with due dates that sync to iOS\n- Managing Apple Reminders lists\n- User wants tasks to appear in their iPhone/iPad Reminders app\n\n## When NOT to Use\n\n❌ **DON'T use this skill when:**\n\n- Scheduling OpenClaw tasks or alerts → use `cron` tool with systemEvent instead\n- Calendar events or appointments → use Apple Calendar\n- Project/work task management → use Notion, GitHub Issues, or task queue\n- One-time notifications → use `cron` tool for timed alerts\n- User says \"remind me\" but means an OpenClaw alert → clarify first\n\n## Setup\n\n- Install: `brew install steipete/tap/remindctl`\n- macOS-only; grant Reminders permission when prompted\n- Check status: `remindctl status`\n- Request access: `remindctl authorize`\n\n## Common Commands\n\n### View Reminders\n\n```bash\nremindctl                    # Today's reminders\nremindctl today              # Today\nremindctl tomorrow           # Tomorrow\nremindctl week               # This week\nremindctl overdue            # Past due\nremindctl all                # Everything\nremindctl 2026-01-04         # Specific date\n```\n\n### Manage Lists\n\n```bash\nremindctl list               # List all lists\nremindctl list Work          # Show specific list\nremindctl list Projects --create    # Create list\nremindctl list Work --delete        # Delete list\n```\n\n### Create Reminders\n\n```bash\nremindctl add \"Buy milk\"\nremindctl add --title \"Call mom\" --list Personal --due tomorrow\nremindctl add --title \"Meeting prep\" --due \"2026-02-15 09:00\"\n```\n\n### Complete/Delete\n\n```bash\nremindctl complete 1 2 3     # Complete by ID\nremindctl delete 4A83 --force  # Delete by ID\n```\n\n### Output Formats\n\n```bash\nremindctl today --json       # JSON for scripting\nremindctl today --plain      # TSV format\nremindctl today --quiet      # Counts only\n```\n\n## Date Formats\n\nAccepted by `--due` and date filters:\n\n- `today`, `tomorrow`, `yesterday`\n- `YYYY-MM-DD`\n- `YYYY-MM-DD HH:mm`\n- ISO 8601 (`2026-01-04T12:34:56Z`)\n\n## Example: Clarifying User Intent\n\nUser: \"Remind me to check on the deploy in 2 hours\"\n\n**Ask:** \"Do you want this in Apple Reminders (syncs to your phone) or as an OpenClaw alert (I'll message you here)?\"\n\n- Apple Reminders → use this skill\n- OpenClaw alert → use `cron` tool with systemEvent\n","skills/bear-notes/SKILL.md":"---\nname: bear-notes\ndescription: Create, search, and manage Bear notes via grizzly CLI.\nhomepage: https://bear.app\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🐻\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"grizzly\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/tylerwince/grizzly/cmd/grizzly@latest\",\n              \"bins\": [\"grizzly\"],\n              \"label\": \"Install grizzly (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# Bear Notes\n\nUse `grizzly` to create, read, and manage notes in Bear on macOS.\n\nRequirements\n\n- Bear app installed and running\n- For some operations (add-text, tags, open-note --selected), a Bear app token (stored in `~/.config/grizzly/token`)\n\n## Getting a Bear Token\n\nFor operations that require a token (add-text, tags, open-note --selected), you need an authentication token:\n\n1. Open Bear → Help → API Token → Copy Token\n2. Save it: `echo \"YOUR_TOKEN\" > ~/.config/grizzly/token`\n\n## Common Commands\n\nCreate a note\n\n```bash\necho \"Note content here\" | grizzly create --title \"My Note\" --tag work\ngrizzly create --title \"Quick Note\" --tag inbox < /dev/null\n```\n\nOpen/read a note by ID\n\n```bash\ngrizzly open-note --id \"NOTE_ID\" --enable-callback --json\n```\n\nAppend text to a note\n\n```bash\necho \"Additional content\" | grizzly add-text --id \"NOTE_ID\" --mode append --token-file ~/.config/grizzly/token\n```\n\nList all tags\n\n```bash\ngrizzly tags --enable-callback --json --token-file ~/.config/grizzly/token\n```\n\nSearch notes (via open-tag)\n\n```bash\ngrizzly open-tag --name \"work\" --enable-callback --json\n```\n\n## Options\n\nCommon flags:\n\n- `--dry-run` — Preview the URL without executing\n- `--print-url` — Show the x-callback-url\n- `--enable-callback` — Wait for Bear's response (needed for reading data)\n- `--json` — Output as JSON (when using callbacks)\n- `--token-file PATH` — Path to Bear API token file\n\n## Configuration\n\nGrizzly reads config from (in priority order):\n\n1. CLI flags\n2. Environment variables (`GRIZZLY_TOKEN_FILE`, `GRIZZLY_CALLBACK_URL`, `GRIZZLY_TIMEOUT`)\n3. `.grizzly.toml` in current directory\n4. `~/.config/grizzly/config.toml`\n\nExample `~/.config/grizzly/config.toml`:\n\n```toml\ntoken_file = \"~/.config/grizzly/token\"\ncallback_url = \"http://127.0.0.1:42123/success\"\ntimeout = \"5s\"\n```\n\n## Notes\n\n- Bear must be running for commands to work\n- Note IDs are Bear's internal identifiers (visible in note info or via callbacks)\n- Use `--enable-callback` when you need to read data back from Bear\n- Some operations require a valid token (add-text, tags, open-note --selected)\n","skills/blogwatcher/SKILL.md":"---\nname: blogwatcher\ndescription: Monitor blogs and RSS/Atom feeds for updates using the blogwatcher CLI.\nhomepage: https://github.com/Hyaxia/blogwatcher\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📰\",\n        \"requires\": { \"bins\": [\"blogwatcher\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest\",\n              \"bins\": [\"blogwatcher\"],\n              \"label\": \"Install blogwatcher (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# blogwatcher\n\nTrack blog and RSS/Atom feed updates with the `blogwatcher` CLI.\n\nInstall\n\n- Go: `go install github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest`\n\nQuick start\n\n- `blogwatcher --help`\n\nCommon commands\n\n- Add a blog: `blogwatcher add \"My Blog\" https://example.com`\n- List blogs: `blogwatcher blogs`\n- Scan for updates: `blogwatcher scan`\n- List articles: `blogwatcher articles`\n- Mark an article read: `blogwatcher read 1`\n- Mark all articles read: `blogwatcher read-all`\n- Remove a blog: `blogwatcher remove \"My Blog\"`\n\nExample output\n\n```\n$ blogwatcher blogs\nTracked blogs (1):\n\n  xkcd\n    URL: https://xkcd.com\n```\n\n```\n$ blogwatcher scan\nScanning 1 blog(s)...\n\n  xkcd\n    Source: RSS | Found: 4 | New: 4\n\nFound 4 new article(s) total!\n```\n\nNotes\n\n- Use `blogwatcher <command> --help` to discover flags and options.\n","skills/blucli/SKILL.md":"---\nname: blucli\ndescription: BluOS CLI (blu) for discovery, playback, grouping, and volume.\nhomepage: https://blucli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🫐\",\n        \"requires\": { \"bins\": [\"blu\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/blucli/cmd/blu@latest\",\n              \"bins\": [\"blu\"],\n              \"label\": \"Install blucli (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# blucli (blu)\n\nUse `blu` to control Bluesound/NAD players.\n\nQuick start\n\n- `blu devices` (pick target)\n- `blu --device <id> status`\n- `blu play|pause|stop`\n- `blu volume set 15`\n\nTarget selection (in priority order)\n\n- `--device <id|name|alias>`\n- `BLU_DEVICE`\n- config default (if set)\n\nCommon tasks\n\n- Grouping: `blu group status|add|remove`\n- TuneIn search/play: `blu tunein search \"query\"`, `blu tunein play \"query\"`\n\nPrefer `--json` for scripts. Confirm the target device before changing playback.\n","skills/bluebubbles/SKILL.md":"---\nname: bluebubbles\ndescription: Use when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel=\"bluebubbles\".\nmetadata: { \"openclaw\": { \"emoji\": \"🫧\", \"requires\": { \"config\": [\"channels.bluebubbles\"] } } }\n---\n\n# BlueBubbles Actions\n\n## Overview\n\nBlueBubbles is OpenClaw’s recommended iMessage integration. Use the `message` tool with `channel: \"bluebubbles\"` to send messages and manage iMessage conversations: send texts and attachments, react (tapbacks), edit/unsend, reply in threads, and manage group participants/names/icons.\n\n## Inputs to collect\n\n- `target` (prefer `chat_guid:...`; also `+15551234567` in E.164 or `user@example.com`)\n- `message` text for send/edit/reply\n- `messageId` for react/edit/unsend/reply\n- Attachment `path` for local files, or `buffer` + `filename` for base64\n\nIf the user is vague (\"text my mom\"), ask for the recipient handle or chat guid and the exact message content.\n\n## Actions\n\n### Send a message\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"message\": \"hello from OpenClaw\"\n}\n```\n\n### React (tapback)\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"emoji\": \"❤️\"\n}\n```\n\n### Remove a reaction\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"emoji\": \"❤️\",\n  \"remove\": true\n}\n```\n\n### Edit a previously sent message\n\n```json\n{\n  \"action\": \"edit\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"message\": \"updated text\"\n}\n```\n\n### Unsend a message\n\n```json\n{\n  \"action\": \"unsend\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\"\n}\n```\n\n### Reply to a specific message\n\n```json\n{\n  \"action\": \"reply\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"replyTo\": \"<message-guid>\",\n  \"message\": \"replying to that\"\n}\n```\n\n### Send an attachment\n\n```json\n{\n  \"action\": \"sendAttachment\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"path\": \"/tmp/photo.jpg\",\n  \"caption\": \"here you go\"\n}\n```\n\n### Send with an iMessage effect\n\n```json\n{\n  \"action\": \"sendWithEffect\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"message\": \"big news\",\n  \"effect\": \"balloons\"\n}\n```\n\n## Notes\n\n- Requires gateway config `channels.bluebubbles` (serverUrl/password/webhookPath).\n- Prefer `chat_guid` targets when you have them (especially for group chats).\n- BlueBubbles supports rich actions, but some are macOS-version dependent (for example, edit may be broken on macOS 26 Tahoe).\n- The gateway may expose both short and full message ids; full ids are more durable across restarts.\n- Developer reference for the underlying plugin lives in the BlueBubbles plugin package README.\n\n## Ideas to try\n\n- React with a tapback to acknowledge a request.\n- Reply in-thread when a user references a specific message.\n- Send a file attachment with a short caption.\n","skills/camsnap/SKILL.md":"---\nname: camsnap\ndescription: Capture frames or clips from RTSP/ONVIF cameras.\nhomepage: https://camsnap.ai\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📸\",\n        \"requires\": { \"bins\": [\"camsnap\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/camsnap\",\n              \"bins\": [\"camsnap\"],\n              \"label\": \"Install camsnap (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# camsnap\n\nUse `camsnap` to grab snapshots, clips, or motion events from configured cameras.\n\nSetup\n\n- Config file: `~/.config/camsnap/config.yaml`\n- Add camera: `camsnap add --name kitchen --host 192.168.0.10 --user user --pass pass`\n\nCommon commands\n\n- Discover: `camsnap discover --info`\n- Snapshot: `camsnap snap kitchen --out shot.jpg`\n- Clip: `camsnap clip kitchen --dur 5s --out clip.mp4`\n- Motion watch: `camsnap watch kitchen --threshold 0.2 --action '...'`\n- Doctor: `camsnap doctor --probe`\n\nNotes\n\n- Requires `ffmpeg` on PATH.\n- Prefer a short test capture before longer clips.\n","skills/canvas/SKILL.md":"# Canvas Skill\n\nDisplay HTML content on connected OpenClaw nodes (Mac app, iOS, Android).\n\n## Overview\n\nThe canvas tool lets you present web content on any connected node's canvas view. Great for:\n\n- Displaying games, visualizations, dashboards\n- Showing generated HTML content\n- Interactive demos\n\n## How It Works\n\n### Architecture\n\n```\n┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐\n│  Canvas Host    │────▶│   Node Bridge    │────▶│  Node App   │\n│  (HTTP Server)  │     │  (TCP Server)    │     │ (Mac/iOS/   │\n│  Port 18793     │     │  Port 18790      │     │  Android)   │\n└─────────────────┘     └──────────────────┘     └─────────────┘\n```\n\n1. **Canvas Host Server**: Serves static HTML/CSS/JS files from `canvasHost.root` directory\n2. **Node Bridge**: Communicates canvas URLs to connected nodes\n3. **Node Apps**: Render the content in a WebView\n\n### Tailscale Integration\n\nThe canvas host server binds based on `gateway.bind` setting:\n\n| Bind Mode  | Server Binds To     | Canvas URL Uses            |\n| ---------- | ------------------- | -------------------------- |\n| `loopback` | 127.0.0.1           | localhost (local only)     |\n| `lan`      | LAN interface       | LAN IP address             |\n| `tailnet`  | Tailscale interface | Tailscale hostname         |\n| `auto`     | Best available      | Tailscale > LAN > loopback |\n\n**Key insight:** The `canvasHostHostForBridge` is derived from `bridgeHost`. When bound to Tailscale, nodes receive URLs like:\n\n```\nhttp://<tailscale-hostname>:18793/__openclaw__/canvas/<file>.html\n```\n\nThis is why localhost URLs don't work - the node receives the Tailscale hostname from the bridge!\n\n## Actions\n\n| Action     | Description                          |\n| ---------- | ------------------------------------ |\n| `present`  | Show canvas with optional target URL |\n| `hide`     | Hide the canvas                      |\n| `navigate` | Navigate to a new URL                |\n| `eval`     | Execute JavaScript in the canvas     |\n| `snapshot` | Capture screenshot of canvas         |\n\n## Configuration\n\nIn the active OpenClaw config file (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`):\n\n```json\n{\n  \"canvasHost\": {\n    \"enabled\": true,\n    \"port\": 18793,\n    \"root\": \"/Users/you/clawd/canvas\",\n    \"liveReload\": true\n  },\n  \"gateway\": {\n    \"bind\": \"auto\"\n  }\n}\n```\n\n### Live Reload\n\nWhen `liveReload: true` (default), the canvas host:\n\n- Watches the root directory for changes (via chokidar)\n- Injects a WebSocket client into HTML files\n- Automatically reloads connected canvases when files change\n\nGreat for development!\n\n## Workflow\n\n### 1. Create HTML content\n\nPlace files in the canvas root directory (default `~/clawd/canvas/`):\n\n```bash\ncat > ~/clawd/canvas/my-game.html << 'HTML'\n<!DOCTYPE html>\n<html>\n<head><title>My Game</title></head>\n<body>\n  <h1>Hello Canvas!</h1>\n</body>\n</html>\nHTML\n```\n\n### 2. Find your canvas host URL\n\nCheck how your gateway is bound:\n\n```bash\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\ncat \"$CONFIG_PATH\" | jq '.gateway.bind'\n```\n\nThen construct the URL:\n\n- **loopback**: `http://127.0.0.1:18793/__openclaw__/canvas/<file>.html`\n- **lan/tailnet/auto**: `http://<hostname>:18793/__openclaw__/canvas/<file>.html`\n\nFind your Tailscale hostname:\n\n```bash\ntailscale status --json | jq -r '.Self.DNSName' | sed 's/\\.$//'\n```\n\n### 3. Find connected nodes\n\n```bash\nopenclaw nodes list\n```\n\nLook for Mac/iOS/Android nodes with canvas capability.\n\n### 4. Present content\n\n```\ncanvas action:present node:<node-id> target:<full-url>\n```\n\n**Example:**\n\n```\ncanvas action:present node:mac-63599bc4-b54d-4392-9048-b97abd58343a target:http://peters-mac-studio-1.sheep-coho.ts.net:18793/__openclaw__/canvas/snake.html\n```\n\n### 5. Navigate, snapshot, or hide\n\n```\ncanvas action:navigate node:<node-id> url:<new-url>\ncanvas action:snapshot node:<node-id>\ncanvas action:hide node:<node-id>\n```\n\n## Debugging\n\n### White screen / content not loading\n\n**Cause:** URL mismatch between server bind and node expectation.\n\n**Debug steps:**\n\n1. Check server bind: `CONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"; cat \"$CONFIG_PATH\" | jq '.gateway.bind'`\n2. Check what port canvas is on: `lsof -i :18793`\n3. Test URL directly: `curl http://<hostname>:18793/__openclaw__/canvas/<file>.html`\n\n**Solution:** Use the full hostname matching your bind mode, not localhost.\n\n### \"node required\" error\n\nAlways specify `node:<node-id>` parameter.\n\n### \"node not connected\" error\n\nNode is offline. Use `openclaw nodes list` to find online nodes.\n\n### Content not updating\n\nIf live reload isn't working:\n\n1. Check `liveReload: true` in config\n2. Ensure file is in the canvas root directory\n3. Check for watcher errors in logs\n\n## URL Path Structure\n\nThe canvas host serves from `/__openclaw__/canvas/` prefix:\n\n```\nhttp://<host>:18793/__openclaw__/canvas/index.html  → ~/clawd/canvas/index.html\nhttp://<host>:18793/__openclaw__/canvas/games/snake.html → ~/clawd/canvas/games/snake.html\n```\n\nThe `/__openclaw__/canvas/` prefix is defined by `CANVAS_HOST_PATH` constant.\n\n## Tips\n\n- Keep HTML self-contained (inline CSS/JS) for best results\n- Use the default index.html as a test page (has bridge diagnostics)\n- The canvas persists until you `hide` it or navigate away\n- Live reload makes development fast - just save and it updates!\n- A2UI JSON push is WIP - use HTML files for now\n","skills/clawhub/SKILL.md":"---\nname: clawhub\ndescription: 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.\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"requires\": { \"bins\": [\"clawhub\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node\",\n              \"kind\": \"node\",\n              \"package\": \"clawhub\",\n              \"bins\": [\"clawhub\"],\n              \"label\": \"Install ClawHub CLI (npm)\",\n            },\n          ],\n      },\n  }\n---\n\n# ClawHub CLI\n\nInstall\n\n```bash\nnpm i -g clawhub\n```\n\nAuth (publish)\n\n```bash\nclawhub login\nclawhub whoami\n```\n\nSearch\n\n```bash\nclawhub search \"postgres backups\"\n```\n\nInstall\n\n```bash\nclawhub install my-skill\nclawhub install my-skill --version 1.2.3\n```\n\nUpdate (hash-based match + upgrade)\n\n```bash\nclawhub update my-skill\nclawhub update my-skill --version 1.2.3\nclawhub update --all\nclawhub update my-skill --force\nclawhub update --all --no-input --force\n```\n\nList\n\n```bash\nclawhub list\n```\n\nPublish\n\n```bash\nclawhub publish ./my-skill --slug my-skill --name \"My Skill\" --version 1.2.0 --changelog \"Fixes + docs\"\n```\n\nNotes\n\n- Default registry: https://clawhub.com (override with CLAWHUB_REGISTRY or --registry)\n- Default workdir: cwd (falls back to OpenClaw workspace); install dir: ./skills (override with --workdir / --dir / CLAWHUB_WORKDIR)\n- Update command hashes local files, resolves matching version, and upgrades to latest unless --version is set\n","skills/coding-agent/SKILL.md":"---\nname: coding-agent\ndescription: 'Delegate coding tasks to Codex, Claude Code, or Pi agents via background process. Use when: (1) building/creating new features or apps, (2) reviewing PRs (spawn in temp dir), (3) refactoring large codebases, (4) iterative coding that needs file exploration. NOT for: simple one-liner fixes (just edit), reading code (use read tool), thread-bound ACP harness requests in chat (for example spawn/run Codex or Claude Code in a Discord thread; use sessions_spawn with runtime:\"acp\"), or any work in ~/clawd workspace (never spawn agents here). Claude Code: use --print --permission-mode bypassPermissions (no PTY). Codex/Pi/OpenCode: pty:true required.'\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🧩\",\n        \"requires\": { \"anyBins\": [\"claude\", \"codex\", \"opencode\", \"pi\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node-claude\",\n              \"kind\": \"node\",\n              \"package\": \"@anthropic-ai/claude-code\",\n              \"bins\": [\"claude\"],\n              \"label\": \"Install Claude Code CLI (npm)\",\n            },\n            {\n              \"id\": \"node-codex\",\n              \"kind\": \"node\",\n              \"package\": \"@openai/codex\",\n              \"bins\": [\"codex\"],\n              \"label\": \"Install Codex CLI (npm)\",\n            },\n          ],\n      },\n  }\n---\n\n# Coding Agent (bash-first)\n\nUse **bash** (with optional background mode) for all coding agent work. Simple and effective.\n\n## ⚠️ PTY Mode: Codex/Pi/OpenCode yes, Claude Code no\n\nFor **Codex, Pi, and OpenCode**, PTY is still required (interactive terminal apps):\n\n```bash\n# ✅ Correct for Codex/Pi/OpenCode\nbash pty:true command:\"codex exec 'Your prompt'\"\n```\n\nFor **Claude Code** (`claude` CLI), use `--print --permission-mode bypassPermissions` instead.\n`--dangerously-skip-permissions` with PTY can exit after the confirmation dialog.\n`--print` mode keeps full tool access and avoids interactive confirmation:\n\n```bash\n# ✅ Correct for Claude Code (no PTY needed)\ncd /path/to/project && claude --permission-mode bypassPermissions --print 'Your task'\n\n# For background execution: use background:true on the exec tool\n\n# ❌ Wrong for Claude Code\nbash pty:true command:\"claude --dangerously-skip-permissions 'task'\"\n```\n\n### Bash Tool Parameters\n\n| Parameter    | Type    | Description                                                                 |\n| ------------ | ------- | --------------------------------------------------------------------------- |\n| `command`    | string  | The shell command to run                                                    |\n| `pty`        | boolean | **Use for coding agents!** Allocates a pseudo-terminal for interactive CLIs |\n| `workdir`    | string  | Working directory (agent sees only this folder's context)                   |\n| `background` | boolean | Run in background, returns sessionId for monitoring                         |\n| `timeout`    | number  | Timeout in seconds (kills process on expiry)                                |\n| `elevated`   | boolean | Run on host instead of sandbox (if allowed)                                 |\n\n### Process Tool Actions (for background sessions)\n\n| Action      | Description                                          |\n| ----------- | ---------------------------------------------------- |\n| `list`      | List all running/recent sessions                     |\n| `poll`      | Check if session is still running                    |\n| `log`       | Get session output (with optional offset/limit)      |\n| `write`     | Send raw data to stdin                               |\n| `submit`    | Send data + newline (like typing and pressing Enter) |\n| `send-keys` | Send key tokens or hex bytes                         |\n| `paste`     | Paste text (with optional bracketed mode)            |\n| `kill`      | Terminate the session                                |\n\n---\n\n## Quick Start: One-Shot Tasks\n\nFor quick prompts/chats, create a temp git repo and run:\n\n```bash\n# Quick chat (Codex needs a git repo!)\nSCRATCH=$(mktemp -d) && cd $SCRATCH && git init && codex exec \"Your prompt here\"\n\n# Or in a real project - with PTY!\nbash pty:true workdir:~/Projects/myproject command:\"codex exec 'Add error handling to the API calls'\"\n```\n\n**Why git init?** Codex refuses to run outside a trusted git directory. Creating a temp repo solves this for scratch work.\n\n---\n\n## The Pattern: workdir + background + pty\n\nFor longer tasks, use background mode with PTY:\n\n```bash\n# Start agent in target directory (with PTY!)\nbash pty:true workdir:~/project background:true command:\"codex exec --full-auto 'Build a snake game'\"\n# Returns sessionId for tracking\n\n# Monitor progress\nprocess action:log sessionId:XXX\n\n# Check if done\nprocess action:poll sessionId:XXX\n\n# Send input (if agent asks a question)\nprocess action:write sessionId:XXX data:\"y\"\n\n# Submit with Enter (like typing \"yes\" and pressing Enter)\nprocess action:submit sessionId:XXX data:\"yes\"\n\n# Kill if needed\nprocess action:kill sessionId:XXX\n```\n\n**Why workdir matters:** Agent wakes up in a focused directory, doesn't wander off reading unrelated files (like your soul.md 😅).\n\n---\n\n## Codex CLI\n\n**Model:** `gpt-5.2-codex` is the default (set in ~/.codex/config.toml)\n\n### Flags\n\n| Flag            | Effect                                             |\n| --------------- | -------------------------------------------------- |\n| `exec \"prompt\"` | One-shot execution, exits when done                |\n| `--full-auto`   | Sandboxed but auto-approves in workspace           |\n| `--yolo`        | NO sandbox, NO approvals (fastest, most dangerous) |\n\n### Building/Creating\n\n```bash\n# Quick one-shot (auto-approves) - remember PTY!\nbash pty:true workdir:~/project command:\"codex exec --full-auto 'Build a dark mode toggle'\"\n\n# Background for longer work\nbash pty:true workdir:~/project background:true command:\"codex --yolo 'Refactor the auth module'\"\n```\n\n### Reviewing PRs\n\n**⚠️ CRITICAL: Never review PRs in OpenClaw's own project folder!**\nClone to temp folder or use git worktree.\n\n```bash\n# Clone to temp for safe review\nREVIEW_DIR=$(mktemp -d)\ngit clone https://github.com/user/repo.git $REVIEW_DIR\ncd $REVIEW_DIR && gh pr checkout 130\nbash pty:true workdir:$REVIEW_DIR command:\"codex review --base origin/main\"\n# Clean up after: trash $REVIEW_DIR\n\n# Or use git worktree (keeps main intact)\ngit worktree add /tmp/pr-130-review pr-130-branch\nbash pty:true workdir:/tmp/pr-130-review command:\"codex review --base main\"\n```\n\n### Batch PR Reviews (parallel army!)\n\n```bash\n# Fetch all PR refs first\ngit fetch origin '+refs/pull/*/head:refs/remotes/origin/pr/*'\n\n# Deploy the army - one Codex per PR (all with PTY!)\nbash pty:true workdir:~/project background:true command:\"codex exec 'Review PR #86. git diff origin/main...origin/pr/86'\"\nbash pty:true workdir:~/project background:true command:\"codex exec 'Review PR #87. git diff origin/main...origin/pr/87'\"\n\n# Monitor all\nprocess action:list\n\n# Post results to GitHub\ngh pr comment <PR#> --body \"<review content>\"\n```\n\n---\n\n## Claude Code\n\n```bash\n# Foreground\nbash workdir:~/project command:\"claude --permission-mode bypassPermissions --print 'Your task'\"\n\n# Background\nbash workdir:~/project background:true command:\"claude --permission-mode bypassPermissions --print 'Your task'\"\n```\n\n---\n\n## OpenCode\n\n```bash\nbash pty:true workdir:~/project command:\"opencode run 'Your task'\"\n```\n\n---\n\n## Pi Coding Agent\n\n```bash\n# Install: npm install -g @mariozechner/pi-coding-agent\nbash pty:true workdir:~/project command:\"pi 'Your task'\"\n\n# Non-interactive mode (PTY still recommended)\nbash pty:true command:\"pi -p 'Summarize src/'\"\n\n# Different provider/model\nbash pty:true command:\"pi --provider openai --model gpt-4o-mini -p 'Your task'\"\n```\n\n**Note:** Pi now has Anthropic prompt caching enabled (PR #584, merged Jan 2026)!\n\n---\n\n## Parallel Issue Fixing with git worktrees\n\nFor fixing multiple issues in parallel, use git worktrees:\n\n```bash\n# 1. Create worktrees for each issue\ngit worktree add -b fix/issue-78 /tmp/issue-78 main\ngit worktree add -b fix/issue-99 /tmp/issue-99 main\n\n# 2. Launch Codex in each (background + PTY!)\nbash pty:true workdir:/tmp/issue-78 background:true command:\"pnpm install && codex --yolo 'Fix issue #78: <description>. Commit and push.'\"\nbash pty:true workdir:/tmp/issue-99 background:true command:\"pnpm install && codex --yolo 'Fix issue #99 from the approved ticket summary. Implement only the in-scope edits and commit after review.'\"\n\n# 3. Monitor progress\nprocess action:list\nprocess action:log sessionId:XXX\n\n# 4. Create PRs after fixes\ncd /tmp/issue-78 && git push -u origin fix/issue-78\ngh pr create --repo user/repo --head fix/issue-78 --title \"fix: ...\" --body \"...\"\n\n# 5. Cleanup\ngit worktree remove /tmp/issue-78\ngit worktree remove /tmp/issue-99\n```\n\n---\n\n## ⚠️ Rules\n\n1. **Use the right execution mode per agent**:\n   - Codex/Pi/OpenCode: `pty:true`\n   - Claude Code: `--print --permission-mode bypassPermissions` (no PTY required)\n2. **Respect tool choice** - if user asks for Codex, use Codex.\n   - Orchestrator mode: do NOT hand-code patches yourself.\n   - If an agent fails/hangs, respawn it or ask the user for direction, but don't silently take over.\n3. **Be patient** - don't kill sessions because they're \"slow\"\n4. **Monitor with process:log** - check progress without interfering\n5. **--full-auto for building** - auto-approves changes\n6. **vanilla for reviewing** - no special flags needed\n7. **Parallel is OK** - run many Codex processes at once for batch work\n8. **NEVER start Codex inside your OpenClaw state directory** (`$OPENCLAW_STATE_DIR`, default `~/.openclaw`) - it'll read your soul docs and get weird ideas about the org chart!\n9. **NEVER checkout branches in ~/Projects/openclaw/** - that's the LIVE OpenClaw instance!\n\n---\n\n## Progress Updates (Critical)\n\nWhen you spawn coding agents in the background, keep the user in the loop.\n\n- Send 1 short message when you start (what's running + where).\n- Then only update again when something changes:\n  - a milestone completes (build finished, tests passed)\n  - the agent asks a question / needs input\n  - you hit an error or need user action\n  - the agent finishes (include what changed + where)\n- If you kill a session, immediately say you killed it and why.\n\nThis prevents the user from seeing only \"Agent failed before reply\" and having no idea what happened.\n\n---\n\n## Auto-Notify on Completion\n\nFor long-running background tasks, append a wake trigger to your prompt so OpenClaw gets notified immediately when the agent finishes (instead of waiting for the next heartbeat):\n\n```\n... your task here.\n\nWhen completely finished, run this command to notify me:\nopenclaw system event --text \"Done: [brief summary of what was built]\" --mode now\n```\n\n**Example:**\n\n```bash\nbash pty:true workdir:~/project background:true command:\"codex --yolo exec 'Build a REST API for todos.\n\nWhen completely finished, run: openclaw system event --text \\\"Done: Built todos REST API with CRUD endpoints\\\" --mode now'\"\n```\n\nThis triggers an immediate wake event — Skippy gets pinged in seconds, not 10 minutes.\n\n---\n\n## Learnings (Jan 2026)\n\n- **PTY is essential:** Coding agents are interactive terminal apps. Without `pty:true`, output breaks or agent hangs.\n- **Git repo required:** Codex won't run outside a git directory. Use `mktemp -d && git init` for scratch work.\n- **exec is your friend:** `codex exec \"prompt\"` runs and exits cleanly - perfect for one-shots.\n- **submit vs write:** Use `submit` to send input + Enter, `write` for raw data without newline.\n- **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\"_ 🦞\n","skills/discord/SKILL.md":"---\nname: discord\ndescription: \"Discord ops via the message tool (channel=discord).\"\nmetadata: { \"openclaw\": { \"emoji\": \"🎮\", \"requires\": { \"config\": [\"channels.discord.token\"] } } }\nallowed-tools: [\"message\"]\n---\n\n# Discord (Via `message`)\n\nUse the `message` tool. No provider-specific `discord` tool exposed to the agent.\n\n## Musts\n\n- Always: `channel: \"discord\"`.\n- Respect gating: `channels.discord.actions.*` (some default off: `roles`, `moderation`, `presence`, `channels`).\n- Prefer explicit ids: `guildId`, `channelId`, `messageId`, `userId`.\n- Multi-account: optional `accountId`.\n\n## Guidelines\n\n- Avoid Markdown tables in outbound Discord messages.\n- Mention users as `<@USER_ID>`.\n- Prefer Discord components v2 (`components`) for rich UI; use legacy `embeds` only when you must.\n\n## Targets\n\n- Send-like actions: `to: \"channel:<id>\"` or `to: \"user:<id>\"`.\n- Message-specific actions: `channelId: \"<id>\"` (or `to`) + `messageId: \"<id>\"`.\n\n## Common Actions (Examples)\n\nSend message:\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"hello\",\n  \"silent\": true\n}\n```\n\nSend with media:\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"see attachment\",\n  \"media\": \"file:///tmp/example.png\"\n}\n```\n\n- Optional `silent: true` to suppress Discord notifications.\n\nSend with components v2 (recommended for rich UI):\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"Status update\",\n  \"components\": \"[Carbon v2 components]\"\n}\n```\n\n- `components` expects Carbon component instances (Container, TextDisplay, etc.) from JS/TS integrations.\n- Do not combine `components` with `embeds` (Discord rejects v2 + embeds).\n\nLegacy embeds (not recommended):\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"Status update\",\n  \"embeds\": [{ \"title\": \"Legacy\", \"description\": \"Embeds are legacy.\" }]\n}\n```\n\n- `embeds` are ignored when components v2 are present.\n\nReact:\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"emoji\": \"✅\"\n}\n```\n\nRead:\n\n```json\n{\n  \"action\": \"read\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"limit\": 20\n}\n```\n\nEdit / delete:\n\n```json\n{\n  \"action\": \"edit\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"message\": \"fixed typo\"\n}\n```\n\n```json\n{\n  \"action\": \"delete\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\"\n}\n```\n\nPoll:\n\n```json\n{\n  \"action\": \"poll\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"pollQuestion\": \"Lunch?\",\n  \"pollOption\": [\"Pizza\", \"Sushi\", \"Salad\"],\n  \"pollMulti\": false,\n  \"pollDurationHours\": 24\n}\n```\n\nPins:\n\n```json\n{\n  \"action\": \"pin\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\"\n}\n```\n\nThreads:\n\n```json\n{\n  \"action\": \"thread-create\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"threadName\": \"bug triage\"\n}\n```\n\nSearch:\n\n```json\n{\n  \"action\": \"search\",\n  \"channel\": \"discord\",\n  \"guildId\": \"999\",\n  \"query\": \"release notes\",\n  \"channelIds\": [\"123\", \"456\"],\n  \"limit\": 10\n}\n```\n\nPresence (often gated):\n\n```json\n{\n  \"action\": \"set-presence\",\n  \"channel\": \"discord\",\n  \"activityType\": \"playing\",\n  \"activityName\": \"with fire\",\n  \"status\": \"online\"\n}\n```\n\n## Writing Style (Discord)\n\n- Short, conversational, low ceremony.\n- No markdown tables.\n- Mention users as `<@USER_ID>`.\n","skills/eightctl/SKILL.md":"---\nname: eightctl\ndescription: Control Eight Sleep pods (status, temperature, alarms, schedules).\nhomepage: https://eightctl.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🛌\",\n        \"requires\": { \"bins\": [\"eightctl\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/eightctl/cmd/eightctl@latest\",\n              \"bins\": [\"eightctl\"],\n              \"label\": \"Install eightctl (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# eightctl\n\nUse `eightctl` for Eight Sleep pod control. Requires auth.\n\nAuth\n\n- Config: `~/.config/eightctl/config.yaml`\n- Env: `EIGHTCTL_EMAIL`, `EIGHTCTL_PASSWORD`\n\nQuick start\n\n- `eightctl status`\n- `eightctl on|off`\n- `eightctl temp 20`\n\nCommon tasks\n\n- Alarms: `eightctl alarm list|create|dismiss`\n- Schedules: `eightctl schedule list|create|update`\n- Audio: `eightctl audio state|play|pause`\n- Base: `eightctl base info|angle`\n\nNotes\n\n- API is unofficial and rate-limited; avoid repeated logins.\n- Confirm before changing temperature or alarms.\n","skills/gemini/SKILL.md":"---\nname: gemini\ndescription: Gemini CLI for one-shot Q&A, summaries, and generation.\nhomepage: https://ai.google.dev/\nmetadata:\n  {\n    \"emoji\": \"✨\",\n    \"requires\": { \"bins\": [\"gemini\"] }\n  }\n---\n\n# Gemini CLI\n\nUse Gemini in one-shot mode with a positional prompt (avoid interactive mode).\n\nQuick start\n\n- `gemini \"Answer this question...\"`\n- `gemini --model <name> \"Prompt...\"`\n- `gemini --output-format json \"Return JSON\"`\n\nExtensions\n\n- List: `gemini --list-extensions`\n- Manage: `gemini extensions <command>`\n\nNotes\n\n- If auth is required, run `gemini` once interactively and follow the login flow.\n- Avoid `--yolo` for safety.\n","skills/gh-issues/SKILL.md":"---\nname: gh-issues\ndescription: \"Fetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]\"\nuser-invocable: true\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"requires\": { \"bins\": [\"curl\", \"git\", \"gh\"] },\n        \"primaryEnv\": \"GH_TOKEN\",\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"gh\",\n              \"bins\": [\"gh\"],\n              \"label\": \"Install GitHub CLI (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# gh-issues — Auto-fix GitHub Issues with Parallel Sub-agents\n\nYou are an orchestrator. Follow these 6 phases exactly. Do not skip phases.\n\nIMPORTANT — No `gh` CLI dependency. This skill uses curl + the GitHub REST API exclusively. The GH_TOKEN env var is already injected by OpenClaw. Pass it as a Bearer token in all API calls:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" ...\n```\n\n---\n\n## Phase 1 — Parse Arguments\n\nParse the arguments string provided after /gh-issues.\n\nPositional:\n\n- owner/repo — optional. This is the source repo to fetch issues from. If omitted, detect from the current git remote:\n  `git remote get-url origin`\n  Extract owner/repo from the URL (handles both HTTPS and SSH formats).\n  - HTTPS: https://github.com/owner/repo.git → owner/repo\n  - SSH: git@github.com:owner/repo.git → owner/repo\n    If not in a git repo or no remote found, stop with an error asking the user to specify owner/repo.\n\nFlags (all optional):\n| Flag | Default | Description |\n|------|---------|-------------|\n| --label | _(none)_ | Filter by label (e.g. bug, `enhancement`) |\n| --limit | 10 | Max issues to fetch per poll |\n| --milestone | _(none)_ | Filter by milestone title |\n| --assignee | _(none)_ | Filter by assignee (`@me` for self) |\n| --state | open | Issue state: open, closed, all |\n| --fork | _(none)_ | Your fork (`user/repo`) to push branches and open PRs from. Issues are fetched from the source repo; code is pushed to the fork; PRs are opened from the fork to the source repo. |\n| --watch | false | Keep polling for new issues and PR reviews after each batch |\n| --interval | 5 | Minutes between polls (only with `--watch`) |\n| --dry-run | false | Fetch and display only — no sub-agents |\n| --yes | false | Skip confirmation and auto-process all filtered issues |\n| --reviews-only | false | Skip issue processing (Phases 2-5). Only run Phase 6 — check open PRs for review comments and address them. |\n| --cron | false | Cron-safe mode: fetch issues and spawn sub-agents, exit without waiting for results. |\n| --model | _(none)_ | Model to use for sub-agents (e.g. `glm-5`, `zai/glm-5`). If not specified, uses the agent's default model. |\n| --notify-channel | _(none)_ | Telegram channel ID to send final PR summary to (e.g. -1002381931352). Only the final result with PR links is sent, not status updates. |\n\nStore parsed values for use in subsequent phases.\n\nDerived values:\n\n- SOURCE_REPO = the positional owner/repo (where issues live)\n- PUSH_REPO = --fork value if provided, otherwise same as SOURCE_REPO\n- FORK_MODE = true if --fork was provided, false otherwise\n\n**If `--reviews-only` is set:** Skip directly to Phase 6. Run token resolution (from Phase 2) first, then jump to Phase 6.\n\n**If `--cron` is set:**\n\n- Force `--yes` (skip confirmation)\n- If `--reviews-only` is also set, run token resolution then jump to Phase 6 (cron review mode)\n- Otherwise, proceed normally through Phases 2-5 with cron-mode behavior active\n\n---\n\n## Phase 2 — Fetch Issues\n\n**Token Resolution:**\nFirst, ensure GH_TOKEN is available. Check environment:\n\n```\necho $GH_TOKEN\n```\n\nIf empty, read from config:\n\n```\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\ncat \"$CONFIG_PATH\" | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty'\n```\n\nIf still empty, check `/data/.clawdbot/openclaw.json`:\n\n```\ncat /data/.clawdbot/openclaw.json | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty'\n```\n\nExport as GH_TOKEN for subsequent commands:\n\n```\nexport GH_TOKEN=\"<token>\"\n```\n\nBuild and run a curl request to the GitHub Issues API via exec:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/issues?per_page={limit}&state={state}&{query_params}\"\n```\n\nWhere {query_params} is built from:\n\n- labels={label} if --label was provided\n- milestone={milestone} if --milestone was provided (note: API expects milestone _number_, so if user provides a title, first resolve it via GET /repos/{SOURCE_REPO}/milestones and match by title)\n- assignee={assignee} if --assignee was provided (if @me, first resolve your username via `GET /user`)\n\nIMPORTANT: The GitHub Issues API also returns pull requests. Filter them out — exclude any item where pull_request key exists in the response object.\n\nIf in watch mode: Also filter out any issue numbers already in the PROCESSED_ISSUES set from previous batches.\n\nError handling:\n\n- If curl returns an HTTP 401 or 403 → stop and tell the user:\n  > \"GitHub authentication failed. Please check your apiKey in the OpenClaw dashboard or in the active OpenClaw config path (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`) under `skills.entries.gh-issues`.\"\n- If the response is an empty array (after filtering) → report \"No issues found matching filters\" and stop (or loop back if in watch mode).\n- If curl fails or returns any other error → report the error verbatim and stop.\n\nParse the JSON response. For each issue, extract: number, title, body, labels (array of label names), assignees, html_url.\n\n---\n\n## Phase 3 — Present & Confirm\n\nDisplay a markdown table of fetched issues:\n\n| #   | Title                         | Labels        |\n| --- | ----------------------------- | ------------- |\n| 42  | Fix null pointer in parser    | bug, critical |\n| 37  | Add retry logic for API calls | enhancement   |\n\nIf FORK_MODE is active, also display:\n\n> \"Fork mode: branches will be pushed to {PUSH_REPO}, PRs will target `{SOURCE_REPO}`\"\n\nIf `--dry-run` is active:\n\n- Display the table and stop. Do not proceed to Phase 4.\n\nIf `--yes` is active:\n\n- Display the table for visibility\n- Auto-process ALL listed issues without asking for confirmation\n- Proceed directly to Phase 4\n\nOtherwise:\nAsk the user to confirm which issues to process:\n\n- \"all\" — process every listed issue\n- Comma-separated numbers (e.g. `42, 37`) — process only those\n- \"cancel\" — abort entirely\n\nWait for user response before proceeding.\n\nWatch mode note: On the first poll, always confirm with the user (unless --yes is set). On subsequent polls, auto-process all new issues without re-confirming (the user already opted in). Still display the table so they can see what's being processed.\n\n---\n\n## Phase 4 — Pre-flight Checks\n\nRun these checks sequentially via exec:\n\n1. **Dirty working tree check:**\n\n   ```\n   git status --porcelain\n   ```\n\n   If output is non-empty, warn the user:\n\n   > \"Working tree has uncommitted changes. Sub-agents will create branches from HEAD — uncommitted changes will NOT be included. Continue?\"\n   > Wait for confirmation. If declined, stop.\n\n2. **Record base branch:**\n\n   ```\n   git rev-parse --abbrev-ref HEAD\n   ```\n\n   Store as BASE_BRANCH.\n\n3. **Verify remote access:**\n   If FORK_MODE:\n   - Verify the fork remote exists. Check if a git remote named `fork` exists:\n     ```\n     git remote get-url fork\n     ```\n     If it doesn't exist, add it:\n     ```\n     git remote add fork https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\n     ```\n   - Also verify origin (the source repo) is reachable:\n     ```\n     git ls-remote --exit-code origin HEAD\n     ```\n\n   If not FORK_MODE:\n\n   ```\n   git ls-remote --exit-code origin HEAD\n   ```\n\n   If this fails, stop with: \"Cannot reach remote origin. Check your network and git config.\"\n\n4. **Verify GH_TOKEN validity:**\n\n   ```\n   curl -s -o /dev/null -w \"%{http_code}\" -H \"Authorization: Bearer $GH_TOKEN\" https://api.github.com/user\n   ```\n\n   If HTTP status is not 200, stop with:\n\n   > \"GitHub authentication failed. Please check your apiKey in the OpenClaw dashboard or in the active OpenClaw config path (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`) under `skills.entries.gh-issues`.\"\n\n5. **Check for existing PRs:**\n   For each confirmed issue number N, run:\n\n   ```\n   curl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n     \"https://api.github.com/repos/{SOURCE_REPO}/pulls?head={PUSH_REPO_OWNER}:fix/issue-{N}&state=open&per_page=1\"\n   ```\n\n   (Where PUSH_REPO_OWNER is the owner portion of `PUSH_REPO`)\n   If the response array is non-empty, remove that issue from the processing list and report:\n\n   > \"Skipping #{N} — PR already exists: {html_url}\"\n\n   If all issues are skipped, report and stop (or loop back if in watch mode).\n\n6. **Check for in-progress branches (no PR yet = sub-agent still working):**\n   For each remaining issue number N (not already skipped by the PR check above), check if a `fix/issue-{N}` branch exists on the **push repo** (which may be a fork, not origin):\n\n   ```\n   curl -s -o /dev/null -w \"%{http_code}\" \\\n     -H \"Authorization: Bearer $GH_TOKEN\" \\\n     \"https://api.github.com/repos/{PUSH_REPO}/branches/fix/issue-{N}\"\n   ```\n\n   If HTTP 200 → the branch exists on the push repo but no open PR was found for it in step 5. Skip that issue:\n\n   > \"Skipping #{N} — branch fix/issue-{N} exists on {PUSH_REPO}, fix likely in progress\"\n\n   This check uses the GitHub API instead of `git ls-remote` so it works correctly in fork mode (where branches are pushed to the fork, not origin).\n\n   If all issues are skipped after this check, report and stop (or loop back if in watch mode).\n\n7. **Check claim-based in-progress tracking:**\n   This prevents duplicate processing when a sub-agent from a previous cron run is still working but hasn't pushed a branch or opened a PR yet.\n\n   Read the claims file (create empty `{}` if missing):\n\n   ```\n   CLAIMS_FILE=\"/data/.clawdbot/gh-issues-claims.json\"\n   if [ ! -f \"$CLAIMS_FILE\" ]; then\n     mkdir -p /data/.clawdbot\n     echo '{}' > \"$CLAIMS_FILE\"\n   fi\n   ```\n\n   Parse the claims file. For each entry, check if the claim timestamp is older than 2 hours. If so, remove it (expired — the sub-agent likely finished or failed silently). Write back the cleaned file:\n\n   ```\n   CLAIMS=$(cat \"$CLAIMS_FILE\")\n   CUTOFF=$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-2H +%Y-%m-%dT%H:%M:%SZ)\n   CLAIMS=$(echo \"$CLAIMS\" | jq --arg cutoff \"$CUTOFF\" 'to_entries | map(select(.value > $cutoff)) | from_entries')\n   echo \"$CLAIMS\" > \"$CLAIMS_FILE\"\n   ```\n\n   For each remaining issue number N (not already skipped by steps 5 or 6), check if `{SOURCE_REPO}#{N}` exists as a key in the claims file.\n\n   If claimed and not expired → skip:\n\n   > \"Skipping #{N} — sub-agent claimed this issue {minutes}m ago, still within timeout window\"\n\n   Where `{minutes}` is calculated from the claim timestamp to now.\n\n   If all issues are skipped after this check, report and stop (or loop back if in watch mode).\n\n---\n\n## Phase 5 — Spawn Sub-agents (Parallel)\n\n**Cron mode (`--cron` is active):**\n\n- **Sequential cursor tracking:** Use a cursor file to track which issue to process next:\n\n  ```\n  CURSOR_FILE=\"/data/.clawdbot/gh-issues-cursor-{SOURCE_REPO_SLUG}.json\"\n  # SOURCE_REPO_SLUG = owner-repo with slashes replaced by hyphens (e.g., openclaw-openclaw)\n  ```\n\n  Read the cursor file (create if missing):\n\n  ```\n  if [ ! -f \"$CURSOR_FILE\" ]; then\n    echo '{\"last_processed\": null, \"in_progress\": null}' > \"$CURSOR_FILE\"\n  fi\n  ```\n\n  - `last_processed`: issue number of the last completed issue (or null if none)\n  - `in_progress`: issue number currently being processed (or null)\n\n- **Select next issue:** Filter the fetched issues list to find the first issue where:\n  - Issue number > last_processed (if last_processed is set)\n  - AND issue is not in the claims file (not already in progress)\n  - AND no PR exists for the issue (checked in Phase 4 step 5)\n  - AND no branch exists on the push repo (checked in Phase 4 step 6)\n- If no eligible issue is found after the last_processed cursor, wrap around to the beginning (start from the oldest eligible issue).\n\n- If an eligible issue is found:\n  1. Mark it as in_progress in the cursor file\n  2. Spawn a single sub-agent for that one issue with `cleanup: \"keep\"` and `runTimeoutSeconds: 3600`\n  3. If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n  4. If `--notify-channel` was provided, include the channel in the task so the sub-agent can notify\n  5. Do NOT await the sub-agent result — fire and forget\n  6. **Write claim:** After spawning, read the claims file, add `{SOURCE_REPO}#{N}` with the current ISO timestamp, and write it back\n  7. Immediately report: \"Spawned fix agent for #{N} — will create PR when complete\"\n  8. Exit the skill. Do not proceed to Results Collection or Phase 6.\n\n- If no eligible issue is found (all issues either have PRs, have branches, or are in progress), report \"No eligible issues to process — all issues have PRs/branches or are in progress\" and exit.\n\n**Normal mode (`--cron` is NOT active):**\nFor each confirmed issue, spawn a sub-agent using sessions_spawn. Launch up to 8 concurrently (matching `subagents.maxConcurrent: 8`). If more than 8 issues, batch them — launch the next agent as each completes.\n\n**Write claims:** After spawning each sub-agent, read the claims file, add `{SOURCE_REPO}#{N}` with the current ISO timestamp, and write it back (same procedure as cron mode above). This covers interactive usage where watch mode might overlap with cron runs.\n\n### Sub-agent Task Prompt\n\nFor each issue, construct the following prompt and pass it to sessions_spawn. Variables to inject into the template:\n\n- {SOURCE_REPO} — upstream repo where the issue lives\n- {PUSH_REPO} — repo to push branches to (same as SOURCE_REPO unless fork mode)\n- {FORK_MODE} — true/false\n- {PUSH_REMOTE} — `fork` if FORK_MODE, otherwise `origin`\n- {number}, {title}, {url}, {labels}, {body} — from the issue\n- {BASE_BRANCH} — from Phase 4\n- {notify_channel} — Telegram channel ID for notifications (empty if not set). Replace {notify_channel} in the template below with the value of `--notify-channel` flag (or leave as empty string if not provided).\n\nWhen constructing the task, replace all template variables including {notify_channel} with actual values.\n\n```\nYou are a focused code-fix agent. Your task is to fix a single GitHub issue and open a PR.\n\nIMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.\n\nFirst, ensure GH_TOKEN is set. Check: `echo $GH_TOKEN`. If empty, read from config:\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nGH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty')\n\nUse the token in all GitHub API calls:\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" ...\n\n<config>\nSource repo (issues): {SOURCE_REPO}\nPush repo (branches + PRs): {PUSH_REPO}\nFork mode: {FORK_MODE}\nPush remote name: {PUSH_REMOTE}\nBase branch: {BASE_BRANCH}\nNotify channel: {notify_channel}\n</config>\n\n<issue>\nRepository: {SOURCE_REPO}\nIssue: #{number}\nTitle: {title}\nURL: {url}\nLabels: {labels}\nBody: {body}\n</issue>\n\n<instructions>\nFollow these steps in order. If any step fails, report the failure and stop.\n\n0. SETUP — Ensure GH_TOKEN is available:\n```\n\nexport GH_TOKEN=$(node -e \"const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/openclaw.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')\")\n\n```\nIf that fails, also try:\n```\n\nexport CONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nexport GH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | node -e \"const fs=require('fs');const d=JSON.parse(fs.readFileSync(0,'utf8'));console.log(d.skills?.entries?.['gh-issues']?.apiKey||'')\")\n\n```\nVerify: echo \"Token: ${GH_TOKEN:0:10}...\"\n\n1. CONFIDENCE CHECK — Before implementing, assess whether this issue is actionable:\n- Read the issue body carefully. Is the problem clearly described?\n- Search the codebase (grep/find) for the relevant code. Can you locate it?\n- Is the scope reasonable? (single file/function = good, whole subsystem = bad)\n- Is a specific fix suggested or is it a vague complaint?\n\nRate your confidence (1-10). If confidence < 7, STOP and report:\n> \"Skipping #{number}: Low confidence (score: N/10) — [reason: vague requirements | cannot locate code | scope too large | no clear fix suggested]\"\n\nOnly proceed if confidence >= 7.\n\n1. UNDERSTAND — Read the issue carefully. Identify what needs to change and where.\n\n2. BRANCH — Create a feature branch from the base branch:\ngit checkout -b fix/issue-{number} {BASE_BRANCH}\n\n3. ANALYZE — Search the codebase to find relevant files:\n- Use grep/find via exec to locate code related to the issue\n- Read the relevant files to understand the current behavior\n- Identify the root cause\n\n4. IMPLEMENT — Make the minimal, focused fix:\n- Follow existing code style and conventions\n- Change only what is necessary to fix the issue\n- Do not add unrelated changes or new dependencies without justification\n\n5. TEST — Discover and run the existing test suite if one exists:\n- Look for package.json scripts, Makefile targets, pytest, cargo test, etc.\n- Run the relevant tests\n- If tests fail after your fix, attempt ONE retry with a corrected approach\n- If tests still fail, report the failure\n\n6. COMMIT — Stage and commit your changes:\ngit add {changed_files}\ngit commit -m \"fix: {short_description}\n\nFixes {SOURCE_REPO}#{number}\"\n\n7. PUSH — Push the branch:\nFirst, ensure the push remote uses token auth and disable credential helpers:\ngit config --global credential.helper \"\"\ngit remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\nThen push:\nGIT_ASKPASS=true git push -u {PUSH_REMOTE} fix/issue-{number}\n\n8. PR — Create a pull request using the GitHub API:\n\nIf FORK_MODE is true, the PR goes from your fork to the source repo:\n- head = \"{PUSH_REPO_OWNER}:fix/issue-{number}\"\n- base = \"{BASE_BRANCH}\"\n- PR is created on {SOURCE_REPO}\n\nIf FORK_MODE is false:\n- head = \"fix/issue-{number}\"\n- base = \"{BASE_BRANCH}\"\n- PR is created on {SOURCE_REPO}\n\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/pulls \\\n  -d '{\n    \"title\": \"fix: {title}\",\n    \"head\": \"{head_value}\",\n    \"base\": \"{BASE_BRANCH}\",\n    \"body\": \"## Summary\\n\\n{one_paragraph_description_of_fix}\\n\\n## Changes\\n\\n{bullet_list_of_changes}\\n\\n## Testing\\n\\n{what_was_tested_and_results}\\n\\nFixes {SOURCE_REPO}#{number}\"\n  }'\n\nExtract the `html_url` from the response — this is the PR link.\n\n9. REPORT — Send back a summary:\n- PR URL (the html_url from step 8)\n- Files changed (list)\n- Fix summary (1-2 sentences)\n- Any caveats or concerns\n\n10. NOTIFY (if notify_channel is set) — If {notify_channel} is not empty, send a notification to the Telegram channel:\n```\n\nUse the message tool with:\n\n- action: \"send\"\n- channel: \"telegram\"\n- target: \"{notify_channel}\"\n- message: \"✅ PR Created: {SOURCE_REPO}#{number}\n\n{title}\n\n{pr_url}\n\nFiles changed: {files_changed_list}\"\n\n```\n</instructions>\n\n<constraints>\n- No force-push, no modifying the base branch\n- No unrelated changes or gratuitous refactoring\n- No new dependencies without strong justification\n- If the issue is unclear or too complex to fix confidently, report your analysis instead of guessing\n- Do NOT use the gh CLI — it is not available. Use curl + GitHub REST API for all GitHub operations.\n- GH_TOKEN is already in the environment — do NOT prompt for auth\n- Time limit: you have 60 minutes max. Be thorough — analyze properly, test your fix, don't rush.\n</constraints>\n```\n\n### Spawn configuration per sub-agent:\n\n- runTimeoutSeconds: 3600 (60 minutes)\n- cleanup: \"keep\" (preserve transcripts for review)\n- If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n\n### Timeout Handling\n\nIf a sub-agent exceeds 60 minutes, record it as:\n\n> \"#{N} — Timed out (issue may be too complex for auto-fix)\"\n\n---\n\n## Results Collection\n\n**If `--cron` is active:** Skip this section entirely — the orchestrator already exited after spawning in Phase 5.\n\nAfter ALL sub-agents complete (or timeout), collect their results. Store the list of successfully opened PRs in `OPEN_PRS` (PR number, branch name, issue number, PR URL) for use in Phase 6.\n\nPresent a summary table:\n\n| Issue                 | Status    | PR                             | Notes                          |\n| --------------------- | --------- | ------------------------------ | ------------------------------ |\n| #42 Fix null pointer  | PR opened | https://github.com/.../pull/99 | 3 files changed                |\n| #37 Add retry logic   | Failed    | --                             | Could not identify target code |\n| #15 Update docs       | Timed out | --                             | Too complex for auto-fix       |\n| #8 Fix race condition | Skipped   | --                             | PR already exists              |\n\n**Status values:**\n\n- **PR opened** — success, link to PR\n- **Failed** — sub-agent could not complete (include reason in Notes)\n- **Timed out** — exceeded 60-minute limit\n- **Skipped** — existing PR detected in pre-flight\n\nEnd with a one-line summary:\n\n> \"Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.\"\n\n**Send notification to channel (if --notify-channel is set):**\nIf `--notify-channel` was provided, send the final summary to that Telegram channel using the `message` tool:\n\n```\nUse the message tool with:\n- action: \"send\"\n- channel: \"telegram\"\n- target: \"{notify-channel}\"\n- message: \"✅ GitHub Issues Processed\n\nProcessed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.\n\n{PR_LIST}\"\n\nWhere PR_LIST includes only successfully opened PRs in format:\n• #{issue_number}: {PR_url} ({notes})\n```\n\nThen proceed to Phase 6.\n\n---\n\n## Phase 6 — PR Review Handler\n\nThis phase monitors open PRs (created by this skill or pre-existing `fix/issue-*` PRs) for review comments and spawns sub-agents to address them.\n\n**When this phase runs:**\n\n- After Results Collection (Phases 2-5 completed) — checks PRs that were just opened\n- When `--reviews-only` flag is set — skips Phases 2-5 entirely, runs only this phase\n- In watch mode — runs every poll cycle after checking for new issues\n\n**Cron review mode (`--cron --reviews-only`):**\nWhen both `--cron` and `--reviews-only` are set:\n\n1. Run token resolution (Phase 2 token section)\n2. Discover open `fix/issue-*` PRs (Step 6.1)\n3. Fetch review comments (Step 6.2)\n4. **Analyze comment content for actionability** (Step 6.3)\n5. If actionable comments are found, spawn ONE review-fix sub-agent for the first PR with unaddressed comments — fire-and-forget (do NOT await result)\n   - Use `cleanup: \"keep\"` and `runTimeoutSeconds: 3600`\n   - If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n6. Report: \"Spawned review handler for PR #{N} — will push fixes when complete\"\n7. Exit the skill immediately. Do not proceed to Step 6.5 (Review Results).\n\nIf no actionable comments found, report \"No actionable review comments found\" and exit.\n\n**Normal mode (non-cron) continues below:**\n\n### Step 6.1 — Discover PRs to Monitor\n\nCollect PRs to check for review comments:\n\n**If coming from Phase 5:** Use the `OPEN_PRS` list from Results Collection.\n\n**If `--reviews-only` or subsequent watch cycle:** Fetch all open PRs with `fix/issue-` branch pattern:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls?state=open&per_page=100\"\n```\n\nFilter to only PRs where `head.ref` starts with `fix/issue-`.\n\nFor each PR, extract: `number` (PR number), `head.ref` (branch name), `html_url`, `title`, `body`.\n\nIf no PRs found, report \"No open fix/ PRs to monitor\" and stop (or loop back if in watch mode).\n\n### Step 6.2 — Fetch All Review Sources\n\nFor each PR, fetch reviews from multiple sources:\n\n**Fetch PR reviews:**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/reviews\"\n```\n\n**Fetch PR review comments (inline/file-level):**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments\"\n```\n\n**Fetch PR issue comments (general conversation):**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments\"\n```\n\n**Fetch PR body for embedded reviews:**\nSome review tools (like Greptile) embed their feedback directly in the PR body. Check for:\n\n- `<!-- greptile_comment -->` markers\n- Other structured review sections in the PR body\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}\"\n```\n\nExtract the `body` field and parse for embedded review content.\n\n### Step 6.3 — Analyze Comments for Actionability\n\n**Determine the bot's own username** for filtering:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" https://api.github.com/user | jq -r '.login'\n```\n\nStore as `BOT_USERNAME`. Exclude any comment where `user.login` equals `BOT_USERNAME`.\n\n**For each comment/review, analyze the content to determine if it requires action:**\n\n**NOT actionable (skip):**\n\n- Pure approvals or \"LGTM\" without suggestions\n- Bot comments that are informational only (CI status, auto-generated summaries without specific requests)\n- Comments already addressed (check if bot replied with \"Addressed in commit...\")\n- Reviews with state `APPROVED` and no inline comments requesting changes\n\n**IS actionable (requires attention):**\n\n- Reviews with state `CHANGES_REQUESTED`\n- Reviews with state `COMMENTED` that contain specific requests:\n  - \"this test needs to be updated\"\n  - \"please fix\", \"change this\", \"update\", \"can you\", \"should be\", \"needs to\"\n  - \"will fail\", \"will break\", \"causes an error\"\n  - Mentions of specific code issues (bugs, missing error handling, edge cases)\n- Inline review comments pointing out issues in the code\n- Embedded reviews in PR body that identify:\n  - Critical issues or breaking changes\n  - Test failures expected\n  - Specific code that needs attention\n  - Confidence scores with concerns\n\n**Parse embedded review content (e.g., Greptile):**\nLook for sections marked with `<!-- greptile_comment -->` or similar. Extract:\n\n- Summary text\n- Any mentions of \"Critical issue\", \"needs attention\", \"will fail\", \"test needs to be updated\"\n- Confidence scores below 4/5 (indicates concerns)\n\n**Build actionable_comments list** with:\n\n- Source (review, inline comment, PR body, etc.)\n- Author\n- Body text\n- For inline: file path and line number\n- Specific action items identified\n\nIf no actionable comments found across any PR, report \"No actionable review comments found\" and stop (or loop back if in watch mode).\n\n### Step 6.4 — Present Review Comments\n\nDisplay a table of PRs with pending actionable comments:\n\n```\n| PR | Branch | Actionable Comments | Sources |\n|----|--------|---------------------|---------|\n| #99 | fix/issue-42 | 2 comments | @reviewer1, greptile |\n| #101 | fix/issue-37 | 1 comment | @reviewer2 |\n```\n\nIf `--yes` is NOT set and this is not a subsequent watch poll: ask the user to confirm which PRs to address (\"all\", comma-separated PR numbers, or \"skip\").\n\n### Step 6.5 — Spawn Review Fix Sub-agents (Parallel)\n\nFor each PR with actionable comments, spawn a sub-agent. Launch up to 8 concurrently.\n\n**Review fix sub-agent prompt:**\n\n```\nYou are a PR review handler agent. Your task is to address review comments on a pull request by making the requested changes, pushing updates, and replying to each comment.\n\nIMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.\n\nFirst, ensure GH_TOKEN is set. Check: echo $GH_TOKEN. If empty, read from config:\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nGH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty')\n\n<config>\nRepository: {SOURCE_REPO}\nPush repo: {PUSH_REPO}\nFork mode: {FORK_MODE}\nPush remote: {PUSH_REMOTE}\nPR number: {pr_number}\nPR URL: {pr_url}\nBranch: {branch_name}\n</config>\n\n<review_comments>\n{json_array_of_actionable_comments}\n\nEach comment has:\n- id: comment ID (for replying)\n- user: who left it\n- body: the comment text\n- path: file path (for inline comments)\n- line: line number (for inline comments)\n- diff_hunk: surrounding diff context (for inline comments)\n- source: where the comment came from (review, inline, pr_body, greptile, etc.)\n</review_comments>\n\n<instructions>\nFollow these steps in order:\n\n0. SETUP — Ensure GH_TOKEN is available:\n```\n\nexport GH_TOKEN=$(node -e \"const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/openclaw.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')\")\n\n```\nVerify: echo \"Token: ${GH_TOKEN:0:10}...\"\n\n1. CHECKOUT — Switch to the PR branch:\ngit fetch {PUSH_REMOTE} {branch_name}\ngit checkout {branch_name}\ngit pull {PUSH_REMOTE} {branch_name}\n\n2. UNDERSTAND — Read ALL review comments carefully. Group them by file. Understand what each reviewer is asking for.\n\n3. IMPLEMENT — For each comment, make the requested change:\n- Read the file and locate the relevant code\n- Make the change the reviewer requested\n- If the comment is vague or you disagree, still attempt a reasonable fix but note your concern\n- If the comment asks for something impossible or contradictory, skip it and explain why in your reply\n\n4. TEST — Run existing tests to make sure your changes don't break anything:\n- If tests fail, fix the issue or revert the problematic change\n- Note any test failures in your replies\n\n5. COMMIT — Stage and commit all changes in a single commit:\ngit add {changed_files}\ngit commit -m \"fix: address review comments on PR #{pr_number}\n\nAddresses review feedback from {reviewer_names}\"\n\n6. PUSH — Push the updated branch:\ngit config --global credential.helper \"\"\ngit remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\nGIT_ASKPASS=true git push {PUSH_REMOTE} {branch_name}\n\n7. REPLY — For each addressed comment, post a reply:\n\nFor inline review comments (have a path/line), reply to the comment thread:\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments/{comment_id}/replies \\\n  -d '{\"body\": \"Addressed in commit {short_sha} — {brief_description_of_change}\"}'\n\nFor general PR comments (issue comments), reply on the PR:\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments \\\n  -d '{\"body\": \"Addressed feedback from @{reviewer}:\\n\\n{summary_of_changes_made}\\n\\nUpdated in commit {short_sha}\"}'\n\nFor comments you could NOT address, reply explaining why:\n\"Unable to address this comment: {reason}. This may need manual review.\"\n\n8. REPORT — Send back a summary:\n- PR URL\n- Number of comments addressed vs skipped\n- Commit SHA\n- Files changed\n- Any comments that need manual attention\n</instructions>\n\n<constraints>\n- Only modify files relevant to the review comments\n- Do not make unrelated changes\n- Do not force-push — always regular push\n- If a comment contradicts another comment, address the most recent one and flag the conflict\n- Do NOT use the gh CLI — use curl + GitHub REST API\n- GH_TOKEN is already in the environment — do not prompt for auth\n- Time limit: 60 minutes max\n</constraints>\n```\n\n**Spawn configuration per sub-agent:**\n\n- runTimeoutSeconds: 3600 (60 minutes)\n- cleanup: \"keep\" (preserve transcripts for review)\n- If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n\n### Step 6.6 — Review Results\n\nAfter all review sub-agents complete, present a summary:\n\n```\n| PR | Comments Addressed | Comments Skipped | Commit | Status |\n|----|-------------------|-----------------|--------|--------|\n| #99 fix/issue-42 | 3 | 0 | abc123f | All addressed |\n| #101 fix/issue-37 | 1 | 1 | def456a | 1 needs manual review |\n```\n\nAdd comment IDs from this batch to `ADDRESSED_COMMENTS` set to prevent re-processing.\n\n---\n\n## Watch Mode (if --watch is active)\n\nAfter presenting results from the current batch:\n\n1. Add all issue numbers from this batch to the running set PROCESSED_ISSUES.\n2. Add all addressed comment IDs to ADDRESSED_COMMENTS.\n3. Tell the user:\n   > \"Next poll in {interval} minutes... (say 'stop' to end watch mode)\"\n4. Sleep for {interval} minutes.\n5. Go back to **Phase 2 — Fetch Issues**. The fetch will automatically filter out:\n   - Issues already in PROCESSED_ISSUES\n   - Issues that have existing fix/issue-{N} PRs (caught in Phase 4 pre-flight)\n6. After Phases 2-5 (or if no new issues), run **Phase 6** to check for new review comments on ALL tracked PRs (both newly created and previously opened).\n7. If no new issues AND no new actionable review comments → report \"No new activity. Polling again in {interval} minutes...\" and loop back to step 4.\n8. The user can say \"stop\" at any time to exit watch mode. When stopping, present a final cumulative summary of ALL batches — issues processed AND review comments addressed.\n\n**Context hygiene between polls — IMPORTANT:**\nOnly retain between poll cycles:\n\n- PROCESSED_ISSUES (set of issue numbers)\n- ADDRESSED_COMMENTS (set of comment IDs)\n- OPEN_PRS (list of tracked PRs: number, branch, URL)\n- Cumulative results (one line per issue + one line per review batch)\n- Parsed arguments from Phase 1\n- BASE_BRANCH, SOURCE_REPO, PUSH_REPO, FORK_MODE, BOT_USERNAME\n  Do NOT retain issue bodies, comment bodies, sub-agent transcripts, or codebase analysis between polls.\n","skills/gifgrep/SKILL.md":"---\nname: gifgrep\ndescription: Search GIF providers with CLI/TUI, download results, and extract stills/sheets.\nhomepage: https://gifgrep.com\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🧲\",\n        \"requires\": { \"bins\": [\"gifgrep\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/gifgrep\",\n              \"bins\": [\"gifgrep\"],\n              \"label\": \"Install gifgrep (brew)\",\n            },\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/gifgrep/cmd/gifgrep@latest\",\n              \"bins\": [\"gifgrep\"],\n              \"label\": \"Install gifgrep (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# gifgrep\n\nUse `gifgrep` to search GIF providers (Tenor/Giphy), browse in a TUI, download results, and extract stills or sheets.\n\nGIF-Grab (gifgrep workflow)\n\n- Search → preview → download → extract (still/sheet) for fast review and sharing.\n\nQuick start\n\n- `gifgrep cats --max 5`\n- `gifgrep cats --format url | head -n 5`\n- `gifgrep search --json cats | jq '.[0].url'`\n- `gifgrep tui \"office handshake\"`\n- `gifgrep cats --download --max 1 --format url`\n\nTUI + previews\n\n- TUI: `gifgrep tui \"query\"`\n- CLI still previews: `--thumbs` (Kitty/Ghostty only; still frame)\n\nDownload + reveal\n\n- `--download` saves to `~/Downloads`\n- `--reveal` shows the last download in Finder\n\nStills + sheets\n\n- `gifgrep still ./clip.gif --at 1.5s -o still.png`\n- `gifgrep sheet ./clip.gif --frames 9 --cols 3 -o sheet.png`\n- Sheets = single PNG grid of sampled frames (great for quick review, docs, PRs, chat).\n- Tune: `--frames` (count), `--cols` (grid width), `--padding` (spacing).\n\nProviders\n\n- `--source auto|tenor|giphy`\n- `GIPHY_API_KEY` required for `--source giphy`\n- `TENOR_API_KEY` optional (Tenor demo key used if unset)\n\nOutput\n\n- `--json` prints an array of results (`id`, `title`, `url`, `preview_url`, `tags`, `width`, `height`)\n- `--format` for pipe-friendly fields (e.g., `url`)\n\nEnvironment tweaks\n\n- `GIFGREP_SOFTWARE_ANIM=1` to force software animation\n- `GIFGREP_CELL_ASPECT=0.5` to tweak preview geometry\n","skills/github/SKILL.md":"---\nname: github\ndescription: Use when interacting with GitHub repositories, authentication, remotes, pushes, or repository creation through the GitHub CLI and related git workflows.\nhomepage: https://cli.github.com/\nmetadata:\n  {\n    \"emoji\": \"🐙\"\n  }\n---\n\n# GitHub\n\n## Overview\n\nPrefer `gh` for all GitHub operations. The default goal is to complete the full GitHub flow with the least possible user effort: detect existing authentication first, reuse it if available, and only ask the user for the smallest missing step.\n\nNever ask the user to manually perform a long GitHub setup if the agent can check, reuse, or finish it directly.\n\n## Core Rules\n\n- Prefer `gh` over raw GitHub API calls.\n- Prefer existing authenticated state over asking for new credentials.\n- Prefer non-interactive checks before prompting the user.\n- Prefer the smallest unblock request:\n  - paste a one-time code\n  - confirm browser login\n  - provide a PAT\n  - confirm repository visibility\n- Do not ask the user to manually create the repository if `gh` can do it.\n- Do not ask the user to manually add remotes, initialize Git, or push if the agent can do it.\n- Never print, echo, log, summarize, or restate secrets.\n- Never store secrets in files, commits, repo config, docs, examples, or chat summaries.\n- Never switch credential strategy unless the current one actually fails.\n\n### Interactive login driving rules\n\nWhen using `gh auth login` through a shell session, do not assume the browser login step has started after the first prompt or after sending one key.\n\nYou must continue driving the interactive flow until there is explicit evidence of one of these states:\n\n1. GitHub CLI has clearly started browser/device authentication\n2. GitHub CLI is explicitly waiting for the user to complete external authentication\n3. GitHub CLI reports that authentication succeeded\n4. GitHub CLI reports a real error and cannot continue\n\nWhile driving the login flow:\n\n- keep polling the session after each input\n- handle intermediate prompts such as protocol selection, SSH/HTTPS choice, credential helper confirmation, browser/device flow confirmation, or similar setup questions\n- use minimal inputs such as `enter`, arrow keys, `y`, `n`, or short submitted text as needed\n- do not stop after only one successful prompt response\n- do not tell the user that the browser has opened unless the command output explicitly indicates that a browser login or web flow has started\n- do not tell the user that login is complete unless the command output explicitly indicates success\n\nAcceptable evidence that browser or external auth has actually started includes output such as:\n\n- a message that a browser is being opened\n- a URL to visit\n- a device code to enter\n- explicit wording that GitHub authentication is waiting for completion\n\nIf the shell session remains running but only shows that an earlier prompt was answered, continue polling instead of summarizing the state prematurely.\n\nOnly ask the user to act when the output clearly shows an external action is now required.\n\nWhen the user says they completed browser or device authorization, do not start a new `gh auth login` flow immediately.\n\nInstead:\n\n1. continue polling the existing shell session if it is still running\n2. wait for explicit success or failure from that same session\n3. only if that session has exited or is unrecoverable, then check `gh auth status`\n4. only start a brand-new login flow if the previous login session has clearly failed or been closed\n\nIf the shell output contains prompts like:\n\n- \"Press Enter to open ...\"\n- \"Press Enter to continue\"\n- similar one-step local confirmation prompts\n\nthen send `enter` automatically and continue polling the same session.\n\nDo not stop at that prompt.\nDo not ask the user to confirm completion yet.\nDo not start a new login flow while this session is still running.\n\n## Credential Handling Priority\n\n### 1. Reuse `gh` authentication first\n\nCheck whether `gh` exists and is already authenticated.\n\nUse this path when possible:\n\n- `gh auth status`\n- if authenticated, continue directly with:\n  - repo detection\n  - `gh repo create`\n  - remote setup\n  - push\n\nIf `gh` is authenticated, do not ask the user for tokens, passwords, SSH keys, or browser login.\n\n### 2. If `gh` exists but is not authenticated, log in with `gh` directly\n\nIf `gh` is installed but not authenticated, use `gh auth login` directly and continue the GitHub flow after login succeeds.\n\nPreferred order:\n\n1. browser/web login via `gh auth login -h github.com -w`\n2. standard interactive `gh auth login -h github.com`\n3. PAT-based login only if the normal `gh` login path is unavailable or fails\n\nRules:\n\n- do not switch away from `gh` login unless it actually fails\n- do not ask the user to manually create repositories or configure git\n- do not ask the user to perform a long GitHub setup outside the login itself\n- continue automatically as soon as login succeeds\n\nWhen login requires user interaction:\n\n- ask for only the smallest possible action\n- examples:\n  - “Please complete the GitHub browser login that just opened.”\n  - “Please finish the GitHub CLI login prompts.”\n  - “Please paste a GitHub PAT with repo access.”\n\nAfter login succeeds, continue directly with:\n\n- repo detection\n- `gh repo create`\n- remote setup\n- push\n\n### 3. If `gh` is unavailable, try to install it first\n\nIf `gh` is missing:\n\n- first try to install `gh` automatically using the platform's normal package manager or a reasonable official installation path\n- prefer non-interactive installation methods when available\n- after installation, re-check whether `gh` exists and continue with the normal `gh`-first flow\n- only fall back away from `gh` if installation actually fails, is blocked by the environment, or would require disproportionate user effort\n\nAcceptable install attempts include examples like:\n\n- Homebrew on macOS\n- `apt`, `dnf`, or similar package managers on Linux\n- `winget` or other normal package-manager paths on Windows\n\nRules:\n\n- do not ask the user to install `gh` manually before you have tried a reasonable automatic install path\n- do not switch to a different credential strategy just because `gh` is missing initially\n- if installation fails, summarize the blocker briefly and then continue to the fallback path below\n\n### 4. If `gh` is still unavailable, fall back to Git\n\nIf `gh` is missing:\n\n- continue with plain `git`\n- prefer existing Git credential helpers or existing SSH authentication\n- inspect the environment before asking for credentials\n\nCheck for likely reusable auth:\n\n- existing `origin` remote\n- existing Git credential helper\n- working SSH auth to GitHub\n- existing authenticated HTTPS remote behavior\n\nOnly if those paths fail should the user be asked for one minimal credential input.\n\n## Minimal-Interruption Prompting\n\nWhen user input is required, ask for only one concrete item at a time.\n\nGood prompts:\n\n- “Please confirm whether the repo should be public or private.”\n- “Please complete the GitHub login in your browser.”\n- “Please paste a GitHub PAT with repo access.”\n- “Please confirm the GitHub owner/org name.”\n\nBad prompts:\n\n- asking the user to manually create a repo\n- asking the user to configure Git from scratch without first checking\n- asking for multiple credentials at once\n- asking the user to inspect local config manually if the agent can inspect it\n\n## Safe Credential Rules\n\n- Treat PATs, device codes, auth tokens, cookies, and SSH private keys as secrets.\n- Never echo secrets back to the user.\n- Never include secrets in command output summaries.\n- Never write secrets into repo files or examples.\n- Never commit `.env`, token files, or copied credentials.\n- If a command might expose a secret in stdout/stderr, avoid reproducing that output verbatim.\n- If the user gives a token, use it only for the current task unless explicitly told otherwise.\n\n## Standard Execution Flow\n\n### A. Detect environment\n\nCheck in this order:\n\n1. Is this already a Git repo?\n2. Is there already a remote?\n3. Is `gh` installed?\n4. Is `gh` authenticated?\n5. If not using `gh`, does Git already have usable auth?\n\n### B. If already connected to GitHub\n\nIf the repo already has a valid GitHub remote and push access works:\n\n- do not recreate the repo\n- do not replace auth\n- just push\n\n### C. If Git repo exists but no GitHub repo exists yet\n\nPreferred:\n\n- create the GitHub repo with `gh repo create`\n- set or update `origin`\n- push the current branch\n\nOnly ask the user for:\n\n- owner/org if ambiguous\n- public vs private if not specified\n- authentication only if required\n\n### D. If not a Git repo yet\n\nDo the setup directly:\n\n- initialize Git\n- add files\n- create initial commit if needed\n- create GitHub repo\n- add remote\n- push\n\nDo not ask the user to perform these steps manually.\n\n## `gh`-First Behavior\n\nWhen `gh` is available and authenticated, prefer commands in this shape:\n\n- inspect auth\n- create repo\n- set remote if needed\n- push branch\n- optionally enable features like description/homepage/topics later\n\nUse plain Git only for local repository operations or when `gh` cannot complete the GitHub-side action.\n\n## Git / API Fallback Behavior\n\nOnly use this fallback when:\n\n- `gh` is not installed and cannot be installed reasonably\n- or `gh` is installed but unusable for the required operation\n- or the environment blocks the normal `gh` flow in a way that cannot be resolved with minimal user input\n\nFallback order:\n\n1. direct GitHub API + local `git`\n2. plain `git` with existing SSH or credential-helper auth\n3. HTTPS + PAT only if no better reusable auth exists\n\nFallback rules:\n\n- still minimize user effort\n- still prefer reusing existing credentials\n- still never ask the user to create the repository manually if the agent can do it through the API\n- still avoid exposing or persisting secrets\n- still continue automatically once the minimal missing credential or confirmation is provided\n\nIf PAT is required:\n\n- ask only for a token with the minimum needed scope, usually repo write access\n- use it only to complete the push flow\n- avoid leaving it in shell history, files, process args, or remote URLs when possible\n\n## Success Criteria\n\nA successful run means:\n\n- the local project is a valid Git repo if needed\n- the GitHub repo exists\n- the correct remote is configured\n- the current branch is pushed\n- credentials were reused when possible\n- user involvement was limited to the smallest truly necessary action\n- no secret was exposed or persisted unsafely\n","skills/gog/SKILL.md":"---\nname: gog\ndescription: Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.\nhomepage: https://gogcli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🎮\",\n        \"requires\": { \"bins\": [\"gog\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/gogcli\",\n              \"bins\": [\"gog\"],\n              \"label\": \"Install gog (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# gog\n\nUse `gog` for Gmail/Calendar/Drive/Contacts/Sheets/Docs. Requires OAuth setup.\n\nSetup (once)\n\n- `gog auth credentials /path/to/client_secret.json`\n- `gog auth add you@gmail.com --services gmail,calendar,drive,contacts,docs,sheets`\n- `gog auth list`\n\nCommon commands\n\n- Gmail search: `gog gmail search 'newer_than:7d' --max 10`\n- Gmail messages search (per email, ignores threading): `gog gmail messages search \"in:inbox from:ryanair.com\" --max 20 --account you@example.com`\n- Gmail send (plain): `gog gmail send --to a@b.com --subject \"Hi\" --body \"Hello\"`\n- Gmail send (multi-line): `gog gmail send --to a@b.com --subject \"Hi\" --body-file ./message.txt`\n- Gmail send (stdin): `gog gmail send --to a@b.com --subject \"Hi\" --body-file -`\n- Gmail send (HTML): `gog gmail send --to a@b.com --subject \"Hi\" --body-html \"<p>Hello</p>\"`\n- Gmail draft: `gog gmail drafts create --to a@b.com --subject \"Hi\" --body-file ./message.txt`\n- Gmail send draft: `gog gmail drafts send <draftId>`\n- Gmail reply: `gog gmail send --to a@b.com --subject \"Re: Hi\" --body \"Reply\" --reply-to-message-id <msgId>`\n- Calendar list events: `gog calendar events <calendarId> --from <iso> --to <iso>`\n- Calendar create event: `gog calendar create <calendarId> --summary \"Title\" --from <iso> --to <iso>`\n- Calendar create with color: `gog calendar create <calendarId> --summary \"Title\" --from <iso> --to <iso> --event-color 7`\n- Calendar update event: `gog calendar update <calendarId> <eventId> --summary \"New Title\" --event-color 4`\n- Calendar show colors: `gog calendar colors`\n- Drive search: `gog drive search \"query\" --max 10`\n- Contacts: `gog contacts list --max 20`\n- Sheets get: `gog sheets get <sheetId> \"Tab!A1:D10\" --json`\n- Sheets update: `gog sheets update <sheetId> \"Tab!A1:B2\" --values-json '[[\"A\",\"B\"],[\"1\",\"2\"]]' --input USER_ENTERED`\n- Sheets append: `gog sheets append <sheetId> \"Tab!A:C\" --values-json '[[\"x\",\"y\",\"z\"]]' --insert INSERT_ROWS`\n- Sheets clear: `gog sheets clear <sheetId> \"Tab!A2:Z\"`\n- Sheets metadata: `gog sheets metadata <sheetId> --json`\n- Docs export: `gog docs export <docId> --format txt --out /tmp/doc.txt`\n- Docs cat: `gog docs cat <docId>`\n\nCalendar Colors\n\n- Use `gog calendar colors` to see all available event colors (IDs 1-11)\n- Add colors to events with `--event-color <id>` flag\n- Event color IDs (from `gog calendar colors` output):\n  - 1: #a4bdfc\n  - 2: #7ae7bf\n  - 3: #dbadff\n  - 4: #ff887c\n  - 5: #fbd75b\n  - 6: #ffb878\n  - 7: #46d6db\n  - 8: #e1e1e1\n  - 9: #5484ed\n  - 10: #51b749\n  - 11: #dc2127\n\nEmail Formatting\n\n- Prefer plain text. Use `--body-file` for multi-paragraph messages (or `--body-file -` for stdin).\n- Same `--body-file` pattern works for drafts and replies.\n- `--body` does not unescape `\\n`. If you need inline newlines, use a heredoc or `$'Line 1\\n\\nLine 2'`.\n- Use `--body-html` only when you need rich formatting.\n- HTML tags: `<p>` for paragraphs, `<br>` for line breaks, `<strong>` for bold, `<em>` for italic, `<a href=\"url\">` for links, `<ul>`/`<li>` for lists.\n- Example (plain text via stdin):\n\n  ```bash\n  gog gmail send --to recipient@example.com \\\n    --subject \"Meeting Follow-up\" \\\n    --body-file - <<'EOF'\n  Hi Name,\n\n  Thanks for meeting today. Next steps:\n  - Item one\n  - Item two\n\n  Best regards,\n  Your Name\n  EOF\n  ```\n\n- Example (HTML list):\n  ```bash\n  gog gmail send --to recipient@example.com \\\n    --subject \"Meeting Follow-up\" \\\n    --body-html \"<p>Hi Name,</p><p>Thanks for meeting today. Here are the next steps:</p><ul><li>Item one</li><li>Item two</li></ul><p>Best regards,<br>Your Name</p>\"\n  ```\n\nNotes\n\n- Set `GOG_ACCOUNT=you@gmail.com` to avoid repeating `--account`.\n- For scripting, prefer `--json` plus `--no-input`.\n- Sheets values can be passed via `--values-json` (recommended) or as inline rows.\n- Docs supports export/cat/copy. In-place edits require a Docs API client (not in gog).\n- Confirm before sending mail or creating events.\n- `gog gmail search` returns one row per thread; use `gog gmail messages search` when you need every individual email returned separately.\n","skills/goplaces/SKILL.md":"---\nname: goplaces\ndescription: Query Google Places API (New) via the goplaces CLI for text search, place details, resolve, and reviews. Use for human-friendly place lookup or JSON output for scripts.\nhomepage: https://github.com/steipete/goplaces\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📍\",\n        \"requires\": { \"bins\": [\"goplaces\"], \"env\": [\"GOOGLE_PLACES_API_KEY\"] },\n        \"primaryEnv\": \"GOOGLE_PLACES_API_KEY\",\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/goplaces\",\n              \"bins\": [\"goplaces\"],\n              \"label\": \"Install goplaces (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# goplaces\n\nModern Google Places API (New) CLI. Human output by default, `--json` for scripts.\n\nInstall\n\n- Homebrew: `brew install steipete/tap/goplaces`\n\nConfig\n\n- `GOOGLE_PLACES_API_KEY` required.\n- Optional: `GOOGLE_PLACES_BASE_URL` for testing/proxying.\n\nCommon commands\n\n- Search: `goplaces search \"coffee\" --open-now --min-rating 4 --limit 5`\n- Bias: `goplaces search \"pizza\" --lat 40.8 --lng -73.9 --radius-m 3000`\n- Pagination: `goplaces search \"pizza\" --page-token \"NEXT_PAGE_TOKEN\"`\n- Resolve: `goplaces resolve \"Soho, London\" --limit 5`\n- Details: `goplaces details <place_id> --reviews`\n- JSON: `goplaces search \"sushi\" --json`\n\nNotes\n\n- `--no-color` or `NO_COLOR` disables ANSI color.\n- Price levels: 0..4 (free → very expensive).\n- Type filter sends only the first `--type` value (API accepts one).\n","skills/healthcheck/SKILL.md":"---\nname: healthcheck\ndescription: Host security hardening and risk-tolerance configuration for OpenClaw deployments. Use when a user asks for security audits, firewall/SSH/update hardening, risk posture, exposure review, OpenClaw cron scheduling for periodic checks, or version status checks on a machine running OpenClaw (laptop, workstation, Pi, VPS).\n---\n\n# OpenClaw Host Hardening\n\n## Overview\n\nAssess and harden the host running OpenClaw, then align it to a user-defined risk tolerance without breaking access. Use OpenClaw security tooling as a first-class signal, but treat OS hardening as a separate, explicit set of steps.\n\n## Core rules\n\n- Recommend running this skill with a state-of-the-art model (e.g., Opus 4.5, GPT 5.2+). The agent should self-check the current model and suggest switching if below that level; do not block execution.\n- Require explicit approval before any state-changing action.\n- Do not modify remote access settings without confirming how the user connects.\n- Prefer reversible, staged changes with a rollback plan.\n- Never claim OpenClaw changes the host firewall, SSH, or OS updates; it does not.\n- If role/identity is unknown, provide recommendations only.\n- Formatting: every set of user choices must be numbered so the user can reply with a single digit.\n- System-level backups are recommended; try to verify status.\n\n## Workflow (follow in order)\n\n### 0) Model self-check (non-blocking)\n\nBefore starting, check the current model. If it is below state-of-the-art (e.g., Opus 4.5, GPT 5.2+), recommend switching. Do not block execution.\n\n### 1) Establish context (read-only)\n\nTry to infer 1–5 from the environment before asking. Prefer simple, non-technical questions if you need confirmation.\n\nDetermine (in order):\n\n1. OS and version (Linux/macOS/Windows), container vs host.\n2. Privilege level (root/admin vs user).\n3. Access path (local console, SSH, RDP, tailnet).\n4. Network exposure (public IP, reverse proxy, tunnel).\n5. OpenClaw gateway status and bind address.\n6. Backup system and status (e.g., Time Machine, system images, snapshots).\n7. Deployment context (local mac app, headless gateway host, remote gateway, container/CI).\n8. Disk encryption status (FileVault/LUKS/BitLocker).\n9. OS automatic security updates status.\n   Note: these are not blocking items, but are highly recommended, especially if OpenClaw can access sensitive data.\n10. Usage mode for a personal assistant with full access (local workstation vs headless/remote vs other).\n\nFirst ask once for permission to run read-only checks. If granted, run them by default and only ask questions for items you cannot infer or verify. Do not ask for information already visible in runtime or command output. Keep the permission ask as a single sentence, and list follow-up info needed as an unordered list (not numbered) unless you are presenting selectable choices.\n\nIf you must ask, use non-technical prompts:\n\n- “Are you using a Mac, Windows PC, or Linux?”\n- “Are you logged in directly on the machine, or connecting from another computer?”\n- “Is this machine reachable from the public internet, or only on your home/network?”\n- “Do you have backups enabled (e.g., Time Machine), and are they current?”\n- “Is disk encryption turned on (FileVault/BitLocker/LUKS)?”\n- “Are automatic security updates enabled?”\n- “How do you use this machine?”\n  Examples:\n  - Personal machine shared with the assistant\n  - Dedicated local machine for the assistant\n  - Dedicated remote machine/server accessed remotely (always on)\n  - Something else?\n\nOnly ask for the risk profile after system context is known.\n\nIf the user grants read-only permission, run the OS-appropriate checks by default. If not, offer them (numbered). Examples:\n\n1. OS: `uname -a`, `sw_vers`, `cat /etc/os-release`.\n2. Listening ports:\n   - Linux: `ss -ltnup` (or `ss -ltnp` if `-u` unsupported).\n   - macOS: `lsof -nP -iTCP -sTCP:LISTEN`.\n3. Firewall status:\n   - Linux: `ufw status`, `firewall-cmd --state`, `nft list ruleset` (pick what is installed).\n   - macOS: `/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate` and `pfctl -s info`.\n4. Backups (macOS): `tmutil status` (if Time Machine is used).\n\n### 2) Run OpenClaw security audits (read-only)\n\nAs part of the default read-only checks, run `openclaw security audit --deep`. Only offer alternatives if the user requests them:\n\n1. `openclaw security audit` (faster, non-probing)\n2. `openclaw security audit --json` (structured output)\n\nOffer to apply OpenClaw safe defaults (numbered):\n\n1. `openclaw security audit --fix`\n\nBe explicit that `--fix` only tightens OpenClaw defaults and file permissions. It does not change host firewall, SSH, or OS update policies.\n\nIf browser control is enabled, recommend that 2FA be enabled on all important accounts, with hardware keys preferred and SMS not sufficient.\n\n### 3) Check OpenClaw version/update status (read-only)\n\nAs part of the default read-only checks, run `openclaw update status`.\n\nReport the current channel and whether an update is available.\n\n### 4) Determine risk tolerance (after system context)\n\nAsk the user to pick or confirm a risk posture and any required open services/ports (numbered choices below).\nDo not pigeonhole into fixed profiles; if the user prefers, capture requirements instead of choosing a profile.\nOffer suggested profiles as optional defaults (numbered). Note that most users pick Home/Workstation Balanced:\n\n1. Home/Workstation Balanced (most common): firewall on with reasonable defaults, remote access restricted to LAN or tailnet.\n2. VPS Hardened: deny-by-default inbound firewall, minimal open ports, key-only SSH, no root login, automatic security updates.\n3. Developer Convenience: more local services allowed, explicit exposure warnings, still audited.\n4. Custom: user-defined constraints (services, exposure, update cadence, access methods).\n\n### 5) Produce a remediation plan\n\nProvide a plan that includes:\n\n- Target profile\n- Current posture summary\n- Gaps vs target\n- Step-by-step remediation with exact commands\n- Access-preservation strategy and rollback\n- Risks and potential lockout scenarios\n- Least-privilege notes (e.g., avoid admin usage, tighten ownership/permissions where safe)\n- Credential hygiene notes (location of OpenClaw creds, prefer disk encryption)\n\nAlways show the plan before any changes.\n\n### 6) Offer execution options\n\nOffer one of these choices (numbered so users can reply with a single digit):\n\n1. Do it for me (guided, step-by-step approvals)\n2. Show plan only\n3. Fix only critical issues\n4. Export commands for later\n\n### 7) Execute with confirmations\n\nFor each step:\n\n- Show the exact command\n- Explain impact and rollback\n- Confirm access will remain available\n- Stop on unexpected output and ask for guidance\n\n### 8) Verify and report\n\nRe-check:\n\n- Firewall status\n- Listening ports\n- Remote access still works\n- OpenClaw security audit (re-run)\n\nDeliver a final posture report and note any deferred items.\n\n## Required confirmations (always)\n\nRequire explicit approval for:\n\n- Firewall rule changes\n- Opening/closing ports\n- SSH/RDP configuration changes\n- Installing/removing packages\n- Enabling/disabling services\n- User/group modifications\n- Scheduling tasks or startup persistence\n- Update policy changes\n- Access to sensitive files or credentials\n\nIf unsure, ask.\n\n## Periodic checks\n\nAfter OpenClaw install or first hardening pass, run at least one baseline audit and version check:\n\n- `openclaw security audit`\n- `openclaw security audit --deep`\n- `openclaw update status`\n\nOngoing monitoring is recommended. Use the OpenClaw cron tool/CLI to schedule periodic audits (Gateway scheduler). Do not create scheduled tasks without explicit approval. Store outputs in a user-approved location and avoid secrets in logs.\nWhen scheduling headless cron runs, include a note in the output that instructs the user to call `healthcheck` so issues can be fixed.\n\n### Required prompt to schedule (always)\n\nAfter any audit or hardening pass, explicitly offer scheduling and require a direct response. Use a short prompt like (numbered):\n\n1. “Do you want me to schedule periodic audits (e.g., daily/weekly) via `openclaw cron add`?”\n\nIf the user says yes, ask for:\n\n- cadence (daily/weekly), preferred time window, and output location\n- whether to also schedule `openclaw update status`\n\nUse a stable cron job name so updates are deterministic. Prefer exact names:\n\n- `healthcheck:security-audit`\n- `healthcheck:update-status`\n\nBefore creating, `openclaw cron list` and match on exact `name`. If found, `openclaw cron edit <id> ...`.\nIf not found, `openclaw cron add --name <name> ...`.\n\nAlso offer a periodic version check so the user can decide when to update (numbered):\n\n1. `openclaw update status` (preferred for source checkouts and channels)\n2. `npm view openclaw version` (published npm version)\n\n## OpenClaw command accuracy\n\nUse only supported commands and flags:\n\n- `openclaw security audit [--deep] [--fix] [--json]`\n- `openclaw status` / `openclaw status --deep`\n- `openclaw health --json`\n- `openclaw update status`\n- `openclaw cron add|list|runs|run`\n\nDo not invent CLI flags or imply OpenClaw enforces host firewall/SSH policies.\n\n## Logging and audit trail\n\nRecord:\n\n- Gateway identity and role\n- Plan ID and timestamp\n- Approved steps and exact commands\n- Exit codes and files modified (best effort)\n\nRedact secrets. Never log tokens or full credential contents.\n\n## Memory writes (conditional)\n\nOnly write to memory files when the user explicitly opts in and the session is a private/local workspace\n(per `docs/reference/templates/AGENTS.md`). Otherwise provide a redacted, paste-ready summary the user can\ndecide to save elsewhere.\n\nFollow the durable-memory prompt format used by OpenClaw compaction:\n\n- Write lasting notes to `memory/YYYY-MM-DD.md`.\n\nAfter each audit/hardening run, if opted-in, append a short, dated summary to `memory/YYYY-MM-DD.md`\n(what was checked, key findings, actions taken, any scheduled cron jobs, key decisions,\nand all commands executed). Append-only: never overwrite existing entries.\nRedact sensitive host details (usernames, hostnames, IPs, serials, service names, tokens).\nIf there are durable preferences or decisions (risk posture, allowed ports, update policy),\nalso update `MEMORY.md` (long-term memory is optional and only used in private sessions).\n\nIf the session cannot write to the workspace, ask for permission or provide exact entries\nthe user can paste into the memory files.\n","skills/himalaya/SKILL.md":"---\nname: himalaya\ndescription: \"CLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).\"\nhomepage: https://github.com/pimalaya/himalaya\nmetadata:\n  {\n    \"emoji\": \"📧\",\n    \"requires\": { \"bins\": [\"himalaya\"] }\n  }\n---\n\n# Himalaya Email CLI\n\nHimalaya is a CLI email client that lets you manage emails from the terminal using IMAP, SMTP, Notmuch, or Sendmail backends.\n\n## References\n\n- `references/configuration.md` (config file setup + IMAP/SMTP authentication)\n- `references/message-composition.md` (MML syntax for composing emails)\n\n## Prerequisites\n\n1. Himalaya CLI installed (`himalaya --version` to verify)\n2. A configuration file at `~/.config/himalaya/config.toml`\n3. IMAP/SMTP credentials configured (password stored securely)\n\n## Configuration Setup\n\nRun the interactive wizard to set up an account:\n\n```bash\nhimalaya account configure\n```\n\nOr create `~/.config/himalaya/config.toml` manually:\n\n```toml\n[accounts.personal]\nemail = \"you@example.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\nbackend.type = \"imap\"\nbackend.host = \"imap.example.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@example.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show email/imap\"  # or use keyring\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.example.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@example.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show email/smtp\"\n```\n\n## Common Operations\n\n### List Folders\n\n```bash\nhimalaya folder list\n```\n\n### List Emails\n\nList emails in INBOX (default):\n\n```bash\nhimalaya envelope list\n```\n\nList emails in a specific folder:\n\n```bash\nhimalaya envelope list --folder \"Sent\"\n```\n\nList with pagination:\n\n```bash\nhimalaya envelope list --page 1 --page-size 20\n```\n\n### Search Emails\n\n```bash\nhimalaya envelope list from john@example.com subject meeting\n```\n\n### Read an Email\n\nRead email by ID (shows plain text):\n\n```bash\nhimalaya message read 42\n```\n\nExport raw MIME:\n\n```bash\nhimalaya message export 42 --full\n```\n\n### Reply to an Email\n\nInteractive reply (opens $EDITOR):\n\n```bash\nhimalaya message reply 42\n```\n\nReply-all:\n\n```bash\nhimalaya message reply 42 --all\n```\n\n### Forward an Email\n\n```bash\nhimalaya message forward 42\n```\n\n### Write a New Email\n\nInteractive compose (opens $EDITOR):\n\n```bash\nhimalaya message write\n```\n\nSend directly using template:\n\n```bash\ncat << 'EOF' | himalaya template send\nFrom: you@example.com\nTo: recipient@example.com\nSubject: Test Message\n\nHello from Himalaya!\nEOF\n```\n\nOr with headers flag:\n\n```bash\nhimalaya message write -H \"To:recipient@example.com\" -H \"Subject:Test\" \"Message body here\"\n```\n\n### Move/Copy Emails\n\nMove to folder:\n\n```bash\nhimalaya message move 42 \"Archive\"\n```\n\nCopy to folder:\n\n```bash\nhimalaya message copy 42 \"Important\"\n```\n\n### Delete an Email\n\n```bash\nhimalaya message delete 42\n```\n\n### Manage Flags\n\nAdd flag:\n\n```bash\nhimalaya flag add 42 --flag seen\n```\n\nRemove flag:\n\n```bash\nhimalaya flag remove 42 --flag seen\n```\n\n## Multiple Accounts\n\nList accounts:\n\n```bash\nhimalaya account list\n```\n\nUse a specific account:\n\n```bash\nhimalaya --account work envelope list\n```\n\n## Attachments\n\nSave attachments from a message:\n\n```bash\nhimalaya attachment download 42\n```\n\nSave to specific directory:\n\n```bash\nhimalaya attachment download 42 --dir ~/Downloads\n```\n\n## Output Formats\n\nMost commands support `--output` for structured output:\n\n```bash\nhimalaya envelope list --output json\nhimalaya envelope list --output plain\n```\n\n## Debugging\n\nEnable debug logging:\n\n```bash\nRUST_LOG=debug himalaya envelope list\n```\n\nFull trace with backtrace:\n\n```bash\nRUST_LOG=trace RUST_BACKTRACE=1 himalaya envelope list\n```\n\n## Tips\n\n- Use `himalaya --help` or `himalaya <command> --help` for detailed usage.\n- Message IDs are relative to the current folder; re-list after folder changes.\n- For composing rich emails with attachments, use MML syntax (see `references/message-composition.md`).\n- Store passwords securely using `pass`, system keyring, or a command that outputs the password.\n","skills/himalaya/references/configuration.md":"# Himalaya Configuration Reference\n\nConfiguration file location: `~/.config/himalaya/config.toml`\n\n## Minimal IMAP + SMTP Setup\n\n```toml\n[accounts.default]\nemail = \"user@example.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\n# IMAP backend for reading emails\nbackend.type = \"imap\"\nbackend.host = \"imap.example.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"user@example.com\"\nbackend.auth.type = \"password\"\nbackend.auth.raw = \"your-password\"\n\n# SMTP backend for sending emails\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.example.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"user@example.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.raw = \"your-password\"\n```\n\n## Password Options\n\n### Raw password (testing only, not recommended)\n\n```toml\nbackend.auth.raw = \"your-password\"\n```\n\n### Password from command (recommended)\n\n```toml\nbackend.auth.cmd = \"pass show email/imap\"\n# backend.auth.cmd = \"security find-generic-password -a user@example.com -s imap -w\"\n```\n\n### System keyring (requires keyring feature)\n\n```toml\nbackend.auth.keyring = \"imap-example\"\n```\n\nThen run `himalaya account configure <account>` to store the password.\n\n## Gmail Configuration\n\n```toml\n[accounts.gmail]\nemail = \"you@gmail.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\nbackend.type = \"imap\"\nbackend.host = \"imap.gmail.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@gmail.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show google/app-password\"\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.gmail.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@gmail.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show google/app-password\"\n```\n\n**Note:** Gmail requires an App Password if 2FA is enabled.\n\n## iCloud Configuration\n\n```toml\n[accounts.icloud]\nemail = \"you@icloud.com\"\ndisplay-name = \"Your Name\"\n\nbackend.type = \"imap\"\nbackend.host = \"imap.mail.me.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@icloud.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show icloud/app-password\"\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.mail.me.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@icloud.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show icloud/app-password\"\n```\n\n**Note:** Generate an app-specific password at appleid.apple.com\n\n## Folder Aliases\n\nMap custom folder names:\n\n```toml\n[accounts.default.folder.alias]\ninbox = \"INBOX\"\nsent = \"Sent\"\ndrafts = \"Drafts\"\ntrash = \"Trash\"\n```\n\n## Multiple Accounts\n\n```toml\n[accounts.personal]\nemail = \"personal@example.com\"\ndefault = true\n# ... backend config ...\n\n[accounts.work]\nemail = \"work@company.com\"\n# ... backend config ...\n```\n\nSwitch accounts with `--account`:\n\n```bash\nhimalaya --account work envelope list\n```\n\n## Notmuch Backend (local mail)\n\n```toml\n[accounts.local]\nemail = \"user@example.com\"\n\nbackend.type = \"notmuch\"\nbackend.db-path = \"~/.mail/.notmuch\"\n```\n\n## OAuth2 Authentication (for providers that support it)\n\n```toml\nbackend.auth.type = \"oauth2\"\nbackend.auth.client-id = \"your-client-id\"\nbackend.auth.client-secret.cmd = \"pass show oauth/client-secret\"\nbackend.auth.access-token.cmd = \"pass show oauth/access-token\"\nbackend.auth.refresh-token.cmd = \"pass show oauth/refresh-token\"\nbackend.auth.auth-url = \"https://provider.com/oauth/authorize\"\nbackend.auth.token-url = \"https://provider.com/oauth/token\"\n```\n\n## Additional Options\n\n### Signature\n\n```toml\n[accounts.default]\nsignature = \"Best regards,\\nYour Name\"\nsignature-delim = \"-- \\n\"\n```\n\n### Downloads directory\n\n```toml\n[accounts.default]\ndownloads-dir = \"~/Downloads/himalaya\"\n```\n\n### Editor for composing\n\nSet via environment variable:\n\n```bash\nexport EDITOR=\"vim\"\n```\n","skills/himalaya/references/message-composition.md":"# Message Composition with MML (MIME Meta Language)\n\nHimalaya uses MML for composing emails. MML is a simple XML-based syntax that compiles to MIME messages.\n\n## Basic Message Structure\n\nAn email message is a list of **headers** followed by a **body**, separated by a blank line:\n\n```\nFrom: sender@example.com\nTo: recipient@example.com\nSubject: Hello World\n\nThis is the message body.\n```\n\n## Headers\n\nCommon headers:\n\n- `From`: Sender address\n- `To`: Primary recipient(s)\n- `Cc`: Carbon copy recipients\n- `Bcc`: Blind carbon copy recipients\n- `Subject`: Message subject\n- `Reply-To`: Address for replies (if different from From)\n- `In-Reply-To`: Message ID being replied to\n\n### Address Formats\n\n```\nTo: user@example.com\nTo: John Doe <john@example.com>\nTo: \"John Doe\" <john@example.com>\nTo: user1@example.com, user2@example.com, \"Jane\" <jane@example.com>\n```\n\n## Plain Text Body\n\nSimple plain text email:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Plain Text Example\n\nHello, this is a plain text email.\nNo special formatting needed.\n\nBest,\nAlice\n```\n\n## MML for Rich Emails\n\n### Multipart Messages\n\nAlternative text/html parts:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Multipart Example\n\n<#multipart type=alternative>\nThis is the plain text version.\n<#part type=text/html>\n<html><body><h1>This is the HTML version</h1></body></html>\n<#/multipart>\n```\n\n### Attachments\n\nAttach a file:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: With Attachment\n\nHere is the document you requested.\n\n<#part filename=/path/to/document.pdf><#/part>\n```\n\nAttachment with custom name:\n\n```\n<#part filename=/path/to/file.pdf name=report.pdf><#/part>\n```\n\nMultiple attachments:\n\n```\n<#part filename=/path/to/doc1.pdf><#/part>\n<#part filename=/path/to/doc2.pdf><#/part>\n```\n\n### Inline Images\n\nEmbed an image inline:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Inline Image\n\n<#multipart type=related>\n<#part type=text/html>\n<html><body>\n<p>Check out this image:</p>\n<img src=\"cid:image1\">\n</body></html>\n<#part disposition=inline id=image1 filename=/path/to/image.png><#/part>\n<#/multipart>\n```\n\n### Mixed Content (Text + Attachments)\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Mixed Content\n\n<#multipart type=mixed>\n<#part type=text/plain>\nPlease find the attached files.\n\nBest,\nAlice\n<#part filename=/path/to/file1.pdf><#/part>\n<#part filename=/path/to/file2.zip><#/part>\n<#/multipart>\n```\n\n## MML Tag Reference\n\n### `<#multipart>`\n\nGroups multiple parts together.\n\n- `type=alternative`: Different representations of same content\n- `type=mixed`: Independent parts (text + attachments)\n- `type=related`: Parts that reference each other (HTML + images)\n\n### `<#part>`\n\nDefines a message part.\n\n- `type=<mime-type>`: Content type (e.g., `text/html`, `application/pdf`)\n- `filename=<path>`: File to attach\n- `name=<name>`: Display name for attachment\n- `disposition=inline`: Display inline instead of as attachment\n- `id=<cid>`: Content ID for referencing in HTML\n\n## Composing from CLI\n\n### Interactive compose\n\nOpens your `$EDITOR`:\n\n```bash\nhimalaya message write\n```\n\n### Reply (opens editor with quoted message)\n\n```bash\nhimalaya message reply 42\nhimalaya message reply 42 --all  # reply-all\n```\n\n### Forward\n\n```bash\nhimalaya message forward 42\n```\n\n### Send from stdin\n\n```bash\ncat message.txt | himalaya template send\n```\n\n### Prefill headers from CLI\n\n```bash\nhimalaya message write \\\n  -H \"To:recipient@example.com\" \\\n  -H \"Subject:Quick Message\" \\\n  \"Message body here\"\n```\n\n## Tips\n\n- The editor opens with a template; fill in headers and body.\n- Save and exit the editor to send; exit without saving to cancel.\n- MML parts are compiled to proper MIME when sending.\n- Use `himalaya message export --full` to inspect the raw MIME structure of received emails.\n","skills/imsg/SKILL.md":"---\nname: imsg\ndescription: iMessage/SMS CLI for listing chats, history, and sending messages via Messages.app.\nhomepage: https://imsg.to\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📨\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"imsg\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/imsg\",\n              \"bins\": [\"imsg\"],\n              \"label\": \"Install imsg (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# imsg\n\nUse `imsg` to read and send iMessage/SMS via macOS Messages.app.\n\n## When to Use\n\n✅ **USE this skill when:**\n\n- User explicitly asks to send iMessage or SMS\n- Reading iMessage conversation history\n- Checking recent Messages.app chats\n- Sending to phone numbers or Apple IDs\n\n## When NOT to Use\n\n❌ **DON'T use this skill when:**\n\n- Telegram messages → use `message` tool with `channel:telegram`\n- Signal messages → use Signal channel if configured\n- WhatsApp messages → use WhatsApp channel if configured\n- Discord messages → use `message` tool with `channel:discord`\n- Slack messages → use `slack` skill\n- Group chat management (adding/removing members) → not supported\n- Bulk/mass messaging → always confirm with user first\n- Replying in current conversation → just reply normally (OpenClaw routes automatically)\n\n## Requirements\n\n- macOS with Messages.app signed in\n- Full Disk Access for terminal\n- Automation permission for Messages.app (for sending)\n\n## Common Commands\n\n### List Chats\n\n```bash\nimsg chats --limit 10 --json\n```\n\n### View History\n\n```bash\n# By chat ID\nimsg history --chat-id 1 --limit 20 --json\n\n# With attachments info\nimsg history --chat-id 1 --limit 20 --attachments --json\n```\n\n### Watch for New Messages\n\n```bash\nimsg watch --chat-id 1 --attachments\n```\n\n### Send Messages\n\n```bash\n# Text only\nimsg send --to \"+14155551212\" --text \"Hello!\"\n\n# With attachment\nimsg send --to \"+14155551212\" --text \"Check this out\" --file /path/to/image.jpg\n\n# Specify service\nimsg send --to \"+14155551212\" --text \"Hi\" --service imessage\nimsg send --to \"+14155551212\" --text \"Hi\" --service sms\n```\n\n## Service Options\n\n- `--service imessage` — Force iMessage (requires recipient has iMessage)\n- `--service sms` — Force SMS (green bubble)\n- `--service auto` — Let Messages.app decide (default)\n\n## Safety Rules\n\n1. **Always confirm recipient and message content** before sending\n2. **Never send to unknown numbers** without explicit user approval\n3. **Be careful with attachments** — confirm file path exists\n4. **Rate limit yourself** — don't spam\n\n## Example Workflow\n\nUser: \"Text mom that I'll be late\"\n\n```bash\n# 1. Find mom's chat\nimsg chats --limit 20 --json | jq '.[] | select(.displayName | contains(\"Mom\"))'\n\n# 2. Confirm with user\n# \"Found Mom at +1555123456. Send 'I'll be late' via iMessage?\"\n\n# 3. Send after confirmation\nimsg send --to \"+1555123456\" --text \"I'll be late\"\n```\n","skills/mcporter/SKILL.md":"---\nname: mcporter\ndescription: Use the mcporter CLI to list, configure, auth, and call MCP servers/tools directly (HTTP or stdio), including ad-hoc servers, config edits, and CLI/type generation.\nhomepage: http://mcporter.dev\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📦\",\n        \"requires\": { \"bins\": [\"mcporter\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node\",\n              \"kind\": \"node\",\n              \"package\": \"mcporter\",\n              \"bins\": [\"mcporter\"],\n              \"label\": \"Install mcporter (node)\",\n            },\n          ],\n      },\n  }\n---\n\n# mcporter\n\nUse `mcporter` to work with MCP servers directly.\n\nQuick start\n\n- `mcporter list`\n- `mcporter list <server> --schema`\n- `mcporter call <server.tool> key=value`\n\nCall tools\n\n- Selector: `mcporter call linear.list_issues team=ENG limit:5`\n- Function syntax: `mcporter call \"linear.create_issue(title: \\\"Bug\\\")\"`\n- Full URL: `mcporter call https://api.example.com/mcp.fetch url:https://example.com`\n- Stdio: `mcporter call --stdio \"bun run ./server.ts\" scrape url=https://example.com`\n- JSON payload: `mcporter call <server.tool> --args '{\"limit\":5}'`\n\nAuth + config\n\n- OAuth: `mcporter auth <server | url> [--reset]`\n- Config: `mcporter config list|get|add|remove|import|login|logout`\n\nDaemon\n\n- `mcporter daemon start|status|stop|restart`\n\nCodegen\n\n- CLI: `mcporter generate-cli --server <name>` or `--command <url>`\n- Inspect: `mcporter inspect-cli <path> [--json]`\n- TS: `mcporter emit-ts <server> --mode client|types`\n\nNotes\n\n- Config default: `./config/mcporter.json` (override with `--config`).\n- Prefer `--output json` for machine-readable results.\n","skills/model-usage/SKILL.md":"---\nname: model-usage\ndescription: Use CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📊\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"codexbar\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew-cask\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/codexbar\",\n              \"bins\": [\"codexbar\"],\n              \"label\": \"Install CodexBar (brew cask)\",\n            },\n          ],\n      },\n  }\n---\n\n# Model usage\n\n## Overview\n\nGet per-model usage cost from CodexBar's local cost logs. Supports \"current model\" (most recent daily entry) or \"all models\" summaries for Codex or Claude.\n\nTODO: add Linux CLI support guidance once CodexBar CLI install path is documented for Linux.\n\n## Quick start\n\n1. Fetch cost JSON via CodexBar CLI or pass a JSON file.\n2. Use the bundled script to summarize by model.\n\n```bash\npython {baseDir}/scripts/model_usage.py --provider codex --mode current\npython {baseDir}/scripts/model_usage.py --provider codex --mode all\npython {baseDir}/scripts/model_usage.py --provider claude --mode all --format json --pretty\n```\n\n## Current model logic\n\n- Uses the most recent daily row with `modelBreakdowns`.\n- Picks the model with the highest cost in that row.\n- Falls back to the last entry in `modelsUsed` when breakdowns are missing.\n- Override with `--model <name>` when you need a specific model.\n\n## Inputs\n\n- Default: runs `codexbar cost --format json --provider <codex|claude>`.\n- File or stdin:\n\n```bash\ncodexbar cost --provider codex --format json > /tmp/cost.json\npython {baseDir}/scripts/model_usage.py --input /tmp/cost.json --mode all\ncat /tmp/cost.json | python {baseDir}/scripts/model_usage.py --input - --mode current\n```\n\n## Output\n\n- Text (default) or JSON (`--format json --pretty`).\n- Values are cost-only per model; tokens are not split by model in CodexBar output.\n\n## References\n\n- Read `references/codexbar-cli.md` for CLI flags and cost JSON fields.\n","skills/model-usage/references/codexbar-cli.md":"# CodexBar CLI quick ref (usage + cost)\n\n## Install\n\n- App: Preferences -> Advanced -> Install CLI\n- Repo: ./bin/install-codexbar-cli.sh\n\n## Commands\n\n- Usage snapshot (web/cli sources):\n  - codexbar usage --format json --pretty\n  - codexbar --provider all --format json\n- Local cost usage (Codex + Claude only):\n  - codexbar cost --format json --pretty\n  - codexbar cost --provider codex|claude --format json\n\n## Cost JSON fields\n\nThe payload is an array (one per provider).\n\n- provider, source, updatedAt\n- sessionTokens, sessionCostUSD\n- last30DaysTokens, last30DaysCostUSD\n- daily[]: date, inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, totalCost, modelsUsed, modelBreakdowns[]\n- modelBreakdowns[]: modelName, cost\n- totals: totalInputTokens, totalOutputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, totalCost\n\n## Notes\n\n- Cost usage is local-only. It reads JSONL logs under:\n  - Codex: ~/.codex/sessions/\\*_/_.jsonl\n  - Claude: ~/.config/claude/projects/**/\\*.jsonl or ~/.claude/projects/**/\\*.jsonl\n- If web usage is required (non-local), use codexbar usage (not cost).\n","skills/nano-pdf/SKILL.md":"---\nname: nano-pdf\ndescription: Edit PDFs with natural-language instructions using the nano-pdf CLI.\nhomepage: https://pypi.org/project/nano-pdf/\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📄\",\n        \"requires\": { \"bins\": [\"nano-pdf\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"uv\",\n              \"kind\": \"uv\",\n              \"package\": \"nano-pdf\",\n              \"bins\": [\"nano-pdf\"],\n              \"label\": \"Install nano-pdf (uv)\",\n            },\n          ],\n      },\n  }\n---\n\n# nano-pdf\n\nUse `nano-pdf` to apply edits to a specific page in a PDF using a natural-language instruction.\n\n## Quick start\n\n```bash\nnano-pdf edit deck.pdf 1 \"Change the title to 'Q3 Results' and fix the typo in the subtitle\"\n```\n\nNotes:\n\n- Page numbers are 0-based or 1-based depending on the tool’s version/config; if the result looks off by one, retry with the other.\n- Always sanity-check the output PDF before sending it out.\n","skills/node-connect/SKILL.md":"---\nname: node-connect\ndescription: Diagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps. Use when QR/setup code/manual connect fails, local Wi-Fi works but VPS/tailnet does not, or errors mention pairing required, unauthorized, bootstrap token invalid or expired, gateway.bind, gateway.remote.url, Tailscale, or plugins.entries.device-pair.config.publicUrl.\n---\n\n# Node Connect\n\nGoal: find the one real route from node -> gateway, verify OpenClaw is advertising that route, then fix pairing/auth.\n\n## Topology first\n\nDecide which case you are in before proposing fixes:\n\n- same machine / emulator / USB tunnel\n- same LAN / local Wi-Fi\n- same Tailscale tailnet\n- public URL / reverse proxy\n\nDo not mix them.\n\n- Local Wi-Fi problem: do not switch to Tailscale unless remote access is actually needed.\n- VPS / remote gateway problem: do not keep debugging `localhost` or LAN IPs.\n\n## If ambiguous, ask first\n\nIf the setup is unclear or the failure report is vague, ask short clarifying questions before diagnosing.\n\nAsk for:\n\n- which route they intend: same machine, same LAN, Tailscale tailnet, or public URL\n- whether they used QR/setup code or manual host/port\n- the exact app text/status/error, quoted exactly if possible\n- whether `openclaw devices list` shows a pending pairing request\n\nDo not guess from `can't connect`.\n\n## Canonical checks\n\nPrefer `openclaw qr --json`. It uses the same setup-code payload Android scans.\n\n```bash\nopenclaw config get gateway.mode\nopenclaw config get gateway.bind\nopenclaw config get gateway.tailscale.mode\nopenclaw config get gateway.remote.url\nopenclaw config get gateway.auth.mode\nopenclaw config get gateway.auth.allowTailscale\nopenclaw config get plugins.entries.device-pair.config.publicUrl\nopenclaw qr --json\nopenclaw devices list\nopenclaw nodes status\n```\n\nIf this OpenClaw instance is pointed at a remote gateway, also run:\n\n```bash\nopenclaw qr --remote --json\n```\n\nIf Tailscale is part of the story:\n\n```bash\ntailscale status --json\n```\n\n## Read the result, not guesses\n\n`openclaw qr --json` success means:\n\n- `gatewayUrl`: this is the actual endpoint the app should use.\n- `urlSource`: this tells you which config path won.\n\nCommon good sources:\n\n- `gateway.bind=lan`: same Wi-Fi / LAN only\n- `gateway.bind=tailnet`: direct tailnet access\n- `gateway.tailscale.mode=serve` or `gateway.tailscale.mode=funnel`: Tailscale route\n- `plugins.entries.device-pair.config.publicUrl`: explicit public/reverse-proxy route\n- `gateway.remote.url`: remote gateway route\n\n## Root-cause map\n\nIf `openclaw qr --json` says `Gateway is only bound to loopback`:\n\n- remote node cannot connect yet\n- fix the route, then generate a fresh setup code\n- `gateway.bind=auto` is not enough if the effective QR route is still loopback\n- same LAN: use `gateway.bind=lan`\n- same tailnet: prefer `gateway.tailscale.mode=serve` or use `gateway.bind=tailnet`\n- public internet: set a real `plugins.entries.device-pair.config.publicUrl` or `gateway.remote.url`\n\nIf `gateway.bind=tailnet set, but no tailnet IP was found`:\n\n- gateway host is not actually on Tailscale\n\nIf `qr --remote requires gateway.remote.url`:\n\n- remote-mode config is incomplete\n\nIf the app says `pairing required`:\n\n- network route and auth worked\n- approve the pending device\n\n```bash\nopenclaw devices list\nopenclaw devices approve --latest\n```\n\nIf the app says `bootstrap token invalid or expired`:\n\n- old setup code\n- generate a fresh one and rescan\n- do this after any URL/auth fix too\n\nIf the app says `unauthorized`:\n\n- wrong token/password, or wrong Tailscale expectation\n- for Tailscale Serve, `gateway.auth.allowTailscale` must match the intended flow\n- otherwise use explicit token/password\n\n## Fast heuristics\n\n- Same Wi-Fi setup + gateway advertises `127.0.0.1`, `localhost`, or loopback-only config: wrong.\n- Remote setup + setup/manual uses private LAN IP: wrong.\n- Tailnet setup + gateway advertises LAN IP instead of MagicDNS / tailnet route: wrong.\n- Public URL set but QR still advertises something else: inspect `urlSource`; config is not what you think.\n- `openclaw devices list` shows pending requests: stop changing network config and approve first.\n\n## Fix style\n\nReply with one concrete diagnosis and one route.\n\nIf there is not enough signal yet, ask for setup + exact app text instead of guessing.\n\nGood:\n\n- `The gateway is still loopback-only, so a node on another network can never reach it. Enable Tailscale Serve, restart the gateway, run openclaw qr again, rescan, then approve the pending device pairing.`\n\nBad:\n\n- `Maybe LAN, maybe Tailscale, maybe port forwarding, maybe public URL.`\n","skills/notion/SKILL.md":"---\nname: notion\ndescription: Notion API for creating and managing pages, databases, and blocks.\nhomepage: https://developers.notion.com\nmetadata:\n  {\n    \"emoji\": \"📝\",\n    \"requires\": { \"env\": [\"NOTION_API_KEY\"] },\n    \"primaryEnv\": \"NOTION_API_KEY\"\n  }\n---\n\n# notion\n\nUse the Notion API to create/read/update pages, data sources (databases), and blocks.\n\n## Setup\n\n1. Create an integration at https://notion.so/my-integrations\n2. Copy the API key (starts with `ntn_` or `secret_`)\n3. Store it:\n\n```bash\nmkdir -p ~/.config/notion\necho \"ntn_your_key_here\" > ~/.config/notion/api_key\n```\n\n4. Share target pages/databases with your integration (click \"...\" → \"Connect to\" → your integration name)\n\n## API Basics\n\nAll requests need:\n\n```bash\nNOTION_KEY=$(cat ~/.config/notion/api_key)\ncurl -X GET \"https://api.notion.com/v1/...\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\"\n```\n\n> **Note:** The `Notion-Version` header is required. This skill uses `2025-09-03` (latest). In this version, databases are called \"data sources\" in the API.\n\n## Common Operations\n\n**Search for pages and data sources:**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/search\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"query\": \"page title\"}'\n```\n\n**Get page:**\n\n```bash\ncurl \"https://api.notion.com/v1/pages/{page_id}\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\"\n```\n\n**Get page content (blocks):**\n\n```bash\ncurl \"https://api.notion.com/v1/blocks/{page_id}/children\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\"\n```\n\n**Create page in a data source:**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/pages\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"parent\": {\"database_id\": \"xxx\"},\n    \"properties\": {\n      \"Name\": {\"title\": [{\"text\": {\"content\": \"New Item\"}}]},\n      \"Status\": {\"select\": {\"name\": \"Todo\"}}\n    }\n  }'\n```\n\n**Query a data source (database):**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/data_sources/{data_source_id}/query\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"filter\": {\"property\": \"Status\", \"select\": {\"equals\": \"Active\"}},\n    \"sorts\": [{\"property\": \"Date\", \"direction\": \"descending\"}]\n  }'\n```\n\n**Create a data source (database):**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/data_sources\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"parent\": {\"page_id\": \"xxx\"},\n    \"title\": [{\"text\": {\"content\": \"My Database\"}}],\n    \"properties\": {\n      \"Name\": {\"title\": {}},\n      \"Status\": {\"select\": {\"options\": [{\"name\": \"Todo\"}, {\"name\": \"Done\"}]}},\n      \"Date\": {\"date\": {}}\n    }\n  }'\n```\n\n**Update page properties:**\n\n```bash\ncurl -X PATCH \"https://api.notion.com/v1/pages/{page_id}\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"properties\": {\"Status\": {\"select\": {\"name\": \"Done\"}}}}'\n```\n\n**Add blocks to page:**\n\n```bash\ncurl -X PATCH \"https://api.notion.com/v1/blocks/{page_id}/children\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"children\": [\n      {\"object\": \"block\", \"type\": \"paragraph\", \"paragraph\": {\"rich_text\": [{\"text\": {\"content\": \"Hello\"}}]}}\n    ]\n  }'\n```\n\n## Property Types\n\nCommon property formats for database items:\n\n- **Title:** `{\"title\": [{\"text\": {\"content\": \"...\"}}]}`\n- **Rich text:** `{\"rich_text\": [{\"text\": {\"content\": \"...\"}}]}`\n- **Select:** `{\"select\": {\"name\": \"Option\"}}`\n- **Multi-select:** `{\"multi_select\": [{\"name\": \"A\"}, {\"name\": \"B\"}]}`\n- **Date:** `{\"date\": {\"start\": \"2024-01-15\", \"end\": \"2024-01-16\"}}`\n- **Checkbox:** `{\"checkbox\": true}`\n- **Number:** `{\"number\": 42}`\n- **URL:** `{\"url\": \"https://...\"}`\n- **Email:** `{\"email\": \"a@b.com\"}`\n- **Relation:** `{\"relation\": [{\"id\": \"page_id\"}]}`\n\n## Key Differences in 2025-09-03\n\n- **Databases → Data Sources:** Use `/data_sources/` endpoints for queries and retrieval\n- **Two IDs:** Each database now has both a `database_id` and a `data_source_id`\n  - Use `database_id` when creating pages (`parent: {\"database_id\": \"...\"}`)\n  - Use `data_source_id` when querying (`POST /v1/data_sources/{id}/query`)\n- **Search results:** Databases return as `\"object\": \"data_source\"` with their `data_source_id`\n- **Parent in responses:** Pages show `parent.data_source_id` alongside `parent.database_id`\n- **Finding the data_source_id:** Search for the database, or call `GET /v1/data_sources/{data_source_id}`\n\n## Notes\n\n- Page/database IDs are UUIDs (with or without dashes)\n- The API cannot set database view filters — that's UI-only\n- Rate limit: ~3 requests/second average, with `429 rate_limited` responses using `Retry-After`\n- Append block children: up to 100 children per request, up to two levels of nesting in a single append request\n- Payload size limits: up to 1000 block elements and 500KB overall\n- Use `is_inline: true` when creating data sources to embed them in pages\n","skills/obsidian/SKILL.md":"---\nname: obsidian\ndescription: Work with Obsidian vaults (plain Markdown notes) and automate via obsidian-cli.\nhomepage: https://help.obsidian.md\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"💎\",\n        \"requires\": { \"bins\": [\"obsidian-cli\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"yakitrak/yakitrak/obsidian-cli\",\n              \"bins\": [\"obsidian-cli\"],\n              \"label\": \"Install obsidian-cli (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# Obsidian\n\nObsidian vault = a normal folder on disk.\n\nVault structure (typical)\n\n- Notes: `*.md` (plain text Markdown; edit with any editor)\n- Config: `.obsidian/` (workspace + plugin settings; usually don’t touch from scripts)\n- Canvases: `*.canvas` (JSON)\n- Attachments: whatever folder you chose in Obsidian settings (images/PDFs/etc.)\n\n## Find the active vault(s)\n\nObsidian desktop tracks vaults here (source of truth):\n\n- `~/Library/Application Support/obsidian/obsidian.json`\n\n`obsidian-cli` resolves vaults from that file; vault name is typically the **folder name** (path suffix).\n\nFast “what vault is active / where are the notes?”\n\n- If you’ve already set a default: `obsidian-cli print-default --path-only`\n- Otherwise, read `~/Library/Application Support/obsidian/obsidian.json` and use the vault entry with `\"open\": true`.\n\nNotes\n\n- Multiple vaults common (iCloud vs `~/Documents`, work/personal, etc.). Don’t guess; read config.\n- Avoid writing hardcoded vault paths into scripts; prefer reading the config or using `print-default`.\n\n## obsidian-cli quick start\n\nPick a default vault (once):\n\n- `obsidian-cli set-default \"<vault-folder-name>\"`\n- `obsidian-cli print-default` / `obsidian-cli print-default --path-only`\n\nSearch\n\n- `obsidian-cli search \"query\"` (note names)\n- `obsidian-cli search-content \"query\"` (inside notes; shows snippets + lines)\n\nCreate\n\n- `obsidian-cli create \"Folder/New note\" --content \"...\" --open`\n- Requires Obsidian URI handler (`obsidian://…`) working (Obsidian installed).\n- Avoid creating notes under “hidden” dot-folders (e.g. `.something/...`) via URI; Obsidian may refuse.\n\nMove/rename (safe refactor)\n\n- `obsidian-cli move \"old/path/note\" \"new/path/note\"`\n- Updates `[[wikilinks]]` and common Markdown links across the vault (this is the main win vs `mv`).\n\nDelete\n\n- `obsidian-cli delete \"path/note\"`\n\nPrefer direct edits when appropriate: open the `.md` file and change it; Obsidian will pick it up.\n","skills/openai-whisper-api/SKILL.md":"---\nname: openai-whisper-api\ndescription: Transcribe audio via OpenAI Audio Transcriptions API (Whisper).\nhomepage: https://platform.openai.com/docs/guides/speech-to-text\nmetadata:\n  {\n    \"emoji\": \"🌐\",\n    \"requires\": { \"bins\": [\"curl\"], \"env\": [\"OPENAI_API_KEY\"] },\n    \"primaryEnv\": \"OPENAI_API_KEY\"\n  }\n---\n\n# OpenAI Whisper API (curl)\n\nTranscribe an audio file via OpenAI's `/v1/audio/transcriptions` endpoint. Set `OPENAI_BASE_URL` to use an OpenAI-compatible proxy or local gateway.\n\n## Quick start\n\n```bash\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@/path/to/audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  > transcript.txt\n```\n\nDefaults:\n\n- Model: `whisper-1`\n- Output format: `text`\n\n## Options\n\n```bash\n# With language hint\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.ogg\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  -F \"language=en\" \\\n  > transcript.txt\n\n# With speaker hint (prompt)\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  -F \"prompt=Speaker names: Peter, Daniel\" \\\n  > transcript.txt\n\n# JSON output\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=json\" \\\n  > transcript.json\n```\n\n## Custom base URL\n\nSet `OPENAI_BASE_URL` to use an OpenAI-compatible proxy or local gateway:\n\n```bash\nAPI_BASE=\"${OPENAI_BASE_URL:-https://api.openai.com/v1}\"\ncurl -sS \"${API_BASE}/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  > transcript.txt\n```\n\n## API key\n\nSet `OPENAI_API_KEY` environment variable before running commands.\n","skills/openai-whisper/SKILL.md":"---\nname: openai-whisper\ndescription: Local speech-to-text with the Whisper CLI (no API key).\nhomepage: https://openai.com/research/whisper\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🎤\",\n        \"requires\": { \"bins\": [\"whisper\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"openai-whisper\",\n              \"bins\": [\"whisper\"],\n              \"label\": \"Install OpenAI Whisper (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# Whisper (CLI)\n\nUse `whisper` to transcribe audio locally.\n\nQuick start\n\n- `whisper /path/audio.mp3 --model medium --output_format txt --output_dir .`\n- `whisper /path/audio.m4a --task translate --output_format srt`\n\nNotes\n\n- Models download to `~/.cache/whisper` on first run.\n- `--model` defaults to `turbo` on this install.\n- Use smaller models for speed, larger for accuracy.\n","skills/openhue/SKILL.md":"---\nname: openhue\ndescription: Control Philips Hue lights and scenes via the OpenHue CLI.\nhomepage: https://www.openhue.io/cli\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"💡\",\n        \"requires\": { \"bins\": [\"openhue\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"openhue/cli/openhue-cli\",\n              \"bins\": [\"openhue\"],\n              \"label\": \"Install OpenHue CLI (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# OpenHue CLI\n\nUse `openhue` to control Philips Hue lights and scenes via a Hue Bridge.\n\n## When to Use\n\n✅ **USE this skill when:**\n\n- \"Turn on/off the lights\"\n- \"Dim the living room lights\"\n- \"Set a scene\" or \"movie mode\"\n- Controlling specific Hue rooms or zones\n- Adjusting brightness, color, or color temperature\n\n## When NOT to Use\n\n❌ **DON'T use this skill when:**\n\n- Non-Hue smart devices (other brands) → not supported\n- HomeKit scenes or Shortcuts → use Apple's ecosystem\n- TV or entertainment system control\n- Thermostat or HVAC\n- Smart plugs (unless Hue smart plugs)\n\n## Common Commands\n\n### List Resources\n\n```bash\nopenhue get light       # List all lights\nopenhue get room        # List all rooms\nopenhue get scene       # List all scenes\n```\n\n### Control Lights\n\n```bash\n# Turn on/off\nopenhue set light \"Bedroom Lamp\" --on\nopenhue set light \"Bedroom Lamp\" --off\n\n# Brightness (0-100)\nopenhue set light \"Bedroom Lamp\" --on --brightness 50\n\n# Color temperature (warm to cool: 153-500 mirek)\nopenhue set light \"Bedroom Lamp\" --on --temperature 300\n\n# Color (by name or hex)\nopenhue set light \"Bedroom Lamp\" --on --color red\nopenhue set light \"Bedroom Lamp\" --on --rgb \"#FF5500\"\n```\n\n### Control Rooms\n\n```bash\n# Turn off entire room\nopenhue set room \"Bedroom\" --off\n\n# Set room brightness\nopenhue set room \"Bedroom\" --on --brightness 30\n```\n\n### Scenes\n\n```bash\n# Activate scene\nopenhue set scene \"Relax\" --room \"Bedroom\"\nopenhue set scene \"Concentrate\" --room \"Office\"\n```\n\n## Quick Presets\n\n```bash\n# Bedtime (dim warm)\nopenhue set room \"Bedroom\" --on --brightness 20 --temperature 450\n\n# Work mode (bright cool)\nopenhue set room \"Office\" --on --brightness 100 --temperature 250\n\n# Movie mode (dim)\nopenhue set room \"Living Room\" --on --brightness 10\n```\n\n## Notes\n\n- Bridge must be on local network\n- First run requires button press on Hue bridge to pair\n- Colors only work on color-capable bulbs (not white-only)\n","skills/oracle/SKILL.md":"---\nname: oracle\ndescription: Best practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns).\nhomepage: https://askoracle.dev\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🧿\",\n        \"requires\": { \"bins\": [\"oracle\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node\",\n              \"kind\": \"node\",\n              \"package\": \"@steipete/oracle\",\n              \"bins\": [\"oracle\"],\n              \"label\": \"Install oracle (node)\",\n            },\n          ],\n      },\n  }\n---\n\n# oracle — best use\n\nOracle bundles your prompt + selected files into one “one-shot” request so another model can answer with real repo context (API or browser automation). Treat output as advisory: verify against code + tests.\n\n## Main use case (browser, GPT‑5.2 Pro)\n\nDefault workflow here: `--engine browser` with GPT‑5.2 Pro in ChatGPT. This is the common “long think” path: ~10 minutes to ~1 hour is normal; expect a stored session you can reattach to.\n\nRecommended defaults:\n\n- Engine: browser (`--engine browser`)\n- Model: GPT‑5.2 Pro (`--model gpt-5.2-pro` or `--model \"5.2 Pro\"`)\n\n## Golden path\n\n1. Pick a tight file set (fewest files that still contain the truth).\n2. Preview payload + token spend (`--dry-run` + `--files-report`).\n3. Use browser mode for the usual GPT‑5.2 Pro workflow; use API only when you explicitly want it.\n4. If the run detaches/timeouts: reattach to the stored session (don’t re-run).\n\n## Commands (preferred)\n\n- Help:\n  - `oracle --help`\n  - If the binary isn’t installed: `npx -y @steipete/oracle --help` (avoid `pnpx` here; sqlite bindings).\n\n- Preview (no tokens):\n  - `oracle --dry-run summary -p \"<task>\" --file \"src/**\" --file \"!**/*.test.*\"`\n  - `oracle --dry-run full -p \"<task>\" --file \"src/**\"`\n\n- Token sanity:\n  - `oracle --dry-run summary --files-report -p \"<task>\" --file \"src/**\"`\n\n- Browser run (main path; long-running is normal):\n  - `oracle --engine browser --model gpt-5.2-pro -p \"<task>\" --file \"src/**\"`\n\n- Manual paste fallback:\n  - `oracle --render --copy -p \"<task>\" --file \"src/**\"`\n  - Note: `--copy` is a hidden alias for `--copy-markdown`.\n\n## Attaching files (`--file`)\n\n`--file` accepts files, directories, and globs. You can pass it multiple times; entries can be comma-separated.\n\n- Include:\n  - `--file \"src/**\"`\n  - `--file src/index.ts`\n  - `--file docs --file README.md`\n\n- Exclude:\n  - `--file \"src/**\" --file \"!src/**/*.test.ts\" --file \"!**/*.snap\"`\n\n- Defaults (implementation behavior):\n  - Default-ignored dirs: `node_modules`, `dist`, `coverage`, `.git`, `.turbo`, `.next`, `build`, `tmp` (skipped unless explicitly passed as literal dirs/files).\n  - Honors `.gitignore` when expanding globs.\n  - Does not follow symlinks.\n  - Dotfiles filtered unless opted in via pattern (e.g. `--file \".github/**\"`).\n  - Files > 1 MB rejected.\n\n## Engines (API vs browser)\n\n- Auto-pick: `api` when `OPENAI_API_KEY` is set; otherwise `browser`.\n- Browser supports GPT + Gemini only; use `--engine api` for Claude/Grok/Codex or multi-model runs.\n- Browser attachments:\n  - `--browser-attachments auto|never|always` (auto pastes inline up to ~60k chars then uploads).\n- Remote browser host:\n  - Host: `oracle serve --host 0.0.0.0 --port 9473 --token <secret>`\n  - Client: `oracle --engine browser --remote-host <host:port> --remote-token <secret> -p \"<task>\" --file \"src/**\"`\n\n## Sessions + slugs\n\n- Stored under `~/.oracle/sessions` (override with `ORACLE_HOME_DIR`).\n- Runs may detach or take a long time (browser + GPT‑5.2 Pro often does). If the CLI times out: don’t re-run; reattach.\n  - List: `oracle status --hours 72`\n  - Attach: `oracle session <id> --render`\n- Use `--slug \"<3-5 words>\"` to keep session IDs readable.\n- Duplicate prompt guard exists; use `--force` only when you truly want a fresh run.\n\n## Prompt template (high signal)\n\nOracle starts with **zero** project knowledge. Assume the model cannot infer your stack, build tooling, conventions, or “obvious” paths. Include:\n\n- Project briefing (stack + build/test commands + platform constraints).\n- “Where things live” (key directories, entrypoints, config files, boundaries).\n- Exact question + what you tried + the error text (verbatim).\n- Constraints (“don’t change X”, “must keep public API”, etc).\n- Desired output (“return patch plan + tests”, “give 3 options with tradeoffs”).\n\n## Safety\n\n- Don’t attach secrets by default (`.env`, key files, auth tokens). Redact aggressively; share only what’s required.\n\n## “Exhaustive prompt” restoration pattern\n\nFor long investigations, write a standalone prompt + file set so you can rerun days later:\n\n- 6–30 sentence project briefing + the goal.\n- Repro steps + exact errors + what you tried.\n- Attach all context files needed (entrypoints, configs, key modules, docs).\n\nOracle runs are one-shot; the model doesn’t remember prior runs. “Restoring context” means re-running with the same prompt + `--file …` set (or reattaching a still-running stored session).\n","skills/ordercli/SKILL.md":"---\nname: ordercli\ndescription: Foodora-only CLI for checking past orders and active order status (Deliveroo WIP).\nhomepage: https://ordercli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🛵\",\n        \"requires\": { \"bins\": [\"ordercli\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/ordercli\",\n              \"bins\": [\"ordercli\"],\n              \"label\": \"Install ordercli (brew)\",\n            },\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/ordercli/cmd/ordercli@latest\",\n              \"bins\": [\"ordercli\"],\n              \"label\": \"Install ordercli (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# ordercli\n\nUse `ordercli` to check past orders and track active order status (Foodora only right now).\n\nQuick start (Foodora)\n\n- `ordercli foodora countries`\n- `ordercli foodora config set --country AT`\n- `ordercli foodora login --email you@example.com --password-stdin`\n- `ordercli foodora orders`\n- `ordercli foodora history --limit 20`\n- `ordercli foodora history show <orderCode>`\n\nOrders\n\n- Active list (arrival/status): `ordercli foodora orders`\n- Watch: `ordercli foodora orders --watch`\n- Active order detail: `ordercli foodora order <orderCode>`\n- History detail JSON: `ordercli foodora history show <orderCode> --json`\n\nReorder (adds to cart)\n\n- Preview: `ordercli foodora reorder <orderCode>`\n- Confirm: `ordercli foodora reorder <orderCode> --confirm`\n- Address: `ordercli foodora reorder <orderCode> --confirm --address-id <id>`\n\nCloudflare / bot protection\n\n- Browser login: `ordercli foodora login --email you@example.com --password-stdin --browser`\n- Reuse profile: `--browser-profile \"$HOME/Library/Application Support/ordercli/browser-profile\"`\n- Import Chrome cookies: `ordercli foodora cookies chrome --profile \"Default\"`\n\nSession import (no password)\n\n- `ordercli foodora session chrome --url https://www.foodora.at/ --profile \"Default\"`\n- `ordercli foodora session refresh --client-id android`\n\nDeliveroo (WIP, not working yet)\n\n- Requires `DELIVEROO_BEARER_TOKEN` (optional `DELIVEROO_COOKIE`).\n- `ordercli deliveroo config set --market uk`\n- `ordercli deliveroo history`\n\nNotes\n\n- Use `--config /tmp/ordercli.json` for testing.\n- Confirm before any reorder or cart-changing action.\n","skills/peekaboo/SKILL.md":"---\nname: peekaboo\ndescription: Capture and automate macOS UI with the Peekaboo CLI.\nhomepage: https://peekaboo.boo\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"👀\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"peekaboo\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/peekaboo\",\n              \"bins\": [\"peekaboo\"],\n              \"label\": \"Install Peekaboo (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# Peekaboo\n\nPeekaboo is a full macOS UI automation CLI: capture/inspect screens, target UI\nelements, drive input, and manage apps/windows/menus. Commands share a snapshot\ncache and support `--json`/`-j` for scripting. Run `peekaboo` or\n`peekaboo <cmd> --help` for flags; `peekaboo --version` prints build metadata.\nTip: run via `polter peekaboo` to ensure fresh builds.\n\n## Features (all CLI capabilities, excluding agent/MCP)\n\nCore\n\n- `bridge`: inspect Peekaboo Bridge host connectivity\n- `capture`: live capture or video ingest + frame extraction\n- `clean`: prune snapshot cache and temp files\n- `config`: init/show/edit/validate, providers, models, credentials\n- `image`: capture screenshots (screen/window/menu bar regions)\n- `learn`: print the full agent guide + tool catalog\n- `list`: apps, windows, screens, menubar, permissions\n- `permissions`: check Screen Recording/Accessibility status\n- `run`: execute `.peekaboo.json` scripts\n- `sleep`: pause execution for a duration\n- `tools`: list available tools with filtering/display options\n\nInteraction\n\n- `click`: target by ID/query/coords with smart waits\n- `drag`: drag & drop across elements/coords/Dock\n- `hotkey`: modifier combos like `cmd,shift,t`\n- `move`: cursor positioning with optional smoothing\n- `paste`: set clipboard -> paste -> restore\n- `press`: special-key sequences with repeats\n- `scroll`: directional scrolling (targeted + smooth)\n- `swipe`: gesture-style drags between targets\n- `type`: text + control keys (`--clear`, delays)\n\nSystem\n\n- `app`: launch/quit/relaunch/hide/unhide/switch/list apps\n- `clipboard`: read/write clipboard (text/images/files)\n- `dialog`: click/input/file/dismiss/list system dialogs\n- `dock`: launch/right-click/hide/show/list Dock items\n- `menu`: click/list application menus + menu extras\n- `menubar`: list/click status bar items\n- `open`: enhanced `open` with app targeting + JSON payloads\n- `space`: list/switch/move-window (Spaces)\n- `visualizer`: exercise Peekaboo visual feedback animations\n- `window`: close/minimize/maximize/move/resize/focus/list\n\nVision\n\n- `see`: annotated UI maps, snapshot IDs, optional analysis\n\nGlobal runtime flags\n\n- `--json`/`-j`, `--verbose`/`-v`, `--log-level <level>`\n- `--no-remote`, `--bridge-socket <path>`\n\n## Quickstart (happy path)\n\n```bash\npeekaboo permissions\npeekaboo list apps --json\npeekaboo see --annotate --path /tmp/peekaboo-see.png\npeekaboo click --on B1\npeekaboo type \"Hello\" --return\n```\n\n## Common targeting parameters (most interaction commands)\n\n- App/window: `--app`, `--pid`, `--window-title`, `--window-id`, `--window-index`\n- Snapshot targeting: `--snapshot` (ID from `see`; defaults to latest)\n- Element/coords: `--on`/`--id` (element ID), `--coords x,y`\n- Focus control: `--no-auto-focus`, `--space-switch`, `--bring-to-current-space`,\n  `--focus-timeout-seconds`, `--focus-retry-count`\n\n## Common capture parameters\n\n- Output: `--path`, `--format png|jpg`, `--retina`\n- Targeting: `--mode screen|window|frontmost`, `--screen-index`,\n  `--window-title`, `--window-id`\n- Analysis: `--analyze \"prompt\"`, `--annotate`\n- Capture engine: `--capture-engine auto|classic|cg|modern|sckit`\n\n## Common motion/typing parameters\n\n- Timing: `--duration` (drag/swipe), `--steps`, `--delay` (type/scroll/press)\n- Human-ish movement: `--profile human|linear`, `--wpm` (typing)\n- Scroll: `--direction up|down|left|right`, `--amount <ticks>`, `--smooth`\n\n## Examples\n\n### See -> click -> type (most reliable flow)\n\n```bash\npeekaboo see --app Safari --window-title \"Login\" --annotate --path /tmp/see.png\npeekaboo click --on B3 --app Safari\npeekaboo type \"user@example.com\" --app Safari\npeekaboo press tab --count 1 --app Safari\npeekaboo type \"supersecret\" --app Safari --return\n```\n\n### Target by window id\n\n```bash\npeekaboo list windows --app \"Visual Studio Code\" --json\npeekaboo click --window-id 12345 --coords 120,160\npeekaboo type \"Hello from Peekaboo\" --window-id 12345\n```\n\n### Capture screenshots + analyze\n\n```bash\npeekaboo image --mode screen --screen-index 0 --retina --path /tmp/screen.png\npeekaboo image --app Safari --window-title \"Dashboard\" --analyze \"Summarize KPIs\"\npeekaboo see --mode screen --screen-index 0 --analyze \"Summarize the dashboard\"\n```\n\n### Live capture (motion-aware)\n\n```bash\npeekaboo capture live --mode region --region 100,100,800,600 --duration 30 \\\n  --active-fps 8 --idle-fps 2 --highlight-changes --path /tmp/capture\n```\n\n### App + window management\n\n```bash\npeekaboo app launch \"Safari\" --open https://example.com\npeekaboo window focus --app Safari --window-title \"Example\"\npeekaboo window set-bounds --app Safari --x 50 --y 50 --width 1200 --height 800\npeekaboo app quit --app Safari\n```\n\n### Menus, menubar, dock\n\n```bash\npeekaboo menu click --app Safari --item \"New Window\"\npeekaboo menu click --app TextEdit --path \"Format > Font > Show Fonts\"\npeekaboo menu click-extra --title \"WiFi\"\npeekaboo dock launch Safari\npeekaboo menubar list --json\n```\n\n### Mouse + gesture input\n\n```bash\npeekaboo move 500,300 --smooth\npeekaboo drag --from B1 --to T2\npeekaboo swipe --from-coords 100,500 --to-coords 100,200 --duration 800\npeekaboo scroll --direction down --amount 6 --smooth\n```\n\n### Keyboard input\n\n```bash\npeekaboo hotkey --keys \"cmd,shift,t\"\npeekaboo press escape\npeekaboo type \"Line 1\\nLine 2\" --delay 10\n```\n\nNotes\n\n- Requires Screen Recording + Accessibility permissions.\n- Use `peekaboo see --annotate` to identify targets before clicking.\n","skills/powerpoint/SKILL.md":"---\nname: powerpoint\ndescription: Create designed, editable PowerPoint .pptx presentations with PptxGenJS. Use when the user asks to create, generate, update, or inspect a deck, slide deck, presentation, or .pptx file.\n---\n\n# PowerPoint\n\nUse this skill whenever a PowerPoint deck is involved. For new decks, pass a trusted PptxGenJS build script directly to the `pptx_write` tool. For filling or editing an existing template, call `pptx_template_analyze` first and then `pptx_template_fill` with the exact IDs returned by analysis.\n\n## Workflow\n\n1. Decide the deck outline and choose a visual system: palette, typography, repeated motif, and slide rhythm.\n2. Write JavaScript module content that exports `default async function build(pptx, ctx)` or named `build(pptx, ctx)`.\n3. In the script, add slides directly with PptxGenJS. Do not generate HTML for this workflow.\n4. Call `pptx_write` with `path`, `script`, optional `assets_dir`, and optional `data`.\n5. Verify the result with `pptx_read`; for visual QA, convert the PPTX to images if the environment has LibreOffice and Poppler.\n\n## Template Workflow\n\n- Use `pptx_template_analyze` when the user provides a `.pptx` template or wants to preserve existing layouts, charts, images, tables, or SmartArt.\n- Build a `template_fill_pptx_plan.v1` plan from the returned slide IDs and object IDs, then call `pptx_template_fill`.\n- For SmartArt, use `smartarts[*].smartart_id` and `smartarts[*].nodes[*].node_id` in `smartart_edits`. This edits existing node text only; it does not create, delete, or relayout SmartArt nodes.\n\n## Script Creation\n\n- Put the complete JavaScript module in the `script` argument.\n- Do not use `local_file_write` or shell commands to create a temporary `.mjs` file for this workflow.\n- If revising a deck, update the `script` content and call `pptx_write` again.\n\n## Tool Contract\n\n```json\n{\n  \"tool\": \"pptx_write\",\n  \"arguments\": {\n    \"path\": \"deck.pptx\",\n    \"script\": \"export default async function build(pptx, ctx) {\\\\n  pptx.layout = \\\"LAYOUT_WIDE\\\";\\\\n}\",\n    \"assets_dir\": \"/absolute/path/to/assets\",\n    \"data\": {\"title\": \"Quarterly Review\"}\n  }\n}\n```\n\nThe worker creates the PptxGenJS instance and writes the output file. The script only adds slides and content.\n\n```javascript\nexport default async function build(pptx, ctx) {\n  pptx.layout = \"LAYOUT_WIDE\";\n  pptx.author = \"OpenAgent\";\n\n  const slide = pptx.addSlide();\n  slide.background = { color: \"FFFFFF\" };\n  slide.addText(\"Title\", {\n    x: 0.6, y: 0.4, w: 8, h: 0.6,\n    fontSize: 36, bold: true, color: \"1F2937\",\n    margin: 0,\n  });\n  slide.addNotes(\"speaker notes\");\n}\n```\n\n`ctx` includes:\n\n- `ctx.data`: JSON data passed from the tool call.\n- `ctx.assetsDir`: resolved asset directory.\n- `ctx.outPath`: final PPTX path.\n- `ctx.resolveAsset(\"image.png\")`: absolute path under `assets_dir`.\n- `ctx.imageData(\"image.png\")`: base64 image data URL.\n- `ctx.iconSvgData(\"check\", \"16A34A\")`: Font Awesome solid icon as SVG data.\n\n## Design Rules\n\n- Avoid plain white bullet decks. Every slide should have a visual element: shape, image, chart, icon, timeline, stat callout, or diagram.\n- Vary layouts across the deck: title, divider, two-column, card grid, process flow, quote/callout, and conclusion.\n- Pick topic-specific colors. Use one dominant color, one or two supporting tones, and one accent.\n- Use strong hierarchy: titles around 36-44 pt, section labels around 20-24 pt, body text around 14-18 pt.\n- Keep at least 0.5 inch margins and consistent gaps around 0.3-0.5 inch.\n- Use editable text wherever practical; use images for photos, screenshots, logos, or complex visual backgrounds.\n- Add speaker notes when useful; `pptx_read` can surface them later.\n\n## PptxGenJS Reference\n\nFor API patterns, chart examples, bullets, image sizing, icons, and common file-corruption pitfalls, load `pptxgenjs.md`.\n\n## Required QA\n\n- Run `pptx_read` on the generated file and check slide order, missing text, typo risk, and notes.\n- Inspect generated XML or render slides when visual precision matters.\n- Watch for overlap, text overflow, low contrast, cramped spacing, repeated layouts, and leftover placeholder text.\n- If a visual issue is found, edit the `.mjs` script and rewrite the PPTX.\n","skills/powerpoint/references/pptxgenjs.md":"# PptxGenJS Reference\n\nUse this reference when writing the JavaScript build script for the `pptx_write` tool.\n\n## Script Creation\n\nPass the complete JavaScript module in the `script` argument to `pptx_write`. Do not create a temporary `.mjs` file with `local_file_write` or shell commands for this workflow.\n\n## Basic Structure\n\n```javascript\nexport default async function build(pptx, ctx) {\n  pptx.layout = \"LAYOUT_WIDE\";\n  pptx.title = ctx.data?.title || \"Presentation\";\n\n  const slide = pptx.addSlide();\n  slide.background = { color: \"FFFFFF\" };\n  slide.addText(\"Hello\", { x: 0.6, y: 0.5, w: 8, h: 0.6, fontSize: 36 });\n}\n```\n\nUseful layouts:\n\n- `LAYOUT_WIDE`: 13.333 x 7.5 in.\n- `LAYOUT_16X9`: 10 x 5.625 in.\n- `LAYOUT_4X3`: 10 x 7.5 in.\n\nUse inches for all `x`, `y`, `w`, and `h` values.\n\n## Text\n\n```javascript\nslide.addText(\"Main title\", {\n  x: 0.6, y: 0.4, w: 8.5, h: 0.6,\n  fontFace: \"Aptos Display\",\n  fontSize: 38,\n  bold: true,\n  color: \"111827\",\n  margin: 0,\n});\n\nslide.addText([\n  { text: \"First point\", options: { bullet: true, breakLine: true } },\n  { text: \"Second point\", options: { bullet: true } },\n], {\n  x: 0.8, y: 1.4, w: 5.4, h: 1.2,\n  fontSize: 17,\n  color: \"374151\",\n  breakLine: false,\n  paraSpaceAfterPt: 8,\n});\n```\n\nRules:\n\n- Use `margin: 0` when aligning text with shapes or icons.\n- Use PptxGenJS bullets; do not type bullet glyphs into strings.\n- Use `breakLine: true` in rich text arrays when items must appear on separate lines.\n- Avoid tiny body text; most slide body copy should stay at or above 14 pt.\n\n## Shapes\n\n```javascript\nslide.addShape(pptx.ShapeType.rect, {\n  x: 0, y: 0, w: 13.333, h: 7.5,\n  fill: { color: \"F8FAFC\" },\n  line: { color: \"F8FAFC\" },\n});\n\nslide.addShape(pptx.ShapeType.roundRect, {\n  x: 0.7, y: 1.2, w: 3.5, h: 1.2,\n  rectRadius: 0.08,\n  fill: { color: \"FFFFFF\" },\n  line: { color: \"E5E7EB\", width: 1 },\n  shadow: { type: \"outer\", color: \"000000\", opacity: 0.12, blur: 2, angle: 45, distance: 1 },\n});\n\nslide.addShape(pptx.ShapeType.line, {\n  x: 0.7, y: 6.8, w: 11.8, h: 0,\n  line: { color: \"CBD5E1\", width: 1 },\n});\n```\n\nColor rules:\n\n- Hex colors must not include `#`.\n- Do not use 8-character hex for transparency. Use `transparency` or `opacity`.\n- Use a fresh options object for each shape; PptxGenJS mutates some option values internally.\n\n## Images and Icons\n\n```javascript\nslide.addImage({\n  data: ctx.imageData(\"photo.png\"),\n  x: 7.1, y: 1.0, w: 5.4, h: 3.4,\n  sizing: { type: \"cover\", x: 7.1, y: 1.0, w: 5.4, h: 3.4 },\n});\n\nslide.addImage({\n  data: ctx.iconSvgData(\"chart-line\", \"2563EB\"),\n  x: 0.8, y: 1.2, w: 0.35, h: 0.35,\n});\n```\n\nUse `ctx.resolveAsset()` when a PptxGenJS API needs a file path. Use `ctx.imageData()` when embedding local PNG/JPG/GIF/SVG assets. Use `ctx.iconSvgData()` for Font Awesome solid icons by names such as `check`, `chart-line`, `shield-halved`, or `circle-info`.\n\n## Tables and Charts\n\n```javascript\nslide.addTable([\n  [\"Metric\", \"Current\", \"Target\"],\n  [\"Activation\", \"42%\", \"55%\"],\n  [\"Retention\", \"68%\", \"74%\"],\n], {\n  x: 0.7, y: 1.5, w: 6.0, h: 1.2,\n  border: { pt: 1, color: \"E5E7EB\" },\n  fill: { color: \"FFFFFF\" },\n  color: \"111827\",\n  fontSize: 12,\n});\n\nslide.addChart(pptx.ChartType.bar, [{\n  name: \"Revenue\",\n  labels: [\"Q1\", \"Q2\", \"Q3\", \"Q4\"],\n  values: [12, 16, 21, 28],\n}], {\n  x: 0.7, y: 1.2, w: 6.2, h: 3.8,\n  barDir: \"col\",\n  chartColors: [\"2563EB\"],\n  showLegend: false,\n  showValue: true,\n  valGridLine: { color: \"E2E8F0\", size: 0.5 },\n});\n```\n\nPrefer a small set of chart colors from the deck palette. Hide legends when there is only one series.\n\n## Layout Ideas\n\n- Cover: full color block or image area with large title and short subtitle.\n- Agenda: strong sidebar plus numbered sections.\n- Two-column: explanatory text on one side, image/chart/diagram on the other.\n- Card grid: 2x2 or 3x2 cards with icon, header, and one-sentence body.\n- Timeline/process: numbered steps connected by lines or arrows.\n- Data slide: one large chart plus two or three stat callouts.\n- Closing: strong statement, next action, or summary trio.\n\n## QA Checklist\n\n- Slide order and text match the requested content.\n- Text does not collide with shapes, images, footers, or other text.\n- Long labels fit in their boxes and do not clip.\n- Contrast is readable on every background.\n- Margins are at least 0.5 in unless intentionally full bleed.\n- Layouts vary; the deck does not read as repeated title-plus-bullets.\n- Generated file opens and `pptx_read` returns the expected text.\n","skills/sag/SKILL.md":"---\nname: sag\ndescription: ElevenLabs text-to-speech with mac-style say UX.\nhomepage: https://sag.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔊\",\n        \"requires\": { \"bins\": [\"sag\"], \"env\": [\"ELEVENLABS_API_KEY\"] },\n        \"primaryEnv\": \"ELEVENLABS_API_KEY\",\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/sag\",\n              \"bins\": [\"sag\"],\n              \"label\": \"Install sag (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# sag\n\nUse `sag` for ElevenLabs TTS with local playback.\n\nAPI key (required)\n\n- `ELEVENLABS_API_KEY` (preferred)\n- `SAG_API_KEY` also supported by the CLI\n\nQuick start\n\n- `sag \"Hello there\"`\n- `sag speak -v \"Roger\" \"Hello\"`\n- `sag voices`\n- `sag prompting` (model-specific tips)\n\nModel notes\n\n- Default: `eleven_v3` (expressive)\n- Stable: `eleven_multilingual_v2`\n- Fast: `eleven_flash_v2_5`\n\nPronunciation + delivery rules\n\n- First fix: respell (e.g. \"key-note\"), add hyphens, adjust casing.\n- Numbers/units/URLs: `--normalize auto` (or `off` if it harms names).\n- Language bias: `--lang en|de|fr|...` to guide normalization.\n- v3: SSML `<break>` not supported; use `[pause]`, `[short pause]`, `[long pause]`.\n- v2/v2.5: SSML `<break time=\"1.5s\" />` supported; `<phoneme>` not exposed in `sag`.\n\nv3 audio tags (put at the entrance of a line)\n\n- `[whispers]`, `[shouts]`, `[sings]`\n- `[laughs]`, `[starts laughing]`, `[sighs]`, `[exhales]`\n- `[sarcastic]`, `[curious]`, `[excited]`, `[crying]`, `[mischievously]`\n- Example: `sag \"[whispers] keep this quiet. [short pause] ok?\"`\n\nVoice defaults\n\n- `ELEVENLABS_VOICE_ID` or `SAG_VOICE_ID`\n\nConfirm voice + speaker before long output.\n\n## Chat voice responses\n\nWhen the user asks for a \"voice\" reply (e.g., \"crazy scientist voice\", \"explain in voice\"), generate audio and send it:\n\n```bash\n# Generate audio file\nsag -v Clawd -o /tmp/voice-reply.mp3 \"Your message here\"\n\n# Then include in reply:\n# MEDIA:/tmp/voice-reply.mp3\n```\n\nVoice character tips:\n\n- Crazy scientist: Use `[excited]` tags, dramatic pauses `[short pause]`, vary intensity\n- Calm: Use `[whispers]` or slower pacing\n- Dramatic: Use `[sings]` or `[shouts]` sparingly\n\nDefault voice for Clawd: `lj2rcrvANS3gaWWnczSX` (or just `-v Clawd`)\n","skills/session-logs/SKILL.md":"---\nname: session-logs\ndescription: Search and analyze your own session logs (older/parent conversations) using jq.\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📜\",\n        \"requires\": { \"bins\": [\"jq\", \"rg\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew-jq\",\n              \"kind\": \"brew\",\n              \"formula\": \"jq\",\n              \"bins\": [\"jq\"],\n              \"label\": \"Install jq (brew)\",\n            },\n            {\n              \"id\": \"brew-rg\",\n              \"kind\": \"brew\",\n              \"formula\": \"ripgrep\",\n              \"bins\": [\"rg\"],\n              \"label\": \"Install ripgrep (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# session-logs\n\nSearch your complete conversation history stored in session JSONL files. Use this when a user references older/parent conversations or asks what was said before.\n\n## Trigger\n\nUse this skill when the user asks about prior chats, parent conversations, or historical context that isn't in memory files.\n\n## Location\n\nSession logs live under the active state directory:\n`$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/` (default: `~/.openclaw/agents/<agentId>/sessions/`).\nUse the `agent=<id>` value from the system prompt Runtime line.\n\n- **`sessions.json`** - Index mapping session keys to session IDs\n- **`<session-id>.jsonl`** - Full conversation transcript per session\n\n## Structure\n\nEach `.jsonl` file contains messages with:\n\n- `type`: \"session\" (metadata) or \"message\"\n- `timestamp`: ISO timestamp\n- `message.role`: \"user\", \"assistant\", or \"toolResult\"\n- `message.content[]`: Text, thinking, or tool calls (filter `type==\"text\"` for human-readable content)\n- `message.usage.cost.total`: Cost per response\n\n## Common Queries\n\n### List all sessions by date and size\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nfor f in \"$SESSION_DIR\"/*.jsonl; do\n  date=$(head -1 \"$f\" | jq -r '.timestamp' | cut -dT -f1)\n  size=$(ls -lh \"$f\" | awk '{print $5}')\n  echo \"$date $size $(basename $f)\"\ndone | sort -r\n```\n\n### Find sessions from a specific day\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nfor f in \"$SESSION_DIR\"/*.jsonl; do\n  head -1 \"$f\" | jq -r '.timestamp' | grep -q \"2026-01-06\" && echo \"$f\"\ndone\n```\n\n### Extract user messages from a session\n\n```bash\njq -r 'select(.message.role == \"user\") | .message.content[]? | select(.type == \"text\") | .text' <session>.jsonl\n```\n\n### Search for keyword in assistant responses\n\n```bash\njq -r 'select(.message.role == \"assistant\") | .message.content[]? | select(.type == \"text\") | .text' <session>.jsonl | rg -i \"keyword\"\n```\n\n### Get total cost for a session\n\n```bash\njq -s '[.[] | .message.usage.cost.total // 0] | add' <session>.jsonl\n```\n\n### Daily cost summary\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nfor f in \"$SESSION_DIR\"/*.jsonl; do\n  date=$(head -1 \"$f\" | jq -r '.timestamp' | cut -dT -f1)\n  cost=$(jq -s '[.[] | .message.usage.cost.total // 0] | add' \"$f\")\n  echo \"$date $cost\"\ndone | awk '{a[$1]+=$2} END {for(d in a) print d, \"$\"a[d]}' | sort -r\n```\n\n### Count messages and tokens in a session\n\n```bash\njq -s '{\n  messages: length,\n  user: [.[] | select(.message.role == \"user\")] | length,\n  assistant: [.[] | select(.message.role == \"assistant\")] | length,\n  first: .[0].timestamp,\n  last: .[-1].timestamp\n}' <session>.jsonl\n```\n\n### Tool usage breakdown\n\n```bash\njq -r '.message.content[]? | select(.type == \"toolCall\") | .name' <session>.jsonl | sort | uniq -c | sort -rn\n```\n\n### Search across ALL sessions for a phrase\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nrg -l \"phrase\" \"$SESSION_DIR\"/*.jsonl\n```\n\n## Tips\n\n- Sessions are append-only JSONL (one JSON object per line)\n- Large sessions can be several MB - use `head`/`tail` for sampling\n- The `sessions.json` index maps chat providers (discord, whatsapp, etc.) to session IDs\n- Deleted sessions have `.deleted.<timestamp>` suffix\n\n## Fast text-only hint (low noise)\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\njq -r 'select(.type==\"message\") | .message.content[]? | select(.type==\"text\") | .text' \"$SESSION_DIR\"/<id>.jsonl | rg 'keyword'\n```\n","skills/sherpa-onnx-tts/SKILL.md":"---\nname: sherpa-onnx-tts\ndescription: Local text-to-speech via sherpa-onnx (offline, no cloud)\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔉\",\n        \"os\": [\"darwin\", \"linux\", \"win32\"],\n        \"requires\": { \"env\": [\"SHERPA_ONNX_RUNTIME_DIR\", \"SHERPA_ONNX_MODEL_DIR\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"download-runtime-macos\",\n              \"kind\": \"download\",\n              \"os\": [\"darwin\"],\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-osx-universal2-shared.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"stripComponents\": 1,\n              \"targetDir\": \"runtime\",\n              \"label\": \"Download sherpa-onnx runtime (macOS)\",\n            },\n            {\n              \"id\": \"download-runtime-linux-x64\",\n              \"kind\": \"download\",\n              \"os\": [\"linux\"],\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-linux-x64-shared.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"stripComponents\": 1,\n              \"targetDir\": \"runtime\",\n              \"label\": \"Download sherpa-onnx runtime (Linux x64)\",\n            },\n            {\n              \"id\": \"download-runtime-win-x64\",\n              \"kind\": \"download\",\n              \"os\": [\"win32\"],\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-win-x64-shared.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"stripComponents\": 1,\n              \"targetDir\": \"runtime\",\n              \"label\": \"Download sherpa-onnx runtime (Windows x64)\",\n            },\n            {\n              \"id\": \"download-model-lessac\",\n              \"kind\": \"download\",\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-en_US-lessac-high.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"targetDir\": \"models\",\n              \"label\": \"Download Piper en_US lessac (high)\",\n            },\n          ],\n      },\n  }\n---\n\n# sherpa-onnx-tts\n\nLocal TTS using the sherpa-onnx offline CLI.\n\n## Install\n\n1. Download the runtime for your OS (extracts into `$OPENCLAW_STATE_DIR/tools/sherpa-onnx-tts/runtime`, default `~/.openclaw/tools/sherpa-onnx-tts/runtime`)\n2. Download a voice model (extracts into `$OPENCLAW_STATE_DIR/tools/sherpa-onnx-tts/models`, default `~/.openclaw/tools/sherpa-onnx-tts/models`)\n\nResolve the active state directory first:\n\n```bash\nSTATE_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}\"\n```\n\nThen write those resolved paths into the active OpenClaw config file (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`):\n\n```json5\n{\n  skills: {\n    entries: {\n      \"sherpa-onnx-tts\": {\n        env: {\n          SHERPA_ONNX_RUNTIME_DIR: \"/path/to/your/state-dir/tools/sherpa-onnx-tts/runtime\",\n          SHERPA_ONNX_MODEL_DIR: \"/path/to/your/state-dir/tools/sherpa-onnx-tts/models/vits-piper-en_US-lessac-high\",\n        },\n      },\n    },\n  },\n}\n```\n\nThe wrapper lives in this skill folder. Run it directly, or add the wrapper to PATH:\n\n```bash\nexport PATH=\"{baseDir}/bin:$PATH\"\n```\n\n## Usage\n\n```bash\n{baseDir}/bin/sherpa-onnx-tts -o ./tts.wav \"Hello from local TTS.\"\n```\n\nNotes:\n\n- Pick a different model from the sherpa-onnx `tts-models` release if you want another voice.\n- If the model dir has multiple `.onnx` files, set `SHERPA_ONNX_MODEL_FILE` or pass `--model-file`.\n- You can also pass `--tokens-file` or `--data-dir` to override the defaults.\n- Windows: run `node {baseDir}\\\\bin\\\\sherpa-onnx-tts -o tts.wav \"Hello from local TTS.\"`\n","skills/skill-creator/SKILL.md":"---\nname: skill-creator\ndescription: Create, edit, improve, or audit AgentSkills. Use when creating a new skill from scratch or when asked to improve, review, audit, tidy up, or clean up an existing skill or SKILL.md file. Also use when editing or restructuring a skill directory (moving files to references/ or scripts/, removing stale content, validating against the AgentSkills spec). Triggers on phrases like \"create a skill\", \"author a skill\", \"tidy up a skill\", \"improve this skill\", \"review the skill\", \"clean up the skill\", \"audit the skill\".\n---\n\n# Skill Creator\n\nThis skill provides guidance for creating effective skills.\n\n## About Skills\n\nSkills are modular, self-contained packages that extend Codex's capabilities by providing\nspecialized knowledge, workflows, and tools. Think of them as \"onboarding guides\" for specific\ndomains or tasks—they transform Codex from a general-purpose agent into a specialized agent\nequipped with procedural knowledge that no model can fully possess.\n\n### What Skills Provide\n\n1. Specialized workflows - Multi-step procedures for specific domains\n2. Tool integrations - Instructions for working with specific file formats or APIs\n3. Domain expertise - Company-specific knowledge, schemas, business logic\n4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks\n\n## Core Principles\n\n### Concise is Key\n\nThe context window is a public good. Skills share the context window with everything else Codex needs: system prompt, conversation history, other Skills' metadata, and the actual user request.\n\n**Default assumption: Codex is already very smart.** Only add context Codex doesn't already have. Challenge each piece of information: \"Does Codex really need this explanation?\" and \"Does this paragraph justify its token cost?\"\n\nPrefer concise examples over verbose explanations.\n\n### Set Appropriate Degrees of Freedom\n\nMatch the level of specificity to the task's fragility and variability:\n\n**High freedom (text-based instructions)**: Use when multiple approaches are valid, decisions depend on context, or heuristics guide the approach.\n\n**Medium freedom (pseudocode or scripts with parameters)**: Use when a preferred pattern exists, some variation is acceptable, or configuration affects behavior.\n\n**Low freedom (specific scripts, few parameters)**: Use when operations are fragile and error-prone, consistency is critical, or a specific sequence must be followed.\n\nThink of Codex as exploring a path: a narrow bridge with cliffs needs specific guardrails (low freedom), while an open field allows many routes (high freedom).\n\n### Anatomy of a Skill\n\nEvery skill consists of a required SKILL.md file and optional bundled resources:\n\n```\nskill-name/\n├── SKILL.md (required)\n│   ├── YAML frontmatter metadata (required)\n│   │   ├── name: (required)\n│   │   └── description: (required)\n│   └── Markdown instructions (required)\n└── Bundled Resources (optional)\n    ├── scripts/          - Executable code (Python/Bash/etc.)\n    ├── references/       - Documentation intended to be loaded into context as needed\n    └── assets/           - Files used in output (templates, icons, fonts, etc.)\n```\n\n#### SKILL.md (required)\n\nEvery SKILL.md consists of:\n\n- **Frontmatter** (YAML): Contains `name` and `description` fields. These are the only fields that Codex reads to determine when the skill gets used, thus it is very important to be clear and comprehensive in describing what the skill is, and when it should be used.\n- **Body** (Markdown): Instructions and guidance for using the skill. Only loaded AFTER the skill triggers (if at all).\n\n#### Bundled Resources (optional)\n\n##### Scripts (`scripts/`)\n\nExecutable code (Python/Bash/etc.) for tasks that require deterministic reliability or are repeatedly rewritten.\n\n- **When to include**: When the same code is being rewritten repeatedly or deterministic reliability is needed\n- **Example**: `scripts/rotate_pdf.py` for PDF rotation tasks\n- **Benefits**: Token efficient, deterministic, may be executed without loading into context\n- **Note**: Scripts may still need to be read by Codex for patching or environment-specific adjustments\n\n##### References (`references/`)\n\nDocumentation and reference material intended to be loaded as needed into context to inform Codex's process and thinking.\n\n- **When to include**: For documentation that Codex should reference while working\n- **Examples**: `references/finance.md` for financial schemas, `references/mnda.md` for company NDA template, `references/policies.md` for company policies, `references/api_docs.md` for API specifications\n- **Use cases**: Database schemas, API documentation, domain knowledge, company policies, detailed workflow guides\n- **Benefits**: Keeps SKILL.md lean, loaded only when Codex determines it's needed\n- **Best practice**: If files are large (>10k words), include grep search patterns in SKILL.md\n- **Avoid duplication**: Information should live in either SKILL.md or references files, not both. Prefer references files for detailed information unless it's truly core to the skill—this keeps SKILL.md lean while making information discoverable without hogging the context window. Keep only essential procedural instructions and workflow guidance in SKILL.md; move detailed reference material, schemas, and examples to references files.\n\n##### Assets (`assets/`)\n\nFiles not intended to be loaded into context, but rather used within the output Codex produces.\n\n- **When to include**: When the skill needs files that will be used in the final output\n- **Examples**: `assets/logo.png` for brand assets, `assets/slides.pptx` for PowerPoint templates, `assets/frontend-template/` for HTML/React boilerplate, `assets/font.ttf` for typography\n- **Use cases**: Templates, images, icons, boilerplate code, fonts, sample documents that get copied or modified\n- **Benefits**: Separates output resources from documentation, enables Codex to use files without loading them into context\n\n#### What to Not Include in a Skill\n\nA skill should only contain essential files that directly support its functionality. Do NOT create extraneous documentation or auxiliary files, including:\n\n- README.md\n- INSTALLATION_GUIDE.md\n- QUICK_REFERENCE.md\n- CHANGELOG.md\n- etc.\n\nThe skill should only contain the information needed for an AI agent to do the job at hand. It should not contain auxiliary context about the process that went into creating it, setup and testing procedures, user-facing documentation, etc. Creating additional documentation files just adds clutter and confusion.\n\n### Progressive Disclosure Design Principle\n\nSkills use a three-level loading system to manage context efficiently:\n\n1. **Metadata (name + description)** - Always in context (~100 words)\n2. **SKILL.md body** - When skill triggers (<5k words)\n3. **Bundled resources** - As needed by Codex (Unlimited because scripts can be executed without reading into context window)\n\n#### Progressive Disclosure Patterns\n\nKeep SKILL.md body to the essentials and under 500 lines to minimize context bloat. Split content into separate files when approaching this limit. When splitting out content into other files, it is very important to reference them from SKILL.md and describe clearly when to read them, to ensure the reader of the skill knows they exist and when to use them.\n\n**Key principle:** When a skill supports multiple variations, frameworks, or options, keep only the core workflow and selection guidance in SKILL.md. Move variant-specific details (patterns, examples, configuration) into separate reference files.\n\n**Pattern 1: High-level guide with references**\n\n```markdown\n# PDF Processing\n\n## Quick start\n\nExtract text with pdfplumber:\n[code example]\n\n## Advanced features\n\n- **Form filling**: See [FORMS.md](FORMS.md) for complete guide\n- **API reference**: See [REFERENCE.md](REFERENCE.md) for all methods\n- **Examples**: See [EXAMPLES.md](EXAMPLES.md) for common patterns\n```\n\nCodex loads FORMS.md, REFERENCE.md, or EXAMPLES.md only when needed.\n\n**Pattern 2: Domain-specific organization**\n\nFor Skills with multiple domains, organize content by domain to avoid loading irrelevant context:\n\n```\nbigquery-skill/\n├── SKILL.md (overview and navigation)\n└── reference/\n    ├── finance.md (revenue, billing metrics)\n    ├── sales.md (opportunities, pipeline)\n    ├── product.md (API usage, features)\n    └── marketing.md (campaigns, attribution)\n```\n\nWhen a user asks about sales metrics, Codex only reads sales.md.\n\nSimilarly, for skills supporting multiple frameworks or variants, organize by variant:\n\n```\ncloud-deploy/\n├── SKILL.md (workflow + provider selection)\n└── references/\n    ├── aws.md (AWS deployment patterns)\n    ├── gcp.md (GCP deployment patterns)\n    └── azure.md (Azure deployment patterns)\n```\n\nWhen the user chooses AWS, Codex only reads aws.md.\n\n**Pattern 3: Conditional details**\n\nShow basic content, link to advanced content:\n\n```markdown\n# DOCX Processing\n\n## Creating documents\n\nUse docx-js for new documents. See [DOCX-JS.md](DOCX-JS.md).\n\n## Editing documents\n\nFor simple edits, modify the XML directly.\n\n**For tracked changes**: See [REDLINING.md](REDLINING.md)\n**For OOXML details**: See [OOXML.md](OOXML.md)\n```\n\nCodex reads REDLINING.md or OOXML.md only when the user needs those features.\n\n**Important guidelines:**\n\n- **Avoid deeply nested references** - Keep references one level deep from SKILL.md. All reference files should link directly from SKILL.md.\n- **Structure longer reference files** - For files longer than 100 lines, include a table of contents at the top so Codex can see the full scope when previewing.\n\n## Skill Creation Process\n\nSkill creation involves these steps:\n\n1. Understand the skill with concrete examples\n2. Plan reusable skill contents (scripts, references, assets)\n3. Initialize the skill (run init_skill.py)\n4. Edit the skill (implement resources and write SKILL.md)\n5. Package the skill (run package_skill.py)\n6. Iterate based on real usage\n\nFollow these steps in order, skipping only if there is a clear reason why they are not applicable.\n\n### Skill Naming\n\n- Use lowercase letters, digits, and hyphens only; normalize user-provided titles to hyphen-case (e.g., \"Plan Mode\" -> `plan-mode`).\n- When generating names, generate a name under 64 characters (letters, digits, hyphens).\n- Prefer short, verb-led phrases that describe the action.\n- Namespace by tool when it improves clarity or triggering (e.g., `gh-address-comments`, `linear-address-issue`).\n- Name the skill folder exactly after the skill name.\n\n### Step 1: Understanding the Skill with Concrete Examples\n\nSkip this step only when the skill's usage patterns are already clearly understood. It remains valuable even when working with an existing skill.\n\nTo create an effective skill, clearly understand concrete examples of how the skill will be used. This understanding can come from either direct user examples or generated examples that are validated with user feedback.\n\nFor example, when building an image-editor skill, relevant questions include:\n\n- \"What functionality should the image-editor skill support? Editing, rotating, anything else?\"\n- \"Can you give some examples of how this skill would be used?\"\n- \"I can imagine users asking for things like 'Remove the red-eye from this image' or 'Rotate this image'. Are there other ways you imagine this skill being used?\"\n- \"What would a user say that should trigger this skill?\"\n\nTo avoid overwhelming users, avoid asking too many questions in a single message. Start with the most important questions and follow up as needed for better effectiveness.\n\nConclude this step when there is a clear sense of the functionality the skill should support.\n\n### Step 2: Planning the Reusable Skill Contents\n\nTo turn concrete examples into an effective skill, analyze each example by:\n\n1. Considering how to execute on the example from scratch\n2. Identifying what scripts, references, and assets would be helpful when executing these workflows repeatedly\n\nExample: When building a `pdf-editor` skill to handle queries like \"Help me rotate this PDF,\" the analysis shows:\n\n1. Rotating a PDF requires re-writing the same code each time\n2. A `scripts/rotate_pdf.py` script would be helpful to store in the skill\n\nExample: When designing a `frontend-webapp-builder` skill for queries like \"Build me a todo app\" or \"Build me a dashboard to track my steps,\" the analysis shows:\n\n1. Writing a frontend webapp requires the same boilerplate HTML/React each time\n2. An `assets/hello-world/` template containing the boilerplate HTML/React project files would be helpful to store in the skill\n\nExample: When building a `big-query` skill to handle queries like \"How many users have logged in today?\" the analysis shows:\n\n1. Querying BigQuery requires re-discovering the table schemas and relationships each time\n2. A `references/schema.md` file documenting the table schemas would be helpful to store in the skill\n\nTo establish the skill's contents, analyze each concrete example to create a list of the reusable resources to include: scripts, references, and assets.\n\n### Step 3: Initializing the Skill\n\nAt this point, it is time to actually create the skill.\n\nSkip this step only if the skill being developed already exists, and iteration or packaging is needed. In this case, continue to the next step.\n\nWhen creating a new skill from scratch, always run the `init_skill.py` script. The script conveniently generates a new template skill directory that automatically includes everything a skill requires, making the skill creation process much more efficient and reliable.\n\nUsage:\n\n```bash\nscripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples]\n```\n\nExamples:\n\n```bash\nscripts/init_skill.py my-skill --path skills/public\nscripts/init_skill.py my-skill --path skills/public --resources scripts,references\nscripts/init_skill.py my-skill --path skills/public --resources scripts --examples\n```\n\nThe script:\n\n- Creates the skill directory at the specified path\n- Generates a SKILL.md template with proper frontmatter and TODO placeholders\n- Optionally creates resource directories based on `--resources`\n- Optionally adds example files when `--examples` is set\n\nAfter initialization, customize the SKILL.md and add resources as needed. If you used `--examples`, replace or delete placeholder files.\n\n### Step 4: Edit the Skill\n\nWhen editing the (newly-generated or existing) skill, remember that the skill is being created for another instance of Codex to use. Include information that would be beneficial and non-obvious to Codex. Consider what procedural knowledge, domain-specific details, or reusable assets would help another Codex instance execute these tasks more effectively.\n\n#### Learn Proven Design Patterns\n\nConsult these helpful guides based on your skill's needs:\n\n- **Multi-step processes**: See references/workflows.md for sequential workflows and conditional logic\n- **Specific output formats or quality standards**: See references/output-patterns.md for template and example patterns\n\nThese files contain established best practices for effective skill design.\n\n#### Start with Reusable Skill Contents\n\nTo begin implementation, start with the reusable resources identified above: `scripts/`, `references/`, and `assets/` files. Note that this step may require user input. For example, when implementing a `brand-guidelines` skill, the user may need to provide brand assets or templates to store in `assets/`, or documentation to store in `references/`.\n\nAdded scripts must be tested by actually running them to ensure there are no bugs and that the output matches what is expected. If there are many similar scripts, only a representative sample needs to be tested to ensure confidence that they all work while balancing time to completion.\n\nIf you used `--examples`, delete any placeholder files that are not needed for the skill. Only create resource directories that are actually required.\n\n#### Update SKILL.md\n\n**Writing Guidelines:** Always use imperative/infinitive form.\n\n##### Frontmatter\n\nWrite the YAML frontmatter with `name` and `description`:\n\n- `name`: The skill name\n- `description`: This is the primary triggering mechanism for your skill, and helps Codex understand when to use the skill.\n  - Include both what the Skill does and specific triggers/contexts for when to use it.\n  - Include all \"when to use\" information here - Not in the body. The body is only loaded after triggering, so \"When to Use This Skill\" sections in the body are not helpful to Codex.\n  - Example description for a `docx` skill: \"Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. Use when Codex needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks\"\n\nDo not include any other fields in YAML frontmatter.\n\n##### Body\n\nWrite instructions for using the skill and its bundled resources.\n\n### Step 5: Packaging a Skill\n\nOnce development of the skill is complete, it must be packaged into a distributable .skill file that gets shared with the user. The packaging process automatically validates the skill first to ensure it meets all requirements:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder>\n```\n\nOptional output directory specification:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder> ./dist\n```\n\nThe packaging script will:\n\n1. **Validate** the skill automatically, checking:\n   - YAML frontmatter format and required fields\n   - Skill naming conventions and directory structure\n   - Description completeness and quality\n   - File organization and resource references\n\n2. **Package** the skill if validation passes, creating a .skill file named after the skill (e.g., `my-skill.skill`) that includes all files and maintains the proper directory structure for distribution. The .skill file is a zip file with a .skill extension.\n\n   Security restriction: symlinks are rejected and packaging fails when any symlink is present.\n\nIf validation fails, the script will report the errors and exit without creating a package. Fix any validation errors and run the packaging command again.\n\n### Step 6: Iterate\n\nAfter testing the skill, users may request improvements. Often this happens right after using the skill, with fresh context of how the skill performed.\n\n**Iteration workflow:**\n\n1. Use the skill on real tasks\n2. Notice struggles or inefficiencies\n3. Identify how SKILL.md or bundled resources should be updated\n4. Implement changes and test again\n","skills/slack/SKILL.md":"---\nname: slack\ndescription: Use when you need to control Slack from OpenClaw via the slack tool, including reacting to messages or pinning/unpinning items in Slack channels or DMs.\nmetadata: { \"openclaw\": { \"emoji\": \"💬\", \"requires\": { \"config\": [\"channels.slack\"] } } }\n---\n\n# Slack Actions\n\n## Overview\n\nUse `slack` to react, manage pins, send/edit/delete messages, and fetch member info. The tool uses the bot token configured for OpenClaw.\n\n## Inputs to collect\n\n- `channelId` and `messageId` (Slack message timestamp, e.g. `1712023032.1234`).\n- For reactions, an `emoji` (Unicode or `:name:`).\n- For message sends, a `to` target (`channel:<id>` or `user:<id>`) and `content`.\n\nMessage context lines include `slack message id` and `channel` fields you can reuse directly.\n\n## Actions\n\n### Action groups\n\n| Action group | Default | Notes                  |\n| ------------ | ------- | ---------------------- |\n| reactions    | enabled | React + list reactions |\n| messages     | enabled | Read/send/edit/delete  |\n| pins         | enabled | Pin/unpin/list         |\n| memberInfo   | enabled | Member info            |\n| emojiList    | enabled | Custom emoji list      |\n\n### React to a message\n\n```json\n{\n  \"action\": \"react\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\",\n  \"emoji\": \"✅\"\n}\n```\n\n### List reactions\n\n```json\n{\n  \"action\": \"reactions\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### Send a message\n\n```json\n{\n  \"action\": \"sendMessage\",\n  \"to\": \"channel:C123\",\n  \"content\": \"Hello from OpenClaw\"\n}\n```\n\n### Edit a message\n\n```json\n{\n  \"action\": \"editMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\",\n  \"content\": \"Updated text\"\n}\n```\n\n### Delete a message\n\n```json\n{\n  \"action\": \"deleteMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### Read recent messages\n\n```json\n{\n  \"action\": \"readMessages\",\n  \"channelId\": \"C123\",\n  \"limit\": 20\n}\n```\n\n### Pin a message\n\n```json\n{\n  \"action\": \"pinMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### Unpin a message\n\n```json\n{\n  \"action\": \"unpinMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### List pinned items\n\n```json\n{\n  \"action\": \"listPins\",\n  \"channelId\": \"C123\"\n}\n```\n\n### Member info\n\n```json\n{\n  \"action\": \"memberInfo\",\n  \"userId\": \"U123\"\n}\n```\n\n### Emoji list\n\n```json\n{\n  \"action\": \"emojiList\"\n}\n```\n\n## Ideas to try\n\n- React with ✅ to mark completed tasks.\n- Pin key decisions or weekly status updates.\n","skills/songsee/SKILL.md":"---\nname: songsee\ndescription: Generate spectrograms and feature-panel visualizations from audio with the songsee CLI.\nhomepage: https://github.com/steipete/songsee\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🌊\",\n        \"requires\": { \"bins\": [\"songsee\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/songsee\",\n              \"bins\": [\"songsee\"],\n              \"label\": \"Install songsee (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# songsee\n\nGenerate spectrograms + feature panels from audio.\n\nQuick start\n\n- Spectrogram: `songsee track.mp3`\n- Multi-panel: `songsee track.mp3 --viz spectrogram,mel,chroma,hpss,selfsim,loudness,tempogram,mfcc,flux`\n- Time slice: `songsee track.mp3 --start 12.5 --duration 8 -o slice.jpg`\n- Stdin: `cat track.mp3 | songsee - --format png -o out.png`\n\nCommon flags\n\n- `--viz` list (repeatable or comma-separated)\n- `--style` palette (classic, magma, inferno, viridis, gray)\n- `--width` / `--height` output size\n- `--window` / `--hop` FFT settings\n- `--min-freq` / `--max-freq` frequency range\n- `--start` / `--duration` time slice\n- `--format` jpg|png\n\nNotes\n\n- WAV/MP3 decode native; other formats use ffmpeg if available.\n- Multiple `--viz` renders a grid.\n","skills/sonoscli/SKILL.md":"---\nname: sonoscli\ndescription: Control Sonos speakers (discover/status/play/volume/group).\nhomepage: https://sonoscli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔊\",\n        \"requires\": { \"bins\": [\"sonos\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/sonoscli/cmd/sonos@latest\",\n              \"bins\": [\"sonos\"],\n              \"label\": \"Install sonoscli (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# Sonos CLI\n\nUse `sonos` to control Sonos speakers on the local network.\n\n## Quick start\n\n- `sonos discover`\n- `sonos status --name \"Kitchen\"`\n- `sonos play|pause|stop --name \"Kitchen\"`\n- `sonos volume set 15 --name \"Kitchen\"`\n\n## Common tasks\n\n- Grouping: `sonos group status|join|unjoin|party|solo`\n- Favorites: `sonos favorites list|open`\n- Queue: `sonos queue list|play|clear`\n- Spotify search (via SMAPI): `sonos smapi search --service \"Spotify\" --category tracks \"query\"`\n\n## Notes\n\n- If SSDP fails, specify `--ip <speaker-ip>`.\n- Spotify Web API search is optional and requires `SPOTIFY_CLIENT_ID/SECRET`.\n- If there is an error, check the troubleshooting section and offer advice if there is a decent match.\n\n## Troubleshooting\n\n### `sonos discover` - `no route to host`\n\n- On error `Error: write udp4 0.0.0.0:64326->239.255.255.250:1900: sendto: no route to host (Command exited with code 1)`\n  - Note that the port number after `0.0.0.0:` is ephemeral and will change and the netmask may not match exactly either\n  - The `sendto: no route to host` should stay consistent\n- Advise the user that in `direct` (no Docker sandbox) mode on Mac OS that Settings -> Privacy & Security -> Local Network will need to be enabled for the top-level host parent process of the Gateway\n  - `node` if running via `launchd`\n  - `Terminal` if running the gateway directly in the terminal\n  - `Visual Studio Code` if running via terminal in VS Code\n- An alternative option is to use `sandbox` (docker container) with network access allowed for that sandbox\n\n### `sonos discover` - `bind: operation not permitted`\n\n- On error `Error: listen udp4 0.0.0.0:0: bind: operation not permitted`\n- Advise the user that they may be running via a Codex or other sandbox that does not permit network access (this can be replicated by running `sonos discover` within a Codex CLI session with sandbox enabled and not approving the escalation request)\n","skills/spotify-player/SKILL.md":"---\nname: spotify-player\ndescription: Terminal Spotify playback/search via spogo (preferred) or spotify_player.\nhomepage: https://www.spotify.com\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🎵\",\n        \"requires\": { \"anyBins\": [\"spogo\", \"spotify_player\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"spogo\",\n              \"tap\": \"steipete/tap\",\n              \"bins\": [\"spogo\"],\n              \"label\": \"Install spogo (brew)\",\n            },\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"spotify_player\",\n              \"bins\": [\"spotify_player\"],\n              \"label\": \"Install spotify_player (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# spogo / spotify_player\n\nUse `spogo` **(preferred)** for Spotify playback/search. Fall back to `spotify_player` if needed.\n\nRequirements\n\n- Spotify Premium account.\n- Either `spogo` or `spotify_player` installed.\n\nspogo setup\n\n- Import cookies: `spogo auth import --browser chrome`\n\nCommon CLI commands\n\n- Search: `spogo search track \"query\"`\n- Playback: `spogo play|pause|next|prev`\n- Devices: `spogo device list`, `spogo device set \"<name|id>\"`\n- Status: `spogo status`\n\nspotify_player commands (fallback)\n\n- Search: `spotify_player search \"query\"`\n- Playback: `spotify_player playback play|pause|next|previous`\n- Connect device: `spotify_player connect`\n- Like track: `spotify_player like`\n\nNotes\n\n- Config folder: `~/.config/spotify-player` (e.g., `app.toml`).\n- For Spotify Connect integration, set a user `client_id` in config.\n- TUI shortcuts are available via `?` in the app.\n","skills/summarize/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fsummarize%2FSKILL.md -->","skills/taskflow-inbox-triage/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftaskflow-inbox-triage%2FSKILL.md -->","skills/taskflow/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftaskflow%2FSKILL.md -->","skills/things-mac/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fthings-mac%2FSKILL.md -->","skills/tmux/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftmux%2FSKILL.md -->","skills/trello/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftrello%2FSKILL.md -->","skills/video-frames/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fvideo-frames%2FSKILL.md -->","skills/voice-call/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fvoice-call%2FSKILL.md -->","skills/wacli/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fwacli%2FSKILL.md -->","skills/weather/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fweather%2FSKILL.md -->","skills/xurl/SKILL.md":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fxurl%2FSKILL.md -->"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Commands\n\n**Backend (Go)**\n```bash\ngo build                    # build backend binary\ngo run main.go              # run backend (port 14000)\ngo test ./...               # run all tests\ngo test ./object/... -run TestFoo  # run a single test\n```\n\n**Frontend (React + shadcn/ui, built with Vite)**\n```bash\ncd web && yarn install\nyarn start                  # dev server on port 13001, proxies /api to localhost:14000\nyarn build                  # production build (vite writes build-temp, mv.js swaps it into web/build)\nyarn typecheck              # tsc --noEmit\nyarn lint                   # eslint\n```\n\n`web-old/` holds the previous Ant Design frontend. It is kept for reference only\nwhile bugs are chased down against it — it is not built, served or linted, and it\ndoes not get new work.\n\n**Full production build** uses `build.sh` which cross-compiles for linux/amd64, linux/arm64, linux/riscv64.\n\nBackend serves the frontend as embedded static files (`embed.go`); during development the frontend proxy is configured to hit `localhost:14000`.\n\n## Architecture\n\n### Backend (Go / Beego)\n\n**Module:** `github.com/the-open-agent/openagent`\n\nThe backend is a standard [Beego](https://beego.vip/) MVC app. Every entity follows the same three-layer pattern:\n\n1. **`object/<entity>.go`** — struct definition (xorm tags for DB), and all DB access functions: `GetGlobal<Entities>`, `Get<Entity>Count`, `GetPagination<Entities>`, `Get<Entity>`, `Add<Entity>`, `Update<Entity>`, `Delete<Entity>`. The primary key is always `(Owner string, Name string)`. Use `util.GetId(owner, name)` / `util.GetOwnerAndNameFromIdWithError(id)` for composite key serialization.\n\n2. **`controllers/<entity>.go`** — Beego controller methods wired to routes. Standard set: `GetGlobal<Entities>`, `Get<Entities>`, `Get<Entity>`, `Add<Entity>`, `Update<Entity>`, `Delete<Entity>`. Use `c.IsAdmin()` / `c.IsGlobalAdmin()` / `c.RequireSignedIn()` for auth. Paginated list APIs accept `p`, `pageSize`, `field`, `value`, `sortField`, `sortOrder` query params and use `pagination.SetPaginator`.\n\n3. **`routers/router.go`** — `beego.Router` calls grouped by entity (all routes for the same entity's CRUD kept together). No alphabetical ordering within a group. New entity routes should be inserted next to their entity's existing routes (not appended to the end of the file).\n\n**DB schema** is auto-migrated via `object/adapter.go` → `createTable()` using `engine.Sync2(new(EntityStruct))`. Add new entities there.\n\n**i18n (backend):** `i18n/locales/{en,zh}/data.json` — keys are namespaced by category (e.g. `\"comment:...\"`, `\"general:...\"`). Use `c.T(\"namespace:key\")` in controllers.\n\n### Frontend (React + shadcn/ui on Tailwind, TypeScript, Vite)\n\n**Routing** lives in `web/src/App.tsx`; every file in `web/src/pages` is its own lazy chunk, loaded by name. To add a new admin page:\n1. Create `web/src/pages/<Name>Page.tsx` (default export).\n2. Add a `<Route>` in `App.tsx` pointing at `page(\"<Name>Page\")`.\n3. Add a menu entry in `getNavGroups()` (`web/src/lib/nav.ts`), in the correct group — Basic / Connectors / Admin etc.\n4. Add the nav key to `web/src/components/common/NavItemTree.tsx` so it appears in the site Navbar Items config.\n\n**Page pattern:** list pages render `CrudListPage` (`web/src/components/crud/`) with a `ColumnDef[]`; edit pages render `SimpleEditPage` with an `EditField[]`, or compose `EditPageShell` directly when the layout is custom. Both patterns are illustrated by `MessageListPage.tsx` / `MessageEditPage.tsx`.\n\n**Backend API layer:** `web/src/backend/<Entity>Backend.ts` — thin `fetch()` wrappers, one file per entity. Standard exports: `getGlobal<Entities>`, `get<Entity>`, `update<Entity>`, `add<Entity>`, `delete<Entity>`.\n\n**Per-instance config:** the backend sends the Casdoor issuer, client ID and branding in the `jsonWebConfig` cookie (set with index.html and with every `/api/get-account` response) and `web/src/Conf.ts` reads it at boot, so one build serves every instance.\n\n**i18n (frontend):** `web/src/locales/{en,zh}/data.json` namespaced by page/domain. Use `i18next.t(\"namespace:key\")`.\n\n### Key conventions\n\n- Owner is always `\"admin\"` for system-created entities.\n- Entity `Name` is a random string generated with `util.GetRandomString(n)` or a user-provided slug.\n- `CreatedTime` / `UpdatedTime` use `util.GetCurrentTimeWithMilli()` (string format).\n- Delete responses use `affected != 0` boolean pattern.\n- All API responses go through `c.ResponseOk(data)` or `c.ResponseError(msg)`.\n- The `conf/app.conf` file configures DB (`driverName`, `dataSourceName`, `dbName`) and port (`httpport = 14000`).\n","isInternal":false,"tokens":1227,"sizeBytes":4753},{"name":"cli-examples.md","path":"skills/1password/references/cli-examples.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/1password/references/cli-examples.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# op CLI examples (from op help)\n\n## Sign in\n\n- `op signin`\n- `op signin --account <shorthand|signin-address|account-id|user-id>`\n\n## Read\n\n- `op read op://app-prod/db/password`\n- `op read \"op://app-prod/db/one-time password?attribute=otp\"`\n- `op read \"op://app-prod/ssh key/private key?ssh-format=openssh\"`\n- `op read --out-file ./key.pem op://app-prod/server/ssh/key.pem`\n\n## Run\n\n- `export DB_PASSWORD=\"op://app-prod/db/password\"`\n- `op run --no-masking -- printenv DB_PASSWORD`\n- `op run --env-file=\"./.env\" -- printenv DB_PASSWORD`\n\n## Inject\n\n- `echo \"db_password: {{ op://app-prod/db/password }}\" | op inject`\n- `op inject -i config.yml.tpl -o config.yml`\n\n## Whoami / accounts\n\n- `op whoami`\n- `op account list`\n","isInternal":false,"tokens":219,"sizeBytes":720},{"name":"get-started.md","path":"skills/1password/references/get-started.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/1password/references/get-started.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# 1Password CLI get-started (summary)\n\n- Works on macOS, Windows, and Linux.\n  - macOS/Linux shells: bash, zsh, sh, fish.\n  - Windows shell: PowerShell.\n- Requires a 1Password subscription and the desktop app to use app integration.\n- macOS requirement: Big Sur 11.0.0 or later.\n- Linux app integration requires PolKit + an auth agent.\n- Install the CLI per the official doc for your OS.\n- Enable desktop app integration in the 1Password app:\n  - Open and unlock the app, then select your account/collection.\n  - macOS: Settings > Developer > Integrate with 1Password CLI (Touch ID optional).\n  - Windows: turn on Windows Hello, then Settings > Developer > Integrate.\n  - Linux: Settings > Security > Unlock using system authentication, then Settings > Developer > Integrate.\n- After integration, run any command to sign in (example in docs: `op vault list`).\n- If multiple accounts: use `op signin` to pick one, or `--account` / `OP_ACCOUNT`.\n- For non-integration auth, use `op account add`.\n","isInternal":false,"tokens":241,"sizeBytes":994},{"name":"SKILL.md","path":"skills/1password/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/1password/SKILL.md","title":"1password","category":"anthropic-skill","format":"markdown","content":"---\nname: 1password\ndescription: 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.\nhomepage: https://developer.1password.com/docs/cli/get-started/\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔐\",\n        \"requires\": { \"bins\": [\"op\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"1password-cli\",\n              \"bins\": [\"op\"],\n              \"label\": \"Install 1Password CLI (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# 1Password CLI\n\nFollow the official CLI get-started steps. Don't guess install commands.\n\n## References\n\n- `references/get-started.md` (install + app integration + sign-in flow)\n- `references/cli-examples.md` (real `op` examples)\n\n## Workflow\n\n1. Check OS + shell.\n2. Verify CLI present: `op --version`.\n3. Confirm desktop app integration is enabled (per get-started) and the app is unlocked.\n4. REQUIRED: create a fresh tmux session for all `op` commands (no direct `op` calls outside tmux).\n5. Sign in / authorize inside tmux: `op signin` (expect app prompt).\n6. Verify access inside tmux: `op whoami` (must succeed before any secret read).\n7. If multiple accounts: use `--account` or `OP_ACCOUNT`.\n\n## REQUIRED tmux session (T-Max)\n\nThe shell tool uses a fresh TTY per command. To avoid re-prompts and failures, always run `op` inside a dedicated tmux session with a fresh socket/session name.\n\nExample (see `tmux` skill for socket conventions, do not reuse old session names):\n\n```bash\nSOCKET_DIR=\"${OPENCLAW_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/openclaw-tmux-sockets}\"\nmkdir -p \"$SOCKET_DIR\"\nSOCKET=\"$SOCKET_DIR/openclaw-op.sock\"\nSESSION=\"op-auth-$(date +%Y%m%d-%H%M%S)\"\n\ntmux -S \"$SOCKET\" new -d -s \"$SESSION\" -n shell\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op signin --account my.1password.com\" Enter\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op whoami\" Enter\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op vault list\" Enter\ntmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\ntmux -S \"$SOCKET\" kill-session -t \"$SESSION\"\n```\n\n## Guardrails\n\n- Never paste secrets into logs, chat, or code.\n- Prefer `op run` / `op inject` over writing secrets to disk.\n- If sign-in without app integration is needed, use `op account add`.\n- If a command returns \"account is not signed in\", re-run `op signin` inside tmux and authorize in the app.\n- Do not run `op` outside tmux; stop and ask if tmux is unavailable.\n","frontmatter":{"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)"}]}}},"isInternal":false,"tokens":715,"sizeBytes":2596},{"name":"SKILL.md","path":"skills/apple-notes/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/apple-notes/SKILL.md","title":"apple-notes","category":"anthropic-skill","format":"markdown","content":"---\nname: apple-notes\ndescription: 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.\nhomepage: https://github.com/antoniorodr/memo\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📝\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"memo\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"antoniorodr/memo/memo\",\n              \"bins\": [\"memo\"],\n              \"label\": \"Install memo via Homebrew\",\n            },\n          ],\n      },\n  }\n---\n\n# Apple Notes CLI\n\nUse `memo notes` to manage Apple Notes directly from the terminal. Create, view, edit, delete, search, move notes between folders, and export to HTML/Markdown.\n\nSetup\n\n- Install (Homebrew): `brew tap antoniorodr/memo && brew install antoniorodr/memo/memo`\n- Manual (pip): `pip install .` (after cloning the repo)\n- macOS-only; if prompted, grant Automation access to Notes.app.\n\nView Notes\n\n- List all notes: `memo notes`\n- Filter by folder: `memo notes -f \"Folder Name\"`\n- Search notes (fuzzy): `memo notes -s \"query\"`\n\nCreate Notes\n\n- Add a new note: `memo notes -a`\n  - Opens an interactive editor to compose the note.\n- Quick add with title: `memo notes -a \"Note Title\"`\n\nEdit Notes\n\n- Edit existing note: `memo notes -e`\n  - Interactive selection of note to edit.\n\nDelete Notes\n\n- Delete a note: `memo notes -d`\n  - Interactive selection of note to delete.\n\nMove Notes\n\n- Move note to folder: `memo notes -m`\n  - Interactive selection of note and destination folder.\n\nExport Notes\n\n- Export to HTML/Markdown: `memo notes -ex`\n  - Exports selected note; uses Mistune for markdown processing.\n\nLimitations\n\n- Cannot edit notes containing images or attachments.\n- Interactive prompts may require terminal access.\n\nNotes\n\n- macOS-only.\n- Requires Apple Notes.app to be accessible.\n- For automation, grant permissions in System Settings > Privacy & Security > Automation.\n","frontmatter":{"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"}]}}},"isInternal":false,"tokens":513,"sizeBytes":2090},{"name":"SKILL.md","path":"skills/apple-reminders/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/apple-reminders/SKILL.md","title":"apple-reminders","category":"anthropic-skill","format":"markdown","content":"---\nname: apple-reminders\ndescription: Manage Apple Reminders via remindctl CLI (list, add, edit, complete, delete). Supports lists, date filters, and JSON/plain output.\nhomepage: https://github.com/steipete/remindctl\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"⏰\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"remindctl\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/remindctl\",\n              \"bins\": [\"remindctl\"],\n              \"label\": \"Install remindctl via Homebrew\",\n            },\n          ],\n      },\n  }\n---\n\n# Apple Reminders CLI (remindctl)\n\nUse `remindctl` to manage Apple Reminders directly from the terminal.\n\n## When to Use\n\n✅ **USE this skill when:**\n\n- User explicitly mentions \"reminder\" or \"Reminders app\"\n- Creating personal to-dos with due dates that sync to iOS\n- Managing Apple Reminders lists\n- User wants tasks to appear in their iPhone/iPad Reminders app\n\n## When NOT to Use\n\n❌ **DON'T use this skill when:**\n\n- Scheduling OpenClaw tasks or alerts → use `cron` tool with systemEvent instead\n- Calendar events or appointments → use Apple Calendar\n- Project/work task management → use Notion, GitHub Issues, or task queue\n- One-time notifications → use `cron` tool for timed alerts\n- User says \"remind me\" but means an OpenClaw alert → clarify first\n\n## Setup\n\n- Install: `brew install steipete/tap/remindctl`\n- macOS-only; grant Reminders permission when prompted\n- Check status: `remindctl status`\n- Request access: `remindctl authorize`\n\n## Common Commands\n\n### View Reminders\n\n```bash\nremindctl                    # Today's reminders\nremindctl today              # Today\nremindctl tomorrow           # Tomorrow\nremindctl week               # This week\nremindctl overdue            # Past due\nremindctl all                # Everything\nremindctl 2026-01-04         # Specific date\n```\n\n### Manage Lists\n\n```bash\nremindctl list               # List all lists\nremindctl list Work          # Show specific list\nremindctl list Projects --create    # Create list\nremindctl list Work --delete        # Delete list\n```\n\n### Create Reminders\n\n```bash\nremindctl add \"Buy milk\"\nremindctl add --title \"Call mom\" --list Personal --due tomorrow\nremindctl add --title \"Meeting prep\" --due \"2026-02-15 09:00\"\n```\n\n### Complete/Delete\n\n```bash\nremindctl complete 1 2 3     # Complete by ID\nremindctl delete 4A83 --force  # Delete by ID\n```\n\n### Output Formats\n\n```bash\nremindctl today --json       # JSON for scripting\nremindctl today --plain      # TSV format\nremindctl today --quiet      # Counts only\n```\n\n## Date Formats\n\nAccepted by `--due` and date filters:\n\n- `today`, `tomorrow`, `yesterday`\n- `YYYY-MM-DD`\n- `YYYY-MM-DD HH:mm`\n- ISO 8601 (`2026-01-04T12:34:56Z`)\n\n## Example: Clarifying User Intent\n\nUser: \"Remind me to check on the deploy in 2 hours\"\n\n**Ask:** \"Do you want this in Apple Reminders (syncs to your phone) or as an OpenClaw alert (I'll message you here)?\"\n\n- Apple Reminders → use this skill\n- OpenClaw alert → use `cron` tool with systemEvent\n","frontmatter":{"name":"apple-reminders","description":"Manage Apple Reminders via remindctl CLI (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"}]}}},"isInternal":false,"tokens":818,"sizeBytes":3125},{"name":"SKILL.md","path":"skills/bear-notes/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/bear-notes/SKILL.md","title":"bear-notes","category":"anthropic-skill","format":"markdown","content":"---\nname: bear-notes\ndescription: Create, search, and manage Bear notes via grizzly CLI.\nhomepage: https://bear.app\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🐻\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"grizzly\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/tylerwince/grizzly/cmd/grizzly@latest\",\n              \"bins\": [\"grizzly\"],\n              \"label\": \"Install grizzly (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# Bear Notes\n\nUse `grizzly` to create, read, and manage notes in Bear on macOS.\n\nRequirements\n\n- Bear app installed and running\n- For some operations (add-text, tags, open-note --selected), a Bear app token (stored in `~/.config/grizzly/token`)\n\n## Getting a Bear Token\n\nFor operations that require a token (add-text, tags, open-note --selected), you need an authentication token:\n\n1. Open Bear → Help → API Token → Copy Token\n2. Save it: `echo \"YOUR_TOKEN\" > ~/.config/grizzly/token`\n\n## Common Commands\n\nCreate a note\n\n```bash\necho \"Note content here\" | grizzly create --title \"My Note\" --tag work\ngrizzly create --title \"Quick Note\" --tag inbox < /dev/null\n```\n\nOpen/read a note by ID\n\n```bash\ngrizzly open-note --id \"NOTE_ID\" --enable-callback --json\n```\n\nAppend text to a note\n\n```bash\necho \"Additional content\" | grizzly add-text --id \"NOTE_ID\" --mode append --token-file ~/.config/grizzly/token\n```\n\nList all tags\n\n```bash\ngrizzly tags --enable-callback --json --token-file ~/.config/grizzly/token\n```\n\nSearch notes (via open-tag)\n\n```bash\ngrizzly open-tag --name \"work\" --enable-callback --json\n```\n\n## Options\n\nCommon flags:\n\n- `--dry-run` — Preview the URL without executing\n- `--print-url` — Show the x-callback-url\n- `--enable-callback` — Wait for Bear's response (needed for reading data)\n- `--json` — Output as JSON (when using callbacks)\n- `--token-file PATH` — Path to Bear API token file\n\n## Configuration\n\nGrizzly reads config from (in priority order):\n\n1. CLI flags\n2. Environment variables (`GRIZZLY_TOKEN_FILE`, `GRIZZLY_CALLBACK_URL`, `GRIZZLY_TIMEOUT`)\n3. `.grizzly.toml` in current directory\n4. `~/.config/grizzly/config.toml`\n\nExample `~/.config/grizzly/config.toml`:\n\n```toml\ntoken_file = \"~/.config/grizzly/token\"\ncallback_url = \"http://127.0.0.1:42123/success\"\ntimeout = \"5s\"\n```\n\n## Notes\n\n- Bear must be running for commands to work\n- Note IDs are Bear's internal identifiers (visible in note info or via callbacks)\n- Use `--enable-callback` when you need to read data back from Bear\n- Some operations require a valid token (add-text, tags, open-note --selected)\n","frontmatter":{"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)"}]}}},"isInternal":false,"tokens":703,"sizeBytes":2662},{"name":"SKILL.md","path":"skills/blogwatcher/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/blogwatcher/SKILL.md","title":"blogwatcher","category":"anthropic-skill","format":"markdown","content":"---\nname: blogwatcher\ndescription: Monitor blogs and RSS/Atom feeds for updates using the blogwatcher CLI.\nhomepage: https://github.com/Hyaxia/blogwatcher\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📰\",\n        \"requires\": { \"bins\": [\"blogwatcher\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest\",\n              \"bins\": [\"blogwatcher\"],\n              \"label\": \"Install blogwatcher (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# blogwatcher\n\nTrack blog and RSS/Atom feed updates with the `blogwatcher` CLI.\n\nInstall\n\n- Go: `go install github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest`\n\nQuick start\n\n- `blogwatcher --help`\n\nCommon commands\n\n- Add a blog: `blogwatcher add \"My Blog\" https://example.com`\n- List blogs: `blogwatcher blogs`\n- Scan for updates: `blogwatcher scan`\n- List articles: `blogwatcher articles`\n- Mark an article read: `blogwatcher read 1`\n- Mark all articles read: `blogwatcher read-all`\n- Remove a blog: `blogwatcher remove \"My Blog\"`\n\nExample output\n\n```\n$ blogwatcher blogs\nTracked blogs (1):\n\n  xkcd\n    URL: https://xkcd.com\n```\n\n```\n$ blogwatcher scan\nScanning 1 blog(s)...\n\n  xkcd\n    Source: RSS | Found: 4 | New: 4\n\nFound 4 new article(s) total!\n```\n\nNotes\n\n- Use `blogwatcher <command> --help` to discover flags and options.\n","frontmatter":{"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)"}]}}},"isInternal":false,"tokens":399,"sizeBytes":1415},{"name":"SKILL.md","path":"skills/blucli/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/blucli/SKILL.md","title":"blucli","category":"anthropic-skill","format":"markdown","content":"---\nname: blucli\ndescription: BluOS CLI (blu) for discovery, playback, grouping, and volume.\nhomepage: https://blucli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🫐\",\n        \"requires\": { \"bins\": [\"blu\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/blucli/cmd/blu@latest\",\n              \"bins\": [\"blu\"],\n              \"label\": \"Install blucli (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# blucli (blu)\n\nUse `blu` to control Bluesound/NAD players.\n\nQuick start\n\n- `blu devices` (pick target)\n- `blu --device <id> status`\n- `blu play|pause|stop`\n- `blu volume set 15`\n\nTarget selection (in priority order)\n\n- `--device <id|name|alias>`\n- `BLU_DEVICE`\n- config default (if set)\n\nCommon tasks\n\n- Grouping: `blu group status|add|remove`\n- TuneIn search/play: `blu tunein search \"query\"`, `blu tunein play \"query\"`\n\nPrefer `--json` for scripts. Confirm the target device before changing playback.\n","frontmatter":{"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)"}]}}},"isInternal":false,"tokens":294,"sizeBytes":1020},{"name":"SKILL.md","path":"skills/bluebubbles/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/bluebubbles/SKILL.md","title":"bluebubbles","category":"anthropic-skill","format":"markdown","content":"---\nname: bluebubbles\ndescription: Use when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel=\"bluebubbles\".\nmetadata: { \"openclaw\": { \"emoji\": \"🫧\", \"requires\": { \"config\": [\"channels.bluebubbles\"] } } }\n---\n\n# BlueBubbles Actions\n\n## Overview\n\nBlueBubbles is OpenClaw’s recommended iMessage integration. Use the `message` tool with `channel: \"bluebubbles\"` to send messages and manage iMessage conversations: send texts and attachments, react (tapbacks), edit/unsend, reply in threads, and manage group participants/names/icons.\n\n## Inputs to collect\n\n- `target` (prefer `chat_guid:...`; also `+15551234567` in E.164 or `user@example.com`)\n- `message` text for send/edit/reply\n- `messageId` for react/edit/unsend/reply\n- Attachment `path` for local files, or `buffer` + `filename` for base64\n\nIf the user is vague (\"text my mom\"), ask for the recipient handle or chat guid and the exact message content.\n\n## Actions\n\n### Send a message\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"message\": \"hello from OpenClaw\"\n}\n```\n\n### React (tapback)\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"emoji\": \"❤️\"\n}\n```\n\n### Remove a reaction\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"emoji\": \"❤️\",\n  \"remove\": true\n}\n```\n\n### Edit a previously sent message\n\n```json\n{\n  \"action\": \"edit\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"message\": \"updated text\"\n}\n```\n\n### Unsend a message\n\n```json\n{\n  \"action\": \"unsend\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\"\n}\n```\n\n### Reply to a specific message\n\n```json\n{\n  \"action\": \"reply\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"replyTo\": \"<message-guid>\",\n  \"message\": \"replying to that\"\n}\n```\n\n### Send an attachment\n\n```json\n{\n  \"action\": \"sendAttachment\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"path\": \"/tmp/photo.jpg\",\n  \"caption\": \"here you go\"\n}\n```\n\n### Send with an iMessage effect\n\n```json\n{\n  \"action\": \"sendWithEffect\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"message\": \"big news\",\n  \"effect\": \"balloons\"\n}\n```\n\n## Notes\n\n- Requires gateway config `channels.bluebubbles` (serverUrl/password/webhookPath).\n- Prefer `chat_guid` targets when you have them (especially for group chats).\n- BlueBubbles supports rich actions, but some are macOS-version dependent (for example, edit may be broken on macOS 26 Tahoe).\n- The gateway may expose both short and full message ids; full ids are more durable across restarts.\n- Developer reference for the underlying plugin lives in the BlueBubbles plugin package README.\n\n## Ideas to try\n\n- React with a tapback to acknowledge a request.\n- Reply in-thread when a user references a specific message.\n- Send a file attachment with a short caption.\n","frontmatter":{"name":"bluebubbles","description":"Use when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel=\"bluebubbles\".","metadata":{"openclaw":{"emoji":"🫧","requires":{"config":["channels.bluebubbles"]}}}},"isInternal":false,"tokens":848,"sizeBytes":3087},{"name":"SKILL.md","path":"skills/camsnap/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/camsnap/SKILL.md","title":"camsnap","category":"anthropic-skill","format":"markdown","content":"---\nname: camsnap\ndescription: Capture frames or clips from RTSP/ONVIF cameras.\nhomepage: https://camsnap.ai\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📸\",\n        \"requires\": { \"bins\": [\"camsnap\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/camsnap\",\n              \"bins\": [\"camsnap\"],\n              \"label\": \"Install camsnap (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# camsnap\n\nUse `camsnap` to grab snapshots, clips, or motion events from configured cameras.\n\nSetup\n\n- Config file: `~/.config/camsnap/config.yaml`\n- Add camera: `camsnap add --name kitchen --host 192.168.0.10 --user user --pass pass`\n\nCommon commands\n\n- Discover: `camsnap discover --info`\n- Snapshot: `camsnap snap kitchen --out shot.jpg`\n- Clip: `camsnap clip kitchen --dur 5s --out clip.mp4`\n- Motion watch: `camsnap watch kitchen --threshold 0.2 --action '...'`\n- Doctor: `camsnap doctor --probe`\n\nNotes\n\n- Requires `ffmpeg` on PATH.\n- Prefer a short test capture before longer clips.\n","frontmatter":{"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)"}]}}},"isInternal":false,"tokens":288,"sizeBytes":1089},{"name":"SKILL.md","path":"skills/canvas/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/canvas/SKILL.md","title":"canvas","category":"anthropic-skill","format":"markdown","content":"# Canvas Skill\n\nDisplay HTML content on connected OpenClaw nodes (Mac app, iOS, Android).\n\n## Overview\n\nThe canvas tool lets you present web content on any connected node's canvas view. Great for:\n\n- Displaying games, visualizations, dashboards\n- Showing generated HTML content\n- Interactive demos\n\n## How It Works\n\n### Architecture\n\n```\n┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐\n│  Canvas Host    │────▶│   Node Bridge    │────▶│  Node App   │\n│  (HTTP Server)  │     │  (TCP Server)    │     │ (Mac/iOS/   │\n│  Port 18793     │     │  Port 18790      │     │  Android)   │\n└─────────────────┘     └──────────────────┘     └─────────────┘\n```\n\n1. **Canvas Host Server**: Serves static HTML/CSS/JS files from `canvasHost.root` directory\n2. **Node Bridge**: Communicates canvas URLs to connected nodes\n3. **Node Apps**: Render the content in a WebView\n\n### Tailscale Integration\n\nThe canvas host server binds based on `gateway.bind` setting:\n\n| Bind Mode  | Server Binds To     | Canvas URL Uses            |\n| ---------- | ------------------- | -------------------------- |\n| `loopback` | 127.0.0.1           | localhost (local only)     |\n| `lan`      | LAN interface       | LAN IP address             |\n| `tailnet`  | Tailscale interface | Tailscale hostname         |\n| `auto`     | Best available      | Tailscale > LAN > loopback |\n\n**Key insight:** The `canvasHostHostForBridge` is derived from `bridgeHost`. When bound to Tailscale, nodes receive URLs like:\n\n```\nhttp://<tailscale-hostname>:18793/__openclaw__/canvas/<file>.html\n```\n\nThis is why localhost URLs don't work - the node receives the Tailscale hostname from the bridge!\n\n## Actions\n\n| Action     | Description                          |\n| ---------- | ------------------------------------ |\n| `present`  | Show canvas with optional target URL |\n| `hide`     | Hide the canvas                      |\n| `navigate` | Navigate to a new URL                |\n| `eval`     | Execute JavaScript in the canvas     |\n| `snapshot` | Capture screenshot of canvas         |\n\n## Configuration\n\nIn the active OpenClaw config file (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`):\n\n```json\n{\n  \"canvasHost\": {\n    \"enabled\": true,\n    \"port\": 18793,\n    \"root\": \"/Users/you/clawd/canvas\",\n    \"liveReload\": true\n  },\n  \"gateway\": {\n    \"bind\": \"auto\"\n  }\n}\n```\n\n### Live Reload\n\nWhen `liveReload: true` (default), the canvas host:\n\n- Watches the root directory for changes (via chokidar)\n- Injects a WebSocket client into HTML files\n- Automatically reloads connected canvases when files change\n\nGreat for development!\n\n## Workflow\n\n### 1. Create HTML content\n\nPlace files in the canvas root directory (default `~/clawd/canvas/`):\n\n```bash\ncat > ~/clawd/canvas/my-game.html << 'HTML'\n<!DOCTYPE html>\n<html>\n<head><title>My Game</title></head>\n<body>\n  <h1>Hello Canvas!</h1>\n</body>\n</html>\nHTML\n```\n\n### 2. Find your canvas host URL\n\nCheck how your gateway is bound:\n\n```bash\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\ncat \"$CONFIG_PATH\" | jq '.gateway.bind'\n```\n\nThen construct the URL:\n\n- **loopback**: `http://127.0.0.1:18793/__openclaw__/canvas/<file>.html`\n- **lan/tailnet/auto**: `http://<hostname>:18793/__openclaw__/canvas/<file>.html`\n\nFind your Tailscale hostname:\n\n```bash\ntailscale status --json | jq -r '.Self.DNSName' | sed 's/\\.$//'\n```\n\n### 3. Find connected nodes\n\n```bash\nopenclaw nodes list\n```\n\nLook for Mac/iOS/Android nodes with canvas capability.\n\n### 4. Present content\n\n```\ncanvas action:present node:<node-id> target:<full-url>\n```\n\n**Example:**\n\n```\ncanvas action:present node:mac-63599bc4-b54d-4392-9048-b97abd58343a target:http://peters-mac-studio-1.sheep-coho.ts.net:18793/__openclaw__/canvas/snake.html\n```\n\n### 5. Navigate, snapshot, or hide\n\n```\ncanvas action:navigate node:<node-id> url:<new-url>\ncanvas action:snapshot node:<node-id>\ncanvas action:hide node:<node-id>\n```\n\n## Debugging\n\n### White screen / content not loading\n\n**Cause:** URL mismatch between server bind and node expectation.\n\n**Debug steps:**\n\n1. Check server bind: `CONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"; cat \"$CONFIG_PATH\" | jq '.gateway.bind'`\n2. Check what port canvas is on: `lsof -i :18793`\n3. Test URL directly: `curl http://<hostname>:18793/__openclaw__/canvas/<file>.html`\n\n**Solution:** Use the full hostname matching your bind mode, not localhost.\n\n### \"node required\" error\n\nAlways specify `node:<node-id>` parameter.\n\n### \"node not connected\" error\n\nNode is offline. Use `openclaw nodes list` to find online nodes.\n\n### Content not updating\n\nIf live reload isn't working:\n\n1. Check `liveReload: true` in config\n2. Ensure file is in the canvas root directory\n3. Check for watcher errors in logs\n\n## URL Path Structure\n\nThe canvas host serves from `/__openclaw__/canvas/` prefix:\n\n```\nhttp://<host>:18793/__openclaw__/canvas/index.html  → ~/clawd/canvas/index.html\nhttp://<host>:18793/__openclaw__/canvas/games/snake.html → ~/clawd/canvas/games/snake.html\n```\n\nThe `/__openclaw__/canvas/` prefix is defined by `CANVAS_HOST_PATH` constant.\n\n## Tips\n\n- Keep HTML self-contained (inline CSS/JS) for best results\n- Use the default index.html as a test page (has bridge diagnostics)\n- The canvas persists until you `hide` it or navigate away\n- Live reload makes development fast - just save and it updates!\n- A2UI JSON push is WIP - use HTML files for now\n","isInternal":false,"tokens":1490,"sizeBytes":5735},{"name":"SKILL.md","path":"skills/clawhub/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/clawhub/SKILL.md","title":"clawhub","category":"anthropic-skill","format":"markdown","content":"---\nname: clawhub\ndescription: 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.\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"requires\": { \"bins\": [\"clawhub\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node\",\n              \"kind\": \"node\",\n              \"package\": \"clawhub\",\n              \"bins\": [\"clawhub\"],\n              \"label\": \"Install ClawHub CLI (npm)\",\n            },\n          ],\n      },\n  }\n---\n\n# ClawHub CLI\n\nInstall\n\n```bash\nnpm i -g clawhub\n```\n\nAuth (publish)\n\n```bash\nclawhub login\nclawhub whoami\n```\n\nSearch\n\n```bash\nclawhub search \"postgres backups\"\n```\n\nInstall\n\n```bash\nclawhub install my-skill\nclawhub install my-skill --version 1.2.3\n```\n\nUpdate (hash-based match + upgrade)\n\n```bash\nclawhub update my-skill\nclawhub update my-skill --version 1.2.3\nclawhub update --all\nclawhub update my-skill --force\nclawhub update --all --no-input --force\n```\n\nList\n\n```bash\nclawhub list\n```\n\nPublish\n\n```bash\nclawhub publish ./my-skill --slug my-skill --name \"My Skill\" --version 1.2.0 --changelog \"Fixes + docs\"\n```\n\nNotes\n\n- Default registry: https://clawhub.com (override with CLAWHUB_REGISTRY or --registry)\n- Default workdir: cwd (falls back to OpenClaw workspace); install dir: ./skills (override with --workdir / --dir / CLAWHUB_WORKDIR)\n- Update command hashes local files, resolves matching version, and upgrades to latest unless --version is set\n","frontmatter":{"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)"}]}}},"isInternal":false,"tokens":442,"sizeBytes":1613},{"name":"SKILL.md","path":"skills/coding-agent/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/coding-agent/SKILL.md","title":"coding-agent","category":"anthropic-skill","format":"markdown","content":"---\nname: coding-agent\ndescription: 'Delegate coding tasks to Codex, Claude Code, or Pi agents via background process. Use when: (1) building/creating new features or apps, (2) reviewing PRs (spawn in temp dir), (3) refactoring large codebases, (4) iterative coding that needs file exploration. NOT for: simple one-liner fixes (just edit), reading code (use read tool), thread-bound ACP harness requests in chat (for example spawn/run Codex or Claude Code in a Discord thread; use sessions_spawn with runtime:\"acp\"), or any work in ~/clawd workspace (never spawn agents here). Claude Code: use --print --permission-mode bypassPermissions (no PTY). Codex/Pi/OpenCode: pty:true required.'\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🧩\",\n        \"requires\": { \"anyBins\": [\"claude\", \"codex\", \"opencode\", \"pi\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node-claude\",\n              \"kind\": \"node\",\n              \"package\": \"@anthropic-ai/claude-code\",\n              \"bins\": [\"claude\"],\n              \"label\": \"Install Claude Code CLI (npm)\",\n            },\n            {\n              \"id\": \"node-codex\",\n              \"kind\": \"node\",\n              \"package\": \"@openai/codex\",\n              \"bins\": [\"codex\"],\n              \"label\": \"Install Codex CLI (npm)\",\n            },\n          ],\n      },\n  }\n---\n\n# Coding Agent (bash-first)\n\nUse **bash** (with optional background mode) for all coding agent work. Simple and effective.\n\n## ⚠️ PTY Mode: Codex/Pi/OpenCode yes, Claude Code no\n\nFor **Codex, Pi, and OpenCode**, PTY is still required (interactive terminal apps):\n\n```bash\n# ✅ Correct for Codex/Pi/OpenCode\nbash pty:true command:\"codex exec 'Your prompt'\"\n```\n\nFor **Claude Code** (`claude` CLI), use `--print --permission-mode bypassPermissions` instead.\n`--dangerously-skip-permissions` with PTY can exit after the confirmation dialog.\n`--print` mode keeps full tool access and avoids interactive confirmation:\n\n```bash\n# ✅ Correct for Claude Code (no PTY needed)\ncd /path/to/project && claude --permission-mode bypassPermissions --print 'Your task'\n\n# For background execution: use background:true on the exec tool\n\n# ❌ Wrong for Claude Code\nbash pty:true command:\"claude --dangerously-skip-permissions 'task'\"\n```\n\n### Bash Tool Parameters\n\n| Parameter    | Type    | Description                                                                 |\n| ------------ | ------- | --------------------------------------------------------------------------- |\n| `command`    | string  | The shell command to run                                                    |\n| `pty`        | boolean | **Use for coding agents!** Allocates a pseudo-terminal for interactive CLIs |\n| `workdir`    | string  | Working directory (agent sees only this folder's context)                   |\n| `background` | boolean | Run in background, returns sessionId for monitoring                         |\n| `timeout`    | number  | Timeout in seconds (kills process on expiry)                                |\n| `elevated`   | boolean | Run on host instead of sandbox (if allowed)                                 |\n\n### Process Tool Actions (for background sessions)\n\n| Action      | Description                                          |\n| ----------- | ---------------------------------------------------- |\n| `list`      | List all running/recent sessions                     |\n| `poll`      | Check if session is still running                    |\n| `log`       | Get session output (with optional offset/limit)      |\n| `write`     | Send raw data to stdin                               |\n| `submit`    | Send data + newline (like typing and pressing Enter) |\n| `send-keys` | Send key tokens or hex bytes                         |\n| `paste`     | Paste text (with optional bracketed mode)            |\n| `kill`      | Terminate the session                                |\n\n---\n\n## Quick Start: One-Shot Tasks\n\nFor quick prompts/chats, create a temp git repo and run:\n\n```bash\n# Quick chat (Codex needs a git repo!)\nSCRATCH=$(mktemp -d) && cd $SCRATCH && git init && codex exec \"Your prompt here\"\n\n# Or in a real project - with PTY!\nbash pty:true workdir:~/Projects/myproject command:\"codex exec 'Add error handling to the API calls'\"\n```\n\n**Why git init?** Codex refuses to run outside a trusted git directory. Creating a temp repo solves this for scratch work.\n\n---\n\n## The Pattern: workdir + background + pty\n\nFor longer tasks, use background mode with PTY:\n\n```bash\n# Start agent in target directory (with PTY!)\nbash pty:true workdir:~/project background:true command:\"codex exec --full-auto 'Build a snake game'\"\n# Returns sessionId for tracking\n\n# Monitor progress\nprocess action:log sessionId:XXX\n\n# Check if done\nprocess action:poll sessionId:XXX\n\n# Send input (if agent asks a question)\nprocess action:write sessionId:XXX data:\"y\"\n\n# Submit with Enter (like typing \"yes\" and pressing Enter)\nprocess action:submit sessionId:XXX data:\"yes\"\n\n# Kill if needed\nprocess action:kill sessionId:XXX\n```\n\n**Why workdir matters:** Agent wakes up in a focused directory, doesn't wander off reading unrelated files (like your soul.md 😅).\n\n---\n\n## Codex CLI\n\n**Model:** `gpt-5.2-codex` is the default (set in ~/.codex/config.toml)\n\n### Flags\n\n| Flag            | Effect                                             |\n| --------------- | -------------------------------------------------- |\n| `exec \"prompt\"` | One-shot execution, exits when done                |\n| `--full-auto`   | Sandboxed but auto-approves in workspace           |\n| `--yolo`        | NO sandbox, NO approvals (fastest, most dangerous) |\n\n### Building/Creating\n\n```bash\n# Quick one-shot (auto-approves) - remember PTY!\nbash pty:true workdir:~/project command:\"codex exec --full-auto 'Build a dark mode toggle'\"\n\n# Background for longer work\nbash pty:true workdir:~/project background:true command:\"codex --yolo 'Refactor the auth module'\"\n```\n\n### Reviewing PRs\n\n**⚠️ CRITICAL: Never review PRs in OpenClaw's own project folder!**\nClone to temp folder or use git worktree.\n\n```bash\n# Clone to temp for safe review\nREVIEW_DIR=$(mktemp -d)\ngit clone https://github.com/user/repo.git $REVIEW_DIR\ncd $REVIEW_DIR && gh pr checkout 130\nbash pty:true workdir:$REVIEW_DIR command:\"codex review --base origin/main\"\n# Clean up after: trash $REVIEW_DIR\n\n# Or use git worktree (keeps main intact)\ngit worktree add /tmp/pr-130-review pr-130-branch\nbash pty:true workdir:/tmp/pr-130-review command:\"codex review --base main\"\n```\n\n### Batch PR Reviews (parallel army!)\n\n```bash\n# Fetch all PR refs first\ngit fetch origin '+refs/pull/*/head:refs/remotes/origin/pr/*'\n\n# Deploy the army - one Codex per PR (all with PTY!)\nbash pty:true workdir:~/project background:true command:\"codex exec 'Review PR #86. git diff origin/main...origin/pr/86'\"\nbash pty:true workdir:~/project background:true command:\"codex exec 'Review PR #87. git diff origin/main...origin/pr/87'\"\n\n# Monitor all\nprocess action:list\n\n# Post results to GitHub\ngh pr comment <PR#> --body \"<review content>\"\n```\n\n---\n\n## Claude Code\n\n```bash\n# Foreground\nbash workdir:~/project command:\"claude --permission-mode bypassPermissions --print 'Your task'\"\n\n# Background\nbash workdir:~/project background:true command:\"claude --permission-mode bypassPermissions --print 'Your task'\"\n```\n\n---\n\n## OpenCode\n\n```bash\nbash pty:true workdir:~/project command:\"opencode run 'Your task'\"\n```\n\n---\n\n## Pi Coding Agent\n\n```bash\n# Install: npm install -g @mariozechner/pi-coding-agent\nbash pty:true workdir:~/project command:\"pi 'Your task'\"\n\n# Non-interactive mode (PTY still recommended)\nbash pty:true command:\"pi -p 'Summarize src/'\"\n\n# Different provider/model\nbash pty:true command:\"pi --provider openai --model gpt-4o-mini -p 'Your task'\"\n```\n\n**Note:** Pi now has Anthropic prompt caching enabled (PR #584, merged Jan 2026)!\n\n---\n\n## Parallel Issue Fixing with git worktrees\n\nFor fixing multiple issues in parallel, use git worktrees:\n\n```bash\n# 1. Create worktrees for each issue\ngit worktree add -b fix/issue-78 /tmp/issue-78 main\ngit worktree add -b fix/issue-99 /tmp/issue-99 main\n\n# 2. Launch Codex in each (background + PTY!)\nbash pty:true workdir:/tmp/issue-78 background:true command:\"pnpm install && codex --yolo 'Fix issue #78: <description>. Commit and push.'\"\nbash pty:true workdir:/tmp/issue-99 background:true command:\"pnpm install && codex --yolo 'Fix issue #99 from the approved ticket summary. Implement only the in-scope edits and commit after review.'\"\n\n# 3. Monitor progress\nprocess action:list\nprocess action:log sessionId:XXX\n\n# 4. Create PRs after fixes\ncd /tmp/issue-78 && git push -u origin fix/issue-78\ngh pr create --repo user/repo --head fix/issue-78 --title \"fix: ...\" --body \"...\"\n\n# 5. Cleanup\ngit worktree remove /tmp/issue-78\ngit worktree remove /tmp/issue-99\n```\n\n---\n\n## ⚠️ Rules\n\n1. **Use the right execution mode per agent**:\n   - Codex/Pi/OpenCode: `pty:true`\n   - Claude Code: `--print --permission-mode bypassPermissions` (no PTY required)\n2. **Respect tool choice** - if user asks for Codex, use Codex.\n   - Orchestrator mode: do NOT hand-code patches yourself.\n   - If an agent fails/hangs, respawn it or ask the user for direction, but don't silently take over.\n3. **Be patient** - don't kill sessions because they're \"slow\"\n4. **Monitor with process:log** - check progress without interfering\n5. **--full-auto for building** - auto-approves changes\n6. **vanilla for reviewing** - no special flags needed\n7. **Parallel is OK** - run many Codex processes at once for batch work\n8. **NEVER start Codex inside your OpenClaw state directory** (`$OPENCLAW_STATE_DIR`, default `~/.openclaw`) - it'll read your soul docs and get weird ideas about the org chart!\n9. **NEVER checkout branches in ~/Projects/openclaw/** - that's the LIVE OpenClaw instance!\n\n---\n\n## Progress Updates (Critical)\n\nWhen you spawn coding agents in the background, keep the user in the loop.\n\n- Send 1 short message when you start (what's running + where).\n- Then only update again when something changes:\n  - a milestone completes (build finished, tests passed)\n  - the agent asks a question / needs input\n  - you hit an error or need user action\n  - the agent finishes (include what changed + where)\n- If you kill a session, immediately say you killed it and why.\n\nThis prevents the user from seeing only \"Agent failed before reply\" and having no idea what happened.\n\n---\n\n## Auto-Notify on Completion\n\nFor long-running background tasks, append a wake trigger to your prompt so OpenClaw gets notified immediately when the agent finishes (instead of waiting for the next heartbeat):\n\n```\n... your task here.\n\nWhen completely finished, run this command to notify me:\nopenclaw system event --text \"Done: [brief summary of what was built]\" --mode now\n```\n\n**Example:**\n\n```bash\nbash pty:true workdir:~/project background:true command:\"codex --yolo exec 'Build a REST API for todos.\n\nWhen completely finished, run: openclaw system event --text \\\"Done: Built todos REST API with CRUD endpoints\\\" --mode now'\"\n```\n\nThis triggers an immediate wake event — Skippy gets pinged in seconds, not 10 minutes.\n\n---\n\n## Learnings (Jan 2026)\n\n- **PTY is essential:** Coding agents are interactive terminal apps. Without `pty:true`, output breaks or agent hangs.\n- **Git repo required:** Codex won't run outside a git directory. Use `mktemp -d && git init` for scratch work.\n- **exec is your friend:** `codex exec \"prompt\"` runs and exits cleanly - perfect for one-shots.\n- **submit vs write:** Use `submit` to send input + Enter, `write` for raw data without newline.\n- **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\"_ 🦞\n","frontmatter":{"name":"coding-agent","description":"Delegate coding tasks to Codex, Claude Code, or Pi agents via background process. Use when: (1) building/creating new features or apps, (2) reviewing PRs (spawn in temp dir), (3) refactoring large codebases, (4) iterative coding that needs file exploration. NOT for: simple one-liner fixes (just edit), reading code (use read tool), thread-bound ACP harness requests in chat (for example spawn/run Codex or Claude Code in a Discord thread; use sessions_spawn with runtime:\"acp\"), or any work in ~/clawd workspace (never spawn agents here). Claude Code: use --print --permission-mode bypassPermissions (no PTY). Codex/Pi/OpenCode: pty:true required.","metadata":{"openclaw":{"emoji":"🧩","requires":{"anyBins":["claude","codex","opencode","pi"]},"install":[{"id":"node-claude","kind":"node","package":"@anthropic-ai/claude-code","bins":["claude"],"label":"Install Claude Code CLI (npm)"},{"id":"node-codex","kind":"node","package":"@openai/codex","bins":["codex"],"label":"Install Codex CLI (npm)"}]}}},"isInternal":false,"tokens":2889,"sizeBytes":11825},{"name":"SKILL.md","path":"skills/discord/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/discord/SKILL.md","title":"discord","category":"anthropic-skill","format":"markdown","content":"---\nname: discord\ndescription: \"Discord ops via the message tool (channel=discord).\"\nmetadata: { \"openclaw\": { \"emoji\": \"🎮\", \"requires\": { \"config\": [\"channels.discord.token\"] } } }\nallowed-tools: [\"message\"]\n---\n\n# Discord (Via `message`)\n\nUse the `message` tool. No provider-specific `discord` tool exposed to the agent.\n\n## Musts\n\n- Always: `channel: \"discord\"`.\n- Respect gating: `channels.discord.actions.*` (some default off: `roles`, `moderation`, `presence`, `channels`).\n- Prefer explicit ids: `guildId`, `channelId`, `messageId`, `userId`.\n- Multi-account: optional `accountId`.\n\n## Guidelines\n\n- Avoid Markdown tables in outbound Discord messages.\n- Mention users as `<@USER_ID>`.\n- Prefer Discord components v2 (`components`) for rich UI; use legacy `embeds` only when you must.\n\n## Targets\n\n- Send-like actions: `to: \"channel:<id>\"` or `to: \"user:<id>\"`.\n- Message-specific actions: `channelId: \"<id>\"` (or `to`) + `messageId: \"<id>\"`.\n\n## Common Actions (Examples)\n\nSend message:\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"hello\",\n  \"silent\": true\n}\n```\n\nSend with media:\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"see attachment\",\n  \"media\": \"file:///tmp/example.png\"\n}\n```\n\n- Optional `silent: true` to suppress Discord notifications.\n\nSend with components v2 (recommended for rich UI):\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"Status update\",\n  \"components\": \"[Carbon v2 components]\"\n}\n```\n\n- `components` expects Carbon component instances (Container, TextDisplay, etc.) from JS/TS integrations.\n- Do not combine `components` with `embeds` (Discord rejects v2 + embeds).\n\nLegacy embeds (not recommended):\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"Status update\",\n  \"embeds\": [{ \"title\": \"Legacy\", \"description\": \"Embeds are legacy.\" }]\n}\n```\n\n- `embeds` are ignored when components v2 are present.\n\nReact:\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"emoji\": \"✅\"\n}\n```\n\nRead:\n\n```json\n{\n  \"action\": \"read\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"limit\": 20\n}\n```\n\nEdit / delete:\n\n```json\n{\n  \"action\": \"edit\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"message\": \"fixed typo\"\n}\n```\n\n```json\n{\n  \"action\": \"delete\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\"\n}\n```\n\nPoll:\n\n```json\n{\n  \"action\": \"poll\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"pollQuestion\": \"Lunch?\",\n  \"pollOption\": [\"Pizza\", \"Sushi\", \"Salad\"],\n  \"pollMulti\": false,\n  \"pollDurationHours\": 24\n}\n```\n\nPins:\n\n```json\n{\n  \"action\": \"pin\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\"\n}\n```\n\nThreads:\n\n```json\n{\n  \"action\": \"thread-create\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"threadName\": \"bug triage\"\n}\n```\n\nSearch:\n\n```json\n{\n  \"action\": \"search\",\n  \"channel\": \"discord\",\n  \"guildId\": \"999\",\n  \"query\": \"release notes\",\n  \"channelIds\": [\"123\", \"456\"],\n  \"limit\": 10\n}\n```\n\nPresence (often gated):\n\n```json\n{\n  \"action\": \"set-presence\",\n  \"channel\": \"discord\",\n  \"activityType\": \"playing\",\n  \"activityName\": \"with fire\",\n  \"status\": \"online\"\n}\n```\n\n## Writing Style (Discord)\n\n- Short, conversational, low ceremony.\n- No markdown tables.\n- Mention users as `<@USER_ID>`.\n","frontmatter":{"name":"discord","description":"Discord ops via the message tool (channel=discord).","metadata":{"openclaw":{"emoji":"🎮","requires":{"config":["channels.discord.token"]}}},"allowed-tools":["message"],"allowedTools":["message"]},"allowedTools":["message"],"isInternal":false,"tokens":1037,"sizeBytes":3446},{"name":"SKILL.md","path":"skills/eightctl/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/eightctl/SKILL.md","title":"eightctl","category":"anthropic-skill","format":"markdown","content":"---\nname: eightctl\ndescription: Control Eight Sleep pods (status, temperature, alarms, schedules).\nhomepage: https://eightctl.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🛌\",\n        \"requires\": { \"bins\": [\"eightctl\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/eightctl/cmd/eightctl@latest\",\n              \"bins\": [\"eightctl\"],\n              \"label\": \"Install eightctl (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# eightctl\n\nUse `eightctl` for Eight Sleep pod control. Requires auth.\n\nAuth\n\n- Config: `~/.config/eightctl/config.yaml`\n- Env: `EIGHTCTL_EMAIL`, `EIGHTCTL_PASSWORD`\n\nQuick start\n\n- `eightctl status`\n- `eightctl on|off`\n- `eightctl temp 20`\n\nCommon tasks\n\n- Alarms: `eightctl alarm list|create|dismiss`\n- Schedules: `eightctl schedule list|create|update`\n- Audio: `eightctl audio state|play|pause`\n- Base: `eightctl base info|angle`\n\nNotes\n\n- API is unofficial and rate-limited; avoid repeated logins.\n- Confirm before changing temperature or alarms.\n","frontmatter":{"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)"}]}}},"isInternal":false,"tokens":286,"sizeBytes":1094},{"name":"SKILL.md","path":"skills/gemini/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/gemini/SKILL.md","title":"gemini","category":"anthropic-skill","format":"markdown","content":"---\nname: gemini\ndescription: Gemini CLI for one-shot Q&A, summaries, and generation.\nhomepage: https://ai.google.dev/\nmetadata:\n  {\n    \"emoji\": \"✨\",\n    \"requires\": { \"bins\": [\"gemini\"] }\n  }\n---\n\n# Gemini CLI\n\nUse Gemini in one-shot mode with a positional prompt (avoid interactive mode).\n\nQuick start\n\n- `gemini \"Answer this question...\"`\n- `gemini --model <name> \"Prompt...\"`\n- `gemini --output-format json \"Return JSON\"`\n\nExtensions\n\n- List: `gemini --list-extensions`\n- Manage: `gemini extensions <command>`\n\nNotes\n\n- If auth is required, run `gemini` once interactively and follow the login flow.\n- Avoid `--yolo` for safety.\n","frontmatter":{"name":"gemini","description":"Gemini CLI for one-shot Q&A, summaries, and generation.","homepage":"https://ai.google.dev/","metadata":{"emoji":"✨","requires":{"bins":["gemini"]}}},"isInternal":false,"tokens":170,"sizeBytes":636},{"name":"SKILL.md","path":"skills/gh-issues/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/gh-issues/SKILL.md","title":"gh-issues","category":"anthropic-skill","format":"markdown","content":"---\nname: gh-issues\ndescription: \"Fetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]\"\nuser-invocable: true\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"requires\": { \"bins\": [\"curl\", \"git\", \"gh\"] },\n        \"primaryEnv\": \"GH_TOKEN\",\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"gh\",\n              \"bins\": [\"gh\"],\n              \"label\": \"Install GitHub CLI (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# gh-issues — Auto-fix GitHub Issues with Parallel Sub-agents\n\nYou are an orchestrator. Follow these 6 phases exactly. Do not skip phases.\n\nIMPORTANT — No `gh` CLI dependency. This skill uses curl + the GitHub REST API exclusively. The GH_TOKEN env var is already injected by OpenClaw. Pass it as a Bearer token in all API calls:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" ...\n```\n\n---\n\n## Phase 1 — Parse Arguments\n\nParse the arguments string provided after /gh-issues.\n\nPositional:\n\n- owner/repo — optional. This is the source repo to fetch issues from. If omitted, detect from the current git remote:\n  `git remote get-url origin`\n  Extract owner/repo from the URL (handles both HTTPS and SSH formats).\n  - HTTPS: https://github.com/owner/repo.git → owner/repo\n  - SSH: git@github.com:owner/repo.git → owner/repo\n    If not in a git repo or no remote found, stop with an error asking the user to specify owner/repo.\n\nFlags (all optional):\n| Flag | Default | Description |\n|------|---------|-------------|\n| --label | _(none)_ | Filter by label (e.g. bug, `enhancement`) |\n| --limit | 10 | Max issues to fetch per poll |\n| --milestone | _(none)_ | Filter by milestone title |\n| --assignee | _(none)_ | Filter by assignee (`@me` for self) |\n| --state | open | Issue state: open, closed, all |\n| --fork | _(none)_ | Your fork (`user/repo`) to push branches and open PRs from. Issues are fetched from the source repo; code is pushed to the fork; PRs are opened from the fork to the source repo. |\n| --watch | false | Keep polling for new issues and PR reviews after each batch |\n| --interval | 5 | Minutes between polls (only with `--watch`) |\n| --dry-run | false | Fetch and display only — no sub-agents |\n| --yes | false | Skip confirmation and auto-process all filtered issues |\n| --reviews-only | false | Skip issue processing (Phases 2-5). Only run Phase 6 — check open PRs for review comments and address them. |\n| --cron | false | Cron-safe mode: fetch issues and spawn sub-agents, exit without waiting for results. |\n| --model | _(none)_ | Model to use for sub-agents (e.g. `glm-5`, `zai/glm-5`). If not specified, uses the agent's default model. |\n| --notify-channel | _(none)_ | Telegram channel ID to send final PR summary to (e.g. -1002381931352). Only the final result with PR links is sent, not status updates. |\n\nStore parsed values for use in subsequent phases.\n\nDerived values:\n\n- SOURCE_REPO = the positional owner/repo (where issues live)\n- PUSH_REPO = --fork value if provided, otherwise same as SOURCE_REPO\n- FORK_MODE = true if --fork was provided, false otherwise\n\n**If `--reviews-only` is set:** Skip directly to Phase 6. Run token resolution (from Phase 2) first, then jump to Phase 6.\n\n**If `--cron` is set:**\n\n- Force `--yes` (skip confirmation)\n- If `--reviews-only` is also set, run token resolution then jump to Phase 6 (cron review mode)\n- Otherwise, proceed normally through Phases 2-5 with cron-mode behavior active\n\n---\n\n## Phase 2 — Fetch Issues\n\n**Token Resolution:**\nFirst, ensure GH_TOKEN is available. Check environment:\n\n```\necho $GH_TOKEN\n```\n\nIf empty, read from config:\n\n```\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\ncat \"$CONFIG_PATH\" | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty'\n```\n\nIf still empty, check `/data/.clawdbot/openclaw.json`:\n\n```\ncat /data/.clawdbot/openclaw.json | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty'\n```\n\nExport as GH_TOKEN for subsequent commands:\n\n```\nexport GH_TOKEN=\"<token>\"\n```\n\nBuild and run a curl request to the GitHub Issues API via exec:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/issues?per_page={limit}&state={state}&{query_params}\"\n```\n\nWhere {query_params} is built from:\n\n- labels={label} if --label was provided\n- milestone={milestone} if --milestone was provided (note: API expects milestone _number_, so if user provides a title, first resolve it via GET /repos/{SOURCE_REPO}/milestones and match by title)\n- assignee={assignee} if --assignee was provided (if @me, first resolve your username via `GET /user`)\n\nIMPORTANT: The GitHub Issues API also returns pull requests. Filter them out — exclude any item where pull_request key exists in the response object.\n\nIf in watch mode: Also filter out any issue numbers already in the PROCESSED_ISSUES set from previous batches.\n\nError handling:\n\n- If curl returns an HTTP 401 or 403 → stop and tell the user:\n  > \"GitHub authentication failed. Please check your apiKey in the OpenClaw dashboard or in the active OpenClaw config path (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`) under `skills.entries.gh-issues`.\"\n- If the response is an empty array (after filtering) → report \"No issues found matching filters\" and stop (or loop back if in watch mode).\n- If curl fails or returns any other error → report the error verbatim and stop.\n\nParse the JSON response. For each issue, extract: number, title, body, labels (array of label names), assignees, html_url.\n\n---\n\n## Phase 3 — Present & Confirm\n\nDisplay a markdown table of fetched issues:\n\n| #   | Title                         | Labels        |\n| --- | ----------------------------- | ------------- |\n| 42  | Fix null pointer in parser    | bug, critical |\n| 37  | Add retry logic for API calls | enhancement   |\n\nIf FORK_MODE is active, also display:\n\n> \"Fork mode: branches will be pushed to {PUSH_REPO}, PRs will target `{SOURCE_REPO}`\"\n\nIf `--dry-run` is active:\n\n- Display the table and stop. Do not proceed to Phase 4.\n\nIf `--yes` is active:\n\n- Display the table for visibility\n- Auto-process ALL listed issues without asking for confirmation\n- Proceed directly to Phase 4\n\nOtherwise:\nAsk the user to confirm which issues to process:\n\n- \"all\" — process every listed issue\n- Comma-separated numbers (e.g. `42, 37`) — process only those\n- \"cancel\" — abort entirely\n\nWait for user response before proceeding.\n\nWatch mode note: On the first poll, always confirm with the user (unless --yes is set). On subsequent polls, auto-process all new issues without re-confirming (the user already opted in). Still display the table so they can see what's being processed.\n\n---\n\n## Phase 4 — Pre-flight Checks\n\nRun these checks sequentially via exec:\n\n1. **Dirty working tree check:**\n\n   ```\n   git status --porcelain\n   ```\n\n   If output is non-empty, warn the user:\n\n   > \"Working tree has uncommitted changes. Sub-agents will create branches from HEAD — uncommitted changes will NOT be included. Continue?\"\n   > Wait for confirmation. If declined, stop.\n\n2. **Record base branch:**\n\n   ```\n   git rev-parse --abbrev-ref HEAD\n   ```\n\n   Store as BASE_BRANCH.\n\n3. **Verify remote access:**\n   If FORK_MODE:\n   - Verify the fork remote exists. Check if a git remote named `fork` exists:\n     ```\n     git remote get-url fork\n     ```\n     If it doesn't exist, add it:\n     ```\n     git remote add fork https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\n     ```\n   - Also verify origin (the source repo) is reachable:\n     ```\n     git ls-remote --exit-code origin HEAD\n     ```\n\n   If not FORK_MODE:\n\n   ```\n   git ls-remote --exit-code origin HEAD\n   ```\n\n   If this fails, stop with: \"Cannot reach remote origin. Check your network and git config.\"\n\n4. **Verify GH_TOKEN validity:**\n\n   ```\n   curl -s -o /dev/null -w \"%{http_code}\" -H \"Authorization: Bearer $GH_TOKEN\" https://api.github.com/user\n   ```\n\n   If HTTP status is not 200, stop with:\n\n   > \"GitHub authentication failed. Please check your apiKey in the OpenClaw dashboard or in the active OpenClaw config path (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`) under `skills.entries.gh-issues`.\"\n\n5. **Check for existing PRs:**\n   For each confirmed issue number N, run:\n\n   ```\n   curl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n     \"https://api.github.com/repos/{SOURCE_REPO}/pulls?head={PUSH_REPO_OWNER}:fix/issue-{N}&state=open&per_page=1\"\n   ```\n\n   (Where PUSH_REPO_OWNER is the owner portion of `PUSH_REPO`)\n   If the response array is non-empty, remove that issue from the processing list and report:\n\n   > \"Skipping #{N} — PR already exists: {html_url}\"\n\n   If all issues are skipped, report and stop (or loop back if in watch mode).\n\n6. **Check for in-progress branches (no PR yet = sub-agent still working):**\n   For each remaining issue number N (not already skipped by the PR check above), check if a `fix/issue-{N}` branch exists on the **push repo** (which may be a fork, not origin):\n\n   ```\n   curl -s -o /dev/null -w \"%{http_code}\" \\\n     -H \"Authorization: Bearer $GH_TOKEN\" \\\n     \"https://api.github.com/repos/{PUSH_REPO}/branches/fix/issue-{N}\"\n   ```\n\n   If HTTP 200 → the branch exists on the push repo but no open PR was found for it in step 5. Skip that issue:\n\n   > \"Skipping #{N} — branch fix/issue-{N} exists on {PUSH_REPO}, fix likely in progress\"\n\n   This check uses the GitHub API instead of `git ls-remote` so it works correctly in fork mode (where branches are pushed to the fork, not origin).\n\n   If all issues are skipped after this check, report and stop (or loop back if in watch mode).\n\n7. **Check claim-based in-progress tracking:**\n   This prevents duplicate processing when a sub-agent from a previous cron run is still working but hasn't pushed a branch or opened a PR yet.\n\n   Read the claims file (create empty `{}` if missing):\n\n   ```\n   CLAIMS_FILE=\"/data/.clawdbot/gh-issues-claims.json\"\n   if [ ! -f \"$CLAIMS_FILE\" ]; then\n     mkdir -p /data/.clawdbot\n     echo '{}' > \"$CLAIMS_FILE\"\n   fi\n   ```\n\n   Parse the claims file. For each entry, check if the claim timestamp is older than 2 hours. If so, remove it (expired — the sub-agent likely finished or failed silently). Write back the cleaned file:\n\n   ```\n   CLAIMS=$(cat \"$CLAIMS_FILE\")\n   CUTOFF=$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-2H +%Y-%m-%dT%H:%M:%SZ)\n   CLAIMS=$(echo \"$CLAIMS\" | jq --arg cutoff \"$CUTOFF\" 'to_entries | map(select(.value > $cutoff)) | from_entries')\n   echo \"$CLAIMS\" > \"$CLAIMS_FILE\"\n   ```\n\n   For each remaining issue number N (not already skipped by steps 5 or 6), check if `{SOURCE_REPO}#{N}` exists as a key in the claims file.\n\n   If claimed and not expired → skip:\n\n   > \"Skipping #{N} — sub-agent claimed this issue {minutes}m ago, still within timeout window\"\n\n   Where `{minutes}` is calculated from the claim timestamp to now.\n\n   If all issues are skipped after this check, report and stop (or loop back if in watch mode).\n\n---\n\n## Phase 5 — Spawn Sub-agents (Parallel)\n\n**Cron mode (`--cron` is active):**\n\n- **Sequential cursor tracking:** Use a cursor file to track which issue to process next:\n\n  ```\n  CURSOR_FILE=\"/data/.clawdbot/gh-issues-cursor-{SOURCE_REPO_SLUG}.json\"\n  # SOURCE_REPO_SLUG = owner-repo with slashes replaced by hyphens (e.g., openclaw-openclaw)\n  ```\n\n  Read the cursor file (create if missing):\n\n  ```\n  if [ ! -f \"$CURSOR_FILE\" ]; then\n    echo '{\"last_processed\": null, \"in_progress\": null}' > \"$CURSOR_FILE\"\n  fi\n  ```\n\n  - `last_processed`: issue number of the last completed issue (or null if none)\n  - `in_progress`: issue number currently being processed (or null)\n\n- **Select next issue:** Filter the fetched issues list to find the first issue where:\n  - Issue number > last_processed (if last_processed is set)\n  - AND issue is not in the claims file (not already in progress)\n  - AND no PR exists for the issue (checked in Phase 4 step 5)\n  - AND no branch exists on the push repo (checked in Phase 4 step 6)\n- If no eligible issue is found after the last_processed cursor, wrap around to the beginning (start from the oldest eligible issue).\n\n- If an eligible issue is found:\n  1. Mark it as in_progress in the cursor file\n  2. Spawn a single sub-agent for that one issue with `cleanup: \"keep\"` and `runTimeoutSeconds: 3600`\n  3. If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n  4. If `--notify-channel` was provided, include the channel in the task so the sub-agent can notify\n  5. Do NOT await the sub-agent result — fire and forget\n  6. **Write claim:** After spawning, read the claims file, add `{SOURCE_REPO}#{N}` with the current ISO timestamp, and write it back\n  7. Immediately report: \"Spawned fix agent for #{N} — will create PR when complete\"\n  8. Exit the skill. Do not proceed to Results Collection or Phase 6.\n\n- If no eligible issue is found (all issues either have PRs, have branches, or are in progress), report \"No eligible issues to process — all issues have PRs/branches or are in progress\" and exit.\n\n**Normal mode (`--cron` is NOT active):**\nFor each confirmed issue, spawn a sub-agent using sessions_spawn. Launch up to 8 concurrently (matching `subagents.maxConcurrent: 8`). If more than 8 issues, batch them — launch the next agent as each completes.\n\n**Write claims:** After spawning each sub-agent, read the claims file, add `{SOURCE_REPO}#{N}` with the current ISO timestamp, and write it back (same procedure as cron mode above). This covers interactive usage where watch mode might overlap with cron runs.\n\n### Sub-agent Task Prompt\n\nFor each issue, construct the following prompt and pass it to sessions_spawn. Variables to inject into the template:\n\n- {SOURCE_REPO} — upstream repo where the issue lives\n- {PUSH_REPO} — repo to push branches to (same as SOURCE_REPO unless fork mode)\n- {FORK_MODE} — true/false\n- {PUSH_REMOTE} — `fork` if FORK_MODE, otherwise `origin`\n- {number}, {title}, {url}, {labels}, {body} — from the issue\n- {BASE_BRANCH} — from Phase 4\n- {notify_channel} — Telegram channel ID for notifications (empty if not set). Replace {notify_channel} in the template below with the value of `--notify-channel` flag (or leave as empty string if not provided).\n\nWhen constructing the task, replace all template variables including {notify_channel} with actual values.\n\n```\nYou are a focused code-fix agent. Your task is to fix a single GitHub issue and open a PR.\n\nIMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.\n\nFirst, ensure GH_TOKEN is set. Check: `echo $GH_TOKEN`. If empty, read from config:\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nGH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty')\n\nUse the token in all GitHub API calls:\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" ...\n\n<config>\nSource repo (issues): {SOURCE_REPO}\nPush repo (branches + PRs): {PUSH_REPO}\nFork mode: {FORK_MODE}\nPush remote name: {PUSH_REMOTE}\nBase branch: {BASE_BRANCH}\nNotify channel: {notify_channel}\n</config>\n\n<issue>\nRepository: {SOURCE_REPO}\nIssue: #{number}\nTitle: {title}\nURL: {url}\nLabels: {labels}\nBody: {body}\n</issue>\n\n<instructions>\nFollow these steps in order. If any step fails, report the failure and stop.\n\n0. SETUP — Ensure GH_TOKEN is available:\n```\n\nexport GH_TOKEN=$(node -e \"const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/openclaw.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')\")\n\n```\nIf that fails, also try:\n```\n\nexport CONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nexport GH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | node -e \"const fs=require('fs');const d=JSON.parse(fs.readFileSync(0,'utf8'));console.log(d.skills?.entries?.['gh-issues']?.apiKey||'')\")\n\n```\nVerify: echo \"Token: ${GH_TOKEN:0:10}...\"\n\n1. CONFIDENCE CHECK — Before implementing, assess whether this issue is actionable:\n- Read the issue body carefully. Is the problem clearly described?\n- Search the codebase (grep/find) for the relevant code. Can you locate it?\n- Is the scope reasonable? (single file/function = good, whole subsystem = bad)\n- Is a specific fix suggested or is it a vague complaint?\n\nRate your confidence (1-10). If confidence < 7, STOP and report:\n> \"Skipping #{number}: Low confidence (score: N/10) — [reason: vague requirements | cannot locate code | scope too large | no clear fix suggested]\"\n\nOnly proceed if confidence >= 7.\n\n1. UNDERSTAND — Read the issue carefully. Identify what needs to change and where.\n\n2. BRANCH — Create a feature branch from the base branch:\ngit checkout -b fix/issue-{number} {BASE_BRANCH}\n\n3. ANALYZE — Search the codebase to find relevant files:\n- Use grep/find via exec to locate code related to the issue\n- Read the relevant files to understand the current behavior\n- Identify the root cause\n\n4. IMPLEMENT — Make the minimal, focused fix:\n- Follow existing code style and conventions\n- Change only what is necessary to fix the issue\n- Do not add unrelated changes or new dependencies without justification\n\n5. TEST — Discover and run the existing test suite if one exists:\n- Look for package.json scripts, Makefile targets, pytest, cargo test, etc.\n- Run the relevant tests\n- If tests fail after your fix, attempt ONE retry with a corrected approach\n- If tests still fail, report the failure\n\n6. COMMIT — Stage and commit your changes:\ngit add {changed_files}\ngit commit -m \"fix: {short_description}\n\nFixes {SOURCE_REPO}#{number}\"\n\n7. PUSH — Push the branch:\nFirst, ensure the push remote uses token auth and disable credential helpers:\ngit config --global credential.helper \"\"\ngit remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\nThen push:\nGIT_ASKPASS=true git push -u {PUSH_REMOTE} fix/issue-{number}\n\n8. PR — Create a pull request using the GitHub API:\n\nIf FORK_MODE is true, the PR goes from your fork to the source repo:\n- head = \"{PUSH_REPO_OWNER}:fix/issue-{number}\"\n- base = \"{BASE_BRANCH}\"\n- PR is created on {SOURCE_REPO}\n\nIf FORK_MODE is false:\n- head = \"fix/issue-{number}\"\n- base = \"{BASE_BRANCH}\"\n- PR is created on {SOURCE_REPO}\n\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/pulls \\\n  -d '{\n    \"title\": \"fix: {title}\",\n    \"head\": \"{head_value}\",\n    \"base\": \"{BASE_BRANCH}\",\n    \"body\": \"## Summary\\n\\n{one_paragraph_description_of_fix}\\n\\n## Changes\\n\\n{bullet_list_of_changes}\\n\\n## Testing\\n\\n{what_was_tested_and_results}\\n\\nFixes {SOURCE_REPO}#{number}\"\n  }'\n\nExtract the `html_url` from the response — this is the PR link.\n\n9. REPORT — Send back a summary:\n- PR URL (the html_url from step 8)\n- Files changed (list)\n- Fix summary (1-2 sentences)\n- Any caveats or concerns\n\n10. NOTIFY (if notify_channel is set) — If {notify_channel} is not empty, send a notification to the Telegram channel:\n```\n\nUse the message tool with:\n\n- action: \"send\"\n- channel: \"telegram\"\n- target: \"{notify_channel}\"\n- message: \"✅ PR Created: {SOURCE_REPO}#{number}\n\n{title}\n\n{pr_url}\n\nFiles changed: {files_changed_list}\"\n\n```\n</instructions>\n\n<constraints>\n- No force-push, no modifying the base branch\n- No unrelated changes or gratuitous refactoring\n- No new dependencies without strong justification\n- If the issue is unclear or too complex to fix confidently, report your analysis instead of guessing\n- Do NOT use the gh CLI — it is not available. Use curl + GitHub REST API for all GitHub operations.\n- GH_TOKEN is already in the environment — do NOT prompt for auth\n- Time limit: you have 60 minutes max. Be thorough — analyze properly, test your fix, don't rush.\n</constraints>\n```\n\n### Spawn configuration per sub-agent:\n\n- runTimeoutSeconds: 3600 (60 minutes)\n- cleanup: \"keep\" (preserve transcripts for review)\n- If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n\n### Timeout Handling\n\nIf a sub-agent exceeds 60 minutes, record it as:\n\n> \"#{N} — Timed out (issue may be too complex for auto-fix)\"\n\n---\n\n## Results Collection\n\n**If `--cron` is active:** Skip this section entirely — the orchestrator already exited after spawning in Phase 5.\n\nAfter ALL sub-agents complete (or timeout), collect their results. Store the list of successfully opened PRs in `OPEN_PRS` (PR number, branch name, issue number, PR URL) for use in Phase 6.\n\nPresent a summary table:\n\n| Issue                 | Status    | PR                             | Notes                          |\n| --------------------- | --------- | ------------------------------ | ------------------------------ |\n| #42 Fix null pointer  | PR opened | https://github.com/.../pull/99 | 3 files changed                |\n| #37 Add retry logic   | Failed    | --                             | Could not identify target code |\n| #15 Update docs       | Timed out | --                             | Too complex for auto-fix       |\n| #8 Fix race condition | Skipped   | --                             | PR already exists              |\n\n**Status values:**\n\n- **PR opened** — success, link to PR\n- **Failed** — sub-agent could not complete (include reason in Notes)\n- **Timed out** — exceeded 60-minute limit\n- **Skipped** — existing PR detected in pre-flight\n\nEnd with a one-line summary:\n\n> \"Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.\"\n\n**Send notification to channel (if --notify-channel is set):**\nIf `--notify-channel` was provided, send the final summary to that Telegram channel using the `message` tool:\n\n```\nUse the message tool with:\n- action: \"send\"\n- channel: \"telegram\"\n- target: \"{notify-channel}\"\n- message: \"✅ GitHub Issues Processed\n\nProcessed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.\n\n{PR_LIST}\"\n\nWhere PR_LIST includes only successfully opened PRs in format:\n• #{issue_number}: {PR_url} ({notes})\n```\n\nThen proceed to Phase 6.\n\n---\n\n## Phase 6 — PR Review Handler\n\nThis phase monitors open PRs (created by this skill or pre-existing `fix/issue-*` PRs) for review comments and spawns sub-agents to address them.\n\n**When this phase runs:**\n\n- After Results Collection (Phases 2-5 completed) — checks PRs that were just opened\n- When `--reviews-only` flag is set — skips Phases 2-5 entirely, runs only this phase\n- In watch mode — runs every poll cycle after checking for new issues\n\n**Cron review mode (`--cron --reviews-only`):**\nWhen both `--cron` and `--reviews-only` are set:\n\n1. Run token resolution (Phase 2 token section)\n2. Discover open `fix/issue-*` PRs (Step 6.1)\n3. Fetch review comments (Step 6.2)\n4. **Analyze comment content for actionability** (Step 6.3)\n5. If actionable comments are found, spawn ONE review-fix sub-agent for the first PR with unaddressed comments — fire-and-forget (do NOT await result)\n   - Use `cleanup: \"keep\"` and `runTimeoutSeconds: 3600`\n   - If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n6. Report: \"Spawned review handler for PR #{N} — will push fixes when complete\"\n7. Exit the skill immediately. Do not proceed to Step 6.5 (Review Results).\n\nIf no actionable comments found, report \"No actionable review comments found\" and exit.\n\n**Normal mode (non-cron) continues below:**\n\n### Step 6.1 — Discover PRs to Monitor\n\nCollect PRs to check for review comments:\n\n**If coming from Phase 5:** Use the `OPEN_PRS` list from Results Collection.\n\n**If `--reviews-only` or subsequent watch cycle:** Fetch all open PRs with `fix/issue-` branch pattern:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls?state=open&per_page=100\"\n```\n\nFilter to only PRs where `head.ref` starts with `fix/issue-`.\n\nFor each PR, extract: `number` (PR number), `head.ref` (branch name), `html_url`, `title`, `body`.\n\nIf no PRs found, report \"No open fix/ PRs to monitor\" and stop (or loop back if in watch mode).\n\n### Step 6.2 — Fetch All Review Sources\n\nFor each PR, fetch reviews from multiple sources:\n\n**Fetch PR reviews:**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/reviews\"\n```\n\n**Fetch PR review comments (inline/file-level):**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments\"\n```\n\n**Fetch PR issue comments (general conversation):**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments\"\n```\n\n**Fetch PR body for embedded reviews:**\nSome review tools (like Greptile) embed their feedback directly in the PR body. Check for:\n\n- `<!-- greptile_comment -->` markers\n- Other structured review sections in the PR body\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}\"\n```\n\nExtract the `body` field and parse for embedded review content.\n\n### Step 6.3 — Analyze Comments for Actionability\n\n**Determine the bot's own username** for filtering:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" https://api.github.com/user | jq -r '.login'\n```\n\nStore as `BOT_USERNAME`. Exclude any comment where `user.login` equals `BOT_USERNAME`.\n\n**For each comment/review, analyze the content to determine if it requires action:**\n\n**NOT actionable (skip):**\n\n- Pure approvals or \"LGTM\" without suggestions\n- Bot comments that are informational only (CI status, auto-generated summaries without specific requests)\n- Comments already addressed (check if bot replied with \"Addressed in commit...\")\n- Reviews with state `APPROVED` and no inline comments requesting changes\n\n**IS actionable (requires attention):**\n\n- Reviews with state `CHANGES_REQUESTED`\n- Reviews with state `COMMENTED` that contain specific requests:\n  - \"this test needs to be updated\"\n  - \"please fix\", \"change this\", \"update\", \"can you\", \"should be\", \"needs to\"\n  - \"will fail\", \"will break\", \"causes an error\"\n  - Mentions of specific code issues (bugs, missing error handling, edge cases)\n- Inline review comments pointing out issues in the code\n- Embedded reviews in PR body that identify:\n  - Critical issues or breaking changes\n  - Test failures expected\n  - Specific code that needs attention\n  - Confidence scores with concerns\n\n**Parse embedded review content (e.g., Greptile):**\nLook for sections marked with `<!-- greptile_comment -->` or similar. Extract:\n\n- Summary text\n- Any mentions of \"Critical issue\", \"needs attention\", \"will fail\", \"test needs to be updated\"\n- Confidence scores below 4/5 (indicates concerns)\n\n**Build actionable_comments list** with:\n\n- Source (review, inline comment, PR body, etc.)\n- Author\n- Body text\n- For inline: file path and line number\n- Specific action items identified\n\nIf no actionable comments found across any PR, report \"No actionable review comments found\" and stop (or loop back if in watch mode).\n\n### Step 6.4 — Present Review Comments\n\nDisplay a table of PRs with pending actionable comments:\n\n```\n| PR | Branch | Actionable Comments | Sources |\n|----|--------|---------------------|---------|\n| #99 | fix/issue-42 | 2 comments | @reviewer1, greptile |\n| #101 | fix/issue-37 | 1 comment | @reviewer2 |\n```\n\nIf `--yes` is NOT set and this is not a subsequent watch poll: ask the user to confirm which PRs to address (\"all\", comma-separated PR numbers, or \"skip\").\n\n### Step 6.5 — Spawn Review Fix Sub-agents (Parallel)\n\nFor each PR with actionable comments, spawn a sub-agent. Launch up to 8 concurrently.\n\n**Review fix sub-agent prompt:**\n\n```\nYou are a PR review handler agent. Your task is to address review comments on a pull request by making the requested changes, pushing updates, and replying to each comment.\n\nIMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.\n\nFirst, ensure GH_TOKEN is set. Check: echo $GH_TOKEN. If empty, read from config:\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nGH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty')\n\n<config>\nRepository: {SOURCE_REPO}\nPush repo: {PUSH_REPO}\nFork mode: {FORK_MODE}\nPush remote: {PUSH_REMOTE}\nPR number: {pr_number}\nPR URL: {pr_url}\nBranch: {branch_name}\n</config>\n\n<review_comments>\n{json_array_of_actionable_comments}\n\nEach comment has:\n- id: comment ID (for replying)\n- user: who left it\n- body: the comment text\n- path: file path (for inline comments)\n- line: line number (for inline comments)\n- diff_hunk: surrounding diff context (for inline comments)\n- source: where the comment came from (review, inline, pr_body, greptile, etc.)\n</review_comments>\n\n<instructions>\nFollow these steps in order:\n\n0. SETUP — Ensure GH_TOKEN is available:\n```\n\nexport GH_TOKEN=$(node -e \"const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/openclaw.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')\")\n\n```\nVerify: echo \"Token: ${GH_TOKEN:0:10}...\"\n\n1. CHECKOUT — Switch to the PR branch:\ngit fetch {PUSH_REMOTE} {branch_name}\ngit checkout {branch_name}\ngit pull {PUSH_REMOTE} {branch_name}\n\n2. UNDERSTAND — Read ALL review comments carefully. Group them by file. Understand what each reviewer is asking for.\n\n3. IMPLEMENT — For each comment, make the requested change:\n- Read the file and locate the relevant code\n- Make the change the reviewer requested\n- If the comment is vague or you disagree, still attempt a reasonable fix but note your concern\n- If the comment asks for something impossible or contradictory, skip it and explain why in your reply\n\n4. TEST — Run existing tests to make sure your changes don't break anything:\n- If tests fail, fix the issue or revert the problematic change\n- Note any test failures in your replies\n\n5. COMMIT — Stage and commit all changes in a single commit:\ngit add {changed_files}\ngit commit -m \"fix: address review comments on PR #{pr_number}\n\nAddresses review feedback from {reviewer_names}\"\n\n6. PUSH — Push the updated branch:\ngit config --global credential.helper \"\"\ngit remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\nGIT_ASKPASS=true git push {PUSH_REMOTE} {branch_name}\n\n7. REPLY — For each addressed comment, post a reply:\n\nFor inline review comments (have a path/line), reply to the comment thread:\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments/{comment_id}/replies \\\n  -d '{\"body\": \"Addressed in commit {short_sha} — {brief_description_of_change}\"}'\n\nFor general PR comments (issue comments), reply on the PR:\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments \\\n  -d '{\"body\": \"Addressed feedback from @{reviewer}:\\n\\n{summary_of_changes_made}\\n\\nUpdated in commit {short_sha}\"}'\n\nFor comments you could NOT address, reply explaining why:\n\"Unable to address this comment: {reason}. This may need manual review.\"\n\n8. REPORT — Send back a summary:\n- PR URL\n- Number of comments addressed vs skipped\n- Commit SHA\n- Files changed\n- Any comments that need manual attention\n</instructions>\n\n<constraints>\n- Only modify files relevant to the review comments\n- Do not make unrelated changes\n- Do not force-push — always regular push\n- If a comment contradicts another comment, address the most recent one and flag the conflict\n- Do NOT use the gh CLI — use curl + GitHub REST API\n- GH_TOKEN is already in the environment — do not prompt for auth\n- Time limit: 60 minutes max\n</constraints>\n```\n\n**Spawn configuration per sub-agent:**\n\n- runTimeoutSeconds: 3600 (60 minutes)\n- cleanup: \"keep\" (preserve transcripts for review)\n- If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n\n### Step 6.6 — Review Results\n\nAfter all review sub-agents complete, present a summary:\n\n```\n| PR | Comments Addressed | Comments Skipped | Commit | Status |\n|----|-------------------|-----------------|--------|--------|\n| #99 fix/issue-42 | 3 | 0 | abc123f | All addressed |\n| #101 fix/issue-37 | 1 | 1 | def456a | 1 needs manual review |\n```\n\nAdd comment IDs from this batch to `ADDRESSED_COMMENTS` set to prevent re-processing.\n\n---\n\n## Watch Mode (if --watch is active)\n\nAfter presenting results from the current batch:\n\n1. Add all issue numbers from this batch to the running set PROCESSED_ISSUES.\n2. Add all addressed comment IDs to ADDRESSED_COMMENTS.\n3. Tell the user:\n   > \"Next poll in {interval} minutes... (say 'stop' to end watch mode)\"\n4. Sleep for {interval} minutes.\n5. Go back to **Phase 2 — Fetch Issues**. The fetch will automatically filter out:\n   - Issues already in PROCESSED_ISSUES\n   - Issues that have existing fix/issue-{N} PRs (caught in Phase 4 pre-flight)\n6. After Phases 2-5 (or if no new issues), run **Phase 6** to check for new review comments on ALL tracked PRs (both newly created and previously opened).\n7. If no new issues AND no new actionable review comments → report \"No new activity. Polling again in {interval} minutes...\" and loop back to step 4.\n8. The user can say \"stop\" at any time to exit watch mode. When stopping, present a final cumulative summary of ALL batches — issues processed AND review comments addressed.\n\n**Context hygiene between polls — IMPORTANT:**\nOnly retain between poll cycles:\n\n- PROCESSED_ISSUES (set of issue numbers)\n- ADDRESSED_COMMENTS (set of comment IDs)\n- OPEN_PRS (list of tracked PRs: number, branch, URL)\n- Cumulative results (one line per issue + one line per review batch)\n- Parsed arguments from Phase 1\n- BASE_BRANCH, SOURCE_REPO, PUSH_REPO, FORK_MODE, BOT_USERNAME\n  Do NOT retain issue bodies, comment bodies, sub-agent transcripts, or codebase analysis between polls.\n","frontmatter":{"name":"gh-issues","description":"Fetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]","user-invocable":true,"metadata":{"openclaw":{"requires":{"bins":["curl","git","gh"]},"primaryEnv":"GH_TOKEN","install":[{"id":"brew","kind":"brew","formula":"gh","bins":["gh"],"label":"Install GitHub CLI (brew)"}]}}},"isInternal":false,"tokens":8872,"sizeBytes":35037},{"name":"SKILL.md","path":"skills/gifgrep/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/gifgrep/SKILL.md","title":"gifgrep","category":"anthropic-skill","format":"markdown","content":"---\nname: gifgrep\ndescription: Search GIF providers with CLI/TUI, download results, and extract stills/sheets.\nhomepage: https://gifgrep.com\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🧲\",\n        \"requires\": { \"bins\": [\"gifgrep\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/gifgrep\",\n              \"bins\": [\"gifgrep\"],\n              \"label\": \"Install gifgrep (brew)\",\n            },\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/gifgrep/cmd/gifgrep@latest\",\n              \"bins\": [\"gifgrep\"],\n              \"label\": \"Install gifgrep (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# gifgrep\n\nUse `gifgrep` to search GIF providers (Tenor/Giphy), browse in a TUI, download results, and extract stills or sheets.\n\nGIF-Grab (gifgrep workflow)\n\n- Search → preview → download → extract (still/sheet) for fast review and sharing.\n\nQuick start\n\n- `gifgrep cats --max 5`\n- `gifgrep cats --format url | head -n 5`\n- `gifgrep search --json cats | jq '.[0].url'`\n- `gifgrep tui \"office handshake\"`\n- `gifgrep cats --download --max 1 --format url`\n\nTUI + previews\n\n- TUI: `gifgrep tui \"query\"`\n- CLI still previews: `--thumbs` (Kitty/Ghostty only; still frame)\n\nDownload + reveal\n\n- `--download` saves to `~/Downloads`\n- `--reveal` shows the last download in Finder\n\nStills + sheets\n\n- `gifgrep still ./clip.gif --at 1.5s -o still.png`\n- `gifgrep sheet ./clip.gif --frames 9 --cols 3 -o sheet.png`\n- Sheets = single PNG grid of sampled frames (great for quick review, docs, PRs, chat).\n- Tune: `--frames` (count), `--cols` (grid width), `--padding` (spacing).\n\nProviders\n\n- `--source auto|tenor|giphy`\n- `GIPHY_API_KEY` required for `--source giphy`\n- `TENOR_API_KEY` optional (Tenor demo key used if unset)\n\nOutput\n\n- `--json` prints an array of results (`id`, `title`, `url`, `preview_url`, `tags`, `width`, `height`)\n- `--format` for pipe-friendly fields (e.g., `url`)\n\nEnvironment tweaks\n\n- `GIFGREP_SOFTWARE_ANIM=1` to force software animation\n- `GIFGREP_CELL_ASPECT=0.5` to tweak preview geometry\n","frontmatter":{"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)"}]}}},"isInternal":false,"tokens":616,"sizeBytes":2185},{"name":"SKILL.md","path":"skills/github/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/github/SKILL.md","title":"github","category":"anthropic-skill","format":"markdown","content":"---\nname: github\ndescription: Use when interacting with GitHub repositories, authentication, remotes, pushes, or repository creation through the GitHub CLI and related git workflows.\nhomepage: https://cli.github.com/\nmetadata:\n  {\n    \"emoji\": \"🐙\"\n  }\n---\n\n# GitHub\n\n## Overview\n\nPrefer `gh` for all GitHub operations. The default goal is to complete the full GitHub flow with the least possible user effort: detect existing authentication first, reuse it if available, and only ask the user for the smallest missing step.\n\nNever ask the user to manually perform a long GitHub setup if the agent can check, reuse, or finish it directly.\n\n## Core Rules\n\n- Prefer `gh` over raw GitHub API calls.\n- Prefer existing authenticated state over asking for new credentials.\n- Prefer non-interactive checks before prompting the user.\n- Prefer the smallest unblock request:\n  - paste a one-time code\n  - confirm browser login\n  - provide a PAT\n  - confirm repository visibility\n- Do not ask the user to manually create the repository if `gh` can do it.\n- Do not ask the user to manually add remotes, initialize Git, or push if the agent can do it.\n- Never print, echo, log, summarize, or restate secrets.\n- Never store secrets in files, commits, repo config, docs, examples, or chat summaries.\n- Never switch credential strategy unless the current one actually fails.\n\n### Interactive login driving rules\n\nWhen using `gh auth login` through a shell session, do not assume the browser login step has started after the first prompt or after sending one key.\n\nYou must continue driving the interactive flow until there is explicit evidence of one of these states:\n\n1. GitHub CLI has clearly started browser/device authentication\n2. GitHub CLI is explicitly waiting for the user to complete external authentication\n3. GitHub CLI reports that authentication succeeded\n4. GitHub CLI reports a real error and cannot continue\n\nWhile driving the login flow:\n\n- keep polling the session after each input\n- handle intermediate prompts such as protocol selection, SSH/HTTPS choice, credential helper confirmation, browser/device flow confirmation, or similar setup questions\n- use minimal inputs such as `enter`, arrow keys, `y`, `n`, or short submitted text as needed\n- do not stop after only one successful prompt response\n- do not tell the user that the browser has opened unless the command output explicitly indicates that a browser login or web flow has started\n- do not tell the user that login is complete unless the command output explicitly indicates success\n\nAcceptable evidence that browser or external auth has actually started includes output such as:\n\n- a message that a browser is being opened\n- a URL to visit\n- a device code to enter\n- explicit wording that GitHub authentication is waiting for completion\n\nIf the shell session remains running but only shows that an earlier prompt was answered, continue polling instead of summarizing the state prematurely.\n\nOnly ask the user to act when the output clearly shows an external action is now required.\n\nWhen the user says they completed browser or device authorization, do not start a new `gh auth login` flow immediately.\n\nInstead:\n\n1. continue polling the existing shell session if it is still running\n2. wait for explicit success or failure from that same session\n3. only if that session has exited or is unrecoverable, then check `gh auth status`\n4. only start a brand-new login flow if the previous login session has clearly failed or been closed\n\nIf the shell output contains prompts like:\n\n- \"Press Enter to open ...\"\n- \"Press Enter to continue\"\n- similar one-step local confirmation prompts\n\nthen send `enter` automatically and continue polling the same session.\n\nDo not stop at that prompt.\nDo not ask the user to confirm completion yet.\nDo not start a new login flow while this session is still running.\n\n## Credential Handling Priority\n\n### 1. Reuse `gh` authentication first\n\nCheck whether `gh` exists and is already authenticated.\n\nUse this path when possible:\n\n- `gh auth status`\n- if authenticated, continue directly with:\n  - repo detection\n  - `gh repo create`\n  - remote setup\n  - push\n\nIf `gh` is authenticated, do not ask the user for tokens, passwords, SSH keys, or browser login.\n\n### 2. If `gh` exists but is not authenticated, log in with `gh` directly\n\nIf `gh` is installed but not authenticated, use `gh auth login` directly and continue the GitHub flow after login succeeds.\n\nPreferred order:\n\n1. browser/web login via `gh auth login -h github.com -w`\n2. standard interactive `gh auth login -h github.com`\n3. PAT-based login only if the normal `gh` login path is unavailable or fails\n\nRules:\n\n- do not switch away from `gh` login unless it actually fails\n- do not ask the user to manually create repositories or configure git\n- do not ask the user to perform a long GitHub setup outside the login itself\n- continue automatically as soon as login succeeds\n\nWhen login requires user interaction:\n\n- ask for only the smallest possible action\n- examples:\n  - “Please complete the GitHub browser login that just opened.”\n  - “Please finish the GitHub CLI login prompts.”\n  - “Please paste a GitHub PAT with repo access.”\n\nAfter login succeeds, continue directly with:\n\n- repo detection\n- `gh repo create`\n- remote setup\n- push\n\n### 3. If `gh` is unavailable, try to install it first\n\nIf `gh` is missing:\n\n- first try to install `gh` automatically using the platform's normal package manager or a reasonable official installation path\n- prefer non-interactive installation methods when available\n- after installation, re-check whether `gh` exists and continue with the normal `gh`-first flow\n- only fall back away from `gh` if installation actually fails, is blocked by the environment, or would require disproportionate user effort\n\nAcceptable install attempts include examples like:\n\n- Homebrew on macOS\n- `apt`, `dnf`, or similar package managers on Linux\n- `winget` or other normal package-manager paths on Windows\n\nRules:\n\n- do not ask the user to install `gh` manually before you have tried a reasonable automatic install path\n- do not switch to a different credential strategy just because `gh` is missing initially\n- if installation fails, summarize the blocker briefly and then continue to the fallback path below\n\n### 4. If `gh` is still unavailable, fall back to Git\n\nIf `gh` is missing:\n\n- continue with plain `git`\n- prefer existing Git credential helpers or existing SSH authentication\n- inspect the environment before asking for credentials\n\nCheck for likely reusable auth:\n\n- existing `origin` remote\n- existing Git credential helper\n- working SSH auth to GitHub\n- existing authenticated HTTPS remote behavior\n\nOnly if those paths fail should the user be asked for one minimal credential input.\n\n## Minimal-Interruption Prompting\n\nWhen user input is required, ask for only one concrete item at a time.\n\nGood prompts:\n\n- “Please confirm whether the repo should be public or private.”\n- “Please complete the GitHub login in your browser.”\n- “Please paste a GitHub PAT with repo access.”\n- “Please confirm the GitHub owner/org name.”\n\nBad prompts:\n\n- asking the user to manually create a repo\n- asking the user to configure Git from scratch without first checking\n- asking for multiple credentials at once\n- asking the user to inspect local config manually if the agent can inspect it\n\n## Safe Credential Rules\n\n- Treat PATs, device codes, auth tokens, cookies, and SSH private keys as secrets.\n- Never echo secrets back to the user.\n- Never include secrets in command output summaries.\n- Never write secrets into repo files or examples.\n- Never commit `.env`, token files, or copied credentials.\n- If a command might expose a secret in stdout/stderr, avoid reproducing that output verbatim.\n- If the user gives a token, use it only for the current task unless explicitly told otherwise.\n\n## Standard Execution Flow\n\n### A. Detect environment\n\nCheck in this order:\n\n1. Is this already a Git repo?\n2. Is there already a remote?\n3. Is `gh` installed?\n4. Is `gh` authenticated?\n5. If not using `gh`, does Git already have usable auth?\n\n### B. If already connected to GitHub\n\nIf the repo already has a valid GitHub remote and push access works:\n\n- do not recreate the repo\n- do not replace auth\n- just push\n\n### C. If Git repo exists but no GitHub repo exists yet\n\nPreferred:\n\n- create the GitHub repo with `gh repo create`\n- set or update `origin`\n- push the current branch\n\nOnly ask the user for:\n\n- owner/org if ambiguous\n- public vs private if not specified\n- authentication only if required\n\n### D. If not a Git repo yet\n\nDo the setup directly:\n\n- initialize Git\n- add files\n- create initial commit if needed\n- create GitHub repo\n- add remote\n- push\n\nDo not ask the user to perform these steps manually.\n\n## `gh`-First Behavior\n\nWhen `gh` is available and authenticated, prefer commands in this shape:\n\n- inspect auth\n- create repo\n- set remote if needed\n- push branch\n- optionally enable features like description/homepage/topics later\n\nUse plain Git only for local repository operations or when `gh` cannot complete the GitHub-side action.\n\n## Git / API Fallback Behavior\n\nOnly use this fallback when:\n\n- `gh` is not installed and cannot be installed reasonably\n- or `gh` is installed but unusable for the required operation\n- or the environment blocks the normal `gh` flow in a way that cannot be resolved with minimal user input\n\nFallback order:\n\n1. direct GitHub API + local `git`\n2. plain `git` with existing SSH or credential-helper auth\n3. HTTPS + PAT only if no better reusable auth exists\n\nFallback rules:\n\n- still minimize user effort\n- still prefer reusing existing credentials\n- still never ask the user to create the repository manually if the agent can do it through the API\n- still avoid exposing or persisting secrets\n- still continue automatically once the minimal missing credential or confirmation is provided\n\nIf PAT is required:\n\n- ask only for a token with the minimum needed scope, usually repo write access\n- use it only to complete the push flow\n- avoid leaving it in shell history, files, process args, or remote URLs when possible\n\n## Success Criteria\n\nA successful run means:\n\n- the local project is a valid Git repo if needed\n- the GitHub repo exists\n- the correct remote is configured\n- the current branch is pushed\n- credentials were reused when possible\n- user involvement was limited to the smallest truly necessary action\n- no secret was exposed or persisted unsafely\n","frontmatter":{"name":"github","description":"Use when interacting with GitHub repositories, authentication, remotes, pushes, or repository creation through the GitHub CLI and related git workflows.","homepage":"https://cli.github.com/","metadata":{"emoji":"🐙"}},"isInternal":false,"tokens":2238,"sizeBytes":10513},{"name":"SKILL.md","path":"skills/gog/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/gog/SKILL.md","title":"gog","category":"anthropic-skill","format":"markdown","content":"---\nname: gog\ndescription: Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.\nhomepage: https://gogcli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🎮\",\n        \"requires\": { \"bins\": [\"gog\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/gogcli\",\n              \"bins\": [\"gog\"],\n              \"label\": \"Install gog (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# gog\n\nUse `gog` for Gmail/Calendar/Drive/Contacts/Sheets/Docs. Requires OAuth setup.\n\nSetup (once)\n\n- `gog auth credentials /path/to/client_secret.json`\n- `gog auth add you@gmail.com --services gmail,calendar,drive,contacts,docs,sheets`\n- `gog auth list`\n\nCommon commands\n\n- Gmail search: `gog gmail search 'newer_than:7d' --max 10`\n- Gmail messages search (per email, ignores threading): `gog gmail messages search \"in:inbox from:ryanair.com\" --max 20 --account you@example.com`\n- Gmail send (plain): `gog gmail send --to a@b.com --subject \"Hi\" --body \"Hello\"`\n- Gmail send (multi-line): `gog gmail send --to a@b.com --subject \"Hi\" --body-file ./message.txt`\n- Gmail send (stdin): `gog gmail send --to a@b.com --subject \"Hi\" --body-file -`\n- Gmail send (HTML): `gog gmail send --to a@b.com --subject \"Hi\" --body-html \"<p>Hello</p>\"`\n- Gmail draft: `gog gmail drafts create --to a@b.com --subject \"Hi\" --body-file ./message.txt`\n- Gmail send draft: `gog gmail drafts send <draftId>`\n- Gmail reply: `gog gmail send --to a@b.com --subject \"Re: Hi\" --body \"Reply\" --reply-to-message-id <msgId>`\n- Calendar list events: `gog calendar events <calendarId> --from <iso> --to <iso>`\n- Calendar create event: `gog calendar create <calendarId> --summary \"Title\" --from <iso> --to <iso>`\n- Calendar create with color: `gog calendar create <calendarId> --summary \"Title\" --from <iso> --to <iso> --event-color 7`\n- Calendar update event: `gog calendar update <calendarId> <eventId> --summary \"New Title\" --event-color 4`\n- Calendar show colors: `gog calendar colors`\n- Drive search: `gog drive search \"query\" --max 10`\n- Contacts: `gog contacts list --max 20`\n- Sheets get: `gog sheets get <sheetId> \"Tab!A1:D10\" --json`\n- Sheets update: `gog sheets update <sheetId> \"Tab!A1:B2\" --values-json '[[\"A\",\"B\"],[\"1\",\"2\"]]' --input USER_ENTERED`\n- Sheets append: `gog sheets append <sheetId> \"Tab!A:C\" --values-json '[[\"x\",\"y\",\"z\"]]' --insert INSERT_ROWS`\n- Sheets clear: `gog sheets clear <sheetId> \"Tab!A2:Z\"`\n- Sheets metadata: `gog sheets metadata <sheetId> --json`\n- Docs export: `gog docs export <docId> --format txt --out /tmp/doc.txt`\n- Docs cat: `gog docs cat <docId>`\n\nCalendar Colors\n\n- Use `gog calendar colors` to see all available event colors (IDs 1-11)\n- Add colors to events with `--event-color <id>` flag\n- Event color IDs (from `gog calendar colors` output):\n  - 1: #a4bdfc\n  - 2: #7ae7bf\n  - 3: #dbadff\n  - 4: #ff887c\n  - 5: #fbd75b\n  - 6: #ffb878\n  - 7: #46d6db\n  - 8: #e1e1e1\n  - 9: #5484ed\n  - 10: #51b749\n  - 11: #dc2127\n\nEmail Formatting\n\n- Prefer plain text. Use `--body-file` for multi-paragraph messages (or `--body-file -` for stdin).\n- Same `--body-file` pattern works for drafts and replies.\n- `--body` does not unescape `\\n`. If you need inline newlines, use a heredoc or `$'Line 1\\n\\nLine 2'`.\n- Use `--body-html` only when you need rich formatting.\n- HTML tags: `<p>` for paragraphs, `<br>` for line breaks, `<strong>` for bold, `<em>` for italic, `<a href=\"url\">` for links, `<ul>`/`<li>` for lists.\n- Example (plain text via stdin):\n\n  ```bash\n  gog gmail send --to recipient@example.com \\\n    --subject \"Meeting Follow-up\" \\\n    --body-file - <<'EOF'\n  Hi Name,\n\n  Thanks for meeting today. Next steps:\n  - Item one\n  - Item two\n\n  Best regards,\n  Your Name\n  EOF\n  ```\n\n- Example (HTML list):\n  ```bash\n  gog gmail send --to recipient@example.com \\\n    --subject \"Meeting Follow-up\" \\\n    --body-html \"<p>Hi Name,</p><p>Thanks for meeting today. Here are the next steps:</p><ul><li>Item one</li><li>Item two</li></ul><p>Best regards,<br>Your Name</p>\"\n  ```\n\nNotes\n\n- Set `GOG_ACCOUNT=you@gmail.com` to avoid repeating `--account`.\n- For scripting, prefer `--json` plus `--no-input`.\n- Sheets values can be passed via `--values-json` (recommended) or as inline rows.\n- Docs supports export/cat/copy. In-place edits require a Docs API client (not in gog).\n- Confirm before sending mail or creating events.\n- `gog gmail search` returns one row per thread; use `gog gmail messages search` when you need every individual email returned separately.\n","frontmatter":{"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)"}]}}},"isInternal":false,"tokens":1395,"sizeBytes":4572},{"name":"SKILL.md","path":"skills/goplaces/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/goplaces/SKILL.md","title":"goplaces","category":"anthropic-skill","format":"markdown","content":"---\nname: goplaces\ndescription: Query Google Places API (New) via the goplaces CLI for text search, place details, resolve, and reviews. Use for human-friendly place lookup or JSON output for scripts.\nhomepage: https://github.com/steipete/goplaces\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📍\",\n        \"requires\": { \"bins\": [\"goplaces\"], \"env\": [\"GOOGLE_PLACES_API_KEY\"] },\n        \"primaryEnv\": \"GOOGLE_PLACES_API_KEY\",\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/goplaces\",\n              \"bins\": [\"goplaces\"],\n              \"label\": \"Install goplaces (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# goplaces\n\nModern Google Places API (New) CLI. Human output by default, `--json` for scripts.\n\nInstall\n\n- Homebrew: `brew install steipete/tap/goplaces`\n\nConfig\n\n- `GOOGLE_PLACES_API_KEY` required.\n- Optional: `GOOGLE_PLACES_BASE_URL` for testing/proxying.\n\nCommon commands\n\n- Search: `goplaces search \"coffee\" --open-now --min-rating 4 --limit 5`\n- Bias: `goplaces search \"pizza\" --lat 40.8 --lng -73.9 --radius-m 3000`\n- Pagination: `goplaces search \"pizza\" --page-token \"NEXT_PAGE_TOKEN\"`\n- Resolve: `goplaces resolve \"Soho, London\" --limit 5`\n- Details: `goplaces details <place_id> --reviews`\n- JSON: `goplaces search \"sushi\" --json`\n\nNotes\n\n- `--no-color` or `NO_COLOR` disables ANSI color.\n- Price levels: 0..4 (free → very expensive).\n- Type filter sends only the first `--type` value (API accepts one).\n","frontmatter":{"name":"goplaces","description":"Query Google Places API (New) via the goplaces CLI for text search, place details, resolve, and reviews. Use for human-friendly place lookup or JSON output for scripts.","homepage":"https://github.com/steipete/goplaces","metadata":{"openclaw":{"emoji":"📍","requires":{"bins":["goplaces"],"env":["GOOGLE_PLACES_API_KEY"]},"primaryEnv":"GOOGLE_PLACES_API_KEY","install":[{"id":"brew","kind":"brew","formula":"steipete/tap/goplaces","bins":["goplaces"],"label":"Install goplaces (brew)"}]}}},"isInternal":false,"tokens":431,"sizeBytes":1538},{"name":"SKILL.md","path":"skills/healthcheck/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/healthcheck/SKILL.md","title":"healthcheck","category":"anthropic-skill","format":"markdown","content":"---\nname: healthcheck\ndescription: Host security hardening and risk-tolerance configuration for OpenClaw deployments. Use when a user asks for security audits, firewall/SSH/update hardening, risk posture, exposure review, OpenClaw cron scheduling for periodic checks, or version status checks on a machine running OpenClaw (laptop, workstation, Pi, VPS).\n---\n\n# OpenClaw Host Hardening\n\n## Overview\n\nAssess and harden the host running OpenClaw, then align it to a user-defined risk tolerance without breaking access. Use OpenClaw security tooling as a first-class signal, but treat OS hardening as a separate, explicit set of steps.\n\n## Core rules\n\n- Recommend running this skill with a state-of-the-art model (e.g., Opus 4.5, GPT 5.2+). The agent should self-check the current model and suggest switching if below that level; do not block execution.\n- Require explicit approval before any state-changing action.\n- Do not modify remote access settings without confirming how the user connects.\n- Prefer reversible, staged changes with a rollback plan.\n- Never claim OpenClaw changes the host firewall, SSH, or OS updates; it does not.\n- If role/identity is unknown, provide recommendations only.\n- Formatting: every set of user choices must be numbered so the user can reply with a single digit.\n- System-level backups are recommended; try to verify status.\n\n## Workflow (follow in order)\n\n### 0) Model self-check (non-blocking)\n\nBefore starting, check the current model. If it is below state-of-the-art (e.g., Opus 4.5, GPT 5.2+), recommend switching. Do not block execution.\n\n### 1) Establish context (read-only)\n\nTry to infer 1–5 from the environment before asking. Prefer simple, non-technical questions if you need confirmation.\n\nDetermine (in order):\n\n1. OS and version (Linux/macOS/Windows), container vs host.\n2. Privilege level (root/admin vs user).\n3. Access path (local console, SSH, RDP, tailnet).\n4. Network exposure (public IP, reverse proxy, tunnel).\n5. OpenClaw gateway status and bind address.\n6. Backup system and status (e.g., Time Machine, system images, snapshots).\n7. Deployment context (local mac app, headless gateway host, remote gateway, container/CI).\n8. Disk encryption status (FileVault/LUKS/BitLocker).\n9. OS automatic security updates status.\n   Note: these are not blocking items, but are highly recommended, especially if OpenClaw can access sensitive data.\n10. Usage mode for a personal assistant with full access (local workstation vs headless/remote vs other).\n\nFirst ask once for permission to run read-only checks. If granted, run them by default and only ask questions for items you cannot infer or verify. Do not ask for information already visible in runtime or command output. Keep the permission ask as a single sentence, and list follow-up info needed as an unordered list (not numbered) unless you are presenting selectable choices.\n\nIf you must ask, use non-technical prompts:\n\n- “Are you using a Mac, Windows PC, or Linux?”\n- “Are you logged in directly on the machine, or connecting from another computer?”\n- “Is this machine reachable from the public internet, or only on your home/network?”\n- “Do you have backups enabled (e.g., Time Machine), and are they current?”\n- “Is disk encryption turned on (FileVault/BitLocker/LUKS)?”\n- “Are automatic security updates enabled?”\n- “How do you use this machine?”\n  Examples:\n  - Personal machine shared with the assistant\n  - Dedicated local machine for the assistant\n  - Dedicated remote machine/server accessed remotely (always on)\n  - Something else?\n\nOnly ask for the risk profile after system context is known.\n\nIf the user grants read-only permission, run the OS-appropriate checks by default. If not, offer them (numbered). Examples:\n\n1. OS: `uname -a`, `sw_vers`, `cat /etc/os-release`.\n2. Listening ports:\n   - Linux: `ss -ltnup` (or `ss -ltnp` if `-u` unsupported).\n   - macOS: `lsof -nP -iTCP -sTCP:LISTEN`.\n3. Firewall status:\n   - Linux: `ufw status`, `firewall-cmd --state`, `nft list ruleset` (pick what is installed).\n   - macOS: `/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate` and `pfctl -s info`.\n4. Backups (macOS): `tmutil status` (if Time Machine is used).\n\n### 2) Run OpenClaw security audits (read-only)\n\nAs part of the default read-only checks, run `openclaw security audit --deep`. Only offer alternatives if the user requests them:\n\n1. `openclaw security audit` (faster, non-probing)\n2. `openclaw security audit --json` (structured output)\n\nOffer to apply OpenClaw safe defaults (numbered):\n\n1. `openclaw security audit --fix`\n\nBe explicit that `--fix` only tightens OpenClaw defaults and file permissions. It does not change host firewall, SSH, or OS update policies.\n\nIf browser control is enabled, recommend that 2FA be enabled on all important accounts, with hardware keys preferred and SMS not sufficient.\n\n### 3) Check OpenClaw version/update status (read-only)\n\nAs part of the default read-only checks, run `openclaw update status`.\n\nReport the current channel and whether an update is available.\n\n### 4) Determine risk tolerance (after system context)\n\nAsk the user to pick or confirm a risk posture and any required open services/ports (numbered choices below).\nDo not pigeonhole into fixed profiles; if the user prefers, capture requirements instead of choosing a profile.\nOffer suggested profiles as optional defaults (numbered). Note that most users pick Home/Workstation Balanced:\n\n1. Home/Workstation Balanced (most common): firewall on with reasonable defaults, remote access restricted to LAN or tailnet.\n2. VPS Hardened: deny-by-default inbound firewall, minimal open ports, key-only SSH, no root login, automatic security updates.\n3. Developer Convenience: more local services allowed, explicit exposure warnings, still audited.\n4. Custom: user-defined constraints (services, exposure, update cadence, access methods).\n\n### 5) Produce a remediation plan\n\nProvide a plan that includes:\n\n- Target profile\n- Current posture summary\n- Gaps vs target\n- Step-by-step remediation with exact commands\n- Access-preservation strategy and rollback\n- Risks and potential lockout scenarios\n- Least-privilege notes (e.g., avoid admin usage, tighten ownership/permissions where safe)\n- Credential hygiene notes (location of OpenClaw creds, prefer disk encryption)\n\nAlways show the plan before any changes.\n\n### 6) Offer execution options\n\nOffer one of these choices (numbered so users can reply with a single digit):\n\n1. Do it for me (guided, step-by-step approvals)\n2. Show plan only\n3. Fix only critical issues\n4. Export commands for later\n\n### 7) Execute with confirmations\n\nFor each step:\n\n- Show the exact command\n- Explain impact and rollback\n- Confirm access will remain available\n- Stop on unexpected output and ask for guidance\n\n### 8) Verify and report\n\nRe-check:\n\n- Firewall status\n- Listening ports\n- Remote access still works\n- OpenClaw security audit (re-run)\n\nDeliver a final posture report and note any deferred items.\n\n## Required confirmations (always)\n\nRequire explicit approval for:\n\n- Firewall rule changes\n- Opening/closing ports\n- SSH/RDP configuration changes\n- Installing/removing packages\n- Enabling/disabling services\n- User/group modifications\n- Scheduling tasks or startup persistence\n- Update policy changes\n- Access to sensitive files or credentials\n\nIf unsure, ask.\n\n## Periodic checks\n\nAfter OpenClaw install or first hardening pass, run at least one baseline audit and version check:\n\n- `openclaw security audit`\n- `openclaw security audit --deep`\n- `openclaw update status`\n\nOngoing monitoring is recommended. Use the OpenClaw cron tool/CLI to schedule periodic audits (Gateway scheduler). Do not create scheduled tasks without explicit approval. Store outputs in a user-approved location and avoid secrets in logs.\nWhen scheduling headless cron runs, include a note in the output that instructs the user to call `healthcheck` so issues can be fixed.\n\n### Required prompt to schedule (always)\n\nAfter any audit or hardening pass, explicitly offer scheduling and require a direct response. Use a short prompt like (numbered):\n\n1. “Do you want me to schedule periodic audits (e.g., daily/weekly) via `openclaw cron add`?”\n\nIf the user says yes, ask for:\n\n- cadence (daily/weekly), preferred time window, and output location\n- whether to also schedule `openclaw update status`\n\nUse a stable cron job name so updates are deterministic. Prefer exact names:\n\n- `healthcheck:security-audit`\n- `healthcheck:update-status`\n\nBefore creating, `openclaw cron list` and match on exact `name`. If found, `openclaw cron edit <id> ...`.\nIf not found, `openclaw cron add --name <name> ...`.\n\nAlso offer a periodic version check so the user can decide when to update (numbered):\n\n1. `openclaw update status` (preferred for source checkouts and channels)\n2. `npm view openclaw version` (published npm version)\n\n## OpenClaw command accuracy\n\nUse only supported commands and flags:\n\n- `openclaw security audit [--deep] [--fix] [--json]`\n- `openclaw status` / `openclaw status --deep`\n- `openclaw health --json`\n- `openclaw update status`\n- `openclaw cron add|list|runs|run`\n\nDo not invent CLI flags or imply OpenClaw enforces host firewall/SSH policies.\n\n## Logging and audit trail\n\nRecord:\n\n- Gateway identity and role\n- Plan ID and timestamp\n- Approved steps and exact commands\n- Exit codes and files modified (best effort)\n\nRedact secrets. Never log tokens or full credential contents.\n\n## Memory writes (conditional)\n\nOnly write to memory files when the user explicitly opts in and the session is a private/local workspace\n(per `docs/reference/templates/AGENTS.md`). Otherwise provide a redacted, paste-ready summary the user can\ndecide to save elsewhere.\n\nFollow the durable-memory prompt format used by OpenClaw compaction:\n\n- Write lasting notes to `memory/YYYY-MM-DD.md`.\n\nAfter each audit/hardening run, if opted-in, append a short, dated summary to `memory/YYYY-MM-DD.md`\n(what was checked, key findings, actions taken, any scheduled cron jobs, key decisions,\nand all commands executed). Append-only: never overwrite existing entries.\nRedact sensitive host details (usernames, hostnames, IPs, serials, service names, tokens).\nIf there are durable preferences or decisions (risk posture, allowed ports, update policy),\nalso update `MEMORY.md` (long-term memory is optional and only used in private sessions).\n\nIf the session cannot write to the workspace, ask for permission or provide exact entries\nthe user can paste into the memory files.\n","frontmatter":{"name":"healthcheck","description":"Host security hardening and risk-tolerance configuration for OpenClaw deployments. Use when a user asks for security audits, firewall/SSH/update hardening, risk posture, exposure review, OpenClaw cron scheduling for periodic checks, or version status checks on a machine running OpenClaw (laptop, workstation, Pi, VPS)."},"isInternal":false,"tokens":2416,"sizeBytes":10538},{"name":"configuration.md","path":"skills/himalaya/references/configuration.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/himalaya/references/configuration.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Himalaya Configuration Reference\n\nConfiguration file location: `~/.config/himalaya/config.toml`\n\n## Minimal IMAP + SMTP Setup\n\n```toml\n[accounts.default]\nemail = \"user@example.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\n# IMAP backend for reading emails\nbackend.type = \"imap\"\nbackend.host = \"imap.example.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"user@example.com\"\nbackend.auth.type = \"password\"\nbackend.auth.raw = \"your-password\"\n\n# SMTP backend for sending emails\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.example.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"user@example.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.raw = \"your-password\"\n```\n\n## Password Options\n\n### Raw password (testing only, not recommended)\n\n```toml\nbackend.auth.raw = \"your-password\"\n```\n\n### Password from command (recommended)\n\n```toml\nbackend.auth.cmd = \"pass show email/imap\"\n# backend.auth.cmd = \"security find-generic-password -a user@example.com -s imap -w\"\n```\n\n### System keyring (requires keyring feature)\n\n```toml\nbackend.auth.keyring = \"imap-example\"\n```\n\nThen run `himalaya account configure <account>` to store the password.\n\n## Gmail Configuration\n\n```toml\n[accounts.gmail]\nemail = \"you@gmail.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\nbackend.type = \"imap\"\nbackend.host = \"imap.gmail.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@gmail.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show google/app-password\"\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.gmail.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@gmail.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show google/app-password\"\n```\n\n**Note:** Gmail requires an App Password if 2FA is enabled.\n\n## iCloud Configuration\n\n```toml\n[accounts.icloud]\nemail = \"you@icloud.com\"\ndisplay-name = \"Your Name\"\n\nbackend.type = \"imap\"\nbackend.host = \"imap.mail.me.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@icloud.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show icloud/app-password\"\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.mail.me.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@icloud.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show icloud/app-password\"\n```\n\n**Note:** Generate an app-specific password at appleid.apple.com\n\n## Folder Aliases\n\nMap custom folder names:\n\n```toml\n[accounts.default.folder.alias]\ninbox = \"INBOX\"\nsent = \"Sent\"\ndrafts = \"Drafts\"\ntrash = \"Trash\"\n```\n\n## Multiple Accounts\n\n```toml\n[accounts.personal]\nemail = \"personal@example.com\"\ndefault = true\n# ... backend config ...\n\n[accounts.work]\nemail = \"work@company.com\"\n# ... backend config ...\n```\n\nSwitch accounts with `--account`:\n\n```bash\nhimalaya --account work envelope list\n```\n\n## Notmuch Backend (local mail)\n\n```toml\n[accounts.local]\nemail = \"user@example.com\"\n\nbackend.type = \"notmuch\"\nbackend.db-path = \"~/.mail/.notmuch\"\n```\n\n## OAuth2 Authentication (for providers that support it)\n\n```toml\nbackend.auth.type = \"oauth2\"\nbackend.auth.client-id = \"your-client-id\"\nbackend.auth.client-secret.cmd = \"pass show oauth/client-secret\"\nbackend.auth.access-token.cmd = \"pass show oauth/access-token\"\nbackend.auth.refresh-token.cmd = \"pass show oauth/refresh-token\"\nbackend.auth.auth-url = \"https://provider.com/oauth/authorize\"\nbackend.auth.token-url = \"https://provider.com/oauth/token\"\n```\n\n## Additional Options\n\n### Signature\n\n```toml\n[accounts.default]\nsignature = \"Best regards,\\nYour Name\"\nsignature-delim = \"-- \\n\"\n```\n\n### Downloads directory\n\n```toml\n[accounts.default]\ndownloads-dir = \"~/Downloads/himalaya\"\n```\n\n### Editor for composing\n\nSet via environment variable:\n\n```bash\nexport EDITOR=\"vim\"\n```\n","isInternal":false,"tokens":991,"sizeBytes":4084},{"name":"message-composition.md","path":"skills/himalaya/references/message-composition.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/himalaya/references/message-composition.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Message Composition with MML (MIME Meta Language)\n\nHimalaya uses MML for composing emails. MML is a simple XML-based syntax that compiles to MIME messages.\n\n## Basic Message Structure\n\nAn email message is a list of **headers** followed by a **body**, separated by a blank line:\n\n```\nFrom: sender@example.com\nTo: recipient@example.com\nSubject: Hello World\n\nThis is the message body.\n```\n\n## Headers\n\nCommon headers:\n\n- `From`: Sender address\n- `To`: Primary recipient(s)\n- `Cc`: Carbon copy recipients\n- `Bcc`: Blind carbon copy recipients\n- `Subject`: Message subject\n- `Reply-To`: Address for replies (if different from From)\n- `In-Reply-To`: Message ID being replied to\n\n### Address Formats\n\n```\nTo: user@example.com\nTo: John Doe <john@example.com>\nTo: \"John Doe\" <john@example.com>\nTo: user1@example.com, user2@example.com, \"Jane\" <jane@example.com>\n```\n\n## Plain Text Body\n\nSimple plain text email:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Plain Text Example\n\nHello, this is a plain text email.\nNo special formatting needed.\n\nBest,\nAlice\n```\n\n## MML for Rich Emails\n\n### Multipart Messages\n\nAlternative text/html parts:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Multipart Example\n\n<#multipart type=alternative>\nThis is the plain text version.\n<#part type=text/html>\n<html><body><h1>This is the HTML version</h1></body></html>\n<#/multipart>\n```\n\n### Attachments\n\nAttach a file:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: With Attachment\n\nHere is the document you requested.\n\n<#part filename=/path/to/document.pdf><#/part>\n```\n\nAttachment with custom name:\n\n```\n<#part filename=/path/to/file.pdf name=report.pdf><#/part>\n```\n\nMultiple attachments:\n\n```\n<#part filename=/path/to/doc1.pdf><#/part>\n<#part filename=/path/to/doc2.pdf><#/part>\n```\n\n### Inline Images\n\nEmbed an image inline:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Inline Image\n\n<#multipart type=related>\n<#part type=text/html>\n<html><body>\n<p>Check out this image:</p>\n<img src=\"cid:image1\">\n</body></html>\n<#part disposition=inline id=image1 filename=/path/to/image.png><#/part>\n<#/multipart>\n```\n\n### Mixed Content (Text + Attachments)\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Mixed Content\n\n<#multipart type=mixed>\n<#part type=text/plain>\nPlease find the attached files.\n\nBest,\nAlice\n<#part filename=/path/to/file1.pdf><#/part>\n<#part filename=/path/to/file2.zip><#/part>\n<#/multipart>\n```\n\n## MML Tag Reference\n\n### `<#multipart>`\n\nGroups multiple parts together.\n\n- `type=alternative`: Different representations of same content\n- `type=mixed`: Independent parts (text + attachments)\n- `type=related`: Parts that reference each other (HTML + images)\n\n### `<#part>`\n\nDefines a message part.\n\n- `type=<mime-type>`: Content type (e.g., `text/html`, `application/pdf`)\n- `filename=<path>`: File to attach\n- `name=<name>`: Display name for attachment\n- `disposition=inline`: Display inline instead of as attachment\n- `id=<cid>`: Content ID for referencing in HTML\n\n## Composing from CLI\n\n### Interactive compose\n\nOpens your `$EDITOR`:\n\n```bash\nhimalaya message write\n```\n\n### Reply (opens editor with quoted message)\n\n```bash\nhimalaya message reply 42\nhimalaya message reply 42 --all  # reply-all\n```\n\n### Forward\n\n```bash\nhimalaya message forward 42\n```\n\n### Send from stdin\n\n```bash\ncat message.txt | himalaya template send\n```\n\n### Prefill headers from CLI\n\n```bash\nhimalaya message write \\\n  -H \"To:recipient@example.com\" \\\n  -H \"Subject:Quick Message\" \\\n  \"Message body here\"\n```\n\n## Tips\n\n- The editor opens with a template; fill in headers and body.\n- Save and exit the editor to send; exit without saving to cancel.\n- MML parts are compiled to proper MIME when sending.\n- Use `himalaya message export --full` to inspect the raw MIME structure of received emails.\n","isInternal":false,"tokens":991,"sizeBytes":3799},{"name":"SKILL.md","path":"skills/himalaya/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/himalaya/SKILL.md","title":"himalaya","category":"anthropic-skill","format":"markdown","content":"---\nname: himalaya\ndescription: \"CLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).\"\nhomepage: https://github.com/pimalaya/himalaya\nmetadata:\n  {\n    \"emoji\": \"📧\",\n    \"requires\": { \"bins\": [\"himalaya\"] }\n  }\n---\n\n# Himalaya Email CLI\n\nHimalaya is a CLI email client that lets you manage emails from the terminal using IMAP, SMTP, Notmuch, or Sendmail backends.\n\n## References\n\n- `references/configuration.md` (config file setup + IMAP/SMTP authentication)\n- `references/message-composition.md` (MML syntax for composing emails)\n\n## Prerequisites\n\n1. Himalaya CLI installed (`himalaya --version` to verify)\n2. A configuration file at `~/.config/himalaya/config.toml`\n3. IMAP/SMTP credentials configured (password stored securely)\n\n## Configuration Setup\n\nRun the interactive wizard to set up an account:\n\n```bash\nhimalaya account configure\n```\n\nOr create `~/.config/himalaya/config.toml` manually:\n\n```toml\n[accounts.personal]\nemail = \"you@example.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\nbackend.type = \"imap\"\nbackend.host = \"imap.example.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@example.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show email/imap\"  # or use keyring\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.example.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@example.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show email/smtp\"\n```\n\n## Common Operations\n\n### List Folders\n\n```bash\nhimalaya folder list\n```\n\n### List Emails\n\nList emails in INBOX (default):\n\n```bash\nhimalaya envelope list\n```\n\nList emails in a specific folder:\n\n```bash\nhimalaya envelope list --folder \"Sent\"\n```\n\nList with pagination:\n\n```bash\nhimalaya envelope list --page 1 --page-size 20\n```\n\n### Search Emails\n\n```bash\nhimalaya envelope list from john@example.com subject meeting\n```\n\n### Read an Email\n\nRead email by ID (shows plain text):\n\n```bash\nhimalaya message read 42\n```\n\nExport raw MIME:\n\n```bash\nhimalaya message export 42 --full\n```\n\n### Reply to an Email\n\nInteractive reply (opens $EDITOR):\n\n```bash\nhimalaya message reply 42\n```\n\nReply-all:\n\n```bash\nhimalaya message reply 42 --all\n```\n\n### Forward an Email\n\n```bash\nhimalaya message forward 42\n```\n\n### Write a New Email\n\nInteractive compose (opens $EDITOR):\n\n```bash\nhimalaya message write\n```\n\nSend directly using template:\n\n```bash\ncat << 'EOF' | himalaya template send\nFrom: you@example.com\nTo: recipient@example.com\nSubject: Test Message\n\nHello from Himalaya!\nEOF\n```\n\nOr with headers flag:\n\n```bash\nhimalaya message write -H \"To:recipient@example.com\" -H \"Subject:Test\" \"Message body here\"\n```\n\n### Move/Copy Emails\n\nMove to folder:\n\n```bash\nhimalaya message move 42 \"Archive\"\n```\n\nCopy to folder:\n\n```bash\nhimalaya message copy 42 \"Important\"\n```\n\n### Delete an Email\n\n```bash\nhimalaya message delete 42\n```\n\n### Manage Flags\n\nAdd flag:\n\n```bash\nhimalaya flag add 42 --flag seen\n```\n\nRemove flag:\n\n```bash\nhimalaya flag remove 42 --flag seen\n```\n\n## Multiple Accounts\n\nList accounts:\n\n```bash\nhimalaya account list\n```\n\nUse a specific account:\n\n```bash\nhimalaya --account work envelope list\n```\n\n## Attachments\n\nSave attachments from a message:\n\n```bash\nhimalaya attachment download 42\n```\n\nSave to specific directory:\n\n```bash\nhimalaya attachment download 42 --dir ~/Downloads\n```\n\n## Output Formats\n\nMost commands support `--output` for structured output:\n\n```bash\nhimalaya envelope list --output json\nhimalaya envelope list --output plain\n```\n\n## Debugging\n\nEnable debug logging:\n\n```bash\nRUST_LOG=debug himalaya envelope list\n```\n\nFull trace with backtrace:\n\n```bash\nRUST_LOG=trace RUST_BACKTRACE=1 himalaya envelope list\n```\n\n## Tips\n\n- Use `himalaya --help` or `himalaya <command> --help` for detailed usage.\n- Message IDs are relative to the current folder; re-list after folder changes.\n- For composing rich emails with attachments, use MML syntax (see `references/message-composition.md`).\n- Store passwords securely using `pass`, system keyring, or a command that outputs the password.\n","frontmatter":{"name":"himalaya","description":"CLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).","homepage":"https://github.com/pimalaya/himalaya","metadata":{"emoji":"📧","requires":{"bins":["himalaya"]}}},"isInternal":false,"tokens":1113,"sizeBytes":4320},{"name":"SKILL.md","path":"skills/imsg/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/imsg/SKILL.md","title":"imsg","category":"anthropic-skill","format":"markdown","content":"---\nname: imsg\ndescription: iMessage/SMS CLI for listing chats, history, and sending messages via Messages.app.\nhomepage: https://imsg.to\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📨\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"imsg\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/imsg\",\n              \"bins\": [\"imsg\"],\n              \"label\": \"Install imsg (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# imsg\n\nUse `imsg` to read and send iMessage/SMS via macOS Messages.app.\n\n## When to Use\n\n✅ **USE this skill when:**\n\n- User explicitly asks to send iMessage or SMS\n- Reading iMessage conversation history\n- Checking recent Messages.app chats\n- Sending to phone numbers or Apple IDs\n\n## When NOT to Use\n\n❌ **DON'T use this skill when:**\n\n- Telegram messages → use `message` tool with `channel:telegram`\n- Signal messages → use Signal channel if configured\n- WhatsApp messages → use WhatsApp channel if configured\n- Discord messages → use `message` tool with `channel:discord`\n- Slack messages → use `slack` skill\n- Group chat management (adding/removing members) → not supported\n- Bulk/mass messaging → always confirm with user first\n- Replying in current conversation → just reply normally (OpenClaw routes automatically)\n\n## Requirements\n\n- macOS with Messages.app signed in\n- Full Disk Access for terminal\n- Automation permission for Messages.app (for sending)\n\n## Common Commands\n\n### List Chats\n\n```bash\nimsg chats --limit 10 --json\n```\n\n### View History\n\n```bash\n# By chat ID\nimsg history --chat-id 1 --limit 20 --json\n\n# With attachments info\nimsg history --chat-id 1 --limit 20 --attachments --json\n```\n\n### Watch for New Messages\n\n```bash\nimsg watch --chat-id 1 --attachments\n```\n\n### Send Messages\n\n```bash\n# Text only\nimsg send --to \"+14155551212\" --text \"Hello!\"\n\n# With attachment\nimsg send --to \"+14155551212\" --text \"Check this out\" --file /path/to/image.jpg\n\n# Specify service\nimsg send --to \"+14155551212\" --text \"Hi\" --service imessage\nimsg send --to \"+14155551212\" --text \"Hi\" --service sms\n```\n\n## Service Options\n\n- `--service imessage` — Force iMessage (requires recipient has iMessage)\n- `--service sms` — Force SMS (green bubble)\n- `--service auto` — Let Messages.app decide (default)\n\n## Safety Rules\n\n1. **Always confirm recipient and message content** before sending\n2. **Never send to unknown numbers** without explicit user approval\n3. **Be careful with attachments** — confirm file path exists\n4. **Rate limit yourself** — don't spam\n\n## Example Workflow\n\nUser: \"Text mom that I'll be late\"\n\n```bash\n# 1. Find mom's chat\nimsg chats --limit 20 --json | jq '.[] | select(.displayName | contains(\"Mom\"))'\n\n# 2. Confirm with user\n# \"Found Mom at +1555123456. Send 'I'll be late' via iMessage?\"\n\n# 3. Send after confirmation\nimsg send --to \"+1555123456\" --text \"I'll be late\"\n```\n","frontmatter":{"name":"imsg","description":"iMessage/SMS CLI for listing chats, history, and sending messages via Messages.app.","homepage":"https://imsg.to","metadata":{"openclaw":{"emoji":"📨","os":["darwin"],"requires":{"bins":["imsg"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/imsg","bins":["imsg"],"label":"Install imsg (brew)"}]}}},"isInternal":false,"tokens":772,"sizeBytes":2976},{"name":"SKILL.md","path":"skills/mcporter/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/mcporter/SKILL.md","title":"mcporter","category":"anthropic-skill","format":"markdown","content":"---\nname: mcporter\ndescription: Use the mcporter CLI to list, configure, auth, and call MCP servers/tools directly (HTTP or stdio), including ad-hoc servers, config edits, and CLI/type generation.\nhomepage: http://mcporter.dev\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📦\",\n        \"requires\": { \"bins\": [\"mcporter\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node\",\n              \"kind\": \"node\",\n              \"package\": \"mcporter\",\n              \"bins\": [\"mcporter\"],\n              \"label\": \"Install mcporter (node)\",\n            },\n          ],\n      },\n  }\n---\n\n# mcporter\n\nUse `mcporter` to work with MCP servers directly.\n\nQuick start\n\n- `mcporter list`\n- `mcporter list <server> --schema`\n- `mcporter call <server.tool> key=value`\n\nCall tools\n\n- Selector: `mcporter call linear.list_issues team=ENG limit:5`\n- Function syntax: `mcporter call \"linear.create_issue(title: \\\"Bug\\\")\"`\n- Full URL: `mcporter call https://api.example.com/mcp.fetch url:https://example.com`\n- Stdio: `mcporter call --stdio \"bun run ./server.ts\" scrape url=https://example.com`\n- JSON payload: `mcporter call <server.tool> --args '{\"limit\":5}'`\n\nAuth + config\n\n- OAuth: `mcporter auth <server | url> [--reset]`\n- Config: `mcporter config list|get|add|remove|import|login|logout`\n\nDaemon\n\n- `mcporter daemon start|status|stop|restart`\n\nCodegen\n\n- CLI: `mcporter generate-cli --server <name>` or `--command <url>`\n- Inspect: `mcporter inspect-cli <path> [--json]`\n- TS: `mcporter emit-ts <server> --mode client|types`\n\nNotes\n\n- Config default: `./config/mcporter.json` (override with `--config`).\n- Prefer `--output json` for machine-readable results.\n","frontmatter":{"name":"mcporter","description":"Use the mcporter CLI to list, configure, auth, and call MCP servers/tools directly (HTTP or stdio), including ad-hoc servers, config edits, and CLI/type generation.","homepage":"http://mcporter.dev","metadata":{"openclaw":{"emoji":"📦","requires":{"bins":["mcporter"]},"install":[{"id":"node","kind":"node","package":"mcporter","bins":["mcporter"],"label":"Install mcporter (node)"}]}}},"isInternal":false,"tokens":446,"sizeBytes":1674},{"name":"codexbar-cli.md","path":"skills/model-usage/references/codexbar-cli.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/model-usage/references/codexbar-cli.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# CodexBar CLI quick ref (usage + cost)\n\n## Install\n\n- App: Preferences -> Advanced -> Install CLI\n- Repo: ./bin/install-codexbar-cli.sh\n\n## Commands\n\n- Usage snapshot (web/cli sources):\n  - codexbar usage --format json --pretty\n  - codexbar --provider all --format json\n- Local cost usage (Codex + Claude only):\n  - codexbar cost --format json --pretty\n  - codexbar cost --provider codex|claude --format json\n\n## Cost JSON fields\n\nThe payload is an array (one per provider).\n\n- provider, source, updatedAt\n- sessionTokens, sessionCostUSD\n- last30DaysTokens, last30DaysCostUSD\n- daily[]: date, inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, totalCost, modelsUsed, modelBreakdowns[]\n- modelBreakdowns[]: modelName, cost\n- totals: totalInputTokens, totalOutputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, totalCost\n\n## Notes\n\n- Cost usage is local-only. It reads JSONL logs under:\n  - Codex: ~/.codex/sessions/\\*_/_.jsonl\n  - Claude: ~/.config/claude/projects/**/\\*.jsonl or ~/.claude/projects/**/\\*.jsonl\n- If web usage is required (non-local), use codexbar usage (not cost).\n","isInternal":false,"tokens":304,"sizeBytes":1120},{"name":"SKILL.md","path":"skills/model-usage/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/model-usage/SKILL.md","title":"model-usage","category":"anthropic-skill","format":"markdown","content":"---\nname: model-usage\ndescription: Use CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📊\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"codexbar\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew-cask\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/codexbar\",\n              \"bins\": [\"codexbar\"],\n              \"label\": \"Install CodexBar (brew cask)\",\n            },\n          ],\n      },\n  }\n---\n\n# Model usage\n\n## Overview\n\nGet per-model usage cost from CodexBar's local cost logs. Supports \"current model\" (most recent daily entry) or \"all models\" summaries for Codex or Claude.\n\nTODO: add Linux CLI support guidance once CodexBar CLI install path is documented for Linux.\n\n## Quick start\n\n1. Fetch cost JSON via CodexBar CLI or pass a JSON file.\n2. Use the bundled script to summarize by model.\n\n```bash\npython {baseDir}/scripts/model_usage.py --provider codex --mode current\npython {baseDir}/scripts/model_usage.py --provider codex --mode all\npython {baseDir}/scripts/model_usage.py --provider claude --mode all --format json --pretty\n```\n\n## Current model logic\n\n- Uses the most recent daily row with `modelBreakdowns`.\n- Picks the model with the highest cost in that row.\n- Falls back to the last entry in `modelsUsed` when breakdowns are missing.\n- Override with `--model <name>` when you need a specific model.\n\n## Inputs\n\n- Default: runs `codexbar cost --format json --provider <codex|claude>`.\n- File or stdin:\n\n```bash\ncodexbar cost --provider codex --format json > /tmp/cost.json\npython {baseDir}/scripts/model_usage.py --input /tmp/cost.json --mode all\ncat /tmp/cost.json | python {baseDir}/scripts/model_usage.py --input - --mode current\n```\n\n## Output\n\n- Text (default) or JSON (`--format json --pretty`).\n- Values are cost-only per model; tokens are not split by model in CodexBar output.\n\n## References\n\n- Read `references/codexbar-cli.md` for CLI flags and cost JSON fields.\n","frontmatter":{"name":"model-usage","description":"Use CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.","metadata":{"openclaw":{"emoji":"📊","os":["darwin"],"requires":{"bins":["codexbar"]},"install":[{"id":"brew-cask","kind":"brew","formula":"steipete/tap/codexbar","bins":["codexbar"],"label":"Install CodexBar (brew cask)"}]}}},"isInternal":false,"tokens":563,"sizeBytes":2253},{"name":"SKILL.md","path":"skills/nano-pdf/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/nano-pdf/SKILL.md","title":"nano-pdf","category":"anthropic-skill","format":"markdown","content":"---\nname: nano-pdf\ndescription: Edit PDFs with natural-language instructions using the nano-pdf CLI.\nhomepage: https://pypi.org/project/nano-pdf/\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📄\",\n        \"requires\": { \"bins\": [\"nano-pdf\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"uv\",\n              \"kind\": \"uv\",\n              \"package\": \"nano-pdf\",\n              \"bins\": [\"nano-pdf\"],\n              \"label\": \"Install nano-pdf (uv)\",\n            },\n          ],\n      },\n  }\n---\n\n# nano-pdf\n\nUse `nano-pdf` to apply edits to a specific page in a PDF using a natural-language instruction.\n\n## Quick start\n\n```bash\nnano-pdf edit deck.pdf 1 \"Change the title to 'Q3 Results' and fix the typo in the subtitle\"\n```\n\nNotes:\n\n- Page numbers are 0-based or 1-based depending on the tool’s version/config; if the result looks off by one, retry with the other.\n- Always sanity-check the output PDF before sending it out.\n","frontmatter":{"name":"nano-pdf","description":"Edit PDFs with natural-language instructions using the nano-pdf CLI.","homepage":"https://pypi.org/project/nano-pdf/","metadata":{"openclaw":{"emoji":"📄","requires":{"bins":["nano-pdf"]},"install":[{"id":"uv","kind":"uv","package":"nano-pdf","bins":["nano-pdf"],"label":"Install nano-pdf (uv)"}]}}},"isInternal":false,"tokens":230,"sizeBytes":954},{"name":"SKILL.md","path":"skills/node-connect/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/node-connect/SKILL.md","title":"node-connect","category":"anthropic-skill","format":"markdown","content":"---\nname: node-connect\ndescription: Diagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps. Use when QR/setup code/manual connect fails, local Wi-Fi works but VPS/tailnet does not, or errors mention pairing required, unauthorized, bootstrap token invalid or expired, gateway.bind, gateway.remote.url, Tailscale, or plugins.entries.device-pair.config.publicUrl.\n---\n\n# Node Connect\n\nGoal: find the one real route from node -> gateway, verify OpenClaw is advertising that route, then fix pairing/auth.\n\n## Topology first\n\nDecide which case you are in before proposing fixes:\n\n- same machine / emulator / USB tunnel\n- same LAN / local Wi-Fi\n- same Tailscale tailnet\n- public URL / reverse proxy\n\nDo not mix them.\n\n- Local Wi-Fi problem: do not switch to Tailscale unless remote access is actually needed.\n- VPS / remote gateway problem: do not keep debugging `localhost` or LAN IPs.\n\n## If ambiguous, ask first\n\nIf the setup is unclear or the failure report is vague, ask short clarifying questions before diagnosing.\n\nAsk for:\n\n- which route they intend: same machine, same LAN, Tailscale tailnet, or public URL\n- whether they used QR/setup code or manual host/port\n- the exact app text/status/error, quoted exactly if possible\n- whether `openclaw devices list` shows a pending pairing request\n\nDo not guess from `can't connect`.\n\n## Canonical checks\n\nPrefer `openclaw qr --json`. It uses the same setup-code payload Android scans.\n\n```bash\nopenclaw config get gateway.mode\nopenclaw config get gateway.bind\nopenclaw config get gateway.tailscale.mode\nopenclaw config get gateway.remote.url\nopenclaw config get gateway.auth.mode\nopenclaw config get gateway.auth.allowTailscale\nopenclaw config get plugins.entries.device-pair.config.publicUrl\nopenclaw qr --json\nopenclaw devices list\nopenclaw nodes status\n```\n\nIf this OpenClaw instance is pointed at a remote gateway, also run:\n\n```bash\nopenclaw qr --remote --json\n```\n\nIf Tailscale is part of the story:\n\n```bash\ntailscale status --json\n```\n\n## Read the result, not guesses\n\n`openclaw qr --json` success means:\n\n- `gatewayUrl`: this is the actual endpoint the app should use.\n- `urlSource`: this tells you which config path won.\n\nCommon good sources:\n\n- `gateway.bind=lan`: same Wi-Fi / LAN only\n- `gateway.bind=tailnet`: direct tailnet access\n- `gateway.tailscale.mode=serve` or `gateway.tailscale.mode=funnel`: Tailscale route\n- `plugins.entries.device-pair.config.publicUrl`: explicit public/reverse-proxy route\n- `gateway.remote.url`: remote gateway route\n\n## Root-cause map\n\nIf `openclaw qr --json` says `Gateway is only bound to loopback`:\n\n- remote node cannot connect yet\n- fix the route, then generate a fresh setup code\n- `gateway.bind=auto` is not enough if the effective QR route is still loopback\n- same LAN: use `gateway.bind=lan`\n- same tailnet: prefer `gateway.tailscale.mode=serve` or use `gateway.bind=tailnet`\n- public internet: set a real `plugins.entries.device-pair.config.publicUrl` or `gateway.remote.url`\n\nIf `gateway.bind=tailnet set, but no tailnet IP was found`:\n\n- gateway host is not actually on Tailscale\n\nIf `qr --remote requires gateway.remote.url`:\n\n- remote-mode config is incomplete\n\nIf the app says `pairing required`:\n\n- network route and auth worked\n- approve the pending device\n\n```bash\nopenclaw devices list\nopenclaw devices approve --latest\n```\n\nIf the app says `bootstrap token invalid or expired`:\n\n- old setup code\n- generate a fresh one and rescan\n- do this after any URL/auth fix too\n\nIf the app says `unauthorized`:\n\n- wrong token/password, or wrong Tailscale expectation\n- for Tailscale Serve, `gateway.auth.allowTailscale` must match the intended flow\n- otherwise use explicit token/password\n\n## Fast heuristics\n\n- Same Wi-Fi setup + gateway advertises `127.0.0.1`, `localhost`, or loopback-only config: wrong.\n- Remote setup + setup/manual uses private LAN IP: wrong.\n- Tailnet setup + gateway advertises LAN IP instead of MagicDNS / tailnet route: wrong.\n- Public URL set but QR still advertises something else: inspect `urlSource`; config is not what you think.\n- `openclaw devices list` shows pending requests: stop changing network config and approve first.\n\n## Fix style\n\nReply with one concrete diagnosis and one route.\n\nIf there is not enough signal yet, ask for setup + exact app text instead of guessing.\n\nGood:\n\n- `The gateway is still loopback-only, so a node on another network can never reach it. Enable Tailscale Serve, restart the gateway, run openclaw qr again, rescan, then approve the pending device pairing.`\n\nBad:\n\n- `Maybe LAN, maybe Tailscale, maybe port forwarding, maybe public URL.`\n","frontmatter":{"name":"node-connect","description":"Diagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps. Use when QR/setup code/manual connect fails, local Wi-Fi works but VPS/tailnet does not, or errors mention pairing required, unauthorized, bootstrap token invalid or expired, gateway.bind, gateway.remote.url, Tailscale, or plugins.entries.device-pair.config.publicUrl."},"isInternal":false,"tokens":1127,"sizeBytes":4649},{"name":"SKILL.md","path":"skills/notion/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/notion/SKILL.md","title":"notion","category":"anthropic-skill","format":"markdown","content":"---\nname: notion\ndescription: Notion API for creating and managing pages, databases, and blocks.\nhomepage: https://developers.notion.com\nmetadata:\n  {\n    \"emoji\": \"📝\",\n    \"requires\": { \"env\": [\"NOTION_API_KEY\"] },\n    \"primaryEnv\": \"NOTION_API_KEY\"\n  }\n---\n\n# notion\n\nUse the Notion API to create/read/update pages, data sources (databases), and blocks.\n\n## Setup\n\n1. Create an integration at https://notion.so/my-integrations\n2. Copy the API key (starts with `ntn_` or `secret_`)\n3. Store it:\n\n```bash\nmkdir -p ~/.config/notion\necho \"ntn_your_key_here\" > ~/.config/notion/api_key\n```\n\n4. Share target pages/databases with your integration (click \"...\" → \"Connect to\" → your integration name)\n\n## API Basics\n\nAll requests need:\n\n```bash\nNOTION_KEY=$(cat ~/.config/notion/api_key)\ncurl -X GET \"https://api.notion.com/v1/...\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\"\n```\n\n> **Note:** The `Notion-Version` header is required. This skill uses `2025-09-03` (latest). In this version, databases are called \"data sources\" in the API.\n\n## Common Operations\n\n**Search for pages and data sources:**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/search\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"query\": \"page title\"}'\n```\n\n**Get page:**\n\n```bash\ncurl \"https://api.notion.com/v1/pages/{page_id}\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\"\n```\n\n**Get page content (blocks):**\n\n```bash\ncurl \"https://api.notion.com/v1/blocks/{page_id}/children\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\"\n```\n\n**Create page in a data source:**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/pages\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"parent\": {\"database_id\": \"xxx\"},\n    \"properties\": {\n      \"Name\": {\"title\": [{\"text\": {\"content\": \"New Item\"}}]},\n      \"Status\": {\"select\": {\"name\": \"Todo\"}}\n    }\n  }'\n```\n\n**Query a data source (database):**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/data_sources/{data_source_id}/query\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"filter\": {\"property\": \"Status\", \"select\": {\"equals\": \"Active\"}},\n    \"sorts\": [{\"property\": \"Date\", \"direction\": \"descending\"}]\n  }'\n```\n\n**Create a data source (database):**\n\n```bash\ncurl -X POST \"https://api.notion.com/v1/data_sources\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"parent\": {\"page_id\": \"xxx\"},\n    \"title\": [{\"text\": {\"content\": \"My Database\"}}],\n    \"properties\": {\n      \"Name\": {\"title\": {}},\n      \"Status\": {\"select\": {\"options\": [{\"name\": \"Todo\"}, {\"name\": \"Done\"}]}},\n      \"Date\": {\"date\": {}}\n    }\n  }'\n```\n\n**Update page properties:**\n\n```bash\ncurl -X PATCH \"https://api.notion.com/v1/pages/{page_id}\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"properties\": {\"Status\": {\"select\": {\"name\": \"Done\"}}}}'\n```\n\n**Add blocks to page:**\n\n```bash\ncurl -X PATCH \"https://api.notion.com/v1/blocks/{page_id}/children\" \\\n  -H \"Authorization: Bearer $NOTION_KEY\" \\\n  -H \"Notion-Version: 2025-09-03\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"children\": [\n      {\"object\": \"block\", \"type\": \"paragraph\", \"paragraph\": {\"rich_text\": [{\"text\": {\"content\": \"Hello\"}}]}}\n    ]\n  }'\n```\n\n## Property Types\n\nCommon property formats for database items:\n\n- **Title:** `{\"title\": [{\"text\": {\"content\": \"...\"}}]}`\n- **Rich text:** `{\"rich_text\": [{\"text\": {\"content\": \"...\"}}]}`\n- **Select:** `{\"select\": {\"name\": \"Option\"}}`\n- **Multi-select:** `{\"multi_select\": [{\"name\": \"A\"}, {\"name\": \"B\"}]}`\n- **Date:** `{\"date\": {\"start\": \"2024-01-15\", \"end\": \"2024-01-16\"}}`\n- **Checkbox:** `{\"checkbox\": true}`\n- **Number:** `{\"number\": 42}`\n- **URL:** `{\"url\": \"https://...\"}`\n- **Email:** `{\"email\": \"a@b.com\"}`\n- **Relation:** `{\"relation\": [{\"id\": \"page_id\"}]}`\n\n## Key Differences in 2025-09-03\n\n- **Databases → Data Sources:** Use `/data_sources/` endpoints for queries and retrieval\n- **Two IDs:** Each database now has both a `database_id` and a `data_source_id`\n  - Use `database_id` when creating pages (`parent: {\"database_id\": \"...\"}`)\n  - Use `data_source_id` when querying (`POST /v1/data_sources/{id}/query`)\n- **Search results:** Databases return as `\"object\": \"data_source\"` with their `data_source_id`\n- **Parent in responses:** Pages show `parent.data_source_id` alongside `parent.database_id`\n- **Finding the data_source_id:** Search for the database, or call `GET /v1/data_sources/{data_source_id}`\n\n## Notes\n\n- Page/database IDs are UUIDs (with or without dashes)\n- The API cannot set database view filters — that's UI-only\n- Rate limit: ~3 requests/second average, with `429 rate_limited` responses using `Retry-After`\n- Append block children: up to 100 children per request, up to two levels of nesting in a single append request\n- Payload size limits: up to 1000 block elements and 500KB overall\n- Use `is_inline: true` when creating data sources to embed them in pages\n","frontmatter":{"name":"notion","description":"Notion API for creating and managing pages, databases, and blocks.","homepage":"https://developers.notion.com","metadata":{"emoji":"📝","requires":{"env":["NOTION_API_KEY"]},"primaryEnv":"NOTION_API_KEY"}},"isInternal":false,"tokens":1617,"sizeBytes":5366},{"name":"SKILL.md","path":"skills/obsidian/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/obsidian/SKILL.md","title":"obsidian","category":"anthropic-skill","format":"markdown","content":"---\nname: obsidian\ndescription: Work with Obsidian vaults (plain Markdown notes) and automate via obsidian-cli.\nhomepage: https://help.obsidian.md\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"💎\",\n        \"requires\": { \"bins\": [\"obsidian-cli\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"yakitrak/yakitrak/obsidian-cli\",\n              \"bins\": [\"obsidian-cli\"],\n              \"label\": \"Install obsidian-cli (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# Obsidian\n\nObsidian vault = a normal folder on disk.\n\nVault structure (typical)\n\n- Notes: `*.md` (plain text Markdown; edit with any editor)\n- Config: `.obsidian/` (workspace + plugin settings; usually don’t touch from scripts)\n- Canvases: `*.canvas` (JSON)\n- Attachments: whatever folder you chose in Obsidian settings (images/PDFs/etc.)\n\n## Find the active vault(s)\n\nObsidian desktop tracks vaults here (source of truth):\n\n- `~/Library/Application Support/obsidian/obsidian.json`\n\n`obsidian-cli` resolves vaults from that file; vault name is typically the **folder name** (path suffix).\n\nFast “what vault is active / where are the notes?”\n\n- If you’ve already set a default: `obsidian-cli print-default --path-only`\n- Otherwise, read `~/Library/Application Support/obsidian/obsidian.json` and use the vault entry with `\"open\": true`.\n\nNotes\n\n- Multiple vaults common (iCloud vs `~/Documents`, work/personal, etc.). Don’t guess; read config.\n- Avoid writing hardcoded vault paths into scripts; prefer reading the config or using `print-default`.\n\n## obsidian-cli quick start\n\nPick a default vault (once):\n\n- `obsidian-cli set-default \"<vault-folder-name>\"`\n- `obsidian-cli print-default` / `obsidian-cli print-default --path-only`\n\nSearch\n\n- `obsidian-cli search \"query\"` (note names)\n- `obsidian-cli search-content \"query\"` (inside notes; shows snippets + lines)\n\nCreate\n\n- `obsidian-cli create \"Folder/New note\" --content \"...\" --open`\n- Requires Obsidian URI handler (`obsidian://…`) working (Obsidian installed).\n- Avoid creating notes under “hidden” dot-folders (e.g. `.something/...`) via URI; Obsidian may refuse.\n\nMove/rename (safe refactor)\n\n- `obsidian-cli move \"old/path/note\" \"new/path/note\"`\n- Updates `[[wikilinks]]` and common Markdown links across the vault (this is the main win vs `mv`).\n\nDelete\n\n- `obsidian-cli delete \"path/note\"`\n\nPrefer direct edits when appropriate: open the `.md` file and change it; Obsidian will pick it up.\n","frontmatter":{"name":"obsidian","description":"Work with Obsidian vaults (plain Markdown notes) and automate via obsidian-cli.","homepage":"https://help.obsidian.md","metadata":{"openclaw":{"emoji":"💎","requires":{"bins":["obsidian-cli"]},"install":[{"id":"brew","kind":"brew","formula":"yakitrak/yakitrak/obsidian-cli","bins":["obsidian-cli"],"label":"Install obsidian-cli (brew)"}]}}},"isInternal":false,"tokens":627,"sizeBytes":2531},{"name":"SKILL.md","path":"skills/openai-whisper-api/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/openai-whisper-api/SKILL.md","title":"openai-whisper-api","category":"anthropic-skill","format":"markdown","content":"---\nname: openai-whisper-api\ndescription: Transcribe audio via OpenAI Audio Transcriptions API (Whisper).\nhomepage: https://platform.openai.com/docs/guides/speech-to-text\nmetadata:\n  {\n    \"emoji\": \"🌐\",\n    \"requires\": { \"bins\": [\"curl\"], \"env\": [\"OPENAI_API_KEY\"] },\n    \"primaryEnv\": \"OPENAI_API_KEY\"\n  }\n---\n\n# OpenAI Whisper API (curl)\n\nTranscribe an audio file via OpenAI's `/v1/audio/transcriptions` endpoint. Set `OPENAI_BASE_URL` to use an OpenAI-compatible proxy or local gateway.\n\n## Quick start\n\n```bash\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@/path/to/audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  > transcript.txt\n```\n\nDefaults:\n\n- Model: `whisper-1`\n- Output format: `text`\n\n## Options\n\n```bash\n# With language hint\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.ogg\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  -F \"language=en\" \\\n  > transcript.txt\n\n# With speaker hint (prompt)\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  -F \"prompt=Speaker names: Peter, Daniel\" \\\n  > transcript.txt\n\n# JSON output\ncurl -sS \"https://api.openai.com/v1/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=json\" \\\n  > transcript.json\n```\n\n## Custom base URL\n\nSet `OPENAI_BASE_URL` to use an OpenAI-compatible proxy or local gateway:\n\n```bash\nAPI_BASE=\"${OPENAI_BASE_URL:-https://api.openai.com/v1}\"\ncurl -sS \"${API_BASE}/audio/transcriptions\" \\\n  -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n  -F \"file=@audio.m4a\" \\\n  -F \"model=whisper-1\" \\\n  -F \"response_format=text\" \\\n  > transcript.txt\n```\n\n## API key\n\nSet `OPENAI_API_KEY` environment variable before running commands.\n","frontmatter":{"name":"openai-whisper-api","description":"Transcribe audio via OpenAI Audio Transcriptions API (Whisper).","homepage":"https://platform.openai.com/docs/guides/speech-to-text","metadata":{"emoji":"🌐","requires":{"bins":["curl"],"env":["OPENAI_API_KEY"]},"primaryEnv":"OPENAI_API_KEY"}},"isInternal":false,"tokens":620,"sizeBytes":2007},{"name":"SKILL.md","path":"skills/openai-whisper/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/openai-whisper/SKILL.md","title":"openai-whisper","category":"anthropic-skill","format":"markdown","content":"---\nname: openai-whisper\ndescription: Local speech-to-text with the Whisper CLI (no API key).\nhomepage: https://openai.com/research/whisper\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🎤\",\n        \"requires\": { \"bins\": [\"whisper\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"openai-whisper\",\n              \"bins\": [\"whisper\"],\n              \"label\": \"Install OpenAI Whisper (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# Whisper (CLI)\n\nUse `whisper` to transcribe audio locally.\n\nQuick start\n\n- `whisper /path/audio.mp3 --model medium --output_format txt --output_dir .`\n- `whisper /path/audio.m4a --task translate --output_format srt`\n\nNotes\n\n- Models download to `~/.cache/whisper` on first run.\n- `--model` defaults to `turbo` on this install.\n- Use smaller models for speed, larger for accuracy.\n","frontmatter":{"name":"openai-whisper","description":"Local speech-to-text with the Whisper CLI (no API key).","homepage":"https://openai.com/research/whisper","metadata":{"openclaw":{"emoji":"🎤","requires":{"bins":["whisper"]},"install":[{"id":"brew","kind":"brew","formula":"openai-whisper","bins":["whisper"],"label":"Install OpenAI Whisper (brew)"}]}}},"isInternal":false,"tokens":234,"sizeBytes":912},{"name":"SKILL.md","path":"skills/openhue/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/openhue/SKILL.md","title":"openhue","category":"anthropic-skill","format":"markdown","content":"---\nname: openhue\ndescription: Control Philips Hue lights and scenes via the OpenHue CLI.\nhomepage: https://www.openhue.io/cli\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"💡\",\n        \"requires\": { \"bins\": [\"openhue\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"openhue/cli/openhue-cli\",\n              \"bins\": [\"openhue\"],\n              \"label\": \"Install OpenHue CLI (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# OpenHue CLI\n\nUse `openhue` to control Philips Hue lights and scenes via a Hue Bridge.\n\n## When to Use\n\n✅ **USE this skill when:**\n\n- \"Turn on/off the lights\"\n- \"Dim the living room lights\"\n- \"Set a scene\" or \"movie mode\"\n- Controlling specific Hue rooms or zones\n- Adjusting brightness, color, or color temperature\n\n## When NOT to Use\n\n❌ **DON'T use this skill when:**\n\n- Non-Hue smart devices (other brands) → not supported\n- HomeKit scenes or Shortcuts → use Apple's ecosystem\n- TV or entertainment system control\n- Thermostat or HVAC\n- Smart plugs (unless Hue smart plugs)\n\n## Common Commands\n\n### List Resources\n\n```bash\nopenhue get light       # List all lights\nopenhue get room        # List all rooms\nopenhue get scene       # List all scenes\n```\n\n### Control Lights\n\n```bash\n# Turn on/off\nopenhue set light \"Bedroom Lamp\" --on\nopenhue set light \"Bedroom Lamp\" --off\n\n# Brightness (0-100)\nopenhue set light \"Bedroom Lamp\" --on --brightness 50\n\n# Color temperature (warm to cool: 153-500 mirek)\nopenhue set light \"Bedroom Lamp\" --on --temperature 300\n\n# Color (by name or hex)\nopenhue set light \"Bedroom Lamp\" --on --color red\nopenhue set light \"Bedroom Lamp\" --on --rgb \"#FF5500\"\n```\n\n### Control Rooms\n\n```bash\n# Turn off entire room\nopenhue set room \"Bedroom\" --off\n\n# Set room brightness\nopenhue set room \"Bedroom\" --on --brightness 30\n```\n\n### Scenes\n\n```bash\n# Activate scene\nopenhue set scene \"Relax\" --room \"Bedroom\"\nopenhue set scene \"Concentrate\" --room \"Office\"\n```\n\n## Quick Presets\n\n```bash\n# Bedtime (dim warm)\nopenhue set room \"Bedroom\" --on --brightness 20 --temperature 450\n\n# Work mode (bright cool)\nopenhue set room \"Office\" --on --brightness 100 --temperature 250\n\n# Movie mode (dim)\nopenhue set room \"Living Room\" --on --brightness 10\n```\n\n## Notes\n\n- Bridge must be on local network\n- First run requires button press on Hue bridge to pair\n- Colors only work on color-capable bulbs (not white-only)\n","frontmatter":{"name":"openhue","description":"Control Philips Hue lights and scenes via the OpenHue CLI.","homepage":"https://www.openhue.io/cli","metadata":{"openclaw":{"emoji":"💡","requires":{"bins":["openhue"]},"install":[{"id":"brew","kind":"brew","formula":"openhue/cli/openhue-cli","bins":["openhue"],"label":"Install OpenHue CLI (brew)"}]}}},"isInternal":false,"tokens":661,"sizeBytes":2460},{"name":"SKILL.md","path":"skills/oracle/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/oracle/SKILL.md","title":"oracle","category":"anthropic-skill","format":"markdown","content":"---\nname: oracle\ndescription: Best practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns).\nhomepage: https://askoracle.dev\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🧿\",\n        \"requires\": { \"bins\": [\"oracle\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node\",\n              \"kind\": \"node\",\n              \"package\": \"@steipete/oracle\",\n              \"bins\": [\"oracle\"],\n              \"label\": \"Install oracle (node)\",\n            },\n          ],\n      },\n  }\n---\n\n# oracle — best use\n\nOracle bundles your prompt + selected files into one “one-shot” request so another model can answer with real repo context (API or browser automation). Treat output as advisory: verify against code + tests.\n\n## Main use case (browser, GPT‑5.2 Pro)\n\nDefault workflow here: `--engine browser` with GPT‑5.2 Pro in ChatGPT. This is the common “long think” path: ~10 minutes to ~1 hour is normal; expect a stored session you can reattach to.\n\nRecommended defaults:\n\n- Engine: browser (`--engine browser`)\n- Model: GPT‑5.2 Pro (`--model gpt-5.2-pro` or `--model \"5.2 Pro\"`)\n\n## Golden path\n\n1. Pick a tight file set (fewest files that still contain the truth).\n2. Preview payload + token spend (`--dry-run` + `--files-report`).\n3. Use browser mode for the usual GPT‑5.2 Pro workflow; use API only when you explicitly want it.\n4. If the run detaches/timeouts: reattach to the stored session (don’t re-run).\n\n## Commands (preferred)\n\n- Help:\n  - `oracle --help`\n  - If the binary isn’t installed: `npx -y @steipete/oracle --help` (avoid `pnpx` here; sqlite bindings).\n\n- Preview (no tokens):\n  - `oracle --dry-run summary -p \"<task>\" --file \"src/**\" --file \"!**/*.test.*\"`\n  - `oracle --dry-run full -p \"<task>\" --file \"src/**\"`\n\n- Token sanity:\n  - `oracle --dry-run summary --files-report -p \"<task>\" --file \"src/**\"`\n\n- Browser run (main path; long-running is normal):\n  - `oracle --engine browser --model gpt-5.2-pro -p \"<task>\" --file \"src/**\"`\n\n- Manual paste fallback:\n  - `oracle --render --copy -p \"<task>\" --file \"src/**\"`\n  - Note: `--copy` is a hidden alias for `--copy-markdown`.\n\n## Attaching files (`--file`)\n\n`--file` accepts files, directories, and globs. You can pass it multiple times; entries can be comma-separated.\n\n- Include:\n  - `--file \"src/**\"`\n  - `--file src/index.ts`\n  - `--file docs --file README.md`\n\n- Exclude:\n  - `--file \"src/**\" --file \"!src/**/*.test.ts\" --file \"!**/*.snap\"`\n\n- Defaults (implementation behavior):\n  - Default-ignored dirs: `node_modules`, `dist`, `coverage`, `.git`, `.turbo`, `.next`, `build`, `tmp` (skipped unless explicitly passed as literal dirs/files).\n  - Honors `.gitignore` when expanding globs.\n  - Does not follow symlinks.\n  - Dotfiles filtered unless opted in via pattern (e.g. `--file \".github/**\"`).\n  - Files > 1 MB rejected.\n\n## Engines (API vs browser)\n\n- Auto-pick: `api` when `OPENAI_API_KEY` is set; otherwise `browser`.\n- Browser supports GPT + Gemini only; use `--engine api` for Claude/Grok/Codex or multi-model runs.\n- Browser attachments:\n  - `--browser-attachments auto|never|always` (auto pastes inline up to ~60k chars then uploads).\n- Remote browser host:\n  - Host: `oracle serve --host 0.0.0.0 --port 9473 --token <secret>`\n  - Client: `oracle --engine browser --remote-host <host:port> --remote-token <secret> -p \"<task>\" --file \"src/**\"`\n\n## Sessions + slugs\n\n- Stored under `~/.oracle/sessions` (override with `ORACLE_HOME_DIR`).\n- Runs may detach or take a long time (browser + GPT‑5.2 Pro often does). If the CLI times out: don’t re-run; reattach.\n  - List: `oracle status --hours 72`\n  - Attach: `oracle session <id> --render`\n- Use `--slug \"<3-5 words>\"` to keep session IDs readable.\n- Duplicate prompt guard exists; use `--force` only when you truly want a fresh run.\n\n## Prompt template (high signal)\n\nOracle starts with **zero** project knowledge. Assume the model cannot infer your stack, build tooling, conventions, or “obvious” paths. Include:\n\n- Project briefing (stack + build/test commands + platform constraints).\n- “Where things live” (key directories, entrypoints, config files, boundaries).\n- Exact question + what you tried + the error text (verbatim).\n- Constraints (“don’t change X”, “must keep public API”, etc).\n- Desired output (“return patch plan + tests”, “give 3 options with tradeoffs”).\n\n## Safety\n\n- Don’t attach secrets by default (`.env`, key files, auth tokens). Redact aggressively; share only what’s required.\n\n## “Exhaustive prompt” restoration pattern\n\nFor long investigations, write a standalone prompt + file set so you can rerun days later:\n\n- 6–30 sentence project briefing + the goal.\n- Repro steps + exact errors + what you tried.\n- Attach all context files needed (entrypoints, configs, key modules, docs).\n\nOracle runs are one-shot; the model doesn’t remember prior runs. “Restoring context” means re-running with the same prompt + `--file …` set (or reattaching a still-running stored session).\n","frontmatter":{"name":"oracle","description":"Best practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns).","homepage":"https://askoracle.dev","metadata":{"openclaw":{"emoji":"🧿","requires":{"bins":["oracle"]},"install":[{"id":"node","kind":"node","package":"@steipete/oracle","bins":["oracle"],"label":"Install oracle (node)"}]}}},"isInternal":false,"tokens":1323,"sizeBytes":5073},{"name":"SKILL.md","path":"skills/ordercli/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/ordercli/SKILL.md","title":"ordercli","category":"anthropic-skill","format":"markdown","content":"---\nname: ordercli\ndescription: Foodora-only CLI for checking past orders and active order status (Deliveroo WIP).\nhomepage: https://ordercli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🛵\",\n        \"requires\": { \"bins\": [\"ordercli\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/ordercli\",\n              \"bins\": [\"ordercli\"],\n              \"label\": \"Install ordercli (brew)\",\n            },\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/ordercli/cmd/ordercli@latest\",\n              \"bins\": [\"ordercli\"],\n              \"label\": \"Install ordercli (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# ordercli\n\nUse `ordercli` to check past orders and track active order status (Foodora only right now).\n\nQuick start (Foodora)\n\n- `ordercli foodora countries`\n- `ordercli foodora config set --country AT`\n- `ordercli foodora login --email you@example.com --password-stdin`\n- `ordercli foodora orders`\n- `ordercli foodora history --limit 20`\n- `ordercli foodora history show <orderCode>`\n\nOrders\n\n- Active list (arrival/status): `ordercli foodora orders`\n- Watch: `ordercli foodora orders --watch`\n- Active order detail: `ordercli foodora order <orderCode>`\n- History detail JSON: `ordercli foodora history show <orderCode> --json`\n\nReorder (adds to cart)\n\n- Preview: `ordercli foodora reorder <orderCode>`\n- Confirm: `ordercli foodora reorder <orderCode> --confirm`\n- Address: `ordercli foodora reorder <orderCode> --confirm --address-id <id>`\n\nCloudflare / bot protection\n\n- Browser login: `ordercli foodora login --email you@example.com --password-stdin --browser`\n- Reuse profile: `--browser-profile \"$HOME/Library/Application Support/ordercli/browser-profile\"`\n- Import Chrome cookies: `ordercli foodora cookies chrome --profile \"Default\"`\n\nSession import (no password)\n\n- `ordercli foodora session chrome --url https://www.foodora.at/ --profile \"Default\"`\n- `ordercli foodora session refresh --client-id android`\n\nDeliveroo (WIP, not working yet)\n\n- Requires `DELIVEROO_BEARER_TOKEN` (optional `DELIVEROO_COOKIE`).\n- `ordercli deliveroo config set --market uk`\n- `ordercli deliveroo history`\n\nNotes\n\n- Use `--config /tmp/ordercli.json` for testing.\n- Confirm before any reorder or cart-changing action.\n","frontmatter":{"name":"ordercli","description":"Foodora-only CLI for checking past orders and active order status (Deliveroo WIP).","homepage":"https://ordercli.sh","metadata":{"openclaw":{"emoji":"🛵","requires":{"bins":["ordercli"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/ordercli","bins":["ordercli"],"label":"Install ordercli (brew)"},{"id":"go","kind":"go","module":"github.com/steipete/ordercli/cmd/ordercli@latest","bins":["ordercli"],"label":"Install ordercli (go)"}]}}},"isInternal":false,"tokens":593,"sizeBytes":2389},{"name":"SKILL.md","path":"skills/peekaboo/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/peekaboo/SKILL.md","title":"peekaboo","category":"anthropic-skill","format":"markdown","content":"---\nname: peekaboo\ndescription: Capture and automate macOS UI with the Peekaboo CLI.\nhomepage: https://peekaboo.boo\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"👀\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"peekaboo\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/peekaboo\",\n              \"bins\": [\"peekaboo\"],\n              \"label\": \"Install Peekaboo (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# Peekaboo\n\nPeekaboo is a full macOS UI automation CLI: capture/inspect screens, target UI\nelements, drive input, and manage apps/windows/menus. Commands share a snapshot\ncache and support `--json`/`-j` for scripting. Run `peekaboo` or\n`peekaboo <cmd> --help` for flags; `peekaboo --version` prints build metadata.\nTip: run via `polter peekaboo` to ensure fresh builds.\n\n## Features (all CLI capabilities, excluding agent/MCP)\n\nCore\n\n- `bridge`: inspect Peekaboo Bridge host connectivity\n- `capture`: live capture or video ingest + frame extraction\n- `clean`: prune snapshot cache and temp files\n- `config`: init/show/edit/validate, providers, models, credentials\n- `image`: capture screenshots (screen/window/menu bar regions)\n- `learn`: print the full agent guide + tool catalog\n- `list`: apps, windows, screens, menubar, permissions\n- `permissions`: check Screen Recording/Accessibility status\n- `run`: execute `.peekaboo.json` scripts\n- `sleep`: pause execution for a duration\n- `tools`: list available tools with filtering/display options\n\nInteraction\n\n- `click`: target by ID/query/coords with smart waits\n- `drag`: drag & drop across elements/coords/Dock\n- `hotkey`: modifier combos like `cmd,shift,t`\n- `move`: cursor positioning with optional smoothing\n- `paste`: set clipboard -> paste -> restore\n- `press`: special-key sequences with repeats\n- `scroll`: directional scrolling (targeted + smooth)\n- `swipe`: gesture-style drags between targets\n- `type`: text + control keys (`--clear`, delays)\n\nSystem\n\n- `app`: launch/quit/relaunch/hide/unhide/switch/list apps\n- `clipboard`: read/write clipboard (text/images/files)\n- `dialog`: click/input/file/dismiss/list system dialogs\n- `dock`: launch/right-click/hide/show/list Dock items\n- `menu`: click/list application menus + menu extras\n- `menubar`: list/click status bar items\n- `open`: enhanced `open` with app targeting + JSON payloads\n- `space`: list/switch/move-window (Spaces)\n- `visualizer`: exercise Peekaboo visual feedback animations\n- `window`: close/minimize/maximize/move/resize/focus/list\n\nVision\n\n- `see`: annotated UI maps, snapshot IDs, optional analysis\n\nGlobal runtime flags\n\n- `--json`/`-j`, `--verbose`/`-v`, `--log-level <level>`\n- `--no-remote`, `--bridge-socket <path>`\n\n## Quickstart (happy path)\n\n```bash\npeekaboo permissions\npeekaboo list apps --json\npeekaboo see --annotate --path /tmp/peekaboo-see.png\npeekaboo click --on B1\npeekaboo type \"Hello\" --return\n```\n\n## Common targeting parameters (most interaction commands)\n\n- App/window: `--app`, `--pid`, `--window-title`, `--window-id`, `--window-index`\n- Snapshot targeting: `--snapshot` (ID from `see`; defaults to latest)\n- Element/coords: `--on`/`--id` (element ID), `--coords x,y`\n- Focus control: `--no-auto-focus`, `--space-switch`, `--bring-to-current-space`,\n  `--focus-timeout-seconds`, `--focus-retry-count`\n\n## Common capture parameters\n\n- Output: `--path`, `--format png|jpg`, `--retina`\n- Targeting: `--mode screen|window|frontmost`, `--screen-index`,\n  `--window-title`, `--window-id`\n- Analysis: `--analyze \"prompt\"`, `--annotate`\n- Capture engine: `--capture-engine auto|classic|cg|modern|sckit`\n\n## Common motion/typing parameters\n\n- Timing: `--duration` (drag/swipe), `--steps`, `--delay` (type/scroll/press)\n- Human-ish movement: `--profile human|linear`, `--wpm` (typing)\n- Scroll: `--direction up|down|left|right`, `--amount <ticks>`, `--smooth`\n\n## Examples\n\n### See -> click -> type (most reliable flow)\n\n```bash\npeekaboo see --app Safari --window-title \"Login\" --annotate --path /tmp/see.png\npeekaboo click --on B3 --app Safari\npeekaboo type \"user@example.com\" --app Safari\npeekaboo press tab --count 1 --app Safari\npeekaboo type \"supersecret\" --app Safari --return\n```\n\n### Target by window id\n\n```bash\npeekaboo list windows --app \"Visual Studio Code\" --json\npeekaboo click --window-id 12345 --coords 120,160\npeekaboo type \"Hello from Peekaboo\" --window-id 12345\n```\n\n### Capture screenshots + analyze\n\n```bash\npeekaboo image --mode screen --screen-index 0 --retina --path /tmp/screen.png\npeekaboo image --app Safari --window-title \"Dashboard\" --analyze \"Summarize KPIs\"\npeekaboo see --mode screen --screen-index 0 --analyze \"Summarize the dashboard\"\n```\n\n### Live capture (motion-aware)\n\n```bash\npeekaboo capture live --mode region --region 100,100,800,600 --duration 30 \\\n  --active-fps 8 --idle-fps 2 --highlight-changes --path /tmp/capture\n```\n\n### App + window management\n\n```bash\npeekaboo app launch \"Safari\" --open https://example.com\npeekaboo window focus --app Safari --window-title \"Example\"\npeekaboo window set-bounds --app Safari --x 50 --y 50 --width 1200 --height 800\npeekaboo app quit --app Safari\n```\n\n### Menus, menubar, dock\n\n```bash\npeekaboo menu click --app Safari --item \"New Window\"\npeekaboo menu click --app TextEdit --path \"Format > Font > Show Fonts\"\npeekaboo menu click-extra --title \"WiFi\"\npeekaboo dock launch Safari\npeekaboo menubar list --json\n```\n\n### Mouse + gesture input\n\n```bash\npeekaboo move 500,300 --smooth\npeekaboo drag --from B1 --to T2\npeekaboo swipe --from-coords 100,500 --to-coords 100,200 --duration 800\npeekaboo scroll --direction down --amount 6 --smooth\n```\n\n### Keyboard input\n\n```bash\npeekaboo hotkey --keys \"cmd,shift,t\"\npeekaboo press escape\npeekaboo type \"Line 1\\nLine 2\" --delay 10\n```\n\nNotes\n\n- Requires Screen Recording + Accessibility permissions.\n- Use `peekaboo see --annotate` to identify targets before clicking.\n","frontmatter":{"name":"peekaboo","description":"Capture and automate macOS UI with the Peekaboo CLI.","homepage":"https://peekaboo.boo","metadata":{"openclaw":{"emoji":"👀","os":["darwin"],"requires":{"bins":["peekaboo"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/peekaboo","bins":["peekaboo"],"label":"Install Peekaboo (brew)"}]}}},"isInternal":false,"tokens":1649,"sizeBytes":5970},{"name":"pptxgenjs.md","path":"skills/powerpoint/references/pptxgenjs.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/powerpoint/references/pptxgenjs.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# PptxGenJS Reference\n\nUse this reference when writing the JavaScript build script for the `pptx_write` tool.\n\n## Script Creation\n\nPass the complete JavaScript module in the `script` argument to `pptx_write`. Do not create a temporary `.mjs` file with `local_file_write` or shell commands for this workflow.\n\n## Basic Structure\n\n```javascript\nexport default async function build(pptx, ctx) {\n  pptx.layout = \"LAYOUT_WIDE\";\n  pptx.title = ctx.data?.title || \"Presentation\";\n\n  const slide = pptx.addSlide();\n  slide.background = { color: \"FFFFFF\" };\n  slide.addText(\"Hello\", { x: 0.6, y: 0.5, w: 8, h: 0.6, fontSize: 36 });\n}\n```\n\nUseful layouts:\n\n- `LAYOUT_WIDE`: 13.333 x 7.5 in.\n- `LAYOUT_16X9`: 10 x 5.625 in.\n- `LAYOUT_4X3`: 10 x 7.5 in.\n\nUse inches for all `x`, `y`, `w`, and `h` values.\n\n## Text\n\n```javascript\nslide.addText(\"Main title\", {\n  x: 0.6, y: 0.4, w: 8.5, h: 0.6,\n  fontFace: \"Aptos Display\",\n  fontSize: 38,\n  bold: true,\n  color: \"111827\",\n  margin: 0,\n});\n\nslide.addText([\n  { text: \"First point\", options: { bullet: true, breakLine: true } },\n  { text: \"Second point\", options: { bullet: true } },\n], {\n  x: 0.8, y: 1.4, w: 5.4, h: 1.2,\n  fontSize: 17,\n  color: \"374151\",\n  breakLine: false,\n  paraSpaceAfterPt: 8,\n});\n```\n\nRules:\n\n- Use `margin: 0` when aligning text with shapes or icons.\n- Use PptxGenJS bullets; do not type bullet glyphs into strings.\n- Use `breakLine: true` in rich text arrays when items must appear on separate lines.\n- Avoid tiny body text; most slide body copy should stay at or above 14 pt.\n\n## Shapes\n\n```javascript\nslide.addShape(pptx.ShapeType.rect, {\n  x: 0, y: 0, w: 13.333, h: 7.5,\n  fill: { color: \"F8FAFC\" },\n  line: { color: \"F8FAFC\" },\n});\n\nslide.addShape(pptx.ShapeType.roundRect, {\n  x: 0.7, y: 1.2, w: 3.5, h: 1.2,\n  rectRadius: 0.08,\n  fill: { color: \"FFFFFF\" },\n  line: { color: \"E5E7EB\", width: 1 },\n  shadow: { type: \"outer\", color: \"000000\", opacity: 0.12, blur: 2, angle: 45, distance: 1 },\n});\n\nslide.addShape(pptx.ShapeType.line, {\n  x: 0.7, y: 6.8, w: 11.8, h: 0,\n  line: { color: \"CBD5E1\", width: 1 },\n});\n```\n\nColor rules:\n\n- Hex colors must not include `#`.\n- Do not use 8-character hex for transparency. Use `transparency` or `opacity`.\n- Use a fresh options object for each shape; PptxGenJS mutates some option values internally.\n\n## Images and Icons\n\n```javascript\nslide.addImage({\n  data: ctx.imageData(\"photo.png\"),\n  x: 7.1, y: 1.0, w: 5.4, h: 3.4,\n  sizing: { type: \"cover\", x: 7.1, y: 1.0, w: 5.4, h: 3.4 },\n});\n\nslide.addImage({\n  data: ctx.iconSvgData(\"chart-line\", \"2563EB\"),\n  x: 0.8, y: 1.2, w: 0.35, h: 0.35,\n});\n```\n\nUse `ctx.resolveAsset()` when a PptxGenJS API needs a file path. Use `ctx.imageData()` when embedding local PNG/JPG/GIF/SVG assets. Use `ctx.iconSvgData()` for Font Awesome solid icons by names such as `check`, `chart-line`, `shield-halved`, or `circle-info`.\n\n## Tables and Charts\n\n```javascript\nslide.addTable([\n  [\"Metric\", \"Current\", \"Target\"],\n  [\"Activation\", \"42%\", \"55%\"],\n  [\"Retention\", \"68%\", \"74%\"],\n], {\n  x: 0.7, y: 1.5, w: 6.0, h: 1.2,\n  border: { pt: 1, color: \"E5E7EB\" },\n  fill: { color: \"FFFFFF\" },\n  color: \"111827\",\n  fontSize: 12,\n});\n\nslide.addChart(pptx.ChartType.bar, [{\n  name: \"Revenue\",\n  labels: [\"Q1\", \"Q2\", \"Q3\", \"Q4\"],\n  values: [12, 16, 21, 28],\n}], {\n  x: 0.7, y: 1.2, w: 6.2, h: 3.8,\n  barDir: \"col\",\n  chartColors: [\"2563EB\"],\n  showLegend: false,\n  showValue: true,\n  valGridLine: { color: \"E2E8F0\", size: 0.5 },\n});\n```\n\nPrefer a small set of chart colors from the deck palette. Hide legends when there is only one series.\n\n## Layout Ideas\n\n- Cover: full color block or image area with large title and short subtitle.\n- Agenda: strong sidebar plus numbered sections.\n- Two-column: explanatory text on one side, image/chart/diagram on the other.\n- Card grid: 2x2 or 3x2 cards with icon, header, and one-sentence body.\n- Timeline/process: numbered steps connected by lines or arrows.\n- Data slide: one large chart plus two or three stat callouts.\n- Closing: strong statement, next action, or summary trio.\n\n## QA Checklist\n\n- Slide order and text match the requested content.\n- Text does not collide with shapes, images, footers, or other text.\n- Long labels fit in their boxes and do not clip.\n- Contrast is readable on every background.\n- Margins are at least 0.5 in unless intentionally full bleed.\n- Layouts vary; the deck does not read as repeated title-plus-bullets.\n- Generated file opens and `pptx_read` returns the expected text.\n","isInternal":false,"tokens":1505,"sizeBytes":4480},{"name":"SKILL.md","path":"skills/powerpoint/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/powerpoint/SKILL.md","title":"powerpoint","category":"anthropic-skill","format":"markdown","content":"---\nname: powerpoint\ndescription: Create designed, editable PowerPoint .pptx presentations with PptxGenJS. Use when the user asks to create, generate, update, or inspect a deck, slide deck, presentation, or .pptx file.\n---\n\n# PowerPoint\n\nUse this skill whenever a PowerPoint deck is involved. For new decks, pass a trusted PptxGenJS build script directly to the `pptx_write` tool. For filling or editing an existing template, call `pptx_template_analyze` first and then `pptx_template_fill` with the exact IDs returned by analysis.\n\n## Workflow\n\n1. Decide the deck outline and choose a visual system: palette, typography, repeated motif, and slide rhythm.\n2. Write JavaScript module content that exports `default async function build(pptx, ctx)` or named `build(pptx, ctx)`.\n3. In the script, add slides directly with PptxGenJS. Do not generate HTML for this workflow.\n4. Call `pptx_write` with `path`, `script`, optional `assets_dir`, and optional `data`.\n5. Verify the result with `pptx_read`; for visual QA, convert the PPTX to images if the environment has LibreOffice and Poppler.\n\n## Template Workflow\n\n- Use `pptx_template_analyze` when the user provides a `.pptx` template or wants to preserve existing layouts, charts, images, tables, or SmartArt.\n- Build a `template_fill_pptx_plan.v1` plan from the returned slide IDs and object IDs, then call `pptx_template_fill`.\n- For SmartArt, use `smartarts[*].smartart_id` and `smartarts[*].nodes[*].node_id` in `smartart_edits`. This edits existing node text only; it does not create, delete, or relayout SmartArt nodes.\n\n## Script Creation\n\n- Put the complete JavaScript module in the `script` argument.\n- Do not use `local_file_write` or shell commands to create a temporary `.mjs` file for this workflow.\n- If revising a deck, update the `script` content and call `pptx_write` again.\n\n## Tool Contract\n\n```json\n{\n  \"tool\": \"pptx_write\",\n  \"arguments\": {\n    \"path\": \"deck.pptx\",\n    \"script\": \"export default async function build(pptx, ctx) {\\\\n  pptx.layout = \\\"LAYOUT_WIDE\\\";\\\\n}\",\n    \"assets_dir\": \"/absolute/path/to/assets\",\n    \"data\": {\"title\": \"Quarterly Review\"}\n  }\n}\n```\n\nThe worker creates the PptxGenJS instance and writes the output file. The script only adds slides and content.\n\n```javascript\nexport default async function build(pptx, ctx) {\n  pptx.layout = \"LAYOUT_WIDE\";\n  pptx.author = \"OpenAgent\";\n\n  const slide = pptx.addSlide();\n  slide.background = { color: \"FFFFFF\" };\n  slide.addText(\"Title\", {\n    x: 0.6, y: 0.4, w: 8, h: 0.6,\n    fontSize: 36, bold: true, color: \"1F2937\",\n    margin: 0,\n  });\n  slide.addNotes(\"speaker notes\");\n}\n```\n\n`ctx` includes:\n\n- `ctx.data`: JSON data passed from the tool call.\n- `ctx.assetsDir`: resolved asset directory.\n- `ctx.outPath`: final PPTX path.\n- `ctx.resolveAsset(\"image.png\")`: absolute path under `assets_dir`.\n- `ctx.imageData(\"image.png\")`: base64 image data URL.\n- `ctx.iconSvgData(\"check\", \"16A34A\")`: Font Awesome solid icon as SVG data.\n\n## Design Rules\n\n- Avoid plain white bullet decks. Every slide should have a visual element: shape, image, chart, icon, timeline, stat callout, or diagram.\n- Vary layouts across the deck: title, divider, two-column, card grid, process flow, quote/callout, and conclusion.\n- Pick topic-specific colors. Use one dominant color, one or two supporting tones, and one accent.\n- Use strong hierarchy: titles around 36-44 pt, section labels around 20-24 pt, body text around 14-18 pt.\n- Keep at least 0.5 inch margins and consistent gaps around 0.3-0.5 inch.\n- Use editable text wherever practical; use images for photos, screenshots, logos, or complex visual backgrounds.\n- Add speaker notes when useful; `pptx_read` can surface them later.\n\n## PptxGenJS Reference\n\nFor API patterns, chart examples, bullets, image sizing, icons, and common file-corruption pitfalls, load `pptxgenjs.md`.\n\n## Required QA\n\n- Run `pptx_read` on the generated file and check slide order, missing text, typo risk, and notes.\n- Inspect generated XML or render slides when visual precision matters.\n- Watch for overlap, text overflow, low contrast, cramped spacing, repeated layouts, and leftover placeholder text.\n- If a visual issue is found, edit the `.mjs` script and rewrite the PPTX.\n","frontmatter":{"name":"powerpoint","description":"Create designed, editable PowerPoint .pptx presentations with PptxGenJS. Use when the user asks to create, generate, update, or inspect a deck, slide deck, presentation, or .pptx file."},"isInternal":false,"tokens":1094,"sizeBytes":4230},{"name":"SKILL.md","path":"skills/sag/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/sag/SKILL.md","title":"sag","category":"anthropic-skill","format":"markdown","content":"---\nname: sag\ndescription: ElevenLabs text-to-speech with mac-style say UX.\nhomepage: https://sag.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔊\",\n        \"requires\": { \"bins\": [\"sag\"], \"env\": [\"ELEVENLABS_API_KEY\"] },\n        \"primaryEnv\": \"ELEVENLABS_API_KEY\",\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/sag\",\n              \"bins\": [\"sag\"],\n              \"label\": \"Install sag (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# sag\n\nUse `sag` for ElevenLabs TTS with local playback.\n\nAPI key (required)\n\n- `ELEVENLABS_API_KEY` (preferred)\n- `SAG_API_KEY` also supported by the CLI\n\nQuick start\n\n- `sag \"Hello there\"`\n- `sag speak -v \"Roger\" \"Hello\"`\n- `sag voices`\n- `sag prompting` (model-specific tips)\n\nModel notes\n\n- Default: `eleven_v3` (expressive)\n- Stable: `eleven_multilingual_v2`\n- Fast: `eleven_flash_v2_5`\n\nPronunciation + delivery rules\n\n- First fix: respell (e.g. \"key-note\"), add hyphens, adjust casing.\n- Numbers/units/URLs: `--normalize auto` (or `off` if it harms names).\n- Language bias: `--lang en|de|fr|...` to guide normalization.\n- v3: SSML `<break>` not supported; use `[pause]`, `[short pause]`, `[long pause]`.\n- v2/v2.5: SSML `<break time=\"1.5s\" />` supported; `<phoneme>` not exposed in `sag`.\n\nv3 audio tags (put at the entrance of a line)\n\n- `[whispers]`, `[shouts]`, `[sings]`\n- `[laughs]`, `[starts laughing]`, `[sighs]`, `[exhales]`\n- `[sarcastic]`, `[curious]`, `[excited]`, `[crying]`, `[mischievously]`\n- Example: `sag \"[whispers] keep this quiet. [short pause] ok?\"`\n\nVoice defaults\n\n- `ELEVENLABS_VOICE_ID` or `SAG_VOICE_ID`\n\nConfirm voice + speaker before long output.\n\n## Chat voice responses\n\nWhen the user asks for a \"voice\" reply (e.g., \"crazy scientist voice\", \"explain in voice\"), generate audio and send it:\n\n```bash\n# Generate audio file\nsag -v Clawd -o /tmp/voice-reply.mp3 \"Your message here\"\n\n# Then include in reply:\n# MEDIA:/tmp/voice-reply.mp3\n```\n\nVoice character tips:\n\n- Crazy scientist: Use `[excited]` tags, dramatic pauses `[short pause]`, vary intensity\n- Calm: Use `[whispers]` or slower pacing\n- Dramatic: Use `[sings]` or `[shouts]` sparingly\n\nDefault voice for Clawd: `lj2rcrvANS3gaWWnczSX` (or just `-v Clawd`)\n","frontmatter":{"name":"sag","description":"ElevenLabs text-to-speech with mac-style say UX.","homepage":"https://sag.sh","metadata":{"openclaw":{"emoji":"🔊","requires":{"bins":["sag"],"env":["ELEVENLABS_API_KEY"]},"primaryEnv":"ELEVENLABS_API_KEY","install":[{"id":"brew","kind":"brew","formula":"steipete/tap/sag","bins":["sag"],"label":"Install sag (brew)"}]}}},"isInternal":false,"tokens":708,"sizeBytes":2300},{"name":"SKILL.md","path":"skills/session-logs/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/session-logs/SKILL.md","title":"session-logs","category":"anthropic-skill","format":"markdown","content":"---\nname: session-logs\ndescription: Search and analyze your own session logs (older/parent conversations) using jq.\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📜\",\n        \"requires\": { \"bins\": [\"jq\", \"rg\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew-jq\",\n              \"kind\": \"brew\",\n              \"formula\": \"jq\",\n              \"bins\": [\"jq\"],\n              \"label\": \"Install jq (brew)\",\n            },\n            {\n              \"id\": \"brew-rg\",\n              \"kind\": \"brew\",\n              \"formula\": \"ripgrep\",\n              \"bins\": [\"rg\"],\n              \"label\": \"Install ripgrep (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# session-logs\n\nSearch your complete conversation history stored in session JSONL files. Use this when a user references older/parent conversations or asks what was said before.\n\n## Trigger\n\nUse this skill when the user asks about prior chats, parent conversations, or historical context that isn't in memory files.\n\n## Location\n\nSession logs live under the active state directory:\n`$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/` (default: `~/.openclaw/agents/<agentId>/sessions/`).\nUse the `agent=<id>` value from the system prompt Runtime line.\n\n- **`sessions.json`** - Index mapping session keys to session IDs\n- **`<session-id>.jsonl`** - Full conversation transcript per session\n\n## Structure\n\nEach `.jsonl` file contains messages with:\n\n- `type`: \"session\" (metadata) or \"message\"\n- `timestamp`: ISO timestamp\n- `message.role`: \"user\", \"assistant\", or \"toolResult\"\n- `message.content[]`: Text, thinking, or tool calls (filter `type==\"text\"` for human-readable content)\n- `message.usage.cost.total`: Cost per response\n\n## Common Queries\n\n### List all sessions by date and size\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nfor f in \"$SESSION_DIR\"/*.jsonl; do\n  date=$(head -1 \"$f\" | jq -r '.timestamp' | cut -dT -f1)\n  size=$(ls -lh \"$f\" | awk '{print $5}')\n  echo \"$date $size $(basename $f)\"\ndone | sort -r\n```\n\n### Find sessions from a specific day\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nfor f in \"$SESSION_DIR\"/*.jsonl; do\n  head -1 \"$f\" | jq -r '.timestamp' | grep -q \"2026-01-06\" && echo \"$f\"\ndone\n```\n\n### Extract user messages from a session\n\n```bash\njq -r 'select(.message.role == \"user\") | .message.content[]? | select(.type == \"text\") | .text' <session>.jsonl\n```\n\n### Search for keyword in assistant responses\n\n```bash\njq -r 'select(.message.role == \"assistant\") | .message.content[]? | select(.type == \"text\") | .text' <session>.jsonl | rg -i \"keyword\"\n```\n\n### Get total cost for a session\n\n```bash\njq -s '[.[] | .message.usage.cost.total // 0] | add' <session>.jsonl\n```\n\n### Daily cost summary\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nfor f in \"$SESSION_DIR\"/*.jsonl; do\n  date=$(head -1 \"$f\" | jq -r '.timestamp' | cut -dT -f1)\n  cost=$(jq -s '[.[] | .message.usage.cost.total // 0] | add' \"$f\")\n  echo \"$date $cost\"\ndone | awk '{a[$1]+=$2} END {for(d in a) print d, \"$\"a[d]}' | sort -r\n```\n\n### Count messages and tokens in a session\n\n```bash\njq -s '{\n  messages: length,\n  user: [.[] | select(.message.role == \"user\")] | length,\n  assistant: [.[] | select(.message.role == \"assistant\")] | length,\n  first: .[0].timestamp,\n  last: .[-1].timestamp\n}' <session>.jsonl\n```\n\n### Tool usage breakdown\n\n```bash\njq -r '.message.content[]? | select(.type == \"toolCall\") | .name' <session>.jsonl | sort | uniq -c | sort -rn\n```\n\n### Search across ALL sessions for a phrase\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\nrg -l \"phrase\" \"$SESSION_DIR\"/*.jsonl\n```\n\n## Tips\n\n- Sessions are append-only JSONL (one JSON object per line)\n- Large sessions can be several MB - use `head`/`tail` for sampling\n- The `sessions.json` index maps chat providers (discord, whatsapp, etc.) to session IDs\n- Deleted sessions have `.deleted.<timestamp>` suffix\n\n## Fast text-only hint (low noise)\n\n```bash\nAGENT_ID=\"<agentId>\"\nSESSION_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions\"\njq -r 'select(.type==\"message\") | .message.content[]? | select(.type==\"text\") | .text' \"$SESSION_DIR\"/<id>.jsonl | rg 'keyword'\n```\n","frontmatter":{"name":"session-logs","description":"Search and analyze your own session logs (older/parent conversations) using jq.","metadata":{"openclaw":{"emoji":"📜","requires":{"bins":["jq","rg"]},"install":[{"id":"brew-jq","kind":"brew","formula":"jq","bins":["jq"],"label":"Install jq (brew)"},{"id":"brew-rg","kind":"brew","formula":"ripgrep","bins":["rg"],"label":"Install ripgrep (brew)"}]}}},"isInternal":false,"tokens":1234,"sizeBytes":4385},{"name":"SKILL.md","path":"skills/sherpa-onnx-tts/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/sherpa-onnx-tts/SKILL.md","title":"sherpa-onnx-tts","category":"anthropic-skill","format":"markdown","content":"---\nname: sherpa-onnx-tts\ndescription: Local text-to-speech via sherpa-onnx (offline, no cloud)\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔉\",\n        \"os\": [\"darwin\", \"linux\", \"win32\"],\n        \"requires\": { \"env\": [\"SHERPA_ONNX_RUNTIME_DIR\", \"SHERPA_ONNX_MODEL_DIR\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"download-runtime-macos\",\n              \"kind\": \"download\",\n              \"os\": [\"darwin\"],\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-osx-universal2-shared.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"stripComponents\": 1,\n              \"targetDir\": \"runtime\",\n              \"label\": \"Download sherpa-onnx runtime (macOS)\",\n            },\n            {\n              \"id\": \"download-runtime-linux-x64\",\n              \"kind\": \"download\",\n              \"os\": [\"linux\"],\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-linux-x64-shared.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"stripComponents\": 1,\n              \"targetDir\": \"runtime\",\n              \"label\": \"Download sherpa-onnx runtime (Linux x64)\",\n            },\n            {\n              \"id\": \"download-runtime-win-x64\",\n              \"kind\": \"download\",\n              \"os\": [\"win32\"],\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-win-x64-shared.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"stripComponents\": 1,\n              \"targetDir\": \"runtime\",\n              \"label\": \"Download sherpa-onnx runtime (Windows x64)\",\n            },\n            {\n              \"id\": \"download-model-lessac\",\n              \"kind\": \"download\",\n              \"url\": \"https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-en_US-lessac-high.tar.bz2\",\n              \"archive\": \"tar.bz2\",\n              \"extract\": true,\n              \"targetDir\": \"models\",\n              \"label\": \"Download Piper en_US lessac (high)\",\n            },\n          ],\n      },\n  }\n---\n\n# sherpa-onnx-tts\n\nLocal TTS using the sherpa-onnx offline CLI.\n\n## Install\n\n1. Download the runtime for your OS (extracts into `$OPENCLAW_STATE_DIR/tools/sherpa-onnx-tts/runtime`, default `~/.openclaw/tools/sherpa-onnx-tts/runtime`)\n2. Download a voice model (extracts into `$OPENCLAW_STATE_DIR/tools/sherpa-onnx-tts/models`, default `~/.openclaw/tools/sherpa-onnx-tts/models`)\n\nResolve the active state directory first:\n\n```bash\nSTATE_DIR=\"${OPENCLAW_STATE_DIR:-$HOME/.openclaw}\"\n```\n\nThen write those resolved paths into the active OpenClaw config file (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`):\n\n```json5\n{\n  skills: {\n    entries: {\n      \"sherpa-onnx-tts\": {\n        env: {\n          SHERPA_ONNX_RUNTIME_DIR: \"/path/to/your/state-dir/tools/sherpa-onnx-tts/runtime\",\n          SHERPA_ONNX_MODEL_DIR: \"/path/to/your/state-dir/tools/sherpa-onnx-tts/models/vits-piper-en_US-lessac-high\",\n        },\n      },\n    },\n  },\n}\n```\n\nThe wrapper lives in this skill folder. Run it directly, or add the wrapper to PATH:\n\n```bash\nexport PATH=\"{baseDir}/bin:$PATH\"\n```\n\n## Usage\n\n```bash\n{baseDir}/bin/sherpa-onnx-tts -o ./tts.wav \"Hello from local TTS.\"\n```\n\nNotes:\n\n- Pick a different model from the sherpa-onnx `tts-models` release if you want another voice.\n- If the model dir has multiple `.onnx` files, set `SHERPA_ONNX_MODEL_FILE` or pass `--model-file`.\n- You can also pass `--tokens-file` or `--data-dir` to override the defaults.\n- Windows: run `node {baseDir}\\\\bin\\\\sherpa-onnx-tts -o tts.wav \"Hello from local TTS.\"`\n","frontmatter":{"name":"sherpa-onnx-tts","description":"Local text-to-speech via sherpa-onnx (offline, no cloud)","metadata":{"openclaw":{"emoji":"🔉","os":["darwin","linux","win32"],"requires":{"env":["SHERPA_ONNX_RUNTIME_DIR","SHERPA_ONNX_MODEL_DIR"]},"install":[{"id":"download-runtime-macos","kind":"download","os":["darwin"],"url":"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-osx-universal2-shared.tar.bz2","archive":"tar.bz2","extract":true,"stripComponents":1,"targetDir":"runtime","label":"Download sherpa-onnx runtime (macOS)"},{"id":"download-runtime-linux-x64","kind":"download","os":["linux"],"url":"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-linux-x64-shared.tar.bz2","archive":"tar.bz2","extract":true,"stripComponents":1,"targetDir":"runtime","label":"Download sherpa-onnx runtime (Linux x64)"},{"id":"download-runtime-win-x64","kind":"download","os":["win32"],"url":"https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-win-x64-shared.tar.bz2","archive":"tar.bz2","extract":true,"stripComponents":1,"targetDir":"runtime","label":"Download sherpa-onnx runtime (Windows x64)"},{"id":"download-model-lessac","kind":"download","url":"https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-en_US-lessac-high.tar.bz2","archive":"tar.bz2","extract":true,"targetDir":"models","label":"Download Piper en_US lessac (high)"}]}}},"isInternal":false,"tokens":1009,"sizeBytes":3750},{"name":"SKILL.md","path":"skills/skill-creator/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/skill-creator/SKILL.md","title":"skill-creator","category":"anthropic-skill","format":"markdown","content":"---\nname: skill-creator\ndescription: Create, edit, improve, or audit AgentSkills. Use when creating a new skill from scratch or when asked to improve, review, audit, tidy up, or clean up an existing skill or SKILL.md file. Also use when editing or restructuring a skill directory (moving files to references/ or scripts/, removing stale content, validating against the AgentSkills spec). Triggers on phrases like \"create a skill\", \"author a skill\", \"tidy up a skill\", \"improve this skill\", \"review the skill\", \"clean up the skill\", \"audit the skill\".\n---\n\n# Skill Creator\n\nThis skill provides guidance for creating effective skills.\n\n## About Skills\n\nSkills are modular, self-contained packages that extend Codex's capabilities by providing\nspecialized knowledge, workflows, and tools. Think of them as \"onboarding guides\" for specific\ndomains or tasks—they transform Codex from a general-purpose agent into a specialized agent\nequipped with procedural knowledge that no model can fully possess.\n\n### What Skills Provide\n\n1. Specialized workflows - Multi-step procedures for specific domains\n2. Tool integrations - Instructions for working with specific file formats or APIs\n3. Domain expertise - Company-specific knowledge, schemas, business logic\n4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks\n\n## Core Principles\n\n### Concise is Key\n\nThe context window is a public good. Skills share the context window with everything else Codex needs: system prompt, conversation history, other Skills' metadata, and the actual user request.\n\n**Default assumption: Codex is already very smart.** Only add context Codex doesn't already have. Challenge each piece of information: \"Does Codex really need this explanation?\" and \"Does this paragraph justify its token cost?\"\n\nPrefer concise examples over verbose explanations.\n\n### Set Appropriate Degrees of Freedom\n\nMatch the level of specificity to the task's fragility and variability:\n\n**High freedom (text-based instructions)**: Use when multiple approaches are valid, decisions depend on context, or heuristics guide the approach.\n\n**Medium freedom (pseudocode or scripts with parameters)**: Use when a preferred pattern exists, some variation is acceptable, or configuration affects behavior.\n\n**Low freedom (specific scripts, few parameters)**: Use when operations are fragile and error-prone, consistency is critical, or a specific sequence must be followed.\n\nThink of Codex as exploring a path: a narrow bridge with cliffs needs specific guardrails (low freedom), while an open field allows many routes (high freedom).\n\n### Anatomy of a Skill\n\nEvery skill consists of a required SKILL.md file and optional bundled resources:\n\n```\nskill-name/\n├── SKILL.md (required)\n│   ├── YAML frontmatter metadata (required)\n│   │   ├── name: (required)\n│   │   └── description: (required)\n│   └── Markdown instructions (required)\n└── Bundled Resources (optional)\n    ├── scripts/          - Executable code (Python/Bash/etc.)\n    ├── references/       - Documentation intended to be loaded into context as needed\n    └── assets/           - Files used in output (templates, icons, fonts, etc.)\n```\n\n#### SKILL.md (required)\n\nEvery SKILL.md consists of:\n\n- **Frontmatter** (YAML): Contains `name` and `description` fields. These are the only fields that Codex reads to determine when the skill gets used, thus it is very important to be clear and comprehensive in describing what the skill is, and when it should be used.\n- **Body** (Markdown): Instructions and guidance for using the skill. Only loaded AFTER the skill triggers (if at all).\n\n#### Bundled Resources (optional)\n\n##### Scripts (`scripts/`)\n\nExecutable code (Python/Bash/etc.) for tasks that require deterministic reliability or are repeatedly rewritten.\n\n- **When to include**: When the same code is being rewritten repeatedly or deterministic reliability is needed\n- **Example**: `scripts/rotate_pdf.py` for PDF rotation tasks\n- **Benefits**: Token efficient, deterministic, may be executed without loading into context\n- **Note**: Scripts may still need to be read by Codex for patching or environment-specific adjustments\n\n##### References (`references/`)\n\nDocumentation and reference material intended to be loaded as needed into context to inform Codex's process and thinking.\n\n- **When to include**: For documentation that Codex should reference while working\n- **Examples**: `references/finance.md` for financial schemas, `references/mnda.md` for company NDA template, `references/policies.md` for company policies, `references/api_docs.md` for API specifications\n- **Use cases**: Database schemas, API documentation, domain knowledge, company policies, detailed workflow guides\n- **Benefits**: Keeps SKILL.md lean, loaded only when Codex determines it's needed\n- **Best practice**: If files are large (>10k words), include grep search patterns in SKILL.md\n- **Avoid duplication**: Information should live in either SKILL.md or references files, not both. Prefer references files for detailed information unless it's truly core to the skill—this keeps SKILL.md lean while making information discoverable without hogging the context window. Keep only essential procedural instructions and workflow guidance in SKILL.md; move detailed reference material, schemas, and examples to references files.\n\n##### Assets (`assets/`)\n\nFiles not intended to be loaded into context, but rather used within the output Codex produces.\n\n- **When to include**: When the skill needs files that will be used in the final output\n- **Examples**: `assets/logo.png` for brand assets, `assets/slides.pptx` for PowerPoint templates, `assets/frontend-template/` for HTML/React boilerplate, `assets/font.ttf` for typography\n- **Use cases**: Templates, images, icons, boilerplate code, fonts, sample documents that get copied or modified\n- **Benefits**: Separates output resources from documentation, enables Codex to use files without loading them into context\n\n#### What to Not Include in a Skill\n\nA skill should only contain essential files that directly support its functionality. Do NOT create extraneous documentation or auxiliary files, including:\n\n- README.md\n- INSTALLATION_GUIDE.md\n- QUICK_REFERENCE.md\n- CHANGELOG.md\n- etc.\n\nThe skill should only contain the information needed for an AI agent to do the job at hand. It should not contain auxiliary context about the process that went into creating it, setup and testing procedures, user-facing documentation, etc. Creating additional documentation files just adds clutter and confusion.\n\n### Progressive Disclosure Design Principle\n\nSkills use a three-level loading system to manage context efficiently:\n\n1. **Metadata (name + description)** - Always in context (~100 words)\n2. **SKILL.md body** - When skill triggers (<5k words)\n3. **Bundled resources** - As needed by Codex (Unlimited because scripts can be executed without reading into context window)\n\n#### Progressive Disclosure Patterns\n\nKeep SKILL.md body to the essentials and under 500 lines to minimize context bloat. Split content into separate files when approaching this limit. When splitting out content into other files, it is very important to reference them from SKILL.md and describe clearly when to read them, to ensure the reader of the skill knows they exist and when to use them.\n\n**Key principle:** When a skill supports multiple variations, frameworks, or options, keep only the core workflow and selection guidance in SKILL.md. Move variant-specific details (patterns, examples, configuration) into separate reference files.\n\n**Pattern 1: High-level guide with references**\n\n```markdown\n# PDF Processing\n\n## Quick start\n\nExtract text with pdfplumber:\n[code example]\n\n## Advanced features\n\n- **Form filling**: See [FORMS.md](FORMS.md) for complete guide\n- **API reference**: See [REFERENCE.md](REFERENCE.md) for all methods\n- **Examples**: See [EXAMPLES.md](EXAMPLES.md) for common patterns\n```\n\nCodex loads FORMS.md, REFERENCE.md, or EXAMPLES.md only when needed.\n\n**Pattern 2: Domain-specific organization**\n\nFor Skills with multiple domains, organize content by domain to avoid loading irrelevant context:\n\n```\nbigquery-skill/\n├── SKILL.md (overview and navigation)\n└── reference/\n    ├── finance.md (revenue, billing metrics)\n    ├── sales.md (opportunities, pipeline)\n    ├── product.md (API usage, features)\n    └── marketing.md (campaigns, attribution)\n```\n\nWhen a user asks about sales metrics, Codex only reads sales.md.\n\nSimilarly, for skills supporting multiple frameworks or variants, organize by variant:\n\n```\ncloud-deploy/\n├── SKILL.md (workflow + provider selection)\n└── references/\n    ├── aws.md (AWS deployment patterns)\n    ├── gcp.md (GCP deployment patterns)\n    └── azure.md (Azure deployment patterns)\n```\n\nWhen the user chooses AWS, Codex only reads aws.md.\n\n**Pattern 3: Conditional details**\n\nShow basic content, link to advanced content:\n\n```markdown\n# DOCX Processing\n\n## Creating documents\n\nUse docx-js for new documents. See [DOCX-JS.md](DOCX-JS.md).\n\n## Editing documents\n\nFor simple edits, modify the XML directly.\n\n**For tracked changes**: See [REDLINING.md](REDLINING.md)\n**For OOXML details**: See [OOXML.md](OOXML.md)\n```\n\nCodex reads REDLINING.md or OOXML.md only when the user needs those features.\n\n**Important guidelines:**\n\n- **Avoid deeply nested references** - Keep references one level deep from SKILL.md. All reference files should link directly from SKILL.md.\n- **Structure longer reference files** - For files longer than 100 lines, include a table of contents at the top so Codex can see the full scope when previewing.\n\n## Skill Creation Process\n\nSkill creation involves these steps:\n\n1. Understand the skill with concrete examples\n2. Plan reusable skill contents (scripts, references, assets)\n3. Initialize the skill (run init_skill.py)\n4. Edit the skill (implement resources and write SKILL.md)\n5. Package the skill (run package_skill.py)\n6. Iterate based on real usage\n\nFollow these steps in order, skipping only if there is a clear reason why they are not applicable.\n\n### Skill Naming\n\n- Use lowercase letters, digits, and hyphens only; normalize user-provided titles to hyphen-case (e.g., \"Plan Mode\" -> `plan-mode`).\n- When generating names, generate a name under 64 characters (letters, digits, hyphens).\n- Prefer short, verb-led phrases that describe the action.\n- Namespace by tool when it improves clarity or triggering (e.g., `gh-address-comments`, `linear-address-issue`).\n- Name the skill folder exactly after the skill name.\n\n### Step 1: Understanding the Skill with Concrete Examples\n\nSkip this step only when the skill's usage patterns are already clearly understood. It remains valuable even when working with an existing skill.\n\nTo create an effective skill, clearly understand concrete examples of how the skill will be used. This understanding can come from either direct user examples or generated examples that are validated with user feedback.\n\nFor example, when building an image-editor skill, relevant questions include:\n\n- \"What functionality should the image-editor skill support? Editing, rotating, anything else?\"\n- \"Can you give some examples of how this skill would be used?\"\n- \"I can imagine users asking for things like 'Remove the red-eye from this image' or 'Rotate this image'. Are there other ways you imagine this skill being used?\"\n- \"What would a user say that should trigger this skill?\"\n\nTo avoid overwhelming users, avoid asking too many questions in a single message. Start with the most important questions and follow up as needed for better effectiveness.\n\nConclude this step when there is a clear sense of the functionality the skill should support.\n\n### Step 2: Planning the Reusable Skill Contents\n\nTo turn concrete examples into an effective skill, analyze each example by:\n\n1. Considering how to execute on the example from scratch\n2. Identifying what scripts, references, and assets would be helpful when executing these workflows repeatedly\n\nExample: When building a `pdf-editor` skill to handle queries like \"Help me rotate this PDF,\" the analysis shows:\n\n1. Rotating a PDF requires re-writing the same code each time\n2. A `scripts/rotate_pdf.py` script would be helpful to store in the skill\n\nExample: When designing a `frontend-webapp-builder` skill for queries like \"Build me a todo app\" or \"Build me a dashboard to track my steps,\" the analysis shows:\n\n1. Writing a frontend webapp requires the same boilerplate HTML/React each time\n2. An `assets/hello-world/` template containing the boilerplate HTML/React project files would be helpful to store in the skill\n\nExample: When building a `big-query` skill to handle queries like \"How many users have logged in today?\" the analysis shows:\n\n1. Querying BigQuery requires re-discovering the table schemas and relationships each time\n2. A `references/schema.md` file documenting the table schemas would be helpful to store in the skill\n\nTo establish the skill's contents, analyze each concrete example to create a list of the reusable resources to include: scripts, references, and assets.\n\n### Step 3: Initializing the Skill\n\nAt this point, it is time to actually create the skill.\n\nSkip this step only if the skill being developed already exists, and iteration or packaging is needed. In this case, continue to the next step.\n\nWhen creating a new skill from scratch, always run the `init_skill.py` script. The script conveniently generates a new template skill directory that automatically includes everything a skill requires, making the skill creation process much more efficient and reliable.\n\nUsage:\n\n```bash\nscripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples]\n```\n\nExamples:\n\n```bash\nscripts/init_skill.py my-skill --path skills/public\nscripts/init_skill.py my-skill --path skills/public --resources scripts,references\nscripts/init_skill.py my-skill --path skills/public --resources scripts --examples\n```\n\nThe script:\n\n- Creates the skill directory at the specified path\n- Generates a SKILL.md template with proper frontmatter and TODO placeholders\n- Optionally creates resource directories based on `--resources`\n- Optionally adds example files when `--examples` is set\n\nAfter initialization, customize the SKILL.md and add resources as needed. If you used `--examples`, replace or delete placeholder files.\n\n### Step 4: Edit the Skill\n\nWhen editing the (newly-generated or existing) skill, remember that the skill is being created for another instance of Codex to use. Include information that would be beneficial and non-obvious to Codex. Consider what procedural knowledge, domain-specific details, or reusable assets would help another Codex instance execute these tasks more effectively.\n\n#### Learn Proven Design Patterns\n\nConsult these helpful guides based on your skill's needs:\n\n- **Multi-step processes**: See references/workflows.md for sequential workflows and conditional logic\n- **Specific output formats or quality standards**: See references/output-patterns.md for template and example patterns\n\nThese files contain established best practices for effective skill design.\n\n#### Start with Reusable Skill Contents\n\nTo begin implementation, start with the reusable resources identified above: `scripts/`, `references/`, and `assets/` files. Note that this step may require user input. For example, when implementing a `brand-guidelines` skill, the user may need to provide brand assets or templates to store in `assets/`, or documentation to store in `references/`.\n\nAdded scripts must be tested by actually running them to ensure there are no bugs and that the output matches what is expected. If there are many similar scripts, only a representative sample needs to be tested to ensure confidence that they all work while balancing time to completion.\n\nIf you used `--examples`, delete any placeholder files that are not needed for the skill. Only create resource directories that are actually required.\n\n#### Update SKILL.md\n\n**Writing Guidelines:** Always use imperative/infinitive form.\n\n##### Frontmatter\n\nWrite the YAML frontmatter with `name` and `description`:\n\n- `name`: The skill name\n- `description`: This is the primary triggering mechanism for your skill, and helps Codex understand when to use the skill.\n  - Include both what the Skill does and specific triggers/contexts for when to use it.\n  - Include all \"when to use\" information here - Not in the body. The body is only loaded after triggering, so \"When to Use This Skill\" sections in the body are not helpful to Codex.\n  - Example description for a `docx` skill: \"Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. Use when Codex needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks\"\n\nDo not include any other fields in YAML frontmatter.\n\n##### Body\n\nWrite instructions for using the skill and its bundled resources.\n\n### Step 5: Packaging a Skill\n\nOnce development of the skill is complete, it must be packaged into a distributable .skill file that gets shared with the user. The packaging process automatically validates the skill first to ensure it meets all requirements:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder>\n```\n\nOptional output directory specification:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder> ./dist\n```\n\nThe packaging script will:\n\n1. **Validate** the skill automatically, checking:\n   - YAML frontmatter format and required fields\n   - Skill naming conventions and directory structure\n   - Description completeness and quality\n   - File organization and resource references\n\n2. **Package** the skill if validation passes, creating a .skill file named after the skill (e.g., `my-skill.skill`) that includes all files and maintains the proper directory structure for distribution. The .skill file is a zip file with a .skill extension.\n\n   Security restriction: symlinks are rejected and packaging fails when any symlink is present.\n\nIf validation fails, the script will report the errors and exit without creating a package. Fix any validation errors and run the packaging command again.\n\n### Step 6: Iterate\n\nAfter testing the skill, users may request improvements. Often this happens right after using the skill, with fresh context of how the skill performed.\n\n**Iteration workflow:**\n\n1. Use the skill on real tasks\n2. Notice struggles or inefficiencies\n3. Identify how SKILL.md or bundled resources should be updated\n4. Implement changes and test again\n","frontmatter":{"name":"skill-creator","description":"Create, edit, improve, or audit AgentSkills. Use when creating a new skill from scratch or when asked to improve, review, audit, tidy up, or clean up an existing skill or SKILL.md file. Also use when editing or restructuring a skill directory (moving files to references/ or scripts/, removing stale content, validating against the AgentSkills spec). Triggers on phrases like \"create a skill\", \"author a skill\", \"tidy up a skill\", \"improve this skill\", \"review the skill\", \"clean up the skill\", \"audit the skill\"."},"isInternal":false,"tokens":3912,"sizeBytes":18845},{"name":"SKILL.md","path":"skills/slack/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/slack/SKILL.md","title":"slack","category":"anthropic-skill","format":"markdown","content":"---\nname: slack\ndescription: Use when you need to control Slack from OpenClaw via the slack tool, including reacting to messages or pinning/unpinning items in Slack channels or DMs.\nmetadata: { \"openclaw\": { \"emoji\": \"💬\", \"requires\": { \"config\": [\"channels.slack\"] } } }\n---\n\n# Slack Actions\n\n## Overview\n\nUse `slack` to react, manage pins, send/edit/delete messages, and fetch member info. The tool uses the bot token configured for OpenClaw.\n\n## Inputs to collect\n\n- `channelId` and `messageId` (Slack message timestamp, e.g. `1712023032.1234`).\n- For reactions, an `emoji` (Unicode or `:name:`).\n- For message sends, a `to` target (`channel:<id>` or `user:<id>`) and `content`.\n\nMessage context lines include `slack message id` and `channel` fields you can reuse directly.\n\n## Actions\n\n### Action groups\n\n| Action group | Default | Notes                  |\n| ------------ | ------- | ---------------------- |\n| reactions    | enabled | React + list reactions |\n| messages     | enabled | Read/send/edit/delete  |\n| pins         | enabled | Pin/unpin/list         |\n| memberInfo   | enabled | Member info            |\n| emojiList    | enabled | Custom emoji list      |\n\n### React to a message\n\n```json\n{\n  \"action\": \"react\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\",\n  \"emoji\": \"✅\"\n}\n```\n\n### List reactions\n\n```json\n{\n  \"action\": \"reactions\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### Send a message\n\n```json\n{\n  \"action\": \"sendMessage\",\n  \"to\": \"channel:C123\",\n  \"content\": \"Hello from OpenClaw\"\n}\n```\n\n### Edit a message\n\n```json\n{\n  \"action\": \"editMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\",\n  \"content\": \"Updated text\"\n}\n```\n\n### Delete a message\n\n```json\n{\n  \"action\": \"deleteMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### Read recent messages\n\n```json\n{\n  \"action\": \"readMessages\",\n  \"channelId\": \"C123\",\n  \"limit\": 20\n}\n```\n\n### Pin a message\n\n```json\n{\n  \"action\": \"pinMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### Unpin a message\n\n```json\n{\n  \"action\": \"unpinMessage\",\n  \"channelId\": \"C123\",\n  \"messageId\": \"1712023032.1234\"\n}\n```\n\n### List pinned items\n\n```json\n{\n  \"action\": \"listPins\",\n  \"channelId\": \"C123\"\n}\n```\n\n### Member info\n\n```json\n{\n  \"action\": \"memberInfo\",\n  \"userId\": \"U123\"\n}\n```\n\n### Emoji list\n\n```json\n{\n  \"action\": \"emojiList\"\n}\n```\n\n## Ideas to try\n\n- React with ✅ to mark completed tasks.\n- Pin key decisions or weekly status updates.\n","frontmatter":{"name":"slack","description":"Use when you need to control Slack from OpenClaw via the slack tool, including reacting to messages or pinning/unpinning items in Slack channels or DMs.","metadata":{"openclaw":{"emoji":"💬","requires":{"config":["channels.slack"]}}}},"isInternal":false,"tokens":739,"sizeBytes":2501},{"name":"SKILL.md","path":"skills/songsee/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/songsee/SKILL.md","title":"songsee","category":"anthropic-skill","format":"markdown","content":"---\nname: songsee\ndescription: Generate spectrograms and feature-panel visualizations from audio with the songsee CLI.\nhomepage: https://github.com/steipete/songsee\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🌊\",\n        \"requires\": { \"bins\": [\"songsee\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/songsee\",\n              \"bins\": [\"songsee\"],\n              \"label\": \"Install songsee (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# songsee\n\nGenerate spectrograms + feature panels from audio.\n\nQuick start\n\n- Spectrogram: `songsee track.mp3`\n- Multi-panel: `songsee track.mp3 --viz spectrogram,mel,chroma,hpss,selfsim,loudness,tempogram,mfcc,flux`\n- Time slice: `songsee track.mp3 --start 12.5 --duration 8 -o slice.jpg`\n- Stdin: `cat track.mp3 | songsee - --format png -o out.png`\n\nCommon flags\n\n- `--viz` list (repeatable or comma-separated)\n- `--style` palette (classic, magma, inferno, viridis, gray)\n- `--width` / `--height` output size\n- `--window` / `--hop` FFT settings\n- `--min-freq` / `--max-freq` frequency range\n- `--start` / `--duration` time slice\n- `--format` jpg|png\n\nNotes\n\n- WAV/MP3 decode native; other formats use ffmpeg if available.\n- Multiple `--viz` renders a grid.\n","frontmatter":{"name":"songsee","description":"Generate spectrograms and feature-panel visualizations from audio with the songsee CLI.","homepage":"https://github.com/steipete/songsee","metadata":{"openclaw":{"emoji":"🌊","requires":{"bins":["songsee"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/songsee","bins":["songsee"],"label":"Install songsee (brew)"}]}}},"isInternal":false,"tokens":370,"sizeBytes":1314},{"name":"SKILL.md","path":"skills/sonoscli/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/sonoscli/SKILL.md","title":"sonoscli","category":"anthropic-skill","format":"markdown","content":"---\nname: sonoscli\ndescription: Control Sonos speakers (discover/status/play/volume/group).\nhomepage: https://sonoscli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔊\",\n        \"requires\": { \"bins\": [\"sonos\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/sonoscli/cmd/sonos@latest\",\n              \"bins\": [\"sonos\"],\n              \"label\": \"Install sonoscli (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# Sonos CLI\n\nUse `sonos` to control Sonos speakers on the local network.\n\n## Quick start\n\n- `sonos discover`\n- `sonos status --name \"Kitchen\"`\n- `sonos play|pause|stop --name \"Kitchen\"`\n- `sonos volume set 15 --name \"Kitchen\"`\n\n## Common tasks\n\n- Grouping: `sonos group status|join|unjoin|party|solo`\n- Favorites: `sonos favorites list|open`\n- Queue: `sonos queue list|play|clear`\n- Spotify search (via SMAPI): `sonos smapi search --service \"Spotify\" --category tracks \"query\"`\n\n## Notes\n\n- If SSDP fails, specify `--ip <speaker-ip>`.\n- Spotify Web API search is optional and requires `SPOTIFY_CLIENT_ID/SECRET`.\n- If there is an error, check the troubleshooting section and offer advice if there is a decent match.\n\n## Troubleshooting\n\n### `sonos discover` - `no route to host`\n\n- On error `Error: write udp4 0.0.0.0:64326->239.255.255.250:1900: sendto: no route to host (Command exited with code 1)`\n  - Note that the port number after `0.0.0.0:` is ephemeral and will change and the netmask may not match exactly either\n  - The `sendto: no route to host` should stay consistent\n- Advise the user that in `direct` (no Docker sandbox) mode on Mac OS that Settings -> Privacy & Security -> Local Network will need to be enabled for the top-level host parent process of the Gateway\n  - `node` if running via `launchd`\n  - `Terminal` if running the gateway directly in the terminal\n  - `Visual Studio Code` if running via terminal in VS Code\n- An alternative option is to use `sandbox` (docker container) with network access allowed for that sandbox\n\n### `sonos discover` - `bind: operation not permitted`\n\n- On error `Error: listen udp4 0.0.0.0:0: bind: operation not permitted`\n- Advise the user that they may be running via a Codex or other sandbox that does not permit network access (this can be replicated by running `sonos discover` within a Codex CLI session with sandbox enabled and not approving the escalation request)\n","frontmatter":{"name":"sonoscli","description":"Control Sonos speakers (discover/status/play/volume/group).","homepage":"https://sonoscli.sh","metadata":{"openclaw":{"emoji":"🔊","requires":{"bins":["sonos"]},"install":[{"id":"go","kind":"go","module":"github.com/steipete/sonoscli/cmd/sonos@latest","bins":["sonos"],"label":"Install sonoscli (go)"}]}}},"isInternal":false,"tokens":647,"sizeBytes":2455},{"name":"SKILL.md","path":"skills/spotify-player/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/spotify-player/SKILL.md","title":"spotify-player","category":"anthropic-skill","format":"markdown","content":"---\nname: spotify-player\ndescription: Terminal Spotify playback/search via spogo (preferred) or spotify_player.\nhomepage: https://www.spotify.com\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🎵\",\n        \"requires\": { \"anyBins\": [\"spogo\", \"spotify_player\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"spogo\",\n              \"tap\": \"steipete/tap\",\n              \"bins\": [\"spogo\"],\n              \"label\": \"Install spogo (brew)\",\n            },\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"spotify_player\",\n              \"bins\": [\"spotify_player\"],\n              \"label\": \"Install spotify_player (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# spogo / spotify_player\n\nUse `spogo` **(preferred)** for Spotify playback/search. Fall back to `spotify_player` if needed.\n\nRequirements\n\n- Spotify Premium account.\n- Either `spogo` or `spotify_player` installed.\n\nspogo setup\n\n- Import cookies: `spogo auth import --browser chrome`\n\nCommon CLI commands\n\n- Search: `spogo search track \"query\"`\n- Playback: `spogo play|pause|next|prev`\n- Devices: `spogo device list`, `spogo device set \"<name|id>\"`\n- Status: `spogo status`\n\nspotify_player commands (fallback)\n\n- Search: `spotify_player search \"query\"`\n- Playback: `spotify_player playback play|pause|next|previous`\n- Connect device: `spotify_player connect`\n- Like track: `spotify_player like`\n\nNotes\n\n- Config folder: `~/.config/spotify-player` (e.g., `app.toml`).\n- For Spotify Connect integration, set a user `client_id` in config.\n- TUI shortcuts are available via `?` in the app.\n","frontmatter":{"name":"spotify-player","description":"Terminal Spotify playback/search via spogo (preferred) or spotify_player.","homepage":"https://www.spotify.com","metadata":{"openclaw":{"emoji":"🎵","requires":{"anyBins":["spogo","spotify_player"]},"install":[{"id":"brew","kind":"brew","formula":"spogo","tap":"steipete/tap","bins":["spogo"],"label":"Install spogo (brew)"},{"id":"brew","kind":"brew","formula":"spotify_player","bins":["spotify_player"],"label":"Install spotify_player (brew)"}]}}},"isInternal":false,"tokens":411,"sizeBytes":1686},{"name":"SKILL.md","path":"skills/summarize/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/summarize/SKILL.md","title":"summarize","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fsummarize%2FSKILL.md -->","frontmatter":{"name":"summarize","description":"Summarize or extract text/transcripts from URLs, podcasts, and local files (great fallback for “transcribe this YouTube/video”).","homepage":"https://summarize.sh","metadata":{"openclaw":{"emoji":"🧾","requires":{"bins":["summarize"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/summarize","bins":["summarize"],"label":"Install summarize (brew)"}]}}},"isInternal":false,"tokens":620,"sizeBytes":2232},{"name":"SKILL.md","path":"skills/taskflow-inbox-triage/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/taskflow-inbox-triage/SKILL.md","title":"taskflow-inbox-triage","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftaskflow-inbox-triage%2FSKILL.md -->","frontmatter":{"name":"taskflow-inbox-triage","description":"Example TaskFlow authoring pattern for inbox triage. Use when messages need different treatment based on intent, with some routes notifying immediately, some waiting on outside answers, and others rolling into a later summary.","metadata":{"openclaw":{"emoji":"📥"}}},"isInternal":false,"tokens":684,"sizeBytes":2697},{"name":"SKILL.md","path":"skills/taskflow/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/taskflow/SKILL.md","title":"taskflow","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftaskflow%2FSKILL.md -->","frontmatter":{"name":"taskflow","description":"Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.","metadata":{"openclaw":{"emoji":"🪝"}}},"isInternal":false,"tokens":1192,"sizeBytes":5014},{"name":"SKILL.md","path":"skills/things-mac/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/things-mac/SKILL.md","title":"things-mac","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fthings-mac%2FSKILL.md -->","frontmatter":{"name":"things-mac","description":"Manage Things 3 via the `things` CLI on macOS (add/update projects+todos via URL scheme; read/search/list from the local Things database). Use when a user asks OpenClaw to add a task to Things, list inbox/today/upcoming, search tasks, or inspect projects/areas/tags.","homepage":"https://github.com/ossianhempel/things3-cli","metadata":{"openclaw":{"emoji":"✅","os":["darwin"],"requires":{"bins":["things"]},"install":[{"id":"go","kind":"go","module":"github.com/ossianhempel/things3-cli/cmd/things@latest","bins":["things"],"label":"Install things3-cli (go)"}]}}},"isInternal":false,"tokens":980,"sizeBytes":3555},{"name":"SKILL.md","path":"skills/tmux/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/tmux/SKILL.md","title":"tmux","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftmux%2FSKILL.md -->","frontmatter":{"name":"tmux","description":"Remote-control tmux sessions for interactive CLIs by sending keystrokes and scraping pane output.","metadata":{"emoji":"🧵","requires":{"bins":["tmux"]}}},"isInternal":false,"tokens":720,"sizeBytes":2661},{"name":"SKILL.md","path":"skills/trello/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/trello/SKILL.md","title":"trello","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Ftrello%2FSKILL.md -->","frontmatter":{"name":"trello","description":"Manage Trello boards, lists, and cards via the Trello REST API.","homepage":"https://developer.atlassian.com/cloud/trello/rest/","metadata":{"emoji":"📋","requires":{"bins":["jq"],"env":["TRELLO_API_KEY","TRELLO_TOKEN"]}}},"isInternal":false,"tokens":838,"sizeBytes":2669},{"name":"SKILL.md","path":"skills/video-frames/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/video-frames/SKILL.md","title":"video-frames","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fvideo-frames%2FSKILL.md -->","frontmatter":{"name":"video-frames","description":"Extract frames or short clips from videos using ffmpeg.","homepage":"https://ffmpeg.org","metadata":{"emoji":"🎬","requires":{"bins":["ffmpeg"]}}},"isInternal":false,"tokens":300,"sizeBytes":998},{"name":"SKILL.md","path":"skills/voice-call/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/voice-call/SKILL.md","title":"voice-call","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fvoice-call%2FSKILL.md -->","frontmatter":{"name":"voice-call","description":"Start voice calls via the OpenClaw voice-call plugin.","metadata":{"openclaw":{"emoji":"📞","skillKey":"voice-call","requires":{"config":["plugins.entries.voice-call.enabled"]}}}},"isInternal":false,"tokens":332,"sizeBytes":1159},{"name":"SKILL.md","path":"skills/wacli/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/wacli/SKILL.md","title":"wacli","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fwacli%2FSKILL.md -->","frontmatter":{"name":"wacli","description":"Send WhatsApp messages to other people or search/sync WhatsApp history via the wacli CLI.","homepage":"https://wacli.sh","metadata":{"emoji":"📱","requires":{"bins":["wacli"]}}},"isInternal":false,"tokens":423,"sizeBytes":1480},{"name":"SKILL.md","path":"skills/weather/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/weather/SKILL.md","title":"weather","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fweather%2FSKILL.md -->","frontmatter":{"name":"weather","description":"Get current weather and forecasts via wttr.in or Open-Meteo. Use when: user asks about weather, temperature, or forecasts for any location. NOT for: historical weather data, severe weather alerts, or detailed meteorological analysis. No API key needed.","homepage":"https://wttr.in/:help","metadata":{"emoji":"☔","requires":{"bins":["curl"]}}},"isInternal":false,"tokens":658,"sizeBytes":2283},{"name":"SKILL.md","path":"skills/xurl/SKILL.md","rawUrl":"https://raw.githubusercontent.com/the-open-agent/openagent/HEAD/skills/xurl/SKILL.md","title":"xurl","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/the-open-agent/openagent?path=skills%2Fxurl%2FSKILL.md -->","frontmatter":{"name":"xurl","description":"A CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.","metadata":{"emoji":"🐦","requires":{"bins":["xurl"]}}},"isInternal":false,"tokens":3136,"sizeBytes":13008}],"systemPromptSnippet":"<yakaai_skills repo=\"the-open-agent/openagent\">\n<!-- File: CLAUDE.md (Tokens: ~1227 | Category: claude-rule) -->\n# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Commands\n\n**Backend (Go)**\n```bash\ngo build                    # build backend binary\ngo run main.go              # run backend (port 14000)\ngo test ./...               # run all tests\ngo test ./object/... -run TestFoo  # run a single test\n```\n\n**Frontend (React + shadcn/ui, built with Vite)**\n```bash\ncd web && yarn install\nyarn start                  # dev server on port 13001, proxies /api to localhost:14000\nyarn build                  # production build (vite writes build-temp, mv.js swaps it into web/build)\nyarn typecheck              # tsc --noEmit\nyarn lint                   # eslint\n```\n\n`web-old/` holds the previous Ant Design frontend. It is kept for reference only\nwhile bugs are chased down against it — it is not built, served or linted, and it\ndoes not get new work.\n\n**Full production build** uses `build.sh` which cross-compiles for linux/amd64, linux/arm64, linux/riscv64.\n\nBackend serves the frontend as embedded static files (`embed.go`); during development the frontend proxy is configured to hit `localhost:14000`.\n\n## Architecture\n\n### Backend (Go / Beego)\n\n**Module:** `github.com/the-open-agent/openagent`\n\nThe backend is a standard [Beego](https://beego.vip/) MVC app. Every entity follows the same three-layer pattern:\n\n1. **`object/<entity>.go`** — struct definition (xorm tags for DB), and all DB access functions: `GetGlobal<Entities>`, `Get<Entity>Count`, `GetPagination<Entities>`, `Get<Entity>`, `Add<Entity>`, `Update<Entity>`, `Delete<Entity>`. The primary key is always `(Owner string, Name string)`. Use `util.GetId(owner, name)` / `util.GetOwnerAndNameFromIdWithError(id)` for composite key serialization.\n\n2. **`controllers/<entity>.go`** — Beego controller methods wired to routes. Standard set: `GetGlobal<Entities>`, `Get<Entities>`, `Get<Entity>`, `Add<Entity>`, `Update<Entity>`, `Delete<Entity>`. Use `c.IsAdmin()` / `c.IsGlobalAdmin()` / `c.RequireSignedIn()` for auth. Paginated list APIs accept `p`, `pageSize`, `field`, `value`, `sortField`, `sortOrder` query params and use `pagination.SetPaginator`.\n\n3. **`routers/router.go`** — `beego.Router` calls grouped by entity (all routes for the same entity's CRUD kept together). No alphabetical ordering within a group. New entity routes should be inserted next to their entity's existing routes (not appended to the end of the file).\n\n**DB schema** is auto-migrated via `object/adapter.go` → `createTable()` using `engine.Sync2(new(EntityStruct))`. Add new entities there.\n\n**i18n (backend):** `i18n/locales/{en,zh}/data.json` — keys are namespaced by category (e.g. `\"comment:...\"`, `\"general:...\"`). Use `c.T(\"namespace:key\")` in controllers.\n\n### Frontend (React + shadcn/ui on Tailwind, TypeScript, Vite)\n\n**Routing** lives in `web/src/App.tsx`; every file in `web/src/pages` is its own lazy chunk, loaded by name. To add a new admin page:\n1. Create `web/src/pages/<Name>Page.tsx` (default export).\n2. Add a `<Route>` in `App.tsx` pointing at `page(\"<Name>Page\")`.\n3. Add a menu entry in `getNavGroups()` (`web/src/lib/nav.ts`), in the correct group — Basic / Connectors / Admin etc.\n4. Add the nav key to `web/src/components/common/NavItemTree.tsx` so it appears in the site Navbar Items config.\n\n**Page pattern:** list pages render `CrudListPage` (`web/src/components/crud/`) with a `ColumnDef[]`; edit pages render `SimpleEditPage` with an `EditField[]`, or compose `EditPageShell` directly when the layout is custom. Both patterns are illustrated by `MessageListPage.tsx` / `MessageEditPage.tsx`.\n\n**Backend API layer:** `web/src/backend/<Entity>Backend.ts` — thin `fetch()` wrappers, one file per entity. Standard exports: `getGlobal<Entities>`, `get<Entity>`, `update<Entity>`, `add<Entity>`, `delete<Entity>`.\n\n**Per-instance config:** the backend sends the Casdoor issuer, client ID and branding in the `jsonWebConfig` cookie (set with index.html and with every `/api/get-account` response) and `web/src/Conf.ts` reads it at boot, so one build serves every instance.\n\n**i18n (frontend):** `web/src/locales/{en,zh}/data.json` namespaced by page/domain. Use `i18next.t(\"namespace:key\")`.\n\n### Key conventions\n\n- Owner is always `\"admin\"` for system-created entities.\n- Entity `Name` is a random string generated with `util.GetRandomString(n)` or a user-provided slug.\n- `CreatedTime` / `UpdatedTime` use `util.GetCurrentTimeWithMilli()` (string format).\n- Delete responses use `affected != 0` boolean pattern.\n- All API responses go through `c.ResponseOk(data)` or `c.ResponseError(msg)`.\n- The `conf/app.conf` file configures DB (`driverName`, `dataSourceName`, `dbName`) and port (`httpport = 14000`).\n\n<!-- File: skills/1password/references/cli-examples.md (Tokens: ~219 | Category: anthropic-skill) -->\n# op CLI examples (from op help)\n\n## Sign in\n\n- `op signin`\n- `op signin --account <shorthand|signin-address|account-id|user-id>`\n\n## Read\n\n- `op read op://app-prod/db/password`\n- `op read \"op://app-prod/db/one-time password?attribute=otp\"`\n- `op read \"op://app-prod/ssh key/private key?ssh-format=openssh\"`\n- `op read --out-file ./key.pem op://app-prod/server/ssh/key.pem`\n\n## Run\n\n- `export DB_PASSWORD=\"op://app-prod/db/password\"`\n- `op run --no-masking -- printenv DB_PASSWORD`\n- `op run --env-file=\"./.env\" -- printenv DB_PASSWORD`\n\n## Inject\n\n- `echo \"db_password: {{ op://app-prod/db/password }}\" | op inject`\n- `op inject -i config.yml.tpl -o config.yml`\n\n## Whoami / accounts\n\n- `op whoami`\n- `op account list`\n\n<!-- File: skills/1password/references/get-started.md (Tokens: ~241 | Category: anthropic-skill) -->\n# 1Password CLI get-started (summary)\n\n- Works on macOS, Windows, and Linux.\n  - macOS/Linux shells: bash, zsh, sh, fish.\n  - Windows shell: PowerShell.\n- Requires a 1Password subscription and the desktop app to use app integration.\n- macOS requirement: Big Sur 11.0.0 or later.\n- Linux app integration requires PolKit + an auth agent.\n- Install the CLI per the official doc for your OS.\n- Enable desktop app integration in the 1Password app:\n  - Open and unlock the app, then select your account/collection.\n  - macOS: Settings > Developer > Integrate with 1Password CLI (Touch ID optional).\n  - Windows: turn on Windows Hello, then Settings > Developer > Integrate.\n  - Linux: Settings > Security > Unlock using system authentication, then Settings > Developer > Integrate.\n- After integration, run any command to sign in (example in docs: `op vault list`).\n- If multiple accounts: use `op signin` to pick one, or `--account` / `OP_ACCOUNT`.\n- For non-integration auth, use `op account add`.\n\n<!-- File: skills/1password/SKILL.md (Tokens: ~715 | Category: anthropic-skill) -->\n---\nname: 1password\ndescription: 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.\nhomepage: https://developer.1password.com/docs/cli/get-started/\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🔐\",\n        \"requires\": { \"bins\": [\"op\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"1password-cli\",\n              \"bins\": [\"op\"],\n              \"label\": \"Install 1Password CLI (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# 1Password CLI\n\nFollow the official CLI get-started steps. Don't guess install commands.\n\n## References\n\n- `references/get-started.md` (install + app integration + sign-in flow)\n- `references/cli-examples.md` (real `op` examples)\n\n## Workflow\n\n1. Check OS + shell.\n2. Verify CLI present: `op --version`.\n3. Confirm desktop app integration is enabled (per get-started) and the app is unlocked.\n4. REQUIRED: create a fresh tmux session for all `op` commands (no direct `op` calls outside tmux).\n5. Sign in / authorize inside tmux: `op signin` (expect app prompt).\n6. Verify access inside tmux: `op whoami` (must succeed before any secret read).\n7. If multiple accounts: use `--account` or `OP_ACCOUNT`.\n\n## REQUIRED tmux session (T-Max)\n\nThe shell tool uses a fresh TTY per command. To avoid re-prompts and failures, always run `op` inside a dedicated tmux session with a fresh socket/session name.\n\nExample (see `tmux` skill for socket conventions, do not reuse old session names):\n\n```bash\nSOCKET_DIR=\"${OPENCLAW_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/openclaw-tmux-sockets}\"\nmkdir -p \"$SOCKET_DIR\"\nSOCKET=\"$SOCKET_DIR/openclaw-op.sock\"\nSESSION=\"op-auth-$(date +%Y%m%d-%H%M%S)\"\n\ntmux -S \"$SOCKET\" new -d -s \"$SESSION\" -n shell\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op signin --account my.1password.com\" Enter\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op whoami\" Enter\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- \"op vault list\" Enter\ntmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\ntmux -S \"$SOCKET\" kill-session -t \"$SESSION\"\n```\n\n## Guardrails\n\n- Never paste secrets into logs, chat, or code.\n- Prefer `op run` / `op inject` over writing secrets to disk.\n- If sign-in without app integration is needed, use `op account add`.\n- If a command returns \"account is not signed in\", re-run `op signin` inside tmux and authorize in the app.\n- Do not run `op` outside tmux; stop and ask if tmux is unavailable.\n\n<!-- File: skills/apple-notes/SKILL.md (Tokens: ~513 | Category: anthropic-skill) -->\n---\nname: apple-notes\ndescription: 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.\nhomepage: https://github.com/antoniorodr/memo\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📝\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"memo\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"antoniorodr/memo/memo\",\n              \"bins\": [\"memo\"],\n              \"label\": \"Install memo via Homebrew\",\n            },\n          ],\n      },\n  }\n---\n\n# Apple Notes CLI\n\nUse `memo notes` to manage Apple Notes directly from the terminal. Create, view, edit, delete, search, move notes between folders, and export to HTML/Markdown.\n\nSetup\n\n- Install (Homebrew): `brew tap antoniorodr/memo && brew install antoniorodr/memo/memo`\n- Manual (pip): `pip install .` (after cloning the repo)\n- macOS-only; if prompted, grant Automation access to Notes.app.\n\nView Notes\n\n- List all notes: `memo notes`\n- Filter by folder: `memo notes -f \"Folder Name\"`\n- Search notes (fuzzy): `memo notes -s \"query\"`\n\nCreate Notes\n\n- Add a new note: `memo notes -a`\n  - Opens an interactive editor to compose the note.\n- Quick add with title: `memo notes -a \"Note Title\"`\n\nEdit Notes\n\n- Edit existing note: `memo notes -e`\n  - Interactive selection of note to edit.\n\nDelete Notes\n\n- Delete a note: `memo notes -d`\n  - Interactive selection of note to delete.\n\nMove Notes\n\n- Move note to folder: `memo notes -m`\n  - Interactive selection of note and destination folder.\n\nExport Notes\n\n- Export to HTML/Markdown: `memo notes -ex`\n  - Exports selected note; uses Mistune for markdown processing.\n\nLimitations\n\n- Cannot edit notes containing images or attachments.\n- Interactive prompts may require terminal access.\n\nNotes\n\n- macOS-only.\n- Requires Apple Notes.app to be accessible.\n- For automation, grant permissions in System Settings > Privacy & Security > Automation.\n\n<!-- File: skills/apple-reminders/SKILL.md (Tokens: ~818 | Category: anthropic-skill) -->\n---\nname: apple-reminders\ndescription: Manage Apple Reminders via remindctl CLI (list, add, edit, complete, delete). Supports lists, date filters, and JSON/plain output.\nhomepage: https://github.com/steipete/remindctl\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"⏰\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"remindctl\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/remindctl\",\n              \"bins\": [\"remindctl\"],\n              \"label\": \"Install remindctl via Homebrew\",\n            },\n          ],\n      },\n  }\n---\n\n# Apple Reminders CLI (remindctl)\n\nUse `remindctl` to manage Apple Reminders directly from the terminal.\n\n## When to Use\n\n✅ **USE this skill when:**\n\n- User explicitly mentions \"reminder\" or \"Reminders app\"\n- Creating personal to-dos with due dates that sync to iOS\n- Managing Apple Reminders lists\n- User wants tasks to appear in their iPhone/iPad Reminders app\n\n## When NOT to Use\n\n❌ **DON'T use this skill when:**\n\n- Scheduling OpenClaw tasks or alerts → use `cron` tool with systemEvent instead\n- Calendar events or appointments → use Apple Calendar\n- Project/work task management → use Notion, GitHub Issues, or task queue\n- One-time notifications → use `cron` tool for timed alerts\n- User says \"remind me\" but means an OpenClaw alert → clarify first\n\n## Setup\n\n- Install: `brew install steipete/tap/remindctl`\n- macOS-only; grant Reminders permission when prompted\n- Check status: `remindctl status`\n- Request access: `remindctl authorize`\n\n## Common Commands\n\n### View Reminders\n\n```bash\nremindctl                    # Today's reminders\nremindctl today              # Today\nremindctl tomorrow           # Tomorrow\nremindctl week               # This week\nremindctl overdue            # Past due\nremindctl all                # Everything\nremindctl 2026-01-04         # Specific date\n```\n\n### Manage Lists\n\n```bash\nremindctl list               # List all lists\nremindctl list Work          # Show specific list\nremindctl list Projects --create    # Create list\nremindctl list Work --delete        # Delete list\n```\n\n### Create Reminders\n\n```bash\nremindctl add \"Buy milk\"\nremindctl add --title \"Call mom\" --list Personal --due tomorrow\nremindctl add --title \"Meeting prep\" --due \"2026-02-15 09:00\"\n```\n\n### Complete/Delete\n\n```bash\nremindctl complete 1 2 3     # Complete by ID\nremindctl delete 4A83 --force  # Delete by ID\n```\n\n### Output Formats\n\n```bash\nremindctl today --json       # JSON for scripting\nremindctl today --plain      # TSV format\nremindctl today --quiet      # Counts only\n```\n\n## Date Formats\n\nAccepted by `--due` and date filters:\n\n- `today`, `tomorrow`, `yesterday`\n- `YYYY-MM-DD`\n- `YYYY-MM-DD HH:mm`\n- ISO 8601 (`2026-01-04T12:34:56Z`)\n\n## Example: Clarifying User Intent\n\nUser: \"Remind me to check on the deploy in 2 hours\"\n\n**Ask:** \"Do you want this in Apple Reminders (syncs to your phone) or as an OpenClaw alert (I'll message you here)?\"\n\n- Apple Reminders → use this skill\n- OpenClaw alert → use `cron` tool with systemEvent\n\n<!-- File: skills/bear-notes/SKILL.md (Tokens: ~703 | Category: anthropic-skill) -->\n---\nname: bear-notes\ndescription: Create, search, and manage Bear notes via grizzly CLI.\nhomepage: https://bear.app\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🐻\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"grizzly\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/tylerwince/grizzly/cmd/grizzly@latest\",\n              \"bins\": [\"grizzly\"],\n              \"label\": \"Install grizzly (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# Bear Notes\n\nUse `grizzly` to create, read, and manage notes in Bear on macOS.\n\nRequirements\n\n- Bear app installed and running\n- For some operations (add-text, tags, open-note --selected), a Bear app token (stored in `~/.config/grizzly/token`)\n\n## Getting a Bear Token\n\nFor operations that require a token (add-text, tags, open-note --selected), you need an authentication token:\n\n1. Open Bear → Help → API Token → Copy Token\n2. Save it: `echo \"YOUR_TOKEN\" > ~/.config/grizzly/token`\n\n## Common Commands\n\nCreate a note\n\n```bash\necho \"Note content here\" | grizzly create --title \"My Note\" --tag work\ngrizzly create --title \"Quick Note\" --tag inbox < /dev/null\n```\n\nOpen/read a note by ID\n\n```bash\ngrizzly open-note --id \"NOTE_ID\" --enable-callback --json\n```\n\nAppend text to a note\n\n```bash\necho \"Additional content\" | grizzly add-text --id \"NOTE_ID\" --mode append --token-file ~/.config/grizzly/token\n```\n\nList all tags\n\n```bash\ngrizzly tags --enable-callback --json --token-file ~/.config/grizzly/token\n```\n\nSearch notes (via open-tag)\n\n```bash\ngrizzly open-tag --name \"work\" --enable-callback --json\n```\n\n## Options\n\nCommon flags:\n\n- `--dry-run` — Preview the URL without executing\n- `--print-url` — Show the x-callback-url\n- `--enable-callback` — Wait for Bear's response (needed for reading data)\n- `--json` — Output as JSON (when using callbacks)\n- `--token-file PATH` — Path to Bear API token file\n\n## Configuration\n\nGrizzly reads config from (in priority order):\n\n1. CLI flags\n2. Environment variables (`GRIZZLY_TOKEN_FILE`, `GRIZZLY_CALLBACK_URL`, `GRIZZLY_TIMEOUT`)\n3. `.grizzly.toml` in current directory\n4. `~/.config/grizzly/config.toml`\n\nExample `~/.config/grizzly/config.toml`:\n\n```toml\ntoken_file = \"~/.config/grizzly/token\"\ncallback_url = \"http://127.0.0.1:42123/success\"\ntimeout = \"5s\"\n```\n\n## Notes\n\n- Bear must be running for commands to work\n- Note IDs are Bear's internal identifiers (visible in note info or via callbacks)\n- Use `--enable-callback` when you need to read data back from Bear\n- Some operations require a valid token (add-text, tags, open-note --selected)\n\n<!-- File: skills/blogwatcher/SKILL.md (Tokens: ~399 | Category: anthropic-skill) -->\n---\nname: blogwatcher\ndescription: Monitor blogs and RSS/Atom feeds for updates using the blogwatcher CLI.\nhomepage: https://github.com/Hyaxia/blogwatcher\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📰\",\n        \"requires\": { \"bins\": [\"blogwatcher\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest\",\n              \"bins\": [\"blogwatcher\"],\n              \"label\": \"Install blogwatcher (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# blogwatcher\n\nTrack blog and RSS/Atom feed updates with the `blogwatcher` CLI.\n\nInstall\n\n- Go: `go install github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest`\n\nQuick start\n\n- `blogwatcher --help`\n\nCommon commands\n\n- Add a blog: `blogwatcher add \"My Blog\" https://example.com`\n- List blogs: `blogwatcher blogs`\n- Scan for updates: `blogwatcher scan`\n- List articles: `blogwatcher articles`\n- Mark an article read: `blogwatcher read 1`\n- Mark all articles read: `blogwatcher read-all`\n- Remove a blog: `blogwatcher remove \"My Blog\"`\n\nExample output\n\n```\n$ blogwatcher blogs\nTracked blogs (1):\n\n  xkcd\n    URL: https://xkcd.com\n```\n\n```\n$ blogwatcher scan\nScanning 1 blog(s)...\n\n  xkcd\n    Source: RSS | Found: 4 | New: 4\n\nFound 4 new article(s) total!\n```\n\nNotes\n\n- Use `blogwatcher <command> --help` to discover flags and options.\n\n<!-- File: skills/blucli/SKILL.md (Tokens: ~294 | Category: anthropic-skill) -->\n---\nname: blucli\ndescription: BluOS CLI (blu) for discovery, playback, grouping, and volume.\nhomepage: https://blucli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🫐\",\n        \"requires\": { \"bins\": [\"blu\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/blucli/cmd/blu@latest\",\n              \"bins\": [\"blu\"],\n              \"label\": \"Install blucli (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# blucli (blu)\n\nUse `blu` to control Bluesound/NAD players.\n\nQuick start\n\n- `blu devices` (pick target)\n- `blu --device <id> status`\n- `blu play|pause|stop`\n- `blu volume set 15`\n\nTarget selection (in priority order)\n\n- `--device <id|name|alias>`\n- `BLU_DEVICE`\n- config default (if set)\n\nCommon tasks\n\n- Grouping: `blu group status|add|remove`\n- TuneIn search/play: `blu tunein search \"query\"`, `blu tunein play \"query\"`\n\nPrefer `--json` for scripts. Confirm the target device before changing playback.\n\n<!-- File: skills/bluebubbles/SKILL.md (Tokens: ~848 | Category: anthropic-skill) -->\n---\nname: bluebubbles\ndescription: Use when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel=\"bluebubbles\".\nmetadata: { \"openclaw\": { \"emoji\": \"🫧\", \"requires\": { \"config\": [\"channels.bluebubbles\"] } } }\n---\n\n# BlueBubbles Actions\n\n## Overview\n\nBlueBubbles is OpenClaw’s recommended iMessage integration. Use the `message` tool with `channel: \"bluebubbles\"` to send messages and manage iMessage conversations: send texts and attachments, react (tapbacks), edit/unsend, reply in threads, and manage group participants/names/icons.\n\n## Inputs to collect\n\n- `target` (prefer `chat_guid:...`; also `+15551234567` in E.164 or `user@example.com`)\n- `message` text for send/edit/reply\n- `messageId` for react/edit/unsend/reply\n- Attachment `path` for local files, or `buffer` + `filename` for base64\n\nIf the user is vague (\"text my mom\"), ask for the recipient handle or chat guid and the exact message content.\n\n## Actions\n\n### Send a message\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"message\": \"hello from OpenClaw\"\n}\n```\n\n### React (tapback)\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"emoji\": \"❤️\"\n}\n```\n\n### Remove a reaction\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"emoji\": \"❤️\",\n  \"remove\": true\n}\n```\n\n### Edit a previously sent message\n\n```json\n{\n  \"action\": \"edit\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\",\n  \"message\": \"updated text\"\n}\n```\n\n### Unsend a message\n\n```json\n{\n  \"action\": \"unsend\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"messageId\": \"<message-guid>\"\n}\n```\n\n### Reply to a specific message\n\n```json\n{\n  \"action\": \"reply\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"replyTo\": \"<message-guid>\",\n  \"message\": \"replying to that\"\n}\n```\n\n### Send an attachment\n\n```json\n{\n  \"action\": \"sendAttachment\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"path\": \"/tmp/photo.jpg\",\n  \"caption\": \"here you go\"\n}\n```\n\n### Send with an iMessage effect\n\n```json\n{\n  \"action\": \"sendWithEffect\",\n  \"channel\": \"bluebubbles\",\n  \"target\": \"+15551234567\",\n  \"message\": \"big news\",\n  \"effect\": \"balloons\"\n}\n```\n\n## Notes\n\n- Requires gateway config `channels.bluebubbles` (serverUrl/password/webhookPath).\n- Prefer `chat_guid` targets when you have them (especially for group chats).\n- BlueBubbles supports rich actions, but some are macOS-version dependent (for example, edit may be broken on macOS 26 Tahoe).\n- The gateway may expose both short and full message ids; full ids are more durable across restarts.\n- Developer reference for the underlying plugin lives in the BlueBubbles plugin package README.\n\n## Ideas to try\n\n- React with a tapback to acknowledge a request.\n- Reply in-thread when a user references a specific message.\n- Send a file attachment with a short caption.\n\n<!-- File: skills/camsnap/SKILL.md (Tokens: ~288 | Category: anthropic-skill) -->\n---\nname: camsnap\ndescription: Capture frames or clips from RTSP/ONVIF cameras.\nhomepage: https://camsnap.ai\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📸\",\n        \"requires\": { \"bins\": [\"camsnap\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/camsnap\",\n              \"bins\": [\"camsnap\"],\n              \"label\": \"Install camsnap (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# camsnap\n\nUse `camsnap` to grab snapshots, clips, or motion events from configured cameras.\n\nSetup\n\n- Config file: `~/.config/camsnap/config.yaml`\n- Add camera: `camsnap add --name kitchen --host 192.168.0.10 --user user --pass pass`\n\nCommon commands\n\n- Discover: `camsnap discover --info`\n- Snapshot: `camsnap snap kitchen --out shot.jpg`\n- Clip: `camsnap clip kitchen --dur 5s --out clip.mp4`\n- Motion watch: `camsnap watch kitchen --threshold 0.2 --action '...'`\n- Doctor: `camsnap doctor --probe`\n\nNotes\n\n- Requires `ffmpeg` on PATH.\n- Prefer a short test capture before longer clips.\n\n<!-- File: skills/canvas/SKILL.md (Tokens: ~1490 | Category: anthropic-skill) -->\n# Canvas Skill\n\nDisplay HTML content on connected OpenClaw nodes (Mac app, iOS, Android).\n\n## Overview\n\nThe canvas tool lets you present web content on any connected node's canvas view. Great for:\n\n- Displaying games, visualizations, dashboards\n- Showing generated HTML content\n- Interactive demos\n\n## How It Works\n\n### Architecture\n\n```\n┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐\n│  Canvas Host    │────▶│   Node Bridge    │────▶│  Node App   │\n│  (HTTP Server)  │     │  (TCP Server)    │     │ (Mac/iOS/   │\n│  Port 18793     │     │  Port 18790      │     │  Android)   │\n└─────────────────┘     └──────────────────┘     └─────────────┘\n```\n\n1. **Canvas Host Server**: Serves static HTML/CSS/JS files from `canvasHost.root` directory\n2. **Node Bridge**: Communicates canvas URLs to connected nodes\n3. **Node Apps**: Render the content in a WebView\n\n### Tailscale Integration\n\nThe canvas host server binds based on `gateway.bind` setting:\n\n| Bind Mode  | Server Binds To     | Canvas URL Uses            |\n| ---------- | ------------------- | -------------------------- |\n| `loopback` | 127.0.0.1           | localhost (local only)     |\n| `lan`      | LAN interface       | LAN IP address             |\n| `tailnet`  | Tailscale interface | Tailscale hostname         |\n| `auto`     | Best available      | Tailscale > LAN > loopback |\n\n**Key insight:** The `canvasHostHostForBridge` is derived from `bridgeHost`. When bound to Tailscale, nodes receive URLs like:\n\n```\nhttp://<tailscale-hostname>:18793/__openclaw__/canvas/<file>.html\n```\n\nThis is why localhost URLs don't work - the node receives the Tailscale hostname from the bridge!\n\n## Actions\n\n| Action     | Description                          |\n| ---------- | ------------------------------------ |\n| `present`  | Show canvas with optional target URL |\n| `hide`     | Hide the canvas                      |\n| `navigate` | Navigate to a new URL                |\n| `eval`     | Execute JavaScript in the canvas     |\n| `snapshot` | Capture screenshot of canvas         |\n\n## Configuration\n\nIn the active OpenClaw config file (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`):\n\n```json\n{\n  \"canvasHost\": {\n    \"enabled\": true,\n    \"port\": 18793,\n    \"root\": \"/Users/you/clawd/canvas\",\n    \"liveReload\": true\n  },\n  \"gateway\": {\n    \"bind\": \"auto\"\n  }\n}\n```\n\n### Live Reload\n\nWhen `liveReload: true` (default), the canvas host:\n\n- Watches the root directory for changes (via chokidar)\n- Injects a WebSocket client into HTML files\n- Automatically reloads connected canvases when files change\n\nGreat for development!\n\n## Workflow\n\n### 1. Create HTML content\n\nPlace files in the canvas root directory (default `~/clawd/canvas/`):\n\n```bash\ncat > ~/clawd/canvas/my-game.html << 'HTML'\n<!DOCTYPE html>\n<html>\n<head><title>My Game</title></head>\n<body>\n  <h1>Hello Canvas!</h1>\n</body>\n</html>\nHTML\n```\n\n### 2. Find your canvas host URL\n\nCheck how your gateway is bound:\n\n```bash\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\ncat \"$CONFIG_PATH\" | jq '.gateway.bind'\n```\n\nThen construct the URL:\n\n- **loopback**: `http://127.0.0.1:18793/__openclaw__/canvas/<file>.html`\n- **lan/tailnet/auto**: `http://<hostname>:18793/__openclaw__/canvas/<file>.html`\n\nFind your Tailscale hostname:\n\n```bash\ntailscale status --json | jq -r '.Self.DNSName' | sed 's/\\.$//'\n```\n\n### 3. Find connected nodes\n\n```bash\nopenclaw nodes list\n```\n\nLook for Mac/iOS/Android nodes with canvas capability.\n\n### 4. Present content\n\n```\ncanvas action:present node:<node-id> target:<full-url>\n```\n\n**Example:**\n\n```\ncanvas action:present node:mac-63599bc4-b54d-4392-9048-b97abd58343a target:http://peters-mac-studio-1.sheep-coho.ts.net:18793/__openclaw__/canvas/snake.html\n```\n\n### 5. Navigate, snapshot, or hide\n\n```\ncanvas action:navigate node:<node-id> url:<new-url>\ncanvas action:snapshot node:<node-id>\ncanvas action:hide node:<node-id>\n```\n\n## Debugging\n\n### White screen / content not loading\n\n**Cause:** URL mismatch between server bind and node expectation.\n\n**Debug steps:**\n\n1. Check server bind: `CONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"; cat \"$CONFIG_PATH\" | jq '.gateway.bind'`\n2. Check what port canvas is on: `lsof -i :18793`\n3. Test URL directly: `curl http://<hostname>:18793/__openclaw__/canvas/<file>.html`\n\n**Solution:** Use the full hostname matching your bind mode, not localhost.\n\n### \"node required\" error\n\nAlways specify `node:<node-id>` parameter.\n\n### \"node not connected\" error\n\nNode is offline. Use `openclaw nodes list` to find online nodes.\n\n### Content not updating\n\nIf live reload isn't working:\n\n1. Check `liveReload: true` in config\n2. Ensure file is in the canvas root directory\n3. Check for watcher errors in logs\n\n## URL Path Structure\n\nThe canvas host serves from `/__openclaw__/canvas/` prefix:\n\n```\nhttp://<host>:18793/__openclaw__/canvas/index.html  → ~/clawd/canvas/index.html\nhttp://<host>:18793/__openclaw__/canvas/games/snake.html → ~/clawd/canvas/games/snake.html\n```\n\nThe `/__openclaw__/canvas/` prefix is defined by `CANVAS_HOST_PATH` constant.\n\n## Tips\n\n- Keep HTML self-contained (inline CSS/JS) for best results\n- Use the default index.html as a test page (has bridge diagnostics)\n- The canvas persists until you `hide` it or navigate away\n- Live reload makes development fast - just save and it updates!\n- A2UI JSON push is WIP - use HTML files for now\n\n<!-- File: skills/clawhub/SKILL.md (Tokens: ~442 | Category: anthropic-skill) -->\n---\nname: clawhub\ndescription: 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.\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"requires\": { \"bins\": [\"clawhub\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node\",\n              \"kind\": \"node\",\n              \"package\": \"clawhub\",\n              \"bins\": [\"clawhub\"],\n              \"label\": \"Install ClawHub CLI (npm)\",\n            },\n          ],\n      },\n  }\n---\n\n# ClawHub CLI\n\nInstall\n\n```bash\nnpm i -g clawhub\n```\n\nAuth (publish)\n\n```bash\nclawhub login\nclawhub whoami\n```\n\nSearch\n\n```bash\nclawhub search \"postgres backups\"\n```\n\nInstall\n\n```bash\nclawhub install my-skill\nclawhub install my-skill --version 1.2.3\n```\n\nUpdate (hash-based match + upgrade)\n\n```bash\nclawhub update my-skill\nclawhub update my-skill --version 1.2.3\nclawhub update --all\nclawhub update my-skill --force\nclawhub update --all --no-input --force\n```\n\nList\n\n```bash\nclawhub list\n```\n\nPublish\n\n```bash\nclawhub publish ./my-skill --slug my-skill --name \"My Skill\" --version 1.2.0 --changelog \"Fixes + docs\"\n```\n\nNotes\n\n- Default registry: https://clawhub.com (override with CLAWHUB_REGISTRY or --registry)\n- Default workdir: cwd (falls back to OpenClaw workspace); install dir: ./skills (override with --workdir / --dir / CLAWHUB_WORKDIR)\n- Update command hashes local files, resolves matching version, and upgrades to latest unless --version is set\n\n<!-- File: skills/coding-agent/SKILL.md (Tokens: ~2889 | Category: anthropic-skill) -->\n---\nname: coding-agent\ndescription: 'Delegate coding tasks to Codex, Claude Code, or Pi agents via background process. Use when: (1) building/creating new features or apps, (2) reviewing PRs (spawn in temp dir), (3) refactoring large codebases, (4) iterative coding that needs file exploration. NOT for: simple one-liner fixes (just edit), reading code (use read tool), thread-bound ACP harness requests in chat (for example spawn/run Codex or Claude Code in a Discord thread; use sessions_spawn with runtime:\"acp\"), or any work in ~/clawd workspace (never spawn agents here). Claude Code: use --print --permission-mode bypassPermissions (no PTY). Codex/Pi/OpenCode: pty:true required.'\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🧩\",\n        \"requires\": { \"anyBins\": [\"claude\", \"codex\", \"opencode\", \"pi\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node-claude\",\n              \"kind\": \"node\",\n              \"package\": \"@anthropic-ai/claude-code\",\n              \"bins\": [\"claude\"],\n              \"label\": \"Install Claude Code CLI (npm)\",\n            },\n            {\n              \"id\": \"node-codex\",\n              \"kind\": \"node\",\n              \"package\": \"@openai/codex\",\n              \"bins\": [\"codex\"],\n              \"label\": \"Install Codex CLI (npm)\",\n            },\n          ],\n      },\n  }\n---\n\n# Coding Agent (bash-first)\n\nUse **bash** (with optional background mode) for all coding agent work. Simple and effective.\n\n## ⚠️ PTY Mode: Codex/Pi/OpenCode yes, Claude Code no\n\nFor **Codex, Pi, and OpenCode**, PTY is still required (interactive terminal apps):\n\n```bash\n# ✅ Correct for Codex/Pi/OpenCode\nbash pty:true command:\"codex exec 'Your prompt'\"\n```\n\nFor **Claude Code** (`claude` CLI), use `--print --permission-mode bypassPermissions` instead.\n`--dangerously-skip-permissions` with PTY can exit after the confirmation dialog.\n`--print` mode keeps full tool access and avoids interactive confirmation:\n\n```bash\n# ✅ Correct for Claude Code (no PTY needed)\ncd /path/to/project && claude --permission-mode bypassPermissions --print 'Your task'\n\n# For background execution: use background:true on the exec tool\n\n# ❌ Wrong for Claude Code\nbash pty:true command:\"claude --dangerously-skip-permissions 'task'\"\n```\n\n### Bash Tool Parameters\n\n| Parameter    | Type    | Description                                                                 |\n| ------------ | ------- | --------------------------------------------------------------------------- |\n| `command`    | string  | The shell command to run                                                    |\n| `pty`        | boolean | **Use for coding agents!** Allocates a pseudo-terminal for interactive CLIs |\n| `workdir`    | string  | Working directory (agent sees only this folder's context)                   |\n| `background` | boolean | Run in background, returns sessionId for monitoring                         |\n| `timeout`    | number  | Timeout in seconds (kills process on expiry)                                |\n| `elevated`   | boolean | Run on host instead of sandbox (if allowed)                                 |\n\n### Process Tool Actions (for background sessions)\n\n| Action      | Description                                          |\n| ----------- | ---------------------------------------------------- |\n| `list`      | List all running/recent sessions                     |\n| `poll`      | Check if session is still running                    |\n| `log`       | Get session output (with optional offset/limit)      |\n| `write`     | Send raw data to stdin                               |\n| `submit`    | Send data + newline (like typing and pressing Enter) |\n| `send-keys` | Send key tokens or hex bytes                         |\n| `paste`     | Paste text (with optional bracketed mode)            |\n| `kill`      | Terminate the session                                |\n\n---\n\n## Quick Start: One-Shot Tasks\n\nFor quick prompts/chats, create a temp git repo and run:\n\n```bash\n# Quick chat (Codex needs a git repo!)\nSCRATCH=$(mktemp -d) && cd $SCRATCH && git init && codex exec \"Your prompt here\"\n\n# Or in a real project - with PTY!\nbash pty:true workdir:~/Projects/myproject command:\"codex exec 'Add error handling to the API calls'\"\n```\n\n**Why git init?** Codex refuses to run outside a trusted git directory. Creating a temp repo solves this for scratch work.\n\n---\n\n## The Pattern: workdir + background + pty\n\nFor longer tasks, use background mode with PTY:\n\n```bash\n# Start agent in target directory (with PTY!)\nbash pty:true workdir:~/project background:true command:\"codex exec --full-auto 'Build a snake game'\"\n# Returns sessionId for tracking\n\n# Monitor progress\nprocess action:log sessionId:XXX\n\n# Check if done\nprocess action:poll sessionId:XXX\n\n# Send input (if agent asks a question)\nprocess action:write sessionId:XXX data:\"y\"\n\n# Submit with Enter (like typing \"yes\" and pressing Enter)\nprocess action:submit sessionId:XXX data:\"yes\"\n\n# Kill if needed\nprocess action:kill sessionId:XXX\n```\n\n**Why workdir matters:** Agent wakes up in a focused directory, doesn't wander off reading unrelated files (like your soul.md 😅).\n\n---\n\n## Codex CLI\n\n**Model:** `gpt-5.2-codex` is the default (set in ~/.codex/config.toml)\n\n### Flags\n\n| Flag            | Effect                                             |\n| --------------- | -------------------------------------------------- |\n| `exec \"prompt\"` | One-shot execution, exits when done                |\n| `--full-auto`   | Sandboxed but auto-approves in workspace           |\n| `--yolo`        | NO sandbox, NO approvals (fastest, most dangerous) |\n\n### Building/Creating\n\n```bash\n# Quick one-shot (auto-approves) - remember PTY!\nbash pty:true workdir:~/project command:\"codex exec --full-auto 'Build a dark mode toggle'\"\n\n# Background for longer work\nbash pty:true workdir:~/project background:true command:\"codex --yolo 'Refactor the auth module'\"\n```\n\n### Reviewing PRs\n\n**⚠️ CRITICAL: Never review PRs in OpenClaw's own project folder!**\nClone to temp folder or use git worktree.\n\n```bash\n# Clone to temp for safe review\nREVIEW_DIR=$(mktemp -d)\ngit clone https://github.com/user/repo.git $REVIEW_DIR\ncd $REVIEW_DIR && gh pr checkout 130\nbash pty:true workdir:$REVIEW_DIR command:\"codex review --base origin/main\"\n# Clean up after: trash $REVIEW_DIR\n\n# Or use git worktree (keeps main intact)\ngit worktree add /tmp/pr-130-review pr-130-branch\nbash pty:true workdir:/tmp/pr-130-review command:\"codex review --base main\"\n```\n\n### Batch PR Reviews (parallel army!)\n\n```bash\n# Fetch all PR refs first\ngit fetch origin '+refs/pull/*/head:refs/remotes/origin/pr/*'\n\n# Deploy the army - one Codex per PR (all with PTY!)\nbash pty:true workdir:~/project background:true command:\"codex exec 'Review PR #86. git diff origin/main...origin/pr/86'\"\nbash pty:true workdir:~/project background:true command:\"codex exec 'Review PR #87. git diff origin/main...origin/pr/87'\"\n\n# Monitor all\nprocess action:list\n\n# Post results to GitHub\ngh pr comment <PR#> --body \"<review content>\"\n```\n\n---\n\n## Claude Code\n\n```bash\n# Foreground\nbash workdir:~/project command:\"claude --permission-mode bypassPermissions --print 'Your task'\"\n\n# Background\nbash workdir:~/project background:true command:\"claude --permission-mode bypassPermissions --print 'Your task'\"\n```\n\n---\n\n## OpenCode\n\n```bash\nbash pty:true workdir:~/project command:\"opencode run 'Your task'\"\n```\n\n---\n\n## Pi Coding Agent\n\n```bash\n# Install: npm install -g @mariozechner/pi-coding-agent\nbash pty:true workdir:~/project command:\"pi 'Your task'\"\n\n# Non-interactive mode (PTY still recommended)\nbash pty:true command:\"pi -p 'Summarize src/'\"\n\n# Different provider/model\nbash pty:true command:\"pi --provider openai --model gpt-4o-mini -p 'Your task'\"\n```\n\n**Note:** Pi now has Anthropic prompt caching enabled (PR #584, merged Jan 2026)!\n\n---\n\n## Parallel Issue Fixing with git worktrees\n\nFor fixing multiple issues in parallel, use git worktrees:\n\n```bash\n# 1. Create worktrees for each issue\ngit worktree add -b fix/issue-78 /tmp/issue-78 main\ngit worktree add -b fix/issue-99 /tmp/issue-99 main\n\n# 2. Launch Codex in each (background + PTY!)\nbash pty:true workdir:/tmp/issue-78 background:true command:\"pnpm install && codex --yolo 'Fix issue #78: <description>. Commit and push.'\"\nbash pty:true workdir:/tmp/issue-99 background:true command:\"pnpm install && codex --yolo 'Fix issue #99 from the approved ticket summary. Implement only the in-scope edits and commit after review.'\"\n\n# 3. Monitor progress\nprocess action:list\nprocess action:log sessionId:XXX\n\n# 4. Create PRs after fixes\ncd /tmp/issue-78 && git push -u origin fix/issue-78\ngh pr create --repo user/repo --head fix/issue-78 --title \"fix: ...\" --body \"...\"\n\n# 5. Cleanup\ngit worktree remove /tmp/issue-78\ngit worktree remove /tmp/issue-99\n```\n\n---\n\n## ⚠️ Rules\n\n1. **Use the right execution mode per agent**:\n   - Codex/Pi/OpenCode: `pty:true`\n   - Claude Code: `--print --permission-mode bypassPermissions` (no PTY required)\n2. **Respect tool choice** - if user asks for Codex, use Codex.\n   - Orchestrator mode: do NOT hand-code patches yourself.\n   - If an agent fails/hangs, respawn it or ask the user for direction, but don't silently take over.\n3. **Be patient** - don't kill sessions because they're \"slow\"\n4. **Monitor with process:log** - check progress without interfering\n5. **--full-auto for building** - auto-approves changes\n6. **vanilla for reviewing** - no special flags needed\n7. **Parallel is OK** - run many Codex processes at once for batch work\n8. **NEVER start Codex inside your OpenClaw state directory** (`$OPENCLAW_STATE_DIR`, default `~/.openclaw`) - it'll read your soul docs and get weird ideas about the org chart!\n9. **NEVER checkout branches in ~/Projects/openclaw/** - that's the LIVE OpenClaw instance!\n\n---\n\n## Progress Updates (Critical)\n\nWhen you spawn coding agents in the background, keep the user in the loop.\n\n- Send 1 short message when you start (what's running + where).\n- Then only update again when something changes:\n  - a milestone completes (build finished, tests passed)\n  - the agent asks a question / needs input\n  - you hit an error or need user action\n  - the agent finishes (include what changed + where)\n- If you kill a session, immediately say you killed it and why.\n\nThis prevents the user from seeing only \"Agent failed before reply\" and having no idea what happened.\n\n---\n\n## Auto-Notify on Completion\n\nFor long-running background tasks, append a wake trigger to your prompt so OpenClaw gets notified immediately when the agent finishes (instead of waiting for the next heartbeat):\n\n```\n... your task here.\n\nWhen completely finished, run this command to notify me:\nopenclaw system event --text \"Done: [brief summary of what was built]\" --mode now\n```\n\n**Example:**\n\n```bash\nbash pty:true workdir:~/project background:true command:\"codex --yolo exec 'Build a REST API for todos.\n\nWhen completely finished, run: openclaw system event --text \\\"Done: Built todos REST API with CRUD endpoints\\\" --mode now'\"\n```\n\nThis triggers an immediate wake event — Skippy gets pinged in seconds, not 10 minutes.\n\n---\n\n## Learnings (Jan 2026)\n\n- **PTY is essential:** Coding agents are interactive terminal apps. Without `pty:true`, output breaks or agent hangs.\n- **Git repo required:** Codex won't run outside a git directory. Use `mktemp -d && git init` for scratch work.\n- **exec is your friend:** `codex exec \"prompt\"` runs and exits cleanly - perfect for one-shots.\n- **submit vs write:** Use `submit` to send input + Enter, `write` for raw data without newline.\n- **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\"_ 🦞\n\n<!-- File: skills/discord/SKILL.md (Tokens: ~1037 | Category: anthropic-skill) -->\n---\nname: discord\ndescription: \"Discord ops via the message tool (channel=discord).\"\nmetadata: { \"openclaw\": { \"emoji\": \"🎮\", \"requires\": { \"config\": [\"channels.discord.token\"] } } }\nallowed-tools: [\"message\"]\n---\n\n# Discord (Via `message`)\n\nUse the `message` tool. No provider-specific `discord` tool exposed to the agent.\n\n## Musts\n\n- Always: `channel: \"discord\"`.\n- Respect gating: `channels.discord.actions.*` (some default off: `roles`, `moderation`, `presence`, `channels`).\n- Prefer explicit ids: `guildId`, `channelId`, `messageId`, `userId`.\n- Multi-account: optional `accountId`.\n\n## Guidelines\n\n- Avoid Markdown tables in outbound Discord messages.\n- Mention users as `<@USER_ID>`.\n- Prefer Discord components v2 (`components`) for rich UI; use legacy `embeds` only when you must.\n\n## Targets\n\n- Send-like actions: `to: \"channel:<id>\"` or `to: \"user:<id>\"`.\n- Message-specific actions: `channelId: \"<id>\"` (or `to`) + `messageId: \"<id>\"`.\n\n## Common Actions (Examples)\n\nSend message:\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"hello\",\n  \"silent\": true\n}\n```\n\nSend with media:\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"see attachment\",\n  \"media\": \"file:///tmp/example.png\"\n}\n```\n\n- Optional `silent: true` to suppress Discord notifications.\n\nSend with components v2 (recommended for rich UI):\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"Status update\",\n  \"components\": \"[Carbon v2 components]\"\n}\n```\n\n- `components` expects Carbon component instances (Container, TextDisplay, etc.) from JS/TS integrations.\n- Do not combine `components` with `embeds` (Discord rejects v2 + embeds).\n\nLegacy embeds (not recommended):\n\n```json\n{\n  \"action\": \"send\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"message\": \"Status update\",\n  \"embeds\": [{ \"title\": \"Legacy\", \"description\": \"Embeds are legacy.\" }]\n}\n```\n\n- `embeds` are ignored when components v2 are present.\n\nReact:\n\n```json\n{\n  \"action\": \"react\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"emoji\": \"✅\"\n}\n```\n\nRead:\n\n```json\n{\n  \"action\": \"read\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"limit\": 20\n}\n```\n\nEdit / delete:\n\n```json\n{\n  \"action\": \"edit\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"message\": \"fixed typo\"\n}\n```\n\n```json\n{\n  \"action\": \"delete\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\"\n}\n```\n\nPoll:\n\n```json\n{\n  \"action\": \"poll\",\n  \"channel\": \"discord\",\n  \"to\": \"channel:123\",\n  \"pollQuestion\": \"Lunch?\",\n  \"pollOption\": [\"Pizza\", \"Sushi\", \"Salad\"],\n  \"pollMulti\": false,\n  \"pollDurationHours\": 24\n}\n```\n\nPins:\n\n```json\n{\n  \"action\": \"pin\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\"\n}\n```\n\nThreads:\n\n```json\n{\n  \"action\": \"thread-create\",\n  \"channel\": \"discord\",\n  \"channelId\": \"123\",\n  \"messageId\": \"456\",\n  \"threadName\": \"bug triage\"\n}\n```\n\nSearch:\n\n```json\n{\n  \"action\": \"search\",\n  \"channel\": \"discord\",\n  \"guildId\": \"999\",\n  \"query\": \"release notes\",\n  \"channelIds\": [\"123\", \"456\"],\n  \"limit\": 10\n}\n```\n\nPresence (often gated):\n\n```json\n{\n  \"action\": \"set-presence\",\n  \"channel\": \"discord\",\n  \"activityType\": \"playing\",\n  \"activityName\": \"with fire\",\n  \"status\": \"online\"\n}\n```\n\n## Writing Style (Discord)\n\n- Short, conversational, low ceremony.\n- No markdown tables.\n- Mention users as `<@USER_ID>`.\n\n<!-- File: skills/eightctl/SKILL.md (Tokens: ~286 | Category: anthropic-skill) -->\n---\nname: eightctl\ndescription: Control Eight Sleep pods (status, temperature, alarms, schedules).\nhomepage: https://eightctl.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🛌\",\n        \"requires\": { \"bins\": [\"eightctl\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/eightctl/cmd/eightctl@latest\",\n              \"bins\": [\"eightctl\"],\n              \"label\": \"Install eightctl (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# eightctl\n\nUse `eightctl` for Eight Sleep pod control. Requires auth.\n\nAuth\n\n- Config: `~/.config/eightctl/config.yaml`\n- Env: `EIGHTCTL_EMAIL`, `EIGHTCTL_PASSWORD`\n\nQuick start\n\n- `eightctl status`\n- `eightctl on|off`\n- `eightctl temp 20`\n\nCommon tasks\n\n- Alarms: `eightctl alarm list|create|dismiss`\n- Schedules: `eightctl schedule list|create|update`\n- Audio: `eightctl audio state|play|pause`\n- Base: `eightctl base info|angle`\n\nNotes\n\n- API is unofficial and rate-limited; avoid repeated logins.\n- Confirm before changing temperature or alarms.\n\n<!-- File: skills/gemini/SKILL.md (Tokens: ~170 | Category: anthropic-skill) -->\n---\nname: gemini\ndescription: Gemini CLI for one-shot Q&A, summaries, and generation.\nhomepage: https://ai.google.dev/\nmetadata:\n  {\n    \"emoji\": \"✨\",\n    \"requires\": { \"bins\": [\"gemini\"] }\n  }\n---\n\n# Gemini CLI\n\nUse Gemini in one-shot mode with a positional prompt (avoid interactive mode).\n\nQuick start\n\n- `gemini \"Answer this question...\"`\n- `gemini --model <name> \"Prompt...\"`\n- `gemini --output-format json \"Return JSON\"`\n\nExtensions\n\n- List: `gemini --list-extensions`\n- Manage: `gemini extensions <command>`\n\nNotes\n\n- If auth is required, run `gemini` once interactively and follow the login flow.\n- Avoid `--yolo` for safety.\n\n<!-- File: skills/gh-issues/SKILL.md (Tokens: ~8872 | Category: anthropic-skill) -->\n---\nname: gh-issues\ndescription: \"Fetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]\"\nuser-invocable: true\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"requires\": { \"bins\": [\"curl\", \"git\", \"gh\"] },\n        \"primaryEnv\": \"GH_TOKEN\",\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"gh\",\n              \"bins\": [\"gh\"],\n              \"label\": \"Install GitHub CLI (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# gh-issues — Auto-fix GitHub Issues with Parallel Sub-agents\n\nYou are an orchestrator. Follow these 6 phases exactly. Do not skip phases.\n\nIMPORTANT — No `gh` CLI dependency. This skill uses curl + the GitHub REST API exclusively. The GH_TOKEN env var is already injected by OpenClaw. Pass it as a Bearer token in all API calls:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" ...\n```\n\n---\n\n## Phase 1 — Parse Arguments\n\nParse the arguments string provided after /gh-issues.\n\nPositional:\n\n- owner/repo — optional. This is the source repo to fetch issues from. If omitted, detect from the current git remote:\n  `git remote get-url origin`\n  Extract owner/repo from the URL (handles both HTTPS and SSH formats).\n  - HTTPS: https://github.com/owner/repo.git → owner/repo\n  - SSH: git@github.com:owner/repo.git → owner/repo\n    If not in a git repo or no remote found, stop with an error asking the user to specify owner/repo.\n\nFlags (all optional):\n| Flag | Default | Description |\n|------|---------|-------------|\n| --label | _(none)_ | Filter by label (e.g. bug, `enhancement`) |\n| --limit | 10 | Max issues to fetch per poll |\n| --milestone | _(none)_ | Filter by milestone title |\n| --assignee | _(none)_ | Filter by assignee (`@me` for self) |\n| --state | open | Issue state: open, closed, all |\n| --fork | _(none)_ | Your fork (`user/repo`) to push branches and open PRs from. Issues are fetched from the source repo; code is pushed to the fork; PRs are opened from the fork to the source repo. |\n| --watch | false | Keep polling for new issues and PR reviews after each batch |\n| --interval | 5 | Minutes between polls (only with `--watch`) |\n| --dry-run | false | Fetch and display only — no sub-agents |\n| --yes | false | Skip confirmation and auto-process all filtered issues |\n| --reviews-only | false | Skip issue processing (Phases 2-5). Only run Phase 6 — check open PRs for review comments and address them. |\n| --cron | false | Cron-safe mode: fetch issues and spawn sub-agents, exit without waiting for results. |\n| --model | _(none)_ | Model to use for sub-agents (e.g. `glm-5`, `zai/glm-5`). If not specified, uses the agent's default model. |\n| --notify-channel | _(none)_ | Telegram channel ID to send final PR summary to (e.g. -1002381931352). Only the final result with PR links is sent, not status updates. |\n\nStore parsed values for use in subsequent phases.\n\nDerived values:\n\n- SOURCE_REPO = the positional owner/repo (where issues live)\n- PUSH_REPO = --fork value if provided, otherwise same as SOURCE_REPO\n- FORK_MODE = true if --fork was provided, false otherwise\n\n**If `--reviews-only` is set:** Skip directly to Phase 6. Run token resolution (from Phase 2) first, then jump to Phase 6.\n\n**If `--cron` is set:**\n\n- Force `--yes` (skip confirmation)\n- If `--reviews-only` is also set, run token resolution then jump to Phase 6 (cron review mode)\n- Otherwise, proceed normally through Phases 2-5 with cron-mode behavior active\n\n---\n\n## Phase 2 — Fetch Issues\n\n**Token Resolution:**\nFirst, ensure GH_TOKEN is available. Check environment:\n\n```\necho $GH_TOKEN\n```\n\nIf empty, read from config:\n\n```\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\ncat \"$CONFIG_PATH\" | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty'\n```\n\nIf still empty, check `/data/.clawdbot/openclaw.json`:\n\n```\ncat /data/.clawdbot/openclaw.json | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty'\n```\n\nExport as GH_TOKEN for subsequent commands:\n\n```\nexport GH_TOKEN=\"<token>\"\n```\n\nBuild and run a curl request to the GitHub Issues API via exec:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/issues?per_page={limit}&state={state}&{query_params}\"\n```\n\nWhere {query_params} is built from:\n\n- labels={label} if --label was provided\n- milestone={milestone} if --milestone was provided (note: API expects milestone _number_, so if user provides a title, first resolve it via GET /repos/{SOURCE_REPO}/milestones and match by title)\n- assignee={assignee} if --assignee was provided (if @me, first resolve your username via `GET /user`)\n\nIMPORTANT: The GitHub Issues API also returns pull requests. Filter them out — exclude any item where pull_request key exists in the response object.\n\nIf in watch mode: Also filter out any issue numbers already in the PROCESSED_ISSUES set from previous batches.\n\nError handling:\n\n- If curl returns an HTTP 401 or 403 → stop and tell the user:\n  > \"GitHub authentication failed. Please check your apiKey in the OpenClaw dashboard or in the active OpenClaw config path (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`) under `skills.entries.gh-issues`.\"\n- If the response is an empty array (after filtering) → report \"No issues found matching filters\" and stop (or loop back if in watch mode).\n- If curl fails or returns any other error → report the error verbatim and stop.\n\nParse the JSON response. For each issue, extract: number, title, body, labels (array of label names), assignees, html_url.\n\n---\n\n## Phase 3 — Present & Confirm\n\nDisplay a markdown table of fetched issues:\n\n| #   | Title                         | Labels        |\n| --- | ----------------------------- | ------------- |\n| 42  | Fix null pointer in parser    | bug, critical |\n| 37  | Add retry logic for API calls | enhancement   |\n\nIf FORK_MODE is active, also display:\n\n> \"Fork mode: branches will be pushed to {PUSH_REPO}, PRs will target `{SOURCE_REPO}`\"\n\nIf `--dry-run` is active:\n\n- Display the table and stop. Do not proceed to Phase 4.\n\nIf `--yes` is active:\n\n- Display the table for visibility\n- Auto-process ALL listed issues without asking for confirmation\n- Proceed directly to Phase 4\n\nOtherwise:\nAsk the user to confirm which issues to process:\n\n- \"all\" — process every listed issue\n- Comma-separated numbers (e.g. `42, 37`) — process only those\n- \"cancel\" — abort entirely\n\nWait for user response before proceeding.\n\nWatch mode note: On the first poll, always confirm with the user (unless --yes is set). On subsequent polls, auto-process all new issues without re-confirming (the user already opted in). Still display the table so they can see what's being processed.\n\n---\n\n## Phase 4 — Pre-flight Checks\n\nRun these checks sequentially via exec:\n\n1. **Dirty working tree check:**\n\n   ```\n   git status --porcelain\n   ```\n\n   If output is non-empty, warn the user:\n\n   > \"Working tree has uncommitted changes. Sub-agents will create branches from HEAD — uncommitted changes will NOT be included. Continue?\"\n   > Wait for confirmation. If declined, stop.\n\n2. **Record base branch:**\n\n   ```\n   git rev-parse --abbrev-ref HEAD\n   ```\n\n   Store as BASE_BRANCH.\n\n3. **Verify remote access:**\n   If FORK_MODE:\n   - Verify the fork remote exists. Check if a git remote named `fork` exists:\n     ```\n     git remote get-url fork\n     ```\n     If it doesn't exist, add it:\n     ```\n     git remote add fork https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\n     ```\n   - Also verify origin (the source repo) is reachable:\n     ```\n     git ls-remote --exit-code origin HEAD\n     ```\n\n   If not FORK_MODE:\n\n   ```\n   git ls-remote --exit-code origin HEAD\n   ```\n\n   If this fails, stop with: \"Cannot reach remote origin. Check your network and git config.\"\n\n4. **Verify GH_TOKEN validity:**\n\n   ```\n   curl -s -o /dev/null -w \"%{http_code}\" -H \"Authorization: Bearer $GH_TOKEN\" https://api.github.com/user\n   ```\n\n   If HTTP status is not 200, stop with:\n\n   > \"GitHub authentication failed. Please check your apiKey in the OpenClaw dashboard or in the active OpenClaw config path (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`) under `skills.entries.gh-issues`.\"\n\n5. **Check for existing PRs:**\n   For each confirmed issue number N, run:\n\n   ```\n   curl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n     \"https://api.github.com/repos/{SOURCE_REPO}/pulls?head={PUSH_REPO_OWNER}:fix/issue-{N}&state=open&per_page=1\"\n   ```\n\n   (Where PUSH_REPO_OWNER is the owner portion of `PUSH_REPO`)\n   If the response array is non-empty, remove that issue from the processing list and report:\n\n   > \"Skipping #{N} — PR already exists: {html_url}\"\n\n   If all issues are skipped, report and stop (or loop back if in watch mode).\n\n6. **Check for in-progress branches (no PR yet = sub-agent still working):**\n   For each remaining issue number N (not already skipped by the PR check above), check if a `fix/issue-{N}` branch exists on the **push repo** (which may be a fork, not origin):\n\n   ```\n   curl -s -o /dev/null -w \"%{http_code}\" \\\n     -H \"Authorization: Bearer $GH_TOKEN\" \\\n     \"https://api.github.com/repos/{PUSH_REPO}/branches/fix/issue-{N}\"\n   ```\n\n   If HTTP 200 → the branch exists on the push repo but no open PR was found for it in step 5. Skip that issue:\n\n   > \"Skipping #{N} — branch fix/issue-{N} exists on {PUSH_REPO}, fix likely in progress\"\n\n   This check uses the GitHub API instead of `git ls-remote` so it works correctly in fork mode (where branches are pushed to the fork, not origin).\n\n   If all issues are skipped after this check, report and stop (or loop back if in watch mode).\n\n7. **Check claim-based in-progress tracking:**\n   This prevents duplicate processing when a sub-agent from a previous cron run is still working but hasn't pushed a branch or opened a PR yet.\n\n   Read the claims file (create empty `{}` if missing):\n\n   ```\n   CLAIMS_FILE=\"/data/.clawdbot/gh-issues-claims.json\"\n   if [ ! -f \"$CLAIMS_FILE\" ]; then\n     mkdir -p /data/.clawdbot\n     echo '{}' > \"$CLAIMS_FILE\"\n   fi\n   ```\n\n   Parse the claims file. For each entry, check if the claim timestamp is older than 2 hours. If so, remove it (expired — the sub-agent likely finished or failed silently). Write back the cleaned file:\n\n   ```\n   CLAIMS=$(cat \"$CLAIMS_FILE\")\n   CUTOFF=$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-2H +%Y-%m-%dT%H:%M:%SZ)\n   CLAIMS=$(echo \"$CLAIMS\" | jq --arg cutoff \"$CUTOFF\" 'to_entries | map(select(.value > $cutoff)) | from_entries')\n   echo \"$CLAIMS\" > \"$CLAIMS_FILE\"\n   ```\n\n   For each remaining issue number N (not already skipped by steps 5 or 6), check if `{SOURCE_REPO}#{N}` exists as a key in the claims file.\n\n   If claimed and not expired → skip:\n\n   > \"Skipping #{N} — sub-agent claimed this issue {minutes}m ago, still within timeout window\"\n\n   Where `{minutes}` is calculated from the claim timestamp to now.\n\n   If all issues are skipped after this check, report and stop (or loop back if in watch mode).\n\n---\n\n## Phase 5 — Spawn Sub-agents (Parallel)\n\n**Cron mode (`--cron` is active):**\n\n- **Sequential cursor tracking:** Use a cursor file to track which issue to process next:\n\n  ```\n  CURSOR_FILE=\"/data/.clawdbot/gh-issues-cursor-{SOURCE_REPO_SLUG}.json\"\n  # SOURCE_REPO_SLUG = owner-repo with slashes replaced by hyphens (e.g., openclaw-openclaw)\n  ```\n\n  Read the cursor file (create if missing):\n\n  ```\n  if [ ! -f \"$CURSOR_FILE\" ]; then\n    echo '{\"last_processed\": null, \"in_progress\": null}' > \"$CURSOR_FILE\"\n  fi\n  ```\n\n  - `last_processed`: issue number of the last completed issue (or null if none)\n  - `in_progress`: issue number currently being processed (or null)\n\n- **Select next issue:** Filter the fetched issues list to find the first issue where:\n  - Issue number > last_processed (if last_processed is set)\n  - AND issue is not in the claims file (not already in progress)\n  - AND no PR exists for the issue (checked in Phase 4 step 5)\n  - AND no branch exists on the push repo (checked in Phase 4 step 6)\n- If no eligible issue is found after the last_processed cursor, wrap around to the beginning (start from the oldest eligible issue).\n\n- If an eligible issue is found:\n  1. Mark it as in_progress in the cursor file\n  2. Spawn a single sub-agent for that one issue with `cleanup: \"keep\"` and `runTimeoutSeconds: 3600`\n  3. If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n  4. If `--notify-channel` was provided, include the channel in the task so the sub-agent can notify\n  5. Do NOT await the sub-agent result — fire and forget\n  6. **Write claim:** After spawning, read the claims file, add `{SOURCE_REPO}#{N}` with the current ISO timestamp, and write it back\n  7. Immediately report: \"Spawned fix agent for #{N} — will create PR when complete\"\n  8. Exit the skill. Do not proceed to Results Collection or Phase 6.\n\n- If no eligible issue is found (all issues either have PRs, have branches, or are in progress), report \"No eligible issues to process — all issues have PRs/branches or are in progress\" and exit.\n\n**Normal mode (`--cron` is NOT active):**\nFor each confirmed issue, spawn a sub-agent using sessions_spawn. Launch up to 8 concurrently (matching `subagents.maxConcurrent: 8`). If more than 8 issues, batch them — launch the next agent as each completes.\n\n**Write claims:** After spawning each sub-agent, read the claims file, add `{SOURCE_REPO}#{N}` with the current ISO timestamp, and write it back (same procedure as cron mode above). This covers interactive usage where watch mode might overlap with cron runs.\n\n### Sub-agent Task Prompt\n\nFor each issue, construct the following prompt and pass it to sessions_spawn. Variables to inject into the template:\n\n- {SOURCE_REPO} — upstream repo where the issue lives\n- {PUSH_REPO} — repo to push branches to (same as SOURCE_REPO unless fork mode)\n- {FORK_MODE} — true/false\n- {PUSH_REMOTE} — `fork` if FORK_MODE, otherwise `origin`\n- {number}, {title}, {url}, {labels}, {body} — from the issue\n- {BASE_BRANCH} — from Phase 4\n- {notify_channel} — Telegram channel ID for notifications (empty if not set). Replace {notify_channel} in the template below with the value of `--notify-channel` flag (or leave as empty string if not provided).\n\nWhen constructing the task, replace all template variables including {notify_channel} with actual values.\n\n```\nYou are a focused code-fix agent. Your task is to fix a single GitHub issue and open a PR.\n\nIMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.\n\nFirst, ensure GH_TOKEN is set. Check: `echo $GH_TOKEN`. If empty, read from config:\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nGH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty')\n\nUse the token in all GitHub API calls:\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" ...\n\n<config>\nSource repo (issues): {SOURCE_REPO}\nPush repo (branches + PRs): {PUSH_REPO}\nFork mode: {FORK_MODE}\nPush remote name: {PUSH_REMOTE}\nBase branch: {BASE_BRANCH}\nNotify channel: {notify_channel}\n</config>\n\n<issue>\nRepository: {SOURCE_REPO}\nIssue: #{number}\nTitle: {title}\nURL: {url}\nLabels: {labels}\nBody: {body}\n</issue>\n\n<instructions>\nFollow these steps in order. If any step fails, report the failure and stop.\n\n0. SETUP — Ensure GH_TOKEN is available:\n```\n\nexport GH_TOKEN=$(node -e \"const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/openclaw.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')\")\n\n```\nIf that fails, also try:\n```\n\nexport CONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nexport GH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | node -e \"const fs=require('fs');const d=JSON.parse(fs.readFileSync(0,'utf8'));console.log(d.skills?.entries?.['gh-issues']?.apiKey||'')\")\n\n```\nVerify: echo \"Token: ${GH_TOKEN:0:10}...\"\n\n1. CONFIDENCE CHECK — Before implementing, assess whether this issue is actionable:\n- Read the issue body carefully. Is the problem clearly described?\n- Search the codebase (grep/find) for the relevant code. Can you locate it?\n- Is the scope reasonable? (single file/function = good, whole subsystem = bad)\n- Is a specific fix suggested or is it a vague complaint?\n\nRate your confidence (1-10). If confidence < 7, STOP and report:\n> \"Skipping #{number}: Low confidence (score: N/10) — [reason: vague requirements | cannot locate code | scope too large | no clear fix suggested]\"\n\nOnly proceed if confidence >= 7.\n\n1. UNDERSTAND — Read the issue carefully. Identify what needs to change and where.\n\n2. BRANCH — Create a feature branch from the base branch:\ngit checkout -b fix/issue-{number} {BASE_BRANCH}\n\n3. ANALYZE — Search the codebase to find relevant files:\n- Use grep/find via exec to locate code related to the issue\n- Read the relevant files to understand the current behavior\n- Identify the root cause\n\n4. IMPLEMENT — Make the minimal, focused fix:\n- Follow existing code style and conventions\n- Change only what is necessary to fix the issue\n- Do not add unrelated changes or new dependencies without justification\n\n5. TEST — Discover and run the existing test suite if one exists:\n- Look for package.json scripts, Makefile targets, pytest, cargo test, etc.\n- Run the relevant tests\n- If tests fail after your fix, attempt ONE retry with a corrected approach\n- If tests still fail, report the failure\n\n6. COMMIT — Stage and commit your changes:\ngit add {changed_files}\ngit commit -m \"fix: {short_description}\n\nFixes {SOURCE_REPO}#{number}\"\n\n7. PUSH — Push the branch:\nFirst, ensure the push remote uses token auth and disable credential helpers:\ngit config --global credential.helper \"\"\ngit remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\nThen push:\nGIT_ASKPASS=true git push -u {PUSH_REMOTE} fix/issue-{number}\n\n8. PR — Create a pull request using the GitHub API:\n\nIf FORK_MODE is true, the PR goes from your fork to the source repo:\n- head = \"{PUSH_REPO_OWNER}:fix/issue-{number}\"\n- base = \"{BASE_BRANCH}\"\n- PR is created on {SOURCE_REPO}\n\nIf FORK_MODE is false:\n- head = \"fix/issue-{number}\"\n- base = \"{BASE_BRANCH}\"\n- PR is created on {SOURCE_REPO}\n\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/pulls \\\n  -d '{\n    \"title\": \"fix: {title}\",\n    \"head\": \"{head_value}\",\n    \"base\": \"{BASE_BRANCH}\",\n    \"body\": \"## Summary\\n\\n{one_paragraph_description_of_fix}\\n\\n## Changes\\n\\n{bullet_list_of_changes}\\n\\n## Testing\\n\\n{what_was_tested_and_results}\\n\\nFixes {SOURCE_REPO}#{number}\"\n  }'\n\nExtract the `html_url` from the response — this is the PR link.\n\n9. REPORT — Send back a summary:\n- PR URL (the html_url from step 8)\n- Files changed (list)\n- Fix summary (1-2 sentences)\n- Any caveats or concerns\n\n10. NOTIFY (if notify_channel is set) — If {notify_channel} is not empty, send a notification to the Telegram channel:\n```\n\nUse the message tool with:\n\n- action: \"send\"\n- channel: \"telegram\"\n- target: \"{notify_channel}\"\n- message: \"✅ PR Created: {SOURCE_REPO}#{number}\n\n{title}\n\n{pr_url}\n\nFiles changed: {files_changed_list}\"\n\n```\n</instructions>\n\n<constraints>\n- No force-push, no modifying the base branch\n- No unrelated changes or gratuitous refactoring\n- No new dependencies without strong justification\n- If the issue is unclear or too complex to fix confidently, report your analysis instead of guessing\n- Do NOT use the gh CLI — it is not available. Use curl + GitHub REST API for all GitHub operations.\n- GH_TOKEN is already in the environment — do NOT prompt for auth\n- Time limit: you have 60 minutes max. Be thorough — analyze properly, test your fix, don't rush.\n</constraints>\n```\n\n### Spawn configuration per sub-agent:\n\n- runTimeoutSeconds: 3600 (60 minutes)\n- cleanup: \"keep\" (preserve transcripts for review)\n- If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n\n### Timeout Handling\n\nIf a sub-agent exceeds 60 minutes, record it as:\n\n> \"#{N} — Timed out (issue may be too complex for auto-fix)\"\n\n---\n\n## Results Collection\n\n**If `--cron` is active:** Skip this section entirely — the orchestrator already exited after spawning in Phase 5.\n\nAfter ALL sub-agents complete (or timeout), collect their results. Store the list of successfully opened PRs in `OPEN_PRS` (PR number, branch name, issue number, PR URL) for use in Phase 6.\n\nPresent a summary table:\n\n| Issue                 | Status    | PR                             | Notes                          |\n| --------------------- | --------- | ------------------------------ | ------------------------------ |\n| #42 Fix null pointer  | PR opened | https://github.com/.../pull/99 | 3 files changed                |\n| #37 Add retry logic   | Failed    | --                             | Could not identify target code |\n| #15 Update docs       | Timed out | --                             | Too complex for auto-fix       |\n| #8 Fix race condition | Skipped   | --                             | PR already exists              |\n\n**Status values:**\n\n- **PR opened** — success, link to PR\n- **Failed** — sub-agent could not complete (include reason in Notes)\n- **Timed out** — exceeded 60-minute limit\n- **Skipped** — existing PR detected in pre-flight\n\nEnd with a one-line summary:\n\n> \"Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.\"\n\n**Send notification to channel (if --notify-channel is set):**\nIf `--notify-channel` was provided, send the final summary to that Telegram channel using the `message` tool:\n\n```\nUse the message tool with:\n- action: \"send\"\n- channel: \"telegram\"\n- target: \"{notify-channel}\"\n- message: \"✅ GitHub Issues Processed\n\nProcessed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.\n\n{PR_LIST}\"\n\nWhere PR_LIST includes only successfully opened PRs in format:\n• #{issue_number}: {PR_url} ({notes})\n```\n\nThen proceed to Phase 6.\n\n---\n\n## Phase 6 — PR Review Handler\n\nThis phase monitors open PRs (created by this skill or pre-existing `fix/issue-*` PRs) for review comments and spawns sub-agents to address them.\n\n**When this phase runs:**\n\n- After Results Collection (Phases 2-5 completed) — checks PRs that were just opened\n- When `--reviews-only` flag is set — skips Phases 2-5 entirely, runs only this phase\n- In watch mode — runs every poll cycle after checking for new issues\n\n**Cron review mode (`--cron --reviews-only`):**\nWhen both `--cron` and `--reviews-only` are set:\n\n1. Run token resolution (Phase 2 token section)\n2. Discover open `fix/issue-*` PRs (Step 6.1)\n3. Fetch review comments (Step 6.2)\n4. **Analyze comment content for actionability** (Step 6.3)\n5. If actionable comments are found, spawn ONE review-fix sub-agent for the first PR with unaddressed comments — fire-and-forget (do NOT await result)\n   - Use `cleanup: \"keep\"` and `runTimeoutSeconds: 3600`\n   - If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n6. Report: \"Spawned review handler for PR #{N} — will push fixes when complete\"\n7. Exit the skill immediately. Do not proceed to Step 6.5 (Review Results).\n\nIf no actionable comments found, report \"No actionable review comments found\" and exit.\n\n**Normal mode (non-cron) continues below:**\n\n### Step 6.1 — Discover PRs to Monitor\n\nCollect PRs to check for review comments:\n\n**If coming from Phase 5:** Use the `OPEN_PRS` list from Results Collection.\n\n**If `--reviews-only` or subsequent watch cycle:** Fetch all open PRs with `fix/issue-` branch pattern:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls?state=open&per_page=100\"\n```\n\nFilter to only PRs where `head.ref` starts with `fix/issue-`.\n\nFor each PR, extract: `number` (PR number), `head.ref` (branch name), `html_url`, `title`, `body`.\n\nIf no PRs found, report \"No open fix/ PRs to monitor\" and stop (or loop back if in watch mode).\n\n### Step 6.2 — Fetch All Review Sources\n\nFor each PR, fetch reviews from multiple sources:\n\n**Fetch PR reviews:**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/reviews\"\n```\n\n**Fetch PR review comments (inline/file-level):**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments\"\n```\n\n**Fetch PR issue comments (general conversation):**\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments\"\n```\n\n**Fetch PR body for embedded reviews:**\nSome review tools (like Greptile) embed their feedback directly in the PR body. Check for:\n\n- `<!-- greptile_comment -->` markers\n- Other structured review sections in the PR body\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" -H \"Accept: application/vnd.github+json\" \\\n  \"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}\"\n```\n\nExtract the `body` field and parse for embedded review content.\n\n### Step 6.3 — Analyze Comments for Actionability\n\n**Determine the bot's own username** for filtering:\n\n```\ncurl -s -H \"Authorization: Bearer $GH_TOKEN\" https://api.github.com/user | jq -r '.login'\n```\n\nStore as `BOT_USERNAME`. Exclude any comment where `user.login` equals `BOT_USERNAME`.\n\n**For each comment/review, analyze the content to determine if it requires action:**\n\n**NOT actionable (skip):**\n\n- Pure approvals or \"LGTM\" without suggestions\n- Bot comments that are informational only (CI status, auto-generated summaries without specific requests)\n- Comments already addressed (check if bot replied with \"Addressed in commit...\")\n- Reviews with state `APPROVED` and no inline comments requesting changes\n\n**IS actionable (requires attention):**\n\n- Reviews with state `CHANGES_REQUESTED`\n- Reviews with state `COMMENTED` that contain specific requests:\n  - \"this test needs to be updated\"\n  - \"please fix\", \"change this\", \"update\", \"can you\", \"should be\", \"needs to\"\n  - \"will fail\", \"will break\", \"causes an error\"\n  - Mentions of specific code issues (bugs, missing error handling, edge cases)\n- Inline review comments pointing out issues in the code\n- Embedded reviews in PR body that identify:\n  - Critical issues or breaking changes\n  - Test failures expected\n  - Specific code that needs attention\n  - Confidence scores with concerns\n\n**Parse embedded review content (e.g., Greptile):**\nLook for sections marked with `<!-- greptile_comment -->` or similar. Extract:\n\n- Summary text\n- Any mentions of \"Critical issue\", \"needs attention\", \"will fail\", \"test needs to be updated\"\n- Confidence scores below 4/5 (indicates concerns)\n\n**Build actionable_comments list** with:\n\n- Source (review, inline comment, PR body, etc.)\n- Author\n- Body text\n- For inline: file path and line number\n- Specific action items identified\n\nIf no actionable comments found across any PR, report \"No actionable review comments found\" and stop (or loop back if in watch mode).\n\n### Step 6.4 — Present Review Comments\n\nDisplay a table of PRs with pending actionable comments:\n\n```\n| PR | Branch | Actionable Comments | Sources |\n|----|--------|---------------------|---------|\n| #99 | fix/issue-42 | 2 comments | @reviewer1, greptile |\n| #101 | fix/issue-37 | 1 comment | @reviewer2 |\n```\n\nIf `--yes` is NOT set and this is not a subsequent watch poll: ask the user to confirm which PRs to address (\"all\", comma-separated PR numbers, or \"skip\").\n\n### Step 6.5 — Spawn Review Fix Sub-agents (Parallel)\n\nFor each PR with actionable comments, spawn a sub-agent. Launch up to 8 concurrently.\n\n**Review fix sub-agent prompt:**\n\n```\nYou are a PR review handler agent. Your task is to address review comments on a pull request by making the requested changes, pushing updates, and replying to each comment.\n\nIMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.\n\nFirst, ensure GH_TOKEN is set. Check: echo $GH_TOKEN. If empty, read from config:\nCONFIG_PATH=\"${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}\"\nGH_TOKEN=$(cat \"$CONFIG_PATH\" 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries[\"gh-issues\"].apiKey // empty')\n\n<config>\nRepository: {SOURCE_REPO}\nPush repo: {PUSH_REPO}\nFork mode: {FORK_MODE}\nPush remote: {PUSH_REMOTE}\nPR number: {pr_number}\nPR URL: {pr_url}\nBranch: {branch_name}\n</config>\n\n<review_comments>\n{json_array_of_actionable_comments}\n\nEach comment has:\n- id: comment ID (for replying)\n- user: who left it\n- body: the comment text\n- path: file path (for inline comments)\n- line: line number (for inline comments)\n- diff_hunk: surrounding diff context (for inline comments)\n- source: where the comment came from (review, inline, pr_body, greptile, etc.)\n</review_comments>\n\n<instructions>\nFollow these steps in order:\n\n0. SETUP — Ensure GH_TOKEN is available:\n```\n\nexport GH_TOKEN=$(node -e \"const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/openclaw.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')\")\n\n```\nVerify: echo \"Token: ${GH_TOKEN:0:10}...\"\n\n1. CHECKOUT — Switch to the PR branch:\ngit fetch {PUSH_REMOTE} {branch_name}\ngit checkout {branch_name}\ngit pull {PUSH_REMOTE} {branch_name}\n\n2. UNDERSTAND — Read ALL review comments carefully. Group them by file. Understand what each reviewer is asking for.\n\n3. IMPLEMENT — For each comment, make the requested change:\n- Read the file and locate the relevant code\n- Make the change the reviewer requested\n- If the comment is vague or you disagree, still attempt a reasonable fix but note your concern\n- If the comment asks for something impossible or contradictory, skip it and explain why in your reply\n\n4. TEST — Run existing tests to make sure your changes don't break anything:\n- If tests fail, fix the issue or revert the problematic change\n- Note any test failures in your replies\n\n5. COMMIT — Stage and commit all changes in a single commit:\ngit add {changed_files}\ngit commit -m \"fix: address review comments on PR #{pr_number}\n\nAddresses review feedback from {reviewer_names}\"\n\n6. PUSH — Push the updated branch:\ngit config --global credential.helper \"\"\ngit remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git\nGIT_ASKPASS=true git push {PUSH_REMOTE} {branch_name}\n\n7. REPLY — For each addressed comment, post a reply:\n\nFor inline review comments (have a path/line), reply to the comment thread:\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments/{comment_id}/replies \\\n  -d '{\"body\": \"Addressed in commit {short_sha} — {brief_description_of_change}\"}'\n\nFor general PR comments (issue comments), reply on the PR:\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $GH_TOKEN\" \\\n  -H \"Accept: application/vnd.github+json\" \\\n  https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments \\\n  -d '{\"body\": \"Addressed feedback from @{reviewer}:\\n\\n{summary_of_changes_made}\\n\\nUpdated in commit {short_sha}\"}'\n\nFor comments you could NOT address, reply explaining why:\n\"Unable to address this comment: {reason}. This may need manual review.\"\n\n8. REPORT — Send back a summary:\n- PR URL\n- Number of comments addressed vs skipped\n- Commit SHA\n- Files changed\n- Any comments that need manual attention\n</instructions>\n\n<constraints>\n- Only modify files relevant to the review comments\n- Do not make unrelated changes\n- Do not force-push — always regular push\n- If a comment contradicts another comment, address the most recent one and flag the conflict\n- Do NOT use the gh CLI — use curl + GitHub REST API\n- GH_TOKEN is already in the environment — do not prompt for auth\n- Time limit: 60 minutes max\n</constraints>\n```\n\n**Spawn configuration per sub-agent:**\n\n- runTimeoutSeconds: 3600 (60 minutes)\n- cleanup: \"keep\" (preserve transcripts for review)\n- If `--model` was provided, include `model: \"{MODEL}\"` in the spawn config\n\n### Step 6.6 — Review Results\n\nAfter all review sub-agents complete, present a summary:\n\n```\n| PR | Comments Addressed | Comments Skipped | Commit | Status |\n|----|-------------------|-----------------|--------|--------|\n| #99 fix/issue-42 | 3 | 0 | abc123f | All addressed |\n| #101 fix/issue-37 | 1 | 1 | def456a | 1 needs manual review |\n```\n\nAdd comment IDs from this batch to `ADDRESSED_COMMENTS` set to prevent re-processing.\n\n---\n\n## Watch Mode (if --watch is active)\n\nAfter presenting results from the current batch:\n\n1. Add all issue numbers from this batch to the running set PROCESSED_ISSUES.\n2. Add all addressed comment IDs to ADDRESSED_COMMENTS.\n3. Tell the user:\n   > \"Next poll in {interval} minutes... (say 'stop' to end watch mode)\"\n4. Sleep for {interval} minutes.\n5. Go back to **Phase 2 — Fetch Issues**. The fetch will automatically filter out:\n   - Issues already in PROCESSED_ISSUES\n   - Issues that have existing fix/issue-{N} PRs (caught in Phase 4 pre-flight)\n6. After Phases 2-5 (or if no new issues), run **Phase 6** to check for new review comments on ALL tracked PRs (both newly created and previously opened).\n7. If no new issues AND no new actionable review comments → report \"No new activity. Polling again in {interval} minutes...\" and loop back to step 4.\n8. The user can say \"stop\" at any time to exit watch mode. When stopping, present a final cumulative summary of ALL batches — issues processed AND review comments addressed.\n\n**Context hygiene between polls — IMPORTANT:**\nOnly retain between poll cycles:\n\n- PROCESSED_ISSUES (set of issue numbers)\n- ADDRESSED_COMMENTS (set of comment IDs)\n- OPEN_PRS (list of tracked PRs: number, branch, URL)\n- Cumulative results (one line per issue + one line per review batch)\n- Parsed arguments from Phase 1\n- BASE_BRANCH, SOURCE_REPO, PUSH_REPO, FORK_MODE, BOT_USERNAME\n  Do NOT retain issue bodies, comment bodies, sub-agent transcripts, or codebase analysis between polls.\n\n<!-- File: skills/gifgrep/SKILL.md (Tokens: ~616 | Category: anthropic-skill) -->\n---\nname: gifgrep\ndescription: Search GIF providers with CLI/TUI, download results, and extract stills/sheets.\nhomepage: https://gifgrep.com\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🧲\",\n        \"requires\": { \"bins\": [\"gifgrep\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/gifgrep\",\n              \"bins\": [\"gifgrep\"],\n              \"label\": \"Install gifgrep (brew)\",\n            },\n            {\n              \"id\": \"go\",\n              \"kind\": \"go\",\n              \"module\": \"github.com/steipete/gifgrep/cmd/gifgrep@latest\",\n              \"bins\": [\"gifgrep\"],\n              \"label\": \"Install gifgrep (go)\",\n            },\n          ],\n      },\n  }\n---\n\n# gifgrep\n\nUse `gifgrep` to search GIF providers (Tenor/Giphy), browse in a TUI, download results, and extract stills or sheets.\n\nGIF-Grab (gifgrep workflow)\n\n- Search → preview → download → extract (still/sheet) for fast review and sharing.\n\nQuick start\n\n- `gifgrep cats --max 5`\n- `gifgrep cats --format url | head -n 5`\n- `gifgrep search --json cats | jq '.[0].url'`\n- `gifgrep tui \"office handshake\"`\n- `gifgrep cats --download --max 1 --format url`\n\nTUI + previews\n\n- TUI: `gifgrep tui \"query\"`\n- CLI still previews: `--thumbs` (Kitty/Ghostty only; still frame)\n\nDownload + reveal\n\n- `--download` saves to `~/Downloads`\n- `--reveal` shows the last download in Finder\n\nStills + sheets\n\n- `gifgrep still ./clip.gif --at 1.5s -o still.png`\n- `gifgrep sheet ./clip.gif --frames 9 --cols 3 -o sheet.png`\n- Sheets = single PNG grid of sampled frames (great for quick review, docs, PRs, chat).\n- Tune: `--frames` (count), `--cols` (grid width), `--padding` (spacing).\n\nProviders\n\n- `--source auto|tenor|giphy`\n- `GIPHY_API_KEY` required for `--source giphy`\n- `TENOR_API_KEY` optional (Tenor demo key used if unset)\n\nOutput\n\n- `--json` prints an array of results (`id`, `title`, `url`, `preview_url`, `tags`, `width`, `height`)\n- `--format` for pipe-friendly fields (e.g., `url`)\n\nEnvironment tweaks\n\n- `GIFGREP_SOFTWARE_ANIM=1` to force software animation\n- `GIFGREP_CELL_ASPECT=0.5` to tweak preview geometry\n\n<!-- File: skills/github/SKILL.md (Tokens: ~2238 | Category: anthropic-skill) -->\n---\nname: github\ndescription: Use when interacting with GitHub repositories, authentication, remotes, pushes, or repository creation through the GitHub CLI and related git workflows.\nhomepage: https://cli.github.com/\nmetadata:\n  {\n    \"emoji\": \"🐙\"\n  }\n---\n\n# GitHub\n\n## Overview\n\nPrefer `gh` for all GitHub operations. The default goal is to complete the full GitHub flow with the least possible user effort: detect existing authentication first, reuse it if available, and only ask the user for the smallest missing step.\n\nNever ask the user to manually perform a long GitHub setup if the agent can check, reuse, or finish it directly.\n\n## Core Rules\n\n- Prefer `gh` over raw GitHub API calls.\n- Prefer existing authenticated state over asking for new credentials.\n- Prefer non-interactive checks before prompting the user.\n- Prefer the smallest unblock request:\n  - paste a one-time code\n  - confirm browser login\n  - provide a PAT\n  - confirm repository visibility\n- Do not ask the user to manually create the repository if `gh` can do it.\n- Do not ask the user to manually add remotes, initialize Git, or push if the agent can do it.\n- Never print, echo, log, summarize, or restate secrets.\n- Never store secrets in files, commits, repo config, docs, examples, or chat summaries.\n- Never switch credential strategy unless the current one actually fails.\n\n### Interactive login driving rules\n\nWhen using `gh auth login` through a shell session, do not assume the browser login step has started after the first prompt or after sending one key.\n\nYou must continue driving the interactive flow until there is explicit evidence of one of these states:\n\n1. GitHub CLI has clearly started browser/device authentication\n2. GitHub CLI is explicitly waiting for the user to complete external authentication\n3. GitHub CLI reports that authentication succeeded\n4. GitHub CLI reports a real error and cannot continue\n\nWhile driving the login flow:\n\n- keep polling the session after each input\n- handle intermediate prompts such as protocol selection, SSH/HTTPS choice, credential helper confirmation, browser/device flow confirmation, or similar setup questions\n- use minimal inputs such as `enter`, arrow keys, `y`, `n`, or short submitted text as needed\n- do not stop after only one successful prompt response\n- do not tell the user that the browser has opened unless the command output explicitly indicates that a browser login or web flow has started\n- do not tell the user that login is complete unless the command output explicitly indicates success\n\nAcceptable evidence that browser or external auth has actually started includes output such as:\n\n- a message that a browser is being opened\n- a URL to visit\n- a device code to enter\n- explicit wording that GitHub authentication is waiting for completion\n\nIf the shell session remains running but only shows that an earlier prompt was answered, continue polling instead of summarizing the state prematurely.\n\nOnly ask the user to act when the output clearly shows an external action is now required.\n\nWhen the user says they completed browser or device authorization, do not start a new `gh auth login` flow immediately.\n\nInstead:\n\n1. continue polling the existing shell session if it is still running\n2. wait for explicit success or failure from that same session\n3. only if that session has exited or is unrecoverable, then check `gh auth status`\n4. only start a brand-new login flow if the previous login session has clearly failed or been closed\n\nIf the shell output contains prompts like:\n\n- \"Press Enter to open ...\"\n- \"Press Enter to continue\"\n- similar one-step local confirmation prompts\n\nthen send `enter` automatically and continue polling the same session.\n\nDo not stop at that prompt.\nDo not ask the user to confirm completion yet.\nDo not start a new login flow while this session is still running.\n\n## Credential Handling Priority\n\n### 1. Reuse `gh` authentication first\n\nCheck whether `gh` exists and is already authenticated.\n\nUse this path when possible:\n\n- `gh auth status`\n- if authenticated, continue directly with:\n  - repo detection\n  - `gh repo create`\n  - remote setup\n  - push\n\nIf `gh` is authenticated, do not ask the user for tokens, passwords, SSH keys, or browser login.\n\n### 2. If `gh` exists but is not authenticated, log in with `gh` directly\n\nIf `gh` is installed but not authenticated, use `gh auth login` directly and continue the GitHub flow after login succeeds.\n\nPreferred order:\n\n1. browser/web login via `gh auth login -h github.com -w`\n2. standard interactive `gh auth login -h github.com`\n3. PAT-based login only if the normal `gh` login path is unavailable or fails\n\nRules:\n\n- do not switch away from `gh` login unless it actually fails\n- do not ask the user to manually create repositories or configure git\n- do not ask the user to perform a long GitHub setup outside the login itself\n- continue automatically as soon as login succeeds\n\nWhen login requires user interaction:\n\n- ask for only the smallest possible action\n- examples:\n  - “Please complete the GitHub browser login that just opened.”\n  - “Please finish the GitHub CLI login prompts.”\n  - “Please paste a GitHub PAT with repo access.”\n\nAfter login succeeds, continue directly with:\n\n- repo detection\n- `gh repo create`\n- remote setup\n- push\n\n### 3. If `gh` is unavailable, try to install it first\n\nIf `gh` is missing:\n\n- first try to install `gh` automatically using the platform's normal package manager or a reasonable official installation path\n- prefer non-interactive installation methods when available\n- after installation, re-check whether `gh` exists and continue with the normal `gh`-first flow\n- only fall back away from `gh` if installation actually fails, is blocked by the environment, or would require disproportionate user effort\n\nAcceptable install attempts include examples like:\n\n- Homebrew on macOS\n- `apt`, `dnf`, or similar package managers on Linux\n- `winget` or other normal package-manager paths on Windows\n\nRules:\n\n- do not ask the user to install `gh` manually before you have tried a reasonable automatic install path\n- do not switch to a different credential strategy just because `gh` is missing initially\n- if installation fails, summarize the blocker briefly and then continue to the fallback path below\n\n### 4. If `gh` is still unavailable, fall back to Git\n\nIf `gh` is missing:\n\n- continue with plain `git`\n- prefer existing Git credential helpers or existing SSH authentication\n- inspect the environment before asking for credentials\n\nCheck for likely reusable auth:\n\n- existing `origin` remote\n- existing Git credential helper\n- working SSH auth to GitHub\n- existing authenticated HTTPS remote behavior\n\nOnly if those paths fail should the user be asked for one minimal credential input.\n\n## Minimal-Interruption Prompting\n\nWhen user input is required, ask for only one concrete item at a time.\n\nGood prompts:\n\n- “Please confirm whether the repo should be public or private.”\n- “Please complete the GitHub login in your browser.”\n- “Please paste a GitHub PAT with repo access.”\n- “Please confirm the GitHub owner/org name.”\n\nBad prompts:\n\n- asking the user to manually create a repo\n- asking the user to configure Git from scratch without first checking\n- asking for multiple credentials at once\n- asking the user to inspect local config manually if the agent can inspect it\n\n## Safe Credential Rules\n\n- Treat PATs, device codes, auth tokens, cookies, and SSH private keys as secrets.\n- Never echo secrets back to the user.\n- Never include secrets in command output summaries.\n- Never write secrets into repo files or examples.\n- Never commit `.env`, token files, or copied credentials.\n- If a command might expose a secret in stdout/stderr, avoid reproducing that output verbatim.\n- If the user gives a token, use it only for the current task unless explicitly told otherwise.\n\n## Standard Execution Flow\n\n### A. Detect environment\n\nCheck in this order:\n\n1. Is this already a Git repo?\n2. Is there already a remote?\n3. Is `gh` installed?\n4. Is `gh` authenticated?\n5. If not using `gh`, does Git already have usable auth?\n\n### B. If already connected to GitHub\n\nIf the repo already has a valid GitHub remote and push access works:\n\n- do not recreate the repo\n- do not replace auth\n- just push\n\n### C. If Git repo exists but no GitHub repo exists yet\n\nPreferred:\n\n- create the GitHub repo with `gh repo create`\n- set or update `origin`\n- push the current branch\n\nOnly ask the user for:\n\n- owner/org if ambiguous\n- public vs private if not specified\n- authentication only if required\n\n### D. If not a Git repo yet\n\nDo the setup directly:\n\n- initialize Git\n- add files\n- create initial commit if needed\n- create GitHub repo\n- add remote\n- push\n\nDo not ask the user to perform these steps manually.\n\n## `gh`-First Behavior\n\nWhen `gh` is available and authenticated, prefer commands in this shape:\n\n- inspect auth\n- create repo\n- set remote if needed\n- push branch\n- optionally enable features like description/homepage/topics later\n\nUse plain Git only for local repository operations or when `gh` cannot complete the GitHub-side action.\n\n## Git / API Fallback Behavior\n\nOnly use this fallback when:\n\n- `gh` is not installed and cannot be installed reasonably\n- or `gh` is installed but unusable for the required operation\n- or the environment blocks the normal `gh` flow in a way that cannot be resolved with minimal user input\n\nFallback order:\n\n1. direct GitHub API + local `git`\n2. plain `git` with existing SSH or credential-helper auth\n3. HTTPS + PAT only if no better reusable auth exists\n\nFallback rules:\n\n- still minimize user effort\n- still prefer reusing existing credentials\n- still never ask the user to create the repository manually if the agent can do it through the API\n- still avoid exposing or persisting secrets\n- still continue automatically once the minimal missing credential or confirmation is provided\n\nIf PAT is required:\n\n- ask only for a token with the minimum needed scope, usually repo write access\n- use it only to complete the push flow\n- avoid leaving it in shell history, files, process args, or remote URLs when possible\n\n## Success Criteria\n\nA successful run means:\n\n- the local project is a valid Git repo if needed\n- the GitHub repo exists\n- the correct remote is configured\n- the current branch is pushed\n- credentials were reused when possible\n- user involvement was limited to the smallest truly necessary action\n- no secret was exposed or persisted unsafely\n\n<!-- File: skills/gog/SKILL.md (Tokens: ~1395 | Category: anthropic-skill) -->\n---\nname: gog\ndescription: Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.\nhomepage: https://gogcli.sh\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🎮\",\n        \"requires\": { \"bins\": [\"gog\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/gogcli\",\n              \"bins\": [\"gog\"],\n              \"label\": \"Install gog (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# gog\n\nUse `gog` for Gmail/Calendar/Drive/Contacts/Sheets/Docs. Requires OAuth setup.\n\nSetup (once)\n\n- `gog auth credentials /path/to/client_secret.json`\n- `gog auth add you@gmail.com --services gmail,calendar,drive,contacts,docs,sheets`\n- `gog auth list`\n\nCommon commands\n\n- Gmail search: `gog gmail search 'newer_than:7d' --max 10`\n- Gmail messages search (per email, ignores threading): `gog gmail messages search \"in:inbox from:ryanair.com\" --max 20 --account you@example.com`\n- Gmail send (plain): `gog gmail send --to a@b.com --subject \"Hi\" --body \"Hello\"`\n- Gmail send (multi-line): `gog gmail send --to a@b.com --subject \"Hi\" --body-file ./message.txt`\n- Gmail send (stdin): `gog gmail send --to a@b.com --subject \"Hi\" --body-file -`\n- Gmail send (HTML): `gog gmail send --to a@b.com --subject \"Hi\" --body-html \"<p>Hello</p>\"`\n- Gmail draft: `gog gmail drafts create --to a@b.com --subject \"Hi\" --body-file ./message.txt`\n- Gmail send draft: `gog gmail drafts send <draftId>`\n- Gmail reply: `gog gmail send --to a@b.com --subject \"Re: Hi\" --body \"Reply\" --reply-to-message-id <msgId>`\n- Calendar list events: `gog calendar events <calendarId> --from <iso> --to <iso>`\n- Calendar create event: `gog calendar create <calendarId> --summary \"Title\" --from <iso> --to <iso>`\n- Calendar create with color: `gog calendar create <calendarId> --summary \"Title\" --from <iso> --to <iso> --event-color 7`\n- Calendar update event: `gog calendar update <calendarId> <eventId> --summary \"New Title\" --event-color 4`\n- Calendar show colors: `gog calendar colors`\n- Drive search: `gog drive search \"query\" --max 10`\n- Contacts: `gog contacts list --max 20`\n- Sheets get: `gog sheets get <sheetId> \"Tab!A1:D10\" --json`\n- Sheets update: `gog sheets update <sheetId> \"Tab!A1:B2\" --values-json '[[\"A\",\"B\"],[\"1\",\"2\"]]' --input USER_ENTERED`\n- Sheets append: `gog sheets append <sheetId> \"Tab!A:C\" --values-json '[[\"x\",\"y\",\"z\"]]' --insert INSERT_ROWS`\n- Sheets clear: `gog sheets clear <sheetId> \"Tab!A2:Z\"`\n- Sheets metadata: `gog sheets metadata <sheetId> --json`\n- Docs export: `gog docs export <docId> --format txt --out /tmp/doc.txt`\n- Docs cat: `gog docs cat <docId>`\n\nCalendar Colors\n\n- Use `gog calendar colors` to see all available event colors (IDs 1-11)\n- Add colors to events with `--event-color <id>` flag\n- Event color IDs (from `gog calendar colors` output):\n  - 1: #a4bdfc\n  - 2: #7ae7bf\n  - 3: #dbadff\n  - 4: #ff887c\n  - 5: #fbd75b\n  - 6: #ffb878\n  - 7: #46d6db\n  - 8: #e1e1e1\n  - 9: #5484ed\n  - 10: #51b749\n  - 11: #dc2127\n\nEmail Formatting\n\n- Prefer plain text. Use `--body-file` for multi-paragraph messages (or `--body-file -` for stdin).\n- Same `--body-file` pattern works for drafts and replies.\n- `--body` does not unescape `\\n`. If you need inline newlines, use a heredoc or `$'Line 1\\n\\nLine 2'`.\n- Use `--body-html` only when you need rich formatting.\n- HTML tags: `<p>` for paragraphs, `<br>` for line breaks, `<strong>` for bold, `<em>` for italic, `<a href=\"url\">` for links, `<ul>`/`<li>` for lists.\n- Example (plain text via stdin):\n\n  ```bash\n  gog gmail send --to recipient@example.com \\\n    --subject \"Meeting Follow-up\" \\\n    --body-file - <<'EOF'\n  Hi Name,\n\n  Thanks for meeting today. Next steps:\n  - Item one\n  - Item two\n\n  Best regards,\n  Your Name\n  EOF\n  ```\n\n- Example (HTML list):\n  ```bash\n  gog gmail send --to recipient@example.com \\\n    --subject \"Meeting Follow-up\" \\\n    --body-html \"<p>Hi Name,</p><p>Thanks for meeting today. Here are the next steps:</p><ul><li>Item one</li><li>Item two</li></ul><p>Best regards,<br>Your Name</p>\"\n  ```\n\nNotes\n\n- Set `GOG_ACCOUNT=you@gmail.com` to avoid repeating `--account`.\n- For scripting, prefer `--json` plus `--no-input`.\n- Sheets values can be passed via `--values-json` (recommended) or as inline rows.\n- Docs supports export/cat/copy. In-place edits require a Docs API client (not in gog).\n- Confirm before sending mail or creating events.\n- `gog gmail search` returns one row per thread; use `gog gmail messages search` when you need every individual email returned separately.\n\n<!-- File: skills/goplaces/SKILL.md (Tokens: ~431 | Category: anthropic-skill) -->\n---\nname: goplaces\ndescription: Query Google Places API (New) via the goplaces CLI for text search, place details, resolve, and reviews. Use for human-friendly place lookup or JSON output for scripts.\nhomepage: https://github.com/steipete/goplaces\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📍\",\n        \"requires\": { \"bins\": [\"goplaces\"], \"env\": [\"GOOGLE_PLACES_API_KEY\"] },\n        \"primaryEnv\": \"GOOGLE_PLACES_API_KEY\",\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/goplaces\",\n              \"bins\": [\"goplaces\"],\n              \"label\": \"Install goplaces (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# goplaces\n\nModern Google Places API (New) CLI. Human output by default, `--json` for scripts.\n\nInstall\n\n- Homebrew: `brew install steipete/tap/goplaces`\n\nConfig\n\n- `GOOGLE_PLACES_API_KEY` required.\n- Optional: `GOOGLE_PLACES_BASE_URL` for testing/proxying.\n\nCommon commands\n\n- Search: `goplaces search \"coffee\" --open-now --min-rating 4 --limit 5`\n- Bias: `goplaces search \"pizza\" --lat 40.8 --lng -73.9 --radius-m 3000`\n- Pagination: `goplaces search \"pizza\" --page-token \"NEXT_PAGE_TOKEN\"`\n- Resolve: `goplaces resolve \"Soho, London\" --limit 5`\n- Details: `goplaces details <place_id> --reviews`\n- JSON: `goplaces search \"sushi\" --json`\n\nNotes\n\n- `--no-color` or `NO_COLOR` disables ANSI color.\n- Price levels: 0..4 (free → very expensive).\n- Type filter sends only the first `--type` value (API accepts one).\n\n<!-- File: skills/healthcheck/SKILL.md (Tokens: ~2416 | Category: anthropic-skill) -->\n---\nname: healthcheck\ndescription: Host security hardening and risk-tolerance configuration for OpenClaw deployments. Use when a user asks for security audits, firewall/SSH/update hardening, risk posture, exposure review, OpenClaw cron scheduling for periodic checks, or version status checks on a machine running OpenClaw (laptop, workstation, Pi, VPS).\n---\n\n# OpenClaw Host Hardening\n\n## Overview\n\nAssess and harden the host running OpenClaw, then align it to a user-defined risk tolerance without breaking access. Use OpenClaw security tooling as a first-class signal, but treat OS hardening as a separate, explicit set of steps.\n\n## Core rules\n\n- Recommend running this skill with a state-of-the-art model (e.g., Opus 4.5, GPT 5.2+). The agent should self-check the current model and suggest switching if below that level; do not block execution.\n- Require explicit approval before any state-changing action.\n- Do not modify remote access settings without confirming how the user connects.\n- Prefer reversible, staged changes with a rollback plan.\n- Never claim OpenClaw changes the host firewall, SSH, or OS updates; it does not.\n- If role/identity is unknown, provide recommendations only.\n- Formatting: every set of user choices must be numbered so the user can reply with a single digit.\n- System-level backups are recommended; try to verify status.\n\n## Workflow (follow in order)\n\n### 0) Model self-check (non-blocking)\n\nBefore starting, check the current model. If it is below state-of-the-art (e.g., Opus 4.5, GPT 5.2+), recommend switching. Do not block execution.\n\n### 1) Establish context (read-only)\n\nTry to infer 1–5 from the environment before asking. Prefer simple, non-technical questions if you need confirmation.\n\nDetermine (in order):\n\n1. OS and version (Linux/macOS/Windows), container vs host.\n2. Privilege level (root/admin vs user).\n3. Access path (local console, SSH, RDP, tailnet).\n4. Network exposure (public IP, reverse proxy, tunnel).\n5. OpenClaw gateway status and bind address.\n6. Backup system and status (e.g., Time Machine, system images, snapshots).\n7. Deployment context (local mac app, headless gateway host, remote gateway, container/CI).\n8. Disk encryption status (FileVault/LUKS/BitLocker).\n9. OS automatic security updates status.\n   Note: these are not blocking items, but are highly recommended, especially if OpenClaw can access sensitive data.\n10. Usage mode for a personal assistant with full access (local workstation vs headless/remote vs other).\n\nFirst ask once for permission to run read-only checks. If granted, run them by default and only ask questions for items you cannot infer or verify. Do not ask for information already visible in runtime or command output. Keep the permission ask as a single sentence, and list follow-up info needed as an unordered list (not numbered) unless you are presenting selectable choices.\n\nIf you must ask, use non-technical prompts:\n\n- “Are you using a Mac, Windows PC, or Linux?”\n- “Are you logged in directly on the machine, or connecting from another computer?”\n- “Is this machine reachable from the public internet, or only on your home/network?”\n- “Do you have backups enabled (e.g., Time Machine), and are they current?”\n- “Is disk encryption turned on (FileVault/BitLocker/LUKS)?”\n- “Are automatic security updates enabled?”\n- “How do you use this machine?”\n  Examples:\n  - Personal machine shared with the assistant\n  - Dedicated local machine for the assistant\n  - Dedicated remote machine/server accessed remotely (always on)\n  - Something else?\n\nOnly ask for the risk profile after system context is known.\n\nIf the user grants read-only permission, run the OS-appropriate checks by default. If not, offer them (numbered). Examples:\n\n1. OS: `uname -a`, `sw_vers`, `cat /etc/os-release`.\n2. Listening ports:\n   - Linux: `ss -ltnup` (or `ss -ltnp` if `-u` unsupported).\n   - macOS: `lsof -nP -iTCP -sTCP:LISTEN`.\n3. Firewall status:\n   - Linux: `ufw status`, `firewall-cmd --state`, `nft list ruleset` (pick what is installed).\n   - macOS: `/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate` and `pfctl -s info`.\n4. Backups (macOS): `tmutil status` (if Time Machine is used).\n\n### 2) Run OpenClaw security audits (read-only)\n\nAs part of the default read-only checks, run `openclaw security audit --deep`. Only offer alternatives if the user requests them:\n\n1. `openclaw security audit` (faster, non-probing)\n2. `openclaw security audit --json` (structured output)\n\nOffer to apply OpenClaw safe defaults (numbered):\n\n1. `openclaw security audit --fix`\n\nBe explicit that `--fix` only tightens OpenClaw defaults and file permissions. It does not change host firewall, SSH, or OS update policies.\n\nIf browser control is enabled, recommend that 2FA be enabled on all important accounts, with hardware keys preferred and SMS not sufficient.\n\n### 3) Check OpenClaw version/update status (read-only)\n\nAs part of the default read-only checks, run `openclaw update status`.\n\nReport the current channel and whether an update is available.\n\n### 4) Determine risk tolerance (after system context)\n\nAsk the user to pick or confirm a risk posture and any required open services/ports (numbered choices below).\nDo not pigeonhole into fixed profiles; if the user prefers, capture requirements instead of choosing a profile.\nOffer suggested profiles as optional defaults (numbered). Note that most users pick Home/Workstation Balanced:\n\n1. Home/Workstation Balanced (most common): firewall on with reasonable defaults, remote access restricted to LAN or tailnet.\n2. VPS Hardened: deny-by-default inbound firewall, minimal open ports, key-only SSH, no root login, automatic security updates.\n3. Developer Convenience: more local services allowed, explicit exposure warnings, still audited.\n4. Custom: user-defined constraints (services, exposure, update cadence, access methods).\n\n### 5) Produce a remediation plan\n\nProvide a plan that includes:\n\n- Target profile\n- Current posture summary\n- Gaps vs target\n- Step-by-step remediation with exact commands\n- Access-preservation strategy and rollback\n- Risks and potential lockout scenarios\n- Least-privilege notes (e.g., avoid admin usage, tighten ownership/permissions where safe)\n- Credential hygiene notes (location of OpenClaw creds, prefer disk encryption)\n\nAlways show the plan before any changes.\n\n### 6) Offer execution options\n\nOffer one of these choices (numbered so users can reply with a single digit):\n\n1. Do it for me (guided, step-by-step approvals)\n2. Show plan only\n3. Fix only critical issues\n4. Export commands for later\n\n### 7) Execute with confirmations\n\nFor each step:\n\n- Show the exact command\n- Explain impact and rollback\n- Confirm access will remain available\n- Stop on unexpected output and ask for guidance\n\n### 8) Verify and report\n\nRe-check:\n\n- Firewall status\n- Listening ports\n- Remote access still works\n- OpenClaw security audit (re-run)\n\nDeliver a final posture report and note any deferred items.\n\n## Required confirmations (always)\n\nRequire explicit approval for:\n\n- Firewall rule changes\n- Opening/closing ports\n- SSH/RDP configuration changes\n- Installing/removing packages\n- Enabling/disabling services\n- User/group modifications\n- Scheduling tasks or startup persistence\n- Update policy changes\n- Access to sensitive files or credentials\n\nIf unsure, ask.\n\n## Periodic checks\n\nAfter OpenClaw install or first hardening pass, run at least one baseline audit and version check:\n\n- `openclaw security audit`\n- `openclaw security audit --deep`\n- `openclaw update status`\n\nOngoing monitoring is recommended. Use the OpenClaw cron tool/CLI to schedule periodic audits (Gateway scheduler). Do not create scheduled tasks without explicit approval. Store outputs in a user-approved location and avoid secrets in logs.\nWhen scheduling headless cron runs, include a note in the output that instructs the user to call `healthcheck` so issues can be fixed.\n\n### Required prompt to schedule (always)\n\nAfter any audit or hardening pass, explicitly offer scheduling and require a direct response. Use a short prompt like (numbered):\n\n1. “Do you want me to schedule periodic audits (e.g., daily/weekly) via `openclaw cron add`?”\n\nIf the user says yes, ask for:\n\n- cadence (daily/weekly), preferred time window, and output location\n- whether to also schedule `openclaw update status`\n\nUse a stable cron job name so updates are deterministic. Prefer exact names:\n\n- `healthcheck:security-audit`\n- `healthcheck:update-status`\n\nBefore creating, `openclaw cron list` and match on exact `name`. If found, `openclaw cron edit <id> ...`.\nIf not found, `openclaw cron add --name <name> ...`.\n\nAlso offer a periodic version check so the user can decide when to update (numbered):\n\n1. `openclaw update status` (preferred for source checkouts and channels)\n2. `npm view openclaw version` (published npm version)\n\n## OpenClaw command accuracy\n\nUse only supported commands and flags:\n\n- `openclaw security audit [--deep] [--fix] [--json]`\n- `openclaw status` / `openclaw status --deep`\n- `openclaw health --json`\n- `openclaw update status`\n- `openclaw cron add|list|runs|run`\n\nDo not invent CLI flags or imply OpenClaw enforces host firewall/SSH policies.\n\n## Logging and audit trail\n\nRecord:\n\n- Gateway identity and role\n- Plan ID and timestamp\n- Approved steps and exact commands\n- Exit codes and files modified (best effort)\n\nRedact secrets. Never log tokens or full credential contents.\n\n## Memory writes (conditional)\n\nOnly write to memory files when the user explicitly opts in and the session is a private/local workspace\n(per `docs/reference/templates/AGENTS.md`). Otherwise provide a redacted, paste-ready summary the user can\ndecide to save elsewhere.\n\nFollow the durable-memory prompt format used by OpenClaw compaction:\n\n- Write lasting notes to `memory/YYYY-MM-DD.md`.\n\nAfter each audit/hardening run, if opted-in, append a short, dated summary to `memory/YYYY-MM-DD.md`\n(what was checked, key findings, actions taken, any scheduled cron jobs, key decisions,\nand all commands executed). Append-only: never overwrite existing entries.\nRedact sensitive host details (usernames, hostnames, IPs, serials, service names, tokens).\nIf there are durable preferences or decisions (risk posture, allowed ports, update policy),\nalso update `MEMORY.md` (long-term memory is optional and only used in private sessions).\n\nIf the session cannot write to the workspace, ask for permission or provide exact entries\nthe user can paste into the memory files.\n\n<!-- File: skills/himalaya/references/configuration.md (Tokens: ~991 | Category: anthropic-skill) -->\n# Himalaya Configuration Reference\n\nConfiguration file location: `~/.config/himalaya/config.toml`\n\n## Minimal IMAP + SMTP Setup\n\n```toml\n[accounts.default]\nemail = \"user@example.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\n# IMAP backend for reading emails\nbackend.type = \"imap\"\nbackend.host = \"imap.example.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"user@example.com\"\nbackend.auth.type = \"password\"\nbackend.auth.raw = \"your-password\"\n\n# SMTP backend for sending emails\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.example.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"user@example.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.raw = \"your-password\"\n```\n\n## Password Options\n\n### Raw password (testing only, not recommended)\n\n```toml\nbackend.auth.raw = \"your-password\"\n```\n\n### Password from command (recommended)\n\n```toml\nbackend.auth.cmd = \"pass show email/imap\"\n# backend.auth.cmd = \"security find-generic-password -a user@example.com -s imap -w\"\n```\n\n### System keyring (requires keyring feature)\n\n```toml\nbackend.auth.keyring = \"imap-example\"\n```\n\nThen run `himalaya account configure <account>` to store the password.\n\n## Gmail Configuration\n\n```toml\n[accounts.gmail]\nemail = \"you@gmail.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\nbackend.type = \"imap\"\nbackend.host = \"imap.gmail.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@gmail.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show google/app-password\"\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.gmail.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@gmail.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show google/app-password\"\n```\n\n**Note:** Gmail requires an App Password if 2FA is enabled.\n\n## iCloud Configuration\n\n```toml\n[accounts.icloud]\nemail = \"you@icloud.com\"\ndisplay-name = \"Your Name\"\n\nbackend.type = \"imap\"\nbackend.host = \"imap.mail.me.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@icloud.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show icloud/app-password\"\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.mail.me.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@icloud.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show icloud/app-password\"\n```\n\n**Note:** Generate an app-specific password at appleid.apple.com\n\n## Folder Aliases\n\nMap custom folder names:\n\n```toml\n[accounts.default.folder.alias]\ninbox = \"INBOX\"\nsent = \"Sent\"\ndrafts = \"Drafts\"\ntrash = \"Trash\"\n```\n\n## Multiple Accounts\n\n```toml\n[accounts.personal]\nemail = \"personal@example.com\"\ndefault = true\n# ... backend config ...\n\n[accounts.work]\nemail = \"work@company.com\"\n# ... backend config ...\n```\n\nSwitch accounts with `--account`:\n\n```bash\nhimalaya --account work envelope list\n```\n\n## Notmuch Backend (local mail)\n\n```toml\n[accounts.local]\nemail = \"user@example.com\"\n\nbackend.type = \"notmuch\"\nbackend.db-path = \"~/.mail/.notmuch\"\n```\n\n## OAuth2 Authentication (for providers that support it)\n\n```toml\nbackend.auth.type = \"oauth2\"\nbackend.auth.client-id = \"your-client-id\"\nbackend.auth.client-secret.cmd = \"pass show oauth/client-secret\"\nbackend.auth.access-token.cmd = \"pass show oauth/access-token\"\nbackend.auth.refresh-token.cmd = \"pass show oauth/refresh-token\"\nbackend.auth.auth-url = \"https://provider.com/oauth/authorize\"\nbackend.auth.token-url = \"https://provider.com/oauth/token\"\n```\n\n## Additional Options\n\n### Signature\n\n```toml\n[accounts.default]\nsignature = \"Best regards,\\nYour Name\"\nsignature-delim = \"-- \\n\"\n```\n\n### Downloads directory\n\n```toml\n[accounts.default]\ndownloads-dir = \"~/Downloads/himalaya\"\n```\n\n### Editor for composing\n\nSet via environment variable:\n\n```bash\nexport EDITOR=\"vim\"\n```\n\n<!-- File: skills/himalaya/references/message-composition.md (Tokens: ~991 | Category: anthropic-skill) -->\n# Message Composition with MML (MIME Meta Language)\n\nHimalaya uses MML for composing emails. MML is a simple XML-based syntax that compiles to MIME messages.\n\n## Basic Message Structure\n\nAn email message is a list of **headers** followed by a **body**, separated by a blank line:\n\n```\nFrom: sender@example.com\nTo: recipient@example.com\nSubject: Hello World\n\nThis is the message body.\n```\n\n## Headers\n\nCommon headers:\n\n- `From`: Sender address\n- `To`: Primary recipient(s)\n- `Cc`: Carbon copy recipients\n- `Bcc`: Blind carbon copy recipients\n- `Subject`: Message subject\n- `Reply-To`: Address for replies (if different from From)\n- `In-Reply-To`: Message ID being replied to\n\n### Address Formats\n\n```\nTo: user@example.com\nTo: John Doe <john@example.com>\nTo: \"John Doe\" <john@example.com>\nTo: user1@example.com, user2@example.com, \"Jane\" <jane@example.com>\n```\n\n## Plain Text Body\n\nSimple plain text email:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Plain Text Example\n\nHello, this is a plain text email.\nNo special formatting needed.\n\nBest,\nAlice\n```\n\n## MML for Rich Emails\n\n### Multipart Messages\n\nAlternative text/html parts:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Multipart Example\n\n<#multipart type=alternative>\nThis is the plain text version.\n<#part type=text/html>\n<html><body><h1>This is the HTML version</h1></body></html>\n<#/multipart>\n```\n\n### Attachments\n\nAttach a file:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: With Attachment\n\nHere is the document you requested.\n\n<#part filename=/path/to/document.pdf><#/part>\n```\n\nAttachment with custom name:\n\n```\n<#part filename=/path/to/file.pdf name=report.pdf><#/part>\n```\n\nMultiple attachments:\n\n```\n<#part filename=/path/to/doc1.pdf><#/part>\n<#part filename=/path/to/doc2.pdf><#/part>\n```\n\n### Inline Images\n\nEmbed an image inline:\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Inline Image\n\n<#multipart type=related>\n<#part type=text/html>\n<html><body>\n<p>Check out this image:</p>\n<img src=\"cid:image1\">\n</body></html>\n<#part disposition=inline id=image1 filename=/path/to/image.png><#/part>\n<#/multipart>\n```\n\n### Mixed Content (Text + Attachments)\n\n```\nFrom: alice@localhost\nTo: bob@localhost\nSubject: Mixed Content\n\n<#multipart type=mixed>\n<#part type=text/plain>\nPlease find the attached files.\n\nBest,\nAlice\n<#part filename=/path/to/file1.pdf><#/part>\n<#part filename=/path/to/file2.zip><#/part>\n<#/multipart>\n```\n\n## MML Tag Reference\n\n### `<#multipart>`\n\nGroups multiple parts together.\n\n- `type=alternative`: Different representations of same content\n- `type=mixed`: Independent parts (text + attachments)\n- `type=related`: Parts that reference each other (HTML + images)\n\n### `<#part>`\n\nDefines a message part.\n\n- `type=<mime-type>`: Content type (e.g., `text/html`, `application/pdf`)\n- `filename=<path>`: File to attach\n- `name=<name>`: Display name for attachment\n- `disposition=inline`: Display inline instead of as attachment\n- `id=<cid>`: Content ID for referencing in HTML\n\n## Composing from CLI\n\n### Interactive compose\n\nOpens your `$EDITOR`:\n\n```bash\nhimalaya message write\n```\n\n### Reply (opens editor with quoted message)\n\n```bash\nhimalaya message reply 42\nhimalaya message reply 42 --all  # reply-all\n```\n\n### Forward\n\n```bash\nhimalaya message forward 42\n```\n\n### Send from stdin\n\n```bash\ncat message.txt | himalaya template send\n```\n\n### Prefill headers from CLI\n\n```bash\nhimalaya message write \\\n  -H \"To:recipient@example.com\" \\\n  -H \"Subject:Quick Message\" \\\n  \"Message body here\"\n```\n\n## Tips\n\n- The editor opens with a template; fill in headers and body.\n- Save and exit the editor to send; exit without saving to cancel.\n- MML parts are compiled to proper MIME when sending.\n- Use `himalaya message export --full` to inspect the raw MIME structure of received emails.\n\n<!-- File: skills/himalaya/SKILL.md (Tokens: ~1113 | Category: anthropic-skill) -->\n---\nname: himalaya\ndescription: \"CLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).\"\nhomepage: https://github.com/pimalaya/himalaya\nmetadata:\n  {\n    \"emoji\": \"📧\",\n    \"requires\": { \"bins\": [\"himalaya\"] }\n  }\n---\n\n# Himalaya Email CLI\n\nHimalaya is a CLI email client that lets you manage emails from the terminal using IMAP, SMTP, Notmuch, or Sendmail backends.\n\n## References\n\n- `references/configuration.md` (config file setup + IMAP/SMTP authentication)\n- `references/message-composition.md` (MML syntax for composing emails)\n\n## Prerequisites\n\n1. Himalaya CLI installed (`himalaya --version` to verify)\n2. A configuration file at `~/.config/himalaya/config.toml`\n3. IMAP/SMTP credentials configured (password stored securely)\n\n## Configuration Setup\n\nRun the interactive wizard to set up an account:\n\n```bash\nhimalaya account configure\n```\n\nOr create `~/.config/himalaya/config.toml` manually:\n\n```toml\n[accounts.personal]\nemail = \"you@example.com\"\ndisplay-name = \"Your Name\"\ndefault = true\n\nbackend.type = \"imap\"\nbackend.host = \"imap.example.com\"\nbackend.port = 993\nbackend.encryption.type = \"tls\"\nbackend.login = \"you@example.com\"\nbackend.auth.type = \"password\"\nbackend.auth.cmd = \"pass show email/imap\"  # or use keyring\n\nmessage.send.backend.type = \"smtp\"\nmessage.send.backend.host = \"smtp.example.com\"\nmessage.send.backend.port = 587\nmessage.send.backend.encryption.type = \"start-tls\"\nmessage.send.backend.login = \"you@example.com\"\nmessage.send.backend.auth.type = \"password\"\nmessage.send.backend.auth.cmd = \"pass show email/smtp\"\n```\n\n## Common Operations\n\n### List Folders\n\n```bash\nhimalaya folder list\n```\n\n### List Emails\n\nList emails in INBOX (default):\n\n```bash\nhimalaya envelope list\n```\n\nList emails in a specific folder:\n\n```bash\nhimalaya envelope list --folder \"Sent\"\n```\n\nList with pagination:\n\n```bash\nhimalaya envelope list --page 1 --page-size 20\n```\n\n### Search Emails\n\n```bash\nhimalaya envelope list from john@example.com subject meeting\n```\n\n### Read an Email\n\nRead email by ID (shows plain text):\n\n```bash\nhimalaya message read 42\n```\n\nExport raw MIME:\n\n```bash\nhimalaya message export 42 --full\n```\n\n### Reply to an Email\n\nInteractive reply (opens $EDITOR):\n\n```bash\nhimalaya message reply 42\n```\n\nReply-all:\n\n```bash\nhimalaya message reply 42 --all\n```\n\n### Forward an Email\n\n```bash\nhimalaya message forward 42\n```\n\n### Write a New Email\n\nInteractive compose (opens $EDITOR):\n\n```bash\nhimalaya message write\n```\n\nSend directly using template:\n\n```bash\ncat << 'EOF' | himalaya template send\nFrom: you@example.com\nTo: recipient@example.com\nSubject: Test Message\n\nHello from Himalaya!\nEOF\n```\n\nOr with headers flag:\n\n```bash\nhimalaya message write -H \"To:recipient@example.com\" -H \"Subject:Test\" \"Message body here\"\n```\n\n### Move/Copy Emails\n\nMove to folder:\n\n```bash\nhimalaya message move 42 \"Archive\"\n```\n\nCopy to folder:\n\n```bash\nhimalaya message copy 42 \"Important\"\n```\n\n### Delete an Email\n\n```bash\nhimalaya message delete 42\n```\n\n### Manage Flags\n\nAdd flag:\n\n```bash\nhimalaya flag add 42 --flag seen\n```\n\nRemove flag:\n\n```bash\nhimalaya flag remove 42 --flag seen\n```\n\n## Multiple Accounts\n\nList accounts:\n\n```bash\nhimalaya account list\n```\n\nUse a specific account:\n\n```bash\nhimalaya --account work envelope list\n```\n\n## Attachments\n\nSave attachments from a message:\n\n```bash\nhimalaya attachment download 42\n```\n\nSave to specific directory:\n\n```bash\nhimalaya attachment download 42 --dir ~/Downloads\n```\n\n## Output Formats\n\nMost commands support `--output` for structured output:\n\n```bash\nhimalaya envelope list --output json\nhimalaya envelope list --output plain\n```\n\n## Debugging\n\nEnable debug logging:\n\n```bash\nRUST_LOG=debug himalaya envelope list\n```\n\nFull trace with backtrace:\n\n```bash\nRUST_LOG=trace RUST_BACKTRACE=1 himalaya envelope list\n```\n\n## Tips\n\n- Use `himalaya --help` or `himalaya <command> --help` for detailed usage.\n- Message IDs are relative to the current folder; re-list after folder changes.\n- For composing rich emails with attachments, use MML syntax (see `references/message-composition.md`).\n- Store passwords securely using `pass`, system keyring, or a command that outputs the password.\n\n<!-- File: skills/imsg/SKILL.md (Tokens: ~772 | Category: anthropic-skill) -->\n---\nname: imsg\ndescription: iMessage/SMS CLI for listing chats, history, and sending messages via Messages.app.\nhomepage: https://imsg.to\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📨\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"imsg\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/imsg\",\n              \"bins\": [\"imsg\"],\n              \"label\": \"Install imsg (brew)\",\n            },\n          ],\n      },\n  }\n---\n\n# imsg\n\nUse `imsg` to read and send iMessage/SMS via macOS Messages.app.\n\n## When to Use\n\n✅ **USE this skill when:**\n\n- User explicitly asks to send iMessage or SMS\n- Reading iMessage conversation history\n- Checking recent Messages.app chats\n- Sending to phone numbers or Apple IDs\n\n## When NOT to Use\n\n❌ **DON'T use this skill when:**\n\n- Telegram messages → use `message` tool with `channel:telegram`\n- Signal messages → use Signal channel if configured\n- WhatsApp messages → use WhatsApp channel if configured\n- Discord messages → use `message` tool with `channel:discord`\n- Slack messages → use `slack` skill\n- Group chat management (adding/removing members) → not supported\n- Bulk/mass messaging → always confirm with user first\n- Replying in current conversation → just reply normally (OpenClaw routes automatically)\n\n## Requirements\n\n- macOS with Messages.app signed in\n- Full Disk Access for terminal\n- Automation permission for Messages.app (for sending)\n\n## Common Commands\n\n### List Chats\n\n```bash\nimsg chats --limit 10 --json\n```\n\n### View History\n\n```bash\n# By chat ID\nimsg history --chat-id 1 --limit 20 --json\n\n# With attachments info\nimsg history --chat-id 1 --limit 20 --attachments --json\n```\n\n### Watch for New Messages\n\n```bash\nimsg watch --chat-id 1 --attachments\n```\n\n### Send Messages\n\n```bash\n# Text only\nimsg send --to \"+14155551212\" --text \"Hello!\"\n\n# With attachment\nimsg send --to \"+14155551212\" --text \"Check this out\" --file /path/to/image.jpg\n\n# Specify service\nimsg send --to \"+14155551212\" --text \"Hi\" --service imessage\nimsg send --to \"+14155551212\" --text \"Hi\" --service sms\n```\n\n## Service Options\n\n- `--service imessage` — Force iMessage (requires recipient has iMessage)\n- `--service sms` — Force SMS (green bubble)\n- `--service auto` — Let Messages.app decide (default)\n\n## Safety Rules\n\n1. **Always confirm recipient and message content** before sending\n2. **Never send to unknown numbers** without explicit user approval\n3. **Be careful with attachments** — confirm file path exists\n4. **Rate limit yourself** — don't spam\n\n## Example Workflow\n\nUser: \"Text mom that I'll be late\"\n\n```bash\n# 1. Find mom's chat\nimsg chats --limit 20 --json | jq '.[] | select(.displayName | contains(\"Mom\"))'\n\n# 2. Confirm with user\n# \"Found Mom at +1555123456. Send 'I'll be late' via iMessage?\"\n\n# 3. Send after confirmation\nimsg send --to \"+1555123456\" --text \"I'll be late\"\n```\n\n<!-- File: skills/mcporter/SKILL.md (Tokens: ~446 | Category: anthropic-skill) -->\n---\nname: mcporter\ndescription: Use the mcporter CLI to list, configure, auth, and call MCP servers/tools directly (HTTP or stdio), including ad-hoc servers, config edits, and CLI/type generation.\nhomepage: http://mcporter.dev\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📦\",\n        \"requires\": { \"bins\": [\"mcporter\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"node\",\n              \"kind\": \"node\",\n              \"package\": \"mcporter\",\n              \"bins\": [\"mcporter\"],\n              \"label\": \"Install mcporter (node)\",\n            },\n          ],\n      },\n  }\n---\n\n# mcporter\n\nUse `mcporter` to work with MCP servers directly.\n\nQuick start\n\n- `mcporter list`\n- `mcporter list <server> --schema`\n- `mcporter call <server.tool> key=value`\n\nCall tools\n\n- Selector: `mcporter call linear.list_issues team=ENG limit:5`\n- Function syntax: `mcporter call \"linear.create_issue(title: \\\"Bug\\\")\"`\n- Full URL: `mcporter call https://api.example.com/mcp.fetch url:https://example.com`\n- Stdio: `mcporter call --stdio \"bun run ./server.ts\" scrape url=https://example.com`\n- JSON payload: `mcporter call <server.tool> --args '{\"limit\":5}'`\n\nAuth + config\n\n- OAuth: `mcporter auth <server | url> [--reset]`\n- Config: `mcporter config list|get|add|remove|import|login|logout`\n\nDaemon\n\n- `mcporter daemon start|status|stop|restart`\n\nCodegen\n\n- CLI: `mcporter generate-cli --server <name>` or `--command <url>`\n- Inspect: `mcporter inspect-cli <path> [--json]`\n- TS: `mcporter emit-ts <server> --mode client|types`\n\nNotes\n\n- Config default: `./config/mcporter.json` (override with `--config`).\n- Prefer `--output json` for machine-readable results.\n\n<!-- File: skills/model-usage/references/codexbar-cli.md (Tokens: ~304 | Category: anthropic-skill) -->\n# CodexBar CLI quick ref (usage + cost)\n\n## Install\n\n- App: Preferences -> Advanced -> Install CLI\n- Repo: ./bin/install-codexbar-cli.sh\n\n## Commands\n\n- Usage snapshot (web/cli sources):\n  - codexbar usage --format json --pretty\n  - codexbar --provider all --format json\n- Local cost usage (Codex + Claude only):\n  - codexbar cost --format json --pretty\n  - codexbar cost --provider codex|claude --format json\n\n## Cost JSON fields\n\nThe payload is an array (one per provider).\n\n- provider, source, updatedAt\n- sessionTokens, sessionCostUSD\n- last30DaysTokens, last30DaysCostUSD\n- daily[]: date, inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, totalCost, modelsUsed, modelBreakdowns[]\n- modelBreakdowns[]: modelName, cost\n- totals: totalInputTokens, totalOutputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, totalCost\n\n## Notes\n\n- Cost usage is local-only. It reads JSONL logs under:\n  - Codex: ~/.codex/sessions/\\*_/_.jsonl\n  - Claude: ~/.config/claude/projects/**/\\*.jsonl or ~/.claude/projects/**/\\*.jsonl\n- If web usage is required (non-local), use codexbar usage (not cost).\n\n<!-- File: skills/model-usage/SKILL.md (Tokens: ~563 | Category: anthropic-skill) -->\n---\nname: model-usage\ndescription: Use CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📊\",\n        \"os\": [\"darwin\"],\n        \"requires\": { \"bins\": [\"codexbar\"] },\n        \"install\":\n          [\n            {\n              \"id\": \"brew-cask\",\n              \"kind\": \"brew\",\n              \"formula\": \"steipete/tap/codexbar\",\n              \"bins\": [\"codexbar\"],\n              \"label\": \"Install CodexBar (brew cask)\",\n            },\n          ],\n      },\n  }\n---\n\n# Model usage\n\n## Overview\n\nGet per-model usage cost from CodexBar's local cost logs. Supports \"current model\" (most recent daily entry) or \"all models\" summaries for Codex or Claude.\n\nTODO: add Linux CLI support guidance once CodexBar CLI install path is documented for Linux.\n\n## Quick start\n\n1. Fetch cost JSON via CodexBar CLI or pass a JSON file.\n2. Use the bundled script to summarize by model.\n\n```bash\npython {baseDir}/scripts/model_usage.py --provider codex --mode current\npython {baseDir}/scripts/model_usage.py --provider codex --mode all\npython {baseDir}/scripts/model_usage.py --provider claude --mode all --format json --pretty\n```\n\n## Current model logic\n\n- Uses the most recent daily row with `modelBreakdowns`.\n- Picks the model with the highest cost in that row.\n- Falls back to the last entry in `modelsUsed` when breakdowns are missing.\n- Override with `--model <name>` when you need a specific model.\n\n## Inputs\n\n- Default: runs `codexbar cost --format json --provider <codex|claude>`.\n- File or stdin:\n\n```bash\ncodexbar cost --provider codex --format json > /tmp/cost.json\npython {baseDir}/scripts/model_usage.py --input /tmp/cost.json --mode all\ncat /tmp/cost.json | python {baseDir}/scripts/model_usage.py --input - --mode current\n```\n\n## Output\n\n- Text (default) or JSON (`--format json --pretty`).\n- Values are cost-only per model; tokens are not split by model in CodexBar output.\n\n## References\n\n- Read `references/codexbar-cli.md` for CLI flags and cost JSON fields.\n\n<!-- ... +31 more modular skills available via /api/skills/the-open-agent/openagent?path={path} -->\n</yakaai_skills>"}