Skip to content

Commit d572bab

Browse files
gavrielcclaude
andcommitted
feat: add marketplace skills as local project skills
Move skill definitions from the nanoclaw-skills marketplace plugin into .claude/skills/ so they're available as unprefixed slash commands (e.g. /add-whatsapp instead of /nanoclaw-skills:add-whatsapp). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 621fde8 commit d572bab

13 files changed

Lines changed: 2301 additions & 0 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
---
2+
name: add-compact
3+
description: Add /compact command for manual context compaction. Solves context rot in long sessions by forwarding the SDK's built-in /compact slash command. Main-group or trusted sender only.
4+
---
5+
6+
# Add /compact Command
7+
8+
Adds a `/compact` session command that compacts conversation history to fight context rot in long-running sessions. Uses the Claude Agent SDK's built-in `/compact` slash command — no synthetic system prompts.
9+
10+
**Session contract:** `/compact` keeps the same logical session alive. The SDK returns a new session ID after compaction (via the `init` system message), which the agent-runner forwards to the orchestrator as `newSessionId`. No destructive reset occurs — the agent retains summarized context.
11+
12+
## Phase 1: Pre-flight
13+
14+
Check if `src/session-commands.ts` exists:
15+
16+
```bash
17+
test -f src/session-commands.ts && echo "Already applied" || echo "Not applied"
18+
```
19+
20+
If already applied, skip to Phase 3 (Verify).
21+
22+
## Phase 2: Apply Code Changes
23+
24+
Merge the skill branch:
25+
26+
```bash
27+
git fetch upstream skill/compact
28+
git merge upstream/skill/compact
29+
```
30+
31+
> **Note:** `upstream` is the remote pointing to `qwibitai/nanoclaw`. If using a different remote name, substitute accordingly.
32+
33+
This adds:
34+
- `src/session-commands.ts` (extract and authorize session commands)
35+
- `src/session-commands.test.ts` (unit tests for command parsing and auth)
36+
- Session command interception in `src/index.ts` (both `processGroupMessages` and `startMessageLoop`)
37+
- Slash command handling in `container/agent-runner/src/index.ts`
38+
39+
### Validate
40+
41+
```bash
42+
npm test
43+
npm run build
44+
```
45+
46+
### Rebuild container
47+
48+
```bash
49+
./container/build.sh
50+
```
51+
52+
### Restart service
53+
54+
```bash
55+
launchctl kickstart -k gui/$(id -u)/com.nanoclaw # macOS
56+
# Linux: systemctl --user restart nanoclaw
57+
```
58+
59+
## Phase 3: Verify
60+
61+
### Integration Test
62+
63+
1. Start NanoClaw in dev mode: `npm run dev`
64+
2. From the **main group** (self-chat), send exactly: `/compact`
65+
3. Verify:
66+
- The agent acknowledges compaction (e.g., "Conversation compacted.")
67+
- The session continues — send a follow-up message and verify the agent responds coherently
68+
- A conversation archive is written to `groups/{folder}/conversations/` (by the PreCompact hook)
69+
- Container logs show `Compact boundary observed` (confirms SDK actually compacted)
70+
- If `compact_boundary` was NOT observed, the response says "compact_boundary was not observed"
71+
4. From a **non-main group** as a non-admin user, send: `@<assistant> /compact`
72+
5. Verify:
73+
- The bot responds with "Session commands require admin access."
74+
- No compaction occurs, no container is spawned for the command
75+
6. From a **non-main group** as the admin (device owner / `is_from_me`), send: `@<assistant> /compact`
76+
7. Verify:
77+
- Compaction proceeds normally (same behavior as main group)
78+
8. While an **active container** is running for the main group, send `/compact`
79+
9. Verify:
80+
- The active container is signaled to close (authorized senders only — untrusted senders cannot kill in-flight work)
81+
- Compaction proceeds via a new container once the active one exits
82+
- The command is not dropped (no cursor race)
83+
10. Send a normal message, then `/compact`, then another normal message in quick succession (same polling batch):
84+
11. Verify:
85+
- Pre-compact messages are sent to the agent first (check container logs for two `runAgent` calls)
86+
- Compaction proceeds after pre-compact messages are processed
87+
- Messages **after** `/compact` in the batch are preserved (cursor advances to `/compact`'s timestamp only) and processed on the next poll cycle
88+
12. From a **non-main group** as a non-admin user, send `@<assistant> /compact`:
89+
13. Verify:
90+
- Denial message is sent ("Session commands require admin access.")
91+
- The `/compact` is consumed (cursor advanced) — it does NOT replay on future polls
92+
- Other messages in the same batch are also consumed (cursor is a high-water mark — this is an accepted tradeoff for the narrow edge case of denied `/compact` + other messages in the same polling interval)
93+
- No container is killed or interrupted
94+
14. From a **non-main group** (with `requiresTrigger` enabled) as a non-admin user, send bare `/compact` (no trigger prefix):
95+
15. Verify:
96+
- No denial message is sent (trigger policy prevents untrusted bot responses)
97+
- The `/compact` is consumed silently
98+
- Note: in groups where `requiresTrigger` is `false`, a denial message IS sent because the sender is considered reachable
99+
16. After compaction, verify **no auto-compaction** behavior — only manual `/compact` triggers it
100+
101+
### Validation on Fresh Clone
102+
103+
```bash
104+
git clone <your-fork> /tmp/nanoclaw-test
105+
cd /tmp/nanoclaw-test
106+
claude # then run /add-compact
107+
npm run build
108+
npm test
109+
./container/build.sh
110+
# Manual: send /compact from main group, verify compaction + continuation
111+
# Manual: send @<assistant> /compact from non-main as non-admin, verify denial
112+
# Manual: send @<assistant> /compact from non-main as admin, verify allowed
113+
# Manual: verify no auto-compaction behavior
114+
```
115+
116+
## Security Constraints
117+
118+
- **Main-group or trusted/admin sender only.** The main group is the user's private self-chat and is trusted (see `docs/SECURITY.md`). Non-main groups are untrusted — a careless or malicious user could wipe the agent's short-term memory. However, the device owner (`is_from_me`) is always trusted and can compact from any group.
119+
- **No auto-compaction.** This skill implements manual compaction only. Automatic threshold-based compaction is a separate concern and should be a separate skill.
120+
- **No config file.** NanoClaw's philosophy is customization through code changes, not configuration sprawl.
121+
- **Transcript archived before compaction.** The existing `PreCompact` hook in the agent-runner archives the full transcript to `conversations/` before the SDK compacts it.
122+
- **Session continues after compaction.** This is not a destructive reset. The conversation continues with summarized context.
123+
124+
## What This Does NOT Do
125+
126+
- No automatic compaction threshold (add separately if desired)
127+
- No `/clear` command (separate skill, separate semantics — `/clear` is a destructive reset)
128+
- No cross-group compaction (each group's session is isolated)
129+
- No changes to the container image, Dockerfile, or build script
130+
131+
## Troubleshooting
132+
133+
- **"Session commands require admin access"**: Only the device owner (`is_from_me`) or main-group senders can use `/compact`. Other users are denied.
134+
- **No compact_boundary in logs**: The SDK may not emit this event in all versions. Check the agent-runner logs for the warning message. Compaction may still have succeeded.
135+
- **Pre-compact failure**: If messages before `/compact` fail to process, the error message says "Failed to process messages before /compact." The cursor advances past sent output to prevent duplicates; `/compact` remains pending for the next attempt.
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
---
2+
name: add-discord
3+
description: Add Discord bot channel integration to NanoClaw.
4+
---
5+
6+
# Add Discord Channel
7+
8+
This skill adds Discord support to NanoClaw, then walks through interactive setup.
9+
10+
## Phase 1: Pre-flight
11+
12+
### Check if already applied
13+
14+
Check if `src/channels/discord.ts` exists. If it does, skip to Phase 3 (Setup). The code changes are already in place.
15+
16+
### Ask the user
17+
18+
Use `AskUserQuestion` to collect configuration:
19+
20+
AskUserQuestion: Do you have a Discord bot token, or do you need to create one?
21+
22+
If they have one, collect it now. If not, we'll create one in Phase 3.
23+
24+
## Phase 2: Apply Code Changes
25+
26+
### Ensure channel remote
27+
28+
```bash
29+
git remote -v
30+
```
31+
32+
If `discord` is missing, add it:
33+
34+
```bash
35+
git remote add discord https://github.com/qwibitai/nanoclaw-discord.git
36+
```
37+
38+
### Merge the skill branch
39+
40+
```bash
41+
git fetch discord main
42+
git merge discord/main
43+
```
44+
45+
This merges in:
46+
- `src/channels/discord.ts` (DiscordChannel class with self-registration via `registerChannel`)
47+
- `src/channels/discord.test.ts` (unit tests with discord.js mock)
48+
- `import './discord.js'` appended to the channel barrel file `src/channels/index.ts`
49+
- `discord.js` npm dependency in `package.json`
50+
- `DISCORD_BOT_TOKEN` in `.env.example`
51+
52+
If the merge reports conflicts, resolve them by reading the conflicted files and understanding the intent of both sides.
53+
54+
### Validate code changes
55+
56+
```bash
57+
npm install
58+
npm run build
59+
npx vitest run src/channels/discord.test.ts
60+
```
61+
62+
All tests must pass (including the new Discord tests) and build must be clean before proceeding.
63+
64+
## Phase 3: Setup
65+
66+
### Create Discord Bot (if needed)
67+
68+
If the user doesn't have a bot token, tell them:
69+
70+
> I need you to create a Discord bot:
71+
>
72+
> 1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
73+
> 2. Click **New Application** and give it a name (e.g., "Andy Assistant")
74+
> 3. Go to the **Bot** tab on the left sidebar
75+
> 4. Click **Reset Token** to generate a new bot token — copy it immediately (you can only see it once)
76+
> 5. Under **Privileged Gateway Intents**, enable:
77+
> - **Message Content Intent** (required to read message text)
78+
> - **Server Members Intent** (optional, for member display names)
79+
> 6. Go to **OAuth2** > **URL Generator**:
80+
> - Scopes: select `bot`
81+
> - Bot Permissions: select `Send Messages`, `Read Message History`, `View Channels`
82+
> - Copy the generated URL and open it in your browser to invite the bot to your server
83+
84+
Wait for the user to provide the token.
85+
86+
### Configure environment
87+
88+
Add to `.env`:
89+
90+
```bash
91+
DISCORD_BOT_TOKEN=<their-token>
92+
```
93+
94+
Channels auto-enable when their credentials are present — no extra configuration needed.
95+
96+
Sync to container environment:
97+
98+
```bash
99+
mkdir -p data/env && cp .env data/env/env
100+
```
101+
102+
The container reads environment from `data/env/env`, not `.env` directly.
103+
104+
### Build and restart
105+
106+
```bash
107+
npm run build
108+
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
109+
```
110+
111+
## Phase 4: Registration
112+
113+
### Get Channel ID
114+
115+
Tell the user:
116+
117+
> To get the channel ID for registration:
118+
>
119+
> 1. In Discord, go to **User Settings** > **Advanced** > Enable **Developer Mode**
120+
> 2. Right-click the text channel you want the bot to respond in
121+
> 3. Click **Copy Channel ID**
122+
>
123+
> The channel ID will be a long number like `1234567890123456`.
124+
125+
Wait for the user to provide the channel ID (format: `dc:1234567890123456`).
126+
127+
### Register the channel
128+
129+
Use the IPC register flow or register directly. The channel ID, name, and folder name are needed.
130+
131+
For a main channel (responds to all messages):
132+
133+
```typescript
134+
registerGroup("dc:<channel-id>", {
135+
name: "<server-name> #<channel-name>",
136+
folder: "discord_main",
137+
trigger: `@${ASSISTANT_NAME}`,
138+
added_at: new Date().toISOString(),
139+
requiresTrigger: false,
140+
isMain: true,
141+
});
142+
```
143+
144+
For additional channels (trigger-only):
145+
146+
```typescript
147+
registerGroup("dc:<channel-id>", {
148+
name: "<server-name> #<channel-name>",
149+
folder: "discord_<channel-name>",
150+
trigger: `@${ASSISTANT_NAME}`,
151+
added_at: new Date().toISOString(),
152+
requiresTrigger: true,
153+
});
154+
```
155+
156+
## Phase 5: Verify
157+
158+
### Test the connection
159+
160+
Tell the user:
161+
162+
> Send a message in your registered Discord channel:
163+
> - For main channel: Any message works
164+
> - For non-main: @mention the bot in Discord
165+
>
166+
> The bot should respond within a few seconds.
167+
168+
### Check logs if needed
169+
170+
```bash
171+
tail -f logs/nanoclaw.log
172+
```
173+
174+
## Troubleshooting
175+
176+
### Bot not responding
177+
178+
1. Check `DISCORD_BOT_TOKEN` is set in `.env` AND synced to `data/env/env`
179+
2. Check channel is registered: `sqlite3 store/messages.db "SELECT * FROM registered_groups WHERE jid LIKE 'dc:%'"`
180+
3. For non-main channels: message must include trigger pattern (@mention the bot)
181+
4. Service is running: `launchctl list | grep nanoclaw`
182+
5. Verify the bot has been invited to the server (check OAuth2 URL was used)
183+
184+
### Bot only responds to @mentions
185+
186+
This is the default behavior for non-main channels (`requiresTrigger: true`). To change:
187+
- Update the registered group's `requiresTrigger` to `false`
188+
- Or register the channel as the main channel
189+
190+
### Message Content Intent not enabled
191+
192+
If the bot connects but can't read messages, ensure:
193+
1. Go to [Discord Developer Portal](https://discord.com/developers/applications)
194+
2. Select your application > **Bot** tab
195+
3. Under **Privileged Gateway Intents**, enable **Message Content Intent**
196+
4. Restart NanoClaw
197+
198+
### Getting Channel ID
199+
200+
If you can't copy the channel ID:
201+
- Ensure **Developer Mode** is enabled: User Settings > Advanced > Developer Mode
202+
- Right-click the channel name in the server sidebar > Copy Channel ID
203+
204+
## After Setup
205+
206+
The Discord bot supports:
207+
- Text messages in registered channels
208+
- Attachment descriptions (images, videos, files shown as placeholders)
209+
- Reply context (shows who the user is replying to)
210+
- @mention translation (Discord `<@botId>` → NanoClaw trigger format)
211+
- Message splitting for responses over 2000 characters
212+
- Typing indicators while the agent processes

0 commit comments

Comments
 (0)