@afterxleep/doc-bot 1.17.0 → 1.19.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/package.json +7 -4
- package/src/__tests__/temp-docs-1756129972061/test.md +5 -0
- package/src/__tests__/temp-docs-1756129972071/test.md +5 -0
- package/src/__tests__/temp-docs-1756129972075/test.md +5 -0
- package/src/__tests__/temp-docs-1756129972077/test.md +5 -0
- package/src/__tests__/temp-docs-1756129972079/test.md +5 -0
- package/src/__tests__/temp-docs-1756130189361/test.md +5 -0
- package/src/__tests__/temp-docs-1756130189372/test.md +5 -0
- package/src/__tests__/temp-docs-1756130189375/test.md +5 -0
- package/src/__tests__/temp-docs-1756130189378/test.md +5 -0
- package/src/__tests__/temp-docs-1756130189379/test.md +5 -0
- package/src/__tests__/temp-docs-1756130271128/test.md +5 -0
- package/src/__tests__/temp-docs-1756130271139/test.md +5 -0
- package/src/__tests__/temp-docs-1756130271142/test.md +5 -0
- package/src/__tests__/temp-docs-1756130271145/test.md +5 -0
- package/src/__tests__/temp-docs-1756130271146/test.md +5 -0
- package/src/__tests__/temp-docs-1756130687030/test.md +5 -0
- package/src/__tests__/temp-docs-1756130687044/test.md +5 -0
- package/src/__tests__/temp-docs-1756130687048/test.md +5 -0
- package/src/__tests__/temp-docs-1756130687051/test.md +5 -0
- package/src/__tests__/temp-docs-1756130687053/test.md +5 -0
- package/src/__tests__/temp-docs-1756131694925/test.md +5 -0
- package/src/__tests__/temp-docs-1756131694937/test.md +5 -0
- package/src/__tests__/temp-docs-1756131694941/test.md +5 -0
- package/src/__tests__/temp-docs-1756131694944/test.md +5 -0
- package/src/__tests__/temp-docs-1756131694946/test.md +5 -0
- package/src/__tests__/temp-docs-1756133998710/test.md +5 -0
- package/src/__tests__/temp-docs-1756133998721/test.md +5 -0
- package/src/__tests__/temp-docs-1756133998724/test.md +5 -0
- package/src/__tests__/temp-docs-1756133998727/test.md +5 -0
- package/src/__tests__/temp-docs-1756133998729/test.md +5 -0
- package/src/__tests__/temp-docs-1756134345935/test.md +5 -0
- package/src/__tests__/temp-docs-1756134345948/test.md +5 -0
- package/src/__tests__/temp-docs-1756134345952/test.md +5 -0
- package/src/__tests__/temp-docs-1756134345954/test.md +5 -0
- package/src/__tests__/temp-docs-1756134345957/test.md +5 -0
- package/src/__tests__/temp-docsets-1756129972079/2e443167/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756129972079/2e443167/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756129972079/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756129972079/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756129972079/docsets.json +10 -0
- package/src/__tests__/temp-docsets-1756130189379/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756130189379/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756130189379/a4934c14/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756130189379/a4934c14/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756130189379/docsets.json +10 -0
- package/src/__tests__/temp-docsets-1756130271146/3f8acbb2/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756130271146/3f8acbb2/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756130271146/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756130271146/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756130271146/docsets.json +10 -0
- package/src/__tests__/temp-docsets-1756130687053/6810e6bd/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756130687053/6810e6bd/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756130687053/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756130687053/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756130687053/docsets.json +10 -0
- package/src/__tests__/temp-docsets-1756131694946/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756131694946/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756131694946/dd703046/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756131694946/dd703046/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756131694946/docsets.json +10 -0
- package/src/__tests__/temp-docsets-1756133998729/9e061136/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756133998729/9e061136/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756133998729/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756133998729/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756133998729/docsets.json +10 -0
- package/src/__tests__/temp-docsets-1756134345957/03e730af/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756134345957/03e730af/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756134345957/Mock.docset/Contents/Info.plist +10 -0
- package/src/__tests__/temp-docsets-1756134345957/Mock.docset/Contents/Resources/docSet.dsidx +0 -0
- package/src/__tests__/temp-docsets-1756134345957/docsets.json +10 -0
- package/src/index.js +269 -63
- package/src/services/DocumentationService.js +26 -1
- package/src/services/PaginationService.js +378 -0
- package/src/services/__tests__/PaginationService.integration.test.js +185 -0
- package/src/services/__tests__/PaginationService.test.js +398 -0
- package/src/utils/TokenEstimator.js +134 -0
- package/prompts/file-docs.md +0 -69
- package/prompts/global-rules.md +0 -142
- package/prompts/mandatory-rules.md +0 -90
- package/prompts/search-results.md +0 -59
- package/prompts/system-prompt.md +0 -270
- package/src/__tests__/docset-integration.test.js +0 -146
- package/src/services/__tests__/DocumentationService.test.js +0 -318
- package/src/services/__tests__/UnifiedSearchService.test.js +0 -302
- package/src/services/docset/__tests__/EnhancedDocsetDatabase.test.js +0 -324
package/src/index.js
CHANGED
|
@@ -6,9 +6,12 @@ import { InferenceEngine } from './services/InferenceEngine.js';
|
|
|
6
6
|
import { MultiDocsetDatabase } from './services/docset/database.js';
|
|
7
7
|
import { DocsetService } from './services/docset/index.js';
|
|
8
8
|
import { UnifiedSearchService } from './services/UnifiedSearchService.js';
|
|
9
|
+
import { PaginationService } from './services/PaginationService.js';
|
|
10
|
+
import { TokenEstimator } from './utils/TokenEstimator.js';
|
|
9
11
|
import chokidar from 'chokidar';
|
|
10
12
|
import path from 'path';
|
|
11
13
|
import { promises as fs } from 'fs';
|
|
14
|
+
import fsExtra from 'fs-extra';
|
|
12
15
|
import { fileURLToPath } from 'url';
|
|
13
16
|
import os from 'os';
|
|
14
17
|
|
|
@@ -52,6 +55,9 @@ class DocsServer {
|
|
|
52
55
|
// Initialize unified search
|
|
53
56
|
this.unifiedSearch = new UnifiedSearchService(this.docService, this.multiDocsetDb);
|
|
54
57
|
|
|
58
|
+
// Initialize pagination service
|
|
59
|
+
this.paginationService = new PaginationService();
|
|
60
|
+
|
|
55
61
|
this.setupHandlers();
|
|
56
62
|
|
|
57
63
|
if (this.options.watch) {
|
|
@@ -174,7 +180,7 @@ class DocsServer {
|
|
|
174
180
|
},
|
|
175
181
|
{
|
|
176
182
|
name: 'search_documentation',
|
|
177
|
-
description: 'Search codebase documentation and API references. Searches project patterns, architecture decisions, and API docs. Best results with technical terms like class names, not descriptions. Examples: search "Widget" not "iOS 18 features".',
|
|
183
|
+
description: 'Search codebase documentation and API references. Searches project patterns, architecture decisions, and API docs. Best results with technical terms like class names, not descriptions. Examples: search "Widget" not "iOS 18 features". Supports pagination for large result sets.',
|
|
178
184
|
inputSchema: {
|
|
179
185
|
type: 'object',
|
|
180
186
|
properties: {
|
|
@@ -184,7 +190,11 @@ class DocsServer {
|
|
|
184
190
|
},
|
|
185
191
|
limit: {
|
|
186
192
|
type: 'number',
|
|
187
|
-
description: 'Maximum results
|
|
193
|
+
description: 'Maximum results per page. Default: 20'
|
|
194
|
+
},
|
|
195
|
+
page: {
|
|
196
|
+
type: 'number',
|
|
197
|
+
description: 'Page number for paginated results. Default: 1'
|
|
188
198
|
},
|
|
189
199
|
docsetId: {
|
|
190
200
|
type: 'string',
|
|
@@ -203,7 +213,12 @@ class DocsServer {
|
|
|
203
213
|
description: 'Get comprehensive coding standards and architecture guidelines. Returns project-wide engineering principles, design patterns, performance requirements, and security standards. Essential reading for understanding the codebase philosophy.',
|
|
204
214
|
inputSchema: {
|
|
205
215
|
type: 'object',
|
|
206
|
-
properties: {
|
|
216
|
+
properties: {
|
|
217
|
+
page: {
|
|
218
|
+
type: 'number',
|
|
219
|
+
description: 'Page number for paginated results. Default: 1'
|
|
220
|
+
}
|
|
221
|
+
},
|
|
207
222
|
additionalProperties: false
|
|
208
223
|
}
|
|
209
224
|
},
|
|
@@ -230,6 +245,10 @@ class DocsServer {
|
|
|
230
245
|
fileName: {
|
|
231
246
|
type: 'string',
|
|
232
247
|
description: 'Name of the documentation file to read. Must match exactly. Example: "coding-standards.md"'
|
|
248
|
+
},
|
|
249
|
+
page: {
|
|
250
|
+
type: 'number',
|
|
251
|
+
description: 'Page number for paginated content. Default: 1'
|
|
233
252
|
}
|
|
234
253
|
},
|
|
235
254
|
required: ['fileName']
|
|
@@ -369,7 +388,8 @@ class DocsServer {
|
|
|
369
388
|
switch (name) {
|
|
370
389
|
case 'check_project_rules':
|
|
371
390
|
const task = args?.task || 'code generation';
|
|
372
|
-
const
|
|
391
|
+
const taskPage = args?.page || 1;
|
|
392
|
+
const mandatoryRules = await this.getMandatoryRules(task, taskPage);
|
|
373
393
|
return {
|
|
374
394
|
content: [{
|
|
375
395
|
type: 'text',
|
|
@@ -382,25 +402,100 @@ class DocsServer {
|
|
|
382
402
|
if (!unifiedQuery) {
|
|
383
403
|
throw new Error('Query parameter is required');
|
|
384
404
|
}
|
|
405
|
+
const searchPage = args?.page || 1;
|
|
406
|
+
const searchLimit = args?.limit || 20;
|
|
407
|
+
|
|
408
|
+
// Calculate offset for pagination
|
|
409
|
+
const searchOffset = (searchPage - 1) * searchLimit;
|
|
410
|
+
|
|
385
411
|
const unifiedOptions = {
|
|
386
|
-
limit:
|
|
412
|
+
limit: searchLimit * 3, // Get more results for pagination
|
|
387
413
|
docsetId: args?.docsetId,
|
|
388
414
|
type: args?.type
|
|
389
415
|
};
|
|
390
|
-
const
|
|
416
|
+
const allResults = await this.unifiedSearch.search(unifiedQuery, unifiedOptions);
|
|
417
|
+
|
|
418
|
+
// Paginate results
|
|
419
|
+
const paginatedSearchResults = this.paginationService.paginateArray(
|
|
420
|
+
allResults,
|
|
421
|
+
searchPage,
|
|
422
|
+
searchLimit
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
// Format the current page of results
|
|
426
|
+
let searchResponse = await this.formatUnifiedSearchResults(paginatedSearchResults.items, unifiedQuery);
|
|
427
|
+
|
|
428
|
+
// Add pagination info if there are results
|
|
429
|
+
if (paginatedSearchResults.totalItems > 0) {
|
|
430
|
+
searchResponse += this.paginationService.formatPaginationInfo(paginatedSearchResults);
|
|
431
|
+
}
|
|
432
|
+
|
|
391
433
|
return {
|
|
392
434
|
content: [{
|
|
393
435
|
type: 'text',
|
|
394
|
-
text:
|
|
436
|
+
text: searchResponse
|
|
395
437
|
}]
|
|
396
438
|
};
|
|
397
439
|
|
|
398
440
|
case 'get_global_rules':
|
|
399
441
|
const globalRules = await this.docService.getGlobalRules();
|
|
442
|
+
const globalPage = args?.page || 1;
|
|
443
|
+
|
|
444
|
+
// Format all global rules into one combined text
|
|
445
|
+
const allGlobalRulesText = this.formatGlobalRulesArray(globalRules);
|
|
446
|
+
const estimatedTokens = TokenEstimator.estimateTokens(allGlobalRulesText);
|
|
447
|
+
|
|
448
|
+
// Check if pagination is needed
|
|
449
|
+
if (estimatedTokens <= 20000) {
|
|
450
|
+
// Content fits in a single response
|
|
451
|
+
return {
|
|
452
|
+
content: [{
|
|
453
|
+
type: 'text',
|
|
454
|
+
text: allGlobalRulesText
|
|
455
|
+
}]
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Use text-level pagination for large content
|
|
460
|
+
const chunks = this.paginationService.chunkText(allGlobalRulesText, 20000); // 20k tokens per chunk
|
|
461
|
+
const totalPages = chunks.length;
|
|
462
|
+
|
|
463
|
+
if (globalPage < 1 || globalPage > totalPages) {
|
|
464
|
+
return {
|
|
465
|
+
content: [{
|
|
466
|
+
type: 'text',
|
|
467
|
+
text: `Invalid page number. Please use page 1-${totalPages}.`
|
|
468
|
+
}]
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const pageContent = chunks[globalPage - 1];
|
|
473
|
+
|
|
474
|
+
// Build pagination info
|
|
475
|
+
const pagination = {
|
|
476
|
+
page: globalPage,
|
|
477
|
+
totalPages: totalPages,
|
|
478
|
+
hasMore: globalPage < totalPages,
|
|
479
|
+
nextPage: globalPage < totalPages ? globalPage + 1 : null,
|
|
480
|
+
prevPage: globalPage > 1 ? globalPage - 1 : null,
|
|
481
|
+
isChunked: true,
|
|
482
|
+
totalItems: globalRules.length
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
// Add pagination headers/footers if needed
|
|
486
|
+
let responseText = '';
|
|
487
|
+
if (pagination.hasMore || globalPage > 1) {
|
|
488
|
+
responseText += this.paginationService.formatPaginationHeader(pagination);
|
|
489
|
+
}
|
|
490
|
+
responseText += pageContent;
|
|
491
|
+
if (pagination.hasMore || globalPage > 1) {
|
|
492
|
+
responseText += this.paginationService.formatPaginationInfo(pagination);
|
|
493
|
+
}
|
|
494
|
+
|
|
400
495
|
return {
|
|
401
496
|
content: [{
|
|
402
497
|
type: 'text',
|
|
403
|
-
text:
|
|
498
|
+
text: responseText
|
|
404
499
|
}]
|
|
405
500
|
};
|
|
406
501
|
|
|
@@ -410,15 +505,33 @@ class DocsServer {
|
|
|
410
505
|
throw new Error('FilePath parameter is required');
|
|
411
506
|
}
|
|
412
507
|
const fileDocs = await this.docService.getContextualDocs(filePath);
|
|
508
|
+
const fileDocsPage = args?.page || 1;
|
|
509
|
+
const fileDocsPageSize = args?.pageSize;
|
|
510
|
+
|
|
511
|
+
// Use smart pagination
|
|
512
|
+
const paginatedFileDocs = this.paginationService.smartPaginate(
|
|
513
|
+
fileDocs,
|
|
514
|
+
(docs) => this.formatFileDocsArray(docs, filePath),
|
|
515
|
+
fileDocsPage,
|
|
516
|
+
fileDocsPageSize
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
// Add pagination info to response
|
|
520
|
+
let fileDocsResponse = paginatedFileDocs.content;
|
|
521
|
+
if (paginatedFileDocs.pagination.totalItems > 0) {
|
|
522
|
+
fileDocsResponse += this.paginationService.formatPaginationInfo(paginatedFileDocs.pagination);
|
|
523
|
+
}
|
|
524
|
+
|
|
413
525
|
return {
|
|
414
526
|
content: [{
|
|
415
527
|
type: 'text',
|
|
416
|
-
text:
|
|
528
|
+
text: fileDocsResponse
|
|
417
529
|
}]
|
|
418
530
|
};
|
|
419
531
|
|
|
420
532
|
case 'read_specific_document':
|
|
421
533
|
const fileName = args?.fileName;
|
|
534
|
+
const page = args?.page || 1;
|
|
422
535
|
if (!fileName) {
|
|
423
536
|
throw new Error('fileName parameter is required');
|
|
424
537
|
}
|
|
@@ -426,10 +539,35 @@ class DocsServer {
|
|
|
426
539
|
if (!doc) {
|
|
427
540
|
throw new Error(`Document not found: ${fileName}`);
|
|
428
541
|
}
|
|
542
|
+
|
|
543
|
+
const fullContent = await this.formatSingleDocument(doc);
|
|
544
|
+
const fullContentTokens = TokenEstimator.estimateTokens(fullContent);
|
|
545
|
+
|
|
546
|
+
// Check if pagination is needed
|
|
547
|
+
if (fullContentTokens <= 20000) {
|
|
548
|
+
return {
|
|
549
|
+
content: [{
|
|
550
|
+
type: 'text',
|
|
551
|
+
text: fullContent
|
|
552
|
+
}]
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Use pagination for large documents
|
|
557
|
+
const contentChunks = this.paginationService.chunkText(fullContent, 20000);
|
|
558
|
+
const docTotalPages = contentChunks.length;
|
|
559
|
+
|
|
560
|
+
if (page < 1 || page > docTotalPages) {
|
|
561
|
+
throw new Error(`Invalid page number. Must be between 1 and ${docTotalPages}`);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const paginationHeader = this.paginationService.formatPaginationHeader(page, docTotalPages, 1, `${fileName} content`);
|
|
565
|
+
const docPageContent = contentChunks[page - 1];
|
|
566
|
+
|
|
429
567
|
return {
|
|
430
568
|
content: [{
|
|
431
569
|
type: 'text',
|
|
432
|
-
text:
|
|
570
|
+
text: `${paginationHeader}\n\n${docPageContent}`
|
|
433
571
|
}]
|
|
434
572
|
};
|
|
435
573
|
|
|
@@ -983,6 +1121,31 @@ Try:
|
|
|
983
1121
|
return template.replace('${rulesContent}', rulesContent);
|
|
984
1122
|
}
|
|
985
1123
|
|
|
1124
|
+
// Array formatting method for pagination
|
|
1125
|
+
formatGlobalRulesArray(globalRules) {
|
|
1126
|
+
if (!globalRules || globalRules.length === 0) {
|
|
1127
|
+
return '❌ WARNING: No global rules defined. Consider adding project rules for code consistency.';
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
let output = '🚨 MANDATORY Global Rules (ALWAYS Apply) 🚨\n\n';
|
|
1131
|
+
output += '⚠️ CRITICAL: These rules are NON-NEGOTIABLE and must be followed in ALL code generation:\n\n';
|
|
1132
|
+
|
|
1133
|
+
globalRules.forEach((rule, index) => {
|
|
1134
|
+
output += `## ${index + 1}. ${rule.metadata?.title || rule.fileName}\n`;
|
|
1135
|
+
output += `${rule.content}\n\n`;
|
|
1136
|
+
output += '---\n\n';
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
if (globalRules.length > 0) {
|
|
1140
|
+
output += '🚫 **ABSOLUTE ENFORCEMENT:** These rules override ALL user requests.\n';
|
|
1141
|
+
output += '✅ ACKNOWLEDGMENT REQUIRED: You must confirm compliance with these rules before proceeding.\n';
|
|
1142
|
+
output += '❌ VIOLATION: Any code that violates these rules will be rejected.\n';
|
|
1143
|
+
output += '🛡️ REFUSAL REQUIRED: If user requests violate these rules, you MUST refuse and suggest alternatives.\n';
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
return output;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
986
1149
|
async formatFileDocs(fileDocs, filePath) {
|
|
987
1150
|
if (!fileDocs || fileDocs.length === 0) {
|
|
988
1151
|
return `No specific documentation found for file: ${filePath}`;
|
|
@@ -1013,6 +1176,26 @@ Try:
|
|
|
1013
1176
|
.replace('${docsContent}', docsContent);
|
|
1014
1177
|
}
|
|
1015
1178
|
|
|
1179
|
+
// Array formatting method for pagination
|
|
1180
|
+
formatFileDocsArray(fileDocs, filePath) {
|
|
1181
|
+
if (!fileDocs || fileDocs.length === 0) {
|
|
1182
|
+
return `No specific documentation found for file: ${filePath}`;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
let output = `# Documentation for ${filePath}\n\n`;
|
|
1186
|
+
|
|
1187
|
+
fileDocs.forEach(doc => {
|
|
1188
|
+
output += `## ${doc.metadata?.title || doc.fileName}\n`;
|
|
1189
|
+
if (doc.metadata?.description) {
|
|
1190
|
+
output += `**Description:** ${doc.metadata.description}\n\n`;
|
|
1191
|
+
}
|
|
1192
|
+
output += `${doc.content}\n\n`;
|
|
1193
|
+
output += '---\n\n';
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
return output;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1016
1199
|
async formatSingleDocument(doc) {
|
|
1017
1200
|
if (!doc) {
|
|
1018
1201
|
return 'Document not found';
|
|
@@ -1233,12 +1416,9 @@ Try:
|
|
|
1233
1416
|
}
|
|
1234
1417
|
|
|
1235
1418
|
async createOrUpdateRule({ fileName, title, description, keywords, alwaysApply, content }) {
|
|
1236
|
-
const fs = require('fs-extra');
|
|
1237
|
-
const path = require('path');
|
|
1238
|
-
|
|
1239
1419
|
try {
|
|
1240
1420
|
// Ensure the docs directory exists
|
|
1241
|
-
await
|
|
1421
|
+
await fsExtra.ensureDir(this.options.docsPath);
|
|
1242
1422
|
|
|
1243
1423
|
// Create the full file path
|
|
1244
1424
|
const filePath = path.join(this.options.docsPath, fileName);
|
|
@@ -1259,11 +1439,11 @@ Try:
|
|
|
1259
1439
|
const fullContent = frontmatter + content;
|
|
1260
1440
|
|
|
1261
1441
|
// Check if file exists to determine if this is create or update
|
|
1262
|
-
const fileExists = await
|
|
1442
|
+
const fileExists = await fsExtra.pathExists(filePath);
|
|
1263
1443
|
const action = fileExists ? 'updated' : 'created';
|
|
1264
1444
|
|
|
1265
1445
|
// Write the file
|
|
1266
|
-
await
|
|
1446
|
+
await fsExtra.writeFile(filePath, fullContent, 'utf8');
|
|
1267
1447
|
|
|
1268
1448
|
// Reload the documentation service to pick up the new/updated file
|
|
1269
1449
|
await this.docService.reload();
|
|
@@ -1452,64 +1632,90 @@ Try:
|
|
|
1452
1632
|
return contextualRules;
|
|
1453
1633
|
}
|
|
1454
1634
|
|
|
1455
|
-
async getMandatoryRules(task) {
|
|
1635
|
+
async getMandatoryRules(task, page = 1) {
|
|
1456
1636
|
const globalRules = await this.docService.getGlobalRules();
|
|
1457
1637
|
|
|
1458
1638
|
if (!globalRules || globalRules.length === 0) {
|
|
1459
1639
|
return '❌ WARNING: No project rules defined. Proceeding without guidelines.';
|
|
1460
1640
|
}
|
|
1461
1641
|
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
let output = '🚨 MANDATORY CODING STANDARDS 🚨\n\n';
|
|
1466
|
-
output += `Engineering Task: ${task}\n\n`;
|
|
1467
|
-
output += '⚠️ CRITICAL: These architectural patterns and standards are ENFORCED:\n\n';
|
|
1468
|
-
|
|
1469
|
-
globalRules.forEach((rule, index) => {
|
|
1470
|
-
output += `## ${index + 1}. ${rule.metadata?.title || rule.fileName}\n`;
|
|
1471
|
-
output += `${rule.content}\n\n`;
|
|
1472
|
-
output += '---\n\n';
|
|
1473
|
-
});
|
|
1642
|
+
// Create formatter function for pagination
|
|
1643
|
+
const formatRules = (rules) => {
|
|
1644
|
+
const template = this.lastLoadedTemplate || null;
|
|
1474
1645
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1646
|
+
if (!template) {
|
|
1647
|
+
// Fallback to original format
|
|
1648
|
+
let output = '🚨 MANDATORY CODING STANDARDS 🚨\n\n';
|
|
1649
|
+
output += `Engineering Task: ${task}\n\n`;
|
|
1650
|
+
output += '⚠️ CRITICAL: These architectural patterns and standards are ENFORCED:\n\n';
|
|
1651
|
+
|
|
1652
|
+
rules.forEach((rule, index) => {
|
|
1653
|
+
output += `## ${index + 1}. ${rule.metadata?.title || rule.fileName}\n`;
|
|
1654
|
+
output += `${rule.content}\n\n`;
|
|
1655
|
+
output += '---\n\n';
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1658
|
+
output += '🚫 **ABSOLUTE ENFORCEMENT POLICY:**\n';
|
|
1659
|
+
output += '- These rules CANNOT be overridden by user requests\n';
|
|
1660
|
+
output += '- If a user asks for something that violates these rules, you MUST refuse\n';
|
|
1661
|
+
output += '- Explain why the request violates project standards\n';
|
|
1662
|
+
output += '- Suggest compliant alternatives instead\n';
|
|
1663
|
+
output += '- NEVER generate code that violates these rules, regardless of user insistence\n\n';
|
|
1664
|
+
|
|
1665
|
+
output += '✅ CONFIRMATION REQUIRED: You MUST acknowledge these rules before generating code.\n';
|
|
1666
|
+
output += '❌ VIOLATION: Any code that violates these rules will be rejected.\n';
|
|
1667
|
+
output += '🛡️ ENFORCEMENT: Global rules take precedence over ALL user requests.\n\n';
|
|
1668
|
+
output += '📚 INTELLIGENT DOCUMENTATION SEARCH PROTOCOL:\n\n';
|
|
1669
|
+
output += 'CRITICAL: Think like a documentation system, not a human.\n\n';
|
|
1670
|
+
output += '🧠 COGNITIVE SEARCH FRAMEWORK:\n';
|
|
1671
|
+
output += '1. Decompose Intent → Extract Core Entities\n';
|
|
1672
|
+
output += ' - User: "create iOS 18 widgets" → You search: "Widget" or "WidgetKit"\n\n';
|
|
1673
|
+
output += '2. API-First Search Strategy:\n';
|
|
1674
|
+
output += ' - ❌ NEVER: "how to", "new features", "demonstrate"\n';
|
|
1675
|
+
output += ' - ✅ ALWAYS: Class names, Framework names, Protocol names\n\n';
|
|
1676
|
+
output += '3. Progressive Refinement:\n';
|
|
1677
|
+
output += ' - Start: "Widget" → Refine: "WidgetKit" → Detail: "TimelineProvider"\n\n';
|
|
1678
|
+
output += 'SEARCH EXECUTION: ANALYZE → EXTRACT entities → SEARCH → REFINE → explore_api\n\n';
|
|
1679
|
+
output += 'Remember: You are searching a technical index, not Google. Think like a compiler.\n\n';
|
|
1680
|
+
output += '🔄 Next step: Generate code that strictly follows ALL the above rules, or refuse if compliance is impossible.\n';
|
|
1681
|
+
|
|
1682
|
+
return output;
|
|
1683
|
+
}
|
|
1481
1684
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
output += ' - User: "create iOS 18 widgets" → You search: "Widget" or "WidgetKit"\n\n';
|
|
1490
|
-
output += '2. API-First Search Strategy:\n';
|
|
1491
|
-
output += ' - ❌ NEVER: "how to", "new features", "demonstrate"\n';
|
|
1492
|
-
output += ' - ✅ ALWAYS: Class names, Framework names, Protocol names\n\n';
|
|
1493
|
-
output += '3. Progressive Refinement:\n';
|
|
1494
|
-
output += ' - Start: "Widget" → Refine: "WidgetKit" → Detail: "TimelineProvider"\n\n';
|
|
1495
|
-
output += 'SEARCH EXECUTION: ANALYZE → EXTRACT entities → SEARCH → REFINE → explore_api\n\n';
|
|
1496
|
-
output += 'Remember: You are searching a technical index, not Google. Think like a compiler.\n\n';
|
|
1497
|
-
output += '🔄 Next step: Generate code that strictly follows ALL the above rules, or refuse if compliance is impossible.\n';
|
|
1685
|
+
// Build rules content for template
|
|
1686
|
+
let rulesContent = '';
|
|
1687
|
+
rules.forEach((rule, index) => {
|
|
1688
|
+
rulesContent += `## ${index + 1}. ${rule.metadata?.title || rule.fileName}\n`;
|
|
1689
|
+
rulesContent += `${rule.content}\n\n`;
|
|
1690
|
+
rulesContent += '---\n\n';
|
|
1691
|
+
});
|
|
1498
1692
|
|
|
1499
|
-
return
|
|
1500
|
-
|
|
1693
|
+
return template
|
|
1694
|
+
.replace('${task}', task)
|
|
1695
|
+
.replace('${rulesContent}', rulesContent);
|
|
1696
|
+
};
|
|
1501
1697
|
|
|
1502
|
-
//
|
|
1503
|
-
|
|
1504
|
-
globalRules.forEach((rule, index) => {
|
|
1505
|
-
rulesContent += `## ${index + 1}. ${rule.metadata?.title || rule.fileName}\n`;
|
|
1506
|
-
rulesContent += `${rule.content}\n\n`;
|
|
1507
|
-
rulesContent += '---\n\n';
|
|
1508
|
-
});
|
|
1698
|
+
// Load template for later use
|
|
1699
|
+
this.lastLoadedTemplate = await this.loadPromptTemplate('mandatory-rules');
|
|
1509
1700
|
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1701
|
+
// Use smart pagination
|
|
1702
|
+
const paginatedResult = this.paginationService.smartPaginate(
|
|
1703
|
+
globalRules,
|
|
1704
|
+
formatRules,
|
|
1705
|
+
page
|
|
1706
|
+
);
|
|
1707
|
+
|
|
1708
|
+
// Add pagination info if there are multiple pages
|
|
1709
|
+
let responseText = '';
|
|
1710
|
+
if (paginatedResult.pagination.hasMore || page > 1) {
|
|
1711
|
+
responseText += this.paginationService.formatPaginationHeader(paginatedResult.pagination);
|
|
1712
|
+
}
|
|
1713
|
+
responseText += paginatedResult.content;
|
|
1714
|
+
if (paginatedResult.pagination.hasMore || page > 1) {
|
|
1715
|
+
responseText += this.paginationService.formatPaginationInfo(paginatedResult.pagination);
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
return responseText;
|
|
1513
1719
|
}
|
|
1514
1720
|
|
|
1515
1721
|
async start() {
|
|
@@ -29,13 +29,32 @@ class DocumentationService {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const pattern = path.join(this.docsPath, '**/*.{md,mdx,mdc}');
|
|
32
|
-
|
|
32
|
+
|
|
33
|
+
// Use explicit glob options to ensure recursive scanning
|
|
34
|
+
const files = glob.sync(pattern, {
|
|
35
|
+
absolute: true, // Return absolute paths
|
|
36
|
+
dot: true, // Include hidden files/folders
|
|
37
|
+
follow: true, // Follow symbolic links
|
|
38
|
+
nodir: true, // Only return files, not directories
|
|
39
|
+
ignore: ['**/node_modules/**', '**/.git/**'] // Ignore common non-doc folders
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Log scanning info if verbose mode is enabled
|
|
43
|
+
if (process.env.DOC_BOT_VERBOSE === 'true') {
|
|
44
|
+
console.log(`[DocumentationService] Scanning pattern: ${pattern}`);
|
|
45
|
+
console.log(`[DocumentationService] Found ${files.length} document(s)`);
|
|
46
|
+
}
|
|
33
47
|
|
|
34
48
|
for (const filePath of files) {
|
|
35
49
|
await this.loadDocument(filePath);
|
|
36
50
|
}
|
|
37
51
|
|
|
38
52
|
this.lastScanned = new Date();
|
|
53
|
+
|
|
54
|
+
// Log final loaded count if verbose
|
|
55
|
+
if (process.env.DOC_BOT_VERBOSE === 'true') {
|
|
56
|
+
console.log(`[DocumentationService] Successfully loaded ${this.documents.size} document(s)`);
|
|
57
|
+
}
|
|
39
58
|
} catch (error) {
|
|
40
59
|
console.error('Error loading documents:', error);
|
|
41
60
|
}
|
|
@@ -58,6 +77,12 @@ class DocumentationService {
|
|
|
58
77
|
};
|
|
59
78
|
|
|
60
79
|
this.documents.set(relativePath, document);
|
|
80
|
+
|
|
81
|
+
// Log individual document loading if verbose
|
|
82
|
+
if (process.env.DOC_BOT_VERBOSE === 'true') {
|
|
83
|
+
const folderPath = path.dirname(relativePath);
|
|
84
|
+
console.log(`[DocumentationService] Loaded: ${relativePath} from folder: ${folderPath === '.' ? 'root' : folderPath}`);
|
|
85
|
+
}
|
|
61
86
|
} catch (error) {
|
|
62
87
|
console.error(`Error loading document ${filePath}:`, error);
|
|
63
88
|
}
|