remnote-bridge 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/cli/commands/connect.d.ts +12 -0
  2. package/dist/cli/commands/connect.js +124 -0
  3. package/dist/cli/commands/disconnect.d.ts +11 -0
  4. package/dist/cli/commands/disconnect.js +100 -0
  5. package/dist/cli/commands/edit-rem.d.ts +13 -0
  6. package/dist/cli/commands/edit-rem.js +83 -0
  7. package/dist/cli/commands/edit-tree.d.ts +14 -0
  8. package/dist/cli/commands/edit-tree.js +67 -0
  9. package/dist/cli/commands/health.d.ts +12 -0
  10. package/dist/cli/commands/health.js +100 -0
  11. package/dist/cli/commands/install-skill.d.ts +6 -0
  12. package/dist/cli/commands/install-skill.js +39 -0
  13. package/dist/cli/commands/read-context.d.ts +20 -0
  14. package/dist/cli/commands/read-context.js +77 -0
  15. package/dist/cli/commands/read-globe.d.ts +16 -0
  16. package/dist/cli/commands/read-globe.js +60 -0
  17. package/dist/cli/commands/read-rem.d.ts +16 -0
  18. package/dist/cli/commands/read-rem.js +80 -0
  19. package/dist/cli/commands/read-tree.d.ts +17 -0
  20. package/dist/cli/commands/read-tree.js +85 -0
  21. package/dist/cli/commands/search.d.ts +12 -0
  22. package/dist/cli/commands/search.js +65 -0
  23. package/dist/cli/config.d.ts +55 -0
  24. package/dist/cli/config.js +139 -0
  25. package/dist/cli/daemon/daemon.d.ts +11 -0
  26. package/dist/cli/daemon/daemon.js +186 -0
  27. package/dist/cli/daemon/dev-server.d.ts +26 -0
  28. package/dist/cli/daemon/dev-server.js +81 -0
  29. package/dist/cli/daemon/pid.d.ts +34 -0
  30. package/dist/cli/daemon/pid.js +67 -0
  31. package/dist/cli/daemon/send-request.d.ts +24 -0
  32. package/dist/cli/daemon/send-request.js +92 -0
  33. package/dist/cli/handlers/context-read-handler.d.ts +18 -0
  34. package/dist/cli/handlers/context-read-handler.js +24 -0
  35. package/dist/cli/handlers/edit-handler.d.ts +30 -0
  36. package/dist/cli/handlers/edit-handler.js +133 -0
  37. package/dist/cli/handlers/globe-read-handler.d.ts +17 -0
  38. package/dist/cli/handlers/globe-read-handler.js +23 -0
  39. package/dist/cli/handlers/read-handler.d.ts +16 -0
  40. package/dist/cli/handlers/read-handler.js +78 -0
  41. package/dist/cli/handlers/rem-cache.d.ts +19 -0
  42. package/dist/cli/handlers/rem-cache.js +63 -0
  43. package/dist/cli/handlers/tree-edit-handler.d.ts +30 -0
  44. package/dist/cli/handlers/tree-edit-handler.js +188 -0
  45. package/dist/cli/handlers/tree-parser.d.ts +95 -0
  46. package/dist/cli/handlers/tree-parser.js +506 -0
  47. package/dist/cli/handlers/tree-read-handler.d.ts +28 -0
  48. package/dist/cli/handlers/tree-read-handler.js +53 -0
  49. package/dist/cli/main.d.ts +7 -0
  50. package/dist/cli/main.js +300 -0
  51. package/dist/cli/protocol.d.ts +39 -0
  52. package/dist/cli/protocol.js +35 -0
  53. package/dist/cli/server/config-server.d.ts +26 -0
  54. package/dist/cli/server/config-server.js +363 -0
  55. package/dist/cli/server/ws-server.d.ts +68 -0
  56. package/dist/cli/server/ws-server.js +335 -0
  57. package/dist/cli/utils/output.d.ts +11 -0
  58. package/dist/cli/utils/output.js +13 -0
  59. package/dist/mcp/daemon-client.d.ts +31 -0
  60. package/dist/mcp/daemon-client.js +99 -0
  61. package/dist/mcp/index.d.ts +7 -0
  62. package/dist/mcp/index.js +68 -0
  63. package/dist/mcp/instructions.d.ts +1 -0
  64. package/dist/mcp/instructions.js +249 -0
  65. package/dist/mcp/resources/edit-tree-guide.d.ts +1 -0
  66. package/dist/mcp/resources/edit-tree-guide.js +197 -0
  67. package/dist/mcp/resources/error-reference.d.ts +1 -0
  68. package/dist/mcp/resources/error-reference.js +132 -0
  69. package/dist/mcp/resources/outline-format.d.ts +1 -0
  70. package/dist/mcp/resources/outline-format.js +104 -0
  71. package/dist/mcp/resources/rem-object-fields.d.ts +1 -0
  72. package/dist/mcp/resources/rem-object-fields.js +331 -0
  73. package/dist/mcp/resources/separator-flashcard.d.ts +1 -0
  74. package/dist/mcp/resources/separator-flashcard.js +120 -0
  75. package/dist/mcp/tools/edit-tools.d.ts +5 -0
  76. package/dist/mcp/tools/edit-tools.js +47 -0
  77. package/dist/mcp/tools/infra-tools.d.ts +5 -0
  78. package/dist/mcp/tools/infra-tools.js +43 -0
  79. package/dist/mcp/tools/read-tools.d.ts +5 -0
  80. package/dist/mcp/tools/read-tools.js +195 -0
  81. package/dist/mcp/types.d.ts +12 -0
  82. package/dist/mcp/types.js +4 -0
  83. package/docs/instruction/connect.md +158 -0
  84. package/docs/instruction/disconnect.md +146 -0
  85. package/docs/instruction/edit-rem.md +509 -0
  86. package/docs/instruction/edit-tree.md +419 -0
  87. package/docs/instruction/health.md +159 -0
  88. package/docs/instruction/overall.md +751 -0
  89. package/docs/instruction/read-context.md +353 -0
  90. package/docs/instruction/read-globe.md +206 -0
  91. package/docs/instruction/read-rem.md +476 -0
  92. package/docs/instruction/read-tree.md +428 -0
  93. package/docs/instruction/search.md +196 -0
  94. package/package.json +41 -0
  95. package/remnote-plugin/package.json +48 -0
  96. package/remnote-plugin/postcss.config.js +5 -0
  97. package/remnote-plugin/public/bridge-icon.svg +8 -0
  98. package/remnote-plugin/public/manifest.json +22 -0
  99. package/remnote-plugin/src/bridge/message-router.ts +57 -0
  100. package/remnote-plugin/src/bridge/websocket-client.ts +245 -0
  101. package/remnote-plugin/src/index.css +1 -0
  102. package/remnote-plugin/src/services/breadcrumb.ts +26 -0
  103. package/remnote-plugin/src/services/create-rem.ts +59 -0
  104. package/remnote-plugin/src/services/delete-rem.ts +29 -0
  105. package/remnote-plugin/src/services/index.ts +16 -0
  106. package/remnote-plugin/src/services/move-rem.ts +39 -0
  107. package/remnote-plugin/src/services/powerup-filter.ts +31 -0
  108. package/remnote-plugin/src/services/read-context.ts +368 -0
  109. package/remnote-plugin/src/services/read-globe.ts +197 -0
  110. package/remnote-plugin/src/services/read-rem.ts +284 -0
  111. package/remnote-plugin/src/services/read-tree.ts +222 -0
  112. package/remnote-plugin/src/services/rem-builder.ts +124 -0
  113. package/remnote-plugin/src/services/reorder-children.ts +61 -0
  114. package/remnote-plugin/src/services/search.ts +56 -0
  115. package/remnote-plugin/src/services/write-rem-fields.ts +254 -0
  116. package/remnote-plugin/src/settings.ts +12 -0
  117. package/remnote-plugin/src/style.css +45 -0
  118. package/remnote-plugin/src/test-scripts/AGENTS.md +46 -0
  119. package/remnote-plugin/src/test-scripts/test-actions.ts +230 -0
  120. package/remnote-plugin/src/test-scripts/test-powerup-rendering.ts +722 -0
  121. package/remnote-plugin/src/test-scripts/test-rem-type-mapping.ts +283 -0
  122. package/remnote-plugin/src/test-scripts/test-richtext-builder.ts +207 -0
  123. package/remnote-plugin/src/test-scripts/test-richtext-matrix.ts +332 -0
  124. package/remnote-plugin/src/test-scripts/test-richtext-remaining.ts +245 -0
  125. package/remnote-plugin/src/test-scripts/test-rw-fields.ts +399 -0
  126. package/remnote-plugin/src/types.ts +419 -0
  127. package/remnote-plugin/src/utils/elision.ts +45 -0
  128. package/remnote-plugin/src/utils/index.ts +10 -0
  129. package/remnote-plugin/src/utils/tree-serializer.ts +269 -0
  130. package/remnote-plugin/src/widgets/bridge_widget.tsx +170 -0
  131. package/remnote-plugin/src/widgets/index.tsx +82 -0
  132. package/remnote-plugin/tailwind.config.js +7 -0
  133. package/remnote-plugin/tsconfig.json +21 -0
  134. package/remnote-plugin/webpack.config.js +125 -0
  135. package/skill/SKILL.md +428 -0
package/skill/SKILL.md ADDED
@@ -0,0 +1,428 @@
1
+ ---
2
+ name: remnote-bridge
3
+ description: "RemNote 知识库操作指南。通过 remnote-bridge 命令行工具读取、编辑、搜索 RemNote 知识库中的内容。当用户需要操作 RemNote 知识库时使用此 skill——包括读取笔记、编辑内容、搜索、浏览知识库结构、创建闪卡、修改文档属性等。触发关键词包括:RemNote、知识库、笔记、Rem、闪卡、文档、read-rem、edit-rem、read-tree、edit-tree、search、read-globe、read-context。即使用户没有明确提到 CLI 命令,只要意图是操作 RemNote 中的数据,都应触发此 skill。"
4
+ ---
5
+
6
+ # RemNote CLI 操作指南
7
+
8
+ 本 skill 指导 AI Agent 通过 remnote-bridge 操作 RemNote 知识库。
9
+
10
+ **详细命令文档**位于 `docs/instruction/` 目录下,执行具体命令前务必先读取对应文件:
11
+
12
+ | 命令 | 文档路径 |
13
+ |:-----|:---------|
14
+ | connect | `docs/instruction/connect.md` |
15
+ | disconnect | `docs/instruction/disconnect.md` |
16
+ | health | `docs/instruction/health.md` |
17
+ | read-rem | `docs/instruction/read-rem.md` |
18
+ | edit-rem | `docs/instruction/edit-rem.md` |
19
+ | read-tree | `docs/instruction/read-tree.md` |
20
+ | edit-tree | `docs/instruction/edit-tree.md` |
21
+ | read-globe | `docs/instruction/read-globe.md` |
22
+ | read-context | `docs/instruction/read-context.md` |
23
+ | search | `docs/instruction/search.md` |
24
+ | 全局概览 | `docs/instruction/overall.md` |
25
+
26
+ ---
27
+
28
+ ## 1. 核心概念
29
+
30
+ ### 一切皆 Rem
31
+
32
+ RemNote 中所有内容的基本单元都是 **Rem**。文档、文件夹、闪卡、标签——本质上都是 Rem 的不同形态。
33
+
34
+ | 用户说的 | 实际上是 |
35
+ |:---------|:---------|
36
+ | 笔记 / 条目 | Rem |
37
+ | 文档 / 页面 | Rem(`isDocument=true`) |
38
+ | 文件夹 | Rem(Document 且子节点全是 Document) |
39
+ | 闪卡 / 卡片 | Card——由 RemNote 根据 Rem 属性自动生成,**不可直接操控** |
40
+
41
+ ### Rem 类型系统
42
+
43
+ 两个独立维度:
44
+
45
+ - **type**(闪卡语义):`concept`(`::`,加粗)、`descriptor`(`;;`,斜体)、`default`(普通)、`portal`(只读)
46
+ - **isDocument**(页面语义):与 type 完全独立
47
+
48
+ ### CDF 框架(Concept-Descriptor Framework)
49
+
50
+ RemNote 推荐的知识结构化方法:
51
+
52
+ ```
53
+ 线性回归 :: 最基本的回归模型 ← Concept(type:concept,加粗)
54
+ 假设 ;; 因变量与自变量呈线性关系 ← Descriptor(type:descriptor,斜体)
55
+ 损失函数 ;; 均方误差 (MSE) ← Descriptor
56
+ ```
57
+
58
+ ### 分隔符与闪卡方向
59
+
60
+ | 分隔符 | type | 默认方向 | 用途 |
61
+ |:-------|:-----|:---------|:-----|
62
+ | `::` | concept | both | 概念定义 |
63
+ | `;;` | descriptor | forward | 描述属性 |
64
+ | `>>` | default | forward | 正向问答 |
65
+ | `<<` | default | backward | 反向问答 |
66
+ | `<>` | default | both | 双向问答 |
67
+ | `>>>` | default | forward | 多行答案 |
68
+ | `::>` | concept | both | 概念型多行答案 |
69
+ | `;;>` | descriptor | forward | 描述型多行答案 |
70
+ | `{{}}` | default | forward | 完形填空 |
71
+
72
+ ### 三种链接机制
73
+
74
+ | 机制 | 用户操作 | 本质 | CLI 可见性 |
75
+ |:-----|:---------|:-----|:-----------|
76
+ | **Reference** | `[[` | RichText 中的 `{"i":"q","_id":"remId"}` | read-rem 的 text 数组 |
77
+ | **Tag** | `##` | Rem 的 tags 数组 | read-rem 的 `tags` 字段 |
78
+ | **Portal** | `((` | 嵌入实时视图(**编辑同步**) | read-tree 标记 `type:portal refs:id1,id2` |
79
+
80
+ Portal 的编辑同步意味着修改一处会影响另一处。Portal 引用的 Rem 不会被 read-tree 自动展开,需要对 refs 中的 ID 单独 read-tree。
81
+
82
+ ### RichText 格式
83
+
84
+ `text` 和 `backText` 字段是 JSON 数组,元素为纯字符串或格式化对象:
85
+
86
+ ```json
87
+ ["纯文本", {"i":"m","b":true,"text":"粗体"}, {"i":"q","_id":"remId"}]
88
+ ```
89
+
90
+ | `i` 值 | 类型 | 核心字段 |
91
+ |:-------|:-----|:---------|
92
+ | 纯 string | 纯文本 | — |
93
+ | `"m"` | 带格式文本 | `text` + `b`/`l`/`u`/`h`/`tc`/`code`/`cId`/`iUrl` |
94
+ | `"q"` | Rem 引用 | `_id` |
95
+ | `"i"` | 图片 | `url`, `width`, `height` |
96
+ | `"x"` | LaTeX | `text` |
97
+ | `"a"` | 音频 | `url` |
98
+
99
+ **序列化确定性**:RichText 对象内部按 **key 字母序排列**(如 `{"b":true,"i":"m","text":"粗体"}`),确保 JSON 始终一致。构造 edit-rem 的 oldStr 时必须保持相同的 key 顺序,否则匹配失败。
100
+
101
+ ### Powerup 机制与噪音过滤
102
+
103
+ RemNote 格式设置(fontSize、highlightColor 等)底层通过 Powerup 机制实现,会注入隐藏 Tag 和子 Rem。默认自动过滤(`includePowerup=false`)。read-tree 支持 `--includePowerup` 恢复完整数据,read-globe 和 read-context 硬编码过滤无选项。
104
+
105
+ ---
106
+
107
+ ## 2. 命令决策
108
+
109
+ ### 读取:用户想了解什么?
110
+
111
+ ```
112
+ 知识库整体结构("有哪些文档") → read-globe
113
+ 用户当前在看什么("当前页面") → read-context
114
+ 某个 Rem 的子树("展开这个主题") → read-tree <remId>
115
+ 某个 Rem 的详细属性("详细信息") → read-rem <remId>
116
+ 按关键词搜索("搜索 X") → search <query>(中文搜索有限制,见下方说明)
117
+ ```
118
+
119
+ #### read-globe vs read-context vs read-tree
120
+
121
+ | 场景 | 命令 | 特点 |
122
+ |:-----|:-----|:-----|
123
+ | 知识库有什么 | `read-globe` | 仅 Document 层级,**无缓存**,最小序列化(无 backText/箭头) |
124
+ | 我在编辑什么 | `read-context --mode focus` | 鱼眼视图(焦点 depth=3,siblings depth=1,叔伯 depth=0),**无缓存** |
125
+ | 当前页面内容 | `read-context --mode page` | 均匀展开,**无缓存** |
126
+ | 展开某主题细节 | `read-tree <id>` | 完整子树,**有缓存**供 edit-tree |
127
+
128
+ **重要**:read-globe、read-context、search 都**不写入缓存**,不能替代 read-tree/read-rem 作为 edit 的前置条件。read-context 需要用户在 RemNote 中有焦点(focus 模式)或打开页面(page 模式)。
129
+
130
+ ### 修改:用户想改什么?
131
+
132
+ ```
133
+ Rem 的属性(文本、类型、格式、标签) → edit-rem (前置:先 read-rem)
134
+ 树的结构(新增、删除、移动、重排) → edit-tree (前置:先 read-tree)
135
+ 某行的文字内容 → edit-rem (不是 edit-tree!)
136
+ ```
137
+
138
+ **关键区分**:`edit-rem` 修改 Rem 的**属性**,`edit-tree` 修改 Rem 之间的**结构关系**。`edit-tree` **禁止修改行内容**。
139
+
140
+ ---
141
+
142
+ ## 3. 标准工作流
143
+
144
+ ```
145
+ 1. connect -- 启动会话(幂等,重复调用安全)
146
+ 2. health -- 确认三层就绪:daemon → Plugin → SDK(链式依赖)
147
+ 3. read-globe -- 了解知识库结构(首次探索)
148
+ 或 read-context -- 了解用户当前上下文
149
+ 4. search "关键词" -- 定位目标 Rem(结果不进缓存!)
150
+ 5. read-tree <id> -- 展开子树 → 写入缓存(edit-tree 的前置)
151
+ 6. read-rem <id> -- 读取属性 → 写入缓存(edit-rem 的前置)
152
+ 7. edit-rem / edit-tree -- 执行修改
153
+ 8. disconnect -- 结束会话(缓存全部清空,幂等)
154
+ ```
155
+
156
+ **超时**:daemon 默认 30 分钟无 CLI 交互自动关闭,每次请求重置计时器。长时间操作间可用 health 保活。
157
+
158
+ ---
159
+
160
+ ## 4. 双模式 I/O(`--json`)
161
+
162
+ Agent 应始终使用 JSON 模式调用命令。
163
+
164
+ ```bash
165
+ # 正确:位置参数 = JSON 字符串
166
+ remnote-bridge read-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA","fields":["text","type"]}'
167
+ remnote-bridge search --json '{"query":"机器学习","numResults":10}'
168
+
169
+ # 错误:禁止混用裸 remId + --json
170
+ remnote-bridge read-rem kLrIOHJLyMd8Y2lyA --json # 会失败!
171
+ ```
172
+
173
+ 注意:search 的 JSON 参数名是 `numResults`(不是 `limit`)。
174
+
175
+ ### 中文搜索限制(重要)
176
+
177
+ search 调用 RemNote SDK 官方搜索方法,其分词基于空格分割。**中文、日文、韩文等无空格分词的语言搜索效果差**——SDK 将多字词拆为单字符 token 匹配,导致返回 0 结果或不相关结果。RemNote 本地桌面版已优化此问题,Web 版未优化。
178
+
179
+ **应对策略**:
180
+ 1. 先用完整关键词搜索
181
+ 2. 若返回 0 结果且关键词为中文等非空格语言:用**单个最具区分度的字**重试(如搜 "键" 而非 "共价键")
182
+ 3. 若仍无结果:改用 `read-globe` → `read-tree` 浏览定位
183
+ 4. 搜索持续不佳时,询问用户:"您使用的是 RemNote Web 版还是本地桌面版?Web 版中文搜索存在已知限制。"
184
+
185
+ ### JSON 输出结构
186
+
187
+ ```jsonc
188
+ { "ok": true, "command": "read-rem", ... } // 成功
189
+ { "ok": false, "command": "read-rem", "error": "..." } // 失败
190
+ ```
191
+
192
+ ---
193
+
194
+ ## 5. 安全机制:三道防线
195
+
196
+ ### 防线 1:缓存存在性
197
+
198
+ 必须先 read 再 edit。未缓存的 Rem 不允许编辑。
199
+
200
+ ### 防线 2:乐观并发检测
201
+
202
+ edit 时从 SDK 重新读取最新数据,与缓存严格比较。被外部修改则拒绝编辑且**不更新缓存**——迫使 Agent 重新 read。
203
+
204
+ ### 防线 3:str_replace 精确匹配
205
+
206
+ `oldStr` 必须在目标文本中恰好匹配 1 次。
207
+
208
+ ### 缓存更新规则
209
+
210
+ | 场景 | 缓存行为 | Agent 操作 |
211
+ |:-----|:---------|:-----------|
212
+ | 写入成功 | 从 SDK 重新读取 → 更新缓存 | 可继续编辑 |
213
+ | 防线 2 拒绝 / 部分写入失败 | **不更新缓存** | 必须重新 read |
214
+ | 防线 3 拒绝 / JSON 语法错误 | 缓存保持不变 | 调整 oldStr/newStr 后**直接重试** |
215
+ | 操作执行中异常(edit-tree) | 已执行的操作保留(**无回滚**),不更新缓存 | 必须重新 read-tree |
216
+
217
+ ---
218
+
219
+ ## 6. Markdown 大纲格式
220
+
221
+ read-tree / read-globe / read-context 输出 Markdown 大纲,edit-tree 基于此大纲编辑。
222
+
223
+ ### 缩进规则
224
+
225
+ **每级缩进 2 个空格**。根节点 0 空格,直接子节点 2 空格,孙节点 4 空格。缩进不可跳级(如从 0 直跳 4 空格会报 `indent_skip` 错误)。
226
+
227
+ ### 行结构
228
+
229
+ ```
230
+ {缩进}{前缀}{内容}{箭头}{backText} <!-- {remId} {元数据} -->
231
+ ```
232
+
233
+ ### 前缀
234
+
235
+ | 前缀 | 含义 |
236
+ |:-----|:-----|
237
+ | `# ` / `## ` / `### ` | H1/H2/H3 标题 |
238
+ | `- [ ] ` / `- [x] ` | 未完成/已完成待办 |
239
+ | `` `...` `` | 代码块 |
240
+ | `---` | 分隔线 |
241
+
242
+ ### 箭头(编码 practiceDirection)
243
+
244
+ | 箭头 | 方向 | 格式 |
245
+ |:-----|:-----|:-----|
246
+ | ` → ` | forward | `text → backText` |
247
+ | ` ← ` | backward | `text ← backText` |
248
+ | ` ↔ ` | both | `text ↔ backText` |
249
+ | ` ↓ ` / ` ↓` | forward 多行 | 有 backText: `text ↓ backText`;无: `text ↓` |
250
+ | ` ↑ ` / ` ↑` | backward 多行 | 同上 |
251
+ | ` ↕ ` / ` ↕` | both 多行 | 同上 |
252
+
253
+ ### 元数据标记
254
+
255
+ | 标记 | 含义 |
256
+ |:-----|:-----|
257
+ | `type:concept` / `type:descriptor` / `type:portal` | Rem 类型 |
258
+ | `doc` | isDocument = true |
259
+ | `top` | 知识库顶层 Rem |
260
+ | `children:N` | 折叠的子节点数(深度超限) |
261
+ | `role:card-item` | 多行闪卡答案行 |
262
+ | `tag:Name(id)` | 标签 |
263
+ | `refs:id1,id2` | Portal 引用 |
264
+
265
+ ### 省略占位符(两种类型)
266
+
267
+ ```
268
+ <!--...elided 3 siblings (parent:id range:2-4 total:5)--> 精确省略:maxSiblings 超限,调大 maxSiblings
269
+ <!--...elided >=10 nodes (parent:id range:5-14 total:20)--> 非精确省略:maxNodes 耗尽,调大 maxNodes
270
+ ```
271
+
272
+ 精确省略保留前 70% + 后 30%,中间插入省略行。**省略占位符不可删除或修改**,否则报 `elided_modified` 错误。
273
+
274
+ ---
275
+
276
+ ## 7. edit-tree 结构编辑
277
+
278
+ ### 支持的操作
279
+
280
+ | 操作 | 方式 | 执行顺序 |
281
+ |:-----|:-----|:---------|
282
+ | 新增 | 在 newStr 中添加无 remId 的新行 | 1(从浅到深) |
283
+ | 移动 | 改变行的缩进/位置 | 2 |
284
+ | 重排 | 调换同级行顺序 | 3 |
285
+ | 删除 | 从 newStr 中移除带 remId 的行 | 4(从深到浅) |
286
+
287
+ 一次 edit-tree 可组合多种操作。
288
+
289
+ ### 禁止的操作
290
+
291
+ | 操作 | 错误类型 | 替代方案 |
292
+ |:-----|:---------|:---------|
293
+ | 修改已有行内容 | `content_modified` | 使用 `edit-rem` |
294
+ | 删除/修改/移动根节点 | `root_modified` | — |
295
+ | 删除有隐藏子节点的行 | `folded_delete` | 用更大 depth 重新 read-tree |
296
+ | 删除行但保留子节点 | `orphan_detected` | 必须同时删除所有子行 |
297
+ | 删除/修改省略占位符 | `elided_modified` | 用更大参数重新 read-tree |
298
+ | 缩进跳级 | `indent_skip` | 每级 2 空格,不可跳级 |
299
+
300
+ ### 新增行格式
301
+
302
+ 新增行可用 Markdown 前缀和箭头分隔符:
303
+
304
+ ```markdown
305
+ # 新标题
306
+ 新闪卡 → 答案
307
+ 概念 :: 定义
308
+ - [ ] 新待办
309
+ `代码块`
310
+ ```
311
+
312
+ ### 嵌套新增(一次创建父+子结构)
313
+
314
+ 新增行下面可以再嵌套新增行,缩进表示父子关系:
315
+
316
+ ```
317
+ newStr:
318
+ 父节点 ↓
319
+ 答案行 1
320
+ 答案行 2
321
+ 子节点 A <!--idA-->
322
+ ```
323
+
324
+ 创建顺序从浅到深,嵌套的子行会自动成为新创建父节点的 children。
325
+
326
+ ### practiceDirection 保护
327
+
328
+ SDK bug 自动修复:移动行进入多行闪卡父节点时自动设 `isCardItem=true` 并修正 practiceDirection;移出时自动清除。Rem 自身的合法 practiceDirection 会被保留。
329
+
330
+ ### str_replace 构造示例
331
+
332
+ ```bash
333
+ # 在 idA 前插入新行
334
+ --old-str ' 子节点 A <!--idA-->'
335
+ --new-str ' 新增行\n 子节点 A <!--idA-->'
336
+
337
+ # 删除叶子节点(注意尾部换行)
338
+ --old-str ' 叶子节点 <!--leaf-->\n'
339
+ --new-str ''
340
+
341
+ # 调换两个兄弟顺序
342
+ --old-str ' 节点 A <!--idA-->\n 节点 B <!--idB-->'
343
+ --new-str ' 节点 B <!--idB-->\n 节点 A <!--idA-->'
344
+ ```
345
+
346
+ ---
347
+
348
+ ## 8. edit-rem str_replace 要点
349
+
350
+ 操作对象是 `JSON.stringify(remObject, null, 2)` 的文本(缩进 2 空格的 JSON)。
351
+
352
+ ### 使用技巧
353
+
354
+ 1. **包含字段名**避免模糊匹配:`"\"type\": \"concept\""` 而非 `"concept"`
355
+ 2. 替换后必须是合法 JSON
356
+ 3. 修改 RichText 时注意 key 字母序:
357
+
358
+ ```
359
+ oldStr: "\"text\": [\n \"Hello\"\n ]"
360
+ newStr: "\"text\": [\n \"World\"\n ]"
361
+ ```
362
+
363
+ ### 20 个可编辑字段
364
+
365
+ ```
366
+ text, backText, type, isDocument, parent,
367
+ fontSize, highlightColor,
368
+ isTodo, todoStatus, isCode, isQuote, isListItem, isCardItem, isSlot, isProperty,
369
+ enablePractice, practiceDirection,
370
+ tags, sources, positionAmongstSiblings
371
+ ```
372
+
373
+ ### 特殊字段处理规则
374
+
375
+ | 字段 | 特殊行为 |
376
+ |:-----|:---------|
377
+ | `backText` | `null` → 调用 `setBackText([])`(清除背面);裸字符串自动包装为 `[string]` |
378
+ | `highlightColor` | `null` → 调用 `removePowerup('h')`(SDK 不接受 null) |
379
+ | `fontSize` | `null` → 调用 `setFontSize(undefined)`(恢复普通大小) |
380
+ | `todoStatus` | 依赖 `isTodo=true` 才生效;`null` 被跳过(清除 todo 应设 `isTodo=false`) |
381
+ | `type` | 不可设为 `portal`(只能通过 SDK `createPortal()` 创建) |
382
+ | `parent` + `positionAmongstSiblings` | 共享同一 SDK 调用 `setParent(parentId, position)`,**应在同一次 str_replace 中同时修改** |
383
+ | `tags` / `sources` | **Diff 机制**:对比当前 vs 目标数组,逐项 add/remove。必须列出完整目标数组,缺少的会被删除 |
384
+
385
+ ### 常用只读字段(修改只产生警告,不生效)
386
+
387
+ ```
388
+ id, children, createdAt, updatedAt, remsReferencingThis, remsBeingReferenced,
389
+ aliases, descendants, siblingRem, isTable, portalType, propertyType
390
+ ```
391
+
392
+ ### read-rem 输出模式
393
+
394
+ | 模式 | 字段数 | 用法 |
395
+ |:-----|:-------|:-----|
396
+ | 默认 | 34(RW + R) | 常用场景 |
397
+ | `--full` | 51(含低频 R-F) | 需要 Powerup 标识、时间戳等 |
398
+ | `--fields` | 自选 + id | 精确获取特定字段 |
399
+
400
+ 缓存始终存储完整 51 字段 RemObject,字段过滤仅影响输出。
401
+
402
+ ---
403
+
404
+ ## 9. 错误诊断速查
405
+
406
+ | 错误 | 原因 | 恢复 |
407
+ |:-----|:-----|:-----|
408
+ | 守护进程未运行 | 未 connect 或已超时 | `connect` |
409
+ | Plugin 未连接 | RemNote 未打开 | 打开 RemNote(health 三层:daemon→Plugin→SDK 链式依赖) |
410
+ | has not been read yet | 未先 read | 执行对应 read 命令(search 结果不算 read!) |
411
+ | has been modified since last read | 被外部修改 | 重新 read(必须,不可直接重试) |
412
+ | old_str not found | oldStr 不精确 | 检查引号、空格、换行、RichText key 顺序 |
413
+ | old_str matches N locations | oldStr 不够具体 | 扩大 oldStr 范围,包含更多上下文 |
414
+ | invalid JSON | newStr 破坏了 JSON 结构 | 检查引号、逗号、括号完整性(可直接重试) |
415
+ | content_modified | edit-tree 中改了行内容 | 用 edit-rem |
416
+ | orphan_detected | 删了父但留了子 | 同时删除所有子行 |
417
+ | folded_delete | 删了有隐藏子节点的行 | 用更大 depth 重新 read-tree |
418
+ | elided_modified | 删/改了省略占位符 | 用更大 depth/maxSiblings 重新 read-tree |
419
+ | indent_skip | 缩进跳级(如 0→4 空格) | 每级 2 空格,不可跳级 |
420
+ | Rem not found | remId 无效或已删除 | 用 search 重新定位 |
421
+
422
+ ---
423
+
424
+ ## 10. 配置
425
+
426
+ 配置文件:`.remnote-bridge.json`(项目根目录)
427
+
428
+ 关键默认值:WS 端口 3002、dev-server 8080、config 3003、超时 30 分钟、maxNodes 200、maxSiblings 20、readTreeDepth 3、readGlobeDepth -1(无限)、缓存上限 200 条。