一键导入
add-command-simple
Add a simple CLI command without JSON output support
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Add a simple CLI command without JSON output support
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Add a new runtime implementation to the kdn runtime system
Guide to using the instances manager API for workspace management and project detection
Guide to understanding and working with the kdn runtime system architecture
Guide to configuring the Podman runtime including image setup, agent configuration, and containerfile generation
Conventional Commit Message Generator
Guide to the autoconf package — how secret detection, filtering, and the runner are wired together, and how to extend the system with new detector types
| name | add-command-simple |
| description | Add a simple CLI command without JSON output support |
| argument-hint | <command-name> |
This skill helps you add a simple CLI command that only provides text output (no JSON support).
Create pkg/cmd/<command>.go with the following structure:
package cmd
import (
"fmt"
"path/filepath"
"github.com/spf13/cobra"
"github.com/openkaiden/kdn/pkg/instances"
// "github.com/openkaiden/kdn/pkg/runtimesetup" // Uncomment if registering runtimes
// "github.com/openkaiden/kdn/pkg/steplogger" // Uncomment if calling runtime methods
// Add other imports as needed
)
type <command>Cmd struct {
// Fields for flags and dependencies
verbose bool
manager instances.Manager
}
func New<Command>Cmd() *cobra.Command {
c := &<command>Cmd{}
cmd := &cobra.Command{
Use: "<command> [args]",
Short: "Short description of the command",
Long: `Long description of what the command does.`,
Example: `# Basic usage
kdn <command> arg1
# With verbose output
kdn <command> arg1 --verbose
# With other flags
kdn <command> arg1 --flag value`,
Args: cobra.ExactArgs(1), // Or NoArgs, MinimumNArgs(1), MaximumNArgs(1), etc.
PreRunE: c.preRun,
RunE: c.run,
}
// Bind flags to struct fields using *Var variants
cmd.Flags().BoolVarP(&c.verbose, "verbose", "v", false, "Show detailed output")
return cmd
}
func (c *<command>Cmd) preRun(cmd *cobra.Command, args []string) error {
// Get global flags
storageDir, err := cmd.Flags().GetString("storage")
if err != nil {
return fmt.Errorf("failed to read --storage flag: %w", err)
}
// Validate arguments (if your command accepts args, validate them here)
// Example: if the command requires a valid path argument
// if len(args) > 0 {
// absPath, err := filepath.Abs(args[0])
// if err != nil {
// return fmt.Errorf("invalid path: %w", err)
// }
// // Additional validation logic...
// }
// Convert paths to absolute if needed
// Create dependencies (manager, etc.)
// Example: Create manager
manager, err := instances.NewManager(storageDir)
if err != nil {
return fmt.Errorf("failed to create manager: %w", err)
}
c.manager = manager
// Register runtimes if your command interacts with workspaces
// (e.g., Start, Stop, Remove, Create operations)
// Commands that only list or query workspaces don't need this
//
// if err := runtimesetup.RegisterAll(manager); err != nil {
// return fmt.Errorf("failed to register runtimes: %w", err)
// }
return nil
}
func (c *<command>Cmd) run(cmd *cobra.Command, args []string) error {
// If your command calls runtime methods (Create, Start, Stop, Remove),
// inject StepLogger into context for user progress feedback:
//
// logger := steplogger.NewTextLogger(cmd.ErrOrStderr())
// defer logger.Complete()
// ctx := steplogger.WithLogger(cmd.Context(), logger)
//
// Then pass ctx to runtime methods:
// info, err := runtime.Start(ctx, workspaceID)
// Perform the command logic
data, err := c.manager.GetData()
if err != nil {
return fmt.Errorf("failed to get data: %w", err)
}
// Output results
cmd.Println("Success message")
if c.verbose {
cmd.Printf("Detailed information: %v\n", data)
}
return nil
}
In pkg/cmd/root.go, add to the NewRootCmd() function:
rootCmd.AddCommand(New<Command>Cmd())
Optional: Assign to a command group
If you want the command to appear in a specific group in the help output:
// Define a group if it doesn't exist yet
rootCmd.AddGroup(&cobra.Group{
ID: "mygroup",
Title: "My Command Group:",
})
// Create and assign command to the group
myCmd := New<Command>Cmd()
myCmd.GroupID = "mygroup"
rootCmd.AddCommand(myCmd)
Existing groups:
main - Main Commands (for commonly used workspace operations)workspace - Workspace Commands (for the workspace parent command)Create pkg/cmd/<command>_test.go:
package cmd
import (
"strings"
"testing"
"github.com/openkaiden/kdn/pkg/cmd/testutil"
"github.com/spf13/cobra"
)
func Test<Command>Cmd_PreRun(t *testing.T) {
t.Parallel()
t.Run("sets fields correctly", func(t *testing.T) {
t.Parallel()
storageDir := t.TempDir()
c := &<command>Cmd{
verbose: true,
}
cmd := &cobra.Command{}
cmd.Flags().String("storage", storageDir, "test storage flag")
err := c.preRun(cmd, []string{})
if err != nil {
t.Fatalf("preRun() failed: %v", err)
}
if c.manager == nil {
t.Error("Expected manager to be created")
}
})
// NOTE: Only add this test if your command validates arguments in preRun
// For commands with Args: cobra.NoArgs, Cobra handles validation,
// so this test is not needed. This example shows how to test
// custom argument validation if your command accepts arguments.
//
// t.Run("validates arguments", func(t *testing.T) {
// t.Parallel()
//
// storageDir := t.TempDir()
//
// c := &<command>Cmd{}
// cmd := &cobra.Command{}
// cmd.Flags().String("storage", storageDir, "test storage flag")
//
// // Test with invalid argument
// args := []string{"invalid-value"}
// err := c.preRun(cmd, args)
//
// if err == nil {
// t.Fatal("Expected error for invalid argument")
// }
// if !strings.Contains(err.Error(), "expected error message") {
// t.Errorf("Expected specific error message, got: %v", err)
// }
// })
}
func Test<Command>Cmd_E2E(t *testing.T) {
t.Parallel()
t.Run("executes successfully", func(t *testing.T) {
t.Parallel()
storageDir := t.TempDir()
rootCmd := NewRootCmd()
var output strings.Builder
rootCmd.SetOut(&output)
rootCmd.SetArgs([]string{"<command>", "--storage", storageDir})
err := rootCmd.Execute()
if err != nil {
t.Fatalf("Execute() failed: %v", err)
}
// Verify output contains expected messages
outputStr := output.String()
if !strings.Contains(outputStr, "Success message") {
t.Errorf("Expected success message in output, got: %s", outputStr)
}
})
t.Run("executes with verbose flag", func(t *testing.T) {
t.Parallel()
storageDir := t.TempDir()
rootCmd := NewRootCmd()
var output strings.Builder
rootCmd.SetOut(&output)
rootCmd.SetArgs([]string{"<command>", "--storage", storageDir, "--verbose"})
err := rootCmd.Execute()
if err != nil {
t.Fatalf("Execute() failed: %v", err)
}
// Verify verbose output
outputStr := output.String()
if !strings.Contains(outputStr, "Detailed information") {
t.Errorf("Expected detailed information in verbose output, got: %s", outputStr)
}
})
}
func Test<Command>Cmd_Examples(t *testing.T) {
t.Parallel()
cmd := New<Command>Cmd()
if cmd.Example == "" {
t.Fatal("Example field should not be empty")
}
commands, err := testutil.ParseExampleCommands(cmd.Example)
if err != nil {
t.Fatalf("Failed to parse examples: %v", err)
}
expectedCount := 3 // Adjust based on your examples
if len(commands) != expectedCount {
t.Errorf("Expected %d example commands, got %d", expectedCount, len(commands))
}
rootCmd := NewRootCmd()
err = testutil.ValidateCommandExamples(rootCmd, cmd.Example)
if err != nil {
t.Errorf("Example validation failed: %v", err)
}
}
# Run tests for the new command
go test ./pkg/cmd -run Test<Command>
# Run all tests
make test
If the command warrants user-facing documentation, update relevant docs.
If your command calls runtime methods (Create, Start, Stop, Remove), you MUST inject a StepLogger into the context to provide user progress feedback.
When to use:
runtime.Create(), runtime.Start(), runtime.Stop(), or runtime.Remove()How to integrate:
"github.com/openkaiden/kdn/pkg/steplogger"
func (c *<command>Cmd) run(cmd *cobra.Command, args []string) error {
// Create and attach logger
logger := steplogger.NewTextLogger(cmd.ErrOrStderr())
defer logger.Complete()
ctx := steplogger.WithLogger(cmd.Context(), logger)
// Call runtime methods with the context
info, err := runtime.Stop(ctx, workspaceID)
if err != nil {
return err
}
// ... rest of implementation
}
Benefits:
See also:
pkg/cmd/workspace_stop.go - Example with Stop operationpkg/cmd/workspace_start.go - Example with Start operation*Var variants (StringVarP, BoolVarP, etc.) to bind flags to struct fieldst.Parallel() as the first linefilepath.Join() and t.TempDir() for all path operationsruntimesetup.RegisterAll(manager) in preRun. Commands that only query or list workspaces don't need this.// Boolean flag
cmd.Flags().BoolVarP(&c.force, "force", "f", false, "Force operation without confirmation")
// String flag
cmd.Flags().StringVarP(&c.format, "format", "o", "text", "Output format")
// Integer flag
cmd.Flags().IntVarP(&c.count, "count", "c", 10, "Number of items")
// String slice flag
cmd.Flags().StringSliceVarP(&c.tags, "tag", "t", nil, "Tags to apply")
// No arguments
Args: cobra.NoArgs,
// Exactly N arguments
Args: cobra.ExactArgs(1),
Args: cobra.ExactArgs(2),
// At least N arguments
Args: cobra.MinimumNArgs(1),
// At most N arguments
Args: cobra.MaximumNArgs(1),
// Range of arguments
Args: cobra.RangeArgs(1, 3),
pkg/cmd/workspace.go - Parent command (simpler pattern)