@fanchao8609/agent_brain_sync 1.15.3 → 1.16.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/src/text.js ADDED
@@ -0,0 +1,37 @@
1
+ // src/text.js — 无依赖的纯文本工具。从 store.js 拆出(2026-10-05)。
2
+ //
3
+ // 为何单独一个文件:clip / slugOf 被 log、note、concept 三处共用,而它们本身
4
+ // 零依赖。放在任何业务文件里都会逼出循环 import(note.js ⇄ store.js)。
5
+ // 纯工具独立成格,是谁都能往下依赖的底。
6
+
7
+ /** 摘要收口:超过 n 码点在**语义边界**收尾(标点 → 空格 → 硬切),加省略号。
8
+ * 坑: 曾直接 `.slice(0, n)` 硬切 → log.md 34/85 条断在词中间(revert-c / file-write-lockin /
9
+ * ~/.cl​aude/ski), 文件名也被切成 ...-decision-blo。而 abs load 开机读的就是这份残句,
10
+ * 用户与后续会话看到的天然是半句 —— "摘要读起来抽象"的真因在写入口, 不在表述能力。 */
11
+ export function clip(text, n) {
12
+ const s = String(text || '').trim();
13
+ if (s.length <= n) return s;
14
+ const head = s.slice(0, n);
15
+ // 优先在标点处断开(中文句读 + 英文句读),其次空格,最后才硬切
16
+ const cut = Math.max(
17
+ head.lastIndexOf('。'), head.lastIndexOf(';'), head.lastIndexOf('!'), head.lastIndexOf('?'),
18
+ head.lastIndexOf(','), head.lastIndexOf('、'), head.lastIndexOf(';'), head.lastIndexOf(','),
19
+ head.lastIndexOf('.'), head.lastIndexOf(' '),
20
+ );
21
+ // 边界太靠前(< 一半)说明这一段本就是长句,宁可硬切也不留个残破的短头
22
+ const keep = cut > n / 2 ? cut : n;
23
+ return `${s.slice(0, keep).replace(/[\s,,、;;.。]+$/, '')}…`;
24
+ }
25
+
26
+ /** slug:取前 n 码点 → 非词字符折叠为 '-'。只用于**文件名**,完整标题另存 TITLE 行。
27
+ * 在标点/空格边界收口,不在字中间切断(否则出 `...-硬切-不` 这种残尾)。 */
28
+ export function slugOf(text, n = 24) {
29
+ const s = String(text || '').trim();
30
+ let head = s.slice(0, n);
31
+ if (s.length > n) {
32
+ const cut = Math.max(head.lastIndexOf(','), head.lastIndexOf('。'), head.lastIndexOf('、'),
33
+ head.lastIndexOf(':'), head.lastIndexOf(','), head.lastIndexOf('.'), head.lastIndexOf(' '));
34
+ if (cut > n / 2) head = head.slice(0, cut);
35
+ }
36
+ return head.replace(/[^\w一-鿿]+/g, '-').replace(/^-+|-+$/g, '').toLowerCase();
37
+ }
package/src/todo.js CHANGED
@@ -4,6 +4,13 @@ import { promises as fs } from 'node:fs';
4
4
  import { brainPath } from './index.js';
5
5
  import { editFile, SKIP } from './lock.js';
6
6
 
7
+ // 根文件行数上限(2026-10-05 从 lint.js 移来):lint 用它报 ROOT-OVER-SIZE,
8
+ // archive 用它在超限时放宽归档窗口。**放 todo.js 而非 lint.js** ——
9
+ // lint 依赖 todo(checkFileShape),反向导入会成环。
10
+ export const TODO_MAX_LINES = 60;
11
+ export const LOG_MAX_LINES = 2000;
12
+ export const INDEX_MAX_LINES = 200;
13
+
7
14
  // ---------- 本地日期 ----------
8
15
  export function today() {
9
16
  // 用本地时区取 YYYY-MM-DD(toISOString 是 UTC, 会跨天错一天)
@@ -993,7 +1000,7 @@ function isUndoneLine(l) {
993
1000
  * 无日期组(### (未标日期))无法判天数,**保守不归档**。
994
1001
  * @returns {{text:string, archived:{date:string,lines:string[],slug:string,count:number}[], skipped:{date:string,reason:string}[], count:number}}
995
1002
  */
996
- export function archiveDoneInText(text, { keepDays = 3, from = today(), slugFor = (d) => `log-${d}` } = {}) {
1003
+ export function archiveDoneInText(text, { keepDays = 3, from = today(), maxLines = 0, slugFor = (d) => `log-${d}` } = {}) {
997
1004
  const lines = String(text || '').split('\n');
998
1005
  const di = lines.findIndex((l) => l.startsWith('## Done'));
999
1006
  if (di === -1) return { text, archived: [], skipped: [], count: 0 };
@@ -1004,7 +1011,14 @@ export function archiveDoneInText(text, { keepDays = 3, from = today(), slugFor
1004
1011
 
1005
1012
  const keep = Math.max(1, Number(keepDays) || 3);
1006
1013
  // 保留 cutoff..from 这 keep 天;比 cutoff 更早的才归档
1007
- const cutoff = daysAgo(keep - 1, from);
1014
+ let cutoff = daysAgo(keep - 1, from);
1015
+ // 超限放宽(2026-10-05 加):ROOT-OVER-SIZE 限看板 ≤60 行,而 keepDays≥1 意味着
1016
+ // 密集工作日(一天几十条 Done)必然超限 —— 两条规则打架,且 archive 拒绝动,
1017
+ // 于是每天提交都被 lint 拦一次。修法:**看板已超行数上限时,把 cutoff 推到当天**,
1018
+ // 让「整天已完成」的组可以立刻归档(仍有 ② 未完成则整天不归档 的保护)。
1019
+ // 注意裁到「今天之后」:判断是 `date >= cutoff → 保留`,若 cutoff = 今天,
1020
+ // 今天的组仍被保留(首版就栽在这,dry-run 一看当天没进归档列表)。
1021
+ if (maxLines && lines.length > maxLines) cutoff = daysAgo(-1, from);
1008
1022
 
1009
1023
  const byDate = new Map();
1010
1024
  const undated = [];