Skip to content

Process Commands

Process commands allow you to easily wrap external CLI tools and forward all arguments to them. This is perfect for creating shortcuts, wrappers, or composite tools.

Basic Usage

Extend ProcessCommand and specify the executable path:

csharp
using Promty;
using Promty.Attributes;

[Description("git", "Execute git commands")]
public class GitCommand : ProcessCommand
{
    protected override string ExecutablePath => "git";
}

Usage:

bash
dotnet run -- git status
dotnet run -- git commit -m "Initial commit"
dotnet run -- git push origin main

All arguments after git are forwarded directly to the git executable.

Examples

Docker Wrapper

csharp
[Description("docker", "Execute docker commands")]
public class DockerCommand : ProcessCommand
{
    protected override string ExecutablePath => "docker";
}

Usage:

bash
dotnet run -- docker ps
dotnet run -- docker build -t myapp .
dotnet run -- docker run -p 8080:80 myapp

.NET CLI Wrapper

csharp
[Description("dotnet", "Execute dotnet CLI commands")]
public class DotNetCommand : ProcessCommand
{
    protected override string ExecutablePath => "dotnet";
}

Usage:

bash
dotnet run -- dotnet build
dotnet run -- dotnet test
dotnet run -- dotnet pack

NPM Wrapper

csharp
[Description("npm", "Execute npm commands")]
public class NpmCommand : ProcessCommand
{
    protected override string ExecutablePath => "npm";
}

Usage:

bash
dotnet run -- npm install
dotnet run -- npm run build
dotnet run -- npm test

With Full Paths

If the executable is not in the system PATH, specify the full path:

csharp
[Description("custom", "Execute custom tool")]
public class CustomCommand : ProcessCommand
{
    protected override string ExecutablePath =>
        "/usr/local/bin/custom-tool"; // Unix
        // or
        // @"C:\Tools\custom-tool.exe"; // Windows
}

Cross-Platform Paths

For cross-platform support:

csharp
[Description("python", "Execute Python scripts")]
public class PythonCommand : ProcessCommand
{
    protected override string ExecutablePath =>
        Environment.OSVersion.Platform == PlatformID.Win32NT
            ? "python.exe"
            : "python3";
}

Use Cases

1. Create Shortcuts

Simplify complex commands:

csharp
[Description("build-all", "Build all projects")]
public class BuildAllCommand : ProcessCommand
{
    protected override string ExecutablePath => "dotnet";

    // Override to add default arguments
    protected override string[] GetArguments(string[] args)
    {
        return new[] { "build", "-c", "Release" }
            .Concat(args)
            .ToArray();
    }
}

2. Wrap External Tools

Make external tools part of your CLI:

csharp
[Description("format", "Format code with Prettier")]
public class FormatCommand : ProcessCommand
{
    protected override string ExecutablePath => "npx";

    protected override string[] GetArguments(string[] args)
    {
        return new[] { "prettier", "--write", "." }
            .Concat(args)
            .ToArray();
    }
}

3. Create Composite Commands

Chain multiple operations:

csharp
[Description("deploy", "Build and deploy the application")]
public class DeployCommand : Command<DeployCommand.Args>
{
    public class Args { }

    public override async Task<int> ExecuteAsync(Args args)
    {
        // Build
        Console.WriteLine("Building...");
        var buildResult = await RunProcessAsync("dotnet", "build -c Release");
        if (buildResult != 0) return buildResult;

        // Test
        Console.WriteLine("Testing...");
        var testResult = await RunProcessAsync("dotnet", "test");
        if (testResult != 0) return testResult;

        // Deploy
        Console.WriteLine("Deploying...");
        var deployResult = await RunProcessAsync("./deploy.sh");
        return deployResult;
    }

    private async Task<int> RunProcessAsync(string executable, string args = "")
    {
        var process = new System.Diagnostics.Process
        {
            StartInfo = new System.Diagnostics.ProcessStartInfo
            {
                FileName = executable,
                Arguments = args,
                UseShellExecute = false
            }
        };
        process.Start();
        await process.WaitForExitAsync();
        return process.ExitCode;
    }
}

Exit Codes

Process commands automatically return the exit code from the external process:

  • 0 - Process succeeded
  • Non-zero - Process failed (specific meaning depends on the tool)

Standard Output/Error

By default, process commands:

  • Forward stdout to your console
  • Forward stderr to your console
  • Wait for the process to complete
  • Return the exit code

Advantages

No Argument Parsing - All arguments forwarded as-is ✅ Simple Setup - Just specify the executable path ✅ Exit Code Propagation - Exit codes automatically forwarded ✅ Console Integration - Output appears directly in your console ✅ Quick Wrappers - Perfect for creating shortcuts and aliases

When to Use

Use Process Commands when:

  • Wrapping existing CLI tools
  • Creating shortcuts for complex commands
  • Building composite workflows
  • Forwarding all arguments to external tools

Use Standard Commands when:

  • You need typed argument binding
  • You want validation and help text
  • Building custom business logic
  • Processing arguments in code

Released under the MIT License.