react-docs-mcp 1.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/README.md +217 -0
- package/dist/config.d.ts +28 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +31 -0
- package/dist/config.js.map +1 -0
- package/dist/docsManager.d.ts +57 -0
- package/dist/docsManager.d.ts.map +1 -0
- package/dist/docsManager.js +185 -0
- package/dist/docsManager.js.map +1 -0
- package/dist/embeddingService.d.ts +52 -0
- package/dist/embeddingService.d.ts.map +1 -0
- package/dist/embeddingService.js +113 -0
- package/dist/embeddingService.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +272 -0
- package/dist/index.js.map +1 -0
- package/dist/markdownParser.d.ts +25 -0
- package/dist/markdownParser.d.ts.map +1 -0
- package/dist/markdownParser.js +85 -0
- package/dist/markdownParser.js.map +1 -0
- package/dist/searchEngine.d.ts +66 -0
- package/dist/searchEngine.d.ts.map +1 -0
- package/dist/searchEngine.js +261 -0
- package/dist/searchEngine.js.map +1 -0
- package/dist/summarizer.d.ts +18 -0
- package/dist/summarizer.d.ts.map +1 -0
- package/dist/summarizer.js +76 -0
- package/dist/summarizer.js.map +1 -0
- package/dist/types.d.ts +52 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* embeddingService.ts
|
|
3
|
+
* Generate embeddings using transformers.js for semantic search
|
|
4
|
+
*/
|
|
5
|
+
import { pipeline, env } from '@xenova/transformers';
|
|
6
|
+
// Configure transformers.js
|
|
7
|
+
env.allowLocalModels = true;
|
|
8
|
+
env.allowRemoteModels = true;
|
|
9
|
+
/**
|
|
10
|
+
* Embedding service using all-MiniLM-L6-v2 model
|
|
11
|
+
* This is a lightweight model (23MB) optimized for semantic similarity
|
|
12
|
+
*/
|
|
13
|
+
export class EmbeddingService {
|
|
14
|
+
pipeline = null;
|
|
15
|
+
initialized = false;
|
|
16
|
+
modelName = 'Xenova/all-MiniLM-L6-v2';
|
|
17
|
+
/**
|
|
18
|
+
* Initialize the embedding pipeline
|
|
19
|
+
* Downloads model on first run (~23MB)
|
|
20
|
+
*/
|
|
21
|
+
async initialize() {
|
|
22
|
+
if (this.initialized)
|
|
23
|
+
return;
|
|
24
|
+
console.log('Initializing embedding model (first run may take a moment)...');
|
|
25
|
+
try {
|
|
26
|
+
this.pipeline = await pipeline('feature-extraction', this.modelName);
|
|
27
|
+
this.initialized = true;
|
|
28
|
+
console.log('Embedding model initialized successfully');
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error('Failed to initialize embedding model:', error);
|
|
32
|
+
throw new Error(`Embedding initialization failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Generate embedding for a text
|
|
37
|
+
* @param text - Text to embed
|
|
38
|
+
* @returns Vector embedding (384 dimensions for all-MiniLM-L6-v2)
|
|
39
|
+
*/
|
|
40
|
+
async generateEmbedding(text) {
|
|
41
|
+
if (!this.initialized) {
|
|
42
|
+
await this.initialize();
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
// Truncate text to reasonable length (512 tokens max)
|
|
46
|
+
const truncated = text.slice(0, 2000);
|
|
47
|
+
const output = await this.pipeline(truncated, {
|
|
48
|
+
pooling: 'mean',
|
|
49
|
+
normalize: true,
|
|
50
|
+
});
|
|
51
|
+
// Convert tensor to array
|
|
52
|
+
const embedding = Array.from(output.data);
|
|
53
|
+
return embedding;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error('Failed to generate embedding:', error);
|
|
57
|
+
throw new Error(`Embedding generation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Generate embeddings for multiple texts (batch processing)
|
|
62
|
+
* @param texts - Array of texts to embed
|
|
63
|
+
* @returns Array of vector embeddings
|
|
64
|
+
*/
|
|
65
|
+
async generateEmbeddings(texts) {
|
|
66
|
+
const embeddings = [];
|
|
67
|
+
for (const text of texts) {
|
|
68
|
+
const embedding = await this.generateEmbedding(text);
|
|
69
|
+
embeddings.push(embedding);
|
|
70
|
+
}
|
|
71
|
+
return embeddings;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Calculate cosine similarity between two vectors
|
|
75
|
+
* @param a - First vector
|
|
76
|
+
* @param b - Second vector
|
|
77
|
+
* @returns Similarity score (0-1, higher is more similar)
|
|
78
|
+
*/
|
|
79
|
+
cosineSimilarity(a, b) {
|
|
80
|
+
if (a.length !== b.length) {
|
|
81
|
+
throw new Error('Vectors must have same length');
|
|
82
|
+
}
|
|
83
|
+
let dotProduct = 0;
|
|
84
|
+
let normA = 0;
|
|
85
|
+
let normB = 0;
|
|
86
|
+
for (let i = 0; i < a.length; i++) {
|
|
87
|
+
dotProduct += a[i] * b[i];
|
|
88
|
+
normA += a[i] * a[i];
|
|
89
|
+
normB += b[i] * b[i];
|
|
90
|
+
}
|
|
91
|
+
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
|
|
92
|
+
if (magnitude === 0)
|
|
93
|
+
return 0;
|
|
94
|
+
return dotProduct / magnitude;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Find most similar vectors to a query vector
|
|
98
|
+
* @param queryEmbedding - Query vector
|
|
99
|
+
* @param docEmbeddings - Array of document vectors with metadata
|
|
100
|
+
* @param topK - Number of results to return
|
|
101
|
+
* @returns Array of {index, similarity} sorted by similarity desc
|
|
102
|
+
*/
|
|
103
|
+
findMostSimilar(queryEmbedding, docEmbeddings, topK) {
|
|
104
|
+
const similarities = docEmbeddings.map(({ embedding, index }) => ({
|
|
105
|
+
index,
|
|
106
|
+
similarity: this.cosineSimilarity(queryEmbedding, embedding),
|
|
107
|
+
}));
|
|
108
|
+
// Sort by similarity descending
|
|
109
|
+
similarities.sort((a, b) => b.similarity - a.similarity);
|
|
110
|
+
return similarities.slice(0, topK);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=embeddingService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddingService.js","sourceRoot":"","sources":["../src/embeddingService.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* index.ts
|
|
4
|
+
* MCP server entry point implementing the protocol
|
|
5
|
+
*/
|
|
6
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
7
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
|
+
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { DocsManager } from './docsManager.js';
|
|
11
|
+
import { SearchEngine } from './searchEngine.js';
|
|
12
|
+
import { summarizeContent, extractStructure } from './summarizer.js';
|
|
13
|
+
import CONFIG from './config.js';
|
|
14
|
+
// Initialize components
|
|
15
|
+
const docsManager = new DocsManager();
|
|
16
|
+
const searchEngine = new SearchEngine(docsManager);
|
|
17
|
+
// Create MCP server
|
|
18
|
+
const server = new Server({
|
|
19
|
+
name: CONFIG.server.name,
|
|
20
|
+
version: CONFIG.server.version,
|
|
21
|
+
}, {
|
|
22
|
+
capabilities: {
|
|
23
|
+
resources: {},
|
|
24
|
+
tools: {},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
// Tool input schemas
|
|
28
|
+
const searchDocsSchema = z.object({
|
|
29
|
+
query: z.string().describe('Search query string'),
|
|
30
|
+
section: z.string().optional().describe('Filter by section (learn, reference, blog, community)'),
|
|
31
|
+
limit: z.number().min(1).max(CONFIG.search.maxLimit).optional().describe('Maximum number of results'),
|
|
32
|
+
});
|
|
33
|
+
const getDocSchema = z.object({
|
|
34
|
+
path: z.string().describe('Document path (e.g., "learn/hooks/useState")'),
|
|
35
|
+
});
|
|
36
|
+
// Register list resources handler
|
|
37
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
38
|
+
return {
|
|
39
|
+
resources: [
|
|
40
|
+
{
|
|
41
|
+
uri: 'react-docs://learn',
|
|
42
|
+
name: 'React Learn Documentation',
|
|
43
|
+
description: 'Interactive React tutorial and learning materials',
|
|
44
|
+
mimeType: 'text/plain',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
uri: 'react-docs://reference',
|
|
48
|
+
name: 'React API Reference',
|
|
49
|
+
description: 'Complete React API reference documentation',
|
|
50
|
+
mimeType: 'text/plain',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
uri: 'react-docs://blog',
|
|
54
|
+
name: 'React Blog',
|
|
55
|
+
description: 'React team blog posts and announcements',
|
|
56
|
+
mimeType: 'text/plain',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
// Register read resource handler
|
|
62
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
63
|
+
const uri = request.params.uri.toString();
|
|
64
|
+
// Parse URI: react-docs://{section}/{path}
|
|
65
|
+
const match = uri.match(/^react-docs:\/\/(.+)$/);
|
|
66
|
+
if (!match) {
|
|
67
|
+
throw new Error(`Invalid resource URI: ${uri}`);
|
|
68
|
+
}
|
|
69
|
+
const resourcePath = match[1];
|
|
70
|
+
// If requesting just a section, list docs in that section
|
|
71
|
+
if (CONFIG.sections.includes(resourcePath)) {
|
|
72
|
+
const docs = await searchEngine.getDocsBySection(resourcePath);
|
|
73
|
+
const docList = docs
|
|
74
|
+
.map(doc => `- ${doc.metadata.title} (${doc.path})`)
|
|
75
|
+
.join('\n');
|
|
76
|
+
return {
|
|
77
|
+
contents: [
|
|
78
|
+
{
|
|
79
|
+
uri,
|
|
80
|
+
mimeType: 'text/plain',
|
|
81
|
+
text: `# ${resourcePath} Documentation\n\nAvailable documents:\n\n${docList}`,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Otherwise, fetch specific document
|
|
87
|
+
const doc = await searchEngine.getDocByPath(resourcePath);
|
|
88
|
+
if (!doc) {
|
|
89
|
+
throw new Error(`Document not found: ${resourcePath}`);
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
contents: [
|
|
93
|
+
{
|
|
94
|
+
uri,
|
|
95
|
+
mimeType: 'text/markdown',
|
|
96
|
+
text: `# ${doc.metadata.title}\n\n${doc.content}`,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
// Register list tools handler
|
|
102
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
103
|
+
return {
|
|
104
|
+
tools: [
|
|
105
|
+
{
|
|
106
|
+
name: 'search_react_docs',
|
|
107
|
+
description: 'Search across React documentation. Returns relevant documentation pages with snippets.',
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties: {
|
|
111
|
+
query: {
|
|
112
|
+
type: 'string',
|
|
113
|
+
description: 'Search query string',
|
|
114
|
+
},
|
|
115
|
+
section: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
description: 'Filter by section (learn, reference, blog, community)',
|
|
118
|
+
enum: [...CONFIG.sections],
|
|
119
|
+
},
|
|
120
|
+
limit: {
|
|
121
|
+
type: 'number',
|
|
122
|
+
description: 'Maximum number of results',
|
|
123
|
+
minimum: 1,
|
|
124
|
+
maximum: CONFIG.search.maxLimit,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
required: ['query'],
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'list_sections',
|
|
132
|
+
description: 'List all available documentation sections',
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'get_doc',
|
|
140
|
+
description: 'Get a concise summary of a documentation page (~1500 chars). Use search_react_docs first - only call this if you need more detail than the search snippet provides.',
|
|
141
|
+
inputSchema: {
|
|
142
|
+
type: 'object',
|
|
143
|
+
properties: {
|
|
144
|
+
path: {
|
|
145
|
+
type: 'string',
|
|
146
|
+
description: 'Document path (e.g., "learn/hooks/useState")',
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
required: ['path'],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'update_docs',
|
|
154
|
+
description: 'Pull latest documentation from Git repository',
|
|
155
|
+
inputSchema: {
|
|
156
|
+
type: 'object',
|
|
157
|
+
properties: {},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
// Register call tool handler
|
|
164
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
165
|
+
const { name, arguments: args } = request.params;
|
|
166
|
+
try {
|
|
167
|
+
switch (name) {
|
|
168
|
+
case 'search_react_docs': {
|
|
169
|
+
const { query, section, limit } = searchDocsSchema.parse(args);
|
|
170
|
+
const results = await searchEngine.search(query, { section, limit });
|
|
171
|
+
return {
|
|
172
|
+
content: [
|
|
173
|
+
{
|
|
174
|
+
type: 'text',
|
|
175
|
+
text: JSON.stringify(results.map(r => ({
|
|
176
|
+
path: r.doc.path,
|
|
177
|
+
title: r.doc.metadata.title,
|
|
178
|
+
snippet: r.snippet,
|
|
179
|
+
score: r.score,
|
|
180
|
+
url: `https://react.dev/${r.doc.path}`,
|
|
181
|
+
})), null, 2),
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
case 'list_sections': {
|
|
187
|
+
const sections = searchEngine.getSections();
|
|
188
|
+
return {
|
|
189
|
+
content: [
|
|
190
|
+
{
|
|
191
|
+
type: 'text',
|
|
192
|
+
text: JSON.stringify(sections, null, 2),
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
case 'get_doc': {
|
|
198
|
+
const { path } = getDocSchema.parse(args);
|
|
199
|
+
const doc = await searchEngine.getDocByPath(path);
|
|
200
|
+
if (!doc) {
|
|
201
|
+
throw new Error(`Document not found: ${path}`);
|
|
202
|
+
}
|
|
203
|
+
// Return concise summary instead of full content
|
|
204
|
+
const summary = summarizeContent(doc.content, 1500);
|
|
205
|
+
const structure = extractStructure(doc.content);
|
|
206
|
+
return {
|
|
207
|
+
content: [
|
|
208
|
+
{
|
|
209
|
+
type: 'text',
|
|
210
|
+
text: JSON.stringify({
|
|
211
|
+
path: doc.path,
|
|
212
|
+
section: doc.section,
|
|
213
|
+
title: doc.metadata.title,
|
|
214
|
+
description: doc.metadata.description,
|
|
215
|
+
summary,
|
|
216
|
+
structure,
|
|
217
|
+
url: `https://react.dev/${doc.path}`,
|
|
218
|
+
note: 'This is a summary. Visit the URL for full documentation.',
|
|
219
|
+
}, null, 2),
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
case 'update_docs': {
|
|
225
|
+
const updated = await docsManager.updateRepo();
|
|
226
|
+
if (updated) {
|
|
227
|
+
// Re-index documents after update
|
|
228
|
+
await searchEngine.indexDocuments();
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
content: [
|
|
232
|
+
{
|
|
233
|
+
type: 'text',
|
|
234
|
+
text: JSON.stringify({
|
|
235
|
+
updated,
|
|
236
|
+
message: updated
|
|
237
|
+
? 'Documentation updated successfully'
|
|
238
|
+
: 'Documentation already up to date',
|
|
239
|
+
}, null, 2),
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
default:
|
|
245
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
if (error instanceof z.ZodError) {
|
|
250
|
+
throw new Error(`Invalid arguments: ${error.message}`);
|
|
251
|
+
}
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
// Start server
|
|
256
|
+
async function main() {
|
|
257
|
+
console.error('Initializing React Docs MCP Server...');
|
|
258
|
+
try {
|
|
259
|
+
// Initialize repository
|
|
260
|
+
await docsManager.initialize();
|
|
261
|
+
// Start server with stdio transport
|
|
262
|
+
const transport = new StdioServerTransport();
|
|
263
|
+
await server.connect(transport);
|
|
264
|
+
console.error('React Docs MCP Server running');
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
console.error('Failed to start server:', error);
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
main();
|
|
272
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,wBAAwB;AACxB,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AACtC,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,WAAW,CAAC,CAAC;AAEnD,oBAAoB;AACpB,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;IACE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI;IACxB,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO;CAC/B,EACD;IACE,YAAY,EAAE;QACZ,SAAS,EAAE,EAAE;QACb,KAAK,EAAE,EAAE;KACV;CACF,CACF,CAAC;AAEF,qBAAqB;AACrB,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;IACjD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uDAAuD,CAAC;IAChG,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;CACtG,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;CAC1E,CAAC,CAAC;AAEH,kCAAkC;AAClC,MAAM,CAAC,iBAAiB,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;IAC9D,OAAO;QACL,SAAS,EAAE;YACT;gBACE,GAAG,EAAE,oBAAoB;gBACzB,IAAI,EAAE,2BAA2B;gBACjC,WAAW,EAAE,mDAAmD;gBAChE,QAAQ,EAAE,YAAY;aACvB;YACD;gBACE,GAAG,EAAE,wBAAwB;gBAC7B,IAAI,EAAE,qBAAqB;gBAC3B,WAAW,EAAE,4CAA4C;gBACzD,QAAQ,EAAE,YAAY;aACvB;YACD;gBACE,GAAG,EAAE,mBAAmB;gBACxB,IAAI,EAAE,YAAY;gBAClB,WAAW,EAAE,yCAAyC;gBACtD,QAAQ,EAAE,YAAY;aACvB;SACF;KACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,iCAAiC;AACjC,MAAM,CAAC,iBAAiB,CAAC,yBAAyB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IACpE,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;IAE1C,2CAA2C;IAC3C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAE9B,0DAA0D;IAC1D,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAmB,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,IAAI;aACjB,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,GAAG,CAAC,QAAQ,CAAC,KAAK,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC;aACnD,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,OAAO;YACL,QAAQ,EAAE;gBACR;oBACE,GAAG;oBACH,QAAQ,EAAE,YAAY;oBACtB,IAAI,EAAE,KAAK,YAAY,6CAA6C,OAAO,EAAE;iBAC9E;aACF;SACF,CAAC;IACJ,CAAC;IAED,qCAAqC;IACrC,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;IAE1D,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,uBAAuB,YAAY,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,OAAO;QACL,QAAQ,EAAE;YACR;gBACE,GAAG;gBACH,QAAQ,EAAE,eAAe;gBACzB,IAAI,EAAE,KAAK,GAAG,CAAC,QAAQ,CAAC,KAAK,OAAO,GAAG,CAAC,OAAO,EAAE;aAClD;SACF;KACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,8BAA8B;AAC9B,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;IAC1D,OAAO;QACL,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,mBAAmB;gBACzB,WAAW,EAAE,wFAAwF;gBACrG,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,KAAK,EAAE;4BACL,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,qBAAqB;yBACnC;wBACD,OAAO,EAAE;4BACP,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,uDAAuD;4BACpE,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC;yBAC3B;wBACD,KAAK,EAAE;4BACL,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,2BAA2B;4BACxC,OAAO,EAAE,CAAC;4BACV,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ;yBAChC;qBACF;oBACD,QAAQ,EAAE,CAAC,OAAO,CAAC;iBACpB;aACF;YACD;gBACE,IAAI,EAAE,eAAe;gBACrB,WAAW,EAAE,2CAA2C;gBACxD,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,EAAE;iBACf;aACF;YACD;gBACE,IAAI,EAAE,SAAS;gBACf,WAAW,EAAE,qKAAqK;gBAClL,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,IAAI,EAAE;4BACJ,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,8CAA8C;yBAC5D;qBACF;oBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;iBACnB;aACF;YACD;gBACE,IAAI,EAAE,aAAa;gBACnB,WAAW,EAAE,+CAA+C;gBAC5D,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,EAAE;iBACf;aACF;SACF;KACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,6BAA6B;AAC7B,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAEjD,IAAI,CAAC;QACH,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,mBAAmB,CAAC,CAAC,CAAC;gBACzB,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC/D,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;gBAErE,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gCAChB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI;gCAChB,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK;gCAC3B,OAAO,EAAE,CAAC,CAAC,OAAO;gCAClB,KAAK,EAAE,CAAC,CAAC,KAAK;gCACd,GAAG,EAAE,qBAAqB,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE;6BACvC,CAAC,CAAC,EACH,IAAI,EACJ,CAAC,CACF;yBACF;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,KAAK,eAAe,CAAC,CAAC,CAAC;gBACrB,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;gBAE5C,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;yBACxC;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,KAAK,SAAS,CAAC,CAAC,CAAC;gBACf,MAAM,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC1C,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBAElD,IAAI,CAAC,GAAG,EAAE,CAAC;oBACT,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;gBACjD,CAAC;gBAED,iDAAiD;gBACjD,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBACpD,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAEhD,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;gCACE,IAAI,EAAE,GAAG,CAAC,IAAI;gCACd,OAAO,EAAE,GAAG,CAAC,OAAO;gCACpB,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK;gCACzB,WAAW,EAAE,GAAG,CAAC,QAAQ,CAAC,WAAW;gCACrC,OAAO;gCACP,SAAS;gCACT,GAAG,EAAE,qBAAqB,GAAG,CAAC,IAAI,EAAE;gCACpC,IAAI,EAAE,0DAA0D;6BACjE,EACD,IAAI,EACJ,CAAC,CACF;yBACF;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE,CAAC;gBAE/C,IAAI,OAAO,EAAE,CAAC;oBACZ,kCAAkC;oBAClC,MAAM,YAAY,CAAC,cAAc,EAAE,CAAC;gBACtC,CAAC;gBAED,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;gCACE,OAAO;gCACP,OAAO,EAAE,OAAO;oCACd,CAAC,CAAC,oCAAoC;oCACtC,CAAC,CAAC,kCAAkC;6BACvC,EACD,IAAI,EACJ,CAAC,CACF;yBACF;qBACF;iBACF,CAAC;YACJ,CAAC;YAED;gBACE,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,sBAAsB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,eAAe;AACf,KAAK,UAAU,IAAI;IACjB,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAEvD,IAAI,CAAC;QACH,wBAAwB;QACxB,MAAM,WAAW,CAAC,UAAU,EAAE,CAAC;QAE/B,oCAAoC;QACpC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEhC,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* markdownParser.ts
|
|
3
|
+
* Parse markdown files and extract metadata and content
|
|
4
|
+
*/
|
|
5
|
+
import type { ParsedDoc } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Parse a markdown file
|
|
8
|
+
* @param content - Raw markdown content
|
|
9
|
+
* @param path - File path for context
|
|
10
|
+
* @returns Parsed document with metadata and content
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseMarkdown(content: string, path: string): Promise<ParsedDoc>;
|
|
13
|
+
/**
|
|
14
|
+
* Strip markdown formatting to plain text
|
|
15
|
+
* Used for search indexing
|
|
16
|
+
* @param markdown - Markdown content
|
|
17
|
+
* @returns Plain text
|
|
18
|
+
*/
|
|
19
|
+
export declare function markdownToPlainText(markdown: string): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Extract section from file path
|
|
22
|
+
* E.g., "learn/hooks/useState.md" -> "learn"
|
|
23
|
+
*/
|
|
24
|
+
export declare function extractSection(path: string): string;
|
|
25
|
+
//# sourceMappingURL=markdownParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdownParser.d.ts","sourceRoot":"","sources":["../src/markdownParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,SAAS,CAAC,CA2BpB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ3E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAInD"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* markdownParser.ts
|
|
3
|
+
* Parse markdown files and extract metadata and content
|
|
4
|
+
*/
|
|
5
|
+
import matter from 'gray-matter';
|
|
6
|
+
import { remark } from 'remark';
|
|
7
|
+
import stripMarkdown from 'strip-markdown';
|
|
8
|
+
/**
|
|
9
|
+
* Parse a markdown file
|
|
10
|
+
* @param content - Raw markdown content
|
|
11
|
+
* @param path - File path for context
|
|
12
|
+
* @returns Parsed document with metadata and content
|
|
13
|
+
*/
|
|
14
|
+
export async function parseMarkdown(content, path) {
|
|
15
|
+
// Parse frontmatter
|
|
16
|
+
const { data, content: markdownContent } = matter(content);
|
|
17
|
+
// Extract metadata
|
|
18
|
+
const metadata = {
|
|
19
|
+
title: data.title || extractTitleFromPath(path),
|
|
20
|
+
description: data.description,
|
|
21
|
+
date: data.date,
|
|
22
|
+
author: data.author,
|
|
23
|
+
tags: data.tags,
|
|
24
|
+
...data,
|
|
25
|
+
};
|
|
26
|
+
// Convert to plain text for search
|
|
27
|
+
const plainText = await markdownToPlainText(markdownContent);
|
|
28
|
+
// Extract section from path
|
|
29
|
+
const section = extractSection(path);
|
|
30
|
+
return {
|
|
31
|
+
path: normalizePath(path),
|
|
32
|
+
section,
|
|
33
|
+
metadata,
|
|
34
|
+
content: markdownContent,
|
|
35
|
+
plainText,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Strip markdown formatting to plain text
|
|
40
|
+
* Used for search indexing
|
|
41
|
+
* @param markdown - Markdown content
|
|
42
|
+
* @returns Plain text
|
|
43
|
+
*/
|
|
44
|
+
export async function markdownToPlainText(markdown) {
|
|
45
|
+
const result = await remark()
|
|
46
|
+
.use(stripMarkdown)
|
|
47
|
+
.process(markdown);
|
|
48
|
+
return String(result)
|
|
49
|
+
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
50
|
+
.trim();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Extract section from file path
|
|
54
|
+
* E.g., "learn/hooks/useState.md" -> "learn"
|
|
55
|
+
*/
|
|
56
|
+
export function extractSection(path) {
|
|
57
|
+
const normalized = normalizePath(path);
|
|
58
|
+
const parts = normalized.split('/');
|
|
59
|
+
return parts[0] || 'unknown';
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Normalize path (forward slashes, no leading slash, no .md extension for display)
|
|
63
|
+
*/
|
|
64
|
+
function normalizePath(filePath) {
|
|
65
|
+
return filePath
|
|
66
|
+
.replace(/\\/g, '/') // Convert backslashes to forward slashes
|
|
67
|
+
.replace(/^\/+/, '') // Remove leading slashes
|
|
68
|
+
.replace(/\.md$/, ''); // Remove .md extension for cleaner paths
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Extract title from path if not in frontmatter
|
|
72
|
+
* E.g., "learn/hooks/useState" -> "useState"
|
|
73
|
+
*/
|
|
74
|
+
function extractTitleFromPath(filePath) {
|
|
75
|
+
const normalized = normalizePath(filePath);
|
|
76
|
+
const parts = normalized.split('/');
|
|
77
|
+
const filename = parts[parts.length - 1] || 'Untitled';
|
|
78
|
+
// Convert kebab-case or snake_case to Title Case
|
|
79
|
+
return filename
|
|
80
|
+
.replace(/[-_]/g, ' ')
|
|
81
|
+
.split(' ')
|
|
82
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
83
|
+
.join(' ');
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=markdownParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdownParser.js","sourceRoot":"","sources":["../src/markdownParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,aAAa,MAAM,gBAAgB,CAAC;AAG3C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAe,EACf,IAAY;IAEZ,oBAAoB;IACpB,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAE3D,mBAAmB;IACnB,MAAM,QAAQ,GAAgB;QAC5B,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,oBAAoB,CAAC,IAAI,CAAC;QAC/C,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,GAAG,IAAI;KACR,CAAC;IAEF,mCAAmC;IACnC,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,eAAe,CAAC,CAAC;IAE7D,4BAA4B;IAC5B,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAErC,OAAO;QACL,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC;QACzB,OAAO;QACP,QAAQ;QACR,OAAO,EAAE,eAAe;QACxB,SAAS;KACV,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAAgB;IACxD,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE;SAC1B,GAAG,CAAC,aAAa,CAAC;SAClB,OAAO,CAAC,QAAQ,CAAC,CAAC;IAErB,OAAO,MAAM,CAAC,MAAM,CAAC;SAClB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,uBAAuB;SAC5C,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,QAAgB;IACrC,OAAO,QAAQ;SACZ,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,yCAAyC;SAC7D,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,yBAAyB;SAC7C,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,yCAAyC;AACpE,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAAC,QAAgB;IAC5C,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,UAAU,CAAC;IAEvD,iDAAiD;IACjD,OAAO,QAAQ;SACZ,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SACzD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* searchEngine.ts
|
|
3
|
+
* Implement search functionality over documentation
|
|
4
|
+
*/
|
|
5
|
+
import { DocsManager } from './docsManager.js';
|
|
6
|
+
import type { ParsedDoc, SearchOptions, SearchResult } from './types.js';
|
|
7
|
+
export declare class SearchEngine {
|
|
8
|
+
private docsManager;
|
|
9
|
+
private embeddingService;
|
|
10
|
+
private documentIndex;
|
|
11
|
+
private indexed;
|
|
12
|
+
private embeddingsGenerated;
|
|
13
|
+
/**
|
|
14
|
+
* Initialize search engine
|
|
15
|
+
* @param docsManager - Instance of DocsManager
|
|
16
|
+
*/
|
|
17
|
+
constructor(docsManager: DocsManager);
|
|
18
|
+
/**
|
|
19
|
+
* Index all documents for searching
|
|
20
|
+
* Should be called after repo update
|
|
21
|
+
*/
|
|
22
|
+
indexDocuments(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Generate embeddings for all documents
|
|
25
|
+
* Called lazily when semantic search is first used
|
|
26
|
+
*/
|
|
27
|
+
generateEmbeddings(): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Search documents
|
|
30
|
+
* @param query - Search query string
|
|
31
|
+
* @param options - Search options (section filter, limit, etc.)
|
|
32
|
+
* @returns Ranked search results
|
|
33
|
+
*/
|
|
34
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Keyword-based search
|
|
37
|
+
*/
|
|
38
|
+
private keywordSearch;
|
|
39
|
+
/**
|
|
40
|
+
* Semantic search using embeddings (hybrid with keyword search)
|
|
41
|
+
*/
|
|
42
|
+
private semanticSearch;
|
|
43
|
+
/**
|
|
44
|
+
* Score a document based on query terms
|
|
45
|
+
*/
|
|
46
|
+
private scoreDocument;
|
|
47
|
+
/**
|
|
48
|
+
* Generate context snippet showing matched text
|
|
49
|
+
*/
|
|
50
|
+
private generateSnippet;
|
|
51
|
+
/**
|
|
52
|
+
* Get document by exact path
|
|
53
|
+
* @param path - Document path relative to content root
|
|
54
|
+
* @returns Parsed document or null if not found
|
|
55
|
+
*/
|
|
56
|
+
getDocByPath(path: string): Promise<ParsedDoc | null>;
|
|
57
|
+
/**
|
|
58
|
+
* List all available sections
|
|
59
|
+
*/
|
|
60
|
+
getSections(): string[];
|
|
61
|
+
/**
|
|
62
|
+
* Get all documents in a section
|
|
63
|
+
*/
|
|
64
|
+
getDocsBySection(section: string): Promise<ParsedDoc[]>;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=searchEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchEngine.d.ts","sourceRoot":"","sources":["../src/searchEngine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAI/C,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEzE,qBAAa,YAAY;IACvB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,aAAa,CAAqC;IAC1D,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,mBAAmB,CAAkB;IAE7C;;;OAGG;gBACS,WAAW,EAAE,WAAW;IAKpC;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBrC;;;OAGG;IACG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BzC;;;;;OAKG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,YAAY,EAAE,CAAC;IAsB1B;;OAEG;YACW,aAAa;IAsC3B;;OAEG;YACW,cAAc;IA0D5B;;OAEG;IACH,OAAO,CAAC,aAAa;IAkCrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAkCvB;;;;OAIG;IACG,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAY3D;;OAEG;IACH,WAAW,IAAI,MAAM,EAAE;IAIvB;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;CAiB9D"}
|