agent-master-devsecops 3.9.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 (82) hide show
  1. package/README.md +205 -371
  2. package/agents/AGENT_DEVSECOPS.md +32 -417
  3. package/agents/AGENT_INFRA.md +31 -0
  4. package/agents/AGENT_ORCHESTRATOR.md +39 -264
  5. package/agents/AGENT_QA.md +58 -186
  6. package/agents/AGENT_SCRUM.md +23 -190
  7. package/agents/AGENT_SRE.md +22 -173
  8. package/agents/AGENT_UIUX.md +26 -162
  9. package/docs/ARCHITECTURE.md +152 -148
  10. package/docs/CALCULATIONS.md +17 -17
  11. package/docs/CHANGELOG.md +112 -49
  12. package/docs/ENGINEERING_RULES.md +56 -252
  13. package/docs/IDEAS.md +17 -17
  14. package/docs/LOCAL_BACKLOG.md +20 -0
  15. package/docs/STATE.md +111 -43
  16. package/docs/playbooks/PLAYBOOK_CIRCUIT_BREAKER_LLM.md +58 -58
  17. package/docs/playbooks/PLAYBOOK_OPENTELEMETRY_TRACING.md +58 -58
  18. package/package.json +59 -47
  19. package/scripts/auditors/a11y_auditor.js +462 -0
  20. package/scripts/auditors/code_quality_auditor.js +444 -0
  21. package/scripts/auditors/contract_test_validator.js +396 -0
  22. package/scripts/auditors/eval_harness.js +213 -0
  23. package/scripts/auditors/git_hygiene_linter.js +391 -0
  24. package/scripts/auditors/pr_reviewer.js +627 -0
  25. package/scripts/auditors/sast_scanner.js +442 -0
  26. package/scripts/auditors/sca_dependency_auditor.js +673 -0
  27. package/scripts/{bootstrap_cli.js → bin/bootstrap_cli.js} +597 -468
  28. package/scripts/bin/domain_idea_miner.js +658 -0
  29. package/scripts/bin/smart_onboarding.js +905 -0
  30. package/scripts/{web_inspector.js → bin/web_inspector.js} +578 -355
  31. package/scripts/core/architecture_advisor.js +378 -0
  32. package/scripts/core/ast_skeletonizer.js +413 -0
  33. package/scripts/core/autonomous_remediation_engine.js +305 -0
  34. package/scripts/{circuit_breaker.js → core/circuit_breaker.js} +546 -330
  35. package/scripts/core/context_compressor.js +237 -0
  36. package/scripts/core/fast_feature_scaffold.js +840 -0
  37. package/scripts/core/hot_state_cache.js +238 -0
  38. package/scripts/core/knowledge_gateway.js +888 -0
  39. package/scripts/{magic_prompt_generator.js → core/magic_prompt_generator.js} +250 -205
  40. package/scripts/{multi_agent_orchestrator.js → core/multi_agent_orchestrator.js} +421 -258
  41. package/scripts/{otel_tracer.js → core/otel_tracer.js} +335 -228
  42. package/scripts/{perf_benchmark.js → core/perf_benchmark.js} +212 -153
  43. package/scripts/core/sarif_engine.js +299 -0
  44. package/scripts/{sre_remediation_engine.js → core/sre_remediation_engine.js} +236 -174
  45. package/scripts/{universal_packager.js → core/universal_packager.js} +357 -313
  46. package/scripts/integrations/atlassian_client.js +600 -0
  47. package/scripts/integrations/atlassian_sync_action.js +576 -0
  48. package/scripts/integrations/confluence_sync.js +684 -0
  49. package/scripts/integrations/credentials_guard.js +500 -0
  50. package/scripts/integrations/jira_manager.js +722 -0
  51. package/scripts/mcp/handlers/atlassian_handlers.js +300 -0
  52. package/scripts/mcp/handlers/core_handlers.js +419 -0
  53. package/scripts/mcp/handlers/qa_handlers.js +502 -0
  54. package/scripts/mcp/handlers/rules_handlers.js +379 -0
  55. package/scripts/mcp/handlers/scaffold_handlers.js +334 -0
  56. package/scripts/mcp/handlers/sre_handlers.js +443 -0
  57. package/scripts/mcp/resources_registry.js +453 -0
  58. package/scripts/mcp/server.js +408 -0
  59. package/scripts/mcp/tool_handlers.js +444 -0
  60. package/scripts/mcp/tools_registry.js +1123 -0
  61. package/scripts/mcp_server.js +236 -1884
  62. package/.cursorignore +0 -23
  63. package/agents/compact/AGENT_ORCHESTRATOR_COMPACTO.md +0 -53
  64. package/scripts/a11y_auditor.js +0 -187
  65. package/scripts/ast_skeletonizer.js +0 -220
  66. package/scripts/atlassian_client.js +0 -238
  67. package/scripts/atlassian_sync_action.js +0 -269
  68. package/scripts/code_quality_auditor.js +0 -142
  69. package/scripts/confluence_sync.js +0 -337
  70. package/scripts/context_compressor.js +0 -139
  71. package/scripts/contract_test_validator.js +0 -166
  72. package/scripts/credentials_guard.js +0 -216
  73. package/scripts/domain_idea_miner.js +0 -364
  74. package/scripts/eval_harness.js +0 -100
  75. package/scripts/fast_feature_scaffold.js +0 -338
  76. package/scripts/git_hygiene_linter.js +0 -186
  77. package/scripts/hot_state_cache.js +0 -95
  78. package/scripts/jira_manager.js +0 -408
  79. package/scripts/pr_reviewer.js +0 -373
  80. package/scripts/sast_scanner.js +0 -180
  81. package/scripts/sca_dependency_auditor.js +0 -167
  82. package/scripts/smart_onboarding.js +0 -610
@@ -0,0 +1,444 @@
1
+ /**
2
+ * scripts/auditors/code_quality_auditor.js
3
+ * Code Quality & Cyclomatic Complexity Auditor — Versão 5.1 Enterprise
4
+ *
5
+ * Auditoria independente de qualidade de software, complexidade ciclomática de McCabe,
6
+ * profundidade de aninhamento e detecção de anti-patterns de código.
7
+ * Em conformidade com a Regra 13 (Estrutura de Código), Princípio 4 (Qualidade & DoD),
8
+ * Princípio 5 (DevSecOps) e padrão OASIS SARIF v2.1.0.
9
+ */
10
+ 'use strict';
11
+
12
+ const fs = require('node:fs');
13
+ const path = require('node:path');
14
+ const { createSarifReport } = require('../core/sarif_engine.js');
15
+
16
+ /**
17
+ * Scanner léxico linear O(N) imune a ReDoS (CWE-1333).
18
+ * Sanitiza strings e comentários preservando exatamente todas as quebras de linha (\n)
19
+ * e delimitadores de controle de fluxo para cálculo determinístico de métricas.
20
+ *
21
+ * @param {string} sourceCode
22
+ * @returns {{ sanitized: string, comments: Array<{ line: number, text: string, isBlock: boolean }>, totalLines: number }}
23
+ */
24
+ function lexicalScan(sourceCode) {
25
+ const code = String(sourceCode || '');
26
+ const len = code.length;
27
+ const sanitizedChars = [];
28
+ const comments = [];
29
+
30
+ let state = 'CODE'; // CODE, LINE_COMMENT, BLOCK_COMMENT, SINGLE_QUOTE, DOUBLE_QUOTE, TEMPLATE
31
+ const templateStack = []; // pilha para balancear interpolações ${...} dentro de templates
32
+ let currentComment = '';
33
+ let commentStartLine = 1;
34
+ let currentLine = 1;
35
+
36
+ for (let i = 0; i < len; i++) {
37
+ const ch = code[i];
38
+ const next = i + 1 < len ? code[i + 1] : '';
39
+
40
+ if (ch === '\n') {
41
+ currentLine++;
42
+ }
43
+
44
+ if (state === 'CODE') {
45
+ if (ch === '/' && next === '/') {
46
+ state = 'LINE_COMMENT';
47
+ commentStartLine = currentLine;
48
+ currentComment = '//';
49
+ sanitizedChars.push(' ', ' ');
50
+ i++; // consome proximo caractere
51
+ } else if (ch === '/' && next === '*') {
52
+ state = 'BLOCK_COMMENT';
53
+ commentStartLine = currentLine;
54
+ currentComment = '/*';
55
+ sanitizedChars.push(' ', ' ');
56
+ i++; // consome proximo caractere
57
+ } else if (ch === "'") {
58
+ state = 'SINGLE_QUOTE';
59
+ sanitizedChars.push(' ');
60
+ } else if (ch === '"') {
61
+ state = 'DOUBLE_QUOTE';
62
+ sanitizedChars.push(' ');
63
+ } else if (ch === '`') {
64
+ state = 'TEMPLATE';
65
+ sanitizedChars.push(' ');
66
+ } else if (ch === '}' && templateStack.length > 0) {
67
+ templateStack[templateStack.length - 1]--;
68
+ if (templateStack[templateStack.length - 1] === 0) {
69
+ templateStack.pop();
70
+ state = 'TEMPLATE';
71
+ sanitizedChars.push(' ');
72
+ } else {
73
+ sanitizedChars.push(ch);
74
+ }
75
+ } else if (ch === '{' && templateStack.length > 0) {
76
+ templateStack[templateStack.length - 1]++;
77
+ sanitizedChars.push(ch);
78
+ } else {
79
+ sanitizedChars.push(ch);
80
+ }
81
+ } else if (state === 'LINE_COMMENT') {
82
+ if (ch === '\n') {
83
+ comments.push({ line: commentStartLine, text: currentComment.trim(), isBlock: false });
84
+ currentComment = '';
85
+ state = 'CODE';
86
+ sanitizedChars.push('\n');
87
+ } else {
88
+ currentComment += ch;
89
+ sanitizedChars.push(' ');
90
+ }
91
+ } else if (state === 'BLOCK_COMMENT') {
92
+ if (ch === '*' && next === '/') {
93
+ currentComment += '*/';
94
+ comments.push({ line: commentStartLine, text: currentComment.trim(), isBlock: true });
95
+ currentComment = '';
96
+ state = 'CODE';
97
+ sanitizedChars.push(' ', ' ');
98
+ i++; // consome proximo caractere
99
+ } else {
100
+ currentComment += ch;
101
+ sanitizedChars.push(ch === '\n' ? '\n' : ' ');
102
+ }
103
+ } else if (state === 'SINGLE_QUOTE') {
104
+ if (ch === '\\' && i + 1 < len) {
105
+ sanitizedChars.push(' ', ' ');
106
+ i++;
107
+ } else if (ch === "'") {
108
+ state = 'CODE';
109
+ sanitizedChars.push(' ');
110
+ } else {
111
+ sanitizedChars.push(ch === '\n' ? '\n' : ' ');
112
+ }
113
+ } else if (state === 'DOUBLE_QUOTE') {
114
+ if (ch === '\\' && i + 1 < len) {
115
+ sanitizedChars.push(' ', ' ');
116
+ i++;
117
+ } else if (ch === '"') {
118
+ state = 'CODE';
119
+ sanitizedChars.push(' ');
120
+ } else {
121
+ sanitizedChars.push(ch === '\n' ? '\n' : ' ');
122
+ }
123
+ } else if (state === 'TEMPLATE') {
124
+ if (ch === '\\' && i + 1 < len) {
125
+ sanitizedChars.push(' ', ' ');
126
+ i++;
127
+ } else if (ch === '$' && next === '{') {
128
+ templateStack.push(1);
129
+ state = 'CODE';
130
+ sanitizedChars.push(' ', ' ');
131
+ i++; // consome '{'
132
+ } else if (ch === '`') {
133
+ state = 'CODE';
134
+ sanitizedChars.push(' ');
135
+ } else {
136
+ sanitizedChars.push(ch === '\n' ? '\n' : ' ');
137
+ }
138
+ }
139
+ }
140
+
141
+ if (state === 'LINE_COMMENT' && currentComment) {
142
+ comments.push({ line: commentStartLine, text: currentComment.trim(), isBlock: false });
143
+ } else if (state === 'BLOCK_COMMENT' && currentComment) {
144
+ comments.push({ line: commentStartLine, text: currentComment.trim(), isBlock: true });
145
+ }
146
+
147
+ return {
148
+ sanitized: sanitizedChars.join(''),
149
+ comments,
150
+ totalLines: currentLine
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Calcula a complexidade ciclomática de McCabe v(G) sobre código sanitizado.
156
+ * v(G) = 1 + número de pontos de decisão binária.
157
+ *
158
+ * @param {string} sanitizedCode
159
+ * @returns {number}
160
+ */
161
+ function calculateCyclomaticComplexity(sanitizedCode) {
162
+ let complexity = 1;
163
+
164
+ // Pontos de decisão de controle de fluxo (sem dupla contagem de 'else if')
165
+ const decisionRules = [
166
+ /\b(?:if|for|while)\s*\(/g,
167
+ /\bcatch\s*(?:\([^\)]*\))?\s*\{/g,
168
+ /\bcase\s+[^:]+:/g,
169
+ /&&/g,
170
+ /\|\|/g,
171
+ /\?\?/g,
172
+ /\?[^:]+:/g
173
+ ];
174
+
175
+ for (const regex of decisionRules) {
176
+ const matches = sanitizedCode.match(regex);
177
+ if (matches) {
178
+ complexity += matches.length;
179
+ }
180
+ }
181
+
182
+ return complexity;
183
+ }
184
+
185
+ /**
186
+ * Calcula a profundidade máxima de aninhamento de blocos ({ ... }) no código sanitizado.
187
+ *
188
+ * @param {string} sanitizedCode
189
+ * @returns {number}
190
+ */
191
+ function calculateMaxNestingDepth(sanitizedCode) {
192
+ let maxNesting = 0;
193
+ let currentNesting = 0;
194
+
195
+ for (const ch of sanitizedCode) {
196
+ if (ch === '{') {
197
+ currentNesting++;
198
+ if (currentNesting > maxNesting) {
199
+ maxNesting = currentNesting;
200
+ }
201
+ } else if (ch === '}') {
202
+ currentNesting = Math.max(0, currentNesting - 1);
203
+ }
204
+ }
205
+
206
+ return maxNesting;
207
+ }
208
+
209
+ /**
210
+ * Classe principal do Auditor de Qualidade de Código.
211
+ */
212
+ class CodeQualityAuditor {
213
+ constructor(options = {}) {
214
+ this.maxComplexity = options.maxComplexity || 15;
215
+ this.maxNesting = options.maxNesting || 6;
216
+ this.maxFileLines = options.maxFileLines || 300;
217
+ }
218
+
219
+ auditFile(filePath) {
220
+ if (!fs.existsSync(filePath)) {
221
+ throw new Error(`Arquivo não encontrado para auditoria de qualidade: ${filePath}`);
222
+ }
223
+ const content = fs.readFileSync(filePath, 'utf8');
224
+ return this.auditCode(content, path.basename(filePath));
225
+ }
226
+
227
+ auditCode(code, filename = 'snippet.js') {
228
+ const scan = lexicalScan(code);
229
+ const rawLines = String(code || '').split('\n');
230
+ const totalLines = rawLines.length;
231
+
232
+ // Rastreamento das linhas de comentário
233
+ const commentLinesSet = new Set();
234
+ for (const c of scan.comments) {
235
+ const lineCount = c.text.split('\n').length;
236
+ for (let l = 0; l < lineCount; l++) {
237
+ commentLinesSet.add(c.line + l);
238
+ }
239
+ }
240
+
241
+ let blankLines = 0;
242
+ let commentLines = 0;
243
+
244
+ rawLines.forEach((line, idx) => {
245
+ const lineNum = idx + 1;
246
+ const trimmed = line.trim();
247
+ if (!trimmed) {
248
+ blankLines++;
249
+ } else if (commentLinesSet.has(lineNum)) {
250
+ // Se a linha contém apenas comentário
251
+ const sanitizedLine = scan.sanitized.split('\n')[idx] || '';
252
+ if (!sanitizedLine.trim()) {
253
+ commentLines++;
254
+ }
255
+ }
256
+ });
257
+
258
+ const issues = [];
259
+
260
+ // 1. Verificação de pendências em comentários (TODO, FIXME, HACK)
261
+ for (const c of scan.comments) {
262
+ const todoMatch = c.text.match(/\b(?:TODO|FIXME|HACK)\b[^\r\n]*/);
263
+ if (todoMatch) {
264
+ issues.push({
265
+ ruleId: 'QUALITY-TODO-001',
266
+ type: 'PENDING_TODO',
267
+ line: c.line,
268
+ severity: 'LOW',
269
+ message: `Marcação pendente detectada: "${todoMatch[0].trim()}". Código pronto não deve deixar TODOs (Regra 7).`,
270
+ remediation: 'Resolva a pendência técnica ou crie um ticket no backlog formal.'
271
+ });
272
+ }
273
+ }
274
+
275
+ // 2. Detecção de blocos catch vazios (anti-pattern de engolir exceções - Regra 5)
276
+ const emptyCatchRegex = /\bcatch\s*(?:\([^\)]*\))?\s*\{\s*\}/g;
277
+ let match;
278
+ while ((match = emptyCatchRegex.exec(scan.sanitized)) !== null) {
279
+ const upToMatch = scan.sanitized.slice(0, match.index);
280
+ const lineNum = upToMatch.split('\n').length;
281
+ issues.push({
282
+ ruleId: 'QUALITY-EMPTY-CATCH',
283
+ type: 'EMPTY_CATCH',
284
+ line: lineNum,
285
+ severity: 'HIGH',
286
+ message: 'Bloco catch vazio detectado: erros não devem ser suprimidos silenciosamente (Regra 5).',
287
+ remediation: 'Adicione tratamento do erro, logging com contexto ou propague a exceção.'
288
+ });
289
+ }
290
+
291
+ // 3. Detecção de blocos finally vazios
292
+ const emptyFinallyRegex = /\bfinally\s*\{\s*\}/g;
293
+ while ((match = emptyFinallyRegex.exec(scan.sanitized)) !== null) {
294
+ const upToMatch = scan.sanitized.slice(0, match.index);
295
+ const lineNum = upToMatch.split('\n').length;
296
+ issues.push({
297
+ ruleId: 'QUALITY-EMPTY-FINALLY',
298
+ type: 'EMPTY_FINALLY',
299
+ line: lineNum,
300
+ severity: 'MEDIUM',
301
+ message: 'Bloco finally vazio detectado: remova o bloco redundante ou implemente o cleanup apropriado.',
302
+ remediation: 'Remova o bloco finally vazio caso não execute fechamento de recursos.'
303
+ });
304
+ }
305
+
306
+ // 4. Detecção de declaração 'debugger' deixada para trás
307
+ const debuggerRegex = /\bdebugger\s*(?:;|$)/g;
308
+ while ((match = debuggerRegex.exec(scan.sanitized)) !== null) {
309
+ const upToMatch = scan.sanitized.slice(0, match.index);
310
+ const lineNum = upToMatch.split('\n').length;
311
+ issues.push({
312
+ ruleId: 'QUALITY-DEBUGGER-STMT',
313
+ type: 'DEBUGGER_STATEMENT',
314
+ line: lineNum,
315
+ severity: 'HIGH',
316
+ message: 'Instrução debugger detectada: código de depuração não deve ser commitado para produção.',
317
+ remediation: 'Remova a instrução debugger antes de prosseguir com o commit.'
318
+ });
319
+ }
320
+
321
+ // Métricas de Complexidade e Aninhamento
322
+ const cyclomaticComplexity = calculateCyclomaticComplexity(scan.sanitized);
323
+ const maxNestingDepth = calculateMaxNestingDepth(scan.sanitized);
324
+
325
+ // 5. Verificação de limites de tamanho e complexidade
326
+ if (totalLines > this.maxFileLines) {
327
+ issues.push({
328
+ ruleId: 'QUALITY-FILE-LINES',
329
+ type: 'FILE_TOO_LONG',
330
+ line: totalLines,
331
+ severity: 'MEDIUM',
332
+ message: `Arquivo possui ${totalLines} linhas (limite canônico recomendado: ${this.maxFileLines} linhas - Regra 13).`,
333
+ remediation: 'Divida o módulo em componentes menores com responsabilidade única (SRP).'
334
+ });
335
+ }
336
+
337
+ if (maxNestingDepth > this.maxNesting) {
338
+ issues.push({
339
+ ruleId: 'QUALITY-DEEP-NESTING',
340
+ type: 'DEEP_NESTING',
341
+ line: 1,
342
+ severity: 'MEDIUM',
343
+ message: `Nível máximo de aninhamento (${maxNestingDepth}) excede o limiar tolerado (${this.maxNesting}).`,
344
+ remediation: 'Aplique cláusulas de guarda (early return) e extraia métodos auxiliares.'
345
+ });
346
+ }
347
+
348
+ if (cyclomaticComplexity > this.maxComplexity) {
349
+ issues.push({
350
+ ruleId: 'QUALITY-HIGH-COMPLEXITY',
351
+ type: 'HIGH_CYCLOMATIC_COMPLEXITY',
352
+ line: 1,
353
+ severity: 'MEDIUM',
354
+ message: `Complexidade ciclomática (${cyclomaticComplexity}) excede o limite aceitável de ${this.maxComplexity}.`,
355
+ remediation: 'Decomponha funções complexas e substitua lógicas condicionais densas por polimorfismo ou tabelas de despacho.'
356
+ });
357
+ }
358
+
359
+ const isClean = issues.filter(i => i.severity === 'HIGH' || i.severity === 'CRITICAL').length === 0;
360
+
361
+ return {
362
+ filename,
363
+ status: isClean ? (issues.length === 0 ? 'OPTIMAL' : 'ACCEPTABLE_WITH_NOTES') : 'REFACTOR_REQUIRED',
364
+ metrics: {
365
+ totalLines,
366
+ codeLines: Math.max(0, totalLines - commentLines - blankLines),
367
+ commentLines,
368
+ blankLines,
369
+ cyclomaticComplexity,
370
+ maxNestingDepth
371
+ },
372
+ qualityScore: Math.max(0, 100 - (issues.length * 10) - (cyclomaticComplexity > this.maxComplexity ? 15 : 0)),
373
+ issues
374
+ };
375
+ }
376
+
377
+ /**
378
+ * Exporta a auditoria de qualidade em OASIS SARIF v2.1.0 oficial.
379
+ * @param {string} code
380
+ * @param {string} [filename='snippet.js']
381
+ * @returns {object} SARIF v2.1.0 JSON object
382
+ */
383
+ auditToSarif(code, filename = 'snippet.js') {
384
+ const auditRes = this.auditCode(code, filename);
385
+ const findings = auditRes.issues.map(iss => ({
386
+ ruleId: iss.ruleId || iss.type,
387
+ name: iss.type,
388
+ severity: iss.severity,
389
+ filename: auditRes.filename,
390
+ line: iss.line || 1,
391
+ column: 1,
392
+ snippet: '',
393
+ message: iss.message,
394
+ remediation: iss.remediation || 'Refatore o código para eliminar a violação de qualidade.'
395
+ }));
396
+
397
+ return createSarifReport({
398
+ toolName: 'agent-master-code-quality-auditor',
399
+ toolVersion: '5.1.0',
400
+ findings,
401
+ properties: {
402
+ status: auditRes.status,
403
+ qualityScore: auditRes.qualityScore,
404
+ metrics: auditRes.metrics
405
+ }
406
+ });
407
+ }
408
+
409
+ /**
410
+ * Formata relatório executivo em Markdown para os Agentes e PR Reviewer.
411
+ * @param {object} audit
412
+ * @returns {string}
413
+ */
414
+ formatReport(audit) {
415
+ let md = `## 🔬 Relatório de Qualidade de Código & Complexidade (AGENT DEVSECOPS & QA)\n\n`;
416
+ md += `**Alvo:** \`${audit.filename}\` | **Status:** ${audit.status === 'OPTIMAL' ? '✅ EXCELENTE (OPTIMAL)' : audit.status === 'ACCEPTABLE_WITH_NOTES' ? '⚠️ ACEITÁVEL COM NOTAS' : '❌ REFATORAÇÃO NECESSÁRIA'}\n\n`;
417
+ md += `### 📊 Métricas de Engenharia de Software (McCabe & Aninhamento)\n`;
418
+ md += `- **Complexidade Ciclomática v(G):** ${audit.metrics.cyclomaticComplexity} (Limite: ${this.maxComplexity})\n`;
419
+ md += `- **Profundidade Máxima de Aninhamento:** ${audit.metrics.maxNestingDepth} (Limite: ${this.maxNesting})\n`;
420
+ md += `- **Linhas de Código Efetivas:** ${audit.metrics.codeLines} / Total: ${audit.metrics.totalLines}\n`;
421
+ md += `- **Quality Score:** ${audit.qualityScore}/100\n\n`;
422
+
423
+ if (audit.issues.length > 0) {
424
+ md += `### ⚠️ Apontamentos de Qualidade & Débito Técnico\n\n`;
425
+ md += `| Linha | Tipo | Severidade | Mensagem | Remediação Recomendada |\n`;
426
+ md += `| :---: | :--- | :---: | :--- | :--- |\n`;
427
+ for (const iss of audit.issues) {
428
+ md += `| ${iss.line} | **${iss.type}** | **${iss.severity}** | ${iss.message} | ${iss.remediation || 'Refatoração requerida'} |\n`;
429
+ }
430
+ md += '\n';
431
+ } else {
432
+ md += `> 🌟 **Código Limpo:** Nenhuma violação ou anti-pattern detectado. Arquivo atende aos padrões de Clean Code.\n\n`;
433
+ }
434
+
435
+ return md;
436
+ }
437
+ }
438
+
439
+ module.exports = {
440
+ CodeQualityAuditor,
441
+ lexicalScan,
442
+ calculateCyclomaticComplexity,
443
+ calculateMaxNestingDepth
444
+ };