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
17 changes: 6 additions & 11 deletions .agents/skills/pretty-console-expert/SKILL.md
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,13 @@ description: Expert workflow for using PrettyConsole correctly and efficiently i

## Core Workflow

1. Verify the installed PrettyConsole version before coding.
- Read `Directory.Packages.props`, `*.csproj`, and/or run `dotnet list package`.
- Keep implementation compatible with the installed version; do not "fix" compilation by downgrading unless the user explicitly requests downgrading.

2. Bring extension APIs into scope:
1. Bring extension APIs into scope:

```csharp
using PrettyConsole;
using static System.Console; // optional
```

3. Choose APIs by intent.
2. Choose APIs by intent.
- Styled output: `Console.WriteInterpolated`, `Console.WriteLineInterpolated`.
- Inputs/prompts: `Console.TryReadLine`, `Console.ReadLine`, `Console.Confirm`, `Console.RequestAnyInput`.
- Dynamic rendering and line control: `Console.Overwrite`, `Console.ClearNextLines`, `Console.SkipLines`, `Console.NewLine`.
Expand All @@ -27,7 +22,7 @@ using static System.Console; // optional
- Menus/tables: `Console.Selection`, `Console.MultiSelection`, `Console.TreeMenu`, `Console.Table`.
- Low-level override only: use `Console.Write(...)` / `Console.WriteLine(...)` span+`ISpanFormattable` overloads only when you intentionally bypass the handler for a custom formatting pipeline.

4. Route output deliberately.
3. Route output deliberately.
- Keep normal prompts, menus, tables, durable user-facing output, and machine-readable non-error output on `OutputPipe.Out` unless there is a specific reason not to.
- Use `OutputPipe.Error` for transient live UI and for actual errors/diagnostics/warnings so stdout stays pipe-friendly and error output remains distinguishable.
- `LiveConsoleRegion` should usually live on `OutputPipe.Error` in interactive CLIs. Keep the durable lines that must coordinate with it flowing through the region instance instead of writing directly to the same pipe elsewhere.
Expand All @@ -51,7 +46,7 @@ using static System.Console; // optional
- Prefer `Color`, `Markup`, and guarded `AnsiToken` in styled output. Use `Color.*` for token-based color APIs such as `ProgressBar`, `Spinner`, `TypeWrite`, and `LiveConsoleRegion.RenderProgress`. Keep `ConsoleColor` for APIs that explicitly require it, such as low-level span writes or `Console.SetColors`.
- Route transient UI (spinner/progress/overwrite loops) to `OutputPipe.Error` to keep stdout pipe-friendly, and use `OutputPipe.Error` for genuine errors/diagnostics. Keep ordinary non-error interaction flow on `OutputPipe.Out`.
- Spinner/progress/overwrite output is caller-owned after rendering completes. Explicitly remove it with `Console.ClearNextLines(totalLines, pipe)` or intentionally keep the region with `Console.SkipLines(totalLines)`.
- `LiveConsoleRegion` is the right primitive when durable line output and transient status must interleave over time. It is line-oriented: use `WriteLine`, not inline writes, for cooperating durable output above the retained region.
- `LiveConsoleRegion` is the right primitive when durable line output and transient status must interleave over time. It is line-oriented: use `WriteLine`, not inline writes, for cooperating durable output above the retained region. Disposing the region clears the retained snapshot automatically; call `Clear()` only when the region should disappear before the object itself is disposed and you still want to reuse that same instance later.
- Only use the bounded `Channel<T>` snapshot pattern when multiple producers must update the same live region at high frequency. For single-producer or modest-rate updates, keep the rendering loop simple.

## Practical Patterns
Expand Down Expand Up @@ -114,12 +109,12 @@ live.Render($"Resolving graph");
live.WriteLine($"Updated package-a");
live.RenderProgress(65, (builder, out handler) =>
handler = builder.Build(OutputPipe.Error, $"Compiling"));
live.Clear();
live.Render($"Linking");
```

## Reference File

Read [references/v5-api-map.md](references/v5-api-map.md) when you need exact usage snippets, migration mapping from old APIs, or a compile-fix checklist.
Read [references/v6-api-map.md](references/v6-api-map.md) when you need exact usage snippets, migration mapping from old APIs, or a compile-fix checklist.
Read [references/testing-with-consolecontext.md](references/testing-with-consolecontext.md) when the task involves testing a PrettyConsole-based CLI or command handler.

If public API usage changes in the edited project, ask whether to update `README.md` and changelog/release-notes files.
Original file line number Diff line number Diff line change
@@ -1,30 +1,16 @@
# PrettyConsole v5 API Map
# PrettyConsole v6 API Map

Use this file when implementing or reviewing PrettyConsole usage so code compiles against modern APIs and keeps allocation-conscious patterns.
Use this file when implementing or reviewing PrettyConsole usage so code compiles against the current surface and keeps the library's allocation-conscious patterns.

## 1. Version First

Read installed version before coding:

```bash
dotnet list package
rg -n "PrettyConsole" --glob "*.csproj" .
# optionally also check central package management if present:
# rg -n "PrettyConsole" Directory.Packages.props
```

If version and request conflict, keep the installed version and adapt code accordingly.

## 2. Namespace and Setup
## 1. Namespace and Setup

```csharp
using PrettyConsole;
using static System.Console; // optional
```

PrettyConsole methods are extension members on `System.Console`.

## 3. Correct Modern APIs
## 2. Correct v6 APIs

- Styled writes:
- `Console.WriteInterpolated(...)`
Expand All @@ -39,6 +25,11 @@ PrettyConsole methods are extension members on `System.Console`.
- `Console.ClearNextLines(...)`
- `Console.SkipLines(...)`
- `Console.NewLine(...)`
- Live retained UI:
- `LiveConsoleRegion.WriteLine(...)`
- `LiveConsoleRegion.Render(...)`
- `LiveConsoleRegion.RenderProgress(...)`
- `LiveConsoleRegion.Clear()`
- Progress:
- `ProgressBar.Update(...)`
- `ProgressBar.Render(...)`
Expand All @@ -52,9 +43,25 @@ PrettyConsole methods are extension members on `System.Console`.
### Output routing

- Keep prompts, menus, tables, final user-facing output, and machine-readable non-error output on `OutputPipe.Out` unless you intentionally need a different split.
- Use `OutputPipe.Error` for transient live UI and for actual errors/diagnostics/warnings so stdout remains pipe-friendly and error output stays distinct.
- Use `OutputPipe.Error` for transient live UI and for actual errors, diagnostics, and warnings so stdout remains pipe-friendly and error output stays distinct.
- `LiveConsoleRegion` should usually live on `OutputPipe.Error` in interactive CLIs. Keep durable lines that must coordinate with it flowing through the region instance instead of writing directly to the same pipe elsewhere.
- Disposing a `LiveConsoleRegion` clears the retained snapshot and then permanently closes the region. Call `Clear()` only when you want to remove the current snapshot but keep the region instance reusable for later renders.
- Avoid mixing a single interactive exchange across `Out` and `Error` unless the split is intentional.

### Interpolated-handler styling model

- Prefer `Color`, `Markup`, and guarded `AnsiToken` inside interpolation holes:

```csharp
Console.WriteLineInterpolated($"{Color.Green}ready{Color.Default}");
Console.WriteLineInterpolated($"{Markup.Bold}build{Markup.ResetBold} complete");
```

- `ConsoleColor` interpolation still works for compatibility, but `Color.*` is the primary v6 surface for styled output.
- For APIs that explicitly take `AnsiToken`, use `Color.*` or `new AnsiToken("...")`.
- Do not embed raw escape sequences directly in string literals. Keep ANSI inside interpolation holes so PrettyConsole can isolate the token safely.
- If you must branch on ANSI capability outside the guarded token model, use `ConsoleContext.IsAnsiSupported`.

### Interpolated-handler special formats

- `TimeSpan` with `:duration`:
Expand Down Expand Up @@ -84,31 +91,32 @@ Use these only when intentionally bypassing the interpolated handler for a custo
### New lines and blank lines

- Use `Console.NewLine(pipe)` when the intent is only to end the current line or emit a blank line.
- Do not use `Console.WriteLineInterpolated($"")` or payloads like `$"{ConsoleColor.Default}"` just to force a newline.
- Do not use `Console.WriteLineInterpolated($"")` or reset-only payloads such as `$"{Color.Default}"` just to force a newline.
- Use `WriteLineInterpolated(...)` when you are actually writing content and also want the trailing newline.

## 4. Old -> New Migration Table
## 3. Old -> New Migration Table

- `IndeterminateProgressBar` -> `Spinner`
- `AnimationSequence` -> `Pattern`
- `ProgressBar.WriteProgressBar` -> `ProgressBar.Render`
- `PrettyConsoleExtensions` -> `ConsoleContext`
- Legacy `ColoredOutput`/`Color` types -> `ConsoleColor` helpers and tuples
- Legacy `ColoredOutput`/`Color` types -> `Color`, `Markup`, `AnsiToken`, and `ConsoleColor` compatibility where the API explicitly asks for it
- `Markup` and `AnsiColors` raw string expectations -> guarded `AnsiToken` values

## 5. Compile-Safe Patterns
## 4. Compile-Safe Patterns

### Styled output

```csharp
Console.WriteInterpolated($"[{ConsoleColor.Cyan}info{ConsoleColor.Default}] {message}");
Console.WriteLineInterpolated(OutputPipe.Error, $"{ConsoleColor.Red}error{ConsoleColor.Default}: {message}");
Console.WriteInterpolated($"[{Color.Cyan}info{Color.Default}] {message}");
Console.WriteLineInterpolated(OutputPipe.Error, $"{Color.Red}error{Color.Default}: {message}");
Console.NewLine();
```

### Typed input

```csharp
if (!Console.TryReadLine(out int port, $"Port ({ConsoleColor.Green}5000{ConsoleColor.Default}): "))
if (!Console.TryReadLine(out int port, $"Port ({Color.Green}5000{Color.Default}): "))
port = 5000;
```

Expand All @@ -126,9 +134,9 @@ static string PromptSelection(string title, string[] options) {

while (selection.Length == 0) {
Console.Overwrite(() => {
selection = Console.Selection(options, $"{ConsoleColor.Cyan}{title}{ConsoleColor.DefaultForeground}:");
selection = Console.Selection(options, $"{Color.Cyan}{title}{Color.DefaultForeground}:");
if (selection.Length == 0)
Console.WriteLineInterpolated(OutputPipe.Error, $"{ConsoleColor.Red}Invalid choice.");
Console.WriteLineInterpolated(OutputPipe.Error, $"{Color.Red}Invalid choice.{Color.Default}");
}, lines: options.Length + 3, pipe: OutputPipe.Out);
}

Expand All @@ -152,7 +160,7 @@ var workTask = Task.Run(async () => {
var spinner = new Spinner();
await spinner.RunAsync(workTask, (builder, out handler) => {
var current = Math.Min(Volatile.Read(ref step), steps.Length - 1);
handler = builder.Build(OutputPipe.Error, $"Current step: {ConsoleColor.Green}{steps[current]}");
handler = builder.Build(OutputPipe.Error, $"Current step: {Color.Green}{steps[current]}");
});
```

Expand All @@ -162,20 +170,34 @@ Use this when the spinner header should reflect concurrently changing state with

```csharp
Console.Overwrite(percent, static current => {
ProgressBar.Render(OutputPipe.Error, current, ConsoleColor.Cyan, maxLineWidth: 40);
ProgressBar.Render(OutputPipe.Error, current, Color.Cyan, maxLineWidth: 40);
Console.NewLine(OutputPipe.Error);
Console.WriteInterpolated(OutputPipe.Error, $"Downloading assets... {ConsoleColor.Cyan}{current}");
Console.WriteInterpolated(OutputPipe.Error, $"Downloading assets... {Color.Cyan}{current}{Color.Default}");
}, lines: 2, pipe: OutputPipe.Error);
```

Prefer this shape for live `status + progress` regions. It keeps the state explicit, avoids closure allocations, and makes the rendered height obvious.

### Retained live region

```csharp
using var live = new LiveConsoleRegion(OutputPipe.Error);

live.Render($"Resolving graph");
live.WriteLine($"Fetched package-a");
live.RenderProgress(65, (builder, out handler) =>
handler = builder.Build(OutputPipe.Error, $"Compiling {Color.Cyan}core{Color.Default}"));
live.Render($"Linking {Color.Cyan}core{Color.Default}");
```

Prefer `LiveConsoleRegion` when durable lines must keep flowing above a pinned transient line on the same pipe over time.

### Overwrite loop cleanup

```csharp
Console.Overwrite(() => {
Console.WriteLineInterpolated(OutputPipe.Error, $"Running...");
ProgressBar.Render(OutputPipe.Error, percent, ConsoleColor.Cyan);
ProgressBar.Render(OutputPipe.Error, percent, Color.Cyan);
}, lines: 2, pipe: OutputPipe.Error);

Console.ClearNextLines(2, OutputPipe.Error);
Expand Down Expand Up @@ -221,12 +243,13 @@ Why this works:

For single-producer or modest-rate updates, prefer a simpler render loop without the channel.

## 6. Performance Checklist
## 5. Performance Checklist

- Prefer interpolated handlers over string concatenation.
- Treat span/formattable `Write`/`WriteLine` overloads as advanced escape hatches, not default app-level APIs.
- Use `Console.NewLine(pipe)` for bare line breaks instead of empty/reset-only `WriteLineInterpolated(...)` calls.
- Keep ANSI/decorations in interpolation holes, not raw literal spans.
- Use `OutputPipe.Error` for transient rendering and genuine errors/diagnostics, but keep ordinary non-error interaction flow on `OutputPipe.Out`.
- Treat span/formattable `Write` and `WriteLine` overloads as advanced escape hatches, not default app-level APIs.
- Use `Console.NewLine(pipe)` for bare line breaks instead of empty or reset-only `WriteLineInterpolated(...)` calls.
- Keep ANSI and decorations in interpolation holes, not raw literal spans.
- Use `Color.*` for token-based color APIs such as `ProgressBar`, `Spinner`, `TypeWrite`, and `LiveConsoleRegion.RenderProgress`.
- Use `OutputPipe.Error` for transient rendering and genuine errors or diagnostics, but keep ordinary non-error interaction flow on `OutputPipe.Out`.
- Clean up live UI explicitly after the last frame with `ClearNextLines(...)` or keep it intentionally with `SkipLines(...)`.
- Avoid introducing wrapper abstractions when direct PrettyConsole APIs already solve the task.
Loading
Loading