@buaa_smat/hometrans 0.1.9 → 0.1.11
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 +229 -213
- package/agents/build-fixer.md +17 -10
- package/agents/code-reviewer.md +4 -2
- package/dist/cli/config-store.js +81 -1
- package/dist/cli/config.js +9 -10
- package/dist/cli/index.js +24 -0
- package/dist/cli/init.js +343 -22
- package/dist/cli/mcp-setup.js +2 -6
- package/dist/cli/uninstall.js +20 -9
- package/dist/context/index.js +72 -0
- package/package.json +1 -1
- package/resource/choose_editor.png +0 -0
- package/resource/hometrans_config.png +0 -0
- package/skills/hmos-convert-pipeline/SKILL.md +19 -11
- package/skills/hmos-fix-build-errors/SKILL.md +13 -5
- package/skills/hmos-incremental-ui-align/SKILL.md +15 -1
- package/tools/test-tools/autotest/uv.lock +3156 -3156
- package/resource/finish_init.png +0 -0
package/dist/cli/uninstall.js
CHANGED
|
@@ -17,7 +17,7 @@ import chalk from 'chalk';
|
|
|
17
17
|
import inquirer from 'inquirer';
|
|
18
18
|
import { modify, applyEdits } from 'jsonc-parser';
|
|
19
19
|
import { expandHome, loadHomeTransConfig, } from './config-store.js';
|
|
20
|
-
import { dirExists, prettyHome } from './init.js';
|
|
20
|
+
import { dirExists, isPromptAbort, prettyHome } from './init.js';
|
|
21
21
|
const execFileAsync = promisify(execFile);
|
|
22
22
|
const __filename = fileURLToPath(import.meta.url);
|
|
23
23
|
const __dirname = path.dirname(__filename);
|
|
@@ -291,14 +291,25 @@ export async function uninstallCommand() {
|
|
|
291
291
|
if (plan.deletions.length === 0 && plan.modifications.length === 0) {
|
|
292
292
|
return;
|
|
293
293
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
294
|
+
let proceed;
|
|
295
|
+
try {
|
|
296
|
+
({ proceed } = await inquirer.prompt([
|
|
297
|
+
{
|
|
298
|
+
type: 'confirm',
|
|
299
|
+
name: 'proceed',
|
|
300
|
+
message: 'Continue?',
|
|
301
|
+
default: false,
|
|
302
|
+
},
|
|
303
|
+
]));
|
|
304
|
+
}
|
|
305
|
+
catch (err) {
|
|
306
|
+
// Ctrl-C / Esc at the confirm prompt — cancel cleanly, no stack trace.
|
|
307
|
+
if (isPromptAbort(err)) {
|
|
308
|
+
console.log(chalk.yellow('\n Uninstall cancelled. No files modified.\n'));
|
|
309
|
+
process.exit(130);
|
|
310
|
+
}
|
|
311
|
+
throw err;
|
|
312
|
+
}
|
|
302
313
|
if (!proceed) {
|
|
303
314
|
console.log(chalk.yellow('\n Uninstall cancelled. No files modified.\n'));
|
|
304
315
|
return;
|
package/dist/context/index.js
CHANGED
|
@@ -13,6 +13,29 @@ import {
|
|
|
13
13
|
import fs from "node:fs/promises";
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import os from "node:os";
|
|
16
|
+
function deriveSdkPaths(devecoSdkHome) {
|
|
17
|
+
const home = devecoSdkHome.trim().replace(/[\\/]+$/, "");
|
|
18
|
+
if (!home) {
|
|
19
|
+
return { DEVECO_SDK_HOME: "", DEVECO_PATH: "", OHOS_SDK_PATH: "", HMS_SDK_PATH: "" };
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
DEVECO_SDK_HOME: home,
|
|
23
|
+
DEVECO_PATH: path.dirname(home),
|
|
24
|
+
OHOS_SDK_PATH: path.join(home, "default", "openharmony", "ets"),
|
|
25
|
+
HMS_SDK_PATH: path.join(home, "default", "hms", "ets")
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function sdkHomeFromLegacyPaths(env) {
|
|
29
|
+
const tryStrip = (p, suffix) => {
|
|
30
|
+
if (!p) return "";
|
|
31
|
+
const norm = p.replace(/[\\/]+$/, "").split(/[\\/]/);
|
|
32
|
+
if (norm.length <= suffix.length) return "";
|
|
33
|
+
const tail = norm.slice(-suffix.length).map((s) => s.toLowerCase());
|
|
34
|
+
if (tail.join("/") !== suffix.join("/")) return "";
|
|
35
|
+
return norm.slice(0, -suffix.length).join(path.sep);
|
|
36
|
+
};
|
|
37
|
+
return tryStrip(env.OHOS_SDK_PATH, ["default", "openharmony", "ets"]) || tryStrip(env.HMS_SDK_PATH, ["default", "hms", "ets"]);
|
|
38
|
+
}
|
|
16
39
|
function getConfigDir() {
|
|
17
40
|
return path.join(os.homedir(), ".hometrans");
|
|
18
41
|
}
|
|
@@ -73,6 +96,19 @@ function defaultEditors() {
|
|
|
73
96
|
path: "~/.codex/config.toml",
|
|
74
97
|
section: "mcp_servers.hometrans"
|
|
75
98
|
}
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
// CodeBuddy(腾讯):与 Claude Code 同构的 .codebuddy/ 目录,
|
|
102
|
+
// 全局安装映射到 ~/.codebuddy/{skills,agents},MCP 走 mcpServers JSON。
|
|
103
|
+
name: "CodeBuddy",
|
|
104
|
+
markerDir: "~/.codebuddy",
|
|
105
|
+
skillsDir: "~/.codebuddy/skills",
|
|
106
|
+
agentsDir: "~/.codebuddy/agents",
|
|
107
|
+
mcp: {
|
|
108
|
+
format: "jsonc-object",
|
|
109
|
+
path: "~/.codebuddy/mcp.json",
|
|
110
|
+
keyPath: ["mcpServers", "hometrans"]
|
|
111
|
+
}
|
|
76
112
|
}
|
|
77
113
|
];
|
|
78
114
|
}
|
|
@@ -86,9 +122,12 @@ async function fileExists(p) {
|
|
|
86
122
|
}
|
|
87
123
|
function defaultEnv() {
|
|
88
124
|
return {
|
|
125
|
+
DEVECO_SDK_HOME: "",
|
|
126
|
+
DEVECO_PATH: "",
|
|
89
127
|
OHOS_SDK_PATH: "",
|
|
90
128
|
HMS_SDK_PATH: "",
|
|
91
129
|
TEST_API_KEY: "",
|
|
130
|
+
GLM_API_KEY: "",
|
|
92
131
|
TOOL_PATH: ""
|
|
93
132
|
};
|
|
94
133
|
}
|
|
@@ -132,8 +171,41 @@ async function loadHomeTransConfig() {
|
|
|
132
171
|
delete anyParsed.sdkPaths;
|
|
133
172
|
delete anyParsed.params;
|
|
134
173
|
delete anyParsed.tool_path;
|
|
174
|
+
let envDirty = false;
|
|
175
|
+
if (!config.env.DEVECO_SDK_HOME) {
|
|
176
|
+
const migrated = sdkHomeFromLegacyPaths(config.env);
|
|
177
|
+
if (migrated) {
|
|
178
|
+
config.env.DEVECO_SDK_HOME = migrated;
|
|
179
|
+
envDirty = true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (config.env.DEVECO_SDK_HOME) {
|
|
183
|
+
const derived = deriveSdkPaths(config.env.DEVECO_SDK_HOME);
|
|
184
|
+
for (const key of ["DEVECO_PATH", "OHOS_SDK_PATH", "HMS_SDK_PATH"]) {
|
|
185
|
+
if (!config.env[key]) {
|
|
186
|
+
config.env[key] = derived[key];
|
|
187
|
+
envDirty = true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (envDirty) {
|
|
192
|
+
await saveHomeTransConfig(config);
|
|
193
|
+
}
|
|
194
|
+
const existingNames = new Set(config.editors.map((e) => e.name));
|
|
195
|
+
const missingEditors = defaultEditors().filter(
|
|
196
|
+
(e) => !existingNames.has(e.name)
|
|
197
|
+
);
|
|
198
|
+
if (missingEditors.length > 0) {
|
|
199
|
+
config.editors.push(...missingEditors);
|
|
200
|
+
await saveHomeTransConfig(config);
|
|
201
|
+
}
|
|
135
202
|
return config;
|
|
136
203
|
}
|
|
204
|
+
async function saveHomeTransConfig(config) {
|
|
205
|
+
const configPath = getConfigPath();
|
|
206
|
+
await fs.mkdir(getConfigDir(), { recursive: true });
|
|
207
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
208
|
+
}
|
|
137
209
|
|
|
138
210
|
// src/context/analysis/ArkTsGitInfoAnalysis.ts
|
|
139
211
|
import {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buaa_smat/hometrans",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
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": {
|
|
Binary file
|
|
Binary file
|
|
@@ -17,12 +17,17 @@ Parse `$ARGUMENTS` as positional tokens:
|
|
|
17
17
|
|
|
18
18
|
- **Arg 1** (`android-project-path`): Path to the Android source project (required)
|
|
19
19
|
- **Arg 2** (`harmonyos-project-path`): Path to the target HarmonyOS project directory (required)
|
|
20
|
-
- **Arg 3** (`
|
|
21
|
-
- **Arg 4** (`
|
|
22
|
-
- **Arg 5** (`
|
|
23
|
-
- **Arg 6** (`
|
|
20
|
+
- **Arg 3** (`spec-file-path`): Path to the requirement spec document (required). This is the plan/spec that drives Stage 1 logic development and Stage 3 code review — both stages read it directly from this path.
|
|
21
|
+
- **Arg 4** (`assets-output-path`): Directory to store all output/report files (optional). When omitted, default to `<harmonyos-project-path>/.hometrans_output` — create the directory if it does not exist.
|
|
22
|
+
- **Arg 5** (`test-case-path`): Path to the self-test case file driving the Stage 4 self-testing loop (optional). When omitted, default to `OUTPUT/test_case.md`. If the resolved file does not exist, the Stage 4 loop is skipped (see Loop Setup).
|
|
23
|
+
- **Arg 6** (`pre-test-case-path`): Path to the pre-test case file (optional). When omitted, default to `OUTPUT/pre_test_case.md`. Only passed to the self-tester when the resolved file exists.
|
|
24
|
+
- **Arg 7** (`max-rounds-review`): Maximum number of Stage 3→3a→3b code-review-fix rounds to run (optional, default `2`). Must be a positive integer `>= 1`.
|
|
25
|
+
- **Arg 8** (`max-rounds-test`): Maximum number of Stage 4→4a→4b self-test rounds to run (optional, default `2`). Must be a positive integer `>= 1`.
|
|
26
|
+
- **Arg 9** (`skip-test`): `true` or `false` (optional, default `false`). When `true`, skip Stage 4 / 4a / 4b (Self-Testing Loop) entirely. Use this when no real HarmonyOS device is available for on-device testing.
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
Args are positional: to pass any optional arg, provide explicit values for all optional args before it (e.g., passing `test-case-path` requires an explicit `assets-output-path`; passing `skip-test` requires explicit Args 4–8).
|
|
29
|
+
|
|
30
|
+
If any required argument is missing, ask the user before proceeding. If `spec-file-path` does not exist or is not a readable file, ask the user before proceeding. If `max-rounds-review` or `max-rounds-test` is provided but is not a positive integer, ask the user before proceeding. If `skip-test` is provided but is not `true` or `false`, ask the user before proceeding.
|
|
26
31
|
|
|
27
32
|
Define shorthand variables for the instructions below:
|
|
28
33
|
|
|
@@ -30,7 +35,10 @@ Define shorthand variables for the instructions below:
|
|
|
30
35
|
|----------|---------|
|
|
31
36
|
| `ANDROID` | android-project-path |
|
|
32
37
|
| `HMOS` | harmonyos-project-path |
|
|
33
|
-
| `
|
|
38
|
+
| `SPEC` | spec-file-path — requirement spec document, consumed directly by Stage 1 (`spec-file`) and Stage 3 (`test-case-path`) |
|
|
39
|
+
| `OUTPUT` | assets-output-path; defaults to `HMOS/.hometrans_output` when not provided (created on demand) |
|
|
40
|
+
| `TEST_CASE` | test-case-path; defaults to `OUTPUT/test_case.md` when not provided |
|
|
41
|
+
| `PRE_TEST_CASE` | pre-test-case-path; defaults to `OUTPUT/pre_test_case.md` when not provided |
|
|
34
42
|
| `MAX_ROUNDS_REVIEW` | max-rounds-review — positive integer (default `2`). Controls Stage 3→3a→3b code-review-fix loop. |
|
|
35
43
|
| `MAX_ROUNDS_TEST` | max-rounds-test — positive integer (default `2`). Controls Stage 4→4a→4b self-test loop. |
|
|
36
44
|
| `SKIP_TEST` | skip-test — `true` or `false` (default `false`). When `true`, skip Stage 4 / 4a / 4b entirely (no real device available). |
|
|
@@ -147,7 +155,7 @@ Prompt format (applies to both Stage 1 and Stage 1a): ONLY the key-value lines b
|
|
|
147
155
|
```
|
|
148
156
|
Agent(
|
|
149
157
|
subagent_type="logic-context-builder",
|
|
150
|
-
prompt="harmonyos-project-path: HMOS\nspec-file:
|
|
158
|
+
prompt="harmonyos-project-path: HMOS\nspec-file: SPEC\noutput-path: OUTPUT/logic"
|
|
151
159
|
)
|
|
152
160
|
```
|
|
153
161
|
2. Verify `OUTPUT/logic/plan.md` exists.
|
|
@@ -200,7 +208,7 @@ For each `review_round` from `1..MAX_ROUNDS_REVIEW`, set `REVIEW_ROUND_DIR = OUT
|
|
|
200
208
|
- `harmonyos-project-path`: `HMOS`
|
|
201
209
|
- `commit-id`: For Round 1, use `REVIEW_COMMIT_ID`. For Round 2+, review the project holistically (omit `commit-id` or pass `none`) since fixes have modified the codebase beyond the original commit scope.
|
|
202
210
|
- `output-path`: `REVIEW_ROUND_DIR`
|
|
203
|
-
- `test-case-path`: `
|
|
211
|
+
- `test-case-path`: `SPEC`
|
|
204
212
|
3. If a valid `commit-id` is available, the agent will automatically call the `extract_commit_context` MCP tool to extract commit-scoped code context before reviewing. If the MCP call fails, the agent falls back to direct git-diff analysis. If no `commit-id` is available, the agent reviews the project holistically without commit-scoped extraction.
|
|
205
213
|
4. The agent writes `REVIEW_ROUND_DIR/code-review-report.md` with per-scenario verdicts.
|
|
206
214
|
5. **Extract defect stats**: Read `REVIEW_ROUND_DIR/code-review-report.md`, extract the verdict breakdown (PASS/PARTIAL/FAIL/UNABLE TO VERIFY counts) and overall verdict.
|
|
@@ -277,7 +285,7 @@ Treat Stage 4 → 4a → 4b as a loop that runs up to `MAX_ROUNDS_TEST` times.
|
|
|
277
285
|
|
|
278
286
|
1. **Locate the initial HAP file**: Use the `.hap` mirrored to `OUTPUT/` by the Stage 3 review loop finalization (or from Stage 2 if the review loop did not produce one). The default location is `OUTPUT/entry-default-signed.hap`. If that file does not exist, search for `*-signed.hap` files in `OUTPUT/` and use the first match. If no `.hap` is found in `OUTPUT/`, fall back to searching `HMOS/entry/build/default/outputs/default/`.
|
|
279
287
|
2. Store the resolved path as `CURRENT_HAP`.
|
|
280
|
-
3. **`
|
|
288
|
+
3. **`TEST_CASE` existence guard**: If `TEST_CASE` does not exist, mark Stage 4 / 4a / 4b as failed with note "No test case file available" and skip the loop entirely.
|
|
281
289
|
4. Initialize loop state:
|
|
282
290
|
- `round = 1`
|
|
283
291
|
- `rounds_executed = 0`
|
|
@@ -297,8 +305,8 @@ For each `round` from `1..MAX_ROUNDS_TEST`, set `ROUND_DIR = OUTPUT/round-{round
|
|
|
297
305
|
3. Input (round 1):
|
|
298
306
|
- `hap-path`: `CURRENT_HAP`
|
|
299
307
|
- `output-path`: `OUTPUT` (the ROOT — the agent always writes here)
|
|
300
|
-
- `test-case-path`: `
|
|
301
|
-
- `pre-test-case-path`: `
|
|
308
|
+
- `test-case-path`: `TEST_CASE`
|
|
309
|
+
- `pre-test-case-path`: `PRE_TEST_CASE` (only include this line when the file exists)
|
|
302
310
|
- `setup`: `true`
|
|
303
311
|
4. Input (round 2+):
|
|
304
312
|
- `hap-path`: `CURRENT_HAP`
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hmos-fix-build-errors
|
|
3
3
|
description: Build a HarmonyOS project via CLI and automatically fix compile errors in a loop until the build succeeds. Default unsigned HAP; pass --signed to build a signed HAP (signing config must already exist in the project's build-profile.json5).
|
|
4
|
-
argument-hint: <harmonyos-project-path>
|
|
4
|
+
argument-hint: <harmonyos-project-path> [deveco-studio-path] [--signed]
|
|
5
5
|
allowed-tools: Agent, Read, Write, Edit, Glob, Grep, Bash
|
|
6
6
|
type: tool
|
|
7
7
|
domain: engineering
|
|
@@ -12,8 +12,8 @@ domain: engineering
|
|
|
12
12
|
Automatically build a HarmonyOS NEXT project from the command line, parse compile errors, fix them, and retry — repeating until the build succeeds.
|
|
13
13
|
|
|
14
14
|
- **HarmonyOS Project**: `$ARGUMENTS[0]` (the HarmonyOS project root, e.g. `D:/MyHmosApp`)
|
|
15
|
-
- **DevEco Studio Path
|
|
16
|
-
- **--signed** (optional):
|
|
15
|
+
- **DevEco Studio Path** (optional): `$ARGUMENTS[1]` (DevEco Studio installation root, e.g. `D:/DevEco Studio`) — when omitted, resolved automatically (see Step 0.2)
|
|
16
|
+
- **--signed** (optional): if any argument equals `--signed`, the build produces a **signed HAP**. If omitted, the build produces an **unsigned HAP** (default).
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
@@ -21,12 +21,20 @@ Automatically build a HarmonyOS NEXT project from the command line, parse compil
|
|
|
21
21
|
|
|
22
22
|
1. **Verify project exists** — Check that `$ARGUMENTS[0]` contains a valid HarmonyOS project (look for `build-profile.json5`, `entry/src` directory, `oh-package.json5`).
|
|
23
23
|
|
|
24
|
-
2. **
|
|
24
|
+
2. **Resolve `<deveco-path>`** — stop at the first source that yields a valid path:
|
|
25
|
+
1. `$ARGUMENTS[1]` if provided (and not `--signed`).
|
|
26
|
+
2. `~/.hometrans/config.json` (on Windows `%USERPROFILE%\.hometrans\config.json`): `env.DEVECO_PATH`; else parent dir of `env.DEVECO_SDK_HOME`; else strip the trailing `/sdk/default/openharmony/ets` from `env.OHOS_SDK_PATH` (legacy).
|
|
27
|
+
3. Environment variables, same precedence: `DEVECO_PATH` → parent of `DEVECO_SDK_HOME` → strip suffix from `OHOS_SDK_PATH`.
|
|
28
|
+
4. **Ask the user** for the DevEco Studio install path (suggest running `ht init` to persist it).
|
|
29
|
+
|
|
30
|
+
**Verify the resolved `<deveco-path>`** contains:
|
|
25
31
|
- `tools/node/node.exe`
|
|
26
32
|
- `tools/hvigor/bin/hvigorw.js`
|
|
27
33
|
- `tools/ohpm/bin/ohpm`
|
|
28
34
|
- `sdk/` directory
|
|
29
35
|
|
|
36
|
+
If verification fails for a config/env-sourced path, treat that source as invalid and continue down the list; if it fails for a user-supplied path, report what is missing and ask again.
|
|
37
|
+
|
|
30
38
|
3. **Set up `local.properties`** — Ensure the project root has `local.properties` with:
|
|
31
39
|
```properties
|
|
32
40
|
hwsdk.dir=<deveco-path>/sdk
|
|
@@ -40,7 +48,7 @@ Automatically build a HarmonyOS NEXT project from the command line, parse compil
|
|
|
40
48
|
"<deveco-path>/tools/ohpm/bin/ohpm" install
|
|
41
49
|
```
|
|
42
50
|
|
|
43
|
-
5. **Determine Build Mode** — Check if `$ARGUMENTS[2]`
|
|
51
|
+
5. **Determine Build Mode** — Check if any argument equals `--signed` (it may be `$ARGUMENTS[1]` when the DevEco path is omitted, or `$ARGUMENTS[2]` when it is provided):
|
|
44
52
|
- **If NOT `--signed`** → **Unsigned build mode**. Ensure `build-profile.json5` does NOT have `signingConfigs` or `signingConfig` references in products (remove them if present). Go to Step 1.
|
|
45
53
|
- **If `--signed`** → **Signed build mode**. Proceed to Step 0.5 to validate signing config.
|
|
46
54
|
|
|
@@ -17,7 +17,13 @@ You are writing ArkTS codes.
|
|
|
17
17
|
- 不要做和用户需求无关的其他修改
|
|
18
18
|
-
|
|
19
19
|
## Step 0: Load Config
|
|
20
|
-
|
|
20
|
+
Resolve the config file in this order — stop at the first hit:
|
|
21
|
+
|
|
22
|
+
1. **`config-path` argument** if the user provided one (e.g., `config-path: D:\path\to\config.json`).
|
|
23
|
+
2. **`config.json` in this skill's own directory** — `ht init` seeds it from `config-example.json` and fills `glm_api_key` + `hmos_sdk_dir` from `~/.hometrans/config.json`.
|
|
24
|
+
3. Neither exists → **ask the user** for a valid path.
|
|
25
|
+
|
|
26
|
+
Verify the resolved file with a Read; if it's missing or unreadable, fall through to the next source (or ask the user). Refer to `config-example.json` in this skill's directory for the expected schema.
|
|
21
27
|
|
|
22
28
|
Config fields:
|
|
23
29
|
|
|
@@ -33,6 +39,14 @@ Config fields:
|
|
|
33
39
|
| `glm_api_key` | Zhipu GLM API key for phone-agent |
|
|
34
40
|
| `capture_output_dir` | Base directory for captured page data |
|
|
35
41
|
|
|
42
|
+
**Fallbacks for `hmos_sdk_dir` and `glm_api_key`** when empty or missing in the resolved config — stop at the first hit:
|
|
43
|
+
|
|
44
|
+
1. `~/.hometrans/config.json` (on Windows `%USERPROFILE%\.hometrans\config.json`): `hmos_sdk_dir` = `<env.DEVECO_SDK_HOME>/default`; `glm_api_key` = `env.GLM_API_KEY`.
|
|
45
|
+
2. Environment variables: derive `hmos_sdk_dir` from `DEVECO_SDK_HOME` (append `/default`); `glm_api_key` from `GLM_API_KEY`.
|
|
46
|
+
3. **Ask the user** (suggest running `ht init` to persist them).
|
|
47
|
+
|
|
48
|
+
Validate that `hmos_sdk_dir` exists on disk before use; if it doesn't, continue down the fallback list.
|
|
49
|
+
|
|
36
50
|
## Step 1: Capture All Related Pages on Android & HarmonyOS Devices
|
|
37
51
|
|
|
38
52
|
Read `scripts/navigation-capure.md` to learn the usage of `scripts/app_feature_verify.py` (navigation) and `scripts/page_capture.py` (capture).
|