关于 sim-use
sim-use 是通过 sim-use 命令行操作移动设备模拟器的 Agent 技能,并有限支持实体 iOS 设备。它采用观察、操作、验证的循环,侧重选择器交互和设备能力限制的检查。
主要功能
- 01通过界面大纲观察屏幕
- 02使用选择器点击界面元素
- 03输入、粘贴文本并执行手势和滚动
- 04通过界面或截图验证操作
- 05批量执行已知的 iOS 交互序列
适用场景
- 01在移动设备模拟器中运行脚本流程
- 02检查并测试应用界面状态
- 03在输入框中录入 Unicode 或多行文本
- 04采集模拟器会话的视觉证据
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lycorp-jp/ sim-use --skill sim-use安装后在 Claude Code 中输入/sim-use确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/batch-reference.mdcheatsheet.mdcrash-awareness.mdpitfalls.mdscripts/preflight.py详细介绍
0. 预检
首次与设备交互前,请运行预检:
python3 scripts/preflight.py --device <UDID>
这会验证 sim-use 已安装且与该技能兼容、设备可访问,并且守护进程运行正常。如果没有该脚本,请手动执行以下检查:
sim-use --version— 确认 sim-use 位于 PATH 中,且支持该技能文档中列出的命令。sim-use devices— 确认目标设备已列出,且已启动/连接。sim-use ui --device <UDID>— 确认可以读取屏幕。
WARN UI content 行仍表示预检通过:读取操作成功,但大纲可能遗漏应用控件。请参阅 常见问题 索引。
仅当只有一个模拟器已启动或只有一个守护进程正在运行时,--device 才是可选项。对于 Android,请运行一次 sim-use android init --device <serial> 以安装桥接 APK。已连接的实体 iPhone/iPad 会以 physical 类型显示在 sim-use devices 中,也可以通过顶层动词操作——但仅限 ui、基于选择器的 tap 和 screenshot;该目标不支持其他所有动词。操作实体 iOS 设备前,请参阅下文的 实体 iOS 设备。Linux 版 sim-use 仅支持操作 Android:其中不提供 sim-use ios …、record-video/stream-video、viewer 和 init,每个动词都需要 --device <serial>,并且 sim-use devices 只会列出 Android 设备。
1. 观察—操作循环
每次交互都遵循同一流程:观察 → 操作 → 验证。
观察
sim-use ui --device <UDID>
读取大纲。每个元素都有一个 @N 别名,以及可选的 #<id> 标识符。列表单元格带有 #N(主列表)或 #N@M(限定范围)。
JSON 输出中的帧(--json:entries[].frame、screen)使用平台原生单位——iOS 使用点,Android 使用像素。在跨平台计算坐标前,务必根据外层对象的 platform 字段确定平台。始终将 --json 与 --no-raw 搭配使用——请参阅下文的 精简输出。
操作
按优先顺序选择一个选择器:
| 选择器 | 使用时机 |
|---|---|
tap @N | 紧接 ui 使用。速度最快,且由缓存支持。 |
tap #<id> | 在轻微布局变化下仍然稳定。从大纲中复制。 |
tap --label 'X' | 用于脚本化流程。搭配 --wait-timeout 处理状态转换。 |
tap --label-regex '...' | 用于带计数器/时间戳的动态标签。使用 ^...$ 锚定。 |
tap --label-contains 'X' | 不清楚确切标签时进行子字符串匹配。 |
tap -x N -y N / tap --point x,y | 作为最后手段,用于没有 AX 数据的元素。可直接使用大纲打印的数字,适用于任意方向和显示屏;--coordinate-space native 用于设备原生的纵向点坐标。 |
使用 --element-type 或 --frame minY=0.7r 区分冲突项(参阅 references/cheatsheet.md)。
验证
操作后务必验证——命令会立即返回,不会等待完成:
sim-use ui --device <UDID> # read the new screen state
sim-use screenshot --device <UDID> --output after.png
控制输出大小
读取命令输出的每个字节都会占用上下文。以下默认做法有助于降低循环开销:
- 优先使用默认的文本大纲,而不是
--json。大纲包含一次点击所需的所有信息(@N/#<id>别名、角色、框架、状态);需要用于坐标计算的结构化字段(entries[].frame、screen)或未截断的完整文本时,再使用--json(大纲会将标签截断为 60 个字素,将value=截断为 30 个字素)。 - 使用
--json时,添加--no-raw。data.raw是原始无障碍树,通常占据输出的大部分字节,只有在调试 sim-use 本身时才有用。 - 每次操作只执行一次
ui:第 N 步的验证读取,就是第 N+1 步的观察读取。两者之间不要再运行第二次ui。 - 使用文本大纲验证,而不是截图。读取截图的开销是典型大纲的数倍;仅在确实需要进行视觉检查(颜色、图像、布局)时才截图。
- 在 iOS 上等待过渡时,优先使用
tap --label 'X' --wait-timeout 3(轮询元素),而不是循环运行ui。Android 的tap没有--wait-timeout;请改为在命令之间使用sleep。 - 对于已知的多步 iOS 操作序列,使用
sim-use ios batch(参见references/batch-reference.md)——一次调用,一次输出。
常用操作
| 任务 | 命令 |
|---|---|
| 向下滚动 | sim-use gesture scroll-up --device <UDID>(scroll-up = 内容向上移动 = 页面向下滚动) |
| 输入文本 | sim-use type 'hello' --device <UDID> |
| 粘贴 Unicode | 聚焦输入框,然后执行 sim-use paste 'こんにちは 🎉' --device <UDID>;iOS 在 Simulator.app 或 Device Hub 中均可使用,且不受键盘模式影响 |
| 硬件按钮 | sim-use button home --device <UDID> |
| Android 返回 | sim-use button back --device <UDID> |
| 等待动画 | 在命令之间执行 sleep 0.4,或使用 --pre-delay 0.5 |
| 切换开关 | sim-use tap @N --duration 0.05 --device <UDID>(UISwitch 需要短暂按住) |
| 滑动 | sim-use swipe --from 50,500 --to 350,500 --device <UDID> |
| 捏合放大 | sim-use gesture pinch-out --device <UDID>(双指张开) |
| 旋转(双指手势) | sim-use gesture rotate-cw --angle 90 --device <UDID> |
| 旋转设备(iOS) | sim-use ios rotate landscape-left --device <UDID>——界面名称以 App: 标题打印的名称为准;之后重新运行 ui |
| 折叠 / 展开(iPhone Duo) | sim-use ios fold closed|open --device <UDID>;sim-use ios fold --device <UDID> 会报告当前启用的显示屏;之后重新运行 ui |
| 录制证据 GIF | sim-use record-video --output demo.gif --device <UDID>——使用 SIGINT/SIGTERM 停止(绝不要使用 SIGKILL);停止后会转码;在 PR 中自动内嵌播放;添加 --gif-markers 可插入 START/END 循环边界卡片 |
在 iOS 模拟器中粘贴
对于 Unicode 和兼容输入法的文本,优先使用 paste。默认行为会保留当前焦点字段中的插入点和选区。--replace 会先全选。使用 --via-menu --target-id <id> 或 --target-x/--target-y 可显式聚焦目标。两种方式都会使用系统原生的“粘贴”操作;无需更改硬件键盘或同步设置。
对于多行文本或包含 Shell 特殊字符的文本,请使用 printf '%s' "$TEXT" | sim-use paste --stdin --device <UDID> 或 paste --file <path>。仍会遵循控件的原生规则,包括单行规范化和智能粘贴空格。如果无法使用“全选”,且无法通过辅助功能确认字段为空,--replace 会在粘贴前失败。切勿将此拒绝视为替换成功。对于来源未知或不明确的菜单操作,也会拒绝;不要通过点击应用中标签相同的按钮来绕过此拒绝。务必核实字段中的实际内容。如果命令报告可能已发送粘贴操作,请先检查字段再重试;命令不会自动重新发送粘贴操作。
共享剪贴板同步也可能覆盖 Mac 和其他设备上的内容。请确保只有一个剪贴板写入方处于活动状态。sim-use 会串行处理自身的粘贴操作,并检查是否意外读回了内容,但无法阻止外部写入。切勿打印无关的剪贴板数据,也不要用旧备份覆盖较新的外部内容。
批量粘贴支持默认粘贴、替换、文件和显式菜单目标;paste --stdin 不能作为批处理步骤,因为 stdin 属于整个批处理。发生暂时性的启动就绪错误后,失败的批处理不会自动重放。重试前请检查 UI 和已完成的步骤,包括使用 --continue-on-error 时。
实体 iOS 设备(实验性)
顶层动词会自动路由到实体 UDID,但仅限其中三个:ui、tap(#<id> / --id / --label / --label-contains / --element-type 形式)和 screenshot。切勿假定其能力与模拟器一致——其他所有动词或形式(坐标、@N/#N 别名、滑动/手势/多点触控、输入/粘贴、录制、--value/--label-regex/--frame/--duration/--wait-timeout)都会拒绝执行,并在 hint 中说明原因和最接近的替代方案;请阅读提示,不要重试。sim-use ios-device 命名空间是 ios/android 的实体设备专用对应版本(动词相同,另支持 ECID 寻址和树调优标志)。
**ui / tap 的硬性要求:**设备必须已配对、受信任、解锁并开启开发者模式,且前台目标应用必须使用开发签名,并设置 get-task-allow=true。使用 Development 配置文件安装的 Release 配置二进制文件受支持。Distribution、Ad Hoc、TestFlight、App Store 和系统应用均不受支持;不要重试,也不要声称成功。sim-use 本身不会安装或签署任何 runner,也不需要 Developer Disk Image。screenshot 不受签名规则限制——它通过 CoreDevice 运行并截取任何屏幕,包括系统应用。
# Physical-device preflight — physical rows carry kind `physical`
sim-use devices
# Observe → act → verify, same loop and verbs as the simulator
sim-use ui --device <UDID>
sim-use tap --label "Friends" --element-type Button --device <UDID>
sim-use ui --device <UDID>
# For dynamic labels
sim-use tap --label-contains "Reply" --element-type Button --device <UDID>
# By stable identifier (the #id shown in ui) — positional or --id
sim-use tap '#BackButton' --device <UDID>
# Screenshot — any screen, not limited to development-signed apps
sim-use screenshot --output shot.png --device <UDID>
此实验性功能的规则:
- **将层级结构错误视为能力限制。**如果命令提示层级结构不可用,请确认屏幕已解锁,并检查已安装应用最终的
get-task-allow权限。不要改用坐标或焦点遍历。 - **使用
#id或标签点击,不要使用@N。**元素句柄会随 DTX 连接失效,因此没有@N别名(也不支持坐标——没有几何信息)。使用大纲中显示的#id(位置参数#<id>或--id)——它稳定可靠,在标签动态变化时尤其适用——或使用--label/--label-contains,并通过--element-type消除歧义。导航栏的返回按钮会显示为普通的Button "<previous screen title>" #BackButton;像点击其他元素一样,点击#BackButton(或显示的标签)即可返回——没有专用的“back”动词。 - **始终验证。**Activate 会立即返回,不等待操作结果。重新运行
sim-use ui,确认状态符合预期后再继续。 - **遵守能力限制提示。**出现
not supported on physical iOS devices错误,说明该通道不支持此操作,并非暂时性故障——请遵循提示(通常是使用ui和tap '#<id>' / --label),不要重试或替换为看似相近的形式。所有动词都支持--json,并使用标准的{ok, data}封装;实体设备结果包含"kind":"physical",且不含几何字段(screen、x/y)。 - **按秒而非毫秒预留时间。**完整树需要几秒钟。
sim-use ios-device ui --fast速度更快,但会省略嵌套元素;不要高频轮询。
如果 ui 成功却返回零个元素,或 tap 对不存在或有歧义的标签报告成功,请视为 sim-use 的错误;命令理应明确报错。
2. 常见问题
快速症状索引——详细操作方法请参阅 references/pitfalls.md。
| 症状 | 原因 | 解决方法 |
|---|---|---|
tap --label 点到了错误的元素 | 标签冲突(例如,标题栏和标签栏包含相同文本) | 添加 --frame minY=0.7r 或 --element-type 以缩小范围 |
导航后 tap @N 失败 | 别名缓存已过期 | 点按前重新运行 ui |
App: 行显示了错误的应用 | 系统层(提醒、共享表单)位于最上层 | 先将其关闭,然后重新运行 ui |
出现 multipleMatches 错误 | 多个元素匹配同一选择器 | 使用 --frame、--element-type 或更具体的选择器 |
| 点按成功,但没有任何反应 | 动画正在进行,或元素尚不可交互 | 添加 --pre-delay 0.3 或 --wait-timeout 3 |
iOS:paste 失败或文本缺失 | 没有聚焦的字段、自定义编辑菜单、粘贴提示,或外部剪贴板写入器 | 聚焦该字段,或使用 paste --via-menu --target-id <id>;检查提示并确认实际字段内容。不要改用 type 来替代 Unicode 输入 |
Android:paste 被拒绝 | 后台剪贴板访问被阻止 | 改用 type |
大纲中的标签显示 U+FFFC | iOS 图标占位符字符 | 使用排除该前缀的 --label-regex 进行匹配 |
[i] … covers ~N% of the screen 警告(文本输出,或 --json 顶层的 advisory 键) | 选择器解析到了一个几乎占满全屏的包装元素(Flutter/画布 UI 中很常见),点按落在其中心,因此可能没有点中目标控件 | 重新运行 ui,通过 @N/#<id> 定位控件,或传入明确的 -x/-y/--point |
[i] Screen orientation could not be confirmed…/…coordinates may be stale… 提示 | 设备/应用已旋转(App: 标题显示类似 (landscape-right) 的标记),且方向自动校准无法验证坐标映射;或者 @N 快照早于旋转操作 | 重新运行 ui 并再次点按;校准成功后,选择器会自动处理旋转。明确指定的 -x/-y/--point 使用大纲(视觉空间)坐标,并依赖同一校准;--coordinate-space native 用于设备原生纵向坐标 |
大纲为空或未显示应用中可见的控件,或出现 [i] … recovered from other processes … 提示(--json:advisory.kind: remote_content_recovery) | 应用提供了空的辅助功能树。系统选择器中属于正常情况。其他情况下,在 iOS 模拟器上,一个可能原因是应用启动时辅助功能处于关闭状态 | 将大纲与屏幕进行对照。如果缺少控件,请参阅缺失的应用控件 |
iOS:在所有屏幕上,ui 都报错 No translation object returned for simulator <UDID> for the frontmost application,而 screenshot 和 ui --point 仍可正常工作 | 模拟器的辅助功能桥接(CoreSimulatorBridge)已过期 | 按照错误提示操作:运行 xcrun simctl spawn <UDID> launchctl stop com.apple.CoreSimulator.bridge(launchd 会立即重启它),然后重试 ui。刚启动模拟器后,先等待几秒再重试 |
iOS:ui 标题以 inner display、cover display(较早的版本中,内屏显示为 display LCD-1)或 display Resizable 560x874 结尾 | 应用正在指定的 Duo 屏幕或 Xcode 27 调整大小模式显示屏上运行。内屏处于激活状态并不代表铰链已完全打开。--json 保留原始名称;主屏/外屏上没有 data.display。Touch、ui、ui --point、screenshot 和 record-video/stream-video(h264、bgra)都会自动路由 | 按常规方式操作——选择器和 @N 会定位到该显示屏;iOS 27.1 SDK 应用的竖栏(返回按钮、标签)会最后列为 [Trailing …]/[Leading …]。明确指定的 -x/-y 使用 ui 在那里打印的数值;--coordinate-space native 表示该显示屏的原生坐标点。请参阅 iPhone Duo 和 调整大小模式 |
3. 崩溃感知
完整协议请参阅 references/crash-awareness.md。摘要:
sim-use 会监视目标进程是否在命令之间消失。检测到崩溃时:
================ PROCESS DISAPPEARED ================
com.example.app (pid 12345) was alive at the previous command and is GONE now.
在 Android 上,ui 还会直接通过无障碍树检测 AOSP 系统崩溃对话框。
必须执行的响应:
- 停止。 不要悄悄重新启动或继续操作。
- 使用横幅文本向用户报告崩溃。
- 等待用户指示后再继续。
有意重新启动后,调用 sim-use app-state --reset 清除信号。
4. 升级处理
遇到以下情况时,停止操作并询问用户:
- 使用现有消歧条件无法解决选择器冲突。
- 预检失败,且自动修复未能恢复。
- 任务需要执行破坏性操作(删除数据、卸载应用)。
- 同一操作已重试 3 次,但没有进展。
5. 退出检查清单
在报告任务已完成之前:
- 运行
sim-use ui(或screenshot)以捕获最终状态。 - 确认屏幕与预期结果相符。
- 如果结果不明确,向用户展示最终的
ui输出或截图。