remnote-bridge 0.1.14 → 0.1.16
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/README.md +6 -0
- package/README.zh-CN.md +6 -0
- package/dist/cli/commands/edit-tree.js +12 -1
- package/dist/cli/handlers/edit-handler.js +14 -0
- package/dist/cli/handlers/tree-edit-handler.js +3 -1
- package/dist/cli/handlers/tree-parser.js +17 -0
- package/dist/mcp/instructions.js +67 -71
- package/dist/mcp/tools/edit-tools.js +2 -1
- package/dist/mcp/tools/infra-tools.js +1 -1
- package/package.json +1 -1
- package/remnote-plugin/dist/index-sandbox.js +19 -19
- package/remnote-plugin/dist/index.js +19 -19
- package/remnote-plugin/dist/manifest.json +1 -1
- package/remnote-plugin/package.json +1 -1
- package/remnote-plugin/public/manifest.json +1 -1
- package/remnote-plugin/src/services/read-rem.ts +16 -16
- package/remnote-plugin/src/services/rem-builder.ts +8 -2
- package/remnote-plugin/src/settings.ts +1 -1
- package/remnote-plugin/src/utils/tree-serializer.ts +10 -0
- package/skills/remnote-bridge/SKILL.md +17 -4
- package/skills/remnote-bridge/instructions/connect.md +19 -7
- package/skills/remnote-bridge/instructions/edit-tree.md +100 -93
- package/skills/remnote-bridge/instructions/overall.md +16 -0
- package/skills/remnote-bridge-test/SKILL.md +3 -0
- package/skills/remnote-bridge-test/references/regression-suite.md +64 -0
package/README.md
CHANGED
|
@@ -292,6 +292,12 @@ remnote-bridge addon uninstall remnote-rag --purge
|
|
|
292
292
|
|
|
293
293
|
## Changelog
|
|
294
294
|
|
|
295
|
+
### 0.1.15 (2026-03-18)
|
|
296
|
+
|
|
297
|
+
- **Defense-2 false positive fix** — Removed `.sort()` from ID arrays in RemObject serialization that caused spurious concurrency conflicts in edit-rem
|
|
298
|
+
- **Documentation overhaul** — Strengthened headless mode warnings, added anti-hallucination guardrails for Plugin loading instructions, restructured edit-tree template/fallback dual-mode docs
|
|
299
|
+
- **Skill ↔ MCP doc sync** — Aligned connect, edit-tree, overall instructions across Skill and MCP layers
|
|
300
|
+
|
|
295
301
|
### 0.1.14 (2026-03-18)
|
|
296
302
|
|
|
297
303
|
- **read-rem-in-tree** — New command: fetch subtree outline + all Rem objects in one call (batch read for bulk edits)
|
package/README.zh-CN.md
CHANGED
|
@@ -292,6 +292,12 @@ remnote-bridge addon uninstall remnote-rag --purge
|
|
|
292
292
|
|
|
293
293
|
## Changelog
|
|
294
294
|
|
|
295
|
+
### 0.1.15 (2026-03-18)
|
|
296
|
+
|
|
297
|
+
- **防线 2 误判修复** — 移除 RemObject 序列化中 ID 数组的 `.sort()`,消除 edit-rem 并发检测的假阳性
|
|
298
|
+
- **文档全面优化** — 加强 headless 模式警告、新增 Plugin 加载防幻觉红线、重组 edit-tree 模板/回退双模式文档
|
|
299
|
+
- **Skill ↔ MCP 文档同步** — connect、edit-tree、overall 说明在 Skill 和 MCP 层全面对齐
|
|
300
|
+
|
|
295
301
|
### 0.1.14 (2026-03-18)
|
|
296
302
|
|
|
297
303
|
- **read-rem-in-tree** — 新命令:一次调用获取子树大纲 + 所有 Rem 对象(批量读取,用于批量编辑场景)
|
|
@@ -33,7 +33,12 @@ export async function editTreeCommand(remId, options) {
|
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
35
|
if (json) {
|
|
36
|
-
jsonOutput({
|
|
36
|
+
jsonOutput({
|
|
37
|
+
ok: true,
|
|
38
|
+
command: 'edit-tree',
|
|
39
|
+
operations: data.operations,
|
|
40
|
+
...(data.templateWarnings?.length && { templateWarnings: data.templateWarnings }),
|
|
41
|
+
});
|
|
37
42
|
}
|
|
38
43
|
else {
|
|
39
44
|
if (data.operations.length === 0) {
|
|
@@ -46,5 +51,11 @@ export async function editTreeCommand(remId, options) {
|
|
|
46
51
|
console.log(` - ${o.type}: ${JSON.stringify(o)}`);
|
|
47
52
|
}
|
|
48
53
|
}
|
|
54
|
+
if (data.templateWarnings?.length) {
|
|
55
|
+
console.log('⚠ 模板/前缀警告:');
|
|
56
|
+
for (const w of data.templateWarnings) {
|
|
57
|
+
console.log(` - ${w}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
49
60
|
}
|
|
50
61
|
}
|
|
@@ -63,6 +63,9 @@ export class EditHandler {
|
|
|
63
63
|
const currentJson = JSON.stringify(currentRemObject, null, 2);
|
|
64
64
|
const cachedJson = JSON.stringify(cachedObj, null, 2);
|
|
65
65
|
if (currentJson !== cachedJson) {
|
|
66
|
+
// 诊断日志:打出具体哪些字段不同,帮助定位并发误判
|
|
67
|
+
const diff = diffFields(cachedObj, currentRemObject);
|
|
68
|
+
console.error(`[defense-2] Rem ${remId} conflict detected. Changed fields: ${diff.join(', ') || '(JSON differs but no top-level field diff — possible nested change)'}`);
|
|
66
69
|
// 不更新缓存 — 迫使 AI re-read
|
|
67
70
|
throw new Error(`Rem ${remId} has been modified since last read. Please read it again before editing.`);
|
|
68
71
|
}
|
|
@@ -126,3 +129,14 @@ export class EditHandler {
|
|
|
126
129
|
};
|
|
127
130
|
}
|
|
128
131
|
}
|
|
132
|
+
/** 比较两个 RemObject 的顶层字段,返回值不同的 key 列表 */
|
|
133
|
+
function diffFields(cached, current) {
|
|
134
|
+
const allKeys = new Set([...Object.keys(cached), ...Object.keys(current)]);
|
|
135
|
+
const changed = [];
|
|
136
|
+
for (const key of allKeys) {
|
|
137
|
+
if (JSON.stringify(cached[key]) !== JSON.stringify(current[key])) {
|
|
138
|
+
changed.push(key);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return changed;
|
|
142
|
+
}
|
|
@@ -172,7 +172,9 @@ export class TreeEditHandler {
|
|
|
172
172
|
else {
|
|
173
173
|
// ── 普通 Rem 创建路径 ──
|
|
174
174
|
// 解析 Markdown 前缀 + 箭头分隔符 → 属性
|
|
175
|
-
const { cleanContent, powerups, backText, practiceDirection } = parsePowerupPrefix(op.content);
|
|
175
|
+
const { cleanContent, powerups, backText, practiceDirection, warnings: prefixWarnings } = parsePowerupPrefix(op.content);
|
|
176
|
+
if (prefixWarnings?.length)
|
|
177
|
+
templateWarnings.push(...prefixWarnings);
|
|
176
178
|
const createResult = await this.forwardToPlugin('create_rem', {
|
|
177
179
|
content: cleanContent,
|
|
178
180
|
parentId,
|
|
@@ -141,6 +141,7 @@ export function parseMetadata(metadataStr) {
|
|
|
141
141
|
*/
|
|
142
142
|
export function parsePowerupPrefix(rawContent) {
|
|
143
143
|
const powerups = {};
|
|
144
|
+
const warnings = [];
|
|
144
145
|
if (rawContent === '---') {
|
|
145
146
|
return { cleanContent: '', powerups: { addPowerup: 'dv' } };
|
|
146
147
|
}
|
|
@@ -168,6 +169,20 @@ export function parsePowerupPrefix(rawContent) {
|
|
|
168
169
|
powerups.isTodo = true;
|
|
169
170
|
content = content.slice(6);
|
|
170
171
|
}
|
|
172
|
+
// Quote(引用块)
|
|
173
|
+
if (content.startsWith('> ')) {
|
|
174
|
+
powerups.isQuote = true;
|
|
175
|
+
content = content.slice(2);
|
|
176
|
+
}
|
|
177
|
+
// ListItem(有序列表)— 容错 1-9 开头,归一化为 isListItem
|
|
178
|
+
const listItemMatch = content.match(/^([1-9])\. /);
|
|
179
|
+
if (listItemMatch) {
|
|
180
|
+
powerups.isListItem = true;
|
|
181
|
+
content = content.slice(listItemMatch[0].length);
|
|
182
|
+
if (listItemMatch[1] !== '1') {
|
|
183
|
+
warnings.push(`有序列表前缀 "${listItemMatch[0]}" 已归一化为 isListItem(RemNote 自动编号,请统一使用 "1. ")`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
171
186
|
// Code
|
|
172
187
|
if (content.startsWith('`') && content.endsWith('`') && content.length >= 2) {
|
|
173
188
|
powerups.isCode = true;
|
|
@@ -231,6 +246,8 @@ export function parsePowerupPrefix(rawContent) {
|
|
|
231
246
|
result.practiceDirection = practiceDirection;
|
|
232
247
|
if (isMultiline !== undefined)
|
|
233
248
|
result.isMultiline = isMultiline;
|
|
249
|
+
if (warnings.length > 0)
|
|
250
|
+
result.warnings = warnings;
|
|
234
251
|
return result;
|
|
235
252
|
}
|
|
236
253
|
// ────────────────────────── Multiline 检测 ──────────────────────────
|
package/dist/mcp/instructions.js
CHANGED
|
@@ -119,12 +119,20 @@ RemNote 格式设置(标题、高亮、代码等)底层通过 Powerup 机制
|
|
|
119
119
|
|
|
120
120
|
所有操作都依赖一个活跃的会话(= 守护进程的生命周期)。
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
> **⚠️ 关于 Plugin 加载方式(防幻觉红线)**:
|
|
123
|
+
> - 本插件是**开发者插件**,通过 RemNote「开发你的插件」功能加载本地 URL
|
|
124
|
+
> - **禁止**告诉用户"去插件市场/商店搜索安装"——本插件**不在 RemNote 插件市场中**
|
|
125
|
+
> - **禁止**告诉用户"Settings → Plugins"——这个路径不存在
|
|
126
|
+
> - **禁止**编造不存在的安装流程——严格按照下方步骤引导用户
|
|
127
|
+
|
|
128
|
+
### 标准模式(默认,推荐)
|
|
129
|
+
|
|
130
|
+
**标准模式是日常使用的推荐方式**。用户在自己的浏览器中打开 RemNote 并加载 Plugin,Agent 可以通过 \`read_context\` 感知用户正在浏览的页面和焦点位置,实现真正的协作。
|
|
123
131
|
|
|
124
132
|
\`\`\`
|
|
125
133
|
connect → 启动 daemon(幂等)
|
|
126
134
|
↓
|
|
127
|
-
⚠️
|
|
135
|
+
⚠️ 引导用户加载 Plugin(见下方 ★ 标记的步骤)
|
|
128
136
|
↓
|
|
129
137
|
health → 确认三层就绪(daemon / Plugin / SDK)
|
|
130
138
|
↓
|
|
@@ -133,11 +141,34 @@ health → 确认三层就绪(daemon / Plugin / SDK)
|
|
|
133
141
|
disconnect → 关闭 daemon,清空所有缓存
|
|
134
142
|
\`\`\`
|
|
135
143
|
|
|
136
|
-
###
|
|
144
|
+
### ★ 标准模式:connect 后引导用户加载 Plugin(核心步骤)
|
|
145
|
+
|
|
146
|
+
\`connect\`(不传 headless)成功只意味着 daemon 和 Plugin 服务已启动,**Plugin 并未自动连接**。你必须引导用户完成以下操作:
|
|
147
|
+
|
|
148
|
+
**首次使用**(RemNote 从未加载过此插件):
|
|
149
|
+
1. 打开 RemNote 桌面端或网页端
|
|
150
|
+
2. 点击左侧边栏底部的**插件图标**(拼图形状)
|
|
151
|
+
3. 点击「**开发你的插件**」(Develop Your Plugin)
|
|
152
|
+
4. 在输入框中填入 connect 输出的 **Plugin 服务地址**(如 \`http://localhost:29101\`)
|
|
153
|
+
5. 等待插件加载完成
|
|
154
|
+
|
|
155
|
+
**非首次使用**(之前已加载过此插件):
|
|
156
|
+
- 只需**刷新 RemNote 页面**即可(浏览器 F5 或 Cmd+R)
|
|
157
|
+
|
|
158
|
+
**你必须**:执行 \`connect\` 后,**立即**将上述步骤告知用户,**禁止**跳过此步直接调用业务命令。引导用户完成后,用 \`health\` 确认三层就绪再继续。
|
|
159
|
+
|
|
160
|
+
### Headless 模式(特殊场景,不推荐日常使用)
|
|
137
161
|
|
|
138
162
|
通过 setup(一次性)+ headless Chrome 实现自动连接,后续 connect 无需用户介入。
|
|
139
163
|
|
|
140
|
-
**⚠️
|
|
164
|
+
**⚠️ 不推荐日常使用**。Headless Chrome 是后台独立实例,**会丢失用户上下文**:\`read_context\` 返回的是 headless Chrome 的上下文,不是用户浏览器的。Agent 无法感知用户正在浏览和操作的页面,协作体验大打折扣。
|
|
165
|
+
|
|
166
|
+
**仅在以下场景使用 headless**:
|
|
167
|
+
- 用户明确要求在**服务器/无 GUI 环境**中运行
|
|
168
|
+
- 用户明确表示**不想参与操作**,希望全自动化(CI/CD、定时任务、批量处理等)
|
|
169
|
+
- 用户自己不在 RemNote 前面,不需要与 Agent 协作浏览
|
|
170
|
+
|
|
171
|
+
**默认始终使用标准模式**,除非用户主动要求 headless。
|
|
141
172
|
|
|
142
173
|
#### 首次使用(setup)
|
|
143
174
|
|
|
@@ -169,22 +200,6 @@ disconnect → 关闭 daemon + headless Chrome,清空所有缓存,清除 hea
|
|
|
169
200
|
- \`health(reload=true)\`:重载 headless Chrome 页面
|
|
170
201
|
- Plugin 始终不连接 → 可能登录 session 过期,需重新 setup
|
|
171
202
|
|
|
172
|
-
### ⚠️ 标准模式:connect 后需要用户配合(重要)
|
|
173
|
-
|
|
174
|
-
\`connect\`(不传 headless)成功只意味着 daemon 和 Plugin 服务已启动,**Plugin 并未自动连接**。
|
|
175
|
-
|
|
176
|
-
**首次使用**(RemNote 从未加载过此插件):
|
|
177
|
-
1. 打开 RemNote 桌面端或网页端
|
|
178
|
-
2. 点击左侧边栏底部的插件图标(拼图形状)
|
|
179
|
-
3. 点击「开发你的插件」(Develop Your Plugin)
|
|
180
|
-
4. 在输入框中填入 connect 输出的 Plugin 服务地址(如 \`http://localhost:29101\`)
|
|
181
|
-
5. 等待插件加载完成
|
|
182
|
-
|
|
183
|
-
**非首次使用**(之前已加载过此插件):
|
|
184
|
-
- 只需**刷新 RemNote 页面**即可(浏览器 F5 或 Cmd+R)
|
|
185
|
-
|
|
186
|
-
**你必须**:执行 \`connect\` 后,**立即告知用户需要完成上述操作**,不要直接调用业务命令。引导用户完成后,用 \`health\` 确认三层就绪再继续。
|
|
187
|
-
|
|
188
203
|
### Windows 注意事项
|
|
189
204
|
|
|
190
205
|
- 默认模式秒级启动(预构建 plugin)
|
|
@@ -311,7 +326,9 @@ changes: { "type": "concept", "highlightColor": "Yellow", "fontSize": "H1" }
|
|
|
311
326
|
|
|
312
327
|
新增行(无 remId 注释的行)支持以下格式:
|
|
313
328
|
|
|
314
|
-
**Markdown 前缀**:\`# \` \`## \` \`### \` \`- [ ] \` \`- [x] \` \\\`code\\\` \`---\`
|
|
329
|
+
**Markdown 前缀**:\`# \` \`## \` \`### \` \`- [ ] \` \`- [x] \` \`> \`(引用块) \`1. \`(有序列表) \\\`code\\\` \`---\`
|
|
330
|
+
|
|
331
|
+
> **⚠️ 有序列表必须用 \`1. \` 前缀(Lazy Numbering)**:RemNote 有序列表采用 Lazy Numbering——所有列表项统一写 \`1. \`,RemNote 按层级自动编号(1./2./3./A./B./I./II.)。不要手动编号(如 \`2. \` \`3. \`)。\`2. \`~\`9. \` 会被容错处理(归一化为 isListItem 并返回 templateWarnings 警告),\`10. \` 及以上不会被识别为有序列表。
|
|
315
332
|
|
|
316
333
|
**箭头分隔符**:
|
|
317
334
|
- 单行:\`→\`(forward)\`←\`(backward)\`↔\`(both)——格式 \`text → backText\`
|
|
@@ -326,59 +343,39 @@ changes: { "type": "concept", "highlightColor": "Yellow", "fontSize": "H1" }
|
|
|
326
343
|
- \`<!--portal refs:id1,id2-->\`(创建并引用指定 Rem)
|
|
327
344
|
- \`<!--portal-->\`(创建空 Portal)
|
|
328
345
|
|
|
329
|
-
####
|
|
346
|
+
#### 行引用模板 \`{{remId}}\`
|
|
330
347
|
|
|
331
|
-
|
|
332
|
-
\`\`\`
|
|
333
|
-
oldStr:
|
|
334
|
-
最后一个兄弟 <!--idZ-->
|
|
335
|
-
newStr:
|
|
336
|
-
最后一个兄弟 <!--idZ-->
|
|
337
|
-
新增行
|
|
338
|
-
\`\`\`
|
|
348
|
+
已有行支持两种写法:**模板模式**(推荐)和**完整匹配模式**(回退)。
|
|
339
349
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
oldStr:
|
|
343
|
-
子节点 A <!--idA-->
|
|
344
|
-
孙节点 A1 <!--idA1-->
|
|
345
|
-
子节点 B <!--idB-->
|
|
346
|
-
newStr:
|
|
347
|
-
子节点 B <!--idB-->
|
|
348
|
-
\`\`\`
|
|
350
|
+
**模板模式**(优先使用):用 \`{{remId}}\` 引用已有行,系统自动展开为完整内容(不含缩进)。节省 token、减少复制错误。
|
|
351
|
+
**完整匹配模式**(回退):直接从大纲复制完整行内容(含 \`<!--remId 元数据-->\`)。
|
|
349
352
|
|
|
350
|
-
|
|
351
|
-
\`\`\`
|
|
352
|
-
oldStr:
|
|
353
|
-
子节点 A <!--idA-->
|
|
354
|
-
newStr:
|
|
355
|
-
什么是线性回归? ↓
|
|
356
|
-
一种基本的回归分析方法
|
|
357
|
-
假设因变量与自变量呈线性关系
|
|
358
|
-
子节点 A <!--idA-->
|
|
359
|
-
\`\`\`
|
|
353
|
+
**策略:优先模板,连续失败则回退**。如果模板模式连续 2+ 次因 ID 错误导致 \`old_str not found\`,说明当前上下文不足以准确引用 ID——立即切换到完整匹配模式(从最新大纲复制完整行内容),不要反复重试模板。
|
|
360
354
|
|
|
361
|
-
|
|
355
|
+
**模板模式示例**:
|
|
356
|
+
\`\`\`
|
|
357
|
+
# 重排两个节点
|
|
358
|
+
oldStr: " {{id1_1}}\\n {{id1_2}}"
|
|
359
|
+
newStr: " {{id1_2}}\\n {{id1_1}}"
|
|
362
360
|
|
|
363
|
-
|
|
361
|
+
# 删除带子节点的行
|
|
362
|
+
oldStr: " {{idA}}\\n {{idA1}}\\n {{idB}}"
|
|
363
|
+
newStr: " {{idB}}"
|
|
364
364
|
|
|
365
|
-
|
|
365
|
+
# 末尾新增行(新增行手动写,已有行用模板)
|
|
366
|
+
oldStr: " {{idZ}}"
|
|
367
|
+
newStr: " 新增行\\n {{idZ}}"
|
|
368
|
+
\`\`\`
|
|
366
369
|
|
|
367
|
-
|
|
370
|
+
**完整匹配模式示例**(回退时使用):
|
|
368
371
|
\`\`\`
|
|
369
|
-
// 不用模板:
|
|
370
372
|
oldStr: " 动态数组 <!--id1_1 type:concept-->\\n 静态数组 <!--id1_2 type:concept-->"
|
|
371
373
|
newStr: " 静态数组 <!--id1_2 type:concept-->\\n 动态数组 <!--id1_1 type:concept-->"
|
|
372
|
-
|
|
373
|
-
// 用模板:
|
|
374
|
-
oldStr: " {{id1_1}}\\n {{id1_2}}"
|
|
375
|
-
newStr: " {{id1_2}}\\n {{id1_1}}"
|
|
376
374
|
\`\`\`
|
|
377
375
|
|
|
378
|
-
|
|
376
|
+
**模板规则**:
|
|
379
377
|
- \`{{remId}}\` 展开为**不含缩进**的完整行内容,缩进由你控制
|
|
380
|
-
- 只匹配纯字母数字(\`[a-zA-Z0-9]+\`),与 RemNote cloze 语法 \`{{text}}\`
|
|
381
|
-
- 匹配到但不在缓存大纲中的 \`{{xxx}}\` 原样保留(可能是 cloze),并输出 warning
|
|
378
|
+
- 只匹配纯字母数字(\`[a-zA-Z0-9]+\`),与 RemNote cloze 语法 \`{{text}}\` 不冲突
|
|
382
379
|
- 新增行不能用 \`{{}}\`(新增行没有 remId)
|
|
383
380
|
- 可以混用:部分行用 \`{{id}}\`,部分行手动写
|
|
384
381
|
|
|
@@ -387,15 +384,13 @@ newStr: " {{id1_2}}\\n {{id1_1}}"
|
|
|
387
384
|
在有子节点的 Rem 和其 children 之间插入新行,会导致新行"劫持"已有 children:
|
|
388
385
|
|
|
389
386
|
\`\`\`
|
|
390
|
-
❌
|
|
391
|
-
oldStr:
|
|
392
|
-
|
|
393
|
-
|
|
387
|
+
❌ 错误(模板):
|
|
388
|
+
oldStr: " {{idA}}" newStr: " {{idA}}\\n 新行" ← idA 有子节点,新行劫持 children!
|
|
389
|
+
❌ 错误(完整匹配):
|
|
390
|
+
oldStr: " 水分子 ↓ <!--idA-->" newStr: " 水分子 ↓ <!--idA-->\\n 新行" ← 同理
|
|
394
391
|
|
|
395
392
|
✅ 正确:插在末尾
|
|
396
|
-
oldStr:
|
|
397
|
-
newStr: 最后一个兄弟 <!--idZ-->
|
|
398
|
-
新行
|
|
393
|
+
oldStr: " {{idZ}}" newStr: " {{idZ}}\\n 新行"
|
|
399
394
|
\`\`\`
|
|
400
395
|
|
|
401
396
|
#### 创建新节点并移动已有 children
|
|
@@ -427,7 +422,7 @@ newStr: 最后一个兄弟 <!--idZ-->
|
|
|
427
422
|
|
|
428
423
|
\`health\` 默认查询所有活跃实例的三层状态(daemon / Plugin / SDK),返回 \`instances\` 数组。有 \`--instance\` 或 \`--headless\` 时只查询指定实例。每个实例的 \`plugin.isTwin\` 标记是否为孪生连接。
|
|
429
424
|
|
|
430
|
-
故障定位:无活跃实例 → \`connect\`;Plugin 未连接 → 引导用户操作 RemNote
|
|
425
|
+
故障定位:无活跃实例 → \`connect\`;Plugin 未连接 → 引导用户操作 RemNote(标准模式下刷新页面或首次加载插件);SDK 未就绪 → 等待重试。
|
|
431
426
|
|
|
432
427
|
### 场景 H:管理增强项目
|
|
433
428
|
|
|
@@ -509,7 +504,7 @@ tags, sources, positionAmongstSiblings, portalDirectlyIncludedRem
|
|
|
509
504
|
\`\`\`
|
|
510
505
|
|
|
511
506
|
- 缩进:每级 2 空格
|
|
512
|
-
- 前缀:\`# \`(H1)、\`## \`(H2)、\`### \`(H3)、\`- [ ] \`(待办)、\`- [x] \`(已完成)、\\\`...\\\`(代码)、\`---\`(分隔线)
|
|
507
|
+
- 前缀:\`# \`(H1)、\`## \`(H2)、\`### \`(H3)、\`- [ ] \`(待办)、\`- [x] \`(已完成)、\`> \`(引用块)、\`1. \`(有序列表)、\\\`...\\\`(代码)、\`---\`(分隔线)
|
|
513
508
|
|
|
514
509
|
### 元数据标记
|
|
515
510
|
|
|
@@ -544,7 +539,7 @@ tags, sources, positionAmongstSiblings, portalDirectlyIncludedRem
|
|
|
544
539
|
新增行(无 remId 注释的行)在 newStr 中出现时,会被创建为新的 Rem。格式选项:
|
|
545
540
|
|
|
546
541
|
- 纯文本行:\`新内容\`
|
|
547
|
-
- 带前缀:\`# 新标题\`、\`- [ ]
|
|
542
|
+
- 带前缀:\`# 新标题\`、\`- [ ] 新待办\`、\`> 引用内容\`、\`1. 列表项\`
|
|
548
543
|
- 带箭头:\`问题 → 答案\`、\`概念 ↔ 定义\`、\`题目 ↓\`
|
|
549
544
|
- 带元数据注释(metadata-only,无 remId):\`新行 <!--type:concept doc-->\`
|
|
550
545
|
- Portal 行:\`<!--portal refs:id1,id2-->\`
|
|
@@ -643,6 +638,7 @@ tags, sources, positionAmongstSiblings, portalDirectlyIncludedRem
|
|
|
643
638
|
|
|
644
639
|
### 诊断决策树
|
|
645
640
|
|
|
641
|
+
|
|
646
642
|
\`\`\`
|
|
647
643
|
命令报错
|
|
648
644
|
├─ "守护进程未运行" → connect 未执行或 daemon 已超时 → 执行 connect
|
|
@@ -71,7 +71,8 @@ export function registerEditTools(server) {
|
|
|
71
71
|
'\\n4. 重排:调换同级行的顺序' +
|
|
72
72
|
'\\n执行顺序:Create → Move → Reorder → Delete' +
|
|
73
73
|
'\\n\\n新增行格式:' +
|
|
74
|
-
'\\n- Markdown 前缀:# / ## / ### / - [ ] / - [x] / `代码` / ---' +
|
|
74
|
+
'\\n- Markdown 前缀:# / ## / ### / - [ ] / - [x] / > / 1. / `代码` / ---' +
|
|
75
|
+
'\\n ⚠️ 有序列表必须用 `1. `(Lazy Numbering):RemNote 自动编号,不要写 `2. ` `3. ` 等。`2.`~`9.` 会被容错归一化并返回 templateWarnings 警告,`10.` 及以上不识别为列表。' +
|
|
75
76
|
'\\n- 箭头分隔符(闪卡):→ ← ↔(单行)、↓ ↑ ↕(多行,带 backText 或子节点为答案)' +
|
|
76
77
|
'\\n- 元数据注释(可选):<!--type:concept--> <!--doc--> <!--tag:Name(id)--> 可组合' +
|
|
77
78
|
'\\n- Portal 创建:<!--portal refs:id1,id2--> 或 <!--portal-->(空 Portal)' +
|
|
@@ -22,7 +22,7 @@ export function registerInfraTools(server) {
|
|
|
22
22
|
// -------------------------------------------------------------------------
|
|
23
23
|
server.addTool({
|
|
24
24
|
name: 'connect',
|
|
25
|
-
description: '启动守护进程(daemon),建立 CLI 与 RemNote Plugin 之间的通信通道。这是所有业务命令(read_rem、edit_rem、search 等)的前置步骤。\n\n适用场景:\n- 开始一次 RemNote 操作会话前,必须先调用此工具\n- 不确定 daemon 是否在运行时,也可安全调用(幂等)\n\n两种模式:\n-
|
|
25
|
+
description: '启动守护进程(daemon),建立 CLI 与 RemNote Plugin 之间的通信通道。这是所有业务命令(read_rem、edit_rem、search 等)的前置步骤。\n\n适用场景:\n- 开始一次 RemNote 操作会话前,必须先调用此工具\n- 不确定 daemon 是否在运行时,也可安全调用(幂等)\n\n两种模式:\n- 标准模式(默认,推荐):启动 daemon 后用户在自己的浏览器中加载 Plugin。优势:Agent 可通过 read_context 感知用户正在浏览的页面,实现协作\n- Headless 模式(headless=true,不推荐日常使用):自动启动 headless Chrome,无需用户操作。⚠️ 会丢失用户上下文(read_context 返回 headless 实例的上下文,不是用户浏览器的)。仅在以下场景使用:用户明确要求在服务器/无 GUI 环境运行、用户不想参与操作(全自动化)、用户不在 RemNote 前面。需先完成 setup\n\n⚠️ 标准模式 Plugin 加载(connect 后必须引导用户完成):\n本插件是开发者插件,不在 RemNote 插件市场中,禁止告诉用户去市场搜索安装。正确步骤:\n- 首次:RemNote → 左下角插件图标 → 「开发你的插件」→ 填入 Plugin 服务地址(如 http://localhost:29101)\n- 非首次:刷新 RemNote 页面即可\n引导用户完成后用 health 确认三层就绪再执行业务命令。\n\nHeadless 模式会话持久化:connect(headless=true) 后,MCP Server 自动记住 headless 状态,后续所有工具调用自动路由到 headless 实例。disconnect 或 clean 后自动清除。\n\n输出:返回 JSON,关键字段 ok、alreadyRunning、instance、slotIndex、pid、wsPort、headless。\n幂等:重复调用不会启动多个 daemon。daemon 默认 30 分钟无活动自动关闭。\n关联工具:setup(headless 前置)、disconnect(结束会话)、health(检查状态)',
|
|
26
26
|
parameters: z.object({
|
|
27
27
|
headless: z.boolean().optional().describe('启用 headless 模式:自动启动 Chrome 加载 Plugin(需先 setup)'),
|
|
28
28
|
}),
|