4.7 KiB
EJClaw
Dual-agent AI assistant (Claude Code + Codex) over Discord. Originally derived from qwibitai/nanoclaw.
Quick Context
Single unified service (ejclaw) manages three Discord bots (owner, reviewer, arbiter) in one process. Agent types behind each role remain configurable via OWNER_AGENT_TYPE, REVIEWER_AGENT_TYPE, and ARBITER_AGENT_TYPE in .env. Claude Code uses the Agent SDK; Codex uses the Codex SDK (codex exec). Auth via CLAUDE_CODE_OAUTH_TOKEN in .env (1-year token from claude setup-token).
Key Files
| File | Purpose |
|---|---|
src/index.ts |
Orchestrator: state, message loop, agent invocation |
src/agent-runner.ts |
Spawns agent processes, manages env/sessions/skills |
src/channels/discord.ts |
Discord channel (8s typing refresh, Groq/OpenAI Whisper transcription) |
src/ipc.ts |
IPC watcher and task processing |
src/router.ts |
Message formatting and outbound routing |
src/config.ts |
Trigger pattern, paths, intervals |
src/task-scheduler.ts |
Runs scheduled tasks |
src/db.ts |
SQLite operations |
runners/agent-runner/ |
Claude Code runner (Agent SDK) |
runners/codex-runner/ |
Codex runner (SDK, codex exec wrapper) |
groups/{name}/CLAUDE.md |
Per-group memory (isolated) |
Skills
| Skill | When to Use |
|---|---|
/setup |
First-time installation, authentication, service configuration |
/customize |
Adding channels, integrations, changing behavior |
/debug |
Agent issues, logs, troubleshooting |
Development
Run commands directly—don't tell the user to run them.
bun run build # Build main project
bun run build:runners # Install + build both runners
bun run build:runtime # Build host runtime only
bun run build:container # Optional: rebuild reviewer Docker image
bun run dev # Dev mode with hot reload
Service management (Linux):
systemctl --user restart ejclaw # Restart unified service
systemctl --user status ejclaw # Check status
journalctl --user -u ejclaw -f # Follow logs
Deploy:
bun run deploy
deploy rebuilds only the host runtime. Rebuild the reviewer Docker image
separately with bun run build:container only when you need the container path.
Service Stack Architecture
Single unified service manages all three Discord bots in one process:
ejclaw.service— Single unified process- Discord bots:
DISCORD_OWNER_BOT_TOKEN(owner),DISCORD_REVIEWER_BOT_TOKEN(reviewer),DISCORD_ARBITER_BOT_TOKEN(arbiter) - Old service-based token names are no longer accepted; migrate them to the canonical role-based keys above before starting the service
- Paired review: owner (
OWNER_AGENT_TYPE, default: codex) ↔ reviewer (REVIEWER_AGENT_TYPE, default: claude-code) - Provider fallback is internal routing only — visible Discord bots stay owner/reviewer/arbiter fixed
- Shared dirs:
store/,groups/,data/ - SQLite WAL mode +
busy_timeout=5000for concurrent access
Debugging Paths
Unified DB + directories (both services share store/, groups/, data/):
| 항목 | 경로 |
|---|---|
| DB | store/messages.db (공유, WAL 모드) |
| 서비스 로그 | journalctl --user -u ejclaw -f 또는 logs/ejclaw.log |
| 그룹별 로그 | groups/{name}/logs/ (공유 채널은 양쪽 봇 로그가 같은 폴더) |
| Claude 세션 | data/sessions/{name}/.claude/ |
| Codex 세션 | data/sessions/{name}/.codex/ |
| Claude 플랫폼 규칙 | prompts/claude-platform.md |
| Codex 플랫폼 규칙 | prompts/codex-platform.md |
| Claude 글로벌 메모리 | groups/global/CLAUDE.md |
Codex SDK
Codex runner uses @openai/codex-sdk (wraps codex exec):
codex.startThread()/codex.resumeThread()for session persistencethread.run(input)for single-shot turn execution (completes all work before returning)approvalPolicy: "never"+sandboxMode: "danger-full-access"for bypass- Per-group: model (
CODEX_MODEL), effort (CODEX_EFFORT), MCP servers viaconfig.toml CODEX_HOMEset to per-group session dir, readsAGENTS.mdfrom there + CWD
Voice Transcription
Audio attachments in Discord are transcribed via Groq Whisper (primary) or OpenAI Whisper (fallback):
GROQ_API_KEY— Groqwhisper-large-v3-turbo, ~200x real-time, free tier (console.groq.com)OPENAI_API_KEY— OpenAIwhisper-1, fallback if Groq key not set- Shared file cache (
cache/transcriptions/) deduplicates across both services .pendingfile coordination prevents duplicate API calls