debug-run 0.5.7 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,6 +34,25 @@ Check available adapters:
34
34
  npx debug-run list-adapters
35
35
  ```
36
36
 
37
+ ## Installing the Skill
38
+
39
+ To install this skill for AI coding assistants:
40
+
41
+ ```bash
42
+ # Install to Claude Code (~/.claude/skills/)
43
+ npx debug-run install-skill --claude
44
+
45
+ # Install to GitHub Copilot (~/.copilot/skills/)
46
+ npx debug-run install-skill --copilot
47
+
48
+ # Install to project directory (for project-specific skills)
49
+ npx debug-run install-skill --claude --project
50
+ npx debug-run install-skill --copilot --project
51
+
52
+ # Install to custom directory
53
+ npx debug-run install-skill --dir /path/to/skills
54
+ ```
55
+
37
56
  ## Language-Specific Guides
38
57
 
39
58
  For detailed setup, examples, and troubleshooting for each language:
package/README.md CHANGED
@@ -178,6 +178,35 @@ debug-run outputs newline-delimited JSON (NDJSON) events:
178
178
  {"type":"session_end","summary":{"breakpointsHit":1,"duration":1234}}
179
179
  ```
180
180
 
181
+ ### Breakpoint Set Event (with Diagnostics)
182
+
183
+ When a breakpoint cannot be verified (e.g., source mapping issues, missing debug symbols), the event includes actionable diagnostics:
184
+
185
+ ```json
186
+ {
187
+ "type": "breakpoint_set",
188
+ "id": 1,
189
+ "file": "src/handler.ts",
190
+ "line": 45,
191
+ "verified": false,
192
+ "message": "Could not resolve source location",
193
+ "diagnostics": {
194
+ "requestedFile": "src/handler.ts",
195
+ "requestedLine": 45,
196
+ "adapterMessage": "Could not resolve source location",
197
+ "suggestions": [
198
+ "Ensure \"sourceMap\": true in tsconfig.json",
199
+ "Rebuild with source maps: tsc --sourceMap (or your build command)",
200
+ "Verify .map files exist in your output directory (e.g., dist/**/*.map)"
201
+ ],
202
+ "adapterType": "node",
203
+ "fileExtension": ".ts"
204
+ }
205
+ }
206
+ ```
207
+
208
+ Suggestions are context-aware based on the adapter and file type.
209
+
181
210
  ### Breakpoint Hit Event
182
211
 
183
212
  ```json
package/dist/index.cjs CHANGED
@@ -3356,6 +3356,13 @@ function findVsdbg() {
3356
3356
  return null;
3357
3357
  }
3358
3358
  function findDebugpy() {
3359
+ const debugpyExt = findExtension("ms-python.debugpy");
3360
+ if (debugpyExt) {
3361
+ const debugpyPath = path3.join(debugpyExt, "bundled", "libs", "debugpy");
3362
+ if ((0, import_node_fs2.existsSync)(debugpyPath)) {
3363
+ return debugpyPath;
3364
+ }
3365
+ }
3359
3366
  const pythonExt = findExtension("ms-python.python");
3360
3367
  if (pythonExt) {
3361
3368
  const possiblePaths = [
@@ -3517,6 +3524,16 @@ var debugpyAdapter = {
3517
3524
  get args() {
3518
3525
  return ["-m", "debugpy.adapter"];
3519
3526
  },
3527
+ get env() {
3528
+ if (cachedSource === "vscode" && cachedDebugpyPath) {
3529
+ const debugpyParentDir = path5.dirname(cachedDebugpyPath);
3530
+ const existingPythonPath = process.env.PYTHONPATH || "";
3531
+ return {
3532
+ PYTHONPATH: existingPythonPath ? `${debugpyParentDir}:${existingPythonPath}` : debugpyParentDir
3533
+ };
3534
+ }
3535
+ return void 0;
3536
+ },
3520
3537
  detect: async () => {
3521
3538
  const pythonCmd = await findPythonCommand();
3522
3539
  if (!pythonCmd) {
@@ -5189,7 +5206,7 @@ var OutputFormatter = class {
5189
5206
  /**
5190
5207
  * Emit a breakpoint_set event
5191
5208
  */
5192
- breakpointSet(id, file, line, verified, condition, message) {
5209
+ breakpointSet(id, file, line, verified, condition, message, diagnostics) {
5193
5210
  this.emit(
5194
5211
  this.createEvent("breakpoint_set", {
5195
5212
  id,
@@ -5197,7 +5214,8 @@ var OutputFormatter = class {
5197
5214
  line,
5198
5215
  verified,
5199
5216
  condition,
5200
- message
5217
+ message,
5218
+ diagnostics
5201
5219
  })
5202
5220
  );
5203
5221
  }
@@ -5224,12 +5242,161 @@ function resolveBreakpointPath(file, options = {}) {
5224
5242
  if (options.cwd) {
5225
5243
  return path10.resolve(options.cwd, file);
5226
5244
  }
5227
- if (options.programPath) {
5228
- const programDir = path10.dirname(options.programPath);
5229
- return path10.resolve(programDir, file);
5230
- }
5231
5245
  return path10.resolve(file);
5232
5246
  }
5247
+ function validateBreakpointSpec(spec) {
5248
+ const trimmed = spec.trim();
5249
+ if (!trimmed) {
5250
+ return {
5251
+ valid: false,
5252
+ error: "Breakpoint specification cannot be empty",
5253
+ spec
5254
+ };
5255
+ }
5256
+ if (!trimmed.includes(":")) {
5257
+ return {
5258
+ valid: false,
5259
+ error: `Invalid breakpoint format "${spec}". Expected "file:line" (e.g., "Program.cs:42")`,
5260
+ spec
5261
+ };
5262
+ }
5263
+ const match = trimmed.match(/^(.+):(\d+)(?:\?(.+)|#(\d+))?$/);
5264
+ if (!match) {
5265
+ const colonIdx = trimmed.lastIndexOf(":");
5266
+ if (colonIdx !== -1) {
5267
+ const linePartRaw = trimmed.slice(colonIdx + 1);
5268
+ const linePart = linePartRaw.split("?")[0].split("#")[0];
5269
+ if (linePart === "") {
5270
+ return {
5271
+ valid: false,
5272
+ error: `Missing line number in breakpoint "${spec}". Expected "file:line" (e.g., "Program.cs:42")`,
5273
+ spec
5274
+ };
5275
+ }
5276
+ if (!/^\d+$/.test(linePart)) {
5277
+ return {
5278
+ valid: false,
5279
+ error: `Invalid line number "${linePart}" in breakpoint "${spec}". Line must be a positive integer`,
5280
+ spec
5281
+ };
5282
+ }
5283
+ }
5284
+ return {
5285
+ valid: false,
5286
+ error: `Invalid breakpoint format "${spec}". Expected "file:line" (e.g., "Program.cs:42")`,
5287
+ spec
5288
+ };
5289
+ }
5290
+ const [, file, lineStr] = match;
5291
+ const line = parseInt(lineStr, 10);
5292
+ if (!file || !file.trim()) {
5293
+ return {
5294
+ valid: false,
5295
+ error: `Missing file path in breakpoint "${spec}". Expected "file:line" (e.g., "Program.cs:42")`,
5296
+ spec
5297
+ };
5298
+ }
5299
+ if (isNaN(line) || line < 1) {
5300
+ return {
5301
+ valid: false,
5302
+ error: `Invalid line number "${lineStr}" in breakpoint "${spec}". Line must be a positive integer`,
5303
+ spec
5304
+ };
5305
+ }
5306
+ return { valid: true };
5307
+ }
5308
+ function validateLogpointSpec(spec) {
5309
+ const trimmed = spec.trim();
5310
+ if (!trimmed) {
5311
+ return {
5312
+ valid: false,
5313
+ error: "Logpoint specification cannot be empty",
5314
+ spec
5315
+ };
5316
+ }
5317
+ if (!trimmed.includes("|")) {
5318
+ return {
5319
+ valid: false,
5320
+ error: `Invalid logpoint format "${spec}". Expected "file:line|message" (e.g., "Program.cs:42|value is {x}")`,
5321
+ spec
5322
+ };
5323
+ }
5324
+ const pipeIdx = trimmed.indexOf("|");
5325
+ const beforePipe = trimmed.slice(0, pipeIdx);
5326
+ if (!beforePipe.includes(":")) {
5327
+ return {
5328
+ valid: false,
5329
+ error: `Invalid logpoint format "${spec}". Expected "file:line|message" (e.g., "Program.cs:42|value is {x}")`,
5330
+ spec
5331
+ };
5332
+ }
5333
+ const match = trimmed.match(/^(.+):(\d+)\|(.+)$/);
5334
+ if (!match) {
5335
+ const colonIdx = beforePipe.lastIndexOf(":");
5336
+ if (colonIdx !== -1) {
5337
+ const linePart = beforePipe.slice(colonIdx + 1);
5338
+ if (linePart === "") {
5339
+ return {
5340
+ valid: false,
5341
+ error: `Missing line number in logpoint "${spec}". Expected "file:line|message"`,
5342
+ spec
5343
+ };
5344
+ }
5345
+ if (!/^\d+$/.test(linePart)) {
5346
+ return {
5347
+ valid: false,
5348
+ error: `Invalid line number "${linePart}" in logpoint "${spec}". Line must be a positive integer`,
5349
+ spec
5350
+ };
5351
+ }
5352
+ }
5353
+ return {
5354
+ valid: false,
5355
+ error: `Invalid logpoint format "${spec}". Expected "file:line|message" (e.g., "Program.cs:42|value is {x}")`,
5356
+ spec
5357
+ };
5358
+ }
5359
+ const [, file, lineStr, message] = match;
5360
+ const line = parseInt(lineStr, 10);
5361
+ if (!file || !file.trim()) {
5362
+ return {
5363
+ valid: false,
5364
+ error: `Missing file path in logpoint "${spec}". Expected "file:line|message"`,
5365
+ spec
5366
+ };
5367
+ }
5368
+ if (isNaN(line) || line < 1) {
5369
+ return {
5370
+ valid: false,
5371
+ error: `Invalid line number "${lineStr}" in logpoint "${spec}". Line must be a positive integer`,
5372
+ spec
5373
+ };
5374
+ }
5375
+ if (!message || !message.trim()) {
5376
+ return {
5377
+ valid: false,
5378
+ error: `Missing log message in logpoint "${spec}". Expected "file:line|message"`,
5379
+ spec
5380
+ };
5381
+ }
5382
+ return { valid: true };
5383
+ }
5384
+ function validateAllBreakpoints(breakpoints, logpoints = []) {
5385
+ const errors = [];
5386
+ for (const bp of breakpoints) {
5387
+ const result = validateBreakpointSpec(bp);
5388
+ if (!result.valid && result.error) {
5389
+ errors.push(result.error);
5390
+ }
5391
+ }
5392
+ for (const lp of logpoints) {
5393
+ const result = validateLogpointSpec(lp);
5394
+ if (!result.valid && result.error) {
5395
+ errors.push(result.error);
5396
+ }
5397
+ }
5398
+ return errors;
5399
+ }
5233
5400
  function parseBreakpointSpec(spec, pathOptions = {}) {
5234
5401
  const match = spec.match(/^(.+):(\d+)(?:\?(.+)|#(\d+))?$/);
5235
5402
  if (!match) {
@@ -5271,10 +5438,12 @@ var BreakpointManager = class {
5271
5438
  breakpoints = /* @__PURE__ */ new Map();
5272
5439
  nextId = 1;
5273
5440
  pathOptions;
5274
- constructor(client, formatter, pathOptions = {}) {
5441
+ adapterType;
5442
+ constructor(client, formatter, options = {}) {
5275
5443
  this.client = client;
5276
5444
  this.formatter = formatter;
5277
- this.pathOptions = pathOptions;
5445
+ this.pathOptions = options;
5446
+ this.adapterType = options.adapterType;
5278
5447
  }
5279
5448
  /**
5280
5449
  * Add a breakpoint from a spec string
@@ -5321,6 +5490,7 @@ var BreakpointManager = class {
5321
5490
  hitCondition: spec.hitCondition,
5322
5491
  logMessage: spec.logMessage
5323
5492
  }));
5493
+ const requestedLines = specs.map((spec) => spec.line);
5324
5494
  try {
5325
5495
  const response = await this.client.setBreakpoints({
5326
5496
  source: { path: file },
@@ -5333,22 +5503,34 @@ var BreakpointManager = class {
5333
5503
  specs[i].verified = bp.verified;
5334
5504
  specs[i].message = bp.message;
5335
5505
  specs[i].line = bp.line ?? specs[i].line;
5506
+ const diagnostics = !bp.verified ? this.generateDiagnostics(file, requestedLines[i], bp.message) : void 0;
5336
5507
  this.formatter.breakpointSet(
5337
5508
  specs[i].id,
5338
5509
  file,
5339
5510
  specs[i].line,
5340
5511
  specs[i].verified,
5341
5512
  specs[i].condition,
5342
- specs[i].message
5513
+ specs[i].message,
5514
+ diagnostics
5343
5515
  );
5344
5516
  }
5345
5517
  }
5346
5518
  } catch (error) {
5347
- for (const spec of specs) {
5519
+ for (let i = 0; i < specs.length; i++) {
5520
+ const spec = specs[i];
5348
5521
  spec.id = this.nextId++;
5349
5522
  spec.verified = false;
5350
5523
  spec.message = error instanceof Error ? error.message : "Failed to set breakpoint";
5351
- this.formatter.breakpointSet(spec.id, file, spec.line, false, spec.condition, spec.message);
5524
+ const diagnostics = this.generateDiagnostics(file, requestedLines[i], spec.message);
5525
+ this.formatter.breakpointSet(
5526
+ spec.id,
5527
+ file,
5528
+ spec.line,
5529
+ false,
5530
+ spec.condition,
5531
+ spec.message,
5532
+ diagnostics
5533
+ );
5352
5534
  }
5353
5535
  }
5354
5536
  }
@@ -5378,7 +5560,103 @@ var BreakpointManager = class {
5378
5560
  getHitCount() {
5379
5561
  return 0;
5380
5562
  }
5563
+ /**
5564
+ * Generate diagnostics for an unverified breakpoint
5565
+ */
5566
+ generateDiagnostics(requestedFile, requestedLine, adapterMessage) {
5567
+ const ext = path10.extname(requestedFile).toLowerCase();
5568
+ const suggestions = getBreakpointSuggestions(this.adapterType, ext, adapterMessage);
5569
+ return {
5570
+ requestedFile,
5571
+ requestedLine,
5572
+ adapterMessage,
5573
+ suggestions,
5574
+ adapterType: this.adapterType,
5575
+ fileExtension: ext || void 0
5576
+ };
5577
+ }
5381
5578
  };
5579
+ function normalizeAdapterType(adapterType) {
5580
+ if (!adapterType) return void 0;
5581
+ const lower = adapterType.toLowerCase();
5582
+ if (["dotnet", "coreclr", "vsdbg", "netcoredbg"].includes(lower)) {
5583
+ return "coreclr";
5584
+ }
5585
+ if (["node", "nodejs", "javascript", "js", "typescript", "ts"].includes(lower)) {
5586
+ return "node";
5587
+ }
5588
+ if (["debugpy", "python", "py"].includes(lower)) {
5589
+ return "python";
5590
+ }
5591
+ if (["lldb", "codelldb", "cpp", "c", "rust"].includes(lower)) {
5592
+ return "lldb";
5593
+ }
5594
+ return lower;
5595
+ }
5596
+ function getBreakpointSuggestions(adapterType, fileExtension, adapterMessage) {
5597
+ const suggestions = [];
5598
+ const normalizedAdapter = normalizeAdapterType(adapterType);
5599
+ const ext = fileExtension.toLowerCase();
5600
+ const msgLower = (adapterMessage || "").toLowerCase();
5601
+ const isSourceMapIssue = msgLower.includes("source map") || msgLower.includes("sourcemap") || msgLower.includes("cannot find");
5602
+ const isPathIssue = msgLower.includes("not found") || msgLower.includes("cannot find file") || msgLower.includes("does not exist");
5603
+ if (normalizedAdapter === "node") {
5604
+ if (ext === ".ts" || ext === ".tsx") {
5605
+ suggestions.push('Ensure "sourceMap": true in tsconfig.json');
5606
+ suggestions.push("Rebuild with source maps: tsc --sourceMap (or your build command)");
5607
+ suggestions.push("Verify .map files exist in your output directory (e.g., dist/**/*.map)");
5608
+ suggestions.push(
5609
+ 'Check that "sourceMaps": true is set in your debug config and "outFiles" points to your built JS files'
5610
+ );
5611
+ } else if (ext === ".js" || ext === ".mjs" || ext === ".cjs") {
5612
+ suggestions.push(
5613
+ "Confirm you are setting the breakpoint in the executed file (built output vs source)"
5614
+ );
5615
+ if (isSourceMapIssue) {
5616
+ suggestions.push(
5617
+ "If using bundlers (webpack/esbuild/vite), ensure source maps are generated and accessible"
5618
+ );
5619
+ suggestions.push("Verify source map files (.map) are not excluded from your output");
5620
+ }
5621
+ }
5622
+ } else if (normalizedAdapter === "coreclr") {
5623
+ if (ext === ".cs") {
5624
+ suggestions.push("Build in Debug configuration to ensure PDB files are produced");
5625
+ suggestions.push("Clean and rebuild to refresh symbols: dotnet clean && dotnet build");
5626
+ suggestions.push(
5627
+ "Confirm the running binary matches the source version (stale builds can break mapping)"
5628
+ );
5629
+ suggestions.push("Ensure PDB files are deployed alongside the DLL");
5630
+ }
5631
+ } else if (normalizedAdapter === "python") {
5632
+ if (ext === ".py") {
5633
+ suggestions.push("Confirm the Python interpreter and working directory match your project");
5634
+ suggestions.push(
5635
+ "If debugging remotely or in containers, configure path mappings (localRoot/remoteRoot)"
5636
+ );
5637
+ suggestions.push(
5638
+ "Verify you are setting the breakpoint in code that actually executes (imported module vs different copy)"
5639
+ );
5640
+ }
5641
+ } else if (normalizedAdapter === "lldb") {
5642
+ if ([".c", ".cpp", ".cc", ".cxx", ".h", ".hpp", ".rs", ".m", ".mm"].includes(ext)) {
5643
+ suggestions.push("Compile with debug symbols (-g) and avoid stripping binaries");
5644
+ suggestions.push("Ensure the running binary matches the source used to compile");
5645
+ suggestions.push("For Rust, build with: cargo build (debug mode, not --release)");
5646
+ }
5647
+ }
5648
+ if (isPathIssue) {
5649
+ suggestions.push("Check the file path is correct and exists relative to the provided cwd");
5650
+ }
5651
+ if (suggestions.length === 0) {
5652
+ suggestions.push("Check the file path is correct and exists relative to the provided cwd");
5653
+ suggestions.push(
5654
+ "Ensure the running program corresponds to this source (no stale build artifacts)"
5655
+ );
5656
+ suggestions.push("Verify the file has been compiled/built with debug information");
5657
+ }
5658
+ return suggestions;
5659
+ }
5382
5660
 
5383
5661
  // src/session/variables.ts
5384
5662
  var BLOCKED_PROPERTIES = /* @__PURE__ */ new Set([
@@ -6171,7 +6449,7 @@ var DebugSession = class {
6171
6449
  command: this.config.adapter.command,
6172
6450
  args: this.config.adapter.args,
6173
6451
  cwd: this.config.cwd,
6174
- env: this.config.env,
6452
+ env: { ...this.config.adapter.env, ...this.config.env },
6175
6453
  port: this.config.adapter.socketPort,
6176
6454
  timeout: this.config.timeout
6177
6455
  });
@@ -6180,7 +6458,7 @@ var DebugSession = class {
6180
6458
  command: this.config.adapter.command,
6181
6459
  args: this.config.adapter.args,
6182
6460
  cwd: this.config.cwd,
6183
- env: this.config.env,
6461
+ env: { ...this.config.adapter.env, ...this.config.env },
6184
6462
  timeout: this.config.timeout
6185
6463
  });
6186
6464
  }
@@ -6192,7 +6470,8 @@ var DebugSession = class {
6192
6470
  });
6193
6471
  this.breakpointManager = new BreakpointManager(this.client, this.formatter, {
6194
6472
  cwd: this.config.cwd,
6195
- programPath: this.config.program
6473
+ programPath: this.config.program,
6474
+ adapterType: this.config.adapter.name
6196
6475
  });
6197
6476
  this.variableInspector = new VariableInspector(this.client, {
6198
6477
  compactServices: !this.config.expandServices,
@@ -6993,6 +7272,16 @@ function createCli() {
6993
7272
  console.error(`Available adapters: ${getAdapterNames().join(", ")}`);
6994
7273
  process.exit(1);
6995
7274
  }
7275
+ const breakpointErrors = validateAllBreakpoints(
7276
+ options.breakpoint || [],
7277
+ options.logpoint || []
7278
+ );
7279
+ if (breakpointErrors.length > 0) {
7280
+ for (const error of breakpointErrors) {
7281
+ console.error(`Error: ${error}`);
7282
+ }
7283
+ process.exit(1);
7284
+ }
6996
7285
  await runDebugSession({ ...options, program: programPath, adapter: options.adapter });
6997
7286
  }
6998
7287
  );
@@ -7027,6 +7316,13 @@ async function runTestDebugSession(options) {
7027
7316
  console.error('Example: -b "path/to/TestFile.cs:42"');
7028
7317
  process.exit(1);
7029
7318
  }
7319
+ const breakpointErrors = validateAllBreakpoints(options.breakpoint || [], options.logpoint || []);
7320
+ if (breakpointErrors.length > 0) {
7321
+ for (const error of breakpointErrors) {
7322
+ console.error(`Error: ${error}`);
7323
+ }
7324
+ process.exit(1);
7325
+ }
7030
7326
  console.error(`Starting test runner for: ${options.testProject}`);
7031
7327
  if (options.testFilter) {
7032
7328
  console.error(`Test filter: ${options.testFilter}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "debug-run",
3
- "version": "0.5.7",
3
+ "version": "0.5.8",
4
4
  "description": "CLI tool enabling AI agents to programmatically debug code via DAP",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",