@telvok/librarian-mcp 1.5.3 → 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.
- package/dist/library/errors.d.ts +48 -0
- package/dist/library/errors.js +80 -0
- package/dist/library/parsers/jsonl.d.ts +9 -4
- package/dist/library/parsers/jsonl.js +52 -20
- package/dist/library/schemas.d.ts +6 -6
- package/dist/library/storage.d.ts +2 -2
- package/dist/library/storage.js +2 -2
- package/dist/library 2/embeddings.d.ts +21 -0
- package/dist/library 2/embeddings.js +86 -0
- package/dist/library 2/manager.d.ts +42 -0
- package/dist/library 2/manager.js +218 -0
- package/dist/library 2/parsers/cursor.d.ts +15 -0
- package/dist/library 2/parsers/cursor.js +168 -0
- package/dist/library 2/parsers/index.d.ts +6 -0
- package/dist/library 2/parsers/index.js +5 -0
- package/dist/library 2/parsers/json.d.ts +11 -0
- package/dist/library 2/parsers/json.js +95 -0
- package/dist/library 2/parsers/jsonl.d.ts +14 -0
- package/dist/library 2/parsers/jsonl.js +85 -0
- package/dist/library 2/parsers/markdown.d.ts +15 -0
- package/dist/library 2/parsers/markdown.js +77 -0
- package/dist/library 2/parsers/sqlite.d.ts +8 -0
- package/dist/library 2/parsers/sqlite.js +123 -0
- package/dist/library 2/parsers/types.d.ts +21 -0
- package/dist/library 2/parsers/types.js +4 -0
- package/dist/library 2/query.d.ts +26 -0
- package/dist/library 2/query.js +104 -0
- package/dist/library 2/schemas.d.ts +324 -0
- package/dist/library 2/schemas.js +79 -0
- package/dist/library 2/storage.d.ts +22 -0
- package/dist/library 2/storage.js +36 -0
- package/dist/library 2/vector-index.d.ts +55 -0
- package/dist/library 2/vector-index.js +160 -0
- package/dist/server 2.js +199 -0
- package/dist/server.d 2.ts +2 -0
- package/dist/server.js +102 -54
- package/dist/tools/adopt.d.ts +1 -0
- package/dist/tools/adopt.js +37 -10
- package/dist/tools/auth.d.ts +69 -0
- package/dist/tools/auth.js +379 -0
- package/dist/tools/bounty-claim.d.ts +28 -0
- package/dist/tools/bounty-claim.js +92 -0
- package/dist/tools/bounty-create.d.ts +47 -0
- package/dist/tools/bounty-create.js +118 -0
- package/dist/tools/bounty-list.d.ts +50 -0
- package/dist/tools/bounty-list.js +116 -0
- package/dist/tools/bounty-submit.d.ts +34 -0
- package/dist/tools/bounty-submit.js +94 -0
- package/dist/tools/brief.d.ts +94 -0
- package/dist/tools/brief.js +234 -15
- package/dist/tools/delete.d.ts +87 -0
- package/dist/tools/delete.js +266 -0
- package/dist/tools/feedback.d.ts +27 -0
- package/dist/tools/feedback.js +98 -0
- package/dist/tools/help.d.ts +22 -0
- package/dist/tools/help.js +482 -0
- package/dist/tools/import-memories.d.ts +1 -0
- package/dist/tools/import-memories.js +18 -13
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.js +12 -0
- package/dist/tools/library-buy.d.ts +31 -0
- package/dist/tools/library-buy.js +104 -0
- package/dist/tools/library-download.d.ts +27 -0
- package/dist/tools/library-download.js +177 -0
- package/dist/tools/library-publish.d.ts +112 -0
- package/dist/tools/library-publish.js +387 -0
- package/dist/tools/library-search.d.ts +110 -0
- package/dist/tools/library-search.js +132 -0
- package/dist/tools/mark-hit.d.ts +1 -0
- package/dist/tools/mark-hit.js +83 -5
- package/dist/tools/my-books.d.ts +51 -0
- package/dist/tools/my-books.js +115 -0
- package/dist/tools/my-bounties.d.ts +43 -0
- package/dist/tools/my-bounties.js +126 -0
- package/dist/tools/rate-book.d.ts +40 -0
- package/dist/tools/rate-book.js +147 -0
- package/dist/tools/rebuild-index.d.ts +1 -0
- package/dist/tools/rebuild-index.js +40 -8
- package/dist/tools/record.d.ts +18 -0
- package/dist/tools/record.js +30 -26
- package/dist/tools/seller-analytics.d.ts +53 -0
- package/dist/tools/seller-analytics.js +180 -0
- package/dist/tools/sync.d.ts +55 -0
- package/dist/tools/sync.js +304 -0
- package/dist/tools/unsubscribe.d.ts +48 -0
- package/dist/tools/unsubscribe.js +120 -0
- package/dist/tools 2/adopt.d.ts +24 -0
- package/dist/tools 2/adopt.js +154 -0
- package/dist/tools 2/auth.d.ts +35 -0
- package/dist/tools 2/auth.js +229 -0
- package/dist/tools 2/brief.d.ts +56 -0
- package/dist/tools 2/brief.js +414 -0
- package/dist/tools 2/help.d.ts +21 -0
- package/dist/tools 2/help.js +267 -0
- package/dist/tools 2/import-memories.d.ts +32 -0
- package/dist/tools 2/import-memories.js +231 -0
- package/dist/tools 2/index.d.ts +12 -0
- package/dist/tools 2/index.js +12 -0
- package/dist/tools 2/mark-hit.d.ts +20 -0
- package/dist/tools 2/mark-hit.js +71 -0
- package/dist/tools 2/marketplace-buy.d.ts +30 -0
- package/dist/tools 2/marketplace-buy.js +97 -0
- package/dist/tools 2/marketplace-download.d.ts +26 -0
- package/dist/tools 2/marketplace-download.js +160 -0
- package/dist/tools 2/marketplace-publish.d.ts +111 -0
- package/dist/tools 2/marketplace-publish.js +377 -0
- package/dist/tools 2/marketplace-search.d.ts +57 -0
- package/dist/tools 2/marketplace-search.js +96 -0
- package/dist/tools 2/my-books.d.ts +50 -0
- package/dist/tools 2/my-books.js +107 -0
- package/dist/tools 2/rate-book.d.ts +39 -0
- package/dist/tools 2/rate-book.js +139 -0
- package/dist/tools 2/rebuild-index.d.ts +23 -0
- package/dist/tools 2/rebuild-index.js +107 -0
- package/dist/tools 2/record.d.ts +40 -0
- package/dist/tools 2/record.js +205 -0
- package/dist/tools 2/seller-analytics.d.ts +35 -0
- package/dist/tools 2/seller-analytics.js +102 -0
- package/dist/tools 2/sync.d.ts +54 -0
- package/dist/tools 2/sync.js +298 -0
- 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
|
+
};
|