Running a generator
Options
jen [generator] [--param=value …] [options]. The generator is a name (class), with its pack (cpp:class) or with its source (user:class).
--dry-run,-n: only show the plan, write nothing.--force,-f: overwrite existing files instead of skipping them.--dir=<path>: where the files go, instead of the current directory. See where files go.--from <package>: fetch a pack via npm, run it once, then remove it – or a package a pack runs, e.g. a Yeoman generator:jen --from generator-code yeoman:code.--list,-l: list every generator and where it comes from.--json: with--list, the same list as JSON.--help,-h: jen's own help, or with a generator, its params.
Params
Everything else on the command line is a param of the generator. A value always takes the = form: --name=Foo. A boolean param works as a bare flag (--moveOnly), or with =true/=false – yes/no, on/off and 1/0 work too. Anything else fails, rather than being read as false.
A param without a default is required. Leave it out and jen stops with Missing: --name before anything is written – it never prompts for it.
Some params only take a few values, like --std=20 or --std=23 for cpp:enum. Anything else stops jen with the values it does take: cpp:enum --std: expected one of "20", "23", got "17".
list, dry-run, force, from and help are jen's own, so a generator can't use them as param names. json and dir can be: a generator with its own dir param gets --dir for itself.
Unknown flags
A flag the generator has no param for stops jen too. A typo like --moveonly or --namepsace=gfx would otherwise be dropped without a word, and you'd get different code than you asked for. jen names every unknown flag at once, suggests the closest param for a near-miss, and lists the ones the generator knows:
$ jen cpp:class --name=Foo --copyable --moveonly
cpp:class (pack @codejen/pack-cpp) – create a C++ class (header + source), wired into the CMakeLists.txt with # jen:sources
cpp:class has no --copyable, --moveonly (did you mean --moveOnly?). Known: --name, --namespace, --moveOnly, --resource, --nullValue, --destroy, --withTest. (jen cpp:class --help)
--dir is always accepted, whether the generator has its own dir param or not.
Reading the plan
Every run prints its plan: one line per file, and what happens to it.
+: the file is created.~: the file is changed, with the line that goes in.!: the file is overwritten, with--force.-: the file is deleted.=: nothing to do – the file exists, the line is already there, or nothing matched.?: a problem, so the action is skipped – a missing file or marker, or a path outside the project.
When the project root isn't where you stand, the plan says where jen found it and why, e.g. root: .. (package.json). With --dry-run, the plan is all there is.
Fetching packs
--from <package> (or <package>@<version>) installs a pack into a throwaway directory, runs it once and removes it again – no package.json entry, no node_modules left in your project. Any other package is installed the same way for a pack to run, e.g. a Yeoman generator: jen --from generator-code yeoman:code. The generator name stays the same as for an installed pack: --from only changes where jen gets it.
Ask for a prefixed generator whose pack isn't installed, like lua:function, and jen fetches @codejen/pack-lua the same way. That only ever pulls from jen's own @codejen scope, so a typo can't install someone else's package; for any other scope, or an unprefixed name, jen prints a hint to use --from instead.
Packs fetch, too – only names fixed in their own code, never one from the command line: pack-yeoman its fixed list of generators, pack-js npm create vite@latest and the latest versions for js:esbuild. jen uses your own npm, with --ignore-scripts for its installs. JEN_NO_FETCH=1 turns all of it off.
Looking around
--help
jen <generator> --help shows one generator and its params the way you'd type them: required ones marked (required), strings with their default (--namespace=""), booleans as a bare flag, each followed by its description. A param that only takes a few values lists them all, the default first: --std=23|20.
Below the params come the generator's examples, ready to copy – the first one also the way to run it without installing jen, through npx --yes @codejen/jen. Last, a link to the pack's documentation, if it has one.
$ jen cpp:enum --help
cpp:enum – create an enum class with to_string() and a std::formatter specialization (header-only)
--name (required) the enum, any case: game-state and GameState both make GameState in GameState.h
--values (required) comma-separated enumerators, e.g. "idle, running, paused"
--namespace="" C++ namespace to wrap it in, nested ones as engine::physics; empty means the global namespace
--std=23|20 C++ standard: 23, or 20 (std::abort instead of std::unreachable)
--withTest add a doctest case (needs cpp:doctest)
examples:
jen cpp:enum --name=GameState --values="menu, playing, paused"
jen cpp:enum --name=GameState --values="menu, playing, paused" --std=20 --withTest
npx --yes @codejen/jen cpp:enum --name=GameState --values="menu, playing, paused" (without installing jen)
docs: https://learosema.github.io/jen/recipes.html
--list
jen --list shows every generator, where it comes from, and which ones are shadowed by another of the same name further up the search path. Without a generator name at all, jen prints the same list.
--list --json
jen --list --json prints the same catalog as JSON on stdout, and nothing else, for scripts, editors and other tools. Each generator has:
name: the name to run it by, with the pack prefix, e.g.cpp:class.source: where it comes from, e.g.pack @codejen/pack-cpp,projectorbuilt-in.pack: for a generator from a pack, the pack'sname, and itsversion,descriptionandhomepagewhere itspackage.jsonhas them.shadowed:trueif another generator of the same name is found first, so this one only runs with its source prefix.description: what it makes, or"".params: each param by name, in the order--helpshows them, withtype:stringorboolean, following from its default,required:trueif it has no default,default,choices(every value it takes),pathanddescription, only where the generator gives them.
examples: example calls, each asjen, with jen installed, and asnpx, without:npx --yes @codejen/jen …, with--fromfor a pack outside@codejen.[]if the generator has none.
A generator that fails to load gets an error with the message instead of description, params and examples, and the listing goes on with the next one.
$ jen --list --json [ { "name": "cpp:enum", "source": "pack @codejen/pack-cpp", "pack": { "name": "@codejen/pack-cpp", "version": "1.3.0", "description": "jen pack for C++: class/shader scaffolding in src/, plus SDL3 (+ OpenGL) app starters", "homepage": "https://learosema.github.io/jen/recipes.html" }, "shadowed": false, "description": "create an enum class with to_string() and a std::formatter specialization (header-only)", "params": { "name": { "type": "string", "required": true, "description": "the enum, any case: game-state and GameState both make GameState in GameState.h" }, "std": { "type": "string", "required": false, "default": "23", "choices": ["20", "23"], "description": "C++ standard: 23, or 20 (std::abort instead of std::unreachable)" }, … }, "examples": [ { "jen": "jen cpp:enum --name=GameState --values=\"menu, playing, paused\"", "npx": "npx --yes @codejen/jen cpp:enum --name=GameState --values=\"menu, playing, paused\"" }, … ] }, … ]
For scripts
Exit codes
jen exits with 0 when it ran: files written, nothing to do, or only a plan with --dry-run. Anything else exits with 1 and a message on stderr: a missing or unknown param, a boolean it can't read, a generator that isn't found – and jen without a generator name, after listing them. Nothing is written in any of these cases.
Passing flags on
Some generators run something else and pass the command line on, like pack-yeoman, which answers a Yeoman generator's questions from its flags. Such a generator sets passthrough: true: jen then accepts flags it has no param for and hands them over, with the positionals after the generator name, in ctx.args.