@fanchao8609/agent_brain_sync 1.7.1 → 1.7.2

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/bin/abs.js CHANGED
@@ -83,6 +83,9 @@ const FLAG_SPEC = {
83
83
  'no-skill': { type: 'boolean' },
84
84
  };
85
85
 
86
+ /** 已被"规范键"接管的 raw flag:不再原样漏出(见 parseArgv 返回处的白名单注释)。 */
87
+ const KNOWN_RAW = new Set(['keep-days', 'dry-run', 'no-mcp', 'no-skill']);
88
+
86
89
  function parseArgv(args) {
87
90
  // 坑: parseArgs 会把**任何** `-` 开头的 token 当选项,连正文一起吃:
88
91
  // `abs note "-X 是个坑"` → values={X:true,' ':true,是:true,…},正文全丢(旧手写版只认 `--` 长选项)。
@@ -125,17 +128,39 @@ function parseArgv(args) {
125
128
  if (missing.length) {
126
129
  throw new Error(`✗ ${missing.join('、')} 缺少值\n 用法: --<flag> <值>(如 --dir /path/to/project)`);
127
130
  }
131
+ // 只输出**读者实际用的**规范形状:camelCase 键 + mcp/skill 正向布尔。
132
+ // 坑(OPTS-DOUBLE-KEYS): 曾 `...values` 之后再叠加 camelCase → opts 同时带两份 key
133
+ // (keep-days 与 keepDays、no-mcp 与 mcp),读者得自己猜哪份算数。raw 键无人读,
134
+ // 属纯噪音(grep 验证:opts['keep-days'] 零引用)。
135
+ // 用显式白名单而非 ...values:新增 flag 必须在此声明一次,否则"声明了却没接线"
136
+ // 会静默变成"读不到",而不是意外漏出一个无名键。
128
137
  const o = {
129
- ...values,
130
138
  _: positionals,
139
+ dir: values.dir,
140
+ agent: values.agent,
141
+ id: values.id,
142
+ section: values.section,
143
+ note: values.note,
144
+ as: values.as,
145
+ payload: values.payload,
146
+ tags: values.tags,
147
+ help: !!values.help,
148
+ yes: !!values.yes,
149
+ repair: !!values.repair,
150
+ full: !!values.full,
131
151
  keepDays: values['keep-days'],
132
- dryRun: values['dry-run'],
152
+ dryRun: !!values['dry-run'],
153
+ mcp: !values['no-mcp'],
154
+ skill: !values['no-skill'],
133
155
  };
134
- if (values['no-mcp']) o.mcp = false;
135
- if (values['no-skill']) o.skill = false;
156
+ // 未声明的 `--unknown` 静默收下(旧行为:不进位置参数,也不报错)。
157
+ // 这条保留是因为 rejectExtra 只看位置参数 —— 把未知 flag 放进去会改成报错,
158
+ // 那是另一个行为变更,不在本次范围内。
159
+ for (const [k, v] of Object.entries(values)) {
160
+ if (!(k in o) && !KNOWN_RAW.has(k)) o[k] = v;
161
+ }
136
162
  return o;
137
163
  }
138
-
139
164
  const usage = `abs — agent-brain-sync 记忆工具
140
165
 
141
166
  读:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanchao8609/agent_brain_sync",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
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
@@ -145,11 +145,14 @@ monorepo 若多个子包各自独立交付,可各建一份 `.brain/`。`abs st
145
145
  图谱已存在;收到第一个核心开发指令**之前**走这条链载入上下文:
146
146
 
147
147
  1. **读状态**:`abs load`(或 MCP `abs_load`)读 index 路线 + todo 看板 + 最近 log。
148
- > **Done 区是折叠的(按日期计数)**,不是全量 —— Done 是无上限增长的归档区,
149
- > 全量打印会随项目历史线性膨胀,把上下文塞满(曾占 load 输出 68.8%,
150
- > 长历史项目上单次 load 吃掉 40% 上下文)。
151
- > 要 Done 明细:`abs todo --full`,或看 `.brain/sessions/<日期>-todo归档.md`。
152
- > 「最近动作」每条也按语义边界收口 —— 续接只需线索,不需要日志全文。
148
+ > **load 输出是折叠过的,不是全量。** 两个无上限增长的区块在读取侧收口:
149
+ > - **Done 区** → 按日期计数(曾占 load 输出 68.8%,长历史项目上单次 load 吃掉 40% 上下文)
150
+ > - **index 页面清单** → 各分区只给页数(concept 清单占 load 输出 64%,隨图谱线性增长)
151
+ > - 「最近动作」每条按语义边界收口到 220 字符
152
+ >
153
+ > **路线(Roadmap)区原样保留** —— 那是 load 要传达的状态本身。
154
+ > 要全量明细:`abs todo --full` / `abs index`,或直接读 `.brain/` 文件、
155
+ > `.brain/sessions/<日期>-todo归档.md`。
153
156
  2. **对账滞留(强制,别跳过)**:若 `abs load` 顶部出现 `⏳ 上会话滞留`,说明上会话有任务做完/做到一半就断了。**先收尾再开工**:
154
157
  - 快照里的任务现在真做完了 → `abs todo done <id>`(done 后下次 load 滞留自动消失);
155
158
  - 还没做完 → `abs todo note <id> --note "接到哪/改到哪个文件"` 补断点(别空手续接)。
package/src/store.js CHANGED
@@ -179,7 +179,7 @@ export async function cmdLoad({ dir }) {
179
179
  }
180
180
  sections.push(
181
181
  '--- 当前路线 (index.md) ---',
182
- index || '(index.md 为空)',
182
+ collapseIndex(index) || '(index.md 为空)',
183
183
  '',
184
184
  '--- Todo 看板 (todo.md) ---',
185
185
  collapseDone(todo).text || '(todo.md 为空)',
@@ -205,6 +205,47 @@ export async function cmdLoad({ dir }) {
205
205
  return sections.join('\n');
206
206
  }
207
207
 
208
+ /** index.md 在 `abs load` 里的折叠形态:保留「当前路线」(那是 load 要传达的状态,
209
+ * 且有界),把页面清单各分区折成计数。
210
+ *
211
+ * 坑: index 的 concept 清单带每一页的一句话描述,**隨图谱线性增长** —— 本仓库 17 条
212
+ * 占 load 输出 2286/3571 tok(64%),另一台 40 条的项目约 2.3 倍。它刚成了 Done 之后
213
+ * 最大的单体膨胀源(Done 已折叠,见 collapseDone)。
214
+ *
215
+ * 续接真正需要的只是「有哪些分区、各多少页」(据此知道去哪找),不需要每页写了什么 ——
216
+ * 要那个用 `abs index`(或直接读 index.md / 按词 `abs query`)。
217
+ * `## 当前路线` 不折(它是路线内容本身,非清单)。 */
218
+ export function collapseIndex(text) {
219
+ const s = String(text || '').trim();
220
+ if (!s) return '';
221
+ const out = [];
222
+ let mode = null; // null=逐行透传(路线区/文件头);字符串=当前在计数的分区名
223
+ let n = 0; // mode 非 null 时的清单行计数
224
+ const flush = () => {
225
+ if (mode !== null) out.push(n ? `## ${mode}(${n} 页)` : `## ${mode}`);
226
+ mode = null;
227
+ n = 0;
228
+ };
229
+ for (const l of s.split('\n')) {
230
+ const m = l.match(/^##\s+(.+?)\s*$/);
231
+ if (m) {
232
+ flush();
233
+ const name = m[1].trim();
234
+ if (/路线|Roadmap/i.test(name)) {
235
+ out.push(l); // 「当前路线」是内容不是清单:原样保留
236
+ mode = null;
237
+ } else {
238
+ mode = name; // 页面清单分区:只计数
239
+ }
240
+ continue;
241
+ }
242
+ if (mode === null) out.push(l); // 透传区(含文件头 H1、路线区正文)
243
+ else if (l.trim().startsWith('-')) n++;
244
+ }
245
+ flush();
246
+ return out.join('\n').replace(/\n{3,}/g, '\n\n').trim();
247
+ }
248
+
208
249
  // ---------- wrapup: 滞留快照(B)/ load 内展示由 cmdLoad 完成(A) ----------
209
250
  export async function cmdWrapup({ dir }) {
210
251
  const root = await requireBrain(dir || process.cwd());