Use GitHub Copilot (or any LM-enabled workflow in VS Code) to start, inspect, and resume debug sessions automatically with conditional breakpoints, hit conditions, and logpoints.
The extension contributes Language Model Tools that Copilot can invoke:
- Start Debugger (
startDebugSessionWithBreakpoints) – Launch a configured debug session and wait for the first breakpoint. You must supply at least one breakpoint, butvariableFilteris optional: include it to narrow captured variables, omit it for a pure pause (break/stopDebugging) or automatic capture-all (captureaction). - Resume Debug Session (
resumeDebugSession) – Continue execution of an existing paused session and optionally wait for the next stop (new breakpoints added during resume may omitvariableFilterif you only need a pause, but include it for scoped variable output or interpolation). - Get Variables (
getVariables) – Retrieve all variables in the current top stack frame scopes. - Expand Variable (
expandVariable) – Drill into a single variable to inspect its immediate children. - Evaluate Expression (
evaluateExpression) – Run an arbitrary expression (like the Debug Console) in the paused stack frame. - Stop Debug Session (
stopDebugSession) – Terminate matching debug sessions when you’re done.
All tools return structured data that Copilot can reason over (JSON-like text parts containing call stacks, variables, and metadata).
-
Install the extension from the VS Code Marketplace:
ext install dkattan.copilot-breakpoint-debugger(orcode --install-extension dkattan.copilot-breakpoint-debugger).Prefer to hack on it locally? Clone the repo and run:
git clone https://github.com/dkattan/vscode-copilot-debugger.git
cd vscode-copilot-debugger
npm install
npm run compile-
Open the folder in VS Code (Insiders recommended for dev).
-
Set a default launch configuration name or inline JSON in your settings:
- Start interacting with Copilot Chat. It can reference the tools by name.
Command : copilotBreakpointDebugger.startAndWaitManual
Command : copilotBreakpointDebugger.insertSampleStartDebuggerPayload
Description : Name of the default launch configuration to use when starting the debugger
Type : string
Default : ""
Description : Timeout in seconds waiting for initial entry stop after launching (before first user breakpoint). Supports long startup/build times; must be > 0.
Type : integer
Default : 60
Description : Maximum number of variables auto-captured when a breakpoint onHit=captureAndContinue omits variableFilter (capture-all mode).
Type : integer
Default : 40
Description : Enable serverReady automation (trigger + action). When disabled, provided serverReady payloads are ignored.
Type : boolean
Default : true
Description : Preferred serverReady action type surfaced in samples and quick insert command.
Type : string
Default : "httpRequest"
Description : Maximum number of build diagnostics (from problem matchers) to include in error messages when debug session fails to start.
Type : integer
Default : 5
Description : Maximum number of output lines (stderr/stdout) to buffer per debug session for runtime error reporting.
Type : integer
Default : 50
Description : Maximum number of characters returned by Copilot debugger tools (tool output is truncated with a suffix when exceeded).
Type : integer
Default : 8192
Description : Controls how verbosely logs are mirrored to the developer console (Output panel always receives every log; this only gates console.* mirroring). Changes take effect immediately without reloading.
Type : string
Default : "info"
Description : Emit verbose Debug Adapter Protocol trace logs to the output channel for troubleshooting.
Type : boolean
Default : false
The repo already ships with a workspace-level Run on Save configuration (see .vscode/settings.json) that fires npm run update any time package.json is saved. Just install the Run on Save extension when VS Code recommends it and the tables will stay in sync automatically.
Important (updated):
startDebugSessionWithBreakpointsrequires at least one breakpoint.variableFilteris only required when you want a subset of variables for acaptureaction. If you setaction: "capture"and omitvariableFilter, the tool auto-captures the firstcaptureMaxVariableslocals (case‑sensitive exact names) to reduce friction. ForbreakorstopDebuggingactions, omitvariableFilterfor a pure pause without variable output.
When the debugger fails to pause before copilot-debugger.entryTimeoutSeconds elapses, the tool:
- Lists every new debug session (name, id, request) and whether it was forcibly stopped after diagnostics were captured
- Produces a state-machine style analysis covering
serverReadyAction(pattern hits, capture groups, resolveduriFormat, and the action that should launch your browser), CopilotserverReadytriggers, and the missing entry stop - Appends truncated Debug Console + terminal output so you can confirm whether the expected "listening on..." line actually appeared
- Reminds you to verify those readiness signals before simply bumping the timeout
In other words, the timeout is actionable guidance rather than a suggestion to "try again with a larger number".
Example settings snippet:
You may supply a serverReady object when starting the debugger to run an automated action (shell command, HTTP request, or VS Code command) once the target is "ready".
Important (don’t use
curlwith breakpoints): If any breakpointonHitisbreak, do not useserverReady.action.type = "shellCommand"withcurl/wget/etc to call an endpoint that can hit that breakpoint. The request will often hang because the debuggee is paused and cannot finish responding.Prefer
serverReady.action.type = "httpRequest"when you intend to trigger a breakpoint and capture state.
Structure (legacy union still accepted; new flat shape preferred):
// New flat schema (with discriminator) recommended – mirrors package.json tool schema
interface ServerReadyFlat {
trigger?: { path?: string; line?: number; pattern?: string };
action:
| { type: 'shellCommand'; shellCommand: string }
| {
type: 'httpRequest';
url: string;
method?: string;
headers?: Record<string, string>;
body?: string;
}
| { type: 'vscodeCommand'; command: string; args?: unknown[] };
}
// Legacy (still supported for backward compatibility)
interface ServerReadyLegacy {
trigger?: { path?: string; line?: number; pattern?: string };
action:
| { shellCommand: string }
| {
httpRequest: {
url: string;
method?: string;
headers?: Record<string, string>;
body?: string;
};
}
| { vscodeCommand: { command: string; args?: unknown[] } };
}
Modes (decided by `trigger`):
1. Breakpoint trigger – provide `trigger.path` + `trigger.line`. When that source breakpoint is hit (whether first or after entry) the action executes, then the session automatically continues to the first user breakpoint.
2. Pattern trigger – provide `trigger.pattern` (regex). A VS Code `serverReadyAction` URI is injected; built-in output detectors (debug console / task terminal) fire it when the pattern appears. Action executes asynchronously (fire‑and‑forget) without blocking normal breakpoint flow.
3. Immediate attach – omit `trigger` entirely for `request: "attach"` configurations (e.g., Azure Functions). The action runs immediately after attach since output may have scrolled before the adapter connected.
Actions:
- `shellCommand` – Runs in a new terminal named `serverReady-<phase>` (phase is `entry`, `late`, or `immediate`).
- `httpRequest` – Issues a fetch; dispatched fire‑and‑forget so a slow service cannot block debugger continuation.
- `vscodeCommand` – Invokes a VS Code command (e.g. telemetry-free internal toggles or extension commands) with optional `args`.
**Blocking behavior:**
- `shellCommand` runs in a terminal and can block forever if it launches an HTTP client (like `curl`) while your debug target is paused at a `break` breakpoint.
- `httpRequest` is executed by the tool (with timeout handling) and is the recommended way to trigger a request that is expected to hit a breakpoint.
**Recommended patterns:**
- **To hit a breakpoint (and capture state):** use `serverReady.action.type = "httpRequest"`.
- **To run a smoke command after you’ve resumed:** use `shellCommand` **after** you resume (e.g., via a separate task/command), not as `serverReady`.
**Common failure mode:** configuring `serverReady` with `curl http://localhost/...` while also setting a `break` breakpoint on the request handler causes the `curl` command to hang and the debug session to appear stuck.
Examples:
```jsonc
// Pattern-based readiness executing an HTTP health probe
{
"serverReady": {
"trigger": { "pattern": "listening on .*:3000" },
"action": { "type": "httpRequest", "url": "http://localhost:3000/health" }
},
}
// Breakpoint-triggered shell command (use for non-HTTP actions)
{
"serverReady": {
"trigger": { "path": "src/server.ts", "line": 27 },
"action": {
"type": "shellCommand",
"shellCommand": "echo serverReady action executed"
}
}
}// Immediate attach (Azure Functions) – no trigger; action fires right after attach
{
"serverReady": {
"action": {
"type": "httpRequest",
"url": "http://localhost:7071/api/status"
}
}
}// VS Code command action (e.g., close panel after readiness)
{
"serverReady": {
"trigger": { "path": "src/server.ts", "line": 10 },
"action": {
"type": "vscodeCommand",
"command": "workbench.action.closePanel"
}
}
}Azure Functions Attach Sequence (Immediate Action):
sequenceDiagram
participant VSCode
participant DebugAdapter
participant Extension
participant FunctionsHost as Functions Host
VSCode->>DebugAdapter: start attach (stopOnEntry=true)
DebugAdapter-->>VSCode: (no initial stop; adapter ignores stopOnEntry)
Extension->>Extension: trigger omitted ⇒ immediate serverReady action
Extension->>FunctionsHost: HTTP GET /api/status
FunctionsHost-->>Extension: 200 OK
Extension->>DebugAdapter: wait for first user breakpoint
DebugAdapter-->>VSCode: stopped (breakpoint)
Extension->>VSCode: tool result (variables, call stack)
Pattern detection uses an internal vscode://dkattan.copilot-breakpoint-debugger/serverReady?token=... URI. No custom regex scanning or fallback logic is implemented (honors the NO FALLBACK principle). If omitted, the debugger behaves normally (entry stop then user breakpoints).
{
"copilot-debugger.defaultLaunchConfiguration": "Run test.js",
"copilot-debugger.entryTimeoutSeconds": 120
}The Start Debugger tool uses a single configurationName field. Resolution order:
- Provided
configurationName - Workspace setting
copilot-debugger.defaultLaunchConfiguration - Auto-select when the target workspace folder defines exactly one launch configuration
If none of the above apply (and multiple configs exist), an error is returned so Copilot can request clarification instead of silently guessing.
Minimal example (auto-selection when sole config exists):
Start the debugger in workspace folder /absolute/path/to/project with a breakpoint at src/index.ts line 15 filtering variables foo,bar.
Explicit configuration example:
Start debug with configurationName "Run test.js" adding a breakpoint at test-workspace/b/test.js line 9 filtering variable i.
Capture example:
Start debug with configurationName "Run test.js" and capture action at test-workspace/b/test.js line 9 filtering i and log message "i={i}".
Auto-capture example (omit variableFilter – first N locals collected):
```text
Start debug with configurationName "Run test.js" and capture action at test-workspace/b/test.js line 9 with log message "i={i}" (omit variableFilter for automatic capture).
{
"workspaceFolder": "/abs/path/project",
"configurationName": "Run test.js",
"breakpointConfig": {
"breakpoints": [
{
"path": "src/server.ts",
"line": 27,
"action": "capture",
"logMessage": "port={PORT}",
"variableFilter": ["PORT"]
}
]
},
"serverReady": {
"trigger": { "pattern": "listening on .*:(\\d+)" },
"action": {
"type": "httpRequest",
"url": "http://localhost:%PORT%/swagger"
}
}
}The %PORT% token is substituted from the captured log message interpolation / variable value (pattern group extraction occurs in the debug adapter output before the action executes). If the token cannot be resolved the raw string is used. This encourages discovery of port token replacement without extra explanation.
> Tip: Variable filters are exact name matches (case-sensitive). Provide them only when you want a narrowed subset; omit for capture-all (bounded by `captureMaxVariables`) or for simple pause actions.
## 🧪 Example Copilot Prompts
```text
Start the debugger with a breakpoint at src/app.ts line 42 filtering variables user,session.
Resume the last debug session, add a breakpoint at src/server.ts line 42 filtering variables orderId,orderTotal then wait for it to hit.
Evaluate the expression user.profile[0].email in the currently paused session.
Stop every debug session named "Web API".
Tool responses are rendered with @vscode/prompt-tsx, the same priority-aware prompt builder used in Microsoft’s chat samples. Each tool result includes structured parts so Copilot (or any prompt-tsx–aware planner) can automatically drop low-priority sections when the context window is tight.
- High priority → breakpoint summary (session/file/line)
- Medium priority → thread + frame metadata
- Low priority → filtered scope snapshots (pruned first)
Because variable filters are mandatory and the prompt is minified before returning, typical tool output is only a few thousand characters instead of tens of thousands.
Responses include:
- Breakpoint hit metadata (file, line, reason)
- Call stack (threads, frames, source info)
- Scoped variables (filtered if requested)
- Variable expansion (children)
This extension does not collect or transmit telemetry. All processing occurs locally via the VS Code Debug Adapter Protocol (DAP). If telemetry is added in the future, this section will document exactly what is sent and how to opt out.
All commands are exposed as LM tools and can be invoked via keyboard using Copilot Chat. Output is provided as text parts suitable for screen readers. Please open issues for any accessibility improvements.
Contributions are welcome!
- Fork the repo
- Create a feature branch
- Run
npm run lint && npm test - Submit a PR
npm run watch– Incremental TypeScript compilationnpm test– Compiles then runs test suitenpm run lint– ESLint static analysis (run with--fixfor autofix)
npm test compiles the extension and runs the full VS Code integration suite via @vscode/test-cli. Set CI=true to skip PowerShell-specific cases. Tests live under src/test/ (extension smoke tests, DAP helper flows, and multi-root coverage). You can also run them from VS Code’s Test Explorer using the supplied launch configs—just avoid executing compiled files manually, as the harness wires up the VS Code host and Mocha globals for you. Pre-commit hooks mirror these checks so local commits match CI expectations.
- Prerequisites
- Node.js 18+ (uses VS Code’s Node runtime features)
- npm 10+
- Any edition of VS Code (Stable or Insiders)
- Install:
npm install - Iterate
- Build once:
npm run compile - Watch for changes:
npm run watch - Lint:
npm run lint
- Build once:
- (Optional) Lint autofix:
npm run lint -- --fix
- Test:
npm test- Uses
@vscode/test-clito launch a temporary VS Code build inside.vscode-test/ - If a previous run hangs, delete
.vscode-test/and rerun
- Uses
You can also run tests/debug from VS Code’s Run and Debug view using the provided launch configurations. No dual-install workflow is required anymore; use whichever VS Code channel you prefer.
Standard release checklist (copy/paste):
1. Update code / docs
2. npm run lint
3. npm test (all pass)
4. Update CHANGELOG.md (new section [x.y.z] - YYYY-MM-DD)
5. Bump version in package.json
6. git add . && git commit -m "chore(release): x.y.z <summary>"
7. git tag -a vx.y.z -m "Release vx.y.z: <summary>"
8. git push origin main --follow-tags
9. (CI) Publishes: verifies, packages, publishes marketplace
10. (Optional) gh release create vx.y.z --title "vx.y.z" --notes-file RELEASE_NOTES_x.y.z.md
CI/CD – .github/workflows/ci.yml runs lint/format/test on push & PR, then packages + publishes on GitHub releases.
Secrets – add VSCE_PAT (Marketplace PAT with Manage scope); GITHUB_TOKEN is provided automatically.
Manual publish – npm run lint && npm test, bump version (npm version patch|minor|major), then npx @vscode/vsce package and npx @vscode/vsce publish -p <VSCE_PAT>.
Release flow – push tag & create a GitHub release to trigger publish workflow.
You can automate steps 4–9 with the PowerShell helper New-Release.ps1 (auto-increments version):
./New-Release.ps1 -ReleaseNotes "**Added:** Feature X`n**Fixed:** Issue Y"Parameters:
-ReleaseNotes(string) – Markdown body inserted into CHANGELOG and used for GitHub release notes.-Date(optional) – Override date; defaults to current UTC.-SkipGitPush– Create commit + tag locally but do not push.-DryRun– Show planned changes without applying.
Auto versioning: Script scans existing tags (git tag --list 'v*'), parses semantic versions, selects the highest, then increments the patch (Build) component. If no tags exist it starts at 0.0.1.
Behavior:
- Prepends a new
## [Version] - Datesection after## [Unreleased]inCHANGELOG.md. - Commits the CHANGELOG update.
- Creates annotated tag
vX.Y.Z(.R). - Pushes commit and tag (unless
-SkipGitPush). - Creates GitHub release with the provided notes if
ghCLI is available & authenticated.
For multi-line notes, use a here-string:
$notes = @'
**Added:** Dashboard refresh
**Changed:** Improved variable filtering docs
**Fixed:** Race condition in session start
'@
./New-Release.ps1 -ReleaseNotes $notesSee CHANGELOG.md.
MIT © Contributors