Skip to main content
ClaudeWave
Skill30.7k repo starsupdated 3d ago

add-dial

Add Dial channel integration — a real phone number for SMS and AI voice calls via the Dial platform (getdial.ai). Native adapter — no Chat SDK bridge.

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

SKILL.md

# Add Dial Channel

Adds [Dial](https://getdial.ai) — a real phone number for **SMS and AI voice
calls**. Native adapter (no Chat SDK bridge): both directions go through the
`dial` CLI — outbound via `dial message`, inbound via its command-target daemon. NanoClaw doesn't ship
channels in trunk — this skill copies the Dial adapter, its pairing helper, and
their tests in from the `channels` branch. The `pair-dial` setup step is
maintained in trunk, so it is not copied here.

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. Copy the adapter, pairing helper, and tests

Fetch the `channels` branch and copy the Dial adapter, its pairing store and
user-agent helper (each with its test), and the registration test into place
(overwrite — the branch is canonical):

```nc:copy from-branch:channels
src/channels/dial.ts
src/channels/dial-pairing.ts
src/channels/dial-pairing.test.ts
src/channels/dial-user-agent.ts
src/channels/dial-user-agent.test.ts
src/channels/dial-registration.test.ts
src/channels/dial-grant.test.ts
src/channels/dial-status.test.ts
```

The `dial-cli` container skill is deliberately **not** copied here.
`container/skills/` is mounted read-only into *every* agent container
(`src/container-runner.ts`), and a group with `skills:'all'` picks up whatever
it finds there — so shipping the skill with the adapter would hand it to agents
on installs that never configured Dial. It is installed only by
`/add-dial-tool`, offered under *Add phone superpowers* below, which is the
skill that actually provisions the CLI the skill documents.

`dial.ts` imports `dial-user-agent.js` at module scope, so omitting that helper
breaks the build and every test that loads the channel barrel.

### 2. Register the adapter

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

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

### 3. Register the pairing setup step

Add the `pair-dial` loader to the `STEPS` map in `setup/index.ts`, inside the
dormant marker region (skipped if already present — `pair-dial` ships in core, so
this idempotent-skips on a normal install, but is expressed for a clean-upstream
rebuild). The pairing handshake below spawns this step:

```nc:append to:setup/index.ts at:nanoclaw:setup-steps
'pair-dial': () => import('./pair-dial.js'),
```

### 4. Install the packages

Pinned to exact versions — the supply-chain policy rejects ranges and `latest`.
`qrcode` renders the scannable pairing card:

```nc:dep
qrcode@1.5.4
```

The adapter needs no Dial client library: it shells out to the `dial` CLI, which
this skill already requires for inbound. `@getdial/sdk` was dropped because it
depends on `pubnub`, which pulls react-native, Metro and Hermes into the
lockfile for what is a single send — and the CLI ships in lockstep with the Dial
API, so a contract change arrives as a CLI release rather than breaking a
request pinned in the adapter.

### 5. Build

Build first: it guards the adapter's typed core calls and proves the dependency
is installed.

```nc:run effect:build
pnpm run build
```

### 6. Validate

Then run the one integration test.

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

`dial-registration.test.ts` imports the real channel barrel and asserts the
registry contains `dial` — it goes red if the import line drifts. End-to-end
SMS/voice is verified manually once the service runs.

## Sign in to Dial

### Install the CLI

Dial's CLI owns the account credential (an auth file it writes on sign-in), so
the setup uses the `dial` CLI here. Ensure it's installed — this installs it if
it's missing (for the full onboarding/auth reference, see the `dial-cli` skill or
`curl -fsSL https://getdial.ai/skills.md`):

```nc:run effect:external
command -v dial || curl -fsSL https://getdial.ai/install | bash
```

### Identify this install

Calls this setup makes to Dial identify the install. The `dial` CLI prepends
`DIAL_USER_AGENT` to its own token, so the account's requests stay attributable
to this NanoClaw install in Dial's server-side logs. Resolve the token once
(`nanoclaw/<version>`; an unreadable `package.json` degrades to
`nanoclaw/unknown` rather than blocking the install) and prefix every `dial`
command below with it:

```nc:run capture:dial_ua validate:^nanoclaw/\S+$ effect:fetch
node -p "'nanoclaw/'+(require('./package.json').version||'unknown')" 2>/dev/null || echo nanoclaw/unknown
```

### Pin the CLI path

Now pin the CLI's **absolute** path into `.env`. The adapter shells out to `dial`
to register its inbound command target, and it runs inside the NanoClaw service,
which does not inherit your interactive shell's `PATH`. The CLI usually lands in
a version-manager bin directory (`~/.nvm/versions/node/*/bin`, `~/node/bin`, …)
that the service cannot see, so a bare `dial` fails with `ENOENT`, the command
target is never registered, and the channel comes up connected but deaf — no
inbound SMS or calls, with only a line in `logs/nanoclaw.error.log` to show for
it. `DIAL_CLI_PATH` removes the guesswork; `dial.ts` already prefers it:

```nc:run capture:dial_cli_path validate:^/.+ effect:fetch
command -v dial
```
```nc:env-set
DIAL_CLI_PATH={{dial_cli_path}}
```

### Check the sign-in

Check whether you're already signed in:

```nc:run capture:signed_in=.auth.signedIn validate:^(true|false)$ effect:fetch
DIAL_USER_AGENT={{dial_ua}} dial doctor --json
```

### Skip the reuse question when signed out

If you're **not** signed in, go straight to email verification — default the
choice so the branch guard below stays single-valued:

```nc:run capture:reuse_choice when:signed_in=fal
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.