一个用 Rust 编写的开发效率工具 CLI。
当前版本:0.2.0。完整版本记录请参阅 CHANGELOG.md。
- 递归扫描并清理指定目录下的 Flutter 项目,实时反馈清理进度和释放空间。
- 内置
et update安全升级,并在每天第一次交互使用时检查新版本。 - 文件大小自动选择
B、kB、MB、GB,显示更直观。
在真实交互终端中,主菜单、子菜单以及 y/n 确认均为单键选择,按下对应数字或
字母后立即响应,不需要回车;直接按回车仍可选择默认项。路径、数字参数和密码等
完整内容输入仍需回车。管道、CI 和重定向输入会自动保留按行读取模式。
macOS 可以直接下载同时支持 Apple Silicon 与 Intel 的 Universal Binary:
curl -LO https://github.com/drainlin/EffectiveTool/releases/latest/download/EffectiveTool-macos-universal.tar.gz
tar -xzf EffectiveTool-macos-universal.tar.gz
sudo install -m 755 EffectiveTool-macos-universal/et /usr/local/bin/et
et --versionet 每个自然日第一次在交互终端中运行时会自动检查一次最新正式版。检查不会发生在
管道或 CI 中,网络失败也不会影响当前功能;发现新版本时只显示提示,不会自动安装。
用户可以运行下面的命令完成升级:
et update升级程序会自动下载 macOS Universal Binary、校验 SHA-256,并替换当前使用的 et。
如果安装位置是 /usr/local/bin/et,安装阶段会请求一次系统管理员密码。也可以只检查
而不安装:
et update --check交互菜单中的 7. 检查并更新 Effective Tool 提供相同功能。如果需要禁用每日自动
检查,可以设置环境变量 ET_NO_UPDATE_CHECK=1。
从 v0.1.0 升级到首个支持内置更新的 v0.2.0 时,需要按下面的备用方式手动覆盖
一次。完成这次升级后,后续版本都可以直接使用 et update。
如果内置更新不可用,可以重新下载最新版并手动覆盖原来的 et,不需要安装 Rust,
也不需要先卸载旧版本:
et --version
et_update_dir="$(mktemp -d)"
curl -fL \
https://github.com/drainlin/EffectiveTool/releases/latest/download/EffectiveTool-macos-universal.tar.gz \
-o "$et_update_dir/EffectiveTool-macos-universal.tar.gz"
tar -xzf "$et_update_dir/EffectiveTool-macos-universal.tar.gz" \
-C "$et_update_dir"
sudo install -m 755 \
"$et_update_dir/EffectiveTool-macos-universal/et" \
/usr/local/bin/et
rm -R "$et_update_dir"
et --version最后一次 et --version 应显示新版本号。升级只会替换 /usr/local/bin/et 可执行文件,
不会清理或修改用户的项目文件。如果 command -v et 显示的不是
/usr/local/bin/et,说明当前使用的是其他位置安装的版本,应升级该路径对应的安装。
也可以使用 Rust 工具链直接从 GitHub 安装:
cargo install --git https://github.com/drainlin/EffectiveTool.git --locked --force也可以克隆仓库后在项目根目录安装:
cargo install --path . --locked --forceCargo 会把可执行文件安装到 ${CARGO_HOME:-$HOME/.cargo}/bin/et。确认该目录在
PATH 中后,可以从任意目录直接运行:
et --version
et --help
et如果终端找不到 et,macOS/Linux 可把下面一行加入 shell 配置并重新打开终端:
export PATH="$HOME/.cargo/bin:$PATH"使用 Rust 工具链安装的用户,更新时再次执行对应的 cargo install 命令并保留
--force 即可覆盖旧版本。卸载:
cargo uninstall effective-tool首次从源码构建需要网络,以下载并校验需要嵌入的 Firebase uploader 和对应架构的
FFmpeg;安装完成后的 et 是自包含产物,日常使用不再下载这些组件。
不带子命令运行 et 会进入面向用户的交互菜单。脚本、CI 和 AI Agent 应直接调用
非交互子命令:
| 功能 | 命令 |
|---|---|
| 上传 Firebase dSYM | et upload-dsyms |
| JPG / PNG / WebP 转换 | et convert-image |
| 创建 Android JKS | et create-jks |
| 视频转 WebP 动图 | et video-to-webp |
| 生成 iOS / Android App 图标 | et generate-app-icons |
| 递归清理 Flutter 项目 | et clean-flutter |
| 检查并安装新版本 | et update |
查看任一命令的完整参数:
et <SUBCOMMAND> --help自动化调用建议使用绝对路径;生成 JKS、视频或图标时加 --no-open;只有明确允许
替换现有文件时才传 --overwrite。Firebase 上传应先执行 --dry-run,确认输入
后再进行真实上传。
仓库内提供 effective-tool Skill,包含全部 CLI
选择规则、安全约束和可直接复用的命令模板。Codex 可以从项目根目录安装为全局
Skill:
mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills"
ln -s "$(pwd)/skills/effective-tool" \
"${CODEX_HOME:-$HOME/.codex}/skills/effective-tool"然后在 Agent 请求中显式使用:
使用 $effective-tool 把 ./video.mp4 转成 720px、15 FPS 的 WebP 动图。
其他支持 SKILL.md 的 Agent 也可以直接加载
skills/effective-tool/SKILL.md。Agent 应使用子命令而不是编号菜单,并根据命令
退出码判断成功或失败。
指定一个目录后,et 会递归检查该目录及所有子目录,识别 pubspec.yaml 中声明了
Flutter SDK 的项目,并依次在每个项目中执行 flutter clean:
et clean-flutter ~/Projects
et clean-flutter ~/Projects --jobs 8
et clean-flutter /path/to/projects --flutter /path/to/flutter/bin/flutter默认并发任务数为 CPU 逻辑核心数 和 Flutter 项目数 中的较小值;可以使用
-j, --jobs COUNT 手动限制并发数。清理过程中会显示正在启动的项目、已完成数量和
完成百分比;交互终端使用单行动态进度条原地刷新,不会为每个项目持续刷屏。清理时
不输出单个项目的空间统计。所有项目处理完成后,会汇总成功和失败数量,
并显示本次批量清理实际释放的总空间。单个项目失败不会阻止
后续项目继续清理;只要存在失败项目,命令最终会返回非零退出码。扫描不会跟随目录
软链接,因此不会越出用户指定的目录树。
不带子命令运行 et,选择 6. 清理 Flutter 项目 后输入目录。交互模式会先列出
发现的所有 Flutter 项目,用户确认后才开始清理。flutter 默认从 PATH 查找;
如需使用指定版本,可在命令模式传入 --flutter PATH。
输入一张正方形 JPG、PNG 或 WebP,et 可以一次生成完整的 iOS 和 Android
App 图标目录。默认同时生成两个平台,也可以只生成其中一个:
et generate-app-icons ./app-icon.png
et generate-app-icons ./app-icon.png --platform ios
et generate-app-icons ./app-icon.png --platform android --output ./icons默认输出到输入图片旁的 icons/,目录结构兼容常见的 iOS 与 Android 工程:
icons/
├── ios/
│ └── AppIcon.appiconset/
│ ├── Contents.json
│ └── icon-*.png
└── android/
├── mipmap-ldpi/ ... mipmap-xxxhdpi/
├── mipmap-anydpi-v26/
├── values/
└── playstore-icon.png
iOS 会生成参考目录中的全部 16 个 PNG 尺寸及 Xcode Contents.json,并移除
Alpha 通道。Android 除参考目录已有的传统 Launcher Icon 和 512×512 Play
Store Icon 外,还会生成:
ic_launcher_round.png圆形图标。- API 26+ Adaptive Icon 的 108dp 前景层、背景色和 XML。
- Android 13+ Themed Icon 使用的 Monochrome Layer。
- mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi,以及兼容参考目录的 ldpi 资源。
Adaptive Icon 会自动读取图片边角颜色作为背景,移除相近的背景色,将识别出的
前景缩放到 66dp 安全区,并用前景 Alpha 生成单色层。单张扁平图片无法像设计源
文件一样完美拆层,因此复杂渐变背景建议生成后检查
ic_launcher_foreground.png。建议输入至少 1024×1024;较小的正方形图片也能
生成,但 iOS 1024 图标会被放大。
常用参数:
--platform all|ios|android:默认all。-o, --output DIRECTORY:指定输出目录。--overwrite:更新已有输出目录中的同名生成文件。--no-open:完成后不在文件管理器中定位输出目录。
交互模式选择 5. 生成 iOS / Android App 图标,可在子菜单中选择两个平台、
仅 iOS 或仅 Android;任一步输入 0、b 或 back 可返回主菜单。
et 可以把 MP4、MOV、MKV 等 FFmpeg 能识别的视频转换成 WebP 动图。macOS
Release 已内置静态 FFmpeg,Apple Silicon 使用 FFmpeg 8.1,Intel 使用 FFmpeg
8.0;用户不需要安装 Homebrew、FFmpeg 或其他动态库。
交互模式在主菜单选择:
4. 视频转 WebP 动图
然后依次选择视频、输出位置、帧率、宽度、质量和循环次数。转换期间会显示完成 百分比、视频时间、已处理帧数、处理帧率、速度和耗时。转换完成后默认在文件管理器 中定位输出文件。
命令模式:
et video-to-webp ./clip.mp4
et video-to-webp ./clip.mov \
--output ./clip.webp \
--fps 15 \
--width 720 \
--quality 80 \
--loop 0默认输出为输入文件同目录、同名的 .webp,默认参数是 15 FPS、720px 宽、
质量 80、无限循环。高度会保持比例并自动调整。常用参数:
--fps 1-60:输出帧率;帧率越高,动画越流畅,文件也越大。--width 16-7680:输出宽度;设置为0保持视频原始尺寸。-q, --quality 1-100:有损 WebP 质量。--loop 0-65535:循环次数;0表示无限循环。--overwrite:允许原子替换已有输出。--no-open:完成后不打开输出文件位置。--ffmpeg PATH:使用指定的 FFmpeg,方便开发和调试。--refresh-ffmpeg:从et自身重新释放内置 FFmpeg。
内置 FFmpeg 会在构建时按目标架构下载并验证 SHA-256,再使用 Zstandard 压缩后
嵌入 et。第一次转换时流式解压到用户缓存,之后直接复用并在每次使用前校验
原始 FFmpeg 的 SHA-256;转换过程不需要再次下载。当前内置版本仅支持 macOS;
其他平台可以暂时通过 --ffmpeg PATH 使用自行安装的 FFmpeg。
et 可以直接创建 Android 签名使用的 Java KeyStore(JKS)。RSA 私钥、自签名
X.509 证书、私钥保护和 JKS 编码均由 Rust 完成,运行时不需要 Java、keytool
或 OpenSSL。
交互模式在主菜单选择:
3. 创建 JKS 密钥库
程序会依次询问输出路径、别名、证书名称、有效期、RSA 密钥位数和密码。密码使用
隐藏输入,不会回显到终端;密钥库密码直接回车时默认为 123456,密钥密码留空
时会使用密钥库密码。任一步输入 0、b 或 back 均可返回主菜单。
也可以直接使用命令:
et create-jks
et create-jks \
--output ./android/app/upload-keystore.jks \
--alias upload \
--dname "CN=Upload Key, O=Example, C=CN" \
--validity-days 10000 \
--key-size 2048在交互终端中,命令会隐藏询问密钥库密码,直接回车使用默认值 123456。没有
终端或环境变量时也使用该默认值;密钥密码默认与密钥库密码相同。正式发布建议
通过环境变量设置独立的强密码,避免密码出现在 shell 历史和进程列表:
export ET_JKS_STORE_PASSWORD='your-secret-password'
export ET_JKS_KEY_PASSWORD='your-optional-key-password'
et create-jks --output ./upload-keystore.jks也可以用 --store-password-env NAME 和 --key-password-env NAME 指定自定义环境
变量名。密码至少 6 个字符;为保持 Java JKS 对密码编码的兼容性,目前仅接受
ASCII 字符。默认输出 upload-keystore.jks,默认别名为 upload,证书有效期
为 10000 天,RSA 密钥为 2048 位。使用 --overwrite 才会替换已有文件。
生成后,交互终端默认会在系统文件管理器中定位 JKS:macOS 在 Finder 中选中文件,
Windows 在资源管理器中选中文件,Linux 打开所在目录。脚本可以传入 --no-open
禁用;CI 和重定向输出等非交互环境会自动跳过。
请安全备份 JKS 和密码。Android 应用发布密钥一旦丢失,通常无法再用同一密钥 签署后续更新。
支持 JPG、PNG 和 WebP 之间的六种互转方向。命令模式:
et convert-image ./photo.png --to webp
et convert-image ./photo.webp --to jpg --quality 90
et convert-image ./photo.jpg --to png --output ./exports/photo.png文件夹批量转换:
et convert-image ./images --to webp
et convert-image ./images --to jpg --quality 90 --output ./converted-images批量模式只扫描文件夹第一层,不递归子目录。默认输出到输入文件夹下的
converted/,忽略子文件夹和非 JPG/PNG/WebP 文件。单个文件失败不会中断其余
转换,结束时会输出成功、跳过、失败和忽略数量;存在失败时命令返回非零退出码。
常用参数:
-t, --to jpg|png|webp:目标格式,jpeg也可作为jpg的别名。-o, --output PATH:单文件模式为输出文件;批量模式为输出文件夹。 省略时,单文件替换扩展名,文件夹输出到其converted/子目录。-q, --quality 1-100:JPG 和 WebP 编码质量,默认85。--overwrite:允许原子替换已存在的输出文件;默认拒绝覆盖。
程序按文件内容识别输入格式。PNG 或 WebP 的透明像素转为 JPG 时会铺白色背景。 转换会先写入同目录临时文件,编码成功后再移动到目标位置。
交互模式中在主菜单选择:
2. JPG / PNG / WebP 格式转换
然后依次输入图片或文件夹、目标格式、质量和输出位置。拖入文件夹会自动进入
非递归批量模式。任一步输入 0、b 或 back 均可返回主菜单。
此功能仅能在 macOS 上执行。它会验证 .xcarchive 和
GoogleService-Info.plist,然后把 archive 中的所有 dSYM 上传到 Firebase
Crashlytics。
安装后直接运行 et 即可进入交互模式:
程序会在打开主菜单以及进入具体功能时自动清屏;管道和 CI 环境不会输出清屏控制字符。
$ et
et — 开发效率工具
请选择功能:
1. 上传 Firebase Crashlytics dSYM
2. JPG / PNG / WebP 格式转换
3. 创建 JKS 密钥库
4. 视频转 WebP 动图
5. 生成 iOS / Android App 图标
6. 清理 Flutter 项目
7. 检查并更新 Effective Tool
0. 退出
请输入序号 [1](无需回车): 1
上传 Xcode archive 中的 dSYM 到 Firebase Crashlytics
可以把文件或目录直接拖到终端窗口中。
.xcarchive 或 dSYMs 目录路径:
GoogleService-Info.plist 路径:
交互模式默认上传 iOS dSYM,并会在上传前要求确认。
在路径提示中输入 0、b 或 back 即可返回主菜单。
也可以使用完整参数,适合脚本和 CI:
et upload-dsyms \
--archive ~/Library/Developer/Xcode/Archives/2026-07-31/MyApp.xcarchive \
--google-service-info ./MyApp/GoogleService-Info.plist也兼容 Firebase upload-symbols 常用的参数形式,直接传入 dSYMs 目录:
et upload-dsyms \
-gsp ./ios/Runner/GoogleService-Info.plist \
-p ios \
./build/ios/archive/Runner.xcarchive/dSYMs如果希望使用项目 Pods 中的 uploader:
et upload-dsyms \
--upload-symbols ./ios/Pods/FirebaseCrashlytics/upload-symbols \
-gsp ./ios/Runner/GoogleService-Info.plist \
-p ios \
./build/ios/archive/Runner.xcarchive/dSYMsFirebase 官方 upload-symbols 12.14.0 通用二进制已经嵌入 et(支持 Apple
Silicon 和 Intel Mac)。首次执行时只会从 et 自身释放到用户缓存并进行
SHA-256 校验,不会访问 GitHub,也不依赖 CocoaPods 或本机预装 Firebase。
实际上传 dSYM 到 Firebase 服务仍然需要网络。
上传期间,交互终端会显示活动状态和实时耗时:
⠹ 正在上传 Firebase dSYM · 已用时 00:18
Firebase 原始的成功、失败和重试日志会继续显示。--debug 模式下会关闭动画,
优先展示 Firebase 的详细日志;CI 和重定向输出中只输出普通状态文本。
上传前只进行校验并查看将要执行的命令:
et upload-dsyms \
--archive /path/to/MyApp.xcarchive \
--google-service-info /path/to/GoogleService-Info.plist \
--dry-run其他常用选项:
--platform ios|mac|tvos:目标平台,默认ios;Catalyst 使用mac。--debug:显示 Firebase uploader 的详细日志。--refresh-uploader:从et自身重新释放内置 uploader。--upload-symbols /path/to/upload-symbols:改用指定的 Firebase uploader。--allow-bundle-id-mismatch:明确允许 archive 与 Firebase plist 的 bundle ID 不同。
查看完整帮助:
et upload-dsyms \
--helpEffective Tool 自有源代码采用 MIT License。构建过程下载并嵌入的 Firebase uploader 与 FFmpeg 等第三方组件继续适用各自的许可证;版本、来源及 许可信息请参阅 THIRD_PARTY_NOTICES.md。