Migrating from fluttersdk_artisan
How to move an app that runs fluttersdk_artisan onto dowel, what dowel install and dowel doctor detect for you, and the one-time changes an app already on an earlier dowel meets.
On this page
Quick Reference#
Nothing is migrated automatically. The package, the repository, the barrel, the classes, the executable and the state directories all changed name:
| Was | Is |
|---|---|
fluttersdk_artisan, package:fluttersdk_artisan/artisan.dart |
dowel, package:dowel/dowel.dart |
Artisan* classes, runArtisan |
Dowel* classes, runDowel |
.artisan/, ~/.artisan/ |
.dowel/, ~/.dowel/ |
bin/fsa |
bin/dowel |
ARTISAN_* environment variables, FSA_LOCK_TIMEOUT |
DOWEL_*, DOWEL_LOCK_TIMEOUT |
artisan_* MCP tools, the fluttersdk entry in .mcp.json |
dowel_* tools, the dowel entry |
<name>_artisan_provider.dart |
<name>_dowel_provider.dart |
fluttersdk_dusk, fluttersdk_telescope |
tapir, crumbs |
Migration Steps#
Run them in this order. dart run dowel install stops and prints the same list when it finds a generated file from the old package.
-
Swap the packages. Replace the old package, and each plugin with its renamed package (any other plugin needs a release built on dowel):
bash dart pub remove fluttersdk_artisan dart pub add dowel -
Move the state directories. Move
.artisan/to.dowel/and~/.artisan/to~/.dowel/. Then delete.dowel/plugins.json, which namesfluttersdk_*packages and*ArtisanProviderclasses, and everyfluttersdk_*record under.dowel/installed/. Keep the other install records. -
Delete the generated files. Remove
bin/dispatcher.dart,bin/fsa, a hand-injectedbin/artisan.dart,lib/app/_plugins.g.dartandlib/app/commands/_index.g.dart: they import the old package and no longer compile. In your own commands and providers, importpackage:dowel/dowel.dartinstead ofpackage:fluttersdk_artisan/artisan.dartand rename eachArtisan*class toDowel*. -
Scaffold again, then register.
bash dart run dowel install dart run dowel commands:refresh dart run dowel plugin:install <name>installwrites the dispatcher, the two barrels andbin/dowel, but only recreates an empty command index, socommands:refreshregisters the commands underlib/app/commands/again. Runplugin:installonce per plugin: a plugin's bootstrap command needs the new dispatcher. See Gotchas for the conflicts a reinstall can report. -
Replace the MCP entry. Delete the
fluttersdkentry from.mcp.json, then write the new one:bash ./bin/dowel mcp:installmcp:installaddsdowelnext to an existingfluttersdkentry and does not replace it, so the old one has to go first. Reconnect your MCP client afterwards.
What dowel Detects#
dowel install and dowel doctor both look for what the old package left behind, so a half-finished migration is reported instead of failing inside a compiler error.
install stops, with exit code 1, when bin/dispatcher.dart, lib/app/_plugins.g.dart or lib/app/commands/_index.g.dart still imports package:fluttersdk_artisan/, or names a class the old package exported or runArtisan. Each of them would otherwise be skipped as an existing file, and the build that follows would fail on its imports. It prints every leftover it found, then the steps:
This project still has files from fluttersdk_artisan, which dowel replaced in 0.0.21:
bin/dispatcher.dart
bin/fsa
.artisan/
lib/app/commands/greet_command.dart
.dowel/plugins.json
To migrate:
1. dart pub remove fluttersdk_artisan && dart pub add dowel, and swap each plugin for its renamed package (fluttersdk_dusk is tapir, fluttersdk_telescope is crumbs).
2. Move .artisan/ to .dowel/ and ~/.artisan/ to ~/.dowel/, then delete .dowel/plugins.json and the fluttersdk_* records under .dowel/installed/.
3. Delete bin/dispatcher.dart, bin/fsa, bin/artisan.dart, lib/app/_plugins.g.dart and lib/app/commands/_index.g.dart; in your own commands import package:dowel/dowel.dart instead of package:fluttersdk_artisan/artisan.dart and rename Artisan* classes to Dowel*.
4. dart run dowel install, then dart run dowel commands:refresh, then dart run dowel plugin:install <name> for each plugin.
5. Delete the "fluttersdk" entry from .mcp.json, then run: dart run dowel mcp:install.
install --force skips the stop and overwrites the stale files, as it does for any existing file.
doctor prints the same list as a warning that never changes its exit code. It covers more than install does:
| It reports | When |
|---|---|
bin/dispatcher.dart, lib/app/_plugins.g.dart, lib/app/commands/_index.g.dart |
The file imports package:fluttersdk_artisan/ or names a class the old package exported (ArtisanCommand, ArtisanContext, ArtisanServiceProvider and the rest dowel renamed) or runArtisan. |
bin/fsa, bin/artisan.dart |
The file exists. |
.artisan/ |
The directory exists. |
A file under lib/app/commands/ |
It still imports the old package or names one of those classes. A class of your own that only starts with Artisan, such as ArtisanBreadCommand, does not count. |
.dowel/plugins.json |
It names a fluttersdk package or an ArtisanProvider class. |
.dowel/installed/fluttersdk_*.json |
The record belongs to a package that no longer exists. |
It also warns about a fluttersdk entry in .mcp.json, in any shape, and says to delete it and run mcp:install:
WARN: Old MCP entry detected. Your .mcp.json still has the "fluttersdk" server that fluttersdk_artisan wrote, and that package no longer exists. Delete the "fluttersdk" entry from .mcp.json, then run: ./bin/dowel mcp:install (or: dart run dowel mcp:install).
An entry that starts fluttersdk_artisan:mcp gets its own warning, under whatever key it sits:
WARN: Outdated MCP entry detected. The "fluttersdk" server in .mcp.json starts fluttersdk_artisan:mcp, the MCP server of a package that no longer exists, and it never loads your plugins. Delete the "fluttersdk" entry from .mcp.json, then run: ./bin/dowel mcp:install (or: dart run dowel mcp:install).
doctor runs from dowel itself, like install, so it reaches these checks even while the old bin/dispatcher.dart is still there; it lists what remains to delete.
An App Already on an Earlier dowel#
An app that has already moved to dowel meets a few one-time changes when it updates:
| Change | What to do |
|---|---|
An old install record is refused. A record under .dowel/installed/ that has no "version", or that stores absolute stubHashes keys, is not read. |
plugin:install <name> reports a conflict on the record (unsupported-install-record) and plugin:uninstall refuses it with The install record <path> has no "version" (an older dowel wrote it). Re-run the install with --force to rewrite it. Run plugin:install <name> --force once per plugin; see Gotchas. |
.dowel/plugins.json is tracked. Earlier versions gitignored the whole .dowel/ directory, which hid the registry the generated barrel comes from. |
Re-run dart run dowel make:fast-cli (or install): it replaces a bare .dowel/ line in .gitignore with .dowel/cli-bundle*, .dowel/build.stamp* and .dowel/.dowel.lock/. Then commit .dowel/plugins.json, .dowel/installed/ and .dowel/mcp.json. |
plugin:install refuses a missing registry. When lib/app/_plugins.g.dart registers providers but .dowel/plugins.json is missing, an install would regenerate the barrel with one plugin and drop the rest. |
Restore the file from version control, or delete the barrel, run dart run dowel install to write an empty one and install each plugin again. The error says so: ... is missing. Installing now would regenerate the barrel without them. Restore .dowel/plugins.json from version control (commit it; only .dowel/cli-bundle*, .dowel/build.stamp* and .dowel/.dowel.lock/ are cache), then retry. Or delete lib/app/_plugins.g.dart, run dart run dowel install to write an empty one, and run plugin:install again for each plugin. |
The dowel constraint stays as written. install adds dowel: ^<running version> only when the pubspec has no dowel entry; it no longer writes any. |
An unconstrained any entry an earlier install wrote stays too. Replace it with a caret constraint by hand. |
A dependency you declared survives plugin:uninstall. The install record marks each dependency the project already had, and uninstall keeps it. |
Nothing: records written before this change carry no mark, which is one more reason to reinstall once with --force. |
| The Dart SDK floor moved. dowel needs Dart 3.13 or newer. | Update the Dart SDK before dart pub get. |
package:dowel/dowel.dart exports a fixed list of symbols. Most builtin command classes (DoctorCommand, ListCommand, PluginInstallCommand and the rest), McpServer and the internals are no longer exported, and package:args is re-exported only for ArgParser, ArgParserException and ArgResults. |
Import package:args/args.dart for anything else. A plugin that hid DoctorCommand no longer needs the hide. |
The install results are renamed. Success, Conflict, DryRun and Error are InstallSuccess, InstallConflict, InstallDryRun and InstallError. |
Rename them in every switch over a TransactionResult. The old Error hid dart:core's Error; it no longer does. |
Exported classes are final, and the seams are interfaces. DowelInput, PromptDriver, StubDriver and VirtualFs are interface classes. |
A test fake that extended InMemoryFs or DowelRegistry implements VirtualFs or builds the registry instead. |
install.yaml rejects an unknown top-level key. |
Fix the manifest: the error names the key, the manifest path and the running dowel version. |
Gotchas#
| Mistake | Fix |
|---|---|
Running dart run dowel install before dart pub add dowel |
dart run dowel needs the dependency. Step 1 comes first. |
Running ./bin/dowel <command> right after commands:refresh and meeting Unknown command |
commands:refresh does not rebuild the compiled bin/dowel, which still holds the empty index install built. Delete .dowel/build.stamp and the next ./bin/dowel call rebuilds, or use dart run :dispatcher <command>, which compiles fresh. |
Re-adding .mcp.json with mcp:install while the fluttersdk entry is still there |
Both servers then run side by side. Delete the old entry first. |
Reinstalling a plugin and meeting unmanaged-file or unsupported-install-record |
A file a renamed plugin published earlier has no record, and a record kept from before it was versioned is refused. Commit or stash your work, run plugin:install <name> --force, then read git diff: --force switches off every conflict check, so it overwrites a file you edited. --dry-run skips the conflict check and cannot preview this. |
Ignoring the whole .dowel/ directory |
A fresh clone then has no plugins.json, and the next plugin:install refuses to run. Ignore only the cache entries. |
| Hand-editing the generated barrels | They are overwritten. Run commands:refresh and plugins:refresh. |
Related Documentation#
- Installation: requirements and the scaffold
installwrites. - install: what the scaffold writes, how it picks the
dowelconstraint and when it stops. - plugin:install: the conflict check, the install record and its reversal.
- MCP Setup: wire the server into an AI client after the
.mcp.jsonentry changes.