remnote-bridge 0.1.12 → 0.1.13
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/edit-rem.js +5 -5
- package/dist/cli/commands/read-rem.js +3 -1
- package/dist/cli/handlers/edit-handler.js +49 -140
- package/dist/cli/handlers/read-handler.js +5 -6
- package/dist/cli/handlers/rem-cache.js +10 -5
- package/dist/cli/main.js +18 -10
- package/dist/mcp/format.js +43 -0
- package/dist/mcp/index.js +0 -55
- package/dist/mcp/instructions.js +405 -289
- package/dist/mcp/resources/edit-rem-guide.js +37 -157
- package/dist/mcp/resources/edit-tree-guide.js +1 -1
- package/dist/mcp/resources/error-reference.js +9 -13
- package/dist/mcp/resources/rem-object-fields.js +3 -3
- package/dist/mcp/tools/edit-tools.js +69 -8
- package/dist/mcp/tools/infra-tools.js +14 -26
- package/dist/mcp/tools/read-tools.js +135 -26
- package/package.json +1 -1
- package/skills/remnote-bridge/SKILL.md +34 -34
- package/skills/remnote-bridge/instructions/edit-rem.md +105 -347
- package/skills/remnote-bridge/instructions/overall.md +23 -14
- package/skills/remnote-bridge/instructions/read-rem.md +5 -5
- package/skills/remnote-bridge/instructions/search.md +4 -4
- package/skills/remnote-bridge/instructions/setup.md +5 -6
|
@@ -128,7 +128,7 @@ Rem 有两个**独立维度**的类型:
|
|
|
128
128
|
|:-----|:-----|:-----|
|
|
129
129
|
| 创建 Portal | `edit-tree` | 新增行 `<!--portal refs:id1,id2-->` |
|
|
130
130
|
| 删除 Portal | `edit-tree` | 从大纲中移除 Portal 行(与删除普通行相同) |
|
|
131
|
-
| 修改引用列表(增删引用的 Rem) | `edit-rem` |
|
|
131
|
+
| 修改引用列表(增删引用的 Rem) | `edit-rem` | 直接修改 changes 中的 `portalDirectlyIncludedRem` 数组 |
|
|
132
132
|
| 移动 Portal(换父节点/位置) | `edit-tree` | 与移动普通行相同 |
|
|
133
133
|
| 读取 Portal | `read-rem` | 自动输出 8 字段简化 JSON |
|
|
134
134
|
|
|
@@ -288,7 +288,7 @@ remnote-bridge disconnect --instance work
|
|
|
288
288
|
|
|
289
289
|
| 命令 | 功能 | 前置条件 | 安全机制 | 详细文档 |
|
|
290
290
|
|:-----|:-----|:---------|:---------|:---------|
|
|
291
|
-
| `edit-rem` |
|
|
291
|
+
| `edit-rem` | 直接修改 Rem 属性字段 | 先 `read-rem` | 两道防线 + 字段白名单 | `edit-rem.md` |
|
|
292
292
|
| `edit-tree` | str_replace 编辑树结构 | 先 `read-tree` | 三道防线 + diff | `edit-tree.md` |
|
|
293
293
|
|
|
294
294
|
### 4.2 探索决策指南
|
|
@@ -410,7 +410,7 @@ Agent 需要根据用户意图选择正确的读取命令:
|
|
|
410
410
|
```bash
|
|
411
411
|
read-rem kLrIOHJLyMd8Y2lyA --fields text,type
|
|
412
412
|
read-tree kLrIOHJLyMd8Y2lyA --depth 2
|
|
413
|
-
edit-rem kLrIOHJLyMd8Y2lyA --
|
|
413
|
+
edit-rem kLrIOHJLyMd8Y2lyA --changes '{"type":"descriptor"}'
|
|
414
414
|
```
|
|
415
415
|
|
|
416
416
|
- 位置参数 = remId 或关键词
|
|
@@ -422,7 +422,7 @@ edit-rem kLrIOHJLyMd8Y2lyA --old-str '"concept"' --new-str '"descriptor"'
|
|
|
422
422
|
```bash
|
|
423
423
|
read-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA","fields":["text","type"]}'
|
|
424
424
|
read-tree --json '{"remId":"kLrIOHJLyMd8Y2lyA","depth":2}'
|
|
425
|
-
edit-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA","
|
|
425
|
+
edit-rem --json '{"remId":"kLrIOHJLyMd8Y2lyA","changes":{"type":"descriptor"}}'
|
|
426
426
|
```
|
|
427
427
|
|
|
428
428
|
- **位置参数 = JSON 字符串**,所有参数打包在 JSON 对象中
|
|
@@ -571,7 +571,7 @@ Portal:portalType [R], portalDirectlyIncludedRem [Portal-W]
|
|
|
571
571
|
{ "i": "a", "onlyAudio": true, "url": "..." } // 音频
|
|
572
572
|
```
|
|
573
573
|
|
|
574
|
-
> 在 RemObject 格式化 JSON 中,数组内对象展开为多行。构造 edit-
|
|
574
|
+
> 在 RemObject 格式化 JSON 中,数组内对象展开为多行。构造 edit-tree 的 oldStr/newStr 或 edit-rem 的 changes 中 RichText 值时,需注意多行格式。
|
|
575
575
|
|
|
576
576
|
**⚠️ highlightColor vs h — 两种完全不同的高亮**:
|
|
577
577
|
|
|
@@ -582,7 +582,7 @@ Portal:portalType [R], portalDirectlyIncludedRem [Portal-W]
|
|
|
582
582
|
|
|
583
583
|
`h` 颜色值:0=无, 1=Red, 2=Orange, 3=Yellow, 4=Green, 5=Purple, 6=Blue, 7=Gray, 8=Brown, 9=Pink。
|
|
584
584
|
|
|
585
|
-
**序列化确定性**:RichText 对象内部按 key 字母序排列(`sortRichTextKeys()`)。`_id` 的 `_`(U+005F
|
|
585
|
+
**序列化确定性**:RichText 对象内部按 key 字母序排列(`sortRichTextKeys()`)。`_id` 的 `_`(U+005F)排在所有小写字母之前。这对乐观并发检测和 edit-tree 的 str_replace 至关重要。
|
|
586
586
|
|
|
587
587
|
---
|
|
588
588
|
|
|
@@ -690,7 +690,7 @@ read-tree / read-globe / read-context 的输出核心是 Markdown 大纲文本
|
|
|
690
690
|
|
|
691
691
|
| 前缀 | 用途 | 写入命令 |
|
|
692
692
|
|:-----|:-----|:---------|
|
|
693
|
-
| `rem:{remId}` | RemObject
|
|
693
|
+
| `rem:{remId}` | RemObject 对象 | read-rem |
|
|
694
694
|
| `tree:{remId}` | Markdown 大纲 | read-tree |
|
|
695
695
|
| `tree-depth:{remId}` 等 | read-tree 参数 | read-tree |
|
|
696
696
|
|
|
@@ -698,9 +698,9 @@ read-tree / read-globe / read-context 的输出核心是 Markdown 大纲文本
|
|
|
698
698
|
- disconnect 关闭 daemon 时缓存自动消失
|
|
699
699
|
- **没有 TTL**——三道防线的并发检测已能捕获所有陈旧数据
|
|
700
700
|
|
|
701
|
-
### 9.2
|
|
701
|
+
### 9.2 安全防线
|
|
702
702
|
|
|
703
|
-
`edit-rem` 和 `edit-tree`
|
|
703
|
+
`edit-rem` 和 `edit-tree` 编辑数据时,实施多道防线防止数据损坏:
|
|
704
704
|
|
|
705
705
|
#### 防线 1:缓存存在性检查
|
|
706
706
|
|
|
@@ -718,7 +718,16 @@ edit 时重新从 SDK 读取最新数据 → 与缓存严格比较
|
|
|
718
718
|
|
|
719
719
|
如果 Rem 在 read 之后被外部修改(用户在 RemNote UI 中编辑、其他 Agent 修改等),数据不一致时拒绝编辑,**且不更新缓存**——迫使 Agent 重新 read。
|
|
720
720
|
|
|
721
|
-
#### 防线 3
|
|
721
|
+
#### edit-rem 防线 3:字段白名单校验
|
|
722
|
+
|
|
723
|
+
```
|
|
724
|
+
changes 中的字段必须在 RW 白名单内,枚举值必须合法
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
- 只读字段(R / R-F)写入时**警告跳过**,不阻断其他字段
|
|
728
|
+
- 枚举值非法时**报错拒绝**(如 `type: "invalid"`)
|
|
729
|
+
|
|
730
|
+
#### edit-tree 防线 3:str_replace 精确匹配
|
|
722
731
|
|
|
723
732
|
```
|
|
724
733
|
oldStr 必须在目标文本中恰好匹配 1 次
|
|
@@ -733,7 +742,8 @@ oldStr 必须在目标文本中恰好匹配 1 次
|
|
|
733
742
|
| 场景 | 缓存行为 |
|
|
734
743
|
|:-----|:---------|
|
|
735
744
|
| 写入全部成功 | 从 SDK 重新读取最新状态 → **更新缓存** |
|
|
736
|
-
|
|
|
745
|
+
| 防线拒绝(缓存缺失 / 并发冲突) | **不更新缓存**(迫使 Agent 重新 read) |
|
|
746
|
+
| 枚举值非法 | **报错拒绝**,不更新缓存 |
|
|
737
747
|
| 部分写入失败 | **不更新缓存** |
|
|
738
748
|
|
|
739
749
|
---
|
|
@@ -854,13 +864,12 @@ Agent 遇到错误时的诊断和恢复指南:
|
|
|
854
864
|
| has not been read yet | 未先执行 read-rem / read-tree | 执行对应 read 命令后重试 |
|
|
855
865
|
| has been modified since last read | Rem 在 read 和 edit 之间被外部修改 | 重新执行 read 获取最新状态后重试 |
|
|
856
866
|
|
|
857
|
-
### str_replace 错误
|
|
867
|
+
### edit-tree str_replace 错误
|
|
858
868
|
|
|
859
869
|
| 错误 | 原因 | 恢复 |
|
|
860
870
|
|:-----|:-----|:-----|
|
|
861
|
-
| old_str not found | oldStr 在目标文本中不存在 | 检查 oldStr
|
|
871
|
+
| old_str not found | oldStr 在目标文本中不存在 | 检查 oldStr 是否精确匹配(含空格、换行、缩进) |
|
|
862
872
|
| old_str matches N locations | oldStr 匹配到多个位置 | 扩大 oldStr 范围,包含更多上下文以唯一定位 |
|
|
863
|
-
| invalid JSON | 替换后的文本不是合法 JSON | 检查 newStr 的引号、逗号、括号完整性 |
|
|
864
873
|
|
|
865
874
|
### edit-tree 专用错误
|
|
866
875
|
|
|
@@ -422,7 +422,7 @@ localUpdatedAt, lastPracticed
|
|
|
422
422
|
| `tags` | `rem.addTag()` / `rem.removeTag()` | **Diff 机制**:对比当前 vs 目标,增删差异项。必须列出完整目标数组,缺少的会被删除 |
|
|
423
423
|
| `sources` | `rem.addSource()` / `rem.removeSource()` | **Diff 机制**:同 tags |
|
|
424
424
|
| `positionAmongstSiblings` | `rem.setParent(parent, position)` | 与 `parent` 联动(见下方说明) |
|
|
425
|
-
| `portalDirectlyIncludedRem` | `rem.addToPortal()` / `rem.removeFromPortal()` |
|
|
425
|
+
| `portalDirectlyIncludedRem` | `rem.addToPortal()` / `rem.removeFromPortal()` | **Portal-W Diff 机制**:仅 type=portal 时可修改。对比当前 vs 目标数组,逐项增删 |
|
|
426
426
|
|
|
427
427
|
### parent + positionAmongstSiblings 联动
|
|
428
428
|
|
|
@@ -434,7 +434,7 @@ localUpdatedAt, lastPracticed
|
|
|
434
434
|
| 只有 `parent` 变更 | `setParent(newParent)` 不带 position(保持末尾) |
|
|
435
435
|
| 只有 `positionAmongstSiblings` 变更 | 获取当前 parent → `setParent(currentParent, newPosition)` |
|
|
436
436
|
|
|
437
|
-
**应在同一次
|
|
437
|
+
**应在同一次 edit-rem 的 changes 中同时传入这两个字段。**
|
|
438
438
|
|
|
439
439
|
---
|
|
440
440
|
|
|
@@ -561,7 +561,7 @@ RemObject 中的 `text` 和 `backText` 字段使用 RichText 格式——一个
|
|
|
561
561
|
|
|
562
562
|
### 序列化确定性
|
|
563
563
|
|
|
564
|
-
RichText 对象元素内部按 **key 字母序排列**(Plugin 端 `sortRichTextKeys()` 处理),确保同一内容的序列化 JSON
|
|
564
|
+
RichText 对象元素内部按 **key 字母序排列**(Plugin 端 `sortRichTextKeys()` 处理),确保同一内容的序列化 JSON 始终一致。这对乐观并发检测至关重要。
|
|
565
565
|
|
|
566
566
|
- `_`(下划线)在 Unicode 中排在所有小写字母之前(`_` = U+005F,`a` = U+0061),所以 `_id` 总是排在第一位
|
|
567
567
|
- 排序由 `Object.keys().sort()` 决定,即 JavaScript 默认的 Unicode 字典序
|
|
@@ -589,11 +589,11 @@ RichText 对象元素内部按 **key 字母序排列**(Plugin 端 `sortRichTex
|
|
|
589
589
|
|------|------|
|
|
590
590
|
| 读取成功 | 完整 JSON 写入缓存 `cache.set('rem:' + remId, fullJson)` |
|
|
591
591
|
| 已有缓存 | 覆盖旧缓存,返回 `cacheOverridden` 元数据 |
|
|
592
|
-
| 缓存用途 | 供 `edit-rem`
|
|
592
|
+
| 缓存用途 | 供 `edit-rem` 的防线检查使用(存在性检查 + 乐观并发检测) |
|
|
593
593
|
| 缓存存储 | daemon 内存中的 LRU 缓存(最大 200 条目) |
|
|
594
594
|
| 缓存清空 | daemon 关闭时自动消失 |
|
|
595
595
|
|
|
596
|
-
**重要**:缓存存储的是 **完整 RemObject
|
|
596
|
+
**重要**:缓存存储的是 **完整 RemObject 对象**(含 R-F 字段),不受 `--fields` / `--full` 选项影响。字段过滤仅作用于返回给 CLI 的输出。
|
|
597
597
|
|
|
598
598
|
---
|
|
599
599
|
|
|
@@ -56,7 +56,7 @@ remnote-bridge search <query> [--limit <N>]
|
|
|
56
56
|
### JSON 模式
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
|
-
remnote-bridge search --json '{"query":"机器学习","
|
|
59
|
+
remnote-bridge search --json '{"query":"机器学习","limit":10}'
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
---
|
|
@@ -66,7 +66,7 @@ remnote-bridge search --json '{"query":"机器学习","numResults":10}'
|
|
|
66
66
|
| 字段 | 类型 | 必需 | 说明 |
|
|
67
67
|
|------|------|:----:|------|
|
|
68
68
|
| `query` | string | 是 | 搜索关键词(不能为空) |
|
|
69
|
-
| `
|
|
69
|
+
| `limit` | number | 否 | 结果数量上限(默认 20) |
|
|
70
70
|
|
|
71
71
|
---
|
|
72
72
|
|
|
@@ -159,7 +159,7 @@ remnote-bridge search --json '{"query":"机器学习","numResults":10}'
|
|
|
159
159
|
## 内部流程
|
|
160
160
|
|
|
161
161
|
```
|
|
162
|
-
1. CLI 解析参数(query,
|
|
162
|
+
1. CLI 解析参数(query, limit)
|
|
163
163
|
2. 检查配置:addons.remnote-rag.enabled
|
|
164
164
|
├─ 未启用 → 跳到步骤 4
|
|
165
165
|
└─ 已启用 → 继续步骤 3
|
|
@@ -250,6 +250,6 @@ RemNote 的 Web 版搜索索引按单字符拆分非空格语言的文本。搜
|
|
|
250
250
|
## 注意事项
|
|
251
251
|
|
|
252
252
|
- 搜索结果的 `text` 字段是 Markdown 格式的单行文本(多行换行符已替换为空格)
|
|
253
|
-
- `totalFound` 等于 `results.length`,即实际返回的数量(受 `
|
|
253
|
+
- `totalFound` 等于 `results.length`,即实际返回的数量(受 `limit` 限制)
|
|
254
254
|
- 搜索不会触发缓存写入——search 结果不进入 RemCache
|
|
255
255
|
- 如需获取某个搜索结果的完整属性,需对其 `remId` 执行 `read-rem`
|
|
@@ -83,22 +83,21 @@ remnote-bridge --json setup
|
|
|
83
83
|
|
|
84
84
|
## AI Agent 使用流程
|
|
85
85
|
|
|
86
|
-
setup 会弹出 Chrome
|
|
86
|
+
setup 会弹出 Chrome 窗口,用户只需**登录 RemNote**,然后彻底退出 Chrome。
|
|
87
|
+
|
|
88
|
+
setup 只负责保存登录凭证——配置 dev plugin 是 connect 之后的事(见 `connect.md` 标准模式说明)。
|
|
87
89
|
|
|
88
90
|
### 交互步骤
|
|
89
91
|
|
|
90
92
|
1. 调用 `setup`
|
|
91
93
|
2. **立即告知用户**:
|
|
92
|
-
> 已打开 Chrome
|
|
93
|
-
> 1. 登录 RemNote
|
|
94
|
-
> 2. 在 RemNote 中配置开发插件:点击左下角插件图标 → 开发你的插件 → 输入 connect 输出的 Plugin 服务地址(如 `http://localhost:29101`)
|
|
95
|
-
> 3. 完成后彻底退出 Chrome(macOS 请按 Cmd+Q,仅关窗口不够)
|
|
94
|
+
> 已打开 Chrome 浏览器。请登录 RemNote,完成后彻底退出 Chrome(macOS 请按 Cmd+Q,仅关窗口不够)
|
|
96
95
|
3. 等待 `setup` 命令返回(阻塞式,超时 600 秒)
|
|
97
96
|
4. 收到成功 → 继续执行 `connect --headless`
|
|
98
97
|
|
|
99
98
|
### setup 之后
|
|
100
99
|
|
|
101
|
-
`setup`
|
|
100
|
+
`setup` 只需执行一次。登录凭证已保存,之后每次只需 `connect --headless` 即可自动连接,无需用户操作。
|
|
102
101
|
|
|
103
102
|
如果后续 headless 模式下 Plugin 始终不连接,可能是 RemNote 登录 session 过期,需重新 setup(删除 `~/.remnote-bridge/chrome-profile/.setup-done` 后重新执行)。
|
|
104
103
|
|