@bdayadev/flutter-ultra-mcp 0.0.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +23 -0
- package/.claude-plugin/plugin.json +14 -14
- package/.mcp.json +67 -67
- package/CODE_OF_CONDUCT.md +83 -83
- package/CONTRIBUTING.md +108 -108
- package/LICENSE +201 -201
- package/README.md +77 -77
- package/SECURITY.md +19 -19
- package/dart/ultra_flutter/CHANGELOG.md +34 -34
- package/dart/ultra_flutter/EXTENSIONS.md +61 -61
- package/dart/ultra_flutter/README.md +109 -109
- package/dart/ultra_flutter/pubspec.yaml +37 -37
- package/dart/ultra_flutter_devtools/README.md +7 -7
- package/dart/ultra_flutter_devtools/pubspec.yaml +27 -27
- package/docs/UPSTREAM-PATROL-PRS.md +5 -5
- package/docs/UPSTREAM-SENTRY-PR.md +62 -62
- package/docs/discovery-empirics.md +435 -435
- package/examples/counter-app/README.md +24 -24
- package/examples/counter-app/pubspec.yaml +23 -23
- package/examples/oidc-app/README.md +48 -48
- package/examples/oidc-app/pubspec.yaml +24 -24
- package/package.json +82 -82
- package/packages/flutter-ultra-browser/README.md +29 -29
- package/packages/flutter-ultra-browser/package.json +39 -39
- package/packages/flutter-ultra-build/README.md +60 -60
- package/packages/flutter-ultra-build/package.json +38 -38
- package/packages/flutter-ultra-devtools/README.md +7 -7
- package/packages/flutter-ultra-devtools/package.json +36 -36
- package/packages/flutter-ultra-gesture/README.md +51 -51
- package/packages/flutter-ultra-gesture/package.json +58 -58
- package/packages/flutter-ultra-native-desktop/README.md +131 -131
- package/packages/flutter-ultra-native-desktop/package.json +81 -81
- package/packages/flutter-ultra-native-mobile/README.md +103 -103
- package/packages/flutter-ultra-native-mobile/package.json +72 -72
- package/packages/flutter-ultra-patrol/README.md +40 -40
- package/packages/flutter-ultra-patrol/package.json +38 -38
- package/packages/flutter-ultra-runtime/README.md +63 -63
- package/packages/flutter-ultra-runtime/package.json +69 -69
- package/shared/contracts/package.json +31 -31
- package/shared/device-router/README.md +51 -51
- package/shared/device-router/package.json +62 -62
- package/shared/keyring/README.md +7 -7
- package/shared/keyring/package.json +24 -24
- package/shared/mcp-runtime/README.md +116 -116
- package/shared/mcp-runtime/package.json +58 -58
- package/shared/state-store/README.md +66 -66
- package/shared/state-store/package.json +60 -60
- package/shared/vm-service-client/README.md +135 -135
- package/shared/vm-service-client/package.json +62 -62
- package/skills/_internal-on-tool-failure/SKILL.md +13 -13
- package/skills/_internal-session-bootstrap/SKILL.md +18 -18
- package/skills/debug/SKILL.md +20 -20
- package/skills/devtools/SKILL.md +21 -21
- package/skills/drive/SKILL.md +20 -20
- package/skills/scaffold/SKILL.md +21 -21
- package/skills/setup/SKILL.md +26 -26
- package/skills/test/SKILL.md +19 -19
- package/skills/tour/SKILL.md +22 -22
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.0.1-dev.1
|
|
4
|
-
|
|
5
|
-
Initial fork of [`marionette_flutter`](https://github.com/leancodepl/marionette_mcp)
|
|
6
|
-
v0.5.0 (Apache-2.0).
|
|
7
|
-
|
|
8
|
-
### Breaking changes vs marionette
|
|
9
|
-
|
|
10
|
-
- Binding is now a **mixin** (`mixin UltraFlutterBinding on WidgetsBinding`)
|
|
11
|
-
instead of a class. Apps that need other custom bindings (Sentry,
|
|
12
|
-
integration_test, etc.) compose via `extends WidgetsFlutterBinding with
|
|
13
|
-
...`. The convenience `UltraFlutterBinding.ensureInitialized()` covers
|
|
14
|
-
the no-composition case.
|
|
15
|
-
- Service extensions are now under `ext.flutter.ultra.*` instead of
|
|
16
|
-
`ext.flutter.marionette.*`.
|
|
17
|
-
- `MarionetteBinding` → `UltraFlutterBinding`, `MarionetteConfiguration`
|
|
18
|
-
→ `UltraConfiguration`, `MarionetteExtensionResult` →
|
|
19
|
-
`UltraExtensionResult`, `registerMarionetteExtension` →
|
|
20
|
-
`registerUltraExtension`.
|
|
21
|
-
|
|
22
|
-
### Added
|
|
23
|
-
|
|
24
|
-
- `ext.flutter.ultra.clearText` — convenience wrapper around
|
|
25
|
-
`enterText('')`, saves agents two round-trips.
|
|
26
|
-
- Sentry composition: mix in `SentryWidgetsBindingMixin` directly (no
|
|
27
|
-
companion package needed).
|
|
28
|
-
|
|
29
|
-
### Test coverage
|
|
30
|
-
|
|
31
|
-
- 6 binding/configuration tests new for the mixin form.
|
|
32
|
-
- 91 ported marionette service tests pass unchanged after the rename
|
|
33
|
-
pass.
|
|
34
|
-
- Full suite: 97/97 green on Flutter stable.
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.0.1-dev.1
|
|
4
|
+
|
|
5
|
+
Initial fork of [`marionette_flutter`](https://github.com/leancodepl/marionette_mcp)
|
|
6
|
+
v0.5.0 (Apache-2.0).
|
|
7
|
+
|
|
8
|
+
### Breaking changes vs marionette
|
|
9
|
+
|
|
10
|
+
- Binding is now a **mixin** (`mixin UltraFlutterBinding on WidgetsBinding`)
|
|
11
|
+
instead of a class. Apps that need other custom bindings (Sentry,
|
|
12
|
+
integration_test, etc.) compose via `extends WidgetsFlutterBinding with
|
|
13
|
+
...`. The convenience `UltraFlutterBinding.ensureInitialized()` covers
|
|
14
|
+
the no-composition case.
|
|
15
|
+
- Service extensions are now under `ext.flutter.ultra.*` instead of
|
|
16
|
+
`ext.flutter.marionette.*`.
|
|
17
|
+
- `MarionetteBinding` → `UltraFlutterBinding`, `MarionetteConfiguration`
|
|
18
|
+
→ `UltraConfiguration`, `MarionetteExtensionResult` →
|
|
19
|
+
`UltraExtensionResult`, `registerMarionetteExtension` →
|
|
20
|
+
`registerUltraExtension`.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `ext.flutter.ultra.clearText` — convenience wrapper around
|
|
25
|
+
`enterText('')`, saves agents two round-trips.
|
|
26
|
+
- Sentry composition: mix in `SentryWidgetsBindingMixin` directly (no
|
|
27
|
+
companion package needed).
|
|
28
|
+
|
|
29
|
+
### Test coverage
|
|
30
|
+
|
|
31
|
+
- 6 binding/configuration tests new for the mixin form.
|
|
32
|
+
- 91 ported marionette service tests pass unchanged after the rename
|
|
33
|
+
pass.
|
|
34
|
+
- Full suite: 97/97 green on Flutter stable.
|
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
# `ext.flutter.ultra.*` VM service extension catalogue
|
|
2
|
-
|
|
3
|
-
Every extension below is registered by `UltraFlutterBinding.initServiceExtensions()` when the binding is active. All extensions return a JSON envelope with `type: '_extensionType'`, `method`, and `status: 'Success'` on success; errors map to standard `developer.ServiceExtensionResponse` codes (`extensionErrorMin..extensionErrorMin+16` for typed errors, `invalidParams` for bad input).
|
|
4
|
-
|
|
5
|
-
## Parity matrix vs marionette
|
|
6
|
-
|
|
7
|
-
| `ext.flutter.ultra.*` | marionette equivalent | Notes |
|
|
8
|
-
| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------ |
|
|
9
|
-
| `getVersion` | `marionette.getVersion` | Returns `{version: <semver>}` from `src/version.g.dart`. |
|
|
10
|
-
| `interactiveElements` | `marionette.interactiveElements` | Returns `{elements: [...]}` from `ElementTreeFinder`. |
|
|
11
|
-
| `tap` | `marionette.tap` | Body: `WidgetMatcher` JSON (`key`/`text`/`type`/`x`+`y`/`focused`). |
|
|
12
|
-
| `doubleTap` | `marionette.doubleTap` | Optional `delay` (ms, positive). Default 100ms. |
|
|
13
|
-
| `longPress` | `marionette.longPress` | Optional `duration` (ms). Default 600ms. |
|
|
14
|
-
| `enterText` | `marionette.enterText` | Requires `input` plus a matcher. |
|
|
15
|
-
| `clearText` | _(new — enhancement)_ | Convenience wrapper around `enterText('')`. Saves agents two round-trips. |
|
|
16
|
-
| `swipe` | `marionette.swipe` | Either coord-based (`startX/Y` + `endX/Y`) or element + `direction` + `distance`. |
|
|
17
|
-
| `pinchZoom` | `marionette.pinchZoom` | Required `scale`, optional `startDistance` (default 200). |
|
|
18
|
-
| `scrollTo` | `marionette.scrollTo` | Scrolls until the matched widget is visible. |
|
|
19
|
-
| `getLogs` | `marionette.getLogs` | Requires a `LogCollector` in `UltraConfiguration`. |
|
|
20
|
-
| `takeScreenshots` | `marionette.takeScreenshots` | Returns one screenshot per `RenderView`. |
|
|
21
|
-
| `startScreencast` | `marionette.startScreencast` | Optional `maxWidth`, `maxHeight`, `wsPort`. |
|
|
22
|
-
| `stopScreencast` | `marionette.stopScreencast` | Idempotent. |
|
|
23
|
-
| `pressBackButton` | `marionette.pressBackButton` | Returns `didPop`. |
|
|
24
|
-
| `listExtensions` | `marionette.listExtensions` | Returns user-registered custom extensions (registered via `registerUltraExtension`). |
|
|
25
|
-
|
|
26
|
-
15 extensions in total: 14 ported + 1 enhancement (`clearText`).
|
|
27
|
-
|
|
28
|
-
## Adding a custom extension
|
|
29
|
-
|
|
30
|
-
```dart
|
|
31
|
-
import 'package:ultra_flutter/ultra_flutter.dart';
|
|
32
|
-
|
|
33
|
-
void registerMyExtensions() {
|
|
34
|
-
registerUltraExtension(
|
|
35
|
-
name: 'myApp.fetchProfile', // becomes `ext.flutter.myApp.fetchProfile`
|
|
36
|
-
description: 'Returns the currently-signed-in profile snapshot.',
|
|
37
|
-
callback: (params) async {
|
|
38
|
-
final id = params['id'];
|
|
39
|
-
if (id == null) {
|
|
40
|
-
return UltraExtensionResult.invalidParams('Missing "id"');
|
|
41
|
-
}
|
|
42
|
-
// ...
|
|
43
|
-
return UltraExtensionResult.success({'profile': {/* ... */}});
|
|
44
|
-
},
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Call `registerMyExtensions()` after `AppBinding.ensureInitialized()` and before `runApp()`. Custom extensions show up in the `listExtensions` response so the MCP server can advertise them to the LLM.
|
|
50
|
-
|
|
51
|
-
## Why `ext.flutter.ultra.*` and not `ext.ultra.*`
|
|
52
|
-
|
|
53
|
-
Flutter's DevTools and `package:vm_service` only allow registration under `ext.flutter.*` — the `flutter.` prefix is hard-coded by `developer.registerExtension` validation in our `register_extension_internal.dart`. The `ultra` second segment is our namespace.
|
|
54
|
-
|
|
55
|
-
## Planned future extensions
|
|
56
|
-
|
|
57
|
-
- `ultra.waitFor` — server-side polling that resolves when a matcher hits (saves agents N round-trips).
|
|
58
|
-
- `ultra.scrollUntilVisible` — single-call hybrid of `scrollTo` + `waitFor` for stubborn lazy lists.
|
|
59
|
-
- `ultra.matcher` — hierarchical matchers (`{type: 'TextField', ancestor: {key: 'login_form'}}`).
|
|
60
|
-
|
|
61
|
-
Tracked at plan §6.1; not in 0.0.1-dev.
|
|
1
|
+
# `ext.flutter.ultra.*` VM service extension catalogue
|
|
2
|
+
|
|
3
|
+
Every extension below is registered by `UltraFlutterBinding.initServiceExtensions()` when the binding is active. All extensions return a JSON envelope with `type: '_extensionType'`, `method`, and `status: 'Success'` on success; errors map to standard `developer.ServiceExtensionResponse` codes (`extensionErrorMin..extensionErrorMin+16` for typed errors, `invalidParams` for bad input).
|
|
4
|
+
|
|
5
|
+
## Parity matrix vs marionette
|
|
6
|
+
|
|
7
|
+
| `ext.flutter.ultra.*` | marionette equivalent | Notes |
|
|
8
|
+
| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------ |
|
|
9
|
+
| `getVersion` | `marionette.getVersion` | Returns `{version: <semver>}` from `src/version.g.dart`. |
|
|
10
|
+
| `interactiveElements` | `marionette.interactiveElements` | Returns `{elements: [...]}` from `ElementTreeFinder`. |
|
|
11
|
+
| `tap` | `marionette.tap` | Body: `WidgetMatcher` JSON (`key`/`text`/`type`/`x`+`y`/`focused`). |
|
|
12
|
+
| `doubleTap` | `marionette.doubleTap` | Optional `delay` (ms, positive). Default 100ms. |
|
|
13
|
+
| `longPress` | `marionette.longPress` | Optional `duration` (ms). Default 600ms. |
|
|
14
|
+
| `enterText` | `marionette.enterText` | Requires `input` plus a matcher. |
|
|
15
|
+
| `clearText` | _(new — enhancement)_ | Convenience wrapper around `enterText('')`. Saves agents two round-trips. |
|
|
16
|
+
| `swipe` | `marionette.swipe` | Either coord-based (`startX/Y` + `endX/Y`) or element + `direction` + `distance`. |
|
|
17
|
+
| `pinchZoom` | `marionette.pinchZoom` | Required `scale`, optional `startDistance` (default 200). |
|
|
18
|
+
| `scrollTo` | `marionette.scrollTo` | Scrolls until the matched widget is visible. |
|
|
19
|
+
| `getLogs` | `marionette.getLogs` | Requires a `LogCollector` in `UltraConfiguration`. |
|
|
20
|
+
| `takeScreenshots` | `marionette.takeScreenshots` | Returns one screenshot per `RenderView`. |
|
|
21
|
+
| `startScreencast` | `marionette.startScreencast` | Optional `maxWidth`, `maxHeight`, `wsPort`. |
|
|
22
|
+
| `stopScreencast` | `marionette.stopScreencast` | Idempotent. |
|
|
23
|
+
| `pressBackButton` | `marionette.pressBackButton` | Returns `didPop`. |
|
|
24
|
+
| `listExtensions` | `marionette.listExtensions` | Returns user-registered custom extensions (registered via `registerUltraExtension`). |
|
|
25
|
+
|
|
26
|
+
15 extensions in total: 14 ported + 1 enhancement (`clearText`).
|
|
27
|
+
|
|
28
|
+
## Adding a custom extension
|
|
29
|
+
|
|
30
|
+
```dart
|
|
31
|
+
import 'package:ultra_flutter/ultra_flutter.dart';
|
|
32
|
+
|
|
33
|
+
void registerMyExtensions() {
|
|
34
|
+
registerUltraExtension(
|
|
35
|
+
name: 'myApp.fetchProfile', // becomes `ext.flutter.myApp.fetchProfile`
|
|
36
|
+
description: 'Returns the currently-signed-in profile snapshot.',
|
|
37
|
+
callback: (params) async {
|
|
38
|
+
final id = params['id'];
|
|
39
|
+
if (id == null) {
|
|
40
|
+
return UltraExtensionResult.invalidParams('Missing "id"');
|
|
41
|
+
}
|
|
42
|
+
// ...
|
|
43
|
+
return UltraExtensionResult.success({'profile': {/* ... */}});
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Call `registerMyExtensions()` after `AppBinding.ensureInitialized()` and before `runApp()`. Custom extensions show up in the `listExtensions` response so the MCP server can advertise them to the LLM.
|
|
50
|
+
|
|
51
|
+
## Why `ext.flutter.ultra.*` and not `ext.ultra.*`
|
|
52
|
+
|
|
53
|
+
Flutter's DevTools and `package:vm_service` only allow registration under `ext.flutter.*` — the `flutter.` prefix is hard-coded by `developer.registerExtension` validation in our `register_extension_internal.dart`. The `ultra` second segment is our namespace.
|
|
54
|
+
|
|
55
|
+
## Planned future extensions
|
|
56
|
+
|
|
57
|
+
- `ultra.waitFor` — server-side polling that resolves when a matcher hits (saves agents N round-trips).
|
|
58
|
+
- `ultra.scrollUntilVisible` — single-call hybrid of `scrollTo` + `waitFor` for stubborn lazy lists.
|
|
59
|
+
- `ultra.matcher` — hierarchical matchers (`{type: 'TextField', ancestor: {key: 'login_form'}}`).
|
|
60
|
+
|
|
61
|
+
Tracked at plan §6.1; not in 0.0.1-dev.
|
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
# ultra_flutter
|
|
2
|
-
|
|
3
|
-
In-app Flutter binding mixin that exposes `ext.flutter.ultra.*` VM service
|
|
4
|
-
extensions used by the `flutter-ultra-mcp` Claude Code plugin. AI agents
|
|
5
|
-
attach via the Dart VM Service and drive your app — taps, text input,
|
|
6
|
-
scroll, screenshots, screencast, logs, hot reload, custom RPCs — without
|
|
7
|
-
any flutter_driver instrumentation.
|
|
8
|
-
|
|
9
|
-
A composable fork of [`marionette_flutter`][marionette] v0.5.0 with one
|
|
10
|
-
critical UX win: the binding is a **mixin** (`mixin UltraFlutterBinding on
|
|
11
|
-
WidgetsBinding`) instead of a class, so it stacks cleanly onto other
|
|
12
|
-
`WidgetsFlutterBinding` subclasses (Sentry, integration_test, etc).
|
|
13
|
-
|
|
14
|
-
[marionette]: https://github.com/leancodepl/marionette_mcp
|
|
15
|
-
|
|
16
|
-
## Install
|
|
17
|
-
|
|
18
|
-
```yaml
|
|
19
|
-
dev_dependencies:
|
|
20
|
-
ultra_flutter: ^0.0.1-dev.1
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
(Only `dev_dependencies` — the binding code is fully tree-shaken out of
|
|
24
|
-
release builds when guarded behind `kDebugMode`.)
|
|
25
|
-
|
|
26
|
-
## Usage
|
|
27
|
-
|
|
28
|
-
### Simple — no other custom binding
|
|
29
|
-
|
|
30
|
-
```dart
|
|
31
|
-
import 'package:flutter/foundation.dart';
|
|
32
|
-
import 'package:flutter/widgets.dart';
|
|
33
|
-
import 'package:ultra_flutter/ultra_flutter.dart';
|
|
34
|
-
|
|
35
|
-
Future<void> main() async {
|
|
36
|
-
if (kDebugMode) {
|
|
37
|
-
UltraFlutterBinding.ensureInitialized();
|
|
38
|
-
} else {
|
|
39
|
-
WidgetsFlutterBinding.ensureInitialized();
|
|
40
|
-
}
|
|
41
|
-
runApp(const MyApp());
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### Composed with Sentry
|
|
46
|
-
|
|
47
|
-
Mix in `SentryWidgetsBindingMixin` directly alongside `UltraFlutterBinding`:
|
|
48
|
-
|
|
49
|
-
```dart
|
|
50
|
-
import 'package:flutter/widgets.dart';
|
|
51
|
-
import 'package:sentry_flutter/sentry_flutter.dart';
|
|
52
|
-
import 'package:ultra_flutter/ultra_flutter.dart';
|
|
53
|
-
// ignore: implementation_imports
|
|
54
|
-
import 'package:sentry_flutter/src/binding_wrapper.dart';
|
|
55
|
-
|
|
56
|
-
class AppBinding extends WidgetsFlutterBinding
|
|
57
|
-
with SentryWidgetsBindingMixin, UltraFlutterBinding {}
|
|
58
|
-
|
|
59
|
-
Future<void> main() async {
|
|
60
|
-
AppBinding.ensureInitialized();
|
|
61
|
-
await SentryFlutter.init(
|
|
62
|
-
(options) => options.dsn = '<your dsn>',
|
|
63
|
-
appRunner: () => runApp(const MyApp()),
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
This sidesteps the historical marionette↔Sentry "must be the only binding"
|
|
69
|
-
race — both instrumentations live on a single composed binding instance,
|
|
70
|
-
no singleton race needed.
|
|
71
|
-
|
|
72
|
-
### Configuration
|
|
73
|
-
|
|
74
|
-
```dart
|
|
75
|
-
UltraFlutterBinding.setUltraConfiguration(
|
|
76
|
-
UltraConfiguration(
|
|
77
|
-
isInteractiveWidget: (type) => type.toString() == 'MyCustomButton',
|
|
78
|
-
extractText: (element) {
|
|
79
|
-
final widget = element.widget;
|
|
80
|
-
if (widget is MyCustomLabel) return widget.labelText;
|
|
81
|
-
return null;
|
|
82
|
-
},
|
|
83
|
-
logCollector: PrintLogCollector(), // or LoggingLogCollector, etc.
|
|
84
|
-
maxScreenshotSize: const Size(2000, 2000),
|
|
85
|
-
),
|
|
86
|
-
);
|
|
87
|
-
UltraFlutterBinding.ensureInitialized(); // or your AppBinding subclass
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Configuration **must** be set BEFORE the binding initializes (i.e. before
|
|
91
|
-
the first `runApp` / `ensureInitialized`). The mixin form can't take
|
|
92
|
-
constructor arguments, so the static slot pattern is required.
|
|
93
|
-
|
|
94
|
-
## Extensions
|
|
95
|
-
|
|
96
|
-
See [EXTENSIONS.md](EXTENSIONS.md) for the full catalogue of
|
|
97
|
-
`ext.flutter.ultra.*` service extensions and how to register your own
|
|
98
|
-
custom ones.
|
|
99
|
-
|
|
100
|
-
## License
|
|
101
|
-
|
|
102
|
-
Apache-2.0. This is a fork of `marionette_flutter` (also Apache-2.0); see
|
|
103
|
-
top-level [LICENSE](../../LICENSE) and `NOTICE`.
|
|
104
|
-
|
|
105
|
-
## Status
|
|
106
|
-
|
|
107
|
-
Pre-1.0. Mixin shape stable; further extensions (`waitFor`,
|
|
108
|
-
`scrollUntilVisible`, hierarchical matchers) tracked in the plan §6.1
|
|
109
|
-
followups.
|
|
1
|
+
# ultra_flutter
|
|
2
|
+
|
|
3
|
+
In-app Flutter binding mixin that exposes `ext.flutter.ultra.*` VM service
|
|
4
|
+
extensions used by the `flutter-ultra-mcp` Claude Code plugin. AI agents
|
|
5
|
+
attach via the Dart VM Service and drive your app — taps, text input,
|
|
6
|
+
scroll, screenshots, screencast, logs, hot reload, custom RPCs — without
|
|
7
|
+
any flutter_driver instrumentation.
|
|
8
|
+
|
|
9
|
+
A composable fork of [`marionette_flutter`][marionette] v0.5.0 with one
|
|
10
|
+
critical UX win: the binding is a **mixin** (`mixin UltraFlutterBinding on
|
|
11
|
+
WidgetsBinding`) instead of a class, so it stacks cleanly onto other
|
|
12
|
+
`WidgetsFlutterBinding` subclasses (Sentry, integration_test, etc).
|
|
13
|
+
|
|
14
|
+
[marionette]: https://github.com/leancodepl/marionette_mcp
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```yaml
|
|
19
|
+
dev_dependencies:
|
|
20
|
+
ultra_flutter: ^0.0.1-dev.1
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
(Only `dev_dependencies` — the binding code is fully tree-shaken out of
|
|
24
|
+
release builds when guarded behind `kDebugMode`.)
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Simple — no other custom binding
|
|
29
|
+
|
|
30
|
+
```dart
|
|
31
|
+
import 'package:flutter/foundation.dart';
|
|
32
|
+
import 'package:flutter/widgets.dart';
|
|
33
|
+
import 'package:ultra_flutter/ultra_flutter.dart';
|
|
34
|
+
|
|
35
|
+
Future<void> main() async {
|
|
36
|
+
if (kDebugMode) {
|
|
37
|
+
UltraFlutterBinding.ensureInitialized();
|
|
38
|
+
} else {
|
|
39
|
+
WidgetsFlutterBinding.ensureInitialized();
|
|
40
|
+
}
|
|
41
|
+
runApp(const MyApp());
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Composed with Sentry
|
|
46
|
+
|
|
47
|
+
Mix in `SentryWidgetsBindingMixin` directly alongside `UltraFlutterBinding`:
|
|
48
|
+
|
|
49
|
+
```dart
|
|
50
|
+
import 'package:flutter/widgets.dart';
|
|
51
|
+
import 'package:sentry_flutter/sentry_flutter.dart';
|
|
52
|
+
import 'package:ultra_flutter/ultra_flutter.dart';
|
|
53
|
+
// ignore: implementation_imports
|
|
54
|
+
import 'package:sentry_flutter/src/binding_wrapper.dart';
|
|
55
|
+
|
|
56
|
+
class AppBinding extends WidgetsFlutterBinding
|
|
57
|
+
with SentryWidgetsBindingMixin, UltraFlutterBinding {}
|
|
58
|
+
|
|
59
|
+
Future<void> main() async {
|
|
60
|
+
AppBinding.ensureInitialized();
|
|
61
|
+
await SentryFlutter.init(
|
|
62
|
+
(options) => options.dsn = '<your dsn>',
|
|
63
|
+
appRunner: () => runApp(const MyApp()),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This sidesteps the historical marionette↔Sentry "must be the only binding"
|
|
69
|
+
race — both instrumentations live on a single composed binding instance,
|
|
70
|
+
no singleton race needed.
|
|
71
|
+
|
|
72
|
+
### Configuration
|
|
73
|
+
|
|
74
|
+
```dart
|
|
75
|
+
UltraFlutterBinding.setUltraConfiguration(
|
|
76
|
+
UltraConfiguration(
|
|
77
|
+
isInteractiveWidget: (type) => type.toString() == 'MyCustomButton',
|
|
78
|
+
extractText: (element) {
|
|
79
|
+
final widget = element.widget;
|
|
80
|
+
if (widget is MyCustomLabel) return widget.labelText;
|
|
81
|
+
return null;
|
|
82
|
+
},
|
|
83
|
+
logCollector: PrintLogCollector(), // or LoggingLogCollector, etc.
|
|
84
|
+
maxScreenshotSize: const Size(2000, 2000),
|
|
85
|
+
),
|
|
86
|
+
);
|
|
87
|
+
UltraFlutterBinding.ensureInitialized(); // or your AppBinding subclass
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Configuration **must** be set BEFORE the binding initializes (i.e. before
|
|
91
|
+
the first `runApp` / `ensureInitialized`). The mixin form can't take
|
|
92
|
+
constructor arguments, so the static slot pattern is required.
|
|
93
|
+
|
|
94
|
+
## Extensions
|
|
95
|
+
|
|
96
|
+
See [EXTENSIONS.md](EXTENSIONS.md) for the full catalogue of
|
|
97
|
+
`ext.flutter.ultra.*` service extensions and how to register your own
|
|
98
|
+
custom ones.
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
Apache-2.0. This is a fork of `marionette_flutter` (also Apache-2.0); see
|
|
103
|
+
top-level [LICENSE](../../LICENSE) and `NOTICE`.
|
|
104
|
+
|
|
105
|
+
## Status
|
|
106
|
+
|
|
107
|
+
Pre-1.0. Mixin shape stable; further extensions (`waitFor`,
|
|
108
|
+
`scrollUntilVisible`, hierarchical matchers) tracked in the plan §6.1
|
|
109
|
+
followups.
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
name: ultra_flutter
|
|
2
|
-
description: >-
|
|
3
|
-
In-app Flutter binding mixin exposing ext.flutter.ultra.* VM service
|
|
4
|
-
extensions (gesture, screenshot, inspector, screencast, log collection)
|
|
5
|
-
for the flutter-ultra-mcp Claude Code plugin. Composable with Sentry and
|
|
6
|
-
other WidgetsFlutterBinding subclasses via the mixin form.
|
|
7
|
-
version: 0.0.1-dev.1
|
|
8
|
-
homepage: https://github.com/Bdaya-Dev/flutter-ultra-mcp
|
|
9
|
-
repository: https://github.com/Bdaya-Dev/flutter-ultra-mcp
|
|
10
|
-
issue_tracker: https://github.com/Bdaya-Dev/flutter-ultra-mcp/issues
|
|
11
|
-
topics:
|
|
12
|
-
- mcp
|
|
13
|
-
- ai
|
|
14
|
-
- claude
|
|
15
|
-
- agent
|
|
16
|
-
- testing
|
|
17
|
-
|
|
18
|
-
publish_to: none
|
|
19
|
-
|
|
20
|
-
environment:
|
|
21
|
-
sdk: '>=3.6.0 <4.0.0'
|
|
22
|
-
flutter: '>=3.27.0'
|
|
23
|
-
|
|
24
|
-
resolution: workspace
|
|
25
|
-
|
|
26
|
-
dependencies:
|
|
27
|
-
flutter:
|
|
28
|
-
sdk: flutter
|
|
29
|
-
web: ^1.1.0
|
|
30
|
-
|
|
31
|
-
dev_dependencies:
|
|
32
|
-
flutter_test:
|
|
33
|
-
sdk: flutter
|
|
34
|
-
test: '>=1.24.0'
|
|
35
|
-
|
|
36
|
-
flutter:
|
|
37
|
-
uses-material-design: false
|
|
1
|
+
name: ultra_flutter
|
|
2
|
+
description: >-
|
|
3
|
+
In-app Flutter binding mixin exposing ext.flutter.ultra.* VM service
|
|
4
|
+
extensions (gesture, screenshot, inspector, screencast, log collection)
|
|
5
|
+
for the flutter-ultra-mcp Claude Code plugin. Composable with Sentry and
|
|
6
|
+
other WidgetsFlutterBinding subclasses via the mixin form.
|
|
7
|
+
version: 0.0.1-dev.1
|
|
8
|
+
homepage: https://github.com/Bdaya-Dev/flutter-ultra-mcp
|
|
9
|
+
repository: https://github.com/Bdaya-Dev/flutter-ultra-mcp
|
|
10
|
+
issue_tracker: https://github.com/Bdaya-Dev/flutter-ultra-mcp/issues
|
|
11
|
+
topics:
|
|
12
|
+
- mcp
|
|
13
|
+
- ai
|
|
14
|
+
- claude
|
|
15
|
+
- agent
|
|
16
|
+
- testing
|
|
17
|
+
|
|
18
|
+
publish_to: none
|
|
19
|
+
|
|
20
|
+
environment:
|
|
21
|
+
sdk: '>=3.6.0 <4.0.0'
|
|
22
|
+
flutter: '>=3.27.0'
|
|
23
|
+
|
|
24
|
+
resolution: workspace
|
|
25
|
+
|
|
26
|
+
dependencies:
|
|
27
|
+
flutter:
|
|
28
|
+
sdk: flutter
|
|
29
|
+
web: ^1.1.0
|
|
30
|
+
|
|
31
|
+
dev_dependencies:
|
|
32
|
+
flutter_test:
|
|
33
|
+
sdk: flutter
|
|
34
|
+
test: '>=1.24.0'
|
|
35
|
+
|
|
36
|
+
flutter:
|
|
37
|
+
uses-material-design: false
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# ultra_flutter_devtools
|
|
2
|
-
|
|
3
|
-
Flutter DevTools extension UI for the `flutter-ultra-mcp` plugin. Shows live MCP activity: attached sessions, recent tool calls, errors, screenshot grid.
|
|
4
|
-
|
|
5
|
-
**Status:** scaffold stub. Implementation owner: **wave-2 devtools worker** (see plan §12, §6.2).
|
|
6
|
-
|
|
7
|
-
Loaded by Flutter DevTools as an iframe extension. Pairs with the [`flutter-ultra-devtools`](../../packages/flutter-ultra-devtools/) MCP server which tails `state/tool-events.jsonl` and pushes events over WebSocket.
|
|
1
|
+
# ultra_flutter_devtools
|
|
2
|
+
|
|
3
|
+
Flutter DevTools extension UI for the `flutter-ultra-mcp` plugin. Shows live MCP activity: attached sessions, recent tool calls, errors, screenshot grid.
|
|
4
|
+
|
|
5
|
+
**Status:** scaffold stub. Implementation owner: **wave-2 devtools worker** (see plan §12, §6.2).
|
|
6
|
+
|
|
7
|
+
Loaded by Flutter DevTools as an iframe extension. Pairs with the [`flutter-ultra-devtools`](../../packages/flutter-ultra-devtools/) MCP server which tails `state/tool-events.jsonl` and pushes events over WebSocket.
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
name: ultra_flutter_devtools
|
|
2
|
-
description: >-
|
|
3
|
-
Flutter DevTools extension showing live MCP activity for the flutter-ultra-mcp
|
|
4
|
-
Claude Code plugin (sessions, recent tool calls, errors, screenshot grid).
|
|
5
|
-
version: 0.1.0
|
|
6
|
-
homepage: https://github.com/Bdaya-Dev/flutter-ultra-mcp
|
|
7
|
-
repository: https://github.com/Bdaya-Dev/flutter-ultra-mcp
|
|
8
|
-
issue_tracker: https://github.com/Bdaya-Dev/flutter-ultra-mcp/issues
|
|
9
|
-
|
|
10
|
-
publish_to: none
|
|
11
|
-
|
|
12
|
-
environment:
|
|
13
|
-
sdk: ^3.5.0
|
|
14
|
-
flutter: '>=3.24.0'
|
|
15
|
-
|
|
16
|
-
resolution: workspace
|
|
17
|
-
|
|
18
|
-
dependencies:
|
|
19
|
-
flutter:
|
|
20
|
-
sdk: flutter
|
|
21
|
-
devtools_extensions: ^0.3.0
|
|
22
|
-
devtools_app_shared: ^0.3.0
|
|
23
|
-
web: ^1.1.0
|
|
24
|
-
|
|
25
|
-
dev_dependencies:
|
|
26
|
-
flutter_test:
|
|
27
|
-
sdk: flutter
|
|
1
|
+
name: ultra_flutter_devtools
|
|
2
|
+
description: >-
|
|
3
|
+
Flutter DevTools extension showing live MCP activity for the flutter-ultra-mcp
|
|
4
|
+
Claude Code plugin (sessions, recent tool calls, errors, screenshot grid).
|
|
5
|
+
version: 0.1.0
|
|
6
|
+
homepage: https://github.com/Bdaya-Dev/flutter-ultra-mcp
|
|
7
|
+
repository: https://github.com/Bdaya-Dev/flutter-ultra-mcp
|
|
8
|
+
issue_tracker: https://github.com/Bdaya-Dev/flutter-ultra-mcp/issues
|
|
9
|
+
|
|
10
|
+
publish_to: none
|
|
11
|
+
|
|
12
|
+
environment:
|
|
13
|
+
sdk: ^3.5.0
|
|
14
|
+
flutter: '>=3.24.0'
|
|
15
|
+
|
|
16
|
+
resolution: workspace
|
|
17
|
+
|
|
18
|
+
dependencies:
|
|
19
|
+
flutter:
|
|
20
|
+
sdk: flutter
|
|
21
|
+
devtools_extensions: ^0.3.0
|
|
22
|
+
devtools_app_shared: ^0.3.0
|
|
23
|
+
web: ^1.1.0
|
|
24
|
+
|
|
25
|
+
dev_dependencies:
|
|
26
|
+
flutter_test:
|
|
27
|
+
sdk: flutter
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# Upstream Patrol PRs
|
|
2
|
-
|
|
3
|
-
Placeholder. **Wave-4 worker M** populates this document with the upstream Patrol PRs we open against [`leancodepl/patrol`](https://github.com/leancodepl/patrol) from the Bdaya patrol fork at `vendor/patrol/` — including PR URLs, status, the corresponding fork commits, and the rebase-back-to-bdaya-main plan for the merged ones.
|
|
4
|
-
|
|
5
|
-
See plan §12 wave 4 for the full schedule.
|
|
1
|
+
# Upstream Patrol PRs
|
|
2
|
+
|
|
3
|
+
Placeholder. **Wave-4 worker M** populates this document with the upstream Patrol PRs we open against [`leancodepl/patrol`](https://github.com/leancodepl/patrol) from the Bdaya patrol fork at `vendor/patrol/` — including PR URLs, status, the corresponding fork commits, and the rebase-back-to-bdaya-main plan for the merged ones.
|
|
4
|
+
|
|
5
|
+
See plan §12 wave 4 for the full schedule.
|