mcpField
C
cli-ops

cli-ops

github.com/0xdarkmatter开发7836 次安装2 周前更新
代码仓库

关于 cli-ops

面向开发可供 AI 助手和高级用户组合调用的命令行工具的开发者,提供 CLI 设计模式。重点包括可预测的 JSON 输出、标准化错误处理和命令组合。

主要功能

  1. 01命令结构与命名约定
  2. 02JSON 输出和流分离规范
  3. 03语义化退出码与错误格式
  4. 04帮助信息和使用示例规范
  5. 05认证、筛选与分页模式

适用场景

  1. 01设计便于脚本解析的命令输出
  2. 02统一多个子命令的参数形式
  3. 03让自动化脚本按错误类型分支
  4. 04规划认证命令和凭据存储

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/0xdarkmatter/claude-mods --skill cli-ops

安装后在 Claude Code 中输入/cli-ops确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/
.gitkeep
references/
implementation.md
json-schemas.md
scripts/
.gitkeep

详细介绍

面向 Agent 工作流的命令行模式

用于构建命令行工具的模式,方便 AI 助手和高级用户进行命令串联、解析并可靠地使用。

理念

为Agent 工作流构建命令行工具,面向会串联命令、以编程方式解析输出并期望行为可预测的 AI 助手和高级用户。

核心原则
原则含义重要性
自带文档--help 内容全面且始终保持最新大模型无需外部文档即可了解工具能力
可预测所有命令都遵循相同模式学会一次,处处适用
可组合Unix 理念——做好一件事工具可以自然地串联使用
可解析始终提供 --json,且输出始终有效无需使用解析技巧即可供机器读取
默认安静仅输出数据,除非用户要求,否则不添加装饰脚本不会因意外输出而出错
快速失败输入无效时立即报错不会静默失败或只返回部分结果
设计公理
  1. stdout 神圣不可侵犯——仅输出数据。绝不输出进度、日志或装饰内容。
  2. stderr 面向人类——进度条、颜色、表格和警告都输出到这里。
  3. 退出码有明确含义——脚本可以根据失败模式进行分支处理。
  4. 帮助信息包含示例——这是最快的理解途径。
  5. JSON 结构可预测——所有命令都使用相同的结构。

命令架构

结构模式
<tool> [global-options] <resource> <action> [options] [arguments]

每个命令行工具都遵循以下层级:

<tool>
├── --version, --help              # Global flags
├── auth                           # Authentication (if required)
│   ├── login
│   ├── status
│   └── logout
└── <resource>                     # Domain resources (plural nouns)
    ├── list                       # Get many
    ├── get <id>                   # Get one by ID
    ├── create                     # Make new (if supported)
    ├── update <id>                # Modify existing (if supported)
    ├── delete <id>                # Remove (if supported)
    └── <custom-action>            # Domain-specific verbs
命名约定
元素约定有效示例无效示例
工具名称小写,2-12 个字符mytool、datactlMyTool、my-tool-cli
资源复数名词,小写invoices、usersInvoice、user
操作动词,小写list、get、synclisting、getter
长选项kebab-case--dry-run、--output-format--dryRun、--output_format
短选项单个字母-n、-q、-v-num、-quiet
标准资源操作
操作HTTP 等效项返回内容幂等
listGET /resources数组是
get <id>GET /resources/:id对象是
createPOST /resources已创建的对象否
update <id>PATCH /resources/:id已更新的对象是
delete <id>DELETE /resources/:id确认信息是
searchGET /resources?q=数组是

标志和选项

必需标志

每个命令 MUST 支持:

标志短选项行为输出
--help-h显示包含示例的帮助信息帮助文本输出到 stdout,退出码为 0
--json输出机器可读内容JSON 输出到 stdout

根命令 MUST 另外支持:

标志短选项行为输出
--version-V显示版本<tool> <version> 输出到 stdout,退出码为 0
推荐标志
标志短选项类型用途默认值
--quiet-qbool抑制非必要的 stderr 输出false
--verbose-vbool提高详细程度false
--dry-runbool预览但不执行false
--limit-nint返回结果的最大数量20
--output-opath将输出写入文件stdout
--format-fenum输出格式视情况而定
标志行为规则
  1. 布尔标志不接受值:使用 --json,而非 --json=true
  2. 短标志可以组合:-vq 等同于 -v -q
  3. 未知标志会报错:绝不静默忽略
  4. 重复标志:使用最后一个值(不适用时则报错)

输出规范

流分离

这是最关键的规则:

流内容何时
stdout仅数据始终
stderr其他所有内容交互模式

stdout 接收:

  • 设置 --json 时输出 JSON
  • 交互模式下输出精简文本
  • 绝不输出其他内容。

stderr 接收:

  • 进度指示器(旋转图标、进度条)
  • 状态消息(“正在获取……”“完成”)
  • 警告
  • 富文本格式的表格
  • 颜色和装饰
  • 调试信息(--verbose)
交互模式检测
import sys

def is_interactive() -> bool:
    """True if connected to a terminal, not piped."""
    return sys.stdout.isatty() and sys.stderr.isatty()
上下文stdout.isatty()行为
终端True富文本输出到 stderr,摘要输出到 stdout
管道(| jq)False精简输出/JSON 输出到 stdout
重定向(> file)False精简输出到 stdout
--json 标志任意输出 JSON 到 stdout,抑制 stderr 噪声
JSON 输出架构

完整的 JSON 响应模式请参见 references/json-schemas.md。

关键约定:

  • 列表响应:{"data": [...], "meta": {...}}
  • 单项:{"data": {...}}
  • 错误:{"error": {"code": "...", "message": "..."}}
  • ISO 8601 日期、十进制金额、字符串 ID

退出代码

脚本可以依赖的语义化退出代码:

代码名称含义何时
0SUCCESS操作已完成一切正常
1ERROR一般/未知错误意外故障
2AUTH_REQUIRED未通过身份验证没有令牌、令牌已过期
3NOT_FOUND未找到资源ID 不存在
4VALIDATION输入无效参数错误、验证失败
5FORBIDDEN权限被拒绝已通过身份验证,但未获授权
6RATE_LIMITED请求过多API 限流
7CONFLICT状态冲突并发修改、重复项
用法
# Script can branch on exit code
mytool items get item-001 --json
case $? in
  0) echo "Success" ;;
  2) echo "Need to authenticate" && mytool auth login ;;
  3) echo "Item not found" ;;
  *) echo "Error occurred" ;;
esac
实现
# Constants
EXIT_SUCCESS = 0
EXIT_ERROR = 1
EXIT_AUTH_REQUIRED = 2
EXIT_NOT_FOUND = 3
EXIT_VALIDATION = 4
EXIT_FORBIDDEN = 5
EXIT_RATE_LIMITED = 6
EXIT_CONFLICT = 7

# Usage
raise typer.Exit(EXIT_NOT_FOUND)

错误处理

错误输出格式

使用 --json 时,错误会以结构化 JSON 格式输出到 stdout,同时向 stderr 输出消息:

stderr:

Error: Item not found

stdout:

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Item not found",
    "details": {
      "item_id": "bad-id"
    }
  }
}
错误代码
代码退出代码含义
AUTH_REQUIRED2必须先进行身份验证
TOKEN_EXPIRED2令牌需要刷新
FORBIDDEN5权限不足
NOT_FOUND3资源不存在
VALIDATION_ERROR4输入无效
INVALID_ARGUMENT4参数值错误
MISSING_ARGUMENT4缺少必需参数
RATE_LIMITED6请求过多
CONFLICT7状态冲突
ALREADY_EXISTS7资源重复
INTERNAL_ERROR1意外错误
API_ERROR1上游 API 失败
NETWORK_ERROR1连接失败
实现模式
def _error(
    message: str,
    code: str = "ERROR",
    exit_code: int = EXIT_ERROR,
    details: dict = None,
    as_json: bool = False,
):
    """Output error and exit."""
    error_obj = {"error": {"code": code, "message": message}}
    if details:
        error_obj["error"]["details"] = details

    if as_json:
        print(json.dumps(error_obj, indent=2))

    # Always print human message to stderr
    console.print(f"[red]Error:[/red] {message}")
    raise typer.Exit(exit_code)

帮助系统

帮助要求

每个 --help 输出都必须包含:

  1. 简要说明(一行)
  2. 用法语法
  3. 带说明的选项
  4. 示例(对发现功能至关重要)
帮助格式模板
<one-line description>

Usage: <tool> <resource> <action> [OPTIONS] [ARGS]

Arguments:
  <arg>          Description of positional argument

Options:
  -s, --status TEXT    Filter by status
  -n, --limit INTEGER  Max results [default: 20]
  --json               Output as JSON
  -h, --help           Show this help

Examples:
  <tool> <resource> <action>
  <tool> <resource> <action> --status active
  <tool> <resource> <action> --json | jq '.[0]'
示例至关重要

示例应展示:

  1. 基本用法——最简单的调用方式
  2. 常用筛选条件——最常用的选项
  3. JSON 管道传递——如何与 jq 串联
  4. 实际场景——真实用例

身份验证

身份验证命令

需要身份验证的工具必须实现:

<tool> auth login      # Interactive authentication
<tool> auth status     # Check current state
<tool> auth logout     # Clear credentials
凭据存储优先级

推荐: 使用操作系统密钥环,并配置回退方案,以实现最高安全性

  1. 环境变量(CI/CD、测试)

    • MYTOOL_API_TOKEN 或类似变量
    • 优先级最高,覆盖所有其他来源
  2. 操作系统密钥环(主要存储方式,安全)

    • Windows:Credential Manager
    • macOS:Keychain
    • Linux:Secret Service(GNOME Keyring、KWallet)
    • 静态加密,并按用户隔离
  3. .env 文件(开发环境回退方案)

    • 以纯文本形式存储在当前目录
    • 便于本地开发
    • 必须添加到 .gitignore

依赖项:

dependencies = [
    "keyring>=24.0.0",      # OS keyring access
    "python-dotenv>=1.0.0", # .env file support
]

简单替代方案: 仅使用 ~/.config/<tool>/ 下的配置文件

  • 适用于不含敏感凭据的工具
  • 或操作系统密钥环会增加过多复杂性的情况

完整的凭据存储实现请参阅 references/implementation.md。

未通过身份验证时的行为

需要身份验证但未提供凭据时:

$ mytool items list
Error: Not authenticated. Run: mytool auth login
# exit code: 2
$ mytool items list --json
# stderr: Error: Not authenticated. Run: mytool auth login
{"error": {"code": "AUTH_REQUIRED", "message": "Not authenticated. Run: mytool auth login"}}
# exit code: 2

数据约定

日期处理

输入(灵活): 接受多种格式,方便用户使用

格式示例说明
ISO 日期2025-01-15精确日期
ISO 日期时间2025-01-15T10:30:00Z精确日期时间
相对日期today、yesterday、tomorrow当天、前一天、后一天
相对时间last、this(结合上下文)上一时段、当前时段

输出(严格): 始终输出 ISO 8601 格式

{
  "created_at": "2025-01-15T10:30:00Z",
  "due_date": "2025-02-15",
  "month": "2025-01"
}
金额
  • 存储为十进制数,而非分
  • 金额不明确时,注明货币
  • 不要进行格式化(JSON 中不要包含 "$" 或 ",")
{
  "total": 1250.50,
  "currency": "USD"
}
ID
  • 始终使用字符串(即使 ID 是数字)
  • 保留来源中的确切格式
{
  "id": "abc_123",
  "legacy_id": "12345"
}
枚举
  • 在 JSON 中使用 UPPER_SNAKE_CASE
  • 输入不区分大小写
# All equivalent
--status DRAFT
--status draft
--status Draft
{"status": "IN_PROGRESS"}

筛选与分页

常见筛选模式
# By status
--status DRAFT
--status active,pending    # Multiple values

# By date range
--from 2025-01-01 --to 2025-01-31
--month 2025-01
--month last

# By related entity
--user "Alice"
--project "Project X"

# Text search
--search "keyword"
-q "keyword"

# Boolean filters
--archived
--no-archived
--include-deleted
分页
# Limit results
--limit 50
-n 50

# Offset-based
--page 2
--offset 20

# Cursor-based
--cursor "eyJpZCI6MTIzfQ=="
--after "item_123"

实现

完整的 Python 实现模板请参阅 references/implementation.md,其中包括:

  • 使用 Typer 的 CLI 骨架
  • 使用 httpx 的客户端模式
  • 错误处理
  • 身份验证流程
  • 测试模式

反模式

❌ 输出污染
# BAD: Progress to stdout
$ bad-tool items list --json
Fetching items...
[{"id": "1"}]
Done!

# GOOD: Only JSON to stdout
$ good-tool items list --json
[{"id": "1"}]
❌ 交互式提示
# BAD: Prompts in non-interactive context
$ bad-tool items create
Enter name: _

# GOOD: Fail fast with required flags
$ good-tool items create
Error: --name is required
❌ 标志不一致
# BAD: Different flags for same concept
$ tool1 list -j
$ tool2 list --format=json

# GOOD: Same flags everywhere
$ tool1 list --json
$ tool2 list --json
❌ 静默失败
# BAD: Success exit code on failure
$ bad-tool items delete bad-id
Item not found
$ echo $?
0

# GOOD: Semantic exit code
$ good-tool items delete bad-id
Error: Item not found: bad-id
$ echo $?
3

快速参考

必备检查清单
  • <tool> --version
  • <tool> --help 并附带示例
  • <tool> <resource> list [--json]
  • <tool> <resource> get <id> [--json]
  • 语义化退出代码(0、1、2、3、4、5、6、7)
  • 错误输出到 stderr,数据输出到 stdout
  • --json 输出有效 JSON
  • 流分离(stdout = 数据,stderr = UI)
建议添加项
  • 身份验证命令(auth login、auth status、auth logout)
  • 创建/更新/删除操作
  • --quiet 和 --verbose 模式
  • 对变更操作提供 --dry-run
  • 分页(--limit、--page)
  • 筛选(状态、日期范围、搜索)
  • 自动化测试

框架选择

Typer(新工具首选):

  • 类型提示可提供自动验证
  • 内置帮助生成
  • 集成 Rich,实现美观的输出
  • 样板代码比 Click 更少

Click(现有工具可继续使用):

  • Typer 基于 Click 构建(100% 兼容)
  • 结构良好的 Click 代码无需迁移
  • 两者都必须遵循相同的输出约定
# Typer (preferred)
import typer
from rich.console import Console

app = typer.Typer()
console = Console(stderr=True)  # UI to stderr

# Click (acceptable)
import click
from rich.console import Console

console = Console(stderr=True)  # Same pattern

评论

支持 Markdown