sorcha-cli

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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "sorcha-cli" with this command: npx skills add stuartf303/sorcha/stuartf303-sorcha-sorcha-cli

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

// SPDX-License-Identifier: MIT // Copyright (c) 2026 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<string> _idOption; private readonly Option<bool> _verboseOption;

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&#x3C;string>("--id", "The resource ID")
    {
        Required = true
    };

    _verboseOption = new Option&#x3C;bool>("--verbose", "Enable verbose output")
    {
        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<string>("--id", "Description")

new Option<string>("--id", "-i")

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

_idOption = new Option<string>("--id", "The resource ID") { Required = true }; _idOption.Aliases.Add("-i"); // Add short alias separately Options.Add(_idOption);

Parent Command with Subcommands

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

// Program.cs rootCommand.Subcommands.Add(new MyCommand(clientFactory, authService, configService));

Refit Service Clients

Interface Definition

// Services/IMyServiceClient.cs using Refit;

public interface IMyServiceClient { [Get("/api/resources")] Task<List<Resource>> ListAsync([Header("Authorization")] string authorization);

[Get("/api/resources/{id}")]
Task&#x3C;Resource> GetAsync(string id, [Header("Authorization")] string authorization);

[Post("/api/resources")]
Task&#x3C;Resource> CreateAsync([Body] CreateRequest request, [Header("Authorization")] string authorization);

[Put("/api/resources/{id}")]
Task&#x3C;Resource> UpdateAsync(string id, [Body] UpdateRequest request, [Header("Authorization")] string authorization);

[Delete("/api/resources/{id}")]
Task DeleteAsync(string id, [Header("Authorization")] string authorization);

// Pagination with query parameters
[Get("/api/resources")]
Task&#x3C;List&#x3C;Resource>> ListAsync(
    [Query] int? page,
    [Query] int? pageSize,
    [Header("Authorization")] string authorization);

// OData queries
[Get("/odata/{resource}")]
Task&#x3C;HttpResponseMessage> QueryODataAsync(
    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

// 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

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

// 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

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

// 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 - Complete command reference

  • testing - Unit test patterns and fixes

  • models - 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

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:

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 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 --version vs ./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"

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

entity-framework

No summary provided by upstream source.

Repository SourceNeeds Review
General

signalr

No summary provided by upstream source.

Repository SourceNeeds Review
General

scalar

No summary provided by upstream source.

Repository SourceNeeds Review