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

Code before:
				if err != nil {
					return fmt.Errorf("getting user home directory: %w", err)
				}
				tokens[i] = homeDir
			}
		}
		configPath = filepath.Join(tokens...)
		configBytes, err := os.ReadFile(configPath)
		if err != nil {
			if os.IsNotExist(err) {
				// ignore
			} else {
				fmt.Fprintf(os.Stderr, "warning: could not load defaults from %q: %v\n", configPath, err)
			}
		}
		if len(configBytes) > 0 {
			if err := o.LoadConfiguration(configBytes); err != nil {
				fmt.Fprintf(os.Stderr, "warning: error loading configuration from %q: %v\n", configPath, err)
			}
		}
	}

	return nil
}

func init() {
	// init logic
	opt.InitDefaults()

	f := rootCmd.Flags()
	f.IntVar(&maxIterations, "max-iterations", 20, "maximum number of iterations agent will try before giving up")
	f.StringVar(&kubeconfigPath, "kubeconfig-path", "", "path to kubeconfig file")
	f.StringVar(&promptTemplateFilePath, "prompt-template-file-path", "", "path to custom prompt template file")
	f.StringVar(&tracePath, "trace-path", "trace.log", "path to the trace file")
	f.BoolVar(&removeWorkDir, "remove-workdir", false, "remove the temporary working directory after execution")

	// This part is gotten from Options struct? May be put all of them there?
	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 {
		print(fmt.Errorf("failed to bind viper flags"))
	}
	viper.SetEnvPrefix("KUBECTL_AI")
	viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))

Review comment (suggestion):
would be good to avoid the flag parsing logic out of `init()` block. Let's have this closer to the defaults initialization in the main().

Code after:
			if token == "{HOME}" {
				homeDir, err := os.UserHomeDir()
				if err != nil {
					return fmt.Errorf("getting user home directory: %w", err)
				}
				tokens[i] = homeDir
			}
		}
		configPath = filepath.Join(tokens...)
		configBytes, err := os.ReadFile(configPath)
		if err != nil {
			if os.IsNotExist(err) {
				// ignore
			} else {
				fmt.Fprintf(os.Stderr, "warning: could not load defaults from %q: %v\n", configPath, err)
			}
		}
		if len(configBytes) > 0 {
			if err := o.LoadConfiguration(configBytes); err != nil {
				fmt.Fprintf(os.Stderr, "warning: error loading configuration from %q: %v\n", configPath, err)
			}
		}
	}

	return nil
}

func main() {
	ctx := context.Background()

	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

	// klog setup must happen before Cobra parses any flags
	// add commandline flags for logging
	klog.InitFlags(nil)

	flag.Set("logtostderr", "false")
	flag.Set("log_file", filepath.Join(os.TempDir(), "kubectl-ai.log"))

	// cobra has to know that we pass pass flags with flag lib, otherwise it creates conflict with flags.parse() method
	rootCmd.PersistentFlags().AddGoFlagSet(flag.CommandLine)
	defer klog.Flush()

	go func() {
		sig := <-sigCh
		fmt.Fprintf(os.Stderr, "Received signal, shutting down... %s\n", sig)
		klog.Flush()
		os.Exit(0)
	}()