@buaa_smat/hometrans 0.1.15 → 0.1.16

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 (35) hide show
  1. package/README.md +54 -89
  2. package/agents/build-fixer.md +15 -14
  3. package/agents/code-reviewer.md +3 -3
  4. package/agents/logic-coder.md +1 -1
  5. package/agents/logic-context-builder.md +1 -1
  6. package/agents/self-tester.md +16 -13
  7. package/dist/cli/config-store.js +127 -29
  8. package/dist/cli/config.js +9 -2
  9. package/dist/cli/init.js +367 -248
  10. package/dist/cli/uninstall.js +12 -7
  11. package/dist/context/index.js +19 -2
  12. package/env-requirements.json +19 -23
  13. package/package.json +1 -1
  14. package/resource/choose_editor.png +0 -0
  15. package/resource/set_environment.png +0 -0
  16. package/resource/set_multimodel.png +0 -0
  17. package/resource/set_sdk.png +0 -0
  18. package/skills/hmos-batch-ui-align/SKILL.md +6 -6
  19. package/skills/hmos-convert-pipeline/SKILL.md +7 -7
  20. package/skills/hmos-fix-build-errors/SKILL.md +4 -3
  21. package/skills/hmos-incremental-ui-align/README.md +9 -12
  22. package/skills/hmos-incremental-ui-align/SKILL.md +9 -9
  23. package/skills/hmos-incremental-ui-align/scripts/__pycache__/app_feature_verify.cpython-314.pyc +0 -0
  24. package/skills/hmos-incremental-ui-align/scripts/app_feature_verify.py +128 -29
  25. package/skills/hmos-incremental-ui-align/scripts/navigation-capure.md +37 -37
  26. package/skills/hmos-incremental-ui-align/scripts/page_capture.py +7 -2
  27. package/skills/hmos-integration-test/README.md +3 -3
  28. package/skills/hmos-integration-test/SKILL.md +7 -7
  29. package/skills/hmos-spec-generate/SKILL.md +45 -52
  30. package/tools/test-tools/autotest/README.md +10 -11
  31. package/tools/test-tools/autotest/self_test_runner.py +40 -12
  32. package/resource/common_config.png +0 -0
  33. package/resource/integration_test_config.png +0 -0
  34. package/resource/set_env.png +0 -0
  35. package/resource/ui_align_config.png +0 -0
@@ -1,37 +1,37 @@
1
- Create a timestamped output directory under `capture_output_dir`:
2
- ```
3
- task_dir = capture_output_dir / "task_{timestamp}"
4
- android_capture_dir = task_dir / "android_page_{i}_{name}"
5
- hmos_capture_dir = task_dir / "hmos_page_{i}_{name}"
6
- ```
7
- Each paired hmos-android page-pair should have the same `i`
8
-
9
- ## Navigation prompt rules
10
-
11
- Each page's `android_nav_path` / `hmos_nav_path` is used as the `--prompt` for `app_feature_verify.py`.
12
- The `--prompt` mode auto-prepends "打开{app_name}," so do not include "打开{app_name}" in the prompt.
13
-
14
- ## Navigate to Android page and Capture (If navigated Success)
15
- ```bash
16
- PYTHONIOENCODING=utf-8 python ./app_feature_verify.py \
17
- --device adb \
18
- --app "{android.app_name}" \
19
- --package "{android.package}" \
20
- --prompt "{pages[i].android_nav_path}" \
21
- --api-key "{glm_api_key}" \
22
- --max-steps 15
23
- ```
24
-
25
- **Capture Android page** (if navigation succeeded)
26
- ```bash
27
- PYTHONIOENCODING=utf-8 python ./page_capture.py --device adb -o "{android_capture_dir}"
28
- ```
29
-
30
- ## Navigate & Capture HarmonyOS Pages
31
-
32
- Same as Andorid but with `--device hdc` and HarmonyOS config values. Directory pattern:
33
- ```
34
- hmos_capture_dir = task_dir / "hmos_page_{i}_{name}"
35
- ```
36
- For pages missed in harmony OS app, navigation will certainly fail — `hmos_capture_dir` will be empty, which is expected. Skip navigation for these pages and leave the directory empty.
37
-
1
+ Create a timestamped output directory under `capture_output_dir`:
2
+ ```
3
+ task_dir = capture_output_dir / "task_{timestamp}"
4
+ android_capture_dir = task_dir / "android_page_{i}_{name}"
5
+ hmos_capture_dir = task_dir / "hmos_page_{i}_{name}"
6
+ ```
7
+ Each paired hmos-android page-pair should have the same `i`
8
+
9
+ ## Navigation prompt rules
10
+
11
+ Each page's `android_nav_path` / `hmos_nav_path` is used as the `--prompt` for `app_feature_verify.py`.
12
+ The `--prompt` mode auto-prepends "打开{app_name}," so do not include "打开{app_name}" in the prompt.
13
+
14
+ ## Navigate to Android page and Capture (If navigated Success)
15
+ ```bash
16
+ PYTHONIOENCODING=utf-8 uv run ./app_feature_verify.py \
17
+ --device adb \
18
+ --app "{android.app_name}" \
19
+ --package "{android.package}" \
20
+ --prompt "{pages[i].android_nav_path}" \
21
+ \
22
+ --max-steps 15
23
+ ```
24
+
25
+ **Capture Android page** (if navigation succeeded)
26
+ ```bash
27
+ PYTHONIOENCODING=utf-8 uv run ./page_capture.py --device adb -o "{android_capture_dir}"
28
+ ```
29
+
30
+ ## Navigate & Capture HarmonyOS Pages
31
+
32
+ Same as Andorid but with `--device hdc` and HarmonyOS config values. Directory pattern:
33
+ ```
34
+ hmos_capture_dir = task_dir / "hmos_page_{i}_{name}"
35
+ ```
36
+ For pages missed in harmony OS app, navigation will certainly fail — `hmos_capture_dir` will be empty, which is expected. Skip navigation for these pages and leave the directory empty.
37
+
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
+ # /// script
4
+ # requires-python = ">=3.10"
5
+ # [[tool.uv.index]]
6
+ # url = "https://pypi.tuna.tsinghua.edu.cn/simple"
7
+ # ///
3
8
  """
4
9
  统一页面采集工具 —— 单次采集当前设备页面的 View Tree (XML)、截图,支持滚动采集。
5
10
 
@@ -7,10 +12,10 @@
7
12
 
8
13
  用法:
9
14
  # HarmonyOS
10
- python page_capture.py --device hdc -o ./output
15
+ uv run page_capture.py --device hdc -o ./output
11
16
 
12
17
  # Android
13
- python page_capture.py --device adb -o ./output --serial emulator-5554
18
+ uv run page_capture.py --device adb -o ./output --serial emulator-5554
14
19
  """
15
20
 
16
21
  import argparse
@@ -159,7 +159,7 @@
159
159
  | 达到最大轮数 | `max_rounds_reached` | 已完成配置的最大轮数循环(`max_rounds`,默认 3),仍有失败未解决 |
160
160
  | 编译未产出 HAP(异常) | `no_signed_hap` | build-fixer 未产出签名 HAP,无法继续测试 |
161
161
  | 用例列表为空(异常) | `no_testcases` | `testcases.json` 里 0 条用例,没有可跑的内容。常见原因:解析阶段没识别到 `### Scenario:` 区块 |
162
- | 自测 agent 早退(异常) | `agent_early_exit` | `self-tester` 在跑用例之前就退出了(设备没连、autotest api_key 没配(`~/.hometrans/config.json` 的 autotest 块或 `TEST_API_KEY` 环境变量)、`test-tools/autotest` 目录找不到(`HOMETRANS_TOOL_PATH` 环境变量未配置或失效)、batch 启动失败 / 超时 / 崩溃、`setup=false` 但所需 JSON 缺失等)。报告首行会是 `status: FAIL`,第二行 `reason: <原因>`。**这种情况下不会进入 fix 循环**——这些是环境/前置条件问题,不是应用缺陷 |
162
+ | 自测 agent 早退(异常) | `agent_early_exit` | `self-tester` 在跑用例之前就退出了(设备没连、模型 api_key 没配(`HOMETRANS_MODEL_API_KEY` 环境变量或 `~/.hometrans/config.json` 的 `autotest.unified_model.api_key`)、`test-tools/autotest` 目录找不到(`HOMETRANS_TOOL_PATH` 环境变量未配置或失效)、batch 启动失败 / 超时 / 崩溃、`setup=false` 但所需 JSON 缺失等)。报告首行会是 `status: FAIL`,第二行 `reason: <原因>`。**这种情况下不会进入 fix 循环**——这些是环境/前置条件问题,不是应用缺陷 |
163
163
 
164
164
  ---
165
165
 
@@ -242,7 +242,7 @@ build-fixer 的输出摘要:编译状态、签名类型、迭代次数、修
242
242
  - 操作系统:**Windows** 是主要测试目标;macOS/Linux 上的核心命令(`hdc`、`uv`、POSIX 工具链)也能跑,Skill 里涉及到的复制操作会按可用 shell 自适应
243
243
  - `hdc` 已安装并在 PATH 中
244
244
  - `uv` 已安装(Python 包管理)
245
- - `~/.hometrans/config.json` `autotest` 块已配置真实 api_key。`api_key` 来源:`ht init` 填好 `TEST_API_KEY` 后自动写入该块;若仍是占位符,`self_test_runner.py` 会在跑用例前用 OS 环境变量 `TEST_API_KEY` 自动补全。运行时由脚本读取该块、物化成临时 YAML 交给 `AutoTest.batch`,不再需要 `config.yaml`
245
+ - api_key 已配置(与 UI 对齐共用同一个多模态模型)。统一链路「环境变量 → config.json 询问」:`self_test_runner.py` 跑用例前优先读 OS 环境变量 `HOMETRANS_MODEL_API_KEY`(由 `ht init` 导出),缺失时回落 `~/.hometrans/config.json``autotest.unified_model.api_key`;环境变量存在时会覆盖 config.json 里的对应字段。运行时由脚本读取整个 `autotest` 块、物化成临时 YAML 交给 `AutoTest.batch`,不再需要 `config.yaml`
246
246
  - 鸿蒙真机通过 USB 连接,`hdc list targets` 能看到设备
247
247
  - 签名后的 `.hap` 文件
248
248
 
@@ -262,7 +262,7 @@ A: 检查 USB 连接,跑 `hdc list targets` 看看有没有设备 SN。重新
262
262
 
263
263
  **Q: 报 autotest 配置缺失 或 api_key 没填?**
264
264
 
265
- A: 三选一:① 设置环境变量 `TEST_API_KEY`(`self_test_runner.py` 跑用例前会自动用它填充);② 重新跑 `ht init` 并填入 `TEST_API_KEY`,它会自动写入 `~/.hometrans/config.json` 的 `autotest` 块;③ 手动编辑 `~/.hometrans/config.json`,把 `autotest.unified_model.api_key` 的 `YOUR_API_KEY_HERE` 替换成真实的 API Key。
265
+ A: 按统一链路「环境变量 → config.json → 询问」配置 api_key,三选一:① 设置环境变量 `HOMETRANS_MODEL_API_KEY`(优先级最高,`self_test_runner.py` 跑用例前会用它覆盖 config.json);② 重新跑 `ht init` 配置统一模型,它会写入 `~/.hometrans/config.json` 的 `autotest.unified_model` 并导出 `HOMETRANS_MODEL_*` 环境变量;③ 手动编辑 `~/.hometrans/config.json`,把 `autotest.unified_model.api_key` 的 `YOUR_API_KEY_HERE` 替换成真实的 API Key。
266
266
 
267
267
  **Q: 前置用例失败了要不要修代码?**
268
268
 
@@ -25,14 +25,14 @@ A signed `.hap` file is required (this is a device test — the HAP will be inst
25
25
 
26
26
  ## Step 0 — Environment Variables Check (run first)
27
27
 
28
- This skill (via `self-tester`) reads its settings **directly from the OS environment variables**. Before parsing inputs, verify (read each from the OS environment: `echo "$VAR"` on macOS/Linux; `$env:VAR` in PowerShell on Windows):
28
+ This skill (via `self-tester`) resolves each setting along the standard chain — **OS environment variable first, then `~/.hometrans/config.json`, then ask the user**. Before parsing inputs, verify (read env vars with `echo "$VAR"` on macOS/Linux; `$env:VAR` in PowerShell on Windows):
29
29
 
30
- | Variable | Purpose | Valid when |
31
- |---|---|---|
32
- | `TEST_API_KEY` | LLM API key AutoTest uses to drive test cases | non-empty |
33
- | `HOMETRANS_TOOL_PATH` | HomeTrans tools dir (`<HOMETRANS_TOOL_PATH>/test-tools/autotest`) | path exists on disk; if unset, falls back to `~/.hometrans/tools` |
30
+ | Variable | config.json fallback | Purpose | Valid when |
31
+ |---|---|---|---|
32
+ | `HOMETRANS_MODEL_API_KEY` *(legacy `TEST_API_KEY` still honored as a fallback)* | `autotest.unified_model.api_key` | multimodal LLM key AutoTest uses to drive test cases (shared with UI alignment) | non-empty / not the placeholder |
33
+ | `HOMETRANS_TOOL_PATH` | `env.HOMETRANS_TOOL_PATH` | HomeTrans tools dir (`<HOMETRANS_TOOL_PATH>/test-tools/autotest`) | path exists on disk; if unset everywhere, falls back to `~/.hometrans/tools` |
34
34
 
35
- If `TEST_API_KEY` is missing/empty, **stop and ask the user to provide it** before proceeding (suggest running `ht init` to persist it as a machine environment variable). `HOMETRANS_TOOL_PATH` may be left unset (it defaults to `~/.hometrans/tools`).
35
+ The runner (`self_test_runner.py`) applies this same chain at run time (env `HOMETRANS_MODEL_API_KEY` overrides `autotest.unified_model.api_key`). If **neither** the env var **nor** `autotest.unified_model.api_key` in `~/.hometrans/config.json` provides a real key, **stop and ask the user to provide it** before proceeding (suggest running `ht init` to persist it as a machine environment variable). `HOMETRANS_TOOL_PATH` may be left unset (it defaults to `~/.hometrans/tools`).
36
36
 
37
37
  ## Step 1 — Parse Inputs
38
38
 
@@ -336,7 +336,7 @@ If failures remain (for `max_rounds_reached`), list them briefly (scenario names
336
336
  | `test_case_path` does not exist | Ask user for correct path, do not proceed |
337
337
  | `hap_path` does not exist | Ask user for correct HAP path, do not proceed |
338
338
  | `output_path` parent does not exist | Create it, proceed |
339
- | Agent returns error / times out | Report the error to user. Suggest: check device connection (`hdc list targets`), verify HAP is valid, check the `autotest` block in `~/.hometrans/config.json` (or the `TEST_API_KEY` env var) has a real api_key |
339
+ | Agent returns error / times out | Report the error to user. Suggest: check device connection (`hdc list targets`), verify HAP is valid, check the `HOMETRANS_MODEL_API_KEY` env var or `autotest.unified_model.api_key` in `~/.hometrans/config.json` has a real api_key |
340
340
  | `<output_path>/self-test-report.md` missing after agent completes | Report: "Agent completed but no report was generated. Check agent output at <output_path>." |
341
341
  | `self-test-report.md` exists but format is unrecognizable | Show the file path, report the first 80 lines as context, let user investigate |
342
342
  | Device not found (agent reports "No device") | Remind user: connect device via USB, verify `hdc list targets` shows the device |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hmos-spec-generate
3
- description: "Generate atomic-scenario requirement specs (markdown) from raw .txt requirement batches for Android-to-HarmonyOS migration. Reads a single .txt holding multiple REQ blocks (blank-line separated), explores the Android code graph via GitNexus, writes per-REQ trace files first then synthesizes specs from the trace. Triggers: spec generation, generate spec, requirement to spec, atomic scenarios, scenario decomposition, decompose scenarios, req to spec, code-trace-first, batch req txt."
3
+ description: "Generate atomic-scenario requirement specs (markdown) from raw .txt requirement batches for Android-to-HarmonyOS migration. Reads a single .txt holding multiple REQ blocks (blank-line separated), explores the Android code graph via homegraph, writes per-REQ trace files first then synthesizes specs from the trace. Triggers: spec generation, generate spec, requirement to spec, atomic scenarios, scenario decomposition, decompose scenarios, req to spec, code-trace-first, batch req txt."
4
4
  license: MIT
5
5
  allowed-tools:
6
6
  - Read
@@ -9,23 +9,24 @@ allowed-tools:
9
9
  - Bash
10
10
  - Glob
11
11
  - Grep
12
- - Skill
13
12
  - AskUserQuestion
14
13
  - TaskCreate
15
14
  - TaskList
16
15
  - TaskUpdate
17
- - mcp__gitnexus__query
18
- - mcp__gitnexus__context
19
- - mcp__gitnexus__impact
20
- - mcp__gitnexus__cypher
21
- - mcp__gitnexus__list_repos
16
+ - mcp__homegraph__search
17
+ - mcp__homegraph__explore
18
+ - mcp__homegraph__node
19
+ - mcp__homegraph__impact
20
+ - mcp__homegraph__callers
21
+ - mcp__homegraph__callees
22
+ - mcp__homegraph__files
22
23
  ---
23
24
 
24
25
  # Spec Generator Skill
25
26
 
26
27
  Produce atomic-scenario requirement specs from Android source code for Android-to-HarmonyOS migration. The input is a single `.txt` carrying multiple REQ blocks (blank-line separated); each block is processed end-to-end (3.1 → 3.5) before the next block starts.
27
28
 
28
- This skill uses GitNexus as the code-graph backend: the `gitnexus-cli` skill for index management, and `mcp__gitnexus__*` tools for code queries. Spec synthesis MUST go through a trace file written in 3.3 — never synthesize from conversation context.
29
+ This skill uses homegraph as the code-graph backend: the `homegraph` CLI for index management, and `mcp__homegraph__*` tools for code queries. Spec synthesis MUST go through a trace file written in 3.3 — never synthesize from conversation context.
29
30
 
30
31
  ## Expected Input
31
32
 
@@ -82,9 +83,9 @@ Android source layers:
82
83
 
83
84
  ### Replayable fact
84
85
 
85
- **Rule**: Every fact in the trace MUST be **replayable verbatim** from a tool response. `file:line` anchors come from `mcp__gitnexus__context` / `cypher` results or the line-number prefix of a `Read` output. Phrases with counts or ordering ("called N times", "first X then Y", "synchronously then asynchronously", "again") MUST be derivable from precise tool-driven counting and sequencing (e.g. `mcp__gitnexus__cypher` aggregating callers, or repeated `mcp__gitnexus__impact` calls). Delete any fact that cannot be replayed.
86
+ **Rule**: Every fact in the trace MUST be **replayable verbatim** from a tool response. `file:line` anchors come from `mcp__homegraph__explore` / `node` results or the line-number prefix of a `Read` output. Phrases with counts or ordering ("called N times", "first X then Y", "synchronously then asynchronously", "again") MUST be derivable from precise tool-driven counting and sequencing (e.g. `mcp__homegraph__callers` / `callees` enumeration, or repeated `mcp__homegraph__impact` calls). Delete any fact that cannot be replayed.
86
87
 
87
- **Common failure mode**: Hallucinate detail by analogy with common patterns ("synchronous write then asynchronous overwrite", "set X again"); estimate a line number from a `mcp__gitnexus__context` snippet and drift to a neighbouring symbol; make negative assertions from intuition instead of reverse-lookup with `mcp__gitnexus__impact` / `cypher` / `Grep`.
88
+ **Common failure mode**: Hallucinate detail by analogy with common patterns ("synchronous write then asynchronous overwrite", "set X again"); estimate a line number from a `mcp__homegraph__node` / `explore` snippet and drift to a neighbouring symbol; make negative assertions from intuition instead of reverse-lookup with `mcp__homegraph__impact` / `callers` / `Grep`.
88
89
 
89
90
  ---
90
91
 
@@ -125,45 +126,33 @@ Step 0 output: an ordered list of `(title, body, feature)` tuples plus a skip li
125
126
 
126
127
  ### Step 1 — Confirm the Android project is a Git repo
127
128
 
128
- Run `git rev-parse --is-inside-work-tree` inside `android_project_dir`.
129
+ `git rev-parse --is-inside-work-tree` in `android_project_dir`: `true` → Step 2; otherwise STOP — tell the user to `git init` the project first, then re-invoke. Do NOT auto-init / modify the user's repo.
129
130
 
130
- - Output is `true` proceed to Step 2.
131
- - Otherwise → STOP. Tell the user:
131
+ ### Step 2 Ensure homegraph has a fresh index for the project
132
132
 
133
- > `android_project_dir` is not a Git repository. GitNexus indexes by Git identity. Please run:
134
- >
135
- > ```
136
- > cd <android_project_dir>
137
- > git init && git add . && git commit -m "init"
138
- > ```
139
- >
140
- > then re-invoke this skill.
133
+ homegraph exposes two surfaces:
141
134
 
142
- Do NOT auto-`init` / `add` / `commit` the user's project. Do NOT modify `git config`.
135
+ - **Index management** (`init`, `index`, `sync`, `status`) run the `homegraph` CLI via `Bash`.
136
+ - **Code queries** (`search`, `explore`, `node`, `impact`, `callers`, `callees`, `files`) → call `mcp__homegraph__*` tools directly.
143
137
 
144
- ### Step 2 Ensure GitNexus has a fresh index for the project
145
-
146
- GitNexus exposes two surfaces:
147
-
148
- - **Index management** (`status`, `analyze`) → invoke the `gitnexus-cli` skill (CLI wrapper, returns text).
149
- - **Code queries** (`query`, `context`, `impact`, `cypher`, `list_repos`) → call `mcp__gitnexus__*` tools directly.
138
+ homegraph indexes per-project into a local `.homegraph/` directory; there is no central repo registry. Scope every MCP call to the Android project with `projectPath: "<android_project_dir>"` (the skill's working directory is the spec project, NOT the Android project, so the parameter is mandatory — omitting it queries the wrong project).
150
139
 
151
140
  Procedure:
152
141
 
153
- 1. Invoke `gitnexus-cli` skill with `status` against `android_project_dir`.
142
+ 1. Run `homegraph status <android_project_dir>` to check index state.
154
143
  2. Decide based on the output:
155
- - **Not registered** or **stale**invoke `gitnexus-cli` with `analyze`, wait for completion.
144
+ - **Not indexed** (no `.homegraph/`)run `homegraph init -i <android_project_dir>`, wait for completion.
145
+ - **Stale** → run `homegraph sync <android_project_dir>` (incremental; use `homegraph index <android_project_dir> --force` for a full rebuild), wait for completion.
156
146
  - **Fresh** → proceed.
157
- 3. Obtain `<repo-id>` via `mcp__gitnexus__list_repos()`; find the entry whose `path` matches `android_project_dir`; take its `repo` (or equivalent identifier) field.
158
147
 
159
148
  **Hard fail policy** — any of the following stops the skill and surfaces the error to the user; do NOT degrade to `Read` + `Grep`:
160
149
 
161
- - `gitnexus-cli` skill is unavailable → ask the user to run `/setup` first.
162
- - `gitnexus-cli status` or `analyze` returns an error → surface the error verbatim.
163
- - `mcp__gitnexus__list_repos` returns no matching entry ask the user to verify that `analyze` registered the project.
164
- - Any `mcp__gitnexus__*` call in Step 3 fails unexpectedly → surface the error; stop the skill.
150
+ - `homegraph` CLI is not found on PATH → ask the user to install it (`npm install -g homegraph`).
151
+ - `homegraph status`, `init`, `sync`, or `index` returns an error → surface the error verbatim.
152
+ - Any `mcp__homegraph__*` call reports the project is not indexed verify `init -i` ran against `android_project_dir`.
153
+ - Any `mcp__homegraph__*` call in Step 3 fails unexpectedly → surface the error; stop the skill.
165
154
 
166
- > All `mcp__gitnexus__*` calls in Step 3 MUST carry `repo=<repo-id>` (or equivalent scope parameter). Cross-repo results poison the trace.
155
+ > All `mcp__homegraph__*` calls in Step 3 MUST carry `projectPath: "<android_project_dir>"`. A missing or wrong `projectPath` poisons the trace.
167
156
 
168
157
  ### Step 3 — Process each REQ block end-to-end
169
158
 
@@ -179,22 +168,22 @@ Order matters: build the trace from Android source first, then synthesize the sp
179
168
 
180
169
  #### 3.2 Recall — Locate entry points (dual-path union)
181
170
 
182
- Run both paths in parallel within the same turn (emit all mcp calls together); the deduplicated union is the recall result. All mcp calls MUST carry `repo=<repo-id>`.
171
+ Run both paths in parallel within the same turn (emit all mcp calls together); the deduplicated union is the recall result. All mcp calls MUST carry `projectPath: "<android_project_dir>"`.
183
172
 
184
173
  | Path | Approach | Value |
185
174
  |---|---|---|
186
- | **Path 1 · By-concept lookup** | For each entity / UI anchor / intent verb from 3.1, call `mcp__gitnexus__query({ query: "<concept>", repo: "<repo-id>" })` and `mcp__gitnexus__context({ name: "<symbol>", repo: "<repo-id>" })` to find candidate symbols (Activities / Fragments / Composables / ViewModels / Services / Repositories / DAOs). Cross-validate symbol name, file path, and context against `REQ_DESC`. | Direct hits: pages obviously implementing the REQ; data models named after REQ entities. |
187
- | **Path 2 · Reverse-by-usage** | Take the most discriminative keyword (a settings key literal, a persistence column / preference key, a unique business term). `query` → candidate symbol → `mcp__gitnexus__impact({ name: "<symbol>", repo: "<repo-id>" })` returns callers (nested). Walk callers upward until hitting an `Activity` / `Fragment` / `@Composable` host. That host is an entry point. If the walk passes through intermediate dialog / popup / bottom-sheet / inner-composable components that directly reference the target key, record each as a **separate entry point** — these represent distinct user-facing surfaces within the host. | Covers structural blind spots Path 1 misses: deeply nested consumers, pass-through navigation pages, manifest-declared receivers. |
175
+ | **Path 1 · By-concept lookup** | For each entity / UI anchor / intent verb from 3.1, call `mcp__homegraph__search({ query: "<concept>", projectPath: "<android_project_dir>" })` to locate candidate symbols, then `mcp__homegraph__explore({ query: "<symbols / file names>", projectPath: "<android_project_dir>" })` to pull their source + call relations (Activities / Fragments / Composables / ViewModels / Services / Repositories / DAOs). Cross-validate symbol name, file path, and context against `REQ_DESC`. | Direct hits: pages obviously implementing the REQ; data models named after REQ entities. |
176
+ | **Path 2 · Reverse-by-usage** | Take the most discriminative keyword (a settings key literal, a persistence column / preference key, a unique business term). `search` → candidate symbol → `mcp__homegraph__callers({ symbol: "<symbol>", projectPath: "<android_project_dir>" })` (or `mcp__homegraph__impact` for the wider blast radius) returns who reaches it. Walk callers upward until hitting an `Activity` / `Fragment` / `@Composable` host. That host is an entry point. If the walk passes through intermediate dialog / popup / bottom-sheet / inner-composable components that directly reference the target key, record each as a **separate entry point** — these represent distinct user-facing surfaces within the host. | Covers structural blind spots Path 1 misses: deeply nested consumers, pass-through navigation pages, manifest-declared receivers. |
188
177
 
189
- **Behavioral REQ anchor extraction**: when `REQ_DESC` has no explicit literal keyword (e.g. "after track-change X refreshes synchronously"), extract anchors as `(action verb, affected entity)` tuples — e.g. `(track-change, X)` — and feed both into Path 1's `query` and Path 2's seed symbol search.
178
+ **Behavioral REQ anchor extraction**: when `REQ_DESC` has no explicit literal keyword (e.g. "after track-change X refreshes synchronously"), extract anchors as `(action verb, affected entity)` tuples — e.g. `(track-change, X)` — and feed both into Path 1's `search` and Path 2's seed symbol search.
190
179
 
191
180
  **Both paths hit zero** → ask the user via `AskUserQuestion` whether to manually specify an entry point. If declined → write `status: no_recall` in the trace, skip 3.3 / 3.4 / 3.5, flag in Step 4 report. Do NOT fabricate entry points.
192
181
 
193
182
  **Terse REQ handling** — if the REQ body has fewer than ~20 substantive characters, OR contains only topical keywords (e.g. "multi-device", "landscape/portrait") with no concrete action verb / entity:
194
183
 
195
184
  1. **MUST** run two broad enumeration passes (both mandatory, not optional):
196
- - **Pass A · Feature code**: `mcp__gitnexus__cypher` enumeration find all Activity / Fragment / Composable hosts whose `configChanges` manifest attribute is non-default (for orientation / resize cases) or whose layout has a resource qualifier matching the REQ topic (e.g. `-land`, `-sw600dp`, `-night`).
197
- - **Pass B · Settings breadth**: For EACH distinct keyword in the REQ body, run `mcp__gitnexus__query` + `Grep` to enumerate ALL settings / configuration screens that reference that keyword or a semantically related term. Terse REQs frequently span multiple unrelated settings pages; anchoring on the single most obvious page is the primary failure mode. The union of Pass A and Pass B is the recall result.
185
+ - **Pass A · Feature code**: enumerate hosts via `mcp__homegraph__search({ kind: "class" | "component", projectPath })` (Activities / Fragments / Composables), then `Grep` `AndroidManifest.xml` for non-default `configChanges` (orientation / resize cases) and `mcp__homegraph__files({ pattern, projectPath })` / `Grep` over `res/` for layout resource qualifiers matching the REQ topic (e.g. `-land`, `-sw600dp`, `-night`). Manifest attributes and resource qualifiers live outside the code graph, so `Grep` is authoritative there.
186
+ - **Pass B · Settings breadth**: For EACH distinct keyword in the REQ body, run `mcp__homegraph__search` + `Grep` to enumerate ALL settings / configuration screens that reference that keyword or a semantically related term. Terse REQs frequently span multiple unrelated settings pages; anchoring on the single most obvious page is the primary failure mode. The union of Pass A and Pass B is the recall result.
198
187
  2. If ≥3 plausible candidates → list them in the trace's `Status` section as `candidate_hosts: [...]` and proceed normally.
199
188
  3. If <3 candidates → invoke `AskUserQuestion`: show the candidate list, ask which are in-scope for this REQ. Do NOT silently guess.
200
189
  4. Mark in trace `Status` block: `terse_req: true` (this signal feeds 3.4 review).
@@ -210,16 +199,20 @@ Run both paths in parallel within the same turn (emit all mcp calls together); t
210
199
 
211
200
  The trace file is the sole machine-readable contract delivered to 3.5. Exploration and writing form a single unit.
212
201
 
213
- **Exploration checklist — execution order** (all mcp calls carry `repo: "<repo-id>"`):
202
+ **Exploration checklist — execution order** (all mcp calls carry `projectPath: "<android_project_dir>"`):
214
203
 
215
204
  | Step | Action | Tool |
216
205
  |---|---|---|
217
- | 1 | Survey candidate symbols for each anchor from 3.1 | `mcp__gitnexus__query` / `context` |
218
- | 2 | Full call chain (callers / callees, up and down) | `mcp__gitnexus__impact` |
219
- | 3 | Cross-class graph traversal / reverse-lookup state writers / aggregation | `mcp__gitnexus__cypher` |
206
+ | 1 | Survey candidate symbols for each anchor from 3.1 | `mcp__homegraph__search` → `mcp__homegraph__explore` / `node` |
207
+ | 2 | Full call chain (callers / callees, up and down) | `mcp__homegraph__callers` / `callees` / `impact` |
208
+ | 3 | Cross-class traversal / reverse-lookup state writers / aggregation | `mcp__homegraph__impact` (deep reverse closure; raise `depth`), then `Grep` for literal keys |
220
209
  | 4 | Exact literal positioning (settings keys, DAO columns, intent actions, preference `android:key`) | `Grep` |
221
210
  | 5 | Final `file:line` verification / zero-hit counter-evidence | `Read` (small window) / `Grep` |
222
211
 
212
+ > **homegraph has no `cypher`.** Split its two roles by whether you have a seed symbol:
213
+ > - **Reverse-lookup from a known symbol** (who writes / reaches this state, cross-class closure, aggregation) → prefer `mcp__homegraph__impact({ symbol, depth, projectPath })` and raise `depth` for multi-hop. It traverses the full reverse-dependency closure — deeper than `callers` (depth-1). Use `callers` / `callees` only for a single hop.
214
+ > - **Seedless enumeration** (all hosts of a kind, anything matching a pattern / manifest attribute / resource qualifier) → `impact` cannot help (it needs a starting symbol); use `mcp__homegraph__search({ kind })` + `mcp__homegraph__files` + `Grep`.
215
+
223
216
  **Exploration dimensions — information coverage** (orthogonal to the checklist; checklist = order, dimensions = what to capture). Apply each to every recall entry:
224
217
 
225
218
  - **Trigger code** — UI control + event callback (click / long-press / swipe / lifecycle / broadcast receiver) invoking the REQ behavior. Fill Entry's `code` and `resource` layer fields.
@@ -238,8 +231,8 @@ The trace file is the sole machine-readable contract delivered to 3.5. Explorati
238
231
 
239
232
  ## Status
240
233
  status: ok | no_recall | error
241
- repo-id: <repo-id>
242
- backend: gitnexus
234
+ project-path: <android_project_dir>
235
+ backend: homegraph
243
236
 
244
237
  ## Recalled entry points (Path 1 + Path 2 union, deduplicated)
245
238
  - Entry 1: <UI path / page name> — file:line — recalled by: path 1 | path 2 | both
@@ -285,7 +278,7 @@ backend: gitnexus
285
278
  ### Non-consumers (boundary counter-examples with evidence)
286
279
  - claim: <e.g. "App widget does not read this key">
287
280
  closure_layers: [code, resource, manifest]
288
- tools: [Grep "<pattern>", mcp__gitnexus__cypher "<...>", Read "<path>"]
281
+ tools: [Grep "<pattern>", mcp__homegraph__impact "<symbol>", Read "<path>"]
289
282
  zero_hits: <verbatim zero-hit evidence — file globs searched, regex used, hit count>
290
283
 
291
284
  ## Same-source cross-reference (if applicable)
@@ -298,9 +291,9 @@ backend: gitnexus
298
291
 
299
292
  Run the checks below against the trace from 3.3; every check must pass before proceeding to 3.5.
300
293
 
301
- - **No fabrication** — every declared `file:line` actually exists (sample-verify via `mcp__gitnexus__context` or `Read`); every behavioral claim is source-supported; every boundary claim has tool evidence.
294
+ - **No fabrication** — every declared `file:line` actually exists (sample-verify via `mcp__homegraph__node` or `Read`); every behavioral claim is source-supported; every boundary claim has tool evidence.
302
295
  - **No omission** — every detail in `REQ_DESC` has a counterpart in the trace (Entry / behavior / deviation). If REQ mentions a feature code does not implement, record as deviation, not omission.
303
- - **Method-discipline compliance** (Multi-layer claim + Replayable fact) — sample-verify random `file:line` anchors via `mcp__gitnexus__context` / `Read` (≥1 line drift → fail); sample-verify every count / ordering phrase against source (unreplayable → fail); every Entry / Touched-entry has all three layer fields filled (hit or `N/A: <reason>`); every Non-consumer claim has all four structured fields (`claim` / `closure_layers` / `tools` / `zero_hits`).
296
+ - **Method-discipline compliance** (Multi-layer claim + Replayable fact) — sample-verify random `file:line` anchors via `mcp__homegraph__node` / `Read` (≥1 line drift → fail); sample-verify every count / ordering phrase against source (unreplayable → fail); every Entry / Touched-entry has all three layer fields filled (hit or `N/A: <reason>`); every Non-consumer claim has all four structured fields (`claim` / `closure_layers` / `tools` / `zero_hits`).
304
297
  - **Implicit triggers coverage** — the trace's `Implicit triggers` section is present and explicitly filled (≥1 trigger OR `None`). Absence of the section = fail review.
305
298
  - **Trace section presence audit** — every trace MUST contain these H2 sections (with `None` if truly empty; absence = fail): `Status` / `Recalled entry points` / per `Entry` (all 4 fields: `claim` / `layers` / `interaction` / `data_flow`) / `Implicit triggers` / `Core business entities` / `Cross-entry shared declarations` / `Deviations from REQ_DESC` / `Scope / Boundary` / `Same-source cross-reference`.
306
299
 
@@ -328,5 +321,5 @@ Once all REQ blocks are processed (or skipped with warnings), produce a brief re
328
321
 
329
322
  ## Forbidden
330
323
 
331
- - **No nested LLM-agent skill calls** — no `gitnexus-exploring`, no `gitnexus-debugging`, no `gitnexus-refactoring`, no `gitnexus-impact-analysis`. They override this SKILL's instructions and cause role drift. **Exception**: `gitnexus-cli` is a CLI wrapper (not an LLM agent); using it for index management is allowed.
324
+ - **No nested LLM-agent skill calls** — no `homegraph-exploring`, no `homegraph-debugging`, no `homegraph-refactoring`, no `homegraph-impact-analysis`. They override this SKILL's instructions and cause role drift. Index management uses the `homegraph` CLI directly (`Bash`), not a skill.
332
325
  - **No modification of the Android project** — `.gradle`, `AndroidManifest.xml`, source files. This skill is read-only.
@@ -14,11 +14,10 @@ HarmonyOS 应用自测执行器。负责把自然语言写的测试用例(`tes
14
14
 
15
15
  | 工具 | 用途 | 验证命令 |
16
16
  |------|------|----------|
17
- | `uv` | Python 包管理 / 启动 `harmony-autotest` | `uv --version` |
17
+ | `uv` | Python 包管理 / 启动 `harmony-autotest` / 提供 Python 解释器 | `uv --version` |
18
18
  | `hdc` | 设备连接 / 安装 HAP | `hdc version` |
19
- | `python` ≥ 3.10 | 入口脚本 | `python --version` |
20
19
 
21
- > `uv sync`(从本地 `tools/test-tools/` 目录加载 6 个 whl)由 `self_test_runner.py` 自动执行,无需手动 `pip install`。
20
+ > 入口脚本经 `uv run --no-project python self_test_runner.py` 运行 —— uv 自带 Python(≥ 3.10),无需单独安装 Python。`uv sync`(从本地 `tools/test-tools/` 目录加载 6 个 whl)由 `self_test_runner.py` 自动执行,无需手动 `pip install`。
22
21
 
23
22
  ### 2. 配置 `autotest`(在 `~/.hometrans/config.json` 中)
24
23
 
@@ -45,8 +44,8 @@ AutoTest 配置不再使用本目录下的 `config.yaml`,而是集中存放在
45
44
  }
46
45
  ```
47
46
 
48
- - `ht init` 的 **测试配置** 环节会展示该块默认内容并提示填写 `api_key`(及 `name`/`base_url`/`provider`),写入 `~/.hometrans/config.json` 的 `autotest` 块。该 key 不再写入系统环境变量。
49
- - `api_key` 兜底:若该字段仍是占位符/空,`self_test_runner.py` 跑用例前会尝试用 OS 环境变量 `TEST_API_KEY` 补全(一般用不到,仅作手动设置时的安全网);二者都没有才报错退出。
47
+ - `ht init` 的 **模型配置** 环节会展示该块默认内容并提示填写 `api_key`(及 `name`/`base_url`/`provider`),写入 `~/.hometrans/config.json` 的 `autotest.unified_model`,并导出为 `HOMETRANS_MODEL_*` 环境变量(与 UI 对齐共用同一个模型)。
48
+ - 模型字段解析链路「环境变量 → config.json → 报错」:`self_test_runner.py` 跑用例前用 OS 环境变量 `HOMETRANS_MODEL_API_KEY` / `HOMETRANS_MODEL_NAME` / `HOMETRANS_MODEL_BASE_URL` 覆盖 config.json 里 `unified_model` 的对应字段(api_key 兼容老版本 `TEST_API_KEY` 作兜底;`provider` 不导出为环境变量,始终取 config.json,默认 openai);覆盖后 api_key 仍是占位符/空才报错退出。
50
49
  - 运行时 `self_test_runner.py` 读取该块、物化成本次任务专属的临时 YAML(`task_<ts>/autotest_config.yaml`)再交给 `AutoTest.batch`,缺 `agent_mode` 时默认补 `"single"`。
51
50
  - 默认单层模式只需填 `unified_model.api_key`;如需双层模式,可在 `autotest` 块里手动加 `execute_model` / `decision_model`(各自含 `api_key`)并把 `agent_mode` 设为 `"layered"`。
52
51
 
@@ -128,7 +127,7 @@ agent 内部用的就是这个脚本,开发自测时可以直接调:
128
127
  cd agents/test-tools/autotest
129
128
 
130
129
  # 启动一次自测(前台同步做准备 + 后台启动 batch,立即返回 PID)
131
- python self_test_runner.py run \
130
+ uv run --no-project python self_test_runner.py run \
132
131
  --testcases <path-to-testcases.json> \
133
132
  --hap <path-to-app.hap> \
134
133
  --bundle-name com.example.xxx \
@@ -137,14 +136,14 @@ python self_test_runner.py run \
137
136
  --output-dir <out>
138
137
 
139
138
  # 轮询状态(agent 每 60s 查一次)
140
- python self_test_runner.py status --task-dir <out>/task --output-dir <out>
139
+ uv run --no-project python self_test_runner.py status --task-dir <out>/task --output-dir <out>
141
140
 
142
141
  # 强行终止后台 batch
143
- python self_test_runner.py kill --task-dir <out>/task
142
+ uv run --no-project python self_test_runner.py kill --task-dir <out>/task
144
143
 
145
144
  # 重置本地状态(杀残留进程 + 删 .venv + 删本目录残留中间产物)
146
145
  # 用它代替手工 `rm -rf .venv` / `uv cache clean`,不会动全局 uv 缓存
147
- python self_test_runner.py clean
146
+ uv run --no-project python self_test_runner.py clean
148
147
  ```
149
148
 
150
149
  ### `report_tool.py` — 单独重渲染报告
@@ -215,7 +214,7 @@ agents/test-tools/autotest/
215
214
  ## 六、常见问题
216
215
 
217
216
  **Q: `autotest 配置缺失 / api_key 没填`**
218
- A: 跑 `ht init` 重新生成 `~/.hometrans/config.json` 的 `autotest` 块并填好 `TEST_API_KEY`;或手动把 `autotest.unified_model.api_key` 改成真实 key;或设置 OS 环境变量 `TEST_API_KEY` 后重试。
217
+ A: 跑 `ht init` 配置统一模型,它会写入 `~/.hometrans/config.json` 的 `autotest.unified_model` 并导出 `HOMETRANS_MODEL_*` 环境变量;或手动把 `autotest.unified_model.api_key` 改成真实 key;或设置 OS 环境变量 `HOMETRANS_MODEL_API_KEY`(兼容老版本 `TEST_API_KEY`)后重试。
219
218
 
220
219
  **Q: `uv sync` 卡住或失败**
221
220
  A: 6 个 whl 改为从本地 `tools/test-tools/` 目录加载(`pyproject.toml` 的 `[tool.uv.sources]` 用相对路径 `../*.whl`)。失败时确认这 6 个 whl 文件存在于上一级目录;其余依赖仍从清华 PyPI 镜像拉取,需基本网络。也可以 `--force-reinit` 强制重建。
@@ -224,7 +223,7 @@ A: 6 个 whl 改为从本地 `tools/test-tools/` 目录加载(`pyproject.toml`
224
223
  A: 不会。`self_test_runner.py run` 启动时会扫描并 kill 残留的 `AutoTest.batch` / `hypium_mcp` / `harmony_autotest` 进程(不止靠 PID 文件,按命令行名兜底),并自动清掉本目录顶层的 `tmp_hypium/` / `hypium_mcp.log` 等遗留中间产物。`task/` 目录在 agent 流程里也会被清空。
225
224
 
226
225
  **Q: 想彻底重置一遍本地状态再跑?**
227
- A: 跑一次 `python self_test_runner.py clean`:杀残留 → 删 `.venv` → 删 `tmp_hypium/` / `hypium_mcp.log` / `batch.pid` / `batch_stdout.log`。**不要**手工 `uv cache clean`——全局缓存清空后重装 6 个 whl 要从 gitcode 重新拉(1–3 分钟);这 6 个 whl 现在从本地加载、不走网络,但其余依赖仍走缓存,保留缓存的话 `uv sync` 只需几秒。
226
+ A: 跑一次 `uv run --no-project python self_test_runner.py clean`:杀残留 → 删 `.venv` → 删 `tmp_hypium/` / `hypium_mcp.log` / `batch.pid` / `batch_stdout.log`。**不要**手工 `uv cache clean`——全局缓存清空后重装 6 个 whl 要从 gitcode 重新拉(1–3 分钟);这 6 个 whl 现在从本地加载、不走网络,但其余依赖仍走缓存,保留缓存的话 `uv sync` 只需几秒。
228
227
 
229
228
  **Q: 想自己改 `testcases.json` 后再跑?**
230
229
  A: 改完后跑一遍校验,再交给 `self-tester`(`setup=false`):
@@ -333,9 +333,15 @@ def get_uv():
333
333
 
334
334
 
335
335
  def _uv_env():
336
+ # Isolate inner uv calls (`uv sync`, `uv run --project`) from the outer
337
+ # `uv run --no-project` context that launches this script: drop the
338
+ # recursion marker and any inherited VIRTUAL_ENV so the inner uv always
339
+ # targets SCRIPT_DIR/.venv, never the launcher's ephemeral environment.
336
340
  env = os.environ.copy()
337
341
  env["UV_NO_PROGRESS"] = "1"
338
342
  env["RUST_LOG"] = "off"
343
+ env.pop("UV_RUN_RECURSION_DEPTH", None)
344
+ env.pop("VIRTUAL_ENV", None)
339
345
  return env
340
346
 
341
347
 
@@ -433,8 +439,9 @@ def load_autotest_config(explicit: str | None) -> dict:
433
439
  """从 `~/.hometrans/config.json` 读取 `autotest` 配置并校验。
434
440
 
435
441
  - 缺文件 / 缺 `autotest` 字段 → 报错退出,提示运行 `ht init`。
436
- - 任一模型端点的 api_key 仍为占位符/空时,用 OS 环境变量 TEST_API_KEY 兜底填充。
437
- - 兜底后仍有未填的 api_key → 报错退出。
442
+ - 模型字段优先用 OS 环境变量 HOMETRANS_MODEL_* 覆盖 config.json
443
+ (api_key 兼容老版本 TEST_API_KEY 作兜底)。
444
+ - 覆盖后仍有未填的 api_key → 报错退出。
438
445
  - 缺 `agent_mode` 时默认补 "single"。
439
446
  返回可直接物化成 YAML 的 dict。
440
447
  """
@@ -462,16 +469,34 @@ def load_autotest_config(explicit: str | None) -> dict:
462
469
  # 深拷贝,避免改动原始 dict(虽然这里只读不写回)
463
470
  autotest = json.loads(json.dumps(autotest))
464
471
 
465
- # OS 环境变量 TEST_API_KEY 兜底填充占位/空 api_key
466
- env_key = os.environ.get("TEST_API_KEY", "").strip()
472
+ # 统一配置链路「环境变量 config.json 报错」:UI 对齐与自测共用同一个多模态
473
+ # 模型,由 `ht init` 导出为 HOMETRANS_MODEL_*。环境变量优先级最高——存在即覆盖
474
+ # config.json 里 unified_model 的对应字段;缺失时才用 config.json 的值。
475
+ # provider 不导出为环境变量(默认 openai),始终取 config.json 的值。
476
+ env_model = {
477
+ "api_key": (
478
+ os.environ.get("HOMETRANS_MODEL_API_KEY", "").strip()
479
+ or os.environ.get("TEST_API_KEY", "").strip() # 兼容老版本
480
+ ),
481
+ "name": os.environ.get("HOMETRANS_MODEL_NAME", "").strip(),
482
+ "base_url": os.environ.get("HOMETRANS_MODEL_BASE_URL", "").strip(),
483
+ }
484
+ um = autotest.get("unified_model")
485
+ if isinstance(um, dict):
486
+ for field in ("api_key", "name", "base_url"):
487
+ if env_model[field]:
488
+ um[field] = env_model[field]
489
+ # api_key 为各端点共用:环境变量存在时一并覆盖 execute/decision 双层模式的端点。
490
+ if env_model["api_key"]:
491
+ for _name, model in _model_dicts(autotest):
492
+ model["api_key"] = env_model["api_key"]
493
+
494
+ # 覆盖后仍为占位符/空的 api_key → 收集后报错(链路最后一层:提示用户配置)。
467
495
  missing = []
468
496
  for name, model in _model_dicts(autotest):
469
497
  cur = str(model.get("api_key", "")).strip()
470
498
  if not cur or cur == API_KEY_PLACEHOLDER:
471
- if env_key:
472
- model["api_key"] = env_key
473
- else:
474
- missing.append(f"{name}.api_key")
499
+ missing.append(f"{name}.api_key")
475
500
 
476
501
  if not list(_model_dicts(autotest)):
477
502
  logger.error(
@@ -484,7 +509,7 @@ def load_autotest_config(explicit: str | None) -> dict:
484
509
  logger.error(
485
510
  f"autotest 配置中以下 api_key 仍为占位符/空:{', '.join(missing)}。\n"
486
511
  f"请在 {config_path} 的 autotest 配置里填入真实 api_key,"
487
- f"或设置 OS 环境变量 TEST_API_KEY 后重试。"
512
+ f"或设置 OS 环境变量 HOMETRANS_MODEL_API_KEY 后重试(可运行 `ht init` 一并配置)。"
488
513
  )
489
514
  sys.exit(1)
490
515
 
@@ -536,8 +561,9 @@ def cmd_run(args):
536
561
  msg = (
537
562
  f"Refusing to run: this script is executing inside {target_venv}, "
538
563
  f"which it is about to delete and recreate (Windows file locks will leave "
539
- f"the venv in a broken state). Invoke with the system Python instead, e.g. "
540
- f"`python self_test_runner.py run ...` (NOT `uv run python ...`)."
564
+ f"the venv in a broken state). Invoke it so the interpreter stays OUT of "
565
+ f"that venv, e.g. `uv run --no-project python self_test_runner.py run ...` "
566
+ f"(NOT plain `uv run python ...`, which would execute inside the project venv)."
541
567
  )
542
568
  logger.error(msg)
543
569
  print(json.dumps({"status": "ERROR", "reason": msg}))
@@ -622,7 +648,9 @@ def cmd_run(args):
622
648
 
623
649
  # 后台启动 AutoTest.batch
624
650
  logger.info("=== Step 3: 后台启动 AutoTest.batch ===")
625
- batch_env = os.environ.copy()
651
+ # _uv_env() so the detached `uv run --project` also stays isolated from
652
+ # the outer `uv run --no-project` launcher context.
653
+ batch_env = _uv_env()
626
654
  batch_env["PYTHONIOENCODING"] = "utf-8"
627
655
  batch_env["PYTHONUTF8"] = "1"
628
656
  batch_env["PYTHONUNBUFFERED"] = "1"
Binary file
Binary file
Binary file