Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/nuget-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish Spectre.Console.Extensions.Configuration (local)
run: dotnet nuget push D20Tek.Spectre.Console.Extensions.Configuration.${VERSION}.nupkg --source https://nuget.pkg.github.com/d20Tek/index.json --api-key ${GITHUB_TOKEN}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish Spectre.Console.Extensions.Hosting (local)
run: dotnet nuget push D20Tek.Spectre.Console.Extensions.Hosting.${VERSION}.nupkg --source https://nuget.pkg.github.com/d20Tek/index.json --api-key ${GITHUB_TOKEN}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish Spectre.Console.Extensions (nuget.org)
run: dotnet nuget push D20Tek.Spectre.Console.Extensions.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_API_KEY}
env:
Expand All @@ -58,3 +68,13 @@ jobs:
run: dotnet nuget push D20Tek.Spectre.Console.Extensions.MoreContainers.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_API_KEY}
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}

- name: Publish Spectre.Console.Extensions.Configuration (nuget.org)
run: dotnet nuget push D20Tek.Spectre.Console.Extensions.Configuration.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_API_KEY}
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}

- name: Publish Spectre.Console.Extensions.Hosting (nuget.org)
run: dotnet nuget push D20Tek.Spectre.Console.Extensions.Hosting.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_API_KEY}
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
96 changes: 96 additions & 0 deletions .plans/future-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Future Features

This document captures candidate features for future releases of the D20Tek.Spectre.Console.Extensions packages. Each item notes what it adds, why it is valuable, and whether Spectre.Console already covers the capability. Items are grouped by priority tier based on impact relative to effort.

## Guiding Principle

Prioritize features that reinforce the library's existing strengths and fill genuine gaps that Spectre.Console does not already cover. Avoid thin wrappers over existing Spectre.Console.Cli APIs, since they add surface area without meaningful value.

The library's strongest, genuinely differentiating areas are:
- Testing infrastructure (no equivalent ships in Spectre.Console).
- Culture-aware, validated prompt controls (for example, CurrencyPrompt).
- Verbosity services.
- Integration points that Spectre.Console.Cli does not provide (verbosity-aware logging and configuration).

## Tier 1 - High Impact, Fills Genuine Gaps

### 1. Verbosity-Aware Logging Integration (Microsoft.Extensions.Logging) [DONE]
- What it adds: Convenience and cohesion around Microsoft.Extensions.Logging, not basic injection support. Specifically:
- A verbosity bridge that maps the existing VerbosityLevel enum to LogLevel, so the same -v|--verbosity switch that controls prompts and output also sets the minimum log level.
- An IAnsiConsole-backed logger provider so log output renders through Spectre (consistent styling and markup, and respects TestConsole in tests) instead of the stock AddConsole() provider writing directly to System.Console.
- A one-liner builder hook, for example CommandAppBuilder.WithLogging(...), that wires AddLogging plus the verbosity bridge plus the console provider, so users do not have to reach through WithLifetimes().Services.
- Why it matters: The README already lists logging integration as a future goal. Without the verbosity bridge, verbosity and logging are configured independently. Without the IAnsiConsole-backed provider, log output bypasses TestConsole and breaks the testing story.
- Spectre.Console coverage: None. Spectre.Console.Cli does not ship ILogger wiring.
- Important clarification: Basic logger injection already works today with no new code. The DependencyInjectionTypeRegistrar exposes the underlying IServiceCollection via its Services property, and the resolver forwards to IServiceProvider.GetService. A consumer can already call registrar.WithLifetimes().Services.AddLogging(...) in ConfigureServices, and any command can then inject ILogger<T> through its constructor. This feature is therefore about verbosity integration, Spectre-rendered output, and a fluent builder hook, not about enabling injection.

### 2. Configuration and Options Binding (Microsoft.Extensions.Configuration) [DONE]
- What it adds: A new separate package (D20Tek.Spectre.Console.Extensions.Configuration) that wires Microsoft.Extensions.Configuration and Options into the CommandAppBuilder. Two builder hooks:
- WithConfiguration(...): builds an IConfiguration (appsettings.json plus environment variables by default, with an optional configure delegate) and registers it in the container.
- WithOptions&lt;T&gt;(sectionName): binds a configuration section to a strongly typed options class, resolvable as IOptions&lt;T&gt;.
- Why it matters: Configuration is table-stakes for production CLI tools and pairs naturally with the existing dependency injection container. Any command can then inject IConfiguration or IOptions&lt;T&gt; through its constructor, exactly like ILogger&lt;T&gt; today.
- Spectre.Console coverage: None. Spectre.Console.Cli does not ship IConfiguration wiring, so this is a real gap.
- Packaging decision: Separate package. The code surface is small (roughly two extension methods), but it pulls in 4-5 additional Microsoft.Extensions.Configuration/Options dependencies. Keeping it out of the core package preserves the core's minimal-dependency goal, consistent with the MoreContainers split.
- Design decision: Keep CommandSettings (CLI args) and IOptions&lt;T&gt; (config) separate; commands decide precedence explicitly. Config-backed defaults for command options can be added later if needed.
- Implementation note: The builder hooks need access to the container. DONE - CommandAppBuilder now exposes a public ITypeRegistrar? Registrar getter and a public GetServiceCollection() helper that returns the registrar's IServiceCollection (throwing if no DI container is configured). Add-on extension packages (logging, configuration, and future ones) should call GetServiceCollection() rather than reaching through WithLifetimes().Services. The existing WithLogging hook was refactored to use this accessor.
- Status: DONE - Package implemented with WithConfiguration and WithOptions&lt;T&gt; (data-annotation validated), covered by unit tests, and demonstrated by the Configuration.Cli sample.

### 3. Generic Host Integration (Microsoft.Extensions.Hosting) [DONE]
- What it adds: A new separate package (D20Tek.Spectre.Console.Extensions.Hosting) that bridges the .NET Generic Host (HostApplicationBuilder / IHostBuilder) to Spectre.Console.Cli. Consumers configure configuration, options, logging, and services through the standard host model, then run a CommandApp whose types resolve from the host's already-built IServiceProvider. This is a sibling to CommandAppBuilder for teams that want the full .NET app model.
- Why it matters: Generic Host is the standard .NET app-composition model and unlocks hosted services, host lifetime, and the layered configuration/logging defaults with no bespoke wiring. It complements the existing lean CommandAppBuilder rather than replacing it.
- Spectre.Console coverage: None. Spectre.Console.Cli does not ship Generic Host wiring.
- Packaging decision: Separate package that references the core package (reuses the builder pattern and DI-bridge conventions, consistent with the MoreContainers and Configuration splits). Adds a Microsoft.Extensions.Hosting dependency, so it stays out of the core package.
- Key design point: Spectre registers its own types (command types, IAnsiConsole, its config) at Run(), which is after host.Build(). Because the host provider is already immutable by then, the bridge does not try to mutate it. Instead:
- HostTypeRegistrar accepts Spectre's run-time Register/RegisterInstance/RegisterLazy calls into an internal registration map (it does not throw after build).
- HostTypeResolver fuses the two sources: it first tries host.Services.GetService(type); if that is null and the type is in the map, it constructs the instance with ActivatorUtilities.CreateInstance(host.Services, impl) so command constructor dependencies (IOptions<T>, ILogger<T>, IConfiguration, user services) are injected from the host provider. Instance and factory registrations are honored directly from the map.
- API surface: Both a low-level path (HostTypeRegistrar plus an IHost.RunCommandAppAsync(args, configure) extension) and a fluent HostCommandAppBuilder that mirrors CommandAppBuilder (WithDefaultCommand<T>, ConfigureCommands, Build/RunAsync).
- Sample: A GenericHost.Cli sample using HostApplicationBuilder as the active code path, with the equivalent IHostBuilder (Host.CreateDefaultBuilder) style shown as comments in Program.cs.
- Deliverables: New package project, HostTypeResolver, HostTypeRegistrar, HostCommandAppExtensions, HostCommandAppBuilder, exhaustive unit tests with fakes, the GenericHost.Cli sample, and README / CHANGELOG / future-features updates.
- Status: DONE - Package implemented with HostTypeRegistrar, HostTypeResolver, HostRegistration, HostCommandAppExtensions, and the HostCommandAppBuilder fluent builder. Covered by exhaustive unit tests (48 tests, all passing) and demonstrated by the GenericHost.Cli sample. README and CHANGELOG updated.

### 4. Additional Prompt Controls
Round out the "Controls" story with a themed family of culture-aware, validated prompts that follow the existing CurrencyPrompt pattern (IPrompt<T> plus IHasCulture, with a validator and presenter split).

- DatePrompt / DateRangePrompt: Culture-aware date entry with format hints and range validation.
- Spectre.Console coverage: None dedicated. Ask<DateTime>() exists, but there is no culture-aware, format-hinted, range-validating date control. Recommended first control because of its everyday utility and close similarity to CurrencyPrompt.
- PathPrompt: Filesystem path input with existence validation and path auto-completion.
- Spectre.Console coverage: None. Leverages the existing HistoryTextPrompt autocomplete infrastructure. Genuinely new.
- PatternPrompt (formerly proposed as MaskedPrompt): Patterned input such as phone numbers or identifiers, enforcing a format like (###) ###-####.
- Spectre.Console coverage: Partial and easily confused. Spectre.Console provides secret masking (hiding input) but not pattern or format masking (enforcing a layout). Rename away from "Masked" to avoid ambiguity with the existing secret feature. Hold this item unless rebranded.

## Tier 2 - Minor Value-Add

### 5. CompositeCommandInterceptor
- What it adds: A helper that composes multiple ICommandInterceptor instances into a chain (for example, timing plus logging plus telemetry).
- Why it matters: Spectre.Console.Cli's SetInterceptor registers a single interceptor. Composing several currently requires custom code.
- Spectre.Console coverage: The interceptor mechanism (ICommandInterceptor, SetInterceptor) already exists. Only the multi-interceptor composition is additive, and the value is modest.

### 6. Async Cancellation Ergonomics
- What it adds: Out-of-the-box Ctrl+C wiring (Console.CancelKeyPress linked to a CancellationToken) provided through the CommandAppBuilder so long-running commands cancel cleanly.
- Why it matters: InteractiveCommandBase already accepts a CancellationToken. Providing the cancellation plumbing by default removes boilerplate.
- Spectre.Console coverage: Cancellation tokens are supported, but the default Ctrl+C linkage is left to the consumer.

## Tier 3 - Polish for a 1.0 Feel

### 7. Fluent Assertions for Testing
- What it adds: A fluent assertion helper set over CommandAppResult, for example result.ShouldSucceed().AndOutputContains(...).
- Why it matters: Complements the differentiating testing infrastructure and improves the test authoring experience.
- Spectre.Console coverage: None.

### 8. Command Registration Analyzer or Source Generator (stretch)
- What it adds: Auto-discovery of ICommandConfiguration and commands via attributes to reduce startup wiring.
- Why it matters: Cuts boilerplate for larger command sets.
- Spectre.Console coverage: None. This is a larger investment and is intentionally a stretch goal.

### 9. Documentation and Changelog Parity
- What it adds: An api-reference documentation set under docs/ to complement the existing CHANGELOG.md.
- Why it matters: Contributor guidelines require both api-reference docs and changelog entries whenever the public API changes. A public launch should include this structure. The repository now has a CHANGELOG.md following the Keep a Changelog format, but still lacks a docs/ folder.

## Recommended Splash Focus

For the initial public release, prioritize the items that fill genuine gaps and extend existing strengths:
1. Verbosity-aware logging integration (verbosity bridge, Spectre-rendered output, and a builder hook; note that basic logger injection already works today).
2. Configuration and options binding.
3. Generic Host integration (a sibling to CommandAppBuilder for teams that want the full .NET app model).
4. One or two new prompt controls, starting with DatePrompt, then PathPrompt.

This produces a coherent launch narrative: a complete toolkit for building, configuring, testing, and polishing Spectre.Console CLI apps.
31 changes: 30 additions & 1 deletion ReleaseNotes.md → CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,33 @@
# Release Notes
# Changelog

All notable changes to this project are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Release v1.57.1
### Added
- Public `CommandAppBuilder.Registrar` getter and `CommandAppBuilder.GetServiceCollection()` helper so add-on extension packages can access the builder's DI container.
- Verbosity-aware logging that renders through Spectre.Console. New public API includes `LoggingCommandAppBuilderExtensions.WithLogging`, `SpectreLoggingExtensions.AddSpectreConsole`, `SpectreConsoleLoggerProvider`, `SpectreConsoleLogger`, `SpectreConsoleLoggerOptions`, and the `VerbosityLevel`/`LogLevel` mapping extensions.
- New `Logging.Cli` sample that demonstrates enabling verbosity-aware logging with `WithLogging` and injecting `ILogger<T>` into a command.
- New `D20Tek.Spectre.Console.Extensions.Configuration` package that adds Microsoft.Extensions.Configuration and Options binding to the builder. New public API includes `ConfigurationCommandAppBuilderExtensions.WithConfiguration` and `ConfigurationCommandAppBuilderExtensions.WithOptions<TOptions>`.
- New `Configuration.Cli` sample that demonstrates binding configuration with `WithConfiguration` and injecting `IOptions<T>` bound via `WithOptions<TOptions>` into a command.
- New `D20Tek.Spectre.Console.Extensions.Hosting` package that bridges Spectre.Console.Cli to the .NET Generic Host (`Microsoft.Extensions.Hosting`). New public API includes `HostCommandAppExtensions.RunCommandAppAsync`, `HostCommandAppExtensions.RunCommandApp`, `HostCommandAppExtensions.CreateCommandApp`, `HostCommandAppExtensions.CreateCommandAppBuilder`, and the `HostCommandAppBuilder` fluent builder. Run-time registrations captured from Spectre resolve through a composite provider, so a Spectre-registered type can depend on another Spectre-registered type while host services still take precedence.
- New `HostStartupBase` and `HostStartupExtensions.WithStartup<TStartup>` in the Hosting package, providing a host-aware startup that splits `ConfigureServices` (run pre-build against the host's `IServiceCollection`) from `ConfigureCommands` (applied post-build when the CommandApp is built).
- New `GenericHost.Cli` sample that demonstrates bridging Spectre.Console.Cli to the .NET Generic Host so command types resolve from the host's service provider.
- New `docs/` documentation site with a flat structure: an introduction, a detailed getting-started guide, targeted `guide-*.md` task guides, and an `api-reference.md` hub with per-topic and per-package `api-reference-*.md` references covering the core, Configuration, Hosting, and MoreContainers packages.

### Changed
- Upgraded Spectre dependencies to latest version 0.57.2.
- Updated other dependencies to latest versions.
- `LoggingCommandAppBuilderExtensions.WithLogging` now uses the new `GetServiceCollection()` accessor instead of reaching through the internal registrar.
- `SpectreLoggingExtensions.AddSpectreConsole` now sets the logging builder's minimum level from the mapped verbosity, so Debug and Trace entries are emitted when a more detailed verbosity is requested.
- The Configuration and MoreContainers packages now ship dedicated, package-specific README files (packed as the NuGet package readme) instead of the root repository README.
- Enabled SourceLink, symbol packages (snupkg), deterministic builds, and a shared package icon across all four NuGet packages, and consolidated shared package metadata and version into `Directory.Build.props`.

### Fixed
- Corrected XML documentation on `LamarTypeResolver`, which previously referred to SimpleInjector instead of Lamar.
- Enabled XML documentation generation for the MoreContainers package and documented the previously undocumented public members so all four packages ship complete API docs.

## Release v1.56.1
* Upgraded Spectre dependencies to latest version 0.56.
Expand Down
Loading
Loading