← Files stark AI DeveloperARCHIVED FILE

skills/drawio-diagrams/scripts/probe-drawio-toolset.mjs

24.7 KB · Oct 2, 2026 · 00:31 UTC

↓ Download file

#!/usr/bin/env node
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { spawnSync } from "node:child_process";
import { pathToFileURL } from "node:url";

import {
  discoverDrawioCandidates,
  inspectDrawioExecutable,
  resolveCommandPath,
  validatePng,
  validateSvg,
  supportsDescriptorAnchoredChild,
} from "./render-drawio.mjs";

const MAX_OUTPUT_CHARS = 4 * 1024;
const MAX_SPAWN_OUTPUT_CHARS = 64 * 1024;
const MAX_DIAGNOSTIC_CHARS = 512;
const MAX_CANDIDATES = 12;
const MAX_SMOKE_ARTIFACT_BYTES = 16 * 1024 * 1024;
const SMOKE_TIMEOUT_MS = 5_000;
const SCHEMA_VERSION = 1;
const DRAWIO_FORMATS = ["png", "svg", "pdf", "jpg", "xml", "html"];
const SMOKE_DRAWIO_SOURCE = `<mxfile host="app.diagrams.net"><diagram name="Capability probe"><mxGraphModel dx="800" dy="600" grid="1" gridSize="8" page="1" pageScale="1" pageWidth="827" pageHeight="1169"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="probe" value="Capability probe" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1"><mxGeometry x="40" y="40" width="180" height="60" as="geometry"/></mxCell></root></mxGraphModel></diagram></mxfile>`;
const BROWSER_COMMANDS = [
  "chromium",
  "chromium-browser",
  "google-chrome",
  "google-chrome-stable",
  "chrome",
  "msedge",
  "firefox",
];
const PACKAGE_COMMANDS = ["pnpm", "npm", "yarn", "bun", "nix"];
const MCP_PACKAGE_CANDIDATES = ["@drawio/mcp", "drawio-mcp-server", "@next-ai-drawio/mcp-server"];

function truncate(value, limit = MAX_DIAGNOSTIC_CHARS) {
  const text = [...String(value ?? "")]
    .map((character) => {
      const codePoint = character.codePointAt(0);
      return codePoint < 0x20 && ![0x09, 0x0a, 0x0d].includes(codePoint) ? "?" : character;
    })
    .join("");
  return text.length <= limit ? text : `${text.slice(0, Math.max(0, limit - 1))}…`;
}

function commandOutput(result, limit = MAX_OUTPUT_CHARS) {
  return truncate(
    [result?.stdout?.trim(), result?.stderr?.trim()].filter(Boolean).join("\n"),
    limit,
  );
}

function redactPath(value) {
  if (!value) return null;
  const text = String(value);
  if (/^(?:[A-Za-z]:[\\/]|\\\\|\/mnt\/[a-z]\/)/i.test(text)) return "<windows-path>";
  const normalized = text.replaceAll("\\", "/");
  const home = os.homedir().replaceAll("\\", "/");
  const cwd = process.cwd().replaceAll("\\", "/");
  if (normalized === home || normalized.startsWith(`${home}/`)) {
    return `<home>${normalized.slice(home.length) || "/"}`;
  }
  if (normalized === cwd || normalized.startsWith(`${cwd}/`)) {
    return `<workspace>${normalized.slice(cwd.length) || "/"}`;
  }
  if (/^\/tmp(?:\/|$)/.test(normalized) || /^\/run\/user\//.test(normalized)) {
    return `<temp>/${path.basename(normalized)}`;
  }
  if (/^\/nix\/store\//.test(normalized)) return `<nix-store>/${path.basename(normalized)}`;
  if (normalized.startsWith("/")) return `<absolute>/${path.basename(normalized)}`;
  return truncate(text);
}

function redactCommand(value) {
  if (!value) return null;
  const text = String(value);
  if (/^(?:[A-Za-z]:[\\/]|\\\\|\/mnt\/[a-z]\/)/i.test(text)) return "<windows-drawio>";
  if (!text.includes("/") && !text.includes("\\")) return truncate(text);
  const base = path.basename(text);
  return /^[A-Za-z0-9._+-]+$/.test(base) ? base : "<executable>";
}

function redactEndpoint(value) {
  if (!value) return null;
  const text = String(value);
  if (/^[A-Za-z][A-Za-z\d+.-]*:\/\//i.test(text)) return "<configured-endpoint>";
  return redactPath(text);
}

function redactDistro(value) {
  if (!value) return null;
  const text = String(value);
  return /^(?:nixos|ubuntu|debian|fedora|arch|opensuse|alpine)(?:[-._\d]*)$/i.test(text)
    ? text
    : "<wsl-distro>";
}

function redactDiagnostic(value) {
  let text = String(value ?? "");
  text = text.replace(/(?:[A-Za-z]:[\\/]|\\\\|\/)[^\s'"`]+/g, (match, offset, source) => {
    if (source[offset - 1] === ">") return match;
    if (source[offset - 1] && /[A-Za-z0-9_.-]/.test(source[offset - 1])) return match;
    if (match.startsWith("/")) return redactPath(match);
    return "<path>";
  });
  return truncate(text);
}

function executableInfo(
  command,
  { args = ["--version"], env = process.env, timeout = 2_000, runProbe = true } = {},
) {
  const runtimePath = env.PATH ?? process.env.PATH ?? "";
  const resolved = resolveCommandPath(command, {
    pathValue: runtimePath,
    platform: process.platform,
    pathext: env.PATHEXT,
  });
  const descriptor = inspectDrawioExecutable(command, {
    pathValue: runtimePath,
    platform: process.platform,
    pathext: env.PATHEXT,
  });
  const result = {
    command: redactCommand(command),
    path: redactPath(descriptor.resolvedPath || resolved),
    terminalPath: redactPath(descriptor.terminalPath),
    available: Boolean(descriptor.executable && !descriptor.stale),
    status: !descriptor.executable || descriptor.stale ? "missing" : "indeterminate",
    executable: Boolean(descriptor.executable),
    stale: Boolean(descriptor.stale),
    version: null,
    diagnostics: descriptor.diagnostics?.map((item) => redactDiagnostic(item)).slice(0, 4) || [],
  };
  if (!result.available || !runProbe) return result;
  if (descriptor.crossBoundary) return result;
  const probeCommand =
    descriptor.probeCommand ||
    (/^node(?:\.exe)?$/i.test(path.basename(command)) ? process.execPath : command);
  const probe = spawnSync(probeCommand, args, {
    encoding: "utf8",
    env,
    timeout,
    maxBuffer: MAX_SPAWN_OUTPUT_CHARS,
  });
  if (!probe.error && probe.status === 0) {
    result.status = "available";
    result.version = redactDiagnostic(commandOutput(probe));
  } else if (probe.error?.code === "ETIMEDOUT") {
    result.diagnostics.push("version probe timed out");
  } else if (probe.error) {
    result.diagnostics.push(`version probe failed for ${redactCommand(command)}`);
  } else if (probe.status !== 0) result.diagnostics.push(`version probe exited ${probe.status}`);
  return result;
}

export function parseArgs(argv) {
  let json = false;
  for (const arg of argv) {
    if (arg === "--json") json = true;
    else if (arg === "--help" || arg === "-h") return { help: true, json };
    else throw new Error(`Unknown argument: ${arg}`);
  }
  return { help: false, json };
}

export function probeRuntime(command, { env = process.env } = {}) {
  const result = executableInfo(command, { env });
  return {
    ...result,
    name: redactCommand(command),
    platform: process.platform,
    arch: process.arch,
  };
}

export function probePython({ env = process.env } = {}) {
  const result = probeRuntime("python3", { env });
  return { ...result, validatorAvailable: result.status === "available" };
}

export function probeNode({ env = process.env } = {}) {
  const result = probeRuntime("node", { env });
  const majorMatch = result.version?.match(/(?:^|\s)v?(\d+)(?:\.|\s|$)/);
  const major = majorMatch ? Number(majorMatch[1]) : null;
  return {
    ...result,
    requiredMajor: 18,
    major,
    supported: Boolean(result.status === "available" && major !== null && major >= 18),
  };
}

function drawioHelp(command, env) {
  if (!command) return { text: "", status: null, diagnostics: ["no draw.io candidate"] };
  const result = spawnSync(command, ["--help"], {
    encoding: "utf8",
    env,
    timeout: 3_000,
    maxBuffer: MAX_SPAWN_OUTPUT_CHARS,
  });
  return {
    text: commandOutput(result),
    status: result.error ? null : result.status,
    diagnostics: result.error
      ? [truncate(result.error.message)]
      : result.status === 0
        ? []
        : [`help probe exited ${result.status}`],
  };
}

function formatCapabilities(helpText, known = true) {
  const text = String(helpText || "").toLowerCase();
  const formats = Object.fromEntries(
    DRAWIO_FORMATS.map((format) => [
      format,
      known ? new RegExp(`\\b${format}\\b`).test(text) : null,
    ]),
  );
  const theme = known ? /(?:--theme|--svg-theme)/.test(text) : null;
  const pages = known ? /(?:--page-index|--all-pages|--page-range)/.test(text) : null;
  return {
    formats,
    formatList: DRAWIO_FORMATS.filter((format) => formats[format]),
    theme,
    pageSelection: pages,
    evidence: redactDiagnostic(helpText),
  };
}

function validateSmokeArtifact(file, format) {
  let stat;
  try {
    stat = fs.lstatSync(file);
  } catch (error) {
    if (error?.code === "ENOENT") throw new Error(`${format} smoke export created no artifact`);
    throw error;
  }
  if (stat.isSymbolicLink()) throw new Error(`${format} smoke output is a symbolic link`);
  if (!stat.isFile() || stat.size === 0) {
    throw new Error(`${format} smoke output is empty or not a regular file`);
  }
  if (stat.size > MAX_SMOKE_ARTIFACT_BYTES) {
    throw new Error(`${format} smoke output exceeds the bounded size limit`);
  }
  if (format === "png") validatePng(file);
  else validateSvg(file);
}

function smokeDrawioCandidate(command, { env = process.env } = {}) {
  const result = {
    available: false,
    status: "indeterminate",
    attempted: false,
    formats: { png: null, svg: null },
    diagnostics: [],
  };
  let temporaryDirectory = null;
  try {
    temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "drawio-capability-probe-"));
    const input = path.join(temporaryDirectory, "probe.drawio");
    fs.writeFileSync(input, SMOKE_DRAWIO_SOURCE, "utf8");
    for (const format of ["png", "svg"]) {
      const output = path.join(temporaryDirectory, `probe.${format}`);
      const args = [
        "-x",
        "-f",
        format,
        ...(format === "png" ? ["-s", "1", "-b", "0"] : ["--svg-theme", "dark", "-e", "-b", "0"]),
        "-o",
        output,
        input,
      ];
      result.attempted = true;
      const smoke = spawnSync(command, args, {
        cwd: temporaryDirectory,
        encoding: "utf8",
        env,
        timeout: SMOKE_TIMEOUT_MS,
        maxBuffer: MAX_SPAWN_OUTPUT_CHARS,
      });
      if (smoke.error || smoke.status !== 0) {
        result.status = "rejected";
        result.formats[format] = false;
        result.diagnostics.push(
          `${format} smoke probe ${
            smoke.error?.code === "ETIMEDOUT"
              ? "timed out"
              : smoke.error
                ? "failed"
                : `exited ${smoke.status}`
          }`,
          commandOutput(smoke),
        );
        break;
      }
      try {
        validateSmokeArtifact(output, format);
      } catch (error) {
        result.status = "rejected";
        result.formats[format] = false;
        result.diagnostics.push(`${format} smoke artifact rejected`, error.message);
        break;
      }
      result.formats[format] = true;
    }
    if (result.formats.png === true && result.formats.svg === true) {
      result.available = true;
      result.status = "available";
      result.diagnostics = [];
    }
  } catch (error) {
    result.status = "indeterminate";
    result.diagnostics.push("temporary format smoke probe could not run", error.message);
  } finally {
    if (temporaryDirectory) {
      try {
        fs.rmSync(temporaryDirectory, { recursive: true, force: true });
      } catch (error) {
        result.available = false;
        result.status = "indeterminate";
        result.diagnostics.push(
          "temporary format smoke artifacts could not be removed",
          error.message,
        );
      }
    }
  }
  result.diagnostics = result.diagnostics
    .filter(Boolean)
    .map((item) => redactDiagnostic(item))
    .slice(0, 6);
  return result;
}

function unavailableSmoke(candidate, available) {
  const status =
    !candidate?.executable || candidate.stale
      ? "missing"
      : available
        ? "indeterminate"
        : "rejected";
  return {
    available: false,
    status,
    attempted: false,
    formats: { png: null, svg: null },
    diagnostics: [
      status === "missing"
        ? "candidate is unavailable for format smoke"
        : status === "indeterminate"
          ? "candidate is retained for raw/manual export; transactional format smoke was not attempted"
          : "version probe did not establish a usable draw.io executable",
    ],
  };
}

function candidateAvailable(candidate) {
  if (!candidate?.executable || candidate.stale) return false;
  if (candidate.ambiguous) return false;
  if (candidate.crossBoundary) return true;
  return candidate.versionProbeStatus === 0;
}

function serialiseDrawioCandidate(candidate, { env = process.env } = {}) {
  const command = candidate.command;
  const versionProbe = candidate.versionProbe || null;
  const version = executableInfo(command, { env, runProbe: false });
  const available = candidateAvailable(candidate);
  const help =
    available && !candidate.crossBoundary
      ? drawioHelp(command, env)
      : { text: "", status: null, diagnostics: [] };
  const capabilities = formatCapabilities(help.text, help.status === 0);
  const nativeEligible = supportsDescriptorAnchoredChild(candidate);
  const smoke =
    available && nativeEligible
      ? smokeDrawioCandidate(command, { env })
      : unavailableSmoke(candidate, available);
  const formats = { ...capabilities.formats };
  for (const format of ["png", "svg"]) {
    if (smoke.formats[format] !== null) formats[format] = smoke.formats[format];
  }
  const transactional = Boolean(available && nativeEligible && smoke.status === "available");
  return {
    command: redactCommand(command),
    source: candidate.source,
    path: redactPath(candidate.resolvedPath),
    terminalPath: redactPath(candidate.terminalPath),
    chainLength: Array.isArray(candidate.chain) ? candidate.chain.length : 0,
    executable: Boolean(candidate.executable),
    available,
    stale: Boolean(candidate.stale),
    wrapper: Boolean(candidate.wrapper),
    windows: Boolean(candidate.windows),
    fileKind: candidate.fileKind || "unknown",
    shebang: candidate.shebang ? redactDiagnostic(candidate.shebang) : null,
    wrapperReasons: candidate.wrapperReasons?.map((reason) => truncate(reason)).slice(0, 6) || [],
    platform: process.platform,
    version: redactDiagnostic(version.version || versionProbe),
    versionProbeStatus: candidate.versionProbeStatus ?? null,
    probes: { version: "--version", help: "--help", smoke: ["png", "svg"] },
    helpStatus: help.status,
    smoke,
    diagnostics: [
      ...(candidate.diagnostics || []),
      ...(version.diagnostics || []),
      ...help.diagnostics,
      ...smoke.diagnostics,
    ]
      .map((item) => redactDiagnostic(item))
      .slice(0, 8),
    capabilities: {
      ...capabilities,
      formats,
      formatList: DRAWIO_FORMATS.filter((format) => formats[format]),
      rawCli: available,
      transactional,
      smoke: { png: smoke.formats.png, svg: smoke.formats.svg },
    },
  };
}

export function probeDrawio({ env = process.env } = {}) {
  const discovery = discoverDrawioCandidates({
    env,
    pathValue: env.PATH ?? process.env.PATH ?? "",
  });
  const candidates = discovery
    .slice(0, MAX_CANDIDATES)
    .map((candidate) => serialiseDrawioCandidate(candidate, { env }));
  const selected =
    candidates.find((candidate) => candidate.available && candidate.capabilities.transactional) ||
    candidates.find((candidate) => candidate.available) ||
    null;
  const raw = candidates.filter(
    (candidate) => candidate.available && !candidate.capabilities.transactional,
  );
  const formats = Object.fromEntries(
    DRAWIO_FORMATS.map((format) => [format, selected?.capabilities.formats[format] ?? null]),
  );
  return {
    selected,
    candidates,
    transactional: {
      available: Boolean(selected?.available && selected?.capabilities.transactional),
      candidate: selected?.command || null,
      reason: selected?.capabilities.transactional
        ? "Linux-native executable passed temporary PNG/SVG smoke checks and supports descriptor-anchored staging"
        : candidates.some(
              (candidate) => candidate.smoke.attempted && candidate.smoke.status === "rejected",
            )
          ? "A draw.io candidate was found, but temporary PNG/SVG smoke checks did not pass"
          : "No Linux-native direct executable was found",
    },
    raw: {
      available: raw.length > 0,
      candidates: raw.map((candidate) => candidate.command),
      retainedForManualExport: raw.length > 0,
    },
    formats,
  };
}

function findBrowserCandidates({ env = process.env } = {}) {
  const values = [];
  if (env.SVG_RASTER_BROWSER) {
    values.push({ command: env.SVG_RASTER_BROWSER, source: "env:SVG_RASTER_BROWSER" });
  } else if (env.AGENT_BROWSER_EXECUTABLE_PATH) {
    values.push({
      command: env.AGENT_BROWSER_EXECUTABLE_PATH,
      source: "env:AGENT_BROWSER_EXECUTABLE_PATH",
    });
  } else {
    for (const command of BROWSER_COMMANDS) values.push({ command, source: "PATH" });
  }
  const seen = new Set();
  const candidates = [];
  for (const item of values) {
    if (seen.has(item.command)) continue;
    seen.add(item.command);
    const result = executableInfo(item.command, { env, args: ["--version"] });
    if (result.available || item.source.startsWith("env:"))
      candidates.push({ ...result, source: item.source });
  }
  return candidates.slice(0, MAX_CANDIDATES);
}

export function probeBrowser({ env = process.env } = {}) {
  const candidates = findBrowserCandidates({ env }).map((candidate) => ({
    ...candidate,
    status: !candidate.available
      ? "missing"
      : /\b(?:chromium|google chrome|microsoft edge)\b/i.test(candidate.version || "")
        ? "present"
        : "indeterminate",
  }));
  const selected = candidates.find((candidate) => candidate.status === "present") || null;
  return {
    available: Boolean(selected),
    status: selected
      ? "present"
      : candidates.some((candidate) => candidate.status === "indeterminate")
        ? "indeterminate"
        : "missing",
    selected,
    candidates,
    fixedThemeRasterization: {
      available: Boolean(selected),
      browser: selected?.command || null,
      approval: "local-browser-path-only",
    },
  };
}

export function probeAgentBrowser({ env = process.env } = {}) {
  const result = executableInfo("agent-browser", { env, args: ["--version"] });
  let help = "";
  if (result.available) {
    const helpResult = spawnSync("agent-browser", ["--help"], {
      encoding: "utf8",
      env,
      timeout: 2_000,
      maxBuffer: MAX_SPAWN_OUTPUT_CHARS,
    });
    help = commandOutput(helpResult);
  }
  return {
    ...result,
    status:
      result.status === "missing"
        ? "missing"
        : result.status === "available" && result.version
          ? "present"
          : "indeterminate",
    command: result.command || "agent-browser",
    browserExecutableReuse: Boolean(env.AGENT_BROWSER_EXECUTABLE_PATH),
    skillsCoreHint: /skills\s+get\s+core/.test(help),
  };
}

function commandExists(command, env = process.env) {
  return Boolean(
    resolveCommandPath(command, {
      pathValue: env.PATH ?? process.env.PATH ?? "",
      platform: process.platform,
      pathext: env.PATHEXT,
    }),
  );
}

export function probePackageManagers({ env = process.env } = {}) {
  const candidates = PACKAGE_COMMANDS.map((command) => {
    const result = executableInfo(command, { env, args: ["--version"] });
    return { name: command, ...result };
  }).filter((candidate) => candidate.status === "available");
  let active = null;
  const userAgent = env.npm_config_user_agent || env.NPM_CONFIG_USER_AGENT || "";
  const userAgentMatch = userAgent.match(/^(pnpm|npm|yarn|bun)\//i);
  if (userAgentMatch) active = userAgentMatch[1].toLowerCase();
  if (!active) {
    try {
      const packageJson = JSON.parse(
        fs.readFileSync(path.join(process.cwd(), "package.json"), "utf8"),
      );
      active = packageJson.packageManager?.split("@")[0] || null;
    } catch {
      active = null;
    }
  }
  if (!active)
    active =
      candidates.find((candidate) => candidate.name !== "nix")?.name || candidates[0]?.name || null;
  return {
    active,
    candidates,
    nix: candidates.find((candidate) => candidate.name === "nix") || {
      name: "nix",
      available: commandExists("nix", env),
      status: commandExists("nix", env) ? "indeterminate" : "missing",
    },
  };
}

export function probeMcp({ env = process.env } = {}) {
  const npxAvailable = commandExists("npx", env);
  const configuredEndpoint = env.DRAWIO_MCP_URL || env.MCP_DRAWIO_URL || null;
  return {
    local: {
      available: false,
      status: "indeterminate",
      npx: npxAvailable,
      packages: MCP_PACKAGE_CANDIDATES,
      installRequired: true,
      configuredEndpoint: redactEndpoint(configuredEndpoint),
    },
    hosted: {
      configured: Boolean(configuredEndpoint),
      endpoint: configuredEndpoint ? redactEndpoint(configuredEndpoint) : "https://mcp.draw.io/mcp",
      available: configuredEndpoint ? true : null,
      status: configuredEndpoint ? "present" : "indeterminate",
      requiresApproval: true,
      contentTransfer: "diagram XML may be sent to the hosted endpoint",
    },
  };
}

export function probeHostedPreview({ env = process.env } = {}) {
  const configured = env.DRAWIO_HOSTED_PREVIEW_URL || env.DRAWIO_MCP_URL || null;
  return {
    available: configured ? true : null,
    status: configured ? "present" : "indeterminate",
    endpoint: configured ? redactEndpoint(configured) : "https://app.diagrams.net/",
    mode: configured ? "configured-hosted" : "browser-url-fallback",
    requiresApproval: true,
    sensitiveDiagramWarning: "Hosted previews and browser URL fragments can expose diagram content",
  };
}

export function probeNixProposal({ env = process.env } = {}) {
  const nix = executableInfo("nix", { env, args: ["--version"] });
  const nixOs = fs.existsSync("/etc/NIXOS") || Boolean(env.NIXOS_VERSION) || Boolean(env.NIX_PATH);
  return {
    nixOs,
    available: nix.status === "available",
    userProfile: {
      proposal: "nix profile install nixpkgs#drawio",
      command: "nix profile install nixpkgs#drawio",
      package: "nixpkgs#drawio",
      appliesTo: "current-user",
      approvalRequired: true,
      executed: false,
    },
  };
}

export function probeDrawioToolset({ env = process.env } = {}) {
  const runtime = {
    python: probePython({ env }),
    node: probeNode({ env }),
  };
  let procVersion = "";
  try {
    procVersion = fs.readFileSync("/proc/version", "utf8");
  } catch {
    procVersion = "";
  }
  return {
    schemaVersion: SCHEMA_VERSION,
    generatedBy: "probe-drawio-toolset",
    receipt: {
      kind: "sanitized-capability-receipt",
      deterministic: true,
      paths: "redacted",
    },
    platform: {
      nodePlatform: process.platform,
      arch: process.arch,
      osRelease: truncate(os.release()),
      wsl: Boolean(env.WSL_DISTRO_NAME || /microsoft|wsl/i.test(procVersion)),
      distro: redactDistro(env.WSL_DISTRO_NAME),
    },
    runtime,
    drawio: probeDrawio({ env }),
    browser: probeBrowser({ env }),
    agentBrowser: probeAgentBrowser({ env }),
    mcp: probeMcp({ env }),
    hostedPreview: probeHostedPreview({ env }),
    packageManagers: probePackageManagers({ env }),
    nixProposal: probeNixProposal({ env }),
  };
}

function usage() {
  return "Usage: node scripts/probe-drawio-toolset.mjs [--json]";
}

function humanReceipt(report) {
  const python = report.runtime.python;
  const node = report.runtime.node;
  const lines = [
    "sanitized capability receipt:",
    `platform: ${report.platform.nodePlatform}/${report.platform.arch}${report.platform.wsl ? " (WSL)" : ""}`,
    `python3: ${python.status === "available" ? python.version || "available" : python.status}`,
    `node: ${node.supported ? node.version || "available" : node.status === "indeterminate" ? "indeterminate" : node.available ? "unsupported (<18)" : "unavailable"}`,
    `draw.io: ${report.drawio.selected?.command || "not found"} (version probe: --version)`,
    `transactional renderer: ${report.drawio.transactional.available ? "available" : "unavailable"}`,
    `raw/manual candidates: ${report.drawio.raw.candidates.length || "none"}`,
    `browser: ${report.browser.selected?.command || "not found"}`,
    `agent-browser: ${report.agentBrowser.available ? report.agentBrowser.version || "available" : "unavailable"}`,
    `MCP hosted preview: ${report.mcp.hosted.configured ? "configured (approval required)" : "not configured"}`,
    `package manager: ${report.packageManagers.active || "not detected"}`,
    `Nix proposal: ${report.nixProposal.userProfile.command} (proposal only)`,
  ];
  return lines.join("\n");
}

export function main(argv = process.argv.slice(2), env = process.env) {
  let args;
  try {
    args = parseArgs(argv);
  } catch (error) {
    console.error(error.message);
    console.error(usage());
    return 2;
  }
  if (args.help) {
    console.log(usage());
    return 0;
  }
  const report = probeDrawioToolset({ env });
  if (args.json) console.log(JSON.stringify(report, null, 2));
  else console.log(humanReceipt(report));
  return 0;
}

if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
  process.exitCode = main();
}

SHA-256: dd6e08c98a27c86fd214abc6d5abc555def0c48c39c90c46e7d2fe6cb46b64cc