@fanchao8609/agent_brain_sync 1.3.0 → 1.3.2

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/bin/mcp.js CHANGED
@@ -59,8 +59,16 @@ const server = new McpServer({
59
59
  version: pkgVersion(),
60
60
  });
61
61
 
62
+ // 统一注册入口(总是经 withTrace)。
63
+ // 坑: 以前 9 处工具各自直接调 server.tool(...) 而不包 withTrace, 导致 withTrace 定义了
64
+ // 但从未被调用 → mcp.log 永远不生成。可观测性静默缺失(无任何症状), 藏了很久。
65
+ // 现在工具只经本函数注册, 包 trace 这件事无法再被遗漏。
66
+ function tool(name, description, schema, handler) {
67
+ server.tool(name, description, schema, withTrace(name, handler));
68
+ }
69
+
62
70
  // 工具面(narrow on purpose — 只暴露读/查/记状态,不做深提炼)
63
- server.tool(
71
+ tool(
64
72
  'abs_resolve_project',
65
73
  '定位当前项目的 .brain 图谱根(从给定目录向上找最近 .brain/)。多项目隔离的唯一入口。',
66
74
  { cwd: z.string().describe('当前工作目录,默认进程 cwd') },
@@ -73,7 +81,7 @@ server.tool(
73
81
  }
74
82
  );
75
83
 
76
- server.tool(
84
+ tool(
77
85
  'abs_board',
78
86
  '读取当前项目 todo 看板(含断点/Next-Step)。先 resolve 得到项目,再传其根目录。',
79
87
  { cwd: z.string().describe('项目内任意目录') },
@@ -84,7 +92,7 @@ server.tool(
84
92
  }
85
93
  );
86
94
 
87
- server.tool(
95
+ tool(
88
96
  'abs_load',
89
97
  '开机读状态:index 路线 + todo 看板 + 最近 log。跨会话续接的入口。',
90
98
  { cwd: z.string().describe('项目内任意目录') },
@@ -95,7 +103,7 @@ server.tool(
95
103
  }
96
104
  );
97
105
 
98
- server.tool(
106
+ tool(
99
107
  'abs_task',
100
108
  '任务实时落盘(幂等键 = id)。add/start 登记 / note 补断点(改到哪个文件哪行) / blocked 碰壁 / done 完成归位。',
101
109
  {
@@ -113,7 +121,7 @@ server.tool(
113
121
  }
114
122
  );
115
123
 
116
- server.tool(
124
+ tool(
117
125
  'abs_status',
118
126
  '当前项目 + 图谱概要(各类页数)。',
119
127
  { cwd: z.string().describe('项目内任意目录') },
@@ -124,7 +132,7 @@ server.tool(
124
132
  }
125
133
  );
126
134
 
127
- server.tool(
135
+ tool(
128
136
  'abs_query',
129
137
  '检索当前项目 .brain/ 知识页(多词 OR):以前踩过什么坑、哪页记了 X。',
130
138
  { cwd: z.string().describe('项目内任意目录'), terms: z.array(z.string()).min(1).describe('检索词') },
@@ -135,7 +143,7 @@ server.tool(
135
143
  }
136
144
  );
137
145
 
138
- server.tool(
146
+ tool(
139
147
  'abs_lint',
140
148
  '图谱体检:死链/孤岛/缺 frontmatter/超尺寸/sources 堆积/index 漏列。',
141
149
  { cwd: z.string().describe('项目内任意目录') },
@@ -146,7 +154,7 @@ server.tool(
146
154
  }
147
155
  );
148
156
 
149
- server.tool(
157
+ tool(
150
158
  'abs_note',
151
159
  '经验实时暂存:把刚踩的坑/技巧/结论一句话落进 sources/(防 context 断了流失)。Teardown 时再提炼进 concepts/。',
152
160
  {
@@ -161,7 +169,7 @@ server.tool(
161
169
  }
162
170
  );
163
171
 
164
- server.tool(
172
+ tool(
165
173
  'abs_wrapup',
166
174
  '收尾保险:把当前 todo 未完成任务快照到 ~/.abs/log/wrapup.log(下会话 load 时展示滞留对账)。',
167
175
  { cwd: z.string().describe('项目内任意目录') },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanchao8609/agent_brain_sync",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "agent-brain-sync: 跨会话 AI 编码记忆 — hook 纯触发 + CLI/MCP 读写 .brain markdown 图谱, 防并发写保护。",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/install.js CHANGED
@@ -6,6 +6,7 @@
6
6
  // - OpenCode / Pi: 官方只吃 TS plugin/extension(无 shell-hook 配置) → 初版给出手工指引。
7
7
  // 纪律: 幂等(重复安装=更新)、原子写(tmp+rename)、卸载只删自己装的、写入前备份。
8
8
  import { promises as fs } from 'node:fs';
9
+ import * as fsSync from 'node:fs';
9
10
  import { homedir } from 'node:os';
10
11
  import { join, dirname, resolve } from 'node:path';
11
12
  import { fileURLToPath } from 'node:url';
@@ -15,6 +16,31 @@ const ABS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '..');
15
16
  const HOOK_TEMPLATE = join(ABS_DIR, 'hooks', 'event.sh');
16
17
  const SKILL_SOURCE = join(ABS_DIR, 'skill', 'SKILL.md');
17
18
 
19
+ /**
20
+ * 写进宿主 MCP 配置的 mcp.js 路径。
21
+ * 坑: 直接用 ABS_DIR 是不稳定的 —— ABS_DIR = "install.js 自己住哪", 从仓库跑
22
+ * `abs install` 就把仓库路径写进宿主配置。之后若卸载/移动全局包, 该路径直接失效;
23
+ * 而 Co​dex 分支又"已存在即跳过", 一旦写错永不修正。
24
+ * 现在优先解析本包的稳定安装位置(全局 node_modules),解析不到才回退 ABS_DIR。
25
+ */
26
+ function mcpEntryPath() {
27
+ // 本包名(package.json),用于反查全局安装位置
28
+ let pkgName = '@fanchao8609/agent_brain_sync';
29
+ try {
30
+ pkgName = JSON.parse(fsSync.readFileSync(join(ABS_DIR, 'package.json'), 'utf8')).name || pkgName;
31
+ } catch { /* 保持默认 */ }
32
+ const candidates = [
33
+ // npm 全局根(最常见)
34
+ join(dirname(process.execPath), '..', 'lib', 'node_modules', pkgName, 'bin', 'mcp.js'),
35
+ join(process.env.npm_config_prefix || '', 'lib', 'node_modules', pkgName, 'bin', 'mcp.js'),
36
+ ];
37
+ for (const c of candidates) {
38
+ try { if (fsSync.existsSync(c)) return c; } catch { /* 试下一个 */ }
39
+ }
40
+ // 回退:仓库/本地安装形态
41
+ return join(ABS_DIR, 'bin', 'mcp.js');
42
+ }
43
+
18
44
  const MARK = '// abs-managed (agent-brain-sync)'; // TS plugin 标记
19
45
  const JSON_MARK_KEY = 'abs-managed'; // JSON 内我们的命名空间
20
46
 
@@ -103,7 +129,7 @@ async function installClaudeCode({ withMcp, withSkill, log }) {
103
129
  settings.mcpServers = settings.mcpServers || {};
104
130
  settings.mcpServers['abs'] = {
105
131
  command: process.execPath,
106
- args: [join(ABS_DIR, 'bin', 'mcp.js')],
132
+ args: [mcpEntryPath()],
107
133
  };
108
134
  await atomicWrite(settingsP, JSON.stringify(settings, null, 2));
109
135
  steps.push(`✓ MCP → settings.json mcpServers.abs (stdio)`);
@@ -183,12 +209,21 @@ async function installCodex({ withMcp, withSkill, log }) {
183
209
  let text = '';
184
210
  try { text = await fs.readFile(mcpP, 'utf8'); } catch {}
185
211
  if (!text.includes('[mcp_servers.abs]')) {
186
- const block = `\n[mcp_servers.abs]\ncommand = "${process.execPath}"\nargs = ["${join(ABS_DIR, 'bin', 'mcp.js')}"]\n`;
212
+ const block = `\n[mcp_servers.abs]\ncommand = "${process.execPath}"\nargs = ["${mcpEntryPath()}"]\n`;
187
213
  await backup(mcpP);
188
214
  await atomicWrite(mcpP, text.replace(/\s*$/, '') + '\n' + block);
189
215
  steps.push(`✓ MCP → ${mcpP} [mcp_servers.abs]`);
190
216
  } else {
191
- steps.push(`• MCP → ${mcpP} 已存在, 跳过`);
217
+ // 已存在也要校对路径: 旧版可能写入了仓库路径(不稳定) 或全局包已迁移。
218
+ const want = mcpEntryPath();
219
+ const re = /(\[mcp_servers\.abs\][^\[]*?args\s*=\s*\[)"[^"]*"(\])/s;
220
+ if (re.test(text) && !text.includes(`"${want}"`)) {
221
+ await backup(mcpP);
222
+ await atomicWrite(mcpP, text.replace(re, `$1"${want}"$2`));
223
+ steps.push(`✓ MCP → ${mcpP} 路径已校正 → ${want}`);
224
+ } else {
225
+ steps.push(`• MCP → ${mcpP} 已存在且路径正确, 跳过`);
226
+ }
192
227
  }
193
228
  }
194
229
  if (withSkill) {
@@ -526,7 +561,7 @@ async function installOpenCode({ withMcp, withSkill, log }) {
526
561
  cfg.mcp = cfg.mcp || {};
527
562
  cfg.mcp['abs'] = {
528
563
  type: 'local',
529
- command: [process.execPath, join(ABS_DIR, 'bin', 'mcp.js')],
564
+ command: [process.execPath, mcpEntryPath()],
530
565
  };
531
566
  await backup(mcpP);
532
567
  await atomicWrite(mcpP, JSON.stringify(cfg, null, 2));