// Check Ts Max Loc script supports OpenClaw repository automation. import { execFileSync } from "node:child_process"; import { existsSync } from "node:fs"; import { readFile } from "node:fs/promises"; function writeStdoutLine(message: string): void { process.stdout.write(`${message}\n`); } type ParsedArgs = { maxLines: number; }; function parseArgs(argv: string[]): ParsedArgs { let maxLines = 500; for (let index = 0; index < argv.length; index++) { const arg = argv[index]; if (arg === "--max") { const next = argv[index + 1]; if (!next || !/^\d+$/u.test(next)) { throw new Error("--max requires a positive integer"); } maxLines = Number(next); if (!Number.isSafeInteger(maxLines) || maxLines <= 0) { throw new Error("--max requires a positive integer"); } index++; continue; } throw new Error(`Unknown argument: ${arg}`); } return { maxLines }; } function gitLsFilesAll(): string[] { // Include untracked files too so local refactors don’t “pass” by accident. const stdout = execFileSync("git", ["ls-files", "--cached", "--others", "--exclude-standard"], { encoding: "utf8", }); return stdout .split("\n") .map((line) => line.trim()) .filter(Boolean); } async function countLines(filePath: string): Promise { const content = await readFile(filePath, "utf8"); // Count physical lines. Keeps the rule simple + predictable. return content.split("\n").length; } async function main(argv = process.argv.slice(2)): Promise { // Makes `... | head` safe. process.stdout.on("error", (error: NodeJS.ErrnoException) => { if (error.code === "EPIPE") { process.exit(0); } throw error; }); const { maxLines } = parseArgs(argv); const files = gitLsFilesAll() .filter((filePath) => existsSync(filePath)) .filter((filePath) => filePath.endsWith(".ts") || filePath.endsWith(".tsx")); const results = await Promise.all( files.map(async (filePath) => ({ filePath, lines: await countLines(filePath) })), ); const offenders = results .filter((result) => result.lines > maxLines) .toSorted((a, b) => b.lines - a.lines); if (!offenders.length) { return 0; } // Minimal, grep-friendly output. for (const offender of offenders) { writeStdoutLine(`${offender.lines}\t${offender.filePath}`); } return 1; } try { const exitCode = await main(); if (exitCode !== 0) { process.exit(exitCode); } } catch (error) { process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); process.exit(1); }