关于 device-interaction
用于在设备上验证应用表现(包括界面和触控交互)的子 Agent 技能。它规定了启动、检查、操作应用并汇报结果的会话流程。
主要功能
- 01基于设备验证应用
- 02工作区与设备会话流程
- 03检查截图和界面层级
- 04支持触控、键盘和遥控器操作
- 05报告功能与视觉问题
适用场景
- 01实现后检查界面功能
- 02排查异常触控行为
- 03验证应用导航和可见控件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ tartinerlabs/ skills --skill device-interaction安装后在 Claude Code 中输入/device-interaction确认已加载。
详细介绍
设备交互
触发条件:用户要求验证/测试/检查应用在设备上是否能正常工作、在实现影响 UI 的功能后需要进行设备验证、用户说“它能工作吗”“测试一下这个”“在设备上检查”、用户反馈 UI 未按预期工作、需要调试触摸/交互问题。 不触发条件:用户仅询问单元测试、仅构建而不涉及设备测试的请求、不涉及设备测试的代码审查、模拟器配置问题、不影响 UI 的改动(例如注释、重构、非 UI 逻辑)。
面向主 Agent
这是一个 SUBAGENT 技能。 在需要设备验证时,通过 Agent 工具调用它。如果该工作已有打开的会话,请将该会话标识符提供给子 Agent,仅供该子 Agent 独占使用。
Agent tool:
- subagent_type: "general-purpose"
- description: "Verify login feature works"
- prompt: "Using the device-interaction skill, verify that the login feature works correctly on session <session-identifier>. Launch the app, capture screenshot and UI hierarchy, check that the login button is visible and tappable, and report if the implementation is working correctly."
在实现影响 UI 的功能后,调用此技能以验证实现在设备上能正常工作。
会话生命周期
DeviceInteractionStartWorkspaceSession (workspace-backed; do this early, runs in the background)
→ DeviceInteractionInstallAndRun (after each code change; includes building)
→ DeviceEventSynthesize (interact + observe, repeatable)
→ DeviceInteractionEndSession (when done — keeping sessions open is resource-heavy)
有两种方式启动会话:
- DeviceInteractionStartWorkspaceSession —— 绑定到工作区。DeviceInteractionInstallAndRun 必须使用它。在验证你正在构建的应用时使用。
- DeviceInteractionStartSession —— 不绑定任何工作区。仅在与已安装的应用交互时使用。
DeviceInteraction(Workspace)StartSession 工具
设备发现
打开新的设备交互会话时,传入设备标识符以选择设备,或省略它使用当前目标。传入空字符串以获取可用目标列表。
DeviceInteractionInstallAndRun 工具
可选参数
commandLineArguments—— 启动时传递给应用的参数。使用$(inherited)作为标记以保留 scheme 现有的参数(例如["$(inherited)", "--reset-state"]在末尾添加一个额外参数)。environmentVariables—— 启动时在应用环境中设置的键/值对。使用"$(inherited)"作为键以保留 scheme 现有的环境变量(例如{"$(inherited)": "", "DEBUG_MODE": "1"})。
省略这两个参数即保持 scheme 的参数和环境不变。
优先使用这些参数,而不是直接编辑 scheme。 它们仅在该次运行中生效,不会对用户的配置产生持久影响。
面向子 Agent
务必报告可能由代码引起的 UI 问题:文字重叠或不可读、图像/文字意外被裁剪、颜色错误等。
DeviceEventSynthesize 工具
此工具允许执行交互并观察设备状态。
读取层级文件
层级文件包含每个元素计算得出的 hitPoint 位置:
UIView {{100, 200}, {60, 30}}, hitPoint: {130.0, 215.0}
UIButton "Login" {{110, 205}, {30, 20}}, hitPoint: {125.0, 215.0}
UIButton "Login2" {{140, 205}, {20, 20}}, hitPoint: {150.0, 215.0}, activationBundleId: com.your.app
{100, 200}—— 原点位置{50, 30}—— 宽度和高度hitPoint: {125.0, 215.0}—— 计算得出的 hitPoint 点(最适合用于点击)
触摸事件务必优先使用 hitPoint 坐标。 只有在点击 hitPoint 并通过重新捕获确认其没有效果后,才可以退回到通过截图估算的原始位置。
警告:标记为 isRemoteLeafPlaceholder 的元素不会报告子元素 —— 与它们交互需要退回到通过截图估算的坐标。
多个应用(activationBundleId 标注)
当屏幕上重叠了来自多个应用的窗口时,每个元素行都会附加一个 activationBundleId: <bundle-identifier> 后缀。
如果某个元素行带有 activationBundleId,则与之进行任何交互都需要先激活该应用,方法是将 activationBundleId 参数传给 DeviceEventSynthesize 工具。
提示:你可以只传 activationBundleId 而不传 interactionCommand。
应用激活开销很大,因此仅在必要时使用。
示例:
Device orientation: Landscape Right
------------------------
Application bundle identifier: com.some.app
Application UI orientation: Landscape Left
Application, pid: 123, label: ' '
Window, {{0.0, 0.0}, {1133.0, 744.0}}, hitPoint: {566.5, 372.0}
Other, {{0.0, 0.0}, {744.0, 1133.0}}, hitPoint: {372.0, 372.0}
...
------------------------
Application bundle identifier: com.some.other.app
Application UI orientation: Landscape Left
Application, pid: 333, label: ' '
Window, {{0.0, 0.0}, {1133.0, 744.0}}, hitPoint: {566.5, 372.0}
...
交互命令语法
interactionCommand 参数接受一种命令语法:
| 命令 | 描述 |
|---|---|
t <x> <y> [duration] | 在坐标处点按,可指定按住时长 |
d <x> <y> | 双击 |
t <x1> <y1> f <x2> <y2> [duration] | 从 (x1,y1) 滑动到 (x2,y2) |
drag <x1> <y1> <x2> <y2> [holdDuration] [moveDuration] | 拖放:在 (x1,y1) 按住,然后缓慢移动到 (x2,y2)。用于重新排序列表或拖放到目标位置 |
mt [x1 y1, ...] dur [x1 y1, ...] dur ... | 多点触控序列。每个 [...] 关键帧列出触摸位置(x y)。每个块之后的时长是到下一个关键帧的移动时间;对于最后一个块,它是抬起前的按住时间。当某个手指的槽位是空的逗号项(例如 [, x y])或当该帧的条目数少于该手指的索引时,该手指结束——两者等效。抬起后又在后续关键帧中重新出现的手指会开始一次新的点按。 |
b h/p/u/d [duration] | 硬件按钮:h=Home,p=Power,u=VolUp,d=VolDown |
b c/s/a [duration] | 仅 watchOS。 c=按下数码表冠,s=侧边按钮,a=操作按钮(仅部分设备) |
sender keyboard kbd <text> | 输入文本;必须是命令链中的最后一条命令——kbd 之后的所有内容都会按原样处理(保留多个空格)。对于特殊字符,使用 \u{XXXX} Unicode 转义:\u{000A}(回车/换行),\u{0009}(制表符) |
w duration | 等待一段时间而不执行任何操作 |
orientation faceDown/faceUp/landscapeLeft/landscapeRight/portrait/portraitUpsideDown | 设置设备方向(仅 iOS) |
c <rotations> | 仅 watchOS。 旋转数码表冠;符号表示方向,1.0 = 一整圈 |
r up/down/left/right/select/menu/playpause/home | 仅 tvOS。 按下 Siri Remote 按钮以移动焦点、选择或返回主屏幕 |
示例:
"t 100 200"- 在 (100, 200) 处点按"d 200 300"- 在 (200, 300) 处双击"t 200 600 f 200 200 0.3"- 向上滑动(滚动到下方内容)"t 200 200 f 200 600 0.3"- 向下滑动(滚动到上方内容)"drag 100 300 100 100"- 从 (100,300) 拖放到 (100,100),使用默认时长"drag 100 300 100 100 0.5 1.5"- 拖放,按住 0.5s,移动 1.5s"mt [100 200] 0.5 [100 200] 1.0 [300 400] 0.2"- 拖拽:在 (100,200) 按住 0.5s,在 1.0s 内移动到 (300,400),按住 0.2s 后抬起"mt [100 300, 300 300] 0.5 [175 300, 225 300] 0.5"- 双指捏合:两根手指起始相距 200px,并向彼此移动"b h"- 按下 Home 按钮"b h b h"- 按两次 Home 按钮进入应用切换器"b h w 0 b h"- 唤醒并解锁设备(仅无密码设备)"b c"- watchOS:按下数码表冠"b s"- watchOS:按下侧边按钮"b a"- watchOS:按下操作按钮(仅部分设备)"sender keyboard kbd hello world"- 输入带空格的文本"sender keyboard kbd hello world"- 输入保留多个空格的文本"sender keyboard kbd submit\u{000A}"- 输入文本然后按回车/提交"w 0.3"- 等待 0.3s"orientation landscapeLeft"- 将设备旋转到横向"c 1.0"- watchOS:将数码表冠旋转一整圈(例如滚动列表)"c -0.5"- watchOS:将表冠向另一方向旋转半圈"r down"- tvOS:将焦点向下移动"r select"- tvOS:在聚焦元素上按下 Select"r home"- tvOS:返回主屏幕
标准 Subagent 工作流
在任何交互之前,始终捕获并读取层级结构(以及截图)。在任何交互之后,再次捕获并验证结果。对于复杂组件(例如开关或滑块),查看嵌套元素(例如 Switch 或 Slider)——附近的元素可能对应实际控件。完成后,将发现报告给主 Agent。
- 要在不交互的情况下捕获,请使用 DeviceEventSynthesize 并传入空的 interactionCommand。
- 切勿仅凭截图猜测位置——使用层级结构的 hitPoint 坐标;只有在尝试 hitPoint 并失败后,截图估算才可作为后备方案。
- 如果没有把握或缩略图分辨率不足,请分析全尺寸截图。
tvOS(Apple TV)
tvOS 是基于焦点的:没有触摸屏,因此坐标点按/滑动不适用。同一时间恰好有一个元素处于焦点状态,你只能激活当前获得焦点的元素。请改用 Siri Remote 来操作:
- 读取层级结构,查看所有可聚焦元素以及哪一个被标记为
Focused。 - 使用
r up/r down/r left/r right将焦点移向目标,然后用r select激活它。使用r menu返回。 - 在一条命令中串联多次按键并只捕获一次,而不是每次按键后都捕获。层级结构会列出元素顺序,因此计算到目标的步数并一起发送,例如
r right r right r right,然后捕获以确认焦点已落位。
时机与重试
- 应用启动:启动会话后,应用可能需要几秒钟加载。捕获层级结构并检查其是否具有有意义的 UI 元素,然后再进行交互。如果层级结构基本为空或显示启动画面,请再次捕获后再继续。
- 交互之后:如果点按或滑动没有产生预期变化,请重新捕获层级结构并重试交互一次(元素可能在动画期间发生了位移)。如果重试一次后仍然失败,请报告失败,而不是无限重试。
- 加载状态:如果层级结构显示加载指示器或加载指示符,请稍等片刻后再次捕获。不要与仍在加载的元素交互。
- 性能:避免添加可能拖慢流程的等待延迟。工具的设计目标是在动画完成后即完成。
判断成功与失败
验证时,请区分以下类别:
- 功能性缺陷(始终报告):元素不响应点按、导航到错误页面、崩溃、数据未显示、缺少预期 UI 元素。
- 视觉/布局缺陷(始终报告):文本重叠、标签被截断、元素渲染到屏幕外、颜色错误、对齐错乱。
- 瞬态状态(不要报告为缺陷):加载指示器、短暂动画、键盘出现/消失。在过渡完成后再次捕获。
- 意外退出(始终报告):崩溃、应用退出。要识别此类问题,请跟踪进程 id 并捕获进程的标准输出。
- 预期行为(不要报告为缺陷):带占位文本的空状态、表单未完成时按钮禁用、权限对话框。
错误处理
- 如果应用不可见,请重试一次,这可能是设备缓慢导致的。
- 如果点按目标不明确,请重新读取层级结构数据以获取正确的 hitPoint 坐标。
- 你可以检查运行时日志进行故障排查。如果你怀疑存在时机缺陷,请建议主 Agent 在相关代码中临时添加
print语句,这可能有助于诊断问题。 - 将问题及细节和建议报告回主 Agent。