From 423d45c52e480e3ee3d12b5f7032a18e5e460f4d Mon Sep 17 00:00:00 2001 From: gavrielc Date: Sat, 31 Jan 2026 19:36:22 +0200 Subject: [PATCH] Update docs to reflect current architecture - SPEC.md: Add new source files, update config location, document conversation catch-up feature, fix message flow description - customize/SKILL.md: Fix file references (was Python, now TypeScript), update launchd service name Co-Authored-By: Claude Opus 4.5 --- .claude/skills/customize/SKILL.md | 40 +++++++++++---------- SPEC.md | 58 ++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 40 deletions(-) diff --git a/.claude/skills/customize/SKILL.md b/.claude/skills/customize/SKILL.md index 41ee390..d9f5521 100644 --- a/.claude/skills/customize/SKILL.md +++ b/.claude/skills/customize/SKILL.md @@ -18,11 +18,12 @@ This skill helps users add capabilities or modify behavior. Use AskUserQuestion | File | Purpose | |------|---------| -| `src/config.py` | Assistant name, trigger pattern, settings | -| `src/router.py` | Message routing, polling, agent invocation | -| `src/scheduler_worker.py` | Scheduled task execution | -| `src/commands.py` | Command handlers | -| `.mcp.json` | MCP server configuration | +| `src/config.ts` | Assistant name, trigger pattern, directories | +| `src/index.ts` | Message routing, WhatsApp connection, agent invocation | +| `src/db.ts` | Database initialization and queries | +| `src/types.ts` | TypeScript interfaces | +| `src/auth.ts` | Standalone WhatsApp authentication script | +| `.mcp.json` | MCP server configuration (reference) | | `groups/CLAUDE.md` | Global memory/persona | ## Common Customization Patterns @@ -36,9 +37,9 @@ Questions to ask: - Should messages from this channel go to existing groups or new ones? Implementation pattern: -1. Find/add MCP server for the channel to `.mcp.json` -2. Add polling function in `router.py` (similar to `get_new_messages()`) -3. Add to main loop to poll both sources +1. Find/add MCP server for the channel +2. Add connection and message handling in `src/index.ts` +3. Store messages in the database (update `src/db.ts` if needed) 4. Ensure responses route back to correct channel ### Adding a New MCP Integration @@ -49,8 +50,8 @@ Questions to ask: - Which groups should have access? Implementation: -1. Add MCP server config to `.mcp.json` -2. Add tools to `allowed_tools` in `router.py` +1. Add MCP server to the `mcpServers` config in `src/index.ts` +2. Add tools to `allowedTools` array 3. Document in `groups/CLAUDE.md` ### Changing Assistant Behavior @@ -59,7 +60,7 @@ Questions to ask: - What aspect? (name, trigger, persona, response style) - Apply to all groups or specific ones? -Simple changes → edit `src/config.py` +Simple changes → edit `src/config.ts` Persona changes → edit `groups/CLAUDE.md` Per-group behavior → edit specific group's `CLAUDE.md` @@ -71,8 +72,8 @@ Questions to ask: - Does it need new MCP tools? Implementation: -1. Add handler function in `src/commands.py` -2. Claude will recognize natural language and call the function +1. Add command handling in `processMessage()` in `src/index.ts` +2. Follow the pattern used for `/clear` ### Changing Deployment @@ -89,9 +90,10 @@ Implementation: Always tell the user: ```bash -# Restart to apply changes -launchctl unload ~/Library/LaunchAgents/com.nanoclaw.router.plist -launchctl load ~/Library/LaunchAgents/com.nanoclaw.router.plist +# Rebuild and restart +npm run build +launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist +launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist ``` ## Example Interaction @@ -100,7 +102,7 @@ User: "Add Telegram as an input channel" 1. Ask: "Should Telegram use the same @Andy trigger, or a different one?" 2. Ask: "Should Telegram messages create separate conversation contexts, or share with WhatsApp groups?" -3. Find Telegram MCP (e.g., telegram-mcp) -4. Add polling for Telegram in router.py -5. Update .mcp.json +3. Find Telegram MCP or library +4. Add connection handling in index.ts +5. Update message storage in db.ts 6. Tell user how to authenticate and test diff --git a/SPEC.md b/SPEC.md index ffceda9..f502972 100644 --- a/SPEC.md +++ b/SPEC.md @@ -88,10 +88,13 @@ nanoclaw/ ├── .gitignore │ ├── src/ -│ └── index.ts # Main application (WhatsApp + routing + agent) +│ ├── index.ts # Main application (WhatsApp + routing + agent) +│ ├── config.ts # Configuration constants +│ ├── types.ts # TypeScript interfaces +│ ├── db.ts # Database initialization and queries +│ └── auth.ts # Standalone WhatsApp authentication │ ├── dist/ # Compiled JavaScript (gitignored) -│ └── index.js │ ├── .claude/ │ └── skills/ @@ -118,7 +121,7 @@ nanoclaw/ │ ├── sessions.json # Active session IDs per group │ ├── archived_sessions.json # Old sessions after /clear │ ├── registered_groups.json # Group JID → folder mapping -│ └── router_state.json # Last processed timestamp +│ └── router_state.json # Last processed timestamp + last agent timestamps │ ├── logs/ # Runtime logs (gitignored) │ ├── nanoclaw.log # stdout @@ -132,16 +135,17 @@ nanoclaw/ ## Configuration -Configuration is done via environment variables and the CONFIG object in `src/index.ts`: +Configuration constants are in `src/config.ts`: ```typescript -const CONFIG = { - assistantName: process.env.ASSISTANT_NAME || 'Andy', - pollInterval: 2000, // ms - storeDir: './store', - groupsDir: './groups', - dataDir: './data', -}; +export const ASSISTANT_NAME = process.env.ASSISTANT_NAME || 'Andy'; +export const POLL_INTERVAL = 2000; +export const STORE_DIR = './store'; +export const GROUPS_DIR = './groups'; +export const DATA_DIR = './data'; + +export const TRIGGER_PATTERN = new RegExp(`^@${ASSISTANT_NAME}\\b`, 'i'); +export const CLEAR_COMMAND = '/clear'; ``` ### Changing the Assistant Name @@ -152,9 +156,9 @@ Set the `ASSISTANT_NAME` environment variable: ASSISTANT_NAME=Bot npm start ``` -Or edit the default in `src/index.ts`. This changes: +Or edit the default in `src/config.ts`. This changes: - The trigger pattern (messages must start with `@YourName`) -- The response prefix (`YourName:`) +- The response prefix (`YourName:` added automatically) ### Placeholder Values in launchd @@ -248,16 +252,16 @@ When a user sends `/clear` in any group: └── Is message "/clear"? → Yes: handle specially │ ▼ -6. Router prepares invocation: - ├── Load session ID for this group - ├── Determine group folder path - └── Strip trigger word from message +6. Router catches up conversation: + ├── Fetch all messages since last agent interaction + ├── Format with timestamp and sender name + └── Build prompt with full conversation context │ ▼ 7. Router invokes Claude Agent SDK: ├── cwd: groups/{group-name}/ - ├── prompt: user's message - ├── resume: session_id (or undefined) + ├── prompt: conversation history + current message + ├── resume: session_id (for continuity) └── mcpServers: gmail, scheduler │ ▼ @@ -266,10 +270,10 @@ When a user sends `/clear` in any group: └── Uses tools as needed (search, email, etc.) │ ▼ -9. Router captures result and sends via WhatsApp +9. Router prefixes response with assistant name and sends via WhatsApp │ ▼ -10. Router saves new session ID +10. Router updates last agent timestamp and saves session ID ``` ### Trigger Word Matching @@ -281,6 +285,18 @@ Messages must start with the trigger pattern (default: `@Andy`): - `What's up?` → ❌ Ignored (no trigger) - `/clear` → ✅ Special command (no trigger needed) +### Conversation Catch-Up + +When a triggered message arrives, the agent receives all messages since its last interaction in that chat. Each message is formatted with timestamp and sender name: + +``` +[Jan 31 2:32 PM] John: hey everyone, should we do pizza tonight? +[Jan 31 2:33 PM] Sarah: sounds good to me +[Jan 31 2:35 PM] John: @Andy what toppings do you recommend? +``` + +This allows the agent to understand the conversation context even if it wasn't mentioned in every message. + --- ## Commands