@fanchao8609/agent_brain_sync 1.2.0 → 1.2.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/README.md CHANGED
@@ -1,96 +1,123 @@
1
- # abs — agent-brain-sync 跨会话 AI 编码记忆
1
+ # abs — agent-brain-sync
2
2
 
3
- 跨会话记忆的机械落盘:**hook 纯触发 → CLI/MCP 读写 → markdown 图谱(`.brain/`,可进 git)**。
4
- 让每个会话不再无状态——经验/进度/踩坑有统一落点,重开不"失忆"。
5
- 参考 [ai-memory](https://github.com/akitaonrails/ai-memory) 的接入机制,砍掉全部重引擎。
3
+ **跨会话 AI 编码记忆。** 让 AI 会话不再「重开就失忆」:任务进度、经验、踩坑都落盘到项目里的 markdown 图谱,下一个会话开机能直接读回来。
6
4
 
7
- > npm 发布名 `@fanchao8609/agent_brain_sync`(因 `agent-brain-sync` 已存在、相似名被 npm 拦截);全局命令 `abs`(包名与命令名独立)。
5
+ - 图谱纯 markdown,放在项目根 `.brain/`,**可以进 git**,人和 AI 读同一份
6
+ - 多项目自动隔离:从当前目录向上找最近的 `.brain/`
7
+ - 并发安全:CLI / MCP / hook 同时写不会互相覆盖
8
8
 
9
- ## 架构
9
+ npm 包名 `@fanchao8609/agent_brain_sync`,全局命令 **`abs`**。
10
10
 
11
+ ---
12
+
13
+ ## 安装
14
+
15
+ ```bash
16
+ npm install -g @fanchao8609/agent_brain_sync
17
+ abs install
11
18
  ```
12
- hook (宿主事件, 纯触发) ──▶ abs log (一行技术流水, fire-and-forget, 不阻塞)
13
- agent (会话中) ──▶ MCP (abs_load/abs_board/abs_task/…) ──▶ 读写 .brain/
14
- 人 (终端) ──▶ abs CLI 同一套读写
19
+
20
+ `abs install` 会交互式让你勾选智能体;也可以直接指定:
21
+
22
+ ```bash
23
+ abs install --agent pi # 只装 pi
24
+ abs install # 全部: claude-code / codex / opencode / pi
25
+ abs install --agent pi --no-mcp # 只装 hook + skill,不要 MCP
15
26
  ```
16
27
 
17
- - **唯一真源**: `<项目>/.brain/`(index/todo/log + concepts/entities/sources/syntheses/sessions)
18
- - **多项目隔离**: 任何读写先从 cwd 向上找最近 `.brain/`(代码写死,MCP 无状态,各会话各归各位)
19
- - **三层分工**:
20
- - **hook(机械层)**:宿主生命周期事件 → 只写技术日志(`~/.abs/log/`),不碰图谱 todo/log.md
21
- - **CLI/MCP(实时层)**:任务/经验实时落盘(`abs task start/note/blocked/done`、`abs note`)
22
- - **skill(自觉层)**:深提炼(sources→concepts)+ 收尾循环(详见 skill/SKILL.md)
23
- - **并发写保护**: 所有 .brain 读改写经 `lock.editFile`(`open wx` 原子锁 + 排队等锁 + SKIP 哨兵),跨进程(CLI/MCP/hook)同写不覆盖丢更新
28
+ 装了什么:
24
29
 
25
- ## 安装
30
+ | 宿主 | hook | MCP | skill |
31
+ |---|---|---|---|
32
+ | claude-code | `~/.claude/settings.json` hooks | `mcpServers.abs` (stdio) | `~/.claude/skills/abs-agent-brain-sync/` |
33
+ | codex | `~/.codex/hooks.json` | config.toml `[mcp_servers.abs]` | `~/.codex/skills/` |
34
+ | opencode | `~/.config/opencode/plugins/abs.ts` | opencode.json mcp.abs | skills/ |
35
+ | pi | `~/.pi/agent/extensions/abs.ts` | extension 内桥接 | skills/ |
36
+
37
+ - **幂等**:重复安装 = 更新,写入前自动备份
38
+ - **共存**:追加式合并,不会顶掉你这个事件上的其它 hook
39
+ - 装完**重启宿主**才生效
26
40
 
27
- ### 方式一:npm 全局安装(产品模式,推荐)
41
+ 从源码跑(开发用):
28
42
 
29
43
  ```bash
30
- npm install -g @fanchao8609/agent_brain_sync
31
- abs install # 交互式选智能体(或 --agent 指定)
44
+ git clone <repo-url> && cd agent_brain_sync
45
+ npm install && npm link # 之后全局就有 abs
32
46
  ```
33
47
 
34
- ### 方式二:从源码(开发)
48
+ ---
49
+
50
+ ## 怎么用
51
+
52
+ ### 项目里开一次
35
53
 
36
54
  ```bash
37
- git clone <repo> && cd <repo> && npm install
38
- node bin/abs.js install # 或先 npm link 使 `abs` 全局可用
55
+ cd 你的项目
56
+ abs init # 建 .brain/ 图谱,只需一次
39
57
  ```
40
58
 
41
- ### 安装到宿主(hook + MCP + skill)
59
+ ### 日常命令
42
60
 
43
61
  ```bash
44
- abs install # 全部: claude-code codex opencode pi
45
- abs install --agent claude-code # 只装 Claude Code (MCP+hook+skill)
46
- abs install --agent claude-code --no-mcp # 只 hook+skill
62
+ abs load # 开机读状态(路线 + 看板 + 最近流水)
63
+ abs todo # 看板 Today / In Progress / Blocked / Done
64
+
65
+ abs task start TASK-1 --note "要做什么"
66
+ abs task note TASK-1 --note "改到 X 文件 L40" # 实时断点
67
+ abs task blocked TASK-1 --note "卡在哪"
68
+ abs task done TASK-1
69
+
70
+ abs note "一句话经验" --tags 坑,docker # 经验暂存 → sources/
71
+ abs log "完成 X" # 记一行流水;abs log 无参 = 查看
72
+ abs query <词> # 检索图谱(多词 OR)
73
+ abs status # 当前项目 + 图谱概要
74
+ abs lint # 体检:死链/孤岛/超尺寸/堆积
75
+ ```
76
+
77
+ ### 工作流
78
+
79
+ - **hook 自动**:宿主生命周期事件写技术流水到 `~/.abs/log/`
80
+ - **实时层(你/AI 手动)**:任务和经验在边界处立刻用 `abs task` / `abs note` 落盘
81
+ - **收尾层**:`abs wrapup` 在 Stop 时快照未完成任务;下个会话开头对账 todo、沉淀经验、修 index
82
+
83
+ ---
47
84
 
48
- abs uninstall --agent claude-code # 卸载(只删 abs 装的,保留其它共存 hook)
49
- abs uninstall # 全部
85
+ ## 更新
86
+
87
+ ```bash
88
+ abs update
50
89
  ```
51
90
 
52
- 安装内容:
53
- | 宿主 | hook | MCP | skill |
54
- |---|---|---|---|
55
- | claude-code | `~/.claude/settings.json` hooks(与 moshi-hook 等**共存追加**,不覆盖) | settings.json `mcpServers.abs` (stdio) | `~/.claude/skills/abs-agent-brain-sync/` |
56
- | codex | `~/.codex/hooks.json` | config.toml `[mcp_servers.abs]` | `~/.codex/skills/` |
57
- | opencode | `~/.config/opencode/plugins/abs.ts` | opencode.json mcp.abs | skills/ |
58
- | pi | `~/.pi/agent/extensions/abs.ts` | extension 内桥接 | skills/ |
91
+ 查 npm 最新版 → 升级 → 自动重刷四个宿主的 hook/skill(hook 里烧的是绝对路径,升级后必须重装,`abs update` 帮你做了)。完事重启宿主。
92
+
93
+ 手动方式:
59
94
 
60
- 幂等:重复安装=更新;写入前自动备份;卸载只删 abs 的条目、保留其它共存 hook。
95
+ ```bash
96
+ npm i -g @fanchao8609/agent_brain_sync@latest
97
+ abs install # 重新刷 hook/skill
98
+ ```
61
99
 
62
- ## 项目里用
100
+ ---
101
+
102
+ ## 卸载
63
103
 
64
104
  ```bash
65
- abs init # 项目根建 .brain/ 图谱(一次)
66
- abs load # 开机读状态(index 路线 + todo 看板 + 最近 log)
67
- abs todo # 看板(Today/Backlog/Blocked/Done)
68
- abs task start TASK-1 --note "做什么" # 登记(幂等)
69
- abs task note TASK-1 --note "改到X文件L40" # 实时断点(↳ 断点: 行,幂等)
70
- abs task blocked TASK-1 --note "卡点原因" # 碰壁移 Blocked
71
- abs task done TASK-1 # 完成归位 Done
72
- abs note "经验一句话" --tags 坑,docker # 经验实时暂存 → sources/
73
- abs log "完成X:…" # 记一行工作成果流水;abs log 无参=查看 log.md
74
- abs query <词> # 检索图谱(多词 OR)
75
- abs lint # 图谱体检(死链/孤岛/超尺寸/堆积/index 漏列)
76
- abs status # 当前项目 + 图谱概要
105
+ abs uninstall # 全部宿主
106
+ abs uninstall --agent pi # 只卸 pi
107
+ npm uninstall -g @fanchao8609/agent_brain_sync
77
108
  ```
78
109
 
79
- 实时化分工:**hook 自动记技术流水;任务/经验经 CLI/MCP 实时落盘(每个任务边界立即调);
80
- 深提炼(sources→concepts)归收尾自觉层,工具不替你判断什么值得沉淀。** 详见 `skill/SKILL.md`。
110
+ 只删 abs 自己装的条目,**保留其它共存 hook**。
81
111
 
82
- ## 核心能力
112
+ **注意**:卸载**不会**删项目里的 `.brain/`——那是你的知识资产,要删自己 `rm -rf .brain`。
83
113
 
84
- - **并发写保护(lock)**: `.brain` 是"读-改-整写回",跨进程并发会互相覆盖。`src/lock.js` 用同目录 `.lock` 文件 `open('wx')` 原子抢占 + 排队等锁(预算 30s,指数退避)+ `SKIP` 哨兵,把 todo/index/log 的写串行化,避免丢失更新。
85
- - **统一读写收口(brainio)**: `src/brainio.js` 提供 `readBrain / writeBrain / appendBrain / createBrainFile`——所有需读写 .brain 文档的地方统一走它,写自动带 lock 防并发(新代码遵循此入口)。
86
- - **收尾自动化**: Stop 时 hook 在 `~/.abs/log/wrapup.log` 留 wrapup 提醒;下会话开头走"收尾循环"(对账 todo / 沉淀经验 / 修 index/log)。
87
- - **多宿主共存**: install 分区合并追加,不会顶掉同事件的其它 hook(如 moshi-hook)。
114
+ ---
88
115
 
89
116
  ## 开发
90
117
 
91
118
  ```bash
92
- npm test # 全量 110+ 单测(四层: CLI/install/MCP/hook + lock/brainio)
93
- npm run pack:check # 预览 npm 发布产物(files 白名单)
119
+ npm test # 110+ 单测(node:test,零外部测试依赖)
120
+ npm run pack:check # 预览 npm 发布产物
94
121
  ```
95
122
 
96
123
  ## 目录
@@ -98,15 +125,17 @@ npm run pack:check # 预览 npm 发布产物(files 白名单)
98
125
  ```
99
126
  agent_brain_sync/
100
127
  ├── bin/abs.js CLI 入口
101
- ├── bin/mcp.js MCP server (stdio, 官方 SDK, 窄工具面)
128
+ ├── bin/mcp.js MCP server (stdio)
102
129
  ├── src/index.js 图谱定位(向上找最近 .brain/)
103
- ├── src/lock.js .brain 并发写保护(原子锁 + 排队 + SKIP)
104
- ├── src/brainio.js 统一读写收口(readBrain/writeBrain/appendBrain)
130
+ ├── src/lock.js 并发写保护(原子锁 + 排队 + SKIP)
131
+ ├── src/brainio.js 统一读写收口
105
132
  ├── src/todo.js todo.md 分区读写
106
- ├── src/store.js CLI 命令实现 (init/load/todo/task/log/query/lint/status/…)
133
+ ├── src/store.js CLI 命令实现
107
134
  ├── src/hosts.js 四宿主接入定义
108
- ├── src/install.js 安装/卸载向导(分区共存合并)
109
- ├── hooks/event.sh hook 模板(纯触发 → 技术日志 + Stop wrapup 提醒)
110
- ├── skill/SKILL.md 技能(安装到各智能体)
111
- └── test/ 单测(node:test,零外部测试依赖)
135
+ ├── src/install.js 安装/卸载(分区共存合并)
136
+ ├── hooks/event.sh hook 模板
137
+ ├── skill/SKILL.md 技能(装到各智能体)
138
+ └── test/ 单测
112
139
  ```
140
+
141
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanchao8609/agent_brain_sync",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "agent-brain-sync: 跨会话 AI 编码记忆 — hook 纯触发 + CLI/MCP 读写 .brain markdown 图谱, 防并发写保护。",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/skill/SKILL.md CHANGED
@@ -103,6 +103,11 @@ monorepo 若多个子包各自独立交付,可各建一份 `.brain/`;横向
103
103
  **todo 不是收尾仪式,是干活中随改随写的活看板。** 每个任务边界立即更新,和 git commit
104
104
  同一个反射,别等收尾。用工具(MCP `abs_task` / CLI `abs task`):
105
105
 
106
+ > **写操作优先 MCP,不要用 `bash` 跑 `abs`。** 实测:MCP 单次 ~2ms(server 常驻),
107
+ > CLI 单次 27ms(其中 20ms 是每次起 node 进程的固定开销,读写本身仅 3ms)。
108
+ > 一轮发多条命令时差距明显。CLI 留给「人手动查看看板」,agent 读写走 MCP。
109
+ > 详见 [[perf-fixed-overhead]]。
110
+
106
111
  | 时机 | 动作 |
107
112
  |---|---|
108
113
  | 认领新任务 | `abs task start <id> --note "做什么"` |
package/src/store.js CHANGED
@@ -4,7 +4,7 @@ import { promises as fs } from 'node:fs';
4
4
  import { join, resolve, dirname } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
6
  import { requireBrain, findBrainRoot, brainPath } from './index.js';
7
- import { addTask, upsertTask, boardText, readTodo, ensureTodo, todoTemplate, today, localStamp, setBreakpoint, moveBlocked, insertDoneGrouped } from './todo.js';
7
+ import { addTask, upsertTask, boardText, readTodo, ensureTodo, todoTemplate, today, localStamp, setBreakpoint, moveBlocked, insertDoneGrouped, idOfTaskLine } from './todo.js';
8
8
  import { editFile, SKIP } from './lock.js';
9
9
  import { appendWrapup, strandedFor, wrapupLogPath } from './wrapup.js';
10
10
 
@@ -320,9 +320,10 @@ async function markDone(file, id) {
320
320
  let changed = false;
321
321
  let moved = null;
322
322
  const kept = [];
323
+ const wantId = String(id).replace(/\u200b/g, '');
323
324
  for (let i = 0; i < lines.length; i++) {
324
325
  const l = lines[i];
325
- if (!moved && l.startsWith('- [ ]') && l.includes(id)) {
326
+ if (!moved && idOfTaskLine(l) === wantId) {
326
327
  changed = true;
327
328
  const head = l.replace('- [ ]', '- [x]').replace(/\(认领[^)]*\)/, '') + ` (完成 ${today()})`;
328
329
  const bp = [];
package/src/todo.js CHANGED
@@ -215,8 +215,8 @@ export async function upsertTask(brainRoot, { section, text }) {
215
215
  const out = await editFile(p, (orig) => {
216
216
  const base = normalizeTodo(orig);
217
217
  const lines = base.split('\n');
218
- // 在所有分区中找含该 id 的未完成任务行(Done 的已完成行不重复动)
219
- const idx = lines.findIndex((l) => l.startsWith('- [ ]') && id && l.includes(id));
218
+ // 在所有分区中找该 id 对应的未完成任务行(Done 的已完成行不重复动);全等比对
219
+ const idx = findTaskLine(lines, id);
220
220
  if (idx !== -1) {
221
221
  // 原位更新:保留断点附属行,只换任务行本体
222
222
  const oldNote = extractNote(lines[idx].replace(/^- \[ \] /, ''));
@@ -244,10 +244,25 @@ export function extractNote(text) {
244
244
  return i === -1 ? '' : String(text).slice(i + 3).replace(/\s*\(认领[^)]*\)\s*$/, '');
245
245
  }
246
246
 
247
- /** 定位含 id 的未完成任务行下标;附属断点行(↳ 开头)不算任务行。 */
247
+ /**
248
+ * 从任务行取出行首 id token(`- [ ] <id> …` 里的 <id>)。
249
+ * 坑: 不能用 l.includes(id) 定位任务 —— 那是子串匹配,`T1` 会命中 `T11`(同理
250
+ * TASK-1/TASK-10、fix-hook/fix-hook-2),导致 upsert 误判"已存在"而原地改写别的任务、
251
+ * 新任务静默消失。必须取出 id token 做全等比对。
252
+ * id 里可能混入零宽字符(历史瑕疵),比对前先剥掉。
253
+ */
254
+ export function idOfTaskLine(line) {
255
+ if (!line || !line.startsWith('- [ ]')) return null;
256
+ const m = line.match(/^- \[ \] (\S+)/);
257
+ return m ? m[1].replace(/\u200b/g, '') : null;
258
+ }
259
+
260
+ /** 定位 id 对应的未完成任务行下标;附属断点行(↳ 开头)不算任务行。
261
+ * 全等比对,不用 includes(否则 T1 会误命中 T11)。 */
248
262
  export function findTaskLine(lines, id) {
249
263
  if (!id) return -1;
250
- return lines.findIndex((l) => l.startsWith('- [ ]') && l.includes(id));
264
+ const want = String(id).replace(/\u200b/g, '');
265
+ return lines.findIndex((l) => idOfTaskLine(l) === want);
251
266
  }
252
267
 
253
268
  /** 实时断点: 在 id 任务行下原位补/换 `↳ 断点:` 附属行(不挪任务位置)。 */