{"id":37056568,"url":"https://github.com/13cyberpunk02/solimuswrapper","last_synced_at":"2026-01-14T06:23:40.826Z","repository":{"id":331093934,"uuid":"1125196615","full_name":"13cyberpunk02/SolimusWrapper","owner":"13cyberpunk02","description":"Simple lightweight command line wrapper","archived":false,"fork":false,"pushed_at":"2026-01-02T22:35:37.000Z","size":172,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-03T02:09:44.070Z","etag":null,"topics":["bash","cmd"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/13cyberpunk02.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-30T09:57:40.000Z","updated_at":"2026-01-02T22:35:40.000Z","dependencies_parsed_at":"2026-01-03T12:02:31.910Z","dependency_job_id":null,"html_url":"https://github.com/13cyberpunk02/SolimusWrapper","commit_stats":null,"previous_names":["13cyberpunk02/solimuswrapper"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/13cyberpunk02/SolimusWrapper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/13cyberpunk02%2FSolimusWrapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/13cyberpunk02%2FSolimusWrapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/13cyberpunk02%2FSolimusWrapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/13cyberpunk02%2FSolimusWrapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/13cyberpunk02","download_url":"https://codeload.github.com/13cyberpunk02/SolimusWrapper/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/13cyberpunk02%2FSolimusWrapper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28412211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","cmd"],"created_at":"2026-01-14T06:23:40.292Z","updated_at":"2026-01-14T06:23:40.818Z","avatar_url":"https://github.com/13cyberpunk02.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SolimusWrapper\n\n[![NuGet](https://img.shields.io/nuget/v/SolimusWrapper.svg)](https://www.nuget.org/packages/SolimusWrapper)\n[![Downloads](https://img.shields.io/nuget/dt/SolimusWrapper.svg)](https://www.nuget.org/packages/SolimusWrapper)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![Build and Test](https://github.com/13cyberpunk02/SolimusWrapper/actions/workflows/dotnet.yml/badge.svg)](https://github.com/13cyberpunk02/SolimusWrapper/actions/workflows/dotnet.yml)\n[![.NET](https://img.shields.io/badge/.NET-10.0-blue)](https://dotnet.microsoft.com/)\n\nЛёгкая и оптимизированная библиотека для работы с CLI процессами в .NET. Простой и интуитивный API для запуска внешних команд с поддержкой перенаправления потоков, таймаутов, повторных попыток, логирования и кросс-платформенной работы.\n\n## ✨ Особенности\n\n- 🚀 **Fluent API** — читаемый и удобный синтаксис\n- 🔒 **Immutable** — потокобезопасные команды\n- ⚡ **Async/Await** — полная асинхронность с CancellationToken\n- 🔄 **Pipes** — перенаправление stdout, stderr, stdin\n- ⏱️ **Таймауты** — автоматическое завершение процессов\n- 🔁 **Retry Logic** — повторные попытки с exponential backoff\n- 🛡️ **Shell Escaping** — защита от инъекций команд\n- 📝 **Structured Logging** — гибкое логирование в консоль и файлы\n- 🌍 **Кросс-платформенность** — Windows, Linux, macOS\n- 📦 **Zero Dependencies** — никаких внешних зависимостей\n- 🎯 **Native AOT** — поддержка trimming\n\n## 📁 Структура проекта\n```\nSolimusWrapper/\n├── README.md                              # Главная документация\n├── LICENSE                                # MIT License\n├── SolimusWrapper.sln                       # Solution файл\n│\n├── src/\n│   └── SolimusWrapper.Core/                      # 📦 Основная библиотека\n│       ├── Command.cs                     # Основной класс\n│       ├── CommandResult.cs               # Результат выполнения\n│       ├── CommandExtensions.cs           # Кросс-платформенные хелперы\n│       ├── PipeTarget.cs                  # Перенаправление вывода\n│       ├── PipeSource.cs                  # Перенаправление ввода\n│       ├── RetryOptions.cs                # Настройки повторов\n│       ├── RetryExecutor.cs               # Логика повторов\n│       ├── ShellEscaper.cs                # Экранирование аргументов\n│       ├── SafeCommand.cs                 # Безопасные команды\n│       ├── Builders/\n│       │   └── CommandBuilder.cs          # Builder pattern\n│       └── Logging/\n│           ├── ICommandLogger.cs          # Интерфейс логгера\n│           ├── LoggingOptions.cs          # Настройки логирования\n│           ├── ConsoleCommandLogger.cs    # Логгер в консоль\n│           ├── FileCommandLogger.cs       # Логгер в файл\n│           ├── CompositeCommandLogger.cs  # Композитный логгер\n│           └── NullCommandLogger.cs       # Пустой логгер\n│\n├── samples/\n│   └── SolimusWrapper.Samples/                 # 🎮 Примеры использования\n│\n└── tests/\n    └── SolimusWrapper.Tests/                # 🧪 Unit-тесты\n```\n\n## 📦 Установка\n```bash\ndotnet add package SolimusWrapper\n```\n\nИли через Package Manager:\n```powershell\nInstall-Package SolimusWrapper\n```\n\n## 🚀 Быстрый старт\n```csharp\nusing SolimusWrapper;\n\n// Простой запуск\nvar result = await Command.Run(\"dotnet\")\n    .WithArguments(\"--version\")\n    .ExecuteAsync();\n\nConsole.WriteLine($\"Exit code: {result.ExitCode}\");\n\n// Получить вывод\nvar output = await Command.Run(\"git\")\n    .WithArguments(\"status\")\n    .ExecuteAndReadOutputAsync();\n\nConsole.WriteLine(output);\n```\n\n## 📖 Основные возможности\n\n### Базовые операции\n```csharp\n// Запуск с аргументами\nawait Command.Run(\"dotnet\")\n    .WithArguments(\"build\", \"-c\", \"Release\")\n    .ExecuteAsync();\n\n// Аргументы как коллекция\nvar args = new[] { \"test\", \"--no-build\", \"--logger\", \"console\" };\nawait Command.Run(\"dotnet\")\n    .WithArguments(args)\n    .ExecuteAsync();\n\n// Получить stdout и stderr отдельно\nvar (stdOut, stdErr) = await Command.Run(\"dotnet\")\n    .WithArguments(\"build\")\n    .ExecuteAndReadAllAsync();\n```\n\n### Перенаправление потоков\n```csharp\n// В StringBuilder\nvar output = new StringBuilder();\nvar errors = new StringBuilder();\n\nawait Command.Run(\"dotnet\")\n    .WithArguments(\"build\")\n    .WithStandardOutputPipe(PipeTarget.ToStringBuilder(output))\n    .WithStandardErrorPipe(PipeTarget.ToStringBuilder(errors))\n    .ExecuteAsync();\n\n// Вывод в реальном времени\nawait Command.Run(\"dotnet\")\n    .WithArguments(\"test\")\n    .WithStandardOutputPipe(PipeTarget.ToDelegate(line =\u003e \n        Console.WriteLine($\"[OUT] {line}\")))\n    .WithStandardErrorPipe(PipeTarget.ToDelegate(line =\u003e \n        Console.WriteLine($\"[ERR] {line}\")))\n    .ExecuteAsync();\n\n// В файл\nawait Command.Run(\"dotnet\")\n    .WithArguments(\"build\", \"-v\", \"detailed\")\n    .WithStandardOutputPipe(PipeTarget.ToFile(\"build.log\"))\n    .ExecuteAsync();\n```\n\n### Передача данных в stdin\n```csharp\n// Из строки\nawait Command.Run(\"grep\")\n    .WithArguments(\"error\")\n    .WithStandardInputPipe(PipeSource.FromString(\"line1\\nerror here\\nline3\"))\n    .ExecuteAsync();\n\n// Из файла\nawait Command.Run(\"cat\")\n    .WithStandardInputPipe(PipeSource.FromFile(\"input.txt\"))\n    .ExecuteAsync();\n\n// Из потока\nusing var stream = File.OpenRead(\"data.bin\");\nawait Command.Run(\"processor\")\n    .WithStandardInputPipe(PipeSource.FromStream(stream))\n    .ExecuteAsync();\n\n// Из байтов\nvar bytes = Encoding.UTF8.GetBytes(\"data\");\nawait Command.Run(\"consumer\")\n    .WithStandardInputPipe(PipeSource.FromBytes(bytes))\n    .ExecuteAsync();\n```\n\n### Рабочая директория и переменные окружения\n```csharp\nawait Command.Run(\"npm\")\n    .WithArguments(\"install\")\n    .WithWorkingDirectory(\"/path/to/project\")\n    .WithEnvironmentVariable(\"NODE_ENV\", \"production\")\n    .WithEnvironmentVariable(\"PORT\", \"3000\")\n    .ExecuteAsync();\n\n// Несколько переменных сразу\nvar envVars = new Dictionary\u003cstring, string?\u003e\n{\n    [\"API_KEY\"] = \"secret\",\n    [\"DEBUG\"] = \"true\"\n};\n\nawait Command.Run(\"myapp\")\n    .WithEnvironmentVariables(envVars)\n    .ExecuteAsync();\n```\n\n### Таймауты и отмена\n```csharp\n// Таймаут\ntry\n{\n    await Command.Run(\"long-process\")\n        .WithTimeout(TimeSpan.FromSeconds(30))\n        .ExecuteAsync();\n}\ncatch (TimeoutException)\n{\n    Console.WriteLine(\"Процесс превысил лимит времени\");\n}\n\n// Отмена через CancellationToken\nusing var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));\n\ntry\n{\n    await Command.Run(\"long-task\")\n        .ExecuteAsync(cts.Token);\n}\ncatch (OperationCanceledException)\n{\n    Console.WriteLine(\"Операция отменена\");\n}\n```\n\n### Обработка кодов выхода\n```csharp\n// По умолчанию выбрасывает исключение при ненулевом коде\ntry\n{\n    await Command.Run(\"failing-command\").ExecuteAsync();\n}\ncatch (CommandExecutionException ex)\n{\n    Console.WriteLine($\"Команда завершилась с кодом: {ex.ExitCode}\");\n}\n\n// Отключить проверку\nvar result = await Command.Run(\"might-fail\")\n    .WithValidation(false)\n    .ExecuteAsync();\n\nif (!result.IsSuccess)\n{\n    Console.WriteLine($\"Exit code: {result.ExitCode}\");\n}\n\n// Callback при завершении\nawait Command.Run(\"task\")\n    .OnExit(exitCode =\u003e Console.WriteLine($\"Finished with: {exitCode}\"))\n    .WithValidation(false)\n    .ExecuteAsync();\n```\n\n---\n\n## 🔁 Retry Logic (Повторные попытки)\n\nАвтоматические повторы при ошибках с поддержкой exponential backoff:\n```csharp\nusing SolimusWrapper;\n\n// Простой retry с настройками по умолчанию\nvar result = await Command.Run(\"curl\")\n    .WithArguments(\"https://api.example.com/data\")\n    .ExecuteWithRetryAsync(RetryOptions.Default);\n\n// Exponential backoff\nvar result = await Command.Run(\"flaky-api\")\n    .ExecuteWithRetryAsync(RetryOptions.Exponential(\n        maxAttempts: 5, \n        initialDelay: TimeSpan.FromMilliseconds(500)));\n\n// Linear retry (постоянная задержка)\nvar result = await Command.Run(\"service\")\n    .ExecuteWithRetryAsync(RetryOptions.Linear(\n        maxAttempts: 3, \n        delay: TimeSpan.FromSeconds(2)));\n\n// Немедленные повторы без задержки\nvar result = await Command.Run(\"quick-check\")\n    .ExecuteWithRetryAsync(RetryOptions.Immediate(maxAttempts: 3));\n```\n\n### Расширенная настройка Retry\n```csharp\nvar result = await Command.Run(\"network-command\")\n    .ExecuteWithRetryAsync(options =\u003e\n    {\n        options.MaxAttempts = 5;\n        options.Delay = TimeSpan.FromSeconds(1);\n        options.BackoffMultiplier = 2.0;        // Exponential: 1s, 2s, 4s, 8s...\n        options.MaxDelay = TimeSpan.FromSeconds(30);  // Максимальная задержка\n        options.UseJitter = true;               // Случайное отклонение для избежания thundering herd\n        \n        // Повторять только при определённых exit codes\n        options.ShouldRetryOnExitCode = code =\u003e code == 1 || code == 2;\n        \n        // Повторять только при определённых исключениях\n        options.ShouldRetry = ex =\u003e ex is CommandExecutionException;\n        \n        // Callback перед каждым повтором\n        options.OnRetry = attempt =\u003e\n        {\n            Console.WriteLine($\"Попытка {attempt.AttemptNumber}/{attempt.MaxAttempts} не удалась.\");\n            Console.WriteLine($\"Причина: {attempt.LastException?.Message ?? $\"Exit code: {attempt.LastExitCode}\"}\");\n            Console.WriteLine($\"Повтор через: {attempt.NextDelay.TotalMilliseconds}ms\");\n        };\n    });\n```\n\n### Retry с получением вывода\n```csharp\nvar output = await Command.Run(\"curl\")\n    .WithArguments(\"-s\", \"https://api.example.com/data\")\n    .ExecuteWithRetryAndReadOutputAsync(RetryOptions.Exponential(3));\n```\n\n---\n\n## 🛡️ Shell Escaping (Защита от инъекций)\n\n### Автоматическое экранирование аргументов\n```csharp\n// Безопасные аргументы — автоматическое экранирование\nvar userInput = \"file with spaces; rm -rf /\";\n\nawait Command.Run(\"cat\")\n    .WithSafeArguments(userInput)  // Безопасно экранируется\n    .ExecuteAsync();\n\n// Несколько аргументов\nawait Command.Run(\"echo\")\n    .WithSafeArguments(\"hello\", \"world; malicious\", \"test\")\n    .ExecuteAsync();\n```\n\n### Утилиты ShellEscaper\n```csharp\nusing SolimusWrapper;\n\n// Ручное экранирование\nvar escaped = ShellEscaper.Escape(\"file; rm -rf /\");\n// Windows: \"file; rm -rf /\"\n// Unix: 'file; rm -rf /'\n\n// Экранирование для конкретной платформы\nvar windowsEscaped = ShellEscaper.EscapeWindows(\"test%PATH%\");\nvar unixEscaped = ShellEscaper.EscapeUnix(\"$HOME/file\");\nvar psEscaped = ShellEscaper.EscapePowerShell(\"it's a test\");\n\n// Проверка на опасные символы\nif (ShellEscaper.ContainsDangerousCharacters(userInput))\n{\n    Console.WriteLine(\"Входные данные содержат опасные символы!\");\n}\n\n// Проверка на паттерны инъекций\nif (ShellEscaper.LooksLikeInjection(userInput))\n{\n    Console.WriteLine(\"Возможная попытка инъекции команды!\");\n}\n\n// Построение безопасной командной строки\nvar cmdLine = ShellEscaper.BuildCommandLine(\"echo\", \"hello\", \"world; rm -rf /\");\n```\n\n### SafeCommand — безопасный запуск с валидацией\n```csharp\nusing SolimusWrapper;\n\n// Валидирует путь и аргументы перед выполнением\nvar command = SafeCommand.RunWithSafeArgs(\"echo\", userInput);\n\n// Выбрасывает исключение при подозрительных паттернах\ntry\n{\n    var cmd = SafeCommand.Shell(\"echo hello \u0026\u0026 rm -rf /\");\n}\ncatch (ArgumentException ex)\n{\n    Console.WriteLine($\"Отклонено: {ex.Message}\");\n}\n\n// Валидация пути к исполняемому файлу\ntry\n{\n    var cmd = SafeCommand.Run(\"../../../etc/passwd\");\n}\ncatch (ArgumentException ex)\n{\n    Console.WriteLine($\"Path traversal заблокирован: {ex.Message}\");\n}\n```\n\n---\n\n## 📝 Structured Logging (Логирование)\n\n### Консольное логирование\n```csharp\nusing SolimusWrapper;\n\n// Простое консольное логирование\nawait Command.Run(\"dotnet\")\n    .WithArguments(\"build\")\n    .WithConsoleLogging()\n    .ExecuteAsync();\n\n// С настройками\nawait Command.Run(\"dotnet\")\n    .WithArguments(\"test\")\n    .WithConsoleLogging(options =\u003e\n    {\n        options.LogCommandStart = true;\n        options.LogCommandEnd = true;\n        options.StandardOutputLevel = CommandLogLevel.Debug;\n        options.StandardErrorLevel = CommandLogLevel.Warning;\n        options.IncludeTimestamp = true;\n        options.TimestampFormat = \"HH:mm:ss.fff\";\n    })\n    .ExecuteAsync();\n```\n\n### Файловое логирование\n```csharp\nusing SolimusWrapper;\nusing SolimusWrapper.Logging;\n\n// Простое файловое логирование\nusing var logger = new FileCommandLogger(\"commands.log\");\n\nawait Command.Run(\"dotnet\")\n    .WithArguments(\"build\")\n    .WithLogger(logger)\n    .ExecuteAsync();\n\n// С настройками\nvar options = new LoggingOptions\n{\n    MaskSensitiveData = true,\n    MaxLineLength = 500,\n    StandardOutputLevel = CommandLogLevel.Information\n};\n\nusing var fileLogger = new FileCommandLogger(\"app.log\", options);\n\nawait Command.Run(\"deploy\")\n    .WithArguments(\"--token\", \"secret123\")\n    .WithLogger(fileLogger)\n    .ExecuteAsync();\n\n// В логе: --token ****\n```\n\n### Маскирование чувствительных данных\n```csharp\nvar options = new LoggingOptions\n{\n    MaskSensitiveData = true,\n    SensitivePatterns = \n    [\n        @\"(?i)(password|pwd|secret|token|key|apikey|api_key)[\\s:=]+\\S+\",\n        @\"(?i)bearer\\s+\\S+\",\n        @\"(?i)basic\\s+\\S+\",\n        @\"--token\\s+\\S+\"  // Кастомный паттерн\n    ]\n};\n\nusing var logger = new FileCommandLogger(\"audit.log\", options);\n\nawait Command.Run(\"curl\")\n    .WithArguments(\"-H\", \"Authorization: Bearer secret_token_123\", \"https://api.example.com\")\n    .WithLogger(logger)\n    .ExecuteAsync();\n\n// В логе: Authorization: Bearer ****\n```\n\n### Композитный логгер (несколько целей)\n```csharp\nusing SolimusWrapper.Logging;\n\n// Логирование одновременно в консоль и файл\nusing var fileLogger = new FileCommandLogger(\"commands.log\");\nvar compositeLogger = new CompositeCommandLogger(\n    new ConsoleCommandLogger(),\n    fileLogger\n);\n\nawait Command.Run(\"important-task\")\n    .WithLogger(compositeLogger)\n    .ExecuteAsync();\n```\n\n### Кастомный логгер\n```csharp\nusing SolimusWrapper.Logging;\n\npublic class SerilogCommandLogger : ICommandLogger\n{\n    private readonly ILogger _logger;\n\n    public SerilogCommandLogger(ILogger logger) =\u003e _logger = logger;\n\n    public void LogCommandStart(CommandStartInfo info)\n        =\u003e _logger.Information(\"Starting command: {Command}\", info.CommandLine);\n\n    public void LogStandardOutput(string line)\n        =\u003e _logger.Debug(\"[stdout] {Line}\", line);\n\n    public void LogStandardError(string line)\n        =\u003e _logger.Warning(\"[stderr] {Line}\", line);\n\n    public void LogCommandEnd(CommandEndInfo info)\n        =\u003e _logger.Information(\"Command completed: {ExitCode} in {Duration}ms\", \n            info.ExitCode, info.Duration.TotalMilliseconds);\n\n    public void LogError(Exception exception)\n        =\u003e _logger.Error(exception, \"Command failed\");\n\n    public void LogRetry(RetryAttempt attempt)\n        =\u003e _logger.Warning(\"Retry {Attempt}/{Max}: {Reason}\", \n            attempt.AttemptNumber, attempt.MaxAttempts, \n            attempt.LastException?.Message ?? $\"Exit code: {attempt.LastExitCode}\");\n}\n\n// Использование\nvar serilogLogger = new SerilogCommandLogger(Log.Logger);\n\nawait Command.Run(\"task\")\n    .WithLogger(serilogLogger)\n    .ExecuteAsync();\n```\n\n### Уровни логирования\n```csharp\npublic enum CommandLogLevel\n{\n    Trace,       // Максимальная детализация\n    Debug,       // Отладочная информация\n    Information, // Общая информация\n    Warning,     // Предупреждения\n    Error,       // Ошибки\n    None         // Отключено\n}\n```\n\n---\n\n## 🔨 CommandBuilder\n\nАльтернативный способ построения команд с условной логикой:\n```csharp\nusing SolimusWrapper.Builders;\n\nvar verbose = true;\nvar configuration = \"Release\";\nvar outputPath = \"./publish\";\n\nvar result = await new CommandBuilder(\"dotnet\")\n    .AddArgument(\"publish\")\n    .AddFlag(\"--no-restore\")\n    .AddFlag(\"-v\", verbose)                                    // Условный флаг\n    .AddArgumentIfNotEmpty(\"-c\", configuration)                // Если не пустой\n    .AddArgumentIfNotEmpty(\"-o\", outputPath)\n    .AddArgumentIf(OperatingSystem.IsLinux(), \"-r\", \"linux-x64\")     // Платформо-зависимый\n    .AddArgumentIf(OperatingSystem.IsWindows(), \"-r\", \"win-x64\")\n    .AddSafeArgument(userProvidedPath)                         // Безопасный аргумент\n    .SetWorkingDirectory(\"/path/to/project\")\n    .SetTimeout(TimeSpan.FromMinutes(10))\n    .SetValidation(true)\n    .UseConsoleLogging()                                       // Логирование\n    .ExecuteAsync();\n```\n\n### CommandBuilder с Retry и Logging\n```csharp\nvar result = await new CommandBuilder(\"curl\")\n    .AddArguments(\"-s\", \"https://api.example.com\")\n    .SetTimeout(TimeSpan.FromSeconds(30))\n    .UseConsoleLogging(options =\u003e\n    {\n        options.MaskSensitiveData = true;\n        options.StandardOutputLevel = CommandLogLevel.Debug;\n    })\n    .ExecuteWithRetryAsync(options =\u003e\n    {\n        options.MaxAttempts = 3;\n        options.Delay = TimeSpan.FromSeconds(1);\n        options.BackoffMultiplier = 2.0;\n    });\n```\n\n### Методы CommandBuilder\n\n| Метод | Описание |\n|-------|----------|\n| `SetTarget(string)` | Устанавливает исполняемый файл |\n| `AddArgument(string)` | Добавляет один аргумент |\n| `AddArgument(string, string)` | Добавляет аргумент с значением |\n| `AddArguments(params string[])` | Добавляет несколько аргументов |\n| `AddArgumentIf(bool, string)` | Условное добавление аргумента |\n| `AddArgumentIf(bool, string, string)` | Условное добавление с значением |\n| `AddArgumentIfNotEmpty(string, string?)` | Добавляет если значение не пустое |\n| `AddFlag(string, bool)` | Добавляет флаг если enabled = true |\n| `AddSafeArgument(string)` | Добавляет с экранированием |\n| `AddSafeArguments(params string[])` | Добавляет несколько с экранированием |\n| `ClearArguments()` | Очищает все аргументы |\n| `SetWorkingDirectory(string)` | Устанавливает рабочую директорию |\n| `SetEnvironmentVariable(string, string?)` | Добавляет переменную окружения |\n| `SetStandardOutput(PipeTarget)` | Перенаправляет stdout |\n| `SetStandardOutput(Action\u003cstring\u003e)` | stdout в делегат |\n| `SetStandardError(PipeTarget)` | Перенаправляет stderr |\n| `SetStandardInput(PipeSource)` | Устанавливает stdin |\n| `SetEncoding(Encoding)` | Устанавливает кодировку |\n| `SetValidation(bool)` | Проверка exit code |\n| `SetTimeout(TimeSpan)` | Устанавливает таймаут |\n| `OnExit(Action\u003cint\u003e)` | Callback при завершении |\n| `SetLogger(ICommandLogger)` | Устанавливает логгер |\n| `UseConsoleLogging()` | Консольное логирование |\n| `UseFileLogging(string)` | Файловое логирование |\n| `Build()` | Создаёт объект Command |\n| `ExecuteAsync()` | Строит и выполняет |\n| `ExecuteWithRetryAsync(RetryOptions)` | Выполняет с повторами |\n\n---\n\n## 🌐 Кросс-платформенные хелперы\n```csharp\nusing SolimusWrapper;\n\n// Shell команда (cmd на Windows, sh на Unix)\nvar result = await CommandExtensions.Shell(\"echo Hello\").ExecuteAndReadOutputAsync();\n\n// Список файлов (dir на Windows, ls на Unix)\nvar files = await CommandExtensions.ListFiles().ExecuteAndReadOutputAsync();\nvar files2 = await CommandExtensions.ListFiles(\"/path/to/dir\").ExecuteAndReadOutputAsync();\n\n// Текущая директория (cd на Windows, pwd на Unix)\nvar pwd = await CommandExtensions.GetCurrentDirectory().ExecuteAndReadOutputAsync();\n\n// Echo\nawait CommandExtensions.Echo(\"Hello, World!\").ExecuteAsync();\n\n// Sleep (timeout на Windows, sleep на Unix)\nawait CommandExtensions.Sleep(5).ExecuteAsync();\n\n// Ping (-n на Windows, -c на Unix)\nawait CommandExtensions.Ping(\"google.com\", count: 4)\n    .WithStandardOutputPipe(PipeTarget.ToDelegate(Console.WriteLine))\n    .ExecuteAsync();\n\n// Переменная окружения\nvar path = await CommandExtensions.GetEnvironmentVariable(\"PATH\").ExecuteAndReadOutputAsync();\n\n// Поиск файлов\nvar found = await CommandExtensions.FindFiles(\"*.cs\", \"/path/to/search\").ExecuteAndReadOutputAsync();\n\n// Проверка существования файла\nvar exists = await CommandExtensions.FileExists(\"myfile.txt\").ExecuteAndReadOutputAsync();\n```\n\n---\n\n## 📊 API Reference\n\n### Command\n\n| Метод | Описание |\n|-------|----------|\n| `Run(string)` | Создаёт команду |\n| `WithArguments(...)` | Устанавливает аргументы |\n| `WithSafeArguments(...)` | Аргументы с экранированием |\n| `WithWorkingDirectory(string)` | Рабочая директория |\n| `WithEnvironmentVariable(string, string?)` | Переменная окружения |\n| `WithEnvironmentVariables(...)` | Несколько переменных |\n| `WithStandardOutputPipe(PipeTarget)` | Перенаправление stdout |\n| `WithStandardErrorPipe(PipeTarget)` | Перенаправление stderr |\n| `WithStandardInputPipe(PipeSource)` | Источник stdin |\n| `WithEncoding(Encoding)` | Кодировка |\n| `WithValidation(bool)` | Проверка exit code |\n| `WithTimeout(TimeSpan)` | Таймаут |\n| `OnExit(Action\u003cint\u003e)` | Callback при завершении |\n| `WithLogger(ICommandLogger)` | Устанавливает логгер |\n| `WithConsoleLogging()` | Консольное логирование |\n| `WithFileLogging(string)` | Файловое логирование |\n| `WithoutLogging()` | Отключает логирование |\n| `ExecuteAsync()` | Выполнить команду |\n| `ExecuteAndReadOutputAsync()` | Выполнить и получить stdout |\n| `ExecuteAndReadAllAsync()` | Получить stdout + stderr |\n| `ExecuteWithRetryAsync(...)` | Выполнить с повторами |\n\n### PipeTarget\n\n| Метод | Описание |\n|-------|----------|\n| `Null` | Отбрасывает вывод |\n| `ToStringBuilder(sb)` | В StringBuilder |\n| `ToDelegate(action)` | Построчный callback |\n| `ToStream(stream)` | В поток |\n| `ToFile(path)` | В файл |\n\n### PipeSource\n\n| Метод | Описание |\n|-------|----------|\n| `Null` | Пустой ввод |\n| `FromString(text)` | Из строки |\n| `FromStream(stream)` | Из потока |\n| `FromFile(path)` | Из файла |\n| `FromBytes(data)` | Из массива байтов |\n\n### CommandResult\n\n| Свойство/Метод | Описание |\n|----------------|----------|\n| `ExitCode` | Код выхода процесса |\n| `IsSuccess` | true если ExitCode == 0 |\n| `StartTime` | Время запуска |\n| `ExitTime` | Время завершения |\n| `RunTime` | Длительность выполнения |\n| `EnsureSuccess()` | Выбрасывает исключение если не успех |\n\n### RetryOptions\n\n| Свойство | Описание |\n|----------|----------|\n| `MaxAttempts` | Максимум попыток (по умолчанию 3) |\n| `Delay` | Начальная задержка |\n| `BackoffMultiplier` | Множитель для exponential backoff |\n| `MaxDelay` | Максимальная задержка |\n| `UseJitter` | Добавить случайность к задержке |\n| `ShouldRetry` | Предикат для исключений |\n| `ShouldRetryOnExitCode` | Предикат для exit codes |\n| `OnRetry` | Callback перед повтором |\n\n### LoggingOptions\n\n| Свойство | Описание |\n|----------|----------|\n| `LogCommandStart` | Логировать запуск |\n| `LogCommandEnd` | Логировать завершение |\n| `StandardOutputLevel` | Уровень для stdout |\n| `StandardErrorLevel` | Уровень для stderr |\n| `MaskSensitiveData` | Маскировать пароли и токены |\n| `SensitivePatterns` | Регулярные выражения для маскирования |\n| `MaxLineLength` | Максимальная длина строки в логе |\n| `IncludeTimestamp` | Включать timestamp |\n| `TimestampFormat` | Формат timestamp |\n\n---\n\n## 🧪 Примеры\n\n### Git операции\n```csharp\n// Статус репозитория\nvar status = await Command.Run(\"git\")\n    .WithArguments(\"status\", \"--short\")\n    .WithWorkingDirectory(\"/path/to/repo\")\n    .ExecuteAndReadOutputAsync();\n\n// Коммит с retry\nawait Command.Run(\"git\")\n    .WithArguments(\"push\", \"origin\", \"main\")\n    .WithTimeout(TimeSpan.FromMinutes(2))\n    .ExecuteWithRetryAsync(RetryOptions.Exponential(3));\n```\n\n### Docker\n```csharp\n// Запуск контейнера с логированием\nawait Command.Run(\"docker\")\n    .WithArguments(\"run\", \"--rm\", \"-d\", \"-p\", \"8080:80\", \"nginx\")\n    .WithConsoleLogging()\n    .ExecuteAsync();\n\n// Docker Compose с переменными окружения\nawait Command.Run(\"docker-compose\")\n    .WithArguments(\"up\", \"-d\")\n    .WithWorkingDirectory(\"/path/to/project\")\n    .WithEnvironmentVariable(\"COMPOSE_PROJECT_NAME\", \"myapp\")\n    .WithConsoleLogging()\n    .ExecuteAsync();\n```\n\n### .NET CLI\n```csharp\n// Сборка проекта\nvar buildOutput = new StringBuilder();\n\nvar result = await Command.Run(\"dotnet\")\n    .WithArguments(\"build\", \"-c\", \"Release\", \"--no-restore\")\n    .WithStandardOutputPipe(PipeTarget.ToStringBuilder(buildOutput))\n    .WithWorkingDirectory(\"/path/to/solution\")\n    .WithConsoleLogging()\n    .ExecuteAsync();\n\n// Публикация с условной логикой\nawait new CommandBuilder(\"dotnet\")\n    .AddArgument(\"publish\")\n    .AddArgument(\"-c\", \"Release\")\n    .AddArgument(\"-o\", \"./publish\")\n    .AddFlag(\"--self-contained\")\n    .AddArgumentIf(OperatingSystem.IsLinux(), \"-r\", \"linux-x64\")\n    .AddArgumentIf(OperatingSystem.IsWindows(), \"-r\", \"win-x64\")\n    .UseConsoleLogging()\n    .ExecuteAsync();\n```\n\n### HTTP запросы с curl\n```csharp\n// GET запрос с retry\nvar response = await Command.Run(\"curl\")\n    .WithArguments(\"-s\", \"https://api.github.com/users/octocat\")\n    .ExecuteWithRetryAndReadOutputAsync(RetryOptions.Exponential(3));\n\n// POST запрос с безопасными данными\nusing var logger = new FileCommandLogger(\"api.log\", new LoggingOptions { MaskSensitiveData = true });\n\nawait Command.Run(\"curl\")\n    .WithArguments(\n        \"-X\", \"POST\",\n        \"-H\", \"Authorization: Bearer secret_token\",\n        \"-d\", \"{\\\"name\\\": \\\"test\\\"}\",\n        \"https://api.example.com/data\")\n    .WithLogger(logger)\n    .ExecuteAsync();\n```\n\n---\n\n## ⚡ Оптимизации\n\nБиблиотека оптимизирована для производительности:\n\n| Аспект | Реализация |\n|--------|------------|\n| Memory | `ArrayPool\u003cchar\u003e` для буферов чтения |\n| ValueTask | Меньше аллокаций для sync-path |\n| Record struct | `CommandResult` размещается на стеке |\n| file sealed | Внутренние классы скрыты от API |\n| UTF8 без BOM | Корректная работа stdin |\n| Trimming | Полная поддержка Native AOT |\n\n---\n\n## 🛠️ Сборка из исходников\n```bash\n# Клонировать репозиторий\ngit clone https://github.com/yourname/SolimusWrapper.git\ncd SolimusWrapper\n\n# Восстановить зависимости\ndotnet restore\n\n# Собрать\ndotnet build\n\n# Запустить тесты\ndotnet test\n\n# Запустить демо\ndotnet run --project samples/SolimusWrapper.Demo\n\n# Создать NuGet пакет\ndotnet pack -c Release\n```\n\n---\n\n## 🔧 Требования\n\n- .NET 10.0 или выше\n- Поддерживаемые платформы: Windows, Linux, macOS\n\n---\n\n## 📄 Лицензия\n\nMIT License. См. [LICENSE](LICENSE) для подробностей.\n\n---\n\n## 🤝 Contributing\n\nContributions welcome! Пожалуйста, создайте issue или pull request.\n\n1. Fork репозитория\n2. Создайте feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit изменения (`git commit -m 'Add amazing feature'`)\n4. Push в branch (`git push origin feature/amazing-feature`)\n5. Откройте Pull Request\n\n---\n\n## 📞 Связь\n\n- GitHub Issues: [Issues](https://github.com/13cyberpunk02/SolimusWrapper/issues)\n- Email: salawat1302@gmail.com\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F13cyberpunk02%2Fsolimuswrapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F13cyberpunk02%2Fsolimuswrapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F13cyberpunk02%2Fsolimuswrapper/lists"}