@afterxleep/doc-bot 1.8.0 → 1.9.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
@@ -10,24 +10,21 @@ It's platform agnostic and designed to replace and extend the rule systems provi
10
10
  ## What is doc-bot?
11
11
 
12
12
  doc-bot is an intelligent documentation server that:
13
- - 🔍 **Searches** your project documentation instantly
14
- - 🧠 **Auto-indexes** content for smart inference (no manual keyword mapping!)
15
- - 📋 **Applies** global rules to every AI interaction
16
- - 🎯 **Suggests** contextual documentation based on file patterns
17
- - 🤖 **Detects** code patterns, frameworks, and keywords automatically
13
+ - 🧠 **Auto-indexes** content for smart inference, based on metadata and keywords
14
+ - 🤖 **Provides agentic tools** to query, and update your documentation
18
15
  - 🔄 **Updates** automatically when docs change
19
16
 
20
17
  ## Why MCP Instead of Static Rules?
21
18
 
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:
19
+ IDE's use static rule files (like Cursor Rules or Copilot's .github/copilot-instructions.md), and each one has their own format, metadata and approach.
23
20
 
24
21
  ### 🚀 Dynamic Search vs Static Rules
25
22
 
26
23
  **Static Systems:**
27
24
  - All rules must fit in a single file or limited token window
28
25
  - AI reads everything, even irrelevant rules
29
- - No way to search or filter documentation
30
- - Rules compete for precious context space
26
+ - No way to search or filter documentation (besides plain 'grep')
27
+ - Rules compete for context space
31
28
 
32
29
  **MCP with doc-bot:**
33
30
  - AI searches for exactly what it needs
@@ -38,9 +35,8 @@ Traditional AI assistants use static rule files (like Cursor Rules or Copilot's
38
35
  ### 🧠 Contextual Intelligence
39
36
 
40
37
  **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
38
+ - Duplicate or symlinked rules to work with multiple agents
39
+ - Agents use `grep` for basic text-base searching
44
40
 
45
41
  **MCP with doc-bot:**
46
42
  - AI searches for relevant documentation based on your query
@@ -51,8 +47,8 @@ Traditional AI assistants use static rule files (like Cursor Rules or Copilot's
51
47
  ### 📈 Scalability Without Limits
52
48
 
53
49
  **Static Systems:**
54
- - Limited by token count (typically 2-4k tokens)
55
- - Adding more rules means removing others
50
+ - Limited by token count
51
+ - Adding more rules has impact in your context window
56
52
  - Documentation competes with your actual code for context
57
53
 
58
54
  **MCP with doc-bot:**
@@ -66,7 +62,7 @@ Traditional AI assistants use static rule files (like Cursor Rules or Copilot's
66
62
  **Static Systems:**
67
63
  - Changes require restarting your AI/IDE
68
64
  - No way to know if rules are current
69
- - Manual synchronization across tools
65
+ - Manual synchronization across tools and AI agents
70
66
 
71
67
  **MCP with doc-bot:**
72
68
  - Hot reload on documentation changes
@@ -84,7 +80,7 @@ Traditional AI assistants use static rule files (like Cursor Rules or Copilot's
84
80
  **MCP with doc-bot:**
85
81
  - AI can list all available documentation
86
82
  - Discovers relevant docs automatically
87
- - Suggests documentation based on context
83
+ - Suggests documentation based on relevance
88
84
  - Searchable knowledge base with intelligent ranking
89
85
  - No need for AI to grep through your codebase - dedicated search engine
90
86
 
@@ -116,6 +112,8 @@ Traditional AI assistants use static rule files (like Cursor Rules or Copilot's
116
112
  }
117
113
  }
118
114
  ```
115
+ Note: If a relative path does not work, you can use VSCode `${workspaceFolder}`environment variable `${workspaceFolder}/my-custom-docs`
116
+
119
117
 
120
118
  **With verbose logging (for debugging):**
121
119
  ```json
@@ -193,6 +191,7 @@ alwaysApply: false
193
191
  title: "Testing Guide"
194
192
  description: "How to write and run tests"
195
193
  keywords: ["testing", "jest", "tdd", "unit-tests"]
194
+ filePatterns: ["*.test.js", "*.spec.js", "__tests__/**/*"]
196
195
  ---
197
196
 
198
197
  # Testing Guide
@@ -208,14 +207,16 @@ Run tests with: `npm test`
208
207
 
209
208
  ## Frontmatter-Based Configuration
210
209
 
211
- doc-bot uses frontmatter in your markdown files to automatically detect and categorize rules - **no manifest.json required!**
210
+ doc-bot uses frontmatter in your markdown files to automatically detect and categorize rules.
212
211
 
213
212
  ### Frontmatter Fields:
214
213
 
215
214
  - **`alwaysApply: true`** - Global rules applied to every AI interaction
216
215
  - **`alwaysApply: false`** - Contextual rules searched and applied based on relevance
217
216
  - **`keywords: ["list", "of", "keywords"]`** - For smart indexing and search
217
+ - **`filePatterns: ["*.js", "src/**/*.ts"]`** - Apply docs to specific files (see below)
218
218
  - **`title`** and **`description`** - Standard metadata
219
+ - **`confidence: 0.9`** - Relevance confidence score (0-1)
219
220
 
220
221
  ### 🎯 Automatic Intelligence
221
222
 
@@ -243,6 +244,31 @@ keywords: ["react", "components", "hooks", "jsx"]
243
244
  Your documentation content here...
244
245
  ```
245
246
 
247
+ ### File Pattern Matching
248
+
249
+ doc-bot supports contextual documentation using file patterns. Documentation can be targeted to specific files:
250
+
251
+ ```markdown
252
+ ---
253
+ alwaysApply: false
254
+ title: "React Testing Guide"
255
+ keywords: ["testing", "jest", "react"]
256
+ filePatterns: ["*.test.jsx", "*.test.tsx", "__tests__/**/*"]
257
+ ---
258
+
259
+ # React Testing Guide
260
+
261
+ This documentation appears only when working with test files...
262
+ ```
263
+
264
+ **Pattern Examples:**
265
+ - `*.js` - All JavaScript files
266
+ - `src/**/*.ts` - TypeScript files in src directory
267
+ - `[Tt]est.js` - Test.js or test.js
268
+ - `*.{jsx,tsx}` - React component files
269
+
270
+ When AI requests documentation for a specific file (e.g., `Button.test.jsx`), only docs with matching patterns are returned.
271
+
246
272
  ## Development setup
247
273
 
248
274
  ### Running locally
@@ -267,13 +293,7 @@ Your documentation content here...
267
293
  ```bash
268
294
  npm run dev
269
295
  ```
270
-
271
- 5. **Run with examples documentation:**
272
- ```bash
273
- npm run start:examples
274
- ```
275
-
276
- 6. **Run tests:**
296
+ 5. **Run tests:**
277
297
  ```bash
278
298
  npm test
279
299
  ```
@@ -291,7 +311,6 @@ doc-bot [options]
291
311
 
292
312
  Options:
293
313
  -d, --docs <path> Path to docs folder (default: doc-bot)
294
- -c, --config <path> Path to manifest file (optional, for backward compatibility)
295
314
  -v, --verbose Enable verbose logging
296
315
  -w, --watch Watch for file changes
297
316
  -h, --help Show help
@@ -307,9 +326,6 @@ doc-bot --docs ./my-docs
307
326
 
308
327
  # With verbose logging and file watching
309
328
  doc-bot --verbose --watch
310
-
311
- # With optional manifest for backward compatibility
312
- doc-bot --config ./manifest.json
313
329
  ```
314
330
 
315
331
  ## Publishing and Development
@@ -378,4 +394,4 @@ MIT License - see the [LICENSE](LICENSE) file for details.
378
394
 
379
395
  - [npm package](https://www.npmjs.com/package/@afterxleep/doc-bot)
380
396
  - [GitHub repository](https://github.com/afterxleep/doc-bot)
381
- - [Model Context Protocol](https://github.com/modelcontextprotocol/specification)
397
+ - [Model Context Protocol](https://github.com/modelcontextprotocol/specification)
package/bin/doc-bot.js CHANGED
@@ -1,17 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { program } = require('commander');
4
- const path = require('path');
5
- const fs = require('fs-extra');
6
- const { DocsServer } = require('../src/index.js');
7
- const packageJson = require('../package.json');
3
+ import { program } from 'commander';
4
+ import path from 'path';
5
+ import fs from 'fs-extra';
6
+ import { DocsServer } from '../src/index.js';
7
+ import { readFileSync } from 'fs';
8
+ import { fileURLToPath } from 'url';
9
+ import { dirname, join } from 'path';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
8
14
 
9
15
  program
10
16
  .name('doc-bot')
11
17
  .description('Generic MCP server for intelligent documentation access')
12
18
  .version(packageJson.version)
13
19
  .option('-d, --docs <path>', 'Path to docs folder', 'doc-bot')
14
- .option('-c, --config <path>', 'Path to manifest file')
15
20
  .option('-v, --verbose', 'Enable verbose logging')
16
21
  .option('-w, --watch', 'Watch for file changes')
17
22
  .parse();
@@ -20,7 +25,6 @@ const options = program.opts();
20
25
 
21
26
  async function main() {
22
27
  const docsPath = path.resolve(options.docs);
23
- const configPath = options.config ? path.resolve(options.config) : path.resolve(options.docs, 'manifest.json');
24
28
 
25
29
  // Check if documentation folder exists
26
30
  if (!await fs.pathExists(docsPath)) {
@@ -40,23 +44,8 @@ async function main() {
40
44
  process.exit(1);
41
45
  }
42
46
 
43
- // Manifest is now optional - only create if explicitly requested
44
- if (options.config && !await fs.pathExists(configPath)) {
45
- if (options.verbose) {
46
- console.error('📝 Creating default manifest.json...');
47
- }
48
- const defaultManifest = {
49
- name: 'Project Documentation',
50
- version: '1.0.0',
51
- description: 'AI-powered documentation (auto-generated from frontmatter)',
52
- note: 'This manifest is auto-generated. Configure rules using frontmatter in your markdown files.'
53
- };
54
- await fs.writeJSON(configPath, defaultManifest, { spaces: 2 });
55
- }
56
-
57
47
  const server = new DocsServer({
58
48
  docsPath,
59
- configPath,
60
49
  verbose: options.verbose,
61
50
  watch: options.watch
62
51
  });
@@ -64,11 +53,7 @@ async function main() {
64
53
  if (options.verbose) {
65
54
  console.error('🚀 Starting doc-bot...');
66
55
  console.error(`📁 Documentation: ${docsPath}`);
67
- if (await fs.pathExists(configPath)) {
68
- console.error(`⚙️ Configuration: ${configPath}`);
69
- } else {
70
- console.error(`⚙️ Configuration: Auto-generated from frontmatter`);
71
- }
56
+ console.error(`⚙️ Configuration: Frontmatter-based`);
72
57
 
73
58
  if (options.watch) {
74
59
  console.error('👀 Watching for file changes...');
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@afterxleep/doc-bot",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Generic MCP server for intelligent documentation access in any project",
5
+ "type": "module",
5
6
  "main": "src/index.js",
6
7
  "bin": {
7
8
  "doc-bot": "bin/doc-bot.js"
@@ -11,7 +12,7 @@
11
12
  "start:watch": "node bin/doc-bot.js --verbose --watch",
12
13
  "start:examples": "node bin/doc-bot.js --docs ./samples --verbose",
13
14
  "dev": "node bin/doc-bot.js --verbose --watch",
14
- "test": "jest",
15
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest",
15
16
  "test:watch": "jest --watch",
16
17
  "test:coverage": "jest --coverage",
17
18
  "lint": "eslint src/ bin/ --ext .js",
@@ -51,6 +52,10 @@
51
52
  "jest": "^29.7.0",
52
53
  "supertest": "^6.3.0"
53
54
  },
55
+ "jest": {
56
+ "testEnvironment": "node",
57
+ "transform": {}
58
+ },
54
59
  "engines": {
55
60
  "node": ">=18.0.0"
56
61
  },
package/src/index.js CHANGED
@@ -1,18 +1,21 @@
1
- const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
2
- const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
3
- const { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } = require('@modelcontextprotocol/sdk/types.js');
4
- const { DocumentationService } = require('./services/DocumentationService.js');
5
- const { InferenceEngine } = require('./services/InferenceEngine.js');
6
- const { ManifestLoader } = require('./services/ManifestLoader.js');
7
- const chokidar = require('chokidar');
8
- const path = require('path');
9
- const fs = require('fs').promises;
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
+ import { DocumentationService } from './services/DocumentationService.js';
5
+ import { InferenceEngine } from './services/InferenceEngine.js';
6
+ import chokidar from 'chokidar';
7
+ import path from 'path';
8
+ import { promises as fs } from 'fs';
9
+ import { fileURLToPath } from 'url';
10
+ import { dirname } from 'path';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = dirname(__filename);
10
14
 
11
15
  class DocsServer {
12
16
  constructor(options = {}) {
13
17
  this.options = {
14
18
  docsPath: options.docsPath || './doc-bot',
15
- configPath: options.configPath || './doc-bot/manifest.json', // Optional, for backward compatibility
16
19
  verbose: options.verbose || false,
17
20
  watch: options.watch || false,
18
21
  ...options
@@ -32,10 +35,8 @@ class DocsServer {
32
35
  }
33
36
  });
34
37
 
35
- // ManifestLoader is now optional - only create if manifest exists
36
- this.manifestLoader = null;
37
38
  this.docService = new DocumentationService(this.options.docsPath);
38
- this.inferenceEngine = new InferenceEngine(this.docService, null);
39
+ this.inferenceEngine = new InferenceEngine(this.docService);
39
40
 
40
41
  this.setupHandlers();
41
42
 
@@ -60,7 +61,6 @@ class DocsServer {
60
61
  setupHandlers() {
61
62
  // List available resources
62
63
  this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
63
- const manifest = await this.manifestLoader.load();
64
64
  return {
65
65
  resources: [
66
66
  {
@@ -81,12 +81,6 @@ class DocsServer {
81
81
  description: 'Smart documentation suggestions based on your current context',
82
82
  mimeType: 'application/json'
83
83
  },
84
- {
85
- uri: 'docs://manifest',
86
- name: 'Documentation Manifest',
87
- description: 'Project documentation configuration',
88
- mimeType: 'application/json'
89
- },
90
84
  {
91
85
  uri: 'docs://system-prompt',
92
86
  name: 'System Prompt Injection',
@@ -122,24 +116,6 @@ class DocsServer {
122
116
  }]
123
117
  };
124
118
 
125
- case 'docs://manifest':
126
- // Generate manifest from frontmatter data
127
- const manifestDocs = await this.docService.getAllDocuments();
128
- const generatedManifest = {
129
- name: 'Project Documentation',
130
- version: '1.0.0',
131
- description: 'Auto-generated from frontmatter',
132
- globalRules: manifestDocs.filter(doc => doc.metadata?.alwaysApply === true).map(doc => doc.fileName),
133
- contextualRules: this.extractContextualRules(manifestDocs)
134
- };
135
- return {
136
- contents: [{
137
- uri,
138
- mimeType: 'application/json',
139
- text: JSON.stringify(generatedManifest, null, 2)
140
- }]
141
- };
142
-
143
119
  case 'docs://system-prompt':
144
120
  const systemPrompt = await this.generateSystemPrompt();
145
121
  return {
@@ -407,7 +383,7 @@ class DocsServer {
407
383
  }
408
384
 
409
385
  setupWatcher() {
410
- const watcher = chokidar.watch([this.options.docsPath, this.options.configPath], {
386
+ const watcher = chokidar.watch(this.options.docsPath, {
411
387
  ignored: /(^|[\/\\])\../, // ignore dotfiles
412
388
  persistent: true
413
389
  });
@@ -417,15 +393,8 @@ class DocsServer {
417
393
  console.error(`📄 Documentation updated: ${path.relative(process.cwd(), filePath)}`);
418
394
  }
419
395
 
420
- // Reload manifest if config changed
421
- if (filePath === this.options.configPath) {
422
- await this.manifestLoader.reload();
423
- }
424
-
425
396
  // Reload docs if documentation changed
426
- if (filePath.startsWith(this.options.docsPath)) {
427
- await this.docService.reload();
428
- }
397
+ await this.docService.reload();
429
398
  });
430
399
  }
431
400
 
@@ -599,12 +568,11 @@ class DocsServer {
599
568
  }
600
569
 
601
570
  async createOrUpdateRule({ fileName, title, description, keywords, alwaysApply, content }) {
602
- const fs = require('fs-extra');
603
- const path = require('path');
571
+ const { default: fsExtra } = await import('fs-extra');
604
572
 
605
573
  try {
606
574
  // Ensure the docs directory exists
607
- await fs.ensureDir(this.options.docsPath);
575
+ await fsExtra.ensureDir(this.options.docsPath);
608
576
 
609
577
  // Create the full file path
610
578
  const filePath = path.join(this.options.docsPath, fileName);
@@ -625,7 +593,7 @@ class DocsServer {
625
593
  const fullContent = frontmatter + content;
626
594
 
627
595
  // Check if file exists to determine if this is create or update
628
- const fileExists = await fs.pathExists(filePath);
596
+ const fileExists = await fsExtra.pathExists(filePath);
629
597
  const action = fileExists ? 'updated' : 'created';
630
598
 
631
599
  // Write the file
@@ -867,15 +835,6 @@ class DocsServer {
867
835
  }
868
836
 
869
837
  async start() {
870
- // Initialize manifest loader if manifest exists (backward compatibility)
871
- const fs = require('fs-extra');
872
- if (await fs.pathExists(this.options.configPath)) {
873
- this.manifestLoader = new ManifestLoader(this.options.configPath);
874
- await this.manifestLoader.load();
875
- // Update services with manifest loader
876
- this.inferenceEngine = new InferenceEngine(this.docService, this.manifestLoader);
877
- }
878
-
879
838
  // Initialize services
880
839
  await this.docService.initialize();
881
840
  await this.inferenceEngine.initialize();
@@ -886,13 +845,9 @@ class DocsServer {
886
845
 
887
846
  if (this.options.verbose) {
888
847
  console.error('🔧 Server initialized with MCP transport');
889
- if (this.manifestLoader) {
890
- console.error('📄 Using manifest.json for additional configuration');
891
- } else {
892
- console.error('🚀 Using frontmatter-based configuration (no manifest needed)');
893
- }
848
+ console.error('🚀 Using frontmatter-based configuration');
894
849
  }
895
850
  }
896
851
  }
897
852
 
898
- module.exports = { DocsServer };
853
+ export { DocsServer };
package/src/index.test.js CHANGED
@@ -1,6 +1,11 @@
1
- const { DocumentationService } = require('./services/DocumentationService');
2
- const fs = require('fs-extra');
3
- const path = require('path');
1
+ import { DocumentationService } from './services/DocumentationService.js';
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname } from 'path';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
4
9
 
5
10
  describe('DocumentationService get_document_index functionality', () => {
6
11
  let docService;
@@ -376,4 +376,4 @@ class DocumentIndex {
376
376
  }
377
377
  }
378
378
 
379
- module.exports = { DocumentIndex };
379
+ export { DocumentIndex };
@@ -1,7 +1,7 @@
1
- const fs = require('fs-extra');
2
- const path = require('path');
3
- const glob = require('glob');
4
- const yaml = require('yaml');
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { glob } from 'glob';
4
+ import yaml from 'yaml';
5
5
 
6
6
  class DocumentationService {
7
7
  constructor(docsPath, manifestLoader = null) {
@@ -326,4 +326,4 @@ class DocumentationService {
326
326
 
327
327
  }
328
328
 
329
- module.exports = { DocumentationService };
329
+ export { DocumentationService };
@@ -1,10 +1,9 @@
1
- const path = require('path');
2
- const { DocumentIndex } = require('./DocumentIndex');
1
+ import path from 'path';
2
+ import { DocumentIndex } from './DocumentIndex.js';
3
3
 
4
4
  class InferenceEngine {
5
- constructor(documentationService, manifestLoader = null) {
5
+ constructor(documentationService) {
6
6
  this.docService = documentationService;
7
- this.manifestLoader = manifestLoader;
8
7
  this.documentIndex = new DocumentIndex();
9
8
  this.isIndexBuilt = false;
10
9
  }
@@ -106,52 +105,13 @@ class InferenceEngine {
106
105
  }
107
106
 
108
107
  async getDocsByKeywords(query) {
109
- if (!this.manifestLoader) {
110
- return [];
111
- }
112
-
113
- const manifest = await this.manifestLoader.load();
114
- const keywords = manifest.inference?.keywords || {};
115
-
116
- const docs = [];
117
- const queryLower = query.toLowerCase();
118
-
119
- for (const [keyword, docPaths] of Object.entries(keywords)) {
120
- if (queryLower.includes(keyword.toLowerCase())) {
121
- for (const docPath of docPaths) {
122
- const doc = this.docService.getDocument(docPath);
123
- if (doc) {
124
- docs.push(doc);
125
- }
126
- }
127
- }
128
- }
129
-
130
- return docs;
108
+ // Now handled by DocumentIndex
109
+ return [];
131
110
  }
132
111
 
133
112
  async getDocsByPatterns(codeSnippet) {
134
- if (!this.manifestLoader) {
135
- return [];
136
- }
137
-
138
- const manifest = await this.manifestLoader.load();
139
- const patterns = manifest.inference?.patterns || {};
140
-
141
- const docs = [];
142
-
143
- for (const [pattern, docPaths] of Object.entries(patterns)) {
144
- if (codeSnippet.includes(pattern)) {
145
- for (const docPath of docPaths) {
146
- const doc = this.docService.getDocument(docPath);
147
- if (doc) {
148
- docs.push(doc);
149
- }
150
- }
151
- }
152
- }
153
-
154
- return docs;
113
+ // Now handled by DocumentIndex
114
+ return [];
155
115
  }
156
116
 
157
117
  async getDocsByFileExtension(filePath) {
@@ -245,4 +205,4 @@ class InferenceEngine {
245
205
  }
246
206
  }
247
207
 
248
- module.exports = { InferenceEngine };
208
+ export { InferenceEngine };
@@ -1,4 +1,4 @@
1
- const { DocumentIndex } = require('../DocumentIndex');
1
+ import { DocumentIndex } from '../DocumentIndex.js';
2
2
 
3
3
  describe('DocumentIndex', () => {
4
4
  let documentIndex;
@@ -1,13 +1,17 @@
1
- const { InferenceEngine } = require('../InferenceEngine');
2
- const { DocumentationService } = require('../DocumentationService');
3
- const { ManifestLoader } = require('../ManifestLoader');
4
- const fs = require('fs-extra');
5
- const path = require('path');
1
+ import { jest } from '@jest/globals';
2
+ import { InferenceEngine } from '../InferenceEngine.js';
3
+ import { DocumentationService } from '../DocumentationService.js';
4
+ import fs from 'fs-extra';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import { dirname } from 'path';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
6
11
 
7
12
  describe('InferenceEngine Integration', () => {
8
13
  let inferenceEngine;
9
14
  let mockDocService;
10
- let mockManifestLoader;
11
15
  let tempDir;
12
16
 
13
17
  beforeEach(async () => {
@@ -109,19 +113,7 @@ Building REST APIs with Express.js.
109
113
  getContextualDocs: jest.fn().mockResolvedValue([])
110
114
  };
111
115
 
112
- // Create mock ManifestLoader
113
- mockManifestLoader = {
114
- load: jest.fn().mockResolvedValue({
115
- globalRules: [],
116
- contextualRules: {},
117
- inference: {
118
- keywords: {},
119
- patterns: {}
120
- }
121
- })
122
- };
123
-
124
- inferenceEngine = new InferenceEngine(mockDocService, mockManifestLoader);
116
+ inferenceEngine = new InferenceEngine(mockDocService);
125
117
  await inferenceEngine.initialize();
126
118
  });
127
119
 
@@ -257,7 +249,7 @@ Building REST APIs with Express.js.
257
249
  getContextualDocs: jest.fn().mockResolvedValue([])
258
250
  };
259
251
 
260
- const fallbackEngine = new InferenceEngine(failingDocService, mockManifestLoader);
252
+ const fallbackEngine = new InferenceEngine(failingDocService);
261
253
  await fallbackEngine.initialize();
262
254
 
263
255
  expect(fallbackEngine.isIndexBuilt).toBe(false);
@@ -1,135 +0,0 @@
1
- const fs = require('fs-extra');
2
- const path = require('path');
3
-
4
- class ManifestLoader {
5
- constructor(configPath) {
6
- this.configPath = configPath;
7
- this.manifest = null;
8
- this.lastModified = null;
9
- }
10
-
11
- async load() {
12
- try {
13
- const stats = await fs.stat(this.configPath);
14
-
15
- // Only reload if file has changed
16
- if (this.manifest && this.lastModified && stats.mtime <= this.lastModified) {
17
- return this.manifest;
18
- }
19
-
20
- const content = await fs.readFile(this.configPath, 'utf8');
21
- this.manifest = JSON.parse(content);
22
- this.lastModified = stats.mtime;
23
-
24
- // Validate manifest structure
25
- this.validateManifest();
26
-
27
- return this.manifest;
28
- } catch (error) {
29
- if (error.code === 'ENOENT') {
30
- // Create default manifest if file doesn't exist
31
- this.manifest = this.createDefaultManifest();
32
- await this.save();
33
- return this.manifest;
34
- }
35
- throw new Error(`Failed to load manifest: ${error.message}`);
36
- }
37
- }
38
-
39
- async reload() {
40
- this.manifest = null;
41
- this.lastModified = null;
42
- return await this.load();
43
- }
44
-
45
- async save() {
46
- if (!this.manifest) {
47
- throw new Error('No manifest to save');
48
- }
49
-
50
- await fs.ensureDir(path.dirname(this.configPath));
51
- await fs.writeJSON(this.configPath, this.manifest, { spaces: 2 });
52
- }
53
-
54
- validateManifest() {
55
- if (!this.manifest) {
56
- throw new Error('Manifest is null');
57
- }
58
-
59
- // Required fields
60
- if (!this.manifest.name) {
61
- this.manifest.name = 'Project Documentation';
62
- }
63
-
64
- if (!this.manifest.version) {
65
- this.manifest.version = '1.0.0';
66
- }
67
-
68
- // Optional fields with defaults
69
- if (!this.manifest.globalRules) {
70
- this.manifest.globalRules = [];
71
- }
72
-
73
- if (!this.manifest.contextualRules) {
74
- this.manifest.contextualRules = {};
75
- }
76
-
77
- if (!this.manifest.inference) {
78
- this.manifest.inference = {
79
- keywords: {},
80
- patterns: {}
81
- };
82
- }
83
-
84
- // Validate globalRules is array
85
- if (!Array.isArray(this.manifest.globalRules)) {
86
- throw new Error('globalRules must be an array');
87
- }
88
-
89
- // Validate contextualRules is object
90
- if (typeof this.manifest.contextualRules !== 'object') {
91
- throw new Error('contextualRules must be an object');
92
- }
93
-
94
- // Validate inference structure
95
- if (!this.manifest.inference.keywords) {
96
- this.manifest.inference.keywords = {};
97
- }
98
-
99
- if (!this.manifest.inference.patterns) {
100
- this.manifest.inference.patterns = {};
101
- }
102
- }
103
-
104
- createDefaultManifest() {
105
- return {
106
- name: 'Project Documentation',
107
- version: '1.0.0',
108
- description: 'AI-powered documentation',
109
- globalRules: [],
110
- contextualRules: {},
111
- inference: {
112
- keywords: {},
113
- patterns: {}
114
- }
115
- };
116
- }
117
-
118
- getManifest() {
119
- return this.manifest;
120
- }
121
-
122
- getGlobalRules() {
123
- return this.manifest?.globalRules || [];
124
- }
125
-
126
- getContextualRules() {
127
- return this.manifest?.contextualRules || {};
128
- }
129
-
130
- getInferenceConfig() {
131
- return this.manifest?.inference || { keywords: {}, patterns: {} };
132
- }
133
- }
134
-
135
- module.exports = { ManifestLoader };