compound-workflow 0.1.1

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 (55) hide show
  1. package/.claude-plugin/marketplace.json +11 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/.cursor-plugin/plugin.json +12 -0
  4. package/README.md +155 -0
  5. package/package.json +22 -0
  6. package/scripts/install-cli.mjs +313 -0
  7. package/scripts/sync-into-repo.sh +103 -0
  8. package/src/.agents/agents/research/best-practices-researcher.md +132 -0
  9. package/src/.agents/agents/research/framework-docs-researcher.md +134 -0
  10. package/src/.agents/agents/research/git-history-analyzer.md +62 -0
  11. package/src/.agents/agents/research/learnings-researcher.md +288 -0
  12. package/src/.agents/agents/research/repo-research-analyst.md +146 -0
  13. package/src/.agents/agents/review/agent-native-reviewer.md +299 -0
  14. package/src/.agents/agents/workflow/bug-reproduction-validator.md +87 -0
  15. package/src/.agents/agents/workflow/lint.md +20 -0
  16. package/src/.agents/agents/workflow/spec-flow-analyzer.md +149 -0
  17. package/src/.agents/commands/assess.md +60 -0
  18. package/src/.agents/commands/install.md +53 -0
  19. package/src/.agents/commands/metrics.md +59 -0
  20. package/src/.agents/commands/setup.md +9 -0
  21. package/src/.agents/commands/sync.md +9 -0
  22. package/src/.agents/commands/test-browser.md +393 -0
  23. package/src/.agents/commands/workflow/brainstorm.md +252 -0
  24. package/src/.agents/commands/workflow/compound.md +142 -0
  25. package/src/.agents/commands/workflow/plan.md +737 -0
  26. package/src/.agents/commands/workflow/review-v2.md +148 -0
  27. package/src/.agents/commands/workflow/review.md +110 -0
  28. package/src/.agents/commands/workflow/triage.md +54 -0
  29. package/src/.agents/commands/workflow/work.md +439 -0
  30. package/src/.agents/references/README.md +12 -0
  31. package/src/.agents/references/standards/README.md +9 -0
  32. package/src/.agents/scripts/self-check.mjs +227 -0
  33. package/src/.agents/scripts/sync-opencode.mjs +355 -0
  34. package/src/.agents/skills/agent-browser/SKILL.md +223 -0
  35. package/src/.agents/skills/audit-traceability/SKILL.md +260 -0
  36. package/src/.agents/skills/brainstorming/SKILL.md +250 -0
  37. package/src/.agents/skills/compound-docs/SKILL.md +533 -0
  38. package/src/.agents/skills/compound-docs/assets/critical-pattern-template.md +34 -0
  39. package/src/.agents/skills/compound-docs/assets/resolution-template.md +97 -0
  40. package/src/.agents/skills/compound-docs/references/yaml-schema.md +87 -0
  41. package/src/.agents/skills/compound-docs/schema.project.yaml +18 -0
  42. package/src/.agents/skills/compound-docs/schema.yaml +119 -0
  43. package/src/.agents/skills/data-foundations/SKILL.md +185 -0
  44. package/src/.agents/skills/document-review/SKILL.md +108 -0
  45. package/src/.agents/skills/file-todos/SKILL.md +177 -0
  46. package/src/.agents/skills/file-todos/assets/todo-template.md +106 -0
  47. package/src/.agents/skills/financial-workflow-integrity/SKILL.md +423 -0
  48. package/src/.agents/skills/git-worktree/SKILL.md +268 -0
  49. package/src/.agents/skills/pii-protection-prisma/SKILL.md +629 -0
  50. package/src/.agents/skills/process-metrics/SKILL.md +46 -0
  51. package/src/.agents/skills/process-metrics/assets/daily-template.md +37 -0
  52. package/src/.agents/skills/process-metrics/assets/monthly-template.md +21 -0
  53. package/src/.agents/skills/process-metrics/assets/weekly-template.md +25 -0
  54. package/src/.agents/skills/technical-review/SKILL.md +83 -0
  55. package/src/AGENTS.md +213 -0
@@ -0,0 +1,355 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+
5
+ function usage(exitCode = 0) {
6
+ const msg = `
7
+ Usage:
8
+ node .agents/scripts/sync-opencode.mjs [--root <repoRoot>] [--dry-run]
9
+
10
+ What it does:
11
+ - Discovers commands from .agents/commands/**/*.md
12
+ - id: frontmatter 'invocation' (preferred) else 'name' else filename
13
+ - Discovers agents from .agents/agents/**/*.md
14
+ - id: frontmatter 'name' else filename
15
+ - Creates/updates repo-root opencode.json with managed command/agent entries
16
+ - Prunes managed entries whose source files no longer exist
17
+
18
+ Output:
19
+ - Always prints resolved root and absolute modified paths
20
+ - Prints idempotency summary (0 changes needed vs updated N managed entries)
21
+ `;
22
+ (exitCode === 0 ? console.log : console.error)(msg.trimStart());
23
+ process.exit(exitCode);
24
+ }
25
+
26
+ function parseArgs(argv) {
27
+ const out = { root: process.cwd(), dryRun: false };
28
+ for (let i = 2; i < argv.length; i++) {
29
+ const a = argv[i];
30
+ if (a === "--dry-run") out.dryRun = true;
31
+ else if (a === "--root") {
32
+ const v = argv[i + 1];
33
+ if (!v) usage(1);
34
+ out.root = v;
35
+ i++;
36
+ } else if (a === "-h" || a === "--help") usage(0);
37
+ else usage(1);
38
+ }
39
+ return out;
40
+ }
41
+
42
+ function realpathSafe(p) {
43
+ try {
44
+ return fs.realpathSync(p);
45
+ } catch {
46
+ return path.resolve(p);
47
+ }
48
+ }
49
+
50
+ function walkFiles(dirAbs, predicate) {
51
+ const out = [];
52
+ const stack = [dirAbs];
53
+ while (stack.length) {
54
+ const cur = stack.pop();
55
+ let entries;
56
+ try {
57
+ entries = fs.readdirSync(cur, { withFileTypes: true });
58
+ } catch {
59
+ continue;
60
+ }
61
+ for (const e of entries) {
62
+ const p = path.join(cur, e.name);
63
+ if (e.isDirectory()) stack.push(p);
64
+ else if (e.isFile() && predicate(p)) out.push(p);
65
+ }
66
+ }
67
+ out.sort();
68
+ return out;
69
+ }
70
+
71
+ function stripJsonc(input) {
72
+ // Removes // and /* */ comments while preserving strings.
73
+ let out = "";
74
+ let i = 0;
75
+ let inStr = false;
76
+ let strQuote = "";
77
+ let escape = false;
78
+ while (i < input.length) {
79
+ const c = input[i];
80
+ const n = input[i + 1];
81
+ if (inStr) {
82
+ out += c;
83
+ if (escape) escape = false;
84
+ else if (c === "\\") escape = true;
85
+ else if (c === strQuote) inStr = false;
86
+ i++;
87
+ continue;
88
+ }
89
+
90
+ if (c === '"' || c === "'") {
91
+ inStr = true;
92
+ strQuote = c;
93
+ out += c;
94
+ i++;
95
+ continue;
96
+ }
97
+
98
+ if (c === "/" && n === "/") {
99
+ while (i < input.length && input[i] !== "\n") i++;
100
+ continue;
101
+ }
102
+ if (c === "/" && n === "*") {
103
+ i += 2;
104
+ while (i < input.length && !(input[i] === "*" && input[i + 1] === "/")) i++;
105
+ i += 2;
106
+ continue;
107
+ }
108
+
109
+ out += c;
110
+ i++;
111
+ }
112
+ return out;
113
+ }
114
+
115
+ function readJsonMaybeJsonc(fileAbs) {
116
+ if (!fs.existsSync(fileAbs)) return null;
117
+ const raw = fs.readFileSync(fileAbs, "utf8");
118
+ const stripped = stripJsonc(raw);
119
+ return JSON.parse(stripped);
120
+ }
121
+
122
+ function parseFrontmatter(md) {
123
+ // Only supports simple YAML key: value lines (enough for name/description/invocation).
124
+ if (!md.startsWith("---\n") && !md.startsWith("---\r\n")) return {};
125
+ const end = md.indexOf("\n---", 4);
126
+ if (end === -1) return {};
127
+ const block = md.slice(4, end + 1); // include trailing newline for simpler parsing
128
+ const out = {};
129
+ for (const line of block.split(/\r?\n/)) {
130
+ const m = line.match(/^([A-Za-z0-9_-]+):\s*(.*)\s*$/);
131
+ if (!m) continue;
132
+ const k = m[1];
133
+ let v = m[2] ?? "";
134
+ v = v.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
135
+ out[k] = v;
136
+ }
137
+ return out;
138
+ }
139
+
140
+ function isUnder(parentAbs, childAbs) {
141
+ const rel = path.relative(parentAbs, childAbs);
142
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
143
+ }
144
+
145
+ function discoverCommands(rootAbs) {
146
+ const commandsDir = path.join(rootAbs, ".agents", "commands");
147
+ const files = walkFiles(commandsDir, (p) => p.endsWith(".md"));
148
+ const map = new Map();
149
+ const warnings = [];
150
+ for (const fileAbs of files) {
151
+ const rel = path.relative(rootAbs, fileAbs).replaceAll(path.sep, "/");
152
+ const md = fs.readFileSync(fileAbs, "utf8");
153
+ const fm = parseFrontmatter(md);
154
+ const id = (fm.invocation || fm.name || path.basename(fileAbs, ".md")).trim();
155
+ const description = (fm.description || id).trim();
156
+ if (!id) {
157
+ warnings.push(`Command file missing id (name/invocation): ${rel}`);
158
+ continue;
159
+ }
160
+ if (!fm.description) warnings.push(`Command missing frontmatter description: ${rel} (${id})`);
161
+
162
+ // Prefer first occurrence to avoid silent overwrites.
163
+ if (map.has(id)) {
164
+ warnings.push(`Duplicate command id '${id}': ${rel} (already have ${map.get(id).rel})`);
165
+ continue;
166
+ }
167
+ map.set(id, { id, rel, fileAbs, description });
168
+ }
169
+ return { map, warnings };
170
+ }
171
+
172
+ function discoverAgents(rootAbs) {
173
+ const agentsDir = path.join(rootAbs, ".agents", "agents");
174
+ const files = walkFiles(agentsDir, (p) => p.endsWith(".md"));
175
+ const map = new Map();
176
+ const warnings = [];
177
+ for (const fileAbs of files) {
178
+ const rel = path.relative(rootAbs, fileAbs).replaceAll(path.sep, "/");
179
+ const md = fs.readFileSync(fileAbs, "utf8");
180
+ const fm = parseFrontmatter(md);
181
+ const id = (fm.name || path.basename(fileAbs, ".md")).trim();
182
+ const description = (fm.description || id).trim();
183
+ if (!id) {
184
+ warnings.push(`Agent file missing id (name): ${rel}`);
185
+ continue;
186
+ }
187
+ if (!fm.description) warnings.push(`Agent missing frontmatter description: ${rel} (${id})`);
188
+ if (map.has(id)) {
189
+ warnings.push(`Duplicate agent id '${id}': ${rel} (already have ${map.get(id).rel})`);
190
+ continue;
191
+ }
192
+ map.set(id, { id, rel, fileAbs, description });
193
+ }
194
+ return { map, warnings };
195
+ }
196
+
197
+ function ensureObject(v) {
198
+ return v && typeof v === "object" && !Array.isArray(v) ? v : {};
199
+ }
200
+
201
+ function stableStringify(obj) {
202
+ return JSON.stringify(obj, null, 2) + "\n";
203
+ }
204
+
205
+ function buildManagedCommandTemplate(cmdRel) {
206
+ // Keep simple + consistent with existing docs.
207
+ return `@AGENTS.md\n@${cmdRel}\nArguments: $ARGUMENTS\n`;
208
+ }
209
+
210
+ function buildManagedAgentPrompt(agentRel) {
211
+ return `{file:${agentRel}}`;
212
+ }
213
+
214
+ function isManagedCommand(entry) {
215
+ const tpl = entry?.template;
216
+ return typeof tpl === "string" && tpl.includes("@.agents/commands/");
217
+ }
218
+
219
+ function isManagedAgent(entry) {
220
+ const p = entry?.prompt;
221
+ return typeof p === "string" && p.includes("{file:.agents/agents/");
222
+ }
223
+
224
+ function fileExists(rootAbs, rel) {
225
+ const abs = path.join(rootAbs, rel);
226
+ return fs.existsSync(abs);
227
+ }
228
+
229
+ function main() {
230
+ const args = parseArgs(process.argv);
231
+ const rootAbs = realpathSafe(args.root);
232
+ const agentsDir = path.join(rootAbs, ".agents");
233
+ const commandsDir = path.join(agentsDir, "commands");
234
+ const agentsMdAbs = path.join(rootAbs, "AGENTS.md");
235
+ const opencodeAbs = path.join(rootAbs, "opencode.json");
236
+
237
+ if (!fs.existsSync(commandsDir)) {
238
+ console.error(`Error: missing .agents/commands in ${rootAbs}`);
239
+ process.exit(2);
240
+ }
241
+ if (!fs.existsSync(path.join(agentsDir, "agents"))) {
242
+ console.error(`Error: missing .agents/agents in ${rootAbs}`);
243
+ process.exit(2);
244
+ }
245
+
246
+ const { map: commands, warnings: cmdWarn } = discoverCommands(rootAbs);
247
+ const { map: agents, warnings: agentWarn } = discoverAgents(rootAbs);
248
+ const warnings = [...cmdWarn, ...agentWarn];
249
+
250
+ const existing = readJsonMaybeJsonc(opencodeAbs) ?? {};
251
+ const next = structuredClone(existing);
252
+
253
+ next.$schema = next.$schema || "https://opencode.ai/config.json";
254
+ next.skills = ensureObject(next.skills);
255
+ next.skills.paths = Array.isArray(next.skills.paths) ? next.skills.paths : [".agents/skills"];
256
+ next.command = ensureObject(next.command);
257
+ next.agent = ensureObject(next.agent);
258
+
259
+ let created = 0;
260
+ let updated = 0;
261
+ let pruned = 0;
262
+
263
+ // Upsert commands
264
+ for (const [id, cmd] of commands.entries()) {
265
+ const entry = ensureObject(next.command[id]);
266
+ const desired = {
267
+ ...entry,
268
+ description: cmd.description,
269
+ agent: "build",
270
+ template: buildManagedCommandTemplate(cmd.rel),
271
+ };
272
+ const before = JSON.stringify(next.command[id] ?? null);
273
+ const after = JSON.stringify(desired);
274
+ if (!next.command[id]) created++;
275
+ else if (before !== after) updated++;
276
+ next.command[id] = desired;
277
+ }
278
+
279
+ // Upsert agents
280
+ for (const [id, ag] of agents.entries()) {
281
+ const entry = ensureObject(next.agent[id]);
282
+ const desired = {
283
+ ...entry,
284
+ description: ag.description,
285
+ mode: "subagent",
286
+ prompt: buildManagedAgentPrompt(ag.rel),
287
+ permission: {
288
+ ...(ensureObject(entry.permission)),
289
+ edit: ensureObject(entry.permission).edit ?? "deny",
290
+ },
291
+ };
292
+ const before = JSON.stringify(next.agent[id] ?? null);
293
+ const after = JSON.stringify(desired);
294
+ if (!next.agent[id]) created++;
295
+ else if (before !== after) updated++;
296
+ next.agent[id] = desired;
297
+ }
298
+
299
+ // Prune stale managed commands
300
+ for (const [id, entry] of Object.entries(next.command)) {
301
+ if (!isManagedCommand(entry)) continue;
302
+ const tpl = entry.template;
303
+ const m = tpl.match(/@(\.agents\/commands\/[^\n\r]+)/);
304
+ const rel = m?.[1];
305
+ if (!rel) continue;
306
+ if (!fileExists(rootAbs, rel)) {
307
+ delete next.command[id];
308
+ pruned++;
309
+ }
310
+ }
311
+
312
+ // Prune stale managed agents
313
+ for (const [id, entry] of Object.entries(next.agent)) {
314
+ if (!isManagedAgent(entry)) continue;
315
+ const p = entry.prompt;
316
+ const m = p.match(/\{file:(\.agents\/agents\/[^}]+)\}/);
317
+ const rel = m?.[1];
318
+ if (!rel) continue;
319
+ if (!fileExists(rootAbs, rel)) {
320
+ delete next.agent[id];
321
+ pruned++;
322
+ }
323
+ }
324
+
325
+ const afterText = stableStringify(next);
326
+ // Semantic idempotency (ignore formatting / key order differences in the source file).
327
+ const semanticChanged = JSON.stringify(existing) !== JSON.stringify(next);
328
+
329
+ console.log(`Resolved root: ${rootAbs}`);
330
+ console.log(`Target opencode.json: ${opencodeAbs}`);
331
+ if (!fs.existsSync(agentsMdAbs)) console.log(`Note: missing AGENTS.md at ${agentsMdAbs} (commands may still run; templates reference it).`);
332
+
333
+ if (warnings.length) {
334
+ console.log("\nWarnings:");
335
+ for (const w of warnings) console.log(`- ${w}`);
336
+ }
337
+
338
+ if (!semanticChanged) {
339
+ console.log("\nIdempotency: 0 changes needed.");
340
+ process.exit(0);
341
+ }
342
+
343
+ console.log(`\nPlanned managed changes: created=${created}, updated=${updated}, pruned=${pruned}`);
344
+ if (args.dryRun) {
345
+ console.log("Dry-run: no changes made.");
346
+ process.exit(0);
347
+ }
348
+
349
+ fs.writeFileSync(opencodeAbs, afterText, "utf8");
350
+ console.log(`Wrote: ${opencodeAbs}`);
351
+ console.log(`Idempotency: updated ${created + updated + pruned} managed entries.`);
352
+ }
353
+
354
+ main();
355
+
@@ -0,0 +1,223 @@
1
+ ---
2
+ name: agent-browser
3
+ description: Browser automation using Vercel's agent-browser CLI. Use when you need to interact with web pages, fill forms, take screenshots, or scrape data. Alternative to Playwright MCP - uses Bash commands with ref-based element selection. Triggers on "browse website", "fill form", "click button", "take screenshot", "scrape page", "web automation".
4
+ ---
5
+
6
+ # agent-browser: CLI Browser Automation
7
+
8
+ Vercel's headless browser automation CLI designed for AI agents. Uses ref-based selection (@e1, @e2) from accessibility snapshots.
9
+
10
+ ## Setup Check
11
+
12
+ ```bash
13
+ # Check installation
14
+ command -v agent-browser >/dev/null 2>&1 && echo "Installed" || echo "NOT INSTALLED - run: npm install -g agent-browser && agent-browser install"
15
+ ```
16
+
17
+ ### Install if needed
18
+
19
+ ```bash
20
+ npm install -g agent-browser
21
+ agent-browser install # Downloads Chromium
22
+ ```
23
+
24
+ ## Core Workflow
25
+
26
+ **The snapshot + ref pattern is optimal for LLMs:**
27
+
28
+ 1. **Navigate** to URL
29
+ 2. **Snapshot** to get interactive elements with refs
30
+ 3. **Interact** using refs (@e1, @e2, etc.)
31
+ 4. **Re-snapshot** after navigation or DOM changes
32
+
33
+ ```bash
34
+ # Step 1: Open URL
35
+ agent-browser open https://example.com
36
+
37
+ # Step 2: Get interactive elements with refs
38
+ agent-browser snapshot -i --json
39
+
40
+ # Step 3: Interact using refs
41
+ agent-browser click @e1
42
+ agent-browser fill @e2 "search query"
43
+
44
+ # Step 4: Re-snapshot after changes
45
+ agent-browser snapshot -i
46
+ ```
47
+
48
+ ## Key Commands
49
+
50
+ ### Navigation
51
+
52
+ ```bash
53
+ agent-browser open <url> # Navigate to URL
54
+ agent-browser back # Go back
55
+ agent-browser forward # Go forward
56
+ agent-browser reload # Reload page
57
+ agent-browser close # Close browser
58
+ ```
59
+
60
+ ### Snapshots (Essential for AI)
61
+
62
+ ```bash
63
+ agent-browser snapshot # Full accessibility tree
64
+ agent-browser snapshot -i # Interactive elements only (recommended)
65
+ agent-browser snapshot -i --json # JSON output for parsing
66
+ agent-browser snapshot -c # Compact (remove empty elements)
67
+ agent-browser snapshot -d 3 # Limit depth
68
+ ```
69
+
70
+ ### Interactions
71
+
72
+ ```bash
73
+ agent-browser click @e1 # Click element
74
+ agent-browser dblclick @e1 # Double-click
75
+ agent-browser fill @e1 "text" # Clear and fill input
76
+ agent-browser type @e1 "text" # Type without clearing
77
+ agent-browser press Enter # Press key
78
+ agent-browser hover @e1 # Hover element
79
+ agent-browser check @e1 # Check checkbox
80
+ agent-browser uncheck @e1 # Uncheck checkbox
81
+ agent-browser select @e1 "option" # Select dropdown option
82
+ agent-browser scroll down 500 # Scroll (up/down/left/right)
83
+ agent-browser scrollintoview @e1 # Scroll element into view
84
+ ```
85
+
86
+ ### Get Information
87
+
88
+ ```bash
89
+ agent-browser get text @e1 # Get element text
90
+ agent-browser get html @e1 # Get element HTML
91
+ agent-browser get value @e1 # Get input value
92
+ agent-browser get attr href @e1 # Get attribute
93
+ agent-browser get title # Get page title
94
+ agent-browser get url # Get current URL
95
+ agent-browser get count "button" # Count matching elements
96
+ ```
97
+
98
+ ### Screenshots & PDFs
99
+
100
+ ```bash
101
+ agent-browser screenshot # Viewport screenshot
102
+ agent-browser screenshot --full # Full page
103
+ agent-browser screenshot output.png # Save to file
104
+ agent-browser screenshot --full output.png # Full page to file
105
+ agent-browser pdf output.pdf # Save as PDF
106
+ ```
107
+
108
+ ### Wait
109
+
110
+ ```bash
111
+ agent-browser wait @e1 # Wait for element
112
+ agent-browser wait 2000 # Wait milliseconds
113
+ agent-browser wait "text" # Wait for text to appear
114
+ ```
115
+
116
+ ## Semantic Locators (Alternative to Refs)
117
+
118
+ ```bash
119
+ agent-browser find role button click --name "Submit"
120
+ agent-browser find text "Sign up" click
121
+ agent-browser find label "Email" fill "user@example.com"
122
+ agent-browser find placeholder "Search..." fill "query"
123
+ ```
124
+
125
+ ## Sessions (Parallel Browsers)
126
+
127
+ ```bash
128
+ # Run multiple independent browser sessions
129
+ agent-browser --session browser1 open https://site1.com
130
+ agent-browser --session browser2 open https://site2.com
131
+
132
+ # List active sessions
133
+ agent-browser session list
134
+ ```
135
+
136
+ ## Examples
137
+
138
+ ### Login Flow
139
+
140
+ ```bash
141
+ agent-browser open https://app.example.com/login
142
+ agent-browser snapshot -i
143
+ # Output shows: textbox "Email" [ref=e1], textbox "Password" [ref=e2], button "Sign in" [ref=e3]
144
+ agent-browser fill @e1 "user@example.com"
145
+ agent-browser fill @e2 "password123"
146
+ agent-browser click @e3
147
+ agent-browser wait 2000
148
+ agent-browser snapshot -i # Verify logged in
149
+ ```
150
+
151
+ ### Search and Extract
152
+
153
+ ```bash
154
+ agent-browser open https://news.ycombinator.com
155
+ agent-browser snapshot -i --json
156
+ # Parse JSON to find story links
157
+ agent-browser get text @e12 # Get headline text
158
+ agent-browser click @e12 # Click to open story
159
+ ```
160
+
161
+ ### Form Filling
162
+
163
+ ```bash
164
+ agent-browser open https://forms.example.com
165
+ agent-browser snapshot -i
166
+ agent-browser fill @e1 "John Doe"
167
+ agent-browser fill @e2 "john@example.com"
168
+ agent-browser select @e3 "United States"
169
+ agent-browser check @e4 # Agree to terms
170
+ agent-browser click @e5 # Submit button
171
+ agent-browser screenshot confirmation.png
172
+ ```
173
+
174
+ ### Debug Mode
175
+
176
+ ```bash
177
+ # Run with visible browser window
178
+ agent-browser --headed open https://example.com
179
+ agent-browser --headed snapshot -i
180
+ agent-browser --headed click @e1
181
+ ```
182
+
183
+ ## JSON Output
184
+
185
+ Add `--json` for structured output:
186
+
187
+ ```bash
188
+ agent-browser snapshot -i --json
189
+ ```
190
+
191
+ Returns:
192
+ ```json
193
+ {
194
+ "success": true,
195
+ "data": {
196
+ "refs": {
197
+ "e1": {"name": "Submit", "role": "button"},
198
+ "e2": {"name": "Email", "role": "textbox"}
199
+ },
200
+ "snapshot": "- button \"Submit\" [ref=e1]\n- textbox \"Email\" [ref=e2]"
201
+ }
202
+ }
203
+ ```
204
+
205
+ ## vs Playwright MCP
206
+
207
+ | Feature | agent-browser (CLI) | Playwright MCP |
208
+ |---------|---------------------|----------------|
209
+ | Interface | Bash commands | MCP tools |
210
+ | Selection | Refs (@e1) | Refs (e1) |
211
+ | Output | Text/JSON | Tool responses |
212
+ | Parallel | Sessions | Tabs |
213
+ | Best for | Quick automation | Tool integration |
214
+
215
+ Use agent-browser when:
216
+ - You prefer Bash-based workflows
217
+ - You want simpler CLI commands
218
+ - You need quick one-off automation
219
+
220
+ Use Playwright MCP when:
221
+ - You need deep MCP tool integration
222
+ - You want tool-based responses
223
+ - You're building complex automation