@fanchao8609/agent_brain_sync 1.19.2 → 1.19.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanchao8609/agent_brain_sync",
3
- "version": "1.19.2",
3
+ "version": "1.19.3",
4
4
  "description": "agent-brain-sync: 跨会话 AI 编码记忆 — hook 纯触发 + CLI/MCP 读写 .brain markdown 图谱, 防并发写保护。",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -77,6 +77,36 @@ export function checkFileShape(file, text, { lintMode = false } = {}) {
77
77
  }
78
78
  }
79
79
  }
80
+ // 孤儿缩进行:缩进的任务行(子任务)上方必须紧跟着一条任务行。
81
+ //
82
+ // 为何要这个(2026-10-18 实测):我手动清理 Done 区时只删了父任务行、
83
+ // 留下了它的 ` - [x] 测试子项`,于是看板上出现一条“没有主项的子项”
84
+ // —— 而当时 checkFileShape 报 0 违规,lint 也说健康,没人拦得住。
85
+ // 现实中它会来自:手改文件、父任务行被删、分区重建时父任务被搬走。
86
+ // 子任务离开父任务就没有意义(它靠缩进表达归属),所以这是硬错。
87
+ if (file === 'todo.md') {
88
+ for (let i = 0; i < lines.length; i++) {
89
+ if (!/^\s+- \[[ x]\]/.test(lines[i])) continue;
90
+ // 往上找最近的非空行
91
+ let p = i - 1;
92
+ while (p >= 0 && !lines[p].trim()) p--;
93
+ const prev = p >= 0 ? lines[p] : null;
94
+ // 父行可以是:顶层任务 `- [x]`、子任务 ` - [x]`、或旧格式一等任务 ` ↳ [ ] …`
95
+ // (后者缩进是历史形态,它本身是任务不是子项 —— 实测漏认会把合法数据误判成孤儿)。
96
+ const ok = prev !== null && (
97
+ /^- \[[ x]\]/.test(prev) ||
98
+ /^\s+- \[[ x]\]/.test(prev) ||
99
+ /^\s*↳\s*\[[ x]\]/.test(prev)
100
+ );
101
+ if (!ok) {
102
+ issues.push({
103
+ code: 'TODO-ORPHAN-CHILD',
104
+ line: i + 1,
105
+ msg: `todo.md:${i + 1} 孤儿子任务(「${lines[i].trim().slice(0, 40)}」上方没有主任务)—— 子任务靠缩进表达归属,删父任务时要连子项一起删`,
106
+ });
107
+ }
108
+ }
109
+ }
80
110
  // 条目之间的空行(与写入闸门 assertNoStrayBlank 同判据)。
81
111
  // lint 只能「报」,不能抛 —— 体检不能因格式坏而挂。
82
112
  const isEntryLine = (l) => /^-\s/.test(l) || /^#{1,3}\s+\[/.test(l);