Skip to content

Getting started ​

Jinju is a toolkit for building Node command-line apps in TypeScript. You define flags, args and commands as separate values, compose them into a program, and get back fully typed input in each command's run handler. Help pages, error messages and exit codes are handled for you.

Requirements ​

  • Node >=22.12
  • ESM projects work as usual. CommonJS projects can require('jinju'), which loads the ESM build.

Install ​

sh
npm install jinju
sh
pnpm add jinju
sh
yarn add jinju

Your first CLI ​

Create cli.ts:

ts
import { 
arg
,
command
,
flag
,
jinju
,
packageVersion
} from 'jinju';
const
greet
=
command
({
description
: 'Greet someone',
args
: [
arg
.
string
({
name
: 'name',
description
: 'Who to greet' }).
required
()],
flags
: {
shout
:
flag
.
boolean
({
alias
: 's',
description
: 'Shout the greeting' }),
times
:
flag
.
number
({
description
: 'How many times',
min
: 1,
integer
: true }).
default
(1),
},
run
: ({
args
: [
name
],
flags
}) => {
name: string
const
text
= `Hello, ${
name
}!`;
for (let
i
= 0;
i
<
flags
.
times
;
i
++)
console
.
log
(
flags
.
shout
?
text
.
toUpperCase
() :
text
);
}, }); const
app
=
jinju
({
name
: 'greet',
version
:
packageVersion
(import.meta.
url
),
commands
: {
$
:
greet
},
}); await
app
.
run
();

$ is the command for a level of the tree, so here greet runs without a subcommand name. Hover over name and flags above: their types come from the definitions, so name is a string because the arg is .required(), and times is a number because it has a default.

Run it:

sh
$ npx tsx cli.ts World --shout
HELLO, WORLD!

$ npx tsx cli.ts --help
Greet someone

Usage:
  greet [flags] <name>

Arguments:
  <name>  Who to greet (required)

Flags:
  -h, --help        Show help
  -s, --shout       Shout the greeting
  --times <number>  How many times (default: 1)
  --version         Show version

$ npx tsx cli.ts
error: Missing required argument <name>

Run 'greet --help' for usage.

$ npx tsx cli.ts World --times 0
error: Invalid value '0' for --times: must be at least 1

Run 'greet --help' for usage.

jinju() creates the program, and app.run() runs it. run() reads process.argv.slice(2) by default, sets process.exitCode and resolves to the exit code: 0 on success and 1 on errors. You can also pass the arguments yourself, which is useful in tests:

ts
const 
code
= await
app
.
run
(['World', '--shout']);

Publishing a CLI ​

To ship your CLI as a package, point bin in package.json at the built file and add a shebang to it:

json
{
	"name": "greet",
	"version": "1.0.0",
	"type": "module",
	"bin": { "greet": "./dist/cli.js" }
}
ts
#!/usr/bin/env node

packageVersion(import.meta.url) reads version from the nearest package.json above the file, so --version always matches what you published.

Using an AI assistant ​

The docs are also published for AI assistants and coding agents. Point yours at llms.txt, an index of every page, or llms-full.txt, the whole site in one file. Every page is also available as Markdown: add .md to its URL, or use the Copy page menu under its title to copy it or open it in ChatGPT or Claude. In those copies, the types shown on hover here are written into the examples as comments.

Next steps ​

  • Flags: every flag type, modifiers and switches
  • Args: positional arguments, including variadic args
  • Commands and Program: building a command tree
  • Scopes: sharing flags and args between commands