Visual Studio Code Integration Guide
Technology: vscode · Category: tooling · Last reviewed: 2026-08-23
Source: https://tech-stack.codeamanilabs.org/guide/vscode
Insight:
VS Code's real superpower on Windows is not the editor — it's the client/server split. Install VS Code on Windows, type
code .inside Ubuntu, and the UI stays on Windows while the language servers, terminal, debugger, and file watchers all run as Linux processes on the Linux filesystem. That is the only setup where a Next.js/TypeScript stack behaves identically on a dev laptop and on Vercel's Linux builders. The trade-off: extensions now live in two places and you have to know which side each one runs on.
██╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ███████╗
██║ ██║██╔════╝ ██╔════╝██╔═══██╗██╔══██╗██╔════╝
██║ ██║███████╗ ██║ ██║ ██║██║ ██║█████╗
╚██╗ ██╔╝╚════██║ ██║ ██║ ██║██║ ██║██╔══╝
╚████╔╝ ███████║ ╚██████╗╚██████╔╝██████╔╝███████╗
╚═══╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
Visual Studio Code Integration Guide
Focus: A developer's guide to VS Code driving WSL Ubuntu from Windows — the WSL extension,
code ., extension placement, workspace/remote settings, the integrated terminal, tasks, debugging, dev containers, and running Claude Code inside it.
Overview
VS Code is a free, cross-platform editor built on Electron with a remote-capable architecture: the workbench (UI, themes, keybindings) runs on your machine, while a VS Code Server process can run somewhere else — inside a WSL distro, over SSH, or in a container. The WSL extension (ms-vscode-remote.remote-wsl) installs that server into your Linux distro and runs "commands and other extensions directly in WSL so you can edit files located in WSL or the mounted Windows filesystem (for example /mnt/c) without worrying about pathing issues, binary compatibility, or other cross-OS challenges."
For codeAmani that matters because the whole stack — Next.js 15, Node, pnpm/npm, Prisma engines, sharp, Playwright browsers, Docker — is built and tested on Linux. Developing on Windows-native Node and deploying to Vercel's Linux builders is the classic source of "works on my machine": case-sensitive imports, node_modules binaries compiled for the wrong platform, CRLF diffs, and path separators. Remote-WSL removes the class entirely.
flowchart LR
subgraph WIN["Windows 11 — client side"]
A["VS Code workbench<br/>UI · themes · keybindings"]
B["code CLI on PATH"]
end
subgraph WSL["WSL 2 · Ubuntu — server side"]
C["VS Code Server<br/>~/.vscode-server"]
D["Extension host<br/>ESLint · TS server · Tailwind"]
E["Integrated terminal<br/>bash · node · git · claude"]
F["Debugger + file watchers<br/>on /home/<user>/projects"]
end
B -->|"code . · code --remote wsl+Ubuntu"| A
A <-->|"RPC over the WSL boundary"| C
C --> D
C --> E
C --> F
Not to be confused with Visual Studio
| VS Code (this guide) | Visual Studio (see visual-studio/) |
|
|---|---|---|
| What it is | Cross-platform editor, ~200 MB | Full Windows IDE, multi-GB |
| Platforms | Windows, macOS, Linux, web | Windows (and a separate macOS product, retired) |
| Primary stack | JS/TS, Python, Go, Rust, anything | .NET, C++, MSBuild |
| Extension model | Node/TypeScript extensions from the VS Code Marketplace | VSIX / NuGet, Microsoft.VisualStudio.Extensibility or VSSDK + MEF |
| Remote dev | First-class (WSL, SSH, containers, tunnels) | Not the same model |
| Config | JSON (settings.json, launch.json, tasks.json) |
.sln / .csproj / MSBuild |
They share a name and nothing else. This guide is VS Code.
Local Windows vs Remote-WSL
| Concern | VS Code on Windows only | VS Code + WSL extension |
|---|---|---|
| Node / package manager | Windows build | Linux build — same as CI and Vercel |
| Path & case semantics | Case-insensitive, \ separators |
Case-sensitive, / — matches production |
| File watching | Native | Native on ext4; polling needed on WSL 1 |
node_modules native binaries |
Windows-compiled | Linux-compiled |
| Terminal | PowerShell / Git Bash | Real bash in the distro |
| Docker / dev containers | Docker Desktop | Docker Desktop WSL 2 backend, or Docker in the distro |
Official Documentation
Setup — Windows + WSL Ubuntu in five minutes
Order matters: WSL first, VS Code on the Windows side, extension last.
# 1. Windows side — install WSL 2 + Ubuntu (PowerShell as Administrator, once)
wsl --install -d Ubuntu
# 2. Install VS Code on WINDOWS, not inside the distro.
# https://code.visualstudio.com/download
# On the "Select Additional Tasks" screen, CHECK "Add to PATH".
# 3. Install the WSL extension (any shell where `code` is on PATH)
code --install-extension ms-vscode-remote.remote-wsl
# ...or the whole Remote Development pack (WSL + SSH + Dev Containers)
code --install-extension ms-vscode-remote.vscode-remote-extensionpack
# 4. Verify
code --version
code --list-extensions --show-versions
Do not
apt install codeinside Ubuntu. The WSL extension pushes its own VS Code Server into~/.vscode-server; a second Linux-native VS Code is redundant and confusescodeon the distro PATH.
The daily loop
From an Ubuntu shell, in the project folder:
cd ~/projects/boda-dispatch
code .
First run downloads the server components into WSL (once, ~30s). A WSL: Ubuntu indicator appears in the bottom-left status bar — that is the single reliable signal that you are editing on the Linux side.
Other entry points:
# From Windows PowerShell / CMD — open a WSL path directly
code --remote wsl+Ubuntu /home/<user>/projects/boda-dispatch
# Force folder interpretation for a path containing a dot
code --folder-uri vscode-remote://wsl+Ubuntu/home/<user>/app.v2
# Already inside a WSL window? The same code CLI works there too
code --diff old.ts new.ts
code --goto lib/domain/dispatch.ts:42
From the Command Palette (F1) on the Windows side:
| Command | Does |
|---|---|
| WSL: Connect to WSL | New window on the default distro |
| WSL: Connect to WSL using Distro | Pick a specific distro |
| WSL: Reopen Folder in WSL | Move the current folder to the Linux side |
| WSL: Reopen in Windows | Move it back |
Put the code on the Linux filesystem
Microsoft is unambiguous: "We recommend against working across operating systems with your files… For the fastest performance speed, store your files in the WSL file system if you are working in a Linux command line."
✅ /home/<user>/projects/boda-dispatch ← ext4, fast, correct case semantics
❌ /mnt/c/Users/<user>/projects/boda-... ← 9p bridge; npm install and HMR crawl
Editing /mnt/c works and VS Code handles the paths, but npm install, Turbopack HMR, and file watching over the Windows mount are dramatically slower. To browse the Linux files from Windows Explorer, run explorer.exe . from the WSL shell, or type \\wsl$ in the Explorer address bar.
Extensions: two installs, two homes
Once connected, the Extensions view splits into Local - Installed (UI-side: themes, icons, keymaps) and WSL: Ubuntu - Installed (everything that touches code or the filesystem: language servers, linters, formatters, debuggers, test runners). Installing from the Extensions view while connected puts the extension in the right place automatically. Extensions that should be remote but are only installed locally appear dimmed with an Install in WSL: Ubuntu button, and the cloud icon in the Local - Installed title bar offers Install Local Extensions in WSL: {Name} for a bulk move.
Commit the stack's recommendations so a new machine is one click from correct — .vscode/extensions.json:
{
"recommendations": [
"ms-vscode-remote.remote-wsl",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"Prisma.prisma",
"vitest.explorer",
"ms-playwright.playwright",
"eamodio.gitlens",
"ms-vscode-remote.remote-containers",
"anthropic.claude-code"
]
}
VS Code surfaces these as Workspace Recommendations; Extensions: Configure Recommended Extensions (Workspace Folder) generates the file. Scripted setup inside the distro:
code --install-extension dbaeumer.vscode-eslint \
--install-extension esbenp.prettier-vscode \
--install-extension bradlc.vscode-tailwindcss --force
Rarely, an extension guesses wrong about where to run. Override it explicitly:
// settings.json — "ui" = local/client, "workspace" = remote (WSL)
"remote.extensionKind": {
"ms-azuretools.vscode-containers": ["ui"]
}
Use this sparingly — the docs warn it can break extensions.
Settings: four scopes, one precedence order
Precedence, lowest → highest: default → user → remote → workspace → workspace folder → language-specific → policy. The remote layer is the one people forget.
| Scope | Where | Use it for |
|---|---|---|
| User | %APPDATA%\Code\User\settings.json (Windows client) |
Theme, font, keybindings — travels everywhere |
| Remote | Preferences: Open Remote Settings → the Remote tab | Anything true only inside WSL (interpreter paths, watcher tuning) |
| Workspace | .vscode/settings.json — commit this |
Team formatting, TS SDK, per-repo behavior |
| Workspace folder | Per-folder in a multi-root .code-workspace |
Monorepo package overrides |
A sane committed .vscode/settings.json for the Next.js/TypeScript stack:
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" },
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.preferences.importModuleSpecifier": "non-relative",
"files.eol": "\n",
"files.exclude": { "**/.next": true },
"files.watcherExclude": { "**/node_modules/**": true, "**/.next/**": true },
"search.exclude": { "**/node_modules": true, "**/.next": true, "**/dist": true },
"[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
"[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
"tailwindCSS.experimental.classRegex": [["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]]
}
"files.eol": "\n" is not cosmetic here — the same repo touched from Windows and WSL is the documented cause of "every file is modified" Git noise.
Integrated terminal
Once a folder is open in WSL, any terminal you open (Terminal → New Terminal, Ctrl+`) is already a bash shell in the distro, cwd at the workspace root. No profile configuration needed — that is the whole point.
For local Windows windows, VS Code auto-detects WSL distros as terminal profiles (terminal.integrated.useWslProfiles, on by default). To pin one:
{
"terminal.integrated.defaultProfile.windows": "Ubuntu (WSL)",
"terminal.integrated.profiles.windows": {
"Ubuntu (WSL)": { "path": "C:\\WINDOWS\\System32\\wsl.exe", "args": ["-d", "Ubuntu"] }
}
}
Gotcha: when the VS Code Server starts in WSL, no shell startup scripts are run —
.bashrc/.profileare skipped for the server process. Terminals you open still source them, but tasks and debug sessions inherit the server's environment. If a tool needs env setup before the server boots, put it in~/.vscode-server/server-env-setup, which is processed before the server starts.
terminal.integrated.automationProfile.<platform> gives tasks and the debugger a lighter shell when your interactive profile has heavy startup (oh-my-zsh, nvm, direnv).
Tasks — tasks.json
Tasks run in WSL when the window is remote, so npm run dev is Linux npm. npm scripts are auto-detected (Tasks: Run Task); write explicit tasks when you need a preLaunchTask, a problem matcher, or ordering.
{
"version": "2.0.0",
"tasks": [
{
"label": "dev",
"type": "shell",
"command": "npm run dev",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": { "regexp": "^$" },
"background": {
"activeOnStart": true,
"beginsPattern": "starting the development server",
"endsPattern": "Ready in"
}
},
"presentation": { "reveal": "always", "panel": "dedicated" }
},
{
"label": "typecheck",
"type": "shell",
"command": "npx tsc --noEmit",
"group": { "kind": "build", "isDefault": true },
"problemMatcher": ["$tsc"]
},
{
"label": "verify",
"dependsOn": ["typecheck", "lint", "test"],
"dependsOrder": "sequence",
"group": "test"
}
]
}
isBackground: true requires a background matcher with beginsPattern/endsPattern — without it a watch task used as preLaunchTask hangs the debug launch forever. Set options.cwd for monorepo packages.
Debugging — launch.json
Every launch config needs type, request (launch | attach), and name. In a WSL window the app starts in WSL and the debugger attaches there; nothing extra to configure. Next.js's own documented configuration:
{
"version": "0.2.0",
"configurations": [
{
"name": "Next.js: debug server-side",
"type": "node-terminal",
"request": "launch",
"command": "npm run dev -- --inspect"
},
{
"name": "Next.js: debug client-side",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000"
},
{
"name": "Next.js: debug full stack",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/next/dist/bin/next",
"runtimeArgs": ["--inspect"],
"skipFiles": ["<node_internals>/**"],
"serverReadyAction": {
"action": "debugWithChrome",
"killOnServerStop": true,
"pattern": "- Local:.+(https?://.+)",
"uriFormat": "%s",
"webRoot": "${workspaceFolder}"
}
}
]
}
Attributes worth knowing: env / envFile (point at .env.local), cwd (monorepos), console: "integratedTerminal", preLaunchTask / postDebugTask, compounds to start server + client together, and ${workspaceFolder} / ${env:NAME} substitution. serverReadyAction.pattern scans stdout and opens a browser debug session on the captured URL — that is what makes full-stack breakpoints work in one F5.
WSL port forwarding is automatic. A dev server bound in the distro is reachable at http://localhost:3000 from the Windows browser; VS Code's Ports view lists forwarded ports for remote windows.
Dev containers from WSL
With Docker Desktop's WSL 2 backend (Settings → Resources → WSL Integration, enable your distro), open the folder in WSL first, then run Dev Containers: Reopen in Container. If there is no .devcontainer/devcontainer.json, VS Code offers Dev Containers: Add Dev Container Configuration Files.
{
"name": "codeAmani Next.js",
"image": "mcr.microsoft.com/devcontainers/typescript-node:22",
"features": { "ghcr.io/devcontainers/features/github-cli:1": {} },
"forwardPorts": [3000],
"postCreateCommand": "npm ci",
"remoteUser": "node",
"customizations": {
"vscode": {
"extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "bradlc.vscode-tailwindcss"],
"settings": { "editor.formatOnSave": true }
}
}
}
Rule of thumb: WSL for daily work (fast, zero ceremony, one shared Node/pnpm store), dev containers when the environment itself is the deliverable — a pinned Postgres + app pair, or onboarding where "install these six things" is the bottleneck. customizations.vscode.extensions is the container's answer to .vscode/extensions.json.
Claude Code inside VS Code
Two distinct things, and mixing them up is the usual confusion:
| Claude Code VS Code extension | Claude Code CLI | |
|---|---|---|
| Install | anthropic.claude-code from the Marketplace |
Standalone install, inside the distro |
| Surface | Native chat panel with inline diffs, plan review, @-mentions |
claude in the integrated terminal |
| Requires | VS Code 1.94.0+ | Nothing but a shell |
| PATH | Bundles a private CLI copy — does not put claude on PATH |
Is the thing on your PATH |
# In the VS Code integrated terminal (already a WSL bash shell)
claude
- Toggle focus between editor and Claude with Ctrl+Esc (Cmd+Esc on macOS).
- The CLI auto-detects the IDE when launched from VS Code's terminal (diff viewing, diagnostics sharing). From an external terminal, run
/ideinside Claude Code to connect it to VS Code. - Prefer the terminal UI in the panel? Enable the extension's Use Terminal setting (
claudeCode.useTerminal). - WSL note: the integrated terminal in a remote window is a Linux shell, so the CLI must be installed inside the distro. A Windows-side
claude.exeis not on that PATH. - Launching VS Code as
code .from a shell means it inherits that shell's environment — the documented fix when the extension can't seeANTHROPIC_API_KEY.
codeAmani notes
- Standard Windows setup: VS Code on Windows + WSL 2 Ubuntu + the WSL extension, project files under
/home/<user>/projects/. EverycodeAmani-labs-projects/*app is a Linux-built Next.js app deployed to Vercel/Netlify Linux runners; developing on Windows-native Node re-introduces case-sensitivity and native-binary bugs that CI will find later and more expensively. - Secrets stay out of committed JSON.
.vscode/settings.json,launch.json, andtasks.jsonare committed — never put keys inenvblocks. Use"envFile": "${workspaceFolder}/.env.local"(gitignored) or inject through Hazina..env.localliving on the Linux side also keeps it out of Windows-indexed folders and OneDrive sync. .vscode/is a team artifact. Commitsettings.json,extensions.json,launch.json,tasks.json; gitignore.vscode/*.logand any machine-local scratch. A new laptop should reach a working state withwsl --install,code ., and "Install All" on the recommendations prompt.- Line endings.
files.eol: "\n"plusgit config --global core.autocrlf inputinside the distro. Editing one repo from both Windows and WSL without this produces whole-file diffs — the single most common WSL Git complaint. - Git credentials. Configure WSL to use the Windows Git Credential Manager rather than duplicating tokens in the distro. Note the documented limitation: cloning over SSH with a passphrase-protected key can hang VS Code's pull/sync — use HTTPS, or a passphrase-less key, or push from the CLI.
- Low-bandwidth (Kenya-targeted work): the remote server download is a one-time ~tens-of-MB hit per distro, and extensions are then installed into WSL — so a shared
.vscode/extensions.jsonplus a scriptedcode --install-extensionrun beats each engineer discovering extensions ad hoc over a metered connection. Settings Sync carries settings/keybindings/extension lists to a new machine without re-downloading a profile by hand. - AI routing is unchanged by the editor. VS Code is where Claude Code runs; the provider policy in
CLAUDE.md(Anthropic primary, OpenAI for structured output, HuggingFace/Together for open models) still governs application code.
Troubleshooting
| Issue | Fix |
|---|---|
code . not found in Ubuntu |
VS Code wasn't installed with Add to PATH on Windows, or the terminal predates the install — restart the shell, or reinstall checking the box |
| Editing feels slow, HMR lags | Project is on /mnt/c. Move it to /home/<user>/… — Microsoft recommends against working across filesystems |
| Extension missing / greyed out | It's installed Local but needs to run remote — click Install in WSL: Ubuntu in the Extensions view |
| Extension still runs on the wrong side | Force it with "remote.extensionKind": { "<publisher.ext>": ["ui" | "workspace"] } — sparingly, it can break extensions |
EACCES: permission denied renaming a folder |
Known WSL 1 issue — set remote.WSL.fileWatcher.polling: true (and raise remote.WSL.fileWatcher.pollingInterval on large repos), or move to WSL 2 |
| Whole repo shows as modified in Git | CRLF/LF mismatch — set files.eol: "\n" and core.autocrlf input |
| Env var visible in bash but not to a task/debug session | The server skips shell startup scripts — put it in ~/.vscode-server/server-env-setup |
| Debug session never starts after a watch task | isBackground task missing a background problem matcher (beginsPattern/endsPattern) |
| Extensions fail on Alpine distros | glibc dependencies in native extension code — use Ubuntu/Debian for the dev distro |
| Git pull/sync hangs on a remote window | Passphrase-protected SSH key — clone over HTTPS or push from the terminal |
VS Code can't see ANTHROPIC_API_KEY |
Launch it from the shell with code . so it inherits the environment |
Official docs:
- https://code.visualstudio.com/docs/remote/wsl
- https://code.visualstudio.com/docs/remote/wsl-tutorial
- https://code.visualstudio.com/docs/configure/settings
- https://code.visualstudio.com/docs/debugtest/debugging-configuration
- https://code.visualstudio.com/docs/debugtest/tasks
- https://code.visualstudio.com/docs/devcontainers/containers