Skip to content

Commit 983dcae

Browse files
yotsudaclaude
andcommitted
fix(streams): dedup HostWrite against whitespace-normalized pipeline lines
Format-Table / Format-Wide / Format-List output rendered through Out-String (in Format-McpOutput) and Out-Host (visible terminal) calculate column padding from different sources — Out-String uses the IRawUserInterface buffer width (often 120) while Out-Host uses the actual visible-terminal width. Both renders carry the same data but inter-column spacing differs by one or more characters per row, e.g. pipeline section: "AdobeARMservice ADPSvc" (12 sp) HOST.UI section: "AdobeARMservice ADPSvc" (11 sp) The existing exact-match HashSet and substring-contain checks both miss this shape (neither line is a substring of the other), so every row of a Format-X table that exhibits the drift gets duplicated into the === HOST.UI (direct) === section. Add a parallel whitespace-normalized HashSet (consecutive whitespace runs collapsed to a single space). HostWrite candidate lines are checked against the exact set first, then the normalized set, then the substring containment loop. The normalized check catches the column-width drift cleanly without affecting the existing dedup paths — legitimate host-UI lines that happen to normalise the same as a pipeline line are virtually impossible in practice. Verified on Format-Wide (Get-Service | Format-Wide -Column 4) and large Format-Table (30+ rows of pscustomobject) — both previously showed full duplicates in HOST.UI section, now produce a single clean pipeline-section render. xunit + Pester suites all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8bee551 commit 983dcae

1 file changed

Lines changed: 23 additions & 1 deletion

File tree

PowerShell.MCP/Resources/MCPPollingEngine.ps1

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,12 +653,32 @@ if (-not (Test-Path Variable:global:McpTimer)) {
653653
# section as plain text duplicates.
654654
$known = [System.Collections.Generic.HashSet[string]]::new(
655655
[System.StringComparer]::Ordinal)
656+
# Whitespace-collapsed parallel set: catches the
657+
# Out-String vs Out-Host column-width drift on
658+
# Format-Table / Format-Wide / Format-List.
659+
# Out-String uses the IRawUserInterface buffer
660+
# width (often 120) while Out-Host uses the
661+
# actual visible terminal width, so the same
662+
# row often ends up with one space difference
663+
# in inter-column padding ("xxx yyy" vs
664+
# "xxx yyy"). Exact-match and substring
665+
# checks both miss it. Normalising whitespace
666+
# runs to a single space catches these without
667+
# affecting other dedup paths (legitimate
668+
# host-UI lines that happen to normalise the
669+
# same as a pipeline line are virtually
670+
# impossible in practice).
671+
$knownNorm = [System.Collections.Generic.HashSet[string]]::new(
672+
[System.StringComparer]::Ordinal)
656673
foreach ($src in @($pipelineText, $exceptionText, $infoText, $consoleOutText, $consoleErrText)) {
657674
if (-not [string]::IsNullOrEmpty($src)) {
658675
$srcStripped = $src -replace $vtPattern, ""
659676
foreach ($line in $srcStripped -split "`r?`n") {
660677
$trimmed = $line.Trim()
661-
if ($trimmed) { [void]$known.Add($trimmed) }
678+
if ($trimmed) {
679+
[void]$known.Add($trimmed)
680+
[void]$knownNorm.Add(($trimmed -replace '\s+', ' '))
681+
}
662682
}
663683
}
664684
}
@@ -667,6 +687,8 @@ if (-not (Test-Path Variable:global:McpTimer)) {
667687
$trimmed = $line.Trim()
668688
if (-not $trimmed) { continue }
669689
if ($known.Contains($trimmed)) { continue }
690+
$normalized = $trimmed -replace '\s+', ' '
691+
if ($knownNorm.Contains($normalized)) { continue }
670692
# Check substring containment for partial
671693
# matches: Out-Host renders ErrorRecord
672694
# through a multi-line formatter so a

0 commit comments

Comments
 (0)