MCPInstall

dotnet-spectre-console

Building rich console output. Spectre.Console: tables, trees, progress, prompts, live displays.

Installs
88
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-spectre-console
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads dotnet-spectre-console automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

dotnet-spectre-console

Spectre.Console for building rich console output (tables, trees, progress bars, prompts, markup, live displays) and Spectre.Console.Cli for structured command-line application parsing. Cross-platform across Windows, macOS, and Linux terminals.

Version assumptions: .NET 8.0+ baseline. Spectre.Console 0.54.0 (latest stable). Spectre.Console.Cli 0.53.1 (latest stable). Both packages target net8.0+ and netstandard2.0.

Scope boundary: This skill owns rich console output and Spectre.Console.Cli command parsing. Full TUI applications (windows, menus, dialogs, views) are owned by [skill:dotnet-terminal-gui]. System.CommandLine parsing is owned by [skill:dotnet-system-commandline]. CLI application architecture and distribution are owned by [skill:dotnet-cli-architecture] and [skill:dotnet-cli-distribution].

Cross-references: [skill:dotnet-terminal-gui] for full TUI alternative, [skill:dotnet-system-commandline] for System.CommandLine scope boundary, [skill:dotnet-cli-architecture] for CLI structure, [skill:dotnet-csharp-async-patterns] for async patterns, [skill:dotnet-csharp-dependency-injection] for DI with Spectre.Console.Cli, [skill:dotnet-accessibility] for TUI accessibility limitations and screen reader considerations.


Package References

<ItemGroup>
  <!-- Rich console output: markup, tables, trees, progress, prompts, live displays -->
  <PackageReference Include="Spectre.Console" Version="0.54.0" />

  <!-- CLI command framework (adds command parsing, settings, DI support) -->
  <PackageReference Include="Spectre.Console.Cli" Version="0.53.1" />
</ItemGroup>

Spectre.Console.Cli has a dependency on Spectre.Console -- install both only when you need the CLI framework. For rich output only, Spectre.Console alone is sufficient.


Markup and Styling

Spectre.Console uses a BBCode-inspired markup syntax for styled console output.

Basic Markup

using Spectre.Console;

// Styled text with markup tags
AnsiConsole.MarkupLine("[bold red]Error:[/] File not found.");
AnsiConsole.MarkupLine("[green]Success![/] Build completed in [blue]2.3s[/].");
AnsiConsole.MarkupLine("[underline]https://example.com[/]");
AnsiConsole.MarkupLine("[dim italic]This is subtle text[/]");

// Nested styles
AnsiConsole.MarkupLine("[bold [red on white]Warning:[/] check config[/]");

// Escape brackets with double brackets
AnsiConsole.MarkupLine("Use [[bold]] for bold text.");

Figlet Text

AnsiConsole.Write(
    new FigletText("Hello!")
        .Color(Color.Green)
        .Centered());

Rule (Horizontal Line)

// Simple rule
AnsiConsole.Write(new Rule());

// Titled rule
AnsiConsole.Write(new Rule("[yellow]Section Title[/]"));

// Aligned rule
AnsiConsole.Write(new Rule("[blue]Left Aligned[/]").LeftJustified());

Tables

var table = new Table();

// Add columns
table.AddColumn("Name");
table.AddColumn(new TableColumn("Age").Centered());
table.AddColumn(new TableColumn("City").RightAligned());

// Add rows
table.AddRow("Alice", "30", "Seattle");
table.AddRow("[green]Bob[/]", "25", "Portland");
table.AddRow("Charlie", "35", "Vancouver");

// Styling
table.Border(TableBorder.Rounded);
table.BorderColor(Color.Grey);
table.Title("[underline]Team Members[/]");
table.Caption("[dim]Updated daily[/]");

// Column configuration
table.Columns[0].PadLeft(2);
table.Columns[0].NoWrap();

AnsiConsole.Write(table);

Nested Tables

var innerTable = new Table()
    .AddColumn("Detail")
    .AddColumn("Value")
    .AddRow("Role", "Developer")
    .AddRow("Level", "Senior");

var outerTable = new Table()
    .AddColumn("Name")
    .AddColumn("Info")
    .AddRow("Alice", innerTable);

AnsiConsole.Write(outerTable);

Trees

var tree = new Tree("Solution");

// Add nodes
var srcNode = tree.AddNode("[yellow]src[/]");
var apiNode = srcNode.AddNode("Api");
apiNode.AddNode("Controllers/");
apiNode.AddNode("Program.cs");

var libNode = srcNode.AddNode("Library");
libNode.AddNode("Services/");

var testNode = tree.AddNode("[blue]tests[/]");
testNode.AddNode("Api.Tests/");

// Styling
tree.Style = Style.Parse("dim");

AnsiConsole.Write(tree);

Panels

var panel = new Panel("This is [green]important[/] content.")
    .Header("[bold]Notice[/]")
    .Border(BoxBorder.Rounded)
    .BorderColor(Color.Blue)
    .Padding(2, 1)    // horizontal, vertical
    .Expand();         // fill available width

AnsiConsole.Write(panel);

Composing Renderables with Columns

AnsiConsole.Write(new Columns(
    new Panel("Left panel").Expand(),
    new Panel("Right panel").Expand()));

Progress Displays

Progress Bars

await AnsiConsole.Progress()
    .AutoClear(false)       // keep completed tasks visible
    .HideCompleted(false)
    .Columns(
        new TaskDescriptionColumn(),
        new ProgressBarColumn(),
        new PercentageColumn(),
        new RemainingTimeColumn(),
        new SpinnerColumn())
    .StartAsync(async ctx =>
    {
        var downloadTask = ctx.AddTask("[green]Downloading[/]", maxValue: 100);
        var extractTask = ctx.AddTask("[blue]Extracting[/]", maxValue: 100);

        while (!ctx.IsFinished)
        {
            await Task.Delay(50);
            downloadTask.Increment(1.5);

            if (downloadTask.Value > 50)
            {
                extractTask.Increment(0.8);
            }
        }
    });

Status Spinners

await AnsiConsole.Status()
    .Spinner(Spinner.Known.Dots)
    .SpinnerStyle(Style.Parse("green bold"))
    .StartAsync("Processing...", async ctx =>
    {
        await Task.Delay(1000);
        ctx.Status("Compiling...");
        ctx.Spinner(Spinner.Known.Star);
        await Task.Delay(1000);
        ctx.Status("Publishing...");
        await Task.Delay(1000);
    });

Prompts

Text Prompt

// Simple typed input
var name = AnsiConsole.Ask<string>("What's your [green]name[/]?");
var age = AnsiConsole.Ask<int>("What's your [green]age[/]?");

// With default value
var city = AnsiConsole.Prompt(
    new TextPrompt<string>("Enter [green]city[/]:")
        .DefaultValue("Seattle")
        .ShowDefaultValue());

// Secret input (password)
var password = AnsiConsole.Prompt(
    new TextPrompt<string>("Enter [green]password[/]:")
        .Secret());

// With validation
var email = AnsiConsole.Prompt(
    new TextPrompt<string>("Enter [green]email[/]:")
        .Validate(input =>
            input.Contains('@') && input.Contains('.')
                ? ValidationResult.Success()
                : ValidationResult.Error("[red]Invalid email address[/]")));

// Optional (allow empty)
var nickname = AnsiConsole.Prompt(
    new TextPrompt<string>("Enter [green]nickname[/] (optional):")
        .AllowEmpty());

Confirmation Prompt

bool proceed = AnsiConsole.Confirm("Continue with deployment?");

Selection Prompt

var fruit = AnsiConsole.Prompt(
    new SelectionPrompt<string>()
        .Title("Pick a [green]fruit[/]:")
        .PageSize(10)
        .EnableSearch()
        .WrapAround()
        .AddChoices("Apple", "Banana", "Orange", "Mango", "Grape"));

// Grouped choices
var country = AnsiConsole.Prompt(
    new SelectionPrompt<string>()
        .Title("Select [green]destination[/]:")
        .AddChoiceGroup("Europe", "France", "Italy", "Spain")
        .AddChoiceGroup("Asia", "Japan", "Thailand", "Vietnam"));

Multi-Selection Prompt

var toppings = AnsiConsole.Prompt(
    new MultiSelectionPrompt<string>()
        .Title("Choose [green]toppings[/]:")
        .PageSize(10)
        .Required()
        .InstructionsText("[grey](Press [blue]<space>[/] to toggle, [green]<enter>[/] to accept)[/]")
        .AddChoices("Cheese", "Pepperoni", "Mushrooms", "Olives", "Onions"));

Live Displays

Live displays update in-place for dynamic content that changes over time.

var table = new Table()
    .AddColumn("Time")
    .AddColumn("Status");

await AnsiConsole.Live(table)
    .AutoClear(false)
    .Overflow(VerticalOverflow.Ellipsis)
    .Cropping(VerticalOverflowCropping.Bottom)
    .StartAsync(async ctx =>
    {
        table.AddRow(DateTime.Now.ToString("T"), "[yellow]Starting...[/]");
        ctx.Refresh();
        await Task.Delay(1000);

        table.AddRow(DateTime.Now.ToString("T"), "[green]Processing...[/]");
        ctx.Refresh();
        await Task.Delay(1000);

        table.AddRow(DateTime.Now.ToString("T"), "[blue]Complete![/]");
        ctx.Refresh();
    });

Replacing the Target

await AnsiConsole.Live(new Markup("[yellow]Initializing...[/]"))
    .StartAsync(async ctx =>
    {
        await Task.Delay(1000);
        ctx.UpdateTarget(new Markup("[green]Ready![/]"));
        await Task.Delay(1000);
        ctx.UpdateTarget(
            new Panel("Final result: [bold]42[/]")
                .Header("Done")
                .Border(BoxBorder.Rounded));
    });

Spectre.Console.Cli Framework

Spectre.Console.Cli provides a structured command-line parsing framework with command hierarchies, typed settings, validation, and automatic help generation.

Basic Command App

using Spectre.Console.Cli;

var app = new CommandApp<GreetCommand>();
return app.Run(args);

// Command with typed settings
public sealed class GreetSettings : CommandSettings
{
    [CommandArgument(0, "<name>")]
    [Description("The person to greet")]
    public string Name { get; init; } = string.Empty;

    [CommandOption("-c|--count")]
    [Description("Number of times to greet")]
    [DefaultValue(1)]
    public int Count { get; init; }

    [CommandOption("--shout")]
    [Description("Greet in uppercase")]
    public bool Shout { get; init; }
}

public sealed class GreetCommand : Command<GreetSettings>
{
    public override int Execute(CommandContext context, GreetSettings settings)
    {
        for (int i = 0; i < settings.Count; i++)
        {
            var greeting = $"Hello, {settings.Name}!";
            AnsiConsole.MarkupLine(settings.Shout
                ? $"[bold]{greeting.ToUpperInvariant()}[/]"
                : greeting);
        }
        return 0;  // exit code
    }
}

Command Hierarchy with Branches

var app = new CommandApp();
app.Configure(config =>
{
    config.AddBranch<RemoteSettings>("remote", remote =>
    {
        remote.AddCommand<RemoteAddCommand>("add")
            .WithDescription("Add a remote");
        remote.AddCommand<RemoteRemoveCommand>("remove")
            .WithDescription("Remove a remote");
    });

    config.AddCommand<CloneCommand>("clone")
        .WithDescription("Clone a repository");
});

return app.Run(args);

// Shared settings for the branch -- inherited by subcommands
public class RemoteSettings : CommandSettings
{
    [CommandOption("-v|--verbose")]
    [Description("Verbose output")]
    public bool Verbose { get; init; }
}

// Subcommand settings inherit from branch settings
public sealed class RemoteAddSettings : RemoteSettings
{
    [CommandArgument(0, "<name>")]
    public string Name { get; init; } = string.Empty;

    [CommandArgument(1, "<url>")]
    public string Url { get; init; } = string.Empty;
}

public sealed class RemoteAddCommand : Command<RemoteAddSettings>
{
    public override int Execute(CommandContext context, RemoteAddSettings settings)
    {
        if (settings.Verbose)
        {
            AnsiConsole.MarkupLine($"[dim]Adding remote...[/]");
        }
        AnsiConsole.MarkupLine($"Added remote [green]{settings.Name}[/] -> {settings.Url}");
        return 0;
    }
}

Settings Validation

public sealed class DeploySettings : CommandSettings
{
    [CommandArgument(0, "<environment>")]
    public string Environment { get; init; } = string.Empty;

    [CommandOption("--timeout")]
    [DefaultValue(30)]
    public int TimeoutSeconds { get; init; }

    public override ValidationResult Validate()
    {
        var validEnvs = new[] { "dev", "staging", "prod" };
        if (!validEnvs.Contains(Environment, StringComparer.OrdinalIgnoreCase))
        {
            return ValidationResult.Error(
                $"Environment must be one of: {string.Join(", ", validEnvs)}");
        }

        if (TimeoutSeconds <= 0)
        {
            return ValidationResult.Error("Timeout must be positive");
        }

        return ValidationResult.Success();
    }
}

Async Commands

public sealed class FetchCommand : AsyncCommand<FetchSettings>
{
    public override async Task<int> ExecuteAsync(
        CommandContext context, FetchSettings settings)
    {
        await AnsiConsole.Status()
            .StartAsync("Fetching data...", async ctx =>
            {
                await Task.Delay(2000); // simulate work
            });

        AnsiConsole.MarkupLine("[green]Done![/]");
        return 0;
    }
}

Dependency Injection with ITypeRegistrar

using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;

// Set up DI container
var services = new ServiceCollection();
services.AddSingleton<IGreetingService, GreetingService>();
services.AddSingleton<IAnsiConsole>(AnsiConsole.Console);

var registrar = new TypeRegistrar(services);
var app = new CommandApp<GreetCommand>(registrar);
return app.Run(args);

// TypeRegistrar bridges Microsoft DI to Spectre.Console.Cli
public sealed class TypeRegistrar(IServiceCollection services) : ITypeRegistrar
{
    public ITypeResolver Build() => new TypeResolver(services.BuildServiceProvider());

    public void Register(Type service, Type implementation)
        => services.AddSingleton(service, implementation);

    public void RegisterInstance(Type service, object implementation)
        => services.AddSingleton(service, implementation);

    public void RegisterLazy(Type service, Func<object> factory)
        => services.AddSingleton(service, _ => factory());
}

public sealed class TypeResolver(IServiceProvider provider) : ITypeResolver
{
    public object? Resolve(Type? type)
        => type is null ? null : provider.GetService(type);
}

// Command receives services via constructor injection
public sealed class GreetCommand(IGreetingService greetingService) : Command<GreetSettings>
{
    public override int Execute(CommandContext context, GreetSettings settings)
    {
        var message = greetingService.GetGreeting(settings.Name);
        AnsiConsole.MarkupLine(message);
        return 0;
    }
}

Testable Console Output

Spectre.Console provides IAnsiConsole for testable output instead of writing directly to the real console.

// Production: use AnsiConsole.Console (the real console)
IAnsiConsole console = AnsiConsole.Console;

// Testing: use a recording console
var console = AnsiConsole.Create(new AnsiConsoleSettings
{
    Out = new AnsiConsoleOutput(new StringWriter())
});

// Use the abstraction instead of static AnsiConsole methods
console.MarkupLine("[green]Testable output[/]");
console.Write(new Table().AddColumn("Col").AddRow("Val"));

Agent Gotchas

  1. Do not use AnsiConsole.Markup* in redirected output. When stdout is redirected (piped to a file or another process), ANSI escape codes corrupt the output. Check AnsiConsole.Profile.Capabilities.Ansi before using markup, or use IAnsiConsole with appropriate settings. See [skill:dotnet-csharp-async-patterns] for async pipeline patterns.
  2. Do not assume ANSI support in CI environments. CI runners (GitHub Actions, Azure Pipelines) may not support ANSI escape codes. Set TERM=dumb or use AnsiConsole.Create() with ColorSystemSupport.NoColors for CI-safe output. Spectre.Console auto-detects capabilities, but explicit configuration prevents flaky rendering.
  3. Do not mix AnsiConsole static calls with IAnsiConsole instance calls. Static AnsiConsole.Write() always targets the real console. When using DI with IAnsiConsole, consistently use the injected instance. Mixing the two produces duplicated or interleaved output.
  4. Do not modify a renderable from a background thread during Live(). Live displays are not thread-safe. Mutate the target renderable only inside the Start/StartAsync callback, then call ctx.Refresh(). Concurrent mutations cause corrupted terminal output.
  5. Do not use prompts in non-interactive contexts. TextPrompt, SelectionPrompt, and ConfirmationPrompt block waiting for user input. In CI or automated scripts, use environment variables or command-line arguments for input instead of prompts. Check AnsiConsole.Profile.Capabilities.Interactive before prompting.
  6. Do not confuse Spectre.Console.Cli with System.CommandLine. They are independent frameworks with different APIs. Spectre.Console.Cli uses CommandSettings classes with [CommandArgument]/[CommandOption] attributes, while System.CommandLine uses Option<T> and Argument<T> builder pattern. Do not mix APIs. For System.CommandLine, see [skill:dotnet-system-commandline].
  7. Do not forget ctx.Refresh() after modifying live display content. Changes to tables, trees, or panels inside a Live() callback are not rendered until ctx.Refresh() is called. Omitting it produces stale displays.
  8. Do not hardcode color values without fallback. Terminals with limited color support silently degrade TrueColor values. Use named colors (Color.Green) when possible and test with NO_COLOR=1 environment variable to verify graceful degradation.

Prerequisites

  • NuGet packages: Spectre.Console 0.54.0 for rich output; add Spectre.Console.Cli 0.53.1 for CLI framework
  • Target framework: net8.0 or later (also supports netstandard2.0)
  • Terminal: Any terminal emulator supporting ANSI escape sequences. Windows Terminal, iTerm2, or modern Linux terminal recommended for best experience (TrueColor, Unicode). Console output degrades gracefully on limited terminals.
  • For DI with Spectre.Console.Cli: Microsoft.Extensions.DependencyInjection package for the ITypeRegistrar/ITypeResolver bridge

References

FAQ

What does the dotnet-spectre-console skill do?

Building rich console output. Spectre.Console: tables, trees, progress, prompts, live displays.

How do I install dotnet-spectre-console?

Run npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-spectre-console in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/dotnet-spectre-console/ (personal) or .claude/skills/dotnet-spectre-console/ (project).

Which AI agents can use dotnet-spectre-console?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is dotnet-spectre-console safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains dotnet-spectre-console?

It is published by wshaddix in wshaddix/dotnet-skills. It has 88 recorded installs.

More from wshaddix/dotnet-skills

View all →

dotnet-xunit

wshaddix/dotnet-skills

Writing xUnit tests. v3 Fact/Theory, fixtures, parallelism, IAsyncLifetime, v2 compatibility.

Backend & APIs613 installs

dotnet-testing-strategy

wshaddix/dotnet-skills

Deciding how to test .NET code. Unit vs integration vs E2E decision tree, test doubles.

Testing & QA610 installs

modern-csharp-coding-standards

wshaddix/dotnet-skills

Write modern, high-performance C code using records, pattern matching, value objects, async/await, Span /Memory , and best-practice API design patterns. Emphasizes functional-style programming with C 12+ features. Use when writing new C code or refactoring existing code, designing public APIs for libraries or services, optimizing performance-critical code paths, or building async/await-heavy applications.

csharp-wolverinefx

wshaddix/dotnet-skills

Build .NET applications with WolverineFX for messaging, HTTP services, and event sourcing. Use when implementing command handlers, message handlers, HTTP endpoints with WolverineFx.HTTP, transactional outbox patterns, event sourcing with Marten, CQRS architectures, cascading messages, batch message processing, or configuring transports like RabbitMQ, Azure Service Bus, or Amazon SQS.

Backend & APIs570 installs

testcontainers

wshaddix/dotnet-skills

Patterns for using Testcontainers in .NET integration tests to spin up real dependencies like databases and message queues. Use when writing integration tests that require real databases, testing with message brokers like RabbitMQ or Kafka, or isolating test dependencies with Docker containers.

Testing & QA565 installs

Dependency injection patterns and best practices using Microsoft.Extensions.DependencyInjection for .NET applications. Use when configuring DI containers in .NET, choosing between service lifetimes (Singleton, Scoped, Transient), or implementing decorator patterns and service interception.

Security549 installs

Popular backend & apis skills

View category →

find-skills

vercel-labs/skills

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.

OfficialBackend & APIs4.1M installs

entra-app-registration

microsoft/azure-skills

Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.

OfficialBackend & APIs566.7K installs

This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).

Backend & APIs514.8K installs

embedded-captions

heygen-com/hyperframes

Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.

Backend & APIs453.3K installs

entra-agent-id

microsoft/azure-skills

Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).

OfficialBackend & APIs336.6K installs

neon

neondatabase/agent-skills

Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.

Backend & APIs237.1K installs