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,
};
}
}