Repository: GoogleCloudPlatform/kubectl-ai
File: pkg/tools/kubectl_filter.go (Go)

Code before:
		return "unknown"
	}

	// Check if this is kubectl
	if !strings.Contains(firstArg, "kubectl") {
		klog.V(2).Infof("analyzeCall: first arg does not contain kubectl: %q", firstArg)
		return "unknown"
	}

	klog.V(2).Infof("analyzeCall: found kubectl: %q", firstArg)

	// Get the verb (first non-flag argument after kubectl)
	verbPos := 1 // Start after kubectl at position 0
	for verbPos < len(args) && strings.HasPrefix(args[verbPos], "-") {
		verbPos++
	}

	if verbPos >= len(args) {
		klog.Warningf("analyzeCall: no verb found after kubectl in args: %v", args)
		return "unknown"
	}

	verb := args[verbPos]
	subVerb := ""
	if verbPos+1 < len(args) && !strings.HasPrefix(args[verbPos+1], "-") {
		subVerb = args[verbPos+1]
	}

	hasDryRun := hasDryRunFlag(strings.Join(args, " "))

	// Check standard operations - write operations first (prioritize immediate detection)
	if (writeOps[verb] || writeSubOps[verb][subVerb]) && !hasDryRun {
		klog.V(1).Infof("analyzeCall: write op for verb=%q subVerb=%q", verb, subVerb)
		return "yes"
	}

	// Check read-only operations or dry-run write operations
	if (readOnlyOps[verb] || readOnlySubOps[verb][subVerb]) || ((writeOps[verb] || writeSubOps[verb][subVerb]) && hasDryRun) {
		klog.V(1).Infof("analyzeCall: read op for verb=%q subVerb=%q (dry-run=%v)", verb, subVerb, hasDryRun)
		return "no"
	}

	klog.V(1).Infof("analyzeCall: unknown op for verb=%q subVerb=%q", verb, subVerb)
	return "unknown"
}

func hasDryRunFlag(command string) bool {
	tokens := strings.Fields(command)
	for _, token := range tokens {
		if strings.HasPrefix(token, "--dry-run") {
			return true

Review comment (refactor):
Not sure how likely it is but technically possible to specify the flags between `verb` and `subverb` :) `k --f1 rollout --f2 history ` and this check will miss it.

a few options:

1. we can extract verb and subverb in a single for loop (above) (we stop at verb today)
2. filter out flags from the args to create nonFlagArgs and then simply extract verb, subverb from the filtered args.



Code after:
		return "unknown"
	}

	// Check if this is kubectl
	if !strings.Contains(firstArg, "kubectl") {
		klog.V(2).Infof("analyzeCall: first arg does not contain kubectl: %q", firstArg)
		return "unknown"
	}

	klog.V(2).Infof("analyzeCall: found kubectl: %q", firstArg)

	// Parse kubectl arguments to extract verb, subverb, and flags
	verb, subVerb, hasDryRun := parseKubectlArgs(args[1:])
	if verb == "" {
		klog.Warningf("analyzeCall: no verb found after kubectl in args: %v", args)
		return "unknown"
	}

	// Check standard operations - write operations first (prioritize immediate detection)
	if (writeOps[verb] || writeSubOps[verb][subVerb]) && !hasDryRun {
		klog.V(1).Infof("analyzeCall: write op for verb=%q subVerb=%q", verb, subVerb)
		return "yes"
	}

	// Check read-only operations or dry-run write operations
	if (readOnlyOps[verb] || readOnlySubOps[verb][subVerb]) || ((writeOps[verb] || writeSubOps[verb][subVerb]) && hasDryRun) {
		klog.V(1).Infof("analyzeCall: read op for verb=%q subVerb=%q (dry-run=%v)", verb, subVerb, hasDryRun)
		return "no"
	}

	klog.V(1).Infof("analyzeCall: unknown op for verb=%q subVerb=%q", verb, subVerb)
	return "unknown"
}

// parseKubectlArgs extracts verb, subverb, and dry-run flag from kubectl arguments
func parseKubectlArgs(args []string) (verb, subVerb string, hasDryRun bool) {
	for _, arg := range args {
		if strings.HasPrefix(arg, "--dry-run") {
			hasDryRun = true
		}
		if !strings.HasPrefix(arg, "-") {
			if verb == "" {
				verb = arg
			} else if subVerb == "" {
				subVerb = arg
			}
		}
	}
	return verb, subVerb, hasDryRun
}