@fanchao8609/agent_brain_sync 1.5.3 → 1.5.4
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/store.js +44 -7
package/package.json
CHANGED
package/src/store.js
CHANGED
|
@@ -347,12 +347,46 @@ function recentLogLines(text, n) {
|
|
|
347
347
|
.join('\n');
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
+
/** 摘要收口:超过 n 码点在**语义边界**收尾(标点 → 空格 → 硬切),加省略号。
|
|
351
|
+
* 坑: 曾直接 `.slice(0, n)` 硬切 → log.md 34/85 条断在词中间(revert-c / file-write-lockin /
|
|
352
|
+
* ~/.claude/ski), 文件名也被切成 ...-decision-blo。而 abs load 开机读的就是这份残句,
|
|
353
|
+
* 用户与后续会话看到的天然是半句 —— "摘要读起来抽象"的真因在写入口, 不在表述能力。 */
|
|
354
|
+
export function clip(text, n) {
|
|
355
|
+
const s = String(text || '').trim();
|
|
356
|
+
if (s.length <= n) return s;
|
|
357
|
+
const head = s.slice(0, n);
|
|
358
|
+
// 优先在标点处断开(中文句读 + 英文句读),其次空格,最后才硬切
|
|
359
|
+
const cut = Math.max(
|
|
360
|
+
head.lastIndexOf('。'), head.lastIndexOf(';'), head.lastIndexOf('!'), head.lastIndexOf('?'),
|
|
361
|
+
head.lastIndexOf(','), head.lastIndexOf('、'), head.lastIndexOf(';'), head.lastIndexOf(','),
|
|
362
|
+
head.lastIndexOf('.'), head.lastIndexOf(' '),
|
|
363
|
+
);
|
|
364
|
+
// 边界太靠前(< 一半)说明这一段本就是长句,宁可硬切也不留个残破的短头
|
|
365
|
+
const keep = cut > n / 2 ? cut : n;
|
|
366
|
+
return `${s.slice(0, keep).replace(/[\s,,、;;.。]+$/, '')}…`;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** slug:取前 n 码点 → 非词字符折叠为 '-'。只用于**文件名**,完整标题另存 TITLE 行。
|
|
370
|
+
* 在标点/空格边界收口,不在字中间切断(否则出 `...-硬切-不` 这种残尾)。 */
|
|
371
|
+
function slugOf(text, n = 24) {
|
|
372
|
+
const s = String(text || '').trim();
|
|
373
|
+
let head = s.slice(0, n);
|
|
374
|
+
if (s.length > n) {
|
|
375
|
+
const cut = Math.max(head.lastIndexOf(','), head.lastIndexOf('。'), head.lastIndexOf('、'),
|
|
376
|
+
head.lastIndexOf(':'), head.lastIndexOf(','), head.lastIndexOf('.'), head.lastIndexOf(' '));
|
|
377
|
+
if (cut > n / 2) head = head.slice(0, cut);
|
|
378
|
+
}
|
|
379
|
+
return head.replace(/[^\w一-鿿]+/g, '-').replace(/^-+|-+$/g, '').toLowerCase();
|
|
380
|
+
}
|
|
381
|
+
|
|
350
382
|
// ---------- log: 追加工作成果沉淀摘要(用户/AI 主动 abs log "..." 记, 不收工具动作流水) ----------
|
|
351
383
|
export async function cmdLog({ dir, title, kind = 'dev' }) {
|
|
352
384
|
const root = await requireBrain(dir || process.cwd());
|
|
353
385
|
const p = brainPath(root, 'log.md');
|
|
354
386
|
const stamp = localStamp();
|
|
355
|
-
|
|
387
|
+
// 不硬切: log.md 是人类读的成果摘要, 也是 abs load 的开机入口。600 码点够一条完整小结,
|
|
388
|
+
// 超出才在语义边界收口(曾 slice(0,100) → 34/85 条断在词中间)
|
|
389
|
+
const clean = clip(String(title || '').replace(/\n/g, ' '), 600);
|
|
356
390
|
const line = `## [${stamp}] ${kind} | ${clean}`;
|
|
357
391
|
await editFile(p, (cur) => {
|
|
358
392
|
const text = cur ?? '# 🗒 操作日志\n';
|
|
@@ -487,7 +521,7 @@ function firstHitLine(body, words) {
|
|
|
487
521
|
for (const line of body.split('\n')) {
|
|
488
522
|
const l = line.toLowerCase();
|
|
489
523
|
if (words.some((w) => l.includes(w.toLowerCase())) && line.trim() && !KNOWN_SLUG_HINT.test(line)) {
|
|
490
|
-
return line.trim()
|
|
524
|
+
return clip(line.trim(), 160);
|
|
491
525
|
}
|
|
492
526
|
}
|
|
493
527
|
return '';
|
|
@@ -517,8 +551,9 @@ export async function cmdNote({ dir, text, tags }) {
|
|
|
517
551
|
}
|
|
518
552
|
const tagList = String(tags || '').split(',').map((t) => t.trim()).filter(Boolean);
|
|
519
553
|
const fmTags = ['source', ...tagList].join(', ');
|
|
520
|
-
const slugSrc = clean
|
|
554
|
+
const slugSrc = slugOf(clean);
|
|
521
555
|
const file = `${today()}-${slugSrc || 'note'}.md`;
|
|
556
|
+
const heading = clip(clean, 80); // 页面标题: 完整优先, 超长才收口
|
|
522
557
|
const body = [
|
|
523
558
|
'---',
|
|
524
559
|
`tags: [${fmTags}]`,
|
|
@@ -526,7 +561,9 @@ export async function cmdNote({ dir, text, tags }) {
|
|
|
526
561
|
'status: draft',
|
|
527
562
|
'---',
|
|
528
563
|
'',
|
|
529
|
-
`# 来源:${
|
|
564
|
+
`# 来源:${heading}`,
|
|
565
|
+
'',
|
|
566
|
+
`TITLE: ${clean}`,
|
|
530
567
|
'',
|
|
531
568
|
`## 记录(实时暂存,Teardown 时提炼进 concepts/ 后本页可删)`,
|
|
532
569
|
`- ${clean}`,
|
|
@@ -543,7 +580,7 @@ export async function cmdNote({ dir, text, tags }) {
|
|
|
543
580
|
// index Sources 区登记(锁内幂等:别页已登记则跳过,防并发重复) + log 一行
|
|
544
581
|
const iP = brainPath(root, 'index.md');
|
|
545
582
|
const slug = file.replace(/\.md$/, '');
|
|
546
|
-
const line = `- [[${slug}]] — ${
|
|
583
|
+
const line = `- [[${slug}]] — ${heading}`;
|
|
547
584
|
await editFile(iP, (index) => {
|
|
548
585
|
if (!index || index.includes(`[[${slug}]]`)) return SKIP;
|
|
549
586
|
const sIdx = index.indexOf('## Sources');
|
|
@@ -554,8 +591,8 @@ export async function cmdNote({ dir, text, tags }) {
|
|
|
554
591
|
: index.slice(0, after) + `\n${line}` + index.slice(after);
|
|
555
592
|
return { text: next };
|
|
556
593
|
});
|
|
557
|
-
await cmdLog({ dir: root, title: clean
|
|
558
|
-
return `✓ 经验暂存 → sources/${file}\n ${clean
|
|
594
|
+
await cmdLog({ dir: root, title: clean, kind: 'note' });
|
|
595
|
+
return `✓ 经验暂存 → sources/${file}\n ${clean}`;
|
|
559
596
|
}
|
|
560
597
|
// ---------- lint: 体检(与 scripts/lint.sh 同规则的 Node 版,供 CLI/MCP 直调) ----------
|
|
561
598
|
export async function cmdLint({ dir }) {
|