The Problem

Running a personal AI assistant that can read WhatsApp, schedule jobs, and keep per‑group memory normally requires a large, tangled codebase (e.g., OpenClaw) and a single host process with broad permissions. That makes security audits hard and customisation painful for a single user.

What This Does

nanoclaw delivers the same core features in a repository that can be read in ≈ 8 minutes. The core runtime lives in src/ (src/index.ts exports the main CLI) and the skills engine (skills‑engine/) provides the plug‑in model used by the Claude Agent SDK. Container isolation is defined in container/Dockerfile and the runner (container/agent‑runner/src/index.ts). All channel adapters (WhatsApp, Discord, Telegram, Gmail, etc.) are implemented as self‑contained Claude skills under .claude/skills/….

How It Is Wired

Execution begins in the skill that boots the Discord integration:

  • Entry point – .claude/skills/add-discord/modify/src/index.ts → function main (line 460).
  • main loads the persisted state (loadState → skills‑engine/state.readState), registers groups, then starts the message loop (startMessageLoop).

From there the call chain is:

main → loadState → readState (skills-engine/state.ts)
      → registerGroup → getAvailableGroups
      → startMessageLoop → connect → on (Discord client)
      → processGroupMessages → saveState → writeState (skills-engine/state.ts)
      → sendMessage → findChannel → client.channels.fetch (network)

Key functions with the widest blast radius:

FunctionCallersCalleesWhy it matters
readState15 places–Centralised file‑based persistence; any change ripples to all skills.
writeState10 places–Writes the same JSON file; errors affect every channel.
isGitRepo9 places–Used by the merge helper; a change may break version‑control handling.
sendMessage6 placesfindChannelDirect network call; bugs affect outbound traffic.
on (event dispatcher)6 places–Event routing hub; mis‑wiring can drop messages.

The hub modules identified by the import graph are skills‑engine/constants.ts, skills‑engine/state.ts, and skills‑engine/types.ts. They are imported by 14–17 other files, so they should stay stable and lightweight.

External effects are limited to:

  • Network – 4 functions (e.g., client.channels.fetch in Discord, WhatsApp API).
  • Filesystem – 54 functions (state files, DB files, container snapshots).
  • Database – 15 functions (SQLite via src/db.ts).
  • Model inference – 1 function (Claude Agent SDK call inside the agent runner).

No circular imports were detected, and the internal call graph contains 485 resolved edges, indicating a fairly flat call hierarchy.

How To Use It

# Clone the repository
git clone https://github.com/moses-y/nanoclaw
cd nanoclaw

# Install Node dependencies (root package.json)
npm ci

# Build the container image used for sandboxed agents
docker build -t nanoclaw-agent -f container/Dockerfile .

# Initialise the assistant (first‑run wizard)
npx ts-node src/index.ts  # or use the provided `claude` wrapper if installed globally

# Follow the on‑screen prompts to authenticate WhatsApp and register groups

Configuration – the project ships .env.example; copy it to .env and fill the required tokens (WhatsApp, Discord, etc.). The skill manifest files (e.g., .claude/skills/add-discord/manifest.yaml) describe each integration’s metadata and are read by the Claude Agent SDK at runtime.

Running a skill – after setup, invoke a skill via the Claude chat interface, e.g., /add-telegram. The skill’s code lives in .claude/skills/add-telegram/modify/src/index.ts and follows the same load‑state → register → loop pattern as the Discord entry point.

Real‑World Use

A user wants a private WhatsApp‑backed assistant that can run a nightly “summarise today’s email” job. They:

  1. Run the quick‑start steps above.
  2. In the Claude chat, execute /add-gmail (skill located at .claude/skills/add-gmail/).
  3. Add a scheduled task via /schedule that calls a custom script stored under scripts/.
  4. The task runs inside an isolated Docker container (container/agent‑runner), reads the Gmail token from the mounted secret, writes a summary to the group’s CLAUDE.md file, and sends the result back through WhatsApp.

All actions are confined to the per‑group filesystem; a compromise in one group cannot affect another.

Code Health & Issues

  • High – Pushes go directly to the default branch (.github/workflows/update-tokens.yml). Fix: use a bot branch and PR workflow.
  • Medium – GitHub token permissions are not scoped (.github/workflows/skill-tests.yml). Fix: add permissions: block.
  • Medium – No Dependabot/Renovate configured. Fix: add .github/dependabot.yml.
  • Medium – Base image not pinned (container/Dockerfile uses node:22-slim). Fix: pin by digest.
  • Medium – No dependency‑vulnerability scan in CI. Fix: add dependency-review-action or osv-scanner.
  • Medium – Large binary (assets/nanoclaw-profile.jpeg 5.6 MiB) stored in Git. Fix: use Git LFS or external storage.
  • Medium – Checkout step keeps credentials (persist-credentials default). Fix: set persist-credentials: false.
  • Low – Workflow jobs lack timeout‑minutes. Fix: add reasonable timeouts.

Additional observations: tests exist (43 files) and run via GitHub Actions; a LICENSE file and npm lockfiles are present; no committed secrets were detected.

The Bottom Line

nanoclaw provides a minimal, container‑isolated Claude assistant that is easy to audit and extend for a single user. The codebase is small and well‑structured, but several hub modules carry a high change impact, and the repo would benefit from tighter CI controls and dependency management. It is suitable for engineers who need a personal AI chat‑assistant and are comfortable modifying TypeScript source directly.