mcp-lab-agent 1.0.1 → 1.1.1
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/README.md +6 -1
- package/dist/index.js +453 -5
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
- package/qa-lab-flows.json.example +19 -0
package/README.md
CHANGED
|
@@ -142,6 +142,10 @@ Configure no `~/.cursor/mcp.json`:
|
|
|
142
142
|
| `get_test_coverage` | Gera relatório de cobertura de testes (Jest) |
|
|
143
143
|
| `watch_tests` | Inicia testes em watch mode (Jest/Vitest) |
|
|
144
144
|
| `create_test_template` | Gera boilerplate de teste (API/UI/Unit) para qualquer framework |
|
|
145
|
+
| `suggest_selector_fix` | **Self-healing:** Sugere seletor alternativo quando UI muda (element not found) |
|
|
146
|
+
| `get_business_metrics` | **Métricas de negócio:** Tempo até bug, custo por defeito, cobertura por fluxo |
|
|
147
|
+
| `suggest_selector_fix` | **Self-healing:** Sugere correção de seletor quando UI muda (LLM) |
|
|
148
|
+
| `get_business_metrics` | **Métricas de negócio:** Tempo até bug, custo por defeito, cobertura por fluxo |
|
|
145
149
|
|
|
146
150
|
## Variáveis de ambiente (opcional)
|
|
147
151
|
|
|
@@ -211,7 +215,8 @@ Você não precisa chamar essas ferramentas diretamente, mas é útil saber o qu
|
|
|
211
215
|
| **Detecção** | `detect_project`, `read_project`, `list_test_files` |
|
|
212
216
|
| **Execução** | `run_tests`, `watch_tests`, `get_test_coverage` |
|
|
213
217
|
| **Geração** | `generate_tests`, `write_test`, `create_test_template` |
|
|
214
|
-
| **Análise** | `analyze_failures`, `suggest_fix`, `create_bug_report` |
|
|
218
|
+
| **Análise** | `analyze_failures`, `suggest_fix`, `suggest_selector_fix`, `create_bug_report` |
|
|
219
|
+
| **Métricas** | `get_business_metrics` |
|
|
215
220
|
| **Manutenção** | `run_linter`, `install_dependencies` |
|
|
216
221
|
|
|
217
222
|
## Publicar no npm
|
package/dist/index.js
CHANGED
|
@@ -80,6 +80,22 @@ function detectProjectStructure() {
|
|
|
80
80
|
structure.testFrameworks.push("pactum");
|
|
81
81
|
structure.hasTests = true;
|
|
82
82
|
}
|
|
83
|
+
if (deps.testcafe || deps["testcafe"]) {
|
|
84
|
+
structure.testFrameworks.push("testcafe");
|
|
85
|
+
structure.hasTests = true;
|
|
86
|
+
}
|
|
87
|
+
if (deps.nightwatch || deps["nightwatch"]) {
|
|
88
|
+
structure.testFrameworks.push("nightwatch");
|
|
89
|
+
structure.hasTests = true;
|
|
90
|
+
}
|
|
91
|
+
if (deps.puppeteer) {
|
|
92
|
+
structure.testFrameworks.push("puppeteer");
|
|
93
|
+
structure.hasTests = true;
|
|
94
|
+
}
|
|
95
|
+
if (deps.codeceptjs || deps["codeceptjs"]) {
|
|
96
|
+
structure.testFrameworks.push("codeceptjs");
|
|
97
|
+
structure.hasTests = true;
|
|
98
|
+
}
|
|
83
99
|
if (deps.express || deps.fastify || deps["@nestjs/core"] || deps.koa) {
|
|
84
100
|
structure.hasBackend = true;
|
|
85
101
|
}
|
|
@@ -123,14 +139,76 @@ function detectProjectStructure() {
|
|
|
123
139
|
"features",
|
|
124
140
|
"scenarios",
|
|
125
141
|
"mobile",
|
|
126
|
-
"api"
|
|
142
|
+
"api",
|
|
143
|
+
// Monorepo: subprojetos por framework
|
|
144
|
+
"playwright-js",
|
|
145
|
+
"puppeteer-js",
|
|
146
|
+
"testcafe-js",
|
|
147
|
+
"wdio-webdriver-io",
|
|
148
|
+
"nightwatch-js",
|
|
149
|
+
"codeceptjs",
|
|
150
|
+
"robot-framework",
|
|
151
|
+
"selenium-python"
|
|
127
152
|
];
|
|
128
153
|
for (const dir of commonTestDirs) {
|
|
129
154
|
const fullPath = path.join(PROJECT_ROOT, dir);
|
|
130
|
-
if (fs.existsSync(fullPath)) {
|
|
155
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
|
|
131
156
|
structure.testDirs.push(dir);
|
|
132
157
|
}
|
|
133
158
|
}
|
|
159
|
+
const skipDirs = ["node_modules", ".git", "dist", "build", ".next", ".venv"];
|
|
160
|
+
try {
|
|
161
|
+
const rootEntries = fs.readdirSync(PROJECT_ROOT, { withFileTypes: true });
|
|
162
|
+
for (const e of rootEntries) {
|
|
163
|
+
if (!e.isDirectory() || skipDirs.includes(e.name)) continue;
|
|
164
|
+
const subPath = path.join(PROJECT_ROOT, e.name);
|
|
165
|
+
if (structure.testDirs.includes(e.name)) continue;
|
|
166
|
+
const hasPkg = fs.existsSync(path.join(subPath, "package.json"));
|
|
167
|
+
const hasTests = fs.existsSync(path.join(subPath, "tests")) || fs.existsSync(path.join(subPath, "test")) || fs.existsSync(path.join(subPath, "e2e")) || fs.existsSync(path.join(subPath, "__tests__")) || fs.existsSync(path.join(subPath, "specs"));
|
|
168
|
+
if (hasPkg || hasTests) {
|
|
169
|
+
structure.testDirs.push(e.name);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} catch {
|
|
173
|
+
}
|
|
174
|
+
for (const dir of structure.testDirs) {
|
|
175
|
+
const subPkg = path.join(PROJECT_ROOT, dir, "package.json");
|
|
176
|
+
if (!fs.existsSync(subPkg)) continue;
|
|
177
|
+
try {
|
|
178
|
+
const sub = JSON.parse(fs.readFileSync(subPkg, "utf8"));
|
|
179
|
+
const subDeps = { ...sub.dependencies || {}, ...sub.devDependencies || {} };
|
|
180
|
+
const toAdd = [];
|
|
181
|
+
if (subDeps.cypress && !structure.testFrameworks.includes("cypress")) toAdd.push("cypress");
|
|
182
|
+
if ((subDeps["@playwright/test"] || subDeps.playwright) && !structure.testFrameworks.includes("playwright")) toAdd.push("playwright");
|
|
183
|
+
if ((subDeps.webdriverio || subDeps["@wdio/cli"]) && !structure.testFrameworks.includes("webdriverio")) toAdd.push("webdriverio");
|
|
184
|
+
if (subDeps.testcafe && !structure.testFrameworks.includes("testcafe")) toAdd.push("testcafe");
|
|
185
|
+
if (subDeps.nightwatch && !structure.testFrameworks.includes("nightwatch")) toAdd.push("nightwatch");
|
|
186
|
+
if (subDeps.puppeteer && !structure.testFrameworks.includes("puppeteer")) toAdd.push("puppeteer");
|
|
187
|
+
if (subDeps.codeceptjs && !structure.testFrameworks.includes("codeceptjs")) toAdd.push("codeceptjs");
|
|
188
|
+
if (subDeps.jest && !structure.testFrameworks.includes("jest")) toAdd.push("jest");
|
|
189
|
+
toAdd.forEach((fw) => {
|
|
190
|
+
structure.testFrameworks.push(fw);
|
|
191
|
+
structure.hasTests = true;
|
|
192
|
+
});
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
for (const dir of structure.testDirs) {
|
|
197
|
+
const reqPath = path.join(PROJECT_ROOT, dir, "requirements.txt");
|
|
198
|
+
if (!fs.existsSync(reqPath)) continue;
|
|
199
|
+
try {
|
|
200
|
+
const req = fs.readFileSync(reqPath, "utf8");
|
|
201
|
+
if (/robotframework/i.test(req) && !structure.testFrameworks.includes("robot")) {
|
|
202
|
+
structure.testFrameworks.push("robot");
|
|
203
|
+
structure.hasTests = true;
|
|
204
|
+
}
|
|
205
|
+
if (/pytest|selenium/i.test(req) && !structure.testFrameworks.includes("pytest")) {
|
|
206
|
+
structure.testFrameworks.push("pytest");
|
|
207
|
+
structure.hasTests = true;
|
|
208
|
+
}
|
|
209
|
+
} catch {
|
|
210
|
+
}
|
|
211
|
+
}
|
|
134
212
|
const commonBackendDirs = ["backend", "server", "api", "src"];
|
|
135
213
|
for (const dir of commonBackendDirs) {
|
|
136
214
|
const fullPath = path.join(PROJECT_ROOT, dir);
|
|
@@ -155,6 +233,8 @@ function detectProjectStructure() {
|
|
|
155
233
|
}
|
|
156
234
|
var UNIVERSAL_TEST_PATTERNS = [
|
|
157
235
|
/\.(cy|spec|test)\.(js|ts|jsx|tsx)$/i,
|
|
236
|
+
/_test\.(js|ts)$/i,
|
|
237
|
+
// CodeceptJS
|
|
158
238
|
/\.robot$/i,
|
|
159
239
|
/\.feature$/i,
|
|
160
240
|
/^(test_.*|.*_test)\.py$/i,
|
|
@@ -176,11 +256,12 @@ function collectTestFiles(structure, options = {}) {
|
|
|
176
256
|
for (const e of entries) {
|
|
177
257
|
const rel = base ? `${base}/${e.name}` : e.name;
|
|
178
258
|
if (e.isDirectory()) {
|
|
259
|
+
if (e.name === "node_modules" || e.name === ".git" || e.name === ".venv") continue;
|
|
179
260
|
walk(path.join(p, e.name), rel);
|
|
180
261
|
} else if (e.isFile() && isTestFile(e.name)) {
|
|
181
262
|
const filePath = `${dir}/${rel}`;
|
|
182
263
|
if (pattern && !filePath.toLowerCase().includes(pattern.toLowerCase())) continue;
|
|
183
|
-
const inferredFw = inferFrameworkFromFile(e.name, structure);
|
|
264
|
+
const inferredFw = inferFrameworkFromFile(e.name, structure, filePath);
|
|
184
265
|
if (framework && framework !== "all" && inferredFw !== framework && !matchesFramework(inferredFw, framework)) continue;
|
|
185
266
|
const entry = { path: filePath, inferredFramework: inferredFw };
|
|
186
267
|
if (maxContentFiles > 0 && results.length < maxContentFiles) {
|
|
@@ -197,8 +278,21 @@ function collectTestFiles(structure, options = {}) {
|
|
|
197
278
|
}
|
|
198
279
|
return results;
|
|
199
280
|
}
|
|
200
|
-
function inferFrameworkFromFile(name, structure = {}) {
|
|
281
|
+
function inferFrameworkFromFile(name, structure = {}, filePath = "") {
|
|
282
|
+
const pathLower = (filePath || "").toLowerCase().replace(/\\/g, "/");
|
|
283
|
+
if (/[\/]cypress[\/\-]/.test(pathLower)) return "cypress";
|
|
284
|
+
if (/[\/]playwright[\/\-]/.test(pathLower)) return "playwright";
|
|
285
|
+
if (/[\/]wdio[\/\-]|[\/]webdriver[\/\-]/.test(pathLower)) return "webdriverio";
|
|
286
|
+
if (/[\/]appium[\/\-]/.test(pathLower)) return "appium";
|
|
287
|
+
if (/[\/]selenium-python[\/]|[\/]pytest[\/\-]/.test(pathLower)) return "pytest";
|
|
288
|
+
if (/[\/]robot[\/\-]/.test(pathLower)) return "robot";
|
|
289
|
+
if (/[\/]codecept[\/\-]/.test(pathLower)) return "codeceptjs";
|
|
290
|
+
if (/[\/]nightwatch[\/\-]/.test(pathLower)) return "nightwatch";
|
|
291
|
+
if (/[\/]testcafe[\/\-]/.test(pathLower)) return "testcafe";
|
|
292
|
+
if (/[\/]puppeteer[\/\-]/.test(pathLower)) return "puppeteer";
|
|
293
|
+
if (/[\/]behave[\/\-]|[\/]features[\/]/.test(pathLower)) return "behave";
|
|
201
294
|
if (/\.cy\.(js|ts|jsx|tsx)/i.test(name)) return "cypress";
|
|
295
|
+
if (/_test\.(js|ts)$/i.test(name)) return "codeceptjs";
|
|
202
296
|
if (/\.spec\.(js|ts|jsx|tsx)/i.test(name)) {
|
|
203
297
|
if (structure?.testFrameworks?.includes("webdriverio")) return "webdriverio";
|
|
204
298
|
if (structure?.testFrameworks?.includes("appium")) return "appium";
|
|
@@ -216,6 +310,62 @@ function matchesFramework(inferred, requested) {
|
|
|
216
310
|
if (inferred === requested) return true;
|
|
217
311
|
return aliases[inferred]?.includes(requested);
|
|
218
312
|
}
|
|
313
|
+
var METRICS_FILE = path.join(PROJECT_ROOT, ".qa-lab-metrics.json");
|
|
314
|
+
var FLOWS_CONFIG_FILE = path.join(PROJECT_ROOT, "qa-lab-flows.json");
|
|
315
|
+
function parseTestRunResult(runOutput, exitCode) {
|
|
316
|
+
let passed = 0;
|
|
317
|
+
let failed = 0;
|
|
318
|
+
const jestMatch = runOutput.match(/Tests:\s+(\d+)\s+passed(?:,\s*(\d+)\s+failed)?/);
|
|
319
|
+
if (jestMatch) {
|
|
320
|
+
passed = parseInt(jestMatch[1], 10);
|
|
321
|
+
failed = jestMatch[2] ? parseInt(jestMatch[2], 10) : 0;
|
|
322
|
+
}
|
|
323
|
+
const cypressPass = runOutput.match(/(\d+)\s+passing/);
|
|
324
|
+
const cypressFail = runOutput.match(/(\d+)\s+failing/);
|
|
325
|
+
if (cypressPass) passed = parseInt(cypressPass[1], 10);
|
|
326
|
+
if (cypressFail) failed = parseInt(cypressFail[1], 10);
|
|
327
|
+
const pwPass = runOutput.match(/(\d+)\s+passed/);
|
|
328
|
+
const pwFail = runOutput.match(/(\d+)\s+failed/);
|
|
329
|
+
if (pwPass) passed = parseInt(pwPass[1], 10);
|
|
330
|
+
if (pwFail) failed = parseInt(pwFail[1], 10);
|
|
331
|
+
if (passed === 0 && failed === 0) {
|
|
332
|
+
if (exitCode === 0) passed = 1;
|
|
333
|
+
else failed = 1;
|
|
334
|
+
}
|
|
335
|
+
return { passed, failed };
|
|
336
|
+
}
|
|
337
|
+
function appendMetricsEvent(event) {
|
|
338
|
+
try {
|
|
339
|
+
let data = { events: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
340
|
+
if (fs.existsSync(METRICS_FILE)) {
|
|
341
|
+
const raw = fs.readFileSync(METRICS_FILE, "utf8");
|
|
342
|
+
try {
|
|
343
|
+
data = JSON.parse(raw);
|
|
344
|
+
} catch {
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
data.events = data.events || [];
|
|
348
|
+
data.events.push({ ...event, timestamp: event.timestamp || (/* @__PURE__ */ new Date()).toISOString() });
|
|
349
|
+
data.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
350
|
+
if (data.events.length > 500) data.events = data.events.slice(-400);
|
|
351
|
+
fs.writeFileSync(METRICS_FILE, JSON.stringify(data, null, 2), "utf8");
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function extractFailuresFromOutput(runOutput) {
|
|
356
|
+
const failures = [];
|
|
357
|
+
const lines = runOutput.split("\n");
|
|
358
|
+
for (let i = 0; i < lines.length; i++) {
|
|
359
|
+
const line = lines[i];
|
|
360
|
+
if (/fail|error|assertion|timeout|element not found|selector/i.test(line)) {
|
|
361
|
+
failures.push({
|
|
362
|
+
test: lines[Math.max(0, i - 1)]?.trim() || "unknown",
|
|
363
|
+
message: line.trim().slice(0, 500)
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return failures.slice(0, 20);
|
|
368
|
+
}
|
|
219
369
|
server.registerTool(
|
|
220
370
|
"read_file",
|
|
221
371
|
{
|
|
@@ -319,6 +469,10 @@ server.registerTool(
|
|
|
319
469
|
"pytest",
|
|
320
470
|
"supertest",
|
|
321
471
|
"pactum",
|
|
472
|
+
"testcafe",
|
|
473
|
+
"nightwatch",
|
|
474
|
+
"puppeteer",
|
|
475
|
+
"codeceptjs",
|
|
322
476
|
"npm"
|
|
323
477
|
]).optional().describe("Framework espec\xEDfico ou 'npm' para npm test."),
|
|
324
478
|
spec: z.string().optional().describe("Caminho do spec (ex: cypress/e2e/test.cy.js)."),
|
|
@@ -360,7 +514,23 @@ server.registerTool(
|
|
|
360
514
|
} else if (selectedFramework === "webdriverio") {
|
|
361
515
|
cmd = "npx";
|
|
362
516
|
args = spec ? ["wdio", "run", spec] : ["wdio", "run"];
|
|
363
|
-
cwd =
|
|
517
|
+
cwd = getFrameworkCwd(structure, ["wdio-webdriver-io", "specs", "tests"]);
|
|
518
|
+
} else if (selectedFramework === "testcafe") {
|
|
519
|
+
cmd = "npx";
|
|
520
|
+
args = spec ? ["testcafe", spec] : ["testcafe"];
|
|
521
|
+
cwd = getFrameworkCwd(structure, ["testcafe-js", "testcafe", "tests"]);
|
|
522
|
+
} else if (selectedFramework === "nightwatch") {
|
|
523
|
+
cmd = "npx";
|
|
524
|
+
args = spec ? ["nightwatch", "--test", spec] : ["nightwatch"];
|
|
525
|
+
cwd = getFrameworkCwd(structure, ["nightwatch-js", "nightwatch", "tests"]);
|
|
526
|
+
} else if (selectedFramework === "puppeteer") {
|
|
527
|
+
cmd = "npx";
|
|
528
|
+
args = spec ? ["jest", spec, "--config", "jest.config.js"] : ["jest"];
|
|
529
|
+
cwd = getFrameworkCwd(structure, ["puppeteer-js", "puppeteer", "__tests__"]);
|
|
530
|
+
} else if (selectedFramework === "codeceptjs") {
|
|
531
|
+
cmd = "npx";
|
|
532
|
+
args = spec ? ["codeceptjs", "run", "--grep", spec] : ["codeceptjs", "run"];
|
|
533
|
+
cwd = getFrameworkCwd(structure, ["codeceptjs", "tests"]);
|
|
364
534
|
} else if (selectedFramework === "jest") {
|
|
365
535
|
cmd = "npx";
|
|
366
536
|
args = ["jest"];
|
|
@@ -401,6 +571,7 @@ server.registerTool(
|
|
|
401
571
|
args = ["test"];
|
|
402
572
|
cwd = PROJECT_ROOT;
|
|
403
573
|
}
|
|
574
|
+
const startTime = Date.now();
|
|
404
575
|
return new Promise((resolve) => {
|
|
405
576
|
const child = spawn(cmd, args, {
|
|
406
577
|
cwd,
|
|
@@ -427,12 +598,24 @@ server.registerTool(
|
|
|
427
598
|
child.on("close", (code) => {
|
|
428
599
|
const runOutput = [stdout, stderr].filter(Boolean).join("\n").trim();
|
|
429
600
|
const passed = code === 0;
|
|
601
|
+
const durationSeconds = Math.round((Date.now() - startTime) / 1e3);
|
|
430
602
|
if (!passed && runOutput) {
|
|
431
603
|
try {
|
|
432
604
|
fs.writeFileSync(path.join(PROJECT_ROOT, ".qa-lab-last-failure.log"), runOutput, "utf8");
|
|
433
605
|
} catch {
|
|
434
606
|
}
|
|
435
607
|
}
|
|
608
|
+
const { passed: p, failed: f } = parseTestRunResult(runOutput, code);
|
|
609
|
+
appendMetricsEvent({
|
|
610
|
+
type: "test_run",
|
|
611
|
+
framework: selectedFramework,
|
|
612
|
+
spec: spec || void 0,
|
|
613
|
+
passed: p,
|
|
614
|
+
failed: f,
|
|
615
|
+
durationSeconds,
|
|
616
|
+
exitCode: code ?? 1,
|
|
617
|
+
failures: !passed ? extractFailuresFromOutput(runOutput) : void 0
|
|
618
|
+
});
|
|
436
619
|
resolve({
|
|
437
620
|
content: [{ type: "text", text: passed ? "Testes executados com sucesso." : "Falha na execu\xE7\xE3o dos testes." }],
|
|
438
621
|
structuredContent: {
|
|
@@ -1009,6 +1192,130 @@ server.registerTool(
|
|
|
1009
1192
|
};
|
|
1010
1193
|
}
|
|
1011
1194
|
);
|
|
1195
|
+
server.registerTool(
|
|
1196
|
+
"suggest_selector_fix",
|
|
1197
|
+
{
|
|
1198
|
+
title: "Sugerir corre\xE7\xE3o de seletor (Self-healing)",
|
|
1199
|
+
description: "Quando um teste falha por elemento n\xE3o encontrado (seletor quebrado ap\xF3s mudan\xE7a de UI), usa LLM para sugerir seletor alternativo mais resiliente. Prioriza data-testid, role, texto acess\xEDvel.",
|
|
1200
|
+
inputSchema: z.object({
|
|
1201
|
+
testFilePath: z.string().describe("Caminho do arquivo de teste que falhou (ex: specs/login.spec.js)."),
|
|
1202
|
+
errorOutput: z.string().optional().describe("Output do terminal da falha. Se vazio, l\xEA de .qa-lab-last-failure.log."),
|
|
1203
|
+
framework: z.enum(["cypress", "playwright", "webdriverio", "appium"]).optional().describe("Framework do teste. Detectado automaticamente se omitido.")
|
|
1204
|
+
}),
|
|
1205
|
+
outputSchema: z.object({
|
|
1206
|
+
ok: z.boolean(),
|
|
1207
|
+
selectorSugerido: z.string().optional(),
|
|
1208
|
+
codigoCorrigido: z.string().optional(),
|
|
1209
|
+
explicacao: z.string().optional(),
|
|
1210
|
+
error: z.string().optional()
|
|
1211
|
+
})
|
|
1212
|
+
},
|
|
1213
|
+
async ({ testFilePath, errorOutput, framework }) => {
|
|
1214
|
+
const structure = detectProjectStructure();
|
|
1215
|
+
const fw = framework || inferFrameworkFromFile(testFilePath.split("/").pop(), structure);
|
|
1216
|
+
let resolvedOutput = errorOutput;
|
|
1217
|
+
if (!resolvedOutput) {
|
|
1218
|
+
const logPath = path.join(PROJECT_ROOT, ".qa-lab-last-failure.log");
|
|
1219
|
+
if (fs.existsSync(logPath)) {
|
|
1220
|
+
resolvedOutput = fs.readFileSync(logPath, "utf8");
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
if (!resolvedOutput) {
|
|
1224
|
+
return {
|
|
1225
|
+
content: [{ type: "text", text: "Nenhum output de erro. Rode os testes primeiro ou forne\xE7a errorOutput." }],
|
|
1226
|
+
structuredContent: { ok: false, error: "No error output" }
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
if (!/element not found|selector|timeout|locator|cy\.get|page\.locator/i.test(resolvedOutput)) {
|
|
1230
|
+
return {
|
|
1231
|
+
content: [{ type: "text", text: "A falha n\xE3o parece ser de seletor/elemento. Use por_que_falhou ou suggest_fix para outros tipos de falha." }],
|
|
1232
|
+
structuredContent: { ok: false, error: "Not a selector-related failure" }
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
let testCode = "";
|
|
1236
|
+
const fullPath = path.join(PROJECT_ROOT, testFilePath.replace(/^\//, "").replace(/\\/g, "/"));
|
|
1237
|
+
if (fs.existsSync(fullPath)) {
|
|
1238
|
+
try {
|
|
1239
|
+
testCode = fs.readFileSync(fullPath, "utf8");
|
|
1240
|
+
} catch {
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
const GROQ_KEY = process.env.GROQ_API_KEY;
|
|
1244
|
+
const GEMINI_KEY = process.env.GEMINI_API_KEY;
|
|
1245
|
+
const OPENAI_KEY = process.env.OPENAI_API_KEY || process.env.QA_LAB_LLM_API_KEY;
|
|
1246
|
+
if (!GROQ_KEY && !GEMINI_KEY && !OPENAI_KEY) {
|
|
1247
|
+
return {
|
|
1248
|
+
content: [{ type: "text", text: "Configure GROQ_API_KEY, GEMINI_API_KEY ou OPENAI_API_KEY no .env" }],
|
|
1249
|
+
structuredContent: { ok: false, error: "No API key configured" }
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
const provider = GROQ_KEY ? "groq" : GEMINI_KEY ? "gemini" : "openai";
|
|
1253
|
+
const apiKey = GROQ_KEY || GEMINI_KEY || OPENAI_KEY;
|
|
1254
|
+
const baseUrl = provider === "groq" ? "https://api.groq.com/openai/v1" : provider === "gemini" ? "https://generativelanguage.googleapis.com/v1beta" : "https://api.openai.com/v1";
|
|
1255
|
+
const model = provider === "groq" ? "llama-3.3-70b-versatile" : provider === "gemini" ? "gemini-1.5-flash" : "gpt-4o-mini";
|
|
1256
|
+
const fwHints = {
|
|
1257
|
+
cypress: "Cypress: cy.get('[data-testid=...]'), cy.contains(), cy.get('button').filter(':visible')",
|
|
1258
|
+
playwright: `Playwright: page.getByRole(), page.getByTestId(), page.locator('button:has-text("...")')`,
|
|
1259
|
+
webdriverio: "WebdriverIO: $('[data-testid=...]'), $('button=Texto')",
|
|
1260
|
+
appium: "Appium: $('~accessibility-id'), $('//android.view.View')"
|
|
1261
|
+
};
|
|
1262
|
+
const systemPrompt = `Voc\xEA \xE9 um especialista em testes E2E. O teste falhou porque um seletor n\xE3o encontrou o elemento (UI mudou).
|
|
1263
|
+
Analise o erro e o c\xF3digo e responda APENAS em JSON (sem markdown) com as chaves:
|
|
1264
|
+
- selectorSugerido: string (o novo seletor recomendado, mais resiliente)
|
|
1265
|
+
- codigoCorrigido: string (bloco de c\xF3digo completo corrigido, apenas a parte relevante do teste)
|
|
1266
|
+
- explicacao: string (breve explica\xE7\xE3o em portugu\xEAs: por que o antigo falhou e por que o novo \xE9 melhor)
|
|
1267
|
+
|
|
1268
|
+
Priorize nesta ordem: data-testid > role + accessible name > texto vis\xEDvel > estrutura. Evite classes CSS e IDs que mudam.
|
|
1269
|
+
|
|
1270
|
+
Framework: ${fw}. ${fwHints[fw] || ""}`;
|
|
1271
|
+
const userPrompt = `Output do erro:
|
|
1272
|
+
---
|
|
1273
|
+
${resolvedOutput.slice(0, 8e3)}
|
|
1274
|
+
---
|
|
1275
|
+
C\xF3digo do teste:
|
|
1276
|
+
---
|
|
1277
|
+
${testCode ? testCode.slice(0, 6e3) : "N\xE3o dispon\xEDvel"}
|
|
1278
|
+
---`;
|
|
1279
|
+
try {
|
|
1280
|
+
let raw = await callLlmForExplanation(provider, apiKey, baseUrl, model, systemPrompt, userPrompt);
|
|
1281
|
+
raw = raw.replace(/^```(?:json)?\s*/i, "").replace(/\s*```\s*$/i, "").trim();
|
|
1282
|
+
let data = {};
|
|
1283
|
+
try {
|
|
1284
|
+
data = JSON.parse(raw);
|
|
1285
|
+
} catch {
|
|
1286
|
+
data = {
|
|
1287
|
+
selectorSugerido: null,
|
|
1288
|
+
codigoCorrigido: raw.slice(0, 2e3),
|
|
1289
|
+
explicacao: "N\xE3o foi poss\xEDvel parsear. Resposta do LLM acima."
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
const text = [
|
|
1293
|
+
data.explicacao && `## Explica\xE7\xE3o
|
|
1294
|
+
${data.explicacao}`,
|
|
1295
|
+
data.selectorSugerido && `## Seletor sugerido
|
|
1296
|
+
\`${data.selectorSugerido}\``,
|
|
1297
|
+
data.codigoCorrigido && `## C\xF3digo corrigido
|
|
1298
|
+
\`\`\`${fw}
|
|
1299
|
+
${data.codigoCorrigido}
|
|
1300
|
+
\`\`\``
|
|
1301
|
+
].filter(Boolean).join("\n\n");
|
|
1302
|
+
return {
|
|
1303
|
+
content: [{ type: "text", text: text || JSON.stringify(data, null, 2) }],
|
|
1304
|
+
structuredContent: {
|
|
1305
|
+
ok: true,
|
|
1306
|
+
selectorSugerido: data.selectorSugerido,
|
|
1307
|
+
codigoCorrigido: data.codigoCorrigido,
|
|
1308
|
+
explicacao: data.explicacao
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
} catch (err) {
|
|
1312
|
+
return {
|
|
1313
|
+
content: [{ type: "text", text: `Erro ao chamar LLM: ${err.message}` }],
|
|
1314
|
+
structuredContent: { ok: false, error: err.message }
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
);
|
|
1012
1319
|
server.registerTool(
|
|
1013
1320
|
"create_bug_report",
|
|
1014
1321
|
{
|
|
@@ -1060,12 +1367,153 @@ server.registerTool(
|
|
|
1060
1367
|
lines.push("- [ ] Aplicar corre\xE7\xE3o");
|
|
1061
1368
|
lines.push("- [ ] Validar com testes");
|
|
1062
1369
|
const report = lines.join("\n");
|
|
1370
|
+
appendMetricsEvent({ type: "bug_reported", failuresCount: failures.length, title: bugTitle });
|
|
1063
1371
|
return {
|
|
1064
1372
|
content: [{ type: "text", text: report }],
|
|
1065
1373
|
structuredContent: { ok: true, report, title: bugTitle }
|
|
1066
1374
|
};
|
|
1067
1375
|
}
|
|
1068
1376
|
);
|
|
1377
|
+
server.registerTool(
|
|
1378
|
+
"get_business_metrics",
|
|
1379
|
+
{
|
|
1380
|
+
title: "Obter m\xE9tricas de neg\xF3cio",
|
|
1381
|
+
description: "Retorna m\xE9tricas: tempo at\xE9 bug, custo por defeito (tempo estimado), cobertura por fluxo. Requer run_tests executados e opcionalmente qa-lab-flows.json.",
|
|
1382
|
+
inputSchema: z.object({
|
|
1383
|
+
period: z.enum(["7d", "30d", "all"]).optional().describe("Per\xEDodo para analisar. Default: 30d.")
|
|
1384
|
+
}),
|
|
1385
|
+
outputSchema: z.object({
|
|
1386
|
+
ok: z.boolean(),
|
|
1387
|
+
timeToBug: z.object({
|
|
1388
|
+
avgHours: z.number(),
|
|
1389
|
+
lastFailureAt: z.string().optional(),
|
|
1390
|
+
runsWithFailures: z.number()
|
|
1391
|
+
}).optional(),
|
|
1392
|
+
costPerDefect: z.object({
|
|
1393
|
+
avgMinutesPerDefect: z.number(),
|
|
1394
|
+
totalFailures: z.number(),
|
|
1395
|
+
estimatedHoursSpent: z.number()
|
|
1396
|
+
}).optional(),
|
|
1397
|
+
flowCoverage: z.object({
|
|
1398
|
+
totalFlows: z.number(),
|
|
1399
|
+
coveredFlows: z.number(),
|
|
1400
|
+
percent: z.number(),
|
|
1401
|
+
details: z.array(z.object({ flow: z.string(), covered: z.boolean() }))
|
|
1402
|
+
}).optional(),
|
|
1403
|
+
summary: z.string()
|
|
1404
|
+
})
|
|
1405
|
+
},
|
|
1406
|
+
async ({ period = "30d" } = {}) => {
|
|
1407
|
+
const now = Date.now();
|
|
1408
|
+
const msByPeriod = { "7d": 7 * 24 * 60 * 60 * 1e3, "30d": 30 * 24 * 60 * 60 * 1e3, all: Infinity };
|
|
1409
|
+
const cutoff = now - msByPeriod[period];
|
|
1410
|
+
let data = { events: [] };
|
|
1411
|
+
if (fs.existsSync(METRICS_FILE)) {
|
|
1412
|
+
try {
|
|
1413
|
+
data = JSON.parse(fs.readFileSync(METRICS_FILE, "utf8"));
|
|
1414
|
+
} catch {
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
const events = (data.events || []).filter((e) => new Date(e.timestamp).getTime() >= cutoff);
|
|
1418
|
+
const testRuns = events.filter((e) => e.type === "test_run");
|
|
1419
|
+
const failedRuns = testRuns.filter((e) => (e.failed || 0) > 0);
|
|
1420
|
+
const totalFailed = testRuns.reduce((sum, e) => sum + (e.failed || 0), 0);
|
|
1421
|
+
const totalDuration = testRuns.reduce((sum, e) => sum + (e.durationSeconds || 0), 0);
|
|
1422
|
+
let timeToBug = null;
|
|
1423
|
+
if (failedRuns.length > 0) {
|
|
1424
|
+
const lastFailure = failedRuns[failedRuns.length - 1];
|
|
1425
|
+
timeToBug = {
|
|
1426
|
+
avgHours: 0,
|
|
1427
|
+
lastFailureAt: lastFailure.timestamp,
|
|
1428
|
+
runsWithFailures: failedRuns.length
|
|
1429
|
+
};
|
|
1430
|
+
if (failedRuns.length >= 2) {
|
|
1431
|
+
const deltas = [];
|
|
1432
|
+
for (let i = 1; i < failedRuns.length; i++) {
|
|
1433
|
+
const prev = new Date(failedRuns[i - 1].timestamp).getTime();
|
|
1434
|
+
const curr = new Date(failedRuns[i].timestamp).getTime();
|
|
1435
|
+
deltas.push((curr - prev) / (1e3 * 60 * 60));
|
|
1436
|
+
}
|
|
1437
|
+
timeToBug.avgHours = deltas.reduce((a, b) => a + b, 0) / deltas.length;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
let costPerDefect = null;
|
|
1441
|
+
if (totalFailed > 0) {
|
|
1442
|
+
const estimatedMinutesSpent = totalDuration + totalFailed * 5;
|
|
1443
|
+
costPerDefect = {
|
|
1444
|
+
avgMinutesPerDefect: Math.round(estimatedMinutesSpent / totalFailed),
|
|
1445
|
+
totalFailures: totalFailed,
|
|
1446
|
+
estimatedHoursSpent: Math.round(estimatedMinutesSpent / 60 * 10) / 10
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
let flowCoverage = null;
|
|
1450
|
+
if (fs.existsSync(FLOWS_CONFIG_FILE)) {
|
|
1451
|
+
try {
|
|
1452
|
+
const flowsConfig = JSON.parse(fs.readFileSync(FLOWS_CONFIG_FILE, "utf8"));
|
|
1453
|
+
const flows = flowsConfig.flows || [];
|
|
1454
|
+
const structure = detectProjectStructure();
|
|
1455
|
+
const allTestFiles = new Set(collectTestFiles(structure).map((e) => e.path));
|
|
1456
|
+
const details = flows.map((f) => {
|
|
1457
|
+
const testFiles = f.testFiles || [];
|
|
1458
|
+
const covered = testFiles.some((tf) => allTestFiles.has(tf) || allTestFiles.has(tf.replace(/\\/g, "/")));
|
|
1459
|
+
return { flow: f.name || f.id || "?", covered };
|
|
1460
|
+
});
|
|
1461
|
+
flowCoverage = {
|
|
1462
|
+
totalFlows: flows.length,
|
|
1463
|
+
coveredFlows: details.filter((d) => d.covered).length,
|
|
1464
|
+
percent: flows.length ? Math.round(details.filter((d) => d.covered).length / flows.length * 100) : 0,
|
|
1465
|
+
details
|
|
1466
|
+
};
|
|
1467
|
+
} catch {
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
const lines = [
|
|
1471
|
+
"## M\xE9tricas de neg\xF3cio",
|
|
1472
|
+
"",
|
|
1473
|
+
`Per\xEDodo: ${period}`,
|
|
1474
|
+
"",
|
|
1475
|
+
timeToBug ? [
|
|
1476
|
+
"### Tempo at\xE9 bug",
|
|
1477
|
+
`- \xDAltima falha: ${timeToBug.lastFailureAt || "N/A"}`,
|
|
1478
|
+
`- Execu\xE7\xF5es com falha: ${timeToBug.runsWithFailures}`,
|
|
1479
|
+
timeToBug.avgHours > 0 ? `- M\xE9dia entre falhas: ${timeToBug.avgHours.toFixed(1)}h` : ""
|
|
1480
|
+
].filter(Boolean).join("\n") : "",
|
|
1481
|
+
costPerDefect ? [
|
|
1482
|
+
"### Custo por defeito (estimativa)",
|
|
1483
|
+
`- Total de falhas: ${costPerDefect.totalFailures}`,
|
|
1484
|
+
`- Tempo m\xE9dio por defeito: ~${costPerDefect.avgMinutesPerDefect} min`,
|
|
1485
|
+
`- Horas estimadas gastas: ${costPerDefect.estimatedHoursSpent}h`
|
|
1486
|
+
].join("\n") : "",
|
|
1487
|
+
flowCoverage ? [
|
|
1488
|
+
"### Cobertura por fluxo",
|
|
1489
|
+
`- Fluxos cobertos: ${flowCoverage.coveredFlows}/${flowCoverage.totalFlows} (${flowCoverage.percent}%)`,
|
|
1490
|
+
flowCoverage.details.map((d) => ` - ${d.flow}: ${d.covered ? "\u2705" : "\u274C"}`).join("\n")
|
|
1491
|
+
].join("\n") : ""
|
|
1492
|
+
].filter(Boolean).join("\n\n");
|
|
1493
|
+
if (!timeToBug && !costPerDefect && !flowCoverage) {
|
|
1494
|
+
const msg = "Nenhuma m\xE9trica dispon\xEDvel. Rode run_tests para gerar dados. Para cobertura por fluxo, crie qa-lab-flows.json.";
|
|
1495
|
+
return {
|
|
1496
|
+
content: [{ type: "text", text: msg }],
|
|
1497
|
+
structuredContent: { ok: false, summary: msg }
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
const summary = [
|
|
1501
|
+
timeToBug && `${timeToBug.runsWithFailures} execu\xE7\xF5es com falha`,
|
|
1502
|
+
costPerDefect && `${costPerDefect.totalFailures} falhas (~${costPerDefect.avgMinutesPerDefect} min/defeito)`,
|
|
1503
|
+
flowCoverage && `${flowCoverage.coveredFlows}/${flowCoverage.totalFlows} fluxos cobertos`
|
|
1504
|
+
].filter(Boolean).join(" | ");
|
|
1505
|
+
return {
|
|
1506
|
+
content: [{ type: "text", text: lines || summary }],
|
|
1507
|
+
structuredContent: {
|
|
1508
|
+
ok: true,
|
|
1509
|
+
timeToBug,
|
|
1510
|
+
costPerDefect,
|
|
1511
|
+
flowCoverage,
|
|
1512
|
+
summary
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
);
|
|
1069
1517
|
server.registerTool(
|
|
1070
1518
|
"list_test_files",
|
|
1071
1519
|
{
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.js"],"sourcesContent":["#!/usr/bin/env node\n/**\n * MCP Lab Agent - Standalone\n * MCP server genérico para QA automation em qualquer projeto.\n * Detecta automaticamente Cypress, Playwright, Jest, estrutura do projeto, etc.\n */\nimport { config } from \"dotenv\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport { spawn } from \"node:child_process\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\n\nconst PROJECT_ROOT = process.cwd();\nconfig({ path: path.join(PROJECT_ROOT, \".env\") });\n\nconst server = new McpServer({\n name: \"mcp-lab-agent\",\n version: \"1.0.0\",\n});\n\n// ============================================================================\n// DETECÇÃO AUTOMÁTICA DE ESTRUTURA\n// ============================================================================\n\nfunction detectProjectStructure() {\n const structure = {\n hasTests: false,\n testFrameworks: [],\n testDirs: [],\n hasBackend: false,\n backendDir: null,\n hasFrontend: false,\n frontendDir: null,\n hasMobile: false,\n packageJson: null,\n pythonRequirements: null,\n };\n\n // Detectar Node.js/JavaScript/TypeScript\n const pkgPath = path.join(PROJECT_ROOT, \"package.json\");\n if (fs.existsSync(pkgPath)) {\n structure.packageJson = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n const deps = {\n ...structure.packageJson.dependencies,\n ...structure.packageJson.devDependencies,\n };\n\n // Frameworks E2E/UI\n if (deps.cypress) {\n structure.testFrameworks.push(\"cypress\");\n structure.hasTests = true;\n }\n if (deps[\"@playwright/test\"] || deps.playwright) {\n structure.testFrameworks.push(\"playwright\");\n structure.hasTests = true;\n }\n if (deps.webdriverio || deps[\"@wdio/cli\"]) {\n structure.testFrameworks.push(\"webdriverio\");\n structure.hasTests = true;\n }\n\n // Frameworks Unit/Integration\n if (deps.jest) {\n structure.testFrameworks.push(\"jest\");\n structure.hasTests = true;\n }\n if (deps.vitest) {\n structure.testFrameworks.push(\"vitest\");\n structure.hasTests = true;\n }\n if (deps.mocha) {\n structure.testFrameworks.push(\"mocha\");\n structure.hasTests = true;\n }\n if (deps.jasmine) {\n structure.testFrameworks.push(\"jasmine\");\n structure.hasTests = true;\n }\n\n // Frameworks Mobile\n if (deps.appium || deps[\"appium-webdriverio\"]) {\n structure.testFrameworks.push(\"appium\");\n structure.hasTests = true;\n structure.hasMobile = true;\n }\n if (deps.detox) {\n structure.testFrameworks.push(\"detox\");\n structure.hasTests = true;\n structure.hasMobile = true;\n }\n\n // API Testing\n if (deps.supertest) {\n structure.testFrameworks.push(\"supertest\");\n structure.hasTests = true;\n }\n if (deps[\"@pactum/pactum\"] || deps.pactum) {\n structure.testFrameworks.push(\"pactum\");\n structure.hasTests = true;\n }\n\n // Backend detection\n if (deps.express || deps.fastify || deps[\"@nestjs/core\"] || deps.koa) {\n structure.hasBackend = true;\n }\n \n // Frontend detection\n if (deps.next || deps.react || deps.vue || deps.svelte || deps.angular) {\n structure.hasFrontend = true;\n }\n }\n\n // Detectar Python (Robot Framework, pytest, etc.)\n const requirementsPath = path.join(PROJECT_ROOT, \"requirements.txt\");\n if (fs.existsSync(requirementsPath)) {\n const requirements = fs.readFileSync(requirementsPath, \"utf8\");\n structure.pythonRequirements = requirements;\n\n if (/robotframework/i.test(requirements)) {\n structure.testFrameworks.push(\"robot\");\n structure.hasTests = true;\n }\n if (/pytest/i.test(requirements)) {\n structure.testFrameworks.push(\"pytest\");\n structure.hasTests = true;\n }\n if (/behave/i.test(requirements)) {\n structure.testFrameworks.push(\"behave\");\n structure.hasTests = true;\n }\n if (/requests/i.test(requirements)) {\n structure.hasBackend = true;\n }\n }\n\n // Detectar pastas de teste (genérico)\n const commonTestDirs = [\n \"tests\", \"test\", \"e2e\", \"cypress\", \"playwright\", \"__tests__\",\n \"specs\", \"spec\", \"integration\", \"unit\", \"functional\", \"robot\",\n \"features\", \"scenarios\", \"mobile\", \"api\"\n ];\n for (const dir of commonTestDirs) {\n const fullPath = path.join(PROJECT_ROOT, dir);\n if (fs.existsSync(fullPath)) {\n structure.testDirs.push(dir);\n }\n }\n\n // Detectar backend\n const commonBackendDirs = [\"backend\", \"server\", \"api\", \"src\"];\n for (const dir of commonBackendDirs) {\n const fullPath = path.join(PROJECT_ROOT, dir);\n if (fs.existsSync(fullPath) && !structure.backendDir) {\n const hasServerFile = fs.existsSync(path.join(fullPath, \"server.js\")) ||\n fs.existsSync(path.join(fullPath, \"index.js\")) ||\n fs.existsSync(path.join(fullPath, \"app.js\"));\n if (hasServerFile) {\n structure.backendDir = dir;\n }\n }\n }\n\n // Detectar frontend\n const commonFrontendDirs = [\"frontend\", \"client\", \"web\", \"app\", \"src\"];\n for (const dir of commonFrontendDirs) {\n const fullPath = path.join(PROJECT_ROOT, dir);\n if (fs.existsSync(fullPath) && !structure.frontendDir) {\n const hasAppFile = fs.existsSync(path.join(fullPath, \"App.js\")) ||\n fs.existsSync(path.join(fullPath, \"App.tsx\")) ||\n fs.existsSync(path.join(fullPath, \"index.html\"));\n if (hasAppFile) {\n structure.frontendDir = dir;\n }\n }\n }\n\n return structure;\n}\n\n// ============================================================================\n// PADRÕES UNIVERSAIS DE ARQUIVOS DE TESTE (qualquer framework)\n// ============================================================================\n\nconst UNIVERSAL_TEST_PATTERNS = [\n /\\.(cy|spec|test)\\.(js|ts|jsx|tsx)$/i,\n /\\.robot$/i,\n /\\.feature$/i,\n /^(test_.*|.*_test)\\.py$/i,\n /\\.steps?\\.(js|ts|py)$/i,\n /\\.e2e\\.(js|ts)$/i,\n /\\.it\\.(js|ts)$/i,\n];\n\nfunction isTestFile(name) {\n return UNIVERSAL_TEST_PATTERNS.some((re) => re.test(name));\n}\n\nfunction collectTestFiles(structure, options = {}) {\n const { pattern, framework, maxContentFiles = 0 } = options;\n const results = [];\n\n for (const dir of structure.testDirs) {\n const fullPath = path.join(PROJECT_ROOT, dir);\n const walk = (p, base = \"\") => {\n if (!fs.existsSync(p)) return;\n const entries = fs.readdirSync(p, { withFileTypes: true });\n for (const e of entries) {\n const rel = base ? `${base}/${e.name}` : e.name;\n if (e.isDirectory()) {\n walk(path.join(p, e.name), rel);\n } else if (e.isFile() && isTestFile(e.name)) {\n const filePath = `${dir}/${rel}`;\n if (pattern && !filePath.toLowerCase().includes(pattern.toLowerCase())) continue;\n const inferredFw = inferFrameworkFromFile(e.name, structure);\n if (framework && framework !== \"all\" && inferredFw !== framework && !matchesFramework(inferredFw, framework)) continue;\n const entry = { path: filePath, inferredFramework: inferredFw };\n if (maxContentFiles > 0 && results.length < maxContentFiles) {\n try {\n entry.content = fs.readFileSync(path.join(PROJECT_ROOT, filePath), \"utf8\");\n } catch {}\n }\n results.push(entry);\n }\n }\n };\n walk(fullPath);\n }\n return results;\n}\n\nfunction inferFrameworkFromFile(name, structure = {}) {\n if (/\\.cy\\.(js|ts|jsx|tsx)/i.test(name)) return \"cypress\";\n if (/\\.spec\\.(js|ts|jsx|tsx)/i.test(name)) {\n if (structure?.testFrameworks?.includes(\"webdriverio\")) return \"webdriverio\";\n if (structure?.testFrameworks?.includes(\"appium\")) return \"appium\";\n return \"playwright\";\n }\n if (/\\.test\\.(js|ts|jsx|tsx)/i.test(name)) return structure?.testFrameworks?.includes(\"vitest\") ? \"vitest\" : \"jest\";\n if (/\\.robot$/i.test(name)) return \"robot\";\n if (/\\.feature$/i.test(name)) return \"behave\";\n if (/\\.(py|steps?\\.py)$/i.test(name) || /^(test_.*|.*_test)\\.py$/i.test(name)) return \"pytest\";\n if (/\\.e2e\\.(js|ts)/i.test(name)) return \"playwright\";\n return \"unknown\";\n}\n\nfunction matchesFramework(inferred, requested) {\n const aliases = { spec: [\"playwright\", \"webdriverio\", \"appium\"] };\n if (inferred === requested) return true;\n return aliases[inferred]?.includes(requested);\n}\n\n// ============================================================================\n// FERRAMENTAS GENÉRICAS\n// ============================================================================\n\nserver.registerTool(\n \"read_file\",\n {\n title: \"Ler qualquer arquivo\",\n description: \"Lê o conteúdo de QUALQUER arquivo do projeto por caminho. Use para specs, page objects, componentes, código fonte - qualquer formato.\",\n inputSchema: z.object({\n path: z.string().describe(\"Caminho relativo ao projeto (ex: cypress/e2e/login.cy.js, src/pages/Login.tsx, tests/login.robot).\"),\n encoding: z.enum([\"utf8\", \"utf-8\"]).optional().describe(\"Encoding. Default: utf8\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n content: z.string().optional(),\n error: z.string().optional(),\n }),\n },\n async ({ path: filePath, encoding = \"utf8\" }) => {\n const normalized = filePath.replace(/^\\//, \"\").replace(/\\\\/g, \"/\");\n const fullPath = path.join(PROJECT_ROOT, normalized);\n\n if (!fullPath.startsWith(PROJECT_ROOT)) {\n return {\n content: [{ type: \"text\", text: \"Caminho fora do projeto.\" }],\n structuredContent: { ok: false, error: \"Path outside project\" },\n };\n }\n if (!fs.existsSync(fullPath)) {\n return {\n content: [{ type: \"text\", text: `Arquivo não encontrado: ${normalized}` }],\n structuredContent: { ok: false, error: \"File not found\" },\n };\n }\n const stat = fs.statSync(fullPath);\n if (stat.isDirectory()) {\n return {\n content: [{ type: \"text\", text: \"É um diretório. Use um caminho de arquivo.\" }],\n structuredContent: { ok: false, error: \"Is directory\" },\n };\n }\n\n try {\n const content = fs.readFileSync(fullPath, encoding);\n return {\n content: [{ type: \"text\", text: content }],\n structuredContent: { ok: true, content },\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Erro ao ler: ${err.message}` }],\n structuredContent: { ok: false, error: err.message },\n };\n }\n }\n);\n\nserver.registerTool(\n \"detect_project\",\n {\n title: \"Detectar estrutura do projeto\",\n description: \"Analisa o projeto e identifica frameworks de teste, pastas, backend, frontend.\",\n inputSchema: z.object({}),\n outputSchema: z.object({\n ok: z.boolean(),\n structure: z.object({\n hasTests: z.boolean(),\n testFrameworks: z.array(z.string()),\n testDirs: z.array(z.string()),\n hasBackend: z.boolean(),\n backendDir: z.string().nullable(),\n hasFrontend: z.boolean(),\n frontendDir: z.string().nullable(),\n }),\n }),\n },\n async () => {\n const structure = detectProjectStructure();\n const summary = [\n `Frameworks de teste: ${structure.testFrameworks.join(\", \") || \"nenhum\"}`,\n `Pastas de teste: ${structure.testDirs.join(\", \") || \"nenhuma\"}`,\n `Backend: ${structure.backendDir || \"não detectado\"}`,\n `Frontend: ${structure.frontendDir || \"não detectado\"}`,\n ].join(\"\\n\");\n\n return {\n content: [{ type: \"text\", text: summary }],\n structuredContent: { ok: true, structure },\n };\n }\n);\n\nserver.registerTool(\n \"run_tests\",\n {\n title: \"Executar testes\",\n description: \"Roda testes do projeto. Suporta: Cypress, Playwright, WebdriverIO, Jest, Vitest, Mocha, Appium, Detox, Robot Framework, pytest, e mais. Detecta automaticamente.\",\n inputSchema: z.object({\n framework: z.enum([\n \"cypress\", \"playwright\", \"webdriverio\", \"jest\", \"vitest\", \"mocha\", \n \"appium\", \"detox\", \"robot\", \"pytest\", \"supertest\", \"pactum\", \"npm\"\n ]).optional().describe(\"Framework específico ou 'npm' para npm test.\"),\n spec: z.string().optional().describe(\"Caminho do spec (ex: cypress/e2e/test.cy.js).\"),\n suite: z.string().optional().describe(\"Suite ou pattern (ex: e2e, api).\"),\n explainOnFailure: z.boolean().optional().describe(\"Se true, quando falhar gera automaticamente: O que aconteceu, Por que falhou, O que fazer, Sugestão de correção. Requer API key.\"),\n }),\n outputSchema: z.object({\n status: z.enum([\"passed\", \"failed\", \"not_found\"]),\n message: z.string(),\n exitCode: z.number(),\n runOutput: z.string().optional(),\n }),\n },\n async ({ framework, spec, suite, explainOnFailure }) => {\n const structure = detectProjectStructure();\n \n if (!structure.hasTests) {\n return {\n content: [{ type: \"text\", text: \"Nenhum framework de teste detectado no projeto.\" }],\n structuredContent: {\n status: \"not_found\",\n message: \"No test framework found\",\n exitCode: 1,\n },\n };\n }\n\n let selectedFramework = framework;\n if (!selectedFramework && structure.testFrameworks.length > 0) {\n selectedFramework = structure.testFrameworks[0];\n }\n\n let cmd, args, cwd;\n\n // E2E/UI Frameworks\n if (selectedFramework === \"cypress\") {\n cmd = \"npx\";\n args = spec ? [\"cypress\", \"run\", \"--spec\", spec] : [\"cypress\", \"run\"];\n cwd = structure.testDirs.includes(\"cypress\") \n ? path.join(PROJECT_ROOT, \"cypress\")\n : structure.testDirs[0] \n ? path.join(PROJECT_ROOT, structure.testDirs[0])\n : PROJECT_ROOT;\n } else if (selectedFramework === \"playwright\") {\n cmd = \"npx\";\n args = spec ? [\"playwright\", \"test\", spec] : [\"playwright\", \"test\"];\n cwd = structure.testDirs.includes(\"playwright\")\n ? path.join(PROJECT_ROOT, \"playwright\")\n : structure.testDirs[0]\n ? path.join(PROJECT_ROOT, structure.testDirs[0])\n : PROJECT_ROOT;\n } else if (selectedFramework === \"webdriverio\") {\n cmd = \"npx\";\n args = spec ? [\"wdio\", \"run\", spec] : [\"wdio\", \"run\"];\n cwd = PROJECT_ROOT;\n \n // Unit/Integration Frameworks\n } else if (selectedFramework === \"jest\") {\n cmd = \"npx\";\n args = [\"jest\"];\n if (spec) args.push(spec);\n cwd = PROJECT_ROOT;\n } else if (selectedFramework === \"vitest\") {\n cmd = \"npx\";\n args = [\"vitest\", \"run\"];\n if (spec) args.push(spec);\n cwd = PROJECT_ROOT;\n } else if (selectedFramework === \"mocha\") {\n cmd = \"npx\";\n args = spec ? [\"mocha\", spec] : [\"mocha\"];\n cwd = PROJECT_ROOT;\n \n // Mobile Frameworks\n } else if (selectedFramework === \"appium\") {\n cmd = \"npx\";\n args = spec ? [\"wdio\", \"run\", spec] : [\"wdio\", \"run\"];\n cwd = PROJECT_ROOT;\n } else if (selectedFramework === \"detox\") {\n cmd = \"npx\";\n args = [\"detox\", \"test\"];\n if (spec) args.push(spec);\n cwd = PROJECT_ROOT;\n \n // Python Frameworks\n } else if (selectedFramework === \"robot\") {\n cmd = \"robot\";\n args = spec ? [spec] : [structure.testDirs[0] || \"tests\"];\n cwd = PROJECT_ROOT;\n } else if (selectedFramework === \"pytest\") {\n cmd = \"pytest\";\n args = spec ? [spec] : [];\n cwd = PROJECT_ROOT;\n \n // API Testing\n } else if (selectedFramework === \"supertest\" || selectedFramework === \"pactum\") {\n cmd = \"npm\";\n args = [\"test\"];\n cwd = PROJECT_ROOT;\n \n // Fallback\n } else {\n cmd = \"npm\";\n args = [\"test\"];\n cwd = PROJECT_ROOT;\n }\n\n return new Promise((resolve) => {\n const child = spawn(cmd, args, {\n cwd,\n stdio: [\"inherit\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n env: { ...process.env },\n });\n\n let stdout = \"\";\n let stderr = \"\";\n if (child.stdout) {\n child.stdout.on(\"data\", (d) => {\n const s = d.toString();\n stdout += s;\n process.stdout.write(s);\n });\n }\n if (child.stderr) {\n child.stderr.on(\"data\", (d) => {\n const s = d.toString();\n stderr += s;\n process.stderr.write(s);\n });\n }\n\n child.on(\"close\", (code) => {\n const runOutput = [stdout, stderr].filter(Boolean).join(\"\\n\").trim();\n const passed = code === 0;\n if (!passed && runOutput) {\n try {\n fs.writeFileSync(path.join(PROJECT_ROOT, \".qa-lab-last-failure.log\"), runOutput, \"utf8\");\n } catch {}\n }\n resolve({\n content: [{ type: \"text\", text: passed ? \"Testes executados com sucesso.\" : \"Falha na execução dos testes.\" }],\n structuredContent: {\n status: passed ? \"passed\" : \"failed\",\n message: passed ? \"Tests passed\" : \"Tests failed\",\n exitCode: code ?? 1,\n runOutput: !passed ? runOutput : undefined,\n },\n });\n });\n });\n }\n);\n\nserver.registerTool(\n \"read_project\",\n {\n title: \"Ler estrutura do projeto\",\n description: \"Lê package.json, specs existentes (qualquer framework: Cypress, Playwright, WDIO, Robot, pytest, etc) e retorna contexto. Use includeContent para trazer código de exemplos.\",\n inputSchema: z.object({\n includeContent: z.boolean().optional().describe(\"Se true, inclui conteúdo dos primeiros 3 arquivos de teste como referência. Default: false.\"),\n maxFiles: z.number().optional().describe(\"Máximo de arquivos cujo conteúdo será lido. Default: 3.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n summary: z.string(),\n packageJson: z.object({}).passthrough().optional(),\n testFiles: z.array(z.string()).optional(),\n testFilesWithContent: z.array(z.object({ path: z.string(), content: z.string() })).optional(),\n }),\n },\n async ({ includeContent = false, maxFiles = 3 } = {}) => {\n const structure = detectProjectStructure();\n const collected = collectTestFiles(structure, {\n maxContentFiles: includeContent ? maxFiles : 0,\n });\n\n const testFiles = collected.map((e) => e.path);\n const testFilesWithContent = includeContent\n ? collected.filter((e) => e.content).map((e) => ({ path: e.path, content: e.content }))\n : undefined;\n\n const summary = [\n `Frameworks: ${structure.testFrameworks.join(\", \") || \"nenhum\"}`,\n `Arquivos de teste: ${testFiles.length} (qualquer framework)`,\n `Backend: ${structure.backendDir || \"não detectado\"}`,\n `Frontend: ${structure.frontendDir || \"não detectado\"}`,\n includeContent && testFilesWithContent?.length\n ? `Conteúdo incluído: ${testFilesWithContent.length} arquivo(s) como referência`\n : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n return {\n content: [{ type: \"text\", text: summary }],\n structuredContent: {\n ok: true,\n summary,\n packageJson: structure.packageJson,\n testFiles: testFiles.slice(0, 100),\n testFilesWithContent,\n structure,\n },\n };\n }\n);\n\nserver.registerTool(\n \"generate_tests\",\n {\n title: \"Gerar ou traduzir testes com LLM\",\n description: \"Gera spec em QUALQUER framework. Aceita referência de outro framework: leia com read_file e passe em referenceCode. Traduz automaticamente (ex: Robot→Playwright, Cypress→WDIO).\",\n inputSchema: z.object({\n context: z.string().describe(\"Contexto do projeto (read_project) ou descrição.\"),\n request: z.string().describe(\"O que testar (ex: 'logout flow', 'teste de login') ou 'traduzir o teste abaixo'.\"),\n framework: z.enum([\n \"cypress\", \"playwright\", \"webdriverio\", \"jest\", \"vitest\", \"mocha\",\n \"appium\", \"robot\", \"pytest\", \"supertest\", \"behave\", \"detox\"\n ]).optional().describe(\"Framework alvo (detectado do projeto se omitido).\"),\n referenceCode: z.string().optional().describe(\"Código de referência em QUALQUER framework (Cypress, Robot, WDIO, etc). O LLM traduz/adapta para o framework alvo.\"),\n referencePaths: z.array(z.string()).optional().describe(\"Caminhos de arquivos para ler como referência. O agente lê e usa como padrão.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n specContent: z.string().optional(),\n suggestedFileName: z.string().optional(),\n error: z.string().optional(),\n }),\n },\n async ({ context, request, framework, referenceCode, referencePaths }) => {\n const structure = detectProjectStructure();\n const fw = framework || structure.testFrameworks[0] || \"cypress\";\n\n let referenceBlock = \"\";\n if (referenceCode) referenceBlock += `\\n\\n--- CÓDIGO DE REFERÊNCIA (use como padrão, traduza/adapte para ${fw}) ---\\n${referenceCode.slice(0, 8000)}`;\n if (referencePaths?.length) {\n for (const p of referencePaths.slice(0, 5)) {\n const full = path.join(PROJECT_ROOT, p.replace(/^\\//, \"\").replace(/\\\\/g, \"/\"));\n if (fs.existsSync(full)) {\n try {\n const content = fs.readFileSync(full, \"utf8\");\n referenceBlock += `\\n\\n--- Arquivo: ${p} ---\\n${content.slice(0, 6000)}`;\n } catch {}\n }\n }\n }\n\n const GROQ_KEY = process.env.GROQ_API_KEY;\n const GEMINI_KEY = process.env.GEMINI_API_KEY;\n const OPENAI_KEY = process.env.OPENAI_API_KEY || process.env.QA_LAB_LLM_API_KEY;\n\n if (!GROQ_KEY && !GEMINI_KEY && !OPENAI_KEY) {\n return {\n content: [{ type: \"text\", text: \"Configure GROQ_API_KEY, GEMINI_API_KEY ou OPENAI_API_KEY no .env\" }],\n structuredContent: { ok: false, error: \"No API key configured\" },\n };\n }\n\n const provider = GROQ_KEY ? \"groq\" : GEMINI_KEY ? \"gemini\" : \"openai\";\n const apiKey = GROQ_KEY || GEMINI_KEY || OPENAI_KEY;\n const baseUrl = provider === \"groq\" \n ? \"https://api.groq.com/openai/v1\"\n : provider === \"gemini\"\n ? \"https://generativelanguage.googleapis.com/v1beta\"\n : \"https://api.openai.com/v1\";\n const model = provider === \"groq\"\n ? \"llama-3.3-70b-versatile\"\n : provider === \"gemini\"\n ? \"gemini-1.5-flash\"\n : \"gpt-4o-mini\";\n\n const hasReference = Boolean(referenceBlock?.trim());\n const systemPrompt = hasReference\n ? `Você é um engenheiro de QA. TRADUZA/ADAPTE o código de referência para o framework ${fw}.\nO código de referência pode estar em QUALQUER framework (Cypress, Robot, Playwright, WDIO, Appium, pytest, etc).\n- Mantenha a MESMA lógica e fluxo de teste\n- Traduza seletores, comandos e asserções para ${fw}\n- Use Page Objects se o projeto já usa\n- Retorne SOMENTE o código, sem markdown`\n : `Você é um engenheiro de QA especializado em ${fw}. Gere APENAS o código do spec, sem explicações.\nFramework: ${fw}\nRegras:\n- Cypress: cy.request(), cy.visit(), cy.get()\n- Playwright: test(), test.describe(), page.goto(), page.locator()\n- WebdriverIO/Appium: describe(), it(), $(), browser.$\n- Jest/Vitest: describe(), test(), expect()\n- Robot: Keywords, [Tags], Steps\n- pytest: def test_*, assert, fixtures\n- Código limpo. Retorne SOMENTE o código, sem markdown`;\n\n const userPrompt = `Contexto do projeto:\n${context.slice(0, 5000)}\n\nGere um teste para: ${request}\nFramework alvo: ${fw}${referenceBlock}`;\n\n try {\n let specContent;\n if (provider === \"gemini\") {\n const url = `${baseUrl}/models/${model}:generateContent?key=${apiKey}`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n systemInstruction: { parts: [{ text: systemPrompt }] },\n contents: [{ parts: [{ text: userPrompt }] }],\n generationConfig: { temperature: 0.3, maxOutputTokens: 4096 },\n }),\n });\n const data = await res.json();\n specContent = data.candidates?.[0]?.content?.parts?.[0]?.text || \"\";\n } else {\n const res = await fetch(`${baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: userPrompt },\n ],\n temperature: 0.3,\n max_tokens: 4096,\n }),\n });\n const data = await res.json();\n specContent = data.choices?.[0]?.message?.content || \"\";\n }\n\n specContent = specContent.replace(/^```(?:js|javascript)?\\n?/i, \"\").replace(/\\n?```\\s*$/i, \"\").trim();\n const fileName = request.toLowerCase().replace(/\\s+/g, \"-\").replace(/[^a-z0-9-]/g, \"\").slice(0, 40);\n\n return {\n content: [{ type: \"text\", text: `Spec gerado (${specContent.length} chars). Use write_test para gravar.` }],\n structuredContent: {\n ok: true,\n specContent,\n suggestedFileName: fileName,\n },\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Erro ao gerar: ${err.message}` }],\n structuredContent: { ok: false, error: err.message },\n };\n }\n }\n);\n\nfunction getExtensionAndBaseDir(fw, structure) {\n const extMap = {\n cypress: \".cy.js\",\n playwright: \".spec.js\",\n jest: \".test.js\",\n vitest: \".test.js\",\n mocha: \".test.js\",\n webdriverio: \".spec.js\",\n appium: \".spec.js\",\n detox: \".e2e.js\",\n robot: \".robot\",\n pytest: \"_test.py\",\n behave: \".feature\",\n supertest: \".test.js\",\n pactum: \".test.js\",\n };\n const ext = extMap[fw] || \".spec.js\";\n\n const baseMap = {\n cypress: structure.testDirs.includes(\"cypress\") ? \"cypress\" : structure.testDirs[0] || \"tests\",\n playwright: structure.testDirs.includes(\"playwright\") ? \"playwright\" : structure.testDirs[0] || \"tests\",\n webdriverio: structure.testDirs.includes(\"specs\") ? \"specs\" : structure.testDirs[0] || \"tests\",\n appium: structure.testDirs.includes(\"specs\") ? \"specs\" : structure.testDirs[0] || \"tests\",\n robot: structure.testDirs.includes(\"robot\") ? \"robot\" : structure.testDirs[0] || \"tests\",\n behave: structure.testDirs.includes(\"features\") ? \"features\" : structure.testDirs[0] || \"tests\",\n };\n const baseDir = path.join(PROJECT_ROOT, baseMap[fw] || structure.testDirs[0] || \"tests\");\n return { ext, baseDir };\n}\n\nserver.registerTool(\n \"write_test\",\n {\n title: \"Escrever arquivo de teste\",\n description: \"Grava spec no disco. Suporta QUALQUER framework (Cypress, Playwright, WDIO, Appium, Robot, pytest, etc.). Detecta automaticamente pasta e extensão.\",\n inputSchema: z.object({\n name: z.string().describe(\"Nome do arquivo (ex: login-test, logout_spec).\"),\n content: z.string().describe(\"Conteúdo do spec.\"),\n framework: z.enum([\n \"cypress\", \"playwright\", \"jest\", \"vitest\", \"mocha\", \"webdriverio\",\n \"appium\", \"detox\", \"robot\", \"pytest\", \"behave\", \"supertest\"\n ]).optional().describe(\"Framework (detectado automaticamente se omitido).\"),\n subdir: z.string().optional().describe(\"Subpasta (ex: e2e, api). Default: raiz da pasta de testes.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n path: z.string().optional(),\n error: z.string().optional(),\n }),\n },\n async ({ name, content, framework, subdir }) => {\n const structure = detectProjectStructure();\n const fw = framework || structure.testFrameworks[0];\n\n if (!fw) {\n return {\n content: [{ type: \"text\", text: \"Nenhum framework de teste detectado.\" }],\n structuredContent: { ok: false, error: \"No test framework\" },\n };\n }\n\n const { ext, baseDir } = getExtensionAndBaseDir(fw, structure);\n const safeName = name\n .replace(/[^a-z0-9-_]/gi, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/_+/g, \"_\")\n .replace(/\\.(cy|spec|test|robot|feature|py)\\.?(js|ts|py)?$/i, \"\")\n .replace(/^[-_]+|[-_]+$/g, \"\");\n const fileName = ext.startsWith(\"_\") ? `${safeName}${ext}` : `${safeName}${ext}`;\n\n const targetDir = subdir ? path.join(baseDir, subdir) : baseDir;\n const filePath = path.join(targetDir, fileName);\n\n try {\n if (!fs.existsSync(targetDir)) {\n fs.mkdirSync(targetDir, { recursive: true });\n }\n fs.writeFileSync(filePath, content, \"utf8\");\n return {\n content: [{ type: \"text\", text: `Arquivo gravado: ${filePath}` }],\n structuredContent: { ok: true, path: filePath },\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Erro ao gravar: ${err.message}` }],\n structuredContent: { ok: false, error: err.message },\n };\n }\n }\n);\n\nserver.registerTool(\n \"analyze_failures\",\n {\n title: \"Analisar falhas de testes\",\n description: \"Recebe output de testes e extrai falhas estruturadas.\",\n inputSchema: z.object({\n runOutput: z.string().describe(\"Output do teste (stdout/stderr).\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n summary: z.string(),\n failures: z.array(z.object({\n test: z.string().optional(),\n message: z.string().optional(),\n stack: z.string().optional(),\n })).optional(),\n }),\n },\n async ({ runOutput }) => {\n const failures = [];\n const lines = runOutput.split(\"\\n\");\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (/fail|error|assertion/i.test(line)) {\n failures.push({\n test: lines[i - 1] || \"unknown\",\n message: line.trim(),\n stack: lines.slice(i, i + 5).join(\"\\n\"),\n });\n }\n }\n\n const summary = failures.length\n ? `${failures.length} falha(s) detectada(s).`\n : \"Nenhuma falha detectada.\";\n\n return {\n content: [{ type: \"text\", text: summary }],\n structuredContent: { ok: true, summary, failures: failures.length ? failures : undefined },\n };\n }\n);\n\n// ============================================================================\n// POR QUE FALHOU? - Explicação de falhas para juniores (escalável)\n// ============================================================================\n\nfunction formatFailureExplanation(data) {\n const lines = [\n \"## O que aconteceu\",\n \"\",\n data.oQueAconteceu || \"\",\n \"\",\n \"## Por que provavelmente falhou\",\n \"\",\n ...(Array.isArray(data.porQueProvavelmenteFalhou)\n ? data.porQueProvavelmenteFalhou.map((s) => `• ${s}`)\n : [data.porQueProvavelmenteFalhou || \"\"]),\n \"\",\n \"## O que fazer agora\",\n \"\",\n ...(Array.isArray(data.oQueFazerAgora)\n ? data.oQueFazerAgora.map((s, i) => `${i + 1}. ${s}`)\n : [data.oQueFazerAgora || \"\"]),\n ];\n if (data.sugestaoCorrecao) {\n lines.push(\"\", \"## Sugestão de correção\", \"\", \"```\" + (data.framework || \"js\"), data.sugestaoCorrecao, \"```\");\n }\n if (data.conceito) {\n lines.push(\"\", \"## Conceito\", \"\", data.conceito);\n }\n return lines.filter(Boolean).join(\"\\n\");\n}\n\nasync function callLlmForExplanation(provider, apiKey, baseUrl, model, systemPrompt, userPrompt) {\n if (provider === \"gemini\") {\n const url = `${baseUrl}/models/${model}:generateContent?key=${apiKey}`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n contents: [{ parts: [{ text: systemPrompt + \"\\n\\n\" + userPrompt }] }],\n generationConfig: { temperature: 0.2, maxOutputTokens: 4096 },\n }),\n });\n const data = await res.json();\n return data.candidates?.[0]?.content?.parts?.[0]?.text || \"\";\n }\n const res = await fetch(`${baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: userPrompt },\n ],\n temperature: 0.2,\n max_tokens: 4096,\n }),\n });\n const data = await res.json();\n return data.choices?.[0]?.message?.content || \"\";\n}\n\n/** Gera explicação de falha (o que aconteceu, por que, o que fazer, sugestão). Usado por por_que_falhou e run_tests_and_explain. */\nasync function generateFailureExplanation(resolvedOutput, testFilePath = null) {\n const structure = detectProjectStructure();\n const fw = structure.testFrameworks[0] || \"unknown\";\n\n let testCode = \"\";\n if (testFilePath) {\n const normalized = testFilePath.replace(/^\\//, \"\").replace(/\\\\/g, \"/\");\n const fullPath = path.join(PROJECT_ROOT, normalized);\n if (fs.existsSync(fullPath) && !fs.statSync(fullPath).isDirectory()) {\n try {\n testCode = fs.readFileSync(fullPath, \"utf8\");\n } catch {}\n }\n }\n\n const GROQ_KEY = process.env.GROQ_API_KEY;\n const GEMINI_KEY = process.env.GEMINI_API_KEY;\n const OPENAI_KEY = process.env.OPENAI_API_KEY || process.env.QA_LAB_LLM_API_KEY;\n if (!GROQ_KEY && !GEMINI_KEY && !OPENAI_KEY) {\n return { ok: false, error: \"No API key\", formattedText: null };\n }\n\n const provider = GROQ_KEY ? \"groq\" : GEMINI_KEY ? \"gemini\" : \"openai\";\n const apiKey = GROQ_KEY || GEMINI_KEY || OPENAI_KEY;\n const baseUrl = provider === \"groq\"\n ? \"https://api.groq.com/openai/v1\"\n : provider === \"gemini\"\n ? \"https://generativelanguage.googleapis.com/v1beta\"\n : \"https://api.openai.com/v1\";\n const model = provider === \"groq\"\n ? \"llama-3.3-70b-versatile\"\n : provider === \"gemini\"\n ? \"gemini-1.5-flash\"\n : \"gpt-4o-mini\";\n\n const fwHints = {\n webdriverio: \"WebdriverIO (describe/it, $, browser.$)\",\n appium: \"Appium/WebdriverIO (mobile, $, browser.$)\",\n playwright: \"Playwright (test, page, locator)\",\n cypress: \"Cypress (cy.get, cy.click)\",\n jest: \"Jest (describe, test, expect)\",\n vitest: \"Vitest (describe, test, expect)\",\n robot: \"Robot Framework\",\n pytest: \"pytest\",\n };\n\n const systemPrompt = `Você é um mentor de QA. Analise o output de falha e responda em JSON (apenas o JSON, sem markdown) com as chaves:\n- oQueAconteceu: string (explicação em português do que aconteceu, simples)\n- porQueProvavelmenteFalhou: array de strings (lista de possíveis causas, uma por item)\n- oQueFazerAgora: array de strings (passos numerados do que fazer)\n- sugestaoCorrecao: string ou null (código de correção se aplicável, no formato do framework)\n- conceito: string ou null (ex: \"Flaky test = teste intermitente. Geralmente por timing ou seletores frágeis.\")\n- framework: string (framework do projeto)\n\nFramework do projeto: ${fw}. ${fwHints[fw] || \"\"}\nResponda APENAS com o JSON válido, sem texto antes ou depois.`;\n\n const userPrompt = `Output do terminal/log (teste falhou):\n---\n${resolvedOutput.slice(0, 12000)}\n---\n${testCode ? `\\nCódigo do teste que falhou:\\n---\\n${testCode.slice(0, 6000)}\\n---` : \"\"}`;\n\n try {\n let raw = await callLlmForExplanation(provider, apiKey, baseUrl, model, systemPrompt, userPrompt);\n raw = raw.replace(/^```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\").trim();\n let data = {};\n try {\n data = JSON.parse(raw);\n } catch {\n data = {\n oQueAconteceu: raw.slice(0, 500) || \"Não foi possível parsear a resposta.\",\n porQueProvavelmenteFalhou: [],\n oQueFazerAgora: [],\n sugestaoCorrecao: null,\n conceito: null,\n framework: fw,\n };\n }\n data.framework = data.framework || fw;\n const formattedText = formatFailureExplanation(data);\n return { ok: true, formattedText, structuredContent: data };\n } catch (err) {\n return { ok: false, error: err.message, formattedText: null };\n }\n}\n\nserver.registerTool(\n \"por_que_falhou\",\n {\n title: \"Por que falhou? Explicação para juniores\",\n description: \"Traduz stack trace em explicação humana. Recebe output do terminal/log, lê o projeto e o teste (se path dado), e retorna: O que aconteceu, Por que falhou, O que fazer, Sugestão de correção, Conceito. Escalável e procedural.\",\n inputSchema: z.object({\n errorOutput: z.string().optional().describe(\"Output do terminal quando o teste falhou. Se vazio, lê automaticamente de .qa-lab-last-failure.log (capturado pelo run_tests). Cole aqui ou deixe vazio para usar última falha.\"),\n testFilePath: z.string().optional().describe(\"Caminho do arquivo de teste que falhou (ex: specs/login.spec.js). Se informado, o agente lê o código e dá sugestão mais precisa.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n oQueAconteceu: z.string().optional(),\n porQueProvavelmenteFalhou: z.array(z.string()).optional(),\n oQueFazerAgora: z.array(z.string()).optional(),\n sugestaoCorrecao: z.string().optional(),\n conceito: z.string().optional(),\n framework: z.string().optional(),\n formattedText: z.string().optional(),\n error: z.string().optional(),\n }),\n },\n async ({ errorOutput, testFilePath }) => {\n const structure = detectProjectStructure();\n const fw = structure.testFrameworks[0] || \"unknown\";\n\n let resolvedOutput = errorOutput?.trim() || \"\";\n if (!resolvedOutput) {\n const lastFailurePath = path.join(PROJECT_ROOT, \".qa-lab-last-failure.log\");\n if (fs.existsSync(lastFailurePath)) {\n try {\n resolvedOutput = fs.readFileSync(lastFailurePath, \"utf8\");\n } catch {}\n }\n }\n if (!resolvedOutput) {\n return {\n content: [{\n type: \"text\",\n text: \"Nenhum output de erro fornecido e nenhuma falha recente capturada.\\n\\nComo usar:\\n1. Rode os testes (run_tests) – se falhar, a saída é salva automaticamente.\\n2. Ou cole aqui o output do terminal quando o teste falhou.\\n3. Depois peça: 'Por que falhou?' ou chame por_que_falhou.\",\n }],\n structuredContent: { ok: false, error: \"No error output\" },\n };\n }\n\n let testCode = \"\";\n if (testFilePath) {\n const normalized = testFilePath.replace(/^\\//, \"\").replace(/\\\\/g, \"/\");\n const fullPath = path.join(PROJECT_ROOT, normalized);\n if (fs.existsSync(fullPath) && !fs.statSync(fullPath).isDirectory()) {\n try {\n testCode = fs.readFileSync(fullPath, \"utf8\");\n } catch {}\n }\n }\n\n const GROQ_KEY = process.env.GROQ_API_KEY;\n const GEMINI_KEY = process.env.GEMINI_API_KEY;\n const OPENAI_KEY = process.env.OPENAI_API_KEY || process.env.QA_LAB_LLM_API_KEY;\n\n if (!GROQ_KEY && !GEMINI_KEY && !OPENAI_KEY) {\n return {\n content: [{\n type: \"text\",\n text: \"Configure GROQ_API_KEY, GEMINI_API_KEY ou OPENAI_API_KEY no .env do projeto para usar a explicação com LLM.\",\n }],\n structuredContent: { ok: false, error: \"No API key configured\" },\n };\n }\n\n const provider = GROQ_KEY ? \"groq\" : GEMINI_KEY ? \"gemini\" : \"openai\";\n const apiKey = GROQ_KEY || GEMINI_KEY || OPENAI_KEY;\n const baseUrl = provider === \"groq\"\n ? \"https://api.groq.com/openai/v1\"\n : provider === \"gemini\"\n ? \"https://generativelanguage.googleapis.com/v1beta\"\n : \"https://api.openai.com/v1\";\n const model = provider === \"groq\"\n ? \"llama-3.3-70b-versatile\"\n : provider === \"gemini\"\n ? \"gemini-1.5-flash\"\n : \"gpt-4o-mini\";\n\n const fwHints = {\n webdriverio: \"WebdriverIO (describe/it, $, browser.$)\",\n appium: \"Appium/WebdriverIO (mobile, $, browser.$)\",\n playwright: \"Playwright (test, page, locator)\",\n cypress: \"Cypress (cy.get, cy.click)\",\n jest: \"Jest (describe, test, expect)\",\n vitest: \"Vitest (describe, test, expect)\",\n robot: \"Robot Framework\",\n pytest: \"pytest\",\n };\n\n const systemPrompt = `Você é um mentor de QA. Analise o output de falha e responda em JSON (apenas o JSON, sem markdown) com as chaves:\n- oQueAconteceu: string (explicação em português do que aconteceu, simples)\n- porQueProvavelmenteFalhou: array de strings (lista de possíveis causas, uma por item)\n- oQueFazerAgora: array de strings (passos numerados do que fazer)\n- sugestaoCorrecao: string ou null (código de correção se aplicável, no formato do framework)\n- conceito: string ou null (ex: \"Flaky test = teste intermitente. Geralmente por timing ou seletores frágeis.\")\n- framework: string (framework do projeto)\n\nFramework do projeto: ${fw}. ${fwHints[fw] || \"\"}\nResponda APENAS com o JSON válido, sem texto antes ou depois.`;\n\n const userPrompt = `Output do terminal/log (teste falhou):\n---\n${resolvedOutput.slice(0, 12000)}\n---\n${testCode ? `\\nCódigo do teste que falhou:\\n---\\n${testCode.slice(0, 6000)}\\n---` : \"\"}`;\n\n try {\n let raw = await callLlmForExplanation(provider, apiKey, baseUrl, model, systemPrompt, userPrompt);\n raw = raw.replace(/^```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\").trim();\n\n let data = {};\n try {\n data = JSON.parse(raw);\n } catch {\n data = {\n oQueAconteceu: raw.slice(0, 500) || \"Não foi possível parsear a resposta.\",\n porQueProvavelmenteFalhou: [],\n oQueFazerAgora: [],\n sugestaoCorrecao: null,\n conceito: null,\n framework: fw,\n };\n }\n\n data.framework = data.framework || fw;\n const formattedText = formatFailureExplanation(data);\n\n return {\n content: [{ type: \"text\", text: formattedText }],\n structuredContent: {\n ok: true,\n oQueAconteceu: data.oQueAconteceu,\n porQueProvavelmenteFalhou: data.porQueProvavelmenteFalhou,\n oQueFazerAgora: data.oQueFazerAgora,\n sugestaoCorrecao: data.sugestaoCorrecao ?? null,\n conceito: data.conceito ?? null,\n framework: data.framework,\n formattedText,\n },\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Erro ao analisar: ${err.message}` }],\n structuredContent: { ok: false, error: err.message },\n };\n }\n }\n);\n\n// ============================================================================\n// NOVAS FERRAMENTAS\n// ============================================================================\n\nserver.registerTool(\n \"suggest_fix\",\n {\n title: \"Sugerir correção para falhas\",\n description: \"Recebe análise de falhas e sugere correções (patch, refactor, etc.).\",\n inputSchema: z.object({\n failures: z.array(z.object({\n test: z.string().optional(),\n message: z.string().optional(),\n stack: z.string().optional(),\n })).describe(\"Resultado de analyze_failures.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n suggestions: z.array(z.object({\n test: z.string().optional(),\n description: z.string(),\n fix: z.string().optional(),\n })),\n }),\n },\n async ({ failures }) => {\n const suggestions = [];\n\n for (const f of failures) {\n const msg = f.message || \"\";\n \n if (/element not found|selector|timeout/i.test(msg)) {\n suggestions.push({\n test: f.test,\n description: \"Elemento não encontrado ou timeout\",\n fix: \"Verifique seletores, adicione waits ou aumente timeout. Use data-testid para seletores mais estáveis.\",\n });\n } else if (/expected.*to.*but/i.test(msg)) {\n suggestions.push({\n test: f.test,\n description: \"Asserção falhou\",\n fix: \"Revise o valor esperado. Verifique se o estado da aplicação está correto antes da asserção.\",\n });\n } else if (/network|fetch|ECONNREFUSED/i.test(msg)) {\n suggestions.push({\n test: f.test,\n description: \"Erro de rede ou API não disponível\",\n fix: \"Verifique se o backend está rodando. Confirme a URL e porta da API.\",\n });\n } else {\n suggestions.push({\n test: f.test,\n description: \"Falha detectada\",\n fix: \"Revise o stack trace e o código do teste.\",\n });\n }\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(suggestions, null, 2) }],\n structuredContent: { ok: true, suggestions },\n };\n }\n);\n\nserver.registerTool(\n \"create_bug_report\",\n {\n title: \"Criar relatório de bug\",\n description: \"Gera um bug report estruturado a partir de falhas de teste.\",\n inputSchema: z.object({\n failures: z.array(z.object({\n test: z.string().optional(),\n message: z.string().optional(),\n stack: z.string().optional(),\n })).describe(\"Falhas (de analyze_failures).\"),\n title: z.string().optional().describe(\"Título do bug.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n report: z.string(),\n title: z.string(),\n }),\n },\n async ({ failures, title }) => {\n const bugTitle = title || `Falha em ${failures.length} teste(s)`;\n const lines = [\n `# ${bugTitle}`,\n \"\",\n \"## Resumo\",\n \"\",\n `${failures.length} teste(s) falharam durante a execução.`,\n \"\",\n \"## Falhas detectadas\",\n \"\",\n ];\n\n failures.forEach((f, i) => {\n lines.push(`### ${i + 1}. ${f.test || \"Teste desconhecido\"}`);\n lines.push(\"\");\n lines.push(`**Mensagem:** ${f.message || \"N/A\"}`);\n lines.push(\"\");\n if (f.stack) {\n lines.push(\"**Stack trace:**\");\n lines.push(\"```\");\n lines.push(f.stack);\n lines.push(\"```\");\n lines.push(\"\");\n }\n });\n\n lines.push(\"## Próximos passos\");\n lines.push(\"\");\n lines.push(\"- [ ] Reproduzir localmente\");\n lines.push(\"- [ ] Identificar causa raiz\");\n lines.push(\"- [ ] Aplicar correção\");\n lines.push(\"- [ ] Validar com testes\");\n\n const report = lines.join(\"\\n\");\n\n return {\n content: [{ type: \"text\", text: report }],\n structuredContent: { ok: true, report, title: bugTitle },\n };\n }\n);\n\nserver.registerTool(\n \"list_test_files\",\n {\n title: \"Listar arquivos de teste\",\n description: \"Lista TODOS os arquivos de teste (qualquer framework: Cypress, Playwright, WDIO, Robot, pytest, Behave, etc.) com filtro opcional.\",\n inputSchema: z.object({\n framework: z.enum([\n \"cypress\", \"playwright\", \"jest\", \"webdriverio\", \"appium\", \"robot\", \"pytest\", \"behave\", \"detox\", \"all\"\n ]).optional().describe(\"Filtrar por framework. Default: all.\"),\n pattern: z.string().optional().describe(\"Pattern para filtrar (ex: 'login', 'api').\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n files: z.array(z.string()),\n total: z.number(),\n }),\n },\n async ({ framework = \"all\", pattern } = {}) => {\n const structure = detectProjectStructure();\n const collected = collectTestFiles(structure, { framework, pattern });\n const allFiles = collected.map((e) => e.path);\n\n const summary = `Encontrados ${allFiles.length} arquivo(s) de teste (qualquer framework).`;\n\n return {\n content: [{ type: \"text\", text: `${summary}\\n\\n${allFiles.slice(0, 50).join(\"\\n\")}` }],\n structuredContent: { ok: true, files: allFiles, total: allFiles.length },\n };\n }\n);\n\nserver.registerTool(\n \"run_linter\",\n {\n title: \"Executar linter\",\n description: \"Roda ESLint, Prettier ou linter configurado no projeto.\",\n inputSchema: z.object({\n fix: z.boolean().optional().describe(\"Auto-fix (--fix). Default: false.\"),\n path: z.string().optional().describe(\"Caminho específico (ex: src/). Default: todo o projeto.\"),\n }),\n outputSchema: z.object({\n status: z.enum([\"passed\", \"failed\", \"not_found\"]),\n message: z.string(),\n exitCode: z.number(),\n output: z.string().optional(),\n }),\n },\n async ({ fix, path: targetPath }) => {\n const structure = detectProjectStructure();\n const scripts = structure.packageJson?.scripts || {};\n\n let cmd, args;\n if (scripts.lint) {\n cmd = \"npm\";\n args = [\"run\", \"lint\"];\n } else if (structure.packageJson?.devDependencies?.eslint || structure.packageJson?.dependencies?.eslint) {\n cmd = \"npx\";\n args = [\"eslint\", targetPath || \".\"];\n if (fix) args.push(\"--fix\");\n } else {\n return {\n content: [{ type: \"text\", text: \"Linter não detectado no projeto.\" }],\n structuredContent: { status: \"not_found\", message: \"No linter found\", exitCode: 1 },\n };\n }\n\n return new Promise((resolve) => {\n const child = spawn(cmd, args, {\n cwd: PROJECT_ROOT,\n stdio: [\"inherit\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n env: { ...process.env },\n });\n\n let stdout = \"\";\n let stderr = \"\";\n if (child.stdout) child.stdout.on(\"data\", (d) => { stdout += d.toString(); });\n if (child.stderr) child.stderr.on(\"data\", (d) => { stderr += d.toString(); });\n\n child.on(\"close\", (code) => {\n const output = [stdout, stderr].filter(Boolean).join(\"\\n\").trim();\n const passed = code === 0;\n resolve({\n content: [{ type: \"text\", text: passed ? \"Linter passou.\" : \"Linter encontrou problemas.\" }],\n structuredContent: {\n status: passed ? \"passed\" : \"failed\",\n message: passed ? \"Lint passed\" : \"Lint failed\",\n exitCode: code ?? 1,\n output: !passed ? output : undefined,\n },\n });\n });\n });\n }\n);\n\nserver.registerTool(\n \"install_dependencies\",\n {\n title: \"Instalar dependências\",\n description: \"Roda npm install, yarn install ou pnpm install (detecta automaticamente).\",\n inputSchema: z.object({\n packageManager: z.enum([\"npm\", \"yarn\", \"pnpm\", \"auto\"]).optional().describe(\"Package manager. Default: auto.\"),\n }),\n outputSchema: z.object({\n status: z.enum([\"success\", \"failed\"]),\n message: z.string(),\n exitCode: z.number(),\n }),\n },\n async ({ packageManager = \"auto\" }) => {\n let pm = packageManager;\n \n if (pm === \"auto\") {\n if (fs.existsSync(path.join(PROJECT_ROOT, \"yarn.lock\"))) pm = \"yarn\";\n else if (fs.existsSync(path.join(PROJECT_ROOT, \"pnpm-lock.yaml\"))) pm = \"pnpm\";\n else pm = \"npm\";\n }\n\n return new Promise((resolve) => {\n const child = spawn(pm, [\"install\"], {\n cwd: PROJECT_ROOT,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n env: { ...process.env },\n });\n\n child.on(\"close\", (code) => {\n const passed = code === 0;\n resolve({\n content: [{ type: \"text\", text: passed ? \"Dependências instaladas.\" : \"Erro ao instalar dependências.\" }],\n structuredContent: {\n status: passed ? \"success\" : \"failed\",\n message: passed ? \"Dependencies installed\" : \"Install failed\",\n exitCode: code ?? 1,\n },\n });\n });\n });\n }\n);\n\nserver.registerTool(\n \"get_test_coverage\",\n {\n title: \"Obter cobertura de testes\",\n description: \"Roda testes com coverage (Jest, Playwright, Cypress com plugin).\",\n inputSchema: z.object({\n framework: z.enum([\"jest\", \"playwright\", \"cypress\"]).optional().describe(\"Framework. Default: detectado automaticamente.\"),\n }),\n outputSchema: z.object({\n status: z.enum([\"success\", \"failed\", \"not_supported\"]),\n message: z.string(),\n coveragePercent: z.number().optional(),\n output: z.string().optional(),\n }),\n },\n async ({ framework }) => {\n const structure = detectProjectStructure();\n const fw = framework || structure.testFrameworks[0];\n\n if (fw === \"jest\") {\n return new Promise((resolve) => {\n const child = spawn(\"npx\", [\"jest\", \"--coverage\"], {\n cwd: PROJECT_ROOT,\n stdio: [\"inherit\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n env: { ...process.env },\n });\n\n let stdout = \"\";\n if (child.stdout) child.stdout.on(\"data\", (d) => { stdout += d.toString(); });\n\n child.on(\"close\", (code) => {\n const coverageMatch = stdout.match(/All files.*?(\\d+\\.?\\d*)/);\n const coveragePercent = coverageMatch ? parseFloat(coverageMatch[1]) : undefined;\n\n resolve({\n content: [{ type: \"text\", text: `Coverage: ${coveragePercent || \"N/A\"}%` }],\n structuredContent: {\n status: code === 0 ? \"success\" : \"failed\",\n message: code === 0 ? \"Coverage generated\" : \"Coverage failed\",\n coveragePercent,\n output: stdout,\n },\n });\n });\n });\n }\n\n return {\n content: [{ type: \"text\", text: `Coverage não suportado para ${fw} ainda.` }],\n structuredContent: { status: \"not_supported\", message: \"Coverage not supported for this framework\" },\n };\n }\n);\n\nserver.registerTool(\n \"watch_tests\",\n {\n title: \"Rodar testes em modo watch\",\n description: \"Inicia testes em watch mode (Jest, Vitest). Útil para desenvolvimento.\",\n inputSchema: z.object({\n framework: z.enum([\"jest\", \"vitest\"]).optional().describe(\"Framework. Default: detectado.\"),\n }),\n outputSchema: z.object({\n status: z.string(),\n message: z.string(),\n }),\n },\n async ({ framework }) => {\n const structure = detectProjectStructure();\n const fw = framework || (structure.testFrameworks.includes(\"jest\") ? \"jest\" : \"vitest\");\n\n if (!structure.testFrameworks.includes(fw)) {\n return {\n content: [{ type: \"text\", text: `${fw} não detectado no projeto.` }],\n structuredContent: { status: \"not_found\", message: \"Framework not found\" },\n };\n }\n\n return {\n content: [{ type: \"text\", text: `Para watch mode, rode manualmente: npx ${fw} --watch` }],\n structuredContent: {\n status: \"info\",\n message: `Watch mode requires interactive terminal. Run: npx ${fw} --watch`,\n },\n };\n }\n);\n\nserver.registerTool(\n \"create_test_template\",\n {\n title: \"Criar template de teste\",\n description: \"Gera template básico de teste (boilerplate) para o framework escolhido.\",\n inputSchema: z.object({\n framework: z.enum([\"cypress\", \"playwright\", \"jest\"]).describe(\"Framework.\"),\n type: z.enum([\"api\", \"ui\", \"unit\"]).optional().describe(\"Tipo de teste. Default: api.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n template: z.string(),\n suggestedFileName: z.string(),\n }),\n },\n async ({ framework, type = \"api\" }) => {\n let template = \"\";\n let fileName = \"\";\n\n if (framework === \"cypress\") {\n fileName = `${type}-test.cy.js`;\n template = `describe('${type.toUpperCase()} Test', () => {\n it('should pass', () => {\n ${type === \"api\" ? \"cy.request('GET', 'http://localhost:3000/api/health').then((res) => {\\n expect(res.status).to.eq(200);\\n });\" : \"cy.visit('/');\\n cy.get('h1').should('be.visible');\"}\n });\n});`;\n } else if (framework === \"playwright\") {\n fileName = `${type}-test.spec.js`;\n template = `const { test, expect } = require('@playwright/test');\n\ntest.describe('${type.toUpperCase()} Test', () => {\n test('should pass', async ({ ${type === \"api\" ? \"request\" : \"page\"} }) => {\n ${type === \"api\" ? \"const res = await request.get('http://localhost:3000/api/health');\\n expect(res.status()).toBe(200);\" : \"await page.goto('/');\\n await expect(page.locator('h1')).toBeVisible();\"}\n });\n});`;\n } else {\n fileName = `${type}-test.test.js`;\n template = `describe('${type.toUpperCase()} Test', () => {\n test('should pass', ${type === \"api\" ? \"async () => {\\n const res = await fetch('http://localhost:3000/api/health');\\n expect(res.status).toBe(200);\\n }\" : \"() => {\\n expect(true).toBe(true);\\n }\"});\n});`;\n }\n\n return {\n content: [{ type: \"text\", text: `Template criado. Use write_test para gravar.` }],\n structuredContent: { ok: true, template, suggestedFileName: fileName },\n };\n }\n);\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((err) => {\n console.error(\"Erro no MCP server:\", err);\n process.exit(1);\n});\n"],"mappings":"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","names":["res","data"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.js"],"sourcesContent":["#!/usr/bin/env node\n/**\n * MCP Lab Agent - Standalone\n * MCP server genérico para QA automation em qualquer projeto.\n * Detecta automaticamente Cypress, Playwright, Jest, estrutura do projeto, etc.\n */\nimport { config } from \"dotenv\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport { spawn } from \"node:child_process\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\n\nconst PROJECT_ROOT = process.cwd();\nconfig({ path: path.join(PROJECT_ROOT, \".env\") });\n\nconst server = new McpServer({\n name: \"mcp-lab-agent\",\n version: \"1.0.0\",\n});\n\n// ============================================================================\n// DETECÇÃO AUTOMÁTICA DE ESTRUTURA\n// ============================================================================\n\nfunction detectProjectStructure() {\n const structure = {\n hasTests: false,\n testFrameworks: [],\n testDirs: [],\n hasBackend: false,\n backendDir: null,\n hasFrontend: false,\n frontendDir: null,\n hasMobile: false,\n packageJson: null,\n pythonRequirements: null,\n };\n\n // Detectar Node.js/JavaScript/TypeScript\n const pkgPath = path.join(PROJECT_ROOT, \"package.json\");\n if (fs.existsSync(pkgPath)) {\n structure.packageJson = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n const deps = {\n ...structure.packageJson.dependencies,\n ...structure.packageJson.devDependencies,\n };\n\n // Frameworks E2E/UI\n if (deps.cypress) {\n structure.testFrameworks.push(\"cypress\");\n structure.hasTests = true;\n }\n if (deps[\"@playwright/test\"] || deps.playwright) {\n structure.testFrameworks.push(\"playwright\");\n structure.hasTests = true;\n }\n if (deps.webdriverio || deps[\"@wdio/cli\"]) {\n structure.testFrameworks.push(\"webdriverio\");\n structure.hasTests = true;\n }\n\n // Frameworks Unit/Integration\n if (deps.jest) {\n structure.testFrameworks.push(\"jest\");\n structure.hasTests = true;\n }\n if (deps.vitest) {\n structure.testFrameworks.push(\"vitest\");\n structure.hasTests = true;\n }\n if (deps.mocha) {\n structure.testFrameworks.push(\"mocha\");\n structure.hasTests = true;\n }\n if (deps.jasmine) {\n structure.testFrameworks.push(\"jasmine\");\n structure.hasTests = true;\n }\n\n // Frameworks Mobile\n if (deps.appium || deps[\"appium-webdriverio\"]) {\n structure.testFrameworks.push(\"appium\");\n structure.hasTests = true;\n structure.hasMobile = true;\n }\n if (deps.detox) {\n structure.testFrameworks.push(\"detox\");\n structure.hasTests = true;\n structure.hasMobile = true;\n }\n\n // API Testing\n if (deps.supertest) {\n structure.testFrameworks.push(\"supertest\");\n structure.hasTests = true;\n }\n if (deps[\"@pactum/pactum\"] || deps.pactum) {\n structure.testFrameworks.push(\"pactum\");\n structure.hasTests = true;\n }\n\n // Outros frameworks E2E\n if (deps.testcafe || deps[\"testcafe\"]) {\n structure.testFrameworks.push(\"testcafe\");\n structure.hasTests = true;\n }\n if (deps.nightwatch || deps[\"nightwatch\"]) {\n structure.testFrameworks.push(\"nightwatch\");\n structure.hasTests = true;\n }\n if (deps.puppeteer) {\n structure.testFrameworks.push(\"puppeteer\");\n structure.hasTests = true;\n }\n if (deps.codeceptjs || deps[\"codeceptjs\"]) {\n structure.testFrameworks.push(\"codeceptjs\");\n structure.hasTests = true;\n }\n\n // Backend detection\n if (deps.express || deps.fastify || deps[\"@nestjs/core\"] || deps.koa) {\n structure.hasBackend = true;\n }\n \n // Frontend detection\n if (deps.next || deps.react || deps.vue || deps.svelte || deps.angular) {\n structure.hasFrontend = true;\n }\n }\n\n // Detectar Python (Robot Framework, pytest, etc.)\n const requirementsPath = path.join(PROJECT_ROOT, \"requirements.txt\");\n if (fs.existsSync(requirementsPath)) {\n const requirements = fs.readFileSync(requirementsPath, \"utf8\");\n structure.pythonRequirements = requirements;\n\n if (/robotframework/i.test(requirements)) {\n structure.testFrameworks.push(\"robot\");\n structure.hasTests = true;\n }\n if (/pytest/i.test(requirements)) {\n structure.testFrameworks.push(\"pytest\");\n structure.hasTests = true;\n }\n if (/behave/i.test(requirements)) {\n structure.testFrameworks.push(\"behave\");\n structure.hasTests = true;\n }\n if (/requests/i.test(requirements)) {\n structure.hasBackend = true;\n }\n }\n\n // Detectar pastas de teste (genérico)\n const commonTestDirs = [\n \"tests\", \"test\", \"e2e\", \"cypress\", \"playwright\", \"__tests__\",\n \"specs\", \"spec\", \"integration\", \"unit\", \"functional\", \"robot\",\n \"features\", \"scenarios\", \"mobile\", \"api\",\n // Monorepo: subprojetos por framework\n \"playwright-js\", \"puppeteer-js\", \"testcafe-js\", \"wdio-webdriver-io\",\n \"nightwatch-js\", \"codeceptjs\", \"robot-framework\", \"selenium-python\"\n ];\n for (const dir of commonTestDirs) {\n const fullPath = path.join(PROJECT_ROOT, dir);\n if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {\n structure.testDirs.push(dir);\n }\n }\n\n // Monorepo: escanear subdirs de primeiro nível que parecem ter testes\n const skipDirs = [\"node_modules\", \".git\", \"dist\", \"build\", \".next\", \".venv\"];\n try {\n const rootEntries = fs.readdirSync(PROJECT_ROOT, { withFileTypes: true });\n for (const e of rootEntries) {\n if (!e.isDirectory() || skipDirs.includes(e.name)) continue;\n const subPath = path.join(PROJECT_ROOT, e.name);\n if (structure.testDirs.includes(e.name)) continue;\n // Verificar se há package.json ou tests/ dentro\n const hasPkg = fs.existsSync(path.join(subPath, \"package.json\"));\n const hasTests = fs.existsSync(path.join(subPath, \"tests\")) ||\n fs.existsSync(path.join(subPath, \"test\")) ||\n fs.existsSync(path.join(subPath, \"e2e\")) ||\n fs.existsSync(path.join(subPath, \"__tests__\")) ||\n fs.existsSync(path.join(subPath, \"specs\"));\n if (hasPkg || hasTests) {\n structure.testDirs.push(e.name);\n }\n }\n } catch {}\n\n // Monorepo: detectar frameworks em package.json de subdirs\n for (const dir of structure.testDirs) {\n const subPkg = path.join(PROJECT_ROOT, dir, \"package.json\");\n if (!fs.existsSync(subPkg)) continue;\n try {\n const sub = JSON.parse(fs.readFileSync(subPkg, \"utf8\"));\n const subDeps = { ...(sub.dependencies || {}), ...(sub.devDependencies || {}) };\n const toAdd = [];\n if (subDeps.cypress && !structure.testFrameworks.includes(\"cypress\")) toAdd.push(\"cypress\");\n if ((subDeps[\"@playwright/test\"] || subDeps.playwright) && !structure.testFrameworks.includes(\"playwright\")) toAdd.push(\"playwright\");\n if ((subDeps.webdriverio || subDeps[\"@wdio/cli\"]) && !structure.testFrameworks.includes(\"webdriverio\")) toAdd.push(\"webdriverio\");\n if (subDeps.testcafe && !structure.testFrameworks.includes(\"testcafe\")) toAdd.push(\"testcafe\");\n if (subDeps.nightwatch && !structure.testFrameworks.includes(\"nightwatch\")) toAdd.push(\"nightwatch\");\n if (subDeps.puppeteer && !structure.testFrameworks.includes(\"puppeteer\")) toAdd.push(\"puppeteer\");\n if (subDeps.codeceptjs && !structure.testFrameworks.includes(\"codeceptjs\")) toAdd.push(\"codeceptjs\");\n if (subDeps.jest && !structure.testFrameworks.includes(\"jest\")) toAdd.push(\"jest\");\n toAdd.forEach((fw) => { structure.testFrameworks.push(fw); structure.hasTests = true; });\n } catch {}\n }\n\n // Monorepo: detectar Robot/pytest em requirements.txt de subdirs\n for (const dir of structure.testDirs) {\n const reqPath = path.join(PROJECT_ROOT, dir, \"requirements.txt\");\n if (!fs.existsSync(reqPath)) continue;\n try {\n const req = fs.readFileSync(reqPath, \"utf8\");\n if (/robotframework/i.test(req) && !structure.testFrameworks.includes(\"robot\")) {\n structure.testFrameworks.push(\"robot\");\n structure.hasTests = true;\n }\n if (/pytest|selenium/i.test(req) && !structure.testFrameworks.includes(\"pytest\")) {\n structure.testFrameworks.push(\"pytest\");\n structure.hasTests = true;\n }\n } catch {}\n }\n\n // Detectar backend\n const commonBackendDirs = [\"backend\", \"server\", \"api\", \"src\"];\n for (const dir of commonBackendDirs) {\n const fullPath = path.join(PROJECT_ROOT, dir);\n if (fs.existsSync(fullPath) && !structure.backendDir) {\n const hasServerFile = fs.existsSync(path.join(fullPath, \"server.js\")) ||\n fs.existsSync(path.join(fullPath, \"index.js\")) ||\n fs.existsSync(path.join(fullPath, \"app.js\"));\n if (hasServerFile) {\n structure.backendDir = dir;\n }\n }\n }\n\n // Detectar frontend\n const commonFrontendDirs = [\"frontend\", \"client\", \"web\", \"app\", \"src\"];\n for (const dir of commonFrontendDirs) {\n const fullPath = path.join(PROJECT_ROOT, dir);\n if (fs.existsSync(fullPath) && !structure.frontendDir) {\n const hasAppFile = fs.existsSync(path.join(fullPath, \"App.js\")) ||\n fs.existsSync(path.join(fullPath, \"App.tsx\")) ||\n fs.existsSync(path.join(fullPath, \"index.html\"));\n if (hasAppFile) {\n structure.frontendDir = dir;\n }\n }\n }\n\n return structure;\n}\n\n// ============================================================================\n// PADRÕES UNIVERSAIS DE ARQUIVOS DE TESTE (qualquer framework)\n// ============================================================================\n\nconst UNIVERSAL_TEST_PATTERNS = [\n /\\.(cy|spec|test)\\.(js|ts|jsx|tsx)$/i,\n /_test\\.(js|ts)$/i, // CodeceptJS\n /\\.robot$/i,\n /\\.feature$/i,\n /^(test_.*|.*_test)\\.py$/i,\n /\\.steps?\\.(js|ts|py)$/i,\n /\\.e2e\\.(js|ts)$/i,\n /\\.it\\.(js|ts)$/i,\n];\n\nfunction isTestFile(name) {\n return UNIVERSAL_TEST_PATTERNS.some((re) => re.test(name));\n}\n\nfunction collectTestFiles(structure, options = {}) {\n const { pattern, framework, maxContentFiles = 0 } = options;\n const results = [];\n\n for (const dir of structure.testDirs) {\n const fullPath = path.join(PROJECT_ROOT, dir);\n const walk = (p, base = \"\") => {\n if (!fs.existsSync(p)) return;\n const entries = fs.readdirSync(p, { withFileTypes: true });\n for (const e of entries) {\n const rel = base ? `${base}/${e.name}` : e.name;\n if (e.isDirectory()) {\n if (e.name === \"node_modules\" || e.name === \".git\" || e.name === \".venv\") continue;\n walk(path.join(p, e.name), rel);\n } else if (e.isFile() && isTestFile(e.name)) {\n const filePath = `${dir}/${rel}`;\n if (pattern && !filePath.toLowerCase().includes(pattern.toLowerCase())) continue;\n const inferredFw = inferFrameworkFromFile(e.name, structure, filePath);\n if (framework && framework !== \"all\" && inferredFw !== framework && !matchesFramework(inferredFw, framework)) continue;\n const entry = { path: filePath, inferredFramework: inferredFw };\n if (maxContentFiles > 0 && results.length < maxContentFiles) {\n try {\n entry.content = fs.readFileSync(path.join(PROJECT_ROOT, filePath), \"utf8\");\n } catch {}\n }\n results.push(entry);\n }\n }\n };\n walk(fullPath);\n }\n return results;\n}\n\nfunction inferFrameworkFromFile(name, structure = {}, filePath = \"\") {\n const pathLower = (filePath || \"\").toLowerCase().replace(/\\\\/g, \"/\");\n // Inferir pelo CAMINHO quando há múltiplos frameworks (ex: tests/cypress/, tests/selenium-python/)\n if (/[\\/]cypress[\\/\\-]/.test(pathLower)) return \"cypress\";\n if (/[\\/]playwright[\\/\\-]/.test(pathLower)) return \"playwright\";\n if (/[\\/]wdio[\\/\\-]|[\\/]webdriver[\\/\\-]/.test(pathLower)) return \"webdriverio\";\n if (/[\\/]appium[\\/\\-]/.test(pathLower)) return \"appium\";\n if (/[\\/]selenium-python[\\/]|[\\/]pytest[\\/\\-]/.test(pathLower)) return \"pytest\";\n if (/[\\/]robot[\\/\\-]/.test(pathLower)) return \"robot\";\n if (/[\\/]codecept[\\/\\-]/.test(pathLower)) return \"codeceptjs\";\n if (/[\\/]nightwatch[\\/\\-]/.test(pathLower)) return \"nightwatch\";\n if (/[\\/]testcafe[\\/\\-]/.test(pathLower)) return \"testcafe\";\n if (/[\\/]puppeteer[\\/\\-]/.test(pathLower)) return \"puppeteer\";\n if (/[\\/]behave[\\/\\-]|[\\/]features[\\/]/.test(pathLower)) return \"behave\";\n // Fallback: inferir pela extensão do arquivo\n if (/\\.cy\\.(js|ts|jsx|tsx)/i.test(name)) return \"cypress\";\n if (/_test\\.(js|ts)$/i.test(name)) return \"codeceptjs\";\n if (/\\.spec\\.(js|ts|jsx|tsx)/i.test(name)) {\n if (structure?.testFrameworks?.includes(\"webdriverio\")) return \"webdriverio\";\n if (structure?.testFrameworks?.includes(\"appium\")) return \"appium\";\n return \"playwright\";\n }\n if (/\\.test\\.(js|ts|jsx|tsx)/i.test(name)) return structure?.testFrameworks?.includes(\"vitest\") ? \"vitest\" : \"jest\";\n if (/\\.robot$/i.test(name)) return \"robot\";\n if (/\\.feature$/i.test(name)) return \"behave\";\n if (/\\.(py|steps?\\.py)$/i.test(name) || /^(test_.*|.*_test)\\.py$/i.test(name)) return \"pytest\";\n if (/\\.e2e\\.(js|ts)/i.test(name)) return \"playwright\";\n return \"unknown\";\n}\n\nfunction matchesFramework(inferred, requested) {\n const aliases = { spec: [\"playwright\", \"webdriverio\", \"appium\"] };\n if (inferred === requested) return true;\n return aliases[inferred]?.includes(requested);\n}\n\n// ============================================================================\n// MÉTRICAS DE NEGÓCIO - Helpers\n// ============================================================================\n\nconst METRICS_FILE = path.join(PROJECT_ROOT, \".qa-lab-metrics.json\");\nconst FLOWS_CONFIG_FILE = path.join(PROJECT_ROOT, \"qa-lab-flows.json\");\n\nfunction parseTestRunResult(runOutput, exitCode) {\n let passed = 0;\n let failed = 0;\n const jestMatch = runOutput.match(/Tests:\\s+(\\d+)\\s+passed(?:,\\s*(\\d+)\\s+failed)?/);\n if (jestMatch) {\n passed = parseInt(jestMatch[1], 10);\n failed = jestMatch[2] ? parseInt(jestMatch[2], 10) : 0;\n }\n const cypressPass = runOutput.match(/(\\d+)\\s+passing/);\n const cypressFail = runOutput.match(/(\\d+)\\s+failing/);\n if (cypressPass) passed = parseInt(cypressPass[1], 10);\n if (cypressFail) failed = parseInt(cypressFail[1], 10);\n const pwPass = runOutput.match(/(\\d+)\\s+passed/);\n const pwFail = runOutput.match(/(\\d+)\\s+failed/);\n if (pwPass) passed = parseInt(pwPass[1], 10);\n if (pwFail) failed = parseInt(pwFail[1], 10);\n if (passed === 0 && failed === 0) {\n if (exitCode === 0) passed = 1;\n else failed = 1;\n }\n return { passed, failed };\n}\n\nfunction appendMetricsEvent(event) {\n try {\n let data = { events: [], lastUpdated: new Date().toISOString() };\n if (fs.existsSync(METRICS_FILE)) {\n const raw = fs.readFileSync(METRICS_FILE, \"utf8\");\n try {\n data = JSON.parse(raw);\n } catch {}\n }\n data.events = data.events || [];\n data.events.push({ ...event, timestamp: event.timestamp || new Date().toISOString() });\n data.lastUpdated = new Date().toISOString();\n if (data.events.length > 500) data.events = data.events.slice(-400);\n fs.writeFileSync(METRICS_FILE, JSON.stringify(data, null, 2), \"utf8\");\n } catch {}\n}\n\nfunction extractFailuresFromOutput(runOutput) {\n const failures = [];\n const lines = runOutput.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (/fail|error|assertion|timeout|element not found|selector/i.test(line)) {\n failures.push({\n test: lines[Math.max(0, i - 1)]?.trim() || \"unknown\",\n message: line.trim().slice(0, 500),\n });\n }\n }\n return failures.slice(0, 20);\n}\n\n// ============================================================================\n// FERRAMENTAS GENÉRICAS\n// ============================================================================\n\nserver.registerTool(\n \"read_file\",\n {\n title: \"Ler qualquer arquivo\",\n description: \"Lê o conteúdo de QUALQUER arquivo do projeto por caminho. Use para specs, page objects, componentes, código fonte - qualquer formato.\",\n inputSchema: z.object({\n path: z.string().describe(\"Caminho relativo ao projeto (ex: cypress/e2e/login.cy.js, src/pages/Login.tsx, tests/login.robot).\"),\n encoding: z.enum([\"utf8\", \"utf-8\"]).optional().describe(\"Encoding. Default: utf8\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n content: z.string().optional(),\n error: z.string().optional(),\n }),\n },\n async ({ path: filePath, encoding = \"utf8\" }) => {\n const normalized = filePath.replace(/^\\//, \"\").replace(/\\\\/g, \"/\");\n const fullPath = path.join(PROJECT_ROOT, normalized);\n\n if (!fullPath.startsWith(PROJECT_ROOT)) {\n return {\n content: [{ type: \"text\", text: \"Caminho fora do projeto.\" }],\n structuredContent: { ok: false, error: \"Path outside project\" },\n };\n }\n if (!fs.existsSync(fullPath)) {\n return {\n content: [{ type: \"text\", text: `Arquivo não encontrado: ${normalized}` }],\n structuredContent: { ok: false, error: \"File not found\" },\n };\n }\n const stat = fs.statSync(fullPath);\n if (stat.isDirectory()) {\n return {\n content: [{ type: \"text\", text: \"É um diretório. Use um caminho de arquivo.\" }],\n structuredContent: { ok: false, error: \"Is directory\" },\n };\n }\n\n try {\n const content = fs.readFileSync(fullPath, encoding);\n return {\n content: [{ type: \"text\", text: content }],\n structuredContent: { ok: true, content },\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Erro ao ler: ${err.message}` }],\n structuredContent: { ok: false, error: err.message },\n };\n }\n }\n);\n\nserver.registerTool(\n \"detect_project\",\n {\n title: \"Detectar estrutura do projeto\",\n description: \"Analisa o projeto e identifica frameworks de teste, pastas, backend, frontend.\",\n inputSchema: z.object({}),\n outputSchema: z.object({\n ok: z.boolean(),\n structure: z.object({\n hasTests: z.boolean(),\n testFrameworks: z.array(z.string()),\n testDirs: z.array(z.string()),\n hasBackend: z.boolean(),\n backendDir: z.string().nullable(),\n hasFrontend: z.boolean(),\n frontendDir: z.string().nullable(),\n }),\n }),\n },\n async () => {\n const structure = detectProjectStructure();\n const summary = [\n `Frameworks de teste: ${structure.testFrameworks.join(\", \") || \"nenhum\"}`,\n `Pastas de teste: ${structure.testDirs.join(\", \") || \"nenhuma\"}`,\n `Backend: ${structure.backendDir || \"não detectado\"}`,\n `Frontend: ${structure.frontendDir || \"não detectado\"}`,\n ].join(\"\\n\");\n\n return {\n content: [{ type: \"text\", text: summary }],\n structuredContent: { ok: true, structure },\n };\n }\n);\n\nserver.registerTool(\n \"run_tests\",\n {\n title: \"Executar testes\",\n description: \"Roda testes do projeto. Suporta: Cypress, Playwright, WebdriverIO, Jest, Vitest, Mocha, Appium, Detox, Robot Framework, pytest, e mais. Detecta automaticamente.\",\n inputSchema: z.object({\n framework: z.enum([\n \"cypress\", \"playwright\", \"webdriverio\", \"jest\", \"vitest\", \"mocha\",\n \"appium\", \"detox\", \"robot\", \"pytest\", \"supertest\", \"pactum\",\n \"testcafe\", \"nightwatch\", \"puppeteer\", \"codeceptjs\", \"npm\"\n ]).optional().describe(\"Framework específico ou 'npm' para npm test.\"),\n spec: z.string().optional().describe(\"Caminho do spec (ex: cypress/e2e/test.cy.js).\"),\n suite: z.string().optional().describe(\"Suite ou pattern (ex: e2e, api).\"),\n explainOnFailure: z.boolean().optional().describe(\"Se true, quando falhar gera automaticamente: O que aconteceu, Por que falhou, O que fazer, Sugestão de correção. Requer API key.\"),\n }),\n outputSchema: z.object({\n status: z.enum([\"passed\", \"failed\", \"not_found\"]),\n message: z.string(),\n exitCode: z.number(),\n runOutput: z.string().optional(),\n }),\n },\n async ({ framework, spec, suite, explainOnFailure }) => {\n const structure = detectProjectStructure();\n \n if (!structure.hasTests) {\n return {\n content: [{ type: \"text\", text: \"Nenhum framework de teste detectado no projeto.\" }],\n structuredContent: {\n status: \"not_found\",\n message: \"No test framework found\",\n exitCode: 1,\n },\n };\n }\n\n let selectedFramework = framework;\n if (!selectedFramework && structure.testFrameworks.length > 0) {\n selectedFramework = structure.testFrameworks[0];\n }\n\n let cmd, args, cwd;\n\n // E2E/UI Frameworks\n if (selectedFramework === \"cypress\") {\n cmd = \"npx\";\n args = spec ? [\"cypress\", \"run\", \"--spec\", spec] : [\"cypress\", \"run\"];\n cwd = structure.testDirs.includes(\"cypress\") \n ? path.join(PROJECT_ROOT, \"cypress\")\n : structure.testDirs[0] \n ? path.join(PROJECT_ROOT, structure.testDirs[0])\n : PROJECT_ROOT;\n } else if (selectedFramework === \"playwright\") {\n cmd = \"npx\";\n args = spec ? [\"playwright\", \"test\", spec] : [\"playwright\", \"test\"];\n cwd = structure.testDirs.includes(\"playwright\")\n ? path.join(PROJECT_ROOT, \"playwright\")\n : structure.testDirs[0]\n ? path.join(PROJECT_ROOT, structure.testDirs[0])\n : PROJECT_ROOT;\n } else if (selectedFramework === \"webdriverio\") {\n cmd = \"npx\";\n args = spec ? [\"wdio\", \"run\", spec] : [\"wdio\", \"run\"];\n cwd = getFrameworkCwd(structure, [\"wdio-webdriver-io\", \"specs\", \"tests\"]);\n } else if (selectedFramework === \"testcafe\") {\n cmd = \"npx\";\n args = spec ? [\"testcafe\", spec] : [\"testcafe\"];\n cwd = getFrameworkCwd(structure, [\"testcafe-js\", \"testcafe\", \"tests\"]);\n } else if (selectedFramework === \"nightwatch\") {\n cmd = \"npx\";\n args = spec ? [\"nightwatch\", \"--test\", spec] : [\"nightwatch\"];\n cwd = getFrameworkCwd(structure, [\"nightwatch-js\", \"nightwatch\", \"tests\"]);\n } else if (selectedFramework === \"puppeteer\") {\n cmd = \"npx\";\n args = spec ? [\"jest\", spec, \"--config\", \"jest.config.js\"] : [\"jest\"];\n cwd = getFrameworkCwd(structure, [\"puppeteer-js\", \"puppeteer\", \"__tests__\"]);\n } else if (selectedFramework === \"codeceptjs\") {\n cmd = \"npx\";\n args = spec ? [\"codeceptjs\", \"run\", \"--grep\", spec] : [\"codeceptjs\", \"run\"];\n cwd = getFrameworkCwd(structure, [\"codeceptjs\", \"tests\"]);\n\n // Unit/Integration Frameworks\n } else if (selectedFramework === \"jest\") {\n cmd = \"npx\";\n args = [\"jest\"];\n if (spec) args.push(spec);\n cwd = PROJECT_ROOT;\n } else if (selectedFramework === \"vitest\") {\n cmd = \"npx\";\n args = [\"vitest\", \"run\"];\n if (spec) args.push(spec);\n cwd = PROJECT_ROOT;\n } else if (selectedFramework === \"mocha\") {\n cmd = \"npx\";\n args = spec ? [\"mocha\", spec] : [\"mocha\"];\n cwd = PROJECT_ROOT;\n \n // Mobile Frameworks\n } else if (selectedFramework === \"appium\") {\n cmd = \"npx\";\n args = spec ? [\"wdio\", \"run\", spec] : [\"wdio\", \"run\"];\n cwd = PROJECT_ROOT;\n } else if (selectedFramework === \"detox\") {\n cmd = \"npx\";\n args = [\"detox\", \"test\"];\n if (spec) args.push(spec);\n cwd = PROJECT_ROOT;\n \n // Python Frameworks\n } else if (selectedFramework === \"robot\") {\n cmd = \"robot\";\n args = spec ? [spec] : [structure.testDirs[0] || \"tests\"];\n cwd = PROJECT_ROOT;\n } else if (selectedFramework === \"pytest\") {\n cmd = \"pytest\";\n args = spec ? [spec] : [];\n cwd = PROJECT_ROOT;\n \n // API Testing\n } else if (selectedFramework === \"supertest\" || selectedFramework === \"pactum\") {\n cmd = \"npm\";\n args = [\"test\"];\n cwd = PROJECT_ROOT;\n \n // Fallback\n } else {\n cmd = \"npm\";\n args = [\"test\"];\n cwd = PROJECT_ROOT;\n }\n\n const startTime = Date.now();\n return new Promise((resolve) => {\n const child = spawn(cmd, args, {\n cwd,\n stdio: [\"inherit\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n env: { ...process.env },\n });\n\n let stdout = \"\";\n let stderr = \"\";\n if (child.stdout) {\n child.stdout.on(\"data\", (d) => {\n const s = d.toString();\n stdout += s;\n process.stdout.write(s);\n });\n }\n if (child.stderr) {\n child.stderr.on(\"data\", (d) => {\n const s = d.toString();\n stderr += s;\n process.stderr.write(s);\n });\n }\n\n child.on(\"close\", (code) => {\n const runOutput = [stdout, stderr].filter(Boolean).join(\"\\n\").trim();\n const passed = code === 0;\n const durationSeconds = Math.round((Date.now() - startTime) / 1000);\n if (!passed && runOutput) {\n try {\n fs.writeFileSync(path.join(PROJECT_ROOT, \".qa-lab-last-failure.log\"), runOutput, \"utf8\");\n } catch {}\n }\n const { passed: p, failed: f } = parseTestRunResult(runOutput, code);\n appendMetricsEvent({\n type: \"test_run\",\n framework: selectedFramework,\n spec: spec || undefined,\n passed: p,\n failed: f,\n durationSeconds,\n exitCode: code ?? 1,\n failures: !passed ? extractFailuresFromOutput(runOutput) : undefined,\n });\n resolve({\n content: [{ type: \"text\", text: passed ? \"Testes executados com sucesso.\" : \"Falha na execução dos testes.\" }],\n structuredContent: {\n status: passed ? \"passed\" : \"failed\",\n message: passed ? \"Tests passed\" : \"Tests failed\",\n exitCode: code ?? 1,\n runOutput: !passed ? runOutput : undefined,\n },\n });\n });\n });\n }\n);\n\nserver.registerTool(\n \"read_project\",\n {\n title: \"Ler estrutura do projeto\",\n description: \"Lê package.json, specs existentes (qualquer framework: Cypress, Playwright, WDIO, Robot, pytest, etc) e retorna contexto. Use includeContent para trazer código de exemplos.\",\n inputSchema: z.object({\n includeContent: z.boolean().optional().describe(\"Se true, inclui conteúdo dos primeiros 3 arquivos de teste como referência. Default: false.\"),\n maxFiles: z.number().optional().describe(\"Máximo de arquivos cujo conteúdo será lido. Default: 3.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n summary: z.string(),\n packageJson: z.object({}).passthrough().optional(),\n testFiles: z.array(z.string()).optional(),\n testFilesWithContent: z.array(z.object({ path: z.string(), content: z.string() })).optional(),\n }),\n },\n async ({ includeContent = false, maxFiles = 3 } = {}) => {\n const structure = detectProjectStructure();\n const collected = collectTestFiles(structure, {\n maxContentFiles: includeContent ? maxFiles : 0,\n });\n\n const testFiles = collected.map((e) => e.path);\n const testFilesWithContent = includeContent\n ? collected.filter((e) => e.content).map((e) => ({ path: e.path, content: e.content }))\n : undefined;\n\n const summary = [\n `Frameworks: ${structure.testFrameworks.join(\", \") || \"nenhum\"}`,\n `Arquivos de teste: ${testFiles.length} (qualquer framework)`,\n `Backend: ${structure.backendDir || \"não detectado\"}`,\n `Frontend: ${structure.frontendDir || \"não detectado\"}`,\n includeContent && testFilesWithContent?.length\n ? `Conteúdo incluído: ${testFilesWithContent.length} arquivo(s) como referência`\n : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n return {\n content: [{ type: \"text\", text: summary }],\n structuredContent: {\n ok: true,\n summary,\n packageJson: structure.packageJson,\n testFiles: testFiles.slice(0, 100),\n testFilesWithContent,\n structure,\n },\n };\n }\n);\n\nserver.registerTool(\n \"generate_tests\",\n {\n title: \"Gerar ou traduzir testes com LLM\",\n description: \"Gera spec em QUALQUER framework. Aceita referência de outro framework: leia com read_file e passe em referenceCode. Traduz automaticamente (ex: Robot→Playwright, Cypress→WDIO).\",\n inputSchema: z.object({\n context: z.string().describe(\"Contexto do projeto (read_project) ou descrição.\"),\n request: z.string().describe(\"O que testar (ex: 'logout flow', 'teste de login') ou 'traduzir o teste abaixo'.\"),\n framework: z.enum([\n \"cypress\", \"playwright\", \"webdriverio\", \"jest\", \"vitest\", \"mocha\",\n \"appium\", \"robot\", \"pytest\", \"supertest\", \"behave\", \"detox\"\n ]).optional().describe(\"Framework alvo (detectado do projeto se omitido).\"),\n referenceCode: z.string().optional().describe(\"Código de referência em QUALQUER framework (Cypress, Robot, WDIO, etc). O LLM traduz/adapta para o framework alvo.\"),\n referencePaths: z.array(z.string()).optional().describe(\"Caminhos de arquivos para ler como referência. O agente lê e usa como padrão.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n specContent: z.string().optional(),\n suggestedFileName: z.string().optional(),\n error: z.string().optional(),\n }),\n },\n async ({ context, request, framework, referenceCode, referencePaths }) => {\n const structure = detectProjectStructure();\n const fw = framework || structure.testFrameworks[0] || \"cypress\";\n\n let referenceBlock = \"\";\n if (referenceCode) referenceBlock += `\\n\\n--- CÓDIGO DE REFERÊNCIA (use como padrão, traduza/adapte para ${fw}) ---\\n${referenceCode.slice(0, 8000)}`;\n if (referencePaths?.length) {\n for (const p of referencePaths.slice(0, 5)) {\n const full = path.join(PROJECT_ROOT, p.replace(/^\\//, \"\").replace(/\\\\/g, \"/\"));\n if (fs.existsSync(full)) {\n try {\n const content = fs.readFileSync(full, \"utf8\");\n referenceBlock += `\\n\\n--- Arquivo: ${p} ---\\n${content.slice(0, 6000)}`;\n } catch {}\n }\n }\n }\n\n const GROQ_KEY = process.env.GROQ_API_KEY;\n const GEMINI_KEY = process.env.GEMINI_API_KEY;\n const OPENAI_KEY = process.env.OPENAI_API_KEY || process.env.QA_LAB_LLM_API_KEY;\n\n if (!GROQ_KEY && !GEMINI_KEY && !OPENAI_KEY) {\n return {\n content: [{ type: \"text\", text: \"Configure GROQ_API_KEY, GEMINI_API_KEY ou OPENAI_API_KEY no .env\" }],\n structuredContent: { ok: false, error: \"No API key configured\" },\n };\n }\n\n const provider = GROQ_KEY ? \"groq\" : GEMINI_KEY ? \"gemini\" : \"openai\";\n const apiKey = GROQ_KEY || GEMINI_KEY || OPENAI_KEY;\n const baseUrl = provider === \"groq\" \n ? \"https://api.groq.com/openai/v1\"\n : provider === \"gemini\"\n ? \"https://generativelanguage.googleapis.com/v1beta\"\n : \"https://api.openai.com/v1\";\n const model = provider === \"groq\"\n ? \"llama-3.3-70b-versatile\"\n : provider === \"gemini\"\n ? \"gemini-1.5-flash\"\n : \"gpt-4o-mini\";\n\n const hasReference = Boolean(referenceBlock?.trim());\n const systemPrompt = hasReference\n ? `Você é um engenheiro de QA. TRADUZA/ADAPTE o código de referência para o framework ${fw}.\nO código de referência pode estar em QUALQUER framework (Cypress, Robot, Playwright, WDIO, Appium, pytest, etc).\n- Mantenha a MESMA lógica e fluxo de teste\n- Traduza seletores, comandos e asserções para ${fw}\n- Use Page Objects se o projeto já usa\n- Retorne SOMENTE o código, sem markdown`\n : `Você é um engenheiro de QA especializado em ${fw}. Gere APENAS o código do spec, sem explicações.\nFramework: ${fw}\nRegras:\n- Cypress: cy.request(), cy.visit(), cy.get()\n- Playwright: test(), test.describe(), page.goto(), page.locator()\n- WebdriverIO/Appium: describe(), it(), $(), browser.$\n- Jest/Vitest: describe(), test(), expect()\n- Robot: Keywords, [Tags], Steps\n- pytest: def test_*, assert, fixtures\n- Código limpo. Retorne SOMENTE o código, sem markdown`;\n\n const userPrompt = `Contexto do projeto:\n${context.slice(0, 5000)}\n\nGere um teste para: ${request}\nFramework alvo: ${fw}${referenceBlock}`;\n\n try {\n let specContent;\n if (provider === \"gemini\") {\n const url = `${baseUrl}/models/${model}:generateContent?key=${apiKey}`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n systemInstruction: { parts: [{ text: systemPrompt }] },\n contents: [{ parts: [{ text: userPrompt }] }],\n generationConfig: { temperature: 0.3, maxOutputTokens: 4096 },\n }),\n });\n const data = await res.json();\n specContent = data.candidates?.[0]?.content?.parts?.[0]?.text || \"\";\n } else {\n const res = await fetch(`${baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: userPrompt },\n ],\n temperature: 0.3,\n max_tokens: 4096,\n }),\n });\n const data = await res.json();\n specContent = data.choices?.[0]?.message?.content || \"\";\n }\n\n specContent = specContent.replace(/^```(?:js|javascript)?\\n?/i, \"\").replace(/\\n?```\\s*$/i, \"\").trim();\n const fileName = request.toLowerCase().replace(/\\s+/g, \"-\").replace(/[^a-z0-9-]/g, \"\").slice(0, 40);\n\n return {\n content: [{ type: \"text\", text: `Spec gerado (${specContent.length} chars). Use write_test para gravar.` }],\n structuredContent: {\n ok: true,\n specContent,\n suggestedFileName: fileName,\n },\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Erro ao gerar: ${err.message}` }],\n structuredContent: { ok: false, error: err.message },\n };\n }\n }\n);\n\nfunction getExtensionAndBaseDir(fw, structure) {\n const extMap = {\n cypress: \".cy.js\",\n playwright: \".spec.js\",\n jest: \".test.js\",\n vitest: \".test.js\",\n mocha: \".test.js\",\n webdriverio: \".spec.js\",\n appium: \".spec.js\",\n detox: \".e2e.js\",\n robot: \".robot\",\n pytest: \"_test.py\",\n behave: \".feature\",\n supertest: \".test.js\",\n pactum: \".test.js\",\n };\n const ext = extMap[fw] || \".spec.js\";\n\n const baseMap = {\n cypress: structure.testDirs.includes(\"cypress\") ? \"cypress\" : structure.testDirs[0] || \"tests\",\n playwright: structure.testDirs.includes(\"playwright\") ? \"playwright\" : structure.testDirs[0] || \"tests\",\n webdriverio: structure.testDirs.includes(\"specs\") ? \"specs\" : structure.testDirs[0] || \"tests\",\n appium: structure.testDirs.includes(\"specs\") ? \"specs\" : structure.testDirs[0] || \"tests\",\n robot: structure.testDirs.includes(\"robot\") ? \"robot\" : structure.testDirs[0] || \"tests\",\n behave: structure.testDirs.includes(\"features\") ? \"features\" : structure.testDirs[0] || \"tests\",\n };\n const baseDir = path.join(PROJECT_ROOT, baseMap[fw] || structure.testDirs[0] || \"tests\");\n return { ext, baseDir };\n}\n\nserver.registerTool(\n \"write_test\",\n {\n title: \"Escrever arquivo de teste\",\n description: \"Grava spec no disco. Suporta QUALQUER framework (Cypress, Playwright, WDIO, Appium, Robot, pytest, etc.). Detecta automaticamente pasta e extensão.\",\n inputSchema: z.object({\n name: z.string().describe(\"Nome do arquivo (ex: login-test, logout_spec).\"),\n content: z.string().describe(\"Conteúdo do spec.\"),\n framework: z.enum([\n \"cypress\", \"playwright\", \"jest\", \"vitest\", \"mocha\", \"webdriverio\",\n \"appium\", \"detox\", \"robot\", \"pytest\", \"behave\", \"supertest\"\n ]).optional().describe(\"Framework (detectado automaticamente se omitido).\"),\n subdir: z.string().optional().describe(\"Subpasta (ex: e2e, api). Default: raiz da pasta de testes.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n path: z.string().optional(),\n error: z.string().optional(),\n }),\n },\n async ({ name, content, framework, subdir }) => {\n const structure = detectProjectStructure();\n const fw = framework || structure.testFrameworks[0];\n\n if (!fw) {\n return {\n content: [{ type: \"text\", text: \"Nenhum framework de teste detectado.\" }],\n structuredContent: { ok: false, error: \"No test framework\" },\n };\n }\n\n const { ext, baseDir } = getExtensionAndBaseDir(fw, structure);\n const safeName = name\n .replace(/[^a-z0-9-_]/gi, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/_+/g, \"_\")\n .replace(/\\.(cy|spec|test|robot|feature|py)\\.?(js|ts|py)?$/i, \"\")\n .replace(/^[-_]+|[-_]+$/g, \"\");\n const fileName = ext.startsWith(\"_\") ? `${safeName}${ext}` : `${safeName}${ext}`;\n\n const targetDir = subdir ? path.join(baseDir, subdir) : baseDir;\n const filePath = path.join(targetDir, fileName);\n\n try {\n if (!fs.existsSync(targetDir)) {\n fs.mkdirSync(targetDir, { recursive: true });\n }\n fs.writeFileSync(filePath, content, \"utf8\");\n return {\n content: [{ type: \"text\", text: `Arquivo gravado: ${filePath}` }],\n structuredContent: { ok: true, path: filePath },\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Erro ao gravar: ${err.message}` }],\n structuredContent: { ok: false, error: err.message },\n };\n }\n }\n);\n\nserver.registerTool(\n \"analyze_failures\",\n {\n title: \"Analisar falhas de testes\",\n description: \"Recebe output de testes e extrai falhas estruturadas.\",\n inputSchema: z.object({\n runOutput: z.string().describe(\"Output do teste (stdout/stderr).\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n summary: z.string(),\n failures: z.array(z.object({\n test: z.string().optional(),\n message: z.string().optional(),\n stack: z.string().optional(),\n })).optional(),\n }),\n },\n async ({ runOutput }) => {\n const failures = [];\n const lines = runOutput.split(\"\\n\");\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (/fail|error|assertion/i.test(line)) {\n failures.push({\n test: lines[i - 1] || \"unknown\",\n message: line.trim(),\n stack: lines.slice(i, i + 5).join(\"\\n\"),\n });\n }\n }\n\n const summary = failures.length\n ? `${failures.length} falha(s) detectada(s).`\n : \"Nenhuma falha detectada.\";\n\n return {\n content: [{ type: \"text\", text: summary }],\n structuredContent: { ok: true, summary, failures: failures.length ? failures : undefined },\n };\n }\n);\n\n// ============================================================================\n// POR QUE FALHOU? - Explicação de falhas para juniores (escalável)\n// ============================================================================\n\nfunction formatFailureExplanation(data) {\n const lines = [\n \"## O que aconteceu\",\n \"\",\n data.oQueAconteceu || \"\",\n \"\",\n \"## Por que provavelmente falhou\",\n \"\",\n ...(Array.isArray(data.porQueProvavelmenteFalhou)\n ? data.porQueProvavelmenteFalhou.map((s) => `• ${s}`)\n : [data.porQueProvavelmenteFalhou || \"\"]),\n \"\",\n \"## O que fazer agora\",\n \"\",\n ...(Array.isArray(data.oQueFazerAgora)\n ? data.oQueFazerAgora.map((s, i) => `${i + 1}. ${s}`)\n : [data.oQueFazerAgora || \"\"]),\n ];\n if (data.sugestaoCorrecao) {\n lines.push(\"\", \"## Sugestão de correção\", \"\", \"```\" + (data.framework || \"js\"), data.sugestaoCorrecao, \"```\");\n }\n if (data.conceito) {\n lines.push(\"\", \"## Conceito\", \"\", data.conceito);\n }\n return lines.filter(Boolean).join(\"\\n\");\n}\n\nasync function callLlmForExplanation(provider, apiKey, baseUrl, model, systemPrompt, userPrompt) {\n if (provider === \"gemini\") {\n const url = `${baseUrl}/models/${model}:generateContent?key=${apiKey}`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n contents: [{ parts: [{ text: systemPrompt + \"\\n\\n\" + userPrompt }] }],\n generationConfig: { temperature: 0.2, maxOutputTokens: 4096 },\n }),\n });\n const data = await res.json();\n return data.candidates?.[0]?.content?.parts?.[0]?.text || \"\";\n }\n const res = await fetch(`${baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: userPrompt },\n ],\n temperature: 0.2,\n max_tokens: 4096,\n }),\n });\n const data = await res.json();\n return data.choices?.[0]?.message?.content || \"\";\n}\n\n/** Gera explicação de falha (o que aconteceu, por que, o que fazer, sugestão). Usado por por_que_falhou e run_tests_and_explain. */\nasync function generateFailureExplanation(resolvedOutput, testFilePath = null) {\n const structure = detectProjectStructure();\n const fw = structure.testFrameworks[0] || \"unknown\";\n\n let testCode = \"\";\n if (testFilePath) {\n const normalized = testFilePath.replace(/^\\//, \"\").replace(/\\\\/g, \"/\");\n const fullPath = path.join(PROJECT_ROOT, normalized);\n if (fs.existsSync(fullPath) && !fs.statSync(fullPath).isDirectory()) {\n try {\n testCode = fs.readFileSync(fullPath, \"utf8\");\n } catch {}\n }\n }\n\n const GROQ_KEY = process.env.GROQ_API_KEY;\n const GEMINI_KEY = process.env.GEMINI_API_KEY;\n const OPENAI_KEY = process.env.OPENAI_API_KEY || process.env.QA_LAB_LLM_API_KEY;\n if (!GROQ_KEY && !GEMINI_KEY && !OPENAI_KEY) {\n return { ok: false, error: \"No API key\", formattedText: null };\n }\n\n const provider = GROQ_KEY ? \"groq\" : GEMINI_KEY ? \"gemini\" : \"openai\";\n const apiKey = GROQ_KEY || GEMINI_KEY || OPENAI_KEY;\n const baseUrl = provider === \"groq\"\n ? \"https://api.groq.com/openai/v1\"\n : provider === \"gemini\"\n ? \"https://generativelanguage.googleapis.com/v1beta\"\n : \"https://api.openai.com/v1\";\n const model = provider === \"groq\"\n ? \"llama-3.3-70b-versatile\"\n : provider === \"gemini\"\n ? \"gemini-1.5-flash\"\n : \"gpt-4o-mini\";\n\n const fwHints = {\n webdriverio: \"WebdriverIO (describe/it, $, browser.$)\",\n appium: \"Appium/WebdriverIO (mobile, $, browser.$)\",\n playwright: \"Playwright (test, page, locator)\",\n cypress: \"Cypress (cy.get, cy.click)\",\n jest: \"Jest (describe, test, expect)\",\n vitest: \"Vitest (describe, test, expect)\",\n robot: \"Robot Framework\",\n pytest: \"pytest\",\n };\n\n const systemPrompt = `Você é um mentor de QA. Analise o output de falha e responda em JSON (apenas o JSON, sem markdown) com as chaves:\n- oQueAconteceu: string (explicação em português do que aconteceu, simples)\n- porQueProvavelmenteFalhou: array de strings (lista de possíveis causas, uma por item)\n- oQueFazerAgora: array de strings (passos numerados do que fazer)\n- sugestaoCorrecao: string ou null (código de correção se aplicável, no formato do framework)\n- conceito: string ou null (ex: \"Flaky test = teste intermitente. Geralmente por timing ou seletores frágeis.\")\n- framework: string (framework do projeto)\n\nFramework do projeto: ${fw}. ${fwHints[fw] || \"\"}\nResponda APENAS com o JSON válido, sem texto antes ou depois.`;\n\n const userPrompt = `Output do terminal/log (teste falhou):\n---\n${resolvedOutput.slice(0, 12000)}\n---\n${testCode ? `\\nCódigo do teste que falhou:\\n---\\n${testCode.slice(0, 6000)}\\n---` : \"\"}`;\n\n try {\n let raw = await callLlmForExplanation(provider, apiKey, baseUrl, model, systemPrompt, userPrompt);\n raw = raw.replace(/^```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\").trim();\n let data = {};\n try {\n data = JSON.parse(raw);\n } catch {\n data = {\n oQueAconteceu: raw.slice(0, 500) || \"Não foi possível parsear a resposta.\",\n porQueProvavelmenteFalhou: [],\n oQueFazerAgora: [],\n sugestaoCorrecao: null,\n conceito: null,\n framework: fw,\n };\n }\n data.framework = data.framework || fw;\n const formattedText = formatFailureExplanation(data);\n return { ok: true, formattedText, structuredContent: data };\n } catch (err) {\n return { ok: false, error: err.message, formattedText: null };\n }\n}\n\nserver.registerTool(\n \"por_que_falhou\",\n {\n title: \"Por que falhou? Explicação para juniores\",\n description: \"Traduz stack trace em explicação humana. Recebe output do terminal/log, lê o projeto e o teste (se path dado), e retorna: O que aconteceu, Por que falhou, O que fazer, Sugestão de correção, Conceito. Escalável e procedural.\",\n inputSchema: z.object({\n errorOutput: z.string().optional().describe(\"Output do terminal quando o teste falhou. Se vazio, lê automaticamente de .qa-lab-last-failure.log (capturado pelo run_tests). Cole aqui ou deixe vazio para usar última falha.\"),\n testFilePath: z.string().optional().describe(\"Caminho do arquivo de teste que falhou (ex: specs/login.spec.js). Se informado, o agente lê o código e dá sugestão mais precisa.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n oQueAconteceu: z.string().optional(),\n porQueProvavelmenteFalhou: z.array(z.string()).optional(),\n oQueFazerAgora: z.array(z.string()).optional(),\n sugestaoCorrecao: z.string().optional(),\n conceito: z.string().optional(),\n framework: z.string().optional(),\n formattedText: z.string().optional(),\n error: z.string().optional(),\n }),\n },\n async ({ errorOutput, testFilePath }) => {\n const structure = detectProjectStructure();\n const fw = structure.testFrameworks[0] || \"unknown\";\n\n let resolvedOutput = errorOutput?.trim() || \"\";\n if (!resolvedOutput) {\n const lastFailurePath = path.join(PROJECT_ROOT, \".qa-lab-last-failure.log\");\n if (fs.existsSync(lastFailurePath)) {\n try {\n resolvedOutput = fs.readFileSync(lastFailurePath, \"utf8\");\n } catch {}\n }\n }\n if (!resolvedOutput) {\n return {\n content: [{\n type: \"text\",\n text: \"Nenhum output de erro fornecido e nenhuma falha recente capturada.\\n\\nComo usar:\\n1. Rode os testes (run_tests) – se falhar, a saída é salva automaticamente.\\n2. Ou cole aqui o output do terminal quando o teste falhou.\\n3. Depois peça: 'Por que falhou?' ou chame por_que_falhou.\",\n }],\n structuredContent: { ok: false, error: \"No error output\" },\n };\n }\n\n let testCode = \"\";\n if (testFilePath) {\n const normalized = testFilePath.replace(/^\\//, \"\").replace(/\\\\/g, \"/\");\n const fullPath = path.join(PROJECT_ROOT, normalized);\n if (fs.existsSync(fullPath) && !fs.statSync(fullPath).isDirectory()) {\n try {\n testCode = fs.readFileSync(fullPath, \"utf8\");\n } catch {}\n }\n }\n\n const GROQ_KEY = process.env.GROQ_API_KEY;\n const GEMINI_KEY = process.env.GEMINI_API_KEY;\n const OPENAI_KEY = process.env.OPENAI_API_KEY || process.env.QA_LAB_LLM_API_KEY;\n\n if (!GROQ_KEY && !GEMINI_KEY && !OPENAI_KEY) {\n return {\n content: [{\n type: \"text\",\n text: \"Configure GROQ_API_KEY, GEMINI_API_KEY ou OPENAI_API_KEY no .env do projeto para usar a explicação com LLM.\",\n }],\n structuredContent: { ok: false, error: \"No API key configured\" },\n };\n }\n\n const provider = GROQ_KEY ? \"groq\" : GEMINI_KEY ? \"gemini\" : \"openai\";\n const apiKey = GROQ_KEY || GEMINI_KEY || OPENAI_KEY;\n const baseUrl = provider === \"groq\"\n ? \"https://api.groq.com/openai/v1\"\n : provider === \"gemini\"\n ? \"https://generativelanguage.googleapis.com/v1beta\"\n : \"https://api.openai.com/v1\";\n const model = provider === \"groq\"\n ? \"llama-3.3-70b-versatile\"\n : provider === \"gemini\"\n ? \"gemini-1.5-flash\"\n : \"gpt-4o-mini\";\n\n const fwHints = {\n webdriverio: \"WebdriverIO (describe/it, $, browser.$)\",\n appium: \"Appium/WebdriverIO (mobile, $, browser.$)\",\n playwright: \"Playwright (test, page, locator)\",\n cypress: \"Cypress (cy.get, cy.click)\",\n jest: \"Jest (describe, test, expect)\",\n vitest: \"Vitest (describe, test, expect)\",\n robot: \"Robot Framework\",\n pytest: \"pytest\",\n };\n\n const systemPrompt = `Você é um mentor de QA. Analise o output de falha e responda em JSON (apenas o JSON, sem markdown) com as chaves:\n- oQueAconteceu: string (explicação em português do que aconteceu, simples)\n- porQueProvavelmenteFalhou: array de strings (lista de possíveis causas, uma por item)\n- oQueFazerAgora: array de strings (passos numerados do que fazer)\n- sugestaoCorrecao: string ou null (código de correção se aplicável, no formato do framework)\n- conceito: string ou null (ex: \"Flaky test = teste intermitente. Geralmente por timing ou seletores frágeis.\")\n- framework: string (framework do projeto)\n\nFramework do projeto: ${fw}. ${fwHints[fw] || \"\"}\nResponda APENAS com o JSON válido, sem texto antes ou depois.`;\n\n const userPrompt = `Output do terminal/log (teste falhou):\n---\n${resolvedOutput.slice(0, 12000)}\n---\n${testCode ? `\\nCódigo do teste que falhou:\\n---\\n${testCode.slice(0, 6000)}\\n---` : \"\"}`;\n\n try {\n let raw = await callLlmForExplanation(provider, apiKey, baseUrl, model, systemPrompt, userPrompt);\n raw = raw.replace(/^```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\").trim();\n\n let data = {};\n try {\n data = JSON.parse(raw);\n } catch {\n data = {\n oQueAconteceu: raw.slice(0, 500) || \"Não foi possível parsear a resposta.\",\n porQueProvavelmenteFalhou: [],\n oQueFazerAgora: [],\n sugestaoCorrecao: null,\n conceito: null,\n framework: fw,\n };\n }\n\n data.framework = data.framework || fw;\n const formattedText = formatFailureExplanation(data);\n\n return {\n content: [{ type: \"text\", text: formattedText }],\n structuredContent: {\n ok: true,\n oQueAconteceu: data.oQueAconteceu,\n porQueProvavelmenteFalhou: data.porQueProvavelmenteFalhou,\n oQueFazerAgora: data.oQueFazerAgora,\n sugestaoCorrecao: data.sugestaoCorrecao ?? null,\n conceito: data.conceito ?? null,\n framework: data.framework,\n formattedText,\n },\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Erro ao analisar: ${err.message}` }],\n structuredContent: { ok: false, error: err.message },\n };\n }\n }\n);\n\n// ============================================================================\n// NOVAS FERRAMENTAS\n// ============================================================================\n\nserver.registerTool(\n \"suggest_fix\",\n {\n title: \"Sugerir correção para falhas\",\n description: \"Recebe análise de falhas e sugere correções (patch, refactor, etc.).\",\n inputSchema: z.object({\n failures: z.array(z.object({\n test: z.string().optional(),\n message: z.string().optional(),\n stack: z.string().optional(),\n })).describe(\"Resultado de analyze_failures.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n suggestions: z.array(z.object({\n test: z.string().optional(),\n description: z.string(),\n fix: z.string().optional(),\n })),\n }),\n },\n async ({ failures }) => {\n const suggestions = [];\n\n for (const f of failures) {\n const msg = f.message || \"\";\n \n if (/element not found|selector|timeout/i.test(msg)) {\n suggestions.push({\n test: f.test,\n description: \"Elemento não encontrado ou timeout\",\n fix: \"Verifique seletores, adicione waits ou aumente timeout. Use data-testid para seletores mais estáveis.\",\n });\n } else if (/expected.*to.*but/i.test(msg)) {\n suggestions.push({\n test: f.test,\n description: \"Asserção falhou\",\n fix: \"Revise o valor esperado. Verifique se o estado da aplicação está correto antes da asserção.\",\n });\n } else if (/network|fetch|ECONNREFUSED/i.test(msg)) {\n suggestions.push({\n test: f.test,\n description: \"Erro de rede ou API não disponível\",\n fix: \"Verifique se o backend está rodando. Confirme a URL e porta da API.\",\n });\n } else {\n suggestions.push({\n test: f.test,\n description: \"Falha detectada\",\n fix: \"Revise o stack trace e o código do teste.\",\n });\n }\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(suggestions, null, 2) }],\n structuredContent: { ok: true, suggestions },\n };\n }\n);\n\n// ============================================================================\n// SELF-HEALING - Sugestão de correção de seletores quando UI muda\n// ============================================================================\n\nserver.registerTool(\n \"suggest_selector_fix\",\n {\n title: \"Sugerir correção de seletor (Self-healing)\",\n description: \"Quando um teste falha por elemento não encontrado (seletor quebrado após mudança de UI), usa LLM para sugerir seletor alternativo mais resiliente. Prioriza data-testid, role, texto acessível.\",\n inputSchema: z.object({\n testFilePath: z.string().describe(\"Caminho do arquivo de teste que falhou (ex: specs/login.spec.js).\"),\n errorOutput: z.string().optional().describe(\"Output do terminal da falha. Se vazio, lê de .qa-lab-last-failure.log.\"),\n framework: z.enum([\"cypress\", \"playwright\", \"webdriverio\", \"appium\"]).optional().describe(\"Framework do teste. Detectado automaticamente se omitido.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n selectorSugerido: z.string().optional(),\n codigoCorrigido: z.string().optional(),\n explicacao: z.string().optional(),\n error: z.string().optional(),\n }),\n },\n async ({ testFilePath, errorOutput, framework }) => {\n const structure = detectProjectStructure();\n const fw = framework || inferFrameworkFromFile(testFilePath.split(\"/\").pop(), structure);\n\n let resolvedOutput = errorOutput;\n if (!resolvedOutput) {\n const logPath = path.join(PROJECT_ROOT, \".qa-lab-last-failure.log\");\n if (fs.existsSync(logPath)) {\n resolvedOutput = fs.readFileSync(logPath, \"utf8\");\n }\n }\n if (!resolvedOutput) {\n return {\n content: [{ type: \"text\", text: \"Nenhum output de erro. Rode os testes primeiro ou forneça errorOutput.\" }],\n structuredContent: { ok: false, error: \"No error output\" },\n };\n }\n\n if (!/element not found|selector|timeout|locator|cy\\.get|page\\.locator/i.test(resolvedOutput)) {\n return {\n content: [{ type: \"text\", text: \"A falha não parece ser de seletor/elemento. Use por_que_falhou ou suggest_fix para outros tipos de falha.\" }],\n structuredContent: { ok: false, error: \"Not a selector-related failure\" },\n };\n }\n\n let testCode = \"\";\n const fullPath = path.join(PROJECT_ROOT, testFilePath.replace(/^\\//, \"\").replace(/\\\\/g, \"/\"));\n if (fs.existsSync(fullPath)) {\n try {\n testCode = fs.readFileSync(fullPath, \"utf8\");\n } catch {}\n }\n\n const GROQ_KEY = process.env.GROQ_API_KEY;\n const GEMINI_KEY = process.env.GEMINI_API_KEY;\n const OPENAI_KEY = process.env.OPENAI_API_KEY || process.env.QA_LAB_LLM_API_KEY;\n if (!GROQ_KEY && !GEMINI_KEY && !OPENAI_KEY) {\n return {\n content: [{ type: \"text\", text: \"Configure GROQ_API_KEY, GEMINI_API_KEY ou OPENAI_API_KEY no .env\" }],\n structuredContent: { ok: false, error: \"No API key configured\" },\n };\n }\n\n const provider = GROQ_KEY ? \"groq\" : GEMINI_KEY ? \"gemini\" : \"openai\";\n const apiKey = GROQ_KEY || GEMINI_KEY || OPENAI_KEY;\n const baseUrl = provider === \"groq\" ? \"https://api.groq.com/openai/v1\" : provider === \"gemini\" ? \"https://generativelanguage.googleapis.com/v1beta\" : \"https://api.openai.com/v1\";\n const model = provider === \"groq\" ? \"llama-3.3-70b-versatile\" : provider === \"gemini\" ? \"gemini-1.5-flash\" : \"gpt-4o-mini\";\n\n const fwHints = {\n cypress: \"Cypress: cy.get('[data-testid=...]'), cy.contains(), cy.get('button').filter(':visible')\",\n playwright: \"Playwright: page.getByRole(), page.getByTestId(), page.locator('button:has-text(\\\"...\\\")')\",\n webdriverio: \"WebdriverIO: $('[data-testid=...]'), $('button=Texto')\",\n appium: \"Appium: $('~accessibility-id'), $('//android.view.View')\",\n };\n\n const systemPrompt = `Você é um especialista em testes E2E. O teste falhou porque um seletor não encontrou o elemento (UI mudou).\nAnalise o erro e o código e responda APENAS em JSON (sem markdown) com as chaves:\n- selectorSugerido: string (o novo seletor recomendado, mais resiliente)\n- codigoCorrigido: string (bloco de código completo corrigido, apenas a parte relevante do teste)\n- explicacao: string (breve explicação em português: por que o antigo falhou e por que o novo é melhor)\n\nPriorize nesta ordem: data-testid > role + accessible name > texto visível > estrutura. Evite classes CSS e IDs que mudam.\n\nFramework: ${fw}. ${fwHints[fw] || \"\"}`;\n\n const userPrompt = `Output do erro:\n---\n${resolvedOutput.slice(0, 8000)}\n---\nCódigo do teste:\n---\n${testCode ? testCode.slice(0, 6000) : \"Não disponível\"}\n---`;\n\n try {\n let raw = await callLlmForExplanation(provider, apiKey, baseUrl, model, systemPrompt, userPrompt);\n raw = raw.replace(/^```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\").trim();\n let data = {};\n try {\n data = JSON.parse(raw);\n } catch {\n data = {\n selectorSugerido: null,\n codigoCorrigido: raw.slice(0, 2000),\n explicacao: \"Não foi possível parsear. Resposta do LLM acima.\",\n };\n }\n\n const text = [\n data.explicacao && `## Explicação\\n${data.explicacao}`,\n data.selectorSugerido && `## Seletor sugerido\\n\\`${data.selectorSugerido}\\``,\n data.codigoCorrigido && `## Código corrigido\\n\\`\\`\\`${fw}\\n${data.codigoCorrigido}\\n\\`\\`\\``,\n ]\n .filter(Boolean)\n .join(\"\\n\\n\");\n\n return {\n content: [{ type: \"text\", text: text || JSON.stringify(data, null, 2) }],\n structuredContent: {\n ok: true,\n selectorSugerido: data.selectorSugerido,\n codigoCorrigido: data.codigoCorrigido,\n explicacao: data.explicacao,\n },\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Erro ao chamar LLM: ${err.message}` }],\n structuredContent: { ok: false, error: err.message },\n };\n }\n }\n);\n\nserver.registerTool(\n \"create_bug_report\",\n {\n title: \"Criar relatório de bug\",\n description: \"Gera um bug report estruturado a partir de falhas de teste.\",\n inputSchema: z.object({\n failures: z.array(z.object({\n test: z.string().optional(),\n message: z.string().optional(),\n stack: z.string().optional(),\n })).describe(\"Falhas (de analyze_failures).\"),\n title: z.string().optional().describe(\"Título do bug.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n report: z.string(),\n title: z.string(),\n }),\n },\n async ({ failures, title }) => {\n const bugTitle = title || `Falha em ${failures.length} teste(s)`;\n const lines = [\n `# ${bugTitle}`,\n \"\",\n \"## Resumo\",\n \"\",\n `${failures.length} teste(s) falharam durante a execução.`,\n \"\",\n \"## Falhas detectadas\",\n \"\",\n ];\n\n failures.forEach((f, i) => {\n lines.push(`### ${i + 1}. ${f.test || \"Teste desconhecido\"}`);\n lines.push(\"\");\n lines.push(`**Mensagem:** ${f.message || \"N/A\"}`);\n lines.push(\"\");\n if (f.stack) {\n lines.push(\"**Stack trace:**\");\n lines.push(\"```\");\n lines.push(f.stack);\n lines.push(\"```\");\n lines.push(\"\");\n }\n });\n\n lines.push(\"## Próximos passos\");\n lines.push(\"\");\n lines.push(\"- [ ] Reproduzir localmente\");\n lines.push(\"- [ ] Identificar causa raiz\");\n lines.push(\"- [ ] Aplicar correção\");\n lines.push(\"- [ ] Validar com testes\");\n\n const report = lines.join(\"\\n\");\n\n appendMetricsEvent({ type: \"bug_reported\", failuresCount: failures.length, title: bugTitle });\n\n return {\n content: [{ type: \"text\", text: report }],\n structuredContent: { ok: true, report, title: bugTitle },\n };\n }\n);\n\n// ============================================================================\n// MÉTRICAS DE NEGÓCIO - Relatório agregado\n// ============================================================================\n\nserver.registerTool(\n \"get_business_metrics\",\n {\n title: \"Obter métricas de negócio\",\n description: \"Retorna métricas: tempo até bug, custo por defeito (tempo estimado), cobertura por fluxo. Requer run_tests executados e opcionalmente qa-lab-flows.json.\",\n inputSchema: z.object({\n period: z.enum([\"7d\", \"30d\", \"all\"]).optional().describe(\"Período para analisar. Default: 30d.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n timeToBug: z.object({\n avgHours: z.number(),\n lastFailureAt: z.string().optional(),\n runsWithFailures: z.number(),\n }).optional(),\n costPerDefect: z.object({\n avgMinutesPerDefect: z.number(),\n totalFailures: z.number(),\n estimatedHoursSpent: z.number(),\n }).optional(),\n flowCoverage: z.object({\n totalFlows: z.number(),\n coveredFlows: z.number(),\n percent: z.number(),\n details: z.array(z.object({ flow: z.string(), covered: z.boolean() })),\n }).optional(),\n summary: z.string(),\n }),\n },\n async ({ period = \"30d\" } = {}) => {\n const now = Date.now();\n const msByPeriod = { \"7d\": 7 * 24 * 60 * 60 * 1000, \"30d\": 30 * 24 * 60 * 60 * 1000, all: Infinity };\n const cutoff = now - msByPeriod[period];\n\n let data = { events: [] };\n if (fs.existsSync(METRICS_FILE)) {\n try {\n data = JSON.parse(fs.readFileSync(METRICS_FILE, \"utf8\"));\n } catch {}\n }\n\n const events = (data.events || []).filter((e) => new Date(e.timestamp).getTime() >= cutoff);\n\n const testRuns = events.filter((e) => e.type === \"test_run\");\n const failedRuns = testRuns.filter((e) => (e.failed || 0) > 0);\n const totalFailed = testRuns.reduce((sum, e) => sum + (e.failed || 0), 0);\n const totalDuration = testRuns.reduce((sum, e) => sum + (e.durationSeconds || 0), 0);\n\n let timeToBug = null;\n if (failedRuns.length > 0) {\n const lastFailure = failedRuns[failedRuns.length - 1];\n timeToBug = {\n avgHours: 0,\n lastFailureAt: lastFailure.timestamp,\n runsWithFailures: failedRuns.length,\n };\n if (failedRuns.length >= 2) {\n const deltas = [];\n for (let i = 1; i < failedRuns.length; i++) {\n const prev = new Date(failedRuns[i - 1].timestamp).getTime();\n const curr = new Date(failedRuns[i].timestamp).getTime();\n deltas.push((curr - prev) / (1000 * 60 * 60));\n }\n timeToBug.avgHours = deltas.reduce((a, b) => a + b, 0) / deltas.length;\n }\n }\n\n let costPerDefect = null;\n if (totalFailed > 0) {\n const estimatedMinutesSpent = totalDuration + totalFailed * 5;\n costPerDefect = {\n avgMinutesPerDefect: Math.round(estimatedMinutesSpent / totalFailed),\n totalFailures: totalFailed,\n estimatedHoursSpent: Math.round((estimatedMinutesSpent / 60) * 10) / 10,\n };\n }\n\n let flowCoverage = null;\n if (fs.existsSync(FLOWS_CONFIG_FILE)) {\n try {\n const flowsConfig = JSON.parse(fs.readFileSync(FLOWS_CONFIG_FILE, \"utf8\"));\n const flows = flowsConfig.flows || [];\n const structure = detectProjectStructure();\n const allTestFiles = new Set(collectTestFiles(structure).map((e) => e.path));\n\n const details = flows.map((f) => {\n const testFiles = f.testFiles || [];\n const covered = testFiles.some((tf) => allTestFiles.has(tf) || allTestFiles.has(tf.replace(/\\\\/g, \"/\")));\n return { flow: f.name || f.id || \"?\", covered };\n });\n\n flowCoverage = {\n totalFlows: flows.length,\n coveredFlows: details.filter((d) => d.covered).length,\n percent: flows.length ? Math.round((details.filter((d) => d.covered).length / flows.length) * 100) : 0,\n details,\n };\n } catch {}\n }\n\n const lines = [\n \"## Métricas de negócio\",\n \"\",\n `Período: ${period}`,\n \"\",\n timeToBug\n ? [\n \"### Tempo até bug\",\n `- Última falha: ${timeToBug.lastFailureAt || \"N/A\"}`,\n `- Execuções com falha: ${timeToBug.runsWithFailures}`,\n timeToBug.avgHours > 0 ? `- Média entre falhas: ${timeToBug.avgHours.toFixed(1)}h` : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\")\n : \"\",\n costPerDefect\n ? [\n \"### Custo por defeito (estimativa)\",\n `- Total de falhas: ${costPerDefect.totalFailures}`,\n `- Tempo médio por defeito: ~${costPerDefect.avgMinutesPerDefect} min`,\n `- Horas estimadas gastas: ${costPerDefect.estimatedHoursSpent}h`,\n ].join(\"\\n\")\n : \"\",\n flowCoverage\n ? [\n \"### Cobertura por fluxo\",\n `- Fluxos cobertos: ${flowCoverage.coveredFlows}/${flowCoverage.totalFlows} (${flowCoverage.percent}%)`,\n flowCoverage.details.map((d) => ` - ${d.flow}: ${d.covered ? \"✅\" : \"❌\"}`).join(\"\\n\"),\n ].join(\"\\n\")\n : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\\n\");\n\n if (!timeToBug && !costPerDefect && !flowCoverage) {\n const msg =\n \"Nenhuma métrica disponível. Rode run_tests para gerar dados. Para cobertura por fluxo, crie qa-lab-flows.json.\";\n return {\n content: [{ type: \"text\", text: msg }],\n structuredContent: { ok: false, summary: msg },\n };\n }\n\n const summary = [\n timeToBug && `${timeToBug.runsWithFailures} execuções com falha`,\n costPerDefect && `${costPerDefect.totalFailures} falhas (~${costPerDefect.avgMinutesPerDefect} min/defeito)`,\n flowCoverage && `${flowCoverage.coveredFlows}/${flowCoverage.totalFlows} fluxos cobertos`,\n ]\n .filter(Boolean)\n .join(\" | \");\n\n return {\n content: [{ type: \"text\", text: lines || summary }],\n structuredContent: {\n ok: true,\n timeToBug,\n costPerDefect,\n flowCoverage,\n summary,\n },\n };\n }\n);\n\nserver.registerTool(\n \"list_test_files\",\n {\n title: \"Listar arquivos de teste\",\n description: \"Lista TODOS os arquivos de teste (qualquer framework: Cypress, Playwright, WDIO, Robot, pytest, Behave, etc.) com filtro opcional.\",\n inputSchema: z.object({\n framework: z.enum([\n \"cypress\", \"playwright\", \"jest\", \"webdriverio\", \"appium\", \"robot\", \"pytest\", \"behave\", \"detox\", \"all\"\n ]).optional().describe(\"Filtrar por framework. Default: all.\"),\n pattern: z.string().optional().describe(\"Pattern para filtrar (ex: 'login', 'api').\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n files: z.array(z.string()),\n total: z.number(),\n }),\n },\n async ({ framework = \"all\", pattern } = {}) => {\n const structure = detectProjectStructure();\n const collected = collectTestFiles(structure, { framework, pattern });\n const allFiles = collected.map((e) => e.path);\n\n const summary = `Encontrados ${allFiles.length} arquivo(s) de teste (qualquer framework).`;\n\n return {\n content: [{ type: \"text\", text: `${summary}\\n\\n${allFiles.slice(0, 50).join(\"\\n\")}` }],\n structuredContent: { ok: true, files: allFiles, total: allFiles.length },\n };\n }\n);\n\nserver.registerTool(\n \"run_linter\",\n {\n title: \"Executar linter\",\n description: \"Roda ESLint, Prettier ou linter configurado no projeto.\",\n inputSchema: z.object({\n fix: z.boolean().optional().describe(\"Auto-fix (--fix). Default: false.\"),\n path: z.string().optional().describe(\"Caminho específico (ex: src/). Default: todo o projeto.\"),\n }),\n outputSchema: z.object({\n status: z.enum([\"passed\", \"failed\", \"not_found\"]),\n message: z.string(),\n exitCode: z.number(),\n output: z.string().optional(),\n }),\n },\n async ({ fix, path: targetPath }) => {\n const structure = detectProjectStructure();\n const scripts = structure.packageJson?.scripts || {};\n\n let cmd, args;\n if (scripts.lint) {\n cmd = \"npm\";\n args = [\"run\", \"lint\"];\n } else if (structure.packageJson?.devDependencies?.eslint || structure.packageJson?.dependencies?.eslint) {\n cmd = \"npx\";\n args = [\"eslint\", targetPath || \".\"];\n if (fix) args.push(\"--fix\");\n } else {\n return {\n content: [{ type: \"text\", text: \"Linter não detectado no projeto.\" }],\n structuredContent: { status: \"not_found\", message: \"No linter found\", exitCode: 1 },\n };\n }\n\n return new Promise((resolve) => {\n const child = spawn(cmd, args, {\n cwd: PROJECT_ROOT,\n stdio: [\"inherit\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n env: { ...process.env },\n });\n\n let stdout = \"\";\n let stderr = \"\";\n if (child.stdout) child.stdout.on(\"data\", (d) => { stdout += d.toString(); });\n if (child.stderr) child.stderr.on(\"data\", (d) => { stderr += d.toString(); });\n\n child.on(\"close\", (code) => {\n const output = [stdout, stderr].filter(Boolean).join(\"\\n\").trim();\n const passed = code === 0;\n resolve({\n content: [{ type: \"text\", text: passed ? \"Linter passou.\" : \"Linter encontrou problemas.\" }],\n structuredContent: {\n status: passed ? \"passed\" : \"failed\",\n message: passed ? \"Lint passed\" : \"Lint failed\",\n exitCode: code ?? 1,\n output: !passed ? output : undefined,\n },\n });\n });\n });\n }\n);\n\nserver.registerTool(\n \"install_dependencies\",\n {\n title: \"Instalar dependências\",\n description: \"Roda npm install, yarn install ou pnpm install (detecta automaticamente).\",\n inputSchema: z.object({\n packageManager: z.enum([\"npm\", \"yarn\", \"pnpm\", \"auto\"]).optional().describe(\"Package manager. Default: auto.\"),\n }),\n outputSchema: z.object({\n status: z.enum([\"success\", \"failed\"]),\n message: z.string(),\n exitCode: z.number(),\n }),\n },\n async ({ packageManager = \"auto\" }) => {\n let pm = packageManager;\n \n if (pm === \"auto\") {\n if (fs.existsSync(path.join(PROJECT_ROOT, \"yarn.lock\"))) pm = \"yarn\";\n else if (fs.existsSync(path.join(PROJECT_ROOT, \"pnpm-lock.yaml\"))) pm = \"pnpm\";\n else pm = \"npm\";\n }\n\n return new Promise((resolve) => {\n const child = spawn(pm, [\"install\"], {\n cwd: PROJECT_ROOT,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n env: { ...process.env },\n });\n\n child.on(\"close\", (code) => {\n const passed = code === 0;\n resolve({\n content: [{ type: \"text\", text: passed ? \"Dependências instaladas.\" : \"Erro ao instalar dependências.\" }],\n structuredContent: {\n status: passed ? \"success\" : \"failed\",\n message: passed ? \"Dependencies installed\" : \"Install failed\",\n exitCode: code ?? 1,\n },\n });\n });\n });\n }\n);\n\nserver.registerTool(\n \"get_test_coverage\",\n {\n title: \"Obter cobertura de testes\",\n description: \"Roda testes com coverage (Jest, Playwright, Cypress com plugin).\",\n inputSchema: z.object({\n framework: z.enum([\"jest\", \"playwright\", \"cypress\"]).optional().describe(\"Framework. Default: detectado automaticamente.\"),\n }),\n outputSchema: z.object({\n status: z.enum([\"success\", \"failed\", \"not_supported\"]),\n message: z.string(),\n coveragePercent: z.number().optional(),\n output: z.string().optional(),\n }),\n },\n async ({ framework }) => {\n const structure = detectProjectStructure();\n const fw = framework || structure.testFrameworks[0];\n\n if (fw === \"jest\") {\n return new Promise((resolve) => {\n const child = spawn(\"npx\", [\"jest\", \"--coverage\"], {\n cwd: PROJECT_ROOT,\n stdio: [\"inherit\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n env: { ...process.env },\n });\n\n let stdout = \"\";\n if (child.stdout) child.stdout.on(\"data\", (d) => { stdout += d.toString(); });\n\n child.on(\"close\", (code) => {\n const coverageMatch = stdout.match(/All files.*?(\\d+\\.?\\d*)/);\n const coveragePercent = coverageMatch ? parseFloat(coverageMatch[1]) : undefined;\n\n resolve({\n content: [{ type: \"text\", text: `Coverage: ${coveragePercent || \"N/A\"}%` }],\n structuredContent: {\n status: code === 0 ? \"success\" : \"failed\",\n message: code === 0 ? \"Coverage generated\" : \"Coverage failed\",\n coveragePercent,\n output: stdout,\n },\n });\n });\n });\n }\n\n return {\n content: [{ type: \"text\", text: `Coverage não suportado para ${fw} ainda.` }],\n structuredContent: { status: \"not_supported\", message: \"Coverage not supported for this framework\" },\n };\n }\n);\n\nserver.registerTool(\n \"watch_tests\",\n {\n title: \"Rodar testes em modo watch\",\n description: \"Inicia testes em watch mode (Jest, Vitest). Útil para desenvolvimento.\",\n inputSchema: z.object({\n framework: z.enum([\"jest\", \"vitest\"]).optional().describe(\"Framework. Default: detectado.\"),\n }),\n outputSchema: z.object({\n status: z.string(),\n message: z.string(),\n }),\n },\n async ({ framework }) => {\n const structure = detectProjectStructure();\n const fw = framework || (structure.testFrameworks.includes(\"jest\") ? \"jest\" : \"vitest\");\n\n if (!structure.testFrameworks.includes(fw)) {\n return {\n content: [{ type: \"text\", text: `${fw} não detectado no projeto.` }],\n structuredContent: { status: \"not_found\", message: \"Framework not found\" },\n };\n }\n\n return {\n content: [{ type: \"text\", text: `Para watch mode, rode manualmente: npx ${fw} --watch` }],\n structuredContent: {\n status: \"info\",\n message: `Watch mode requires interactive terminal. Run: npx ${fw} --watch`,\n },\n };\n }\n);\n\nserver.registerTool(\n \"create_test_template\",\n {\n title: \"Criar template de teste\",\n description: \"Gera template básico de teste (boilerplate) para o framework escolhido.\",\n inputSchema: z.object({\n framework: z.enum([\"cypress\", \"playwright\", \"jest\"]).describe(\"Framework.\"),\n type: z.enum([\"api\", \"ui\", \"unit\"]).optional().describe(\"Tipo de teste. Default: api.\"),\n }),\n outputSchema: z.object({\n ok: z.boolean(),\n template: z.string(),\n suggestedFileName: z.string(),\n }),\n },\n async ({ framework, type = \"api\" }) => {\n let template = \"\";\n let fileName = \"\";\n\n if (framework === \"cypress\") {\n fileName = `${type}-test.cy.js`;\n template = `describe('${type.toUpperCase()} Test', () => {\n it('should pass', () => {\n ${type === \"api\" ? \"cy.request('GET', 'http://localhost:3000/api/health').then((res) => {\\n expect(res.status).to.eq(200);\\n });\" : \"cy.visit('/');\\n cy.get('h1').should('be.visible');\"}\n });\n});`;\n } else if (framework === \"playwright\") {\n fileName = `${type}-test.spec.js`;\n template = `const { test, expect } = require('@playwright/test');\n\ntest.describe('${type.toUpperCase()} Test', () => {\n test('should pass', async ({ ${type === \"api\" ? \"request\" : \"page\"} }) => {\n ${type === \"api\" ? \"const res = await request.get('http://localhost:3000/api/health');\\n expect(res.status()).toBe(200);\" : \"await page.goto('/');\\n await expect(page.locator('h1')).toBeVisible();\"}\n });\n});`;\n } else {\n fileName = `${type}-test.test.js`;\n template = `describe('${type.toUpperCase()} Test', () => {\n test('should pass', ${type === \"api\" ? \"async () => {\\n const res = await fetch('http://localhost:3000/api/health');\\n expect(res.status).toBe(200);\\n }\" : \"() => {\\n expect(true).toBe(true);\\n }\"});\n});`;\n }\n\n return {\n content: [{ type: \"text\", text: `Template criado. Use write_test para gravar.` }],\n structuredContent: { ok: true, template, suggestedFileName: fileName },\n };\n }\n);\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((err) => {\n console.error(\"Erro no MCP server:\", err);\n process.exit(1);\n});\n"],"mappings":"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","names":["res","data"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-lab-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "MCP server + AI agents for QA automation in any project (Cypress, Playwright, Jest)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"mcp-lab-agent": "dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"dist"
|
|
11
|
+
"dist",
|
|
12
|
+
"qa-lab-flows.json.example"
|
|
12
13
|
],
|
|
13
14
|
"scripts": {
|
|
14
15
|
"build": "tsup",
|
|
@@ -24,7 +25,9 @@
|
|
|
24
25
|
"jest",
|
|
25
26
|
"cursor",
|
|
26
27
|
"ai-agent",
|
|
27
|
-
"test-automation"
|
|
28
|
+
"test-automation",
|
|
29
|
+
"self-healing",
|
|
30
|
+
"qa-metrics"
|
|
28
31
|
],
|
|
29
32
|
"repository": {
|
|
30
33
|
"type": "git",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"flows": [
|
|
3
|
+
{
|
|
4
|
+
"id": "login",
|
|
5
|
+
"name": "Login",
|
|
6
|
+
"testFiles": ["cypress/e2e/login.cy.js", "specs/login.spec.js"]
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"id": "checkout",
|
|
10
|
+
"name": "Checkout",
|
|
11
|
+
"testFiles": ["e2e/checkout.cy.js", "e2e/payment.cy.js"]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "cadastro",
|
|
15
|
+
"name": "Cadastro de usuário",
|
|
16
|
+
"testFiles": ["specs/register.spec.js"]
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|