Tasks

Short commands you run all day: build, lint, reset the database. You keep them in one file, and you run them from the sidebar.

Just the keys and defaults? See the cheat sheet.

  1. What they are
  2. The short way to write one
  3. The long way to write one
  4. The rest of the file
  5. needs: pulls in what it must, and nothing else
  6. Changing the order you see
  7. The name in front of the colon is the id
  8. Where tasks come from

What they are

A task here is the same kind of task a workflow is made of. The same parser reads both. These ones live in .local-workflows/tasks.yml:

version: 1

tasks:
  setup: echo Restoring dependencies...

They are kept apart from workflows for one reason. A workflow is a pipeline, and you run it as a whole. These are single commands, and you run them one at a time, many times a day.

Every task runs exactly one thing, and that thing is always a plugin. A plain string like the one above is sugar for the shell@1 plugin — the task is the wrapper, the plugin is what executes.


The short way to write one

A plain string is the whole task:

tasks:
  setup: echo Restoring dependencies...
  lint:  npm run lint

The string is short for run:. A run: command uses the shell your OS already has: cmd on Windows, bash everywhere else. You do not need to add anything, and nothing is added for you.

If you write PowerShell, say so with shell: pwsh. Without it, a task using $env:NAME runs in cmd and comes back empty.


The long way to write one

A task here can carry anything a workflow task can carry:

tasks:

  setup: echo Restoring dependencies...

  build:
    name: Build              # the row's label; without it, you see the id
    desc: Compiles the app   # its tooltip
    needs: setup             # one name, or a list of them
    shell: pwsh
    env:
      BUILD_CONFIGURATION: Debug
    run: |
      echo one
      echo two

The rest of the keys:

Key What it does
uses:, args: run a plugin instead of a shell command
cwd: the folder the command runs in
vars:, dotenv: values for this task, next to env:
trigger: manual stop and wait for a person to click Run
output: store what this task printed, for a later task to read
if: run this task only when the condition is true
timeout:, retries: how long it gets, and how many extra tries
continueOnError: let the run carry on when this task fails
session: names an AI conversation; it does nothing for other tasks

That is the whole list. Any other key is an error, so a typo tells you rather than being quietly ignored.


The rest of the file

Five more keys can sit next to tasks:. They set values that every task in the file shares:

version: 1          # you can leave this out; it is read as 1
vars:               # values you paste into fields below, never exported
dotenv:             # .env files, read before env:
plugins:            # name a plugin call once, then use it by that name
env:                # passed to every task as environment variables
tasks:

You can also write name: at the top of the file, but nothing shows it. A tasks.yml is a list of commands, not a pipeline with a title.

Here the rules are the other way round. A key that is not one of these is skipped without a word, so writing taks: gives you an empty list and no error.


needs: pulls in what it must, and nothing else

You can run any task on its own. When you do, whatever it needs: runs first, and whatever those tasks need, and so on. Nothing else in the file runs.

tasks:
  setup: echo Restoring...
  build:
    needs: setup
    run: echo Compiling...

Run build, and setup runs first. That is all.


Changing the order you see

Tasks are listed in the order you wrote them in the file. The Tasks row has one icon that changes this:

The list is in The icon offers
file order Sort Tasks A-Z
A-Z Sort Tasks in File Order

You can do the same from the Command Palette, with Local Workflows: Sort Tasks A-Z and Local Workflows: Sort Tasks in File Order.

A-Z sorts by the label you see, which is name:, or the id when there is no name:. Upper and lower case count as the same letter. Profile tasks are sorted in with the rest, not pushed to the end. If two labels match, those two keep their file order.

Your choice is saved for this workspace only, and nothing is written into your repository. Some projects read best in the order you wrote them. Others are thirty unrelated commands, and then you want them A-Z.


The name in front of the colon is the id

In build:, the word build is the task’s id. That is the name needs: points at, and the name everything else uses too, including the merge below.

This is the one real difference from a workflow. There, a job’s tasks are a list, and each task is known by its position instead of a name.


Where tasks come from

Scope    
folder <any>/.local-workflows/tasks.yml this project’s
workspace beside an open .code-workspace shared by every repository in it
profile ~/.local-workflows/tasks.yml yours, in every workspace you open

The middle one only exists when you opened a .code-workspace file — see Workspaces. Open a plain folder and there are two.

On the folder side, every .local-workflows/tasks.yml in the tree counts, however deep it sits. So a monorepo can keep packages/api/.local-workflows/tasks.yml next to the code it builds. If more than one file has tasks, each row is labelled with the project folder it came from.

You see one list, in that order: folder, then workspace, then profile. A wider task is left out only when a narrower one has the same id. So your personal sync sits happily beside the project’s build, and if the project writes its own sync, the project wins.

When more than one scope has tasks, the rows sit under Folder / Workspace / Profile headings. One scope and the list stays flat.

Tasks that belong to no repository still run in one, not in your home folder — git pull is only useful where the repo is. In a .code-workspace window you are asked which one, every time you run it.



Back to top

Local Workflows is a VS Code extension. Everything it does is declared in a YAML file you own.