sorcha-cli
π―Skillfrom stuartf303/sorcha
Builds and manages a .NET CLI tool for the Sorcha distributed ledger platform using System.CommandLine, Refit, and Spectre.Console.
Part of
stuartf303/sorcha(22 items)
Installation
git clone https://github.com/StuartF303/Sorcha.gitSkill Details
|
Overview
# Sorcha CLI Skill
The Sorcha CLI (sorcha) is a .NET 10 global tool for managing the Sorcha distributed ledger platform. It uses System.CommandLine 2.0.2 for command parsing, Refit for HTTP API clients, and Spectre.Console for rich terminal output.
Project Location
```
src/Apps/Sorcha.Cli/
βββ Commands/ # Command implementations
βββ Infrastructure/ # Helpers (ConsoleHelper, ExitCodes, HttpClientFactory)
βββ Models/ # Request/Response DTOs for APIs
βββ Services/ # Refit interfaces and service contracts
βββ Program.cs # Entry point and command registration
```
Quick Reference
Creating a New Command
```csharp
// SPDX-License-Identifier: MIT
// Copyright (c) 2025 Sorcha Contributors
using System.CommandLine;
using System.CommandLine.Parsing;
using System.Net;
using System.Text.Json;
using Refit;
using Sorcha.Cli.Infrastructure;
using Sorcha.Cli.Services;
namespace Sorcha.Cli.Commands;
public class MyCommand : Command
{
private readonly Option
private readonly Option
public MyCommand(
HttpClientFactory clientFactory,
IAuthenticationService authService,
IConfigurationService configService)
: base("mycommand", "Description of command")
{
// IMPORTANT: Option constructor takes (name, description) NOT (alias1, alias2)
_idOption = new Option
{
Required = true
};
_verboseOption = new Option
{
Required = false
};
// Add options to command
Options.Add(_idOption);
Options.Add(_verboseOption);
// Set the action handler
this.SetAction(async (ParseResult parseResult, CancellationToken ct) =>
{
var id = parseResult.GetValue(_idOption)!;
var verbose = parseResult.GetValue(_verboseOption);
// Implementation here
return ExitCodes.Success;
});
}
}
```
System.CommandLine 2.0.2 Key Points
| Pattern | Correct | Wrong |
|---------|---------|-------|
| Option constructor | new Option | new Option |
| Add short alias | option.Aliases.Add("-i") | Second constructor param |
| Option name property | Returns "--id" (with dashes) | Does NOT return "id" |
| Get option value | parseResult.GetValue(_option) | parseResult.GetValueForOption() |
| Set action | this.SetAction(async (pr, ct) => {...}) | Override Execute |
| Test find option | o.Name == "--id" | o.Aliases.Contains("--id") |
Testing Note: The Aliases collection does NOT include the option name. Use o.Name == "--id" to find options in tests.
Adding Short Aliases
```csharp
_idOption = new Option
_idOption.Aliases.Add("-i"); // Add short alias separately
Options.Add(_idOption);
```
Parent Command with Subcommands
```csharp
public class ParentCommand : Command
{
public ParentCommand(
HttpClientFactory clientFactory,
IAuthenticationService authService,
IConfigurationService configService)
: base("parent", "Parent command description")
{
Subcommands.Add(new ChildListCommand(clientFactory, authService, configService));
Subcommands.Add(new ChildGetCommand(clientFactory, authService, configService));
Subcommands.Add(new ChildCreateCommand(clientFactory, authService, configService));
}
}
```
Registering Commands in Program.cs
```csharp
// Program.cs
rootCommand.Subcommands.Add(new MyCommand(clientFactory, authService, configService));
```
Refit Service Clients
Interface Definition
```csharp
// Services/IMyServiceClient.cs
using Refit;
public interface IMyServiceClient
{
[Get("/api/resources")]
Task> ListAsync([Header("Authorization")] string authorization);
[Get("/api/resources/{id}")]
Task
[Post("/api/resources")]
Task
[Put("/api/resources/{id}")]
Task
[Delete("/api/resources/{id}")]
Task DeleteAsync(string id, [Header("Authorization")] string authorization);
// Pagination with query parameters
[Get("/api/resources")]
Task> ListAsync(
[Query] int? page,
[Query] int? pageSize,
[Header("Authorization")] string authorization);
// OData queries
[Get("/odata/{resource}")]
Task
string resource,
[Query("$filter")] string? filter,
[Query("$orderby")] string? orderby,
[Query("$top")] int? top,
[Query("$skip")] int? skip,
[Header("Authorization")] string authorization);
}
```
Using Refit Client in Commands
```csharp
// Get client from factory
var client = await clientFactory.CreateMyServiceClientAsync(profileName);
// Get auth token
var token = await authService.GetAccessTokenAsync(profileName);
if (string.IsNullOrEmpty(token))
{
ConsoleHelper.WriteError("You must be authenticated.");
return ExitCodes.AuthenticationError;
}
// Call API with Bearer token
var result = await client.GetAsync(id, $"Bearer {token}");
```
Error Handling Pattern
```csharp
try
{
var result = await client.GetAsync(id, $"Bearer {token}");
// Success handling
}
catch (ApiException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
ConsoleHelper.WriteError($"Resource '{id}' not found.");
return ExitCodes.NotFound;
}
catch (ApiException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)
{
ConsoleHelper.WriteError("Authentication failed. Your access token may have expired.");
ConsoleHelper.WriteInfo("Run 'sorcha auth login' to re-authenticate.");
return ExitCodes.AuthenticationError;
}
catch (ApiException ex) when (ex.StatusCode == HttpStatusCode.Forbidden)
{
ConsoleHelper.WriteError("You do not have permission to access this resource.");
return ExitCodes.AuthorizationError;
}
catch (ApiException ex)
{
ConsoleHelper.WriteError($"API Error: {ex.Message}");
if (ex.Content != null)
{
ConsoleHelper.WriteError($"Details: {ex.Content}");
}
return ExitCodes.GeneralError;
}
catch (Exception ex)
{
ConsoleHelper.WriteError($"Failed: {ex.Message}");
return ExitCodes.GeneralError;
}
```
Console Output Helpers
```csharp
// Success message (green)
ConsoleHelper.WriteSuccess("Operation completed successfully!");
// Error message (red)
ConsoleHelper.WriteError("Something went wrong.");
// Warning message (yellow)
ConsoleHelper.WriteWarning("This action cannot be undone.");
// Info message (cyan)
ConsoleHelper.WriteInfo("Use 'sorcha help' for more information.");
```
Exit Codes
```csharp
public static class ExitCodes
{
public const int Success = 0;
public const int GeneralError = 1;
public const int AuthenticationError = 2;
public const int AuthorizationError = 3;
public const int NotFound = 4;
public const int ValidationError = 5;
}
```
JSON Output Support
```csharp
// Check output format option
var outputFormat = parseResult.GetValue(BaseCommand.OutputOption) ?? "table";
if (outputFormat.Equals("json", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine(JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true }));
return ExitCodes.Success;
}
// Otherwise display as table
Console.WriteLine($"{"ID",-36} {"Name",-30} {"Status",-10}");
Console.WriteLine(new string('-', 80));
foreach (var item in results)
{
Console.WriteLine($"{item.Id,-36} {item.Name,-30} {item.Status,-10}");
}
```
See Also
- [commands](references/commands.md) - Complete command reference
- [testing](references/testing.md) - Unit test patterns and fixes
- [models](references/models.md) - DTO and model patterns
Related Skills
- dotnet - .NET 10 / C# 13 patterns
- xunit - Unit testing with xUnit
- fluent-assertions - FluentAssertions patterns
- moq - Mocking with Moq
Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| System.CommandLine | 2.0.2 | CLI framework |
| Refit | 9.0.2 | HTTP client |
| Refit.HttpClientFactory | 9.0.2 | DI integration |
| Spectre.Console | 0.54.0 | Rich console output |
| System.IdentityModel.Tokens.Jwt | 8.3.0 | JWT token handling |
Tool Version Management
The Sorcha CLI can be installed as a .NET global tool or run from local build. When working on the CLI, check for version mismatches.
Check for Global Tool Installation
```bash
# Check if sorcha is installed as a global tool
dotnet tool list --global | grep -i sorcha
# Find where the current 'sorcha' command is located
which sorcha # Linux/macOS
where sorcha # Windows
```
Uninstall Global Tool (for local development)
If a global tool is installed, it may conflict with local development builds:
```bash
# Uninstall global tool to use local build
dotnet tool uninstall --global sorcha.cli
```
Local Build Paths
After building with dotnet build src/Apps/Sorcha.Cli, the executable is at:
- Release:
src/Apps/Sorcha.Cli/bin/Release/net10.0/Sorcha.Cli.exe - Debug:
src/Apps/Sorcha.Cli/bin/Debug/net10.0/Sorcha.Cli.exe
Walkthrough Scripts
When writing walkthrough scripts that use the CLI, prefer finding the local build:
```powershell
# PowerShell pattern for finding CLI
$RepoRoot = (Get-Item $PSScriptRoot).Parent.Parent.FullName
$LocalCliPath = Join-Path $RepoRoot "src/Apps/Sorcha.Cli/bin/Release/net10.0/Sorcha.Cli.exe"
$DebugCliPath = Join-Path $RepoRoot "src/Apps/Sorcha.Cli/bin/Debug/net10.0/Sorcha.Cli.exe"
if (Test-Path $LocalCliPath) {
$SorchaCliPath = $LocalCliPath
} elseif (Test-Path $DebugCliPath) {
$SorchaCliPath = $DebugCliPath
} else {
$SorchaCliPath = "sorcha" # Fall back to global tool
}
```
Version Mismatch Symptoms
If you see unexpected command options or missing features:
- Check if global tool version differs from source code
- Rebuild with
dotnet build src/Apps/Sorcha.Cli -c Release - Verify using
sorcha --versionvs./Sorcha.Cli.exe --version
Documentation Resources
> Fetch latest System.CommandLine documentation with Context7.
Library ID: /dotnet/command-line-api
Recommended Queries:
- "Option constructor aliases System.CommandLine 2.0"
- "SetAction handler pattern"
- "ParseResult GetValue"
More from this repository10
xunit skill from stuartf303/sorcha
Styles Blazor WASM components using MudBlazor Material Design, implementing responsive layouts with CSS isolation and custom design patterns.
Configures .NET 10 projects with C# 13 features, service defaults, dependency injection, and modern development patterns.
Generates cryptographic keys, signs/verifies data, and manages HD wallets using multi-algorithm support across ED25519, P-256, and RSA-4096.
Manages Docker containerization and docker-compose orchestration for building, running, and configuring multi-service container environments.
minimal-apis skill from stuartf303/sorcha
yarp skill from stuartf303/sorcha
nbitcoin skill from stuartf303/sorcha
Implements Redis caching, session management, and distributed coordination for Sorcha, enabling efficient token tracking, rate limiting, and shared state across services.
entity-framework skill from stuartf303/sorcha