Configuring tasks (.glueprint/tasks.json)
Define custom commands, mark services, declare readiness checks, and override how discovered package scripts behave.
On this page
.glueprint/tasks.json at the root of a worktree is the run dock’s configuration file. It does two jobs: it defines custom commands that don’t exist as package scripts, and it overrides how discovered tasks behave. It’s a normal project file — commit it, like .vscode/tasks.json, so the whole team gets the same catalog.
The fastest way to create one is New command in the dock (or Edit task configuration in the + menu) — Glueprint scaffolds a valid file and opens it in the IDE.
The schema
{
"version": 1,
"tasks": [
{
"id": "session-checks",
"label": "Desktop session checks",
"command": "pnpm vitest run src/components/chat",
"behavior": "one-shot",
"default": true
},
{
"id": "dev-stack",
"label": "Full dev stack",
"command": "pnpm dev:stack",
"behavior": "persistent",
"readiness": {
"ports": ["localhost:3000", "localhost:5173"],
"logContains": "ready in",
"timeoutMs": 120000
}
}
]
}
| Field | Required | Meaning |
|---|---|---|
version | yes | Must be 1. Any other value disables the file (with a visible diagnostic). |
id | yes | Stable identifier. Also the override hook — see below. |
label | yes | Display name in the catalog and on run tabs. |
command | yes | A full command line, run through the platform shell (sh -c on macOS/Linux, cmd /C on Windows) in the worktree root, or in cwd when the entry sets one. Pipes, &&, and env prefixes work. |
behavior | no | "one-shot" (default) or "persistent" for services that run until stopped. |
default | no | true floats the task to the top of its group with a Default badge. |
readiness | no | When a service counts as ready — see below. Only meaningful with "behavior": "persistent". |
cwd | no | Working directory for the command, relative to the worktree root and always forward slashed ("portal", "packages/api"). Defaults to the worktree root. Absolute paths, backslashes, .., and null bytes are refused with a diagnostic and the entry is skipped. |
env | no | Extra environment variables, as an object of strings. They are added on top of the environment Glueprint runs with and override same-named variables. An empty key, an equals sign in a key, or a null byte in either half is refused with a diagnostic and the entry is skipped. |
problemMatcher | no | Parses the run’s output into problems. Write tsc, eslint, or msCompile (typescript, typecheck, lint, dotnet, and msbuild are accepted too, and normalize to those three). Any other value is reported as a diagnostic, the task still runs, and Glueprint falls back to guessing from the label. |
Unknown keys are ignored, so the file stays forward-compatible. A malformed file never silently empties your catalog — the parse error appears as a diagnostic in the Tasks tab, and the same-id rules below apply per entry (a duplicated id keeps the last entry and flags a diagnostic).
Overriding discovered tasks
Every discovered task has a predictable id built from the file it came from:
| Source | Id |
|---|---|
Root package.json script | package.json:<script> |
| Workspace member script | <member-path>/package.json:<script> |
| Root Cargo task | Cargo.toml:<command-slug> or src-tauri/Cargo.toml:<command-slug> |
| Cargo member task | <member-path>/Cargo.toml:<command-slug> |
| Solution task | <Solution>.sln:<verb> or <Solution>.slnx:<verb> |
| Project task | <path>/<Project>.csproj:<verb> or <path>/<Project>.fsproj:<verb> |
Member paths are always forward slashed and relative to the worktree root, so
a portal script is portal/package.json:dev regardless of platform. A
tasks.json entry with the same id replaces the discovered task entirely —
your label, command, behavior, default flag, readiness, cwd, env, and
problemMatcher all win, and the task moves to the Configured group. Ids
are stable: a root task’s id never changes because a workspace was added around
it.
Cargo suffixes are lowercase, hyphenated slugs of the command key, not package
or binary names. Examples include Cargo.toml:fmt-all,
Cargo.toml:clippy-workspace, and Cargo.toml:run-bin-my-app for a binary named
my_app.
A solution task id can be App.sln:build or App.slnx:build. A project task id
carries its root-relative forward-slash path, so src/Api/Api.csproj:build and
src/Api/Api.fsproj:build are both valid. The verbs are restore, build,
test, format, and run.
The most common use is fixing classification. Glueprint guesses that scripts named dev, start, serve, watch, or preview (including prefixed forms like watch:api) are services; everything else is a one-shot. When the guess is wrong, override it:
{
"version": 1,
"tasks": [
{
"id": "package.json:e2e",
"label": "E2E (keeps a browser open)",
"command": "npm run e2e",
"behavior": "persistent"
},
{
"id": "package.json:dev",
"label": "Dev server",
"command": "npm run dev",
"behavior": "persistent",
"readiness": ["localhost:5173"]
}
]
}
The second entry shows the other common reason to override a script that was already classified correctly: attaching readiness to it, so the dock can show the violet Ready state when the server is actually up rather than merely started.
Working directory and environment
{
"version": 1,
"tasks": [
{
"id": "portal/package.json:dev",
"label": "Portal dev server",
"command": "pnpm dev",
"cwd": "portal",
"env": { "PORT": "5174" },
"behavior": "persistent",
"readiness": ["localhost:5174"]
},
{
"id": "typecheck-all",
"label": "Typecheck everything",
"command": "pnpm typecheck",
"problemMatcher": "tsc"
}
]
}
cwd is relative to the worktree root and always forward slashed, so the same
committed file works on macOS, Linux, and Windows. . segments are ignored and
an empty value means the root. Nothing is expanded: there is no ~ handling and
no variable substitution, so the value is used exactly as written. A directory
that doesn’t exist yet isn’t flagged when the file is read — the run fails when
you start it.
This matters most for overrides. An entry replaces the discovered task
entirely, so an override of a workspace member script runs in the worktree root
until you give it a cwd — the first example above is the fix, and it is worth
checking any override you already have.
env values are literal too. "PATH": "$PATH:/opt/bin" sets that text, not an
expanded path; put expansions in command, which runs through a shell.
problemMatcher is what turns compiler output into the problems list. Without
it, Glueprint still guesses from the label — a task labelled Typecheck gets
the TypeScript matcher — so set it explicitly when the guess is wrong or the
label doesn’t hint at the tool.
msCompile reads MSBuild’s diagnostic format, which is what dotnet build
emits; discovered .NET build and test tasks set it for you. On dotnet test,
the diagnostics it surfaces come from the build that runs first. Failing test
assertions are not MSBuild diagnostics and do not appear in the problems list
— read the run’s output for those. This matches VS Code’s behavior with the
same matcher.
Readiness
A service with no readiness counts as ready the moment it spawns. With readiness declared, the dock probes every 500ms until:
- every entry in
portsaccepts a TCP connection ("host:port"strings), and - if
logContainsis set, that substring has appeared in the service’s output.
The shorthand "readiness": ["localhost:3000"] is equivalent to { "ports": ["localhost:3000"] }. When readiness passes, the run flips to Ready and the output shows one Ready at http://... line per port. If it hasn’t passed within timeoutMs (default 120000), the service keeps running — you just get a notice line in the output instead of a Ready badge; readiness never kills a run.