| 读取Confluence需求文档并整理成指定格式。采集原则是"忠实记录",而非"需求分析"。输出包括:{序号}_{标题}.md(每个页面一个Markdown文件)、requirement-meta.md(元信息)、images/(所有图片,文件中包含图片引用)。
Install with the open skills CLI (global, non-interactive — available in every Claude Code session):
npx skills add LeoYeAI/openclaw-master-skills --skill "theo-confluence-reader" -g -a claude-code -yOr manually — clone and copy the skill directory (SKILL.md + companion files):
git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills /tmp/openclaw-master-skills && cp -r /tmp/openclaw-master-skills/skills/theo-confluence-reader ~/.claude/skills/theo-confluence-readerThis skill is a directory: SKILL.md is the entry point; the files below ship with it.
---
name: theo-confluence-reader
description: |
读取Confluence需求文档并整理成指定格式。采集原则是"忠实记录",而非"需求分析"。输出包括:{序号}_{标题}.md(每个页面一个Markdown文件)、requirement-meta.md(元信息)、images/(所有图片,文件中包含图片引用)。
---
# Theo Confluence Reader
## 全局配置
```powershell
# Confluence 配置(根据实际环境修改)
$confluenceBaseUrl = "https://confluence.xxx.com"
$outputDir = "C:\Users\xxx\.openclaw\workspace\output"
$workspaceDir = "C:\Users\xxx\.openclaw\workspace"
$maxSize = 1GB
$warnThreshold = 0.8
```
## 核心原则
**这个skill的职责是"忠实采集",不是"需求分析"**
- 只做格式转换(HTML → MD),不做内容删减、概括或精简
- 保留原文的所有细节,包括表格、列表、备注、批注等
- 不要对原文做任何主观判断、删除或重组
## 执行规则(强制)
**⚠️ 必须抓取所有页面,不允许部分抓取或偷懒**
### 默认行为
1. **自动抓取全部**:给定一个Confluence页面链接后,必须抓取该页面**及其所有子页面**的全部内容
2. **不允许只抓索引**:不能只抓父页面或索引页就停下来,必须深入到每个叶子节点
3. **不允许跳过**:不能因为"内容太多"或"时间太长"而跳过某些页面
### 唯一例外:确认模式(仅允许询问一次)
如果预估采集时间超过5分钟或页面数量超过10个,可以先整理大纲并预估资源,返回给用户确认。
**但必须遵守以下规则**:
- **只能询问一次**:确认后必须执行完整,不能再次询问
- **大纲必须包含**:
- 所有待抓取页面的完整列表
- 预估页面数量
- 预估时间和资源消耗
- **确认后必须执行**:用户确认后,立即开始抓取所有页面,中途不能停止或询问
- **禁止多次询问**:一旦用户确认后,不能以任何理由再次询问是否继续
### 示例
**错误做法(禁止)**:
- 只抓父页面索引,给用户后说"详细内容需要再抓"
- 抓了一部分后说"太多了要不要继续"
- 用户确认后 又问"这个子页面要不要抓"
**正确做法**:
- 要么直接全部抓完
- 要么先给大纲确认,**确认后全部抓完**
## 存储上限
**output/ 目录总大小上限:1GB**
- 每次创建新目录前检查总大小
- 超过 80%(800MB)时:自动删除最早的目录
- 超过 100%(1GB)时:强制删除最早的目录直到低于 80%
**检查并清理脚本**:
```powershell
$outputDir = "C:\Users\xxx\.openclaw\workspace\output"
$workspaceDir = "C:\Users\xxx\.openclaw\workspace"
$maxSize = 1GB
$warnThreshold = 0.8
if (!(Test-Path $outputDir)) {
New-Item -ItemType Directory -Path $outputDir -Force
}
# 计算 output 目录大小 + workspace 根目录下的 zip 文件大小
$outputSize = 0
$zipSize = 0
$outputItems = Get-ChildItem $outputDir -Recurse -ErrorAction SilentlyContinue
if ($outputItems) {
$outputSize = ($outputItems | Measure-Object -Property Length -Sum).Sum
}
$zipFiles = Get-ChildItem $workspaceDir -Filter "*.zip" -ErrorAction SilentlyContinue
if ($zipFiles) {
$zipSize = ($zipFiles | Measure-Object -Property Length -Sum).Sum
}
$totalSize = $outputSize + $zipSize
if ($totalSize -gt ($maxSize * $warnThreshold)) {
Write-Host "存储超过 80%,开始清理..."
# 1. 删除最早的 zip 文件
if ($zipSize -gt 0) {
$zipFilesSorted = $zipFiles | Sort-Object LastWriteTime
foreach ($zip in $zipFilesSorted) {
if ($totalSize -lt ($maxSize * $warnThreshold)) { break }
$z = $zip.Length
Remove-Item $zip.FullName -Force
$totalSize -= $z
Write-Host "已删除压缩包: $($zip.Name)"
}
}
# 2. 删除最早的 output 子目录
$dirs = Get-ChildItem $outputDir -Directory -ErrorAction SilentlyContinue | Sort-Object LastWriteTime
foreach ($dir in $dirs) {
if ($totalSize -lt ($maxSize * $warnThreshold)) { break }
$dirSize = (Get-ChildItem $dir.FullName -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum
if ($dirSize -gt 0) {
Remove-Item $dir.FullName -Recurse -Force
$totalSize -= $dirSize
Write-Host "已删除: $($dir.Name)"
}
}
}
```
## 输出目录结构
**重要:每次运行都会在 output/ 下创建一个以"页面标题_时间戳"命名的独立目录,避免文件混淆。**
**pageId获取方式**:
- 从Confluence URL中提取:`/pages/viewpage.action?pageId=272188760` → `pageId=272188760`
- 或从页面源码中获取
```
output/
├── 页面标题_2026-03-13_2030/
│ ├── 01_需求概述.md # 页面1的MD转换版
│ ├── 02_登录注册.md # 页面2的MD转换版
│ ├── 03_系统首页.md # 页面3的MD转换版
│ ├── ... # 其他页面
│ ├── requirement-meta.md # 元信息(整个采集任务的元信息)
│ └── images/ # 关联图片
│ ├── 01_需求概述_功能架构图.png
│ ├── 02_登录注册.png
│ └── ...
```
**时间戳格式**:`YYYY-MM-DD_HHmm`(年月日_时分)
## 前置要求
**必须先登录 Confluence**:
1. 在运行 OpenClaw 的机器上打开浏览器
2. 访问 Confluence 页面并完成登录
3. 保持登录状态,后续的图片下载和内容获取都依赖这个已登录的 session
## 操作流程
### Step 1: 分析页面结构 + 创建输出目录 + 检查存储上限
**⚠️ 重要:必须抓取所有子页面**
- 这一步必须展开页面树,列出**所有**子页面(包括二级、三级、四级等全部层级)
- 不能只抓父页面就停止
- 记录每个子页面的:pageId、标题、层级
**输入验证**:
```powershell
param(
[Parameter(Mandatory=$true)]
[string]$confluenceUrl
)
# 验证URL格式
if ($confluenceUrl -notmatch 'https?://[^/]+/pages/(viewpage\.action\?pageId=|viewpage\.action\?title=)') {
throw "无效的Confluence URL,请提供形如 https://confluence.xxx.com/pages/viewpage.action?pageId=123456 的URL"
}
# 提取pageId(如果URL中包含)
if ($confluenceUrl -match 'pageId=(\d+)') {
$rootPageId = $matches[1]
Write-Host "根页面ID: $rootPageId"
}
```
**获取完整页面树的两种方法:**
#### 方法1:通过浏览器页面树获取(默认推荐)
1. 用浏览器打开用户提供的 Confluence URL
2. 在页面左侧找到"页面树结构"
3. 点击"展开全部"或手动展开**所有层级**(包括二级、三级、四级...直到叶子节点)
4. 使用 browser evaluate 获取页面树的完整结构:
```javascript
// 在浏览器控制台执行,获取所有页面链接
const links = Array.from(document.querySelectorAll('a[href*="pageId="]')).map(a => ({
title: a.textContent.trim(),
pageId: a.href.match(/pageId=(\d+)/)?.[1],
href: a.href
}));
console.log(JSON.stringify(links, null, 2));
```
5. 解析获取的数据,提取所有 pageId 和标题
6. **⚠️ 关键**:必须确保页面树完全展开,所有层级的子页面都被展开
7. 确认总共有多少个页面需要获取
#### 方法2:通过 Confluence REST API 获取(备选)
```powershell
# 获取页面及其所有子页面
function Get-ConfluencePageTree {
param(
[string]$baseUrl = "https://confluence.xxx.com",
[string]$rootPageId,
[string]$cookie # 登录 cookie
)
$pages = @()
# 获取当前页面的直接子页面
$apiUrl = "$baseUrl/rest/api/content/$rootPageId/child/page"
$headers = @{
"Cookie" = $cookie
"Content-Type" = "application/json"
}
$response = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Get
foreach ($page in $response.results) {
$pages += @{
pageId = $page.id
title = $page.title
type = $page.type
}
# 递归获取子页面的子页面
$childPages = Get-ConfluencePageTree -baseUrl $baseUrl -rootPageId $page.id -cookie $cookie
$pages += $childPages
}
return $pages
}
# 调用
$allPages = Get-ConfluencePageTree -rootPageId $rootPageId -cookie $cookie
Write-Host "共获取 $($allPages.Count) 个页面"
```
**检查并清理存储(如果超过80%上限)**:
```powershell
# 复用全局配置的变量,或直接使用以下默认值
$outputDir = "C:\Users\xxx\.openclaw\workspace\output"
$workspaceDir = "C:\Users\xxx\.openclaw\workspace"
$maxSize = 1GB
$warnThreshold = 0.8
if (!(Test-Path $outputDir)) {
New-Item -ItemType Directory -Path $outputDir -Force
}
# 计算 output 目录大小 + workspace 根目录下的 zip 文件大小
$outputSize = 0
$zipSize = 0
$outputItems = Get-ChildItem $outputDir -Recurse -ErrorAction SilentlyContinue
if ($outputItems) {
$outputSize = ($outputItems | Measure-Object -Property Length -Sum).Sum
}
$zipFiles = Get-ChildItem $workspaceDir -Filter "*.zip" -ErrorAction SilentlyContinue
if ($zipFiles) {
$zipSize = ($zipFiles | Measure-Object -Property Length -Sum).Sum
}
$totalSize = $outputSize + $zipSize
if ($totalSize -gt ($maxSize * $warnThreshold)) {
Write-Host "存储超过 80%,开始清理..."
# 1. 删除最早的 zip 文件
if ($zipSize -gt 0) {
$zipFilesSorted = $zipFiles | Sort-Object LastWriteTime
foreach ($zip in $zipFilesSorted) {
if ($totalSize -lt ($maxSize * $warnThreshold)) { break }
$z = $zip.Length
Remove-Item $zip.FullName -Force
$totalSize -= $z
Write-Host "已删除压缩包: $($zip.Name)"
}
}
# 2. 删除最早的 output 子目录
$dirs = Get-ChildItem $outputDir -Directory -ErrorAction SilentlyContinue | Sort-Object LastWriteTime
foreach ($dir in $dirs) {
if ($totalSize -lt ($maxSize * $warnThreshold)) { break }
$dirSize = (Get-ChildItem $dir.FullName -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum
if ($dirSize -gt 0) {
Remove-Item $dir.FullName -Recurse -Force
$totalSize -= $dirSize
Write-Host "已删除: $($dir.Name)"
}
}
}
```
**创建带时间戳的输出目录**:
```powershell
# 获取当前时间戳
$timestamp = Get-Date -Format "yyyy-MM-dd_HHmm"
# 页面标题(去除非法字符)
$safeTitle = "页面标题" -replace '[\\/:*?"<>|]', '_'
# 创建目录
$outputDir = "C:\Users\root\.openclaw\workspace\output\${safeTitle}_${timestamp}"
New-Item -ItemType Directory -Path "$outputDir\images" -Force
```
**后续所有文件都写入这个新创建的目录**,而不是直接写进 `output/` 根目录。
### Step 2: 获取每个页面的内容(使用 sessions_spawn)
**⚠️ 必须抓取所有页面,不允许选择性抓取**
**重要**:由于需要获取多个页面,必须使用 sessions_spawn 派发子任务并行处理。
**步骤**:
1. 分析所有pageId列表(从页面URL或页面树中提取)
2. 派发子任务获取所有页面的文字内容
3. 派发子任务下载所有页面的图片
4. 监控子任务直到完成
**获取页面内容的方法**:
1. 使用 browser 访问 Confluence 页面
2. 获取页面完整HTML内容(browser evaluate 或 snapshot)
3. 将HTML转换为Markdown格式
**HTML转Markdown推荐方法**:
- 方法1:使用 Python `markdownify` 库
```powershell
python -m pip install markdownify
python -c "from markdownify import markdownify; print(markdownify(html_content))"
```
- 方法2:使用 Node.js `turndown` 库
```powershell
npm install -g turndown
echo $htmlContent | turndown
```
- 方法3:使用在线API(如 html2md.com 的API)
**关键要求**:转换过程中不得删除任何内容,包括:
- 表格(保持表格结构)
- 图片引用(转换为 `` 格式)
- 代码块(保持原始格式)
- 特殊字符和格式
### Step 3: 获取页面图片(使用 curl 直接下载)
**重要**:使用 curl 直接下载图片原始文件,不再使用 browser 截图方式。
**⚠️ 必须为每个页面下载图片,不管页面层级**
**不管是一级页面、二级页面、三级页面还是更深层次的子页面,都必须下载该页面的所有图片附件!**
> **关键原则**:每个页面独立获取图片 — 页面层级不影响图片下载,父页面的图片 ≠ 子页面的图片,子页面需要单独获取自己的图片!
**⚠️ 前置要求:必须先获取登录 Cookie**
由于 Confluence 私有图片需要登录才能下载,**需要用户手动提供 Cookie**:
1. 用户在浏览器中登录 Confluence
2. 按 F12 打开开发者工具 → Application/Storage → Cookies
3. 复制以下 cookie 的值:
- `JSESSIONID`
- `CONFLAuth`
- 其他可能需要的 cookie(如 `_ga`、`tgw_l7_route` 等)
4. 将完整 cookie 字符串提供给模型,格式如:
```
_ga=GA1.1.xxx; pt_66be4504=xxx; JSESSIONID=xxx; tgw_l7_route=xxx
```
**获取认证Cookie**:
```powershell
# 用户提供的 cookie(直接使用,无需额外处理)
$cookie = "用户提供的完整cookie字符串"
```
**图片获取步骤**:
1. **遍历所有页面**:对 Step 1 中获取的**每一个**页面(包括所有层级的一级、二级、三级...子页面)
2. 对每个pageId,访问附件页面:`https://confluence.xxx.com/pages/viewpageattachments.action?pageId={pageId}`
3. 使用 curl 获取附件列表页面源码
4. 解析 `data-attachment-filename` 属性,找到该页面**所有**图片附件
5. 使用 curl 下载每个图片
6. **重复步骤1-5直到所有页面的图片都下载完成**
**图片命名规则**:
- 格式:`{页面序号}_{页面名称}.{扩展名}`
- 例如:`01_需求概述_功能架构图.png`、`02_登录注册.png`
**curl 下载命令示例**:
```powershell
$cookie = "用户提供的cookie"
$imageUrl = "https://confluence.xxx.com/download/attachments/272188760/image-2026-3-3_14-10-42.png?api=v2"
$outputFile = "C:\Users\root\.openclaw\workspace\output\images\01_需求概述_功能架构图.png"
curl.exe -L -o $outputFile $imageUrl -H "Cookie: $cookie"
```
**注意事项**:
- 使用 `curl.exe` 而非 PowerShell 内置 `curl` 别名
- 添加 `-L` 参数跟随重定向
- 必须携带完整登录 Cookie 才能下载私有图片
- Cookie 可能有有效期限制,如失效需让用户重新提供
**curl 下载脚本(含错误处理和重试)**:
```powershell
# 配置
$cookie = "JSESSIONID=xxx; CONFLAuth=xxx" # 替换为实际 cookie
$imagesDir = "$outputDir\images"
$maxRetries = 3
# 创建 images 目录
if (!(Test-Path $imagesDir)) {
New-Item -ItemType Directory -Path $imagesDir -Force
}
# 图片列表(从附件页面解析得到)
$imageList = @(
@{pageId="272191284"; filename="image.png"; ext="png"},
@{pageId="272191284"; filename="photo.jpg"; ext="jpg"}
)
$index = 1
foreach ($img in $imageList) {
$imageUrl = "https://confluence.xxx.com/download/attachments/$($img.pageId)/$($img.filename)"
$outputFile = "$imagesDir\$($img.pageId)_$index.$($img.ext)"
# 重试逻辑
$success = $false
for ($retry = 1; $retry -le $maxRetries; $retry++) {
Write-Host "尝试下载 ($retry/$maxRetries): $imageUrl"
$result = curl.exe -L -o $outputFile $imageUrl `
-H "Cookie: $cookie" `
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" `
--silent --show-error `
2>&1
# 检查文件是否成功下载
if ((Test-Path $outputFile) -and (Get-Item $outputFile).Length -gt 0) {
$success = $true
Write-Host "已下载: $outputFile"
break
} else {
Write-Host "下载失败,等待重试..."
Start-Sleep -Seconds 2
}
}
if (-not $success) {
Write-Host "警告: 图片下载失败 - $imageUrl" -ForegroundColor Yellow
}
$index++
}
Write-Host "图片下载完成,共 $($imageList.Count) 张"
```
**注意事项**:
- 使用 `curl.exe` 而非 PowerShell 内置 `curl` 别名
- 添加 `-L` 参数跟随重定向
- 保留原始文件格式(png/jpg/svg等)
- 必须携带登录 Cookie 才能下载私有图片
- 添加重试逻辑应对网络不稳定
- 下载后检查文件大小确保下载成功
### Step 4: 生成输出文件
**所有文件都写入 Step 1 创建的 `$outputDir` 目录**。
#### 4.1 {序号}_{标题}.md(每个页面一个)
将Confluence页面的HTML内容忠实转换为Markdown格式。
**关键:必须插入图片引用!**
在生成 md 文件时,对于该页面关联的每张图片,必须在**适当位置**插入图片引用:
```powershell
# 假设当前页面是 "02_登录注册",该页面有 2 张图片
# images/ 目录中的文件:
# - 02_登录注册.png
# - 02_登录注册_2.png
# 在 md 文件内容中插入图片引用(建议放在页面标题下方或相关章节后)
$markdownContent = @"
# 2.1 登录注册(一期交付)

## 1、功能设计
...
## 2、原型图

...
"@
# 写入文件
$mdFileName = "02_登录注册.md"
$mdFilePath = Join-Path $outputDir $mdFileName
Set-Content -Path $mdFilePath -Value $markdownContent -Encoding UTF8
```
**图片引用规则**:
- 格式:``
- 位置:
- 如果图片是功能架构图/流程图,放在页面标题下方或相应章节后
- 如果是原型图,放在对应功能描述之后
- 如果无法确定位置,放在页面末尾
- 如果一个页面有多张图片,按文件名排序后依次插入
#### 4.2 requirement-meta.md
```markdown
# 采集元信息
- 来源页面: [Confluence 页面 URL]
- 采集根页面: [根页面标题和 URL]
- 采集时间: [ISO 8601 格式,如 2026-03-15T18:00:00+08:00]
- 采集环境: [Confluence URL 域名]
- 总页面数: [页面总数]
- 页面列表:
- 01_需求概述 (pageId: 272188760)
- 02_登录注册 (pageId: 272189290)
- ...
```
写入 `$outputDir\requirement-meta.md`。
#### 4.3 images/ 目录
- 保存所有下载的图片到 `$outputDir\images\`
- 文件名格式:`{序号}_{页面标题}_{图片描述}.{扩展名}`
- 例如:`01_需求概述_功能架构图.png`、`02_登录注册.png`、`02_登录注册_2.png`
### Step 5: 打包输出
将整个output目录打包成zip文件,发送给用户。
**打包命令**:
```powershell
# 打包整个 output 目录
$timestamp = Get-Date -Format "yyyy-MM-dd_HHmm"
$zipName = "confluence_export_$timestamp.zip"
$zipPath = "C:\Users\root\.openclaw\workspace\$zipName"
# 使用 PowerShell Compress-Archive
Compress-Archive -Path "C:\Users\root\.openclaw\workspace\output\*" -DestinationPath $zipPath -Force
# 验证打包结果
if (Test-Path $zipPath) {
$zipSize = (Get-Item $zipPath).Length
Write-Host "打包完成: $zipName (大小: $([math]::Round($zipSize/1MB, 2)) MB)"
} else {
Write-Host "打包失败!" -ForegroundColor Red
}
```
**或者使用 7z(如果安装了中国)**:
```powershell
7z a -r "$zipPath" "C:\Users\root\.openclaw\workspace\output\*"
```
**输出文件后**:
- 将 zip 文件路径返回给用户
- 用户下载后可解压查看
## 注意事项
1. **使用sessions_spawn**:长时间任务必须使用sessions_spawn派发子任务并行处理
2. **图片直接下载**:
- 访问 `viewpageattachments.action?pageId={pageId}` 获取附件列表
- 使用 curl 携带登录 Cookie 直接下载图片原始文件
- 保留原始格式(png/jpg/svg等)
- 添加重试逻辑(建议3次)确保稳定性
3. **认证Cookie获取**:
- 主session已登录,可以从browser获取Cookie
- Cookie格式:`JSESSIONID=xxx; CONFLAuth=xxx`
- 必须携带Cookie才能下载私有图片
4. **HTML转Markdown**:
- 推荐使用 `markdownify` (Python) 或 `turndown` (Node.js)
- 转换过程不得删除任何内容
- 表格、代码块、图片引用需保持完整
5. **存储管理**:
- 每次创建新目录前检查存储上限
- 超过80%自动清理最早的目录和zip文件
- 注意处理空目录的边界情况
6. **错误处理**:
- 所有网络请求添加重试逻辑
- 下载后验证文件大小
- 记录错误日志以便排查
7. **不删减内容**:原文的每一个字都要保留
8. **保留格式**:表格、列表等都要完整保留
9. **层级准确**:严格按照Confluence的页面树层级整理
Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies
Use when you have a written implementation plan to execute in a separate session with review checkpoints
Use when executing implementation plans with independent tasks in the current session