agent-dbg 0.1.0 → 0.1.2

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.
Files changed (71) hide show
  1. package/.claude/settings.local.json +7 -5
  2. package/.claude/skills/agent-dbg/SKILL.md +116 -0
  3. package/.claude/skills/agent-dbg/references/commands.md +173 -0
  4. package/.vscode/launch.json +19 -0
  5. package/CLAUDE.md +2 -2
  6. package/PROGRESS.md +91 -91
  7. package/README.md +45 -17
  8. package/{ndbg-spec.md → SPEC.md} +152 -152
  9. package/dist/main.js +12500 -0
  10. package/package.json +3 -3
  11. package/src/cdp/client.ts +18 -4
  12. package/src/cdp/logger.ts +69 -0
  13. package/src/cli/parser.ts +54 -43
  14. package/src/commands/attach.ts +2 -2
  15. package/src/commands/blackbox-ls.ts +1 -1
  16. package/src/commands/blackbox-rm.ts +3 -3
  17. package/src/commands/blackbox.ts +2 -2
  18. package/src/commands/break-ls.ts +3 -2
  19. package/src/commands/break-rm.ts +2 -2
  20. package/src/commands/break-toggle.ts +2 -2
  21. package/src/commands/break.ts +46 -17
  22. package/src/commands/breakable.ts +2 -2
  23. package/src/commands/catch.ts +2 -2
  24. package/src/commands/console.ts +1 -1
  25. package/src/commands/continue.ts +5 -18
  26. package/src/commands/eval.ts +2 -2
  27. package/src/commands/exceptions.ts +1 -1
  28. package/src/commands/hotpatch.ts +2 -2
  29. package/src/commands/launch.ts +7 -11
  30. package/src/commands/logpoint.ts +7 -6
  31. package/src/commands/logs.ts +116 -0
  32. package/src/commands/pause.ts +5 -18
  33. package/src/commands/print-state.ts +85 -0
  34. package/src/commands/props.ts +2 -2
  35. package/src/commands/restart-frame.ts +1 -1
  36. package/src/commands/restart.ts +42 -0
  37. package/src/commands/run-to.ts +7 -20
  38. package/src/commands/scripts.ts +1 -1
  39. package/src/commands/search.ts +4 -3
  40. package/src/commands/set-return.ts +2 -2
  41. package/src/commands/set.ts +3 -3
  42. package/src/commands/source.ts +3 -2
  43. package/src/commands/sourcemap.ts +1 -1
  44. package/src/commands/stack.ts +1 -1
  45. package/src/commands/state.ts +3 -73
  46. package/src/commands/status.ts +3 -2
  47. package/src/commands/step.ts +5 -18
  48. package/src/commands/vars.ts +3 -1
  49. package/src/daemon/entry.ts +16 -6
  50. package/src/daemon/paths.ts +6 -2
  51. package/src/daemon/server.ts +1 -1
  52. package/src/daemon/session-breakpoints.ts +7 -2
  53. package/src/daemon/session-inspection.ts +6 -10
  54. package/src/daemon/session-state.ts +6 -5
  55. package/src/daemon/session.ts +23 -3
  56. package/src/formatter/logs.ts +110 -0
  57. package/src/formatter/path.ts +27 -0
  58. package/src/formatter/stack.ts +5 -2
  59. package/src/formatter/variables.ts +48 -1
  60. package/src/main.ts +2 -0
  61. package/src/protocol/messages.ts +3 -0
  62. package/tests/fixtures/async-app.js +1 -1
  63. package/tests/fixtures/error-app.js +1 -1
  64. package/tests/fixtures/simple-app.js +1 -1
  65. package/tests/fixtures/ts-app/src/app.ts +4 -0
  66. package/tests/integration/state.test.ts +7 -7
  67. package/tests/unit/daemon.test.ts +1 -1
  68. package/tests/unit/formatter.test.ts +8 -4
  69. package/.bin/ndbg +0 -0
  70. package/.claude/skills/ndbg-debugger/ndbg-debugger/SKILL.md +0 -116
  71. package/.claude/skills/ndbg-debugger/ndbg-debugger/references/commands.md +0 -173
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "agent-dbg",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Node.js Debugger CLI for AI Agents",
5
5
  "type": "module",
6
6
  "bin": {
7
- "ndbg": "./src/main.ts"
7
+ "agent-dbg": "./dist/main.js"
8
8
  },
9
9
  "scripts": {
10
10
  "dev": "bun run src/main.ts",
11
- "build": "bun build src/main.ts --compile --outfile dist/ndbg",
11
+ "build": "bun build src/main.ts --outdir dist --target=bun",
12
12
  "test": "bun test",
13
13
  "lint": "biome check .",
14
14
  "format": "biome check --write .",
package/src/cdp/client.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ProtocolMapping } from "devtools-protocol/types/protocol-mapping.js";
2
+ import type { CdpLogger } from "./logger.ts";
2
3
  import type { CdpEvent, CdpRequest, CdpResponse } from "./types.ts";
3
4
 
4
5
  const DEFAULT_TIMEOUT_MS = 30_000;
@@ -21,20 +22,24 @@ export class CdpClient {
21
22
  private pending = new Map<number, PendingRequest>();
22
23
  private listeners = new Map<string, Set<AnyHandler>>();
23
24
  private isConnected = false;
25
+ private logger: CdpLogger | null;
26
+ /** Map request id → method name for response logging */
27
+ private sentMethods = new Map<number, string>();
24
28
 
25
- private constructor(ws: WebSocket) {
29
+ private constructor(ws: WebSocket, logger?: CdpLogger) {
26
30
  this.ws = ws;
31
+ this.logger = logger ?? null;
27
32
  this.isConnected = true;
28
33
  this.setupHandlers();
29
34
  }
30
35
 
31
- static async connect(wsUrl: string): Promise<CdpClient> {
36
+ static async connect(wsUrl: string, logger?: CdpLogger): Promise<CdpClient> {
32
37
  return new Promise<CdpClient>((resolve, reject) => {
33
38
  const ws = new WebSocket(wsUrl);
34
39
 
35
40
  const onOpen = () => {
36
41
  ws.removeEventListener("error", onError);
37
- const client = new CdpClient(ws);
42
+ const client = new CdpClient(ws, logger);
38
43
  resolve(client);
39
44
  };
40
45
 
@@ -53,7 +58,6 @@ export class CdpClient {
53
58
  method: T,
54
59
  ...params: ProtocolMapping.Commands[T]["paramsType"]
55
60
  ): Promise<ProtocolMapping.Commands[T]["returnType"]>;
56
- async send(method: string, params?: Record<string, unknown>): Promise<unknown>;
57
61
  async send(method: string, ...args: unknown[]): Promise<unknown> {
58
62
  if (!this.isConnected) {
59
63
  throw new Error("CDP client is not connected");
@@ -66,9 +70,13 @@ export class CdpClient {
66
70
  request.params = params;
67
71
  }
68
72
 
73
+ this.sentMethods.set(id, method);
74
+ this.logger?.logSend(id, method, params as Record<string, unknown> | undefined);
75
+
69
76
  return new Promise<unknown>((resolve, reject) => {
70
77
  const timer = setTimeout(() => {
71
78
  this.pending.delete(id);
79
+ this.sentMethods.delete(id);
72
80
  reject(new Error(`CDP request timed out: ${method} (id=${id})`));
73
81
  }, DEFAULT_TIMEOUT_MS);
74
82
 
@@ -173,6 +181,10 @@ export class CdpClient {
173
181
 
174
182
  if ("id" in parsed && typeof parsed.id === "number") {
175
183
  const response = parsed as CdpResponse;
184
+ const method = this.sentMethods.get(response.id) ?? "unknown";
185
+ this.sentMethods.delete(response.id);
186
+ this.logger?.logResponse(response.id, method, response.result, response.error);
187
+
176
188
  const pending = this.pending.get(response.id);
177
189
  if (!pending) {
178
190
  return;
@@ -187,6 +199,8 @@ export class CdpClient {
187
199
  }
188
200
  } else if ("method" in parsed) {
189
201
  const event = parsed as CdpEvent;
202
+ this.logger?.logEvent(event.method, event.params);
203
+
190
204
  const handlers = this.listeners.get(event.method);
191
205
  if (handlers) {
192
206
  for (const handler of handlers) {
@@ -0,0 +1,69 @@
1
+ import { appendFileSync, writeFileSync } from "node:fs";
2
+
3
+ export interface CdpLogEntry {
4
+ ts: number;
5
+ dir: "send" | "recv" | "event";
6
+ method: string;
7
+ id?: number;
8
+ params?: Record<string, unknown>;
9
+ result?: unknown;
10
+ error?: { code: number; message: string };
11
+ ms?: number;
12
+ }
13
+
14
+ export class CdpLogger {
15
+ private logPath: string;
16
+ /** Map request id → { method, sentAt } for latency tracking */
17
+ private pending = new Map<number, { method: string; sentAt: number }>();
18
+
19
+ constructor(logPath: string) {
20
+ this.logPath = logPath;
21
+ // Truncate log file on creation (new session = fresh log)
22
+ writeFileSync(logPath, "");
23
+ }
24
+
25
+ logSend(id: number, method: string, params?: Record<string, unknown>): void {
26
+ this.pending.set(id, { method, sentAt: Date.now() });
27
+ const entry: CdpLogEntry = { ts: Date.now(), dir: "send", method, id };
28
+ if (params !== undefined) {
29
+ entry.params = params;
30
+ }
31
+ this.append(entry);
32
+ }
33
+
34
+ logResponse(
35
+ id: number,
36
+ method: string,
37
+ result?: unknown,
38
+ error?: { code: number; message: string },
39
+ ): void {
40
+ const pending = this.pending.get(id);
41
+ const ms = pending ? Date.now() - pending.sentAt : undefined;
42
+ if (pending) this.pending.delete(id);
43
+
44
+ const entry: CdpLogEntry = { ts: Date.now(), dir: "recv", method, id };
45
+ if (ms !== undefined) entry.ms = ms;
46
+ if (error) {
47
+ entry.error = error;
48
+ } else if (result !== undefined) {
49
+ entry.result = result;
50
+ }
51
+ this.append(entry);
52
+ }
53
+
54
+ logEvent(method: string, params?: unknown): void {
55
+ const entry: CdpLogEntry = { ts: Date.now(), dir: "event", method };
56
+ if (params !== undefined) {
57
+ entry.params = params as Record<string, unknown>;
58
+ }
59
+ this.append(entry);
60
+ }
61
+
62
+ clear(): void {
63
+ writeFileSync(this.logPath, "");
64
+ }
65
+
66
+ private append(entry: CdpLogEntry): void {
67
+ appendFileSync(this.logPath, `${JSON.stringify(entry)}\n`);
68
+ }
69
+ }
package/src/cli/parser.ts CHANGED
@@ -98,6 +98,7 @@ export function parseArgs(argv: string[]): ParsedArgs {
98
98
  s: "stack",
99
99
  b: "breakpoints",
100
100
  c: "code",
101
+ f: "follow",
101
102
  };
102
103
  const mapped = shortMap[key];
103
104
  if (mapped) {
@@ -141,7 +142,7 @@ export async function run(args: ParsedArgs): Promise<number> {
141
142
  const handler = registry.get(args.command);
142
143
  if (!handler) {
143
144
  console.error(`✗ Unknown command: ${args.command}`);
144
- console.error(" → Try: ndbg --help");
145
+ console.error(" → Try: agent-dbg --help");
145
146
  return 1;
146
147
  }
147
148
 
@@ -155,9 +156,9 @@ export async function run(args: ParsedArgs): Promise<number> {
155
156
  }
156
157
 
157
158
  function printHelp(): void {
158
- console.log(`ndbg — Node.js debugger CLI for AI agents
159
+ console.log(`agent-dbg — Node.js debugger CLI for AI agents
159
160
 
160
- Usage: ndbg <command> [options]
161
+ Usage: agent-dbg <command> [options]
161
162
 
162
163
  Session:
163
164
  launch [--brk] <command...> Start + attach debugger
@@ -218,6 +219,10 @@ Source Maps:
218
219
  sourcemap [file] Show source map info
219
220
  sourcemap --disable Disable resolution globally
220
221
 
222
+ Diagnostics:
223
+ logs [-f|--follow] Show CDP protocol log
224
+ [--limit N] [--domain <name>] [--clear]
225
+
221
226
  Global flags:
222
227
  --session NAME Target session (default: "default")
223
228
  --json JSON output
@@ -227,61 +232,67 @@ Global flags:
227
232
  }
228
233
 
229
234
  function printHelpAgent(): void {
230
- console.log(`ndbg — Node.js debugger CLI for AI agents
235
+ console.log(`agent-dbg — Node.js debugger CLI for AI agents
231
236
 
232
237
  CORE LOOP:
233
- 1. ndbg launch --brk "node app.js" → pauses at first line, returns state
234
- 2. ndbg break src/file.ts:42 → set breakpoint
235
- 3. ndbg continue → run to breakpoint, returns state
236
- 4. Inspect: ndbg vars, ndbg eval, ndbg props @v1
237
- 5. Mutate/fix: ndbg set @v1 value, ndbg hotpatch src/file.ts
238
+ 1. agent-dbg launch --brk "node app.js" → pauses at first line, returns state
239
+ 2. agent-dbg break src/file.ts:42 → set breakpoint
240
+ 3. agent-dbg continue → run to breakpoint, returns state
241
+ 4. Inspect: agent-dbg vars, agent-dbg eval, agent-dbg props @v1
242
+ 5. Mutate/fix: agent-dbg set @v1 value, agent-dbg hotpatch src/file.ts
238
243
  6. Repeat from 3
239
244
 
240
245
  REFS: Every output assigns @refs. Use them everywhere:
241
- @v1..@vN variables | ndbg props @v1, ndbg set @v2 true
242
- @f0..@fN stack frames | ndbg eval --frame @f1
243
- BP#1..N breakpoints | ndbg break-rm BP#1, ndbg break-toggle BP#1
246
+ @v1..@vN variables | agent-dbg props @v1, agent-dbg set @v2 true
247
+ @f0..@fN stack frames | agent-dbg eval --frame @f1
248
+ BP#1..N breakpoints | agent-dbg break-rm BP#1, agent-dbg break-toggle BP#1
244
249
 
245
250
  EXECUTION (all return state automatically):
246
- ndbg continue Resume to next breakpoint
247
- ndbg step [over|into|out] Step one statement
248
- ndbg run-to file:line Continue to location
249
- ndbg pause Interrupt running process
250
- ndbg restart-frame [@fN] Re-run frame from beginning
251
+ agent-dbg continue Resume to next breakpoint
252
+ agent-dbg step [over|into|out] Step one statement
253
+ agent-dbg run-to file:line Continue to location
254
+ agent-dbg pause Interrupt running process
255
+ agent-dbg restart-frame [@fN] Re-run frame from beginning
251
256
 
252
257
  BREAKPOINTS:
253
- ndbg break file:line [--condition expr] [--hit-count N] [--continue]
254
- ndbg break --pattern "regex":line
255
- ndbg break-rm <BP#|all> Remove breakpoints
256
- ndbg break-ls List breakpoints
257
- ndbg break-toggle <BP#|all> Enable/disable breakpoints
258
- ndbg breakable file:start-end Valid breakpoint locations
259
- ndbg logpoint file:line "template \${var}" [--condition expr]
260
- ndbg catch [all|uncaught|caught|none]
258
+ agent-dbg break file:line [--condition expr] [--hit-count N] [--continue]
259
+ agent-dbg break --pattern "regex":line
260
+ agent-dbg break-rm <BP#|all> Remove breakpoints
261
+ agent-dbg break-ls List breakpoints
262
+ agent-dbg break-toggle <BP#|all> Enable/disable breakpoints
263
+ agent-dbg breakable file:start-end Valid breakpoint locations
264
+ agent-dbg logpoint file:line "template \${var}" [--condition expr]
265
+ agent-dbg catch [all|uncaught|caught|none]
261
266
 
262
267
  INSPECTION:
263
- ndbg state [-v|-s|-b|-c] [--depth N] [--lines N] [--frame @fN] [--all-scopes] [--compact] [--generated]
264
- ndbg vars [name...] [--frame @fN] [--all-scopes]
265
- ndbg stack [--async-depth N] [--generated]
266
- ndbg eval <expr> [--frame @fN] [--silent] [--timeout MS] [--side-effect-free]
267
- ndbg props @ref [--own] [--depth N] [--private] [--internal]
268
- ndbg source [--lines N] [--file path] [--all] [--generated]
269
- ndbg search "query" [--regex] [--case-sensitive] [--file id]
270
- ndbg scripts [--filter pattern]
271
- ndbg console [--since N] [--level type] [--clear]
272
- ndbg exceptions [--since N]
268
+ agent-dbg state [-v|-s|-b|-c] [--depth N] [--lines N] [--frame @fN] [--all-scopes] [--compact] [--generated]
269
+ agent-dbg vars [name...] [--frame @fN] [--all-scopes]
270
+ agent-dbg stack [--async-depth N] [--generated]
271
+ agent-dbg eval <expr> [--frame @fN] [--silent] [--timeout MS] [--side-effect-free]
272
+ agent-dbg props @ref [--own] [--depth N] [--private] [--internal]
273
+ agent-dbg source [--lines N] [--file path] [--all] [--generated]
274
+ agent-dbg search "query" [--regex] [--case-sensitive] [--file id]
275
+ agent-dbg scripts [--filter pattern]
276
+ agent-dbg console [--since N] [--level type] [--clear]
277
+ agent-dbg exceptions [--since N]
273
278
 
274
279
  MUTATION:
275
- ndbg set <@ref|name> <value> Change variable
276
- ndbg set-return <value> Change return value (at return point)
277
- ndbg hotpatch <file> [--dry-run] Live-edit code (no restart!)
280
+ agent-dbg set <@ref|name> <value> Change variable
281
+ agent-dbg set-return <value> Change return value (at return point)
282
+ agent-dbg hotpatch <file> [--dry-run] Live-edit code (no restart!)
278
283
 
279
284
  BLACKBOXING:
280
- ndbg blackbox <pattern...> Skip stepping into matching scripts
281
- ndbg blackbox-ls List current patterns
282
- ndbg blackbox-rm <pattern|all> Remove patterns
285
+ agent-dbg blackbox <pattern...> Skip stepping into matching scripts
286
+ agent-dbg blackbox-ls List current patterns
287
+ agent-dbg blackbox-rm <pattern|all> Remove patterns
283
288
 
284
289
  SOURCE MAPS:
285
- ndbg sourcemap [file] Show source map info
286
- ndbg sourcemap --disable Disable resolution globally`);
290
+ agent-dbg sourcemap [file] Show source map info
291
+ agent-dbg sourcemap --disable Disable resolution globally
292
+
293
+ DIAGNOSTICS:
294
+ agent-dbg logs [-f|--follow] Show CDP protocol log
295
+ agent-dbg logs --limit 100 Show last N entries (default: 50)
296
+ agent-dbg logs --domain Debugger Filter by CDP domain
297
+ agent-dbg logs --clear Clear the log file`);
287
298
  }
@@ -8,14 +8,14 @@ registerCommand("attach", async (args) => {
8
8
 
9
9
  if (!target) {
10
10
  console.error("No target specified");
11
- console.error(" -> Try: ndbg attach <ws-url | port>");
11
+ console.error(" -> Try: agent-dbg attach <ws-url | port>");
12
12
  return 1;
13
13
  }
14
14
 
15
15
  // Check if daemon already running
16
16
  if (DaemonClient.isRunning(session)) {
17
17
  console.error(`Session "${session}" is already active`);
18
- console.error(` -> Try: ndbg stop --session ${session}`);
18
+ console.error(` -> Try: agent-dbg stop --session ${session}`);
19
19
  return 1;
20
20
  }
21
21
 
@@ -6,7 +6,7 @@ registerCommand("blackbox-ls", async (args) => {
6
6
 
7
7
  if (!DaemonClient.isRunning(session)) {
8
8
  console.error(`No active session "${session}"`);
9
- console.error(" -> Try: ndbg launch --brk node app.js");
9
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
10
10
  return 1;
11
11
  }
12
12
 
@@ -6,7 +6,7 @@ registerCommand("blackbox-rm", async (args) => {
6
6
 
7
7
  if (!DaemonClient.isRunning(session)) {
8
8
  console.error(`No active session "${session}"`);
9
- console.error(" -> Try: ndbg launch --brk node app.js");
9
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
10
10
  return 1;
11
11
  }
12
12
 
@@ -24,8 +24,8 @@ registerCommand("blackbox-rm", async (args) => {
24
24
 
25
25
  if (patterns.length === 0) {
26
26
  console.error("No patterns specified");
27
- console.error(" -> Try: ndbg blackbox-rm node_modules");
28
- console.error(" -> Try: ndbg blackbox-rm all");
27
+ console.error(" -> Try: agent-dbg blackbox-rm node_modules");
28
+ console.error(" -> Try: agent-dbg blackbox-rm all");
29
29
  return 1;
30
30
  }
31
31
 
@@ -6,7 +6,7 @@ registerCommand("blackbox", async (args) => {
6
6
 
7
7
  if (!DaemonClient.isRunning(session)) {
8
8
  console.error(`No active session "${session}"`);
9
- console.error(" -> Try: ndbg launch --brk node app.js");
9
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
10
10
  return 1;
11
11
  }
12
12
 
@@ -20,7 +20,7 @@ registerCommand("blackbox", async (args) => {
20
20
 
21
21
  if (patterns.length === 0) {
22
22
  console.error("No patterns specified");
23
- console.error(" -> Try: ndbg blackbox node_modules");
23
+ console.error(" -> Try: agent-dbg blackbox node_modules");
24
24
  return 1;
25
25
  }
26
26
 
@@ -1,12 +1,13 @@
1
1
  import { registerCommand } from "../cli/registry.ts";
2
2
  import { DaemonClient } from "../daemon/client.ts";
3
+ import { shortPath } from "../formatter/path.ts";
3
4
 
4
5
  registerCommand("break-ls", async (args) => {
5
6
  const session = args.global.session;
6
7
 
7
8
  if (!DaemonClient.isRunning(session)) {
8
9
  console.error(`No active session "${session}"`);
9
- console.error(" -> Try: ndbg launch --brk node app.js");
10
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
10
11
  return 1;
11
12
  }
12
13
 
@@ -37,7 +38,7 @@ registerCommand("break-ls", async (args) => {
37
38
  console.log("No breakpoints or logpoints set");
38
39
  } else {
39
40
  for (const bp of data) {
40
- const loc = `${bp.url}:${bp.line}`;
41
+ const loc = `${shortPath(bp.url)}:${bp.line}`;
41
42
  let line = `${bp.ref} ${loc}`;
42
43
  if (bp.type === "LP" && bp.template) {
43
44
  line += ` (log: ${bp.template})`;
@@ -6,14 +6,14 @@ registerCommand("break-rm", async (args) => {
6
6
 
7
7
  if (!DaemonClient.isRunning(session)) {
8
8
  console.error(`No active session "${session}"`);
9
- console.error(" -> Try: ndbg launch --brk node app.js");
9
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
10
10
  return 1;
11
11
  }
12
12
 
13
13
  const ref = args.subcommand;
14
14
  if (!ref) {
15
15
  console.error("No breakpoint ref specified");
16
- console.error(" -> Try: ndbg break-rm BP#1");
16
+ console.error(" -> Try: agent-dbg break-rm BP#1");
17
17
  return 1;
18
18
  }
19
19
 
@@ -6,14 +6,14 @@ registerCommand("break-toggle", async (args) => {
6
6
 
7
7
  if (!DaemonClient.isRunning(session)) {
8
8
  console.error(`No active session "${session}"`);
9
- console.error(" -> Try: ndbg launch --brk node app.js");
9
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
10
10
  return 1;
11
11
  }
12
12
 
13
13
  const ref = args.subcommand;
14
14
  if (!ref) {
15
15
  console.error("No breakpoint ref specified");
16
- console.error(" -> Try: ndbg break-toggle BP#1");
16
+ console.error(" -> Try: agent-dbg break-toggle BP#1");
17
17
  return 1;
18
18
  }
19
19
 
@@ -1,12 +1,44 @@
1
1
  import { registerCommand } from "../cli/registry.ts";
2
2
  import { DaemonClient } from "../daemon/client.ts";
3
+ import { shortPath } from "../formatter/path.ts";
4
+
5
+ function parseFileLineColumn(
6
+ target: string,
7
+ ): { file: string; line: number; column?: number } | null {
8
+ // Match file:line:column or file:line
9
+ // Walk backwards to find the last two colon-separated numbers
10
+ const lastColon = target.lastIndexOf(":");
11
+ if (lastColon === -1 || lastColon === 0) return null;
12
+
13
+ const afterLast = target.slice(lastColon + 1);
14
+ const maybeNum = parseInt(afterLast, 10);
15
+ if (Number.isNaN(maybeNum) || maybeNum <= 0) return null;
16
+
17
+ const beforeLast = target.slice(0, lastColon);
18
+ const secondColon = beforeLast.lastIndexOf(":");
19
+ if (secondColon > 0) {
20
+ const afterSecond = beforeLast.slice(secondColon + 1);
21
+ const maybeLine = parseInt(afterSecond, 10);
22
+ if (!Number.isNaN(maybeLine) && maybeLine > 0) {
23
+ // file:line:column
24
+ return {
25
+ file: beforeLast.slice(0, secondColon),
26
+ line: maybeLine,
27
+ column: maybeNum,
28
+ };
29
+ }
30
+ }
31
+
32
+ // file:line
33
+ return { file: beforeLast, line: maybeNum };
34
+ }
3
35
 
4
36
  registerCommand("break", async (args) => {
5
37
  const session = args.global.session;
6
38
 
7
39
  if (!DaemonClient.isRunning(session)) {
8
40
  console.error(`No active session "${session}"`);
9
- console.error(" -> Try: ndbg launch --brk node app.js");
41
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
10
42
  return 1;
11
43
  }
12
44
 
@@ -15,13 +47,14 @@ registerCommand("break", async (args) => {
15
47
 
16
48
  let file: string;
17
49
  let line: number;
50
+ let column: number | undefined;
18
51
 
19
52
  if (patternFlag) {
20
53
  // --pattern urlRegex:line
21
54
  const lastColon = patternFlag.lastIndexOf(":");
22
55
  if (lastColon === -1 || lastColon === 0) {
23
56
  console.error(`Invalid --pattern target: "${patternFlag}"`);
24
- console.error(" -> Try: ndbg break --pattern 'app\\.js':42");
57
+ console.error(" -> Try: agent-dbg break --pattern 'app\\.js':42");
25
58
  return 1;
26
59
  }
27
60
  file = patternFlag.slice(0, lastColon);
@@ -34,25 +67,20 @@ registerCommand("break", async (args) => {
34
67
  const target = args.subcommand;
35
68
  if (!target) {
36
69
  console.error("No target specified");
37
- console.error(" -> Try: ndbg break src/app.ts:42");
70
+ console.error(" -> Try: agent-dbg break src/app.ts:42");
38
71
  return 1;
39
72
  }
40
73
 
41
- // Parse file:line from the target
42
- const lastColon = target.lastIndexOf(":");
43
- if (lastColon === -1 || lastColon === 0) {
74
+ // Parse file:line[:column] from the target
75
+ const parsed = parseFileLineColumn(target);
76
+ if (!parsed) {
44
77
  console.error(`Invalid breakpoint target: "${target}"`);
45
- console.error(" -> Try: ndbg break src/app.ts:42");
46
- return 1;
47
- }
48
-
49
- file = target.slice(0, lastColon);
50
- line = parseInt(target.slice(lastColon + 1), 10);
51
- if (Number.isNaN(line) || line <= 0) {
52
- console.error(`Invalid line number in "${target}"`);
53
- console.error(" -> Try: ndbg break src/app.ts:42");
78
+ console.error(" -> Try: agent-dbg break src/app.ts:42 or src/app.ts:42:5");
54
79
  return 1;
55
80
  }
81
+ file = parsed.file;
82
+ line = parsed.line;
83
+ column = parsed.column;
56
84
  }
57
85
 
58
86
  const condition = typeof args.flags.condition === "string" ? args.flags.condition : undefined;
@@ -85,7 +113,7 @@ registerCommand("break", async (args) => {
85
113
  if (args.global.json) {
86
114
  console.log(JSON.stringify(data, null, 2));
87
115
  } else {
88
- const loc = `${data.location.url}:${data.location.line}`;
116
+ const loc = `${shortPath(data.location.url)}:${data.location.line}`;
89
117
  console.log(`${data.ref} set at ${loc} (log: ${logTemplate})`);
90
118
  }
91
119
 
@@ -97,6 +125,7 @@ registerCommand("break", async (args) => {
97
125
  line,
98
126
  condition,
99
127
  hitCount,
128
+ column,
100
129
  };
101
130
  if (patternFlag) {
102
131
  breakArgs.urlRegex = file;
@@ -118,7 +147,7 @@ registerCommand("break", async (args) => {
118
147
  if (args.global.json) {
119
148
  console.log(JSON.stringify(data, null, 2));
120
149
  } else {
121
- const loc = `${data.location.url}:${data.location.line}`;
150
+ const loc = `${shortPath(data.location.url)}:${data.location.line}`;
122
151
  let msg = `${data.ref} set at ${loc}`;
123
152
  if (condition) {
124
153
  msg += ` (condition: ${condition})`;
@@ -6,14 +6,14 @@ registerCommand("breakable", async (args) => {
6
6
 
7
7
  if (!DaemonClient.isRunning(session)) {
8
8
  console.error(`No active session "${session}"`);
9
- console.error(" -> Try: ndbg launch --brk node app.js");
9
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
10
10
  return 1;
11
11
  }
12
12
 
13
13
  const target = args.subcommand;
14
14
  if (!target) {
15
15
  console.error("No target specified");
16
- console.error(" -> Try: ndbg breakable src/app.ts:10-20");
16
+ console.error(" -> Try: agent-dbg breakable src/app.ts:10-20");
17
17
  return 1;
18
18
  }
19
19
 
@@ -8,14 +8,14 @@ registerCommand("catch", async (args) => {
8
8
 
9
9
  if (!DaemonClient.isRunning(session)) {
10
10
  console.error(`No active session "${session}"`);
11
- console.error(" -> Try: ndbg launch --brk node app.js");
11
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
12
12
  return 1;
13
13
  }
14
14
 
15
15
  const mode = args.subcommand ?? "all";
16
16
  if (!VALID_MODES.has(mode)) {
17
17
  console.error(`Invalid catch mode: "${mode}"`);
18
- console.error(" -> Try: ndbg catch [all | uncaught | caught | none]");
18
+ console.error(" -> Try: agent-dbg catch [all | uncaught | caught | none]");
19
19
  return 1;
20
20
  }
21
21
 
@@ -15,7 +15,7 @@ registerCommand("console", async (args) => {
15
15
 
16
16
  if (!DaemonClient.isRunning(session)) {
17
17
  console.error(`No active session "${session}"`);
18
- console.error(" -> Try: ndbg launch --brk node app.js");
18
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
19
19
  return 1;
20
20
  }
21
21
 
@@ -1,13 +1,14 @@
1
1
  import { registerCommand } from "../cli/registry.ts";
2
2
  import { DaemonClient } from "../daemon/client.ts";
3
- import type { SessionStatus } from "../daemon/session.ts";
3
+ import type { StateSnapshot } from "../daemon/session.ts";
4
+ import { printState } from "./print-state.ts";
4
5
 
5
6
  registerCommand("continue", async (args) => {
6
7
  const session = args.global.session;
7
8
 
8
9
  if (!DaemonClient.isRunning(session)) {
9
10
  console.error(`No active session "${session}"`);
10
- console.error(" -> Try: ndbg launch --brk node app.js");
11
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
11
12
  return 1;
12
13
  }
13
14
 
@@ -20,27 +21,13 @@ registerCommand("continue", async (args) => {
20
21
  return 1;
21
22
  }
22
23
 
23
- const data = response.data as SessionStatus;
24
+ const data = response.data as StateSnapshot;
24
25
 
25
26
  if (args.global.json) {
26
27
  console.log(JSON.stringify(data, null, 2));
27
28
  } else {
28
- printStatus(data);
29
+ printState(data);
29
30
  }
30
31
 
31
32
  return 0;
32
33
  });
33
-
34
- function printStatus(data: SessionStatus): void {
35
- if (data.state === "paused" && data.pauseInfo) {
36
- const col = data.pauseInfo.column !== undefined ? `:${data.pauseInfo.column + 1}` : "";
37
- const loc = data.pauseInfo.url
38
- ? `${data.pauseInfo.url}:${(data.pauseInfo.line ?? 0) + 1}${col}`
39
- : "unknown";
40
- console.log(`Paused at ${loc} (${data.pauseInfo.reason})`);
41
- } else if (data.state === "running") {
42
- console.log("Running");
43
- } else {
44
- console.log(`${data.state}`);
45
- }
46
- }
@@ -6,7 +6,7 @@ registerCommand("eval", async (args) => {
6
6
 
7
7
  if (!DaemonClient.isRunning(session)) {
8
8
  console.error(`No active session "${session}"`);
9
- console.error(" -> Try: ndbg launch --brk node app.js");
9
+ console.error(" -> Try: agent-dbg launch --brk node app.js");
10
10
  return 1;
11
11
  }
12
12
 
@@ -20,7 +20,7 @@ registerCommand("eval", async (args) => {
20
20
 
21
21
  if (!expression) {
22
22
  console.error("No expression specified");
23
- console.error(" -> Try: ndbg eval 1 + 2");
23
+ console.error(" -> Try: agent-dbg eval 1 + 2");
24
24
  return 1;
25
25
  }
26
26