remnote-bridge 0.1.13 → 0.1.14

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 (37) hide show
  1. package/README.md +141 -28
  2. package/README.zh-CN.md +368 -0
  3. package/dist/cli/commands/health.js +231 -112
  4. package/dist/cli/commands/read-rem-in-tree.js +84 -0
  5. package/dist/cli/config.js +2 -0
  6. package/dist/cli/daemon/registry.js +8 -0
  7. package/dist/cli/handlers/patch-engine.js +347 -0
  8. package/dist/cli/handlers/read-handler.js +2 -53
  9. package/dist/cli/handlers/rem-field-filter.js +102 -0
  10. package/dist/cli/handlers/tree-edit-handler.js +67 -7
  11. package/dist/cli/handlers/tree-read-handler.js +4 -1
  12. package/dist/cli/handlers/tree-rem-read-handler.js +73 -0
  13. package/dist/cli/main.js +53 -2
  14. package/dist/cli/server/ws-server.js +9 -1
  15. package/dist/mcp/daemon-client.js +22 -2
  16. package/dist/mcp/instructions.js +54 -7
  17. package/dist/mcp/tools/edit-tools.js +7 -2
  18. package/dist/mcp/tools/infra-tools.js +20 -11
  19. package/dist/mcp/tools/read-tools.js +88 -2
  20. package/package.json +1 -1
  21. package/remnote-plugin/dist/index-sandbox.js +24 -24
  22. package/remnote-plugin/dist/index.js +24 -24
  23. package/remnote-plugin/src/bridge/message-router.ts +3 -0
  24. package/remnote-plugin/src/services/read-rem-in-tree.ts +43 -0
  25. package/remnote-plugin/src/services/read-rem.ts +15 -0
  26. package/remnote-plugin/src/services/read-tree.ts +5 -0
  27. package/skills/remnote-bridge/SKILL.md +37 -4
  28. package/skills/remnote-bridge/instructions/connect.md +12 -1
  29. package/skills/remnote-bridge/instructions/disconnect.md +5 -0
  30. package/skills/remnote-bridge/instructions/edit-tree.md +71 -2
  31. package/skills/remnote-bridge/instructions/health.md +81 -53
  32. package/skills/remnote-bridge/instructions/overall.md +33 -8
  33. package/skills/remnote-bridge/instructions/read-rem-in-tree.md +100 -0
  34. package/skills/remnote-bridge/instructions/read-rem.md +30 -11
  35. package/skills/remnote-bridge-test/SKILL.md +847 -0
  36. package/skills/remnote-bridge-test/references/regression-suite.md +960 -0
  37. package/skills/remnote-bridge-test/references/verification-guide.md +161 -0
@@ -44,6 +44,7 @@ Agent 的核心任务是将用户的自然语言请求翻译为 CLI 命令。以
44
44
  | "我现在在看什么"、"当前页面" | 用户当前焦点/页面 | `read-context` |
45
45
  | "展开这个主题"、"看看下面有什么" | Rem 子树 | `read-tree <remId>` |
46
46
  | "这个笔记的详细信息" | Rem 的完整属性 | `read-rem <remId>` |
47
+ | "展开子树并获取所有属性" | 子树大纲 + 节点属性 | `read-rem-in-tree <remId>` |
47
48
  | "搜索 X"、"查找关于 X 的内容" | 全文搜索 | `search <query>` |
48
49
 
49
50
  #### 修改 / 写入
@@ -239,7 +240,7 @@ remnote-bridge disconnect --instance work
239
240
  | 2 | 29120 | 29121 | 29122 |
240
241
  | 3 | 29130 | 29131 | 29132 |
241
242
 
242
- **实例名解析优先级**:CLI `--instance` > 环境变量 `REMNOTE_BRIDGE_INSTANCE` > 默认值 `default`。
243
+ **实例名解析优先级**:CLI `--instance` > 环境变量 `REMNOTE_BRIDGE_INSTANCE` > 默认值 `default`。`headless` 是保留实例名,不可用于 `--instance`(会报错),必须使用 `--headless` 全局选项。
243
244
 
244
245
  **Plugin 自动发现**:Plugin 启动后通过 `/api/discovery` 获取其孪生 daemon 的连接信息(WS 端口、槽位索引等),自动建立连接。一个 Plugin 可同时连接最多 4 个 daemon。
245
246
 
@@ -282,6 +283,7 @@ remnote-bridge disconnect --instance work
282
283
  | `read-context` | 当前上下文视图 | mode + 参数 | 否 | `read-context.md` |
283
284
  | `read-tree` | 读取子树为 Markdown 大纲 | remId + 展开参数 | 是(`tree:`) | `read-tree.md` |
284
285
  | `read-rem` | 读取单个 Rem 的 JSON 属性 | remId | 是(`rem:`) | `read-rem.md` |
286
+ | `read-rem-in-tree` | 子树大纲 + 节点属性一次获取 | remId + 展开参数 + 过滤参数 | 是(`tree:` + `rem:`) | `read-rem-in-tree.md` |
285
287
  | `search` | 全文搜索 | query | 否 | `search.md` |
286
288
 
287
289
  #### 写入命令
@@ -309,10 +311,17 @@ Agent 需要根据用户意图选择正确的读取命令:
309
311
  ├─ 某个具体 Rem 的子树 → read-tree <remId>
310
312
  │ 完整展开子树(支持深度/节点预算控制)
311
313
  │ 结果缓存,供 edit-tree 使用
314
+ │ ⚠️ 如果需要读取子树后对其中多个节点执行 edit-rem,请改用 read-rem-in-tree
312
315
 
313
316
  ├─ 某个 Rem 的详细属性 → read-rem <remId>
314
317
  │ 返回 51 字段的 RemObject JSON
315
318
  │ 结果缓存,供 edit-rem 使用
319
+ │ ⚠️ 如果需要读取多个 Rem 属性(≥3 个)且在同一子树下,请改用 read-rem-in-tree
320
+
321
+ ├─ 子树结构 + 每个节点的详细属性 → read-rem-in-tree <remId>
322
+ │ read-tree + read-rem 的合体,一次调用同时获取大纲和 RemObject
323
+ │ 同时建立 tree 和 rem 双重缓存,供 edit-tree 和 edit-rem 使用
324
+ │ 默认 maxNodes=50(比 read-tree 的 200 低,因每节点开销大)
316
325
 
317
326
  └─ 按关键词搜索 → search <query>
318
327
  全文搜索,返回匹配的 Rem 列表
@@ -327,6 +336,7 @@ Agent 需要根据用户意图选择正确的读取命令:
327
336
  | "我现在在编辑什么" | `read-context --mode focus` | 鱼眼视图,焦点处详细 |
328
337
  | "当前页面的内容" | `read-context --mode page` | 以页面为根展开 |
329
338
  | "展开某个主题的细节" | `read-tree <id>` | 完整子树,可缓存供编辑 |
339
+ | "展开子树并查看每个节点属性" | `read-rem-in-tree <id>` | 大纲 + RemObject,双重缓存 |
330
340
 
331
341
  #### read-globe 特性
332
342
 
@@ -390,14 +400,28 @@ Agent 需要根据用户意图选择正确的读取命令:
390
400
  4. read-globe ← 了解知识库结构(首次探索)
391
401
  或 read-context ← 了解用户当前上下文
392
402
  5. search "关键词" ← 定位目标 Rem(中文搜索可能需单字策略,详见 search.md)
393
- 6. read-tree <id> 展开目标区域的子树
394
- 7. read-rem <id> 读取详细属性(编辑前必需)
395
- 8. edit-rem <id> ... ← 修改 Rem 属性
403
+ 6a. [单节点] read-tree <id> + read-rem <id> 各自建立缓存
404
+ 6b. [多节点] read-rem-in-tree <id> 一次建立双重缓存(推荐 ≥3 个节点需修改时)
405
+ 7. edit-rem <id> ... ← 修改 Rem 属性
396
406
  或 edit-tree <id> ...← 修改树结构
397
407
  9. disconnect ← 结束会话
398
408
  ```
399
409
 
400
- **注意**:步骤 7`edit-rem` 的强制前置条件,步骤 6 是 `edit-tree` 的强制前置条件。跳过会触发防线 1 错误。步骤 2 是必须的——connect 后不引导用户加载插件就直接调用业务命令,会报"Plugin 未连接"错误。
410
+ **注意**:步骤 6a/6b 是 edit-rem / edit-tree 的强制前置条件。跳过会触发防线 1 错误。步骤 2 是必须的——connect 后不引导用户加载插件就直接调用业务命令,会报"Plugin 未连接"错误。
411
+
412
+ ### 4.5 批量标注工作流(课本划重点场景)
413
+
414
+ 当需要对一棵子树中的多个节点进行富文本标注(行级高亮、行内荧光、粗体等)时:
415
+
416
+ 1. read-rem-in-tree <id> --maxNodes 50 -- 一次获取大纲 + 所有 RemObject
417
+ 2. 从 remObjects 中定位需要标注的节点
418
+ 3. 对每个目标节点 edit-rem 设置格式:
419
+ - 行级高亮:changes.highlightColor = "Yellow"/"Red"/...
420
+ - 行内荧光:changes.text = [..., {"h": 3, "i": "m", "text": "关键词"}, ...]
421
+ - 粗体:changes.text = [..., {"b": true, "i": "m", "text": "核心概念"}, ...]
422
+ 4. 如需结构变更(如新增/移动节点),直接 edit-tree(tree 缓存已就绪)
423
+
424
+ 关键:read-rem-in-tree 同时建立了 tree 和 rem 两种缓存,后续 edit-tree 和 edit-rem 都无需再单独 read。
401
425
 
402
426
  ---
403
427
 
@@ -465,6 +489,7 @@ daemon → CLI 响应:
465
489
  | `read_rem` | read-rem | readRem() | 直接转发 |
466
490
  | `edit_rem` | edit-rem | — | daemon handler 编排 |
467
491
  | `read_tree` | read-tree | readTree() | 直接转发 |
492
+ | `read_rem_in_tree` | read-rem-in-tree | readRemInTree() | daemon handler 编排 + Plugin 转发 |
468
493
  | `edit_tree` | edit-tree | — | daemon handler 编排 |
469
494
  | `read_globe` | read-globe | readGlobe() | 直接转发 |
470
495
  | `read_context` | read-context | readContext() | 直接转发 |
@@ -690,9 +715,9 @@ read-tree / read-globe / read-context 的输出核心是 Markdown 大纲文本
690
715
 
691
716
  | 前缀 | 用途 | 写入命令 |
692
717
  |:-----|:-----|:---------|
693
- | `rem:{remId}` | RemObject 对象 | read-rem |
694
- | `tree:{remId}` | Markdown 大纲 | read-tree |
695
- | `tree-depth:{remId}` 等 | read-tree 参数 | read-tree |
718
+ | `rem:{remId}` | RemObject 对象 | read-rem, read-rem-in-tree |
719
+ | `tree:{remId}` | Markdown 大纲 | read-tree, read-rem-in-tree |
720
+ | `tree-depth:{remId}` 等 | read-tree 参数 | read-tree, read-rem-in-tree |
696
721
 
697
722
  - LRU 淘汰:上限 200 条目
698
723
  - disconnect 关闭 daemon 时缓存自动消失
@@ -0,0 +1,100 @@
1
+ # read-rem-in-tree — 子树大纲 + 节点属性一次获取
2
+
3
+ > `read_tree` + `read_rem` 的完美结合体。一次调用同时获取 Markdown 大纲和每个节点的完整 RemObject JSON,建立双重缓存。
4
+
5
+ ---
6
+
7
+ ## 适用场景
8
+
9
+ - 需要同时查看子树结构和节点详细属性(如批量编辑前的全量读取)
10
+ - 一次调用建立 tree 缓存(供 `edit_tree`)和 rem 缓存(供 `edit_rem`)
11
+ - 替代连续调用 `read_tree` + 多次 `read_rem` 的场景
12
+
13
+ ## 不适用场景
14
+
15
+ - 只需大纲不需属性 → 用 `read_tree`(更轻量)
16
+ - 只需单个 Rem 属性 → 用 `read_rem`
17
+ - 大规模子树(>50 节点) → 每节点 40+ SDK 调用,性能开销大
18
+
19
+ ---
20
+
21
+ ## 命令格式
22
+
23
+ ```bash
24
+ # 人类模式
25
+ remnote-bridge read-rem-in-tree <remId> [options]
26
+
27
+ # JSON 模式
28
+ remnote-bridge --json read-rem-in-tree '{"remId":"...","depth":3,"maxNodes":50}'
29
+ ```
30
+
31
+ ## 参数
32
+
33
+ | 参数 | 类型 | 默认值 | 说明 |
34
+ |:-----|:-----|:-------|:-----|
35
+ | `remId` | string | **必需** | 子树根节点的 Rem ID |
36
+ | `depth` | number | 3 | 递归展开深度(-1 = 无限) |
37
+ | `maxNodes` | number | **50** | 全局节点总预算(注意比 read_tree 的 200 低) |
38
+ | `maxSiblings` | number | 20 | 单个父节点下最大可见子节点数 |
39
+ | `ancestorLevels` | number | 0 | 向上追溯祖先层数(上限 10) |
40
+ | `fields` | string[] | - | RemObject 字段过滤(只返回指定子集) |
41
+ | `full` | boolean | false | 返回全部 51 个 RemObject 字段 |
42
+ | `includePowerup` | boolean | false | 包含 Powerup 系统数据 |
43
+
44
+ ## 输出
45
+
46
+ ### JSON 模式
47
+
48
+ ```jsonc
49
+ {
50
+ "ok": true,
51
+ "command": "read-rem-in-tree",
52
+ "data": {
53
+ "rootId": "kLr...",
54
+ "depth": 3,
55
+ "nodeCount": 15,
56
+ "outline": "# 数据结构 <!--kLr type:concept doc-->\n ...",
57
+ "remObjects": {
58
+ "kLr": { "id": "kLr", "text": [...], "type": "concept", ... },
59
+ "ABC": { "id": "ABC", "text": [...], ... }
60
+ }
61
+ },
62
+ "ancestors": [...], // 可选
63
+ "cacheOverridden": {...}, // 可选
64
+ "powerupFiltered": {...} // 可选
65
+ }
66
+ ```
67
+
68
+ ### 核心字段
69
+
70
+ - `outline`:Markdown 大纲文本,与 `read_tree` 输出格式完全一致
71
+ - `remObjects`:扁平 map `{ remId → RemObject }`,每个 RemObject 与 `read_rem` 输出一致
72
+ - 默认启用 Token Slimming(省略默认值字段)
73
+ - `fields` / `full` 参数控制过滤行为
74
+
75
+ ## 缓存行为
76
+
77
+ | 缓存 Key | 内容 | 用途 |
78
+ |:---------|:-----|:-----|
79
+ | `tree:{remId}` | outline 大纲文本 | `edit_tree` 前置缓存 |
80
+ | `tree-depth:{remId}` 等 | 读取参数 | `edit_tree` 乐观并发检测 |
81
+ | `rem:{nodeRemId}` | 完整 RemObject(N 个) | `edit_rem` 前置缓存 |
82
+
83
+ 总缓存条目:1(tree) + 3(参数) + N(rem) ≈ N+4。注意 LRU 上限 200。
84
+
85
+ ## 典型工作流
86
+
87
+ ```
88
+ read_rem_in_tree → 一次获取全部信息
89
+
90
+ edit_tree 结构编辑(tree 缓存已就绪)
91
+ +
92
+ edit_rem 属性编辑(rem 缓存已就绪)
93
+ ```
94
+
95
+ ## 关联工具
96
+
97
+ - `read_tree`:只需大纲,更轻量
98
+ - `read_rem`:只需单个 Rem 属性
99
+ - `edit_tree`:结构编辑(需先 read_tree 或 read_rem_in_tree)
100
+ - `edit_rem`:属性编辑(需先 read_rem 或 read_rem_in_tree)
@@ -9,7 +9,8 @@
9
9
  `read-rem` 通过 Rem ID 读取一个 Rem 的所有可获取属性,返回标准化的 RemObject。读取结果会被缓存在 daemon 内存中,供后续 `edit-rem` 使用。
10
10
 
11
11
  核心能力:
12
- - 返回 51 个字段的完整 Rem 数据(默认 33 个,Portal 简化 8 个,`--full` 时 51 个)
12
+ - **Token Slimming**(默认模式):省略处于默认值的字段,典型普通 Rem 仅输出 5-6 个差异字段,大幅减少 AI token 消耗。未显示的字段即为默认值
13
+ - Portal 简化 8 个字段,`--full` 时全部 51 个字段
13
14
  - 支持 `--fields` 指定字段子集
14
15
  - 支持 Powerup 噪音过滤(默认过滤)
15
16
  - 自动缓存,为 `edit-rem` 建立编辑基础
@@ -72,20 +73,17 @@ remnote-bridge read-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA"}'
72
73
  "data": {
73
74
  "id": "kLrIOHJLyMd8Y2lyA",
74
75
  "text": [{ "i": "m", "text": "示例文本", "b": true }],
75
- "backText": null,
76
76
  "type": "concept",
77
- "isDocument": false,
78
77
  "parent": "parentRemId",
79
- "fontSize": null,
80
- "highlightColor": null,
81
- "isTodo": false,
82
- "todoStatus": null,
83
- "...": "(共 33 个字段,--full 时 51 个)"
78
+ "createdAt": 1709712000000,
79
+ "updatedAt": 1709712000000
84
80
  },
85
81
  "timestamp": "2026-03-06T10:00:00.000Z"
86
82
  }
87
83
  ```
88
84
 
85
+ > **Token Slimming**:默认模式省略了 backText(null)、isDocument(false)、fontSize(null) 等处于默认值的字段。未出现的字段即为默认值。`--full` 可输出全部 51 个字段。
86
+
89
87
  ### 成功(含缓存覆盖提示)
90
88
 
91
89
  当该 Rem 之前已有缓存时,输出中附加 `cacheOverridden` 字段:
@@ -159,7 +157,7 @@ remnote-bridge read-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA"}'
159
157
  │ ├─ --full → 返回全部 51 字段
160
158
  │ ├─ --fields → 返回指定字段 + id
161
159
  │ ├─ type=portal → Portal 简化模式(返回 8 个关键字段)
162
- │ └─ 默认 → 排除 R-F 字段(返回 33 字段)
160
+ │ └─ 默认 → 排除 R-F 字段 + Token Slimming(省略匹配默认值的字段)
163
161
  └─ 附加 _cacheOverridden 元数据(若之前有缓存)
164
162
  4. CLI 格式化输出(人类模式 pretty-print / JSON 模式单行)
165
163
  ```
@@ -376,11 +374,32 @@ text | number | date | checkbox | single_select | multi_select | url | image | t
376
374
 
377
375
  | 模式 | 输出字段数 | 说明 |
378
376
  |------|:----------:|------|
379
- | 默认 | 33 | RW + R 字段,覆盖常用场景 |
377
+ | 默认(Token Slimming) | 5-6(典型) | 省略匹配默认值的字段,仅输出有差异的字段。始终输出:id、text、parent、createdAt、updatedAt |
380
378
  | Portal 简化 | 8 | type=portal 时自动使用(id、type、portalType、portalDirectlyIncludedRem、parent、positionAmongstSiblings、createdAt、updatedAt)。`--full` / `--fields` 可覆盖 |
381
- | `--full` | 51 | 全部字段(含 R-F 低频字段) |
379
+ | `--full` | 51 | 全部字段(含 R-F 低频字段),不省略默认值 |
382
380
  | `--fields` | 自选 + id | 仅返回指定字段(始终包含 id) |
383
381
 
382
+ ### 默认值参考表
383
+
384
+ 默认模式下,以下字段在值匹配默认值时被省略。**未出现在输出中的字段即为默认值。**
385
+
386
+ | 字段 | 默认值 | 字段 | 默认值 |
387
+ |------|--------|------|--------|
388
+ | `backText` | `null` | `type` | `"default"` |
389
+ | `isDocument` | `false` | `fontSize` | `null` |
390
+ | `highlightColor` | `null` | `isTodo` | `false` |
391
+ | `todoStatus` | `null` | `isCode` | `false` |
392
+ | `isQuote` | `false` | `isListItem` | `false` |
393
+ | `isCardItem` | `false` | `isTable` | `false` |
394
+ | `isSlot` | `false` | `isProperty` | `false` |
395
+ | `portalType` | `null` | `portalDirectlyIncludedRem` | `[]` |
396
+ | `propertyType` | `null` | `enablePractice` | `false` |
397
+ | `practiceDirection` | `"forward"` | `tags` | `[]` |
398
+ | `sources` | `[]` | `aliases` | `[]` |
399
+ | `remsBeingReferenced` | `[]` | `remsReferencingThis` | `[]` |
400
+ | `taggedRem` | `[]` | `descendants` | `[]` |
401
+ | `siblingRem` | `[]` | `positionAmongstSiblings` | `null` |
402
+
384
403
  ### R-F 字段列表(默认不输出,`--full` 时输出)
385
404
 
386
405
  ```