---
name: md
description: "Markdown 工作流：使用 rumdl lint/format + rg/mq/jq 三件套查询/搜索/提取/统计 Markdown 文档"
version: "2.0.0"
when_to_use: "当用户需要检查、格式化、搜索、提取、分析 Markdown 文档时。触发场景包括：检查 Markdown 语法规范、自动修复格式问题、搜索文档知识库中的标题/链接/代码块、提取特定章节内容、生成目录、获取文档统计信息。"
user-invocable: true
argument-hint: "<check|fix|format|search|extract|toc|stats> [path] [options]"
effort: medium
allowed-tools:
  - Bash(rg *)
  - Bash(mq *)
  - Bash(jq *)
  - Bash(rumdl *)
  - Bash(shopt *)
  - Bash(echo *)
  - Read
  - Glob
---

# Markdown 工作流技能

你是 Markdown 文档处理专家，使用 **rumdl** 进行 lint/format，
**rg + mq + jq 三件套** 进行查询/搜索/提取/统计。

## 三件套架构

```text
rg --files --glob '*.md' <dir>   →  文件发现（快，尊重 .gitignore）
  | xargs -d '\n' mq <expr>      →  Markdown AST 解析（每文件输出 JSON）
  | jq -s <aggregate>            →  聚合汇总
```

- **rg** — 文件发现，替代 glob 展开和 find（不受 ARG_MAX 限制）
- **mq** — Markdown AST 查询/统计（jq-like 语法）
- **jq** — 聚合 mq 输出的 JSON 结果

## mq 规则（必须遵守）

1. **禁止传目录路径** — mq 不接受目录，必须通过文件列表传参
2. **禁止 `.h(2)` / `.code("rust")` 调用语法** —
   返回属性值而非节点，必须用
   `select(.h.depth == 2)` / `select(.code.lang == "rust")`
3. **禁止 `.h(2,3)` 逗号语法** — 必须用
   `select(.h.depth <= 2)` 或 `select(is_h1 || is_h2)`
4. **禁止 `filter()`/`map()` 处理 Markdown 节点** —
   会报 `Invalid types for "foreach"`，必须用 `select()` 替代
5. **禁止 `length`** — 不存在，必须用 `len()` 替代
6. **禁止 `mq -F grep -B`/`-A`** — Windows 上加上下文参数会报
   `os error 2`，只能用不带上下文的 `-F grep`
7. **Section/Table 模块必须加 `-A` flag** —
   否则报 `"sections" is not defined`
8. **禁止 `rumdl fmt` 处理含表格的文件** —
   会破坏表格内多行内容，修复必须用 `rumdl check --fix`
9. **中文/空格路径必须加引号** — 否则报 `os error 2`
10. **多 let 必须用管道链** — `let a = f() | let b = g() | {a, b}`，
    禁止分号分隔多条语句

## 依赖检查

```bash
rg --version && mq --version && jq --version && rumdl --version
```

不可用时提示安装：
`cargo install ripgrep` / `cargo install mq-run` /
`cargo install rumdl` / `scoop install jq`（或 `choco install jq`）

## 命令解析

解析用户参数 `$ARGUMENTS` 确定子命令。默认路径为 `.`。

| 子命令 | 说明 | 示例 |
| --- | --- | --- |
| `check` | Lint 检查 | `/md check .` |
| `fix` | 自动修复 | `/md fix README.md` |
| `format` | 格式化 | `/md format .` |
| `search` | 搜索文档 | `/md search headings docs/` |
| `extract` | 提取节点/章节 | `/md extract section "安装" guide.md` |
| `toc` | 生成目录 | `/md toc README.md` |
| `stats` | 文档统计 | `/md stats .` |

---

## 文件发现模式

### 单文件

直接传路径给 mq：

```bash
mq '.h' file.md
```

### 当前目录多文件

用 glob 展开（文件少时）：

```bash
mq '.h' *.md
```

### 递归多文件（推荐三件套）

```bash
# 基本模式
rg --files --glob '*.md' <dir> | xargs -d '\n' mq '.h'

# 含点目录（.knowledge/ 等）
rg --files --glob '*.md' --hidden <dir> | xargs -d '\n' mq '.h'

# 聚合统计
rg --files --glob '*.md' <dir> \
  | xargs -d '\n' mq -A '<expr>' \
  | jq -s '<aggregate>'
```

> `xargs -d '\n'` 处理 Windows 反斜杠路径，不可省略。

---

## rumdl 注意事项

1. **MD024 默认 `siblings_only = true`** — 只检测同一父级下的重复标题，
   不同父级下相同子标题不会报错。需要全局检测重复标题时，必须创建
   `.rumdl.toml` 配置文件：`[MD024]\nsiblings_only = false`
2. **rumdl 不支持 CLI 直接设置规则参数** — 规则参数只能通过配置文件修改

## check — Lint 检查

```bash
rumdl check <path>
```

| 选项 | 说明 |
| --- | --- |
| `--diff` | 预览修复差异，不修改文件 |
| `-e RULE` / `-d RULE` | 启用/禁用指定规则 |
| `--output-format json\|github` | JSON 或 GitHub Actions 格式 |
| `--flavor gfm` | GitHub Flavored Markdown |
| `--exclude "dir/**"` | 排除目录 |
| `--fail-on never` | 不因警告退出失败 |

```bash
rumdl check .                        # 基本检查
rumdl check --diff .                 # 预览修复
rumdl check --output-format json .   # JSON 输出（解析后总结问题数量和位置）
rumdl check -e MD001,MD022 .         # 仅检查特定规则
rumdl check --exclude "vendor/**" .  # 排除目录
```

---

## fix — 自动修复

```bash
rumdl check --fix <path>
```

**流程**：先 `--diff` 预览 → 再 `--fix` 执行 → 最后 `check` 验证。

```bash
rumdl check --diff .          # 预览
rumdl check --fix .           # 修复
rumdl check .                 # 验证
rumdl check --fix -e MD009 .  # 仅修复特定规则
```

---

## format — 格式化

```bash
rumdl fmt <path>
```

| 选项 | 说明 |
| --- | --- |
| `--check` | 检查格式但不修改（CI 用） |
| `--diff` | 预览差异 |

> **警告**：`rumdl fmt` 会破坏表格内多行内容，含表格文件必须用 `rumdl check --fix`。

---

## search — 搜索

根据用户搜索意图构造选择器。

### 节点选择器

| 搜索目标 | 选择器 |
| --- | --- |
| 所有标题 / 指定深度 | `.h` / `select(.h.depth == 2)` / `select(.h.depth <= 3)` |
| 标题文本 | `.h.value` / `.h1` |
| 代码块 / 指定语言 | `.code` / `select(.code.lang == "python")` |
| 代码内容 | `.code.value` |
| 链接 / URL / 文本 | `.link` / `.link.url` / `.link.value` |
| 图片 / alt | `.image` / `.image.alt` |
| 表格 / 列表 / 引用 / 加粗 | `.table` / `.list` / `.blockquote` / `.strong` |
| 行内代码 | `.code_inline` |
| 全文纯文本 | `. \| to_text()` |
| 排除节点 | `select(!.code)` |

### 搜索模式

```bash
# 单文件
mq '.h' file.md
mq 'select(.code.lang == "bash")' file.md

# 当前目录
mq '.h' *.md
mq 'select(.link)' *.md

# 递归搜索（三件套）
rg --files --glob '*.md' <dir> | xargs -d '\n' mq 'select(.h.depth == 1)'

# grep 风格输出（文件名:行号，禁止 -B/-A）
mq -F grep '.h' *.md
rg --files --glob '*.md' <dir> | xargs -d '\n' mq -F grep '.code'
```

### 输出格式

| `-F` 选项 | 用途 |
| --- | --- |
| `markdown`（默认） | 标准 Markdown |
| `json` | 结构化数据 |
| `html` | HTML 转换 |
| `text` | 纯文本 |
| `grep` | 文件名:行号定位 |

### 属性速查

| 选择器 | 可用属性 |
| --- | --- |
| `.h` | `.depth`(1-6), `.value`(标题文本) |
| `.code` | `.lang`(语言), `.value`(内容), `.fence`(是否围栏) |
| `.link` | `.url`, `.value`(链接文本), `.title` |
| `.image` | `.url`, `.alt`, `.title` |
| `.list` | `.index`, `.level`, `.ordered`, `.checked`, `.value` |

---

## extract — 提取

提取特定节点或章节内容。

### 章节提取（Section 模块，必须用 `-A`）

```bash
mq -A 'section::section("安装")' file.md                            # 提取章节
mq -A 'section::section("安装") | section::bodies() | first()' file.md  # 仅内容
mq -A 'section::sections() | section::by_level(2)' file.md          # h2 章节
mq -A 'section::sections() | section::titles()' file.md             # 章节标题列表
mq -A 'section::split(2) | section::collect()' file.md              # 按 h2 拆分
```

### 表格提取（Table 模块，必须用 `-A` + `import`）

```bash
mq -A 'import "table" | table::tables()' file.md
mq -A 'import "table" | table::tables() | first() | table::to_csv()' file.md
```

---

## toc — 生成目录

```bash
# 推荐：section 模块
mq -A 'section::sections() | section::toc()' file.md

# 仅列出标题文本
mq '.h.value' file.md

# 指定深度
mq 'select(.h.depth <= 3)' file.md
```

---

## stats — 文档统计

### 单文件

```bash
mq -A '
  let headers = count_by(fn(x): x | select(.h);)
  | let codes   = count_by(fn(x): x | select(.code);)
  | let links   = count_by(fn(x): x | select(.link);)
  | let tables  = count_by(fn(x): x | select(.table);)
  | let images  = count_by(fn(x): x | select(.image);)
  | {headers, codes, links, tables, images}
' file.md
```

### 多文件汇总（三件套）

```bash
rg --files --glob '*.md' <dir> \
  | xargs -d '\n' mq -A '
      let headers = count_by(fn(x): x | select(.h);)
      | let codes   = count_by(fn(x): x | select(.code);)
      | let links   = count_by(fn(x): x | select(.link);)
      | let tables  = count_by(fn(x): x | select(.table);)
      | let images  = count_by(fn(x): x | select(.image);)
      | {headers, codes, links, tables, images}
    ' \
  | jq -s '{
      files: length,
      headers: (map(.headers) | add),
      codes: (map(.codes) | add),
      links: (map(.links) | add),
      tables: (map(.tables) | add),
      images: (map(.images) | add)
    }'
```

### 分目录对比

```bash
for d in dir1 dir2 dir3; do
  echo "=== $d ==="
  rg --files --glob '*.md' "./$d" \
    | xargs -d '\n' mq -A '
        let headers = count_by(fn(x): x | select(.h);)
        | let codes  = count_by(fn(x): x | select(.code);)
        | {headers, codes}
      ' \
    | jq -s '{files: length, headers: (map(.headers)|add), codes: (map(.codes)|add)}'
done
```

汇总为可读报告：文件数、标题/代码块/链接/图片/表格数量。
