CLI-Anything 把GUI软件变成AI命令行:3个决策定生死
【免费下载链接】CLI-Anything"CLI-Anything: Making ALL Software Agent-Native" -- CLI-Hub: https://clianything.cc/项目地址: https://gitcode.com/GitHub_Trending/cl/CLI-Anything
CLI-Anything 是一个为任意软件构建 AI 原生命令行接口的开源项目。它把 Blender、GIMP、LibreOffice 这类原本为人类设计的 GUI 应用,包一层 AI 代理能直接在终端操作的 CLI,让你的 AI agent 不用再"看屏幕"就能干活。
🎬 场景钩子
你让 AI 把那份 PPT 导出成 PDF,它盯着 GUI 界面愣了十秒——然后你开始想:如果它有一根"命令行"会怎样?CLI-Anything 就是干这个的:把任意 GUI 软件包一层 AI 能直接操作的命令行接口。
🗺️ 全景拆解
看这张架构图,把整个项目当成一条流水线。输入端喂进一个目标软件的代码库,分析出它的后端引擎、API 映射和数据模型;处理端按分析结果设计状态模型和命令结构,写出 CLI 与真实软件调用层,再过三道测试关;输出端是一个可pip安装的 PyPI 包,外加一份 AI 代理能直接读的 SKILL.md。任何新软件,走的是同一条流水线,产出同一形态的交付物。
⚖️ 三个关键设计决策
调真实软件还是用 Python 重模拟:这条是红线
问题:你希望 CLI 真能产出成品,但成品只能从软件的渲染引擎里长出来。
错误做法:拿 Pillow 拼图层去"模拟" GIMP,或者只生成 bpy 脚本却从不调用 Blender。我踩过这类坑的评审现场:它产出一个处理不了真实工作负载的玩具,行为和真实软件完全脱节——用户装的 GIMP 是哪个版本、滤镜参数怎么命名、alpha 通道怎么处理,全对不上。
正确做法:直接拿软件的 CLI/脚本接口当后端。GIMP 是gimp -i -b '(script-fu-console-eval ...)',Blender 是blender --background --python script.py,LibreOffice 是libreoffice --headless --convert-to pdf。生成合法的项目文件或中间文件,然后交给真实软件渲染。软件是必需依赖,不是可选项。
为什么:CLI 的职责是"理解项目、表达意图","按真实行为渲染"交给真正的引擎。职责一拆开,行为就永远和真实软件一致。
交互模型选型:有状态 REPL 和无状态子命令各管什么
问题:同一个 CLI 要伺候两个主人——AI 代理的管线,和人的交互操作。
错误做法:二选一硬推。纯无状态子命令,人编辑 20 步就要传 20 次--project,还没有撤销;纯有状态 REPL,代理没法把它拼进一次性管线里。
正确做法:同一个 CLI 里两者都有。子命令无状态、一条命令一件事、输出 JSON,代理爱用;REPL 持一个会话,带 undo/redo,人爱用。交互层统一用repl_skin.py里的ReplSkin,横幅、提示符、颜色消息全套皮肤,所有 CLI 长得一样:
from cli_anything.gimp.utils.repl_skin import ReplSkin skin = ReplSkin("gimp", version="1.0.0") skin.print_banner() pt_session = skin.create_prompt_session() # prompt_toolkit 会话 line = skin.get_input(pt_session, project_name="demo") skin.success("Saved") # ✓ 绿色 skin.error("Not found") # ✗ 红色为什么:状态是 session 层的实现细节,命令层保持无状态。代理的调用不依赖"你之前做了什么",测试才能用 subprocess 一条命令一条命令地写。
命名空间包怎么配才不冲突
问题:仓库里 60 多个 CLI,每个都是独立 PyPI 包,包路径全在cli_anything前缀下。
错误做法:在每个包的cli_anything/目录里放__init__.py。后果:装第二个包时,第一个包的命名空间目录被覆盖,先装的命令直接"消失"。这里你别那么写,原因是普通包会独占cli_anything这个顶层目录,两个包必然打架。
正确做法:cli_anything/目录不放__init__.py(PEP 420 命名空间包),每个子包(gimp/、blender/)自己带__init__.py。
为什么:这样site-packages/cli_anything/下,多个独立安装的包的子目录可以并排共存,互不覆盖。这是硬约束,违反就装不了第二个。
🔨 动手搭建
先看标准骨架,注意那个关键注释:
gimp/ └── agent-harness/ ├── GIMP.md # 项目特定分析与 SOP ├── setup.py └── cli_anything/ # ← 注意:这里没有 __init__.py(PEP 420 命名空间包) └── gimp/ ├── __init__.py # 子包有 __init__.py ├── __main__.py # python -m cli_anything.gimp 入口 ├── gimp_cli.py # 主 CLI(Click + REPL) ├── core/ # 领域模块:project/export/session... ├── utils/ │ ├── gimp_backend.py # 真实软件调用层 │ └── repl_skin.py # 统一 REPL 皮肤 └── tests/ ├── TEST.md # 测试计划 + 结果 ├── test_core.py └── test_full_e2e.py先跑通最小闭环。别一上来就碰真实软件,先用纯 Python 把"建项目 → 查项目"跑通:
import click, json @click.group() def cli(): pass @cli.command @click.option("--out", default="project.json") def new(out): json.dump({"name": "demo", "layers": []}, open(out, "w")) click.echo(f"✓ created {out}") if __name__ == "__main__": cli()这一步的价值是逼你想清楚状态模型:项目存成什么结构、哪些字段会变。后面所有模块都挂在它上面。
再接真实后端。写utils/gimp_backend.py,这是唯一允许subprocess的地方:
import shutil, subprocess def batch_script_fu(script: str) -> dict: """调用真实 GIMP:无界面 + Script-Fu 批处理模式""" gimp = shutil.which("gimp") if not gimp: raise RuntimeError("GIMP 未安装:apt install gimp") cmd = [gimp, "-i", "-b", script, "-b", "(gimp-quit 0)"] r = subprocess.run(cmd, capture_output=True, text=True, timeout=120) return {"stdout": r.stdout, "returncode": r.returncode}注意缺软件时直接抛错,不降级、不假装成功。
再补测试。三道质量关对应三个位置:test_core.py放合成数据单测,test_full_e2e.py里放文件管道验证和真实后端全流程,最后一条子进程测试从命令行调自己:
class TestRealBackendE2E: def test_render_real_output(self, tmp_path): proj, out = tmp_path / "p.json", tmp_path / "r.png" self._run(["canvas", "new", "-o", str(proj)]) # _run 即 subprocess.run self._run(["--project", str(proj), "export", "render", str(out)]) assert out.exists() and out.stat().st_size > 0 print("ARTIFACT:", out) # 打印工件路径,留给人工核查最后打包。命名空间包加入口点,就这几行:
from setuptools import setup, find_namespace_packages setup( name="cli-anything-gimp", # ← 关键1:PyPI 包名 version="1.0.0", packages=find_namespace_packages(include=["cli_anything.*"]), # ← 关键2 install_requires=["click>=8.0.0", "prompt-toolkit>=3.0.0"], entry_points={ # ← 关键3 "console_scripts": [ "cli-anything-gimp=cli_anything.gimp.gimp_cli:main", ], }, python_requires=">=3.10", )🚦 三个质量关卡
关卡一:合成数据自测。验什么:项目构建、状态流转、参数转换这些纯逻辑对不对。怎么验:全部写在test_core.py,合成数据,不碰真实软件,整套 5 秒内跑完。不通过:bug 一定在你自己代码里,定位精准,直接修。这层最便宜,先跑它。
关卡二:文件管道验证。验什么:中间文件(ODF/MLT/项目 JSON)结构对不对、是否良构、该有的元素在不在。怎么验:解析生成的中间文件断言结构——XML 里滤镜节点有没有、图层字段全不全。不通过:问题在构建层,别让坏的项目文件漏给真实软件,否则后面排查成本翻十倍。
关卡三:真实后端全流程。验什么:最终成品(PDF、渲染图、视频)是不是真出自真实软件且内容正确。怎么验:断言输出文件存在、大小 > 0、格式正确(比如 PDF 前 5 字节是%PDF-),并打印工件路径供人工检查。不通过:真实软件没装,测试直接 fail。禁止 skip,更禁止 mock。这条是红线——没有软件,这个 CLI 就是摆设,绿测试是假象。
📦 上线与分发
上面那个 setup.py 就是核心,重点三处:name决定 PyPI 上叫什么,packages用find_namespace_packages只收cli_anything.*下的子包,entry_points把控制台命令cli-anything-gimp绑到main()。命名空间包下各子包的关系一句话讲清:cli_anything本身不装任何东西,cli_anything.gimp、cli_anything.blender是各自独立安装、互不干扰的兄弟包,同一个 Python 环境里随便并排装。
装一个试试,复制即可跑:
pip install cli-anything-gimp cli-anything-gimp --help🕳️ 避坑手册
1. 输出和输入一模一样?
- 症状:导出的视频/图片看起来和源文件没区别。
- 根因:渲染器读的是原始媒体,跳过了项目级效果(比如 ffmpeg concat 解复用器会忽略 MLT 项目文件里的全部滤镜)。
- 解法:优先用应用原生渲染器(如
melt)读项目文件;不行就建一层滤镜转换,把项目效果翻译成渲染工具的原生语法。
2. 长视频切点漂移两三帧
- 症状:短片段准,10 分钟的片子越来越偏。
- 根因:非整数帧率(29.97 = 30000/1001),用
int()截断做浮点转帧,误差逐帧累积。 - 解法:浮点转帧用
round(),时间码显示用整数算术,往返测试接受 ±1 帧容差。
3. "导出成功"但内容是错的
- 症状:退出码 0,文件生成了,内容是源文件的复刻。
- 根因:没有报错不等于正确,你从没验证过输出。
- 解法:验魔术字节、用 ffmpeg 探指定帧、比对音频首尾 RMS,验证要落到具体字节上。
4. 装了第二个 CLI,第一个命令"消失"
- 症状:
cli-anything-gimp还能用,装上另一个包后某个命令找不到了。 - 根因:
cli_anything/里误加了__init__.py,两个包互相覆盖命名空间目录。 - 解法:
cli_anything/保持无__init__.py(PEP 420),__init__.py只放在各子包内。
5. 测试全绿,真环境一跑就废
- 症状:CI 通过,真机导出却没产出。
- 根因:e2e 测试碰到缺软件时静默 skip 或伪造了结果。
- 解法:真实软件是硬依赖,缺了直接 fail,跳过和造假都不允许。
🚪 下一步
三个动作,现在就做:
git clone https://gitcode.com/GitHub_Trending/cl/CLI-Anything,然后pip install cli-anything-hub,用cli-hub search gimp看看社区都建了什么。- 读一份完整 SOP:GIMP.md,这是"代码库分析 → CLI 设计"的标尺样本;方法论正文在 HARNESS.md。
- 自己过一遍三道关:
pip install -e gimp/agent-harness,再跑pytest gimp/agent-harness/cli_anything/gimp/tests/,真实后端那关缺 GIMP 会直接 fail——这是预期行为。
【免费下载链接】CLI-Anything"CLI-Anything: Making ALL Software Agent-Native" -- CLI-Hub: https://clianything.cc/项目地址: https://gitcode.com/GitHub_Trending/cl/CLI-Anything
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考