# chefe

> One manifest for every package manager.


# chefe

# chefe

One manifest for every package manager.

Conda, Python, Node.js, Rust, and whatever toolchains your project declares. Real projects need several at once, scattered across `pixi.toml`, `package.json`, and language-specific manifests. **chefe** is the head chef: you write **one `chefe.toml`** recipe, it compiles each native manifest under `.chefe/`, runs the real tools, and plates them as a single environment. It never re-implements a solver. It runs the cooks.

<div class="grid cards" markdown>

- :material-silverware-variant: **One recipe**

    Every language/toolchain in a single `chefe.toml`. No more juggling four manifests.

- :material-cog-transfer-outline: **Native output**

    Compiles to real `pixi.toml`, `package.json` and friends. The actual tools do the solving.

- :material-source-branch: **Composable**

    Platform overlays and named environments stack like pixi features.

- :material-broom: **Self-contained**

    The whole environment lives in `.chefe/`, so one command wipes it.

</div>

## Installation

```sh
pip install chefe      # or: pipx install chefe
```

chefe installs [pixi](https://pixi.sh), the engine it compiles to, on first run, so a single `pip install` is all you need.

```toml title="chefe.toml"
[workspace]
name = "my-project"

[deps]                 # conda, the default resolver
python  = ">=3.12"
nodejs  = ">=25"
ripgrep = "*"

[python.deps]
torch = ">=2.6"

[nodejs.dev.deps]
prettier = ">=3"
```

!!! warning "chefe is early (`0.0.x`)"
    The manifest format and commands may still change.

## Next

<div class="grid cards" markdown>

- [:material-cogs: **How it works**](how-it-works.md), the compile-and-run pipeline.
- [:material-console: **Commands**](commands.md), the full CLI.
- [:material-file-document-outline: **Manifest**](manifest.md), every `chefe.toml` table.
- [:material-test-tube: **Examples**](examples.md), a real-world monorepo recipe.

</div>

## Lore

A head chef never cooks every dish alone. They write the recipe and run the line, and the cooks each work their station. Scattered package managers are that line, so chefe directs them from one recipe. 🧑‍🍳


# How it works

# How it works

`chefe sync` compiles your one `chefe.toml` into the native manifests under `.chefe/`, then `chefe install` hands each one to the real tool so they solve and build a single shared environment.

```mermaid
flowchart TB
    subgraph recipe["one recipe (chefe.toml)"]
        direction LR
        D["[deps]<br/>conda"]
        PY["[python.deps]"]
        NP["[nodejs.deps]"]
        CG["[rust.deps]"]
    end

    subgraph compiled["chefe sync generates .chefe/"]
        direction LR
        PT["pixi.toml"]
        PJ["package.json"]
    end

    subgraph solve["chefe install runs the real tools"]
        direction LR
        PIXI["pixi<br/>conda-forge + Python"]
        NPM["npm"]
        CARGO["cargo<br/>via pixi run cargo"]
    end

    ENV(["one activated environment<br/>.chefe/ prefix on PATH"]):::brand

    D --> PT
    PY --> PT
    NP --> PJ
    CG -. no file, installs in-place .-> CARGO

    PT --> PIXI
    PJ --> NPM

    PIXI --> ENV
    NPM --> ENV
    CARGO --> ENV

    classDef brand fill:#eab308,stroke:#1a1a1a,stroke-width:2px,color:#1a1a1a;
```

- **Structure** is validated by chefe's schema, while **package specs** stay each tool's job.
- Editing `chefe.toml` through `chefe add` and `chefe remove` keeps your comments and formatting.
- `pixi` is the deep engine for conda and Python packages, and runtime-keyed toolchains are thin, explicit layers on top.

## Quickstart

```sh
chefe init                 # scaffold a chefe.toml
chefe add ripgrep          # conda is the default resolver
chefe add torch -l python
chefe add prettier -l nodejs
chefe install              # provision every language/toolchain at once
chefe tree                 # what's declared vs installed, per language/toolchain
```

Next, the [manifest reference](manifest.md) and the [command reference](commands.md).


# Commands

# Commands

chefe mirrors pixi's verbs over the unified manifest. Most commands take an optional `env` that defaults to `default`.

| command | what it does |
|---|---|
| `chefe init` | scaffold a starter `chefe.toml` in the current directory |
| `chefe sync` | compile `chefe.toml` into `.chefe/{pixi.toml, package.json, …}` |
| `chefe install [env]` | sync, then provision every language/toolchain for `env` |
| `chefe activate [env]` | write `.chefe/activate.sh` for this host (HPC modules + pixi env) |
| `chefe update [env]` | re-solve to the newest allowed versions across languages/toolchains |
| `chefe upgrade [pkg…]` | update within current bounds, or bump named conda and Python constraints |
| `chefe add <pkg…>` | add packages to the manifest, then re-sync |
| `chefe remove <pkg…>` | remove packages wherever they're declared, then re-sync |
| `chefe tree [env]` | declared vs installed, each dep checked in **its own** language/toolchain |
| `chefe run <cmd> [args…]` | run a task or installed executable inside the environment |
| `chefe x <cmd…>` | run a command in a throwaway env, like pipx run |
| `chefe shell [env]` | open an activated shell in `env` |
| `chefe global install [name]` | install every language/toolchain's deps into a shared global env |
| `chefe global add <pkg…>` | add conda packages to the workspace-named global env |
| `chefe global remove <pkg…>` | remove packages from the workspace-named global env |
| `chefe global list` | show all global envs, or one env with `-e` |
| `chefe clean` | remove the generated `.chefe/` env and manifests |

## init

```sh
chefe init                 # name taken from the current directory
chefe init --name myproj
```

Writes a minimal `chefe.toml` with the current platform, `conda-forge`, and `python >=3.11`. It refuses to overwrite an existing manifest.

## add

Conda is the default resolver, and `--language`/`-l` selects Python or any runtime declared in `[deps]`.
`--spec` sets the version (default `*`), while `--env` targets a named environment.

```sh
chefe add ripgrep numpy
chefe add torch -l python --spec ">=2.6"
chefe add prettier -l nodejs
chefe add vllm -l python --env serving
```

Edits keep your comments and formatting.

## tree

```sh
chefe tree
chefe tree serving
```

Each declared package is checked against the language/toolchain it was declared in. Conda goes against the pixi env, Node.js packages against `.chefe/node_modules`, and Rust crates against the env's `.crates.toml`. chefe reports each as `✓` ok, `≠` drift, or `✗` missing, with a transitive count.

## run and shell

```sh
chefe run build
chefe run qmd query "topic" -c zettel
chefe shell                # activated shell in the default env
chefe shell serving
```

`chefe run` first lets pixi resolve declared tasks, then ordinary executables from the managed
environment are available on PATH. That includes Node.js package binaries from `.chefe/node_modules/.bin`,
so a dev tool declared in `[nodejs.dev.deps]` does not need a redundant task just to expose its CLI.

## activate

```sh
chefe activate                # write .chefe/activate.sh for the default env
chefe activate serving
chefe install --activate-only # refresh the script without reinstalling
```

Writes a per-host `.chefe/activate.sh` that loads the manifest's `[modules]` stack and the pixi
environment in one `source`. A job or interactive shell then runs `source .chefe/activate.sh && python -m ...`
with the same modules and env it needs. `chefe install` always regenerates this script, and
`--activate-only` refreshes it against an already provisioned env.

## x

```sh
chefe x ruff check .                   # run a tool in a throwaway env, no manifest needed
chefe x --with build python -m build   # add extra packages with --with
```

Like `pipx run`, `chefe x` provisions an ephemeral environment for the tool and runs it, leaving no `chefe.toml` behind.

## global install

Provision every language/toolchain into one shared global env, the parity of `chefe install` for tools you want everywhere. Conda goes through `pixi global`, and adapters then use binaries from that global env for packages that need a second install step, such as Python, Node.js, and Rust.

```sh
chefe global install          # every language/toolchain's deps into a shared global env
chefe global install mytools  # name the env explicitly
chefe global add ripgrep fd-find
chefe global remove fd-find
chefe global list
chefe global list -e mytools
```

## clean

```sh
chefe clean                   # wipe .chefe/, a fresh chefe install rebuilds it
```


# Examples

# Examples

A real `chefe.toml` is the most convincing tour. This is the manifest behind a
multi-language ML monorepo, trimmed for readability but with every feature on
display: conda + Python (torch from a custom index) + Node.js + Rust, a system CUDA
floor, environment variables, platform overlays, an isolated serving
environment, and tasks.

```toml
[workspace]
name      = "life"
platforms = ["osx-arm64", "linux-64", "linux-aarch64"]
channels  = ["conda-forge", "nvidia"]
dotenv    = true                          # auto-load .env

[system]                                  # the conda virtual-package floor
cuda = "13.0"

[env]                                     # build vars; secrets stay in .env
CUDA_DEVICE_ORDER    = "PCI_BUS_ID"
CUDA_VISIBLE_DEVICES = "0"

[deps]                                    # bare table is conda, the default resolver
python      = ">=3.14"
nodejs      = ">=25"
rust        = "*"
ripgrep-all = ">=0.10"
pandoc      = ">=3.9"
# … runtimes, libs, and CLIs all live here

[python]                                  # settings for Python packages
index-strategy   = "unsafe-best-match"
extra-index-urls = ["https://pypi.nvidia.com"]

[python.indexes]                            # named indexes a dep can pin
pytorch = "https://download.pytorch.org/whl/cu132"

[python.deps]
pydantic = ">=2.13"
polars   = ">=1.40"
torch    = { version = ">=2.11", index = "pytorch" }   # pinned to the named index
# … the rest of the Python stack

[nodejs.dev.deps]
"@tobilu/qmd" = ">=0.1"
prettier      = ">=3"

[rust.deps]
bookokrat = { version = ">=0.1", locked = true }

[modules]                                 # HPC modules baked into .chefe/activate.sh
cuda = "13.2"
gcc  = "15.2.0"

[on.linux.deps]                           # platform overlay → pixi [target.linux]
cuda-nvcc = ">=13.3"
cupy      = ">=14"

[envs.serving]                            # a named, isolated environment
no-default = true

[envs.serving.deps]
python = "*"

[envs.serving.python.deps]
sglang        = ">=0.5"
sglang-kernel = { url = "https://github.com/sgl-project/whl/releases/download/v0.4.2/sglang_kernel-0.4.2+cu130-cp310-abi3-manylinux2014_aarch64.whl" }

[envs.serving.on.linux-64.deps]           # overlay nested inside an env
vllm = { version = ">=0.19", build = "cuda129*" }

[tasks]                                   # run inside the activated env
test = "python -m pytest"
lint = "pre-commit run --all-files"
```

## Setting it up

```sh
chefe sync                 # compile chefe.toml → .chefe/{pixi.toml, package.json}
chefe install              # provision every language/toolchain at once
chefe shell                # an activated shell with all the binaries on PATH
chefe run test             # run a task inside the environment
chefe tree                 # declared vs installed, each checked in its own language/toolchain
```

The isolated `serving` environment is provisioned and inspected the same way:

```sh
chefe install serving      # solve + provision just the serving env (sglang, vllm, …)
chefe tree serving
```

And for one-off tools, no manifest needed:

```sh
chefe x ruff check .       # run a tool in a throwaway env, like pipx run
```


# The manifest

# The manifest

Everything lives in one `chefe.toml`. The header configures the workspace, `[deps]` declares conda packages and runtime packages, matching runtime tables declare toolchain packages, and platform overlays and named environments compose on top.

## Workspace

```toml
[workspace]
name     = "my-project"
platforms = ["osx-arm64", "linux-64"]
channels = ["conda-forge", "nvidia"]
dotenv   = true                 # read .env into the environment, on by default
```

## Dependencies

The bare `[deps]` table is **conda**, the default resolver. A bare string is a version spec, and `*` means any.

```toml
[deps]
python = ">=3.11"
nodejs = ">=22"
ripgrep = "*"
pueue  = ">=4"
```

## Python

Python packages are resolved into the **same** environment. `[python]` holds settings, `[python.deps]` the packages, and `[python.indexes]` named extra indexes.

```toml
[python]
index-strategy = "unsafe-best-match"

[python.indexes]
pytorch = "https://download.pytorch.org/whl/cu124"

[python.deps]
torch = { version = ">=2.6", index = "pytorch" }
ruff  = ">=0.6"
```

## Runtime-keyed toolchains

Any package declared in `[deps]` can have a matching table with toolchain settings and packages.
The table name is the contract. Chefe does not need a language catalog.

```toml
[deps]
nodejs = ">=25"
rust = "*"
zig = ">=0.14"
cxx-compiler = "*"

[nodejs]
manager = "pnpm"

[nodejs.dev.deps]
"@tobilu/qmd" = "*"
prettier = ">=3"

[rust.deps]
bookokrat = "*"

[zig]
manager = "zig"

[cxx-compiler]
manager = "conan"

[cxx-compiler.deps]
fmt = ">=11"
```

`[nodejs]` has an adapter that emits `package.json` and runs `manager`, defaulting to `npm`.
`[rust]` has an adapter that installs crates with Cargo. Other toolchains are still discovered,
validated, shown in `chefe tree`, and can expose extra executable directories through `bin_dirs`.

!!! tip "Let tools fail where they run"
    Chefe does not preflight every possible manager. If `manager = "zig"` or `manager = "conan"`
    names a binary that is unavailable, the real command fails when it is used.

A runtime table only works when its name is also declared in `[deps]`. A table with no matching package fails with a self-contained error that names the running chefe and leads with the upgrade path, since the usual cause is a table from a newer chefe than the one installed. Run `pip install -U chefe`, or add the name to `[deps]`, or remove the table.

## JavaScript applications

By default `[nodejs.deps]` installs as tooling under `.chefe/`, beside the conda env. An application
sets `app = true`, and chefe installs at the project root and writes a full `package.json` there,
so Vite, SvelteKit, and the rest resolve `node_modules` the usual way.

```toml
[deps]
nodejs = ">=25"

[nodejs]
manager = "pnpm"
app = true

[nodejs.deps]
svelte = ">=5"
vite = ">=8"

[nodejs.package]
type = "module"
pnpm = { onlyBuiltDependencies = ["esbuild", "workerd"] }
```

`[nodejs.package]` is merged into `package.json` verbatim, so any field a tool expects rides through,
from `type` and `engines` to a package manager's own settings such as pnpm's
`onlyBuiltDependencies`. chefe writes the file, so `chefe.toml` stays the one thing you edit and a
generated `package.json` is a build artifact you can gitignore.

## Dev dependencies

`[dev.*]` mirrors the base scope for tools you need to build and test but not to run. Each group
compiles to its language/toolchain's own dev mechanism, and `chefe install` provisions them by default.

```toml
[dev.deps]            # conda dev tools
ruff = "*"

[dev.python.deps]       # Python dev tools
pytest = ">=8"

[nodejs.dev.deps]     # -> package.json devDependencies
vite = ">=8"
```

`[nodejs.dev.deps]` lands in `devDependencies`, while `[dev.deps]` and `[dev.python.deps]` become a
`dev` feature added to the default environment, so your linters and test runner install beside the
runtime deps. This is lighter than a full `[envs.dev]`, which is a separate environment with its
own solve.

Command-line tools declared in `[nodejs.dev.deps]` are available through `chefe run` and `chefe shell`.
For example, declaring `@tobilu/qmd` here lets `chefe run qmd ...` find the executable linked
under `.chefe/node_modules/.bin`. Do not add a task that only repeats that binary path.

## System requirements

The conda virtual-package floor used for cross-platform solving, not a module-load.

```toml
[system]
cuda = "13.0"
```

## Environment variables

Static variables applied when the environment activates. `.env` is also read by default, toggled with `dotenv` in the header.

```toml
[env]
LOG_LEVEL = "info"
CUDA_MODULE_LOADING = "LAZY"
```

## Activation scripts

Shell scripts sourced when the environment activates, for setup that static env vars can't express (computed paths, library symlinks). They compile to pixi's `[activation] scripts`, and a repo-root path keeps working from the generated `.chefe/`.

```toml
[activation]
scripts = ["scripts/activate.sh"]
```

## HPC modules

`[modules]` is a stack of HPC environment modules as `name = "version"` pairs. Each pair becomes one `module load name/version` line, in declared order, baked into the generated `.chefe/activate.sh`. So `source .chefe/activate.sh` loads the same modules a job needs before it runs.

```toml
[modules]
cuda = "13.2"
gcc  = "15.2.0"
```

This compiles to `module purge` followed by `module load cuda/13.2 gcc/15.2.0`. On a host without Lmod or environment-modules (a laptop, for instance) the load is guarded by `command -v module` and becomes a harmless no-op, so the same manifest is safe everywhere. Refresh the script for the current host with `chefe install` or `chefe activate`.

## Platform overlays

Conditionally add deps per platform, and they compile to native pixi targets. Any scope nests under `[on.…]`.

```toml
[on.linux.deps]
cupy = ">=13"

[on.linux-aarch64.deps]
python = "*"

[on.linux-aarch64.python.deps]
some-arm-wheel = "*"
```

## Named environments

Compose extra environments, like pixi features. `no-default = true` excludes the base deps, and `platforms` restricts the environment to where it can build (so a GPU env is skipped when solving on a laptop).

```toml
[envs.serving]
no-default = true
platforms  = ["linux-64", "linux-aarch64"]

[envs.serving.deps]
python = "*"

[envs.serving.python.deps]
vllm = ">=0.6"
```

Install or inspect one with `chefe install serving` or `chefe tree serving`.

## Tasks

Named commands that run inside the environment, reached with `chefe run <task>`. They run *code*, never install dependencies. Every package belongs in `[deps]`, `[python.deps]`, or a runtime-keyed table such as `[nodejs.deps]` or `[rust.deps]`, so `chefe install` and `chefe global` own it. Add packages with `chefe add pkg -l python`, `chefe add pkg -l nodejs`, or another declared language/toolchain. A task that shells out to `npm install -g` or `cargo install` is the anti-pattern chefe replaces.

```toml
[tasks]
serve = { run = "python -m my_project.server", depends = ["build"] }
build = "python -m my_project.build"
```

