I spend a lot of my time working with AI coding agents. The thing that kept eating at my productivity though was that every time I asked an agent to fix something on the frontend, the actual edit took maybe 200 tokens. But before that happened, the agent would go on this whole journey, grepping through the codebase and at times having to backtrack. That search loop could burn 5,000-10,000 tokens before a single line of code changed.
The agent has zero awareness of what's actually rendering in the browser because the rendered DOM and the source tree are two completely different topologies. So I started thinking about this differently; what if we could optimize a solution to the search problem by giving the agents the right context? What if every DOM element already knew exactly where it came from in source?
That's the core idea behind Domscribe. At build time, an AST transform walks your JSX and Vue templates, assigns every element a deterministic ID via xxhash64 content hashing, and writes a manifest mapping each ID to its exact file, line, and column. The IDs are stable across HMR; same file content and element position always produces the same ID. At runtime, framework adapters walk the React fiber tree or inspect Vue VNodes to capture live props, state, and component metadata. A local relay daemon connects the browser and your agent via MCP.
The result is two workflows that didn't exist before. Your agent can query any source location and get back only what's actually rendering there; live DOM snapshot, props, state, the works. And from the other direction, you can click any element in the browser overlay, type "make this button use the primary color" in plain English, and Domscribe resolves the exact file:line:col so your agent edits the right file on the first try.
(I should mention that the overlay runs as Lit web components inside shadow DOM, specifically so it can't mess with your app's styles.)
One thing I'm genuinely proud of is the architecture. I spent a LOT of time making sure this wasn't just a tool that works for React-and-Vite-and-nothing-else. The transform layer has a ParserInterface that the injector consumes without knowing which parser produced the AST (the Acorn, Babel, and Vue SFC parsers are all implementations of the same interface), and the ID injection logic is completely abstracted away. Adding a new template syntax (Lit, Svelte, Stencil) means implementing one interface. On the runtime side, a FrameworkAdapter interface defines methods such as captureProps, captureState, and getComponentInstance; and the React and Vue adapters are built on the same public interface that any community contributor would use. No internal escape hatches.
I took the same approach with bundler support (Vite, Webpack, Turbopack) and agent support (Claude Code, Cursor, GitHub Copilot CLI, Gemini CLI - all via first-party plugins that bundle the MCP config and a skill file). But the MCP layer means any compatible agent works out of the box.
PII redaction is built in and on by default. All captured data passes through pattern matching for emails, phone numbers, API keys, JWTs etc. before it leaves the browser.
Everything is stripped in production builds; zero data-ds attributes and zero overlay scripts. It's enforced by CI tests that run against every real fixture. (Speaking of tests: the entire integration and e2e suite runs against real published packages via a local Verdaccio registry. )
Anyway, MIT licensed, fully open source, the whole codebase is right there on GitHub. I'd genuinely love feedback on the architecture and the approach. And I'm especially curious if anyone has thoughts on extending the ParserInterface to other template syntaxes; that's the piece I'm most excited about people contributing to.