* refactor: implement channel architecture and dynamic setup - Introduced ChannelRegistry for dynamic channel loading - Decoupled WhatsApp from core index.ts and config.ts - Updated setup wizard to support ENABLED_CHANNELS selection - Refactored IPC and group registration to be channel-aware - Verified with 359 passing tests and clean typecheck * style: fix formatting in config.ts to pass CI * refactor(setup): full platform-agnostic transformation - Harmonized all instructional text and help prompts - Implemented conditional guards for WhatsApp-specific steps - Normalized CLI terminology across all 4 initial channels - Unified troubleshooting and verification logic - Verified 369 tests pass with clean typecheck * feat(skills): transform WhatsApp into a pluggable skill - Created .claude/skills/add-whatsapp with full 5-phase interactive setup - Fixed TS7006 'implicit any' error in IpcDeps - Added auto-creation of STORE_DIR to prevent crashes on fresh installs - Verified with 369 passing tests and clean typecheck * refactor(skills): move WhatsApp from core to pluggable skill - Move src/channels/whatsapp.ts to add-whatsapp skill add/ folder - Move src/channels/whatsapp.test.ts to skill add/ folder - Move src/whatsapp-auth.ts to skill add/ folder - Create modify/ for barrel file (src/channels/index.ts) - Create tests/ with skill package validation test - Update manifest with adds/modifies lists - Remove WhatsApp deps from core package.json (now skill-managed) - Remove WhatsApp-specific ghost language from types.ts - Update SKILL.md to reflect skill-apply workflow Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(skills): move setup/whatsapp-auth.ts into WhatsApp skill The WhatsApp auth setup step is channel-specific — move it from core to the add-whatsapp skill so core stays minimal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(skills): convert Telegram skill to pluggable channel pattern Replace the old direct-integration approach (modifying src/index.ts, src/config.ts, src/routing.test.ts) with self-registration via the channel registry, matching the WhatsApp skill pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(skills): fix add-whatsapp build failure and improve auth flow - Add missing @types/qrcode-terminal to manifest npm_dependencies (build failed after skill apply without it) - Make QR-browser the recommended auth method (terminal QR too small, pairing codes expire too fast) - Remove "replace vs alongside" question — channels are additive - Add pairing code retry guidance and QR-browser fallback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove hardcoded WhatsApp default and stale Baileys comment - ENABLED_CHANNELS now defaults to empty (fresh installs must configure channels explicitly via /setup; existing installs already have .env) - Remove Baileys-specific comment from storeMessageDirect() in db.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(skills): convert Discord, Slack, Gmail skills to pluggable channel pattern All channel skills now use the same self-registration pattern: - registerChannel() factory at module load time - Barrel file append (src/channels/index.ts) instead of orchestrator modifications - No more *_ONLY flags (DISCORD_ONLY, SLACK_ONLY) — use ENABLED_CHANNELS instead - Removed ~2500 lines of old modify/ files (src/index.ts, src/config.ts, src/routing.test.ts) Gmail retains its container-runner.ts and agent-runner modifications (MCP mount + server config) since those are independent of channel wiring. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: use getRegisteredChannels instead of ENABLED_CHANNELS Remove the ENABLED_CHANNELS env var entirely. The orchestrator now iterates getRegisteredChannelNames() from the channel registry — channels self-register via barrel imports and their factories return null when credentials are missing, so unconfigured channels are skipped automatically. Deleted setup/channels.ts (and its tests) since its sole purpose was writing ENABLED_CHANNELS to .env. Refactored verify, groups, and environment setup steps to detect channels by credential presence instead of reading ENABLED_CHANNELS. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add breaking change notice and whatsapp migration instructions CHANGELOG.md documents the pluggable channel architecture shift and provides migration steps for existing WhatsApp users. CLAUDE.md updated: Quick Context reflects multi-channel architecture, Key Files lists registry.ts instead of whatsapp.ts, and a new Troubleshooting section directs users to /add-whatsapp if WhatsApp stops connecting after upgrade. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: rewrite READMEs for pluggable multi-channel architecture Reflects the architectural shift from a hardcoded WhatsApp bot to a pluggable channel platform. Adds upgrading notice, Mermaid architecture diagram, CI/License/TypeScript/PRs badges, and clarifies that slash commands run inside the Claude Code CLI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: move pluggable channel architecture details to SPEC.md Revert READMEs to original tone with only two targeted changes: - Add upgrading notice for WhatsApp breaking change - Mention pluggable channels in "What It Supports" Move Mermaid diagram, channel registry internals, factory pattern explanation, and self-registration walkthrough into docs/SPEC.md. Update stale WhatsApp-specific references in SPEC.md to be channel-agnostic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: move upgrading notice to CHANGELOG, add changelog link Remove the "Upgrading from Pre-Pluggable Versions" section from README.md — breaking change details belong in the CHANGELOG. Add a Changelog section linking to CHANGELOG.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: expand CHANGELOG with full PR #500 changes Cover all changes: channel registry, WhatsApp moved to skill, removed core dependencies, all 5 skills simplified, orchestrator refactored, setup decoupled. Use Claude Code CLI instructions for migration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: bump version to 1.2.0 for pluggable channel architecture Minor version bump — new functionality (pluggable channels) with a managed migration path for existing WhatsApp users. Update version references in CHANGELOG and update skill. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix skill application * fix: use slotted barrel file to prevent channel merge conflicts Pre-allocate a named comment slot for each channel in src/channels/index.ts, separated by blank lines. Each skill's modify file only touches its own slot, so three-way merges never conflict when applying multiple channels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve real chat ID during setup for token-based channels Instead of registering with `pending@telegram` (which never matches incoming messages), the setup skill now runs an inline bot that waits for the user to send /chatid, capturing the real chat ID before registration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: setup delegates to channel skills, fix group sync and Discord metadata - Restructure setup SKILL.md to delegate channel setup to individual channel skills (/add-whatsapp, /add-telegram, etc.) instead of reimplementing auth/registration inline with broken placeholder JIDs - Move channel selection to step 5 where it's immediately acted on - Fix setup/groups.ts: write sync script to temp file instead of passing via node -e which broke on shell escaping of newlines - Fix Discord onChatMetadata missing channel and isGroup parameters - Add .tmp-* to .gitignore for temp sync script cleanup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: align add-whatsapp skill with main setup patterns Add headless detection for auth method selection, structured inline error handling, dedicated number DM flow, and reorder questions to match main's trigger-first flow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add missing auth script to package.json The add-whatsapp skill adds src/whatsapp-auth.ts but doesn't add the corresponding npm script. Setup and SKILL.md reference `npm run auth` for WhatsApp QR terminal authentication. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: update Discord skill tests to match onChatMetadata signature The onChatMetadata callback now takes 5 arguments (jid, timestamp, name, channel, isGroup) but the Discord skill tests only expected 3. This caused skill application to roll back on test failure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: replace 'pluggable' jargon with clearer language User-facing text now says "multi-channel" or describes what it does. Developer-facing text uses "self-registering" or "channel registry". Also removes extra badge row from README. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: align Chinese README with English version Remove extra badges, replace pluggable jargon, remove upgrade section (now in CHANGELOG), add missing intro line and changelog section, fix setup FAQ answer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: warn on installed-but-unconfigured channels instead of silent skip Channels with missing credentials now emit WARN logs naming the exact missing variable, so misconfigurations surface instead of being hidden. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: simplify changelog to one-liner with compare link Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add isMain flag and channel-prefixed group folders Replace MAIN_GROUP_FOLDER constant with explicit isMain boolean on RegisteredGroup. Group folders now use channel prefix convention (e.g., whatsapp_main, telegram_family-chat) to prevent cross-channel collisions. - Add isMain to RegisteredGroup type and SQLite schema (with migration) - Replace all folder-based main group checks with group.isMain - Add --is-main flag to setup/register.ts - Strip isMain from IPC payload (defense in depth) - Update MCP tool description for channel-prefixed naming - Update all channel SKILL.md files and documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: gavrielc <gabicohen22@yahoo.com> Co-authored-by: Koshkoshinski <daniel.milliner@gmail.com>
243 lines
8.4 KiB
Markdown
243 lines
8.4 KiB
Markdown
---
|
|
name: add-gmail
|
|
description: Add Gmail integration to NanoClaw. Can be configured as a tool (agent reads/sends emails when triggered from WhatsApp) or as a full channel (emails can trigger the agent, schedule tasks, and receive replies). Guides through GCP OAuth setup and implements the integration.
|
|
---
|
|
|
|
# Add Gmail Integration
|
|
|
|
This skill adds Gmail support to NanoClaw — either as a tool (read, send, search, draft) or as a full channel that polls the inbox.
|
|
|
|
## Phase 1: Pre-flight
|
|
|
|
### Check if already applied
|
|
|
|
Read `.nanoclaw/state.yaml`. If `gmail` is in `applied_skills`, skip to Phase 3 (Setup). The code changes are already in place.
|
|
|
|
### Ask the user
|
|
|
|
Use `AskUserQuestion`:
|
|
|
|
AskUserQuestion: Should incoming emails be able to trigger the agent?
|
|
|
|
- **Yes** — Full channel mode: the agent listens on Gmail and responds to incoming emails automatically
|
|
- **No** — Tool-only: the agent gets full Gmail tools (read, send, search, draft) but won't monitor the inbox. No channel code is added.
|
|
|
|
## Phase 2: Apply Code Changes
|
|
|
|
### Initialize skills system (if needed)
|
|
|
|
If `.nanoclaw/` directory doesn't exist yet:
|
|
|
|
```bash
|
|
npx tsx scripts/apply-skill.ts --init
|
|
```
|
|
|
|
### Path A: Tool-only (user chose "No")
|
|
|
|
Do NOT run the full apply script. Only two source files need changes. This avoids adding dead code (`gmail.ts`, `gmail.test.ts`, index.ts channel logic, routing tests, `googleapis` dependency).
|
|
|
|
#### 1. Mount Gmail credentials in container
|
|
|
|
Apply the changes described in `modify/src/container-runner.ts.intent.md` to `src/container-runner.ts`: import `os`, add a conditional read-write mount of `~/.gmail-mcp` to `/home/node/.gmail-mcp` in `buildVolumeMounts()` after the session mounts.
|
|
|
|
#### 2. Add Gmail MCP server to agent runner
|
|
|
|
Apply the changes described in `modify/container/agent-runner/src/index.ts.intent.md` to `container/agent-runner/src/index.ts`: add `gmail` MCP server (`npx -y @gongrzhe/server-gmail-autoauth-mcp`) and `'mcp__gmail__*'` to `allowedTools`.
|
|
|
|
#### 3. Record in state
|
|
|
|
Add `gmail` to `.nanoclaw/state.yaml` under `applied_skills` with `mode: tool-only`.
|
|
|
|
#### 4. Validate
|
|
|
|
```bash
|
|
npm run build
|
|
```
|
|
|
|
Build must be clean before proceeding. Skip to Phase 3.
|
|
|
|
### Path B: Channel mode (user chose "Yes")
|
|
|
|
Run the full skills engine to apply all code changes:
|
|
|
|
```bash
|
|
npx tsx scripts/apply-skill.ts .claude/skills/add-gmail
|
|
```
|
|
|
|
This deterministically:
|
|
|
|
- Adds `src/channels/gmail.ts` (GmailChannel class with self-registration via `registerChannel`)
|
|
- Adds `src/channels/gmail.test.ts` (unit tests)
|
|
- Appends `import './gmail.js'` to the channel barrel file `src/channels/index.ts`
|
|
- Three-way merges Gmail credentials mount into `src/container-runner.ts` (~/.gmail-mcp -> /home/node/.gmail-mcp)
|
|
- Three-way merges Gmail MCP server into `container/agent-runner/src/index.ts` (@gongrzhe/server-gmail-autoauth-mcp)
|
|
- Installs the `googleapis` npm dependency
|
|
- Records the application in `.nanoclaw/state.yaml`
|
|
|
|
If the apply reports merge conflicts, read the intent files:
|
|
|
|
- `modify/src/channels/index.ts.intent.md` — what changed for the barrel file
|
|
- `modify/src/container-runner.ts.intent.md` — what changed for container-runner.ts
|
|
- `modify/container/agent-runner/src/index.ts.intent.md` — what changed for agent-runner
|
|
|
|
#### Add email handling instructions
|
|
|
|
Append the following to `groups/main/CLAUDE.md` (before the formatting section):
|
|
|
|
```markdown
|
|
## Email Notifications
|
|
|
|
When you receive an email notification (messages starting with `[Email from ...`), inform the user about it but do NOT reply to the email unless specifically asked. You have Gmail tools available — use them only when the user explicitly asks you to reply, forward, or take action on an email.
|
|
```
|
|
|
|
#### Validate
|
|
|
|
```bash
|
|
npm test
|
|
npm run build
|
|
```
|
|
|
|
All tests must pass (including the new gmail tests) and build must be clean before proceeding.
|
|
|
|
## Phase 3: Setup
|
|
|
|
### Check existing Gmail credentials
|
|
|
|
```bash
|
|
ls -la ~/.gmail-mcp/ 2>/dev/null || echo "No Gmail config found"
|
|
```
|
|
|
|
If `credentials.json` already exists, skip to "Build and restart" below.
|
|
|
|
### GCP Project Setup
|
|
|
|
Tell the user:
|
|
|
|
> I need you to set up Google Cloud OAuth credentials:
|
|
>
|
|
> 1. Open https://console.cloud.google.com — create a new project or select existing
|
|
> 2. Go to **APIs & Services > Library**, search "Gmail API", click **Enable**
|
|
> 3. Go to **APIs & Services > Credentials**, click **+ CREATE CREDENTIALS > OAuth client ID**
|
|
> - If prompted for consent screen: choose "External", fill in app name and email, save
|
|
> - Application type: **Desktop app**, name: anything (e.g., "NanoClaw Gmail")
|
|
> 4. Click **DOWNLOAD JSON** and save as `gcp-oauth.keys.json`
|
|
>
|
|
> Where did you save the file? (Give me the full path, or paste the file contents here)
|
|
|
|
If user provides a path, copy it:
|
|
|
|
```bash
|
|
mkdir -p ~/.gmail-mcp
|
|
cp "/path/user/provided/gcp-oauth.keys.json" ~/.gmail-mcp/gcp-oauth.keys.json
|
|
```
|
|
|
|
If user pastes JSON content, write it to `~/.gmail-mcp/gcp-oauth.keys.json`.
|
|
|
|
### OAuth Authorization
|
|
|
|
Tell the user:
|
|
|
|
> I'm going to run Gmail authorization. A browser window will open — sign in and grant access. If you see an "app isn't verified" warning, click "Advanced" then "Go to [app name] (unsafe)" — this is normal for personal OAuth apps.
|
|
|
|
Run the authorization:
|
|
|
|
```bash
|
|
npx -y @gongrzhe/server-gmail-autoauth-mcp auth
|
|
```
|
|
|
|
If that fails (some versions don't have an auth subcommand), try `timeout 60 npx -y @gongrzhe/server-gmail-autoauth-mcp || true`. Verify with `ls ~/.gmail-mcp/credentials.json`.
|
|
|
|
### Build and restart
|
|
|
|
Clear stale per-group agent-runner copies (they only get re-created if missing, so existing copies won't pick up the new Gmail server):
|
|
|
|
```bash
|
|
rm -r data/sessions/*/agent-runner-src 2>/dev/null || true
|
|
```
|
|
|
|
Rebuild the container (agent-runner changed):
|
|
|
|
```bash
|
|
cd container && ./build.sh
|
|
```
|
|
|
|
Then compile and restart:
|
|
|
|
```bash
|
|
npm run build
|
|
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
|
|
# Linux: systemctl --user restart nanoclaw
|
|
```
|
|
|
|
## Phase 4: Verify
|
|
|
|
### Test tool access (both modes)
|
|
|
|
Tell the user:
|
|
|
|
> Gmail is connected! Send this in your main channel:
|
|
>
|
|
> `@Andy check my recent emails` or `@Andy list my Gmail labels`
|
|
|
|
### Test channel mode (Channel mode only)
|
|
|
|
Tell the user to send themselves a test email. The agent should pick it up within a minute. Monitor: `tail -f logs/nanoclaw.log | grep -iE "(gmail|email)"`.
|
|
|
|
Once verified, offer filter customization via `AskUserQuestion` — by default, only emails in the Primary inbox trigger the agent (Promotions, Social, Updates, and Forums are excluded). The user can keep this default or narrow further by sender, label, or keywords. No code changes needed for filters.
|
|
|
|
### Check logs if needed
|
|
|
|
```bash
|
|
tail -f logs/nanoclaw.log
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Gmail connection not responding
|
|
|
|
Test directly:
|
|
|
|
```bash
|
|
npx -y @gongrzhe/server-gmail-autoauth-mcp
|
|
```
|
|
|
|
### OAuth token expired
|
|
|
|
Re-authorize:
|
|
|
|
```bash
|
|
rm ~/.gmail-mcp/credentials.json
|
|
npx -y @gongrzhe/server-gmail-autoauth-mcp
|
|
```
|
|
|
|
### Container can't access Gmail
|
|
|
|
- Verify `~/.gmail-mcp` is mounted: check `src/container-runner.ts` for the `.gmail-mcp` mount
|
|
- Check container logs: `cat groups/main/logs/container-*.log | tail -50`
|
|
|
|
### Emails not being detected (Channel mode only)
|
|
|
|
- By default, the channel polls unread Primary inbox emails (`is:unread category:primary`)
|
|
- Check logs for Gmail polling errors
|
|
|
|
## Removal
|
|
|
|
### Tool-only mode
|
|
|
|
1. Remove `~/.gmail-mcp` mount from `src/container-runner.ts`
|
|
2. Remove `gmail` MCP server and `mcp__gmail__*` from `container/agent-runner/src/index.ts`
|
|
3. Remove `gmail` from `.nanoclaw/state.yaml`
|
|
4. Clear stale agent-runner copies: `rm -r data/sessions/*/agent-runner-src 2>/dev/null || true`
|
|
5. Rebuild: `cd container && ./build.sh && cd .. && npm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux)
|
|
|
|
### Channel mode
|
|
|
|
1. Delete `src/channels/gmail.ts` and `src/channels/gmail.test.ts`
|
|
2. Remove `import './gmail.js'` from `src/channels/index.ts`
|
|
3. Remove `~/.gmail-mcp` mount from `src/container-runner.ts`
|
|
4. Remove `gmail` MCP server and `mcp__gmail__*` from `container/agent-runner/src/index.ts`
|
|
5. Uninstall: `npm uninstall googleapis`
|
|
6. Remove `gmail` from `.nanoclaw/state.yaml`
|
|
7. Clear stale agent-runner copies: `rm -r data/sessions/*/agent-runner-src 2>/dev/null || true`
|
|
8. Rebuild: `cd container && ./build.sh && cd .. && npm run build && launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux)
|