feat: Add reload, restart and logs commands to flame_cli - #4056
Merged
Merged
Conversation
4 of 5 tasks
spydon
added this pull request to stack #4058
September 25, 2026 16:13
spydon
force-pushed
the
feat/flame-cli-run
branch
from
September 28, 2026 12:36
d3f9452 to
00e19b6
Compare
spydon
force-pushed
the
feat/flame-cli-run
branch
2 times, most recently
from
September 30, 2026 13:11
303dbe8 to
1c6c5a3
Compare
…ds to flame_cli (#4055) Stacked on #4054. This adds the commands that let a script or an AI agent control and inspect a running game, so that a snapshot can be taken of a stable, known state: - `flame pause`, `flame resume` and `flame step [--frames N] [--time S]`, backed by the existing game loop service extensions. `step` pauses the game first if it is running. - `flame inspect <id> [--json]` prints the type, parent, child count, debug mode and attributes of a component, through a new `ext.flame_devtools.getComponentInfo` service extension. - `flame set <id> --position x,y --size w,h --angle a --scale s --anchor name --priority p` changes a component and prints it afterwards. `setPositionComponentAttributes` now also accepts `anchor` and `priority` (the latter for any component), validates the value, and responds with JSON instead of the plain string `Success`, which is not valid JSON and made `vm_service` clients wait forever. - `flame debug [on|off] [--component <id>]` shows or changes the debug mode. - `flame overlays [--json]`, `flame overlay show|hide|only <name>`. `getOverlays` now also returns the `active` overlays, and a new `ext.flame_devtools.setOverlay` extension shows or hides a single overlay without touching the others. - `flame tree` now shows the attributes of every component (position, size, angle, scale, anchor, priority, with defaults left out), and gets `--filter <regex>`, `--depth <n>` and `--json`. The `ComponentTreeNode` carries an `attributes` map, which the DevTools extension ignores, and `fromJson` accepts trees without it. All of this is documented on the Flame CLI page, and the service extension reference in the debugging page now lists every extension. I verified every command end to end against a game on the `flutter-tester` device started with `flame run`, including the error paths (unknown component, unknown overlay, invalid values).
flame run now owns the flutter run process: it forwards the terminal input, mirrors the output to .dart_tool/flame/log, and accepts reload and restart requests on a loopback port written to .dart_tool/flame/control_port. The reload and restart commands send those requests and report the line that flutter run prints when it is done, and logs prints the recorded output.
…ne receiving the commands flame run keeps its files in the root of the project so that a game is tied to its project. A RunController only owns the files while the control port file holds its port: a superseded run stops writing to the log and leaves the files alone on exit, and takes the project back when the newer run stops.
…rt from flame run With several games started from the same project, --uri already reaches any of them for the commands that talk to the game, and --port now does the same for reload and restart. flame run prints its control port when it starts so that it is easy to find.
spydon
force-pushed
the
feat/flame-cli-run
branch
from
September 30, 2026 13:14
1c6c5a3 to
31ae7f3
Compare
erickzanardo
approved these changes
Sep 30, 2026
spydon
added a commit
that referenced
this pull request
Sep 30, 2026
Stacked on #4056. This turns the CLI from an observer into something that can play test a game, and gives agents a way to compare snapshots that does not rely on eyeballing two images. - A new `InputConnector` in `flame` registers `ext.flame_devtools.tap`, `drag` and `key`. Taps and drags are delivered through the game's `MultiTapDispatcher` and `MultiDragScaleDispatcher`, so they reach `TapCallbacks` and `DragCallbacks` components with the same propagation as real input, in canvas coordinates (the same as the pixels of a snapshot). Keys go to `onKeyEvent` of a `HasKeyboardHandlerComponents` or `KeyboardEvents` game, with `press`, `down` and `up` actions. Key names are matched against the `LogicalKeyboardKey` names without regard to case, spaces and underscores (`arrowLeft`, `Arrow Left`, `space`, `a`, `digit1`). A missing mixin is reported as an invalid parameters error instead of silently doing nothing. - `flame input tap <x,y>`, `flame input drag <x,y> <x,y> [--steps N]` and `flame input key <name> [--down|--up]` call those. - `getGameSnapshot` takes `world=true` or `rect=x,y,w,h` to render the world directly without the camera, so off screen components are visible. Without a rect the image covers the bounding rectangle of all the position components in the world. `flame snapshot --world` and `--rect` expose it. Snapshotting the `World` component itself used to give a blank 100x100 image, since `World.renderTree` is a no-op, it now renders the world's children the same way. - `flame diff <before.png> <after.png> [--output diff.png] [--threshold N] [--exit-code]` reports the percentage of differing pixels and the bounding rectangle of the change, and can write an image with the changes in red on a dimmed copy of the second image. This adds the `image` package as a dependency of `flame_cli`. Tests cover the input connector with real components (tap position, drag steps, key down and up with the pressed set, the missing mixin messages, key name lookup), world bounds and world snapshots, the diff algorithm and command, and the usage errors of the new commands. Verified end to end on the `flutter-tester` device: a tap toggled a component's color and `flame diff` reported exactly the 200x100 rectangle at 300,250 with exit code 1, a drag and arrow keys moved the component, and `--world` and `--rect` rendered an off screen component.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Stacked on #4055. This closes the edit, reload, check loop for scripts and agents, which
previously still needed a person to press
rin the terminal offlutter run.flame runnow startsflutter runwith piped standard streams instead of inheriting them. ARunControllermirrors the output to the terminal and to.dart_tool/flame/log, forwards theterminal input to
flutter run(putting the terminal in the same raw mode thatflutter runuses, and restoring it afterwards), and listens on a loopback port that it writes to
.dart_tool/flame/control_port. The port and the URI file are removed when the game stops, thelog is kept.
flame reloadandflame restartconnect to that port.flame runpressesrorRontheir behalf and answers with the line that
flutter runprints when it is done (Reloaded ...,Restarted application ...orTry again after fixing the above error(s).), together with theoutput in between. A failed reload prints the compiler errors and exits with
70. Requests areserialized, and there is a two minute timeout in case
flutter runnever answers.flame logs [--lines N] [--follow]prints the recorded output, which includes everything thegame printed and the exceptions it threw, also after a crash.
project_files.dart, with the oldvm_service_uri_file.dartnames kept as thin wrappers.flame runkeeps them in the root ofthe project (the closest
pubspec.yaml), so a running game is tied to its project: differentprojects, including different git worktrees of the same game, are separate instances.
commands go to the one started last. A
RunControllerowns the files whilecontrol_portholds its port; when a newer run takes over it stops writing to the log and leaves the files
alone when it exits, and when the newer run stops it takes the files back, including the URI
it remembered, so the older game becomes reachable again. Any game in the project can also be
addressed explicitly:
--urion the commands that talk to the game, and--portonreloadand
restart, using the control port thatflame runprints when it starts.flame run --helpprints an introduction about what it adds before handing over to the helpof
flutter run.The docs describe the three files, the new commands, how to run
flame runin the background ofan interactive shell (redirect its input from
/dev/null, likeflutter run), and that a hotreload keeps component state so constructor changes need a restart.
Tests cover the controller with a fake process (port file lifecycle,
randRbeing pressed,success, failure with output, timeout, process exiting during a request, unknown requests), the
commands' error paths and
logs. I also verified it end to end on theflutter-testerdevice:flame reloadafter a code change, a broken change reporting the compiler error with exit code70,
flame restart,flame logs, and both commands after the game had stopped. With two gamesfrom the same project,
flame reloadfrom a subdirectory reached the second one and only itsoutput landed in the log, and after stopping it the first game took the project back and
flame reloadreached it again.Checklist
docsand added dartdoc comments with///.examplesordocs.Breaking Change?
Related Issues