Skip to main content
ClaudeWave
Skill30.7k repo starsupdated yesterday

add-imessage

This Claude Code skill integrates iMessage messaging into NanoClaw via the Chat SDK by copying the iMessage adapter from a separate branch and installing its dependencies. Use this when adding native iMessage channel support to a NanoClaw instance, selecting either local mode for macOS with Full Disk Access permissions or remote mode via the Photon API for other platforms.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/nanocoai/nanoclaw /tmp/add-imessage && cp -r /tmp/add-imessage/.claude/skills/add-imessage ~/.claude/skills/add-imessage
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Add iMessage

NanoClaw talks to iMessage through a single **`imessage`** channel with two
pluggable backends:

- **Local (this Mac)** — the Chat SDK bridge over `chat-adapter-imessage`,
  reading this Mac's signed-in iMessage account (`chat.db`). macOS only; the
  Node binary needs Full Disk Access.
- **Hosted iMessage (via photon.codes)** — a native adapter over Photon's
  `spectrum-ts` gRPC stream. The hosted service owns the iMessage line, so
  there's no Mac relay, webhook, or public URL. Works on any OS, and a
  device-login flow provisions everything for you.

Both register the same `imessage` channel type; only one runs per install.
NanoClaw doesn't ship channels in trunk — this skill copies the unified
`imessage` adapter in from the `channels` branch. Full reference:
[docs/imessage.md](docs.md).

The mechanical steps under **Apply** carry `nc:` directive fences: an agent reads
the prose and applies them, and a parser can apply them deterministically from
the same document. Every directive is idempotent, so the whole skill is safe to
re-run; anything a parser can't apply falls back to the prose beside it.

## Apply

### 1. Choose a backend

Pick the backend first — it decides which package gets installed and which
walkthrough runs below (the other backend's steps are skipped):

```nc:prompt backend validate:^(local|hosted)$
How should iMessage run — `local` (this Mac's signed-in iMessage account; macOS only, needs Full Disk Access) or `hosted` (a managed line via photon.codes; works on any OS)?
```

The local backend only works on a Mac — it reads this machine's iMessage
`chat.db` directly, and there is no such database off macOS. On any other OS,
stop here and choose `hosted` instead; otherwise you'd write a local config
that can never receive a message:

```nc:run effect:check when:backend=local
[ "$(uname)" = Darwin ]
```

### 2. Copy the adapter

Fetch the `channels` branch and copy the unified iMessage adapter and its tests
into `src/channels/`:

```nc:copy from-branch:channels
src/channels/imessage.ts
src/channels/imessage.test.ts
src/channels/imessage-registration.test.ts
```

### 3. Register the adapter

Append the self-registration import to the channel barrel (skipped if the line
is already present). This one line is the skill's only reach-in into core:

```nc:append to:src/channels/index.ts
import './imessage.js';
```

### 4. Install the chosen backend's package

Pinned to an exact version — the supply-chain policy rejects ranges and
`latest`. Install only the chosen backend's package.

**Local** — the Chat SDK iMessage adapter:

```nc:dep when:backend=local
chat-adapter-imessage@0.1.1
```

**Hosted** — Photon's Spectrum SDK:

```nc:dep when:backend=hosted
spectrum-ts@11.0.0
```

> Pin exactly. `spectrum-ts` ships breaking majors (v11 is what the adapter
> targets); don't `@latest`. NanoClaw's pnpm gate (`minimumReleaseAge`) requires
> a version ≥3 days old — both pins clear it. A fresher pin needs human sign-off
> before a `minimumReleaseAgeExclude` entry (CLAUDE.md → Supply Chain Security).

### 5. Build and validate

Build guards the typed `createChatSdkBridge(...)` core call used by the local
backend, and the registration test proves the channel is wired:

```nc:run effect:build
pnpm run build
```
```nc:run effect:test
pnpm exec vitest run src/channels/imessage-registration.test.ts
```

Both must be clean. `imessage-registration.test.ts` imports the real channel
barrel and asserts the registry contains `imessage` — it goes red if the
`import './imessage.js';` line is missing or the barrel fails to evaluate. The
adapter loads neither backend's SDK at import (hosted `spectrum-ts` only in
`setup()`, local `chat-adapter-imessage` only in the factory), so the test
needs no package.

For the hosted backend, also run the full adapter suite — it includes an
integration block that exercises the real installed `spectrum-ts` (version,
exports, builders) and auto-skips when the package is absent:

```nc:run effect:test when:backend=hosted
pnpm exec vitest run src/channels/imessage.test.ts
```

## Local backend: Full Disk Access (macOS)

The adapter reads this Mac's `chat.db`, which requires Full Disk Access granted
to the Node binary the host runs under. The Node path is buried deep (e.g.
`~/.nvm/versions/node/v22.x.x/bin/node`), so open its folder in Finder to make
the drag-and-drop target obvious. Harmless off a desktop (SSH/headless) — it
just no-ops:

```nc:run effect:external when:backend=local
open "$(dirname "$(which node)")" 2>/dev/null || true
```

Then tell the user:

```nc:operator when:backend=local
Grant Full Disk Access to Node so iMessage can read your chat history:
1. Open System Settings > Privacy & Security > Full Disk Access.
2. Click +, then drag the "node" file from the Finder window that just opened.
3. Toggle it on, then come back here.
```

Stop and wait for the user to confirm Full Disk Access is granted before
continuing.

Now select the local backend in `.env`. The configure script owns this
upsert-and-remove (a plain set-if-absent env write can neither replace a stale
value nor delete a key, and a lingering hosted selector would shadow the
choice):

```nc:run effect:external when:backend=local
bash setup/channels/imessage-configure.sh local
```

## Hosted backend: device login (via photon.codes)

The provisioning flow needs the phone number you send iMessages from — it
registers that number with your project so the hosted line recognises you:

```nc:prompt owner_handle normalize:trim validate:^\+\d{8,15}$ when:backend=hosted
The phone number you iMessage from, in E.164 format — + followed by country code and number, no spaces or dashes (e.g. +14155551234).
```

Tell the user what's about to happen:

```nc:operator when:backend=hosted
Connect your hosted iMessage line (photon.codes):
1. A login URL and a short code will print below.
2. Open the URL in a browser, approve the device, and enter the code.
3. Setup then registers your number and prints
add-atomic-chat-toolSkill

Add Atomic Chat MCP server so the container agent can call local models served by the Atomic Chat desktop app via its OpenAI-compatible API.

add-codexSkill

Use Codex (OpenAI's codex app-server) as a full agent provider — planning, tool orchestration, MCP tools, server-side history, session resume — alongside or instead of Claude. ChatGPT subscription or OpenAI API key, vault-only via OneCLI. Per-group via `ncl groups config update --provider codex`. Distinct from using OpenAI as an MCP tool (where Claude remains the planner).

add-dashboardSkill

Add a monitoring dashboard to NanoClaw. Installs @nanoco/nanoclaw-dashboard and a pusher that sends periodic JSON snapshots.

add-deltachatSkill

Add DeltaChat channel integration via @deltachat/stdio-rpc-server. Native adapter — no Chat SDK bridge. Email-based messaging with end-to-end encryption.

add-discordSkill

Add Discord bot channel integration via Chat SDK.

add-emacsSkill

Add Emacs as a channel. Opens an interactive chat buffer and org-mode integration so you can talk to NanoClaw from within Emacs (Doom, Spacemacs, or vanilla). Local HTTP bridge — no bot token or external service needed.

add-gcal-toolSkill

Add Google Calendar as an MCP tool (list calendars, list/search/create events, free/busy queries) using OneCLI-managed OAuth. Multi-calendar and multi-account supported. Mirrors /add-gmail-tool's stub pattern — no raw credentials ever reach the container; OneCLI injects real tokens at request time.

add-gchatSkill

Add Google Chat channel integration via Chat SDK.