debug-run 0.5.7 → 0.5.9
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.
- package/.claude/skills/debug-run/SKILL.md +19 -0
- package/README.md +60 -0
- package/dist/index.cjs +742 -69
- package/package.json +1 -1
|
@@ -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
|
@@ -143,6 +143,8 @@ Options:
|
|
|
143
143
|
--attach Attach to running process
|
|
144
144
|
--pid <id> Process ID to attach to
|
|
145
145
|
--env <key=value...> Environment variables
|
|
146
|
+
--compact Enable compact output for reduced token usage
|
|
147
|
+
--stack-limit <N> Max stack frames to include (default: 3 in compact)
|
|
146
148
|
|
|
147
149
|
Commands:
|
|
148
150
|
list-adapters List available debug adapters
|
|
@@ -178,6 +180,35 @@ debug-run outputs newline-delimited JSON (NDJSON) events:
|
|
|
178
180
|
{"type":"session_end","summary":{"breakpointsHit":1,"duration":1234}}
|
|
179
181
|
```
|
|
180
182
|
|
|
183
|
+
### Breakpoint Set Event (with Diagnostics)
|
|
184
|
+
|
|
185
|
+
When a breakpoint cannot be verified (e.g., source mapping issues, missing debug symbols), the event includes actionable diagnostics:
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"type": "breakpoint_set",
|
|
190
|
+
"id": 1,
|
|
191
|
+
"file": "src/handler.ts",
|
|
192
|
+
"line": 45,
|
|
193
|
+
"verified": false,
|
|
194
|
+
"message": "Could not resolve source location",
|
|
195
|
+
"diagnostics": {
|
|
196
|
+
"requestedFile": "src/handler.ts",
|
|
197
|
+
"requestedLine": 45,
|
|
198
|
+
"adapterMessage": "Could not resolve source location",
|
|
199
|
+
"suggestions": [
|
|
200
|
+
"Ensure \"sourceMap\": true in tsconfig.json",
|
|
201
|
+
"Rebuild with source maps: tsc --sourceMap (or your build command)",
|
|
202
|
+
"Verify .map files exist in your output directory (e.g., dist/**/*.map)"
|
|
203
|
+
],
|
|
204
|
+
"adapterType": "node",
|
|
205
|
+
"fileExtension": ".ts"
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Suggestions are context-aware based on the adapter and file type.
|
|
211
|
+
|
|
181
212
|
### Breakpoint Hit Event
|
|
182
213
|
|
|
183
214
|
```json
|
|
@@ -307,6 +338,35 @@ When an assertion fails, you get an `assertion_failed` event with:
|
|
|
307
338
|
|
|
308
339
|
Assertions are checked at breakpoints, during stepping, and during trace mode.
|
|
309
340
|
|
|
341
|
+
### Compact output mode (for AI agents)
|
|
342
|
+
|
|
343
|
+
Reduce token usage by 40-60% with compact output:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
npx debug-run ./app.dll \
|
|
347
|
+
-a dotnet \
|
|
348
|
+
-b "src/OrderService.cs:45" \
|
|
349
|
+
--compact \
|
|
350
|
+
--pretty
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Compact mode applies these optimizations:
|
|
354
|
+
- **Stack trace limiting**: Only top 3 frames by default (configurable with `--stack-limit`)
|
|
355
|
+
- **Internal frame filtering**: Removes node_modules, runtime internals, webpack frames
|
|
356
|
+
- **Path abbreviation**: `~/project/src/file.ts` instead of `/Users/name/project/src/file.ts`
|
|
357
|
+
- **Variable diffing**: On repeated breakpoint hits, only reports changed variables (`_diff` key)
|
|
358
|
+
- **Trace path collapsing**: Consecutive identical locations collapsed to `functionName (x5)`
|
|
359
|
+
|
|
360
|
+
Example compact output vs verbose:
|
|
361
|
+
|
|
362
|
+
```json
|
|
363
|
+
// Compact (~60 tokens)
|
|
364
|
+
{"type":"breakpoint_hit","location":{"file":".../src/OrderService.cs","line":45,"function":"ProcessOrder"},"stackTrace":[{"function":"ProcessOrder","file":".../src/OrderService.cs","line":45}],"locals":{"order":{"type":"OrderDto","value":{"Id":"abc-123","Total":150}}}}
|
|
365
|
+
|
|
366
|
+
// Verbose (~180 tokens)
|
|
367
|
+
{"type":"breakpoint_hit","timestamp":"2025-01-15T10:30:01.234Z","id":1,"threadId":1,"location":{"file":"/home/user/project/src/OrderService.cs","line":45,"column":12,"function":"ProcessOrder","module":"MyApp"},"stackTrace":[{"frameId":1,"function":"ProcessOrder","file":"/home/user/project/src/OrderService.cs","line":45,"column":12,"module":"MyApp"},{"frameId":2,"function":"Main","file":"/home/user/project/src/Program.cs","line":10,"column":5},...],"locals":{"order":{"type":"OrderDto","value":{"Id":"abc-123","Total":150,"CreatedAt":"2025-01-15T00:00:00Z","Status":"pending",...}},"this":{...}}}
|
|
368
|
+
```
|
|
369
|
+
|
|
310
370
|
### Trace mode (follow execution path)
|
|
311
371
|
|
|
312
372
|
Automatically step through code after hitting a breakpoint:
|
package/dist/index.cjs
CHANGED
|
@@ -3066,7 +3066,6 @@ var path = __toESM(require("node:path"), 1);
|
|
|
3066
3066
|
var os = __toESM(require("node:os"), 1);
|
|
3067
3067
|
var import_promises2 = require("node:stream/promises");
|
|
3068
3068
|
var import_node_stream = require("node:stream");
|
|
3069
|
-
var import_node_url = require("node:url");
|
|
3070
3069
|
var execAsync = (0, import_node_util.promisify)(import_node_child_process.exec);
|
|
3071
3070
|
var NETCOREDBG_VERSION = "3.1.3-1062";
|
|
3072
3071
|
var NETCOREDBG_BASE_URL = `https://github.com/Samsung/netcoredbg/releases/download/${NETCOREDBG_VERSION}`;
|
|
@@ -3119,10 +3118,14 @@ function getDownloadUrl() {
|
|
|
3119
3118
|
const filename = `netcoredbg-${info.os}-${info.arch}.${info.archiveExt}`;
|
|
3120
3119
|
return `${NETCOREDBG_BASE_URL}/${filename}`;
|
|
3121
3120
|
}
|
|
3121
|
+
function getDebugRunHome() {
|
|
3122
|
+
if (process.env.DEBUG_RUN_HOME) {
|
|
3123
|
+
return process.env.DEBUG_RUN_HOME;
|
|
3124
|
+
}
|
|
3125
|
+
return path.join(os.homedir(), ".debug-run");
|
|
3126
|
+
}
|
|
3122
3127
|
function getAdaptersDir() {
|
|
3123
|
-
|
|
3124
|
-
const packageRoot = path.resolve(path.dirname(currentFilePath), "..", "..");
|
|
3125
|
-
return path.join(packageRoot, "bin", "adapters");
|
|
3128
|
+
return path.join(getDebugRunHome(), "adapters");
|
|
3126
3129
|
}
|
|
3127
3130
|
function getNetcoredbgPath() {
|
|
3128
3131
|
const info = getPlatformInfo();
|
|
@@ -3144,6 +3147,9 @@ async function installNetcoredbg(onProgress) {
|
|
|
3144
3147
|
if (!response.ok) {
|
|
3145
3148
|
throw new Error(`Failed to download netcoredbg: ${response.statusText}`);
|
|
3146
3149
|
}
|
|
3150
|
+
if (!response.body) {
|
|
3151
|
+
throw new Error("Response body is null");
|
|
3152
|
+
}
|
|
3147
3153
|
const fileStream = (0, import_node_fs.createWriteStream)(archivePath);
|
|
3148
3154
|
await (0, import_promises2.pipeline)(import_node_stream.Readable.fromWeb(response.body), fileStream);
|
|
3149
3155
|
log("Extracting...");
|
|
@@ -3177,6 +3183,9 @@ async function installJsDebug(onProgress) {
|
|
|
3177
3183
|
if (!response.ok) {
|
|
3178
3184
|
throw new Error(`Failed to download js-debug: ${response.statusText}`);
|
|
3179
3185
|
}
|
|
3186
|
+
if (!response.body) {
|
|
3187
|
+
throw new Error("Response body is null");
|
|
3188
|
+
}
|
|
3180
3189
|
const fileStream = (0, import_node_fs.createWriteStream)(archivePath);
|
|
3181
3190
|
await (0, import_promises2.pipeline)(import_node_stream.Readable.fromWeb(response.body), fileStream);
|
|
3182
3191
|
log("Extracting...");
|
|
@@ -3294,7 +3303,7 @@ function findVsdbg() {
|
|
|
3294
3303
|
const arch3 = os2.arch();
|
|
3295
3304
|
const vsdbgDirs = [];
|
|
3296
3305
|
if (platform3 === "win32") {
|
|
3297
|
-
vsdbgDirs.push("vsdbg", "win32", "x64", "x86");
|
|
3306
|
+
vsdbgDirs.push("vsdbg", "win32", "x64", "x86", "x86_64");
|
|
3298
3307
|
} else if (platform3 === "darwin") {
|
|
3299
3308
|
vsdbgDirs.push(
|
|
3300
3309
|
arch3 === "arm64" ? "arm64" : "x86_64",
|
|
@@ -3356,6 +3365,13 @@ function findVsdbg() {
|
|
|
3356
3365
|
return null;
|
|
3357
3366
|
}
|
|
3358
3367
|
function findDebugpy() {
|
|
3368
|
+
const debugpyExt = findExtension("ms-python.debugpy");
|
|
3369
|
+
if (debugpyExt) {
|
|
3370
|
+
const debugpyPath = path3.join(debugpyExt, "bundled", "libs", "debugpy");
|
|
3371
|
+
if ((0, import_node_fs2.existsSync)(debugpyPath)) {
|
|
3372
|
+
return debugpyPath;
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3359
3375
|
const pythonExt = findExtension("ms-python.python");
|
|
3360
3376
|
if (pythonExt) {
|
|
3361
3377
|
const possiblePaths = [
|
|
@@ -3517,6 +3533,16 @@ var debugpyAdapter = {
|
|
|
3517
3533
|
get args() {
|
|
3518
3534
|
return ["-m", "debugpy.adapter"];
|
|
3519
3535
|
},
|
|
3536
|
+
get env() {
|
|
3537
|
+
if (cachedSource === "vscode" && cachedDebugpyPath) {
|
|
3538
|
+
const debugpyParentDir = path5.dirname(cachedDebugpyPath);
|
|
3539
|
+
const existingPythonPath = process.env.PYTHONPATH || "";
|
|
3540
|
+
return {
|
|
3541
|
+
PYTHONPATH: existingPythonPath ? `${debugpyParentDir}:${existingPythonPath}` : debugpyParentDir
|
|
3542
|
+
};
|
|
3543
|
+
}
|
|
3544
|
+
return void 0;
|
|
3545
|
+
},
|
|
3520
3546
|
detect: async () => {
|
|
3521
3547
|
const pythonCmd = await findPythonCommand();
|
|
3522
3548
|
if (!pythonCmd) {
|
|
@@ -3957,7 +3983,7 @@ var DapTransport = class extends import_node_events.EventEmitter {
|
|
|
3957
3983
|
command,
|
|
3958
3984
|
arguments: args
|
|
3959
3985
|
};
|
|
3960
|
-
return new Promise((
|
|
3986
|
+
return new Promise((resolve9, reject) => {
|
|
3961
3987
|
const timeout = setTimeout(() => {
|
|
3962
3988
|
this.pendingRequests.delete(seq);
|
|
3963
3989
|
reject(new Error(`Request '${command}' timed out after ${this.requestTimeout}ms`));
|
|
@@ -3966,7 +3992,7 @@ var DapTransport = class extends import_node_events.EventEmitter {
|
|
|
3966
3992
|
resolve: (response) => {
|
|
3967
3993
|
clearTimeout(timeout);
|
|
3968
3994
|
if (response.success) {
|
|
3969
|
-
|
|
3995
|
+
resolve9(response.body);
|
|
3970
3996
|
} else {
|
|
3971
3997
|
reject(new Error(response.message || `Request '${command}' failed`));
|
|
3972
3998
|
}
|
|
@@ -4272,8 +4298,8 @@ var DapClient = class extends import_node_events2.EventEmitter {
|
|
|
4272
4298
|
async initialize(args = {}) {
|
|
4273
4299
|
this.ensureConnected();
|
|
4274
4300
|
let initializedResolve;
|
|
4275
|
-
const initializedPromise = new Promise((
|
|
4276
|
-
initializedResolve =
|
|
4301
|
+
const initializedPromise = new Promise((resolve9) => {
|
|
4302
|
+
initializedResolve = resolve9;
|
|
4277
4303
|
});
|
|
4278
4304
|
this.once("initialized", () => initializedResolve());
|
|
4279
4305
|
const response = await this.transport.sendRequest("initialize", {
|
|
@@ -4291,7 +4317,7 @@ var DapClient = class extends import_node_events2.EventEmitter {
|
|
|
4291
4317
|
});
|
|
4292
4318
|
this.capabilities = response?.capabilities || response || {};
|
|
4293
4319
|
const timeoutPromise = new Promise((_, reject) => {
|
|
4294
|
-
setTimeout(() => reject(new Error("Timeout waiting for initialized event")),
|
|
4320
|
+
setTimeout(() => reject(new Error("Timeout waiting for initialized event")), 500);
|
|
4295
4321
|
});
|
|
4296
4322
|
try {
|
|
4297
4323
|
await Promise.race([initializedPromise, timeoutPromise]);
|
|
@@ -4484,7 +4510,7 @@ var SocketDapTransport = class extends import_node_events3.EventEmitter {
|
|
|
4484
4510
|
* Connect to the DAP server
|
|
4485
4511
|
*/
|
|
4486
4512
|
async connect() {
|
|
4487
|
-
return new Promise((
|
|
4513
|
+
return new Promise((resolve9, reject) => {
|
|
4488
4514
|
this.socket = (0, import_node_net.connect)({
|
|
4489
4515
|
host: this.options.host,
|
|
4490
4516
|
port: this.options.port
|
|
@@ -4496,7 +4522,7 @@ var SocketDapTransport = class extends import_node_events3.EventEmitter {
|
|
|
4496
4522
|
const onConnect = () => {
|
|
4497
4523
|
this.socket?.removeListener("error", onError);
|
|
4498
4524
|
this.setupSocket();
|
|
4499
|
-
|
|
4525
|
+
resolve9();
|
|
4500
4526
|
};
|
|
4501
4527
|
this.socket.once("error", onError);
|
|
4502
4528
|
this.socket.once("connect", onConnect);
|
|
@@ -4530,7 +4556,7 @@ var SocketDapTransport = class extends import_node_events3.EventEmitter {
|
|
|
4530
4556
|
command,
|
|
4531
4557
|
arguments: args
|
|
4532
4558
|
};
|
|
4533
|
-
return new Promise((
|
|
4559
|
+
return new Promise((resolve9, reject) => {
|
|
4534
4560
|
const timeout = setTimeout(() => {
|
|
4535
4561
|
this.pendingRequests.delete(seq);
|
|
4536
4562
|
reject(new Error(`Request '${command}' timed out after ${this.requestTimeout}ms`));
|
|
@@ -4539,7 +4565,7 @@ var SocketDapTransport = class extends import_node_events3.EventEmitter {
|
|
|
4539
4565
|
resolve: (response) => {
|
|
4540
4566
|
clearTimeout(timeout);
|
|
4541
4567
|
if (response.success) {
|
|
4542
|
-
|
|
4568
|
+
resolve9(response.body);
|
|
4543
4569
|
} else {
|
|
4544
4570
|
reject(new Error(response.message || `Request '${command}' failed`));
|
|
4545
4571
|
}
|
|
@@ -4721,7 +4747,7 @@ var SocketDapClient = class extends import_node_events4.EventEmitter {
|
|
|
4721
4747
|
this.emit("serverError", error);
|
|
4722
4748
|
});
|
|
4723
4749
|
const delay = this.options.connectDelay ?? 1e3;
|
|
4724
|
-
await new Promise((
|
|
4750
|
+
await new Promise((resolve9) => setTimeout(resolve9, delay));
|
|
4725
4751
|
this.transport = new SocketDapTransport({
|
|
4726
4752
|
host: this.options.host || "localhost",
|
|
4727
4753
|
port: this.options.port,
|
|
@@ -5060,20 +5086,20 @@ var SocketDapClient = class extends import_node_events4.EventEmitter {
|
|
|
5060
5086
|
if (process.env.DEBUG_DAP) {
|
|
5061
5087
|
console.error("[DAP child] Initialize response received, waiting for initialized event...");
|
|
5062
5088
|
}
|
|
5063
|
-
await new Promise((
|
|
5089
|
+
await new Promise((resolve9) => {
|
|
5064
5090
|
let resolved = false;
|
|
5065
5091
|
const onInitialized = () => {
|
|
5066
5092
|
if (resolved) return;
|
|
5067
5093
|
resolved = true;
|
|
5068
5094
|
this.childTransport?.removeListener("event:initialized", onInitialized);
|
|
5069
|
-
|
|
5095
|
+
resolve9();
|
|
5070
5096
|
};
|
|
5071
5097
|
this.childTransport.on("event:initialized", onInitialized);
|
|
5072
5098
|
setTimeout(() => {
|
|
5073
5099
|
if (resolved) return;
|
|
5074
5100
|
resolved = true;
|
|
5075
5101
|
this.childTransport?.removeListener("event:initialized", onInitialized);
|
|
5076
|
-
|
|
5102
|
+
resolve9();
|
|
5077
5103
|
}, 500);
|
|
5078
5104
|
});
|
|
5079
5105
|
if (process.env.DEBUG_DAP) {
|
|
@@ -5112,16 +5138,38 @@ var SocketDapClient = class extends import_node_events4.EventEmitter {
|
|
|
5112
5138
|
};
|
|
5113
5139
|
|
|
5114
5140
|
// src/output/formatter.ts
|
|
5141
|
+
var INTERNAL_PATTERNS = [
|
|
5142
|
+
/^node:/,
|
|
5143
|
+
/node_modules/,
|
|
5144
|
+
/internal\//,
|
|
5145
|
+
/<anonymous>/,
|
|
5146
|
+
/^native /,
|
|
5147
|
+
/\[native code\]/,
|
|
5148
|
+
/webpack:/,
|
|
5149
|
+
/^async /,
|
|
5150
|
+
/^Module\./,
|
|
5151
|
+
/processTicksAndRejections/,
|
|
5152
|
+
/^RunMain$/,
|
|
5153
|
+
/^bootstrap_node\.js$/,
|
|
5154
|
+
/^_compile$/,
|
|
5155
|
+
/^\.js$/
|
|
5156
|
+
];
|
|
5115
5157
|
var OutputFormatter = class {
|
|
5116
5158
|
stream;
|
|
5117
5159
|
pretty;
|
|
5118
5160
|
include;
|
|
5119
5161
|
exclude;
|
|
5162
|
+
compact;
|
|
5163
|
+
stackLimit;
|
|
5164
|
+
/** Track previous locals for variable diffing in compact mode */
|
|
5165
|
+
previousLocals = {};
|
|
5120
5166
|
constructor(options = {}) {
|
|
5121
5167
|
this.stream = options.stream ?? process.stdout;
|
|
5122
5168
|
this.pretty = options.pretty ?? false;
|
|
5123
5169
|
this.include = options.include ? new Set(options.include) : void 0;
|
|
5124
5170
|
this.exclude = options.exclude ? new Set(options.exclude) : void 0;
|
|
5171
|
+
this.compact = options.compact ?? false;
|
|
5172
|
+
this.stackLimit = options.stackLimit ?? (options.compact ? 3 : Infinity);
|
|
5125
5173
|
}
|
|
5126
5174
|
/**
|
|
5127
5175
|
* Check if an event type should be emitted based on include/exclude filters
|
|
@@ -5142,9 +5190,248 @@ var OutputFormatter = class {
|
|
|
5142
5190
|
if (!this.shouldEmit(event.type)) {
|
|
5143
5191
|
return;
|
|
5144
5192
|
}
|
|
5145
|
-
const
|
|
5193
|
+
const outputEvent = this.compact ? this.compactifyEvent(event) : event;
|
|
5194
|
+
const json = this.pretty ? JSON.stringify(outputEvent, null, 2) : JSON.stringify(outputEvent);
|
|
5146
5195
|
this.stream.write(json + "\n");
|
|
5147
5196
|
}
|
|
5197
|
+
/**
|
|
5198
|
+
* Check if a stack frame is an internal/runtime frame
|
|
5199
|
+
*/
|
|
5200
|
+
isInternalFrame(frame) {
|
|
5201
|
+
const file = frame.file ?? "";
|
|
5202
|
+
const fn = frame.function ?? "";
|
|
5203
|
+
return INTERNAL_PATTERNS.some((pattern) => pattern.test(file) || pattern.test(fn));
|
|
5204
|
+
}
|
|
5205
|
+
/**
|
|
5206
|
+
* Filter and limit stack frames for compact output
|
|
5207
|
+
*/
|
|
5208
|
+
compactifyStackTrace(frames) {
|
|
5209
|
+
const userFrames = frames.filter((frame) => !this.isInternalFrame(frame));
|
|
5210
|
+
const relevantFrames = userFrames.length > 0 ? userFrames : frames.slice(0, 1);
|
|
5211
|
+
const limitedFrames = relevantFrames.slice(0, this.stackLimit);
|
|
5212
|
+
return limitedFrames.map((frame) => ({
|
|
5213
|
+
...frame,
|
|
5214
|
+
file: frame.file ? this.abbreviatePath(frame.file) : null
|
|
5215
|
+
}));
|
|
5216
|
+
}
|
|
5217
|
+
/**
|
|
5218
|
+
* Abbreviate a file path for compact output
|
|
5219
|
+
*/
|
|
5220
|
+
abbreviatePath(filePath) {
|
|
5221
|
+
let abbreviated = filePath;
|
|
5222
|
+
const nodeModulesMatch = abbreviated.match(/node_modules\/(.+)/);
|
|
5223
|
+
if (nodeModulesMatch) {
|
|
5224
|
+
const modulePath = nodeModulesMatch[1];
|
|
5225
|
+
const moduleSegments = modulePath.split("/");
|
|
5226
|
+
if (moduleSegments.length > 3) {
|
|
5227
|
+
abbreviated = `<node_modules>/${moduleSegments.slice(0, 2).join("/")}/...`;
|
|
5228
|
+
} else {
|
|
5229
|
+
abbreviated = `<node_modules>/${modulePath}`;
|
|
5230
|
+
}
|
|
5231
|
+
return abbreviated;
|
|
5232
|
+
}
|
|
5233
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
5234
|
+
if (homeDir && abbreviated.startsWith(homeDir)) {
|
|
5235
|
+
abbreviated = "~" + abbreviated.slice(homeDir.length);
|
|
5236
|
+
}
|
|
5237
|
+
const segments = abbreviated.split("/");
|
|
5238
|
+
if (segments.length > 4) {
|
|
5239
|
+
abbreviated = ".../" + segments.slice(-3).join("/");
|
|
5240
|
+
}
|
|
5241
|
+
return abbreviated;
|
|
5242
|
+
}
|
|
5243
|
+
/**
|
|
5244
|
+
* Abbreviate a source location for compact output
|
|
5245
|
+
*/
|
|
5246
|
+
compactifyLocation(location) {
|
|
5247
|
+
return {
|
|
5248
|
+
...location,
|
|
5249
|
+
file: this.abbreviatePath(location.file),
|
|
5250
|
+
// Remove module in compact mode as it's often redundant
|
|
5251
|
+
module: void 0
|
|
5252
|
+
};
|
|
5253
|
+
}
|
|
5254
|
+
/**
|
|
5255
|
+
* Compact locals by computing diff from previous state
|
|
5256
|
+
* Returns only changed variables
|
|
5257
|
+
*/
|
|
5258
|
+
compactifyLocals(locals) {
|
|
5259
|
+
const isFirstCapture = Object.keys(this.previousLocals).length === 0;
|
|
5260
|
+
if (isFirstCapture) {
|
|
5261
|
+
this.previousLocals = { ...locals };
|
|
5262
|
+
return this.abbreviateLocals(locals);
|
|
5263
|
+
}
|
|
5264
|
+
const diff = {};
|
|
5265
|
+
let hasChanges = false;
|
|
5266
|
+
for (const [name, value] of Object.entries(locals)) {
|
|
5267
|
+
const previousValue = this.previousLocals[name];
|
|
5268
|
+
const currentJson = JSON.stringify(value);
|
|
5269
|
+
const previousJson = previousValue !== void 0 ? JSON.stringify(previousValue) : void 0;
|
|
5270
|
+
if (currentJson !== previousJson) {
|
|
5271
|
+
diff[name] = value;
|
|
5272
|
+
hasChanges = true;
|
|
5273
|
+
}
|
|
5274
|
+
}
|
|
5275
|
+
for (const name of Object.keys(this.previousLocals)) {
|
|
5276
|
+
if (!(name in locals)) {
|
|
5277
|
+
hasChanges = true;
|
|
5278
|
+
diff[name] = { type: "deleted", value: null };
|
|
5279
|
+
}
|
|
5280
|
+
}
|
|
5281
|
+
this.previousLocals = { ...locals };
|
|
5282
|
+
if (hasChanges) {
|
|
5283
|
+
return { _diff: this.abbreviateLocals(diff) };
|
|
5284
|
+
}
|
|
5285
|
+
return {};
|
|
5286
|
+
}
|
|
5287
|
+
/**
|
|
5288
|
+
* Abbreviate local variable values for compact output
|
|
5289
|
+
*/
|
|
5290
|
+
abbreviateLocals(locals) {
|
|
5291
|
+
const abbreviated = {};
|
|
5292
|
+
for (const [name, value] of Object.entries(locals)) {
|
|
5293
|
+
abbreviated[name] = this.abbreviateValue(value);
|
|
5294
|
+
}
|
|
5295
|
+
return abbreviated;
|
|
5296
|
+
}
|
|
5297
|
+
/**
|
|
5298
|
+
* Abbreviate a single variable value for compact output
|
|
5299
|
+
*/
|
|
5300
|
+
abbreviateValue(value) {
|
|
5301
|
+
if (typeof value.value === "string" && value.value.length > 100) {
|
|
5302
|
+
return {
|
|
5303
|
+
...value,
|
|
5304
|
+
value: value.value.slice(0, 100) + "..."
|
|
5305
|
+
};
|
|
5306
|
+
}
|
|
5307
|
+
if (Array.isArray(value.value) && value.value.length > 5) {
|
|
5308
|
+
const abbreviated = value.value.slice(0, 3);
|
|
5309
|
+
return {
|
|
5310
|
+
...value,
|
|
5311
|
+
value: `[${abbreviated.join(", ")}, ... (${value.value.length} items)]`
|
|
5312
|
+
};
|
|
5313
|
+
}
|
|
5314
|
+
return value;
|
|
5315
|
+
}
|
|
5316
|
+
/**
|
|
5317
|
+
* Apply compact transformations to an event
|
|
5318
|
+
*/
|
|
5319
|
+
compactifyEvent(event) {
|
|
5320
|
+
switch (event.type) {
|
|
5321
|
+
case "breakpoint_hit": {
|
|
5322
|
+
const compacted = {
|
|
5323
|
+
...event,
|
|
5324
|
+
location: this.compactifyLocation(event.location),
|
|
5325
|
+
stackTrace: this.compactifyStackTrace(event.stackTrace)
|
|
5326
|
+
};
|
|
5327
|
+
if (event.locals && Object.keys(event.locals).length > 0) {
|
|
5328
|
+
const compactedLocals = this.compactifyLocals(event.locals);
|
|
5329
|
+
compacted.locals = compactedLocals;
|
|
5330
|
+
}
|
|
5331
|
+
return compacted;
|
|
5332
|
+
}
|
|
5333
|
+
case "exception_thrown": {
|
|
5334
|
+
const compacted = {
|
|
5335
|
+
...event,
|
|
5336
|
+
location: this.compactifyLocation(event.location)
|
|
5337
|
+
};
|
|
5338
|
+
if (event.exception.stackTrace) {
|
|
5339
|
+
compacted.exception = {
|
|
5340
|
+
...event.exception,
|
|
5341
|
+
stackTrace: this.abbreviateStackTraceString(event.exception.stackTrace)
|
|
5342
|
+
};
|
|
5343
|
+
}
|
|
5344
|
+
if (event.locals && Object.keys(event.locals).length > 0) {
|
|
5345
|
+
const compactedLocals = this.compactifyLocals(event.locals);
|
|
5346
|
+
compacted.locals = compactedLocals;
|
|
5347
|
+
}
|
|
5348
|
+
return compacted;
|
|
5349
|
+
}
|
|
5350
|
+
case "step_completed": {
|
|
5351
|
+
return {
|
|
5352
|
+
...event,
|
|
5353
|
+
location: this.compactifyLocation(event.location),
|
|
5354
|
+
stackTrace: this.compactifyStackTrace(event.stackTrace),
|
|
5355
|
+
locals: this.abbreviateLocals(event.locals)
|
|
5356
|
+
};
|
|
5357
|
+
}
|
|
5358
|
+
case "trace_step": {
|
|
5359
|
+
return {
|
|
5360
|
+
...event,
|
|
5361
|
+
location: this.compactifyLocation(event.location)
|
|
5362
|
+
};
|
|
5363
|
+
}
|
|
5364
|
+
case "trace_completed": {
|
|
5365
|
+
const compactedPath = event.path.map((loc) => this.compactifyLocation(loc));
|
|
5366
|
+
const collapsedPath = [];
|
|
5367
|
+
let repeatCount = 1;
|
|
5368
|
+
for (let i = 0; i < compactedPath.length; i++) {
|
|
5369
|
+
const current = compactedPath[i];
|
|
5370
|
+
const next = compactedPath[i + 1];
|
|
5371
|
+
if (next && current.file === next.file && current.line === next.line && current.function === next.function) {
|
|
5372
|
+
repeatCount++;
|
|
5373
|
+
} else {
|
|
5374
|
+
if (repeatCount > 1) {
|
|
5375
|
+
collapsedPath.push({
|
|
5376
|
+
...current,
|
|
5377
|
+
function: `${current.function} (x${repeatCount})`
|
|
5378
|
+
});
|
|
5379
|
+
} else {
|
|
5380
|
+
collapsedPath.push(current);
|
|
5381
|
+
}
|
|
5382
|
+
repeatCount = 1;
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
5385
|
+
return {
|
|
5386
|
+
...event,
|
|
5387
|
+
path: collapsedPath,
|
|
5388
|
+
finalLocation: this.compactifyLocation(event.finalLocation),
|
|
5389
|
+
stackTrace: this.compactifyStackTrace(event.stackTrace),
|
|
5390
|
+
locals: this.abbreviateLocals(event.locals)
|
|
5391
|
+
};
|
|
5392
|
+
}
|
|
5393
|
+
case "assertion_failed": {
|
|
5394
|
+
return {
|
|
5395
|
+
...event,
|
|
5396
|
+
location: this.compactifyLocation(event.location),
|
|
5397
|
+
stackTrace: this.compactifyStackTrace(event.stackTrace),
|
|
5398
|
+
locals: this.abbreviateLocals(event.locals)
|
|
5399
|
+
};
|
|
5400
|
+
}
|
|
5401
|
+
case "session_start": {
|
|
5402
|
+
const compacted = { ...event };
|
|
5403
|
+
if (compacted.program) {
|
|
5404
|
+
compacted.program = this.abbreviatePath(compacted.program);
|
|
5405
|
+
}
|
|
5406
|
+
if (compacted.cwd) {
|
|
5407
|
+
compacted.cwd = this.abbreviatePath(compacted.cwd);
|
|
5408
|
+
}
|
|
5409
|
+
return compacted;
|
|
5410
|
+
}
|
|
5411
|
+
default:
|
|
5412
|
+
return event;
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
/**
|
|
5416
|
+
* Abbreviate a stack trace string (from exception)
|
|
5417
|
+
*/
|
|
5418
|
+
abbreviateStackTraceString(stackTrace) {
|
|
5419
|
+
const lines = stackTrace.split("\n");
|
|
5420
|
+
const userLines = lines.filter((line) => {
|
|
5421
|
+
return !INTERNAL_PATTERNS.some((pattern) => pattern.test(line));
|
|
5422
|
+
});
|
|
5423
|
+
const limitedLines = userLines.slice(0, this.stackLimit);
|
|
5424
|
+
return limitedLines.map((line) => this.abbreviatePathsInLine(line)).join("\n");
|
|
5425
|
+
}
|
|
5426
|
+
/**
|
|
5427
|
+
* Abbreviate file paths within a line of text
|
|
5428
|
+
*/
|
|
5429
|
+
abbreviatePathsInLine(line) {
|
|
5430
|
+
return line.replace(/(?:at\s+)?(\/[^\s:]+)/g, (match, path12) => {
|
|
5431
|
+
const abbreviated = this.abbreviatePath(path12);
|
|
5432
|
+
return match.replace(path12, abbreviated);
|
|
5433
|
+
});
|
|
5434
|
+
}
|
|
5148
5435
|
/**
|
|
5149
5436
|
* Create an event with timestamp
|
|
5150
5437
|
*/
|
|
@@ -5189,7 +5476,7 @@ var OutputFormatter = class {
|
|
|
5189
5476
|
/**
|
|
5190
5477
|
* Emit a breakpoint_set event
|
|
5191
5478
|
*/
|
|
5192
|
-
breakpointSet(id, file, line, verified, condition, message) {
|
|
5479
|
+
breakpointSet(id, file, line, verified, condition, message, diagnostics) {
|
|
5193
5480
|
this.emit(
|
|
5194
5481
|
this.createEvent("breakpoint_set", {
|
|
5195
5482
|
id,
|
|
@@ -5197,7 +5484,8 @@ var OutputFormatter = class {
|
|
|
5197
5484
|
line,
|
|
5198
5485
|
verified,
|
|
5199
5486
|
condition,
|
|
5200
|
-
message
|
|
5487
|
+
message,
|
|
5488
|
+
diagnostics
|
|
5201
5489
|
})
|
|
5202
5490
|
);
|
|
5203
5491
|
}
|
|
@@ -5224,12 +5512,161 @@ function resolveBreakpointPath(file, options = {}) {
|
|
|
5224
5512
|
if (options.cwd) {
|
|
5225
5513
|
return path10.resolve(options.cwd, file);
|
|
5226
5514
|
}
|
|
5227
|
-
if (options.programPath) {
|
|
5228
|
-
const programDir = path10.dirname(options.programPath);
|
|
5229
|
-
return path10.resolve(programDir, file);
|
|
5230
|
-
}
|
|
5231
5515
|
return path10.resolve(file);
|
|
5232
5516
|
}
|
|
5517
|
+
function validateBreakpointSpec(spec) {
|
|
5518
|
+
const trimmed = spec.trim();
|
|
5519
|
+
if (!trimmed) {
|
|
5520
|
+
return {
|
|
5521
|
+
valid: false,
|
|
5522
|
+
error: "Breakpoint specification cannot be empty",
|
|
5523
|
+
spec
|
|
5524
|
+
};
|
|
5525
|
+
}
|
|
5526
|
+
if (!trimmed.includes(":")) {
|
|
5527
|
+
return {
|
|
5528
|
+
valid: false,
|
|
5529
|
+
error: `Invalid breakpoint format "${spec}". Expected "file:line" (e.g., "Program.cs:42")`,
|
|
5530
|
+
spec
|
|
5531
|
+
};
|
|
5532
|
+
}
|
|
5533
|
+
const match = trimmed.match(/^(.+):(\d+)(?:\?(.+)|#(\d+))?$/);
|
|
5534
|
+
if (!match) {
|
|
5535
|
+
const colonIdx = trimmed.lastIndexOf(":");
|
|
5536
|
+
if (colonIdx !== -1) {
|
|
5537
|
+
const linePartRaw = trimmed.slice(colonIdx + 1);
|
|
5538
|
+
const linePart = linePartRaw.split("?")[0].split("#")[0];
|
|
5539
|
+
if (linePart === "") {
|
|
5540
|
+
return {
|
|
5541
|
+
valid: false,
|
|
5542
|
+
error: `Missing line number in breakpoint "${spec}". Expected "file:line" (e.g., "Program.cs:42")`,
|
|
5543
|
+
spec
|
|
5544
|
+
};
|
|
5545
|
+
}
|
|
5546
|
+
if (!/^\d+$/.test(linePart)) {
|
|
5547
|
+
return {
|
|
5548
|
+
valid: false,
|
|
5549
|
+
error: `Invalid line number "${linePart}" in breakpoint "${spec}". Line must be a positive integer`,
|
|
5550
|
+
spec
|
|
5551
|
+
};
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
return {
|
|
5555
|
+
valid: false,
|
|
5556
|
+
error: `Invalid breakpoint format "${spec}". Expected "file:line" (e.g., "Program.cs:42")`,
|
|
5557
|
+
spec
|
|
5558
|
+
};
|
|
5559
|
+
}
|
|
5560
|
+
const [, file, lineStr] = match;
|
|
5561
|
+
const line = parseInt(lineStr, 10);
|
|
5562
|
+
if (!file || !file.trim()) {
|
|
5563
|
+
return {
|
|
5564
|
+
valid: false,
|
|
5565
|
+
error: `Missing file path in breakpoint "${spec}". Expected "file:line" (e.g., "Program.cs:42")`,
|
|
5566
|
+
spec
|
|
5567
|
+
};
|
|
5568
|
+
}
|
|
5569
|
+
if (isNaN(line) || line < 1) {
|
|
5570
|
+
return {
|
|
5571
|
+
valid: false,
|
|
5572
|
+
error: `Invalid line number "${lineStr}" in breakpoint "${spec}". Line must be a positive integer`,
|
|
5573
|
+
spec
|
|
5574
|
+
};
|
|
5575
|
+
}
|
|
5576
|
+
return { valid: true };
|
|
5577
|
+
}
|
|
5578
|
+
function validateLogpointSpec(spec) {
|
|
5579
|
+
const trimmed = spec.trim();
|
|
5580
|
+
if (!trimmed) {
|
|
5581
|
+
return {
|
|
5582
|
+
valid: false,
|
|
5583
|
+
error: "Logpoint specification cannot be empty",
|
|
5584
|
+
spec
|
|
5585
|
+
};
|
|
5586
|
+
}
|
|
5587
|
+
if (!trimmed.includes("|")) {
|
|
5588
|
+
return {
|
|
5589
|
+
valid: false,
|
|
5590
|
+
error: `Invalid logpoint format "${spec}". Expected "file:line|message" (e.g., "Program.cs:42|value is {x}")`,
|
|
5591
|
+
spec
|
|
5592
|
+
};
|
|
5593
|
+
}
|
|
5594
|
+
const pipeIdx = trimmed.indexOf("|");
|
|
5595
|
+
const beforePipe = trimmed.slice(0, pipeIdx);
|
|
5596
|
+
if (!beforePipe.includes(":")) {
|
|
5597
|
+
return {
|
|
5598
|
+
valid: false,
|
|
5599
|
+
error: `Invalid logpoint format "${spec}". Expected "file:line|message" (e.g., "Program.cs:42|value is {x}")`,
|
|
5600
|
+
spec
|
|
5601
|
+
};
|
|
5602
|
+
}
|
|
5603
|
+
const match = trimmed.match(/^(.+):(\d+)\|(.+)$/);
|
|
5604
|
+
if (!match) {
|
|
5605
|
+
const colonIdx = beforePipe.lastIndexOf(":");
|
|
5606
|
+
if (colonIdx !== -1) {
|
|
5607
|
+
const linePart = beforePipe.slice(colonIdx + 1);
|
|
5608
|
+
if (linePart === "") {
|
|
5609
|
+
return {
|
|
5610
|
+
valid: false,
|
|
5611
|
+
error: `Missing line number in logpoint "${spec}". Expected "file:line|message"`,
|
|
5612
|
+
spec
|
|
5613
|
+
};
|
|
5614
|
+
}
|
|
5615
|
+
if (!/^\d+$/.test(linePart)) {
|
|
5616
|
+
return {
|
|
5617
|
+
valid: false,
|
|
5618
|
+
error: `Invalid line number "${linePart}" in logpoint "${spec}". Line must be a positive integer`,
|
|
5619
|
+
spec
|
|
5620
|
+
};
|
|
5621
|
+
}
|
|
5622
|
+
}
|
|
5623
|
+
return {
|
|
5624
|
+
valid: false,
|
|
5625
|
+
error: `Invalid logpoint format "${spec}". Expected "file:line|message" (e.g., "Program.cs:42|value is {x}")`,
|
|
5626
|
+
spec
|
|
5627
|
+
};
|
|
5628
|
+
}
|
|
5629
|
+
const [, file, lineStr, message] = match;
|
|
5630
|
+
const line = parseInt(lineStr, 10);
|
|
5631
|
+
if (!file || !file.trim()) {
|
|
5632
|
+
return {
|
|
5633
|
+
valid: false,
|
|
5634
|
+
error: `Missing file path in logpoint "${spec}". Expected "file:line|message"`,
|
|
5635
|
+
spec
|
|
5636
|
+
};
|
|
5637
|
+
}
|
|
5638
|
+
if (isNaN(line) || line < 1) {
|
|
5639
|
+
return {
|
|
5640
|
+
valid: false,
|
|
5641
|
+
error: `Invalid line number "${lineStr}" in logpoint "${spec}". Line must be a positive integer`,
|
|
5642
|
+
spec
|
|
5643
|
+
};
|
|
5644
|
+
}
|
|
5645
|
+
if (!message || !message.trim()) {
|
|
5646
|
+
return {
|
|
5647
|
+
valid: false,
|
|
5648
|
+
error: `Missing log message in logpoint "${spec}". Expected "file:line|message"`,
|
|
5649
|
+
spec
|
|
5650
|
+
};
|
|
5651
|
+
}
|
|
5652
|
+
return { valid: true };
|
|
5653
|
+
}
|
|
5654
|
+
function validateAllBreakpoints(breakpoints, logpoints = []) {
|
|
5655
|
+
const errors = [];
|
|
5656
|
+
for (const bp of breakpoints) {
|
|
5657
|
+
const result = validateBreakpointSpec(bp);
|
|
5658
|
+
if (!result.valid && result.error) {
|
|
5659
|
+
errors.push(result.error);
|
|
5660
|
+
}
|
|
5661
|
+
}
|
|
5662
|
+
for (const lp of logpoints) {
|
|
5663
|
+
const result = validateLogpointSpec(lp);
|
|
5664
|
+
if (!result.valid && result.error) {
|
|
5665
|
+
errors.push(result.error);
|
|
5666
|
+
}
|
|
5667
|
+
}
|
|
5668
|
+
return errors;
|
|
5669
|
+
}
|
|
5233
5670
|
function parseBreakpointSpec(spec, pathOptions = {}) {
|
|
5234
5671
|
const match = spec.match(/^(.+):(\d+)(?:\?(.+)|#(\d+))?$/);
|
|
5235
5672
|
if (!match) {
|
|
@@ -5271,10 +5708,12 @@ var BreakpointManager = class {
|
|
|
5271
5708
|
breakpoints = /* @__PURE__ */ new Map();
|
|
5272
5709
|
nextId = 1;
|
|
5273
5710
|
pathOptions;
|
|
5274
|
-
|
|
5711
|
+
adapterType;
|
|
5712
|
+
constructor(client, formatter, options = {}) {
|
|
5275
5713
|
this.client = client;
|
|
5276
5714
|
this.formatter = formatter;
|
|
5277
|
-
this.pathOptions =
|
|
5715
|
+
this.pathOptions = options;
|
|
5716
|
+
this.adapterType = options.adapterType;
|
|
5278
5717
|
}
|
|
5279
5718
|
/**
|
|
5280
5719
|
* Add a breakpoint from a spec string
|
|
@@ -5321,6 +5760,7 @@ var BreakpointManager = class {
|
|
|
5321
5760
|
hitCondition: spec.hitCondition,
|
|
5322
5761
|
logMessage: spec.logMessage
|
|
5323
5762
|
}));
|
|
5763
|
+
const requestedLines = specs.map((spec) => spec.line);
|
|
5324
5764
|
try {
|
|
5325
5765
|
const response = await this.client.setBreakpoints({
|
|
5326
5766
|
source: { path: file },
|
|
@@ -5333,22 +5773,34 @@ var BreakpointManager = class {
|
|
|
5333
5773
|
specs[i].verified = bp.verified;
|
|
5334
5774
|
specs[i].message = bp.message;
|
|
5335
5775
|
specs[i].line = bp.line ?? specs[i].line;
|
|
5776
|
+
const diagnostics = !bp.verified ? this.generateDiagnostics(file, requestedLines[i], bp.message) : void 0;
|
|
5336
5777
|
this.formatter.breakpointSet(
|
|
5337
5778
|
specs[i].id,
|
|
5338
5779
|
file,
|
|
5339
5780
|
specs[i].line,
|
|
5340
5781
|
specs[i].verified,
|
|
5341
5782
|
specs[i].condition,
|
|
5342
|
-
specs[i].message
|
|
5783
|
+
specs[i].message,
|
|
5784
|
+
diagnostics
|
|
5343
5785
|
);
|
|
5344
5786
|
}
|
|
5345
5787
|
}
|
|
5346
5788
|
} catch (error) {
|
|
5347
|
-
for (
|
|
5789
|
+
for (let i = 0; i < specs.length; i++) {
|
|
5790
|
+
const spec = specs[i];
|
|
5348
5791
|
spec.id = this.nextId++;
|
|
5349
5792
|
spec.verified = false;
|
|
5350
5793
|
spec.message = error instanceof Error ? error.message : "Failed to set breakpoint";
|
|
5351
|
-
this.
|
|
5794
|
+
const diagnostics = this.generateDiagnostics(file, requestedLines[i], spec.message);
|
|
5795
|
+
this.formatter.breakpointSet(
|
|
5796
|
+
spec.id,
|
|
5797
|
+
file,
|
|
5798
|
+
spec.line,
|
|
5799
|
+
false,
|
|
5800
|
+
spec.condition,
|
|
5801
|
+
spec.message,
|
|
5802
|
+
diagnostics
|
|
5803
|
+
);
|
|
5352
5804
|
}
|
|
5353
5805
|
}
|
|
5354
5806
|
}
|
|
@@ -5378,7 +5830,103 @@ var BreakpointManager = class {
|
|
|
5378
5830
|
getHitCount() {
|
|
5379
5831
|
return 0;
|
|
5380
5832
|
}
|
|
5833
|
+
/**
|
|
5834
|
+
* Generate diagnostics for an unverified breakpoint
|
|
5835
|
+
*/
|
|
5836
|
+
generateDiagnostics(requestedFile, requestedLine, adapterMessage) {
|
|
5837
|
+
const ext = path10.extname(requestedFile).toLowerCase();
|
|
5838
|
+
const suggestions = getBreakpointSuggestions(this.adapterType, ext, adapterMessage);
|
|
5839
|
+
return {
|
|
5840
|
+
requestedFile,
|
|
5841
|
+
requestedLine,
|
|
5842
|
+
adapterMessage,
|
|
5843
|
+
suggestions,
|
|
5844
|
+
adapterType: this.adapterType,
|
|
5845
|
+
fileExtension: ext || void 0
|
|
5846
|
+
};
|
|
5847
|
+
}
|
|
5381
5848
|
};
|
|
5849
|
+
function normalizeAdapterType(adapterType) {
|
|
5850
|
+
if (!adapterType) return void 0;
|
|
5851
|
+
const lower = adapterType.toLowerCase();
|
|
5852
|
+
if (["dotnet", "coreclr", "vsdbg", "netcoredbg"].includes(lower)) {
|
|
5853
|
+
return "coreclr";
|
|
5854
|
+
}
|
|
5855
|
+
if (["node", "nodejs", "javascript", "js", "typescript", "ts"].includes(lower)) {
|
|
5856
|
+
return "node";
|
|
5857
|
+
}
|
|
5858
|
+
if (["debugpy", "python", "py"].includes(lower)) {
|
|
5859
|
+
return "python";
|
|
5860
|
+
}
|
|
5861
|
+
if (["lldb", "codelldb", "cpp", "c", "rust"].includes(lower)) {
|
|
5862
|
+
return "lldb";
|
|
5863
|
+
}
|
|
5864
|
+
return lower;
|
|
5865
|
+
}
|
|
5866
|
+
function getBreakpointSuggestions(adapterType, fileExtension, adapterMessage) {
|
|
5867
|
+
const suggestions = [];
|
|
5868
|
+
const normalizedAdapter = normalizeAdapterType(adapterType);
|
|
5869
|
+
const ext = fileExtension.toLowerCase();
|
|
5870
|
+
const msgLower = (adapterMessage || "").toLowerCase();
|
|
5871
|
+
const isSourceMapIssue = msgLower.includes("source map") || msgLower.includes("sourcemap") || msgLower.includes("cannot find");
|
|
5872
|
+
const isPathIssue = msgLower.includes("not found") || msgLower.includes("cannot find file") || msgLower.includes("does not exist");
|
|
5873
|
+
if (normalizedAdapter === "node") {
|
|
5874
|
+
if (ext === ".ts" || ext === ".tsx") {
|
|
5875
|
+
suggestions.push('Ensure "sourceMap": true in tsconfig.json');
|
|
5876
|
+
suggestions.push("Rebuild with source maps: tsc --sourceMap (or your build command)");
|
|
5877
|
+
suggestions.push("Verify .map files exist in your output directory (e.g., dist/**/*.map)");
|
|
5878
|
+
suggestions.push(
|
|
5879
|
+
'Check that "sourceMaps": true is set in your debug config and "outFiles" points to your built JS files'
|
|
5880
|
+
);
|
|
5881
|
+
} else if (ext === ".js" || ext === ".mjs" || ext === ".cjs") {
|
|
5882
|
+
suggestions.push(
|
|
5883
|
+
"Confirm you are setting the breakpoint in the executed file (built output vs source)"
|
|
5884
|
+
);
|
|
5885
|
+
if (isSourceMapIssue) {
|
|
5886
|
+
suggestions.push(
|
|
5887
|
+
"If using bundlers (webpack/esbuild/vite), ensure source maps are generated and accessible"
|
|
5888
|
+
);
|
|
5889
|
+
suggestions.push("Verify source map files (.map) are not excluded from your output");
|
|
5890
|
+
}
|
|
5891
|
+
}
|
|
5892
|
+
} else if (normalizedAdapter === "coreclr") {
|
|
5893
|
+
if (ext === ".cs") {
|
|
5894
|
+
suggestions.push("Build in Debug configuration to ensure PDB files are produced");
|
|
5895
|
+
suggestions.push("Clean and rebuild to refresh symbols: dotnet clean && dotnet build");
|
|
5896
|
+
suggestions.push(
|
|
5897
|
+
"Confirm the running binary matches the source version (stale builds can break mapping)"
|
|
5898
|
+
);
|
|
5899
|
+
suggestions.push("Ensure PDB files are deployed alongside the DLL");
|
|
5900
|
+
}
|
|
5901
|
+
} else if (normalizedAdapter === "python") {
|
|
5902
|
+
if (ext === ".py") {
|
|
5903
|
+
suggestions.push("Confirm the Python interpreter and working directory match your project");
|
|
5904
|
+
suggestions.push(
|
|
5905
|
+
"If debugging remotely or in containers, configure path mappings (localRoot/remoteRoot)"
|
|
5906
|
+
);
|
|
5907
|
+
suggestions.push(
|
|
5908
|
+
"Verify you are setting the breakpoint in code that actually executes (imported module vs different copy)"
|
|
5909
|
+
);
|
|
5910
|
+
}
|
|
5911
|
+
} else if (normalizedAdapter === "lldb") {
|
|
5912
|
+
if ([".c", ".cpp", ".cc", ".cxx", ".h", ".hpp", ".rs", ".m", ".mm"].includes(ext)) {
|
|
5913
|
+
suggestions.push("Compile with debug symbols (-g) and avoid stripping binaries");
|
|
5914
|
+
suggestions.push("Ensure the running binary matches the source used to compile");
|
|
5915
|
+
suggestions.push("For Rust, build with: cargo build (debug mode, not --release)");
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5918
|
+
if (isPathIssue) {
|
|
5919
|
+
suggestions.push("Check the file path is correct and exists relative to the provided cwd");
|
|
5920
|
+
}
|
|
5921
|
+
if (suggestions.length === 0) {
|
|
5922
|
+
suggestions.push("Check the file path is correct and exists relative to the provided cwd");
|
|
5923
|
+
suggestions.push(
|
|
5924
|
+
"Ensure the running program corresponds to this source (no stale build artifacts)"
|
|
5925
|
+
);
|
|
5926
|
+
suggestions.push("Verify the file has been compiled/built with debug information");
|
|
5927
|
+
}
|
|
5928
|
+
return suggestions;
|
|
5929
|
+
}
|
|
5382
5930
|
|
|
5383
5931
|
// src/session/variables.ts
|
|
5384
5932
|
var BLOCKED_PROPERTIES = /* @__PURE__ */ new Set([
|
|
@@ -6119,10 +6667,17 @@ var DebugSession = class {
|
|
|
6119
6667
|
traceInitialStackDepth = 0;
|
|
6120
6668
|
/** Previous locals for variable diffing (only used when diffVars is enabled) */
|
|
6121
6669
|
previousLocals = {};
|
|
6670
|
+
/** Whether we are stepping to evaluate expressions after line execution */
|
|
6671
|
+
isEvalAfterStep = false;
|
|
6672
|
+
/** Pending data for eval-after-step (original breakpoint data) */
|
|
6673
|
+
evalAfterStepData = null;
|
|
6122
6674
|
sessionPromise = null;
|
|
6123
6675
|
sessionResolve = null;
|
|
6124
|
-
_sessionReject = null;
|
|
6125
6676
|
timeoutHandle = null;
|
|
6677
|
+
/** Error that occurred during session (timeout, etc.) - used to avoid unhandled promise rejections */
|
|
6678
|
+
sessionError = null;
|
|
6679
|
+
/** Whether session_end event has been emitted (to prevent duplicate emissions) */
|
|
6680
|
+
sessionEndEmitted = false;
|
|
6126
6681
|
constructor(config, formatter) {
|
|
6127
6682
|
this.config = config;
|
|
6128
6683
|
this.formatter = formatter ?? new OutputFormatter();
|
|
@@ -6132,6 +6687,8 @@ var DebugSession = class {
|
|
|
6132
6687
|
*/
|
|
6133
6688
|
async run() {
|
|
6134
6689
|
this.startTime = Date.now();
|
|
6690
|
+
this.sessionError = null;
|
|
6691
|
+
this.sessionEndEmitted = false;
|
|
6135
6692
|
if (this.config.attach && this.config.pid) {
|
|
6136
6693
|
this.formatter.sessionStartAttach(this.config.adapter.name, this.config.pid);
|
|
6137
6694
|
} else {
|
|
@@ -6142,9 +6699,8 @@ var DebugSession = class {
|
|
|
6142
6699
|
this.config.cwd
|
|
6143
6700
|
);
|
|
6144
6701
|
}
|
|
6145
|
-
this.sessionPromise = new Promise((
|
|
6146
|
-
this.sessionResolve =
|
|
6147
|
-
this._sessionReject = reject;
|
|
6702
|
+
this.sessionPromise = new Promise((resolve9) => {
|
|
6703
|
+
this.sessionResolve = resolve9;
|
|
6148
6704
|
});
|
|
6149
6705
|
if (this.config.timeout) {
|
|
6150
6706
|
this.timeoutHandle = setTimeout(() => {
|
|
@@ -6152,13 +6708,21 @@ var DebugSession = class {
|
|
|
6152
6708
|
}, this.config.timeout);
|
|
6153
6709
|
}
|
|
6154
6710
|
try {
|
|
6155
|
-
await this.start();
|
|
6711
|
+
await Promise.race([this.start(), this.sessionPromise]);
|
|
6712
|
+
if (this.sessionError) {
|
|
6713
|
+
throw this.sessionError;
|
|
6714
|
+
}
|
|
6156
6715
|
await this.sessionPromise;
|
|
6716
|
+
if (this.sessionError) {
|
|
6717
|
+
throw this.sessionError;
|
|
6718
|
+
}
|
|
6157
6719
|
} catch (error) {
|
|
6158
|
-
this.
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6720
|
+
if (error !== this.sessionError) {
|
|
6721
|
+
this.formatter.error(
|
|
6722
|
+
"Session failed",
|
|
6723
|
+
error instanceof Error ? error.message : String(error)
|
|
6724
|
+
);
|
|
6725
|
+
}
|
|
6162
6726
|
throw error;
|
|
6163
6727
|
} finally {
|
|
6164
6728
|
await this.cleanup();
|
|
@@ -6171,7 +6735,7 @@ var DebugSession = class {
|
|
|
6171
6735
|
command: this.config.adapter.command,
|
|
6172
6736
|
args: this.config.adapter.args,
|
|
6173
6737
|
cwd: this.config.cwd,
|
|
6174
|
-
env: this.config.env,
|
|
6738
|
+
env: { ...this.config.adapter.env, ...this.config.env },
|
|
6175
6739
|
port: this.config.adapter.socketPort,
|
|
6176
6740
|
timeout: this.config.timeout
|
|
6177
6741
|
});
|
|
@@ -6180,7 +6744,7 @@ var DebugSession = class {
|
|
|
6180
6744
|
command: this.config.adapter.command,
|
|
6181
6745
|
args: this.config.adapter.args,
|
|
6182
6746
|
cwd: this.config.cwd,
|
|
6183
|
-
env: this.config.env,
|
|
6747
|
+
env: { ...this.config.adapter.env, ...this.config.env },
|
|
6184
6748
|
timeout: this.config.timeout
|
|
6185
6749
|
});
|
|
6186
6750
|
}
|
|
@@ -6192,7 +6756,8 @@ var DebugSession = class {
|
|
|
6192
6756
|
});
|
|
6193
6757
|
this.breakpointManager = new BreakpointManager(this.client, this.formatter, {
|
|
6194
6758
|
cwd: this.config.cwd,
|
|
6195
|
-
programPath: this.config.program
|
|
6759
|
+
programPath: this.config.program,
|
|
6760
|
+
adapterType: this.config.adapter.name
|
|
6196
6761
|
});
|
|
6197
6762
|
this.variableInspector = new VariableInspector(this.client, {
|
|
6198
6763
|
compactServices: !this.config.expandServices,
|
|
@@ -6265,14 +6830,14 @@ var DebugSession = class {
|
|
|
6265
6830
|
* Wait for the 'initialized' event from the debug adapter
|
|
6266
6831
|
*/
|
|
6267
6832
|
async waitForInitialized() {
|
|
6268
|
-
return new Promise((
|
|
6833
|
+
return new Promise((resolve9) => {
|
|
6269
6834
|
const onInitialized = () => {
|
|
6270
|
-
|
|
6835
|
+
resolve9();
|
|
6271
6836
|
};
|
|
6272
6837
|
this.client.once("initialized", onInitialized);
|
|
6273
6838
|
const timeout = setTimeout(() => {
|
|
6274
6839
|
this.client.removeListener("initialized", onInitialized);
|
|
6275
|
-
|
|
6840
|
+
resolve9();
|
|
6276
6841
|
}, 3e4);
|
|
6277
6842
|
this.client.once("initialized", () => {
|
|
6278
6843
|
clearTimeout(timeout);
|
|
@@ -6345,7 +6910,8 @@ var DebugSession = class {
|
|
|
6345
6910
|
locals = await this.variableInspector.getLocals(topFrame.id);
|
|
6346
6911
|
}
|
|
6347
6912
|
let evaluations;
|
|
6348
|
-
|
|
6913
|
+
const shouldDeferEval = this.config.evalAfterStep && reason === "breakpoint";
|
|
6914
|
+
if (this.config.evaluations?.length && topFrame && !shouldDeferEval) {
|
|
6349
6915
|
evaluations = await this.variableInspector.evaluateExpressions(
|
|
6350
6916
|
topFrame.id,
|
|
6351
6917
|
this.config.evaluations
|
|
@@ -6361,6 +6927,64 @@ var DebugSession = class {
|
|
|
6361
6927
|
);
|
|
6362
6928
|
return;
|
|
6363
6929
|
}
|
|
6930
|
+
if (reason === "step" && this.isEvalAfterStep && this.evalAfterStepData) {
|
|
6931
|
+
this.isEvalAfterStep = false;
|
|
6932
|
+
const pendingData = this.evalAfterStepData;
|
|
6933
|
+
this.evalAfterStepData = null;
|
|
6934
|
+
let evaluations2;
|
|
6935
|
+
if (this.config.evaluations?.length && topFrame) {
|
|
6936
|
+
evaluations2 = await this.variableInspector.evaluateExpressions(
|
|
6937
|
+
topFrame.id,
|
|
6938
|
+
this.config.evaluations
|
|
6939
|
+
);
|
|
6940
|
+
}
|
|
6941
|
+
if (topFrame) {
|
|
6942
|
+
const failed = await this.checkAssertions(topFrame.id);
|
|
6943
|
+
if (failed) {
|
|
6944
|
+
await this.emitAssertionFailed(
|
|
6945
|
+
pendingData.threadId,
|
|
6946
|
+
failed.assertion,
|
|
6947
|
+
failed.value,
|
|
6948
|
+
failed.error,
|
|
6949
|
+
location,
|
|
6950
|
+
stackTrace,
|
|
6951
|
+
topFrame.id
|
|
6952
|
+
);
|
|
6953
|
+
this.endSession();
|
|
6954
|
+
return;
|
|
6955
|
+
}
|
|
6956
|
+
}
|
|
6957
|
+
const event2 = {
|
|
6958
|
+
type: "breakpoint_hit",
|
|
6959
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6960
|
+
id: pendingData.breakpointId,
|
|
6961
|
+
threadId: pendingData.threadId,
|
|
6962
|
+
location: pendingData.originalLocation,
|
|
6963
|
+
stackTrace: pendingData.originalStackTrace,
|
|
6964
|
+
locals,
|
|
6965
|
+
evaluations: evaluations2
|
|
6966
|
+
};
|
|
6967
|
+
this.formatter.emit(event2);
|
|
6968
|
+
if (this.config.trace) {
|
|
6969
|
+
await this.startTrace(
|
|
6970
|
+
pendingData.threadId,
|
|
6971
|
+
location,
|
|
6972
|
+
stackResponse.stackFrames.length,
|
|
6973
|
+
topFrame?.id
|
|
6974
|
+
);
|
|
6975
|
+
return;
|
|
6976
|
+
}
|
|
6977
|
+
if (this.config.steps && this.config.steps > 1) {
|
|
6978
|
+
this.remainingSteps = this.config.steps - 1;
|
|
6979
|
+
this.isStepping = true;
|
|
6980
|
+
await this.client.next({ threadId });
|
|
6981
|
+
this.state = "running";
|
|
6982
|
+
return;
|
|
6983
|
+
}
|
|
6984
|
+
await this.client.continue({ threadId });
|
|
6985
|
+
this.state = "running";
|
|
6986
|
+
return;
|
|
6987
|
+
}
|
|
6364
6988
|
if (reason === "step" && this.isStepping) {
|
|
6365
6989
|
this.stepsExecuted++;
|
|
6366
6990
|
this.remainingSteps--;
|
|
@@ -6440,6 +7064,18 @@ var DebugSession = class {
|
|
|
6440
7064
|
await this.endTrace(threadId, "breakpoint", stackTrace, topFrame?.id);
|
|
6441
7065
|
}
|
|
6442
7066
|
this.breakpointsHit++;
|
|
7067
|
+
if (this.config.evalAfterStep && this.config.evaluations?.length) {
|
|
7068
|
+
this.isEvalAfterStep = true;
|
|
7069
|
+
this.evalAfterStepData = {
|
|
7070
|
+
threadId,
|
|
7071
|
+
originalLocation: location,
|
|
7072
|
+
originalStackTrace: stackTrace,
|
|
7073
|
+
breakpointId: body.hitBreakpointIds?.[0]
|
|
7074
|
+
};
|
|
7075
|
+
await this.client.next({ threadId });
|
|
7076
|
+
this.state = "running";
|
|
7077
|
+
return;
|
|
7078
|
+
}
|
|
6443
7079
|
if (topFrame) {
|
|
6444
7080
|
const failed = await this.checkAssertions(topFrame.id);
|
|
6445
7081
|
if (failed) {
|
|
@@ -6535,31 +7171,36 @@ var DebugSession = class {
|
|
|
6535
7171
|
this.endSessionWithError(new Error(`Session timed out after ${this.config.timeout}ms`));
|
|
6536
7172
|
}
|
|
6537
7173
|
endSessionWithError(error) {
|
|
6538
|
-
this.
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
7174
|
+
this.sessionError = error;
|
|
7175
|
+
if (!this.sessionEndEmitted) {
|
|
7176
|
+
this.sessionEndEmitted = true;
|
|
7177
|
+
this.formatter.sessionEnd({
|
|
7178
|
+
durationMs: Date.now() - this.startTime,
|
|
7179
|
+
exitCode: this.exitCode,
|
|
7180
|
+
breakpointsHit: this.breakpointsHit,
|
|
7181
|
+
exceptionsCaught: this.exceptionsCaught,
|
|
7182
|
+
stepsExecuted: this.stepsExecuted
|
|
7183
|
+
});
|
|
7184
|
+
}
|
|
7185
|
+
if (this.sessionResolve) {
|
|
7186
|
+
this.sessionResolve();
|
|
6547
7187
|
this.sessionResolve = null;
|
|
6548
|
-
this._sessionReject = null;
|
|
6549
7188
|
}
|
|
6550
7189
|
}
|
|
6551
7190
|
endSession() {
|
|
6552
|
-
this.
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
7191
|
+
if (!this.sessionEndEmitted) {
|
|
7192
|
+
this.sessionEndEmitted = true;
|
|
7193
|
+
this.formatter.sessionEnd({
|
|
7194
|
+
durationMs: Date.now() - this.startTime,
|
|
7195
|
+
exitCode: this.exitCode,
|
|
7196
|
+
breakpointsHit: this.breakpointsHit,
|
|
7197
|
+
exceptionsCaught: this.exceptionsCaught,
|
|
7198
|
+
stepsExecuted: this.stepsExecuted
|
|
7199
|
+
});
|
|
7200
|
+
}
|
|
6559
7201
|
if (this.sessionResolve) {
|
|
6560
7202
|
this.sessionResolve();
|
|
6561
7203
|
this.sessionResolve = null;
|
|
6562
|
-
this._sessionReject = null;
|
|
6563
7204
|
}
|
|
6564
7205
|
}
|
|
6565
7206
|
async cleanup() {
|
|
@@ -6819,7 +7460,7 @@ async function launchTestRunner(config) {
|
|
|
6819
7460
|
});
|
|
6820
7461
|
const stdoutReader = readline.createInterface({ input: testProcess.stdout });
|
|
6821
7462
|
const stderrReader = readline.createInterface({ input: testProcess.stderr });
|
|
6822
|
-
return new Promise((
|
|
7463
|
+
return new Promise((resolve9, reject) => {
|
|
6823
7464
|
let foundPid = null;
|
|
6824
7465
|
let exitedEarly = false;
|
|
6825
7466
|
let sawDebugHint = false;
|
|
@@ -6834,7 +7475,7 @@ async function launchTestRunner(config) {
|
|
|
6834
7475
|
if (match && !foundPid && sawDebugHint) {
|
|
6835
7476
|
foundPid = parseInt(match[1], 10);
|
|
6836
7477
|
onProgress?.(`Found testhost PID: ${foundPid} (process: ${match[2]})`);
|
|
6837
|
-
|
|
7478
|
+
resolve9({
|
|
6838
7479
|
pid: foundPid,
|
|
6839
7480
|
process: testProcess
|
|
6840
7481
|
});
|
|
@@ -6932,6 +7573,10 @@ function createCli() {
|
|
|
6932
7573
|
"--diff-vars",
|
|
6933
7574
|
"Show only changed variables in trace steps instead of full dumps",
|
|
6934
7575
|
false
|
|
7576
|
+
).option(
|
|
7577
|
+
"--eval-after-step",
|
|
7578
|
+
"Step once before evaluating expressions (useful for evaluating variables being assigned on the breakpoint line)",
|
|
7579
|
+
false
|
|
6935
7580
|
).option("-o, --output <file>", "Write events to file instead of stdout").option("--include <types...>", "Only emit these event types (e.g., breakpoint_hit error)").option(
|
|
6936
7581
|
"--exclude <types...>",
|
|
6937
7582
|
"Suppress these event types (e.g., program_output exception_thrown)"
|
|
@@ -6962,6 +7607,14 @@ function createCli() {
|
|
|
6962
7607
|
"Maximum depth to traverse exception chain (default: 10)",
|
|
6963
7608
|
(val) => parseInt(val, 10),
|
|
6964
7609
|
10
|
|
7610
|
+
).option(
|
|
7611
|
+
"--compact",
|
|
7612
|
+
"Enable compact output mode for reduced token usage (limits stack traces, filters internals, abbreviates paths)",
|
|
7613
|
+
false
|
|
7614
|
+
).option(
|
|
7615
|
+
"--stack-limit <count>",
|
|
7616
|
+
"Maximum stack frames to include (default: 3 in compact mode, unlimited otherwise)",
|
|
7617
|
+
(val) => parseInt(val, 10)
|
|
6965
7618
|
).action(
|
|
6966
7619
|
async (programPath, options) => {
|
|
6967
7620
|
if (options.testProject) {
|
|
@@ -6993,6 +7646,16 @@ function createCli() {
|
|
|
6993
7646
|
console.error(`Available adapters: ${getAdapterNames().join(", ")}`);
|
|
6994
7647
|
process.exit(1);
|
|
6995
7648
|
}
|
|
7649
|
+
const breakpointErrors = validateAllBreakpoints(
|
|
7650
|
+
options.breakpoint || [],
|
|
7651
|
+
options.logpoint || []
|
|
7652
|
+
);
|
|
7653
|
+
if (breakpointErrors.length > 0) {
|
|
7654
|
+
for (const error of breakpointErrors) {
|
|
7655
|
+
console.error(`Error: ${error}`);
|
|
7656
|
+
}
|
|
7657
|
+
process.exit(1);
|
|
7658
|
+
}
|
|
6996
7659
|
await runDebugSession({ ...options, program: programPath, adapter: options.adapter });
|
|
6997
7660
|
}
|
|
6998
7661
|
);
|
|
@@ -7027,6 +7690,13 @@ async function runTestDebugSession(options) {
|
|
|
7027
7690
|
console.error('Example: -b "path/to/TestFile.cs:42"');
|
|
7028
7691
|
process.exit(1);
|
|
7029
7692
|
}
|
|
7693
|
+
const breakpointErrors = validateAllBreakpoints(options.breakpoint || [], options.logpoint || []);
|
|
7694
|
+
if (breakpointErrors.length > 0) {
|
|
7695
|
+
for (const error of breakpointErrors) {
|
|
7696
|
+
console.error(`Error: ${error}`);
|
|
7697
|
+
}
|
|
7698
|
+
process.exit(1);
|
|
7699
|
+
}
|
|
7030
7700
|
console.error(`Starting test runner for: ${options.testProject}`);
|
|
7031
7701
|
if (options.testFilter) {
|
|
7032
7702
|
console.error(`Test filter: ${options.testFilter}`);
|
|
@@ -7105,7 +7775,9 @@ async function runDebugSession(options) {
|
|
|
7105
7775
|
pretty: options.pretty,
|
|
7106
7776
|
stream: outputStream,
|
|
7107
7777
|
include: options.include,
|
|
7108
|
-
exclude: options.exclude
|
|
7778
|
+
exclude: options.exclude,
|
|
7779
|
+
compact: options.compact,
|
|
7780
|
+
stackLimit: options.stackLimit
|
|
7109
7781
|
});
|
|
7110
7782
|
const session = new DebugSession(
|
|
7111
7783
|
{
|
|
@@ -7130,6 +7802,7 @@ async function runDebugSession(options) {
|
|
|
7130
7802
|
traceLimit: options.traceLimit,
|
|
7131
7803
|
traceUntil: options.traceUntil,
|
|
7132
7804
|
diffVars: options.diffVars,
|
|
7805
|
+
evalAfterStep: options.evalAfterStep,
|
|
7133
7806
|
// Token efficiency options
|
|
7134
7807
|
expandServices: options.expandServices,
|
|
7135
7808
|
showNullProps: options.showNullProps,
|
|
@@ -7253,8 +7926,8 @@ function getSkillTargets(options) {
|
|
|
7253
7926
|
}
|
|
7254
7927
|
async function installSkill(options = {}) {
|
|
7255
7928
|
const path12 = await import("node:path");
|
|
7256
|
-
const { fileURLToPath
|
|
7257
|
-
const moduleDir = path12.dirname(
|
|
7929
|
+
const { fileURLToPath } = await import("node:url");
|
|
7930
|
+
const moduleDir = path12.dirname(fileURLToPath(__importMetaUrl));
|
|
7258
7931
|
const possibleSources = [
|
|
7259
7932
|
path12.join(moduleDir, "..", ".claude", "skills", "debug-run"),
|
|
7260
7933
|
path12.join(moduleDir, ".claude", "skills", "debug-run"),
|