tapir:install
One-shot bootstrap that patches a Flutter app's lib/main.dart for tapir and registers the plugin with dowel.
On this page
It runs in two phases, both idempotent:
- Patch
lib/main.dart: inject the imports (includingimport 'package:magic_devtools/tapir.dart';when applicable),WidgetsFlutterBinding.ensureInitialized(), and a!kReleaseMode-gatedTapirPlugin.install()block beforerunApp((debug and profile builds carry tapir, sotapir:perf_runcan measure a profile build; release tree-shakes it). Magic-stack apps additionally wireMagicTapirIntegration.install()afterMagic.init(whenmagic_devtools:is present inpubspec.yamland the resolvedmagic_devtoolsshipslib/tapir.dart. - Chain fastcli setup: best-effort
dart run tapir install(scaffoldsbin/dispatcher.dart+./bin/dowelAOT wrapper) +dart run tapir plugin:install tapir(registersTapirDowelProvider). Both sub-process calls are skipped when their idempotency markers already exist (bin/dispatcher.dartfor the scaffold,.dowel/installed/tapir.jsonfor the plugin record). Failures are swallowed with a warning; the Phase 1 patch always succeeds on its own, so the consumer can still drive tapir viadart run tapir <cmd>even when the chain skipped.
Together, the two phases mean a fresh consumer needs only:
flutter pub add tapir
dart run tapir tapir:install
When Phase 2 succeeded, ./bin/dowel list surfaces every tapir:* command and ./bin/dowel mcp:serve exposes every tapir_* MCP tool. A Phase 2 subprocess that fails does not change the exit code: the installer still exits 0, prints a warning, and leaves the dispatcher or the plugin registration missing, so check that bin/dispatcher.dart exists and ./bin/dowel list shows the tapir:* commands. To retry, run the commands the warning names:
dart run tapir install
dart run tapir plugin:install tapir
Synopsis#
dart run tapir tapir:install
tapir:install accepts no positional arguments and no flags. The command reads lib/main.dart and pubspec.yaml from the current working directory and injects the runtime wiring snippets that fit the detected stack.
Arguments#
tapir:install takes no options. Its two inputs come from the current working directory:
| Input | Purpose |
|---|---|
lib/main.dart |
Target file for snippet injection. |
pubspec.yaml |
Inspected for a magic_devtools: dependency entry. Drives the conditional Magic wiring described under Anchor modes. |
.dart_tool/package_config.json |
Read only when the Magic wiring applies, to find the resolved magic_devtools and check that it ships lib/tapir.dart. The nearest one at or above the project is used, so a pub workspace member reads the workspace root's; an unreadable or invalid one counts as unresolved and gets the warning. |
Returns#
tapir:install returns an integer exit code via Future<int>:
| Exit code | Meaning |
|---|---|
0 |
Success. lib/main.dart was either updated with the required snippets or already contained them. The command prints a tapir:install complete success line, including when a Phase 2 subprocess failed (that case prints a warning above it). |
1 |
lib/main.dart not found at the resolved path. The command prints an error advising the operator to run tapir:install from a Flutter project root. |
No structured payload is emitted. Status flows through DowelOutput.info / success / error so it surfaces with the same ℹ / ✓ / ✗ symbols as every other dowel command.
Anchor modes#
The injector picks one of two anchor strings depending on what lib/main.dart already contains:
- Magic-stack apps (
lib/main.dartcontainsawait Magic.init():TapirPlugin.install()is wired BEFOREMagic.init(so the driver is live during Magic boot. Whenmagic_devtools:is also a pubspec dependency or dev_dependency,import 'package:magic_devtools/tapir.dart';is added andMagicTapirIntegration.install()is injected AFTERMagic.init()(the integration queriesMagic.find<X>()for the form and nav enrichers, which only resolves once the container is ready). Both lines are written only when the resolvedmagic_devtools(from.dart_tool/package_config.json) shipslib/tapir.dart. Amagic_devtoolsrelease that predates tapir does not, and importing it would leave alib/main.dartthat does not compile, so the installer skips the Magic lines and warns: upgrademagic_devtoolsto a release built on tapir first, then rerundart run tapir tapir:install. - Vanilla apps (no
Magic.initanchor):TapirPlugin.install()is wired immediately beforerunApp(.
The installer does not wire Wind. When the consumer uses wind_kit, add Wind.installDebugResolver() by hand inside the same !kReleaseMode block as TapirPlugin.install(). wind_kit ships no tapir-specific integration class; tapir reads wind state through the neutral WindDebugRegistry bridge at snap time, independent of the Magic detection, so a magic-free app with that call still gets the wind metadata block.
An app wired before the guard moved keeps its if (kDebugMode) block: the installer checks for the TapirPlugin.install() and MagicTapirIntegration.install() calls themselves, not for the snippet, so a re-run adds neither a second block nor an unused kReleaseMode import. That app works in debug but registers no ext.tapir.* in a profile build; change the guard to !kReleaseMode by hand to measure one.
The full sub-step list (from the source docblock):
- Add the two required imports (
kReleaseModefrompackage:flutter/foundation.dart, only when a block is about to be injected; thepackage:tapir/tapir.dartbarrel). - Inject
WidgetsFlutterBinding.ensureInitialized()(skip when already present) plus the!kReleaseMode-gated tapir block before the canonical install anchor, unlessTapirPlugin.install()is already called. - When pubspec has
magic_devtools:(dependency or dev_dependency), the resolvedmagic_devtoolsshipslib/tapir.dart, AND main.dart hasawait Magic.init(, injectimport 'package:magic_devtools/tapir.dart';andMagicTapirIntegration.install()AFTER that call, unless it is already called.
Examples#
1. Fresh install in a vanilla Flutter app#
flutter create my_app
cd my_app
flutter pub add tapir
dart run tapir tapir:install
Expected output (illustrative):
ℹ Wiring TapirPlugin into lib/main.dart...
ℹ Scaffolding fastcli (dowel install)...
ℹ Registering tapir as a dowel plugin...
✓ tapir:install complete. Run `dart run tapir <cmd>` to invoke tapir commands (or `./bin/dowel <cmd>` once fastcli is ready).
Diff against lib/main.dart:
import 'package:flutter/foundation.dart' show kReleaseMode;
import 'package:tapir/tapir.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
if (!kReleaseMode) {
TapirPlugin.install();
}
runApp(const MyApp());
}
2. Re-running on an already-installed project#
dart run tapir tapir:install
The injector early-returns on every duplicate snippet. The output still prints tapir:install complete; the file is left untouched.
3. Magic-stack app with the wind resolver#
When pubspec lists magic_devtools: and the resolved release ships lib/tapir.dart, the post-install lib/main.dart looks like this, with the Wind.installDebugResolver() line and its import added by hand:
import 'package:flutter/foundation.dart' show kReleaseMode;
import 'package:tapir/tapir.dart';
import 'package:magic_devtools/tapir.dart';
import 'package:wind_kit/wind_kit.dart'; // added by hand
void main() async {
WidgetsFlutterBinding.ensureInitialized();
if (!kReleaseMode) {
TapirPlugin.install();
Wind.installDebugResolver(); // added by hand
}
await Magic.init(MyApp.new);
if (!kReleaseMode) {
MagicTapirIntegration.install(); // magic_devtools wiring
}
}
Related Documentation#
- Getting Started: Installation: step-by-step walkthrough from
dart pub addto first snapshot. - tapir:doctor: verify the post-install wiring is healthy and the running session can be reached.
- Plugins: Magic integration: full surface of
MagicTapirIntegrationand which enrichers it ships. - Plugins: Wind integration: the six-field
wind:block surfaced through the neutralWindDebugRegistrybridge.