@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 +96 -67
- package/package.json +1 -1
- package/skill/SKILL.md +5 -0
- package/src/store.js +3 -2
- package/src/todo.js +19 -4
package/README.md
CHANGED
|
@@ -1,96 +1,123 @@
|
|
|
1
|
-
# abs — agent-brain-sync
|
|
1
|
+
# abs — agent-brain-sync
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
让每个会话不再无状态——经验/进度/踩坑有统一落点,重开不"失忆"。
|
|
5
|
-
参考 [ai-memory](https://github.com/akitaonrails/ai-memory) 的接入机制,砍掉全部重引擎。
|
|
3
|
+
**跨会话 AI 编码记忆。** 让 AI 会话不再「重开就失忆」:任务进度、经验、踩坑都落盘到项目里的 markdown 图谱,下一个会话开机能直接读回来。
|
|
6
4
|
|
|
7
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
+
从源码跑(开发用):
|
|
28
42
|
|
|
29
43
|
```bash
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
38
|
-
|
|
55
|
+
cd 你的项目
|
|
56
|
+
abs init # 建 .brain/ 图谱,只需一次
|
|
39
57
|
```
|
|
40
58
|
|
|
41
|
-
###
|
|
59
|
+
### 日常命令
|
|
42
60
|
|
|
43
61
|
```bash
|
|
44
|
-
abs
|
|
45
|
-
abs
|
|
46
|
-
|
|
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
|
-
|
|
49
|
-
|
|
85
|
+
## 更新
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
abs update
|
|
50
89
|
```
|
|
51
90
|
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
|
66
|
-
abs
|
|
67
|
-
|
|
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
|
-
|
|
80
|
-
深提炼(sources→concepts)归收尾自觉层,工具不替你判断什么值得沉淀。** 详见 `skill/SKILL.md`。
|
|
110
|
+
只删 abs 自己装的条目,**保留其它共存 hook**。
|
|
81
111
|
|
|
82
|
-
|
|
112
|
+
**注意**:卸载**不会**删项目里的 `.brain/`——那是你的知识资产,要删自己 `rm -rf .brain`。
|
|
83
113
|
|
|
84
|
-
|
|
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
|
|
93
|
-
npm run pack:check
|
|
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
|
|
128
|
+
├── bin/mcp.js MCP server (stdio)
|
|
102
129
|
├── src/index.js 图谱定位(向上找最近 .brain/)
|
|
103
|
-
├── src/lock.js
|
|
104
|
-
├── src/brainio.js
|
|
130
|
+
├── src/lock.js 并发写保护(原子锁 + 排队 + SKIP)
|
|
131
|
+
├── src/brainio.js 统一读写收口
|
|
105
132
|
├── src/todo.js todo.md 分区读写
|
|
106
|
-
├── src/store.js CLI 命令实现
|
|
133
|
+
├── src/store.js CLI 命令实现
|
|
107
134
|
├── src/hosts.js 四宿主接入定义
|
|
108
|
-
├── src/install.js
|
|
109
|
-
├── hooks/event.sh hook
|
|
110
|
-
├── skill/SKILL.md
|
|
111
|
-
└── 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
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
|
|
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
|
-
//
|
|
219
|
-
const idx = lines
|
|
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
|
-
/**
|
|
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
|
-
|
|
264
|
+
const want = String(id).replace(/\u200b/g, '');
|
|
265
|
+
return lines.findIndex((l) => idOfTaskLine(l) === want);
|
|
251
266
|
}
|
|
252
267
|
|
|
253
268
|
/** 实时断点: 在 id 任务行下原位补/换 `↳ 断点:` 附属行(不挪任务位置)。 */
|