Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pkg/cli/run_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,9 @@ func RunWorkflowOnGitHub(workflowIdOrName string, enable bool, engineOverride st
if runErr == nil && runInfo.URL != "" {
fmt.Printf("\n🔗 View workflow run: %s\n", runInfo.URL)
runLog.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", constants.CLIExtensionPrefix, runInfo.DatabaseID)
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The codebase uses console.FormatInfoMessage() for informational messages with emojis to maintain consistent styling across the CLI. This pattern is used in other parts of the codebase (e.g., mcp_inspect_mcp.go line 533, 568).

Consider using:

fmt.Printf("\n%s\n", console.FormatInfoMessage(fmt.Sprintf("💡 To analyze this run, use: %s audit %d", constants.CLIExtensionPrefix, runInfo.DatabaseID)))

This ensures the message uses the standard info message styling (with ℹ prefix) applied by FormatInfoMessage().

Suggested change
fmt.Printf("\n💡 To analyze this run, use: %s audit %d\n", constants.CLIExtensionPrefix, runInfo.DatabaseID)
fmt.Printf("\n%s\n", console.FormatInfoMessage(fmt.Sprintf("💡 To analyze this run, use: %s audit %d", constants.CLIExtensionPrefix, runInfo.DatabaseID)))

Copilot uses AI. Check for mistakes.
} else if verbose && runErr != nil {
fmt.Printf("Note: Could not get workflow run URL: %v\n", runErr)
}
Expand Down
128 changes: 128 additions & 0 deletions pkg/cli/run_command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package cli

import (
"fmt"
"strings"
"testing"

"github.com/githubnext/gh-aw/pkg/constants"
)

// TestAuditSuggestionMessage tests that the audit suggestion message
// has the expected format and includes the CLI command prefix
func TestAuditSuggestionMessage(t *testing.T) {
// Sample run ID
runID := int64(1234567890)

// Generate the audit suggestion message
auditSuggestion := fmt.Sprintf("💡 To analyze this run, use: %s audit %d", constants.CLIExtensionPrefix, runID)

// Verify the message contains the expected elements
expectedElements := []string{
"💡", // Lightbulb emoji for friendly suggestion
"To analyze this run",
"use:",
constants.CLIExtensionPrefix, // Should be "gh aw"
"audit",
fmt.Sprintf("%d", runID),
}

for _, element := range expectedElements {
if !strings.Contains(auditSuggestion, element) {
t.Errorf("Expected audit suggestion to contain %q, got: %s", element, auditSuggestion)
}
}

// Verify the full command format
expectedCommand := fmt.Sprintf("%s audit %d", constants.CLIExtensionPrefix, runID)
if !strings.Contains(auditSuggestion, expectedCommand) {
t.Errorf("Expected audit suggestion to contain full command %q, got: %s", expectedCommand, auditSuggestion)
}
}

// TestAuditSuggestionMessageFormat tests the exact format of the audit suggestion
func TestAuditSuggestionMessageFormat(t *testing.T) {
tests := []struct {
name string
runID int64
expected string
}{
{
name: "small run ID",
runID: 123,
expected: "💡 To analyze this run, use: gh aw audit 123",
},
{
name: "large run ID",
runID: 9876543210,
expected: "💡 To analyze this run, use: gh aw audit 9876543210",
},
{
name: "typical run ID",
runID: 1234567890,
expected: "💡 To analyze this run, use: gh aw audit 1234567890",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Generate the audit suggestion message
auditSuggestion := fmt.Sprintf("💡 To analyze this run, use: %s audit %d", constants.CLIExtensionPrefix, tt.runID)

// Verify exact format
if auditSuggestion != tt.expected {
t.Errorf("Expected audit suggestion %q, got %q", tt.expected, auditSuggestion)
}

// Verify it's agent-friendly (clear, actionable, no ambiguity)
if !strings.HasPrefix(auditSuggestion, "💡") {
t.Error("Expected audit suggestion to start with lightbulb emoji for friendliness")
}

if !strings.Contains(auditSuggestion, "To analyze this run") {
t.Error("Expected audit suggestion to clearly state the purpose")
}

if !strings.Contains(auditSuggestion, "use:") {
t.Error("Expected audit suggestion to provide clear action keyword 'use:'")
}
})
}
}

// TestAuditSuggestionAgentFriendliness tests that the message is suitable for AI agents
func TestAuditSuggestionAgentFriendliness(t *testing.T) {
runID := int64(1234567890)
auditSuggestion := fmt.Sprintf("💡 To analyze this run, use: %s audit %d", constants.CLIExtensionPrefix, runID)

// Agent-friendly characteristics:
// 1. Clear action verb ("use")
if !strings.Contains(auditSuggestion, "use:") {
t.Error("Expected clear action verb 'use:'")
}

// 2. Specific command (not just a hint)
if !strings.Contains(auditSuggestion, "gh aw audit") {
t.Error("Expected specific command 'gh aw audit'")
}

// 3. Includes the run ID (no need to look it up)
if !strings.Contains(auditSuggestion, fmt.Sprintf("%d", runID)) {
t.Error("Expected run ID to be included in the command")
}

// 4. Not too wordy (agents prefer concise)
wordCount := len(strings.Fields(auditSuggestion))
if wordCount > 15 {
t.Errorf("Expected concise message (< 15 words), got %d words", wordCount)
}

// 5. No ambiguous pronouns or references
// Note: "this run" is acceptable as it refers to the just-triggered workflow run
auditSuggestionLower := strings.ToLower(auditSuggestion)
if strings.Contains(auditSuggestionLower, " it ") ||
strings.Contains(auditSuggestionLower, "this one") ||
strings.Contains(auditSuggestionLower, " that ") {
t.Error("Expected no ambiguous references like 'it', 'this one', 'that'")
}
}
Loading