README.md

August 13, 2026 · View on GitHub

reactive-axi

CI Release npm Platform License

Your live React, Vue, or Svelte app, reviewable by anyone, in one click.

Reactive Editor demo: queuing two annotations on a live React app, sending them as a batch to the agent, watching both fixes hot-reload live with the agent's reply landing in chat, then a look at every supported tech stack

Screenshots and long "here's what I mean" descriptions are a lossy way to give an agent feedback on a UI. The thing a live app is best at - being live - gets thrown away the moment you have to describe it in words.

Reactive Editor opens your project's own dev server (Vite, TanStack Start, Next.js, Create React App, plain Vite+Vue, or plain Vite+Svelte - auto-detected, nothing to configure) behind a local reverse proxy, lets you click any element in the running app, and resolves that click to the exact source location before it ever reaches your agent - down to the file and line where the framework's own dev tooling makes that possible. No screenshots, no "the button in the header, you know the one" - just click it and say what you want.

  • Local-first - A local CLI and a local browser tab, reverse-proxying your own dev server. No cloud dependency, no data leaving your machine.
  • Zero-config detection - Framework and its installed version are read straight from the project's own package.json/node_modules and shown right in the chrome shell - nothing to declare, nothing to get wrong.
  • Real source, not a guess - Every click resolves through the framework's own runtime metadata to a real, verified target - {file, line, component} for React (Fiber tree, React 16 through 19) and Svelte (compiler-emitted location metadata), {file, component} for Vue (no per-element line metadata by default - see below) - with an honest fallback (not a wrong answer) wherever full precision genuinely isn't available yet.

Reactive Editor is an AXI, which means -

  • It's just a CLI any capable agent can run without setup.
  • It's optimized for agent ergonomics: long polling, structured next_step guidance on every response, and idempotent commands.
  • The skill below only handles discovery; agents learn to use the AXI by using it.

Quick Start

Regardless of which install method you use below, always run this first:

npx skills add adeeshsharma/reactive-axi --skill reactive-editor

This installs the Agent Skill in the Agent Skills format with npx skills - it's not an alternative to the CLI-install options in the next section, it's a separate, required step regardless of which of those you pick. It teaches your agent the full open → poll → apply → poll loop, the polling discipline (keep it running, never kill it, re-run if interrupted), and how to interpret every resolved target shape, including the honest fallbacks (unresolved/lineUnresolved/vendorSource) for the elements that can't be pinpointed exactly or resolve into a dependency instead of your own code. Without it, you have to know these exact CLI invocations and the poll rules yourself - the "How the CLI Runs" section below only covers how the reactive-axi command gets invoked (on demand via npx, a global install, or from source), not how an agent should use it once invoked. Skipping this step means driving the raw CLI by hand with no guidance.

By default the skill lands in the current project's skills directory (.claude/skills/, for example); add -g to install it for all projects (~/.claude/skills/).

In agents that expose skills as slash commands (Claude Code, for example), invoke it directly:

/reactive-editor review the app in ./my-app

Or just ask your agent to open your app for review, and it loads the skill on its own when it recognizes the intent.

How the CLI Runs

The skill above already documents npx -y reactive-axi as its invocation by default - once it's installed, no separate CLI install step is needed. The options below are just different ways the underlying reactive-axi command itself can run; pick whichever fits, but the skill install above is still required regardless of which one you pick - none of these three teach an agent how to use the tool on their own.

Zero setup (what the skill already expects)

Any capable agent can run the CLI directly with nothing installed at all - this is exactly what the skill documents and expects by default:

Use `npx -y reactive-axi` to open my app at ./my-app for review.

Install globally

This installs the CLI only - it does not teach your agent how to use it. If you haven't already, go run the skill install command from Quick Start above; otherwise you'll be driving the raw CLI by hand with no guidance on the poll loop or how to read a resolved target.

npm install -g reactive-axi
reactive-axi <path-to-your-app>

Install from source

For working on Reactive Editor itself, or running an unreleased change. This installs the CLI only, same caveat as above - also run the skill install command from Quick Start unless you're intentionally driving the CLI by hand.

git clone https://github.com/adeeshsharma/reactive-axi.git
cd reactive-axi
pnpm install
pnpm --filter reactive-axi run check
node packages/reactive-axi/bin/reactive-axi.js <path-to-your-app>

How It Works

┌──────────────────────────┐
│ reactive-axi <project>   │
│ detects framework +      │
│ version, spawns the      │
│ project's own dev server │
└───────────┬───────────────┘

┌──────────────────────────┐
│ Reverse proxy injects an │
│ SDK into the served HTML;│
│ browser opens the chrome │
│ shell with your app in   │
│ a sandboxed iframe       │
└───────────┬───────────────┘

┌──────────────────────────┐
│ Reviewer clicks elements │
│ (Annotate mode), queues  │
│ notes, sends them        │
└───────────┬───────────────┘

┌──────────────────────────┐
│ reactive-axi poll waits  │
│ and returns each prompt  │
│ resolved to file/line    │
└──────────────────────────┘
  • Framework detection - Reads the project's package.json in priority order (TanStack Start, Next.js, Create React App, Vue, Svelte, then plain Vite) so a framework that also happens to use Vite under the hood, like TanStack Start or plain Vite+Vue/Svelte, is never misclassified. The installed framework and its version (read from each package's own node_modules/<name>/package.json, not the semver range) are shown live in the chrome shell's topbar.
  • Click-to-source resolution, per framework's own real capabilities:
    • React - An external window.__REACT_DEVTOOLS_GLOBAL_HOOK__ is installed before the app's own React bundle runs, then a clicked DOM node's fiber is read directly off its own expando property - no changes to your project required. React 16 through 18 read fiber._debugSource directly; React 19+ captures a real V8 stack trace (_debugStack) and resolves it against the dev server's own inline sourcemap. Verified against real pinned fixtures at React 16, 18, and 19 across every supported framework.
    • Svelte - The compiler attaches __svelte_meta.loc = {file, line, column} directly onto every DOM element in dev mode, genuinely zero-config. Svelte 4 reports 0-indexed lines, Svelte 5 reports 1-indexed - confirmed by a real spike, not assumed, and handled transparently.
    • Vue - A clicked element's __vueParentComponent expando (attached by Vue's runtime in dev mode) resolves to a real component instance, giving a real file and component name via @vitejs/plugin-vue's __file metadata - effectively zero-config, since that plugin is required for .vue files to work with Vite at all. No exact line/column yet: Vue templates don't carry per-element line metadata the way React's JSX or Svelte's compiler output does. The prompt's target reports lineUnresolved: true in this case rather than guessing a line.
  • Honest about what it can't resolve - A Next.js App Router Server Component's click target resolves into React's own internal RSC-deserialization runtime, not your application code - confirmed empirically, not assumed, and true regardless of React version. Reactive Editor reports this plainly ("unresolved": true on the prompt's target) instead of returning a wrong or empty answer as if it were real data. Vue targets missing a line number are reported the same honest way ("lineUnresolved": true), never a fabricated line. Create React App's own dev server generates its sourcemap with cheap-module-source-map (a real webpack setting, not something Reactive Editor controls) - line-level, not column-level, so a CRA resolution can land a line or two off the exact click, always in the same file and component.
  • Never points your agent at a dependency's own code - Clicking a UI element rendered by a component library (a design system, a headless-UI package) resolves to your usage of it, not the library's internal source - React walks the component ownership chain past the library's own files, Vue and Svelte do the same across their own component/DOM ancestry. If every candidate genuinely turns out to be inside a dependency, the target reports "vendorSource": true (plus "vendorPackage" when the package name is known) rather than silently handing your agent a file it doesn't own and shouldn't edit.
  • Annotate / Explore mode - Toggle with ⌘I/Ctrl+I or the topbar switch. Annotate mode intercepts every click for review; Explore mode passes clicks straight through so you can actually use your app while reviewing it - the mode is always visible (a colored border, a site-wide crosshair cursor, a badge on the app itself), since misjudging it in a live, stateful app has real consequences a static artifact never had.
  • Queue, don't fire-and-forget - Click an element, pick a kind (Change, Question, Comment, Bug), write a note, and it joins a visible queue - separate from what's already been sent - that you can edit or remove before sending as a batch.
  • HMR survives the proxy - Each session gets its own dynamically allocated port pair (the project's real dev server, plus a public reverse-proxy port), with the framework's HMR/Fast Refresh client explicitly reconnected through the proxy rather than the internal port it can't otherwise reach. A live source edit hot-reloads the already-open review page in place - verified with a real edit to a real file for every supported framework, not just a page reload check.
  • Session end etiquette - Ending from the browser (user-initiated) and reactive-axi end <project-dir> (agent-initiated) are tracked separately. A plain reopen after a user-initiated end refuses and explains why; pass --reopen when the user asks for further review. Ending clears the session's queued state to a genuine clean slate - reopening never silently resumes a conversation you already closed.
  • Agent presence - The browser shows whether an agent is listening, working, or hasn't attached yet, and blocks new sends only while the agent is actively working on delivered feedback; an --agent-reply concludes that state.
  • Server cleanup - The detached control server self-stops after the last session ends with nothing connected, or after REACTIVE_AXI_IDLE_TIMEOUT_MS (default 30 minutes) with no browser or poll connections.
  • Local-first state - Session state lives under ~/.reactive-axi/ by default, or REACTIVE_AXI_STATE_DIR when set.
  • Network binding - The server binds to loopback (127.0.0.1) by default. Set REACTIVE_AXI_HOST to bind elsewhere - binding beyond loopback exposes an unauthenticated server that re-proxies your dev server's full uncompiled source tree, so only do this on a trusted network.
  • Allowed hosts - A Host-header allowlist defends against DNS rebinding: the server rejects any request whose Host isn't a loopback name or the configured bind/link host. Add extra names via REACTIVE_AXI_ALLOWED_HOSTS (whitespace-separated); a lone * disables the check for operators fronting it with their own auth.
  • Open in editor - Every resolved {file, line} can be opened directly in your running editor via launch-editor, the same library Vite and Vue use for their own error overlays.

CLI Reference

CommandDescription
reactive-axi <project-dir>Open or resume a review session. Detects the framework and its version, spawns the project's own dev server, reverse-proxies it, and opens a browser.
reactive-axi poll <project-dir>Long-poll until the reviewer sends feedback or ends the session. Leave no-timeout polls running, or re-run them if interrupted - queued feedback is never lost.
reactive-axi end <project-dir>End a session as the agent; unlike a user-initiated end from the browser, this still allows a plain reopen later.
reactive-axi stopShut down the background server.
reactive-axi serverRun the local control server directly (used internally - normal use never needs this).

Flags

CommandFlagDescription
reactive-axi <project-dir>--no-openEnsure the server/session exists without opening another browser window.
reactive-axi <project-dir>--reopenReopen a session the reviewer explicitly ended from the browser; without it, a plain open refuses.
reactive-axi poll--agent-reply "..."Show the agent's reply in the existing browser chat and re-enable human sends before polling again.
reactive-axi poll--timeout-ms <ms>Test/debug escape hatch only; agents should normally omit it and leave the long poll running.
reactive-axi stop / server--port <port>Target a server running on a non-default port.
reactive-axi server--verboseLog session and dev-server events to stderr; also enabled with REACTIVE_AXI_DEBUG=1.

Environment variables

VariableDefaultPurpose
REACTIVE_AXI_PORT4388Control server port.
REACTIVE_AXI_HOST127.0.0.1Address the control server binds to.
REACTIVE_AXI_LINK_HOSTbind addressHostname written into generated session links.
REACTIVE_AXI_ALLOWED_HOSTS(none)Extra Host-header values to accept (whitespace-separated); * disables the check.
REACTIVE_AXI_STATE_DIR~/.reactive-axiWhere session state and logs are kept.
REACTIVE_AXI_IDLE_TIMEOUT_MS1800000 (30 min)Self-shutdown after this long with no connections; 0/off disables it.
REACTIVE_AXI_NO_OPEN(unset)Equivalent to --no-open.
REACTIVE_AXI_DEBUG(unset)Equivalent to --verbose on reactive-axi server.

Supported stacks

FrameworkDetected viaVersions verifiedClick-to-source precision
Vite + plain Reactvite in package.jsonReact 16, 18, 19Exact file + line + column
TanStack Start@tanstack/react-startReact 18, 19Exact file + line + column
Next.js (Pages Router)nextReact 18, 19Exact file + line + column
Next.js (App Router)nextReact 18, 19Exact file + line + column (Server Components report "unresolved": true - see above)
Create React Appreact-scriptsWhatever the installed react-scripts scaffolds (CRA itself is in maintenance mode upstream)File + component, line approximate (CRA's own dev sourcemap - cheap-module-source-map - only maps line-level granularity, not columns; see below)
Vite + plain Vuevue in package.jsonVue 3 (Vue 2 is EOL and out of scope)File + component name only (reports "lineUnresolved": true - see above)
Vite + plain Sveltesvelte in package.jsonSvelte 4, 5Exact file + line + column

Development

This is a pnpm workspace: packages/reactive-axi is the published CLI, fixtures/* are throwaway real apps used to validate against actual dev servers.

pnpm install                                # from the repo root - installs every fixture too
pnpm --filter reactive-axi run check        # build + lint + format check + typecheck + test
pnpm --filter reactive-axi run build        # bundle bin/reactive-axi.js -> dist/cli.mjs
pnpm --filter reactive-axi test             # node:test only
pnpm --filter reactive-axi run lint         # ESLint
pnpm --filter reactive-axi run format:check # Prettier check
pnpm --filter reactive-axi run typecheck    # TypeScript checkJs validation
pnpm --filter reactive-axi run build:skill  # Regenerate the installable skill
pnpm --filter reactive-axi run build:plugin # Regenerate plugin.json from package.json

CI runs pnpm --filter reactive-axi run check on every push and PR (see .github/workflows/ci.yml) - it installs the whole workspace, including every fixture, so the real integration tests that spawn each fixture's actual dev server run for real instead of skipping themselves.

License

MIT © Adeesh Sharma - see LICENSE.