- Container step: duration hint + 3-line rolling output window with
60s stall detector that offers "keep waiting" vs "ask Claude"
- First chat: reframed as a try-out with sandbox-model explainer
(wakes on message, sleeps when idle, context persists)
- Timezone: auto-detected non-UTC zones now get an explicit
confirm from the user instead of silent persist
- Outro: added always-on warning + prominent "check your DM" banner
when a channel was configured; directive last line
- Discord: always show token-location reminder even when user says
they have one; new "do you have a server?" branch walks through
server creation if not
- All select prompts: custom brightSelect renderer keeps inactive
option labels at full brightness (was dim gray); adds @clack/core
as a direct dep
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trunk ships no channel adapters — /add-telegram installs the package on demand from the channels branch. This dependency was stale and pulled ~2 transitive packages into every fresh install.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Major version for the v2 rewrite. CHANGELOG documents the breaking
changes users will hit on upgrade: new entity model, two-DB session
split, `bash nanoclaw.sh` as default install, channels/providers
relocated to sibling branches, three-level isolation, Apple Container
removed from default setup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wraps the scripted setup flow in a branded, friendly UI. Each step runs
under a clack spinner with elapsed time; child stdout/stderr is captured
quietly and dumped only on failure. Interactive children (token paste,
Anthropic OAuth) bypass the spinner and inherit the TTY.
- intro: NanoClaw wordmark + brand-cyan accent chip, truecolor with
kleur fallback and NO_COLOR / non-TTY awareness
- pair-telegram: emits PAIR_TELEGRAM_CODE / _ATTEMPT status blocks only;
auto.ts renders clack notes + "received X — doesn't match" checkpoints
- streaming status-block parser handles mid-step events without waiting
for the child to exit
- terminal-block detection now finds any block with a STATUS field
(handles MOUNTS emitting CONFIGURE_MOUNTS, etc.) and treats 'skipped'
as a success variant with an optional friendlier label
Also fixes a latent bash bug where `$VAR…` (unbraced followed by a
multi-byte Unicode character) pulled ellipsis bytes into the variable
name lookup and tripped `set -u`. Braced `${VAR}` in add-telegram.sh
and register-claude-token.sh.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`pnpm run setup:auto` chains the deterministic setup steps (environment
→ timezone → container → mounts → service → verify) by spawning the
existing per-step CLI and parsing its status blocks. Config via env:
NANOCLAW_TZ, NANOCLAW_SKIP.
Credentials + channel install + /manage-channels stay interactive —
verify reports what's left and exits 0 rather than failing the driver.
Also have the container step try to start Docker when it's installed
but not running (open -a Docker on macOS, sudo systemctl start docker
on Linux) and poll `docker info` for up to 60s before giving up. Both
/setup and setup:auto pick this up automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First default channel that ships with main. Unix-socket adapter + thin
client; plugs into the running daemon rather than spawning its own host.
## src/channels/cli.ts
- ChannelAdapter with channelType='cli', platformId='local'.
- setup() unlinks any stale socket, listens on $DATA_DIR/cli.sock (mode 0600
so only the local user can connect).
- On client connect: reads newline-delimited JSON ({"text": "..."}) and
calls config.onInbound('local', null, {id, kind:'chat', content, ts}).
- deliver() writes {"text": <body>} back to the connected socket; silently
no-ops when no client is attached (outbound row still persists).
- Single-client policy: a second connection supersedes the first with a
[superseded] notice.
- teardown() closes the client, closes the server, removes the socket file.
## scripts/chat.ts + pnpm run chat
One-shot client:
- pnpm run chat <message...>
- Connects to the socket, writes one JSON line with the message.
- Reads replies; exits 2s after the first reply lands (hard timeout 120s).
- ENOENT/ECONNREFUSED prints a hint to start the daemon.
## scripts/init-first-agent.ts
- Fix stale imports after earlier module extractions (permissions +
agent-to-agent moved their DB helpers into modules/).
- After wiring the DM channel, also create cli/local messaging_group
(unknown_sender_policy='public' — local socket perms handle auth) and
wire it to the same agent. User can `pnpm run chat` immediately.
## package.json
- Add "chat": "tsx scripts/chat.ts" script.
## Validation
- pnpm run build clean.
- pnpm test — 137 host tests pass.
- bun test in container/agent-runner — 17 pass.
- Service boot logs: "CLI channel listening" + "Channel adapter started
channel=cli type=cli". Clean SIGTERM shutdown; socket file removed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
setup/groups.ts is whatsapp-only — its inline syncScript imports baileys
and pino to fetch group metadata via Baileys.groupFetchAllParticipating.
On trunk it was a no-op for non-whatsapp users (returned early without
auth) and the only thing keeping pino alive.
Removed:
- setup/groups.ts (lives on `channels` branch; restored by /add-whatsapp-v2)
- `groups` STEPS entry from setup/index.ts
- pino from package.json (no longer used outside the moved file)
/add-whatsapp-v2 skill updated to copy setup/groups.ts and register both
groups + whatsapp-auth in setup/index.ts STEPS, install pino@9.6.0 along
with baileys + qrcode.
Verified: build clean, 326 host tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The only use was channel-registry.ts checking `err instanceof NetworkError`
to retry transient setup failures. Switched to a duck-type predicate
(`err.name === 'NetworkError'`) so the dep is no longer needed at trunk
level. Channel skills bring it in transitively when they install their
Chat SDK adapter package.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v2 ships with no channels baked in. All channel adapters (discord, slack,
telegram + helpers, whatsapp, whatsapp-cloud, gchat, github, imessage,
linear, matrix, resend, teams, webex) and their channel-specific setup
steps (pair-telegram, whatsapp-auth) now live on the `channels` branch
and get copied in via /add-*-v2 skills.
Removed:
- src/channels/{discord,slack,telegram*,whatsapp*,gchat,github,imessage,linear,matrix,resend,teams,webex}.ts
- setup/{pair-telegram,whatsapp-auth}.ts
- 14 channel-specific deps from package.json (@chat-adapter/*, @beeper/*,
@bitbasti/*, @resend/chat-sdk-adapter, @whiskeysockets/baileys,
chat-adapter-imessage, qrcode, @chat-adapter/state-memory unused)
- Their corresponding STEPS entries from setup/index.ts
- Channel imports from src/channels/index.ts
Kept:
- Channel infra: adapter.ts, channel-registry.ts (+ test), chat-sdk-bridge.ts,
ask-question.ts, an empty-imports index.ts
- Chat SDK runtime (`chat`) for channels that copy in via Chat SDK bridge
- @chat-adapter/shared promoted from transitive to direct dep
(channel-registry.ts uses NetworkError from it)
Verified: pnpm run build clean, 326 host tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Baileys 6.7.21 silently failed the pairing handshake. Upgrade to 6.17.16
which fixes this. Three related issues:
1. proto is no longer a named ESM export in 6.17.x — use createRequire
to import via CJS (matching the proven v1 pattern).
2. Setup auth script didn't handle the 515 stream restart that WhatsApp
sends after successful pairing. Refactored to reconnect (matching v1's
connectSocket(isReconnect) pattern) instead of hanging until timeout.
3. Added succeeded guard and process.exit(0) to prevent timeout race
after successful auth.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>