@hir4ta/mneme 0.25.0 → 0.25.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/.claude-plugin/plugin.json +1 -1
- package/README.ja.md +1 -1
- package/README.md +1 -1
- package/dist/lib/save/index.js +62 -6
- package/dist/lib/session/finalize.js +62 -6
- package/dist/public/assets/{index-rwYM2mwM.js → index-DQM1sNuo.js} +87 -87
- package/dist/public/assets/{react-force-graph-2d-n2R24ZBV.js → react-force-graph-2d-pVR76oYE.js} +1 -1
- package/dist/public/index.html +1 -1
- package/dist/server.js +21 -4
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mneme",
|
|
3
3
|
"description": "A plugin that provides long-term memory for Claude Code. It automatically saves context lost during auto-compact, offering features for session restoration, recording technical decisions, and learning developer patterns.",
|
|
4
|
-
"version": "0.25.
|
|
4
|
+
"version": "0.25.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "hir4ta"
|
|
7
7
|
},
|
package/README.ja.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# mneme
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|

|
|
5
5
|
[](https://www.npmjs.com/package/@hir4ta/mneme)
|
|
6
6
|
[](https://github.com/hir4ta/mneme/blob/main/LICENSE)
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# mneme
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|

|
|
5
5
|
[](https://www.npmjs.com/package/@hir4ta/mneme)
|
|
6
6
|
[](https://github.com/hir4ta/mneme/blob/main/LICENSE)
|
package/dist/lib/save/index.js
CHANGED
|
@@ -518,9 +518,16 @@ async function parseTranscriptIncremental(transcriptPath, lastSavedLine) {
|
|
|
518
518
|
progressEvents.get(key)?.push(event);
|
|
519
519
|
}
|
|
520
520
|
}
|
|
521
|
+
const planContentEntries = entries.filter(
|
|
522
|
+
(e) => e.type === "user" && e.message?.role === "user" && !!e.planContent && typeof e.message?.content === "string"
|
|
523
|
+
).map((e) => ({
|
|
524
|
+
timestamp: e.timestamp,
|
|
525
|
+
content: e.message?.content
|
|
526
|
+
}));
|
|
521
527
|
const userMessages = entries.filter((e) => {
|
|
522
528
|
if (e.type !== "user" || e.message?.role !== "user") return false;
|
|
523
529
|
if (e.isMeta === true) return false;
|
|
530
|
+
if (e.planContent) return false;
|
|
524
531
|
const content = e.message?.content;
|
|
525
532
|
if (typeof content !== "string") return false;
|
|
526
533
|
if (content.startsWith("<local-command-stdout>")) return false;
|
|
@@ -531,7 +538,7 @@ async function parseTranscriptIncremental(transcriptPath, lastSavedLine) {
|
|
|
531
538
|
return {
|
|
532
539
|
timestamp: e.timestamp,
|
|
533
540
|
content,
|
|
534
|
-
isCompactSummary: e.isCompactSummary ||
|
|
541
|
+
isCompactSummary: e.isCompactSummary || false,
|
|
535
542
|
slashCommand: extractSlashCommand(content)
|
|
536
543
|
};
|
|
537
544
|
});
|
|
@@ -581,7 +588,8 @@ async function parseTranscriptIncremental(transcriptPath, lastSavedLine) {
|
|
|
581
588
|
const orphanedResponses = assistantMessages.filter(
|
|
582
589
|
(a) => a.timestamp < firstUserTs
|
|
583
590
|
);
|
|
584
|
-
|
|
591
|
+
const planEntry = planContentEntries.find((p) => p.timestamp <= firstUserTs);
|
|
592
|
+
if (orphanedResponses.length > 0 || planEntry) {
|
|
585
593
|
const allToolDetails = orphanedResponses.flatMap((r) => r.toolDetails);
|
|
586
594
|
const orphanedTimeKeys = new Set(
|
|
587
595
|
orphanedResponses.map((r) => r.timestamp.slice(0, 16))
|
|
@@ -593,15 +601,18 @@ async function parseTranscriptIncremental(transcriptPath, lastSavedLine) {
|
|
|
593
601
|
(k) => progressEvents.get(k) || []
|
|
594
602
|
);
|
|
595
603
|
interactions.push({
|
|
596
|
-
timestamp: orphanedResponses[0].timestamp,
|
|
597
|
-
|
|
604
|
+
timestamp: orphanedResponses.length > 0 ? orphanedResponses[0].timestamp : planEntry?.timestamp ?? "",
|
|
605
|
+
// Include plan content for compact detection (UUID extraction)
|
|
606
|
+
user: planEntry?.content || "",
|
|
598
607
|
thinking: orphanedResponses.filter((r) => r.thinking).map((r) => r.thinking).join("\n"),
|
|
599
608
|
assistant: orphanedResponses.filter((r) => r.text).map((r) => r.text).join("\n"),
|
|
600
|
-
isCompactSummary:
|
|
609
|
+
isCompactSummary: !!planEntry,
|
|
601
610
|
isContinuation: true,
|
|
602
611
|
toolsUsed: [...new Set(allToolDetails.map((t) => t.name))],
|
|
603
612
|
toolDetails: allToolDetails,
|
|
604
|
-
inPlanMode: isInPlanMode(
|
|
613
|
+
inPlanMode: isInPlanMode(
|
|
614
|
+
orphanedResponses[0]?.timestamp ?? planEntry?.timestamp ?? ""
|
|
615
|
+
) || void 0,
|
|
605
616
|
toolResults: allToolResults.length > 0 ? allToolResults : void 0,
|
|
606
617
|
progressEvents: allProgressEvents.length > 0 ? allProgressEvents : void 0
|
|
607
618
|
});
|
|
@@ -647,6 +658,50 @@ var IGNORED_PREFIXES = [
|
|
|
647
658
|
".claude/"
|
|
648
659
|
];
|
|
649
660
|
var IGNORED_FILES = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"];
|
|
661
|
+
function ensureCompactSessionLink(projectPath, currentClaudeSessionId) {
|
|
662
|
+
const mnemeDir = path4.join(projectPath, ".mneme");
|
|
663
|
+
const pendingFile = path4.join(mnemeDir, ".pending-compact.json");
|
|
664
|
+
if (!fs5.existsSync(pendingFile)) return;
|
|
665
|
+
const sessionLinksDir = path4.join(mnemeDir, "session-links");
|
|
666
|
+
const linkFile = path4.join(sessionLinksDir, `${currentClaudeSessionId}.json`);
|
|
667
|
+
if (fs5.existsSync(linkFile)) return;
|
|
668
|
+
try {
|
|
669
|
+
const pending = JSON.parse(fs5.readFileSync(pendingFile, "utf8"));
|
|
670
|
+
const oldClaudeSessionId = pending.claudeSessionId || "";
|
|
671
|
+
const timestamp = pending.timestamp || "";
|
|
672
|
+
if (timestamp) {
|
|
673
|
+
const age = Date.now() - new Date(timestamp).getTime();
|
|
674
|
+
if (age > 5 * 60 * 1e3) return;
|
|
675
|
+
}
|
|
676
|
+
if (!oldClaudeSessionId || oldClaudeSessionId === currentClaudeSessionId) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
const masterSessionId = resolveMnemeSessionId(
|
|
680
|
+
projectPath,
|
|
681
|
+
oldClaudeSessionId
|
|
682
|
+
);
|
|
683
|
+
if (!fs5.existsSync(sessionLinksDir)) {
|
|
684
|
+
fs5.mkdirSync(sessionLinksDir, { recursive: true });
|
|
685
|
+
}
|
|
686
|
+
fs5.writeFileSync(
|
|
687
|
+
linkFile,
|
|
688
|
+
JSON.stringify(
|
|
689
|
+
{
|
|
690
|
+
masterSessionId,
|
|
691
|
+
claudeSessionId: currentClaudeSessionId,
|
|
692
|
+
linkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
693
|
+
},
|
|
694
|
+
null,
|
|
695
|
+
2
|
|
696
|
+
)
|
|
697
|
+
);
|
|
698
|
+
console.error(
|
|
699
|
+
`[mneme] Save: compact continuation linked: ${currentClaudeSessionId} \u2192 ${masterSessionId}`
|
|
700
|
+
);
|
|
701
|
+
} catch (e) {
|
|
702
|
+
console.error(`[mneme] Error in ensureCompactSessionLink: ${e}`);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
650
705
|
function detectCompactContinuation(interactions, projectPath) {
|
|
651
706
|
const compactInteraction = interactions.find((i) => i.isCompactSummary);
|
|
652
707
|
if (!compactInteraction?.user) return null;
|
|
@@ -753,6 +808,7 @@ async function incrementalSave(claudeSessionId, transcriptPath, projectPath) {
|
|
|
753
808
|
message: `Transcript not found: ${transcriptPath}`
|
|
754
809
|
};
|
|
755
810
|
}
|
|
811
|
+
ensureCompactSessionLink(projectPath, claudeSessionId);
|
|
756
812
|
const dbPath = path4.join(projectPath, ".mneme", "local.db");
|
|
757
813
|
const db = initDatabase(dbPath);
|
|
758
814
|
let mnemeSessionId = resolveMnemeSessionId(projectPath, claudeSessionId);
|
|
@@ -522,9 +522,16 @@ async function parseTranscriptIncremental(transcriptPath, lastSavedLine) {
|
|
|
522
522
|
progressEvents.get(key)?.push(event);
|
|
523
523
|
}
|
|
524
524
|
}
|
|
525
|
+
const planContentEntries = entries.filter(
|
|
526
|
+
(e) => e.type === "user" && e.message?.role === "user" && !!e.planContent && typeof e.message?.content === "string"
|
|
527
|
+
).map((e) => ({
|
|
528
|
+
timestamp: e.timestamp,
|
|
529
|
+
content: e.message?.content
|
|
530
|
+
}));
|
|
525
531
|
const userMessages = entries.filter((e) => {
|
|
526
532
|
if (e.type !== "user" || e.message?.role !== "user") return false;
|
|
527
533
|
if (e.isMeta === true) return false;
|
|
534
|
+
if (e.planContent) return false;
|
|
528
535
|
const content = e.message?.content;
|
|
529
536
|
if (typeof content !== "string") return false;
|
|
530
537
|
if (content.startsWith("<local-command-stdout>")) return false;
|
|
@@ -535,7 +542,7 @@ async function parseTranscriptIncremental(transcriptPath, lastSavedLine) {
|
|
|
535
542
|
return {
|
|
536
543
|
timestamp: e.timestamp,
|
|
537
544
|
content,
|
|
538
|
-
isCompactSummary: e.isCompactSummary ||
|
|
545
|
+
isCompactSummary: e.isCompactSummary || false,
|
|
539
546
|
slashCommand: extractSlashCommand(content)
|
|
540
547
|
};
|
|
541
548
|
});
|
|
@@ -585,7 +592,8 @@ async function parseTranscriptIncremental(transcriptPath, lastSavedLine) {
|
|
|
585
592
|
const orphanedResponses = assistantMessages.filter(
|
|
586
593
|
(a) => a.timestamp < firstUserTs
|
|
587
594
|
);
|
|
588
|
-
|
|
595
|
+
const planEntry = planContentEntries.find((p) => p.timestamp <= firstUserTs);
|
|
596
|
+
if (orphanedResponses.length > 0 || planEntry) {
|
|
589
597
|
const allToolDetails = orphanedResponses.flatMap((r) => r.toolDetails);
|
|
590
598
|
const orphanedTimeKeys = new Set(
|
|
591
599
|
orphanedResponses.map((r) => r.timestamp.slice(0, 16))
|
|
@@ -597,15 +605,18 @@ async function parseTranscriptIncremental(transcriptPath, lastSavedLine) {
|
|
|
597
605
|
(k) => progressEvents.get(k) || []
|
|
598
606
|
);
|
|
599
607
|
interactions.push({
|
|
600
|
-
timestamp: orphanedResponses[0].timestamp,
|
|
601
|
-
|
|
608
|
+
timestamp: orphanedResponses.length > 0 ? orphanedResponses[0].timestamp : planEntry?.timestamp ?? "",
|
|
609
|
+
// Include plan content for compact detection (UUID extraction)
|
|
610
|
+
user: planEntry?.content || "",
|
|
602
611
|
thinking: orphanedResponses.filter((r) => r.thinking).map((r) => r.thinking).join("\n"),
|
|
603
612
|
assistant: orphanedResponses.filter((r) => r.text).map((r) => r.text).join("\n"),
|
|
604
|
-
isCompactSummary:
|
|
613
|
+
isCompactSummary: !!planEntry,
|
|
605
614
|
isContinuation: true,
|
|
606
615
|
toolsUsed: [...new Set(allToolDetails.map((t) => t.name))],
|
|
607
616
|
toolDetails: allToolDetails,
|
|
608
|
-
inPlanMode: isInPlanMode(
|
|
617
|
+
inPlanMode: isInPlanMode(
|
|
618
|
+
orphanedResponses[0]?.timestamp ?? planEntry?.timestamp ?? ""
|
|
619
|
+
) || void 0,
|
|
609
620
|
toolResults: allToolResults.length > 0 ? allToolResults : void 0,
|
|
610
621
|
progressEvents: allProgressEvents.length > 0 ? allProgressEvents : void 0
|
|
611
622
|
});
|
|
@@ -651,6 +662,50 @@ var IGNORED_PREFIXES = [
|
|
|
651
662
|
".claude/"
|
|
652
663
|
];
|
|
653
664
|
var IGNORED_FILES = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"];
|
|
665
|
+
function ensureCompactSessionLink(projectPath, currentClaudeSessionId) {
|
|
666
|
+
const mnemeDir = path4.join(projectPath, ".mneme");
|
|
667
|
+
const pendingFile = path4.join(mnemeDir, ".pending-compact.json");
|
|
668
|
+
if (!fs5.existsSync(pendingFile)) return;
|
|
669
|
+
const sessionLinksDir = path4.join(mnemeDir, "session-links");
|
|
670
|
+
const linkFile = path4.join(sessionLinksDir, `${currentClaudeSessionId}.json`);
|
|
671
|
+
if (fs5.existsSync(linkFile)) return;
|
|
672
|
+
try {
|
|
673
|
+
const pending = JSON.parse(fs5.readFileSync(pendingFile, "utf8"));
|
|
674
|
+
const oldClaudeSessionId = pending.claudeSessionId || "";
|
|
675
|
+
const timestamp = pending.timestamp || "";
|
|
676
|
+
if (timestamp) {
|
|
677
|
+
const age = Date.now() - new Date(timestamp).getTime();
|
|
678
|
+
if (age > 5 * 60 * 1e3) return;
|
|
679
|
+
}
|
|
680
|
+
if (!oldClaudeSessionId || oldClaudeSessionId === currentClaudeSessionId) {
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const masterSessionId = resolveMnemeSessionId(
|
|
684
|
+
projectPath,
|
|
685
|
+
oldClaudeSessionId
|
|
686
|
+
);
|
|
687
|
+
if (!fs5.existsSync(sessionLinksDir)) {
|
|
688
|
+
fs5.mkdirSync(sessionLinksDir, { recursive: true });
|
|
689
|
+
}
|
|
690
|
+
fs5.writeFileSync(
|
|
691
|
+
linkFile,
|
|
692
|
+
JSON.stringify(
|
|
693
|
+
{
|
|
694
|
+
masterSessionId,
|
|
695
|
+
claudeSessionId: currentClaudeSessionId,
|
|
696
|
+
linkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
697
|
+
},
|
|
698
|
+
null,
|
|
699
|
+
2
|
|
700
|
+
)
|
|
701
|
+
);
|
|
702
|
+
console.error(
|
|
703
|
+
`[mneme] Save: compact continuation linked: ${currentClaudeSessionId} \u2192 ${masterSessionId}`
|
|
704
|
+
);
|
|
705
|
+
} catch (e) {
|
|
706
|
+
console.error(`[mneme] Error in ensureCompactSessionLink: ${e}`);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
654
709
|
function detectCompactContinuation(interactions, projectPath) {
|
|
655
710
|
const compactInteraction = interactions.find((i) => i.isCompactSummary);
|
|
656
711
|
if (!compactInteraction?.user) return null;
|
|
@@ -757,6 +812,7 @@ async function incrementalSave(claudeSessionId, transcriptPath, projectPath) {
|
|
|
757
812
|
message: `Transcript not found: ${transcriptPath}`
|
|
758
813
|
};
|
|
759
814
|
}
|
|
815
|
+
ensureCompactSessionLink(projectPath, claudeSessionId);
|
|
760
816
|
const dbPath = path4.join(projectPath, ".mneme", "local.db");
|
|
761
817
|
const db = initDatabase(dbPath);
|
|
762
818
|
let mnemeSessionId = resolveMnemeSessionId(projectPath, claudeSessionId);
|