burtenshaw HF Staff commited on
Commit
e38a84f
·
verified ·
1 Parent(s): 403302c

Deploy agent-card demo

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Dockerfile +19 -0
  2. README.md +23 -4
  3. claw/AGENTS.md +212 -0
  4. package-lock.json +37 -0
  5. package.json +19 -0
  6. public/app.js +765 -0
  7. public/index.html +202 -0
  8. public/styles.css +825 -0
  9. skills/1password/SKILL.md +70 -0
  10. skills/apple-notes/SKILL.md +77 -0
  11. skills/apple-reminders/SKILL.md +118 -0
  12. skills/bear-notes/SKILL.md +107 -0
  13. skills/blogwatcher/SKILL.md +69 -0
  14. skills/blucli/SKILL.md +47 -0
  15. skills/bluebubbles/SKILL.md +131 -0
  16. skills/camsnap/SKILL.md +45 -0
  17. skills/canvas/SKILL.md +199 -0
  18. skills/clawhub/SKILL.md +77 -0
  19. skills/coding-agent/SKILL.md +316 -0
  20. skills/discord/SKILL.md +197 -0
  21. skills/eightctl/SKILL.md +50 -0
  22. skills/gemini/SKILL.md +43 -0
  23. skills/gh-issues/SKILL.md +885 -0
  24. skills/gifgrep/SKILL.md +79 -0
  25. skills/github/SKILL.md +163 -0
  26. skills/gog/SKILL.md +116 -0
  27. skills/goplaces/SKILL.md +52 -0
  28. skills/healthcheck/SKILL.md +245 -0
  29. skills/himalaya/SKILL.md +257 -0
  30. skills/imsg/SKILL.md +122 -0
  31. skills/mcporter/SKILL.md +61 -0
  32. skills/model-usage/SKILL.md +69 -0
  33. skills/nano-pdf/SKILL.md +38 -0
  34. skills/node-connect/SKILL.md +142 -0
  35. skills/notion/SKILL.md +174 -0
  36. skills/obsidian/SKILL.md +81 -0
  37. skills/openai-whisper-api/SKILL.md +62 -0
  38. skills/openai-whisper/SKILL.md +38 -0
  39. skills/openhue/SKILL.md +112 -0
  40. skills/oracle/SKILL.md +125 -0
  41. skills/ordercli/SKILL.md +78 -0
  42. skills/peekaboo/SKILL.md +190 -0
  43. skills/sag/SKILL.md +87 -0
  44. skills/session-logs/SKILL.md +151 -0
  45. skills/sherpa-onnx-tts/SKILL.md +109 -0
  46. skills/skill-creator/SKILL.md +372 -0
  47. skills/slack/SKILL.md +144 -0
  48. skills/songsee/SKILL.md +49 -0
  49. skills/sonoscli/SKILL.md +65 -0
  50. skills/spotify-player/SKILL.md +64 -0
Dockerfile ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:22-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY package.json package-lock.json ./
6
+ RUN npm ci --omit=dev && npm cache clean --force
7
+
8
+ COPY claw ./claw
9
+ COPY public ./public
10
+ COPY skills ./skills
11
+ COPY src ./src
12
+
13
+ ENV NODE_ENV=production
14
+ ENV HOST=0.0.0.0
15
+ ENV PORT=7860
16
+
17
+ EXPOSE 7860
18
+
19
+ CMD ["node", "src/server.js"]
README.md CHANGED
@@ -1,10 +1,29 @@
1
  ---
2
  title: Agent Card
3
- emoji:
4
- colorFrom: red
5
- colorTo: blue
6
  sdk: docker
 
7
  pinned: false
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: Agent Card
3
+ emoji: 🤗
4
+ colorFrom: yellow
5
+ colorTo: gray
6
  sdk: docker
7
+ app_port: 7860
8
  pinned: false
9
+ short_description: Hugging Face-style agent card demo for local coding agents.
10
  ---
11
 
12
+ # Agent Card
13
+
14
+ Prototype demo for an agent-card page inspired by the Hugging Face Hub model-card layout.
15
+
16
+ ## What it shows
17
+
18
+ - `claw/AGENTS.md` rendered as the main agent card
19
+ - skills discovered from local `SKILL.md` files
20
+ - a `Use this agent` flow with:
21
+ - inference engine install/run snippets
22
+ - agent-specific config snippets
23
+ - a `Use skills` flow that saves selected skills locally in the browser
24
+
25
+ ## Runtime notes
26
+
27
+ - This Space runs the demo server from `src/server.js`.
28
+ - The default reference model is `unsloth/gemma-4-26B-A4B-it-GGUF:Q4_K_M`.
29
+ - The app reads the bundled `claw/` and `skills/` directories shipped in the Space image.
claw/AGENTS.md ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AGENTS.md - Your Workspace
2
+
3
+ This folder is home. Treat it that way.
4
+
5
+ ## First Run
6
+
7
+ If `BOOTSTRAP.md` exists, that's your birth certificate. Follow it, figure out who you are, then delete it. You won't need it again.
8
+
9
+ ## Every Session
10
+
11
+ Before doing anything else:
12
+
13
+ 1. Read `SOUL.md` — this is who you are
14
+ 2. Read `USER.md` — this is who you're helping
15
+ 3. Read `memory/YYYY-MM-DD.md` (today + yesterday) for recent context
16
+ 4. **If in MAIN SESSION** (direct chat with your human): Also read `MEMORY.md`
17
+
18
+ Don't ask permission. Just do it.
19
+
20
+ ## Memory
21
+
22
+ You wake up fresh each session. These files are your continuity:
23
+
24
+ - **Daily notes:** `memory/YYYY-MM-DD.md` (create `memory/` if needed) — raw logs of what happened
25
+ - **Long-term:** `MEMORY.md` — your curated memories, like a human's long-term memory
26
+
27
+ Capture what matters. Decisions, context, things to remember. Skip the secrets unless asked to keep them.
28
+
29
+ ### 🧠 MEMORY.md - Your Long-Term Memory
30
+
31
+ - **ONLY load in main session** (direct chats with your human)
32
+ - **DO NOT load in shared contexts** (Discord, group chats, sessions with other people)
33
+ - This is for **security** — contains personal context that shouldn't leak to strangers
34
+ - You can **read, edit, and update** MEMORY.md freely in main sessions
35
+ - Write significant events, thoughts, decisions, opinions, lessons learned
36
+ - This is your curated memory — the distilled essence, not raw logs
37
+ - Over time, review your daily files and update MEMORY.md with what's worth keeping
38
+
39
+ ### 📝 Write It Down - No "Mental Notes"!
40
+
41
+ - **Memory is limited** — if you want to remember something, WRITE IT TO A FILE
42
+ - "Mental notes" don't survive session restarts. Files do.
43
+ - When someone says "remember this" → update `memory/YYYY-MM-DD.md` or relevant file
44
+ - When you learn a lesson → update AGENTS.md, TOOLS.md, or the relevant skill
45
+ - When you make a mistake → document it so future-you doesn't repeat it
46
+ - **Text > Brain** 📝
47
+
48
+ ## Safety
49
+
50
+ - Don't exfiltrate private data. Ever.
51
+ - Don't run destructive commands without asking.
52
+ - `trash` > `rm` (recoverable beats gone forever)
53
+ - When in doubt, ask.
54
+
55
+ ## External vs Internal
56
+
57
+ **Safe to do freely:**
58
+
59
+ - Read files, explore, organize, learn
60
+ - Search the web, check calendars
61
+ - Work within this workspace
62
+
63
+ **Ask first:**
64
+
65
+ - Sending emails, tweets, public posts
66
+ - Anything that leaves the machine
67
+ - Anything you're uncertain about
68
+
69
+ ## Group Chats
70
+
71
+ You have access to your human's stuff. That doesn't mean you _share_ their stuff. In groups, you're a participant — not their voice, not their proxy. Think before you speak.
72
+
73
+ ### 💬 Know When to Speak!
74
+
75
+ In group chats where you receive every message, be **smart about when to contribute**:
76
+
77
+ **Respond when:**
78
+
79
+ - Directly mentioned or asked a question
80
+ - You can add genuine value (info, insight, help)
81
+ - Something witty/funny fits naturally
82
+ - Correcting important misinformation
83
+ - Summarizing when asked
84
+
85
+ **Stay silent (HEARTBEAT_OK) when:**
86
+
87
+ - It's just casual banter between humans
88
+ - Someone already answered the question
89
+ - Your response would just be "yeah" or "nice"
90
+ - The conversation is flowing fine without you
91
+ - Adding a message would interrupt the vibe
92
+
93
+ **The human rule:** Humans in group chats don't respond to every single message. Neither should you. Quality > quantity. If you wouldn't send it in a real group chat with friends, don't send it.
94
+
95
+ **Avoid the triple-tap:** Don't respond multiple times to the same message with different reactions. One thoughtful response beats three fragments.
96
+
97
+ Participate, don't dominate.
98
+
99
+ ### 😊 React Like a Human!
100
+
101
+ On platforms that support reactions (Discord, Slack), use emoji reactions naturally:
102
+
103
+ **React when:**
104
+
105
+ - You appreciate something but don't need to reply (👍, ❤️, 🙌)
106
+ - Something made you laugh (😂, 💀)
107
+ - You find it interesting or thought-provoking (🤔, 💡)
108
+ - You want to acknowledge without interrupting the flow
109
+ - It's a simple yes/no or approval situation (✅, 👀)
110
+
111
+ **Why it matters:**
112
+ Reactions are lightweight social signals. Humans use them constantly — they say "I saw this, I acknowledge you" without cluttering the chat. You should too.
113
+
114
+ **Don't overdo it:** One reaction per message max. Pick the one that fits best.
115
+
116
+ ## Tools
117
+
118
+ Skills provide your tools. When you need one, check its `SKILL.md`. Keep local notes (camera names, SSH details, voice preferences) in `TOOLS.md`.
119
+
120
+ **🎭 Voice Storytelling:** If you have `sag` (ElevenLabs TTS), use voice for stories, movie summaries, and "storytime" moments! Way more engaging than walls of text. Surprise people with funny voices.
121
+
122
+ **📝 Platform Formatting:**
123
+
124
+ - **Discord/WhatsApp:** No markdown tables! Use bullet lists instead
125
+ - **Discord links:** Wrap multiple links in `<>` to suppress embeds: `<https://example.com>`
126
+ - **WhatsApp:** No headers — use **bold** or CAPS for emphasis
127
+
128
+ ## 💓 Heartbeats - Be Proactive!
129
+
130
+ When you receive a heartbeat poll (message matches the configured heartbeat prompt), don't just reply `HEARTBEAT_OK` every time. Use heartbeats productively!
131
+
132
+ Default heartbeat prompt:
133
+ `Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.`
134
+
135
+ You are free to edit `HEARTBEAT.md` with a short checklist or reminders. Keep it small to limit token burn.
136
+
137
+ ### Heartbeat vs Cron: When to Use Each
138
+
139
+ **Use heartbeat when:**
140
+
141
+ - Multiple checks can batch together (inbox + calendar + notifications in one turn)
142
+ - You need conversational context from recent messages
143
+ - Timing can drift slightly (every ~30 min is fine, not exact)
144
+ - You want to reduce API calls by combining periodic checks
145
+
146
+ **Use cron when:**
147
+
148
+ - Exact timing matters ("9:00 AM sharp every Monday")
149
+ - Task needs isolation from main session history
150
+ - You want a different model or thinking level for the task
151
+ - One-shot reminders ("remind me in 20 minutes")
152
+ - Output should deliver directly to a channel without main session involvement
153
+
154
+ **Tip:** Batch similar periodic checks into `HEARTBEAT.md` instead of creating multiple cron jobs. Use cron for precise schedules and standalone tasks.
155
+
156
+ **Things to check (rotate through these, 2-4 times per day):**
157
+
158
+ - **Emails** - Any urgent unread messages?
159
+ - **Calendar** - Upcoming events in next 24-48h?
160
+ - **Mentions** - Twitter/social notifications?
161
+ - **Weather** - Relevant if your human might go out?
162
+
163
+ **Track your checks** in `memory/heartbeat-state.json`:
164
+
165
+ ```json
166
+ {
167
+ "lastChecks": {
168
+ "email": 1703275200,
169
+ "calendar": 1703260800,
170
+ "weather": null
171
+ }
172
+ }
173
+ ```
174
+
175
+ **When to reach out:**
176
+
177
+ - Important email arrived
178
+ - Calendar event coming up (&lt;2h)
179
+ - Something interesting you found
180
+ - It's been >8h since you said anything
181
+
182
+ **When to stay quiet (HEARTBEAT_OK):**
183
+
184
+ - Late night (23:00-08:00) unless urgent
185
+ - Human is clearly busy
186
+ - Nothing new since last check
187
+ - You just checked &lt;30 minutes ago
188
+
189
+ **Proactive work you can do without asking:**
190
+
191
+ - Read and organize memory files
192
+ - Check on projects (git status, etc.)
193
+ - Update documentation
194
+ - Commit and push your own changes
195
+ - **Review and update MEMORY.md** (see below)
196
+
197
+ ### 🔄 Memory Maintenance (During Heartbeats)
198
+
199
+ Periodically (every few days), use a heartbeat to:
200
+
201
+ 1. Read through recent `memory/YYYY-MM-DD.md` files
202
+ 2. Identify significant events, lessons, or insights worth keeping long-term
203
+ 3. Update `MEMORY.md` with distilled learnings
204
+ 4. Remove outdated info from MEMORY.md that's no longer relevant
205
+
206
+ Think of it like a human reviewing their journal and updating their mental model. Daily files are raw notes; MEMORY.md is curated wisdom.
207
+
208
+ The goal: Be helpful without being annoying. Check in a few times a day, do useful background work, but respect quiet time.
209
+
210
+ ## Make It Yours
211
+
212
+ This is a starting point. Add your own conventions, style, and rules as you figure out what works.
package-lock.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "hf-launch",
3
+ "version": "0.1.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "hf-launch",
9
+ "version": "0.1.0",
10
+ "dependencies": {
11
+ "@huggingface/tasks": "^0.20.13",
12
+ "marked": "^16.0.0"
13
+ },
14
+ "bin": {
15
+ "hf-launch": "src/cli.js"
16
+ }
17
+ },
18
+ "node_modules/@huggingface/tasks": {
19
+ "version": "0.20.13",
20
+ "resolved": "https://registry.npmjs.org/@huggingface/tasks/-/tasks-0.20.13.tgz",
21
+ "integrity": "sha512-DrP7L8q6UMFJhRsstL5ELO/sxTL3UBZBY+JBKVUaJuORZ1AWtrUrML4hljm8zMuGHpRw9tw7BP2dbzp/+tuOAA==",
22
+ "license": "MIT"
23
+ },
24
+ "node_modules/marked": {
25
+ "version": "16.4.2",
26
+ "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz",
27
+ "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",
28
+ "license": "MIT",
29
+ "bin": {
30
+ "marked": "bin/marked.js"
31
+ },
32
+ "engines": {
33
+ "node": ">= 20"
34
+ }
35
+ }
36
+ }
37
+ }
package.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "hf-launch",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "description": "Prototype CLI for turning Hugging Face local-app metadata into agent onboarding artifacts.",
7
+ "bin": {
8
+ "hf-launch": "./src/cli.js"
9
+ },
10
+ "scripts": {
11
+ "demo": "node src/server.js",
12
+ "space:stage": "node scripts/stage-space.mjs",
13
+ "test": "node --test"
14
+ },
15
+ "dependencies": {
16
+ "@huggingface/tasks": "^0.20.13",
17
+ "marked": "^16.0.0"
18
+ }
19
+ }
public/app.js ADDED
@@ -0,0 +1,765 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const SKILLS_STORAGE_KEY = "hf-launch.demo.saved-skills";
2
+
3
+ const state = {
4
+ payload: null,
5
+ selectedRuntimeKey: null,
6
+ selectedAgentKey: null,
7
+ selectedRuntimeExampleKey: null,
8
+ selectedSkillPath: null,
9
+ savedSkillPaths: loadSavedSkillPaths(),
10
+ lastSkillsSaveMessage: ""
11
+ };
12
+
13
+ const elements = {
14
+ agentTitle: document.getElementById("agent-title"),
15
+ agentTitleInline: document.getElementById("agent-title-inline"),
16
+ agentSummary: document.getElementById("agent-summary"),
17
+ useAgentButton: document.getElementById("use-agent-button"),
18
+ cardTitle: document.getElementById("card-title"),
19
+ cardPathLabel: document.getElementById("card-path-label"),
20
+ cardPathPill: document.getElementById("card-path-pill"),
21
+ agentCard: document.getElementById("agent-card"),
22
+ modelName: document.getElementById("model-name"),
23
+ modelMeta: document.getElementById("model-meta"),
24
+ useSkillsButton: document.getElementById("use-skills-button"),
25
+ skillsTableBody: document.getElementById("skills-table-body"),
26
+ runtimeModal: document.getElementById("runtime-modal"),
27
+ closeModalButton: document.getElementById("close-modal-button"),
28
+ runtimeTabs: document.getElementById("runtime-tabs"),
29
+ agentTabs: document.getElementById("agent-tabs"),
30
+ runtimeMeta: document.getElementById("runtime-meta"),
31
+ runtimeExampleGroup: document.getElementById("runtime-example-group"),
32
+ runtimeExampleTabs: document.getElementById("runtime-example-tabs"),
33
+ runtimePanel: document.getElementById("runtime-panel"),
34
+ skillModal: document.getElementById("skill-modal"),
35
+ closeSkillModalButton: document.getElementById("close-skill-modal-button"),
36
+ skillModalTitle: document.getElementById("skill-modal-title"),
37
+ skillSummary: document.getElementById("skill-summary"),
38
+ skillMeta: document.getElementById("skill-meta"),
39
+ skillPathPill: document.getElementById("skill-path-pill"),
40
+ skillCardBody: document.getElementById("skill-card-body"),
41
+ useSkillsModal: document.getElementById("use-skills-modal"),
42
+ closeUseSkillsModalButton: document.getElementById("close-use-skills-modal-button"),
43
+ skillsPicker: document.getElementById("skills-picker"),
44
+ saveSkillsButton: document.getElementById("save-skills-button"),
45
+ skillsSaveStatus: document.getElementById("skills-save-status"),
46
+ codeCardTemplate: document.getElementById("code-card-template")
47
+ };
48
+
49
+ function loadSavedSkillPaths() {
50
+ try {
51
+ const rawValue = window.localStorage.getItem(SKILLS_STORAGE_KEY);
52
+ const parsed = rawValue ? JSON.parse(rawValue) : [];
53
+ return Array.isArray(parsed) ? parsed.filter((value) => typeof value === "string") : [];
54
+ } catch {
55
+ return [];
56
+ }
57
+ }
58
+
59
+ function persistSavedSkillPaths(paths) {
60
+ try {
61
+ window.localStorage.setItem(SKILLS_STORAGE_KEY, JSON.stringify(paths));
62
+ } catch {
63
+ // Ignore storage failures in the prototype.
64
+ }
65
+ }
66
+
67
+ function escapeHtml(value) {
68
+ return String(value)
69
+ .replace(/&/g, "&amp;")
70
+ .replace(/</g, "&lt;")
71
+ .replace(/>/g, "&gt;")
72
+ .replace(/"/g, "&quot;")
73
+ .replace(/'/g, "&#39;");
74
+ }
75
+
76
+ function humanize(value) {
77
+ return String(value)
78
+ .replace(/_/g, " ")
79
+ .replace(/-/g, " ")
80
+ .replace(/\b\w/g, (match) => match.toUpperCase());
81
+ }
82
+
83
+ function relativeFromRoot(absolutePath) {
84
+ if (!state.payload) {
85
+ return absolutePath;
86
+ }
87
+
88
+ const normalizedRoot = `${state.payload.workspaceRoot.replace(/\\/g, "/")}/`;
89
+ const normalizedPath = absolutePath.replace(/\\/g, "/");
90
+ return normalizedPath.startsWith(normalizedRoot) ? normalizedPath.slice(normalizedRoot.length) : normalizedPath;
91
+ }
92
+
93
+ function setExternalLinkBehavior(container) {
94
+ for (const link of container.querySelectorAll("a[href]")) {
95
+ if (link.getAttribute("href")?.startsWith("http")) {
96
+ link.target = "_blank";
97
+ link.rel = "noreferrer";
98
+ }
99
+ }
100
+ }
101
+
102
+ function copyText(button, text) {
103
+ navigator.clipboard.writeText(text).then(() => {
104
+ const originalLabel = button.textContent;
105
+ button.textContent = "Copied";
106
+ button.classList.add("is-copied");
107
+
108
+ window.setTimeout(() => {
109
+ button.textContent = originalLabel;
110
+ button.classList.remove("is-copied");
111
+ }, 1200);
112
+ });
113
+ }
114
+
115
+ function attachCopyButton(button, text) {
116
+ button.addEventListener("click", () => {
117
+ copyText(button, text);
118
+ });
119
+ }
120
+
121
+ function decorateCodeBlocks(container) {
122
+ for (const pre of container.querySelectorAll("pre")) {
123
+ if (pre.querySelector(".copy-button")) {
124
+ continue;
125
+ }
126
+
127
+ const button = document.createElement("button");
128
+ button.className = "copy-button pre-copy-button";
129
+ button.type = "button";
130
+ button.textContent = "Copy";
131
+ pre.appendChild(button);
132
+ attachCopyButton(button, pre.innerText.replace(/\nCopy$/, "").trimEnd());
133
+ }
134
+ }
135
+
136
+ function renderMetaList(items) {
137
+ return items
138
+ .map(
139
+ (item) => `
140
+ <div class="meta-row">
141
+ <dt>${escapeHtml(item.label)}</dt>
142
+ <dd>${item.value}</dd>
143
+ </div>
144
+ `
145
+ )
146
+ .join("");
147
+ }
148
+
149
+ function runtimeExampleKey(example) {
150
+ return example.title;
151
+ }
152
+
153
+ function labelRuntimeExample(example) {
154
+ const normalizedTitle = example.title.toLowerCase();
155
+ if (normalizedTitle.includes("winget") || normalizedTitle.includes("windows")) {
156
+ return "Windows";
157
+ }
158
+ if (normalizedTitle.includes("brew")) {
159
+ return "macOS";
160
+ }
161
+ if (normalizedTitle.includes("pip")) {
162
+ return "pip";
163
+ }
164
+ if (normalizedTitle.includes("source")) {
165
+ return "Source";
166
+ }
167
+ if (normalizedTitle.includes("binary")) {
168
+ return "Binary";
169
+ }
170
+
171
+ return humanize(
172
+ example.title
173
+ .replace(/^Install from\s+/i, "")
174
+ .replace(/^Use\s+/i, "")
175
+ .replace(/\s+and serve model$/i, "")
176
+ );
177
+ }
178
+
179
+ function syncBodyScrollLock() {
180
+ const hasOpenModal = [elements.runtimeModal, elements.skillModal, elements.useSkillsModal].some(
181
+ (modal) => modal && !modal.classList.contains("is-hidden")
182
+ );
183
+ document.body.style.overflow = hasOpenModal ? "hidden" : "";
184
+ }
185
+
186
+ function getActiveRuntime() {
187
+ return state.payload?.runtimes.find((runtime) => runtime.key === state.selectedRuntimeKey) ?? null;
188
+ }
189
+
190
+ function getActiveAgent() {
191
+ return state.payload?.agentOptions.find((agent) => agent.key === state.selectedAgentKey) ?? null;
192
+ }
193
+
194
+ function getActiveRuntimeExample(runtime = getActiveRuntime()) {
195
+ return runtime?.runtimeExamples?.find((example) => runtimeExampleKey(example) === state.selectedRuntimeExampleKey) ?? null;
196
+ }
197
+
198
+ function getActiveSkill() {
199
+ return state.payload?.skills.find((skill) => skill.relativePath === state.selectedSkillPath) ?? null;
200
+ }
201
+
202
+ function ensureSelections() {
203
+ if (!state.payload) {
204
+ return;
205
+ }
206
+
207
+ const availableRuntime = state.payload.runtimes.find((runtime) => runtime.key === state.selectedRuntimeKey);
208
+ const runtime = availableRuntime ?? state.payload.runtimes[0] ?? null;
209
+ state.selectedRuntimeKey = runtime?.key ?? null;
210
+
211
+ if (!runtime) {
212
+ state.selectedAgentKey = null;
213
+ state.selectedRuntimeExampleKey = null;
214
+ return;
215
+ }
216
+
217
+ const compatibleAgents = runtime.compatibleAgents ?? [];
218
+ if (!compatibleAgents.includes(state.selectedAgentKey)) {
219
+ state.selectedAgentKey = compatibleAgents.includes(state.payload.agent.key)
220
+ ? state.payload.agent.key
221
+ : compatibleAgents[0] ?? null;
222
+ }
223
+
224
+ const runtimeExamples = runtime.runtimeExamples ?? [];
225
+ const activeExample = runtimeExamples.find((example) => runtimeExampleKey(example) === state.selectedRuntimeExampleKey);
226
+ state.selectedRuntimeExampleKey = activeExample
227
+ ? runtimeExampleKey(activeExample)
228
+ : runtimeExamples[0]
229
+ ? runtimeExampleKey(runtimeExamples[0])
230
+ : null;
231
+ }
232
+
233
+ function renderHeader() {
234
+ const { agent } = state.payload;
235
+
236
+ document.title = `${agent.title} · Hugging Face`;
237
+ elements.agentTitle.textContent = agent.title;
238
+ elements.agentTitleInline.textContent = agent.title;
239
+ elements.agentSummary.textContent = agent.summary;
240
+ elements.cardTitle.textContent = agent.cardTitle;
241
+ elements.cardPathLabel.textContent = "Workspace file";
242
+ elements.cardPathPill.textContent = relativeFromRoot(agent.cardPath);
243
+ }
244
+
245
+ function renderModelMeta() {
246
+ const { model, runtimes } = state.payload;
247
+ elements.modelName.textContent = model.displayName;
248
+ elements.modelMeta.innerHTML = renderMetaList([
249
+ { label: "Repository", value: `<code>${escapeHtml(model.repoId)}</code>` },
250
+ { label: "Model ref", value: `<code>${escapeHtml(model.ref)}</code>` },
251
+ { label: "Pipeline", value: escapeHtml(humanize(model.pipelineTag)) },
252
+ { label: "Library", value: escapeHtml(model.libraryName) },
253
+ { label: "Engines", value: escapeHtml(runtimes.map((runtime) => runtime.label).join(", ")) }
254
+ ]);
255
+ }
256
+
257
+ function renderUseSkillsButton() {
258
+ const count = state.savedSkillPaths.length;
259
+ elements.useSkillsButton.textContent = count ? `Use skills (${count})` : "Use skills";
260
+ }
261
+
262
+ function openSkillModal(skillPath) {
263
+ state.selectedSkillPath = skillPath;
264
+ renderSkillModal();
265
+ elements.skillModal.classList.remove("is-hidden");
266
+ elements.skillModal.setAttribute("aria-hidden", "false");
267
+ syncBodyScrollLock();
268
+ }
269
+
270
+ function closeSkillModal() {
271
+ elements.skillModal.classList.add("is-hidden");
272
+ elements.skillModal.setAttribute("aria-hidden", "true");
273
+ syncBodyScrollLock();
274
+ }
275
+
276
+ function renderSkillsTable() {
277
+ elements.skillsTableBody.innerHTML = "";
278
+
279
+ for (const skill of state.payload.skills) {
280
+ const row = document.createElement("tr");
281
+ row.tabIndex = 0;
282
+ row.className = "skill-row";
283
+
284
+ if (state.savedSkillPaths.includes(skill.relativePath)) {
285
+ row.classList.add("is-saved");
286
+ }
287
+
288
+ const titleCell = document.createElement("td");
289
+ titleCell.textContent = skill.name;
290
+
291
+ const pathCell = document.createElement("td");
292
+ const pathCode = document.createElement("code");
293
+ pathCode.textContent = skill.relativePath;
294
+ pathCell.appendChild(pathCode);
295
+
296
+ row.append(titleCell, pathCell);
297
+ row.addEventListener("click", () => {
298
+ openSkillModal(skill.relativePath);
299
+ });
300
+ row.addEventListener("keydown", (event) => {
301
+ if (event.key === "Enter" || event.key === " ") {
302
+ event.preventDefault();
303
+ openSkillModal(skill.relativePath);
304
+ }
305
+ });
306
+
307
+ elements.skillsTableBody.appendChild(row);
308
+ }
309
+ }
310
+
311
+ function buildCodeCard({ kicker, title, target, content }) {
312
+ const fragment = elements.codeCardTemplate.content.cloneNode(true);
313
+ fragment.querySelector(".snippet-kicker").textContent = kicker;
314
+ fragment.querySelector(".snippet-title").textContent = title;
315
+ fragment.querySelector(".snippet-target").textContent = target ?? "";
316
+ fragment.querySelector("code").textContent = content;
317
+
318
+ const button = fragment.querySelector(".copy-button");
319
+ attachCopyButton(button, content);
320
+
321
+ return fragment;
322
+ }
323
+
324
+ function renderRuntimeTabs() {
325
+ elements.runtimeTabs.innerHTML = "";
326
+
327
+ for (const runtime of state.payload.runtimes) {
328
+ const button = document.createElement("button");
329
+ button.className = `selection-pill${runtime.key === state.selectedRuntimeKey ? " is-active" : ""}`;
330
+ button.type = "button";
331
+ button.textContent = runtime.label;
332
+ button.addEventListener("click", () => {
333
+ state.selectedRuntimeKey = runtime.key;
334
+ ensureSelections();
335
+ renderModal();
336
+ });
337
+ elements.runtimeTabs.appendChild(button);
338
+ }
339
+ }
340
+
341
+ function renderAgentTabs(runtime) {
342
+ elements.agentTabs.innerHTML = "";
343
+
344
+ for (const agent of state.payload.agentOptions) {
345
+ const compatible = runtime.compatibleAgents.includes(agent.key);
346
+ const button = document.createElement("button");
347
+ button.className = `selection-pill${agent.key === state.selectedAgentKey ? " is-active" : ""}`;
348
+ button.type = "button";
349
+ button.textContent = agent.label;
350
+ button.disabled = !compatible;
351
+ if (!compatible) {
352
+ button.classList.add("is-disabled");
353
+ }
354
+ button.addEventListener("click", () => {
355
+ if (!compatible) {
356
+ return;
357
+ }
358
+ state.selectedAgentKey = agent.key;
359
+ renderModal();
360
+ });
361
+ elements.agentTabs.appendChild(button);
362
+ }
363
+ }
364
+
365
+ function renderRuntimeExampleTabs(runtime) {
366
+ const runtimeExamples = runtime.runtimeExamples ?? [];
367
+ elements.runtimeExampleTabs.innerHTML = "";
368
+ elements.runtimeExampleGroup.hidden = runtimeExamples.length === 0;
369
+
370
+ for (const example of runtimeExamples) {
371
+ const key = runtimeExampleKey(example);
372
+ const button = document.createElement("button");
373
+ button.className = `selection-pill${key === state.selectedRuntimeExampleKey ? " is-active" : ""}`;
374
+ button.type = "button";
375
+ button.textContent = labelRuntimeExample(example);
376
+ button.addEventListener("click", () => {
377
+ state.selectedRuntimeExampleKey = key;
378
+ renderModal();
379
+ });
380
+ elements.runtimeExampleTabs.appendChild(button);
381
+ }
382
+ }
383
+
384
+ function renderRuntimeMeta(runtime, agent) {
385
+ elements.runtimeMeta.innerHTML = "";
386
+
387
+ const summary = document.createElement("p");
388
+ summary.className = "modal-summary";
389
+
390
+ const summaryParts = [];
391
+ if (agent) {
392
+ summaryParts.push(`${agent.label} uses ${runtime.label} through the agent's existing provider config.`);
393
+ }
394
+
395
+ if (!runtime.baseUrl) {
396
+ summaryParts.push("Add the runtime base URL first so hf-launch can render the final config.");
397
+ } else if (runtime.servedModelId === "__PROBE_V1_MODELS__") {
398
+ summaryParts.push(`Base URL: ${runtime.baseUrl}. Probe /v1/models after startup to confirm the served model id.`);
399
+ } else {
400
+ summaryParts.push(`Base URL: ${runtime.baseUrl}. Served model: ${runtime.servedModelId}.`);
401
+ }
402
+
403
+ summary.textContent = summaryParts.join(" ");
404
+ elements.runtimeMeta.appendChild(summary);
405
+
406
+ if (runtime.docsUrl) {
407
+ const link = document.createElement("a");
408
+ link.className = "inline-link";
409
+ link.href = runtime.docsUrl;
410
+ link.textContent = "Runtime docs";
411
+ elements.runtimeMeta.appendChild(link);
412
+ }
413
+
414
+ setExternalLinkBehavior(elements.runtimeMeta);
415
+ }
416
+
417
+ function renderWarnings(runtime) {
418
+ if (!runtime.warnings.length) {
419
+ return "";
420
+ }
421
+
422
+ return `
423
+ <section class="note-card">
424
+ <ul class="warning-list">
425
+ ${runtime.warnings.map((warning) => `<li>${escapeHtml(warning)}</li>`).join("")}
426
+ </ul>
427
+ </section>
428
+ `;
429
+ }
430
+
431
+ function renderModal() {
432
+ ensureSelections();
433
+ const runtime = getActiveRuntime();
434
+ const agent = getActiveAgent();
435
+ renderRuntimeTabs();
436
+
437
+ if (!runtime) {
438
+ elements.runtimeMeta.innerHTML = "";
439
+ elements.runtimeExampleTabs.innerHTML = "";
440
+ elements.runtimeExampleGroup.hidden = true;
441
+ elements.runtimePanel.innerHTML = `<p class="empty-state">No runtime metadata is available for this model.</p>`;
442
+ return;
443
+ }
444
+
445
+ renderAgentTabs(runtime);
446
+ renderRuntimeMeta(runtime, agent);
447
+ renderRuntimeExampleTabs(runtime);
448
+
449
+ const runtimeExample = getActiveRuntimeExample(runtime);
450
+ const agentOutput = runtime.agentOutputs[state.selectedAgentKey] ?? null;
451
+ elements.runtimePanel.innerHTML = renderWarnings(runtime);
452
+
453
+ const warningsFragment = document.createElement("div");
454
+ warningsFragment.innerHTML = elements.runtimePanel.innerHTML;
455
+ elements.runtimePanel.innerHTML = "";
456
+ if (warningsFragment.firstElementChild) {
457
+ elements.runtimePanel.appendChild(warningsFragment.firstElementChild);
458
+ }
459
+
460
+ if (runtimeExample) {
461
+ elements.runtimePanel.appendChild(
462
+ buildCodeCard({
463
+ kicker: "Inference engine",
464
+ title: runtimeExample.title,
465
+ target: "Run this locally before starting the agent",
466
+ content: runtimeExample.content
467
+ })
468
+ );
469
+ }
470
+
471
+ if (agentOutput?.canGenerateAgentConfig) {
472
+ for (const file of agentOutput.files) {
473
+ elements.runtimePanel.appendChild(
474
+ buildCodeCard({
475
+ kicker: "Agent config",
476
+ title: file.name,
477
+ target: file.targetPath,
478
+ content: file.content
479
+ })
480
+ );
481
+ }
482
+
483
+ for (const command of agentOutput.cliCommands) {
484
+ elements.runtimePanel.appendChild(
485
+ buildCodeCard({
486
+ kicker: "Agent helper",
487
+ title: command.title,
488
+ target: "Run after the backend is up",
489
+ content: command.content
490
+ })
491
+ );
492
+ }
493
+
494
+ if (!runtimeExample && !agentOutput.files.length && !agentOutput.cliCommands.length) {
495
+ const message = document.createElement("p");
496
+ message.className = "empty-state";
497
+ message.textContent = "No snippets are available for this selection yet.";
498
+ elements.runtimePanel.appendChild(message);
499
+ }
500
+ } else {
501
+ const message = document.createElement("p");
502
+ message.className = "empty-state";
503
+ message.textContent =
504
+ "This runtime still needs a concrete base URL before hf-launch can render the final agent config.";
505
+ elements.runtimePanel.appendChild(message);
506
+ }
507
+
508
+ const footnote = document.createElement("p");
509
+ footnote.className = "modal-footnote";
510
+ footnote.textContent =
511
+ "hf-launch writes provider config and onboarding helpers. It does not start the backend or launch the agent process yet.";
512
+ elements.runtimePanel.appendChild(footnote);
513
+ }
514
+
515
+ function openModal() {
516
+ elements.runtimeModal.classList.remove("is-hidden");
517
+ elements.runtimeModal.setAttribute("aria-hidden", "false");
518
+ syncBodyScrollLock();
519
+ }
520
+
521
+ function closeModal() {
522
+ elements.runtimeModal.classList.add("is-hidden");
523
+ elements.runtimeModal.setAttribute("aria-hidden", "true");
524
+ syncBodyScrollLock();
525
+ }
526
+
527
+ function renderSkillModal() {
528
+ const skill = getActiveSkill();
529
+ if (!skill) {
530
+ return;
531
+ }
532
+
533
+ elements.skillModalTitle.textContent = skill.name;
534
+ elements.skillSummary.textContent = skill.description;
535
+ elements.skillPathPill.textContent = skill.relativePath;
536
+
537
+ const metadataItems = [
538
+ skill.homepage
539
+ ? {
540
+ label: "Homepage",
541
+ value: `<a class="inline-link" href="${escapeHtml(skill.homepage)}">${escapeHtml(skill.homepage)}</a>`
542
+ }
543
+ : null,
544
+ {
545
+ label: "User",
546
+ value: skill.userInvocable ? "User invocable" : "Agent only"
547
+ },
548
+ {
549
+ label: "Tools",
550
+ value: skill.allowedTools.length
551
+ ? skill.allowedTools.map((tool) => `<code>${escapeHtml(tool)}</code>`).join(", ")
552
+ : "None declared"
553
+ },
554
+ {
555
+ label: "Bins",
556
+ value: skill.requiresBins.length
557
+ ? skill.requiresBins.map((entry) => `<code>${escapeHtml(entry)}</code>`).join(", ")
558
+ : "None declared"
559
+ },
560
+ {
561
+ label: "Env",
562
+ value: skill.requiresEnv.length
563
+ ? skill.requiresEnv.map((entry) => `<code>${escapeHtml(entry)}</code>`).join(", ")
564
+ : "None declared"
565
+ },
566
+ {
567
+ label: "Config",
568
+ value: skill.requiresConfig.length
569
+ ? skill.requiresConfig.map((entry) => `<code>${escapeHtml(entry)}</code>`).join(", ")
570
+ : "None declared"
571
+ },
572
+ {
573
+ label: "Install",
574
+ value: skill.install.length ? skill.install.map((entry) => escapeHtml(entry)).join(", ") : "None declared"
575
+ },
576
+ {
577
+ label: "OS",
578
+ value: skill.os.length ? skill.os.map((entry) => escapeHtml(entry)).join(", ") : "Not specified"
579
+ }
580
+ ].filter(Boolean);
581
+
582
+ elements.skillMeta.innerHTML = renderMetaList(metadataItems);
583
+ elements.skillCardBody.innerHTML = skill.bodyHtml;
584
+
585
+ decorateCodeBlocks(elements.skillCardBody);
586
+ setExternalLinkBehavior(elements.skillMeta);
587
+ setExternalLinkBehavior(elements.skillCardBody);
588
+ }
589
+
590
+ function updateSkillsSaveStatus() {
591
+ if (state.lastSkillsSaveMessage) {
592
+ elements.skillsSaveStatus.textContent = state.lastSkillsSaveMessage;
593
+ return;
594
+ }
595
+
596
+ elements.skillsSaveStatus.textContent = state.savedSkillPaths.length
597
+ ? `${state.savedSkillPaths.length} skill${state.savedSkillPaths.length === 1 ? "" : "s"} saved locally.`
598
+ : "Selections are stored in this browser on the current machine.";
599
+ }
600
+
601
+ function renderUseSkillsModal() {
602
+ elements.skillsPicker.innerHTML = "";
603
+
604
+ for (const skill of state.payload.skills) {
605
+ const label = document.createElement("label");
606
+ label.className = "skill-picker-row";
607
+
608
+ const input = document.createElement("input");
609
+ input.type = "checkbox";
610
+ input.name = "saved-skills";
611
+ input.value = skill.relativePath;
612
+ input.checked = state.savedSkillPaths.includes(skill.relativePath);
613
+
614
+ const copy = document.createElement("div");
615
+ copy.className = "skill-picker-copy";
616
+
617
+ const title = document.createElement("strong");
618
+ title.textContent = skill.name;
619
+
620
+ const description = document.createElement("p");
621
+ description.className = "skill-picker-description";
622
+ description.textContent = skill.description;
623
+
624
+ const path = document.createElement("code");
625
+ path.className = "skill-picker-path";
626
+ path.textContent = skill.relativePath;
627
+
628
+ copy.append(title, description, path);
629
+ label.append(input, copy);
630
+ elements.skillsPicker.appendChild(label);
631
+ }
632
+
633
+ updateSkillsSaveStatus();
634
+ }
635
+
636
+ function openUseSkillsModal() {
637
+ state.lastSkillsSaveMessage = "";
638
+ renderUseSkillsModal();
639
+ elements.useSkillsModal.classList.remove("is-hidden");
640
+ elements.useSkillsModal.setAttribute("aria-hidden", "false");
641
+ syncBodyScrollLock();
642
+ }
643
+
644
+ function closeUseSkillsModal() {
645
+ elements.useSkillsModal.classList.add("is-hidden");
646
+ elements.useSkillsModal.setAttribute("aria-hidden", "true");
647
+ syncBodyScrollLock();
648
+ }
649
+
650
+ function saveSkillsSelection() {
651
+ const selectedPaths = [...elements.skillsPicker.querySelectorAll('input[name="saved-skills"]:checked')]
652
+ .map((input) => input.value)
653
+ .sort((left, right) => left.localeCompare(right));
654
+
655
+ state.savedSkillPaths = selectedPaths;
656
+ state.lastSkillsSaveMessage = `${selectedPaths.length} skill${selectedPaths.length === 1 ? "" : "s"} saved locally.`;
657
+ persistSavedSkillPaths(selectedPaths);
658
+ updateSkillsSaveStatus();
659
+ renderUseSkillsButton();
660
+ renderSkillsTable();
661
+ }
662
+
663
+ function renderCard() {
664
+ const { agent } = state.payload;
665
+ elements.agentCard.innerHTML = agent.cardHtml;
666
+ decorateCodeBlocks(elements.agentCard);
667
+ setExternalLinkBehavior(elements.agentCard);
668
+ }
669
+
670
+ function renderApp() {
671
+ ensureSelections();
672
+ renderHeader();
673
+ renderCard();
674
+ renderModelMeta();
675
+ renderUseSkillsButton();
676
+ renderSkillsTable();
677
+
678
+ elements.useAgentButton.disabled = false;
679
+ elements.useAgentButton.textContent = "Use this agent";
680
+ }
681
+
682
+ function renderError(message) {
683
+ elements.agentTitle.textContent = "Demo failed to load";
684
+ elements.agentSummary.textContent = message;
685
+ elements.useAgentButton.disabled = true;
686
+ }
687
+
688
+ async function loadAgentCard() {
689
+ const response = await fetch("/api/agent-card");
690
+ if (!response.ok) {
691
+ const payload = await response.json().catch(() => ({ error: "Unknown server error" }));
692
+ throw new Error(payload.error ?? "Unknown server error");
693
+ }
694
+
695
+ state.payload = await response.json();
696
+ }
697
+
698
+ async function main() {
699
+ try {
700
+ await loadAgentCard();
701
+ renderApp();
702
+ } catch (error) {
703
+ renderError(error.message);
704
+ }
705
+ }
706
+
707
+ elements.useAgentButton.addEventListener("click", () => {
708
+ if (!state.payload) {
709
+ return;
710
+ }
711
+ openModal();
712
+ renderModal();
713
+ });
714
+
715
+ elements.useSkillsButton.addEventListener("click", () => {
716
+ if (!state.payload) {
717
+ return;
718
+ }
719
+ openUseSkillsModal();
720
+ });
721
+
722
+ elements.closeModalButton.addEventListener("click", closeModal);
723
+ elements.closeSkillModalButton.addEventListener("click", closeSkillModal);
724
+ elements.closeUseSkillsModalButton.addEventListener("click", closeUseSkillsModal);
725
+ elements.saveSkillsButton.addEventListener("click", saveSkillsSelection);
726
+
727
+ elements.runtimeModal.addEventListener("click", (event) => {
728
+ if (event.target === elements.runtimeModal) {
729
+ closeModal();
730
+ }
731
+ });
732
+
733
+ elements.skillModal.addEventListener("click", (event) => {
734
+ if (event.target === elements.skillModal) {
735
+ closeSkillModal();
736
+ }
737
+ });
738
+
739
+ elements.useSkillsModal.addEventListener("click", (event) => {
740
+ if (event.target === elements.useSkillsModal) {
741
+ closeUseSkillsModal();
742
+ }
743
+ });
744
+
745
+ document.addEventListener("keydown", (event) => {
746
+ if (event.key !== "Escape") {
747
+ return;
748
+ }
749
+
750
+ if (!elements.skillModal.classList.contains("is-hidden")) {
751
+ closeSkillModal();
752
+ return;
753
+ }
754
+
755
+ if (!elements.useSkillsModal.classList.contains("is-hidden")) {
756
+ closeUseSkillsModal();
757
+ return;
758
+ }
759
+
760
+ if (!elements.runtimeModal.classList.contains("is-hidden")) {
761
+ closeModal();
762
+ }
763
+ });
764
+
765
+ main();
public/index.html ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Hugging Face</title>
7
+ <link rel="stylesheet" href="/styles.css" />
8
+ </head>
9
+ <body>
10
+ <header class="site-nav">
11
+ <div class="site-nav-inner">
12
+ <div class="site-brand">
13
+ <img
14
+ class="site-brand-logo"
15
+ src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
16
+ alt=""
17
+ />
18
+ <span>Hugging Face</span>
19
+ </div>
20
+
21
+ <label class="site-search" aria-label="Search">
22
+ <span class="site-search-icon">⌕</span>
23
+ <input type="text" value="" placeholder="Search models, datasets, users..." readonly />
24
+ </label>
25
+
26
+ <nav class="site-links" aria-label="Primary">
27
+ <a href="#">Models</a>
28
+ <a href="#">Datasets</a>
29
+ <a href="#">Spaces</a>
30
+ <a href="#">Docs</a>
31
+ </nav>
32
+ </div>
33
+ </header>
34
+
35
+ <main class="page-shell">
36
+ <section class="repo-header">
37
+ <div class="repo-heading-row">
38
+ <div class="repo-heading-copy">
39
+ <p class="repo-breadcrumb">
40
+ <span>demo</span>
41
+ <span>/</span>
42
+ <span id="agent-title-inline">claw</span>
43
+ </p>
44
+ <h1 id="agent-title">Loading agent card...</h1>
45
+ <p class="repo-summary" id="agent-summary">
46
+ Loading the agent card, runtime metadata, and workspace skills.
47
+ </p>
48
+ </div>
49
+
50
+ <div class="repo-actions">
51
+ <button class="primary-button" id="use-agent-button" type="button" disabled>Use this agent</button>
52
+ </div>
53
+ </div>
54
+
55
+ <div class="repo-tabs">
56
+ <span class="repo-tab is-active">Agent card</span>
57
+ <span class="repo-tab" id="card-path-label"></span>
58
+ </div>
59
+ </section>
60
+
61
+ <section class="page-grid">
62
+ <section class="content-column">
63
+ <article class="content-card">
64
+ <div class="content-card-header">
65
+ <div>
66
+ <p class="content-kicker">Rendered from workspace</p>
67
+ <h2 id="card-title">AGENTS.md</h2>
68
+ </div>
69
+ <span class="subtle-path" id="card-path-pill"></span>
70
+ </div>
71
+
72
+ <div class="markdown-body" id="agent-card"></div>
73
+ </article>
74
+ </section>
75
+
76
+ <aside class="sidebar-column">
77
+ <section class="sidebar-card">
78
+ <p class="sidebar-kicker">Reference model</p>
79
+ <h2 id="model-name"></h2>
80
+ <div class="meta-list" id="model-meta"></div>
81
+ </section>
82
+
83
+ <section class="sidebar-card" id="skills">
84
+ <div class="sidebar-card-header">
85
+ <div>
86
+ <p class="sidebar-kicker">Skills</p>
87
+ <h2>skills</h2>
88
+ </div>
89
+ <button class="secondary-button" id="use-skills-button" type="button">Use skills</button>
90
+ </div>
91
+
92
+ <div class="skills-table-wrap">
93
+ <table class="skills-table">
94
+ <thead>
95
+ <tr>
96
+ <th scope="col">Title</th>
97
+ <th scope="col">Path</th>
98
+ </tr>
99
+ </thead>
100
+ <tbody id="skills-table-body"></tbody>
101
+ </table>
102
+ </div>
103
+ </section>
104
+ </aside>
105
+ </section>
106
+ </main>
107
+
108
+ <div class="modal-overlay is-hidden" id="runtime-modal" aria-hidden="true">
109
+ <div class="modal-shell" role="dialog" aria-modal="true" aria-labelledby="modal-title">
110
+ <header class="modal-header">
111
+ <div>
112
+ <p class="modal-kicker">Local usage</p>
113
+ <h2 id="modal-title">Use this agent</h2>
114
+ </div>
115
+ <button class="icon-button" id="close-modal-button" type="button" aria-label="Close">x</button>
116
+ </header>
117
+
118
+ <div class="selection-toolbar">
119
+ <section class="selection-group">
120
+ <p class="selection-label">Inference engine</p>
121
+ <div class="selection-pills" id="runtime-tabs"></div>
122
+ </section>
123
+
124
+ <section class="selection-group">
125
+ <p class="selection-label">Agent</p>
126
+ <div class="selection-pills" id="agent-tabs"></div>
127
+ </section>
128
+ </div>
129
+
130
+ <div class="modal-meta" id="runtime-meta"></div>
131
+ <section class="runtime-example-group" id="runtime-example-group">
132
+ <p class="selection-label">Install and run</p>
133
+ <div class="selection-pills" id="runtime-example-tabs"></div>
134
+ </section>
135
+ <div class="runtime-panel" id="runtime-panel"></div>
136
+ </div>
137
+ </div>
138
+
139
+ <div class="modal-overlay is-hidden" id="skill-modal" aria-hidden="true">
140
+ <div class="modal-shell modal-shell-skill" role="dialog" aria-modal="true" aria-labelledby="skill-modal-title">
141
+ <header class="modal-header">
142
+ <div>
143
+ <p class="modal-kicker">Skill card</p>
144
+ <h2 id="skill-modal-title">Skill</h2>
145
+ </div>
146
+ <button class="icon-button" id="close-skill-modal-button" type="button" aria-label="Close">x</button>
147
+ </header>
148
+
149
+ <p class="modal-summary" id="skill-summary"></p>
150
+ <div class="skill-meta-grid" id="skill-meta"></div>
151
+
152
+ <section class="skill-file-section">
153
+ <div class="content-card-header">
154
+ <div>
155
+ <p class="content-kicker">Workspace file</p>
156
+ <h3>SKILL.md</h3>
157
+ </div>
158
+ <span class="subtle-path" id="skill-path-pill"></span>
159
+ </div>
160
+
161
+ <div class="markdown-body skill-markdown" id="skill-card-body"></div>
162
+ </section>
163
+ </div>
164
+ </div>
165
+
166
+ <div class="modal-overlay is-hidden" id="use-skills-modal" aria-hidden="true">
167
+ <div class="modal-shell modal-shell-skills" role="dialog" aria-modal="true" aria-labelledby="use-skills-title">
168
+ <header class="modal-header">
169
+ <div>
170
+ <p class="modal-kicker">Local skills</p>
171
+ <h2 id="use-skills-title">Use skills</h2>
172
+ </div>
173
+ <button class="icon-button" id="close-use-skills-modal-button" type="button" aria-label="Close">x</button>
174
+ </header>
175
+
176
+ <p class="modal-summary">Select the skills you want to save locally for this demo.</p>
177
+ <div class="skills-picker" id="skills-picker"></div>
178
+
179
+ <div class="modal-actions">
180
+ <p class="modal-status" id="skills-save-status"></p>
181
+ <button class="primary-button" id="save-skills-button" type="button">Save locally</button>
182
+ </div>
183
+ </div>
184
+ </div>
185
+
186
+ <template id="code-card-template">
187
+ <section class="snippet-card">
188
+ <div class="snippet-card-header">
189
+ <div>
190
+ <p class="snippet-kicker"></p>
191
+ <h3 class="snippet-title"></h3>
192
+ </div>
193
+ <button class="copy-button" type="button">Copy</button>
194
+ </div>
195
+ <p class="snippet-target"></p>
196
+ <pre><code></code></pre>
197
+ </section>
198
+ </template>
199
+
200
+ <script type="module" src="/app.js"></script>
201
+ </body>
202
+ </html>
public/styles.css ADDED
@@ -0,0 +1,825 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --bg: #ffffff;
3
+ --surface: #ffffff;
4
+ --surface-muted: #f9fafb;
5
+ --surface-soft: #f3f4f6;
6
+ --border: #e5e7eb;
7
+ --border-strong: #d1d5db;
8
+ --text: #111827;
9
+ --muted: #6b7280;
10
+ --muted-strong: #4b5563;
11
+ --accent: #111827;
12
+ --accent-soft: #f3f4f6;
13
+ --shadow: 0 18px 48px rgba(15, 23, 42, 0.08);
14
+ --mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
15
+ --sans:
16
+ ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
17
+ }
18
+
19
+ * {
20
+ box-sizing: border-box;
21
+ }
22
+
23
+ html,
24
+ body {
25
+ margin: 0;
26
+ min-height: 100%;
27
+ }
28
+
29
+ body {
30
+ background: var(--bg);
31
+ color: var(--text);
32
+ font-family: var(--sans);
33
+ }
34
+
35
+ a {
36
+ color: inherit;
37
+ text-decoration: none;
38
+ }
39
+
40
+ button,
41
+ input,
42
+ textarea,
43
+ select {
44
+ font: inherit;
45
+ }
46
+
47
+ code,
48
+ pre {
49
+ font-family: var(--mono);
50
+ }
51
+
52
+ .site-nav {
53
+ position: sticky;
54
+ top: 0;
55
+ z-index: 20;
56
+ background: rgba(255, 255, 255, 0.94);
57
+ border-bottom: 1px solid var(--border);
58
+ backdrop-filter: blur(14px);
59
+ }
60
+
61
+ .site-nav-inner {
62
+ width: min(1440px, calc(100vw - 32px));
63
+ margin: 0 auto;
64
+ min-height: 72px;
65
+ display: grid;
66
+ gap: 18px;
67
+ grid-template-columns: auto minmax(280px, 1fr) auto;
68
+ align-items: center;
69
+ }
70
+
71
+ .site-brand {
72
+ display: inline-flex;
73
+ align-items: center;
74
+ gap: 10px;
75
+ font-size: 1.05rem;
76
+ font-weight: 700;
77
+ }
78
+
79
+ .site-brand-logo {
80
+ width: 28px;
81
+ height: 28px;
82
+ display: block;
83
+ }
84
+
85
+ .site-search {
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 10px;
89
+ height: 44px;
90
+ padding: 0 14px;
91
+ border: 1px solid var(--border);
92
+ border-radius: 14px;
93
+ background: var(--surface-muted);
94
+ color: var(--muted);
95
+ }
96
+
97
+ .site-search input {
98
+ width: 100%;
99
+ border: 0;
100
+ background: transparent;
101
+ color: var(--muted);
102
+ outline: 0;
103
+ }
104
+
105
+ .site-links {
106
+ display: flex;
107
+ align-items: center;
108
+ gap: 22px;
109
+ color: var(--muted-strong);
110
+ font-size: 0.96rem;
111
+ }
112
+
113
+ .page-shell {
114
+ width: min(1440px, calc(100vw - 32px));
115
+ margin: 0 auto;
116
+ padding: 24px 0 56px;
117
+ }
118
+
119
+ .repo-header {
120
+ padding-top: 10px;
121
+ }
122
+
123
+ .repo-heading-row {
124
+ display: flex;
125
+ justify-content: space-between;
126
+ gap: 24px;
127
+ align-items: flex-start;
128
+ }
129
+
130
+ .repo-breadcrumb {
131
+ display: flex;
132
+ align-items: center;
133
+ gap: 8px;
134
+ margin: 0 0 10px;
135
+ color: #94a3b8;
136
+ font-size: 1.05rem;
137
+ font-weight: 600;
138
+ }
139
+
140
+ .repo-heading-copy h1 {
141
+ margin: 0;
142
+ font-size: clamp(2rem, 3vw, 2.7rem);
143
+ line-height: 1.02;
144
+ letter-spacing: -0.03em;
145
+ }
146
+
147
+ .repo-summary {
148
+ max-width: 72ch;
149
+ margin: 14px 0 0;
150
+ color: var(--muted-strong);
151
+ line-height: 1.65;
152
+ }
153
+
154
+ .repo-actions {
155
+ display: flex;
156
+ align-items: center;
157
+ gap: 10px;
158
+ }
159
+
160
+ .primary-button,
161
+ .secondary-button,
162
+ .copy-button,
163
+ .selection-pill,
164
+ .icon-button {
165
+ border: 1px solid transparent;
166
+ cursor: pointer;
167
+ transition:
168
+ background 120ms ease,
169
+ border-color 120ms ease,
170
+ color 120ms ease,
171
+ transform 120ms ease;
172
+ }
173
+
174
+ .primary-button {
175
+ height: 40px;
176
+ padding: 0 16px;
177
+ border-radius: 12px;
178
+ background: linear-gradient(180deg, #1f2937 0%, #111827 100%);
179
+ color: #ffffff;
180
+ font-weight: 600;
181
+ box-shadow: 0 8px 20px rgba(17, 24, 39, 0.18);
182
+ }
183
+
184
+ .secondary-button {
185
+ min-height: 36px;
186
+ padding: 0 14px;
187
+ border-radius: 10px;
188
+ border-color: var(--border);
189
+ background: var(--surface-muted);
190
+ color: var(--text);
191
+ font-weight: 600;
192
+ }
193
+
194
+ .primary-button:disabled {
195
+ cursor: wait;
196
+ opacity: 0.5;
197
+ box-shadow: none;
198
+ }
199
+
200
+ .primary-button:not(:disabled):hover,
201
+ .secondary-button:hover,
202
+ .copy-button:hover,
203
+ .selection-pill:hover,
204
+ .icon-button:hover {
205
+ transform: translateY(-1px);
206
+ }
207
+
208
+ .modal-meta {
209
+ display: grid;
210
+ gap: 10px;
211
+ }
212
+
213
+ .repo-tabs {
214
+ display: flex;
215
+ align-items: center;
216
+ gap: 24px;
217
+ margin-top: 22px;
218
+ border-bottom: 1px solid var(--border);
219
+ }
220
+
221
+ .repo-tab {
222
+ position: relative;
223
+ display: inline-flex;
224
+ align-items: center;
225
+ min-height: 48px;
226
+ color: var(--muted);
227
+ font-weight: 600;
228
+ }
229
+
230
+ .repo-tab.is-active {
231
+ color: var(--text);
232
+ }
233
+
234
+ .repo-tab.is-active::after {
235
+ content: "";
236
+ position: absolute;
237
+ left: 0;
238
+ right: 0;
239
+ bottom: -1px;
240
+ height: 2px;
241
+ background: var(--text);
242
+ }
243
+
244
+ .page-grid {
245
+ display: grid;
246
+ gap: 32px;
247
+ grid-template-columns: minmax(0, 1.55fr) minmax(300px, 0.85fr);
248
+ align-items: start;
249
+ margin-top: 0;
250
+ }
251
+
252
+ .content-column {
253
+ min-width: 0;
254
+ }
255
+
256
+ .content-card,
257
+ .sidebar-card,
258
+ .snippet-card,
259
+ .note-card,
260
+ .modal-shell {
261
+ background: var(--surface);
262
+ border: 1px solid var(--border);
263
+ box-shadow: var(--shadow);
264
+ }
265
+
266
+ .content-card {
267
+ border-top: 0;
268
+ border-radius: 0 0 18px 18px;
269
+ padding: 24px 28px 30px;
270
+ }
271
+
272
+ .content-card-header,
273
+ .sidebar-card-header,
274
+ .modal-header,
275
+ .snippet-card-header {
276
+ display: flex;
277
+ justify-content: space-between;
278
+ gap: 18px;
279
+ align-items: flex-start;
280
+ }
281
+
282
+ .content-kicker,
283
+ .sidebar-kicker,
284
+ .modal-kicker,
285
+ .snippet-kicker,
286
+ .selection-label {
287
+ margin: 0 0 6px;
288
+ color: var(--muted);
289
+ font-size: 0.78rem;
290
+ font-weight: 700;
291
+ letter-spacing: 0.04em;
292
+ text-transform: uppercase;
293
+ }
294
+
295
+ .content-card-header h2,
296
+ .sidebar-card h2,
297
+ .modal-header h2,
298
+ .snippet-title {
299
+ margin: 0;
300
+ font-size: 1.15rem;
301
+ line-height: 1.15;
302
+ }
303
+
304
+ .subtle-path {
305
+ display: inline-flex;
306
+ align-items: center;
307
+ min-height: 32px;
308
+ padding: 0 12px;
309
+ border: 1px solid var(--border);
310
+ border-radius: 999px;
311
+ background: var(--surface-muted);
312
+ color: var(--muted);
313
+ font-size: 0.82rem;
314
+ }
315
+
316
+ .sidebar-column {
317
+ display: grid;
318
+ gap: 18px;
319
+ position: sticky;
320
+ top: 92px;
321
+ }
322
+
323
+ .sidebar-card {
324
+ border-radius: 18px;
325
+ padding: 18px;
326
+ }
327
+
328
+ .meta-list {
329
+ display: grid;
330
+ gap: 10px;
331
+ margin-top: 16px;
332
+ }
333
+
334
+ .meta-row {
335
+ display: grid;
336
+ grid-template-columns: 96px 1fr;
337
+ gap: 12px;
338
+ font-size: 0.93rem;
339
+ }
340
+
341
+ .meta-row dt {
342
+ color: var(--muted);
343
+ font-weight: 600;
344
+ }
345
+
346
+ .meta-row dd {
347
+ margin: 0;
348
+ color: var(--text);
349
+ }
350
+
351
+ .meta-row code {
352
+ font-size: 0.9em;
353
+ }
354
+
355
+ .skills-table-wrap {
356
+ max-height: 640px;
357
+ margin-top: 14px;
358
+ overflow: auto;
359
+ border: 1px solid var(--border);
360
+ border-radius: 14px;
361
+ }
362
+
363
+ .skills-table {
364
+ width: 100%;
365
+ border-collapse: collapse;
366
+ }
367
+
368
+ .skills-table thead th {
369
+ position: sticky;
370
+ top: 0;
371
+ z-index: 1;
372
+ padding: 10px 14px;
373
+ background: var(--surface-muted);
374
+ border-bottom: 1px solid var(--border);
375
+ color: var(--muted);
376
+ font-size: 0.76rem;
377
+ font-weight: 700;
378
+ letter-spacing: 0.04em;
379
+ text-align: left;
380
+ text-transform: uppercase;
381
+ }
382
+
383
+ .skills-table tbody td {
384
+ padding: 11px 14px;
385
+ border-bottom: 1px solid var(--border);
386
+ font-size: 0.94rem;
387
+ }
388
+
389
+ .skills-table tbody td code {
390
+ padding: 0;
391
+ background: transparent;
392
+ color: var(--muted-strong);
393
+ font-size: 0.86rem;
394
+ }
395
+
396
+ .skills-table tbody tr {
397
+ cursor: pointer;
398
+ outline: none;
399
+ transition: background 120ms ease;
400
+ }
401
+
402
+ .skills-table tbody tr:hover,
403
+ .skills-table tbody tr:focus-visible {
404
+ background: var(--surface-muted);
405
+ }
406
+
407
+ .skills-table tbody tr.is-saved {
408
+ background: #fff8eb;
409
+ }
410
+
411
+ .skills-table tbody tr:last-child td {
412
+ border-bottom: 0;
413
+ }
414
+
415
+ .markdown-body {
416
+ margin-top: 22px;
417
+ color: var(--text);
418
+ font-size: 0.98rem;
419
+ line-height: 1.74;
420
+ }
421
+
422
+ .markdown-body > :first-child {
423
+ margin-top: 0;
424
+ }
425
+
426
+ .markdown-body > :last-child {
427
+ margin-bottom: 0;
428
+ }
429
+
430
+ .markdown-body h1,
431
+ .markdown-body h2,
432
+ .markdown-body h3 {
433
+ margin: 1.7em 0 0.7em;
434
+ font-size: 1.35rem;
435
+ line-height: 1.18;
436
+ }
437
+
438
+ .markdown-body h1 {
439
+ font-size: 1.6rem;
440
+ }
441
+
442
+ .markdown-body p,
443
+ .markdown-body ul,
444
+ .markdown-body ol,
445
+ .markdown-body blockquote {
446
+ margin: 0 0 1.05em;
447
+ }
448
+
449
+ .markdown-body ul,
450
+ .markdown-body ol {
451
+ padding-left: 1.4em;
452
+ }
453
+
454
+ .markdown-body code {
455
+ padding: 0.12em 0.35em;
456
+ border-radius: 6px;
457
+ background: var(--surface-soft);
458
+ font-size: 0.92em;
459
+ }
460
+
461
+ .markdown-body pre,
462
+ .snippet-card pre {
463
+ position: relative;
464
+ margin: 0;
465
+ padding: 18px;
466
+ overflow: auto;
467
+ border-radius: 14px;
468
+ background: #0f172a;
469
+ color: #e5edf8;
470
+ font-size: 0.88rem;
471
+ line-height: 1.65;
472
+ }
473
+
474
+ .markdown-body pre {
475
+ margin: 0 0 1.2em;
476
+ }
477
+
478
+ .markdown-body pre code,
479
+ .snippet-card pre code {
480
+ padding: 0;
481
+ background: transparent;
482
+ color: inherit;
483
+ }
484
+
485
+ .markdown-body blockquote {
486
+ padding-left: 16px;
487
+ border-left: 3px solid var(--border-strong);
488
+ color: var(--muted-strong);
489
+ }
490
+
491
+ .modal-overlay {
492
+ position: fixed;
493
+ inset: 0;
494
+ z-index: 30;
495
+ display: grid;
496
+ place-items: center;
497
+ padding: 24px;
498
+ background: rgba(15, 23, 42, 0.38);
499
+ backdrop-filter: blur(10px);
500
+ }
501
+
502
+ .modal-overlay.is-hidden {
503
+ display: none;
504
+ }
505
+
506
+ .modal-shell {
507
+ width: min(1120px, 100%);
508
+ max-height: calc(100vh - 48px);
509
+ overflow: auto;
510
+ border-radius: 20px;
511
+ padding: 22px;
512
+ }
513
+
514
+ .icon-button {
515
+ width: 38px;
516
+ height: 38px;
517
+ border-radius: 12px;
518
+ background: var(--surface-muted);
519
+ color: var(--text);
520
+ }
521
+
522
+ .selection-toolbar {
523
+ display: grid;
524
+ gap: 18px;
525
+ margin-top: 18px;
526
+ padding: 16px 0 18px;
527
+ border-top: 1px solid var(--border);
528
+ border-bottom: 1px solid var(--border);
529
+ }
530
+
531
+ .selection-group {
532
+ display: grid;
533
+ gap: 10px;
534
+ }
535
+
536
+ .selection-pills {
537
+ display: flex;
538
+ flex-wrap: wrap;
539
+ gap: 10px;
540
+ }
541
+
542
+ .runtime-example-group {
543
+ display: grid;
544
+ gap: 10px;
545
+ margin-top: 18px;
546
+ }
547
+
548
+ .runtime-example-group[hidden] {
549
+ display: none;
550
+ }
551
+
552
+ .selection-pill {
553
+ min-height: 34px;
554
+ padding: 0 12px;
555
+ border-radius: 999px;
556
+ border-color: var(--border);
557
+ background: var(--surface);
558
+ color: var(--muted-strong);
559
+ font-size: 0.92rem;
560
+ }
561
+
562
+ .selection-pill.is-active {
563
+ border-color: #111827;
564
+ background: #111827;
565
+ color: #ffffff;
566
+ }
567
+
568
+ .selection-pill.is-disabled,
569
+ .selection-pill:disabled {
570
+ cursor: not-allowed;
571
+ opacity: 0.45;
572
+ }
573
+
574
+ .modal-meta {
575
+ margin-top: 16px;
576
+ }
577
+
578
+ .inline-link {
579
+ display: inline-flex;
580
+ align-items: center;
581
+ color: var(--muted-strong);
582
+ font-weight: 600;
583
+ }
584
+
585
+ .inline-link:hover {
586
+ color: var(--text);
587
+ }
588
+
589
+ .modal-summary {
590
+ margin: 0;
591
+ color: var(--muted-strong);
592
+ line-height: 1.6;
593
+ }
594
+
595
+ .modal-meta .inline-link {
596
+ width: fit-content;
597
+ margin-top: -2px;
598
+ }
599
+
600
+ .skill-meta-grid {
601
+ display: grid;
602
+ gap: 10px;
603
+ margin-top: 18px;
604
+ padding: 18px 0;
605
+ border-top: 1px solid var(--border);
606
+ border-bottom: 1px solid var(--border);
607
+ }
608
+
609
+ .skill-file-section {
610
+ margin-top: 18px;
611
+ }
612
+
613
+ .skills-picker {
614
+ display: grid;
615
+ gap: 12px;
616
+ margin-top: 18px;
617
+ }
618
+
619
+ .skill-picker-row {
620
+ display: grid;
621
+ grid-template-columns: auto minmax(0, 1fr);
622
+ gap: 12px;
623
+ align-items: flex-start;
624
+ padding: 14px;
625
+ border: 1px solid var(--border);
626
+ border-radius: 14px;
627
+ background: var(--surface-muted);
628
+ }
629
+
630
+ .skill-picker-row input {
631
+ margin-top: 2px;
632
+ }
633
+
634
+ .skill-picker-copy {
635
+ display: grid;
636
+ gap: 6px;
637
+ min-width: 0;
638
+ }
639
+
640
+ .skill-picker-copy strong {
641
+ font-size: 0.96rem;
642
+ }
643
+
644
+ .skill-picker-description,
645
+ .modal-status {
646
+ margin: 0;
647
+ color: var(--muted-strong);
648
+ line-height: 1.55;
649
+ }
650
+
651
+ .skill-picker-path {
652
+ display: inline-block;
653
+ width: fit-content;
654
+ padding: 0;
655
+ background: transparent;
656
+ color: var(--muted);
657
+ font-size: 0.86rem;
658
+ }
659
+
660
+ .skill-markdown {
661
+ margin-top: 18px;
662
+ }
663
+
664
+ .modal-shell-skill .content-card-header h3 {
665
+ margin: 0;
666
+ font-size: 1.05rem;
667
+ line-height: 1.2;
668
+ }
669
+
670
+ .modal-shell-skill .subtle-path {
671
+ max-width: 100%;
672
+ overflow: hidden;
673
+ text-overflow: ellipsis;
674
+ white-space: nowrap;
675
+ }
676
+
677
+ .modal-shell-skill .meta-row {
678
+ grid-template-columns: 110px 1fr;
679
+ }
680
+
681
+ .modal-link {
682
+ display: inline-flex;
683
+ align-items: center;
684
+ min-height: 32px;
685
+ padding: 0 12px;
686
+ border: 1px solid var(--border);
687
+ border-radius: 999px;
688
+ color: var(--muted-strong);
689
+ }
690
+
691
+ .runtime-panel {
692
+ display: grid;
693
+ gap: 14px;
694
+ margin-top: 18px;
695
+ }
696
+
697
+ .modal-actions {
698
+ display: flex;
699
+ justify-content: space-between;
700
+ align-items: center;
701
+ gap: 16px;
702
+ margin-top: 18px;
703
+ }
704
+
705
+ .snippet-card,
706
+ .note-card {
707
+ border-radius: 16px;
708
+ padding: 16px;
709
+ }
710
+
711
+ .snippet-target {
712
+ margin: 8px 0 12px;
713
+ color: var(--muted);
714
+ font-size: 0.9rem;
715
+ line-height: 1.5;
716
+ }
717
+
718
+ .copy-button {
719
+ min-height: 34px;
720
+ padding: 0 12px;
721
+ border-radius: 10px;
722
+ border-color: var(--border);
723
+ background: var(--surface-muted);
724
+ color: var(--text);
725
+ font-weight: 600;
726
+ }
727
+
728
+ .copy-button.is-copied {
729
+ background: #111827;
730
+ border-color: #111827;
731
+ color: #ffffff;
732
+ }
733
+
734
+ .pre-copy-button {
735
+ position: absolute;
736
+ top: 12px;
737
+ right: 12px;
738
+ }
739
+
740
+ .warning-list {
741
+ margin: 0;
742
+ padding-left: 1.2em;
743
+ color: var(--muted-strong);
744
+ line-height: 1.6;
745
+ }
746
+
747
+ .empty-state,
748
+ .modal-footnote {
749
+ margin: 0;
750
+ padding: 14px 16px;
751
+ border: 1px solid var(--border);
752
+ border-radius: 14px;
753
+ background: var(--surface-muted);
754
+ color: var(--muted-strong);
755
+ line-height: 1.6;
756
+ }
757
+
758
+ .modal-footnote {
759
+ font-size: 0.92rem;
760
+ }
761
+
762
+ @media (max-width: 1100px) {
763
+ .page-grid {
764
+ grid-template-columns: 1fr;
765
+ }
766
+
767
+ .sidebar-column {
768
+ position: static;
769
+ }
770
+ }
771
+
772
+ @media (max-width: 920px) {
773
+ .site-nav-inner {
774
+ grid-template-columns: 1fr;
775
+ padding: 14px 0;
776
+ }
777
+
778
+ .site-links {
779
+ flex-wrap: wrap;
780
+ gap: 14px;
781
+ }
782
+
783
+ .repo-heading-row {
784
+ flex-direction: column;
785
+ }
786
+
787
+ .repo-actions {
788
+ width: 100%;
789
+ }
790
+ }
791
+
792
+ @media (max-width: 720px) {
793
+ .page-shell,
794
+ .site-nav-inner {
795
+ width: min(100vw - 20px, 1440px);
796
+ }
797
+
798
+ .content-card,
799
+ .sidebar-card,
800
+ .modal-shell {
801
+ padding: 16px;
802
+ }
803
+
804
+ .content-card {
805
+ padding-top: 18px;
806
+ }
807
+
808
+ .repo-tabs,
809
+ .content-card-header,
810
+ .sidebar-card-header,
811
+ .modal-header,
812
+ .snippet-card-header,
813
+ .modal-actions {
814
+ flex-direction: column;
815
+ }
816
+
817
+ .meta-row {
818
+ grid-template-columns: 1fr;
819
+ gap: 4px;
820
+ }
821
+
822
+ .modal-overlay {
823
+ padding: 12px;
824
+ }
825
+ }
skills/1password/SKILL.md ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: 1password
3
+ 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.
4
+ homepage: https://developer.1password.com/docs/cli/get-started/
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🔐",
10
+ "requires": { "bins": ["op"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "1password-cli",
17
+ "bins": ["op"],
18
+ "label": "Install 1Password CLI (brew)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # 1Password CLI
26
+
27
+ Follow the official CLI get-started steps. Don't guess install commands.
28
+
29
+ ## References
30
+
31
+ - `references/get-started.md` (install + app integration + sign-in flow)
32
+ - `references/cli-examples.md` (real `op` examples)
33
+
34
+ ## Workflow
35
+
36
+ 1. Check OS + shell.
37
+ 2. Verify CLI present: `op --version`.
38
+ 3. Confirm desktop app integration is enabled (per get-started) and the app is unlocked.
39
+ 4. REQUIRED: create a fresh tmux session for all `op` commands (no direct `op` calls outside tmux).
40
+ 5. Sign in / authorize inside tmux: `op signin` (expect app prompt).
41
+ 6. Verify access inside tmux: `op whoami` (must succeed before any secret read).
42
+ 7. If multiple accounts: use `--account` or `OP_ACCOUNT`.
43
+
44
+ ## REQUIRED tmux session (T-Max)
45
+
46
+ The 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.
47
+
48
+ Example (see `tmux` skill for socket conventions, do not reuse old session names):
49
+
50
+ ```bash
51
+ SOCKET_DIR="${OPENCLAW_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/openclaw-tmux-sockets}"
52
+ mkdir -p "$SOCKET_DIR"
53
+ SOCKET="$SOCKET_DIR/openclaw-op.sock"
54
+ SESSION="op-auth-$(date +%Y%m%d-%H%M%S)"
55
+
56
+ tmux -S "$SOCKET" new -d -s "$SESSION" -n shell
57
+ tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- "op signin --account my.1password.com" Enter
58
+ tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- "op whoami" Enter
59
+ tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- "op vault list" Enter
60
+ tmux -S "$SOCKET" capture-pane -p -J -t "$SESSION":0.0 -S -200
61
+ tmux -S "$SOCKET" kill-session -t "$SESSION"
62
+ ```
63
+
64
+ ## Guardrails
65
+
66
+ - Never paste secrets into logs, chat, or code.
67
+ - Prefer `op run` / `op inject` over writing secrets to disk.
68
+ - If sign-in without app integration is needed, use `op account add`.
69
+ - If a command returns "account is not signed in", re-run `op signin` inside tmux and authorize in the app.
70
+ - Do not run `op` outside tmux; stop and ask if tmux is unavailable.
skills/apple-notes/SKILL.md ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: apple-notes
3
+ 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.
4
+ homepage: https://github.com/antoniorodr/memo
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "📝",
10
+ "os": ["darwin"],
11
+ "requires": { "bins": ["memo"] },
12
+ "install":
13
+ [
14
+ {
15
+ "id": "brew",
16
+ "kind": "brew",
17
+ "formula": "antoniorodr/memo/memo",
18
+ "bins": ["memo"],
19
+ "label": "Install memo via Homebrew",
20
+ },
21
+ ],
22
+ },
23
+ }
24
+ ---
25
+
26
+ # Apple Notes CLI
27
+
28
+ Use `memo notes` to manage Apple Notes directly from the terminal. Create, view, edit, delete, search, move notes between folders, and export to HTML/Markdown.
29
+
30
+ Setup
31
+
32
+ - Install (Homebrew): `brew tap antoniorodr/memo && brew install antoniorodr/memo/memo`
33
+ - Manual (pip): `pip install .` (after cloning the repo)
34
+ - macOS-only; if prompted, grant Automation access to Notes.app.
35
+
36
+ View Notes
37
+
38
+ - List all notes: `memo notes`
39
+ - Filter by folder: `memo notes -f "Folder Name"`
40
+ - Search notes (fuzzy): `memo notes -s "query"`
41
+
42
+ Create Notes
43
+
44
+ - Add a new note: `memo notes -a`
45
+ - Opens an interactive editor to compose the note.
46
+ - Quick add with title: `memo notes -a "Note Title"`
47
+
48
+ Edit Notes
49
+
50
+ - Edit existing note: `memo notes -e`
51
+ - Interactive selection of note to edit.
52
+
53
+ Delete Notes
54
+
55
+ - Delete a note: `memo notes -d`
56
+ - Interactive selection of note to delete.
57
+
58
+ Move Notes
59
+
60
+ - Move note to folder: `memo notes -m`
61
+ - Interactive selection of note and destination folder.
62
+
63
+ Export Notes
64
+
65
+ - Export to HTML/Markdown: `memo notes -ex`
66
+ - Exports selected note; uses Mistune for markdown processing.
67
+
68
+ Limitations
69
+
70
+ - Cannot edit notes containing images or attachments.
71
+ - Interactive prompts may require terminal access.
72
+
73
+ Notes
74
+
75
+ - macOS-only.
76
+ - Requires Apple Notes.app to be accessible.
77
+ - For automation, grant permissions in System Settings > Privacy & Security > Automation.
skills/apple-reminders/SKILL.md ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: apple-reminders
3
+ description: Manage Apple Reminders via remindctl CLI (list, add, edit, complete, delete). Supports lists, date filters, and JSON/plain output.
4
+ homepage: https://github.com/steipete/remindctl
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "⏰",
10
+ "os": ["darwin"],
11
+ "requires": { "bins": ["remindctl"] },
12
+ "install":
13
+ [
14
+ {
15
+ "id": "brew",
16
+ "kind": "brew",
17
+ "formula": "steipete/tap/remindctl",
18
+ "bins": ["remindctl"],
19
+ "label": "Install remindctl via Homebrew",
20
+ },
21
+ ],
22
+ },
23
+ }
24
+ ---
25
+
26
+ # Apple Reminders CLI (remindctl)
27
+
28
+ Use `remindctl` to manage Apple Reminders directly from the terminal.
29
+
30
+ ## When to Use
31
+
32
+ ✅ **USE this skill when:**
33
+
34
+ - User explicitly mentions "reminder" or "Reminders app"
35
+ - Creating personal to-dos with due dates that sync to iOS
36
+ - Managing Apple Reminders lists
37
+ - User wants tasks to appear in their iPhone/iPad Reminders app
38
+
39
+ ## When NOT to Use
40
+
41
+ ❌ **DON'T use this skill when:**
42
+
43
+ - Scheduling OpenClaw tasks or alerts → use `cron` tool with systemEvent instead
44
+ - Calendar events or appointments → use Apple Calendar
45
+ - Project/work task management → use Notion, GitHub Issues, or task queue
46
+ - One-time notifications → use `cron` tool for timed alerts
47
+ - User says "remind me" but means an OpenClaw alert → clarify first
48
+
49
+ ## Setup
50
+
51
+ - Install: `brew install steipete/tap/remindctl`
52
+ - macOS-only; grant Reminders permission when prompted
53
+ - Check status: `remindctl status`
54
+ - Request access: `remindctl authorize`
55
+
56
+ ## Common Commands
57
+
58
+ ### View Reminders
59
+
60
+ ```bash
61
+ remindctl # Today's reminders
62
+ remindctl today # Today
63
+ remindctl tomorrow # Tomorrow
64
+ remindctl week # This week
65
+ remindctl overdue # Past due
66
+ remindctl all # Everything
67
+ remindctl 2026-01-04 # Specific date
68
+ ```
69
+
70
+ ### Manage Lists
71
+
72
+ ```bash
73
+ remindctl list # List all lists
74
+ remindctl list Work # Show specific list
75
+ remindctl list Projects --create # Create list
76
+ remindctl list Work --delete # Delete list
77
+ ```
78
+
79
+ ### Create Reminders
80
+
81
+ ```bash
82
+ remindctl add "Buy milk"
83
+ remindctl add --title "Call mom" --list Personal --due tomorrow
84
+ remindctl add --title "Meeting prep" --due "2026-02-15 09:00"
85
+ ```
86
+
87
+ ### Complete/Delete
88
+
89
+ ```bash
90
+ remindctl complete 1 2 3 # Complete by ID
91
+ remindctl delete 4A83 --force # Delete by ID
92
+ ```
93
+
94
+ ### Output Formats
95
+
96
+ ```bash
97
+ remindctl today --json # JSON for scripting
98
+ remindctl today --plain # TSV format
99
+ remindctl today --quiet # Counts only
100
+ ```
101
+
102
+ ## Date Formats
103
+
104
+ Accepted by `--due` and date filters:
105
+
106
+ - `today`, `tomorrow`, `yesterday`
107
+ - `YYYY-MM-DD`
108
+ - `YYYY-MM-DD HH:mm`
109
+ - ISO 8601 (`2026-01-04T12:34:56Z`)
110
+
111
+ ## Example: Clarifying User Intent
112
+
113
+ User: "Remind me to check on the deploy in 2 hours"
114
+
115
+ **Ask:** "Do you want this in Apple Reminders (syncs to your phone) or as an OpenClaw alert (I'll message you here)?"
116
+
117
+ - Apple Reminders → use this skill
118
+ - OpenClaw alert → use `cron` tool with systemEvent
skills/bear-notes/SKILL.md ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: bear-notes
3
+ description: Create, search, and manage Bear notes via grizzly CLI.
4
+ homepage: https://bear.app
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🐻",
10
+ "os": ["darwin"],
11
+ "requires": { "bins": ["grizzly"] },
12
+ "install":
13
+ [
14
+ {
15
+ "id": "go",
16
+ "kind": "go",
17
+ "module": "github.com/tylerwince/grizzly/cmd/grizzly@latest",
18
+ "bins": ["grizzly"],
19
+ "label": "Install grizzly (go)",
20
+ },
21
+ ],
22
+ },
23
+ }
24
+ ---
25
+
26
+ # Bear Notes
27
+
28
+ Use `grizzly` to create, read, and manage notes in Bear on macOS.
29
+
30
+ Requirements
31
+
32
+ - Bear app installed and running
33
+ - For some operations (add-text, tags, open-note --selected), a Bear app token (stored in `~/.config/grizzly/token`)
34
+
35
+ ## Getting a Bear Token
36
+
37
+ For operations that require a token (add-text, tags, open-note --selected), you need an authentication token:
38
+
39
+ 1. Open Bear → Help → API Token → Copy Token
40
+ 2. Save it: `echo "YOUR_TOKEN" > ~/.config/grizzly/token`
41
+
42
+ ## Common Commands
43
+
44
+ Create a note
45
+
46
+ ```bash
47
+ echo "Note content here" | grizzly create --title "My Note" --tag work
48
+ grizzly create --title "Quick Note" --tag inbox < /dev/null
49
+ ```
50
+
51
+ Open/read a note by ID
52
+
53
+ ```bash
54
+ grizzly open-note --id "NOTE_ID" --enable-callback --json
55
+ ```
56
+
57
+ Append text to a note
58
+
59
+ ```bash
60
+ echo "Additional content" | grizzly add-text --id "NOTE_ID" --mode append --token-file ~/.config/grizzly/token
61
+ ```
62
+
63
+ List all tags
64
+
65
+ ```bash
66
+ grizzly tags --enable-callback --json --token-file ~/.config/grizzly/token
67
+ ```
68
+
69
+ Search notes (via open-tag)
70
+
71
+ ```bash
72
+ grizzly open-tag --name "work" --enable-callback --json
73
+ ```
74
+
75
+ ## Options
76
+
77
+ Common flags:
78
+
79
+ - `--dry-run` — Preview the URL without executing
80
+ - `--print-url` — Show the x-callback-url
81
+ - `--enable-callback` — Wait for Bear's response (needed for reading data)
82
+ - `--json` — Output as JSON (when using callbacks)
83
+ - `--token-file PATH` — Path to Bear API token file
84
+
85
+ ## Configuration
86
+
87
+ Grizzly reads config from (in priority order):
88
+
89
+ 1. CLI flags
90
+ 2. Environment variables (`GRIZZLY_TOKEN_FILE`, `GRIZZLY_CALLBACK_URL`, `GRIZZLY_TIMEOUT`)
91
+ 3. `.grizzly.toml` in current directory
92
+ 4. `~/.config/grizzly/config.toml`
93
+
94
+ Example `~/.config/grizzly/config.toml`:
95
+
96
+ ```toml
97
+ token_file = "~/.config/grizzly/token"
98
+ callback_url = "http://127.0.0.1:42123/success"
99
+ timeout = "5s"
100
+ ```
101
+
102
+ ## Notes
103
+
104
+ - Bear must be running for commands to work
105
+ - Note IDs are Bear's internal identifiers (visible in note info or via callbacks)
106
+ - Use `--enable-callback` when you need to read data back from Bear
107
+ - Some operations require a valid token (add-text, tags, open-note --selected)
skills/blogwatcher/SKILL.md ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: blogwatcher
3
+ description: Monitor blogs and RSS/Atom feeds for updates using the blogwatcher CLI.
4
+ homepage: https://github.com/Hyaxia/blogwatcher
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "📰",
10
+ "requires": { "bins": ["blogwatcher"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "go",
15
+ "kind": "go",
16
+ "module": "github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest",
17
+ "bins": ["blogwatcher"],
18
+ "label": "Install blogwatcher (go)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # blogwatcher
26
+
27
+ Track blog and RSS/Atom feed updates with the `blogwatcher` CLI.
28
+
29
+ Install
30
+
31
+ - Go: `go install github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest`
32
+
33
+ Quick start
34
+
35
+ - `blogwatcher --help`
36
+
37
+ Common commands
38
+
39
+ - Add a blog: `blogwatcher add "My Blog" https://example.com`
40
+ - List blogs: `blogwatcher blogs`
41
+ - Scan for updates: `blogwatcher scan`
42
+ - List articles: `blogwatcher articles`
43
+ - Mark an article read: `blogwatcher read 1`
44
+ - Mark all articles read: `blogwatcher read-all`
45
+ - Remove a blog: `blogwatcher remove "My Blog"`
46
+
47
+ Example output
48
+
49
+ ```
50
+ $ blogwatcher blogs
51
+ Tracked blogs (1):
52
+
53
+ xkcd
54
+ URL: https://xkcd.com
55
+ ```
56
+
57
+ ```
58
+ $ blogwatcher scan
59
+ Scanning 1 blog(s)...
60
+
61
+ xkcd
62
+ Source: RSS | Found: 4 | New: 4
63
+
64
+ Found 4 new article(s) total!
65
+ ```
66
+
67
+ Notes
68
+
69
+ - Use `blogwatcher <command> --help` to discover flags and options.
skills/blucli/SKILL.md ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: blucli
3
+ description: BluOS CLI (blu) for discovery, playback, grouping, and volume.
4
+ homepage: https://blucli.sh
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🫐",
10
+ "requires": { "bins": ["blu"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "go",
15
+ "kind": "go",
16
+ "module": "github.com/steipete/blucli/cmd/blu@latest",
17
+ "bins": ["blu"],
18
+ "label": "Install blucli (go)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # blucli (blu)
26
+
27
+ Use `blu` to control Bluesound/NAD players.
28
+
29
+ Quick start
30
+
31
+ - `blu devices` (pick target)
32
+ - `blu --device <id> status`
33
+ - `blu play|pause|stop`
34
+ - `blu volume set 15`
35
+
36
+ Target selection (in priority order)
37
+
38
+ - `--device <id|name|alias>`
39
+ - `BLU_DEVICE`
40
+ - config default (if set)
41
+
42
+ Common tasks
43
+
44
+ - Grouping: `blu group status|add|remove`
45
+ - TuneIn search/play: `blu tunein search "query"`, `blu tunein play "query"`
46
+
47
+ Prefer `--json` for scripts. Confirm the target device before changing playback.
skills/bluebubbles/SKILL.md ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: bluebubbles
3
+ 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".
4
+ metadata: { "openclaw": { "emoji": "🫧", "requires": { "config": ["channels.bluebubbles"] } } }
5
+ ---
6
+
7
+ # BlueBubbles Actions
8
+
9
+ ## Overview
10
+
11
+ BlueBubbles 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.
12
+
13
+ ## Inputs to collect
14
+
15
+ - `target` (prefer `chat_guid:...`; also `+15551234567` in E.164 or `user@example.com`)
16
+ - `message` text for send/edit/reply
17
+ - `messageId` for react/edit/unsend/reply
18
+ - Attachment `path` for local files, or `buffer` + `filename` for base64
19
+
20
+ If the user is vague ("text my mom"), ask for the recipient handle or chat guid and the exact message content.
21
+
22
+ ## Actions
23
+
24
+ ### Send a message
25
+
26
+ ```json
27
+ {
28
+ "action": "send",
29
+ "channel": "bluebubbles",
30
+ "target": "+15551234567",
31
+ "message": "hello from OpenClaw"
32
+ }
33
+ ```
34
+
35
+ ### React (tapback)
36
+
37
+ ```json
38
+ {
39
+ "action": "react",
40
+ "channel": "bluebubbles",
41
+ "target": "+15551234567",
42
+ "messageId": "<message-guid>",
43
+ "emoji": "❤️"
44
+ }
45
+ ```
46
+
47
+ ### Remove a reaction
48
+
49
+ ```json
50
+ {
51
+ "action": "react",
52
+ "channel": "bluebubbles",
53
+ "target": "+15551234567",
54
+ "messageId": "<message-guid>",
55
+ "emoji": "❤️",
56
+ "remove": true
57
+ }
58
+ ```
59
+
60
+ ### Edit a previously sent message
61
+
62
+ ```json
63
+ {
64
+ "action": "edit",
65
+ "channel": "bluebubbles",
66
+ "target": "+15551234567",
67
+ "messageId": "<message-guid>",
68
+ "message": "updated text"
69
+ }
70
+ ```
71
+
72
+ ### Unsend a message
73
+
74
+ ```json
75
+ {
76
+ "action": "unsend",
77
+ "channel": "bluebubbles",
78
+ "target": "+15551234567",
79
+ "messageId": "<message-guid>"
80
+ }
81
+ ```
82
+
83
+ ### Reply to a specific message
84
+
85
+ ```json
86
+ {
87
+ "action": "reply",
88
+ "channel": "bluebubbles",
89
+ "target": "+15551234567",
90
+ "replyTo": "<message-guid>",
91
+ "message": "replying to that"
92
+ }
93
+ ```
94
+
95
+ ### Send an attachment
96
+
97
+ ```json
98
+ {
99
+ "action": "sendAttachment",
100
+ "channel": "bluebubbles",
101
+ "target": "+15551234567",
102
+ "path": "/tmp/photo.jpg",
103
+ "caption": "here you go"
104
+ }
105
+ ```
106
+
107
+ ### Send with an iMessage effect
108
+
109
+ ```json
110
+ {
111
+ "action": "sendWithEffect",
112
+ "channel": "bluebubbles",
113
+ "target": "+15551234567",
114
+ "message": "big news",
115
+ "effect": "balloons"
116
+ }
117
+ ```
118
+
119
+ ## Notes
120
+
121
+ - Requires gateway config `channels.bluebubbles` (serverUrl/password/webhookPath).
122
+ - Prefer `chat_guid` targets when you have them (especially for group chats).
123
+ - BlueBubbles supports rich actions, but some are macOS-version dependent (for example, edit may be broken on macOS 26 Tahoe).
124
+ - The gateway may expose both short and full message ids; full ids are more durable across restarts.
125
+ - Developer reference for the underlying plugin lives in the BlueBubbles plugin package README.
126
+
127
+ ## Ideas to try
128
+
129
+ - React with a tapback to acknowledge a request.
130
+ - Reply in-thread when a user references a specific message.
131
+ - Send a file attachment with a short caption.
skills/camsnap/SKILL.md ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: camsnap
3
+ description: Capture frames or clips from RTSP/ONVIF cameras.
4
+ homepage: https://camsnap.ai
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "📸",
10
+ "requires": { "bins": ["camsnap"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "steipete/tap/camsnap",
17
+ "bins": ["camsnap"],
18
+ "label": "Install camsnap (brew)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # camsnap
26
+
27
+ Use `camsnap` to grab snapshots, clips, or motion events from configured cameras.
28
+
29
+ Setup
30
+
31
+ - Config file: `~/.config/camsnap/config.yaml`
32
+ - Add camera: `camsnap add --name kitchen --host 192.168.0.10 --user user --pass pass`
33
+
34
+ Common commands
35
+
36
+ - Discover: `camsnap discover --info`
37
+ - Snapshot: `camsnap snap kitchen --out shot.jpg`
38
+ - Clip: `camsnap clip kitchen --dur 5s --out clip.mp4`
39
+ - Motion watch: `camsnap watch kitchen --threshold 0.2 --action '...'`
40
+ - Doctor: `camsnap doctor --probe`
41
+
42
+ Notes
43
+
44
+ - Requires `ffmpeg` on PATH.
45
+ - Prefer a short test capture before longer clips.
skills/canvas/SKILL.md ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Canvas Skill
2
+
3
+ Display HTML content on connected OpenClaw nodes (Mac app, iOS, Android).
4
+
5
+ ## Overview
6
+
7
+ The canvas tool lets you present web content on any connected node's canvas view. Great for:
8
+
9
+ - Displaying games, visualizations, dashboards
10
+ - Showing generated HTML content
11
+ - Interactive demos
12
+
13
+ ## How It Works
14
+
15
+ ### Architecture
16
+
17
+ ```
18
+ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
19
+ │ Canvas Host │────▶│ Node Bridge │────▶│ Node App │
20
+ │ (HTTP Server) │ │ (TCP Server) │ │ (Mac/iOS/ │
21
+ │ Port 18793 │ │ Port 18790 │ │ Android) │
22
+ └─────────────────┘ └──────────────────┘ └─────────────┘
23
+ ```
24
+
25
+ 1. **Canvas Host Server**: Serves static HTML/CSS/JS files from `canvasHost.root` directory
26
+ 2. **Node Bridge**: Communicates canvas URLs to connected nodes
27
+ 3. **Node Apps**: Render the content in a WebView
28
+
29
+ ### Tailscale Integration
30
+
31
+ The canvas host server binds based on `gateway.bind` setting:
32
+
33
+ | Bind Mode | Server Binds To | Canvas URL Uses |
34
+ | ---------- | ------------------- | -------------------------- |
35
+ | `loopback` | 127.0.0.1 | localhost (local only) |
36
+ | `lan` | LAN interface | LAN IP address |
37
+ | `tailnet` | Tailscale interface | Tailscale hostname |
38
+ | `auto` | Best available | Tailscale > LAN > loopback |
39
+
40
+ **Key insight:** The `canvasHostHostForBridge` is derived from `bridgeHost`. When bound to Tailscale, nodes receive URLs like:
41
+
42
+ ```
43
+ http://<tailscale-hostname>:18793/__openclaw__/canvas/<file>.html
44
+ ```
45
+
46
+ This is why localhost URLs don't work - the node receives the Tailscale hostname from the bridge!
47
+
48
+ ## Actions
49
+
50
+ | Action | Description |
51
+ | ---------- | ------------------------------------ |
52
+ | `present` | Show canvas with optional target URL |
53
+ | `hide` | Hide the canvas |
54
+ | `navigate` | Navigate to a new URL |
55
+ | `eval` | Execute JavaScript in the canvas |
56
+ | `snapshot` | Capture screenshot of canvas |
57
+
58
+ ## Configuration
59
+
60
+ In the active OpenClaw config file (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`):
61
+
62
+ ```json
63
+ {
64
+ "canvasHost": {
65
+ "enabled": true,
66
+ "port": 18793,
67
+ "root": "/Users/you/clawd/canvas",
68
+ "liveReload": true
69
+ },
70
+ "gateway": {
71
+ "bind": "auto"
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### Live Reload
77
+
78
+ When `liveReload: true` (default), the canvas host:
79
+
80
+ - Watches the root directory for changes (via chokidar)
81
+ - Injects a WebSocket client into HTML files
82
+ - Automatically reloads connected canvases when files change
83
+
84
+ Great for development!
85
+
86
+ ## Workflow
87
+
88
+ ### 1. Create HTML content
89
+
90
+ Place files in the canvas root directory (default `~/clawd/canvas/`):
91
+
92
+ ```bash
93
+ cat > ~/clawd/canvas/my-game.html << 'HTML'
94
+ <!DOCTYPE html>
95
+ <html>
96
+ <head><title>My Game</title></head>
97
+ <body>
98
+ <h1>Hello Canvas!</h1>
99
+ </body>
100
+ </html>
101
+ HTML
102
+ ```
103
+
104
+ ### 2. Find your canvas host URL
105
+
106
+ Check how your gateway is bound:
107
+
108
+ ```bash
109
+ CONFIG_PATH="${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}"
110
+ cat "$CONFIG_PATH" | jq '.gateway.bind'
111
+ ```
112
+
113
+ Then construct the URL:
114
+
115
+ - **loopback**: `http://127.0.0.1:18793/__openclaw__/canvas/<file>.html`
116
+ - **lan/tailnet/auto**: `http://<hostname>:18793/__openclaw__/canvas/<file>.html`
117
+
118
+ Find your Tailscale hostname:
119
+
120
+ ```bash
121
+ tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//'
122
+ ```
123
+
124
+ ### 3. Find connected nodes
125
+
126
+ ```bash
127
+ openclaw nodes list
128
+ ```
129
+
130
+ Look for Mac/iOS/Android nodes with canvas capability.
131
+
132
+ ### 4. Present content
133
+
134
+ ```
135
+ canvas action:present node:<node-id> target:<full-url>
136
+ ```
137
+
138
+ **Example:**
139
+
140
+ ```
141
+ canvas action:present node:mac-63599bc4-b54d-4392-9048-b97abd58343a target:http://peters-mac-studio-1.sheep-coho.ts.net:18793/__openclaw__/canvas/snake.html
142
+ ```
143
+
144
+ ### 5. Navigate, snapshot, or hide
145
+
146
+ ```
147
+ canvas action:navigate node:<node-id> url:<new-url>
148
+ canvas action:snapshot node:<node-id>
149
+ canvas action:hide node:<node-id>
150
+ ```
151
+
152
+ ## Debugging
153
+
154
+ ### White screen / content not loading
155
+
156
+ **Cause:** URL mismatch between server bind and node expectation.
157
+
158
+ **Debug steps:**
159
+
160
+ 1. Check server bind: `CONFIG_PATH="${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}"; cat "$CONFIG_PATH" | jq '.gateway.bind'`
161
+ 2. Check what port canvas is on: `lsof -i :18793`
162
+ 3. Test URL directly: `curl http://<hostname>:18793/__openclaw__/canvas/<file>.html`
163
+
164
+ **Solution:** Use the full hostname matching your bind mode, not localhost.
165
+
166
+ ### "node required" error
167
+
168
+ Always specify `node:<node-id>` parameter.
169
+
170
+ ### "node not connected" error
171
+
172
+ Node is offline. Use `openclaw nodes list` to find online nodes.
173
+
174
+ ### Content not updating
175
+
176
+ If live reload isn't working:
177
+
178
+ 1. Check `liveReload: true` in config
179
+ 2. Ensure file is in the canvas root directory
180
+ 3. Check for watcher errors in logs
181
+
182
+ ## URL Path Structure
183
+
184
+ The canvas host serves from `/__openclaw__/canvas/` prefix:
185
+
186
+ ```
187
+ http://<host>:18793/__openclaw__/canvas/index.html → ~/clawd/canvas/index.html
188
+ http://<host>:18793/__openclaw__/canvas/games/snake.html → ~/clawd/canvas/games/snake.html
189
+ ```
190
+
191
+ The `/__openclaw__/canvas/` prefix is defined by `CANVAS_HOST_PATH` constant.
192
+
193
+ ## Tips
194
+
195
+ - Keep HTML self-contained (inline CSS/JS) for best results
196
+ - Use the default index.html as a test page (has bridge diagnostics)
197
+ - The canvas persists until you `hide` it or navigate away
198
+ - Live reload makes development fast - just save and it updates!
199
+ - A2UI JSON push is WIP - use HTML files for now
skills/clawhub/SKILL.md ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: clawhub
3
+ 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.
4
+ metadata:
5
+ {
6
+ "openclaw":
7
+ {
8
+ "requires": { "bins": ["clawhub"] },
9
+ "install":
10
+ [
11
+ {
12
+ "id": "node",
13
+ "kind": "node",
14
+ "package": "clawhub",
15
+ "bins": ["clawhub"],
16
+ "label": "Install ClawHub CLI (npm)",
17
+ },
18
+ ],
19
+ },
20
+ }
21
+ ---
22
+
23
+ # ClawHub CLI
24
+
25
+ Install
26
+
27
+ ```bash
28
+ npm i -g clawhub
29
+ ```
30
+
31
+ Auth (publish)
32
+
33
+ ```bash
34
+ clawhub login
35
+ clawhub whoami
36
+ ```
37
+
38
+ Search
39
+
40
+ ```bash
41
+ clawhub search "postgres backups"
42
+ ```
43
+
44
+ Install
45
+
46
+ ```bash
47
+ clawhub install my-skill
48
+ clawhub install my-skill --version 1.2.3
49
+ ```
50
+
51
+ Update (hash-based match + upgrade)
52
+
53
+ ```bash
54
+ clawhub update my-skill
55
+ clawhub update my-skill --version 1.2.3
56
+ clawhub update --all
57
+ clawhub update my-skill --force
58
+ clawhub update --all --no-input --force
59
+ ```
60
+
61
+ List
62
+
63
+ ```bash
64
+ clawhub list
65
+ ```
66
+
67
+ Publish
68
+
69
+ ```bash
70
+ clawhub publish ./my-skill --slug my-skill --name "My Skill" --version 1.2.0 --changelog "Fixes + docs"
71
+ ```
72
+
73
+ Notes
74
+
75
+ - Default registry: https://clawhub.com (override with CLAWHUB_REGISTRY or --registry)
76
+ - Default workdir: cwd (falls back to OpenClaw workspace); install dir: ./skills (override with --workdir / --dir / CLAWHUB_WORKDIR)
77
+ - Update command hashes local files, resolves matching version, and upgrades to latest unless --version is set
skills/coding-agent/SKILL.md ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: coding-agent
3
+ 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.'
4
+ metadata:
5
+ {
6
+ "openclaw":
7
+ {
8
+ "emoji": "🧩",
9
+ "requires": { "anyBins": ["claude", "codex", "opencode", "pi"] },
10
+ "install":
11
+ [
12
+ {
13
+ "id": "node-claude",
14
+ "kind": "node",
15
+ "package": "@anthropic-ai/claude-code",
16
+ "bins": ["claude"],
17
+ "label": "Install Claude Code CLI (npm)",
18
+ },
19
+ {
20
+ "id": "node-codex",
21
+ "kind": "node",
22
+ "package": "@openai/codex",
23
+ "bins": ["codex"],
24
+ "label": "Install Codex CLI (npm)",
25
+ },
26
+ ],
27
+ },
28
+ }
29
+ ---
30
+
31
+ # Coding Agent (bash-first)
32
+
33
+ Use **bash** (with optional background mode) for all coding agent work. Simple and effective.
34
+
35
+ ## ⚠️ PTY Mode: Codex/Pi/OpenCode yes, Claude Code no
36
+
37
+ For **Codex, Pi, and OpenCode**, PTY is still required (interactive terminal apps):
38
+
39
+ ```bash
40
+ # ✅ Correct for Codex/Pi/OpenCode
41
+ bash pty:true command:"codex exec 'Your prompt'"
42
+ ```
43
+
44
+ For **Claude Code** (`claude` CLI), use `--print --permission-mode bypassPermissions` instead.
45
+ `--dangerously-skip-permissions` with PTY can exit after the confirmation dialog.
46
+ `--print` mode keeps full tool access and avoids interactive confirmation:
47
+
48
+ ```bash
49
+ # ✅ Correct for Claude Code (no PTY needed)
50
+ cd /path/to/project && claude --permission-mode bypassPermissions --print 'Your task'
51
+
52
+ # For background execution: use background:true on the exec tool
53
+
54
+ # ❌ Wrong for Claude Code
55
+ bash pty:true command:"claude --dangerously-skip-permissions 'task'"
56
+ ```
57
+
58
+ ### Bash Tool Parameters
59
+
60
+ | Parameter | Type | Description |
61
+ | ------------ | ------- | --------------------------------------------------------------------------- |
62
+ | `command` | string | The shell command to run |
63
+ | `pty` | boolean | **Use for coding agents!** Allocates a pseudo-terminal for interactive CLIs |
64
+ | `workdir` | string | Working directory (agent sees only this folder's context) |
65
+ | `background` | boolean | Run in background, returns sessionId for monitoring |
66
+ | `timeout` | number | Timeout in seconds (kills process on expiry) |
67
+ | `elevated` | boolean | Run on host instead of sandbox (if allowed) |
68
+
69
+ ### Process Tool Actions (for background sessions)
70
+
71
+ | Action | Description |
72
+ | ----------- | ---------------------------------------------------- |
73
+ | `list` | List all running/recent sessions |
74
+ | `poll` | Check if session is still running |
75
+ | `log` | Get session output (with optional offset/limit) |
76
+ | `write` | Send raw data to stdin |
77
+ | `submit` | Send data + newline (like typing and pressing Enter) |
78
+ | `send-keys` | Send key tokens or hex bytes |
79
+ | `paste` | Paste text (with optional bracketed mode) |
80
+ | `kill` | Terminate the session |
81
+
82
+ ---
83
+
84
+ ## Quick Start: One-Shot Tasks
85
+
86
+ For quick prompts/chats, create a temp git repo and run:
87
+
88
+ ```bash
89
+ # Quick chat (Codex needs a git repo!)
90
+ SCRATCH=$(mktemp -d) && cd $SCRATCH && git init && codex exec "Your prompt here"
91
+
92
+ # Or in a real project - with PTY!
93
+ bash pty:true workdir:~/Projects/myproject command:"codex exec 'Add error handling to the API calls'"
94
+ ```
95
+
96
+ **Why git init?** Codex refuses to run outside a trusted git directory. Creating a temp repo solves this for scratch work.
97
+
98
+ ---
99
+
100
+ ## The Pattern: workdir + background + pty
101
+
102
+ For longer tasks, use background mode with PTY:
103
+
104
+ ```bash
105
+ # Start agent in target directory (with PTY!)
106
+ bash pty:true workdir:~/project background:true command:"codex exec --full-auto 'Build a snake game'"
107
+ # Returns sessionId for tracking
108
+
109
+ # Monitor progress
110
+ process action:log sessionId:XXX
111
+
112
+ # Check if done
113
+ process action:poll sessionId:XXX
114
+
115
+ # Send input (if agent asks a question)
116
+ process action:write sessionId:XXX data:"y"
117
+
118
+ # Submit with Enter (like typing "yes" and pressing Enter)
119
+ process action:submit sessionId:XXX data:"yes"
120
+
121
+ # Kill if needed
122
+ process action:kill sessionId:XXX
123
+ ```
124
+
125
+ **Why workdir matters:** Agent wakes up in a focused directory, doesn't wander off reading unrelated files (like your soul.md 😅).
126
+
127
+ ---
128
+
129
+ ## Codex CLI
130
+
131
+ **Model:** `gpt-5.2-codex` is the default (set in ~/.codex/config.toml)
132
+
133
+ ### Flags
134
+
135
+ | Flag | Effect |
136
+ | --------------- | -------------------------------------------------- |
137
+ | `exec "prompt"` | One-shot execution, exits when done |
138
+ | `--full-auto` | Sandboxed but auto-approves in workspace |
139
+ | `--yolo` | NO sandbox, NO approvals (fastest, most dangerous) |
140
+
141
+ ### Building/Creating
142
+
143
+ ```bash
144
+ # Quick one-shot (auto-approves) - remember PTY!
145
+ bash pty:true workdir:~/project command:"codex exec --full-auto 'Build a dark mode toggle'"
146
+
147
+ # Background for longer work
148
+ bash pty:true workdir:~/project background:true command:"codex --yolo 'Refactor the auth module'"
149
+ ```
150
+
151
+ ### Reviewing PRs
152
+
153
+ **⚠️ CRITICAL: Never review PRs in OpenClaw's own project folder!**
154
+ Clone to temp folder or use git worktree.
155
+
156
+ ```bash
157
+ # Clone to temp for safe review
158
+ REVIEW_DIR=$(mktemp -d)
159
+ git clone https://github.com/user/repo.git $REVIEW_DIR
160
+ cd $REVIEW_DIR && gh pr checkout 130
161
+ bash pty:true workdir:$REVIEW_DIR command:"codex review --base origin/main"
162
+ # Clean up after: trash $REVIEW_DIR
163
+
164
+ # Or use git worktree (keeps main intact)
165
+ git worktree add /tmp/pr-130-review pr-130-branch
166
+ bash pty:true workdir:/tmp/pr-130-review command:"codex review --base main"
167
+ ```
168
+
169
+ ### Batch PR Reviews (parallel army!)
170
+
171
+ ```bash
172
+ # Fetch all PR refs first
173
+ git fetch origin '+refs/pull/*/head:refs/remotes/origin/pr/*'
174
+
175
+ # Deploy the army - one Codex per PR (all with PTY!)
176
+ bash pty:true workdir:~/project background:true command:"codex exec 'Review PR #86. git diff origin/main...origin/pr/86'"
177
+ bash pty:true workdir:~/project background:true command:"codex exec 'Review PR #87. git diff origin/main...origin/pr/87'"
178
+
179
+ # Monitor all
180
+ process action:list
181
+
182
+ # Post results to GitHub
183
+ gh pr comment <PR#> --body "<review content>"
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Claude Code
189
+
190
+ ```bash
191
+ # Foreground
192
+ bash workdir:~/project command:"claude --permission-mode bypassPermissions --print 'Your task'"
193
+
194
+ # Background
195
+ bash workdir:~/project background:true command:"claude --permission-mode bypassPermissions --print 'Your task'"
196
+ ```
197
+
198
+ ---
199
+
200
+ ## OpenCode
201
+
202
+ ```bash
203
+ bash pty:true workdir:~/project command:"opencode run 'Your task'"
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Pi Coding Agent
209
+
210
+ ```bash
211
+ # Install: npm install -g @mariozechner/pi-coding-agent
212
+ bash pty:true workdir:~/project command:"pi 'Your task'"
213
+
214
+ # Non-interactive mode (PTY still recommended)
215
+ bash pty:true command:"pi -p 'Summarize src/'"
216
+
217
+ # Different provider/model
218
+ bash pty:true command:"pi --provider openai --model gpt-4o-mini -p 'Your task'"
219
+ ```
220
+
221
+ **Note:** Pi now has Anthropic prompt caching enabled (PR #584, merged Jan 2026)!
222
+
223
+ ---
224
+
225
+ ## Parallel Issue Fixing with git worktrees
226
+
227
+ For fixing multiple issues in parallel, use git worktrees:
228
+
229
+ ```bash
230
+ # 1. Create worktrees for each issue
231
+ git worktree add -b fix/issue-78 /tmp/issue-78 main
232
+ git worktree add -b fix/issue-99 /tmp/issue-99 main
233
+
234
+ # 2. Launch Codex in each (background + PTY!)
235
+ bash pty:true workdir:/tmp/issue-78 background:true command:"pnpm install && codex --yolo 'Fix issue #78: <description>. Commit and push.'"
236
+ bash 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.'"
237
+
238
+ # 3. Monitor progress
239
+ process action:list
240
+ process action:log sessionId:XXX
241
+
242
+ # 4. Create PRs after fixes
243
+ cd /tmp/issue-78 && git push -u origin fix/issue-78
244
+ gh pr create --repo user/repo --head fix/issue-78 --title "fix: ..." --body "..."
245
+
246
+ # 5. Cleanup
247
+ git worktree remove /tmp/issue-78
248
+ git worktree remove /tmp/issue-99
249
+ ```
250
+
251
+ ---
252
+
253
+ ## ⚠️ Rules
254
+
255
+ 1. **Use the right execution mode per agent**:
256
+ - Codex/Pi/OpenCode: `pty:true`
257
+ - Claude Code: `--print --permission-mode bypassPermissions` (no PTY required)
258
+ 2. **Respect tool choice** - if user asks for Codex, use Codex.
259
+ - Orchestrator mode: do NOT hand-code patches yourself.
260
+ - If an agent fails/hangs, respawn it or ask the user for direction, but don't silently take over.
261
+ 3. **Be patient** - don't kill sessions because they're "slow"
262
+ 4. **Monitor with process:log** - check progress without interfering
263
+ 5. **--full-auto for building** - auto-approves changes
264
+ 6. **vanilla for reviewing** - no special flags needed
265
+ 7. **Parallel is OK** - run many Codex processes at once for batch work
266
+ 8. **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!
267
+ 9. **NEVER checkout branches in ~/Projects/openclaw/** - that's the LIVE OpenClaw instance!
268
+
269
+ ---
270
+
271
+ ## Progress Updates (Critical)
272
+
273
+ When you spawn coding agents in the background, keep the user in the loop.
274
+
275
+ - Send 1 short message when you start (what's running + where).
276
+ - Then only update again when something changes:
277
+ - a milestone completes (build finished, tests passed)
278
+ - the agent asks a question / needs input
279
+ - you hit an error or need user action
280
+ - the agent finishes (include what changed + where)
281
+ - If you kill a session, immediately say you killed it and why.
282
+
283
+ This prevents the user from seeing only "Agent failed before reply" and having no idea what happened.
284
+
285
+ ---
286
+
287
+ ## Auto-Notify on Completion
288
+
289
+ For 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):
290
+
291
+ ```
292
+ ... your task here.
293
+
294
+ When completely finished, run this command to notify me:
295
+ openclaw system event --text "Done: [brief summary of what was built]" --mode now
296
+ ```
297
+
298
+ **Example:**
299
+
300
+ ```bash
301
+ bash pty:true workdir:~/project background:true command:"codex --yolo exec 'Build a REST API for todos.
302
+
303
+ When completely finished, run: openclaw system event --text \"Done: Built todos REST API with CRUD endpoints\" --mode now'"
304
+ ```
305
+
306
+ This triggers an immediate wake event — Skippy gets pinged in seconds, not 10 minutes.
307
+
308
+ ---
309
+
310
+ ## Learnings (Jan 2026)
311
+
312
+ - **PTY is essential:** Coding agents are interactive terminal apps. Without `pty:true`, output breaks or agent hangs.
313
+ - **Git repo required:** Codex won't run outside a git directory. Use `mktemp -d && git init` for scratch work.
314
+ - **exec is your friend:** `codex exec "prompt"` runs and exits cleanly - perfect for one-shots.
315
+ - **submit vs write:** Use `submit` to send input + Enter, `write` for raw data without newline.
316
+ - **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"_ 🦞
skills/discord/SKILL.md ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: discord
3
+ description: "Discord ops via the message tool (channel=discord)."
4
+ metadata: { "openclaw": { "emoji": "🎮", "requires": { "config": ["channels.discord.token"] } } }
5
+ allowed-tools: ["message"]
6
+ ---
7
+
8
+ # Discord (Via `message`)
9
+
10
+ Use the `message` tool. No provider-specific `discord` tool exposed to the agent.
11
+
12
+ ## Musts
13
+
14
+ - Always: `channel: "discord"`.
15
+ - Respect gating: `channels.discord.actions.*` (some default off: `roles`, `moderation`, `presence`, `channels`).
16
+ - Prefer explicit ids: `guildId`, `channelId`, `messageId`, `userId`.
17
+ - Multi-account: optional `accountId`.
18
+
19
+ ## Guidelines
20
+
21
+ - Avoid Markdown tables in outbound Discord messages.
22
+ - Mention users as `<@USER_ID>`.
23
+ - Prefer Discord components v2 (`components`) for rich UI; use legacy `embeds` only when you must.
24
+
25
+ ## Targets
26
+
27
+ - Send-like actions: `to: "channel:<id>"` or `to: "user:<id>"`.
28
+ - Message-specific actions: `channelId: "<id>"` (or `to`) + `messageId: "<id>"`.
29
+
30
+ ## Common Actions (Examples)
31
+
32
+ Send message:
33
+
34
+ ```json
35
+ {
36
+ "action": "send",
37
+ "channel": "discord",
38
+ "to": "channel:123",
39
+ "message": "hello",
40
+ "silent": true
41
+ }
42
+ ```
43
+
44
+ Send with media:
45
+
46
+ ```json
47
+ {
48
+ "action": "send",
49
+ "channel": "discord",
50
+ "to": "channel:123",
51
+ "message": "see attachment",
52
+ "media": "file:///tmp/example.png"
53
+ }
54
+ ```
55
+
56
+ - Optional `silent: true` to suppress Discord notifications.
57
+
58
+ Send with components v2 (recommended for rich UI):
59
+
60
+ ```json
61
+ {
62
+ "action": "send",
63
+ "channel": "discord",
64
+ "to": "channel:123",
65
+ "message": "Status update",
66
+ "components": "[Carbon v2 components]"
67
+ }
68
+ ```
69
+
70
+ - `components` expects Carbon component instances (Container, TextDisplay, etc.) from JS/TS integrations.
71
+ - Do not combine `components` with `embeds` (Discord rejects v2 + embeds).
72
+
73
+ Legacy embeds (not recommended):
74
+
75
+ ```json
76
+ {
77
+ "action": "send",
78
+ "channel": "discord",
79
+ "to": "channel:123",
80
+ "message": "Status update",
81
+ "embeds": [{ "title": "Legacy", "description": "Embeds are legacy." }]
82
+ }
83
+ ```
84
+
85
+ - `embeds` are ignored when components v2 are present.
86
+
87
+ React:
88
+
89
+ ```json
90
+ {
91
+ "action": "react",
92
+ "channel": "discord",
93
+ "channelId": "123",
94
+ "messageId": "456",
95
+ "emoji": "✅"
96
+ }
97
+ ```
98
+
99
+ Read:
100
+
101
+ ```json
102
+ {
103
+ "action": "read",
104
+ "channel": "discord",
105
+ "to": "channel:123",
106
+ "limit": 20
107
+ }
108
+ ```
109
+
110
+ Edit / delete:
111
+
112
+ ```json
113
+ {
114
+ "action": "edit",
115
+ "channel": "discord",
116
+ "channelId": "123",
117
+ "messageId": "456",
118
+ "message": "fixed typo"
119
+ }
120
+ ```
121
+
122
+ ```json
123
+ {
124
+ "action": "delete",
125
+ "channel": "discord",
126
+ "channelId": "123",
127
+ "messageId": "456"
128
+ }
129
+ ```
130
+
131
+ Poll:
132
+
133
+ ```json
134
+ {
135
+ "action": "poll",
136
+ "channel": "discord",
137
+ "to": "channel:123",
138
+ "pollQuestion": "Lunch?",
139
+ "pollOption": ["Pizza", "Sushi", "Salad"],
140
+ "pollMulti": false,
141
+ "pollDurationHours": 24
142
+ }
143
+ ```
144
+
145
+ Pins:
146
+
147
+ ```json
148
+ {
149
+ "action": "pin",
150
+ "channel": "discord",
151
+ "channelId": "123",
152
+ "messageId": "456"
153
+ }
154
+ ```
155
+
156
+ Threads:
157
+
158
+ ```json
159
+ {
160
+ "action": "thread-create",
161
+ "channel": "discord",
162
+ "channelId": "123",
163
+ "messageId": "456",
164
+ "threadName": "bug triage"
165
+ }
166
+ ```
167
+
168
+ Search:
169
+
170
+ ```json
171
+ {
172
+ "action": "search",
173
+ "channel": "discord",
174
+ "guildId": "999",
175
+ "query": "release notes",
176
+ "channelIds": ["123", "456"],
177
+ "limit": 10
178
+ }
179
+ ```
180
+
181
+ Presence (often gated):
182
+
183
+ ```json
184
+ {
185
+ "action": "set-presence",
186
+ "channel": "discord",
187
+ "activityType": "playing",
188
+ "activityName": "with fire",
189
+ "status": "online"
190
+ }
191
+ ```
192
+
193
+ ## Writing Style (Discord)
194
+
195
+ - Short, conversational, low ceremony.
196
+ - No markdown tables.
197
+ - Mention users as `<@USER_ID>`.
skills/eightctl/SKILL.md ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: eightctl
3
+ description: Control Eight Sleep pods (status, temperature, alarms, schedules).
4
+ homepage: https://eightctl.sh
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🛌",
10
+ "requires": { "bins": ["eightctl"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "go",
15
+ "kind": "go",
16
+ "module": "github.com/steipete/eightctl/cmd/eightctl@latest",
17
+ "bins": ["eightctl"],
18
+ "label": "Install eightctl (go)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # eightctl
26
+
27
+ Use `eightctl` for Eight Sleep pod control. Requires auth.
28
+
29
+ Auth
30
+
31
+ - Config: `~/.config/eightctl/config.yaml`
32
+ - Env: `EIGHTCTL_EMAIL`, `EIGHTCTL_PASSWORD`
33
+
34
+ Quick start
35
+
36
+ - `eightctl status`
37
+ - `eightctl on|off`
38
+ - `eightctl temp 20`
39
+
40
+ Common tasks
41
+
42
+ - Alarms: `eightctl alarm list|create|dismiss`
43
+ - Schedules: `eightctl schedule list|create|update`
44
+ - Audio: `eightctl audio state|play|pause`
45
+ - Base: `eightctl base info|angle`
46
+
47
+ Notes
48
+
49
+ - API is unofficial and rate-limited; avoid repeated logins.
50
+ - Confirm before changing temperature or alarms.
skills/gemini/SKILL.md ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: gemini
3
+ description: Gemini CLI for one-shot Q&A, summaries, and generation.
4
+ homepage: https://ai.google.dev/
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "✨",
10
+ "requires": { "bins": ["gemini"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "gemini-cli",
17
+ "bins": ["gemini"],
18
+ "label": "Install Gemini CLI (brew)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # Gemini CLI
26
+
27
+ Use Gemini in one-shot mode with a positional prompt (avoid interactive mode).
28
+
29
+ Quick start
30
+
31
+ - `gemini "Answer this question..."`
32
+ - `gemini --model <name> "Prompt..."`
33
+ - `gemini --output-format json "Return JSON"`
34
+
35
+ Extensions
36
+
37
+ - List: `gemini --list-extensions`
38
+ - Manage: `gemini extensions <command>`
39
+
40
+ Notes
41
+
42
+ - If auth is required, run `gemini` once interactively and follow the login flow.
43
+ - Avoid `--yolo` for safety.
skills/gh-issues/SKILL.md ADDED
@@ -0,0 +1,885 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: gh-issues
3
+ 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]"
4
+ user-invocable: true
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "requires": { "bins": ["curl", "git", "gh"] },
10
+ "primaryEnv": "GH_TOKEN",
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "gh",
17
+ "bins": ["gh"],
18
+ "label": "Install GitHub CLI (brew)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # gh-issues — Auto-fix GitHub Issues with Parallel Sub-agents
26
+
27
+ You are an orchestrator. Follow these 6 phases exactly. Do not skip phases.
28
+
29
+ IMPORTANT — 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:
30
+
31
+ ```
32
+ curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" ...
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Phase 1 — Parse Arguments
38
+
39
+ Parse the arguments string provided after /gh-issues.
40
+
41
+ Positional:
42
+
43
+ - owner/repo — optional. This is the source repo to fetch issues from. If omitted, detect from the current git remote:
44
+ `git remote get-url origin`
45
+ Extract owner/repo from the URL (handles both HTTPS and SSH formats).
46
+ - HTTPS: https://github.com/owner/repo.git → owner/repo
47
+ - SSH: git@github.com:owner/repo.git → owner/repo
48
+ If not in a git repo or no remote found, stop with an error asking the user to specify owner/repo.
49
+
50
+ Flags (all optional):
51
+ | Flag | Default | Description |
52
+ |------|---------|-------------|
53
+ | --label | _(none)_ | Filter by label (e.g. bug, `enhancement`) |
54
+ | --limit | 10 | Max issues to fetch per poll |
55
+ | --milestone | _(none)_ | Filter by milestone title |
56
+ | --assignee | _(none)_ | Filter by assignee (`@me` for self) |
57
+ | --state | open | Issue state: open, closed, all |
58
+ | --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. |
59
+ | --watch | false | Keep polling for new issues and PR reviews after each batch |
60
+ | --interval | 5 | Minutes between polls (only with `--watch`) |
61
+ | --dry-run | false | Fetch and display only — no sub-agents |
62
+ | --yes | false | Skip confirmation and auto-process all filtered issues |
63
+ | --reviews-only | false | Skip issue processing (Phases 2-5). Only run Phase 6 — check open PRs for review comments and address them. |
64
+ | --cron | false | Cron-safe mode: fetch issues and spawn sub-agents, exit without waiting for results. |
65
+ | --model | _(none)_ | Model to use for sub-agents (e.g. `glm-5`, `zai/glm-5`). If not specified, uses the agent's default model. |
66
+ | --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. |
67
+
68
+ Store parsed values for use in subsequent phases.
69
+
70
+ Derived values:
71
+
72
+ - SOURCE_REPO = the positional owner/repo (where issues live)
73
+ - PUSH_REPO = --fork value if provided, otherwise same as SOURCE_REPO
74
+ - FORK_MODE = true if --fork was provided, false otherwise
75
+
76
+ **If `--reviews-only` is set:** Skip directly to Phase 6. Run token resolution (from Phase 2) first, then jump to Phase 6.
77
+
78
+ **If `--cron` is set:**
79
+
80
+ - Force `--yes` (skip confirmation)
81
+ - If `--reviews-only` is also set, run token resolution then jump to Phase 6 (cron review mode)
82
+ - Otherwise, proceed normally through Phases 2-5 with cron-mode behavior active
83
+
84
+ ---
85
+
86
+ ## Phase 2 — Fetch Issues
87
+
88
+ **Token Resolution:**
89
+ First, ensure GH_TOKEN is available. Check environment:
90
+
91
+ ```
92
+ echo $GH_TOKEN
93
+ ```
94
+
95
+ If empty, read from config:
96
+
97
+ ```
98
+ CONFIG_PATH="${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}"
99
+ cat "$CONFIG_PATH" | jq -r '.skills.entries["gh-issues"].apiKey // empty'
100
+ ```
101
+
102
+ If still empty, check `/data/.clawdbot/openclaw.json`:
103
+
104
+ ```
105
+ cat /data/.clawdbot/openclaw.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
106
+ ```
107
+
108
+ Export as GH_TOKEN for subsequent commands:
109
+
110
+ ```
111
+ export GH_TOKEN="<token>"
112
+ ```
113
+
114
+ Build and run a curl request to the GitHub Issues API via exec:
115
+
116
+ ```
117
+ curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
118
+ "https://api.github.com/repos/{SOURCE_REPO}/issues?per_page={limit}&state={state}&{query_params}"
119
+ ```
120
+
121
+ Where {query_params} is built from:
122
+
123
+ - labels={label} if --label was provided
124
+ - 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)
125
+ - assignee={assignee} if --assignee was provided (if @me, first resolve your username via `GET /user`)
126
+
127
+ IMPORTANT: The GitHub Issues API also returns pull requests. Filter them out — exclude any item where pull_request key exists in the response object.
128
+
129
+ If in watch mode: Also filter out any issue numbers already in the PROCESSED_ISSUES set from previous batches.
130
+
131
+ Error handling:
132
+
133
+ - If curl returns an HTTP 401 or 403 → stop and tell the user:
134
+ > "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`."
135
+ - If the response is an empty array (after filtering) → report "No issues found matching filters" and stop (or loop back if in watch mode).
136
+ - If curl fails or returns any other error → report the error verbatim and stop.
137
+
138
+ Parse the JSON response. For each issue, extract: number, title, body, labels (array of label names), assignees, html_url.
139
+
140
+ ---
141
+
142
+ ## Phase 3 — Present & Confirm
143
+
144
+ Display a markdown table of fetched issues:
145
+
146
+ | # | Title | Labels |
147
+ | --- | ----------------------------- | ------------- |
148
+ | 42 | Fix null pointer in parser | bug, critical |
149
+ | 37 | Add retry logic for API calls | enhancement |
150
+
151
+ If FORK_MODE is active, also display:
152
+
153
+ > "Fork mode: branches will be pushed to {PUSH_REPO}, PRs will target `{SOURCE_REPO}`"
154
+
155
+ If `--dry-run` is active:
156
+
157
+ - Display the table and stop. Do not proceed to Phase 4.
158
+
159
+ If `--yes` is active:
160
+
161
+ - Display the table for visibility
162
+ - Auto-process ALL listed issues without asking for confirmation
163
+ - Proceed directly to Phase 4
164
+
165
+ Otherwise:
166
+ Ask the user to confirm which issues to process:
167
+
168
+ - "all" — process every listed issue
169
+ - Comma-separated numbers (e.g. `42, 37`) — process only those
170
+ - "cancel" — abort entirely
171
+
172
+ Wait for user response before proceeding.
173
+
174
+ Watch 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.
175
+
176
+ ---
177
+
178
+ ## Phase 4 — Pre-flight Checks
179
+
180
+ Run these checks sequentially via exec:
181
+
182
+ 1. **Dirty working tree check:**
183
+
184
+ ```
185
+ git status --porcelain
186
+ ```
187
+
188
+ If output is non-empty, warn the user:
189
+
190
+ > "Working tree has uncommitted changes. Sub-agents will create branches from HEAD — uncommitted changes will NOT be included. Continue?"
191
+ > Wait for confirmation. If declined, stop.
192
+
193
+ 2. **Record base branch:**
194
+
195
+ ```
196
+ git rev-parse --abbrev-ref HEAD
197
+ ```
198
+
199
+ Store as BASE_BRANCH.
200
+
201
+ 3. **Verify remote access:**
202
+ If FORK_MODE:
203
+ - Verify the fork remote exists. Check if a git remote named `fork` exists:
204
+ ```
205
+ git remote get-url fork
206
+ ```
207
+ If it doesn't exist, add it:
208
+ ```
209
+ git remote add fork https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git
210
+ ```
211
+ - Also verify origin (the source repo) is reachable:
212
+ ```
213
+ git ls-remote --exit-code origin HEAD
214
+ ```
215
+
216
+ If not FORK_MODE:
217
+
218
+ ```
219
+ git ls-remote --exit-code origin HEAD
220
+ ```
221
+
222
+ If this fails, stop with: "Cannot reach remote origin. Check your network and git config."
223
+
224
+ 4. **Verify GH_TOKEN validity:**
225
+
226
+ ```
227
+ curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/user
228
+ ```
229
+
230
+ If HTTP status is not 200, stop with:
231
+
232
+ > "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`."
233
+
234
+ 5. **Check for existing PRs:**
235
+ For each confirmed issue number N, run:
236
+
237
+ ```
238
+ curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
239
+ "https://api.github.com/repos/{SOURCE_REPO}/pulls?head={PUSH_REPO_OWNER}:fix/issue-{N}&state=open&per_page=1"
240
+ ```
241
+
242
+ (Where PUSH_REPO_OWNER is the owner portion of `PUSH_REPO`)
243
+ If the response array is non-empty, remove that issue from the processing list and report:
244
+
245
+ > "Skipping #{N} — PR already exists: {html_url}"
246
+
247
+ If all issues are skipped, report and stop (or loop back if in watch mode).
248
+
249
+ 6. **Check for in-progress branches (no PR yet = sub-agent still working):**
250
+ 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):
251
+
252
+ ```
253
+ curl -s -o /dev/null -w "%{http_code}" \
254
+ -H "Authorization: Bearer $GH_TOKEN" \
255
+ "https://api.github.com/repos/{PUSH_REPO}/branches/fix/issue-{N}"
256
+ ```
257
+
258
+ If HTTP 200 → the branch exists on the push repo but no open PR was found for it in step 5. Skip that issue:
259
+
260
+ > "Skipping #{N} — branch fix/issue-{N} exists on {PUSH_REPO}, fix likely in progress"
261
+
262
+ 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).
263
+
264
+ If all issues are skipped after this check, report and stop (or loop back if in watch mode).
265
+
266
+ 7. **Check claim-based in-progress tracking:**
267
+ 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.
268
+
269
+ Read the claims file (create empty `{}` if missing):
270
+
271
+ ```
272
+ CLAIMS_FILE="/data/.clawdbot/gh-issues-claims.json"
273
+ if [ ! -f "$CLAIMS_FILE" ]; then
274
+ mkdir -p /data/.clawdbot
275
+ echo '{}' > "$CLAIMS_FILE"
276
+ fi
277
+ ```
278
+
279
+ 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:
280
+
281
+ ```
282
+ CLAIMS=$(cat "$CLAIMS_FILE")
283
+ 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)
284
+ CLAIMS=$(echo "$CLAIMS" | jq --arg cutoff "$CUTOFF" 'to_entries | map(select(.value > $cutoff)) | from_entries')
285
+ echo "$CLAIMS" > "$CLAIMS_FILE"
286
+ ```
287
+
288
+ 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.
289
+
290
+ If claimed and not expired → skip:
291
+
292
+ > "Skipping #{N} — sub-agent claimed this issue {minutes}m ago, still within timeout window"
293
+
294
+ Where `{minutes}` is calculated from the claim timestamp to now.
295
+
296
+ If all issues are skipped after this check, report and stop (or loop back if in watch mode).
297
+
298
+ ---
299
+
300
+ ## Phase 5 — Spawn Sub-agents (Parallel)
301
+
302
+ **Cron mode (`--cron` is active):**
303
+
304
+ - **Sequential cursor tracking:** Use a cursor file to track which issue to process next:
305
+
306
+ ```
307
+ CURSOR_FILE="/data/.clawdbot/gh-issues-cursor-{SOURCE_REPO_SLUG}.json"
308
+ # SOURCE_REPO_SLUG = owner-repo with slashes replaced by hyphens (e.g., openclaw-openclaw)
309
+ ```
310
+
311
+ Read the cursor file (create if missing):
312
+
313
+ ```
314
+ if [ ! -f "$CURSOR_FILE" ]; then
315
+ echo '{"last_processed": null, "in_progress": null}' > "$CURSOR_FILE"
316
+ fi
317
+ ```
318
+
319
+ - `last_processed`: issue number of the last completed issue (or null if none)
320
+ - `in_progress`: issue number currently being processed (or null)
321
+
322
+ - **Select next issue:** Filter the fetched issues list to find the first issue where:
323
+ - Issue number > last_processed (if last_processed is set)
324
+ - AND issue is not in the claims file (not already in progress)
325
+ - AND no PR exists for the issue (checked in Phase 4 step 5)
326
+ - AND no branch exists on the push repo (checked in Phase 4 step 6)
327
+ - If no eligible issue is found after the last_processed cursor, wrap around to the beginning (start from the oldest eligible issue).
328
+
329
+ - If an eligible issue is found:
330
+ 1. Mark it as in_progress in the cursor file
331
+ 2. Spawn a single sub-agent for that one issue with `cleanup: "keep"` and `runTimeoutSeconds: 3600`
332
+ 3. If `--model` was provided, include `model: "{MODEL}"` in the spawn config
333
+ 4. If `--notify-channel` was provided, include the channel in the task so the sub-agent can notify
334
+ 5. Do NOT await the sub-agent result — fire and forget
335
+ 6. **Write claim:** After spawning, read the claims file, add `{SOURCE_REPO}#{N}` with the current ISO timestamp, and write it back
336
+ 7. Immediately report: "Spawned fix agent for #{N} — will create PR when complete"
337
+ 8. Exit the skill. Do not proceed to Results Collection or Phase 6.
338
+
339
+ - 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.
340
+
341
+ **Normal mode (`--cron` is NOT active):**
342
+ For 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.
343
+
344
+ **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.
345
+
346
+ ### Sub-agent Task Prompt
347
+
348
+ For each issue, construct the following prompt and pass it to sessions_spawn. Variables to inject into the template:
349
+
350
+ - {SOURCE_REPO} — upstream repo where the issue lives
351
+ - {PUSH_REPO} — repo to push branches to (same as SOURCE_REPO unless fork mode)
352
+ - {FORK_MODE} — true/false
353
+ - {PUSH_REMOTE} — `fork` if FORK_MODE, otherwise `origin`
354
+ - {number}, {title}, {url}, {labels}, {body} — from the issue
355
+ - {BASE_BRANCH} — from Phase 4
356
+ - {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).
357
+
358
+ When constructing the task, replace all template variables including {notify_channel} with actual values.
359
+
360
+ ```
361
+ You are a focused code-fix agent. Your task is to fix a single GitHub issue and open a PR.
362
+
363
+ IMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.
364
+
365
+ First, ensure GH_TOKEN is set. Check: `echo $GH_TOKEN`. If empty, read from config:
366
+ CONFIG_PATH="${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}"
367
+ GH_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')
368
+
369
+ Use the token in all GitHub API calls:
370
+ curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" ...
371
+
372
+ <config>
373
+ Source repo (issues): {SOURCE_REPO}
374
+ Push repo (branches + PRs): {PUSH_REPO}
375
+ Fork mode: {FORK_MODE}
376
+ Push remote name: {PUSH_REMOTE}
377
+ Base branch: {BASE_BRANCH}
378
+ Notify channel: {notify_channel}
379
+ </config>
380
+
381
+ <issue>
382
+ Repository: {SOURCE_REPO}
383
+ Issue: #{number}
384
+ Title: {title}
385
+ URL: {url}
386
+ Labels: {labels}
387
+ Body: {body}
388
+ </issue>
389
+
390
+ <instructions>
391
+ Follow these steps in order. If any step fails, report the failure and stop.
392
+
393
+ 0. SETUP — Ensure GH_TOKEN is available:
394
+ ```
395
+
396
+ export 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 || '')")
397
+
398
+ ```
399
+ If that fails, also try:
400
+ ```
401
+
402
+ export CONFIG_PATH="${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}"
403
+ export 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||'')")
404
+
405
+ ```
406
+ Verify: echo "Token: ${GH_TOKEN:0:10}..."
407
+
408
+ 1. CONFIDENCE CHECK — Before implementing, assess whether this issue is actionable:
409
+ - Read the issue body carefully. Is the problem clearly described?
410
+ - Search the codebase (grep/find) for the relevant code. Can you locate it?
411
+ - Is the scope reasonable? (single file/function = good, whole subsystem = bad)
412
+ - Is a specific fix suggested or is it a vague complaint?
413
+
414
+ Rate your confidence (1-10). If confidence < 7, STOP and report:
415
+ > "Skipping #{number}: Low confidence (score: N/10) — [reason: vague requirements | cannot locate code | scope too large | no clear fix suggested]"
416
+
417
+ Only proceed if confidence >= 7.
418
+
419
+ 1. UNDERSTAND — Read the issue carefully. Identify what needs to change and where.
420
+
421
+ 2. BRANCH — Create a feature branch from the base branch:
422
+ git checkout -b fix/issue-{number} {BASE_BRANCH}
423
+
424
+ 3. ANALYZE — Search the codebase to find relevant files:
425
+ - Use grep/find via exec to locate code related to the issue
426
+ - Read the relevant files to understand the current behavior
427
+ - Identify the root cause
428
+
429
+ 4. IMPLEMENT — Make the minimal, focused fix:
430
+ - Follow existing code style and conventions
431
+ - Change only what is necessary to fix the issue
432
+ - Do not add unrelated changes or new dependencies without justification
433
+
434
+ 5. TEST — Discover and run the existing test suite if one exists:
435
+ - Look for package.json scripts, Makefile targets, pytest, cargo test, etc.
436
+ - Run the relevant tests
437
+ - If tests fail after your fix, attempt ONE retry with a corrected approach
438
+ - If tests still fail, report the failure
439
+
440
+ 6. COMMIT — Stage and commit your changes:
441
+ git add {changed_files}
442
+ git commit -m "fix: {short_description}
443
+
444
+ Fixes {SOURCE_REPO}#{number}"
445
+
446
+ 7. PUSH — Push the branch:
447
+ First, ensure the push remote uses token auth and disable credential helpers:
448
+ git config --global credential.helper ""
449
+ git remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git
450
+ Then push:
451
+ GIT_ASKPASS=true git push -u {PUSH_REMOTE} fix/issue-{number}
452
+
453
+ 8. PR — Create a pull request using the GitHub API:
454
+
455
+ If FORK_MODE is true, the PR goes from your fork to the source repo:
456
+ - head = "{PUSH_REPO_OWNER}:fix/issue-{number}"
457
+ - base = "{BASE_BRANCH}"
458
+ - PR is created on {SOURCE_REPO}
459
+
460
+ If FORK_MODE is false:
461
+ - head = "fix/issue-{number}"
462
+ - base = "{BASE_BRANCH}"
463
+ - PR is created on {SOURCE_REPO}
464
+
465
+ curl -s -X POST \
466
+ -H "Authorization: Bearer $GH_TOKEN" \
467
+ -H "Accept: application/vnd.github+json" \
468
+ https://api.github.com/repos/{SOURCE_REPO}/pulls \
469
+ -d '{
470
+ "title": "fix: {title}",
471
+ "head": "{head_value}",
472
+ "base": "{BASE_BRANCH}",
473
+ "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}"
474
+ }'
475
+
476
+ Extract the `html_url` from the response — this is the PR link.
477
+
478
+ 9. REPORT — Send back a summary:
479
+ - PR URL (the html_url from step 8)
480
+ - Files changed (list)
481
+ - Fix summary (1-2 sentences)
482
+ - Any caveats or concerns
483
+
484
+ 10. NOTIFY (if notify_channel is set) — If {notify_channel} is not empty, send a notification to the Telegram channel:
485
+ ```
486
+
487
+ Use the message tool with:
488
+
489
+ - action: "send"
490
+ - channel: "telegram"
491
+ - target: "{notify_channel}"
492
+ - message: "✅ PR Created: {SOURCE_REPO}#{number}
493
+
494
+ {title}
495
+
496
+ {pr_url}
497
+
498
+ Files changed: {files_changed_list}"
499
+
500
+ ```
501
+ </instructions>
502
+
503
+ <constraints>
504
+ - No force-push, no modifying the base branch
505
+ - No unrelated changes or gratuitous refactoring
506
+ - No new dependencies without strong justification
507
+ - If the issue is unclear or too complex to fix confidently, report your analysis instead of guessing
508
+ - Do NOT use the gh CLI — it is not available. Use curl + GitHub REST API for all GitHub operations.
509
+ - GH_TOKEN is already in the environment — do NOT prompt for auth
510
+ - Time limit: you have 60 minutes max. Be thorough — analyze properly, test your fix, don't rush.
511
+ </constraints>
512
+ ```
513
+
514
+ ### Spawn configuration per sub-agent:
515
+
516
+ - runTimeoutSeconds: 3600 (60 minutes)
517
+ - cleanup: "keep" (preserve transcripts for review)
518
+ - If `--model` was provided, include `model: "{MODEL}"` in the spawn config
519
+
520
+ ### Timeout Handling
521
+
522
+ If a sub-agent exceeds 60 minutes, record it as:
523
+
524
+ > "#{N} — Timed out (issue may be too complex for auto-fix)"
525
+
526
+ ---
527
+
528
+ ## Results Collection
529
+
530
+ **If `--cron` is active:** Skip this section entirely — the orchestrator already exited after spawning in Phase 5.
531
+
532
+ After 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.
533
+
534
+ Present a summary table:
535
+
536
+ | Issue | Status | PR | Notes |
537
+ | --------------------- | --------- | ------------------------------ | ------------------------------ |
538
+ | #42 Fix null pointer | PR opened | https://github.com/.../pull/99 | 3 files changed |
539
+ | #37 Add retry logic | Failed | -- | Could not identify target code |
540
+ | #15 Update docs | Timed out | -- | Too complex for auto-fix |
541
+ | #8 Fix race condition | Skipped | -- | PR already exists |
542
+
543
+ **Status values:**
544
+
545
+ - **PR opened** — success, link to PR
546
+ - **Failed** — sub-agent could not complete (include reason in Notes)
547
+ - **Timed out** — exceeded 60-minute limit
548
+ - **Skipped** — existing PR detected in pre-flight
549
+
550
+ End with a one-line summary:
551
+
552
+ > "Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped."
553
+
554
+ **Send notification to channel (if --notify-channel is set):**
555
+ If `--notify-channel` was provided, send the final summary to that Telegram channel using the `message` tool:
556
+
557
+ ```
558
+ Use the message tool with:
559
+ - action: "send"
560
+ - channel: "telegram"
561
+ - target: "{notify-channel}"
562
+ - message: "✅ GitHub Issues Processed
563
+
564
+ Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.
565
+
566
+ {PR_LIST}"
567
+
568
+ Where PR_LIST includes only successfully opened PRs in format:
569
+ • #{issue_number}: {PR_url} ({notes})
570
+ ```
571
+
572
+ Then proceed to Phase 6.
573
+
574
+ ---
575
+
576
+ ## Phase 6 — PR Review Handler
577
+
578
+ This phase monitors open PRs (created by this skill or pre-existing `fix/issue-*` PRs) for review comments and spawns sub-agents to address them.
579
+
580
+ **When this phase runs:**
581
+
582
+ - After Results Collection (Phases 2-5 completed) — checks PRs that were just opened
583
+ - When `--reviews-only` flag is set — skips Phases 2-5 entirely, runs only this phase
584
+ - In watch mode — runs every poll cycle after checking for new issues
585
+
586
+ **Cron review mode (`--cron --reviews-only`):**
587
+ When both `--cron` and `--reviews-only` are set:
588
+
589
+ 1. Run token resolution (Phase 2 token section)
590
+ 2. Discover open `fix/issue-*` PRs (Step 6.1)
591
+ 3. Fetch review comments (Step 6.2)
592
+ 4. **Analyze comment content for actionability** (Step 6.3)
593
+ 5. 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)
594
+ - Use `cleanup: "keep"` and `runTimeoutSeconds: 3600`
595
+ - If `--model` was provided, include `model: "{MODEL}"` in the spawn config
596
+ 6. Report: "Spawned review handler for PR #{N} — will push fixes when complete"
597
+ 7. Exit the skill immediately. Do not proceed to Step 6.5 (Review Results).
598
+
599
+ If no actionable comments found, report "No actionable review comments found" and exit.
600
+
601
+ **Normal mode (non-cron) continues below:**
602
+
603
+ ### Step 6.1 — Discover PRs to Monitor
604
+
605
+ Collect PRs to check for review comments:
606
+
607
+ **If coming from Phase 5:** Use the `OPEN_PRS` list from Results Collection.
608
+
609
+ **If `--reviews-only` or subsequent watch cycle:** Fetch all open PRs with `fix/issue-` branch pattern:
610
+
611
+ ```
612
+ curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
613
+ "https://api.github.com/repos/{SOURCE_REPO}/pulls?state=open&per_page=100"
614
+ ```
615
+
616
+ Filter to only PRs where `head.ref` starts with `fix/issue-`.
617
+
618
+ For each PR, extract: `number` (PR number), `head.ref` (branch name), `html_url`, `title`, `body`.
619
+
620
+ If no PRs found, report "No open fix/ PRs to monitor" and stop (or loop back if in watch mode).
621
+
622
+ ### Step 6.2 — Fetch All Review Sources
623
+
624
+ For each PR, fetch reviews from multiple sources:
625
+
626
+ **Fetch PR reviews:**
627
+
628
+ ```
629
+ curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
630
+ "https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/reviews"
631
+ ```
632
+
633
+ **Fetch PR review comments (inline/file-level):**
634
+
635
+ ```
636
+ curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
637
+ "https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments"
638
+ ```
639
+
640
+ **Fetch PR issue comments (general conversation):**
641
+
642
+ ```
643
+ curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
644
+ "https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments"
645
+ ```
646
+
647
+ **Fetch PR body for embedded reviews:**
648
+ Some review tools (like Greptile) embed their feedback directly in the PR body. Check for:
649
+
650
+ - `<!-- greptile_comment -->` markers
651
+ - Other structured review sections in the PR body
652
+
653
+ ```
654
+ curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
655
+ "https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}"
656
+ ```
657
+
658
+ Extract the `body` field and parse for embedded review content.
659
+
660
+ ### Step 6.3 — Analyze Comments for Actionability
661
+
662
+ **Determine the bot's own username** for filtering:
663
+
664
+ ```
665
+ curl -s -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/user | jq -r '.login'
666
+ ```
667
+
668
+ Store as `BOT_USERNAME`. Exclude any comment where `user.login` equals `BOT_USERNAME`.
669
+
670
+ **For each comment/review, analyze the content to determine if it requires action:**
671
+
672
+ **NOT actionable (skip):**
673
+
674
+ - Pure approvals or "LGTM" without suggestions
675
+ - Bot comments that are informational only (CI status, auto-generated summaries without specific requests)
676
+ - Comments already addressed (check if bot replied with "Addressed in commit...")
677
+ - Reviews with state `APPROVED` and no inline comments requesting changes
678
+
679
+ **IS actionable (requires attention):**
680
+
681
+ - Reviews with state `CHANGES_REQUESTED`
682
+ - Reviews with state `COMMENTED` that contain specific requests:
683
+ - "this test needs to be updated"
684
+ - "please fix", "change this", "update", "can you", "should be", "needs to"
685
+ - "will fail", "will break", "causes an error"
686
+ - Mentions of specific code issues (bugs, missing error handling, edge cases)
687
+ - Inline review comments pointing out issues in the code
688
+ - Embedded reviews in PR body that identify:
689
+ - Critical issues or breaking changes
690
+ - Test failures expected
691
+ - Specific code that needs attention
692
+ - Confidence scores with concerns
693
+
694
+ **Parse embedded review content (e.g., Greptile):**
695
+ Look for sections marked with `<!-- greptile_comment -->` or similar. Extract:
696
+
697
+ - Summary text
698
+ - Any mentions of "Critical issue", "needs attention", "will fail", "test needs to be updated"
699
+ - Confidence scores below 4/5 (indicates concerns)
700
+
701
+ **Build actionable_comments list** with:
702
+
703
+ - Source (review, inline comment, PR body, etc.)
704
+ - Author
705
+ - Body text
706
+ - For inline: file path and line number
707
+ - Specific action items identified
708
+
709
+ If no actionable comments found across any PR, report "No actionable review comments found" and stop (or loop back if in watch mode).
710
+
711
+ ### Step 6.4 — Present Review Comments
712
+
713
+ Display a table of PRs with pending actionable comments:
714
+
715
+ ```
716
+ | PR | Branch | Actionable Comments | Sources |
717
+ |----|--------|---------------------|---------|
718
+ | #99 | fix/issue-42 | 2 comments | @reviewer1, greptile |
719
+ | #101 | fix/issue-37 | 1 comment | @reviewer2 |
720
+ ```
721
+
722
+ If `--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").
723
+
724
+ ### Step 6.5 — Spawn Review Fix Sub-agents (Parallel)
725
+
726
+ For each PR with actionable comments, spawn a sub-agent. Launch up to 8 concurrently.
727
+
728
+ **Review fix sub-agent prompt:**
729
+
730
+ ```
731
+ You 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.
732
+
733
+ IMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.
734
+
735
+ First, ensure GH_TOKEN is set. Check: echo $GH_TOKEN. If empty, read from config:
736
+ CONFIG_PATH="${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/openclaw.json}"
737
+ GH_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')
738
+
739
+ <config>
740
+ Repository: {SOURCE_REPO}
741
+ Push repo: {PUSH_REPO}
742
+ Fork mode: {FORK_MODE}
743
+ Push remote: {PUSH_REMOTE}
744
+ PR number: {pr_number}
745
+ PR URL: {pr_url}
746
+ Branch: {branch_name}
747
+ </config>
748
+
749
+ <review_comments>
750
+ {json_array_of_actionable_comments}
751
+
752
+ Each comment has:
753
+ - id: comment ID (for replying)
754
+ - user: who left it
755
+ - body: the comment text
756
+ - path: file path (for inline comments)
757
+ - line: line number (for inline comments)
758
+ - diff_hunk: surrounding diff context (for inline comments)
759
+ - source: where the comment came from (review, inline, pr_body, greptile, etc.)
760
+ </review_comments>
761
+
762
+ <instructions>
763
+ Follow these steps in order:
764
+
765
+ 0. SETUP — Ensure GH_TOKEN is available:
766
+ ```
767
+
768
+ export 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 || '')")
769
+
770
+ ```
771
+ Verify: echo "Token: ${GH_TOKEN:0:10}..."
772
+
773
+ 1. CHECKOUT — Switch to the PR branch:
774
+ git fetch {PUSH_REMOTE} {branch_name}
775
+ git checkout {branch_name}
776
+ git pull {PUSH_REMOTE} {branch_name}
777
+
778
+ 2. UNDERSTAND — Read ALL review comments carefully. Group them by file. Understand what each reviewer is asking for.
779
+
780
+ 3. IMPLEMENT — For each comment, make the requested change:
781
+ - Read the file and locate the relevant code
782
+ - Make the change the reviewer requested
783
+ - If the comment is vague or you disagree, still attempt a reasonable fix but note your concern
784
+ - If the comment asks for something impossible or contradictory, skip it and explain why in your reply
785
+
786
+ 4. TEST — Run existing tests to make sure your changes don't break anything:
787
+ - If tests fail, fix the issue or revert the problematic change
788
+ - Note any test failures in your replies
789
+
790
+ 5. COMMIT — Stage and commit all changes in a single commit:
791
+ git add {changed_files}
792
+ git commit -m "fix: address review comments on PR #{pr_number}
793
+
794
+ Addresses review feedback from {reviewer_names}"
795
+
796
+ 6. PUSH — Push the updated branch:
797
+ git config --global credential.helper ""
798
+ git remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git
799
+ GIT_ASKPASS=true git push {PUSH_REMOTE} {branch_name}
800
+
801
+ 7. REPLY — For each addressed comment, post a reply:
802
+
803
+ For inline review comments (have a path/line), reply to the comment thread:
804
+ curl -s -X POST \
805
+ -H "Authorization: Bearer $GH_TOKEN" \
806
+ -H "Accept: application/vnd.github+json" \
807
+ https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments/{comment_id}/replies \
808
+ -d '{"body": "Addressed in commit {short_sha} — {brief_description_of_change}"}'
809
+
810
+ For general PR comments (issue comments), reply on the PR:
811
+ curl -s -X POST \
812
+ -H "Authorization: Bearer $GH_TOKEN" \
813
+ -H "Accept: application/vnd.github+json" \
814
+ https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments \
815
+ -d '{"body": "Addressed feedback from @{reviewer}:\n\n{summary_of_changes_made}\n\nUpdated in commit {short_sha}"}'
816
+
817
+ For comments you could NOT address, reply explaining why:
818
+ "Unable to address this comment: {reason}. This may need manual review."
819
+
820
+ 8. REPORT — Send back a summary:
821
+ - PR URL
822
+ - Number of comments addressed vs skipped
823
+ - Commit SHA
824
+ - Files changed
825
+ - Any comments that need manual attention
826
+ </instructions>
827
+
828
+ <constraints>
829
+ - Only modify files relevant to the review comments
830
+ - Do not make unrelated changes
831
+ - Do not force-push — always regular push
832
+ - If a comment contradicts another comment, address the most recent one and flag the conflict
833
+ - Do NOT use the gh CLI — use curl + GitHub REST API
834
+ - GH_TOKEN is already in the environment — do not prompt for auth
835
+ - Time limit: 60 minutes max
836
+ </constraints>
837
+ ```
838
+
839
+ **Spawn configuration per sub-agent:**
840
+
841
+ - runTimeoutSeconds: 3600 (60 minutes)
842
+ - cleanup: "keep" (preserve transcripts for review)
843
+ - If `--model` was provided, include `model: "{MODEL}"` in the spawn config
844
+
845
+ ### Step 6.6 — Review Results
846
+
847
+ After all review sub-agents complete, present a summary:
848
+
849
+ ```
850
+ | PR | Comments Addressed | Comments Skipped | Commit | Status |
851
+ |----|-------------------|-----------------|--------|--------|
852
+ | #99 fix/issue-42 | 3 | 0 | abc123f | All addressed |
853
+ | #101 fix/issue-37 | 1 | 1 | def456a | 1 needs manual review |
854
+ ```
855
+
856
+ Add comment IDs from this batch to `ADDRESSED_COMMENTS` set to prevent re-processing.
857
+
858
+ ---
859
+
860
+ ## Watch Mode (if --watch is active)
861
+
862
+ After presenting results from the current batch:
863
+
864
+ 1. Add all issue numbers from this batch to the running set PROCESSED_ISSUES.
865
+ 2. Add all addressed comment IDs to ADDRESSED_COMMENTS.
866
+ 3. Tell the user:
867
+ > "Next poll in {interval} minutes... (say 'stop' to end watch mode)"
868
+ 4. Sleep for {interval} minutes.
869
+ 5. Go back to **Phase 2 — Fetch Issues**. The fetch will automatically filter out:
870
+ - Issues already in PROCESSED_ISSUES
871
+ - Issues that have existing fix/issue-{N} PRs (caught in Phase 4 pre-flight)
872
+ 6. 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).
873
+ 7. 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.
874
+ 8. 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.
875
+
876
+ **Context hygiene between polls — IMPORTANT:**
877
+ Only retain between poll cycles:
878
+
879
+ - PROCESSED_ISSUES (set of issue numbers)
880
+ - ADDRESSED_COMMENTS (set of comment IDs)
881
+ - OPEN_PRS (list of tracked PRs: number, branch, URL)
882
+ - Cumulative results (one line per issue + one line per review batch)
883
+ - Parsed arguments from Phase 1
884
+ - BASE_BRANCH, SOURCE_REPO, PUSH_REPO, FORK_MODE, BOT_USERNAME
885
+ Do NOT retain issue bodies, comment bodies, sub-agent transcripts, or codebase analysis between polls.
skills/gifgrep/SKILL.md ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: gifgrep
3
+ description: Search GIF providers with CLI/TUI, download results, and extract stills/sheets.
4
+ homepage: https://gifgrep.com
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🧲",
10
+ "requires": { "bins": ["gifgrep"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "steipete/tap/gifgrep",
17
+ "bins": ["gifgrep"],
18
+ "label": "Install gifgrep (brew)",
19
+ },
20
+ {
21
+ "id": "go",
22
+ "kind": "go",
23
+ "module": "github.com/steipete/gifgrep/cmd/gifgrep@latest",
24
+ "bins": ["gifgrep"],
25
+ "label": "Install gifgrep (go)",
26
+ },
27
+ ],
28
+ },
29
+ }
30
+ ---
31
+
32
+ # gifgrep
33
+
34
+ Use `gifgrep` to search GIF providers (Tenor/Giphy), browse in a TUI, download results, and extract stills or sheets.
35
+
36
+ GIF-Grab (gifgrep workflow)
37
+
38
+ - Search → preview → download → extract (still/sheet) for fast review and sharing.
39
+
40
+ Quick start
41
+
42
+ - `gifgrep cats --max 5`
43
+ - `gifgrep cats --format url | head -n 5`
44
+ - `gifgrep search --json cats | jq '.[0].url'`
45
+ - `gifgrep tui "office handshake"`
46
+ - `gifgrep cats --download --max 1 --format url`
47
+
48
+ TUI + previews
49
+
50
+ - TUI: `gifgrep tui "query"`
51
+ - CLI still previews: `--thumbs` (Kitty/Ghostty only; still frame)
52
+
53
+ Download + reveal
54
+
55
+ - `--download` saves to `~/Downloads`
56
+ - `--reveal` shows the last download in Finder
57
+
58
+ Stills + sheets
59
+
60
+ - `gifgrep still ./clip.gif --at 1.5s -o still.png`
61
+ - `gifgrep sheet ./clip.gif --frames 9 --cols 3 -o sheet.png`
62
+ - Sheets = single PNG grid of sampled frames (great for quick review, docs, PRs, chat).
63
+ - Tune: `--frames` (count), `--cols` (grid width), `--padding` (spacing).
64
+
65
+ Providers
66
+
67
+ - `--source auto|tenor|giphy`
68
+ - `GIPHY_API_KEY` required for `--source giphy`
69
+ - `TENOR_API_KEY` optional (Tenor demo key used if unset)
70
+
71
+ Output
72
+
73
+ - `--json` prints an array of results (`id`, `title`, `url`, `preview_url`, `tags`, `width`, `height`)
74
+ - `--format` for pipe-friendly fields (e.g., `url`)
75
+
76
+ Environment tweaks
77
+
78
+ - `GIFGREP_SOFTWARE_ANIM=1` to force software animation
79
+ - `GIFGREP_CELL_ASPECT=0.5` to tweak preview geometry
skills/github/SKILL.md ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: github
3
+ description: "GitHub operations via `gh` CLI: issues, PRs, CI runs, code review, API queries. Use when: (1) checking PR status or CI, (2) creating/commenting on issues, (3) listing/filtering PRs or issues, (4) viewing run logs. NOT for: complex web UI interactions requiring manual browser flows (use browser tooling when available), bulk operations across many repos (script with gh api), or when gh auth is not configured."
4
+ metadata:
5
+ {
6
+ "openclaw":
7
+ {
8
+ "emoji": "🐙",
9
+ "requires": { "bins": ["gh"] },
10
+ "install":
11
+ [
12
+ {
13
+ "id": "brew",
14
+ "kind": "brew",
15
+ "formula": "gh",
16
+ "bins": ["gh"],
17
+ "label": "Install GitHub CLI (brew)",
18
+ },
19
+ {
20
+ "id": "apt",
21
+ "kind": "apt",
22
+ "package": "gh",
23
+ "bins": ["gh"],
24
+ "label": "Install GitHub CLI (apt)",
25
+ },
26
+ ],
27
+ },
28
+ }
29
+ ---
30
+
31
+ # GitHub Skill
32
+
33
+ Use the `gh` CLI to interact with GitHub repositories, issues, PRs, and CI.
34
+
35
+ ## When to Use
36
+
37
+ ✅ **USE this skill when:**
38
+
39
+ - Checking PR status, reviews, or merge readiness
40
+ - Viewing CI/workflow run status and logs
41
+ - Creating, closing, or commenting on issues
42
+ - Creating or merging pull requests
43
+ - Querying GitHub API for repository data
44
+ - Listing repos, releases, or collaborators
45
+
46
+ ## When NOT to Use
47
+
48
+ ❌ **DON'T use this skill when:**
49
+
50
+ - Local git operations (commit, push, pull, branch) → use `git` directly
51
+ - Non-GitHub repos (GitLab, Bitbucket, self-hosted) → different CLIs
52
+ - Cloning repositories → use `git clone`
53
+ - Reviewing actual code changes → use `coding-agent` skill
54
+ - Complex multi-file diffs → use `coding-agent` or read files directly
55
+
56
+ ## Setup
57
+
58
+ ```bash
59
+ # Authenticate (one-time)
60
+ gh auth login
61
+
62
+ # Verify
63
+ gh auth status
64
+ ```
65
+
66
+ ## Common Commands
67
+
68
+ ### Pull Requests
69
+
70
+ ```bash
71
+ # List PRs
72
+ gh pr list --repo owner/repo
73
+
74
+ # Check CI status
75
+ gh pr checks 55 --repo owner/repo
76
+
77
+ # View PR details
78
+ gh pr view 55 --repo owner/repo
79
+
80
+ # Create PR
81
+ gh pr create --title "feat: add feature" --body "Description"
82
+
83
+ # Merge PR
84
+ gh pr merge 55 --squash --repo owner/repo
85
+ ```
86
+
87
+ ### Issues
88
+
89
+ ```bash
90
+ # List issues
91
+ gh issue list --repo owner/repo --state open
92
+
93
+ # Create issue
94
+ gh issue create --title "Bug: something broken" --body "Details..."
95
+
96
+ # Close issue
97
+ gh issue close 42 --repo owner/repo
98
+ ```
99
+
100
+ ### CI/Workflow Runs
101
+
102
+ ```bash
103
+ # List recent runs
104
+ gh run list --repo owner/repo --limit 10
105
+
106
+ # View specific run
107
+ gh run view <run-id> --repo owner/repo
108
+
109
+ # View failed step logs only
110
+ gh run view <run-id> --repo owner/repo --log-failed
111
+
112
+ # Re-run failed jobs
113
+ gh run rerun <run-id> --failed --repo owner/repo
114
+ ```
115
+
116
+ ### API Queries
117
+
118
+ ```bash
119
+ # Get PR with specific fields
120
+ gh api repos/owner/repo/pulls/55 --jq '.title, .state, .user.login'
121
+
122
+ # List all labels
123
+ gh api repos/owner/repo/labels --jq '.[].name'
124
+
125
+ # Get repo stats
126
+ gh api repos/owner/repo --jq '{stars: .stargazers_count, forks: .forks_count}'
127
+ ```
128
+
129
+ ## JSON Output
130
+
131
+ Most commands support `--json` for structured output with `--jq` filtering:
132
+
133
+ ```bash
134
+ gh issue list --repo owner/repo --json number,title --jq '.[] | "\(.number): \(.title)"'
135
+ gh pr list --json number,title,state,mergeable --jq '.[] | select(.mergeable == "MERGEABLE")'
136
+ ```
137
+
138
+ ## Templates
139
+
140
+ ### PR Review Summary
141
+
142
+ ```bash
143
+ # Get PR overview for review
144
+ PR=55 REPO=owner/repo
145
+ echo "## PR #$PR Summary"
146
+ gh pr view $PR --repo $REPO --json title,body,author,additions,deletions,changedFiles \
147
+ --jq '"**\(.title)** by @\(.author.login)\n\n\(.body)\n\n📊 +\(.additions) -\(.deletions) across \(.changedFiles) files"'
148
+ gh pr checks $PR --repo $REPO
149
+ ```
150
+
151
+ ### Issue Triage
152
+
153
+ ```bash
154
+ # Quick issue triage view
155
+ gh issue list --repo owner/repo --state open --json number,title,labels,createdAt \
156
+ --jq '.[] | "[\(.number)] \(.title) - \([.labels[].name] | join(", ")) (\(.createdAt[:10]))"'
157
+ ```
158
+
159
+ ## Notes
160
+
161
+ - Always specify `--repo owner/repo` when not in a git directory
162
+ - Use URLs directly: `gh pr view https://github.com/owner/repo/pull/55`
163
+ - Rate limits apply; use `gh api --cache 1h` for repeated queries
skills/gog/SKILL.md ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: gog
3
+ description: Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.
4
+ homepage: https://gogcli.sh
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🎮",
10
+ "requires": { "bins": ["gog"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "steipete/tap/gogcli",
17
+ "bins": ["gog"],
18
+ "label": "Install gog (brew)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # gog
26
+
27
+ Use `gog` for Gmail/Calendar/Drive/Contacts/Sheets/Docs. Requires OAuth setup.
28
+
29
+ Setup (once)
30
+
31
+ - `gog auth credentials /path/to/client_secret.json`
32
+ - `gog auth add you@gmail.com --services gmail,calendar,drive,contacts,docs,sheets`
33
+ - `gog auth list`
34
+
35
+ Common commands
36
+
37
+ - Gmail search: `gog gmail search 'newer_than:7d' --max 10`
38
+ - Gmail messages search (per email, ignores threading): `gog gmail messages search "in:inbox from:ryanair.com" --max 20 --account you@example.com`
39
+ - Gmail send (plain): `gog gmail send --to a@b.com --subject "Hi" --body "Hello"`
40
+ - Gmail send (multi-line): `gog gmail send --to a@b.com --subject "Hi" --body-file ./message.txt`
41
+ - Gmail send (stdin): `gog gmail send --to a@b.com --subject "Hi" --body-file -`
42
+ - Gmail send (HTML): `gog gmail send --to a@b.com --subject "Hi" --body-html "<p>Hello</p>"`
43
+ - Gmail draft: `gog gmail drafts create --to a@b.com --subject "Hi" --body-file ./message.txt`
44
+ - Gmail send draft: `gog gmail drafts send <draftId>`
45
+ - Gmail reply: `gog gmail send --to a@b.com --subject "Re: Hi" --body "Reply" --reply-to-message-id <msgId>`
46
+ - Calendar list events: `gog calendar events <calendarId> --from <iso> --to <iso>`
47
+ - Calendar create event: `gog calendar create <calendarId> --summary "Title" --from <iso> --to <iso>`
48
+ - Calendar create with color: `gog calendar create <calendarId> --summary "Title" --from <iso> --to <iso> --event-color 7`
49
+ - Calendar update event: `gog calendar update <calendarId> <eventId> --summary "New Title" --event-color 4`
50
+ - Calendar show colors: `gog calendar colors`
51
+ - Drive search: `gog drive search "query" --max 10`
52
+ - Contacts: `gog contacts list --max 20`
53
+ - Sheets get: `gog sheets get <sheetId> "Tab!A1:D10" --json`
54
+ - Sheets update: `gog sheets update <sheetId> "Tab!A1:B2" --values-json '[["A","B"],["1","2"]]' --input USER_ENTERED`
55
+ - Sheets append: `gog sheets append <sheetId> "Tab!A:C" --values-json '[["x","y","z"]]' --insert INSERT_ROWS`
56
+ - Sheets clear: `gog sheets clear <sheetId> "Tab!A2:Z"`
57
+ - Sheets metadata: `gog sheets metadata <sheetId> --json`
58
+ - Docs export: `gog docs export <docId> --format txt --out /tmp/doc.txt`
59
+ - Docs cat: `gog docs cat <docId>`
60
+
61
+ Calendar Colors
62
+
63
+ - Use `gog calendar colors` to see all available event colors (IDs 1-11)
64
+ - Add colors to events with `--event-color <id>` flag
65
+ - Event color IDs (from `gog calendar colors` output):
66
+ - 1: #a4bdfc
67
+ - 2: #7ae7bf
68
+ - 3: #dbadff
69
+ - 4: #ff887c
70
+ - 5: #fbd75b
71
+ - 6: #ffb878
72
+ - 7: #46d6db
73
+ - 8: #e1e1e1
74
+ - 9: #5484ed
75
+ - 10: #51b749
76
+ - 11: #dc2127
77
+
78
+ Email Formatting
79
+
80
+ - Prefer plain text. Use `--body-file` for multi-paragraph messages (or `--body-file -` for stdin).
81
+ - Same `--body-file` pattern works for drafts and replies.
82
+ - `--body` does not unescape `\n`. If you need inline newlines, use a heredoc or `$'Line 1\n\nLine 2'`.
83
+ - Use `--body-html` only when you need rich formatting.
84
+ - HTML tags: `<p>` for paragraphs, `<br>` for line breaks, `<strong>` for bold, `<em>` for italic, `<a href="url">` for links, `<ul>`/`<li>` for lists.
85
+ - Example (plain text via stdin):
86
+
87
+ ```bash
88
+ gog gmail send --to recipient@example.com \
89
+ --subject "Meeting Follow-up" \
90
+ --body-file - <<'EOF'
91
+ Hi Name,
92
+
93
+ Thanks for meeting today. Next steps:
94
+ - Item one
95
+ - Item two
96
+
97
+ Best regards,
98
+ Your Name
99
+ EOF
100
+ ```
101
+
102
+ - Example (HTML list):
103
+ ```bash
104
+ gog gmail send --to recipient@example.com \
105
+ --subject "Meeting Follow-up" \
106
+ --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>"
107
+ ```
108
+
109
+ Notes
110
+
111
+ - Set `GOG_ACCOUNT=you@gmail.com` to avoid repeating `--account`.
112
+ - For scripting, prefer `--json` plus `--no-input`.
113
+ - Sheets values can be passed via `--values-json` (recommended) or as inline rows.
114
+ - Docs supports export/cat/copy. In-place edits require a Docs API client (not in gog).
115
+ - Confirm before sending mail or creating events.
116
+ - `gog gmail search` returns one row per thread; use `gog gmail messages search` when you need every individual email returned separately.
skills/goplaces/SKILL.md ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: goplaces
3
+ 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.
4
+ homepage: https://github.com/steipete/goplaces
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "📍",
10
+ "requires": { "bins": ["goplaces"], "env": ["GOOGLE_PLACES_API_KEY"] },
11
+ "primaryEnv": "GOOGLE_PLACES_API_KEY",
12
+ "install":
13
+ [
14
+ {
15
+ "id": "brew",
16
+ "kind": "brew",
17
+ "formula": "steipete/tap/goplaces",
18
+ "bins": ["goplaces"],
19
+ "label": "Install goplaces (brew)",
20
+ },
21
+ ],
22
+ },
23
+ }
24
+ ---
25
+
26
+ # goplaces
27
+
28
+ Modern Google Places API (New) CLI. Human output by default, `--json` for scripts.
29
+
30
+ Install
31
+
32
+ - Homebrew: `brew install steipete/tap/goplaces`
33
+
34
+ Config
35
+
36
+ - `GOOGLE_PLACES_API_KEY` required.
37
+ - Optional: `GOOGLE_PLACES_BASE_URL` for testing/proxying.
38
+
39
+ Common commands
40
+
41
+ - Search: `goplaces search "coffee" --open-now --min-rating 4 --limit 5`
42
+ - Bias: `goplaces search "pizza" --lat 40.8 --lng -73.9 --radius-m 3000`
43
+ - Pagination: `goplaces search "pizza" --page-token "NEXT_PAGE_TOKEN"`
44
+ - Resolve: `goplaces resolve "Soho, London" --limit 5`
45
+ - Details: `goplaces details <place_id> --reviews`
46
+ - JSON: `goplaces search "sushi" --json`
47
+
48
+ Notes
49
+
50
+ - `--no-color` or `NO_COLOR` disables ANSI color.
51
+ - Price levels: 0..4 (free → very expensive).
52
+ - Type filter sends only the first `--type` value (API accepts one).
skills/healthcheck/SKILL.md ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: healthcheck
3
+ 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).
4
+ ---
5
+
6
+ # OpenClaw Host Hardening
7
+
8
+ ## Overview
9
+
10
+ Assess 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.
11
+
12
+ ## Core rules
13
+
14
+ - 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.
15
+ - Require explicit approval before any state-changing action.
16
+ - Do not modify remote access settings without confirming how the user connects.
17
+ - Prefer reversible, staged changes with a rollback plan.
18
+ - Never claim OpenClaw changes the host firewall, SSH, or OS updates; it does not.
19
+ - If role/identity is unknown, provide recommendations only.
20
+ - Formatting: every set of user choices must be numbered so the user can reply with a single digit.
21
+ - System-level backups are recommended; try to verify status.
22
+
23
+ ## Workflow (follow in order)
24
+
25
+ ### 0) Model self-check (non-blocking)
26
+
27
+ Before 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.
28
+
29
+ ### 1) Establish context (read-only)
30
+
31
+ Try to infer 1–5 from the environment before asking. Prefer simple, non-technical questions if you need confirmation.
32
+
33
+ Determine (in order):
34
+
35
+ 1. OS and version (Linux/macOS/Windows), container vs host.
36
+ 2. Privilege level (root/admin vs user).
37
+ 3. Access path (local console, SSH, RDP, tailnet).
38
+ 4. Network exposure (public IP, reverse proxy, tunnel).
39
+ 5. OpenClaw gateway status and bind address.
40
+ 6. Backup system and status (e.g., Time Machine, system images, snapshots).
41
+ 7. Deployment context (local mac app, headless gateway host, remote gateway, container/CI).
42
+ 8. Disk encryption status (FileVault/LUKS/BitLocker).
43
+ 9. OS automatic security updates status.
44
+ Note: these are not blocking items, but are highly recommended, especially if OpenClaw can access sensitive data.
45
+ 10. Usage mode for a personal assistant with full access (local workstation vs headless/remote vs other).
46
+
47
+ First 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.
48
+
49
+ If you must ask, use non-technical prompts:
50
+
51
+ - “Are you using a Mac, Windows PC, or Linux?”
52
+ - “Are you logged in directly on the machine, or connecting from another computer?”
53
+ - “Is this machine reachable from the public internet, or only on your home/network?”
54
+ - “Do you have backups enabled (e.g., Time Machine), and are they current?”
55
+ - “Is disk encryption turned on (FileVault/BitLocker/LUKS)?”
56
+ - “Are automatic security updates enabled?”
57
+ - “How do you use this machine?”
58
+ Examples:
59
+ - Personal machine shared with the assistant
60
+ - Dedicated local machine for the assistant
61
+ - Dedicated remote machine/server accessed remotely (always on)
62
+ - Something else?
63
+
64
+ Only ask for the risk profile after system context is known.
65
+
66
+ If the user grants read-only permission, run the OS-appropriate checks by default. If not, offer them (numbered). Examples:
67
+
68
+ 1. OS: `uname -a`, `sw_vers`, `cat /etc/os-release`.
69
+ 2. Listening ports:
70
+ - Linux: `ss -ltnup` (or `ss -ltnp` if `-u` unsupported).
71
+ - macOS: `lsof -nP -iTCP -sTCP:LISTEN`.
72
+ 3. Firewall status:
73
+ - Linux: `ufw status`, `firewall-cmd --state`, `nft list ruleset` (pick what is installed).
74
+ - macOS: `/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate` and `pfctl -s info`.
75
+ 4. Backups (macOS): `tmutil status` (if Time Machine is used).
76
+
77
+ ### 2) Run OpenClaw security audits (read-only)
78
+
79
+ As part of the default read-only checks, run `openclaw security audit --deep`. Only offer alternatives if the user requests them:
80
+
81
+ 1. `openclaw security audit` (faster, non-probing)
82
+ 2. `openclaw security audit --json` (structured output)
83
+
84
+ Offer to apply OpenClaw safe defaults (numbered):
85
+
86
+ 1. `openclaw security audit --fix`
87
+
88
+ Be explicit that `--fix` only tightens OpenClaw defaults and file permissions. It does not change host firewall, SSH, or OS update policies.
89
+
90
+ If browser control is enabled, recommend that 2FA be enabled on all important accounts, with hardware keys preferred and SMS not sufficient.
91
+
92
+ ### 3) Check OpenClaw version/update status (read-only)
93
+
94
+ As part of the default read-only checks, run `openclaw update status`.
95
+
96
+ Report the current channel and whether an update is available.
97
+
98
+ ### 4) Determine risk tolerance (after system context)
99
+
100
+ Ask the user to pick or confirm a risk posture and any required open services/ports (numbered choices below).
101
+ Do not pigeonhole into fixed profiles; if the user prefers, capture requirements instead of choosing a profile.
102
+ Offer suggested profiles as optional defaults (numbered). Note that most users pick Home/Workstation Balanced:
103
+
104
+ 1. Home/Workstation Balanced (most common): firewall on with reasonable defaults, remote access restricted to LAN or tailnet.
105
+ 2. VPS Hardened: deny-by-default inbound firewall, minimal open ports, key-only SSH, no root login, automatic security updates.
106
+ 3. Developer Convenience: more local services allowed, explicit exposure warnings, still audited.
107
+ 4. Custom: user-defined constraints (services, exposure, update cadence, access methods).
108
+
109
+ ### 5) Produce a remediation plan
110
+
111
+ Provide a plan that includes:
112
+
113
+ - Target profile
114
+ - Current posture summary
115
+ - Gaps vs target
116
+ - Step-by-step remediation with exact commands
117
+ - Access-preservation strategy and rollback
118
+ - Risks and potential lockout scenarios
119
+ - Least-privilege notes (e.g., avoid admin usage, tighten ownership/permissions where safe)
120
+ - Credential hygiene notes (location of OpenClaw creds, prefer disk encryption)
121
+
122
+ Always show the plan before any changes.
123
+
124
+ ### 6) Offer execution options
125
+
126
+ Offer one of these choices (numbered so users can reply with a single digit):
127
+
128
+ 1. Do it for me (guided, step-by-step approvals)
129
+ 2. Show plan only
130
+ 3. Fix only critical issues
131
+ 4. Export commands for later
132
+
133
+ ### 7) Execute with confirmations
134
+
135
+ For each step:
136
+
137
+ - Show the exact command
138
+ - Explain impact and rollback
139
+ - Confirm access will remain available
140
+ - Stop on unexpected output and ask for guidance
141
+
142
+ ### 8) Verify and report
143
+
144
+ Re-check:
145
+
146
+ - Firewall status
147
+ - Listening ports
148
+ - Remote access still works
149
+ - OpenClaw security audit (re-run)
150
+
151
+ Deliver a final posture report and note any deferred items.
152
+
153
+ ## Required confirmations (always)
154
+
155
+ Require explicit approval for:
156
+
157
+ - Firewall rule changes
158
+ - Opening/closing ports
159
+ - SSH/RDP configuration changes
160
+ - Installing/removing packages
161
+ - Enabling/disabling services
162
+ - User/group modifications
163
+ - Scheduling tasks or startup persistence
164
+ - Update policy changes
165
+ - Access to sensitive files or credentials
166
+
167
+ If unsure, ask.
168
+
169
+ ## Periodic checks
170
+
171
+ After OpenClaw install or first hardening pass, run at least one baseline audit and version check:
172
+
173
+ - `openclaw security audit`
174
+ - `openclaw security audit --deep`
175
+ - `openclaw update status`
176
+
177
+ Ongoing 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.
178
+ When scheduling headless cron runs, include a note in the output that instructs the user to call `healthcheck` so issues can be fixed.
179
+
180
+ ### Required prompt to schedule (always)
181
+
182
+ After any audit or hardening pass, explicitly offer scheduling and require a direct response. Use a short prompt like (numbered):
183
+
184
+ 1. “Do you want me to schedule periodic audits (e.g., daily/weekly) via `openclaw cron add`?”
185
+
186
+ If the user says yes, ask for:
187
+
188
+ - cadence (daily/weekly), preferred time window, and output location
189
+ - whether to also schedule `openclaw update status`
190
+
191
+ Use a stable cron job name so updates are deterministic. Prefer exact names:
192
+
193
+ - `healthcheck:security-audit`
194
+ - `healthcheck:update-status`
195
+
196
+ Before creating, `openclaw cron list` and match on exact `name`. If found, `openclaw cron edit <id> ...`.
197
+ If not found, `openclaw cron add --name <name> ...`.
198
+
199
+ Also offer a periodic version check so the user can decide when to update (numbered):
200
+
201
+ 1. `openclaw update status` (preferred for source checkouts and channels)
202
+ 2. `npm view openclaw version` (published npm version)
203
+
204
+ ## OpenClaw command accuracy
205
+
206
+ Use only supported commands and flags:
207
+
208
+ - `openclaw security audit [--deep] [--fix] [--json]`
209
+ - `openclaw status` / `openclaw status --deep`
210
+ - `openclaw health --json`
211
+ - `openclaw update status`
212
+ - `openclaw cron add|list|runs|run`
213
+
214
+ Do not invent CLI flags or imply OpenClaw enforces host firewall/SSH policies.
215
+
216
+ ## Logging and audit trail
217
+
218
+ Record:
219
+
220
+ - Gateway identity and role
221
+ - Plan ID and timestamp
222
+ - Approved steps and exact commands
223
+ - Exit codes and files modified (best effort)
224
+
225
+ Redact secrets. Never log tokens or full credential contents.
226
+
227
+ ## Memory writes (conditional)
228
+
229
+ Only write to memory files when the user explicitly opts in and the session is a private/local workspace
230
+ (per `docs/reference/templates/AGENTS.md`). Otherwise provide a redacted, paste-ready summary the user can
231
+ decide to save elsewhere.
232
+
233
+ Follow the durable-memory prompt format used by OpenClaw compaction:
234
+
235
+ - Write lasting notes to `memory/YYYY-MM-DD.md`.
236
+
237
+ After each audit/hardening run, if opted-in, append a short, dated summary to `memory/YYYY-MM-DD.md`
238
+ (what was checked, key findings, actions taken, any scheduled cron jobs, key decisions,
239
+ and all commands executed). Append-only: never overwrite existing entries.
240
+ Redact sensitive host details (usernames, hostnames, IPs, serials, service names, tokens).
241
+ If there are durable preferences or decisions (risk posture, allowed ports, update policy),
242
+ also update `MEMORY.md` (long-term memory is optional and only used in private sessions).
243
+
244
+ If the session cannot write to the workspace, ask for permission or provide exact entries
245
+ the user can paste into the memory files.
skills/himalaya/SKILL.md ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: himalaya
3
+ 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)."
4
+ homepage: https://github.com/pimalaya/himalaya
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "📧",
10
+ "requires": { "bins": ["himalaya"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "himalaya",
17
+ "bins": ["himalaya"],
18
+ "label": "Install Himalaya (brew)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # Himalaya Email CLI
26
+
27
+ Himalaya is a CLI email client that lets you manage emails from the terminal using IMAP, SMTP, Notmuch, or Sendmail backends.
28
+
29
+ ## References
30
+
31
+ - `references/configuration.md` (config file setup + IMAP/SMTP authentication)
32
+ - `references/message-composition.md` (MML syntax for composing emails)
33
+
34
+ ## Prerequisites
35
+
36
+ 1. Himalaya CLI installed (`himalaya --version` to verify)
37
+ 2. A configuration file at `~/.config/himalaya/config.toml`
38
+ 3. IMAP/SMTP credentials configured (password stored securely)
39
+
40
+ ## Configuration Setup
41
+
42
+ Run the interactive wizard to set up an account:
43
+
44
+ ```bash
45
+ himalaya account configure
46
+ ```
47
+
48
+ Or create `~/.config/himalaya/config.toml` manually:
49
+
50
+ ```toml
51
+ [accounts.personal]
52
+ email = "you@example.com"
53
+ display-name = "Your Name"
54
+ default = true
55
+
56
+ backend.type = "imap"
57
+ backend.host = "imap.example.com"
58
+ backend.port = 993
59
+ backend.encryption.type = "tls"
60
+ backend.login = "you@example.com"
61
+ backend.auth.type = "password"
62
+ backend.auth.cmd = "pass show email/imap" # or use keyring
63
+
64
+ message.send.backend.type = "smtp"
65
+ message.send.backend.host = "smtp.example.com"
66
+ message.send.backend.port = 587
67
+ message.send.backend.encryption.type = "start-tls"
68
+ message.send.backend.login = "you@example.com"
69
+ message.send.backend.auth.type = "password"
70
+ message.send.backend.auth.cmd = "pass show email/smtp"
71
+ ```
72
+
73
+ ## Common Operations
74
+
75
+ ### List Folders
76
+
77
+ ```bash
78
+ himalaya folder list
79
+ ```
80
+
81
+ ### List Emails
82
+
83
+ List emails in INBOX (default):
84
+
85
+ ```bash
86
+ himalaya envelope list
87
+ ```
88
+
89
+ List emails in a specific folder:
90
+
91
+ ```bash
92
+ himalaya envelope list --folder "Sent"
93
+ ```
94
+
95
+ List with pagination:
96
+
97
+ ```bash
98
+ himalaya envelope list --page 1 --page-size 20
99
+ ```
100
+
101
+ ### Search Emails
102
+
103
+ ```bash
104
+ himalaya envelope list from john@example.com subject meeting
105
+ ```
106
+
107
+ ### Read an Email
108
+
109
+ Read email by ID (shows plain text):
110
+
111
+ ```bash
112
+ himalaya message read 42
113
+ ```
114
+
115
+ Export raw MIME:
116
+
117
+ ```bash
118
+ himalaya message export 42 --full
119
+ ```
120
+
121
+ ### Reply to an Email
122
+
123
+ Interactive reply (opens $EDITOR):
124
+
125
+ ```bash
126
+ himalaya message reply 42
127
+ ```
128
+
129
+ Reply-all:
130
+
131
+ ```bash
132
+ himalaya message reply 42 --all
133
+ ```
134
+
135
+ ### Forward an Email
136
+
137
+ ```bash
138
+ himalaya message forward 42
139
+ ```
140
+
141
+ ### Write a New Email
142
+
143
+ Interactive compose (opens $EDITOR):
144
+
145
+ ```bash
146
+ himalaya message write
147
+ ```
148
+
149
+ Send directly using template:
150
+
151
+ ```bash
152
+ cat << 'EOF' | himalaya template send
153
+ From: you@example.com
154
+ To: recipient@example.com
155
+ Subject: Test Message
156
+
157
+ Hello from Himalaya!
158
+ EOF
159
+ ```
160
+
161
+ Or with headers flag:
162
+
163
+ ```bash
164
+ himalaya message write -H "To:recipient@example.com" -H "Subject:Test" "Message body here"
165
+ ```
166
+
167
+ ### Move/Copy Emails
168
+
169
+ Move to folder:
170
+
171
+ ```bash
172
+ himalaya message move 42 "Archive"
173
+ ```
174
+
175
+ Copy to folder:
176
+
177
+ ```bash
178
+ himalaya message copy 42 "Important"
179
+ ```
180
+
181
+ ### Delete an Email
182
+
183
+ ```bash
184
+ himalaya message delete 42
185
+ ```
186
+
187
+ ### Manage Flags
188
+
189
+ Add flag:
190
+
191
+ ```bash
192
+ himalaya flag add 42 --flag seen
193
+ ```
194
+
195
+ Remove flag:
196
+
197
+ ```bash
198
+ himalaya flag remove 42 --flag seen
199
+ ```
200
+
201
+ ## Multiple Accounts
202
+
203
+ List accounts:
204
+
205
+ ```bash
206
+ himalaya account list
207
+ ```
208
+
209
+ Use a specific account:
210
+
211
+ ```bash
212
+ himalaya --account work envelope list
213
+ ```
214
+
215
+ ## Attachments
216
+
217
+ Save attachments from a message:
218
+
219
+ ```bash
220
+ himalaya attachment download 42
221
+ ```
222
+
223
+ Save to specific directory:
224
+
225
+ ```bash
226
+ himalaya attachment download 42 --dir ~/Downloads
227
+ ```
228
+
229
+ ## Output Formats
230
+
231
+ Most commands support `--output` for structured output:
232
+
233
+ ```bash
234
+ himalaya envelope list --output json
235
+ himalaya envelope list --output plain
236
+ ```
237
+
238
+ ## Debugging
239
+
240
+ Enable debug logging:
241
+
242
+ ```bash
243
+ RUST_LOG=debug himalaya envelope list
244
+ ```
245
+
246
+ Full trace with backtrace:
247
+
248
+ ```bash
249
+ RUST_LOG=trace RUST_BACKTRACE=1 himalaya envelope list
250
+ ```
251
+
252
+ ## Tips
253
+
254
+ - Use `himalaya --help` or `himalaya <command> --help` for detailed usage.
255
+ - Message IDs are relative to the current folder; re-list after folder changes.
256
+ - For composing rich emails with attachments, use MML syntax (see `references/message-composition.md`).
257
+ - Store passwords securely using `pass`, system keyring, or a command that outputs the password.
skills/imsg/SKILL.md ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: imsg
3
+ description: iMessage/SMS CLI for listing chats, history, and sending messages via Messages.app.
4
+ homepage: https://imsg.to
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "📨",
10
+ "os": ["darwin"],
11
+ "requires": { "bins": ["imsg"] },
12
+ "install":
13
+ [
14
+ {
15
+ "id": "brew",
16
+ "kind": "brew",
17
+ "formula": "steipete/tap/imsg",
18
+ "bins": ["imsg"],
19
+ "label": "Install imsg (brew)",
20
+ },
21
+ ],
22
+ },
23
+ }
24
+ ---
25
+
26
+ # imsg
27
+
28
+ Use `imsg` to read and send iMessage/SMS via macOS Messages.app.
29
+
30
+ ## When to Use
31
+
32
+ ✅ **USE this skill when:**
33
+
34
+ - User explicitly asks to send iMessage or SMS
35
+ - Reading iMessage conversation history
36
+ - Checking recent Messages.app chats
37
+ - Sending to phone numbers or Apple IDs
38
+
39
+ ## When NOT to Use
40
+
41
+ ❌ **DON'T use this skill when:**
42
+
43
+ - Telegram messages → use `message` tool with `channel:telegram`
44
+ - Signal messages → use Signal channel if configured
45
+ - WhatsApp messages → use WhatsApp channel if configured
46
+ - Discord messages → use `message` tool with `channel:discord`
47
+ - Slack messages → use `slack` skill
48
+ - Group chat management (adding/removing members) → not supported
49
+ - Bulk/mass messaging → always confirm with user first
50
+ - Replying in current conversation → just reply normally (OpenClaw routes automatically)
51
+
52
+ ## Requirements
53
+
54
+ - macOS with Messages.app signed in
55
+ - Full Disk Access for terminal
56
+ - Automation permission for Messages.app (for sending)
57
+
58
+ ## Common Commands
59
+
60
+ ### List Chats
61
+
62
+ ```bash
63
+ imsg chats --limit 10 --json
64
+ ```
65
+
66
+ ### View History
67
+
68
+ ```bash
69
+ # By chat ID
70
+ imsg history --chat-id 1 --limit 20 --json
71
+
72
+ # With attachments info
73
+ imsg history --chat-id 1 --limit 20 --attachments --json
74
+ ```
75
+
76
+ ### Watch for New Messages
77
+
78
+ ```bash
79
+ imsg watch --chat-id 1 --attachments
80
+ ```
81
+
82
+ ### Send Messages
83
+
84
+ ```bash
85
+ # Text only
86
+ imsg send --to "+14155551212" --text "Hello!"
87
+
88
+ # With attachment
89
+ imsg send --to "+14155551212" --text "Check this out" --file /path/to/image.jpg
90
+
91
+ # Specify service
92
+ imsg send --to "+14155551212" --text "Hi" --service imessage
93
+ imsg send --to "+14155551212" --text "Hi" --service sms
94
+ ```
95
+
96
+ ## Service Options
97
+
98
+ - `--service imessage` — Force iMessage (requires recipient has iMessage)
99
+ - `--service sms` — Force SMS (green bubble)
100
+ - `--service auto` — Let Messages.app decide (default)
101
+
102
+ ## Safety Rules
103
+
104
+ 1. **Always confirm recipient and message content** before sending
105
+ 2. **Never send to unknown numbers** without explicit user approval
106
+ 3. **Be careful with attachments** — confirm file path exists
107
+ 4. **Rate limit yourself** — don't spam
108
+
109
+ ## Example Workflow
110
+
111
+ User: "Text mom that I'll be late"
112
+
113
+ ```bash
114
+ # 1. Find mom's chat
115
+ imsg chats --limit 20 --json | jq '.[] | select(.displayName | contains("Mom"))'
116
+
117
+ # 2. Confirm with user
118
+ # "Found Mom at +1555123456. Send 'I'll be late' via iMessage?"
119
+
120
+ # 3. Send after confirmation
121
+ imsg send --to "+1555123456" --text "I'll be late"
122
+ ```
skills/mcporter/SKILL.md ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: mcporter
3
+ 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.
4
+ homepage: http://mcporter.dev
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "📦",
10
+ "requires": { "bins": ["mcporter"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "node",
15
+ "kind": "node",
16
+ "package": "mcporter",
17
+ "bins": ["mcporter"],
18
+ "label": "Install mcporter (node)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # mcporter
26
+
27
+ Use `mcporter` to work with MCP servers directly.
28
+
29
+ Quick start
30
+
31
+ - `mcporter list`
32
+ - `mcporter list <server> --schema`
33
+ - `mcporter call <server.tool> key=value`
34
+
35
+ Call tools
36
+
37
+ - Selector: `mcporter call linear.list_issues team=ENG limit:5`
38
+ - Function syntax: `mcporter call "linear.create_issue(title: \"Bug\")"`
39
+ - Full URL: `mcporter call https://api.example.com/mcp.fetch url:https://example.com`
40
+ - Stdio: `mcporter call --stdio "bun run ./server.ts" scrape url=https://example.com`
41
+ - JSON payload: `mcporter call <server.tool> --args '{"limit":5}'`
42
+
43
+ Auth + config
44
+
45
+ - OAuth: `mcporter auth <server | url> [--reset]`
46
+ - Config: `mcporter config list|get|add|remove|import|login|logout`
47
+
48
+ Daemon
49
+
50
+ - `mcporter daemon start|status|stop|restart`
51
+
52
+ Codegen
53
+
54
+ - CLI: `mcporter generate-cli --server <name>` or `--command <url>`
55
+ - Inspect: `mcporter inspect-cli <path> [--json]`
56
+ - TS: `mcporter emit-ts <server> --mode client|types`
57
+
58
+ Notes
59
+
60
+ - Config default: `./config/mcporter.json` (override with `--config`).
61
+ - Prefer `--output json` for machine-readable results.
skills/model-usage/SKILL.md ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: model-usage
3
+ 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.
4
+ metadata:
5
+ {
6
+ "openclaw":
7
+ {
8
+ "emoji": "📊",
9
+ "os": ["darwin"],
10
+ "requires": { "bins": ["codexbar"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew-cask",
15
+ "kind": "brew",
16
+ "formula": "steipete/tap/codexbar",
17
+ "bins": ["codexbar"],
18
+ "label": "Install CodexBar (brew cask)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # Model usage
26
+
27
+ ## Overview
28
+
29
+ Get 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.
30
+
31
+ TODO: add Linux CLI support guidance once CodexBar CLI install path is documented for Linux.
32
+
33
+ ## Quick start
34
+
35
+ 1. Fetch cost JSON via CodexBar CLI or pass a JSON file.
36
+ 2. Use the bundled script to summarize by model.
37
+
38
+ ```bash
39
+ python {baseDir}/scripts/model_usage.py --provider codex --mode current
40
+ python {baseDir}/scripts/model_usage.py --provider codex --mode all
41
+ python {baseDir}/scripts/model_usage.py --provider claude --mode all --format json --pretty
42
+ ```
43
+
44
+ ## Current model logic
45
+
46
+ - Uses the most recent daily row with `modelBreakdowns`.
47
+ - Picks the model with the highest cost in that row.
48
+ - Falls back to the last entry in `modelsUsed` when breakdowns are missing.
49
+ - Override with `--model <name>` when you need a specific model.
50
+
51
+ ## Inputs
52
+
53
+ - Default: runs `codexbar cost --format json --provider <codex|claude>`.
54
+ - File or stdin:
55
+
56
+ ```bash
57
+ codexbar cost --provider codex --format json > /tmp/cost.json
58
+ python {baseDir}/scripts/model_usage.py --input /tmp/cost.json --mode all
59
+ cat /tmp/cost.json | python {baseDir}/scripts/model_usage.py --input - --mode current
60
+ ```
61
+
62
+ ## Output
63
+
64
+ - Text (default) or JSON (`--format json --pretty`).
65
+ - Values are cost-only per model; tokens are not split by model in CodexBar output.
66
+
67
+ ## References
68
+
69
+ - Read `references/codexbar-cli.md` for CLI flags and cost JSON fields.
skills/nano-pdf/SKILL.md ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: nano-pdf
3
+ description: Edit PDFs with natural-language instructions using the nano-pdf CLI.
4
+ homepage: https://pypi.org/project/nano-pdf/
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "📄",
10
+ "requires": { "bins": ["nano-pdf"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "uv",
15
+ "kind": "uv",
16
+ "package": "nano-pdf",
17
+ "bins": ["nano-pdf"],
18
+ "label": "Install nano-pdf (uv)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # nano-pdf
26
+
27
+ Use `nano-pdf` to apply edits to a specific page in a PDF using a natural-language instruction.
28
+
29
+ ## Quick start
30
+
31
+ ```bash
32
+ nano-pdf edit deck.pdf 1 "Change the title to 'Q3 Results' and fix the typo in the subtitle"
33
+ ```
34
+
35
+ Notes:
36
+
37
+ - 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.
38
+ - Always sanity-check the output PDF before sending it out.
skills/node-connect/SKILL.md ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: node-connect
3
+ 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.
4
+ ---
5
+
6
+ # Node Connect
7
+
8
+ Goal: find the one real route from node -> gateway, verify OpenClaw is advertising that route, then fix pairing/auth.
9
+
10
+ ## Topology first
11
+
12
+ Decide which case you are in before proposing fixes:
13
+
14
+ - same machine / emulator / USB tunnel
15
+ - same LAN / local Wi-Fi
16
+ - same Tailscale tailnet
17
+ - public URL / reverse proxy
18
+
19
+ Do not mix them.
20
+
21
+ - Local Wi-Fi problem: do not switch to Tailscale unless remote access is actually needed.
22
+ - VPS / remote gateway problem: do not keep debugging `localhost` or LAN IPs.
23
+
24
+ ## If ambiguous, ask first
25
+
26
+ If the setup is unclear or the failure report is vague, ask short clarifying questions before diagnosing.
27
+
28
+ Ask for:
29
+
30
+ - which route they intend: same machine, same LAN, Tailscale tailnet, or public URL
31
+ - whether they used QR/setup code or manual host/port
32
+ - the exact app text/status/error, quoted exactly if possible
33
+ - whether `openclaw devices list` shows a pending pairing request
34
+
35
+ Do not guess from `can't connect`.
36
+
37
+ ## Canonical checks
38
+
39
+ Prefer `openclaw qr --json`. It uses the same setup-code payload Android scans.
40
+
41
+ ```bash
42
+ openclaw config get gateway.mode
43
+ openclaw config get gateway.bind
44
+ openclaw config get gateway.tailscale.mode
45
+ openclaw config get gateway.remote.url
46
+ openclaw config get gateway.auth.mode
47
+ openclaw config get gateway.auth.allowTailscale
48
+ openclaw config get plugins.entries.device-pair.config.publicUrl
49
+ openclaw qr --json
50
+ openclaw devices list
51
+ openclaw nodes status
52
+ ```
53
+
54
+ If this OpenClaw instance is pointed at a remote gateway, also run:
55
+
56
+ ```bash
57
+ openclaw qr --remote --json
58
+ ```
59
+
60
+ If Tailscale is part of the story:
61
+
62
+ ```bash
63
+ tailscale status --json
64
+ ```
65
+
66
+ ## Read the result, not guesses
67
+
68
+ `openclaw qr --json` success means:
69
+
70
+ - `gatewayUrl`: this is the actual endpoint the app should use.
71
+ - `urlSource`: this tells you which config path won.
72
+
73
+ Common good sources:
74
+
75
+ - `gateway.bind=lan`: same Wi-Fi / LAN only
76
+ - `gateway.bind=tailnet`: direct tailnet access
77
+ - `gateway.tailscale.mode=serve` or `gateway.tailscale.mode=funnel`: Tailscale route
78
+ - `plugins.entries.device-pair.config.publicUrl`: explicit public/reverse-proxy route
79
+ - `gateway.remote.url`: remote gateway route
80
+
81
+ ## Root-cause map
82
+
83
+ If `openclaw qr --json` says `Gateway is only bound to loopback`:
84
+
85
+ - remote node cannot connect yet
86
+ - fix the route, then generate a fresh setup code
87
+ - `gateway.bind=auto` is not enough if the effective QR route is still loopback
88
+ - same LAN: use `gateway.bind=lan`
89
+ - same tailnet: prefer `gateway.tailscale.mode=serve` or use `gateway.bind=tailnet`
90
+ - public internet: set a real `plugins.entries.device-pair.config.publicUrl` or `gateway.remote.url`
91
+
92
+ If `gateway.bind=tailnet set, but no tailnet IP was found`:
93
+
94
+ - gateway host is not actually on Tailscale
95
+
96
+ If `qr --remote requires gateway.remote.url`:
97
+
98
+ - remote-mode config is incomplete
99
+
100
+ If the app says `pairing required`:
101
+
102
+ - network route and auth worked
103
+ - approve the pending device
104
+
105
+ ```bash
106
+ openclaw devices list
107
+ openclaw devices approve --latest
108
+ ```
109
+
110
+ If the app says `bootstrap token invalid or expired`:
111
+
112
+ - old setup code
113
+ - generate a fresh one and rescan
114
+ - do this after any URL/auth fix too
115
+
116
+ If the app says `unauthorized`:
117
+
118
+ - wrong token/password, or wrong Tailscale expectation
119
+ - for Tailscale Serve, `gateway.auth.allowTailscale` must match the intended flow
120
+ - otherwise use explicit token/password
121
+
122
+ ## Fast heuristics
123
+
124
+ - Same Wi-Fi setup + gateway advertises `127.0.0.1`, `localhost`, or loopback-only config: wrong.
125
+ - Remote setup + setup/manual uses private LAN IP: wrong.
126
+ - Tailnet setup + gateway advertises LAN IP instead of MagicDNS / tailnet route: wrong.
127
+ - Public URL set but QR still advertises something else: inspect `urlSource`; config is not what you think.
128
+ - `openclaw devices list` shows pending requests: stop changing network config and approve first.
129
+
130
+ ## Fix style
131
+
132
+ Reply with one concrete diagnosis and one route.
133
+
134
+ If there is not enough signal yet, ask for setup + exact app text instead of guessing.
135
+
136
+ Good:
137
+
138
+ - `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.`
139
+
140
+ Bad:
141
+
142
+ - `Maybe LAN, maybe Tailscale, maybe port forwarding, maybe public URL.`
skills/notion/SKILL.md ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: notion
3
+ description: Notion API for creating and managing pages, databases, and blocks.
4
+ homepage: https://developers.notion.com
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ { "emoji": "📝", "requires": { "env": ["NOTION_API_KEY"] }, "primaryEnv": "NOTION_API_KEY" },
9
+ }
10
+ ---
11
+
12
+ # notion
13
+
14
+ Use the Notion API to create/read/update pages, data sources (databases), and blocks.
15
+
16
+ ## Setup
17
+
18
+ 1. Create an integration at https://notion.so/my-integrations
19
+ 2. Copy the API key (starts with `ntn_` or `secret_`)
20
+ 3. Store it:
21
+
22
+ ```bash
23
+ mkdir -p ~/.config/notion
24
+ echo "ntn_your_key_here" > ~/.config/notion/api_key
25
+ ```
26
+
27
+ 4. Share target pages/databases with your integration (click "..." → "Connect to" → your integration name)
28
+
29
+ ## API Basics
30
+
31
+ All requests need:
32
+
33
+ ```bash
34
+ NOTION_KEY=$(cat ~/.config/notion/api_key)
35
+ curl -X GET "https://api.notion.com/v1/..." \
36
+ -H "Authorization: Bearer $NOTION_KEY" \
37
+ -H "Notion-Version: 2025-09-03" \
38
+ -H "Content-Type: application/json"
39
+ ```
40
+
41
+ > **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.
42
+
43
+ ## Common Operations
44
+
45
+ **Search for pages and data sources:**
46
+
47
+ ```bash
48
+ curl -X POST "https://api.notion.com/v1/search" \
49
+ -H "Authorization: Bearer $NOTION_KEY" \
50
+ -H "Notion-Version: 2025-09-03" \
51
+ -H "Content-Type: application/json" \
52
+ -d '{"query": "page title"}'
53
+ ```
54
+
55
+ **Get page:**
56
+
57
+ ```bash
58
+ curl "https://api.notion.com/v1/pages/{page_id}" \
59
+ -H "Authorization: Bearer $NOTION_KEY" \
60
+ -H "Notion-Version: 2025-09-03"
61
+ ```
62
+
63
+ **Get page content (blocks):**
64
+
65
+ ```bash
66
+ curl "https://api.notion.com/v1/blocks/{page_id}/children" \
67
+ -H "Authorization: Bearer $NOTION_KEY" \
68
+ -H "Notion-Version: 2025-09-03"
69
+ ```
70
+
71
+ **Create page in a data source:**
72
+
73
+ ```bash
74
+ curl -X POST "https://api.notion.com/v1/pages" \
75
+ -H "Authorization: Bearer $NOTION_KEY" \
76
+ -H "Notion-Version: 2025-09-03" \
77
+ -H "Content-Type: application/json" \
78
+ -d '{
79
+ "parent": {"database_id": "xxx"},
80
+ "properties": {
81
+ "Name": {"title": [{"text": {"content": "New Item"}}]},
82
+ "Status": {"select": {"name": "Todo"}}
83
+ }
84
+ }'
85
+ ```
86
+
87
+ **Query a data source (database):**
88
+
89
+ ```bash
90
+ curl -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query" \
91
+ -H "Authorization: Bearer $NOTION_KEY" \
92
+ -H "Notion-Version: 2025-09-03" \
93
+ -H "Content-Type: application/json" \
94
+ -d '{
95
+ "filter": {"property": "Status", "select": {"equals": "Active"}},
96
+ "sorts": [{"property": "Date", "direction": "descending"}]
97
+ }'
98
+ ```
99
+
100
+ **Create a data source (database):**
101
+
102
+ ```bash
103
+ curl -X POST "https://api.notion.com/v1/data_sources" \
104
+ -H "Authorization: Bearer $NOTION_KEY" \
105
+ -H "Notion-Version: 2025-09-03" \
106
+ -H "Content-Type: application/json" \
107
+ -d '{
108
+ "parent": {"page_id": "xxx"},
109
+ "title": [{"text": {"content": "My Database"}}],
110
+ "properties": {
111
+ "Name": {"title": {}},
112
+ "Status": {"select": {"options": [{"name": "Todo"}, {"name": "Done"}]}},
113
+ "Date": {"date": {}}
114
+ }
115
+ }'
116
+ ```
117
+
118
+ **Update page properties:**
119
+
120
+ ```bash
121
+ curl -X PATCH "https://api.notion.com/v1/pages/{page_id}" \
122
+ -H "Authorization: Bearer $NOTION_KEY" \
123
+ -H "Notion-Version: 2025-09-03" \
124
+ -H "Content-Type: application/json" \
125
+ -d '{"properties": {"Status": {"select": {"name": "Done"}}}}'
126
+ ```
127
+
128
+ **Add blocks to page:**
129
+
130
+ ```bash
131
+ curl -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \
132
+ -H "Authorization: Bearer $NOTION_KEY" \
133
+ -H "Notion-Version: 2025-09-03" \
134
+ -H "Content-Type: application/json" \
135
+ -d '{
136
+ "children": [
137
+ {"object": "block", "type": "paragraph", "paragraph": {"rich_text": [{"text": {"content": "Hello"}}]}}
138
+ ]
139
+ }'
140
+ ```
141
+
142
+ ## Property Types
143
+
144
+ Common property formats for database items:
145
+
146
+ - **Title:** `{"title": [{"text": {"content": "..."}}]}`
147
+ - **Rich text:** `{"rich_text": [{"text": {"content": "..."}}]}`
148
+ - **Select:** `{"select": {"name": "Option"}}`
149
+ - **Multi-select:** `{"multi_select": [{"name": "A"}, {"name": "B"}]}`
150
+ - **Date:** `{"date": {"start": "2024-01-15", "end": "2024-01-16"}}`
151
+ - **Checkbox:** `{"checkbox": true}`
152
+ - **Number:** `{"number": 42}`
153
+ - **URL:** `{"url": "https://..."}`
154
+ - **Email:** `{"email": "a@b.com"}`
155
+ - **Relation:** `{"relation": [{"id": "page_id"}]}`
156
+
157
+ ## Key Differences in 2025-09-03
158
+
159
+ - **Databases → Data Sources:** Use `/data_sources/` endpoints for queries and retrieval
160
+ - **Two IDs:** Each database now has both a `database_id` and a `data_source_id`
161
+ - Use `database_id` when creating pages (`parent: {"database_id": "..."}`)
162
+ - Use `data_source_id` when querying (`POST /v1/data_sources/{id}/query`)
163
+ - **Search results:** Databases return as `"object": "data_source"` with their `data_source_id`
164
+ - **Parent in responses:** Pages show `parent.data_source_id` alongside `parent.database_id`
165
+ - **Finding the data_source_id:** Search for the database, or call `GET /v1/data_sources/{data_source_id}`
166
+
167
+ ## Notes
168
+
169
+ - Page/database IDs are UUIDs (with or without dashes)
170
+ - The API cannot set database view filters — that's UI-only
171
+ - Rate limit: ~3 requests/second average, with `429 rate_limited` responses using `Retry-After`
172
+ - Append block children: up to 100 children per request, up to two levels of nesting in a single append request
173
+ - Payload size limits: up to 1000 block elements and 500KB overall
174
+ - Use `is_inline: true` when creating data sources to embed them in pages
skills/obsidian/SKILL.md ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: obsidian
3
+ description: Work with Obsidian vaults (plain Markdown notes) and automate via obsidian-cli.
4
+ homepage: https://help.obsidian.md
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "💎",
10
+ "requires": { "bins": ["obsidian-cli"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "yakitrak/yakitrak/obsidian-cli",
17
+ "bins": ["obsidian-cli"],
18
+ "label": "Install obsidian-cli (brew)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # Obsidian
26
+
27
+ Obsidian vault = a normal folder on disk.
28
+
29
+ Vault structure (typical)
30
+
31
+ - Notes: `*.md` (plain text Markdown; edit with any editor)
32
+ - Config: `.obsidian/` (workspace + plugin settings; usually don’t touch from scripts)
33
+ - Canvases: `*.canvas` (JSON)
34
+ - Attachments: whatever folder you chose in Obsidian settings (images/PDFs/etc.)
35
+
36
+ ## Find the active vault(s)
37
+
38
+ Obsidian desktop tracks vaults here (source of truth):
39
+
40
+ - `~/Library/Application Support/obsidian/obsidian.json`
41
+
42
+ `obsidian-cli` resolves vaults from that file; vault name is typically the **folder name** (path suffix).
43
+
44
+ Fast “what vault is active / where are the notes?”
45
+
46
+ - If you’ve already set a default: `obsidian-cli print-default --path-only`
47
+ - Otherwise, read `~/Library/Application Support/obsidian/obsidian.json` and use the vault entry with `"open": true`.
48
+
49
+ Notes
50
+
51
+ - Multiple vaults common (iCloud vs `~/Documents`, work/personal, etc.). Don’t guess; read config.
52
+ - Avoid writing hardcoded vault paths into scripts; prefer reading the config or using `print-default`.
53
+
54
+ ## obsidian-cli quick start
55
+
56
+ Pick a default vault (once):
57
+
58
+ - `obsidian-cli set-default "<vault-folder-name>"`
59
+ - `obsidian-cli print-default` / `obsidian-cli print-default --path-only`
60
+
61
+ Search
62
+
63
+ - `obsidian-cli search "query"` (note names)
64
+ - `obsidian-cli search-content "query"` (inside notes; shows snippets + lines)
65
+
66
+ Create
67
+
68
+ - `obsidian-cli create "Folder/New note" --content "..." --open`
69
+ - Requires Obsidian URI handler (`obsidian://…`) working (Obsidian installed).
70
+ - Avoid creating notes under “hidden” dot-folders (e.g. `.something/...`) via URI; Obsidian may refuse.
71
+
72
+ Move/rename (safe refactor)
73
+
74
+ - `obsidian-cli move "old/path/note" "new/path/note"`
75
+ - Updates `[[wikilinks]]` and common Markdown links across the vault (this is the main win vs `mv`).
76
+
77
+ Delete
78
+
79
+ - `obsidian-cli delete "path/note"`
80
+
81
+ Prefer direct edits when appropriate: open the `.md` file and change it; Obsidian will pick it up.
skills/openai-whisper-api/SKILL.md ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: openai-whisper-api
3
+ description: Transcribe audio via OpenAI Audio Transcriptions API (Whisper).
4
+ homepage: https://platform.openai.com/docs/guides/speech-to-text
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🌐",
10
+ "requires": { "bins": ["curl"], "env": ["OPENAI_API_KEY"] },
11
+ "primaryEnv": "OPENAI_API_KEY",
12
+ "install":
13
+ [
14
+ {
15
+ "id": "brew",
16
+ "kind": "brew",
17
+ "formula": "curl",
18
+ "bins": ["curl"],
19
+ "label": "Install curl (brew)",
20
+ },
21
+ ],
22
+ },
23
+ }
24
+ ---
25
+
26
+ # OpenAI Whisper API (curl)
27
+
28
+ Transcribe an audio file via OpenAI’s `/v1/audio/transcriptions` endpoint. Set `OPENAI_BASE_URL` to use an OpenAI-compatible proxy or local gateway.
29
+
30
+ ## Quick start
31
+
32
+ ```bash
33
+ {baseDir}/scripts/transcribe.sh /path/to/audio.m4a
34
+ ```
35
+
36
+ Defaults:
37
+
38
+ - Model: `whisper-1`
39
+ - Output: `<input>.txt`
40
+
41
+ ## Useful flags
42
+
43
+ ```bash
44
+ {baseDir}/scripts/transcribe.sh /path/to/audio.ogg --model whisper-1 --out /tmp/transcript.txt
45
+ {baseDir}/scripts/transcribe.sh /path/to/audio.m4a --language en
46
+ {baseDir}/scripts/transcribe.sh /path/to/audio.m4a --prompt "Speaker names: Peter, Daniel"
47
+ {baseDir}/scripts/transcribe.sh /path/to/audio.m4a --json --out /tmp/transcript.json
48
+ ```
49
+
50
+ ## API key
51
+
52
+ Set `OPENAI_API_KEY`, or configure it in the active OpenClaw config file (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`). Optionally set `OPENAI_BASE_URL` (for example `http://127.0.0.1:51805/v1`) to use an OpenAI-compatible proxy or local gateway:
53
+
54
+ ```json5
55
+ {
56
+ skills: {
57
+ "openai-whisper-api": {
58
+ apiKey: "OPENAI_KEY_HERE",
59
+ },
60
+ },
61
+ }
62
+ ```
skills/openai-whisper/SKILL.md ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: openai-whisper
3
+ description: Local speech-to-text with the Whisper CLI (no API key).
4
+ homepage: https://openai.com/research/whisper
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🎤",
10
+ "requires": { "bins": ["whisper"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "openai-whisper",
17
+ "bins": ["whisper"],
18
+ "label": "Install OpenAI Whisper (brew)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # Whisper (CLI)
26
+
27
+ Use `whisper` to transcribe audio locally.
28
+
29
+ Quick start
30
+
31
+ - `whisper /path/audio.mp3 --model medium --output_format txt --output_dir .`
32
+ - `whisper /path/audio.m4a --task translate --output_format srt`
33
+
34
+ Notes
35
+
36
+ - Models download to `~/.cache/whisper` on first run.
37
+ - `--model` defaults to `turbo` on this install.
38
+ - Use smaller models for speed, larger for accuracy.
skills/openhue/SKILL.md ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: openhue
3
+ description: Control Philips Hue lights and scenes via the OpenHue CLI.
4
+ homepage: https://www.openhue.io/cli
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "💡",
10
+ "requires": { "bins": ["openhue"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "openhue/cli/openhue-cli",
17
+ "bins": ["openhue"],
18
+ "label": "Install OpenHue CLI (brew)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # OpenHue CLI
26
+
27
+ Use `openhue` to control Philips Hue lights and scenes via a Hue Bridge.
28
+
29
+ ## When to Use
30
+
31
+ ✅ **USE this skill when:**
32
+
33
+ - "Turn on/off the lights"
34
+ - "Dim the living room lights"
35
+ - "Set a scene" or "movie mode"
36
+ - Controlling specific Hue rooms or zones
37
+ - Adjusting brightness, color, or color temperature
38
+
39
+ ## When NOT to Use
40
+
41
+ ❌ **DON'T use this skill when:**
42
+
43
+ - Non-Hue smart devices (other brands) → not supported
44
+ - HomeKit scenes or Shortcuts → use Apple's ecosystem
45
+ - TV or entertainment system control
46
+ - Thermostat or HVAC
47
+ - Smart plugs (unless Hue smart plugs)
48
+
49
+ ## Common Commands
50
+
51
+ ### List Resources
52
+
53
+ ```bash
54
+ openhue get light # List all lights
55
+ openhue get room # List all rooms
56
+ openhue get scene # List all scenes
57
+ ```
58
+
59
+ ### Control Lights
60
+
61
+ ```bash
62
+ # Turn on/off
63
+ openhue set light "Bedroom Lamp" --on
64
+ openhue set light "Bedroom Lamp" --off
65
+
66
+ # Brightness (0-100)
67
+ openhue set light "Bedroom Lamp" --on --brightness 50
68
+
69
+ # Color temperature (warm to cool: 153-500 mirek)
70
+ openhue set light "Bedroom Lamp" --on --temperature 300
71
+
72
+ # Color (by name or hex)
73
+ openhue set light "Bedroom Lamp" --on --color red
74
+ openhue set light "Bedroom Lamp" --on --rgb "#FF5500"
75
+ ```
76
+
77
+ ### Control Rooms
78
+
79
+ ```bash
80
+ # Turn off entire room
81
+ openhue set room "Bedroom" --off
82
+
83
+ # Set room brightness
84
+ openhue set room "Bedroom" --on --brightness 30
85
+ ```
86
+
87
+ ### Scenes
88
+
89
+ ```bash
90
+ # Activate scene
91
+ openhue set scene "Relax" --room "Bedroom"
92
+ openhue set scene "Concentrate" --room "Office"
93
+ ```
94
+
95
+ ## Quick Presets
96
+
97
+ ```bash
98
+ # Bedtime (dim warm)
99
+ openhue set room "Bedroom" --on --brightness 20 --temperature 450
100
+
101
+ # Work mode (bright cool)
102
+ openhue set room "Office" --on --brightness 100 --temperature 250
103
+
104
+ # Movie mode (dim)
105
+ openhue set room "Living Room" --on --brightness 10
106
+ ```
107
+
108
+ ## Notes
109
+
110
+ - Bridge must be on local network
111
+ - First run requires button press on Hue bridge to pair
112
+ - Colors only work on color-capable bulbs (not white-only)
skills/oracle/SKILL.md ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: oracle
3
+ description: Best practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns).
4
+ homepage: https://askoracle.dev
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🧿",
10
+ "requires": { "bins": ["oracle"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "node",
15
+ "kind": "node",
16
+ "package": "@steipete/oracle",
17
+ "bins": ["oracle"],
18
+ "label": "Install oracle (node)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # oracle — best use
26
+
27
+ Oracle 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.
28
+
29
+ ## Main use case (browser, GPT‑5.2 Pro)
30
+
31
+ Default 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.
32
+
33
+ Recommended defaults:
34
+
35
+ - Engine: browser (`--engine browser`)
36
+ - Model: GPT‑5.2 Pro (`--model gpt-5.2-pro` or `--model "5.2 Pro"`)
37
+
38
+ ## Golden path
39
+
40
+ 1. Pick a tight file set (fewest files that still contain the truth).
41
+ 2. Preview payload + token spend (`--dry-run` + `--files-report`).
42
+ 3. Use browser mode for the usual GPT‑5.2 Pro workflow; use API only when you explicitly want it.
43
+ 4. If the run detaches/timeouts: reattach to the stored session (don’t re-run).
44
+
45
+ ## Commands (preferred)
46
+
47
+ - Help:
48
+ - `oracle --help`
49
+ - If the binary isn’t installed: `npx -y @steipete/oracle --help` (avoid `pnpx` here; sqlite bindings).
50
+
51
+ - Preview (no tokens):
52
+ - `oracle --dry-run summary -p "<task>" --file "src/**" --file "!**/*.test.*"`
53
+ - `oracle --dry-run full -p "<task>" --file "src/**"`
54
+
55
+ - Token sanity:
56
+ - `oracle --dry-run summary --files-report -p "<task>" --file "src/**"`
57
+
58
+ - Browser run (main path; long-running is normal):
59
+ - `oracle --engine browser --model gpt-5.2-pro -p "<task>" --file "src/**"`
60
+
61
+ - Manual paste fallback:
62
+ - `oracle --render --copy -p "<task>" --file "src/**"`
63
+ - Note: `--copy` is a hidden alias for `--copy-markdown`.
64
+
65
+ ## Attaching files (`--file`)
66
+
67
+ `--file` accepts files, directories, and globs. You can pass it multiple times; entries can be comma-separated.
68
+
69
+ - Include:
70
+ - `--file "src/**"`
71
+ - `--file src/index.ts`
72
+ - `--file docs --file README.md`
73
+
74
+ - Exclude:
75
+ - `--file "src/**" --file "!src/**/*.test.ts" --file "!**/*.snap"`
76
+
77
+ - Defaults (implementation behavior):
78
+ - Default-ignored dirs: `node_modules`, `dist`, `coverage`, `.git`, `.turbo`, `.next`, `build`, `tmp` (skipped unless explicitly passed as literal dirs/files).
79
+ - Honors `.gitignore` when expanding globs.
80
+ - Does not follow symlinks.
81
+ - Dotfiles filtered unless opted in via pattern (e.g. `--file ".github/**"`).
82
+ - Files > 1 MB rejected.
83
+
84
+ ## Engines (API vs browser)
85
+
86
+ - Auto-pick: `api` when `OPENAI_API_KEY` is set; otherwise `browser`.
87
+ - Browser supports GPT + Gemini only; use `--engine api` for Claude/Grok/Codex or multi-model runs.
88
+ - Browser attachments:
89
+ - `--browser-attachments auto|never|always` (auto pastes inline up to ~60k chars then uploads).
90
+ - Remote browser host:
91
+ - Host: `oracle serve --host 0.0.0.0 --port 9473 --token <secret>`
92
+ - Client: `oracle --engine browser --remote-host <host:port> --remote-token <secret> -p "<task>" --file "src/**"`
93
+
94
+ ## Sessions + slugs
95
+
96
+ - Stored under `~/.oracle/sessions` (override with `ORACLE_HOME_DIR`).
97
+ - 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.
98
+ - List: `oracle status --hours 72`
99
+ - Attach: `oracle session <id> --render`
100
+ - Use `--slug "<3-5 words>"` to keep session IDs readable.
101
+ - Duplicate prompt guard exists; use `--force` only when you truly want a fresh run.
102
+
103
+ ## Prompt template (high signal)
104
+
105
+ Oracle starts with **zero** project knowledge. Assume the model cannot infer your stack, build tooling, conventions, or “obvious” paths. Include:
106
+
107
+ - Project briefing (stack + build/test commands + platform constraints).
108
+ - “Where things live” (key directories, entrypoints, config files, boundaries).
109
+ - Exact question + what you tried + the error text (verbatim).
110
+ - Constraints (“don’t change X”, “must keep public API”, etc).
111
+ - Desired output (“return patch plan + tests”, “give 3 options with tradeoffs”).
112
+
113
+ ## Safety
114
+
115
+ - Don’t attach secrets by default (`.env`, key files, auth tokens). Redact aggressively; share only what’s required.
116
+
117
+ ## “Exhaustive prompt” restoration pattern
118
+
119
+ For long investigations, write a standalone prompt + file set so you can rerun days later:
120
+
121
+ - 6–30 sentence project briefing + the goal.
122
+ - Repro steps + exact errors + what you tried.
123
+ - Attach all context files needed (entrypoints, configs, key modules, docs).
124
+
125
+ Oracle 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).
skills/ordercli/SKILL.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: ordercli
3
+ description: Foodora-only CLI for checking past orders and active order status (Deliveroo WIP).
4
+ homepage: https://ordercli.sh
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🛵",
10
+ "requires": { "bins": ["ordercli"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "steipete/tap/ordercli",
17
+ "bins": ["ordercli"],
18
+ "label": "Install ordercli (brew)",
19
+ },
20
+ {
21
+ "id": "go",
22
+ "kind": "go",
23
+ "module": "github.com/steipete/ordercli/cmd/ordercli@latest",
24
+ "bins": ["ordercli"],
25
+ "label": "Install ordercli (go)",
26
+ },
27
+ ],
28
+ },
29
+ }
30
+ ---
31
+
32
+ # ordercli
33
+
34
+ Use `ordercli` to check past orders and track active order status (Foodora only right now).
35
+
36
+ Quick start (Foodora)
37
+
38
+ - `ordercli foodora countries`
39
+ - `ordercli foodora config set --country AT`
40
+ - `ordercli foodora login --email you@example.com --password-stdin`
41
+ - `ordercli foodora orders`
42
+ - `ordercli foodora history --limit 20`
43
+ - `ordercli foodora history show <orderCode>`
44
+
45
+ Orders
46
+
47
+ - Active list (arrival/status): `ordercli foodora orders`
48
+ - Watch: `ordercli foodora orders --watch`
49
+ - Active order detail: `ordercli foodora order <orderCode>`
50
+ - History detail JSON: `ordercli foodora history show <orderCode> --json`
51
+
52
+ Reorder (adds to cart)
53
+
54
+ - Preview: `ordercli foodora reorder <orderCode>`
55
+ - Confirm: `ordercli foodora reorder <orderCode> --confirm`
56
+ - Address: `ordercli foodora reorder <orderCode> --confirm --address-id <id>`
57
+
58
+ Cloudflare / bot protection
59
+
60
+ - Browser login: `ordercli foodora login --email you@example.com --password-stdin --browser`
61
+ - Reuse profile: `--browser-profile "$HOME/Library/Application Support/ordercli/browser-profile"`
62
+ - Import Chrome cookies: `ordercli foodora cookies chrome --profile "Default"`
63
+
64
+ Session import (no password)
65
+
66
+ - `ordercli foodora session chrome --url https://www.foodora.at/ --profile "Default"`
67
+ - `ordercli foodora session refresh --client-id android`
68
+
69
+ Deliveroo (WIP, not working yet)
70
+
71
+ - Requires `DELIVEROO_BEARER_TOKEN` (optional `DELIVEROO_COOKIE`).
72
+ - `ordercli deliveroo config set --market uk`
73
+ - `ordercli deliveroo history`
74
+
75
+ Notes
76
+
77
+ - Use `--config /tmp/ordercli.json` for testing.
78
+ - Confirm before any reorder or cart-changing action.
skills/peekaboo/SKILL.md ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: peekaboo
3
+ description: Capture and automate macOS UI with the Peekaboo CLI.
4
+ homepage: https://peekaboo.boo
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "👀",
10
+ "os": ["darwin"],
11
+ "requires": { "bins": ["peekaboo"] },
12
+ "install":
13
+ [
14
+ {
15
+ "id": "brew",
16
+ "kind": "brew",
17
+ "formula": "steipete/tap/peekaboo",
18
+ "bins": ["peekaboo"],
19
+ "label": "Install Peekaboo (brew)",
20
+ },
21
+ ],
22
+ },
23
+ }
24
+ ---
25
+
26
+ # Peekaboo
27
+
28
+ Peekaboo is a full macOS UI automation CLI: capture/inspect screens, target UI
29
+ elements, drive input, and manage apps/windows/menus. Commands share a snapshot
30
+ cache and support `--json`/`-j` for scripting. Run `peekaboo` or
31
+ `peekaboo <cmd> --help` for flags; `peekaboo --version` prints build metadata.
32
+ Tip: run via `polter peekaboo` to ensure fresh builds.
33
+
34
+ ## Features (all CLI capabilities, excluding agent/MCP)
35
+
36
+ Core
37
+
38
+ - `bridge`: inspect Peekaboo Bridge host connectivity
39
+ - `capture`: live capture or video ingest + frame extraction
40
+ - `clean`: prune snapshot cache and temp files
41
+ - `config`: init/show/edit/validate, providers, models, credentials
42
+ - `image`: capture screenshots (screen/window/menu bar regions)
43
+ - `learn`: print the full agent guide + tool catalog
44
+ - `list`: apps, windows, screens, menubar, permissions
45
+ - `permissions`: check Screen Recording/Accessibility status
46
+ - `run`: execute `.peekaboo.json` scripts
47
+ - `sleep`: pause execution for a duration
48
+ - `tools`: list available tools with filtering/display options
49
+
50
+ Interaction
51
+
52
+ - `click`: target by ID/query/coords with smart waits
53
+ - `drag`: drag & drop across elements/coords/Dock
54
+ - `hotkey`: modifier combos like `cmd,shift,t`
55
+ - `move`: cursor positioning with optional smoothing
56
+ - `paste`: set clipboard -> paste -> restore
57
+ - `press`: special-key sequences with repeats
58
+ - `scroll`: directional scrolling (targeted + smooth)
59
+ - `swipe`: gesture-style drags between targets
60
+ - `type`: text + control keys (`--clear`, delays)
61
+
62
+ System
63
+
64
+ - `app`: launch/quit/relaunch/hide/unhide/switch/list apps
65
+ - `clipboard`: read/write clipboard (text/images/files)
66
+ - `dialog`: click/input/file/dismiss/list system dialogs
67
+ - `dock`: launch/right-click/hide/show/list Dock items
68
+ - `menu`: click/list application menus + menu extras
69
+ - `menubar`: list/click status bar items
70
+ - `open`: enhanced `open` with app targeting + JSON payloads
71
+ - `space`: list/switch/move-window (Spaces)
72
+ - `visualizer`: exercise Peekaboo visual feedback animations
73
+ - `window`: close/minimize/maximize/move/resize/focus/list
74
+
75
+ Vision
76
+
77
+ - `see`: annotated UI maps, snapshot IDs, optional analysis
78
+
79
+ Global runtime flags
80
+
81
+ - `--json`/`-j`, `--verbose`/`-v`, `--log-level <level>`
82
+ - `--no-remote`, `--bridge-socket <path>`
83
+
84
+ ## Quickstart (happy path)
85
+
86
+ ```bash
87
+ peekaboo permissions
88
+ peekaboo list apps --json
89
+ peekaboo see --annotate --path /tmp/peekaboo-see.png
90
+ peekaboo click --on B1
91
+ peekaboo type "Hello" --return
92
+ ```
93
+
94
+ ## Common targeting parameters (most interaction commands)
95
+
96
+ - App/window: `--app`, `--pid`, `--window-title`, `--window-id`, `--window-index`
97
+ - Snapshot targeting: `--snapshot` (ID from `see`; defaults to latest)
98
+ - Element/coords: `--on`/`--id` (element ID), `--coords x,y`
99
+ - Focus control: `--no-auto-focus`, `--space-switch`, `--bring-to-current-space`,
100
+ `--focus-timeout-seconds`, `--focus-retry-count`
101
+
102
+ ## Common capture parameters
103
+
104
+ - Output: `--path`, `--format png|jpg`, `--retina`
105
+ - Targeting: `--mode screen|window|frontmost`, `--screen-index`,
106
+ `--window-title`, `--window-id`
107
+ - Analysis: `--analyze "prompt"`, `--annotate`
108
+ - Capture engine: `--capture-engine auto|classic|cg|modern|sckit`
109
+
110
+ ## Common motion/typing parameters
111
+
112
+ - Timing: `--duration` (drag/swipe), `--steps`, `--delay` (type/scroll/press)
113
+ - Human-ish movement: `--profile human|linear`, `--wpm` (typing)
114
+ - Scroll: `--direction up|down|left|right`, `--amount <ticks>`, `--smooth`
115
+
116
+ ## Examples
117
+
118
+ ### See -> click -> type (most reliable flow)
119
+
120
+ ```bash
121
+ peekaboo see --app Safari --window-title "Login" --annotate --path /tmp/see.png
122
+ peekaboo click --on B3 --app Safari
123
+ peekaboo type "user@example.com" --app Safari
124
+ peekaboo press tab --count 1 --app Safari
125
+ peekaboo type "supersecret" --app Safari --return
126
+ ```
127
+
128
+ ### Target by window id
129
+
130
+ ```bash
131
+ peekaboo list windows --app "Visual Studio Code" --json
132
+ peekaboo click --window-id 12345 --coords 120,160
133
+ peekaboo type "Hello from Peekaboo" --window-id 12345
134
+ ```
135
+
136
+ ### Capture screenshots + analyze
137
+
138
+ ```bash
139
+ peekaboo image --mode screen --screen-index 0 --retina --path /tmp/screen.png
140
+ peekaboo image --app Safari --window-title "Dashboard" --analyze "Summarize KPIs"
141
+ peekaboo see --mode screen --screen-index 0 --analyze "Summarize the dashboard"
142
+ ```
143
+
144
+ ### Live capture (motion-aware)
145
+
146
+ ```bash
147
+ peekaboo capture live --mode region --region 100,100,800,600 --duration 30 \
148
+ --active-fps 8 --idle-fps 2 --highlight-changes --path /tmp/capture
149
+ ```
150
+
151
+ ### App + window management
152
+
153
+ ```bash
154
+ peekaboo app launch "Safari" --open https://example.com
155
+ peekaboo window focus --app Safari --window-title "Example"
156
+ peekaboo window set-bounds --app Safari --x 50 --y 50 --width 1200 --height 800
157
+ peekaboo app quit --app Safari
158
+ ```
159
+
160
+ ### Menus, menubar, dock
161
+
162
+ ```bash
163
+ peekaboo menu click --app Safari --item "New Window"
164
+ peekaboo menu click --app TextEdit --path "Format > Font > Show Fonts"
165
+ peekaboo menu click-extra --title "WiFi"
166
+ peekaboo dock launch Safari
167
+ peekaboo menubar list --json
168
+ ```
169
+
170
+ ### Mouse + gesture input
171
+
172
+ ```bash
173
+ peekaboo move 500,300 --smooth
174
+ peekaboo drag --from B1 --to T2
175
+ peekaboo swipe --from-coords 100,500 --to-coords 100,200 --duration 800
176
+ peekaboo scroll --direction down --amount 6 --smooth
177
+ ```
178
+
179
+ ### Keyboard input
180
+
181
+ ```bash
182
+ peekaboo hotkey --keys "cmd,shift,t"
183
+ peekaboo press escape
184
+ peekaboo type "Line 1\nLine 2" --delay 10
185
+ ```
186
+
187
+ Notes
188
+
189
+ - Requires Screen Recording + Accessibility permissions.
190
+ - Use `peekaboo see --annotate` to identify targets before clicking.
skills/sag/SKILL.md ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: sag
3
+ description: ElevenLabs text-to-speech with mac-style say UX.
4
+ homepage: https://sag.sh
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🔊",
10
+ "requires": { "bins": ["sag"], "env": ["ELEVENLABS_API_KEY"] },
11
+ "primaryEnv": "ELEVENLABS_API_KEY",
12
+ "install":
13
+ [
14
+ {
15
+ "id": "brew",
16
+ "kind": "brew",
17
+ "formula": "steipete/tap/sag",
18
+ "bins": ["sag"],
19
+ "label": "Install sag (brew)",
20
+ },
21
+ ],
22
+ },
23
+ }
24
+ ---
25
+
26
+ # sag
27
+
28
+ Use `sag` for ElevenLabs TTS with local playback.
29
+
30
+ API key (required)
31
+
32
+ - `ELEVENLABS_API_KEY` (preferred)
33
+ - `SAG_API_KEY` also supported by the CLI
34
+
35
+ Quick start
36
+
37
+ - `sag "Hello there"`
38
+ - `sag speak -v "Roger" "Hello"`
39
+ - `sag voices`
40
+ - `sag prompting` (model-specific tips)
41
+
42
+ Model notes
43
+
44
+ - Default: `eleven_v3` (expressive)
45
+ - Stable: `eleven_multilingual_v2`
46
+ - Fast: `eleven_flash_v2_5`
47
+
48
+ Pronunciation + delivery rules
49
+
50
+ - First fix: respell (e.g. "key-note"), add hyphens, adjust casing.
51
+ - Numbers/units/URLs: `--normalize auto` (or `off` if it harms names).
52
+ - Language bias: `--lang en|de|fr|...` to guide normalization.
53
+ - v3: SSML `<break>` not supported; use `[pause]`, `[short pause]`, `[long pause]`.
54
+ - v2/v2.5: SSML `<break time="1.5s" />` supported; `<phoneme>` not exposed in `sag`.
55
+
56
+ v3 audio tags (put at the entrance of a line)
57
+
58
+ - `[whispers]`, `[shouts]`, `[sings]`
59
+ - `[laughs]`, `[starts laughing]`, `[sighs]`, `[exhales]`
60
+ - `[sarcastic]`, `[curious]`, `[excited]`, `[crying]`, `[mischievously]`
61
+ - Example: `sag "[whispers] keep this quiet. [short pause] ok?"`
62
+
63
+ Voice defaults
64
+
65
+ - `ELEVENLABS_VOICE_ID` or `SAG_VOICE_ID`
66
+
67
+ Confirm voice + speaker before long output.
68
+
69
+ ## Chat voice responses
70
+
71
+ When the user asks for a "voice" reply (e.g., "crazy scientist voice", "explain in voice"), generate audio and send it:
72
+
73
+ ```bash
74
+ # Generate audio file
75
+ sag -v Clawd -o /tmp/voice-reply.mp3 "Your message here"
76
+
77
+ # Then include in reply:
78
+ # MEDIA:/tmp/voice-reply.mp3
79
+ ```
80
+
81
+ Voice character tips:
82
+
83
+ - Crazy scientist: Use `[excited]` tags, dramatic pauses `[short pause]`, vary intensity
84
+ - Calm: Use `[whispers]` or slower pacing
85
+ - Dramatic: Use `[sings]` or `[shouts]` sparingly
86
+
87
+ Default voice for Clawd: `lj2rcrvANS3gaWWnczSX` (or just `-v Clawd`)
skills/session-logs/SKILL.md ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: session-logs
3
+ description: Search and analyze your own session logs (older/parent conversations) using jq.
4
+ metadata:
5
+ {
6
+ "openclaw":
7
+ {
8
+ "emoji": "📜",
9
+ "requires": { "bins": ["jq", "rg"] },
10
+ "install":
11
+ [
12
+ {
13
+ "id": "brew-jq",
14
+ "kind": "brew",
15
+ "formula": "jq",
16
+ "bins": ["jq"],
17
+ "label": "Install jq (brew)",
18
+ },
19
+ {
20
+ "id": "brew-rg",
21
+ "kind": "brew",
22
+ "formula": "ripgrep",
23
+ "bins": ["rg"],
24
+ "label": "Install ripgrep (brew)",
25
+ },
26
+ ],
27
+ },
28
+ }
29
+ ---
30
+
31
+ # session-logs
32
+
33
+ Search your complete conversation history stored in session JSONL files. Use this when a user references older/parent conversations or asks what was said before.
34
+
35
+ ## Trigger
36
+
37
+ Use this skill when the user asks about prior chats, parent conversations, or historical context that isn't in memory files.
38
+
39
+ ## Location
40
+
41
+ Session logs live under the active state directory:
42
+ `$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/` (default: `~/.openclaw/agents/<agentId>/sessions/`).
43
+ Use the `agent=<id>` value from the system prompt Runtime line.
44
+
45
+ - **`sessions.json`** - Index mapping session keys to session IDs
46
+ - **`<session-id>.jsonl`** - Full conversation transcript per session
47
+
48
+ ## Structure
49
+
50
+ Each `.jsonl` file contains messages with:
51
+
52
+ - `type`: "session" (metadata) or "message"
53
+ - `timestamp`: ISO timestamp
54
+ - `message.role`: "user", "assistant", or "toolResult"
55
+ - `message.content[]`: Text, thinking, or tool calls (filter `type=="text"` for human-readable content)
56
+ - `message.usage.cost.total`: Cost per response
57
+
58
+ ## Common Queries
59
+
60
+ ### List all sessions by date and size
61
+
62
+ ```bash
63
+ AGENT_ID="<agentId>"
64
+ SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
65
+ for f in "$SESSION_DIR"/*.jsonl; do
66
+ date=$(head -1 "$f" | jq -r '.timestamp' | cut -dT -f1)
67
+ size=$(ls -lh "$f" | awk '{print $5}')
68
+ echo "$date $size $(basename $f)"
69
+ done | sort -r
70
+ ```
71
+
72
+ ### Find sessions from a specific day
73
+
74
+ ```bash
75
+ AGENT_ID="<agentId>"
76
+ SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
77
+ for f in "$SESSION_DIR"/*.jsonl; do
78
+ head -1 "$f" | jq -r '.timestamp' | grep -q "2026-01-06" && echo "$f"
79
+ done
80
+ ```
81
+
82
+ ### Extract user messages from a session
83
+
84
+ ```bash
85
+ jq -r 'select(.message.role == "user") | .message.content[]? | select(.type == "text") | .text' <session>.jsonl
86
+ ```
87
+
88
+ ### Search for keyword in assistant responses
89
+
90
+ ```bash
91
+ jq -r 'select(.message.role == "assistant") | .message.content[]? | select(.type == "text") | .text' <session>.jsonl | rg -i "keyword"
92
+ ```
93
+
94
+ ### Get total cost for a session
95
+
96
+ ```bash
97
+ jq -s '[.[] | .message.usage.cost.total // 0] | add' <session>.jsonl
98
+ ```
99
+
100
+ ### Daily cost summary
101
+
102
+ ```bash
103
+ AGENT_ID="<agentId>"
104
+ SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
105
+ for f in "$SESSION_DIR"/*.jsonl; do
106
+ date=$(head -1 "$f" | jq -r '.timestamp' | cut -dT -f1)
107
+ cost=$(jq -s '[.[] | .message.usage.cost.total // 0] | add' "$f")
108
+ echo "$date $cost"
109
+ done | awk '{a[$1]+=$2} END {for(d in a) print d, "$"a[d]}' | sort -r
110
+ ```
111
+
112
+ ### Count messages and tokens in a session
113
+
114
+ ```bash
115
+ jq -s '{
116
+ messages: length,
117
+ user: [.[] | select(.message.role == "user")] | length,
118
+ assistant: [.[] | select(.message.role == "assistant")] | length,
119
+ first: .[0].timestamp,
120
+ last: .[-1].timestamp
121
+ }' <session>.jsonl
122
+ ```
123
+
124
+ ### Tool usage breakdown
125
+
126
+ ```bash
127
+ jq -r '.message.content[]? | select(.type == "toolCall") | .name' <session>.jsonl | sort | uniq -c | sort -rn
128
+ ```
129
+
130
+ ### Search across ALL sessions for a phrase
131
+
132
+ ```bash
133
+ AGENT_ID="<agentId>"
134
+ SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
135
+ rg -l "phrase" "$SESSION_DIR"/*.jsonl
136
+ ```
137
+
138
+ ## Tips
139
+
140
+ - Sessions are append-only JSONL (one JSON object per line)
141
+ - Large sessions can be several MB - use `head`/`tail` for sampling
142
+ - The `sessions.json` index maps chat providers (discord, whatsapp, etc.) to session IDs
143
+ - Deleted sessions have `.deleted.<timestamp>` suffix
144
+
145
+ ## Fast text-only hint (low noise)
146
+
147
+ ```bash
148
+ AGENT_ID="<agentId>"
149
+ SESSION_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/agents/$AGENT_ID/sessions"
150
+ jq -r 'select(.type=="message") | .message.content[]? | select(.type=="text") | .text' "$SESSION_DIR"/<id>.jsonl | rg 'keyword'
151
+ ```
skills/sherpa-onnx-tts/SKILL.md ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: sherpa-onnx-tts
3
+ description: Local text-to-speech via sherpa-onnx (offline, no cloud)
4
+ metadata:
5
+ {
6
+ "openclaw":
7
+ {
8
+ "emoji": "🔉",
9
+ "os": ["darwin", "linux", "win32"],
10
+ "requires": { "env": ["SHERPA_ONNX_RUNTIME_DIR", "SHERPA_ONNX_MODEL_DIR"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "download-runtime-macos",
15
+ "kind": "download",
16
+ "os": ["darwin"],
17
+ "url": "https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-osx-universal2-shared.tar.bz2",
18
+ "archive": "tar.bz2",
19
+ "extract": true,
20
+ "stripComponents": 1,
21
+ "targetDir": "runtime",
22
+ "label": "Download sherpa-onnx runtime (macOS)",
23
+ },
24
+ {
25
+ "id": "download-runtime-linux-x64",
26
+ "kind": "download",
27
+ "os": ["linux"],
28
+ "url": "https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-linux-x64-shared.tar.bz2",
29
+ "archive": "tar.bz2",
30
+ "extract": true,
31
+ "stripComponents": 1,
32
+ "targetDir": "runtime",
33
+ "label": "Download sherpa-onnx runtime (Linux x64)",
34
+ },
35
+ {
36
+ "id": "download-runtime-win-x64",
37
+ "kind": "download",
38
+ "os": ["win32"],
39
+ "url": "https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-win-x64-shared.tar.bz2",
40
+ "archive": "tar.bz2",
41
+ "extract": true,
42
+ "stripComponents": 1,
43
+ "targetDir": "runtime",
44
+ "label": "Download sherpa-onnx runtime (Windows x64)",
45
+ },
46
+ {
47
+ "id": "download-model-lessac",
48
+ "kind": "download",
49
+ "url": "https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-en_US-lessac-high.tar.bz2",
50
+ "archive": "tar.bz2",
51
+ "extract": true,
52
+ "targetDir": "models",
53
+ "label": "Download Piper en_US lessac (high)",
54
+ },
55
+ ],
56
+ },
57
+ }
58
+ ---
59
+
60
+ # sherpa-onnx-tts
61
+
62
+ Local TTS using the sherpa-onnx offline CLI.
63
+
64
+ ## Install
65
+
66
+ 1. Download the runtime for your OS (extracts into `$OPENCLAW_STATE_DIR/tools/sherpa-onnx-tts/runtime`, default `~/.openclaw/tools/sherpa-onnx-tts/runtime`)
67
+ 2. Download a voice model (extracts into `$OPENCLAW_STATE_DIR/tools/sherpa-onnx-tts/models`, default `~/.openclaw/tools/sherpa-onnx-tts/models`)
68
+
69
+ Resolve the active state directory first:
70
+
71
+ ```bash
72
+ STATE_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}"
73
+ ```
74
+
75
+ Then write those resolved paths into the active OpenClaw config file (`$OPENCLAW_CONFIG_PATH`, default `~/.openclaw/openclaw.json`):
76
+
77
+ ```json5
78
+ {
79
+ skills: {
80
+ entries: {
81
+ "sherpa-onnx-tts": {
82
+ env: {
83
+ SHERPA_ONNX_RUNTIME_DIR: "/path/to/your/state-dir/tools/sherpa-onnx-tts/runtime",
84
+ SHERPA_ONNX_MODEL_DIR: "/path/to/your/state-dir/tools/sherpa-onnx-tts/models/vits-piper-en_US-lessac-high",
85
+ },
86
+ },
87
+ },
88
+ },
89
+ }
90
+ ```
91
+
92
+ The wrapper lives in this skill folder. Run it directly, or add the wrapper to PATH:
93
+
94
+ ```bash
95
+ export PATH="{baseDir}/bin:$PATH"
96
+ ```
97
+
98
+ ## Usage
99
+
100
+ ```bash
101
+ {baseDir}/bin/sherpa-onnx-tts -o ./tts.wav "Hello from local TTS."
102
+ ```
103
+
104
+ Notes:
105
+
106
+ - Pick a different model from the sherpa-onnx `tts-models` release if you want another voice.
107
+ - If the model dir has multiple `.onnx` files, set `SHERPA_ONNX_MODEL_FILE` or pass `--model-file`.
108
+ - You can also pass `--tokens-file` or `--data-dir` to override the defaults.
109
+ - Windows: run `node {baseDir}\\bin\\sherpa-onnx-tts -o tts.wav "Hello from local TTS."`
skills/skill-creator/SKILL.md ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: skill-creator
3
+ 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".
4
+ ---
5
+
6
+ # Skill Creator
7
+
8
+ This skill provides guidance for creating effective skills.
9
+
10
+ ## About Skills
11
+
12
+ Skills are modular, self-contained packages that extend Codex's capabilities by providing
13
+ specialized knowledge, workflows, and tools. Think of them as "onboarding guides" for specific
14
+ domains or tasks—they transform Codex from a general-purpose agent into a specialized agent
15
+ equipped with procedural knowledge that no model can fully possess.
16
+
17
+ ### What Skills Provide
18
+
19
+ 1. Specialized workflows - Multi-step procedures for specific domains
20
+ 2. Tool integrations - Instructions for working with specific file formats or APIs
21
+ 3. Domain expertise - Company-specific knowledge, schemas, business logic
22
+ 4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks
23
+
24
+ ## Core Principles
25
+
26
+ ### Concise is Key
27
+
28
+ The 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.
29
+
30
+ **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?"
31
+
32
+ Prefer concise examples over verbose explanations.
33
+
34
+ ### Set Appropriate Degrees of Freedom
35
+
36
+ Match the level of specificity to the task's fragility and variability:
37
+
38
+ **High freedom (text-based instructions)**: Use when multiple approaches are valid, decisions depend on context, or heuristics guide the approach.
39
+
40
+ **Medium freedom (pseudocode or scripts with parameters)**: Use when a preferred pattern exists, some variation is acceptable, or configuration affects behavior.
41
+
42
+ **Low freedom (specific scripts, few parameters)**: Use when operations are fragile and error-prone, consistency is critical, or a specific sequence must be followed.
43
+
44
+ Think 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).
45
+
46
+ ### Anatomy of a Skill
47
+
48
+ Every skill consists of a required SKILL.md file and optional bundled resources:
49
+
50
+ ```
51
+ skill-name/
52
+ ├── SKILL.md (required)
53
+ │ ├── YAML frontmatter metadata (required)
54
+ │ │ ├── name: (required)
55
+ │ │ └── description: (required)
56
+ │ └── Markdown instructions (required)
57
+ └── Bundled Resources (optional)
58
+ ├── scripts/ - Executable code (Python/Bash/etc.)
59
+ ├── references/ - Documentation intended to be loaded into context as needed
60
+ └── assets/ - Files used in output (templates, icons, fonts, etc.)
61
+ ```
62
+
63
+ #### SKILL.md (required)
64
+
65
+ Every SKILL.md consists of:
66
+
67
+ - **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.
68
+ - **Body** (Markdown): Instructions and guidance for using the skill. Only loaded AFTER the skill triggers (if at all).
69
+
70
+ #### Bundled Resources (optional)
71
+
72
+ ##### Scripts (`scripts/`)
73
+
74
+ Executable code (Python/Bash/etc.) for tasks that require deterministic reliability or are repeatedly rewritten.
75
+
76
+ - **When to include**: When the same code is being rewritten repeatedly or deterministic reliability is needed
77
+ - **Example**: `scripts/rotate_pdf.py` for PDF rotation tasks
78
+ - **Benefits**: Token efficient, deterministic, may be executed without loading into context
79
+ - **Note**: Scripts may still need to be read by Codex for patching or environment-specific adjustments
80
+
81
+ ##### References (`references/`)
82
+
83
+ Documentation and reference material intended to be loaded as needed into context to inform Codex's process and thinking.
84
+
85
+ - **When to include**: For documentation that Codex should reference while working
86
+ - **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
87
+ - **Use cases**: Database schemas, API documentation, domain knowledge, company policies, detailed workflow guides
88
+ - **Benefits**: Keeps SKILL.md lean, loaded only when Codex determines it's needed
89
+ - **Best practice**: If files are large (>10k words), include grep search patterns in SKILL.md
90
+ - **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.
91
+
92
+ ##### Assets (`assets/`)
93
+
94
+ Files not intended to be loaded into context, but rather used within the output Codex produces.
95
+
96
+ - **When to include**: When the skill needs files that will be used in the final output
97
+ - **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
98
+ - **Use cases**: Templates, images, icons, boilerplate code, fonts, sample documents that get copied or modified
99
+ - **Benefits**: Separates output resources from documentation, enables Codex to use files without loading them into context
100
+
101
+ #### What to Not Include in a Skill
102
+
103
+ A skill should only contain essential files that directly support its functionality. Do NOT create extraneous documentation or auxiliary files, including:
104
+
105
+ - README.md
106
+ - INSTALLATION_GUIDE.md
107
+ - QUICK_REFERENCE.md
108
+ - CHANGELOG.md
109
+ - etc.
110
+
111
+ The 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.
112
+
113
+ ### Progressive Disclosure Design Principle
114
+
115
+ Skills use a three-level loading system to manage context efficiently:
116
+
117
+ 1. **Metadata (name + description)** - Always in context (~100 words)
118
+ 2. **SKILL.md body** - When skill triggers (<5k words)
119
+ 3. **Bundled resources** - As needed by Codex (Unlimited because scripts can be executed without reading into context window)
120
+
121
+ #### Progressive Disclosure Patterns
122
+
123
+ Keep 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.
124
+
125
+ **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.
126
+
127
+ **Pattern 1: High-level guide with references**
128
+
129
+ ```markdown
130
+ # PDF Processing
131
+
132
+ ## Quick start
133
+
134
+ Extract text with pdfplumber:
135
+ [code example]
136
+
137
+ ## Advanced features
138
+
139
+ - **Form filling**: See [FORMS.md](FORMS.md) for complete guide
140
+ - **API reference**: See [REFERENCE.md](REFERENCE.md) for all methods
141
+ - **Examples**: See [EXAMPLES.md](EXAMPLES.md) for common patterns
142
+ ```
143
+
144
+ Codex loads FORMS.md, REFERENCE.md, or EXAMPLES.md only when needed.
145
+
146
+ **Pattern 2: Domain-specific organization**
147
+
148
+ For Skills with multiple domains, organize content by domain to avoid loading irrelevant context:
149
+
150
+ ```
151
+ bigquery-skill/
152
+ ├── SKILL.md (overview and navigation)
153
+ └── reference/
154
+ ├── finance.md (revenue, billing metrics)
155
+ ├── sales.md (opportunities, pipeline)
156
+ ├── product.md (API usage, features)
157
+ └── marketing.md (campaigns, attribution)
158
+ ```
159
+
160
+ When a user asks about sales metrics, Codex only reads sales.md.
161
+
162
+ Similarly, for skills supporting multiple frameworks or variants, organize by variant:
163
+
164
+ ```
165
+ cloud-deploy/
166
+ ├── SKILL.md (workflow + provider selection)
167
+ └── references/
168
+ ├── aws.md (AWS deployment patterns)
169
+ ├── gcp.md (GCP deployment patterns)
170
+ └── azure.md (Azure deployment patterns)
171
+ ```
172
+
173
+ When the user chooses AWS, Codex only reads aws.md.
174
+
175
+ **Pattern 3: Conditional details**
176
+
177
+ Show basic content, link to advanced content:
178
+
179
+ ```markdown
180
+ # DOCX Processing
181
+
182
+ ## Creating documents
183
+
184
+ Use docx-js for new documents. See [DOCX-JS.md](DOCX-JS.md).
185
+
186
+ ## Editing documents
187
+
188
+ For simple edits, modify the XML directly.
189
+
190
+ **For tracked changes**: See [REDLINING.md](REDLINING.md)
191
+ **For OOXML details**: See [OOXML.md](OOXML.md)
192
+ ```
193
+
194
+ Codex reads REDLINING.md or OOXML.md only when the user needs those features.
195
+
196
+ **Important guidelines:**
197
+
198
+ - **Avoid deeply nested references** - Keep references one level deep from SKILL.md. All reference files should link directly from SKILL.md.
199
+ - **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.
200
+
201
+ ## Skill Creation Process
202
+
203
+ Skill creation involves these steps:
204
+
205
+ 1. Understand the skill with concrete examples
206
+ 2. Plan reusable skill contents (scripts, references, assets)
207
+ 3. Initialize the skill (run init_skill.py)
208
+ 4. Edit the skill (implement resources and write SKILL.md)
209
+ 5. Package the skill (run package_skill.py)
210
+ 6. Iterate based on real usage
211
+
212
+ Follow these steps in order, skipping only if there is a clear reason why they are not applicable.
213
+
214
+ ### Skill Naming
215
+
216
+ - Use lowercase letters, digits, and hyphens only; normalize user-provided titles to hyphen-case (e.g., "Plan Mode" -> `plan-mode`).
217
+ - When generating names, generate a name under 64 characters (letters, digits, hyphens).
218
+ - Prefer short, verb-led phrases that describe the action.
219
+ - Namespace by tool when it improves clarity or triggering (e.g., `gh-address-comments`, `linear-address-issue`).
220
+ - Name the skill folder exactly after the skill name.
221
+
222
+ ### Step 1: Understanding the Skill with Concrete Examples
223
+
224
+ Skip this step only when the skill's usage patterns are already clearly understood. It remains valuable even when working with an existing skill.
225
+
226
+ To 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.
227
+
228
+ For example, when building an image-editor skill, relevant questions include:
229
+
230
+ - "What functionality should the image-editor skill support? Editing, rotating, anything else?"
231
+ - "Can you give some examples of how this skill would be used?"
232
+ - "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?"
233
+ - "What would a user say that should trigger this skill?"
234
+
235
+ To 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.
236
+
237
+ Conclude this step when there is a clear sense of the functionality the skill should support.
238
+
239
+ ### Step 2: Planning the Reusable Skill Contents
240
+
241
+ To turn concrete examples into an effective skill, analyze each example by:
242
+
243
+ 1. Considering how to execute on the example from scratch
244
+ 2. Identifying what scripts, references, and assets would be helpful when executing these workflows repeatedly
245
+
246
+ Example: When building a `pdf-editor` skill to handle queries like "Help me rotate this PDF," the analysis shows:
247
+
248
+ 1. Rotating a PDF requires re-writing the same code each time
249
+ 2. A `scripts/rotate_pdf.py` script would be helpful to store in the skill
250
+
251
+ Example: 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:
252
+
253
+ 1. Writing a frontend webapp requires the same boilerplate HTML/React each time
254
+ 2. An `assets/hello-world/` template containing the boilerplate HTML/React project files would be helpful to store in the skill
255
+
256
+ Example: When building a `big-query` skill to handle queries like "How many users have logged in today?" the analysis shows:
257
+
258
+ 1. Querying BigQuery requires re-discovering the table schemas and relationships each time
259
+ 2. A `references/schema.md` file documenting the table schemas would be helpful to store in the skill
260
+
261
+ To establish the skill's contents, analyze each concrete example to create a list of the reusable resources to include: scripts, references, and assets.
262
+
263
+ ### Step 3: Initializing the Skill
264
+
265
+ At this point, it is time to actually create the skill.
266
+
267
+ Skip this step only if the skill being developed already exists, and iteration or packaging is needed. In this case, continue to the next step.
268
+
269
+ When 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.
270
+
271
+ Usage:
272
+
273
+ ```bash
274
+ scripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples]
275
+ ```
276
+
277
+ Examples:
278
+
279
+ ```bash
280
+ scripts/init_skill.py my-skill --path skills/public
281
+ scripts/init_skill.py my-skill --path skills/public --resources scripts,references
282
+ scripts/init_skill.py my-skill --path skills/public --resources scripts --examples
283
+ ```
284
+
285
+ The script:
286
+
287
+ - Creates the skill directory at the specified path
288
+ - Generates a SKILL.md template with proper frontmatter and TODO placeholders
289
+ - Optionally creates resource directories based on `--resources`
290
+ - Optionally adds example files when `--examples` is set
291
+
292
+ After initialization, customize the SKILL.md and add resources as needed. If you used `--examples`, replace or delete placeholder files.
293
+
294
+ ### Step 4: Edit the Skill
295
+
296
+ When 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.
297
+
298
+ #### Learn Proven Design Patterns
299
+
300
+ Consult these helpful guides based on your skill's needs:
301
+
302
+ - **Multi-step processes**: See references/workflows.md for sequential workflows and conditional logic
303
+ - **Specific output formats or quality standards**: See references/output-patterns.md for template and example patterns
304
+
305
+ These files contain established best practices for effective skill design.
306
+
307
+ #### Start with Reusable Skill Contents
308
+
309
+ To 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/`.
310
+
311
+ Added 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.
312
+
313
+ If you used `--examples`, delete any placeholder files that are not needed for the skill. Only create resource directories that are actually required.
314
+
315
+ #### Update SKILL.md
316
+
317
+ **Writing Guidelines:** Always use imperative/infinitive form.
318
+
319
+ ##### Frontmatter
320
+
321
+ Write the YAML frontmatter with `name` and `description`:
322
+
323
+ - `name`: The skill name
324
+ - `description`: This is the primary triggering mechanism for your skill, and helps Codex understand when to use the skill.
325
+ - Include both what the Skill does and specific triggers/contexts for when to use it.
326
+ - 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.
327
+ - 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"
328
+
329
+ Do not include any other fields in YAML frontmatter.
330
+
331
+ ##### Body
332
+
333
+ Write instructions for using the skill and its bundled resources.
334
+
335
+ ### Step 5: Packaging a Skill
336
+
337
+ Once 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:
338
+
339
+ ```bash
340
+ scripts/package_skill.py <path/to/skill-folder>
341
+ ```
342
+
343
+ Optional output directory specification:
344
+
345
+ ```bash
346
+ scripts/package_skill.py <path/to/skill-folder> ./dist
347
+ ```
348
+
349
+ The packaging script will:
350
+
351
+ 1. **Validate** the skill automatically, checking:
352
+ - YAML frontmatter format and required fields
353
+ - Skill naming conventions and directory structure
354
+ - Description completeness and quality
355
+ - File organization and resource references
356
+
357
+ 2. **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.
358
+
359
+ Security restriction: symlinks are rejected and packaging fails when any symlink is present.
360
+
361
+ If validation fails, the script will report the errors and exit without creating a package. Fix any validation errors and run the packaging command again.
362
+
363
+ ### Step 6: Iterate
364
+
365
+ After testing the skill, users may request improvements. Often this happens right after using the skill, with fresh context of how the skill performed.
366
+
367
+ **Iteration workflow:**
368
+
369
+ 1. Use the skill on real tasks
370
+ 2. Notice struggles or inefficiencies
371
+ 3. Identify how SKILL.md or bundled resources should be updated
372
+ 4. Implement changes and test again
skills/slack/SKILL.md ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: slack
3
+ 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.
4
+ metadata: { "openclaw": { "emoji": "💬", "requires": { "config": ["channels.slack"] } } }
5
+ ---
6
+
7
+ # Slack Actions
8
+
9
+ ## Overview
10
+
11
+ Use `slack` to react, manage pins, send/edit/delete messages, and fetch member info. The tool uses the bot token configured for OpenClaw.
12
+
13
+ ## Inputs to collect
14
+
15
+ - `channelId` and `messageId` (Slack message timestamp, e.g. `1712023032.1234`).
16
+ - For reactions, an `emoji` (Unicode or `:name:`).
17
+ - For message sends, a `to` target (`channel:<id>` or `user:<id>`) and `content`.
18
+
19
+ Message context lines include `slack message id` and `channel` fields you can reuse directly.
20
+
21
+ ## Actions
22
+
23
+ ### Action groups
24
+
25
+ | Action group | Default | Notes |
26
+ | ------------ | ------- | ---------------------- |
27
+ | reactions | enabled | React + list reactions |
28
+ | messages | enabled | Read/send/edit/delete |
29
+ | pins | enabled | Pin/unpin/list |
30
+ | memberInfo | enabled | Member info |
31
+ | emojiList | enabled | Custom emoji list |
32
+
33
+ ### React to a message
34
+
35
+ ```json
36
+ {
37
+ "action": "react",
38
+ "channelId": "C123",
39
+ "messageId": "1712023032.1234",
40
+ "emoji": "✅"
41
+ }
42
+ ```
43
+
44
+ ### List reactions
45
+
46
+ ```json
47
+ {
48
+ "action": "reactions",
49
+ "channelId": "C123",
50
+ "messageId": "1712023032.1234"
51
+ }
52
+ ```
53
+
54
+ ### Send a message
55
+
56
+ ```json
57
+ {
58
+ "action": "sendMessage",
59
+ "to": "channel:C123",
60
+ "content": "Hello from OpenClaw"
61
+ }
62
+ ```
63
+
64
+ ### Edit a message
65
+
66
+ ```json
67
+ {
68
+ "action": "editMessage",
69
+ "channelId": "C123",
70
+ "messageId": "1712023032.1234",
71
+ "content": "Updated text"
72
+ }
73
+ ```
74
+
75
+ ### Delete a message
76
+
77
+ ```json
78
+ {
79
+ "action": "deleteMessage",
80
+ "channelId": "C123",
81
+ "messageId": "1712023032.1234"
82
+ }
83
+ ```
84
+
85
+ ### Read recent messages
86
+
87
+ ```json
88
+ {
89
+ "action": "readMessages",
90
+ "channelId": "C123",
91
+ "limit": 20
92
+ }
93
+ ```
94
+
95
+ ### Pin a message
96
+
97
+ ```json
98
+ {
99
+ "action": "pinMessage",
100
+ "channelId": "C123",
101
+ "messageId": "1712023032.1234"
102
+ }
103
+ ```
104
+
105
+ ### Unpin a message
106
+
107
+ ```json
108
+ {
109
+ "action": "unpinMessage",
110
+ "channelId": "C123",
111
+ "messageId": "1712023032.1234"
112
+ }
113
+ ```
114
+
115
+ ### List pinned items
116
+
117
+ ```json
118
+ {
119
+ "action": "listPins",
120
+ "channelId": "C123"
121
+ }
122
+ ```
123
+
124
+ ### Member info
125
+
126
+ ```json
127
+ {
128
+ "action": "memberInfo",
129
+ "userId": "U123"
130
+ }
131
+ ```
132
+
133
+ ### Emoji list
134
+
135
+ ```json
136
+ {
137
+ "action": "emojiList"
138
+ }
139
+ ```
140
+
141
+ ## Ideas to try
142
+
143
+ - React with ✅ to mark completed tasks.
144
+ - Pin key decisions or weekly status updates.
skills/songsee/SKILL.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: songsee
3
+ description: Generate spectrograms and feature-panel visualizations from audio with the songsee CLI.
4
+ homepage: https://github.com/steipete/songsee
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🌊",
10
+ "requires": { "bins": ["songsee"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "steipete/tap/songsee",
17
+ "bins": ["songsee"],
18
+ "label": "Install songsee (brew)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # songsee
26
+
27
+ Generate spectrograms + feature panels from audio.
28
+
29
+ Quick start
30
+
31
+ - Spectrogram: `songsee track.mp3`
32
+ - Multi-panel: `songsee track.mp3 --viz spectrogram,mel,chroma,hpss,selfsim,loudness,tempogram,mfcc,flux`
33
+ - Time slice: `songsee track.mp3 --start 12.5 --duration 8 -o slice.jpg`
34
+ - Stdin: `cat track.mp3 | songsee - --format png -o out.png`
35
+
36
+ Common flags
37
+
38
+ - `--viz` list (repeatable or comma-separated)
39
+ - `--style` palette (classic, magma, inferno, viridis, gray)
40
+ - `--width` / `--height` output size
41
+ - `--window` / `--hop` FFT settings
42
+ - `--min-freq` / `--max-freq` frequency range
43
+ - `--start` / `--duration` time slice
44
+ - `--format` jpg|png
45
+
46
+ Notes
47
+
48
+ - WAV/MP3 decode native; other formats use ffmpeg if available.
49
+ - Multiple `--viz` renders a grid.
skills/sonoscli/SKILL.md ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: sonoscli
3
+ description: Control Sonos speakers (discover/status/play/volume/group).
4
+ homepage: https://sonoscli.sh
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🔊",
10
+ "requires": { "bins": ["sonos"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "go",
15
+ "kind": "go",
16
+ "module": "github.com/steipete/sonoscli/cmd/sonos@latest",
17
+ "bins": ["sonos"],
18
+ "label": "Install sonoscli (go)",
19
+ },
20
+ ],
21
+ },
22
+ }
23
+ ---
24
+
25
+ # Sonos CLI
26
+
27
+ Use `sonos` to control Sonos speakers on the local network.
28
+
29
+ ## Quick start
30
+
31
+ - `sonos discover`
32
+ - `sonos status --name "Kitchen"`
33
+ - `sonos play|pause|stop --name "Kitchen"`
34
+ - `sonos volume set 15 --name "Kitchen"`
35
+
36
+ ## Common tasks
37
+
38
+ - Grouping: `sonos group status|join|unjoin|party|solo`
39
+ - Favorites: `sonos favorites list|open`
40
+ - Queue: `sonos queue list|play|clear`
41
+ - Spotify search (via SMAPI): `sonos smapi search --service "Spotify" --category tracks "query"`
42
+
43
+ ## Notes
44
+
45
+ - If SSDP fails, specify `--ip <speaker-ip>`.
46
+ - Spotify Web API search is optional and requires `SPOTIFY_CLIENT_ID/SECRET`.
47
+ - If there is an error, check the troubleshooting section and offer advice if there is a decent match.
48
+
49
+ ## Troubleshooting
50
+
51
+ ### `sonos discover` - `no route to host`
52
+
53
+ - 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)`
54
+ - Note that the port number after `0.0.0.0:` is ephemeral and will change and the netmask may not match exactly either
55
+ - The `sendto: no route to host` should stay consistent
56
+ - 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
57
+ - `node` if running via `launchd`
58
+ - `Terminal` if running the gateway directly in the terminal
59
+ - `Visual Studio Code` if running via terminal in VS Code
60
+ - An alternative option is to use `sandbox` (docker container) with network access allowed for that sandbox
61
+
62
+ ### `sonos discover` - `bind: operation not permitted`
63
+
64
+ - On error `Error: listen udp4 0.0.0.0:0: bind: operation not permitted`
65
+ - 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)
skills/spotify-player/SKILL.md ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: spotify-player
3
+ description: Terminal Spotify playback/search via spogo (preferred) or spotify_player.
4
+ homepage: https://www.spotify.com
5
+ metadata:
6
+ {
7
+ "openclaw":
8
+ {
9
+ "emoji": "🎵",
10
+ "requires": { "anyBins": ["spogo", "spotify_player"] },
11
+ "install":
12
+ [
13
+ {
14
+ "id": "brew",
15
+ "kind": "brew",
16
+ "formula": "spogo",
17
+ "tap": "steipete/tap",
18
+ "bins": ["spogo"],
19
+ "label": "Install spogo (brew)",
20
+ },
21
+ {
22
+ "id": "brew",
23
+ "kind": "brew",
24
+ "formula": "spotify_player",
25
+ "bins": ["spotify_player"],
26
+ "label": "Install spotify_player (brew)",
27
+ },
28
+ ],
29
+ },
30
+ }
31
+ ---
32
+
33
+ # spogo / spotify_player
34
+
35
+ Use `spogo` **(preferred)** for Spotify playback/search. Fall back to `spotify_player` if needed.
36
+
37
+ Requirements
38
+
39
+ - Spotify Premium account.
40
+ - Either `spogo` or `spotify_player` installed.
41
+
42
+ spogo setup
43
+
44
+ - Import cookies: `spogo auth import --browser chrome`
45
+
46
+ Common CLI commands
47
+
48
+ - Search: `spogo search track "query"`
49
+ - Playback: `spogo play|pause|next|prev`
50
+ - Devices: `spogo device list`, `spogo device set "<name|id>"`
51
+ - Status: `spogo status`
52
+
53
+ spotify_player commands (fallback)
54
+
55
+ - Search: `spotify_player search "query"`
56
+ - Playback: `spotify_player playback play|pause|next|previous`
57
+ - Connect device: `spotify_player connect`
58
+ - Like track: `spotify_player like`
59
+
60
+ Notes
61
+
62
+ - Config folder: `~/.config/spotify-player` (e.g., `app.toml`).
63
+ - For Spotify Connect integration, set a user `client_id` in config.
64
+ - TUI shortcuts are available via `?` in the app.