TimeWarp.Amuru
1.0.0-beta.1
Prefix Reserved
See the version list below for details.
dotnet add package TimeWarp.Amuru --version 1.0.0-beta.1
NuGet\Install-Package TimeWarp.Amuru -Version 1.0.0-beta.1
<PackageReference Include="TimeWarp.Amuru" Version="1.0.0-beta.1" />
<PackageVersion Include="TimeWarp.Amuru" Version="1.0.0-beta.1" />
<PackageReference Include="TimeWarp.Amuru" />
paket add TimeWarp.Amuru --version 1.0.0-beta.1
#r "nuget: TimeWarp.Amuru, 1.0.0-beta.1"
#:package TimeWarp.Amuru@1.0.0-beta.1
#addin nuget:?package=TimeWarp.Amuru&version=1.0.0-beta.1&prerelease
#tool nuget:?package=TimeWarp.Amuru&version=1.0.0-beta.1&prerelease
<img src="https://raw.githubusercontent.com/TimeWarpEngineering/timewarpengineering.github.io/refs/heads/master/images/LogoNoMarginNoShadow.svg" alt="logo" height="120" style="float: right" />
TimeWarp.Amuru
TimeWarp.Amuru (formerly TimeWarp.Cli) is a powerful fluent API library for elegant command-line execution in C#. It transforms shell scripting into a type-safe, IntelliSense-friendly experience with a simple static Run()
method, async operations, and proper error handling.
Designed for modern C# developers, TimeWarp.Amuru brings the power of shell scripting directly into your C# code. Whether you're building automation tools, DevOps scripts, or integrating command-line tools into your applications, TimeWarp.Amuru provides the elegant, type-safe API you need.
Why TimeWarp.Amuru?
- Zero Learning Curve: If you know C#, you already know how to use TimeWarp.Amuru
- IntelliSense Everything: Full IDE support with autocomplete, parameter hints, and documentation
- Type Safety: Catch errors at compile-time, not runtime
- No String Escaping Hell: Use C# arrays and parameters naturally
- Built for .NET 10: Modern C# features and performance optimizations
- Script or Library: Use it in quick scripts or production applications
Give a Star! ⭐
If you find this project useful, please give it a star. Thanks!
Quick Start
#!/usr/bin/dotnet run
#:package TimeWarp.Amuru
using TimeWarp.Amuru;
// Get command output as string
var date = await Run("date").GetStringAsync();
Console.WriteLine($"Current date: {date}");
// Process output line by line
var files = await Run("find", ".", "-name", "*.cs").GetLinesAsync();
foreach (var file in files)
{
Console.WriteLine($"Found: {file}");
}
// Execute without capturing output
await Run("echo", "Hello World").ExecuteAsync();
// Chain commands with pipelines
var filteredFiles = await Run("find", ".", "-name", "*.cs")
.Pipe("grep", "async")
.GetLinesAsync();
// Use caching for expensive operations
var files = Run("find", "/large/dir", "-name", "*.log").Cached();
var errors = await files.Pipe("grep", "ERROR").GetLinesAsync();
var warnings = await files.Pipe("grep", "WARN").GetLinesAsync();
// Only one expensive find operation executed!
// C# scripts with arguments work seamlessly
await Run("./myscript.cs", "--verbose", "-o", "output.txt").ExecuteAsync();
// Use the new fluent builder API for complex commands
var result = await Shell.Run("git")
.WithArguments("log", "--oneline", "-n", "10")
.WithWorkingDirectory("/my/repo")
.GetStringAsync();
// Provide standard input to commands
var grepResult = await Shell.Run("grep")
.WithArguments("pattern")
.WithStandardInput("line1\nline2 with pattern\nline3")
.GetStringAsync();
// Use fluent command builders for .NET commands
var packages = await DotNet.ListPackages()
.WithOutdated()
.AsJson()
.ToListAsync();
// Interactive file selection with Fzf (NEW in v0.6.0)
// Use GetStringInteractiveAsync() to show FZF UI and capture selection
var selectedFile = await Fzf.Run()
.FromInput("file1.txt", "file2.txt", "file3.txt")
.WithPreview("cat {}")
.GetStringInteractiveAsync();
// Interactive pipeline - find files and select with FZF
var chosenFile = await Shell.Run("find")
.WithArguments(".", "-name", "*.cs")
.Pipe("fzf", "--preview", "head -20 {}")
.GetStringInteractiveAsync();
// Multi-select with interactive FZF
var selectedItems = await Fzf.Run()
.FromInput("Red", "Green", "Blue", "Yellow")
.WithMulti()
.GetStringInteractiveAsync();
// Full interactive mode (e.g., for vim, nano, etc.)
await Shell.Run("vim")
.WithArguments("myfile.txt")
.ExecuteInteractiveAsync();
Installation
dotnet add package TimeWarp.Cli
Or reference in your C# script:
#:package TimeWarp.Cli
Check out the latest NuGet package: TimeWarp.Cli
Key Features
- Simple Static API: Global
Run()
method for immediate access - Fluent Interface: Chain operations naturally with
.Pipe()
,.Cached()
, etc. - Async-First Design: All operations support modern async/await patterns
- Smart Error Handling: Commands throw on errors by default, with opt-in graceful degradation
- Pipeline Support: Chain commands with Unix-like pipe semantics
- Standard Input Support: Provide stdin to commands with
.WithStandardInput()
- Opt-in Caching: Cache expensive command results with
.Cached()
method - Configuration Options: Working directory, environment variables, and more
- Cancellation Support: Full CancellationToken support for timeouts and manual cancellation
- Cross-Platform: Works on Windows, Linux, and macOS
- C# Script Support: Seamless execution of C# scripts with proper argument handling
- Command Builders: Fluent builders for complex commands (DotNet, Fzf, Ghq, Gwq)
- Interactive Commands: Support for interactive tools like FZF with
GetStringInteractiveAsync()
andExecuteInteractiveAsync()
Error Handling
TimeWarp.Cli provides intelligent error handling that distinguishes between different failure types:
Default Behavior (Throws Exceptions)
// Throws CommandExecutionException on non-zero exit code
await Run("ls", "/nonexistent").GetStringAsync();
// Throws exception if command not found
await Run("nonexistentcommand").GetStringAsync();
Graceful Degradation (Opt-in)
// Returns empty string/array on command failure
var options = new CommandOptions().WithValidation(CommandResultValidation.None);
var result = await Run("ls", "/nonexistent", options).GetStringAsync(); // ""
// Note: Process start failures (command not found) always throw
await Run("nonexistentcommand", options).GetStringAsync(); // Still throws!
Special Cases
- Empty/whitespace commands return empty results (no exception)
- Null command options return empty results (defensive programming)
- Pipeline failures propagate based on validation settings
Testing and Mocking
TimeWarp.Cli provides built-in support for mocking commands during testing through the CliConfiguration
class:
Basic Mocking
// Set up mock commands for testing
CliConfiguration.SetCommandPath("fzf", "/path/to/mock/fzf");
CliConfiguration.SetCommandPath("git", "/path/to/mock/git");
// Your code using these commands will now use the mocks
var selected = await Fzf.Run()
.FromInput("option1", "option2", "option3")
.GetStringAsync(); // Uses mock fzf
// Clean up after tests
CliConfiguration.Reset();
Creating Mock Executables
// Create a simple mock script
File.WriteAllText("/tmp/mock-fzf", "#!/bin/bash\necho 'mock-selection'");
Run("chmod", "+x", "/tmp/mock-fzf");
// Configure TimeWarp.Cli to use it
CliConfiguration.SetCommandPath("fzf", "/tmp/mock-fzf");
Testing Interactive Commands
For commands like fzf
that are normally interactive, you can either:
- Use mock executables as shown above
- Use non-interactive modes (e.g.,
fzf --filter
)
API Reference
CliConfiguration.SetCommandPath(command, path)
- Set custom executable pathCliConfiguration.ClearCommandPath(command)
- Remove custom path for a commandCliConfiguration.Reset()
- Clear all custom pathsCliConfiguration.HasCustomPath(command)
- Check if command has custom pathCliConfiguration.AllCommandPaths
- Get all configured paths
Architecture
TimeWarp.Cli is built on several key architectural principles:
- Static Entry Point: Minimal ceremony with global
Run()
method - Immutable Design: Thread-safe, readonly objects throughout
- Integration Testing: Real command validation over mocking
- Predictable Error Handling: Clear distinction between failure types
- Opt-in Complexity: Advanced features available when needed
See our Architectural Decision Records for detailed design rationale.
Documentation
- CLAUDE.md - Complete API reference and usage guide
- CommandExtensions.md - Static API documentation
- CommandResult.md - Fluent interface documentation
- Architectural Decisions - Design rationale and decisions
Example Scripts
See Spikes/CsScripts/ for example scripts demonstrating TimeWarp.Cli usage patterns.
Unlicense
This project is licensed under the Unlicense.
Contributing
Your contributions are welcome! Before starting any work, please open a discussion.
See our Kanban board for current development tasks and priorities.
Contact
If you have an issue and don't receive a timely response, feel free to reach out on our Discord server.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- CliWrap (>= 3.9.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
1.0.0-beta.8 | 168 | 8/28/2025 |
1.0.0-beta.7 | 157 | 8/28/2025 |
1.0.0-beta.6 | 158 | 8/28/2025 |
1.0.0-beta.5 | 167 | 8/27/2025 |
1.0.0-beta.4 | 161 | 8/26/2025 |
1.0.0-beta.3 | 45 | 8/23/2025 |
1.0.0-beta.2 | 68 | 8/22/2025 |
1.0.0-beta.1 | 86 | 8/3/2025 |