@fanchao8609/agent_brain_sync 1.19.4 → 1.19.5
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 +1 -1
- package/src/lock.js +5 -10
- package/src/store.js +17 -18
- package/src/todo/spec.js +9 -5
package/package.json
CHANGED
package/src/lock.js
CHANGED
|
@@ -15,22 +15,17 @@ export class LockTimeout extends Error {}
|
|
|
15
15
|
let formatGate = null;
|
|
16
16
|
export function setFormatGate(fn) { formatGate = fn; }
|
|
17
17
|
|
|
18
|
-
/** 三个受管文件名 —— 它们存在就是为了当“数据库表”,格式必须恒定。 */
|
|
19
|
-
const MANAGED = new Set(['index.md', 'log.md', 'todo.md']);
|
|
20
|
-
|
|
21
18
|
/** 懒加载闸门:**漏了 setFormatGate 就等于没闸门**,而注册是 import 副作用,
|
|
22
19
|
* 谁少 import 一次 store.js 就静默裂开(2026-10-18 实测:只 import lock.js 时
|
|
23
20
|
* 裸散文直接写进了 index.md)。故这里不信任外部注册,缺失时自己补上。
|
|
24
21
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
22
|
+
* 返回 null 表示「该文件不归闸门管」(非受管文件,或闸门自己说不管)—— 原样写盘。
|
|
23
|
+
* 依赖:`store.js` 顶层无条件调 `setFormatGate`(见那里)。若那行被删,受管文件
|
|
24
|
+
* 会退回静默直写 —— 这是已知的单点,防它靠 review 不靠代码(加守卫就是给不可能发生的
|
|
25
|
+
* 情况多写 6 行,2026-10-18 审查定)。 */
|
|
27
26
|
async function gateFor(file) {
|
|
28
27
|
if (!formatGate) await import('./store.js'); // 它的模块顶层会调 setFormatGate
|
|
29
|
-
|
|
30
|
-
if (MANAGED.has(basename(file))) {
|
|
31
|
-
throw new Error(`✗ ${basename(file)} 是受管文件,但格式闸门未注册 —— 拒绝写入(防止格崩坏)`);
|
|
32
|
-
}
|
|
33
|
-
return null;
|
|
28
|
+
return formatGate;
|
|
34
29
|
}
|
|
35
30
|
const LOCK_WAIT_BASE_MS = 15; // 指数退避起始重试间隔
|
|
36
31
|
const LOCK_WAIT_MAX_MS = 150; // 指数退避上限
|
package/src/store.js
CHANGED
|
@@ -259,7 +259,14 @@ export async function checkBrainShape(root) {
|
|
|
259
259
|
if (next2 === cur) return SKIP;
|
|
260
260
|
changed = r.fixed.length ? r.fixed : ['结构按标准重排'];
|
|
261
261
|
return { text: next2 };
|
|
262
|
-
}).catch(() => {
|
|
262
|
+
}).catch((e) => {
|
|
263
|
+
// 不能吞:load 是开机第一入口,静默跳过会让人以为库是健康的。
|
|
264
|
+
// 典型触发:文件里有孤儿子任务(真错,不修而报)。
|
|
265
|
+
// 只报**具体问题行**(以 `·` 开头的那些),不把整个错误头拼进去。
|
|
266
|
+
const msg = e && e.message ? e.message : '未知错误';
|
|
267
|
+
const detail = msg.split('\n').filter((x) => x.trim().startsWith('·')).map((x) => x.trim().slice(1).trim());
|
|
268
|
+
warn.push(`${file}: 未能自动归一 —— ${detail.length ? detail.join('; ') : msg.split('\n')[0]}`.slice(0, 200));
|
|
269
|
+
});
|
|
263
270
|
if (changed.length) fixed.push(`${file}: ${changed.join('; ')}`);
|
|
264
271
|
if (!knownH1) warn.push(`${file}: 标题非标准(读到 "${clip(l1, 24) || '(空)'}")`);
|
|
265
272
|
}
|
|
@@ -315,7 +322,10 @@ export async function cmdLoad({ dir }) {
|
|
|
315
322
|
...shape.fixed.map((a) => ` ✓ 已补: ${a}`),
|
|
316
323
|
...shape.warn.map((a) => ` ⚠ ${a}`),
|
|
317
324
|
];
|
|
318
|
-
|
|
325
|
+
// 那句提示只适用于「标题非标准」:其他 warn(如格式不合规)说这句会误导。
|
|
326
|
+
if (shape.warn.some((a) => a.includes('标题非标准'))) {
|
|
327
|
+
rows.push(' (无头文件不自动改:结构可能整体脱轨,请手工对齐 .brain/ 模板)');
|
|
328
|
+
}
|
|
319
329
|
sections.push('--- 文件形状核对 ---', ...rows, '');
|
|
320
330
|
}
|
|
321
331
|
// 未设姓名/姓名是占位名时开场就提醒 —— load 是开机第一屏,不在这里提,
|
|
@@ -986,7 +996,6 @@ async function markDone(file, id, kind = '落地', conclusion, bySid = '') {
|
|
|
986
996
|
let changed = false;
|
|
987
997
|
let moved = null;
|
|
988
998
|
const kept = [];
|
|
989
|
-
const leftBehind = [];
|
|
990
999
|
const wantId = String(id).replace(/\u200b/g, '');
|
|
991
1000
|
for (let i = 0; i < lines.length; i++) {
|
|
992
1001
|
const l = lines[i];
|
|
@@ -1012,15 +1021,9 @@ async function markDone(file, id, kind = '落地', conclusion, bySid = '') {
|
|
|
1012
1021
|
const head = withDoneKind(`${base}${tail} ${parts.join(' ')}`, kind);
|
|
1013
1022
|
const bp = [];
|
|
1014
1023
|
const leftover = [];
|
|
1015
|
-
//
|
|
1016
|
-
//
|
|
1017
|
-
//
|
|
1018
|
-
// 消失」。实测 factory-docs-slim 就带着「待做六链」的 `- [ ]` 落地,那条待办
|
|
1019
|
-
// 此后无处可寻,同时父任务盖着【落地】——看板与事实矛盾。
|
|
1020
|
-
//
|
|
1021
|
-
// 为何不是硬拦(A 方案被否):`abs todo note` 写的子项与断点记录**同形**(都是
|
|
1022
|
-
// ` - [ ] …`),无法从字段上区分「待办」与「过程记录」;硬拦会把所有带断点的
|
|
1023
|
-
// 任务一起拦住(实测弄挂两条既有测试)。改为「不搬 + 点名」,信息不丢且不误伤。
|
|
1024
|
+
// 子项搬运:**已勾选**的随任务进 Done;未勾选的不搬(下方直接拒)。
|
|
1025
|
+
// 已勾的必须搬:否则任务进 Done 了、子项剩在原区,就是无主孤儿行,
|
|
1026
|
+
// 重建分区时会被清掉(历史上就是这么丢过数据)。
|
|
1024
1027
|
while (i + 1 < lines.length) {
|
|
1025
1028
|
const nx = lines[i + 1];
|
|
1026
1029
|
if (!nx.trim()) break;
|
|
@@ -1068,15 +1071,11 @@ async function markDone(file, id, kind = '落地', conclusion, bySid = '') {
|
|
|
1068
1071
|
}
|
|
1069
1072
|
if (!changed || !moved) return SKIP;
|
|
1070
1073
|
// 归位 + 按日期分组:insertDoneGrouped 一次重建 Done 区(新日期在前,未标日期归尾)
|
|
1071
|
-
return { text: insertDoneGrouped(kept.join('\n'), moved)
|
|
1074
|
+
return { text: insertDoneGrouped(kept.join('\n'), moved) };
|
|
1072
1075
|
});
|
|
1073
1076
|
return res === SKIP
|
|
1074
1077
|
? `[NO_MATCH] 未找到含 "${id}" 的未完成任务行`
|
|
1075
|
-
: `✓ 已完成并归位 Done: ${id} 【${kind}
|
|
1076
|
-
(res.leftBehind?.length
|
|
1077
|
-
? `\n ⚠ ${res.leftBehind.length} 条未勾子项留在 Todo 区(未跟入 Done,仍可在看板看到):\n` +
|
|
1078
|
-
res.leftBehind.map((p) => ` - [ ] ${p}`).join('\n')
|
|
1079
|
-
: '');
|
|
1078
|
+
: `✓ 已完成并归位 Done: ${id} 【${kind}】`;
|
|
1080
1079
|
}
|
|
1081
1080
|
|
|
1082
1081
|
// ---------- show: 查看 index/todo/log(只读面) ----------
|
package/src/todo/spec.js
CHANGED
|
@@ -84,12 +84,16 @@ export const ENTRY_SHAPES = {
|
|
|
84
84
|
// 摘要里不许带 Markdown 结构符(实测污染就是摘要取了页面正文开头)
|
|
85
85
|
if (/^\s*-\s*\[\[[^\]]+\]\]\s*[—-]\s*#+\s/.test(l)) return '摘要不能带 `## ` 标题前缀(取页面首行标题了)';
|
|
86
86
|
// ★ 2026-10-18 补:摘要正文本身必须是**纯文本一句话**。
|
|
87
|
-
// 原规则只拦了 `## ` 前缀,实测下列全放行:摘要写成 `-
|
|
88
|
-
//
|
|
89
|
-
// index
|
|
87
|
+
// 原规则只拦了 `## ` 前缀,实测下列全放行:摘要写成 `- 列表项`(看着就像
|
|
88
|
+
// 另一条条目)、```js(开代码围栏把后面全吞)、`| a | b |`(表格)。
|
|
89
|
+
// index 是表,摘要是一列纯文本。
|
|
90
|
+
//
|
|
91
|
+
// 但别拦过头:摘要位置在 `- [[页名]] — ` 之后,是**一行中间**,
|
|
92
|
+
// `+ ` / `* ` / `> ` / `1. ` 在这里不会真渲染成块级结构,都是人话开头
|
|
93
|
+
// (实测放过一次「+ 一点补充」被误拒)。只拦真正会破坏解析的几种。
|
|
90
94
|
const desc = l.replace(/^\s*-\s*\[\[[^\]]+\]\]\s*[—-]\s*/, '');
|
|
91
|
-
if (/^(
|
|
92
|
-
return '
|
|
95
|
+
if (/^(?:-\s|```|~~~|\|)/.test(desc) || /^#{1,6}\s/.test(desc)) {
|
|
96
|
+
return '摘要必须以文字开头(不能是列表项/代码围栏/表格/标题)';
|
|
93
97
|
}
|
|
94
98
|
if (desc.includes('```')) return '摘要不能含代码围栏 ```(会吞掉后续所有条目)';
|
|
95
99
|
return null;
|