plugin:install
Register a third-party artisan plugin into the consumer project. After adding a
package to pubspec.yaml and running flutter pub get, plugin:install wires
the plugin's ArtisanServiceProvider without requiring manual edits to
bin/artisan.dart or lib/app/_plugins.g.dart.
Basic Usage
dart run fluttersdk_artisan plugin:install
is the plugin's pubspec package name (e.g. magic_logger). The
package must be listed in pubspec.yaml and resolved via flutter pub get
before this command runs.
Pre-flight checks (lib/src/commands/plugin_install_command.dart:197-241):
bin/artisan.dart must exist, the package must appear in pubspec.yaml, and
.dart_tool/package_config.json must contain the package name. Any failure
exits with code 1 before any write is attempted.
Synopsis
Full signature as declared in
lib/src/commands/plugin_install_command.dart:67:
plugin:install
{name : Plugin pubspec package name (e.g. magic_logger)}
{--provider= : Override the auto-derived provider class name}
{--bootstrap-command= : Plugin install sub-command to chain after registration}
{--use-yaml-only : Fail if install.yaml not found instead of falling back to legacy injection}
{--force : Bypass conflict detection (inherited from ArtisanInstallCommand)}
{--dry-run : Print staged ops without writing (inherited)}
{--non-interactive : Skip prompts, use defaults (inherited)}
{--no-bootstrap : Skip post-install bootstrap hint message (inherited)}
The four inherited flags come from ArtisanInstallCommand.baseFlags and are
available on every install command, not only plugin:install.
| Flag | Effect |
|---|---|
--provider=ClassName |
Overrides convention (Mode 3 only). |
--bootstrap-command=cmd |
Overrides the post-registration bootstrap hint (e.g. logger:install). |
--use-yaml-only |
Error out when no install.yaml found; no Mode 2 or Mode 3 fallback. |
--force |
Bypass conflict detection; overwrite user-modified files. |
--dry-run |
Print every staged op without touching disk. |
--non-interactive |
Skip interactive prompts; use defaults. Required in non-TTY environments. |
--no-bootstrap |
Suppress the post-install bootstrap hint message. |
Three Routing Modes
The core of handle dispatches through three distinct code paths
(lib/src/commands/plugin_install_command.dart:130-181). The selection logic
runs in strict priority order: Mode 1 wins when an install.yaml manifest is
present; Mode 2 wins when the canonical scaffold barrel exists; Mode 3 is the
final fallback.
Mode 1: install.yaml Manifest Flow
Condition: resolveInstallYaml(name) returns a non-null path. The resolver
checks first, then
(lib/src/commands/plugin_install_command.dart:110-127).
ManifestParser.parseFile parses the YAML into an InstallManifest.
ManifestInstaller stages InstallOperation objects on an InstallTransaction.
InstallTransaction.commit writes every file to a .tmp path first; on full
success, all .tmp files are renamed atomically. Any .tmp write failure deletes
all staged temps and returns Error(rolledBack: true). On Success, the plugin
entry is written to .artisan/plugins.json and PluginsRefreshCommand regenerates
lib/app/_plugins.g.dart in-process.
Preferred authoring path: declarative, conflict-checked, dry-run-capable, and
partially reversible via plugin:uninstall (V1 limitations; see Reversibility).
Mode 2: Canonical Scaffold Fast Path
Condition: No install.yaml found AND lib/app/_plugins.g.dart exists at
the project root (lib/src/commands/plugin_install_command.dart:172-178).
_registerArtisanProvider runs directly: writes a PluginEntry to
.artisan/plugins.json and regenerates _plugins.g.dart. No bin/artisan.dart
edit occurs. Covers plugins without a manifest whose consumers used
consumer:scaffold. Naming convention: my_plugin maps to
package:my_plugin/cli.dart and class MyPluginArtisanProvider.
Mode 3: Legacy bin/artisan.dart Injection
Condition: No install.yaml found AND lib/app/_plugins.g.dart does not
exist (lib/src/commands/plugin_install_command.dart:180).
import 'package:is appended to/cli.dart'; bin/artisan.dartviaConfigEditor.addImportToFile(idempotent; skips when already present).registry.registerProvider(is inserted after the()); registry.registerAll(auto.commands, ...)anchor, or before theArtisanApplicationconstruction line when the anchor is absent.- Provider class defaults to
; override withArtisanProvider --provider=ClassName.
Retained for backward compatibility with plugins predating the install.yaml
schema. Pass --use-yaml-only to error out instead of reaching this fallback.
Examples
1. Manifest-based plugin install (Mode 1)
flutter pub add magic_logger
dart run fluttersdk_artisan plugin:install magic_logger
Output:
Success: applied 4 ops; record at .artisan/installed/magic_logger.json
Bootstrap with: artisan logger:install
2. Canonical scaffold, no install.yaml (Mode 2)
flutter pub add awesome_plugin
dart run fluttersdk_artisan plugin:install awesome_plugin
Output:
Registered "awesome_plugin" via canonical scaffold (no install.yaml needed).
3. Dry-run preview
dart run fluttersdk_artisan plugin:install magic_logger --dry-run
Output:
DryRun: previewed 4 ops; no changes written
WriteFile lib/config/logger.dart
InjectImport lib/main.dart
InjectAfterPattern lib/app/providers/app_service_provider.dart
AddPubspecAsset assets/logging/
Idempotency
Re-running plugin:install for the same plugin is safe in all three modes.
PluginsRegistryFile.addPlugin replaces by name so .artisan/plugins.json
always contains exactly one entry per plugin. ConfigEditor.addImportToFile
and insertCodeAfterPattern skip injection when the target content is already
present. Mode 3's registerProvider line is also checked before insertion
(unless --force is passed).
All InstallTransaction file writes use .tmp + atomic rename: concurrent
readers never observe partial state. A mid-commit .tmp write failure deletes
all staged temps and returns Error(rolledBack: true) before any rename occurs.
Reversibility
plugin:uninstall is the complement to plugin:install. A Mode 1
manifest commit writes .artisan/installed/ listing every op and
content hash. V1 reversibility is partial: WriteFile ops are fully reversed
(tamper-check then delete); InjectImport, InjectBeforePattern, and
InjectAfterPattern ops are logged as [skipped] (no anchor-bracketed markers
in V1; operator must reverse by hand). Mode 2 and Mode 3 installs write no
record, so plugin:uninstall cannot auto-reverse them. Full bidirectional
reversibility is planned for V1.1.
Related
- plugin:uninstall: removes a plugin registered via
plugin:install, using the.artisan/installed/record..json - plugins:refresh: regenerates
lib/app/_plugins.g.dartfrom.artisan/plugins.jsonwhen the codegen barrel drifts out of sync. - install.yaml schema: full reference for the declarative manifest format consumed by Mode 1.