Skip to content

Commit 069eeb1

Browse files
yotsudaclaude
andcommitted
fix(streams): keep Write-Progress visible to user, strip overlay from AI capture
Pre-fix v1.8.0 set $ProgressPreference = 'SilentlyContinue' inside the AI command's invocation chain, which kept captured output clean but also hid progress bars from the user watching the visible console — a regression for AI-initiated long-running commands like Compress-Archive and Invoke-WebRequest. This change leaves $ProgressPreference at its default so the bar renders on the visible terminal as usual. Each redraw of pwsh 7's "Minimal" Progress view also writes the bar's text to Console.Out (the visible cursor positioning happens via Win32 SetCursorPosition, which doesn't hit our tee — only the bar's text bytes do). Those bytes form a highly recognizable shape: \e[<sgr>;1m <activity> [ \e[7m <status> <padding> \e[27m <padding> ] \e[0m The reverse-video framing of the status (\e[7m...\e[27m) combined with the bar's literal [ ... ] anchors is distinctive enough that no legitimate user output coincidentally matches. Format-McpOutput strips these blocks from the captured ConsoleOut buffer BEFORE the generic VT-strip (so the inner SGR markers are still present to anchor on), leaving the AI response clean. Verified: progress bar visible on console for Compress-Archive, nested progress (parent/child Ids), and long activity/status strings. Captured AI response shows pipeline output only — no === CONSOLE.OUT (direct) === clutter. No leak into follow-up commands. xunit + Pester suites still green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9e5205a commit 069eeb1

2 files changed

Lines changed: 30 additions & 22 deletions

File tree

PowerShell.MCP/Resources/MCPPollingEngine.ps1

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -104,26 +104,18 @@ if (-not (Test-Path Variable:global:McpTimer)) {
104104
function Invoke-Captured {
105105
[CmdletBinding()]
106106
param([scriptblock]$Block)
107-
# Suppress Write-Progress overlay during AI commands.
108-
# Two reasons:
109-
# 1. The AI cannot meaningfully consume a real-time
110-
# progress bar — the bar updates in place via
111-
# cursor manipulation, not as text the AI can read.
112-
# A static "10% done" snapshot delivered in the
113-
# tool response carries no value.
114-
# 2. ConPTY leaks Progress redraw bytes into the next
115-
# command's Console.Out tee. The first command runs
116-
# Write-Progress, finishes; the second command
117-
# starts and ConPTY emits cleanup bytes for the
118-
# prior overlay, which our tee captures. The user
119-
# sees a CONSOLE.OUT section in command #2's
120-
# response containing the bar from command #1.
121-
# Setting $ProgressPreference here scopes only to the
122-
# AI command's invocation chain (PowerShell uses dynamic
123-
# scope for preference variables), so user-typed
124-
# commands at the visible terminal keep their normal
125-
# progress display.
126-
$ProgressPreference = 'SilentlyContinue'
107+
# Write-Progress renders normally on the visible console
108+
# so the user can see progress for AI-initiated
109+
# long-running commands (Compress-Archive,
110+
# Invoke-WebRequest, etc.). Each redraw of pwsh 7's
111+
# "Minimal" Progress view also writes the bar's text to
112+
# Console.Out, which our tee captures.
113+
# Format-McpOutput's $progressOverlayPattern strips the
114+
# overlay blocks (recognized by their reverse-video
115+
# bracketed-status framing) from the captured ConsoleOut
116+
# buffer before surfacing to the AI, so the response
117+
# stays clean while the visible terminal keeps the
118+
# animated bar.
127119
& $Block
128120
}
129121

@@ -557,10 +549,26 @@ if (-not (Test-Path Variable:global:McpTimer)) {
557549
# If after stripping the result is whitespace only,
558550
# surface as empty so the section is omitted entirely
559551
# rather than rendering an empty header.
552+
# pwsh 7's "Minimal" Progress view writes each redraw to
553+
# Console.Out (not via cursor save/restore — the visible
554+
# console position is fixed by Win32 SetCursorPosition,
555+
# which doesn't go through our tee). The redraw bytes
556+
# captured to ConsoleOut form a highly recognizable
557+
# shape:
558+
# \e[<sgr>;1m <activity> [
559+
# \e[7m <status> <padding> \e[27m
560+
# <padding> ] \e[0m
561+
# The reverse-video framing of the status (\e[7m...\e[27m)
562+
# combined with the bar's literal [ ... ] is distinctive
563+
# enough that no legitimate user output would coincidentally
564+
# match it. Strip these blocks BEFORE the generic VT-strip
565+
# so the inner SGR markers are still there to anchor on.
566+
$progressOverlayPattern = "`e\[\d+(?:;\d+)*m[^`e]*?\[`e\[7m[^`e]*?`e\[27m[^`e]*?\]`e\[0m"
560567
$cleanConsoleStream = {
561568
param([string]$raw)
562569
if (-not $raw) { return "" }
563-
$stripped = $raw -replace $vtPattern, ""
570+
$stripped = $raw -replace $progressOverlayPattern, ""
571+
$stripped = $stripped -replace $vtPattern, ""
564572
$collapsed = $stripped -replace ' {8,}', ' '
565573
$trimmed = $collapsed.TrimEnd("`r","`n", " ", "`t")
566574
if ([string]::IsNullOrWhiteSpace($trimmed)) { return "" }

Staging/ReleaseNotes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
## Improvements
1414
- Real-time streaming preserved through the new capture wiring — items render to the visible console as they arrive, not collected and rendered after the pipeline finishes.
1515
- Color preserved on the visible console for every stream type: red `Write-Error`, yellow `WARNING:`, yellow `VERBOSE:` / `DEBUG:` prefixes, and `Write-Host`'s user-chosen `ForegroundColor`.
16-
- `Write-Progress` is suppressed during AI-initiated commands. Real-time bar text is unreadable to the AI as a snapshot, and ConPTY was leaking progress redraw bytes into subsequent commands' captured output. User-typed commands at the visible terminal keep their normal progress display.
16+
- `Write-Progress` keeps rendering on the visible console for AI-initiated commands (`Compress-Archive`, `Invoke-WebRequest`, etc.) so the user can watch progress. Each redraw of pwsh 7's "Minimal" Progress view also writes the bar text to `Console.Out`; the polling engine recognizes those overlay blocks by their reverse-video bracketed-status framing (`\e[<sgr>m … [\e[7m … \e[27m … ]\e[0m`) and strips them from the captured `=== CONSOLE.OUT (direct) ===` buffer before surfacing to the AI, so the response stays clean.
1717

1818
## Internal
1919
- New `TeeTextWriter` for `[Console]::Out` / `[Console]::Error` tee, written to in parallel with the original streams so visible-console output is unaffected.

0 commit comments

Comments
 (0)