VS Code's Node debugger launches your program with its own node, which skips Nub entirely. Point it at the nub binary instead: Nub launches the file, the inspector starts, and the debugger attaches.

.vscode/launch.json
{
  "type": "node",
  "request": "launch",
  "name": "Debug (nub)",
  "runtimeExecutable": "nub",
  "program": "${workspaceFolder}/src/index.ts",
  "skipFiles": ["<node_internals>/**"]
}

Set a breakpoint and run the configuration; execution stops at it. VS Code injects --inspect-brk ahead of your file, and Nub forwards it to the Node child it spawns, so the inspector binds to the port VS Code chose and attaches.

If nub isn't found

VS Code resolves runtimeExecutable against its own PATH. If it can't find nub, set the field to the absolute path that which nub prints — for example ~/.nub/bin/nub.

Program arguments

Pass your program's argv through args. Each entry appears on process.argv, as it would after the file on the command line.

.vscode/launch.json
{
  "runtimeExecutable": "nub",
  "program": "${workspaceFolder}/src/build.ts",
  "args": ["--out", "./dist", "--verbose"]
}

Read the full docs for launch configurations on code.visualstudio.com.

TypeScript

Breakpoints in .ts source resolve to the source line. Nub transpiles TypeScript with an inline source map and injects --enable-source-maps, so the debugger maps the running code back to your original file.

Running without augmentation

To debug on plain Node — every augmentation off, the project's pinned version still used — add --node through runtimeArgs.

.vscode/launch.json
{
  "runtimeExecutable": "nub",
  "runtimeArgs": ["--node"],
  "program": "${workspaceFolder}/src/index.ts"
}