Documentation
Topics Overview Overview Linux macOS Windows VS Code for the Web Raspberry Pi Network Additional Components Uninstall VS Code Tutorial Copilot Quickstart User Interface Personalize VS Code Install Extensions Tips and Tricks Intro Videos Overview Setup Quickstart Overview Language Models Context Tools Agents Customization Trust & Safety Overview Agents Tutorial Agents Window Planning Memory Tools Subagents Local Agents Copilot CLI Cloud Agents Third-Party Agents Overview Chat Sessions Add Context Inline Chat Review Edits Checkpoints Artifacts Panel Debug Chat Interactions Prompt Examples Overview Instructions Prompt Files Custom Agents Agent Skills Language Models MCP Hooks Plugins Context Engineering Customize AI Test-Driven Development Edit Notebooks with AI Test with AI Test Web Apps with Browser Tools Debug with AI MCP Dev Guide OpenTelemetry Monitoring Inline Suggestions Smart Actions Best Practices Security Troubleshooting FAQ Cheat Sheet Settings Reference MCP Configuration Workspace Context Display Language Layout Keyboard Shortcuts Settings Settings Sync Extension Marketplace Extension Runtime Security Themes Profiles Overview Voice Interactions Command Line Interface Telemetry Basic Editing IntelliSense Code Navigation Refactoring Snippets Overview Multi-Root Workspaces Workspace Trust Tasks Debugging Debug Configuration Testing Port Forwarding Integrated Browser Overview Quickstart Staging & Committing Branches & Worktrees Repositories & Remotes Merge Conflicts Collaborate on GitHub Troubleshooting FAQ Getting Started Tutorial Terminal Basics Terminal Profiles Shell Integration Appearance Advanced Overview Enterprise Policies AI Settings Extensions Telemetry Updates Overview JavaScript JSON HTML Emmet CSS, SCSS and Less TypeScript Markdown PowerShell C++ Java PHP Python Julia R Ruby Rust Go T-SQL C# .NET Swift Working with JavaScript Node.js Tutorial Node.js Debugging Deploy Node.js Apps Browser Debugging Angular Tutorial React Tutorial Vue Tutorial Debugging Recipes Performance Profiling Extensions Tutorial Transpiling Editing Refactoring Debugging Quick Start Tutorial Run Python Code Editing Linting Formatting Debugging Environments Testing Python Interactive Django Tutorial FastAPI Tutorial Flask Tutorial Create Containers Deploy Python Apps Python in the Web Settings Reference Getting Started Navigate and Edit Refactoring Formatting and Linting Project Management Build Tools Run and Debug Testing Spring Boot Modernizing Java Apps Application Servers Deploy Java Apps GUI Applications Extensions FAQ Intro Videos GCC on Linux GCC on Windows GCC on Windows Subsystem for Linux Clang on macOS Microsoft C++ on Windows Build with CMake CMake Tools on Linux CMake Quick Start C++ Dev Tools for Copilot Editing and Navigating Debugging Configure Debugging Refactoring Settings Reference Configure IntelliSense Configure IntelliSense for Cross-Compiling FAQ Intro Videos Get Started Navigate and Edit IntelliCode Refactoring Formatting and Linting Project Management Build Tools Package Management Run and Debug Testing FAQ Overview Node.js Python ASP.NET Core Debug Docker Compose Registries Deploy to Azure Choose a Dev Environment Customize Develop with Kubernetes Tips and Tricks Overview Jupyter Notebooks Data Science Tutorial Python Interactive Data Wrangler Quick Start Data Wrangler PyTorch Support Azure Machine Learning Manage Jupyter Kernels Jupyter Notebooks on the Web Data Science in Microsoft Fabric Foundry Toolkit Overview Foundry Toolkit Copilot Tools Create Agents Models Playground Agent Builder Agent Inspector Evaluation Tool Catalog Fine-Tuning (Automated Setup) Fine-Tuning (Project Template) Model Conversion Tracing Profiling (Windows ML) FAQ File Structure Manual Model Conversion Manual Model Conversion on GPU Setup Environment Without Foundry Toolkit Template Project Migrating from Visualizer to Agent Inspector Overview Getting Started Resources View Deployment VS Code for the Web - Azure Containers Azure Kubernetes Service Kubernetes MongoDB Remote Debugging for Node.js Overview SSH Dev Containers Windows Subsystem for Linux GitHub Codespaces VS Code Server Tunnels SSH Tutorial WSL Tutorial Tips and Tricks FAQ Overview Tutorial Attach to Container Create Dev Container Advanced Containers devcontainer.json Dev Container CLI Tips and Tricks FAQ Default Keyboard Shortcuts Default Settings Substitution Variables Tasks SchemaOn this page there are 19 sections
Integrate with External Tools via Tasks
Lots of tools exist to automate tasks like linting, building, packaging, testing, or deploying software systems. Examples include the TypeScript Compiler, linters like ESLint and TSLint as well as build systems like Make, Ant, Gulp, Jake, Rake, and MSBuild.
These tools are mostly run from the command line and automate jobs inside and outside the inner software development loop (edit, compile, test, and debug). Given their importance in the development life cycle, it is helpful to be able to run tools and analyze their results from within VS Code. Tasks in VS Code can be configured to run scripts and start processes so that many of these existing tools can be used from within VS Code without having to enter a command line or write new code. Workspace or folder specific tasks are configured from the tasks.json file in the .vscode folder for a workspace.
Extensions can also contribute tasks using a Task Provider, and these contributed tasks can add workspace-specific configurations defined in the tasks.json file.
Note: Task support is only available when working on a workspace folder. It is not available when editing single files.
TypeScript Hello World
Let's start with a simple "Hello World" TypeScript program that we want to compile to JavaScript.
Create an empty folder "mytask", generate a tsconfig.json file and start VS Code from that folder.
function sayHello(name: string): void { console.log(`Hello ${name}!`); } sayHello('Dave');Pressing ⇧⌘B (Windows, Linux Ctrl+Shift+B) or running Run Build Task from the global Terminal menu show the following picker:
The first entry executes the TypeScript compiler and translates the TypeScript file to a JavaScript file. When the compiler has finished, there should be a HelloWorld.js file. The second entry starts the TypeScript compiler in watch mode. Every save to the HelloWorld.ts file will regenerate the HelloWorld.js file.
You can also define the TypeScript build or watch task as the default build task so that it is executed directly when triggering Run Build Task (⇧⌘B (Windows, Linux Ctrl+Shift+B)). To do so, select Configure Default Build Task from the global Terminal menu. This shows you a picker with the available build tasks. Select tsc: build or tsc: watch and VS Code will generate a tasks.json file. The one shown below makes the tsc: build task the default build task:
{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "type": "npm", "script": "lint", "problemMatcher": ["$eslint-stylish"] } ] }This instructs VS Code to scan the output of the npm lint script for problems using the ESLint stylish format.
For Gulp, Grunt, and Jake, the task auto-detection works the same. Below is an example of the tasks detected for the vscode-node-debug extension.
Tip: You can run your task through Quick Open (⌘P (Windows, Linux Ctrl+P)) by typing 'task', Space and the command name. In this case, 'task lint'.
Task auto detection can be disabled using the following settings:
{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "Run tests", "type": "shell", "command": "./scripts/test.sh", "windows": { "command": ".\\scripts\\test.cmd" }, "group": "test", "presentation": { "reveal": "always", "panel": "new" } } ] }The task's properties have the following semantic:
- label: The task's label used in the user interface.
- type: The task's type. For a custom task, this can either be shell or process. If shell is specified, the command is interpreted as a shell command (for example: bash, cmd, or PowerShell). If process is specified, the command is interpreted as a process to execute.
- command: The actual command to execute.
- windows: Any Windows specific properties. Will be used instead of the default properties when the command is executed on the Windows operating system.
- group: Defines to which group the task belongs. In the example, it belongs to the test group. Tasks that belong to the test group can be executed by running Run Test Task from the Command Palette.
- presentation: Defines how the task output is handled in the user interface. In this example, the Integrated Terminal showing the output is always revealed and a new terminal is created on every task run.
- options: Override the defaults for cwd (current working directory), env (environment variables), or shell (default shell). Options can be set per task but also globally or per platform. Environment variables configured here can only be referenced from within your task script or process and will not be resolved if they are part of your args, command, or other task attributes.
- runOptions: Defines when and how a task is run.
- hide: Hides the task from the Run Task Quick Pick, which can be useful for elements of a compound task that are not independently runnable.
You can see the full set of task properties and values with IntelliSense in your tasks.json file. Bring up suggestions with Trigger Suggest (⌃Space (Windows, Linux Ctrl+Space)) and read the descriptions on hover or with the Read More... ('i') flyout.
You can also review the tasks.json schema.
Shell commands need special treatment when it comes to commands and arguments that contain spaces or other special characters like $. By default, the task system supports the following behavior:
- If a single command is provided, the task system passes the command as is to the underlying shell. If the command needs quoting or escaping to function properly, the command needs to contain the proper quotes or escape characters. For example, to list the directory of a folder containing spaces in its name, the command executed in bash should look like this: ls 'folder with spaces'.
- If you want to control how the argument is quoted, the argument can be a literal specifying the value and a quoting style. The example below uses escaping instead of quoting for an argument with spaces.
If you specify "dependsOrder": "sequence", then your task dependencies are executed in the order they are listed in dependsOn. Any background/watch tasks used in dependsOn with "dependsOrder": "sequence" must have a problem matcher that tracks when they are "done". The following task runs task Two, task Three, and then task One.
{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "type": "npm", "script": "lint", "problemMatcher": ["$eslint-stylish"], "presentation": { "reveal": "never" } } ] }You can also mix custom tasks with configurations for detected tasks. A tasks.json that configures the npm: run lint task and adds a custom Run Test tasks looks like this:
const gulp = require('gulp'); const eslint = require('gulp-eslint'); gulp.task('lint', () => { // ESLint ignores files with "node_modules" paths. // So, it's best to have gulp ignore the directory as well. // Also, Be sure to return the stream from the task; // Otherwise, the task may end before the stream has finished. return ( gulp .src(['**/*.js', '!node_modules/**']) // eslint() attaches the lint output to the "eslint" property // of the file object so it can be used by other modules. .pipe(eslint()) // eslint.format() outputs the lint results to the console. // Alternatively use eslint.formatEach() (see Docs). .pipe(eslint.format()) // To have the process exit with an error code (1) on // lint error, return the stream and pipe to failAfterError last. .pipe(eslint.failAfterError()) ); }); gulp.task('default', ['lint'], function() { // This will only run if the lint task is successful... });Executing Run Task from the global Terminal menu will show the following picker:
Press the gear icon. This will create the following tasks.json file:
{ "key": "ctrl+h", "command": "workbench.action.tasks.runTask", "args": "Run tests" }Variable substitution
When authoring tasks configurations, it is useful to have a set of predefined common variables such as the active file (${file}) or workspace root folder (${workspaceFolder}). VS Code supports variable substitution inside strings in the tasks.json file and you can see a full list of predefined variables in the Variables Reference.
Note: Not all properties will accept variable substitution. Specifically, only command, args, and options support variable substitution.
Below is an example of a custom task configuration that passes the current opened file to the TypeScript compiler.
{ "label": "autopep8 current file", "type": "process", "command": "${config:python.formatting.autopep8Path}", "args": ["--in-place", "${file}"] }If you want to specify the selected Python interpreter used by the Python extension for tasks.json or launch.json, you can use the ${command:python.interpreterPath} command.
If simple variable substitution isn't enough, you can also get input from the user of your task by adding an inputs section to your tasks.json file.
For more information about inputs, see the Variables Reference.
Operating system specific properties
The task system supports defining values (for example, the command to be executed) specific to an operating system. To do so, put an operating system specific literal into the tasks.json file and specify the corresponding properties inside that literal.
Below is an example that uses the Node.js executable as a command and is treated differently on Windows and Linux:
{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "presentation": { "panel": "new" }, "tasks": [ { "label": "TS - Compile current file", "type": "shell", "command": "tsc ${file}", "problemMatcher": ["$tsc"] } ] }Tip: To get access to the global scope tasks.json file, open the Command Palette (⇧⌘P (Windows, Linux Ctrl+Shift+P)) and run the Tasks: Open User Tasks command.
Character escaping in PowerShell
When the default shell is PowerShell, or when a task is configured to use PowerShell, you might see unexpected space and quote escaping. The unexpected escaping only occurs with cmdlets because VS Code doesn't know if your command contains cmdlets. Example 1 below shows a case where you'll get escaping that doesn't work with PowerShell. Example 2 shows the best, cross-platform, way to get good escaping. In some cases, you might not be able to follow example 2 and you'll need to do the manual escaping shown in example 3.
{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "more", "type": "shell", "command": "chcp 866 && more russian.txt", "problemMatcher": [] } ] }If the task is executed in PowerShell, the command needs to read like this chcp 866; more russian.txt. On Linux and macOS, the locale command can be used to inspect the locale and tweak the necessary environment variables.
Examples of tasks in action
To highlight the power of tasks, here are a few examples of how VS Code can use tasks to integrate external tools like linters and compilers.
Transpiling TypeScript to JavaScript
The TypeScript topic includes an example that creates a task to transpile TypeScript to JavaScript and observe any related errors from within VS Code.
Transpiling Less and SCSS into CSS
The CSS topic provides examples of how to use Tasks to generate CSS files.
Defining a problem matcher
VS Code ships some of the most common problem matchers 'in-the-box'. However, there are lots of compilers and linting tools out there, all of which produce their own style of errors and warnings so you may want to create your own problem matcher.
We have a helloWorld.c program in which the developer mistyped printf as prinft. Compiling it with gcc will produce the following warning:
{ // The problem is owned by the cpp language service. "owner": "cpp", // The file name for reported problems is relative to the opened folder. "fileLocation": ["relative", "${workspaceFolder}"], // The name that will be shown as the source of the problem. "source": "gcc", // The actual pattern to match problems in the output. "pattern": { // The regular expression. Example to match: helloWorld.c:5:3: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration] "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", // The first match group matches the file name which is relative. "file": 1, // The second match group matches the line on which the problem occurred. "line": 2, // The third match group matches the column at which the problem occurred. "column": 3, // The fourth match group matches the problem's severity. Can be ignored. Then all problems are captured as errors. "severity": 4, // The fifth match group matches the message. "message": 5 } }Note that the file, line, and message properties are mandatory. The fileLocation specifies whether the file paths that are produced by the task output and matched in the problem are absolute or relative. If the task produces both absolute and relative paths, you can use the autoDetect file location. With autoDetect, paths are first tested as absolute paths, and if the file doesn't exist then the path is assumed to be relative.
The severity specifies which problem severity to use if the pattern doesn't include one. The possible values for severity are error, warning, or info.
Here is a finished tasks.json file with the code above (comments removed) wrapped with the actual task details:
test.js 1:0 error Missing "use strict" statement strict ✖ 1 problems (1 errors, 0 warnings)Our problem matcher is line-based so we need to capture the file name (test.js) with a different regular expression than the actual problem location and message (1:0 error Missing "use strict" statement).
To do this, use an array of problem patterns for the pattern property. This way you define a pattern per each line you want to match.
Note: In a multi-line problem matcher, each line in the output must be matched sequentially by the pattern array. Intermediate lines cannot be skipped, even if they are not needed for capturing values.
The following problem pattern matches the output from ESLint in stylish mode - but still has one small issue that we need to resolve next. The code below has a first regular expression to capture the file name and the second to capture the line, column, severity, message, and error code:
test.js 1:0 error Missing "use strict" statement strict 1:9 error foo is defined but never used no-unused-vars 2:5 error x is defined but never used no-unused-vars 2:11 error Missing semicolon semi 3:1 error "bar" is not defined no-undef 4:1 error Newline required at end of file but not found eol-last ✖ 6 problems (6 errors, 0 warnings)The pattern's first regular expression will match "test.js", the second "1:0 error ...". The next line "1:9 error ..." is processed but not matched by the first regular expression and so no problem is captured.
To make this work, the last regular expression of a multiline pattern can specify the loop property. If set to true, it instructs the task system to apply the last pattern of a multiline matcher to the lines in the output as long as the regular expression matches.
The information captured by the first pattern, which in this case matches test.js, will be combined with each of the subsequent lines that match the loop pattern to create multiple problems. In this example, six problems would be created.
Here is a problem matcher to fully capture ESLint stylish problems:
{ "type": "npm", "script": "watch", "problemMatcher": { "base": "$tsc-watch", "applyTo": "allDocuments" }, "isBackground": true }Other modifiable problem matcher properties include background, fileLocation, owner, pattern, severity, and source.
Background / watching tasks
Some tools support running in the background while watching the file system for changes and then triggering an action when a file changes on disk. With Gulp such functionality is provided through the npm module gulp-watch. The TypeScript compiler tsc has built in support for this via the --watch command line option.
To provide feedback that a background task is active in VS Code and producing problem results, a problem matcher has to use additional information to detect these state changes in the output. Let's take the tsc compiler as an example. When the compiler is started in watch mode, it prints the following additional information to the console:
12:32:35 PM - File change detected. Starting incremental compilation... src/messages.ts(276,9): error TS2304: Cannot find name 'candidate'. 12:32:35 PM - Compilation complete. Watching for file changes.Looking at the output shows the following pattern:
- The compiler runs when File change detected. Starting incremental compilation... is printed to the console.
- The compiler stops when Compilation complete. Watching for file changes. is printed to the console.
- Between those two strings problems are reported.
- The compiler also runs once the initial start (without printing File change detected. Starting incremental compilation... to the console).
To capture this information, a problem matcher can provide a background property.
For the tsc compiler, an appropriate background property looks like this:
{ "version": "2.0.0", "tasks": [ { "label": "watch", "command": "tsc", "args": ["--watch"], "isBackground": true, "problemMatcher": { "owner": "typescript", "fileLocation": "relative", "pattern": { "regexp": "^([^\\s].*)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\):\\s+(error|warning|info)\\s+(TS\\d+)\\s*:\\s*(.*)$", "file": 1, "location": 2, "severity": 3, "code": 4, "message": 5 }, "background": { "activeOnStart": true, "beginsPattern": "^\\s*\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM)? - File change detected\\. Starting incremental compilation\\.\\.\\.", "endsPattern": "^\\s*\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM)? - Compilation complete\\. Watching for file changes\\." } } } ] }Next steps
That was tasks - let's keep going...
- tasks.json Schema - You can review the full tasks.json schema and descriptions.
- Basic Editing - Learn about the powerful VS Code editor.
- Code Navigation - Move quickly through your source code.
- Language Support - Learn about our supported programming languages, both shipped with VS Code and through community extensions.
- Debugging - Debug your source code directly in the VS Code editor.
Common questions
Can a task use a different shell than the one specified for the Integrated Terminal?
Yes. You can use the "terminal.integrated.automationProfile.*" setting to set the shell that will be used for all automation in VS Code, which includes Tasks.
{ "version": "2.0.0", "windows": { "options": { "shell": { "executable": "cmd.exe", "args": [ "/d", "/c" ] } } }, ...Can a background task be used as a prelaunchTask in launch.json?
Yes. Since a background task will run until killed, a background task on its own has no signal that it has "completed". To use a background task as a prelaunchTask, you must add an appropriate background problemMatcher to the background task so that there is a way for the task system and debug system to know that the task "finished".
Your task could be:
{ "name": "Launch Extension", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", "args": ["--extensionDevelopmentPath=${workspaceRoot}"], "stopOnEntry": false, "sourceMaps": true, "outFiles": ["${workspaceRoot}/out/src/**/*.js"], "preLaunchTask": "npm: watch" }For more on background tasks, go to Background / watching tasks.
Why do I get "command not found" when running a task?
The message "command not found" happens when the task command you're trying to run is not recognized by your terminal as something runnable. Most often, this occurs because the command is configured as part of your shell's startup scripts. Tasks are run as non-login and non-interactive, which means that the startup scripts for your shell won't be run. nvm in particular is known to use startup scripts as part of its configuration.
There are several ways to resolve this issue:
- Make sure your command is on your path and doesn't require startup scripts to get added to your path. This is the most thorough way to solve the problem, and is the recommended solution.
- You can make a one-off fix for your task to run as login or interactive. This is not recommended, as it can have other consequences. However, it can also be a quick and easy fix for a single task. Below is an example of a task that does this with bash as the shell: