diff --git a/.gitignore b/.gitignore
index be7c44f..9e0ec99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -420,4 +420,5 @@ FodyWeavers.xsd
node_modules/*
out/*
+dist/*
.vscode-test/*
\ No newline at end of file
diff --git a/.vscodeignore b/.vscodeignore
index 24f8a50..af03773 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -1,12 +1,18 @@
.vscode/**
.vscode-test/**
+.github/**
src/**
+out/**
+node_modules/**
+docs/**
+debugmcp-ad/**
+.pytest_cache/**
.gitignore
.yarnrc
+esbuild.js
vsc-extension-quickstart.md
**/tsconfig.json
**/eslint.config.mjs
**/*.map
**/*.ts
**/.vscode-test.*
-debugmcp-ad/**
diff --git a/README.md b/README.md
index 97fb0ad..d193ca9 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ Let AI agents debug your code inside VS Code - set breakpoints, step through exe
[](https://opensource.org/licenses/MIT)
[](https://code.visualstudio.com/)
-[](https://github.com/microsoft/DebugMCP)
+[](https://github.com/microsoft/DebugMCP)
[](https://marketplace.visualstudio.com/items?itemName=ozzafar.debugmcpextension)
> ⭐ **If you find DebugMCP useful, please [star the repo on GitHub](https://github.com/microsoft/DebugMCP)!** It helps others discover the project and motivates continued development.
@@ -17,11 +17,12 @@ Let AI agents debug your code inside VS Code - set breakpoints, step through exe
-## ✨ What's New in 2.2.0
+## ✨ What's New
-- **`/debug-live` Agent Skill** — DebugMCP now ships a companion [Agent Skill](./skills/debug-live/SKILL.md) that is auto-installed into each configured harness's personal skills directory (e.g. `~/.copilot/skills/debug-live/`). Invoke it with `/debug-live` in supporting agents to load the systematic debugging workflow and trigger DebugMCP tools with the right context.
-- **Robust debugging via the VS Code Testing API** — `start_debugging` with a `testName` now uses the VS Code Testing API to discover and launch the test, replacing the previous best-effort path. This works reliably across language test runners that integrate with the Testing API (pytest, Jest/Vitest, Java, .NET, Go, etc.) and produces consistent breakpoint hits inside individual test cases.
-- **Concurrent debugging** - supports multiple concurrent debug sessions, allowing effective parallel agentic debugging.
+### 2.2
+- **Cross-agent `debug-live` skill install** — the systematic debugging workflow ships as an [Agent Skill](https://agentskills.io) and is now installed into the **standard skills directories** — `~/.agents/skills/` (the cross-agent location honored by skills-compatible harnesses, including VS Code agent mode) and `~/.copilot/skills/` when present — so it's discoverable everywhere instead of being copied next to each agent's config where nothing scans it (fixes [#105](https://github.com/microsoft/DebugMCP/issues/105), where VS Code never loaded the skill). The server also advertises MCP `instructions` and the `start_debugging` tool points at the skill for the full workflow.
+- **Pause running programs** — new `pause_execution` tool interrupts a freely-running program and stops at its current location, even with no breakpoint set (great for busy loops and embedded/bare-metal targets), so you can then inspect state or step from there.
+- **Robust debugging via the VS Code Testing API** — `start_debugging` with a `testName` uses the VS Code Testing API to discover and launch the test, producing consistent breakpoint hits inside individual test cases across language test runners (pytest, Jest/Vitest, Java, .NET, Go, etc.).
## 🚀 Quick Install
@@ -57,6 +58,7 @@ DebugMCP is an MCP server that gives AI coding agents full control over the VS C
| **step_into** | Step into function calls | None |
| **step_out** | Step out of the current function | None |
| **continue_execution** | Continue until next breakpoint | None |
+| **pause_execution** | Interrupt a freely-running program and stop at its current location (no breakpoint needed) | None |
| **restart_debugging** | Restart the current debug session | None |
| **add_breakpoint** | Add a breakpoint at a specific line (optionally conditional) | `fileFullPath` (required)
`lineContent` (required)
`condition` (optional) |
| **remove_breakpoint** | Remove a breakpoint from a specific line | `fileFullPath` (required)
`line` (required) |
@@ -65,11 +67,13 @@ DebugMCP is an MCP server that gives AI coding agents full control over the VS C
| **get_variables_values** | Get variables and their values at current execution point | `scope` (optional: 'local', 'global', 'all') |
| **evaluate_expression** | Evaluate an expression in debug context | `expression` (required) |
-> **Note:** The MCP server intentionally exposes **tools only** — no procedural
-> instructions, no documentation resources. Workflow guidance (when to debug, how to
-> structure a root-cause investigation, language-specific quirks) lives in the companion
-> [DebugMCP Agent Skill](./skills/debug-live/SKILL.md) so it can be loaded into an
-> agent's prompt context independently of the MCP capability surface.
+> **Note:** The MCP server exposes **tools** for debugger actions, while the procedural
+> workflow guidance (when to debug, how to structure a root-cause investigation,
+> language-specific quirks) lives in the companion [Agent Skill](./skills/debug-live/SKILL.md).
+> Tool descriptions stay terse and behavioral; the extension installs the `debug-live` skill
+> into the standard skills directories (`~/.agents/skills/`, plus `~/.copilot/skills/` when
+> present) so skills-compatible harnesses load the full workflow on demand. The server also
+> advertises MCP `instructions` pointing agents at it before debugging.
### 🎯 Debugging Best Practices
diff --git a/docs/architecture/agentConfigurationManager.md b/docs/architecture/agentConfigurationManager.md
index 0494ab6..8cd43c2 100644
--- a/docs/architecture/agentConfigurationManager.md
+++ b/docs/architecture/agentConfigurationManager.md
@@ -73,11 +73,22 @@ url = "http://localhost:3001/mcp"
Uses VS Code's `globalState` to track whether the onboarding popup has been shown, preventing repeated prompts on every activation.
+### Skill delivery — standard skills directories
+
+The `debug-live` Agent Skill is installed into the **standard personal skills directories** defined by the Agent Skills open standard (agentskills.io), rather than being copied next to each agent's config file:
+- **`~/.agents/skills/debug-live/`** — the cross-agent location honored by skills-compatible harnesses, including VS Code agent mode and Copilot CLI. Always installed.
+- **`~/.copilot/skills/debug-live/`** — Copilot's own skills path; also installed when a Copilot home directory (`~/.copilot`, or `$COPILOT_HOME`) exists.
+
+`installDebugMCPSkill()` copies the one bundled source (`skills/debug-live/SKILL.md`) into each target with `force: true` (idempotent refresh) and removes stale legacy copies (`debug`, `really-debug`). It is agent-independent — a single shared install covers every skills-compatible harness.
+
+This fixes issue #105: earlier builds copied the skill next to each agent's config (e.g. `Code/User/skills/` for VS Code Copilot), a directory no harness scans, so the skill never loaded. Installing to `~/.agents/skills/` — which VS Code agent mode does scan — makes it discoverable.
+
## Key Code Locations
- Class definition: `src/utils/agentConfigurationManager.ts`
- Agent definitions: `getSupportedAgents()`
- Config writing: `addDebugMCPToAgent()`
+- Skill install: `installDebugMCPSkill()` / `getSkillInstallTargets()` / `ensureSkillRegistered()`
- Codex TOML upsert: `upsertCodexDebugMCPConfig()`
- Path detection: `getConfigBasePath()`
- Popup logic: `shouldShowPopup()`, `showAgentSelectionPopup()`
@@ -91,6 +102,8 @@ Uses VS Code's `globalState` to track whether the onboarding popup has been show
5. Show success message with option to open config file
6. Mark popup as shown
+The bundled `debug-live` skill is installed into the standard skills directories (`~/.agents/skills/`, plus `~/.copilot/skills/` when present) during step 4, so every skills-compatible harness discovers it from one shared location.
+
## Commands
- `debugmcp.showAgentSelectionPopup`: Manually trigger agent setup
diff --git a/docs/architecture/debugMCPServer.md b/docs/architecture/debugMCPServer.md
index 6a79940..643199c 100644
--- a/docs/architecture/debugMCPServer.md
+++ b/docs/architecture/debugMCPServer.md
@@ -54,17 +54,26 @@ extension. To avoid debugging the wrong workspace when several windows are open:
`DebugMCPServer` builds one handler **per MCP session** via a handler factory, which is
what lets concurrent agent sessions drive debuggers in different repos simultaneously.
-### Tools only — no resources, no instructions
+### Tools & debug-live skill
-`DebugMCPServer` exposes **tools only**. Procedural workflow guidance (when to debug,
-how to structure a root-cause investigation, language-specific quirks) lives in the
-companion Agent Skill at `skills/debug-live/SKILL.md`, not in tool descriptions or MCP
-resources. This separation matches modern agent ecosystems where MCP servers provide
-*capabilities* and skills provide *procedural knowledge* an agent loads as context.
+`DebugMCPServer` exposes **tools** for debugger capabilities. Detailed procedural guidance
+(when to debug, how to structure a root-cause investigation, language-specific quirks) lives
+in the companion Agent Skill at `skills/debug-live/SKILL.md`, which `AgentConfigurationManager`
+installs into the standard personal skills directories (`~/.agents/skills/`, and
+`~/.copilot/skills/` when present) so skills-compatible harnesses load it on demand. This
+separation matches modern agent ecosystems where MCP servers provide *capabilities* and
+skills provide *procedural knowledge* an agent loads as context.
Tool descriptions are intentionally terse and behavioral — they describe *what* the
tool does, not *when* or *how* to use it in a multi-step workflow.
+### Ensuring the workflow is loaded
+
+The server `instructions` (passed to the `McpServer` constructor and returned to the client
+at `initialize`) and the `start_debugging` tool description both point agents at the
+`debug-live` skill for the full step-through workflow, so the pointer is visible even before
+the skill activates.
+
### Streamable HTTP Transport
Uses stateless HTTP POST requests for MCP communication. The express server exposes:
@@ -96,7 +105,7 @@ error wins:
- Per-window control server: `src/controlServer.ts`
- Cross-window routing handler: `src/routingDebuggingHandler.ts`
- Shared window registry: `src/utils/workspaceRegistry.ts`
-- Agent Skill (companion, not part of the MCP surface): `skills/debug-live/SKILL.md`
+- Agent Skill (procedural workflow): `skills/debug-live/SKILL.md`
## Exposed Tools
@@ -106,6 +115,7 @@ error wins:
| `stop_debugging` | Stop current session |
| `step_over/into/out` | Stepping commands |
| `continue_execution` | Continue to next breakpoint |
+| `pause_execution` | Interrupt a running program (no breakpoint needed) |
| `restart_debugging` | Restart session |
| `add/remove_breakpoint` | Breakpoint management |
| `clear_all_breakpoints` | Remove all breakpoints |
diff --git a/docs/architecture/debuggingExecutor.md b/docs/architecture/debuggingExecutor.md
index 4ecdf15..d147656 100644
--- a/docs/architecture/debuggingExecutor.md
+++ b/docs/architecture/debuggingExecutor.md
@@ -45,6 +45,7 @@ Stepping and control operations use VS Code's command system:
- `workbench.action.debug.stepInto`
- `workbench.action.debug.stepOut`
- `workbench.action.debug.continue`
+- `workbench.action.debug.pause`
- `workbench.action.debug.restart`
### DAP Custom Requests
diff --git a/esbuild.js b/esbuild.js
new file mode 100644
index 0000000..3a0fbfc
--- /dev/null
+++ b/esbuild.js
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Corporation.
+
+const esbuild = require('esbuild');
+
+const production = process.argv.includes('--production');
+const watch = process.argv.includes('--watch');
+
+/**
+ * Bundles the extension into a single CommonJS file so the published VSIX ships
+ * one JS file instead of the entire production node_modules tree. `vscode` is
+ * kept external because it is provided by the host at runtime.
+ * @type {import('esbuild').Plugin}
+ */
+const esbuildProblemMatcherPlugin = {
+ name: 'esbuild-problem-matcher',
+ setup(build) {
+ build.onStart(() => {
+ console.log('[esbuild] build started');
+ });
+ build.onEnd((result) => {
+ result.errors.forEach(({ text, location }) => {
+ console.error(`✘ [ERROR] ${text}`);
+ if (location) {
+ console.error(` ${location.file}:${location.line}:${location.column}`);
+ }
+ });
+ console.log('[esbuild] build finished');
+ });
+ },
+};
+
+async function main() {
+ const ctx = await esbuild.context({
+ entryPoints: ['src/extension.ts'],
+ bundle: true,
+ format: 'cjs',
+ minify: production,
+ sourcemap: !production,
+ sourcesContent: false,
+ platform: 'node',
+ outfile: 'dist/extension.js',
+ external: ['vscode'],
+ logLevel: 'silent',
+ plugins: [esbuildProblemMatcherPlugin],
+ });
+ if (watch) {
+ await ctx.watch();
+ } else {
+ await ctx.rebuild();
+ await ctx.dispose();
+ }
+}
+
+main().catch((e) => {
+ console.error(e);
+ process.exit(1);
+});
diff --git a/package-lock.json b/package-lock.json
index bb2bf63..520bd59 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "debugmcpextension",
- "version": "2.1.0",
+ "version": "2.2.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "debugmcpextension",
- "version": "2.1.0",
+ "version": "2.2.1",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.26.0",
@@ -22,6 +22,7 @@
"@typescript-eslint/parser": "^8.56.0",
"@vscode/test-cli": "^0.0.12",
"@vscode/test-electron": "^2.5.2",
+ "esbuild": "^0.28.1",
"eslint": "^10.0.1",
"typescript": "^5.9.3"
},
@@ -39,6 +40,448 @@
"node": ">=18"
}
},
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
+ "integrity": "sha1-egGo0uwvuy2seK2tCbD6eB5Agr4=",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
+ "integrity": "sha1-cEvSl95tdi3lTqu+r79V9nVqvi8=",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
+ "integrity": "sha1-tUCifRTkr9BYSWpNvsTT9BTbEQo=",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
+ "integrity": "sha1-0csWbTSw+/D+irRgpVlPJKN4cB4=",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
+ "integrity": "sha1-EDSyZFf8iGNo/mG70J9lP2r6jlQ=",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
+ "integrity": "sha1-ZVVqQyoeTXIDLYIYwZMvzKGkl3I=",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
+ "integrity": "sha1-LmHgWS+QMNfj2uGO4l68U1kYrvY=",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
+ "integrity": "sha1-yV7CiZWe+AecTcqBeh4sS+Zrm9M=",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
+ "integrity": "sha1-wJoPZ5F1kqwN6JKpvk04FN69Kmw=",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
+ "integrity": "sha1-QLIhdd2gYYLz7oFBGGxf8wTEpxc=",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
+ "integrity": "sha1-pYD5xnZ5eDOJHlGfx6EzfIr9jbM=",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
+ "integrity": "sha1-RkUs8yHcf56Rwvp4Cla7Vuec1os=",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
+ "integrity": "sha1-QhGzGE3WYI9T3LIuOfXTTuCIUsg=",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
+ "integrity": "sha1-aXhXwqYcubC2u2ZS5AwdxeHKjl0=",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
+ "integrity": "sha1-0ZKUPrFGpArExkl9DPe+NbmGvwg=",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
+ "integrity": "sha1-rOoDVtoODrwI+Xz3ucLkAeHmSNw=",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
+ "integrity": "sha1-bww84MtkxTS3DExF7LLBbTTjXf0=",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
+ "integrity": "sha1-i813B3oNzjN4tXT+2ybSolO3PTY=",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
+ "integrity": "sha1-5/sqAemcgwyU5mI82f77TI+1g0c=",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
+ "integrity": "sha1-xSkJNy24uG4sVeBaiUADO1Zgo7I=",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
+ "integrity": "sha1-xCe5vlpkwmL/mn63C1+7qt9EbGw=",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
+ "integrity": "sha1-3JsUe6yi5sSzyFVxdB70hgpIkJc=",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
+ "integrity": "sha1-zoZtEt8TwV5MmfBzo9Rm9uBkmzo=",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
+ "integrity": "sha1-dGjjaS0B1inVlB5dg4F7uA+eObQ=",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
+ "integrity": "sha1-pbwAY/sryrbQ7WPyoVN5WLwmnsY=",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
+ "integrity": "sha1-EAZO5E9DR7kMmgK0Rrv4CpFjKxI=",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
@@ -499,6 +942,7 @@
"integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.56.0",
"@typescript-eslint/types": "8.56.0",
@@ -757,6 +1201,7 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -1480,6 +1925,48 @@
"node": ">= 0.4"
}
},
+ "node_modules/esbuild": {
+ "version": "0.28.1",
+ "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/esbuild/-/esbuild-0.28.1.tgz",
+ "integrity": "sha1-70W0Y0ycnZeilq6kEUpfmED5VXg=",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.28.1",
+ "@esbuild/android-arm": "0.28.1",
+ "@esbuild/android-arm64": "0.28.1",
+ "@esbuild/android-x64": "0.28.1",
+ "@esbuild/darwin-arm64": "0.28.1",
+ "@esbuild/darwin-x64": "0.28.1",
+ "@esbuild/freebsd-arm64": "0.28.1",
+ "@esbuild/freebsd-x64": "0.28.1",
+ "@esbuild/linux-arm": "0.28.1",
+ "@esbuild/linux-arm64": "0.28.1",
+ "@esbuild/linux-ia32": "0.28.1",
+ "@esbuild/linux-loong64": "0.28.1",
+ "@esbuild/linux-mips64el": "0.28.1",
+ "@esbuild/linux-ppc64": "0.28.1",
+ "@esbuild/linux-riscv64": "0.28.1",
+ "@esbuild/linux-s390x": "0.28.1",
+ "@esbuild/linux-x64": "0.28.1",
+ "@esbuild/netbsd-arm64": "0.28.1",
+ "@esbuild/netbsd-x64": "0.28.1",
+ "@esbuild/openbsd-arm64": "0.28.1",
+ "@esbuild/openbsd-x64": "0.28.1",
+ "@esbuild/openharmony-arm64": "0.28.1",
+ "@esbuild/sunos-x64": "0.28.1",
+ "@esbuild/win32-arm64": "0.28.1",
+ "@esbuild/win32-ia32": "0.28.1",
+ "@esbuild/win32-x64": "0.28.1"
+ }
+ },
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
@@ -1515,6 +2002,7 @@
"integrity": "sha512-20MV9SUdeN6Jd84xESsKhRly+/vxI+hwvpBMA93s+9dAcjdCuCojn4IqUGS3lvVaqjVYGYHSRMCpeFtF2rQYxQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.2",
@@ -1745,6 +2233,7 @@
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.1",
@@ -2149,6 +2638,7 @@
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.26.tgz",
"integrity": "sha512-uyZtpnYxM9CmQ7QsQknM4zN8EftNqhON1qYeIKM0Se67CCEe2c44xyGURwB0axX2fBDu1dqHrHAc1hmNT8ITkw==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=16.9.0"
}
@@ -3732,6 +4222,7 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=12"
},
@@ -3807,6 +4298,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -4122,6 +4614,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
+ "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/package.json b/package.json
index 3872e82..2da86e3 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
{
"name": "debugmcpextension",
- "displayName": "DebugMCP",
- "description": "Let AI agents debug your code inside VS Code — breakpoints, step-through execution, variable inspection, and expression evaluation. Automatically exposes itself as an MCP (Model Context Protocol) server for seamless integration with AI assistants.",
- "version": "2.2.0",
+ "displayName": "DebugMCP — AI Agent Debugging for VS Code",
+ "description": "Your AI agent debugs for you — right inside VS Code. Let Copilot, Cline, Cursor, Codex & any MCP agent set breakpoints, step through code, and inspect variables live instead of guessing from logs.",
+ "version": "2.2.1",
"publisher": "ozzafar",
"author": {
"name": "Oz Zafar",
@@ -28,15 +28,34 @@
"breakpoints",
"debugger",
"ai agent",
- "copilot"
+ "copilot",
+ "cline",
+ "cursor",
+ "windsurf",
+ "roo code",
+ "codex",
+ "claude",
+ "autonomous debugging",
+ "agent debugging",
+ "test debugging",
+ "pytest",
+ "jest",
+ "debug adapter protocol",
+ "dap"
],
"engines": {
"vscode": "^1.104.0"
},
"icon": "assets/debug_mcp_icon.png",
+ "galleryBanner": {
+ "color": "#1e1e1e",
+ "theme": "dark"
+ },
"categories": [
"Debuggers",
"AI",
+ "Chat",
+ "Machine Learning",
"Programming Languages"
],
"extensionDependencies": [],
@@ -48,7 +67,7 @@
"onStartupFinished",
"onDebug"
],
- "main": "./out/extension.js",
+ "main": "./dist/extension.js",
"contributes": {
"commands": [
{
@@ -96,10 +115,13 @@
}
},
"scripts": {
- "vscode:prepublish": "npm run compile",
+ "vscode:prepublish": "npm run package",
"compile": "tsc -p ./",
+ "check-types": "tsc --noEmit -p ./",
+ "bundle": "node esbuild.js",
+ "package": "npm run check-types && node esbuild.js --production",
"watch": "tsc -watch -p ./",
- "pretest": "npm run compile && npm run lint",
+ "pretest": "npm run compile && npm run bundle && npm run lint",
"lint": "eslint src",
"test": "vscode-test"
},
@@ -117,6 +139,7 @@
"@typescript-eslint/parser": "^8.56.0",
"@vscode/test-cli": "^0.0.12",
"@vscode/test-electron": "^2.5.2",
+ "esbuild": "^0.28.1",
"eslint": "^10.0.1",
"typescript": "^5.9.3"
},
diff --git a/skills/debug-live/SKILL.md b/skills/debug-live/SKILL.md
index 660d6d9..ac8c010 100644
--- a/skills/debug-live/SKILL.md
+++ b/skills/debug-live/SKILL.md
@@ -14,6 +14,7 @@ allowed-tools:
- step_into
- step_out
- continue_execution
+ - pause_execution
- get_variables_values
- evaluate_expression
---
@@ -57,7 +58,9 @@ If you can step through the code in a few tool calls, do that instead of specula
program either hits a breakpoint (`stopped`) or runs to completion without pausing
(`terminated`).
4. **Navigate and inspect.** Use `step_over`, `step_into`, `step_out`, `continue_execution`
- to move through code. Use `get_variables_values` to see local/global state and
+ to move through code. Use `pause_execution` to interrupt a freely-running program
+ (e.g. a busy loop or embedded target) when there is no breakpoint to stop at.
+ Use `get_variables_values` to see local/global state and
`evaluate_expression` to test hypotheses live (call methods, read properties, run
list comprehensions, etc.).
5. **Find the root cause** (see framework below). Don't stop at the first wrong thing
diff --git a/src/controlServer.ts b/src/controlServer.ts
index cf72d1b..6a21512 100644
--- a/src/controlServer.ts
+++ b/src/controlServer.ts
@@ -91,6 +91,8 @@ export class ControlServer {
return this.handler.handleStepOut();
case 'handleContinue':
return this.handler.handleContinue();
+ case 'handlePause':
+ return this.handler.handlePause();
case 'handleRestart':
return this.handler.handleRestart();
case 'handleAddBreakpoint':
diff --git a/src/debugMCPServer.ts b/src/debugMCPServer.ts
index 121568f..39530ac 100644
--- a/src/debugMCPServer.ts
+++ b/src/debugMCPServer.ts
@@ -161,6 +161,14 @@ export class DebugMCPServer {
const server = new McpServer({
name: 'debugmcp',
version: '1.0.0',
+ }, {
+ // Surfaced to clients at `initialize`. Points agents at the
+ // `debug-live` Agent Skill, which the extension installs into the
+ // standard skills directories for harnesses that load skills.
+ instructions: 'These tools drive the VS Code debugger to investigate bugs, failing tests, ' +
+ 'wrong/null values and other "it doesn\'t work" reports by stepping through code. ' +
+ 'The companion "debug-live" Agent Skill describes the full interactive workflow: ' +
+ 'when to set breakpoints, how to step and inspect state, and how to do root-cause analysis.',
});
this.setupTools(server, this.handlerFactory());
return server;
@@ -206,7 +214,7 @@ export class DebugMCPServer {
server.registerTool('start_debugging', {
description: 'Start a VS Code debug session for a source file, optionally for a single test method. ' +
'Use when investigating bugs, failing tests, wrong/null variable values, unexpected runtime behavior, ' +
- 'or any "it doesn\'t work" report where stepping through the code is cheaper than speculation.',
+ 'or any "it doesn\'t work" report. See the "debug-live" skill for the full investigation workflow.',
inputSchema: {
fileFullPath: z.string().describe('Full path to the source code file to debug'),
workingDirectory: z.string().describe('Working directory for the debug session'),
@@ -248,6 +256,11 @@ export class DebugMCPServer {
description: 'Resume program execution until the next breakpoint is hit or the program completes.',
}, async () => this.runTool('continue_execution', () => debuggingHandler.handleContinue()));
+ // Pause execution tool
+ server.registerTool('pause_execution', {
+ description: 'Interrupt a running program and stop at its current location, even when no breakpoint is set. Useful for busy loops or embedded/bare-metal targets running freely — then inspect variables or step from where it stopped.',
+ }, async () => this.runTool('pause_execution', () => debuggingHandler.handlePause()));
+
// Restart debugging tool
server.registerTool('restart_debugging', {
description: 'Restart the debug session from the beginning with the same configuration.',
diff --git a/src/debuggingExecutor.ts b/src/debuggingExecutor.ts
index c2d2cd8..39d0b68 100644
--- a/src/debuggingExecutor.ts
+++ b/src/debuggingExecutor.ts
@@ -30,6 +30,7 @@ export interface IDebuggingExecutor {
stepInto(): Promise;
stepOut(): Promise;
continue(): Promise;
+ pause(): Promise;
restart(): Promise;
addBreakpoint(uri: vscode.Uri, line: number, condition?: string): Promise;
removeBreakpoint(uri: vscode.Uri, line: number): Promise;
@@ -305,6 +306,19 @@ export class DebuggingExecutor implements IDebuggingExecutor {
}
}
+ /**
+ * Execute pause command — interrupt a running program so execution stops at
+ * its current point (useful for embedded/bare-metal or busy-loop debugging
+ * where there is no breakpoint to stop at).
+ */
+ public async pause(): Promise {
+ try {
+ await vscode.commands.executeCommand('workbench.action.debug.pause');
+ } catch (error) {
+ throw new Error(`Failed to pause: ${error}`);
+ }
+ }
+
/**
* Execute restart command
*/
diff --git a/src/debuggingHandler.ts b/src/debuggingHandler.ts
index b04a785..cec55e5 100644
--- a/src/debuggingHandler.ts
+++ b/src/debuggingHandler.ts
@@ -16,6 +16,7 @@ export interface IDebuggingHandler {
handleStepInto(): Promise;
handleStepOut(): Promise;
handleContinue(): Promise;
+ handlePause(): Promise;
handleRestart(): Promise;
handleAddBreakpoint(args: { fileFullPath: string; lineContent: string; condition?: string }): Promise;
handleRemoveBreakpoint(args: { fileFullPath: string; line: number }): Promise;
@@ -250,6 +251,31 @@ export class DebuggingHandler implements IDebuggingHandler {
}
}
+ /**
+ * Pause execution — interrupt a running program so it stops at its current
+ * location. Unlike a breakpoint, this works even when no breakpoint is set
+ * (e.g. a busy loop or an embedded/bare-metal target that is running freely).
+ */
+ public async handlePause(): Promise {
+ try {
+ if (!(await this.executor.hasActiveSession())) {
+ throw new Error('Debug session is not ready. Please wait for initialization to complete.');
+ }
+
+ // Get the state before executing the command
+ const beforeState = await this.executor.getCurrentDebugState(this.numNextLines);
+
+ await this.executor.pause();
+
+ // Wait for the debugger to stop (pause raises a 'stopped' event)
+ const afterState = await this.waitForStateChange(beforeState);
+
+ return afterState.toString();
+ } catch (error) {
+ throw new Error(`Error executing pause: ${error}`);
+ }
+ }
+
/**
* Restart the debugging session
*/
diff --git a/src/routingDebuggingHandler.ts b/src/routingDebuggingHandler.ts
index efb7171..ee1acc4 100644
--- a/src/routingDebuggingHandler.ts
+++ b/src/routingDebuggingHandler.ts
@@ -190,6 +190,10 @@ export class RoutingDebuggingHandler implements IDebuggingHandler {
return this.forward('handleContinue', {});
}
+ public handlePause(): Promise {
+ return this.forward('handlePause', {});
+ }
+
public handleRestart(): Promise {
return this.forward('handleRestart', {});
}
diff --git a/src/test/debuggingHandler.test.ts b/src/test/debuggingHandler.test.ts
index d49e511..e2d2730 100644
--- a/src/test/debuggingHandler.test.ts
+++ b/src/test/debuggingHandler.test.ts
@@ -144,6 +144,7 @@ suite('DebuggingHandler waitForStateChange (event-driven)', () => {
stepInto: async () => { /* noop */ },
stepOut: async () => { /* noop */ },
continue: async () => { /* noop */ },
+ pause: async () => { /* noop */ },
restart: async () => { /* noop */ },
addBreakpoint: async () => { /* noop */ },
removeBreakpoint: async () => { /* noop */ },
diff --git a/src/test/routing.test.ts b/src/test/routing.test.ts
index cec6a3b..a53ccf8 100644
--- a/src/test/routing.test.ts
+++ b/src/test/routing.test.ts
@@ -24,6 +24,7 @@ class RecordingHandler implements IDebuggingHandler {
handleStepInto() { return this.record('stepInto', {}); }
handleStepOut() { return this.record('stepOut', {}); }
handleContinue() { return this.record('continue', {}); }
+ handlePause() { return this.record('pause', {}); }
handleRestart() { return this.record('restart', {}); }
handleAddBreakpoint(args: any) { return this.record('addBp', args); }
handleRemoveBreakpoint(args: any) { return this.record('removeBp', args); }
diff --git a/src/test/startDebuggingMatrix.test.ts b/src/test/startDebuggingMatrix.test.ts
index 5b9feca..098934b 100644
--- a/src/test/startDebuggingMatrix.test.ts
+++ b/src/test/startDebuggingMatrix.test.ts
@@ -75,6 +75,7 @@ function makeMocks(opts: MockOpts) {
stepInto: async () => { /* noop */ },
stepOut: async () => { /* noop */ },
continue: async () => { /* noop */ },
+ pause: async () => { /* noop */ },
restart: async () => { /* noop */ },
addBreakpoint: async () => { /* noop */ },
removeBreakpoint: async () => { /* noop */ },
diff --git a/src/utils/agentConfigurationManager.ts b/src/utils/agentConfigurationManager.ts
index 7534265..af23a90 100644
--- a/src/utils/agentConfigurationManager.ts
+++ b/src/utils/agentConfigurationManager.ts
@@ -189,14 +189,20 @@ export class AgentConfigurationManager {
}
/**
- * Get the personal skills directory for a given agent. By convention we
- * place skills alongside the agent's MCP config file (i.e. in a `skills/`
- * sibling of the config file's parent directory). This matches harnesses
- * like Copilot CLI (`~/.copilot/skills/`) and gives a sensible default
- * for the other supported agents.
+ * Personal skill install targets, following the Agent Skills open standard
+ * (agentskills.io). `~/.agents/skills/` is the cross-agent location honored
+ * by skills-compatible harnesses (including VS Code agent mode and Copilot
+ * CLI); we also install into `~/.copilot/skills/` when a Copilot home exists.
+ * See issue #105.
*/
- private getSkillsDirForAgent(agent: AgentInfo): string {
- return path.join(path.dirname(agent.configPath), 'skills');
+ private getSkillInstallTargets(): string[] {
+ const home = os.homedir();
+ const targets = [path.join(home, '.agents', 'skills', 'debug-live')];
+ const copilotHome = process.env.COPILOT_HOME || path.join(home, '.copilot');
+ if (fs.existsSync(copilotHome)) {
+ targets.push(path.join(copilotHome, 'skills', 'debug-live'));
+ }
+ return targets;
}
/**
@@ -207,49 +213,59 @@ export class AgentConfigurationManager {
}
/**
- * Copy the bundled debugmcp skill into the agent's personal skills
- * directory. This is best-effort: if the bundled skill is missing or the
- * copy fails, we log a warning but do not fail the MCP configuration.
+ * Install the bundled debugmcp skill into the standard personal skills
+ * directories (see getSkillInstallTargets). Agent-independent — the skill
+ * lives in one shared location per the Agent Skills open standard rather
+ * than being copied next to each agent's config. Returns the primary
+ * destination for UI, or null when nothing could be installed. Best-effort:
+ * failures are logged and skipped.
*/
- private async registerDebugMCPSkill(agent: AgentInfo): Promise {
+ private async installDebugMCPSkill(): Promise {
const bundledSkillPath = this.getBundledSkillPath();
if (!fs.existsSync(bundledSkillPath)) {
- console.warn(`Bundled debugmcp skill not found at ${bundledSkillPath}; skipping skill registration for ${agent.name}`);
+ console.warn(`Bundled debugmcp skill not found at ${bundledSkillPath}; skipping skill install`);
return null;
}
- const skillsDir = this.getSkillsDirForAgent(agent);
- const destination = path.join(skillsDir, 'debug-live');
-
- try {
- await fs.promises.mkdir(skillsDir, { recursive: true });
- await fs.promises.cp(bundledSkillPath, destination, { recursive: true, force: true });
- console.log(`Successfully registered debugmcp skill for ${agent.name} at ${destination}`);
-
- // Back-compat cleanup: earlier builds installed the skill under
- // different directory names (`debug` in 1.2.0, later `really-debug`).
- // Remove any stale copies so users don't end up with competing
- // entries alongside the current `debug-live` skill.
- const legacyDestinations = [
- path.join(skillsDir, 'debug'),
- path.join(skillsDir, 'really-debug'),
- ];
- for (const legacyDestination of legacyDestinations) {
- if (fs.existsSync(legacyDestination)) {
- try {
- await fs.promises.rm(legacyDestination, { recursive: true, force: true });
- console.log(`Removed legacy debugmcp skill at ${legacyDestination}`);
- } catch (cleanupError) {
- console.warn(`Failed to remove legacy debugmcp skill at ${legacyDestination}:`, cleanupError);
- }
+ let primaryDestination: string | null = null;
+ for (const destination of this.getSkillInstallTargets()) {
+ const skillsDir = path.dirname(destination);
+ try {
+ await fs.promises.mkdir(skillsDir, { recursive: true });
+ await fs.promises.cp(bundledSkillPath, destination, { recursive: true, force: true });
+ console.log(`Installed debugmcp skill at ${destination}`);
+ await this.removeLegacySkills(skillsDir);
+ if (!primaryDestination) {
+ primaryDestination = destination;
}
+ } catch (error) {
+ console.warn(`Failed to install debugmcp skill at ${destination}:`, error);
}
+ }
- return destination;
- } catch (error) {
- console.warn(`Failed to register debugmcp skill for ${agent.name} at ${destination}:`, error);
- return null;
+ return primaryDestination;
+ }
+
+ /**
+ * Remove stale skill copies from earlier builds (`debug` in 1.2.0, later
+ * `really-debug`) so users don't end up with competing entries alongside
+ * the current `debug-live` skill.
+ */
+ private async removeLegacySkills(skillsDir: string): Promise {
+ const legacyDestinations = [
+ path.join(skillsDir, 'debug'),
+ path.join(skillsDir, 'really-debug'),
+ ];
+ for (const legacyDestination of legacyDestinations) {
+ if (fs.existsSync(legacyDestination)) {
+ try {
+ await fs.promises.rm(legacyDestination, { recursive: true, force: true });
+ console.log(`Removed legacy debugmcp skill at ${legacyDestination}`);
+ } catch (cleanupError) {
+ console.warn(`Failed to remove legacy debugmcp skill at ${legacyDestination}:`, cleanupError);
+ }
+ }
}
}
@@ -379,7 +395,7 @@ export class AgentConfigurationManager {
// Back-compat: existing Codex users had only the MCP server registered.
// If DebugMCP is configured, also ensure the bundled skill is installed.
if (this.hasCodexDebugMCPSection(configContent)) {
- await this.ensureSkillRegistered(agent);
+ await this.ensureSkillRegistered();
}
continue;
@@ -431,7 +447,7 @@ export class AgentConfigurationManager {
// Back-compat: existing users had only the MCP server registered.
// If DebugMCP is configured, also ensure the bundled skill is installed.
- await this.ensureSkillRegistered(agent);
+ await this.ensureSkillRegistered();
} catch (error) {
console.error(`Error migrating config for ${agent.name}:`, error);
// Continue with other agents even if one fails
@@ -446,16 +462,16 @@ export class AgentConfigurationManager {
}
/**
- * Ensure the bundled debugmcp skill is installed in the agent's personal
- * skills directory. Safe to call on every activation — `registerDebugMCPSkill`
- * uses `fs.cp` with `force: true`, so it will refresh stale copies but is a
+ * Ensure the bundled debugmcp skill is installed in the standard personal
+ * skills directories. Safe to call on every activation — `installDebugMCPSkill`
+ * uses `fs.cp` with `force: true`, so it refreshes stale copies but is a
* no-op when the destination already matches.
*/
- private async ensureSkillRegistered(agent: AgentInfo): Promise {
+ private async ensureSkillRegistered(): Promise {
try {
- await this.registerDebugMCPSkill(agent);
+ await this.installDebugMCPSkill();
} catch (error) {
- console.warn(`Failed to ensure skill registration for ${agent.name}:`, error);
+ console.warn('Failed to ensure skill installation:', error);
}
}
@@ -508,7 +524,7 @@ export class AgentConfigurationManager {
);
console.log(`Successfully added DebugMCP configuration to ${agent.name}`);
- const skillPath = await this.registerDebugMCPSkill(agent);
+ const skillPath = await this.installDebugMCPSkill();
return { success: true, skillPath };
}
@@ -542,7 +558,7 @@ export class AgentConfigurationManager {
);
console.log(`Successfully added DebugMCP configuration to ${agent.name}`);
- const skillPath = await this.registerDebugMCPSkill(agent);
+ const skillPath = await this.installDebugMCPSkill();
return { success: true, skillPath };
} catch (error) {
console.error(`Error adding DebugMCP to ${agent.name}:`, error);