Tyler Worley
All posts

Automating Navattic Captures

  • November 2026
  • 13 min read

Capturing a demo in Navattic by hand is slow. You click through the flow, wait for each page to load, check each capture, and redo the ones that came out wrong. When the product changes, you do it all again.

I built a Claude Code plugin that does the clicking. I give Claude the steps or a screen recording of the flow, and it comes back with all of the captures ready to be put into a flow. A 12-step flow takes about 71 seconds from start to finish and costs under two cents.

0:00 / 0:00

The plugin is built on jev-browser, an open source repo on GitHub. It adds an Agent Skill file for instructions, along with a number of scripts to help the process along.

This was tested on Built LMS, the Learning Management System I've created with AI for the past year and a half to help learn AI tools, along with the hopes and dreams of getting bought out for a billion dollars. That's not happening, sadly.

The first attempt

When Navattic first released their MCP server, it made me think of trying to let AI take the web captures itself so I could go do other work while it toils away (mostly taking naps).

I tried a few different methods, and they were all very very slow. I want to say I was using Sonnet 4.5 at the time. In Claude Code, I had it create captures for a workflow that had maybe 10 or so steps. It took at least 5 minutes. I don't remember the exact benchmarks, but it was painful. The models are much smarter and faster now, and we have decision models like Jev.

How I set up Navattic for this

I use Shift+click instead of click to capture. With click to capture on, every click takes a capture, so one misclick adds a step you don't want. Shift+click greatly reduces the chance of that, because a capture only happens when Claude means to take one.

It is currently set up to add the captures to an existing demo, because I did all of this on my free account. It can easily be configured to create a new demo and give it a name, however.

Typing steps get a Shift+click on the empty field first, so the demo shows that step before the text appears.

How Claude controls the browser

Claude already has a Chrome extension that can click and type in your browser, so a separate browser looks unnecessary. The problem is that Navattic is also an extension, and Chrome keeps extensions apart.

That means Claude's extension can't press Navattic's button or use its capture bar, because Chrome won't let one extension control another. The plugin isn't an extension, so that rule doesn't apply. It uses Playwright to drive the browser from outside, through Chrome's debugging port, and it can reach everything Navattic adds.

Playwright is Microsoft's framework for testing web apps. Claude uses it to control a real browser: open pages, click, type and read what's on screen.

Decision models

What really makes this cook are decision models. Jev burst onto the scene on September 15, 2026 and created a whole new category of AI models. A decision model picks from a set of options instead of writing text. You give it context and a question, and it returns a probability for each possible answer.

For example, you could use Jev to moderate comments before they post. Give it a comment and ask "Is this spam?" and "Allow, hide or escalate?" It returns a probability for each answer in one call, and if the value is above a certain score, it's marked as spam.

That's what fixes the slowness from my first attempt. Claude no longer has to read the whole page and pick an element at every step. It describes the element, and the decision model picks it.

Using the skill

Building a good prompt is crucial. Here's an example I used in the benchmarks:

text
Use the navattic-capture skill to capture a demo of https://app.builtlms.com/admin-dashboard/Add it to the existing "Built LMS" demo.
Steps:1. In the left side navigation, under the Learning section, click "Courses".2. Click the "Create Course" button in the top corner.3. Enter the Course Title of "It's a Brand New Jev".4. Click "Create Course".5. In the Course Information section, under Short Description, type "This is a short description."6. Under Full Description, enter "This is a full description for this amazing and wonderful course for testing Jev and Navattic captures together. It is a wonderful wonderful thing."7. Under Difficulty Level, open the dropdown and choose "Expert".8. Set Target Audience to "Everyone who loves Jev."9. Set Prerequisites to "Being able to spell Jev"10. Set Duration (minutes) to 30.11. Under Learning objectives, type "Demonstrate through the power of Jev how to automate Navattic captures for efficient demo creation." into the text box, then click "Add".12. Click the "Save changes" button. A confirmation toast should be in the last capture.

Giving clear instructions makes it easier for Jev to follow, and giving details about the element's location helps too.

0:00 / 0:00

From a screen recording

You can skip writing steps. Record the flow with any screen recorder and give Claude the file and the app's URL.

video2prompt.mjs sends the whole video to Gemini and gets back a prompt in the same format as the one above. If you don't want to use Gemini, Claude will use ffmpeg to extract a frame per second and create the prompt. But if things happen in between those frames, it might miss something. Gemini has native Video Understanding built in, so its the better choice if possible.

Getting to the first capture

The plugin has three layers: one shared browser, jev-browser to drive it, and the plugin's own scripts and skill on top.

Claude reads the skill, runs the scripts from Bash, and reads one line of output per command. It only looks at the page when a step goes wrong.

setup.sh: one-time install

setup.sh puts everything the plugin needs in ~/navattic-capture and skips anything already there:

  • jev-browser, built from GitHub, plus the Chromium build its Playwright version expects
  • the Navattic extension from the Chrome Web Store, pinned to its store ID
  • opts.json, which tells jev-browser to attach to the shared browser instead of launching its own
  • .env, copied from .env.example, for the decision-model key and an optional Gemini key

Ask Claude to set up navattic-capture, and it runs this.

browser.sh: the shared browser

browser.sh launches a developer version of Chrome with the Navattic extension loaded, a persistent profile, and Chrome's remote debugging port open. Every other tool attaches to that port, so they all share the same tabs and the same Navattic session.

The browser runs in headless mode by default. If Navattic needs a login, start.mjs stops and Claude reopens the browser in a window. You log in to Navattic and your app once in that window, and the profile keeps the logins.

start.mjs: getting to the first capture

The "Choose a demo" dialog that start.mjs answers

Starting a capture session by hand takes several clicks: the extension icon, the "Choose a demo" dialog, the demo picker, and "Start capturing". start.mjs does all of it in one command and reports back with one line of JSON.

The details:

  • No browser automation tool can click an extension's toolbar icon, because it isn't part of the page. start.mjs uses Chrome's Extensions.triggerAction command to enable the Navattic extension for that tab.
  • The "Choose a demo" dialog lives in an iframe the extension injects. Plain Playwright locators don't reach it, so start.mjs answers it through jev-browser's snapshot and click.

How Jev finds an element in the plugin

The plugin uses Jev from TypeSafe by default. A call takes a second or two.

jev-browser reads the page's accessibility tree, which lists each control with its role and visible name (button "Save Changes", textbox "Course Title"). It sends that list and Claude's description to Jev, and Jev scores every element. If the top score is 0.7 or higher, jev.mjs clicks it. If nothing clears the bar, the command fails, and Claude runs find, reads the list, and clicks the ref itself.

The description doesn't have to match the label exactly, but the closer it is to what's actually on the page, the better results.

Running the steps

jev.mjs: one short command per action

jev.mjs wraps the jev-browser CLI. Each command does one action, checks it, and prints one line. A failed step prints FAIL and stops the chain, so Claude can send many steps in one Bash call.

CommandWhat it does
tab <url part>Selects the newest matching tab that shows the capture bar, and reads the counter
shift "<description>"Asks the decision model which element matches, then Shift+clicks it
type "<description>" "<text>"Shift+clicks the field, waits for the capture counter to go up, then types
settleWaits until the page has changed and then stopped changing
capturePresses Control+c for a capture with no click
finishPresses Finish, finds the "Select your captures" dialog, and closes it or builds the demo
find "<name>"The fallback: lists elements whose names contain a term, with refs Claude can click

One run sent its first four steps as one Bash call:

bash
node jev.mjs tab admin-dashboard \  && node jev.mjs shift "Courses link under the Learning section in the left side navigation" \  && node jev.mjs settle \  && node jev.mjs shift "Create Course button in the top corner of the page" \  && node jev.mjs settle \  && node jev.mjs type "Course Title text field in the Create Course form" "It's a Brand New Jev" \  && node jev.mjs settle \  && node jev.mjs shift "Create Course button" \  && node jev.mjs settle

And got back:

bash
shift+clicked link "Courses" (confidence 1.00)settled in 0.9sshift+clicked link "Create course" (confidence 0.86)settled in 0.9sshift+clicked and typed into textbox "Course Title" (confidence 1.00)settled in 0.3sshift+clicked button "Create Course" (confidence 0.99)settled in 1.1s

Why settle? Early runs moved faster than Navattic could capture. The plugin would click the next element while Navattic was still recording the last page, and the captures came out wrong. settle waits for the page to change after a click, then waits for it to stop changing, before the next step runs. That usually takes under a second and a half.

The skill: how Claude runs a capture

The skill holds the instructions Claude follows. It covers the capture choices above, the expected count (click and typing steps plus one), and the order of a run:

The dialog at the end of a run, with the count the skill checks
  1. Check that the browser is up, and start it if not.
  2. Run start.mjs with the app URL and the demo the prompt names.
  3. Chain the steps into as few Bash calls as possible, with settle after any step that opens a page or a menu.
  4. After a save, wait 1 so the confirmation toast is in the last capture, then capture.
  5. Run finish, then compare the counter and the dialog's "Generating from N captures" with the expected count.

The chaining rule does the most work. Every Claude turn costs a few seconds, and a chain of eight steps is one turn.

The benchmark

The baseline: Claude with Playwright MCP

I wanted to know how much of the speed came from the plugin and how much from Claude. So I gave Claude Haiku the same prompt with Microsoft's Playwright MCP server and no navattic-capture plugin.

How I ran it

Each setup ran the prompt five times, with Claude at low effort.

"Capturing" runs from the first capture to the end of the run. "Total" also includes starting Claude, checking the browser and starting the capture session. Claude's cost comes from Claude Code's own report.

Results

Setup (median of five runs)CapturingTotalClaude costJev cost
Claude Haiku, Playwright MCP, no plugin1:382:13$0.053
Claude Haiku, plugin, no decision model2:062:27$0.027
Claude Haiku, plugin + Jev0:541:14$0.010$0.0065
Claude Sonnet, plugin, no decision model1:321:54$0.257
Claude Sonnet, plugin + Jev0:481:07$0.179$0.0065

Haiku with Jev beat Sonnet without a decision model by 38 seconds of capturing, at about a fifteenth of the cost with Jev included.

Why the gap

The plugin without a decision model was slower than Playwright MCP. That bothered me, so I split each run into time spent in tools and time Claude spent thinking between them:

Setup (median per run)Tool callsTime in toolsClaude thinkingfind callsTool output Claude read
Haiku, Playwright MCP5752 s83 s0894,000 characters
Haiku, plugin, no decision model3076 s63 s2411,000 characters
Haiku, plugin + Jev752 s19 s03,000 characters
Sonnet, plugin, no decision model1860 s49 s128,000 characters
Sonnet, plugin + Jev653 s12 s02,000 characters

Without a decision model, the plugin can't locate anything from a description, so each step becomes three commands: a shift that fails, a find, and a click on the ref find returned. Claude has to read each find result before the next step, so it can't chain. Each find also starts a fresh Node process, which starts the jev-browser CLI, which reconnects to the browser and snapshots the whole page. That costs 2 to 4 seconds, about 24 times a run.

Playwright MCP keeps one connection open and hands Claude the next page with every click, so its tools are quick. The cost lands on Claude instead: it reads close to a million characters of snapshots per run and spends more time thinking than waiting on tools.

Decision models compared

Jev isn't the only decision model the plugin can use. Each of these ran the same prompt twice with Haiku, on an earlier version of the test form:

Decision modelCapturing, run 1Capturing, run 2Cost per run
liquid/d1-ultra0:470:52$0.0055
baseten/elephant-10:560:49$0.0053
liquid/d1.11:010:50$0.0027
microsoft/microsoft-decision-11:021:00$0.0048
openai/gpt-6-luna-decisions1:001:08$0.0112
perplexity/pplx-decider-v1.20:541:16$0.0027
perplexity/pplx-decider-flash-v1.21:111:33$0.0014

These were all run from OpenRouter, which the plugin supports by configuring the .env file.

Try it

You need Claude Code, Node.js 22.15 or later, and a Decision model API key.

  1. In Claude Code, add the plugin:

    text
    /plugin marketplace add percusser/navattic-capture/plugin install navattic-capture@navattic-capture
  2. Ask Claude to set up navattic-capture. It installs the browser and the Navattic extension.

  3. Configure your decision model provider, model choice, and API key in ~/navattic-capture/.env.

  4. In the browser window that opens, log in to Navattic and your app.

  5. Give Claude your steps or your recording.

Every run's numbers are in the README.