> For the complete documentation index, see [llms.txt](https://novusedge0.gitbook.io/stoat/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://novusedge0.gitbook.io/stoat/recipes/sharing.md).

# Sharing recipes

A remote recipe is a Git repository with `recipe.toml` at its root. Stoat validates the manifest and scripts, resolves a ref to a full commit, and records that commit in `stoat.lock`. Git must be installed on the host.

## Find and add a recipe

The curated index is `index.toml` at the root of the Stoat repository. Stoat clones it into its data root and refreshes the clone after 24 hours. Search by name or description:

```sh
stoat recipe search docker
stoat recipe search --refresh
```

Add an index entry by name. An optional `@ref` selects a tag or branch:

```sh
stoat recipe add my-tools
stoat recipe add my-tools@v1.2
```

You can add a repository URL when it is not in the index. Stoat previews the manifest in a terminal and asks for confirmation; `-y` skips that prompt:

```sh
stoat recipe add https://github.com/example/stoat-my-tools@main -y
```

`--global` forces the home scope from inside a project. `--force` permits a name collision with a bundled, local, or remote recipe. Use it only when the replacement is intentional.

## Publish a recipe to the index

Put the recipe in its own repository, with `recipe.toml` at the root and the scripts beside it, then open a pull request against `index.toml` in the Stoat repository:

```toml
[recipes.caddy]
source = "https://github.com/NovusEdge/stoat-recipe-caddy"
description = "Caddy web server, serving a local directory over HTTP"
os = ["alpine", "ubuntu", "debian", "fedora", "arch"]
```

Pick a name no bundled recipe uses. `stoat recipe add` refuses a bundled name without `--force`, and the MCP `add_recipe` tool passes no force, so an agent can never install an entry named after a bundled recipe.

Tag the recipe repository so users can pin a ref. An entry changes what a new `recipe add` resolves; it never moves a recipe an existing VM already pinned, because the commit lives in the user's `stoat.lock`.

List installed recipes, their scope, and the short commit pin:

```sh
stoat recipe list
```

The visible roots have these labels:

| Scope     | Location and ownership                                                             |
| --------- | ---------------------------------------------------------------------------------- |
| `project` | `./.stoat/recipes/`, when the current directory contains `stoat.toml`              |
| `global`  | remote recipes pinned by `~/.stoat/stoat.lock`                                     |
| `local`   | user directories under `~/.stoat/recipes/` that are not bundled or globally pinned |
| `bundled` | recipes shipped by Stoat and recorded in `.manifest`                               |

Project recipes shadow global, local and bundled recipes with the same name. Within the home directory, a globally pinned remote recipe shadows a local or bundled recipe. `stoat recipe show <name>` displays the visible contract.

## Project and global scopes

If the current directory contains `stoat.toml`, recipe commands use project scope. Stoat does not search parent directories. Declare an index recipe by ref, or declare a repository explicitly:

```toml
[recipes]
my-tools = "v1.2"
other-tools = { source = "https://github.com/example/stoat-other-tools", ref = "main" }
```

Project scope writes `./stoat.lock` and caches checkouts under `./.stoat/recipes/`. In a Git checkout Stoat adds `.stoat/` to `.gitignore`. Commit both `stoat.toml` and `stoat.lock` so another checkout uses the same commits. Pass `--global` to `add`, `lock`, `sync`, or `rm` to force global scope. `update` has no `--global` flag and uses the active scope.

Without a project file, the global lock is `~/.stoat/stoat.lock` and the cache is `~/.stoat/recipes/`. Global scope has no declaration file; its lock entries are the source of truth.

## Lock and synchronize

In project scope, `lock` resolves each declaration to a fresh commit and writes only the lock. It does not populate the cache:

```sh
stoat recipe lock
```

`sync` makes the cache match the lock. It validates every checkout, replaces missing or stale clean checkouts, and removes project cache directories that are absent from the lock:

```sh
stoat recipe sync
```

An existing checkout with uncommitted changes is refused. Copy it to a local recipe before changing it. Global sync leaves unrelated local recipes in the home recipes directory alone.

`apply`, `apply --dry-run`, and recipe listing take a coordinated snapshot. For a project, a stale declaration or lock reports an instruction to run `stoat recipe lock`; a missing or mismatched clean cache is repaired from the lock before apply. A dirty checkout remains an error.

## Update and remove

`update` fetches the ref stored in the lock and repins it. It accepts one or more plain names, or no names to update every remote recipe:

```sh
stoat recipe update my-tools
stoat recipe update
```

It does not search the index again. A checkout with local changes is refused.

`rm` removes a remote declaration, lock entry, and checkout. It asks for confirmation unless `-y` is present. It refuses when a VM lists the recipe; `--force` removes it despite those references:

```sh
stoat recipe rm my-tools -y
stoat recipe rm my-tools --force -y
```

`rm` is scoped to the current project when one is active. The MCP `remove_recipe` tool has no force option and refuses a recipe still used by a VM.

## Lock file shape

`stoat.lock` is generated by Stoat. It contains schema 1 and one entry per remote recipe. Each entry stores `source`, the requested `ref`, the resolved 40-character hexadecimal `commit`, and the original `added` timestamp. Treat it as generated data and commit it; do not hand-edit it to change a recipe ref. Change the declaration, then run `stoat recipe lock`.
