From df7f0e6b238552aed45a863fe123056f1d24bd16 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:02:07 +0000 Subject: [PATCH 1/5] Initial plan From aed3f8063bccf805c761e045385b8cb44e59994d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:31:33 +0000 Subject: [PATCH 2/5] fix: wrap cli stderr output with console helpers Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/mcp_list_tools.go | 20 ++++--- pkg/cli/org_runner.go | 12 ++-- pkg/cli/stderr_console_formatting_test.go | 69 +++++++++++++++++++++++ 3 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 pkg/cli/stderr_console_formatting_test.go diff --git a/pkg/cli/mcp_list_tools.go b/pkg/cli/mcp_list_tools.go index cf48692b3c4..9eb013dcf9a 100644 --- a/pkg/cli/mcp_list_tools.go +++ b/pkg/cli/mcp_list_tools.go @@ -48,7 +48,7 @@ func ListToolsForMCP(workflowFile string, mcpServerName string, verbose bool) er } if verbose { - fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Looking for MCP server '%s' in: %s", mcpServerName, workflowPath))) + fmt.Fprintln(os.Stderr, console.FormatInfoMessageStderr(fmt.Sprintf("Looking for MCP server '%s' in: %s", mcpServerName, workflowPath))) } // Parse the workflow file and extract MCP configurations @@ -74,9 +74,8 @@ func ListToolsForMCP(workflowFile string, mcpServerName string, verbose bool) er // Show available servers if len(mcpConfigs) > 0 { - fmt.Fprintf(os.Stderr, "Available MCP servers: ") serverNames := sliceutil.Map(mcpConfigs, func(config parser.RegistryMCPServerConfig) string { return config.Name }) - fmt.Fprintf(os.Stderr, "%s\n", strings.Join(serverNames, ", ")) + fmt.Fprintln(os.Stderr, console.FormatInfoMessageStderr("Available MCP servers: "+strings.Join(serverNames, ", "))) } return nil } @@ -84,7 +83,7 @@ func ListToolsForMCP(workflowFile string, mcpServerName string, verbose bool) er mcpListToolsLog.Printf("Found MCP server: name=%s, type=%s", targetConfig.Name, targetConfig.Type) // Connect to the MCP server and get its tools - fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("šŸ“” Connecting to MCP server: %s (%s)", + fmt.Fprintln(os.Stderr, console.FormatInfoMessageStderr(fmt.Sprintf("šŸ“” Connecting to MCP server: %s (%s)", targetConfig.Name, targetConfig.Type))) @@ -131,9 +130,13 @@ func findWorkflowsWithMCPServer(workflowsDir string, mcpServerName string, verbo } // Display matching workflows and suggest using one - fmt.Fprintf(os.Stderr, "Found MCP server '%s' in %d workflow(s): %s\n", - mcpServerName, len(matchingWorkflows), strings.Join(matchingWorkflows, ", ")) - fmt.Fprintf(os.Stderr, "\nRun 'gh aw mcp list-tools --server %s' to list tools for a specific workflow\n", mcpServerName) + fmt.Fprintln(os.Stderr, console.FormatInfoMessageStderr( + fmt.Sprintf("Found MCP server '%s' in %d workflow(s): %s", mcpServerName, len(matchingWorkflows), strings.Join(matchingWorkflows, ", ")), + )) + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, console.FormatInfoMessageStderr( + fmt.Sprintf("Run 'gh aw mcp list-tools --server %s' to list tools for a specific workflow", mcpServerName), + )) return nil } @@ -145,7 +148,8 @@ func displayToolsList(info *parser.MCPServerInfo, verbose bool) { return } - fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("\nšŸ› ļø Available Tools (%d total)", len(info.Tools)))) + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, console.FormatInfoMessageStderr(fmt.Sprintf("šŸ› ļø Available Tools (%d total)", len(info.Tools)))) // Configure options based on verbose flag opts := MCPToolTableOptions{ diff --git a/pkg/cli/org_runner.go b/pkg/cli/org_runner.go index 81f0491d081..af2a4f1ea20 100644 --- a/pkg/cli/org_runner.go +++ b/pkg/cli/org_runner.go @@ -84,23 +84,23 @@ type orgRunCallbacks struct { func renderOrgActionSummary(preview orgRepoPreview, action string) { fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Ready to "+action)) - fmt.Fprintf(os.Stderr, " - Repository: %s\n", preview.Repo) + fmt.Fprintln(os.Stderr, console.FormatListItemStderr("Repository: "+preview.Repo)) if preview.TotalWorkflows > 0 { - fmt.Fprintf(os.Stderr, " - Workflows: %d\n", preview.TotalWorkflows) + fmt.Fprintln(os.Stderr, console.FormatListItemStderr(fmt.Sprintf("Workflows: %d", preview.TotalWorkflows))) } if len(preview.Workflows) > 0 { - fmt.Fprintln(os.Stderr, " - Pending workflow updates:") + fmt.Fprintln(os.Stderr, console.FormatListItemStderr("Pending workflow updates:")) for _, wf := range preview.Workflows { - fmt.Fprintf(os.Stderr, " - %s: %s -> %s\n", wf.Name, wf.CurrentRef, wf.LatestRef) + fmt.Fprintln(os.Stderr, console.FormatListItemStderr(fmt.Sprintf("%s: %s -> %s", wf.Name, wf.CurrentRef, wf.LatestRef))) } } currentVersion := normalizeDisplayVersion(preview.CurrentVersion) targetVersion := normalizeDisplayVersion(GetVersion()) if currentVersion != "" { if targetVersion != "" && targetVersion != currentVersion { - fmt.Fprintf(os.Stderr, " - Compiler version: %s -> %s\n", currentVersion, targetVersion) + fmt.Fprintln(os.Stderr, console.FormatListItemStderr(fmt.Sprintf("Compiler version: %s -> %s", currentVersion, targetVersion))) } else { - fmt.Fprintf(os.Stderr, " - Compiler version: %s\n", currentVersion) + fmt.Fprintln(os.Stderr, console.FormatListItemStderr("Compiler version: "+currentVersion)) } } } diff --git a/pkg/cli/stderr_console_formatting_test.go b/pkg/cli/stderr_console_formatting_test.go new file mode 100644 index 00000000000..2dec828579e --- /dev/null +++ b/pkg/cli/stderr_console_formatting_test.go @@ -0,0 +1,69 @@ +//go:build !integration + +package cli + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/github/gh-aw/pkg/console" + "github.com/github/gh-aw/pkg/constants" + "github.com/github/gh-aw/pkg/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRenderOrgActionSummaryUsesConsoleListItems(t *testing.T) { + preview := orgRepoPreview{ + Repo: "octo/repo", + TotalWorkflows: 2, + Workflows: []orgWorkflowPreview{ + {Name: "repo-assist", CurrentRef: "v1.0.0", LatestRef: "v1.1.0"}, + }, + } + + _, stderr := captureOutput(t, func() error { + renderOrgActionSummary(preview, "update") + return nil + }) + + lines := strings.Split(strings.TrimSuffix(stderr, "\n"), "\n") + require.Len(t, lines, 5) + assert.Equal(t, console.FormatInfoMessage("Ready to update"), lines[0]) + assert.Equal(t, console.FormatListItemStderr("Repository: octo/repo"), lines[1]) + assert.Equal(t, console.FormatListItemStderr("Workflows: 2"), lines[2]) + assert.Equal(t, console.FormatListItemStderr("Pending workflow updates:"), lines[3]) + assert.Equal(t, console.FormatListItemStderr("repo-assist: v1.0.0 -> v1.1.0"), lines[4]) +} + +func TestFindWorkflowsWithMCPServerUsesInfoMessages(t *testing.T) { + tmpDir := testutil.TempDir(t, "test-*") + workflowsDir := filepath.Join(tmpDir, constants.GetWorkflowDir()) + require.NoError(t, os.MkdirAll(workflowsDir, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(workflowsDir, "workflow1.md"), []byte(`--- +tools: + github: + allowed: ["create_issue"] +--- +# Workflow 1 +`), 0o644)) + + originalDir, err := os.Getwd() + require.NoError(t, err) + defer func() { + _ = os.Chdir(originalDir) + }() + require.NoError(t, os.Chdir(tmpDir)) + + _, stderr := captureOutput(t, func() error { + return findWorkflowsWithMCPServer(workflowsDir, "github", false) + }) + + lines := strings.Split(strings.TrimSuffix(stderr, "\n"), "\n") + require.Len(t, lines, 3) + assert.Equal(t, console.FormatInfoMessageStderr("Found MCP server 'github' in 1 workflow(s): workflow1"), lines[0]) + assert.Empty(t, lines[1]) + assert.Equal(t, console.FormatInfoMessageStderr("Run 'gh aw mcp list-tools --server github' to list tools for a specific workflow"), lines[2]) +} From 9b078fea0cdaf1c6f830edc42a816abf6e5a86d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:34:18 +0000 Subject: [PATCH 3/5] chore: address stderr formatting review note Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/mcp_list_tools.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/cli/mcp_list_tools.go b/pkg/cli/mcp_list_tools.go index 9eb013dcf9a..22595df6b03 100644 --- a/pkg/cli/mcp_list_tools.go +++ b/pkg/cli/mcp_list_tools.go @@ -133,7 +133,7 @@ func findWorkflowsWithMCPServer(workflowsDir string, mcpServerName string, verbo fmt.Fprintln(os.Stderr, console.FormatInfoMessageStderr( fmt.Sprintf("Found MCP server '%s' in %d workflow(s): %s", mcpServerName, len(matchingWorkflows), strings.Join(matchingWorkflows, ", ")), )) - fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "") fmt.Fprintln(os.Stderr, console.FormatInfoMessageStderr( fmt.Sprintf("Run 'gh aw mcp list-tools --server %s' to list tools for a specific workflow", mcpServerName), )) @@ -148,7 +148,7 @@ func displayToolsList(info *parser.MCPServerInfo, verbose bool) { return } - fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "") fmt.Fprintln(os.Stderr, console.FormatInfoMessageStderr(fmt.Sprintf("šŸ› ļø Available Tools (%d total)", len(info.Tools)))) // Configure options based on verbose flag From 0669171bb334de454909b44ffcf3a7fd89f646eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:36:59 +0000 Subject: [PATCH 4/5] test: clarify stderr formatting regression names Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/stderr_console_formatting_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/cli/stderr_console_formatting_test.go b/pkg/cli/stderr_console_formatting_test.go index 2dec828579e..620ec85bea4 100644 --- a/pkg/cli/stderr_console_formatting_test.go +++ b/pkg/cli/stderr_console_formatting_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestRenderOrgActionSummaryUsesConsoleListItems(t *testing.T) { +func TestRenderOrgActionSummaryUsesConsoleFormatListItemStderr(t *testing.T) { preview := orgRepoPreview{ Repo: "octo/repo", TotalWorkflows: 2, @@ -38,7 +38,7 @@ func TestRenderOrgActionSummaryUsesConsoleListItems(t *testing.T) { assert.Equal(t, console.FormatListItemStderr("repo-assist: v1.0.0 -> v1.1.0"), lines[4]) } -func TestFindWorkflowsWithMCPServerUsesInfoMessages(t *testing.T) { +func TestFindWorkflowsWithMCPServerUsesConsoleFormatInfoMessageStderr(t *testing.T) { tmpDir := testutil.TempDir(t, "test-*") workflowsDir := filepath.Join(tmpDir, constants.GetWorkflowDir()) require.NoError(t, os.MkdirAll(workflowsDir, 0o755)) From ab29367a5840b8b643fa07535bff5ca6cbbd293f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Jul 2026 18:14:40 +0000 Subject: [PATCH 5/5] Fix stderr console formatting: use *Stderr variants throughout Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/cli/mcp_list_tools.go | 2 +- pkg/cli/org_runner.go | 2 +- pkg/cli/stderr_console_formatting_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/cli/mcp_list_tools.go b/pkg/cli/mcp_list_tools.go index 22595df6b03..78dc1a6d7f9 100644 --- a/pkg/cli/mcp_list_tools.go +++ b/pkg/cli/mcp_list_tools.go @@ -95,7 +95,7 @@ func ListToolsForMCP(workflowFile string, mcpServerName string, verbose bool) er mcpListToolsLog.Printf("Connected to MCP server: tools=%d", len(info.Tools)) if verbose { - fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Successfully connected to MCP server")) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessageStderr("Successfully connected to MCP server")) } // Display the tools diff --git a/pkg/cli/org_runner.go b/pkg/cli/org_runner.go index af2a4f1ea20..dd4e4eb51bc 100644 --- a/pkg/cli/org_runner.go +++ b/pkg/cli/org_runner.go @@ -83,7 +83,7 @@ type orgRunCallbacks struct { } func renderOrgActionSummary(preview orgRepoPreview, action string) { - fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Ready to "+action)) + fmt.Fprintln(os.Stderr, console.FormatInfoMessageStderr("Ready to "+action)) fmt.Fprintln(os.Stderr, console.FormatListItemStderr("Repository: "+preview.Repo)) if preview.TotalWorkflows > 0 { fmt.Fprintln(os.Stderr, console.FormatListItemStderr(fmt.Sprintf("Workflows: %d", preview.TotalWorkflows))) diff --git a/pkg/cli/stderr_console_formatting_test.go b/pkg/cli/stderr_console_formatting_test.go index 620ec85bea4..ed910951275 100644 --- a/pkg/cli/stderr_console_formatting_test.go +++ b/pkg/cli/stderr_console_formatting_test.go @@ -31,7 +31,7 @@ func TestRenderOrgActionSummaryUsesConsoleFormatListItemStderr(t *testing.T) { lines := strings.Split(strings.TrimSuffix(stderr, "\n"), "\n") require.Len(t, lines, 5) - assert.Equal(t, console.FormatInfoMessage("Ready to update"), lines[0]) + assert.Equal(t, console.FormatInfoMessageStderr("Ready to update"), lines[0]) assert.Equal(t, console.FormatListItemStderr("Repository: octo/repo"), lines[1]) assert.Equal(t, console.FormatListItemStderr("Workflows: 2"), lines[2]) assert.Equal(t, console.FormatListItemStderr("Pending workflow updates:"), lines[3])