| name | fix-broken-links |
| description | 扫描 Obsidian vault 中所有失效的 [[双链]](目标文件不存在),呈现死链清单,提供删除链接、创建占位笔记、替换为正确链接三种修复方式。触发时机:用户发现链接失效、手动删除文件后需清理、定期 vault 健康检查时。 |
Skill: fix-broken-links(失效双链检测与修复)
触发时机
用户发现链接失效、手动删除了文件需要清理残留链接、或定期 vault 健康检查时。
输入
- 扫描范围(默认全 vault,用户可限定目录)
- 修复策略(用户逐条选择)
执行步骤
Step 1: 确认扫描范围
- 用户已指定范围 → 直接使用
- 未指定 → 询问:"扫描整个 vault 还是指定目录?" 默认建议全库扫描
- 若指定了目录,确认存在(
ls 检查)
Step 2: 收集所有 wikilink 目标
使用 Grep 在扫描范围内搜索所有 [[...]] 模式:
Grep pattern="\[\[.*?\]\]" path="{scope}" output_mode="content" -n
对每条匹配结果,提取并记录:
| 字段 | 提取方式 |
|---|
| 源文件路径 | Grep 返回的文件名 |
| 行号 | Grep 返回的行号 |
| 原始链接文本 | 完整的 [[...]] 部分 |
| 链接目标 | 去掉别名 |...、去掉锚点 #...、去掉嵌入前缀 ! |
| 是否有别名 | 链接中含 | 则为 true |
| 是否有锚点 | 链接中含 # 则为 true |
去重: 同一链接目标(标准化后的小写文件名)只验证一次,但保留每条引用记录。
Step 3: 验证目标是否存在
对每个唯一的链接目标,在 vault 中搜索对应的 .md 文件:
3a. 精确匹配:
- 搜索路径:
{VAULT_PATH}/**/{target}.md
- 使用 Glob 或
find 搜索
3b. 若精确匹配失败,模糊搜索:
- 若目标含路径(如
subfolder/target),在对应子目录下搜索
- 使用 Glob
**/*{target}*.md 宽松匹配
- 若找到 1 个候选 → 标记为 "可修复"(文件可能被重命名)
- 若找到多个候选 → 标记为 "模糊匹配",列出所有候选
- 若 0 个候选 → 标记为 "目标不存在"
状态标记:
✅ 存在 — 目标文件找到,链接有效
❌ 目标不存在 — 完全找不到对应文件
⚠️ 模糊匹配 — 找到 N 个可能的目标
🔗 有效(忽略) — 存在,跳过
Step 4: 呈现失效链接清单
只展示状态为 ❌ 和 ⚠️ 的条目:
## 失效链接报告
- 扫描范围:`{scope}`
- 总链接数:{total} 处
- 失效:{broken} 处(涉及 {N} 个文件)
- 模糊匹配:{ambiguous} 处
| # | 源文件 | 行号 | 失效链接 | 状态 |
|---|--------|------|---------|------|
| 1 | Notes/A.md | L12 | [[Deleted-Note]] | ❌ 目标不存在 |
| 2 | Notes/B.md | L5 | [[old/OldNote]] | ❌ 目标不存在 |
| 3 | Notes/C.md | L8 | [[Renamed]] | ⚠️ 找到 2 个候选 |
若没有失效链接 → 告知用户"所有链接有效",结束。
Step 5: 用户选择修复策略
呈现每条失效链接,逐一或批量询问修复方式:
四种策略:
delete — 移除死链(从源文件的该行删除链接语法)
stub — 创建占位笔记 {target}.md,内容为 > 待补充,让链接变有效
replace:新目标 — 替换链接为 [[新目标]](用户指定正确的目标笔记名)
skip — 暂时保留,不做处理
批量选择语法示例:
1-3 delete ← 删除 #1 到 #3 的死链
4 stub ← 为 #4 创建占位笔记
5 replace:Hooks ← 将 #5 的链接替换为 [[Hooks]]
6-8 skip ← 跳过 #6 到 #8
对模糊匹配(⚠️)的条目,先展示候选列表让用户确认:
#3 [[Renamed]] 可能指向:
a) Notes/React/RenamedNote.md
b) Notes/Vue/RenamedGuide.md
请选择正确的目标(a/b),或手动输入。
Step 6: 执行修复
按用户选择的策略逐条处理:
delete 策略:
- 读取源文件当前内容(Read)
- 若链接独占一行(如
- [[BrokenLink]])→ 使用 Edit 删除整行
- 若链接嵌入句子中(如
详见 [[BrokenLink]] 了解更多)→ 仅移除 [[BrokenLink]],保留周围文字
- 若链接带别名(如
[[BrokenLink\|旧文档]])→ 移除整个链接语法,保留别名文本
- 删除后若产生连续空行 → 合并为单个空行
stub 策略:
replace 策略:
- 使用 Edit 将
[[旧目标]] 替换为 [[新目标]]
- 若原链接有别名
[[旧目标\|别名]] → 保留别名:[[新目标\|别名]]
- 若原链接有锚点
[[旧目标#heading]] → 保留锚点(前提新目标有对应标题,否则仅替换目标名)
每修复一条报告:[N/M] {策略}: {源文件} — 完成
Step 7: 报告结果
## 修复完成
| 策略 | 数量 |
|------|------|
| 🗑️ 删除死链 | N |
| 📝 创建占位笔记 | M |
| 🔗 替换链接 | P |
| ⏭️ 跳过 | Q |
| **合计处理** | **{total}** |
所有失效链接已处理。
产出
- 失效链接全部修复(删除/替换/创建占位)
- vault 链接完整性恢复
禁止行为
- 不要修改未被确认为失效的链接
- 不要创建与已有笔记同名的占位笔记
- 不要未经用户确认就批量执行修复
- 不要修改链接以外的任何文件内容
- 不要在用户未选择策略时替用户做决定
- 不要跳过模糊匹配的确认步骤