Stream: ideas

Topic: basic-cli new Cmd API


view this post on Zulip Anton (Aug 20 2025 at 17:12):

I made a proposed overhaul of the basic-cli Cmd API.

The old output! function required people to write substantial scaffolding code just to see human readable stderr of a command in case it failed. With the new exec_output!, all the info you typically want is included in the error. This works well with just ?, for convenient scripting.
output! used to return:

Output : { status : Result I32 InternalIOErr.IOErr, stdout : List U8, stderr : List U8}

exec_output! now returns Ok({stdout_utf8 : Str, stderr_utf8_lossy : Str}) if the exit code (=status) is 0, if not, the exit code is included in the error.

exec_output_bytes! was added to get stdout and stderr in bytes, similar to the old output!, for fine grained control or max perf.

exec! got nicer errors.

exec_cmd! for when you want to execute your command with specific env vars and forward stdout, stderr, and stdin directly to your terminal.

status! was renamed to exec_exit_code! and given nicer errors as well.

What do you think?

view this post on Zulip Ruby (Aug 21 2025 at 19:00):

Comparing to the old way it looks better! I had not used the exec yet, so it's hard for me to evaluate this change pragmatically.

view this post on Zulip Anton (Aug 22 2025 at 08:56):

I'll make a before and after example to make it easier to evaluate

view this post on Zulip Anton (Aug 23 2025 at 16:32):

Cmd.exec!("date", ["-X"])
date: invalid option -- 'X'
Try 'date --help' for more information.

Program exited with error:

    ❌ CmdStatusErr (Other "Non-zero exit code: 1")
date: invalid option -- 'X'
Try 'date --help' for more information.

Program exited with error:

    ❌ ExecFailed {command: "date -X", exit_code: 1}

view this post on Zulip Anton (Aug 23 2025 at 16:33):

output = run_cmd_w_output!("date", ["-X"])?

# You need this whole function to get usable output on error
run_cmd_w_output! : Str, List Str => Result Str [BadCmdOutput(Str)]_
run_cmd_w_output! = |cmd_str, args|
    cmd_out =
        Cmd.new(cmd_str)
        |> Cmd.args(args)
        |> Cmd.output!()

    stdout_utf8 = Str.from_utf8_lossy(cmd_out.stdout)

    when cmd_out.status is
        Ok(0) ->
            Ok(stdout_utf8)
        _ ->
            stderr_utf8 = Str.from_utf8_lossy(cmd_out.stderr)
            err_data =
                """
                Cmd `${cmd_str} ${Str.join_with(args, " ")}` failed:
                - status: ${Inspect.to_str(cmd_out.status)}
                - stdout: ${stdout_utf8}
                - stderr: ${stderr_utf8}
                """

            Err(BadCmdOutput(err_data))
Program exited with error:

    ❌ BadCmdOutput "Cmd `date -X` failed:
- status: (Err (Other "Non-zero exit code: 1"))
- stdout:
- stderr: date: invalid option -- 'X'
Try 'date --help' for more information.
"
output =
    Cmd.new("date")
    |> Cmd.arg("-X")
    |> Cmd.exec_output!()?
Program exited with error:

    ❌ NonZeroExitCode {command: "{ cmd: date, args: -X }", exit_code: 1, stderr_utf8_lossy: "date: invalid option -- 'X'
Try 'date --help' for more information.
", stdout_utf8_lossy: ""}

view this post on Zulip Anton (Aug 23 2025 at 16:54):

exit_code =
    Cmd.new("dat")
    |> Cmd.arg("-X")
    |> Cmd.status!()?
Program exited with error:

    ❌ CmdStatusErr NotFound
exit_code =
    Cmd.new("dat")
    |> Cmd.arg("-X")
    |> Cmd.exec_exit_code!()?
Program exited with error:

    ❌ FailedToGetExitCode {command: "{ cmd: dat, args: -X }", err: NotFound}

view this post on Zulip Luke Boswell (Aug 23 2025 at 22:22):

The Before/After look like a nice improvement to me. :smiley:


Last updated: Jun 16 2026 at 16:19 UTC