@synth-coder/memhub 0.2.3 → 0.2.5
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/.github/workflows/ci.yml +48 -12
- package/.github/workflows/release.yml +70 -0
- package/AGENTS.md +71 -73
- package/README.md +284 -195
- package/README.zh-CN.md +90 -30
- package/dist/src/cli/agents/claude-code.d.ts +5 -0
- package/dist/src/cli/agents/claude-code.d.ts.map +1 -0
- package/dist/src/cli/agents/claude-code.js +14 -0
- package/dist/src/cli/agents/claude-code.js.map +1 -0
- package/dist/src/cli/agents/cline.d.ts +5 -0
- package/dist/src/cli/agents/cline.d.ts.map +1 -0
- package/dist/src/cli/agents/cline.js +14 -0
- package/dist/src/cli/agents/cline.js.map +1 -0
- package/dist/src/cli/agents/cursor.d.ts +5 -0
- package/dist/src/cli/agents/cursor.d.ts.map +1 -0
- package/dist/src/cli/agents/cursor.js +14 -0
- package/dist/src/cli/agents/cursor.js.map +1 -0
- package/dist/src/cli/agents/factory-droid.d.ts +5 -0
- package/dist/src/cli/agents/factory-droid.d.ts.map +1 -0
- package/dist/src/cli/agents/factory-droid.js +14 -0
- package/dist/src/cli/agents/factory-droid.js.map +1 -0
- package/dist/src/cli/agents/gemini-cli.d.ts +5 -0
- package/dist/src/cli/agents/gemini-cli.d.ts.map +1 -0
- package/dist/src/cli/agents/gemini-cli.js +14 -0
- package/dist/src/cli/agents/gemini-cli.js.map +1 -0
- package/dist/src/cli/agents/index.d.ts +13 -0
- package/dist/src/cli/agents/index.d.ts.map +1 -0
- package/dist/src/cli/agents/index.js +27 -0
- package/dist/src/cli/agents/index.js.map +1 -0
- package/dist/src/cli/agents/windsurf.d.ts +5 -0
- package/dist/src/cli/agents/windsurf.d.ts.map +1 -0
- package/dist/src/cli/agents/windsurf.js +14 -0
- package/dist/src/cli/agents/windsurf.js.map +1 -0
- package/dist/src/cli/index.d.ts +8 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +168 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/init.d.ts +34 -0
- package/dist/src/cli/init.d.ts.map +1 -0
- package/dist/src/cli/init.js +140 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/instructions.d.ts +29 -0
- package/dist/src/cli/instructions.d.ts.map +1 -0
- package/dist/src/cli/instructions.js +141 -0
- package/dist/src/cli/instructions.js.map +1 -0
- package/dist/src/cli/types.d.ts +22 -0
- package/dist/src/cli/types.d.ts.map +1 -0
- package/dist/src/cli/types.js +75 -0
- package/dist/src/cli/types.js.map +1 -0
- package/dist/src/contracts/mcp.js +34 -34
- package/dist/src/contracts/schemas.js.map +1 -1
- package/dist/src/server/mcp-server.d.ts.map +1 -1
- package/dist/src/server/mcp-server.js +7 -14
- package/dist/src/server/mcp-server.js.map +1 -1
- package/dist/src/services/embedding-service.d.ts.map +1 -1
- package/dist/src/services/embedding-service.js +1 -1
- package/dist/src/services/embedding-service.js.map +1 -1
- package/dist/src/services/memory-service.d.ts.map +1 -1
- package/dist/src/services/memory-service.js.map +1 -1
- package/dist/src/storage/markdown-storage.d.ts.map +1 -1
- package/dist/src/storage/markdown-storage.js +1 -1
- package/dist/src/storage/markdown-storage.js.map +1 -1
- package/dist/src/storage/vector-index.d.ts.map +1 -1
- package/dist/src/storage/vector-index.js +4 -5
- package/dist/src/storage/vector-index.js.map +1 -1
- package/docs/README.md +21 -0
- package/docs/mcp-tools.md +136 -0
- package/docs/user-guide.md +184 -0
- package/package.json +61 -59
- package/src/cli/agents/claude-code.ts +14 -0
- package/src/cli/agents/cline.ts +14 -0
- package/src/cli/agents/codex.ts +14 -0
- package/src/cli/agents/cursor.ts +14 -0
- package/src/cli/agents/factory-droid.ts +14 -0
- package/src/cli/agents/gemini-cli.ts +14 -0
- package/src/cli/agents/index.ts +36 -0
- package/src/cli/agents/windsurf.ts +14 -0
- package/src/cli/index.ts +192 -0
- package/src/cli/init.ts +218 -0
- package/src/cli/instructions.ts +156 -0
- package/src/cli/types.ts +112 -0
- package/src/contracts/index.ts +12 -12
- package/src/contracts/mcp.ts +223 -223
- package/src/contracts/schemas.ts +307 -307
- package/src/contracts/types.ts +410 -410
- package/src/index.ts +8 -8
- package/src/server/index.ts +5 -5
- package/src/server/mcp-server.ts +169 -186
- package/src/services/embedding-service.ts +114 -114
- package/src/services/index.ts +5 -5
- package/src/services/memory-service.ts +656 -663
- package/src/storage/frontmatter-parser.ts +243 -243
- package/src/storage/index.ts +6 -6
- package/src/storage/markdown-storage.ts +228 -236
- package/src/storage/vector-index.ts +159 -160
- package/src/utils/index.ts +5 -5
- package/src/utils/slugify.ts +63 -63
- package/test/cli/init.test.ts +402 -0
- package/test/contracts/schemas.test.ts +313 -313
- package/test/contracts/types.test.ts +21 -21
- package/test/frontmatter-parser-more.test.ts +94 -94
- package/test/server/mcp-server.test.ts +211 -210
- package/test/services/memory-service-edge.test.ts +248 -248
- package/test/services/memory-service.test.ts +291 -279
- package/test/storage/frontmatter-parser.test.ts +223 -223
- package/test/storage/markdown-storage.test.ts +226 -217
- package/test/storage/storage-edge.test.ts +238 -238
- package/test/storage/vector-index.test.ts +149 -153
- package/test/utils/slugify-edge.test.ts +94 -94
- package/test/utils/slugify.test.ts +72 -68
- package/docs/architecture-diagrams.md +0 -368
- package/docs/architecture.md +0 -381
- package/docs/contracts.md +0 -190
- package/docs/prompt-template.md +0 -33
- package/docs/proposals/mcp-typescript-sdk-refactor.md +0 -568
- package/docs/proposals/proposal-close-gates.md +0 -58
- package/docs/tool-calling-policy.md +0 -101
- package/docs/vector-search.md +0 -306
|
@@ -1,223 +1,223 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FrontMatter Parser Tests
|
|
3
|
-
* Tests for the FrontMatter parser
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, it, expect } from 'vitest';
|
|
7
|
-
import {
|
|
8
|
-
parseFrontMatter,
|
|
9
|
-
stringifyFrontMatter,
|
|
10
|
-
memoryToFrontMatter,
|
|
11
|
-
frontMatterToMemory,
|
|
12
|
-
FrontMatterError,
|
|
13
|
-
} from '../../src/storage/frontmatter-parser.js';
|
|
14
|
-
import type { Memory, MemoryFrontMatter } from '../../src/contracts/types.js';
|
|
15
|
-
|
|
16
|
-
describe('parseFrontMatter', () => {
|
|
17
|
-
it('should parse valid front matter and content', () => {
|
|
18
|
-
const markdown = `---
|
|
19
|
-
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
20
|
-
created_at: "2024-03-15T10:30:00Z"
|
|
21
|
-
updated_at: "2024-03-15T14:20:00Z"
|
|
22
|
-
tags:
|
|
23
|
-
- project
|
|
24
|
-
- meeting
|
|
25
|
-
category: "work"
|
|
26
|
-
importance: 4
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
# Project Meeting
|
|
30
|
-
|
|
31
|
-
This is the meeting content.
|
|
32
|
-
|
|
33
|
-
## Action Items
|
|
34
|
-
|
|
35
|
-
- Item 1
|
|
36
|
-
- Item 2
|
|
37
|
-
`;
|
|
38
|
-
|
|
39
|
-
const result = parseFrontMatter(markdown);
|
|
40
|
-
expect(result.frontMatter.id).toBe('550e8400-e29b-41d4-a716-446655440000');
|
|
41
|
-
expect(result.frontMatter.category).toBe('work');
|
|
42
|
-
expect(result.frontMatter.importance).toBe(4);
|
|
43
|
-
expect(result.title).toBe('Project Meeting');
|
|
44
|
-
expect(result.content).toContain('Action Items');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should parse front matter with empty tags', () => {
|
|
48
|
-
const markdown = `---
|
|
49
|
-
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
50
|
-
created_at: "2024-03-15T10:30:00Z"
|
|
51
|
-
updated_at: "2024-03-15T10:30:00Z"
|
|
52
|
-
tags: []
|
|
53
|
-
category: "general"
|
|
54
|
-
importance: 3
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
# Empty Tags Test
|
|
58
|
-
|
|
59
|
-
Content here.
|
|
60
|
-
`;
|
|
61
|
-
|
|
62
|
-
const result = parseFrontMatter(markdown);
|
|
63
|
-
expect(result.frontMatter.tags).toEqual([]);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should throw error for missing front matter', () => {
|
|
67
|
-
const markdown = `# No Front Matter
|
|
68
|
-
|
|
69
|
-
This markdown has no front matter.
|
|
70
|
-
`;
|
|
71
|
-
|
|
72
|
-
expect(() => parseFrontMatter(markdown)).toThrow(FrontMatterError);
|
|
73
|
-
expect(() => parseFrontMatter(markdown)).toThrow('Missing front matter delimiter');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should throw error for invalid YAML', () => {
|
|
77
|
-
const markdown = `---
|
|
78
|
-
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
79
|
-
created_at: [invalid yaml
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
# Title
|
|
83
|
-
|
|
84
|
-
Content.
|
|
85
|
-
`;
|
|
86
|
-
|
|
87
|
-
expect(() => parseFrontMatter(markdown)).toThrow(FrontMatterError);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should throw error for missing required fields', () => {
|
|
91
|
-
const markdown = `---
|
|
92
|
-
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
# Title
|
|
96
|
-
|
|
97
|
-
Content.
|
|
98
|
-
`;
|
|
99
|
-
|
|
100
|
-
expect(() => parseFrontMatter(markdown)).toThrow(FrontMatterError);
|
|
101
|
-
expect(() => parseFrontMatter(markdown)).toThrow('Missing required fields');
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('should handle multiline content correctly', () => {
|
|
105
|
-
const markdown = `---
|
|
106
|
-
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
107
|
-
created_at: "2024-03-15T10:30:00Z"
|
|
108
|
-
updated_at: "2024-03-15T10:30:00Z"
|
|
109
|
-
tags: []
|
|
110
|
-
category: "general"
|
|
111
|
-
importance: 3
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
# Title
|
|
115
|
-
|
|
116
|
-
Line 1
|
|
117
|
-
Line 2
|
|
118
|
-
|
|
119
|
-
Line 3 after blank
|
|
120
|
-
`;
|
|
121
|
-
|
|
122
|
-
const result = parseFrontMatter(markdown);
|
|
123
|
-
expect(result.content).toContain('Line 1');
|
|
124
|
-
expect(result.content).toContain('Line 3 after blank');
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
describe('stringifyFrontMatter', () => {
|
|
129
|
-
const frontMatter: MemoryFrontMatter = {
|
|
130
|
-
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
131
|
-
created_at: '2024-03-15T10:30:00Z',
|
|
132
|
-
updated_at: '2024-03-15T14:20:00Z',
|
|
133
|
-
tags: ['project', 'meeting'],
|
|
134
|
-
category: 'work',
|
|
135
|
-
importance: 4,
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
it('should stringify front matter and content', () => {
|
|
139
|
-
const result = stringifyFrontMatter(frontMatter, 'Title', 'Content here');
|
|
140
|
-
expect(result).toContain('---');
|
|
141
|
-
expect(result).toContain('id: "550e8400-e29b-41d4-a716-446655440000"');
|
|
142
|
-
expect(result).toContain('# Title');
|
|
143
|
-
expect(result).toContain('Content here');
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should format tags as YAML array', () => {
|
|
147
|
-
const result = stringifyFrontMatter(frontMatter, 'Title', 'Content');
|
|
148
|
-
expect(result).toContain('tags:');
|
|
149
|
-
expect(result).toContain('project');
|
|
150
|
-
expect(result).toContain('meeting');
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should use LF line endings', () => {
|
|
154
|
-
const result = stringifyFrontMatter(frontMatter, 'Title', 'Content');
|
|
155
|
-
expect(result).not.toContain('\r\n');
|
|
156
|
-
expect(result).toContain('\n');
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('should add blank line between front matter and content', () => {
|
|
160
|
-
const result = stringifyFrontMatter(frontMatter, 'Title', 'Content');
|
|
161
|
-
expect(result).toMatch(/---\n\n# Title/);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('should handle empty tags', () => {
|
|
165
|
-
const fmWithEmptyTags: MemoryFrontMatter = { ...frontMatter, tags: [] };
|
|
166
|
-
const result = stringifyFrontMatter(fmWithEmptyTags, 'Title', 'Content');
|
|
167
|
-
expect(result).toContain('tags: []');
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should handle multiline content', () => {
|
|
171
|
-
const content = 'Line 1\n\nLine 2\n\nLine 3';
|
|
172
|
-
const result = stringifyFrontMatter(frontMatter, 'Title', content);
|
|
173
|
-
expect(result).toContain('Line 1');
|
|
174
|
-
expect(result).toContain('Line 2');
|
|
175
|
-
expect(result).toContain('Line 3');
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
describe('memoryToFrontMatter', () => {
|
|
180
|
-
it('should convert Memory to MemoryFrontMatter', () => {
|
|
181
|
-
const memory: Memory = {
|
|
182
|
-
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
183
|
-
createdAt: '2024-03-15T10:30:00Z',
|
|
184
|
-
updatedAt: '2024-03-15T14:20:00Z',
|
|
185
|
-
tags: ['test'],
|
|
186
|
-
category: 'work',
|
|
187
|
-
importance: 3,
|
|
188
|
-
title: 'Test',
|
|
189
|
-
content: 'Content',
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const result = memoryToFrontMatter(memory);
|
|
193
|
-
expect(result.id).toBe(memory.id);
|
|
194
|
-
expect(result.created_at).toBe(memory.createdAt);
|
|
195
|
-
expect(result.updated_at).toBe(memory.updatedAt);
|
|
196
|
-
expect(result.tags).toEqual(memory.tags);
|
|
197
|
-
expect(result.category).toBe(memory.category);
|
|
198
|
-
expect(result.importance).toBe(memory.importance);
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
describe('frontMatterToMemory', () => {
|
|
203
|
-
it('should convert MemoryFrontMatter to Memory', () => {
|
|
204
|
-
const fm: MemoryFrontMatter = {
|
|
205
|
-
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
206
|
-
created_at: '2024-03-15T10:30:00Z',
|
|
207
|
-
updated_at: '2024-03-15T14:20:00Z',
|
|
208
|
-
tags: ['test'],
|
|
209
|
-
category: 'work',
|
|
210
|
-
importance: 3,
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const result = frontMatterToMemory(fm, 'Title', 'Content');
|
|
214
|
-
expect(result.id).toBe(fm.id);
|
|
215
|
-
expect(result.createdAt).toBe(fm.created_at);
|
|
216
|
-
expect(result.updatedAt).toBe(fm.updated_at);
|
|
217
|
-
expect(result.tags).toEqual(fm.tags);
|
|
218
|
-
expect(result.category).toBe(fm.category);
|
|
219
|
-
expect(result.importance).toBe(fm.importance);
|
|
220
|
-
expect(result.title).toBe('Title');
|
|
221
|
-
expect(result.content).toBe('Content');
|
|
222
|
-
});
|
|
223
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* FrontMatter Parser Tests
|
|
3
|
+
* Tests for the FrontMatter parser
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect } from 'vitest';
|
|
7
|
+
import {
|
|
8
|
+
parseFrontMatter,
|
|
9
|
+
stringifyFrontMatter,
|
|
10
|
+
memoryToFrontMatter,
|
|
11
|
+
frontMatterToMemory,
|
|
12
|
+
FrontMatterError,
|
|
13
|
+
} from '../../src/storage/frontmatter-parser.js';
|
|
14
|
+
import type { Memory, MemoryFrontMatter } from '../../src/contracts/types.js';
|
|
15
|
+
|
|
16
|
+
describe('parseFrontMatter', () => {
|
|
17
|
+
it('should parse valid front matter and content', () => {
|
|
18
|
+
const markdown = `---
|
|
19
|
+
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
20
|
+
created_at: "2024-03-15T10:30:00Z"
|
|
21
|
+
updated_at: "2024-03-15T14:20:00Z"
|
|
22
|
+
tags:
|
|
23
|
+
- project
|
|
24
|
+
- meeting
|
|
25
|
+
category: "work"
|
|
26
|
+
importance: 4
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
# Project Meeting
|
|
30
|
+
|
|
31
|
+
This is the meeting content.
|
|
32
|
+
|
|
33
|
+
## Action Items
|
|
34
|
+
|
|
35
|
+
- Item 1
|
|
36
|
+
- Item 2
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const result = parseFrontMatter(markdown);
|
|
40
|
+
expect(result.frontMatter.id).toBe('550e8400-e29b-41d4-a716-446655440000');
|
|
41
|
+
expect(result.frontMatter.category).toBe('work');
|
|
42
|
+
expect(result.frontMatter.importance).toBe(4);
|
|
43
|
+
expect(result.title).toBe('Project Meeting');
|
|
44
|
+
expect(result.content).toContain('Action Items');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should parse front matter with empty tags', () => {
|
|
48
|
+
const markdown = `---
|
|
49
|
+
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
50
|
+
created_at: "2024-03-15T10:30:00Z"
|
|
51
|
+
updated_at: "2024-03-15T10:30:00Z"
|
|
52
|
+
tags: []
|
|
53
|
+
category: "general"
|
|
54
|
+
importance: 3
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
# Empty Tags Test
|
|
58
|
+
|
|
59
|
+
Content here.
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const result = parseFrontMatter(markdown);
|
|
63
|
+
expect(result.frontMatter.tags).toEqual([]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should throw error for missing front matter', () => {
|
|
67
|
+
const markdown = `# No Front Matter
|
|
68
|
+
|
|
69
|
+
This markdown has no front matter.
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
expect(() => parseFrontMatter(markdown)).toThrow(FrontMatterError);
|
|
73
|
+
expect(() => parseFrontMatter(markdown)).toThrow('Missing front matter delimiter');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should throw error for invalid YAML', () => {
|
|
77
|
+
const markdown = `---
|
|
78
|
+
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
79
|
+
created_at: [invalid yaml
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
# Title
|
|
83
|
+
|
|
84
|
+
Content.
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
expect(() => parseFrontMatter(markdown)).toThrow(FrontMatterError);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should throw error for missing required fields', () => {
|
|
91
|
+
const markdown = `---
|
|
92
|
+
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
# Title
|
|
96
|
+
|
|
97
|
+
Content.
|
|
98
|
+
`;
|
|
99
|
+
|
|
100
|
+
expect(() => parseFrontMatter(markdown)).toThrow(FrontMatterError);
|
|
101
|
+
expect(() => parseFrontMatter(markdown)).toThrow('Missing required fields');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should handle multiline content correctly', () => {
|
|
105
|
+
const markdown = `---
|
|
106
|
+
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
107
|
+
created_at: "2024-03-15T10:30:00Z"
|
|
108
|
+
updated_at: "2024-03-15T10:30:00Z"
|
|
109
|
+
tags: []
|
|
110
|
+
category: "general"
|
|
111
|
+
importance: 3
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
# Title
|
|
115
|
+
|
|
116
|
+
Line 1
|
|
117
|
+
Line 2
|
|
118
|
+
|
|
119
|
+
Line 3 after blank
|
|
120
|
+
`;
|
|
121
|
+
|
|
122
|
+
const result = parseFrontMatter(markdown);
|
|
123
|
+
expect(result.content).toContain('Line 1');
|
|
124
|
+
expect(result.content).toContain('Line 3 after blank');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('stringifyFrontMatter', () => {
|
|
129
|
+
const frontMatter: MemoryFrontMatter = {
|
|
130
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
131
|
+
created_at: '2024-03-15T10:30:00Z',
|
|
132
|
+
updated_at: '2024-03-15T14:20:00Z',
|
|
133
|
+
tags: ['project', 'meeting'],
|
|
134
|
+
category: 'work',
|
|
135
|
+
importance: 4,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
it('should stringify front matter and content', () => {
|
|
139
|
+
const result = stringifyFrontMatter(frontMatter, 'Title', 'Content here');
|
|
140
|
+
expect(result).toContain('---');
|
|
141
|
+
expect(result).toContain('id: "550e8400-e29b-41d4-a716-446655440000"');
|
|
142
|
+
expect(result).toContain('# Title');
|
|
143
|
+
expect(result).toContain('Content here');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should format tags as YAML array', () => {
|
|
147
|
+
const result = stringifyFrontMatter(frontMatter, 'Title', 'Content');
|
|
148
|
+
expect(result).toContain('tags:');
|
|
149
|
+
expect(result).toContain('project');
|
|
150
|
+
expect(result).toContain('meeting');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should use LF line endings', () => {
|
|
154
|
+
const result = stringifyFrontMatter(frontMatter, 'Title', 'Content');
|
|
155
|
+
expect(result).not.toContain('\r\n');
|
|
156
|
+
expect(result).toContain('\n');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should add blank line between front matter and content', () => {
|
|
160
|
+
const result = stringifyFrontMatter(frontMatter, 'Title', 'Content');
|
|
161
|
+
expect(result).toMatch(/---\n\n# Title/);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should handle empty tags', () => {
|
|
165
|
+
const fmWithEmptyTags: MemoryFrontMatter = { ...frontMatter, tags: [] };
|
|
166
|
+
const result = stringifyFrontMatter(fmWithEmptyTags, 'Title', 'Content');
|
|
167
|
+
expect(result).toContain('tags: []');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should handle multiline content', () => {
|
|
171
|
+
const content = 'Line 1\n\nLine 2\n\nLine 3';
|
|
172
|
+
const result = stringifyFrontMatter(frontMatter, 'Title', content);
|
|
173
|
+
expect(result).toContain('Line 1');
|
|
174
|
+
expect(result).toContain('Line 2');
|
|
175
|
+
expect(result).toContain('Line 3');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('memoryToFrontMatter', () => {
|
|
180
|
+
it('should convert Memory to MemoryFrontMatter', () => {
|
|
181
|
+
const memory: Memory = {
|
|
182
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
183
|
+
createdAt: '2024-03-15T10:30:00Z',
|
|
184
|
+
updatedAt: '2024-03-15T14:20:00Z',
|
|
185
|
+
tags: ['test'],
|
|
186
|
+
category: 'work',
|
|
187
|
+
importance: 3,
|
|
188
|
+
title: 'Test',
|
|
189
|
+
content: 'Content',
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const result = memoryToFrontMatter(memory);
|
|
193
|
+
expect(result.id).toBe(memory.id);
|
|
194
|
+
expect(result.created_at).toBe(memory.createdAt);
|
|
195
|
+
expect(result.updated_at).toBe(memory.updatedAt);
|
|
196
|
+
expect(result.tags).toEqual(memory.tags);
|
|
197
|
+
expect(result.category).toBe(memory.category);
|
|
198
|
+
expect(result.importance).toBe(memory.importance);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('frontMatterToMemory', () => {
|
|
203
|
+
it('should convert MemoryFrontMatter to Memory', () => {
|
|
204
|
+
const fm: MemoryFrontMatter = {
|
|
205
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
206
|
+
created_at: '2024-03-15T10:30:00Z',
|
|
207
|
+
updated_at: '2024-03-15T14:20:00Z',
|
|
208
|
+
tags: ['test'],
|
|
209
|
+
category: 'work',
|
|
210
|
+
importance: 3,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const result = frontMatterToMemory(fm, 'Title', 'Content');
|
|
214
|
+
expect(result.id).toBe(fm.id);
|
|
215
|
+
expect(result.createdAt).toBe(fm.created_at);
|
|
216
|
+
expect(result.updatedAt).toBe(fm.updated_at);
|
|
217
|
+
expect(result.tags).toEqual(fm.tags);
|
|
218
|
+
expect(result.category).toBe(fm.category);
|
|
219
|
+
expect(result.importance).toBe(fm.importance);
|
|
220
|
+
expect(result.title).toBe('Title');
|
|
221
|
+
expect(result.content).toBe('Content');
|
|
222
|
+
});
|
|
223
|
+
});
|