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.
- package/dist/cli/commands/connect.d.ts +12 -0
- package/dist/cli/commands/connect.js +124 -0
- package/dist/cli/commands/disconnect.d.ts +11 -0
- package/dist/cli/commands/disconnect.js +100 -0
- package/dist/cli/commands/edit-rem.d.ts +13 -0
- package/dist/cli/commands/edit-rem.js +83 -0
- package/dist/cli/commands/edit-tree.d.ts +14 -0
- package/dist/cli/commands/edit-tree.js +67 -0
- package/dist/cli/commands/health.d.ts +12 -0
- package/dist/cli/commands/health.js +100 -0
- package/dist/cli/commands/install-skill.d.ts +6 -0
- package/dist/cli/commands/install-skill.js +39 -0
- package/dist/cli/commands/read-context.d.ts +20 -0
- package/dist/cli/commands/read-context.js +77 -0
- package/dist/cli/commands/read-globe.d.ts +16 -0
- package/dist/cli/commands/read-globe.js +60 -0
- package/dist/cli/commands/read-rem.d.ts +16 -0
- package/dist/cli/commands/read-rem.js +80 -0
- package/dist/cli/commands/read-tree.d.ts +17 -0
- package/dist/cli/commands/read-tree.js +85 -0
- package/dist/cli/commands/search.d.ts +12 -0
- package/dist/cli/commands/search.js +65 -0
- package/dist/cli/config.d.ts +55 -0
- package/dist/cli/config.js +139 -0
- package/dist/cli/daemon/daemon.d.ts +11 -0
- package/dist/cli/daemon/daemon.js +186 -0
- package/dist/cli/daemon/dev-server.d.ts +26 -0
- package/dist/cli/daemon/dev-server.js +81 -0
- package/dist/cli/daemon/pid.d.ts +34 -0
- package/dist/cli/daemon/pid.js +67 -0
- package/dist/cli/daemon/send-request.d.ts +24 -0
- package/dist/cli/daemon/send-request.js +92 -0
- package/dist/cli/handlers/context-read-handler.d.ts +18 -0
- package/dist/cli/handlers/context-read-handler.js +24 -0
- package/dist/cli/handlers/edit-handler.d.ts +30 -0
- package/dist/cli/handlers/edit-handler.js +133 -0
- package/dist/cli/handlers/globe-read-handler.d.ts +17 -0
- package/dist/cli/handlers/globe-read-handler.js +23 -0
- package/dist/cli/handlers/read-handler.d.ts +16 -0
- package/dist/cli/handlers/read-handler.js +78 -0
- package/dist/cli/handlers/rem-cache.d.ts +19 -0
- package/dist/cli/handlers/rem-cache.js +63 -0
- package/dist/cli/handlers/tree-edit-handler.d.ts +30 -0
- package/dist/cli/handlers/tree-edit-handler.js +188 -0
- package/dist/cli/handlers/tree-parser.d.ts +95 -0
- package/dist/cli/handlers/tree-parser.js +506 -0
- package/dist/cli/handlers/tree-read-handler.d.ts +28 -0
- package/dist/cli/handlers/tree-read-handler.js +53 -0
- package/dist/cli/main.d.ts +7 -0
- package/dist/cli/main.js +300 -0
- package/dist/cli/protocol.d.ts +39 -0
- package/dist/cli/protocol.js +35 -0
- package/dist/cli/server/config-server.d.ts +26 -0
- package/dist/cli/server/config-server.js +363 -0
- package/dist/cli/server/ws-server.d.ts +68 -0
- package/dist/cli/server/ws-server.js +335 -0
- package/dist/cli/utils/output.d.ts +11 -0
- package/dist/cli/utils/output.js +13 -0
- package/dist/mcp/daemon-client.d.ts +31 -0
- package/dist/mcp/daemon-client.js +99 -0
- package/dist/mcp/index.d.ts +7 -0
- package/dist/mcp/index.js +68 -0
- package/dist/mcp/instructions.d.ts +1 -0
- package/dist/mcp/instructions.js +249 -0
- package/dist/mcp/resources/edit-tree-guide.d.ts +1 -0
- package/dist/mcp/resources/edit-tree-guide.js +197 -0
- package/dist/mcp/resources/error-reference.d.ts +1 -0
- package/dist/mcp/resources/error-reference.js +132 -0
- package/dist/mcp/resources/outline-format.d.ts +1 -0
- package/dist/mcp/resources/outline-format.js +104 -0
- package/dist/mcp/resources/rem-object-fields.d.ts +1 -0
- package/dist/mcp/resources/rem-object-fields.js +331 -0
- package/dist/mcp/resources/separator-flashcard.d.ts +1 -0
- package/dist/mcp/resources/separator-flashcard.js +120 -0
- package/dist/mcp/tools/edit-tools.d.ts +5 -0
- package/dist/mcp/tools/edit-tools.js +47 -0
- package/dist/mcp/tools/infra-tools.d.ts +5 -0
- package/dist/mcp/tools/infra-tools.js +43 -0
- package/dist/mcp/tools/read-tools.d.ts +5 -0
- package/dist/mcp/tools/read-tools.js +195 -0
- package/dist/mcp/types.d.ts +12 -0
- package/dist/mcp/types.js +4 -0
- package/docs/instruction/connect.md +158 -0
- package/docs/instruction/disconnect.md +146 -0
- package/docs/instruction/edit-rem.md +509 -0
- package/docs/instruction/edit-tree.md +419 -0
- package/docs/instruction/health.md +159 -0
- package/docs/instruction/overall.md +751 -0
- package/docs/instruction/read-context.md +353 -0
- package/docs/instruction/read-globe.md +206 -0
- package/docs/instruction/read-rem.md +476 -0
- package/docs/instruction/read-tree.md +428 -0
- package/docs/instruction/search.md +196 -0
- package/package.json +41 -0
- package/remnote-plugin/package.json +48 -0
- package/remnote-plugin/postcss.config.js +5 -0
- package/remnote-plugin/public/bridge-icon.svg +8 -0
- package/remnote-plugin/public/manifest.json +22 -0
- package/remnote-plugin/src/bridge/message-router.ts +57 -0
- package/remnote-plugin/src/bridge/websocket-client.ts +245 -0
- package/remnote-plugin/src/index.css +1 -0
- package/remnote-plugin/src/services/breadcrumb.ts +26 -0
- package/remnote-plugin/src/services/create-rem.ts +59 -0
- package/remnote-plugin/src/services/delete-rem.ts +29 -0
- package/remnote-plugin/src/services/index.ts +16 -0
- package/remnote-plugin/src/services/move-rem.ts +39 -0
- package/remnote-plugin/src/services/powerup-filter.ts +31 -0
- package/remnote-plugin/src/services/read-context.ts +368 -0
- package/remnote-plugin/src/services/read-globe.ts +197 -0
- package/remnote-plugin/src/services/read-rem.ts +284 -0
- package/remnote-plugin/src/services/read-tree.ts +222 -0
- package/remnote-plugin/src/services/rem-builder.ts +124 -0
- package/remnote-plugin/src/services/reorder-children.ts +61 -0
- package/remnote-plugin/src/services/search.ts +56 -0
- package/remnote-plugin/src/services/write-rem-fields.ts +254 -0
- package/remnote-plugin/src/settings.ts +12 -0
- package/remnote-plugin/src/style.css +45 -0
- package/remnote-plugin/src/test-scripts/AGENTS.md +46 -0
- package/remnote-plugin/src/test-scripts/test-actions.ts +230 -0
- package/remnote-plugin/src/test-scripts/test-powerup-rendering.ts +722 -0
- package/remnote-plugin/src/test-scripts/test-rem-type-mapping.ts +283 -0
- package/remnote-plugin/src/test-scripts/test-richtext-builder.ts +207 -0
- package/remnote-plugin/src/test-scripts/test-richtext-matrix.ts +332 -0
- package/remnote-plugin/src/test-scripts/test-richtext-remaining.ts +245 -0
- package/remnote-plugin/src/test-scripts/test-rw-fields.ts +399 -0
- package/remnote-plugin/src/types.ts +419 -0
- package/remnote-plugin/src/utils/elision.ts +45 -0
- package/remnote-plugin/src/utils/index.ts +10 -0
- package/remnote-plugin/src/utils/tree-serializer.ts +269 -0
- package/remnote-plugin/src/widgets/bridge_widget.tsx +170 -0
- package/remnote-plugin/src/widgets/index.tsx +82 -0
- package/remnote-plugin/tailwind.config.js +7 -0
- package/remnote-plugin/tsconfig.json +21 -0
- package/remnote-plugin/webpack.config.js +125 -0
- package/skill/SKILL.md +428 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# edit-tree
|
|
2
|
+
|
|
3
|
+
> 通过 str_replace 对 Markdown 大纲进行结构编辑(行级增/删/移/重排),禁止修改已有行内容。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 功能
|
|
8
|
+
|
|
9
|
+
`edit-tree` 基于 `read-tree` 缓存的 Markdown 大纲,使用 str_replace 替换文本片段,解析新旧大纲的结构差异,自动生成并执行原子操作。
|
|
10
|
+
|
|
11
|
+
核心能力:
|
|
12
|
+
- **新增行**:在大纲中插入无 remId 的新行,支持 Markdown 前缀和箭头分隔符
|
|
13
|
+
- **删除行**:从大纲中移除带 remId 的行(必须同时删除所有子行)
|
|
14
|
+
- **移动行**:改变行的缩进层级或移到不同父节点下
|
|
15
|
+
- **重排行**:调换同级行的顺序
|
|
16
|
+
- 三道防线保障数据安全(缓存存在性、乐观并发检测、精确匹配)
|
|
17
|
+
- SDK bug 自动修复(practiceDirection 保护)
|
|
18
|
+
|
|
19
|
+
**核心限制**:仅支持结构操作,**禁止修改已有行的内容**——内容修改走 `edit-rem`。
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 用法
|
|
24
|
+
|
|
25
|
+
### 人类模式
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
remnote-bridge edit-tree <remId> --old-str <text> --new-str <text>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| 参数/选项 | 类型 | 必需 | 说明 |
|
|
32
|
+
|-----------|------|:----:|------|
|
|
33
|
+
| `remId` | string(位置参数) | 是 | 已 read-tree 过的子树根节点 Rem ID |
|
|
34
|
+
| `--old-str` | string | 是 | 待替换的原始文本片段(必须精确匹配缓存中的大纲) |
|
|
35
|
+
| `--new-str` | string | 是 | 替换后的新文本片段 |
|
|
36
|
+
|
|
37
|
+
### JSON 模式
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
remnote-bridge edit-tree --json '{"remId":"kLrIOHJLyMd8Y2lyA","oldStr":" 旧行 <!--id1-->","newStr":" 新行\n 旧行 <!--id1-->"}'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## JSON 输入参数
|
|
46
|
+
|
|
47
|
+
| 字段 | 类型 | 必需 | 说明 |
|
|
48
|
+
|------|------|:----:|------|
|
|
49
|
+
| `remId` | string | 是 | 子树根节点 Rem ID |
|
|
50
|
+
| `oldStr` | string | 是 | 待替换的原始文本片段 |
|
|
51
|
+
| `newStr` | string | 是 | 替换后的新文本片段 |
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## JSON 输出
|
|
56
|
+
|
|
57
|
+
### 成功
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"ok": true,
|
|
62
|
+
"command": "edit-tree",
|
|
63
|
+
"operations": [
|
|
64
|
+
{ "type": "create", "content": "新节点", "parentId": "abc123", "position": 0 },
|
|
65
|
+
{ "type": "move", "remId": "def456", "fromParentId": "abc123", "toParentId": "ghi789", "position": 1 },
|
|
66
|
+
{ "type": "reorder", "parentId": "abc123", "order": ["child1", "child2", "child3"] },
|
|
67
|
+
{ "type": "delete", "remId": "xyz789" }
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 无结构变更
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"ok": true,
|
|
77
|
+
"command": "edit-tree",
|
|
78
|
+
"operations": []
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 缓存未建立
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"ok": false,
|
|
87
|
+
"command": "edit-tree",
|
|
88
|
+
"error": "Tree rooted at kLrIOHJLyMd8Y2lyA has not been read yet. Use read-tree first."
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 并发冲突
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"ok": false,
|
|
97
|
+
"command": "edit-tree",
|
|
98
|
+
"error": "Tree rooted at kLrIOHJLyMd8Y2lyA has been modified since last read-tree. Please read-tree again."
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### old_str 匹配失败
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"ok": false,
|
|
107
|
+
"command": "edit-tree",
|
|
108
|
+
"error": "old_str not found in the tree outline of kLrIOHJLyMd8Y2lyA"
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"ok": false,
|
|
115
|
+
"command": "edit-tree",
|
|
116
|
+
"error": "old_str matches 3 locations in the tree outline of kLrIOHJLyMd8Y2lyA. Make old_str more specific to match exactly once."
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 禁止操作
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"ok": false,
|
|
125
|
+
"command": "edit-tree",
|
|
126
|
+
"error": "Content modification of existing Rem is not allowed in tree edit mode.",
|
|
127
|
+
"modified_rems": [
|
|
128
|
+
{ "remId": "abc123", "original_content": "旧文本", "new_content": "新文本" }
|
|
129
|
+
],
|
|
130
|
+
"hint": "Use edit-rem abc123 --old-str ... --new-str ... for content changes."
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### daemon 不可达
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"ok": false,
|
|
139
|
+
"command": "edit-tree",
|
|
140
|
+
"error": "守护进程未运行,请先执行 remnote-bridge connect"
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 三道防线
|
|
147
|
+
|
|
148
|
+
### 防线 1:缓存存在性检查
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
必须先 read-tree → 建立缓存 → 再 edit-tree
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
检查 `cache.get('tree:' + remId)` 是否存在。不存在则拒绝编辑。
|
|
155
|
+
|
|
156
|
+
### 防线 2:乐观并发检测
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
用 read-tree 时的相同参数(depth, maxNodes, maxSiblings)重新从 Plugin 获取最新大纲 → 与缓存对比
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
- 缓存中同时存储了 `tree-depth:`、`tree-maxNodes:`、`tree-maxSiblings:` 用于复现查询
|
|
163
|
+
- 如果最新大纲与缓存不一致,拒绝编辑且**不更新缓存**——迫使 Agent 重新 read-tree
|
|
164
|
+
|
|
165
|
+
### 防线 3:str_replace 精确匹配
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
oldStr 必须在缓存大纲中恰好匹配 1 次
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
- 0 次匹配:`old_str not found`
|
|
172
|
+
- \>1 次匹配:`matches N locations, make old_str more specific`
|
|
173
|
+
- 精确 1 次:执行替换,得到修改后的大纲
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 支持的操作
|
|
178
|
+
|
|
179
|
+
### 新增行
|
|
180
|
+
|
|
181
|
+
在 newStr 中添加**无 remId 注释**的新行。新行可以使用 Markdown 前缀和箭头分隔符来设置属性。
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
oldStr:
|
|
185
|
+
子节点 A <!--idA-->
|
|
186
|
+
|
|
187
|
+
newStr:
|
|
188
|
+
新增节点
|
|
189
|
+
子节点 A <!--idA-->
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### 新增行的 Markdown 前缀
|
|
193
|
+
|
|
194
|
+
| 前缀 | 效果 |
|
|
195
|
+
|------|------|
|
|
196
|
+
| `# text` | 创建 H1 标题 |
|
|
197
|
+
| `## text` | 创建 H2 标题 |
|
|
198
|
+
| `### text` | 创建 H3 标题 |
|
|
199
|
+
| `- [ ] text` | 创建未完成待办 |
|
|
200
|
+
| `- [x] text` | 创建已完成待办 |
|
|
201
|
+
| `` `text` `` | 创建代码块 |
|
|
202
|
+
| `---` | 创建分隔线 |
|
|
203
|
+
|
|
204
|
+
#### 新增行的箭头分隔符
|
|
205
|
+
|
|
206
|
+
| 箭头 | 格式 | 效果 |
|
|
207
|
+
|------|------|------|
|
|
208
|
+
| ` → ` | `问题 → 答案` | 创建 forward 闪卡(单行) |
|
|
209
|
+
| ` ← ` | `问题 ← 答案` | 创建 backward 闪卡(单行) |
|
|
210
|
+
| ` ↔ ` | `问题 ↔ 答案` | 创建 both 闪卡(单行) |
|
|
211
|
+
| ` ↓` | `问题 ↓` | 创建 forward 多行闪卡(子节点为答案) |
|
|
212
|
+
| ` ↑` | `问题 ↑` | 创建 backward 多行闪卡 |
|
|
213
|
+
| ` ↕` | `问题 ↕` | 创建 both 多行闪卡 |
|
|
214
|
+
|
|
215
|
+
#### 嵌套新增
|
|
216
|
+
|
|
217
|
+
新增行下面可以再嵌套新增行,通过缩进表示父子关系:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
newStr:
|
|
221
|
+
父节点 ↓
|
|
222
|
+
答案行 1
|
|
223
|
+
答案行 2
|
|
224
|
+
子节点 A <!--idA-->
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
嵌套新增行的父 ID 通过内部占位标记 `__new_N__` 管理,创建顺序保证从浅到深。
|
|
228
|
+
|
|
229
|
+
### 删除行
|
|
230
|
+
|
|
231
|
+
从 newStr 中移除带 remId 的行。**必须同时删除该行的所有可见子行**,否则报 orphan_detected 错误。
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
oldStr:
|
|
235
|
+
子节点 A <!--idA-->
|
|
236
|
+
孙节点 A1 <!--idA1-->
|
|
237
|
+
子节点 B <!--idB-->
|
|
238
|
+
|
|
239
|
+
newStr:
|
|
240
|
+
子节点 B <!--idB-->
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
删除操作按深度**从深到浅**执行(先删子后删父),确保 RemNote SDK 不会拒绝操作。
|
|
244
|
+
|
|
245
|
+
### 移动行
|
|
246
|
+
|
|
247
|
+
改变行的缩进级别或位置,使其移动到新的父节点下:
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
oldStr:
|
|
251
|
+
子节点 A <!--idA-->
|
|
252
|
+
目标行 <!--idT-->
|
|
253
|
+
子节点 B <!--idB-->
|
|
254
|
+
|
|
255
|
+
newStr:
|
|
256
|
+
子节点 A <!--idA-->
|
|
257
|
+
子节点 B <!--idB-->
|
|
258
|
+
目标行 <!--idT-->
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### 重排行
|
|
262
|
+
|
|
263
|
+
调换同级行的顺序:
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
oldStr:
|
|
267
|
+
子节点 A <!--idA-->
|
|
268
|
+
子节点 B <!--idB-->
|
|
269
|
+
子节点 C <!--idC-->
|
|
270
|
+
|
|
271
|
+
newStr:
|
|
272
|
+
子节点 C <!--idC-->
|
|
273
|
+
子节点 A <!--idA-->
|
|
274
|
+
子节点 B <!--idB-->
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## 禁止的操作
|
|
280
|
+
|
|
281
|
+
| 操作 | 错误类型 | 错误信息 | 替代方案 |
|
|
282
|
+
|------|----------|----------|----------|
|
|
283
|
+
| 修改已有行内容 | `content_modified` | Content modification of existing Rem is not allowed in tree edit mode. | 使用 `edit-rem` |
|
|
284
|
+
| 删除/修改根节点 | `root_modified` | Root node cannot be changed, deleted or moved. | — |
|
|
285
|
+
| 删除有隐藏子节点的行 | `folded_delete` | Cannot delete {id} because it has {N} hidden children. | 用更大的 depth 重新 read-tree |
|
|
286
|
+
| 删除行但保留子节点 | `orphan_detected` | Cannot delete {id} because it has children that were not removed. | 同时删除所有子行 |
|
|
287
|
+
| 删除/修改省略占位符 | `elided_modified` | Cannot delete or modify elided region directly. | 用更大的 depth/maxSiblings 重新 read-tree 展开 |
|
|
288
|
+
| 缩进跳级 | `indent_skip` | 缩进跳级:行 ... 的缩进级别为 N,但找不到上一级的父节点。 | 检查缩进是否正确(每级 2 空格) |
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 操作执行顺序
|
|
293
|
+
|
|
294
|
+
diff 算法生成的操作按以下顺序排列并执行(在 `diffTrees` 中已排好):
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
1. Create(新增) — 按出现顺序,从浅到深
|
|
298
|
+
2. Move(移动) — 父节点变更的行
|
|
299
|
+
3. Reorder(重排) — 同父节点内顺序变更
|
|
300
|
+
4. Delete(删除) — 按深度从深到浅
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
每个操作通过 `forwardToPlugin` 调用对应的原子 SDK 操作:
|
|
304
|
+
|
|
305
|
+
| 操作 | Plugin action | payload |
|
|
306
|
+
|------|---------------|---------|
|
|
307
|
+
| create | `create_rem` + `write_rem_fields` | content, parentId, position + Markdown 属性 |
|
|
308
|
+
| delete | `delete_rem` | remId |
|
|
309
|
+
| move | `move_rem` + 条件性 `write_rem_fields` | remId, newParentId, position |
|
|
310
|
+
| reorder | `reorder_children` | parentId, order[] |
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## practiceDirection 保护机制
|
|
315
|
+
|
|
316
|
+
RemNote SDK 存在已知 bug:
|
|
317
|
+
|
|
318
|
+
- `setIsCardItem(true)` 会**偷偷设置** `practiceDirection: "forward"`
|
|
319
|
+
- `setIsCardItem(false)` **不会清除** `practiceDirection`(不对称行为)
|
|
320
|
+
|
|
321
|
+
### 对策
|
|
322
|
+
|
|
323
|
+
**新增行(create)**:当父节点是多行闪卡(含 ↓↑↕ 箭头)时,新创建的子行需要 `isCardItem: true`。设置后立即用 `practiceDirection: 'none'` 覆盖 SDK 副作用。
|
|
324
|
+
|
|
325
|
+
**移动行(move)**:
|
|
326
|
+
- 移入多行父节点:设 `isCardItem: true` + `practiceDirection: 'none'`
|
|
327
|
+
- 移出多行父节点:设 `isCardItem: false` + `practiceDirection: 'none'`
|
|
328
|
+
- 例外:如果 Rem 自身有合法的 practiceDirection(箭头分隔符),保留不覆盖
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 缓存行为
|
|
333
|
+
|
|
334
|
+
| 场景 | 缓存行为 |
|
|
335
|
+
|------|----------|
|
|
336
|
+
| 全部操作成功 | 用相同参数重新 read-tree,更新 `tree:` 缓存 |
|
|
337
|
+
| 防线失败(1-3) | **不更新缓存**,迫使 Agent 重新 read-tree |
|
|
338
|
+
| Diff 错误 | **不更新缓存** |
|
|
339
|
+
| 操作执行中异常 | **不更新缓存**(已执行的操作保留,无回滚) |
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 退出码
|
|
344
|
+
|
|
345
|
+
| 退出码 | 含义 | 触发条件 |
|
|
346
|
+
|:------:|------|----------|
|
|
347
|
+
| 0 | 成功 | 操作成功或无结构变更(noop) |
|
|
348
|
+
| 1 | 业务错误 | 防线失败、Diff 错误、执行异常 |
|
|
349
|
+
| 2 | daemon 不可达 | daemon 未运行或 WS 连接失败 |
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## 输出字段说明
|
|
354
|
+
|
|
355
|
+
| 字段 | 类型 | 说明 |
|
|
356
|
+
|------|------|------|
|
|
357
|
+
| `operations` | TreeOp[] | 执行的原子操作列表 |
|
|
358
|
+
| `operations[].type` | string | 操作类型:`create` / `delete` / `move` / `reorder` |
|
|
359
|
+
| `error` | string | 失败时的错误消息 |
|
|
360
|
+
| `details` | object | Diff 错误时的详细信息(modified_rems、hint 等) |
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## 内部流程
|
|
365
|
+
|
|
366
|
+
```
|
|
367
|
+
1. CLI 解析参数(remId, oldStr, newStr)
|
|
368
|
+
2. noop 检查:oldStr === newStr → 直接返回 ok
|
|
369
|
+
3. sendRequest → WS → daemon
|
|
370
|
+
4. daemon TreeEditHandler:
|
|
371
|
+
├─ 防线 1: cache.get('tree:' + remId) 存在?
|
|
372
|
+
├─ 防线 2: 用缓存的 depth/maxNodes/maxSiblings 重新 read-tree → 对比
|
|
373
|
+
├─ 防线 3: countOccurrences(cachedOutline, oldStr) === 1?
|
|
374
|
+
├─ modifiedOutline = cachedOutline.replace(oldStr, newStr)
|
|
375
|
+
├─ 解析新旧大纲为树(parseOutline)
|
|
376
|
+
├─ 对比差异(diffTrees)
|
|
377
|
+
│ ├─ 根节点校验
|
|
378
|
+
│ ├─ 省略行防线
|
|
379
|
+
│ ├─ 内容变更检测
|
|
380
|
+
│ ├─ 折叠删除防线
|
|
381
|
+
│ ├─ 孤儿检测
|
|
382
|
+
│ └─ 生成操作列表(create → move → reorder → delete)
|
|
383
|
+
├─ 逐项执行操作(forwardToPlugin 调用原子操作)
|
|
384
|
+
│ ├─ create: create_rem + write_rem_fields
|
|
385
|
+
│ ├─ move: move_rem + isCardItem 同步
|
|
386
|
+
│ ├─ reorder: reorder_children
|
|
387
|
+
│ └─ delete: delete_rem
|
|
388
|
+
├─ 全部成功 → 重新 read-tree 更新缓存
|
|
389
|
+
└─ 返回 { ok: true, operations }
|
|
390
|
+
5. CLI 格式化输出
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## 常见使用模式
|
|
396
|
+
|
|
397
|
+
### 在指定位置插入新行
|
|
398
|
+
|
|
399
|
+
```bash
|
|
400
|
+
remnote-bridge edit-tree kLr --old-str ' 子节点 A <!--idA-->' --new-str ' 新增行\n 子节点 A <!--idA-->'
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### 删除一个叶子节点
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
remnote-bridge edit-tree kLr --old-str ' 叶子节点 <!--leaf-->\n' --new-str ''
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### 调换两个兄弟的顺序
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
remnote-bridge edit-tree kLr --old-str ' 节点 A <!--idA-->\n 节点 B <!--idB-->' --new-str ' 节点 B <!--idB-->\n 节点 A <!--idA-->'
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### 将节点移到另一个父节点下
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
remnote-bridge edit-tree kLr --old-str ' 旧父 <!--oldP-->\n 目标 <!--target-->\n 新父 <!--newP-->' --new-str ' 旧父 <!--oldP-->\n 新父 <!--newP-->\n 目标 <!--target-->'
|
|
419
|
+
```
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# health
|
|
2
|
+
|
|
3
|
+
> 检查系统三层状态(daemon / Plugin / SDK),诊断连接问题。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 功能
|
|
8
|
+
|
|
9
|
+
`health` 分两步检查系统状态:
|
|
10
|
+
|
|
11
|
+
1. **本地检查**:读取 PID 文件,确认 daemon 进程是否存活
|
|
12
|
+
2. **远程检查**:通过 WS 连接 daemon,获取 Plugin 连接状态和 SDK 就绪状态
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 用法
|
|
17
|
+
|
|
18
|
+
### 人类模式
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
remnote-bridge health
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
输出示例(全部健康):
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
✅ 守护进程 运行中(PID: 12345,已运行 5 分钟)
|
|
28
|
+
✅ Plugin 已连接
|
|
29
|
+
✅ SDK 就绪
|
|
30
|
+
|
|
31
|
+
超时: 25 分钟后自动关闭
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
输出示例(部分不健康):
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
✅ 守护进程 运行中(PID: 12345,已运行 2 分钟)
|
|
38
|
+
❌ Plugin 未连接
|
|
39
|
+
❌ SDK 未就绪
|
|
40
|
+
|
|
41
|
+
超时: 28 分钟后自动关闭
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
输出示例(daemon 未运行):
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
❌ 守护进程 未运行
|
|
48
|
+
❌ Plugin 未连接
|
|
49
|
+
❌ SDK 不可用
|
|
50
|
+
|
|
51
|
+
提示: 执行 `remnote-bridge connect` 启动守护进程
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### JSON 模式
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
remnote-bridge --json health
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## JSON 输出
|
|
63
|
+
|
|
64
|
+
### 全部健康
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"ok": true,
|
|
69
|
+
"command": "health",
|
|
70
|
+
"exitCode": 0,
|
|
71
|
+
"daemon": { "running": true, "pid": 12345, "reachable": true, "uptime": 300 },
|
|
72
|
+
"plugin": { "connected": true },
|
|
73
|
+
"sdk": { "ready": true },
|
|
74
|
+
"timeoutRemaining": 1500,
|
|
75
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Plugin 未连接
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"ok": false,
|
|
84
|
+
"command": "health",
|
|
85
|
+
"exitCode": 1,
|
|
86
|
+
"daemon": { "running": true, "pid": 12345, "reachable": true, "uptime": 120 },
|
|
87
|
+
"plugin": { "connected": false },
|
|
88
|
+
"sdk": { "ready": false },
|
|
89
|
+
"timeoutRemaining": 1680,
|
|
90
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### daemon 未运行
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"ok": false,
|
|
99
|
+
"command": "health",
|
|
100
|
+
"exitCode": 2,
|
|
101
|
+
"daemon": { "running": false },
|
|
102
|
+
"plugin": { "connected": false },
|
|
103
|
+
"sdk": { "ready": false },
|
|
104
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 检查项详解
|
|
111
|
+
|
|
112
|
+
| 检查项 | 检查方式 | 含义 |
|
|
113
|
+
|--------|----------|------|
|
|
114
|
+
| **daemon** | PID 文件 + `kill(pid, 0)` 探活 | 守护进程是否在运行且可达 |
|
|
115
|
+
| **plugin** | daemon 内部的 `pluginConnected` 状态 | RemNote Plugin 是否已通过 WS 连接到 daemon |
|
|
116
|
+
| **sdk** | Plugin 的 hello 握手中的 `sdkReady` 字段 | RemNote SDK 是否就绪(知识库已加载,可调用 API) |
|
|
117
|
+
|
|
118
|
+
### 三层关系
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
daemon 运行 → Plugin 连接 → SDK 就绪
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
每一层都依赖前一层。如果 daemon 未运行,Plugin 和 SDK 一定不可用。
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 退出码
|
|
129
|
+
|
|
130
|
+
| 退出码 | 含义 | 触发条件 |
|
|
131
|
+
|--------|------|----------|
|
|
132
|
+
| 0 | 全部健康 | daemon 运行 + Plugin 已连接 + SDK 就绪 |
|
|
133
|
+
| 1 | 部分不健康 | daemon 运行但 Plugin 未连接或 SDK 未就绪 |
|
|
134
|
+
| 2 | 不可达 | daemon 未运行,或运行但 WS 连接失败 |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 输出字段说明
|
|
139
|
+
|
|
140
|
+
| 字段 | 类型 | 说明 |
|
|
141
|
+
|------|------|------|
|
|
142
|
+
| `daemon.running` | boolean | 进程是否存活 |
|
|
143
|
+
| `daemon.pid` | number | 进程 ID(仅运行时) |
|
|
144
|
+
| `daemon.reachable` | boolean | WS 连接是否成功(仅运行时) |
|
|
145
|
+
| `daemon.uptime` | number | 运行秒数(仅可达时) |
|
|
146
|
+
| `plugin.connected` | boolean | Plugin WS 连接是否建立 |
|
|
147
|
+
| `sdk.ready` | boolean | RemNote SDK 是否就绪 |
|
|
148
|
+
| `timeoutRemaining` | number | 距自动关闭的剩余秒数(仅可达时) |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 常见问题诊断
|
|
153
|
+
|
|
154
|
+
| 症状 | 可能原因 | 解决方案 |
|
|
155
|
+
|------|----------|----------|
|
|
156
|
+
| daemon 未运行 | 未执行 connect / 已超时关闭 | 执行 `connect` |
|
|
157
|
+
| daemon 运行但不可达 | WS 端口被占用或配置不匹配 | 检查 `.remnote-bridge.json` 中的 `wsPort` |
|
|
158
|
+
| Plugin 未连接 | RemNote 未打开 / Plugin 未安装 / URL 不匹配 | 打开 RemNote,确认 Plugin 中的 WS URL 设置 |
|
|
159
|
+
| SDK 未就绪 | 知识库加载中 / Plugin 异常 | 等待几秒后重试,或刷新 RemNote 页面 |
|