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 实测。

CookCLICookCLI食谱管理自托管2026/11/01

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 实测清单):

子命令别名功能关键参数
listls按分区列全部库存--section <NAME>
adda加条目(文件/分区不存在自动创建)--quantity --low --expire --bought
updateup改属性(只动给定的 flag)同 add
removerm删条目(分区删空则连分区删除)无
depletedd缺货/低库存--all
expiringe临期-d/--days(默认 7)--include-unknown
recipesr现有库存能做什么菜-p/--partial --threshold <PCT>(默认 75)
planpl反推「补齐哪些食材覆盖最多食谱」-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 输出。

十五、注意事项

  1. 长输出管道给 head 会触发 panic:0.39.0 在 stdout 提前关闭时以 Broken pipe (os error 32) panic 退出(见「常见问题」Q1)。批量脚本先重定向到文件,或容忍非零退出码。
  2. 参数形态:pantry 的 --quantity/--low/--expire/--bought 只有长形式(-q/-s 实测报错,见 Q3);search 无输出格式参数(见 Q2)。
  3. search 结果含 _site/ 副本:构建过静态站点后检索会命中站点内副本,管道前可 grep -v '^"_site/' 过滤。
  4. 库存扣除依赖名称与单位双匹配:单位不同类只 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 时按配置篇第六章的键约束来。

十七、参考链接

CookCLI 0.39.0 常用 CLI 命令与工具详解 - Quzuo