Compile declarative CI recipes into fully-wired GitHub Actions workflows with typed data-flow validation.
Project description
reci
Compile declarative CI recipes into fully-wired GitHub Actions workflows with typed data-flow validation.
To install: pip install reci
Why reci
Writing GitHub Actions workflows by hand means wading through hundreds of lines of
boilerplate: forwarding step outputs to job outputs, threading needs.X.outputs.Y
expressions across jobs, and copy-pasting config values that belong in one place.
When something changes you touch a dozen lines across three jobs — and one typo
silently breaks your pipeline.
reci lets you write a short recipe that says what your CI does (which actions,
in what order, with what config), and the compiler handles how — auto-wiring
cross-job outputs, injecting config from pyproject.toml, and validating data-flow
before you push.
Agent skills (recommended)
reci ships with CLI and Python APIs (see below), but the best way to use it is through AI agent skills — structured instructions that let coding assistants operate reci on your behalf. Instead of memorizing CLI flags and YAML syntax, just say what you want:
- "Set up CI for this project"
- "Migrate my existing workflow to reci"
- "What CI actions make sense here?"
Available skills
| Skill | What it does |
|---|---|
| ci-setup | Examines your project, proposes a CI plan, writes a recipe, compiles to a workflow |
| ci-migrate | Converts an existing GitHub Actions workflow into a reci recipe |
| ci-advisor | Interactive discussion about what CI pipeline makes sense for your project |
| reci-dev | Guide for contributors — architecture, adding rules, adding adapters |
Using the skills
Claude Code — symlink or copy the skills into your project or your personal skills directory:
# Make available to all your projects
ln -s /path/to/reci/.claude/skills/ci-setup ~/.claude/skills/ci-setup
ln -s /path/to/reci/.claude/skills/ci-migrate ~/.claude/skills/ci-migrate
ln -s /path/to/reci/.claude/skills/ci-advisor ~/.claude/skills/ci-advisor
# Or just for one project
ln -s /path/to/reci/.claude/skills/ci-setup my-project/.claude/skills/ci-setup
Then invoke with /ci-setup, /ci-migrate, or /ci-advisor in Claude Code.
See the Claude Code skills docs for details.
Other agents — the skills are plain Markdown files following the open Agent Skills standard. For Cursor, Copilot, Windsurf, and other tools, see the standard's integration guide.
Quick start
from reci import parse_recipe_string, compile_recipe, dump_workflow, ActionSpec, InputSpec, OutputSpec
recipe = parse_recipe_string("""
name: CI
on: [push, pull_request]
jobs:
test:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '${{ config.python_version }}'
- id: run_tests
run: pytest
""")
# Action specs can be fetched from GitHub or provided manually
specs = {
"actions/checkout@v4": ActionSpec(ref="actions/checkout@v4"),
"actions/setup-python@v5": ActionSpec(
ref="actions/setup-python@v5",
inputs={"python_version": InputSpec(name="python_version", default="3.x")},
),
}
config = {"python_version": "3.12"}
workflow = compile_recipe(recipe, specs, config=config)
print(dump_workflow(workflow))
The compiler:
- Injects a
setupjob that exports config values as job outputs - Rewrites
${{ config.* }}to${{ needs.setup.outputs.* }} - Auto-wires
needs:edges from data-flow dependencies - Generates the cross-job output forwarding ceremony (step output -> job output ->
needsconsumption)
CLI
# Inspect an action's input/output contract
reci inspect actions/setup-python@v5
# Compile a recipe to workflow YAML
reci compile recipe.yml --output .github/workflows/ci.yml
# Validate a recipe + config
reci validate --recipe recipe.yml --format cli
# Scaffold a config skeleton from a recipe
reci scaffold recipe.yml
Five-level input resolution
For each action input, the compiler resolves its value using this precedence:
- Explicit
with:in the recipe — used verbatim - Upstream output match —
${{ steps.<id>.outputs.<name> }} - Config value (scoped
action__key, then sharedkey) —${{ needs.setup.outputs.<key> }} - Default from action.yml — omitted (GitHub uses the default)
- Required + no source — validation error
Validation
ESLint-style severity (error/warning/info) with ruff-style rule prefixes:
| Rule | Category | What it catches |
|---|---|---|
| DAG001 | Structure | Cycle detected |
| DAG002 | Structure | Duplicate output name in a job |
| FLOW001 | Data flow | Required input has no source |
| FLOW002 | Data flow | Ambiguous wiring (multiple upstream matches) |
| FLOW006 | Data flow | Matrix job output consumed downstream (non-deterministic) |
| CONF001 | Config | Required config key missing |
| PURE001 | Purity | run: step breaks typed contract |
| ACT002 | Action | Referenced action not found |
Config adapters
Read CI config from your project file of choice:
| Adapter | Section | Library |
|---|---|---|
pyproject |
[tool.ci] |
tomlkit (round-trip) |
wads |
[tool.wads.ci] |
tomlkit |
package-json |
"ci" key |
json |
yaml |
.ci.yml |
ruamel.yaml |
The recipe format
A recipe looks like a GitHub Actions workflow with reci extensions:
${{ config.* }}— references to config values (resolved from pyproject.toml etc.)bind:— input renaming (bind: {tag_name: version}wires upstream outputversionto inputtag_name)outputs:onrun:steps — manual output annotation for untyped steps
name: Python CI
on: [push, pull_request]
jobs:
test:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '${{ config.python_version }}'
- run: pytest
publish:
needs: [test]
steps:
- uses: actions/checkout@v4
- id: bump
uses: i2mint/isee/actions/bump-version-number@master
- uses: i2mint/wads/actions/git-tag@master
bind:
tag_name: version # wire bump's "version" output to git-tag's "tag_name" input
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file reci-0.0.4.tar.gz.
File metadata
- Download URL: reci-0.0.4.tar.gz
- Upload date:
- Size: 78.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2469e51df96303b5ab5d56e79acc57f51171f578c0c3cd94d3f53b9ad558910c
|
|
| MD5 |
f183f7135a5882fd8f92dbc04f6a0a07
|
|
| BLAKE2b-256 |
93545ebc061810e8389952e4d7a3fbe2e27dd7d267e85db846640e9a5e90f5dd
|
File details
Details for the file reci-0.0.4-py3-none-any.whl.
File metadata
- Download URL: reci-0.0.4-py3-none-any.whl
- Upload date:
- Size: 31.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7b09a1c324a6b365e38db066e966595e259381f362f6169e1b2ed97d76b8ec7c
|
|
| MD5 |
6891f2354241e958cf9db653db56b116
|
|
| BLAKE2b-256 |
dd60cbe6fa9370357b98df6d1fbac304bd8b74121f67cf55eab0924f026084de
|