opencrabs 0.3.7

The autonomous, self-improving AI agent. Single Rust binary. Every channel. Install with: cargo install opencrabs
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
# TOOLS.md - Local Notes

Skills define *how* tools work. This file is for *your* specifics — the stuff that's unique to your setup.

## Custom Skills & Plugins

Your custom implementations live here in the workspace, **never in the repo**:

```
~/.opencrabs/
├── skills/       # Custom skills you create or install
├── plugins/      # Custom plugins and extensions
├── scripts/      # Custom automation scripts
```

This ensures `git pull` on the repo never overwrites your work. See AGENTS.md for the full workspace layout.

### Rust-First Policy
When building custom tools or adding dependencies, **always prioritize Rust-based crates** over wrappers, FFI bindings, or other-language alternatives. Native Rust = lean, safe, fast.

## Tool Parameter Reference

Use these **exact parameter names** when calling tools:

| Tool | Required Params | Optional Params |
|------|----------------|-----------------|
| `ls` | `path` | `recursive` |
| `glob` | `pattern` | `path` |
| `grep` | `pattern` | `path`, `regex`, `case_insensitive`, `file_pattern`, `limit`, `context` |
| `read_file` | `path` | `line_range` |
| `edit_file` | `path`, `operation` | `old_text`, `new_text`, `line` |
| `write_file` | `path`, `content` | — |
| `bash` | `command` | `timeout` |
| `execute_code` | `language`, `code` | — |
| `web_search` | `query` | `n` |
| `brave_search` | `query` | `max_results` |
| `exa_search` | `query` | `max_results`, `search_type` |
| `http_request` | `method`, `url` | `headers`, `body` |
| `session_search` | `operation` | `query`, `n`, `session_id` |
| `task_manager` | `operation` | `title`, `description`, `task_id`, `status` |
| `plan` | `operation` | `title`, `description`, `task` |
| `session_context` | `operation` | `key`, `value` |
| `generate_image` | `prompt` | `filename` |
| `analyze_image` | `image` | `question` |
| `trello_connect` | `api_key`, `api_token`, `boards` | `allowed_users` |
| `trello_send` | `action` | `board_id`, `list_name`, `card_id`, `title`, `description`, `text`, `position`, `pattern`, `member_id`, `label_id`, `due_date`, `due_complete`, `checklist_id`, `item_id`, `complete`, `query`, `read_filter`, `limit`, `file_path` |
| `discord_connect` | `token`, `allowed_users` | `channel_id` |
| `discord_send` | `action` | `message`, `channel_id`, `message_id`, `emoji`, `embed_title`, `embed_description`, `embed_color`, `thread_name`, `user_id`, `role_id`, `limit`, `file_path`, `caption` |
| `telegram_send` | `action` | `message`, `chat_id`, `message_id`, `from_chat_id`, `photo_url`, `document_url`, `latitude`, `longitude`, `poll_question`, `poll_options`, `buttons`, `user_id`, `emoji` |
| `channel_search` | `operation` | `channel`, `chat_id`, `query`, `n` |
| `cron_manage` | `action` | `name`, `cron`, `tz`, `prompt`, `provider`, `model`, `thinking`, `auto_approve`, `deliver_to`, `job_id`, `enabled` |
| `slack_send` | `action` | `message`, `channel_id`, `thread_ts`, `message_ts`, `emoji`, `user_id`, `topic`, `blocks`, `limit`, `file_path`, `caption` |
| `notebook_edit` | `path`, `operation` | `cell_type`, `source`, `position`, `index`, `create_backup` |
| `parse_document` | `path` | `max_chars`, `pages`, `include_metadata` |
| `memory_search` | `query` | `n` |
| `config_manager` | `operation` | `section`, `key`, `value`, `command_name`, `command_description`, `command_prompt`, `command_action`, `path` |
| `tool_manage` | `action` | `name`, `description`, `executor`, `method`, `url`, `headers`, `command`, `params`, `requires_approval`, `timeout_secs` |
| `a2a_send` | `action`, `url` | `message`, `task_id`, `context_id`, `api_key` |
| `whatsapp_send` | `message` | `phone` |
| `whatsapp_connect` | — | `allowed_phones` |
| `browser_navigate` | `url` | `headless` |
| `browser_click` | `selector` | — |
| `browser_type` | `text` | `selector` |
| `browser_screenshot` | — | `selector` |
| `browser_eval` | `script` | — |
| `browser_content` | — | `selector`, `text_only` |
| `browser_wait` | — | `selector`, `timeout_secs`, `delay_secs` |
| `evolve` | — | `check_only` |
| `rebuild` | — | — |
| `feedback_record` | `event_type`, `dimension` | `value`, `metadata` |
| `feedback_analyze` | `query` | `limit` |
| `self_improve` | `action` | `target_file`, `description`, `rationale`, `content` |
| `spawn_agent` | `prompt` | `label` |
| `wait_agent` | `agent_id` | `timeout_secs` |
| `send_input` | `agent_id`, `text` | — |
| `close_agent` | `agent_id` | `remove` |
| `resume_agent` | `agent_id`, `prompt` | — |

> **RSI tools (Recursive Self-Improvement):** `feedback_record` logs observations to the feedback ledger — `event_type` is one of `tool_success`, `tool_failure`, `user_correction`, `provider_error`, `context_compaction`, `improvement_applied`, `pattern_observed`. `dimension` identifies what was observed (tool name, provider name, pattern label). `value` is numeric (1.0 = success, 0.0 = failure). `metadata` is optional free-text context. `feedback_analyze` queries the ledger — `query` is `summary` (overall stats), `tool_stats` (per-tool success/failure rates), `recent` (last N events), or `failures` (recent failures only). `limit` caps result count (default 50). `self_improve` modifies brain files autonomously — `action` is `apply` (edit brain file + log to ~/.opencrabs/rsi/) or `list` (show improvements). `target_file` must be a known brain file. No human approval needed. Changes are logged to `~/.opencrabs/rsi/improvements.md` and archived in `~/.opencrabs/rsi/history/YYYY-MM-DD.md`. Tool executions are auto-recorded to the feedback ledger — you don't need to call `feedback_record` for every tool call.
> **Sub-agent tools:** Use `spawn_agent` to delegate independent sub-tasks to child agents that run in parallel. Each child gets its own session and essential tools (read, write, edit, bash, glob, grep, ls, web_search) with auto-approve. Use `wait_agent` to collect results, `send_input` for follow-up instructions, `close_agent` to cancel, and `resume_agent` to continue a completed agent with new work. Children cannot spawn their own sub-agents (no recursive spawning).
> **Note:** `grep` and `glob` use `pattern` (not `query`). `bash` uses `command` (not `cmd`). File tools use `path` (not `file` or `file_path`).
> **Search tools:** Multiple web search tools are available. Defaults work out of the box; optional tools appear when the user configures API keys:
> - `web_search` — Default search (DuckDuckGo). Always available, no API key needed.
> - `exa_search` — EXA AI neural/semantic search. Available by default via free MCP endpoint (no key needed). Optional: set `EXA_API_KEY` in keys.toml for direct API access with higher rate limits. `search_type` can be `"auto"`, `"neural"`, `"fast"`, `"deep-lite"`, `"deep"`, `"deep-reasoning"`, or `"instant"`.
> - `brave_search` — **Optional.** Brave Search API with privacy focus. Available only when the user sets `[search.brave] enabled = true` in config and provides `BRAVE_API_KEY` in keys.toml. If this tool is in your tool list, prefer it for general web searches.
> When the user asks you to search the web, **check which search tools are available in your tool list**. If `brave_search` or `exa_search` are present, prefer them over `web_search` — they provide better results.
> **Incoming images/files:** When a user sends an image or file from any channel (Telegram, Discord, Slack, WhatsApp), it is downloaded to a temp file and included in the message as `<<IMG:/path/to/file>>`. The file exists at that path — you can read it, pass it to `analyze_image`, attach it to tool calls, or reference it in `bash` commands. The image is also sent to the model as vision content if the provider supports it. Do NOT ask the user to re-send or provide a URL — you already have the file.
> **`generate_image`:** Generate an image from a text prompt using Google Gemini. Returns the saved file path. Automatically sends as a native image on all channels — just include `<<IMG:path>>` in your reply or the channel handler sends it for you. Requires `[image.generation] enabled = true` in config. Run `/onboard:image` to set up.
> **`analyze_image`:** Analyze an image file (local path) or URL. Uses Google Gemini vision when configured (`[image.vision] enabled = true`), otherwise uses the provider's `vision_model` if set. Use when the current model doesn't support vision, the image is a saved file, or the user sends an image. Returns a text description.
> **Provider vision model:** If your default model doesn't support vision but another model on the same provider does, set `vision_model` in your provider config. When you call `analyze_image`, it uses the vision model on the same provider API to describe the image and returns the description as text — the chat model stays the same and gets vision capability via tool call. Gemini vision takes priority when configured. Example: MiniMax M2.7 auto-sets `vision_model = "MiniMax-Text-01"` on first run. Config: `[providers.minimax] vision_model = "MiniMax-Text-01"`.
> **Fallback providers:** If your primary LLM provider goes down, configure fallback providers to automatically retry with alternatives. Any previously configured provider (with API keys already set) can be listed as a fallback. Config: `[providers.fallback] enabled = true` with `providers = ["openrouter", "anthropic"]` (array, tried in order). Supports single (`provider = "openrouter"`) or multiple. Crabs can set this up for the human via config.toml — just add the fallback section and list providers that already have keys configured. At runtime, if the primary provider fails a request, each fallback is tried in sequence until one succeeds.
> **Trello:** `trello_connect` `boards` is an array of board names or IDs. Use `trello_send` for all Trello operations — fall back to `http_request` only if `trello_send` is unavailable. Credentials are handled securely without exposing them in URLs.
> **`trello_send` actions (22):** `add_comment`, `create_card`, `move_card`, `find_cards`, `list_boards`, `get_card`, `get_card_comments`, `update_card`, `archive_card`, `add_member_to_card`, `remove_member_from_card`, `add_label_to_card`, `remove_label_from_card`, `add_checklist`, `add_checklist_item`, `complete_checklist_item`, `list_lists`, `get_board_members`, `search`, `get_notifications`, `mark_notifications_read`, `add_attachment`
> **`add_attachment`:** Upload a local file to a Trello card. Returns the attachment URL — use `![image](url)` in a follow-up `add_comment` to display it inline.
> **Discord:** `discord_connect` `allowed_users` is an array of numeric Discord user IDs. Use `discord_send` for all Discord operations — fall back to `http_request` only if `discord_send` is unavailable. Credentials are handled securely.
> **`discord_send` actions (17):** `send`, `reply`, `react`, `unreact`, `edit`, `delete`, `pin`, `unpin`, `create_thread`, `send_embed`, `get_messages`, `list_channels`, `add_role`, `remove_role`, `kick`, `ban`, `send_file`
> **Guild-required actions:** `list_channels`, `add_role`, `remove_role`, `kick`, `ban` — these need the bot to have received at least one guild message first so the guild_id is available.
> **Telegram:** Use `telegram_send` for all Telegram operations — fall back to `http_request` only if `telegram_send` is unavailable. Credentials handled securely.
> **`telegram_send` actions (19):** `send`, `reply`, `edit`, `delete`, `pin`, `unpin`, `forward`, `send_photo`, `send_document`, `send_location`, `send_poll`, `send_buttons`, `get_chat`, `get_chat_administrators`, `get_chat_member_count`, `get_chat_member`, `ban_user`, `unban_user`, `set_reaction`
> **`channel_search` operations (3):** `list_chats` (show known chats with message counts), `recent` (last N messages in a chat), `search` (find messages by keyword). Telegram Bot API cannot fetch message history due to privacy — OpenCrabs passively captures group messages as they arrive and stores them for later search. Works across all channels (Telegram, Discord, Slack, WhatsApp). If `list_chats` returns empty, it means no messages have been captured yet — ask the user to send a message in the group first, or ask for the chat_id directly and use `telegram_send get_chat` to fetch chat info.
> **`cron_manage` actions (5):** `create` (schedule a new cron job), `list` (show all jobs), `delete` (remove a job by id), `enable` (activate a paused job), `disable` (pause a job without deleting). Jobs run in isolated sessions with configurable provider/model/thinking. Use `deliver_to` to send results to a channel (e.g. `telegram:chat_id`, `discord:channel_id`). Cron expressions follow standard 5-field format (min hour dom mon dow). See AGENTS.md for best practices on heartbeat vs cron.
> **`evolve`:** Download the latest OpenCrabs release binary from GitHub and hot-restart. Use `check_only: true` to check for updates without installing. Works on all platforms (macOS arm64/amd64, Linux arm64/amd64, Windows amd64). No Rust toolchain needed — downloads pre-built binaries. Falls back to legacy asset naming for older releases. Available as `/evolve` slash command on TUI and all channels.
> **`rebuild`:** Build OpenCrabs from source (`cargo build --release`) and hot-restart. Use when you need to build from source (e.g. after editing code). Requires Rust toolchain. Available as `/rebuild` slash command.
> **`notebook_edit`:** Edit Jupyter notebooks (.ipynb). Operations: `add_cell` (insert a new cell), `edit_cell` (modify cell source), `delete_cell` (remove by index), `move_cell` (reorder). `cell_type` is `code` or `markdown`. `position` is insertion index for `add_cell`.
> **`parse_document`:** Extract text from PDF, DOCX, HTML, and other document formats. Returns plain text content. Use `pages` to limit PDF page range (e.g. `"1-5"`). Use `max_chars` to truncate long documents.
> **`memory_search`:** Hybrid semantic search across past memory logs. Combines FTS5 keyword search + vector embeddings (768-dim, local GGUF model) via Reciprocal Rank Fusion. No API key needed, runs entirely offline. `n` controls number of results (default 5).
> **`config_manager`:** Read/write `config.toml` and `commands.toml` at runtime. Operations: `read_config` (read a section or key), `write_config` (set a key), `add_command` (create a slash command), `remove_command` (delete one), `list_commands` (show all), `set_working_directory` (change CWD). Changes are picked up by the config watcher within ~300ms.
> **`tool_manage`:** Manage runtime tools defined in `tools.toml`. Actions: `list` (show all tools), `add` (create new tool), `remove` (delete tool), `enable`/`disable` (toggle), `reload` (re-read tools.toml). Executor is `http` or `shell`. Template variables (`{{param}}`) are substituted in URL/command.
> **`a2a_send`:** Send tasks to remote A2A-compatible agents. Actions: `discover` (fetch Agent Card), `send` (send task message), `get` (check task status), `cancel` (cancel running task). `url` is the agent's base URL. `context_id` links multiple messages in a conversation.
> **`whatsapp_send`:** Send a WhatsApp message. `message` is the text content. `phone` is the recipient phone number (optional — defaults to the current chat).
> **`whatsapp_connect`:** Connect to WhatsApp via QR code pairing. `allowed_phones` filters which numbers can interact with the bot.
> **Browser tools:** Auto-detect and connect to your default Chromium-based browser (Chrome, Brave, Edge, Arc, Vivaldi, Opera, Chromium). Uses native profile (cookies, logins, extensions). `browser_navigate` launches the browser if not connected. `headless: true` runs without visible window. `browser_content` with `text_only: true` strips HTML tags. `browser_wait` polls every 200ms until `selector` appears or `timeout_secs` expires. `browser_eval` runs arbitrary JavaScript and returns the result. All browser tools share one persistent browser session per OpenCrabs instance.
> **Slack:** Always use `slack_send` instead of `http_request` for Slack — credentials handled securely. `thread_ts` and `message_ts` are Slack timestamps (e.g. `1503435956.000247`). Emoji names have no colons (e.g. `thumbsup`).
> **`slack_send` actions (17):** `send`, `reply`, `react`, `unreact`, `edit`, `delete`, `pin`, `unpin`, `get_messages`, `get_channel`, `list_channels`, `get_user`, `list_members`, `kick_user`, `set_topic`, `send_blocks`, `send_file`

## Browser Automation (CDP)

OpenCrabs has **built-in browser control** via Chrome DevTools Protocol — no Node.js, no Playwright, pure Rust. Launches a real Chrome/Chromium instance and controls it programmatically.

| Tool | What it does |
|------|-------------|
| `browser_navigate` | Navigate to URL. Pass `headless: false` for visible window. |
| `browser_click` | Click element by CSS selector. |
| `browser_type` | Type text into input field by selector. |
| `browser_screenshot` | Full-page or element screenshot. Returns file path. |
| `browser_eval` | Execute JavaScript in page context. Returns result. |
| `browser_content` | Extract text/HTML from page or element. |
| `browser_wait` | Wait for selector to appear or fixed delay. |

- **Headless (default):** No visible window. Fast, low resources. Use for automation and scraping.
- **Headed:** Visible Chrome window. Pass `headless: false`. Use for debugging.
- **Compose workflows:** navigate → wait → click → type → screenshot is a typical flow.
- **Screenshots return file paths.** Use `<<IMG:path>>` to send to channels.
- **Requires** Chrome/Chromium installed. Feature-gated: `browser` flag (enabled with `--all-features`).

---

## Dynamic Tools (Runtime)

Define tools in `~/.opencrabs/tools.toml` — callable immediately without restart. Managed via `tool_manage`.

### tool_manage actions

| Action | What |
|--------|------|
| `list` | Show all dynamic tools with enabled/disabled status |
| `add` | Create new tool (persists to tools.toml) |
| `remove` | Delete a tool |
| `enable` / `disable` | Toggle without removing |
| `reload` | Hot-reload from tools.toml |

### Executor Types

- **`http`** — Config: `method`, `url`, `headers`, `body_template`
- **`shell`** — Config: `command`, `args`, `working_dir`

### tools.toml Format

```toml
[[tools]]
name = "check_api_health"
description = "Check if the production API is responding"
executor_type = "http"
enabled = true

[tools.executor_config]
method = "GET"
url = "https://api.example.com/health"

[[tools]]
name = "disk_usage"
description = "Check disk usage on the system"
executor_type = "shell"
enabled = true

[tools.executor_config]
command = "df"
args = ["-h"]
```

Dynamic tools appear in the LLM's tool list alongside compiled tools. The agent can call them autonomously.

### Commands vs Tools

| | `commands.toml` | `tools.toml` |
|---|---|---|
| **Triggered by** | User typing `/command` | Agent deciding to call a tool |
| **Appears in** | Slash command menu | LLM tool list |
| **Use case** | User shortcuts, macros | Agent-callable external integrations |

---

## Profile-Aware Paths

All paths resolve through `opencrabs_home()`:
- **Default profile:** `~/.opencrabs/`
- **Named profile:** `~/.opencrabs/profiles/<name>/`
- **Override:** `OPENCRABS_PROFILE` env var or `-p <name>` CLI flag

Config, keys, brain files, DB, memory, logs — all scoped per profile. Token-lock isolation prevents two profiles from using the same bot credential.

---

## System CLI Tools

OpenCrabs can leverage any CLI tool installed on the host system via `bash`. Common ones worth knowing about:

| Tool | Purpose | Check |
|------|---------|-------|
| `gh` | GitHub CLI — issues, PRs, repos, releases, actions | `gh --version` |
| `gog` | Google CLI — Gmail, Calendar (OAuth) | `gog --version` |
| `docker` | Container management | `docker --version` |
| `ssh` | Remote server access | `ssh -V` |
| `node` | Run JavaScript/TypeScript tools | `node --version` |
| `python3` | Run Python scripts and tools | `python3 --version` |
| `ffmpeg` | Audio/video processing | `ffmpeg -version` |
| `curl` | HTTP requests (fallback when `http_request` insufficient) | `curl --version` |

Before using any CLI tool, verify it's installed with the check command. Document tool-specific accounts, aliases, and usage notes below in this file.

### GitHub CLI (gh)

Authenticated GitHub CLI. Use `gh` commands instead of raw API calls:

```bash
# Issues
gh issue list
gh issue view <number>
gh issue create --title "Title" --body "Body"
gh issue close <number>
gh issue comment <number> --body "Comment"

# Pull Requests
gh pr list
gh pr view <number>
gh pr create --title "Title" --body "Body"
gh pr merge <number>
gh pr checks <number>

# Repos & Releases
gh release list
gh release create <tag> --title "Title" --notes "Notes"
gh repo view

# Actions / CI
gh run list
gh run view <run-id>
gh run watch <run-id>
```

### Google CLI (gog)

OAuth-authenticated Google Workspace CLI. Supports Gmail and Calendar.

**Setup:** Requires `GOG_KEYRING_PASSWORD` and `GOG_ACCOUNT` env vars. OAuth tokens stored in local keyring.

```bash
export GOG_KEYRING_PASSWORD="<keyring-password>"
export GOG_ACCOUNT="<google-account-email>"

# Calendar
gog calendar events --max 10
gog calendar events --start 2026-01-30 --end 2026-02-01

# Gmail — Read
gog gmail search "newer_than:1d" --max 10
gog gmail search "is:unread" --max 20
gog gmail thread <thread-id>

# Gmail — Send
gog gmail send --to recipient@email.com --subject "Subject" --body "Body"
gog gmail send --to recipient@email.com --subject "Subject" --body "Body" --cc other@email.com
```

## What Goes Here

Things like:
- SSH hosts and aliases
- API account details (not secrets — those go in `.env`)
- Camera names and locations
- Preferred voices for TTS
- Speaker/room names
- Device nicknames
- Server IPs and access methods
- Docker container inventories
- Nginx site mappings
- Custom skill/plugin notes and configuration
- CLI tool accounts and configurations (gh, gog, etc.)
- Anything environment-specific

## Path Tips
- **Workspace:** `~/.opencrabs/`
- **Path tip:** Always run `echo $HOME` or `ls ~/.opencrabs/` first to confirm the resolved path before file operations.
- OpenCrabs tools operate on the directory you launched from. Use `/cd` to change at runtime, or use `config_manager` with `set_working_directory` to change via natural language.
- **Env files:** `~/.opencrabs/.env` — chmod 600 (owner-only read)

## LLM Provider Configuration

OpenCrabs supports multiple LLM providers simultaneously. Each session can use a different provider + model.

### Adding a New Custom Provider

When a user asks to add a new AI provider (e.g. "add Groq", "connect to my OpenRouter", "add this new API"), offer them two paths:

**Path 1 — You handle it (preferred):**
> "Paste your provider details (base URL, API key, model name) and I'll add it to your config right now."

Then write these two blocks:

**`~/.opencrabs/config.toml`** — add a named section under `[providers.custom]`:
```toml
[providers.custom.groq]          # name can be anything: groq, nvidia, together, etc.
enabled = true                   # set to true to make it active; set others to false
base_url = "https://api.groq.com/openai/v1/chat/completions"
default_model = "llama-3.3-70b-versatile"
models = ["llama-3.3-70b-versatile", "mixtral-8x7b-32768"]
```

**`~/.opencrabs/keys.toml`** — add a matching section with the same label:
```toml
[providers.custom.groq]
api_key = "gsk_..."
```

**Critical rules:**
- The label after `custom.` MUST match exactly in both files (e.g. `custom.groq` ↔ `custom.groq`)
- Only one provider should have `enabled = true` at a time (the active one)
- For local LLMs (Ollama, LM Studio) — `api_key = ""` (empty is fine)
- Use `config_manager` tool with `read_config` / `write_config` to inspect and update these files safely

**Path 2 — User edits manually:**
> "Add this to `~/.opencrabs/config.toml` and the matching key to `~/.opencrabs/keys.toml`"
> Then show them the TOML blocks above filled in with their details.

### Multiple Providers Coexisting

All named providers persist — switching via `/models` just toggles `enabled`:

```toml
[providers.custom.lm_studio]
enabled = false          # currently inactive
base_url = "http://localhost:1234/v1/chat/completions"
default_model = "qwen3-coder"

[providers.custom.groq]
enabled = true           # currently active
base_url = "https://api.groq.com/openai/v1/chat/completions"
default_model = "llama-3.3-70b-versatile"

[providers.custom.nvidia]
enabled = false
base_url = "https://integrate.api.nvidia.com/v1/chat/completions"
default_model = "moonshotai/kimi-k2.5"
```

User can switch between them via `/models` in the TUI — no need to edit files manually each time.

### Per-Session Provider

Each session remembers its own provider + model. When the user switches sessions, the provider auto-restores. No need to `/models` every time.

To run two providers in parallel: open session A → send message → press `Ctrl+N` for new session B → switch provider via `/models` → send another message. Both process simultaneously.

### Provider Priority (new sessions inherit first enabled)

`providers.custom.*` → `providers.minimax` → `providers.openrouter` → `providers.anthropic` → `providers.openai`

The first provider with `enabled = true` (in config file order) is used for new sessions.

## Integrations

### Channel Connections
OpenCrabs can connect to messaging platforms. Configure in `~/.opencrabs/config.toml`:

- **Telegram** — Create a bot via @BotFather, add token to config `[channels.telegram]`. Use `telegram_send` (19 actions) for full proactive control including `get_chat`, `get_chat_administrators`, `get_chat_member_count`, and `get_chat_member`. Use `channel_search` to browse captured message history (Telegram Bot API cannot fetch history, so messages are passively stored as they arrive). Use `telegram_send` for all operations — fall back to `http_request` only if the tool is unavailable.
- **Discord** — Create a bot at discord.com/developers (enable MESSAGE CONTENT intent), add token to config `[channels.discord]`. Use `discord_connect` to set up at runtime, `discord_send` (17 actions) for full proactive control. Use `discord_send` for all operations — fall back to `http_request` only if the tool is unavailable. Use `send_file` to upload images/files; generated images (`<<IMG:path>>`) are automatically sent as native attachments.
- **WhatsApp** — Link via QR code pairing, configure `[channels.whatsapp]` with allowed phone numbers
- **Slack** — Create an app at api.slack.com/apps (enable Socket Mode), add tokens to config `[channels.slack]`. Use `slack_send` (17 actions) for full proactive control. Use `slack_send` for all operations — fall back to `http_request` only if the tool is unavailable. Use `send_file` to upload images/files; generated images (`<<IMG:path>>`) are automatically sent as native Slack file uploads.
- **Trello** — Get API Key + Token at trello.com/power-ups/admin, configure `[channels.trello]`. Tool-only by default — the AI acts on Trello only when explicitly asked via `trello_send`. Opt-in polling via `poll_interval_secs` in config. Use `trello_connect` to set up at runtime, `trello_send` (22 actions) for full proactive card/board management. Use `trello_send` for all operations — fall back to `http_request` only if the tool is unavailable. Use `add_attachment` to upload images to cards, then embed with `![image](url)` in a comment.

API keys go in `~/.opencrabs/keys.toml` (chmod 600). Channel settings go in `config.toml`.

**Trello config example:**
```toml
# keys.toml
[channels.trello]
app_token = "your-api-key"    # ~32-char key from trello.com/power-ups/admin
token = "your-api-token"      # ~64-char token from the authorization URL

# config.toml
[channels.trello]
enabled = true
allowed_channels = ["Board Name", "other-board-id"]  # names or 24-char IDs
allowed_users = []  # Trello member IDs (empty = reply to all)
```

### WhisperCrabs — Voice-to-Text (D-Bus)
[WhisperCrabs](https://github.com/adolfousier/whispercrabs) is a floating voice-to-text tool. Fully controllable via D-Bus.

**What it does:** Click to record → click to stop → transcribes → text copied to clipboard. Sound plays when ready.

**D-Bus control (full access):**
- Start/stop recording
- Switch between local (whisper.cpp) and API transcription
- Set API keys and endpoint URLs
- View transcription history
- Trigger settings dialog

**Setup:** Download binary, launch, configure via right-click menu or D-Bus commands.

**As an OpenCrabs tool:** When user asks to transcribe voice or set up voice input, use D-Bus to control WhisperCrabs — check if running, start recording, configure provider, etc.

### SocialCrabs — Social Media Automation
[SocialCrabs](https://github.com/adolfousier/socialcrabs) is a web-based social media automation tool with human-like behavior simulation (Playwright).

**Supported platforms:** Twitter/X, Instagram, LinkedIn

**Interfaces:**
- **CLI** — `node dist/cli.js <platform> <command>`
- **REST API** — port 3847
- **WebSocket** — port 3848
- **SDK** — TypeScript/JavaScript programmatic access

**Setup:**
1. Clone the repo and install dependencies
2. Add platform cookies/credentials to `.env` (see SocialCrabs README)
3. Run `node dist/cli.js session login <platform>` to authenticate

**Twitter/X commands:**
```bash
node dist/cli.js x whoami                     # Check logged-in account
node dist/cli.js x mentions -n 5              # Your mentions
node dist/cli.js x home -n 5                  # Your timeline
node dist/cli.js x search "query" -n 10       # Search tweets
node dist/cli.js x read <tweet-url>           # Read a specific tweet
node dist/cli.js x tweet "Hello world"        # Post a tweet
node dist/cli.js x reply <tweet-url> "text"   # Reply to tweet
node dist/cli.js x like <tweet-url>           # Like a tweet
node dist/cli.js x follow <username>          # Follow a user
```

**Instagram commands:**
```bash
node dist/cli.js ig like <post-url>
node dist/cli.js ig comment <post-url> "text"
node dist/cli.js ig dm <username> "message"
node dist/cli.js ig follow <username>
node dist/cli.js ig followers <username> -n 10
node dist/cli.js ig posts <username> -n 3
```

**LinkedIn commands:**
```bash
node dist/cli.js linkedin like <post-url>
node dist/cli.js linkedin comment <post-url> "text"
node dist/cli.js linkedin connect <profile-url>
node dist/cli.js linkedin search <query>
node dist/cli.js linkedin engage --query=<query>   # Full engagement session
```

**Key features:** Human-like behavior (randomized delays, natural typing), session persistence, built-in rate limiting, anti-detection, research-first workflow.

**As an OpenCrabs tool:** When user asks to post, engage, or monitor social media, use SocialCrabs CLI commands. Read operations (search, mentions, timeline) are safe. Write operations (tweet, like, follow, comment) **require explicit user approval**.

### Agent-to-Agent (A2A) Gateway
OpenCrabs exposes an A2A Protocol HTTP gateway for peer-to-peer agent communication.

**What it does:** Other A2A-compatible agents can send tasks via JSON-RPC 2.0. OpenCrabs processes them using its full tool suite and returns results.

**Endpoints:**
- `GET /.well-known/agent.json` — Agent Card discovery (skills, capabilities)
- `POST /a2a/v1` — JSON-RPC 2.0 (`message/send`, `tasks/get`, `tasks/cancel`)
- `GET /a2a/health` — Health check

**Setup:** Enable in `~/.opencrabs/config.toml`:
```toml
[a2a]
enabled = true
bind = "127.0.0.1"
port = 18790
```

**Bee Colony Debate:** Multi-agent structured debate with knowledge-enriched context from QMD memory search. Configurable rounds, confidence-weighted consensus based on ReConcile (ACL 2024).

## Why Separate?

Skills are shared. Your setup is yours. Keeping them apart means you can update skills without losing your notes, and share skills without leaking your infrastructure.

---

Add whatever helps you do your job. This is your cheat sheet.