🧪 Skills

Siyuan Notes Skill

思源笔记工具——搜索、阅读、编辑、组织用户的笔记。Use when user says: 搜索笔记、查找文档、打开文档、阅读笔记、编辑笔记、创建文档、修改块、思

v0.1.0
❤️ 0
⬇️ 10
👁 1
Share

Description


name: siyuan-notes-skill description: "思源笔记工具——搜索、阅读、编辑、组织用户的笔记。Use when user says: 搜索笔记、查找文档、打开文档、阅读笔记、编辑笔记、创建文档、修改块、思源笔记、SiYuan、整理文档、管理标签、Daily Note、反向链接、最近修改、PMF补丁、SQL查询blocks。不要用于与思源笔记无关的通用编程或问答任务。" allowed-tools: "Bash" license: MIT compatibility: "Requires Node.js 18+. Needs a running SiYuan kernel with API access. Claude Code only." metadata: author: fanxing-6 version: 1.0.5

开始前(可选):版本检查

如需确认是否为最新版本,请运行:

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 脚本,循环 openDocumentupdateBlock(同一进程内版本自动刷新) 并行 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 例外):

# 步骤 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-docopen-section 计为"已读";headings/blocks/doc-tree 等不算
  • 核心保护:版本检查(乐观锁)——写入前对比文档 updated 时间戳,若文档在读取后被其他端修改过则拒绝写入
  • 连续写入安全:每次写入成功后自动刷新版本号,open-doc → write → write → write 不会误报冲突
  • ⚠️ 版本刷新仅在同一 node 进程内生效。若用多个独立 Bash 命令(如 Claude Code 并行调用),每次写入会改变文档版本,后续的独立命令会版本冲突。解决方案:对同一文档的多次写入必须串行执行,不能并行
  • 读标记持久化存储在磁盘缓存中,跨 CLI 调用有效(同一台机器上的不同终端共享读标记)
  • 读标记超过 3600 秒自动过期(仅作为缓存清理,版本检查才是真正的安全机制)
  • 例外:create-docrename-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. 搜索并阅读

node index.js search "项目总结" 5
node index.js open-doc "找到的文档ID" readable

2. 修改已有块内容(apply-patch 安全用法)

# 导出完整 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. 添加新内容到文档

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 在指定位置插入内容

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. 重构文档(如拆分表格为多个)

# 读取原始内容
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. 创建新文档

# 先查笔记本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. 修改/删除单个块

# 修改单个块
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",并做写后校验,避免刷新后内容丢失。

7. 超长文档处理

# open-doc 超长文档自动截断/分页,输出大纲和导航提示
node index.js open-doc "超长文档ID" readable
# → 自动截断到 ~15K 字符,附带标题大纲

# 读取特定章节(精确获取标题下内容)
node index.js open-section "标题块ID" readable

# 在文档内搜索关键词(无需读完全文)
node index.js search-in-doc "文档ID" "关键词"

# patchable 视图分页(默认每页 50 块)
node index.js open-doc "文档ID" patchable
# → 分页时 PMF header 含 partial=true next_cursor=xxx

# 翻页
node index.js open-doc "文档ID" patchable --cursor "下一块ID"

# 需要完整 PMF(如整文 apply-patch)→ 用 --full 跳过分页
node index.js open-doc "文档ID" patchable --full | tee /tmp/doc.pmf
# ⚠️ 输出可能很大,注意上下文限制

8. 跨章节分散修改多个块(单进程批量脚本)

当需要修改同一文档中分散在不同章节的多个块时,不能用多个并行 Bash 命令(会版本冲突)。正确做法是编写单个 JS 脚本:

// /tmp/batch_edit.js
const s = require('/root/.claude/skills/siyuan-notes-skill/index.js');
async function main() {
  const docId = '文档ID';

  // 辅助函数:刷新版本 + 更新块(同一进程内版本自动刷新)
  async function edit(blockId, markdown) {
    await s.openDocument(docId, 'readable');
    await s.updateBlock(blockId, markdown);
    console.log(`Updated ${blockId}: OK`);
  }

  // 辅助函数:刷新版本 + 在锚点后插入
  async function insertAfter(previousID, markdown) {
    await s.openDocument(docId, 'readable');
    await s.insertBlock(markdown, { previousID });
    console.log(`Inserted after ${previousID}: OK`);
  }

  // 依次执行所有修改(必须串行,不能 Promise.all)
  await edit('块ID1', '新内容1');
  await edit('块ID2', '新内容2');
  await insertAfter('锚点块ID', '插入的新内容');
}
main().catch(e => { console.error(e.message); process.exit(1); });
# 执行
cd /root/.claude/skills/siyuan-notes-skill
SIYUAN_ENABLE_WRITE=true node /tmp/batch_edit.js

关键点

  • 所有操作在同一个 node 进程内串行执行
  • 每次写入前 openDocument 刷新版本号(进程内版本缓存自动更新)
  • JS API 签名:updateBlock(id, markdown)insertBlock(markdown, { previousID?, nextID?, parentID? })deleteBlock(id)appendMarkdownToBlock(parentID, markdown)

错误恢复

错误 原因 恢复方法
invalid ID argument 块 ID 不存在或格式错误 重新导出 open-doc ... patchable --full,校验 block ID 后再提交
文档被清空 提交了不完整 PMF,缺失块被当作删除 open-doc ... patchable --full 重新导出完整 PMF 后恢复
写入围栏报错 未先 open-doc/open-section 或读标记过期 open-doc "docID" readable(或 open-section)然后重试
版本冲突报错 文档在读取后被其他端修改 open-doc "docID" readable 重新读取最新版本然后重试
PMF 版本冲突 PMF 导出后文档被修改 open-doc "docID" patchable --full 重新导出后再编辑,输出用 tee 保存
partial PMF 被拒绝 分页/章节导出的 PMF 不完整 改用 update-block 编辑单块,或 open-section + replace-section 编辑章节
只读模式报错 未设置 SIYUAN_ENABLE_WRITE=true 在命令前加 SIYUAN_ENABLE_WRITE=true
文档标题为"未命名文档" createDocWithMd 的 path 参数决定标题 create-doc CLI 命令(自动设置标题)或 rename-doc 修正
连接失败 思源未运行/端口/Token 错误 node index.js check 验证
search 返回空 关键词过短/确实无匹配 改用 search-in-doc 限定文档范围,或扩大关键词上下文
并行写入版本冲突 多个独立 Bash 命令并行修改同一文档 改用单个 JS 脚本串行执行(见模式8),或每次写入前重新 open-doc

Output Guidance

  • 读取命令返回格式化文本 → 直接展示给用户
  • open-doc readable 返回干净 Markdown → 适合阅读和总结(超长文档自动截断到 ~15K 字符,附带标题大纲)
  • open-doc patchable 返回 PMF 格式 → 仅用于编辑后 apply-patch(超长文档自动分页,分页 PMF 含 partial=true不可用于 apply-patch
  • open-section 返回单章节内容 → 适合精确阅读/编辑特定章节
  • 写入命令返回 JSON(通常很冗长) → 只提取关键信息(如新文档 ID、成功/失败)展示给用户,不要原样输出全部 JSON
  • 公式渲染:行内公式用 $...$,独立公式块用 $$...$$(思源会渲染为数学块)

KaTeX Formula Rules (No Silent Rewrite)

核心原则:写入内容必须与模型输出一致。禁止在写入前/写入中对公式做隐式重写。

必须遵守

  • 数学定界符只用两种:行内 $...$,独立 $$...$$
  • 数学模式内禁止再次出现 $(例如 $$ ... $x$ ... $$ 是错误)
  • 需要乘幂星号时写 e^*e^{\ast},不要写 e^\*
  • 计数项不要写 #web_search#tokens,改写为 N_{web\_search}N_{tokens}
  • 不要把"给普通文本用的转义"直接搬进数学模式(如 \_\=\*

常见错误与正确写法

  • 错误:$$ ... $s_0,a_0^j,o_1^j$ ... $$(数学模式内嵌 $) 正确:$$ ... s_0, a_0^j, o_1^j ... $$
  • 错误:$-\lambda \cdot #web_search$ 正确:$-\lambda \cdot N_{web\_search}$
  • 错误:$\hat e = e^\*$ 正确:$\hat e = e^*$

写后验证(必须)

  • 写入后立刻 open-doc {docID} readable 回读,确保模型看到的是"最终落地文本"
  • 至少搜索以下高风险串:KaTeX parse errorUndefined control sequencee^\\*#web_search#tokens
  • 若命中,优先修正文档内容本身,不要在工具层做自动改写

Supporting Files

SQL Quick Reference

  • SQLite 语法;默认最多返回 64 行
  • 时间格式 YYYYMMDDHHmmss
  • 主要表:blocks(块)、refs(引用)、attributes(属性)
  • 块类型:d文档 h标题 p段落 l列表 c代码 t表格 m公式 b引述 s超级块
  • ⚠️ tb 是分割线(thematic break / ---),不是"表格体"! 表格的块类型是 t
  • 层级:root_id → 文档,parent_id → 父容器
  • JS API 查询:s.executeSiyuanQuery('SQL') 执行查询,s.formatResults(r) 格式化输出

Block Type Reference(思源内核源码 treenode/node.go

类型代码 节点类型 说明 是否容器块
d NodeDocument 文档
h NodeHeading 标题
p NodeParagraph 段落
l NodeList 列表
i NodeListItem 列表项
c NodeCodeBlock 代码块
m NodeMathBlock 公式块
t NodeTable 表格(整体为一个块,内部 head/body/row/cell 不是独立块)
b NodeBlockquote 引述
s NodeSuperBlock 超级块
tb NodeThematicBreak 分割线---),⚠️ 不是表格体
html NodeHTMLBlock HTML 块
av NodeAttributeView 属性视图(数据库)

表格结构说明:思源中表格(t)是原子块,内部的 TableHead/TableBody/TableRow/TableCell 是 AST 节点,没有独立 block ID。编辑表格只能整体替换,不能操作单个单元格。

Notes

  • cwd 必须是 skill 目录(index.js 所在目录)
  • .env 自动从 index.js 目录加载
  • 路径含空格需加引号
  • Markdown 内容含换行时必须用 $'...\n...' 语法(Bash ANSI-C quoting),普通双引号 "...\n..." 中的 \n 是字面文本不是换行
  • 连接检查:node index.js check

Reviews (0)

Sign in to write a review.

No reviews yet. Be the first to review!

Comments (0)

Sign in to join the discussion.

No comments yet. Be the first to share your thoughts!

Compatible Platforms

Pricing

Free

Related Configs