Spec: project.recipe
- Status: Draft
- Last amended: 2026-06-19
- Constrained by: ADR-0033, ADR-0004
- Implements:
packages/agent-tooling/src/builtins/project-recipe-tools.ts,packages/daemon/src/runtime/tool-handlers/project-recipe-handlers.ts
Purpose
The project.recipe tool auto-detects project-specific build runners (npm, bun, cargo, poetry, pdm, uv, make) and provides a high-level interface for executing common project tasks. It resolves a simple op parameter to the appropriate command for the detected runner and delegates execution to shell.bash.
This tool is a convenience layer on top of shell.bash. It is intended to be auto-injected when shell.bash is present and the operator has enabled recipe support.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
op |
string | yes | The operation to run (e.g., "test", "build --release") |
cwd |
string | no | Working directory, relative to project root. Default: project root. |
Returns
| Field | Type | Description |
|---|---|---|
runner |
string | The detected runner used (e.g., "npm", "cargo", "make") |
command |
string | The resolved command that was executed |
stdout |
string | Combined stdout/stderr from the PTY |
stderr |
string | Always "" — PTY merges streams |
exit_code |
integer | Process exit code |
timed_out |
boolean | Whether the process was killed due to timeout |
available_tasks |
object | Map of runner name to list of available tasks |
Runner Detection
The tool scans the target directory (resolved from projectRoot + cwd) for the following project files, in order of priority:
| Runner | Detection Files | Task Source |
|---|---|---|
npm |
package.json |
scripts field |
bun |
package.json + bun.lockb or bun.lock |
scripts field |
cargo |
Cargo.toml |
Built-in subcommands |
poetry |
pyproject.toml with [tool.poetry] |
[tool.poetry.scripts] |
pdm |
pyproject.toml with [tool.pdm] |
[tool.pdm.scripts] |
uv |
pyproject.toml with [tool.uv] or uv.lock |
[tool.uv.scripts] or [project.scripts] |
make |
Makefile |
Target names (lines matching ^[a-zA-Z_][a-zA-Z0-9_.-]*:) |
Command Resolution
- The first word of
opis matched against each detected runner's task list. - If an exact match is found, that runner is used.
- If no exact match is found, the first detected runner is used as a fallback.
- The runner's command template constructs the final command:
- npm:
npm run <op> - bun:
bun run <op> - cargo:
cargo <op> - poetry:
poetry run <op> - pdm:
pdm run <op> - uv:
uv run <op> - make:
make <op>
- npm:
Error Handling
- No runners detected: Returns
invalid_paramswith message "No project runners detected in this directory." - Invalid
op: Returnsinvalid_paramsifopis empty, missing, or not a string. - Path traversal: Returns
invalid_paramsifcwdescapes the project root. - Shell execution failure: Returns the same error as
shell.bash(e.g.,shell_spawn_failed).
Security
The tool requires the same permissions as shell.bash:
fs: rw(the shell may write files)capability: shell
Path traversal on cwd is validated against the project root boundary. The tool delegates execution to the shell.bash handler, which enforces the same security model.
Notes
- This tool is a thin wrapper around
shell.bash. It does not introduce new execution capabilities; it only adds runner detection and command resolution. - The
available_tasksfield in the response helps the model discover what operations are available for subsequent calls. - Auto-injection (when
bashis present andrecipe.enabledis true) is a DSL/compiler concern, not a handler concern.