import { stat } from "node:fs/promises"; import { join } from "node:path"; import { readBoundedJsonFile } from "../storage/json-record-files.js"; const MAX_MANIFEST_BYTES = 256 * 1024; const OVERSIZED_MANIFEST = `Project package manifest exceeds ${MAX_MANIFEST_BYTES} bytes`; async function exists(path: string): Promise { try { await stat(path); return true; } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return false; throw error; } } async function packageScripts(projectRoot: string): Promise> { const path = join(projectRoot, "package.json"); try { const value = await readBoundedJsonFile(path, MAX_MANIFEST_BYTES, OVERSIZED_MANIFEST); if (typeof value !== "object" || value === null || Array.isArray(value)) return {}; const scripts = (value as Record).scripts; if (typeof scripts !== "object" || scripts === null || Array.isArray(scripts)) return {}; return Object.fromEntries(Object.entries(scripts).filter((entry): entry is [string, string] => typeof entry[1] === "string")); } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT" || error instanceof SyntaxError || (error instanceof Error && error.message === OVERSIZED_MANIFEST)) return {}; throw error; } } function usefulTestScript(script: string | undefined): boolean { if (!script?.trim()) return false; return !/echo\s+["']?error:\s*no test specified/i.test(script); } export async function inferProjectVerifierCommands(projectRoot: string, goal: string): Promise { const lowerGoal = goal.toLowerCase(); const scripts = await packageScripts(projectRoot); const commands: string[] = []; if (usefulTestScript(scripts.test)) commands.push("npm test"); if (/\b(lint|format|style)\b/.test(lowerGoal) && scripts.lint) commands.push("npm run lint"); if (/\b(build|compile|bundle|type.?check)\b/.test(lowerGoal)) { if (scripts.build) commands.push("npm run build"); else if (scripts.typecheck) commands.push("npm run typecheck"); } if (commands.length === 0 && /\b(test|tests|testing)\b/.test(lowerGoal)) { if (await exists(join(projectRoot, "Cargo.toml"))) commands.push("cargo test"); else if (await exists(join(projectRoot, "go.mod"))) commands.push("go test ./..."); else if (await exists(join(projectRoot, "pyproject.toml"))) commands.push("pytest"); } return [...new Set(commands)].slice(0, 3); }