import { z } from "zod";
import { exec } from "child_process";
import { promisify } from "util";
import fs from "fs/promises";
const execAsync = promisify(exec);
// --- Tools ---
export const runTestsSafeSchema = {
name: "run_tests_safe",
description: "Run project tests safely. Only allows standard test commands.",
inputSchema: z.object({
command: z.enum(["npm test", "yarn test", "pnpm test", "go test", "cargo test", "pytest"]).describe("Test command to run"),
args: z.string().optional().describe("Additional arguments (e.g., specific file)"),
}),
};
export const analyzeCoverageSchema = {
name: "analyze_average",
description: "Analyze test coverage reports (lcov).",
inputSchema: z.object({
reportPath: z.string().describe("Path to lcov.info or coverage file"),
}),
};
// --- Handlers ---
export async function runTestsSafeHandler(args: { command: string, args?: string }) {
try {
// Strict command allowlist is enforced by Zod enumSchema
const fullCommand = args.args ? `${args.command} ${args.args}` : args.command;
const { stdout, stderr } = await execAsync(fullCommand, { timeout: 30000 }); // 30s timeout
return {
content: [{
type: "text",
text: `STDOUT:\n${stdout}\n\nSTDERR:\n${stderr}`
}]
};
} catch (error: any) {
return {
content: [{
type: "text",
text: `Test Execution Failed:\n${error.message}\nSTDOUT:\n${error.stdout}\nSTDERR:\n${error.stderr}`
}],
isError: true
};
}
}
export async function analyzeCoverageHandler(args: { reportPath: string }) {
try {
const content = await fs.readFile(args.reportPath, "utf-8");
// Very basic lcov parsing
const records = content.split("end_of_record");
let totalLines = 0;
let coveredLines = 0;
for (const record of records) {
const lfMatch = record.match(/LF:(\d+)/); // Lines Found
const lhMatch = record.match(/LH:(\d+)/); // Lines Hit
if (lfMatch && lhMatch) {
totalLines += parseInt(lfMatch[1]);
coveredLines += parseInt(lhMatch[1]);
}
}
const percentage = totalLines > 0 ? (coveredLines / totalLines) * 100 : 0;
return {
content: [{
type: "text",
text: JSON.stringify({
reportFile: args.reportPath,
totalLines,
coveredLines,
coveragePercent: percentage.toFixed(2) + "%"
}, null, 2)
}]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error reading coverage report: ${error instanceof Error ? error.message : String(error)}` }],
isError: true
};
}
}