@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.
- package/.env.example +65 -0
- package/AGENT.md +840 -0
- package/README.md +72 -0
- package/agent/design/.gitkeep +0 -0
- package/agent/design/access-control-result-pattern.md +458 -0
- package/agent/design/action-audit-memory-types.md +637 -0
- package/agent/design/common-template-fields.md +282 -0
- package/agent/design/complete-tool-set.md +407 -0
- package/agent/design/content-types-expansion.md +521 -0
- package/agent/design/cross-database-id-strategy.md +358 -0
- package/agent/design/default-template-library.md +423 -0
- package/agent/design/firestore-wrapper-analysis.md +606 -0
- package/agent/design/llm-provider-abstraction.md +691 -0
- package/agent/design/location-handling-architecture.md +523 -0
- package/agent/design/memory-templates-design.md +364 -0
- package/agent/design/permissions-storage-architecture.md +680 -0
- package/agent/design/relationship-storage-strategy.md +361 -0
- package/agent/design/remember-mcp-implementation-tasks.md +417 -0
- package/agent/design/remember-mcp-progress.yaml +141 -0
- package/agent/design/requirements-enhancements.md +468 -0
- package/agent/design/requirements.md +56 -0
- package/agent/design/template-storage-strategy.md +412 -0
- package/agent/design/template-suggestion-system.md +853 -0
- package/agent/design/trust-escalation-prevention.md +343 -0
- package/agent/design/trust-system-implementation.md +592 -0
- package/agent/design/user-preferences.md +683 -0
- package/agent/design/weaviate-collection-strategy.md +461 -0
- package/agent/milestones/.gitkeep +0 -0
- package/agent/milestones/milestone-1-project-foundation.md +121 -0
- package/agent/milestones/milestone-2-core-memory-system.md +150 -0
- package/agent/milestones/milestone-3-relationships-graph.md +116 -0
- package/agent/milestones/milestone-4-user-preferences.md +103 -0
- package/agent/milestones/milestone-5-template-system.md +126 -0
- package/agent/milestones/milestone-6-auth-multi-tenancy.md +124 -0
- package/agent/milestones/milestone-7-trust-permissions.md +133 -0
- package/agent/milestones/milestone-8-testing-quality.md +137 -0
- package/agent/milestones/milestone-9-deployment-documentation.md +147 -0
- package/agent/patterns/.gitkeep +0 -0
- package/agent/patterns/bootstrap.md +1271 -0
- package/agent/patterns/firebase-admin-sdk-v8-usage.md +950 -0
- package/agent/patterns/firestore-users-pattern-best-practices.md +347 -0
- package/agent/patterns/library-services.md +454 -0
- package/agent/patterns/testing-colocated.md +316 -0
- package/agent/progress.yaml +395 -0
- package/agent/tasks/.gitkeep +0 -0
- package/agent/tasks/task-1-initialize-project-structure.md +266 -0
- package/agent/tasks/task-2-install-dependencies.md +199 -0
- package/agent/tasks/task-3-setup-weaviate-client.md +330 -0
- package/agent/tasks/task-4-setup-firestore-client.md +362 -0
- package/agent/tasks/task-5-create-basic-mcp-server.md +114 -0
- package/agent/tasks/task-6-create-integration-tests.md +195 -0
- package/agent/tasks/task-7-finalize-milestone-1.md +363 -0
- package/agent/tasks/task-8-setup-utility-scripts.md +382 -0
- package/agent/tasks/task-9-create-server-factory.md +404 -0
- package/dist/config.d.ts +26 -0
- package/dist/constants/content-types.d.ts +60 -0
- package/dist/firestore/init.d.ts +14 -0
- package/dist/firestore/paths.d.ts +53 -0
- package/dist/firestore/paths.spec.d.ts +2 -0
- package/dist/server-factory.d.ts +40 -0
- package/dist/server-factory.js +1741 -0
- package/dist/server-factory.spec.d.ts +2 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +1690 -0
- package/dist/tools/create-memory.d.ts +94 -0
- package/dist/tools/delete-memory.d.ts +47 -0
- package/dist/tools/search-memory.d.ts +88 -0
- package/dist/types/memory.d.ts +183 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/weaviate/client.d.ts +39 -0
- package/dist/weaviate/client.spec.d.ts +2 -0
- package/dist/weaviate/schema.d.ts +29 -0
- package/esbuild.build.js +60 -0
- package/esbuild.watch.js +25 -0
- package/jest.config.js +31 -0
- package/jest.e2e.config.js +17 -0
- package/package.json +68 -0
- package/src/.gitkeep +0 -0
- package/src/config.ts +56 -0
- package/src/constants/content-types.ts +454 -0
- package/src/firestore/init.ts +68 -0
- package/src/firestore/paths.spec.ts +75 -0
- package/src/firestore/paths.ts +124 -0
- package/src/server-factory.spec.ts +60 -0
- package/src/server-factory.ts +215 -0
- package/src/server.ts +243 -0
- package/src/tools/create-memory.ts +198 -0
- package/src/tools/delete-memory.ts +126 -0
- package/src/tools/search-memory.ts +216 -0
- package/src/types/memory.ts +276 -0
- package/src/utils/logger.ts +42 -0
- package/src/weaviate/client.spec.ts +58 -0
- package/src/weaviate/client.ts +114 -0
- package/src/weaviate/schema.ts +288 -0
- 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
|
+
});
|