@rozek/nanoclaw 1.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +1 -0
- package/.claude/skills/add-compact/SKILL.md +135 -0
- package/.claude/skills/add-discord/SKILL.md +203 -0
- package/.claude/skills/add-gmail/SKILL.md +220 -0
- package/.claude/skills/add-image-vision/SKILL.md +94 -0
- package/.claude/skills/add-ollama-tool/SKILL.md +153 -0
- package/.claude/skills/add-parallel/SKILL.md +290 -0
- package/.claude/skills/add-pdf-reader/SKILL.md +104 -0
- package/.claude/skills/add-reactions/SKILL.md +117 -0
- package/.claude/skills/add-slack/SKILL.md +207 -0
- package/.claude/skills/add-telegram/SKILL.md +222 -0
- package/.claude/skills/add-telegram-swarm/SKILL.md +384 -0
- package/.claude/skills/add-voice-transcription/SKILL.md +148 -0
- package/.claude/skills/add-whatsapp/SKILL.md +372 -0
- package/.claude/skills/convert-to-apple-container/SKILL.md +175 -0
- package/.claude/skills/customize/SKILL.md +110 -0
- package/.claude/skills/debug/SKILL.md +349 -0
- package/.claude/skills/get-qodo-rules/SKILL.md +122 -0
- package/.claude/skills/get-qodo-rules/references/output-format.md +41 -0
- package/.claude/skills/get-qodo-rules/references/pagination.md +33 -0
- package/.claude/skills/get-qodo-rules/references/repository-scope.md +26 -0
- package/.claude/skills/qodo-pr-resolver/SKILL.md +326 -0
- package/.claude/skills/qodo-pr-resolver/resources/providers.md +329 -0
- package/.claude/skills/setup/SKILL.md +218 -0
- package/.claude/skills/update-nanoclaw/SKILL.md +235 -0
- package/.claude/skills/update-skills/SKILL.md +130 -0
- package/.claude/skills/use-local-whisper/SKILL.md +152 -0
- package/.claude/skills/x-integration/SKILL.md +417 -0
- package/.claude/skills/x-integration/agent.ts +243 -0
- package/.claude/skills/x-integration/host.ts +159 -0
- package/.claude/skills/x-integration/lib/browser.ts +148 -0
- package/.claude/skills/x-integration/lib/config.ts +62 -0
- package/.claude/skills/x-integration/scripts/like.ts +56 -0
- package/.claude/skills/x-integration/scripts/post.ts +66 -0
- package/.claude/skills/x-integration/scripts/quote.ts +80 -0
- package/.claude/skills/x-integration/scripts/reply.ts +74 -0
- package/.claude/skills/x-integration/scripts/retweet.ts +62 -0
- package/.claude/skills/x-integration/scripts/setup.ts +87 -0
- package/.env.example +1 -0
- package/.github/CODEOWNERS +10 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/.github/workflows/bump-version.yml +32 -0
- package/.github/workflows/ci.yml +25 -0
- package/.github/workflows/merge-forward-skills.yml +160 -0
- package/.github/workflows/update-tokens.yml +42 -0
- package/.husky/pre-commit +1 -0
- package/.mcp.json +3 -0
- package/.nvmrc +1 -0
- package/.prettierrc +3 -0
- package/CHANGELOG.md +8 -0
- package/CLAUDE.md +64 -0
- package/CONTRIBUTING.md +23 -0
- package/CONTRIBUTORS.md +15 -0
- package/LICENSE +21 -0
- package/NanoClaw_with_Web-Support.md +290 -0
- package/README.md +261 -0
- package/README_zh.md +200 -0
- package/assets/nanoclaw-favicon.png +0 -0
- package/assets/nanoclaw-icon.png +0 -0
- package/assets/nanoclaw-logo-dark.png +0 -0
- package/assets/nanoclaw-logo.png +0 -0
- package/assets/nanoclaw-profile.jpeg +0 -0
- package/assets/nanoclaw-sales.png +0 -0
- package/assets/social-preview.jpg +0 -0
- package/config-examples/mount-allowlist.json +25 -0
- package/container/Dockerfile +70 -0
- package/container/agent-runner/package-lock.json +1524 -0
- package/container/agent-runner/package.json +21 -0
- package/container/agent-runner/src/index.ts +558 -0
- package/container/agent-runner/src/ipc-mcp-stdio.ts +338 -0
- package/container/agent-runner/tsconfig.json +15 -0
- package/container/build.sh +23 -0
- package/container/skills/agent-browser/SKILL.md +159 -0
- package/container/skills/capabilities/SKILL.md +100 -0
- package/container/skills/status/SKILL.md +104 -0
- package/dist/channels/index.d.ts +2 -0
- package/dist/channels/index.d.ts.map +1 -0
- package/dist/channels/index.js +9 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/registry.d.ts +13 -0
- package/dist/channels/registry.d.ts.map +1 -0
- package/dist/channels/registry.js +11 -0
- package/dist/channels/registry.js.map +1 -0
- package/dist/channels/registry.test.d.ts +2 -0
- package/dist/channels/registry.test.d.ts.map +1 -0
- package/dist/channels/registry.test.js +32 -0
- package/dist/channels/registry.test.js.map +1 -0
- package/dist/channels/web.d.ts +2 -0
- package/dist/channels/web.d.ts.map +1 -0
- package/dist/channels/web.js +1738 -0
- package/dist/channels/web.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +182 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +36 -0
- package/dist/config.js.map +1 -0
- package/dist/container-runner.d.ts +44 -0
- package/dist/container-runner.d.ts.map +1 -0
- package/dist/container-runner.js +467 -0
- package/dist/container-runner.js.map +1 -0
- package/dist/container-runner.test.d.ts +2 -0
- package/dist/container-runner.test.d.ts.map +1 -0
- package/dist/container-runner.test.js +150 -0
- package/dist/container-runner.test.js.map +1 -0
- package/dist/container-runtime.d.ts +22 -0
- package/dist/container-runtime.d.ts.map +1 -0
- package/dist/container-runtime.js +96 -0
- package/dist/container-runtime.js.map +1 -0
- package/dist/container-runtime.test.d.ts +2 -0
- package/dist/container-runtime.test.d.ts.map +1 -0
- package/dist/container-runtime.test.js +93 -0
- package/dist/container-runtime.test.js.map +1 -0
- package/dist/credential-proxy.d.ts +21 -0
- package/dist/credential-proxy.d.ts.map +1 -0
- package/dist/credential-proxy.js +95 -0
- package/dist/credential-proxy.js.map +1 -0
- package/dist/credential-proxy.test.d.ts +2 -0
- package/dist/credential-proxy.test.d.ts.map +1 -0
- package/dist/credential-proxy.test.js +134 -0
- package/dist/credential-proxy.test.js.map +1 -0
- package/dist/db.d.ts +115 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +549 -0
- package/dist/db.js.map +1 -0
- package/dist/db.test.d.ts +2 -0
- package/dist/db.test.d.ts.map +1 -0
- package/dist/db.test.js +360 -0
- package/dist/db.test.js.map +1 -0
- package/dist/env.d.ts +8 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +42 -0
- package/dist/env.js.map +1 -0
- package/dist/formatting.test.d.ts +2 -0
- package/dist/formatting.test.d.ts.map +1 -0
- package/dist/formatting.test.js +183 -0
- package/dist/formatting.test.js.map +1 -0
- package/dist/group-folder.d.ts +5 -0
- package/dist/group-folder.d.ts.map +1 -0
- package/dist/group-folder.js +44 -0
- package/dist/group-folder.js.map +1 -0
- package/dist/group-folder.test.d.ts +2 -0
- package/dist/group-folder.test.d.ts.map +1 -0
- package/dist/group-folder.test.js +29 -0
- package/dist/group-folder.test.js.map +1 -0
- package/dist/group-queue.d.ts +34 -0
- package/dist/group-queue.d.ts.map +1 -0
- package/dist/group-queue.js +263 -0
- package/dist/group-queue.js.map +1 -0
- package/dist/group-queue.test.d.ts +2 -0
- package/dist/group-queue.test.d.ts.map +1 -0
- package/dist/group-queue.test.js +341 -0
- package/dist/group-queue.test.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +518 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc-auth.test.d.ts +2 -0
- package/dist/ipc-auth.test.d.ts.map +1 -0
- package/dist/ipc-auth.test.js +434 -0
- package/dist/ipc-auth.test.js.map +1 -0
- package/dist/ipc.d.ts +32 -0
- package/dist/ipc.d.ts.map +1 -0
- package/dist/ipc.js +311 -0
- package/dist/ipc.js.map +1 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +14 -0
- package/dist/logger.js.map +1 -0
- package/dist/mount-security.d.ts +34 -0
- package/dist/mount-security.d.ts.map +1 -0
- package/dist/mount-security.js +325 -0
- package/dist/mount-security.js.map +1 -0
- package/dist/remote-control.d.ts +32 -0
- package/dist/remote-control.d.ts.map +1 -0
- package/dist/remote-control.js +185 -0
- package/dist/remote-control.js.map +1 -0
- package/dist/remote-control.test.d.ts +2 -0
- package/dist/remote-control.test.d.ts.map +1 -0
- package/dist/remote-control.test.js +321 -0
- package/dist/remote-control.test.js.map +1 -0
- package/dist/router.d.ts +8 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +37 -0
- package/dist/router.js.map +1 -0
- package/dist/routing.test.d.ts +2 -0
- package/dist/routing.test.d.ts.map +1 -0
- package/dist/routing.test.js +81 -0
- package/dist/routing.test.js.map +1 -0
- package/dist/sender-allowlist.d.ts +14 -0
- package/dist/sender-allowlist.d.ts.map +1 -0
- package/dist/sender-allowlist.js +79 -0
- package/dist/sender-allowlist.js.map +1 -0
- package/dist/sender-allowlist.test.d.ts +2 -0
- package/dist/sender-allowlist.test.d.ts.map +1 -0
- package/dist/sender-allowlist.test.js +186 -0
- package/dist/sender-allowlist.test.js.map +1 -0
- package/dist/session-commands.d.ts +47 -0
- package/dist/session-commands.d.ts.map +1 -0
- package/dist/session-commands.js +102 -0
- package/dist/session-commands.js.map +1 -0
- package/dist/session-commands.test.d.ts +2 -0
- package/dist/session-commands.test.d.ts.map +1 -0
- package/dist/session-commands.test.js +190 -0
- package/dist/session-commands.test.js.map +1 -0
- package/dist/task-scheduler.d.ts +22 -0
- package/dist/task-scheduler.d.ts.map +1 -0
- package/dist/task-scheduler.js +210 -0
- package/dist/task-scheduler.js.map +1 -0
- package/dist/task-scheduler.test.d.ts +2 -0
- package/dist/task-scheduler.test.d.ts.map +1 -0
- package/dist/task-scheduler.test.js +107 -0
- package/dist/task-scheduler.test.js.map +1 -0
- package/dist/timezone.d.ts +6 -0
- package/dist/timezone.d.ts.map +1 -0
- package/dist/timezone.js +17 -0
- package/dist/timezone.js.map +1 -0
- package/dist/timezone.test.d.ts +2 -0
- package/dist/timezone.test.d.ts.map +1 -0
- package/dist/timezone.test.js +23 -0
- package/dist/timezone.test.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/APPLE-CONTAINER-NETWORKING.md +90 -0
- package/docs/DEBUG_CHECKLIST.md +143 -0
- package/docs/REQUIREMENTS.md +196 -0
- package/docs/SDK_DEEP_DIVE.md +643 -0
- package/docs/SECURITY.md +122 -0
- package/docs/SPEC.md +785 -0
- package/docs/docker-sandboxes.md +359 -0
- package/docs/nanoclaw-architecture-final.md +1063 -0
- package/docs/nanorepo-architecture.md +168 -0
- package/docs/skills-as-branches.md +662 -0
- package/groups/global/CLAUDE.md +58 -0
- package/groups/main/CLAUDE.md +246 -0
- package/launchd/com.nanoclaw.plist +32 -0
- package/package.json +45 -0
- package/repo-tokens/README.md +113 -0
- package/repo-tokens/action.yml +186 -0
- package/repo-tokens/badge.svg +23 -0
- package/repo-tokens/examples/green.svg +14 -0
- package/repo-tokens/examples/red.svg +14 -0
- package/repo-tokens/examples/yellow-green.svg +14 -0
- package/repo-tokens/examples/yellow.svg +14 -0
- package/scripts/run-migrations.ts +105 -0
- package/setup/container.ts +144 -0
- package/setup/environment.test.ts +121 -0
- package/setup/environment.ts +94 -0
- package/setup/groups.ts +229 -0
- package/setup/index.ts +58 -0
- package/setup/mounts.ts +115 -0
- package/setup/platform.test.ts +120 -0
- package/setup/platform.ts +132 -0
- package/setup/register.test.ts +257 -0
- package/setup/register.ts +177 -0
- package/setup/service.test.ts +187 -0
- package/setup/service.ts +362 -0
- package/setup/status.ts +16 -0
- package/setup/verify.ts +192 -0
- package/setup.sh +161 -0
- package/src/channels/index.ts +12 -0
- package/src/channels/registry.test.ts +42 -0
- package/src/channels/registry.ts +32 -0
- package/src/channels/web.ts +1856 -0
- package/src/cli.ts +209 -0
- package/src/config.ts +73 -0
- package/src/container-runner.test.ts +210 -0
- package/src/container-runner.ts +707 -0
- package/src/container-runtime.test.ts +149 -0
- package/src/container-runtime.ts +127 -0
- package/src/credential-proxy.test.ts +192 -0
- package/src/credential-proxy.ts +125 -0
- package/src/db.test.ts +484 -0
- package/src/db.ts +803 -0
- package/src/env.ts +42 -0
- package/src/formatting.test.ts +256 -0
- package/src/group-folder.test.ts +43 -0
- package/src/group-folder.ts +44 -0
- package/src/group-queue.test.ts +484 -0
- package/src/group-queue.ts +365 -0
- package/src/index.ts +731 -0
- package/src/ipc-auth.test.ts +679 -0
- package/src/ipc.ts +461 -0
- package/src/logger.ts +16 -0
- package/src/mount-security.ts +419 -0
- package/src/remote-control.test.ts +397 -0
- package/src/remote-control.ts +224 -0
- package/src/router.ts +52 -0
- package/src/routing.test.ts +170 -0
- package/src/sender-allowlist.test.ts +216 -0
- package/src/sender-allowlist.ts +128 -0
- package/src/session-commands.test.ts +247 -0
- package/src/session-commands.ts +163 -0
- package/src/task-scheduler.test.ts +129 -0
- package/src/task-scheduler.ts +295 -0
- package/src/timezone.test.ts +29 -0
- package/src/timezone.ts +16 -0
- package/src/types.ts +107 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +7 -0
- package/vitest.skills.config.ts +7 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stdio MCP Server for NanoClaw
|
|
3
|
+
* Standalone process that agent teams subagents can inherit.
|
|
4
|
+
* Reads context from environment variables, writes IPC files for the host.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { CronExpressionParser } from 'cron-parser';
|
|
13
|
+
|
|
14
|
+
const IPC_DIR = '/workspace/ipc';
|
|
15
|
+
const MESSAGES_DIR = path.join(IPC_DIR, 'messages');
|
|
16
|
+
const TASKS_DIR = path.join(IPC_DIR, 'tasks');
|
|
17
|
+
|
|
18
|
+
// Context from environment variables (set by the agent runner)
|
|
19
|
+
const chatJid = process.env.NANOCLAW_CHAT_JID!;
|
|
20
|
+
const groupFolder = process.env.NANOCLAW_GROUP_FOLDER!;
|
|
21
|
+
const isMain = process.env.NANOCLAW_IS_MAIN === '1';
|
|
22
|
+
|
|
23
|
+
function writeIpcFile(dir: string, data: object): string {
|
|
24
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
25
|
+
|
|
26
|
+
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`;
|
|
27
|
+
const filepath = path.join(dir, filename);
|
|
28
|
+
|
|
29
|
+
// Atomic write: temp file then rename
|
|
30
|
+
const tempPath = `${filepath}.tmp`;
|
|
31
|
+
fs.writeFileSync(tempPath, JSON.stringify(data, null, 2));
|
|
32
|
+
fs.renameSync(tempPath, filepath);
|
|
33
|
+
|
|
34
|
+
return filename;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const server = new McpServer({
|
|
38
|
+
name: 'nanoclaw',
|
|
39
|
+
version: '1.0.0',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
server.tool(
|
|
43
|
+
'send_message',
|
|
44
|
+
"Send a message to the user or group immediately while you're still running. Use this for progress updates or to send multiple messages. You can call this multiple times.",
|
|
45
|
+
{
|
|
46
|
+
text: z.string().describe('The message text to send'),
|
|
47
|
+
sender: z.string().optional().describe('Your role/identity name (e.g. "Researcher"). When set, messages appear from a dedicated bot in Telegram.'),
|
|
48
|
+
},
|
|
49
|
+
async (args) => {
|
|
50
|
+
const data: Record<string, string | undefined> = {
|
|
51
|
+
type: 'message',
|
|
52
|
+
chatJid,
|
|
53
|
+
text: args.text,
|
|
54
|
+
sender: args.sender || undefined,
|
|
55
|
+
groupFolder,
|
|
56
|
+
timestamp: new Date().toISOString(),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
writeIpcFile(MESSAGES_DIR, data);
|
|
60
|
+
|
|
61
|
+
return { content: [{ type: 'text' as const, text: 'Message sent.' }] };
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
server.tool(
|
|
66
|
+
'schedule_task',
|
|
67
|
+
`Schedule a recurring or one-time task. The task will run as a full agent with access to all tools. Returns the task ID for future reference. To modify an existing task, use update_task instead.
|
|
68
|
+
|
|
69
|
+
CONTEXT MODE - Choose based on task type:
|
|
70
|
+
\u2022 "group": Task runs in the group's conversation context, with access to chat history. Use for tasks that need context about ongoing discussions, user preferences, or recent interactions.
|
|
71
|
+
\u2022 "isolated": Task runs in a fresh session with no conversation history. Use for independent tasks that don't need prior context. When using isolated mode, include all necessary context in the prompt itself.
|
|
72
|
+
|
|
73
|
+
If unsure which mode to use, you can ask the user. Examples:
|
|
74
|
+
- "Remind me about our discussion" \u2192 group (needs conversation context)
|
|
75
|
+
- "Check the weather every morning" \u2192 isolated (self-contained task)
|
|
76
|
+
- "Follow up on my request" \u2192 group (needs to know what was requested)
|
|
77
|
+
- "Generate a daily report" \u2192 isolated (just needs instructions in prompt)
|
|
78
|
+
|
|
79
|
+
MESSAGING BEHAVIOR - The task agent's output is sent to the user or group. It can also use send_message for immediate delivery, or wrap output in <internal> tags to suppress it. Include guidance in the prompt about whether the agent should:
|
|
80
|
+
\u2022 Always send a message (e.g., reminders, daily briefings)
|
|
81
|
+
\u2022 Only send a message when there's something to report (e.g., "notify me if...")
|
|
82
|
+
\u2022 Never send a message (background maintenance tasks)
|
|
83
|
+
|
|
84
|
+
SCHEDULE VALUE FORMAT (all times are LOCAL timezone):
|
|
85
|
+
\u2022 cron: Standard cron expression (e.g., "*/5 * * * *" for every 5 minutes, "0 9 * * *" for daily at 9am LOCAL time)
|
|
86
|
+
\u2022 interval: Milliseconds between runs (e.g., "300000" for 5 minutes, "3600000" for 1 hour)
|
|
87
|
+
\u2022 once: Local time WITHOUT "Z" suffix (e.g., "2026-02-01T15:30:00"). Do NOT use UTC/Z suffix.`,
|
|
88
|
+
{
|
|
89
|
+
prompt: z.string().describe('What the agent should do when the task runs. For isolated mode, include all necessary context here.'),
|
|
90
|
+
schedule_type: z.enum(['cron', 'interval', 'once']).describe('cron=recurring at specific times, interval=recurring every N ms, once=run once at specific time'),
|
|
91
|
+
schedule_value: z.string().describe('cron: "*/5 * * * *" | interval: milliseconds like "300000" | once: local timestamp like "2026-02-01T15:30:00" (no Z suffix!)'),
|
|
92
|
+
context_mode: z.enum(['group', 'isolated']).default('group').describe('group=runs with chat history and memory, isolated=fresh session (include context in prompt)'),
|
|
93
|
+
target_group_jid: z.string().optional().describe('(Main group only) JID of the group to schedule the task for. Defaults to the current group.'),
|
|
94
|
+
},
|
|
95
|
+
async (args) => {
|
|
96
|
+
// Validate schedule_value before writing IPC
|
|
97
|
+
if (args.schedule_type === 'cron') {
|
|
98
|
+
try {
|
|
99
|
+
CronExpressionParser.parse(args.schedule_value);
|
|
100
|
+
} catch {
|
|
101
|
+
return {
|
|
102
|
+
content: [{ type: 'text' as const, text: `Invalid cron: "${args.schedule_value}". Use format like "0 9 * * *" (daily 9am) or "*/5 * * * *" (every 5 min).` }],
|
|
103
|
+
isError: true,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
} else if (args.schedule_type === 'interval') {
|
|
107
|
+
const ms = parseInt(args.schedule_value, 10);
|
|
108
|
+
if (isNaN(ms) || ms <= 0) {
|
|
109
|
+
return {
|
|
110
|
+
content: [{ type: 'text' as const, text: `Invalid interval: "${args.schedule_value}". Must be positive milliseconds (e.g., "300000" for 5 min).` }],
|
|
111
|
+
isError: true,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
} else if (args.schedule_type === 'once') {
|
|
115
|
+
if (/[Zz]$/.test(args.schedule_value) || /[+-]\d{2}:\d{2}$/.test(args.schedule_value)) {
|
|
116
|
+
return {
|
|
117
|
+
content: [{ type: 'text' as const, text: `Timestamp must be local time without timezone suffix. Got "${args.schedule_value}" — use format like "2026-02-01T15:30:00".` }],
|
|
118
|
+
isError: true,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const date = new Date(args.schedule_value);
|
|
122
|
+
if (isNaN(date.getTime())) {
|
|
123
|
+
return {
|
|
124
|
+
content: [{ type: 'text' as const, text: `Invalid timestamp: "${args.schedule_value}". Use local time format like "2026-02-01T15:30:00".` }],
|
|
125
|
+
isError: true,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Non-main groups can only schedule for themselves
|
|
131
|
+
const targetJid = isMain && args.target_group_jid ? args.target_group_jid : chatJid;
|
|
132
|
+
|
|
133
|
+
const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
134
|
+
|
|
135
|
+
const data = {
|
|
136
|
+
type: 'schedule_task',
|
|
137
|
+
taskId,
|
|
138
|
+
prompt: args.prompt,
|
|
139
|
+
schedule_type: args.schedule_type,
|
|
140
|
+
schedule_value: args.schedule_value,
|
|
141
|
+
context_mode: args.context_mode || 'group',
|
|
142
|
+
targetJid,
|
|
143
|
+
createdBy: groupFolder,
|
|
144
|
+
timestamp: new Date().toISOString(),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
writeIpcFile(TASKS_DIR, data);
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
content: [{ type: 'text' as const, text: `Task ${taskId} scheduled: ${args.schedule_type} - ${args.schedule_value}` }],
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
server.tool(
|
|
156
|
+
'list_tasks',
|
|
157
|
+
"List all scheduled tasks. From main: shows all tasks. From other groups: shows only that group's tasks.",
|
|
158
|
+
{},
|
|
159
|
+
async () => {
|
|
160
|
+
const tasksFile = path.join(IPC_DIR, 'current_tasks.json');
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
if (!fs.existsSync(tasksFile)) {
|
|
164
|
+
return { content: [{ type: 'text' as const, text: 'No scheduled tasks found.' }] };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const allTasks = JSON.parse(fs.readFileSync(tasksFile, 'utf-8'));
|
|
168
|
+
|
|
169
|
+
const tasks = isMain
|
|
170
|
+
? allTasks
|
|
171
|
+
: allTasks.filter((t: { groupFolder: string }) => t.groupFolder === groupFolder);
|
|
172
|
+
|
|
173
|
+
if (tasks.length === 0) {
|
|
174
|
+
return { content: [{ type: 'text' as const, text: 'No scheduled tasks found.' }] };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const formatted = tasks
|
|
178
|
+
.map(
|
|
179
|
+
(t: { id: string; prompt: string; schedule_type: string; schedule_value: string; status: string; next_run: string }) =>
|
|
180
|
+
`- [${t.id}] ${t.prompt.slice(0, 50)}... (${t.schedule_type}: ${t.schedule_value}) - ${t.status}, next: ${t.next_run || 'N/A'}`,
|
|
181
|
+
)
|
|
182
|
+
.join('\n');
|
|
183
|
+
|
|
184
|
+
return { content: [{ type: 'text' as const, text: `Scheduled tasks:\n${formatted}` }] };
|
|
185
|
+
} catch (err) {
|
|
186
|
+
return {
|
|
187
|
+
content: [{ type: 'text' as const, text: `Error reading tasks: ${err instanceof Error ? err.message : String(err)}` }],
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
server.tool(
|
|
194
|
+
'pause_task',
|
|
195
|
+
'Pause a scheduled task. It will not run until resumed.',
|
|
196
|
+
{ task_id: z.string().describe('The task ID to pause') },
|
|
197
|
+
async (args) => {
|
|
198
|
+
const data = {
|
|
199
|
+
type: 'pause_task',
|
|
200
|
+
taskId: args.task_id,
|
|
201
|
+
groupFolder,
|
|
202
|
+
isMain,
|
|
203
|
+
timestamp: new Date().toISOString(),
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
writeIpcFile(TASKS_DIR, data);
|
|
207
|
+
|
|
208
|
+
return { content: [{ type: 'text' as const, text: `Task ${args.task_id} pause requested.` }] };
|
|
209
|
+
},
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
server.tool(
|
|
213
|
+
'resume_task',
|
|
214
|
+
'Resume a paused task.',
|
|
215
|
+
{ task_id: z.string().describe('The task ID to resume') },
|
|
216
|
+
async (args) => {
|
|
217
|
+
const data = {
|
|
218
|
+
type: 'resume_task',
|
|
219
|
+
taskId: args.task_id,
|
|
220
|
+
groupFolder,
|
|
221
|
+
isMain,
|
|
222
|
+
timestamp: new Date().toISOString(),
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
writeIpcFile(TASKS_DIR, data);
|
|
226
|
+
|
|
227
|
+
return { content: [{ type: 'text' as const, text: `Task ${args.task_id} resume requested.` }] };
|
|
228
|
+
},
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
server.tool(
|
|
232
|
+
'cancel_task',
|
|
233
|
+
'Cancel and delete a scheduled task.',
|
|
234
|
+
{ task_id: z.string().describe('The task ID to cancel') },
|
|
235
|
+
async (args) => {
|
|
236
|
+
const data = {
|
|
237
|
+
type: 'cancel_task',
|
|
238
|
+
taskId: args.task_id,
|
|
239
|
+
groupFolder,
|
|
240
|
+
isMain,
|
|
241
|
+
timestamp: new Date().toISOString(),
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
writeIpcFile(TASKS_DIR, data);
|
|
245
|
+
|
|
246
|
+
return { content: [{ type: 'text' as const, text: `Task ${args.task_id} cancellation requested.` }] };
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
server.tool(
|
|
251
|
+
'update_task',
|
|
252
|
+
'Update an existing scheduled task. Only provided fields are changed; omitted fields stay the same.',
|
|
253
|
+
{
|
|
254
|
+
task_id: z.string().describe('The task ID to update'),
|
|
255
|
+
prompt: z.string().optional().describe('New prompt for the task'),
|
|
256
|
+
schedule_type: z.enum(['cron', 'interval', 'once']).optional().describe('New schedule type'),
|
|
257
|
+
schedule_value: z.string().optional().describe('New schedule value (see schedule_task for format)'),
|
|
258
|
+
},
|
|
259
|
+
async (args) => {
|
|
260
|
+
// Validate schedule_value if provided
|
|
261
|
+
if (args.schedule_type === 'cron' || (!args.schedule_type && args.schedule_value)) {
|
|
262
|
+
if (args.schedule_value) {
|
|
263
|
+
try {
|
|
264
|
+
CronExpressionParser.parse(args.schedule_value);
|
|
265
|
+
} catch {
|
|
266
|
+
return {
|
|
267
|
+
content: [{ type: 'text' as const, text: `Invalid cron: "${args.schedule_value}".` }],
|
|
268
|
+
isError: true,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (args.schedule_type === 'interval' && args.schedule_value) {
|
|
274
|
+
const ms = parseInt(args.schedule_value, 10);
|
|
275
|
+
if (isNaN(ms) || ms <= 0) {
|
|
276
|
+
return {
|
|
277
|
+
content: [{ type: 'text' as const, text: `Invalid interval: "${args.schedule_value}".` }],
|
|
278
|
+
isError: true,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const data: Record<string, string | undefined> = {
|
|
284
|
+
type: 'update_task',
|
|
285
|
+
taskId: args.task_id,
|
|
286
|
+
groupFolder,
|
|
287
|
+
isMain: String(isMain),
|
|
288
|
+
timestamp: new Date().toISOString(),
|
|
289
|
+
};
|
|
290
|
+
if (args.prompt !== undefined) data.prompt = args.prompt;
|
|
291
|
+
if (args.schedule_type !== undefined) data.schedule_type = args.schedule_type;
|
|
292
|
+
if (args.schedule_value !== undefined) data.schedule_value = args.schedule_value;
|
|
293
|
+
|
|
294
|
+
writeIpcFile(TASKS_DIR, data);
|
|
295
|
+
|
|
296
|
+
return { content: [{ type: 'text' as const, text: `Task ${args.task_id} update requested.` }] };
|
|
297
|
+
},
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
server.tool(
|
|
301
|
+
'register_group',
|
|
302
|
+
`Register a new chat/group so the agent can respond to messages there. Main group only.
|
|
303
|
+
|
|
304
|
+
Use available_groups.json to find the JID for a group. The folder name must be channel-prefixed: "{channel}_{group-name}" (e.g., "whatsapp_family-chat", "telegram_dev-team", "discord_general"). Use lowercase with hyphens for the group name part.`,
|
|
305
|
+
{
|
|
306
|
+
jid: z.string().describe('The chat JID (e.g., "120363336345536173@g.us", "tg:-1001234567890", "dc:1234567890123456")'),
|
|
307
|
+
name: z.string().describe('Display name for the group'),
|
|
308
|
+
folder: z.string().describe('Channel-prefixed folder name (e.g., "whatsapp_family-chat", "telegram_dev-team")'),
|
|
309
|
+
trigger: z.string().describe('Trigger word (e.g., "@Andy")'),
|
|
310
|
+
},
|
|
311
|
+
async (args) => {
|
|
312
|
+
if (!isMain) {
|
|
313
|
+
return {
|
|
314
|
+
content: [{ type: 'text' as const, text: 'Only the main group can register new groups.' }],
|
|
315
|
+
isError: true,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const data = {
|
|
320
|
+
type: 'register_group',
|
|
321
|
+
jid: args.jid,
|
|
322
|
+
name: args.name,
|
|
323
|
+
folder: args.folder,
|
|
324
|
+
trigger: args.trigger,
|
|
325
|
+
timestamp: new Date().toISOString(),
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
writeIpcFile(TASKS_DIR, data);
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
content: [{ type: 'text' as const, text: `Group "${args.name}" registered. It will start receiving messages immediately.` }],
|
|
332
|
+
};
|
|
333
|
+
},
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
// Start the stdio transport
|
|
337
|
+
const transport = new StdioServerTransport();
|
|
338
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"declaration": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"],
|
|
14
|
+
"exclude": ["node_modules", "dist"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Build the NanoClaw agent container image
|
|
3
|
+
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
cd "$SCRIPT_DIR"
|
|
8
|
+
|
|
9
|
+
IMAGE_NAME="nanoclaw-agent"
|
|
10
|
+
TAG="${1:-latest}"
|
|
11
|
+
CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-docker}"
|
|
12
|
+
|
|
13
|
+
echo "Building NanoClaw agent container image..."
|
|
14
|
+
echo "Image: ${IMAGE_NAME}:${TAG}"
|
|
15
|
+
|
|
16
|
+
${CONTAINER_RUNTIME} build -t "${IMAGE_NAME}:${TAG}" .
|
|
17
|
+
|
|
18
|
+
echo ""
|
|
19
|
+
echo "Build complete!"
|
|
20
|
+
echo "Image: ${IMAGE_NAME}:${TAG}"
|
|
21
|
+
echo ""
|
|
22
|
+
echo "Test with:"
|
|
23
|
+
echo " echo '{\"prompt\":\"What is 2+2?\",\"groupFolder\":\"test\",\"chatJid\":\"test@g.us\",\"isMain\":false}' | ${CONTAINER_RUNTIME} run -i ${IMAGE_NAME}:${TAG}"
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-browser
|
|
3
|
+
description: Browse the web for any task — research topics, read articles, interact with web apps, fill forms, take screenshots, extract data, and test web pages. Use whenever a browser would be useful, not just when the user explicitly asks.
|
|
4
|
+
allowed-tools: Bash(agent-browser:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Browser Automation with agent-browser
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
agent-browser open <url> # Navigate to page
|
|
13
|
+
agent-browser snapshot -i # Get interactive elements with refs
|
|
14
|
+
agent-browser click @e1 # Click element by ref
|
|
15
|
+
agent-browser fill @e2 "text" # Fill input by ref
|
|
16
|
+
agent-browser close # Close browser
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Core workflow
|
|
20
|
+
|
|
21
|
+
1. Navigate: `agent-browser open <url>`
|
|
22
|
+
2. Snapshot: `agent-browser snapshot -i` (returns elements with refs like `@e1`, `@e2`)
|
|
23
|
+
3. Interact using refs from the snapshot
|
|
24
|
+
4. Re-snapshot after navigation or significant DOM changes
|
|
25
|
+
|
|
26
|
+
## Commands
|
|
27
|
+
|
|
28
|
+
### Navigation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
agent-browser open <url> # Navigate to URL
|
|
32
|
+
agent-browser back # Go back
|
|
33
|
+
agent-browser forward # Go forward
|
|
34
|
+
agent-browser reload # Reload page
|
|
35
|
+
agent-browser close # Close browser
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Snapshot (page analysis)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
agent-browser snapshot # Full accessibility tree
|
|
42
|
+
agent-browser snapshot -i # Interactive elements only (recommended)
|
|
43
|
+
agent-browser snapshot -c # Compact output
|
|
44
|
+
agent-browser snapshot -d 3 # Limit depth to 3
|
|
45
|
+
agent-browser snapshot -s "#main" # Scope to CSS selector
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Interactions (use @refs from snapshot)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
agent-browser click @e1 # Click
|
|
52
|
+
agent-browser dblclick @e1 # Double-click
|
|
53
|
+
agent-browser fill @e2 "text" # Clear and type
|
|
54
|
+
agent-browser type @e2 "text" # Type without clearing
|
|
55
|
+
agent-browser press Enter # Press key
|
|
56
|
+
agent-browser hover @e1 # Hover
|
|
57
|
+
agent-browser check @e1 # Check checkbox
|
|
58
|
+
agent-browser uncheck @e1 # Uncheck checkbox
|
|
59
|
+
agent-browser select @e1 "value" # Select dropdown option
|
|
60
|
+
agent-browser scroll down 500 # Scroll page
|
|
61
|
+
agent-browser upload @e1 file.pdf # Upload files
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Get information
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
agent-browser get text @e1 # Get element text
|
|
68
|
+
agent-browser get html @e1 # Get innerHTML
|
|
69
|
+
agent-browser get value @e1 # Get input value
|
|
70
|
+
agent-browser get attr @e1 href # Get attribute
|
|
71
|
+
agent-browser get title # Get page title
|
|
72
|
+
agent-browser get url # Get current URL
|
|
73
|
+
agent-browser get count ".item" # Count matching elements
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Screenshots & PDF
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
agent-browser screenshot # Save to temp directory
|
|
80
|
+
agent-browser screenshot path.png # Save to specific path
|
|
81
|
+
agent-browser screenshot --full # Full page
|
|
82
|
+
agent-browser pdf output.pdf # Save as PDF
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Wait
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
agent-browser wait @e1 # Wait for element
|
|
89
|
+
agent-browser wait 2000 # Wait milliseconds
|
|
90
|
+
agent-browser wait --text "Success" # Wait for text
|
|
91
|
+
agent-browser wait --url "**/dashboard" # Wait for URL pattern
|
|
92
|
+
agent-browser wait --load networkidle # Wait for network idle
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Semantic locators (alternative to refs)
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
agent-browser find role button click --name "Submit"
|
|
99
|
+
agent-browser find text "Sign In" click
|
|
100
|
+
agent-browser find label "Email" fill "user@test.com"
|
|
101
|
+
agent-browser find placeholder "Search" type "query"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Authentication with saved state
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Login once
|
|
108
|
+
agent-browser open https://app.example.com/login
|
|
109
|
+
agent-browser snapshot -i
|
|
110
|
+
agent-browser fill @e1 "username"
|
|
111
|
+
agent-browser fill @e2 "password"
|
|
112
|
+
agent-browser click @e3
|
|
113
|
+
agent-browser wait --url "**/dashboard"
|
|
114
|
+
agent-browser state save auth.json
|
|
115
|
+
|
|
116
|
+
# Later: load saved state
|
|
117
|
+
agent-browser state load auth.json
|
|
118
|
+
agent-browser open https://app.example.com/dashboard
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Cookies & Storage
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
agent-browser cookies # Get all cookies
|
|
125
|
+
agent-browser cookies set name value # Set cookie
|
|
126
|
+
agent-browser cookies clear # Clear cookies
|
|
127
|
+
agent-browser storage local # Get localStorage
|
|
128
|
+
agent-browser storage local set k v # Set value
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### JavaScript
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
agent-browser eval "document.title" # Run JavaScript
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Example: Form submission
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
agent-browser open https://example.com/form
|
|
141
|
+
agent-browser snapshot -i
|
|
142
|
+
# Output shows: textbox "Email" [ref=e1], textbox "Password" [ref=e2], button "Submit" [ref=e3]
|
|
143
|
+
|
|
144
|
+
agent-browser fill @e1 "user@example.com"
|
|
145
|
+
agent-browser fill @e2 "password123"
|
|
146
|
+
agent-browser click @e3
|
|
147
|
+
agent-browser wait --load networkidle
|
|
148
|
+
agent-browser snapshot -i # Check result
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Example: Data extraction
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
agent-browser open https://example.com/products
|
|
155
|
+
agent-browser snapshot -i
|
|
156
|
+
agent-browser get text @e1 # Get product title
|
|
157
|
+
agent-browser get attr @e2 href # Get link URL
|
|
158
|
+
agent-browser screenshot products.png
|
|
159
|
+
```
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: capabilities
|
|
3
|
+
description: Show what this NanoClaw instance can do — installed skills, available tools, and system info. Read-only. Use when the user asks what the bot can do, what's installed, or runs /capabilities.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /capabilities — System Capabilities Report
|
|
7
|
+
|
|
8
|
+
Generate a structured read-only report of what this NanoClaw instance can do.
|
|
9
|
+
|
|
10
|
+
**Main-channel check:** Only the main channel has `/workspace/project` mounted. Run:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
test -d /workspace/project && echo "MAIN" || echo "NOT_MAIN"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
If `NOT_MAIN`, respond with:
|
|
17
|
+
> This command is available in your main chat only. Send `/capabilities` there to see what I can do.
|
|
18
|
+
|
|
19
|
+
Then stop — do not generate the report.
|
|
20
|
+
|
|
21
|
+
## How to gather the information
|
|
22
|
+
|
|
23
|
+
Run these commands and compile the results into the report format below.
|
|
24
|
+
|
|
25
|
+
### 1. Installed skills
|
|
26
|
+
|
|
27
|
+
List skill directories available to you:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
ls -1 /home/node/.claude/skills/ 2>/dev/null || echo "No skills found"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Each directory is an installed skill. The directory name is the skill name (e.g., `agent-browser` → `/agent-browser`).
|
|
34
|
+
|
|
35
|
+
### 2. Available tools
|
|
36
|
+
|
|
37
|
+
Read the allowed tools from your SDK configuration. You always have access to:
|
|
38
|
+
- **Core:** Bash, Read, Write, Edit, Glob, Grep
|
|
39
|
+
- **Web:** WebSearch, WebFetch
|
|
40
|
+
- **Orchestration:** Task, TaskOutput, TaskStop, TeamCreate, TeamDelete, SendMessage
|
|
41
|
+
- **Other:** TodoWrite, ToolSearch, Skill, NotebookEdit
|
|
42
|
+
- **MCP:** mcp__nanoclaw__* (messaging, tasks, group management)
|
|
43
|
+
|
|
44
|
+
### 3. MCP server tools
|
|
45
|
+
|
|
46
|
+
The NanoClaw MCP server exposes these tools (via `mcp__nanoclaw__*` prefix):
|
|
47
|
+
- `send_message` — send a message to the user/group
|
|
48
|
+
- `schedule_task` — schedule a recurring or one-time task
|
|
49
|
+
- `list_tasks` — list scheduled tasks
|
|
50
|
+
- `pause_task` — pause a scheduled task
|
|
51
|
+
- `resume_task` — resume a paused task
|
|
52
|
+
- `cancel_task` — cancel and delete a task
|
|
53
|
+
- `update_task` — update an existing task
|
|
54
|
+
- `register_group` — register a new chat/group (main only)
|
|
55
|
+
|
|
56
|
+
### 4. Container skills (Bash tools)
|
|
57
|
+
|
|
58
|
+
Check for executable tools in the container:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
which agent-browser 2>/dev/null && echo "agent-browser: available" || echo "agent-browser: not found"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 5. Group info
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
ls /workspace/group/CLAUDE.md 2>/dev/null && echo "Group memory: yes" || echo "Group memory: no"
|
|
68
|
+
ls /workspace/extra/ 2>/dev/null && echo "Extra mounts: $(ls /workspace/extra/ 2>/dev/null | wc -l | tr -d ' ')" || echo "Extra mounts: none"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Report format
|
|
72
|
+
|
|
73
|
+
Present the report as a clean, readable message. Example:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
📋 *NanoClaw Capabilities*
|
|
77
|
+
|
|
78
|
+
*Installed Skills:*
|
|
79
|
+
• /agent-browser — Browse the web, fill forms, extract data
|
|
80
|
+
• /capabilities — This report
|
|
81
|
+
(list all found skills)
|
|
82
|
+
|
|
83
|
+
*Tools:*
|
|
84
|
+
• Core: Bash, Read, Write, Edit, Glob, Grep
|
|
85
|
+
• Web: WebSearch, WebFetch
|
|
86
|
+
• Orchestration: Task, TeamCreate, SendMessage
|
|
87
|
+
• MCP: send_message, schedule_task, list_tasks, pause/resume/cancel/update_task, register_group
|
|
88
|
+
|
|
89
|
+
*Container Tools:*
|
|
90
|
+
• agent-browser: ✓
|
|
91
|
+
|
|
92
|
+
*System:*
|
|
93
|
+
• Group memory: yes/no
|
|
94
|
+
• Extra mounts: N directories
|
|
95
|
+
• Main channel: yes
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Adapt the output based on what you actually find — don't list things that aren't installed.
|
|
99
|
+
|
|
100
|
+
**See also:** `/status` for a quick health check of session, workspace, and tasks.
|