browser-debugger-cli 0.10.0 → 0.11.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.
@@ -0,0 +1,268 @@
1
+ ---
2
+ name: bdg
3
+ description: Use bdg CLI to drive and debug a real Chrome via Chrome DevTools Protocol - navigate, click, fill and submit forms, check what an action changed (navigation, new messages, pending requests), inspect elements without screenshots (box, layout, fonts, colors, a11y), read network requests and console errors, and call any CDP method. Use this skill when you need to verify a UI change in a running app, debug a page, automate a browser flow, or scrape dynamic content.
4
+ ---
5
+
6
+ # bdg - Browser Automation CLI
7
+
8
+ ## Quick Start
9
+
10
+ ```bash
11
+ bdg https://example.com # Start session (launches Chrome)
12
+ bdg dom screenshot /tmp/page.png # Take screenshot
13
+ bdg stop # End session
14
+ ```
15
+
16
+ ## Session Management
17
+
18
+ ```bash
19
+ bdg <url> # Start session (1920x1080, headless if no display)
20
+ bdg <url> --headless # Force headless mode
21
+ bdg <url> --no-headless # Force visible browser window
22
+ bdg status # Check session status
23
+ bdg peek # Preview collected telemetry
24
+ bdg stop # End session (use sparingly)
25
+ bdg cleanup # Clean up after a crashed session
26
+ bdg cleanup --force # Kill a stuck session (daemon + its Chrome)
27
+ ```
28
+
29
+ **Sessions run indefinitely by default** (no timeout). With HMR/hot-reload dev servers, keep the session running:
30
+
31
+ ```bash
32
+ bdg http://localhost:5173 # Start once
33
+ # ... make code changes, HMR updates the page ...
34
+ bdg dom screenshot /tmp/s.png # Check anytime
35
+ bdg peek # Preview collected data
36
+ # No need to stop/restart - Chrome stays on the page
37
+ ```
38
+
39
+ **Don't stop sessions prematurely** - use `bdg peek` to inspect data. Only call `bdg stop` when completely done with browser automation.
40
+
41
+ ## Screenshots
42
+
43
+ Always use `bdg dom screenshot` (raw CDP is blocked):
44
+
45
+ ```bash
46
+ bdg dom screenshot /tmp/page.png # Full page
47
+ bdg dom screenshot /tmp/viewport.png --no-full-page # Viewport only
48
+ bdg dom screenshot /tmp/el.png --selector "#main" # Element only
49
+ bdg dom screenshot /tmp/scroll.png --scroll "#target" # Scroll to element first
50
+ ```
51
+
52
+ ## Actions Report What Changed
53
+
54
+ `dom click`, `fill`, `submit`, `pressKey`, `hover` and `scroll` wait for the requests the action starts, then say what happened. Read this before reaching for a screenshot:
55
+
56
+ ```text
57
+ ✓ Element Clicked
58
+ Page: navigated to https://app.test/secure (200) # navigation (or "URL changed ... (same document)")
59
+ New text: "Your password is invalid!" (div#flash) # alert/status/aria-live messages that appeared
60
+ ⚠ Element Clicked (no visible effect observed ...) # nothing changed - wrong element or a broken handler
61
+ ```
62
+
63
+ - In `--json`: `navigation`, `messages`, `effect: "none"` and pending work (timers, spinners) are fields on `data`.
64
+ - Results the page shows later are not waited for: follow up with `bdg dom wait` (below).
65
+
66
+ ## Form Interaction
67
+
68
+ ```bash
69
+ # Discover forms
70
+ bdg dom form --brief # Quick scan: field names, types, required
71
+
72
+ # Fill and interact
73
+ bdg dom fill "input[name='user']" "myuser" # Fill by selector
74
+ bdg dom fill 0 "value" # Fill by index (from query)
75
+ bdg dom click "button.submit" # Click element
76
+ bdg dom submit "form" --wait-navigation # Submit and wait for page load
77
+ bdg dom pressKey "input" Enter # Press Enter key
78
+
79
+ # Options
80
+ --no-wait # Skip network stability wait
81
+ --wait-navigation # Wait for page navigation (traditional forms)
82
+ --wait-network <ms> # Wait for network idle (SPA forms)
83
+ --index <n> # Select nth element when multiple match
84
+ ```
85
+
86
+ ## DOM Inspection
87
+
88
+ ```bash
89
+ bdg dom query "selector" # Find elements, returns [0], [1], [2]... (0-based)
90
+ bdg dom get "selector" # Get semantic a11y info (token-efficient)
91
+ bdg dom get "selector" --raw # Get full HTML
92
+ bdg dom eval "js expression" # Run JavaScript
93
+ bdg dom a11y "role:button" # Query by accessibility role/name
94
+ ```
95
+
96
+ Selectors search open shadow roots and same-origin iframes, and accept `:has-text("...")` and `:visible`.
97
+
98
+ ### Look Without a Screenshot
99
+
100
+ ```bash
101
+ bdg dom inspect "button.primary" # Box, layout, rendered font, colors + WCAG contrast, borders, state (~60-100 tokens)
102
+ bdg dom inspect ".card" --why color # Which CSS rule set a property, and what it overrode
103
+ bdg dom layout ".card" # Positions/sizes of every match: above/below the fold, hidden, covered
104
+ bdg dom listeners "#save" # Event listeners that run for an element (incl. delegated, React/Preact)
105
+ bdg page emulate --viewport 390x844 --color-scheme dark # Responsive/theme check mid-session
106
+ ```
107
+
108
+ ### Wait for Something
109
+
110
+ ```bash
111
+ bdg dom wait '#result' --visible # Appears and is visible
112
+ bdg dom wait '.toast' --text 'Saved' # Contains text
113
+ bdg dom wait '#loading' --gone # Spinner went away
114
+ bdg dom wait --load # Page finished loading
115
+ ```
116
+
117
+ ## Network and Console
118
+
119
+ ```bash
120
+ bdg network list # Requests (DevTools-style)
121
+ bdg network list --filter "status-code:>=400" # Failed requests
122
+ bdg details network <id> # Headers, timing, body of one request
123
+ bdg console --level error # Console errors on the current page
124
+ bdg console --follow # Stream messages live
125
+ bdg network har /tmp/session.har # Export HAR 1.2
126
+ ```
127
+
128
+ ## CDP Access
129
+
130
+ Direct access to Chrome DevTools Protocol:
131
+
132
+ ```bash
133
+ # Execute any CDP method
134
+ bdg cdp Runtime.evaluate --params '{"expression": "document.title", "returnByValue": true}'
135
+ bdg cdp Page.navigate --params '{"url": "https://example.com"}'
136
+ bdg cdp Page.reload --params '{"ignoreCache": true}'
137
+
138
+ # Discovery
139
+ bdg cdp --list # List all domains
140
+ bdg cdp Network --list # List methods in domain
141
+ bdg cdp Network.getCookies --describe # Show method schema
142
+ bdg cdp --search cookie # Search methods
143
+ ```
144
+
145
+ **Important**: Always use `returnByValue: true` for Runtime.evaluate to get serialized values.
146
+
147
+ ## Common Patterns
148
+
149
+ ### Login Flow
150
+ ```bash
151
+ bdg https://example.com/login
152
+ bdg dom form --brief
153
+ bdg dom fill "input[name='username']" "$USER"
154
+ bdg dom fill "input[name='password']" "$PASS"
155
+ bdg dom submit "button[type='submit']" --wait-navigation
156
+ bdg dom screenshot /tmp/result.png
157
+ bdg stop
158
+ ```
159
+
160
+ ### Verify a UI Change (dev server with HMR)
161
+ ```bash
162
+ bdg http://localhost:5173 # Once; keep the session running
163
+ bdg dom click "button.save" # Read the reported effect
164
+ bdg dom wait '.toast' --text 'Saved'
165
+ bdg console --level error # Anything thrown?
166
+ bdg dom inspect ".toast" # Looks right? (no screenshot needed)
167
+ ```
168
+
169
+ ### Extract Data
170
+ ```bash
171
+ bdg cdp Runtime.evaluate --params '{
172
+ "expression": "Array.from(document.querySelectorAll(\"a\")).map(a => ({text: a.textContent, href: a.href}))",
173
+ "returnByValue": true
174
+ }' | jq '.data.result.result.value'
175
+ ```
176
+
177
+ ## JSON Output and Exit Codes
178
+
179
+ Add `--json` (`-j`) to any command for `{ version, success, data }` (or `{ success: false, error, exitCode, suggestion }`). `bdg --help --json` lists every command, flag and exit code.
180
+
181
+ | Code | Meaning | Action |
182
+ |------|---------|--------|
183
+ | 0 | Success | - |
184
+ | 81 | Invalid arguments (incl. blocked raw CDP methods) | Read the suggestion, use the alternative |
185
+ | 83 | Resource not found | Element/session doesn't exist |
186
+ | 85 | Session busy (still starting/stopping) | Retry shortly |
187
+ | 87 | Stale index (page changed since `dom query`) | Re-run the query |
188
+ | 91 | `dom eval` script threw | Fix the JavaScript |
189
+ | 101 | CDP connection failure | Run `bdg cleanup --force` and retry |
190
+ | 102 | Timeout (CDP, or `dom wait --timeout`) | Increase timeout or check page load |
191
+
192
+ ## Troubleshooting
193
+
194
+ ```bash
195
+ bdg status --verbose # Full diagnostics
196
+ bdg cleanup # Clean up after a crashed session
197
+ bdg cleanup --force # Kill a stuck session (daemon + its Chrome)
198
+ ```
199
+
200
+ **Chrome won't launch?** Run `bdg cleanup --force` then retry.
201
+
202
+ **Session stuck?** Run `bdg cleanup --force` to reset.
203
+
204
+ ### Custom Chrome Flags
205
+
206
+ Use `--chrome-flags` or `BDG_CHROME_FLAGS` for self-signed certificates, CORS, etc.:
207
+
208
+ ```bash
209
+ # CLI option
210
+ bdg https://localhost:5173 --chrome-flags="--ignore-certificate-errors"
211
+
212
+ # Environment variable
213
+ BDG_CHROME_FLAGS="--ignore-certificate-errors" bdg https://localhost:5173
214
+
215
+ # Multiple flags
216
+ bdg https://example.com --chrome-flags="--ignore-certificate-errors --disable-web-security"
217
+ ```
218
+
219
+ **Common flags for development:**
220
+ - `--ignore-certificate-errors` - Self-signed SSL certs
221
+ - `--disable-web-security` - CORS issues in development
222
+ - `--allow-insecure-localhost` - Insecure localhost
223
+ - `--disable-features=IsolateOrigins,site-per-process` - Cross-origin iframes
224
+
225
+ ## Verification Best Practices
226
+
227
+ **Prefer DOM queries over screenshots** for verification:
228
+
229
+ ```bash
230
+ # GOOD: Fast, precise, scriptable
231
+ bdg cdp Runtime.evaluate --params '{
232
+ "expression": "document.querySelector(\".error-message\")?.textContent",
233
+ "returnByValue": true
234
+ }'
235
+
236
+ # GOOD: Check element exists
237
+ bdg dom query ".submit-btn"
238
+
239
+ # GOOD: Check text content
240
+ bdg cdp Runtime.evaluate --params '{
241
+ "expression": "document.body.innerText.includes(\"Success\")",
242
+ "returnByValue": true
243
+ }'
244
+
245
+ # AVOID: Screenshots for simple verification (slow, requires visual inspection)
246
+ bdg dom screenshot /tmp/check.png # Only use when you need visual proof
247
+ ```
248
+
249
+ **When to use screenshots:**
250
+ - Visual regression testing
251
+ - Capturing proof for user review
252
+ - Debugging layout issues
253
+ - When DOM structure is unknown
254
+
255
+ **When to use DOM queries:**
256
+ - Verifying text content appeared
257
+ - Checking element exists/visible
258
+ - Validating form state
259
+ - Counting elements
260
+ - Any programmatic assertion
261
+
262
+ ## When NOT to Use bdg
263
+
264
+ - **Static HTML** - Use `curl` + `htmlq`/`pq`
265
+ - **API calls** - Use `curl` + `jq`
266
+ - **Simple HTTP** - Use `wget`/`curl`
267
+
268
+ Use bdg when you need: JavaScript execution, dynamic content, browser APIs, screenshots, or network manipulation.
package/README.md CHANGED
@@ -43,6 +43,17 @@ npm install -g browser-debugger-cli
43
43
  - ✅ Windows via WSL
44
44
  - ❌ PowerShell/Git Bash (not yet)
45
45
 
46
+ ## Use with Claude Code and Other Agents
47
+
48
+ bdg ships an agent skill (`SKILL.md`) that teaches the workflow: start once, act, read what the action changed, inspect without screenshots, check network and console.
49
+
50
+ ```bash
51
+ bdg install-skill # ~/.claude/skills/bdg (Claude Code) + ~/.agents/skills/bdg (Codex, Gemini CLI, ...)
52
+ bdg install-skill --claude # Claude Code only
53
+ ```
54
+
55
+ Start a new agent session afterwards, and re-run `bdg install-skill` after upgrading bdg. In Claude Code the skill loads when a task needs a browser, or on demand with `/bdg`.
56
+
46
57
  ## Quick Start
47
58
 
48
59
  ```bash
@@ -0,0 +1,20 @@
1
+ import type { Command } from 'commander';
2
+ import type { InstalledSkill, SkillTarget } from '../types.js';
3
+ /**
4
+ * Copy the bdg skill into each target's skill directory, overwriting an
5
+ * older copy.
6
+ *
7
+ * @param targets - Agents to install for
8
+ * @param home - Home directory the skill roots are relative to
9
+ * @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)
12
+ */
13
+ export declare function installSkill(targets: SkillTarget[], home?: string, source?: string): InstalledSkill[];
14
+ /**
15
+ * Register the install-skill command.
16
+ *
17
+ * @param program - Commander.js Command instance to register commands on
18
+ */
19
+ export declare function registerInstallSkillCommand(program: Command): void;
20
+ //# sourceMappingURL=installSkill.d.ts.map
@@ -0,0 +1,87 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { dirname, join } from 'path';
4
+ import { runCommand } from './shared/CommandRunner.js';
5
+ import { jsonOption } from './shared/commonOptions.js';
6
+ import { CommandError } from '../errors/index.js';
7
+ import { skillSourceMissingError, skillWriteFailedError } from '../errors/messages.js';
8
+ import { formatInstalledSkills } from '../ui/formatters/installSkill.js';
9
+ import { getErrorMessage } from '../utils/errors.js';
10
+ import { EXIT_CODES } from '../utils/exitCodes.js';
11
+ import { PACKAGE_ROOT } from '../utils/packageRoot.js';
12
+ /** The skill shipped with the package (also used by agents working in this repo). */
13
+ const SKILL_SOURCE_PATH = join(PACKAGE_ROOT, '.claude', 'skills', 'bdg', 'SKILL.md');
14
+ /** Skill roots, relative to the home directory, of the agents the skill is installed for. */
15
+ const SKILL_ROOTS = {
16
+ claude: join('.claude', 'skills'),
17
+ agents: join('.agents', 'skills'),
18
+ };
19
+ /**
20
+ * Copy the bdg skill into each target's skill directory, overwriting an
21
+ * older copy.
22
+ *
23
+ * @param targets - Agents to install for
24
+ * @param home - Home directory the skill roots are relative to
25
+ * @param source - SKILL.md to copy
26
+ * @returns One entry per target, in the given order
27
+ * @throws CommandError when the source is missing (83) or a write fails (82)
28
+ */
29
+ export function installSkill(targets, home = homedir(), source = SKILL_SOURCE_PATH) {
30
+ if (!existsSync(source)) {
31
+ const err = skillSourceMissingError(source);
32
+ throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
33
+ }
34
+ const content = readFileSync(source, 'utf-8');
35
+ return targets.map((target) => writeSkill(target, join(home, SKILL_ROOTS[target], 'bdg', 'SKILL.md'), content));
36
+ }
37
+ /**
38
+ * Write the skill to one path unless it already holds the same content.
39
+ *
40
+ * @param target - Agent the path belongs to
41
+ * @param path - Destination SKILL.md
42
+ * @param content - Skill text
43
+ * @returns What happened to the file
44
+ * @throws CommandError (82) when the directory or file cannot be written
45
+ */
46
+ function writeSkill(target, path, content) {
47
+ const existing = existsSync(path) ? readFileSync(path, 'utf-8') : undefined;
48
+ if (existing === content) {
49
+ return { target, path, status: 'unchanged' };
50
+ }
51
+ try {
52
+ mkdirSync(dirname(path), { recursive: true });
53
+ writeFileSync(path, content);
54
+ }
55
+ catch (caught) {
56
+ const err = skillWriteFailedError(path, getErrorMessage(caught));
57
+ throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.PERMISSION_DENIED);
58
+ }
59
+ return { target, path, status: existing === undefined ? 'installed' : 'updated' };
60
+ }
61
+ /**
62
+ * Targets picked by the flags; no flag means every agent.
63
+ *
64
+ * @param options - Parsed command options
65
+ * @returns Targets to install for
66
+ */
67
+ function selectedTargets(options) {
68
+ const picked = Object.keys(SKILL_ROOTS).filter((target) => options[target]);
69
+ return picked.length > 0 ? picked : Object.keys(SKILL_ROOTS);
70
+ }
71
+ /**
72
+ * Register the install-skill command.
73
+ *
74
+ * @param program - Commander.js Command instance to register commands on
75
+ */
76
+ export function registerInstallSkillCommand(program) {
77
+ program
78
+ .command('install-skill')
79
+ .description('Install the bdg agent skill for Claude Code (~/.claude/skills) and agents reading ~/.agents/skills (Codex, Gemini CLI, ...); re-run after upgrading bdg')
80
+ .option('--claude', 'Only ~/.claude/skills (Claude Code)')
81
+ .option('--agents', 'Only ~/.agents/skills (Codex, Gemini CLI and other agents)')
82
+ .addOption(jsonOption())
83
+ .action(async (options) => {
84
+ await runCommand((opts) => Promise.resolve({ success: true, data: { skills: installSkill(selectedTargets(opts)) } }), options, formatInstalledSkills);
85
+ });
86
+ }
87
+ //# sourceMappingURL=installSkill.js.map
package/dist/commands.js CHANGED
@@ -4,6 +4,7 @@ import { registerConsoleCommand } from './commands/console.js';
4
4
  import { registerDetailsCommand } from './commands/details.js';
5
5
  import { registerFormInteractionCommands } from './commands/dom/formInteraction.js';
6
6
  import { registerDomCommands } from './commands/dom/index.js';
7
+ import { registerInstallSkillCommand } from './commands/installSkill.js';
7
8
  import { registerNetworkCommands } from './commands/network/index.js';
8
9
  import { registerPageCommands } from './commands/page.js';
9
10
  import { registerPeekCommand } from './commands/peek.js';
@@ -44,5 +45,7 @@ export const commandRegistry = [
44
45
  registerNetworkCommands,
45
46
  addCommandGroup('Console Commands:'),
46
47
  registerConsoleCommand,
48
+ addCommandGroup('Agent Setup:'),
49
+ registerInstallSkillCommand,
47
50
  ];
48
51
  //# sourceMappingURL=commands.js.map
@@ -973,5 +973,21 @@ export declare function formInIframeError(iframeUrl: string, crossOrigin: boolea
973
973
  export declare function cdpCallError(method: string, message: string): ErrorWithSuggestion & {
974
974
  notFound: boolean;
975
975
  };
976
+ /**
977
+ * The skill file is not where the package should have it (a broken or
978
+ * partial install).
979
+ *
980
+ * @param source - Expected SKILL.md path
981
+ * @returns Message and suggestion
982
+ */
983
+ export declare function skillSourceMissingError(source: string): ErrorWithSuggestion;
984
+ /**
985
+ * The skill could not be written to an agent's skill directory.
986
+ *
987
+ * @param path - Destination SKILL.md
988
+ * @param reason - The file system error
989
+ * @returns Message and suggestion
990
+ */
991
+ export declare function skillWriteFailedError(path: string, reason: string): ErrorWithSuggestion;
976
992
  export {};
977
993
  //# sourceMappingURL=messages.d.ts.map
@@ -1751,4 +1751,30 @@ export function cdpCallError(method, message) {
1751
1751
  notFound: false,
1752
1752
  };
1753
1753
  }
1754
+ /**
1755
+ * The skill file is not where the package should have it (a broken or
1756
+ * partial install).
1757
+ *
1758
+ * @param source - Expected SKILL.md path
1759
+ * @returns Message and suggestion
1760
+ */
1761
+ export function skillSourceMissingError(source) {
1762
+ return {
1763
+ message: `The bdg skill is missing from the installed package (${source})`,
1764
+ suggestion: 'Reinstall bdg: npm install -g browser-debugger-cli',
1765
+ };
1766
+ }
1767
+ /**
1768
+ * The skill could not be written to an agent's skill directory.
1769
+ *
1770
+ * @param path - Destination SKILL.md
1771
+ * @param reason - The file system error
1772
+ * @returns Message and suggestion
1773
+ */
1774
+ export function skillWriteFailedError(path, reason) {
1775
+ return {
1776
+ message: `Could not write ${path}: ${reason}`,
1777
+ suggestion: 'Check the permissions of that directory, or install for the other agent only (--claude or --agents)',
1778
+ };
1779
+ }
1754
1780
  //# sourceMappingURL=messages.js.map
package/dist/index.js CHANGED
@@ -3358,6 +3358,18 @@ function formInIframeError(iframeUrl, crossOrigin) {
3358
3358
  suggestion: crossOrigin ? "Cross-origin iframes cannot be read or controlled; open the iframe URL directly: bdg <iframe url>" : 'dom form lists forms of the main document only; its fields are reachable directly: bdg dom query "input, select, textarea", then bdg dom fill <index> <value>'
3359
3359
  };
3360
3360
  }
3361
+ function skillSourceMissingError(source) {
3362
+ return {
3363
+ message: `The bdg skill is missing from the installed package (${source})`,
3364
+ suggestion: "Reinstall bdg: npm install -g browser-debugger-cli"
3365
+ };
3366
+ }
3367
+ function skillWriteFailedError(path6, reason) {
3368
+ return {
3369
+ message: `Could not write ${path6}: ${reason}`,
3370
+ suggestion: "Check the permissions of that directory, or install for the other agent only (--claude or --agents)"
3371
+ };
3372
+ }
3361
3373
  var LAUNCHED_CHROME_DESCRIPTION, A11Y_NAME_QUERY_EXAMPLE, SCOPED_FILTER_EXAMPLES, PLAYWRIGHT_ONLY_SYNTAX, MALFORMED_SELECTOR_DETAILS, SESSION_DIR_SUGGESTION, SCROLL_PROBLEMS, VIA_LABEL_SUFFIX, NAME_QUERY_PLACEHOLDER, LABEL_WITHOUT_CONTROL;
3362
3374
  var init_messages = __esm({
3363
3375
  "src/errors/messages.ts"() {
@@ -16670,6 +16682,87 @@ function registerDomCommands(program2) {
16670
16682
  );
16671
16683
  }
16672
16684
 
16685
+ // src/commands/installSkill.ts
16686
+ import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "fs";
16687
+ import { homedir as homedir3 } from "os";
16688
+ import { dirname as dirname5, join as join6 } from "path";
16689
+ init_errors3();
16690
+ init_messages();
16691
+
16692
+ // src/ui/formatters/installSkill.ts
16693
+ init_formatting();
16694
+ import { homedir as homedir2 } from "os";
16695
+ function tildePath(path6) {
16696
+ const home = homedir2();
16697
+ return path6.startsWith(`${home}/`) ? `~${path6.slice(home.length)}` : path6;
16698
+ }
16699
+ function formatInstalledSkills(data) {
16700
+ const fmt = new OutputFormatter().text("bdg skill:");
16701
+ for (const skill of data.skills) {
16702
+ fmt.text(` ${skill.target.padEnd(6)} ${skill.status.padEnd(9)} ${tildePath(skill.path)}`);
16703
+ }
16704
+ return fmt.hints("Next:", [
16705
+ "Start a new agent session to load it (running sessions keep the old list)",
16706
+ "After upgrading bdg, run bdg install-skill again"
16707
+ ]).build();
16708
+ }
16709
+
16710
+ // src/commands/installSkill.ts
16711
+ init_errors();
16712
+ init_exitCodes();
16713
+ var SKILL_SOURCE_PATH = join6(PACKAGE_ROOT, ".claude", "skills", "bdg", "SKILL.md");
16714
+ var SKILL_ROOTS = {
16715
+ claude: join6(".claude", "skills"),
16716
+ agents: join6(".agents", "skills")
16717
+ };
16718
+ function installSkill(targets, home = homedir3(), source = SKILL_SOURCE_PATH) {
16719
+ if (!existsSync10(source)) {
16720
+ const err = skillSourceMissingError(source);
16721
+ throw new CommandError(
16722
+ err.message,
16723
+ { suggestion: err.suggestion },
16724
+ EXIT_CODES.RESOURCE_NOT_FOUND
16725
+ );
16726
+ }
16727
+ const content = readFileSync8(source, "utf-8");
16728
+ return targets.map(
16729
+ (target) => writeSkill(target, join6(home, SKILL_ROOTS[target], "bdg", "SKILL.md"), content)
16730
+ );
16731
+ }
16732
+ function writeSkill(target, path6, content) {
16733
+ const existing = existsSync10(path6) ? readFileSync8(path6, "utf-8") : void 0;
16734
+ if (existing === content) {
16735
+ return { target, path: path6, status: "unchanged" };
16736
+ }
16737
+ try {
16738
+ mkdirSync3(dirname5(path6), { recursive: true });
16739
+ writeFileSync2(path6, content);
16740
+ } catch (caught) {
16741
+ const err = skillWriteFailedError(path6, getErrorMessage(caught));
16742
+ throw new CommandError(
16743
+ err.message,
16744
+ { suggestion: err.suggestion },
16745
+ EXIT_CODES.PERMISSION_DENIED
16746
+ );
16747
+ }
16748
+ return { target, path: path6, status: existing === void 0 ? "installed" : "updated" };
16749
+ }
16750
+ function selectedTargets(options) {
16751
+ const picked = Object.keys(SKILL_ROOTS).filter((target) => options[target]);
16752
+ return picked.length > 0 ? picked : Object.keys(SKILL_ROOTS);
16753
+ }
16754
+ function registerInstallSkillCommand(program2) {
16755
+ program2.command("install-skill").description(
16756
+ "Install the bdg agent skill for Claude Code (~/.claude/skills) and agents reading ~/.agents/skills (Codex, Gemini CLI, ...); re-run after upgrading bdg"
16757
+ ).option("--claude", "Only ~/.claude/skills (Claude Code)").option("--agents", "Only ~/.agents/skills (Codex, Gemini CLI and other agents)").addOption(jsonOption()).action(async (options) => {
16758
+ await runCommand(
16759
+ (opts) => Promise.resolve({ success: true, data: { skills: installSkill(selectedTargets(opts)) } }),
16760
+ options,
16761
+ formatInstalledSkills
16762
+ );
16763
+ });
16764
+ }
16765
+
16673
16766
  // src/commands/network/har.ts
16674
16767
  import * as fs13 from "fs";
16675
16768
  import * as path5 from "path";
@@ -18300,7 +18393,7 @@ var SessionDirError = class extends DaemonError {
18300
18393
  // src/daemon/launcher.ts
18301
18394
  import { spawn } from "child_process";
18302
18395
  import fs14 from "fs";
18303
- import { join as join7 } from "path";
18396
+ import { join as join8 } from "path";
18304
18397
  init_messages();
18305
18398
  init_paths();
18306
18399
  init_logging();
@@ -18321,7 +18414,7 @@ async function launchDaemon() {
18321
18414
  );
18322
18415
  }
18323
18416
  assertUsableSessionDir();
18324
- const logPath = join7(getSessionDir(), "daemon.log");
18417
+ const logPath = join8(getSessionDir(), "daemon.log");
18325
18418
  rotateLog(logPath);
18326
18419
  const logFd = fs14.openSync(logPath, "a");
18327
18420
  log24.debug(`Starting daemon: ${DAEMON_SCRIPT_PATH}`);
@@ -18382,7 +18475,7 @@ async function waitForDaemonReady(hasExited) {
18382
18475
  }
18383
18476
  if (hasExited() && !await isDaemonAlive()) {
18384
18477
  throw new DaemonStartupError(
18385
- `Daemon exited during startup. See ${join7(getSessionDir(), "daemon.log")}`,
18478
+ `Daemon exited during startup. See ${join8(getSessionDir(), "daemon.log")}`,
18386
18479
  "DAEMON_EXITED"
18387
18480
  );
18388
18481
  }
@@ -19741,7 +19834,9 @@ var commandRegistry = [
19741
19834
  addCommandGroup("Network Commands:"),
19742
19835
  registerNetworkCommands,
19743
19836
  addCommandGroup("Console Commands:"),
19744
- registerConsoleCommand
19837
+ registerConsoleCommand,
19838
+ addCommandGroup("Agent Setup:"),
19839
+ registerInstallSkillCommand
19745
19840
  ];
19746
19841
 
19747
19842
  // src/index.ts
package/dist/types.d.ts CHANGED
@@ -668,4 +668,12 @@ export interface FormDiscoveryResult {
668
668
  }>;
669
669
  brief?: boolean | undefined;
670
670
  }
671
+ /** Agent whose skill directory receives the bdg skill (`bdg install-skill`). */
672
+ export type SkillTarget = 'claude' | 'agents';
673
+ /** What `bdg install-skill` did to the skill file in one agent's skill directory. */
674
+ export interface InstalledSkill {
675
+ target: SkillTarget;
676
+ path: string;
677
+ status: 'installed' | 'updated' | 'unchanged';
678
+ }
671
679
  //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,11 @@
1
+ import type { InstalledSkill } from '../../types.js';
2
+ /**
3
+ * Format where the skill was installed, one line per agent.
4
+ *
5
+ * @param data - Install results
6
+ * @returns Human-readable summary
7
+ */
8
+ export declare function formatInstalledSkills(data: {
9
+ skills: InstalledSkill[];
10
+ }): string;
11
+ //# sourceMappingURL=installSkill.d.ts.map
@@ -0,0 +1,31 @@
1
+ import { homedir } from 'os';
2
+ import { OutputFormatter } from '../formatting.js';
3
+ /**
4
+ * Shorten a path under the home directory to `~/...`.
5
+ *
6
+ * @param path - Absolute path
7
+ * @returns Path as the user would type it
8
+ */
9
+ function tildePath(path) {
10
+ const home = homedir();
11
+ return path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;
12
+ }
13
+ /**
14
+ * Format where the skill was installed, one line per agent.
15
+ *
16
+ * @param data - Install results
17
+ * @returns Human-readable summary
18
+ */
19
+ export function formatInstalledSkills(data) {
20
+ const fmt = new OutputFormatter().text('bdg skill:');
21
+ for (const skill of data.skills) {
22
+ fmt.text(` ${skill.target.padEnd(6)} ${skill.status.padEnd(9)} ${tildePath(skill.path)}`);
23
+ }
24
+ return fmt
25
+ .hints('Next:', [
26
+ 'Start a new agent session to load it (running sessions keep the old list)',
27
+ 'After upgrading bdg, run bdg install-skill again',
28
+ ])
29
+ .build();
30
+ }
31
+ //# sourceMappingURL=installSkill.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-debugger-cli",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "DevTools telemetry in your terminal. For humans and agents. Direct WebSocket to Chrome's debugging port.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -90,6 +90,7 @@
90
90
  },
91
91
  "files": [
92
92
  "dist",
93
+ ".claude/skills/bdg/SKILL.md",
93
94
  "!dist/**/__tests__",
94
95
  "!dist/__testutils__",
95
96
  "!dist/__testfixtures__",