mcpField
R
recipe-cleanup

recipe-cleanup

github.com/gkwa生产力073 次安装2 天前更新
代码仓库

关于 recipe-cleanup

用于清理并规范化食谱文件的技能,可从本地文件、网页、YouTube 视频说明或内联文本提取内容。它按食材、步骤、备料和来源等规则整理格式,并处理图片、创作者信息及食材链接。

主要功能

  1. 01读取本地文件、网页或内联食谱
  2. 02从 YouTube 说明提取食谱
  3. 03规范食材清单与烹饪步骤
  4. 04整理备料和烹饪阶段
  5. 05生成创作者元数据并查找图片
  6. 06解析食材笔记链接

适用场景

  1. 01将网页食谱整理为统一文件
  2. 02从 YouTube 视频说明制作食谱笔记
  3. 03将食材链接对应到现有产品笔记
  4. 04按备料和烹饪阶段重排步骤

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gkwa/volcanicviper --skill recipe-cleanup

安装后在 Claude Code 中输入/recipe-cleanup确认已加载。

附带文件tile.json

详细介绍

食谱整理

解析输入:第一个参数可以是本地食谱文件路径,也可以是食谱页面的 URL。可选的第二个参数是原食谱的来源 URL。URL 或文件路径之后的所有文本都视为内联食谱内容(食材、做法、备注)。

根据第一个参数确定输入模式:

  • 如果以 http:// 或 https:// 开头,则视为 URL — 获取页面并提取食谱内容;该 URL 也会作为隐式来源 URL(除非第二个参数覆盖它)
    • YouTube 观看 URL(youtube.com/watch?v=VIDEO_ID):提取视频 ID,并将缩略图设为 https://i.ytimg.com/vi/VIDEO_ID/hq720.jpg,然后尝试按以下备用顺序获取视频简介:
      1. 先对 YouTube URL 使用 WebFetch — 如果返回了可用的视频简介内容,则使用这些内容
      2. 如果 WebFetch 失败或未返回视频简介,则改用 Playwright:访问页面,等待 3 秒使页面加载,然后点击 "...more" 按钮展开完整简介
      3. 获取视频简介后(无论通过哪种方式),扫描其中是否包含食谱内容:
        • 简介中包含食谱:如果简介直接包含食材或做法,则将其用作食谱内容
        • 简介中包含链接:如果简介包含指向外部食谱页面的链接(例如博客文章或专门的食谱 URL),则使用 WebFetch 获取该 URL,并从中提取食谱;记录 YouTube URL 和食谱页面 URL — 两者都会出现在输出文件中
        • 内联文本备用方案:如果简介中两者都没有,则使用参数中提供的任何内联文本
      4. 使用频道名称和视频标题识别创作者
  • 否则,将其视为本地文件路径 — 读取该文件

根据频道 URL 识别创作者:

  • 如果任一参数是 YouTube 频道 URL(例如 youtube.com/@Handle 或 youtube.com/c/name),尝试获取该页面以提取创作者的真实姓名
  • 如果获取失败,则通过拆分句首大写的部分,从账号名称推导姓名(例如 @FastEasyRecipes → Fast Easy Recipes)
  • 在 creator frontmatter 字段中使用该姓名

根据以下格式规则处理食谱内容。

Frontmatter:

  • 在文件顶部(来源 URL 行之前)生成 YAML frontmatter,并包含以下字段:
    ---
    creator: '[[Author Name]]'
    pic: <hero-image-url>
    tags:
    - recipe
    - <creator tag>
    ---
    
  • 创作者标签为解析出的页面名称转为小写,并移除所有空格和分隔符(例如,Andrea Geary → andreageary、budgetbytes → budgetbytes、Fast Easy Recipes → fasteasyrecipes)
  • 从食谱页面提取作者/创作者姓名(例如,从食谱卡片的作者字段或博客的关于页面中提取)
  • 按以下优先顺序确定创作者的页面名称:
    1. 如果食谱来自作者自己的个人域名,则使用不含 TLD 的域名作为页面名称(例如,budgetbytes.com → budgetbytes、halfbakedharvest.com → halfbakedharvest)。这是作者选定的品牌名称。
    2. 如果食谱来自大型出版物(例如,Bon Appétit、Serious Eats、NYT Cooking、Food Network、Epicurious、Tasty),则使用该出版物署名的作者姓名 — 内容属于出版物,而非个人品牌。
    3. 如果没有个人域名(例如,仅在 YouTube 上发布内容的创作者),则使用作者的真实姓名。
  • 在 creator frontmatter wikilink 中使用解析出的页面名称(例如,[[budgetbytes]])
  • 写入文件后,检查与食谱文件相同目录下是否存在名为 <page name>.md 的页面 — 如果不存在,则创建一个最简存根文件:包含 # <page name>,后接一个空行和作者的来源 URL(例如,其 YouTube 频道 URL、网站,或食谱的来源页面)

格式规则

食材:

  • 将每行食材格式化为已勾选的复选框:- [x] quantity [[ingredient name]]
  • 使用 Obsidian 双链包裹食材名称(不包括数量或描述):[[ingredient name]]
  • 确保双链涵盖完整的产品名称,以链接到仓库中对应的产品页面
    • 示例:1 cup julienned carrots → - [x] 1 cup julienned [[carrots]]
    • 示例:2 teaspoons sesame oil → - [x] 2 teaspoons [[sesame oil]](而非 sesame [[oil]])
  • 如果食材已有双链,则保持原样
  • 写入文件后,解决每个未指向现有笔记的食材双链——请参阅下方的“解决食材双链”部分

解决食材双链:

  • 清理后的文件写入后,每个食材名称都应使用 [[wikilink]] 包裹,但其中许多双链可能不会指向知识库中现有的产品笔记
  • 不要自行编造或手动构造目标;请委派给 resolve-recipe-ingredient-link 技能,该技能是唯一的规范解析器
  • 对于每个目标笔记在知识库中不存在的食材双链(按 Obsidian 解析链接时使用的方式,通过基本名称判定),调用 resolve-recipe-ingredient-link,查找最匹配的产品笔记并将链接重写为解析后的形式
  • 将完整的食材行作为上下文传给解析器,而不只是单独的括号文本——链接词语周围的描述性文字有助于区分匹配项
  • 只枚举一次候选产品笔记,并基于同一列表解析该食谱中所有未解析的链接,而不是针对每种食材重新枚举
  • 已解析到现有笔记的双链保持不变
  • 如果解析器仅凭食材行无法确定链接目标,请遵循其三级规则——列出候选项并询问用户,不要强行选择错误的匹配项

说明:

  • 指令行的首字母绝不大写(以小写字母开头)

  • 指令末尾不要加句号

  • 每个句子单独成段——将包含多个句子的指令拆分为独立句子,并以空行分隔

  • 删除所有粗体、斜体或强调格式,仅保留纯文本

  • 指令始终使用普通段落——如果原文使用编号列表或有序列表,则将每个项目转换为普通段落

  • 如果某个步骤以逗号分隔的序列列出多个项目(例如“add onions, oil, butter, and salt”),请将该列表展开为 Markdown 项目符号子列表,同时保留周围句子的原样:

    输入示例:

    add onions, oil, remaining butter, salt and pepper to pan
    

    输出示例:

    add
    
    - onions
    - oil
    - remaining butter
    - salt
    - pepper
    
    to pan
    

备料:

  • 备料(Mise en place)意为“一切各就其位”,指在开始烹饪前完成所有准备工作,从而让烹饪过程不中断
  • 原则:将准备工作与烹饪过程分开;锅已经烧热时还在切菜的厨师,既会烧焦食物,也会分散注意力
  • 从说明中提取所有准备类步骤,并将其放在紧挨第一段烹饪内容上方的 ## Mise en Place 部分;如果已有 ## Prep 部分,则将其重命名为 ## Mise en Place
  • 准备步骤包括所有可在开始加热或计时前完成的任务:切碎、切丁、剁碎、切片、去皮、称量、浸泡、清洗、醒面、将食材恢复至室温、预热等
  • 如果食谱包含不同的烹饪阶段(例如熬制高汤、制作酱汁、准备馅料),请为每个阶段设置单独的命名部分(例如 ## Broth、## Sauce),而不是将所有内容合并到一个 ## Instructions 部分中——这会在阶段层面应用备料原则,而不仅仅是在食材层面应用
  • 仅将最终组装步骤放在 ## Instructions 中
  • 每个准备步骤都使用普通段落格式(不加项目符号),段落之间以空行分隔——与说明部分的结构相同
  • 如果准备步骤涉及多个组成项目,将操作写成普通段落,并在其下方用项目符号子列表列出这些项目(遵循与说明部分相同的逗号展开规则)
  • 遵循相同的说明格式规则:以小写字母开头,末尾不加句号
  • 从烹饪部分中移除已提取的步骤——不要重复
  • 如果已存在 ## Mise en Place 部分,则将新提取的步骤合并到其中

菜品图片:

  • 查找食谱的主图(例如,来自 og:image 元标签或主要食谱照片)
  • 将其作为内嵌 Markdown 图片放在 frontmatter 块之后、来源 URL 之前:![dish name](image-url)
  • 如果该位置已有图片,则保持原样

营养部分:

  • 如果存在 Nutrition 部分,请将其整个移除

YouTube 链接:

  • 将所有较长的 YouTube URL 转换为短格式:https://youtu.be/VIDEO_ID
    • 示例:https://www.youtube.com/watch?v=abc123 → https://youtu.be/abc123
    • 示例:https://www.youtube.com/watch?v=abc123&t=45s → https://youtu.be/abc123?t=45
  • 已有的短 youtu.be URL 保持原样
  • 如果文件中存在 YouTube 视频 ID 和 pic frontmatter 字段,请在使用缩略图 URL 前验证缩略图:先对 hq720.jpg 运行 curl -s -o /dev/null -w "%{http_code}" URL;如果返回 200,则使用该图片;如果返回 404,则改用 hqdefault.jpg

时间戳:

  • 如果某条说明引用了 [MM:SS] 格式的视频时间戳,并且有对应的 YouTube URL,请将其转换为使用 ?t= 参数的绝对 Markdown 链接,参数值为秒数
    • 示例:视频为 https://youtu.be/Wnt90dkEwFs,[00:17] → [00:17](https://youtu.be/Wnt90dkEwFs?t=17)
    • 示例:[01:05] → ?t=65
  • 链接文本应为 [MM:SS] 格式的时间戳
  • 如果时间戳没有对应的 YouTube URL,则保持为纯文本

输出示例

crack the eggs into a bowl, see [00:05](https://youtu.be/Wnt90dkEwFs?t=5)

whisk until fully combined

pour into a heated, greased pan, see [00:17](https://youtu.be/Wnt90dkEwFs?t=17)

cook until edges begin to set

步骤

  1. 根据第一个参数判断输入模式(URL 还是文件路径)
  2. 据此获取或读取食谱内容;提取作者姓名和主图 URL
  3. 将所有格式规则应用于食谱内容——将说明中的有序列表转换为普通段落;仅对说明行应用说明专属规则(小写、不加句末句号、拆分句子)
  4. 在文件最顶部添加 YAML frontmatter,包含 creator、pic 和 tags
  5. 将主图紧接在 frontmatter 区块之后,再在下一行或数行放置来源 URL,随后空一行——如果已有来源 URL,则替换它;如果食谱来自 YouTube 描述中链接的外部页面,则先放 YouTube URL,再在下一行放食谱页面 URL
  6. 不要添加 # Title 标题——在 Obsidian 中,文件名就是页面标题
  7. 对于文件模式:用清理后的内容覆盖文件并确认成功——不要在聊天中输出完整食谱内容
  8. 对于 URL 模式:在聊天中显示格式化后的食谱,然后立即写入文件,无需询问——使用食谱标题作为文件名,以空格(而非连字符)分隔,并添加 .md 扩展名;如果作者已知,则在文件名前加上作者姓名,避免同名食谱冲突(例如,Fast Easy Recipes crepes.md);保存到知识库中其他食谱文件所在的目录
  9. 写入文件后,检查作者页面是否存在于同一目录;如果不存在,则创建名为 <page name>.md 的空白页面,仅包含 # <page name>(使用上述 frontmatter 规则确定的页面名称)
  10. 写入文件后,委派给 resolve-recipe-ingredient-link 技能以解决未解析的食材 wikilink——参见上文“解决食材 wikilink”格式规则
  11. 对写入的笔记调用 outline-headings 技能——这是必需步骤,不可省略,否则来源 URL、份量、说明文字和主图会位于第一个标题上方,且不会显示在大纲中
  12. 确认 frontmatter 中的 pic 值非空,并且同一张图片直接嵌入在第一个标题下方——没有主图的食谱笔记是不完整的

作者命名空间

根据作者的账号名或个人域名生成命名空间,仅保留小写字母和数字。

在以下三个位置统一使用这个单一标记:creator frontmatter wikilink、tags 条目和空白页面文件名。

Instagram 账号名 chef.sam.black 在所有位置都转换为 chefsamblack——[[chefsamblack]]、标签 chefsamblack、文件 chefsamblack.md。个人域名 halfbakedharvest.com 转换为 halfbakedharvest。

标签不能包含句点、空格或其他分隔符,而且真实姓名可能重复,因此,将账号名合并后得到的标记既能作为标签、wikilink,也能作为文件名,并且不会包含分隔符。

只有在没有账号名或域名时,才使用合并后的真实姓名。

知识库中已有的先例:pantrymama、thesaltymarshmallow、ediblealchemyacademy、allrecipes。

评论

支持 Markdown