CookCLI 0.39.0 常用 CLI 命令与工具详解
CookCLI 0.39.0 全部 14 个子命令实测详解:recipe 多格式渲染与缩放、shopping-list 分区与库存扣除、search 管道、pantry CRUD 与分析、doctor 体检、build web 静态站、report 模板、seed/import/server/lsp/completions/update,含 broken pipe 等实测踩坑,Ubuntu 24.04 实测。
CookCLI 0.39.0 常用 CLI 命令与工具详解
目标:按子命令逐个讲透 CookCLI 0.39.0 的命令行面——
recipe/shopping-list/search/pantry/doctor/build web/report/seed/import/server/lsp/login/completions/update的语法、关键参数与真实回显,并沉淀管道、缩放、库存扣除等组合用法与实测踩坑。全部命令在 Ubuntu 24.04 服务器、cook seed示例食谱库(12 份.cook+ 2 份.menu)上逐条实测。
一、环境信息
| 项 | 值 |
|---|---|
| 操作系统 | Ubuntu 24.04 LTS(x86_64) |
| 软件 | CookCLI v0.39.0,预编译 musl 二进制(部署过程见本专栏部署篇) |
| 版本回显 | cook --version → cookcli 0.39.0 - in food we trust |
| 工作目录 | /root/my-recipes(cook seed 生成的官方示例库) |
| 配置文件 | config/aisle.conf、config/pantry.conf(格式详解见本专栏配置篇) |
二、工具概览
CookCLI 是单二进制多命令的 UNIX 式工具集,v0.39.0 共 14 个子命令(cook --help 实测清单):
Commands:
recipe Parse, validate and display recipe files in various formats
server Start a local web server to browse and view your recipe collection
build Build artifacts from your recipe collection
shopping-list Generate a combined shopping list from multiple recipes [alias: sl]
seed Initialize a directory with example Cooklang recipes
search Search through your recipe collection for matching text
import Import recipes from supported websites and convert to Cooklang
report Generate custom reports from recipes using templates
doctor Analyze your recipe collection for issues and improvements
pantry Manage and analyze your pantry inventory
lsp Start the Cooklang Language Server Protocol (LSP) server
login Sign in to CookCloud
logout Sign out of CookCloud
completions Generate a shell completion script
update Update CookCLI to the latest version
全局选项只有 -v/--verbose(-v info、-vv debug、-vvv trace)。多数命令共享 -b/--base-path(食谱库根,默认当前目录)与 -f/--format(human/json/yaml,shopping-list 另支持 markdown)。
三、前置条件
安装 cook 并准备食谱库(没有自有库就用示例库):
cook --version
# cookcli 0.39.0 - in food we trust
cook seed /root/my-recipes
cd /root/my-recipes
本文所有回显均出自该目录。
四、cook recipe:解析、渲染与缩放
语法:cook recipe [OPTIONS] [RECIPE],另有 cook recipe read 子命令。RECIPE 支持完整路径、相对路径、裸食谱名(扩展名 .cook 可省),以及 :N 内联缩放(优先于 -s/--scale);省略参数则从 stdin 读取。
输出格式(-f,8 种):human(默认)、json、yaml、cooklang、markdown、latex、typst、schema(Schema.org JSON-LD);配合 -o 时可按目标文件后缀自动推断(.json/.yaml/.md/.cook/.tex/.typ/.txt)。--pretty 美化 JSON/YAML;-p/--paper-size、-m/--margin 仅作用于 LaTeX/Typst。
人读格式实测(节选):
cook recipe "Neapolitan Pizza.cook"
# Neapolitan Pizza
# #italian #pizza
#
# source: https://www.stadlermade.com/how-to-pizza-dough/neapolitan/
# time: 20m
# servings: 6
# cuisine: Italian
# difficulty: advanced
#
# Ingredients:
# Pizza Dough (recipe: ./Shared/Pizza Dough) 6 balls
# San Marzano tomato sauce 5 tbsp
# basil leaves
# flour
# mozzarella cheese 100 grams
# semolina
#
# Cookware:
# outdoor oven
# spatula
#
# Steps:
# 1. Preheat your outdoor oven so it’s around 450/500°C (842/932°F).
# [-]
# ...
Markdown 导出(批量转文档的主力格式,节选):
cook recipe "Neapolitan Pizza.cook" -f markdown
# ---
# servings: 6
# source: https://www.stadlermade.com/how-to-pizza-dough/neapolitan/
# tags: italian, pizza
# cuisine: Italian
# difficulty: advanced
# time: 20 min
# name: Neapolitan Pizza
# ---
#
# # Neapolitan Pizza
# ...
# ## Ingredients
#
# - semolina
# - *6 balls* [Pizza Dough](./Shared/Pizza%20Dough)
# - *5 tbsp* San Marzano tomato sauce
# - *100 grams* mozzarella cheese
JSON 输出为完整解析树(metadata + sections,步骤内食材/炊具以 {"type":"ingredient","index":n} 引用),长输出节选(实测首尾):
cook recipe "Neapolitan Pizza.cook" -f json
# {"metadata":{"map":{"servings":6,"source":"https://www.stadlermade.com/...
# ...
缩放实测——:2 后份数与用量同步翻倍:
cook recipe "Neapolitan Pizza.cook:2" | head -20
# Neapolitan Pizza @ 2
# ...
# servings: 12
# ...
# Pizza Dough (recipe: ./Shared/Pizza Dough) 12 balls
# San Marzano tomato sauce 10 tbsp
# mozzarella cheese 200 grams
批量转 Markdown 的官方组合套路:
for r in *.cook; do cook recipe "$r" -f markdown > "docs/${r%.cook}.md"; done
五、cook shopping-list(别名 sl):合并购物清单
语法:cook shopping-list [OPTIONS] [RECIPES]...,多食谱/菜单合并去重,同名食材自动累加(忽略大小写、保留首个拼法)。
关键参数:-b 库根;:N 缩放;-o 写文件(按后缀推断格式);-f human|json|yaml|markdown;-p/--plain 不分区;-a 指定 aisle 文件;--pantry/--ignore-pantry 控制库存扣除;-i/--ignore-references 不展开子食谱引用;--extra <ITEM> 追加清单外物品("eggs{12}" 带量);--include-optional 带出可选食材;--from/--to 只取菜单日期区间。
单食谱(自动展开 @./Shared/Pizza Dough 引用、按 aisle 分区、扣库存),实测回显含真实 WARN:
cook shopping-list "Neapolitan Pizza.cook"
# WARN Unit mismatch for 'salt': recipe needs 'g', pantry has 'kg'
# WARN Unit mismatch for 'tipo zero flour': recipe needs 'g', pantry has 'kg'
# WARN /root/my-recipes/Shared/Pizza Dough.cook: Unsupported value for key: 'yield'
# [milk and dairy]
# mozzarella cheese 100 grams
# [tinned goods and baking]
# tipo zero flour 840 g
# fresh yeast 1.6 g
# [dried herbs and spices]
# salt 24.6 g
# [other]
# semolina
# San Marzano tomato sauce 5 tbsp
# basil leaves
解读:面团食材(flour/yeast/salt)来自被展开的子食谱并按 :1 倍量聚合;mozzarella cheese 未被库存扣除,而库存里的 parmesan cheese 等没有出现在清单——两条 Unit mismatch WARN 说明单位不同类时不换算、不扣除(配置篇第八章)。
菜单聚合 + 日期窗口(Weekly Plan.menu 只取今日起):
cook shopping-list "Weekly Plan.menu" --from today
多食谱合并(节选,8 份食谱 + 菜单聚合出 13 个分区组):
cook shopping-list "Neapolitan Pizza.cook" "Risotto.cook" "lamb-chops.cook" "2 Day Plan.menu" | head -30
# [fruit and veg]
# lemon
# onion 1
# ripe tomatoes 3 large
# garlic 2 cloves
# [milk and dairy]
# milk 1/2 cup, 1500 ml
# egg 18
# [meat and seafood]
# lamb chops 8 large
# ...
# [dried herbs and spices]
# salt pinch, pinch, pinch, 49.2 g, 6 tbsp, 1 1/8 tsp
写文件(扩展名推断格式):
cook shopping-list "Neapolitan Pizza.cook" -o /tmp/sl.txt; echo exit=$?
# exit=0
cat /tmp/sl.txt
# [milk and dairy]
# mozzarella cheese 100 grams
# [tinned goods and baking]
# tipo zero flour 840 g
# fresh yeast 1.6 g
# [dried herbs and spices]
# salt 24.6 g
# [other]
六、cook search:全文检索
语法:cook search [OPTIONS] <TERMS>...。多词为「与」关系(每个词都必须命中),按相关度排序,文件名命中权重最高;-b/--base-dir 指定检索根目录。输出为带引号的食谱路径列表,可直接管道给 shopping-list(xargs 能正确处理含空格路径):
cook search pizza
# "Neapolitan Pizza.cook"
# "_site/recipe/Neapolitan Pizza.cook"
# "Shared/Pizza Dough.cook"
# "_site/recipe/Shared/Pizza Dough.cook"
# "2 Day Plan.menu"
# "Weekly Plan.menu"
cook search chicken
# "Thai Green Curry.cook"
# "_site/recipe/Thai Green Curry.cook"
cook search pizza | xargs cook shopping-list 2>/dev/null | head -8
# [fruit and veg]
# lemon
# lime 3 1/2
# avocado 2
# onion 2
# red onion 1
# red bell pepper 1
# cherry tomatoes 10
注意:search 没有 -f 格式参数(实测报错见「常见问题」);静态站点目录 _site/ 里的副本也会被检出,检索前可先删除或加目录过滤。
七、cook pantry:库存 CRUD 与分析
语法:cook pantry [OPTIONS] <COMMAND>,9 个子命令(cook pantry --help 实测清单):
| 子命令 | 别名 | 功能 | 关键参数 |
|---|---|---|---|
list | ls | 按分区列全部库存 | --section <NAME> |
add | a | 加条目(文件/分区不存在自动创建) | --quantity --low --expire --bought |
update | up | 改属性(只动给定的 flag) | 同 add |
remove | rm | 删条目(分区删空则连分区删除) | 无 |
depleted | d | 缺货/低库存 | --all |
expiring | e | 临期 | -d/--days(默认 7)--include-unknown |
recipes | r | 现有库存能做什么菜 | -p/--partial --threshold <PCT>(默认 75) |
plan | pl | 反推「补齐哪些食材覆盖最多食谱」 | -n -s -m |
全局 -f human|json|yaml 对输出生效。
CRUD 实测(示例库 pantry 上增→查→改→删闭环):
cook pantry add pantry "tomato paste" --quantity "2%tins"
# Added 'tomato paste' to section 'pantry'.
grep -n "tomato paste" config/pantry.conf
# 29:"tomato paste" = "2%tins"
cook pantry update pantry "tomato paste" --quantity "1%tins"
# Updated 'tomato paste' in section 'pantry'.
grep -n "tomato paste" config/pantry.conf
# 29:"tomato paste" = "1%tins"
cook pantry depleted
# Depleted or Low Stock Items:
# ============================
#
# PANTRY:
# • tinned tomatoes (0)
# • tomato paste (1%tins)
#
# SPICE RACK:
# • fennel seeds (30%g)
# ...
cook pantry remove pantry "tomato paste"
# Removed 'tomato paste' from section 'pantry'.
分析类命令实测:
cook pantry expiring
# Items Expiring Within 7 Days:
# ================================
#
# Expiring Soon:
# • butter - 2026-04-15 (EXPIRED 200 days ago) [fridge]
cook pantry recipes
# Recipes You Can Make with Pantry Items:
# ========================================
#
# ✓ Complete Matches (all ingredients available):
# • Easy Pancakes
cook pantry plan | head -8
# Optimal Pantry Plan (Greedy Coverage):
# =======================================
#
# With these 67 ingredients, you can cook 24 out of 24 recipes:
#
# 1. black pepper (+0 recipes, 0 total)
# 2. salt (+0 recipes, 0 total)
JSON 输出(脚本集成):
cook pantry -f json depleted | head -12
# {
# "items": [
# {
# "name": "tinned tomatoes",
# "section": "pantry",
# "quantity": "0",
# "low_threshold": null,
# "is_low": false
# },
要点:add/update/remove 原地编辑 pantry.conf,注释与键序保留;plan 的贪心算法按「新增一种食材可多解锁的食谱数」排序(示例库已全量可 cook,故每项 +0)。
八、cook doctor:食谱库体检
语法:cook doctor [COMMAND],子命令 validate(语法校验)、aisle(分区覆盖度)、pantry(库存联动检查);不带子命令则全跑并加版本检查。
全量体检实测(节选,长输出标注实测首尾):
cook doctor
# Running all doctor checks...
#
# === Version Check ===
# ✅ You are running the latest version.
#
# === Recipe Validation ===
#
# 📄 Risotto.cook
# Warning: Unnecessary scaling lock modifier
# ╭─[Risotto.cook]
# │
# 48 │ Season with @salt{=1%tsp} and @black pepper{} to taste.
# │ ╰── this scaling lock has no effect
# ...
# === Validation Summary ===
# Total recipes scanned: 26
# ⚠️ 6 warning(s) found in 6 recipe(s)
体检输出是结构化的「文件 → 行号 → 原因」诊断(如 yield: 6%balls 报 Expected 'number' but got 'string'),并给出行动建议(Run cook doctor aisle --show-recipes ... / Consider adding these ingredients to your aisle.conf file.)。aisle/pantry 两个子检查的输出样例见配置篇第五章。
九、cook build web:静态站点构建
语法:cook build web [OUTPUT_DIR](默认 ./_site),把整个食谱库编译成自包含静态站(可 file:// 直开或托管任意静态主机),不含购物清单/库存/编辑等动态功能。
关键参数:--base-path、--base-url(子路径托管)、--lang(构建期定语言,默认系统 locale 回退 en-US)、--sitemap <URL>、--feed[=URL](atom/rss/json feed)、--repo-url(页脚源码链接)、--compress(并写 .gz 预压缩副本)。
实测:
cook build web
# WARN Recipe 'Vinaigrette': Unsupported value for key: 'yield'
# WARN Recipe 'Classic Risotto alla Milanese': Unnecessary scaling lock modifier
# WARN Recipe 'Pizza Dough': Unsupported value for key: 'yield'
# Wrote index, directories, 14 recipe pages, 5 images, 22 static assets, 14 search entries
ls _site
# api directory index.html menu recipe static
构建警告即 doctor 同款问题,不阻断出站;站点自带搜索索引(14 search entries)与 /api 只读端点。
十、cook report:Jinja2 模板报表
语法:cook report --template <TPL> [OPTIONS] <RECIPE>,-d 数据仓(db/ 的价格/元数据)、-a/-p/-b 同购物清单,:N 缩放同样可用。模板变量:metadata、ingredients、sections、cookware、scale。
实测(示例库自带 reports/recipe-card.md.jinja):
cook report -t reports/recipe-card.md.jinja "Neapolitan Pizza.cook"
# WARN ⚠️ The report command is a prototype feature and will change in future versions.
# **Servings:** 6
#
# ## Ingredients
# - 6 balls Pizza Dough
# - 5 tbsp San Marzano tomato sauce
# - 100 grams mozzarella cheese
# ...
注意回显首行:report 是官方标注的原型特性,模板 API 未来会变,批量生产前留意版本说明。
十一、cook seed:示例库初始化
cook seed [DIR](目录不存在则创建)铺出全套功能素材:食谱/菜单/图片/config/(aisle+pantry 示例)/db//reports/,输出即文件清单(实测见第三章)。上手、复现 bug、测试配置都用它,10 秒得到与本文完全一致的环境。
十二、cook import:网页食谱抓取转换
语法:cook import [OPTIONS] <URL>,支持 allrecipes/bbc good food/nytimes cooking 等主流站点的 schema.org 标注,抓取后自动转 Cooklang。参数:-s/--skip-conversion(输出原始抽取数据)、--metadata frontmatter|json|yaml|none、--metadata-only。
转换由 LLM 完成,需要环境变量 OPENAI_API_KEY(系统级密钥,注入方式示例):
export OPENAI_API_KEY="${OPENAI_API_KEY}" # 从你的密钥管理注入,勿硬编码
cook import "https://www.bbcgoodfood.com/recipes/easy-pizza-dough"
本次取证环境未配置密钥,故只给出 --help 实测参数面与官方用法,未附抓取回显。
十三、cook server:Web 服务与用户管理
cook server [OPTIONS] [BASE_PATH] 起常驻 Web 界面(浏览/编辑/购物清单/库存/API),参数与部署形态详解见本专栏部署篇与配置篇。CLI 侧常用的是它的两个子命令:
cook server hash-password # 生成 argon2id 口令哈希,手编 users.toml 用
cook server user add|passwd|role|remove|list # 用户与角色管理
cook server user list
# admin admin
角色 reader/shopper/editor/admin 能力阶梯见配置篇 5.3。
十四、其余命令与全局习惯
cook lsp:启动 Cooklang 语言服务器(stdio),供编辑器接入(VS Code/neovim 插件配置 Cooklang 语法后调用),一般由编辑器拉起而非手跑;cook server的内置编辑器就是按 tab 拉起它的子进程。cook login/cook logout:CookCloud OAuth 设备码流程(浏览器确认),配合cook server在手机 App 与服务器间同步食谱目录,需 Cook Basic 订阅;无订阅环境跑cook login只会进入等待授权状态。cook completions <shell>:bash/zsh/fish/elvish/powershell 补全脚本,安装即重定向:
cook completions bash > /etc/bash_completion.d/cook
head -3 /etc/bash_completion.d/cook
# _cook() {
# local i cur prev opts cmd
# COMPREPLY=()
cook update:从 GitHub Releases 原地自更新(--check-only只查、--force强装):
cook update --check-only
# Current version: 0.39.0
# Checking for updates...
# You are already on the latest version!
- 详细日志:任何命令加
-v/-vv/-vvv,提 issue 时官方要求附-vvv输出。
十五、注意事项
- 长输出管道给
head会触发 panic:0.39.0 在 stdout 提前关闭时以Broken pipe (os error 32)panic 退出(见「常见问题」Q1)。批量脚本先重定向到文件,或容忍非零退出码。 - 参数形态:
pantry的--quantity/--low/--expire/--bought只有长形式(-q/-s实测报错,见 Q3);search无输出格式参数(见 Q2)。 - search 结果含
_site/副本:构建过静态站点后检索会命中站点内副本,管道前可grep -v '^"_site/'过滤。 - 库存扣除依赖名称与单位双匹配:单位不同类只 WARN 不扣除(见第五章回显),名称匹配不吃 aisle 别名(配置篇 Q2)。
十六、常见问题
Q1:cook recipe ... -f json | head 报 Broken pipe 甚至 panic?
实测两种形态:
Error: Broken pipe (os error 32)
thread 'main' (3826) panicked at .../library/std/src/io/stdio.rs:1168:9:
failed printing to stdout: Broken pipe (os error 32)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
原因:head 读满即关闭管道,Rust 写 stdout 收到 EPIPE 后按未处理 IO 错误退出。规避:cook recipe x.cook -f json > /tmp/out.json && head /tmp/out.json,或 | head -c 2000 之类限量读取。
Q2:cook search -f json pizza 报 unexpected argument?
search 没有 -f 参数(其输出本就是纯路径列表,供管道消费)。实测报错:
error: unexpected argument '-f' found
tip: to pass '-f' as a value, use '-- -f'
Usage: cook search [OPTIONS] <TERMS>...
要 JSON 就自己拼:cook search pizza | sed 's/.*/"&",/' 或改用 cook server 的 /api/search?q=pizza(返回真 JSON,见部署篇 4.6 实测)。
Q3:cook pantry add pantry flour -q 1%kg 报错?
pantry add/update 的数量参数只有长形式 --quantity,无 -q 短参;-s 同理不存在。实测:
error: unexpected argument '-q' found
tip: to pass '-q' as a value, use '-- -q'
Usage: cook pantry add [OPTIONS] <SECTION> <NAME>
正确写法:cook pantry add pantry flour --quantity "1%kg"(见第七章)。另注意 remove 的 <NAME> 是必填位置参数,漏了会报 the following required arguments were not provided。
Q4:cook shopping-list 刷屏一堆 Unit mismatch WARN,能静音吗?
WARN 是设计行为(提示「这项不会被库存扣除」)。治本是修数据:把 pantry.conf 单位与食谱对齐;临时静音可 2>/dev/null(错误与警告都走 stderr,正常清单走 stdout,管道安全)。
Q5:cook doctor 报 Unsupported value for key: 'yield',但示例库不该错?
0.39.0 要求 yield 为纯数字(份数),示例库个别食谱写了 yield: 6%balls(带单位字符串)故被警告——不影响解析与清单,属官方示例的宽松写法。自写 frontmatter 时按配置篇第六章的键约束来。
十七、参考链接
- 官方命令文档:https://cooklang.org/cli/
- Cooklang 规范:https://cooklang.org/docs/spec
- 仓库与 Releases:https://github.com/cooklang/cookcli
- 本专栏:《CookCLI 介绍与选型指南》、《CookCLI Docker Compose 部署指南》、《CookCLI 0.39.0 配置参数详解》