---
name: ob-tidy
description: "Obsidian 知识库健康检查与维护。当用户想要检查知识库健康度、修复断链、发现重复概念、整理知识库时触发此 skill。"
---

<role>Obsidian 知识库维护助手，负责检测并修复知识库中的标题、结构、链接、内容、元数据等各类健康问题。</role>
<purpose>保持知识库健康，确保标题清晰、结构规范、链接有效、概念无重复、元数据完整，让每篇文章对读者友好。</purpose>
<trigger>

```text
触发词：
- 检查知识库
- 健康检查
- 知识库 lint
- ob-tidy
- 整理知识库
- 修复断链
- 去重
- 标题检查
- 知识库体检

示例：
- "ob-tidy"
- "检查一下知识库健康状况"
- "帮我整理一下知识库"
- "知识库有没有断链"
- "检查标题规范"
```

</trigger>
<gsd:workflow xmlns:gsd="urn:gsd:workflow">
  <gsd:meta>requires=OBSIDIAN_REPO; focus=lint,fix,health</gsd:meta>
  <gsd:goal>检测知识库健康问题，生成报告并提供修复建议或自动修复。</gsd:goal>
  <gsd:phase>获取 OBSIDIAN_REPO，扫描 wiki/ 目录下所有文件。</gsd:phase>
  <gsd:phase>运行十二项健康检查：标题清晰度、文章结构、断裂链接、孤立文章、低链接文章、重复概念、缺失概念、长文章、缺失索引、Frontmatter 规范、标签体系、内容新鲜度。</gsd:phase>
  <gsd:phase>生成健康报告，展示问题清单（按优先级排序）。</gsd:phase>
  <gsd:phase>对可自动修复的问题提供修复选项，等待用户确认后执行。</gsd:phase>
</gsd:workflow>

# Obsidian 知识库维护 (ob-tidy)

检测并修复知识库健康问题，生成健康报告。共 12 项检查，覆盖**可读性、链接引用、内容质量、组织规范**四个维度。

## 配置检查

1. 检查全局 CLAUDE.md 中 `OBSIDIAN_REPO` 配置变量
2. 如果未定义，使用 AskUserQuestion 询问用户
3. 检查 `$OBSIDIAN_REPO/wiki/` 目录是否存在
4. 如果不存在，提示用户先运行 ob-index 初始化

## 执行流程

### 第一步：扫描知识库

扫描 `$OBSIDIAN_REPO/wiki/` 下所有 `.md` 文件（排除 `index.md`、`log.md`）：

1. 读取每个文件的 frontmatter（tags、type、updated_at、created_at）
2. 提取所有 `[[wikilinks]]` 引用
3. 统计文件字数
4. 提取标题层级结构（H1-H4）
5. 收集所有标签
6. 读取 `$OBSIDIAN_REPO/wiki/index.md` 获取索引条目

### 第二步：运行十二项检查

---

#### 维度一：可读性（面向人类阅读体验）

##### 检查 1：标题清晰度

**目标**：确保每篇文章标题能让人一眼看出在讲什么。

**标题命名规范**（根据文章类型）：

| 文章类型 | 标题格式 | 合格示例 | 不合格示例 |
|---------|---------|---------|-----------|
| 概念类（type=concept） | `中文名 (英文名)` | `注意力机制 (Attention Mechanism)` | `attention`、`关于注意力的笔记` |
| 教程类（type=tutorial） | `如何在 XXX 中 YYY` | `如何在 Tauri 中创建系统托盘` | `tauri 教程`、`系统托盘` |
| 问题解决类（type=troubleshooting） | `如何解决 XXX 问题` | `如何解决 React 内存泄漏问题` | `react 问题`、`bug 记录` |
| 学习笔记类（type=learning） | `从 XXX 学习/了解 YYY` | `从 Tauri 源码学习 Rust 异步编程` | `学习笔记`、`tauri` |
| 参考类（type=reference） | `XXX 参考指南/速查表` | `Vite 配置参考指南` | `vite`、`配置` |

**检测规则**：

```
对每个 wiki 文件：
  1. 读取 H1 标题（或文件名作为标题）
  2. 检查以下问题：
     a. 标题过短（< 4 个有效字符）→ 标记为"标题过短"
     b. 标题过长（> 60 个字符）→ 标记为"标题过长"
     c. 标题含通用词（"笔记"、"总结"、"TODO"、"未命名"、"misc"）→ 标记为"标题含糊"
     d. 标题为纯英文且 article type=concept → 建议加中文名
     e. 标题为纯中文且涉及专有名词 → 建议加英文名
     f. 标题符合文件名格式（含连字符、全小写）→ 建议改为自然语言标题
     g. 根据 frontmatter.type 检查标题是否符合对应格式 → 不符合则建议调整
```

**修复建议**：
- 根据文章内容生成建议标题
- 展示当前标题 vs 建议标题对比
- 用户确认后批量更新

---

##### 检查 2：文章结构

**目标**：确保每篇文章有清晰的内部结构，方便阅读和快速定位。

**结构规范**：

| 文章长度 | 最低要求 | 推荐结构 |
|---------|---------|---------|
| < 100 字 | 无强制要求 | 一段话即可 |
| 100-300 字 | 至少 1 个 H2 分段 | 引言 → 核心内容 → 小结 |
| 300-500 字 | 至少 2 个 H2 分段 | 引言 → 2-3 个主题段落 → 小结 |
| > 500 字 | 至少 3 个 H2 + 内部目录 | 目录 → 引言 → 多个主题 → 总结 → 参考链接 |

**检测规则**：

```
对每个 wiki 文件：
  1. 提取所有标题层级（H1-H4）
  2. 检查以下问题：
     a. 无标题（纯文本无结构）→ 标记为"无结构"
     b. 标题层级跳跃（如 H1 后直接 H3，跳过 H2）→ 标记为"层级跳跃"
     c. 文章 > 200 字但无 H2 → 标记为"缺少分段"
     d. 文章 > 500 字但无内部目录 → 标记为"缺少目录"
     e. 缺少引言段落（第一段未说明文章目标/适用场景）→ 标记为"缺少引言"
     f. 缺少总结段落（最后一段没有收束性内容）→ 标记为"缺少总结"
```

**修复建议**：
- 为无结构文章生成建议的标题层级
- 为长文章生成内部目录
- 建议引言/总结的模板内容

---

#### 维度二：链接与引用

##### 检查 3：断裂链接

**检测**：收集所有文章中的 `[[xxx]]` 引用，检查对应文件是否存在。

```
对每个 wiki 文件：
  提取所有 [[wikilinks]]
  对每个 link：
    检查 wiki/ 下是否存在对应文件
    不存在 → 记录为断裂链接
```

**修复建议**：
- 创建占位概念文章（含 stub 标记）
- 或移除断裂链接（如果概念不再相关）

---

##### 检查 4：孤立文章

**检测**：统计每篇文章的入链数（被其他文章引用的次数）。入链为 0 的文章为孤立文章。

**标准**：孤立文章比例应 < 10%

**修复建议**：
- 检查是否有相关文章应该链接到它
- 考虑是否应该从索引中补充引用

---

##### 检查 5：低链接文章

**检测**：统计每篇文章的入链数。入链 < 2 的文章为低链接文章。

**标准**：
- 入链 = 0 → 孤立文章（检查 4 已覆盖）
- 入链 = 1 → 低链接，建议补充关联
- 入链 ≥ 2 → 正常

**修复建议**：
- 列出可能与该文章相关的其他文章
- 建议在相关文章中添加引用链接

---

##### 检查 6：缺失概念

**检测**：扫描所有来源文章和概念文章中的 `[[concepts/xxx]]` 引用，检查对应概念文章是否存在。

**修复建议**：列出待创建的概念文章清单。

---

#### 维度三：内容质量

##### 检查 7：重复概念

**检测**：比较 concepts/ 下所有文章的标题和内容相似度。

**检测方法**：
1. 标题相似（如 `attention` vs `attention-mechanism`）
2. 内容重叠度 > 60%
3. 引用了相同的来源文章

**修复建议**：使用合并功能，将两篇合并为一篇：
1. 读取两篇文章
2. 合并内容（去重保留所有独特信息）
3. 更新所有引用了旧文章的 wikilinks
4. 将旧文章移到 wiki/archive/（保留重定向说明）

---

##### 检查 8：长文章

**检测**：统计每篇文章字数。

**标准**：建议每篇文章 ≤ 500 字。超出不强制拆分，可通过 [[reference]] 链接将详细内容延伸到关联文章。

**修复建议**：对远超 500 字的文章，建议将核心要点控制在 500 字以内，详细展开部分拆分到子概念文章并通过 [[reference]] 链接。

---

##### 检查 9：内容新鲜度

**检测**：检查文章的最后更新时间（读取 frontmatter 中 `updated_at` 字段，无则用文件修改时间）。

**标准**：

| 未更新时长 | 标记 | 建议 |
|-----------|------|------|
| < 3 个月 | ✅ | 无需操作 |
| 3-6 个月 | 🟡 | 提醒关注，可能需要更新 |
| 6-12 个月 | ⚠️ | 建议审查内容是否过时 |
| > 12 个月 | 🔴 | 建议重新审视或标记归档 |

**修复建议**：
- 列出需要审查的文章
- 对确实过时的内容建议归档（移到 wiki/archive/）
- 对仍相关但需更新的文章标记 `needs-update` tag

---

#### 维度四：组织与规范

##### 检查 10：缺失索引

**检测**：比较 wiki/ 下实际文件与 index.md 中的条目。

**修复建议**：直接补充缺失的索引条目（可自动修复）。

---

##### 检查 11：Frontmatter 规范

**目标**：确保每篇文章的元数据完整且规范。

**Frontmatter 规范**：

| 字段 | 是否必需 | 规范 | 示例 |
|------|---------|------|------|
| `tags` | ✅ 必需 | 必须是非空数组 | `[react, hooks]` |
| `type` | ✅ 必需 | 必须是预定义值之一 | `concept` / `tutorial` / `troubleshooting` / `learning` / `reference` / `note` |
| `updated_at` | ✅ 必需 | 有效的日期格式 | `2025-01-15` |
| `created_at` | 推荐 | 有效的日期格式 | `2025-01-10` |

**预定义 type 值**：

| type | 含义 | 用途 |
|------|------|------|
| `concept` | 概念解释 | 解释某个技术概念、术语 |
| `tutorial` | 教程 | 手把手教如何完成某事 |
| `troubleshooting` | 问题解决 | 记录问题及解决方案 |
| `learning` | 学习笔记 | 从某个项目/资源中学习的记录 |
| `reference` | 参考资料 | API 文档、速查表、配置参考 |
| `note` | 普通笔记 | 不属于以上分类的笔记 |

**检测规则**：

```
对每个 wiki 文件：
  1. 读取 frontmatter
  2. 检查以下问题：
     a. 无 frontmatter → 标记为"缺少 frontmatter"
     b. 缺少 tags 字段 → 标记为"缺少标签"
     c. tags 为空数组 → 标记为"标签为空"
     d. 缺少 type 字段 → 标记为"缺少类型"
     e. type 值不在预定义列表中 → 标记为"类型不规范"
     f. 缺少 updated_at → 标记为"缺少更新时间"
     g. updated_at 格式不正确 → 标记为"日期格式错误"
```

**修复建议**：
- 为缺少 frontmatter 的文章生成建议的 frontmatter
- 对不规范的 type 值建议替换为预定义值
- 可自动补充 updated_at 为文件修改时间

---

##### 检查 12：标签体系

**目标**：确保标签体系健康，能真正帮助分类和检索。

**检测规则**：

```
1. 收集所有文章的标签，统计每个标签的使用次数
2. 检查以下问题：
   a. 孤立标签（仅使用 1 次的标签）→ 标记为"孤立标签"
   b. 相似标签（语义相同但写法不同，如 "react" vs "React" vs "react.js"）→ 标记为"标签不一致"
   c. 过于宽泛的标签（使用次数 > 总文章数 50%）→ 标记为"标签过于宽泛"
   d. 中英文混用的同类标签 → 建议统一
   e. 无标签文章（与检查 11 交叉，此处单独列出）→ 标记为"无标签"
```

**修复建议**：
- 建议合并相似标签（展示替换预览）
- 建议拆分过于宽泛的标签
- 为无标签文章推荐标签（基于内容分析）

---

### 第三步：生成健康报告

**健康评分**：根据 12 项检查结果计算总分（满分 100）

```
评分权重：
- 标题清晰度（15 分）- 标题是第一印象
- 文章结构（10 分）
- 断裂链接（15 分）- 影响阅读体验
- 孤立文章（5 分）
- 低链接文章（5 分）
- 缺失概念（10 分）
- 重复概念（5 分）
- 长文章（5 分）
- 内容新鲜度（5 分）
- 缺失索引（10 分）
- Frontmatter 规范（10 分）
- 标签体系（5 分）
```

**终端摘要**：

```
📊 知识库健康报告

健康评分：82/100 ✅

总览：
- 文章总数：87
- 平均长度：420 字 ✅
- 索引覆盖率：92% ⚠️
- 平均链接数：2.8 ⚠️
- 标题合格率：75% ⚠️
- 结构合格率：85% ✅
- Frontmatter 合格率：90% ✅

问题清单（按优先级）：
🔴 严重（必须修复）：
  1. 3 个断裂链接
  2. 2 对重复概念
  3. 5 篇文章标题严重含糊

🟡 警告（建议修复）：
  4. 7 篇文章缺少索引条目（可自动修复）
  5. 12 篇文章链接数 < 2
  6. 8 篇文章标题不符合类型规范
  7. 4 篇文章缺少 frontmatter

🟢 提示（可选优化）：
  8. 4 篇孤立文章
  9. 1 篇文章 > 500 字
  10. 3 篇文章超过 6 个月未更新
  11. 6 个孤立标签
```

**完整报告**写入 `$OBSIDIAN_REPO/outputs/lint-{YYYY-MM-DD}.md`：

```markdown
---
tags: [lint, health-report]
type: output
created_at: {日期}
---

# 知识库健康报告 {日期}

## 总览

| 指标 | 值 | 状态 |
|------|-----|------|
| 健康评分 | {n}/100 | ✅/⚠️/🔴 |
| 文章总数 | {n} | — |
| 平均长度 | {n} 字 | ✅/⚠️/🔴 |
| 索引覆盖率 | {n}% | ✅/⚠️/🔴 |
| 标题合格率 | {n}% | ✅/⚠️/🔴 |
| 结构合格率 | {n}% | ✅/⚠️/🔴 |
| 平均链接数 | {n} | ✅/⚠️/⚠️ |
| Frontmatter 合格率 | {n}% | ✅/⚠️/🔴 |

## 严重问题 🔴

### 断裂链接（{n} 个）
{每个断裂链接的详情：源文件 → 引用的不存在文件}

### 重复概念（{n} 对）
{每对重复的详情：文件 A vs 文件 B，相似度}

### 标题严重含糊（{n} 篇）
{标题含糊的文章列表，附建议标题}

## 警告问题 🟡

### 标题不符合类型规范（{n} 篇）
{不符合对应类型标题格式的文章列表}

### 文章结构问题（{n} 篇）
{缺少分段/目录/引言/总结的文章列表}

### 缺失索引（{n} 篇）
{缺少索引条目的文章列表}

### 低链接文章（{n} 篇）
{链接数 < 2 的文章列表}

### Frontmatter 问题（{n} 篇）
{缺少/不规范的文章列表，附具体缺失字段}

## 提示信息 🟢

### 孤立文章（{n} 篇）
{无入链的文章列表}

### 长文章（{n} 篇）
{超过 500 字的文章及其字数}

### 内容新鲜度（{n} 篇需关注）
{超过 3 个月未更新的文章列表，附最后更新时间}

### 标签体系（{n} 个问题）
{孤立标签、相似标签等}

## 修复建议

{按优先级排列的可执行修复步骤}
```

### 第四步：交互式修复

展示问题后，使用 AskUserQuestion 提供修复选项：

```
发现 {n} 个可自动修复的问题：

🔴 严重（建议立即修复）：
  □ 修复 3 个断裂链接（需确认修复方式）
  □ 合并 2 对重复概念（需确认合并内容）

🟡 警告（建议修复）：
  □ 补充 7 条缺失索引（自动）
  □ 修复 8 个标题问题（预览建议标题后确认）
  □ 补充 4 篇文章的 frontmatter（自动生成）
  □ 为 12 篇低链接文章推荐关联（需确认）

🟢 提示（可选）：
  □ 清理 6 个孤立标签（需确认合并方案）

选择要执行的修复（可多选）
```

**执行修复**：

对用户选择的每项修复：
1. 展示修复预览（将做什么改动）
2. 确认后执行
3. 每项修复一个 commit

### 追加日志

修复完成后追加 `$OBSIDIAN_REPO/wiki/log.md`：

```markdown
## [{日期}] lint | 健康检查
- 健康评分：{n}/100
- 问题总数：{n}
- 已修复：{n}
- 报告：outputs/lint-{日期}.md
```
