Show HN: Crust – A CLI framework for TypeScript and Bun (github.com)

95 points by jellyotsiro ↗ HN
We've been building Crust (https://crustjs.com/), a TypeScript-first, Bun-native CLI framework with zero dependencies. It's been powering our core product internally for a while, and we're now open-sourcing it.

The problem we kept running into: existing CLI frameworks in the JS ecosystem are either minimal arg parsers where you wire everything yourself, or heavyweight frameworks with large dependency trees and Node-era assumptions. We wanted something in between.

What Crust does differently:

- Full type inference from definitions — args and flags are inferred automatically. No manual type annotations, no generics to wrangle. You define a flag as type: "string" and it flows through to your handler.

- Compile-time validation — catches flag alias collisions and variadic arg mistakes before your code runs, not at runtime.

- Zero runtime dependencies — @crustjs/core is ~3.6kB gzipped (21kB install). For comparison: yargs is 509kB, oclif is 411kB.

- Composable modules — core, plugins, prompts, styling, validation, and build tooling are all separate packages. Install only what you need.

- Plugin system — middleware-based with lifecycle hooks (preRun/postRun). Official plugins for help, version, and shell autocompletion.

- Built for Bun — no Node compatibility layers, no legacy baggage.

Quick example:

  import { Crust } from "@crustjs/core";
  import { helpPlugin, versionPlugin } from "@crustjs/plugins";

  const main = new Crust("greet")
    .args([{ name: "name", type: "string", default: "world" }])
    .flags({ shout: { type: "boolean", short: "s" } })
    .use(helpPlugin())
    .use(versionPlugin("1.0.0"))
    .run(({ args, flags }) => {
      const msg = `Hello, ${args.name}!`;
      console.log(flags.shout ? msg.toUpperCase() : msg);
    });

  await main.execute();
Scaffold a new project:

  bun create crust my-cli
Site: https://crustjs.com GitHub: https://github.com/chenxin-yan/crust

Happy to answer any questions about the design decisions or internals.

21 comments

[ 0.34 ms ] story [ 62.2 ms ] thread
Is there an examples section? Would be helpful to see a demo
> Versions before 1.0 do not strictly follow semantic versioning.

Sorry for being nitpicky, but yes they do. Semantic versioning[0] allows arbitrary changes while the major version is 0:

> Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.

[0]: https://semver.org/

this is cool! i'd recommend fleshing out the README. Clicked on the link before the discussion and was a tad confused.
This looks useful. But, it's interesting how the backend-world and front-end world keep diverging. I must admit, I had no idea what this was from the title. "CLI framework"? But in backend-land, these would typically be called "argument parsers" or "command line argument parsers". But maybe I am missing some of the functionality.
nice, congrats on launch. To get an idea... what's the size of a standalone hello world cli binary?
I’ve been using the jsr:@cliffy/* packages from deno to solve the same problem.
yup yup same. That one's worked well for me. Between that and the deno std, it's nice to have it feel like mostly everything you need is available with very little searching.
Any plans to support Node.js? Also some comparison (at least design choices) with existing frameworks would be nice.
also: no "C", and no "rust", despite being a portmanteau of both these other languages names :)
I was looking for something like this.

Does the help plugin not support color?

Looks like the spacing is messed up too. I just converted my app to use it and its coming out like

``` COMMANDS: export-schemaExport table definitions from existing database to YAML export-dataExport table data in CSV format import-dataImport table data from CSV file schema-sqlConvert YAML schema back to MySQL export-usersExport users in YAML format users-sql Convert users.yaml back into SQL export-allExport all data from host export-all-tgzExport all data from host databases-sqlConvert databases.yaml back into SQL export-typescriptExport TypeScript interfaces ```

i.e. there's no space after `export-schema` it just goes immediately to the description.

(comment deleted)
As a Bun user, this caught my eye. Gonna give it a try!
nice work. TS + Bun for CLI is the right bet — the DX is so much better than the traditional node + commander setup. curious about your approach to type-safe argument parsing. we've been exploring similar patterns and found that deriving types from the schema definition (instead of manually typing args) eliminates a whole class of bugs.