@rozek/nanoclaw 1.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/.claude/settings.json +1 -0
  2. package/.claude/skills/add-compact/SKILL.md +135 -0
  3. package/.claude/skills/add-discord/SKILL.md +203 -0
  4. package/.claude/skills/add-gmail/SKILL.md +220 -0
  5. package/.claude/skills/add-image-vision/SKILL.md +94 -0
  6. package/.claude/skills/add-ollama-tool/SKILL.md +153 -0
  7. package/.claude/skills/add-parallel/SKILL.md +290 -0
  8. package/.claude/skills/add-pdf-reader/SKILL.md +104 -0
  9. package/.claude/skills/add-reactions/SKILL.md +117 -0
  10. package/.claude/skills/add-slack/SKILL.md +207 -0
  11. package/.claude/skills/add-telegram/SKILL.md +222 -0
  12. package/.claude/skills/add-telegram-swarm/SKILL.md +384 -0
  13. package/.claude/skills/add-voice-transcription/SKILL.md +148 -0
  14. package/.claude/skills/add-whatsapp/SKILL.md +372 -0
  15. package/.claude/skills/convert-to-apple-container/SKILL.md +175 -0
  16. package/.claude/skills/customize/SKILL.md +110 -0
  17. package/.claude/skills/debug/SKILL.md +349 -0
  18. package/.claude/skills/get-qodo-rules/SKILL.md +122 -0
  19. package/.claude/skills/get-qodo-rules/references/output-format.md +41 -0
  20. package/.claude/skills/get-qodo-rules/references/pagination.md +33 -0
  21. package/.claude/skills/get-qodo-rules/references/repository-scope.md +26 -0
  22. package/.claude/skills/qodo-pr-resolver/SKILL.md +326 -0
  23. package/.claude/skills/qodo-pr-resolver/resources/providers.md +329 -0
  24. package/.claude/skills/setup/SKILL.md +218 -0
  25. package/.claude/skills/update-nanoclaw/SKILL.md +235 -0
  26. package/.claude/skills/update-skills/SKILL.md +130 -0
  27. package/.claude/skills/use-local-whisper/SKILL.md +152 -0
  28. package/.claude/skills/x-integration/SKILL.md +417 -0
  29. package/.claude/skills/x-integration/agent.ts +243 -0
  30. package/.claude/skills/x-integration/host.ts +159 -0
  31. package/.claude/skills/x-integration/lib/browser.ts +148 -0
  32. package/.claude/skills/x-integration/lib/config.ts +62 -0
  33. package/.claude/skills/x-integration/scripts/like.ts +56 -0
  34. package/.claude/skills/x-integration/scripts/post.ts +66 -0
  35. package/.claude/skills/x-integration/scripts/quote.ts +80 -0
  36. package/.claude/skills/x-integration/scripts/reply.ts +74 -0
  37. package/.claude/skills/x-integration/scripts/retweet.ts +62 -0
  38. package/.claude/skills/x-integration/scripts/setup.ts +87 -0
  39. package/.env.example +1 -0
  40. package/.github/CODEOWNERS +10 -0
  41. package/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  42. package/.github/workflows/bump-version.yml +32 -0
  43. package/.github/workflows/ci.yml +25 -0
  44. package/.github/workflows/merge-forward-skills.yml +160 -0
  45. package/.github/workflows/update-tokens.yml +42 -0
  46. package/.husky/pre-commit +1 -0
  47. package/.mcp.json +3 -0
  48. package/.nvmrc +1 -0
  49. package/.prettierrc +3 -0
  50. package/CHANGELOG.md +8 -0
  51. package/CLAUDE.md +64 -0
  52. package/CONTRIBUTING.md +23 -0
  53. package/CONTRIBUTORS.md +15 -0
  54. package/LICENSE +21 -0
  55. package/NanoClaw_with_Web-Support.md +290 -0
  56. package/README.md +261 -0
  57. package/README_zh.md +200 -0
  58. package/assets/nanoclaw-favicon.png +0 -0
  59. package/assets/nanoclaw-icon.png +0 -0
  60. package/assets/nanoclaw-logo-dark.png +0 -0
  61. package/assets/nanoclaw-logo.png +0 -0
  62. package/assets/nanoclaw-profile.jpeg +0 -0
  63. package/assets/nanoclaw-sales.png +0 -0
  64. package/assets/social-preview.jpg +0 -0
  65. package/config-examples/mount-allowlist.json +25 -0
  66. package/container/Dockerfile +70 -0
  67. package/container/agent-runner/package-lock.json +1524 -0
  68. package/container/agent-runner/package.json +21 -0
  69. package/container/agent-runner/src/index.ts +558 -0
  70. package/container/agent-runner/src/ipc-mcp-stdio.ts +338 -0
  71. package/container/agent-runner/tsconfig.json +15 -0
  72. package/container/build.sh +23 -0
  73. package/container/skills/agent-browser/SKILL.md +159 -0
  74. package/container/skills/capabilities/SKILL.md +100 -0
  75. package/container/skills/status/SKILL.md +104 -0
  76. package/dist/channels/index.d.ts +2 -0
  77. package/dist/channels/index.d.ts.map +1 -0
  78. package/dist/channels/index.js +9 -0
  79. package/dist/channels/index.js.map +1 -0
  80. package/dist/channels/registry.d.ts +13 -0
  81. package/dist/channels/registry.d.ts.map +1 -0
  82. package/dist/channels/registry.js +11 -0
  83. package/dist/channels/registry.js.map +1 -0
  84. package/dist/channels/registry.test.d.ts +2 -0
  85. package/dist/channels/registry.test.d.ts.map +1 -0
  86. package/dist/channels/registry.test.js +32 -0
  87. package/dist/channels/registry.test.js.map +1 -0
  88. package/dist/channels/web.d.ts +2 -0
  89. package/dist/channels/web.d.ts.map +1 -0
  90. package/dist/channels/web.js +1738 -0
  91. package/dist/channels/web.js.map +1 -0
  92. package/dist/cli.d.ts +11 -0
  93. package/dist/cli.d.ts.map +1 -0
  94. package/dist/cli.js +182 -0
  95. package/dist/cli.js.map +1 -0
  96. package/dist/config.d.ts +19 -0
  97. package/dist/config.d.ts.map +1 -0
  98. package/dist/config.js +36 -0
  99. package/dist/config.js.map +1 -0
  100. package/dist/container-runner.d.ts +44 -0
  101. package/dist/container-runner.d.ts.map +1 -0
  102. package/dist/container-runner.js +467 -0
  103. package/dist/container-runner.js.map +1 -0
  104. package/dist/container-runner.test.d.ts +2 -0
  105. package/dist/container-runner.test.d.ts.map +1 -0
  106. package/dist/container-runner.test.js +150 -0
  107. package/dist/container-runner.test.js.map +1 -0
  108. package/dist/container-runtime.d.ts +22 -0
  109. package/dist/container-runtime.d.ts.map +1 -0
  110. package/dist/container-runtime.js +96 -0
  111. package/dist/container-runtime.js.map +1 -0
  112. package/dist/container-runtime.test.d.ts +2 -0
  113. package/dist/container-runtime.test.d.ts.map +1 -0
  114. package/dist/container-runtime.test.js +93 -0
  115. package/dist/container-runtime.test.js.map +1 -0
  116. package/dist/credential-proxy.d.ts +21 -0
  117. package/dist/credential-proxy.d.ts.map +1 -0
  118. package/dist/credential-proxy.js +95 -0
  119. package/dist/credential-proxy.js.map +1 -0
  120. package/dist/credential-proxy.test.d.ts +2 -0
  121. package/dist/credential-proxy.test.d.ts.map +1 -0
  122. package/dist/credential-proxy.test.js +134 -0
  123. package/dist/credential-proxy.test.js.map +1 -0
  124. package/dist/db.d.ts +115 -0
  125. package/dist/db.d.ts.map +1 -0
  126. package/dist/db.js +549 -0
  127. package/dist/db.js.map +1 -0
  128. package/dist/db.test.d.ts +2 -0
  129. package/dist/db.test.d.ts.map +1 -0
  130. package/dist/db.test.js +360 -0
  131. package/dist/db.test.js.map +1 -0
  132. package/dist/env.d.ts +8 -0
  133. package/dist/env.d.ts.map +1 -0
  134. package/dist/env.js +42 -0
  135. package/dist/env.js.map +1 -0
  136. package/dist/formatting.test.d.ts +2 -0
  137. package/dist/formatting.test.d.ts.map +1 -0
  138. package/dist/formatting.test.js +183 -0
  139. package/dist/formatting.test.js.map +1 -0
  140. package/dist/group-folder.d.ts +5 -0
  141. package/dist/group-folder.d.ts.map +1 -0
  142. package/dist/group-folder.js +44 -0
  143. package/dist/group-folder.js.map +1 -0
  144. package/dist/group-folder.test.d.ts +2 -0
  145. package/dist/group-folder.test.d.ts.map +1 -0
  146. package/dist/group-folder.test.js +29 -0
  147. package/dist/group-folder.test.js.map +1 -0
  148. package/dist/group-queue.d.ts +34 -0
  149. package/dist/group-queue.d.ts.map +1 -0
  150. package/dist/group-queue.js +263 -0
  151. package/dist/group-queue.js.map +1 -0
  152. package/dist/group-queue.test.d.ts +2 -0
  153. package/dist/group-queue.test.d.ts.map +1 -0
  154. package/dist/group-queue.test.js +341 -0
  155. package/dist/group-queue.test.js.map +1 -0
  156. package/dist/index.d.ts +12 -0
  157. package/dist/index.d.ts.map +1 -0
  158. package/dist/index.js +518 -0
  159. package/dist/index.js.map +1 -0
  160. package/dist/ipc-auth.test.d.ts +2 -0
  161. package/dist/ipc-auth.test.d.ts.map +1 -0
  162. package/dist/ipc-auth.test.js +434 -0
  163. package/dist/ipc-auth.test.js.map +1 -0
  164. package/dist/ipc.d.ts +32 -0
  165. package/dist/ipc.d.ts.map +1 -0
  166. package/dist/ipc.js +311 -0
  167. package/dist/ipc.js.map +1 -0
  168. package/dist/logger.d.ts +3 -0
  169. package/dist/logger.d.ts.map +1 -0
  170. package/dist/logger.js +14 -0
  171. package/dist/logger.js.map +1 -0
  172. package/dist/mount-security.d.ts +34 -0
  173. package/dist/mount-security.d.ts.map +1 -0
  174. package/dist/mount-security.js +325 -0
  175. package/dist/mount-security.js.map +1 -0
  176. package/dist/remote-control.d.ts +32 -0
  177. package/dist/remote-control.d.ts.map +1 -0
  178. package/dist/remote-control.js +185 -0
  179. package/dist/remote-control.js.map +1 -0
  180. package/dist/remote-control.test.d.ts +2 -0
  181. package/dist/remote-control.test.d.ts.map +1 -0
  182. package/dist/remote-control.test.js +321 -0
  183. package/dist/remote-control.test.js.map +1 -0
  184. package/dist/router.d.ts +8 -0
  185. package/dist/router.d.ts.map +1 -0
  186. package/dist/router.js +37 -0
  187. package/dist/router.js.map +1 -0
  188. package/dist/routing.test.d.ts +2 -0
  189. package/dist/routing.test.d.ts.map +1 -0
  190. package/dist/routing.test.js +81 -0
  191. package/dist/routing.test.js.map +1 -0
  192. package/dist/sender-allowlist.d.ts +14 -0
  193. package/dist/sender-allowlist.d.ts.map +1 -0
  194. package/dist/sender-allowlist.js +79 -0
  195. package/dist/sender-allowlist.js.map +1 -0
  196. package/dist/sender-allowlist.test.d.ts +2 -0
  197. package/dist/sender-allowlist.test.d.ts.map +1 -0
  198. package/dist/sender-allowlist.test.js +186 -0
  199. package/dist/sender-allowlist.test.js.map +1 -0
  200. package/dist/session-commands.d.ts +47 -0
  201. package/dist/session-commands.d.ts.map +1 -0
  202. package/dist/session-commands.js +102 -0
  203. package/dist/session-commands.js.map +1 -0
  204. package/dist/session-commands.test.d.ts +2 -0
  205. package/dist/session-commands.test.d.ts.map +1 -0
  206. package/dist/session-commands.test.js +190 -0
  207. package/dist/session-commands.test.js.map +1 -0
  208. package/dist/task-scheduler.d.ts +22 -0
  209. package/dist/task-scheduler.d.ts.map +1 -0
  210. package/dist/task-scheduler.js +210 -0
  211. package/dist/task-scheduler.js.map +1 -0
  212. package/dist/task-scheduler.test.d.ts +2 -0
  213. package/dist/task-scheduler.test.d.ts.map +1 -0
  214. package/dist/task-scheduler.test.js +107 -0
  215. package/dist/task-scheduler.test.js.map +1 -0
  216. package/dist/timezone.d.ts +6 -0
  217. package/dist/timezone.d.ts.map +1 -0
  218. package/dist/timezone.js +17 -0
  219. package/dist/timezone.js.map +1 -0
  220. package/dist/timezone.test.d.ts +2 -0
  221. package/dist/timezone.test.d.ts.map +1 -0
  222. package/dist/timezone.test.js +23 -0
  223. package/dist/timezone.test.js.map +1 -0
  224. package/dist/types.d.ts +78 -0
  225. package/dist/types.d.ts.map +1 -0
  226. package/dist/types.js +2 -0
  227. package/dist/types.js.map +1 -0
  228. package/docs/APPLE-CONTAINER-NETWORKING.md +90 -0
  229. package/docs/DEBUG_CHECKLIST.md +143 -0
  230. package/docs/REQUIREMENTS.md +196 -0
  231. package/docs/SDK_DEEP_DIVE.md +643 -0
  232. package/docs/SECURITY.md +122 -0
  233. package/docs/SPEC.md +785 -0
  234. package/docs/docker-sandboxes.md +359 -0
  235. package/docs/nanoclaw-architecture-final.md +1063 -0
  236. package/docs/nanorepo-architecture.md +168 -0
  237. package/docs/skills-as-branches.md +662 -0
  238. package/groups/global/CLAUDE.md +58 -0
  239. package/groups/main/CLAUDE.md +246 -0
  240. package/launchd/com.nanoclaw.plist +32 -0
  241. package/package.json +45 -0
  242. package/repo-tokens/README.md +113 -0
  243. package/repo-tokens/action.yml +186 -0
  244. package/repo-tokens/badge.svg +23 -0
  245. package/repo-tokens/examples/green.svg +14 -0
  246. package/repo-tokens/examples/red.svg +14 -0
  247. package/repo-tokens/examples/yellow-green.svg +14 -0
  248. package/repo-tokens/examples/yellow.svg +14 -0
  249. package/scripts/run-migrations.ts +105 -0
  250. package/setup/container.ts +144 -0
  251. package/setup/environment.test.ts +121 -0
  252. package/setup/environment.ts +94 -0
  253. package/setup/groups.ts +229 -0
  254. package/setup/index.ts +58 -0
  255. package/setup/mounts.ts +115 -0
  256. package/setup/platform.test.ts +120 -0
  257. package/setup/platform.ts +132 -0
  258. package/setup/register.test.ts +257 -0
  259. package/setup/register.ts +177 -0
  260. package/setup/service.test.ts +187 -0
  261. package/setup/service.ts +362 -0
  262. package/setup/status.ts +16 -0
  263. package/setup/verify.ts +192 -0
  264. package/setup.sh +161 -0
  265. package/src/channels/index.ts +12 -0
  266. package/src/channels/registry.test.ts +42 -0
  267. package/src/channels/registry.ts +32 -0
  268. package/src/channels/web.ts +1856 -0
  269. package/src/cli.ts +209 -0
  270. package/src/config.ts +73 -0
  271. package/src/container-runner.test.ts +210 -0
  272. package/src/container-runner.ts +707 -0
  273. package/src/container-runtime.test.ts +149 -0
  274. package/src/container-runtime.ts +127 -0
  275. package/src/credential-proxy.test.ts +192 -0
  276. package/src/credential-proxy.ts +125 -0
  277. package/src/db.test.ts +484 -0
  278. package/src/db.ts +803 -0
  279. package/src/env.ts +42 -0
  280. package/src/formatting.test.ts +256 -0
  281. package/src/group-folder.test.ts +43 -0
  282. package/src/group-folder.ts +44 -0
  283. package/src/group-queue.test.ts +484 -0
  284. package/src/group-queue.ts +365 -0
  285. package/src/index.ts +731 -0
  286. package/src/ipc-auth.test.ts +679 -0
  287. package/src/ipc.ts +461 -0
  288. package/src/logger.ts +16 -0
  289. package/src/mount-security.ts +419 -0
  290. package/src/remote-control.test.ts +397 -0
  291. package/src/remote-control.ts +224 -0
  292. package/src/router.ts +52 -0
  293. package/src/routing.test.ts +170 -0
  294. package/src/sender-allowlist.test.ts +216 -0
  295. package/src/sender-allowlist.ts +128 -0
  296. package/src/session-commands.test.ts +247 -0
  297. package/src/session-commands.ts +163 -0
  298. package/src/task-scheduler.test.ts +129 -0
  299. package/src/task-scheduler.ts +295 -0
  300. package/src/timezone.test.ts +29 -0
  301. package/src/timezone.ts +16 -0
  302. package/src/types.ts +107 -0
  303. package/tsconfig.json +20 -0
  304. package/vitest.config.ts +7 -0
  305. package/vitest.skills.config.ts +7 -0
@@ -0,0 +1,372 @@
1
+ ---
2
+ name: add-whatsapp
3
+ description: Add WhatsApp as a channel. Can replace other channels entirely or run alongside them. Uses QR code or pairing code for authentication.
4
+ ---
5
+
6
+ # Add WhatsApp Channel
7
+
8
+ This skill adds WhatsApp support to NanoClaw. It installs the WhatsApp channel code, dependencies, and guides through authentication, registration, and configuration.
9
+
10
+ ## Phase 1: Pre-flight
11
+
12
+ ### Check current state
13
+
14
+ Check if WhatsApp is already configured. If `store/auth/` exists with credential files, skip to Phase 4 (Registration) or Phase 5 (Verify).
15
+
16
+ ```bash
17
+ ls store/auth/creds.json 2>/dev/null && echo "WhatsApp auth exists" || echo "No WhatsApp auth"
18
+ ```
19
+
20
+ ### Detect environment
21
+
22
+ Check whether the environment is headless (no display server):
23
+
24
+ ```bash
25
+ [[ -z "$DISPLAY" && -z "$WAYLAND_DISPLAY" && "$OSTYPE" != darwin* ]] && echo "IS_HEADLESS=true" || echo "IS_HEADLESS=false"
26
+ ```
27
+
28
+ ### Ask the user
29
+
30
+ Use `AskUserQuestion` to collect configuration. **Adapt auth options based on environment:**
31
+
32
+ If IS_HEADLESS=true AND not WSL → AskUserQuestion: How do you want to authenticate WhatsApp?
33
+ - **Pairing code** (Recommended) - Enter a numeric code on your phone (no camera needed, requires phone number)
34
+ - **QR code in terminal** - Displays QR code in the terminal (can be too small on some displays)
35
+
36
+ Otherwise (macOS, desktop Linux, or WSL) → AskUserQuestion: How do you want to authenticate WhatsApp?
37
+ - **QR code in browser** (Recommended) - Opens a browser window with a large, scannable QR code
38
+ - **Pairing code** - Enter a numeric code on your phone (no camera needed, requires phone number)
39
+ - **QR code in terminal** - Displays QR code in the terminal (can be too small on some displays)
40
+
41
+ If they chose pairing code:
42
+
43
+ AskUserQuestion: What is your phone number? (Include country code without +, e.g., 1234567890)
44
+
45
+ ## Phase 2: Apply Code Changes
46
+
47
+ Check if `src/channels/whatsapp.ts` already exists. If it does, skip to Phase 3 (Authentication).
48
+
49
+ ### Ensure channel remote
50
+
51
+ ```bash
52
+ git remote -v
53
+ ```
54
+
55
+ If `whatsapp` is missing, add it:
56
+
57
+ ```bash
58
+ git remote add whatsapp https://github.com/qwibitai/nanoclaw-whatsapp.git
59
+ ```
60
+
61
+ ### Merge the skill branch
62
+
63
+ ```bash
64
+ git fetch whatsapp main
65
+ git merge whatsapp/main || {
66
+ git checkout --theirs package-lock.json
67
+ git add package-lock.json
68
+ git merge --continue
69
+ }
70
+ ```
71
+
72
+ This merges in:
73
+ - `src/channels/whatsapp.ts` (WhatsAppChannel class with self-registration via `registerChannel`)
74
+ - `src/channels/whatsapp.test.ts` (41 unit tests)
75
+ - `src/whatsapp-auth.ts` (standalone WhatsApp authentication script)
76
+ - `setup/whatsapp-auth.ts` (WhatsApp auth setup step)
77
+ - `import './whatsapp.js'` appended to the channel barrel file `src/channels/index.ts`
78
+ - `'whatsapp-auth'` step added to `setup/index.ts`
79
+ - `@whiskeysockets/baileys`, `qrcode`, `qrcode-terminal` npm dependencies in `package.json`
80
+ - `ASSISTANT_HAS_OWN_NUMBER` in `.env.example`
81
+
82
+ If the merge reports conflicts, resolve them by reading the conflicted files and understanding the intent of both sides.
83
+
84
+ ### Validate code changes
85
+
86
+ ```bash
87
+ npm install
88
+ npm run build
89
+ npx vitest run src/channels/whatsapp.test.ts
90
+ ```
91
+
92
+ All tests must pass and build must be clean before proceeding.
93
+
94
+ ## Phase 3: Authentication
95
+
96
+ ### Clean previous auth state (if re-authenticating)
97
+
98
+ ```bash
99
+ rm -rf store/auth/
100
+ ```
101
+
102
+ ### Run WhatsApp authentication
103
+
104
+ For QR code in browser (recommended):
105
+
106
+ ```bash
107
+ npx tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
108
+ ```
109
+
110
+ (Bash timeout: 150000ms)
111
+
112
+ Tell the user:
113
+
114
+ > A browser window will open with a QR code.
115
+ >
116
+ > 1. Open WhatsApp > **Settings** > **Linked Devices** > **Link a Device**
117
+ > 2. Scan the QR code in the browser
118
+ > 3. The page will show "Authenticated!" when done
119
+
120
+ For QR code in terminal:
121
+
122
+ ```bash
123
+ npx tsx setup/index.ts --step whatsapp-auth -- --method qr-terminal
124
+ ```
125
+
126
+ Tell the user to run `npm run auth` in another terminal, then:
127
+
128
+ > 1. Open WhatsApp > **Settings** > **Linked Devices** > **Link a Device**
129
+ > 2. Scan the QR code displayed in the terminal
130
+
131
+ For pairing code:
132
+
133
+ Tell the user to have WhatsApp open on **Settings > Linked Devices > Link a Device**, ready to tap **"Link with phone number instead"** — the code expires in ~60 seconds and must be entered immediately.
134
+
135
+ Run the auth process in the background and poll `store/pairing-code.txt` for the code:
136
+
137
+ ```bash
138
+ rm -f store/pairing-code.txt && npx tsx setup/index.ts --step whatsapp-auth -- --method pairing-code --phone <their-phone-number> > /tmp/wa-auth.log 2>&1 &
139
+ ```
140
+
141
+ Then immediately poll for the code (do NOT wait for the background command to finish):
142
+
143
+ ```bash
144
+ for i in $(seq 1 20); do [ -f store/pairing-code.txt ] && cat store/pairing-code.txt && break; sleep 1; done
145
+ ```
146
+
147
+ Display the code to the user the moment it appears. Tell them:
148
+
149
+ > **Enter this code now** — it expires in ~60 seconds.
150
+ >
151
+ > 1. Open WhatsApp > **Settings** > **Linked Devices** > **Link a Device**
152
+ > 2. Tap **Link with phone number instead**
153
+ > 3. Enter the code immediately
154
+
155
+ After the user enters the code, poll for authentication to complete:
156
+
157
+ ```bash
158
+ for i in $(seq 1 60); do grep -q 'AUTH_STATUS: authenticated' /tmp/wa-auth.log 2>/dev/null && echo "authenticated" && break; grep -q 'AUTH_STATUS: failed' /tmp/wa-auth.log 2>/dev/null && echo "failed" && break; sleep 2; done
159
+ ```
160
+
161
+ **If failed:** qr_timeout → re-run. logged_out → delete `store/auth/` and re-run. 515 → re-run. timeout → ask user, offer retry.
162
+
163
+ ### Verify authentication succeeded
164
+
165
+ ```bash
166
+ test -f store/auth/creds.json && echo "Authentication successful" || echo "Authentication failed"
167
+ ```
168
+
169
+ ### Configure environment
170
+
171
+ Channels auto-enable when their credentials are present — WhatsApp activates when `store/auth/creds.json` exists.
172
+
173
+ Sync to container environment:
174
+
175
+ ```bash
176
+ mkdir -p data/env && cp .env data/env/env
177
+ ```
178
+
179
+ ## Phase 4: Registration
180
+
181
+ ### Configure trigger and channel type
182
+
183
+ Get the bot's WhatsApp number: `node -e "const c=require('./store/auth/creds.json');console.log(c.me.id.split(':')[0].split('@')[0])"`
184
+
185
+ AskUserQuestion: Is this a shared phone number (personal WhatsApp) or a dedicated number (separate device)?
186
+ - **Shared number** - Your personal WhatsApp number (recommended: use self-chat or a solo group)
187
+ - **Dedicated number** - A separate phone/SIM for the assistant
188
+
189
+ AskUserQuestion: What trigger word should activate the assistant?
190
+ - **@Andy** - Default trigger
191
+ - **@Claw** - Short and easy
192
+ - **@Claude** - Match the AI name
193
+
194
+ AskUserQuestion: What should the assistant call itself?
195
+ - **Andy** - Default name
196
+ - **Claw** - Short and easy
197
+ - **Claude** - Match the AI name
198
+
199
+ AskUserQuestion: Where do you want to chat with the assistant?
200
+
201
+ **Shared number options:**
202
+ - **Self-chat** (Recommended) - Chat in your own "Message Yourself" conversation
203
+ - **Solo group** - A group with just you and the linked device
204
+ - **Existing group** - An existing WhatsApp group
205
+
206
+ **Dedicated number options:**
207
+ - **DM with bot** (Recommended) - Direct message the bot's number
208
+ - **Solo group** - A group with just you and the bot
209
+ - **Existing group** - An existing WhatsApp group
210
+
211
+ ### Get the JID
212
+
213
+ **Self-chat:** JID = your phone number with `@s.whatsapp.net`. Extract from auth credentials:
214
+
215
+ ```bash
216
+ node -e "const c=JSON.parse(require('fs').readFileSync('store/auth/creds.json','utf-8'));console.log(c.me?.id?.split(':')[0]+'@s.whatsapp.net')"
217
+ ```
218
+
219
+ **DM with bot:** Ask for the bot's phone number. JID = `NUMBER@s.whatsapp.net`
220
+
221
+ **Group (solo, existing):** Run group sync and list available groups:
222
+
223
+ ```bash
224
+ npx tsx setup/index.ts --step groups
225
+ npx tsx setup/index.ts --step groups --list
226
+ ```
227
+
228
+ The output shows `JID|GroupName` pairs. Present candidates as AskUserQuestion (names only, not JIDs).
229
+
230
+ ### Register the chat
231
+
232
+ ```bash
233
+ npx tsx setup/index.ts --step register \
234
+ --jid "<jid>" \
235
+ --name "<chat-name>" \
236
+ --trigger "@<trigger>" \
237
+ --folder "whatsapp_main" \
238
+ --channel whatsapp \
239
+ --assistant-name "<name>" \
240
+ --is-main \
241
+ --no-trigger-required # Only for main/self-chat
242
+ ```
243
+
244
+ For additional groups (trigger-required):
245
+
246
+ ```bash
247
+ npx tsx setup/index.ts --step register \
248
+ --jid "<group-jid>" \
249
+ --name "<group-name>" \
250
+ --trigger "@<trigger>" \
251
+ --folder "whatsapp_<group-name>" \
252
+ --channel whatsapp
253
+ ```
254
+
255
+ ## Phase 5: Verify
256
+
257
+ ### Build and restart
258
+
259
+ ```bash
260
+ npm run build
261
+ ```
262
+
263
+ Restart the service:
264
+
265
+ ```bash
266
+ # macOS (launchd)
267
+ launchctl kickstart -k gui/$(id -u)/com.nanoclaw
268
+
269
+ # Linux (systemd)
270
+ systemctl --user restart nanoclaw
271
+
272
+ # Linux (nohup fallback)
273
+ bash start-nanoclaw.sh
274
+ ```
275
+
276
+ ### Test the connection
277
+
278
+ Tell the user:
279
+
280
+ > Send a message to your registered WhatsApp chat:
281
+ > - For self-chat / main: Any message works
282
+ > - For groups: Use the trigger word (e.g., "@Andy hello")
283
+ >
284
+ > The assistant should respond within a few seconds.
285
+
286
+ ### Check logs if needed
287
+
288
+ ```bash
289
+ tail -f logs/nanoclaw.log
290
+ ```
291
+
292
+ ## Troubleshooting
293
+
294
+ ### QR code expired
295
+
296
+ QR codes expire after ~60 seconds. Re-run the auth command:
297
+
298
+ ```bash
299
+ rm -rf store/auth/ && npx tsx src/whatsapp-auth.ts
300
+ ```
301
+
302
+ ### Pairing code not working
303
+
304
+ Codes expire in ~60 seconds. To retry:
305
+
306
+ ```bash
307
+ rm -rf store/auth/ && npx tsx src/whatsapp-auth.ts --pairing-code --phone <phone>
308
+ ```
309
+
310
+ Enter the code **immediately** when it appears. Also ensure:
311
+ 1. Phone number includes country code without `+` (e.g., `1234567890`)
312
+ 2. Phone has internet access
313
+ 3. WhatsApp is updated to the latest version
314
+
315
+ If pairing code keeps failing, switch to QR-browser auth instead:
316
+
317
+ ```bash
318
+ rm -rf store/auth/ && npx tsx setup/index.ts --step whatsapp-auth -- --method qr-browser
319
+ ```
320
+
321
+ ### "conflict" disconnection
322
+
323
+ This happens when two instances connect with the same credentials. Ensure only one NanoClaw process is running:
324
+
325
+ ```bash
326
+ pkill -f "node dist/index.js"
327
+ # Then restart
328
+ ```
329
+
330
+ ### Bot not responding
331
+
332
+ Check:
333
+ 1. Auth credentials exist: `ls store/auth/creds.json`
334
+ 3. Chat is registered: `sqlite3 store/messages.db "SELECT * FROM registered_groups WHERE jid LIKE '%whatsapp%' OR jid LIKE '%@g.us' OR jid LIKE '%@s.whatsapp.net'"`
335
+ 4. Service is running: `launchctl list | grep nanoclaw` (macOS) or `systemctl --user status nanoclaw` (Linux)
336
+ 5. Logs: `tail -50 logs/nanoclaw.log`
337
+
338
+ ### Group names not showing
339
+
340
+ Run group metadata sync:
341
+
342
+ ```bash
343
+ npx tsx setup/index.ts --step groups
344
+ ```
345
+
346
+ This fetches all group names from WhatsApp. Runs automatically every 24 hours.
347
+
348
+ ## After Setup
349
+
350
+ If running `npm run dev` while the service is active:
351
+
352
+ ```bash
353
+ # macOS:
354
+ launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
355
+ npm run dev
356
+ # When done testing:
357
+ launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
358
+
359
+ # Linux:
360
+ # systemctl --user stop nanoclaw
361
+ # npm run dev
362
+ # systemctl --user start nanoclaw
363
+ ```
364
+
365
+ ## Removal
366
+
367
+ To remove WhatsApp integration:
368
+
369
+ 1. Delete auth credentials: `rm -rf store/auth/`
370
+ 2. Remove WhatsApp registrations: `sqlite3 store/messages.db "DELETE FROM registered_groups WHERE jid LIKE '%@g.us' OR jid LIKE '%@s.whatsapp.net'"`
371
+ 3. Sync env: `mkdir -p data/env && cp .env data/env/env`
372
+ 4. Rebuild and restart: `npm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `npm run build && systemctl --user restart nanoclaw` (Linux)
@@ -0,0 +1,175 @@
1
+ ---
2
+ name: convert-to-apple-container
3
+ description: Switch from Docker to Apple Container for macOS-native container isolation. Use when the user wants Apple Container instead of Docker, or is setting up on macOS and prefers the native runtime. Triggers on "apple container", "convert to apple container", "switch to apple container", or "use apple container".
4
+ ---
5
+
6
+ # Convert to Apple Container
7
+
8
+ This skill switches NanoClaw's container runtime from Docker to Apple Container (macOS-only). It uses the skills engine for deterministic code changes, then walks through verification.
9
+
10
+ **What this changes:**
11
+ - Container runtime binary: `docker` → `container`
12
+ - Mount syntax: `-v path:path:ro` → `--mount type=bind,source=...,target=...,readonly`
13
+ - Startup check: `docker info` → `container system status` (with auto-start)
14
+ - Orphan detection: `docker ps --filter` → `container ls --format json`
15
+ - Build script default: `docker` → `container`
16
+ - Dockerfile entrypoint: `.env` shadowing via `mount --bind` inside the container (Apple Container only supports directory mounts, not file mounts like Docker's `/dev/null` overlay)
17
+ - Container runner: main-group containers start as root for `mount --bind`, then drop privileges via `setpriv`
18
+
19
+ **What stays the same:**
20
+ - Mount security/allowlist validation
21
+ - All exported interfaces and IPC protocol
22
+ - Non-main container behavior (still uses `--user` flag)
23
+ - All other functionality
24
+
25
+ ## Prerequisites
26
+
27
+ Verify Apple Container is installed:
28
+
29
+ ```bash
30
+ container --version && echo "Apple Container ready" || echo "Install Apple Container first"
31
+ ```
32
+
33
+ If not installed:
34
+ - Download from https://github.com/apple/container/releases
35
+ - Install the `.pkg` file
36
+ - Verify: `container --version`
37
+
38
+ Apple Container requires macOS. It does not work on Linux.
39
+
40
+ ## Phase 1: Pre-flight
41
+
42
+ ### Check if already applied
43
+
44
+ ```bash
45
+ grep "CONTAINER_RUNTIME_BIN" src/container-runtime.ts
46
+ ```
47
+
48
+ If it already shows `'container'`, the runtime is already Apple Container. Skip to Phase 3.
49
+
50
+ ## Phase 2: Apply Code Changes
51
+
52
+ ### Ensure upstream remote
53
+
54
+ ```bash
55
+ git remote -v
56
+ ```
57
+
58
+ If `upstream` is missing, add it:
59
+
60
+ ```bash
61
+ git remote add upstream https://github.com/qwibitai/nanoclaw.git
62
+ ```
63
+
64
+ ### Merge the skill branch
65
+
66
+ ```bash
67
+ git fetch upstream skill/apple-container
68
+ git merge upstream/skill/apple-container
69
+ ```
70
+
71
+ This merges in:
72
+ - `src/container-runtime.ts` — Apple Container implementation (replaces Docker)
73
+ - `src/container-runtime.test.ts` — Apple Container-specific tests
74
+ - `src/container-runner.ts` — .env shadow mount fix and privilege dropping
75
+ - `container/Dockerfile` — entrypoint that shadows .env via `mount --bind`
76
+ - `container/build.sh` — default runtime set to `container`
77
+
78
+ If the merge reports conflicts, resolve them by reading the conflicted files and understanding the intent of both sides.
79
+
80
+ ### Validate code changes
81
+
82
+ ```bash
83
+ npm test
84
+ npm run build
85
+ ```
86
+
87
+ All tests must pass and build must be clean before proceeding.
88
+
89
+ ## Phase 3: Verify
90
+
91
+ ### Ensure Apple Container runtime is running
92
+
93
+ ```bash
94
+ container system status || container system start
95
+ ```
96
+
97
+ ### Build the container image
98
+
99
+ ```bash
100
+ ./container/build.sh
101
+ ```
102
+
103
+ ### Test basic execution
104
+
105
+ ```bash
106
+ echo '{}' | container run -i --entrypoint /bin/echo nanoclaw-agent:latest "Container OK"
107
+ ```
108
+
109
+ ### Test readonly mounts
110
+
111
+ ```bash
112
+ mkdir -p /tmp/test-ro && echo "test" > /tmp/test-ro/file.txt
113
+ container run --rm --entrypoint /bin/bash \
114
+ --mount type=bind,source=/tmp/test-ro,target=/test,readonly \
115
+ nanoclaw-agent:latest \
116
+ -c "cat /test/file.txt && touch /test/new.txt 2>&1 || echo 'Write blocked (expected)'"
117
+ rm -rf /tmp/test-ro
118
+ ```
119
+
120
+ Expected: Read succeeds, write fails with "Read-only file system".
121
+
122
+ ### Test read-write mounts
123
+
124
+ ```bash
125
+ mkdir -p /tmp/test-rw
126
+ container run --rm --entrypoint /bin/bash \
127
+ -v /tmp/test-rw:/test \
128
+ nanoclaw-agent:latest \
129
+ -c "echo 'test write' > /test/new.txt && cat /test/new.txt"
130
+ cat /tmp/test-rw/new.txt && rm -rf /tmp/test-rw
131
+ ```
132
+
133
+ Expected: Both operations succeed.
134
+
135
+ ### Full integration test
136
+
137
+ ```bash
138
+ npm run build
139
+ launchctl kickstart -k gui/$(id -u)/com.nanoclaw
140
+ ```
141
+
142
+ Send a message via WhatsApp and verify the agent responds.
143
+
144
+ ## Troubleshooting
145
+
146
+ **Apple Container not found:**
147
+ - Download from https://github.com/apple/container/releases
148
+ - Install the `.pkg` file
149
+ - Verify: `container --version`
150
+
151
+ **Runtime won't start:**
152
+ ```bash
153
+ container system start
154
+ container system status
155
+ ```
156
+
157
+ **Image build fails:**
158
+ ```bash
159
+ # Clean rebuild — Apple Container caches aggressively
160
+ container builder stop && container builder rm && container builder start
161
+ ./container/build.sh
162
+ ```
163
+
164
+ **Container can't write to mounted directories:**
165
+ Check directory permissions on the host. The container runs as uid 1000.
166
+
167
+ ## Summary of Changed Files
168
+
169
+ | File | Type of Change |
170
+ |------|----------------|
171
+ | `src/container-runtime.ts` | Full replacement — Docker → Apple Container API |
172
+ | `src/container-runtime.test.ts` | Full replacement — tests for Apple Container behavior |
173
+ | `src/container-runner.ts` | .env shadow mount removed, main containers start as root with privilege drop |
174
+ | `container/Dockerfile` | Entrypoint: `mount --bind` for .env shadowing, `setpriv` privilege drop |
175
+ | `container/build.sh` | Default runtime: `docker` → `container` |
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: customize
3
+ description: Add new capabilities or modify NanoClaw behavior. Use when user wants to add channels (Telegram, Slack, email input), change triggers, add integrations, modify the router, or make any other customizations. This is an interactive skill that asks questions to understand what the user wants.
4
+ ---
5
+
6
+ # NanoClaw Customization
7
+
8
+ This skill helps users add capabilities or modify behavior. Use AskUserQuestion to understand what they want before making changes.
9
+
10
+ ## Workflow
11
+
12
+ 1. **Understand the request** - Ask clarifying questions
13
+ 3. **Plan the changes** - Identify files to modify. If a skill exists for the request (e.g., `/add-telegram` for adding Telegram), invoke it instead of implementing manually.
14
+ 4. **Implement** - Make changes directly to the code
15
+ 5. **Test guidance** - Tell user how to verify
16
+
17
+ ## Key Files
18
+
19
+ | File | Purpose |
20
+ |------|---------|
21
+ | `src/index.ts` | Orchestrator: state, message loop, agent invocation |
22
+ | `src/channels/whatsapp.ts` | WhatsApp connection, auth, send/receive |
23
+ | `src/ipc.ts` | IPC watcher and task processing |
24
+ | `src/router.ts` | Message formatting and outbound routing |
25
+ | `src/types.ts` | TypeScript interfaces (includes Channel) |
26
+ | `src/config.ts` | Assistant name, trigger pattern, directories |
27
+ | `src/db.ts` | Database initialization and queries |
28
+ | `src/whatsapp-auth.ts` | Standalone WhatsApp authentication script |
29
+ | `groups/CLAUDE.md` | Global memory/persona |
30
+
31
+ ## Common Customization Patterns
32
+
33
+ ### Adding a New Input Channel (e.g., Telegram, Slack, Email)
34
+
35
+ Questions to ask:
36
+ - Which channel? (Telegram, Slack, Discord, email, SMS, etc.)
37
+ - Same trigger word or different?
38
+ - Same memory hierarchy or separate?
39
+ - Should messages from this channel go to existing groups or new ones?
40
+
41
+ Implementation pattern:
42
+ 1. Create `src/channels/{name}.ts` implementing the `Channel` interface from `src/types.ts` (see `src/channels/whatsapp.ts` for reference)
43
+ 2. Add the channel instance to `main()` in `src/index.ts` and wire callbacks (`onMessage`, `onChatMetadata`)
44
+ 3. Messages are stored via the `onMessage` callback; routing is automatic via `ownsJid()`
45
+
46
+ ### Adding a New MCP Integration
47
+
48
+ Questions to ask:
49
+ - What service? (Calendar, Notion, database, etc.)
50
+ - What operations needed? (read, write, both)
51
+ - Which groups should have access?
52
+
53
+ Implementation:
54
+ 1. Add MCP server config to the container settings (see `src/container-runner.ts` for how MCP servers are mounted)
55
+ 2. Document available tools in `groups/CLAUDE.md`
56
+
57
+ ### Changing Assistant Behavior
58
+
59
+ Questions to ask:
60
+ - What aspect? (name, trigger, persona, response style)
61
+ - Apply to all groups or specific ones?
62
+
63
+ Simple changes → edit `src/config.ts`
64
+ Persona changes → edit `groups/CLAUDE.md`
65
+ Per-group behavior → edit specific group's `CLAUDE.md`
66
+
67
+ ### Adding New Commands
68
+
69
+ Questions to ask:
70
+ - What should the command do?
71
+ - Available in all groups or main only?
72
+ - Does it need new MCP tools?
73
+
74
+ Implementation:
75
+ 1. Commands are handled by the agent naturally — add instructions to `groups/CLAUDE.md` or the group's `CLAUDE.md`
76
+ 2. For trigger-level routing changes, modify `processGroupMessages()` in `src/index.ts`
77
+
78
+ ### Changing Deployment
79
+
80
+ Questions to ask:
81
+ - Target platform? (Linux server, Docker, different Mac)
82
+ - Service manager? (systemd, Docker, supervisord)
83
+
84
+ Implementation:
85
+ 1. Create appropriate service files
86
+ 2. Update paths in config
87
+ 3. Provide setup instructions
88
+
89
+ ## After Changes
90
+
91
+ Always tell the user:
92
+ ```bash
93
+ # Rebuild and restart
94
+ npm run build
95
+ # macOS:
96
+ launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
97
+ launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
98
+ # Linux:
99
+ # systemctl --user restart nanoclaw
100
+ ```
101
+
102
+ ## Example Interaction
103
+
104
+ User: "Add Telegram as an input channel"
105
+
106
+ 1. Ask: "Should Telegram use the same @Andy trigger, or a different one?"
107
+ 2. Ask: "Should Telegram messages create separate conversation contexts, or share with WhatsApp groups?"
108
+ 3. Create `src/channels/telegram.ts` implementing the `Channel` interface (see `src/channels/whatsapp.ts`)
109
+ 4. Add the channel to `main()` in `src/index.ts`
110
+ 5. Tell user how to authenticate and test