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 +2 -2
- package/dist/cli.js +1 -1
- package/dist/plugin.js +134 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -180,5 +180,5 @@ service=memory-bank Plugin initialized (unified) {"projectRoot":"..."}
|
|
|
180
180
|
|
|
181
181
|
## 版本
|
|
182
182
|
|
|
183
|
-
- **版本**: 7.
|
|
184
|
-
- **主要更新**:
|
|
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.
|
|
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
|
-
|
|
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 });
|