diff --git a/pkg/cmd/claudecodehint.go b/pkg/cmd/claudecodehint.go new file mode 100644 index 00000000..dbffcc80 --- /dev/null +++ b/pkg/cmd/claudecodehint.go @@ -0,0 +1,26 @@ +package cmd + +import ( + "fmt" + "io" + "os" + + "github.com/stripe/stripe-cli/pkg/useragent" +) + +const claudeCodePluginHint = `` + +// emitClaudeCodePluginHint writes a plugin hint to stderr when running inside +// Claude Code. Claude Code strips the hint from command output and may prompt +// the user to install the Stripe plugin. See +// https://code.claude.com/docs/en/plugin-hints for the protocol. +func emitClaudeCodePluginHint() { + writeClaudeCodePluginHint(os.Getenv, os.Stderr) +} + +func writeClaudeCodePluginHint(getEnv func(string) string, w io.Writer) { + if useragent.DetectAIAgent(getEnv) != "claude_code" { + return + } + fmt.Fprintln(w, claudeCodePluginHint) +} diff --git a/pkg/cmd/claudecodehint_test.go b/pkg/cmd/claudecodehint_test.go new file mode 100644 index 00000000..d9303799 --- /dev/null +++ b/pkg/cmd/claudecodehint_test.go @@ -0,0 +1,31 @@ +package cmd + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestWriteClaudeCodePluginHint_EmitsWhenSet(t *testing.T) { + var buf bytes.Buffer + getEnv := func(key string) string { + if key == "CLAUDECODE" { + return "1" + } + return "" + } + + writeClaudeCodePluginHint(getEnv, &buf) + + assert.Equal(t, claudeCodePluginHint+"\n", buf.String()) +} + +func TestWriteClaudeCodePluginHint_SilentWhenUnset(t *testing.T) { + var buf bytes.Buffer + getEnv := func(string) string { return "" } + + writeClaudeCodePluginHint(getEnv, &buf) + + assert.Empty(t, buf.String()) +} diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 55cc46be..69b0d199 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -113,6 +113,8 @@ func showSuggestion() { // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute(ctx context.Context) { + emitClaudeCodePluginHint() + telemetryMetadata := stripe.NewEventMetadata() updatedCtx := stripe.WithEventMetadata(ctx, telemetryMetadata)