argoyle
v1.0.0
Published
Minimal, zero-dependency CLI argument parser with auto-generated help.
Maintainers
Readme
Minimal, zero-dependency CLI argument parser with auto-generated help.
Install
npm install argoyleUsage
import Argoyle from "argoyle";
const cli = new Argoyle("1.0.0");
cli
.line("Usage: mytool [options] <files...>")
.line("")
.option("out", {
short: "o",
value: "<path>",
description: "Output directory",
})
.option("format", {
short: "f",
value: "[type]",
description: "Output format",
})
.option("verbose", { short: "V", description: "Verbose output" })
.option("port", {
value: "<n>",
description: "Port number",
callback: (val) => parseInt(val, 10),
})
.line("")
.line("Examples:")
.line(" mytool --out dist -V file.txt");
// mytool --out dist -V --port 3000 file.txt
const { flags, positionals } = cli.parse();
console.log(flags); // { out: 'dist', format: null, verbose: true, port: 3000, version: false, help: false }
console.log(positionals); // ['file.txt']Running mytool --help outputs:
Usage: mytool [options] <files...>
-o, --out <path> Output directory
-f, --format [type] Output format
-V, --verbose Verbose output
--port <n> Port number
-v, --version Show version
-h, --help Show help
Examples:
mytool --out dist -V file.txtAPI
new Argoyle(version?)
Creates a new parser. Pass a version string to auto-register --version / -v.
.option(name, opts?)
Registers a CLI option. Returns this for chaining.
name- Option name (used as--name)opts.short- Single-character short alias (used as-x)opts.value- Value placeholder. Use<name>for required values,[name]for optional values. Omit for boolean flags.opts.default- Default value. Boolean flags default tofalse, value options default tonull.opts.description- Description shown in help output.opts.callback- Function called when the option is parsed. Receives the parsed value (truefor boolean flags, a string for value options). Return a value to transform it, or returnundefinedto keep the original.
.line(text?)
Adds a text line to the help output. Use for headers, sections, and examples. Returns this for chaining.
.help()
Returns the formatted help string.
.parse(argv?)
Parses arguments and returns { flags, positionals }. Accepts either an explicit argv array or an options object. When called without arguments, it auto-detects user arguments from process.argv — using slice(2) for standard Node.js invocations (node script.js ...) and slice(1) for compiled binaries.
// Explicit argv
cli.parse(["--verbose", "file.txt"]);
// Custom offset for exotic runtimes with extra pre-argument entries
cli.parse({ offset: 3 });
// Stop at first positional (useful for subcommands)
cli.parse({ stopEarly: true });flags- Object with parsed option values keyed by option name.positionals- Array of non-option arguments.stopEarly- Whentrue, stops parsing at the first positional argument. Everything after it (including flags) is collected intopositionals.
A bare -- stops option parsing. Everything after it is treated as positionals, even if it looks like a flag.
Built-in Options
When a version string is provided, --version / -v is auto-registered. --help / -h is always auto-registered. Both are placed after user-defined options in the help output and call process.exit(0) after printing.
These defaults are skipped if you register your own --version or --help.
Subcommands
Argoyle doesn't have built-in subcommand routing, but you can compose multiple instances using stopEarly:
import Argoyle from "argoyle";
const cli = new Argoyle("1.0.0");
cli.option("verbose", { short: "V", description: "Verbose output" });
const { flags, positionals } = cli.parse({ stopEarly: true });
// mytool -V serve --port 3000
// flags = { verbose: true, ... }
// positionals = ['serve', '--port', '3000']
if (positionals[0] === "serve") {
const serve = new Argoyle();
serve.option("port", {
value: "<n>",
description: "Port number",
callback: (val) => parseInt(val, 10),
});
const { flags: serveFlags } = serve.parse(positionals.slice(1));
}Nested subcommands work by chaining parsers:
// mytool remote add origin https://example.com
const root = new Argoyle("1.0.0");
root.option("verbose", { short: "V" });
const { positionals } = root.parse({ stopEarly: true });
// positionals = ['remote', 'add', 'origin', 'https://example.com']
if (positionals[0] === "remote") {
const remote = new Argoyle();
const { positionals: sub } = remote.parse({
stopEarly: true,
argv: positionals.slice(1),
});
// sub = ['add', 'origin', 'https://example.com']
if (sub[0] === "add") {
const [name, url] = sub.slice(1);
}
}Comparison
| | argoyle | commander | yargs | minimist | meow | arg | util.parseArgs |
| -------------------- | -------- | --------- | ------- | -------- | -------- | -------- | ---------------- |
| Unpacked size | ~18 KB | ~209 KB | ~231 KB | ~54 KB | ~411 KB | ~14 KB | 0 (built-in) |
| Runtime deps | 0 | 0 | 6 | 0 | 0* | 0 | 0 |
| TypeScript | Built-in | Built-in | @types | @types | Built-in | Built-in | @types/node |
| Auto-help | Yes | Yes | Yes | No | Yes | No | No |
| Subcommands | No | Yes | Yes | No | No | No | No |
| -abc combined | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| --key=value | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| -- end-of-opts | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| Callbacks | Yes | Yes | Yes | No | Partial | Yes | No |
* meow bundles its dependencies (including yargs-parser) at build time.
License
P.S.
The name is an homage to Gargoyles TV series, one of my favorite shows when I was a kid.
With ❤️, @stamat

