| name | defuddle |
| description | Extract clean markdown content from web pages using Defuddle CLI, attach a unified Obsidian frontmatter, auto-translate English content into academic Chinese, download images to the local 附件/ folder (rewriting references to Obsidian ![[file]] embeds), and save to the 网页裁剪/ directory. Always dedupes first — before fetching any URL it greps the 网页裁剪/ directory's frontmatter 来源 field to check whether that page was already clipped; if so it reuses the existing note as-is (never re-fetches or alters it) and only builds a wikilink to it. Use instead of WebFetch when the user provides a URL to read, clip, archive, or to batch-clip the sub-links of an index/trail page — for online documentation, articles, blog posts, GitHub READMEs, or any standard web page. Do NOT use for URLs ending in .md — those are already markdown, use WebFetch directly. |
Defuddle
Use Defuddle CLI to extract clean readable content from web pages, wrap it with a unified frontmatter, and — when the content is English — translate it into academic Chinese before saving. 正文中的远程图片会自动下载到本地 附件/ 目录,并将引用改写为 Obsidian 的 ![[文件名]] 嵌入语法,避免远程图片失效。
If not installed: npm install -g defuddle
默认输出目录
抓取的网页内容统一保存到 网页裁剪/ 目录(不要放进 01-编程笔记/,详见 AGENTS.md 的「网页裁剪约定」)。
- 文件名取网页核心主题(结合 defuddle 提取的
-p title),转为简短中文短语,去掉日期前缀、仓库 owner 前缀、特殊字符 / \ : * ? " < > |。
- 例:标题
tanweai/pua: 你是一个曾经被寄予厚望的 P8 级工程师… → 文件名 pua.md;标题 DietrichGebert/ponytail: Makes your AI agent… → 文件名 ponytail README.md。
- 若标题过长或含大量标点,提炼核心主题后再命名;无法确定时与用户确认。
- 仅当用户明确指定其它路径时,才覆盖默认目录。
标准抓取流程
按顺序执行:抓取前查重 → 取元数据 → 抓正文 → 组装 frontmatter → 判断语言并按需翻译 → 补全代码块语言标识 → 下载图片并改写引用 → 落盘。其中第一步「查重」是前置门控——命中已入库笔记时直接跳过后续全部抓取步骤。
0. 抓取前查重(必做,先于一切抓取动作)
抓取任何 URL 之前,先检查 网页裁剪/ 下是否已有笔记的 来源 字段指向同一 URL。同一个页面常常在多个父页面(索引页 / 目录页)的子链接里反复出现,或之前已单独抓过——重复抓取既浪费请求,又会与既有笔记冲突。先查重,再决定抓不抓。
查重方法:用 Grep 工具在 网页裁剪/ 目录搜索目标 URL。取 URL 去掉协议(http:// / https://)后的域名 + 路径部分作为 pattern,并把 . 转义为 \.(URL 含 ?、(、+ 等正则元字符时也一并转义,避免误匹配)。
Grep pattern: 来源:.*docs\.oracle\.com/javase/tutorial/getStarted/problems/index\.html
path: 网页裁剪/
output_mode: files_with_matches
- 命中的文件即「已入库的同一页面」。读取其 frontmatter 确认
来源 确实一致(URL 规范化:忽略 http/https、末尾斜杠、大小写、#片段 差异)。
- 记下命中笔记的文件名(不含
.md),后续用它建双链。
命中时(已存在)—— 原样保留,绝不重抓、绝不改既有笔记:
- 不要抓取正文、不要翻译、不要改 frontmatter、不要改该笔记正文的任何字符。它当初入库时已定稿,重复抓取不应篡改。
- 尤其不要改它的
分类。已有笔记的 分类 反映它当初的物理归属(如指向 [[入门]])。分类是「树轴」上的唯一物理位置,不该因后来的引用而变动;当前页面需要关联它时,用双链引用表达即可——这恰好落在「图轴」的关联关系上(参见 AGENTS.md 的双轴分类设计)。例如 常见问题及其解决方案 归属 入门,而 Hello World 应用程序 通过 [[常见问题及其解决方案|…]] 双链引用它,两者各得其所。
- 单页抓取场景:告知用户该 URL 已入库为
[[笔记名]],并询问是否仍要覆盖重抓(默认不重抓)。
- 批量子链接场景:该子链接跳过抓取,直接纳入下方「父页面外部链接改双链」清单,用
[[已有笔记名|显示文本]] 建链即可。
未命中时:继续下面的标准抓取流程(第 1 步起)。
1. 提取元数据
defuddle parse <url> -p title
defuddle parse <url> -p description
-p title 返回的标题常含 owner/repo: 副标题 形式(尤其 GitHub),完整保留到 frontmatter 的 标题 字段;文件名另行精简。
2. 抓取正文
defuddle parse <url> --md -o "网页裁剪/<文件名>.md"
Windows / Git Bash 下路径含中文需加引号。
3. 组装统一 frontmatter
在抓取到的文件最顶部插入如下 frontmatter(字段顺序固定,空值字段保留键、值留空):
---
分类:
- "网页裁剪"
- "[[父页面名]]"
标题: "<标题的中文译文>"
描述: "<描述的中文译文>"
来源: "<用户提供的原始 URL,原样保留>"
发布者: "<平台标识-发布者>"
发布时间:
创建时间: "<当前时间,ISO 8601 带时区>"
---
字段规则:
- 分类:第一项固定为
- "网页裁剪"。当本页是从某个父页面(索引页 / 目录页)拉取的子链接时,追加第二项 - "[[父页面名]]",指向父页面笔记。父页面名取父笔记的文件名(不含 .md),如父笔记是 网页裁剪/入门.md 则写 - "[[入门]]"。这样子页面可向上回溯到父节点,形成文档层级树(详见下方「批量抓取子链接」)。无父节点时只保留 - "网页裁剪"。
- 标题:defuddle
-p title 提取的原始标题,翻译为中文。翻译时去掉无信息量的 owner/repo: 前缀,保留核心主题;英文术语按「学术翻译规范」处理(如 LLM 译为「大语言模型(LLM)」)。例:upstash/context7: Context7 Platform -- Up-to-date code documentation for LLMs and AI code editors → Context7 平台 —— 为大语言模型(LLM)和 AI 代码编辑器提供最新代码文档。
- 描述:defuddle
-p description 提取的内容;为空则取标题的副标题部分。无论来源为何种语言,一律翻译为中文,并遵循与正文相同的术语规范。
- 来源:用户传入的原始 URL,即使指向
blob/main/README.zh-CN.md 也原样保留,便于追溯。
- 发布者:
<平台>-<发布者> 形式。
- GitHub:取 owner,如
GitHub-tanweai、GitHub-DietrichGebert;owner 无法判断时写 GitHub-。
- 其他平台:域名短名 + 发布者,如
知乎-张三、Medium-johndoe;无法判断时用 <域名短名>-。
- 发布时间:defuddle 通常无法获取,留空。若页面明确标注发布日期且可解析,则填
YYYY-MM-DD。
- 创建时间:抓取当下的本机时间,ISO 8601 带时区。Git Bash 下用
date -Iseconds 生成(形如 2026-06-26T11:23:37+08:00)。
4. 判断语言并按需翻译
读取抓取到的正文,判断主体语言:
- 若已是中文(含中英混排但主体为中文,如 GitHub 的
README.zh-CN.md):保留原文,仅做 frontmatter 组装即可,不重新翻译正文。但 frontmatter 的 标题、描述 若来自 defuddle 元数据且为英文,仍需翻译为中文。
- 若主体为英文:按下方「学术翻译规范」整篇翻译为中文,用译文替换正文(frontmatter 之后的全部内容)。同时
标题、描述 也译为中文。
不要局部翻译、不要逐句直译——先通读全文理解语境,再产出连贯译文,最后替换落盘。
5. 补全代码块语言标识(默认执行)
翻译完成、最终正文确定后,运行代码语言检测脚本:扫描正文里无语言标识的代码围栏(裸 ```),根据代码内容启发式判定语言并补上标识(如 ```java、```bash),使 Obsidian 能正确语法高亮。
node ".claude/skills/defuddle/scripts/detect-code-languages.js" "网页裁剪/<文件名>.md"
要点:
- 脚本位于本 skill 目录下的
scripts/detect-code-languages.js,就地修改传入文件:只改写无标识的围栏开头行,代码内容与已有标识不动。已有标识(如 ```yaml)原样保留。
- 保证不出现裸围栏:脚本对每个无标识的非空代码块都给出一个语言标识——命中某条规则就标具体语言(如
java、bash);命不中就标 text(等宽、不高亮,适合程序输出、编译器报错、纯片段这类「不是任何语言代码」的内容)。只有真正空的代码块才保留原样。这是设计目标,不要因为某块是「输出」就跳过本步——让脚本统一标成 text 才能避免裸 ```。
- 覆盖 java/bash/batch/python/js/ts/go/rust/cpp/csharp/sql/json/yaml/xml/html/css 等 20+ 种语言,基于关键字与结构特征判断。Java 规则覆盖完整类定义(
public class、System.out、import java.)以及纯类型化变量声明、数组声明(int[] a; / float a[];)、数组元素赋值与 new int[10] 等,避免教程示例里的短代码片段被漏判。
- 可一次传多个文件:
node ".../detect-code-languages.js" "网页裁剪/a.md" "网页裁剪/b.md"。
- 在翻译完成、图片下载之前运行。翻译时原文里的代码块(含裸围栏)原样保留到译文,由本步骤统一补标识。
- 仅当用户明确说「不要检测代码语言」时才跳过;否则一律执行。
6. 下载图片到本地并改写引用(默认执行)
翻译完成、最终正文确定后,默认对落盘文件运行图片本地化脚本:扫描正文中的远程图片(Markdown  与 HTML <img src="https://…">),下载到 附件/ 目录,并把引用改写为 Obsidian 嵌入语法 ![[文件名]]。
node ".claude/skills/defuddle/scripts/download-images.js" "网页裁剪/<文件名>.md" --prefix "<可选前缀>"
要点:
- 脚本位于本 skill 目录下的
scripts/download-images.js,路径相对仓库根目录。它就地修改传入的 Markdown 文件:下载成功的图片引用改写为 ![[文件名]],下载失败的保留原远程链接并在控制台告警。
- 在翻译完成、文件最终落盘后运行——否则脚本改写后的
![[]] 引用会被后续的整段译文替换覆盖掉。
- 命名:基于内容 hash——文件名为图片内容的 sha256 前 16 位 + 扩展名,形如
1c55b4dff2677827.svg。定长干净,不会因 URL 过长而产出超长文件名(早期曾把整段 URL 编码进文件名);同内容图片(不同 URL)自动命中同一文件名并直接复用,天然去重、不重复写入。
--prefix(可选,通常不需要):加在 hash 前。hash 已保证唯一,仅当想为某篇笔记的图片做人工分组时才用,如 --prefix context7-。
- 脚本对同一 URL 只下载一次;按 URL 末段 / Content-Type 推断扩展名(兜底
png);下载失败(403/404/超时)保留原远程链接,不中断流程。
- 图片压缩(默认开启):下载后自动用
sharp 压缩——PNG/JPEG/WebP/AVIF/BMP 转为 WebP(质量 82),体积通常降 50-90%;GIF 与 SVG 保留原格式不动。压缩在内存完成,直接以最终名(<hash>.webp)落盘并写入引用。实测 1.7MB 的 JPEG → 139KB WebP(-92%)。压缩依赖 sharp,已在 scripts/ 下安装(node_modules 被 gitignore)。如需关闭:加 --no-compress;如仅压缩大图:--threshold 204800(只压 >200KB 的)。
- 仅当用户明确说「不要下载图片」「保留远程图片」时才跳过此步;否则一律执行。
翻译时,正文里出现的  远程图片占位照原样保留(URL 不改动),由本步骤统一改写。不要在翻译阶段手工改写图片链接。
批量抓取子链接与父子关系
用户常提供一个索引页 / 目录页,要求把页面中的若干子链接「抓取到本地,并把外部链接改成双链」(如:抓取「入门」学习路径下的 4 个子课程)。这种场景下需要额外维护父子层级关系,使文档自下而上可回溯。
文档树可以任意深度(常 2-4 层)。抓取时逐级展开:先抓本批直接子节点,凡是某个子节点页面内又含「下一层子链接」的,视用户需求对该子节点按同一套流程再展开一层(它此时成为下一层的父节点),最终构建成一棵干净的父子树。关键:每个父页面只列自己的直接子节点,不跨级罗列孙节点——孙节点关系靠各自相邻层级的「父→子」逐层双链表达,打开任一节点即可向上回溯到根(详见第 2.2 节)。
核心目标是构建一棵干净的父子 tree 结构:父子关系只在「子页面 frontmatter 分类」和「父页面正文双链」两个通道表达,兄弟子页面之间不互建双链(详见第 2.1 节),避免同层节点在关系图里两两连成网。
1. 抓取每个子页面
对索引页中列出的每个子链接,先按上方「第 0 步:抓取前查重」检查 来源 是否已入库:
- 命中(已存在):跳过抓取,原样保留既有笔记不改(不改
分类、不重译)。只把该子链接记入「改双链清单」,用既有笔记名建双链。这是最常见的情形——同一子页常被多个父页面引用,例如 常见问题及其解决方案 已归属 入门,当 Hello World 应用程序 也引用它时,只需建双链,不重复抓取、不改动它原本的 分类: - "[[入门]]"。
- 未命中:执行「标准抓取流程」(第 1 步取元数据 → … → 第 6 步下载图片),子页面命名与父页面同级放在
网页裁剪/ 下(不要建子目录),文件名取网页核心主题转简短中文短语。新建子页面的 frontmatter 中,分类 在 - "网页裁剪" 之后追加一项指向父页面的双链 - "[[父页面名]]"(见上方「字段规则」)。
关键判断:抓取前查重保护的是「已存在的物理笔记」。命中的笔记原封不动,需要建立关系时一律走双链引用,不通过改 分类 来「归并」——分类管物理归属,双链管引用关系,两者不可混用。
2. 父子双链匹配
子页面的 分类 中的父节点双链,其 [[父页面名]] 必须与父笔记的文件名完全一致(不含扩展名)。
示例——父笔记是 网页裁剪/入门.md,则其 4 个子页面的 frontmatter 写:
分类:
- "网页裁剪"
- "[[入门]]"
这样在 Obsidian 的「反向链接(Backlinks)」面板中,打开「入门」笔记即可看到所有归属它的子页面,形成从下而上的索引。
2.1 子页面之间禁止互引双链(保持 tree 结构干净)
父子关系是唯一需要建立的层级关系,且只通过两个通道表达:子页面 frontmatter 的 分类 双链(自下而上),以及父页面正文中指向各子页面的双链(自上而下)。除此之外,子页面之间不要互建任何双链,避免在关系图里把同一层节点两两连成网。
具体地,抓取正文时常见但必须剔除的两类子节点互引:
- 顶部的「兄弟页/同章节」列表:原始网页常在正文顶部罗列一组同级标题链接(如「什么是对象 / 什么是类 / 什么是继承 / 什么是接口 / 什么是包」互相列出)。这些链接指向的都是同一批兄弟子页面,属于互引,一律删除整段列表。
- 底部的「上一页 / 下一页」导航:指向前后兄弟页的
[[兄弟页|...]] 双链也是子节点互引,删除;底部分隔线 --- 一并去掉。该走线性阅读顺序时,由父节点正文中的列表顺序表达,无需在子页面里重复。
保留不删的双链:
- 子页面 frontmatter
分类 中指向父节点的双链(层级归属,必须保留)。
- 跨主题引用——即指向不属于本组兄弟的其它笔记的双链(例如某子页面正文中提到后续课程「类和对象」「接口和继承」)。这类双链表达的是跨分类的知识关联,不是兄弟互引,照常建立、保留。
判断口径很简单:这条双链指向的是与本页同一个父节点下的另一个子页面吗? 如果是 → 互引,删掉。如果指向父节点本身(frontmatter)或指向本组之外的主题(跨主题引用)→ 保留。
设计意图:关系图保持干净的「父子树」形态——兄弟关系只在父节点那一处集中表达。打开任一子页面时,它的反向链接面板应当只显示父节点这一个入口,而不是被一堆兄弟节点填满。
2.2 多级树(任意深度)
文档树常不止一层(如 Oracle Java 教程的树深达 4-5 层)。处理多级树时,把单层规则套用到每一层即可,但有三点必须牢记:
① 父页面只列直接子节点,绝不跨级罗列孙节点
这是多级树最关键的规则,也是用户反复强调的标准。每个父页面(含「既是子又是父」的中转节点)的「子页面」清单只放直接子节点——孙节点、曾孙节点一律不出现在本页清单里,它们只出现在各自直接父节点的清单中。
经全目录 Grep 验证:现有所有父页面的子页面清单都是单层平级,0 处缩进嵌套——这就是目标范式。不要为了「在一个页面看到全局」而用缩进把孙节点塞进父页面;全局结构靠 Obsidian 反向链接面板逐层导航,而非在单页堆砌。
② 「既是子又是父」的中转节点
多级树里大量节点既是上一层的子、又是下一层的父(如「类」既是「类和对象」的子,又是「声明类 / 定义方法 / 构造方法…」5 篇的父)。这类节点的两层关系各走各的通道,互不干扰:
- 作为子:frontmatter 的
分类 指向上一级父节点(如 分类: - "网页裁剪" - "[[类和对象]]")。这是它的唯一物理归属。
- 作为父:在正文末尾加一节
## 子页面,列出它自己的直接子节点(如「声明类」「定义方法」等)。
两层关系通过不同通道表达,同一节点既挂父链、又有子页面清单,完全正常。
③ 逐级抓取流程
抓取一棵多级树时,按层级从上到下逐级展开:
- 抓取本批(当前父节点列出的)直接子页面——遵循第 1 节「查重→抓取→frontmatter 父链」流程。
- 处理完直接子页面后,对每个子页面判断它「有没有自己的下一层子链接」:
- 抓取的子页面正文里如果罗列了它自己的子链接(与父节点当初的情形一样),就把它当作下一层的「父节点」,对它的子链接再走一遍本套「批量抓取」流程。
- 该子页面此时需要:① frontmatter 指向上一级(已在第 1 步完成);② 正文加
## 子页面 清单列自己的直接子节点。
- 每展开一层都重复同样的动作,直到叶子节点(不再含子链接的页面)。
- 各层独立处理:第 N 层的父页面只列第 N+1 层的直接子节点,第 N+2 层及更深只出现在第 N+1 层的清单里。
抓取前查重在多级树里尤其重要——同一子页面可能被树的不同位置引用,命中既有笔记就跳过、不重抓、不改它的 分类,只在当前父页面建双链。
2.3 子页面清单格式(新抓父页面的统一规范)
父页面(含「既是子又是父」的中转节点)正文里的「子页面」清单,新抓取的一律用 ## 子页面 小节 + 无序列表,与已整理过的父页面(如 控制流语句、类和对象 子树)对齐:
## 子页面
- [[子页面名|显示文本]] —— 一句话中文说明。
- [[子页面名|显示文本]] —— 一句话中文说明。
- 显示文本:沿用网页原链接文本(如「if-then 和 if-then-else 语句(The if-then and if-then-else Statements)」),保留中英对照便于追溯。
- 一句话说明:概括该子页主题,便于在父页面快速浏览各子页内容。写不写都行,建议写。
- 平级不嵌套:列表项一律单层
- ,不缩进、不嵌套孙节点(孙节点见各自的直接父页面,见第 2.2 节①)。
历史既有父页面若用 inline 段落链接等其它格式(如 入门、语言基础),不强制回改——遵循「不擅自改既有笔记」原则。仅对新抓取的父页面执行本格式规范。
3. 父页面的外部链接改双链
子页面全部处理完毕(新抓取的入库 + 查重命中保留的既有笔记)后,回到父页面笔记,把指向这些子页面的外部链接(Markdown [文本](https://…))改为 Obsidian 双链 [[子页面名|显示文本]]:
- 子页面名取对应笔记的文件名(不含
.md)——无论该笔记是本批新抓的还是查重命中的既有笔记,统一用它现有的文件名建链。
- 显示文本沿用原链接文本,如原为
[入门(Getting Started)](https://...) → [[入门|入门(Getting Started)]]。
- 仅改本批清单中的子链接(新抓取入库的 + 查重命中保留的)。父页面里若有更下一层的链接(孙链接):若本次任务要把它们也展开为下一级子页面(见第 2.2 节逐级抓取),它们会在下一层处理时改双链;若本次不展开,则保持原外部链接不变,便于后续追溯。
- 父页面里指向「其它平台 / 资源」的链接(非本批子页面)也不动,只替换本批清单中的子页面链接。
示例
场景 A —— 两个子页都未抓取过(全部新抓):
父笔记 网页裁剪/入门.md 原有:
- [深入剖析「Hello World!」](https://docs.oracle.com/javase/tutorial/getStarted/application/index.html) —— ...
- [常见问题及其解决方案](https://docs.oracle.com/javase/tutorial/getStarted/problems/index.html) —— ...
查重两个 URL 均未命中,分别新抓为 深入剖析 Hello World.md、常见问题及其解决方案.md(frontmatter 分类 带 - "[[入门]]")。父笔记改为:
- [[深入剖析 Hello World|深入剖析「Hello World!」]] —— ...
- [[常见问题及其解决方案|常见问题及其解决方案]] —— ...
场景 B —— 子页已被其它父页面抓取过(查重命中,只建双链):
常见问题及其解决方案 已归属 入门(分类: - "[[入门]]")。现在抓取 Hello World 应用程序.md,它也引用了同一个 problems 页面。查重命中既有笔记 常见问题及其解决方案.md:
- 不重抓、不改它的
分类(它仍是 [[入门]]);
- 该子链接直接记入改双链清单。
Hello World 应用程序.md 中对应行改为双链即可:
[[常见问题及其解决方案|常见问题及其解决方案(Common Problems (and Their Solutions))]] —— ...
这样 problems 笔记物理上只存在一份、归属不变,却能被 入门 和 Hello World 应用程序 两个父页面同时引用——靠的就是双链,而非重复抓取或篡改分类。
场景 C —— 多级树(含中转节点,逐级展开):
抓取 类和对象 的完整子树(3 层:类和对象 → 类/对象/类的更多内容/嵌套类/枚举类型 → 各自的子页面)。
第 1 步:类和对象 列出 5 个直接子节点。查重均未命中,新抓为 类.md、对象.md、类的更多内容.md、嵌套类.md、枚举类型.md,frontmatter 各写 分类: - "网页裁剪" - "[[类和对象]]"。类和对象 的「子页面」清单只列这 5 个直接子节点,不列孙节点:
## 子页面
- [[类|类(Classes)]] —— 类的结构与声明(字段、方法、构造方法)。
- [[对象|对象(Objects)]] —— 创建和使用对象,以及对象的生命周期。
- [[类的更多内容|类的更多内容(More on Classes)]] —— 从方法返回值、this 关键字、类成员、访问控制。
- [[嵌套类|嵌套类(Nested Classes)]] —— 静态嵌套类、内部类、局部类、匿名类、Lambda 表达式。
- [[枚举类型|枚举类型(Enum Types)]] —— 定义和使用常量集合的特殊类。
第 2 步:对每个一级子页面判断「有没有下一层子链接」。类 含 5 个子链接(声明类、声明成员变量、定义方法、为类提供构造方法、向方法或构造方法传递信息),于是把 类 当作下一层的父节点展开——抓取这 5 个孙页面,frontmatter 写 分类: - "网页裁剪" - "[[类]]",并在 类.md 正文加 ## 子页面 清单列出它们:
## 子页面
- [[声明类|声明类(Declaring Classes)]] —— ...
- [[声明成员变量|声明成员变量(Declaring Member Variables)]] —— ...
- [[定义方法|定义方法(Defining Methods)]] —— ...
- [[为类提供构造方法|为类提供构造方法(Providing Constructors for Your Classes)]] —— ...
- [[向方法或构造方法传递信息|向方法或构造方法传递信息(Passing Information to a Method or a Constructor)]] —— ...
关键:声明类 等 5 个孙节点只出现在 类.md 的清单里,绝不出现在 类和对象.md 的清单里。类和对象 只看到「类」,打开「类」才看到它的子,逐层向下。这样整棵树每一处都只表达相邻层级的父子关系,关系图干净不缠。
同理对 对象、类的更多内容、嵌套类、枚举类型 各自展开它们的下一层,各自加自己的 ## 子页面 清单。
学术翻译规范(英文 → 中文)
当正文主体为英文时,按以下规范翻译。翻译完成后用译文整体替换正文。frontmatter 的 标题、描述 字段同样按本规范翻译为中文(来源、发布者、创建时间 等非文本字段保持原样)。
你是一位专业的中文母语译者,擅长学术内容翻译,能够流畅地将文本翻译成中文。
翻译规则
-
只输出译文,不添加解释、注释或额外内容;不要在文末附「以下是翻译:」之类的话。
-
保留专业术语:学科术语、专有名词、技术黑话原则上译为中文,并在首次出现时用「中文(原文)」格式标注原文,例如 "Skills" 译为「技能(Skills)」、「agent」译为「智能体(agent)」;后续重复出现时可只用中文。判断某词是否需要保留原文,以「读者准确理解技术含义」为准——无公认中文译法的术语、产品名、命令名直接保留英文。
-
HTML 标签:若原文含 HTML 标签(<a>、<sub>、<code> 等),在译文中保持标签结构,把标签放到译文里对应的位置,确保渲染正确、语句通顺。
-
引用与参考文献:引用、参考文献、书目格式原样保留,不翻译作者名、论文标题、URL。
-
保持正式学术语调:语域、语气、复杂度对齐原文,不口语化、不简化。
-
公式与科学记数法:数学公式、等式、科学记数法准确保留,不改动符号。
-
术语一致性:贯穿全文,同一术语的译法保持一致。
-
Markdown 结构:标题层级、列表、代码块(含语言标注)、表格、引用块、链接 URL 原样保留;只翻译可读文本(代码块的语言标识如 ```bash 不翻译)。原文若代码围栏无语言标识(裸 ```),翻译时保持无标识原样——语言由第 5 步的检测脚本统一补全,不要手工猜测添加。
-
代码块内的注释:代码块中的注释文本也需翻译为中文,包括块注释(/* … */、/** … */、<!-- … -->、''' … ''')、行注释(// …、# …、-- …、REM …)等各语言形式。翻译时只改注释里的可读文字,注释定界符与代码本身保持不变(语句、关键字、字符串字面量、变量名一概不动)。保持原有缩进与对齐。
示例——翻译前:
class HelloWorldApp {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
翻译后(仅注释变中文,代码不变):
class HelloWorldApp {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
要点:行内注释(如 // Display the string.)紧跟代码时,保留注释定界符 // 与前面的代码,只把注释文字译成中文;多行文档注释每行的 * 前缀保留,换行时按中文语义断句。若注释本身已是中文或为无意义的占位(如 // TODO),原样保留。
-
链接文本:链接的显示文本译为中文,URL 不改动;图片 alt 文本可译,但图片引用本身()照原样保留,不要手工改成 ![[]]——图片本地化由第 5 步的脚本统一处理。
常见技术术语对照(参考,非强制)
| English | 中文 |
|---|
| agent | 智能体(agent) |
| skill | 技能(skill) |
| prompt | 提示词(prompt) |
| LLM | 大语言模型(LLM) |
| token | 词元(token) |
| hallucination | 幻觉(hallucination) |
| MCP | 上下文协议(MCP) |
| benchmark | 基准测试(benchmark) |
| middleware | 中间件(middleware) |
| API | API(保留原文) |
遇到对照表外的术语,按规则 2 处理;产品名、命令名(如 defuddle、ctx7、Cursor)直接保留英文。
历史附件迁移(rename-to-hash.js)
早期版本曾用 URL 末段或整段 URL 编码命名附件,产出过超长 / 混乱的文件名(如 awesome-design-md-68747470733a2f2f….svg)。scripts/rename-to-hash.js 把 附件/ 下所有图片批量改名为 <sha256前16位>.<ext>,并同步更新全仓库 .md 中的 ![[引用]](含 ![[名|尺寸]] 变体):
- 同内容图片(hash 相同)去重:保留一份,删除重复,引用统一指向保留文件。
- hash 碰撞(前 16 位相同但内容不同,极罕见)追加序号,绝不误删。
- 已是 hash 命名(16 位 hex stem)的文件跳过,幂等可重复运行。
- 非图片文件不动;GIF/SVG 等保留原扩展名,只改名。
node .claude/skills/defuddle/scripts/rename-to-hash.js --dry-run
node .claude/skills/defuddle/scripts/rename-to-hash.js
建议先 --dry-run 看映射与「去重删除 / 碰撞」计数,确认无误再实跑。迁移前确保已 git 提交,便于回退。
Usage
仅查看内容不保存(临时阅读):
defuddle parse <url> --md
提取特定元数据:
defuddle parse <url> -p title
defuddle parse <url> -p description
defuddle parse <url> -p domain
Output formats
| Flag | Format |
|---|
--md | Markdown (default choice) |
--json | JSON with both HTML and markdown |
| (none) | HTML |
-p <name> | Specific metadata property |
完整示例
用户输入:defuddle https://github.com/upstash/context7
执行:
defuddle parse "https://github.com/upstash/context7" -p title
defuddle parse "https://github.com/upstash/context7" -p description
defuddle parse "https://github.com/upstash/context7" --md -o "网页裁剪/Context7 README.md"
date -Iseconds
读取落盘文件 → 顶部插入 frontmatter → 判定正文为英文 → 整篇按学术规范翻译 → 用译文替换正文 → 补全代码块语言标识 → 下载图片到 附件/ → 保存:
node ".claude/skills/defuddle/scripts/detect-code-languages.js" "网页裁剪/Context7 README.md"
node ".claude/skills/defuddle/scripts/download-images.js" "网页裁剪/Context7 README.md" --prefix "context7-"
最终文件头部形如:
---
分类:
- "网页裁剪"
标题: "Context7 平台 —— 为大语言模型(LLM)和 AI 代码编辑器提供最新代码文档"
描述: "为大语言模型(LLM)和 AI 代码编辑器提供最新代码文档 - upstash/context7"
来源: "https://github.com/upstash/context7"
发布者: "GitHub-upstash"
发布时间:
创建时间: "2026-06-26T11:23:37+08:00"
---
若本页是从某个父页面拉取的子链接,分类 在 - "网页裁剪" 下追加一项 - "[[父页面名]]"(详见上方「批量抓取子链接与父子关系」)。