persisted-memory 1.0.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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +281 -0
  3. package/dist/cli/generate-summary.d.ts +1 -0
  4. package/dist/cli/generate-summary.js +13 -0
  5. package/dist/cli/generate-summary.js.map +1 -0
  6. package/dist/cli/viewer.d.ts +1 -0
  7. package/dist/cli/viewer.js +4 -0
  8. package/dist/cli/viewer.js.map +1 -0
  9. package/dist/embeddings/ollama.d.ts +3 -0
  10. package/dist/embeddings/ollama.js +63 -0
  11. package/dist/embeddings/ollama.js.map +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +25 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/search/hybrid.d.ts +8 -0
  16. package/dist/search/hybrid.js +54 -0
  17. package/dist/search/hybrid.js.map +1 -0
  18. package/dist/server.d.ts +2 -0
  19. package/dist/server.js +399 -0
  20. package/dist/server.js.map +1 -0
  21. package/dist/storage/knowledge-graph.d.ts +32 -0
  22. package/dist/storage/knowledge-graph.js +259 -0
  23. package/dist/storage/knowledge-graph.js.map +1 -0
  24. package/dist/storage/lance-store.d.ts +21 -0
  25. package/dist/storage/lance-store.js +288 -0
  26. package/dist/storage/lance-store.js.map +1 -0
  27. package/dist/storage/markdown-store.d.ts +7 -0
  28. package/dist/storage/markdown-store.js +63 -0
  29. package/dist/storage/markdown-store.js.map +1 -0
  30. package/dist/storage/types.d.ts +19 -0
  31. package/dist/storage/types.js +13 -0
  32. package/dist/storage/types.js.map +1 -0
  33. package/dist/utils/chunking.d.ts +1 -0
  34. package/dist/utils/chunking.js +55 -0
  35. package/dist/utils/chunking.js.map +1 -0
  36. package/dist/utils/privacy.d.ts +13 -0
  37. package/dist/utils/privacy.js +23 -0
  38. package/dist/utils/privacy.js.map +1 -0
  39. package/dist/utils/project.d.ts +3 -0
  40. package/dist/utils/project.js +11 -0
  41. package/dist/utils/project.js.map +1 -0
  42. package/dist/utils/summarize.d.ts +12 -0
  43. package/dist/utils/summarize.js +123 -0
  44. package/dist/utils/summarize.js.map +1 -0
  45. package/dist/viewer/index.html +328 -0
  46. package/dist/viewer/server.d.ts +1 -0
  47. package/dist/viewer/server.js +203 -0
  48. package/dist/viewer/server.js.map +1 -0
  49. package/hooks/on-post-tool.sh +17 -0
  50. package/hooks/on-pre-compact.sh +20 -0
  51. package/hooks/on-session-end.sh +46 -0
  52. package/hooks/on-session-start.sh +14 -0
  53. package/hooks/on-stop.sh +6 -0
  54. package/package.json +60 -0
  55. package/scripts/install.sh +125 -0
  56. package/scripts/uninstall.sh +59 -0
@@ -0,0 +1,63 @@
1
+ import { mkdirSync, readFileSync, writeFileSync, appendFileSync, existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { stripPrivate } from "../utils/privacy.js";
4
+ export function ensureDirs(memoryDir) {
5
+ mkdirSync(join(memoryDir, "daily"), { recursive: true });
6
+ mkdirSync(join(memoryDir, "sessions"), { recursive: true });
7
+ }
8
+ export function appendDaily(memoryDir, content) {
9
+ ensureDirs(memoryDir);
10
+ const date = new Date().toISOString().slice(0, 10);
11
+ const filePath = join(memoryDir, "daily", `${date}.md`);
12
+ const timestamp = new Date().toISOString().slice(11, 19);
13
+ const safeContent = stripPrivate(content);
14
+ appendFileSync(filePath, `\n### ${timestamp}\n\n${safeContent}\n`);
15
+ }
16
+ export function appendDecision(memoryDir, decision) {
17
+ ensureDirs(memoryDir);
18
+ const filePath = join(memoryDir, "decisions.md");
19
+ const timestamp = new Date().toISOString();
20
+ if (!existsSync(filePath)) {
21
+ writeFileSync(filePath, "# Decisions Log\n\n");
22
+ }
23
+ const safeDecision = stripPrivate(decision);
24
+ appendFileSync(filePath, `## ${timestamp}\n\n${safeDecision}\n\n---\n\n`);
25
+ }
26
+ export function writeSession(memoryDir, sessionId, content) {
27
+ ensureDirs(memoryDir);
28
+ const now = new Date();
29
+ const filename = `${now.toISOString().slice(0, 10)}-${now
30
+ .toISOString()
31
+ .slice(11, 16)
32
+ .replace(":", "-")}.md`;
33
+ const filePath = join(memoryDir, "sessions", filename);
34
+ writeFileSync(filePath, `# Session: ${sessionId}\n\n_Archived: ${now.toISOString()}_\n\n${content}\n`);
35
+ }
36
+ export function writeSummary(memoryDir, content) {
37
+ ensureDirs(memoryDir);
38
+ const filePath = join(memoryDir, "SUMMARY.md");
39
+ writeFileSync(filePath, content);
40
+ }
41
+ export function readSummary(memoryDir) {
42
+ const filePath = join(memoryDir, "SUMMARY.md");
43
+ try {
44
+ return readFileSync(filePath, "utf-8");
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
50
+ export function readAllDecisions(memoryDir) {
51
+ const filePath = join(memoryDir, "decisions.md");
52
+ try {
53
+ const content = readFileSync(filePath, "utf-8");
54
+ return content
55
+ .split(/^---$/m)
56
+ .map((s) => s.trim())
57
+ .filter((s) => s.length > 0 && s !== "# Decisions Log");
58
+ }
59
+ catch {
60
+ return [];
61
+ }
62
+ }
63
+ //# sourceMappingURL=markdown-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-store.js","sourceRoot":"","sources":["../../src/storage/markdown-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export declare const MEMORY_TYPES: readonly ["decision", "architecture", "code_change", "error_fix", "pattern", "context", "preference"];
2
+ export type MemoryType = typeof MEMORY_TYPES[number];
3
+ export declare const IMPORTANCE_LEVELS: readonly ["low", "medium", "high", "critical"];
4
+ export type Importance = typeof IMPORTANCE_LEVELS[number];
5
+ export interface MemoryEntry {
6
+ id: string;
7
+ text: string;
8
+ type: MemoryType;
9
+ importance: Importance;
10
+ timestamp: number;
11
+ session_id?: string;
12
+ file_paths?: string[];
13
+ tags?: string[];
14
+ }
15
+ export interface MemoryRow extends MemoryEntry {
16
+ vector: number[];
17
+ }
18
+ export declare const VECTOR_DIM = 768;
19
+ export declare const MEMORY_TABLE = "memories";
@@ -0,0 +1,13 @@
1
+ export const MEMORY_TYPES = [
2
+ "decision",
3
+ "architecture",
4
+ "code_change",
5
+ "error_fix",
6
+ "pattern",
7
+ "context",
8
+ "preference",
9
+ ];
10
+ export const IMPORTANCE_LEVELS = ["low", "medium", "high", "critical"];
11
+ export const VECTOR_DIM = 768;
12
+ export const MEMORY_TABLE = "memories";
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,UAAU;IACV,cAAc;IACd,aAAa;IACb,WAAW;IACX,SAAS;IACT,SAAS;IACT,YAAY;CACJ,CAAC;AAIX,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAU,CAAC;AAmBhF,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,CAAC;AAC9B,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function chunkText(text: string, maxChars?: number, overlapChars?: number): string[];
@@ -0,0 +1,55 @@
1
+ const DEFAULT_MAX_CHARS = 1600;
2
+ const DEFAULT_OVERLAP_CHARS = 320;
3
+ export function chunkText(text, maxChars = DEFAULT_MAX_CHARS, overlapChars = DEFAULT_OVERLAP_CHARS) {
4
+ if (text.length <= maxChars)
5
+ return [text];
6
+ const paragraphs = text.split(/\n\n+/);
7
+ const chunks = [];
8
+ let current = "";
9
+ for (const para of paragraphs) {
10
+ if (para.length > maxChars) {
11
+ // Flush current buffer
12
+ if (current.trim()) {
13
+ chunks.push(current.trim());
14
+ current = "";
15
+ }
16
+ // Split long paragraph by sentence boundaries
17
+ const sentences = splitSentences(para);
18
+ let sentBuf = "";
19
+ for (const sent of sentences) {
20
+ if (sentBuf.length + sent.length + 1 > maxChars) {
21
+ if (sentBuf.trim())
22
+ chunks.push(sentBuf.trim());
23
+ // Overlap: keep tail of previous chunk
24
+ sentBuf = overlapChars > 0
25
+ ? sentBuf.slice(-overlapChars) + " " + sent
26
+ : sent;
27
+ }
28
+ else {
29
+ sentBuf += (sentBuf ? " " : "") + sent;
30
+ }
31
+ }
32
+ if (sentBuf.trim())
33
+ chunks.push(sentBuf.trim());
34
+ }
35
+ else if (current.length + para.length + 2 > maxChars) {
36
+ if (current.trim())
37
+ chunks.push(current.trim());
38
+ // Overlap: keep tail of previous chunk
39
+ current = overlapChars > 0
40
+ ? current.slice(-overlapChars) + "\n\n" + para
41
+ : para;
42
+ }
43
+ else {
44
+ current += (current ? "\n\n" : "") + para;
45
+ }
46
+ }
47
+ if (current.trim())
48
+ chunks.push(current.trim());
49
+ return chunks.length > 0 ? chunks : [text];
50
+ }
51
+ function splitSentences(text) {
52
+ const parts = text.split(/(?<=[.!?])\s+/);
53
+ return parts.filter((s) => s.trim().length > 0);
54
+ }
55
+ //# sourceMappingURL=chunking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunking.js","sourceRoot":"","sources":["../../src/utils/chunking.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Strip content between <private> tags for public-facing outputs.
3
+ * Replaces <private>...</private> blocks with "[private content redacted]"
4
+ */
5
+ export declare function stripPrivate(text: string): string;
6
+ /**
7
+ * Check if a memory contains any private content.
8
+ */
9
+ export declare function hasPrivateContent(text: string): boolean;
10
+ /**
11
+ * Extract only the private portions of text.
12
+ */
13
+ export declare function extractPrivate(text: string): string[];
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Strip content between <private> tags for public-facing outputs.
3
+ * Replaces <private>...</private> blocks with "[private content redacted]"
4
+ */
5
+ export function stripPrivate(text) {
6
+ return text.replace(/<private>[\s\S]*?<\/private>/gi, "[private content redacted]");
7
+ }
8
+ /**
9
+ * Check if a memory contains any private content.
10
+ */
11
+ export function hasPrivateContent(text) {
12
+ return /<private>[\s\S]*?<\/private>/i.test(text);
13
+ }
14
+ /**
15
+ * Extract only the private portions of text.
16
+ */
17
+ export function extractPrivate(text) {
18
+ const matches = text.match(/<private>([\s\S]*?)<\/private>/gi);
19
+ if (!matches)
20
+ return [];
21
+ return matches.map((m) => m.replace(/<\/?private>/gi, "").trim());
22
+ }
23
+ //# sourceMappingURL=privacy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privacy.js","sourceRoot":"","sources":["../../src/utils/privacy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,gCAAgC,EAAE,4BAA4B,CAAC,CAAC;AACtF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC/D,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACpE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function getProjectDir(): string;
2
+ export declare function getMemoryDir(): string;
3
+ export declare function getProjectName(): string;
@@ -0,0 +1,11 @@
1
+ import { join, basename } from "node:path";
2
+ export function getProjectDir() {
3
+ return process.env.CLAUDE_PROJECT_DIR || process.cwd();
4
+ }
5
+ export function getMemoryDir() {
6
+ return join(getProjectDir(), ".claude", "memory");
7
+ }
8
+ export function getProjectName() {
9
+ return basename(getProjectDir());
10
+ }
11
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/utils/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,IAAI,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Call Ollama's generate endpoint with the given content and prompt.
3
+ * Returns the generated text, or null if Ollama is unavailable or errors.
4
+ * Never throws.
5
+ */
6
+ export declare function summarize(content: string, prompt: string): Promise<string | null>;
7
+ /**
8
+ * Generate a summary of the current memory directory contents.
9
+ * Tries AI-powered summarization via Ollama first, then falls back
10
+ * to the tail-based approach if Ollama is unavailable.
11
+ */
12
+ export declare function generateSummary(memoryDir: string): Promise<string>;
@@ -0,0 +1,123 @@
1
+ import { readFileSync, existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ const OLLAMA_URL = process.env.OLLAMA_URL || "http://localhost:11434";
4
+ const SUMMARIZE_MODEL = process.env.OLLAMA_SUMMARIZE_MODEL || "llama3.2";
5
+ const MAX_SUMMARY_CHARS = 4000;
6
+ const SUMMARIZE_TIMEOUT_MS = 30_000;
7
+ /**
8
+ * Call Ollama's generate endpoint with the given content and prompt.
9
+ * Returns the generated text, or null if Ollama is unavailable or errors.
10
+ * Never throws.
11
+ */
12
+ export async function summarize(content, prompt) {
13
+ try {
14
+ const controller = new AbortController();
15
+ const timer = setTimeout(() => controller.abort(), SUMMARIZE_TIMEOUT_MS);
16
+ const res = await fetch(`${OLLAMA_URL}/api/generate`, {
17
+ method: "POST",
18
+ headers: { "Content-Type": "application/json" },
19
+ body: JSON.stringify({
20
+ model: SUMMARIZE_MODEL,
21
+ prompt: `${prompt}\n\n${content}`,
22
+ stream: false,
23
+ }),
24
+ signal: controller.signal,
25
+ });
26
+ clearTimeout(timer);
27
+ if (!res.ok)
28
+ return null;
29
+ const data = (await res.json());
30
+ return data.response ?? null;
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ /**
37
+ * Build a fallback summary using the tail-based approach (last 1500 chars
38
+ * of decisions.md and today's daily log). This matches the original
39
+ * shell-based logic in on-session-end.sh.
40
+ */
41
+ function buildFallbackSummary(memoryDir) {
42
+ const date = new Date().toISOString().slice(0, 10);
43
+ const timestamp = new Date()
44
+ .toISOString()
45
+ .slice(0, 16)
46
+ .replace("T", " ");
47
+ let summary = "# Memory Summary\n\n";
48
+ summary += `_Auto-generated ${timestamp} — use memory_search for full context_\n\n`;
49
+ const decisionsPath = join(memoryDir, "decisions.md");
50
+ if (existsSync(decisionsPath)) {
51
+ try {
52
+ const content = readFileSync(decisionsPath, "utf-8");
53
+ const tail = content.slice(-1500);
54
+ summary += "## Recent Decisions\n\n";
55
+ summary += tail + "\n";
56
+ }
57
+ catch {
58
+ // skip if unreadable
59
+ }
60
+ }
61
+ const dailyPath = join(memoryDir, "daily", `${date}.md`);
62
+ if (existsSync(dailyPath)) {
63
+ try {
64
+ const content = readFileSync(dailyPath, "utf-8");
65
+ const tail = content.slice(-1500);
66
+ summary += "## Today's Activity\n\n";
67
+ summary += tail + "\n";
68
+ }
69
+ catch {
70
+ // skip if unreadable
71
+ }
72
+ }
73
+ return summary.slice(0, MAX_SUMMARY_CHARS);
74
+ }
75
+ /**
76
+ * Generate a summary of the current memory directory contents.
77
+ * Tries AI-powered summarization via Ollama first, then falls back
78
+ * to the tail-based approach if Ollama is unavailable.
79
+ */
80
+ export async function generateSummary(memoryDir) {
81
+ const date = new Date().toISOString().slice(0, 10);
82
+ // Gather raw content for summarization
83
+ let rawContent = "";
84
+ const decisionsPath = join(memoryDir, "decisions.md");
85
+ if (existsSync(decisionsPath)) {
86
+ try {
87
+ rawContent += readFileSync(decisionsPath, "utf-8") + "\n\n";
88
+ }
89
+ catch {
90
+ // skip if unreadable
91
+ }
92
+ }
93
+ const dailyPath = join(memoryDir, "daily", `${date}.md`);
94
+ if (existsSync(dailyPath)) {
95
+ try {
96
+ rawContent += readFileSync(dailyPath, "utf-8") + "\n\n";
97
+ }
98
+ catch {
99
+ // skip if unreadable
100
+ }
101
+ }
102
+ // If there is no content at all, return a minimal summary
103
+ if (rawContent.trim().length === 0) {
104
+ return buildFallbackSummary(memoryDir);
105
+ }
106
+ const prompt = "Summarize the following development session notes into a concise context summary. " +
107
+ "Focus on: key decisions made, important code changes, unresolved issues, and patterns discovered. " +
108
+ "Keep it under 3000 characters. Format as markdown with sections.";
109
+ const aiSummary = await summarize(rawContent, prompt);
110
+ if (aiSummary) {
111
+ const timestamp = new Date()
112
+ .toISOString()
113
+ .slice(0, 16)
114
+ .replace("T", " ");
115
+ let result = "# Memory Summary\n\n";
116
+ result += `_AI-generated ${timestamp} — use memory_search for full context_\n\n`;
117
+ result += aiSummary;
118
+ return result.slice(0, MAX_SUMMARY_CHARS);
119
+ }
120
+ // Ollama unavailable — fall back to tail-based approach
121
+ return buildFallbackSummary(memoryDir);
122
+ }
123
+ //# sourceMappingURL=summarize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summarize.js","sourceRoot":"","sources":["../../src/utils/summarize.ts"],"names":[],"mappings":"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"}