@@ -68,7 +68,7 @@ part 'tool_handlers/bug_report_handlers.dart';
6868part 'tool_handlers/fixture_handlers.dart' ;
6969part 'tool_handlers/explore_handlers.dart' ;
7070
71- const String currentVersion = '0.9.25 ' ;
71+ const String currentVersion = '0.9.26 ' ;
7272
7373/// Session information for multi-session support
7474class SessionInfo {
@@ -108,9 +108,18 @@ Future<void> runServer(List<String> args) async {
108108 // Acquire lock to prevent multiple instances
109109 final lockFile = await _acquireLock ();
110110 if (lockFile == null ) {
111- stderr.writeln ('ERROR: Another flutter-skill server is already running.' );
112- stderr.writeln (
113- 'If you believe this is an error, delete: ~/.flutter_skill.lock' );
111+ final home = Platform .environment['HOME' ] ??
112+ Platform .environment['USERPROFILE' ] ??
113+ '~' ;
114+ _printCliError (
115+ 'Another flutter-skill server is already running' ,
116+ 'A lock file prevents starting a second instance.' ,
117+ fixes: [
118+ 'Stop the existing server first' ,
119+ 'Or delete the stale lock: rm "$home /.flutter_skill.lock"' ,
120+ 'On Windows: del "%USERPROFILE%\\ .flutter_skill.lock"' ,
121+ ],
122+ );
114123 exit (1 );
115124 }
116125
@@ -331,7 +340,25 @@ class FlutterMcpServer {
331340 listener.onClientDisconnected = () {
332341 stderr.writeln ('Browser client disconnected from bridge listener' );
333342 };
334- await listener.start (port);
343+ try {
344+ await listener.start (port);
345+ } on SocketException catch (e) {
346+ if (e.message.contains ('Address already in use' ) ||
347+ e.osError? .errorCode == 98 ||
348+ e.osError? .errorCode == 48 ) {
349+ _printCliError (
350+ 'Port $port is already in use' ,
351+ 'Another process is already using this port.' ,
352+ fixes: [
353+ 'Run: lsof -i :$port ' ,
354+ 'Kill the process using that port' ,
355+ 'Or start with a different port: flutter-skill --bridge-port=9090' ,
356+ ],
357+ );
358+ exit (1 );
359+ }
360+ rethrow ;
361+ }
335362 _webBridgeListener = listener;
336363 stderr.writeln ('Bridge listener started on ws://127.0.0.1:$port ' );
337364 }
@@ -1037,11 +1064,30 @@ class FlutterMcpServer {
10371064 /// Export recorded steps as Jest test
10381065 }
10391066
1067+ // ==================== CLI Error Formatting ====================
1068+
1069+ /// Print a structured, actionable CLI error to stderr.
1070+ void _printCliError (String title, String detail, {List <String > fixes = const []}) {
1071+ stderr.writeln ('' );
1072+ stderr.writeln ('❌ $title ' );
1073+ stderr.writeln ('' );
1074+ stderr.writeln (' $detail ' );
1075+ if (fixes.isNotEmpty) {
1076+ stderr.writeln ('' );
1077+ stderr.writeln ('🔧 Suggested Fixes:' );
1078+ for (final fix in fixes) {
1079+ stderr.writeln (' • $fix ' );
1080+ }
1081+ }
1082+ stderr.writeln ('' );
1083+ }
1084+
10401085// ==================== Lock Management ====================
10411086
10421087/// Acquire a lock file to prevent multiple server instances
10431088Future <File ?> _acquireLock () async {
1044- final home = Platform .environment['HOME' ];
1089+ final home = Platform .environment['HOME' ] ??
1090+ Platform .environment['USERPROFILE' ];
10451091 if (home == null ) return null ;
10461092
10471093 final lockFile = File ('$home /.flutter_skill.lock' );
0 commit comments