@fanchao8609/agent_brain_sync 1.8.8 → 1.9.0
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/abs.js +155 -53
- package/bin/mcp.js +5 -4
- package/hooks/abs.opencode.ts +14 -4
- package/hooks/event.sh +8 -0
- package/package.json +3 -2
- package/skill/abs-agent-brain-sync/SKILL.md +2 -2
- package/skill/abs-bug-hunter/SKILL.md +109 -158
- package/src/index.js +22 -3
- package/src/relevant.js +244 -0
- package/src/serve.js +506 -0
- package/src/store.js +149 -26
- package/src/todo.js +1 -1
- package/src/userconfig.js +9 -5
- package/skill/abs-think-tree/SKILL.md +0 -194
- package/skill/abs-think-tree/check.js +0 -119
- package/skill/abs-think-tree/check.test.js +0 -161
package/bin/abs.js
CHANGED
|
@@ -57,7 +57,7 @@ async function cmdUpdate({ yes }) {
|
|
|
57
57
|
console.log(r);
|
|
58
58
|
console.log('\n刷新四宿主 hook/skill(hook 脚本烧的是绝对路径 + 模板快照,升级后必须重装)…');
|
|
59
59
|
await runInstall({ agent: undefined, mcp: true, skill: true, yes: yes !== false });
|
|
60
|
-
console.log(`\n完成。重启宿主(pi /
|
|
60
|
+
console.log(`\n完成。重启宿主(pi / opencode / claude-code / codex)后新 hook 生效。`);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const [,, cmd, ...rest] = process.argv;
|
|
@@ -73,6 +73,9 @@ const FLAG_SPEC = {
|
|
|
73
73
|
'as': { type: 'string' },
|
|
74
74
|
'payload': { type: 'string' },
|
|
75
75
|
'tags': { type: 'string' },
|
|
76
|
+
'port': { type: 'string' },
|
|
77
|
+
// note 的触发条件(何时该读这条经验)—— 供 load 的相关页推荐匹配。
|
|
78
|
+
'when': { type: 'string' },
|
|
76
79
|
// concept 骨架用: 不在 FLAG_SPEC 里的 `--title` 会被静默当布尔 true(见 parseArgv 注释),
|
|
77
80
|
// 于是 `--title "一句话"` 的正文会进位置参数 → 必须在这声明。
|
|
78
81
|
'title': { type: 'string' },
|
|
@@ -88,10 +91,13 @@ const FLAG_SPEC = {
|
|
|
88
91
|
'repair': { type: 'boolean' },
|
|
89
92
|
'no-mcp': { type: 'boolean' },
|
|
90
93
|
'no-skill': { type: 'boolean' },
|
|
94
|
+
// serve 用: --no-open 不自动开浏览器(open 命令仅 macOS)。坑: 曾漏声明 → serve 判
|
|
95
|
+
// opts.open !== false 恒真, --no-open 接不上。
|
|
96
|
+
'no-open': { type: 'boolean' },
|
|
91
97
|
};
|
|
92
98
|
|
|
93
99
|
/** 已被"规范键"接管的 raw flag:不再原样漏出(见 parseArgv 返回处的白名单注释)。 */
|
|
94
|
-
const KNOWN_RAW = new Set(['keep-days', 'dry-run', 'no-mcp', 'no-skill']);
|
|
100
|
+
const KNOWN_RAW = new Set(['keep-days', 'dry-run', 'no-mcp', 'no-skill', 'no-open']);
|
|
95
101
|
|
|
96
102
|
function parseArgv(args) {
|
|
97
103
|
// 坑: parseArgs 会把**任何** `-` 开头的 token 当选项,连正文一起吃:
|
|
@@ -164,6 +170,7 @@ function parseArgv(args) {
|
|
|
164
170
|
dryRun: !!values['dry-run'],
|
|
165
171
|
mcp: !values['no-mcp'],
|
|
166
172
|
skill: !values['no-skill'],
|
|
173
|
+
open: !values['no-open'],
|
|
167
174
|
};
|
|
168
175
|
// 未声明的 `--unknown` 静默收下(旧行为:不进位置参数,也不报错)。
|
|
169
176
|
// 这条保留是因为 rejectExtra 只看位置参数 —— 把未知 flag 放进去会改成报错,
|
|
@@ -173,56 +180,56 @@ function parseArgv(args) {
|
|
|
173
180
|
}
|
|
174
181
|
return o;
|
|
175
182
|
}
|
|
176
|
-
const usage = `abs —
|
|
183
|
+
const usage = `abs — 跨会话记忆工具(.brain/ 图谱)
|
|
177
184
|
|
|
178
|
-
|
|
179
|
-
abs load
|
|
180
|
-
abs todo
|
|
181
|
-
abs
|
|
182
|
-
abs
|
|
183
|
-
abs status 当前项目 + 图谱概要
|
|
185
|
+
常用:
|
|
186
|
+
abs load 开工先跑: 读状态、接上次的活
|
|
187
|
+
abs todo add X --note "做什么" 登记任务 (done/state/note 管后续)
|
|
188
|
+
abs note "经验一句话" 随手记经验; abs log "完成 X" 记成果
|
|
189
|
+
abs query <词…> 检索 .brain/; abs todo 看任务板
|
|
184
190
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
191
|
+
全部命令 (细节: abs <命令> --help):
|
|
192
|
+
init [--repair] 建图谱 (当前目录)
|
|
193
|
+
load 开机读状态
|
|
194
|
+
todo 任务看板 (add/note/state/done/archive)
|
|
195
|
+
index | log | status 索引 / 流水 / 概要
|
|
196
|
+
note <经验> [--tags a,b] 经验暂存 → sources/
|
|
197
|
+
concept <slug> --title … 建概念页骨架
|
|
198
|
+
query <词…> [--all] 检索知识页 (多词 OR)
|
|
199
|
+
resolve <页名…> 反查页面路径
|
|
200
|
+
supersede <页名> [--by 页] 标经验已失效
|
|
201
|
+
lint 图谱体检 (死链/超限)
|
|
202
|
+
rule [add "一句话"] 硬规则读写
|
|
203
|
+
serve [--port N] [--no-open] 浏览 .brain/ (仅本机; 端口默认自动选)
|
|
204
|
+
install|uninstall [--agent <宿主>] [--no-mcp] [--no-skill]
|
|
205
|
+
update 升级并刷新 hook/skill
|
|
206
|
+
config [set user <名字>] 使用者姓名 (写操作需先设置)
|
|
207
|
+
--version | help 版本 / 本帮助
|
|
196
208
|
|
|
197
|
-
|
|
198
|
-
abs query <词1> [词2 …] [--all]
|
|
199
|
-
检索 .brain/ 知识页 (多词 OR);superseded 默认隐藏
|
|
200
|
-
abs resolve <id-or-slug> [更多…]
|
|
201
|
-
按 id/页面名反查路径 (页改名后 id 不变,仍能找回)
|
|
202
|
-
abs supersede <页名> [--by <取代它的页>]
|
|
203
|
-
标记一条经验已失效 (不删文件;query/load 默认不再展示)
|
|
204
|
-
abs todo archive [--keep-days N] [--dry-run]
|
|
205
|
-
归档 Done 区旧日期组 → sessions/<日期>-todo归档.md
|
|
206
|
-
(默认保留近 3 天; 任一天有未完成则整天不归档)
|
|
207
|
-
abs lint 图谱体检 (死链/孤岛/超尺寸/堆积)
|
|
208
|
-
abs rule 列出 index.md 的 ## Rules 硬规则
|
|
209
|
-
abs rule add "一句话" 追加一条硬规则 (只放违反会丢数据/静默失效级的;展开写概念页)
|
|
210
|
-
abs config [show] 查看使用者姓名 (标记作者用)
|
|
211
|
-
abs config set user <名字> 设置使用者姓名 → ~/.abs/config.json
|
|
212
|
-
未设置时写操作(todo/log/note)会报错要求先设置
|
|
213
|
-
临时覆盖: ABS_USER=<名字> abs ...
|
|
214
|
-
abs init [--repair] 建 .brain/ 图谱; 结构不完整时报明细, --repair 只补缺不覆盖
|
|
215
|
-
abs install [--agent <宿主>] 安装 MCP+hook+skill (宿主: claude-code/codex/opencode/pi)
|
|
216
|
-
abs uninstall [--agent <...>] 卸载
|
|
217
|
-
abs update 升级到最新版并刷新四宿主 hook/skill
|
|
218
|
-
abs --version 显示当前版本
|
|
219
|
-
abs help 本帮助
|
|
220
|
-
|
|
221
|
-
注: abs wrapup / abs teardown-check 是 hook 内部命令, 不需手动调用。
|
|
209
|
+
注: wrapup / teardown-check 是 hook 内部命令。
|
|
222
210
|
`;
|
|
223
211
|
|
|
224
212
|
/** 子命令级用法(abs <cmd> --help 时打印)。 */
|
|
225
213
|
const subUsage = {
|
|
214
|
+
load: [
|
|
215
|
+
'abs load — 开机读状态(Rules 全量 + 图谱计数 + todo 看板 + 最近 log)',
|
|
216
|
+
'',
|
|
217
|
+
'用法:',
|
|
218
|
+
' abs load [--dir <项目根>]',
|
|
219
|
+
'',
|
|
220
|
+
'说明:',
|
|
221
|
+
' • 顶部出现「⏳ 上会话滞留」时先收尾再开工',
|
|
222
|
+
' • 输出是折叠过的;全量明细用 abs todo --full / abs index',
|
|
223
|
+
].join('\n'),
|
|
224
|
+
init: [
|
|
225
|
+
'abs init — 在当前目录(或 --dir)建 .brain/ 图谱',
|
|
226
|
+
'',
|
|
227
|
+
'用法:',
|
|
228
|
+
' abs init [--repair] [--dir <项目根>]',
|
|
229
|
+
'',
|
|
230
|
+
'说明:',
|
|
231
|
+
' • 结构不完整时报明细;--repair 只补缺不覆盖,已有内容一律不动',
|
|
232
|
+
].join('\n'),
|
|
226
233
|
install: [
|
|
227
234
|
'abs install — 把 MCP + hook + skill 安装到 AI 编码宿主',
|
|
228
235
|
'',
|
|
@@ -260,6 +267,65 @@ const subUsage = {
|
|
|
260
267
|
' • 只删 abs 自己装的东西,保留你其它的 hook / MCP / 配置字段',
|
|
261
268
|
' • 配置文件无法解析时会跳过该文件(不覆盖)但仍清理 abs 的脚本与 skill',
|
|
262
269
|
].join('\n'),
|
|
270
|
+
serve: [
|
|
271
|
+
'abs serve — 把 .brain/ 挂成只读网页(左目录树 + 右正文 + [[双链]]跳转)',
|
|
272
|
+
'',
|
|
273
|
+
'用法:',
|
|
274
|
+
' abs serve [--port N] [--no-open] [--dir <项目根>]',
|
|
275
|
+
'',
|
|
276
|
+
'参数:',
|
|
277
|
+
' --port N 端口。默认 0 = 自动选空闲端口(多项目可同时开各的)',
|
|
278
|
+
' --no-open 不自动打开浏览器(open 仅 macOS)',
|
|
279
|
+
' --dir <路径> 服务别的项目的 .brain/',
|
|
280
|
+
'',
|
|
281
|
+
'说明:',
|
|
282
|
+
' • 只读、仅绑定 127.0.0.1,外部访问不了',
|
|
283
|
+
' • 主题/明暗切换在网页右上角;最后一页会记住',
|
|
284
|
+
].join('\n'),
|
|
285
|
+
todo: [
|
|
286
|
+
'abs todo — 任务看板(Todo / Done 两区;未完成都带行首状态标记)',
|
|
287
|
+
'',
|
|
288
|
+
'用法:',
|
|
289
|
+
' abs todo 看板',
|
|
290
|
+
' abs todo add <id> [--note "做什么"] [--section 讨论中|滞留中]',
|
|
291
|
+
' abs todo note <id> --note "断点/进度"',
|
|
292
|
+
' abs todo state <id> --note 进行中|讨论中|滞留中',
|
|
293
|
+
' abs todo done <id> [--as 落地|否决|仅方案] [结语文字]',
|
|
294
|
+
' abs todo archive [--keep-days N] [--dry-run]',
|
|
295
|
+
'',
|
|
296
|
+
'done 的结语:',
|
|
297
|
+
' 落地(默认) = 真做成且有验证; 否决 = 评估后不做(含做了又撤); 仅方案 = 只设计过',
|
|
298
|
+
' 结语失真会让下个会话把"想过"当成"做完了" —— 不确定就写清楚',
|
|
299
|
+
].join('\n'),
|
|
300
|
+
note: [
|
|
301
|
+
'abs note — 经验实时暂存 → sources/(幂等去重;先记后提炼)',
|
|
302
|
+
'',
|
|
303
|
+
'用法:',
|
|
304
|
+
' abs note "一句话经验" [--tags 坑,docker] [--when "什么时候该读这条"]',
|
|
305
|
+
'',
|
|
306
|
+
'说明:',
|
|
307
|
+
' • --tags 首个标签建议带类别(坑/技巧/决策…),检索按词 OR 命中',
|
|
308
|
+
' • --when 供 load 的相关页推荐匹配「何时该读」',
|
|
309
|
+
' • sources 是暂存区: 提炼成 concept 后应清理',
|
|
310
|
+
].join('\n'),
|
|
311
|
+
query: [
|
|
312
|
+
'abs query — 全文检索 .brain/ 知识页',
|
|
313
|
+
'',
|
|
314
|
+
'用法:',
|
|
315
|
+
' abs query <词1> [词2 …] [--all] 多词 OR;命中页按命中数排序',
|
|
316
|
+
'',
|
|
317
|
+
'参数:',
|
|
318
|
+
' --all 连 superseded(已失效)页一起返回',
|
|
319
|
+
].join('\n'),
|
|
320
|
+
config: [
|
|
321
|
+
'abs config — 使用者姓名(写操作的作者标记)',
|
|
322
|
+
'',
|
|
323
|
+
'用法:',
|
|
324
|
+
' abs config 查看当前姓名',
|
|
325
|
+
' abs config set user <名字> 设置(未设置时写操作报错要求先设置)',
|
|
326
|
+
'',
|
|
327
|
+
'临时用法: ABS_USER=<名字> abs …',
|
|
328
|
+
].join('\n'),
|
|
263
329
|
};
|
|
264
330
|
|
|
265
331
|
/**
|
|
@@ -326,7 +392,12 @@ async function main() {
|
|
|
326
392
|
try {
|
|
327
393
|
const opts = parseArgv(rest);
|
|
328
394
|
if (RENAMED[cmd]) throw new Error(RENAMED[cmd]());
|
|
329
|
-
|
|
395
|
+
// 子命令级 --help: 有用法页的命令在此一处拦截(此前只在 install/uninstall 分支里判,
|
|
396
|
+
// 其余命令的 --help 被忽略 —— serve --help 曾直接把服务起起来挂住终端)。
|
|
397
|
+
if (opts.help && subUsage[cmd] && !(cmd === 'todo' && opts._.length)) {
|
|
398
|
+
console.log(subUsage[cmd]);
|
|
399
|
+
} else {
|
|
400
|
+
switch (cmd) {
|
|
330
401
|
case 'init': {
|
|
331
402
|
rejectExtra(opts._, 'abs init [--repair]');
|
|
332
403
|
const msg = opts.repair
|
|
@@ -356,6 +427,32 @@ async function main() {
|
|
|
356
427
|
rejectExtra(opts._, 'abs status');
|
|
357
428
|
console.log(await cmdStatus({ dir: opts.dir }));
|
|
358
429
|
break;
|
|
430
|
+
// abs serve —— 把 .brain/ 挂成只读网页(浏览器无法自己列目录,所以必须有服务端)
|
|
431
|
+
case 'serve': {
|
|
432
|
+
rejectExtra(opts._, 'abs serve');
|
|
433
|
+
const { serve } = await import('../src/serve.js');
|
|
434
|
+
const { requireBrain, brainPath } = await import('../src/index.js');
|
|
435
|
+
// 坑(2026-09-16 实测): requireBrain 返回的是【项目根】, .brain/ 在它下面 ——
|
|
436
|
+
// 直接把项目根当服务根会扫到 node_modules。必须走 brainPath()。
|
|
437
|
+
const root = brainPath(await requireBrain(opts.dir || process.cwd()));
|
|
438
|
+
// 默认 0 = 让系统挑空闲端口:固定 7777 第二个项目就起不来了。
|
|
439
|
+
// --port abc 曾静默变 NaN → server listen 报怪错;在此拦下。
|
|
440
|
+
const port = opts.port ? Number(opts.port) : 0;
|
|
441
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
442
|
+
throw new Error(`✗ --port 需为 0-65535 的整数(收到 "${opts.port}")。0 = 自动选空闲端口`);
|
|
443
|
+
}
|
|
444
|
+
// --no-open 显式声明在 FLAG_SPEC(type:boolean),parseArgv 归一化为 open:false。
|
|
445
|
+
// 坑(2026-09-16 审计#6): 曾判 opts.open !== false 但 FLAG_SPEC 无 open →
|
|
446
|
+
// 条件恒真且 --no-open 接不上(open 命令也仅 macOS)。
|
|
447
|
+
const { url, server } = await serve({ root, port });
|
|
448
|
+
console.log(`📖 abs serve → ${url}`);
|
|
449
|
+
console.log(` 根: ${root}`);
|
|
450
|
+
console.log(' (只读,仅本机可访;Ctrl+C 停止)');
|
|
451
|
+
if (opts.open) runCmd('open', [url]);
|
|
452
|
+
// 不断开进程:服务要活着才有用
|
|
453
|
+
await new Promise(() => {});
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
359
456
|
// abs todo —— 无子命令=看板;带子命令=任务写操作
|
|
360
457
|
case 'todo': {
|
|
361
458
|
const [sub, id, ...rest2] = opts._;
|
|
@@ -386,7 +483,7 @@ async function main() {
|
|
|
386
483
|
break;
|
|
387
484
|
}
|
|
388
485
|
case 'note': {
|
|
389
|
-
console.log(await cmdNote({ dir: opts.dir, text: opts._.join(' '), tags: opts.tags }));
|
|
486
|
+
console.log(await cmdNote({ dir: opts.dir, text: opts._.join(' '), tags: opts.tags, when: opts.when }));
|
|
390
487
|
break;
|
|
391
488
|
}
|
|
392
489
|
case 'concept': {
|
|
@@ -402,11 +499,7 @@ async function main() {
|
|
|
402
499
|
}
|
|
403
500
|
case 'install':
|
|
404
501
|
case 'uninstall': {
|
|
405
|
-
//
|
|
406
|
-
// 坑(INSTALL-HELP-FOOTGUN): 以前 --help 只在顶层命令被识别,跟在 install 后面时
|
|
407
|
-
// 落到 parseArgv 的通用分支,而 install 分支根本不读它 → 用户想看帮助,
|
|
408
|
-
// 实际执行了全量安装(幂等不炸,但确实改了四宿主配置,写了 15 个文件)。
|
|
409
|
-
if (opts.help) { console.log(subUsage[cmd]); break; }
|
|
502
|
+
// --help 已在 switch 前统一拦截(subUsage)。
|
|
410
503
|
if (cmd === 'install') {
|
|
411
504
|
await runInstall({ agent: opts.agent, mcp: opts.mcp !== false, skill: opts.skill !== false, yes: opts.yes });
|
|
412
505
|
} else {
|
|
@@ -459,9 +552,18 @@ async function main() {
|
|
|
459
552
|
case '--version': case '-v': case 'version': console.log(pkgVersion()); break;
|
|
460
553
|
case 'help': case undefined: case '--help': console.log(usage); break;
|
|
461
554
|
default: throw new Error(`未知命令: ${cmd}\n\n${usage}`);
|
|
555
|
+
}
|
|
462
556
|
}
|
|
463
557
|
} catch (e) {
|
|
464
|
-
|
|
558
|
+
// 带码的错(AbsError):首行印 [CODE],fallback 另起一行。
|
|
559
|
+
// 为何:hook/脚本需要机器可读的分支依据(借 Anneal 的 templateRefusal 惯例)。
|
|
560
|
+
// 无码的错照旧只印 message —— 不能把内部堆栈当错误码泄给使用人。
|
|
561
|
+
if (e && e.code) {
|
|
562
|
+
console.error(`[${e.code}] ${e.message}`);
|
|
563
|
+
if (e.fallback) console.error(` → ${e.fallback}`);
|
|
564
|
+
} else {
|
|
565
|
+
console.error(String(e && e.message ? e.message : e));
|
|
566
|
+
}
|
|
465
567
|
process.exit(1);
|
|
466
568
|
}
|
|
467
569
|
}
|
package/bin/mcp.js
CHANGED
|
@@ -112,19 +112,20 @@ tool(
|
|
|
112
112
|
|
|
113
113
|
tool(
|
|
114
114
|
'abs_task',
|
|
115
|
-
'任务实时落盘(幂等键 = id)。start 登记进 Todo / note 补断点(改到哪文件哪行) / state 改状态 / done 完成归位 Done
|
|
115
|
+
'任务实时落盘(幂等键 = id)。start 登记进 Todo / note 补断点(改到哪文件哪行) / state 改状态 / done 完成归位 Done(done 时 note=结语文字,as=结语类型:落地|否决|仅方案)。',
|
|
116
116
|
{
|
|
117
117
|
action: z.enum(['add', 'start', 'done', 'note', 'state']),
|
|
118
118
|
id: z.string().describe('任务幂等键,如 TASK-xxx 或子任务名'),
|
|
119
119
|
cwd: z.string().describe('项目根目录(.brain/ 所在处)'),
|
|
120
|
-
note: z.string().optional().describe('add/start=做什么; note=断点(文件/到哪步); state
|
|
120
|
+
note: z.string().optional().describe('add/start=做什么; note=断点(文件/到哪步); state=进行中|讨论中|滞留中; done=结语文字'),
|
|
121
|
+
as: z.enum(['落地', '否决', '仅方案']).optional().describe('仅 done:结语类型。默认 落地。做了又撤/评估后不做用 否决,只设计过用 仅方案 —— 别让假【落地】污染看板'),
|
|
121
122
|
},
|
|
122
|
-
async ({ action, id, cwd, note }) => {
|
|
123
|
+
async ({ action, id, cwd, note, as }) => {
|
|
123
124
|
const root = await findBrainRoot(cwd || process.cwd());
|
|
124
125
|
if (!root) return errNoBrain(cwd || process.cwd());
|
|
125
126
|
// add 是 start 的别名(与 CLI `abs todo add` 对齐)
|
|
126
127
|
const act = action === 'add' ? 'start' : action;
|
|
127
|
-
return { content: [{ type: 'text', text: await cmdTask({ dir: root, action: act, id, note }) }] };
|
|
128
|
+
return { content: [{ type: 'text', text: await cmdTask({ dir: root, action: act, id, note, as }) }] };
|
|
128
129
|
}
|
|
129
130
|
);
|
|
130
131
|
|
package/hooks/abs.opencode.ts
CHANGED
|
@@ -39,16 +39,18 @@ const server = async ({ client, directory }) => {
|
|
|
39
39
|
try { const st = await stat(join(d, ".brain")); return st.isDirectory() ? join(d, ".brain") : null } catch { return null }
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
// 判定"今天是否收尾过":
|
|
43
|
-
//
|
|
44
|
-
//
|
|
42
|
+
// 判定"今天是否收尾过": 只认【工作成果】条目(kind=dev),行头 '## [YYYY-MM-DD HH:MM] [[name]] dev |'。
|
|
43
|
+
// 坑1: 曾用裸日期 substring, 正文/任务行里任何一处提到今天就把 nudge 永久压掉(守卫形同虚设)。
|
|
44
|
+
// 坑2(2026-09-13 实测, pi 侧先修): `abs note` 也写 log.md(kind=note),
|
|
45
|
+
// 旧判据只看「今天有没有行」会把「沉淀了一条经验」误判成「已收尾」→ 整天不再提醒。
|
|
46
|
+
// 三宿主(pi/CC/opencode)判据必须一致 —— 见 [[hook-throttle-alignment]]。
|
|
45
47
|
async function loggedToday(brain) {
|
|
46
48
|
try {
|
|
47
49
|
const txt = await readFile(join(brain, "log.md"), "utf8")
|
|
48
50
|
const d = new Date()
|
|
49
51
|
const pad = (n) => String(n).padStart(2, "0")
|
|
50
52
|
const today = d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate())
|
|
51
|
-
return new RegExp("^## \\[" + today + " \\d{2}:\\d{2}\\]", "m").test(txt)
|
|
53
|
+
return new RegExp("^## \\[" + today + " \\d{2}:\\d{2}\\] (?:\\[[^\\]]+\\]\\] )?dev \\|", "m").test(txt)
|
|
52
54
|
} catch { return false }
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -90,6 +92,14 @@ const server = async ({ client, directory }) => {
|
|
|
90
92
|
// session.created / idle 等事件带 sessionID —— 存下来供 tool.execute.after 用
|
|
91
93
|
const sid = event.properties && event.properties.sessionID
|
|
92
94
|
if (sid) currentSessionID = String(sid)
|
|
95
|
+
// 新会话 = 重置节流状态。坑(2026-09-13 pi 侧同构实测): 状态声明在 server 工厂闭包里,
|
|
96
|
+
// 同一进程的第二个会话会继承上个会话的 true → 永久不再提醒(后半场全部静默)。
|
|
97
|
+
// pi 侧在 session_start 里重置; opencode 的对应事件就是 session.created。
|
|
98
|
+
if (type === "session.created") {
|
|
99
|
+
wroteFiles = false
|
|
100
|
+
nudged = false
|
|
101
|
+
idleSeen = false
|
|
102
|
+
}
|
|
93
103
|
return
|
|
94
104
|
}
|
|
95
105
|
if (type !== "session.idle") return // idle = 每轮结束, 不记日志(太吵), 只做收尾判定
|
package/hooks/event.sh
CHANGED
|
@@ -38,6 +38,14 @@ fi
|
|
|
38
38
|
MARK="$MARK_DIR/abs-hook-${FINGER}-${STAMP}.mark"
|
|
39
39
|
[ -e "$MARK" ] && exit 0
|
|
40
40
|
: > "$MARK" 2>/dev/null
|
|
41
|
+
# mark 只增不减: STAMP 是分钟级 → 每分钟一批, 永不回收。实测堆了 361 个。
|
|
42
|
+
# 幂等窗口只 60s, 非本分钟的 mark 不可能再命中 → 清掉。
|
|
43
|
+
# 注意: 同一分钟内不同 payload 有不同 FINGER, 它们各自合法 —— 只按 STAMP 清, 不按 FINGER。
|
|
44
|
+
for old in "$MARK_DIR"/abs-hook-*.mark; do
|
|
45
|
+
[ -e "$old" ] || continue
|
|
46
|
+
case "$old" in *-"$STAMP".mark) continue ;; esac
|
|
47
|
+
rm -f "$old" 2>/dev/null
|
|
48
|
+
done
|
|
41
49
|
|
|
42
50
|
# 定位项目由 CLI 完成 (向上找 .brain/, 代码写死); 找不到图谱则静默放弃。
|
|
43
51
|
# 纪律: hook 事件只进技术日志 (~/.abs/log/), 不进图谱 log.md (那是活动流水, 不收琐碎请求)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanchao8609/agent_brain_sync",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "agent-brain-sync: 跨会话 AI 编码记忆 — hook 纯触发 + CLI/MCP 读写 .brain markdown 图谱, 防并发写保护。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"node": ">=18"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@modelcontextprotocol/sdk": "^1.30.0"
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
33
|
+
"zod": "^3.25 || ^4.0"
|
|
33
34
|
},
|
|
34
35
|
"scripts": {
|
|
35
36
|
"abs": "node bin/abs.js",
|
|
@@ -259,7 +259,7 @@ abs todo archive # 一条命令搞定,无需手工搬
|
|
|
259
259
|
| 只在讨论、还没动手 | `abs_task {action:state, id:"T-1", note:"讨论中"}` | 原地改标记 |
|
|
260
260
|
| 卡住了/等人等数据 | `abs_task {action:state, id:"T-1", note:"滞留中"}` | 原地改标记 |
|
|
261
261
|
| 被打断/干到一半 | `abs_task {action:note, id:"T-1", note:"改到哪个文件/到哪步"}` | 原地 ↳断点 |
|
|
262
|
-
| 子任务做完 | `abs_task {action:done, id:"T-1", note:"
|
|
262
|
+
| 子任务做完 | `abs_task {action:done, id:"T-1", note:"结语文字", as:"落地|否决|仅方案"}` | Done(as 默认 落地;做了又撤用否决,只设计过用仅方案,别让假【落地】污染看板) |
|
|
263
263
|
| 总结出经验/坑/规律 | `abs_note {text:"一句话", tags:"坑,docker"}` | sources/ |
|
|
264
264
|
|
|
265
265
|
> **为什么只有两区**(2026-09-13 实测):跨 4 个项目,Backlog/Today 常年 **0 条**,而 log.md
|
|
@@ -389,6 +389,6 @@ abs supersede <页名> --by <取代它的新页> # 不写 --by 也行 = 单纯
|
|
|
389
389
|
- 只读写 `.brain/` 与目标代码,不动全局配置(一次性接入除外)。
|
|
390
390
|
- 只写真实发生的事实;遵守容量纪律,宁缺毋滥。
|
|
391
391
|
- 双链/frontmatter/index 必须自洽 —— 坏链 = 掰断接力棒。
|
|
392
|
-
- **排查 bug 时挂载 `bug-hunter` skill**(随 abs
|
|
392
|
+
- **排查 bug 时挂载 `bug-hunter` skill**(随 abs 一并安装,安装名为 `abs-bug-hunter`,位于 ~/.pi/agent/skills/abs-bug-hunter/ 等宿主对应目录):
|
|
393
393
|
完整排查流程(列假设 → 复现 → 定位改动 → 打印 → 顺藤摸瓜 → 根因 → 验证)
|
|
394
394
|
与「给结论要明确」都在那份里。**排查方法不写在本文件**(两份维护必漂)。
|