@fanchao8609/agent_brain_sync 1.8.2 → 1.8.3

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 CHANGED
@@ -32,7 +32,11 @@ abs install --agent pi --no-mcp # 只装 hook + skill,不要 MCP
32
32
  | claude-code | `~/.claude/settings.json` hooks | `mcpServers.abs` (stdio) | `~/.claude/skills/abs-agent-brain-sync/` |
33
33
  | codex | `~/.codex/hooks.json` | config.toml `[mcp_servers.abs]` | `~/.codex/skills/` |
34
34
  | opencode | `~/.config/opencode/plugins/abs.ts` | opencode.json mcp.abs | skills/ |
35
- | pi | `~/.pi/agent/extensions/abs.ts` | extension 内桥接 | skills/ |
35
+ | pi | `~/.pi/agent/extensions/abs.ts` | `~/.pi/agent/mcp.json` `mcpServers.abs` | `~/.pi/agent/skills/` |
36
+
37
+ > **skill 规则**:`skill/` 下每个含 `SKILL.md` 的子目录 = 一个 skill,
38
+ > **目录名即安装名**(须与 frontmatter `name` 一致,否则 pi 会告警)。
39
+ > 新增 skill 只需建目录,无需改代码。
36
40
 
37
41
  - **幂等**:重复安装 = 更新,写入前自动备份
38
42
  - **共存**:追加式合并,不会顶掉你这个事件上的其它 hook
@@ -110,7 +114,7 @@ npm i -g @fanchao8609/agent_brain_sync@latest
110
114
  abs install # 重新刷 hook/skill
111
115
  ```
112
116
 
113
- > ⚠️ 改完源码(尤其 `skill/SKILL.md`、`src/`)后**必须 `abs install --yes` 重扇出**,否则宿主还在跑旧副本 —— 版本号与代码会脱节。发布用 `npm version patch`(自动打 tag),别手改 `package.json` 的 version;发布后 `npm view` 有缓存延迟,必要时 `npm cache clean --force` 再验。
117
+ > ⚠️ 改完源码(尤其 `skill/*/SKILL.md`、`src/`)后**必须 `abs install --yes` 重扇出**,否则宿主还在跑旧副本 —— 版本号与代码会脱节。发布用 `npm version patch`(自动打 tag),别手改 `package.json` 的 version;发布后 `npm view` 有缓存延迟,必要时 `npm cache clean --force` 再验。
114
118
 
115
119
  ---
116
120
 
@@ -150,7 +154,7 @@ agent_brain_sync/
150
154
  ├── src/userconfig.js 使用者姓名配置(作者标记)
151
155
  ├── src/wrapup.js Stop 收尾快照/归档
152
156
  ├── hooks/event.sh hook 模板
153
- ├── skill/SKILL.md 技能(装到各智能体)
157
+ ├── skill/<名称>/SKILL.md 技能(每个子目录 = 一个 skill,装到各智能体)
154
158
  └── test/ 单测
155
159
  ```
156
160
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanchao8609/agent_brain_sync",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "description": "agent-brain-sync: 跨会话 AI 编码记忆 — hook 纯触发 + CLI/MCP 读写 .brain markdown 图谱, 防并发写保护。",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: bug-hunter
2
+ name: abs-bug-hunter
3
3
  description: 按流程论排查 bug,先定位改动 → 日志打印 → 顺藤摸瓜逐层排查
4
4
  ---
5
5
 
package/src/install.js CHANGED
@@ -14,30 +14,64 @@ import { HOSTS, hostByKey } from './hosts.js';
14
14
 
15
15
  const ABS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '..');
16
16
  const HOOK_TEMPLATE = join(ABS_DIR, 'hooks', 'event.sh');
17
- const SKILL_SOURCE = join(ABS_DIR, 'skill', 'SKILL.md');
18
17
 
19
- // 随 abs 一并安装的附带 skill(agent 排查 bug 时挂载)。
20
- // 必须放在本包内随包发布 —— 不能假设宿主已装:实测 co​dex 只有 abs 自己装的 skill,
21
- // 若引用外部 skill 会成悬空。ponytail: 只加这一个,需要更多时改成扫 skill/ 子目录。
22
- const BUNDLED_SKILLS = [
23
- { name: 'bug-hunter', src: join(ABS_DIR, 'skill', 'bug-hunter', 'SKILL.md') },
24
- ];
25
-
26
- /** 安装主 skill + 全部附带 skill 到该宿主。收口在此:四处安装点共用,
27
- * 新增附带 skill 时只改本函数(避免「改一份不算改」)。返回步骤行。 */
18
+ // ---- skill 包规则(单一规则,无特例)----
19
+ // skill/ 下**每个含 SKILL.md 的子目录**就是一个 skill,随包发布。
20
+ // 目录名 = 宿主 skills/ 下的安装目录名 = frontmatter 的 name(三者必须一致:
21
+ // pi 在目录名与 name 不一致时会告警)。
22
+ //
23
+ // 为什么全带 abs- 前缀:
24
+ // 1. 归属可辨 —— 带前缀才敢判「这是 abs 装的、可随 install 更新/卸载」;
25
+ // 不带前缀的副本可能是用户自己放的,卸载时不敢动。
26
+ // 2. 避开同名冲突 —— pi 同时扫 ~/.pi/agent/skills 与 ~/.agents/skills
27
+ // (pi 文档 Locations 一节),后者常有同名副本。两份同名实文件 → pi 报
28
+ // skill 冲突(实测: 曾装成 bug-hunter,与 ~/.agents/skills/bug-hunter 相撞)。
29
+ //
30
+ // 新增 skill: 建 skill/<名称>/SKILL.md 即可,无需改代码(原先写死一条,见旧注释 ponytail)。
31
+ const SKILL_ROOT = join(ABS_DIR, 'skill');
32
+ const ALL_SKILLS = (() => {
33
+ const out = [];
34
+ let entries = [];
35
+ try { entries = fsSync.readdirSync(SKILL_ROOT, { withFileTypes: true }); } catch { /* 无 skill/ 目录: 无 skill 可装 */ }
36
+ for (const e of entries) {
37
+ if (!e.isDirectory()) continue;
38
+ const src = join(SKILL_ROOT, e.name, 'SKILL.md');
39
+ if (!fsSync.existsSync(src)) continue; // 只认含 SKILL.md 的子目录
40
+ out.push({ name: e.name, src });
41
+ }
42
+ return out;
43
+ })();
44
+ // 主 skill(会话开场加载的那个)。它也只是 ALL_SKILLS 的一员,此处仅用于告警比对。
45
+ const SKILL_SOURCE = join(SKILL_ROOT, 'abs-agent-brain-sync', 'SKILL.md');
46
+
47
+ /** 安装全部 skill 到该宿主。收口在此:四处安装点共用 —— 新增 skill 只改
48
+ * skill/ 目录,本函数与调用方都不动(避免「改一份不算改」)。返回步骤行。 */
28
49
  async function installSkills(agentKey) {
29
50
  const steps = [];
30
- const base = hostSkillDir(agentKey);
31
- await atomicWrite(join(base, 'SKILL.md'), await fs.readFile(SKILL_SOURCE, 'utf8'));
32
- steps.push(`✓ skill → ${join(base, 'SKILL.md')}`);
33
- for (const b of BUNDLED_SKILLS) {
34
- const t = join(base, '..', b.name, 'SKILL.md');
35
- await atomicWrite(t, await fs.readFile(b.src, 'utf8'));
51
+ const root = join(hostSkillDir(agentKey), '..');
52
+ for (const s of ALL_SKILLS) {
53
+ const t = join(root, s.name, 'SKILL.md');
54
+ await atomicWrite(t, await fs.readFile(s.src, 'utf8'));
36
55
  steps.push(`✓ skill → ${t}`);
37
56
  }
38
57
  return steps;
39
58
  }
40
59
 
60
+ /** 卸载该宿主的全部 skill(主 + 附带)。与 installSkills 同源同规则。
61
+ * 曾经的坑: 只有 claude-code 删了附带 skill,其余三宿主留下 abs-bug-hunter/ 残留。
62
+ */
63
+ async function uninstallSkills(agentKey) {
64
+ const steps = [];
65
+ const root = join(hostSkillDir(agentKey), '..');
66
+ for (const s of ALL_SKILLS) {
67
+ const d = join(root, s.name);
68
+ await fs.rm(d, { recursive: true, force: true });
69
+ steps.push(`✓ skill 已删除 → ${d}`);
70
+ }
71
+ return steps;
72
+ }
73
+
74
+
41
75
  /**
42
76
  * 本包的稳定入口路径解析(mcp.js / abs.js 通用)。
43
77
  *
@@ -512,10 +546,7 @@ async function uninstallClaudeCode() {
512
546
  await fs.rm(join(homedir(), '.abs', 'hooks', 'claude-code'), { recursive: true, force: true });
513
547
  steps.push(`✓ ~/.abs/hooks/claude-code/ (本 agent hook 脚本) 已删除`);
514
548
  // skill
515
- const skillDir = hostSkillDir('claude-code');
516
- await fs.rm(skillDir, { recursive: true, force: true });
517
- for (const b of BUNDLED_SKILLS) await fs.rm(join(skillDir, '..', b.name), { recursive: true, force: true });
518
- steps.push(`✓ skill 已删除(含附带: ${BUNDLED_SKILLS.map((x) => x.name).join(", ")})`);
549
+ steps.push(...await uninstallSkills('claude-code'));
519
550
  return steps;
520
551
  }
521
552
 
@@ -640,8 +671,7 @@ async function uninstallCodex() {
640
671
  steps.push(`✓ MCP 已从 ${mcpP} 移除`);
641
672
  }
642
673
  } catch {}
643
- await fs.rm(hostSkillDir('codex'), { recursive: true, force: true });
644
- steps.push(`✓ skill 已删除`);
674
+ steps.push(...await uninstallSkills('codex'));
645
675
  return steps;
646
676
  }
647
677
 
@@ -768,8 +798,7 @@ async function uninstallOpenCode() {
768
798
  await atomicWrite(mcpP, JSON.stringify(cfg, null, 2));
769
799
  steps.push(`✓ MCP 已从 ${mcpP} 移除`);
770
800
  }
771
- await fs.rm(hostSkillDir('opencode'), { recursive: true, force: true });
772
- steps.push(`✓ skill 已删除`);
801
+ steps.push(...await uninstallSkills('opencode'));
773
802
  return steps;
774
803
  }
775
804
 
@@ -801,8 +830,7 @@ async function uninstallPi() {
801
830
  const steps = [];
802
831
  await fs.rm(join(hostConfigRoot('pi'), 'agent', 'extensions', 'abs.ts'), { force: true });
803
832
  steps.push(`✓ extension 已删除`);
804
- await fs.rm(hostSkillDir('pi'), { recursive: true, force: true });
805
- steps.push(`✓ skill 已删除`);
833
+ steps.push(...await uninstallSkills('pi'));
806
834
  const mcpP = join(hostConfigRoot('pi'), 'agent', 'mcp.json');
807
835
  const cfg = await readJson(mcpP, { strict: false });
808
836
  if (cfg === null) {