import { getCliConfigValue } from "./helpers.smartconfig.js"; export type TCliOutputMode = "human" | "plain" | "json"; export interface ICliMode { output: TCliOutputMode; interactive: boolean; json: boolean; plain: boolean; quiet: boolean; yes: boolean; help: boolean; agent: boolean; checkUpdates: boolean; isTty: boolean; command?: string; } interface ICliConfigSettings { interactive?: boolean; output?: TCliOutputMode; checkUpdates?: boolean; } type TArgSource = Record & { _?: string[] }; const camelCase = (value: string): string => { return value.replace(/-([a-z])/g, (_match, group: string) => group.toUpperCase(), ); }; const getArgValue = (argvArg: TArgSource, key: string): any => { const keyVariants = [key, camelCase(key), key.replace(/-/g, "")]; for (const keyVariant of keyVariants) { if (argvArg[keyVariant] !== undefined) { return argvArg[keyVariant]; } } return undefined; }; const parseRawArgv = (argv: string[]): TArgSource => { const parsedArgv: TArgSource = { _: [] }; for (let i = 0; i < argv.length; i++) { const currentArg = argv[i]; if (currentArg.startsWith("--no-")) { const key = currentArg.slice(5); parsedArgv[key] = false; parsedArgv[camelCase(key)] = false; continue; } if (currentArg.startsWith("--")) { const withoutPrefix = currentArg.slice(2); const [rawKey, inlineValue] = withoutPrefix.split("=", 2); if (inlineValue !== undefined) { parsedArgv[rawKey] = inlineValue; parsedArgv[camelCase(rawKey)] = inlineValue; continue; } const nextArg = argv[i + 1]; if (nextArg && !nextArg.startsWith("-")) { parsedArgv[rawKey] = nextArg; parsedArgv[camelCase(rawKey)] = nextArg; i++; } else { parsedArgv[rawKey] = true; parsedArgv[camelCase(rawKey)] = true; } continue; } if (currentArg.startsWith("-") && currentArg.length > 1) { for (const shortFlag of currentArg.slice(1).split("")) { parsedArgv[shortFlag] = true; } continue; } parsedArgv._ = parsedArgv._ || []; parsedArgv._.push(currentArg); } return parsedArgv; }; const normalizeOutputMode = (value: unknown): TCliOutputMode | undefined => { if (value === "human" || value === "plain" || value === "json") { return value; } return undefined; }; const resolveCliMode = ( argvArg: TArgSource, cliConfig: ICliConfigSettings, ): ICliMode => { const isTty = Boolean(process.stdout?.isTTY && process.stdin?.isTTY); const agentMode = Boolean(getArgValue(argvArg, "agent")); const outputOverride = normalizeOutputMode(getArgValue(argvArg, "output")); let output: TCliOutputMode = normalizeOutputMode(cliConfig.output) || (isTty ? "human" : "plain"); if (agentMode || getArgValue(argvArg, "json")) { output = "json"; } else if (getArgValue(argvArg, "plain")) { output = "plain"; } else if (outputOverride) { output = outputOverride; } const interactiveSetting = getArgValue(argvArg, "interactive"); let interactive = cliConfig.interactive ?? isTty; if (interactiveSetting === true) { interactive = true; } else if (interactiveSetting === false) { interactive = false; } if (!isTty || output !== "human" || agentMode) { interactive = false; } const checkUpdatesSetting = getArgValue(argvArg, "check-updates"); let checkUpdates = cliConfig.checkUpdates ?? output === "human"; if (checkUpdatesSetting === true) { checkUpdates = true; } else if (checkUpdatesSetting === false) { checkUpdates = false; } if (output !== "human" || agentMode) { checkUpdates = false; } return { output, interactive, json: output === "json", plain: output === "plain", quiet: Boolean( getArgValue(argvArg, "quiet") || getArgValue(argvArg, "q") || output === "json", ), yes: Boolean(getArgValue(argvArg, "yes") || getArgValue(argvArg, "y")), help: Boolean( getArgValue(argvArg, "help") || getArgValue(argvArg, "h") || argvArg._?.[0] === "help", ), agent: agentMode, checkUpdates, isTty, command: argvArg._?.[0], }; }; const getCliModeConfig = async (): Promise => { return await getCliConfigValue("cli", {}); }; export const getCliMode = async ( argvArg: TArgSource = {}, ): Promise => { const cliConfig = await getCliModeConfig(); return resolveCliMode(argvArg, cliConfig); }; export const getRawCliMode = async (): Promise => { const cliConfig = await getCliModeConfig(); const rawArgv = parseRawArgv(process.argv.slice(2)); return resolveCliMode(rawArgv, cliConfig); }; export const printJson = (data: unknown): void => { console.log(JSON.stringify(data, null, 2)); }; export const runWithSuppressedOutput = async ( fn: () => Promise, ): Promise => { const originalConsole = { log: console.log, info: console.info, warn: console.warn, error: console.error, }; const originalStdoutWrite = process.stdout.write.bind(process.stdout); const originalStderrWrite = process.stderr.write.bind(process.stderr); const noop = () => undefined; console.log = noop; console.info = noop; console.warn = noop; console.error = noop; process.stdout.write = (() => true) as typeof process.stdout.write; process.stderr.write = (() => true) as typeof process.stderr.write; try { return await fn(); } finally { console.log = originalConsole.log; console.info = originalConsole.info; console.warn = originalConsole.warn; console.error = originalConsole.error; process.stdout.write = originalStdoutWrite; process.stderr.write = originalStderrWrite; } };