js/commands/task
js/commands/task.ts
fino:commands/task — reusable project-local task command loader.
Implements the fino task subcommand: it turns a project's tasks/
directory into a CLI command tree at runtime. Every direct file in the
directory with a script extension (.ts, .mts, .cts, .js, .mjs,
.cjs) must default-export one Task from fino:task; dotfiles and
.d.ts declarations are skipped, and subdirectories are not searched.
Discovered tasks are mounted as children of a generated root whose CLI name
is fino task, and the remaining argv (task name, options, positionals) is
delegated to that tree — so fino task build --watch behaves as if build
were a built-in command. Running fino task with no arguments prints the
generated root's help, which lists every discovered task.
Task files are imported in lexicographic path order, and each task name may
appear only once across the directory: a duplicate name is a hard error
naming both files. Default exports are recognized by the shared
Symbol.for('fino.task') brand rather than instanceof, so tasks
constructed by a different copy of the fino:task module still qualify.
Output follows the standard task writer contract. When the invoking CLI
requested JSON (--json), the caller's JSON writer is passed through to the
project task unchanged; in text mode the loader substitutes a writer that
writes directly to process stdout and flushes after every chunk, so
long-running tasks stream output incrementally.
// tasks/build.ts — one project task file, run as `fino task build`
import { task } from 'fino:task';
export default task({
name: 'build',
description: 'Compile the project',
cli: { options: [{ flags: '--watch', type: 'boolean' }] },
run: async (input: { watch?: boolean }, ctx) => {
await ctx.writer.writeText(`building (watch=${input.watch ?? false})\n`);
},
});
// Embedding the command in another CLI surface.
import taskCommand from 'fino:commands/task';
await taskCommand.parse(['build', '--watch']);
Constants
const command
The task subcommand mounted by the root Fino CLI.
Accepts --dir to select the directory containing task modules (default
tasks, resolved against the process working directory) and forwards every
remaining token to the loaded task tree. Unknown options are allowed at this
level and --help is not intercepted, so both flow through to the selected
project task — fino task build --help prints the help of build, not of
the loader.
Execution fails with an error if the directory does not exist, contains no
task files, a file does not default-export a Task, or two files export
tasks with the same name.
import taskCommand from 'fino:commands/task';
// Equivalent to running `fino task deploy --env prod` from the shell.
await taskCommand.parse(['--dir', 'ops/tasks', 'deploy', '--env', 'prod']);