diff --git a/MSIXplainer.Core.Tests/TerminalLauncherTests.cs b/MSIXplainer.Core.Tests/TerminalLauncherTests.cs
new file mode 100644
index 0000000..ad5fb42
--- /dev/null
+++ b/MSIXplainer.Core.Tests/TerminalLauncherTests.cs
@@ -0,0 +1,56 @@
+using MSIXplainer.Services;
+using Xunit;
+
+namespace MSIXplainer.Core.Tests;
+
+public class TerminalLauncherTests
+{
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData(" ")]
+ public void GetLaunchOptions_BlankPath_ReturnsEmpty(string? path)
+ {
+ Assert.Empty(TerminalLauncher.GetLaunchOptions(path));
+ }
+
+ [Fact]
+ public void GetLaunchOptions_OrdersWindowsTerminalFirstThenPowerShellThenCmd()
+ {
+ var options = TerminalLauncher.GetLaunchOptions(@"C:\apps\myapp");
+
+ Assert.Equal(3, options.Count);
+ Assert.Equal("wt.exe", options[0].FileName);
+ Assert.Equal("powershell.exe", options[1].FileName);
+ Assert.Equal("cmd.exe", options[2].FileName);
+ }
+
+ [Fact]
+ public void GetLaunchOptions_WindowsTerminal_EncodesFolderInQuotedDArgument()
+ {
+ var wt = TerminalLauncher.GetLaunchOptions(@"C:\apps\my app").First();
+
+ Assert.Equal("-d \"C:\\apps\\my app\"", wt.Arguments);
+ Assert.Null(wt.WorkingDirectory);
+ }
+
+ [Fact]
+ public void GetLaunchOptions_PowerShellAndCmd_UseWorkingDirectory()
+ {
+ var options = TerminalLauncher.GetLaunchOptions(@"C:\apps\myapp");
+
+ Assert.Equal(@"C:\apps\myapp", options[1].WorkingDirectory);
+ Assert.Equal("-NoExit", options[1].Arguments);
+
+ Assert.Equal(@"C:\apps\myapp", options[2].WorkingDirectory);
+ Assert.Null(options[2].Arguments);
+ }
+
+ [Fact]
+ public void GetLaunchOptions_TrimsTrailingSeparator()
+ {
+ var wt = TerminalLauncher.GetLaunchOptions(@"C:\apps\myapp\").First();
+
+ Assert.Equal("-d \"C:\\apps\\myapp\"", wt.Arguments);
+ }
+}
diff --git a/MSIXplainer.Core/Models/TerminalLaunchOption.cs b/MSIXplainer.Core/Models/TerminalLaunchOption.cs
new file mode 100644
index 0000000..5577ac1
--- /dev/null
+++ b/MSIXplainer.Core/Models/TerminalLaunchOption.cs
@@ -0,0 +1,14 @@
+namespace MSIXplainer.Models;
+
+///
+/// A single terminal launch candidate produced by
+/// . The UI layer maps this onto a
+/// ProcessStartInfo with UseShellExecute = true.
+///
+/// Executable to launch (e.g. wt.exe).
+/// Command-line arguments, or null for none.
+///
+/// Working directory to start in, or null when the folder is already
+/// encoded in (as it is for Windows Terminal).
+///
+public sealed record TerminalLaunchOption(string FileName, string? Arguments, string? WorkingDirectory);
diff --git a/MSIXplainer.Core/Services/TerminalLauncher.cs b/MSIXplainer.Core/Services/TerminalLauncher.cs
new file mode 100644
index 0000000..e9c4072
--- /dev/null
+++ b/MSIXplainer.Core/Services/TerminalLauncher.cs
@@ -0,0 +1,35 @@
+using MSIXplainer.Models;
+
+namespace MSIXplainer.Services;
+
+///
+/// Builds an ordered list of terminal launch candidates for a folder. The UI
+/// layer tries each candidate in turn until one starts, so a machine without
+/// Windows Terminal still falls back to PowerShell and finally cmd.exe (both
+/// of which ship with every supported Windows release).
+///
+public static class TerminalLauncher
+{
+ ///
+ /// Returns the terminals to try, most-preferred first, for opening
+ /// . Returns an empty list when the path is
+ /// null/blank so callers can skip launching entirely.
+ ///
+ public static IReadOnlyList GetLaunchOptions(string? folderPath)
+ {
+ if (string.IsNullOrWhiteSpace(folderPath))
+ return [];
+
+ var path = folderPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
+
+ return
+ [
+ // Windows Terminal opens directly in the folder via -d.
+ new TerminalLaunchOption("wt.exe", $"-d \"{path}\"", WorkingDirectory: null),
+ // PowerShell always ships with Windows; -NoExit keeps the window open.
+ new TerminalLaunchOption("powershell.exe", "-NoExit", WorkingDirectory: path),
+ // cmd.exe is the last-resort fallback.
+ new TerminalLaunchOption("cmd.exe", Arguments: null, WorkingDirectory: path),
+ ];
+ }
+}
diff --git a/MSIXplainer/MainPage.xaml b/MSIXplainer/MainPage.xaml
index 3a0b7a7..84ba79a 100644
--- a/MSIXplainer/MainPage.xaml
+++ b/MSIXplainer/MainPage.xaml
@@ -97,6 +97,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/MSIXplainer/MainPage.xaml.cs b/MSIXplainer/MainPage.xaml.cs
index b61f4e4..75acf8f 100644
--- a/MSIXplainer/MainPage.xaml.cs
+++ b/MSIXplainer/MainPage.xaml.cs
@@ -178,6 +178,38 @@ private void OnInstalledAppClick(object sender, ItemClickEventArgs e)
}
}
+ // Opens the selected app's install folder in a terminal. Tries Windows
+ // Terminal first, then falls back to PowerShell / cmd (see TerminalLauncher).
+ private void OnOpenInstallFolderInTerminal(object sender, RoutedEventArgs e)
+ {
+ if (sender is not FrameworkElement { DataContext: InstalledPackage pkg })
+ return;
+
+ foreach (var option in Services.TerminalLauncher.GetLaunchOptions(pkg.InstallLocation))
+ {
+ try
+ {
+ var psi = new System.Diagnostics.ProcessStartInfo
+ {
+ FileName = option.FileName,
+ UseShellExecute = true,
+ };
+ if (!string.IsNullOrEmpty(option.Arguments))
+ psi.Arguments = option.Arguments;
+ if (!string.IsNullOrEmpty(option.WorkingDirectory))
+ psi.WorkingDirectory = option.WorkingDirectory;
+
+ System.Diagnostics.Process.Start(psi);
+ return;
+ }
+ catch (System.Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(
+ $"[MSIXplainer] Terminal launch '{option.FileName}' failed: {ex.Message}");
+ }
+ }
+ }
+
private void EnterCompareMode()
{
ViewModel.IsCompareMode = true;