memory-bank-skill 7.4.0 → 7.4.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/README.md CHANGED
@@ -180,5 +180,5 @@ service=memory-bank Plugin initialized (unified) {"projectRoot":"..."}
180
180
 
181
181
  ## 版本
182
182
 
183
- - **版本**: 7.4.0
184
- - **主要更新**: Writer 轻量化 去掉 writer subagent,主 agent 直接写入 + Plugin 注入 writing guideline
183
+ - **版本**: 7.4.1
184
+ - **主要更新**: Writing guideline 改为 post-write advisory 写入后建议参考规范检查,per-turn 去重
package/dist/cli.js CHANGED
@@ -1,21 +1,5 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- var __create = Object.create;
4
- var __getProtoOf = Object.getPrototypeOf;
5
- var __defProp = Object.defineProperty;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __toESM = (mod, isNodeMode, target) => {
9
- target = mod != null ? __create(__getProtoOf(mod)) : {};
10
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
- for (let key of __getOwnPropNames(mod))
12
- if (!__hasOwnProp.call(to, key))
13
- __defProp(to, key, {
14
- get: () => mod[key],
15
- enumerable: true
16
- });
17
- return to;
18
- };
19
3
  var __require = import.meta.require;
20
4
 
21
5
  // src/cli.ts
@@ -27,7 +11,7 @@ import { fileURLToPath } from "url";
27
11
  // package.json
28
12
  var package_default = {
29
13
  name: "memory-bank-skill",
30
- version: "7.4.0",
14
+ version: "7.4.2",
31
15
  description: "Memory Bank - \u9879\u76EE\u8BB0\u5FC6\u7CFB\u7EDF\uFF0C\u8BA9 AI \u52A9\u624B\u5728\u6BCF\u6B21\u5BF9\u8BDD\u4E2D\u90FD\u80FD\u5FEB\u901F\u7406\u89E3\u9879\u76EE\u4E0A\u4E0B\u6587",
32
16
  type: "module",
33
17
  main: "dist/plugin.js",
package/dist/plugin.js CHANGED
@@ -1,24 +1,9 @@
1
1
  // @bun
2
- var __create = Object.create;
3
- var __getProtoOf = Object.getPrototypeOf;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __toESM = (mod, isNodeMode, target) => {
8
- target = mod != null ? __create(__getProtoOf(mod)) : {};
9
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
- for (let key of __getOwnPropNames(mod))
11
- if (!__hasOwnProp.call(to, key))
12
- __defProp(to, key, {
13
- get: () => mod[key],
14
- enumerable: true
15
- });
16
- return to;
17
- };
18
2
  var __require = import.meta.require;
19
3
 
20
4
  // plugin/memory-bank.ts
21
5
  import { stat, readFile, access, realpath } from "fs/promises";
6
+ import { realpathSync } from "fs";
22
7
  import { execSync } from "child_process";
23
8
  import path from "path";
24
9
  var DEBUG = process.env.MEMORY_BANK_DEBUG === "1";
@@ -374,7 +359,7 @@ function getMessageGatingState(gatingKey, sessionId, projectRoot) {
374
359
  meta.pendingDocFirstSatisfied = false;
375
360
  }
376
361
  }
377
- state = { readFiles: new Set, contextSatisfied: false, warnedThisMessage: false, docFirstSatisfied: inheritDocFirst, docFirstWarned: false };
362
+ state = { readFiles: new Set, contextSatisfied: false, warnedThisMessage: false, docFirstSatisfied: inheritDocFirst, docFirstWarned: false, writingGuideInjected: false };
378
363
  messageGatingStates.set(gatingKey, state);
379
364
  if (messageGatingStates.size > 100) {
380
365
  const first = messageGatingStates.keys().next().value;
@@ -398,12 +383,63 @@ var FALLBACK_ANCHORS = [
398
383
  ];
399
384
  function canonicalizeRelPath(rawPath, projectRoot) {
400
385
  const abs = path.isAbsolute(rawPath) ? rawPath : path.resolve(projectRoot, rawPath);
401
- const rel = path.relative(projectRoot, abs);
402
- if (rel.startsWith(".."))
403
- return "";
386
+ let rel = path.relative(projectRoot, abs);
387
+ if (rel.startsWith("..")) {
388
+ try {
389
+ const realAbs = realpathSync(abs);
390
+ const realRoot = realpathSync(projectRoot);
391
+ rel = path.relative(realRoot, realAbs);
392
+ if (rel.startsWith(".."))
393
+ return "";
394
+ } catch {
395
+ return "";
396
+ }
397
+ }
404
398
  const posix = rel.replace(/\\/g, "/");
405
399
  return process.platform === "darwin" || process.platform === "win32" ? posix.toLowerCase() : posix;
406
400
  }
401
+ function stripQuotedContent(cmd) {
402
+ let result = "";
403
+ let inSingle = false;
404
+ let inDouble = false;
405
+ for (let i = 0;i < cmd.length; i++) {
406
+ const ch = cmd[i];
407
+ if (inSingle) {
408
+ if (ch === "'")
409
+ inSingle = false;
410
+ continue;
411
+ }
412
+ if (inDouble) {
413
+ if (ch === "\\" && i + 1 < cmd.length) {
414
+ i++;
415
+ continue;
416
+ }
417
+ if (ch === '"')
418
+ inDouble = false;
419
+ continue;
420
+ }
421
+ if (ch === "'") {
422
+ inSingle = true;
423
+ continue;
424
+ }
425
+ if (ch === '"') {
426
+ inDouble = true;
427
+ continue;
428
+ }
429
+ result += ch;
430
+ }
431
+ return result;
432
+ }
433
+ var BASH_REDIRECT_PATTERNS = [
434
+ /(?<![0-9&])>(?![&>])\s*\S/,
435
+ /(?<![0-9&])>>\s*\S/,
436
+ /\|\s*tee\s/,
437
+ /\bsed\s+(-[^-]*)?-i/,
438
+ /\bperl\s+(-[^-]*)?-[pi]/
439
+ ];
440
+ function hasBashRedirectOutsideQuotes(cmd) {
441
+ return BASH_REDIRECT_PATTERNS.some((p) => p.test(stripQuotedContent(cmd)));
442
+ }
407
443
  function isAnchorPath(canonicalPath) {
408
444
  return ANCHOR_PATH_PATTERNS.some((p) => p.test(canonicalPath));
409
445
  }
@@ -1061,17 +1097,7 @@ ${triggers.join(`
1061
1097
  if (anchorState.recovery?.required) {
1062
1098
  const writeToolsForRecovery = ["write", "edit", "multiedit", "apply_patch", "patch"];
1063
1099
  const isWriteTool = writeToolsForRecovery.includes(toolLowerForRecovery);
1064
- const isBashWrite = toolLowerForRecovery === "bash" && (() => {
1065
- const cmd = output.args?.command || "";
1066
- const bashWritePatterns = [
1067
- /(?<![0-9&])>(?![&>])\s*\S/,
1068
- /(?<![0-9&])>>\s*\S/,
1069
- /\|\s*tee\s/,
1070
- /\bsed\s+(-[^-]*)?-i/,
1071
- /\bperl\s+(-[^-]*)?-[pi]/
1072
- ];
1073
- return bashWritePatterns.some((p) => p.test(cmd));
1074
- })();
1100
+ const isBashWrite = toolLowerForRecovery === "bash" && hasBashRedirectOutsideQuotes(output.args?.command || "");
1075
1101
  if (isWriteTool || isBashWrite) {
1076
1102
  const riskLevel = isWriteTool ? assessWriteRisk(toolLowerForRecovery, output.args || {}, projectRoot) : "medium";
1077
1103
  if (riskLevel !== "low") {
@@ -1229,13 +1255,6 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1229
1255
  }
1230
1256
  if (toolLower2 === "bash" && !gatingState.contextSatisfied) {
1231
1257
  const command = output.args?.command || "";
1232
- const bashWritePatterns = [
1233
- /(?<![0-9&])>(?![&>])\s*\S/,
1234
- /(?<![0-9&])>>\s*\S/,
1235
- /\|\s*tee\s/,
1236
- /\bsed\s+(-[^-]*)?-i/,
1237
- /\bperl\s+(-[^-]*)?-[pi]/
1238
- ];
1239
1258
  const bashSensitivePatterns = [
1240
1259
  /package\.json/i,
1241
1260
  /\.env/i,
@@ -1247,7 +1266,7 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1247
1266
  /\.github\/workflows/i,
1248
1267
  /infra\//i
1249
1268
  ];
1250
- const isLikelyWrite = bashWritePatterns.some((p) => p.test(command));
1269
+ const isLikelyWrite = hasBashRedirectOutsideQuotes(command);
1251
1270
  const isSensitiveTarget = bashSensitivePatterns.some((p) => p.test(command));
1252
1271
  if (isLikelyWrite) {
1253
1272
  const riskLevel = isSensitiveTarget ? "high" : "medium";
@@ -1375,7 +1394,16 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1375
1394
  }
1376
1395
  }
1377
1396
  }
1378
- async function injectWritingGuideline(sid) {
1397
+ async function injectWritingGuideline(sid, writtenPaths) {
1398
+ const meta = getSessionMeta(sid, projectRoot);
1399
+ const messageKey = meta.lastUserMessageKey || "default";
1400
+ const gatingKey = `${sid}::${messageKey}`;
1401
+ const gatingState = getMessageGatingState(gatingKey, sid, projectRoot);
1402
+ if (gatingState.writingGuideInjected)
1403
+ return;
1404
+ gatingState.writingGuideInjected = true;
1405
+ const pathList = writtenPaths.map((p) => `- ${p}`).join(`
1406
+ `);
1379
1407
  client.session.prompt({
1380
1408
  path: { id: sid },
1381
1409
  body: {
@@ -1385,8 +1413,13 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1385
1413
  type: "text",
1386
1414
  text: `## [Memory Bank Writing Guide]
1387
1415
 
1388
- ` + `\u6B63\u5728\u5199\u5165 memory-bank/\uFF0C\u8BF7\u5148\u52A0\u8F7D\u5199\u5165\u89C4\u8303\uFF1A
1389
- ` + `read({ filePath: "~/.config/opencode/skills/memory-bank/references/writer.md" })`
1416
+ ` + `\u5DF2\u68C0\u6D4B\u5230 memory-bank/ \u5199\u5165\uFF1A
1417
+ ${pathList}
1418
+
1419
+ ` + `\u5EFA\u8BAE\u53C2\u8003\u5199\u5165\u89C4\u8303\u786E\u8BA4\u5185\u5BB9\u662F\u5426\u9700\u8981\u8C03\u6574\uFF1A
1420
+ ` + `read({ filePath: "~/.config/opencode/skills/memory-bank/references/writer.md" })
1421
+
1422
+ ` + `\u5982\u9700\u4FEE\u6B63\u8BF7\u76F4\u63A5\u4FEE\u6539\uFF0C\u7136\u540E\u7EE7\u7EED\u539F\u672C\u7684\u4EFB\u52A1\u3002`
1390
1423
  }]
1391
1424
  }
1392
1425
  }).catch((err) => log.error("Failed to send writing guideline:", String(err)));
@@ -1439,6 +1472,7 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1439
1472
  const targetPaths = extractPaths(toolLower, output.args || {});
1440
1473
  if (targetPaths.length === 0)
1441
1474
  return;
1475
+ const mbWrittenPaths = [];
1442
1476
  for (const targetPath of targetPaths) {
1443
1477
  if (!await isMemoryBankPath(targetPath))
1444
1478
  continue;
@@ -1447,10 +1481,13 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1447
1481
  throw new Error(`[Memory Bank Guard] memory-bank/ \u4E0B\u53EA\u5141\u8BB8\u5199\u5165 .md \u6587\u4EF6\u3002
1448
1482
  ` + `\u76EE\u6807\u6587\u4EF6: ${targetPath}`);
1449
1483
  }
1450
- await injectWritingGuideline(sessionID);
1484
+ mbWrittenPaths.push(targetPath);
1451
1485
  markDocFirstSatisfied(sessionID);
1452
1486
  log.debug("Memory Bank write allowed", { sessionID, tool, targetPath });
1453
1487
  }
1488
+ if (mbWrittenPaths.length > 0) {
1489
+ await injectWritingGuideline(sessionID, mbWrittenPaths);
1490
+ }
1454
1491
  }
1455
1492
  if (tool.toLowerCase() === "bash") {
1456
1493
  const command = output.args?.command;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memory-bank-skill",
3
- "version": "7.4.0",
3
+ "version": "7.4.2",
4
4
  "description": "Memory Bank - 项目记忆系统,让 AI 助手在每次对话中都能快速理解项目上下文",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",
@@ -284,6 +284,33 @@ USER_BLOCK 将保持不变。
284
284
  | Decision Highlights | 新增决策时追加(保持 10-20 条以内) |
285
285
  | Routing Rules | 仅在 details/ 结构变化时更新 |
286
286
 
287
+ ### MEMORY.md 膨胀控制
288
+
289
+ MEMORY.md 是单入口索引,必须保持精简。按行数控制膨胀:
290
+
291
+ | 行数 | 动作 |
292
+ |------|------|
293
+ | ≤ 120 行 | 正常维护 |
294
+ | 120 - 200 行 | 审查:Decision Highlights 超 15 条归档旧条目到 details/patterns.md,Quick Answers 超 8 条删过期项 |
295
+ | > 200 行 | 强制分区:膨胀区块提取到 details/,MEMORY.md 只留摘要 + 指针 |
296
+
297
+ **分区操作**(> 200 行时):
298
+
299
+ ```
300
+ 1. 识别膨胀区块(通常是 Decision Highlights 或 Routing Rules)
301
+ 2. 提取到 details/:
302
+ - Decision Highlights 旧条目 → details/patterns.md
303
+ - Routing Rules 过多 → details/routing-extended.md
304
+ - Quick Answers 过多 → details/faq.md
305
+ 3. MEMORY.md 保留:
306
+ - 最近 5-8 条 Decision Highlights
307
+ - 核心 Routing Rules(通用 + 高频项目特定)
308
+ - 3-5 条最高频 Quick Answers
309
+ 4. 添加指针:「完整历史见 details/patterns.md」
310
+ ```
311
+
312
+ **原则**:MEMORY.md 是路由器不是数据库,超过一屏能扫完的量就该分区。
313
+
287
314
  ### 详情文件写入
288
315
 
289
316
  ```