EEZ Studio 的 Agent 原生 CLI 工具链:cli-anything-eez-studio 实战指南
cli-anything-eez-studio 是 CLI-Anything 生态为 EEZ Studio 提供的 Agent 原生(agent-native)命令行工具集,它让大模型 Agent 可以像人类工程师一样:直接读写原生 .eez-project 工程 JSON、批量搭建 LVGL 界面与控件、维护 SCPI 仪器指令元数据,并通过真实的 EEZ Studio Node/Docker 后端完成 LVGL 模拟器构建。读完本指南,你将掌握该工具从安装、日常命令、会话回滚到后端桥接的全部用法,能够在脚本、CI 与 Agent 工作流中可靠地自动化 EEZ Studio 项目。
一、工具定位与架构总览
EEZ Studio 是面向嵌入式 UI 的 Electron/Node 应用,主要承载 LVGL 代码生成、SCPI 仪器模型编辑与 Flow 流式自动化三类任务。官方工程文件是一个以 .eez-project 结尾的 JSON 文档。cli-anything-eez-studio 的总体设计遵循"双轨制":
- 纯单元命令:直接在 Python 侧解析、改写原生
.eez-projectJSON,不依赖 EEZ Studio 本体即可完成工程创建、LVGL 控件脚手架、SCPI 元数据维护; - 后端桥接命令:通过
EEZ_STUDIO_SOURCE指向一个真实构建好的 EEZ Studio 源码树,调用其编译产物(如build/project-editor/lvgl/docker-build/docker-build-lib.js)完成工程解析与 LVGL 模拟器构建。
这段设计意图可以在仓库的架构文档 EEZ_STUDIO.md 与入口 CLI 源码 eez_studio_cli.py 中互相印证。整个 agent-harness 的目录组织如下:
eez-studio/agent-harness/
├── setup.py
└── cli_anything/eez_studio/
├── eez_studio_cli.py # click 命令行入口与全部命令组
├── core/
│ ├── project.py # .eez-project 原生 JSON 读写与 LVGL 结构构造
│ ├── scpi.py # SCPI 子系统/命令/参数模型维护
│ ├── session.py # 会话状态、undo/redo、持久化
│ └── export.py # 后端构建编排与产物校验
├── utils/
│ ├── eez_studio_backend.py # 定位 EEZ Studio 源码树、调用其 Node 模块
│ └── repl_skin.py # REPL 交互皮肤
├── skills/SKILL.md # 本文所述的 Agent Skill 定义
├── tests/ # 单元与端到端测试
└── README.md
二、安装与环境准备
Skill 文档给出的安装方式如下,它本质上是把 eez-studio/agent-harness 以可编辑模式装入当前 Python 环境:
cd eez-studio/agent-harness
pip install -e .
查看 setup.py 可以看到安装约束:Python >= 3.10,运行依赖仅 click>=8.0.0 与 prompt-toolkit>=3.0.0,并通过 console_scripts 注册了 cli-anything-eez-studio 命令。也就是说纯工程编辑场景的依赖非常轻。
后端命令还需要真实的 EEZ Studio 源码树。Skill 与源码 eez_studio_backend.py 中给出的标准流程是:
# 获取 EEZ Studio 官方开源源码树并构建
git clone https://github.com/eez-open/studio.git
cd studio
npm install
npm run build
# 关键:让工具定位到构建产物
export EEZ_STUDIO_SOURCE=/absolute/path/to/studio
环境中与后端相关的变量可以总结如下:
| 环境变量 | 作用 | 取值来源 |
|---|---|---|
EEZ_STUDIO_SOURCE |
指向构建好的 EEZ Studio 源码树(其 package.json 的 name 须为 eezstudio) |
源码 find_source_tree() |
EEZ_STUDIO_NODE |
覆盖 node 可执行文件路径,缺省时用 shutil.which("node") 查找 |
源码 find_node() |
EEZ_STUDIO_BUILD_COMMAND |
自定义的原生 EEZ 构建命令,配合 lvgl build-files 使用 |
源码 run_custom_build_command() |
EEZ_STUDIO_RUN_LIVE_BACKEND |
置 1 时开启需要真实后端的在线 E2E 测试 | 测试文档 |
全量 LVGL 模拟器构建(lvgl simulator-build)额外要求 Docker 可用。若后端缺失,相关命令会**大声失败(fail loudly)**并给出上述安装指引,而不是静默返回错误结果。
三、核心用法:JSON 输出与工程脚手架
Skill 明确强调:面向 Agent 的输出一律使用 --json。在 CLI 顶层传入 --json 后,所有子命令都会输出结构化的 JSON(实现见 eez_studio_cli.py 的 output()/_emit_error()),便于下游直接 json.loads;不加 --json 时则以人类可读的缩进文本输出。
Skill 中给出的最小可用流程,是一口气完成"建工程 → 放控件 → 查看控件"的闭环:
cli-anything-eez-studio --json project new -o panel.eez-project --name Panel
cli-anything-eez-studio --json --project panel.eez-project lvgl add-label --text "Ready"
cli-anything-eez-studio --json --project panel.eez-project lvgl add-button --text "Run"
cli-anything-eez-studio --json --project panel.eez-project project widgets
这里体现了一个重要的会话/持久化语义:单条带 --project 的命令会自动保存工程变更,除非显式传 --dry-run。这个"自动保存"由 CLI 的 call_on_close 钩子实现(见 _auto_save,仅当非 REPL、非 --dry-run 且工程确有修改时触发)。
顶层全局选项汇总如下(均出自 CLI 入口 eez_studio_cli.py):
| 选项 | 含义 |
|---|---|
--json |
输出机器可读 JSON |
--project / -p PATH |
打开指定的 .eez-project 文件 |
--session ID |
指定会话 ID |
--dry-run |
不自动保存工程修改 |
3.1 创建工程时的重要参数
project new 并不是只写一个文件名,它会根据 EEZ Studio 原生模型生成一个结构完整的 LVGL 工程。可用参数与默认值如下(对应源码 create_project()):
| 选项 | 默认值 | 说明 |
|---|---|---|
--name / -n |
Untitled |
工程名,写入 settings.general.projectName |
--width |
800 |
LVGL 显示宽度(必须为正整数) |
--height |
480 |
LVGL 显示高度(必须为正整数) |
--lvgl-version |
9.2.2 |
LVGL 版本号,常量见 project.py 的 DEFAULT_LVGL_VERSION |
--destination |
src/ui |
构建输出目录,写入 settings.build.destinationFolder |
--flow-support |
关闭 | 是否启用 EEZ Flow 支持标记 |
--output / -o |
必填 | 生成的 .eez-project 文件路径 |
从源码 create_project() 可以看出生成的原生 JSON 骨架:settings.general(projectName、projectType=lvgl、projectVersion=v3、lvglVersion、displayWidth/Height、colorBpp=32 等)、settings.build(含 6 个带 //${eez-studio LVGL_*_DECL/DEF} 标记的模板文件 ui.h/ui.c/screens.c/screens.h/vars.h/actions.h、destinationFolder 等)、userPages(默认一个名为 Main 的页面及其 LVGLScreenWidget 根组件)、以及 scpi、instrumentCommands、variables、styles、fonts 等空白集合。工程还会附带一个 cliAnything 标记字段,记录是由哪个 harness、在什么时间以何种格式生成的,便于溯源。
3.2 工程查看与修改命令
project 命令组还包含一组常用子命令(技能原文 + README 均给出全量清单):
| 子命令 | 作用 |
|---|---|
project open PATH |
打开现有工程并做结构校验 |
project save [PATH] |
保存当前工程 |
project info |
输出工程摘要(名称、类型、版本、LVGL 版本、显示尺寸、页面/控件/SCPI 统计等) |
project validate [PATH] |
校验原生工程结构并返回 {"valid": true} |
project pages |
列出页面/屏幕及各自控件数 |
project widgets [--page NAME] |
列出控件(可过滤页面),含坐标尺寸、文本与父子关系 |
project set KEY VALUE |
设置 settings.general 白名单键 |
project set-destination DIR |
设置 settings.build.destinationFolder |
project add-build-file FILE --template ... |
新增或替换构建模板文件 |
注意 project set 并不是任意键都能写:源码 project.py 只允许 projectName、lvglVersion、flowSupport、displayWidth、displayHeight、colorBpp 六个键,其中宽高会被强转正整数、flowSupport 会做布尔字符串归一化(true/1/yes/on 等)。
四、LVGL 界面脚手架:页面、标签与按钮
lvgl 命令组在 Agent 场景里最常见的使用方式是"照需求快速搭 UI"。核心命令:
| 子命令 | 关键选项 | 说明 |
|---|---|---|
lvgl add-page NAME [--width] [--height] |
宽高缺省继承工程显示尺寸 | 新增 LVGL 页面/屏幕 |
lvgl add-label --text TEXT |
--page(默认 Main)、--name、--x(20)、--y(20)、--width(160)、--height(32) |
在页面根组件下加 LVGLLabelWidget |
lvgl add-button --text TEXT |
--page(默认 Main)、--x(20)、--y(72)、--width(140)、--height(48) |
加 LVGLButtonWidget,并自动挂一个同名 _label 子控件 |
从源码可以看出控件在 JSON 中的落位方式(project.py):
- 每个控件都通过
objID = uuid4().hex生成唯一对象 ID; - 控件统一携带
left/top/width/height、hidden/clickableFlag/checkedFlag/disabledFlag等 LVGL 原生字段; - 标签与按钮都挂到目标页面
components[0](即屏幕根组件)的children数组里; - 控件名若未显式提供,会用文本经
sanitize_identifier()归一化生成(非字母数字统一转下划线、首字符为数字时加screen_/button_前缀)。
所以执行完 Skill 示例后,project widgets 输出的就是"Main 页上有几个标签、几个按钮以及它们的位置尺寸"这类信息,Agent 可据此判断 UI 是否已按预期搭建。
4.1 准备构建目录
cli-anything-eez-studio --json --project panel.eez-project lvgl ensure-destination
该命令读取工程 settings.build.destinationFolder(默认 src/ui),在工程文件同级的对应路径下创建目录,并返回绝对路径与 exists 标记。源码要求工程必须先保存为文件(需要 project_path)才能确定"工程文件同级"的位置,否则会报错提示。这是纯本地、不依赖后端的命令,适合在脚本早期阶段调用。
五、SCPI 仪器指令元数据管理
针对"仪器前端"型项目,工具提供 SCPI 模型的三级结构维护:子系统(subsystem)→ 命令(command)→ 参数(parameter)。Skill 给出的示例:
cli-anything-eez-studio --json --project panel.eez-project scpi subsystem-add SOURCE
cli-anything-eez-studio --json --project panel.eez-project scpi command-add SOURCE :VOLTage?
cli-anything-eez-studio --json --project panel.eez-project scpi parameter-add SOURCE :VOLTage? channel --type nr1 --optional
各子命令能力一览(实现见 scpi.py):
| 子命令 | 作用与校验 |
|---|---|
scpi subsystem-list / scpi subsystem-add NAME [-d DESC] |
列出/新增子系统,重名会直接报 ValueError |
scpi command-list [-s SUBSYS] |
列出命令并计算 query(名字以 ? 结尾)、参数个数、响应类型 |
scpi command-add SUBSYS NAME [-d] [--response-type] |
新增命令;命令名以 ? 结尾时自动判定为查询并生成 response.type(默认 quoted-string,可覆盖,如 nr3) |
scpi parameter-add SUBSYS COMMAND NAME [--type] [--optional] |
为命令追加参数;--type 默认 nr1,--optional 写入 isOptional |
查询命令的"响应"设计比较值得注意:command-add :VOLTage? 后,工具的 list 输出里该命令的 query 字段为 true,说明工具明确理解了 ? 后缀在 SCPI 协议中代表查询请求的语义,并据此自动配置响应类型结构。
六、后端桥接:真实 EEZ Studio 的解析与模拟器构建
当需要让 EEZ Studio 亲自处理工程(而不是让 Python 模拟导出)时,使用后端命令:
cli-anything-eez-studio --json backend status
cli-anything-eez-studio --json --project panel.eez-project lvgl backend-inspect
cli-anything-eez-studio --json --project panel.eez-project lvgl simulator-build build/sim
6.1 backend status:探测后端可用性
backend status 会按以下逻辑探测(源码 eez_studio_backend.py):优先用命令行的 --source,其次读 EEZ_STUDIO_SOURCE,找到 package.json 中 name == "eezstudio" 的目录后,再检查 build/project-editor/lvgl/docker-build/docker-build-lib.js 是否存在。返回的 JSON 包含 available、源码路径、EEZ Studio version、node 路径、docker-build-lib 是否就位等探测信息。后端不可用时返回 {"available": false, "error": ...}(不会让整条命令崩溃),而真正的后端操作命令则会抛错。
6.2 backend-inspect:借上游库解析工程
lvgl backend-inspect 会写一个临时 Node runner,调用 EEZ Studio 构建产物中的 docker-build-lib.js 的 readProjectFile() 来解析工程元数据,把 { ok, projectInfo, logs } JSON 返回给调用方(源码 eez_studio_backend.py)。如果源码树在但未构建(缺 docker-build-lib.js),命令会提示先执行 npm run build——这正是 Skill 中"backend 命令缺失时应大声失败"的具体体现。
6.3 simulator-build:完整的 LVGL 模拟器 Docker 构建
lvgl simulator-build OUTPUT_DIR 是能力最重的命令。它以 docker-build-lib.js 为底座,串起"解析工程 → 检查 Docker → 准备工程 → 构建 → 提取产物"全流程,并额外校验关键工件非空。可用参数:
| 选项 | 默认值 | 说明 |
|---|---|---|
--repository-name |
eez-framework |
传给 EEZ Docker 构建的仓库名 |
--docker-volume |
eez-studio-cli-anything |
Docker 卷名 |
--timeout |
900(秒) |
子进程超时 |
--source / --path |
各自回退到环境变量/当前打开工程 | 后端源码树与工程路径 |
命令结束后 CLI 会立刻对输出目录执行产物校验(verify_simulator_output):要求 index.html、index.js、index.wasm 三个文件都存在且非空,其中 index.html 头 32 字节需含 <!DOCTYPE html/<html,index.wasm 前 4 字节必须是 WebAssembly 魔数 \x00asm。这套"结构 + 魔数"的双重校验也可单独通过 lvgl verify-simulator OUTPUT_DIR 触发,非常适合作为 CI 的收尾断言。
6.4 build-files 与自定义构建命令
cli-anything-eez-studio --json --project panel.eez-project lvgl build-files
lvgl build-files 走的是 EEZ_STUDIO_BUILD_COMMAND 通道:它把环境变量中的命令以 shell 分词后执行,并把工程绝对路径作为最后一个参数追加。需要说明的是,这一命令面向"未来 EEZ Studio 发布公开 headless 代码生成命令"或"本地打过补丁的构建"这类场景;当下没有配置该变量时它会明确报错并指引改用 lvgl simulator-build。正如 EEZ_STUDIO.md 所强调的:"harness 不会在 Python 里自行合成 EEZ 的导出结果"——这是刻意保持的边界。
七、会话管理:undo / redo 与自动保存
Skill 建议在 REPL 或脚本会话中使用会话命令做状态控制:
cli-anything-eez-studio --json session status
cli-anything-eez-studio --json session undo
cli-anything-eez-studio --json session redo
会话语义的完整实现位于 session.py。值得展开的机制点:
- checkpoint 快照:每个会修改工程的命令(add-page、add-label、add-button、set、SCPI 系列等)在动手前都会调用
session.checkpoint(),把当前工程做一次deepcopy压入撤销栈;每次新变更都会清空重做栈。 - 深度上限:撤销栈上限
MAX_UNDO_DEPTH = 50,超出时从栈底丢弃最旧快照。 - 自动保存触发链:
--project一次性命令在进程退出(call_on_close)时若工程is_modified为真则写盘;REPL 模式与--dry-run会绕过自动保存,REPL 退出前若仍有未保存修改会给出提示。 - 跨会话持久化:
session save-state把会话状态 JSON 写到~/.eez-studio-cli/sessions/<session_id>.json(Linux 下即$HOME/.eez-studio-cli/sessions),session list按时间戳倒序列出这些状态文件;默认session_id为session_<unix时间戳>,也可用顶层--session显式指定。
八、REPL 交互模式
不带任何子命令直接运行即可进入 REPL:
cli-anything-eez-studio --project panel.eez-project
REPL 由 prompt-toolkit 驱动(repl_skin.py),交互时它会为当前行自动注入 --project 与 --json 前缀,让用户输入 lvgl add-label --text Hello 即可操作当前工程;内置 help 列出各命令速查、quit/exit 退出。REPL 内若发生业务错误不会终止会话,而是打印错误后回到提示符,适合人工/Agent 半交互式探索工程。
九、错误输出契约与调试
无论一次性命令还是 REPL,CLI 都用统一的错误契约(handle_error 装饰器 + _emit_error):--json 模式下错误被序列化为
{"error": "<消息>", "type": "RuntimeError|FileNotFoundError|ValueError|..."}
并写入 stderr;非 JSON 模式输出 Error: ...。一次性命令模式下业务异常会以退出码 1 结束,便于脚本捕获。这让 Agent 可以精确区分"后端未安装""页面不存在""参数不合法""文件已存在"等失败类型并给出相应处置。
十、测试与验证方法
仓库自带两层测试来保障上述行为(测试入口见 tests/ 与 EEZ_STUDIO.md):
cd eez-studio/agent-harness
python3 -m pytest cli_anything/eez_studio/tests/test_core.py -v
python3 -m pytest cli_anything/eez_studio/tests/test_full_e2e.py -v
- test_core.py(纯单元,无后端依赖):覆盖"工程包含原生 section、save/load 往返、set general/destination、页面控件增查、SCPI 三级结构、session undo/redo 数量精确性、CLI
--json project new输出可解析"等断言,还验证了默认 LVGL 版本、默认src/ui目录与根组件类型(LVGLScreenWidget)。 - test_full_e2e.py(默认免后端):验证 backend status 探测以及"后端不可用"的结构化错误路径。只有设置了
EEZ_STUDIO_RUN_LIVE_BACKEND=1且提供已构建的EEZ_STUDIO_SOURCE时,才会真正跑backend-inspect类的在线用例。
这种"默认离线可测、在线深度用例显式开关"的测试设计,使得该 harness 可以在完全没有 EEZ Studio 的环境里做回归,同时保留了对真实后端的契约测试通道。
结语
从 project new 生成带模板标记的原生工程,到 lvgl add-button 拼接控件树,再到 scpi command-add 维护仪器指令、simulator-build 借真实 EEZ Docker 后端产出 index.html/index.js/index.wasm,cli-anything-eez-studio 提供的是一条从"纯 JSON 编辑"到"真实工具链构建"的完整自动化链路。其核心方法论可以归纳为三点:一是直接编辑原生格式而非造自己的中间格式,保证与 EEZ Studio 的互操作性;二是统一 --json 输出 + 结构化错误,让 Agent 每次调用都有确定性的可解析结果;三是后端缺失即大声失败并给出修复指引,把环境依赖问题转化为明确的行动项。掌握这些模式后,你可以把它嵌入 LVGL 原型生成、测试仪器前面板搭建、CI 模拟器构建流水线等各类自动化场景。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00