CLI Task Runner and Build Tool
Build a command-line task runner in TypeScript, a small alternative to npm scripts or Make. You describe tasks in a tasks.json file: each task has a shell command and can depend on other tasks. Running runner build runs build's dependencies first, in the right order, runs tasks that don't depend on each other at the same time, and shows colored output with a timing summary. If a task fails, the tasks that depend on it are skipped. You'll learn to design a typed configuration format and validate it, build a CLI with Commander, order tasks with a topological sort and detect cycles, run child processes and stream their output, and control how many tasks run in parallel.
- Estimate
- ~13h
- Steps
- 7
- Completed by
- 0
- Proposed by
- codeseed.app
Node.js · Commander · chalk
Project roadmap
- 01
Set up the CLI
~1.5hCreate a Node.js project with TypeScript in strict mode, and install commander and chalk. Set up a bin entry so the tool runs as runner. Add the commands run <task...>, list and graph with Commander, each printing a placeholder. Add a --config option (default tasks.json) and a --parallel option (default 4). Done when: runner --help lists the three commands and both options.
- 02
Define and validate the config
~2hDefine TypeScript types: a config with a tasks object, where each task has cmd (a string), optional deps (task names), optional cwd and optional env. Validate the loaded JSON at runtime, for example with zod, and report every problem with its path, like tasks.test.deps[0]: unknown task lint. Report dependencies on tasks that don't exist. Done when: a valid tasks.json loads, and a broken one lists every error.
- 03
Order tasks and detect cycles
~2hGiven the requested tasks, collect them and all their dependencies, then order them with a topological sort (Kahn's algorithm). If there's a cycle, report it with the task names involved, like build -> test -> build, and exit with code 2. The list command prints tasks with their dependencies, and graph prints the execution order. Done when: runner graph build prints a correct order, and a config with a cycle reports it.
- 04
Run tasks as child processes
~2hRun each task's command with child_process.spawn using shell: true, with the task's cwd and env merged into process.env. Stream stdout and stderr live, prefixing each line with the task name in its own color. A non-zero exit code marks the task as failed. Done when: a chain of tasks runs in order and their output appears live with prefixes.
- 05
Run independent tasks in parallel
~2.5hInstead of running the sorted list one by one, start every task whose dependencies have all succeeded, up to --parallel tasks at a time. When a task fails, mark every task that depends on it, directly or indirectly, as skipped, but let unrelated tasks finish. Done when: two independent tasks run at the same time, and a failure skips only its dependents.
- 06
Print a colored summary
~1hAt the end, print a summary with chalk: each task with a status icon (green for success, red for failed, gray for skipped) and its duration, then the total time. Exit with code 1 if any task failed. Stop all running child processes when the user presses Ctrl+C. Done when: the summary is correct and the exit code reflects failures.
- 07
Write tests
~2hWrite tests with Vitest for config validation, the topological sort, cycle detection, and a full run with small commands (such as node -e) covering a successful chain, a failing task that skips its dependents, and parallel tasks. Done when: npm test passes.
Resources
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack