# CLAUDE.md — Cortex / Inara Project This file is loaded automatically by Claude Code when working in this directory. Read it before touching any files. --- ## Identity & Context - **Project:** Cortex (dispatcher) + Inara (resident agent) - **Owner:** Scott Idem (One Sky IT / Danger Zone) - **Machine context:** See `~/CLAUDE.md` for fleet identity (`scott_lpt` = General Manager) - **Named after:** The 'verse-wide communications network (Firefly) --- ## Directory Map ``` Cortex_and_Inara_dev/ cortex/ ← FastAPI service (the dispatcher) main.py ← App entry point, router registration config.py ← All settings (pydantic-settings, reads .env) llm_client.py ← Claude CLI + Gemini CLI subprocess backends orchestrator_engine.py ← Gemini API ReAct tool loop → Claude handoff context_loader.py ← Loads Inara's system prompt from inara/ files session_store.py ← In-memory + file session persistence session_logger.py ← Writes session turns to inara/sessions/ memory_distiller.py ← Short/mid/long distill jobs (APScheduler) scheduler.py ← APScheduler setup event_bus.py ← Internal SSE pub/sub (NC Talk → browser) routers/ chat.py ← POST /chat (streaming SSE) orchestrator.py ← POST /orchestrate, GET /orchestrate/{job_id} auth.py ← GET /auth/status (Claude + Gemini CLI token checks) distill.py ← POST /distill/*, GET /distill/status files.py ← GET /files (inara/ file browser) nextcloud_talk.py ← POST /webhook/nextcloud (NC Talk bot) google_chat.py ← POST /webhook/google (Google Chat — stub) tools/ __init__.py ← Tool registry (Gemini FunctionDeclarations + dispatcher) web.py ← DuckDuckGo web_search tool system.py ← Local machine tools (claude_allow_dir) static/ ← Single-page web UI (index.html, style.css, app.js) data/sessions/ ← Persisted session JSON files inara/ ← Inara identity, memory, context files IDENTITY.md ← Who Inara is SOUL.md ← Values, personality, voice PROTOCOLS.md ← Behavioral rules CONTEXT_TIERS.md ← What each tier (1–3) includes in the system prompt USER.md ← Scott's profile (loaded into context) HELP.md ← In-app help content (rendered in UI) MEMORY.md ← Persistent facts (written by distiller or manually) MEMORY_SHORT.md ← Rolling short-term memory (auto-distilled daily) MEMORY_MID.md ← Mid-term memory (auto-distilled weekly) MEMORY_LONG.md ← Long-term memory (auto-distilled monthly) sessions/ ← Session turn logs (YYYY-MM-DD_.md) docs/ ← Integration reference docs NEXTCLOUD_TALK_BOT.md documentation/ ← Architecture decisions and agent task list TODO__Agents.md ← READ THIS FIRST — active task list ARCH__Intelligence_Layer.md ← Orchestrator, dev agent, knowledge architecture docker-compose.yml ← Docker deployment .env.default ← Reference config (copy to .env, fill in secrets) README.md ← Project orientation ``` --- ## Run Commands ```bash # Start (Docker) docker compose up -d # Restart service (after any Python change) sudo systemctl restart cortex # Syntax check a file before restarting python3 -m py_compile cortex/.py # Syntax check all routers for f in cortex/routers/*.py cortex/tools/*.py cortex/orchestrator_engine.py; do python3 -m py_compile "$f" && echo "OK: $f" done # Install/update dependencies cd cortex && .venv/bin/pip install -r requirements.txt # Logs journalctl -u cortex -f # Web UI (local) http://localhost:8000 # Swagger docs http://localhost:8000/docs ``` --- ## Key Design Decisions ### Two-Brain Architecture (Orchestrator / Responder) - **Gemini API** (`orchestrator_engine.py`) — runs the ReAct tool loop; handles tool calling, planning, research - **Claude CLI** (`llm_client.py`) — produces all user-facing responses; receives enriched context from Gemini - **Direct chat** bypasses the orchestrator entirely — `POST /chat` goes straight to Claude (faster) - **Orchestrated tasks** go to `POST /orchestrate` — returns a job_id, result is polled ### LLM Backends - `llm_client.py` manages Claude CLI (`claude --print`) and Gemini CLI (`gemini -p`) subprocesses - `orchestrator_engine.py` uses the Gemini **API** (google-genai SDK) — completely separate from the Gemini CLI - Claude OAuth token is read live from `~/.claude/.credentials.json` (never rely on stale env var) ### Tool Strategy - Orchestrator tools live in `cortex/tools/` — separate from the `ae_*` MCP tools - **Do not modify** the `ae_*` MCP server to support orchestrator needs; add new tools to `cortex/tools/` instead - Tools are registered in `cortex/tools/__init__.py` as both Gemini FunctionDeclarations and Python callables ### Context / Memory - `context_loader.py` assembles Inara's system prompt from `inara/` files based on tier (1–3) - Tier 1 = minimal (identity only); Tier 2 = standard (+ memory + user profile); Tier 3 = full - Memory files are written by the distiller or manually — do not delete them ### Security / Safety - **Never `rm`** — move files to `~/tmp/gemini_trash` - **Never commit secrets** — `.env` is gitignored; use `.env.default` as the reference - `NEXTCLOUD_TALK_BOT_SECRET` and `GEMINI_API_KEY` live in `.env` only - Cortex should only be accessible via WireGuard — never internet-exposed without VPN --- ## Adding a New Tool 1. Implement the tool function in `cortex/tools/.py` - Must be `async def`; use `asyncio.to_thread` for blocking calls - Return a plain string result 2. Add a `FunctionDeclaration` and register it in `cortex/tools/__init__.py` 3. Syntax check: `python3 -m py_compile cortex/tools/.py` 4. Restart Cortex ## Managing Claude Code Directory Permissions Claude Code prompts (or silently hangs) when it needs to read or write a directory outside its current working directory. The `claude-allow-dir` script patches `~/.claude/settings.json` to add auto-allow rules so Claude no longer blocks on those paths. ### Script: `~/.local/bin/claude-allow-dir` ```bash # Allow read + write (default) claude-allow-dir ~/OSIT_dev/aether_api_fastapi # Read-only claude-allow-dir ~/agents_sync r # Write-only claude-allow-dir /tmp w ``` Adds `Read(path/*)` and/or `Edit(path/*)` + `Write(path/*)` entries to the `permissions.allow` array in `~/.claude/settings.json`. Idempotent — safe to run twice on the same path. Changes take effect in the next Claude Code session (or after opening `/hooks` in the UI). ### Orchestrator tool: `claude_allow_dir` Cortex exposes this as a Gemini tool (`cortex/tools/system.py`) so the orchestrator can add allow rules on Inara's behalf without human intervention. **Security note:** This tool modifies Claude Code's own permission settings. The Gemini orchestrator calling it can grant Claude access to any directory on the machine. Keep this in mind when evaluating orchestrator behavior — it should only be invoked when Scott has clearly asked for a directory to be unblocked. ## Adding a New Router 1. Create `cortex/routers/.py` with `router = APIRouter()` 2. Import and register in `cortex/main.py` 3. Syntax check, restart --- ## Active Tasks See `documentation/TODO__Agents.md` for the current task list. High priority items as of 2026-03-18: - Ollama backend (third LLM option — local, no API cost) - NC Talk integration stabilization - Knowledge consolidation (markdown → AE Journals) --- ## Related Docs | File | Purpose | |---|---| | `documentation/TODO__Agents.md` | Active task list — read before starting work | | `documentation/ARCH__Intelligence_Layer.md` | Full architecture design | | `~/agents_sync/projects/CORTEX.md` | High-level project vision and phases | | `~/agents_sync/CLAUDE.md` | Fleet coordination rules | | `~/CLAUDE.md` | Machine identity (`scott_lpt`) |