File tasks
A file task is an executable script in a task directory. Its path is the task name, and #MISE comments at the top hold its configuration. Use one when a script is long enough to benefit from your editor's highlighting and linting.
Save this as mise-tasks/build:
#!/usr/bin/env bash
#MISE description="Build the CLI"
cargo buildOn Linux and macOS, make the script executable:
chmod +x mise-tasks/buildmise run build now runs the script with the tools and environment from your config. mise lists only executable scripts as tasks. When you run a script that is not executable, mise asks whether to mark it executable, and fails if you decline. Windows has no execute bit; see Windows.
You can still run the script without mise, but its #MISE and #USAGE comments then have no effect, so the caller must supply its tools, environment, and any usage_* variables.
mise-tasks/ is one of several default task directories. Setting task_config.includes replaces them with the paths you list.
Name and group file tasks
A file's path below the task directory is its task name, with / replaced by :. mise-tasks/db/migrate.sh is db:migrate, and you can run it as mise run db:migrate or mise run db:migrate.sh. mise shows the name without a script extension, such as .sh, .py, .js or .ps1, unless another task already has that name. Any other extension stays in the name, so mise-tasks/gen.proto is the task gen.proto. A file named _default takes its directory's name, so mise-tasks/test/_default is test.
For this layout:
mise-tasks
├── build
├── db
│ └── migrate.sh
└── test
├── _default
├── integration
└── unitsmise tasks --extended lists:
Name Aliases Source Description
build ./mise-tasks/build
db:migrate ./mise-tasks/db/migrate.sh
test ./mise-tasks/test/_default
test:integration ./mise-tasks/test/integration
test:units ./mise-tasks/test/unitsmise run 'test:*' runs test:integration and test:units; see Wildcards.
Configure with #MISE comments
Add #MISE comments near the top of the script to set task properties:
#!/usr/bin/env bash
#MISE description="Build the CLI"
#MISE alias="b"
#MISE sources=["Cargo.toml", "src/**/*.rs"]
#MISE outputs=["target/debug/mycli"]
#MISE env={RUST_BACKTRACE = "1"}
#MISE depends=["lint", "test"]
#MISE tools={rust="1.90"}
cargo buildmise run build or its alias mise run b runs this file.
The comment marker can be #, // (for JavaScript, TypeScript, or Go) or :: (for batch files), and whitespace may follow it. The keyword can also be bracketed. #MISE, # MISE, // MISE, and # [MISE] all work, so a formatter that adds a space after # does not break the header. To disable a header line, change the keyword, for example to # NOMISE.
Each #MISE line holds TOML. Headers accept the same task properties as a [tasks.<name>] table, except run, run_windows, file and usage, which the script and its #USAGE lines provide. To configure a script you cannot edit, add a [tasks.<name>] block for it to mise.toml; see Configuring file tasks from TOML.
Multi-line values
An array or inline table can span several lines as long as every line keeps the #MISE prefix, which keeps long depends and sources lists readable:
#!/usr/bin/env bash
#MISE description="Build the CLI"
#MISE depends=[
#MISE "lint",
#MISE "test",
#MISE ]
#MISE sources=[
#MISE "Cargo.toml",
#MISE "src/**/*.rs",
#MISE ]
cargo buildDotted keys build a table one line at a time, without braces:
#MISE tools.node="24"
#MISE tools.python="3.13"Extend a task template
extends names a task template, so several file tasks can share one set of tools, environment variables, and arguments:
[task_templates.rust]
tools = { rust = "1.90" }
env = { RUST_BACKTRACE = "1" }#!/usr/bin/env bash
#MISE extends="rust"
#MISE description="Build the CLI"
cargo buildThe script is the task's command, so a file task ignores the template's run. It inherits everything else by the template merge rules.
Shebang
The shebang selects the interpreter. It is optional on Linux and macOS, but Windows needs it for files without an executable extension (see Windows). Use it to write tasks in any language:
#!/usr/bin/env node
//MISE description="Hello, World in Node.js"
console.log("Hello, World!");#!/usr/bin/env python
#MISE description="Hello, World in Python"
print('Hello, World!')#!/usr/bin/env -S deno run
//MISE description="Hello, World in Deno"
console.log("Hello, World!");#!/usr/bin/env pwsh
#MISE description="Hello, World in PowerShell"
$current_directory = Get-Location
Write-Host "Hello from PowerShell, current directory is $current_directory"Windows
Windows has no execute bit. A file there is a task if its extension is listed in windows_executable_extensions, such as .cmd or .ps1, or if it starts with a shebang. Windows does not implement shebangs itself: mise reads the line and starts the interpreter. A file with neither is not a task on Windows, even though it works on Linux and macOS:
# no shebang and no extension: not a task on Windows
cargo buildAdd #!/usr/bin/env bash to scripts you share. It changes nothing on the other platforms.
PowerShell scripts without .ps1
Windows PowerShell runs only files whose names end in .ps1, so mise runs a #!/usr/bin/env pwsh task from a temporary .ps1 copy and removes the copy when the task finishes. $PSScriptRoot and $PSCommandPath name the copy; the working directory, $args, and the environment are unchanged.
To find files next to the task, read $env:MISE_TASK_DIR, which names the directory of the task file on every platform (see Task environment). Or give the task a .ps1 extension, which runs in place.
One task, two scripts
A file task has no equivalent of a TOML task's run_windows. Instead, put a POSIX script and a Windows script with the same stem in the same directory:
mise-tasks/
build.sh # #!/usr/bin/env bash
build.ps1 # the Windows versionOn Windows, mise runs build.ps1 as build and drops the POSIX script. On Linux and macOS, the .ps1 has no execute bit, so only build.sh is a task. mise run build picks the right script on each platform. The POSIX script is any file without one of the windows_executable_extensions, so a script with no extension, such as build, pairs the same way.
Keep the .ps1 non-executable on Linux and macOS. If it is executable, it is a task there too: next to build.sh it also answers to build, so mise run build runs both scripts. When there is more than one Windows candidate, such as build.ps1 and build.cmd, mise does not choose between them on Windows and keeps all three files as tasks; run one by its full name, such as mise run build.cmd.
To give the scripts unrelated names, or to choose explicitly, use a TOML task that calls them:
[tasks.build]
run = "./scripts/build.sh"
run_windows = "pwsh -File ./scripts/windows-build.ps1"The Windows command calls pwsh explicitly because windows_default_inline_shell_args is cmd /c by default, and cmd does not start a .ps1 file by itself.
Create or edit a file task
mise tasks edit build opens build in $EDITOR. If the task does not exist, mise first creates an executable script with a Bash shebang in the first task directory that exists, or in mise-tasks/ when there is none. mise tasks add --file creates a file task from a command:
mise tasks add --file hello -- echo helloArguments
Declare arguments with #USAGE comments. mise parses them, passes each value to the script as a usage_* environment variable, and provides --help and shell completions:
#!/usr/bin/env bash
set -e
#USAGE flag "-c --clean" help="Clean the build directory before building"
#USAGE flag "-p --profile <profile>" help="Build with the specified profile" default="dev" {
#USAGE choices "dev" "release"
#USAGE }
#USAGE flag "-u --user <user>" help="The user to build for"
#USAGE complete "user" run="mycli users"
#USAGE arg "<target>" help="The target to build"
if [ "${usage_clean:-false}" = "true" ]; then
cargo clean
fi
cargo build --profile "${usage_profile?}" --target "${usage_target?}"With mise's shell completions enabled, mise run build --profile <Tab> offers dev and release, and mise run build --user <Tab> offers the output of mycli users. The separate usage CLI is not needed. mise run build --help prints help for the task, and mise generate task-docs renders Markdown documentation from the same spec.
Put mise's own flags before the task name, as in mise run --dry-run build --profile release x86_64-unknown-linux-gnu. Everything after the task name goes to the task.
Task arguments covers the spec syntax, reading values in Bash, and environment variable backing.
If completions or --help do not reflect your spec, run mise tasks validate or the task itself. An invalid spec produces a warning such as invalid usage spec in task file mise-tasks/build, followed by the line that failed to parse.
A Node.js task with arguments
//USAGE comments declare arguments in JavaScript; mise parses them the same way as #USAGE lines:
#!/usr/bin/env node
//MISE description="Write a greeting to a file"
//USAGE flag "-f --force" help="Overwrite existing <file>"
//USAGE flag "-u --user <user>" help="User to run as"
//USAGE arg "<output_file>" help="The file to write" default="file.txt" {
//USAGE choices "greeting.txt" "file.txt"
//USAGE }
const fs = require("fs");
const { usage_user, usage_force, usage_output_file } = process.env;
if (usage_force === "true") {
fs.rmSync(usage_output_file, { force: true });
}
const user = usage_user ?? "world";
fs.appendFileSync(usage_output_file, `Hello, ${user}\n`);
console.log(`Greeting written to ${usage_output_file}`);mise run greet greeting.txt --user Alice
# Greeting written to greeting.txtmise rejects a value outside the choices before the script starts:
mise run greet invalid.txt --user Alicemise ERROR failed to validate task greet
mise ERROR Invalid choice for arg output_file: invalid.txt, expected one of greeting.txt, file.txtWith completions enabled, mise run greet <Tab> offers greeting.txt and file.txt.
Working directory
File tasks run from the config root, the project directory, such as ~/proj for both ~/proj/mise.toml and ~/proj/.config/mise.toml. Add #MISE dir="{{cwd}}" to run from the directory where you called mise:
#!/usr/bin/env bash
#MISE dir="{{cwd}}"The directory where you called mise is also in MISE_ORIGINAL_CWD, and MISE_PROJECT_ROOT and the other task variables locate the project from any directory:
#!/usr/bin/env bash
cd "$MISE_ORIGINAL_CWD"Run a script by path
mise run ./scripts/build.sh runs any executable script as a task, with its #MISE and #USAGE headers, even outside a task directory:
mise run ./path/to/script.shThe path must start with /, ./, or ../ (on Windows, backslash forms such as .\, ..\, or C:\ also work) and the file must exist. Otherwise mise looks the argument up as a task name. On Linux and macOS the file must also be executable. In a terminal mise offers to mark it executable; otherwise it stops with `./build.sh` is not executable. Run: chmod +x ./build.sh.
A relative path resolves from the current directory, as in a shell, so mise run ./build.sh in a subdirectory runs that subdirectory's build.sh. The script still runs in the config root, like other tasks.