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,428 @@
|
|
|
1
|
+
# read-tree
|
|
2
|
+
|
|
3
|
+
> 将 Rem 子树序列化为 Markdown 大纲,支持深度/节点预算控制和祖先路径追溯。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 功能
|
|
8
|
+
|
|
9
|
+
`read-tree` 通过 Rem ID 读取一棵子树,将其序列化为带缩进和元数据注释的 Markdown 大纲文本。读取结果会被缓存在 daemon 内存中,供后续 `edit-tree` 使用。
|
|
10
|
+
|
|
11
|
+
核心能力:
|
|
12
|
+
- 递归展开子树,输出人类可读的 Markdown 大纲
|
|
13
|
+
- 三个维度的预算控制:深度(depth)、全局节点上限(maxNodes)、单层兄弟上限(maxSiblings)
|
|
14
|
+
- 祖先路径追溯(ancestorLevels),提供上下文定位
|
|
15
|
+
- Powerup 噪音过滤(默认过滤)
|
|
16
|
+
- Portal 感知:识别 Portal 类型并输出引用的 Rem ID
|
|
17
|
+
- 自动缓存,为 `edit-tree` 建立编辑基础
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 用法
|
|
22
|
+
|
|
23
|
+
### 人类模式
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
remnote-bridge read-tree <remId> [--depth N] [--max-nodes N] [--max-siblings N] [--ancestor-levels N] [--includePowerup]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| 参数/选项 | 类型 | 必需 | 默认值 | 说明 |
|
|
30
|
+
|-----------|------|:----:|--------|------|
|
|
31
|
+
| `remId` | string(位置参数) | 是 | — | 子树根节点的 Rem ID |
|
|
32
|
+
| `--depth` | integer | 否 | 3 | 递归展开深度(-1 = 无限) |
|
|
33
|
+
| `--max-nodes` | integer | 否 | 200 | 全局节点上限(预算控制) |
|
|
34
|
+
| `--max-siblings` | integer | 否 | 20 | 单个父节点下的最大可见子节点数 |
|
|
35
|
+
| `--ancestor-levels` | integer | 否 | 0 | 向上追溯祖先层数(上限 10) |
|
|
36
|
+
| `--includePowerup` | boolean | 否 | false | 包含 Powerup 系统数据(默认过滤) |
|
|
37
|
+
|
|
38
|
+
输出示例:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
<!-- ancestors: 知识库 (root_id [top]) > 项目 (proj_id) -->
|
|
42
|
+
# 项目概览 <!--kLrIOHJLyMd8Y2lyA type:concept doc-->
|
|
43
|
+
第一部分 <!--id1-->
|
|
44
|
+
小节 1.1 <!--id1_1-->
|
|
45
|
+
小节 1.2 → 答案 <!--id1_2-->
|
|
46
|
+
第二部分 <!--id2 children:5-->
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### JSON 模式
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
remnote-bridge read-tree --json '{"remId":"kLrIOHJLyMd8Y2lyA","depth":2,"maxSiblings":10}'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## JSON 输入参数
|
|
58
|
+
|
|
59
|
+
| 字段 | 类型 | 必需 | 说明 |
|
|
60
|
+
|------|------|:----:|------|
|
|
61
|
+
| `remId` | string | 是 | 子树根节点 Rem ID |
|
|
62
|
+
| `depth` | number | 否 | 递归展开深度(-1 = 无限) |
|
|
63
|
+
| `maxNodes` | number | 否 | 全局节点上限 |
|
|
64
|
+
| `maxSiblings` | number | 否 | 单个父节点下的最大可见子节点数 |
|
|
65
|
+
| `ancestorLevels` | number | 否 | 向上追溯祖先层数(上限 10) |
|
|
66
|
+
| `includePowerup` | boolean | 否 | 包含 Powerup 系统数据 |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## JSON 输出
|
|
71
|
+
|
|
72
|
+
### 成功
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"ok": true,
|
|
77
|
+
"command": "read-tree",
|
|
78
|
+
"data": {
|
|
79
|
+
"outline": "# 项目概览 <!--kLrIOHJLyMd8Y2lyA type:concept doc-->\n 第一部分 <!--id1-->\n...",
|
|
80
|
+
"rootId": "kLrIOHJLyMd8Y2lyA",
|
|
81
|
+
"depth": 3,
|
|
82
|
+
"nodeCount": 25
|
|
83
|
+
},
|
|
84
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 成功(含祖先路径)
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"ok": true,
|
|
93
|
+
"command": "read-tree",
|
|
94
|
+
"data": {
|
|
95
|
+
"outline": "...",
|
|
96
|
+
"rootId": "kLrIOHJLyMd8Y2lyA",
|
|
97
|
+
"depth": 2,
|
|
98
|
+
"nodeCount": 18
|
|
99
|
+
},
|
|
100
|
+
"ancestors": [
|
|
101
|
+
{ "id": "proj_id", "name": "项目", "childrenCount": 8, "isDocument": false },
|
|
102
|
+
{ "id": "root_id", "name": "知识库", "childrenCount": 15, "isDocument": true, "isTopLevel": true }
|
|
103
|
+
],
|
|
104
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 成功(含缓存覆盖提示 + Powerup 过滤统计)
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"ok": true,
|
|
113
|
+
"command": "read-tree",
|
|
114
|
+
"data": { "...": "..." },
|
|
115
|
+
"cacheOverridden": {
|
|
116
|
+
"id": "kLrIOHJLyMd8Y2lyA",
|
|
117
|
+
"previousCachedAt": "2026-03-06T09:55:00.000Z"
|
|
118
|
+
},
|
|
119
|
+
"powerupFiltered": { "tags": 3, "children": 5 },
|
|
120
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Rem 不存在
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"ok": false,
|
|
129
|
+
"command": "read-tree",
|
|
130
|
+
"error": "Rem not found: kLrIOHJLyMd8Y2lyA",
|
|
131
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### daemon 不可达
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"ok": false,
|
|
140
|
+
"command": "read-tree",
|
|
141
|
+
"error": "守护进程未运行,请先执行 remnote-bridge connect",
|
|
142
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Markdown 大纲格式
|
|
149
|
+
|
|
150
|
+
read-tree 的核心输出是 Markdown 大纲文本。每一行对应一个 Rem 节点。
|
|
151
|
+
|
|
152
|
+
### 行结构
|
|
153
|
+
|
|
154
|
+
每行的格式为:
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
{缩进}{Markdown 前缀}{内容}{箭头分隔符}{backText} <!-- {remId} {元数据标记} -->
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
- **缩进**:每级 2 个空格(根节点 0 个空格)
|
|
161
|
+
- **Markdown 前缀**:映射 Rem 的格式属性
|
|
162
|
+
- **内容**:Rem 的 text 字段(已通过 SDK 转为 Markdown)
|
|
163
|
+
- **箭头分隔符**:表示 practiceDirection(闪卡方向)
|
|
164
|
+
- **remId**:该行对应的 Rem ID
|
|
165
|
+
- **元数据标记**:空格分隔的属性标记
|
|
166
|
+
|
|
167
|
+
### Markdown 前缀(由外到内)
|
|
168
|
+
|
|
169
|
+
前缀的嵌套顺序与序列化一致:Header → Todo → Code。
|
|
170
|
+
|
|
171
|
+
| 前缀 | 含义 | 对应字段 |
|
|
172
|
+
|------|------|----------|
|
|
173
|
+
| `# ` | H1 标题 | `fontSize: 'H1'` |
|
|
174
|
+
| `## ` | H2 标题 | `fontSize: 'H2'` |
|
|
175
|
+
| `### ` | H3 标题 | `fontSize: 'H3'` |
|
|
176
|
+
| `- [ ] ` | 未完成待办 | `isTodo: true, todoStatus: 'Unfinished'` |
|
|
177
|
+
| `- [x] ` | 已完成待办 | `isTodo: true, todoStatus: 'Finished'` |
|
|
178
|
+
| `` `...` `` | 代码块 | `isCode: true` |
|
|
179
|
+
| `---` | 分隔线 | Divider Powerup |
|
|
180
|
+
|
|
181
|
+
### 箭头分隔符
|
|
182
|
+
|
|
183
|
+
箭头仅编码 **practiceDirection**(闪卡练习方向),不编码 type。type 由元数据标记承载。
|
|
184
|
+
|
|
185
|
+
#### 有 backText 的箭头(中间箭头)
|
|
186
|
+
|
|
187
|
+
| 箭头 | 格式 | practiceDirection |
|
|
188
|
+
|------|------|-------------------|
|
|
189
|
+
| ` → ` | `text → backText` | forward |
|
|
190
|
+
| ` ← ` | `text ← backText` | backward |
|
|
191
|
+
| ` ↔ ` | `text ↔ backText` | both |
|
|
192
|
+
|
|
193
|
+
#### 多行闪卡的箭头
|
|
194
|
+
|
|
195
|
+
多行闪卡(Multiline Card)的答案在子节点中,不在 backText 中。
|
|
196
|
+
|
|
197
|
+
| 箭头 | 格式 | practiceDirection | 说明 |
|
|
198
|
+
|------|------|-------------------|------|
|
|
199
|
+
| ` ↓ ` | `text ↓ backText` | forward | 有 backText 的多行 |
|
|
200
|
+
| ` ↑ ` | `text ↑ backText` | backward | 有 backText 的多行 |
|
|
201
|
+
| ` ↕ ` | `text ↕ backText` | both | 有 backText 的多行 |
|
|
202
|
+
| ` ↓`(尾部) | `text ↓` | forward | 无 backText 的多行 |
|
|
203
|
+
| ` ↑`(尾部) | `text ↑` | backward | 无 backText 的多行 |
|
|
204
|
+
| ` ↕`(尾部) | `text ↕` | both | 无 backText 的多行 |
|
|
205
|
+
|
|
206
|
+
### 元数据标记
|
|
207
|
+
|
|
208
|
+
行尾 `<!-- -->` 注释中的标记,空格分隔。
|
|
209
|
+
|
|
210
|
+
| 标记 | 含义 | 何时出现 |
|
|
211
|
+
|------|------|----------|
|
|
212
|
+
| `type:concept` | Rem 类型为 concept | type 不为 default 时 |
|
|
213
|
+
| `type:descriptor` | Rem 类型为 descriptor | type 不为 default 时 |
|
|
214
|
+
| `type:portal` | Portal 类型 | type 为 portal 时 |
|
|
215
|
+
| `refs:id1,id2,...` | Portal 直接引用的 Rem ID | 仅 portal 类型 |
|
|
216
|
+
| `doc` | isDocument = true | 独立于 type |
|
|
217
|
+
| `role:card-item` | isCardItem = true | 多行闪卡的答案行 |
|
|
218
|
+
| `children:N` | 折叠的子节点数量 | 深度超限且有子节点时 |
|
|
219
|
+
| `tag:Name(id)` | 已附加的标签 | 每个 tag 一个标记 |
|
|
220
|
+
| `top` | 知识库顶层 Rem | 无父节点时 |
|
|
221
|
+
|
|
222
|
+
### 完整示例
|
|
223
|
+
|
|
224
|
+
```markdown
|
|
225
|
+
# 数据结构 <!--kLr type:concept doc top-->
|
|
226
|
+
## 线性结构 <!--ABC type:concept-->
|
|
227
|
+
数组 → Array <!--DEF-->
|
|
228
|
+
链表 ↓ <!--GHI type:concept-->
|
|
229
|
+
单向链表 <!--JKL role:card-item-->
|
|
230
|
+
双向链表 <!--MNO role:card-item-->
|
|
231
|
+
- [x] 复习完成 <!--PQR-->
|
|
232
|
+
`quickSort()` <!--STU-->
|
|
233
|
+
## 树结构 <!--VWX type:concept children:8-->
|
|
234
|
+
<!--...elided 3 siblings (parent:kLr range:3-5 total:6)-->
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 省略占位符
|
|
240
|
+
|
|
241
|
+
当子节点数超过 maxSiblings 或全局预算耗尽时,自动插入省略行。
|
|
242
|
+
|
|
243
|
+
### 精确省略(sibling 裁剪)
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
<!--...elided {N} siblings (parent:{parentId} range:{from}-{to} total:{total})-->
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
当 `children.length > maxSiblings` 时触发。保留前 70%(向上取整)+ 后 30%(向下取整),中间用省略行替代。
|
|
250
|
+
|
|
251
|
+
### 非精确省略(全局预算耗尽)
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
<!--...elided >={N} nodes (parent:{parentId} range:{from}-{to} total:{total})-->
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
当全局 `maxNodes` 预算耗尽时触发。`>=N` 表示被省略的节点数不精确(可能还有后代)。
|
|
258
|
+
|
|
259
|
+
### 字段说明
|
|
260
|
+
|
|
261
|
+
| 字段 | 含义 |
|
|
262
|
+
|------|------|
|
|
263
|
+
| `N` 或 `>=N` | 被省略的节点数 |
|
|
264
|
+
| `siblings` / `nodes` | 精确省略用 siblings,非精确用 nodes |
|
|
265
|
+
| `parent:{id}` | 省略占位的父 Rem ID |
|
|
266
|
+
| `range:{from}-{to}` | 被省略节点在父节点 children 中的索引范围 |
|
|
267
|
+
| `total:{total}` | 父节点的总 children 数 |
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 预算控制
|
|
272
|
+
|
|
273
|
+
三个维度协作限制输出规模,优先级:depth > maxSiblings > maxNodes。
|
|
274
|
+
|
|
275
|
+
### depth(递归深度)
|
|
276
|
+
|
|
277
|
+
- 控制子树展开的层数。根节点 = 深度 0
|
|
278
|
+
- 当 `currentDepth >= depth` 且有子节点时,子树**折叠**——不递归展开,输出 `children:N` 标记
|
|
279
|
+
- `-1` = 无深度限制
|
|
280
|
+
|
|
281
|
+
### maxSiblings(单层兄弟上限)
|
|
282
|
+
|
|
283
|
+
- 每个父节点下最多展示 maxSiblings 个子节点
|
|
284
|
+
- 超限时保留前 70%(`ceil(max * 0.7)`)+ 后 30%(`floor(max * 0.3)`),中间插入精确省略行
|
|
285
|
+
- 示例:maxSiblings=20, 实际 100 个子节点 → 展示前 14 + 后 6,中间省略 80 个
|
|
286
|
+
|
|
287
|
+
### maxNodes(全局节点上限)
|
|
288
|
+
|
|
289
|
+
- 整棵树遍历的节点总预算
|
|
290
|
+
- 每访问一个节点,预算递减 1
|
|
291
|
+
- 预算耗尽后,剩余节点生成非精确省略占位符
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## 祖先路径
|
|
296
|
+
|
|
297
|
+
当 `ancestorLevels > 0` 时,向上追溯根节点的祖先链。
|
|
298
|
+
|
|
299
|
+
### 输出格式(人类模式)
|
|
300
|
+
|
|
301
|
+
由远及近显示祖先路径,追加在大纲前面:
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
<!-- ancestors: 知识库 (root_id [top]) > 项目 (proj_id) > 子模块 (sub_id) -->
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### JSON 模式
|
|
308
|
+
|
|
309
|
+
`ancestors` 数组,元素顺序由近及远(根的直接父亲在前,最远祖先在后):
|
|
310
|
+
|
|
311
|
+
```json
|
|
312
|
+
{
|
|
313
|
+
"ancestors": [
|
|
314
|
+
{ "id": "sub_id", "name": "子模块", "childrenCount": 3, "isDocument": false },
|
|
315
|
+
{ "id": "proj_id", "name": "项目", "childrenCount": 8, "isDocument": true },
|
|
316
|
+
{ "id": "root_id", "name": "知识库", "childrenCount": 15, "isDocument": true, "isTopLevel": true }
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### 限制
|
|
322
|
+
|
|
323
|
+
- 上限 10 层(超过 10 被钳位为 10)
|
|
324
|
+
- 遇到无父节点的顶级 Rem 时提前停止
|
|
325
|
+
- 最远祖先标记 `isTopLevel: true`(如果它是顶级 Rem)
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## Portal 感知
|
|
330
|
+
|
|
331
|
+
read-tree 能识别 Portal 类型的 Rem 并输出其引用信息。
|
|
332
|
+
|
|
333
|
+
```markdown
|
|
334
|
+
嵌入的知识 <!--portal_id type:portal refs:ref_id1,ref_id2-->
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
- `type:portal`:标记为 Portal 类型
|
|
338
|
+
- `refs:id1,id2,...`:该 Portal 直接引用的 Rem ID 列表(不递归展开)
|
|
339
|
+
|
|
340
|
+
Portal 检测依据:`rem.type === 6`(RemNote SDK 内部编码)。
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Powerup 过滤
|
|
345
|
+
|
|
346
|
+
默认 `includePowerup=false`,过滤 Powerup 产生的噪音数据:
|
|
347
|
+
|
|
348
|
+
| 过滤对象 | 过滤条件 | 说明 |
|
|
349
|
+
|----------|----------|------|
|
|
350
|
+
| **tags** | `isPowerup()=true` 的 Tag | 系统 Powerup Tag(标题、高亮、代码等) |
|
|
351
|
+
| **children** | `isPowerupProperty()` / `isPowerupSlot()` / `isPowerupPropertyListItem()` / `isPowerupEnum()` 为 true 的子 Rem | Powerup 产生的隐藏子 Rem |
|
|
352
|
+
|
|
353
|
+
过滤统计通过 `powerupFiltered` 字段返回。`--includePowerup` 可恢复完整数据。
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## 缓存行为
|
|
358
|
+
|
|
359
|
+
| 时机 | 行为 |
|
|
360
|
+
|------|------|
|
|
361
|
+
| 读取成功 | 大纲文本写入 `cache.set('tree:' + remId, outline)` |
|
|
362
|
+
| 参数缓存 | 同时缓存 `tree-depth:`、`tree-maxNodes:`、`tree-maxSiblings:`(供 edit-tree 并发检测复现查询) |
|
|
363
|
+
| 已有缓存 | 覆盖旧缓存,返回 `cacheOverridden` 元数据 |
|
|
364
|
+
| 缓存用途 | 供 `edit-tree` 的三道防线使用 |
|
|
365
|
+
| 缓存存储 | daemon 内存中的 LRU 缓存(最大 200 条目) |
|
|
366
|
+
| 缓存清空 | daemon 关闭时自动消失 |
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## 内部流程
|
|
371
|
+
|
|
372
|
+
```
|
|
373
|
+
1. CLI 解析参数(remId, depth, maxNodes, maxSiblings, ancestorLevels, includePowerup)
|
|
374
|
+
2. sendRequest → WS → daemon
|
|
375
|
+
3. daemon TreeReadHandler:
|
|
376
|
+
├─ 应用默认值(config.ts)
|
|
377
|
+
├─ 记录旧缓存时间(若存在)
|
|
378
|
+
├─ forwardToPlugin('read_tree', payload)
|
|
379
|
+
├─ Plugin 端:
|
|
380
|
+
│ ├─ plugin.rem.findOne(remId)
|
|
381
|
+
│ ├─ 递归遍历子树(buildNode)
|
|
382
|
+
│ │ ├─ depth 检查 → 折叠/展开
|
|
383
|
+
│ │ ├─ maxSiblings → sliceSiblings() 省略
|
|
384
|
+
│ │ ├─ maxNodes → 全局预算递减
|
|
385
|
+
│ │ └─ Powerup 过滤(可选)
|
|
386
|
+
│ ├─ 祖先链构建(ancestorLevels > 0 时)
|
|
387
|
+
│ └─ tree-serializer 序列化为 Markdown 大纲
|
|
388
|
+
├─ 缓存大纲文本 + 读取参数
|
|
389
|
+
└─ 附加 _cacheOverridden 元数据(若之前有缓存)
|
|
390
|
+
4. CLI 格式化输出
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## 退出码
|
|
396
|
+
|
|
397
|
+
| 退出码 | 含义 | 触发条件 |
|
|
398
|
+
|:------:|------|----------|
|
|
399
|
+
| 0 | 成功 | 树读取成功 |
|
|
400
|
+
| 1 | 业务错误 | Rem 不存在、Plugin 未连接等 |
|
|
401
|
+
| 2 | daemon 不可达 | daemon 未运行或 WS 连接失败 |
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## 输出字段说明
|
|
406
|
+
|
|
407
|
+
| 字段 | 类型 | 说明 |
|
|
408
|
+
|------|------|------|
|
|
409
|
+
| `data.outline` | string | Markdown 大纲文本(核心输出) |
|
|
410
|
+
| `data.rootId` | string | 根节点 Rem ID |
|
|
411
|
+
| `data.depth` | number | 实际使用的展开深度 |
|
|
412
|
+
| `data.nodeCount` | number | 树中总节点数 |
|
|
413
|
+
| `ancestors` | AncestorInfo[] | 祖先路径(仅 ancestorLevels > 0 时) |
|
|
414
|
+
| `cacheOverridden` | object | 之前有缓存时附加 |
|
|
415
|
+
| `powerupFiltered` | object | Powerup 过滤统计(`tags` + `children` 数量) |
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## 配置依赖
|
|
420
|
+
|
|
421
|
+
| 配置项 | 默认值 | 说明 |
|
|
422
|
+
|--------|--------|------|
|
|
423
|
+
| `defaults.readTreeDepth` | 3 | 默认递归深度 |
|
|
424
|
+
| `defaults.maxNodes` | 200 | 默认全局节点上限 |
|
|
425
|
+
| `defaults.maxSiblings` | 20 | 默认单层兄弟上限 |
|
|
426
|
+
| `defaults.readTreeAncestorLevels` | 0 | 默认祖先追溯层数 |
|
|
427
|
+
| `defaults.readTreeIncludePowerup` | false | 默认是否包含 Powerup |
|
|
428
|
+
| `defaults.cacheMaxSize` | 200 | 缓存条目上限 |
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# search
|
|
2
|
+
|
|
3
|
+
> 在知识库中按文本搜索 Rem,返回匹配结果列表。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 功能
|
|
8
|
+
|
|
9
|
+
`search` 调用 RemNote SDK 的全文搜索 API(`plugin.search.search()`),在当前知识库中搜索包含关键词的 Rem,返回匹配结果的 ID、文本和文档标记。
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 用法
|
|
14
|
+
|
|
15
|
+
### 人类模式
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
remnote-bridge search <query> [--limit <N>]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| 参数/选项 | 类型 | 必需 | 说明 |
|
|
22
|
+
|-----------|------|:----:|------|
|
|
23
|
+
| `query` | string(位置参数) | 是 | 搜索关键词 |
|
|
24
|
+
| `--limit <N>` | number | 否 | 结果数量上限(默认 20) |
|
|
25
|
+
|
|
26
|
+
输出示例:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
搜索 "机器学习",找到 5 条结果:
|
|
30
|
+
|
|
31
|
+
[kLrIOHJLyMd8Y2lyA] [Doc] 机器学习笔记
|
|
32
|
+
[abc123def456] 监督学习与机器学习的关系
|
|
33
|
+
[xyz789ghi012] 深度学习是机器学习的子集
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
无结果时:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
未找到与 "不存在的关键词" 相关的结果
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### JSON 模式
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
remnote-bridge search --json '{"query":"机器学习","numResults":10}'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## JSON 输入参数
|
|
51
|
+
|
|
52
|
+
| 字段 | 类型 | 必需 | 说明 |
|
|
53
|
+
|------|------|:----:|------|
|
|
54
|
+
| `query` | string | 是 | 搜索关键词(不能为空) |
|
|
55
|
+
| `numResults` | number | 否 | 结果数量上限(默认 20) |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## JSON 输出
|
|
60
|
+
|
|
61
|
+
### 成功
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"ok": true,
|
|
66
|
+
"command": "search",
|
|
67
|
+
"data": {
|
|
68
|
+
"query": "机器学习",
|
|
69
|
+
"results": [
|
|
70
|
+
{ "remId": "kLrIOHJLyMd8Y2lyA", "text": "机器学习笔记", "isDocument": true },
|
|
71
|
+
{ "remId": "abc123def456", "text": "监督学习与机器学习的关系", "isDocument": false }
|
|
72
|
+
],
|
|
73
|
+
"totalFound": 2
|
|
74
|
+
},
|
|
75
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 无结果
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"ok": true,
|
|
84
|
+
"command": "search",
|
|
85
|
+
"data": {
|
|
86
|
+
"query": "不存在的关键词",
|
|
87
|
+
"results": [],
|
|
88
|
+
"totalFound": 0
|
|
89
|
+
},
|
|
90
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 搜索词为空
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"ok": false,
|
|
99
|
+
"command": "search",
|
|
100
|
+
"error": "search query 不能为空",
|
|
101
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### daemon 不可达
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"ok": false,
|
|
110
|
+
"command": "search",
|
|
111
|
+
"error": "守护进程未运行,请先执行 remnote-bridge connect",
|
|
112
|
+
"timestamp": "2026-03-06T10:00:00.000Z"
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 内部流程
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
1. CLI 解析参数(query, numResults)
|
|
122
|
+
2. sendRequest → WS → daemon → forwardToPlugin('search', { query, numResults })
|
|
123
|
+
3. Plugin 端:
|
|
124
|
+
├─ plugin.search.search([query], undefined, { numResults })
|
|
125
|
+
├─ 遍历结果 Rem:
|
|
126
|
+
│ ├─ plugin.richText.toMarkdown(rem.text) → 转 Markdown 文本
|
|
127
|
+
│ ├─ 换行符替换为空格(保持单行)
|
|
128
|
+
│ └─ rem.isDocument() → 是否文档
|
|
129
|
+
└─ 返回 { query, results, totalFound }
|
|
130
|
+
4. CLI 格式化输出
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 结果字段说明
|
|
136
|
+
|
|
137
|
+
| 字段 | 类型 | 说明 |
|
|
138
|
+
|------|------|------|
|
|
139
|
+
| `query` | string | 原始搜索关键词 |
|
|
140
|
+
| `results` | array | 结果数组 |
|
|
141
|
+
| `results[].remId` | string | 匹配 Rem 的 ID |
|
|
142
|
+
| `results[].text` | string | Rem 正面文本(Markdown 格式,单行) |
|
|
143
|
+
| `results[].isDocument` | boolean | 是否为文档页面 |
|
|
144
|
+
| `totalFound` | number | 返回的结果数量 |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 退出码
|
|
149
|
+
|
|
150
|
+
| 退出码 | 含义 | 触发条件 |
|
|
151
|
+
|:------:|------|----------|
|
|
152
|
+
| 0 | 成功 | 搜索完成(含无结果) |
|
|
153
|
+
| 1 | 业务错误 | query 为空、--limit 非数字等 |
|
|
154
|
+
| 2 | daemon 不可达 | daemon 未运行或 WS 连接失败 |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 中文及非空格分词语言的搜索限制
|
|
159
|
+
|
|
160
|
+
`search` 调用的是 RemNote SDK 官方搜索方法(`plugin.search.search()`)。该方法的分词逻辑基于空格分割,对**中文、日文、韩文等无空格分词的语言不友好**。
|
|
161
|
+
|
|
162
|
+
### 问题原因
|
|
163
|
+
|
|
164
|
+
RemNote 的 Web 版搜索索引按单字符拆分非空格语言的文本。搜索 "共价键" 时,SDK 可能将其拆为 "共"、"价"、"键" 三个独立 token 进行匹配,导致多字词搜索命中率低甚至返回 0 结果。
|
|
165
|
+
|
|
166
|
+
> RemNote **本地桌面版**对中文搜索做了优化,体验显著优于 Web 版。
|
|
167
|
+
|
|
168
|
+
### 应对策略
|
|
169
|
+
|
|
170
|
+
| 策略 | 说明 |
|
|
171
|
+
|:-----|:-----|
|
|
172
|
+
| **单字搜索** | 用关键词中最具区分度的**单个字**搜索(如搜 "键" 而非 "共价键"),然后在结果中人工筛选 |
|
|
173
|
+
| **英文/拼音替代** | 如果 Rem 中包含英文注释,尝试用英文关键词搜索(如 "covalent") |
|
|
174
|
+
| **read-globe + read-tree 替代** | 当搜索失败时,通过 read-globe 浏览知识库文档结构,定位可能的父文档后用 read-tree 展开查找 |
|
|
175
|
+
| **询问用户版本** | 搜索效果差时,可询问用户使用的是 Web 版还是本地桌面版,以判断是否为已知限制 |
|
|
176
|
+
|
|
177
|
+
### 建议的搜索流程
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
1. 先用完整关键词搜索
|
|
181
|
+
2. 若返回 0 结果且关键词为中文/日文/韩文:
|
|
182
|
+
a. 用单字重试(选区分度最高的字)
|
|
183
|
+
b. 若仍无结果,改用 read-globe → read-tree 替代定位
|
|
184
|
+
3. 若用户反馈搜索持续不佳,询问:
|
|
185
|
+
- "您使用的是 RemNote Web 版还是本地桌面版?"
|
|
186
|
+
- "Web 版中文搜索存在已知限制,本地版体验更好"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 注意事项
|
|
192
|
+
|
|
193
|
+
- 搜索结果的 `text` 字段是 Markdown 格式的单行文本(多行换行符已替换为空格)
|
|
194
|
+
- `totalFound` 等于 `results.length`,即实际返回的数量(受 `numResults` 限制)
|
|
195
|
+
- 搜索不会触发缓存写入——search 结果不进入 RemCache
|
|
196
|
+
- 如需获取某个搜索结果的完整属性,需对其 `remId` 执行 `read-rem`
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "remnote-bridge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "RemNote 自动化桥接工具集:CLI + MCP Server + Plugin",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"remnote-bridge": "./dist/cli/main.js",
|
|
8
|
+
"unofficial-remnote-bridge": "./dist/cli/main.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/",
|
|
12
|
+
"remnote-plugin/src/",
|
|
13
|
+
"remnote-plugin/public/",
|
|
14
|
+
"remnote-plugin/package.json",
|
|
15
|
+
"remnote-plugin/postcss.config.js",
|
|
16
|
+
"remnote-plugin/tailwind.config.js",
|
|
17
|
+
"remnote-plugin/webpack.config.js",
|
|
18
|
+
"remnote-plugin/tsconfig.json",
|
|
19
|
+
"skill/",
|
|
20
|
+
"docs/instruction/"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"dev": "tsx src/cli/main.ts",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"lint:deps": "node scripts/check-layer-deps.cjs"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"commander": "^12.1.0",
|
|
30
|
+
"ws": "^8.16.0",
|
|
31
|
+
"fastmcp": "latest",
|
|
32
|
+
"zod": "^3.23.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"typescript": "^5.4.0",
|
|
36
|
+
"tsx": "^4.7.0",
|
|
37
|
+
"@types/node": "^20.11.0",
|
|
38
|
+
"@types/ws": "^8.5.0",
|
|
39
|
+
"vitest": "^1.3.0"
|
|
40
|
+
}
|
|
41
|
+
}
|