@fanchao8609/agent_brain_sync 1.4.0 → 1.5.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanchao8609/agent_brain_sync",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
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
@@ -29,12 +29,33 @@ Obsidian 可直接打开的 Markdown 图谱(`.brain/`)做统一落点。
29
29
  | 总结经验 / 结束了 / "把这次记下来" | 收尾 Teardown(沉淀) |
30
30
  | 查询坑 / "我上次怎么解决 X" | `abs query` 检索 |
31
31
  | 体检图谱 / `abs lint` | `abs lint` 跑体检 |
32
- | 开新任务 / 继续开发 / "帮我做 X" | 开场 Init Sync(续接),之后正常做 |
32
+ | 开新任务 / 继续开发 / "帮我做 X" | 开场 Init Sync(续接);**新需求先走下面的受理协议** |
33
+ | 报 bug / 要求加功能 | **受理协议**:先方案 → 再登记 → 问开工(下一节) |
33
34
  | 意图不明 / 默认 | **续 todo**:读未完成项开工续做 |
34
35
 
35
36
  **主心骨**:意图不明且项目有 `.brain/` 时,默认**续 todo**——任何情况下先接上未完成工作,
36
37
  不是停在闲聊。
37
38
 
39
+ ## 新需求受理协议(bug / 新功能:先方案 → 再登记 → 问开工)
40
+
41
+ 用户报 bug 或要求加功能时,**先别动代码**。三步:
42
+
43
+ 1. **总结方案**(一屏内,给用户过目)
44
+ - 需求的准确复述;不确定就写明假设,别猜着做
45
+ - bug → 根因;功能 → 做法。**有证据给证据,没查到就直说"未定位"**
46
+ - 要改哪些文件、怎么验证(跑什么、看什么)
47
+ 2. **登记 todo**:`abs todo add <id> --note "<一句话需求+关键约束>"`
48
+ 方案里的关键结论(根因/取舍)再 `abs todo note <id> --note ...` 落到断点。
49
+ 3. **问是否开工**:明确问一句,**等确认再改代码**。
50
+
51
+ **例外(可直接开工,但回复里须说明援引哪一条)**
52
+ - 用户已说"开工 / 直接做 / 修它" —— 那就是授权
53
+ - 一行级修字、纯查询、纯收尾沉淀 —— 无方案可言
54
+ - 同一需求已登记且用户确认过 —— 接着做即可
55
+
56
+ > 为什么:方案先过目能省掉整轮返工;登记让跨会话可续;"问开工"把决定权留在用户手里。
57
+ > **这不是拖延** —— 总结方案本身就是工作,做完再问。
58
+
38
59
  ## 图谱定位(一个项目一个 `.brain/`,abs 自动定位不用手工指定路径)
39
60
 
40
61
  `.brain/` 放项目根,一个项目只建一份。所有 `abs` 命令(`abs load/todo/note/task/query/lint...`)
package/src/store.js CHANGED
@@ -3,7 +3,7 @@
3
3
  import { promises as fs } from 'node:fs';
4
4
  import { join, resolve, dirname } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
- import { requireBrain, findBrainRoot, brainPath } from './index.js';
6
+ import { requireBrain, findBrainRoot, brainPath, BRAIN_DIR } from './index.js';
7
7
  import { addTask, upsertTask, boardText, readTodo, ensureTodo, todoTemplate, today, localStamp, setBreakpoint, moveBlocked, insertDoneGrouped, idOfTaskLine, archiveDoneInText, renderArchivePage, renderArchiveBody } from './todo.js';
8
8
  import { editFile, SKIP } from './lock.js';
9
9
  import { appendWrapup, strandedFor, wrapupLogPath } from './wrapup.js';
@@ -562,6 +562,12 @@ export async function cmdLint({ dir }) {
562
562
  const vault = brainPath(root);
563
563
  const pages = await listPages(vault);
564
564
  const names = new Set(pages.map((p) => p.slug));
565
+ // `.brain` 顶层文件(index / log / todo)也是真实页:从图谱看 [[todo]] 就是 todo.md。
566
+ // 坑: 以前只把子目录当页 → [[todo]] 被当成死链(误报),反而逼用户去删掉正确引用。
567
+ // 只用于「链接目标是否存在」判定;不参与 ORPHAN / INDEX-MISSING(它们只针对子目录页)。
568
+ for (const f of await fs.readdir(vault).catch(() => [])) {
569
+ if (f.endsWith('.md')) names.add(f.replace(/\.md$/, ''));
570
+ }
565
571
  const linkedNames = new Set(pages.flatMap((p) => p.links));
566
572
  // index.md 里列的 [[x]] —— 用于反向查死引用(列了但页不存在)
567
573
  let indexLinks = [];
@@ -587,8 +593,8 @@ export async function cmdLint({ dir }) {
587
593
  issues.push(`UNRESOLVED-CONFLICT: ${pg.rel}`);
588
594
  }
589
595
  if (['concepts', 'entities', 'syntheses'].includes(pg.dir)) {
590
- if (pg.lines > 150 || pg.bytes > 5 * 1024) {
591
- issues.push(`OVER-SIZE: ${pg.rel} (${pg.lines}L/${pg.bytes}B > 150L/5120B; 拆或外链)`);
596
+ if (pg.lines > PAGE_MAX_LINES || pg.bytes > PAGE_MAX_BYTES) {
597
+ issues.push(`OVER-SIZE: ${pg.rel} (${pg.lines}L/${pg.bytes}B > ${PAGE_MAX_LINES}L/${PAGE_MAX_BYTES / 1024}KB; 拆或外链)`);
592
598
  }
593
599
  }
594
600
  if (pg.dir !== 'sources' && !pg.indexed) {
@@ -633,6 +639,14 @@ export async function cmdLint({ dir }) {
633
639
 
634
640
  const PAGE_DIRS = ['entities', 'concepts', 'sources', 'syntheses', 'sessions'];
635
641
 
642
+ // concept/entity/synthesis 页的容量上限,超出提示"拆或外链"。
643
+ // 曾为 150L/5120B —— 实测偏紧:跨 4 项目 68 页里仅 2 页超限,且都只超一点
644
+ // (5463B / 5440B);为满足它还把一页从 5319B 压到 4972B(内容受损、收益为零)。
645
+ // 放宽到 8KB:当前最大页 5463B,留约 50% 余量,但不至于失去"该拆了"的信号。
646
+ // 提成常量避免检查条件与提示文本各写一份而漂移。
647
+ const PAGE_MAX_LINES = 150;
648
+ const PAGE_MAX_BYTES = 8 * 1024;
649
+
636
650
  async function listPages(vault) {
637
651
  let indexText = '';
638
652
  try {
@@ -655,7 +669,9 @@ async function listPages(vault) {
655
669
  const links = [...new Set([...body.matchAll(/\[\[([^\]]+)\]\]/g)].map((m) => m[1].split('|')[0]))];
656
670
  pages.push({
657
671
  dir: d,
658
- rel: `${d}/${f}`,
672
+ // 带上 .brain/ 前缀:这串会原样出现在 lint 提示里,用户会拿它去找文件。
673
+ // 坑: 曾经只给 vault 相对路径(concepts/x.md),用户到项目根找 concepts/ 找不到。
674
+ rel: `${BRAIN_DIR}/${d}/${f}`,
659
675
  slug: f.replace(/\.md$/, ''),
660
676
  body,
661
677
  frontmatter: fm ? fm[1] : '',