moinsen_runapp 0.7.2
moinsen_runapp: ^0.7.2 copied to clipboard
Universal LLM debug bridge for Flutter. Three-layer error catching, deduplication, screenshot capture, navigation tracking, and a CLI tool for live LLM-assisted debugging.
0.7.2 #
Fixed #
ext.moinsen.navigateno longer throws on Router-2.0 apps.MoinsenNavigatorObserver.pushNamedpreviously calledNavigator.pushNamedunconditionally — apps using GoRouter / Beamer / auto_route don't registeronGenerateRoute, so the call threwNavigator.onGenerateRoute was null, but the route named "/..." was referenced.Errors from the underlyingNavigator.pushNamedare now caught and surfaced asnavigated: falseto the VM Service caller.
Added #
-
MoinsenNavigatorObserver.registerNavigator(handler)— inject a custom push handler soext.moinsen.navigateworks with Router-2.0. For GoRouter:MoinsenNavigatorObserver.instance.registerNavigator( (route, {arguments}) async { final ctx = rootNavigatorKey.currentContext; if (ctx == null) return false; GoRouter.of(ctx).go(route); return true; }, );When set,
pushNameddelegates to the handler instead of callingNavigator.pushNamed. The default behavior is unchanged for Navigator-1.0 apps.
0.7.1 #
Added #
pregrantAndroid support. The command now auto-detects platform from the device id format (36-char hex-with-dashes → iOS UDID, otherwise Android adb serial) and runsadb -s <serial> shell pm grant <package> <perm>per service. Service names (camera,location,microphone, ...) are mapped to the right Android permission constant internally; services without an Android counterpart are skipped with a clear note in the result.- Auto-detect of Android
applicationIdfromandroid/app/build.gradle.ktsorandroid/app/build.gradlewhen--bundle-idis omitted (mirrors the iOS bundle-id auto-detection fromios/Runner.xcodeproj).
0.7.0 — Foundation: robust agentic Flutter testing #
Round of CLI hardening driven by real-world Claude Code session friction (curated_closet, May 2026). The big themes: race-conditions on tap after navigation, no inline screenshot for LLM prompts, stale state file after external kill, system permission dialogs blocking automation.
Added #
screenshot --base64— Return PNG bytes inline as JSON ({base64, width, height, bytes}) instead of writing to disk. Drops thecp screenshot.png ... | sips ...dance from agent scripts.await-route <path> [--timeout 5s] [--startsWith]— Poll the running app's current route until it matches, then exit. Unblocks scripts that tap a navigation button and need to wait for the destination route to settle.await-element [--key|--text] [--timeout 5s]— Poll the interactive element list until a target widget is present and visible. Companion toawait-routefor when the route already matches but the widget tree is mid-rebuild.pregrant— Pre-approve iOS Simulator privacy permissions viaxcrun simctl privacy. Camera/Location/Photos/Microphone dialogs never appear, removing the #1 native-dialog blocker for automated onboarding tests. Auto-detects UDID from.moinsen_run.jsonand bundle-id fromios/Runner.xcodeproj.start --device <id>/-d— Direct flag, no moremoinsen_run start -- -d <id>passthrough dance. The---passthrough still works for ad-hoc args.tap/enter-text/scroll-toretry-with-backoff — 3 attempts, exponential backoff (200ms → 400ms → 800ms, total ≤1.5s) onKeyMatcher: not found-class errors. Solves the silent-fail-after-restart class. Opt out per call with--no-retry.
Changed #
- Stale
.moinsen_run.jsonis now detected viakill -0 <pid>before each VM-Service call. The state file is auto-cleaned and the user gets a clear "App not running" error instead of a generic "Failed to connect to VM Service".
0.6.3 #
- Document global installation via
dart pub global activate moinsen_runappformoinsen_runandmoinsen_mcpexecutables. - Simplify MCP server configuration with global install (just
"command": "moinsen_mcp").
0.6.2 #
- Add
install-skillCLI command for installing the AI agent skill directly into.claude/skills/or.cursor/skills/(project or global level).
0.6.1 #
- Fix skill directory naming to match
skillspackage convention (moinsen_runapp-skillinstead ofmoinsen-runapp).
0.6.0 — Remote Control & MCP Server #
moinsen_runapp evolves from observation-only to full remote control. AI agents can now see what's on screen, tap buttons, enter text, scroll, and verify results — all through VM Service extensions, CLI commands, or the new MCP server.
Added #
- UI Interaction (opt-in via
enableInteraction: true) — Remote-control the app through 4 new VM extensions and CLI commands:ext.moinsen.getInteractiveElements/moinsen_run elements— Discover all interactive widgets on screen with type, key, text, bounds, and visibilityext.moinsen.tap/moinsen_run tap— Tap elements by key, text, widget type, or screen coordinatesext.moinsen.enterText/moinsen_run enter-text— Type text into fields via controller manipulationext.moinsen.scrollTo/moinsen_run scroll-to— Scroll until a target element becomes visible (max 50 attempts)
- Widget Matching — 4-strategy sealed class:
CoordinatesMatcher(fastest, skips tree),KeyMatcher,TextMatcher,TypeStringMatcher. Precedence: coordinates > key > text > type. - InteractionConfig — Extensible widget support via callbacks:
isInteractiveWidget,shouldStopTraversal,extractText. Built-in support for all standard Flutter widgets (buttons, text fields, switches, etc.). - MCP Server (
moinsen_mcpexecutable) — Model Context Protocol server exposing all 17 extensions as MCP tools for AI agents (Claude Code, Cursor, etc.). Includes 2 composite tools:observe— Full context + screenshot + interactive elements in one callinteract_and_verify— Execute action, wait 300ms, take verification screenshot
- Enhanced Context Report —
getContextandgenerateContextnow include an## Interactive Elementssection and expanded## Available Actionswhen interaction is enabled.
RunAppConfig (2 new fields) #
| Field | Default | Description |
|---|---|---|
enableInteraction |
false |
Enable UI interaction VM extensions |
interactionConfig |
InteractionConfig() |
Callbacks for custom widget support |
CLI commands (4 new, 24 total) #
| Command | Description |
|---|---|
elements |
Get interactive elements on screen |
tap |
Tap element (--key, --text, --type, --x/--y) |
enter-text |
Enter text (--key/--text/--type + --input) |
scroll-to |
Scroll until visible (--key, --text) |
VM Service extensions (4 new, 17 total) #
| Extension | Description |
|---|---|
ext.moinsen.getInteractiveElements |
Interactive widget discovery |
ext.moinsen.tap |
Tap by key/text/type/coordinates |
ext.moinsen.enterText |
Text input via controller |
ext.moinsen.scrollTo |
Scroll until visible |
MCP Server (21 tools) #
New moinsen_mcp executable: dart run moinsen_runapp:moinsen_mcp
| Category | Tools |
|---|---|
| Connection | connect, disconnect |
| Observation | get_errors, clear_errors, get_logs, get_route, get_device_info, get_lifecycle, get_network, get_state, take_screenshot, get_prompt |
| Interaction | get_interactive_elements, tap, enter_text, scroll_to |
| Control | navigate, hot_reload, hot_restart |
| Composite | observe, interact_and_verify |
Architecture #
- No custom binding — interaction uses
GestureBinding.instance, compatible with any Flutter binding - Hit-test validation ensures only actually interactable elements are reported
- Element tree traversal with configurable stop rules and text extraction
- Gesture dispatch via low-level
PointerEventrecords (tap + drag)
0.5.0 — Agentic Ready #
moinsen_runapp now provides the full environmental context an LLM needs to diagnose and fix Flutter app issues — not just what went wrong, but why.
Added #
- Device & Environment Info — Screen dimensions, pixel ratio, locale, brightness, text scale factor, OS version, Dart version, and accessibility features. Exposed via
ext.moinsen.getDeviceInfoVM extension andmoinsen_run deviceCLI command. Now an LLM knows whether aRenderFlex overflowis a small-phone problem or a tablet layout bug. - App Lifecycle Tracking —
MoinsenLifecycleObserverautomatically tracksAppLifecycleStatetransitions (resumed, inactive, paused, detached, hidden) with timestamps. Exposed viaext.moinsen.getLifecycleandmoinsen_run lifecycle. Helps diagnose "WebSocket disconnected" bugs caused by background transitions. - HTTP/Network Traffic Monitor — Zero-config HTTP interception via
HttpOverrides. Records method, URL, status code, duration, and sanitized headers (Authorization/Cookie automatically redacted). Ring buffer of last 100 requests. Exposed viaext.moinsen.getNetworkandmoinsen_run network [--errors] [--last N]. Opt-out viaRunAppConfig.monitorHttp. - State Inspection Registry — Opt-in API for exposing app state to LLM tools.
moinsenExposeState('cart', () => cartBloc.state.toJson())registers lazy snapshot functions called only when queried. Exposed viaext.moinsen.getStateandmoinsen_run inspect [key]. - Enriched Context Report —
ext.moinsen.getContextandmoinsen_run contextnow include device info, lifecycle state, network traffic, and app state alongside errors, logs, and navigation. New## Available Actionssection tells the LLM what it can do.
CLI commands (4 new, 20 total) #
| Command | Description |
|---|---|
device |
Get device and environment information |
lifecycle |
Get app lifecycle state and transition history |
network |
Get HTTP/network traffic (--errors, --last N) |
inspect |
Inspect registered app state (inspect [key]) |
VM Service extensions (4 new, 13 total) #
| Extension | Description |
|---|---|
ext.moinsen.getDeviceInfo |
Device context (screen, locale, a11y) |
ext.moinsen.getLifecycle |
Lifecycle state and transition history |
ext.moinsen.getNetwork |
HTTP traffic ring buffer |
ext.moinsen.getState |
Registered app state snapshots |
New public API #
moinsenExposeState(key, snapshotFn)— Register state for LLM accessmoinsenHideState(key)— Remove state registrationRunAppConfig.monitorHttp— Enable/disable HTTP monitoring (default: true)RunAppConfig.httpBufferCapacity— HTTP ring buffer size (default: 100)
0.4.0 — LLM Debug Bridge #
moinsen_runapp is now the universal LLM debug bridge for Flutter apps.
Added #
moinsenLog()— App-level logging API. Surface navigation events, API calls, and state changes to external tooling. Messages appear inext.moinsen.getLogsand themoinsen_run logsCLI command.MoinsenNavigatorObserver— Drop-inNavigatorObserverthat tracks route changes. Exposes current route, navigation history, and programmatic navigation viapushNamed()/pop().- Screenshot capture — New
ext.moinsen.screenshotVM Service extension andmoinsen_run screenshotCLI command. Captures the current screen as PNG using the render view's layer tree directly (noRepaintBoundarywrapper needed). - Route information — New
ext.moinsen.getRouteVM Service extension andmoinsen_run routeCLI command. Returns current route, observer status, and navigation history. - Navigation control — New
ext.moinsen.navigateVM Service extension andmoinsen_run navigateCLI command. Push named routes or pop programmatically (debug mode only). - Context command — New
moinsen_run contextCLI command — the "tell me everything" endpoint. Aggregates errors, logs, route info, optional screenshot, and widget tree into a single LLM-ready markdown document. Supports--with-screenshot,--with-tree,--log-count, and--format(markdown/json). - Enhanced prompt —
ext.moinsen.getPromptnow includes recent logs and navigation history alongside errors for richer LLM-assisted debugging. RunAppConfig.logBufferCapacity— Configure log buffer size (default: 200).RunAppConfig.screenshotMaxDimension— Cap screenshot resolution for memory safety.
CLI commands (6 new, 16 total) #
| Command | Description |
|---|---|
screenshot |
Capture a screenshot from the running app |
route |
Get the current route and navigation history |
navigate |
Push a route or pop the current route |
context |
Get a comprehensive LLM-ready context report |
| (existing) | start, stop, status, errors, logs, prompt, reload, restart, state, analyze |
VM Service extensions (4 new, 9 total) #
| Extension | Description |
|---|---|
ext.moinsen.screenshot |
Capture screen as base64 PNG |
ext.moinsen.getRoute |
Current route and navigation history |
ext.moinsen.navigate |
Push/pop routes programmatically |
ext.moinsen.getContext |
Aggregated context (errors + logs + route) |
| (existing) | getErrors, clearErrors, getInfo, getLogs, getPrompt |
0.3.0 #
- Add
moinsen_runCLI tool for live LLM-assisted debugging via VM Service - Register 5 VM Service extensions in debug mode:
ext.moinsen.getErrors,clearErrors,getInfo,getLogs,getPrompt - Add
LogBufferring buffer for structured log capture (capacity 200) - Extract
generateBugReport()from debug screen into reusable prompt generator - Add
ErrorEntry.toJson()for structured error serialization - CLI commands:
start,stop,status,errors,logs,prompt,reload,restart,state,analyze - All CLI output is structured JSON for machine consumption
0.2.0 #
- Add
moinsenReportError()top-level function for manually reporting caught errors through the full error pipeline (dedup, console log, file log, UI notification, external callback) - Add
ErrorCatcher.reportError()public method as the underlying API - Add
setupTestErrorCatcher()andresetGlobalErrorCatcher()test helpers - Errors from state management (Riverpod, Bloc, etc.), API calls, and background tasks can now be unified with the automatic three-layer error catching
0.1.0 #
- Three-layer error catching: Flutter framework, platform dispatcher, and zone guard
- Error deduplication with configurable time window
- Beautiful release error screens: friendly, minimal, and illustrated variants
- Custom error screen builders for release and debug modes
- Error observer (
ChangeNotifier) for reactive UI updates - Optional file logging with auto-resolved or explicit paths
onErrorcallback for external reporting (Sentry, Crashlytics, etc.)- App always starts regardless of init errors
ErrorBoundaryWidgetwraps the app tree for inline error display