New Persistent assistants now work in governed, cross-host teams. See what changed

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
      }
    }
  ]
}
FieldRequiredMeaning
versionyesMust be 1. Any other value disables the file (with a visible diagnostic).
idyesStable identifier. Also the override hook — see below.
labelyesDisplay name in the catalog and on run tabs.
commandyesA 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.
behaviorno"one-shot" (default) or "persistent" for services that run until stopped.
defaultnotrue floats the task to the top of its group with a Default badge.
readinessnoWhen a service counts as ready — see below. Only meaningful with "behavior": "persistent".
cwdnoWorking 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.
envnoExtra 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.
problemMatchernoParses 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:

SourceId
Root package.json scriptpackage.json:<script>
Workspace member script<member-path>/package.json:<script>
Root Cargo taskCargo.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 ports accepts a TCP connection ("host:port" strings), and
  • if logContains is 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.