Skip to content

CLI Commands

View raw .md

The starter:* commands that scaffold, configure, diagnose, publish and remove the starter inside a Magic application, run through dart run magic:dowel.

On this page

Introduction#

The package has no executable of its own. MagicStarterDowelProvider contributes its commands to the host app's dowel registry, so they run as dart run magic:dowel starter:<command>. dart run magic:dowel plugin:install magic_starter registers the provider and, unless you pass --no-bootstrap, chains starter:install as the plugin's bootstrap command. The chain always forwards --non-interactive, so it writes every feature as false and a later starter:install --features=... skips the config file that exists by then: pass --no-bootstrap and run starter:install yourself (see Installation).

Each command extends DowelCommand (install extends DowelInstallCommand), declares a signature DSL string for its arguments and flags, and implements handle(DowelContext ctx). Every command resolves the host project root through FileHelper.findProjectRoot().

Quick Reference#

Command Purpose
starter:install Scaffolds config, middleware, models, dashboard and routes, and injects the registrations into existing host files.
starter:configure Turns feature flags on in lib/config/magic_starter.dart; --show prints the current state.
starter:doctor Checks the installation; also exposed as the read-only MCP tool starter_doctor.
starter:publish Copies views, layouts, middleware, config or translations into the host for customisation.
starter:uninstall Removes the files and injections starter:install made.

Install Command#

bash
dart run magic:dowel starter:install

The installer validates the host project, asks which features to enable (one yes/no prompt per feature key, unless --features or --non-interactive is passed), generates files from stubs and injects registrations into existing files. It throws when lib/config/app.dart is missing, which means the host has not been scaffolded: run dart run magic:dowel magic:install first.

What It Creates#

Each file is written only when it does not exist yet, or when --force is passed:

File Purpose
lib/config/magic_starter.dart Feature toggle configuration with the selected features enabled
lib/app/middleware/ensure_authenticated.dart Auth guard middleware
lib/app/middleware/redirect_if_authenticated.dart Guest redirect middleware
lib/app/models/user.dart User model
lib/app/models/team.dart Team model (only when teams is enabled)
lib/resources/views/dashboard_view.dart Dashboard view scaffold
lib/routes/app.dart App routes file with the dashboard inside the authenticated layout
lib/app/providers/app_service_provider.dart Created from a stub; an existing file is injected into instead, unless --force replaces it
assets/lang/en.json Translation strings, deep-merged into an existing file

What It Injects#

Beyond creating files, the installer modifies existing host files. Each injection checks for its own marker first, so a re-run adds nothing twice:

  • lib/config/app.dart: adds MagicStarterServiceProvider to the providers list (the install.yaml manifest step).
  • lib/main.dart: adds the magicStarterConfig factory to the config factories, a WindThemeData with a primary palette passed to MagicApplication, and the config/magic_starter.dart and material_ui imports.
  • lib/app/kernel.dart: registers the auth (EnsureAuthenticated) and guest (RedirectIfAuthenticated) middleware aliases.
  • lib/app/providers/route_service_provider.dart: adds registerMagicStarterAuthRoutes() and registerMagicStarterProfileRoutes(), plus the team, notification and support route registrations when those features are enabled.
  • lib/app/providers/app_service_provider.dart: injects MagicStarter.bootstrap(...) (and MagicStarter.useNavigation(...)) into boot() unless the provider already configures the starter.
  • pubspec.yaml: lists assets/lang/en.json under flutter: assets:, and adds magic_notifications when notifications is enabled.
dart
// Injected into lib/config/app.dart
(app) => MagicStarterServiceProvider(app),

// Injected into lib/main.dart
() => magicStarterConfig,

Flags#

Flag Description
--force Overwrite existing generated files and replace app_service_provider.dart instead of injecting into it
--dry-run Stages the transactional writes (config, middleware, models, dashboard, routes, provider registration) without writing them. The in-place edits of main.dart, kernel.dart, the route provider, AppServiceProvider, the translations and pubspec.yaml still run.
--non-interactive Skip the prompts; every feature stays off unless --features names it
--no-bootstrap Inherited from DowelInstallCommand; read by plugin:install, which is what chains this command
--features Comma-separated feature keys; passing it implies --non-interactive
bash
# Interactive installation (prompts for each feature)
dart run magic:dowel starter:install

# Non-interactive with specific features
dart run magic:dowel starter:install --features=teams,notifications,registration

# Force overwrite existing files
dart run magic:dowel starter:install --force

Configure Command#

bash
dart run magic:dowel starter:configure

Updates individual feature flags in lib/config/magic_starter.dart without modifying any other file. It exits with an error when that file does not exist. With no flag it prints a warning and changes nothing.

Show Current Config#

bash
dart run magic:dowel starter:configure --show

Prints every feature key found in the config file with its status:

text
Current Magic Starter Feature Configuration:

Feature        Status
-------------  --------
teams          enabled
social_login   disabled
...

Toggle Features#

bash
# Enable teams and notifications
dart run magic:dowel starter:configure --teams --notifications
CLI Flag Config Key
--teams teams
--social-login social_login
--two-factor two_factor
--sessions sessions
--phone-otp phone_otp
--newsletter newsletter
--notifications notifications
--email-verification email_verification

Each flag is negatable: --teams turns the feature on and --no-teams turns it off, for example dart run magic:dowel starter:configure --no-teams. Flipping a toggle edits the config file and nothing else. A feature that needs scaffolding (teams needs the Team model, the User team accessors, the bootstrap() team callbacks and registerMagicStarterTeamRoutes()) gets it only from starter:install.

Non-Interactive Mode#

The configure command never prompts: it works entirely through flags. That makes it safe for CI pipelines and scripts.

Doctor Command#

bash
dart run magic:dowel starter:doctor

Runs independent checks that the starter was installed correctly. It exits with code 0 when every required check passes and code 1 otherwise. The same report is available to agents as the MCP tool starter_doctor, which takes an optional verbose boolean.

Health Checks#

Check What It Verifies
Magic Framework lib/config/app.dart exists
Starter Config lib/config/magic_starter.dart exists
Provider MagicStarterServiceProvider appears in lib/config/app.dart
Config Factory magicStarterConfig appears in lib/main.dart
Middleware EnsureAuthenticated appears in lib/app/kernel.dart
Routes registerMagicStarterAuthRoutes appears in route_service_provider.dart
Facade app_service_provider.dart calls MagicStarter.bootstrap( or one of the legacy useUserModel, useLocaleOptions, useLogout setters
Translations assets/lang/en.json exists
Billing web origin Reported and required only when billing is enabled: billing.web_origin must be set to a non-empty value

Three more lines appear only when relevant and never fail the command: a Support routes: WARN line when support is enabled but registerMagicStarterSupportRoutes() is not called, a Push external id prefix line when notifications is enabled (the prefix must equal the backend's onesignal.external_id_prefix), and a Published Views list that marks a published view WARN when AppServiceProvider does not register it.

text
Magic Framework: OK
Starter Config: OK
Provider: OK
Config Factory: OK
Middleware: OK
Routes: OK
Facade: FAIL
Translations: OK

Missing Requirements:
  FAIL MagicStarter facade not configured in app_service_provider.dart

1 check(s) failed. Run `dart run magic:dowel starter:install` to fix.

Verbose Mode#

bash
dart run magic:dowel starter:doctor --verbose

Shows the file inspected for each check and the string searched for:

text
Provider: OK
    File: lib/config/app.dart
    Contains: MagicStarterServiceProvider

Publish Command#

bash
dart run magic:dowel starter:publish

Copies plugin source files into the host application for customisation, the starter's equivalent of Laravel's vendor:publish. Without --tag it publishes every group. When views or layouts are published, the command registers them in AppServiceProvider (MagicStarter.view.register(...) and registerLayout(...)) so the registry serves your copy.

A published view or layout compiles where it lands. Its relative imports are rewritten as it is copied: one that the package barrel covers becomes import 'package:magic_starter/magic_starter.dart';, and any other keeps its target through a package:magic_starter/src/... URI. A view or layout that has a registry key needs only the barrel; the support screens' helper widgets, published by the unscoped views tag, still reach into src/, which a host on flutter_lints sees as implementation_imports. Config, middleware and translations are copied unchanged. The registration imports your copy under a prefix, because AppServiceProvider also imports the barrel and the barrel exports the original under the same class name:

dart
import '../../resources/views/starter/auth/magic_starter_login_view.dart' as published;

MagicStarter.view.register('auth.login', () => const published.MagicStarterLoginView());

Publish Tags#

bash
# Publish everything
dart run magic:dowel starter:publish

# Publish only views
dart run magic:dowel starter:publish --tag=views

# One module, one view, one layout
dart run magic:dowel starter:publish --tag=views:auth
dart run magic:dowel starter:publish --tag=views:auth.login
dart run magic:dowel starter:publish --tag=layouts:app

# Overwrite existing files
dart run magic:dowel starter:publish --force
Tag What It Publishes
config lib/config/magic_starter.dart, copied from the package's template, which writes every feature false (starter:install writes the features you chose true). It matters when the host has no config yet; an existing file is skipped, and --force replaces it with this template, which turns every feature off
views View files to lib/resources/views/starter/
layouts app and guest layouts to lib/resources/layouts/starter/
middleware Auth middleware files to lib/app/middleware/
lang Translation file to assets/lang/en.json
all (default) All of the above

A views:<scope> scope is either one registry key (auth.login) or a module prefix (auth) taken from the command's view map; an unknown scope prints an error. Views with no entry in that map, such as the handoff page, are published only by the unscoped views tag.

Uninstall Command#

bash
dart run magic:dowel starter:uninstall

Performs a reverse of the install command. It removes generated files and cleans up injected lines from host config files while keeping user code intact. It lists what it will remove and asks for confirmation first.

What it removes:

  • lib/config/magic_starter.dart
  • The magic_starter dependency from pubspec.yaml
  • The MagicStarterServiceProvider import and registration from lib/config/app.dart
  • The magicStarterConfig import and factory from lib/main.dart
  • The middleware aliases and imports from lib/app/kernel.dart
  • The route imports and registrations from route_service_provider.dart
bash
# Skip the confirmation prompt
dart run magic:dowel starter:uninstall --force

Each removal step is wrapped in error handling, so already-clean projects and partial installations do not crash the command. Afterwards dart format . runs automatically.

Stub Templates#

Generated files are built from stub templates in the assets/stubs/install/ directory:

Stub Output
magic_starter_config.stub lib/config/magic_starter.dart
ensure_authenticated.stub lib/app/middleware/ensure_authenticated.dart
redirect_if_authenticated.stub lib/app/middleware/redirect_if_authenticated.dart
user.stub lib/app/models/user.dart
team.stub lib/app/models/team.dart
app_service_provider.stub lib/app/providers/app_service_provider.dart
dashboard_view.stub lib/resources/views/dashboard_view.dart
app_routes.stub lib/routes/app.dart
en.stub assets/lang/en.json

Stub paths are resolved relative to the plugin package at runtime. The installer searches two locations through getStubSearchPaths(): the plugin's assets/stubs found through .dart_tool/package_config.json, then assets/stubs under the current directory.

Testing CLI Commands#

Commands expose overridable methods so a test can isolate them from the real filesystem:

dart
class TestableInstallCommand extends MagicStarterInstallCommand {
  final String _testRoot;

  TestableInstallCommand(this._testRoot);

  @override
  String getProjectRoot() => _testRoot;

  @override
  List<String> getStubSearchPaths() => [
    '$_testRoot/stubs',
  ];

  @override
  Future<ProcessResult> runDartFormat(String rootPath) async {
    return ProcessResult(0, 0, '', ''); // No-op in tests
  }
}
Method Commands Purpose
getProjectRoot() All Redirect to a temp directory
getStubSearchPaths() Install Point to test stub fixtures
resolveManifestPath() Install Point to a test install.yaml
runDartFormat() Install, Uninstall Skip formatting in tests
getPluginSourceDir() Publish Point to a test plugin source

Gotchas#

Mistake Fix
Running dart run magic_starter install The package has no executable. Every command is dart run magic:dowel starter:<command>.
Running starter:configure --teams on a host installed without teams It only flips 'teams': true in the config; the Team model, the User team accessors, the bootstrap() team callbacks and the team routes are not written, so the app throws on startup. Commit your changes, then re-run dart run magic:dowel starter:install --features=teams --force, adding every other feature you want on, since the config is rewritten from that list. --force also replaces AppServiceProvider and overwrites the translation file.
Expecting starter:configure to toggle every feature Only the flags in the table have one. registration, billing, support and the others are edited in the config file.
Running starter:install before the host is scaffolded It throws when lib/config/app.dart is missing. Run dart run magic:dowel magic:install first.
Expecting --features to leave the prompts on Passing --features skips them, and every key it does not name is written as false.
Expecting starter:uninstall to remove everything AppServiceProvider integrations, middleware files and translation entries stay; it prints them as manual cleanup.
esc

Search index did not load.

No match for "".

Try a class like rounded-lg or a widget like WButton.