# mushi_mushi > **Your AI wrote it. Mushi tells you why it broke.** Flutter SDK for [Mushi Mushi](https://kensaur.us/mushi-mushi) — the open-source, LLM-driven bug intake, classification, and autofix platform. > **Status: preview.** The API surface is stable at 0.3.0, but the package is > **not published to pub.dev yet** — `flutter pub add mushi_mushi` and > `mushi_mushi: ^0.3.0` will not resolve. Install it from this repository as a > git dependency (below) until the pub.dev release lands. ## Features - 📳 **Shake-to-report** via `sensors_plus` — works on iOS & Android - 📦 **Offline queue** that survives app restarts (file-backed, byte-capped) - 🎯 **Material bottom-sheet widget** with category picker and live min-length validation - 🌐 **Device + app context** auto-attached via `device_info_plus` and `package_info_plus` - 🔌 **Optional Sentry bridge** via `Mushi.instance.onReportSubmitted` ## Install ```yaml dependencies: mushi_mushi: git: url: https://github.com/kensaurus/mushi-mushi.git path: packages/flutter # ref: # pin a commit for reproducible builds ``` Then run `flutter pub get`. ## Quickstart ```dart import 'package:flutter/material.dart'; import 'package:mushi_mushi/mushi_mushi.dart'; void main() { Mushi.instance.configure(const MushiConfig( projectId: 'proj_...', apiKey: 'mushi_...', triggerMode: MushiTriggerMode.both, captureScreenshot: true, minDescriptionLength: 20, )); runApp(const MyApp()); } final mushiBoundary = GlobalKey(); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { Mushi.instance.screenshotBoundaryKey = mushiBoundary; return MaterialApp( builder: (ctx, child) { Mushi.instance.rootContext = ctx; return RepaintBoundary(key: mushiBoundary, child: child); }, home: HomeScreen(), ); } } ``` To present the widget programmatically: ```dart ElevatedButton( onPressed: () => Mushi.instance.showWidget(context), child: const Text('Report a bug'), ); ``` To fire a report from code (no UI): ```dart await Mushi.instance.report( description: 'Profile photo upload spinner never stops on tablets', category: 'bug', ); try { await riskyCall(); } catch (e, st) { await Mushi.instance.captureError(e, st); } ``` ## Sentry bridge The Flutter SDK exposes an `onReportSubmitted` callback you can wire to any crash reporter. For Sentry: ```dart import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:mushi_mushi/mushi_mushi.dart'; await SentryFlutter.init((o) => o.dsn = 'https://...sentry.io/0'); Mushi.instance.configure(const MushiConfig(/* ... */)); Mushi.instance.onReportSubmitted = (payload) async { final desc = payload['description']?.toString(); if (desc == null || desc.isEmpty) return; final id = await Sentry.captureMessage('MushiReport: ${desc.substring(0, desc.length.clamp(0, 80))}'); await Sentry.captureUserFeedback(SentryUserFeedback(eventId: id, comments: desc)); }; ``` ## Configuration | Field | Default | Notes | |------------------------|--------------------------------------|-------| | `projectId` | _required_ | Project UUID from Mushi admin | | `apiKey` | _required_ | Project API key (`mushi_...`) | | `endpoint` | `https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api` | Override for self-hosting | | `triggerMode` | `shake` | `shake` / `button` / `both` / `none` | | `captureScreenshot` | `true` | Requires a `RepaintBoundary` boundary key | | `minDescriptionLength` | `20` | Matches the web SDK contract | | `offlineQueueMaxBytes` | `2 * 1024 * 1024` | Soft cap; oldest entries trim first | | `theme` | `MushiTheme(accentColor: Color(0xFF22C55E))` | | | `triggerInsets` | `MushiTriggerInsets(right: 24, bottom: 32)` | Per-edge offset for the in-app `MushiFloatingTrigger` overlay (see `lib/src/overlay.dart`). Set `left` to `0` to anchor on the leading edge instead. | ## Privacy - The SDK never logs secrets, tokens, or full request bodies. - Screenshots are captured only when `captureScreenshot == true` and a `screenshotBoundaryKey` is provided. - The offline queue lives in your app's support directory and is removed when the app is uninstalled. ## License MIT — see [LICENSE](../../LICENSE) at the repo root.