@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
package/dist/tools/mark-hit.js
CHANGED
|
@@ -2,19 +2,27 @@ import * as fs from 'fs/promises';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import matter from 'gray-matter';
|
|
4
4
|
import { getLibraryPath } from '../library/storage.js';
|
|
5
|
+
import { loadApiKey } from './auth.js';
|
|
6
|
+
const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
|
|
5
7
|
// ============================================================================
|
|
6
8
|
// Tool Definition
|
|
7
9
|
// ============================================================================
|
|
8
10
|
export const markHitTool = {
|
|
9
11
|
name: 'mark_hit',
|
|
10
|
-
|
|
12
|
+
title: 'Mark Entry as Helpful',
|
|
13
|
+
description: `CALL THIS when an entry from brief() actually helped. Fire and forget.
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
USE THIS TOOL WHEN:
|
|
16
|
+
- A brief() result helped you solve the problem
|
|
17
|
+
- You used information from an entry to make a decision
|
|
18
|
+
- An entry saved you from making a mistake
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
DO NOT SKIP - this is how the library learns what's useful.
|
|
21
|
+
Entries with more hits rank higher in future searches.
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
TRIGGER PATTERNS:
|
|
24
|
+
- Used entry from brief() → mark_hit({ path: "<path from brief result>" })
|
|
25
|
+
- Entry prevented a mistake → mark_hit({ path: "..." })
|
|
18
26
|
|
|
19
27
|
Example:
|
|
20
28
|
- mark_hit({ path: "local/stripe-webhooks-need-idempotency.md" })`,
|
|
@@ -33,6 +41,48 @@ Example:
|
|
|
33
41
|
if (!entryPath) {
|
|
34
42
|
throw new Error('path is required');
|
|
35
43
|
}
|
|
44
|
+
// Handle cloud entries (from purchased books via brief())
|
|
45
|
+
// These have paths like "cloud:book-slug"
|
|
46
|
+
if (entryPath.startsWith('cloud:')) {
|
|
47
|
+
const bookSlug = entryPath.slice(6); // Remove "cloud:" prefix
|
|
48
|
+
if (!bookSlug) {
|
|
49
|
+
throw new Error('Invalid cloud entry path');
|
|
50
|
+
}
|
|
51
|
+
// Get API key for authenticated request
|
|
52
|
+
const apiKey = await loadApiKey();
|
|
53
|
+
if (!apiKey) {
|
|
54
|
+
// Can't track hit without auth, but don't fail - fire and forget
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
path: entryPath,
|
|
58
|
+
hits: 1, // We don't know actual count for cloud entries
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// POST hit to server with auth
|
|
62
|
+
try {
|
|
63
|
+
await fetch(`${TELVOK_API_URL}/api/hits`, {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: {
|
|
66
|
+
'Content-Type': 'application/json',
|
|
67
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
68
|
+
},
|
|
69
|
+
body: JSON.stringify({
|
|
70
|
+
hits: [{
|
|
71
|
+
slug: bookSlug,
|
|
72
|
+
timestamp: new Date().toISOString(),
|
|
73
|
+
}],
|
|
74
|
+
}),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Silently ignore server errors - fire and forget
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
success: true,
|
|
82
|
+
path: entryPath,
|
|
83
|
+
hits: 1, // We don't track local count for cloud entries
|
|
84
|
+
};
|
|
85
|
+
}
|
|
36
86
|
const libraryPath = getLibraryPath();
|
|
37
87
|
// Resolve the full path
|
|
38
88
|
let fullPath;
|
|
@@ -62,6 +112,34 @@ Example:
|
|
|
62
112
|
const updatedContent = matter.stringify(body, data);
|
|
63
113
|
// Write back
|
|
64
114
|
await fs.writeFile(fullPath, updatedContent, 'utf-8');
|
|
115
|
+
// Sync to server if this is an imported/packages entry
|
|
116
|
+
const normalizedPath = entryPath.replace(/\\/g, '/');
|
|
117
|
+
if (normalizedPath.startsWith('packages/') || normalizedPath.includes('/packages/')) {
|
|
118
|
+
try {
|
|
119
|
+
// Extract library slug from path: packages/{slug}/entry.md
|
|
120
|
+
const parts = normalizedPath.split('/');
|
|
121
|
+
const packagesIndex = parts.indexOf('packages');
|
|
122
|
+
if (packagesIndex !== -1 && parts.length > packagesIndex + 1) {
|
|
123
|
+
const librarySlug = parts[packagesIndex + 1];
|
|
124
|
+
// POST to server - fire and forget
|
|
125
|
+
fetch(`${TELVOK_API_URL}/api/hits`, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: { 'Content-Type': 'application/json' },
|
|
128
|
+
body: JSON.stringify({
|
|
129
|
+
hits: [{
|
|
130
|
+
slug: librarySlug,
|
|
131
|
+
timestamp: new Date().toISOString(),
|
|
132
|
+
}],
|
|
133
|
+
}),
|
|
134
|
+
}).catch(() => {
|
|
135
|
+
// Silently ignore server sync errors - local update already succeeded
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Ignore errors - local update already succeeded
|
|
141
|
+
}
|
|
142
|
+
}
|
|
65
143
|
return {
|
|
66
144
|
success: true,
|
|
67
145
|
path: entryPath,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
interface PublishedBook {
|
|
2
|
+
slug: string;
|
|
3
|
+
name: string;
|
|
4
|
+
entries_count: number;
|
|
5
|
+
pricing: {
|
|
6
|
+
type: string;
|
|
7
|
+
display: string;
|
|
8
|
+
};
|
|
9
|
+
created_at: string;
|
|
10
|
+
url: string;
|
|
11
|
+
}
|
|
12
|
+
interface PurchasedBook {
|
|
13
|
+
slug: string;
|
|
14
|
+
name: string;
|
|
15
|
+
author: string;
|
|
16
|
+
entries_count: number;
|
|
17
|
+
pricing: {
|
|
18
|
+
type: string;
|
|
19
|
+
display: string;
|
|
20
|
+
};
|
|
21
|
+
purchased_at: string;
|
|
22
|
+
status: string;
|
|
23
|
+
}
|
|
24
|
+
interface MyBooksResult {
|
|
25
|
+
success: boolean;
|
|
26
|
+
message: string;
|
|
27
|
+
published?: PublishedBook[];
|
|
28
|
+
purchased?: PurchasedBook[];
|
|
29
|
+
summary?: {
|
|
30
|
+
published_count: number;
|
|
31
|
+
purchased_count: number;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export declare const myBooksTool: {
|
|
35
|
+
name: string;
|
|
36
|
+
title: string;
|
|
37
|
+
description: string;
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: "object";
|
|
40
|
+
properties: {
|
|
41
|
+
filter: {
|
|
42
|
+
type: string;
|
|
43
|
+
enum: string[];
|
|
44
|
+
description: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
required: never[];
|
|
48
|
+
};
|
|
49
|
+
handler(args: unknown): Promise<MyBooksResult>;
|
|
50
|
+
};
|
|
51
|
+
export default myBooksTool;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// My Books Tool
|
|
3
|
+
// View published and purchased books from Telvok library
|
|
4
|
+
// ============================================================================
|
|
5
|
+
import { loadApiKey } from './auth.js';
|
|
6
|
+
const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Tool Definition
|
|
9
|
+
// ============================================================================
|
|
10
|
+
export const myBooksTool = {
|
|
11
|
+
name: 'my_books',
|
|
12
|
+
title: 'View My Books',
|
|
13
|
+
description: `View your published and purchased books.
|
|
14
|
+
|
|
15
|
+
USE THIS TOOL WHEN:
|
|
16
|
+
- User asks "what books do I have" or "show my library"
|
|
17
|
+
- Need to find a book slug for sync(), rate_book(), or other operations
|
|
18
|
+
- Checking what content user owns
|
|
19
|
+
|
|
20
|
+
Shows published (created) and purchased (bought/claimed) books.
|
|
21
|
+
|
|
22
|
+
TRIGGER PATTERNS:
|
|
23
|
+
- "Show my books" → my_books()
|
|
24
|
+
- "What have I published?" → my_books({ filter: "published" })
|
|
25
|
+
- "What books did I buy?" → my_books({ filter: "purchased" })
|
|
26
|
+
- Need book slugs → my_books()
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
- my_books() - Show all your books
|
|
30
|
+
- my_books({ filter: "published" }) - Only your published books
|
|
31
|
+
- my_books({ filter: "purchased" }) - Only books you've bought`,
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
filter: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
enum: ['all', 'published', 'purchased'],
|
|
38
|
+
description: 'Filter results (default: all)',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
required: [],
|
|
42
|
+
},
|
|
43
|
+
async handler(args) {
|
|
44
|
+
const { filter = 'all' } = (args || {});
|
|
45
|
+
// Validate filter
|
|
46
|
+
if (filter && !['all', 'published', 'purchased'].includes(filter)) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
message: 'Invalid filter. Must be: all, published, or purchased',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Check authentication
|
|
53
|
+
const apiKey = await loadApiKey();
|
|
54
|
+
if (!apiKey) {
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
message: 'Not authenticated. Run auth({ action: "login" }) to connect your Telvok account first.',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const url = new URL(`${TELVOK_API_URL}/api/my-books`);
|
|
62
|
+
if (filter && filter !== 'all') {
|
|
63
|
+
url.searchParams.set('filter', filter);
|
|
64
|
+
}
|
|
65
|
+
const response = await fetch(url.toString(), {
|
|
66
|
+
method: 'GET',
|
|
67
|
+
headers: {
|
|
68
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
const data = await response.json();
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
return {
|
|
74
|
+
success: false,
|
|
75
|
+
message: data.error || `Failed to fetch books: HTTP ${response.status}`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// Format output message
|
|
79
|
+
const publishedCount = data.published?.length || 0;
|
|
80
|
+
const purchasedCount = data.purchased?.length || 0;
|
|
81
|
+
let message = '';
|
|
82
|
+
if (filter === 'published') {
|
|
83
|
+
message = publishedCount === 0
|
|
84
|
+
? 'You haven\'t published any books yet.'
|
|
85
|
+
: `You have ${publishedCount} published book${publishedCount === 1 ? '' : 's'}.`;
|
|
86
|
+
}
|
|
87
|
+
else if (filter === 'purchased') {
|
|
88
|
+
message = purchasedCount === 0
|
|
89
|
+
? 'You haven\'t purchased any books yet.'
|
|
90
|
+
: `You have ${purchasedCount} purchased book${purchasedCount === 1 ? '' : 's'}.`;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
const total = publishedCount + purchasedCount;
|
|
94
|
+
message = total === 0
|
|
95
|
+
? 'No books yet. Publish with library_publish() or browse with library_search().'
|
|
96
|
+
: `${publishedCount} published, ${purchasedCount} purchased.`;
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
success: true,
|
|
100
|
+
message,
|
|
101
|
+
published: data.published,
|
|
102
|
+
purchased: data.purchased,
|
|
103
|
+
summary: data.summary,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
108
|
+
throw new Error(`Failed to fetch books: ${message}`);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// Export
|
|
114
|
+
// ============================================================================
|
|
115
|
+
export default myBooksTool;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
interface BountyItem {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
description: string | null;
|
|
5
|
+
amount: string;
|
|
6
|
+
amount_cents: number;
|
|
7
|
+
status: string;
|
|
8
|
+
created_at: string;
|
|
9
|
+
expires_at: string;
|
|
10
|
+
claimed_at: string | null;
|
|
11
|
+
submitted_at: string | null;
|
|
12
|
+
completed_at: string | null;
|
|
13
|
+
}
|
|
14
|
+
interface MyBountiesResult {
|
|
15
|
+
success: boolean;
|
|
16
|
+
message: string;
|
|
17
|
+
created: BountyItem[];
|
|
18
|
+
claimed: BountyItem[];
|
|
19
|
+
summary?: {
|
|
20
|
+
total_created: number;
|
|
21
|
+
total_claimed: number;
|
|
22
|
+
pending_approval: number;
|
|
23
|
+
in_progress: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export declare const myBountiesTool: {
|
|
27
|
+
name: string;
|
|
28
|
+
title: string;
|
|
29
|
+
description: string;
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: "object";
|
|
32
|
+
properties: {
|
|
33
|
+
role: {
|
|
34
|
+
type: string;
|
|
35
|
+
enum: string[];
|
|
36
|
+
description: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
required: never[];
|
|
40
|
+
};
|
|
41
|
+
handler(args: unknown): Promise<MyBountiesResult>;
|
|
42
|
+
};
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// My Bounties Tool
|
|
3
|
+
// View your created and claimed bounties
|
|
4
|
+
// ============================================================================
|
|
5
|
+
import { loadApiKey } from './auth.js';
|
|
6
|
+
const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Tool Definition
|
|
9
|
+
// ============================================================================
|
|
10
|
+
export const myBountiesTool = {
|
|
11
|
+
name: 'my_bounties',
|
|
12
|
+
title: 'View My Bounties',
|
|
13
|
+
description: `View your bounty activity and pending actions.
|
|
14
|
+
|
|
15
|
+
USE THIS TOOL WHEN:
|
|
16
|
+
- User asks "what bounties do I have"
|
|
17
|
+
- Checking status of created or claimed bounties
|
|
18
|
+
- User needs to see pending approvals or submissions
|
|
19
|
+
|
|
20
|
+
Shows bounties you created (as buyer) and claimed (as seller).
|
|
21
|
+
|
|
22
|
+
TRIGGER PATTERNS:
|
|
23
|
+
- "Show my bounties" → my_bounties()
|
|
24
|
+
- "What bounties am I working on?" → my_bounties({ role: "claimer" })
|
|
25
|
+
- "Check bounty status" → my_bounties()
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
- my_bounties() - Show all your bounties
|
|
29
|
+
- my_bounties({ role: "creator" }) - Only bounties you created
|
|
30
|
+
- my_bounties({ role: "claimer" }) - Only bounties you claimed`,
|
|
31
|
+
inputSchema: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
role: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
enum: ['creator', 'claimer', 'all'],
|
|
37
|
+
description: 'Filter by role (default: all)',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: [],
|
|
41
|
+
},
|
|
42
|
+
async handler(args) {
|
|
43
|
+
const { role = 'all' } = (args || {});
|
|
44
|
+
// Check authentication
|
|
45
|
+
const apiKey = await loadApiKey();
|
|
46
|
+
if (!apiKey) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
message: 'Not authenticated. Run auth({ action: "login" }) to connect your Telvok account first.',
|
|
50
|
+
created: [],
|
|
51
|
+
claimed: [],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const params = new URLSearchParams();
|
|
56
|
+
if (role !== 'all') {
|
|
57
|
+
params.set('role', role);
|
|
58
|
+
}
|
|
59
|
+
const response = await fetch(`${TELVOK_API_URL}/api/my-bounties?${params}`, {
|
|
60
|
+
method: 'GET',
|
|
61
|
+
headers: {
|
|
62
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
const data = await response.json();
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
message: data.error || `Failed to fetch bounties: HTTP ${response.status}`,
|
|
71
|
+
created: [],
|
|
72
|
+
claimed: [],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const created = data.created || [];
|
|
76
|
+
const claimed = data.claimed || [];
|
|
77
|
+
const summary = data.summary;
|
|
78
|
+
// Build summary message
|
|
79
|
+
const parts = [];
|
|
80
|
+
if (created.length > 0) {
|
|
81
|
+
parts.push(`**Bounties You Created (${created.length}):**`);
|
|
82
|
+
created.forEach((b, i) => {
|
|
83
|
+
const statusEmoji = b.status === 'submitted' ? '⏳' : b.status === 'completed' ? '✅' : '📝';
|
|
84
|
+
parts.push(`${i + 1}. ${statusEmoji} ${b.title} (${b.amount}) - ${b.status}`);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (claimed.length > 0) {
|
|
88
|
+
if (parts.length > 0)
|
|
89
|
+
parts.push('');
|
|
90
|
+
parts.push(`**Bounties You Claimed (${claimed.length}):**`);
|
|
91
|
+
claimed.forEach((b, i) => {
|
|
92
|
+
const statusEmoji = b.status === 'submitted' ? '⏳' : b.status === 'completed' ? '✅' : '🔨';
|
|
93
|
+
parts.push(`${i + 1}. ${statusEmoji} ${b.title} (${b.amount}) - ${b.status}`);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (parts.length === 0) {
|
|
97
|
+
parts.push('No bounties found. Create one with bounty_create() or browse with bounty_list().');
|
|
98
|
+
}
|
|
99
|
+
// Add action hints
|
|
100
|
+
if (summary?.pending_approval > 0) {
|
|
101
|
+
parts.push('');
|
|
102
|
+
parts.push(`💡 You have ${summary.pending_approval} submission(s) awaiting your approval.`);
|
|
103
|
+
}
|
|
104
|
+
if (summary?.in_progress > 0) {
|
|
105
|
+
parts.push('');
|
|
106
|
+
parts.push(`💡 You have ${summary.in_progress} claimed bounty(s) to complete.`);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
success: true,
|
|
110
|
+
created,
|
|
111
|
+
claimed,
|
|
112
|
+
summary,
|
|
113
|
+
message: parts.join('\n'),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
message: `Failed to fetch bounties: ${message}`,
|
|
121
|
+
created: [],
|
|
122
|
+
claimed: [],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
interface RateBookResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
message: string;
|
|
4
|
+
review?: {
|
|
5
|
+
id: string;
|
|
6
|
+
rating: number;
|
|
7
|
+
is_verified_purchase: boolean;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export declare const rateBookTool: {
|
|
11
|
+
name: string;
|
|
12
|
+
title: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: "object";
|
|
16
|
+
properties: {
|
|
17
|
+
slug: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
rating: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
minimum: number;
|
|
25
|
+
maximum: number;
|
|
26
|
+
};
|
|
27
|
+
title: {
|
|
28
|
+
type: string;
|
|
29
|
+
description: string;
|
|
30
|
+
};
|
|
31
|
+
comment: {
|
|
32
|
+
type: string;
|
|
33
|
+
description: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
required: string[];
|
|
37
|
+
};
|
|
38
|
+
handler(args: unknown): Promise<RateBookResult>;
|
|
39
|
+
};
|
|
40
|
+
export default rateBookTool;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Rate Book Tool
|
|
3
|
+
// Rate a book you've purchased on the Telvok library
|
|
4
|
+
// ============================================================================
|
|
5
|
+
import { loadApiKey } from './auth.js';
|
|
6
|
+
const TELVOK_API_URL = process.env.TELVOK_API_URL || 'https://telvok.com';
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Tool Definition
|
|
9
|
+
// ============================================================================
|
|
10
|
+
export const rateBookTool = {
|
|
11
|
+
name: 'rate_book',
|
|
12
|
+
title: 'Rate a Book',
|
|
13
|
+
description: `Rate a purchased book to help others find quality content.
|
|
14
|
+
|
|
15
|
+
USE THIS TOOL WHEN:
|
|
16
|
+
- A purchased book was helpful (or not) - share the experience
|
|
17
|
+
- User says "rate that book" or "leave a review"
|
|
18
|
+
- After using a book's knowledge successfully
|
|
19
|
+
|
|
20
|
+
Rating scale: 1 (poor) to 5 (excellent). Requires purchase.
|
|
21
|
+
|
|
22
|
+
TRIGGER PATTERNS:
|
|
23
|
+
- Book helped solve problem → rate_book({ slug: "...", rating: 5 })
|
|
24
|
+
- "Rate that book 4 stars" → rate_book({ slug: "...", rating: 4 })
|
|
25
|
+
- "Leave a review" → rate_book({ slug: "...", rating: X, comment: "..." })
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
- rate_book({ slug: "react-best-practices", rating: 5 })
|
|
29
|
+
- rate_book({ slug: "auth-patterns", rating: 4, title: "Very helpful", comment: "Saved hours on token refresh logic" })`,
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {
|
|
33
|
+
slug: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
description: 'Book slug to rate',
|
|
36
|
+
},
|
|
37
|
+
rating: {
|
|
38
|
+
type: 'number',
|
|
39
|
+
description: 'Rating from 1 to 5',
|
|
40
|
+
minimum: 1,
|
|
41
|
+
maximum: 5,
|
|
42
|
+
},
|
|
43
|
+
title: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'Optional review title',
|
|
46
|
+
},
|
|
47
|
+
comment: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: 'Optional review comment',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
required: ['slug', 'rating'],
|
|
53
|
+
},
|
|
54
|
+
async handler(args) {
|
|
55
|
+
const { slug, rating, title, comment } = args;
|
|
56
|
+
// Validation
|
|
57
|
+
if (!slug || typeof slug !== 'string') {
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
message: 'Book slug is required',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (typeof rating !== 'number' || rating < 1 || rating > 5) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
message: 'Rating must be a number between 1 and 5',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// Check authentication
|
|
70
|
+
const apiKey = await loadApiKey();
|
|
71
|
+
if (!apiKey) {
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
message: 'Not authenticated. Run auth({ action: "login" }) to connect your Telvok account first.',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const response = await fetch(`${TELVOK_API_URL}/api/reviews`, {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: {
|
|
81
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
},
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
slug,
|
|
86
|
+
rating: Math.round(rating),
|
|
87
|
+
title: title?.trim() || undefined,
|
|
88
|
+
comment: comment?.trim() || undefined,
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
// Handle specific error cases with user-friendly messages
|
|
94
|
+
if (response.status === 404) {
|
|
95
|
+
return {
|
|
96
|
+
success: false,
|
|
97
|
+
message: `Book '${slug}' not found. Check the slug with my_books().`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (response.status === 400 && data.error?.includes('own')) {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
message: 'Cannot review your own book.',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (response.status === 409) {
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
message: 'You\'ve already reviewed this book.',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (response.status === 403) {
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
message: `You haven't purchased '${slug}'. Buy it first with library_buy().`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
message: data.error || `Rating failed: HTTP ${response.status}`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const stars = '\u2605'.repeat(Math.round(rating)) + '\u2606'.repeat(5 - Math.round(rating));
|
|
124
|
+
const verifiedBadge = data.is_verified_purchase ? ' - Verified Purchase' : '';
|
|
125
|
+
return {
|
|
126
|
+
success: true,
|
|
127
|
+
message: `Rated '${slug}' ${stars} (${Math.round(rating)}/5)${verifiedBadge}`,
|
|
128
|
+
review: {
|
|
129
|
+
id: data.id,
|
|
130
|
+
rating: data.rating,
|
|
131
|
+
is_verified_purchase: data.is_verified_purchase,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
message: `Rating failed: ${message}`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
// ============================================================================
|
|
145
|
+
// Export
|
|
146
|
+
// ============================================================================
|
|
147
|
+
export default rateBookTool;
|