android2harmony 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +1 -407
  2. package/agents/self-tester.md +376 -376
  3. package/dist/index.js +134 -116
  4. package/dist/index.js.map +3 -3
  5. package/package.json +36 -32
  6. package/skills/a2h-resource-convert/SKILL.md +902 -0
  7. package/skills/a2h-resource-convert/references/code-vector-icon-rules.md +335 -0
  8. package/skills/{hmos-resources-convert → a2h-resource-convert}/references/conversion-rules.md +15 -2
  9. package/skills/{hmos-resources-convert → a2h-resource-convert}/references/dependency-analysis-rules.md +24 -2
  10. package/skills/a2h-resource-convert/references/lottie-conversion-rules.md +219 -0
  11. package/skills/{hmos-resources-convert → a2h-resource-convert}/references/resource-mapping-rules.md +27 -2
  12. package/skills/{hmos-resources-convert → a2h-resource-convert}/references/xml-drawable-to-svg-rules.md +118 -1
  13. package/skills/a2h-resource-convert/scripts/a2h_resource_convert.js +2166 -0
  14. package/skills/a2h-resource-convert/scripts/code_vector_icons.js +607 -0
  15. package/skills/a2h-resource-convert/scripts/package.json +3 -0
  16. package/skills/a2h-resource-convert/scripts/svg_fidelity_check.js +632 -0
  17. package/skills/a2h-ui-transfer/SKILL.md +420 -0
  18. package/skills/a2h-ui-transfer/references/conversion-procedure.md +572 -0
  19. package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/_/350/214/203/345/274/217/351/200/211/346/213/251/350/257/264/346/230/216.md +1 -1
  20. package/skills/a2h-ui-transfer/references/mvvm-v2/_/350/243/205/351/245/260/345/231/250/351/200/237/346/237/245.md +133 -0
  21. package/skills/{hmos-batch-ui-align/scripts/android_parse_fast.ts → a2h-ui-transfer/scripts/android_parse_fast.js} +397 -283
  22. package/skills/a2h-ui-transfer/scripts/arkts_static_check.js +1624 -0
  23. package/skills/a2h-ui-transfer/scripts/measure_pack.js +1005 -0
  24. package/skills/a2h-ui-transfer/scripts/package.json +3 -0
  25. package/skills/hmos-incremental-ui-align/README.md +251 -251
  26. package/skills/hmos-incremental-ui-align/SKILL.md +364 -364
  27. package/skills/hmos-incremental-ui-align/references/State_Model_Template.md +2 -2
  28. package/skills/hmos-incremental-ui-align/scripts/app_feature_verify.js +790 -0
  29. package/skills/hmos-incremental-ui-align/scripts/extract_checklist.js +285 -0
  30. package/skills/hmos-incremental-ui-align/scripts/navigation-capure.md +76 -76
  31. package/skills/hmos-incremental-ui-align/scripts/page_capture.js +756 -0
  32. package/skills/hmos-incremental-ui-align/scripts/page_capture_burst.js +155 -0
  33. package/skills/hmos-batch-ui-align/SKILL.md +0 -141
  34. package/skills/hmos-batch-ui-align/references/conversion-procedure.md +0 -217
  35. package/skills/hmos-incremental-ui-align/scripts/app_feature_verify.ts +0 -999
  36. package/skills/hmos-incremental-ui-align/scripts/extract_checklist.ts +0 -343
  37. package/skills/hmos-incremental-ui-align/scripts/page_capture.ts +0 -977
  38. package/skills/hmos-incremental-ui-align/scripts/page_capture_burst.ts +0 -188
  39. package/skills/hmos-resources-convert/SKILL.md +0 -654
  40. package/skills/hmos-resources-convert/template/AppScope/app.json5 +0 -10
  41. package/skills/hmos-resources-convert/template/AppScope/resources/base/element/string.json +0 -8
  42. package/skills/hmos-resources-convert/template/AppScope/resources/base/media/background.png +0 -0
  43. package/skills/hmos-resources-convert/template/AppScope/resources/base/media/foreground.png +0 -0
  44. package/skills/hmos-resources-convert/template/AppScope/resources/base/media/layered_image.json +0 -7
  45. package/skills/hmos-resources-convert/template/build-profile.json5 +0 -42
  46. package/skills/hmos-resources-convert/template/code-linter.json5 +0 -32
  47. package/skills/hmos-resources-convert/template/entry/build-profile.json5 +0 -33
  48. package/skills/hmos-resources-convert/template/entry/hvigorfile.ts +0 -6
  49. package/skills/hmos-resources-convert/template/entry/obfuscation-rules.txt +0 -23
  50. package/skills/hmos-resources-convert/template/entry/oh-package.json5 +0 -10
  51. package/skills/hmos-resources-convert/template/entry/src/main/ets/entryability/EntryAbility.ets +0 -48
  52. package/skills/hmos-resources-convert/template/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets +0 -16
  53. package/skills/hmos-resources-convert/template/entry/src/main/ets/pages/Index.ets +0 -23
  54. package/skills/hmos-resources-convert/template/entry/src/main/module.json5 +0 -55
  55. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/color.json +0 -8
  56. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/float.json +0 -8
  57. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/string.json +0 -16
  58. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/background.png +0 -0
  59. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/foreground.png +0 -0
  60. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/layered_image.json +0 -7
  61. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/startIcon.png +0 -0
  62. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/backup_config.json +0 -3
  63. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/main_pages.json +0 -5
  64. package/skills/hmos-resources-convert/template/entry/src/main/resources/dark/element/color.json +0 -8
  65. package/skills/hmos-resources-convert/template/entry/src/mock/mock-config.json5 +0 -2
  66. package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/Ability.test.ets +0 -35
  67. package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/List.test.ets +0 -5
  68. package/skills/hmos-resources-convert/template/entry/src/ohosTest/module.json5 +0 -16
  69. package/skills/hmos-resources-convert/template/entry/src/test/List.test.ets +0 -5
  70. package/skills/hmos-resources-convert/template/entry/src/test/LocalUnit.test.ets +0 -33
  71. package/skills/hmos-resources-convert/template/hvigor/hvigor-config.json5 +0 -23
  72. package/skills/hmos-resources-convert/template/hvigorfile.ts +0 -6
  73. package/skills/hmos-resources-convert/template/oh-package-lock.json5 +0 -28
  74. package/skills/hmos-resources-convert/template/oh-package.json5 +0 -10
  75. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +0 -0
  76. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-interaction-mapping-reference.md +0 -0
  77. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-layout-mapping-reference.md +0 -0
  78. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md" +0 -0
  79. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md" +0 -0
  80. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md" +0 -0
  81. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md" +0 -0
  82. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md" +0 -0
  83. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md" +0 -0
  84. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md" +0 -0
  85. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md" +0 -0
  86. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md" +0 -0
  87. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md" +0 -0
  88. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md" +0 -0
  89. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md" +0 -0
  90. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md" +0 -0
  91. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/!!/350/257/255/346/263/225/357/274/232/345/217/214/345/220/221/347/273/221/345/256/232.md" +0 -0
  92. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Computed/350/243/205/351/245/260/345/231/250/357/274/232/350/256/241/347/256/227/345/261/236/346/200/247.md" +0 -0
  93. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Event/350/243/205/351/245/260/345/231/250/357/274/232/350/247/204/350/214/203/347/273/204/344/273/266/350/276/223/345/207/272.md" +0 -0
  94. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Local/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/351/203/250/347/212/266/346/200/201.md" +0 -0
  95. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Monitor/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/344/277/256/346/224/271/345/274/202/346/255/245/347/233/221/345/220/254.md" +0 -0
  96. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@ObservedV2/350/243/205/351/245/260/345/231/250/345/222/214@Trace/350/243/205/351/245/260/345/231/250/357/274/232/347/261/273/345/261/236/346/200/247/345/217/230/345/214/226/350/247/202/346/265/213.md" +0 -0
  97. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Once/350/243/205/351/245/260/345/231/250/357/274/232/345/210/235/345/247/213/345/214/226/345/220/214/346/255/245/344/270/200/346/254/241.md" +0 -0
  98. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Param/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/244/226/351/203/250/350/276/223/345/205/245.md" +0 -0
  99. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Provider/350/243/205/351/245/260/345/231/250/345/222/214@Consumer/350/243/205/351/245/260/345/231/250/357/274/232/350/267/250/347/273/204/344/273/266/345/261/202/347/272/247/345/217/214/345/220/221/345/220/214/346/255/245.md" +0 -0
  100. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Type/350/243/205/351/245/260/345/231/250/357/274/232/346/240/207/350/256/260/347/261/273/345/261/236/346/200/247/347/232/204/347/261/273/345/236/213.md" +0 -0
  101. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/AppStorageV2/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200UI/347/212/266/346/200/201/345/255/230/345/202/250.md" +0 -0
  102. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/MVVM/346/250/241/345/274/217/357/274/210V2/357/274/211.md" +0 -0
  103. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/PersistenceV2/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md" +0 -0
  104. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2//347/212/266/346/200/201/347/256/241/347/220/206V1/345/220/221V2/350/277/201/347/247/273/344/270/216/346/267/267/347/224/250/346/214/207/345/257/274.md" +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -1,251 +1,251 @@
1
- # hmos-ui-align
2
-
3
- HarmonyOS-Android UI 自动对齐流水线。
4
-
5
- ## 它解决什么问题
6
-
7
- 以往对齐鸿蒙和安卓 UI 的流程需要:
8
- 1. 人工把设备点到目标页面
9
- 2. 手动跑 parse 脚本截图 + dump view tree
10
- 3. 肉眼对比差异
11
- 4. 手写改鸿蒙源码
12
-
13
- 每多一个页面/弹窗/tab,上述步骤都要重复一遍,非常费时。
14
-
15
- 本 skill 做了三件事:
16
- - 自动**寻路**(视觉模型根据自然语言点到指定页面,模型复用 `~/.hometrans/config.json` 统一配置)
17
- - 自动**采集** view tree + 截图(安卓走 adb、鸿蒙走 hdc)
18
- - 自动**对比 + 改码**,并按 MVVM 模式把 mock 数据放到 Model 层
19
-
20
- 用户只需要一句自然语言 + 点击路径。
21
-
22
- > ⚠️ 当前只保证 **UI 对齐**,不保证功能行为对齐。
23
-
24
- ---
25
-
26
- ## 前置条件
27
-
28
- 1. **设备连接**
29
- - 安卓设备:已装目标 App,`adb devices` 能看到
30
- - 鸿蒙设备:已装目标 App,`hdc list targets` 能看到
31
- - 两台设备建议都保持亮屏解锁
32
-
33
- 2. **运行时依赖**
34
- - 采集脚本 `page_capture.ts` 与寻路脚本 `app_feature_verify.ts` 均为 TypeScript 版,用 Node 直接运行(Node 22.18 23.6,原生剥离类型,无需编译、零第三方依赖)。确认 `node --version` 即可,无需 Python / uv。
35
-
36
- 3. **模型配置**(寻路导航用)
37
- 与自测共用同一个多模态模型。插件场景下由 `a2h_app_feature_verify` 工具从 host 解析 key 并经 stdin 注入 `app_feature_verify.ts`(`--model-stdin`),key 不进环境变量、不泄露给 adb/hdc 子进程,无需单独配置。直接 `node` 运行(调试)时:`ht init` 把模型四要素写入 `~/.hometrans/config.json` 的 `autotest.unified_model`(并导出 `HOMETRANS_MODEL_*` 环境变量),脚本优先读 stdin → `--api-key` → 环境变量 → config.json;都没有时兼容老版本 `GLM_API_KEY`(走智谱 GLM 端点)。
38
-
39
- 4. **鸿蒙 SDK 路径**(给改码阶段查 API 用)
40
- 取自 OS 环境变量 `OHOS_SDK_PATH` / `HMS_SDK_PATH`(为空时由 `DEVECO_SDK_HOME` 派生),由 `ht init` 写入机器环境变量;例如 DevEco Studio 自带的 `E:\DevEco Studio\sdk\default\openharmony\ets`。
41
-
42
- ---
43
-
44
- ## 入参与配置
45
-
46
- 本 skill 需要两类信息:
47
-
48
- **① 调用时传入的入参**(每次按需提供):
49
-
50
- | 入参 | 必填 | 说明 |
51
- |---|---|--------------------------------------------------------------------|
52
- | `android_project_dir` | 是 | 安卓源码根目录 |
53
- | `harmony_project_dir` | 是 | 鸿蒙工程根目录(会被直接修改) |
54
- | `capture_output_dir` | 否 | 采集产物输出目录(截图/view tree/分析报告)。默认 `<harmony_project_dir>/.hometrans/capture_output` |
55
-
56
- **② 全局配置**(统一链路「环境变量 → `~/.hometrans/config.json` → 询问用户」,由 `ht init` 同时写入机器环境变量与 config.json,无需每次传):
57
-
58
- - 模型配置 ← 插件场景由 `a2h_app_feature_verify` 工具经 stdin 注入(不经环境变量);直接 `node` 运行时读 `HOMETRANS_MODEL_*` 环境变量,缺失回落 `~/.hometrans/config.json` 的 `autotest.unified_model`(与自测共用同一个模型)
59
- - 鸿蒙 SDK ETS API 目录 ← 环境变量 `OHOS_SDK_PATH` / `HMS_SDK_PATH`(为空时由 `DEVECO_SDK_HOME` 派生为 `<DEVECO_SDK_HOME>/default/openharmony/ets`、`.../hms/ets`)
60
-
61
- > **Step 0.0** 会在执行前检查这些环境变量是否存在;缺失时会要求用户输入。
62
-
63
- > 安卓/鸿蒙两端的 **App 显示名**(`android.app_name` / `harmony.app_name`)和 **包名**(`android.package` / `harmony.package`)也无需提供——skill 的 **Step 0.1** 会按 `android_project_dir` / `harmony_project_dir` 自动解析:
64
- > - 安卓包名取自 app 模块 `build.gradle(.kts)` 的 `applicationId`(兜底 `AndroidManifest.xml` 的 `package`);App 名取自启动 Activity / `<application>` 的 `android:label`(`@string/xxx` 会去 `res/values*/strings.xml` 解析,优先中文)。
65
- > - 鸿蒙包名取自 `AppScope/app.json5` 的 `bundleName`;App 名取自 `app.json5` 的 `label`(`$string:xxx` 会去 `AppScope/resources/**/element/string.json` 解析,优先中文)。
66
- >
67
- > 若某项无法从工程目录解析,skill 会回到询问用户。
68
-
69
- ---
70
-
71
- ## 使用方式
72
-
73
- 在 Claude Code 里直接调用 skill,**把两个工程目录附在需求里**(可作为命名参数,也可在自然语言里说明):
74
-
75
- ```
76
- /hmos-ui-align android_project_dir: <安卓工程绝对路径> harmony_project_dir: <鸿蒙工程绝对路径>
77
- <自然语言需求,描述要对齐的页面+点击路径>
78
- ```
79
-
80
- ### 写需求的三个要点
81
-
82
- 1. **页面路径写清楚**
83
- 用「→」或「-」标注一级一级的点击步骤,比如 `首页 → 点击"AI智能填报" → 点击"专业"筛选按钮`。路径越具体,寻路成功率越高。
84
-
85
- 2. **说明是否要覆盖交互态**
86
- 默认会自动扫描 tab、弹窗、下拉等交互元素并逐个采集。如果只想对齐主页面,请明确说「只对齐主页面,不管弹窗和 tab」。
87
-
88
- 3. **说明是否用 Mock 数据**
89
- 默认用 mock 数据。如果想调真实后台,在需求里写「不要 mock 数据,调用真实后台」。
90
-
91
- ### 例子
92
-
93
- **例 1(单个弹窗对齐)**
94
- ```
95
- /hmos-ui-align 鸿蒙版本app(登录状态下,进入首页-点击"AI智能填报"-点击"专业"筛选按钮)
96
- 得到的弹窗样式和安卓同样路径得到的页面不一致,请修改鸿蒙源码将上述页面与安卓版本完全对齐
97
- ```
98
-
99
- **例 2(补齐缺失页面 + 调真实后台)**
100
- ```
101
- /hmos-ui-align 鸿蒙版本app(点击我的-超级会员组件)显示与安卓不一致,且安卓版本在超级会员
102
- 上点击"会员中心"会跳到超级会员弹窗页,鸿蒙也没有这页。请修改鸿蒙源码将这些页面与安卓版本
103
- 完全对齐,不要mock数据,调用真实后台数据
104
- ```
105
-
106
- **例 3(多级路径 + 多个页面)**
107
- ```
108
- /hmos-ui-align 鸿蒙版本(点击底部高考按钮 -> 带年纪查专业 -> 点击某一具体专业(如临床医学)
109
- -> 点击就业分析,到达"专业就业健康度"展示页面)以及在"专业就业健康度"页面上点击"完整数
110
- 据指标"到达的就业健康度详情页都和安卓同一路径的不一致。比较这些页面的差别并将他们在视觉
111
- 效果上完全对齐,不要用mock数据,调用后台真实逻辑
112
- ```
113
-
114
- ---
115
-
116
- ## 运行过程中会发生什么
117
-
118
- skill 会严格按 `SKILL.md` 里的流水线跑:
119
-
120
- ### Step 0 · 解析入参
121
- 读取调用入参 `android_project_dir` / `harmony_project_dir` / `capture_output_dir`,并按统一链路「环境变量 → config.json → 询问」解析 `HOMETRANS_MODEL_*`(统一模型,回落 `~/.hometrans/config.json` 的 `autotest.unified_model`)与 `OHOS_SDK_PATH` / `HMS_SDK_PATH`(回落 config.json 的 `env.*`;**Step 0.0** 会先做存在性检查,两层都缺失才要求用户输入)。随后 **Step 0.1** 按两个工程目录自动解析两端的 App 显示名与包名(无需手动配置)。
122
-
123
- ### Step 1 · 双端页面采集
124
- 1.1 解析你的需求,拆成一组「基础页」,每个基础页有 `android_nav_path` 和 `hmos_nav_path`
125
- 1.2 对每个基础页,两端分别:`app_feature_verify.ts` 寻路 → 成功后 `page_capture.ts` 截图 + dump view tree
126
- 1.3 扫描基础页 view tree,发现 tab、弹窗、下拉等交互元素,自动对每个子状态再采集一遍
127
- 1.4 扫描安卓源码里的动画/Toast/语音传感器等 API,产出 `dynamic_ui_inventory.md`(截图和 view tree 抓不到这些效果)
128
- 1.5 识别多状态组件(如语音输入的 idle/recording/cancelling),读源码建 `state_model.md`,用 `page_capture_burst.ts` 连续采帧捕捉每个状态的代表帧
129
- 1.6 鸿蒙端页面不存在时,寻路会失败,对应目录留空(Step 2 会改从源码读)
130
-
131
- 产物目录结构:
132
- ```
133
- {capture_output_dir}/
134
- task_{timestamp}/
135
- android_page_1_{name}/
136
- screenshot.png
137
- view_tree.xml
138
- meta.json # 含 dpi / density_factor,供换算用
139
- hmos_page_1_{name}/
140
- screenshot.png
141
- view_tree.xml
142
- meta.json
143
- android_page_1_{name}_popup_filter/
144
- ...
145
- dynamic_ui_inventory.md # Step 1.4 产物
146
- state_model.md # Step 1.5 产物(仅多状态组件存在时)
147
- ```
148
-
149
- ### Step 2 · UI 差异分析
150
- 主 agent 串行执行,不会下放给子 agent(保证质量):
151
- - **2.1** 读安卓 screenshot + view tree,写 `android_page_*/UI_Analysis.md`(组件清单 + 位置 / 颜色 / icon / 尺寸 / 对齐方式等),并附加 `dynamic_ui_inventory.md` 里与该页相关的动态效果
152
- - **2.2** 读鸿蒙 screenshot + view tree,写 `hmos_page_*/UI_Analysis.md`;鸿蒙页不存在时改读源码写 `UI_Analysis_from_code.md`
153
- - **2.3** 两份 Analysis 对比,按 `references/Comparison_Template.md` 生成 `UI_comparison.md`(静态组件 diff 表 + 动态UI diff 表 + 状态机 diff 表)
154
- - **2.4** 验证所有 `UI_Analysis*.md` 和 `UI_comparison.md` 都已写全
155
-
156
- 所有尺寸都会以 `126px (3.0x → 42vp)` 的格式同时给出原始 px、**从各自 `meta.json` 读到的真实设备密度**、换算后的 vp(不再假设固定 3x)。
157
-
158
- ### Step 3 · 改鸿蒙源码
159
- - 跑 `node ./scripts/extract_checklist.ts --task-dir "{task_dir}"` 确定性地从所有 `UI_comparison.md` 抽取 diff 项,生成 `{task_dir}/fix_checklist.md`(唯一 source of truth),并核对脚本输出的 `items >= diff_rows` 对账报告,避免人工枚举漏项
160
- - 先探测工程状态管理范式(V1/V2,工程级判定一次):新工程/0→1 转换默认 V2,已有 V1 的工程沿用 V1
161
- - 按范式读对应文档:V1 读 `references/MVVM开发文档/`,V2 读 `references/MVVM开发文档V2/`(详见 `references/MVVM开发文档V2/_范式选择说明.md`)
162
- - 读 `page_align.md` 学习转换规则
163
- - 逐条修 diff,每修一个把 `- [ ]` 改成 `- [x]`;`[STATE]`/`[TRANSITION]`/`[ANIMATION]` 类条目需按 `state_model.md` 精确复制手势阈值和动画参数
164
- - 每个尺寸 / icon / alignment 都要回溯到安卓源码的 XML 或资源值,不允许"看起来差不多"
165
-
166
- ### Step 4 · 编译校验
167
- 若可用,调 `hmos-fix-build-errors` skill 确保工程能编过。**不会自动部署**。
168
-
169
- ---
170
-
171
- ## 目录结构
172
-
173
- ```
174
- skills/hmos-incremental-ui-align/
175
- ├── SKILL.md # 流水线定义(主 agent 执行逻辑)
176
- ├── README.md # 本文档
177
- ├── page_align.md # Step 3 的转换规则
178
- ├── diff_analysis.md # 内部说明
179
- ├── scripts/
180
- │ ├── app_feature_verify.ts # 寻路工具(TypeScript,Node 直跑)
181
- │ ├── page_capture.ts # view tree + 截图采集工具(TypeScript,Node 直跑,含密度换算)
182
- │ ├── page_capture_burst.ts # 连续帧采集工具(多状态组件用,TypeScript,Node 直跑)
183
- │ ├── extract_checklist.ts # 确定性 diff→checklist 抽取工具(TypeScript,Node 直跑)
184
- │ └── navigation-capure.md # 各脚本的调用约定
185
- └── references/
186
- ├── UI_Analysis_Template.md # Step 2 分析模板(含动态UI/多状态章节)
187
- ├── Comparison_Template.md # Step 2.3 对比模板(含动态UI/状态机对比章节)
188
- ├── State_Model_Template.md # Step 1.5 状态机建模模板
189
- ├── MVVM开发文档/ # 鸿蒙 MVVM 参考(V1 状态管理)
190
- ├── MVVM开发文档V2/ # 鸿蒙 MVVM 参考(V2 状态管理 + 范式选择说明)
191
- ├── android-to-harmonyOS-ui-layout-mapping-reference.md
192
- ├── android-to-harmonyOS-ui-atomic-component-mapping-reference.md
193
- └── android-to-harmonyOS-ui-interaction-mapping-reference.md
194
- ```
195
-
196
- ---
197
-
198
- ## 单独运行脚本(调试用)
199
-
200
- 跳过 skill 手动跑采集(直接 `node` 运行不经工具,模型 key 需来自 `~/.hometrans/config.json` 的 `autotest.unified_model` 或 `HOMETRANS_MODEL_*` 环境变量;插件场景应改用 `a2h_app_feature_verify` 工具,key 经 stdin 注入不泄露):
201
-
202
- ```powershell
203
- # 安卓寻路(TypeScript 版,Node ≥ 22.18/23.6 直跑,无需 uv / PYTHONIOENCODING)
204
- node Agents/hmos-ui-align/scripts/app_feature_verify.ts `
205
- --device adb `
206
- --app "Salt Player" `
207
- --package "com.salt.music" `
208
- --prompt "进入首页-点击AI智能填报-点击专业筛选按钮" `
209
- --max-steps 15
210
-
211
- # 安卓采集(TypeScript 版,Node ≥ 22.18/23.6 直跑,无需 uv / PYTHONIOENCODING)
212
- node Agents/hmos-ui-align/scripts/page_capture.ts --device adb -o ./tmp/android_page_1_xxx
213
-
214
- # 鸿蒙同理,把 --device adb 换成 --device hdc
215
- ```
216
-
217
- > 注意:`--prompt` 模式会自动在前面加「打开{app_name},」,所以 prompt 里不要再写"打开"。
218
-
219
- ---
220
-
221
- ## 常见问题
222
-
223
- **Q: 寻路失败怎么办?**
224
- A: skill 默认重试两次。流水线的内部规则:①先看页面是否存在;②路径错了就 force-stop 重试;③路径对但工具报错就让 agent 自己看截图判断是否到了。超过两次仍失败才会跳过该页。
225
-
226
- **Q: 鸿蒙端页面根本不存在,能新建吗?**
227
- A: 可以。Step 2.2 会改从鸿蒙源码读,Step 3 会按安卓的实现规格新建 `.ets` 文件到 `entry/src/main/ets/pages/`,并登记路由。
228
-
229
- **Q: 为什么同一次任务会采集多个页面?**
230
- A: Step 1.3 默认扫描交互元素(tab / 弹窗 / 展开收起等)并递归采集。想关掉请在需求里写「只对齐主页面」。
231
-
232
- **Q: 改完会自动部署吗?**
233
- A: 不会。Step 4 只跑编译验证(如果 `hmos-fix-build-errors` skill 可用)。部署自己来。
234
-
235
- **Q: 能不能只做差异分析、不改码?**
236
- A: 目前流水线是端到端的。如果只想要分析产物,跑完 Step 2 后手动中断即可,所有 `UI_Analysis.md` 和 `UI_comparison.md` 都会落盘在 `{capture_output_dir}/task_{timestamp}/` 下。
237
-
238
- **Q: 动画、Toast 这类瞬态效果能对齐吗?**
239
- A: 能。Step 1.4 会扫描安卓源码里的动画/Toast/语音传感器 API,产出 `dynamic_ui_inventory.md`;Step 2.3 会生成对应的「Dynamic / Transient UI Comparison」diff 表;Step 3 逐条修复时会写入真实的 ArkUI `animateTo()` / `promptAction.showToast()` 等实现。
240
-
241
- **Q: 像"按住说话"这种有多个状态的组件怎么对齐?**
242
- A: Step 1.5 会先建 `state_model.md`(状态枚举 + 转移条件 + 每状态属性,均需溯源到源码行号),再用 `page_capture_burst.ts` 连续采帧抓每个状态的代表帧。单击、松开长按等手势可自动触发;长按保持/拖拽/语音等手势需要人工在设备上操作,skill 会打印操作说明等你确认。
243
-
244
- ---
245
-
246
- ## 已知限制
247
-
248
- - 只对齐 UI,不对齐功能/数据流
249
- - 寻路依赖多模态模型对页面文本的识别,小字体或非标准控件可能识别不准
250
- - 双端设备的分辨率/密度差异会影响像素到 vp 的换算,流水线已从各自 `meta.json` 读取真实 `density_factor`,但同一个 Figma 规格下仍可能出现 ±1vp 偏差
251
- - 需要长按保持、拖拽、语音、传感器等物理输入触发的状态,无法全自动采集,需要用户配合手动操作
1
+ # hmos-ui-align
2
+
3
+ HarmonyOS-Android UI 自动对齐流水线。
4
+
5
+ ## 它解决什么问题
6
+
7
+ 以往对齐鸿蒙和安卓 UI 的流程需要:
8
+ 1. 人工把设备点到目标页面
9
+ 2. 手动跑 parse 脚本截图 + dump view tree
10
+ 3. 肉眼对比差异
11
+ 4. 手写改鸿蒙源码
12
+
13
+ 每多一个页面/弹窗/tab,上述步骤都要重复一遍,非常费时。
14
+
15
+ 本 skill 做了三件事:
16
+ - 自动**寻路**(视觉模型根据自然语言点到指定页面,模型复用 `~/.hometrans/config.json` 统一配置)
17
+ - 自动**采集** view tree + 截图(安卓走 adb、鸿蒙走 hdc)
18
+ - 自动**对比 + 改码**,并按 MVVM 模式把 mock 数据放到 Model 层
19
+
20
+ 用户只需要一句自然语言 + 点击路径。
21
+
22
+ > ⚠️ 当前只保证 **UI 对齐**,不保证功能行为对齐。
23
+
24
+ ---
25
+
26
+ ## 前置条件
27
+
28
+ 1. **设备连接**
29
+ - 安卓设备:已装目标 App,`adb devices` 能看到
30
+ - 鸿蒙设备:已装目标 App,`hdc list targets` 能看到
31
+ - 两台设备建议都保持亮屏解锁
32
+
33
+ 2. **运行时依赖**
34
+ - 采集脚本 `page_capture.js` 与寻路脚本 `app_feature_verify.js` 为零第三方依赖的 Node 脚本(`.js` 即源码、零编译、`node` 直跑;路径在不在 `node_modules` 下都能跑,无原生剥类型问题)。确认 `node --version`(≥ 22.18)即可,无需 Python / uv。
35
+
36
+ 3. **模型配置**(寻路导航用)
37
+ 与自测共用同一个多模态模型。插件场景下由 `a2h_run_model_script` 工具(`script="app_feature_verify"`,参数与 key 注入由工具处理:key host 解析、经 stdin 注入 `app_feature_verify.js`、不进环境变量、不泄露给 adb/hdc 子进程),无需单独配置。直接 `node` 运行(调试)时:`ht init` 把模型四要素写入 `~/.hometrans/config.json` 的 `autotest.unified_model`(并导出 `HOMETRANS_MODEL_*` 环境变量),脚本优先读 stdin → `--api-key` → 环境变量 → config.json;都没有时兼容老版本 `GLM_API_KEY`(走智谱 GLM 端点)。
38
+
39
+ 4. **鸿蒙 SDK 路径**(给改码阶段查 API 用)
40
+ 取自 OS 环境变量 `OHOS_SDK_PATH` / `HMS_SDK_PATH`(为空时由 `DEVECO_SDK_HOME` 派生),由 `ht init` 写入机器环境变量;例如 DevEco Studio 自带的 `E:\DevEco Studio\sdk\default\openharmony\ets`。
41
+
42
+ ---
43
+
44
+ ## 入参与配置
45
+
46
+ 本 skill 需要两类信息:
47
+
48
+ **① 调用时传入的入参**(每次按需提供):
49
+
50
+ | 入参 | 必填 | 说明 |
51
+ |---|---|--------------------------------------------------------------------|
52
+ | `android_project_dir` | 是 | 安卓源码根目录 |
53
+ | `harmony_project_dir` | 是 | 鸿蒙工程根目录(会被直接修改) |
54
+ | `capture_output_dir` | 否 | 采集产物输出目录(截图/view tree/分析报告)。默认 `<harmony_project_dir>/.hometrans/capture_output` |
55
+
56
+ **② 全局配置**(统一链路「环境变量 → `~/.hometrans/config.json` → 询问用户」,由 `ht init` 同时写入机器环境变量与 config.json,无需每次传):
57
+
58
+ - 模型配置 ← 插件场景由 `a2h_run_model_script` 工具(`script="app_feature_verify"`)经 stdin 注入(不经环境变量);直接 `node` 运行时读 `HOMETRANS_MODEL_*` 环境变量,缺失回落 `~/.hometrans/config.json` 的 `autotest.unified_model`(与自测共用同一个模型)
59
+ - 鸿蒙 SDK ETS API 目录 ← 环境变量 `OHOS_SDK_PATH` / `HMS_SDK_PATH`(为空时由 `DEVECO_SDK_HOME` 派生为 `<DEVECO_SDK_HOME>/default/openharmony/ets`、`.../hms/ets`)
60
+
61
+ > **Step 0.0** 会在执行前检查这些环境变量是否存在;缺失时会要求用户输入。
62
+
63
+ > 安卓/鸿蒙两端的 **App 显示名**(`android.app_name` / `harmony.app_name`)和 **包名**(`android.package` / `harmony.package`)也无需提供——skill 的 **Step 0.1** 会按 `android_project_dir` / `harmony_project_dir` 自动解析:
64
+ > - 安卓包名取自 app 模块 `build.gradle(.kts)` 的 `applicationId`(兜底 `AndroidManifest.xml` 的 `package`);App 名取自启动 Activity / `<application>` 的 `android:label`(`@string/xxx` 会去 `res/values*/strings.xml` 解析,优先中文)。
65
+ > - 鸿蒙包名取自 `AppScope/app.json5` 的 `bundleName`;App 名取自 `app.json5` 的 `label`(`$string:xxx` 会去 `AppScope/resources/**/element/string.json` 解析,优先中文)。
66
+ >
67
+ > 若某项无法从工程目录解析,skill 会回到询问用户。
68
+
69
+ ---
70
+
71
+ ## 使用方式
72
+
73
+ 在 Claude Code 里直接调用 skill,**把两个工程目录附在需求里**(可作为命名参数,也可在自然语言里说明):
74
+
75
+ ```
76
+ /hmos-ui-align android_project_dir: <安卓工程绝对路径> harmony_project_dir: <鸿蒙工程绝对路径>
77
+ <自然语言需求,描述要对齐的页面+点击路径>
78
+ ```
79
+
80
+ ### 写需求的三个要点
81
+
82
+ 1. **页面路径写清楚**
83
+ 用「→」或「-」标注一级一级的点击步骤,比如 `首页 → 点击"AI智能填报" → 点击"专业"筛选按钮`。路径越具体,寻路成功率越高。
84
+
85
+ 2. **说明是否要覆盖交互态**
86
+ 默认会自动扫描 tab、弹窗、下拉等交互元素并逐个采集。如果只想对齐主页面,请明确说「只对齐主页面,不管弹窗和 tab」。
87
+
88
+ 3. **说明是否用 Mock 数据**
89
+ 默认用 mock 数据。如果想调真实后台,在需求里写「不要 mock 数据,调用真实后台」。
90
+
91
+ ### 例子
92
+
93
+ **例 1(单个弹窗对齐)**
94
+ ```
95
+ /hmos-ui-align 鸿蒙版本app(登录状态下,进入首页-点击"AI智能填报"-点击"专业"筛选按钮)
96
+ 得到的弹窗样式和安卓同样路径得到的页面不一致,请修改鸿蒙源码将上述页面与安卓版本完全对齐
97
+ ```
98
+
99
+ **例 2(补齐缺失页面 + 调真实后台)**
100
+ ```
101
+ /hmos-ui-align 鸿蒙版本app(点击我的-超级会员组件)显示与安卓不一致,且安卓版本在超级会员
102
+ 上点击"会员中心"会跳到超级会员弹窗页,鸿蒙也没有这页。请修改鸿蒙源码将这些页面与安卓版本
103
+ 完全对齐,不要mock数据,调用真实后台数据
104
+ ```
105
+
106
+ **例 3(多级路径 + 多个页面)**
107
+ ```
108
+ /hmos-ui-align 鸿蒙版本(点击底部高考按钮 -> 带年纪查专业 -> 点击某一具体专业(如临床医学)
109
+ -> 点击就业分析,到达"专业就业健康度"展示页面)以及在"专业就业健康度"页面上点击"完整数
110
+ 据指标"到达的就业健康度详情页都和安卓同一路径的不一致。比较这些页面的差别并将他们在视觉
111
+ 效果上完全对齐,不要用mock数据,调用后台真实逻辑
112
+ ```
113
+
114
+ ---
115
+
116
+ ## 运行过程中会发生什么
117
+
118
+ skill 会严格按 `SKILL.md` 里的流水线跑:
119
+
120
+ ### Step 0 · 解析入参
121
+ 读取调用入参 `android_project_dir` / `harmony_project_dir` / `capture_output_dir`,并按统一链路「环境变量 → config.json → 询问」解析 `HOMETRANS_MODEL_*`(统一模型,回落 `~/.hometrans/config.json` 的 `autotest.unified_model`)与 `OHOS_SDK_PATH` / `HMS_SDK_PATH`(回落 config.json 的 `env.*`;**Step 0.0** 会先做存在性检查,两层都缺失才要求用户输入)。随后 **Step 0.1** 按两个工程目录自动解析两端的 App 显示名与包名(无需手动配置)。
122
+
123
+ ### Step 1 · 双端页面采集
124
+ 1.1 解析你的需求,拆成一组「基础页」,每个基础页有 `android_nav_path` 和 `hmos_nav_path`
125
+ 1.2 对每个基础页,两端分别:`app_feature_verify.js` 寻路 → 成功后 `page_capture.js` 截图 + dump view tree
126
+ 1.3 扫描基础页 view tree,发现 tab、弹窗、下拉等交互元素,自动对每个子状态再采集一遍
127
+ 1.4 扫描安卓源码里的动画/Toast/语音传感器等 API,产出 `dynamic_ui_inventory.md`(截图和 view tree 抓不到这些效果)
128
+ 1.5 识别多状态组件(如语音输入的 idle/recording/cancelling),读源码建 `state_model.md`,用 `page_capture_burst.js` 连续采帧捕捉每个状态的代表帧
129
+ 1.6 鸿蒙端页面不存在时,寻路会失败,对应目录留空(Step 2 会改从源码读)
130
+
131
+ 产物目录结构:
132
+ ```
133
+ {capture_output_dir}/
134
+ task_{timestamp}/
135
+ android_page_1_{name}/
136
+ screenshot.png
137
+ view_tree.xml
138
+ meta.json # 含 dpi / density_factor,供换算用
139
+ hmos_page_1_{name}/
140
+ screenshot.png
141
+ view_tree.xml
142
+ meta.json
143
+ android_page_1_{name}_popup_filter/
144
+ ...
145
+ dynamic_ui_inventory.md # Step 1.4 产物
146
+ state_model.md # Step 1.5 产物(仅多状态组件存在时)
147
+ ```
148
+
149
+ ### Step 2 · UI 差异分析
150
+ 主 agent 串行执行,不会下放给子 agent(保证质量):
151
+ - **2.1** 读安卓 screenshot + view tree,写 `android_page_*/UI_Analysis.md`(组件清单 + 位置 / 颜色 / icon / 尺寸 / 对齐方式等),并附加 `dynamic_ui_inventory.md` 里与该页相关的动态效果
152
+ - **2.2** 读鸿蒙 screenshot + view tree,写 `hmos_page_*/UI_Analysis.md`;鸿蒙页不存在时改读源码写 `UI_Analysis_from_code.md`
153
+ - **2.3** 两份 Analysis 对比,按 `references/Comparison_Template.md` 生成 `UI_comparison.md`(静态组件 diff 表 + 动态UI diff 表 + 状态机 diff 表)
154
+ - **2.4** 验证所有 `UI_Analysis*.md` 和 `UI_comparison.md` 都已写全
155
+
156
+ 所有尺寸都会以 `126px (3.0x → 42vp)` 的格式同时给出原始 px、**从各自 `meta.json` 读到的真实设备密度**、换算后的 vp(不再假设固定 3x)。
157
+
158
+ ### Step 3 · 改鸿蒙源码
159
+ - 跑 `node ./scripts/extract_checklist.js --task-dir "{task_dir}"` 确定性地从所有 `UI_comparison.md` 抽取 diff 项,生成 `{task_dir}/fix_checklist.md`(唯一 source of truth),并核对脚本输出的 `items >= diff_rows` 对账报告,避免人工枚举漏项
160
+ - 先探测工程状态管理范式(V1/V2,工程级判定一次):新工程/0→1 转换默认 V2,已有 V1 的工程沿用 V1
161
+ - 按范式读对应文档:V1 读 `references/MVVM开发文档/`,V2 读 `references/MVVM开发文档V2/`(详见 `references/MVVM开发文档V2/_范式选择说明.md`)
162
+ - 读 `page_align.md` 学习转换规则
163
+ - 逐条修 diff,每修一个把 `- [ ]` 改成 `- [x]`;`[STATE]`/`[TRANSITION]`/`[ANIMATION]` 类条目需按 `state_model.md` 精确复制手势阈值和动画参数
164
+ - 每个尺寸 / icon / alignment 都要回溯到安卓源码的 XML 或资源值,不允许"看起来差不多"
165
+
166
+ ### Step 4 · 编译校验
167
+ 若可用,调 `hmos-fix-build-errors` skill 确保工程能编过。**不会自动部署**。
168
+
169
+ ---
170
+
171
+ ## 目录结构
172
+
173
+ ```
174
+ skills/hmos-incremental-ui-align/
175
+ ├── SKILL.md # 流水线定义(主 agent 执行逻辑)
176
+ ├── README.md # 本文档
177
+ ├── page_align.md # Step 3 的转换规则
178
+ ├── diff_analysis.md # 内部说明
179
+ ├── scripts/
180
+ │ ├── app_feature_verify.js # 寻路工具(.js 源码,Node 直跑)
181
+ │ ├── page_capture.js # view tree + 截图采集工具(.js 源码,Node 直跑,含密度换算)
182
+ │ ├── page_capture_burst.js # 连续帧采集工具(多状态组件用,.js 源码,Node 直跑)
183
+ │ ├── extract_checklist.js # 确定性 diff→checklist 抽取工具(.js 源码,Node 直跑)
184
+ │ └── navigation-capure.md # 各脚本的调用约定
185
+ └── references/
186
+ ├── UI_Analysis_Template.md # Step 2 分析模板(含动态UI/多状态章节)
187
+ ├── Comparison_Template.md # Step 2.3 对比模板(含动态UI/状态机对比章节)
188
+ ├── State_Model_Template.md # Step 1.5 状态机建模模板
189
+ ├── MVVM开发文档/ # 鸿蒙 MVVM 参考(V1 状态管理)
190
+ ├── MVVM开发文档V2/ # 鸿蒙 MVVM 参考(V2 状态管理 + 范式选择说明)
191
+ ├── android-to-harmonyOS-ui-layout-mapping-reference.md
192
+ ├── android-to-harmonyOS-ui-atomic-component-mapping-reference.md
193
+ └── android-to-harmonyOS-ui-interaction-mapping-reference.md
194
+ ```
195
+
196
+ ---
197
+
198
+ ## 单独运行脚本(调试用)
199
+
200
+ 跳过 skill 手动跑采集(直接 `node` 运行不经工具,模型 key 需来自 `~/.hometrans/config.json` 的 `autotest.unified_model` 或 `HOMETRANS_MODEL_*` 环境变量;插件场景应改用 `a2h_run_model_script` 工具,`script="app_feature_verify"`,参数与 key 注入由工具处理):
201
+
202
+ ```powershell
203
+ # 安卓寻路(.js 源码,`node …/app_feature_verify.js` 直跑)
204
+ node skills/hmos-incremental-ui-align/scripts/app_feature_verify.js `
205
+ --device adb `
206
+ --app "Salt Player" `
207
+ --package "com.salt.music" `
208
+ --prompt "进入首页-点击AI智能填报-点击专业筛选按钮" `
209
+ --max-steps 15
210
+
211
+ # 安卓采集(.js 源码,`node …/page_capture.js` 直跑)
212
+ node skills/hmos-incremental-ui-align/scripts/page_capture.js --device adb -o ./tmp/android_page_1_xxx
213
+
214
+ # 鸿蒙同理,把 --device adb 换成 --device hdc
215
+ ```
216
+
217
+ > 注意:`--prompt` 模式会自动在前面加「打开{app_name},」,所以 prompt 里不要再写"打开"。
218
+
219
+ ---
220
+
221
+ ## 常见问题
222
+
223
+ **Q: 寻路失败怎么办?**
224
+ A: skill 默认重试两次。流水线的内部规则:①先看页面是否存在;②路径错了就 force-stop 重试;③路径对但工具报错就让 agent 自己看截图判断是否到了。超过两次仍失败才会跳过该页。
225
+
226
+ **Q: 鸿蒙端页面根本不存在,能新建吗?**
227
+ A: 可以。Step 2.2 会改从鸿蒙源码读,Step 3 会按安卓的实现规格新建 `.ets` 文件到 `entry/src/main/ets/pages/`,并登记路由。
228
+
229
+ **Q: 为什么同一次任务会采集多个页面?**
230
+ A: Step 1.3 默认扫描交互元素(tab / 弹窗 / 展开收起等)并递归采集。想关掉请在需求里写「只对齐主页面」。
231
+
232
+ **Q: 改完会自动部署吗?**
233
+ A: 不会。Step 4 只跑编译验证(如果 `hmos-fix-build-errors` skill 可用)。部署自己来。
234
+
235
+ **Q: 能不能只做差异分析、不改码?**
236
+ A: 目前流水线是端到端的。如果只想要分析产物,跑完 Step 2 后手动中断即可,所有 `UI_Analysis.md` 和 `UI_comparison.md` 都会落盘在 `{capture_output_dir}/task_{timestamp}/` 下。
237
+
238
+ **Q: 动画、Toast 这类瞬态效果能对齐吗?**
239
+ A: 能。Step 1.4 会扫描安卓源码里的动画/Toast/语音传感器 API,产出 `dynamic_ui_inventory.md`;Step 2.3 会生成对应的「Dynamic / Transient UI Comparison」diff 表;Step 3 逐条修复时会写入真实的 ArkUI `animateTo()` / `promptAction.showToast()` 等实现。
240
+
241
+ **Q: 像"按住说话"这种有多个状态的组件怎么对齐?**
242
+ A: Step 1.5 会先建 `state_model.md`(状态枚举 + 转移条件 + 每状态属性,均需溯源到源码行号),再用 `page_capture_burst.js` 连续采帧抓每个状态的代表帧。单击、松开长按等手势可自动触发;长按保持/拖拽/语音等手势需要人工在设备上操作,skill 会打印操作说明等你确认。
243
+
244
+ ---
245
+
246
+ ## 已知限制
247
+
248
+ - 只对齐 UI,不对齐功能/数据流
249
+ - 寻路依赖多模态模型对页面文本的识别,小字体或非标准控件可能识别不准
250
+ - 双端设备的分辨率/密度差异会影响像素到 vp 的换算,流水线已从各自 `meta.json` 读取真实 `density_factor`,但同一个 Figma 规格下仍可能出现 ±1vp 偏差
251
+ - 需要长按保持、拖拽、语音、传感器等物理输入触发的状态,无法全自动采集,需要用户配合手动操作