Extracts user customizations from a fork, generates a replayable migration guide, and upgrades to upstream by reapplying customizations on a clean base. Replaces merge-based upgrades with intent-based migration.
NanoClaw users fork the repo and customize it — changing config values, editing source files, modifying personas, adding skills. When upstream ships updates or refactors, git merge produces painful conflicts because the same core files were changed on both sides.
This skill extracts the user's customizations into a migration guide — capturing both the intent (what they want) and the implementation details (how they did it, with code snippets, API calls, and specific configurations). On upgrade, it checks out clean upstream in a worktree, then reapplies customizations using the guide. No merge conflicts because there's nothing to merge.
The migration guide is markdown, not structured data. It needs to capture the full range of what a user might customize, with enough implementation detail that a fresh Claude session can reapply it without having seen the original code. Standard changes (config values, simple logic) can be described briefly. Non-standard changes (specific APIs, custom integrations, unusual patterns) need code snippets and precise instructions.
Two phases: Extract (build the migration guide) and Upgrade (use it). If a guide already exists, offer to skip to Upgrade.
groups/, store/, data/, .env) are never touched — only code.cd .upgrade-worktree — always use the full absolute path: cd /absolute/path/.upgrade-worktree && <command>. Store the worktree absolute path in a variable at creation time and reference it throughout.Run git status --porcelain. If non-empty, offer to stash or commit for them (AskUserQuestion: "Stash changes" / "Commit changes" / "I'll handle it"). If they want to commit, stage and commit with a descriptive message. If they want to stash, run git stash push -m "pre-migration stash".
Check remotes with git remote -v. If upstream is missing, ask for the URL (default: https://github.com/qwibitai/nanoclaw.git), add it, then git fetch upstream --prune.
Detect upstream branch: check git branch -r | grep upstream/ for main or master. Store as UPSTREAM_BRANCH.
Quickly assess the scale of divergence, check for an existing guide, and determine the right approach — all before asking the user anything.
BASE=$(git merge-base HEAD upstream/$UPSTREAM_BRANCH)
# Divergence stats
git rev-list --count $BASE..upstream/$UPSTREAM_BRANCH # upstream commits
git rev-list --count $BASE..HEAD # user commits
git diff --name-only $BASE..HEAD | wc -l # user changed files
git diff --stat $BASE..HEAD | tail -1 # insertions/deletions
git diff --name-only $BASE..upstream/$UPSTREAM_BRANCH | wc -l # upstream changed files
Check for existing guide: .nanoclaw-migrations/guide.md or .nanoclaw-migrations/index.md.
Determine the tier based on the total diff from base:
/update-nanoclaw insteadConditions (any of):
Tell the user the scope is small and suggest /update-nanoclaw might be simpler. Let them choose.
Conditions:
Conditions (any of):
src/index.ts, src/container-runner.ts, etc.) beyond what skills introducedUse the full process: multiple sub-agents in parallel, directory-based guide, migration plan.
Now combine the scope assessment with initial user input in one interaction. Present the scope summary (how many commits, files, which tier) and ask (AskUserQuestion):
For Tier 1:
For Tier 2/3 (with or without existing guide):
This single interaction replaces what were previously separate steps for scope assessment, user input, and existing guide check.
If the user chose to update an existing guide rather than re-extract:
Diff HEAD against
<guide-recorded-hash>. For each changed file, summarize what changed and why.
Spawn a haiku sub-agent (Agent tool, model: haiku) for initial exploration:
Explore this NanoClaw fork to identify all changes from the upstream base. Run these commands and report back:
git diff --name-only $BASE..HEAD— all changed filesgit log --oneline $BASE..HEAD— all commits (look for skill branch merges likeMerge branch 'skill/*')git branch -r --list 'upstream/skill/*'— available upstream skill branchesls .claude/skills/— installed skills- For each skill merge found, record the merge commit hash
Report: (a) list of applied skills with their merge commit hashes, (b) list of all changed files, (c) any custom skill directories that don't match upstream branches.
From the sub-agent results, identify:
Don't try to distinguish "user modified a skill file" from "user made their own change" at this stage. The sub-agents in 1.4 will look at all non-skill changes together and surface what matters.
For each applied skill, ask the user in a single batched question (AskUserQuestion, multiSelect):
"I found these applied skills. Select any you customized further after applying:"
Options: one per skill, plus "None — all used as-is".
Then spawn sub-agents to analyze all non-skill changes. For Tier 2, one or two agents. For Tier 3, run in parallel by area:
src/*.ts) — one sub-agentgit diff <merge-commit-hash>..HEAD -- <files-touched-by-skill>
Each sub-agent task:
Read these diffs and the current file contents. For each change:
git diff $BASE..HEAD -- <file>(orgit diff <skill-merge-hash>..HEAD -- <file>for skill-modified files)- Read the full current file for context
- Summarize: what changed, what the likely intent is
- Assess detail level: could a fresh Claude session reproduce this from intent alone, or does it need specific code snippets, API details, import paths?
- For non-standard changes, extract the key code, imports, API calls, and configurations verbatim.
Inter-skill conflicts: If multiple skills are applied, spawn an additional sub-agent to check for interactions between them. Look for:
Document any findings in the "Skill Interactions" section of the migration guide so they can be resolved after skill branches are re-merged during upgrade.
After sub-agents report back, compile the findings and present to the user.
For customizations where the intent is clear (config values, simple modifications): present as a batch for confirmation. Use AskUserQuestion with multiSelect to let the user flag any entries that need correction.
For customizations where the intent is ambiguous: ask specific questions. Don't ask "what did you do?" — instead ask "I see you added X in this file. Was this for Y or something else?"
The user can select "Other" on any question to provide their own description.
For complex migrations, before writing the guide, create a migration plan:
Present the plan to the user for review before proceeding to the guide.
Storage: .nanoclaw-migrations/guide.md for Tier 2. .nanoclaw-migrations/ directory with index.md and section files for Tier 3.
Verification: After writing the guide, read it back and verify:
The guide is structured markdown that a fresh Claude session can follow to reproduce this user's exact setup on a clean upstream checkout.
Structure:
# NanoClaw Migration Guide
Generated: <timestamp>
Base: <BASE hash>
HEAD at generation: <HEAD hash>
Upstream: <upstream HEAD hash>
## Migration Plan
(Tier 3 only — big-picture overview of order, staging, risks)
## Applied Skills
List each skill with its branch name. These are reapplied by merging the upstream skill branch.
- `add-telegram` — branch `skill/telegram`
- `add-voice-transcription` — branch `skill/voice-transcription`
Custom skills (user-created, not from upstream): `.claude/skills/my-custom-skill/` — copy as-is from main tree.
## Skill Interactions
(Document known conflicts or interactions between applied skills.
When two or more skills modify the same file or depend on shared
config, describe the conflict and how to resolve it after merging.
Example: skill A and skill B both add a PROXY_BIND_HOST declaration —
after merging both, deduplicate. Or: skill A throws if ENV_VAR is
missing, but skill B provides a fallback — use the fallback version.)
## Modifications to Applied Skills
### <Skill name>: <what was modified>
**Intent:** ...
**Files:** ...
**How to apply:** (after the skill branch has been merged)
...
## Customizations
### <Descriptive title for customization>
**Intent:** What the user wants and why.
**Files:** Which files to modify.
**How to apply:**
<For standard changes, a brief description is enough.>
<For non-standard changes, include code snippets, API details,
specific values, import paths — everything needed to reproduce
without seeing the original diff.>
### <Next customization...>
Judging detail level: For each customization, assess whether a fresh Claude session could reproduce it from intent alone:
POLL_INTERVAL in src/config.ts from 2000 to 1000."Example entries at different detail levels:
Standard (brief):
### Custom trigger word
**Intent:** Use `@Bob` instead of the default `@Andy`.
**Files:** `src/config.ts`
**How to apply:** Change the default value of `ASSISTANT_NAME` from `'Andy'` to `'Bob'`.
Non-standard (detailed):
### Spanish translation for outbound messages
**Intent:** All outbound messages are translated to Spanish before sending. Uses the DeepL API via the `deepl-node` package.
**Files:** `src/router.ts`, `package.json`
**How to apply:**
1. Add dependency: `npm install deepl-node`
2. In `src/router.ts`, add import at top:
```typescript
import * as deepl from 'deepl-node';
const translator = new deepl.Translator(process.env.DEEPL_API_KEY!);
formatOutbound function, before the return statement, add:
const result = await translator.translateText(text, null, 'es');
text = result.text;
Note: the function needs to be made async if it isn't already.
After writing, offer to commit for the user:
```bash
git add .nanoclaw-migrations/
git commit -m "chore: save migration guide"
Ask (AskUserQuestion): "Migration guide saved. Want to upgrade now or later?"
Same checks as 1.0 — clean tree (offer to stash/commit if dirty), upstream configured, fetch latest.
Read the migration guide. If missing, tell the user you need to extract customizations first and ask if they want to do that now.
New-changes guard: Compare the guide's "HEAD at generation" hash against current HEAD. If there are commits since the guide was generated, warn the user:
"You've made changes since the migration guide was generated. These changes won't be included in the upgrade."
AskUserQuestion:
HASH=$(git rev-parse --short HEAD)
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
git branch backup/pre-migrate-$HASH-$TIMESTAMP
git tag pre-migrate-$HASH-$TIMESTAMP
Save the tag name for rollback instructions at the end.
BASE=$(git merge-base HEAD upstream/$UPSTREAM_BRANCH)
git log --oneline $BASE..upstream/$UPSTREAM_BRANCH
git diff $BASE..upstream/$UPSTREAM_BRANCH -- CHANGELOG.md
If there are [BREAKING] entries, show them and explain how they interact with the user's customizations from the migration guide.
Ask (AskUserQuestion) to proceed or abort.
PROJECT_ROOT=$(pwd)
git worktree add .upgrade-worktree upstream/$UPSTREAM_BRANCH --detach
WORKTREE="$PROJECT_ROOT/.upgrade-worktree"
Store $PROJECT_ROOT and $WORKTREE as absolute paths. Use $WORKTREE in all subsequent commands — never cd .upgrade-worktree with a relative path.
For each skill listed in the migration guide's "Applied Skills" section:
git branch -r --list "upstream/$branch"cd "$WORKTREE" && git merge upstream/skill/<name> --no-edit
Copy any custom skills mentioned in the guide from the main tree into the worktree.
Work in .upgrade-worktree/. Follow each customization section in the migration guide, including "Modifications to Applied Skills."
For Tier 3 migrations with a migration plan, follow the plan's ordering and staging. If the plan calls for staged validation (e.g. validate after skills, then validate after source changes), do so.
For each customization:
For behavior customizations (CLAUDE.md files): copy from the main tree. These are user content, not code.
cd "$WORKTREE" && npm install && npm run build && npm test
If build fails, show the error. Fix only issues caused by the migration. If unclear, ask the user.
Ask (AskUserQuestion):
If testing live:
Stop the service (do this directly):
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist 2>/dev/null || true
Symlink data into the worktree:
ln -s "$PROJECT_ROOT/store" "$WORKTREE/store"
ln -s "$PROJECT_ROOT/data" "$WORKTREE/data"
ln -s "$PROJECT_ROOT/groups" "$WORKTREE/groups"
ln -s "$PROJECT_ROOT/.env" "$WORKTREE/.env"
Start from worktree: cd "$WORKTREE" && npm run dev
Ask the user to send a test message from their phone. Wait for them to confirm it works.
After confirmation, stop the dev server.
Clean up symlinks:
rm "$WORKTREE/store" "$WORKTREE/data" "$WORKTREE/groups" "$WORKTREE/.env"
The swap must be done carefully — the worktree has the upgraded code, but main needs to point to it cleanly. Use absolute paths throughout.
# 1. Capture the worktree HEAD before removing it
WORKTREE_PATH=$(cd "$PROJECT_ROOT/.upgrade-worktree" && pwd)
UPGRADE_COMMIT=$(git -C "$WORKTREE_PATH" rev-parse HEAD)
# 2. Copy the migration guide out of the worktree before removing it
cp -r "$WORKTREE_PATH/.nanoclaw-migrations" /tmp/nanoclaw-migrations-backup 2>/dev/null || true
# 3. Remove the worktree
git worktree remove "$WORKTREE_PATH" --force
# 4. Point the current branch at the upgraded commit
git reset --hard $UPGRADE_COMMIT
# 5. Restore the migration guide and update its hashes
cp -r /tmp/nanoclaw-migrations-backup/* .nanoclaw-migrations/ 2>/dev/null || true
rm -rf /tmp/nanoclaw-migrations-backup
Update the guide's header hashes to reflect the new state. Offer to commit:
git add .nanoclaw-migrations/
git commit -m "chore: upgrade to upstream $(git rev-parse --short upstream/$UPSTREAM_BRANCH)"
Do NOT use git checkout -B to create an intermediate branch — this caused issues in practice. The git reset --hard to the upgrade commit is the cleanest path since the backup tag already preserves the pre-upgrade state.
Run npm install && npm run build in the main tree to confirm.
Restart the service:
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
Show summary:
git reset --hard <backup-tag>Offer to pop the stash if one was created in preflight: git stash pop
.claude/skills/migrate-nanoclaw/diagnostics.md.