Token导航 LogoToken导航

Agent Skill · 开发

add-wshcmd(添加 wshcmd)

add-wshcmd 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

add-wshcmd 是开发类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为执行命令;许可证信息还不明确,第三方扫描已通过,按安装量在开发类的 21,746 个 Skill 里排第 16,704。

总安装 475周安装 20GitHub Stars 19,398下载 166核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:add-wshcmd(添加 wshcmd)
来源仓库:https://github.com/wavetermdev/waveterm
仓库路径:skills/add-wshcmd
安装命令:
npx skills add https://github.com/wavetermdev/waveterm --skill add-wshcmd
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/wavetermdev/waveterm --skill add-wshcmd

这个 Skill 是做什么的

开发类 Skill 面向写代码、改代码和排查问题,一般会读写仓库文件,部分还会执行构建或测试命令。

add-wshcmd 用于向 Wave Terminal 添加新的 wsh 命令,适合在 Codex、Claude、Cursor、Gemini CLI 中扩展终端功能时使用。适用于需要自定义 CLI 命令并与 Wave Terminal 后端通信的开发场景,支持通过 Cobra 框架注册新命令。

通过创建命令文件并实现 RPC 调用,将新命令集成到现有 wsh 系统中,具体步骤包括文件创建、注册和文档查阅。安装前需确认项目结构、Go 环境及权限范围,注意命令执行可能涉及网络或系统操作,建议核对维护状态。add-wshcmd 属于开发类 Skill,可作为该场景下的辅助能力补充。

来源仓库内路径:skills/add-wshcmd

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/wavetermdev/waveterm --skill add-wshcmd;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要执行命令,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

add-wshcmd 怎么安装?

在终端执行 npx skills add https://github.com/wavetermdev/waveterm --skill add-wshcmd。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

add-wshcmd 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

add-wshcmd 需要什么权限?

权限级别是执行命令。安装流程涉及命令执行,可能通过 npx skills add https://github.com/wavetermdev/waveterm --skill add-wshcmd 联网下载 Skill 或依赖。用户安装前应确认命令来源、仓库内容和执行环境。

add-wshcmd 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

add-wshcmd 用的人多吗?

公开数据是累计安装 475 次,近一周安装 20 次,下载 166 次,GitHub 19,398 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

add-wshcmd 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。
  • 来源仓库有 19,398 星,维护活跃度相对可查。

先想清楚的地方

  • 需要执行命令,装到常用环境前要先评估影响面。
  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理开发类任务、又不想每次都手写提示词的人。

不太适合

  • 不能接受 Skill 在你机器上执行命令的人。
  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

开发类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
skill-vetter

技能审查员 · OpenClaw

8,779,018986操作浏览器MIT-0通过
ontology

本体论 · OpenClaw

6,636,960576只读MIT-0通过
entra-app-registration

内部应用程序注册 · Codex、Claude

6,631,872764接触敏感数据未明确通过
entra-app-registration

内部应用程序注册 · Codex、Claude

3,757,680201接触敏感数据未明确通过
nano-banana-pro

纳米香蕉专业版 · OpenClaw

3,526,898364接触敏感数据MIT-0有提醒
add-wshcmd

添加 wshcmd · 本页

47519,398执行命令未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Adding a New wsh Command to Wave Terminal

This guide explains how to add a new command to the wsh CLI tool.

wsh Command System Overview

Wave Terminal's wsh command provides CLI access to Wave Terminal features. The system uses:

  1. Cobra Framework - CLI command structure and parsing
  2. Command Files - Individual command implementations in cmd/wsh/cmd/wshcmd-*.go
  3. RPC Client - Communication with Wave Terminal backend via RpcClient
  4. Activity Tracking - Telemetry for command usage analytics
  5. Documentation - User-facing docs in docs/docs/wsh-reference.mdx

Commands are registered in their init() functions and execute through the Cobra framework.

Step-by-Step Guide

Step 1: Create Command File

Create a new file in cmd/wsh/cmd/ named wshcmd-[commandname].go:

// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

package cmd

import (
    "fmt"

    "github.com/spf13/cobra"
    "github.com/wavetermdev/waveterm/pkg/wshrpc"
    "github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
)

var myCommandCmd = &cobra.Command{
    Use:   "mycommand [args]",
    Short: "Brief description of what this command does",
    Long: `Detailed description of the command.
Can include multiple lines and examples of usage.`,
    RunE:                  myCommandRun,
    PreRunE:               preRunSetupRpcClient,  // Include if command needs RPC
    DisableFlagsInUseLine: true,
}

// Flag variables
var (
    myCommandFlagExample string
    myCommandFlagVerbose bool
)

func init() {
    // Add command to root
    rootCmd.AddCommand(myCommandCmd)

    // Define flags
    myCommandCmd.Flags().StringVarP(&myCommandFlagExample, "example", "e", "", "example flag description")
    myCommandCmd.Flags().BoolVarP(&myCommandFlagVerbose, "verbose", "v", false, "enable verbose output")
}

func myCommandRun(cmd *cobra.Command, args []string) (rtnErr error) {
    // Always track activity for telemetry
    defer func() {
        sendActivity("mycommand", rtnErr == nil)
    }()

    // Validate arguments
    if len(args) == 0 {
        OutputHelpMessage(cmd)
        return fmt.Errorf("requires at least one argument")
    }

    // Command implementation
    fmt.Printf("Command executed successfully\n")
    return nil
}

File Naming Convention:

  • Use wshcmd-[commandname].go format
  • Use lowercase, hyphenated names for multi-word commands
  • Examples: wshcmd-getvar.go, wshcmd-setmeta.go, wshcmd-ai.go

Step 2: Command Structure

Basic Command Structure
var myCommandCmd = &cobra.Command{
    Use:   "mycommand [required] [optional...]",
    Short: "One-line description (shown in help)",
    Long:  `Detailed multi-line description`,

    // Argument validation
    Args:    cobra.MinimumNArgs(1),  // Or cobra.ExactArgs(1), cobra.NoArgs, etc.

    // Execution function
    RunE:    myCommandRun,

    // Pre-execution setup (if needed)
    PreRunE: preRunSetupRpcClient,  // Sets up RPC client for backend communication

    // Example usage (optional)
    Example: "  wsh mycommand foo\n  wsh mycommand --flag bar",

    // Disable flag notation in usage line
    DisableFlagsInUseLine: true,
}

Key Fields:

  • Use: Command name and argument pattern
  • Short: Brief description for command list
  • Long: Detailed description shown in help
  • Args: Argument validator (optional)
  • RunE: Main execution function (returns error)
  • PreRunE: Setup function that runs before RunE
  • Example: Usage examples (optional)
  • DisableFlagsInUseLine: Clean up help display
When to Use PreRunE

Include PreRunE: preRunSetupRpcClient if your command:

  • Communicates with the Wave Terminal backend
  • Needs access to RpcClient
  • Requires JWT authentication (WAVETERM_JWT env var)
  • Makes RPC calls via wshclient.*Command() functions

Don't include PreRunE for commands that:

  • Only manipulate local state
  • Don't need backend communication
  • Are purely informational/local operations

Step 3: Implement Command Logic

Command Function Pattern
func myCommandRun(cmd *cobra.Command, args []string) (rtnErr error) {
    // Step 1: Always track activity (for telemetry)
    defer func() {
        sendActivity("mycommand", rtnErr == nil)
    }()

    // Step 2: Validate arguments and flags
    if len(args) != 1 {
        OutputHelpMessage(cmd)
        return fmt.Errorf("requires exactly one argument")
    }

    // Step 3: Parse/prepare data
    targetArg := args[0]

    // Step 4: Make RPC call if needed
    result, err := wshclient.SomeCommand(RpcClient, wshrpc.CommandSomeData{
        Field: targetArg,
    }, &wshrpc.RpcOpts{Timeout: 2000})
    if err != nil {
        return fmt.Errorf("executing command: %w", err)
    }

    // Step 5: Output results
    fmt.Printf("Result: %s\n", result)
    return nil
}

Important Patterns:

  1. Activity Tracking: Always include deferred sendActivity() call defer func() {sendActivity("commandname", rtnErr == nil)}()
  2. Error Handling: Return errors, don't call os.Exit() if err!= nil {return fmt.Errorf("context: %w", err)}
  3. Output: Use standard fmt package for output fmt.Printf("Success message\n") fmt.Fprintf(os.Stderr, "Error message\n")
  4. Help Messages: Show help when arguments are invalid if len(args) == 0 {OutputHelpMessage(cmd) return fmt.Errorf("requires arguments")}
  5. Exit Codes: Set custom exit code via WshExitCode if notFound {WshExitCode = 1 return nil // Don't return error, just set exit code}

Step 4: Define Flags

Add flags in the init() function:

var (
    // Declare flag variables at package level
    myCommandFlagString string
    myCommandFlagBool   bool
    myCommandFlagInt    int
)

func init() {
    rootCmd.AddCommand(myCommandCmd)

    // String flag with short version
    myCommandCmd.Flags().StringVarP(&myCommandFlagString, "name", "n", "default", "description")

    // Boolean flag
    myCommandCmd.Flags().BoolVarP(&myCommandFlagBool, "verbose", "v", false, "enable verbose")

    // Integer flag
    myCommandCmd.Flags().IntVar(&myCommandFlagInt, "count", 10, "set count")

    // Flag without short version
    myCommandCmd.Flags().StringVar(&myCommandFlagString, "longname", "", "description")
}

Flag Types:

  • StringVar/StringVarP - String values
  • BoolVar/BoolVarP - Boolean flags
  • IntVar/IntVarP - Integer values
  • The P suffix versions include a short flag name

Flag Naming:

  • Use camelCase for variable names: myCommandFlagName
  • Use kebab-case for flag names: --flag-name
  • Prefix variable names with command name for clarity

Step 5: Working with Block Arguments

Many commands operate on blocks. Use the standard block resolution pattern:

func myCommandRun(cmd *cobra.Command, args []string) (rtnErr error) {
    defer func() {
        sendActivity("mycommand", rtnErr == nil)
    }()

    // Resolve block using the -b/--block flag
    fullORef, err := resolveBlockArg()
    if err != nil {
        return err
    }

    // Use the blockid in RPC call
    err = wshclient.SomeCommand(RpcClient, wshrpc.CommandSomeData{
        BlockId: fullORef.OID,
    }, &wshrpc.RpcOpts{Timeout: 2000})
    if err != nil {
        return fmt.Errorf("command failed: %w", err)
    }

    return nil
}

Block Resolution:

  • The -b/--block flag is defined globally in wshcmd-root.go
  • resolveBlockArg() resolves the block argument to a full ORef
  • Supports: this, tab, full UUIDs, 8-char prefixes, block numbers
  • Default is "this" (current block)

Alternative: Manual Block Resolution

// Get tab ID from environment
tabId := os.Getenv("WAVETERM_TABID")
if tabId == "" {
    return fmt.Errorf("WAVETERM_TABID not set")
}

// Create route for tab-level operations
route := wshutil.MakeTabRouteId(tabId)

// Use route in RPC call
err := wshclient.SomeCommand(RpcClient, commandData, &wshrpc.RpcOpts{
    Route:   route,
    Timeout: 2000,
})

Step 6: Making RPC Calls

Use the wshclient package to make RPC calls:

import (
    "github.com/wavetermdev/waveterm/pkg/wshrpc"
    "github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
)

// Simple RPC call
result, err := wshclient.GetMetaCommand(RpcClient, wshrpc.CommandGetMetaData{
    ORef: *fullORef,
}, &wshrpc.RpcOpts{Timeout: 2000})
if err != nil {
    return fmt.Errorf("getting metadata: %w", err)
}

// RPC call with routing
err := wshclient.SetMetaCommand(RpcClient, wshrpc.CommandSetMetaData{
    ORef: *fullORef,
    Meta: metaMap,
}, &wshrpc.RpcOpts{
    Route:   route,
    Timeout: 5000,
})
if err != nil {
    return fmt.Errorf("setting metadata: %w", err)
}

RPC Options:

  • Timeout: Request timeout in milliseconds (typically 2000-5000)
  • Route: Route ID for targeting specific components
  • Available routes: wshutil.ControlRoute, wshutil.MakeTabRouteId(tabId)

Step 7: Add Documentation

Add your command to docs/docs/wsh-reference.mdx:

## mycommand

Brief description of what the command does.

wsh mycommand [args] [flags]


Detailed explanation of the command's purpose and behavior.

Flags:
- `-n, --name <value>` - description of this flag
- `-v, --verbose` - enable verbose output
- `-b, --block <blockid>` - specify target block (default: current block)

Examples:

Basic usage

wsh mycommand arg1

With flags

wsh mycommand --name value arg1

With block targeting

wsh mycommand -b 2 arg1

Complex example

wsh mycommand -v --name "example" arg1 arg2


Additional notes, tips, or warnings about the command.

---

Documentation Guidelines:

  • Place in alphabetical order with other commands
  • Include command signature with argument pattern
  • List all flags with short and long versions
  • Provide practical examples (at least 3-5)
  • Explain common use cases and patterns
  • Add tips or warnings if relevant
  • Use --- separator between commands

Step 8: Test Your Command

Build and test the command:

# Build wsh
task build:wsh

# Or build everything
task build

# Test the command
./bin/wsh/wsh mycommand --help
./bin/wsh/wsh mycommand arg1 arg2

Testing Checklist:

  • Help message displays correctly
  • Required arguments validated
  • Flags work as expected
  • Error messages are clear
  • Success cases work correctly
  • RPC calls complete successfully
  • Output is formatted correctly

Complete Examples

Example 1: Simple Command with No RPC

Use case: A command that prints Wave Terminal version info

Command File (cmd/wsh/cmd/wshcmd-version.go)
// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

package cmd

import (
    "github.com/spf13/cobra"
    "github.com/wavetermdev/waveterm/pkg/wavebase"
)

var versionCmd = &cobra.Command{
    Use:   "version",
    Short: "Print Wave Terminal version",
    RunE:  versionRun,
}

func init() {
    rootCmd.AddCommand(versionCmd)
}

func versionRun(cmd *cobra.Command, args []string) (rtnErr error) {
    defer func() {
        sendActivity("version", rtnErr == nil)
    }()

    fmt.Printf("Wave Terminal %s\n", wavebase.WaveVersion)
    return nil
}
Documentation
## version

Print the current Wave Terminal version.

wsh version


Examples:

Print version

wsh version

Example 2: Command with Flags and RPC

Use case: A command to update block title

Command File (cmd/wsh/cmd/wshcmd-settitle.go)
// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

package cmd

import (
    "fmt"

    "github.com/spf13/cobra"
    "github.com/wavetermdev/waveterm/pkg/wshrpc"
    "github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
)

var setTitleCmd = &cobra.Command{
    Use:   "settitle [title]",
    Short: "Set block title",
    Long:  `Set the title for the current or specified block.`,
    Args:  cobra.ExactArgs(1),
    RunE:  setTitleRun,
    PreRunE: preRunSetupRpcClient,
    DisableFlagsInUseLine: true,
}

var setTitleIcon string

func init() {
    rootCmd.AddCommand(setTitleCmd)
    setTitleCmd.Flags().StringVarP(&setTitleIcon, "icon", "i", "", "set block icon")
}

func setTitleRun(cmd *cobra.Command, args []string) (rtnErr error) {
    defer func() {
        sendActivity("settitle", rtnErr == nil)
    }()

    title := args[0]

    // Resolve block
    fullORef, err := resolveBlockArg()
    if err != nil {
        return err
    }

    // Build metadata map
    meta := make(map[string]interface{})
    meta["title"] = title
    if setTitleIcon != "" {
        meta["icon"] = setTitleIcon
    }

    // Make RPC call
    err = wshclient.SetMetaCommand(RpcClient, wshrpc.CommandSetMetaData{
        ORef: *fullORef,
        Meta: meta,
    }, &wshrpc.RpcOpts{Timeout: 2000})
    if err != nil {
        return fmt.Errorf("setting title: %w", err)
    }

    fmt.Printf("title updated\n")
    return nil
}
Documentation
## settitle

Set the title for a block.

wsh settitle [title]


Update the display title for the current or specified block. Optionally set an icon as well.

Flags:
- `-i, --icon <icon>` - set block icon along with title
- `-b, --block <blockid>` - specify target block (default: current block)

Examples:

Set title for current block

wsh settitle "My Terminal"

Set title and icon

wsh settitle --icon "terminal" "Development Shell"

Set title for specific block

wsh settitle -b 2 "Build Output"

Example 3: Subcommands

Use case: Command with multiple subcommands (like wsh conn)

Command File (cmd/wsh/cmd/wshcmd-mygroup.go)
// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

package cmd

import (
    "fmt"

    "github.com/spf13/cobra"
    "github.com/wavetermdev/waveterm/pkg/wshrpc"
    "github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
)

var myGroupCmd = &cobra.Command{
    Use:   "mygroup",
    Short: "Manage something",
}

var myGroupListCmd = &cobra.Command{
    Use:   "list",
    Short: "List items",
    RunE:  myGroupListRun,
    PreRunE: preRunSetupRpcClient,
}

var myGroupAddCmd = &cobra.Command{
    Use:   "add [name]",
    Short: "Add an item",
    Args:  cobra.ExactArgs(1),
    RunE:  myGroupAddRun,
    PreRunE: preRunSetupRpcClient,
}

func init() {
    // Add parent command
    rootCmd.AddCommand(myGroupCmd)

    // Add subcommands
    myGroupCmd.AddCommand(myGroupListCmd)
    myGroupCmd.AddCommand(myGroupAddCmd)
}

func myGroupListRun(cmd *cobra.Command, args []string) (rtnErr error) {
    defer func() {
        sendActivity("mygroup:list", rtnErr == nil)
    }()

    // Implementation
    fmt.Printf("Listing items...\n")
    return nil
}

func myGroupAddRun(cmd *cobra.Command, args []string) (rtnErr error) {
    defer func() {
        sendActivity("mygroup:add", rtnErr == nil)
    }()

    name := args[0]
    fmt.Printf("Adding item: %s\n", name)
    return nil
}
Documentation
## mygroup

Manage something with subcommands.

### list

List all items.

wsh mygroup list


### add

Add a new item.

wsh mygroup add [name]


Examples:

List items

wsh mygroup list

Add an item

wsh mygroup add "new-item"

Common Patterns

Reading from Stdin

import "io"

func myCommandRun(cmd *cobra.Command, args []string) (rtnErr error) {
    defer func() {
        sendActivity("mycommand", rtnErr == nil)
    }()

    // Check if reading from stdin (using "-" convention)
    var data []byte
    var err error

    if len(args) > 0 && args[0] == "-" {
        data, err = io.ReadAll(os.Stdin)
        if err != nil {
            return fmt.Errorf("reading stdin: %w", err)
        }
    } else {
        // Read from file or other source
        data, err = os.ReadFile(args[0])
        if err != nil {
            return fmt.Errorf("reading file: %w", err)
        }
    }

    // Process data
    fmt.Printf("Read %d bytes\n", len(data))
    return nil
}

JSON File Input

import (
    "encoding/json"
    "io"
)

func loadJSONFile(filepath string) (map[string]interface{}, error) {
    var data []byte
    var err error

    if filepath == "-" {
        data, err = io.ReadAll(os.Stdin)
        if err != nil {
            return nil, fmt.Errorf("reading stdin: %w", err)
        }
    } else {
        data, err = os.ReadFile(filepath)
        if err != nil {
            return nil, fmt.Errorf("reading file: %w", err)
        }
    }

    var result map[string]interface{}
    if err := json.Unmarshal(data, &result); err != nil {
        return nil, fmt.Errorf("parsing JSON: %w", err)
    }

    return result, nil
}

Conditional Output (TTY Detection)

func myCommandRun(cmd *cobra.Command, args []string) (rtnErr error) {
    defer func() {
        sendActivity("mycommand", rtnErr == nil)
    }()

    isTty := getIsTty()

    // Output value
    fmt.Printf("%s", value)

    // Add newline only if TTY (for better piping experience)
    if isTty {
        fmt.Printf("\n")
    }

    return nil
}

Environment Variable Access

func myCommandRun(cmd *cobra.Command, args []string) (rtnErr error) {
    defer func() {
        sendActivity("mycommand", rtnErr == nil)
    }()

    // Get block ID from environment
    blockId := os.Getenv("WAVETERM_BLOCKID")
    if blockId == "" {
        return fmt.Errorf("WAVETERM_BLOCKID not set")
    }

    // Get tab ID from environment
    tabId := os.Getenv("WAVETERM_TABID")
    if tabId == "" {
        return fmt.Errorf("WAVETERM_TABID not set")
    }

    fmt.Printf("Block: %s, Tab: %s\n", blockId, tabId)
    return nil
}

Best Practices

Command Design

  1. Single Responsibility: Each command should do one thing well
  2. Composable: Design commands to work with pipes and other commands
  3. Consistent: Follow existing wsh command patterns and conventions
  4. Documented: Provide clear help text and examples

Error Handling

  1. Context: Wrap errors with context using fmt.Errorf("context: %w", err)
  2. User-Friendly: Make error messages clear and actionable
  3. No Panics: Return errors instead of calling os.Exit() or panic()
  4. Exit Codes: Use WshExitCode for custom exit codes

Output

  1. Structured: Use consistent formatting for output
  2. Quiet by Default: Only output what's necessary
  3. Verbose Flag: Optionally provide -v for detailed output
  4. Stderr for Errors: Use fmt.Fprintf(os.Stderr,...) for error messages

Flags

  1. Short Versions: Provide -x short versions for common flags
  2. Sensible Defaults: Choose defaults that work for most users
  3. Boolean Flags: Use for on/off options
  4. String Flags: Use for values that need user input

RPC Calls

  1. Timeouts: Always specify reasonable timeouts
  2. Error Context: Wrap RPC errors with operation context
  3. Retries: Don't retry automatically; let user retry command
  4. Routes: Use appropriate routes for different operations

Common Pitfalls

1. Forgetting Activity Tracking

Problem: Command usage not tracked in telemetry

Solution: Always include deferred sendActivity() call:

defer func() {
    sendActivity("commandname", rtnErr == nil)
}()

2. Using os.Exit() Instead of Returning Error

Problem: Breaks defer statements and cleanup

Solution: Return errors from RunE function:

// Bad
if err != nil {
    fmt.Fprintf(os.Stderr, "error: %v\n", err)
    os.Exit(1)
}

// Good
if err != nil {
    return fmt.Errorf("operation failed: %w", err)
}

3. Not Validating Arguments

Problem: Command crashes with nil pointer or index out of range

Solution: Validate arguments early and show help:

if len(args) == 0 {
    OutputHelpMessage(cmd)
    return fmt.Errorf("requires at least one argument")
}

4. Forgetting to Add to init()

Problem: Command not available when running wsh

Solution: Always add command in init() function:

func init() {
    rootCmd.AddCommand(myCommandCmd)
}

5. Inconsistent Output

Problem: Inconsistent use of output methods

Solution: Use standard fmt package functions:

// For stdout
fmt.Printf("output\n")

// For stderr
fmt.Fprintf(os.Stderr, "error message\n")

Quick Reference Checklist

When adding a new wsh command:

  • Create cmd/wsh/cmd/wshcmd-[commandname].go
  • Define command struct with Use, Short, Long descriptions
  • Add PreRunE: preRunSetupRpcClient if using RPC
  • Implement command function with activity tracking
  • Add command to rootCmd in init() function
  • Define flags in init() function if needed
  • Add documentation to docs/docs/wsh-reference.mdx
  • Build and test: task build:wsh
  • Test help: wsh [commandname] --help
  • Test all flag combinations
  • Test error cases

Related Files

  • Root Command: cmd/wsh/cmd/wshcmd-root.go - Main command setup and utilities
  • RPC Client: pkg/wshrpc/wshclient/ - Client functions for RPC calls
  • RPC Types: pkg/wshrpc/wshrpctypes.go - RPC request/response data structures
  • Documentation: docs/docs/wsh-reference.mdx - User-facing command reference
  • Examples: cmd/wsh/cmd/wshcmd-*.go - Existing command implementations

平台分布

Codex

33.23%
按下载量换算55

Claude

32.68%
按下载量换算54

Cursor

19.58%
按下载量换算33

Gemini CLI

10.16%
按下载量换算17

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

执行命令

安装流程涉及命令执行,可能通过 npx skills add https://github.com/wavetermdev/waveterm --skill add-wshcmd 联网下载 Skill 或依赖。用户安装前应确认命令来源、仓库内容和执行环境。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills