@telvok/librarian-mcp 1.5.4 → 2.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 (119) hide show
  1. package/dist/library/errors.d.ts +48 -0
  2. package/dist/library/errors.js +80 -0
  3. package/dist/library/schemas.d.ts +6 -6
  4. package/dist/library/storage.d.ts +2 -2
  5. package/dist/library/storage.js +2 -2
  6. package/dist/library 2/embeddings.d.ts +21 -0
  7. package/dist/library 2/embeddings.js +86 -0
  8. package/dist/library 2/manager.d.ts +42 -0
  9. package/dist/library 2/manager.js +218 -0
  10. package/dist/library 2/parsers/cursor.d.ts +15 -0
  11. package/dist/library 2/parsers/cursor.js +168 -0
  12. package/dist/library 2/parsers/index.d.ts +6 -0
  13. package/dist/library 2/parsers/index.js +5 -0
  14. package/dist/library 2/parsers/json.d.ts +11 -0
  15. package/dist/library 2/parsers/json.js +95 -0
  16. package/dist/library 2/parsers/jsonl.d.ts +14 -0
  17. package/dist/library 2/parsers/jsonl.js +85 -0
  18. package/dist/library 2/parsers/markdown.d.ts +15 -0
  19. package/dist/library 2/parsers/markdown.js +77 -0
  20. package/dist/library 2/parsers/sqlite.d.ts +8 -0
  21. package/dist/library 2/parsers/sqlite.js +123 -0
  22. package/dist/library 2/parsers/types.d.ts +21 -0
  23. package/dist/library 2/parsers/types.js +4 -0
  24. package/dist/library 2/query.d.ts +26 -0
  25. package/dist/library 2/query.js +104 -0
  26. package/dist/library 2/schemas.d.ts +324 -0
  27. package/dist/library 2/schemas.js +79 -0
  28. package/dist/library 2/storage.d.ts +22 -0
  29. package/dist/library 2/storage.js +36 -0
  30. package/dist/library 2/vector-index.d.ts +55 -0
  31. package/dist/library 2/vector-index.js +160 -0
  32. package/dist/server 2.js +199 -0
  33. package/dist/server.d 2.ts +2 -0
  34. package/dist/server.js +102 -54
  35. package/dist/tools/adopt.d.ts +1 -0
  36. package/dist/tools/adopt.js +37 -10
  37. package/dist/tools/auth.d.ts +69 -0
  38. package/dist/tools/auth.js +379 -0
  39. package/dist/tools/bounty-claim.d.ts +28 -0
  40. package/dist/tools/bounty-claim.js +92 -0
  41. package/dist/tools/bounty-create.d.ts +47 -0
  42. package/dist/tools/bounty-create.js +118 -0
  43. package/dist/tools/bounty-list.d.ts +50 -0
  44. package/dist/tools/bounty-list.js +116 -0
  45. package/dist/tools/bounty-submit.d.ts +34 -0
  46. package/dist/tools/bounty-submit.js +94 -0
  47. package/dist/tools/brief.d.ts +94 -0
  48. package/dist/tools/brief.js +234 -15
  49. package/dist/tools/delete.d.ts +87 -0
  50. package/dist/tools/delete.js +266 -0
  51. package/dist/tools/feedback.d.ts +27 -0
  52. package/dist/tools/feedback.js +98 -0
  53. package/dist/tools/help.d.ts +22 -0
  54. package/dist/tools/help.js +482 -0
  55. package/dist/tools/import-memories.d.ts +1 -0
  56. package/dist/tools/import-memories.js +18 -13
  57. package/dist/tools/index.d.ts +11 -0
  58. package/dist/tools/index.js +12 -0
  59. package/dist/tools/library-buy.d.ts +31 -0
  60. package/dist/tools/library-buy.js +104 -0
  61. package/dist/tools/library-download.d.ts +27 -0
  62. package/dist/tools/library-download.js +177 -0
  63. package/dist/tools/library-publish.d.ts +112 -0
  64. package/dist/tools/library-publish.js +387 -0
  65. package/dist/tools/library-search.d.ts +110 -0
  66. package/dist/tools/library-search.js +132 -0
  67. package/dist/tools/mark-hit.d.ts +1 -0
  68. package/dist/tools/mark-hit.js +83 -5
  69. package/dist/tools/my-books.d.ts +51 -0
  70. package/dist/tools/my-books.js +115 -0
  71. package/dist/tools/my-bounties.d.ts +43 -0
  72. package/dist/tools/my-bounties.js +126 -0
  73. package/dist/tools/rate-book.d.ts +40 -0
  74. package/dist/tools/rate-book.js +147 -0
  75. package/dist/tools/rebuild-index.d.ts +1 -0
  76. package/dist/tools/rebuild-index.js +40 -8
  77. package/dist/tools/record.d.ts +18 -0
  78. package/dist/tools/record.js +30 -26
  79. package/dist/tools/seller-analytics.d.ts +53 -0
  80. package/dist/tools/seller-analytics.js +180 -0
  81. package/dist/tools/sync.d.ts +55 -0
  82. package/dist/tools/sync.js +304 -0
  83. package/dist/tools/unsubscribe.d.ts +48 -0
  84. package/dist/tools/unsubscribe.js +120 -0
  85. package/dist/tools 2/adopt.d.ts +24 -0
  86. package/dist/tools 2/adopt.js +154 -0
  87. package/dist/tools 2/auth.d.ts +35 -0
  88. package/dist/tools 2/auth.js +229 -0
  89. package/dist/tools 2/brief.d.ts +56 -0
  90. package/dist/tools 2/brief.js +414 -0
  91. package/dist/tools 2/help.d.ts +21 -0
  92. package/dist/tools 2/help.js +267 -0
  93. package/dist/tools 2/import-memories.d.ts +32 -0
  94. package/dist/tools 2/import-memories.js +231 -0
  95. package/dist/tools 2/index.d.ts +12 -0
  96. package/dist/tools 2/index.js +12 -0
  97. package/dist/tools 2/mark-hit.d.ts +20 -0
  98. package/dist/tools 2/mark-hit.js +71 -0
  99. package/dist/tools 2/marketplace-buy.d.ts +30 -0
  100. package/dist/tools 2/marketplace-buy.js +97 -0
  101. package/dist/tools 2/marketplace-download.d.ts +26 -0
  102. package/dist/tools 2/marketplace-download.js +160 -0
  103. package/dist/tools 2/marketplace-publish.d.ts +111 -0
  104. package/dist/tools 2/marketplace-publish.js +377 -0
  105. package/dist/tools 2/marketplace-search.d.ts +57 -0
  106. package/dist/tools 2/marketplace-search.js +96 -0
  107. package/dist/tools 2/my-books.d.ts +50 -0
  108. package/dist/tools 2/my-books.js +107 -0
  109. package/dist/tools 2/rate-book.d.ts +39 -0
  110. package/dist/tools 2/rate-book.js +139 -0
  111. package/dist/tools 2/rebuild-index.d.ts +23 -0
  112. package/dist/tools 2/rebuild-index.js +107 -0
  113. package/dist/tools 2/record.d.ts +40 -0
  114. package/dist/tools 2/record.js +205 -0
  115. package/dist/tools 2/seller-analytics.d.ts +35 -0
  116. package/dist/tools 2/seller-analytics.js +102 -0
  117. package/dist/tools 2/sync.d.ts +54 -0
  118. package/dist/tools 2/sync.js +298 -0
  119. package/package.json +1 -1
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Error codes following JSON-RPC 2.0 conventions
3
+ * -32xxx: Standard JSON-RPC errors
4
+ * -31xxx: Authentication errors
5
+ * -30xxx: Resource errors
6
+ * -29xxx: Network errors
7
+ */
8
+ export declare const ErrorCodes: {
9
+ readonly AUTH_REQUIRED: -31001;
10
+ readonly AUTH_EXPIRED: -31002;
11
+ readonly AUTH_INVALID_SCOPE: -31003;
12
+ readonly AUTH_INVALID_KEY: -31004;
13
+ readonly ENTRY_NOT_FOUND: -30001;
14
+ readonly BOOK_NOT_FOUND: -30002;
15
+ readonly INDEX_STALE: -30003;
16
+ readonly FILE_NOT_FOUND: -30004;
17
+ readonly INVALID_PATH: -30005;
18
+ readonly INVALID_PARAMS: -32602;
19
+ readonly INVALID_REQUEST: -32600;
20
+ readonly API_UNAVAILABLE: -29001;
21
+ readonly NETWORK_TIMEOUT: -29002;
22
+ readonly API_ERROR: -29003;
23
+ };
24
+ export type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
25
+ /**
26
+ * Custom error class for Librarian MCP
27
+ * Includes error code and retryable flag for AI agents
28
+ */
29
+ export declare class LibrarianError extends Error {
30
+ readonly code: ErrorCode;
31
+ readonly retryable: boolean;
32
+ constructor(code: ErrorCode, message: string, retryable?: boolean);
33
+ /**
34
+ * Convert to JSON-RPC compatible error object
35
+ */
36
+ toJSON(): {
37
+ error: string;
38
+ code: ErrorCode;
39
+ retryable: boolean;
40
+ };
41
+ }
42
+ export declare function authRequired(): LibrarianError;
43
+ export declare function authExpired(): LibrarianError;
44
+ export declare function entryNotFound(path: string): LibrarianError;
45
+ export declare function bookNotFound(slug: string): LibrarianError;
46
+ export declare function apiError(message: string): LibrarianError;
47
+ export declare function networkTimeout(): LibrarianError;
48
+ export declare function invalidParams(message: string): LibrarianError;
@@ -0,0 +1,80 @@
1
+ // ============================================================================
2
+ // Error Code Definitions
3
+ // JSON-RPC compatible error codes for MCP
4
+ // ============================================================================
5
+ /**
6
+ * Error codes following JSON-RPC 2.0 conventions
7
+ * -32xxx: Standard JSON-RPC errors
8
+ * -31xxx: Authentication errors
9
+ * -30xxx: Resource errors
10
+ * -29xxx: Network errors
11
+ */
12
+ export const ErrorCodes = {
13
+ // Auth errors (-31xxx)
14
+ AUTH_REQUIRED: -31001,
15
+ AUTH_EXPIRED: -31002,
16
+ AUTH_INVALID_SCOPE: -31003,
17
+ AUTH_INVALID_KEY: -31004,
18
+ // Resource errors (-30xxx)
19
+ ENTRY_NOT_FOUND: -30001,
20
+ BOOK_NOT_FOUND: -30002,
21
+ INDEX_STALE: -30003,
22
+ FILE_NOT_FOUND: -30004,
23
+ INVALID_PATH: -30005,
24
+ // Validation errors (-32xxx - JSON-RPC standard)
25
+ INVALID_PARAMS: -32602,
26
+ INVALID_REQUEST: -32600,
27
+ // Network errors (-29xxx)
28
+ API_UNAVAILABLE: -29001,
29
+ NETWORK_TIMEOUT: -29002,
30
+ API_ERROR: -29003,
31
+ };
32
+ /**
33
+ * Custom error class for Librarian MCP
34
+ * Includes error code and retryable flag for AI agents
35
+ */
36
+ export class LibrarianError extends Error {
37
+ code;
38
+ retryable;
39
+ constructor(code, message, retryable = false) {
40
+ super(message);
41
+ this.name = 'LibrarianError';
42
+ this.code = code;
43
+ this.retryable = retryable;
44
+ }
45
+ /**
46
+ * Convert to JSON-RPC compatible error object
47
+ */
48
+ toJSON() {
49
+ return {
50
+ error: this.message,
51
+ code: this.code,
52
+ retryable: this.retryable,
53
+ };
54
+ }
55
+ }
56
+ // ============================================================================
57
+ // Helper Functions for Common Errors
58
+ // ============================================================================
59
+ export function authRequired() {
60
+ return new LibrarianError(ErrorCodes.AUTH_REQUIRED, 'Authentication required. Use auth({ action: "login" }) to connect.', false);
61
+ }
62
+ export function authExpired() {
63
+ return new LibrarianError(ErrorCodes.AUTH_EXPIRED, 'API key has expired. Use auth({ action: "login" }) to get a new key.', false);
64
+ }
65
+ export function entryNotFound(path) {
66
+ return new LibrarianError(ErrorCodes.ENTRY_NOT_FOUND, `Entry not found: ${path}`, false);
67
+ }
68
+ export function bookNotFound(slug) {
69
+ return new LibrarianError(ErrorCodes.BOOK_NOT_FOUND, `Book not found: ${slug}`, false);
70
+ }
71
+ export function apiError(message) {
72
+ return new LibrarianError(ErrorCodes.API_ERROR, message, true // Network errors are typically retryable
73
+ );
74
+ }
75
+ export function networkTimeout() {
76
+ return new LibrarianError(ErrorCodes.NETWORK_TIMEOUT, 'Request timed out. Please try again.', true);
77
+ }
78
+ export function invalidParams(message) {
79
+ return new LibrarianError(ErrorCodes.INVALID_PARAMS, message, false);
80
+ }
@@ -146,14 +146,14 @@ export declare const ImportedLibrarySchema: z.ZodObject<{
146
146
  name: string;
147
147
  entry_count: number;
148
148
  purchased_at: string;
149
- sync_preference?: "manual" | "auto" | "pinned" | undefined;
150
149
  last_synced?: string | undefined;
150
+ sync_preference?: "pinned" | "manual" | "auto" | undefined;
151
151
  }, {
152
152
  name: string;
153
153
  entry_count: number;
154
154
  purchased_at: string;
155
- sync_preference?: "manual" | "auto" | "pinned" | undefined;
156
155
  last_synced?: string | undefined;
156
+ sync_preference?: "pinned" | "manual" | "auto" | undefined;
157
157
  }>;
158
158
  export type ImportedLibrary = z.infer<typeof ImportedLibrarySchema>;
159
159
  export declare const StateResultSchema: z.ZodObject<{
@@ -192,14 +192,14 @@ export declare const StateResultSchema: z.ZodObject<{
192
192
  name: string;
193
193
  entry_count: number;
194
194
  purchased_at: string;
195
- sync_preference?: "manual" | "auto" | "pinned" | undefined;
196
195
  last_synced?: string | undefined;
196
+ sync_preference?: "pinned" | "manual" | "auto" | undefined;
197
197
  }, {
198
198
  name: string;
199
199
  entry_count: number;
200
200
  purchased_at: string;
201
- sync_preference?: "manual" | "auto" | "pinned" | undefined;
202
201
  last_synced?: string | undefined;
202
+ sync_preference?: "pinned" | "manual" | "auto" | undefined;
203
203
  }>, "many">;
204
204
  }, "strip", z.ZodTypeAny, {
205
205
  entries: {
@@ -215,8 +215,8 @@ export declare const StateResultSchema: z.ZodObject<{
215
215
  name: string;
216
216
  entry_count: number;
217
217
  purchased_at: string;
218
- sync_preference?: "manual" | "auto" | "pinned" | undefined;
219
218
  last_synced?: string | undefined;
219
+ sync_preference?: "pinned" | "manual" | "auto" | undefined;
220
220
  }[];
221
221
  }, {
222
222
  entries: {
@@ -232,8 +232,8 @@ export declare const StateResultSchema: z.ZodObject<{
232
232
  name: string;
233
233
  entry_count: number;
234
234
  purchased_at: string;
235
- sync_preference?: "manual" | "auto" | "pinned" | undefined;
236
235
  last_synced?: string | undefined;
236
+ sync_preference?: "pinned" | "manual" | "auto" | undefined;
237
237
  }[];
238
238
  }>;
239
239
  export type StateResult = z.infer<typeof StateResultSchema>;
@@ -9,11 +9,11 @@ export declare function getLibraryPath(): string;
9
9
  export declare function getLocalPath(libraryPath: string): string;
10
10
  /**
11
11
  * Get the imported entries path.
12
- * @deprecated Use getPackagesPath for marketplace content
12
+ * @deprecated Use getPackagesPath for library content
13
13
  */
14
14
  export declare function getImportedPath(libraryPath: string): string;
15
15
  /**
16
- * Get the packages path (marketplace content from others).
16
+ * Get the packages path (library content from others).
17
17
  */
18
18
  export declare function getPackagesPath(libraryPath: string): string;
19
19
  /**
@@ -17,13 +17,13 @@ export function getLocalPath(libraryPath) {
17
17
  }
18
18
  /**
19
19
  * Get the imported entries path.
20
- * @deprecated Use getPackagesPath for marketplace content
20
+ * @deprecated Use getPackagesPath for library content
21
21
  */
22
22
  export function getImportedPath(libraryPath) {
23
23
  return path.join(libraryPath, 'imported');
24
24
  }
25
25
  /**
26
- * Get the packages path (marketplace content from others).
26
+ * Get the packages path (library content from others).
27
27
  */
28
28
  export function getPackagesPath(libraryPath) {
29
29
  return path.join(libraryPath, 'packages');
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Get embedding for a text string.
3
+ * Returns a 384-dimensional normalized vector.
4
+ */
5
+ export declare function getEmbedding(text: string): Promise<number[]>;
6
+ /**
7
+ * Check if embeddings are available (model can load).
8
+ */
9
+ export declare function isEmbeddingAvailable(): Promise<boolean>;
10
+ /**
11
+ * Calculate cosine similarity between two vectors.
12
+ * Since vectors are normalized, this is just the dot product.
13
+ */
14
+ export declare function cosineSimilarity(a: number[], b: number[]): number;
15
+ /**
16
+ * Split text into chunks at sentence boundaries.
17
+ * Aims for ~500 chars per chunk to preserve semantic meaning.
18
+ */
19
+ export declare function chunkText(text: string, maxChars?: number): string[];
20
+ export declare const EMBEDDING_MODEL_ID = "Xenova/all-MiniLM-L6-v2";
21
+ export declare const EMBEDDING_DIMENSION = 384;
@@ -0,0 +1,86 @@
1
+ import { pipeline, env } from '@huggingface/transformers';
2
+ import * as path from 'path';
3
+ import { getLibraryPath } from './storage.js';
4
+ // ============================================================================
5
+ // Configuration
6
+ // ============================================================================
7
+ // Cache model in .librarian/models
8
+ env.allowRemoteModels = true;
9
+ const MODEL_ID = 'Xenova/all-MiniLM-L6-v2';
10
+ // ============================================================================
11
+ // Embedding Generation
12
+ // ============================================================================
13
+ let embedder = null;
14
+ /**
15
+ * Get embedding for a text string.
16
+ * Returns a 384-dimensional normalized vector.
17
+ */
18
+ export async function getEmbedding(text) {
19
+ if (!embedder) {
20
+ // Set local model path on first call
21
+ const libraryPath = getLibraryPath();
22
+ env.localModelPath = path.join(libraryPath, 'models');
23
+ embedder = await pipeline('feature-extraction', MODEL_ID);
24
+ }
25
+ const result = await embedder(text, { pooling: 'mean', normalize: true });
26
+ return Array.from(result.data);
27
+ }
28
+ /**
29
+ * Check if embeddings are available (model can load).
30
+ */
31
+ export async function isEmbeddingAvailable() {
32
+ try {
33
+ await getEmbedding('test');
34
+ return true;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ // ============================================================================
41
+ // Similarity Calculation
42
+ // ============================================================================
43
+ /**
44
+ * Calculate cosine similarity between two vectors.
45
+ * Since vectors are normalized, this is just the dot product.
46
+ */
47
+ export function cosineSimilarity(a, b) {
48
+ if (a.length !== b.length) {
49
+ throw new Error('Vectors must have same dimension');
50
+ }
51
+ return a.reduce((sum, val, i) => sum + val * b[i], 0);
52
+ }
53
+ // ============================================================================
54
+ // Text Chunking
55
+ // ============================================================================
56
+ /**
57
+ * Split text into chunks at sentence boundaries.
58
+ * Aims for ~500 chars per chunk to preserve semantic meaning.
59
+ */
60
+ export function chunkText(text, maxChars = 500) {
61
+ // Split at sentence boundaries (. ! ? followed by whitespace)
62
+ const sentences = text.split(/(?<=[.!?])\s+/);
63
+ const chunks = [];
64
+ let current = '';
65
+ for (const sentence of sentences) {
66
+ // If adding this sentence exceeds limit and we have content, start new chunk
67
+ if ((current + ' ' + sentence).length > maxChars && current.trim()) {
68
+ chunks.push(current.trim());
69
+ current = sentence;
70
+ }
71
+ else {
72
+ current = current ? current + ' ' + sentence : sentence;
73
+ }
74
+ }
75
+ // Don't forget the last chunk
76
+ if (current.trim()) {
77
+ chunks.push(current.trim());
78
+ }
79
+ // If no chunks created (e.g., no sentence boundaries), return original text
80
+ return chunks.length > 0 ? chunks : [text];
81
+ }
82
+ // ============================================================================
83
+ // Constants
84
+ // ============================================================================
85
+ export const EMBEDDING_MODEL_ID = MODEL_ID;
86
+ export const EMBEDDING_DIMENSION = 384;
@@ -0,0 +1,42 @@
1
+ import type { LibraryEntry } from './schemas.js';
2
+ export declare class LibraryManager {
3
+ private libraryPath;
4
+ constructor();
5
+ /**
6
+ * Initialize the library directory structure.
7
+ */
8
+ initialize(): Promise<void>;
9
+ /**
10
+ * Get all entries from local library.
11
+ */
12
+ getLocalEntries(): Promise<LibraryEntry[]>;
13
+ /**
14
+ * Get all entries from imported libraries.
15
+ */
16
+ getImportedEntries(): Promise<LibraryEntry[]>;
17
+ /**
18
+ * Get all archived entries.
19
+ */
20
+ getArchivedEntries(): Promise<LibraryEntry[]>;
21
+ /**
22
+ * Query entries by topic.
23
+ */
24
+ queryByTopic(topic: string): Promise<LibraryEntry[]>;
25
+ /**
26
+ * Record a new entry to local library.
27
+ */
28
+ record(topics: string[], content: string): Promise<{
29
+ entry: LibraryEntry;
30
+ path: string;
31
+ }>;
32
+ /**
33
+ * Archive an entry (move to archived/).
34
+ */
35
+ archive(entryId: string): Promise<{
36
+ success: boolean;
37
+ message: string;
38
+ }>;
39
+ private readEntriesFromPath;
40
+ private findEntryById;
41
+ private fileExists;
42
+ }
@@ -0,0 +1,218 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import matter from 'gray-matter';
4
+ import { glob } from 'glob';
5
+ import { v4 as uuidv4 } from 'uuid';
6
+ import { getLibraryPath, getLocalPath, getImportedPath, getArchivedPath, } from './storage.js';
7
+ // ============================================================================
8
+ // Library Manager
9
+ // ============================================================================
10
+ export class LibraryManager {
11
+ libraryPath;
12
+ constructor() {
13
+ this.libraryPath = getLibraryPath();
14
+ }
15
+ /**
16
+ * Initialize the library directory structure.
17
+ */
18
+ async initialize() {
19
+ const dirs = [
20
+ getLocalPath(this.libraryPath),
21
+ getImportedPath(this.libraryPath),
22
+ getArchivedPath(this.libraryPath),
23
+ ];
24
+ for (const dir of dirs) {
25
+ await fs.mkdir(dir, { recursive: true });
26
+ }
27
+ }
28
+ /**
29
+ * Get all entries from local library.
30
+ */
31
+ async getLocalEntries() {
32
+ const localPath = getLocalPath(this.libraryPath);
33
+ return this.readEntriesFromPath(localPath, 'local');
34
+ }
35
+ /**
36
+ * Get all entries from imported libraries.
37
+ */
38
+ async getImportedEntries() {
39
+ const importedPath = getImportedPath(this.libraryPath);
40
+ return this.readEntriesFromPath(importedPath, 'imported');
41
+ }
42
+ /**
43
+ * Get all archived entries.
44
+ */
45
+ async getArchivedEntries() {
46
+ const archivedPath = getArchivedPath(this.libraryPath);
47
+ return this.readEntriesFromPath(archivedPath, 'archived');
48
+ }
49
+ /**
50
+ * Query entries by topic.
51
+ */
52
+ async queryByTopic(topic) {
53
+ const [local, imported] = await Promise.all([
54
+ this.getLocalEntries(),
55
+ this.getImportedEntries(),
56
+ ]);
57
+ const allEntries = [...local, ...imported];
58
+ const searchTerm = topic.toLowerCase();
59
+ return allEntries.filter(entry => entry.topics.some(t => t.toLowerCase().includes(searchTerm)) ||
60
+ entry.content.toLowerCase().includes(searchTerm));
61
+ }
62
+ /**
63
+ * Record a new entry to local library.
64
+ */
65
+ async record(topics, content) {
66
+ const localPath = getLocalPath(this.libraryPath);
67
+ await fs.mkdir(localPath, { recursive: true });
68
+ const id = uuidv4();
69
+ const created = new Date().toISOString();
70
+ const entry = {
71
+ id,
72
+ topics,
73
+ content,
74
+ created,
75
+ source: 'local',
76
+ origin: 'manual',
77
+ };
78
+ // Generate filename
79
+ const slug = topics[0]
80
+ .toLowerCase()
81
+ .replace(/[^a-z0-9]+/g, '-')
82
+ .replace(/^-|-$/g, '');
83
+ const timestamp = created.slice(0, 10);
84
+ let filename = `${slug}-${timestamp}.md`;
85
+ let filePath = path.join(localPath, filename);
86
+ // Handle collisions
87
+ let counter = 1;
88
+ while (await this.fileExists(filePath)) {
89
+ filename = `${slug}-${timestamp}-${counter}.md`;
90
+ filePath = path.join(localPath, filename);
91
+ counter++;
92
+ }
93
+ // Write file
94
+ const frontmatter = {
95
+ id,
96
+ topics,
97
+ created,
98
+ source: 'manual',
99
+ };
100
+ const fileContent = matter.stringify(content, frontmatter);
101
+ await fs.writeFile(filePath, fileContent, 'utf-8');
102
+ return {
103
+ entry,
104
+ path: path.relative(this.libraryPath, filePath),
105
+ };
106
+ }
107
+ /**
108
+ * Archive an entry (move to archived/).
109
+ */
110
+ async archive(entryId) {
111
+ const localPath = getLocalPath(this.libraryPath);
112
+ const archivedPath = getArchivedPath(this.libraryPath);
113
+ // Find the entry
114
+ const found = await this.findEntryById(localPath, entryId);
115
+ if (!found) {
116
+ return { success: false, message: `Entry not found: ${entryId}` };
117
+ }
118
+ await fs.mkdir(archivedPath, { recursive: true });
119
+ const filename = path.basename(found.filePath);
120
+ const newPath = path.join(archivedPath, filename);
121
+ await fs.rename(found.filePath, newPath);
122
+ return {
123
+ success: true,
124
+ message: `Archived to ${path.relative(this.libraryPath, newPath)}`,
125
+ };
126
+ }
127
+ // ============================================================================
128
+ // Private Helpers
129
+ // ============================================================================
130
+ async readEntriesFromPath(dirPath, source) {
131
+ const entries = [];
132
+ try {
133
+ const files = await glob(path.join(dirPath, '**/*.md'), { nodir: true });
134
+ for (const filePath of files) {
135
+ try {
136
+ const content = await fs.readFile(filePath, 'utf-8');
137
+ const { data, content: body } = matter(content);
138
+ let topics;
139
+ if (Array.isArray(data.topics)) {
140
+ topics = data.topics;
141
+ }
142
+ else if (data.topic) {
143
+ topics = [data.topic];
144
+ }
145
+ else {
146
+ topics = ['general'];
147
+ }
148
+ entries.push({
149
+ id: data.id || uuidv4(),
150
+ topics,
151
+ content: body.trim(),
152
+ created: data.created || new Date().toISOString(),
153
+ source,
154
+ origin: data.source,
155
+ imported_from: data.imported_from,
156
+ });
157
+ }
158
+ catch {
159
+ // Skip unreadable files
160
+ }
161
+ }
162
+ }
163
+ catch {
164
+ // Directory doesn't exist
165
+ }
166
+ return entries;
167
+ }
168
+ async findEntryById(dirPath, entryId) {
169
+ try {
170
+ const files = await glob(path.join(dirPath, '**/*.md'), { nodir: true });
171
+ for (const filePath of files) {
172
+ try {
173
+ const content = await fs.readFile(filePath, 'utf-8');
174
+ const { data, content: body } = matter(content);
175
+ if (data.id === entryId) {
176
+ let topics;
177
+ if (Array.isArray(data.topics)) {
178
+ topics = data.topics;
179
+ }
180
+ else if (data.topic) {
181
+ topics = [data.topic];
182
+ }
183
+ else {
184
+ topics = ['general'];
185
+ }
186
+ return {
187
+ entry: {
188
+ id: data.id,
189
+ topics,
190
+ content: body.trim(),
191
+ created: data.created || new Date().toISOString(),
192
+ source: 'local',
193
+ origin: data.source,
194
+ },
195
+ filePath,
196
+ };
197
+ }
198
+ }
199
+ catch {
200
+ // Skip unreadable files
201
+ }
202
+ }
203
+ }
204
+ catch {
205
+ // Directory doesn't exist
206
+ }
207
+ return null;
208
+ }
209
+ async fileExists(filePath) {
210
+ try {
211
+ await fs.access(filePath);
212
+ return true;
213
+ }
214
+ catch {
215
+ return false;
216
+ }
217
+ }
218
+ }
@@ -0,0 +1,15 @@
1
+ import type { ParseResult } from './types.js';
2
+ /**
3
+ * Parse a Cursor Memory Bank folder (.cursor-memory/).
4
+ *
5
+ * Cursor Memory Bank typically contains:
6
+ * - activeContext.md - Current working context
7
+ * - progress.md - Progress log
8
+ * - projectBrief.md - Project overview
9
+ * - systemPatterns.md - System patterns
10
+ * - decisionLog.md - Decision history
11
+ * - techStack.md - Technology stack info
12
+ *
13
+ * Can also contain JSON files and subdirectories.
14
+ */
15
+ export declare function parseCursorMemory(dirPath: string): Promise<ParseResult>;