From 5dc07590b0fe359246a72eaaed125a57e8cb2912 Mon Sep 17 00:00:00 2001 From: Josh Kappler Date: Fri, 10 Jul 2026 08:30:26 -0700 Subject: [PATCH] fix(rendering): honor suppressWarnings in settled build output suppressWarnings was only applied to streaming compiler-warning fragments. The settled text rendered from the domain result called createStandardDiagnosticSections directly, which always emitted the Warnings section, so warnings still reached the final MCP tool response and filled agent context. Thread the flag from the renderer into the build, build-run, and test diagnostic paths. Errors are still rendered when it is set. Other result kinds are unchanged, since the option covers warnings from the build tools. Fixes #447. --- CHANGELOG.md | 4 ++ .../__tests__/cli-text-renderer.test.ts | 17 +++++++++ .../__tests__/domain-result-text.test.ts | 37 +++++++++++++++++++ src/utils/renderers/cli-text-renderer.ts | 2 + src/utils/renderers/domain-result-text.ts | 26 ++++++++++--- 5 files changed, 80 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1779b0b3..9194236b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - Added `xcodebuildmcp purge` to report and explicitly clean XcodeBuildMCP-managed workspace storage, including opt-in DerivedData cleanup with dry-run and confirmation safeguards. - Added `extraArgs` as a first-class session-default value. Repo config or runtime defaults can now carry common `xcodebuild` flags (for example `-skipPackagePluginValidation` or `-disableAutomaticPackageResolution`) so they don't need repeating on every build or test call. Per-call `extraArgs` replace matching configured flags or build settings and append after non-matching defaults, while an explicit empty array (`extraArgs: []`) clears the defaults for a single call. The session management tools show, set, sync, and clear `extraArgs` alongside the other defaults. +### Fixed + +- Fixed `suppressWarnings` being ignored in settled build, build-run, and test output. The flag was honored only while streaming, so warnings still reached the final MCP tool response ([#447](https://github.com/getsentry/XcodeBuildMCP/issues/447)). + ## [2.6.2] ### Fixed diff --git a/src/utils/renderers/__tests__/cli-text-renderer.test.ts b/src/utils/renderers/__tests__/cli-text-renderer.test.ts index cc8789a4..b2400d4b 100644 --- a/src/utils/renderers/__tests__/cli-text-renderer.test.ts +++ b/src/utils/renderers/__tests__/cli-text-renderer.test.ts @@ -175,6 +175,23 @@ describe('cli-text-renderer', () => { ); }); + it('omits settled build warnings when suppressWarnings is set', () => { + const structuredOutput = buildOutput({ + diagnostics: { + warnings: [{ message: 'unused variable "foo"', location: 'App.swift:12' }], + errors: [], + }, + }); + + const shown = renderCliTextTranscript({ structuredOutput }); + expect(shown).toContain('Warnings (1):'); + expect(shown).toContain('unused variable'); + + const suppressed = renderCliTextTranscript({ structuredOutput, suppressWarnings: true }); + expect(suppressed).not.toContain('Warnings (1):'); + expect(suppressed).not.toContain('unused variable'); + }); + it('renders non-interactive test progress durably and deduplicates repeated counts', () => { const output = renderCliTextTranscript({ items: [ diff --git a/src/utils/renderers/__tests__/domain-result-text.test.ts b/src/utils/renderers/__tests__/domain-result-text.test.ts index db1d314c..ca9f7e49 100644 --- a/src/utils/renderers/__tests__/domain-result-text.test.ts +++ b/src/utils/renderers/__tests__/domain-result-text.test.ts @@ -1,10 +1,29 @@ import { describe, expect, it } from 'vitest'; import type { + BuildResultDomainResult, LaunchResultDomainResult, UiActionResultDomainResult, } from '../../../types/domain-results.ts'; import { renderDomainResultTextItems } from '../domain-result-text.ts'; +function buildResultWithDiagnostics(): BuildResultDomainResult { + return { + kind: 'build-result', + didError: false, + error: null, + summary: { status: 'SUCCEEDED' }, + artifacts: { scheme: 'MyScheme' }, + diagnostics: { + warnings: [{ message: 'unused variable "foo"', location: 'App.swift:12' }], + errors: [{ message: 'cannot find "bar" in scope', location: 'App.swift:20' }], + }, + }; +} + +function sectionTitles(items: ReturnType): string[] { + return items.flatMap((item) => (item.type === 'section' ? [item.title] : [])); +} + function uiActionResult(action: UiActionResultDomainResult['action']): UiActionResultDomainResult { return { kind: 'ui-action-result', @@ -114,6 +133,24 @@ describe('renderDomainResultTextItems', () => { `); }); + it('renders build warnings by default', () => { + const titles = sectionTitles(renderDomainResultTextItems(buildResultWithDiagnostics())); + + expect(titles).toContain('Warnings (1):'); + expect(titles).toContain('Errors (1):'); + }); + + it('omits build warnings when suppressWarnings is set, keeping errors', () => { + const titles = sectionTitles( + renderDomainResultTextItems(buildResultWithDiagnostics(), undefined, { + suppressWarnings: true, + }), + ); + + expect(titles).not.toContain('Warnings (1):'); + expect(titles).toContain('Errors (1):'); + }); + it('renders batch UI action results', () => { expect( renderDomainResultTextItems( diff --git a/src/utils/renderers/cli-text-renderer.ts b/src/utils/renderers/cli-text-renderer.ts index 96e0732c..fa37dae1 100644 --- a/src/utils/renderers/cli-text-renderer.ts +++ b/src/utils/renderers/cli-text-renderer.ts @@ -444,6 +444,7 @@ function createCliTextProcessor(options: CliTextProcessorOptions): TranscriptRen const structuredItems = renderDomainResultTextItems( structuredOutput.result, structuredOutput.renderHints, + { suppressWarnings }, ); const replayItems = sawIncomingHeaderEvent && structuredItems[0]?.type === 'header' @@ -456,6 +457,7 @@ function createCliTextProcessor(options: CliTextProcessorOptions): TranscriptRen const structuredItems = renderDomainResultTextItems( structuredOutput.result, structuredOutput.renderHints, + { suppressWarnings }, ); const replayItems = structuredItems.filter((item) => { if (sawIncomingHeaderEvent && item.type === 'header') return false; diff --git a/src/utils/renderers/domain-result-text.ts b/src/utils/renderers/domain-result-text.ts index a117546e..dfc8f281 100644 --- a/src/utils/renderers/domain-result-text.ts +++ b/src/utils/renderers/domain-result-text.ts @@ -87,6 +87,10 @@ export type TextRendererBlock = export type TextRenderableItem = RenderItem | TextRendererBlock; +export interface DomainResultTextOptions { + suppressWarnings?: boolean; +} + const SESSION_DEFAULT_KEYS = [ 'projectPath', 'workspacePath', @@ -248,6 +252,7 @@ function createMarkedDiagnosticLines( function createStandardDiagnosticSections( diagnostics: BasicDiagnostics | TestDiagnostics | undefined, + suppressWarnings = false, ): SectionTextBlock[] { const sections: SectionTextBlock[] = []; if (!diagnostics) { @@ -265,7 +270,7 @@ function createStandardDiagnosticSections( ), ); } - if (diagnostics.warnings.length > 0) { + if (!suppressWarnings && diagnostics.warnings.length > 0) { sections.push( createSection( `Warnings (${diagnostics.warnings.length}):`, @@ -2013,8 +2018,11 @@ function createCleanResultItems( return items; } -function createDiagnosticSections(result: ToolDomainResult): SectionTextBlock[] { - return createStandardDiagnosticSections(getResultDiagnostics(result)); +function createDiagnosticSections( + result: ToolDomainResult, + suppressWarnings: boolean, +): SectionTextBlock[] { + return createStandardDiagnosticSections(getResultDiagnostics(result), suppressWarnings); } function createSummaryBlock(result: ToolDomainResult): SummaryTextBlock | null { @@ -2072,8 +2080,12 @@ function createTestDiscoveryProgress( function createBuildLikeDiagnosticItems( result: Extract, + suppressWarnings: boolean, ): TextRenderableItem[] { - return createStandardDiagnosticSections('diagnostics' in result ? result.diagnostics : undefined); + return createStandardDiagnosticSections( + 'diagnostics' in result ? result.diagnostics : undefined, + suppressWarnings, + ); } function createBuildRunSyntheticStepItems( @@ -2576,12 +2588,14 @@ export function createNextStepsBlock( export function renderDomainResultTextItems( result: ToolDomainResult, hints?: RenderHints, + options?: DomainResultTextOptions, ): TextRenderableItem[] { const specialCaseItems = createSpecialCaseItems(result, hints); if (specialCaseItems) { return specialCaseItems; } + const suppressWarnings = options?.suppressWarnings ?? false; const items: TextRenderableItem[] = []; if ( result.kind === 'build-result' || @@ -2599,7 +2613,7 @@ export function renderDomainResultTextItems( } } const tailItems = createBuildLikeTailItems(result); - items.push(...createBuildLikeDiagnosticItems(result)); + items.push(...createBuildLikeDiagnosticItems(result, suppressWarnings)); if (result.kind === 'build-run-result') { items.push(...createBuildRunSyntheticStepItems(result)); } @@ -2623,7 +2637,7 @@ export function renderDomainResultTextItems( return items; } - items.push(...createDiagnosticSections(result)); + items.push(...createDiagnosticSections(result, suppressWarnings)); const summary = createSummaryBlock(result); if (summary) { items.push(summary);