Repository navigation
[DEV-1573] Spectre.Console UX for setup wizard + history import - #30
Conversation
Design document for narrowing Spectre.Console adoption to the two most human-facing commands (setup, history), with explicit non-TTY fallbacks and no Spectre.Console.Cli dependency to keep the AOT/trim build clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implementation plan for DEV-1573. Ten bite-sized tasks covering package addition + AOT gate, ImportProgress event hierarchy, TDD-driven importer wiring, setup wizard overhaul, HistoryDisplay abstraction with TTY vs piped gate, and background-phase progress with streaming failure lines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review Summary by QodoAdopt Spectre.Console for setup wizard and history import with live progress
WalkthroughsDescription• Adopted Spectre.Console for the setup and history commands, replacing ad-hoc console output
with rich prompts, spinners, rules, grids, and live progress tracking
• **setup command** — Replaced numbered menus with SelectionPrompt, TextPrompt, and
ConfirmationPrompt; wrapped server reachability probe in Status spinner; replaced final summary
with two-column Grid layout; added Rule section headers
• **history command** — Implemented single pinned ProgressTask footer showing `Importing
{done}/{total} · {sid8}: {lines}/{total}` with live subagent completion streaming; added separate
background-phase progress block for titles/summaries with individual failure streaming
• **SessionImporter** — Added optional IProgress<ImportProgress>? parameter to
ImportSessionAsync, SendTranscriptBatches, and SendAgentLifecycle; defined three progress
events: BatchFlushed(int), SubagentStarted(string), SubagentFinished(string, int); changed
SendAgentLifecycle return type from Task to Task<int>
• **TTY vs piped output** — Gated rich output via Console.IsOutputRedirected; non-TTY path
preserves plain line-per-session format for scripts and CI logs
• **Testing** — Added 2 TDD tests for IProgress wiring; all 292 unit tests and 4 integration tests
passing; AOT publish gate clean
• **Documentation** — Comprehensive design spec and implementation plan covering architecture, UX
flows, and testing strategy
Diagramflowchart LR
A["setup/history<br/>commands"] -->|"Console.IsOutputRedirected"| B{"TTY or<br/>piped?"}
B -->|"TTY"| C["Spectre.Console<br/>rich output"]
C --> D["Rules, Prompts,<br/>Spinners, Grids"]
B -->|"piped"| E["Plain text<br/>output"]
F["SessionImporter"] -->|"IProgress<ImportProgress>"| G["Progress events"]
G --> H["BatchFlushed<br/>SubagentStarted<br/>SubagentFinished"]
H --> C
File Changes1. src/kapacitor/Commands/HistoryCommand.cs
|
Code Review by Qodo
1. Invalid with on struct
|
| .StartAsync(async ctx => { | ||
| var footer = ctx.AddTask("[green]Importing[/]", maxValue: transcriptFiles.Count); | ||
| display = display with { Footer = footer }; | ||
|
|
There was a problem hiding this comment.
1. Invalid with on struct 🐞 Bug ≡ Correctness
HistoryCommand uses a with { ... } expression on HistoryDisplay, but HistoryDisplay is
declared as a non-record struct, so this code will not compile and will block the build.
Agent Prompt
### Issue description
`HistoryDisplay` is a `readonly struct` but is copied using `display = display with { Footer = footer };`, which requires a record type and causes compilation failure.
### Issue Context
You likely intended value-type copy semantics with init-only properties.
### Fix Focus Areas
- src/kapacitor/Commands/HistoryCommand.cs[9-36]
- src/kapacitor/Commands/HistoryCommand.cs[454-466]
### Suggested fix
Either:
1) Change the declaration to `readonly record struct HistoryDisplay` (preferred since you already use `with`), or
2) Replace the `with` usage with an explicit new value:
```csharp
var footer = ctx.AddTask(...);
display = new HistoryDisplay { Tty = display.Tty, Footer = footer };
```
(keep other properties as needed).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
False positive — with expressions on non-record structs are legal since C# 10 (the spec allows with on any struct with init/settable members, not just record struct). HistoryDisplay is a readonly struct with init-only properties, which satisfies this. Both the Build and test and AOT publish check CI jobs on this PR are passing, confirming the code compiles cleanly.
…ounting - Tag BatchFlushed with the owning AgentId so HistoryCommand only counts parent-transcript lines against linesTotal (the file it measured with CountFileLines). Previously subagent batches could push linesDone past linesTotal. - Switch the per-session progress sink from Progress<T> to a synchronous InlineProgress<T>. new Progress<T> marshals to the ThreadPool in a console app, so footer mutations and streamed lines could arrive after the sequential import loop moved on. - Track title and summary task counts separately and use them as the respective ProgressTask maxValue. Previously both bars used backgroundTasks.Count, so with both task types enqueued neither bar could reach 100%. Skip the summary bar entirely when no summaries are requested. - Update unit tests to assert AgentId attribution on BatchFlushed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Addressed findings 2, 3, 4 in 7292054. Finding 1 is a false positive — 2. Background progress max — Titles/Summaries bars now use separate 3. Async progress ordering — Swapped 4. Footer line count inflated — All 292 unit tests pass; AOT publish is clean. |
Summary
setupandhistory— replacing ad-hocConsole.Write*with prompts, status spinner, rules, grids, and pinned progress.IProgress<ImportProgress>throughSessionImporterso the importer stays console-agnostic whileHistoryCommanddrives a live footer that survives a 1.5K-session scrollback without a table.Console.IsOutputRedirected; the non-TTY path preserves today's plain line-per-session format so scripts and CI logs don't break.What changed
setup—Rulesection headers,Statusspinner around the reachability probe,TextPrompt/SelectionPrompt/ConfirmationPromptreplace the numbered1/2/3menus, final summary as aGrid.--no-promptpath behaviourally unchanged.history— single pinnedProgressTaskfooter (one task, not per-session — 1.5K tasks would be hostile); footer description mutates to showImporting {done}/{total} · {sid8}: {lines}/{total} ↳ subagent {agentId8}. Subagent completions stream above the footer as↳ imported subagent … (N lines)live, instead of the old trailing summary line. Background phase (titles / summaries) gets a separateRule+Progressblock; individual failures stream as✗ title failed for …/✗ summary failed for ….SessionImporter— gains optionalIProgress<ImportProgress>?parameter onImportSessionAsync,SendTranscriptBatches, andSendAgentLifecycle. New events:BatchFlushed(int),SubagentStarted(string),SubagentFinished(string, int).SendAgentLifecyclereturn type widened fromTasktoTask<int>(line count).Spectre.Console.Cli(reflection-heavy; kept hand-rolledArgParsing). No sharedIConsoleUxabstraction. Hook commands untouched.Design & plan
docs/superpowers/specs/2026-04-23-spectre-console-cli-ux-design.mddocs/superpowers/plans/2026-04-23-spectre-console-cli-ux.mdTest plan
IProgresswiring).dotnet publish -c Release 2>&1 | grep -E 'IL[23][01][0-9]{2}'returns zero lines.Rule/Status/✓markup correctly onsetup --no-prompt.kapacitor setup(interactive) — reviewer check.kapacitor history --min-lines 10against a real server — reviewer check.kapacitor history --min-lines 10 > out.log— reviewer check.🤖 Generated with Claude Code