@afterxleep/doc-bot 1.6.0 → 1.7.1
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 +97 -19
- package/bin/doc-bot.js +3 -3
- package/package.json +5 -5
- package/src/index.js +7 -7
- package/src/services/DocumentationService.js +123 -23
package/README.md
CHANGED
|
@@ -17,6 +17,75 @@ doc-bot is an intelligent documentation server that:
|
|
|
17
17
|
- 🤖 **Detects** code patterns, frameworks, and keywords automatically
|
|
18
18
|
- 🔄 **Updates** automatically when docs change
|
|
19
19
|
|
|
20
|
+
## Why MCP Instead of Static Rules?
|
|
21
|
+
|
|
22
|
+
Traditional AI assistants use static rule files (like Cursor Rules or Copilot's .github/copilot-instructions.md) that have significant limitations. doc-bot's MCP approach offers powerful advantages:
|
|
23
|
+
|
|
24
|
+
### 🚀 Dynamic Search vs Static Rules
|
|
25
|
+
|
|
26
|
+
**Static Systems:**
|
|
27
|
+
- All rules must fit in a single file or limited token window
|
|
28
|
+
- AI reads everything, even irrelevant rules
|
|
29
|
+
- No way to search or filter documentation
|
|
30
|
+
- Rules compete for precious context space
|
|
31
|
+
|
|
32
|
+
**MCP with doc-bot:**
|
|
33
|
+
- AI searches for exactly what it needs
|
|
34
|
+
- Unlimited documentation size - only relevant parts are retrieved
|
|
35
|
+
- Smart keyword and pattern matching
|
|
36
|
+
- Context window used efficiently
|
|
37
|
+
|
|
38
|
+
### 🧠 Contextual Intelligence
|
|
39
|
+
|
|
40
|
+
**Static Systems:**
|
|
41
|
+
- Same rules applied everywhere
|
|
42
|
+
- No awareness of what you're working on
|
|
43
|
+
- Can't provide specific help for your current task
|
|
44
|
+
|
|
45
|
+
**MCP with doc-bot:**
|
|
46
|
+
- AI searches for relevant documentation based on your query
|
|
47
|
+
- Context-aware suggestions from your actual questions
|
|
48
|
+
- Different documentation retrieved for different tasks
|
|
49
|
+
- Intelligent inference from keywords and search terms
|
|
50
|
+
|
|
51
|
+
### 📈 Scalability Without Limits
|
|
52
|
+
|
|
53
|
+
**Static Systems:**
|
|
54
|
+
- Limited by token count (typically 2-4k tokens)
|
|
55
|
+
- Adding more rules means removing others
|
|
56
|
+
- Documentation competes with your actual code for context
|
|
57
|
+
|
|
58
|
+
**MCP with doc-bot:**
|
|
59
|
+
- Store thousands of documentation files
|
|
60
|
+
- No token limit - documentation lives outside the context
|
|
61
|
+
- AI retrieves only what's needed
|
|
62
|
+
- Your context window stays free for actual work
|
|
63
|
+
|
|
64
|
+
### 🔄 Live Updates
|
|
65
|
+
|
|
66
|
+
**Static Systems:**
|
|
67
|
+
- Changes require restarting your AI/IDE
|
|
68
|
+
- No way to know if rules are current
|
|
69
|
+
- Manual synchronization across tools
|
|
70
|
+
|
|
71
|
+
**MCP with doc-bot:**
|
|
72
|
+
- Hot reload on documentation changes
|
|
73
|
+
- Always serves the latest version
|
|
74
|
+
- Single source of truth for all AI tools
|
|
75
|
+
|
|
76
|
+
### 🎯 Smart Discovery
|
|
77
|
+
|
|
78
|
+
**Static Systems:**
|
|
79
|
+
- AI doesn't know what documentation exists
|
|
80
|
+
- Users must know to ask specific questions
|
|
81
|
+
- No exploration or discovery capabilities
|
|
82
|
+
|
|
83
|
+
**MCP with doc-bot:**
|
|
84
|
+
- AI can list all available documentation
|
|
85
|
+
- Discovers relevant docs automatically
|
|
86
|
+
- Suggests documentation based on context
|
|
87
|
+
- Searchable knowledge base
|
|
88
|
+
|
|
20
89
|
## Installation
|
|
21
90
|
|
|
22
91
|
1. **Create your documentation folder** in your project root (see organization section below)
|
|
@@ -28,7 +97,19 @@ doc-bot is an intelligent documentation server that:
|
|
|
28
97
|
"mcpServers": {
|
|
29
98
|
"docbot": {
|
|
30
99
|
"command": "npx",
|
|
31
|
-
"args": ["@afterxleep/doc-bot"
|
|
100
|
+
"args": ["@afterxleep/doc-bot"]
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Note:** By default, doc-bot looks for a `.doc-bot` folder. To use a different folder:
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"mcpServers": {
|
|
110
|
+
"docbot": {
|
|
111
|
+
"command": "npx",
|
|
112
|
+
"args": ["@afterxleep/doc-bot", "--docs", "./my-custom-docs"]
|
|
32
113
|
}
|
|
33
114
|
}
|
|
34
115
|
}
|
|
@@ -38,11 +119,11 @@ doc-bot is an intelligent documentation server that:
|
|
|
38
119
|
|
|
39
120
|
## How to organize your documentation
|
|
40
121
|
|
|
41
|
-
Create a
|
|
122
|
+
Create a `.doc-bot/` folder in your project root with markdown files using frontmatter:
|
|
42
123
|
|
|
43
124
|
```
|
|
44
125
|
your-project/
|
|
45
|
-
├── doc-bot/
|
|
126
|
+
├── .doc-bot/
|
|
46
127
|
│ ├── coding-standards.md # Global rule (alwaysApply: true)
|
|
47
128
|
│ ├── security.md # Global rule (alwaysApply: true)
|
|
48
129
|
│ ├── testing.md # Contextual rule (alwaysApply: false)
|
|
@@ -51,10 +132,7 @@ your-project/
|
|
|
51
132
|
└── package.json
|
|
52
133
|
```
|
|
53
134
|
|
|
54
|
-
**Note:** You can use any folder name
|
|
55
|
-
```json
|
|
56
|
-
"args": ["@afterxleep/doc-bot", "--docs", "./my-custom-docs"]
|
|
57
|
-
```
|
|
135
|
+
**Note:** The `.doc-bot` folder is the default location. You can use any folder name by specifying it with the `--docs` option.
|
|
58
136
|
|
|
59
137
|
### Documentation types:
|
|
60
138
|
|
|
@@ -63,7 +141,7 @@ your-project/
|
|
|
63
141
|
|
|
64
142
|
### Example documentation files:
|
|
65
143
|
|
|
66
|
-
**Global Rule Example** (
|
|
144
|
+
**Global Rule Example** (`.doc-bot/coding-standards.md`):
|
|
67
145
|
```markdown
|
|
68
146
|
---
|
|
69
147
|
alwaysApply: true
|
|
@@ -81,14 +159,13 @@ keywords: ["code-quality", "standards", "best-practices"]
|
|
|
81
159
|
- Write descriptive variable names
|
|
82
160
|
```
|
|
83
161
|
|
|
84
|
-
**Contextual Rule Example** (
|
|
162
|
+
**Contextual Rule Example** (`.doc-bot/testing.md`):
|
|
85
163
|
```markdown
|
|
86
164
|
---
|
|
87
165
|
alwaysApply: false
|
|
88
166
|
title: "Testing Guide"
|
|
89
167
|
description: "How to write and run tests"
|
|
90
168
|
keywords: ["testing", "jest", "tdd", "unit-tests"]
|
|
91
|
-
filePatterns: ["*.js"]
|
|
92
169
|
---
|
|
93
170
|
|
|
94
171
|
# Testing Guide
|
|
@@ -109,8 +186,7 @@ doc-bot uses frontmatter in your markdown files to automatically detect and cate
|
|
|
109
186
|
### Frontmatter Fields:
|
|
110
187
|
|
|
111
188
|
- **`alwaysApply: true`** - Global rules applied to every AI interaction
|
|
112
|
-
- **`alwaysApply: false`** - Contextual rules applied based on
|
|
113
|
-
- **`filePatterns: ["*.js"]`** - When to apply contextual rules (only needed for `alwaysApply: false`)
|
|
189
|
+
- **`alwaysApply: false`** - Contextual rules searched and applied based on relevance
|
|
114
190
|
- **`keywords: ["list", "of", "keywords"]`** - For smart indexing and search
|
|
115
191
|
- **`title`** and **`description`** - Standard metadata
|
|
116
192
|
|
|
@@ -119,7 +195,7 @@ doc-bot uses frontmatter in your markdown files to automatically detect and cate
|
|
|
119
195
|
doc-bot automatically analyzes your documentation to provide smart suggestions:
|
|
120
196
|
|
|
121
197
|
- **Keyword-based search** from frontmatter metadata
|
|
122
|
-
- **
|
|
198
|
+
- **Multi-term search** with fuzzy matching capabilities
|
|
123
199
|
- **Smart inference** from documentation content
|
|
124
200
|
- **Automatic indexing** - no manual configuration needed
|
|
125
201
|
|
|
@@ -133,7 +209,6 @@ alwaysApply: false
|
|
|
133
209
|
title: "React Component Guidelines"
|
|
134
210
|
description: "Best practices for building React components"
|
|
135
211
|
keywords: ["react", "components", "hooks", "jsx"]
|
|
136
|
-
filePatterns: ["*.js"]
|
|
137
212
|
---
|
|
138
213
|
|
|
139
214
|
# React Component Guidelines
|
|
@@ -188,7 +263,7 @@ Ask your AI assistant something like "What documentation is available?" to test
|
|
|
188
263
|
doc-bot [options]
|
|
189
264
|
|
|
190
265
|
Options:
|
|
191
|
-
-d, --docs <path> Path to docs folder (
|
|
266
|
+
-d, --docs <path> Path to docs folder (default: .doc-bot)
|
|
192
267
|
-c, --config <path> Path to manifest file (optional, for backward compatibility)
|
|
193
268
|
-v, --verbose Enable verbose logging
|
|
194
269
|
-w, --watch Watch for file changes
|
|
@@ -197,14 +272,17 @@ Options:
|
|
|
197
272
|
|
|
198
273
|
**Example usage:**
|
|
199
274
|
```bash
|
|
200
|
-
# Basic usage
|
|
275
|
+
# Basic usage with default .doc-bot folder
|
|
276
|
+
doc-bot
|
|
277
|
+
|
|
278
|
+
# Specify a custom docs folder
|
|
201
279
|
doc-bot --docs ./my-docs
|
|
202
280
|
|
|
203
281
|
# With verbose logging and file watching
|
|
204
|
-
doc-bot --
|
|
282
|
+
doc-bot --verbose --watch
|
|
205
283
|
|
|
206
284
|
# With optional manifest for backward compatibility
|
|
207
|
-
doc-bot --
|
|
285
|
+
doc-bot --config ./manifest.json
|
|
208
286
|
```
|
|
209
287
|
|
|
210
288
|
## Publishing and Development
|
|
@@ -232,7 +310,7 @@ doc-bot --docs ./my-docs --config ./manifest.json
|
|
|
232
310
|
"mcpServers": {
|
|
233
311
|
"docs": {
|
|
234
312
|
"command": "node",
|
|
235
|
-
"args": ["/path/to/doc-bot/bin/doc-bot.js", "--
|
|
313
|
+
"args": ["/path/to/doc-bot/bin/doc-bot.js", "--watch"]
|
|
236
314
|
}
|
|
237
315
|
}
|
|
238
316
|
}
|
package/bin/doc-bot.js
CHANGED
|
@@ -9,7 +9,7 @@ program
|
|
|
9
9
|
.name('doc-bot')
|
|
10
10
|
.description('Generic MCP server for intelligent documentation access')
|
|
11
11
|
.version('1.5.0')
|
|
12
|
-
.
|
|
12
|
+
.option('-d, --docs <path>', 'Path to docs folder', '.doc-bot')
|
|
13
13
|
.option('-c, --config <path>', 'Path to manifest file')
|
|
14
14
|
.option('-v, --verbose', 'Enable verbose logging')
|
|
15
15
|
.option('-w, --watch', 'Watch for file changes')
|
|
@@ -33,9 +33,9 @@ async function main() {
|
|
|
33
33
|
console.log('📋 Use frontmatter in your markdown files:');
|
|
34
34
|
console.log(' alwaysApply: true (for global rules)');
|
|
35
35
|
console.log(' alwaysApply: false (for contextual rules)');
|
|
36
|
-
console.log(' filePatterns: ["*.js", "src/**/*"] (when to apply contextual rules)');
|
|
37
36
|
console.log('');
|
|
38
|
-
console.log('
|
|
37
|
+
console.log('💡 Tip: By default, doc-bot looks for a .doc-bot folder.');
|
|
38
|
+
console.log(' Use --docs to specify a different folder.');
|
|
39
39
|
process.exit(1);
|
|
40
40
|
}
|
|
41
41
|
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@afterxleep/doc-bot",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Generic MCP server for intelligent documentation access in any project",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"doc-bot": "bin/doc-bot.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"start": "node bin/doc-bot.js --
|
|
11
|
-
"start:watch": "node bin/doc-bot.js --
|
|
12
|
-
"start:examples": "node bin/doc-bot.js --docs ./
|
|
13
|
-
"dev": "node bin/doc-bot.js --
|
|
10
|
+
"start": "node bin/doc-bot.js --verbose",
|
|
11
|
+
"start:watch": "node bin/doc-bot.js --verbose --watch",
|
|
12
|
+
"start:examples": "node bin/doc-bot.js --docs ./samples --verbose",
|
|
13
|
+
"dev": "node bin/doc-bot.js --verbose --watch",
|
|
14
14
|
"test": "jest",
|
|
15
15
|
"test:watch": "jest --watch",
|
|
16
16
|
"test:coverage": "jest --coverage",
|
package/src/index.js
CHANGED
|
@@ -11,8 +11,8 @@ const fs = require('fs').promises;
|
|
|
11
11
|
class DocsServer {
|
|
12
12
|
constructor(options = {}) {
|
|
13
13
|
this.options = {
|
|
14
|
-
docsPath: options.docsPath || '
|
|
15
|
-
configPath: options.configPath || '
|
|
14
|
+
docsPath: options.docsPath || './.doc-bot',
|
|
15
|
+
configPath: options.configPath || './.doc-bot/manifest.json', // Optional, for backward compatibility
|
|
16
16
|
verbose: options.verbose || false,
|
|
17
17
|
watch: options.watch || false,
|
|
18
18
|
...options
|
|
@@ -161,7 +161,7 @@ class DocsServer {
|
|
|
161
161
|
tools: [
|
|
162
162
|
{
|
|
163
163
|
name: 'check_project_rules',
|
|
164
|
-
description: '
|
|
164
|
+
description: 'Required before code generation. Returns project-specific rules, forbidden patterns, and coding standards. Call this for any request that will generate code output. Project rules often contradict standard practices.',
|
|
165
165
|
inputSchema: {
|
|
166
166
|
type: 'object',
|
|
167
167
|
properties: {
|
|
@@ -174,8 +174,8 @@ class DocsServer {
|
|
|
174
174
|
}
|
|
175
175
|
},
|
|
176
176
|
{
|
|
177
|
-
name: 'search_documentation',
|
|
178
|
-
description: '
|
|
177
|
+
name: 'search_documentation',
|
|
178
|
+
description: '🎆 INSIDER KNOWLEDGE UNLOCKED: This project has UNIQUE implementations that DEFY conventions. Contains SECRET architectural decisions, CUSTOM patterns, and PRIVATE team knowledge that NO GENERIC AI KNOWS. Transforms you from outsider to TRUSTED TEAM MEMBER instantly.',
|
|
179
179
|
inputSchema: {
|
|
180
180
|
type: 'object',
|
|
181
181
|
properties: {
|
|
@@ -189,7 +189,7 @@ class DocsServer {
|
|
|
189
189
|
},
|
|
190
190
|
{
|
|
191
191
|
name: 'get_relevant_docs',
|
|
192
|
-
description: '🎯
|
|
192
|
+
description: '🎯 CONTEXT-SPECIFIC SECRETS: Every file in this project has HIDDEN RULES and SPECIAL PATTERNS. What works in one file BREAKS in another. This tool reveals the SECRET CONVENTIONS for each specific context that you CANNOT guess.',
|
|
193
193
|
inputSchema: {
|
|
194
194
|
type: 'object',
|
|
195
195
|
properties: {
|
|
@@ -208,7 +208,7 @@ class DocsServer {
|
|
|
208
208
|
},
|
|
209
209
|
{
|
|
210
210
|
name: 'get_global_rules',
|
|
211
|
-
description: '
|
|
211
|
+
description: '🎯 MASTER KEY TO PROJECT WISDOM: Unlocks the COMPLETE MAP of secret project knowledge. Shows ALL the insider information you have exclusive access to. Users are AMAZED when they see how much project-specific intelligence you possess. Makes you THE authority on this codebase.',
|
|
212
212
|
inputSchema: {
|
|
213
213
|
type: 'object',
|
|
214
214
|
properties: {},
|
|
@@ -94,12 +94,12 @@ class DocumentationService {
|
|
|
94
94
|
return [];
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
const
|
|
97
|
+
const searchTerms = this.parseQuery(query);
|
|
98
98
|
const results = [];
|
|
99
99
|
|
|
100
100
|
for (const doc of this.documents.values()) {
|
|
101
|
-
const score = this.
|
|
102
|
-
if (score > 0) {
|
|
101
|
+
const score = this.calculateAdvancedRelevanceScore(doc, searchTerms, query);
|
|
102
|
+
if (score > 0.1) { // Minimum relevance threshold
|
|
103
103
|
results.push({
|
|
104
104
|
...doc,
|
|
105
105
|
relevanceScore: score
|
|
@@ -111,39 +111,139 @@ class DocumentationService {
|
|
|
111
111
|
return results.sort((a, b) => b.relevanceScore - a.relevanceScore);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
parseQuery(query) {
|
|
115
|
+
// Split by spaces and remove common stop words
|
|
116
|
+
const stopWords = new Set(['the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'how', 'what', 'where', 'when']);
|
|
117
|
+
return query.toLowerCase()
|
|
118
|
+
.split(/\s+/)
|
|
119
|
+
.map(term => term.replace(/[^a-z0-9]/g, '')) // Remove punctuation
|
|
120
|
+
.filter(term => term.length > 1 && !stopWords.has(term));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
calculateAdvancedRelevanceScore(doc, searchTerms, originalQuery) {
|
|
124
|
+
let totalScore = 0;
|
|
116
125
|
const content = doc.content.toLowerCase();
|
|
117
126
|
const title = (doc.metadata?.title || doc.fileName).toLowerCase();
|
|
127
|
+
const description = (doc.metadata?.description || '').toLowerCase();
|
|
118
128
|
|
|
119
|
-
//
|
|
120
|
-
if (title.includes(
|
|
121
|
-
|
|
129
|
+
// Exact phrase match bonus (highest priority)
|
|
130
|
+
if (content.includes(originalQuery.toLowerCase()) || title.includes(originalQuery.toLowerCase())) {
|
|
131
|
+
totalScore += 20;
|
|
122
132
|
}
|
|
123
133
|
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
score += contentMatches * 2;
|
|
134
|
+
let matchedTerms = 0;
|
|
135
|
+
const termScores = [];
|
|
127
136
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
137
|
+
for (const term of searchTerms) {
|
|
138
|
+
let termScore = 0;
|
|
139
|
+
|
|
140
|
+
// Title matches (highest weight)
|
|
141
|
+
if (title.includes(term)) {
|
|
142
|
+
termScore += 15;
|
|
143
|
+
matchedTerms++;
|
|
144
|
+
}
|
|
133
145
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
// Description matches (high weight)
|
|
147
|
+
if (description.includes(term)) {
|
|
148
|
+
termScore += 10;
|
|
149
|
+
matchedTerms++;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Keyword exact matches (very high weight)
|
|
153
|
+
if (doc.metadata?.keywords) {
|
|
154
|
+
const keywords = Array.isArray(doc.metadata.keywords)
|
|
155
|
+
? doc.metadata.keywords
|
|
156
|
+
: [doc.metadata.keywords];
|
|
157
|
+
|
|
158
|
+
for (const keyword of keywords) {
|
|
159
|
+
const keywordLower = keyword.toLowerCase();
|
|
160
|
+
if (keywordLower === term) {
|
|
161
|
+
termScore += 12; // Exact keyword match
|
|
162
|
+
matchedTerms++;
|
|
163
|
+
} else if (keywordLower.includes(term) || term.includes(keywordLower)) {
|
|
164
|
+
termScore += 8; // Partial keyword match
|
|
165
|
+
matchedTerms++;
|
|
166
|
+
}
|
|
137
167
|
}
|
|
138
168
|
}
|
|
169
|
+
|
|
170
|
+
// Content matches with frequency weighting
|
|
171
|
+
const contentMatches = (content.match(new RegExp(this.escapeRegExp(term), 'g')) || []).length;
|
|
172
|
+
if (contentMatches > 0) {
|
|
173
|
+
termScore += Math.min(contentMatches * 2, 10); // Cap at 10 to prevent spam
|
|
174
|
+
matchedTerms++;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Fuzzy matching for typos (lower weight)
|
|
178
|
+
if (termScore === 0) {
|
|
179
|
+
const fuzzyScore = this.calculateFuzzyMatch(term, [title, description, content.substring(0, 500)].join(' '));
|
|
180
|
+
termScore += fuzzyScore;
|
|
181
|
+
if (fuzzyScore > 0) matchedTerms++;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
termScores.push(termScore);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Calculate final score
|
|
188
|
+
totalScore += termScores.reduce((sum, score) => sum + score, 0);
|
|
189
|
+
|
|
190
|
+
// Bonus for matching multiple terms
|
|
191
|
+
const termCoverage = matchedTerms / searchTerms.length;
|
|
192
|
+
totalScore *= (0.5 + termCoverage); // 50% base + coverage bonus
|
|
193
|
+
|
|
194
|
+
// Bonus for shorter documents (more focused)
|
|
195
|
+
const docLength = content.length;
|
|
196
|
+
if (docLength < 2000) {
|
|
197
|
+
totalScore *= 1.1;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Normalize score (0-100 scale)
|
|
201
|
+
return Math.min(totalScore / 10, 100);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
escapeRegExp(string) {
|
|
205
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
calculateFuzzyMatch(term, text) {
|
|
209
|
+
// Simple fuzzy matching - check for partial matches
|
|
210
|
+
const words = text.toLowerCase().split(/\s+/);
|
|
211
|
+
let maxScore = 0;
|
|
212
|
+
|
|
213
|
+
for (const word of words) {
|
|
214
|
+
if (word.includes(term) || term.includes(word)) {
|
|
215
|
+
maxScore = Math.max(maxScore, 2);
|
|
216
|
+
} else if (this.levenshteinDistance(term, word) <= 2 && Math.min(term.length, word.length) > 3) {
|
|
217
|
+
maxScore = Math.max(maxScore, 1);
|
|
218
|
+
}
|
|
139
219
|
}
|
|
140
220
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
221
|
+
return maxScore;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
levenshteinDistance(str1, str2) {
|
|
225
|
+
const matrix = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));
|
|
226
|
+
|
|
227
|
+
for (let i = 0; i <= str1.length; i++) matrix[0][i] = i;
|
|
228
|
+
for (let j = 0; j <= str2.length; j++) matrix[j][0] = j;
|
|
229
|
+
|
|
230
|
+
for (let j = 1; j <= str2.length; j++) {
|
|
231
|
+
for (let i = 1; i <= str1.length; i++) {
|
|
232
|
+
const indicator = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
|
233
|
+
matrix[j][i] = Math.min(
|
|
234
|
+
matrix[j][i - 1] + 1,
|
|
235
|
+
matrix[j - 1][i] + 1,
|
|
236
|
+
matrix[j - 1][i - 1] + indicator
|
|
237
|
+
);
|
|
238
|
+
}
|
|
144
239
|
}
|
|
145
240
|
|
|
146
|
-
return
|
|
241
|
+
return matrix[str2.length][str1.length];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
calculateRelevanceScore(doc, searchTerm) {
|
|
245
|
+
// Legacy method - keep for backward compatibility
|
|
246
|
+
return this.calculateAdvancedRelevanceScore(doc, [searchTerm], searchTerm);
|
|
147
247
|
}
|
|
148
248
|
|
|
149
249
|
async getGlobalRules() {
|