Skip to main content

Custom tasks and aliases

Declare tasks alongside resources, using the same imported fragments and platform/machine conditions:

import { defineConfig, task } from "@dovocode/workstation";

export default defineConfig({
tasks: {
test: task("pnpm", ["test"], { description: "Run project tests" }),
"build-app": task("pnpm", ["build"], { cwd: "app" }),
},
aliases: { t: "test", b: "build-app" },
});
workstation --list-tasks
workstation test
workstation t -- --watch
workstation --machine studio build-app

A task invocation does not install resources or write the lock, manifest, or ownership state. It evaluates the configuration and executes only the selected command. Run workstation build to reconcile the setup.

Task commands run directly without shell interpolation. Extra arguments are appended unchanged. For shell pipelines, explicitly declare a shell command such as task("bash", ["-lc", "first-command && second-command"]). CLI task output streams live and is also captured; its exit code is returned by the CLI. Stdin is inherited, but stdout/stderr are pipes and no PTY is allocated. Programs requiring a full interactive terminal may behave differently.

Post-apply scriptsโ€‹

Use afterApply for commands that must run after workstation build or workstation upgrade successfully reconciles resources:

import { defineConfig, task } from "@dovocode/workstation";

export default defineConfig({
afterApply: [
task("echo", ["Workstation reconciliation complete"], {
description: "Report successful reconciliation",
}),
],
});

Hooks use the same command options as tasks: literal arguments, a working directory relative to the configuration entry point (defaulting to that directory), and environment overrides. Use an explicit shell for shell syntax. Hooks from imported and platform/machine fragments append in declaration order. They are validated before applying resources and saved in the resolved manifest.

Hooks run sequentially and stream output, including on successful runs with no resource changes. Write them to be safe to repeat. The first failure stops later hooks and makes the command fail; already-applied resources remain in place. Hooks do not run if reconciliation fails, or during plan, tasks, diagnostics, lock updates, or rollback. The embedded client's build() also runs them.

Keep source-based builds in customTool so source changes control rebuilding. Use managed resources for service activation and health checks. Use a post-apply script for follow-up work such as reporting successful reconciliation.

Working directories default to the entry point's directory; relative cwd values resolve there and ~ expands to home. environment overrides inherited environment entries. Later fragments override task names. Aliases can chain; cycles, missing targets, and task/alias name collisions are rejected.

Place Workstation's own flags before the task name. All arguments after it belong to the task; one optional -- separator is removed. All built-in command names are reserved, including help, init, build, upgrade, plan, status, doctor, history, rollback, lock, and update. Task aliases are CLI shortcuts, not executable symlinks or shell aliases; use the symlink and shell helpers for those separately.

Runtime bootstrap and embedded tasksโ€‹

A selected CLI task whose command is node, npm, npx, or pnpm prepares its pinned runtime through mise (Node.js 26.8.1 and, for pnpm, pnpm 12.3.4). Merely declaring the task does not install those runtimes during a file-only build. Other task executables must already be available. Embedded client.task() and runTask() do not perform this CLI bootstrap; provide prerequisites explicitly.

Working directory and environment exampleโ€‹

import { defineConfig, task } from "@dovocode/workstation";

export default defineConfig({
tasks: {
test: task("pnpm", ["test"], {
cwd: "projects/app",
environment: { NODE_ENV: "test" },
description: "Test the app from its project directory",
}),
},
aliases: { t: "test", quick: "t" },
});

projects/app is relative to the configuration entry point. Run workstation --config /path/setup.ts quick -- --watch to append --watch to pnpm test. A task argument named --config belongs to the task when it appears after the task name. Environment values override inherited entries only for that process; no .env file is loaded automatically.