memory-bank-skill 7.2.1 → 7.3.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.
package/README.md CHANGED
@@ -180,5 +180,5 @@ service=memory-bank Plugin initialized (unified) {"projectRoot":"..."}
180
180
 
181
181
  ## 版本
182
182
 
183
- - **版本**: 7.1.0
184
- - **主要更新**: v7.1 Index-First + Direct-First 架构(意图驱动路由 + Gating 收紧 + 两层读取协议 + 模板升级路径)
183
+ - **版本**: 7.3.0
184
+ - **主要更新**: Doc-First Gate 默认启用(warn),无 Memory Bank 项目自动提醒初始化
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ import { fileURLToPath } from "url";
27
27
  // package.json
28
28
  var package_default = {
29
29
  name: "memory-bank-skill",
30
- version: "7.2.1",
30
+ version: "7.3.0",
31
31
  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
32
  type: "module",
33
33
  main: "dist/plugin.js",
package/dist/plugin.js CHANGED
@@ -24,6 +24,7 @@ import path from "path";
24
24
  var DEBUG = process.env.MEMORY_BANK_DEBUG === "1";
25
25
  var DEFAULT_MAX_CHARS = 12000;
26
26
  var GATING_MODE = process.env.MEMORY_BANK_GUARD_MODE || "warn";
27
+ var DOC_FIRST_MODE = process.env.MEMORY_BANK_DOC_FIRST_MODE || "warn";
27
28
  var TRUNCATION_NOTICE = `
28
29
 
29
30
  ---
@@ -292,7 +293,7 @@ async function checkMemoryBankExists(root, log) {
292
293
  function getSessionMeta(sessionId, fallbackRoot) {
293
294
  let meta = sessionMetas.get(sessionId);
294
295
  if (!meta) {
295
- meta = { rootsTouched: new Set, lastActiveRoot: fallbackRoot, notifiedMessageIds: new Set, planOutputted: false, promptInProgress: false, userMessageReceived: false, sessionNotified: false, userMessageSeq: 0 };
296
+ meta = { rootsTouched: new Set, lastActiveRoot: fallbackRoot, notifiedMessageIds: new Set, planOutputted: false, promptInProgress: false, userMessageReceived: false, sessionNotified: false, userMessageSeq: 0, pendingDocFirstSatisfied: false };
296
297
  sessionMetas.set(sessionId, meta);
297
298
  }
298
299
  return meta;
@@ -351,13 +352,32 @@ var EXCLUDED_DIRS = [
351
352
  /^\.claude\//
352
353
  ];
353
354
  var MEMORY_BANK_PATTERN = /^memory-bank\//;
355
+ var DOC_FIRST_FILE_PATTERNS = [
356
+ /\.py$/,
357
+ /\.ts$/,
358
+ /\.tsx$/,
359
+ /\.js$/,
360
+ /\.jsx$/,
361
+ /\.go$/,
362
+ /\.rs$/,
363
+ /\.vue$/,
364
+ /\.svelte$/
365
+ ];
354
366
  function isDisabled() {
355
367
  return process.env.MEMORY_BANK_DISABLED === "1" || process.env.MEMORY_BANK_DISABLED === "true";
356
368
  }
357
- function getMessageGatingState(gatingKey) {
369
+ function getMessageGatingState(gatingKey, sessionId, projectRoot) {
358
370
  let state = messageGatingStates.get(gatingKey);
359
371
  if (!state) {
360
- state = { readFiles: new Set, contextSatisfied: false, warnedThisMessage: false };
372
+ let inheritDocFirst = false;
373
+ if (sessionId && projectRoot) {
374
+ const meta = sessionMetas.get(sessionId);
375
+ if (meta?.pendingDocFirstSatisfied) {
376
+ inheritDocFirst = true;
377
+ meta.pendingDocFirstSatisfied = false;
378
+ }
379
+ }
380
+ state = { readFiles: new Set, contextSatisfied: false, warnedThisMessage: false, docFirstSatisfied: inheritDocFirst, docFirstWarned: false };
361
381
  messageGatingStates.set(gatingKey, state);
362
382
  if (messageGatingStates.size > 100) {
363
383
  const first = messageGatingStates.keys().next().value;
@@ -928,7 +948,7 @@ ${triggers.join(`
928
948
  return;
929
949
  }
930
950
  if (event.type === "session.created") {
931
- sessionMetas.set(sessionId, { rootsTouched: new Set, lastActiveRoot: projectRoot, notifiedMessageIds: new Set, planOutputted: false, promptInProgress: false, userMessageReceived: false, sessionNotified: false, userMessageSeq: 0 });
951
+ sessionMetas.set(sessionId, { rootsTouched: new Set, lastActiveRoot: projectRoot, notifiedMessageIds: new Set, planOutputted: false, promptInProgress: false, userMessageReceived: false, sessionNotified: false, userMessageSeq: 0, pendingDocFirstSatisfied: false });
932
952
  const parentID = info?.parentID;
933
953
  sessionsById.set(sessionId, { parentID });
934
954
  log.info("Session created", { sessionId, parentID });
@@ -1152,7 +1172,7 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1152
1172
  const meta = getSessionMeta(sessionID, projectRoot);
1153
1173
  const messageKey = meta.lastUserMessageKey || "default";
1154
1174
  const gatingKey = `${sessionID}::${messageKey}`;
1155
- const gatingState = getMessageGatingState(gatingKey);
1175
+ const gatingState = getMessageGatingState(gatingKey, sessionID, projectRoot);
1156
1176
  const toolLower2 = tool.toLowerCase();
1157
1177
  const readTools = ["read"];
1158
1178
  if (readTools.includes(toolLower2)) {
@@ -1272,6 +1292,111 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1272
1292
  }
1273
1293
  }
1274
1294
  }
1295
+ if (DOC_FIRST_MODE !== "off") {
1296
+ const dfMeta = getSessionMeta(sessionID, projectRoot);
1297
+ const dfMessageKey = dfMeta.lastUserMessageKey || "default";
1298
+ const dfGatingKey = `${sessionID}::${dfMessageKey}`;
1299
+ const dfState = getMessageGatingState(dfGatingKey, sessionID, projectRoot);
1300
+ const dfToolLower = tool.toLowerCase();
1301
+ const dfWriteTools = ["write", "edit", "multiedit", "apply_patch", "patch"];
1302
+ if (dfWriteTools.includes(dfToolLower) && !dfState.docFirstSatisfied && !dfState.docFirstWarned) {
1303
+ const dfTargetPaths = extractWritePaths(dfToolLower, output.args || {});
1304
+ const mbCheckResults = await Promise.all(dfTargetPaths.map((p) => isMemoryBankPath(p)));
1305
+ const hasCodeFile = dfTargetPaths.some((p, i) => {
1306
+ if (mbCheckResults[i])
1307
+ return false;
1308
+ return DOC_FIRST_FILE_PATTERNS.some((pat) => pat.test(p.toLowerCase()));
1309
+ });
1310
+ if (hasCodeFile && !dfState.warnedThisMessage) {
1311
+ const hasMemoryBank = await checkMemoryBankExists(projectRoot, log);
1312
+ if (!hasMemoryBank) {
1313
+ const state = getRootState(sessionID, projectRoot);
1314
+ if (!state.initReminderFired) {
1315
+ state.initReminderFired = true;
1316
+ log.info("Doc-First Gate: no memory-bank, sending init suggestion", { sessionID });
1317
+ client.session.prompt({
1318
+ path: { id: sessionID },
1319
+ body: {
1320
+ noReply: true,
1321
+ variant: PLUGIN_PROMPT_VARIANT,
1322
+ parts: [{
1323
+ type: "text",
1324
+ text: `## [Memory Bank] \u9879\u76EE\u5C1A\u672A\u542F\u7528
1325
+
1326
+ ` + `\u68C0\u6D4B\u5230\u4EE3\u7801\u5199\u5165\uFF0C\u4F46\u9879\u76EE\u5C1A\u672A\u542F\u7528 Memory Bank\u3002
1327
+
1328
+ ` + `\u5EFA\u8BAE\u8FD0\u884C \`/memory-bank-refresh\` \u521D\u59CB\u5316\uFF0C\u5F00\u542F\u6587\u6863\u5148\u884C\u5DE5\u4F5C\u6D41\u3002`
1329
+ }]
1330
+ }
1331
+ }).catch((err) => log.error("Failed to send init suggestion:", String(err)));
1332
+ }
1333
+ } else if (DOC_FIRST_MODE === "block") {
1334
+ log.warn("Doc-First Gate: code write blocked (no MB doc written)", {
1335
+ sessionID,
1336
+ gatingKey: dfGatingKey,
1337
+ tool,
1338
+ targetPaths: dfTargetPaths
1339
+ });
1340
+ throw new Error(`[Doc-First Gate] \u8BF7\u5148\u6C89\u6DC0\u5DE5\u4F5C\u6587\u6863\u518D\u5199\u4EE3\u7801\u3002
1341
+
1342
+ ` + `\u8BF7\u7528 MemoryWriter \u5148\u8BB0\u5F55\u4F60\u8981\u505A\u4EC0\u4E48\uFF1A
1343
+ ` + `\u2022 \u4FEE Bug / \u8E29\u5751 \u2192 learnings/YYYY-MM-DD-xxx.md
1344
+ ` + `\u2022 \u65B0\u529F\u80FD / \u9700\u6C42 \u2192 requirements/REQ-xxx.md
1345
+ ` + `\u2022 \u91CD\u6784 / \u4F18\u5316 \u2192 design/design-xxx.md
1346
+ ` + `\u2022 \u7B80\u5355\u53D8\u66F4 \u2192 \u8FFD\u52A0\u5230 progress.md
1347
+
1348
+ ` + `\u8C03\u7528\u65B9\u5F0F: proxy_task({ subagent_type: "memory-bank-writer", ... })
1349
+ ` + `\u5199\u5B8C\u6587\u6863\u540E\u518D\u6267\u884C\u4EE3\u7801\u4FEE\u6539\u3002`);
1350
+ } else {
1351
+ dfState.docFirstWarned = true;
1352
+ log.info("Doc-First Gate: warning issued", {
1353
+ sessionID,
1354
+ gatingKey: dfGatingKey,
1355
+ tool,
1356
+ targetPaths: dfTargetPaths
1357
+ });
1358
+ client.session.prompt({
1359
+ path: { id: sessionID },
1360
+ body: {
1361
+ noReply: true,
1362
+ variant: PLUGIN_PROMPT_VARIANT,
1363
+ parts: [{
1364
+ type: "text",
1365
+ text: `## \u26A0\uFE0F [Doc-First] \u5EFA\u8BAE\u5148\u6C89\u6DC0\u5DE5\u4F5C\u6587\u6863\u518D\u5199\u4EE3\u7801
1366
+
1367
+ ` + `\u8BF7\u7528 MemoryWriter \u5148\u8BB0\u5F55\u4F60\u8981\u505A\u4EC0\u4E48\uFF0C\u5E76\u4F5C\u4E3A\u7B2C\u4E00\u4F18\u5148\u7EA7 todo\uFF1A
1368
+ ` + `\u2022 \u4FEE Bug / \u8E29\u5751 \u2192 learnings/YYYY-MM-DD-xxx.md
1369
+ ` + `\u2022 \u65B0\u529F\u80FD / \u9700\u6C42 \u2192 requirements/REQ-xxx.md
1370
+ ` + `\u2022 \u91CD\u6784 / \u4F18\u5316 \u2192 design/design-xxx.md
1371
+ ` + `\u2022 \u7B80\u5355\u53D8\u66F4 \u2192 \u8FFD\u52A0\u5230 progress.md
1372
+
1373
+ ` + `\u8C03\u7528\u65B9\u5F0F: \`proxy_task({ subagent_type: "memory-bank-writer", ... })\``
1374
+ }]
1375
+ }
1376
+ }).catch((err) => log.error("Failed to send doc-first warning:", String(err)));
1377
+ }
1378
+ }
1379
+ }
1380
+ }
1381
+ const markParentDocFirstSatisfied = (writerSessionID) => {
1382
+ const parentID = sessionsById.get(writerSessionID)?.parentID;
1383
+ if (!parentID)
1384
+ return;
1385
+ const parentMeta = getSessionMeta(parentID, projectRoot);
1386
+ const parentMsgKey = parentMeta.lastUserMessageKey;
1387
+ if (parentMsgKey) {
1388
+ const parentGatingKey = `${parentID}::${parentMsgKey}`;
1389
+ const parentGating = getMessageGatingState(parentGatingKey, parentID, projectRoot);
1390
+ parentGating.docFirstSatisfied = true;
1391
+ log.debug("Doc-First: parent satisfied via writer write", { writerSessionID, parentID, parentGatingKey });
1392
+ }
1393
+ const defaultGatingKey = `${parentID}::default`;
1394
+ const defaultState = messageGatingStates.get(defaultGatingKey);
1395
+ if (defaultState) {
1396
+ defaultState.docFirstSatisfied = true;
1397
+ }
1398
+ parentMeta.pendingDocFirstSatisfied = true;
1399
+ };
1275
1400
  const isWriterAllowed = (sid) => {
1276
1401
  if (writerSessionIDs.has(sid))
1277
1402
  return true;
@@ -1338,6 +1463,7 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1338
1463
  }
1339
1464
  if (isWriterAllowed(sessionID)) {
1340
1465
  log.debug("Writer agent write allowed", { sessionID, tool, targetPath });
1466
+ markParentDocFirstSatisfied(sessionID);
1341
1467
  return;
1342
1468
  }
1343
1469
  blockWrite("not writer agent", {
@@ -1488,6 +1614,7 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1488
1614
  if (await isMemoryBankPath(resolvedTarget)) {
1489
1615
  if (isWriterAllowed(sessionID)) {
1490
1616
  log.debug("Writer agent bash redirect allowed", { sessionID, command: command.slice(0, 100) });
1617
+ markParentDocFirstSatisfied(sessionID);
1491
1618
  return;
1492
1619
  }
1493
1620
  blockWrite("bash redirect to memory-bank", { command: command.slice(0, 200), segment: segment.slice(0, 100) });
@@ -1503,6 +1630,7 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1503
1630
  if (await isMemoryBankPath(resolved)) {
1504
1631
  if (isWriterAllowed(sessionID)) {
1505
1632
  log.debug("Writer agent find with dangerous flags allowed", { sessionID });
1633
+ markParentDocFirstSatisfied(sessionID);
1506
1634
  return;
1507
1635
  }
1508
1636
  blockWrite("find with dangerous flags on memory-bank", { command: command.slice(0, 200), segment: segment.slice(0, 100) });
@@ -1519,6 +1647,7 @@ Or call: proxy_task({ subagent_type: "memory-reader", ... })`);
1519
1647
  if (await isMemoryBankPath(resolved)) {
1520
1648
  if (isWriterAllowed(sessionID)) {
1521
1649
  log.debug("Writer agent bash write allowed", { sessionID, command: command.slice(0, 100) });
1650
+ markParentDocFirstSatisfied(sessionID);
1522
1651
  return;
1523
1652
  }
1524
1653
  blockWrite("bash write to memory-bank", { command: command.slice(0, 200), pathArg, resolved });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memory-bank-skill",
3
- "version": "7.2.1",
3
+ "version": "7.3.0",
4
4
  "description": "Memory Bank - 项目记忆系统,让 AI 助手在每次对话中都能快速理解项目上下文",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",