Prism: A Budding CLI Library!

Last thread was closed due to lack of updates, but Prism has been updated to support Mojo 0.26.2!

Inspired by: Cobra and urfave/cli!

Mojo Version
Build Status
Test Status
License: MIT

There’s lot’s of examples in the README and in the examples directory, but here’s a full-ish API example:

from std.sys import exit, stderr

from prism import Command, Flag, FlagSet, Version, no_args, read_args


fn base(args: List[String], flags: FlagSet) -> None:
    print("Pass a subcommand!")


fn connect(args: List[String], flags: FlagSet) raises -> None:
    if host := flags.get_string("host"):
        print("Connecting to", host.value())
    else:
        raise Error("Error: Exit Code 2")


fn my_exit(e: Error) -> None:
    print(e, file=stderr)
    if String(e) == "Error: Exit Code 2":
        print("Exiting with code 2")
        exit(2)
    else:
        exit(1)


fn allow_hosts(args: List[String], flags: FlagSet) raises -> None:
    var hosts = flags.get_string_list("hosts")
    if not hosts:
        print("Received no names to print.")
        return

    print("Allowing: ", hosts.value())


fn version(version: String) -> String:
    return "MyCLI version: " + version


fn validate_hosts(value: String) raises -> None:
    var approved_hosts: List[String] = ["localhost", "0.0.0.0", "192.168.1.1"]
    var hosts = value.split(" ")
    for host in hosts:
        if String(host) not in approved_hosts:
            raise Error(
                "ValueError: Host provided is not permitted.\nReceived: ",
                host,
                " Approved: ",
                approved_hosts,
            )


fn main() -> None:
    var cli = Command(
        name="connector",
        usage="Base Command.",
        run=base,
        exit=my_exit,
        version=Version("0.1.0", action=version),
        suggest=True,
        flags=[
            Flag.bool(name="required", shorthand="r0", usage="Always required.", required=True, persistent=True),
            Flag.string(
                name="host",
                shorthand="h",
                usage="Host",
                persistent=True,
                environment_variable="CONNECTOR_HOST",
                file_path="~/.myapp/config",
                default="localhost",
            ),
            Flag.string(
                name="port",
                shorthand="p",
                usage="Port",
                persistent=True,
            ),
            Flag.bool(
                name="automation",
                shorthand="a",
                usage="In automation?",
                persistent=True,
            ),
            Flag.bool(
                name="verbose",
                shorthand="vv",
                usage="Verbose output.",
                persistent=True,
            ),
        ],
        flags_required_together=["host", "port"],
        children=[
            Command(
                name="connect",
                usage="Connect to a database.",
                run=connect,
                aliases=["db-connect"],
                flags=[
                    Flag.bool(
                        name="also",
                        shorthand="a",
                        usage="Also always required.",
                        required=True,
                    ),
                    Flag.string(
                        name="uri",
                        shorthand="u",
                        usage="URI",
                    ),
                ],
                arg_validator=no_args,
                suggest=True,
            ),
            Command(
                name="allow",
                usage="Add hosts to the allow list!",
                run=allow_hosts,
                flags=[
                    Flag.string_list(
                        name="hosts",
                        shorthand="hl",
                        usage="Hosts to add to the allowlist.",
                        default=["localhost", "0.0.0.0"],
                        action=validate_hosts,
                    )
                ],
            ),
        ],
    )
    cli.execute(read_args())

Some alternatives:

Prism has been bumped to support Mojo 1.0.0b2. No substantial changes at this time, but I do plan on trying to stabilize it as much as possible once 1.0.0 is out :slight_smile:

I’d be interested to learn more about Prism’s scope and design philosophy. CLI libraries are becoming increasingly common, so I’m curious what distinguishes this one.

A few questions that would help me evaluate it:

Architecture: Is Prism built on top of existing libraries like Click or Typer, or is it a ground-up implementation. If it’s a wrapper, what abstractions does it add that justify a new dependency.

Feature set: What core problems does it solve. Are we talking argument parsing, output formatting, interactive prompts, or a broader toolkit. The sweet spot for CLI libraries seems to be solving one thing really well rather than being a Swiss Army knife.

Performance and dependencies: How many external dependencies does Prism require. Some developers specifically choose minimal CLI tools to keep their project lightweight.

Documentation examples: The best way to evaluate a new library is seeing real usage. Even just a hello-world with argument parsing and colored output would help potential users understand the API design quickly.

Status and maintenance: Is this an early-stage experiment or something production-ready. Knowing the development roadmap would help people decide if they should invest in learning it now.

I’m genuinely interested in what prompted you to create this rather than extending an existing solution. Often the “why” reveals whether something fills a real gap or solves a problem someone encountered in their own workflow.