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']);