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
@@ -1,216 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * scripts/credentials_guard.js
4
- *
5
- * Guardião Canônico Unificado de Credenciais, Healthcheck e Imutabilidade de Requisitos (Regras 15 e 17).
6
- * Consolida o Healthcheck interativo de credenciais Atlassian/GitHub e a validação inviolável da Regra 15.
7
- * Garante obtenção ativa, pré-validação compulsória e zero falhas silenciosas em todos os vetores.
8
- */
9
-
10
- 'use strict';
11
-
12
- const fs = require('node:fs');
13
- const path = require('node:path');
14
-
15
- const {
16
- getAtlassianCredentials,
17
- checkGithubAuth,
18
- getGitRepositoryUrl,
19
- getGitMetadata
20
- } = require('./atlassian_client.js');
21
-
22
- class InviolableRuleError extends Error {
23
- constructor(message, missingRequirements = [], remediationSteps = []) {
24
- super(message);
25
- this.name = 'InviolableRuleError';
26
- this.rule = 'Regra 15 - Imutabilidade de Requisitos & Validação Compulsória de Segredos';
27
- this.missingRequirements = missingRequirements;
28
- this.remediationSteps = remediationSteps;
29
- }
30
- }
31
-
32
- /**
33
- * Validação rigorosa da Regra 15 com suporte a Fail-Fast.
34
- */
35
- function validateRule15({ failFast = true, requireGitHub = true, requireAtlassian = true } = {}) {
36
- const missing = [];
37
- const remediation = [];
38
-
39
- const atlassian = getAtlassianCredentials();
40
- if (requireAtlassian && !atlassian.isConfigured) {
41
- if (!atlassian.token) missing.push('ATLASSIAN_API_TOKEN');
42
- if (!atlassian.email) missing.push('ATLASSIAN_EMAIL');
43
- if (!atlassian.domain) missing.push('ATLASSIAN_DOMAIN');
44
-
45
- remediation.push(
46
- 'Configurar variáveis de ambiente ou secrets no GitHub Actions: gh secret set ATLASSIAN_API_TOKEN / ATLASSIAN_EMAIL / ATLASSIAN_DOMAIN'
47
- );
48
- }
49
-
50
- const github = checkGithubAuth();
51
- if (requireGitHub && !github.isAuthenticated) {
52
- missing.push('GITHUB_AUTH (gh CLI ou GITHUB_TOKEN)');
53
- remediation.push('Autenticar GitHub CLI via "gh auth login" ou exportar GITHUB_TOKEN');
54
- }
55
-
56
- const isValid = missing.length === 0;
57
-
58
- if (!isValid && failFast) {
59
- const msg = `[REGRA 15 INVIOLÁVEL: PRÉ-REQUISITOS NÃO ATENDIDOS]\n` +
60
- `Faltam credenciais/ferramentas obrigatórias: ${missing.join(', ')}\n` +
61
- `Remediação: ${remediation.join(' | ')}`;
62
- throw new InviolableRuleError(msg, missing, remediation);
63
- }
64
-
65
- return {
66
- valid: isValid,
67
- missing,
68
- remediation,
69
- atlassian,
70
- github
71
- };
72
- }
73
-
74
- /**
75
- * Verifica credenciais Atlassian a partir do diretório alvo.
76
- */
77
- function checkAtlassianAuth(targetDir = process.cwd()) {
78
- const creds = getAtlassianCredentials(targetDir);
79
- const missingFields = [];
80
- if (!creds.token) missingFields.push('token');
81
- if (!creds.email) missingFields.push('email');
82
- if (!creds.domain) missingFields.push('domain');
83
-
84
- return {
85
- configured: creds.isConfigured,
86
- email: creds.email,
87
- domain: creds.domain,
88
- tokenPresent: Boolean(creds.token),
89
- source: creds.source,
90
- missingFields
91
- };
92
- }
93
-
94
- /**
95
- * Configura credenciais no arquivo .env e garante proteção no .gitignore.
96
- */
97
- function configureCredentials(targetDir = process.cwd(), credentials = {}) {
98
- const envPath = path.join(targetDir, '.env');
99
- let existingContent = '';
100
-
101
- if (fs.existsSync(envPath)) {
102
- existingContent = fs.readFileSync(envPath, 'utf8');
103
- }
104
-
105
- const lines = existingContent ? existingContent.split('\n') : [];
106
- const updates = {
107
- ATLASSIAN_DOMAIN: credentials.domain,
108
- ATLASSIAN_EMAIL: credentials.email,
109
- ATLASSIAN_API_TOKEN: credentials.token,
110
- JIRA_PROJECT_KEY: credentials.jiraKey,
111
- CONFLUENCE_SPACE_KEY: credentials.confluenceSpace
112
- };
113
-
114
- const keysSet = new Set();
115
- const newLines = lines.map(line => {
116
- for (const [key, val] of Object.entries(updates)) {
117
- if (val && line.startsWith(`${key}=`)) {
118
- keysSet.add(key);
119
- return `${key}=${val}`;
120
- }
121
- }
122
- return line;
123
- });
124
-
125
- for (const [key, val] of Object.entries(updates)) {
126
- if (val && !keysSet.has(key)) {
127
- newLines.push(`${key}=${val}`);
128
- }
129
- }
130
-
131
- const finalContent = newLines.filter(Boolean).join('\n') + '\n';
132
- fs.writeFileSync(envPath, finalContent, 'utf8');
133
-
134
- const gitignorePath = path.join(targetDir, '.gitignore');
135
- let gitignoreContent = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf8') : '';
136
- if (!gitignoreContent.includes('.env')) {
137
- gitignoreContent += '\n# Segurança - Credenciais e Segredos (Regra 15)\n.env\n.env.local\n';
138
- fs.writeFileSync(gitignorePath, gitignoreContent, 'utf8');
139
- }
140
-
141
- return {
142
- status: 'SUCCESS',
143
- success: true,
144
- envPath,
145
- configuredKeys: Object.keys(updates).filter(k => updates[k])
146
- };
147
- }
148
-
149
- /**
150
- * Executa o diagnóstico completo de saúde das credenciais Atlassian e GitHub.
151
- */
152
- function runCredentialHealthcheck(targetDir = process.cwd()) {
153
- const gh = checkGithubAuth();
154
- const atlassian = checkAtlassianAuth(targetDir);
155
-
156
- const missing = [];
157
- const remediation = [];
158
-
159
- if (!gh.authenticated) {
160
- missing.push('GitHub CLI');
161
- remediation.push({
162
- tool: 'GitHub CLI',
163
- command: 'gh auth login',
164
- instruction: 'Execute "gh auth login" no terminal para autenticar sua conta GitHub no CLI.'
165
- });
166
- }
167
-
168
- if (!atlassian.configured) {
169
- const missingItems = atlassian.missingFields.join(', ');
170
- missing.push(`Credenciais Atlassian (${missingItems})`);
171
- remediation.push({
172
- tool: 'Atlassian Cloud (Jira & Confluence)',
173
- instruction: 'Cadastre suas credenciais no arquivo .env ou execute "npx agent-master-init":',
174
- requiredVariables: {
175
- ATLASSIAN_DOMAIN: 'seu-subdominio.atlassian.net',
176
- ATLASSIAN_EMAIL: '<seu_email_corporativo>',
177
- ATLASSIAN_API_TOKEN: 'https://id.atlassian.com/manage-profile/security/api-tokens'
178
- }
179
- });
180
- }
181
-
182
- const allReady = gh.authenticated && atlassian.configured;
183
-
184
- let promptGuidance = '';
185
- if (!allReady) {
186
- promptGuidance = `⚠️ [ALERTA DE CONFIGURAÇÃO DE AMBIENTE - REGRA 15]\n` +
187
- `Faltam credenciais essenciais para o funcionamento autônomo da suíte:\n` +
188
- remediation.map(r => `- ${r.tool}: ${r.instruction || r.command}`).join('\n') +
189
- `\nSolicite as credenciais ao usuário ou instrua a execução de "gh auth login" antes de prosseguir com tarefas que exijam Jira/Confluence/Git.`;
190
- } else {
191
- promptGuidance = '✅ Todas as integrações (GitHub, Jira e Confluence) estão configuradas e prontas para operação autônoma.';
192
- }
193
-
194
- return {
195
- allReady,
196
- github: gh,
197
- atlassian,
198
- missing,
199
- remediation,
200
- promptGuidance
201
- };
202
- }
203
-
204
- module.exports = {
205
- InviolableRuleError,
206
- validateRule15,
207
- getAtlassianConfig: getAtlassianCredentials,
208
- getGitHubConfig: checkGithubAuth,
209
- checkGitHubAuth: checkGithubAuth,
210
- checkGithubAuth,
211
- getGitRepositoryUrl,
212
- getGitMetadata,
213
- checkAtlassianAuth,
214
- configureCredentials,
215
- runCredentialHealthcheck
216
- };
@@ -1,364 +0,0 @@
1
- /**
2
- * Domain Idea Miner & Automated Codebase Opportunity Scanner
3
- * Varredura inteligente de código-fonte de aplicação para identificar gargalos reais
4
- * de performance, resiliência, arquitetura e documentação de contratos.
5
- * Em conformidade com a Regra 14 (Ideation Sanity Filter), Regra 6 (Suggestions)
6
- * e o Gate de Arquitetura do AGENT_QA.
7
- */
8
-
9
- const fs = require('node:fs');
10
- const path = require('node:path');
11
-
12
- // Diretórios e extensões ignorados durante a varredura
13
- const IGNORED_DIRS = new Set([
14
- 'node_modules',
15
- '.git',
16
- '.github',
17
- 'build',
18
- 'target',
19
- 'dist',
20
- '.idea',
21
- '.vscode',
22
- 'coverage',
23
- '.gradle',
24
- '.mvn'
25
- ]);
26
-
27
- const CODE_EXTENSIONS = new Set([
28
- '.java',
29
- '.kt',
30
- '.ts',
31
- '.js',
32
- '.py',
33
- '.go'
34
- ]);
35
-
36
- class DomainIdeaMiner {
37
- constructor(options = {}) {
38
- this.rootDir = path.resolve(options.rootDir || process.cwd());
39
- this.maxOpenIdeas = options.maxOpenIdeas || 10;
40
- }
41
-
42
- /**
43
- * Coleta recursiva de arquivos de código relevantes
44
- */
45
- collectCodeFiles(targetDir) {
46
- const files = [];
47
-
48
- const walk = (currentDir) => {
49
- if (!fs.existsSync(currentDir)) return;
50
- let entries = [];
51
- try {
52
- entries = fs.readdirSync(currentDir, { withFileTypes: true });
53
- } catch {
54
- return;
55
- }
56
-
57
- for (const entry of entries) {
58
- if (entry.isDirectory()) {
59
- if (!IGNORED_DIRS.has(entry.name)) {
60
- walk(path.join(currentDir, entry.name));
61
- }
62
- } else if (entry.isFile()) {
63
- const ext = path.extname(entry.name).toLowerCase();
64
- if (CODE_EXTENSIONS.has(ext)) {
65
- files.push(path.join(currentDir, entry.name));
66
- }
67
- }
68
- }
69
- };
70
-
71
- walk(targetDir);
72
- return files;
73
- }
74
-
75
- /**
76
- * Analisa um arquivo e extrai oportunidades de melhoria
77
- */
78
- inspectFile(filePath, rootDir) {
79
- const relativePath = path.relative(rootDir, filePath).replace(/\\/g, '/');
80
- let content = '';
81
- try {
82
- content = fs.readFileSync(filePath, 'utf8');
83
- } catch {
84
- return [];
85
- }
86
-
87
- const lines = content.split('\n');
88
- const opportunities = [];
89
-
90
- // Heurísticas de detecção
91
- const isJava = filePath.endsWith('.java') || filePath.endsWith('.kt');
92
- const isJsTs = filePath.endsWith('.js') || filePath.endsWith('.ts');
93
- const isController = /controller|resource|router|endpoint/i.test(relativePath) || /@RestController|@Controller/i.test(content);
94
- const isRepository = /repository|dao|mapper/i.test(relativePath) || /@Repository/i.test(content);
95
- const isEntity = /entity|model|domain/i.test(relativePath) || /@Entity|@Table/i.test(content);
96
-
97
- lines.forEach((line, index) => {
98
- const lineNum = index + 1;
99
- const trimmed = line.trim();
100
-
101
- // 1. Falta de Cache em Endpoints GET frequentes
102
- if (isController && isJava) {
103
- if (/@GetMapping/i.test(trimmed) && !content.includes('@Cacheable') && !content.includes('CacheControl')) {
104
- opportunities.push({
105
- category: 'SPEED',
106
- title: `Implementar cache local em memória (Caffeine/Spring Cache) no endpoint de leitura`,
107
- evidence: `${relativePath}:${lineNum}`,
108
- value: `redução de latência p99 < 15ms em consultas repetitivas`,
109
- cost: 'S',
110
- priority: 85
111
- });
112
- }
113
- } else if (isController && isJsTs) {
114
- if (/(app|router)\.get\s*\(/i.test(trimmed) && !content.includes('cache') && !content.includes('etag')) {
115
- opportunities.push({
116
- category: 'SPEED',
117
- title: `Adicionar cache HTTP (ETag / Cache-Control) nas rotas de leitura`,
118
- evidence: `${relativePath}:${lineNum}`,
119
- value: `redução de requisições redundantes ao backend`,
120
- cost: 'S',
121
- priority: 80
122
- });
123
- }
124
- }
125
-
126
- // 2. Consultas sem Paginação (Risco OOM)
127
- if (isRepository || isController) {
128
- if (/List<[A-Za-z0-9_]+>\s+findAll\s*\(\s*\)/i.test(trimmed) ||
129
- (isJava && /List<[A-Za-z0-9_]+>\s+(get|find|list)[A-Za-z0-9_]*\s*\([^)]*\)/i.test(trimmed) && !trimmed.includes('Pageable'))) {
130
- opportunities.push({
131
- category: 'RISK',
132
- title: `Adicionar suporte a paginação (Pageable / Page<T>) na listagem de dados`,
133
- evidence: `${relativePath}:${lineNum}`,
134
- value: `prevenção de OutOfMemoryError com crescimento da base de dados`,
135
- cost: 'S',
136
- priority: 90
137
- });
138
- }
139
- }
140
-
141
- // 3. Clientes HTTP Downstream sem Resiliência (Timeout / Circuit Breaker)
142
- if (/RestTemplate|WebClient|HttpClient|fetch\(|axios\./i.test(trimmed)) {
143
- if (!content.includes('@CircuitBreaker') && !content.includes('Resilience4j') && !content.includes('timeout')) {
144
- opportunities.push({
145
- category: 'RISK',
146
- title: `Configurar Circuit Breaker e Timeouts com Resilience4j em chamadas downstream`,
147
- evidence: `${relativePath}:${lineNum}`,
148
- value: `isolamento de falhas em cascata entre microserviços`,
149
- cost: 'M',
150
- priority: 95
151
- });
152
- }
153
- }
154
-
155
- // 4. Ausência de Contratos de API (OpenAPI / Swagger)
156
- if (isController && isJava) {
157
- if (/@(GetMapping|PostMapping|PutMapping|DeleteMapping)/i.test(trimmed) && !content.includes('@Operation') && !content.includes('@Tag')) {
158
- opportunities.push({
159
- category: 'CODE',
160
- title: `Especificar anotações de contrato OpenAPI 3.0 no Controller`,
161
- evidence: `${relativePath}:${lineNum}`,
162
- value: `catálogo de APIs homologado e documentação viva`,
163
- cost: 'S',
164
- priority: 70
165
- });
166
- }
167
- }
168
-
169
- // 5. Entidades sem Índices em Campos de Busca Frequente
170
- if (isEntity && isJava) {
171
- if (/@Entity/i.test(trimmed) && !content.includes('indexes =') && (content.includes('titulo') || content.includes('nome') || content.includes('codigo') || content.includes('email'))) {
172
- opportunities.push({
173
- category: 'SPEED',
174
- title: `Configurar índices de banco de dados (@Index) em colunas de busca frequente`,
175
- evidence: `${relativePath}:${lineNum}`,
176
- value: `redução de Full Table Scans em consultas de texto e chaves`,
177
- cost: 'S',
178
- priority: 75
179
- });
180
- }
181
- }
182
- });
183
-
184
- return opportunities;
185
- }
186
-
187
- /**
188
- * Executa varredura completa no projeto alvo
189
- */
190
- mineIdeas(targetDir = this.rootDir) {
191
- const files = this.collectCodeFiles(targetDir);
192
- const allOpportunities = [];
193
-
194
- for (const file of files) {
195
- const opps = this.inspectFile(file, targetDir);
196
- allOpportunities.push(...opps);
197
- }
198
-
199
- // Deduplicação por categoria e título similar
200
- const uniqueMap = new Map();
201
- for (const opp of allOpportunities) {
202
- const key = `${opp.category}:${opp.title}`;
203
- if (!uniqueMap.has(key)) {
204
- uniqueMap.set(key, opp);
205
- }
206
- }
207
-
208
- // Ordenação por prioridade decrescente
209
- const sorted = Array.from(uniqueMap.values()).sort((a, b) => b.priority - a.priority);
210
- return sorted;
211
- }
212
-
213
- /**
214
- * Lê as ideias existentes em IDEAS.md e extrai o maior ID numérico
215
- */
216
- getExistingIdeas(ideasFilePath) {
217
- if (!fs.existsSync(ideasFilePath)) {
218
- return { highestId: 0, openIdeas: [], content: '' };
219
- }
220
-
221
- const content = fs.readFileSync(ideasFilePath, 'utf8');
222
- const lines = content.split(/\r?\n/);
223
- let highestId = 0;
224
- const openIdeas = [];
225
-
226
- for (const line of lines) {
227
- const trimmed = line.trim();
228
- const match = trimmed.match(/^- I-(\d+)/);
229
- if (match) {
230
- const idNum = parseInt(match[1], 10);
231
- if (idNum > highestId) highestId = idNum;
232
-
233
- // Verificar se é aberta
234
- if (!/\[(CONCLUÍDO|EM PRODUÇÃO|DONE|DROP|REJEITAD[OA]|ARCHIVED)\]/i.test(trimmed)) {
235
- openIdeas.push(trimmed);
236
- }
237
- }
238
- }
239
-
240
- return { highestId, openIdeas, content };
241
- }
242
-
243
- /**
244
- * Formata uma oportunidade no formato canônico da Regra 14 / ENGINEERING_RULES
245
- */
246
- formatCanonicalIdea(idNum, opp) {
247
- return `- I-${idNum} [${opp.category}] ${opp.title} | evidence: ${opp.evidence} | value: ${opp.value} | cost: ${opp.cost}`;
248
- }
249
-
250
- /**
251
- * Sincroniza as ideias mineradas com docs/IDEAS.md
252
- */
253
- syncWithIdeasFile(minedIdeas, ideasFilePath, options = {}) {
254
- const { highestId, openIdeas } = this.getExistingIdeas(ideasFilePath);
255
- let nextId = highestId + 1;
256
- const newlyAdded = [];
257
-
258
- const existingNormalized = openIdeas.map(i => i.toLowerCase());
259
-
260
- for (const opp of minedIdeas) {
261
- if (openIdeas.length + newlyAdded.length >= this.maxOpenIdeas) {
262
- break; // Limite de 10 ideias abertas respeitado
263
- }
264
-
265
- // Evita duplicatas conceituais
266
- const isDuplicate = existingNormalized.some(ex => ex.includes(opp.title.toLowerCase()) || ex.includes(opp.evidence.toLowerCase()));
267
- if (!isDuplicate) {
268
- const canonicalLine = this.formatCanonicalIdea(nextId, opp);
269
- newlyAdded.push(canonicalLine);
270
- nextId++;
271
- }
272
- }
273
-
274
- if (options.dryRun || newlyAdded.length === 0) {
275
- return {
276
- applied: false,
277
- newlyAdded,
278
- totalOpenCount: openIdeas.length + newlyAdded.length
279
- };
280
- }
281
-
282
- // Leitura e inserção atômica no arquivo IDEAS.md
283
- let currentContent = fs.existsSync(ideasFilePath) ? fs.readFileSync(ideasFilePath, 'utf8') : '';
284
-
285
- // Se o arquivo tiver uma seção de backlog aberto, anexa nela
286
- const sectionHeader = '## 🎯 Backlog Aberto de Domínio';
287
- if (currentContent.includes(sectionHeader)) {
288
- const parts = currentContent.split(sectionHeader);
289
- const afterHeader = parts[1];
290
- const nextDividerIndex = afterHeader.indexOf('\n---');
291
-
292
- if (nextDividerIndex !== -1) {
293
- const insertion = newlyAdded.join('\n') + '\n';
294
- const sanitizedAfterContent = afterHeader.slice(0, nextDividerIndex) + '\n' + insertion + afterHeader.slice(nextDividerIndex);
295
- currentContent = parts[0] + sectionHeader + sanitizedAfterContent;
296
- } else {
297
- currentContent += '\n' + newlyAdded.join('\n') + '\n';
298
- }
299
- } else {
300
- // Cria template básico se não existir
301
- currentContent = `# IDEAS (Backlog Vivo de Oportunidades & Inovação de Domínio)\n\n` +
302
- `Este documento registra o backlog rotativo de melhorias e hipóteses técnicas em aberto, auditadas pelo **AGENT_QA (Shift-Left Gatekeeper)** e estritamente aderentes à **Regra 14 (Filtro de Sanidade Contextual)**.\n\n` +
303
- `## 🎯 Backlog Aberto de Domínio (Até 10 Itens Ordenados por Valor)\n\n` +
304
- newlyAdded.join('\n') + '\n';
305
- }
306
-
307
- fs.writeFileSync(ideasFilePath, currentContent, 'utf8');
308
-
309
- return {
310
- applied: true,
311
- newlyAdded,
312
- totalOpenCount: openIdeas.length + newlyAdded.length
313
- };
314
- }
315
- }
316
-
317
- /**
318
- * Helper direto para execução programática
319
- */
320
- function mineDomainIdeas(targetDir = process.cwd(), options = {}) {
321
- const miner = new DomainIdeaMiner({ rootDir: targetDir, maxOpenIdeas: options.maxOpenIdeas || 10 });
322
- const opportunities = miner.mineIdeas(targetDir);
323
- const ideasFilePath = options.ideasFilePath || path.join(targetDir, 'docs', 'IDEAS.md');
324
-
325
- let syncResult = { applied: false, newlyAdded: [] };
326
- if (options.autoApply) {
327
- syncResult = miner.syncWithIdeasFile(opportunities, ideasFilePath, { dryRun: options.dryRun });
328
- }
329
-
330
- return {
331
- scannedFilesCount: miner.collectCodeFiles(targetDir).length,
332
- opportunitiesFound: opportunities.length,
333
- opportunities,
334
- syncResult
335
- };
336
- }
337
-
338
- module.exports = {
339
- DomainIdeaMiner,
340
- mineDomainIdeas
341
- };
342
-
343
- // Execução CLI direta quando chamado via terminal
344
- if (require.main === module) {
345
- const args = process.argv.slice(2);
346
- const autoApply = args.includes('--apply');
347
- const dryRun = args.includes('--dry-run');
348
- const targetDir = process.cwd();
349
-
350
- console.log(`🔍 [Domain Idea Miner] Iniciando varredura em: ${targetDir}`);
351
- const result = mineDomainIdeas(targetDir, { autoApply, dryRun });
352
-
353
- console.log(`📊 Arquivos analisados: ${result.scannedFilesCount}`);
354
- console.log(`💡 Oportunidades identificadas: ${result.opportunitiesFound}`);
355
-
356
- result.opportunities.forEach((opp, i) => {
357
- console.log(` ${i + 1}. [${opp.category}] ${opp.title}`);
358
- console.log(` Evidência: ${opp.evidence} | Valor: ${opp.value}`);
359
- });
360
-
361
- if (autoApply) {
362
- console.log(`📝 Ideias sincronizadas com IDEAS.md: ${result.syncResult.newlyAdded.length} adicionadas.`);
363
- }
364
- }
@@ -1,100 +0,0 @@
1
- /**
2
- * LLM Evaluation Harness — Módulo de Benchmark e Aderência Normativa
3
- * Avalia empiricamente a aderência de respostas de modelos de IA às 14 Regras de Engenharia.
4
- */
5
-
6
- const EVAL_CRITERIA = [
7
- {
8
- id: 'CRIT-1',
9
- rule: 11,
10
- name: 'Dual-Layer Output Protocol (Resumo Compacto)',
11
- weight: 20,
12
- check: (output) => {
13
- // Verifica se a resposta não é um dump colossal (> 4000 caracteres) sem links
14
- const isCompact = output.length < 3500;
15
- const hasLinks = /\[.*?\]\(.*?\)/.test(output);
16
- return isCompact && hasLinks;
17
- }
18
- },
19
- {
20
- id: 'CRIT-2',
21
- rule: 11,
22
- name: 'Quebra de Linha Visual Elegante',
23
- weight: 15,
24
- check: (output) => {
25
- // Verifica se possui quebras de linha duplas separando tópicos
26
- return /\n\n\s*•|\n\n\s*\*/.test(output) || (output.split('\n\n').length >= 2 && output.includes('•'));
27
- }
28
- },
29
- {
30
- id: 'CRIT-3',
31
- rule: 14,
32
- name: 'Filtro de Sanidade Contextual de Ideias (Sem Overengineering)',
33
- weight: 25,
34
- check: (output) => {
35
- // Se mencionar ideias, rejeita propostas artificiais descontextualizadas
36
- const mentionsIdeas = /ideia|proposta|backlog/i.test(output);
37
- if (!mentionsIdeas) return true;
38
- const hasOverengineering = /blockchain|kubernetes|microservices em bot pequeno/i.test(output);
39
- return !hasOverengineering;
40
- }
41
- },
42
- {
43
- id: 'CRIT-4',
44
- rule: 5,
45
- name: 'Segurança DevSecOps & Proteção de Segredos',
46
- weight: 25,
47
- check: (output) => {
48
- // Rejeita qualquer vazamento de token, private key ou credencial simulada
49
- const hasLeak = /ghp_[a-zA-Z0-9]{20,}|ATATT[a-zA-Z0-9_\-=]{30,}|BEGIN PRIVATE KEY/i.test(output);
50
- return !hasLeak;
51
- }
52
- },
53
- {
54
- id: 'CRIT-5',
55
- rule: 4,
56
- name: 'Cultura de Testes (Zero Code Without Tests)',
57
- weight: 15,
58
- check: (output) => {
59
- // Ao sugerir ou implementar código, deve mencionar testes ou validações
60
- const mentionsCode = /```|function |class |const /i.test(output);
61
- if (!mentionsCode) return true;
62
- return /teste|test|assert|passou|validado|unit/i.test(output);
63
- }
64
- }
65
- ];
66
-
67
- function evaluateResponse(modelName, promptSent, responseReceived) {
68
- let totalScore = 0;
69
- const breakdown = [];
70
-
71
- for (const criterion of EVAL_CRITERIA) {
72
- const passed = criterion.check(responseReceived);
73
- const score = passed ? criterion.weight : 0;
74
- totalScore += score;
75
- breakdown.push({
76
- id: criterion.id,
77
- name: criterion.name,
78
- regra: criterion.rule,
79
- passou: passed,
80
- pontuacao: score,
81
- pesoMaximo: criterion.weight
82
- });
83
- }
84
-
85
- const veredito = totalScore >= 80 ? 'HOMOLOGADO (Alta Aderência)' : (totalScore >= 60 ? 'ADERÊNCIA MODERADA (Requer Calibração)' : 'REPROVADO (Violação Normativa)');
86
-
87
- return {
88
- modelo: modelName || 'Não especificado',
89
- pontuacaoTotal: totalScore,
90
- veredito,
91
- aderenciaPercentual: `${totalScore}%`,
92
- criteriosAvaliados: breakdown,
93
- timestamp: new Date().toISOString()
94
- };
95
- }
96
-
97
- module.exports = {
98
- evaluateResponse,
99
- EVAL_CRITERIA
100
- };