|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +ktsu.RunCommand is a .NET library that provides an easy way to execute shell commands and handle output via delegates. It supports both synchronous and asynchronous execution with customizable output handling. |
| 8 | + |
| 9 | +## Build and Test Commands |
| 10 | + |
| 11 | +### Building |
| 12 | +```bash |
| 13 | +dotnet build |
| 14 | +``` |
| 15 | + |
| 16 | +### Running Tests |
| 17 | +```bash |
| 18 | +# Run all tests |
| 19 | +dotnet test |
| 20 | + |
| 21 | +# Run a single test |
| 22 | +dotnet test --filter "FullyQualifiedName~RunCommandTests.ExecuteShouldExecuteCommandAndReturnExitCode" |
| 23 | +``` |
| 24 | + |
| 25 | +### Creating NuGet Package |
| 26 | +```bash |
| 27 | +dotnet pack |
| 28 | +``` |
| 29 | + |
| 30 | +### Using the Build Automation |
| 31 | +The project uses a custom PowerShell build module (PSBuild) for CI/CD: |
| 32 | +```powershell |
| 33 | +Import-Module ./scripts/PSBuild.psm1 |
| 34 | +$buildConfig = Get-BuildConfiguration ... |
| 35 | +Invoke-CIPipeline -BuildConfiguration $buildConfig |
| 36 | +``` |
| 37 | + |
| 38 | +## Architecture |
| 39 | + |
| 40 | +### Core Components |
| 41 | + |
| 42 | +**[RunCommand.cs](RunCommand/RunCommand.cs)** - Main static class providing the public API: |
| 43 | +- `Execute(string command)` - Synchronous command execution |
| 44 | +- `Execute(string command, OutputHandler outputHandler)` - Synchronous with output handling |
| 45 | +- `ExecuteAsync(string command)` - Asynchronous command execution |
| 46 | +- `ExecuteAsync(string command, OutputHandler outputHandler)` - Asynchronous with output handling |
| 47 | +- All methods return process exit codes |
| 48 | +- Commands are parsed by splitting on first space: filename and arguments |
| 49 | + |
| 50 | +**[OutputHandler.cs](RunCommand/OutputHandler.cs)** - Base class for handling command output: |
| 51 | +- Processes output in raw, undelimited chunks as they arrive from the process |
| 52 | +- Provides `OnStandardOutput` and `OnStandardError` delegates |
| 53 | +- Supports custom encoding (defaults to UTF-8) |
| 54 | +- Virtual methods `HandleStandardOutputData` and `HandleStandardErrorData` for extensibility |
| 55 | + |
| 56 | +**[LineOutputHandler.cs](RunCommand/LineOutputHandler.cs)** - Derived output handler for line-by-line processing: |
| 57 | +- Inherits from OutputHandler |
| 58 | +- Buffers incoming chunks and splits by newlines |
| 59 | +- Maintains separate buffers (`outputBuffer`, `errorBuffer`) for incomplete lines |
| 60 | +- Uses `Environment.NewLine` after normalizing line endings with `ReplaceLineEndings()` |
| 61 | +- Invokes delegates for each complete line |
| 62 | + |
| 63 | +**[AsyncProcessStreamReader.cs](RunCommand/AsyncProcessStreamReader.cs)** - Internal async stream reader: |
| 64 | +- Reads from both stdout and stderr concurrently using 4096-character buffers |
| 65 | +- Continuously reads while process is running, then performs final read after exit |
| 66 | +- Uses `Task.WhenAny` to poll streams efficiently |
| 67 | +- Invokes OutputHandler methods for each chunk of data received |
| 68 | + |
| 69 | +### Key Design Patterns |
| 70 | + |
| 71 | +1. **Async Over Sync**: The synchronous `Execute` methods call `ExecuteAsync().Result`, making the async implementation the source of truth. |
| 72 | + |
| 73 | +2. **Strategy Pattern**: OutputHandler and LineOutputHandler allow different output processing strategies to be plugged in. |
| 74 | + |
| 75 | +3. **Template Method**: OutputHandler provides virtual methods that LineOutputHandler overrides to customize behavior. |
| 76 | + |
| 77 | +4. **Buffering Strategy**: LineOutputHandler demonstrates how to buffer incomplete data across multiple chunk reads to reconstruct complete lines. |
| 78 | + |
| 79 | +### Multi-Targeting |
| 80 | + |
| 81 | +The library targets multiple .NET versions: |
| 82 | +- .NET 9.0 |
| 83 | +- .NET 8.0 |
| 84 | +- .NET 7.0 |
| 85 | +- .NET 6.0 |
| 86 | +- .NET 5.0 |
| 87 | +- .NET Standard 2.1 |
| 88 | +- .NET Standard 2.0 |
| 89 | + |
| 90 | +Uses `ktsu.Sdk` for standardized project configuration. |
| 91 | + |
| 92 | +## Testing |
| 93 | + |
| 94 | +Tests are located in [RunCommand.Test/](RunCommand.Test/) using MSTest framework: |
| 95 | +- [RunCommandTests.cs](RunCommand.Test/RunCommandTests.cs) - Tests for main execution methods |
| 96 | +- [LineOutputHandlerTests.cs](RunCommand.Test/LineOutputHandlerTests.cs) - Tests for line buffering logic |
| 97 | +- Tests target .NET 9.0 only |
| 98 | +- Uses MSTest.Sdk for test execution |
| 99 | + |
| 100 | +## Version Management |
| 101 | + |
| 102 | +The project uses semantic versioning with git-based version calculation: |
| 103 | +- Version tags in commit messages: `[major]`, `[minor]`, `[patch]`, `[pre]` |
| 104 | +- Public API changes are automatically detected and trigger minor version bumps |
| 105 | +- VERSION.md, CHANGELOG.md, and other metadata files are auto-generated by PSBuild module |
| 106 | + |
| 107 | +## Important Implementation Notes |
| 108 | + |
| 109 | +### Command Parsing |
| 110 | +Commands are split on the first space character. The first part becomes the filename, the rest becomes arguments. Be aware this simple parsing doesn't handle quoted strings specially. |
| 111 | + |
| 112 | +### Process Configuration |
| 113 | +On Windows, `LoadUserProfile` is set to true for proper environment variable expansion. |
| 114 | + |
| 115 | +### Stream Reading |
| 116 | +The AsyncProcessStreamReader performs a final read after process exit to ensure all buffered data is captured. This is crucial for short-lived processes. |
| 117 | + |
| 118 | +### Encoding |
| 119 | +All input/output streams use UTF-8 by default but can be customized via OutputHandler constructor. |
0 commit comments