@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.
- package/dist/library/errors.d.ts +48 -0
- package/dist/library/errors.js +80 -0
- package/dist/library/schemas.d.ts +6 -6
- package/dist/library/sensitive-scanner.d.ts +20 -0
- package/dist/library/sensitive-scanner.js +56 -0
- 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 +104 -54
- package/dist/tools/adopt.d.ts +1 -0
- package/dist/tools/adopt.js +37 -10
- package/dist/tools/audit.d.ts +27 -0
- package/dist/tools/audit.js +126 -0
- 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 +117 -0
- package/dist/tools/library-publish.js +447 -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,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;
|