browser-debugger-cli 0.11.0 → 0.13.0

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 (222) hide show
  1. package/.claude/skills/bdg/SKILL.md +4 -4
  2. package/README.md +143 -79
  3. package/dist/commands/cdp.d.ts +22 -1
  4. package/dist/commands/cdp.js +100 -43
  5. package/dist/commands/console.d.ts +12 -0
  6. package/dist/commands/console.js +62 -12
  7. package/dist/commands/css.d.ts +13 -0
  8. package/dist/commands/css.js +53 -0
  9. package/dist/commands/dom/DomElementResolver.d.ts +3 -1
  10. package/dist/commands/dom/DomElementResolver.js +10 -3
  11. package/dist/commands/dom/a11y.js +3 -2
  12. package/dist/commands/dom/audit.d.ts +14 -0
  13. package/dist/commands/dom/audit.js +87 -0
  14. package/dist/commands/dom/eval.d.ts +3 -2
  15. package/dist/commands/dom/eval.js +11 -5
  16. package/dist/commands/dom/form.js +10 -9
  17. package/dist/commands/dom/formInteraction.js +42 -11
  18. package/dist/commands/dom/get.js +8 -8
  19. package/dist/commands/dom/helpers/index.d.ts +1 -1
  20. package/dist/commands/dom/helpers/index.js +1 -1
  21. package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
  22. package/dist/commands/dom/helpers/keyAttributes.js +6 -4
  23. package/dist/commands/dom/helpers/query.d.ts +27 -3
  24. package/dist/commands/dom/helpers/query.js +152 -64
  25. package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
  26. package/dist/commands/dom/helpers/screenshot.js +169 -49
  27. package/dist/commands/dom/index.js +7 -2
  28. package/dist/commands/dom/query.d.ts +19 -2
  29. package/dist/commands/dom/query.js +37 -6
  30. package/dist/commands/dom/screenshot.js +12 -7
  31. package/dist/commands/dom/semanticUtils.d.ts +3 -2
  32. package/dist/commands/dom/semanticUtils.js +40 -9
  33. package/dist/commands/dom/wait.js +5 -3
  34. package/dist/commands/helpJson.d.ts +82 -19
  35. package/dist/commands/helpJson.js +112 -41
  36. package/dist/commands/helpTopic.d.ts +16 -1
  37. package/dist/commands/helpTopic.js +59 -1
  38. package/dist/commands/installSkill.d.ts +15 -5
  39. package/dist/commands/installSkill.js +86 -16
  40. package/dist/commands/network/list.js +22 -12
  41. package/dist/commands/optionBehaviors.js +53 -16
  42. package/dist/commands/page.js +7 -4
  43. package/dist/commands/peek.d.ts +7 -0
  44. package/dist/commands/peek.js +65 -23
  45. package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
  46. package/dist/commands/shared/daemonErrorHandler.js +20 -9
  47. package/dist/commands/shared/dataFetcher.d.ts +12 -4
  48. package/dist/commands/shared/dataFetcher.js +12 -4
  49. package/dist/commands/shared/followMode.d.ts +9 -1
  50. package/dist/commands/shared/followMode.js +22 -4
  51. package/dist/commands/shared/optionTypes.d.ts +9 -2
  52. package/dist/commands/shared/outputFile.js +6 -1
  53. package/dist/commands/start.d.ts +20 -5
  54. package/dist/commands/start.js +84 -23
  55. package/dist/commands/stop.d.ts +11 -0
  56. package/dist/commands/stop.js +24 -1
  57. package/dist/commands/tail.d.ts +7 -1
  58. package/dist/commands/tail.js +13 -62
  59. package/dist/commands.js +2 -0
  60. package/dist/connection/cdp.d.ts +7 -0
  61. package/dist/connection/cdp.js +9 -0
  62. package/dist/connection/launcher.js +3 -2
  63. package/dist/daemon/SessionController.js +6 -1
  64. package/dist/daemon/launcher.d.ts +3 -2
  65. package/dist/daemon/launcher.js +47 -3
  66. package/dist/daemon/session/Session.d.ts +4 -1
  67. package/dist/daemon/session/Session.js +33 -2
  68. package/dist/daemon/session/TelemetryStore.d.ts +8 -1
  69. package/dist/daemon/session/TelemetryStore.js +13 -1
  70. package/dist/daemon/session/commandRegistry.js +36 -14
  71. package/dist/daemon/session/interactions.d.ts +2 -1
  72. package/dist/daemon/session/interactions.js +13 -1
  73. package/dist/daemon/session/plugins.js +19 -53
  74. package/dist/daemon/session/teardown.js +1 -1
  75. package/dist/daemon.js +9234 -7222
  76. package/dist/errors/messages.d.ts +88 -15
  77. package/dist/errors/messages.js +177 -27
  78. package/dist/index.js +19322 -13961
  79. package/dist/ipc/client.d.ts +22 -2
  80. package/dist/ipc/client.js +34 -5
  81. package/dist/ipc/protocol/auditTypes.d.ts +135 -0
  82. package/dist/ipc/protocol/auditTypes.js +6 -0
  83. package/dist/ipc/protocol/commands.d.ts +35 -0
  84. package/dist/ipc/protocol/commands.js +2 -0
  85. package/dist/ipc/protocol/domTypes.d.ts +16 -0
  86. package/dist/ipc/protocol/inspectTypes.d.ts +73 -8
  87. package/dist/ipc/session/types.d.ts +2 -0
  88. package/dist/runtime/css/search.d.ts +39 -0
  89. package/dist/runtime/css/search.js +122 -0
  90. package/dist/runtime/dom/actionEffects.d.ts +9 -2
  91. package/dist/runtime/dom/actionEffects.js +30 -14
  92. package/dist/runtime/dom/audit.d.ts +19 -0
  93. package/dist/runtime/dom/audit.js +37 -0
  94. package/dist/runtime/dom/auditModel.d.ts +45 -0
  95. package/dist/runtime/dom/auditModel.js +220 -0
  96. package/dist/runtime/dom/auditScripts.d.ts +113 -0
  97. package/dist/runtime/dom/auditScripts.js +148 -0
  98. package/dist/runtime/dom/elementGeometry.d.ts +16 -3
  99. package/dist/runtime/dom/elementGeometry.js +49 -10
  100. package/dist/runtime/dom/elementInfo.d.ts +74 -17
  101. package/dist/runtime/dom/elementInfo.js +187 -34
  102. package/dist/runtime/dom/evalHelpers.d.ts +12 -2
  103. package/dist/runtime/dom/evalHelpers.js +67 -7
  104. package/dist/runtime/dom/formDiscovery.d.ts +6 -2
  105. package/dist/runtime/dom/formDiscovery.js +20 -3
  106. package/dist/runtime/dom/formFillHelpers/fill.js +8 -12
  107. package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
  108. package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
  109. package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
  110. package/dist/runtime/dom/formSubmitHelpers.js +4 -3
  111. package/dist/runtime/dom/frameLayout.js +1 -0
  112. package/dist/runtime/dom/inspect.d.ts +7 -0
  113. package/dist/runtime/dom/inspect.js +92 -28
  114. package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
  115. package/dist/runtime/dom/inspectAllStyles.js +90 -7
  116. package/dist/runtime/dom/inspectCascade.d.ts +19 -2
  117. package/dist/runtime/dom/inspectCascade.js +214 -44
  118. package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
  119. package/dist/runtime/dom/inspectCascadeModel.js +108 -34
  120. package/dist/runtime/dom/inspectHints.d.ts +26 -3
  121. package/dist/runtime/dom/inspectHints.js +125 -9
  122. package/dist/runtime/dom/inspectModel.d.ts +5 -1
  123. package/dist/runtime/dom/inspectModel.js +37 -10
  124. package/dist/runtime/dom/inspectPaintModel.d.ts +50 -22
  125. package/dist/runtime/dom/inspectPaintModel.js +182 -68
  126. package/dist/runtime/dom/inspectRules.d.ts +19 -0
  127. package/dist/runtime/dom/inspectRules.js +21 -5
  128. package/dist/runtime/dom/inspectScripts.d.ts +112 -12
  129. package/dist/runtime/dom/inspectScripts.js +357 -32
  130. package/dist/runtime/dom/inspectTree.js +10 -2
  131. package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
  132. package/dist/runtime/dom/inspectWhyModel.js +52 -10
  133. package/dist/runtime/dom/layout.js +40 -16
  134. package/dist/runtime/dom/reactEventHelpers.d.ts +21 -4
  135. package/dist/runtime/dom/reactEventHelpers.js +90 -36
  136. package/dist/runtime/dom/targetNode.d.ts +18 -5
  137. package/dist/runtime/dom/targetNode.js +268 -8
  138. package/dist/runtime/dom/wait.js +2 -1
  139. package/dist/runtime/page/bdgWorld.d.ts +57 -0
  140. package/dist/runtime/page/bdgWorld.js +180 -0
  141. package/dist/runtime/page/emulation.d.ts +13 -4
  142. package/dist/runtime/page/emulation.js +69 -4
  143. package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
  144. package/dist/runtime/page/replacedBuiltins.js +136 -0
  145. package/dist/runtime/page/userAgent.d.ts +17 -0
  146. package/dist/runtime/page/userAgent.js +57 -0
  147. package/dist/session/QueryCacheManager.d.ts +4 -1
  148. package/dist/session/QueryCacheManager.js +5 -2
  149. package/dist/session/chrome.d.ts +4 -1
  150. package/dist/session/chrome.js +7 -1
  151. package/dist/session/cleanup/staleSession.d.ts +21 -4
  152. package/dist/session/cleanup/staleSession.js +79 -9
  153. package/dist/session/cleanup/userCommands.d.ts +4 -1
  154. package/dist/session/cleanup/userCommands.js +10 -5
  155. package/dist/session/daemonSocket.d.ts +10 -0
  156. package/dist/session/daemonSocket.js +22 -0
  157. package/dist/session/lastSession.d.ts +6 -3
  158. package/dist/session/lastSession.js +11 -5
  159. package/dist/session/paths.d.ts +3 -1
  160. package/dist/session/paths.js +5 -5
  161. package/dist/session/portClaims.js +4 -3
  162. package/dist/session/sessionList.d.ts +13 -5
  163. package/dist/session/sessionList.js +31 -7
  164. package/dist/telemetry/a11y.js +2 -2
  165. package/dist/telemetry/console.d.ts +2 -1
  166. package/dist/telemetry/console.js +30 -21
  167. package/dist/telemetry/pageCrash.d.ts +26 -0
  168. package/dist/telemetry/pageCrash.js +53 -0
  169. package/dist/types.d.ts +20 -0
  170. package/dist/ui/formatters/audit.d.ts +19 -0
  171. package/dist/ui/formatters/audit.js +115 -0
  172. package/dist/ui/formatters/cdp.d.ts +138 -0
  173. package/dist/ui/formatters/cdp.js +131 -0
  174. package/dist/ui/formatters/console/chronological.js +3 -1
  175. package/dist/ui/formatters/console/follow.d.ts +2 -1
  176. package/dist/ui/formatters/console/follow.js +2 -2
  177. package/dist/ui/formatters/console/json.d.ts +2 -2
  178. package/dist/ui/formatters/console/json.js +11 -5
  179. package/dist/ui/formatters/console/shared.d.ts +30 -0
  180. package/dist/ui/formatters/console/shared.js +16 -0
  181. package/dist/ui/formatters/console/summarize.d.ts +9 -2
  182. package/dist/ui/formatters/console/summarize.js +40 -9
  183. package/dist/ui/formatters/console.d.ts +2 -1
  184. package/dist/ui/formatters/console.js +7 -5
  185. package/dist/ui/formatters/details.js +3 -1
  186. package/dist/ui/formatters/dom.d.ts +2 -2
  187. package/dist/ui/formatters/dom.js +10 -8
  188. package/dist/ui/formatters/helpFormatters.js +1 -1
  189. package/dist/ui/formatters/inspect.js +50 -17
  190. package/dist/ui/formatters/installSkill.d.ts +9 -1
  191. package/dist/ui/formatters/installSkill.js +32 -6
  192. package/dist/ui/formatters/layout.js +2 -1
  193. package/dist/ui/formatters/networkList.d.ts +1 -1
  194. package/dist/ui/formatters/networkList.js +1 -2
  195. package/dist/ui/formatters/preview.d.ts +2 -0
  196. package/dist/ui/formatters/preview.js +17 -7
  197. package/dist/ui/formatters/sessions.d.ts +2 -2
  198. package/dist/ui/formatters/sessions.js +9 -2
  199. package/dist/ui/formatters/status.js +1 -1
  200. package/dist/ui/logging/logger.d.ts +1 -1
  201. package/dist/ui/messages/commands.d.ts +168 -11
  202. package/dist/ui/messages/commands.js +245 -18
  203. package/dist/ui/messages/consoleMessages.d.ts +24 -0
  204. package/dist/ui/messages/consoleMessages.js +32 -0
  205. package/dist/ui/messages/preview.d.ts +12 -0
  206. package/dist/ui/messages/preview.js +18 -2
  207. package/dist/ui/messages/session.d.ts +13 -2
  208. package/dist/ui/messages/session.js +22 -3
  209. package/dist/utils/cssValues.js +36 -4
  210. package/dist/utils/decisionTrees.js +0 -5
  211. package/dist/utils/directories.d.ts +34 -0
  212. package/dist/utils/directories.js +88 -0
  213. package/dist/utils/display.d.ts +16 -0
  214. package/dist/utils/display.js +42 -0
  215. package/dist/utils/exitCodes.d.ts +1 -0
  216. package/dist/utils/exitCodes.js +6 -0
  217. package/dist/utils/process.d.ts +12 -0
  218. package/dist/utils/process.js +25 -0
  219. package/dist/utils/suggestions.d.ts +4 -2
  220. package/dist/utils/suggestions.js +7 -5
  221. package/dist/utils/taskMappings.js +1 -1
  222. package/package.json +3 -2
@@ -5,7 +5,7 @@
5
5
  * their surrounding DOM context and to fall back gracefully when only one
6
6
  * of the two data sources is available.
7
7
  */
8
- import { MASKED_VALUE } from '../../runtime/dom/elementInfo.js';
8
+ import { MASKED_VALUE, isLabelClass } from '../../runtime/dom/elementInfo.js';
9
9
  import { synthesizeA11yNode } from '../../telemetry/roleInference.js';
10
10
  import { keyAttributeItems } from '../../ui/formatters/keyAttributes.js';
11
11
  import { joinLines } from '../../ui/formatting.js';
@@ -29,9 +29,8 @@ function buildContextText(node, domContext) {
29
29
  }
30
30
  if (domContext) {
31
31
  const tagPart = `<${domContext.tag}`;
32
- const classPart = domContext.classes && domContext.classes.length > 0
33
- ? `.${domContext.classes.slice(0, 3).join('.')}`
34
- : '';
32
+ const classes = (domContext.classes ?? []).filter(isLabelClass).slice(0, 3);
33
+ const classPart = classes.length > 0 ? `.${classes.join('.')}` : '';
35
34
  const previewPart = domContext.preview && !domContext.text ? ` "${domContext.preview}"` : '';
36
35
  return ` ${tagPart}${classPart}>${previewPart}`;
37
36
  }
@@ -86,10 +85,11 @@ function buildPropertiesText(node) {
86
85
  * link's href, a field's type and name), like `dom query` does, and is
87
86
  * followed by up to 500 characters of the element's text
88
87
  * (all of it with `dom get --full`) when it is longer than the one-line
89
- * preview, or, for an element without text or name, what it holds.
88
+ * preview or the role line shows an accessible name other than the text,
89
+ * or, for an element without text or name, what it holds.
90
90
  *
91
91
  * @param data - Accessibility node and optional DOM context
92
- * @returns Role line, plus a text line for elements with longer text
92
+ * @returns Role line, plus a text line for elements with longer or differently named text
93
93
  */
94
94
  export function formatSemanticNodeWithContext(data) {
95
95
  const { node, domContext } = data;
@@ -99,13 +99,44 @@ export function formatSemanticNodeWithContext(data) {
99
99
  const propsText = buildPropertiesText(node);
100
100
  const inferredText = node.inferred ? ' (inferred from DOM)' : '';
101
101
  const line = `${roleText}${contextText}${keysText}${propsText}${inferredText}`;
102
- if (domContext?.text)
103
- return joinLines(line, elementTextLine(domContext.text));
102
+ const text = textNotOnRoleLine(node, domContext);
103
+ if (text)
104
+ return joinLines(line, elementTextLine(text));
104
105
  if (domContext?.childCount !== undefined && !node.name) {
105
- return joinLines(line, emptyElementLine(domContext.children ?? [], domContext.childCount));
106
+ return joinLines(line, emptyElementLine(domContext.children ?? [], domContext.childCount, domContext.shadowChildren));
106
107
  }
107
108
  return line;
108
109
  }
110
+ /**
111
+ * The element's text when the role line does not show it: text longer than
112
+ * the one-line preview, or the visible text of an element whose accessible
113
+ * name is something else (an editor named by its aria-label), unless its
114
+ * name or value shows that text (a long heading with inline children is
115
+ * named by all of it). Texts are compared ignoring case and whitespace.
116
+ * A sensitive field's text is never shown.
117
+ *
118
+ * @param node - Accessibility node
119
+ * @param domContext - DOM context with the text
120
+ * @returns Text for the text line, or undefined
121
+ */
122
+ function textNotOnRoleLine(node, domContext) {
123
+ if (domContext?.sensitive)
124
+ return undefined;
125
+ const text = domContext?.text ?? (node.name ? domContext?.preview : undefined);
126
+ if (!text)
127
+ return undefined;
128
+ const shown = [node.name, node.value].map((value) => comparableText(value ?? ''));
129
+ return shown.includes(comparableText(text)) ? undefined : text;
130
+ }
131
+ /**
132
+ * Text in the form texts are compared in.
133
+ *
134
+ * @param text - Text
135
+ * @returns Lowercased text with whitespace collapsed
136
+ */
137
+ function comparableText(text) {
138
+ return text.replace(/\s+/g, ' ').trim().toLowerCase();
139
+ }
109
140
  /**
110
141
  * Resolve an a11y node, falling back to a DOM-synthesized one when only
111
142
  * DOM context is available. Returns null when neither source can produce one.
@@ -24,7 +24,7 @@ export function registerWaitCommand(dom) {
24
24
  dom
25
25
  .command('wait')
26
26
  .description('Wait until elements appear, become visible, contain a text or are gone (or the page loads)')
27
- .argument('[selector]', 'CSS selector (:has-text, :visible allowed; shadow DOM and same-origin iframes searched); optional with --load')
27
+ .argument('[selector]', 'CSS selector (:has-text, :visible allowed; shadow DOM and same-origin iframes searched); without one, waits for the page to load (--load)')
28
28
  .option('--text <text>', 'A match must contain this text (case-insensitive)')
29
29
  .option('--visible', 'Only count visible matches')
30
30
  .option('--gone', 'Wait until no element matches (none visible, with --visible)')
@@ -44,7 +44,9 @@ export function registerWaitCommand(dom) {
44
44
  * @returns Command result
45
45
  */
46
46
  async function waitFor(selector, options) {
47
- const needsSelector = options.text !== undefined || options.gone === true || !options.load;
47
+ const load = options.load === true ||
48
+ (selector === undefined && options.text === undefined && !options.gone && !options.visible);
49
+ const needsSelector = options.text !== undefined || options.gone === true || !load;
48
50
  if (selector === undefined && needsSelector) {
49
51
  const err = waitTargetRequiredError();
50
52
  return {
@@ -58,7 +60,7 @@ async function waitFor(selector, options) {
58
60
  ...filterDefined({ selector, text: options.text }),
59
61
  ...(options.gone && { gone: true }),
60
62
  ...(options.visible && { visible: true }),
61
- ...(options.load && { load: true }),
63
+ ...(load && { load: true }),
62
64
  timeout: options.timeout,
63
65
  });
64
66
  if (response.status === 'error' || !response.data) {
@@ -78,9 +78,30 @@ export interface CommandMetadata {
78
78
  arguments: ArgumentMetadata[];
79
79
  /** Command options */
80
80
  options: OptionMetadata[];
81
+ /** Text shown after the options in `--help` (examples, output legend) */
82
+ helpText?: string;
81
83
  /** Subcommands */
82
84
  subcommands: CommandMetadata[];
83
85
  }
86
+ /**
87
+ * Command summary for the compact root help: one-line description, arguments
88
+ * and flags with their descriptions (behaviors, defaults and choices are in
89
+ * `bdg <command> --help --json`).
90
+ */
91
+ export interface CompactCommand {
92
+ /** Command name */
93
+ name: string;
94
+ /** Command aliases (only when it has some) */
95
+ aliases?: readonly string[];
96
+ /** First line of the description */
97
+ description: string;
98
+ /** Arguments as in usage, e.g. "<selector> [index]" (only when it takes some) */
99
+ arguments?: string;
100
+ /** Visible options: flags to description */
101
+ options?: Record<string, string>;
102
+ /** Subcommands (only for command groups) */
103
+ subcommands?: CompactCommand[];
104
+ }
84
105
  /**
85
106
  * Runtime state information for dynamic command availability.
86
107
  */
@@ -112,7 +133,7 @@ export interface Capabilities {
112
133
  };
113
134
  }
114
135
  /**
115
- * Root machine-readable help structure.
136
+ * Root machine-readable help structure (`bdg --help --json --full`).
116
137
  */
117
138
  export interface MachineReadableHelp {
118
139
  /** CLI name */
@@ -142,10 +163,34 @@ export interface MachineReadableHelp {
142
163
  capabilities: Capabilities;
143
164
  }
144
165
  /**
145
- * Generates machine-readable help from a Commander program.
166
+ * Compact root help (`bdg --help --json`): the full help with a command tree
167
+ * of names, one-line descriptions and flags.
168
+ */
169
+ export interface CompactHelp extends Omit<MachineReadableHelp, 'command'> {
170
+ /** Where the details are */
171
+ details: string;
172
+ /** Root command summary */
173
+ command: CompactCommand;
174
+ }
175
+ /**
176
+ * Help for one command (`bdg <command> --help --json`): its full metadata
177
+ * (option behaviors, defaults, choices, help text), its subcommands in compact
178
+ * form, and the exit codes.
179
+ */
180
+ export interface CommandHelp extends Pick<MachineReadableHelp, 'name' | 'version' | 'description' | 'exitCodes'> {
181
+ /** Full command path, e.g. "bdg dom query" */
182
+ path: string;
183
+ /** Command metadata; subcommands summarized (ask each for its details) */
184
+ command: Omit<CommandMetadata, 'subcommands'> & {
185
+ subcommands: CompactCommand[];
186
+ };
187
+ }
188
+ /**
189
+ * Generates the full machine-readable help from a Commander program
190
+ * (`bdg --help --json --full`).
146
191
  *
147
192
  * Includes comprehensive metadata for agent discovery:
148
- * - Command structure and options
193
+ * - Command structure and options with behaviors
149
194
  * - Exit codes with semantic meanings
150
195
  * - Task-to-command mappings with CDP alternatives
151
196
  * - Runtime state and command availability
@@ -154,34 +199,52 @@ export interface MachineReadableHelp {
154
199
  *
155
200
  * @param program - Commander program instance
156
201
  * @returns Machine-readable help structure
202
+ */
203
+ export declare function generateMachineReadableHelp(program: Command): MachineReadableHelp;
204
+ /**
205
+ * Generates the compact root help (`bdg --help --json`): the full help with
206
+ * the command tree reduced to names, one-line descriptions and flags.
207
+ *
208
+ * @param program - Commander program instance
209
+ * @returns Compact help structure
210
+ */
211
+ export declare function generateCompactHelp(program: Command): CompactHelp;
212
+ /**
213
+ * The command a command line addresses: follows the words that name
214
+ * subcommands and skips the others (option values, arguments), stopping at a
215
+ * command without subcommands.
216
+ *
217
+ * @param program - Root Commander program instance
218
+ * @param words - Command-line words, e.g. ['dom', 'query', '.item']
219
+ * @returns The addressed command (the program when no word names one)
157
220
  *
158
221
  * @example
159
222
  * ```typescript
160
- * import { program } from 'commander';
161
- * import { generateMachineReadableHelp } from './help/machineReadableHelp.js';
162
- *
163
- * const help = generateMachineReadableHelp(program);
164
- * console.log(JSON.stringify(help, null, 2));
223
+ * resolveCommand(program, ['--session', 'a', 'dom', 'query', '.item']).name(); // 'query'
165
224
  * ```
166
225
  */
167
- export declare function generateMachineReadableHelp(program: Command): MachineReadableHelp;
226
+ export declare function resolveCommand(program: Command, words: string[]): Command;
168
227
  /**
169
- * Generates machine-readable help for a specific subcommand.
228
+ * Full command path, e.g. "bdg dom query".
170
229
  *
171
- * Returns the same structure as generateMachineReadableHelp but with
172
- * the command field focused on the requested subcommand. If the subcommand
173
- * is not found, falls back to full root help.
230
+ * @param command - Commander command instance
231
+ * @returns Names from the program down to the command
232
+ */
233
+ export declare function commandPath(command: Command): string;
234
+ /**
235
+ * Generates machine-readable help for one command: its full metadata,
236
+ * compact subcommands (a group lists them like the root help does) and the
237
+ * exit codes.
174
238
  *
175
239
  * @param program - Root Commander program instance
176
- * @param commandPath - Array of command names (e.g., ['dom', 'query'])
177
- * @returns Machine-readable help structure for the subcommand
240
+ * @param command - The command (from {@link resolveCommand})
241
+ * @returns Help for the command
178
242
  *
179
243
  * @example
180
244
  * ```typescript
181
- * // Get help for 'bdg dom query'
182
- * const help = generateSubcommandHelp(program, ['dom', 'query']);
183
- * console.log(help.command.name); // 'query'
245
+ * const help = generateCommandHelp(program, resolveCommand(program, ['dom', 'query']));
246
+ * console.log(help.path); // 'bdg dom query'
184
247
  * ```
185
248
  */
186
- export declare function generateSubcommandHelp(program: Command, commandPath: string[]): MachineReadableHelp;
249
+ export declare function generateCommandHelp(program: Command, command: Command): CommandHelp;
187
250
  //# sourceMappingURL=helpJson.d.ts.map
@@ -4,6 +4,7 @@
4
4
  import { getAllDomainSummaries } from '../cdp/schema.js';
5
5
  import { getOptionBehavior } from './optionBehaviors.js';
6
6
  import { readLiveDaemonPid } from '../session/cleanup/staleSession.js';
7
+ import { helpJsonDetailsNote } from '../ui/messages/commands.js';
7
8
  import { getAllDecisionTrees } from '../utils/decisionTrees.js';
8
9
  import { EXIT_CODE_REGISTRY } from '../utils/exitCodes.js';
9
10
  import { getAllTaskMappings } from '../utils/taskMappings.js';
@@ -65,6 +66,21 @@ function convertArgument(argument) {
65
66
  }
66
67
  return metadata;
67
68
  }
69
+ /**
70
+ * The text a command adds after its options in `--help` (examples, output
71
+ * legend), collected from its `afterHelp` listeners. Commander's Command is an
72
+ * EventEmitter at runtime; its typings leave that out.
73
+ *
74
+ * @param command - Commander command instance
75
+ * @returns The text, or undefined when the command adds none
76
+ */
77
+ function afterHelpText(command) {
78
+ const chunks = [];
79
+ const emitter = command;
80
+ emitter.emit('afterHelp', { error: false, command, write: (text) => chunks.push(text) });
81
+ const text = chunks.join('').trim();
82
+ return text || undefined;
83
+ }
68
84
  /**
69
85
  * Recursively converts a Commander Command to CommandMetadata.
70
86
  *
@@ -75,6 +91,7 @@ function convertArgument(argument) {
75
91
  */
76
92
  function convertCommand(command) {
77
93
  const commandName = command.name();
94
+ const helpText = afterHelpText(command);
78
95
  return {
79
96
  name: commandName,
80
97
  aliases: command.aliases(),
@@ -82,9 +99,45 @@ function convertCommand(command) {
82
99
  usage: command.usage(),
83
100
  arguments: command.registeredArguments.map(convertArgument),
84
101
  options: command.options.map((opt) => convertOption(opt, commandName)),
102
+ ...(helpText && { helpText }),
85
103
  subcommands: command.commands.map(convertCommand),
86
104
  };
87
105
  }
106
+ /**
107
+ * An argument as written in usage: `<name>`, `[name]`, `<name...>`.
108
+ *
109
+ * @param argument - Commander argument instance
110
+ * @returns Usage term
111
+ */
112
+ function argumentTerm(argument) {
113
+ const name = `${argument.name()}${argument.variadic ? '...' : ''}`;
114
+ return argument.required ? `<${name}>` : `[${name}]`;
115
+ }
116
+ /**
117
+ * Recursively converts a Commander Command to its compact summary: first
118
+ * description line, arguments, and visible options with their descriptions.
119
+ * Empty fields are left out.
120
+ *
121
+ * @param command - Commander command instance
122
+ * @returns Compact command summary
123
+ */
124
+ function convertCompactCommand(command) {
125
+ const aliases = command.aliases();
126
+ const args = command.registeredArguments.map(argumentTerm).join(' ');
127
+ const options = command.options.filter((option) => !option.hidden);
128
+ return {
129
+ name: command.name(),
130
+ ...(aliases.length > 0 && { aliases }),
131
+ description: command.description().split('\n')[0] ?? '',
132
+ ...(args && { arguments: args }),
133
+ ...(options.length > 0 && {
134
+ options: Object.fromEntries(options.map((option) => [option.flags, option.description])),
135
+ }),
136
+ ...(command.commands.length > 0 && {
137
+ subcommands: command.commands.map(convertCompactCommand),
138
+ }),
139
+ };
140
+ }
88
141
  /**
89
142
  * Generates runtime state information.
90
143
  *
@@ -97,7 +150,7 @@ function convertCommand(command) {
97
150
  function generateRuntimeState() {
98
151
  const sessionActive = readLiveDaemonPid() !== null;
99
152
  const availableCommands = sessionActive
100
- ? ['peek', 'tail', 'details', 'dom', 'network', 'console', 'cdp', 'status', 'sessions', 'stop']
153
+ ? ['peek', 'details', 'dom', 'network', 'console', 'cdp', 'status', 'sessions', 'stop']
101
154
  : ['bdg <url>', 'sessions', 'cleanup', '--help', '--version'];
102
155
  return {
103
156
  sessionActive,
@@ -150,10 +203,11 @@ function generateCapabilities() {
150
203
  };
151
204
  }
152
205
  /**
153
- * Generates machine-readable help from a Commander program.
206
+ * Generates the full machine-readable help from a Commander program
207
+ * (`bdg --help --json --full`).
154
208
  *
155
209
  * Includes comprehensive metadata for agent discovery:
156
- * - Command structure and options
210
+ * - Command structure and options with behaviors
157
211
  * - Exit codes with semantic meanings
158
212
  * - Task-to-command mappings with CDP alternatives
159
213
  * - Runtime state and command availability
@@ -162,15 +216,6 @@ function generateCapabilities() {
162
216
  *
163
217
  * @param program - Commander program instance
164
218
  * @returns Machine-readable help structure
165
- *
166
- * @example
167
- * ```typescript
168
- * import { program } from 'commander';
169
- * import { generateMachineReadableHelp } from './help/machineReadableHelp.js';
170
- *
171
- * const help = generateMachineReadableHelp(program);
172
- * console.log(JSON.stringify(help, null, 2));
173
- * ```
174
219
  */
175
220
  export function generateMachineReadableHelp(program) {
176
221
  return {
@@ -186,56 +231,82 @@ export function generateMachineReadableHelp(program) {
186
231
  };
187
232
  }
188
233
  /**
189
- * Finds a subcommand by traversing the command path.
234
+ * Generates the compact root help (`bdg --help --json`): the full help with
235
+ * the command tree reduced to names, one-line descriptions and flags.
236
+ *
237
+ * @param program - Commander program instance
238
+ * @returns Compact help structure
239
+ */
240
+ export function generateCompactHelp(program) {
241
+ return {
242
+ ...generateMachineReadableHelp(program),
243
+ details: helpJsonDetailsNote(),
244
+ command: convertCompactCommand(program),
245
+ };
246
+ }
247
+ /**
248
+ * The command a command line addresses: follows the words that name
249
+ * subcommands and skips the others (option values, arguments), stopping at a
250
+ * command without subcommands.
190
251
  *
191
252
  * @param program - Root Commander program instance
192
- * @param commandPath - Array of command names to traverse (e.g., ['dom', 'query'])
193
- * @returns The target Command if found, null otherwise
253
+ * @param words - Command-line words, e.g. ['dom', 'query', '.item']
254
+ * @returns The addressed command (the program when no word names one)
255
+ *
256
+ * @example
257
+ * ```typescript
258
+ * resolveCommand(program, ['--session', 'a', 'dom', 'query', '.item']).name(); // 'query'
259
+ * ```
194
260
  */
195
- function findSubcommand(program, commandPath) {
261
+ export function resolveCommand(program, words) {
196
262
  let current = program;
197
- for (const name of commandPath) {
198
- const found = current.commands.find((cmd) => cmd.name() === name || cmd.aliases().includes(name));
199
- if (!found) {
200
- return null;
201
- }
202
- current = found;
263
+ for (const word of words) {
264
+ if (current.commands.length === 0)
265
+ break;
266
+ const found = current.commands.find((cmd) => cmd.name() === word || cmd.aliases().includes(word));
267
+ if (found)
268
+ current = found;
203
269
  }
204
270
  return current;
205
271
  }
206
272
  /**
207
- * Generates machine-readable help for a specific subcommand.
273
+ * Full command path, e.g. "bdg dom query".
208
274
  *
209
- * Returns the same structure as generateMachineReadableHelp but with
210
- * the command field focused on the requested subcommand. If the subcommand
211
- * is not found, falls back to full root help.
275
+ * @param command - Commander command instance
276
+ * @returns Names from the program down to the command
277
+ */
278
+ export function commandPath(command) {
279
+ const names = [];
280
+ for (let level = command; level; level = level.parent)
281
+ names.unshift(level.name());
282
+ return names.join(' ');
283
+ }
284
+ /**
285
+ * Generates machine-readable help for one command: its full metadata,
286
+ * compact subcommands (a group lists them like the root help does) and the
287
+ * exit codes.
212
288
  *
213
289
  * @param program - Root Commander program instance
214
- * @param commandPath - Array of command names (e.g., ['dom', 'query'])
215
- * @returns Machine-readable help structure for the subcommand
290
+ * @param command - The command (from {@link resolveCommand})
291
+ * @returns Help for the command
216
292
  *
217
293
  * @example
218
294
  * ```typescript
219
- * // Get help for 'bdg dom query'
220
- * const help = generateSubcommandHelp(program, ['dom', 'query']);
221
- * console.log(help.command.name); // 'query'
295
+ * const help = generateCommandHelp(program, resolveCommand(program, ['dom', 'query']));
296
+ * console.log(help.path); // 'bdg dom query'
222
297
  * ```
223
298
  */
224
- export function generateSubcommandHelp(program, commandPath) {
225
- const targetCommand = findSubcommand(program, commandPath);
226
- if (!targetCommand) {
227
- return generateMachineReadableHelp(program);
228
- }
299
+ export function generateCommandHelp(program, command) {
229
300
  return {
230
301
  name: program.name(),
231
302
  version: program.version() ?? 'unknown',
232
303
  description: program.description(),
233
- command: convertCommand(targetCommand),
304
+ path: commandPath(command),
305
+ command: {
306
+ ...convertCommand(command),
307
+ subcommands: command.commands.map(convertCompactCommand),
308
+ },
234
309
  exitCodes: [...EXIT_CODE_DOCS],
235
- taskMappings: getAllTaskMappings(),
236
- runtimeState: generateRuntimeState(),
237
- decisionTrees: getAllDecisionTrees(),
238
- capabilities: generateCapabilities(),
239
310
  };
240
311
  }
241
312
  //# sourceMappingURL=helpJson.js.map
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `bdg help <command...>` topics and Commander usage-error hints.
3
3
  */
4
- import type { Command } from 'commander';
4
+ import type { Command, CommanderError } from 'commander';
5
5
  /**
6
6
  * The command path of `bdg help <path...>`: the words before the first option.
7
7
  *
@@ -29,4 +29,19 @@ export declare function splitCommanderHint(text: string): {
29
29
  message: string;
30
30
  suggestion?: string;
31
31
  };
32
+ /**
33
+ * Message and suggestion for a Commander usage error: a did-you-mean for an
34
+ * unknown option or command, otherwise a pointer to the command's `--help`.
35
+ * A word typed after a single dash (`-josn`) is matched against the long
36
+ * options.
37
+ *
38
+ * @param error - Commander error (not a help or version display)
39
+ * @param command - Command the error came from (see resolveCommand)
40
+ * @param argv - Process arguments (to name an option as typed)
41
+ * @returns Message and suggestion
42
+ */
43
+ export declare function usageErrorDetails(error: CommanderError, command: Command, argv?: string[]): {
44
+ message: string;
45
+ suggestion: string;
46
+ };
32
47
  //# sourceMappingURL=helpTopic.d.ts.map
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * `bdg help <command...>` topics and Commander usage-error hints.
3
3
  */
4
+ import { commandPath } from './helpJson.js';
4
5
  import { CommandError } from '../errors/index.js';
5
- import { unknownHelpTopicError } from '../errors/messages.js';
6
+ import { missingSubcommandMessage, unknownHelpTopicError, usageHelpSuggestion, } from '../errors/messages.js';
6
7
  import { EXIT_CODES } from '../utils/exitCodes.js';
7
8
  import { findSimilar } from '../utils/suggestions.js';
8
9
  /** Commander's typo hint on its own line, e.g. "(Did you mean query?)" */
@@ -55,4 +56,61 @@ export function splitCommanderHint(text) {
55
56
  return { message };
56
57
  return { message: message.slice(0, hint.index).trim(), suggestion: `Did you mean: ${hint[1]}?` };
57
58
  }
59
+ /** The option named in Commander's "unknown option '--x'" message, without an `=value` */
60
+ const UNKNOWN_OPTION = /unknown option '([^'=]+)/;
61
+ /**
62
+ * The long option of a command closest to a mistyped one, hidden global
63
+ * options (`--session`, `--quiet`) included. Up to one edit per three letters
64
+ * of the name counts as a typo, so `--frob` does not suggest `--json`.
65
+ *
66
+ * @param flag - Option as typed, e.g. "--sesion"
67
+ * @param command - Command it was given to
68
+ * @returns Closest long option, if any is similar
69
+ */
70
+ function closestOption(flag, command) {
71
+ const longs = command.options.flatMap((option) => (option.long ? [option.long] : []));
72
+ const maxDistance = Math.ceil(flag.replace(/^-+/, '').length / 3);
73
+ return findSimilar(flag, longs, { maxDistance })[0];
74
+ }
75
+ /**
76
+ * The option as typed, for one Commander split: it reads `-josn` as `-j`
77
+ * (`--json`) followed by `-osn`, and reports `-osn` as unknown.
78
+ *
79
+ * @param flag - Option Commander reported, e.g. "-osn"
80
+ * @param argv - Process arguments
81
+ * @returns The argument it came from, e.g. "-josn", else the option itself
82
+ */
83
+ function typedOption(flag, argv) {
84
+ if (flag.startsWith('--'))
85
+ return flag;
86
+ const rest = flag.slice(1);
87
+ return (argv.find((arg) => /^-[^-]/.test(arg) && arg.length > flag.length && arg.endsWith(rest)) ?? flag);
88
+ }
89
+ /**
90
+ * Message and suggestion for a Commander usage error: a did-you-mean for an
91
+ * unknown option or command, otherwise a pointer to the command's `--help`.
92
+ * A word typed after a single dash (`-josn`) is matched against the long
93
+ * options.
94
+ *
95
+ * @param error - Commander error (not a help or version display)
96
+ * @param command - Command the error came from (see resolveCommand)
97
+ * @param argv - Process arguments (to name an option as typed)
98
+ * @returns Message and suggestion
99
+ */
100
+ export function usageErrorDetails(error, command, argv = process.argv) {
101
+ const help = usageHelpSuggestion(commandPath(command));
102
+ if (error.code === 'commander.help') {
103
+ return { message: missingSubcommandMessage(), suggestion: help };
104
+ }
105
+ const { message, suggestion } = splitCommanderHint(error.message);
106
+ const flag = error.code === 'commander.unknownOption' && UNKNOWN_OPTION.exec(message)?.[1];
107
+ if (!flag)
108
+ return { message, suggestion: suggestion ?? help };
109
+ const typed = typedOption(flag, argv);
110
+ const closest = closestOption(/^-[^-]../.test(typed) ? `-${typed}` : typed, command);
111
+ return {
112
+ message: message.replace(`'${flag}'`, `'${typed}'`),
113
+ suggestion: closest ? `Did you mean: ${closest}?` : help,
114
+ };
115
+ }
58
116
  //# sourceMappingURL=helpTopic.js.map
@@ -1,16 +1,26 @@
1
1
  import type { Command } from 'commander';
2
+ import { CommandError } from '../errors/index.js';
2
3
  import type { InstalledSkill, SkillTarget } from '../types.js';
4
+ /** What installing the skill did, and why it failed for a target, if it did */
5
+ export interface SkillInstallResult {
6
+ /** Targets the skill was installed for (or left unchanged) */
7
+ skills: InstalledSkill[];
8
+ /** Error (82) for the targets that could not be written */
9
+ failure?: CommandError;
10
+ }
3
11
  /**
4
- * Copy the bdg skill into each target's skill directory, overwriting an
5
- * older copy.
12
+ * Copy the bdg skill into each target's skill directory. A copy that differs
13
+ * (an older version, or one the user edited) is kept as `SKILL.md.bak`
14
+ * before it is overwritten. A target that cannot be written does not stop
15
+ * the others.
6
16
  *
7
17
  * @param targets - Agents to install for
8
18
  * @param home - Home directory the skill roots are relative to
9
19
  * @param source - SKILL.md to copy
10
- * @returns One entry per target, in the given order
11
- * @throws CommandError when the source is missing (83) or a write fails (82)
20
+ * @returns The targets written, in the given order, and the failure if any
21
+ * @throws CommandError when the source is missing (83)
12
22
  */
13
- export declare function installSkill(targets: SkillTarget[], home?: string, source?: string): InstalledSkill[];
23
+ export declare function installSkill(targets: SkillTarget[], home?: string, source?: string): SkillInstallResult;
14
24
  /**
15
25
  * Register the install-skill command.
16
26
  *