<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: atani</title>
    <description>The latest articles on DEV Community by atani (@atani).</description>
    <link>https://dev.to/atani</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3773977%2F782f53f4-a8c2-4056-8d0a-bc9e4f55ed6b.jpeg</url>
      <title>DEV Community: atani</title>
      <link>https://dev.to/atani</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/atani"/>
    <language>en</language>
    <item>
      <title>16MB vs 1.2GB — Benchmarking 5 AI Browser Automation Tools</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Mon, 23 Mar 2026 01:37:37 +0000</pubDate>
      <link>https://dev.to/atani/16mb-vs-12gb-benchmarking-5-ai-browser-automation-tools-34pm</link>
      <guid>https://dev.to/atani/16mb-vs-12gb-benchmarking-5-ai-browser-automation-tools-34pm</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fatani%2Fzenn-content%2Fmain%2Fimages%2Fai-browser-automation-tools-benchmark%2Feyecatch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fatani%2Fzenn-content%2Fmain%2Fimages%2Fai-browser-automation-tools-benchmark%2Feyecatch.png" alt="Eyecatch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was using Claude Code for browser automation and found myself stuck choosing between tools. There are five candidates, each with a completely different approach. I installed all of them and ran 10 tests — turns out the best choice depends entirely on your use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; For auth management, go with playwright-CLI. For an agent operation backbone, agent-browser. For autonomous natural-language control, browser-use. For high-volume crawling, Lightpanda. For production infrastructure, steel-browser.&lt;/p&gt;

&lt;p&gt;This article focuses on CLI-mode comparisons. Some tools like browser-use shine brightest in LLM agent mode — I plan to cover that angle in a separate post.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 Tools Compared
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Stars&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;License&lt;/th&gt;
&lt;th&gt;Maintained by&lt;/th&gt;
&lt;th&gt;In a nutshell&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/microsoft/playwright-cli" rel="noopener noreferrer"&gt;playwright-cli&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;6.1K (+85K core)&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;td&gt;CLI strong on auth management and token efficiency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/vercel-labs/agent-browser" rel="noopener noreferrer"&gt;agent-browser&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;24.1K&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;td&gt;Vercel&lt;/td&gt;
&lt;td&gt;CLI purpose-built for agent development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/browser-use/browser-use" rel="noopener noreferrer"&gt;browser-use&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;82.2K&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;browser-use&lt;/td&gt;
&lt;td&gt;LLM autonomously operates the browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/lightpanda-io/browser" rel="noopener noreferrer"&gt;Lightpanda&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;23.6K&lt;/td&gt;
&lt;td&gt;Zig&lt;/td&gt;
&lt;td&gt;AGPL-3.0&lt;/td&gt;
&lt;td&gt;lightpanda-io&lt;/td&gt;
&lt;td&gt;Ultra-low-memory lightweight browser (official claim: 9x faster)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/steel-dev/steel-browser" rel="noopener noreferrer"&gt;steel-browser&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;6.7K&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;td&gt;steel-dev&lt;/td&gt;
&lt;td&gt;Production-grade browser infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Test Environment
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OS&lt;/td&gt;
&lt;td&gt;macOS Darwin 25.3.0 (Apple Silicon)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;
&lt;td&gt;v24.5.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;3.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;playwright-CLI&lt;/td&gt;
&lt;td&gt;0.1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;agent-browser&lt;/td&gt;
&lt;td&gt;0.21.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;browser-use&lt;/td&gt;
&lt;td&gt;0.12.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lightpanda&lt;/td&gt;
&lt;td&gt;nightly (c1fc2b13)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;steel-browser&lt;/td&gt;
&lt;td&gt;Docker (latest)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Results depend on hardware and network conditions. Browser startup speed and memory usage vary significantly with CPU/RAM configuration, so treat these numbers as directional guidance. All tools are under active development — results may differ with newer versions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setup: Lightpanda Is a Single 12MB Binary
&lt;/h2&gt;

&lt;p&gt;The first hurdle is installation. Here's how each tool goes from zero to running:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Dependencies&lt;/th&gt;
&lt;th&gt;Steps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lightpanda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Binary download (12MB)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1 step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;agent-browser&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;npm i -g agent-browser&lt;/code&gt; + &lt;code&gt;install&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Node.js + Chrome download&lt;/td&gt;
&lt;td&gt;2 steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;playwright-CLI&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;npm i -g @playwright/cli&lt;/code&gt; + &lt;code&gt;install-browser&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Node.js + browser download&lt;/td&gt;
&lt;td&gt;2 steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;browser-use&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pip install browser-use&lt;/code&gt; (75 deps)&lt;/td&gt;
&lt;td&gt;Python 3.11+ + LLM API key&lt;/td&gt;
&lt;td&gt;2 steps + config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;steel-browser&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker pull&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;1 step (assumes Docker)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Lightpanda is just one binary download — zero dependencies, as simple as it gets. agent-browser and playwright-CLI are a single npm command each, but require a separate browser download. browser-use pulls in 75 Python packages and needs an LLM API key configured on top. steel-browser is one command if Docker is already running, but Docker itself is a prerequisite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Raw Speed: steel-browser Clocks 0.45s
&lt;/h2&gt;

&lt;p&gt;We opened &lt;code&gt;httpbin.org/html&lt;/code&gt;, took a snapshot, and closed the browser — measured three times each.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Run 1&lt;/th&gt;
&lt;th&gt;Run 2&lt;/th&gt;
&lt;th&gt;Run 3&lt;/th&gt;
&lt;th&gt;Average&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;steel-browser&lt;/td&gt;
&lt;td&gt;0.70s&lt;/td&gt;
&lt;td&gt;0.41s&lt;/td&gt;
&lt;td&gt;0.25s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.45s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lightpanda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.05s&lt;/td&gt;
&lt;td&gt;0.85s&lt;/td&gt;
&lt;td&gt;0.85s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.92s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;agent-browser&lt;/td&gt;
&lt;td&gt;1.93s&lt;/td&gt;
&lt;td&gt;1.80s&lt;/td&gt;
&lt;td&gt;1.90s&lt;/td&gt;
&lt;td&gt;1.88s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;playwright-CLI&lt;/td&gt;
&lt;td&gt;2.15s&lt;/td&gt;
&lt;td&gt;1.84s&lt;/td&gt;
&lt;td&gt;1.85s&lt;/td&gt;
&lt;td&gt;1.95s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;browser-use&lt;/td&gt;
&lt;td&gt;2.30s&lt;/td&gt;
&lt;td&gt;10.21s&lt;/td&gt;
&lt;td&gt;2.22s&lt;/td&gt;
&lt;td&gt;4.91s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;steel-browser connects to an always-running Chromium inside Docker, making the second and third runs especially fast. Lightpanda's custom engine avoids Chrome's startup overhead and delivers consistently quick results. agent-browser and playwright-CLI both run in daemon mode with stable performance. browser-use carries Python runtime startup cost and occasionally spikes (10.21s).&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory: 16MB vs 1.2GB — a 75x Gap
&lt;/h2&gt;

&lt;p&gt;We measured process memory (RSS) with a single page (&lt;code&gt;httpbin.org/html&lt;/code&gt;) open.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Daemon&lt;/th&gt;
&lt;th&gt;Browser&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;th&gt;Processes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lightpanda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;16 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;steel-browser&lt;/td&gt;
&lt;td&gt;(in Docker)&lt;/td&gt;
&lt;td&gt;(in Docker)&lt;/td&gt;
&lt;td&gt;581 MB&lt;/td&gt;
&lt;td&gt;container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;browser-use&lt;/td&gt;
&lt;td&gt;111 MB&lt;/td&gt;
&lt;td&gt;758 MB&lt;/td&gt;
&lt;td&gt;869 MB&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;playwright-CLI&lt;/td&gt;
&lt;td&gt;169 MB&lt;/td&gt;
&lt;td&gt;760 MB&lt;/td&gt;
&lt;td&gt;929 MB&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;agent-browser&lt;/td&gt;
&lt;td&gt;5 MB&lt;/td&gt;
&lt;td&gt;1,197 MB&lt;/td&gt;
&lt;td&gt;1,202 MB&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Lightpanda uses 16MB — roughly 75x less than agent-browser at 1,202MB. The official benchmark claims "9x less memory than Chrome," but in our single-page test the gap was even wider. Results will vary depending on test conditions and page complexity. This is because Lightpanda's engine skips CSS rendering and focuses on DOM and JS execution. steel-browser bundles Chromium and Node.js inside a Docker container at 581MB — consolidating host processes into a container is a practical operational advantage. browser-use's Python daemon takes 111MB, but the lower Chrome process count keeps the total at 869MB. agent-browser's daemon itself is a lean 5MB Rust binary, but the many Chrome processes push the total to the highest.&lt;/p&gt;

&lt;h2&gt;
  
  
  SPA Support: All 5 Tools Loaded react.dev Successfully
&lt;/h2&gt;

&lt;p&gt;We tested whether each tool could take a proper snapshot of &lt;code&gt;react.dev&lt;/code&gt; (React's official site, a client-side rendered SPA).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Output size&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;steel-browser&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.45s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;16 KB&lt;/td&gt;
&lt;td&gt;Markdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lightpanda&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;4.20s&lt;/td&gt;
&lt;td&gt;18 KB&lt;/td&gt;
&lt;td&gt;Markdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;agent-browser&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;4.83s&lt;/td&gt;
&lt;td&gt;34 KB&lt;/td&gt;
&lt;td&gt;Accessibility tree (with ref IDs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;playwright-CLI&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;6.22s&lt;/td&gt;
&lt;td&gt;137 B&lt;/td&gt;
&lt;td&gt;Snapshot file reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;browser-use&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;12.77s&lt;/td&gt;
&lt;td&gt;Title only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;extract&lt;/code&gt; requires LLM agent mode&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All five tools loaded the page successfully. steel-browser's always-on Chromium gives it the edge at 1.45s even for SPAs. Lightpanda returned 18KB of Markdown in 4.20s, handling react.dev-level SPAs just fine. agent-browser returns an accessibility tree with ref IDs, which maps directly to action commands. playwright-CLI outputs only a file reference (137 bytes) — a deliberate design for token efficiency. browser-use in CLI-only mode captures the page title; structured extraction requires LLM agent mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auth Persistence: playwright-CLI's state-save/load Is the Most Complete
&lt;/h2&gt;

&lt;p&gt;When an AI agent needs to operate internal tools behind SAML/SSO, being able to save and restore auth state is critical. We tested using &lt;code&gt;httpbin.org&lt;/code&gt;'s cookie endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  playwright-CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set cookie → save → restore&lt;/span&gt;
playwright-cli open https://httpbin.org/cookies/set/saml_token/mock_abc123 &lt;span class="nt"&gt;--persistent&lt;/span&gt;
playwright-cli cookie-list
&lt;span class="c"&gt;# → saml_token=mock_abc123 (domain: httpbin.org, path: /)&lt;/span&gt;

playwright-cli state-save /tmp/pw-auth.json
playwright-cli close

&lt;span class="c"&gt;# Restore in a new session&lt;/span&gt;
playwright-cli open https://httpbin.org &lt;span class="nt"&gt;--persistent&lt;/span&gt;
playwright-cli state-load /tmp/pw-auth.json
playwright-cli cookie-list
&lt;span class="c"&gt;# → saml_token=mock_abc123  ← restored successfully&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;cookie-list&lt;/code&gt;/&lt;code&gt;cookie-set&lt;/code&gt;/&lt;code&gt;state-save&lt;/code&gt;/&lt;code&gt;state-load&lt;/code&gt; commands and a &lt;code&gt;--persistent&lt;/code&gt; flag that saves the browser profile to disk, playwright-CLI offers the most complete auth management of all five tools. Cookies and localStorage survive restarts.&lt;/p&gt;

&lt;h3&gt;
  
  
  agent-browser
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agent-browser open https://httpbin.org/cookies/set/saml_token/mock_abc123
agent-browser state save test-auth
agent-browser close

agent-browser open https://httpbin.org/cookies
agent-browser state load test-auth
&lt;span class="c"&gt;# → Cookie restored&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supports &lt;code&gt;state save/load&lt;/code&gt; commands and a &lt;code&gt;--profile&lt;/code&gt; flag for persistence. The Auth vault feature encrypts credentials, allowing agents to log in without ever seeing the password.&lt;/p&gt;

&lt;h3&gt;
  
  
  browser-use
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;cookies export/import&lt;/code&gt; commands handle cookie serialization. The &lt;code&gt;--profile&lt;/code&gt; flag preserves cookies and localStorage across sessions. Comprehensive state management is achieved by combining LLM agent mode with profile persistence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lightpanda
&lt;/h3&gt;

&lt;p&gt;Each fetch runs as an independent process — the design is optimized for high-volume public page retrieval. For authenticated access, you can pass HTTP headers or cookies as command-line arguments.&lt;/p&gt;

&lt;h3&gt;
  
  
  steel-browser
&lt;/h3&gt;

&lt;p&gt;Cookies persist via session ID reuse. We confirmed that a &lt;code&gt;saml_token=mock_abc123&lt;/code&gt; cookie survived session recreation. steel-browser manages sessions through a REST API to which Playwright/Puppeteer clients connect, so the actual auth logic lives on the client side.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auth Persistence Summary
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Cookie management&lt;/th&gt;
&lt;th&gt;State save/restore&lt;/th&gt;
&lt;th&gt;Profile persistence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;playwright-CLI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;cookie-list/set/get built in&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;state-save/load&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;--persistent&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;agent-browser&lt;/td&gt;
&lt;td&gt;Via state&lt;/td&gt;
&lt;td&gt;state save/load + Auth vault&lt;/td&gt;
&lt;td&gt;--profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;browser-use&lt;/td&gt;
&lt;td&gt;cookies export/import&lt;/td&gt;
&lt;td&gt;Profile-based&lt;/td&gt;
&lt;td&gt;--profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;steel-browser&lt;/td&gt;
&lt;td&gt;Session ID-based (verified)&lt;/td&gt;
&lt;td&gt;REST API&lt;/td&gt;
&lt;td&gt;Per-session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lightpanda&lt;/td&gt;
&lt;td&gt;Via HTTP headers&lt;/td&gt;
&lt;td&gt;Per-fetch&lt;/td&gt;
&lt;td&gt;Lightweight design — out of scope&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Parallel Execution: Lightpanda Stays at ~1.6GB Even at 100 Concurrent Sessions
&lt;/h2&gt;

&lt;p&gt;We spun up 3 sessions simultaneously and measured completion time and total memory.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;3-session startup time&lt;/th&gt;
&lt;th&gt;Total memory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;playwright-CLI&lt;/td&gt;
&lt;td&gt;4.90s&lt;/td&gt;
&lt;td&gt;559 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lightpanda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9.34s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~48 MB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;agent-browser&lt;/td&gt;
&lt;td&gt;10.58s&lt;/td&gt;
&lt;td&gt;4,165 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;browser-use&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;steel-browser&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;browser-use supports session management via its &lt;code&gt;--session&lt;/code&gt; option, but we did not test it here. steel-browser can create multiple sessions concurrently through its REST API, but was also excluded from this measurement.&lt;/p&gt;

&lt;p&gt;playwright-CLI manages parallel sessions with named sessions (&lt;code&gt;-s s1&lt;/code&gt;, &lt;code&gt;-s s2&lt;/code&gt;) and shares Chrome processes efficiently. Lightpanda runs each fetch as an independent process (~16MB each), so even 100 concurrent sessions would use an estimated 1.6GB (16MB x 100). agent-browser launches an independent Chrome instance per session, leading to higher memory consumption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error Handling: agent-browser Gives the Clearest Messages
&lt;/h2&gt;

&lt;p&gt;We tested behavior on a nonexistent URL, a 404 page, and a timeout scenario.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;playwright-CLI&lt;/th&gt;
&lt;th&gt;agent-browser&lt;/th&gt;
&lt;th&gt;browser-use&lt;/th&gt;
&lt;th&gt;Lightpanda&lt;/th&gt;
&lt;th&gt;steel-browser&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DNS failure&lt;/td&gt;
&lt;td&gt;Opens page but content is empty&lt;/td&gt;
&lt;td&gt;&lt;code&gt;✗ net::ERR_NAME_NOT_RESOLVED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shows URL only&lt;/td&gt;
&lt;td&gt;HTML error (&lt;code&gt;NavigationFailed: CouldntResolveHost&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;JSON &lt;code&gt;{"message":"net::ERR_NAME_NOT_RESOLVED at ..."}&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;404&lt;/td&gt;
&lt;td&gt;Opens page but content is empty&lt;/td&gt;
&lt;td&gt;&lt;code&gt;✗ net::ERR_HTTP_RESPONSE_CODE_FAILURE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shows URL only&lt;/td&gt;
&lt;td&gt;Empty HTML&lt;/td&gt;
&lt;td&gt;Structured JSON error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timeout&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Disconnects precisely at specified time (3.03s)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;agent-browser gives the most specific error messages — you can tell at a glance what went wrong, making it straightforward to build retry logic. steel-browser returns structured JSON errors, ideal for programmatic parsing. Lightpanda returns structured HTML errors, also easy to process programmatically. browser-use shows only the URL — detailed error info requires separate investigation. playwright-CLI reflects Chrome's native behavior of attempting to open the page even on errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  JS-Heavy Sites: steel-browser Fastest Across the Board
&lt;/h2&gt;

&lt;p&gt;We tested how each tool handles sites built as SPAs (Single Page Applications).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Site&lt;/th&gt;
&lt;th&gt;playwright-CLI&lt;/th&gt;
&lt;th&gt;agent-browser&lt;/th&gt;
&lt;th&gt;browser-use&lt;/th&gt;
&lt;th&gt;Lightpanda&lt;/th&gt;
&lt;th&gt;steel-browser&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HackerNews&lt;/td&gt;
&lt;td&gt;OK (4.3s)&lt;/td&gt;
&lt;td&gt;OK (4.0s)&lt;/td&gt;
&lt;td&gt;OK (4.24s)&lt;/td&gt;
&lt;td&gt;OK (1.1s)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;OK (0.92s)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;td&gt;OK (2.9s)&lt;/td&gt;
&lt;td&gt;OK (1.6s)&lt;/td&gt;
&lt;td&gt;OK (title only)&lt;/td&gt;
&lt;td&gt;OK (2.9s)&lt;/td&gt;
&lt;td&gt;OK (3.45s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;react.dev&lt;/td&gt;
&lt;td&gt;OK (6.2s)&lt;/td&gt;
&lt;td&gt;OK (4.8s)&lt;/td&gt;
&lt;td&gt;OK (12.8s)&lt;/td&gt;
&lt;td&gt;OK (4.2s)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;OK (1.5s)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Chrome-based tools (playwright-CLI, agent-browser, browser-use) handle all sites reliably. steel-browser's always-on Chromium inside Docker makes it the fastest on JS-heavy sites — 0.92s for HackerNews, 1.5s for react.dev. Lightpanda performs well on server-side-rendered sites (HackerNews, GitHub) but has limitations with SPAs that rely heavily on client-side JS. This is by design — Lightpanda skips CSS rendering and focuses on DOM and JS execution. Broader SPA support is on the roadmap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token Efficiency: playwright-CLI's 317B Is the Smallest
&lt;/h2&gt;

&lt;p&gt;When an AI agent operates a browser, how much of the LLM's context window it consumes matters. We compared snapshot output sizes for &lt;code&gt;github.com/microsoft/playwright&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool / Format&lt;/th&gt;
&lt;th&gt;Output size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;browser-use &lt;code&gt;get text&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;112 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;playwright-CLI snapshot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;317 bytes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;agent-browser snapshot -i (compact)&lt;/td&gt;
&lt;td&gt;16 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;steel-browser markdown&lt;/td&gt;
&lt;td&gt;31 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lightpanda markdown&lt;/td&gt;
&lt;td&gt;36 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;agent-browser snapshot (full)&lt;/td&gt;
&lt;td&gt;70 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lightpanda html&lt;/td&gt;
&lt;td&gt;409 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;browser-use &lt;code&gt;get html&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;436 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;steel-browser html&lt;/td&gt;
&lt;td&gt;445 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;browser-use's &lt;code&gt;get text&lt;/code&gt; returns just 112 bytes (the page title) — the most token-efficient option for existence checks and quick verifications. However, structured data extraction requires &lt;code&gt;get html&lt;/code&gt; (436KB) or the &lt;code&gt;extract&lt;/code&gt; command in LLM agent mode.&lt;/p&gt;

&lt;p&gt;playwright-CLI's 317 bytes comes from saving the snapshot payload to a file and passing only the file reference to the LLM. For coding agents handling large codebases (Claude Code, GitHub Copilot, etc.), conserving context on browser operations is essential — this design choice makes a lot of sense.&lt;/p&gt;

&lt;p&gt;agent-browser's &lt;code&gt;-i&lt;/code&gt; (inline compact) mode delivers 16KB while preserving the ref-numbered structure needed for interaction. steel-browser's Markdown output is 31KB, on par with Lightpanda's 36KB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Output Quality: ref-tagged Tree vs Markdown vs JS Execution
&lt;/h2&gt;

&lt;p&gt;We extracted top article titles from HackerNews to compare output quality.&lt;/p&gt;

&lt;h3&gt;
  
  
  agent-browser — Actionable structured data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- link "Flash-Moe: Running a 397B Parameter Model on a Mac with 48GB RAM" [ref=e111]
- link "Hormuz Minesweeper – Are you tired of winning?" [ref=e116]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each element carries a ref ID — &lt;code&gt;click e111&lt;/code&gt; clicks that link. The output maps directly to agent commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lightpanda — Readable Markdown
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| 1. | &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Flash-Moe: Running a 397B Parameter Model...&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://github.com/danveloper/flash-moe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; |
| 2. | &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Hormuz Minesweeper – Are you tired of winning?&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://hormuz.pythonic.ninja/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Markdown table format, easy for both humans to read and LLMs to parse. Includes full link URLs.&lt;/p&gt;

&lt;h3&gt;
  
  
  playwright-CLI — Flexible extraction via JS execution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;playwright-cli run-code &lt;span class="s2"&gt;"const titles = await page.&lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="s2"&gt;eval('.titleline &amp;gt; a', els =&amp;gt; els.map(a =&amp;gt; a.textContent));"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run arbitrary JS, giving maximum extraction flexibility. The tradeoff is that you need to know the page structure beforehand.&lt;/p&gt;

&lt;h3&gt;
  
  
  browser-use — LLM decides autonomously
&lt;/h3&gt;

&lt;p&gt;browser-use's &lt;code&gt;extract&lt;/code&gt; command operates in LLM agent mode, where the LLM interprets the page and returns structured data. No selectors needed — you can simply say "get me a list of article titles" in natural language. In CLI-only mode (&lt;code&gt;get text&lt;/code&gt; / &lt;code&gt;get html&lt;/code&gt;), you get raw data.&lt;/p&gt;

&lt;h3&gt;
  
  
  steel-browser — Complete HTML structure
&lt;/h3&gt;

&lt;p&gt;steel-browser's scrape API returns full HTML with link structures intact. Built-in Markdown conversion lets you pick the output format to suit your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Should Use What
&lt;/h2&gt;

&lt;h3&gt;
  
  
  playwright-CLI — Engineers working in enterprise SAML/SSO environments
&lt;/h3&gt;

&lt;p&gt;Ideal if you want an AI agent to operate internal tools like GitHub Enterprise or Jira behind SAML auth. Cookie/state-save/load is the most feature-complete of all five tools, ensuring reliable auth state persistence. Integration with Claude Code works instantly via SKILL.md. The 317-byte snapshot output stands out for token efficiency, preserving context in coding agents dealing with large codebases. It's also the only tool supporting Firefox and WebKit, making it useful for cross-browser E2E automation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Internal tool automation, auth-heavy workflows, Claude Code/Copilot companion tool, cross-browser testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  agent-browser — Full-stack engineers building custom AI agents
&lt;/h3&gt;

&lt;p&gt;The ideal pick if you want to design your own agent loop while keeping browser operations simple. The ref-tagged accessibility tree (&lt;code&gt;[ref=e111]&lt;/code&gt;) from &lt;code&gt;snapshot&lt;/code&gt; can be used directly as action instructions, so "snapshot → decide → click ref" loops write themselves naturally. Error messages are the most helpful for debugging during development. The Rust-built daemon is just 5MB. Features like the Auth vault for encrypted credential management and the diff command for verifying operations make it a rich toolkit for agent development. You can also switch to Lightpanda as a backend for a lighter footprint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Custom AI agent development, browser automation bot prototypes, action-verification loop implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  browser-use — Python engineers who want the fastest path to a browser automation prototype
&lt;/h3&gt;

&lt;p&gt;The only tool where "find the cheapest price on this e-commerce site" or "apply to this job posting" is achievable in a few lines of Python. The LLM looks at the page and decides what to do, so you skip the selector research and page structure analysis. DOM + screenshot dual input adapts automatically to layout changes. Supports 15+ LLM providers (Claude, GPT-4o, Gemini, Ollama, etc.), including local models. Tasks are defined in natural language, which makes it easier to discuss "what the agent should do" with non-engineering team members.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Business process automation POCs, natural-language task definitions, ad-hoc web automation, RPA-style usage of internal tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lightpanda — Data engineers processing high volumes of web pages
&lt;/h3&gt;

&lt;p&gt;100 pages simultaneously at 48MB of memory. Chrome would use over 4GB. That gap translates directly to infrastructure cost. Lightpanda excels at extracting data from structured sites (HackerNews, GitHub), collecting web content for RAG pipelines, and parsing large batches of URLs. A single 12MB binary with zero dependencies, and built-in MCP support for direct AI agent integration. Rich Markdown and semantic tree output makes the data easy for LLMs to digest. SPA support is expanding in upcoming releases. For static sites and structured data, it already delivers a substantial performance advantage.&lt;/p&gt;

&lt;p&gt;The AGPL license requires source code disclosure when providing the software as a network service. Check the license terms before commercial use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; RAG web crawling, bulk URL content extraction, lightweight browser processing in CI/CD pipelines, resource-constrained environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  steel-browser — Infra/SRE teams running browser automation in production
&lt;/h3&gt;

&lt;p&gt;Built for teams that want to take Playwright or Puppeteer scripts straight to production. Fingerprint spoofing, automatic proxy rotation, CAPTCHA handling — steel-browser tackles the "walls you always hit in production" at the infrastructure layer. REST API session management fits naturally into microservice architectures. Deploy with a single Docker command, or use one-click deployment to Railway or Render. Apache-2.0 license means the self-hosted version is free to use.&lt;/p&gt;

&lt;p&gt;Always check the target site's terms of service and robots.txt before use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Production web scraping, data collection requiring bot detection evasion, infrastructure hardening for existing Playwright scripts, shared browser automation platforms for teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision Flowchart
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fatani%2Fzenn-content%2Fmain%2Fimages%2Fai-browser-automation-tools-benchmark%2Fflowchart.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fatani%2Fzenn-content%2Fmain%2Fimages%2Fai-browser-automation-tools-benchmark%2Fflowchart.png" alt="Decision flowchart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overall Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;playwright-CLI&lt;/th&gt;
&lt;th&gt;agent-browser&lt;/th&gt;
&lt;th&gt;browser-use&lt;/th&gt;
&lt;th&gt;Lightpanda&lt;/th&gt;
&lt;th&gt;steel-browser&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SPA support&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth persistence&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parallel execution&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error handling&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JS-heavy sites&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token efficiency&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output quality&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Excellent = stands out in this aspect. Good = practical. Fair = has limitations. — = not tested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;I personally use playwright-CLI with Claude Code in my daily workflow. The solid auth management and token efficiency fit well into my development routine.&lt;/p&gt;

&lt;p&gt;Each of the five tools has a clearly defined sweet spot. For auth management, playwright-CLI. For agent development, agent-browser. For autonomous natural-language operation, browser-use. For high-volume crawling, Lightpanda. For production infrastructure, steel-browser. I encourage you to try the one that matches your use case.&lt;/p&gt;

&lt;p&gt;The data in this article was measured in March 2026. This space moves fast — new tools appear monthly and existing ones evolve rapidly. It's always worth re-running benchmarks on the latest versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/microsoft/playwright-cli" rel="noopener noreferrer"&gt;microsoft/playwright-cli&lt;/a&gt; / &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vercel-labs/agent-browser" rel="noopener noreferrer"&gt;vercel-labs/agent-browser&lt;/a&gt; / &lt;a href="https://agent-browser.dev/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/browser-use/browser-use" rel="noopener noreferrer"&gt;browser-use/browser-use&lt;/a&gt; / &lt;a href="https://browser-use.com/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lightpanda-io/browser" rel="noopener noreferrer"&gt;lightpanda-io/browser&lt;/a&gt; / &lt;a href="https://lightpanda.io/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/steel-dev/steel-browser" rel="noopener noreferrer"&gt;steel-dev/steel-browser&lt;/a&gt; / &lt;a href="https://steel.dev/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>playwright</category>
      <category>browserautomation</category>
      <category>webdev</category>
    </item>
    <item>
      <title>mysh — A MySQL Connection Manager That Auto-Masks PII in Query Output</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Sun, 22 Mar 2026 13:59:19 +0000</pubDate>
      <link>https://dev.to/atani/mysh-a-mysql-connection-manager-that-auto-masks-pii-in-query-output-5a73</link>
      <guid>https://dev.to/atani/mysh-a-mysql-connection-manager-that-auto-masks-pii-in-query-output-5a73</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;You get an error notification filed as an issue. You ask Claude Code to investigate the codebase, and it turns out you need real data to narrow down the root cause. You write a SQL query, run it manually, and paste the results back to Claude Code.&lt;/p&gt;

&lt;p&gt;But if the query results contain personal information — emails, phone numbers, names — you can't just hand them over as-is. Every time, you either rewrite the SQL to exclude sensitive columns or manually redact PII from the output before pasting. Do this dozens of times a day and you'll inevitably forget once. And once is all it takes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Existing Tools Don't Solve This
&lt;/h2&gt;

&lt;p&gt;I looked for a MySQL client with output masking. None of the popular ones had it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Masking?&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;mycli&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Focused on syntax highlighting and autocomplete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DataGrip&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Can hide columns, but doesn't mask output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TablePlus / DBeaver&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Same story across GUI clients&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL Enterprise&lt;/td&gt;
&lt;td&gt;Server-side only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;mask_inner()&lt;/code&gt; etc. — requires Enterprise Edition&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MySQL Enterprise Edition has server-side masking functions, but they're paid and require DB-level configuration. No client-side tool offered output masking with AI workflows in mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I Built mysh
&lt;/h2&gt;

&lt;p&gt;If it doesn't exist, build it. I wrote &lt;strong&gt;mysh&lt;/strong&gt;, a MySQL connection manager that auto-masks query output, in Go. Built it with Claude Code in two days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production — masked automatically:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mysh run prod-db -e "SELECT id, name, email, phone FROM users LIMIT 3"

+----+-------+---------------------+----------------+
| id | name  | email               | phone          |
+----+-------+---------------------+----------------+
|  1 | A***  | a***@example.com    | 0***           |
|  2 | B***  | b***@company.co.jp  | 0***           |
|  3 | C***  | c***@gmail.com      | 0***           |
+----+-------+---------------------+----------------+
3 rows in set
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Development — raw data as-is:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mysh run dev-db -e "SELECT id, name, email, phone FROM users LIMIT 3"

+----+-------------+------------------------+---------------+
| id | name        | email                  | phone         |
+----+-------------+------------------------+---------------+
|  1 | Alice Smith | alice@example.com      | 090-1234-5678 |
|  2 | Bob Jones   | bob@company.co.jp      | 080-9876-5432 |
|  3 | Carol Lee   | carol@gmail.com        | 070-1111-2222 |
+----+-------------+------------------------+---------------+
3 rows in set
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Works: Environment × Output Target
&lt;/h2&gt;

&lt;p&gt;mysh determines whether to mask based on two factors: the &lt;strong&gt;connection's environment&lt;/strong&gt; and the &lt;strong&gt;output destination&lt;/strong&gt; (terminal vs. pipe).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;env&lt;/th&gt;
&lt;th&gt;Terminal (human)&lt;/th&gt;
&lt;th&gt;Pipe/capture (AI)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;production&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Auto-mask&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Auto-mask&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;staging&lt;/td&gt;
&lt;td&gt;Raw&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Auto-mask&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;development&lt;/td&gt;
&lt;td&gt;Raw&lt;/td&gt;
&lt;td&gt;Raw&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Production connections are always masked regardless of output target. If you need raw data, use the &lt;code&gt;--raw&lt;/code&gt; flag — but it &lt;strong&gt;requires interactive confirmation&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mysh run prod-db -e "SELECT * FROM users" --raw
⚠ Raw output requested for production connection "prod-db".
  Masking will be disabled. Continue? [y/N]:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirmation only works on a TTY (terminal). AI tools and scripts run in non-TTY mode, so they physically cannot respond to the prompt — making it impossible for them to bypass masking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Masked Columns
&lt;/h3&gt;

&lt;p&gt;When adding a connection, you specify which columns to mask. Both exact matches and wildcards are supported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Columns to mask (comma-separated, wildcards OK) [email,phone,*password*,*secret*,*token*,*address*]:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For production and staging environments, sensible defaults are suggested — just press Enter to cover common PII columns. Setting &lt;code&gt;*pass*&lt;/code&gt; masks any column containing "pass" in its name.&lt;/p&gt;

&lt;p&gt;Pro tip: show your schema to Claude Code and ask it to pick which columns should be masked. It catches things you might miss.&lt;/p&gt;

&lt;h3&gt;
  
  
  Masking Examples
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Original&lt;/th&gt;
&lt;th&gt;Masked&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:alice@example.com"&gt;alice@example.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;a***@example.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phone&lt;/td&gt;
&lt;td&gt;090-1234-5678&lt;/td&gt;
&lt;td&gt;0***&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;A***&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adding a Connection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mysh add
Connection name: prod-db
Environment (production/staging/development): production
DB host [127.0.0.1]: db.internal
DB port [3306]:
DB user: app_readonly
DB password: ********
DB name: myapp
Use SSH tunnel? (y/n): y
SSH host: bastion.example.com
SSH user: deploy
SSH port [22]:
Columns to mask (comma-separated, wildcards OK) [email,phone,*password*,*secret*,*token*,*address*]:

Testing connection... ✓ Connected successfully
Connection "prod-db" saved.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Walk through the setup interactively, or pre-fill fields with CLI flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Fully interactive&lt;/span&gt;
mysh add

&lt;span class="c"&gt;# Pre-fill with flags (password is always entered interactively)&lt;/span&gt;
mysh add &lt;span class="nt"&gt;--name&lt;/span&gt; prod &lt;span class="nt"&gt;--env&lt;/span&gt; prod &lt;span class="nt"&gt;--db-host&lt;/span&gt; 127.0.0.1 &lt;span class="nt"&gt;--db-user&lt;/span&gt; app &lt;span class="nt"&gt;--db-name&lt;/span&gt; myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ssh-host&lt;/span&gt; bastion.example.com &lt;span class="nt"&gt;--ssh-user&lt;/span&gt; deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A connection test runs after setup. If it fails, you can fix the specific field on the spot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Queries
&lt;/h3&gt;

&lt;p&gt;If you only have one connection, the name can be omitted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysh run &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SELECT COUNT(*) FROM users"&lt;/span&gt;  &lt;span class="c"&gt;# Inline SQL&lt;/span&gt;
mysh run query.sql                          &lt;span class="c"&gt;# SQL file&lt;/span&gt;
mysh tables                                 &lt;span class="c"&gt;# List tables&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SSH Tunneling
&lt;/h3&gt;

&lt;p&gt;Connections through a bastion host work with a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysh tunnel production                                  &lt;span class="c"&gt;# Start tunnel&lt;/span&gt;
mysh run production &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"SHOW PROCESSLIST"&lt;/span&gt;               &lt;span class="c"&gt;# Auto-reuses tunnel&lt;/span&gt;
mysh tunnel stop production                             &lt;span class="c"&gt;# Stop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Security Design
&lt;/h2&gt;

&lt;p&gt;Beyond masking, mysh is designed to minimize the risk of credential leaks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encrypted passwords&lt;/strong&gt; — AES-256-GCM encryption with Argon2id key derivation, resistant to GPU brute-force attacks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keychain integration&lt;/strong&gt; — On macOS, the master password is stored in Keychain so you don't type it every time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File permissions&lt;/strong&gt; — Config files are created with &lt;code&gt;0600&lt;/code&gt;, preventing access by other users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No password CLI flags&lt;/strong&gt; — Passwords cannot be passed as CLI arguments, preventing leaks via shell history or process lists&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Notes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Go?
&lt;/h3&gt;

&lt;p&gt;Single-binary distribution and easy cross-compilation make Go ideal for CLI tools. The &lt;code&gt;golang.org/x/term&lt;/code&gt; package provides TTY detection, which made implementing the environment-aware masking behavior straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  TTY Detection for Masking
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;term.IsTerminal()&lt;/code&gt; checks whether stdout is a terminal or a pipe. Combined with the environment setting, this determines masking behavior. Production always masks regardless; the &lt;code&gt;--raw&lt;/code&gt; override additionally checks &lt;code&gt;os.Stdin&lt;/code&gt; for TTY to block non-interactive bypass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ShouldMask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isTTY&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Env&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"production"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Env&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"development"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isTTY&lt;/span&gt; &lt;span class="c"&gt;// staging: mask only when piped&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connect with a READ-only user&lt;/strong&gt; — Combined with masking, this also prevents accidental data modification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use mysh for development too&lt;/strong&gt; — Masking isn't applied in dev, so it doesn't interfere with testing. Using mysh everywhere means you don't switch tools when investigating production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let AI pick masked columns&lt;/strong&gt; — Run &lt;code&gt;SHOW COLUMNS FROM table_name&lt;/code&gt; and ask Claude Code to identify PII columns. It's thorough&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Column-name-based masking&lt;/strong&gt; — Irregular column names (&lt;code&gt;col1&lt;/code&gt;, &lt;code&gt;data&lt;/code&gt;, etc.) require manual configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL only&lt;/strong&gt; — No PostgreSQL or other database support&lt;/li&gt;
&lt;li&gt;Masking format is fixed: first character preserved, rest replaced with &lt;code&gt;***&lt;/code&gt;. No customization&lt;/li&gt;
&lt;li&gt;Large result sets (tens of thousands of rows) incur overhead from the masking pass&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;Install with Homebrew in 30 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew tap atani/tap &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew &lt;span class="nb"&gt;install &lt;/span&gt;mysh
mysh add  &lt;span class="c"&gt;# Interactively add a connection&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you find it useful, a star on GitHub would mean a lot.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/atani/mysh" rel="noopener noreferrer"&gt;https://github.com/atani/mysh&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>go</category>
      <category>cli</category>
      <category>security</category>
    </item>
    <item>
      <title>Benchmarked browser-use CLI vs playwright-cli for Claude Code browser automation.</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Sat, 21 Mar 2026 14:29:51 +0000</pubDate>
      <link>https://dev.to/atani/benchmarked-browser-use-cli-vs-playwright-cli-for-claude-code-browser-automation-3ah4</link>
      <guid>https://dev.to/atani/benchmarked-browser-use-cli-vs-playwright-cli-for-claude-code-browser-automation-3ah4</guid>
      <description>&lt;p&gt;Benchmarked browser-use CLI vs playwright-cli for Claude Code browser automation.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>I built a custom slash command that lets Claude Code triage GitHub issues — deciding which to clo...</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Fri, 20 Mar 2026 14:37:30 +0000</pubDate>
      <link>https://dev.to/atani/i-built-a-custom-slash-command-that-lets-claude-code-triage-github-issues-deciding-which-to-clo-4gej</link>
      <guid>https://dev.to/atani/i-built-a-custom-slash-command-that-lets-claude-code-triage-github-issues-deciding-which-to-clo-4gej</guid>
      <description>&lt;p&gt;I built a custom slash command that lets Claude Code triage GitHub issues — deciding which to close, which need a PR, and which need human judgment. The hardest part was turning my own implicit decision-making into explicit prompt rules. After 7 iterations, it handles the obvious cases well, freeing me to focus on the truly hard issues.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Built mysh — a MySQL connection manager that auto-masks query output (emails, phones, etc.) befor...</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Tue, 17 Mar 2026 14:49:08 +0000</pubDate>
      <link>https://dev.to/atani/built-mysh-a-mysql-connection-manager-that-auto-masks-query-output-emails-phones-etc-befor-2jid</link>
      <guid>https://dev.to/atani/built-mysh-a-mysql-connection-manager-that-auto-masks-query-output-emails-phones-etc-befor-2jid</guid>
      <description>&lt;p&gt;Built mysh — a MySQL connection manager that auto-masks query output (emails, phones, etc.) before passing results to AI tools. Production env is always masked; dev env shows raw data. TTY detection prevents AI tools from bypassing the mask.&lt;br&gt;
&lt;a href="https://github.com/atani/mysh" rel="noopener noreferrer"&gt;https://github.com/atani/mysh&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Built a "failure learning loop" for Claude Code — PR review comments get recorded in memory/rules...</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Fri, 13 Mar 2026 14:36:33 +0000</pubDate>
      <link>https://dev.to/atani/built-a-failure-learning-loop-for-claude-code-pr-review-comments-get-recorded-in-memoryrules-372o</link>
      <guid>https://dev.to/atani/built-a-failure-learning-loop-for-claude-code-pr-review-comments-get-recorded-in-memoryrules-372o</guid>
      <description>&lt;p&gt;Built a "failure learning loop" for Claude Code — PR review comments get recorded in memory/rules files that persist across sessions. EUC-JP file corruption, test anti-patterns — each mistake only happens once now.&lt;br&gt;
&lt;a href="https://docs.anthropic.com/en/docs/claude-code" rel="noopener noreferrer"&gt;https://docs.anthropic.com/en/docs/claude-code&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Automated our team's weekly report with 2 bash scripts. GraphQL API fetches PRs across the org, j...</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Thu, 12 Mar 2026 14:41:55 +0000</pubDate>
      <link>https://dev.to/atani/automated-our-teams-weekly-report-with-2-bash-scripts-graphql-api-fetches-prs-across-the-org-j-9h8</link>
      <guid>https://dev.to/atani/automated-our-teams-weekly-report-with-2-bash-scripts-graphql-api-fetches-prs-across-the-org-j-9h8</guid>
      <description>&lt;p&gt;Automated our team's weekly report with 2 bash scripts. GraphQL API fetches PRs across the org, jq handles stats, and claude -p groups them by theme into markdown. The "judgment" part is the only thing the LLM does — data collection stays deterministic.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Been using cmux daily for running AI agents — loved it so much I sent 3 PRs. Localized Japanese U...</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Tue, 10 Mar 2026 16:58:47 +0000</pubDate>
      <link>https://dev.to/atani/been-using-cmux-daily-for-running-ai-agents-loved-it-so-much-i-sent-3-prs-localized-japanese-u-2f6n</link>
      <guid>https://dev.to/atani/been-using-cmux-daily-for-running-ai-agents-loved-it-so-much-i-sent-3-prs-localized-japanese-u-2f6n</guid>
      <description>&lt;p&gt;Been using cmux daily for running AI agents — loved it so much I sent 3 PRs. Localized Japanese UI, fixed an omnibar hang, then tracked down a CJK font bug where decorative fonts got picked over Hiragino Sans. Using it is how you find things to fix, caring is why you fix them.&lt;br&gt;
&lt;a href="https://github.com/manaflow-ai/cmux/pull/1017" rel="noopener noreferrer"&gt;https://github.com/manaflow-ai/cmux/pull/1017&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Been using cmux daily for running AI agents — loved it so much I sent 3 PRs. Localized Japanese U...</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Tue, 10 Mar 2026 14:41:10 +0000</pubDate>
      <link>https://dev.to/atani/been-using-cmux-daily-for-running-ai-agents-loved-it-so-much-i-sent-3-prs-localized-japanese-u-43ha</link>
      <guid>https://dev.to/atani/been-using-cmux-daily-for-running-ai-agents-loved-it-so-much-i-sent-3-prs-localized-japanese-u-43ha</guid>
      <description>&lt;p&gt;Been using cmux daily for running AI agents — loved it so much I sent 3 PRs. Localized Japanese UI, fixed an omnibar hang, then tracked down a CJK font bug where decorative fonts got picked over Hiragino Sans. Using it is how you find things to fix, caring is why you fix them.&lt;br&gt;
&lt;a href="https://github.com/manaflow-ai/cmux/pull/1017" rel="noopener noreferrer"&gt;https://github.com/manaflow-ai/cmux/pull/1017&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>I added Japanese localization to cmux, a Ghostty-based macOS terminal for AI coding agents. 624 t...</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Fri, 06 Mar 2026 14:30:06 +0000</pubDate>
      <link>https://dev.to/atani/i-added-japanese-localization-to-cmux-a-ghostty-based-macos-terminal-for-ai-coding-agents-624-t-246d</link>
      <guid>https://dev.to/atani/i-added-japanese-localization-to-cmux-a-ghostty-based-macos-terminal-for-ai-coding-agents-624-t-246d</guid>
      <description>&lt;p&gt;I added Japanese localization to cmux, a Ghostty-based macOS terminal for AI coding agents. 624 translated entries across 18 Swift files, done almost entirely through conversation with Claude Code.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>I archived my CLI tool gh-attach after realizing playwright-cli can do the same thing — upload im...</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Mon, 23 Feb 2026 14:40:39 +0000</pubDate>
      <link>https://dev.to/atani/i-archived-my-cli-tool-gh-attach-after-realizing-playwright-cli-can-do-the-same-thing-upload-im-5cob</link>
      <guid>https://dev.to/atani/i-archived-my-cli-tool-gh-attach-after-realizing-playwright-cli-can-do-the-same-thing-upload-im-5cob</guid>
      <description>&lt;p&gt;I archived my CLI tool gh-attach after realizing playwright-cli can do the same thing — upload images to GitHub Issues via the file chooser UI.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>I tried getting Claude Code to collect E2E test evidence via browser automation. It took 5 attemp...</title>
      <dc:creator>atani</dc:creator>
      <pubDate>Thu, 19 Feb 2026 14:41:29 +0000</pubDate>
      <link>https://dev.to/atani/i-tried-getting-claude-code-to-collect-e2e-test-evidence-via-browser-automation-it-took-5-attemp-4boc</link>
      <guid>https://dev.to/atani/i-tried-getting-claude-code-to-collect-e2e-test-evidence-via-browser-automation-it-took-5-attemp-4boc</guid>
      <description>&lt;p&gt;I tried getting Claude Code to collect E2E test evidence via browser automation. It took 5 attempts to get it right.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
