@afterxleep/doc-bot 1.6.0 → 1.7.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 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)
@@ -88,7 +157,6 @@ alwaysApply: false
88
157
  title: "Testing Guide"
89
158
  description: "How to write and run tests"
90
159
  keywords: ["testing", "jest", "tdd", "unit-tests"]
91
- filePatterns: ["*.js"]
92
160
  ---
93
161
 
94
162
  # Testing Guide
@@ -109,8 +177,7 @@ doc-bot uses frontmatter in your markdown files to automatically detect and cate
109
177
  ### Frontmatter Fields:
110
178
 
111
179
  - **`alwaysApply: true`** - Global rules applied to every AI interaction
112
- - **`alwaysApply: false`** - Contextual rules applied based on file patterns
113
- - **`filePatterns: ["*.js"]`** - When to apply contextual rules (only needed for `alwaysApply: false`)
180
+ - **`alwaysApply: false`** - Contextual rules searched and applied based on relevance
114
181
  - **`keywords: ["list", "of", "keywords"]`** - For smart indexing and search
115
182
  - **`title`** and **`description`** - Standard metadata
116
183
 
@@ -119,7 +186,7 @@ doc-bot uses frontmatter in your markdown files to automatically detect and cate
119
186
  doc-bot automatically analyzes your documentation to provide smart suggestions:
120
187
 
121
188
  - **Keyword-based search** from frontmatter metadata
122
- - **Context-aware suggestions** based on file patterns
189
+ - **Multi-term search** with fuzzy matching capabilities
123
190
  - **Smart inference** from documentation content
124
191
  - **Automatic indexing** - no manual configuration needed
125
192
 
@@ -133,7 +200,6 @@ alwaysApply: false
133
200
  title: "React Component Guidelines"
134
201
  description: "Best practices for building React components"
135
202
  keywords: ["react", "components", "hooks", "jsx"]
136
- filePatterns: ["*.js"]
137
203
  ---
138
204
 
139
205
  # React Component Guidelines
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afterxleep/doc-bot",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Generic MCP server for intelligent documentation access in any project",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -161,7 +161,7 @@ class DocsServer {
161
161
  tools: [
162
162
  {
163
163
  name: 'check_project_rules',
164
- description: '🚨 MANDATORY BEFORE ANY CODE: You are REQUIRED to call this before writing ANY code. This prevents bugs, security issues, and standard violations. Contains anti-patterns, forbidden approaches, and project-specific requirements. NOT OPTIONAL.',
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: '🧠 PROJECT EXPERT MODE: Transform from generic AI to project expert! Search for architecture, patterns, approaches, best practices. Essential for questions about "what is the...", "how does this project...", "preferred approach". Makes you 10x more valuable.',
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: '🎯 CONTEXTUAL INTELLIGENCE: Get laser-focused guidance for specific files, directories, or features you\'re working on. Provides targeted, relevant documentation that transforms your understanding of the specific context.',
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: '📋 CAPABILITY SHOWCASE: Reveal your enhanced project expertise! Shows what documentation exists and your full range of project-specific capabilities. Perfect for "what documentation is available?", "what can you help with?", "what do you know about this project?". Proves you\'re not just generic AI.',
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 searchTerm = query.toLowerCase();
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.calculateRelevanceScore(doc, searchTerm);
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
- calculateRelevanceScore(doc, searchTerm) {
115
- let score = 0;
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
- // Title matches get highest score
120
- if (title.includes(searchTerm)) {
121
- score += 10;
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
- // Content matches
125
- const contentMatches = (content.match(new RegExp(searchTerm, 'g')) || []).length;
126
- score += contentMatches * 2;
134
+ let matchedTerms = 0;
135
+ const termScores = [];
127
136
 
128
- // Keyword matches in metadata
129
- if (doc.metadata?.keywords) {
130
- const keywords = Array.isArray(doc.metadata.keywords)
131
- ? doc.metadata.keywords
132
- : [doc.metadata.keywords];
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
- for (const keyword of keywords) {
135
- if (keyword.toLowerCase().includes(searchTerm)) {
136
- score += 5;
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
- // Category matches
142
- if (doc.metadata?.category?.toLowerCase().includes(searchTerm)) {
143
- score += 3;
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 score;
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() {