docs/skills-as-branches.md
NanoClaw skills are distributed as git branches on the upstream repository. Applying a skill is a git merge. Updating core is a git merge. Everything is standard git.
This replaces the previous skills-engine/ system (three-way file merging, .nanoclaw/ state, manifest files, replay, backup/restore) with plain git operations and Claude for conflict resolution.
The upstream repo (qwibitai/nanoclaw) maintains:
main — core NanoClaw (no skill code)skill/discord — main + Discord integrationskill/telegram — main + Telegram integrationskill/slack — main + Slack integrationskill/gmail — main + Gmail integrationEach skill branch contains all the code changes for that skill: new files, modified source files, updated package.json dependencies, .env.example additions — everything. No manifest, no structured operations, no separate add/ and modify/ directories.
Skills are split into two categories:
Operational skills (on main, always available):
/setup, /debug, /update-nanoclaw, /customize, /update-skills.claude/skills/ on main, immediately available to every userFeature skills (in marketplace, installed on demand):
/add-discord, /add-telegram, /add-slack, /add-gmail, etc.skill/* branch with codeqwibitai/nanoclaw-skills)Users never interact with the marketplace directly. The operational skills /setup and /customize handle plugin installation transparently:
# Claude runs this behind the scenes — users don't see it
claude plugin install nanoclaw-skills@nanoclaw-skills --scope project
Skills are hot-loaded after claude plugin install — no restart needed. This means /setup can install the marketplace plugin, then immediately run any feature skill, all in one session.
/setup asks users what channels they want, then only offers relevant skills:
/add-telegram/add-telegram-swarmDependent skills (e.g., telegram-swarm depends on telegram) are only offered after their parent is installed. /customize follows the same pattern for post-setup additions.
NanoClaw's .claude/settings.json registers the official marketplace:
{
"extraKnownMarketplaces": {
"nanoclaw-skills": {
"source": {
"source": "github",
"repo": "qwibitai/nanoclaw-skills"
}
}
}
}
The marketplace repo uses Claude Code's plugin structure:
qwibitai/nanoclaw-skills/
.claude-plugin/
marketplace.json # Plugin catalog
plugins/
nanoclaw-skills/ # Single plugin bundling all official skills
.claude-plugin/
plugin.json # Plugin manifest
skills/
add-discord/
SKILL.md # Setup instructions; step 1 is "merge the branch"
add-telegram/
SKILL.md
add-slack/
SKILL.md
...
Multiple skills are bundled in one plugin — installing nanoclaw-skills makes all feature skills available at once. Individual skills don't need separate installation.
Each SKILL.md tells Claude to merge the corresponding skill branch as step 1, then walks through interactive setup (env vars, bot creation, etc.).
User runs /add-discord (discovered via marketplace). Claude follows the SKILL.md:
git fetch upstream skill/discordgit merge upstream/skill/discordOr manually:
git fetch upstream skill/discord
git merge upstream/skill/discord
git merge upstream/skill/discord
git merge upstream/skill/telegram
Git handles the composition. If both skills modify the same lines, it's a real conflict and Claude resolves it.
git fetch upstream main
git merge upstream/main
Since skill branches are kept merged-forward with main (see CI section), the user's merged-in skill changes and upstream changes have proper common ancestors.
Users who previously merged a skill branch can check for updates. For each upstream/skill/* branch, check whether the branch has commits that aren't in the user's HEAD:
git fetch upstream
for branch in $(git branch -r | grep 'upstream/skill/'); do
# Check if user has merged this skill at some point
merge_base=$(git merge-base HEAD "$branch" 2>/dev/null) || continue
# Check if the skill branch has new commits beyond what the user has
if ! git merge-base --is-ancestor "$branch" HEAD 2>/dev/null; then
echo "$branch has updates available"
fi
done
This requires no state — it uses git history to determine which skills were previously merged and whether they have new commits.
This logic is available in two ways:
/update-nanoclaw — after merging main, optionally check for skill updates/update-skills — check and merge skill updates independentlyAt any merge step, conflicts may arise. Claude resolves them — reading the conflicted files, understanding the intent of both sides, and producing the correct result. This is what makes the branch approach viable at scale: conflict resolution that previously required human judgment is now automated.
Some skills depend on other skills. E.g., skill/telegram-swarm requires skill/telegram. Dependent skill branches are branched from their parent skill branch, not from main.
This means skill/telegram-swarm includes all of telegram's changes plus its own additions. When a user merges skill/telegram-swarm, they get both — no need to merge telegram separately.
Dependencies are implicit in git history — git merge-base --is-ancestor determines whether one skill branch is an ancestor of another. No separate dependency file is needed.
# Find the merge commit
git log --merges --oneline | grep discord
# Revert it
git revert -m 1 <merge-commit>
This creates a new commit that undoes the skill's changes. Claude can handle the whole flow.
If the user has modified the skill's code since merging (custom changes on top), the revert might conflict — Claude resolves it.
If the user later wants to re-apply the skill, they need to revert the revert first (git treats reverted changes as "already applied and undone"). Claude handles this too.
A GitHub Action runs on every push to main:
skill/* branchesmain into it (merge-forward, not rebase)Why merge-forward instead of rebase:
Why this scales: With a few hundred skills and a few commits to main per day, the CI cost is trivial. Haiku is fast and cheap. The approach that wouldn't have been feasible a year or two ago is now practical because Claude can resolve conflicts at scale.
qwibitai/nanoclaw on GitHub (click the Fork button)git clone https://github.com/<you>/nanoclaw.git
cd nanoclaw
claude
/setup — Claude handles dependencies, authentication, container setup, service configuration, and adds upstream remote if not presentForking is recommended because it gives users a remote to push their customizations to. Clone-only works for trying things out but provides no remote backup.
Users who previously ran git clone https://github.com/qwibitai/nanoclaw.git and have local customizations:
qwibitai/nanoclaw on GitHubgit remote rename origin upstream
git remote add origin https://github.com/<you>/nanoclaw.git
git push --force origin main
--force is needed because the fresh fork's main is at upstream's latest, but the user wants their (possibly behind) version. The fork was just created so there's nothing to lose.origin = their fork, upstream = qwibitai/nanoclawUsers who previously applied skills via the skills-engine/ system have skill code in their tree but no merge commits linking to skill branches. Git doesn't know these changes came from a skill, so merging a skill branch on top would conflict or duplicate.
For new skills going forward: just merge skill branches as normal. No issue.
For existing old-engine skills, two migration paths:
Option A: Per-skill reapply (keep your fork)
Option B: Fresh start (cleanest)
In both cases:
.nanoclaw/ directory (no longer needed)skills-engine/ code will be removed from upstream once all skills are migrated/update-skills only tracks skills applied via branch merge — old-engine skills won't appear in update checksUsers make custom changes directly on their main branch. This is the standard fork workflow — their main IS their customized version.
# Make changes
vim src/config.ts
git commit -am "change trigger word to @Bob"
git push origin main
Custom changes, skills, and core updates all coexist on their main branch. Git handles the three-way merging at each merge step because it can trace common ancestors through the merge history.
Run /add-discord in Claude Code (discovered via the marketplace plugin), or manually:
git fetch upstream skill/discord
git merge upstream/skill/discord
# Follow setup instructions for configuration
git push origin main
If the user is behind upstream's main when they merge a skill branch, the merge might bring in some core changes too (since skill branches are merged-forward with main). This is generally fine — they get a compatible version of everything.
git fetch upstream main
git merge upstream/main
git push origin main
This is the same as the existing /update-nanoclaw skill's merge path.
Run /update-skills or let /update-nanoclaw check after a core update. For each previously-merged skill branch that has new commits, Claude offers to merge the updates.
Users who want to submit a PR to upstream:
git fetch upstream main
git checkout -b my-fix upstream/main
# Make changes
git push origin my-fix
# Create PR from my-fix to qwibitai/nanoclaw:main
Standard fork contribution workflow. Their custom changes stay on their main and don't leak into the PR.
qwibitai/nanoclawmainmainThe contributor opens a normal PR — they don't need to know about skill branches or marketplace repos. They just make code changes and submit.
When a skill PR is reviewed and approved:
skill/<name> branch from the PR's commits:
git fetch origin pull/<PR_NUMBER>/head:skill/<name>
git push origin skill/<name>
CONTRIBUTORS.md (removing all code changes)main (just the contributor addition)qwibitai/nanoclaw-skills)This way:
main stays clean (no skill code)Note: GitHub PRs from forks have "Allow edits from maintainers" checked by default, so the maintainer can push to the contributor's PR branch.
The contributor can optionally provide a SKILL.md (either in the PR or separately). This goes into the marketplace repo and contains:
If the contributor doesn't provide a SKILL.md, the maintainer writes one based on the PR.
Anyone can maintain their own fork with skill branches and their own marketplace repo. This enables a community-driven skill ecosystem without requiring write access to the upstream repo.
A community contributor:
alice/nanoclaw)skill/* branches on their fork with their custom skillsalice/nanoclaw-skills) with a .claude-plugin/marketplace.json and plugin structureIf the community contributor is trusted, they can open a PR to add their marketplace to NanoClaw's .claude/settings.json:
{
"extraKnownMarketplaces": {
"nanoclaw-skills": {
"source": {
"source": "github",
"repo": "qwibitai/nanoclaw-skills"
}
},
"alice-nanoclaw-skills": {
"source": {
"source": "github",
"repo": "alice/nanoclaw-skills"
}
}
}
}
Once merged, all NanoClaw users automatically discover the community marketplace alongside the official one.
/setup and /customize ask users whether they want to enable community skills. If yes, Claude installs community marketplace plugins via claude plugin install:
claude plugin install alice-skills@alice-nanoclaw-skills --scope project
Community skills are hot-loaded and immediately available — no restart needed. Dependent skills are only offered after their prerequisites are met (e.g., community Telegram add-ons only after Telegram is installed).
Users can also browse and install community plugins manually via /plugin.
/plugin.git remote add alice https://github.com/alice/nanoclaw.git
git fetch alice skill/my-cool-feature
git merge alice/skill/my-cool-feature
/plugin marketplace add alice/nanoclaw-skills to discover skills from any source.A flavor is a curated fork of NanoClaw — a combination of skills, custom changes, and configuration tailored for a specific use case (e.g., "NanoClaw for Sales," "NanoClaw Minimal," "NanoClaw for Developers").
qwibitai/nanoclawmain IS the flavorDuring /setup, users are offered a choice of flavors before any configuration happens. The setup skill reads flavors.yaml from the repo (shipped with upstream, always up to date) and presents options:
AskUserQuestion: "Start with a flavor or default NanoClaw?"
If a flavor is chosen:
git remote add <flavor-name> https://github.com/alice/nanoclaw.git
git fetch <flavor-name> main
git merge <flavor-name>/main
Then setup continues normally (dependencies, auth, container, service).
This choice is only offered on a fresh fork — when the user's main matches or is close to upstream's main with no local commits. If /setup detects significant local changes (re-running setup on an existing install), it skips the flavor selection and goes straight to configuration.
After installation, the user's fork has three remotes:
origin — their fork (push customizations here)upstream — qwibitai/nanoclaw (core updates)<flavor-name> — the flavor fork (flavor updates)git fetch <flavor-name> main
git merge <flavor-name>/main
The flavor maintainer keeps their fork updated (merging upstream, updating skills). Users pull flavor updates the same way they pull core updates.
flavors.yaml lives in the upstream repo:
flavors:
- name: NanoClaw for Sales
repo: alice/nanoclaw
description: Gmail + Slack + CRM integration, daily pipeline summaries
maintainer: alice
- name: NanoClaw Minimal
repo: bob/nanoclaw
description: Telegram-only, no container overhead
maintainer: bob
Anyone can PR to add their flavor. The file is available locally when /setup runs since it's part of the cloned repo.
/browse-flavors skill — reads flavors.yaml and presents options at any timenanoclaw-flavor for searchabilityMigration from the old skills engine to branches is complete. All feature skills now live on skill/* branches, and the skills engine has been removed.
| Branch | Base | Description |
|---|---|---|
skill/whatsapp | main | WhatsApp channel |
skill/telegram | main | Telegram channel |
skill/slack | main | Slack channel |
skill/discord | main | Discord channel |
skill/gmail | main | Gmail channel |
skill/voice-transcription | skill/whatsapp | OpenAI Whisper voice transcription |
skill/image-vision | skill/whatsapp | Image attachment processing |
skill/pdf-reader | skill/whatsapp | PDF attachment reading |
skill/local-whisper | skill/voice-transcription | Local whisper.cpp transcription |
skill/ollama-tool | main | Ollama MCP server for local models |
skill/apple-container | main | Apple Container runtime |
skill/reactions | main | WhatsApp emoji reactions |
skills-engine/ directory (entire engine)scripts/apply-skill.ts, scripts/uninstall-skill.ts, scripts/rebase.tsscripts/fix-skill-drift.ts, scripts/validate-all-skills.ts.github/workflows/skill-drift.yml, .github/workflows/skill-pr.ymladd/, modify/, tests/, and manifest.yaml from skill directories.nanoclaw/ state directoryOperational skills (setup, debug, update-nanoclaw, customize, update-skills) remain on main in .claude/skills/.
Before:
git clone https://github.com/qwibitai/NanoClaw.git
cd NanoClaw
claude
After:
1. Fork qwibitai/nanoclaw on GitHub
2. git clone https://github.com/<you>/nanoclaw.git
3. cd nanoclaw
4. claude
5. /setup
/setup)Updates to the setup flow:
upstream remote exists; if not, add it: git remote add upstream https://github.com/qwibitai/nanoclaw.gitorigin points to the user's fork (not qwibitai). If it points to qwibitai, guide them through the fork migration.claude plugin install nanoclaw-skills@nanoclaw-skills --scope project — makes all feature skills available (hot-loaded, no restart)/add-* skills for selected channels.claude/settings.jsonMarketplace configuration so the official marketplace is auto-registered:
{
"extraKnownMarketplaces": {
"nanoclaw-skills": {
"source": {
"source": "github",
"repo": "qwibitai/nanoclaw-skills"
}
}
}
}
The .claude/skills/ directory on main retains only operational skills (setup, debug, update-nanoclaw, customize, update-skills). Feature skills (add-discord, add-telegram, etc.) live in the marketplace repo, installed via claude plugin install during /setup or /customize.
The following can be removed:
skills-engine/ — entire directory (apply, merge, replay, state, backup, etc.)scripts/apply-skill.tsscripts/uninstall-skill.tsscripts/fix-skill-drift.tsscripts/validate-all-skills.ts.nanoclaw/ — state directoryadd/ and modify/ subdirectories from all skill directories.claude/skills/ on main (they now live in the marketplace)Operational skills (setup, debug, update-nanoclaw, customize, update-skills) remain on main in .claude/skills/.
qwibitai/nanoclaw-skills) — single Claude Code plugin bundling SKILL.md files for all feature skillsmain into all skill/* branches on every push to main, using Claude (Haiku) for conflict resolution/update-skills skill — checks for and applies skill branch updates using git historyCONTRIBUTORS.md — tracks skill contributors/update-nanoclaw)The update skill gets simpler with the branch-based approach. The old skills engine required replaying all applied skills after merging core updates — that entire step disappears. Skill changes are already in the user's git history, so git merge upstream/main just works.
What stays the same:
What's removed:
What's added:
/update-skills logicWhy users don't need to re-merge skills after a core update:
When the user merged a skill branch, those changes became part of their git history. When they later merge upstream/main, git performs a normal three-way merge — the skill changes in their tree are untouched, and only core changes are brought in. The merge-forward CI ensures skill branches stay compatible with latest main, but that's for new users applying the skill fresh. Existing users who already merged the skill don't need to do anything.
Users only need to re-merge a skill branch if the skill itself was updated (not just merged-forward with main). The /update-skills check detects this.
Skills are now git branches
We've simplified how skills work in NanoClaw. Instead of a custom skills engine, skills are now git branches that you merge in.
What this means for you:
- Applying a skill:
git fetch upstream skill/discord && git merge upstream/skill/discord- Updating core:
git fetch upstream main && git merge upstream/main- Checking for skill updates:
/update-skills- No more
.nanoclaw/state directory or skills engineWe now recommend forking instead of cloning. This gives you a remote to push your customizations to.
If you currently have a clone with local changes, migrate to a fork:
- Fork
qwibitai/nanoclawon GitHub- Run:
This works even if you're way behind — just push your current state.git remote rename origin upstream git remote add origin https://github.com/<you>/nanoclaw.git git push --force origin mainIf you previously applied skills via the old system, your code changes are already in your working tree — nothing to redo. You can delete the
.nanoclaw/directory. Future skills and updates use the branch-based approach.Discovering skills: Skills are now available through Claude Code's plugin marketplace. Run
/pluginin Claude Code to browse and install available skills.
Contributing skills
To contribute a skill:
- Fork
qwibitai/nanoclaw- Branch from
mainand make your code changes- Open a regular PR
That's it. We'll create a
skill/<name>branch from your PR, add you to CONTRIBUTORS.md, and add the SKILL.md to the marketplace. CI automatically keeps skill branches merged-forward withmainusing Claude to resolve any conflicts.Want to run your own skill marketplace? Maintain skill branches on your fork and create a marketplace repo. Open a PR to add it to NanoClaw's auto-discovered marketplaces — or users can add it manually via
/plugin marketplace add.