@afterxleep/doc-bot 1.7.11 → 1.8.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
@@ -99,7 +99,7 @@ Traditional AI assistants use static rule files (like Cursor Rules or Copilot's
99
99
  "mcpServers": {
100
100
  "doc-bot": {
101
101
  "command": "npx",
102
- "args": ["@afterxleep/doc-bot"]
102
+ "args": ["@afterxleep/doc-bot@latest"]
103
103
  }
104
104
  }
105
105
  }
@@ -111,7 +111,7 @@ Traditional AI assistants use static rule files (like Cursor Rules or Copilot's
111
111
  "mcpServers": {
112
112
  "doc-bot": {
113
113
  "command": "npx",
114
- "args": ["@afterxleep/doc-bot", "--docs", "./my-custom-docs"]
114
+ "args": ["@afterxleep/doc-bot@latest", "--docs", "./my-custom-docs"]
115
115
  }
116
116
  }
117
117
  }
@@ -123,7 +123,7 @@ Traditional AI assistants use static rule files (like Cursor Rules or Copilot's
123
123
  "mcpServers": {
124
124
  "doc-bot": {
125
125
  "command": "npx",
126
- "args": ["@afterxleep/doc-bot", "--verbose"]
126
+ "args": ["@afterxleep/doc-bot@latest", "--verbose"]
127
127
  }
128
128
  }
129
129
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afterxleep/doc-bot",
3
- "version": "1.7.11",
3
+ "version": "1.8.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
@@ -267,6 +267,15 @@ class DocsServer {
267
267
  properties: {},
268
268
  additionalProperties: false
269
269
  }
270
+ },
271
+ {
272
+ name: 'get_document_index',
273
+ description: 'Get an index of all documents in the store with title, description, and last updated date.',
274
+ inputSchema: {
275
+ type: 'object',
276
+ properties: {},
277
+ additionalProperties: false
278
+ }
270
279
  }
271
280
  ]
272
281
  };
@@ -372,6 +381,16 @@ class DocsServer {
372
381
  text: `āœ… Documentation refreshed successfully!\n\n**Files indexed:** ${docCount}\n**Last updated:** ${new Date().toLocaleString()}\n\nšŸ’” All manually added files should now be available for search and reading.`
373
382
  }]
374
383
  };
384
+
385
+ case 'get_document_index':
386
+ const documentIndex = await this.docService.getDocumentIndex();
387
+
388
+ return {
389
+ content: [{
390
+ type: 'text',
391
+ text: await this.formatDocumentIndex(documentIndex)
392
+ }]
393
+ };
375
394
 
376
395
  default:
377
396
  throw new Error(`Unknown tool: ${name}`);
@@ -554,6 +573,31 @@ class DocsServer {
554
573
  return output;
555
574
  }
556
575
 
576
+ async formatDocumentIndex(documentIndex) {
577
+ if (!documentIndex || documentIndex.length === 0) {
578
+ return 'No documents found in the store.';
579
+ }
580
+
581
+ let output = '# Document Index\n\n';
582
+ output += `Found ${documentIndex.length} document(s) in the store:\n\n`;
583
+
584
+ documentIndex.forEach((doc, index) => {
585
+ output += `## ${index + 1}. ${doc.title}\n\n`;
586
+ output += `**File:** ${doc.fileName}\n`;
587
+
588
+ if (doc.description) {
589
+ output += `**Description:** ${doc.description}\n`;
590
+ }
591
+
592
+ output += `**Last Updated:** ${new Date(doc.lastUpdated).toLocaleString()}\n\n`;
593
+ output += '---\n\n';
594
+ });
595
+
596
+ output += 'šŸ’” **Next Steps:** Use the `read_specific_document` tool with the file name to get the full content of any document above.\n';
597
+
598
+ return output;
599
+ }
600
+
557
601
  async createOrUpdateRule({ fileName, title, description, keywords, alwaysApply, content }) {
558
602
  const fs = require('fs-extra');
559
603
  const path = require('path');
@@ -0,0 +1,109 @@
1
+ const { DocumentationService } = require('./services/DocumentationService');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+
5
+ describe('DocumentationService get_document_index functionality', () => {
6
+ let docService;
7
+ let tempDocsPath;
8
+
9
+ beforeEach(async () => {
10
+ // Create a temporary directory for test documents
11
+ tempDocsPath = path.join(__dirname, '../test-docs');
12
+ await fs.ensureDir(tempDocsPath);
13
+
14
+ // Create test documents
15
+ const testDocs = [
16
+ {
17
+ fileName: 'react-guide.md',
18
+ content: '---\nalwaysApply: false\ntitle: "React Component Guide"\ndescription: "Learn how to build React components"\nkeywords: ["react", "components", "jsx"]\n---\n\n# React Components\n\nThis guide covers React components.'
19
+ },
20
+ {
21
+ fileName: 'testing-standards.md',
22
+ content: '---\nalwaysApply: true\ntitle: "Testing Standards"\ndescription: "Project testing requirements"\nkeywords: ["testing", "jest", "standards"]\n---\n\n# Testing Standards\n\nAll code must have tests.'
23
+ },
24
+ {
25
+ fileName: 'api-design.md',
26
+ content: '---\nalwaysApply: false\ntitle: "API Design Guidelines"\ndescription: "REST API design patterns"\nkeywords: ["api", "rest", "design"]\n---\n\n# API Design\n\nFollow REST principles.'
27
+ }
28
+ ];
29
+
30
+ // Write test documents to temp directory
31
+ for (const doc of testDocs) {
32
+ await fs.writeFile(path.join(tempDocsPath, doc.fileName), doc.content);
33
+ }
34
+
35
+ // Create DocumentationService instance
36
+ docService = new DocumentationService(tempDocsPath);
37
+ await docService.initialize();
38
+ });
39
+
40
+ afterEach(async () => {
41
+ // Clean up temporary directory
42
+ await fs.remove(tempDocsPath);
43
+ });
44
+
45
+ describe('getDocumentIndex method', () => {
46
+ it('should be implemented and return document index', async () => {
47
+ expect(typeof docService.getDocumentIndex).toBe('function');
48
+
49
+ const index = await docService.getDocumentIndex();
50
+
51
+ expect(Array.isArray(index)).toBe(true);
52
+ expect(index.length).toBe(3);
53
+
54
+ // Check that each document has required fields
55
+ index.forEach(doc => {
56
+ expect(doc).toHaveProperty('title');
57
+ expect(doc).toHaveProperty('description');
58
+ expect(doc).toHaveProperty('fileName');
59
+ expect(doc).toHaveProperty('lastUpdated');
60
+ expect(typeof doc.lastUpdated).toBe('string');
61
+ });
62
+ });
63
+
64
+ it('should return documents sorted by title', async () => {
65
+ const index = await docService.getDocumentIndex();
66
+
67
+ // Should be sorted alphabetically by title
68
+ expect(index[0].title).toBe('API Design Guidelines');
69
+ expect(index[1].title).toBe('React Component Guide');
70
+ expect(index[2].title).toBe('Testing Standards');
71
+ });
72
+
73
+ it('should include metadata from frontmatter', async () => {
74
+ const index = await docService.getDocumentIndex();
75
+
76
+ const reactDoc = index.find(doc => doc.fileName === 'react-guide.md');
77
+ expect(reactDoc.title).toBe('React Component Guide');
78
+ expect(reactDoc.description).toBe('Learn how to build React components');
79
+
80
+ const testingDoc = index.find(doc => doc.fileName === 'testing-standards.md');
81
+ expect(testingDoc.title).toBe('Testing Standards');
82
+ expect(testingDoc.description).toBe('Project testing requirements');
83
+ });
84
+
85
+ it('should use file name as title when no title in metadata', async () => {
86
+ // Create a document without title metadata
87
+ const docWithoutTitle = '---\ndescription: "A document without title"\n---\n\nSome content';
88
+ await fs.writeFile(path.join(tempDocsPath, 'no-title.md'), docWithoutTitle);
89
+
90
+ // Reload documents to pick up the new file
91
+ await docService.reload();
92
+
93
+ const index = await docService.getDocumentIndex();
94
+ const noTitleDoc = index.find(doc => doc.fileName === 'no-title.md');
95
+
96
+ expect(noTitleDoc.title).toBe('no-title.md');
97
+ expect(noTitleDoc.description).toBe('A document without title');
98
+ });
99
+
100
+ it('should handle empty description gracefully', async () => {
101
+ const index = await docService.getDocumentIndex();
102
+
103
+ // All test documents should have descriptions, but let's test the structure
104
+ index.forEach(doc => {
105
+ expect(typeof doc.description).toBe('string');
106
+ });
107
+ });
108
+ });
109
+ });
@@ -308,6 +308,22 @@ class DocumentationService {
308
308
  return results;
309
309
  }
310
310
 
311
+ async getDocumentIndex() {
312
+ const index = [];
313
+
314
+ for (const doc of this.documents.values()) {
315
+ index.push({
316
+ title: doc.metadata?.title || doc.fileName,
317
+ description: doc.metadata?.description || '',
318
+ fileName: doc.fileName,
319
+ lastUpdated: doc.lastModified.toISOString()
320
+ });
321
+ }
322
+
323
+ // Sort by title for consistent ordering
324
+ return index.sort((a, b) => a.title.localeCompare(b.title));
325
+ }
326
+
311
327
  }
312
328
 
313
329
  module.exports = { DocumentationService };