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

# Bakebook

> A Bakebook is the class that holds your tasks. Subclass it to share tasks, extend it with Pydantic settings, and decorate methods with @command.

A **Bakebook** is a class in `bakefile.py` that holds your tasks:

* Subclass it to share tasks across projects through normal inheritance.
* It extends [Pydantic](https://pydantic.dev/docs/validation/)'s `BaseSettings`, so configuration is typed class attributes with validation, env-var and `.env` loading, defaults, and type coercion.
* Tasks use the `@command()` decorator, same syntax as Typer.
* `ctx.run()` executes CLI commands through Python's subprocess.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from bake import Bakebook, command, Context, console
from pydantic import Field
from typing import Annotated
import typer


class MyBakebook(Bakebook):
    # Pydantic configuration
    api_url: str = Field(default="https://api.example.com", env="API_URL")

    @command()
    def fetch(self) -> None:
        # Run CLI commands via self.ctx
        self.ctx.run(f"curl {self.api_url}")


bakebook = MyBakebook()


# Standalone functions also work
@bakebook.command()
def test(
    verbose: Annotated[bool, typer.Option(False, "--verbose", "-v")] = False,
):
    if verbose:
        console.echo("Running tests...")
    bakebook.ctx.run("pytest")
```

## Defining tasks with @command

There are two patterns, depending on whether you define the task before or after instantiating the bakebook:

* **Pattern 1: Before instantiating** - Use `@command()` on class methods, then access context through `self.ctx`.
* **Pattern 2: After instantiating** - Use `@bakebook.command()` on standalone functions, then access context through `bakebook.ctx`.

`@command()` accepts all Typer options: `name`, `help`, `deprecated`, and so on.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from bake import Bakebook, command, console
from typing import Annotated
import typer


# Pattern 1: On class (use self.ctx for context access)
class MyBakebook(Bakebook):
    @command()
    def task1(self) -> None:
        console.echo("Task 1")
        self.ctx.run("echo 'Task 1 complete'")


bakebook = MyBakebook()


# Pattern 2: On instance (use bakebook.ctx for context access)
@bakebook.command(name="deploy", help="Deploy application")
def deploy(
    env: Annotated[str, typer.Option("dev", help="Environment to deploy")],
):
    console.echo(f"Deploying to {env}...")
    bakebook.ctx.run(f"kubectl apply -f {env}.yaml")
```

See [Commands](/concepts/commands) for typed arguments and decorator options, and [Context](/concepts/context) for the full `ctx.run` reference.

## Configuration with Pydantic settings

Because Bakebook extends `BaseSettings`, every class attribute is typed, validated configuration. Values load from environment variables, `.env` files, or defaults:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from bake import Bakebook
from pydantic import Field


class MyBakebook(Bakebook):
    # Defaults
    database_url: str = "sqlite:///db.sqlite3"

    # With environment variable mapping
    api_key: str = Field(default="default-key", env="API_KEY")

    # With validation
    port: int = Field(default=8000, ge=1, le=65535)
```

Tasks read settings as plain attributes (`self.api_key`), so configuration and the tasks that use it live in the same class. Inherited bakebooks inherit configuration too, and subclasses can override individual fields. See [Settings](/usage/settings) for validation, secrets, and getting values out.

## Reuse through inheritance

The point of Bakebook being a class: task libraries are just classes you inherit from.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from bake import Bakebook, command


class ProjectTasks(Bakebook):
    @command()
    def build(self) -> None:
        self.ctx.run("cargo build --release")


# In another project: inherit every task, override what differs
class AppTasks(ProjectTasks):
    version: str = "2.0.0"

    @command()
    def release(self) -> None:
        self.ctx.run(f"gh release create v{self.version}")
```

Override a task by redefining its method. Add tasks by defining new ones. Compose task sets through multiple inheritance. No include files, no templating, no copy-paste.

## Instantiating

Create a bakebook by inheriting from `Bakebook` or instantiating it directly:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from bake import Bakebook

bakebook = Bakebook()
```

You can also generate a starter with `bakefile init` or `bakefile add-inline`.
