Siyuan Notes Skill
思源笔记工具——搜索、阅读、编辑、组织用户的笔记。Use when user says: 搜索笔记、查找文档、打开文档、阅读笔记、编辑笔记、创建文档、修改块、思源笔记、SiYuan、整理文档、管理标签、Daily Note、反向链接、最近修改、PMF补丁、SQL查询blocks。不要用于与思源笔记无关的通用编程或问答任务。
安装 / 下载方式
TotalClaw CLI推荐
totalclaw install clawskills:fanxing-6~siyuan-notes-skillcURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Afanxing-6~siyuan-notes-skill/file -o siyuan-notes-skill.mdGit 仓库获取源码
git clone https://github.com/openclaw/skills/commit/7c0985cd08cdc62ed2767ffad9bc3445be066c13## 开始前(可选):版本检查
如需确认是否为最新版本,请运行:
```bash
node index.js version-check
```
- 若远程版本获取失败:不会阻塞任务
- 若检测到版本落后:会提示但不阻塞
## 编辑策略选择(最重要)
根据用户意图选择正确的编辑方式,**选错会导致数据丢失**:
| 用户想要 | 正确做法 | 错误做法 |
|---------|---------|---------|
| 修改单个块内容 | `update-block`(最高效) | ~~apply-patch 整个文档~~ |
| 删除单个块 | `delete-block`(最高效) | ~~apply-patch 整个文档~~ |
| 批量修改已有内容 | `apply-patch`(update/delete/reorder/insert) | |
| 批量删除/重排块 | `apply-patch`(delete/reorder) | |
| 添加新内容 | `append-block`(简单稳妥)或 `apply-patch` insert(批量场景) | |
| 在指定位置插入新块 | `insert-block --before/--after`(首选)或 `apply-patch` insert | |
| 替换章节内容 | `replace-section` | ~~apply-patch 删除+插入~~ |
| 重构文档(如拆表格) | `replace-section --clear` + `append-block` 逐步重建 | ~~apply-patch 删除旧块+插入新块~~ |
| 跨章节分散修改多个块 | 编写单个 JS 脚本,循环 `openDocument` → `updateBlock`(同一进程内版本自动刷新) | ~~并行 Bash 调用 update-block(会版本冲突)~~ |
## Intent Decision Tree
```
用户想要…
├─ 查找/搜索内容 ──────→ search / search-md / tag / attr / bookmarks
├─ 在文档内搜索 ──────→ search-in-doc {docID} {关键词}
├─ 阅读文档 ──────────→ open-doc {ID} readable(超长文档自动截断,输出大纲导航)
├─ 阅读章节 ──────────→ open-section {标题块ID} readable(精确读取一个标题的内容)
├─ 浏览最近动态 ──────→ recent / tasks / daily
├─ 了解文档结构 ──────→ docs / notebooks / headings / blocks / doc-tree / doc-tree-id / doc-children
├─ 查看引用关系 ──────→ backlinks / unreferenced
├─ 修改单个块 ────────→ open-doc readable → update-block {块ID} {内容}(最高效)
├─ 删除单个块 ────────→ open-doc readable → delete-block {块ID}(最高效)
├─ 批量修改已有内容 ──→ open-doc patchable → 编辑内容 → apply-patch(支持 update/delete/reorder/insert)
├─ 创建新文档 ────────→ create-doc(指定笔记本、标题、可选初始内容)
├─ 重命名文档 ────────→ rename-doc(只需文档 ID 和新标题)
├─ 添加新内容 ────────→ open-doc readable → append-block(逐个追加)
├─ 指定位置插入 ──────→ open-doc readable → insert-block --before/--after(按锚点插入)
├─ 替换章节 ──────────→ open-doc patchable → replace-section
├─ 重构文档结构 ──────→ open-doc readable → replace-section --clear → append-block 逐步重建
├─ 组织文档层级 ──────→ subdoc-analyze-move → move-docs-by-id
├─ 跨章节分散修改 ────→ 编写单个 JS 脚本,循环 openDocument → updateBlock(见模式8)
├─ SQL 高级查询 ──────→ node -e + executeSiyuanQuery()
└─ 检查连接 ──────────→ check / version
```
## Write Safety Protocol
**写入前通常需要完成这 2 步(`create-doc` / `rename-doc` 例外):**
```bash
# 步骤 1:读取文档或章节(标记为"已读",同时记录文档版本快照)
node index.js open-doc "docID" readable
# 或:node index.js open-section "headingBlockID" readable
# 步骤 2:环境变量启用写入
SIYUAN_ENABLE_WRITE=true node index.js append-block "docID" "内容"
```
- `open-doc` 和 `open-section` 计为"已读";`headings`/`blocks`/`doc-tree` 等不算
- **核心保护:版本检查(乐观锁)**——写入前对比文档 `updated` 时间戳,若文档在读取后被其他端修改过则拒绝写入
- 连续写入安全:每次写入成功后自动刷新版本号,`open-doc → write → write → write` 不会误报冲突
- **⚠️ 版本刷新仅在同一 node 进程内生效**。若用多个独立 Bash 命令(如 Claude Code 并行调用),每次写入会改变文档版本,后续的独立命令会版本冲突。**解决方案**:对同一文档的多次写入必须串行执行,不能并行
- 读标记持久化存储在磁盘缓存中,跨 CLI 调用有效(同一台机器上的不同终端共享读标记)
- 读标记超过 3600 秒自动过期(仅作为缓存清理,版本检查才是真正的安全机制)
- 例外:`create-doc` 与 `rename-doc` 不要求先 `open-doc`
## Core Commands Quick Reference
所有命令:`node index.js {command} [args]`
### 读取
| Command | Signature | Description |
|---------|-----------|-------------|
| `search` | `{keyword} [limit] [type]` | 搜索笔记(type: p/h/l/c/d…) |
| `search-md` | `{keyword} [limit] [type]` | 搜索并输出 Markdown 结果页 |
| `open-doc` | `{docID} [readable\|patchable] [--full] [--cursor {块ID}] [--limit-chars {N}] [--limit-blocks {N}]` | 打开文档(默认 readable)。超长文档自动截断/分页,`--full` 跳过截断输出全量。**副作用:标记已读** |
| `open-section` | `{标题块ID} [readable\|patchable]` | 读取标题下的章节内容。**副作用:标记文档已读** |
| `search-in-doc` | `{docID} {关键词} [数量]` | 在指定文档内搜索匹配的块 |
| `notebooks` | | 列出笔记本 |
| `docs` | `[notebookID] [limit]` | 列出文档(含文档 ID,默认 200) |
| `headings` | `{docID} [level]` | 文档标题(level 格式:`h1`/`h2`/…/`h6`,不是数字) |
| `blocks` | `{docID} [type]` | 文档子块(含块 ID,可用于写入) |
| `doc-children` | `{notebookID} [path]` | 子文档列表 |
| `doc-tree` | `{notebookID} [path] [depth]` | 子文档树(默认深度 4) |
| `doc-tree-id` | `{docID} [depth]` | 以文档 ID 展示子文档树 |
| `tag` | `{tagName}` | 按标签搜索 |
| `backlinks` | `{blockID}` | 反向链接 |
| `tasks` | `[status] [days]` | 任务(`[ ]`/`[x]`/`[-]`,默认 7 天) |
| `daily` | `{start} {end}` | Daily Note(YYYYMMDD) |
| `attr` | `{name} [value]` | 按属性查询(自定义属性加 `custom-` 前缀) |
| `bookmarks` | `[name]` | 书签 |
| `random` | `{docID}` | 随机标题 |
| `recent` | `[days] [type]` | 最近修改(默认 7 天) |
| `unreferenced` | `{notebookID}` | 未被引用的文档 |
| `check` | | 连接检查 |
| `version` | | 内核版本 |
### 写入
| Command | Signature | 适用场景 |
|---------|-----------|---------|
| `create-doc` | `{notebookID} {标题}` | 创建新文档(标题即文档名,初始内容仅支持 stdin) |
| `rename-doc` | `{docID} {新标题}` | 重命名文档 |
| `update-block` | `{块ID}` | 更新块内容(Markdown 仅支持 stdin;多块输入自动拆块安全写入) |
| `delete-block` | `{块ID}` | 删除单个块 |
| `append-block` | `{parentID}` | 添加新内容(parentID 可以是文档 ID 或标题块 ID;Markdown 仅支持 stdin) |
| `insert-block` | `{--before 块ID\|--after 块ID\|--parent 块ID}` | 在指定锚点插入内容(前/后/父块下;Markdown 仅支持 stdin) |
| `replace-section` | `{headingID}` 或 `{headingID} --clear` | 替换/清空章节(保留标题块本身;Markdown 仅支持 stdin) |
| `apply-patch` | `{docID}` (PMF 通过 stdin 传入) | **仅限**批量修改/删除/重排已有块(拒绝 partial PMF) |
| `move-docs-by-id` | `{targetID} {sourceIDs}` | 移动文档(需先 open-doc 目标文档**和**所有来源文档) |
| `subdoc-analyze-move` | `{targetID} {sourceIDs} [depth]` | 分析移动计划(只读) |
## Common Patterns
### 1. 搜索并阅读
```bash
node index.js search "项目总结" 5
node index.js open-doc "找到的文档ID" readable
```
### 2. 修改已有块内容(apply-patch 安全用法)
```bash
# 导出完整 PMF(必须 --full;默认 patchable 在长文档会分页并标记 partial=true)
node index.js open-doc "docID" patchable --full | tee /tmp/doc.pmf
# 编辑 /tmp/doc.pmf:只改 markdown 内容,保留所有块 ID 注释不变
# ⚠️ 关键:PMF 必须包含文档的 **所有** 块!缺失的块会被视为删除!
# 正确做法:导出完整 PMF → 只修改目标块的文本 → 提交完整文件
# 错误做法:只写目标块 → 其他块全部丢失
# 执行
cat /tmp/doc.pmf | SIYUAN_ENABLE_WRITE=true node index.js apply-patch "docID"
```
### 3. 添加新内容到文档
```bash
node index.js open-doc "docID" readable # 先读
printf '## 新标题' | SIYUAN_ENABLE_WRITE=true node index.js append-block "docID"
printf '段落内容' | SIYUAN_ENABLE_WRITE=true node index.js append-block "docID"
printf '- [ ] 任务' | SIYUAN_ENABLE_WRITE=true node index.js append-block "docID"
```
### 3.5 在指定位置插入内容
```bash
node index.js open-doc "docID" readable
printf '插入在该块之前' | SIYUAN_ENABLE_WRITE=true node index.js insert-block --before "目标块ID"
printf '插入在该块之后' | SIYUAN_ENABLE_WRITE=true node index.js insert-block --after "目标块ID"
```
### 4. 重构文档(如拆分表格为多个)
```bash
# 读取原始内容
node index.js open-doc "docID" readable
# 用 patchable 视图找到要操作的块 ID
node index.js open-doc "docID" patchable
# 方案A:有标题块 → 清空章节再重建
# replace-section 保留标题块本身,只删除标题下的子内容
# 所以新内容不要重复标题(例如标题是 "## 表格" 则直接追加表格数据即可)
SIYUAN_ENABLE_WRITE=true node index.js replace-section "标题块ID" --clear
# 追加到标题块 ID 下 → 新内容会出现在该标题的章节内
printf '### 概览' | SIYUAN_ENABLE_WRITE=true node index.js append-block "标题块ID"
printf '|列1|列2|\n|---|---|\n|数据|数据|' | SIYUAN_ENABLE_WRITE=true node index.js append-block "标题块ID"
# 方案B:没有标题块 → 用 node -e 删除旧块再追加
SIYUAN_ENABLE_WRITE=true node -e "
const s = require('./index.js');
s.deleteBlock('旧块ID').then(function(r) { console.log(JSON.stringify(r)); });
"
printf '新内容' | SIYUAN_ENABLE_WRITE=true node index.js append-block "docID"
```
### 5. 创建新文档
```bash
# 先查笔记本ID
node index.js notebooks
# 创建空文档
SIYUAN_ENABLE_WRITE=true node index.js create-doc "笔记本ID" "文档标题"
# 创建带初始内容的文档(Markdown 仅支持 stdin)
printf '## 第一章\n内容' | SIYUAN_ENABLE_WRITE=true node index.js create-doc "笔记本ID" "文档标题"
# 重命名已有文档
SIYUAN_ENABLE_WRITE=true node index.js rename-doc "文档ID" "新标题"
```
### 6. 修改/删除单个块
```bash
# 修改单个块
node index.js open-doc "文档ID" readable
printf '新内容' | SIYUAN_ENABLE_WRITE=true node index.js update-block "块ID"
# 多行内容同样通过 stdin
printf '## 新标题\n\n段落内容' | SIYUAN_ENABLE_WRITE=true node index.js update-block "块ID"
# 删除单个块
node index.js open-doc "文档ID" readable
SIYUAN_ENABLE_WRITE=true node index.js delete-block "块ID"
```
**注意**: 若传入内容可解析为多个块(例如"段落 + $$公式$$"),`update-block` 会自动执行"首块 update + 后续 insert",并做写后校验,避免刷