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 <noreply@anthropic.com>
This commit is contained in:
@@ -18,11 +18,12 @@ This skill helps users add capabilities or modify behavior. Use AskUserQuestion
|
|||||||
|
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
| `src/config.py` | Assistant name, trigger pattern, settings |
|
| `src/config.ts` | Assistant name, trigger pattern, directories |
|
||||||
| `src/router.py` | Message routing, polling, agent invocation |
|
| `src/index.ts` | Message routing, WhatsApp connection, agent invocation |
|
||||||
| `src/scheduler_worker.py` | Scheduled task execution |
|
| `src/db.ts` | Database initialization and queries |
|
||||||
| `src/commands.py` | Command handlers |
|
| `src/types.ts` | TypeScript interfaces |
|
||||||
| `.mcp.json` | MCP server configuration |
|
| `src/auth.ts` | Standalone WhatsApp authentication script |
|
||||||
|
| `.mcp.json` | MCP server configuration (reference) |
|
||||||
| `groups/CLAUDE.md` | Global memory/persona |
|
| `groups/CLAUDE.md` | Global memory/persona |
|
||||||
|
|
||||||
## Common Customization Patterns
|
## Common Customization Patterns
|
||||||
@@ -36,9 +37,9 @@ Questions to ask:
|
|||||||
- Should messages from this channel go to existing groups or new ones?
|
- Should messages from this channel go to existing groups or new ones?
|
||||||
|
|
||||||
Implementation pattern:
|
Implementation pattern:
|
||||||
1. Find/add MCP server for the channel to `.mcp.json`
|
1. Find/add MCP server for the channel
|
||||||
2. Add polling function in `router.py` (similar to `get_new_messages()`)
|
2. Add connection and message handling in `src/index.ts`
|
||||||
3. Add to main loop to poll both sources
|
3. Store messages in the database (update `src/db.ts` if needed)
|
||||||
4. Ensure responses route back to correct channel
|
4. Ensure responses route back to correct channel
|
||||||
|
|
||||||
### Adding a New MCP Integration
|
### Adding a New MCP Integration
|
||||||
@@ -49,8 +50,8 @@ Questions to ask:
|
|||||||
- Which groups should have access?
|
- Which groups should have access?
|
||||||
|
|
||||||
Implementation:
|
Implementation:
|
||||||
1. Add MCP server config to `.mcp.json`
|
1. Add MCP server to the `mcpServers` config in `src/index.ts`
|
||||||
2. Add tools to `allowed_tools` in `router.py`
|
2. Add tools to `allowedTools` array
|
||||||
3. Document in `groups/CLAUDE.md`
|
3. Document in `groups/CLAUDE.md`
|
||||||
|
|
||||||
### Changing Assistant Behavior
|
### Changing Assistant Behavior
|
||||||
@@ -59,7 +60,7 @@ Questions to ask:
|
|||||||
- What aspect? (name, trigger, persona, response style)
|
- What aspect? (name, trigger, persona, response style)
|
||||||
- Apply to all groups or specific ones?
|
- 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`
|
Persona changes → edit `groups/CLAUDE.md`
|
||||||
Per-group behavior → edit specific group's `CLAUDE.md`
|
Per-group behavior → edit specific group's `CLAUDE.md`
|
||||||
|
|
||||||
@@ -71,8 +72,8 @@ Questions to ask:
|
|||||||
- Does it need new MCP tools?
|
- Does it need new MCP tools?
|
||||||
|
|
||||||
Implementation:
|
Implementation:
|
||||||
1. Add handler function in `src/commands.py`
|
1. Add command handling in `processMessage()` in `src/index.ts`
|
||||||
2. Claude will recognize natural language and call the function
|
2. Follow the pattern used for `/clear`
|
||||||
|
|
||||||
### Changing Deployment
|
### Changing Deployment
|
||||||
|
|
||||||
@@ -89,9 +90,10 @@ Implementation:
|
|||||||
|
|
||||||
Always tell the user:
|
Always tell the user:
|
||||||
```bash
|
```bash
|
||||||
# Restart to apply changes
|
# Rebuild and restart
|
||||||
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.router.plist
|
npm run build
|
||||||
launchctl load ~/Library/LaunchAgents/com.nanoclaw.router.plist
|
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
|
||||||
|
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example Interaction
|
## 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?"
|
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?"
|
2. Ask: "Should Telegram messages create separate conversation contexts, or share with WhatsApp groups?"
|
||||||
3. Find Telegram MCP (e.g., telegram-mcp)
|
3. Find Telegram MCP or library
|
||||||
4. Add polling for Telegram in router.py
|
4. Add connection handling in index.ts
|
||||||
5. Update .mcp.json
|
5. Update message storage in db.ts
|
||||||
6. Tell user how to authenticate and test
|
6. Tell user how to authenticate and test
|
||||||
|
|||||||
58
SPEC.md
58
SPEC.md
@@ -88,10 +88,13 @@ nanoclaw/
|
|||||||
├── .gitignore
|
├── .gitignore
|
||||||
│
|
│
|
||||||
├── src/
|
├── 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)
|
├── dist/ # Compiled JavaScript (gitignored)
|
||||||
│ └── index.js
|
|
||||||
│
|
│
|
||||||
├── .claude/
|
├── .claude/
|
||||||
│ └── skills/
|
│ └── skills/
|
||||||
@@ -118,7 +121,7 @@ nanoclaw/
|
|||||||
│ ├── sessions.json # Active session IDs per group
|
│ ├── sessions.json # Active session IDs per group
|
||||||
│ ├── archived_sessions.json # Old sessions after /clear
|
│ ├── archived_sessions.json # Old sessions after /clear
|
||||||
│ ├── registered_groups.json # Group JID → folder mapping
|
│ ├── 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)
|
├── logs/ # Runtime logs (gitignored)
|
||||||
│ ├── nanoclaw.log # stdout
|
│ ├── nanoclaw.log # stdout
|
||||||
@@ -132,16 +135,17 @@ nanoclaw/
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Configuration is done via environment variables and the CONFIG object in `src/index.ts`:
|
Configuration constants are in `src/config.ts`:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const CONFIG = {
|
export const ASSISTANT_NAME = process.env.ASSISTANT_NAME || 'Andy';
|
||||||
assistantName: process.env.ASSISTANT_NAME || 'Andy',
|
export const POLL_INTERVAL = 2000;
|
||||||
pollInterval: 2000, // ms
|
export const STORE_DIR = './store';
|
||||||
storeDir: './store',
|
export const GROUPS_DIR = './groups';
|
||||||
groupsDir: './groups',
|
export const DATA_DIR = './data';
|
||||||
dataDir: './data',
|
|
||||||
};
|
export const TRIGGER_PATTERN = new RegExp(`^@${ASSISTANT_NAME}\\b`, 'i');
|
||||||
|
export const CLEAR_COMMAND = '/clear';
|
||||||
```
|
```
|
||||||
|
|
||||||
### Changing the Assistant Name
|
### Changing the Assistant Name
|
||||||
@@ -152,9 +156,9 @@ Set the `ASSISTANT_NAME` environment variable:
|
|||||||
ASSISTANT_NAME=Bot npm start
|
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 trigger pattern (messages must start with `@YourName`)
|
||||||
- The response prefix (`YourName:`)
|
- The response prefix (`YourName:` added automatically)
|
||||||
|
|
||||||
### Placeholder Values in launchd
|
### Placeholder Values in launchd
|
||||||
|
|
||||||
@@ -248,16 +252,16 @@ When a user sends `/clear` in any group:
|
|||||||
└── Is message "/clear"? → Yes: handle specially
|
└── Is message "/clear"? → Yes: handle specially
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
6. Router prepares invocation:
|
6. Router catches up conversation:
|
||||||
├── Load session ID for this group
|
├── Fetch all messages since last agent interaction
|
||||||
├── Determine group folder path
|
├── Format with timestamp and sender name
|
||||||
└── Strip trigger word from message
|
└── Build prompt with full conversation context
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
7. Router invokes Claude Agent SDK:
|
7. Router invokes Claude Agent SDK:
|
||||||
├── cwd: groups/{group-name}/
|
├── cwd: groups/{group-name}/
|
||||||
├── prompt: user's message
|
├── prompt: conversation history + current message
|
||||||
├── resume: session_id (or undefined)
|
├── resume: session_id (for continuity)
|
||||||
└── mcpServers: gmail, scheduler
|
└── mcpServers: gmail, scheduler
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
@@ -266,10 +270,10 @@ When a user sends `/clear` in any group:
|
|||||||
└── Uses tools as needed (search, email, etc.)
|
└── 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
|
### Trigger Word Matching
|
||||||
@@ -281,6 +285,18 @@ Messages must start with the trigger pattern (default: `@Andy`):
|
|||||||
- `What's up?` → ❌ Ignored (no trigger)
|
- `What's up?` → ❌ Ignored (no trigger)
|
||||||
- `/clear` → ✅ Special command (no trigger needed)
|
- `/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
|
## Commands
|
||||||
|
|||||||
Reference in New Issue
Block a user