Everything a task runner needs
Sensible defaults, no setup to start, fully customizable when you need it.
Zero boilerplate
Plain exported functions become commands. JSDoc descriptions surface in --help.
Type-driven CLI
Argument types come straight from TypeScript. Defaults, optionality, and nested objects map themselves.
Runtime validation
Inputs are validated against zod schemas built from your function's signature. Bad input gets a clean one-line error.
Subcommand groups
Opt into nesting via config.groups[]. Carve the top level into named areas without compound function names.
Honors .gitignore
Discovery walks .gitignore up to the repo root, not above. Predictable across machines.
Powered by Commander
Generated commands are real Commander.js commands. Customize with the setupProgram hook when you need to.
Write a function. Get a command.
Your .ts files stay as they are — uptask does the rest.
// @build.ts
/**
* Build the project.
*
* @param target Build target name
* @param watch Enable watch mode
*/
export function build(
target: string,
watch: boolean = false,
) {
console.log({ target, watch })
}$ uptask build --help
USAGE uptask build [options] <target>
Build the project.
ARGUMENTS
target Build target name
OPTIONS
--watch Enable watch mode
-h, --help display help for commandStop writing CLI glue. Just export a function.
Install, write a function, run it. That's the whole setup.