@fanchao8609/agent_brain_sync 1.14.1 → 1.15.0

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.
Files changed (3) hide show
  1. package/bin/abs.js +2 -0
  2. package/package.json +1 -1
  3. package/src/store.js +42 -19
package/bin/abs.js CHANGED
@@ -284,6 +284,7 @@ const subUsage = {
284
284
  '用法:',
285
285
  ' abs todo 看板',
286
286
  ' abs todo add <id> [--note "做什么"] [--section 讨论中|滞留中]',
287
+ ' abs todo rename <旧id> --note "<新id>" 给自动登记的占位条目起真名',
287
288
  ' abs todo note <id> --note "断点/进度"',
288
289
  ' abs todo state <id> --note 进行中|讨论中|滞留中',
289
290
  ' abs todo done <id> [--as 落地|否决|仅方案] [结语文字]',
@@ -351,6 +352,7 @@ const TODO_ACTIONS = {
351
352
  start: 'start', // add 的别名(老习惯保留)
352
353
  note: 'note',
353
354
  state: 'state', // 改行首状态标记:进行中|讨论中|滞留中(原地,不搬区)
355
+ rename: 'rename', // 改任务 id:hook 只开 auto-TBD-<会话> 占位,真名由此处补
354
356
  done: 'done',
355
357
  };
356
358
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanchao8609/agent_brain_sync",
3
- "version": "1.14.1",
3
+ "version": "1.15.0",
4
4
  "description": "agent-brain-sync: 跨会话 AI 编码记忆 — hook 纯触发 + CLI/MCP 读写 .brain markdown 图谱, 防并发写保护。",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/store.js CHANGED
@@ -1120,6 +1120,31 @@ export async function cmdTask({ dir, action, id, section, note, as }) {
1120
1120
  });
1121
1121
  return `✓ 任务${r.updated ? '更新(幂等)' : '登记'} → ${brainPath(root, 'todo.md')}\n [${st}] ${id} ${atTag(who)}${note ? ' — ' + note : ''}`;
1122
1122
  }
1123
+ if (action === 'rename') {
1124
+ // 给任务改名(2026-10-05):hook 只开 `auto-TBD-<会话>` 占位,真名由理解任务的一方起
1125
+ // (`fix-skill-trigger` 这种不是从用户字面抽出来的,是读懂在干什么之后起的)。
1126
+ // --note 传新 id(沿用既有 CLI 习惯,免得再加一个 flag)。
1127
+ if (!note) throw new Error('✗ 用法: abs todo rename <旧id> --note "<新id>"');
1128
+ const newId = String(note).replace(/[\u200b\u200c\u200d\ufeff]/g, '').trim();
1129
+ if (!newId || /\s/.test(newId)) throw new Error(`✗ 新 id 不能为空或含空白: "${newId}"`);
1130
+ const f = brainPath(root, 'todo.md');
1131
+ let done = false;
1132
+ await editFile(f, (cur) => {
1133
+ if (cur === null) return SKIP;
1134
+ const lines = cur.split('\n');
1135
+ const want = String(id).replace(/[\u200b\u200c\u200d\ufeff]/g, '');
1136
+ for (let i = 0; i < lines.length; i++) {
1137
+ if (idOfTaskLine(lines[i]) !== want) continue;
1138
+ lines[i] = lines[i].replace(want, newId);
1139
+ // 占位描述「待命名」随之换掉 —— 改完名还写着"待命名"会让人以为没改。
1140
+ lines[i] = lines[i].replace(/—\s*待命名\s*/, `— ${newId.replace(/^(auto-|fix-|feat-)/, '')} `);
1141
+ done = true;
1142
+ break;
1143
+ }
1144
+ return done ? { text: lines.join('\n') } : SKIP;
1145
+ });
1146
+ return done ? `✓ 任务改名 ${id} → ${newId}` : `[NO_MATCH] 未找到含 "${id}" 的未完成任务行`;
1147
+ }
1123
1148
  if (action === 'state') {
1124
1149
  // 改状态标记(原地,不搬区):进行中 / 讨论中 / 滞留中
1125
1150
  if (!note) throw new Error(`✗ 用法: abs todo state <id> --note "${TASK_STATES.join('|')}"`);
@@ -1252,32 +1277,30 @@ export async function cmdAutoTask({ dir, session, prompt, note }) {
1252
1277
  return r?.changed ? `↳ ${existingId} 断点更新` : null;
1253
1278
  }
1254
1279
 
1255
- // 首次登记: 关键词优先从用户指令抽;抽不出(寒暄/提问/无指令)时用断点兜底。
1280
+ // 占位登记(2026-10-05 用户实报后重设计)。
1256
1281
  //
1257
- // 为何 note 也能兜底(2026-10-05 时机改动后新增):登记时机已挪到"第一次真改文件",
1258
- // 那一刻【已经动过文件】就是最可靠的"这是件事"的证据 —— 此时因抽不出关键词而放弃,
1259
- // 等于用户寒暄两句后开始干活却什么都没记。用文件名当兜底 slug 比不登记有用。
1260
- // 兜底仅在【有断点】时启用 —— 断点的存在本身就意味着"刚动过文件"。
1261
- // 只有 prompt 而抽不出关键词(纯寒暄/提问、且没动文件)→ 依然不登记(用户实报的那个坑)。
1262
- const slug = autoTaskId(prompt) || (note ? autoTaskSlugFromNote(note) : null);
1263
- if (!slug) return null;
1264
- // id = 关键词(可读);同 slug 已存在则加序号,避免两条不同活撞同一个 id。
1265
- let id = `${AUTO_TASK_PREFIX}${slug}`;
1266
- let n = 1;
1267
- await editFile(todoPath, (cur) => {
1268
- if (cur === null) return SKIP;
1269
- const ids = new Set(cur.split('\n').map((l) => idOfTaskLine(l)).filter(Boolean));
1270
- while (ids.has(id)) id = `${AUTO_TASK_PREFIX}${slug}-${++n}`;
1271
- return SKIP;
1272
- });
1282
+ // 为何不再抽用户话当名字:机器抽词天生不可靠 —— 实测抽出过
1283
+ // `我已经重启测试一下`(用户的开场白)、`本轮有改动`(更糟:那是 hook 自己
1284
+ // 生成的断点文本,等于把机器的话当成用户意图)。
1285
+ // 而用户想要的名字长这样:`fix-skill-trigger` / `fix-opencode-v2-plugin` ——
1286
+ // 那**不是从用户的字面上抽出来的**,是理解"在干什么"之后起的名字。
1287
+ //
1288
+ // 故分工改为:hook 只开一个 `TBD-<会话>` 占位(明确标记"未命名"),
1289
+ // 名字留给理解任务的一方改(`abs todo rename` 或直接编辑 todo.md)。
1290
+ // 占位名机器化、一眼可辨,不冒充真任务名。
1291
+ // 开占位的前置条件:**真的动过文件**(hook 传了 --note)。
1292
+ // 只有 prompt 而无 note = 用户说了句话但没动手 → 不开(这是"时机"那条坑的守门,
1293
+ // 改成固定占位后一度丢掉此判断,导致纯寒暄也开条目 —— 单测抓到的)。
1294
+ if (!note) return null;
1295
+ const id = `${AUTO_TASK_PREFIX}TBD-${sid}`;
1273
1296
  const who = await requireUser();
1274
1297
  await ensurePersonPage(root, who);
1275
1298
  await upsertTask(root, {
1276
1299
  section: SEC.todo,
1277
- text: `[进行中] ${id} ${atTag(who)} — ${slug} (${MARK})`,
1300
+ text: `[进行中] ${id} ${atTag(who)} — 待命名 (${MARK})`,
1278
1301
  });
1279
1302
  if (note) await setBreakpoint(root, { id, text: note });
1280
- return `✓ 自动登记 ${id} — ${slug}`;
1303
+ return `✓ 自动登记占位 ${id}(待命名,稍后改名)`;
1281
1304
  }
1282
1305
 
1283
1306
  /** 收尾: 把自动登记的未完成任务标 done(用户没显式接管它们)。