musubi-sdd 3.10.0 → 5.1.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.
Files changed (44) hide show
  1. package/README.md +24 -19
  2. package/package.json +1 -1
  3. package/src/agents/agent-loop.js +532 -0
  4. package/src/agents/agentic/code-generator.js +767 -0
  5. package/src/agents/agentic/code-reviewer.js +698 -0
  6. package/src/agents/agentic/index.js +43 -0
  7. package/src/agents/function-tool.js +432 -0
  8. package/src/agents/index.js +45 -0
  9. package/src/agents/schema-generator.js +514 -0
  10. package/src/analyzers/ast-extractor.js +870 -0
  11. package/src/analyzers/context-optimizer.js +681 -0
  12. package/src/analyzers/repository-map.js +692 -0
  13. package/src/integrations/index.js +7 -1
  14. package/src/integrations/mcp/index.js +175 -0
  15. package/src/integrations/mcp/mcp-context-provider.js +472 -0
  16. package/src/integrations/mcp/mcp-discovery.js +436 -0
  17. package/src/integrations/mcp/mcp-tool-registry.js +467 -0
  18. package/src/integrations/mcp-connector.js +818 -0
  19. package/src/integrations/tool-discovery.js +589 -0
  20. package/src/managers/index.js +7 -0
  21. package/src/managers/skill-tools.js +565 -0
  22. package/src/monitoring/cost-tracker.js +7 -0
  23. package/src/monitoring/incident-manager.js +10 -0
  24. package/src/monitoring/observability.js +10 -0
  25. package/src/monitoring/quality-dashboard.js +491 -0
  26. package/src/monitoring/release-manager.js +10 -0
  27. package/src/orchestration/agent-skill-binding.js +655 -0
  28. package/src/orchestration/error-handler.js +827 -0
  29. package/src/orchestration/index.js +235 -1
  30. package/src/orchestration/mcp-tool-adapters.js +896 -0
  31. package/src/orchestration/reasoning/index.js +58 -0
  32. package/src/orchestration/reasoning/planning-engine.js +831 -0
  33. package/src/orchestration/reasoning/reasoning-engine.js +710 -0
  34. package/src/orchestration/reasoning/self-correction.js +751 -0
  35. package/src/orchestration/skill-executor.js +665 -0
  36. package/src/orchestration/skill-registry.js +650 -0
  37. package/src/orchestration/workflow-examples.js +1072 -0
  38. package/src/orchestration/workflow-executor.js +779 -0
  39. package/src/phase4-integration.js +248 -0
  40. package/src/phase5-integration.js +402 -0
  41. package/src/steering/steering-auto-update.js +572 -0
  42. package/src/steering/steering-validator.js +547 -0
  43. package/src/templates/template-constraints.js +646 -0
  44. package/src/validators/advanced-validation.js +580 -0
@@ -0,0 +1,698 @@
1
+ /**
2
+ * @file code-reviewer.js
3
+ * @description Autonomous code review engine for agentic coding
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { EventEmitter } = require('events');
10
+ const path = require('path');
11
+
12
+ /**
13
+ * Review severity levels
14
+ * @enum {string}
15
+ */
16
+ const SEVERITY = {
17
+ INFO: 'info',
18
+ WARNING: 'warning',
19
+ ERROR: 'error',
20
+ CRITICAL: 'critical'
21
+ };
22
+
23
+ /**
24
+ * Issue category types
25
+ * @enum {string}
26
+ */
27
+ const CATEGORY = {
28
+ STYLE: 'style',
29
+ LOGIC: 'logic',
30
+ SECURITY: 'security',
31
+ PERFORMANCE: 'performance',
32
+ MAINTAINABILITY: 'maintainability',
33
+ BEST_PRACTICE: 'best-practice',
34
+ ERROR_HANDLING: 'error-handling',
35
+ DOCUMENTATION: 'documentation'
36
+ };
37
+
38
+ /**
39
+ * @typedef {Object} ReviewIssue
40
+ * @property {string} id - Issue identifier
41
+ * @property {string} severity - Issue severity
42
+ * @property {string} category - Issue category
43
+ * @property {string} message - Issue message
44
+ * @property {number} [line] - Line number
45
+ * @property {number} [column] - Column number
46
+ * @property {string} [code] - Problematic code snippet
47
+ * @property {string} [suggestion] - Fix suggestion
48
+ * @property {string} [rule] - Rule that was violated
49
+ */
50
+
51
+ /**
52
+ * @typedef {Object} ReviewResult
53
+ * @property {string} id - Review identifier
54
+ * @property {string} filePath - Reviewed file path
55
+ * @property {string} language - Code language
56
+ * @property {ReviewIssue[]} issues - Found issues
57
+ * @property {number} score - Overall score (0-100)
58
+ * @property {Object} summary - Review summary
59
+ * @property {number} timestamp - Review timestamp
60
+ */
61
+
62
+ /**
63
+ * @typedef {Object} CodeReviewerOptions
64
+ * @property {Object} [rules={}] - Custom review rules
65
+ * @property {string[]} [enabledCategories] - Categories to check
66
+ * @property {number} [minScore=70] - Minimum acceptable score
67
+ * @property {boolean} [strictMode=false] - Enable strict checking
68
+ */
69
+
70
+ /**
71
+ * Default review rules
72
+ */
73
+ const DEFAULT_RULES = {
74
+ javascript: [
75
+ {
76
+ id: 'js-no-var',
77
+ pattern: /\bvar\s+/g,
78
+ category: CATEGORY.BEST_PRACTICE,
79
+ severity: SEVERITY.WARNING,
80
+ message: 'Use const or let instead of var',
81
+ suggestion: 'Replace var with const or let'
82
+ },
83
+ {
84
+ id: 'js-console-log',
85
+ pattern: /console\.(log|debug|info)\(/g,
86
+ category: CATEGORY.BEST_PRACTICE,
87
+ severity: SEVERITY.INFO,
88
+ message: 'Console statements should be removed in production',
89
+ suggestion: 'Remove or replace with proper logging'
90
+ },
91
+ {
92
+ id: 'js-eval',
93
+ pattern: /\beval\s*\(/g,
94
+ category: CATEGORY.SECURITY,
95
+ severity: SEVERITY.CRITICAL,
96
+ message: 'eval() is dangerous and should be avoided',
97
+ suggestion: 'Use safer alternatives to eval()'
98
+ },
99
+ {
100
+ id: 'js-todo',
101
+ pattern: /\/\/\s*TODO:/gi,
102
+ category: CATEGORY.MAINTAINABILITY,
103
+ severity: SEVERITY.INFO,
104
+ message: 'TODO comment found',
105
+ suggestion: 'Complete or track the TODO item'
106
+ },
107
+ {
108
+ id: 'js-empty-catch',
109
+ pattern: /catch\s*\([^)]*\)\s*\{\s*\}/g,
110
+ category: CATEGORY.ERROR_HANDLING,
111
+ severity: SEVERITY.ERROR,
112
+ message: 'Empty catch block swallows errors',
113
+ suggestion: 'Handle or log the caught error'
114
+ },
115
+ {
116
+ id: 'js-magic-number',
117
+ pattern: /(?<![\w.])(?<!\.)\b\d{2,}\b(?![\w.])/g,
118
+ category: CATEGORY.MAINTAINABILITY,
119
+ severity: SEVERITY.INFO,
120
+ message: 'Magic number should be extracted to a named constant',
121
+ suggestion: 'Define a constant with a descriptive name'
122
+ },
123
+ {
124
+ id: 'js-long-function',
125
+ pattern: /function\s+\w+\s*\([^)]*\)\s*\{/g,
126
+ category: CATEGORY.MAINTAINABILITY,
127
+ severity: SEVERITY.WARNING,
128
+ message: 'Function may be too long',
129
+ checkLength: true,
130
+ maxLength: 50
131
+ },
132
+ {
133
+ id: 'js-no-strict',
134
+ checkGlobal: true,
135
+ pattern: null,
136
+ category: CATEGORY.BEST_PRACTICE,
137
+ severity: SEVERITY.WARNING,
138
+ message: 'Consider adding "use strict"',
139
+ condition: (code) => !code.includes("'use strict'") && !code.includes('"use strict"')
140
+ }
141
+ ],
142
+
143
+ python: [
144
+ {
145
+ id: 'py-except-bare',
146
+ pattern: /except\s*:/g,
147
+ category: CATEGORY.ERROR_HANDLING,
148
+ severity: SEVERITY.WARNING,
149
+ message: 'Bare except catches all exceptions',
150
+ suggestion: 'Specify the exception type to catch'
151
+ },
152
+ {
153
+ id: 'py-print',
154
+ pattern: /\bprint\s*\(/g,
155
+ category: CATEGORY.BEST_PRACTICE,
156
+ severity: SEVERITY.INFO,
157
+ message: 'Print statements should use logging in production',
158
+ suggestion: 'Use logging module instead'
159
+ },
160
+ {
161
+ id: 'py-todo',
162
+ pattern: /#\s*TODO:/gi,
163
+ category: CATEGORY.MAINTAINABILITY,
164
+ severity: SEVERITY.INFO,
165
+ message: 'TODO comment found',
166
+ suggestion: 'Complete or track the TODO item'
167
+ },
168
+ {
169
+ id: 'py-global',
170
+ pattern: /\bglobal\s+\w+/g,
171
+ category: CATEGORY.BEST_PRACTICE,
172
+ severity: SEVERITY.WARNING,
173
+ message: 'Global variables should be avoided',
174
+ suggestion: 'Consider using class or function parameters'
175
+ }
176
+ ],
177
+
178
+ common: [
179
+ {
180
+ id: 'common-fixme',
181
+ pattern: /FIXME/gi,
182
+ category: CATEGORY.MAINTAINABILITY,
183
+ severity: SEVERITY.WARNING,
184
+ message: 'FIXME comment found - needs attention',
185
+ suggestion: 'Address the FIXME issue'
186
+ },
187
+ {
188
+ id: 'common-hack',
189
+ pattern: /HACK/gi,
190
+ category: CATEGORY.MAINTAINABILITY,
191
+ severity: SEVERITY.WARNING,
192
+ message: 'HACK comment found - technical debt',
193
+ suggestion: 'Refactor the hack'
194
+ },
195
+ {
196
+ id: 'common-password',
197
+ pattern: /password\s*=\s*['"][^'"]+['"]/gi,
198
+ category: CATEGORY.SECURITY,
199
+ severity: SEVERITY.CRITICAL,
200
+ message: 'Hardcoded password detected',
201
+ suggestion: 'Use environment variables or secure vault'
202
+ },
203
+ {
204
+ id: 'common-api-key',
205
+ pattern: /api[_-]?key\s*=\s*['"][^'"]+['"]/gi,
206
+ category: CATEGORY.SECURITY,
207
+ severity: SEVERITY.CRITICAL,
208
+ message: 'Hardcoded API key detected',
209
+ suggestion: 'Use environment variables'
210
+ }
211
+ ]
212
+ };
213
+
214
+ /**
215
+ * Code Reviewer class for autonomous code review
216
+ * @extends EventEmitter
217
+ */
218
+ class CodeReviewer extends EventEmitter {
219
+ /**
220
+ * Create code reviewer
221
+ * @param {CodeReviewerOptions} [options={}] - Reviewer options
222
+ */
223
+ constructor(options = {}) {
224
+ super();
225
+
226
+ this.rules = this.mergeRules(DEFAULT_RULES, options.rules || {});
227
+ this.enabledCategories = options.enabledCategories || Object.values(CATEGORY);
228
+ this.minScore = options.minScore ?? 70;
229
+ this.strictMode = options.strictMode ?? false;
230
+
231
+ // State
232
+ this.reviews = new Map();
233
+ this.reviewCounter = 0;
234
+ }
235
+
236
+ /**
237
+ * Merge custom rules with defaults
238
+ * @private
239
+ */
240
+ mergeRules(defaults, custom) {
241
+ const merged = { ...defaults };
242
+
243
+ for (const [lang, rules] of Object.entries(custom)) {
244
+ if (merged[lang]) {
245
+ merged[lang] = [...merged[lang], ...rules];
246
+ } else {
247
+ merged[lang] = rules;
248
+ }
249
+ }
250
+
251
+ return merged;
252
+ }
253
+
254
+ /**
255
+ * Review code
256
+ * @param {string} code - Code to review
257
+ * @param {Object} [options={}] - Review options
258
+ * @returns {ReviewResult}
259
+ */
260
+ review(code, options = {}) {
261
+ const id = this.generateId();
262
+ const language = options.language || this.detectLanguage(code, options.filePath);
263
+ const filePath = options.filePath || 'unknown';
264
+
265
+ this.emit('review:start', { id, filePath, language });
266
+
267
+ const issues = [];
268
+
269
+ // Get applicable rules
270
+ const rules = [
271
+ ...(this.rules[language] || []),
272
+ ...(this.rules.common || [])
273
+ ];
274
+
275
+ // Apply each rule
276
+ for (const rule of rules) {
277
+ // Check if category is enabled
278
+ if (!this.enabledCategories.includes(rule.category)) continue;
279
+
280
+ // Apply rule
281
+ const ruleIssues = this.applyRule(rule, code, language);
282
+ issues.push(...ruleIssues);
283
+ }
284
+
285
+ // Run additional checks
286
+ const structuralIssues = this.checkStructure(code, language);
287
+ issues.push(...structuralIssues);
288
+
289
+ // Calculate score
290
+ const score = this.calculateScore(issues);
291
+
292
+ // Create result
293
+ const result = {
294
+ id,
295
+ filePath,
296
+ language,
297
+ issues,
298
+ score,
299
+ summary: this.createSummary(issues, score),
300
+ timestamp: Date.now()
301
+ };
302
+
303
+ // Store review
304
+ this.reviews.set(id, result);
305
+
306
+ this.emit('review:complete', { result });
307
+
308
+ return result;
309
+ }
310
+
311
+ /**
312
+ * Apply a single rule
313
+ * @private
314
+ */
315
+ applyRule(rule, code, language) {
316
+ const issues = [];
317
+
318
+ // Handle condition-based rules
319
+ if (rule.condition) {
320
+ if (rule.condition(code)) {
321
+ issues.push(this.createIssue(rule, null, null));
322
+ }
323
+ return issues;
324
+ }
325
+
326
+ // Handle pattern-based rules
327
+ if (!rule.pattern) return issues;
328
+
329
+ const lines = code.split('\n');
330
+ let match;
331
+
332
+ // Reset regex
333
+ rule.pattern.lastIndex = 0;
334
+
335
+ while ((match = rule.pattern.exec(code)) !== null) {
336
+ const line = this.getLineNumber(code, match.index);
337
+ const column = match.index - this.getLineStart(code, line);
338
+
339
+ // Skip if checking length and within limit
340
+ if (rule.checkLength) {
341
+ const funcEnd = this.findFunctionEnd(code, match.index);
342
+ const funcLines = code.substring(match.index, funcEnd).split('\n').length;
343
+ if (funcLines <= rule.maxLength) continue;
344
+ }
345
+
346
+ issues.push(this.createIssue(rule, line, column, match[0]));
347
+ }
348
+
349
+ return issues;
350
+ }
351
+
352
+ /**
353
+ * Create an issue object
354
+ * @private
355
+ */
356
+ createIssue(rule, line, column, code = null) {
357
+ return {
358
+ id: `issue-${++this.reviewCounter}`,
359
+ severity: rule.severity,
360
+ category: rule.category,
361
+ message: rule.message,
362
+ line,
363
+ column,
364
+ code,
365
+ suggestion: rule.suggestion,
366
+ rule: rule.id
367
+ };
368
+ }
369
+
370
+ /**
371
+ * Check code structure
372
+ * @private
373
+ */
374
+ checkStructure(code, language) {
375
+ const issues = [];
376
+ const lines = code.split('\n');
377
+
378
+ // Check line length
379
+ lines.forEach((line, index) => {
380
+ if (line.length > 120) {
381
+ issues.push({
382
+ id: `issue-${++this.reviewCounter}`,
383
+ severity: SEVERITY.INFO,
384
+ category: CATEGORY.STYLE,
385
+ message: `Line exceeds 120 characters (${line.length})`,
386
+ line: index + 1,
387
+ suggestion: 'Break long lines for better readability'
388
+ });
389
+ }
390
+ });
391
+
392
+ // Check for missing documentation
393
+ if (language === 'javascript' || language === 'typescript') {
394
+ if (!code.includes('/**') && !code.includes('//')) {
395
+ issues.push({
396
+ id: `issue-${++this.reviewCounter}`,
397
+ severity: SEVERITY.INFO,
398
+ category: CATEGORY.DOCUMENTATION,
399
+ message: 'Code lacks documentation comments',
400
+ suggestion: 'Add JSDoc comments to functions and classes'
401
+ });
402
+ }
403
+ }
404
+
405
+ // Check nesting depth
406
+ const maxNesting = this.checkNestingDepth(code);
407
+ if (maxNesting > 4) {
408
+ issues.push({
409
+ id: `issue-${++this.reviewCounter}`,
410
+ severity: SEVERITY.WARNING,
411
+ category: CATEGORY.MAINTAINABILITY,
412
+ message: `Deep nesting detected (depth: ${maxNesting})`,
413
+ suggestion: 'Consider extracting nested code into separate functions'
414
+ });
415
+ }
416
+
417
+ return issues;
418
+ }
419
+
420
+ /**
421
+ * Check nesting depth
422
+ * @private
423
+ */
424
+ checkNestingDepth(code) {
425
+ let maxDepth = 0;
426
+ let currentDepth = 0;
427
+
428
+ for (const char of code) {
429
+ if (char === '{') {
430
+ currentDepth++;
431
+ maxDepth = Math.max(maxDepth, currentDepth);
432
+ } else if (char === '}') {
433
+ currentDepth = Math.max(0, currentDepth - 1);
434
+ }
435
+ }
436
+
437
+ return maxDepth;
438
+ }
439
+
440
+ /**
441
+ * Calculate review score
442
+ * @private
443
+ */
444
+ calculateScore(issues) {
445
+ let score = 100;
446
+
447
+ const penalties = {
448
+ [SEVERITY.INFO]: 1,
449
+ [SEVERITY.WARNING]: 3,
450
+ [SEVERITY.ERROR]: 10,
451
+ [SEVERITY.CRITICAL]: 25
452
+ };
453
+
454
+ for (const issue of issues) {
455
+ score -= penalties[issue.severity] || 0;
456
+ }
457
+
458
+ return Math.max(0, score);
459
+ }
460
+
461
+ /**
462
+ * Create review summary
463
+ * @private
464
+ */
465
+ createSummary(issues, score) {
466
+ const bySeverity = {};
467
+ const byCategory = {};
468
+
469
+ for (const issue of issues) {
470
+ bySeverity[issue.severity] = (bySeverity[issue.severity] || 0) + 1;
471
+ byCategory[issue.category] = (byCategory[issue.category] || 0) + 1;
472
+ }
473
+
474
+ return {
475
+ totalIssues: issues.length,
476
+ bySeverity,
477
+ byCategory,
478
+ score,
479
+ passed: score >= this.minScore,
480
+ recommendation: this.getRecommendation(score, issues)
481
+ };
482
+ }
483
+
484
+ /**
485
+ * Get recommendation based on score
486
+ * @private
487
+ */
488
+ getRecommendation(score, issues) {
489
+ if (score >= 90) return 'Excellent! Code quality is high.';
490
+ if (score >= 80) return 'Good. Minor improvements recommended.';
491
+ if (score >= 70) return 'Acceptable. Several issues should be addressed.';
492
+ if (score >= 50) return 'Needs work. Significant improvements required.';
493
+ return 'Critical issues detected. Immediate attention required.';
494
+ }
495
+
496
+ /**
497
+ * Get line number from position
498
+ * @private
499
+ */
500
+ getLineNumber(code, position) {
501
+ return code.substring(0, position).split('\n').length;
502
+ }
503
+
504
+ /**
505
+ * Get line start position
506
+ * @private
507
+ */
508
+ getLineStart(code, lineNumber) {
509
+ const lines = code.split('\n');
510
+ let start = 0;
511
+ for (let i = 0; i < lineNumber - 1 && i < lines.length; i++) {
512
+ start += lines[i].length + 1;
513
+ }
514
+ return start;
515
+ }
516
+
517
+ /**
518
+ * Find function end position
519
+ * @private
520
+ */
521
+ findFunctionEnd(code, start) {
522
+ let depth = 0;
523
+ let inFunction = false;
524
+
525
+ for (let i = start; i < code.length; i++) {
526
+ if (code[i] === '{') {
527
+ depth++;
528
+ inFunction = true;
529
+ } else if (code[i] === '}') {
530
+ depth--;
531
+ if (inFunction && depth === 0) {
532
+ return i + 1;
533
+ }
534
+ }
535
+ }
536
+
537
+ return code.length;
538
+ }
539
+
540
+ /**
541
+ * Detect language
542
+ * @private
543
+ */
544
+ detectLanguage(code, filePath) {
545
+ if (filePath) {
546
+ const ext = path.extname(filePath);
547
+ const langMap = {
548
+ '.js': 'javascript',
549
+ '.ts': 'typescript',
550
+ '.py': 'python',
551
+ '.jsx': 'javascript',
552
+ '.tsx': 'typescript'
553
+ };
554
+ return langMap[ext] || 'javascript';
555
+ }
556
+
557
+ // Detect from content
558
+ if (code.includes('def ') || code.includes('import ') && !code.includes('from \'')) {
559
+ return 'python';
560
+ }
561
+
562
+ return 'javascript';
563
+ }
564
+
565
+ /**
566
+ * Generate unique ID
567
+ * @private
568
+ */
569
+ generateId() {
570
+ return `review-${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 6)}`;
571
+ }
572
+
573
+ /**
574
+ * Get review by ID
575
+ * @param {string} reviewId - Review identifier
576
+ * @returns {ReviewResult|null}
577
+ */
578
+ getReview(reviewId) {
579
+ return this.reviews.get(reviewId) || null;
580
+ }
581
+
582
+ /**
583
+ * Get all reviews
584
+ * @returns {ReviewResult[]}
585
+ */
586
+ getAllReviews() {
587
+ return Array.from(this.reviews.values());
588
+ }
589
+
590
+ /**
591
+ * Get statistics
592
+ * @returns {Object}
593
+ */
594
+ getStats() {
595
+ const reviews = this.getAllReviews();
596
+ const scores = reviews.map(r => r.score);
597
+
598
+ return {
599
+ totalReviews: reviews.length,
600
+ averageScore: scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : 0,
601
+ passed: reviews.filter(r => r.summary.passed).length,
602
+ failed: reviews.filter(r => !r.summary.passed).length,
603
+ totalIssues: reviews.reduce((sum, r) => sum + r.issues.length, 0)
604
+ };
605
+ }
606
+
607
+ /**
608
+ * Add custom rule
609
+ * @param {string} language - Language
610
+ * @param {Object} rule - Rule definition
611
+ */
612
+ addRule(language, rule) {
613
+ if (!this.rules[language]) {
614
+ this.rules[language] = [];
615
+ }
616
+ this.rules[language].push(rule);
617
+ }
618
+
619
+ /**
620
+ * Clear reviews
621
+ */
622
+ clearReviews() {
623
+ this.reviews.clear();
624
+ }
625
+
626
+ /**
627
+ * Export review to markdown
628
+ * @param {string} reviewId - Review identifier
629
+ * @returns {string}
630
+ */
631
+ exportToMarkdown(reviewId) {
632
+ const review = this.getReview(reviewId);
633
+ if (!review) return '';
634
+
635
+ let md = `# Code Review Report\n\n`;
636
+ md += `**File:** ${review.filePath}\n`;
637
+ md += `**Language:** ${review.language}\n`;
638
+ md += `**Score:** ${review.score}/100 ${review.summary.passed ? '✅' : '❌'}\n\n`;
639
+
640
+ md += `## Summary\n\n`;
641
+ md += `${review.summary.recommendation}\n\n`;
642
+ md += `- Total Issues: ${review.issues.length}\n`;
643
+ for (const [severity, count] of Object.entries(review.summary.bySeverity)) {
644
+ md += `- ${severity}: ${count}\n`;
645
+ }
646
+
647
+ if (review.issues.length > 0) {
648
+ md += `\n## Issues\n\n`;
649
+
650
+ for (const issue of review.issues) {
651
+ const icon = {
652
+ [SEVERITY.INFO]: 'ℹ️',
653
+ [SEVERITY.WARNING]: '⚠️',
654
+ [SEVERITY.ERROR]: '❌',
655
+ [SEVERITY.CRITICAL]: '🚨'
656
+ }[issue.severity];
657
+
658
+ md += `### ${icon} ${issue.message}\n\n`;
659
+ md += `- **Severity:** ${issue.severity}\n`;
660
+ md += `- **Category:** ${issue.category}\n`;
661
+ if (issue.line) md += `- **Line:** ${issue.line}\n`;
662
+ if (issue.suggestion) md += `- **Suggestion:** ${issue.suggestion}\n`;
663
+ md += `\n`;
664
+ }
665
+ }
666
+
667
+ return md;
668
+ }
669
+ }
670
+
671
+ /**
672
+ * Create code reviewer
673
+ * @param {CodeReviewerOptions} [options={}] - Reviewer options
674
+ * @returns {CodeReviewer}
675
+ */
676
+ function createCodeReviewer(options = {}) {
677
+ return new CodeReviewer(options);
678
+ }
679
+
680
+ /**
681
+ * Review code
682
+ * @param {string} code - Code to review
683
+ * @param {Object} [options={}] - Review options
684
+ * @returns {ReviewResult}
685
+ */
686
+ function reviewCode(code, options = {}) {
687
+ const reviewer = createCodeReviewer(options);
688
+ return reviewer.review(code, options);
689
+ }
690
+
691
+ module.exports = {
692
+ CodeReviewer,
693
+ createCodeReviewer,
694
+ reviewCode,
695
+ SEVERITY,
696
+ CATEGORY,
697
+ DEFAULT_RULES
698
+ };