diff --git a/common/cliutils/utils.go b/common/cliutils/utils.go index ef221ba4a..b0d0b7e71 100644 --- a/common/cliutils/utils.go +++ b/common/cliutils/utils.go @@ -187,6 +187,11 @@ func ShouldOfferConfig() (bool, error) { if ci { return false, nil } + // Suppress the interactive prompt when invoked by an AI agent. + // Agents cannot respond to stdin prompts; hanging is worse than a fast failure. + if commands.DetectExecutionContext().IsAgent { + return false, nil + } msg := fmt.Sprintf("To avoid this message in the future, set the %s environment variable to true.\n"+ "The CLI commands require the URL and authentication details\n"+ diff --git a/common/cliutils/utils_test.go b/common/cliutils/utils_test.go index 8a1ee317f..fc7d0b7cf 100644 --- a/common/cliutils/utils_test.go +++ b/common/cliutils/utils_test.go @@ -5,6 +5,7 @@ import ( "path/filepath" "testing" + "github.com/jfrog/jfrog-cli-core/v2/common/commands" testUtils "github.com/jfrog/jfrog-cli-core/v2/utils/tests" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" @@ -77,3 +78,68 @@ func TestReadJFrogApplicationKeyFromConfigOrEnv(t *testing.T) { }) } } + +func TestShouldOfferConfig_AgentSkipsPrompt(t *testing.T) { + tests := []struct { + name string + ciEnv string + agentEnv string // CLAUDECODE env var to simulate an agent + wantOffer bool + wantErr bool + }{ + { + name: "agent env set — no prompt", + agentEnv: "1", + wantOffer: false, + }, + { + name: "CI=true — no prompt", + ciEnv: "true", + wantOffer: false, + }, + { + name: "CI=true and agent set — no prompt", + ciEnv: "true", + agentEnv: "1", + wantOffer: false, + }, + { + name: "neither CI nor agent — would prompt (AskYesNo returns false on non-TTY)", + wantOffer: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Ensure no server config exists for the duration of this subtest. + _, cleanUp := testUtils.CreateTestWorkspace(t, t.TempDir()) + defer cleanUp() + + // Reset the memoized execution context so our env var changes take effect. + commands.ResetExecutionContextForTest() + defer commands.ResetExecutionContextForTest() + + if tt.ciEnv != "" { + assert.NoError(t, os.Setenv(coreutils.CI, tt.ciEnv)) + defer func() { _ = os.Unsetenv(coreutils.CI) }() + } else { + _ = os.Unsetenv(coreutils.CI) + } + + if tt.agentEnv != "" { + assert.NoError(t, os.Setenv("CLAUDECODE", tt.agentEnv)) + defer func() { _ = os.Unsetenv("CLAUDECODE") }() + } else { + _ = os.Unsetenv("CLAUDECODE") + } + + offer, err := ShouldOfferConfig() + if tt.wantErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tt.wantOffer, offer) + }) + } +}