using Sdt.Config; using Sdt.Tui; using Spectre.Console; try { // ── Workspace + project discovery ──────────────────────────────────────── var workspaceResult = WorkspaceLoader.FindAndLoad(); var projectResult = ConfigLoader.FindAndLoad(); var cliArgs = Environment.GetCommandLineArgs().Skip(1).ToArray(); var forceInit = cliArgs.Any(a => string.Equals(a, "init", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "--init", StringComparison.OrdinalIgnoreCase)); if (forceInit) { var scan = ConfigBootstrapper.Scan(Directory.GetCurrentDirectory()); var generated = ConfigBootstrapper.BuildDefaultConfig(scan); var path = ConfigBootstrapper.WriteDefaultConfig(scan.ProjectRoot, generated, overwrite: false); AnsiConsole.MarkupLine(Theme.Ok($"Initialized config at {path}")); projectResult = ConfigLoader.FindAndLoad(scan.ProjectRoot); } if (projectResult is null) { AnsiConsole.MarkupLine($"[bold {Theme.Red}]SDT:[/] [{Theme.Amber}]No devtool.json found[/] in current directory or any parent."); var bootstrap = AnsiConsole.Confirm( $"[{Theme.Amber}]Generate a default devtool.json for this project now?[/]", defaultValue: true); if (!bootstrap) { AnsiConsole.MarkupLine(Theme.Faint("Create a devtool.json in your project root to get started.")); return 1; } var scan = ConfigBootstrapper.Scan(Directory.GetCurrentDirectory()); AnsiConsole.MarkupLine(Theme.Faint($"Detected project root: {scan.ProjectRoot}")); if (scan.ToolFamilies.Count > 0) AnsiConsole.MarkupLine(Theme.Faint($"Detected tool families: {string.Join(", ", scan.ToolFamilies)}")); var generated = ConfigBootstrapper.BuildDefaultConfig(scan); var preview = ConfigBootstrapper.ToJson(generated); AnsiConsole.Write(new Panel(Markup.Escape(preview)).Header("Generated devtool.json preview").BorderStyle(Theme.DimStyle)); var confirmWrite = AnsiConsole.Confirm( $"[{Theme.Amber}]Write generated devtool.json to {scan.ProjectRoot}?[/]", defaultValue: true); if (!confirmWrite) return 1; var path = ConfigBootstrapper.WriteDefaultConfig(scan.ProjectRoot, generated, overwrite: false); AnsiConsole.MarkupLine(Theme.Ok($"Created {path}")); projectResult = ConfigLoader.FindAndLoad(scan.ProjectRoot); if (projectResult is null) { AnsiConsole.MarkupLine(Theme.Fail("Generated config could not be reloaded.")); return 1; } } // ── Main run loop (handles workspace project switching) ──────────────── var currentLoaded = projectResult; var (workspace, workspaceRoot) = workspaceResult.HasValue ? (workspaceResult.Value.Config, workspaceResult.Value.WorkspaceRoot) : ((WorkspaceConfig?)null, (string?)null); while (true) { var app = new App(currentLoaded.Config, currentLoaded.ProjectRoot, currentLoaded.Warnings, workspace, workspaceRoot); var result = await app.RunAsync(); if (result.Reason == AppExitReason.Quit) break; // User switched projects — reload config from new root if (result.Reason == AppExitReason.SwitchProject && result.NewProjectRoot is not null) { LoadedProjectConfig? loaded; try { loaded = ConfigLoader.FindAndLoad(result.NewProjectRoot); } catch (Exception ex) { AnsiConsole.MarkupLine(Theme.Fail(ex.Message)); AnsiConsole.MarkupLine(Theme.Faint("Press any key to stay on current project...")); Console.ReadKey(intercept: true); continue; } if (loaded is null) { AnsiConsole.MarkupLine(Theme.Fail($"No devtool.json found at: {result.NewProjectRoot}")); AnsiConsole.MarkupLine(Theme.Faint("Press any key to stay on current project...")); Console.ReadKey(intercept: true); continue; // go back to current app } currentLoaded = loaded; } } return 0; } catch (Exception ex) { var message = ex.Message; var isExpectedMigrationError = ex is InvalidOperationException && message.Contains("Legacy targets-only config detected", StringComparison.OrdinalIgnoreCase); AnsiConsole.MarkupLine(Theme.Fail($"Fatal: {message}")); if (isExpectedMigrationError) { var configPath = ConfigLoader.FindConfigPath(); if (!string.IsNullOrWhiteSpace(configPath)) { var migrate = AnsiConsole.Confirm( $"[{Theme.Amber}]Apply automatic migration now (creates backup + converts targets -> workflows)?[/]", defaultValue: true); if (migrate) { var result = ConfigLoader.ApplyLegacyTargetMigration(configPath, createBackup: true); if (result.Success) { AnsiConsole.MarkupLine(Theme.Ok("Migration applied successfully.")); if (!string.IsNullOrWhiteSpace(result.BackupPath)) AnsiConsole.MarkupLine(Theme.Faint($"Backup: {result.BackupPath}")); AnsiConsole.MarkupLine(Theme.Faint("Run sdt.exe again in strict mode.")); } else { AnsiConsole.MarkupLine(Theme.Fail($"Migration failed: {result.Message}")); } } } } if (!isExpectedMigrationError) AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything); return 1; }