Files
echo-core/CLAUDE.md
MoltBot Service 21d55cbc6a add eco CLI symlink to setup wizard, document all CLI commands
setup.sh now installs eco → ~/.local/bin/eco (symlink to cli.py).
README.md updated with full eco command reference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 06:48:22 +00:00

3.6 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

# Run all tests
source .venv/bin/activate && pytest tests/

# Run a single test file
pytest tests/test_router.py

# Run a specific test
pytest tests/test_router.py::test_clear_command -v

# Start the bot (via systemd)
systemctl --user start echo-core

# Start manually (foreground)
source .venv/bin/activate && python3 src/main.py

# WhatsApp bridge
systemctl --user start echo-whatsapp-bridge

# CLI diagnostics (eco = symlink to cli.py, installed by setup.sh)
eco status
eco doctor

# Install dependencies
source .venv/bin/activate && pip install -r requirements.txt

Architecture

Message flow: Adapter → router.pyclaude_session.py → Claude CLI subprocess → response split → Adapter sends reply.

Three adapters run concurrently in one asyncio event loop via asyncio.gather() in src/main.py:

  • Discord (src/adapters/discord_bot.py): discord.py slash commands, 2000 char split
  • Telegram (src/adapters/telegram_bot.py): python-telegram-bot commands + inline keyboards, 4096 char split
  • WhatsApp (src/adapters/whatsapp.py): polls Node.js Baileys bridge (bridge/whatsapp/index.js) at http://127.0.0.1:8098 every 2s, 4096 char split

All adapters follow the same pattern: module-level _config, authorization helpers (is_owner, is_admin, is_registered_channel), split_message(), and routing through route_message(channel_id, user_id, text, model).

Claude sessions (src/claude_session.py): Each channel has one persistent session. send_message() auto-starts or resumes via claude --resume <session_id>. System prompt is built by concatenating personality/*.md files. External messages are wrapped in [EXTERNAL CONTENT] injection markers. Sensitive env vars are stripped before subprocess execution.

Session state lives in sessions/active.json — maps channel IDs to {session_id, model, message_count, ...}.

Credentials (src/credential_store.py): All tokens stored in system keyring under service "echo-core". Required: discord_token. Optional: telegram_token. Never pass secrets as CLI arguments — use stdin.

Config (src/config.py): Loads config.json with dot-notation access (config.get("bot.name")). Channel namespaces: channels (Discord), telegram_channels, whatsapp_channels.

Scheduler (src/scheduler.py): APScheduler loading jobs from cron/jobs.json, runs Claude prompts in isolated sessions.

Heartbeat (src/heartbeat.py): Periodic checks (email, calendar, KB, git), quiet hours 23-08.

Memory search (src/memory_search.py): Ollama all-minilm embeddings (384 dim) + SQLite cosine similarity.

Import Convention

All modules use absolute imports from project root via sys.path.insert(0, PROJECT_ROOT). Import as from src.config import ..., from src.adapters.discord_bot import .... No circular imports — adapters → router → claude_session.

Key Files

Path Role
src/main.py Entry point — starts all adapters + scheduler + heartbeat
src/router.py Dispatches commands vs Claude messages
src/claude_session.py Claude CLI wrapper with --resume
src/credential_store.py Keyring secrets (service: echo-core)
cli.py CLI tool (status, doctor, logs, secrets, cron, whatsapp)
config.json Runtime config (channels, admins, models, bridges)
setup.sh Interactive 10-step onboarding wizard
bridge/whatsapp/index.js Node.js Baileys + Express bridge on port 8098
personality/*.md System prompt files concatenated in order