# TOOLS.md - Local Notes & Tool Syntax --- ## 🔧 `read` — Read File Contents **Syntax:** ```javascript read({ file_path: "/path/to/file"[, offset: N, limit: N] }) ``` **When to use:** Read text files or view images (jpg, png, gif, webp). **Required:** `file_path` — NEVER use `path` **Correct:** ```javascript await read({ file_path: "/path/to/file" }) await read({ file_path: "/path/to/file", offset: 1, limit: 50 }) ``` **Wrong:** ```javascript await read({ path: "/path/to/file" }) // ❌ 'path' is wrong, use 'file_path' await read({}) // ❌ missing file_path entirely ``` **Notes:** - Output truncated at 2000 lines or 50KB - Use `offset` + `limit` for files >100 lines - Images sent as attachments automatically --- ## 🔧 `edit` — Edit File Contents **Syntax:** ```javascript edit({ file_path: "/path", old_string: "exact text", new_string: "replacement" }) ``` **When to use:** Precise text replacement. Old text must match exactly (including whitespace). **Required:** BOTH `old_string` AND `new_string` (not `oldText`/`newText`) **Correct:** ```javascript await edit({ file_path: "/path/to/file", old_string: "text to replace", new_string: "replacement text" }) ``` **Wrong:** ```javascript await edit({ file_path: "/path/file", old_string: "text" }) // ❌ missing new_string await edit({ file_path: "/path/file", new_string: "text" }) // ❌ missing old_string await edit({ file_path: "/path/file", oldText: "x", newText: "y" }) // ❌ wrong param names ``` **Recovery:** After 2 failed edit attempts → use `write` to rewrite the file completely. --- ## 🔧 `write` — Write File Contents **Syntax:** ```javascript write({ file_path: "/path", content: "complete file content" }) ``` **When to use:** Creating new files or rewriting entire files after failed edits. **Required:** `file_path` AND complete `content` (overwrites everything) **Correct:** ```javascript await write({ file_path: "/path/to/file", content: "complete file content here" }) ``` **Wrong:** ```javascript await write({ content: "text" }) // ❌ missing file_path await write({ path: "/file", content: "text" }) // ❌ use file_path not path ``` **⚠️ Caution:** Overwrites entire file — make sure you have the full content. --- ## 🔧 `exec` — Execute Shell Commands **Syntax:** ```javascript exec({ command: "shell command"[, timeout: 30, workdir: "/path"] }) ``` **When to use:** Run shell commands, background processes, or TTY-required CLIs. **Required:** `command` **Correct:** ```javascript await exec({ command: "ls -la" }) await exec({ command: "python3 script.py", timeout: 60 }) await exec({ command: "./script.sh", workdir: "/path/to/dir" }) ``` **Cron Scripts — CRITICAL:** ```python # Always exit 0 for cron jobs import sys sys.exit(0) ``` **Why:** OpenClaw logs non-zero exits as failures. Use stdout presence for signaling: ```python if significant_update: print(notification) # Output triggers notification # No output = silent success ``` --- ## 🔧 `browser` — Browser Control **Syntax:** ```javascript browser({ action: "navigate|snapshot|click|...", targetUrl: "..." }) ``` **When to use:** Navigate, screenshot, or interact with web pages. **Required:** `action` **Requirements:** - Gateway must be running - Chrome extension must be attached (click extension icon on tab) **Correct:** ```javascript await browser({ action: "navigate", targetUrl: "https://example.com" }) await browser({ action: "snapshot" }) await browser({ action: "click", ref: "button-name" }) ``` --- ## Quick Reference Summary | Tool | Required Parameters | Common Errors | |------|---------------------|---------------| | `read` | `file_path` | Using `path` | | `edit` | `file_path`, `old_string`, `new_string` | Using `newText`/`oldText`, missing one param | | `write` | `file_path`, `content` | Partial content, missing `file_path` | | `exec` | `command` | Non-zero exit codes for cron | | `browser` | `action` | Using without gateway check | **Critical rules:** Use `file_path` not `path`. Use `old_string`/`new_string` not `oldText`/`newText`. **Quality over speed. Verify before executing. Get it right.** --- ## Unified Search — Perplexity Primary, SearXNG Fallback **Primary:** Perplexity API (cloud, AI-curated, paid) **Fallback:** SearXNG (local, raw results, free) ### Usage ```bash # Default: Perplexity primary, SearXNG fallback on error search "your query" # Perplexity only (p = perplexity) search p "your query" search perplexity "your query" # SearXNG only (local = searxng) search local "your query" search searxng "your query" # With citations (Perplexity) search --citations "your query" # Pro model for complex queries search --model sonar-pro "your query" search --model sonar-deep-research "comprehensive research" ``` ### Models | Model | Best For | Search Context | |-------|----------|----------------| | sonar | Quick answers, simple queries | Low/Medium/High | | sonar-pro | Complex queries, coding | Medium/High | | sonar-reasoning | Step-by-step reasoning | Medium/High | | sonar-deep-research | Comprehensive research | High | ### When to Use Each - **Perplexity**: Complex queries, research, current events, anything needing synthesis - **SearXNG**: Privacy-sensitive searches, simple factual lookups, bulk operations, rate limit fallback ### Scripts - **Unified**: `skills/perplexity/scripts/search.py` - **Perplexity-only**: `skills/perplexity/scripts/query.py` --- ## Perplexity API - **Location**: `/root/.openclaw/workspace/skills/perplexity/` - **Key**: `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` - **Endpoint**: `https://api.perplexity.ai/chat/completions` - **Models**: sonar, sonar-pro, sonar-reasoning, sonar-deep-research - **Format**: OpenAI-compatible - **Cost**: ~$0.005 per query (shown in output) - **Features**: AI-synthesized answers, citations, real-time search - **Note**: Sends queries to Perplexity servers (cloud) --- ### Voice/Text Reply Rules - **Voice message received** → Reply with **voice** (using Kimi-XXX.ogg filename) - Transcribe internally for understanding - **DO NOT send transcript text to Telegram** - **DO NOT include any text with voice messages** — voice-only, completely silent text - Reply with voice-only, no text - **Text message received** → Reply with **text** - **Never** send both voice + text for the same reply - **ENFORCED 2026-02-07:** Voice messages must be sent alone without accompanying text ### Voice Settings - **TTS Provider**: Local Kokoro @ `http://10.0.0.228:8880` - **Voice**: `af_bella` (American Female) - **Filename format**: `Kimi-YYYYMMDD-HHMMSS.ogg` - **Mode**: Voice-only (no text transcript when sending voice) ### Web Search - **Primary**: Perplexity API (unified search, AI-curated) - **Fallback**: SearXNG (local instance at `http://10.0.0.8:8888/`) - **Manual fallback**: Use `search local "query"` for privacy-sensitive searches - **Browser tool**: Only when gateway running and extension attached ### Core Values - **Best accuracy** — No compromises on quality - **Best performance** — Optimize for speed where possible - **Privacy first** — Always prioritize privacy in all decisions - **Always research before install** — Search web for details, docs, best practices - **Local docs exception** — If docs are local (OpenClaw, ClawHub), use those first ### Search Preferences - **Search first** — Try SearXNG before asking clarifying questions - **Prioritize sites**: *(to be filled in)* - GitHub / GitLab — For code, repos, technical docs - Stack Overflow — For programming Q&A - Wikipedia — For general knowledge - Arch Wiki — For Linux/system admin topics - Official docs — project.readthedocs.io, docs.project.org - **Avoid/deprioritize**: *(to be filled in)* - SEO spam sites - Outdated forums (pre-2020 unless historical) - **Search language**: English preferred, unless query is non-English - **Time bias**: Prefer recent results for tech topics, timeless for facts ### Search-First Sites (Priority Order) When searching, prefer results from: 1. **docs.openclaw.ai** / **OpenClaw docs** — OpenClaw documentation 2. **clawhub.com** / **ClawHub** — OpenClaw skills registry 3. **docs.*.org** / **readthedocs.io** — Official documentation 4. **github.com** / **gitlab.com** — Source code, issues, READMEs 5. **stackoverflow.com** — Programming solutions 6. **wikipedia.org** — General reference 7. **archlinux.org/wiki** — Linux/system administration 8. **reddit.com/r/* —** Community discussions (for opinions/experiences) 9. **news.ycombinator.com** — Tech news and discussions 10. **medium.com** / **dev.to** — Developer blogs (verify date) ## SSH Hosts - **epyc-debian-SSH (deb)** — `n8n@10.0.0.38` - Auth: SSH key (no password) - Key: `~/.ssh/id_ed25519` - Sudo password: `passw0rd` - Usage: `ssh n8n@10.0.0.38` - Status: OpenClaw removed 2026-02-07 - **epyc-debian2-SSH (deb2)** — `n8n@10.0.0.39` - Auth: SSH key (same as deb) - Key: `~/.ssh/id_ed25519` - Sudo password: `passw0rd` - Usage: `ssh n8n@10.0.0.39` ## Existing Software Stack **⚠️ ALREADY INSTALLED — Do not recommend these:** - **n8n** — Workflow automation - **ollama** — Local LLM runner - **openclaw** — AI agent platform (this system) - **openwebui** — LLM chat interface - **anythingllm** — RAG/chat with documents - **searxng** — Privacy-focused search engine - **flowise** — Low-code LLM workflow builder - **plex** — Media server - **radarr** — Movie management - **sonarr** — TV show management - **sabnzbd** — Usenet downloader - **comfyui** — Stable Diffusion UI **When recommending software, ALWAYS check this list first and omit any matches.** ## Skills ### Local Whisper STT - **Location**: `/root/.openclaw/workspace/skills/local-whisper-stt/` - **Purpose**: Transcribe inbound voice messages - **Model**: `base` (CPU-only) - **Usage**: Auto-transcribes when voice message received - **Correct path**: `scripts/transcribe.py` (not root level) ### Kimi TTS Custom - **Location**: `/root/.openclaw/workspace/skills/kimi-tts-custom/` - **Purpose**: Generate voice with custom filenames and send voice-only replies - **Scripts**: - `scripts/generate_voice.py` — Generate voice file (returns path, does NOT send) - `scripts/voice_reply.py` — Generate + send voice-only reply (USE THIS for voice replies) - **Usage**: `python3 scripts/voice_reply.py "text"` - **⚠️ CRITICAL**: Text reference to voice file does NOT send audio. Must use `voice_reply.py` or proper Telegram API delivery. Generation ≠ Delivery. ### Qdrant Memory - **Location**: `/root/.openclaw/workspace/skills/qdrant-memory/` - **Mode**: MANUAL ONLY — No automatic storage - **Collections**: - `kimi_memories` (personal) — Identity, rules, preferences, lessons - `kimi_kb` (knowledge base) — Web data, documents, reference materials - **Vector size**: 1024 (snowflake-arctic-embed2) - **Distance**: Cosine - **Qdrant URL**: `http://10.0.0.40:6333` **Personal Memory Scripts (kimi_memories):** - `scripts/store_memory.py` — Manual storage with metadata - `scripts/search_memories.py` — Semantic search - `scripts/hybrid_search.py` — Search files + vectors **Knowledge Base Scripts (kimi_kb):** - `scripts/kb_store.py` — Store web/docs to KB - `scripts/kb_search.py` — Search knowledge base **Usage:** ```bash # Personal memories ("q remember", "q recall") python3 store_memory.py "Memory" --importance high --tags "preference" python3 search_memories.py "voice settings" # Knowledge base (manual document/web storage) python3 kb_store.py "Content" --title "X" --domain "Docker" --tags "container" python3 kb_search.py "docker volumes" --domain "Docker" ``` **⚠️ CRITICAL**: Never auto-store. Only when user explicitly requests with "q" prefix. ## Infrastructure ### Container Limits - **No GPUs attached** — All ML workloads run on CPU - **Whisper**: Use `tiny` or `base` models for speed ### Local Services - **Kokoro TTS**: `http://10.0.0.228:8880` (OpenAI-compatible) - **Ollama**: `http://10.0.0.10:11434` - **SearXNG**: `http://10.0.0.8:8888` (web search via curl) - **Qdrant**: `http://10.0.0.40:6333` (vector database for memory + KB) - **Collections**: `kimi_memories` (personal), `kimi_kb` (knowledge base) - **Vector size**: 1024 (snowflake-arctic-embed2) - **Distance**: Cosine similarity - **Redis**: `10.0.0.36:6379` (task queue, available for future use) ## Cron Jobs - **Default:** Always check `openclaw cron list` first when asked about cron jobs - Rob's scheduled tasks live in OpenClaw's cron system, not system crontab - Only check system crontab (`crontab -l`, `/etc/cron.d/`) if specifically asked about system-level jobs --- ## Lessons Learned & Workarounds ### Embedded Session Tool Errors **Issue:** `read tool called without path` errors occur in embedded sessions even when parameter syntax is correct in workspace scripts. **Workarounds:** 1. **Double-check parameters manually** — Don't trust the model to pass them correctly in embedded contexts 2. **Avoid embedded tool calls when possible** — Use workspace scripts instead 3. **Edit fails twice → Use write immediately** — Don't retry edit tool more than once 4. **Verify file exists before read** — Prevents ENOENT errors 5. **No redis-cli in container** — Use Python redis module instead 6. **Browser tool unreliable** — Use curl/SearXNG as primary web access ### Common Parameter Errors to Avoid | Wrong | Right | Notes | |-------|-------|-------| | `path` | `file_path` | Most common error | | `newText`/`oldText` | `new_string`/`new_string` | Edit tool only | | Missing `new_string` | Include both params | Edit requires both | | Using `write` for small edits | Use `edit` first | Edit is safer for small changes | ### Environment-Specific Gotchas - **Qdrant Python module** — Must use scripts with proper sys.path setup - **Playwright browsers** — Not installed, use curl/SearXNG for web scraping - **Browser gateway** — Requires Chrome extension attached; rarely available - **Redis CLI** — Not available; use `python3 -c "import redis..."` instead