diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..bb1b29fe4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,80 @@ +# Agent Contributor Guide for Odysseus + +> A short, agent-focused companion to `CONTRIBUTING.md`. Read both before opening a PR. + +This file is a snapshot of the conventions that bite new agent contributors most often. It will drift as the codebase moves — when in doubt, the source of truth is the file the convention refers to, and `CONTRIBUTING.md`. + +## What Odysseus is + +A self-hosted AI assistant with a web UI, agent loop, MCP integrations, and an optional Cookbook runtime. It is **trusted-admin software**: the user running it owns the box and the data. Anything an agent can do, the admin can do — there is no untrusted-tenant sandbox inside a single Odysseus install. + +Before touching auth, middleware, or tool-security code, read [`THREAT_MODEL.md`](THREAT_MODEL.md) end-to-end. + +## Commands the maintainer actually runs + +There is no `ruff`, `black`, or `eslint` in CI. The real gates are: + +```bash +# Focused pytest (use area markers, not the full suite) +python -m pytest tests/test_.py -q +python tests/run_focus.py # curated subset the maintainer uses + +# Syntax / compile gates the CI actually runs +python -m py_compile app.py routes/*.py src/*.py +python -m compileall -q routes src core services mcp_servers +node --check static/js/.js + +# Docker sanity +docker compose config +docker compose up -d --build +``` + +If your change adds a new test file, mirror the structure of an existing one in `tests/test__.py` (owner-scoping helper, monkeypatch over globals, no real network). + +## Where things live + +| Area | Path | Notes | +|---|---|---| +| FastAPI app entry, route wiring | `app.py` | Slim orchestrator. New routes get wired here. | +| Auth / security primitives | `core/auth.py`, `core/middleware.py`, `src/tool_security.py` | Read `THREAT_MODEL.md` first. | +| Domain logic | `core/`, `services/` | No HTTP concerns. | +| HTTP routes | `routes/`, `routes//` | New subpackages split off when a domain grows large. | +| Agent loop, tools, MCP | `src/`, `src/agent_tools/`, `mcp_servers/` | See "The agent loop" below. | +| Frontend | `static/` (HTML/JS/CSS), `templates/` | No emoji, no new font assets, no new colour tokens. | +| Persistence | `core/database.py`, migrations in `scripts/` | SQLite by default. | + +`app.py` should stay slim. If your change adds 50+ lines of orchestration there, the routing probably belongs in `routes//`. + +## The agent loop + +The runtime entry is `src/agent_loop.py`. Tools are registered in `src/agent_tools/` (split files by responsibility: admin, document, filesystem, session, subprocess, web, etc.) and discovered through `src/tool_index.py`. Provider / endpoint resolution lives in `src/llm_core.py`, `src/endpoint_resolver.py`, and `services/hwfit/`. **Cookbook** (`routes/cookbook_routes.py`, `services/cookbook/`) requires a tmux session; don't pretend it's optional in tests. + +Memory and RAG are split across `services/memory/`, `services/search/`, and `src/rag_*.py`. They have separate vector stores and config; do not assume a write to one is visible in the other. + +## Conventions that are easy to get wrong + +- **Owner-scoping is mandatory.** Every authenticated read/write goes through `src/auth_helpers.py:owner_filter(...)` or a helper that calls it. A query that omits it is a multi-tenant leak. Look at `tests/test__owner_scope.py` for the pattern. +- **Never hardcode paths, ports, or loopback URLs.** `src/constants.py` is the single source of truth. The source tree is read-only in Docker, so absolute paths from your dev box will silently break. +- **`internal-tool` is security-critical.** It grants unconditional admin scope via `require_admin`. New tools that touch it need a maintainer review, not just CI green. +- **No Unicode emoji.** Use inline monochrome SVG or existing CSS variables. Frontend lint rejects emoji. +- **PRs target `dev`.** See `CONTRIBUTING.md` for the branch model. +- **Conventional Commits** for the title (`feat:`, `fix:`, `refactor:`, `docs:`, `chore:`, `test:`, `build:`, `ci:`, `perf:`, `style:`). +- **PR description must include `## How to Test`** (≥30 chars, real commands, not `## Test Plan`). The bot rejects the wrong heading. +- **One issue, one PR.** Bulk agent-generated PRs that don't match a single issue get closed regardless of correctness — see `CONTRIBUTING.md`. + +## When you should NOT open a PR + +- The change is purely cosmetic in code you didn't otherwise need to touch. +- You can express the same change in a sentence of `CONTRIBUTING.md`. +- There's already an open PR for the issue (check `gh pr list --search ""` before coding). +- The fix is a config flag in `.env.example` the user can set themselves. + +## Pointers + +- `CONTRIBUTING.md` — branch model, before-you-start rules, the LLM-agent contribution policy. +- `THREAT_MODEL.md` — read before touching auth, middleware, or tool-security code. +- `SECURITY.md` — how to report vulnerabilities (do not file them as public issues). +- `docs/setup.md` — install paths beyond the Docker quickstart. +- `docs/architecture-runtime-inventory.md` — what runs where at runtime. + +This file is named `CLAUDE.md` for Claude Code compatibility, but the content is agent-agnostic. If your tooling prefers `AGENTS.md`, a symlink or copy works the same way. \ No newline at end of file