@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,54 @@
1
+ interface SyncedBook {
2
+ slug: string;
3
+ name: string;
4
+ new_entries: number;
5
+ modified_entries: number;
6
+ access: 'cloud' | 'downloaded';
7
+ version?: string;
8
+ synced_from?: string;
9
+ }
10
+ interface SyncResult {
11
+ success: boolean;
12
+ message: string;
13
+ synced?: SyncedBook[];
14
+ available?: {
15
+ slug: string;
16
+ name: string;
17
+ new_entries: number;
18
+ }[];
19
+ pinned?: {
20
+ slug: string;
21
+ name: string;
22
+ }[];
23
+ up_to_date?: number;
24
+ }
25
+ export declare const syncTool: {
26
+ name: string;
27
+ description: string;
28
+ inputSchema: {
29
+ type: "object";
30
+ properties: {
31
+ slug: {
32
+ type: string;
33
+ description: string;
34
+ };
35
+ options: {
36
+ type: string;
37
+ properties: {
38
+ force: {
39
+ type: string;
40
+ description: string;
41
+ };
42
+ download: {
43
+ type: string;
44
+ description: string;
45
+ };
46
+ };
47
+ description: string;
48
+ };
49
+ };
50
+ required: never[];
51
+ };
52
+ handler(args: unknown): Promise<SyncResult>;
53
+ };
54
+ export default syncTool;
@@ -0,0 +1,298 @@
1
+ // ============================================================================
2
+ // Sync Tool
3
+ // Check for and receive updates to owned books from Telvok marketplace
4
+ // ============================================================================
5
+ import * as fs from 'fs/promises';
6
+ import * as path from 'path';
7
+ import { loadApiKey } from './auth.js';
8
+ import { getLibraryPath, getImportedPath } from '../library/storage.js';
9
+ const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
10
+ // ============================================================================
11
+ // Tool Definition
12
+ // ============================================================================
13
+ export const syncTool = {
14
+ name: 'sync',
15
+ description: `Check for and receive updates to owned books from Telvok marketplace.
16
+
17
+ Checks all purchased/claimed books for new or modified content since last sync.
18
+
19
+ Sync preferences:
20
+ - "auto": Synced automatically (default)
21
+ - "manual": Shows as available, requires force: true to sync
22
+ - "pinned": Never synced, locked to specific version
23
+
24
+ Examples:
25
+ - sync() - Check and sync all auto-sync books
26
+ - sync({ slug: "premium-patterns" }) - Sync specific book
27
+ - sync({ options: { force: true } }) - Include manual preference books
28
+ - sync({ slug: "free-book", options: { download: true } }) - Download open book updates`,
29
+ inputSchema: {
30
+ type: 'object',
31
+ properties: {
32
+ slug: {
33
+ type: 'string',
34
+ description: 'Specific book slug to sync (omit for all)',
35
+ },
36
+ options: {
37
+ type: 'object',
38
+ properties: {
39
+ force: {
40
+ type: 'boolean',
41
+ description: 'Include manual preference books (default: false)',
42
+ },
43
+ download: {
44
+ type: 'boolean',
45
+ description: 'Download open book updates to local library (default: false)',
46
+ },
47
+ },
48
+ description: 'Sync options',
49
+ },
50
+ },
51
+ required: [],
52
+ },
53
+ async handler(args) {
54
+ const { slug, options } = (args || {});
55
+ const force = options?.force || false;
56
+ const download = options?.download || false;
57
+ // Check authentication
58
+ const apiKey = await loadApiKey();
59
+ if (!apiKey) {
60
+ return {
61
+ success: false,
62
+ message: 'Not authenticated. Run auth({ action: "login" }) to connect your Telvok account first.',
63
+ };
64
+ }
65
+ try {
66
+ // Step 1: Check for available updates
67
+ const checkUrl = new URL(`${TELVOK_API_URL}/api/sync`);
68
+ if (slug) {
69
+ checkUrl.searchParams.set('slug', slug);
70
+ }
71
+ const checkResponse = await fetch(checkUrl.toString(), {
72
+ method: 'GET',
73
+ headers: {
74
+ 'Authorization': `Bearer ${apiKey}`,
75
+ },
76
+ });
77
+ const checkData = await checkResponse.json();
78
+ if (!checkResponse.ok) {
79
+ return {
80
+ success: false,
81
+ message: checkData.error || `Failed to check for updates: HTTP ${checkResponse.status}`,
82
+ };
83
+ }
84
+ // If no updates available
85
+ const updatesAvailable = checkData.updates_available || [];
86
+ const upToDate = checkData.up_to_date || [];
87
+ const pinned = checkData.pinned || [];
88
+ if (updatesAvailable.length === 0) {
89
+ const totalBooks = upToDate.length + pinned.length;
90
+ let message = '';
91
+ if (totalBooks === 0) {
92
+ message = 'No books to sync. Purchase or claim books first.';
93
+ }
94
+ else if (pinned.length > 0) {
95
+ message = `${upToDate.length} book${upToDate.length === 1 ? ' is' : 's are'} up to date. ${pinned.length} pinned (won't sync).`;
96
+ }
97
+ else {
98
+ message = `All ${totalBooks} book${totalBooks === 1 ? ' is' : 's are'} up to date.`;
99
+ }
100
+ return {
101
+ success: true,
102
+ message,
103
+ pinned: pinned.length > 0 ? pinned.map((p) => ({ slug: p.slug, name: p.name })) : undefined,
104
+ up_to_date: upToDate.length,
105
+ };
106
+ }
107
+ // Step 2: Perform sync
108
+ const syncResponse = await fetch(`${TELVOK_API_URL}/api/sync`, {
109
+ method: 'POST',
110
+ headers: {
111
+ 'Authorization': `Bearer ${apiKey}`,
112
+ 'Content-Type': 'application/json',
113
+ },
114
+ body: JSON.stringify({
115
+ slug,
116
+ force,
117
+ include_content: download,
118
+ }),
119
+ });
120
+ const syncData = await syncResponse.json();
121
+ if (!syncResponse.ok) {
122
+ return {
123
+ success: false,
124
+ message: syncData.error || `Failed to sync: HTTP ${syncResponse.status}`,
125
+ };
126
+ }
127
+ const synced = [];
128
+ const available = [];
129
+ const pinnedBooks = [];
130
+ // Process synced books
131
+ for (const book of syncData.synced || []) {
132
+ const newCount = typeof book.new_entries === 'number' ? book.new_entries : book.new_entries?.length || 0;
133
+ const modifiedCount = typeof book.modified_entries === 'number' ? book.modified_entries : book.modified_entries?.length || 0;
134
+ // If download requested and content provided, save to local
135
+ if (download && book.access_method === 'download' && Array.isArray(book.new_entries)) {
136
+ await saveEntriesToLocal(book.slug, book.new_entries, book.modified_entries);
137
+ }
138
+ synced.push({
139
+ slug: book.slug,
140
+ name: book.name,
141
+ new_entries: newCount,
142
+ modified_entries: modifiedCount,
143
+ access: download && book.pricing_type === 'open' ? 'downloaded' : 'cloud',
144
+ version: formatVersion(book.new_version),
145
+ synced_from: formatVersion(book.previous_version),
146
+ });
147
+ }
148
+ // Process skipped books
149
+ for (const book of syncData.skipped || []) {
150
+ if (book.reason === 'manual_no_force') {
151
+ const update = updatesAvailable.find((u) => u.slug === book.slug);
152
+ if (update) {
153
+ available.push({
154
+ slug: book.slug,
155
+ name: book.name,
156
+ new_entries: update.new_entries_count,
157
+ });
158
+ }
159
+ }
160
+ else if (book.reason === 'pinned') {
161
+ pinnedBooks.push({
162
+ slug: book.slug,
163
+ name: book.name,
164
+ });
165
+ }
166
+ }
167
+ // Build summary message
168
+ const messageParts = [];
169
+ const totalNewEntries = synced.reduce((sum, b) => sum + b.new_entries, 0);
170
+ const totalModified = synced.reduce((sum, b) => sum + b.modified_entries, 0);
171
+ if (synced.length > 0) {
172
+ let syncMsg = `Synced ${synced.length} book${synced.length === 1 ? '' : 's'}`;
173
+ if (totalNewEntries > 0 || totalModified > 0) {
174
+ const parts = [];
175
+ if (totalNewEntries > 0)
176
+ parts.push(`${totalNewEntries} new`);
177
+ if (totalModified > 0)
178
+ parts.push(`${totalModified} modified`);
179
+ syncMsg += `: ${parts.join(', ')} entries.`;
180
+ }
181
+ else {
182
+ syncMsg += '.';
183
+ }
184
+ messageParts.push(syncMsg);
185
+ }
186
+ if (available.length > 0) {
187
+ messageParts.push(`${available.length} book${available.length === 1 ? ' has' : 's have'} updates available (set to manual).`);
188
+ }
189
+ if (pinnedBooks.length > 0) {
190
+ messageParts.push(`${pinnedBooks.length} book${pinnedBooks.length === 1 ? '' : 's'} pinned (won't sync).`);
191
+ }
192
+ if (messageParts.length === 0) {
193
+ if (upToDate.length > 0) {
194
+ messageParts.push(`All ${upToDate.length} book${upToDate.length === 1 ? ' is' : 's are'} up to date.`);
195
+ }
196
+ else {
197
+ messageParts.push('No books to sync. Purchase or claim books first.');
198
+ }
199
+ }
200
+ return {
201
+ success: true,
202
+ message: messageParts.join(' '),
203
+ synced: synced.length > 0 ? synced : undefined,
204
+ available: available.length > 0 ? available : undefined,
205
+ pinned: pinnedBooks.length > 0 ? pinnedBooks : undefined,
206
+ up_to_date: upToDate.length,
207
+ };
208
+ }
209
+ catch (error) {
210
+ const message = error instanceof Error ? error.message : String(error);
211
+ throw new Error(`Sync failed: ${message}`);
212
+ }
213
+ },
214
+ };
215
+ // ============================================================================
216
+ // Helper: Save entries to local imported folder
217
+ // ============================================================================
218
+ async function saveEntriesToLocal(slug, newEntries, modifiedEntries) {
219
+ const libraryPath = getLibraryPath();
220
+ const importedPath = getImportedPath(libraryPath);
221
+ const bookPath = path.join(importedPath, slug);
222
+ // Ensure directory exists
223
+ await fs.mkdir(bookPath, { recursive: true });
224
+ // Save new entries
225
+ for (const entry of newEntries || []) {
226
+ const filename = slugify(entry.title) + '.md';
227
+ const content = formatEntryAsMarkdown(entry);
228
+ await fs.writeFile(path.join(bookPath, filename), content, 'utf-8');
229
+ }
230
+ // Save modified entries (overwrite)
231
+ for (const entry of modifiedEntries || []) {
232
+ const filename = slugify(entry.title) + '.md';
233
+ const content = formatEntryAsMarkdown(entry);
234
+ await fs.writeFile(path.join(bookPath, filename), content, 'utf-8');
235
+ }
236
+ // Update .meta.json
237
+ const metaPath = path.join(bookPath, '.meta.json');
238
+ let meta = {};
239
+ try {
240
+ const existing = await fs.readFile(metaPath, 'utf-8');
241
+ meta = JSON.parse(existing);
242
+ }
243
+ catch {
244
+ // No existing meta
245
+ }
246
+ meta.last_synced = new Date().toISOString();
247
+ await fs.writeFile(metaPath, JSON.stringify(meta, null, 2), 'utf-8');
248
+ }
249
+ function slugify(text) {
250
+ return text
251
+ .toLowerCase()
252
+ .replace(/[^a-z0-9]+/g, '-')
253
+ .replace(/^-|-$/g, '')
254
+ .substring(0, 50);
255
+ }
256
+ /**
257
+ * Format a timestamp as a human-readable version string
258
+ */
259
+ function formatVersion(isoTimestamp) {
260
+ if (!isoTimestamp)
261
+ return 'never synced';
262
+ const date = new Date(isoTimestamp);
263
+ const now = new Date();
264
+ const diffMs = now.getTime() - date.getTime();
265
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
266
+ if (diffDays === 0) {
267
+ return `today at ${date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}`;
268
+ }
269
+ else if (diffDays === 1) {
270
+ return 'yesterday';
271
+ }
272
+ else if (diffDays < 7) {
273
+ return `${diffDays} days ago`;
274
+ }
275
+ else {
276
+ return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
277
+ }
278
+ }
279
+ function formatEntryAsMarkdown(entry) {
280
+ let md = '';
281
+ // Frontmatter
282
+ if (entry.intent || entry.context) {
283
+ md += '---\n';
284
+ if (entry.intent)
285
+ md += `intent: "${entry.intent}"\n`;
286
+ if (entry.context)
287
+ md += `context: "${entry.context}"\n`;
288
+ md += '---\n\n';
289
+ }
290
+ // Title and content
291
+ md += `# ${entry.title}\n\n`;
292
+ md += entry.content;
293
+ return md;
294
+ }
295
+ // ============================================================================
296
+ // Export
297
+ // ============================================================================
298
+ export default syncTool;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telvok/librarian-mcp",
3
- "version": "1.5.4",
3
+ "version": "2.0.0",
4
4
  "description": "Knowledge capture MCP server - remember what you learn with AI",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",