@bro-code/tasks
v2026.2.1
Published
A task runner with animated progress bars, step spinners, child-process log capture, interactive prompt handling, and full configurability via bro.config — powered by @bro-code/logger.
Maintainers
Readme
@bro-code/tasks
A task runner with animated progress bars, step spinners, child-process log capture, interactive prompt handling, and full configurability via bro.config — powered by @bro-code/logger.
Installation
npm install @bro-code/tasksQuick Start
TypeScript
import { Task } from '@bro-code/tasks';
const task = new Task({ message: 'Deploying app', type: 'rocket' });
await task.step('Compile', async (ctx) => {
ctx.log('tsc completed');
});
await task.step('Upload', async (ctx) => {
await uploadFiles();
ctx.log('3 files uploaded');
});
task.end();JavaScript (ESM)
import { Task } from '@bro-code/tasks';
const task = new Task({ message: 'Setup project', type: 'build' });
await task.step('Install deps', async (ctx) => {
ctx.log('npm install completed');
});
task.end();JavaScript (CommonJS)
const { Task } = require('@bro-code/tasks');
const task = new Task({ message: 'Setup project', type: 'build' });
async function main() {
await task.step('Install deps', async (ctx) => {
ctx.log('npm install completed');
});
task.end();
}
main();Features
- Progress bar — animated bar at the top showing overall completion
- Step spinners — each step gets its own spinner animation while running
- Log capture —
ctx.log()captures output displayed after step completion - Interactive prompts —
ctx.prompt()andctx.select()pause the animation for user input, then restore the clean display - Child process runner —
run()executes commands and pipes output into step logs - Conditional steps —
task.set()acceptsshouldAddflags to skip steps - Error persistence — optionally write error details to
.logs/directory - Configurable — reads
bro.config.jsonfor defaults (spinner, colors, progress bar width, etc.)
API
new Task(options)
Create a new task runner.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| message | string | — | Required. Display message for the task |
| type | string | 'default' | Icon type (maps to symbol table) |
| spinner | string | 'pulseDot' | Spinner animation name |
| frameInterval | number | 80 | Animation speed in ms |
| progressBarWidth | number | 20 | Width of the progress bar |
| showDuration | boolean | true | Show elapsed time per step |
| showProgressBar | boolean | true | Show the overall progress bar |
| showStepLogs | boolean | true | Show captured logs inline |
| maxLogLines | number | 5 | Max log lines displayed per step |
| persistErrors | boolean | false | Write errors to log files |
| logDir | string | '.logs' | Directory for error log files |
| activeColor | string | 'cyan' | Color for in-progress text |
| successColor | string | 'green' | Color for success text |
| failColor | string | 'red' | Color for failure text |
| parallel | boolean | false | Run steps in parallel instead of sequentially |
| stopOnFail | boolean | false | Stop remaining steps when a step fails (sequential only) |
task.step(name, fn, options?)
Add and execute a step. The function receives a StepContext:
await task.step('My step', async (ctx) => {
ctx.log('Captured log line');
const answer = await ctx.prompt('Enter value');
const choice = await ctx.select('Pick one', ['A', 'B', 'C']);
});
// With a per-step spinner override:
await task.step('Loading', async () => { ... }, { spinnerName: 'braille' });task.set(steps)
Execute conditional steps (matches the legacy API pattern):
await task.set([
{ name: 'Create .env', shouldAdd: !envExists, method: async () => { ... } },
{ name: 'Create SSL', shouldAdd: !sslExists, method: async () => { ... } },
{ name: 'Create database', shouldAdd: !dbExists, method: async () => { ... } },
]);task.run(steps)
Execute an array of step definitions:
await task.run([
{ name: 'Lint', fn: async () => { ... } },
{ name: 'Test', fn: async () => { ... }, enabled: false },
{ name: 'Build', fn: async () => { ... }, spinnerName: 'build' },
]);Parallel Execution
Run all steps simultaneously:
const task = new Task({ message: 'Parallel work', parallel: true });
await task.run([
{ name: 'Download A', fn: async () => { ... } },
{ name: 'Download B', fn: async () => { ... } },
{ name: 'Download C', fn: async () => { ... } },
]);Priority Steps
When running in parallel, mark steps as priority to execute them sequentially first before the remaining steps run in parallel:
const task = new Task({ message: 'Install deps', parallel: true });
await task.set([
{ name: 'Root', priority: true, method: async () => { ... } }, // runs first
{ name: 'Server', method: async () => { ... } }, // then these
{ name: 'Client', method: async () => { ... } }, // run in parallel
]);Also works with task.run():
await task.run([
{ name: 'Prepare', priority: true, fn: async () => { ... } },
{ name: 'Build A', fn: async () => { ... } },
{ name: 'Build B', fn: async () => { ... } },
]);task.end()
Finalize the display. Called automatically by set() and run().
Stop on Failure
Abort remaining steps when a step fails. Skipped steps show a log explaining why:
const task = new Task({ message: 'Deploy', stopOnFail: true });
await task.set([
{ name: 'Build', method: async () => { ... } }, // if this fails...
{ name: 'Upload', method: async () => { ... } }, // ...these are skipped
{ name: 'Notify', method: async () => { ... } }, // with "Skipped — Build failed"
]);Also works with task.run():
const task = new Task({ message: 'Pipeline', stopOnFail: true });
await task.run([
{ name: 'Lint', fn: async () => { ... } },
{ name: 'Test', fn: async () => { ... } },
{ name: 'Build', fn: async () => { ... } },
]);task.skip()
Mark remaining steps as skipped and finalize.
run(cmd, args, ctx?, opts?)
Execute a child process with log capture:
import { Task, run } from '@bro-code/tasks';
const task = new Task({ message: 'Building' });
await task.step('Compile', async (ctx) => {
await run('tsc', ['--project', 'tsconfig.json'], ctx, { cwd: './packages/app' });
});
task.end();runInteractive(cmd, args, opts?)
Run a command with stdio: 'inherit' for full interactivity.
Available Spinners
pulseDot basic braille arrows dots circles corners blocks bars triangle pulseC line quads moon clock globe build rocket files test security sync hourglass start tree clean load theme weather colors squares
Configuration
Add a tasks section to your bro.config.json:
{
"logger": { ... },
"tasks": {
"spinner": "braille",
"type": "default",
"frameInterval": 100,
"progressBarWidth": 30,
"showDuration": true,
"showProgressBar": true,
"showStepLogs": true,
"maxLogLines": 10,
"activeColor": "cyan",
"successColor": "green",
"failColor": "red",
"persistErrors": true,
"logDir": ".logs",
"parallel": false,
"stopOnFail": false
}
}License
MIT © Bro Code Technologies
Changelog
[2026.2.1] - 2026-04-23
Fixed
- CJS build:
tsconfig.cjs.jsoncorrected to emit real CommonJS output. The previousmodule: Node16setting combined with"type": "module"caused TypeScript to emit ESM syntax (export {) intodist/cjs/, which brokerequire()in Node.js environments.
[2026.2.0] - 2026-04-19
Added
stopOnFailoption — when enabled, remaining sequential steps are skipped after a failure with a log message explaining which step caused the abort
Fixed
- Multi-line error messages (e.g. from failed child processes) no longer break the animation by miscounting rendered lines — error messages are now split into individual lines for correct cursor tracking
- Skipped steps now display their captured logs (previously only success/fail steps showed logs)
[2026.1.2] - 2026-04-19
Documentation
- Added usage examples for all options in the README
[2026.1.1] - 2026-04-19
Changed
- Replaced
console.login prompt UI with@bro-code/logger
[2026.1.0] - 2026-04-19
Added
priorityfield onStepDefinitionandConditionalStep— whenparallel: true, priority steps run sequentially first, then remaining steps run in parallel
[2026.0.1] - 2026-04-19
Fixed
- Avoid Node.js DEP0190 warning by refactoring spawn argument handling in shell mode
[2026.0.0] - 2026-04-18
Added
Taskclass with animated progress bar and step spinnersctx.log()for capturing step outputctx.prompt()andctx.select()for interactive input with animation pause/resumerun()helper for child process execution with log capturerunInteractive()for interactive child processestask.set()for conditional step batches (legacy API compatible)task.run()for step definition arraystask.skip()to skip remaining steps- Configurable via
bro.config.jsontaskssection - 30+ built-in spinner animations
- Error persistence to log files
- Duration tracking per step
- Parallel step execution
- Per-step spinner overrides
- Dual ESM/CJS output with full TypeScript types
