跳到正文
Joeplover
后端开发·2026-04-27·约 3 分钟阅读

CLI-Anything:CLI 扩展框架与工具集

CLI 扩展框架,包含技能注册、AdGuardHome 管理和 TypeScript 扩展,支持多语言 CLI 工具开发。

命令行工具

CLI-Anything:多语言 CLI 扩展框架

项目背景

CLI-Anything 的灵感来源于 VSCode 的插件生态——如果命令行工具也能像 VSCode 一样通过插件机制动态扩展功能,那就太好了。我日常需要在终端中完成大量操作(查看 DNS 状态、管理服务、运行脚本),每个操作对应一个独立的 CLI 工具,使用起来很零碎。CLI-Anything 的目标是提供一个统一框架:通过一个命令行入口管理所有工具,插件可插拔,支持多种语言开发。

这个项目也是我在 U3W-AI 实习期间接触 MCP Server 架构后对 CLI 工具标准化的一次实践探索。

架构设计

cli-anything/
├── cli.py            # 主入口,负责插件发现和命令路由
├── plugins/          # 插件目录
│   ├── dns/          # AdGuardHome DNS 管理插件
│   ├── skills/       # 技能注册插件
│   └── system/       # 系统管理插件
├── plugin_api.py     # 插件接口规范

核心机制是插件发现:启动时扫描 plugins/ 目录,自动注册每个子目录中的 plugin.py(Python 插件)或 plugin.ts/plugin.js(TypeScript 插件)。每个插件只需实现一个约定的接口:

class PluginInterface:
    @property
    def name(self): ...
    @property
    def description(self): ...
    def register_commands(self, cli_group): ...

框架负责:命令路由、参数解析、输出格式化。插件只关心自己的业务逻辑。

多语言支持

这是 CLI-Anything 和其他 CLI 框架最大的区别。大多数 CLI 框架(如 Click、argparse、Commander.js)只支持单一语言。CLI-Anything 通过子进程调用实现跨语言:

  • Python 插件:直接 import 加载
  • TypeScript 插件:通过 ts-node 或编译后的 JS 执行
  • 未来可扩展:Go、Rust 等语言的编译产物

实际插件:AdGuardHome DNS 管理

实际在用的是一个 DNS 管理插件,通过 AdGuardHome REST API 实现:

# 查询当前 DNS 拦截统计
cli-anything dns stats
# 添加自定义拦截规则
cli-anything dns block --domain ads.example.com
# 查看最近查询记录
cli-anything dns query --limit 20

这个插件在日常运维中很实用——不用打开浏览器登录 AdGuardHome 后台,终端里一条命令搞定。

踩坑记录

1. 插件热加载与可靠性

设计的初衷是"插件随便写、框架自动发现",但实际中发现:插件出错(语法错误、未捕获异常)会导致整个 CLI 崩溃。解决:插件加载使用 importlib 的异常隔离,每个插件在独立沙箱中验证后再注册。

2. 跨语言通信的数据格式

TypeScript 插件输出给 Python 框架需要约定数据格式。最终统一用 JSON 通过 stdout 传递,框架读取子进程的 stdout 解析结果。错误信息走 stderr。

3. Windows 兼容性

我在 Windows 上开发,但部分插件(如系统管理)需要 Linux 特有的命令。解决方案:插件声明 platform 字段,框架自动过滤不兼容当前系统的插件。

与 MCP 的关系

CLI-Anything 和 MCP Server 在思路上有异曲同工之处:都是一种工具标准化框架。MCP 是 AI 客户端与工具的接口标准,CLI-Anything 是终端用户与工具的接口标准。两者可以互为补充——MCP Server 包装的 AI 工具可以在 CLI-Anything 中注册为命令,反之亦然。