crew-cli
v0.1.0
Published
**Build your own CLI in minutes using only files.**
Readme
crew (BETA)
Build your own CLI in minutes using only files.
crew turns your TypeScript file structure into a fully functional CLI.
Stop writing boilerplate configuration and focus on your logic.
crewis currently in BETA. Please report any issues you may encounter.
Requirements
This library requires Bun to work.
Features
- 📂 File-system Routing: Your folder structure IS your command structure.
- 🎩 Type-Safe Options: Define options as TypeScript types—flags are generated automatically.
- 🛡️ Auto-Coercion: Arguments are automatically cast to their declared TypeScript types (numbers, booleans).
- 📝 Self-Documentation: JSDoc comments automatically become CLI help text.
- ⏱️ Watch Mode: Instant feedback while you develop with
crew watch. - ⚡ Zero Config: Just export a function.
Get Started
In this guide, we'll build a simple CLI to manage users in a SQLite database.
1. Install
mkdir my-cli && cd my-cli
bun init -y
bun add crew -D
bun add commander # crew depends on commander2. Create your command
crew uses your file structure to create commands. Create a file at src/create-user.ts.
This will automatically become the create-user command.
src/create-user.ts:
import { Database } from "bun:sqlite";
const db = new Database("mydb.sqlite");
// 1. Define your options as a Type
type UserOptions = {
/**
* User's age
*/
age?: number;
/**
* Grant admin privileges
*/
admin?: boolean;
};
/**
* Create a new user in the database
* @param name The username to create
*/
export default function main(name: string, options: UserOptions) {
// 2. Write your logic
db.query("CREATE TABLE IF NOT EXISTS users (name TEXT, age INTEGER, admin BOOLEAN)").run();
db.query("INSERT INTO users (name, age, admin) VALUES ($name, $age, $admin)").run({
$name: name,
$age: options.age || 0,
$admin: options.admin || false,
});
console.log(`Created user ${name}!`);
}3. Build & Run
Run the build command to compile your CLI:
bunx crew buildNow you can run your new CLI:
./cli create-user "Alice" --age 30 --admin
# Output: Created user Alice!Usage
Structure maps to Commands
crew intuitively maps your src directory to CLI commands.
- Named Files (
create.ts) ->mycli create - Directories (
db/) ->mycli db ... - Index Files (
db/index.ts) ->mycli db(root command for the directory)
src/
├── index.ts # -> mycli
├── user.ts # -> mycli user
└── db/
├── index.ts # -> mycli db
└── migrate.ts # -> mycli db migrateDevelopment: Watch Mode
You can use the watch command to automatically rebuild your CLI whenever you make changes:
bunx crew watch "src/**/*.ts"Arguments & Options
Simply export a function. The first argument receives command-line arguments, and the second argument defines your options.
crew is smart enough to coerce your arguments based on your TypeScript types.
src/greet.ts:
type GreetOptions = {
/**
* Shout the greeting
*/
loud?: boolean;
/**
* Name to greet
*/
name?: string;
};
// > mycli greet hello --loud --name=World
export default function (greeting: string, options: GreetOptions) {
const name = options.name || "stranger";
if (options.loud) {
console.log(`${greeting.toUpperCase()} ${name.toUpperCase()}!`);
} else {
console.log(`${greeting} ${name}.`);
}
}Start the watch mode and try it out:
$ ./cli greet hello --loud --name=World
HELLO WORLD!Generated Help Text
crew automatically generates the help menu from your JSDoc comments:
$ ./cli greet --help
Usage: cli greet [options] <greeting>
Options:
--loud Shout the greeting
--name <value> Name to greet
-h, --help display help for command