.cursor/skills/add-cobra-command/SKILL.md
Step-by-step guide for adding new CLI commands to kubectl-mtv. Use when creating new subcommands, adding verbs or resources, or wiring Cobra commands into the command tree.
npx skillsauth add yaacov/kubectl-mtv add-cobra-commandInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
kubectl-mtv uses a two-layer pattern: thin Cobra wiring in cmd/ delegates to business logic in pkg/cmd/.
cmd/<verb>/<resource>.go -> Cobra command (flags, args, RunE)
pkg/cmd/<verb>/<resource>/ -> Business logic (K8s calls, output)
The parent verb (e.g. cmd/get/get.go) registers subcommands. The root (cmd/kubectl-mtv.go) registers top-level verbs via rootCmd.AddCommand().
Always prefer named flags (--name my-plan) over positional arguments. The MCP layer maps flags directly to JSON flags objects. Positional args require special handling and are harder for LLMs to discover.
Use cobra.NoArgs and define all parameters as flags:
cmd := &cobra.Command{
Use: "host",
Args: cobra.NoArgs,
// ...
}
cmd.Flags().StringVarP(&hostName, "name", "M", "", "Host name")
Create pkg/cmd/<verb>/<resource>/list.go (or appropriate name):
package resource
import (
"context"
"fmt"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericclioptions"
"github.com/yaacov/kubectl-mtv/pkg/util/client"
"github.com/yaacov/kubectl-mtv/pkg/util/output"
"github.com/yaacov/kubectl-mtv/pkg/util/watch"
)
func ListItems(ctx context.Context, configFlags *genericclioptions.ConfigFlags, namespace, outputFormat string, useUTC bool) error {
dynamicClient, err := client.GetDynamicClient(configFlags)
if err != nil {
return fmt.Errorf("failed to get client: %v", err)
}
items, err := dynamicClient.Resource(client.YourGVR).Namespace(namespace).List(ctx, metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to list: %v", err)
}
allItems := make([]map[string]interface{}, 0, len(items.Items))
for _, item := range items.Items {
allItems = append(allItems, createItem(item, useUTC))
}
switch strings.ToLower(outputFormat) {
case "json":
return output.PrintJSONWithEmpty(allItems, "No items found.")
case "yaml":
return output.PrintYAMLWithEmpty(allItems, "No items found.")
default:
return printTable(allItems)
}
}
func List(ctx context.Context, configFlags *genericclioptions.ConfigFlags, namespace string, watchMode bool, outputFormat string, useUTC bool) error {
return watch.WrapWithWatch(watchMode, outputFormat, func() error {
return ListItems(ctx, configFlags, namespace, outputFormat, useUTC)
}, watch.DefaultInterval)
}
Create cmd/<verb>/<resource>.go:
package verb
import (
"context"
"time"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"github.com/yaacov/kubectl-mtv/pkg/cmd/<verb>/<resource>"
"github.com/yaacov/kubectl-mtv/pkg/cmd/help"
"github.com/yaacov/kubectl-mtv/pkg/util/client"
"github.com/yaacov/kubectl-mtv/pkg/util/completion"
"github.com/yaacov/kubectl-mtv/pkg/util/flags"
)
func NewResourceCmd(kubeConfigFlags *genericclioptions.ConfigFlags, globalConfig GlobalConfigGetter) *cobra.Command {
outputFormatFlag := flags.NewOutputFormatTypeFlag()
var watch bool
var name string
cmd := &cobra.Command{
Use: "resource",
Short: "Get resources",
Long: `Detailed description of the resource.`,
Example: ` # List all resources
kubectl-mtv get resources
# Get a specific resource
kubectl-mtv get resource --name my-resource --output yaml`,
Args: cobra.NoArgs,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if !watch {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
defer cancel()
}
namespace := client.ResolveNamespaceWithAllFlag(
globalConfig.GetKubeConfigFlags(), globalConfig.GetAllNamespaces())
return resource.List(ctx, globalConfig.GetKubeConfigFlags(),
namespace, watch, outputFormatFlag.GetValue(), globalConfig.GetUseUTC())
},
}
cmd.Flags().StringVarP(&name, "name", "N", "", "Resource name")
cmd.Flags().VarP(outputFormatFlag, "output", "o", "Output format (table, json, yaml)")
cmd.Flags().BoolVarP(&watch, "watch", "w", false, "Watch for changes")
help.MarkMCPHidden(cmd, "watch")
// Register completions
_ = cmd.RegisterFlagCompletionFunc("output", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return outputFormatFlag.GetValidValues(), cobra.ShellCompDirectiveNoFileComp
})
return cmd
}
In the parent verb file (e.g. cmd/get/get.go):
resourceCmd := NewResourceCmd(kubeConfigFlags, globalConfig)
resourceCmd.Aliases = []string{"resources"}
cmd.AddCommand(resourceCmd)
In pkg/util/client/client.go:
YourGVR = schema.GroupVersionResource{
Group: Group, // "forklift.konveyor.io"
Version: Version, // "v1beta1"
Resource: "yourresources",
}
In pkg/util/completion/completion.go, add a function that lists resource names for shell completion.
In pkg/cmd/help/generator.go, update getCategory():
case "get", "describe", "health", "yournewverb":
return "read"
This controls whether the MCP layer exposes the command via mtv_read or mtv_write.
Commands under known verbs are auto-discovered via help --machine. Run kubectl-mtv help --machine and verify your command appears with correct category and flags.
| Package | Purpose |
|---------|---------|
| pkg/util/flags/ | Shared flag types (OutputFormatTypeFlag, ProviderTypeFlag) |
| pkg/util/client/ | K8s dynamic client, GVR constants, namespace helpers |
| pkg/util/output/ | Table, JSON, YAML printers |
| pkg/util/watch/ | WrapWithWatch for --watch mode |
| pkg/util/completion/ | Shell completion for flag values |
| pkg/util/config/ | GlobalConfigGetter interface |
| pkg/cmd/help/ | MarkMCPHidden, category assignment |
cobra.NoArgs -- pass everything via flags.Aliases = []string{"resources"}).SilenceUsage: true on all commands.help.MarkMCPHidden(cmd, "watch") for interactive/TUI flags.GlobalConfigGetter interface for accessing global config.development
Step-by-step guide for updating the kubev2v/forklift Go dependency in kubectl-mtv. Use when bumping the forklift version, syncing settings or CRD types, or checking for upstream changes.
tools
Reference for building, releasing, publishing images, and deploying kubectl-mtv. Use when creating releases, building binaries or container images, deploying to OpenShift, or updating the Krew plugin index.
tools
Guide for writing and running tests in kubectl-mtv, including Go unit tests, MCP e2e tests (Python/pytest), and linting. Use when adding tests, running the test suite, or debugging test failures.
tools
Guide for adding or modifying MCP tools and understanding command discovery in kubectl-mtv. Use when working on the MCP server, adding new tools, or changing how commands are exposed to AI assistants.