Skip to main content
This page is for contributing to nanocoai/nanoclaw itself — fixing bugs, simplifying code, or adding a skill to the upstream repo. If you just want to extend your own installation, you don’t need any of this: see Extending NanoClaw. The upstream CONTRIBUTING.md is canonical for process. The short version: accepted — bug fixes, security fixes, simplifications, reducing code. Not accepted — features, capabilities, compatibility, enhancements. Those should be skills, so users who don’t want them never inherit the code. One thing per PR, and search open PRs and issues before starting.

Repo layout

nanoclaw
src — host process: routing, containers, channel registry, SQLite state (Node)
container — agent image: Dockerfile, entrypoint, agent-runner (Bun), container skills
setup — setup wizard: environment checks, channel installers, service registration
.claude/skills — host-side skills: /setup, /debug, /add-telegram, and the rest
scripts — dev and maintenance scripts: chat.ts, migrations, e2e harnesses
docs — developer docs: architecture, isolation model, skills model, skill guidelines
repo-tokens — GitHub Action that keeps the token-count badge updated

The whole repo fits in context

The token badge in the README currently reads ~185k tokens for the runtime surface (mainly src/, container/agent-runner/src/, the Dockerfile, and CLAUDE.md — host tests excluded). That’s deliberate: the codebase is kept small enough for a coding agent to hold it in context, which is also why feature PRs are redirected to skills. The badge is auto-generated — the update-tokens.yml workflow runs the repo-tokens/ action on every push to main that touches src/, container/, launchd/, or CLAUDE.md and commits the new badge. Don’t update it by hand.

Dev loop

NanoClaw uses pnpm for the host and Bun for the agent-runner inside the container — two separate package trees.
# Host (Node + pnpm, repo root)
pnpm install
pnpm run dev          # run the host with hot reload (tsx)
pnpm run build        # compile TypeScript (tsc)
pnpm test             # host tests (vitest run)
pnpm run test:watch   # vitest in watch mode
pnpm run lint         # eslint src/
pnpm run typecheck    # tsc --noEmit
pnpm run format       # prettier --write

# Container image
./container/build.sh  # rebuild nanoclaw-agent:latest

# Agent-runner (Bun, separate tree)
cd container/agent-runner
bun install
bun test              # container tests (bun:test)
CI on every PR runs format:check, typechecks for both trees, host tests under vitest, and agent-runner tests under bun test.

Test layout

Tests are split across three configs because the two runtimes can’t share one:
  • vitest.config.ts — host tests: src/**/*.test.ts, setup/**/*.test.ts, scripts/**/*.test.ts. Run with pnpm test.
  • vitest.skills.config.ts — skill registration tests: .claude/skills/**/tests/*.test.ts. Run with pnpm exec vitest run --config vitest.skills.config.ts.
  • container/agent-runner — Bun tests, because they depend on bun:sqlite, which vitest (Node) can’t load. Import from bun:test, not vitest.

The three-branch model

Trunk (main) ships no specific channel adapter and no non-default provider. The adapters live on two long-lived registry branches, kept in sync with main:
  • channels — Discord, Slack, Telegram, WhatsApp, Teams, and the other channel adapters, plus their tests and setup steps. Installed via /add-<channel> skills.
  • providers — non-default agent providers like OpenCode. Installed via /add-opencode.
Install is an additive fetch (git show origin/channels:<path> > <path>), never a git mergedocs/skills-model.md explains why. Where your PR goes depends on what you’re contributing:
  • Bug fix or simplification → ordinary PR against main.
  • Channel or provider adapter → fork, branch from main, build the adapter per docs/skill-guidelines.md (self-registering module, one appended barrel import, a registration test against the real barrel), add a SKILL.md with the fetch-and-copy steps and a REMOVE.md that reverses every change, then open a PR against main. Maintainers land the code on the registry branch from your work — you don’t push to channels or providers directly.
  • Utility, operational, or container skill → PR against main; the skill lives in .claude/skills/ (or container/skills/ for container skills). CONTRIBUTING.md describes all four skill types.

Releases

Maintainers cut a GitHub Release for package.json version bumps that land on main: a vX.Y.Z tag, a CHANGELOG.md entry, and a release body that mirrors it. Every release is stable — there’s no pre-release channel — and published tags are never moved, so pinning a tag is the reproducible choice for forks. Details in RELEASING.md.

Where to go

  • Issues and PRs: nanocoai/nanoclaw — link related issues with Closes #N, test on a fresh clone, keep descriptions short.
  • Process: CONTRIBUTING.md is the source of truth, including the PR-template checkboxes that auto-apply labels.
  • Discussion: the NanoClaw Discord — good for checking alignment before you build.
Last modified on June 10, 2026