# Swival - Essential Project-Wide Conventions

## Tool Contracts
- All tools return strings; tool failures use `error: ` prefix (lowercase, with space). Exceptions are internal only (AgentError, ConfigError, ContextOverflowError).

## edit_file Matching (Critical)
When `old_string` not found exactly, tries line-trimmed (`.strip()`) then Unicode-normalized (smart quotes/dashes → ASCII). Raises `ValueError` for "multiple matches" or "no changes".

## Pagination (All Read Tools)
- `offset` is 1-based (not 0). `tail=N` returns last N lines and ignores offset. Continuation hint: `[X more lines, use offset=Y to continue]`.

## Configuration Inverted Keys
- Negation prefix `no_` flips meaning in Session conversion: `no_read_guard` → `read_guard`, `no_history` → `history`, `no_memory` → `memory`, `no_continue` → `continue_here`, `quiet` → `verbose`.

## Session API Semantics
- `Session.run()` = fresh state each call (independent questions). `Session.ask()` = shared state across calls (REPL mode).

## Tool Aliases (All in _TOOL_ALIASES)
- `bash`, `shell`, `terminal`, `run_shell_command` → `run_command`. `search`, `search_files` → `grep`. `find_files` → `list_files`. `create_file`, `file_write` → `write_file`. `file_read` → `read_file`. `read_files` → `read_multiple_files`. `file_edit` → `edit_file`.

## MCP Namespacing
- Tools from MCP server `x` via tool `y` become `mcp__x__y`. Server names must match `^[a-z][a-z0-9-]*$`; double `__` rejected.

## READ_ONLY_TOOLS (Don't Dirty Snapshot Scope)
- Tools that don't mark snapshot dirty: `read_file`, `read_multiple_files`, `list_files`, `grep`, `fetch_url`, `think`, `todo`, `snapshot`. Write/edit/delete → `dirty_tools` → blocks `snapshot restore` unless `force=true`.

## Turn Grouping
- Assistant message with `tool_calls` + all matching tool results form one atomic turn. User turns never dropped.

## State Files (Hidden in .swival/)
- `HISTORY.md` (500KB max, questions truncated to 200 chars). `todo.md` (reset each run, persists across compaction). `memory/MEMORY.md` (auto-loaded into prompt). `continue.md` (interruption recovery).

## Todo Matching Priority
1. Exact match (case-insensitive via `.casefold()`). 2. Prefix match. 3. Substring match. Multiple candidates → error.

## Configuration Precedence
- CLI > project swival.toml > global ~/.config/swival/config.toml > defaults. Relative paths resolve against config file's parent directory; `~` expands before `is_absolute()` check.

## Path Security
- All file operations use `safe_resolve()` that resolves symlinks, checks containment in `base_dir` + extra roots, and rejects paths escaping allowed roots.

## Exit Codes
- `0` = success, `1` = runtime error (exception), `2` = exhausted (max turns/context), `130` = Ctrl+C. Reviewer mode: `0` = ACCEPT, `1` = RETRY.

## Provider Resolution
- All providers resolve via `resolve_provider()` to tuple: `(model_id, api_base, api_key, context_length, llm_kwargs)`.

## AgentError Hierarchy
- `AgentError` is base exception. `ConfigError` extends `AgentError` for configuration failures.

## Tool Response Formats
- `think`, `todo`, `snapshot` return JSON strings. `edit_file` returns structured diff for display.

## Context Compaction Turn Scoring
- `write_file`/`edit_file`: +5, errors/failed operations: +3, `think` turns: +2, snapshot recaps: +5. User turns pinned (never dropped).
