Repository: GoogleCloudPlatform/kubectl-ai
File: main.go (Go)

Code before:

	return nil
}

func setupViperEnv() {
	viper.SetEnvPrefix(viperEnvPrefix)
	viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
	viper.AutomaticEnv()
}

func runCmd(cmd *cobra.Command, args []string) error {
	// Retrieving the context from cobra
	ctx := cmd.Context()

	var opt Options

	// load YAML config values
	if err := opt.LoadConfigurationFile(); err != nil {
		return fmt.Errorf("failed to load config file: %w", err)
	}

	// populate Options either from CLI flags or ENV vars
	populateOptionsFromViper(&opt)

	// add commandline flags for logging
	klog.InitFlags(nil)
	defer klog.Flush()

	// do this early, before the third-party code logs anything.
	redirectStdLogToKlog()

	// resolve kubeconfig path with priority: flag/env > KUBECONFIG > default path
	if err := resolveKubeConfigPath(&opt); err != nil {
		return fmt.Errorf("failed to resolve kubeconfig path: %w", err)
	}

	if opt.MCPServer {
		if err := startMCPServer(ctx, opt); err != nil {
			return fmt.Errorf("failed to start MCP server: %w", err)
		}
	}

	// After reading stdin, it is consumed
	hasStdInData, err := hasStdInData()
	if err != nil {
		return fmt.Errorf("failed to check if stdin has data: %w", err)
	}

	// Handles positional args or stdin
	queryFromCmd, err := resolveQueryInput(hasStdInData, args)
	if err != nil {

Review comment (suggestion):
This needs to be called before flag parsing is called, so move it before `runCmd` is invoked. I think thats the reason you are seeing logs output by default on stdout/stderr.

Code after:
	f.StringVar(&opt.ProviderID, "llm-provider", opt.ProviderID, "language model provider")
	f.StringVar(&opt.ModelID, "model", opt.ModelID, "language model e.g. gemini-2.0-flash-thinking-exp-01-21, gemini-2.0-flash")
	f.BoolVar(&opt.SkipPermissions, "skip-permissions", opt.SkipPermissions, "(dangerous) skip asking for confirmation before executing kubectl commands that modify resources")
	f.BoolVar(&opt.MCPServer, "mcp-server", opt.MCPServer, "run in MCP server mode")
	f.BoolVar(&opt.EnableToolUseShim, "enable-tool-use-shim", opt.EnableToolUseShim, "enable tool use shim")
	f.BoolVar(&opt.Quiet, "quiet", opt.Quiet, "run in non-interactive mode, requires a query to be provided as a positional argument")

	// viper binds and env var prefixes
	err := viper.BindPFlags(f)
	if err != nil {
		return fmt.Errorf("failed to bind viper flags")
	}

	return nil
}

func setupViperEnv() {
	viper.SetEnvPrefix(viperEnvPrefix)
	viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
	viper.AutomaticEnv()
}

func runCmd(cmd *cobra.Command, args []string) error {
	// Retrieving the context from cobra
	ctx := cmd.Context()

	var opt Options

	// load YAML config values
	if err := opt.LoadConfigurationFile(); err != nil {
		return fmt.Errorf("failed to load config file: %w", err)
	}

	// populate Options either from CLI flags or ENV vars
	populateOptionsFromViper(&opt)

	// do this early, before the third-party code logs anything.
	redirectStdLogToKlog()

	// resolve kubeconfig path with priority: flag/env > KUBECONFIG > default path
	if err := resolveKubeConfigPath(&opt); err != nil {
		return fmt.Errorf("failed to resolve kubeconfig path: %w", err)
	}

	if opt.MCPServer {
		if err := startMCPServer(ctx, opt); err != nil {
			return fmt.Errorf("failed to start MCP server: %w", err)
		}
	}

	// After reading stdin, it is consumed