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
@@ -0,0 +1,751 @@
1
+ # Overall — remnote-bridge Agent 操作指南
2
+
3
+ > 本文档面向 AI Agent(MCP / Skills 层),提供操作 RemNote 知识库所需的概念理解、命令决策和技术参考。
4
+ > 各命令的详细参数、输出格式和错误码见各自的 instruction 文件。
5
+
6
+ ---
7
+
8
+ ## 1. 本文档定位
9
+
10
+ 本文档是 Agent 操作 RemNote 的**总纲**。Agent 应按以下策略使用文档体系:
11
+
12
+ 1. **首次加载**:读本文件(overall.md),建立概念理解和操作框架
13
+ 2. **执行命令前**:读取对应命令的 instruction 文件(如 `read-tree.md`、`edit-rem.md`),获取详细参数和输出格式
14
+ 3. **遇到术语困惑**:查阅 `docs/vocabulary-definition/` 下的分类文件
15
+
16
+ ---
17
+
18
+ ## 2. RemNote 核心概念
19
+
20
+ > Agent 必须理解这些概念,才能正确翻译用户的自然语言请求。
21
+
22
+ ### 2.1 一切皆 Rem
23
+
24
+ RemNote 中**一切内容的基本单元都是 Rem**。文档、文件夹、闪卡、标签、属性——本质上都是 Rem 的不同形态,而非独立的数据类型。
25
+
26
+ | 用户说的 | 实际上是 | 区分方式 |
27
+ |:---------|:---------|:---------|
28
+ | 笔记 / 条目 / 项 | Rem | 最基本的数据单元 |
29
+ | 文档 / 页面 | Rem(`isDocument=true`) | 可作为独立页面打开 |
30
+ | 文件夹 / 目录 | Rem(Document 且子节点全是 Document) | UI 概念,SDK 无独立标记 |
31
+ | 闪卡 / 卡片 | **Card**——由 RemNote 根据 Rem 属性自动生成 | **不可直接操控**,修改 Rem 属性即可改变闪卡行为 |
32
+
33
+ **红线**:本项目不操控 Card / Flashcard。要改变闪卡行为,修改 Rem 的 `type`、`backText`、`practiceDirection`、`enablePractice` 等属性。
34
+
35
+ ### 2.2 用户语言 → CLI 操作映射表
36
+
37
+ Agent 的核心任务是将用户的自然语言请求翻译为 CLI 命令。以下映射表覆盖最常见场景:
38
+
39
+ #### 探索 / 读取
40
+
41
+ | 用户表述 | RemNote 概念 | CLI 命令 |
42
+ |:---------|:-------------|:---------|
43
+ | "看看知识库有什么"、"有哪些文档" | 知识库全局 Document 结构 | `read-globe` |
44
+ | "我现在在看什么"、"当前页面" | 用户当前焦点/页面 | `read-context` |
45
+ | "展开这个主题"、"看看下面有什么" | Rem 子树 | `read-tree <remId>` |
46
+ | "这个笔记的详细信息" | Rem 的完整属性 | `read-rem <remId>` |
47
+ | "搜索 X"、"查找关于 X 的内容" | 全文搜索 | `search <query>` |
48
+
49
+ #### 修改 / 写入
50
+
51
+ | 用户表述 | 操作类型 | CLI 命令 |
52
+ |:---------|:---------|:---------|
53
+ | "改文本"、"改标题"、"改颜色"、"改类型" | 修改 Rem 属性 | `edit-rem` |
54
+ | "创建一个概念定义 (::)" | 修改 type + backText | `edit-rem` |
55
+ | "新增一个子节点"、"添加笔记" | 结构操作:新增 | `edit-tree` |
56
+ | "删除这个"、"移除" | 结构操作:删除 | `edit-tree` |
57
+ | "移动到…下面" | 结构操作:移动 | `edit-tree` |
58
+ | "调整顺序"、"排在前面" | 结构操作:重排 | `edit-tree` |
59
+ | "把这段文字改成…" | 行内容修改 | `edit-rem`(**不是** edit-tree) |
60
+
61
+ **关键区分**:`edit-rem` 修改 Rem 的**属性**(文本、类型、格式等),`edit-tree` 修改 Rem 之间的**结构关系**(新增、删除、移动、重排)。edit-tree **禁止修改行内容**。
62
+
63
+ ### 2.3 Rem 的类型系统
64
+
65
+ Rem 有两个**独立维度**的类型:
66
+
67
+ #### type 字段(闪卡语义)
68
+
69
+ | type 值 | 触发分隔符 | 含义 | UI 表现 |
70
+ |:--------|:----------|:-----|:--------|
71
+ | `concept` | `::` 或 `::>` | 概念定义 | 文字**加粗** |
72
+ | `descriptor` | `;;` 或 `;;>` | 描述/属性 | 文字*斜体* |
73
+ | `default` | 无分隔符 / `>>` / `<<` / `<>` / `>>>` / `{{}}` | 普通 Rem | 正常字重 |
74
+ | `portal` | — | 嵌入引用容器 | 紫色左边框(**只读**,不可通过 setType 设置) |
75
+
76
+ #### isDocument 字段(页面语义)
77
+
78
+ `isDocument` 与 `type` 完全独立——一个 `concept` 类型的 Rem 可以同时是 Document。
79
+
80
+ ### 2.4 分隔符与闪卡
81
+
82
+ 用户输入的分隔符决定了 Rem 的 `type`、`backText` 和默认 `practiceDirection`。理解分隔符是理解用户创建闪卡意图的关键。
83
+
84
+ | 分隔符 | type | backText | 默认方向 | 用途 |
85
+ |:-------|:-----|:---------|:---------|:-----|
86
+ | (无) | `default` | `null` | — | 无闪卡行为 |
87
+ | `::` | `concept` | 后半部分 | `both` | 概念定义(CDF 框架) |
88
+ | `;;` | `descriptor` | 后半部分 | `forward` | 描述属性(CDF 框架) |
89
+ | `>>` | `default` | 后半部分 | `forward` | 正向问答 |
90
+ | `<<` | `default` | 后半部分 | `backward` | 反向问答 |
91
+ | `<>` | `default` | 后半部分 | `both` | 双向问答 |
92
+ | `>>>` | `default` | `null` | `forward` | 多行答案(子 Rem 为答案) |
93
+ | `::>` | `concept` | `null` | `both` | 概念型多行答案 |
94
+ | `;;>` | `descriptor` | `null` | `forward` | 描述型多行答案 |
95
+ | `{{}}` | `default` | `null` | `forward` | 完形填空(Cloze) |
96
+
97
+ **重要**:分隔符决定创建时的**默认**方向,用户可以事后独立修改 `practiceDirection`。`practiceDirection` 的取值:`forward`(正向)、`backward`(反向)、`both`(双向)、`none`(不练习)。
98
+
99
+ **CDF(Concept-Descriptor Framework)**:RemNote 推荐的知识结构化方法——`Concept` 是需要理解的概念,`Descriptor` 是概念的属性/描述。典型结构:
100
+
101
+ ```
102
+ 线性回归 :: 最基本的回归模型 ← Concept
103
+ 假设 ;; 因变量与自变量呈线性关系 ← Descriptor
104
+ 损失函数 ;; 均方误差 (MSE) ← Descriptor
105
+ ```
106
+
107
+ ### 2.5 三种链接机制
108
+
109
+ | 机制 | 用户操作 | 本质 | 编辑同步 | CLI 可见性 |
110
+ |:-----|:---------|:-----|:---------|:-----------|
111
+ | **Reference** | `[[` | RichText 中的引用元素 | 否(只是指针) | read-rem/read-tree 中显示为 `[[文本]]` |
112
+ | **Tag** | `##` | 将 Rem 作为标签附加 | 否(分类标记) | read-rem 的 `tags` 数组 |
113
+ | **Portal** | `((` | 嵌入 Rem 的实时视图 | 是(编辑同步) | read-tree 标记为 `type:portal` |
114
+
115
+ ### 2.6 Powerup 机制简述
116
+
117
+ RemNote 的格式设置(fontSize、highlightColor、isCode 等)底层通过 **Powerup 机制**实现:给 Rem 注入隐藏的系统 Tag + 创建隐藏的子 Rem 存储参数值。
118
+
119
+ 这些隐藏数据会混入 `tags` 和 `children` 数组,对 Agent 构成噪音。**默认自动过滤**(`includePowerup=false`)。详见第 11 节。
120
+
121
+ ### 2.7 术语深入参考
122
+
123
+ 需要更深入理解某个概念时,查阅对应的词汇定义文件:
124
+
125
+ | 主题 | 文件 |
126
+ |:-----|:-----|
127
+ | Rem / Document / RemType / RichText / Card | `docs/vocabulary-definition/01-core-data-model.md` |
128
+ | Parent / Child / Hierarchy / Breadcrumb | `docs/vocabulary-definition/02-hierarchy-and-navigation.md` |
129
+ | Reference / Tag / Powerup / Portal | `docs/vocabulary-definition/03-linking-and-references.md` |
130
+ | 分隔符 / CDF / practiceDirection / SRS | `docs/vocabulary-definition/04-flashcard-system.md` |
131
+ | RichText 元素 / 行内格式 / Slot vs Property | `docs/vocabulary-definition/05-formatting-and-richtext.md` |
132
+ | Property / PropertyType / Table / Template | `docs/vocabulary-definition/06-tables-and-properties.md` |
133
+ | Global Search / Search Portal / Query Language | `docs/vocabulary-definition/07-search-and-query.md` |
134
+
135
+ ---
136
+
137
+ ## 3. 系统架构与会话
138
+
139
+ ### 3.1 三层架构
140
+
141
+ ```
142
+ AI Agent(Claude Code / MCP Client)
143
+ │ 调用 CLI 命令(短进程,无状态)
144
+
145
+ remnote-bridge(命令层)
146
+ │ WebSocket IPC
147
+
148
+ daemon(守护进程,长生命周期)
149
+ ├── ws-server:接收请求,分发到 handlers,转发到 Plugin
150
+ ├── handlers:业务编排(缓存、防线、diff)
151
+ └── config-server:HTTP 配置界面
152
+ │ WebSocket 连接
153
+
154
+ remnote-plugin(桥接层,运行在浏览器中)
155
+ ├── bridge:WebSocket 客户端 + 消息路由
156
+ ├── services:业务操作(直接调用 RemNote SDK)
157
+ └── utils:纯函数(序列化、省略)
158
+
159
+
160
+ RemNote SDK → 知识库
161
+ ```
162
+
163
+ 核心设计:
164
+ - **CLI 命令是无状态短进程**:每次调用都是独立 OS 进程,执行完即退出
165
+ - **守护进程持有状态**:缓存、WS 连接、超时计时器都在 daemon 中
166
+ - **Plugin 运行在浏览器中**:通过 RemNote SDK 操作知识库,只响应 daemon 转发的请求
167
+
168
+ ### 3.2 会话生命周期
169
+
170
+ 一次**会话(Session)= 守护进程的生命周期**。
171
+
172
+ ```
173
+ connect → daemon 启动 → 会话开始
174
+ ↕ (业务命令:read-rem, edit-tree, search, ...)
175
+ disconnect → daemon 关闭 → 会话结束,缓存清空
176
+ ```
177
+
178
+ `connect` 启动三个服务:
179
+
180
+ | 服务 | 默认端口 | 用途 |
181
+ |:-----|:---------|:-----|
182
+ | WS Server | 3002 | CLI ↔ daemon ↔ Plugin 通信 |
183
+ | webpack-dev-server | 8080 | 热加载 Plugin 到 RemNote |
184
+ | ConfigServer | 3003 | HTTP 配置界面 |
185
+
186
+ 超时机制:daemon 默认 **30 分钟无活动**自动关闭。每次收到 CLI 请求时重置计时器。
187
+
188
+ ### 3.3 健康状态
189
+
190
+ `health` 命令检查三层状态:
191
+
192
+ | 检查项 | 含义 |
193
+ |:-------|:-----|
194
+ | daemon | 守护进程是否在运行 |
195
+ | plugin | Plugin 是否已通过 WS 连接 |
196
+ | sdk | RemNote SDK 是否就绪(知识库已加载) |
197
+
198
+ 三者全部就绪才能执行业务命令。
199
+
200
+ ---
201
+
202
+ ## 4. 命令体系
203
+
204
+ ### 4.1 命令速查表
205
+
206
+ #### 基础设施命令
207
+
208
+ | 命令 | 功能 | 需要 Plugin | 详细文档 |
209
+ |:-----|:-----|:------------|:---------|
210
+ | `connect` | 启动守护进程 | 否 | `connect.md` |
211
+ | `health` | 检查系统状态 | 否 | `health.md` |
212
+ | `disconnect` | 停止守护进程 | 否 | `disconnect.md` |
213
+
214
+ #### 读取命令
215
+
216
+ | 命令 | 功能 | 输入 | 缓存 | 详细文档 |
217
+ |:-----|:-----|:-----|:-----|:---------|
218
+ | `read-globe` | 知识库全局 Document 概览 | 展开参数 | 否 | `read-globe.md` |
219
+ | `read-context` | 当前上下文视图 | mode + 参数 | 否 | `read-context.md` |
220
+ | `read-tree` | 读取子树为 Markdown 大纲 | remId + 展开参数 | 是(`tree:`) | `read-tree.md` |
221
+ | `read-rem` | 读取单个 Rem 的 JSON 属性 | remId | 是(`rem:`) | `read-rem.md` |
222
+ | `search` | 全文搜索 | query | 否 | `search.md` |
223
+
224
+ #### 写入命令
225
+
226
+ | 命令 | 功能 | 前置条件 | 安全机制 | 详细文档 |
227
+ |:-----|:-----|:---------|:---------|:---------|
228
+ | `edit-rem` | str_replace 编辑 Rem JSON 字段 | 先 `read-rem` | 三道防线 | `edit-rem.md` |
229
+ | `edit-tree` | str_replace 编辑树结构 | 先 `read-tree` | 三道防线 + diff | `edit-tree.md` |
230
+
231
+ ### 4.2 探索决策指南
232
+
233
+ Agent 需要根据用户意图选择正确的读取命令:
234
+
235
+ ```
236
+ 用户想了解什么?
237
+ ├─ 知识库整体结构 → read-globe
238
+ │ 仅展示 Document 层级,非 Document 的 Rem 不展开
239
+ │ 适合:首次探索、了解知识库组织
240
+
241
+ ├─ 用户当前在看什么 → read-context
242
+ │ ├─ focus 模式(默认):以用户聚焦的 Rem 为中心的鱼眼视图
243
+ │ │ 焦点本身展开 depth=3,siblings 浅层展开,祖先路径可见
244
+ │ └─ page 模式:以当前打开页面为根展开子树
245
+
246
+ ├─ 某个具体 Rem 的子树 → read-tree <remId>
247
+ │ 完整展开子树(支持深度/节点预算控制)
248
+ │ 结果缓存,供 edit-tree 使用
249
+
250
+ ├─ 某个 Rem 的详细属性 → read-rem <remId>
251
+ │ 返回 51 字段的 RemObject JSON
252
+ │ 结果缓存,供 edit-rem 使用
253
+
254
+ └─ 按关键词搜索 → search <query>
255
+ 全文搜索,返回匹配的 Rem 列表
256
+ ⚠ 中文等无空格分词语言搜索效果差(详见 search.md)
257
+ ```
258
+
259
+ #### read-globe vs read-context vs read-tree 选择表
260
+
261
+ | 场景 | 命令 | 输出特点 |
262
+ |:-----|:-----|:---------|
263
+ | "看看知识库有什么" | `read-globe` | 仅 Document 层级,无内容 Rem |
264
+ | "我现在在编辑什么" | `read-context --mode focus` | 鱼眼视图,焦点处详细 |
265
+ | "当前页面的内容" | `read-context --mode page` | 以页面为根展开 |
266
+ | "展开某个主题的细节" | `read-tree <id>` | 完整子树,可缓存供编辑 |
267
+
268
+ #### read-globe 特性
269
+
270
+ - 获取所有顶层 Rem(`parent === null`),仅展开 Document 类型节点
271
+ - 非 Document 子 Rem 不递归展开(元数据中标注 `children:N`)
272
+ - 参数:`depth`(默认 -1 无限)、`maxNodes`(200)、`maxSiblings`(20)
273
+ - 不缓存结果
274
+ - 输出头部:`<!-- globe: 知识库概览 -->`
275
+
276
+ #### read-context 特性
277
+
278
+ **focus 模式**(默认):
279
+ - 获取用户当前聚焦的 Rem(需要用户在 RemNote 中点击某个 Rem)
280
+ - 鱼眼视图展开策略:焦点 depth=3,焦点的 siblings depth=1,叔伯节点 depth=0
281
+ - 向上追溯 `ancestorLevels` 层(默认 2)
282
+ - 焦点 Rem 前标记 `* ` 前缀
283
+ - 输出头部含 path 和 focus 信息
284
+
285
+ **page 模式**:
286
+ - 获取当前面板打开的页面 Rem
287
+ - 以该页面为根展开子树
288
+ - 参数:`depth`(默认 3)、`maxNodes`、`maxSiblings`
289
+ - 输出头部含 page 名和 breadcrumb path
290
+
291
+ 两者共同点:不缓存、Portal 感知、Powerup 噪音过滤、返回 breadcrumb。
292
+
293
+ ### 4.3 修改决策指南
294
+
295
+ ```
296
+ 用户想修改什么?
297
+ ├─ Rem 的属性(文本、类型、格式、标签等)
298
+ │ → edit-rem(操作对象:RemObject 的 JSON 文本)
299
+ │ → 前置:必须先 read-rem
300
+
301
+ ├─ 树的结构(新增、删除、移动、重排)
302
+ │ → edit-tree(操作对象:Markdown 大纲文本)
303
+ │ → 前置:必须先 read-tree
304
+
305
+ └─ 修改某行的文字内容
306
+ → edit-rem(不是 edit-tree!edit-tree 禁止修改行内容)
307
+ ```
308
+
309
+ #### edit-rem vs edit-tree 详细选择
310
+
311
+ | 操作 | 命令 | 原因 |
312
+ |:-----|:-----|:-----|
313
+ | 修改文本内容(text / backText) | `edit-rem` | 需要 JSON 级精度操作 RichText |
314
+ | 修改属性(type, fontSize, highlightColor...) | `edit-rem` | 属性级修改 |
315
+ | 修改标签(tags) | `edit-rem` | Diff 机制增删 |
316
+ | 新增子节点 | `edit-tree` | 结构操作 |
317
+ | 删除节点(含所有子节点) | `edit-tree` | 结构操作 |
318
+ | 移动节点到新父节点下 | `edit-tree` | 结构操作 |
319
+ | 调换同级节点顺序 | `edit-tree` | 结构操作 |
320
+
321
+ ### 4.4 标准工作流
322
+
323
+ ```
324
+ 1. connect ← 启动会话
325
+ 2. health ← 确认系统就绪
326
+ 3. read-globe ← 了解知识库结构(首次探索)
327
+ 或 read-context ← 了解用户当前上下文
328
+ 4. search "关键词" ← 定位目标 Rem(中文搜索可能需单字策略,详见 search.md)
329
+ 5. read-tree <id> ← 展开目标区域的子树
330
+ 6. read-rem <id> ← 读取详细属性(编辑前必需)
331
+ 7. edit-rem <id> ... ← 修改 Rem 属性
332
+ 或 edit-tree <id> ...← 修改树结构
333
+ 8. disconnect ← 结束会话
334
+ ```
335
+
336
+ **注意**:步骤 6 是 `edit-rem` 的强制前置条件,步骤 5 是 `edit-tree` 的强制前置条件。跳过会触发防线 1 错误。
337
+
338
+ ---
339
+
340
+ ## 5. 双模式 I/O(`--json`)
341
+
342
+ 所有命令支持两种调用模式:
343
+
344
+ ### 人类模式(默认)
345
+
346
+ ```bash
347
+ read-rem kLrIOHJLyMd8Y2lyA --fields text,type
348
+ read-tree kLrIOHJLyMd8Y2lyA --depth 2
349
+ edit-rem kLrIOHJLyMd8Y2lyA --old-str '"concept"' --new-str '"descriptor"'
350
+ ```
351
+
352
+ - 位置参数 = remId 或关键词
353
+ - 选项通过 `--flag` 传递
354
+ - 输出为人类可读格式
355
+
356
+ ### JSON 模式(`--json`)
357
+
358
+ ```bash
359
+ read-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA","fields":["text","type"]}'
360
+ read-tree --json '{"remId":"kLrIOHJLyMd8Y2lyA","depth":2}'
361
+ edit-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA","oldStr":"\"concept\"","newStr":"\"descriptor\""}'
362
+ ```
363
+
364
+ - **位置参数 = JSON 字符串**,所有参数打包在 JSON 对象中
365
+ - 输出为**单行合法 JSON**
366
+
367
+ **禁止混用**:`read-rem kLrIOHJLyMd8Y2lyA --json` 会失败(裸 remId 被当作 JSON 解析)。
368
+
369
+ ### JSON 输出结构
370
+
371
+ ```jsonc
372
+ // 成功
373
+ { "ok": true, "command": "read-rem", "timestamp": "...", ... }
374
+
375
+ // 失败
376
+ { "ok": false, "command": "read-rem", "error": "Rem not found", ... }
377
+ ```
378
+
379
+ 所有 JSON 输出都包含 `ok`(boolean)和 `command`(string)字段。
380
+
381
+ ---
382
+
383
+ ## 6. 通信协议与 action 映射
384
+
385
+ ### 消息格式
386
+
387
+ CLI → daemon 请求:
388
+ ```json
389
+ { "id": "uuid-v4", "action": "read_rem", "payload": { "remId": "kLr..." } }
390
+ ```
391
+
392
+ daemon → CLI 响应:
393
+ ```json
394
+ { "id": "uuid-v4(与请求匹配)", "result": { ... } }
395
+ ```
396
+
397
+ ### action 映射表
398
+
399
+ | action | CLI 命令 | Plugin service | 说明 |
400
+ |:-------|:---------|:---------------|:-----|
401
+ | `read_rem` | read-rem | readRem() | 直接转发 |
402
+ | `edit_rem` | edit-rem | — | daemon handler 编排 |
403
+ | `read_tree` | read-tree | readTree() | 直接转发 |
404
+ | `edit_tree` | edit-tree | — | daemon handler 编排 |
405
+ | `read_globe` | read-globe | readGlobe() | 直接转发 |
406
+ | `read_context` | read-context | readContext() | 直接转发 |
407
+ | `search` | search | search() | 直接转发 |
408
+ | `write_rem_fields` | —(内部) | writeRemFields() | edit-rem/edit-tree 的原子写入 |
409
+ | `create_rem` | —(内部) | createRem() | edit-tree 的原子创建 |
410
+ | `delete_rem` | —(内部) | deleteRem() | edit-tree 的原子删除 |
411
+ | `move_rem` | —(内部) | moveRem() | edit-tree 的原子移动 |
412
+ | `reorder_children` | —(内部) | reorderChildren() | edit-tree 的原子重排 |
413
+
414
+ `edit_rem` 和 `edit_tree` 在 daemon handler 中编排完成后,调用 `write_rem_fields`、`create_rem` 等原子操作。
415
+
416
+ ---
417
+
418
+ ## 7. RemObject 数据模型
419
+
420
+ RemObject 是本项目对 RemNote Rem 的标准化表示,包含 51 个字段。详细字段表见 `read-rem.md`。
421
+
422
+ ### 7.1 字段分类
423
+
424
+ | 标记 | 含义 | 数量 | 输出条件 |
425
+ |:-----|:-----|:-----|:---------|
426
+ | RW | 可读可写 | 20 | 默认输出 |
427
+ | R | 只读 | 14 | 默认输出 |
428
+ | R-F | 只读低频 | 17 | 仅 `--full` 输出 |
429
+
430
+ ### 7.2 核心字段速览
431
+
432
+ ```
433
+ 标识: id
434
+ 内容: text [RW], backText [RW]
435
+ 类型: type [RW], isDocument [RW]
436
+ 结构: parent [RW], children [R]
437
+ 格式: fontSize [RW], highlightColor [RW]
438
+ 状态: isTodo [RW], todoStatus [RW], isCode [RW], isQuote [RW],
439
+ isListItem [RW], isCardItem [RW], isSlot [RW], isProperty [RW]
440
+ 练习: enablePractice [RW], practiceDirection [RW]
441
+ 关联: tags [RW], sources [RW], aliases [R], remsReferencingThis [R]
442
+ 位置: positionAmongstSiblings [RW]
443
+ 时间: createdAt [R], updatedAt [R]
444
+ Portal:portalType [R], portalDirectlyIncludedRem [R]
445
+ ```
446
+
447
+ ### 7.3 RichText 格式
448
+
449
+ `text` 和 `backText` 字段使用 RichText 格式——一个 JSON 数组,元素为纯字符串或格式化对象:
450
+
451
+ ```json
452
+ [
453
+ "普通文本",
454
+ { "i": "m", "text": "粗体文本", "b": true },
455
+ { "i": "q", "_id": "remId123" },
456
+ { "i": "i", "url": "https://...", "width": 200, "height": 100 }
457
+ ]
458
+ ```
459
+
460
+ | `i` 值 | 类型 | 核心字段 |
461
+ |:-------|:-----|:---------|
462
+ | (纯 string) | 纯文本 | — |
463
+ | `"m"` | 带格式文本 | `text` + `b`/`l`/`u`/`h`/`code`/`cId`/`iUrl` |
464
+ | `"q"` | Rem 引用 | `_id`(被引用 Rem ID) |
465
+ | `"i"` | 图片 | `url`, `width`, `height` |
466
+ | `"x"` | LaTeX | `text` |
467
+ | `"a"` | 音频 | `url` |
468
+
469
+ **序列化确定性**:RichText 对象内部按 key 字母序排列(`sortRichTextKeys()`),确保同一内容的 JSON 始终一致。这对 edit-rem 的 str_replace 和并发检测至关重要。
470
+
471
+ ---
472
+
473
+ ## 8. Markdown 大纲格式
474
+
475
+ read-tree / read-globe / read-context 的输出核心是 Markdown 大纲文本。edit-tree 基于此大纲进行结构编辑。
476
+
477
+ ### 8.1 行结构
478
+
479
+ 每行的格式为:
480
+
481
+ ```
482
+ {缩进}{Markdown 前缀}{内容}{箭头分隔符}{backText} <!-- {remId} {元数据标记} -->
483
+ ```
484
+
485
+ - **缩进**:每级 2 个空格(根节点 0 个空格)
486
+ - **remId**:该行对应的 Rem ID(新增行无 remId)
487
+ - **元数据标记**:空格分隔的属性标记
488
+
489
+ ### 8.2 Markdown 前缀
490
+
491
+ | 前缀 | 含义 | 对应字段 |
492
+ |:-----|:-----|:---------|
493
+ | `# ` | H1 标题 | `fontSize: 'H1'` |
494
+ | `## ` | H2 标题 | `fontSize: 'H2'` |
495
+ | `### ` | H3 标题 | `fontSize: 'H3'` |
496
+ | `- [ ] ` | 未完成待办 | `isTodo: true, todoStatus: 'Unfinished'` |
497
+ | `- [x] ` | 已完成待办 | `isTodo: true, todoStatus: 'Finished'` |
498
+ | `` `...` `` | 代码块 | `isCode: true` |
499
+ | `---` | 分隔线 | Divider Powerup |
500
+
501
+ ### 8.3 箭头分隔符
502
+
503
+ 箭头编码 `practiceDirection`(闪卡练习方向),不编码 type(type 由元数据标记承载)。
504
+
505
+ | 箭头 | 格式 | practiceDirection |
506
+ |:-----|:-----|:------------------|
507
+ | ` → ` | `text → backText` | forward |
508
+ | ` ← ` | `text ← backText` | backward |
509
+ | ` ↔ ` | `text ↔ backText` | both |
510
+ | ` ↓` | `text ↓`(尾部,无 backText 多行) | forward |
511
+ | ` ↑` | `text ↑` | backward |
512
+ | ` ↕` | `text ↕` | both |
513
+
514
+ 多行箭头(↓↑↕)带 backText 时格式为 `text ↓ backText`。
515
+
516
+ ### 8.4 元数据标记
517
+
518
+ 行尾 `<!-- -->` 注释中的标记:
519
+
520
+ | 标记 | 含义 |
521
+ |:-----|:-----|
522
+ | `type:concept` / `type:descriptor` / `type:portal` | Rem 类型(default 时不标记) |
523
+ | `doc` | isDocument = true |
524
+ | `top` | 知识库顶层 Rem |
525
+ | `children:N` | 折叠的子节点数量(深度超限时) |
526
+ | `role:card-item` | isCardItem = true(多行闪卡答案行) |
527
+ | `tag:Name(id)` | 已附加的标签 |
528
+ | `refs:id1,id2,...` | Portal 引用的 Rem ID |
529
+
530
+ ### 8.5 省略占位符
531
+
532
+ 当子节点数超过 maxSiblings 或全局预算耗尽时,插入省略行:
533
+
534
+ ```
535
+ <!--...elided 3 siblings (parent:remIdB range:2-4 total:5)-->
536
+ <!--...elided >=10 nodes (parent:remIdB range:5-14 total:20)-->
537
+ ```
538
+
539
+ - `siblings`(精确省略):子节点超过 maxSiblings,保留前 70% + 后 30%
540
+ - `>=N nodes`(非精确省略):全局 maxNodes 预算耗尽
541
+
542
+ ### 8.6 预算控制参数
543
+
544
+ | 参数 | 默认值 | 说明 |
545
+ |:-----|:-------|:-----|
546
+ | `depth` | 3 | 递归展开深度(-1 = 无限) |
547
+ | `maxNodes` | 200 | 全局节点上限 |
548
+ | `maxSiblings` | 20 | 每个父节点下展示的 children 上限 |
549
+ | `ancestorLevels` | 0 | 向上追溯祖先层数(上限 10) |
550
+ | `includePowerup` | false | 是否包含 Powerup 系统数据 |
551
+
552
+ 完整示例:
553
+
554
+ ```markdown
555
+ # 数据结构 <!--kLr type:concept doc top-->
556
+ ## 线性结构 <!--ABC type:concept-->
557
+ 数组 → Array <!--DEF-->
558
+ 链表 ↓ <!--GHI type:concept-->
559
+ 单向链表 <!--JKL role:card-item-->
560
+ 双向链表 <!--MNO role:card-item-->
561
+ - [x] 复习完成 <!--PQR-->
562
+ `quickSort()` <!--STU-->
563
+ ## 树结构 <!--VWX type:concept children:8-->
564
+ <!--...elided 3 siblings (parent:kLr range:3-5 total:6)-->
565
+ ```
566
+
567
+ ---
568
+
569
+ ## 9. 缓存与安全机制
570
+
571
+ ### 9.1 缓存存储
572
+
573
+ 缓存存储在**守护进程内存中**(Map 结构),不持久化到磁盘。
574
+
575
+ | 前缀 | 用途 | 写入命令 |
576
+ |:-----|:-----|:---------|
577
+ | `rem:{remId}` | RemObject JSON | read-rem |
578
+ | `tree:{remId}` | Markdown 大纲 | read-tree |
579
+ | `tree-depth:{remId}` 等 | read-tree 参数 | read-tree |
580
+
581
+ - LRU 淘汰:上限 200 条目
582
+ - disconnect 关闭 daemon 时缓存自动消失
583
+ - **没有 TTL**——三道防线的并发检测已能捕获所有陈旧数据
584
+
585
+ ### 9.2 三道防线
586
+
587
+ `edit-rem` 和 `edit-tree` 使用 str_replace 语义编辑数据。为防止数据损坏,实施三道防线:
588
+
589
+ #### 防线 1:缓存存在性检查
590
+
591
+ ```
592
+ 必须先 read → 建立缓存 → 再 edit
593
+ ```
594
+
595
+ 未缓存的 Rem 不允许编辑。确保 Agent 看到的数据和即将编辑的数据是同一份。
596
+
597
+ #### 防线 2:乐观并发检测
598
+
599
+ ```
600
+ edit 时重新从 SDK 读取最新数据 → 与缓存严格比较
601
+ ```
602
+
603
+ 如果 Rem 在 read 之后被外部修改(用户在 RemNote UI 中编辑、其他 Agent 修改等),数据不一致时拒绝编辑,**且不更新缓存**——迫使 Agent 重新 read。
604
+
605
+ #### 防线 3:str_replace 精确匹配
606
+
607
+ ```
608
+ oldStr 必须在目标文本中恰好匹配 1 次
609
+ ```
610
+
611
+ - 0 次匹配:`old_str not found`
612
+ - \>1 次匹配:`matches N locations, make old_str more specific`
613
+ - 精确 1 次:执行替换
614
+
615
+ ### 9.3 缓存更新行为
616
+
617
+ | 场景 | 缓存行为 |
618
+ |:-----|:---------|
619
+ | 写入全部成功 | 从 SDK 重新读取最新状态 → **更新缓存** |
620
+ | 防线拒绝 | **不更新缓存**(迫使 Agent 重新 read) |
621
+ | 部分写入失败 | **不更新缓存** |
622
+
623
+ ---
624
+
625
+ ## 10. edit-tree 结构编辑
626
+
627
+ edit-tree 使用 str_replace 对 Markdown 大纲进行结构编辑。详细文档见 `edit-tree.md`。
628
+
629
+ ### 10.1 支持的操作
630
+
631
+ | 操作 | 实现方式 | 执行顺序 |
632
+ |:-----|:---------|:---------|
633
+ | **新增行** | 在 newStr 中添加无 remId 的新行 | 1(从浅到深) |
634
+ | **移动行** | 改变行的缩进层级或位置 | 2 |
635
+ | **重排行** | 调换同级行的顺序 | 3 |
636
+ | **删除行** | 从 newStr 中移除带 remId 的行 | 4(从深到浅) |
637
+
638
+ ### 10.2 禁止的操作
639
+
640
+ | 操作 | 错误类型 | 替代方案 |
641
+ |:-----|:---------|:---------|
642
+ | 修改已有行内容 | `content_modified` | 使用 `edit-rem` |
643
+ | 删除根节点 | `root_modified` | — |
644
+ | 删除有隐藏子节点的行 | `folded_delete` | 用更大的 depth 重新 read-tree |
645
+ | 删除节点但保留子节点 | `orphan_detected` | 必须同时删除所有子行 |
646
+ | 删除/修改省略占位符 | `elided_modified` | 用更大参数重新 read-tree 展开 |
647
+
648
+ ### 10.3 新增行格式
649
+
650
+ 新增行可以使用 Markdown 前缀和箭头分隔符:
651
+
652
+ ```markdown
653
+ # 新标题 H1
654
+ 新闪卡 → 答案
655
+ - [ ] 新待办
656
+ `代码块内容`
657
+ ```
658
+
659
+ 支持的箭头分隔符:`→`(forward)、`←`(backward)、`↔`(both)、`↓`(多行 forward)、`↑`(多行 backward)、`↕`(多行 both)。
660
+
661
+ 新增行可以嵌套(通过缩进表示父子关系),支持创建带子节点的结构。
662
+
663
+ ---
664
+
665
+ ## 11. Powerup 噪音过滤
666
+
667
+ RemNote 的格式设置通过 Powerup 机制实现,会向 Rem 注入隐藏的系统 Tag 和子 Rem。
668
+
669
+ **默认行为**(`includePowerup=false`):自动过滤以下内容:
670
+ - **tags**:移除 `isPowerup === true` 的系统 Tag
671
+ - **children**:移除 `isPowerupProperty / isPowerupSlot / isPowerupPropertyListItem / isPowerupEnum` 的隐藏子 Rem
672
+
673
+ 过滤统计通过 `powerupFiltered: { tags: N, children: N }` 返回。
674
+
675
+ `--includePowerup` 可恢复完整数据(调试或分析 Powerup 机制时使用)。
676
+
677
+ ---
678
+
679
+ ## 12. 配置系统
680
+
681
+ 配置文件位于项目根目录:`.remnote-bridge.json`
682
+
683
+ ```json
684
+ {
685
+ "wsPort": 3002,
686
+ "devServerPort": 8080,
687
+ "configPort": 3003,
688
+ "daemonTimeoutMinutes": 30,
689
+ "defaults": {
690
+ "maxNodes": 200,
691
+ "maxSiblings": 20,
692
+ "cacheMaxSize": 200,
693
+ "readTreeDepth": 3,
694
+ "readTreeAncestorLevels": 0,
695
+ "readTreeIncludePowerup": false,
696
+ "readGlobeDepth": -1,
697
+ "readContextMode": "focus",
698
+ "readContextAncestorLevels": 2,
699
+ "readContextDepth": 3,
700
+ "searchNumResults": 20
701
+ }
702
+ }
703
+ ```
704
+
705
+ - 文件不存在时使用全部默认值
706
+ - 三个端口不允许冲突
707
+
708
+ ---
709
+
710
+ ## 13. 错误处理速查
711
+
712
+ Agent 遇到错误时的诊断和恢复指南:
713
+
714
+ ### 连接问题
715
+
716
+ | 错误 | 原因 | 恢复 |
717
+ |:-----|:-----|:-----|
718
+ | 守护进程未运行 | 未执行 connect 或 daemon 已超时 | 执行 `connect` |
719
+ | Plugin 未连接 | RemNote 未打开或插件未加载 | 打开 RemNote 并确认插件加载 |
720
+ | SDK 未就绪 | 知识库尚未加载完成 | 等待并重试 `health` |
721
+
722
+ ### 防线错误
723
+
724
+ | 错误 | 原因 | 恢复 |
725
+ |:-----|:-----|:-----|
726
+ | has not been read yet | 未先执行 read-rem / read-tree | 执行对应 read 命令后重试 |
727
+ | has been modified since last read | Rem 在 read 和 edit 之间被外部修改 | 重新执行 read 获取最新状态后重试 |
728
+
729
+ ### str_replace 错误
730
+
731
+ | 错误 | 原因 | 恢复 |
732
+ |:-----|:-----|:-----|
733
+ | old_str not found | oldStr 在目标文本中不存在 | 检查 oldStr 是否精确匹配(含引号、空格、换行) |
734
+ | old_str matches N locations | oldStr 匹配到多个位置 | 扩大 oldStr 范围,包含更多上下文以唯一定位 |
735
+ | invalid JSON | 替换后的文本不是合法 JSON | 检查 newStr 的引号、逗号、括号完整性 |
736
+
737
+ ### edit-tree 专用错误
738
+
739
+ | 错误 | 原因 | 恢复 |
740
+ |:-----|:-----|:-----|
741
+ | Content modification not allowed | edit-tree 中修改了已有行内容 | 使用 `edit-rem` 修改内容 |
742
+ | orphan_detected | 删除了父节点但保留了子节点 | 同时删除所有子行 |
743
+ | folded_delete | 删除了有隐藏子节点的行 | 用更大 depth 重新 read-tree |
744
+
745
+ ### 数据问题
746
+
747
+ | 错误 | 原因 | 恢复 |
748
+ |:-----|:-----|:-----|
749
+ | Rem not found | remId 无效或 Rem 已被删除 | 使用 `search` 重新定位 |
750
+ | Failed to update field 'type': Portal... | 尝试将 type 设为 portal | Portal 只能通过 SDK 专用 API 创建 |
751
+ | Field '...' is read-only | 修改了只读字段 | 该字段不可通过 edit-rem 修改(警告,非阻断) |