import { spawnSync } from 'node:child_process'; import { accessSync, constants } from 'node:fs'; import { join } from 'node:path'; import { resolveBrainHome } from '../fleet/brain-home.js'; /** * Shared brain-tool dispatch (fleet CLI integration, Jason ruling * 2026-08-28): the npm package embeds the COMMAND SURFACE; operator-owned * implementations stay in the brain (tools/). Commands resolve the brain * home (MOSAIC_BRAIN_HOME wins — see brain-home.ts) and exec the tool * there. Nothing operator-specific ships inside the package. * * Pass-through contract: arguments, stdout/stderr, and the exit code belong * to the tool. The CLI adds nothing on success; absent tools fail loudly * with the resolved path (127) instead of guessing. */ /** Absolute path of a brain-relative tool. */ export function resolveBrainTool(mosaicHome: string, relPath: string): string { return join(resolveBrainHome(mosaicHome), ...relPath.split('/')); } /** Map a spawnSync result + tool existence to the CLI exit status. */ export function exitStatusFor( result: { status: number | null; error?: NodeJS.ErrnoException }, toolExists: boolean, ): number { if (!toolExists) return 127; if (result.status !== null) return result.status; return 125; // killed by signal / could not run } export function brainToolExists(tool: string): boolean { try { accessSync(tool, constants.X_OK); return true; } catch { return false; } } /** * Exec a brain tool with full pass-through. `interpreter` runs the tool * through e.g. python3 (renderers); omit it for executable scripts. * Returns the process exit status; callers assign it to process.exitCode. */ export function execBrainTool( mosaicHome: string, relPath: string, args: string[], interpreter?: string, ): number { const tool = resolveBrainTool(mosaicHome, relPath); if (!brainToolExists(tool)) { console.error( `mosaic: brain tool not found (expected ${tool}). ` + 'Tool suites live in the brain tree under tools/; ' + 'check MOSAIC_BRAIN_HOME or the brain checkout.', ); return 127; } const result = interpreter ? spawnSync(interpreter, [tool, ...args], { stdio: 'inherit', env: process.env }) : spawnSync(tool, args, { stdio: 'inherit', env: process.env }); return exitStatusFor(result, true); }