# Groups and local commands

> Organize related functions into groups with shared secrets, and add local-only CLI commands.

Source: https://callables.app/docs/groups

A project can hold many functions. Put related ones in a `Group`. People see a group as one section of the project in the web app, and you run its functions as `call <group> <action>`.

```python
from typing import Literal

from callables import Group

backups = Group(
    "backups",
    title="Database backups",
    description="Postgres dumps to object storage",
    secrets=["SPACES_KEY", "SPACES_SECRET"],
)


@backups.deploy(title="Back up now", secrets=["DATABASE_URL"])
def run(env: Literal["prod", "staging"] = "prod", keep: int = 7) -> str:
    ...


@backups.deploy(title="List backups")
def list_all(days: int = 30) -> list[dict]:
    ...
```

```bash
call backups --help
call backups run --help
call backups run --env staging --keep 3
```

## Rules

- A group's `secrets` go to every action in it. Each action can declare more of its own.
- A group name is lowercase letters, digits, `-` or `_`, and can't be a built-in command such as `check` or `local`.
- Define each group once and import it wherever you need it.
- An action's full name is `<group>.<name>`, such as `backups.run`. Use that with `call local`.
- `@group.deploy` takes the same options as [`@deploy`](https://callables.app/docs/deploy).
- Moving an existing function into a group gives it a new name, so it starts with fresh run history and sharing settings.
- Plain `@deploy` functions still work. They appear in the project without a section.

## Local commands

Some tools only make sense on your own machine: they read local files, write reports to disk or open a browser. Write them with `@command` (or `@group.command`) instead of `@deploy`, so the project has one CLI:

```python
from callables import Group

audits = Group("audits", title="Audits")


@audits.command
def render(slug: str, open_browser: bool = False) -> str:
    ...
```

- They run as `call audits render --slug x`, and with `call local`. `call check` lists them as "(local only)".
- They're never deployed. Nobody can run them from the web app.
- `@command` takes `name`, `title`, `description` and `next`, but no `secrets`, `schedule` or `timeout`. Read secrets from your own `.env`.
- Their module is still imported when the platform builds, so keep imports that only work locally inside the function.
- Return the result instead of printing it. The CLI prints what you return: aligned columns in a terminal, JSON when piped.
- To deploy one later, change `@command` to `@deploy`. It keeps its name.
