CLAUDE.md
Rama main · réplica de lectura del repositorio original.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
# Install (dev)
uv pip install -e ".[dev]"
# Run all tests
pytest
# Run a single test file
pytest tests/unit/test_brain.py
# Run a single test by name
pytest tests/unit/test_brain.py::TestBrainInit::test_local_router_created
# Run the app
python -m openacm
# or
uv run openacm
```
Tests use `asyncio_mode = "auto"` — all async test functions run without `@pytest.mark.asyncio`.
## Architecture
### Startup sequence (`app.py`)
`OpenACM.run()` boots in order: config → database → security/LLM/memory → tools → channels → watchers/schedulers → web server → console loop. Each subsystem is wired together by `app.py`; it is the only place where cross-cutting dependencies are assembled.
### The agentic loop (`core/brain.py`)
`Brain.process_message()` is the main entry point for every user message:
1. **LocalRouter** classifies intent in ~5ms — if confidence is above threshold it runs a fast-path handler and skips the LLM entirely.
2. Otherwise the **agentic loop** runs: build system prompt → inject RAG context + skills → call LLM → execute tool calls → loop until no more tool calls or `max_tool_iterations` is hit.
3. Each iteration is fully traced (`_traces`) and accessible via `/api/traces`.
### Tool system (`tools/`)
Tools are registered with the `@tool` decorator. The decorator adds a `ToolDefinition` to a global list in `tools/base.py`. At startup `ToolRegistry` imports each tool module (triggering the decorator) then builds semantic embeddings over all tool descriptions for cosine-similarity selection.
Tool functions always receive `_brain=None` as a kwarg. Use `_brain.tool_registry` to access shared managers (`cron_scheduler`, `swarm_manager`, `mcp_manager`, `app_config`).
### Events (`core/events.py`)
`EventBus` is a simple async pub/sub. Emit events for anything the dashboard needs to see in real-time (tool calls, LLM responses, thinking status). The WebSocket handler in `web/server.py` subscribes to all events and forwards them to connected clients.
### Memory (`core/memory.py`)
`MemoryManager` owns conversation history in an in-memory cache backed by SQLite. It needs `llm_router` and `event_bus` to run auto-compaction — pass them in the constructor, not after the fact.
### Strings (`core/messages.py`)
All user-facing strings and LLM prompts live here as constants. Never hardcode them in engine files.
### Intent keywords (`tools/intent_keywords.py`)
The keyword fallback dict for tool selection lives here, separate from `ToolRegistry` logic. Add or edit keywords only in this file.
## Key conventions
- All tool functions are `async` and end with `**kwargs` to absorb unknown params.
- `_brain` is always the last positional-style kwarg; tools pull their dependencies from `_brain.tool_registry`.
- `ToolRegistry` is the dependency container for tools — not module-level globals.
- The `conftest.py` fixtures wire everything with mocks; use them instead of instantiating classes directly in tests.
- `pytest-asyncio` is configured in `auto` mode — no need to mark individual async tests.