@bdayadev/flutter-ultra-mcp 0.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.
Files changed (57) hide show
  1. package/.claude-plugin/plugin.json +14 -0
  2. package/.mcp.json +67 -0
  3. package/CODE_OF_CONDUCT.md +83 -0
  4. package/CONTRIBUTING.md +108 -0
  5. package/LICENSE +201 -0
  6. package/README.md +77 -0
  7. package/SECURITY.md +19 -0
  8. package/dart/ultra_flutter/CHANGELOG.md +34 -0
  9. package/dart/ultra_flutter/EXTENSIONS.md +61 -0
  10. package/dart/ultra_flutter/README.md +109 -0
  11. package/dart/ultra_flutter/pubspec.yaml +37 -0
  12. package/dart/ultra_flutter_devtools/README.md +7 -0
  13. package/dart/ultra_flutter_devtools/pubspec.yaml +27 -0
  14. package/docs/UPSTREAM-PATROL-PRS.md +5 -0
  15. package/docs/UPSTREAM-SENTRY-PR.md +62 -0
  16. package/docs/discovery-empirics.md +435 -0
  17. package/examples/counter-app/README.md +24 -0
  18. package/examples/counter-app/pubspec.yaml +23 -0
  19. package/examples/oidc-app/README.md +48 -0
  20. package/examples/oidc-app/pubspec.yaml +24 -0
  21. package/package.json +82 -0
  22. package/packages/flutter-ultra-browser/README.md +29 -0
  23. package/packages/flutter-ultra-browser/package.json +39 -0
  24. package/packages/flutter-ultra-build/README.md +60 -0
  25. package/packages/flutter-ultra-build/package.json +38 -0
  26. package/packages/flutter-ultra-devtools/README.md +7 -0
  27. package/packages/flutter-ultra-devtools/package.json +36 -0
  28. package/packages/flutter-ultra-gesture/README.md +51 -0
  29. package/packages/flutter-ultra-gesture/package.json +58 -0
  30. package/packages/flutter-ultra-native-desktop/README.md +131 -0
  31. package/packages/flutter-ultra-native-desktop/package.json +81 -0
  32. package/packages/flutter-ultra-native-mobile/README.md +103 -0
  33. package/packages/flutter-ultra-native-mobile/package.json +72 -0
  34. package/packages/flutter-ultra-patrol/README.md +40 -0
  35. package/packages/flutter-ultra-patrol/package.json +38 -0
  36. package/packages/flutter-ultra-runtime/README.md +63 -0
  37. package/packages/flutter-ultra-runtime/package.json +69 -0
  38. package/shared/contracts/package.json +31 -0
  39. package/shared/device-router/README.md +51 -0
  40. package/shared/device-router/package.json +62 -0
  41. package/shared/keyring/README.md +7 -0
  42. package/shared/keyring/package.json +24 -0
  43. package/shared/mcp-runtime/README.md +116 -0
  44. package/shared/mcp-runtime/package.json +58 -0
  45. package/shared/state-store/README.md +66 -0
  46. package/shared/state-store/package.json +60 -0
  47. package/shared/vm-service-client/README.md +135 -0
  48. package/shared/vm-service-client/package.json +62 -0
  49. package/skills/_internal-on-tool-failure/SKILL.md +13 -0
  50. package/skills/_internal-session-bootstrap/SKILL.md +18 -0
  51. package/skills/debug/SKILL.md +20 -0
  52. package/skills/devtools/SKILL.md +21 -0
  53. package/skills/drive/SKILL.md +20 -0
  54. package/skills/scaffold/SKILL.md +21 -0
  55. package/skills/setup/SKILL.md +26 -0
  56. package/skills/test/SKILL.md +19 -0
  57. package/skills/tour/SKILL.md +22 -0
@@ -0,0 +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.
@@ -0,0 +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.
@@ -0,0 +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
@@ -0,0 +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.
@@ -0,0 +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
@@ -0,0 +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.
@@ -0,0 +1,62 @@
1
+ # Upstream Sentry PR — expose `SentryWidgetsBindingMixin`
2
+
3
+ ## Status
4
+
5
+ **Not filed yet.** Tracked as Path A per `flutter-ultra-mcp-v1.md` §6.1.
6
+ Filing this PR is opportunistic cleanup, not a blocker for v1.
7
+
8
+ ## Current state
9
+
10
+ `package:sentry_flutter/sentry_flutter.dart` (v9.20.0) declares:
11
+
12
+ ```dart
13
+ export 'src/binding_wrapper.dart'
14
+ show BindingWrapper, SentryWidgetsFlutterBinding;
15
+ ```
16
+
17
+ The `show` clause hides `SentryWidgetsBindingMixin` from the public API
18
+ even though the mixin is defined in the same file. Consumers reach it via a
19
+ deliberate `// ignore: implementation_imports` import of
20
+ `package:sentry_flutter/src/binding_wrapper.dart`.
21
+
22
+ ## Proposed change
23
+
24
+ Extend the `show` clause to include the mixin:
25
+
26
+ ```dart
27
+ export 'src/binding_wrapper.dart'
28
+ show BindingWrapper, SentryWidgetsFlutterBinding, SentryWidgetsBindingMixin;
29
+ ```
30
+
31
+ The mixin is already used as a typedef inside other public Sentry classes,
32
+ so exposing it is a non-breaking change.
33
+
34
+ ## Motivation
35
+
36
+ Composability. Multiple Flutter ecosystem packages (testing harnesses,
37
+ debug bindings, AI-agent automation like ultra_flutter) need to mix
38
+ Sentry's binding instrumentation alongside their own. Today they either:
39
+
40
+ 1. Subclass `SentryWidgetsFlutterBinding` (cannot also compose another
41
+ `WidgetsFlutterBinding` subclass — Dart `with` requires a mixin), OR
42
+ 2. Reach into `package:sentry_flutter/src/binding_wrapper.dart` directly
43
+ with `// ignore: implementation_imports` (brittle, breaks the visibility
44
+ contract).
45
+
46
+ Making the mixin public moves option 2 from "private reach-through" to
47
+ "sanctioned composition".
48
+
49
+ ## Risks
50
+
51
+ - Surface-area expansion: once public, the mixin name + signature are
52
+ versioned. Sentry maintainers would need to follow semver on it.
53
+ - Misuse: users might mix the mixin onto unusual base classes and hit
54
+ internal-state assumptions. Counter: the mixin already encapsulates its
55
+ state cleanly (`_isTrackingActive`, `_options`, etc.) and gracefully
56
+ degrades when the rest of Sentry isn't wired.
57
+
58
+ ## When to file
59
+
60
+ After `ultra_flutter` ships v1.0 and we have at least one real consumer
61
+ demonstrating the composition pattern. PR will reference the
62
+ ultra_flutter use case as the concrete motivation.