@rozek/nanoclaw 0.0.1

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 (306) 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 +325 -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.json +21 -0
  68. package/container/agent-runner/src/index.ts +774 -0
  69. package/container/agent-runner/src/ipc-mcp-stdio.ts +338 -0
  70. package/container/agent-runner/tsconfig.json +15 -0
  71. package/container/build.sh +23 -0
  72. package/container/skills/agent-browser/SKILL.md +159 -0
  73. package/container/skills/capabilities/SKILL.md +100 -0
  74. package/container/skills/cwd/SKILL.md +32 -0
  75. package/container/skills/pwd/SKILL.md +19 -0
  76. package/container/skills/status/SKILL.md +104 -0
  77. package/dist/channels/index.d.ts +2 -0
  78. package/dist/channels/index.d.ts.map +1 -0
  79. package/dist/channels/index.js +10 -0
  80. package/dist/channels/index.js.map +1 -0
  81. package/dist/channels/registry.d.ts +13 -0
  82. package/dist/channels/registry.d.ts.map +1 -0
  83. package/dist/channels/registry.js +11 -0
  84. package/dist/channels/registry.js.map +1 -0
  85. package/dist/channels/registry.test.d.ts +2 -0
  86. package/dist/channels/registry.test.d.ts.map +1 -0
  87. package/dist/channels/registry.test.js +32 -0
  88. package/dist/channels/registry.test.js.map +1 -0
  89. package/dist/channels/web.d.ts +2 -0
  90. package/dist/channels/web.d.ts.map +1 -0
  91. package/dist/channels/web.js +1843 -0
  92. package/dist/channels/web.js.map +1 -0
  93. package/dist/cli.d.ts +11 -0
  94. package/dist/cli.d.ts.map +1 -0
  95. package/dist/cli.js +182 -0
  96. package/dist/cli.js.map +1 -0
  97. package/dist/config.d.ts +19 -0
  98. package/dist/config.d.ts.map +1 -0
  99. package/dist/config.js +36 -0
  100. package/dist/config.js.map +1 -0
  101. package/dist/container-runner.d.ts +44 -0
  102. package/dist/container-runner.d.ts.map +1 -0
  103. package/dist/container-runner.js +511 -0
  104. package/dist/container-runner.js.map +1 -0
  105. package/dist/container-runner.test.d.ts +2 -0
  106. package/dist/container-runner.test.d.ts.map +1 -0
  107. package/dist/container-runner.test.js +150 -0
  108. package/dist/container-runner.test.js.map +1 -0
  109. package/dist/container-runtime.d.ts +22 -0
  110. package/dist/container-runtime.d.ts.map +1 -0
  111. package/dist/container-runtime.js +96 -0
  112. package/dist/container-runtime.js.map +1 -0
  113. package/dist/container-runtime.test.d.ts +2 -0
  114. package/dist/container-runtime.test.d.ts.map +1 -0
  115. package/dist/container-runtime.test.js +93 -0
  116. package/dist/container-runtime.test.js.map +1 -0
  117. package/dist/credential-proxy.d.ts +21 -0
  118. package/dist/credential-proxy.d.ts.map +1 -0
  119. package/dist/credential-proxy.js +95 -0
  120. package/dist/credential-proxy.js.map +1 -0
  121. package/dist/credential-proxy.test.d.ts +2 -0
  122. package/dist/credential-proxy.test.d.ts.map +1 -0
  123. package/dist/credential-proxy.test.js +134 -0
  124. package/dist/credential-proxy.test.js.map +1 -0
  125. package/dist/db.d.ts +115 -0
  126. package/dist/db.d.ts.map +1 -0
  127. package/dist/db.js +549 -0
  128. package/dist/db.js.map +1 -0
  129. package/dist/db.test.d.ts +2 -0
  130. package/dist/db.test.d.ts.map +1 -0
  131. package/dist/db.test.js +360 -0
  132. package/dist/db.test.js.map +1 -0
  133. package/dist/env.d.ts +8 -0
  134. package/dist/env.d.ts.map +1 -0
  135. package/dist/env.js +42 -0
  136. package/dist/env.js.map +1 -0
  137. package/dist/formatting.test.d.ts +2 -0
  138. package/dist/formatting.test.d.ts.map +1 -0
  139. package/dist/formatting.test.js +183 -0
  140. package/dist/formatting.test.js.map +1 -0
  141. package/dist/group-folder.d.ts +5 -0
  142. package/dist/group-folder.d.ts.map +1 -0
  143. package/dist/group-folder.js +44 -0
  144. package/dist/group-folder.js.map +1 -0
  145. package/dist/group-folder.test.d.ts +2 -0
  146. package/dist/group-folder.test.d.ts.map +1 -0
  147. package/dist/group-folder.test.js +29 -0
  148. package/dist/group-folder.test.js.map +1 -0
  149. package/dist/group-queue.d.ts +40 -0
  150. package/dist/group-queue.d.ts.map +1 -0
  151. package/dist/group-queue.js +276 -0
  152. package/dist/group-queue.js.map +1 -0
  153. package/dist/group-queue.test.d.ts +2 -0
  154. package/dist/group-queue.test.d.ts.map +1 -0
  155. package/dist/group-queue.test.js +341 -0
  156. package/dist/group-queue.test.js.map +1 -0
  157. package/dist/index.d.ts +13 -0
  158. package/dist/index.d.ts.map +1 -0
  159. package/dist/index.js +592 -0
  160. package/dist/index.js.map +1 -0
  161. package/dist/ipc-auth.test.d.ts +2 -0
  162. package/dist/ipc-auth.test.d.ts.map +1 -0
  163. package/dist/ipc-auth.test.js +434 -0
  164. package/dist/ipc-auth.test.js.map +1 -0
  165. package/dist/ipc.d.ts +32 -0
  166. package/dist/ipc.d.ts.map +1 -0
  167. package/dist/ipc.js +311 -0
  168. package/dist/ipc.js.map +1 -0
  169. package/dist/logger.d.ts +3 -0
  170. package/dist/logger.d.ts.map +1 -0
  171. package/dist/logger.js +14 -0
  172. package/dist/logger.js.map +1 -0
  173. package/dist/mount-security.d.ts +34 -0
  174. package/dist/mount-security.d.ts.map +1 -0
  175. package/dist/mount-security.js +325 -0
  176. package/dist/mount-security.js.map +1 -0
  177. package/dist/remote-control.d.ts +32 -0
  178. package/dist/remote-control.d.ts.map +1 -0
  179. package/dist/remote-control.js +185 -0
  180. package/dist/remote-control.js.map +1 -0
  181. package/dist/remote-control.test.d.ts +2 -0
  182. package/dist/remote-control.test.d.ts.map +1 -0
  183. package/dist/remote-control.test.js +321 -0
  184. package/dist/remote-control.test.js.map +1 -0
  185. package/dist/router.d.ts +8 -0
  186. package/dist/router.d.ts.map +1 -0
  187. package/dist/router.js +37 -0
  188. package/dist/router.js.map +1 -0
  189. package/dist/routing.test.d.ts +2 -0
  190. package/dist/routing.test.d.ts.map +1 -0
  191. package/dist/routing.test.js +81 -0
  192. package/dist/routing.test.js.map +1 -0
  193. package/dist/sender-allowlist.d.ts +14 -0
  194. package/dist/sender-allowlist.d.ts.map +1 -0
  195. package/dist/sender-allowlist.js +79 -0
  196. package/dist/sender-allowlist.js.map +1 -0
  197. package/dist/sender-allowlist.test.d.ts +2 -0
  198. package/dist/sender-allowlist.test.d.ts.map +1 -0
  199. package/dist/sender-allowlist.test.js +186 -0
  200. package/dist/sender-allowlist.test.js.map +1 -0
  201. package/dist/session-commands.d.ts +47 -0
  202. package/dist/session-commands.d.ts.map +1 -0
  203. package/dist/session-commands.js +104 -0
  204. package/dist/session-commands.js.map +1 -0
  205. package/dist/session-commands.test.d.ts +2 -0
  206. package/dist/session-commands.test.d.ts.map +1 -0
  207. package/dist/session-commands.test.js +194 -0
  208. package/dist/session-commands.test.js.map +1 -0
  209. package/dist/task-scheduler.d.ts +22 -0
  210. package/dist/task-scheduler.d.ts.map +1 -0
  211. package/dist/task-scheduler.js +241 -0
  212. package/dist/task-scheduler.js.map +1 -0
  213. package/dist/task-scheduler.test.d.ts +2 -0
  214. package/dist/task-scheduler.test.d.ts.map +1 -0
  215. package/dist/task-scheduler.test.js +107 -0
  216. package/dist/task-scheduler.test.js.map +1 -0
  217. package/dist/timezone.d.ts +6 -0
  218. package/dist/timezone.d.ts.map +1 -0
  219. package/dist/timezone.js +17 -0
  220. package/dist/timezone.js.map +1 -0
  221. package/dist/timezone.test.d.ts +2 -0
  222. package/dist/timezone.test.d.ts.map +1 -0
  223. package/dist/timezone.test.js +23 -0
  224. package/dist/timezone.test.js.map +1 -0
  225. package/dist/types.d.ts +79 -0
  226. package/dist/types.d.ts.map +1 -0
  227. package/dist/types.js +2 -0
  228. package/dist/types.js.map +1 -0
  229. package/docs/APPLE-CONTAINER-NETWORKING.md +90 -0
  230. package/docs/DEBUG_CHECKLIST.md +143 -0
  231. package/docs/REQUIREMENTS.md +196 -0
  232. package/docs/SDK_DEEP_DIVE.md +643 -0
  233. package/docs/SECURITY.md +122 -0
  234. package/docs/SPEC.md +785 -0
  235. package/docs/docker-sandboxes.md +359 -0
  236. package/docs/nanoclaw-architecture-final.md +1063 -0
  237. package/docs/nanorepo-architecture.md +168 -0
  238. package/docs/skills-as-branches.md +662 -0
  239. package/groups/global/CLAUDE.md +58 -0
  240. package/groups/main/CLAUDE.md +246 -0
  241. package/launchd/com.nanoclaw.plist +32 -0
  242. package/package.json +45 -0
  243. package/repo-tokens/README.md +113 -0
  244. package/repo-tokens/action.yml +186 -0
  245. package/repo-tokens/badge.svg +23 -0
  246. package/repo-tokens/examples/green.svg +14 -0
  247. package/repo-tokens/examples/red.svg +14 -0
  248. package/repo-tokens/examples/yellow-green.svg +14 -0
  249. package/repo-tokens/examples/yellow.svg +14 -0
  250. package/scripts/run-migrations.ts +105 -0
  251. package/setup/container.ts +144 -0
  252. package/setup/environment.test.ts +121 -0
  253. package/setup/environment.ts +94 -0
  254. package/setup/groups.ts +229 -0
  255. package/setup/index.ts +58 -0
  256. package/setup/mounts.ts +115 -0
  257. package/setup/platform.test.ts +120 -0
  258. package/setup/platform.ts +132 -0
  259. package/setup/register.test.ts +257 -0
  260. package/setup/register.ts +177 -0
  261. package/setup/service.test.ts +187 -0
  262. package/setup/service.ts +362 -0
  263. package/setup/status.ts +16 -0
  264. package/setup/verify.ts +192 -0
  265. package/setup.sh +161 -0
  266. package/src/channels/index.ts +15 -0
  267. package/src/channels/registry.test.ts +42 -0
  268. package/src/channels/registry.ts +32 -0
  269. package/src/channels/web.ts +1931 -0
  270. package/src/cli.ts +209 -0
  271. package/src/config.ts +73 -0
  272. package/src/container-runner.test.ts +210 -0
  273. package/src/container-runner.ts +768 -0
  274. package/src/container-runtime.test.ts +149 -0
  275. package/src/container-runtime.ts +127 -0
  276. package/src/credential-proxy.test.ts +192 -0
  277. package/src/credential-proxy.ts +125 -0
  278. package/src/db.test.ts +484 -0
  279. package/src/db.ts +803 -0
  280. package/src/env.ts +42 -0
  281. package/src/formatting.test.ts +256 -0
  282. package/src/group-folder.test.ts +43 -0
  283. package/src/group-folder.ts +44 -0
  284. package/src/group-queue.test.ts +484 -0
  285. package/src/group-queue.ts +379 -0
  286. package/src/index.ts +832 -0
  287. package/src/ipc-auth.test.ts +679 -0
  288. package/src/ipc.ts +461 -0
  289. package/src/logger.ts +16 -0
  290. package/src/mount-security.ts +419 -0
  291. package/src/remote-control.test.ts +397 -0
  292. package/src/remote-control.ts +224 -0
  293. package/src/router.ts +52 -0
  294. package/src/routing.test.ts +170 -0
  295. package/src/sender-allowlist.test.ts +216 -0
  296. package/src/sender-allowlist.ts +128 -0
  297. package/src/session-commands.test.ts +247 -0
  298. package/src/session-commands.ts +163 -0
  299. package/src/task-scheduler.test.ts +129 -0
  300. package/src/task-scheduler.ts +328 -0
  301. package/src/timezone.test.ts +29 -0
  302. package/src/timezone.ts +16 -0
  303. package/src/types.ts +109 -0
  304. package/tsconfig.json +20 -0
  305. package/vitest.config.ts +7 -0
  306. package/vitest.skills.config.ts +7 -0
@@ -0,0 +1,1063 @@
1
+ # NanoClaw Skills Architecture
2
+
3
+ ## Core Principle
4
+
5
+ Skills are self-contained, auditable packages that apply programmatically via standard git merge mechanics. Claude Code orchestrates the process — running git commands, reading skill manifests, and stepping in only when git can't resolve a conflict on its own. The system uses existing git features (`merge-file`, `rerere`, `apply`) rather than custom merge infrastructure.
6
+
7
+ ### The Three-Level Resolution Model
8
+
9
+ Every operation in the system follows this escalation:
10
+
11
+ 1. **Git** — deterministic, programmatic. `git merge-file` merges, `git rerere` replays cached resolutions, structured operations apply without merging. No AI involved. This handles the vast majority of cases.
12
+ 2. **Claude Code** — reads `SKILL.md`, `.intent.md`, migration guides, and `state.yaml` to understand context. Resolves conflicts that git can't handle programmatically. Caches the resolution via `git rerere` so it never needs to resolve the same conflict again.
13
+ 3. **User** — Claude Code asks the user when it lacks context or intent. This happens when two features genuinely conflict at an application level (not just a text-level merge conflict) and a human decision is needed about desired behavior.
14
+
15
+ The goal is that Level 1 handles everything on a mature, well-tested installation. Level 2 handles first-time conflicts and edge cases. Level 3 is rare and only for genuine ambiguity.
16
+
17
+ **Important**: a clean merge (exit code 0) does not guarantee working code. Semantic conflicts — a renamed variable, a shifted reference, a changed function signature — can produce clean text merges that break at runtime. **Tests must run after every operation**, regardless of whether the merge was clean. A clean merge with failing tests escalates to Level 2.
18
+
19
+ ### Safe Operations via Backup/Restore
20
+
21
+ Many users clone the repo without forking, don't commit their changes, and don't think of themselves as git users. The system must work safely for them without requiring any git knowledge.
22
+
23
+ Before any operation, the system copies all files that will be modified to `.nanoclaw/backup/`. On success, the backup is deleted. On failure, the backup is restored. This provides rollback safety regardless of whether the user commits, pushes, or understands git.
24
+
25
+ ---
26
+
27
+ ## 1. The Shared Base
28
+
29
+ `.nanoclaw/base/` holds the clean core — the original codebase before any skills or customizations were applied. This is the stable common ancestor for all three-way merges, and it only changes on core updates.
30
+
31
+ - `git merge-file` uses the base to compute two diffs: what the user changed (current vs base) and what the skill wants to change (base vs skill's modified file), then combines both
32
+ - The base enables drift detection: if a file's hash differs from its base hash, something has been modified (skills, user customizations, or both)
33
+ - Each skill's `modify/` files contain the full file as it should look with that skill applied (including any prerequisite skill changes), all authored against the same clean core base
34
+
35
+ On a **fresh codebase**, the user's files are identical to the base. This means `git merge-file` always exits cleanly for the first skill — the merge trivially produces the skill's modified version. No special-casing needed.
36
+
37
+ When multiple skills modify the same file, the three-way merge handles the overlap naturally. If Telegram and Discord both modify `src/index.ts`, and both skill files include the Telegram changes, those common changes merge cleanly against the base. The result is the base + all skill changes + user customizations.
38
+
39
+ ---
40
+
41
+ ## 2. Two Types of Changes: Code Merges vs. Structured Operations
42
+
43
+ Not all files should be merged as text. The system distinguishes between **code files** (merged via `git merge-file`) and **structured data** (modified via deterministic operations).
44
+
45
+ ### Code Files (Three-Way Merge)
46
+
47
+ Source code files where skills weave in logic — route handlers, middleware, business logic. These are merged using `git merge-file` against the shared base. The skill carries a full modified version of the file.
48
+
49
+ ### Structured Data (Deterministic Operations)
50
+
51
+ Files like `package.json`, `docker-compose.yml`, `.env.example`, and generated configs are not code you merge — they're structured data you aggregate. Multiple skills adding npm dependencies to `package.json` shouldn't require a three-way text merge. Instead, skills declare their structured requirements in the manifest, and the system applies them programmatically.
52
+
53
+ **Structured operations are implicit.** If a skill declares `npm_dependencies`, the system handles dependency installation automatically. There is no need for the skill author to add `npm install` to `post_apply`. When multiple skills are applied in sequence, the system batches structured operations: merge all dependency declarations first, write `package.json` once, run `npm install` once at the end.
54
+
55
+ ```yaml
56
+ # In manifest.yaml
57
+ structured:
58
+ npm_dependencies:
59
+ whatsapp-web.js: "^2.1.0"
60
+ qrcode-terminal: "^0.12.0"
61
+ env_additions:
62
+ - WHATSAPP_TOKEN
63
+ - WHATSAPP_VERIFY_TOKEN
64
+ - WHATSAPP_PHONE_ID
65
+ docker_compose_services:
66
+ whatsapp-redis:
67
+ image: redis:alpine
68
+ ports: ["6380:6379"]
69
+ ```
70
+
71
+ ### Structured Operation Conflicts
72
+
73
+ Structured operations eliminate text merge conflicts but can still conflict at a semantic level:
74
+
75
+ - **NPM version conflicts**: two skills request incompatible semver ranges for the same package
76
+ - **Port collisions**: two docker-compose services claim the same host port
77
+ - **Service name collisions**: two skills define a service with the same name
78
+ - **Env var duplicates**: two skills declare the same variable with different expectations
79
+
80
+ The resolution policy:
81
+
82
+ 1. **Automatic where possible**: widen semver ranges to find a compatible version, detect and flag port/name collisions
83
+ 2. **Level 2 (Claude Code)**: if automatic resolution fails, Claude proposes options based on skill intents
84
+ 3. **Level 3 (User)**: if it's a genuine product choice (which Redis instance should get port 6379?), ask the user
85
+
86
+ Structured operation conflicts are included in the CI overlap graph alongside code file overlaps, so the maintainer test matrix catches these before users encounter them.
87
+
88
+ ### State Records Structured Outcomes
89
+
90
+ `state.yaml` records not just the declared dependencies but the resolved outcomes — actual installed versions, resolved port assignments, final env var list. This makes structured operations replayable and auditable.
91
+
92
+ ### Deterministic Serialization
93
+
94
+ All structured output (YAML, JSON) uses stable serialization: sorted keys, consistent quoting, normalized whitespace. This prevents noisy diffs in git history from non-functional formatting changes.
95
+
96
+ ---
97
+
98
+ ## 3. Skill Package Structure
99
+
100
+ A skill contains only the files it adds or modifies. For modified code files, the skill carries the **full modified file** (the clean core with the skill's changes applied).
101
+
102
+ ```
103
+ skills/
104
+ add-whatsapp/
105
+ SKILL.md # Context, intent, what this skill does and why
106
+ manifest.yaml # Metadata, dependencies, env vars, post-apply steps
107
+ tests/ # Integration tests for this skill
108
+ whatsapp.test.ts
109
+ add/ # New files — copied directly
110
+ src/channels/whatsapp.ts
111
+ src/channels/whatsapp.config.ts
112
+ modify/ # Modified code files — merged via git merge-file
113
+ src/
114
+ server.ts # Full file: clean core + whatsapp changes
115
+ server.ts.intent.md # "Adds WhatsApp webhook route and message handler"
116
+ config.ts # Full file: clean core + whatsapp config options
117
+ config.ts.intent.md # "Adds WhatsApp channel configuration block"
118
+ ```
119
+
120
+ ### Why Full Modified Files
121
+
122
+ - `git merge-file` requires three full files — no intermediate reconstruction step
123
+ - Git's three-way merge uses context matching, so it works even if the user has moved code around — unlike line-number-based diffs that break immediately
124
+ - Auditable: `diff .nanoclaw/base/src/server.ts skills/add-whatsapp/modify/src/server.ts` shows exactly what the skill changes
125
+ - Deterministic: same three inputs always produce the same merge result
126
+ - Size is negligible since NanoClaw's core files are small
127
+
128
+ ### Intent Files
129
+
130
+ Each modified code file has a corresponding `.intent.md` with structured headings:
131
+
132
+ ```markdown
133
+ # Intent: server.ts modifications
134
+
135
+ ## What this skill adds
136
+ Adds WhatsApp webhook route and message handler to the Express server.
137
+
138
+ ## Key sections
139
+ - Route registration at `/webhook/whatsapp` (POST and GET for verification)
140
+ - Message handler middleware between auth and response pipeline
141
+
142
+ ## Invariants
143
+ - Must not interfere with other channel webhook routes
144
+ - Auth middleware must run before the WhatsApp handler
145
+ - Error handling must propagate to the global error handler
146
+
147
+ ## Must-keep sections
148
+ - The webhook verification flow (GET route) is required by WhatsApp Cloud API
149
+ ```
150
+
151
+ Structured headings (What, Key sections, Invariants, Must-keep) give Claude Code specific guidance during conflict resolution instead of requiring it to infer from unstructured text.
152
+
153
+ ### Manifest Format
154
+
155
+ ```yaml
156
+ # --- Required fields ---
157
+ skill: whatsapp
158
+ version: 1.2.0
159
+ description: "WhatsApp Business API integration via Cloud API"
160
+ core_version: 0.1.0 # The core version this skill was authored against
161
+
162
+ # Files this skill adds
163
+ adds:
164
+ - src/channels/whatsapp.ts
165
+ - src/channels/whatsapp.config.ts
166
+
167
+ # Code files this skill modifies (three-way merge)
168
+ modifies:
169
+ - src/server.ts
170
+ - src/config.ts
171
+
172
+ # File operations (renames, deletes, moves — see Section 5)
173
+ file_ops: []
174
+
175
+ # Structured operations (deterministic, no merge — implicit handling)
176
+ structured:
177
+ npm_dependencies:
178
+ whatsapp-web.js: "^2.1.0"
179
+ qrcode-terminal: "^0.12.0"
180
+ env_additions:
181
+ - WHATSAPP_TOKEN
182
+ - WHATSAPP_VERIFY_TOKEN
183
+ - WHATSAPP_PHONE_ID
184
+
185
+ # Skill relationships
186
+ conflicts: [] # Skills that cannot coexist without agent resolution
187
+ depends: [] # Skills that must be applied first
188
+
189
+ # Test command — runs after apply to validate the skill works
190
+ test: "npx vitest run src/channels/whatsapp.test.ts"
191
+
192
+ # --- Future fields (not yet implemented in v0.1) ---
193
+ # author: nanoclaw-team
194
+ # license: MIT
195
+ # min_skills_system_version: "0.1.0"
196
+ # tested_with: [telegram@1.0.0]
197
+ # post_apply: []
198
+ ```
199
+
200
+ Note: `post_apply` is only for operations that can't be expressed as structured declarations. Dependency installation is **never** in `post_apply` — it's handled implicitly by the structured operations system.
201
+
202
+ ---
203
+
204
+ ## 4. Skills, Customization, and Layering
205
+
206
+ ### One Skill, One Happy Path
207
+
208
+ A skill implements **one way of doing something — the reasonable default that covers 80% of users.** `add-telegram` gives you a clean, solid Telegram integration. It doesn't try to anticipate every use case with predefined configuration options and modes.
209
+
210
+ ### Customization Is Just More Patching
211
+
212
+ The entire system is built around applying transformations to a codebase. Customizing a skill after applying it is no different from any other modification:
213
+
214
+ - **Apply the skill** — get the standard Telegram integration
215
+ - **Modify from there** — using the customize flow (tracked patch), direct editing (detected by hash tracking), or by applying additional skills that build on top
216
+
217
+ ### Layered Skills
218
+
219
+ Skills can build on other skills:
220
+
221
+ ```
222
+ add-telegram # Core Telegram integration (happy path)
223
+ ├── telegram-reactions # Adds reaction handling (depends: [telegram])
224
+ ├── telegram-multi-bot # Multiple bot instances (depends: [telegram])
225
+ └── telegram-filters # Custom message filtering (depends: [telegram])
226
+ ```
227
+
228
+ Each layer is a separate skill with its own `SKILL.md`, manifest (with `depends: [telegram]`), tests, and modified files. The user composes exactly what they want by stacking skills.
229
+
230
+ ### Custom Skill Application
231
+
232
+ A user can apply a skill with their own modifications in a single step:
233
+
234
+ 1. Apply the skill normally (programmatic merge)
235
+ 2. Claude Code asks if the user wants to make any modifications
236
+ 3. User describes what they want different
237
+ 4. Claude Code makes the modifications on top of the freshly applied skill
238
+ 5. The modifications are recorded as a custom patch tied to this skill
239
+
240
+ Recorded in `state.yaml`:
241
+
242
+ ```yaml
243
+ applied_skills:
244
+ - skill: telegram
245
+ version: 1.0.0
246
+ custom_patch: .nanoclaw/custom/telegram-group-only.patch
247
+ custom_patch_description: "Restrict bot responses to group chats only"
248
+ ```
249
+
250
+ On replay, the skill applies programmatically, then the custom patch applies on top.
251
+
252
+ ---
253
+
254
+ ## 5. File Operations: Renames, Deletes, Moves
255
+
256
+ Core updates and some skills will need to rename, delete, or move files. These are not text merges — they're structural changes handled as explicit scripted operations.
257
+
258
+ ### Declaration in Manifest
259
+
260
+ ```yaml
261
+ file_ops:
262
+ - type: rename
263
+ from: src/server.ts
264
+ to: src/app.ts
265
+ - type: delete
266
+ path: src/deprecated/old-handler.ts
267
+ - type: move
268
+ from: src/utils/helpers.ts
269
+ to: src/lib/helpers.ts
270
+ ```
271
+
272
+ ### Execution Order
273
+
274
+ File operations run **before** code merges, because merges need to target the correct file paths:
275
+
276
+ 1. Pre-flight checks (state validation, core version, dependencies, conflicts, drift detection)
277
+ 2. Acquire operation lock
278
+ 3. **Backup** all files that will be touched
279
+ 4. **File operations** (renames, deletes, moves)
280
+ 5. Copy new files from `add/`
281
+ 6. Three-way merge modified code files
282
+ 7. Conflict resolution (rerere auto-resolve, or return with `backupPending: true`)
283
+ 8. Apply structured operations (npm deps, env vars, docker-compose — batched)
284
+ 9. Run `npm install` (once, if any structured npm_dependencies exist)
285
+ 10. Update state (record skill application, file hashes, structured outcomes)
286
+ 11. Run tests (if `manifest.test` defined; rollback state + backup on failure)
287
+ 12. Clean up (delete backup on success, release lock)
288
+
289
+ ### Path Remapping for Skills
290
+
291
+ When the core renames a file (e.g., `server.ts` → `app.ts`), skills authored against the old path still reference `server.ts` in their `modifies` and `modify/` directories. **Skill packages are never mutated on the user's machine.**
292
+
293
+ Instead, core updates ship a **compatibility map**:
294
+
295
+ ```yaml
296
+ # In the update package
297
+ path_remap:
298
+ src/server.ts: src/app.ts
299
+ src/old-config.ts: src/config/main.ts
300
+ ```
301
+
302
+ The system resolves paths at apply time: if a skill targets `src/server.ts` and the remap says it's now `src/app.ts`, the merge runs against `src/app.ts`. The remap is recorded in `state.yaml` so future operations are consistent.
303
+
304
+ ### Safety Checks
305
+
306
+ Before executing file operations:
307
+
308
+ - Verify the source file exists
309
+ - For deletes: warn if the file has modifications beyond the base (user or skill changes would be lost)
310
+
311
+ ---
312
+
313
+ ## 6. The Apply Flow
314
+
315
+ When a user runs the skill's slash command in Claude Code:
316
+
317
+ ### Step 1: Pre-flight Checks
318
+
319
+ - Core version compatibility
320
+ - Dependencies satisfied
321
+ - No unresolvable conflicts with applied skills
322
+ - Check for untracked changes (see Section 9)
323
+
324
+ ### Step 2: Backup
325
+
326
+ Copy all files that will be modified to `.nanoclaw/backup/`. If the operation fails at any point, restore from backup.
327
+
328
+ ### Step 3: File Operations
329
+
330
+ Execute renames, deletes, or moves with safety checks. Apply path remapping if needed.
331
+
332
+ ### Step 4: Apply New Files
333
+
334
+ ```bash
335
+ cp skills/add-whatsapp/add/src/channels/whatsapp.ts src/channels/whatsapp.ts
336
+ ```
337
+
338
+ ### Step 5: Merge Modified Code Files
339
+
340
+ For each file in `modifies` (with path remapping applied):
341
+
342
+ ```bash
343
+ git merge-file src/server.ts .nanoclaw/base/src/server.ts skills/add-whatsapp/modify/src/server.ts
344
+ ```
345
+
346
+ - **Exit code 0**: clean merge, move on
347
+ - **Exit code > 0**: conflict markers in file, proceed to resolution
348
+
349
+ ### Step 6: Conflict Resolution (Three-Level)
350
+
351
+ 1. **Check shared resolution cache** (`.nanoclaw/resolutions/`) — load into local `git rerere` if a verified resolution exists for this skill combination. **Only apply if input hashes match exactly** (base hash + current hash + skill modified hash).
352
+ 2. **`git rerere`** — checks local cache. If found, applied automatically. Done.
353
+ 3. **Claude Code** — reads conflict markers + `SKILL.md` + `.intent.md` (Invariants, Must-keep sections) of current and previously applied skills. Resolves. `git rerere` caches the resolution.
354
+ 4. **User** — if Claude Code cannot determine intent, it asks the user for the desired behavior.
355
+
356
+ ### Step 7: Apply Structured Operations
357
+
358
+ Collect all structured declarations (from this skill and any previously applied skills if batching). Apply deterministically:
359
+
360
+ - Merge npm dependencies into `package.json` (check for version conflicts)
361
+ - Append env vars to `.env.example`
362
+ - Merge docker-compose services (check for port/name collisions)
363
+ - Run `npm install` **once** at the end
364
+ - Record resolved outcomes in state
365
+
366
+ ### Step 8: Post-Apply and Validate
367
+
368
+ 1. Run any `post_apply` commands (non-structured operations only)
369
+ 2. Update `.nanoclaw/state.yaml` — skill record, file hashes (base, skill, merged per file), structured outcomes
370
+ 3. **Run skill tests** — mandatory, even if all merges were clean
371
+ 4. If tests fail on a clean merge → escalate to Level 2 (Claude Code diagnoses the semantic conflict)
372
+
373
+ ### Step 9: Clean Up
374
+
375
+ If tests pass, delete `.nanoclaw/backup/`. The operation is complete.
376
+
377
+ If tests fail and Level 2 can't resolve, restore from `.nanoclaw/backup/` and report the failure.
378
+
379
+ ---
380
+
381
+ ## 7. Shared Resolution Cache
382
+
383
+ ### The Problem
384
+
385
+ `git rerere` is local by default. But NanoClaw has thousands of users applying the same skill combinations. Every user hitting the same conflict and waiting for Claude Code to resolve it is wasteful.
386
+
387
+ ### The Solution
388
+
389
+ NanoClaw maintains a verified resolution cache in `.nanoclaw/resolutions/` that ships with the project. This is the shared artifact — **not** `.git/rr-cache/`, which stays local.
390
+
391
+ ```
392
+ .nanoclaw/
393
+ resolutions/
394
+ whatsapp@1.2.0+telegram@1.0.0/
395
+ src/
396
+ server.ts.resolution
397
+ server.ts.preimage
398
+ config.ts.resolution
399
+ config.ts.preimage
400
+ meta.yaml
401
+ ```
402
+
403
+ ### Hash Enforcement
404
+
405
+ A cached resolution is **only applied if input hashes match exactly**:
406
+
407
+ ```yaml
408
+ # meta.yaml
409
+ skills:
410
+ - whatsapp@1.2.0
411
+ - telegram@1.0.0
412
+ apply_order: [whatsapp, telegram]
413
+ core_version: 0.6.0
414
+ resolved_at: 2026-02-15T10:00:00Z
415
+ tested: true
416
+ test_passed: true
417
+ resolution_source: maintainer
418
+ input_hashes:
419
+ base: "aaa..."
420
+ current_after_whatsapp: "bbb..."
421
+ telegram_modified: "ccc..."
422
+ output_hash: "ddd..."
423
+ ```
424
+
425
+ If any input hash doesn't match, the cached resolution is skipped and the system proceeds to Level 2.
426
+
427
+ ### Validated: rerere + merge-file Require an Index Adapter
428
+
429
+ `git rerere` does **not** natively recognize `git merge-file` output. This was validated in Phase 0 testing (`tests/phase0-merge-rerere.sh`, 33 tests).
430
+
431
+ The issue is not about conflict marker format — `merge-file` uses filenames as labels (`<<<<<<< current.ts`) while `git merge` uses branch names (`<<<<<<< HEAD`), but rerere strips all labels and hashes only the conflict body. The formats are compatible.
432
+
433
+ The actual issue: **rerere requires unmerged index entries** (stages 1/2/3) to detect that a merge conflict exists. A normal `git merge` creates these automatically. `git merge-file` operates on the filesystem only and does not touch the index.
434
+
435
+ #### The Adapter
436
+
437
+ After `git merge-file` produces a conflict, the system must create the index state that rerere expects:
438
+
439
+ ```bash
440
+ # 1. Run the merge (produces conflict markers in the working tree)
441
+ git merge-file current.ts .nanoclaw/base/src/file.ts skills/add-whatsapp/modify/src/file.ts
442
+
443
+ # 2. If exit code > 0 (conflict), set up rerere adapter:
444
+
445
+ # Create blob objects for the three versions
446
+ base_hash=$(git hash-object -w .nanoclaw/base/src/file.ts)
447
+ ours_hash=$(git hash-object -w skills/previous-skill/modify/src/file.ts) # or the pre-merge current
448
+ theirs_hash=$(git hash-object -w skills/add-whatsapp/modify/src/file.ts)
449
+
450
+ # Create unmerged index entries at stages 1 (base), 2 (ours), 3 (theirs)
451
+ printf '100644 %s 1\tsrc/file.ts\0' "$base_hash" | git update-index --index-info
452
+ printf '100644 %s 2\tsrc/file.ts\0' "$ours_hash" | git update-index --index-info
453
+ printf '100644 %s 3\tsrc/file.ts\0' "$theirs_hash" | git update-index --index-info
454
+
455
+ # Set merge state (rerere checks for MERGE_HEAD)
456
+ echo "$(git rev-parse HEAD)" > .git/MERGE_HEAD
457
+ echo "skill merge" > .git/MERGE_MSG
458
+
459
+ # 3. Now rerere can see the conflict
460
+ git rerere # Records preimage, or auto-resolves from cache
461
+
462
+ # 4. After resolution (manual or auto):
463
+ git add src/file.ts
464
+ git rerere # Records postimage (caches the resolution)
465
+
466
+ # 5. Clean up merge state
467
+ rm .git/MERGE_HEAD .git/MERGE_MSG
468
+ git reset HEAD
469
+ ```
470
+
471
+ #### Key Properties Validated
472
+
473
+ - **Conflict body identity**: `merge-file` and `git merge` produce identical conflict bodies for the same inputs. Rerere hashes the body only, so resolutions learned from either source are interchangeable.
474
+ - **Hash determinism**: The same conflict always produces the same rerere hash. This is critical for the shared resolution cache.
475
+ - **Resolution portability**: Copying `preimage` and `postimage` files (plus the hash directory name) from one repo's `.git/rr-cache/` to another works. Rerere auto-resolves in the target repo.
476
+ - **Adjacent line sensitivity**: Changes within ~3 lines of each other are treated as a single conflict hunk by `merge-file`. Skills that modify the same area of a file will conflict even if they modify different lines. This is expected and handled by the resolution cache.
477
+
478
+ #### Implication: Git Repository Required
479
+
480
+ The adapter requires `git hash-object`, `git update-index`, and `.git/rr-cache/`. This means the project directory must be a git repository for rerere caching to work. Users who download a zip (no `.git/`) lose resolution caching but not functionality — conflicts escalate directly to Level 2 (Claude Code resolves). The system should detect this case and skip rerere operations gracefully.
481
+
482
+ ### Maintainer Workflow
483
+
484
+ When releasing a core update or new skill version:
485
+
486
+ 1. Fresh codebase at target core version
487
+ 2. Apply each official skill individually — verify clean merge, run tests
488
+ 3. Apply pairwise combinations **for skills that modify at least one common file or have overlapping structured operations**
489
+ 4. Apply curated three-skill stacks based on popularity and high overlap
490
+ 5. Resolve all conflicts (code and structured)
491
+ 6. Record all resolutions with input hashes
492
+ 7. Run full test suite for every combination
493
+ 8. Ship verified resolutions with the release
494
+
495
+ The bar: **a user with any common combination of official skills should never encounter an unresolved conflict.**
496
+
497
+ ---
498
+
499
+ ## 8. State Tracking
500
+
501
+ `.nanoclaw/state.yaml` records everything about the installation:
502
+
503
+ ```yaml
504
+ skills_system_version: "0.1.0" # Schema version — tooling checks this before any operation
505
+ core_version: 0.1.0
506
+
507
+ applied_skills:
508
+ - name: telegram
509
+ version: 1.0.0
510
+ applied_at: 2026-02-16T22:47:02.139Z
511
+ file_hashes:
512
+ src/channels/telegram.ts: "f627b9cf..."
513
+ src/channels/telegram.test.ts: "400116769..."
514
+ src/config.ts: "9ae28d1f..."
515
+ src/index.ts: "46dbe495..."
516
+ src/routing.test.ts: "5e1aede9..."
517
+ structured_outcomes:
518
+ npm_dependencies:
519
+ grammy: "^1.39.3"
520
+ env_additions:
521
+ - TELEGRAM_BOT_TOKEN
522
+ - TELEGRAM_ONLY
523
+ test: "npx vitest run src/channels/telegram.test.ts"
524
+
525
+ - name: discord
526
+ version: 1.0.0
527
+ applied_at: 2026-02-17T17:29:37.821Z
528
+ file_hashes:
529
+ src/channels/discord.ts: "5d669123..."
530
+ src/channels/discord.test.ts: "19e1c6b9..."
531
+ src/config.ts: "a0a32df4..."
532
+ src/index.ts: "d61e3a9d..."
533
+ src/routing.test.ts: "edbacb00..."
534
+ structured_outcomes:
535
+ npm_dependencies:
536
+ discord.js: "^14.18.0"
537
+ env_additions:
538
+ - DISCORD_BOT_TOKEN
539
+ - DISCORD_ONLY
540
+ test: "npx vitest run src/channels/discord.test.ts"
541
+
542
+ custom_modifications:
543
+ - description: "Added custom logging middleware"
544
+ applied_at: 2026-02-15T12:00:00Z
545
+ files_modified:
546
+ - src/server.ts
547
+ patch_file: .nanoclaw/custom/001-logging-middleware.patch
548
+ ```
549
+
550
+ **v0.1 implementation notes:**
551
+ - `file_hashes` stores a single SHA-256 hash per file (the final merged result). Three-part hashes (base/skill_modified/merged) are planned for a future version to improve drift diagnosis.
552
+ - Applied skills use `name` as the key field (not `skill`), matching the TypeScript `AppliedSkill` interface.
553
+ - `structured_outcomes` stores the raw manifest values plus the `test` command. Resolved npm versions (actual installed versions vs semver ranges) are not yet tracked.
554
+ - Fields like `installed_at`, `last_updated`, `path_remap`, `rebased_at`, `core_version_at_apply`, `files_added`, and `files_modified` are planned for future versions.
555
+
556
+ ---
557
+
558
+ ## 9. Untracked Changes
559
+
560
+ If a user edits files directly, the system detects this via hash comparison.
561
+
562
+ ### When Detection Happens
563
+
564
+ Before **any operation that modifies the codebase**: applying a skill, removing a skill, updating the core, replaying, or rebasing.
565
+
566
+ ### What Happens
567
+
568
+ ```
569
+ Detected untracked changes to src/server.ts.
570
+ [1] Record these as a custom modification (recommended)
571
+ [2] Continue anyway (changes preserved, but not tracked for future replay)
572
+ [3] Abort
573
+ ```
574
+
575
+ The system never blocks or loses work. Option 1 generates a patch and records it, making changes reproducible. Option 2 preserves the changes but they won't survive replay.
576
+
577
+ ### The Recovery Guarantee
578
+
579
+ No matter how much a user modifies their codebase outside the system, the three-level model can always bring them back:
580
+
581
+ 1. **Git**: diff current files against base, identify what changed
582
+ 2. **Claude Code**: read `state.yaml` to understand what skills were applied, compare against actual file state, identify discrepancies
583
+ 3. **User**: Claude Code asks what they intended, what to keep, what to discard
584
+
585
+ There is no unrecoverable state.
586
+
587
+ ---
588
+
589
+ ## 10. Core Updates
590
+
591
+ Core updates must be as programmatic as possible. The NanoClaw team is responsible for ensuring updates apply cleanly to common skill combinations.
592
+
593
+ ### Patches and Migrations
594
+
595
+ Most core changes — bug fixes, performance improvements, new functionality — propagate automatically through the three-way merge. No special handling needed.
596
+
597
+ **Breaking changes** — changed defaults, removed features, functionality moved to skills — require a **migration**. A migration is a skill that preserves the old behavior, authored against the new core. It's applied automatically during the update so the user's setup doesn't change.
598
+
599
+ The maintainer's responsibility when making a breaking change: make the change in core, author a migration skill that reverts it, add the entry to `migrations.yaml`, test it. That's the cost of breaking changes.
600
+
601
+ ### `migrations.yaml`
602
+
603
+ An append-only file in the repo root. Each entry records a breaking change and the skill that preserves the old behavior:
604
+
605
+ ```yaml
606
+ - since: 0.6.0
607
+ skill: apple-containers@1.0.0
608
+ description: "Preserves Apple Containers (default changed to Docker in 0.6)"
609
+
610
+ - since: 0.7.0
611
+ skill: add-whatsapp@2.0.0
612
+ description: "Preserves WhatsApp (moved from core to skill in 0.7)"
613
+
614
+ - since: 0.8.0
615
+ skill: legacy-auth@1.0.0
616
+ description: "Preserves legacy auth module (removed from core in 0.8)"
617
+ ```
618
+
619
+ Migration skills are regular skills in the `skills/` directory. They have manifests, intent files, tests — everything. They're authored against the **new** core version: the modified file is the new core with the specific breaking change reverted, everything else (bug fixes, new features) identical to the new core.
620
+
621
+ ### How Migrations Work During Updates
622
+
623
+ 1. Three-way merge brings in everything from the new core — patches, breaking changes, all of it
624
+ 2. Conflict resolution (normal)
625
+ 3. Re-apply custom patches (normal)
626
+ 4. **Update base to new core**
627
+ 5. Filter `migrations.yaml` for entries where `since` > user's old `core_version`
628
+ 6. **Apply each migration skill using the normal apply flow against the new base**
629
+ 7. Record migration skills in `state.yaml` like any other skill
630
+ 8. Run tests
631
+
632
+ Step 6 is just the same apply function used for any skill. The migration skill merges against the new base:
633
+
634
+ - **Base**: new core (e.g., v0.8 with Docker)
635
+ - **Current**: user's file after the update merge (new core + user's customizations preserved by the earlier merge)
636
+ - **Other**: migration skill's file (new core with Docker reverted to Apple, everything else identical)
637
+
638
+ Three-way merge correctly keeps user's customizations, reverts the breaking change, and preserves all bug fixes. If there's a conflict, normal resolution: cache → Claude → user.
639
+
640
+ For big version jumps (v0.5 → v0.8), all applicable migrations are applied in sequence. Migration skills are maintained against the latest core version, so they always compose correctly with the current codebase.
641
+
642
+ ### What the User Sees
643
+
644
+ ```
645
+ Core updated: 0.5.0 → 0.8.0
646
+ ✓ All patches applied
647
+
648
+ Preserving your current setup:
649
+ + apple-containers@1.0.0
650
+ + add-whatsapp@2.0.0
651
+ + legacy-auth@1.0.0
652
+
653
+ Skill updates:
654
+ ✓ add-telegram 1.0.0 → 1.2.0
655
+
656
+ To accept new defaults: /remove-skill <name>
657
+ ✓ All tests passing
658
+ ```
659
+
660
+ No prompts, no choices during the update. The user's setup doesn't change. If they later want to accept a new default, they remove the migration skill.
661
+
662
+ ### What the Core Team Ships With an Update
663
+
664
+ ```
665
+ updates/
666
+ 0.5.0-to-0.6.0/
667
+ migration.md # What changed, why, and how it affects skills
668
+ files/ # The new core files
669
+ file_ops: # Any renames, deletes, moves
670
+ path_remap: # Compatibility map for old skill paths
671
+ resolutions/ # Pre-computed resolutions for official skills
672
+ ```
673
+
674
+ Plus any new migration skills added to `skills/` and entries appended to `migrations.yaml`.
675
+
676
+ ### The Maintainer's Process
677
+
678
+ 1. **Make the core change**
679
+ 2. **If it's a breaking change**: author a migration skill against the new core, add entry to `migrations.yaml`
680
+ 3. **Write `migration.md`** — what changed, why, what skills might be affected
681
+ 4. **Test every official skill individually** against the new core (including migration skills)
682
+ 5. **Test pairwise combinations** for skills that share modified files or structured operations
683
+ 6. **Test curated three-skill stacks** based on popularity and overlap
684
+ 7. **Resolve all conflicts**
685
+ 8. **Record all resolutions** with enforced input hashes
686
+ 9. **Run full test suites**
687
+ 10. **Ship everything** — migration guide, migration skills, file ops, path remap, resolutions
688
+
689
+ The bar: **patches apply silently. Breaking changes are auto-preserved via migration skills. A user should never be surprised by a change to their working setup.**
690
+
691
+ ### Update Flow (Full)
692
+
693
+ #### Step 1: Pre-flight
694
+
695
+ - Check for untracked changes
696
+ - Read `state.yaml`
697
+ - Load shipped resolutions
698
+ - Parse `migrations.yaml`, filter for applicable migrations
699
+
700
+ #### Step 2: Preview
701
+
702
+ Before modifying anything, show the user what's coming. This uses only git commands — no files are opened or changed:
703
+
704
+ ```bash
705
+ # Compute common base
706
+ BASE=$(git merge-base HEAD upstream/$BRANCH)
707
+
708
+ # Upstream commits since last sync
709
+ git log --oneline $BASE..upstream/$BRANCH
710
+
711
+ # Files changed upstream
712
+ git diff --name-only $BASE..upstream/$BRANCH
713
+ ```
714
+
715
+ Present a summary grouped by impact:
716
+
717
+ ```
718
+ Update available: 0.5.0 → 0.8.0 (12 commits)
719
+
720
+ Source: 4 files modified (server.ts, config.ts, ...)
721
+ Skills: 2 new skills added, 1 skill updated
722
+ Config: package.json, docker-compose.yml updated
723
+
724
+ Migrations (auto-applied to preserve your setup):
725
+ + apple-containers@1.0.0 (container default changed to Docker)
726
+ + add-whatsapp@2.0.0 (WhatsApp moved from core to skill)
727
+
728
+ Skill updates:
729
+ add-telegram 1.0.0 → 1.2.0
730
+
731
+ [1] Proceed with update
732
+ [2] Abort
733
+ ```
734
+
735
+ If the user aborts, stop here. Nothing was modified.
736
+
737
+ #### Step 3: Backup
738
+
739
+ Copy all files that will be modified to `.nanoclaw/backup/`.
740
+
741
+ #### Step 4: File Operations and Path Remap
742
+
743
+ Apply renames, deletes, moves. Record path remap in state.
744
+
745
+ #### Step 5: Three-Way Merge
746
+
747
+ For each core file that changed:
748
+
749
+ ```bash
750
+ git merge-file src/server.ts .nanoclaw/base/src/server.ts updates/0.5.0-to-0.6.0/files/src/server.ts
751
+ ```
752
+
753
+ #### Step 6: Conflict Resolution
754
+
755
+ 1. Shipped resolutions (hash-verified) → automatic
756
+ 2. `git rerere` local cache → automatic
757
+ 3. Claude Code with `migration.md` + skill intents → resolves
758
+ 4. User → only for genuine ambiguity
759
+
760
+ #### Step 7: Re-apply Custom Patches
761
+
762
+ ```bash
763
+ git apply --3way .nanoclaw/custom/001-logging-middleware.patch
764
+ ```
765
+
766
+ Using `--3way` allows git to fall back to three-way merge when line numbers have drifted. If `--3way` fails, escalate to Level 2.
767
+
768
+ #### Step 8: Update Base
769
+
770
+ `.nanoclaw/base/` replaced with new clean core. This is the **only time** the base changes.
771
+
772
+ #### Step 9: Apply Migration Skills
773
+
774
+ For each applicable migration (where `since` > old `core_version`), apply the migration skill using the normal apply flow against the new base. Record in `state.yaml`.
775
+
776
+ #### Step 10: Re-apply Updated Skills
777
+
778
+ Skills live in the repo and update alongside core files. After the update, compare the version in each skill's `manifest.yaml` on disk against the version recorded in `state.yaml`.
779
+
780
+ For each skill where the on-disk version is newer than the recorded version:
781
+
782
+ 1. Re-apply the skill using the normal apply flow against the new base
783
+ 2. The three-way merge brings in the skill's new changes while preserving user customizations
784
+ 3. Re-apply any custom patches tied to the skill (`git apply --3way`)
785
+ 4. Update the version in `state.yaml`
786
+
787
+ Skills whose version hasn't changed are skipped — no action needed.
788
+
789
+ If the user has a custom patch on a skill that changed significantly, the patch may conflict. Normal resolution: cache → Claude → user.
790
+
791
+ #### Step 11: Re-run Structured Operations
792
+
793
+ Recompute structured operations against the updated codebase to ensure consistency.
794
+
795
+ #### Step 12: Validate
796
+
797
+ - Run all skill tests — mandatory
798
+ - Compatibility report:
799
+
800
+ ```
801
+ Core updated: 0.5.0 → 0.8.0
802
+ ✓ All patches applied
803
+
804
+ Migrations:
805
+ + apple-containers@1.0.0 (preserves container runtime)
806
+ + add-whatsapp@2.0.0 (WhatsApp moved to skill)
807
+
808
+ Skill updates:
809
+ ✓ add-telegram 1.0.0 → 1.2.0 (new features applied)
810
+ ✓ custom/telegram-group-only — re-applied cleanly
811
+
812
+ ✓ All tests passing
813
+ ```
814
+
815
+ #### Step 13: Clean Up
816
+
817
+ Delete `.nanoclaw/backup/`.
818
+
819
+ ### Progressive Core Slimming
820
+
821
+ Migrations enable a clean path for slimming down the core over time. Each release can move more functionality to skills:
822
+
823
+ - The breaking change removes the feature from core
824
+ - The migration skill preserves it for existing users
825
+ - New users start with a minimal core and add what they need
826
+ - Over time, `state.yaml` reflects exactly what each user is running
827
+
828
+ ---
829
+
830
+ ## 11. Skill Removal (Uninstall)
831
+
832
+ Removing a skill is not a reverse-patch operation. **Uninstall is a replay without the skill.**
833
+
834
+ ### How It Works
835
+
836
+ 1. Read `state.yaml` to get the full list of applied skills and custom modifications
837
+ 2. Remove the target skill from the list
838
+ 3. Backup the current codebase to `.nanoclaw/backup/`
839
+ 4. **Replay from clean base** — apply each remaining skill in order, apply custom patches, using the resolution cache
840
+ 5. Run all tests
841
+ 6. If tests pass, delete backup and update `state.yaml`
842
+ 7. If tests fail, restore from backup and report
843
+
844
+ ### Custom Patches Tied to the Removed Skill
845
+
846
+ If the removed skill has a `custom_patch` in `state.yaml`, the user is warned:
847
+
848
+ ```
849
+ Removing telegram will also discard custom patch: "Restrict bot responses to group chats only"
850
+ [1] Continue (discard custom patch)
851
+ [2] Abort
852
+ ```
853
+
854
+ ---
855
+
856
+ ## 12. Rebase
857
+
858
+ Flatten accumulated layers into a clean starting point.
859
+
860
+ ### What Rebase Does
861
+
862
+ 1. Takes the user's current actual files as the new reality
863
+ 2. Updates `.nanoclaw/base/` to the current core version's clean files
864
+ 3. For each applied skill, regenerates the modified file diffs against the new base
865
+ 4. Updates `state.yaml` with `rebased_at` timestamp
866
+ 5. Clears old custom patches (now baked in)
867
+ 6. Clears stale resolution cache entries
868
+
869
+ ### When to Rebase
870
+
871
+ - After a major core update
872
+ - When accumulated patches become unwieldy
873
+ - Before a significant new skill application
874
+ - Periodically as maintenance
875
+
876
+ ### Tradeoffs
877
+
878
+ **Lose**: individual skill patch history, ability to cleanly remove a single old skill, old custom patches as separate artifacts
879
+
880
+ **Gain**: clean base, simpler future merges, reduced cache size, fresh starting point
881
+
882
+ ---
883
+
884
+ ## 13. Replay
885
+
886
+ Given `state.yaml`, reproduce the exact installation on a fresh machine with no AI intervention (assuming all resolutions are cached).
887
+
888
+ ### Replay Flow
889
+
890
+ ```bash
891
+ # Fully programmatic — no Claude Code needed
892
+
893
+ # 1. Install core at specified version
894
+ nanoclaw-init --version 0.5.0
895
+
896
+ # 2. Load shared resolutions into local rerere cache
897
+ load-resolutions .nanoclaw/resolutions/
898
+
899
+ # 3. For each skill in applied_skills (in order):
900
+ for skill in state.applied_skills:
901
+ # File operations
902
+ apply_file_ops(skill)
903
+
904
+ # Copy new files
905
+ cp skills/${skill.name}/add/* .
906
+
907
+ # Merge modified code files (with path remapping)
908
+ for file in skill.files_modified:
909
+ resolved_path = apply_remap(file, state.path_remap)
910
+ git merge-file ${resolved_path} .nanoclaw/base/${resolved_path} skills/${skill.name}/modify/${file}
911
+ # git rerere auto-resolves from shared cache if needed
912
+
913
+ # Apply skill-specific custom patch if recorded
914
+ if skill.custom_patch:
915
+ git apply --3way ${skill.custom_patch}
916
+
917
+ # 4. Apply all structured operations (batched)
918
+ collect_all_structured_ops(state.applied_skills)
919
+ merge_npm_dependencies → write package.json once
920
+ npm install once
921
+ merge_env_additions → write .env.example once
922
+ merge_compose_services → write docker-compose.yml once
923
+
924
+ # 5. Apply standalone custom modifications
925
+ for custom in state.custom_modifications:
926
+ git apply --3way ${custom.patch_file}
927
+
928
+ # 6. Run tests and verify hashes
929
+ run_tests && verify_hashes
930
+ ```
931
+
932
+ ---
933
+
934
+ ## 14. Skill Tests
935
+
936
+ Each skill includes integration tests that validate the skill works correctly when applied.
937
+
938
+ ### Structure
939
+
940
+ ```
941
+ skills/
942
+ add-whatsapp/
943
+ tests/
944
+ whatsapp.test.ts
945
+ ```
946
+
947
+ ### What Tests Validate
948
+
949
+ - **Single skill on fresh core**: apply to clean codebase → tests pass → integration works
950
+ - **Skill functionality**: the feature actually works
951
+ - **Post-apply state**: files in expected state, `state.yaml` correctly updated
952
+
953
+ ### When Tests Run (Always)
954
+
955
+ - **After applying a skill** — even if all merges were clean
956
+ - **After core update** — even if all merges were clean
957
+ - **After uninstall replay** — confirms removal didn't break remaining skills
958
+ - **In CI** — tests all official skills individually and in common combinations
959
+ - **During replay** — validates replayed state
960
+
961
+ Clean merge ≠ working code. Tests are the only reliable signal.
962
+
963
+ ### CI Test Matrix
964
+
965
+ Test coverage is **smart, not exhaustive**:
966
+
967
+ - Every official skill individually against each supported core version
968
+ - **Pairwise combinations for skills that modify at least one common file or have overlapping structured operations**
969
+ - Curated three-skill stacks based on popularity and high overlap
970
+ - Test matrix auto-generated from manifest `modifies` and `structured` fields
971
+
972
+ Each passing combination generates a verified resolution entry for the shared cache.
973
+
974
+ ---
975
+
976
+ ## 15. Project Configuration
977
+
978
+ ### `.gitattributes`
979
+
980
+ Ship with NanoClaw to reduce noisy merge conflicts:
981
+
982
+ ```
983
+ * text=auto
984
+ *.ts text eol=lf
985
+ *.json text eol=lf
986
+ *.yaml text eol=lf
987
+ *.md text eol=lf
988
+ ```
989
+
990
+ ---
991
+
992
+ ## 16. Directory Structure
993
+
994
+ ```
995
+ project/
996
+ src/ # The actual codebase
997
+ server.ts
998
+ config.ts
999
+ channels/
1000
+ whatsapp.ts
1001
+ telegram.ts
1002
+ skills/ # Skill packages (Claude Code slash commands)
1003
+ add-whatsapp/
1004
+ SKILL.md
1005
+ manifest.yaml
1006
+ tests/
1007
+ whatsapp.test.ts
1008
+ add/
1009
+ src/channels/whatsapp.ts
1010
+ modify/
1011
+ src/
1012
+ server.ts
1013
+ server.ts.intent.md
1014
+ config.ts
1015
+ config.ts.intent.md
1016
+ add-telegram/
1017
+ ...
1018
+ telegram-reactions/ # Layered skill
1019
+ ...
1020
+ .nanoclaw/
1021
+ base/ # Clean core (shared base)
1022
+ src/
1023
+ server.ts
1024
+ config.ts
1025
+ ...
1026
+ state.yaml # Full installation state
1027
+ backup/ # Temporary backup during operations
1028
+ custom/ # Custom patches
1029
+ telegram-group-only.patch
1030
+ 001-logging-middleware.patch
1031
+ 001-logging-middleware.md
1032
+ resolutions/ # Shared verified resolution cache
1033
+ whatsapp@1.2.0+telegram@1.0.0/
1034
+ src/
1035
+ server.ts.resolution
1036
+ server.ts.preimage
1037
+ meta.yaml
1038
+ .gitattributes
1039
+ ```
1040
+
1041
+ ---
1042
+
1043
+ ## 17. Design Principles
1044
+
1045
+ 1. **Use git, don't reinvent it.** `git merge-file` for code merges, `git rerere` for caching resolutions, `git apply --3way` for custom patches.
1046
+ 2. **Three-level resolution: git → Claude → user.** Programmatic first, AI second, human third.
1047
+ 3. **Clean merges aren't enough.** Tests run after every operation. Semantic conflicts survive text merges.
1048
+ 4. **All operations are safe.** Backup before, restore on failure. No half-applied state.
1049
+ 5. **One shared base.** `.nanoclaw/base/` is the clean core before any skills or customizations. It's the stable common ancestor for all three-way merges. Only updated on core updates.
1050
+ 6. **Code merges vs. structured operations.** Source code is three-way merged. Dependencies, env vars, and configs are aggregated programmatically. Structured operations are implicit and batched.
1051
+ 7. **Resolutions are learned and shared.** Maintainers resolve conflicts and ship verified resolutions with hash enforcement. `.nanoclaw/resolutions/` is the shared artifact.
1052
+ 8. **One skill, one happy path.** No predefined configuration options. Customization is more patching.
1053
+ 9. **Skills layer and compose.** Core skills provide the foundation. Extension skills add capabilities.
1054
+ 10. **Intent is first-class and structured.** `SKILL.md`, `.intent.md` (What, Invariants, Must-keep), and `migration.md`.
1055
+ 11. **State is explicit and complete.** Skills, custom patches, per-file hashes, structured outcomes, path remaps. Replay is deterministic. Drift is instant to detect.
1056
+ 12. **Always recoverable.** The three-level model reconstructs coherent state from any starting point.
1057
+ 13. **Uninstall is replay.** Replay from clean base without the skill. Backup for safety.
1058
+ 14. **Core updates are the maintainers' responsibility.** Test, resolve, ship. Breaking changes require a migration skill that preserves the old behavior. The cost of a breaking change is authoring and testing the migration. Users should never be surprised by a change to their setup.
1059
+ 15. **File operations and path remapping are first-class.** Renames, deletes, moves in manifests. Skills are never mutated — paths resolve at apply time.
1060
+ 16. **Skills are tested.** Integration tests per skill. CI tests pairwise by overlap. Tests run always.
1061
+ 17. **Deterministic serialization.** Sorted keys, consistent formatting. No noisy diffs.
1062
+ 18. **Rebase when needed.** Flatten layers for a clean starting point.
1063
+ 19. **Progressive core slimming.** Breaking changes move functionality from core to migration skills. Existing users keep what they have automatically. New users start minimal and add what they need.