diff --git a/pkg/cli/copilot-agents.go b/pkg/cli/copilot-agents.go index 2ed6ca9aac..7b3eb05395 100644 --- a/pkg/cli/copilot-agents.go +++ b/pkg/cli/copilot-agents.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + "github.com/githubnext/gh-aw/pkg/console" "github.com/githubnext/gh-aw/pkg/logger" ) @@ -44,7 +45,7 @@ func ensureFileMatchesTemplate(subdir, fileName, templateContent, fileType strin if strings.TrimSpace(existingContent) == expectedContent { copilotAgentsLog.Printf("File is up-to-date: %s", targetPath) if verbose { - fmt.Printf("%s is up-to-date: %s\n", fileType, targetPath) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("%s is up-to-date: %s", fileType, targetPath))) } return nil } @@ -64,9 +65,9 @@ func ensureFileMatchesTemplate(subdir, fileName, templateContent, fileType strin if verbose { if existingContent == "" { - fmt.Printf("Created %s: %s\n", fileType, targetPath) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Created %s: %s", fileType, targetPath))) } else { - fmt.Printf("Updated %s: %s\n", fileType, targetPath) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Updated %s: %s", fileType, targetPath))) } } @@ -100,7 +101,7 @@ func cleanupOldPromptFile(promptFileName string, verbose bool) error { return fmt.Errorf("failed to remove old prompt file: %w", err) } if verbose { - fmt.Printf("Removed old prompt file: %s\n", oldPath) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Removed old prompt file: %s", oldPath))) } } @@ -139,7 +140,7 @@ func cleanupOldCopilotInstructions(verbose bool) error { return fmt.Errorf("failed to remove old instructions file: %w", err) } if verbose { - fmt.Printf("Removed old instructions file: %s\n", oldPath) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Removed old instructions file: %s", oldPath))) } } diff --git a/pkg/cli/mcp_add.go b/pkg/cli/mcp_add.go index 2ea66a8e20..d65a98ce7e 100644 --- a/pkg/cli/mcp_add.go +++ b/pkg/cli/mcp_add.go @@ -148,11 +148,11 @@ func AddMCPTool(workflowFile string, mcpServerID string, registryURL string, tra // Security fix for CWE-312, CWE-315, CWE-359: Avoid logging detailed error messages // that could contain sensitive information from secret references mcpAddLog.Print("Workflow compilation failed") - fmt.Println(console.FormatWarningMessage("Workflow compilation failed. Please check your workflow configuration.")) - fmt.Println(console.FormatInfoMessage("You can fix the issues and run 'gh aw compile' manually")) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Workflow compilation failed. Please check your workflow configuration.")) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("You can fix the issues and run 'gh aw compile' manually")) } else { mcpAddLog.Print("Workflow compiled successfully") - fmt.Println(console.FormatSuccessMessage("Workflow compiled successfully")) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Workflow compiled successfully")) } return nil diff --git a/pkg/cli/mcp_inspect.go b/pkg/cli/mcp_inspect.go index 5b5e85a058..08ab653e49 100644 --- a/pkg/cli/mcp_inspect.go +++ b/pkg/cli/mcp_inspect.go @@ -810,7 +810,7 @@ func spawnMCPInspector(workflowFile string, serverFilter string, verbose bool) e } else { // Direct command mode if config.Command == "" { - fmt.Println(console.FormatWarningMessage(fmt.Sprintf("Skipping server %s: no command specified", config.Name))) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Skipping server %s: no command specified", config.Name))) continue } cmd = exec.Command(config.Command, config.Args...) @@ -837,7 +837,7 @@ func spawnMCPInspector(workflowFile string, serverFilter string, verbose bool) e go func(serverCmd *exec.Cmd, serverName string) { defer wg.Done() if err := serverCmd.Wait(); err != nil && verbose { - fmt.Println(console.FormatWarningMessage(fmt.Sprintf("Server %s exited with error: %v", serverName, err))) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Server %s exited with error: %v", serverName, err))) } }(cmd, config.Name) @@ -851,7 +851,7 @@ func spawnMCPInspector(workflowFile string, serverFilter string, verbose bool) e fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("All stdio servers started successfully")) } - fmt.Println(console.FormatInfoMessage("Configuration details for MCP inspector:")) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Configuration details for MCP inspector:")) for _, config := range mcpConfigs { fmt.Fprintf(os.Stderr, "\nšŸ“” %s (%s):\n", config.Name, config.Type) switch config.Type { diff --git a/pkg/cli/mcp_list_tools.go b/pkg/cli/mcp_list_tools.go index c6f76f2179..95dd6a17cb 100644 --- a/pkg/cli/mcp_list_tools.go +++ b/pkg/cli/mcp_list_tools.go @@ -80,10 +80,9 @@ func ListToolsForMCP(workflowFile string, mcpServerName string, verbose bool) er } // Connect to the MCP server and get its tools - fmt.Printf("%s %s (%s)\n", - console.FormatInfoMessage("šŸ“” Connecting to MCP server:"), + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("šŸ“” Connecting to MCP server: %s (%s)", targetConfig.Name, - targetConfig.Type) + targetConfig.Type))) info, err := connectToMCPServer(*targetConfig, verbose) if err != nil { @@ -140,7 +139,7 @@ func displayToolsList(info *parser.MCPServerInfo, verbose bool) { return } - fmt.Printf("\n%s\n", console.FormatInfoMessage(fmt.Sprintf("šŸ› ļø Available Tools (%d total)", len(info.Tools)))) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("\nšŸ› ļø Available Tools (%d total)", len(info.Tools)))) // Configure options based on verbose flag opts := MCPToolTableOptions{ diff --git a/pkg/cli/mcp_secrets.go b/pkg/cli/mcp_secrets.go index 789e64b02b..1950a336f3 100644 --- a/pkg/cli/mcp_secrets.go +++ b/pkg/cli/mcp_secrets.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "os" "strings" "github.com/githubnext/gh-aw/pkg/console" @@ -36,7 +37,7 @@ func checkAndSuggestSecrets(toolConfig map[string]any, verbose bool) error { mcpSecretsLog.Printf("Found %d required secrets in configuration", len(requiredSecrets)) if verbose { - fmt.Println(console.FormatInfoMessage("Checking repository secrets...")) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Checking repository secrets...")) } // Check each secret using GitHub CLI @@ -47,7 +48,7 @@ func checkAndSuggestSecrets(toolConfig map[string]any, verbose bool) error { // If we get a 403 error, ignore it as requested if strings.Contains(err.Error(), "403") { if verbose { - fmt.Println(console.FormatWarningMessage("Repository secrets check skipped (insufficient permissions)")) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Repository secrets check skipped (insufficient permissions)")) } return nil } @@ -62,14 +63,14 @@ func checkAndSuggestSecrets(toolConfig map[string]any, verbose bool) error { // Suggest CLI commands for missing secrets if len(missingSecrets) > 0 { mcpSecretsLog.Printf("Found %d missing secrets", len(missingSecrets)) - fmt.Println(console.FormatWarningMessage("The following secrets are required but not found in the repository:")) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage("The following secrets are required but not found in the repository:")) for _, secretName := range missingSecrets { - fmt.Println(console.FormatInfoMessage(fmt.Sprintf("To add %s secret:", secretName))) - fmt.Println(console.FormatCommandMessage(fmt.Sprintf("gh secret set %s", secretName))) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("To add %s secret:", secretName))) + fmt.Fprintln(os.Stderr, console.FormatCommandMessage(fmt.Sprintf("gh secret set %s", secretName))) } } else if verbose { mcpSecretsLog.Print("All required secrets are available in repository") - fmt.Println(console.FormatSuccessMessage("All required secrets are available in the repository")) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("All required secrets are available in the repository")) } return nil diff --git a/pkg/cli/remove_command.go b/pkg/cli/remove_command.go index 72184fbd25..f8aa2b21cd 100644 --- a/pkg/cli/remove_command.go +++ b/pkg/cli/remove_command.go @@ -23,7 +23,7 @@ func RemoveWorkflows(pattern string, keepOrphans bool) error { workflowsDir := getWorkflowsDir() if _, err := os.Stat(workflowsDir); os.IsNotExist(err) { - fmt.Println("No .github/workflows directory found.") + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No .github/workflows directory found.")) return nil } @@ -38,7 +38,7 @@ func RemoveWorkflows(pattern string, keepOrphans bool) error { removeLog.Printf("Found %d workflow files", len(mdFiles)) if len(mdFiles) == 0 { - fmt.Println("No workflow files found to remove.") + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No workflow files found to remove.")) return nil } @@ -46,18 +46,18 @@ func RemoveWorkflows(pattern string, keepOrphans bool) error { // If no pattern specified, list all files for user to see if pattern == "" { - fmt.Println("Available workflows to remove:") + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Available workflows to remove:")) for _, file := range mdFiles { workflowName, _ := extractWorkflowNameFromFile(file) base := filepath.Base(file) name := strings.TrimSuffix(base, ".md") if workflowName != "" { - fmt.Printf(" %-20s - %s\n", name, workflowName) + fmt.Fprintf(os.Stderr, " %-20s - %s\n", name, workflowName) } else { - fmt.Printf(" %s\n", name) + fmt.Fprintf(os.Stderr, " %s\n", name) } } - fmt.Println("\nUsage: " + string(constants.CLIExtensionPrefix) + " remove ") + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("\nUsage: %s remove ", string(constants.CLIExtensionPrefix)))) return nil } @@ -76,7 +76,7 @@ func RemoveWorkflows(pattern string, keepOrphans bool) error { if len(filesToRemove) == 0 { removeLog.Printf("No workflows matched pattern: %q", pattern) - fmt.Printf("No workflows found matching pattern: %s\n", pattern) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("No workflows found matching pattern: %s", pattern))) return nil } @@ -88,33 +88,33 @@ func RemoveWorkflows(pattern string, keepOrphans bool) error { var err error orphanedIncludes, err = previewOrphanedIncludes(filesToRemove, false) if err != nil { - fmt.Printf("Warning: Failed to preview orphaned includes: %v\n", err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to preview orphaned includes: %v", err))) orphanedIncludes = []string{} // Continue with empty list } } // Show what will be removed - fmt.Printf("The following workflows will be removed:\n") + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("The following workflows will be removed:")) for _, file := range filesToRemove { workflowName, _ := extractWorkflowNameFromFile(file) if workflowName != "" { - fmt.Printf(" %s - %s\n", filepath.Base(file), workflowName) + fmt.Fprintf(os.Stderr, " %s - %s\n", filepath.Base(file), workflowName) } else { - fmt.Printf(" %s\n", filepath.Base(file)) + fmt.Fprintf(os.Stderr, " %s\n", filepath.Base(file)) } // Also check for corresponding .lock.yml file in .github/workflows lockFile := stringutil.MarkdownToLockFile(file) if _, err := os.Stat(lockFile); err == nil { - fmt.Printf(" %s (compiled workflow)\n", filepath.Base(lockFile)) + fmt.Fprintf(os.Stderr, " %s (compiled workflow)\n", filepath.Base(lockFile)) } } // Show orphaned includes that will also be removed if len(orphanedIncludes) > 0 { - fmt.Printf("\nThe following orphaned include files will also be removed (suppress with --keep-orphans):\n") + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("\nThe following orphaned include files will also be removed (suppress with --keep-orphans):")) for _, include := range orphanedIncludes { - fmt.Printf(" %s (orphaned include)\n", include) + fmt.Fprintf(os.Stderr, " %s (orphaned include)\n", include) } } @@ -136,9 +136,9 @@ func RemoveWorkflows(pattern string, keepOrphans bool) error { var removedFiles []string for _, file := range filesToRemove { if err := os.Remove(file); err != nil { - fmt.Println(console.FormatWarningMessage(fmt.Sprintf("Failed to remove %s: %v", file, err))) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to remove %s: %v", file, err))) } else { - fmt.Println(console.FormatSuccessMessage(fmt.Sprintf("Removed: %s", filepath.Base(file)))) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Removed: %s", filepath.Base(file)))) removedFiles = append(removedFiles, file) } @@ -146,9 +146,9 @@ func RemoveWorkflows(pattern string, keepOrphans bool) error { lockFile := stringutil.MarkdownToLockFile(file) if _, err := os.Stat(lockFile); err == nil { if err := os.Remove(lockFile); err != nil { - fmt.Printf("Warning: Failed to remove %s: %v\n", lockFile, err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to remove %s: %v", lockFile, err))) } else { - fmt.Printf("Removed: %s\n", filepath.Base(lockFile)) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Removed: %s", filepath.Base(lockFile)))) } } } @@ -156,7 +156,7 @@ func RemoveWorkflows(pattern string, keepOrphans bool) error { // Clean up orphaned include files (if orphan removal is enabled) if len(removedFiles) > 0 && !keepOrphans { if err := cleanupOrphanedIncludes(false); err != nil { - fmt.Printf("Warning: Failed to clean up orphaned includes: %v\n", err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to clean up orphaned includes: %v", err))) } } @@ -177,7 +177,7 @@ func cleanupOrphanedIncludes(verbose bool) error { // No markdown files means we can clean up all includes removeLog.Print("No markdown files found, cleaning up all includes") if verbose { - fmt.Printf("No markdown files found, cleaning up all includes\n") + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No markdown files found, cleaning up all includes")) } return cleanupAllIncludes(verbose) } @@ -189,7 +189,7 @@ func cleanupOrphanedIncludes(verbose bool) error { content, err := os.ReadFile(mdFile) if err != nil { if verbose { - fmt.Printf("Warning: Could not read %s for include analysis: %v\n", mdFile, err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Could not read %s for include analysis: %v", mdFile, err))) } continue } @@ -198,7 +198,7 @@ func cleanupOrphanedIncludes(verbose bool) error { includes, err := findIncludesInContent(string(content), filepath.Dir(mdFile), verbose) if err != nil { if verbose { - fmt.Printf("Warning: Could not analyze includes in %s: %v\n", mdFile, err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Could not analyze includes in %s: %v", mdFile, err))) } continue } @@ -245,10 +245,10 @@ func cleanupOrphanedIncludes(verbose bool) error { includePath := filepath.Join(workflowsDir, include) if err := os.Remove(includePath); err != nil { if verbose { - fmt.Printf("Warning: Failed to remove orphaned include %s: %v\n", include, err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to remove orphaned include %s: %v", include, err))) } } else { - fmt.Printf("Removed orphaned include: %s\n", include) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Removed orphaned include: %s", include))) } } } @@ -290,7 +290,7 @@ func previewOrphanedIncludes(filesToRemove []string, verbose bool) ([]string, er content, err := os.ReadFile(mdFile) if err != nil { if verbose { - fmt.Printf("Warning: Could not read %s for include analysis: %v\n", mdFile, err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Could not read %s for include analysis: %v", mdFile, err))) } continue } @@ -299,7 +299,7 @@ func previewOrphanedIncludes(filesToRemove []string, verbose bool) ([]string, er includes, err := findIncludesInContent(string(content), filepath.Dir(mdFile), verbose) if err != nil { if verbose { - fmt.Printf("Warning: Could not analyze includes in %s: %v\n", mdFile, err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Could not analyze includes in %s: %v", mdFile, err))) } continue } @@ -371,10 +371,10 @@ func cleanupAllIncludes(verbose bool) error { if strings.Contains(relPath, string(filepath.Separator)) { if err := os.Remove(path); err != nil { if verbose { - fmt.Printf("Warning: Failed to remove include %s: %v\n", relPath, err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to remove include %s: %v", relPath, err))) } } else { - fmt.Printf("Removed include: %s\n", relPath) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Removed include: %s", relPath))) } } } diff --git a/pkg/cli/run_workflow_execution.go b/pkg/cli/run_workflow_execution.go index e728ca3ad0..056bc0c208 100644 --- a/pkg/cli/run_workflow_execution.go +++ b/pkg/cli/run_workflow_execution.go @@ -48,7 +48,7 @@ func RunWorkflowOnGitHub(ctx context.Context, workflowIdOrName string, enable bo } if verbose { - fmt.Printf("Running workflow on GitHub Actions: %s\n", workflowIdOrName) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Running workflow on GitHub Actions: %s", workflowIdOrName))) } // Check if gh CLI is available @@ -119,7 +119,7 @@ func RunWorkflowOnGitHub(ctx context.Context, workflowIdOrName string, enable bo wf, err := getWorkflowStatus(workflowIdOrName, repoOverride, verbose) if err != nil { if verbose { - fmt.Printf("Warning: Could not check workflow status: %v\n", err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Could not check workflow status: %v", err))) } } @@ -130,7 +130,7 @@ func RunWorkflowOnGitHub(ctx context.Context, workflowIdOrName string, enable bo wasDisabled = true executionLog.Printf("Workflow %s is disabled, temporarily enabling for this run (id=%d)", workflowIdOrName, wf.ID) if verbose { - fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Workflow '%s' is disabled, enabling it temporarily...", workflowIdOrName))) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Workflow '%s' is disabled, enabling it temporarily...", workflowIdOrName))) } // Enable the workflow enableArgs := []string{"workflow", "enable", strconv.FormatInt(wf.ID, 10)} @@ -142,7 +142,7 @@ func RunWorkflowOnGitHub(ctx context.Context, workflowIdOrName string, enable bo executionLog.Printf("Failed to enable workflow %s: %v", workflowIdOrName, err) return fmt.Errorf("failed to enable workflow '%s': %w", workflowIdOrName, err) } - fmt.Println(console.FormatSuccessMessage(fmt.Sprintf("Enabled workflow: %s", workflowIdOrName))) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Enabled workflow: %s", workflowIdOrName))) } else { executionLog.Printf("Workflow %s is already enabled (state=%s)", workflowIdOrName, wf.State) } @@ -199,7 +199,7 @@ func RunWorkflowOnGitHub(ctx context.Context, workflowIdOrName string, enable bo // Recompile workflow if engine override is provided (only for local workflows) if engineOverride != "" && repoOverride == "" { if verbose { - fmt.Printf("Recompiling workflow with engine override: %s\n", engineOverride) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Recompiling workflow with engine override: %s", engineOverride))) } workflowMarkdownPath := stringutil.LockFileToMarkdown(lockFilePath) @@ -222,16 +222,16 @@ func RunWorkflowOnGitHub(ctx context.Context, workflowIdOrName string, enable bo } if verbose { - fmt.Printf("Successfully recompiled workflow with engine: %s\n", engineOverride) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Successfully recompiled workflow with engine: %s", engineOverride))) } } else if engineOverride != "" && repoOverride != "" { if verbose { - fmt.Printf("Note: Engine override ignored for remote repository workflows\n") + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Note: Engine override ignored for remote repository workflows")) } } if verbose { - fmt.Printf("Using lock file: %s\n", lockFileName) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Using lock file: %s", lockFileName))) } // Check for missing or outdated lock files (when not using --push) @@ -271,7 +271,7 @@ func RunWorkflowOnGitHub(ctx context.Context, workflowIdOrName string, enable bo return fmt.Errorf("failed to push workflow files: %w", err) } - fmt.Println(console.FormatSuccessMessage(fmt.Sprintf("Successfully pushed %d file(s) for workflow %s", len(files), workflowIdOrName))) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Successfully pushed %d file(s) for workflow %s", len(files), workflowIdOrName))) } // Handle secret pushing if requested @@ -377,7 +377,7 @@ func RunWorkflowOnGitHub(ctx context.Context, workflowIdOrName string, enable bo ref = currentBranch executionLog.Printf("Using current branch for workflow run: %s", ref) } else if verbose { - fmt.Printf("Note: Could not determine current branch: %v\n", err) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Note: Could not determine current branch: %v", err))) } } if ref != "" { @@ -412,7 +412,7 @@ func RunWorkflowOnGitHub(ctx context.Context, workflowIdOrName string, enable bo cmdParts = append(cmdParts, "-f", input) } } - fmt.Printf("Executing: %s\n", strings.Join(cmdParts, " ")) + fmt.Fprintln(os.Stderr, console.FormatCommandMessage(strings.Join(cmdParts, " "))) } // Capture both stdout and stderr @@ -444,23 +444,23 @@ func RunWorkflowOnGitHub(ctx context.Context, workflowIdOrName string, enable bo // Display the output from gh workflow run output := strings.TrimSpace(string(stdout)) if output != "" { - fmt.Println(output) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(output)) } - fmt.Printf("Successfully triggered workflow: %s\n", lockFileName) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Successfully triggered workflow: %s", lockFileName))) executionLog.Printf("Workflow triggered successfully: %s", lockFileName) // Try to get the latest run for this workflow to show a direct link // Add a delay to allow GitHub Actions time to register the new workflow run runInfo, runErr := getLatestWorkflowRunWithRetry(lockFileName, repoOverride, verbose) if runErr == nil && runInfo.URL != "" { - fmt.Printf("\nšŸ”— View workflow run: %s\n", runInfo.URL) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("šŸ”— View workflow run: %s", runInfo.URL))) executionLog.Printf("Workflow run URL: %s (ID: %d)", runInfo.URL, runInfo.DatabaseID) // Suggest audit command for analysis - fmt.Printf("\nšŸ’” To analyze this run, use: %s audit %d\n", string(constants.CLIExtensionPrefix), runInfo.DatabaseID) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("šŸ’” To analyze this run, use: %s audit %d", string(constants.CLIExtensionPrefix), runInfo.DatabaseID))) } else if verbose && runErr != nil { - fmt.Printf("Note: Could not get workflow run URL: %v\n", runErr) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Note: Could not get workflow run URL: %v", runErr))) } // Wait for workflow completion if requested (for --repeat or --auto-merge-prs) @@ -567,7 +567,7 @@ func RunWorkflowsOnGitHub(ctx context.Context, workflowNames []string, repeatCou // Function to run all workflows once runAllWorkflows := func() error { - fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Running %d workflow(s)...", len(workflowNames)))) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Running %d workflow(s)...", len(workflowNames)))) // Wait for completion when using --repeat to ensure workflows finish before next iteration waitForCompletion := repeatCount > 0 @@ -582,7 +582,7 @@ func RunWorkflowsOnGitHub(ctx context.Context, workflowNames []string, repeatCou } if len(workflowNames) > 1 { - fmt.Println(console.FormatProgressMessage(fmt.Sprintf("Running workflow %d/%d: %s", i+1, len(workflowNames), workflowName))) + fmt.Fprintln(os.Stderr, console.FormatProgressMessage(fmt.Sprintf("Running workflow %d/%d: %s", i+1, len(workflowNames), workflowName))) } if err := RunWorkflowOnGitHub(ctx, workflowName, enable, engineOverride, repoOverride, refOverride, autoMergePRs, pushSecrets, push, waitForCompletion, inputs, verbose); err != nil { @@ -595,7 +595,7 @@ func RunWorkflowsOnGitHub(ctx context.Context, workflowNames []string, repeatCou } } - fmt.Println(console.FormatSuccessMessage(fmt.Sprintf("Successfully triggered %d workflow(s)", len(workflowNames)))) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Successfully triggered %d workflow(s)", len(workflowNames)))) return nil } diff --git a/pkg/cli/status_command.go b/pkg/cli/status_command.go index 6e148f265d..819293b118 100644 --- a/pkg/cli/status_command.go +++ b/pkg/cli/status_command.go @@ -36,16 +36,16 @@ type WorkflowStatus struct { func StatusWorkflows(pattern string, verbose bool, jsonOutput bool, ref string, labelFilter string, repoOverride string) error { statusLog.Printf("Checking workflow status: pattern=%s, jsonOutput=%v, ref=%s, labelFilter=%s, repo=%s", pattern, jsonOutput, ref, labelFilter, repoOverride) if verbose && !jsonOutput { - fmt.Printf("Checking status of workflow files\n") + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Checking status of workflow files")) if pattern != "" { - fmt.Printf("Filtering by pattern: %s\n", pattern) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Filtering by pattern: %s", pattern))) } } mdFiles, err := getMarkdownWorkflowFiles("") if err != nil { statusLog.Printf("Failed to get markdown workflow files: %v", err) - fmt.Println(err.Error()) + fmt.Fprintln(os.Stderr, console.FormatErrorMessage(err.Error())) return nil } @@ -58,13 +58,13 @@ func StatusWorkflows(pattern string, verbose bool, jsonOutput bool, ref string, fmt.Println(string(jsonBytes)) return nil } - fmt.Println("No workflow files found.") + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No workflow files found.")) return nil } if verbose && !jsonOutput { - fmt.Printf("Found %d markdown workflow files\n", len(mdFiles)) - fmt.Printf("Fetching GitHub workflow status...\n") + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Found %d markdown workflow files", len(mdFiles)))) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Fetching GitHub workflow status...")) } // Get GitHub workflows data @@ -73,16 +73,16 @@ func StatusWorkflows(pattern string, verbose bool, jsonOutput bool, ref string, if err != nil { statusLog.Printf("Failed to fetch GitHub workflows: %v", err) if verbose && !jsonOutput { - fmt.Printf("Verbose: Failed to fetch GitHub workflows: %v\n", err) + fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(fmt.Sprintf("Failed to fetch GitHub workflows: %v", err))) } if !jsonOutput { - fmt.Printf("Warning: Could not fetch GitHub workflow status: %v\n", err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Could not fetch GitHub workflow status: %v", err))) } githubWorkflows = make(map[string]*GitHubWorkflow) } else { statusLog.Printf("Successfully fetched %d GitHub workflows", len(githubWorkflows)) if verbose && !jsonOutput { - fmt.Printf("Successfully fetched %d GitHub workflows\n", len(githubWorkflows)) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Successfully fetched %d GitHub workflows", len(githubWorkflows)))) } } @@ -90,22 +90,22 @@ func StatusWorkflows(pattern string, verbose bool, jsonOutput bool, ref string, var latestRunsByWorkflow map[string]*WorkflowRun if ref != "" { if verbose && !jsonOutput { - fmt.Printf("Fetching latest runs for ref: %s\n", ref) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Fetching latest runs for ref: %s", ref))) } latestRunsByWorkflow, err = fetchLatestRunsByRef(ref, repoOverride, verbose && !jsonOutput) if err != nil { statusLog.Printf("Failed to fetch workflow runs for ref %s: %v", ref, err) if verbose && !jsonOutput { - fmt.Printf("Verbose: Failed to fetch workflow runs for ref: %v\n", err) + fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(fmt.Sprintf("Failed to fetch workflow runs for ref: %v", err))) } if !jsonOutput { - fmt.Printf("Warning: Could not fetch workflow runs for ref '%s': %v\n", ref, err) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Could not fetch workflow runs for ref '%s': %v", ref, err))) } latestRunsByWorkflow = make(map[string]*WorkflowRun) } else { statusLog.Printf("Successfully fetched %d workflow runs for ref %s", len(latestRunsByWorkflow), ref) if verbose && !jsonOutput { - fmt.Printf("Successfully fetched %d workflow runs for ref\n", len(latestRunsByWorkflow)) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Successfully fetched %d workflow runs for ref", len(latestRunsByWorkflow)))) } } } diff --git a/pkg/cli/tool_graph.go b/pkg/cli/tool_graph.go index 2636058a16..d24f6b800d 100644 --- a/pkg/cli/tool_graph.go +++ b/pkg/cli/tool_graph.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "os" "sort" "strings" @@ -218,7 +219,7 @@ func generateToolGraph(processedRuns []ProcessedRun, verbose bool) { for _, seq := range sequences { totalTools += len(seq) } - fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Run %d contributed %d tool sequences with %d total tools", + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Run %d contributed %d tool sequences with %d total tools", run.Run.DatabaseID, len(sequences), totalTools))) } for _, sequence := range sequences { @@ -251,7 +252,7 @@ func extractToolSequencesFromRun(run ProcessedRun, verbose bool) [][]string { for _, seq := range metrics.ToolSequences { totalTools += len(seq) } - fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Extracted %d tool sequences with %d total tool calls from run %d", + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Extracted %d tool sequences with %d total tool calls from run %d", len(metrics.ToolSequences), totalTools, run.Run.DatabaseID))) } } else if len(metrics.ToolCalls) > 0 { @@ -270,7 +271,7 @@ func extractToolSequencesFromRun(run ProcessedRun, verbose bool) [][]string { } if verbose && len(tools) > 0 { - fmt.Println(console.FormatWarningMessage(fmt.Sprintf("No tool sequences found, using fallback with %d tool calls from run %d", + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("No tool sequences found, using fallback with %d tool calls from run %d", len(tools), run.Run.DatabaseID))) } } diff --git a/pkg/cli/trial_command.go b/pkg/cli/trial_command.go index d383606cad..fb16410236 100644 --- a/pkg/cli/trial_command.go +++ b/pkg/cli/trial_command.go @@ -493,22 +493,22 @@ func RunWorkflowTrials(workflowSpecs []string, opts TrialOptions) error { // Display safe outputs to stdout if len(artifacts.SafeOutputs) > 0 { outputBytes, _ := json.MarshalIndent(artifacts.SafeOutputs, "", " ") - fmt.Println(console.FormatSuccessMessage(fmt.Sprintf("=== Safe Outputs from %s ===", parsedSpec.WorkflowName))) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("=== Safe Outputs from %s ===", parsedSpec.WorkflowName))) fmt.Println(string(outputBytes)) - fmt.Println(console.FormatSuccessMessage("=== End of Safe Outputs ===")) + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("=== End of Safe Outputs ===")) } else { - fmt.Println(console.FormatInfoMessage(fmt.Sprintf("=== No Safe Outputs Generated by %s ===", parsedSpec.WorkflowName))) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("=== No Safe Outputs Generated by %s ===", parsedSpec.WorkflowName))) } // Display additional artifact information if available // if len(artifacts.AgentStdioLogs) > 0 { - // fmt.Println(console.FormatInfoMessage(fmt.Sprintf("=== Agent Stdio Logs Available from %s (%d files) ===", parsedSpec.WorkflowName, len(artifacts.AgentStdioLogs)))) + // fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("=== Agent Stdio Logs Available from %s (%d files) ===", parsedSpec.WorkflowName, len(artifacts.AgentStdioLogs)))) // } if len(artifacts.AgenticRunInfo) > 0 { - fmt.Println(console.FormatInfoMessage(fmt.Sprintf("=== Agentic Run Information Available from %s ===", parsedSpec.WorkflowName))) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("=== Agentic Run Information Available from %s ===", parsedSpec.WorkflowName))) } if len(artifacts.AdditionalArtifacts) > 0 { - fmt.Println(console.FormatInfoMessage(fmt.Sprintf("=== Additional Artifacts Available from %s (%d files) ===", parsedSpec.WorkflowName, len(artifacts.AdditionalArtifacts)))) + fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("=== Additional Artifacts Available from %s (%d files) ===", parsedSpec.WorkflowName, len(artifacts.AdditionalArtifacts)))) } fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Trial completed for workflow: %s", parsedSpec.WorkflowName)))