bugstweet 0.9.7
bugstweet: ^0.9.7 copied to clipboard
Testers report bugs from inside your Flutter app as GitHub issues an AI coding agent can fix. Tester builds only: store builds never contain it.
bugsTweet #
Testers report bugs from inside your Flutter app, on the phone. They tap a button, mark the bug on the screenshot, and send. Each report arrives as a GitHub issue with everything a developer or an AI coding agent (Claude Code, Codex, Cursor and the like) needs to fix it.
- No account, no server, no cost.
- Your app's code is never changed.
- Store builds never contain bugsTweet. Only tester builds do.
How it stays out of your app #
bugsTweet comes in two packages. You only ever add and type the first one.
bugstweet: the tool. You add it to yourpubspec.yamlas a development package. It has no phone parts, and your app never uses its code, so nothing of it is packed into any build.bugstweet_reporter: the button and the phone parts testers use. You never add it. The tool adds it to tester builds only.
How a tester build is made:
- Your app gets one small file,
.bugstweet/tester.dart. The folder name starts with a dot, so your normal code checks and IDE skip it. dart run bugstweet buildcopies your app to a cache folder on your computer, addsbugstweet_reporterto that copy only, and builds the tester app from the copy.- For a store build, you run
flutter buildas always. Your app does not listbugstweet_reporter, so none of it can be packed into the store app.
What is inside each build (checked on Android and iPhone builds):
| Inside the app | Store build | Tester build |
|---|---|---|
| bugsTweet code (button, screens, sending) | no | yes |
| bugsTweet phone parts (screenshot and shake listeners) | no | yes |
| Android "detect screen capture" permission | no | yes |
| iPhone motion permission text | no | yes |
Set up (once per app) #
You need Flutter 3.38 or later. In your app's folder:
-
Add the tool as a development package:
flutter pub add --dev bugstweet -
Create the tester file:
dart run bugstweet init -
Try it on a phone or emulator:
dart run bugstweet runIt starts on the first phone, emulator or simulator it finds, and tells you how to open one if none is there. The app starts with a round bugsTweet button. Reports go out through the phone's share sheet until you connect GitHub (step 4).
-
Send reports to GitHub as issues: create a reports repository and a key, as described in The GitHub key, then open
.bugstweet/tester.dartand check the two repository names.
That is all. Apart from the one bugstweet line under dev_dependencies,
your pubspec.yaml, lib/ folder and platform files stay as they were.
Make a tester build #
dart run bugstweet build apk --dart-define=BUGS_TWEET_GITHUB_TOKEN=github_pat_...
dart run bugstweet build ipa --dart-define=BUGS_TWEET_GITHUB_TOKEN=github_pat_...
You can build apk, appbundle, ipa or ios. Every option you would give
flutter build works here too (flavors, build numbers, export options).
Paths in options, such as --split-debug-info or --dart-define-from-file,
are read from your app folder, as in a normal build. A signing key kept
outside the app (for example storeFile=../../upload-keystore.jks in
android/key.properties) is found as well.
Give tester builds their own build number, for example
--build-number=9001. Otherwise a tester build has the same number as your
store build, and one uploaded to TestFlight can take the number of a real
release.
The finished tester build goes to build/bugstweet/, for example
build/bugstweet/apk/app-release.apk. It is kept apart from store builds,
which stay in their usual place. Give tester builds to testers only (for
example TestFlight internal testing or Firebase App Distribution), never to
a store.
The first tester build takes a few minutes longer, because the copy starts empty. Later builds reuse it.
What testers get #
- Tap the bugsTweet button (the logo): the screen is saved, and a thumbnail waits in the corner for four seconds. Tap the thumbnail to mark it up right away, or swipe it away and keep testing.
- Hold the button: a menu of choices: Tray, Report now, Long shot, Record 10 s, Timer 3 s, Element and Hide.
- Timer 3 s: a 3, 2, 1 countdown, then a screenshot, so a menu or a pressed state can be captured.
- Element: tap a card or button, then tap it again: a screenshot of just that part.
- Long shot: tap the part of the screen that scrolls, or Whole screen, and bugsTweet scrolls it for you and joins the pictures into one tall capture.
- Record 10 s: use the app while the button counts down; tap it to stop early. The clip, with every touch drawn as a red dot, becomes a report and plays in the GitHub issue. Only the app is recorded, so the phone asks for no permission, and private areas are covered as in screenshots.
- Shake the phone: a screenshot, or the hidden button comes back.
- Screenshot with the phone's own buttons: bugsTweet asks "Report this?". Works on iPhone and on Android 14 or later.
- Marks: arrow, line, box, circle, pen, highlight, spotlight, magnify, blur, text, numbered steps, stamps (Wrong text, Misaligned, Too slow, Crash...). Tap an element with a shape tool and the shape snaps to it.
- Design tools: Ruler (in points), Color (hex), Contrast (WCAG AA/AAA).
- Tray: review, add notes, merge several screenshots of one problem, and send all at once. It also lists reported issues with their status (Open, In progress, Pending, Done); tap one to open it in the GitHub app.
- Retest: every reported issue offers Retest. A card shows the way back to the bug (old screenshot, screen name, last steps); Capture compares the new screen with the old one on a before/after slider. Fixed closes the issue, Still broken reopens it.
- Answer the agent: when a coding agent writes its plan (Actual, Expected, Fix) on an issue, the tray shows it as a card. Tap Go to agree, or Not quite and say what is different.
- Copy for agents: copy one or several open reports as one message for a coding agent.
- Recap: today's test session on one card, ready to share.
- Voice notes: the keyboard's own dictation key. bugsTweet never handles audio.
Every report also carries, without any typing: the screen name, the taps before the bug, a filmstrip of the last 30 seconds, phone settings (text size, dark mode, language), phone model and system, app version, recent app errors, the file and line of each widget, a draft test that replays the taps, and a check for duplicate reports.
Where reports go #
- GitHub (
GitHubDelivery): one issue per report, labeledfrom-bugsTweet. Screenshots are stored on abugs-tweet-mediabranch of the same repository and linked from the issue. Sending starts nothing else: you decide when to hand an issue to a coding agent. - Share sheet (
ShareDelivery): the screenshot and report text go to any app the tester picks. Used by itself when no GitHub key is given. - Your own place: implement
ReportDelivery.
The GitHub key, and why reports get their own repository #
The key is built into the tester app, so anyone who has the tester app can read it. Keep the damage small:
-
Create a private repository that holds only reports, for example
my-app-reports, with a README (GitHub cannot add the screenshots branch to an empty repository). Never point bugsTweet at your code repository. -
Create a fine-grained personal access token for that one reports repository only, with Issues: read and write and Contents: read and write, and a short expiry.
-
Pass the key at build time, never in a file in git:
--dart-define=BUGS_TWEET_GITHUB_TOKEN=github_pat_.... -
Create the labels once in the reports repository:
gh label create from-bugsTweet --color 0B7A6E gh label create "bugsTweet: in progress" --color 2FBFA9 gh label create "bugsTweet: pending" --color F2B84B
When a test period ends, or a tester build reaches someone it should not, delete the key on GitHub. That build can then no longer send.
Optional: the relay (no GitHub key in the app) #
A small Cloudflare Worker that you deploy for free holds the GitHub key. The
tester app holds only a send code, which can do nothing but bugsTweet's own
calls in one reports repository. Setup steps:
relay/README.md.
In .bugstweet/tester.dart, use:
GitHubDelivery.relay(
url: 'https://bugs-tweet-relay.<you>.workers.dev',
sendCode: const String.fromEnvironment('BUGS_TWEET_SEND_CODE'),
repo: 'me/my-app-reports',
codeRepo: 'me/my-app',
)
Hosted: the bugsTweet service (no GitHub key, no relay) #
The bugsTweet service sends the reports for you. The tester app holds only a
project key, which works for one reports repository and only for
bugsTweet's own calls. The issues are written by bugsTweet[bot], and the
labels are made for you.
- Create a private reports repository, with a README, as above.
- Open api.bugstweet.com, sign in with GitHub and install the bugsTweet app on that reports repository only.
- Click New project, pick the repository, and copy the key. It is shown once.
- Run
dart run bugstweet init --hosted(or change.bugstweet/tester.darttoGitHubDelivery.hosted). It adds.bugstweet/keyto.gitignore. - Paste the key into
.bugstweet/key(one line). - Build as usual:
dart run bugstweet build apk. bugstweet puts the key into the tester build; there is no key in the command.
GitHubDelivery.hosted(
key: const String.fromEnvironment('BUGS_TWEET_KEY'),
repo: 'me/my-app-reports',
codeRepo: 'me/my-app',
)
If the key is lost or reaches someone it should not, click New key: the old key stops working at once, and the next build uses the new one.
The tester file #
dart run bugstweet init writes .bugstweet/tester.dart. It is plain Dart, so you
can change it freely. Common settings in BugsTweetConfig:
delivery: where reports go (see above).redact: cleans text before a report leaves the phone, for exampleredact: (text) => text.replaceAll(RegExp(r'[\w.+-]+@[\w-]+\.[\w.]+'), '[email]').readFirst: which doc a coding agent should read first for a report.autoBlurTextFields: blur every text field in screenshots (off by default; password fields are always blurred).shakeToReport,screenshotToReport: turn those off.reproTestApp,reproTestImport: let the draft test start your app.appState: a few facts your app knows at the moment of a capture, so the agent does not have to guess, for exampleappState: () => {'signed in': auth.signedIn, 'cart items': cart.length}. Each value is sent as text, withredactapplied.askBeforeRecording: on by default in release and profile builds. The first time a tester build opens, a card asks the tester before small pictures of the last 30 seconds and the last 30 taps are kept; a switch in the tray changes the answer later. Set it tofalseonly for your own builds.logoColors: the colors of the bugsTweet logo, which is the capture button. Purple by default;BugsTweetLogoColors.teal,.blueor.coral, or seven colors of your own withBugsTweetLogoColors(face: ..., light: ..., shadow: ..., trim: ..., beak: ..., eyes: ..., shine: ...).speedTest: on by default. When reports are sent, the phone downloads about 1 MB from Cloudflare's public speed test (speed.cloudflare.com) and each report says how fast it came. SetspeedTest: falseto skip it.
Every report also says the phone's condition when the picture was taken: network (Wi-Fi, mobile data, offline, VPN), the phone's own speed estimate (Android), battery level, charging and power saving, free storage and memory, and heat. None of it asks the tester for a permission.
To blur a part of the screen in every screenshot, wrap it in
BugsTweetPrivate from the tester file, for example through a hook your app
already has. Your store code never needs to import bugsTweet.
Before each tester build, bugsTweet writes .bugstweet/widget_map.g.dart
into the copy, so reports name the file and line of each widget. It never
appears in your app.
For the coding agent #
Each issue ends with the steps an agent follows: find the cause, write back Actual, Expected and the proposed fix, wait for the tester to agree, then fix, test, and say what is done and what is not. AGENT.md has the details, including how to fetch the screenshots.
Uninstall #
In your app's folder:
-
Delete this app's tester copy from the cache folder:
dart run bugstweet clean -
Remove the tool:
flutter pub remove bugstweet -
Delete the
.bugstweetfolder.
There is nothing else to undo: bugsTweet never changed your code or your platform files. If you set up GitHub, you can also delete the reports repository and the key.
Limits #
- Captures what Flutter draws. Platform views (maps, web views) show blank.
- Widget names in reports come from class names, so
--obfuscatebuilds lose them. dart run bugstweet runstarts the app from the copy, so hot reload does not see edits you make in your app while it runs. Stop and run again.
License #
MIT. See LICENSE.