#!/usr/bin/env node import fs from "node:fs"; import path from "node:path"; import process from "node:process"; import { spawnSync } from "node:child_process"; import { capture, commandExists, run } from "./lib/exec.mjs"; const root = process.cwd(); const requiredRootFiles = [ "CLAUDE.md", "product/milestones.json", "ItWorx.KnowledgeHub.slnx", "src/frontend/package.json" ]; const missingRoot = requiredRootFiles.filter((entry) => !fs.existsSync(path.join(root, entry))); if (missingRoot.length > 0) { console.error("Run this command from the repository root. Missing:", missingRoot.join(", ")); process.exit(1); } const checks = []; async function check(name, command, args, predicate, required = true) { if (!commandExists(command)) { checks.push({ name, value: "missing", ok: false, required }); return; } try { const value = await capture(command, args); checks.push({ name, value: value.split(/\r?\n/)[0], ok: predicate(value), required }); } catch (error) { checks.push({ name, value: `error: ${error.message}`, ok: false, required }); } } await check("Git", "git", ["--version"], (value) => /git version/i.test(value)); await check("Node.js", "node", ["--version"], (value) => { const major = Number(value.match(/v?(\d+)/)?.[1] ?? 0); return major >= 24; }); await check("Corepack", "corepack", ["--version"], (value) => value.length > 0); await check("pnpm", "corepack", ["pnpm", "--version"], (value) => { const major = Number(value.match(/(\d+)/)?.[1] ?? 0); return major >= 11; }); await check(".NET SDK", "dotnet", ["--version"], (value) => value.trim().startsWith("10.")); function findWorkingPython() { for (const candidate of ["python3", "python"]) { if (!commandExists(candidate)) { continue; } const probe = spawnSync(candidate, ["--version"], { stdio: "ignore", shell: false }); if (probe.status === 0) { return candidate; } } return null; } const python = findWorkingPython(); if (python) { await check("Python", python, ["--version"], (value) => { const match = value.match(/Python\s+(\d+)\.(\d+)/i); return Boolean(match && Number(match[1]) >= 3 && Number(match[2]) >= 11); }); } else { checks.push({ name: "Python", value: "missing or non-working", ok: false, required: true }); } await check("Docker", "docker", ["--version"], (value) => /Docker version/i.test(value)); if (commandExists("docker")) { try { const compose = await capture("docker", ["compose", "version"]); checks.push({ name: "Docker Compose", value: compose, ok: /Compose version\s+v?\d+\./i.test(compose), required: true }); } catch (error) { checks.push({ name: "Docker Compose", value: `error: ${error.message}`, ok: false, required: true }); } } const width = Math.max(...checks.map((entry) => entry.name.length), 10); for (const entry of checks) { const marker = entry.ok ? "OK " : entry.required ? "ERR" : "WARN"; console.log(`${marker} ${entry.name.padEnd(width)} ${entry.value}`); } const failures = checks.filter((entry) => entry.required && !entry.ok); if (failures.length > 0) { console.error("\nRequired toolchain checks failed."); process.exit(1); } if (!fs.existsSync(path.join(root, "pnpm-lock.yaml"))) { console.warn("\nWARN pnpm-lock.yaml is not present yet. M0 must generate and commit it."); } console.log("\nToolchain baseline is available.");