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.
- package/.claude/skills/bdg/SKILL.md +268 -0
- package/README.md +11 -0
- package/dist/commands/installSkill.d.ts +20 -0
- package/dist/commands/installSkill.js +87 -0
- package/dist/commands.js +3 -0
- package/dist/errors/messages.d.ts +16 -0
- package/dist/errors/messages.js +26 -0
- package/dist/index.js +99 -4
- package/dist/types.d.ts +8 -0
- package/dist/ui/formatters/installSkill.d.ts +11 -0
- package/dist/ui/formatters/installSkill.js +31 -0
- package/package.json +2 -1
|
@@ -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
|
package/dist/errors/messages.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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 ${
|
|
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.
|
|
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__",
|