> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ollim.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Development guide

> Set up a development environment, understand the project structure, and follow code conventions.

ollim-bot is a standard Python project managed with
[uv](https://docs.astral.sh/uv/). This guide covers setting up a development
environment, running the bot locally, and understanding the project structure
and conventions.

## Prerequisites

* [uv](https://docs.astral.sh/uv/) — `curl -LsSf https://astral.sh/uv/install.sh | sh` (automatically installs the required Python version)
* A Discord bot token (see the [quickstart](/getting-started/quickstart#create-a-discord-bot))
* Google OAuth credentials (see [Google integration setup](/getting-started/google-integration)) if using Google integrations

## Setup

<Steps>
  <Step title="Clone the repository">
    ```bash theme={null}
    git clone https://github.com/Ollim-AI/ollim-bot.git
    cd ollim-bot
    ```
  </Step>

  <Step title="Install dependencies">
    ```bash theme={null}
    uv tool install --editable .
    uv sync
    uv run pre-commit install
    ```

    The first command installs `ollim-bot` and `claude-history` as global
    commands. `claude-history` is a CLI tool required by
    [subagents](/extending/subagents). `uv sync` installs dev dependencies
    (pytest, ruff, ty). Pre-commit hooks enforce linting and type checking
    on every commit.
  </Step>

  <Step title="Configure environment variables">
    Copy the example file and fill in your values:

    ```bash theme={null}
    cp .env.example .env
    ```

    | Variable          | Required    | Description                                                                                  |
    | ----------------- | ----------- | -------------------------------------------------------------------------------------------- |
    | `DISCORD_TOKEN`   | Yes         | Discord bot token                                                                            |
    | `OLLIM_USER_NAME` | Yes         | Your display name (used in the system prompt)                                                |
    | `OLLIM_BOT_NAME`  | Yes         | The bot's display name                                                                       |
    | `OLLIM_TIMEZONE`  | No          | IANA timezone (e.g. `America/New_York`); defaults to auto-detected system timezone, then UTC |
    | `WEBHOOK_PORT`    | No          | Port for the webhook HTTP server (e.g. `8420`)                                               |
    | `WEBHOOK_SECRET`  | Conditional | Bearer token for webhook auth (required if `WEBHOOK_PORT` is set)                            |
  </Step>

  <Step title="Run the bot">
    ```bash theme={null}
    ollim-bot
    ```

    On startup, the bot checks Claude authentication — if not logged in,
    it DMs you an OAuth link via the Discord REST API. See the
    [quickstart](/getting-started/quickstart) for the full startup flow.
    The bot also checks for an existing instance via a PID file at
    `~/.ollim-bot/state/bot.pid` and exits if one is already running.
  </Step>
</Steps>

## Running tests

```bash theme={null}
uv run pytest
```

Dev dependencies include `pytest`, `pytest-asyncio`, and `pytest-cov`.
See [Testing](/development/testing) for the test philosophy and conventions.

## Linting and type checking

ollim-bot uses [ruff](https://docs.astral.sh/ruff/) for linting and formatting,
and [ty](https://docs.astral.sh/ty/) for type checking. Pre-commit hooks run both
automatically on every commit.

```bash theme={null}
# Lint
uv run ruff check

# Format
uv run ruff format

# Type check
uv run ty check

# Install pre-commit hooks (one-time setup)
uv run pre-commit install
```

The pre-commit config runs `ruff --fix` (auto-fixes safe lint issues), `ruff format`,
and `ty check` on staged Python files. CI runs the same checks — see below.

## CI

GitHub Actions runs on every push and pull request to `main`:

* **Lint job** — `ruff check`, `ruff format --check`, `ty check`
* **Test job** — `pytest` across Python 3.11, 3.12, and 3.13

## Project structure

The source lives under `src/ollim_bot/`. Each file owns a single domain —
there are no `utils`, `helpers`, or `common` modules.

```text theme={null}
src/ollim_bot/
├── main.py              # CLI entry point, command router, SDK layout setup
├── bot.py               # Discord interface (DMs, slash commands)
├── channel.py           # DM channel reference, set once at startup
├── agent.py             # Agent SDK wrapper (sessions, MCP servers, slash routing)
├── agent_context.py     # Message context: timestamps, pending updates, ThinkingConfig
├── agent_streaming.py   # Stream consumer: SDK message loop, auto-compaction retry
├── prompts.py           # System prompt for main agent and fork prompt helpers
├── subagents.py         # Bundled agent installation and tool-set extraction
├── subagents/           # Subagent specs (markdown files with YAML frontmatter)
├── skills.py            # Skill data model and directory-based persistence
├── agent_tools.py       # MCP tools (discord_embed, ping_user, enter_fork, etc.)
├── auth.py              # Claude Code auth via bundled CLI (headless login flow)
├── config.py            # Env vars loaded from .env via dotenv
├── runtime_config.py    # Persistent runtime config (model, thinking, timeouts)
├── fork_state.py        # Fork state: contextvars, dataclasses, idle timeouts
├── forks.py             # Pending updates I/O and background fork execution
├── sessions.py          # Session ID persistence and session history JSONL log
├── permissions.py       # Discord-based tool approval (canUseTool callback)
├── tool_policy.py       # Tool-pattern validation and per-job tool restrictions
├── streamer.py          # Streams agent responses to Discord (throttled edits)
├── storage.py           # JSONL/markdown I/O and git auto-commit
├── embeds.py            # Embed/button types, builders, and build_embed/build_view
├── views.py             # Persistent button handlers via DynamicItem
├── inquiries.py         # Button inquiry prompt persistence (7-day TTL)
├── ping_budget.py       # Refill-on-read ping budget for background fork notifications
├── formatting.py        # Tool-label formatting helpers
├── webhook.py           # Webhook HTTP server for external triggers
├── google/
│   ├── auth.py          # Shared Google OAuth2 (Tasks + Calendar + Gmail)
│   ├── tasks.py         # Google Tasks CLI + API helpers
│   ├── calendar.py      # Google Calendar CLI + API helpers
│   └── gmail.py         # Gmail CLI (read-only)
└── scheduling/
    ├── scheduler.py     # APScheduler-based proactive scheduling
    ├── routines.py      # Routine dataclass and markdown I/O
    ├── reminders.py     # Reminder dataclass and markdown I/O
    ├── preamble.py      # Bg preamble and forward schedule builder
    ├── routine_cmd.py   # Routines CLI (ollim-bot routine)
    └── reminder_cmd.py  # Reminders CLI (ollim-bot reminder)
```

## CLI subcommands

The `ollim-bot` command doubles as both the bot runner and a CLI for managing
routines, reminders, and Google integrations. Running `ollim-bot` with no
arguments starts the Discord bot. Subcommands route to their respective modules:

| Command                                                    | Description                       |
| ---------------------------------------------------------- | --------------------------------- |
| `ollim-bot`                                                | Start the Discord bot             |
| `ollim-bot routine add\|list\|cancel`                      | Manage recurring routines         |
| `ollim-bot reminder add\|list\|cancel`                     | Manage one-shot reminders         |
| `ollim-bot tasks list\|add\|done\|update\|delete`          | Google Tasks operations           |
| `ollim-bot cal today\|upcoming\|show\|add\|update\|delete` | Google Calendar operations        |
| `ollim-bot gmail unread\|read\|search\|labels`             | Gmail (read-only)                 |
| `ollim-bot auth login\|status\|logout`                     | Manage Claude Code authentication |
| `ollim-bot help`                                           | Show help text                    |

See the [CLI reference](/development/cli-reference) for full flag documentation.

## Code conventions

These rules come from the project's `CLAUDE.md` and apply to all contributions.

### Hard invariants

<Warning>
  Violating these causes runtime bugs.
</Warning>

**Channel-sync invariant** — every path into `stream_chat` must call both
`agent_tools.set_channel` and `permissions.set_channel`. This applies to
`_dispatch`, `_check_fork_transitions`, `slash_fork`, `send_agent_dm`,
button handlers in `views.py`, and `check_fork_timeout` in `scheduler.py`.

### Design rules

| Rule                                | Rationale                                                                      |
| ----------------------------------- | ------------------------------------------------------------------------------ |
| No `utils`/`helpers`/`common` files | Every function belongs in a domain module                                      |
| No catch-all directories            | Name for what it does (`google/`, `scheduling/`), not what it is               |
| Max \~400 lines per file            | Split by responsibility when approaching the limit                             |
| No duplicate logic across modules   | If 3 modules share a pattern, extract it                                       |
| One logging system                  | `logging.getLogger(__name__)` for library code, `print()` only in CLI commands |

### Dependencies

Key runtime dependencies (from `pyproject.toml`):

| Package                    | Purpose                                                                               |
| -------------------------- | ------------------------------------------------------------------------------------- |
| `discord.py`               | Discord bot framework                                                                 |
| `claude-agent-sdk`         | Claude Agent SDK for persistent AI sessions                                           |
| `google-api-python-client` | Google API access (Tasks, Calendar, Gmail)                                            |
| `google-auth-oauthlib`     | Google OAuth2 flow                                                                    |
| `apscheduler`              | Cron-based scheduling for routines and reminders                                      |
| `python-dotenv`            | `.env` file loading                                                                   |
| `pyyaml`                   | YAML frontmatter parsing                                                              |
| `aiohttp`                  | Webhook HTTP server                                                                   |
| `jsonschema`               | Webhook payload validation                                                            |
| `claude-history`           | Session transcript search — bundled as a dependency, exposes the `claude-history` CLI |

Key dev dependencies:

| Package          | Purpose                                         |
| ---------------- | ----------------------------------------------- |
| `ruff`           | Linting and formatting                          |
| `ty`             | Type checking                                   |
| `pre-commit`     | Git hooks for automated lint/format/type checks |
| `pytest`         | Test framework                                  |
| `pytest-asyncio` | Async test support                              |
| `pytest-cov`     | Coverage reporting                              |

## Data directory

All persistent state lives in `~/.ollim-bot/`. If this directory is a git
repository, `storage.py` auto-commits on writes to routines, reminders, and
other managed files. See
[Data directory](/configuration/data-directory) for the full layout.

## Next steps

<Columns cols={2}>
  <Card title="Testing" icon="vial" href="/development/testing">
    Test philosophy, running tests, and the no-mocks policy.
  </Card>

  <Card title="CLI reference" icon="terminal" href="/development/cli-reference">
    Full documentation of all CLI subcommands and flags.
  </Card>

  <Card title="Architecture overview" icon="sitemap" href="/architecture/overview">
    Module map, dependency diagram, and data flow.
  </Card>

  <Card title="Adding integrations" icon="puzzle-piece" href="/development/adding-integrations">
    How to add new Google services, MCP tools, and webhook specs.
  </Card>
</Columns>
