agent-master-devsecops 0.0.0-stage → 3.9.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 (47) hide show
  1. package/.cursorignore +23 -0
  2. package/README.md +371 -3
  3. package/agents/AGENT_DEVSECOPS.md +417 -0
  4. package/agents/AGENT_ORCHESTRATOR.md +264 -0
  5. package/agents/AGENT_QA.md +186 -0
  6. package/agents/AGENT_SCRUM.md +190 -0
  7. package/agents/AGENT_SRE.md +173 -0
  8. package/agents/AGENT_UIUX.md +162 -0
  9. package/agents/compact/AGENT_ORCHESTRATOR_COMPACTO.md +53 -0
  10. package/docs/ARCHITECTURE.md +148 -0
  11. package/docs/CALCULATIONS.md +17 -0
  12. package/docs/CHANGELOG.md +49 -0
  13. package/docs/ENGINEERING_RULES.md +252 -0
  14. package/docs/IDEAS.md +17 -0
  15. package/docs/STATE.md +43 -0
  16. package/docs/playbooks/PLAYBOOK_CIRCUIT_BREAKER_LLM.md +58 -0
  17. package/docs/playbooks/PLAYBOOK_OPENTELEMETRY_TRACING.md +58 -0
  18. package/package.json +47 -6
  19. package/scripts/a11y_auditor.js +187 -0
  20. package/scripts/ast_skeletonizer.js +220 -0
  21. package/scripts/atlassian_client.js +238 -0
  22. package/scripts/atlassian_sync_action.js +269 -0
  23. package/scripts/bootstrap_cli.js +468 -0
  24. package/scripts/circuit_breaker.js +330 -0
  25. package/scripts/code_quality_auditor.js +142 -0
  26. package/scripts/confluence_sync.js +337 -0
  27. package/scripts/context_compressor.js +139 -0
  28. package/scripts/contract_test_validator.js +166 -0
  29. package/scripts/credentials_guard.js +216 -0
  30. package/scripts/domain_idea_miner.js +364 -0
  31. package/scripts/eval_harness.js +100 -0
  32. package/scripts/fast_feature_scaffold.js +338 -0
  33. package/scripts/git_hygiene_linter.js +186 -0
  34. package/scripts/hot_state_cache.js +95 -0
  35. package/scripts/jira_manager.js +408 -0
  36. package/scripts/magic_prompt_generator.js +205 -0
  37. package/scripts/mcp_server.js +1884 -0
  38. package/scripts/multi_agent_orchestrator.js +258 -0
  39. package/scripts/otel_tracer.js +228 -0
  40. package/scripts/perf_benchmark.js +153 -0
  41. package/scripts/pr_reviewer.js +373 -0
  42. package/scripts/sast_scanner.js +180 -0
  43. package/scripts/sca_dependency_auditor.js +167 -0
  44. package/scripts/smart_onboarding.js +610 -0
  45. package/scripts/sre_remediation_engine.js +174 -0
  46. package/scripts/universal_packager.js +313 -0
  47. package/scripts/web_inspector.js +355 -0
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * scripts/circuit_breaker.js
4
+ *
5
+ * Circuit Breaker Inteligente Multi-Provedor para LLMs (AGDEV-9 / I-10).
6
+ * Abstração de resiliência e auto-cura (CLOSED, OPEN, HALF_OPEN) com fallback automático
7
+ * entre provedores de IA (Anthropic, OpenAI, Groq, Ollama) diante de erros HTTP 429/5xx ou timeouts.
8
+ */
9
+
10
+ const STATES = {
11
+ CLOSED: 'CLOSED', // Operação normal
12
+ OPEN: 'OPEN', // Circuito rompido; chamadas desviadas para fallback
13
+ HALF_OPEN: 'HALF_OPEN' // Sonda de teste para verificar recuperação
14
+ };
15
+
16
+ class CircuitBreaker {
17
+ constructor(name, options = {}) {
18
+ this.name = name;
19
+ this.failureThreshold = options.failureThreshold || 3;
20
+ this.recoveryTimeoutMs = options.recoveryTimeoutMs || 30000;
21
+ this.state = STATES.CLOSED;
22
+ this.failureCount = 0;
23
+ this.successCount = 0;
24
+ this.lastFailureTime = null;
25
+ this.lastStateChange = Date.now();
26
+ }
27
+
28
+ isOpen() {
29
+ if (this.state === STATES.OPEN) {
30
+ const now = Date.now();
31
+ if (now - this.lastFailureTime >= this.recoveryTimeoutMs) {
32
+ this.state = STATES.HALF_OPEN;
33
+ this.lastStateChange = now;
34
+ return false;
35
+ }
36
+ return true;
37
+ }
38
+ return false;
39
+ }
40
+
41
+ recordSuccess() {
42
+ this.failureCount = 0;
43
+ this.successCount++;
44
+ if (this.state === STATES.HALF_OPEN) {
45
+ this.state = STATES.CLOSED;
46
+ this.lastStateChange = Date.now();
47
+ }
48
+ }
49
+
50
+ recordFailure(error) {
51
+ this.failureCount++;
52
+ this.lastFailureTime = Date.now();
53
+
54
+ if (this.state === STATES.HALF_OPEN || this.failureCount >= this.failureThreshold) {
55
+ this.state = STATES.OPEN;
56
+ this.lastStateChange = Date.now();
57
+ }
58
+ }
59
+
60
+ async execute(primaryFn, fallbackFn = null) {
61
+ if (this.isOpen()) {
62
+ if (fallbackFn) {
63
+ return {
64
+ result: await fallbackFn(new Error(`Circuito ${this.name} está ABERTO (OPEN). Redirecionando para fallback.`)),
65
+ circuit: this.name,
66
+ state: this.state,
67
+ fallbackExecuted: true
68
+ };
69
+ }
70
+ throw new Error(`Circuito ${this.name} está ABERTO (OPEN). Nenhuma chamada permitida.`);
71
+ }
72
+
73
+ try {
74
+ const result = await primaryFn();
75
+ this.recordSuccess();
76
+ return {
77
+ result,
78
+ circuit: this.name,
79
+ state: this.state,
80
+ fallbackExecuted: false
81
+ };
82
+ } catch (err) {
83
+ this.recordFailure(err);
84
+ if (fallbackFn) {
85
+ return {
86
+ result: await fallbackFn(err),
87
+ circuit: this.name,
88
+ state: this.state,
89
+ fallbackExecuted: true,
90
+ error: err.message
91
+ };
92
+ }
93
+ throw err;
94
+ }
95
+ }
96
+
97
+ executeSync(primaryFn, fallbackFn = null) {
98
+ if (this.isOpen()) {
99
+ if (fallbackFn) {
100
+ return {
101
+ result: fallbackFn(new Error(`Circuito ${this.name} está ABERTO (OPEN). Redirecionando para fallback.`)),
102
+ circuit: this.name,
103
+ state: this.state,
104
+ fallbackExecuted: true
105
+ };
106
+ }
107
+ throw new Error(`Circuito ${this.name} está ABERTO (OPEN). Nenhuma chamada permitida.`);
108
+ }
109
+
110
+ try {
111
+ const result = primaryFn();
112
+ this.recordSuccess();
113
+ return {
114
+ result,
115
+ circuit: this.name,
116
+ state: this.state,
117
+ fallbackExecuted: false
118
+ };
119
+ } catch (err) {
120
+ this.recordFailure(err);
121
+ if (fallbackFn) {
122
+ return {
123
+ result: fallbackFn(err),
124
+ circuit: this.name,
125
+ state: this.state,
126
+ fallbackExecuted: true,
127
+ error: err.message
128
+ };
129
+ }
130
+ throw err;
131
+ }
132
+ }
133
+
134
+ reset() {
135
+ this.state = STATES.CLOSED;
136
+ this.failureCount = 0;
137
+ this.successCount = 0;
138
+ this.lastFailureTime = null;
139
+ this.lastStateChange = Date.now();
140
+ }
141
+
142
+ getStatus() {
143
+ return {
144
+ name: this.name,
145
+ state: this.state,
146
+ failureCount: this.failureCount,
147
+ failureThreshold: this.failureThreshold,
148
+ recoveryTimeoutMs: this.recoveryTimeoutMs,
149
+ isOpen: this.isOpen()
150
+ };
151
+ }
152
+ }
153
+
154
+ class MultiProviderRouter {
155
+ constructor(providers = ['anthropic', 'openai', 'groq', 'ollama'], options = {}) {
156
+ this.circuits = new Map();
157
+ providers.forEach(p => {
158
+ this.circuits.set(p, new CircuitBreaker(p, options));
159
+ });
160
+ this.providerChain = providers;
161
+ }
162
+
163
+ async dispatch(taskFnMap) {
164
+ const attempted = [];
165
+
166
+ for (const provider of this.providerChain) {
167
+ const circuit = this.circuits.get(provider);
168
+ attempted.push(provider);
169
+
170
+ if (!circuit.isOpen()) {
171
+ const fn = taskFnMap[provider] || taskFnMap['default'];
172
+ if (!fn) continue;
173
+
174
+ try {
175
+ const res = await circuit.execute(fn);
176
+ return {
177
+ success: true,
178
+ providerUsed: provider,
179
+ attempted,
180
+ result: res.result,
181
+ circuitStates: this.getAllStatuses()
182
+ };
183
+ } catch (err) {
184
+ // Erro registrado pelo circuit breaker; continua para o próximo provedor na cadeia
185
+ continue;
186
+ }
187
+ }
188
+ }
189
+
190
+ return {
191
+ success: false,
192
+ error: 'Todos os provedores de IA estão indisponíveis ou com circuito ABERTO.',
193
+ attempted,
194
+ circuitStates: this.getAllStatuses()
195
+ };
196
+ }
197
+
198
+ dispatchSync(taskFnMap) {
199
+ const attempted = [];
200
+
201
+ for (const provider of this.providerChain) {
202
+ const circuit = this.circuits.get(provider);
203
+ attempted.push(provider);
204
+
205
+ if (!circuit.isOpen()) {
206
+ const fn = taskFnMap[provider] || taskFnMap['default'];
207
+ if (!fn) continue;
208
+
209
+ try {
210
+ const res = circuit.executeSync(fn);
211
+ return {
212
+ success: true,
213
+ providerUsed: provider,
214
+ attempted,
215
+ result: res.result,
216
+ circuitStates: this.getAllStatuses()
217
+ };
218
+ } catch (err) {
219
+ continue;
220
+ }
221
+ }
222
+ }
223
+
224
+ return {
225
+ success: false,
226
+ error: 'Todos os provedores de IA estão indisponíveis ou com circuito ABERTO.',
227
+ attempted,
228
+ circuitStates: this.getAllStatuses()
229
+ };
230
+ }
231
+
232
+ getAllStatuses() {
233
+ const statuses = {};
234
+ for (const [name, circuit] of this.circuits.entries()) {
235
+ statuses[name] = circuit.getStatus();
236
+ }
237
+ return statuses;
238
+ }
239
+
240
+ resetAll() {
241
+ for (const circuit of this.circuits.values()) {
242
+ circuit.reset();
243
+ }
244
+ }
245
+ }
246
+
247
+ async function runCli() {
248
+ console.log('=== Simulação de Resiliência Multi-Provedor com Circuit Breaker (I-10) ===');
249
+ const router = new MultiProviderRouter(['anthropic', 'openai', 'groq', 'ollama'], { failureThreshold: 2 });
250
+
251
+ // Simula indisponibilidade em anthropic e openai
252
+ const taskMap = {
253
+ anthropic: async () => { throw new Error('HTTP 429: Too Many Requests (Rate limit exceeded)'); },
254
+ openai: async () => { throw new Error('HTTP 503: Service Unavailable'); },
255
+ groq: async () => { return 'Resposta gerada com sucesso via Groq (Llama 3.3 70B)'; },
256
+ ollama: async () => { return 'Resposta local via Ollama'; }
257
+ };
258
+
259
+ const dispatch1 = await router.dispatch(taskMap);
260
+ console.log('\nDespacho 1 (Anthropic & OpenAI falham -> Fallback para Groq):');
261
+ console.log(`Provedor Utilizado: ${dispatch1.providerUsed}`);
262
+ console.log(`Tentativas: ${dispatch1.attempted.join(' -> ')}`);
263
+ console.log(`Resultado: ${dispatch1.result}`);
264
+
265
+ console.log('\nStatus dos Circuitos:');
266
+ console.log(JSON.stringify(dispatch1.circuitStates, null, 2));
267
+ }
268
+
269
+ if (require.main === module) {
270
+ runCli().catch(console.error);
271
+ }
272
+
273
+
274
+ class AntiThrashingGuard {
275
+ constructor(options = {}) {
276
+ this.maxIdenticalFailures = options.maxIdenticalFailures || 2;
277
+ this.history = [];
278
+ this.thrashingTripped = false;
279
+ }
280
+
281
+ normalizeError(error) {
282
+ if (!error) return 'unknown_error';
283
+ const msg = typeof error === 'string' ? error : (error.message || String(error));
284
+ return msg
285
+ .replace(/:\d+:\d+/g, '')
286
+ .replace(/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\b/g, '')
287
+ .replace(/0x[0-9a-fA-F]+/g, '')
288
+ .trim();
289
+ }
290
+
291
+ recordAttempt(error = null) {
292
+ if (!error) {
293
+ this.history = [];
294
+ this.thrashingTripped = false;
295
+ return { isThrashing: false, count: 0 };
296
+ }
297
+
298
+ const norm = this.normalizeError(error);
299
+ this.history.push({ signature: norm, timestamp: Date.now() });
300
+
301
+ if (this.history.length >= this.maxIdenticalFailures) {
302
+ const recent = this.history.slice(-this.maxIdenticalFailures);
303
+ const allIdentical = recent.every(h => h.signature === norm);
304
+ if (allIdentical) {
305
+ this.thrashingTripped = true;
306
+ return {
307
+ isThrashing: true,
308
+ count: this.history.length,
309
+ signature: norm,
310
+ message: `[ANTI-THRASHING FINOPS VETO]: Detectadas ${this.maxIdenticalFailures} falhas consecutivas com erro idêntico ("${norm.substring(0, 80)}..."). Execução autônoma interrompida para evitar queima descontrolada de tokens.`
311
+ };
312
+ }
313
+ }
314
+
315
+ return { isThrashing: false, count: this.history.length, signature: norm };
316
+ }
317
+
318
+ reset() {
319
+ this.history = [];
320
+ this.thrashingTripped = false;
321
+ }
322
+ }
323
+
324
+
325
+ module.exports = {
326
+ STATES,
327
+ CircuitBreaker,
328
+ MultiProviderRouter,
329
+ AntiThrashingGuard
330
+ };
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Code Quality & Cyclomatic Complexity Auditor
3
+ * Auditoria independente de qualidade de software, complexidade ciclomática,
4
+ * profundidade de aninhamento e detecção de anti-patterns de código.
5
+ * Em conformidade com a Regra 13 (Estrutura de Código) e Matriz de Severidade.
6
+ */
7
+
8
+ const fs = require('node:fs');
9
+ const path = require('node:path');
10
+
11
+ class CodeQualityAuditor {
12
+ constructor(options = {}) {
13
+ this.maxComplexity = options.maxComplexity || 15;
14
+ this.maxNesting = options.maxNesting || 4;
15
+ this.maxFileLines = options.maxFileLines || 300;
16
+ }
17
+
18
+ auditCode(code, filename = 'snippet.js') {
19
+ const lines = code.split('\n');
20
+ const totalLines = lines.length;
21
+ let commentLines = 0;
22
+ let blankLines = 0;
23
+ let maxNestingObserved = 0;
24
+ let currentNesting = 0;
25
+ const issues = [];
26
+
27
+ // Contadores de decisão para complexidade ciclomática
28
+ let complexityScore = 1;
29
+ const decisionPatterns = [
30
+ /\bif\s*\(/g,
31
+ /\belse\s+if\s*\(/g,
32
+ /\bfor\s*\(/g,
33
+ /\bwhile\s*\(/g,
34
+ /\bcatch\s*\(/g,
35
+ /\bcase\s+[^:]+:/g,
36
+ /&&/g,
37
+ /\|\|/g,
38
+ /\?[^:]+:/g
39
+ ];
40
+
41
+ lines.forEach((line, index) => {
42
+ const lineNum = index + 1;
43
+ const trimmed = line.trim();
44
+
45
+ if (!trimmed) {
46
+ blankLines++;
47
+ return;
48
+ }
49
+
50
+ if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
51
+ commentLines++;
52
+ return;
53
+ }
54
+
55
+ // Análise de aninhamento de chaves
56
+ for (const char of trimmed) {
57
+ if (char === '{') currentNesting++;
58
+ if (char === '}') currentNesting = Math.max(0, currentNesting - 1);
59
+ if (currentNesting > maxNestingObserved) {
60
+ maxNestingObserved = currentNesting;
61
+ }
62
+ }
63
+
64
+ // Contagem de complexidade ciclomática
65
+ for (const pattern of decisionPatterns) {
66
+ const matches = trimmed.match(pattern);
67
+ if (matches) {
68
+ complexityScore += matches.length;
69
+ }
70
+ }
71
+
72
+ // Detecção de empty catch block (anti-pattern de engolir exceções)
73
+ if (/catch\s*\([^\)]*\)\s*\{\s*\}/.test(trimmed)) {
74
+ issues.push({
75
+ type: 'EMPTY_CATCH',
76
+ line: lineNum,
77
+ severity: 'HIGH',
78
+ message: 'Bloco catch vazio detectado: erros não devem ser suprimidos silenciosamente (Regra 5).'
79
+ });
80
+ }
81
+
82
+ // Detecção de comentários TODO ou código comentado
83
+ if (/\b(?:TODO|FIXME|HACK)\b/i.test(trimmed)) {
84
+ issues.push({
85
+ type: 'PENDING_TODO',
86
+ line: lineNum,
87
+ severity: 'LOW',
88
+ message: `Marcação pendente detectada: "${trimmed}". Código pronto não deve deixar TODOs (Regra 7).`
89
+ });
90
+ }
91
+ });
92
+
93
+ // Verificação de limites
94
+ if (totalLines > this.maxFileLines) {
95
+ issues.push({
96
+ type: 'FILE_TOO_LONG',
97
+ line: totalLines,
98
+ severity: 'MEDIUM',
99
+ message: `Arquivo possui ${totalLines} linhas (limite canônico recomendado: ${this.maxFileLines} linhas - Regra 13).`
100
+ });
101
+ }
102
+
103
+ if (maxNestingObserved > this.maxNesting) {
104
+ issues.push({
105
+ type: 'DEEP_NESTING',
106
+ line: 1,
107
+ severity: 'MEDIUM',
108
+ message: `Nível máximo de aninhamento (${maxNestingObserved}) excede o limiar tolerado (${this.maxNesting}).`
109
+ });
110
+ }
111
+
112
+ if (complexityScore > this.maxComplexity) {
113
+ issues.push({
114
+ type: 'HIGH_CYCLOMATIC_COMPLEXITY',
115
+ line: 1,
116
+ severity: 'MEDIUM',
117
+ message: `Complexidade ciclomática (${complexityScore}) excede o limite aceitável de ${this.maxComplexity}.`
118
+ });
119
+ }
120
+
121
+ const isClean = issues.filter(i => i.severity === 'HIGH' || i.severity === 'CRITICAL').length === 0;
122
+
123
+ return {
124
+ filename,
125
+ status: isClean ? (issues.length === 0 ? 'OPTIMAL' : 'ACCEPTABLE_WITH_NOTES') : 'REFACTOR_REQUIRED',
126
+ metrics: {
127
+ totalLines,
128
+ codeLines: totalLines - commentLines - blankLines,
129
+ commentLines,
130
+ blankLines,
131
+ cyclomaticComplexity: complexityScore,
132
+ maxNestingDepth: maxNestingObserved
133
+ },
134
+ qualityScore: Math.max(0, 100 - (issues.length * 10) - (complexityScore > this.maxComplexity ? 15 : 0)),
135
+ issues
136
+ };
137
+ }
138
+ }
139
+
140
+ module.exports = {
141
+ CodeQualityAuditor
142
+ };