@prmichaelsen/remember-mcp 0.1.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 (95) hide show
  1. package/.env.example +65 -0
  2. package/AGENT.md +840 -0
  3. package/README.md +72 -0
  4. package/agent/design/.gitkeep +0 -0
  5. package/agent/design/access-control-result-pattern.md +458 -0
  6. package/agent/design/action-audit-memory-types.md +637 -0
  7. package/agent/design/common-template-fields.md +282 -0
  8. package/agent/design/complete-tool-set.md +407 -0
  9. package/agent/design/content-types-expansion.md +521 -0
  10. package/agent/design/cross-database-id-strategy.md +358 -0
  11. package/agent/design/default-template-library.md +423 -0
  12. package/agent/design/firestore-wrapper-analysis.md +606 -0
  13. package/agent/design/llm-provider-abstraction.md +691 -0
  14. package/agent/design/location-handling-architecture.md +523 -0
  15. package/agent/design/memory-templates-design.md +364 -0
  16. package/agent/design/permissions-storage-architecture.md +680 -0
  17. package/agent/design/relationship-storage-strategy.md +361 -0
  18. package/agent/design/remember-mcp-implementation-tasks.md +417 -0
  19. package/agent/design/remember-mcp-progress.yaml +141 -0
  20. package/agent/design/requirements-enhancements.md +468 -0
  21. package/agent/design/requirements.md +56 -0
  22. package/agent/design/template-storage-strategy.md +412 -0
  23. package/agent/design/template-suggestion-system.md +853 -0
  24. package/agent/design/trust-escalation-prevention.md +343 -0
  25. package/agent/design/trust-system-implementation.md +592 -0
  26. package/agent/design/user-preferences.md +683 -0
  27. package/agent/design/weaviate-collection-strategy.md +461 -0
  28. package/agent/milestones/.gitkeep +0 -0
  29. package/agent/milestones/milestone-1-project-foundation.md +121 -0
  30. package/agent/milestones/milestone-2-core-memory-system.md +150 -0
  31. package/agent/milestones/milestone-3-relationships-graph.md +116 -0
  32. package/agent/milestones/milestone-4-user-preferences.md +103 -0
  33. package/agent/milestones/milestone-5-template-system.md +126 -0
  34. package/agent/milestones/milestone-6-auth-multi-tenancy.md +124 -0
  35. package/agent/milestones/milestone-7-trust-permissions.md +133 -0
  36. package/agent/milestones/milestone-8-testing-quality.md +137 -0
  37. package/agent/milestones/milestone-9-deployment-documentation.md +147 -0
  38. package/agent/patterns/.gitkeep +0 -0
  39. package/agent/patterns/bootstrap.md +1271 -0
  40. package/agent/patterns/firebase-admin-sdk-v8-usage.md +950 -0
  41. package/agent/patterns/firestore-users-pattern-best-practices.md +347 -0
  42. package/agent/patterns/library-services.md +454 -0
  43. package/agent/patterns/testing-colocated.md +316 -0
  44. package/agent/progress.yaml +395 -0
  45. package/agent/tasks/.gitkeep +0 -0
  46. package/agent/tasks/task-1-initialize-project-structure.md +266 -0
  47. package/agent/tasks/task-2-install-dependencies.md +199 -0
  48. package/agent/tasks/task-3-setup-weaviate-client.md +330 -0
  49. package/agent/tasks/task-4-setup-firestore-client.md +362 -0
  50. package/agent/tasks/task-5-create-basic-mcp-server.md +114 -0
  51. package/agent/tasks/task-6-create-integration-tests.md +195 -0
  52. package/agent/tasks/task-7-finalize-milestone-1.md +363 -0
  53. package/agent/tasks/task-8-setup-utility-scripts.md +382 -0
  54. package/agent/tasks/task-9-create-server-factory.md +404 -0
  55. package/dist/config.d.ts +26 -0
  56. package/dist/constants/content-types.d.ts +60 -0
  57. package/dist/firestore/init.d.ts +14 -0
  58. package/dist/firestore/paths.d.ts +53 -0
  59. package/dist/firestore/paths.spec.d.ts +2 -0
  60. package/dist/server-factory.d.ts +40 -0
  61. package/dist/server-factory.js +1741 -0
  62. package/dist/server-factory.spec.d.ts +2 -0
  63. package/dist/server.d.ts +3 -0
  64. package/dist/server.js +1690 -0
  65. package/dist/tools/create-memory.d.ts +94 -0
  66. package/dist/tools/delete-memory.d.ts +47 -0
  67. package/dist/tools/search-memory.d.ts +88 -0
  68. package/dist/types/memory.d.ts +183 -0
  69. package/dist/utils/logger.d.ts +7 -0
  70. package/dist/weaviate/client.d.ts +39 -0
  71. package/dist/weaviate/client.spec.d.ts +2 -0
  72. package/dist/weaviate/schema.d.ts +29 -0
  73. package/esbuild.build.js +60 -0
  74. package/esbuild.watch.js +25 -0
  75. package/jest.config.js +31 -0
  76. package/jest.e2e.config.js +17 -0
  77. package/package.json +68 -0
  78. package/src/.gitkeep +0 -0
  79. package/src/config.ts +56 -0
  80. package/src/constants/content-types.ts +454 -0
  81. package/src/firestore/init.ts +68 -0
  82. package/src/firestore/paths.spec.ts +75 -0
  83. package/src/firestore/paths.ts +124 -0
  84. package/src/server-factory.spec.ts +60 -0
  85. package/src/server-factory.ts +215 -0
  86. package/src/server.ts +243 -0
  87. package/src/tools/create-memory.ts +198 -0
  88. package/src/tools/delete-memory.ts +126 -0
  89. package/src/tools/search-memory.ts +216 -0
  90. package/src/types/memory.ts +276 -0
  91. package/src/utils/logger.ts +42 -0
  92. package/src/weaviate/client.spec.ts +58 -0
  93. package/src/weaviate/client.ts +114 -0
  94. package/src/weaviate/schema.ts +288 -0
  95. package/tsconfig.json +26 -0
package/src/.gitkeep ADDED
File without changes
package/src/config.ts ADDED
@@ -0,0 +1,56 @@
1
+ import dotenv from 'dotenv';
2
+
3
+ dotenv.config();
4
+
5
+ export const config = {
6
+ // Weaviate
7
+ weaviate: {
8
+ url: process.env.WEAVIATE_URL || 'http://localhost:8080',
9
+ apiKey: process.env.WEAVIATE_API_KEY || '',
10
+ },
11
+
12
+ // OpenAI
13
+ openai: {
14
+ apiKey: process.env.OPENAI_APIKEY || '',
15
+ },
16
+
17
+ // Firebase (using firebase-admin-sdk-v8)
18
+ firebase: {
19
+ serviceAccount: process.env.FIREBASE_ADMIN_SERVICE_ACCOUNT_KEY || '',
20
+ projectId: process.env.FIREBASE_PROJECT_ID || '',
21
+ },
22
+
23
+ // Server
24
+ server: {
25
+ port: parseInt(process.env.PORT || '3000', 10),
26
+ nodeEnv: process.env.NODE_ENV || 'development',
27
+ logLevel: process.env.LOG_LEVEL || 'info',
28
+ },
29
+
30
+ // MCP
31
+ mcp: {
32
+ transport: process.env.MCP_TRANSPORT || 'sse',
33
+ },
34
+ } as const;
35
+
36
+ /**
37
+ * Validate required configuration
38
+ */
39
+ export function validateConfig(): void {
40
+ const required = [
41
+ { key: 'WEAVIATE_URL', value: config.weaviate.url },
42
+ { key: 'OPENAI_APIKEY', value: config.openai.apiKey },
43
+ { key: 'FIREBASE_ADMIN_SERVICE_ACCOUNT_KEY', value: config.firebase.serviceAccount },
44
+ { key: 'FIREBASE_PROJECT_ID', value: config.firebase.projectId },
45
+ ];
46
+
47
+ const missing = required.filter((r) => !r.value);
48
+
49
+ if (missing.length > 0) {
50
+ throw new Error(
51
+ `Missing required environment variables: ${missing.map((m) => m.key).join(', ')}`
52
+ );
53
+ }
54
+
55
+ console.log('[Config] Configuration validated');
56
+ }
@@ -0,0 +1,454 @@
1
+ /**
2
+ * Content type constants and descriptions
3
+ * Based on agent/design/content-types-expansion.md and default-template-library.md
4
+ */
5
+
6
+ import type { ContentType } from '../types/memory.js';
7
+
8
+ /**
9
+ * All available content types
10
+ */
11
+ export const CONTENT_TYPES: readonly ContentType[] = [
12
+ // Core types
13
+ 'code',
14
+ 'note',
15
+ 'documentation',
16
+ 'reference',
17
+ // Task & Planning
18
+ 'todo',
19
+ 'checklist',
20
+ 'project',
21
+ 'goal',
22
+ 'habit',
23
+ // Communication
24
+ 'email',
25
+ 'conversation',
26
+ 'meeting',
27
+ 'person', // Unified person template (replaces both 'contact' and 'person')
28
+ // Content & Media
29
+ 'article',
30
+ 'webpage',
31
+ 'social',
32
+ 'image',
33
+ 'video',
34
+ 'audio',
35
+ 'transcript',
36
+ 'presentation',
37
+ 'spreadsheet',
38
+ 'pdf',
39
+ // Creative
40
+ 'screenplay',
41
+ 'recipe',
42
+ 'idea',
43
+ 'quote',
44
+ // Personal
45
+ 'journal',
46
+ 'memory',
47
+ 'event',
48
+ // Organizational
49
+ 'bookmark',
50
+ 'form',
51
+ 'location',
52
+ // Business
53
+ 'invoice',
54
+ 'contract',
55
+ // System
56
+ 'system',
57
+ 'action',
58
+ 'audit',
59
+ 'history',
60
+ ] as const;
61
+
62
+ /**
63
+ * Content type metadata
64
+ */
65
+ export interface ContentTypeMetadata {
66
+ name: ContentType;
67
+ category: string;
68
+ description: string;
69
+ examples: string[];
70
+ common_fields?: string[];
71
+ }
72
+
73
+ /**
74
+ * Comprehensive content type descriptions
75
+ */
76
+ export const CONTENT_TYPE_METADATA: Record<ContentType, ContentTypeMetadata> = {
77
+ // Core Types
78
+ code: {
79
+ name: 'code',
80
+ category: 'core',
81
+ description: 'Source code files and programming content',
82
+ examples: ['Code snippets', 'Functions', 'Scripts', 'Configuration files'],
83
+ common_fields: ['language', 'framework', 'purpose'],
84
+ },
85
+ note: {
86
+ name: 'note',
87
+ category: 'core',
88
+ description: 'Personal notes and quick documentation',
89
+ examples: ['Quick notes', 'Reminders', 'Observations', 'Thoughts'],
90
+ },
91
+ documentation: {
92
+ name: 'documentation',
93
+ category: 'core',
94
+ description: 'Technical documentation and guides',
95
+ examples: ['API docs', 'User guides', 'Technical specs', 'How-to guides'],
96
+ },
97
+ reference: {
98
+ name: 'reference',
99
+ category: 'core',
100
+ description: 'Quick reference guides and cheat sheets',
101
+ examples: ['Command references', 'Keyboard shortcuts', 'API references', 'Cheat sheets'],
102
+ },
103
+
104
+ // Task & Planning
105
+ todo: {
106
+ name: 'todo',
107
+ category: 'task',
108
+ description: 'Individual tasks with due dates and priorities',
109
+ examples: ['Task items', 'Action items', 'Assignments'],
110
+ common_fields: ['due_date', 'priority', 'status', 'assignee'],
111
+ },
112
+ checklist: {
113
+ name: 'checklist',
114
+ category: 'task',
115
+ description: 'Reusable checklists and sequential steps',
116
+ examples: ['Grocery lists', 'Packing lists', 'Process checklists', 'Preparation lists'],
117
+ common_fields: ['items', 'completion_percentage'],
118
+ },
119
+ project: {
120
+ name: 'project',
121
+ category: 'task',
122
+ description: 'Project plans and overviews',
123
+ examples: ['Project documentation', 'Project plans', 'Milestones'],
124
+ common_fields: ['status', 'start_date', 'end_date', 'stakeholders'],
125
+ },
126
+ goal: {
127
+ name: 'goal',
128
+ category: 'task',
129
+ description: 'Goals, objectives, and milestones',
130
+ examples: ['Personal goals', 'Professional objectives', 'KPIs'],
131
+ common_fields: ['target_date', 'progress', 'milestones'],
132
+ },
133
+ habit: {
134
+ name: 'habit',
135
+ category: 'task',
136
+ description: 'Routines and habit tracking',
137
+ examples: ['Daily habits', 'Routines', 'Recurring activities'],
138
+ common_fields: ['frequency', 'streak', 'trigger'],
139
+ },
140
+
141
+ // Communication
142
+ email: {
143
+ name: 'email',
144
+ category: 'communication',
145
+ description: 'Email messages and threads',
146
+ examples: ['Email messages', 'Email threads', 'Drafts'],
147
+ common_fields: ['from', 'to', 'subject', 'date'],
148
+ },
149
+ conversation: {
150
+ name: 'conversation',
151
+ category: 'communication',
152
+ description: 'Chat logs and conversations',
153
+ examples: ['Chat messages', 'Conversation logs', 'Discussions'],
154
+ common_fields: ['participants', 'platform'],
155
+ },
156
+ meeting: {
157
+ name: 'meeting',
158
+ category: 'communication',
159
+ description: 'Meeting notes and action items',
160
+ examples: ['Meeting notes', 'Standup notes', 'Conference calls'],
161
+ common_fields: ['attendees', 'agenda', 'decisions', 'action_items'],
162
+ },
163
+ person: {
164
+ name: 'person',
165
+ category: 'communication',
166
+ description: 'Track information about people - personal, professional, or both',
167
+ examples: ['Friends', 'Family', 'Colleagues', 'Professional contacts', 'Business partners'],
168
+ common_fields: ['name', 'relationship', 'company', 'job_title', 'how_we_met', 'contact_info', 'birthday', 'interests'],
169
+ },
170
+
171
+ // Content & Media
172
+ article: {
173
+ name: 'article',
174
+ category: 'content',
175
+ description: 'Articles and blog posts',
176
+ examples: ['Blog posts', 'News articles', 'Long-form content'],
177
+ common_fields: ['author', 'publication', 'url'],
178
+ },
179
+ webpage: {
180
+ name: 'webpage',
181
+ category: 'content',
182
+ description: 'Saved web pages and HTML content',
183
+ examples: ['Web pages', 'HTML documents', 'Web content'],
184
+ common_fields: ['url', 'domain', 'archived_at'],
185
+ },
186
+ social: {
187
+ name: 'social',
188
+ category: 'content',
189
+ description: 'Social media posts and updates',
190
+ examples: ['Tweets', 'Posts', 'Status updates'],
191
+ common_fields: ['platform', 'author', 'url'],
192
+ },
193
+ image: {
194
+ name: 'image',
195
+ category: 'media',
196
+ description: 'Image files and visual content',
197
+ examples: ['Photos', 'Screenshots', 'Diagrams', 'Illustrations'],
198
+ common_fields: ['file_path', 'dimensions', 'format'],
199
+ },
200
+ video: {
201
+ name: 'video',
202
+ category: 'media',
203
+ description: 'Video files and recordings',
204
+ examples: ['Videos', 'Recordings', 'Tutorials'],
205
+ common_fields: ['duration', 'format', 'url'],
206
+ },
207
+ audio: {
208
+ name: 'audio',
209
+ category: 'media',
210
+ description: 'Audio files and recordings',
211
+ examples: ['Voice notes', 'Podcasts', 'Music', 'Recordings'],
212
+ common_fields: ['duration', 'format'],
213
+ },
214
+ transcript: {
215
+ name: 'transcript',
216
+ category: 'media',
217
+ description: 'Transcriptions of audio or video',
218
+ examples: ['Meeting transcripts', 'Podcast transcripts', 'Video captions'],
219
+ common_fields: ['source_media', 'speakers'],
220
+ },
221
+ presentation: {
222
+ name: 'presentation',
223
+ category: 'content',
224
+ description: 'Presentation slides and decks',
225
+ examples: ['Slide decks', 'Pitch decks', 'Presentations'],
226
+ common_fields: ['slide_count', 'format'],
227
+ },
228
+ spreadsheet: {
229
+ name: 'spreadsheet',
230
+ category: 'content',
231
+ description: 'Data tables and spreadsheet content',
232
+ examples: ['Spreadsheets', 'Data tables', 'CSV content'],
233
+ common_fields: ['rows', 'columns', 'format'],
234
+ },
235
+ pdf: {
236
+ name: 'pdf',
237
+ category: 'content',
238
+ description: 'PDF documents and scanned files',
239
+ examples: ['PDF documents', 'Scanned documents', 'Reports'],
240
+ common_fields: ['pages', 'file_size'],
241
+ },
242
+
243
+ // Creative
244
+ screenplay: {
245
+ name: 'screenplay',
246
+ category: 'creative',
247
+ description: 'Screenplay and script content',
248
+ examples: ['Screenplays', 'Scripts', 'Dialogue'],
249
+ common_fields: ['characters', 'scenes'],
250
+ },
251
+ recipe: {
252
+ name: 'recipe',
253
+ category: 'creative',
254
+ description: 'Cooking recipes and instructions',
255
+ examples: ['Recipes', 'Cooking instructions', 'Meal plans'],
256
+ common_fields: ['ingredients', 'instructions', 'servings', 'prep_time', 'cook_time'],
257
+ },
258
+ idea: {
259
+ name: 'idea',
260
+ category: 'creative',
261
+ description: 'Brainstorming and concepts',
262
+ examples: ['Ideas', 'Brainstorms', 'Concepts', 'Inspiration'],
263
+ common_fields: ['category', 'potential_impact'],
264
+ },
265
+ quote: {
266
+ name: 'quote',
267
+ category: 'creative',
268
+ description: 'Memorable quotes and excerpts',
269
+ examples: ['Quotes', 'Excerpts', 'Highlights', 'Citations'],
270
+ common_fields: ['author', 'source'],
271
+ },
272
+
273
+ // Personal
274
+ journal: {
275
+ name: 'journal',
276
+ category: 'personal',
277
+ description: 'Daily journal entries and reflections',
278
+ examples: ['Journal entries', 'Diary entries', 'Reflections'],
279
+ common_fields: ['date', 'mood', 'highlights'],
280
+ },
281
+ memory: {
282
+ name: 'memory',
283
+ category: 'personal',
284
+ description: 'Personal memories and significant moments',
285
+ examples: ['Life events', 'Significant moments', 'Memories'],
286
+ common_fields: ['date', 'people_involved', 'location'],
287
+ },
288
+ event: {
289
+ name: 'event',
290
+ category: 'personal',
291
+ description: 'Calendar events and activities',
292
+ examples: ['Events', 'Activities', 'Appointments'],
293
+ common_fields: ['date', 'time', 'location', 'attendees'],
294
+ },
295
+
296
+ // Organizational
297
+ bookmark: {
298
+ name: 'bookmark',
299
+ category: 'organizational',
300
+ description: 'Web bookmarks and resource collections',
301
+ examples: ['Bookmarks', 'Resource links', 'Reading lists'],
302
+ common_fields: ['url', 'domain', 'read_later'],
303
+ },
304
+ form: {
305
+ name: 'form',
306
+ category: 'organizational',
307
+ description: 'Forms and surveys',
308
+ examples: ['Questionnaires', 'Feedback forms', 'Surveys'],
309
+ common_fields: ['fields', 'responses'],
310
+ },
311
+ location: {
312
+ name: 'location',
313
+ category: 'organizational',
314
+ description: 'Place information and recommendations',
315
+ examples: ['Places', 'Venues', 'Destinations', 'Locations'],
316
+ common_fields: ['address', 'gps', 'rating'],
317
+ },
318
+
319
+ // Business
320
+ invoice: {
321
+ name: 'invoice',
322
+ category: 'business',
323
+ description: 'Invoices and receipts',
324
+ examples: ['Invoices', 'Receipts', 'Bills'],
325
+ common_fields: ['amount', 'date', 'vendor', 'items'],
326
+ },
327
+ contract: {
328
+ name: 'contract',
329
+ category: 'business',
330
+ description: 'Contracts and agreements',
331
+ examples: ['Contracts', 'Agreements', 'Terms of service'],
332
+ common_fields: ['parties', 'effective_date', 'terms'],
333
+ },
334
+
335
+ // System
336
+ system: {
337
+ name: 'system',
338
+ category: 'system',
339
+ description: 'Agent instructions (reserved for internal use only)',
340
+ examples: ['System prompts', 'Agent instructions', 'Configuration'],
341
+ },
342
+ action: {
343
+ name: 'action',
344
+ category: 'system',
345
+ description: 'Agent actions and operations',
346
+ examples: ['Actions taken', 'Operations performed', 'Commands executed'],
347
+ common_fields: ['action_type', 'status', 'result'],
348
+ },
349
+ audit: {
350
+ name: 'audit',
351
+ category: 'system',
352
+ description: 'Audit logs and compliance records',
353
+ examples: ['Audit logs', 'Access logs', 'Security events'],
354
+ common_fields: ['event_type', 'actor', 'target', 'result'],
355
+ },
356
+ history: {
357
+ name: 'history',
358
+ category: 'system',
359
+ description: 'Change history and version tracking',
360
+ examples: ['Edit history', 'Version history', 'Change logs'],
361
+ common_fields: ['target_id', 'change_type', 'previous_value', 'new_value'],
362
+ },
363
+ };
364
+
365
+ /**
366
+ * Content type categories
367
+ */
368
+ export const CONTENT_TYPE_CATEGORIES = {
369
+ core: ['code', 'note', 'documentation', 'reference'],
370
+ task: ['todo', 'checklist', 'project', 'goal', 'habit'],
371
+ communication: ['email', 'conversation', 'meeting', 'person'],
372
+ content: ['article', 'webpage', 'social', 'presentation', 'spreadsheet', 'pdf'],
373
+ media: ['image', 'video', 'audio', 'transcript'],
374
+ creative: ['screenplay', 'recipe', 'idea', 'quote'],
375
+ personal: ['journal', 'memory', 'event'],
376
+ organizational: ['bookmark', 'form', 'location'],
377
+ business: ['invoice', 'contract'],
378
+ system: ['system', 'action', 'audit', 'history'],
379
+ } as const;
380
+
381
+ /**
382
+ * Get content type metadata
383
+ */
384
+ export function getContentTypeMetadata(type: ContentType): ContentTypeMetadata {
385
+ return CONTENT_TYPE_METADATA[type];
386
+ }
387
+
388
+ /**
389
+ * Get content types by category
390
+ */
391
+ export function getContentTypesByCategory(category: keyof typeof CONTENT_TYPE_CATEGORIES): ContentType[] {
392
+ return CONTENT_TYPE_CATEGORIES[category] as unknown as ContentType[];
393
+ }
394
+
395
+ /**
396
+ * Validate content type
397
+ */
398
+ export function isValidContentType(type: string): type is ContentType {
399
+ return CONTENT_TYPES.includes(type as ContentType);
400
+ }
401
+
402
+ /**
403
+ * Get content type description for LLM prompts
404
+ * Generated dynamically from CONTENT_TYPE_METADATA
405
+ */
406
+ export function getContentTypeDescription(): string {
407
+ const categoryNames: Record<string, string> = {
408
+ core: 'Core Types',
409
+ task: 'Task & Planning',
410
+ communication: 'Communication',
411
+ content: 'Content & Media',
412
+ media: 'Content & Media',
413
+ creative: 'Creative',
414
+ personal: 'Personal',
415
+ organizational: 'Organizational',
416
+ business: 'Business',
417
+ system: 'System (Internal Use)',
418
+ };
419
+
420
+ const lines: string[] = ['Type of content:', ''];
421
+
422
+ // Group by category
423
+ const categorized = new Map<string, ContentType[]>();
424
+
425
+ for (const type of CONTENT_TYPES) {
426
+ const metadata = CONTENT_TYPE_METADATA[type];
427
+ const categoryKey = metadata.category;
428
+
429
+ if (!categorized.has(categoryKey)) {
430
+ categorized.set(categoryKey, []);
431
+ }
432
+ categorized.get(categoryKey)!.push(type);
433
+ }
434
+
435
+ // Build description by category
436
+ for (const [categoryKey, types] of categorized) {
437
+ const categoryName = categoryNames[categoryKey] || categoryKey;
438
+ lines.push(`${categoryName}:`);
439
+
440
+ for (const type of types) {
441
+ const metadata = CONTENT_TYPE_METADATA[type];
442
+ lines.push(` - '${type}': ${metadata.description}`);
443
+ }
444
+
445
+ lines.push('');
446
+ }
447
+
448
+ return lines.join('\n').trim();
449
+ }
450
+
451
+ /**
452
+ * Default content type
453
+ */
454
+ export const DEFAULT_CONTENT_TYPE: ContentType = 'note';
@@ -0,0 +1,68 @@
1
+ import { initializeApp } from '@prmichaelsen/firebase-admin-sdk-v8';
2
+ import { config } from '../config.js';
3
+
4
+ let initialized = false;
5
+
6
+ /**
7
+ * Initialize Firebase Admin SDK
8
+ */
9
+ export function initFirestore(): void {
10
+ if (initialized) {
11
+ return;
12
+ }
13
+
14
+ try {
15
+ initializeApp({
16
+ serviceAccount: JSON.parse(config.firebase.serviceAccount),
17
+ projectId: config.firebase.projectId,
18
+ });
19
+
20
+ initialized = true;
21
+ console.log('[Firestore] Initialized');
22
+ } catch (error) {
23
+ console.error('[Firestore] Initialization failed:', error);
24
+ throw error;
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Check if Firestore is initialized
30
+ */
31
+ export function isFirestoreInitialized(): boolean {
32
+ return initialized;
33
+ }
34
+
35
+ /**
36
+ * Test Firestore connection
37
+ */
38
+ export async function testFirestoreConnection(): Promise<boolean> {
39
+ try {
40
+ if (!initialized) {
41
+ throw new Error('Firestore not initialized');
42
+ }
43
+
44
+ // Try a simple operation to test connection
45
+ const { getDocument } = await import('@prmichaelsen/firebase-admin-sdk-v8');
46
+ await getDocument('_health_check', 'test');
47
+
48
+ console.log('[Firestore] Connection successful');
49
+ return true;
50
+ } catch (error) {
51
+ console.error('[Firestore] Connection test failed:', error);
52
+ return false;
53
+ }
54
+ }
55
+
56
+ // Re-export firebase-admin-sdk-v8 functions for convenience
57
+ export {
58
+ getDocument,
59
+ setDocument,
60
+ addDocument,
61
+ updateDocument,
62
+ deleteDocument,
63
+ queryDocuments,
64
+ batchWrite,
65
+ FieldValue,
66
+ verifyIdToken,
67
+ type QueryOptions,
68
+ } from '@prmichaelsen/firebase-admin-sdk-v8';
@@ -0,0 +1,75 @@
1
+ import { describe, it, expect } from '@jest/globals';
2
+ import {
3
+ BASE,
4
+ getUserPreferencesPath,
5
+ getUserTemplatesPath,
6
+ getUserAccessLogsPath,
7
+ getUserTrustRelationshipsPath,
8
+ getUserPermissionsPath,
9
+ getUserPermissionPath,
10
+ getDefaultTemplatesPath,
11
+ getDefaultTemplatePath,
12
+ } from '../../src/firestore/paths.js';
13
+
14
+ describe('Firestore Path Helpers', () => {
15
+ describe('Environment Prefix', () => {
16
+ it('should have BASE prefix', () => {
17
+ // In test environment, should default to e0.remember-mcp
18
+ expect(BASE).toMatch(/^(e\d+\.)?remember-mcp$/);
19
+ });
20
+ });
21
+
22
+ describe('User-Scoped Collections (under users/{user_id}/)', () => {
23
+ it('should generate user preferences path', () => {
24
+ const path = getUserPreferencesPath('user123');
25
+ expect(path).toContain('.users/user123/preferences');
26
+ expect(path).toContain('remember-mcp');
27
+ });
28
+
29
+ it('should generate user templates path', () => {
30
+ const path = getUserTemplatesPath('user123');
31
+ expect(path).toContain('.users/user123/templates');
32
+ expect(path).toContain('remember-mcp');
33
+ });
34
+
35
+ it('should generate user access logs path', () => {
36
+ const path = getUserAccessLogsPath('user123');
37
+ expect(path).toContain('.users/user123/access-logs');
38
+ expect(path).toContain('remember-mcp');
39
+ });
40
+
41
+ it('should generate user trust relationships path', () => {
42
+ const path = getUserTrustRelationshipsPath('user123');
43
+ expect(path).toContain('.users/user123/trust-relationships');
44
+ expect(path).toContain('remember-mcp');
45
+ });
46
+ });
47
+
48
+ describe('Cross-User Permissions (outside users/)', () => {
49
+ it('should generate permissions collection path', () => {
50
+ const path = getUserPermissionsPath('owner123');
51
+ expect(path).toContain('.user-permissions/owner123/allowed-accessors');
52
+ expect(path).toContain('remember-mcp');
53
+ });
54
+
55
+ it('should generate specific permission path', () => {
56
+ const path = getUserPermissionPath('owner123', 'accessor456');
57
+ expect(path).toContain('.user-permissions/owner123/allowed-accessors/accessor456');
58
+ expect(path).toContain('remember-mcp');
59
+ });
60
+ });
61
+
62
+ describe('Shared/Global Collections', () => {
63
+ it('should generate default templates path', () => {
64
+ const path = getDefaultTemplatesPath();
65
+ expect(path).toContain('.templates/default');
66
+ expect(path).toContain('remember-mcp');
67
+ });
68
+
69
+ it('should generate specific default template path', () => {
70
+ const path = getDefaultTemplatePath('template123');
71
+ expect(path).toContain('.templates/default/template123');
72
+ expect(path).toContain('remember-mcp');
73
+ });
74
+ });
75
+ });