diff --git a/.github/PROJECT_STRUCTURE.md b/.github/PROJECT_STRUCTURE.md
new file mode 100644
index 0000000..6f626ba
--- /dev/null
+++ b/.github/PROJECT_STRUCTURE.md
@@ -0,0 +1,121 @@
+# SRunner Project Structure
+
+## Overview
+SRunner is a C# CLI application to run configured services and stacks, built with Terminal.GUI, .NET 10, and System.CommandLine.
+
+## Project Structure
+
+```
+SRunner/
+├── .github/ # GitHub configuration and AI instructions
+│ └── PROJECT_STRUCTURE.md
+├── src/ # Source code directory
+│ ├── Core/ # Core business logic (non-UI)
+│ │ └── ServiceRunner.cs # Service models and management logic
+│ └── Cli/ # Command-line interface (entry point)
+│ ├── Program.cs # Entry point with System.CommandLine
+│ ├── InteractiveUI.cs # Terminal.GUI interactive interface
+│ └── Cli.csproj # CLI project file
+├── SRunner.slnx # Solution file
+├── .gitignore # Git ignore file
+└── README.md # Project documentation
+```
+
+## Key Technologies
+
+- **Framework**: .NET 10
+- **UI Library**: Terminal.Gui 1.19.0
+- **CLI Framework**: System.CommandLine 2.0.0-beta4.22272.1 (Note: Using stable beta4 version as it has better compatibility with the current .NET version)
+- **Language**: C# with nullable reference types enabled
+
+## Architecture
+
+### Core Project (`src/Core/`)
+- **Purpose**: Contains business logic and models independent of the user interface
+- **Components**:
+ - `ServiceRunner.cs`: Contains both ServiceConfig data model and ServiceRunner management logic
+- **Target Framework**: net10.0
+- **Type**: Class Library
+
+### Cli Project (`src/Cli/`)
+- **Purpose**: Entry point and user interface implementation
+- **Components**:
+ - `Program.cs`: Main entry point using System.CommandLine for argument parsing
+ - `InteractiveUI.cs`: Terminal.GUI-based interactive interface
+- **Target Framework**: net10.0
+- **Type**: Console Application
+- **Dependencies**:
+ - Core project reference
+ - Terminal.Gui package
+ - System.CommandLine package
+
+## Usage
+
+### Build the Project
+```bash
+dotnet build
+```
+
+### Run the CLI
+```bash
+# Non-interactive mode
+dotnet run --project src/Cli
+
+# Interactive mode with Terminal.GUI
+dotnet run --project src/Cli -- --interactive
+# or
+dotnet run --project src/Cli -- -i
+```
+
+## Development Guidelines
+
+### Adding New Features
+
+1. **Core Logic**: Add new business logic to `src/Core/`
+ - Keep UI-independent
+ - Follow existing patterns
+ - Add models and services as needed
+
+2. **UI Features**: Add new UI features to `src/Cli/`
+ - UI code goes in `InteractiveUI.cs` or new UI classes
+ - CLI argument handling goes in `Program.cs`
+
+3. **Dependencies**:
+ - Keep Core project minimal with no UI dependencies
+ - UI packages only in Cli project
+
+### Code Style
+- Use nullable reference types
+- Follow C# naming conventions
+- Keep methods focused and single-purpose
+- Add XML documentation comments for public APIs
+
+### Project References
+- Cli project references Core project
+- Core project has no dependencies on Cli
+
+## Command-Line Options
+
+- `--interactive` or `-i`: Launch the Terminal.GUI interactive interface
+- Without flags: Shows basic help information
+
+## Interactive Interface Features
+
+The Terminal.GUI interface provides:
+- Service list view
+- Add new services with dialog
+- Remove services with confirmation
+- View service details
+- Menu bar with File and Help options
+- Keyboard shortcuts for all actions
+
+## Future Enhancements
+
+Potential areas for expansion:
+- Actual service execution (start/stop processes)
+- Service status monitoring
+- Configuration file persistence (JSON/YAML)
+- Logging and diagnostics
+- Service dependency management
+- Multi-stack support
+- Environment variable management
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b3b2d80
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,60 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio cache/options directory
+.vs/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NuGet
+*.nupkg
+**/packages/*
+!**/packages/build/
+
+# Build folders
+**/bin/
+**/obj/
+
+# Rider
+.idea/
+*.sln.iml
+
+# User-specific files
+*.rsuser
+
+# Mono Auto Generated Files
+mono_crash.*
+
+# Windows thumbnail cache
+Thumbs.db
+
+# macOS
+.DS_Store
+
+# VS Code
+.vscode/
diff --git a/README.md b/README.md
index 60c8760..aa595f3 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,81 @@
# SRunner
CLI application to run configured services and stacks
+
+## Overview
+
+SRunner is a C# CLI application built with .NET 10, Terminal.GUI, and System.CommandLine. It provides both a command-line interface and an interactive Terminal UI for managing and running configured services and stacks.
+
+## Features
+
+- **Interactive Terminal UI**: Launch a full-featured Terminal.GUI interface with `--interactive` flag
+- **Service Management**: Add, remove, and view configured services
+- **Modern Architecture**: Clean separation between Core business logic and CLI interface
+- **.NET 10**: Built on the latest .NET framework
+
+## Project Structure
+
+```
+SRunner/
+├── src/
+│ ├── Core/ # Business logic (non-UI)
+│ │ └── ServiceRunner.cs
+│ └── Cli/ # CLI and UI
+│ ├── Program.cs
+│ └── InteractiveUI.cs
+├── .github/
+│ └── PROJECT_STRUCTURE.md # Detailed project documentation
+└── SRunner.slnx
+```
+
+## Building
+
+```bash
+dotnet build
+```
+
+## Usage
+
+### Non-Interactive Mode
+
+```bash
+dotnet run --project src/Cli
+```
+
+This displays basic information about the CLI.
+
+### Interactive Mode
+
+```bash
+dotnet run --project src/Cli -- --interactive
+# or
+dotnet run --project src/Cli -- -i
+```
+
+This launches the Terminal.GUI interactive interface where you can:
+- View configured services
+- Add new services
+- Remove existing services
+- View service details
+- Navigate using keyboard shortcuts
+
+### Help
+
+```bash
+dotnet run --project src/Cli -- --help
+```
+
+## Technologies
+
+- **.NET 10.0**: Latest .NET framework
+- **Terminal.Gui 1.19.0**: Cross-platform Terminal UI toolkit
+- **System.CommandLine 2.0.0-beta4**: Command-line parsing
+- **C# 13**: With nullable reference types enabled
+
+## Development
+
+See [.github/PROJECT_STRUCTURE.md](.github/PROJECT_STRUCTURE.md) for detailed development guidelines and architecture documentation.
+
+## License
+
+See [LICENSE](LICENSE) file for details.
+
diff --git a/SRunner.slnx b/SRunner.slnx
new file mode 100644
index 0000000..0fe02c5
--- /dev/null
+++ b/SRunner.slnx
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/Cli/Cli.csproj b/src/Cli/Cli.csproj
new file mode 100644
index 0000000..85b3ade
--- /dev/null
+++ b/src/Cli/Cli.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/src/Cli/InteractiveUI.cs b/src/Cli/InteractiveUI.cs
new file mode 100644
index 0000000..d8f3c84
--- /dev/null
+++ b/src/Cli/InteractiveUI.cs
@@ -0,0 +1,300 @@
+using Terminal.Gui;
+using Core;
+
+namespace Cli;
+
+///
+/// Interactive Terminal.GUI interface for SRunner
+///
+public class InteractiveUI
+{
+ private readonly ServiceRunner _serviceRunner;
+ private ListView? _servicesListView;
+ private List _serviceNames = new();
+
+ public InteractiveUI()
+ {
+ _serviceRunner = new ServiceRunner();
+ InitializeSampleServices();
+ }
+
+ private void InitializeSampleServices()
+ {
+ // Add some sample services for demonstration
+ _serviceRunner.AddService(new ServiceConfig
+ {
+ Name = "Web Server",
+ Command = "dotnet run",
+ WorkingDirectory = "/app/web",
+ AutoStart = true
+ });
+
+ _serviceRunner.AddService(new ServiceConfig
+ {
+ Name = "API Service",
+ Command = "npm start",
+ WorkingDirectory = "/app/api",
+ AutoStart = false
+ });
+
+ _serviceRunner.AddService(new ServiceConfig
+ {
+ Name = "Database",
+ Command = "docker-compose up",
+ WorkingDirectory = "/app/database",
+ AutoStart = true
+ });
+
+ UpdateServiceList();
+ }
+
+ private void UpdateServiceList()
+ {
+ _serviceNames = _serviceRunner.Services.Select(s => s.Name).ToList();
+ }
+
+ public void Run()
+ {
+ Application.Init();
+
+ try
+ {
+ var top = Application.Top;
+
+ // Create main window
+ var win = new Window("SRunner - Service Manager")
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill(),
+ Height = Dim.Fill()
+ };
+
+ // Create menu bar
+ var menu = new MenuBar(new MenuBarItem[]
+ {
+ new MenuBarItem("_File", new MenuItem[]
+ {
+ new MenuItem("_Quit", "Exit SRunner", () => Application.RequestStop())
+ }),
+ new MenuBarItem("_Help", new MenuItem[]
+ {
+ new MenuItem("_About", "About SRunner", () => ShowAbout())
+ })
+ });
+
+ top.Add(menu);
+
+ // Create label
+ var label = new Label("Configured Services:")
+ {
+ X = 1,
+ Y = 1,
+ Width = Dim.Fill() - 2,
+ Height = 1
+ };
+ win.Add(label);
+
+ // Create services list
+ _servicesListView = new ListView(_serviceNames)
+ {
+ X = 1,
+ Y = 2,
+ Width = Dim.Fill() - 2,
+ Height = Dim.Fill() - 6
+ };
+ win.Add(_servicesListView);
+
+ // Create buttons
+ var addButton = new Button("_Add Service")
+ {
+ X = 1,
+ Y = Pos.Bottom(_servicesListView) + 1
+ };
+ addButton.Clicked += OnAddService;
+ win.Add(addButton);
+
+ var removeButton = new Button("_Remove Service")
+ {
+ X = Pos.Right(addButton) + 2,
+ Y = Pos.Bottom(_servicesListView) + 1
+ };
+ removeButton.Clicked += OnRemoveService;
+ win.Add(removeButton);
+
+ var detailsButton = new Button("_Details")
+ {
+ X = Pos.Right(removeButton) + 2,
+ Y = Pos.Bottom(_servicesListView) + 1
+ };
+ detailsButton.Clicked += OnShowDetails;
+ win.Add(detailsButton);
+
+ var quitButton = new Button("_Quit")
+ {
+ X = Pos.Right(detailsButton) + 2,
+ Y = Pos.Bottom(_servicesListView) + 1
+ };
+ quitButton.Clicked += () => Application.RequestStop();
+ win.Add(quitButton);
+
+ top.Add(win);
+
+ Application.Run();
+ }
+ finally
+ {
+ Application.Shutdown();
+ }
+ }
+
+ private void OnAddService()
+ {
+ var dialog = new Dialog("Add Service", 60, 15);
+
+ var nameLabel = new Label("Name:")
+ {
+ X = 1,
+ Y = 1
+ };
+ dialog.Add(nameLabel);
+
+ var nameField = new TextField("")
+ {
+ X = Pos.Right(nameLabel) + 1,
+ Y = 1,
+ Width = Dim.Fill() - 2
+ };
+ dialog.Add(nameField);
+
+ var commandLabel = new Label("Command:")
+ {
+ X = 1,
+ Y = 3
+ };
+ dialog.Add(commandLabel);
+
+ var commandField = new TextField("")
+ {
+ X = 1,
+ Y = 4,
+ Width = Dim.Fill() - 2
+ };
+ dialog.Add(commandField);
+
+ var workDirLabel = new Label("Working Directory:")
+ {
+ X = 1,
+ Y = 6
+ };
+ dialog.Add(workDirLabel);
+
+ var workDirField = new TextField("")
+ {
+ X = 1,
+ Y = 7,
+ Width = Dim.Fill() - 2
+ };
+ dialog.Add(workDirField);
+
+ var okButton = new Button("OK")
+ {
+ X = Pos.Center() - 10,
+ Y = Pos.Bottom(dialog) - 4,
+ IsDefault = true
+ };
+ okButton.Clicked += () =>
+ {
+ var name = nameField.Text?.ToString() ?? "";
+ var command = commandField.Text?.ToString() ?? "";
+ var workDir = workDirField.Text?.ToString() ?? "";
+
+ if (!string.IsNullOrWhiteSpace(name) && !string.IsNullOrWhiteSpace(command))
+ {
+ try
+ {
+ _serviceRunner.AddService(new ServiceConfig
+ {
+ Name = name,
+ Command = command,
+ WorkingDirectory = workDir,
+ AutoStart = false
+ });
+ UpdateServiceList();
+ if (_servicesListView != null)
+ {
+ _servicesListView.SetSource(_serviceNames);
+ }
+ Application.RequestStop();
+ }
+ catch (InvalidOperationException ex)
+ {
+ MessageBox.ErrorQuery("Error", ex.Message, "OK");
+ }
+ }
+ else
+ {
+ MessageBox.ErrorQuery("Error", "Name and Command are required!", "OK");
+ }
+ };
+ dialog.AddButton(okButton);
+
+ var cancelButton = new Button("Cancel");
+ cancelButton.Clicked += () => Application.RequestStop();
+ dialog.AddButton(cancelButton);
+
+ Application.Run(dialog);
+ }
+
+ private void OnRemoveService()
+ {
+ if (_servicesListView?.SelectedItem >= 0 && _servicesListView.SelectedItem < _serviceNames.Count)
+ {
+ var serviceName = _serviceNames[_servicesListView.SelectedItem];
+ var result = MessageBox.Query("Confirm", $"Remove service '{serviceName}'?", "Yes", "No");
+
+ if (result == 0)
+ {
+ _serviceRunner.RemoveService(serviceName);
+ UpdateServiceList();
+ _servicesListView.SetSource(_serviceNames);
+ }
+ }
+ else
+ {
+ MessageBox.ErrorQuery("Error", "Please select a service to remove", "OK");
+ }
+ }
+
+ private void OnShowDetails()
+ {
+ if (_servicesListView?.SelectedItem >= 0 && _servicesListView.SelectedItem < _serviceNames.Count)
+ {
+ var serviceName = _serviceNames[_servicesListView.SelectedItem];
+ var service = _serviceRunner.GetService(serviceName);
+
+ if (service != null)
+ {
+ var details = $"Name: {service.Name}\n" +
+ $"Command: {service.Command}\n" +
+ $"Working Directory: {service.WorkingDirectory}\n" +
+ $"Auto Start: {service.AutoStart}";
+
+ MessageBox.Query("Service Details", details, "OK");
+ }
+ }
+ else
+ {
+ MessageBox.ErrorQuery("Error", "Please select a service to view", "OK");
+ }
+ }
+
+ private void ShowAbout()
+ {
+ MessageBox.Query("About SRunner",
+ "SRunner v1.0\n\n" +
+ "CLI application to run configured services and stacks\n\n" +
+ "Built with Terminal.GUI and System.CommandLine",
+ "OK");
+ }
+}
diff --git a/src/Cli/Program.cs b/src/Cli/Program.cs
new file mode 100644
index 0000000..797dddb
--- /dev/null
+++ b/src/Cli/Program.cs
@@ -0,0 +1,43 @@
+using System.CommandLine;
+using Core;
+
+namespace Cli;
+
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ // Create the root command
+ var rootCommand = new RootCommand("SRunner - CLI application to run configured services and stacks");
+
+ // Create the --interactive option
+ var interactiveOption = new Option(
+ name: "--interactive",
+ description: "Launch interactive Terminal.GUI interface");
+ interactiveOption.AddAlias("-i");
+
+ rootCommand.AddOption(interactiveOption);
+
+ // Set the handler for the root command
+ rootCommand.SetHandler((interactive) =>
+ {
+ if (interactive)
+ {
+ LaunchInteractiveMode();
+ }
+ else
+ {
+ Console.WriteLine("SRunner - CLI application to run configured services and stacks");
+ Console.WriteLine("Use --interactive or -i to launch the interactive interface");
+ }
+ }, interactiveOption);
+
+ return await rootCommand.InvokeAsync(args);
+ }
+
+ static void LaunchInteractiveMode()
+ {
+ var ui = new InteractiveUI();
+ ui.Run();
+ }
+}
diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj
new file mode 100644
index 0000000..b760144
--- /dev/null
+++ b/src/Core/Core.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/src/Core/ServiceRunner.cs b/src/Core/ServiceRunner.cs
new file mode 100644
index 0000000..13254ed
--- /dev/null
+++ b/src/Core/ServiceRunner.cs
@@ -0,0 +1,67 @@
+namespace Core;
+
+///
+/// Represents a service configuration
+///
+public class ServiceConfig
+{
+ private string _name = string.Empty;
+ private string _command = string.Empty;
+
+ public string Name
+ {
+ get => _name;
+ set
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ throw new ArgumentException("Service name cannot be empty", nameof(value));
+ _name = value;
+ }
+ }
+
+ public string Command
+ {
+ get => _command;
+ set
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ throw new ArgumentException("Service command cannot be empty", nameof(value));
+ _command = value;
+ }
+ }
+
+ public string WorkingDirectory { get; set; } = string.Empty;
+ public bool AutoStart { get; set; }
+}
+
+///
+/// Manages service execution
+///
+public class ServiceRunner
+{
+ private readonly List _services = new();
+
+ public IReadOnlyList Services => _services.AsReadOnly();
+
+ public void AddService(ServiceConfig service)
+ {
+ ArgumentNullException.ThrowIfNull(service);
+
+ if (_services.Any(s => s.Name.Equals(service.Name, StringComparison.OrdinalIgnoreCase)))
+ {
+ throw new InvalidOperationException($"A service with the name '{service.Name}' already exists.");
+ }
+
+ _services.Add(service);
+ }
+
+ public void RemoveService(string name)
+ {
+ _services.RemoveAll(s => s.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
+ }
+
+ public ServiceConfig? GetService(string name)
+ {
+ return _services.FirstOrDefault(s => s.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
+ }
+}