@telvok/librarian-mcp 1.5.4 → 2.3.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 (123) 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/sensitive-scanner.d.ts +20 -0
  5. package/dist/library/sensitive-scanner.js +56 -0
  6. package/dist/library/storage.d.ts +2 -2
  7. package/dist/library/storage.js +2 -2
  8. package/dist/library 2/embeddings.d.ts +21 -0
  9. package/dist/library 2/embeddings.js +86 -0
  10. package/dist/library 2/manager.d.ts +42 -0
  11. package/dist/library 2/manager.js +218 -0
  12. package/dist/library 2/parsers/cursor.d.ts +15 -0
  13. package/dist/library 2/parsers/cursor.js +168 -0
  14. package/dist/library 2/parsers/index.d.ts +6 -0
  15. package/dist/library 2/parsers/index.js +5 -0
  16. package/dist/library 2/parsers/json.d.ts +11 -0
  17. package/dist/library 2/parsers/json.js +95 -0
  18. package/dist/library 2/parsers/jsonl.d.ts +14 -0
  19. package/dist/library 2/parsers/jsonl.js +85 -0
  20. package/dist/library 2/parsers/markdown.d.ts +15 -0
  21. package/dist/library 2/parsers/markdown.js +77 -0
  22. package/dist/library 2/parsers/sqlite.d.ts +8 -0
  23. package/dist/library 2/parsers/sqlite.js +123 -0
  24. package/dist/library 2/parsers/types.d.ts +21 -0
  25. package/dist/library 2/parsers/types.js +4 -0
  26. package/dist/library 2/query.d.ts +26 -0
  27. package/dist/library 2/query.js +104 -0
  28. package/dist/library 2/schemas.d.ts +324 -0
  29. package/dist/library 2/schemas.js +79 -0
  30. package/dist/library 2/storage.d.ts +22 -0
  31. package/dist/library 2/storage.js +36 -0
  32. package/dist/library 2/vector-index.d.ts +55 -0
  33. package/dist/library 2/vector-index.js +160 -0
  34. package/dist/server 2.js +199 -0
  35. package/dist/server.d 2.ts +2 -0
  36. package/dist/server.js +104 -54
  37. package/dist/tools/adopt.d.ts +1 -0
  38. package/dist/tools/adopt.js +37 -10
  39. package/dist/tools/audit.d.ts +27 -0
  40. package/dist/tools/audit.js +126 -0
  41. package/dist/tools/auth.d.ts +69 -0
  42. package/dist/tools/auth.js +379 -0
  43. package/dist/tools/bounty-claim.d.ts +28 -0
  44. package/dist/tools/bounty-claim.js +92 -0
  45. package/dist/tools/bounty-create.d.ts +47 -0
  46. package/dist/tools/bounty-create.js +118 -0
  47. package/dist/tools/bounty-list.d.ts +50 -0
  48. package/dist/tools/bounty-list.js +116 -0
  49. package/dist/tools/bounty-submit.d.ts +34 -0
  50. package/dist/tools/bounty-submit.js +94 -0
  51. package/dist/tools/brief.d.ts +94 -0
  52. package/dist/tools/brief.js +234 -15
  53. package/dist/tools/delete.d.ts +87 -0
  54. package/dist/tools/delete.js +266 -0
  55. package/dist/tools/feedback.d.ts +27 -0
  56. package/dist/tools/feedback.js +98 -0
  57. package/dist/tools/help.d.ts +22 -0
  58. package/dist/tools/help.js +482 -0
  59. package/dist/tools/import-memories.d.ts +1 -0
  60. package/dist/tools/import-memories.js +18 -13
  61. package/dist/tools/index.d.ts +11 -0
  62. package/dist/tools/index.js +12 -0
  63. package/dist/tools/library-buy.d.ts +31 -0
  64. package/dist/tools/library-buy.js +104 -0
  65. package/dist/tools/library-download.d.ts +27 -0
  66. package/dist/tools/library-download.js +177 -0
  67. package/dist/tools/library-publish.d.ts +117 -0
  68. package/dist/tools/library-publish.js +447 -0
  69. package/dist/tools/library-search.d.ts +110 -0
  70. package/dist/tools/library-search.js +132 -0
  71. package/dist/tools/mark-hit.d.ts +1 -0
  72. package/dist/tools/mark-hit.js +83 -5
  73. package/dist/tools/my-books.d.ts +51 -0
  74. package/dist/tools/my-books.js +115 -0
  75. package/dist/tools/my-bounties.d.ts +43 -0
  76. package/dist/tools/my-bounties.js +126 -0
  77. package/dist/tools/rate-book.d.ts +40 -0
  78. package/dist/tools/rate-book.js +147 -0
  79. package/dist/tools/rebuild-index.d.ts +1 -0
  80. package/dist/tools/rebuild-index.js +40 -8
  81. package/dist/tools/record.d.ts +18 -0
  82. package/dist/tools/record.js +30 -26
  83. package/dist/tools/seller-analytics.d.ts +53 -0
  84. package/dist/tools/seller-analytics.js +180 -0
  85. package/dist/tools/sync.d.ts +55 -0
  86. package/dist/tools/sync.js +304 -0
  87. package/dist/tools/unsubscribe.d.ts +48 -0
  88. package/dist/tools/unsubscribe.js +120 -0
  89. package/dist/tools 2/adopt.d.ts +24 -0
  90. package/dist/tools 2/adopt.js +154 -0
  91. package/dist/tools 2/auth.d.ts +35 -0
  92. package/dist/tools 2/auth.js +229 -0
  93. package/dist/tools 2/brief.d.ts +56 -0
  94. package/dist/tools 2/brief.js +414 -0
  95. package/dist/tools 2/help.d.ts +21 -0
  96. package/dist/tools 2/help.js +267 -0
  97. package/dist/tools 2/import-memories.d.ts +32 -0
  98. package/dist/tools 2/import-memories.js +231 -0
  99. package/dist/tools 2/index.d.ts +12 -0
  100. package/dist/tools 2/index.js +12 -0
  101. package/dist/tools 2/mark-hit.d.ts +20 -0
  102. package/dist/tools 2/mark-hit.js +71 -0
  103. package/dist/tools 2/marketplace-buy.d.ts +30 -0
  104. package/dist/tools 2/marketplace-buy.js +97 -0
  105. package/dist/tools 2/marketplace-download.d.ts +26 -0
  106. package/dist/tools 2/marketplace-download.js +160 -0
  107. package/dist/tools 2/marketplace-publish.d.ts +111 -0
  108. package/dist/tools 2/marketplace-publish.js +377 -0
  109. package/dist/tools 2/marketplace-search.d.ts +57 -0
  110. package/dist/tools 2/marketplace-search.js +96 -0
  111. package/dist/tools 2/my-books.d.ts +50 -0
  112. package/dist/tools 2/my-books.js +107 -0
  113. package/dist/tools 2/rate-book.d.ts +39 -0
  114. package/dist/tools 2/rate-book.js +139 -0
  115. package/dist/tools 2/rebuild-index.d.ts +23 -0
  116. package/dist/tools 2/rebuild-index.js +107 -0
  117. package/dist/tools 2/record.d.ts +40 -0
  118. package/dist/tools 2/record.js +205 -0
  119. package/dist/tools 2/seller-analytics.d.ts +35 -0
  120. package/dist/tools 2/seller-analytics.js +102 -0
  121. package/dist/tools 2/sync.d.ts +54 -0
  122. package/dist/tools 2/sync.js +298 -0
  123. package/package.json +1 -1
@@ -0,0 +1,21 @@
1
+ interface HelpResult {
2
+ topic: string;
3
+ content: string;
4
+ }
5
+ export declare const helpTool: {
6
+ name: string;
7
+ description: string;
8
+ inputSchema: {
9
+ type: "object";
10
+ properties: {
11
+ topic: {
12
+ type: string;
13
+ description: string;
14
+ enum: string[];
15
+ };
16
+ };
17
+ required: never[];
18
+ };
19
+ handler(args: unknown): Promise<HelpResult>;
20
+ };
21
+ export default helpTool;
@@ -0,0 +1,267 @@
1
+ // ============================================================================
2
+ // Help Tool
3
+ // Self-documenting system for the Librarian MCP
4
+ // ============================================================================
5
+ // ============================================================================
6
+ // Help Content
7
+ // ============================================================================
8
+ const HELP_TOPICS = {
9
+ overview: `LIBRARIAN - Memory layer for AI agents
10
+
11
+ Your library lives in .librarian/ with three folders:
12
+ - local/ Your knowledge (created via record())
13
+ - imported/ Downloaded books (via marketplace_buy())
14
+ - packages/ Git-synced packages
15
+
16
+ QUICK START:
17
+ brief({ query: "auth" }) Check what we know before diving in
18
+ record({ insight: "..." }) Save what we learned
19
+ mark_hit({ path: "..." }) This entry helped
20
+
21
+ MARKETPLACE:
22
+ auth({ action: "login" }) Connect your Telvok account
23
+ marketplace_search({ ... }) Find books
24
+ marketplace_buy({ ... }) Purchase or claim a book
25
+ my_books() View your books
26
+ sync() Get updates for owned books
27
+ rate_book({ ... }) Rate a purchased book
28
+
29
+ SELLING:
30
+ marketplace_publish({ ... }) Publish entries as a book
31
+ seller_analytics() View sales and reviews
32
+
33
+ Run help({ topic: "name" }) for details on any tool.`,
34
+ brief: `brief({ query?, limit?, include_marketplace? })
35
+
36
+ Check what we already know before diving in.
37
+
38
+ PARAMETERS:
39
+ query What are you working on? (optional)
40
+ limit Max entries to return (default: 5)
41
+ include_marketplace Also search Telvok (default: false)
42
+
43
+ EXAMPLES:
44
+ brief({ query: "stripe webhooks" })
45
+ brief({ query: "auth", include_marketplace: true })
46
+ brief({}) Returns recent entries
47
+
48
+ RANKING: 60% recency + 40% hits. Entries that helped bubble up.
49
+
50
+ When an entry helps, call mark_hit() so it ranks higher next time.`,
51
+ record: `record({ insight, intent?, reasoning?, context?, example?, title? })
52
+
53
+ Save knowledge worth keeping. Call this proactively!
54
+
55
+ PARAMETERS:
56
+ insight (required) What did we learn?
57
+ intent What were we trying to accomplish?
58
+ reasoning Why does this work?
59
+ context Topic, area, or when this applies
60
+ example Code snippet or illustration
61
+ title Entry title (auto-generated if not provided)
62
+
63
+ EXAMPLES:
64
+ record({ insight: "Stripe webhooks need idempotency checks" })
65
+
66
+ record({
67
+ intent: "Setting up auth flow",
68
+ insight: "Add 30s buffer to token validation for clock skew",
69
+ reasoning: "Server clocks drift between services",
70
+ context: "auth, tokens"
71
+ })
72
+
73
+ QUALITY BAR: "I wish we knew this yesterday"`,
74
+ auth: `auth({ action })
75
+
76
+ Connect to your Telvok marketplace account.
77
+
78
+ ACTIONS:
79
+ login Start device code flow
80
+ complete Finish login after browser authorization
81
+ status Check if authenticated
82
+ logout Remove stored credentials
83
+
84
+ FLOW:
85
+ 1. auth({ action: "login" }) Get code and URL
86
+ 2. Visit URL, authorize in browser
87
+ 3. auth({ action: "complete" }) Finish login
88
+
89
+ Credentials saved in .librarian/.auth`,
90
+ marketplace_search: `marketplace_search({ query, filters? })
91
+
92
+ Search Telvok marketplace for books.
93
+
94
+ PARAMETERS:
95
+ query Search terms
96
+ filters Optional filters:
97
+ - pricing: "open" | "one_time" | "subscription"
98
+ - tags: ["tag1", "tag2"]
99
+ - min_rating: 1-5
100
+
101
+ EXAMPLES:
102
+ marketplace_search({ query: "react patterns" })
103
+ marketplace_search({ query: "auth", filters: { pricing: "open" } })`,
104
+ marketplace_buy: `marketplace_buy({ slug })
105
+
106
+ Purchase or claim a book from Telvok.
107
+
108
+ PARAMETERS:
109
+ slug Book slug from search results
110
+
111
+ BEHAVIOR:
112
+ Free (open) books: Instantly adds to library
113
+ Paid books: Returns checkout URL
114
+
115
+ EXAMPLE:
116
+ marketplace_buy({ slug: "react-best-practices" })
117
+
118
+ After purchase, use sync() for updates.`,
119
+ rate_book: `rate_book({ slug, rating, title?, comment? })
120
+
121
+ Rate a book you've purchased.
122
+
123
+ PARAMETERS:
124
+ slug Book slug to rate
125
+ rating 1 to 5 stars
126
+ title Optional review title
127
+ comment Optional review comment
128
+
129
+ EXAMPLES:
130
+ rate_book({ slug: "auth-patterns", rating: 5 })
131
+ rate_book({
132
+ slug: "stripe-patterns",
133
+ rating: 4,
134
+ title: "Saved me hours",
135
+ comment: "Webhook section was exactly what I needed"
136
+ })
137
+
138
+ Ratings help surface quality content.`,
139
+ sync: `sync({ slug?, force?, include_content? })
140
+
141
+ Check for and receive updates to owned books.
142
+
143
+ PARAMETERS:
144
+ slug Specific book (omit for all)
145
+ force Include manual preference books
146
+ include_content Download open book content
147
+
148
+ SYNC PREFERENCES (per-book):
149
+ auto Synced automatically (default)
150
+ manual Requires force: true
151
+ pinned Never synced
152
+
153
+ EXAMPLES:
154
+ sync()
155
+ sync({ slug: "premium-patterns" })
156
+ sync({ force: true })`,
157
+ my_books: `my_books({ filter? })
158
+
159
+ View your published and purchased books.
160
+
161
+ PARAMETERS:
162
+ filter "all" (default), "published", or "purchased"
163
+
164
+ EXAMPLES:
165
+ my_books()
166
+ my_books({ filter: "published" })
167
+
168
+ Shows slugs for sync(), rate_book(), etc.`,
169
+ seller_analytics: `seller_analytics()
170
+
171
+ View analytics for books you've published.
172
+
173
+ SHOWS:
174
+ - Total revenue and purchases
175
+ - Per-book breakdown
176
+ - Recent reviews
177
+ - Hit counts (query frequency)
178
+
179
+ Requires authentication.`,
180
+ mark_hit: `mark_hit({ path })
181
+
182
+ Mark an entry as helpful. Call when brief() helped.
183
+
184
+ PARAMETERS:
185
+ path Path to entry (from brief() results)
186
+
187
+ EXAMPLE:
188
+ mark_hit({ path: "local/stripe-webhooks.md" })
189
+
190
+ Fire and forget. Entries with more hits rank higher.`,
191
+ adopt: `adopt({ path, title? })
192
+
193
+ Make imported knowledge yours.
194
+
195
+ When an imported entry proves useful, adopt it into local/.
196
+
197
+ PARAMETERS:
198
+ path Path to entry (e.g., "imported/package/entry")
199
+ title New title (optional)
200
+
201
+ EXAMPLE:
202
+ adopt({ path: "imported/stripe-patterns/webhooks" })`,
203
+ marketplace_publish: `marketplace_publish({ name, description?, pricing, entries?, attestation })
204
+
205
+ Publish local entries as a book on Telvok.
206
+
207
+ PARAMETERS:
208
+ name Book name
209
+ description Short description
210
+ pricing { type: "open" | "one_time" | "subscription", price_cents? }
211
+ entries Entry paths to include (default: all local/)
212
+ attestation { original_work: true, terms_accepted: true }
213
+
214
+ EXAMPLES:
215
+ marketplace_publish({
216
+ name: "React Patterns",
217
+ pricing: { type: "open" },
218
+ attestation: { original_work: true, terms_accepted: true }
219
+ })
220
+
221
+ Paid books require Stripe Connect setup.`,
222
+ };
223
+ // ============================================================================
224
+ // Tool Definition
225
+ // ============================================================================
226
+ export const helpTool = {
227
+ name: 'help',
228
+ description: `Get help on how to use the Librarian.
229
+
230
+ Run with no arguments for an overview, or specify a topic for details.
231
+
232
+ Examples:
233
+ - help() Overview of all tools
234
+ - help({ topic: "brief" }) Details on brief()
235
+ - help({ topic: "auth" }) Details on authentication`,
236
+ inputSchema: {
237
+ type: 'object',
238
+ properties: {
239
+ topic: {
240
+ type: 'string',
241
+ description: 'Specific topic to get help on',
242
+ enum: Object.keys(HELP_TOPICS),
243
+ },
244
+ },
245
+ required: [],
246
+ },
247
+ async handler(args) {
248
+ const { topic } = (args || {});
249
+ const helpTopic = topic?.toLowerCase() || 'overview';
250
+ const content = HELP_TOPICS[helpTopic];
251
+ if (!content) {
252
+ const availableTopics = Object.keys(HELP_TOPICS).filter(t => t !== 'overview').join(', ');
253
+ return {
254
+ topic: 'error',
255
+ content: `Unknown topic: "${topic}"\n\nAvailable topics: ${availableTopics}`,
256
+ };
257
+ }
258
+ return {
259
+ topic: helpTopic,
260
+ content: content.trim(),
261
+ };
262
+ },
263
+ };
264
+ // ============================================================================
265
+ // Export
266
+ // ============================================================================
267
+ export default helpTool;
@@ -0,0 +1,32 @@
1
+ export interface ImportResult {
2
+ success: boolean;
3
+ imported: number;
4
+ skipped: number;
5
+ errors: string[];
6
+ outputPath: string;
7
+ message: string;
8
+ }
9
+ export declare const importMemoriesTool: {
10
+ name: string;
11
+ description: string;
12
+ inputSchema: {
13
+ type: "object";
14
+ properties: {
15
+ format: {
16
+ type: string;
17
+ enum: string[];
18
+ description: string;
19
+ };
20
+ path: {
21
+ type: string;
22
+ description: string;
23
+ };
24
+ source_name: {
25
+ type: string;
26
+ description: string;
27
+ };
28
+ };
29
+ required: string[];
30
+ };
31
+ handler(args: unknown): Promise<ImportResult>;
32
+ };
@@ -0,0 +1,231 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import { getLibraryPath, getLocalPath } from '../library/storage.js';
4
+ import { loadIndex, saveIndex, addToIndex } from '../library/vector-index.js';
5
+ import { parseJSONL, parseMarkdown, parseCursorMemory, parseJSON, parseSQLite } from '../library/parsers/index.js';
6
+ // ============================================================================
7
+ // Tool Definition
8
+ // ============================================================================
9
+ export const importMemoriesTool = {
10
+ name: 'import_memories',
11
+ description: `Import memories from other AI tools into Librarian.
12
+
13
+ Use this when you see memory files from other AI tools (Cursor .cursor-memory/, Anthropic ~/.aim/, mcp-memory-service .db files, Obsidian vaults) or when user mentions having knowledge in other tools. Import them so past knowledge becomes searchable.
14
+
15
+ Supported formats:
16
+ - jsonl: Anthropic MCP Memory, mcp-knowledge-graph (.jsonl files)
17
+ - markdown: Basic Memory, Obsidian, any .md files
18
+ - cursor: Cursor Memory Bank (.cursor-memory/)
19
+ - json: Simple memory servers, knowledge stores (.json files)
20
+ - sqlite: mcp-memory-service, SQLite-vec (.db, .sqlite files)
21
+
22
+ Imports go to .librarian/local/[source-name]/ and are automatically indexed for semantic search.
23
+
24
+ Examples:
25
+ - import_memories({ format: "jsonl", path: "~/.aim/memory.jsonl", source_name: "anthropic-memory" })
26
+ - import_memories({ format: "markdown", path: "~/basic-memory/", source_name: "basic-memory" })
27
+ - import_memories({ format: "cursor", path: ".cursor-memory/", source_name: "cursor-memory" })
28
+ - import_memories({ format: "json", path: "~/memories.json", source_name: "json-memory" })
29
+ - import_memories({ format: "sqlite", path: "~/memory.db", source_name: "sqlite-memory" })`,
30
+ inputSchema: {
31
+ type: 'object',
32
+ properties: {
33
+ format: {
34
+ type: 'string',
35
+ enum: ['jsonl', 'markdown', 'cursor', 'json', 'sqlite'],
36
+ description: 'Format of the source memories',
37
+ },
38
+ path: {
39
+ type: 'string',
40
+ description: 'Path to memory file or folder',
41
+ },
42
+ source_name: {
43
+ type: 'string',
44
+ description: 'Name for the import folder (e.g., "anthropic-memory"). Auto-generated if not provided.',
45
+ },
46
+ },
47
+ required: ['format', 'path'],
48
+ },
49
+ async handler(args) {
50
+ const { format, path: inputPath, source_name } = args;
51
+ if (!format || !inputPath) {
52
+ throw new Error('format and path are required');
53
+ }
54
+ // Expand ~ to home directory
55
+ const expandedPath = inputPath.replace(/^~/, process.env.HOME || '');
56
+ // Generate source name if not provided
57
+ const sourceName = source_name || generateSourceName(format, expandedPath);
58
+ // Setup output directory
59
+ const libraryPath = getLibraryPath();
60
+ const localPath = getLocalPath(libraryPath);
61
+ const outputPath = path.join(localPath, sourceName);
62
+ // Check if output directory already exists
63
+ try {
64
+ await fs.access(outputPath);
65
+ // Directory exists - we'll add to it but warn about potential duplicates
66
+ }
67
+ catch {
68
+ // Directory doesn't exist - create it
69
+ await fs.mkdir(outputPath, { recursive: true });
70
+ }
71
+ // Parse the source based on format
72
+ let parseResult;
73
+ switch (format) {
74
+ case 'jsonl':
75
+ parseResult = await parseJSONL(expandedPath);
76
+ break;
77
+ case 'markdown':
78
+ parseResult = await parseMarkdown(expandedPath);
79
+ break;
80
+ case 'cursor':
81
+ parseResult = await parseCursorMemory(expandedPath);
82
+ break;
83
+ case 'json':
84
+ parseResult = await parseJSON(expandedPath);
85
+ break;
86
+ case 'sqlite':
87
+ parseResult = await parseSQLite(expandedPath);
88
+ break;
89
+ default:
90
+ throw new Error(`Unknown format: ${format}`);
91
+ }
92
+ // Convert and save entries
93
+ const index = await loadIndex();
94
+ let imported = 0;
95
+ const errors = [...parseResult.errors];
96
+ for (const entry of parseResult.entries) {
97
+ try {
98
+ const relativePath = await saveEntry(entry, outputPath, libraryPath);
99
+ // Add to vector index
100
+ const fullContent = [
101
+ entry.title,
102
+ entry.intent || '',
103
+ entry.content,
104
+ entry.reasoning || '',
105
+ entry.example || '',
106
+ entry.context || '',
107
+ ].filter(Boolean).join('\n\n');
108
+ await addToIndex(index, relativePath, entry.title, fullContent);
109
+ imported++;
110
+ }
111
+ catch (saveError) {
112
+ errors.push(`Failed to save "${entry.title}": ${saveError instanceof Error ? saveError.message : String(saveError)}`);
113
+ }
114
+ }
115
+ // Save the updated index
116
+ await saveIndex(index);
117
+ const message = imported > 0
118
+ ? `Imported ${imported} entries from ${format} format into ${sourceName}/`
119
+ : `No entries imported. ${parseResult.skipped} skipped, ${errors.length} errors.`;
120
+ return {
121
+ success: imported > 0,
122
+ imported,
123
+ skipped: parseResult.skipped,
124
+ errors,
125
+ outputPath: path.relative(libraryPath, outputPath),
126
+ message,
127
+ };
128
+ },
129
+ };
130
+ // ============================================================================
131
+ // Helper Functions
132
+ // ============================================================================
133
+ /**
134
+ * Generate a source name from the format and path.
135
+ */
136
+ function generateSourceName(format, inputPath) {
137
+ const basename = path.basename(inputPath, path.extname(inputPath));
138
+ switch (format) {
139
+ case 'jsonl':
140
+ if (basename.includes('memory')) {
141
+ return 'imported-memory';
142
+ }
143
+ return `imported-${basename}`;
144
+ case 'cursor':
145
+ return 'cursor-memory';
146
+ case 'markdown':
147
+ return basename.replace(/[^a-z0-9-]/gi, '-').toLowerCase() || 'imported-markdown';
148
+ case 'json':
149
+ return basename.replace(/[^a-z0-9-]/gi, '-').toLowerCase() || 'imported-json';
150
+ case 'sqlite':
151
+ return basename.replace(/[^a-z0-9-]/gi, '-').toLowerCase() || 'imported-sqlite';
152
+ default:
153
+ return `imported-${format}`;
154
+ }
155
+ }
156
+ /**
157
+ * Save a parsed entry to the output directory.
158
+ * Returns the relative path from library root.
159
+ */
160
+ async function saveEntry(entry, outputPath, libraryPath) {
161
+ const slug = slugify(entry.title);
162
+ const created = new Date().toISOString();
163
+ // Handle filename collisions
164
+ let filename = `${slug}.md`;
165
+ let filePath = path.join(outputPath, filename);
166
+ let counter = 1;
167
+ while (await fileExists(filePath)) {
168
+ filename = `${slug}-${counter}.md`;
169
+ filePath = path.join(outputPath, filename);
170
+ counter++;
171
+ }
172
+ // Build frontmatter
173
+ const frontmatterLines = ['---'];
174
+ if (entry.intent) {
175
+ frontmatterLines.push(`intent: "${escapeYaml(entry.intent)}"`);
176
+ }
177
+ if (entry.context) {
178
+ frontmatterLines.push(`context: "${escapeYaml(entry.context)}"`);
179
+ }
180
+ frontmatterLines.push(`created: "${created}"`);
181
+ frontmatterLines.push(`updated: "${created}"`);
182
+ frontmatterLines.push(`source: "${entry.source}"`);
183
+ if (entry.originalPath) {
184
+ frontmatterLines.push(`original_path: "${escapeYaml(entry.originalPath)}"`);
185
+ }
186
+ frontmatterLines.push('hits: 0');
187
+ frontmatterLines.push('last_hit: null');
188
+ frontmatterLines.push('---');
189
+ // Build body
190
+ const bodyLines = [];
191
+ bodyLines.push(`# ${entry.title}`);
192
+ bodyLines.push('');
193
+ bodyLines.push(entry.content);
194
+ if (entry.reasoning) {
195
+ bodyLines.push('');
196
+ bodyLines.push('## Reasoning');
197
+ bodyLines.push('');
198
+ bodyLines.push(entry.reasoning);
199
+ }
200
+ if (entry.example) {
201
+ bodyLines.push('');
202
+ bodyLines.push('## Example');
203
+ bodyLines.push('');
204
+ bodyLines.push('```');
205
+ bodyLines.push(entry.example);
206
+ bodyLines.push('```');
207
+ }
208
+ // Combine and write
209
+ const fileContent = frontmatterLines.join('\n') + '\n\n' + bodyLines.join('\n') + '\n';
210
+ await fs.writeFile(filePath, fileContent, 'utf-8');
211
+ return path.relative(libraryPath, filePath);
212
+ }
213
+ function slugify(text) {
214
+ return text
215
+ .toLowerCase()
216
+ .replace(/[^a-z0-9]+/g, '-')
217
+ .replace(/^-+|-+$/g, '')
218
+ .slice(0, 50);
219
+ }
220
+ function escapeYaml(text) {
221
+ return text.replace(/"/g, '\\"').replace(/\n/g, ' ');
222
+ }
223
+ async function fileExists(filePath) {
224
+ try {
225
+ await fs.access(filePath);
226
+ return true;
227
+ }
228
+ catch {
229
+ return false;
230
+ }
231
+ }
@@ -0,0 +1,12 @@
1
+ export { briefTool } from './brief.js';
2
+ export { recordTool } from './record.js';
3
+ export { adoptTool } from './adopt.js';
4
+ export { markHitTool } from './mark-hit.js';
5
+ export { importMemoriesTool } from './import-memories.js';
6
+ export { rebuildIndexTool } from './rebuild-index.js';
7
+ export { authTool, loadApiKey } from './auth.js';
8
+ export { myBooksTool } from './my-books.js';
9
+ export { syncTool } from './sync.js';
10
+ export { sellerAnalyticsTool } from './seller-analytics.js';
11
+ export { rateBookTool } from './rate-book.js';
12
+ export { helpTool } from './help.js';
@@ -0,0 +1,12 @@
1
+ export { briefTool } from './brief.js';
2
+ export { recordTool } from './record.js';
3
+ export { adoptTool } from './adopt.js';
4
+ export { markHitTool } from './mark-hit.js';
5
+ export { importMemoriesTool } from './import-memories.js';
6
+ export { rebuildIndexTool } from './rebuild-index.js';
7
+ export { authTool, loadApiKey } from './auth.js';
8
+ export { myBooksTool } from './my-books.js';
9
+ export { syncTool } from './sync.js';
10
+ export { sellerAnalyticsTool } from './seller-analytics.js';
11
+ export { rateBookTool } from './rate-book.js';
12
+ export { helpTool } from './help.js';
@@ -0,0 +1,20 @@
1
+ export interface MarkHitResult {
2
+ success: boolean;
3
+ path: string;
4
+ hits: number;
5
+ }
6
+ export declare const markHitTool: {
7
+ name: string;
8
+ description: string;
9
+ inputSchema: {
10
+ type: "object";
11
+ properties: {
12
+ path: {
13
+ type: string;
14
+ description: string;
15
+ };
16
+ };
17
+ required: string[];
18
+ };
19
+ handler(args: unknown): Promise<MarkHitResult>;
20
+ };
@@ -0,0 +1,71 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import matter from 'gray-matter';
4
+ import { getLibraryPath } from '../library/storage.js';
5
+ // ============================================================================
6
+ // Tool Definition
7
+ // ============================================================================
8
+ export const markHitTool = {
9
+ name: 'mark_hit',
10
+ description: `Mark a library entry as helpful - call this when knowledge from the library helped solve a problem.
11
+
12
+ When an entry from brief() actually helped you complete a task or make a decision,
13
+ call mark_hit() on it. This helps the library learn which entries are most useful.
14
+
15
+ Entries with more hits bubble up in future brief() results.
16
+
17
+ Fire and forget - call it and move on.
18
+
19
+ Example:
20
+ - mark_hit({ path: "local/stripe-webhooks-need-idempotency.md" })`,
21
+ inputSchema: {
22
+ type: 'object',
23
+ properties: {
24
+ path: {
25
+ type: 'string',
26
+ description: 'Path to the entry that helped (from brief() results)',
27
+ },
28
+ },
29
+ required: ['path'],
30
+ },
31
+ async handler(args) {
32
+ const { path: entryPath } = args;
33
+ if (!entryPath) {
34
+ throw new Error('path is required');
35
+ }
36
+ const libraryPath = getLibraryPath();
37
+ // Resolve the full path
38
+ let fullPath;
39
+ if (path.isAbsolute(entryPath)) {
40
+ fullPath = entryPath;
41
+ }
42
+ else {
43
+ fullPath = path.join(libraryPath, entryPath);
44
+ }
45
+ // Read existing file
46
+ let content;
47
+ try {
48
+ content = await fs.readFile(fullPath, 'utf-8');
49
+ }
50
+ catch {
51
+ throw new Error(`Entry not found: ${entryPath}`);
52
+ }
53
+ // Parse frontmatter
54
+ const { data, content: body } = matter(content);
55
+ // Increment hits
56
+ const currentHits = typeof data.hits === 'number' ? data.hits : 0;
57
+ const newHits = currentHits + 1;
58
+ // Update frontmatter
59
+ data.hits = newHits;
60
+ data.last_hit = new Date().toISOString();
61
+ // Rebuild file content
62
+ const updatedContent = matter.stringify(body, data);
63
+ // Write back
64
+ await fs.writeFile(fullPath, updatedContent, 'utf-8');
65
+ return {
66
+ success: true,
67
+ path: entryPath,
68
+ hits: newHits,
69
+ };
70
+ },
71
+ };