<commit_before>import ErrorWithMetadata from '../../ErrorWithMetadata.js';
import {debug} from '../../console/index.js';
import path from '../../path.js';
import spawn from '../../spawn.js';
import stat from '../../fs/stat.js';
import Context from '../Context.js';

/**
 * Implements basic shell expansion (of ~).
 */
export default async function command(
    executable: string,
    args: Array<string>,
    options: {
        chdir?: string;
        creates?: string;
    } = {}
): Promise<void> {
    const description = [executable, ...args].join(' ');

    if (options.creates) {
        const stats = await stat(path(options.creates).expand);

        if (stats instanceof Error) {
            throw stats;
        }

        if (stats !== null) {
            Context.informSkipped(`command \`${description}\``);
            return;
        }
    }

    try {
        await spawn(
            path(executable).expand,
            args.map((arg) => path(arg).expand),
            options.chdir ? {cwd: options.chdir} : {}
        );

        // TODO: decide whether to log full command here
        Context.informChanged(`command \`${description}\``);
    } catch (error) {
        if (error instanceof ErrorWithMetadata) {
            Context.informFailed(error);
        } else {
            debug(() => console.log(error));
            Context.informFailed(`command \`${description}\` failed`);
        }
    }
}<commit_msg>fix(fig): deal with chdir being String<commit_after>import ErrorWithMetadata from '../../ErrorWithMetadata.js';
import {debug, log} from '../../console/index.js';
import path from '../../path.js';
import spawn from '../../spawn.js';
import stringify from '../../stringify.js';
import stat from '../../fs/stat.js';
import Context from '../Context.js';

/**
 * Implements basic shell expansion (of ~).
 */
export default async function command(
    executable: string,
    args: Array<string>,
    options: {
        chdir?: string;
        creates?: string;
    } = {}
): Promise<void> {
    const description = [executable, ...args].join(' ');

    if (options.creates) {
        const stats = await stat(path(options.creates).expand);

        if (stats instanceof Error) {
            throw stats;
        }

        if (stats !== null) {
            Context.informSkipped(`command \`${description}\``);
            return;
        }
    }

    try {
        log.debug(
            `Run command \`${description}\` with options: ${stringify(options)}`
        );

        await spawn(
            path(executable).expand,
            args.map((arg) => path(arg).expand),
            options.chdir ? {cwd: options.chdir.toString()} : {}
        );

        Context.informChanged(`command \`${description}\``);
    } catch (error) {
        if (error instanceof ErrorWithMetadata) {
            Context.informFailed(error);
        } else {
            debug(() => console.log(error));
            Context.informFailed(`command \`${description}\` failed`);
        }
    }
}
{'fraction_non_alphanumeric': 0.16600133067198936, 'fraction_numerical': 0.0, 'mean_word_length': 2.0683673469387753, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6693245', 'n_tokens_mistral': 828, 'n_tokens_neox': 827, 'n_words': 205}