One kitchen for the whole crew
Cargo stays Cargo, pnpm stays pnpm. Their builds, your codegen and your scripts join one graph and share one cache.
Cargo, CMake, pnpm, codegen, and a scripts/ directory nobody will admit to writing, each with its own idea of what's up to date. Cook is one kitchen for the whole crew: one dependency graph, one content-addressed cache. You describe artifacts in a Cookfile; cook runs exactly the work that changed.
A Cookfile lowers to Lua and registers its units. Registration becomes one graph. Every ready unit checks the cache first; only the misses reach the workers.
recipe reportgather "chapters/*.txt"# fan-out: one count per chaptercook "build/counts/$<in.stem>.count" {wc -w < $<in> | tr -d ' ' > $<out>}# many-to-one: every count incook "build/total.txt" {cat $<in> | paste -sd+ - | bc > $<out>}# one step, two outputscook "build/report.txt" "build/report.csv" {...}
cook reportCooked report/build/counts/one.count in 1msCooked report/build/counts/three.count in 1msCooked report/build/counts/two.count in 1msCooked report/build/total.txt in 1msCooked report/build/report.txt in 2msFinished in 11ms (5 nodes, 0 cached)cook reportCached report (5 nodes)Finished in 1ms (5 nodes, all cached)sed -i 's/moon/sun and stars/' chapters/two.txtcook reportCached report/build/counts/one.countCached report/build/counts/three.countRebuilding report/build/counts/two.count — input changed: chapters/two.txtCooked report/build/counts/two.count in 1msRebuilding report/build/total.txt — input changed: build/counts/two.countCooked report/build/total.txt in 1msRebuilding report/build/report.txt — input changed: build/total.txtCooked report/build/report.txt in 2msFinished in 18ms (5 nodes, 2 cached)
Cargo stays Cargo, pnpm stays pnpm. Their builds, your codegen and your scripts join one graph and share one cache.
Put $<in.stem> in a target and the step fans out, one cached unit per input, across every core. A static target gathers. You never write the loop or the join.
Fan out over a JSON manifest with a probe. Add a record and exactly one new unit builds; delete one and cook sweeps the orphan.
Every unit is keyed on what you declared and nothing else. Want the compiler in the key? seal compiler. Keys travel across machines by default.
cook why prints every determinant behind every key, hit or miss. cook cache verify re-runs cached work and catches any byte of divergence.
Point your team and CI at one path and they reuse each other's artifacts. No server to run.
One graph builds the engine, the game plugins and the map tools, then uses the dmap it just built to compile a playable map. Clone, cook, play.
recipe uppergather "src/*.txt"cook "build/$<in.stem>.txt" >{local text = fs.read(input)fs.write(output, text:upper())}
Change a shared package and Turbo rebuilds everything downstream. Cook checks whether the artifact actually changed, and stops.
Great inside a native build; everything else becomes custom commands around it. In cook, C, pnpm and codegen are all ordinary units.
Bazel asks you to rewrite your build in its terms. Cook wraps the tools you already run, with a cache key you own and can audit.
Cook was born on a project that used make and just side by side. make owned the artifacts and the incremental builds; just owned the commands people actually wanted to run. Both were excellent at their jobs, and every new piece of work came with the same question: Makefile or justfile?
Cook is the one answer. just’s recipe ergonomics, make’s artifact graph, content-addressed reuse across the whole repository, and Lua underneath everything. Every Cookfile lowers to Lua before it runs, and cook emit-lua shows you exactly what yours becomes.
curl -fsSL https://getcook.sh | shcook initCreated CookfileCreated .gitignorecookCooked build/build/hello.txt in 0msFinished in 11ms (1 nodes, 0 cached)
One Rust binary with Lua 5.4 and LuaRocks bundled. Apache-2.0.
Ten runnable projectsFrom hello-cook to cache trust, each with a watch-the-cache-work moment.
Liora Labsppu.toys, Git Mosaic, Canopy, and what's next.