@buaa_smat/hometrans 0.1.16 → 0.1.17

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.
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # 安卓应用迁移鸿蒙指导
2
2
 
3
+ **HomeTrans工具用于辅助鸿蒙开发者完成原生安卓应用迁移到鸿蒙应用,为应用迁移的各个环节提供能力支持。**
4
+
5
+ **本文档为使用HomeTrans工具进行应用迁移的流程指导。**
3
6
 
4
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
8
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D%2022-339933.svg?logo=node.js&logoColor=white)](https://nodejs.org/en/download)
@@ -11,8 +14,6 @@
11
14
  [![Codex](https://img.shields.io/badge/Codex-supported-blueviolet.svg)](#选择本地editor)
12
15
  [![CodeBuddy](https://img.shields.io/badge/CodeBuddy-supported-blueviolet.svg)](#选择本地editor)
13
16
 
14
- 本文档为完整的Android到HarmonyOS迁移流程指导。
15
-
16
17
  ---
17
18
 
18
19
  ## 安装DevEco Studio以及Android Studio
@@ -95,12 +96,19 @@
95
96
 
96
97
  ## 步骤1:UI迁移
97
98
 
98
- ### 1.1UI迁移(`hmos-batch-ui-align`)
99
+ ### 1.1 UI迁移
99
100
 
100
101
  > 📦 依赖:uv(必需,页面快照解析脚本经 `uv run` 运行,uv 自带 Python);Android Studio + 安卓真机/模拟器(可选,仅自动抓取页面快照时需要)。
101
102
 
103
+ 打开本地editor(claude code,open code等),在会话中通过 `斜杠/ `或者 `自然语言 `的方式调用skill--hmos-batch-ui-align,并且传递相关参数)
104
+
105
+ 斜杠调用方式举例
106
+ ```
107
+ /hmos-batch-ui-align android_project_dir=<安卓项目路径> harmony_project_dir=<鸿蒙项目路径> ui_info_root=<页面快照目录> apk_path=<安卓apk路径>
108
+ ```
109
+ 自然语言调用方式举例
102
110
  ```
103
- /hmos-batch-ui-align android_project_dir=<安卓项目路径> harmony_project_dir=<鸿蒙项目路径> ui_info_root=<页面快照目录>
111
+ 帮我使用skill:hmos-batch-ui-align完成安卓应用页面到鸿蒙应用的迁移,传递的参数是 android_project_dir=<安卓项目路径> harmony_project_dir=<鸿蒙项目路径> ui_info_root=<页面快照目录> apk_path=<安卓apk路径>
104
112
  ```
105
113
 
106
114
  **输入参数**
@@ -111,6 +119,7 @@
111
119
  | `harmony_project_dir` | **必选** | HarmonyOS项目输出路径 |
112
120
  | `ui_info_root` | 可选 | 包含 `page_NNNN_ActivityName` 格式子目录的父目录(每子目录含 `meta.json` + `view.xml` + 可选 `screenshot.png`);不提供时自动通过adb抓取 |
113
121
  | `pages` | 可选 | 显式列出的页面子集(不提供则处理所有页面) |
122
+ | `apk_path` | 可选 | Android APK文件路径;透传给内部的 `hmos-resources-convert`,提供后跳过Gradle构建,直接用apktool解包该APK提取资源 |
114
123
 
115
124
  **输出产物**
116
125
 
@@ -120,13 +129,20 @@
120
129
  | 资源文件 | HarmonyOS项目下 | 页面所需资源(含UI迁移内部触发的资源转换产物) |
121
130
  | 批量转换报告 | HarmonyOS项目下 | 各页面转换结果、缺陷与统计 |
122
131
 
123
- ### 1.2UI对齐(`hmos-incremental-ui-align`,按需)
132
+ ### 1.2 UI对齐(按需)
124
133
 
125
134
  > 📦 依赖:DevEco Studio、Android Studio、uv(必需,双端页面采集脚本经 `uv run` 运行,uv 自带 Python);安卓真机/模拟器 + 鸿蒙真机/模拟器(必需)。
126
135
 
136
+ 打开本地editor(claude code,open code等),在会话中通过 `斜杠/ `或者 `自然语言 `的方式调用skill--hmos-incremental-ui-align,并且传递相关参数)
137
+
138
+ 斜杠调用方式举例
127
139
  ```
128
140
  /hmos-incremental-ui-align android_project_dir=<安卓工程路径> harmony_project_dir=<鸿蒙工程路径>
129
141
  ```
142
+ 自然语言调用方式举例
143
+ ```
144
+ 帮我使用skill:hmos-incremental-ui-align完成安卓和鸿蒙应用的页面对齐,传递的参数是 android_project_dir=<安卓工程路径> harmony_project_dir=<鸿蒙工程路径>
145
+ ```
130
146
 
131
147
  **输入参数**
132
148
 
@@ -146,7 +162,7 @@
146
162
 
147
163
  ---
148
164
 
149
- ## 步骤2:生成需求规格(`hmos-spec-generate`)
165
+ ## 步骤2:生成需求规格
150
166
 
151
167
  > 📦 依赖:homegraph(必需)。
152
168
 
@@ -179,9 +195,16 @@
179
195
  8,在播放界面隐藏:开关 默认关闭 打开后如果切换到播放页,则不在悬浮窗区域显示歌词
180
196
  ```
181
197
 
198
+ 打开本地editor(claude code,open code等),在会话中通过 `斜杠/ `或者 `自然语言 `的方式调用skill--hmos-spec-generate,并且传递相关参数)
199
+
200
+ 斜杠调用方式举例
182
201
  ```
183
202
  /hmos-spec-generate requirement_description_file=<需求描述文件路径> android_project_dir=<安卓项目路径> spec_output_dir=<规格输出目录>
184
203
  ```
204
+ 自然语言调用方式举例
205
+ ```
206
+ 帮我使用skill:hmos-spec-generate生成需求spec文档,传递的参数是 requirement_description_file=<需求描述文件路径> android_project_dir=<安卓项目路径> spec_output_dir=<规格输出目录>
207
+ ```
185
208
 
186
209
  **输入参数**
187
210
 
@@ -200,7 +223,7 @@
200
223
 
201
224
  ---
202
225
 
203
- ## 步骤3:逻辑代码转换流水线(`hmos-convert-pipeline`)
226
+ ## 步骤3:逻辑代码转换流水线
204
227
 
205
228
  > 📦 依赖:DevEco Studio(必需,构建/评审修复);uv + 鸿蒙真机(集成测试阶段需要,可经 `skip_test` 跳过;uv 经 `uv run` 运行 `self_test_runner.py` 并自带 Python)。
206
229
 
@@ -208,9 +231,16 @@
208
231
  > 1. 如果执行集成测试,需要**编写测试用例文档**输出前置测试用例(非必须,用于测试用例的环境准备等)通过pre_test_case_path参数传递,测试用例通过test_case_path参数传递。
209
232
  > 2. 如果执行集成测试,需要**连接HarmonyOS真机**并且在DevEco Studio中[**配置签名**](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/networkboost-preparations#%E9%85%8D%E7%BD%AE%E7%AD%BE%E5%90%8D)。
210
233
 
234
+ 打开本地editor(claude code,open code等),在会话中通过 `斜杠/ `或者 `自然语言 `的方式调用skill--hmos-convert-pipeline,并且传递相关参数)
235
+
236
+ 斜杠调用方式举例
211
237
  ```
212
238
  /hmos-convert-pipeline android_project_dir=<安卓项目路径> harmony_project_dir=<鸿蒙项目路径> spec_file_path=<需求规格文档路径>
213
239
  ```
240
+ 自然语言调用方式举例
241
+ ```
242
+ 帮我使用skill:hmos-convert-pipeline完成逻辑代码开发,代码检视,集成测试,传递的参数是 android_project_dir=<安卓项目路径> harmony_project_dir=<鸿蒙项目路径> spec_file_path=<需求规格文档路径>
243
+ ```
214
244
 
215
245
  **输入参数**
216
246
 
@@ -240,7 +270,7 @@
240
270
 
241
271
  ---
242
272
 
243
- ## 步骤4:人工验收(👤 用户介入)
273
+ ## 步骤4:人工验收
244
274
 
245
275
  **输入**
246
276
 
@@ -264,15 +294,22 @@
264
294
 
265
295
  以下能力包含在迁移流程中也可按需独立调用。
266
296
 
267
- ### 资源转换(`hmos-resources-convert`)
297
+ ### 资源转换
268
298
 
269
299
  > 📦 依赖:DevEco Studio中的java(可选,缺失时回退读取源码 `res/`)。
270
300
 
271
301
  > ⚠️ 提示:如果步骤1采用 **UI迁移**(`hmos-batch-ui-align`),则跳过当前能力(UI迁移内部已包含资源转换)。
272
302
 
303
+ 打开本地editor(claude code,open code等),在会话中通过 `斜杠/ `或者 `自然语言 `的方式调用skill--hmos-resources-convert,并且传递相关参数)
304
+
305
+ 斜杠调用方式举例
273
306
  ```
274
307
  /hmos-resources-convert android_project_dir=<安卓项目路径> harmony_project_dir=<鸿蒙项目路径> resource_mapping_path=<资源映射文档路径>
275
308
  ```
309
+ 自然语言调用方式举例
310
+ ```
311
+ 帮我使用skill:hmos-resources-convert完成安卓应用到鸿蒙应用的资源转换,传递的参数是 android_project_dir=<安卓项目路径> harmony_project_dir=<鸿蒙项目路径> resource_mapping_path=<资源映射文档路径>
312
+ ```
276
313
 
277
314
  **输入参数**
278
315
 
@@ -281,7 +318,7 @@
281
318
  | `android_project_dir` | **必选** | Android项目根目录路径(含 `build.gradle` 或 `build.gradle.kts`) |
282
319
  | `harmony_project_dir` | **必选** | HarmonyOS项目输出路径 |
283
320
  | `resource_mapping_path` | **必选** | Android ↔ HarmonyOS资源映射文档的完整输出路径(`.md`) |
284
- | `apk_path` | 可选 | Android APK文件路径;提供后跳过Gradle构建和apktool解包,直接从APK提取资源 |
321
+ | `apk_path` | 可选 | Android APK文件路径;提供后跳过Gradle构建,直接用apktool解包该APK提取资源 |
285
322
 
286
323
  **输出产物**
287
324
 
@@ -291,13 +328,20 @@
291
328
  | 资源映射文档 | `resource_mapping_path` | Android ↔ HarmonyOS资源条目映射(`.md`) |
292
329
  | 转换报告 | HarmonyOS项目下 | 资源转换过程与统计 |
293
330
 
294
- ### 集成测试(`hmos-integration-test`,已包含在步骤3的流水线,也可以单独运行)
331
+ ### 集成测试(已包含在步骤3的流水线,也可以单独运行)
295
332
 
296
333
  > 📦 依赖:DevEco Studio(必需,真机调试);uv(必需,AutoTest在uv下运行);鸿蒙真机(必需)。
297
334
 
335
+ 打开本地editor(claude code,open code等),在会话中通过 `斜杠/ `或者 `自然语言 `的方式调用skill--hmos-integration-test,并且传递相关参数)
336
+
337
+ 斜杠调用方式举例
298
338
  ```
299
339
  /hmos-integration-test test_case_path=<测试用例路径> hap_path=<签名HAP路径>
300
340
  ```
341
+ 自然语言调用方式举例
342
+ ```
343
+ 帮我使用skill:hmos-integration-test基于测试用例完成鸿蒙应用的集成测试,传递的参数是 test_case_path=<测试用例路径> hap_path=<签名HAP路径>
344
+ ```
301
345
 
302
346
  **输入参数**
303
347
 
package/dist/cli/init.js CHANGED
@@ -980,6 +980,61 @@ async function installUv() {
980
980
  console.log(chalk.gray(' Open a new terminal (PATH refresh), then verify: uv --version'));
981
981
  }
982
982
  }
983
+ /**
984
+ * Editor name (as in config.json / defaultEditors) → homegraph `install --target`
985
+ * slug. homegraph ships its own MCP-into-editor installer; after the homegraph CLI
986
+ * is installed globally we invoke it so the homegraph MCP server is registered into
987
+ * the same editors HomeTrans is configuring. Scope intentionally matches init's
988
+ * supported editors — editors without a mapping (a user-added custom editor, or one
989
+ * homegraph doesn't support) are simply skipped.
990
+ */
991
+ const HOMEGRAPH_TARGET_BY_EDITOR = {
992
+ 'Claude Code': 'claude',
993
+ Cursor: 'cursor',
994
+ Codex: 'codex',
995
+ OpenCode: 'opencode',
996
+ 'DevEco Code': 'deveco',
997
+ CodeBuddy: 'codebuddy',
998
+ };
999
+ /**
1000
+ * Register the homegraph MCP server into the selected editors via the homegraph
1001
+ * CLI (`homegraph install --target=<...> --location=global --yes`). Runs after
1002
+ * installNodeDependencies() so the homegraph binary is present. A single invocation
1003
+ * with a comma-joined target list configures every selected editor at once. Never
1004
+ * throws — failures are reported and `ht init` continues. On Windows the global npm
1005
+ * bin is a `.cmd` shim, so the command runs through the shell there (same as
1006
+ * homegraphVersion()).
1007
+ */
1008
+ async function installHomegraphToEditors(selectedEditors) {
1009
+ const targets = selectedEditors
1010
+ .map((name) => HOMEGRAPH_TARGET_BY_EDITOR[name])
1011
+ .filter((t) => Boolean(t));
1012
+ if (targets.length === 0)
1013
+ return;
1014
+ const targetArg = `--target=${targets.join(',')}`;
1015
+ console.log(chalk.gray(' homegraph MCP -> editors'));
1016
+ // Needs the homegraph CLI on PATH. A fresh `npm install -g` may not be visible
1017
+ // until a PATH refresh, but homegraphVersion() probes through the shell so the
1018
+ // shim is usually resolvable within the same `ht init` run.
1019
+ if (homegraphVersion() === null) {
1020
+ console.log(chalk.yellow(' ! homegraph not available — skipping MCP registration into editors.'));
1021
+ console.log(chalk.gray(` After homegraph is installed, run: homegraph install ${targetArg} --location=global --yes`));
1022
+ return;
1023
+ }
1024
+ console.log(chalk.gray(` Running: homegraph install ${targetArg} --location=global --yes ...`));
1025
+ try {
1026
+ execFileSync('homegraph', ['install', targetArg, '--location=global', '--yes'], {
1027
+ stdio: 'inherit',
1028
+ timeout: 5 * 60 * 1000,
1029
+ shell: process.platform === 'win32',
1030
+ });
1031
+ console.log(chalk.green(` + homegraph MCP registered into: ${targets.join(', ')}`));
1032
+ }
1033
+ catch (err) {
1034
+ console.log(chalk.red(` ! homegraph install failed: ${err.message}`));
1035
+ console.log(chalk.gray(` Re-run manually: homegraph install ${targetArg} --location=global --yes`));
1036
+ }
1037
+ }
983
1038
  export async function initCommand(options = {}) {
984
1039
  ensureChalkColor();
985
1040
  const banner = figlet.textSync('HomeTrans', { font: 'Standard' });
@@ -1129,6 +1184,15 @@ export async function initCommand(options = {}) {
1129
1184
  abortInit();
1130
1185
  console.log(chalk.yellow(` ! homegraph / arkanalyzer install skipped: ${err.message}`));
1131
1186
  }
1187
+ // With homegraph installed, register its MCP server into the editors the user
1188
+ // selected, using homegraph's own installer. Scoped to the selected editors so
1189
+ // it stays consistent with what HomeTrans configures below.
1190
+ try {
1191
+ await installHomegraphToEditors(selectedEditors);
1192
+ }
1193
+ catch (err) {
1194
+ console.log(chalk.yellow(` ! homegraph editor registration skipped: ${err.message}`));
1195
+ }
1132
1196
  try {
1133
1197
  await installUv();
1134
1198
  }
@@ -22,6 +22,12 @@ import { removeEnvVars } from './env-vars.js';
22
22
  const execFileAsync = promisify(execFile);
23
23
  const __filename = fileURLToPath(import.meta.url);
24
24
  const __dirname = path.dirname(__filename);
25
+ /**
26
+ * Global Node CLIs that `ht init` installs as a pair (`npm install -g homegraph
27
+ * arkanalyzer`). `ht uninstall` removes the same pair so the two commands stay
28
+ * symmetric.
29
+ */
30
+ const NODE_DEPS = ['homegraph', 'arkanalyzer'];
25
31
  /**
26
32
  * Machine env var names that `ht init` may have set and that have a non-empty
27
33
  * value in config.env: the SDK paths / tool dir plus the mirrored unified model
@@ -72,6 +78,57 @@ async function listBundledAgentEntries(agentsRoot) {
72
78
  }
73
79
  }
74
80
  /* ------------------------------------------------------------------ */
81
+ /* Global npm dependency removal */
82
+ /* ------------------------------------------------------------------ */
83
+ /**
84
+ * Is `npm` runnable? On Windows the global npm bin is a `.cmd` shim, so the
85
+ * command must run through the shell there (same as init's homegraphVersion()).
86
+ */
87
+ async function npmAvailable() {
88
+ try {
89
+ await execFileAsync('npm', ['--version'], {
90
+ timeout: 15000,
91
+ shell: process.platform === 'win32',
92
+ });
93
+ return true;
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ }
99
+ /**
100
+ * Does `homegraph --version` return normally (exit 0)? Used as the presence
101
+ * probe for the homegraph/arkanalyzer pair — the symmetric counterpart to
102
+ * init's idempotency check, which skips the install when homegraph is already
103
+ * present.
104
+ */
105
+ async function homegraphInstalled() {
106
+ try {
107
+ await execFileAsync('homegraph', ['--version'], {
108
+ timeout: 15000,
109
+ shell: process.platform === 'win32',
110
+ });
111
+ return true;
112
+ }
113
+ catch {
114
+ return false;
115
+ }
116
+ }
117
+ /**
118
+ * Decide which global npm packages to remove. Returns the homegraph/arkanalyzer
119
+ * pair when homegraph is detected on the machine (mirroring init, which installs
120
+ * them together), or an empty list when npm is unavailable or homegraph is not
121
+ * present — so a machine that never ran `ht init`'s dependency install shows
122
+ * nothing to remove here.
123
+ */
124
+ async function detectNodeDepsToRemove() {
125
+ if (!(await npmAvailable()))
126
+ return [];
127
+ if (!(await homegraphInstalled()))
128
+ return [];
129
+ return [...NODE_DEPS];
130
+ }
131
+ /* ------------------------------------------------------------------ */
75
132
  /* MCP config removal helpers */
76
133
  /* ------------------------------------------------------------------ */
77
134
  function detectIndentation(raw) {
@@ -303,6 +360,7 @@ function renderDiff(oldContent, newContent, context = 3) {
303
360
  function isPlanEmpty(plan) {
304
361
  return (plan.deletions.length === 0 &&
305
362
  plan.modifications.length === 0 &&
363
+ plan.npmDeps.length === 0 &&
306
364
  plan.envVars.length === 0 &&
307
365
  plan.configDir === null);
308
366
  }
@@ -329,6 +387,14 @@ function renderPlan(plan) {
329
387
  console.log('');
330
388
  }
331
389
  }
390
+ if (plan.npmDeps.length > 0) {
391
+ console.log(chalk.red.bold(` Global npm packages to remove (${plan.npmDeps.length}):`));
392
+ console.log(chalk.gray(` (will run: npm uninstall -g ${plan.npmDeps.join(' ')})`));
393
+ for (const dep of plan.npmDeps) {
394
+ console.log(` ${chalk.red('-')} ${dep}`);
395
+ }
396
+ console.log('');
397
+ }
332
398
  if (plan.envVars.length > 0) {
333
399
  console.log(chalk.red.bold(` Environment variables to remove (${plan.envVars.length}):`));
334
400
  console.log(chalk.gray(process.platform === 'win32'
@@ -351,6 +417,7 @@ function renderPlan(plan) {
351
417
  async function executePlan(plan) {
352
418
  let deleted = 0;
353
419
  let modified = 0;
420
+ let npmNote = null;
354
421
  let envNote = null;
355
422
  for (const mod of plan.modifications) {
356
423
  await fs.writeFile(mod.absPath, mod.newContent, 'utf-8');
@@ -366,6 +433,22 @@ async function executePlan(plan) {
366
433
  // codex CLI unavailable or failed — TOML path already handled above
367
434
  }
368
435
  }
436
+ // Remove the global npm CLIs installed by `ht init` (homegraph + arkanalyzer).
437
+ // Best-effort: a failure is reported in the note but never aborts the rest of
438
+ // the uninstall.
439
+ if (plan.npmDeps.length > 0) {
440
+ console.log(chalk.gray(` Running: npm uninstall -g ${plan.npmDeps.join(' ')} ...`));
441
+ try {
442
+ await execFileAsync('npm', ['uninstall', '-g', ...plan.npmDeps], {
443
+ timeout: 5 * 60 * 1000,
444
+ shell: process.platform === 'win32',
445
+ });
446
+ npmNote = `removed ${plan.npmDeps.join(', ')}`;
447
+ }
448
+ catch (err) {
449
+ npmNote = `failed (${err.message}) — remove manually: npm uninstall -g ${plan.npmDeps.join(' ')}`;
450
+ }
451
+ }
369
452
  for (const del of plan.deletions) {
370
453
  try {
371
454
  await fs.rm(del.absPath, { recursive: true, force: true });
@@ -388,7 +471,7 @@ async function executePlan(plan) {
388
471
  // best-effort
389
472
  }
390
473
  }
391
- return { deleted, modified, envNote };
474
+ return { deleted, modified, npmNote, envNote };
392
475
  }
393
476
  /* ------------------------------------------------------------------ */
394
477
  /* Entry point */
@@ -408,12 +491,16 @@ export async function uninstallCommand() {
408
491
  deletions: [],
409
492
  modifications: [],
410
493
  codexRemove: false,
494
+ npmDeps: [],
411
495
  envVars: [],
412
496
  configDir: null,
413
497
  };
414
498
  for (const editor of editors) {
415
499
  await buildPlanForEditor(editor, skillNames, agentEntries, plan);
416
500
  }
501
+ // Global npm CLIs installed by `ht init` (homegraph + arkanalyzer) — detected
502
+ // the same way init decides whether to (re)install them.
503
+ plan.npmDeps = await detectNodeDepsToRemove();
417
504
  // Env vars to remove come from config.json (`env` + the unified model
418
505
  // fields); the ~/.hometrans dir (which holds config.json) is deleted last.
419
506
  plan.envVars = plannedEnvVarNames(config);
@@ -451,6 +538,9 @@ export async function uninstallCommand() {
451
538
  const summary = await executePlan(plan);
452
539
  console.log('');
453
540
  console.log(chalk.green(` Uninstalled: ${summary.deleted} entries deleted, ${summary.modified} files modified.`));
541
+ if (summary.npmNote) {
542
+ console.log(chalk.green(` Global npm packages: ${summary.npmNote}`));
543
+ }
454
544
  if (summary.envNote) {
455
545
  console.log(chalk.green(` Environment variables: ${summary.envNote}`));
456
546
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buaa_smat/hometrans",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "HomeTrans (Android-to-HarmonyOS) skill + agent installer. Run `ht init` to distribute conversion skills and subagents into AI editors.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,6 +27,7 @@ Extract 4 paths from the user's message (natural language, no fixed format):
27
27
  | `harmony_project_dir` | HarmonyOS target project root | "target Harmony project", "鸿蒙项目", "output project" |
28
28
  | `ui_info_root` | **Parent directory containing all `page_NNNN_*` subdirectories** | "page screenshots and view tree", "page snapshots", "page folder" |
29
29
  | `pages` (optional) | User-explicitly listed page subset | When user lists `1. .../page_0001_X` ... take these |
30
+ | `apk_path` (optional) | Full path of the Android APK corresponding to `android_project_dir`. Passed through to `hmos-resources-convert` in Step 2 to skip the Gradle build and decompile this APK directly with apktool to extract resources | "APK path", "apk文件", "安卓apk" |
30
31
 
31
32
  **`references_dir` and MVVM document directories always use skill-bundled relative paths** — **do not** accept user-provided overrides:
32
33
 
@@ -41,6 +42,7 @@ Invoke the `hmos-resources-convert` skill with the following parameters:
41
42
  1. **Android project path** = `android_project_dir`
42
43
  2. **HarmonyOS project output path** = `harmony_project_dir`
43
44
  3. **resource_mapping_path** = generate the resource mapping document under `harmony_project_dir` at `${harmony_project_dir}/resource_mapping.md`
45
+ 4. **Android apk path** = `apk_path` — **only if the user provided it**. When passed through, `hmos-resources-convert` skips the Gradle build and decompiles this APK directly with apktool to extract resources. Omit this parameter entirely when `apk_path` was not provided.
44
46
 
45
47
  This skill batch-converts Android resources (strings, colors, drawables, images, etc.) to HarmonyOS resource format and generates the mapping document.
46
48
 
@@ -1,85 +0,0 @@
1
- @echo off
2
- setlocal
3
- set BASENAME=apktool_
4
- chcp 65001 2>nul >nul
5
-
6
- set java_exe=java.exe
7
-
8
- if defined JAVA_HOME (
9
- set "java_exe=%JAVA_HOME%\bin\java.exe"
10
- )
11
-
12
- rem Find the highest version .jar available in the same directory as the script
13
- setlocal EnableDelayedExpansion
14
- pushd "%~dp0"
15
- if exist apktool.jar (
16
- set BASENAME=apktool
17
- goto skipversioned
18
- )
19
-
20
- set BASENAME=apktool
21
- set max_major=0
22
- set max_minor=0
23
- set max_patch=0
24
-
25
- rem Loop through all versioned .jar files matching the basename
26
- for %%F in (%BASENAME%*.jar) do (
27
- set "filename=%%~nF"
28
-
29
- rem Extract version part (apktool-X.Y.Z)
30
- for /f "tokens=2 delims=_-" %%A in ("!filename!") do (
31
- for /f "tokens=1,2,3 delims=." %%B in ("%%A") do (
32
- set "major=%%B"
33
- set "minor=%%C"
34
- set "patch=%%D"
35
-
36
- rem Set Default minor/patch to 0
37
- if "!minor!"=="" set "minor=0"
38
- if "!patch!"=="" set "patch=0"
39
-
40
- rem Compare major version
41
- if !major! gtr !max_major! (
42
- set "max_major=!major!"
43
- set "max_minor=!minor!"
44
- set "max_patch=!patch!"
45
- ) else if !major! == !max_major! (
46
- rem Compare minor version
47
- if !minor! gtr !max_minor! (
48
- set "max_minor=!minor!"
49
- set "max_patch=!patch!"
50
- ) else if !minor! == !max_minor! (
51
- rem Compare patch version
52
- if !patch! gtr !max_patch! (
53
- set "max_patch=!patch!"
54
- )
55
- )
56
- )
57
- )
58
- )
59
- )
60
-
61
- rem Construct full version string
62
- set "max=_!max_major!.!max_minor!.!max_patch!"
63
-
64
- :skipversioned
65
- popd
66
- setlocal DisableDelayedExpansion
67
-
68
- rem Find out if the commandline is a parameterless .jar or directory, for fast unpack/repack
69
- if "%~1"=="" goto load
70
- if not "%~2"=="" goto load
71
- set ATTR=%~a1
72
- if "%ATTR:~0,1%"=="d" (
73
- rem Directory, rebuild
74
- set fastCommand=b
75
- )
76
- if "%ATTR:~0,1%"=="-" if "%~x1"==".apk" (
77
- rem APK file, unpack
78
- set fastCommand=d
79
- )
80
-
81
- :load
82
- "%java_exe%" -jar -Xmx1024M -Duser.language=en -Dfile.encoding=UTF8 -Djdk.util.zip.disableZip64ExtraFieldValidation=true -Djdk.nio.zipfs.allowDotZipEntry=true "%~dp0%BASENAME%%max%.jar" %fastCommand% %*
83
-
84
- rem Pause when ran non interactively
85
- for %%i in (%cmdcmdline%) do if /i "%%~i"=="/c" pause & exit /b