@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.
- package/README.md +54 -89
- package/agents/build-fixer.md +15 -14
- package/agents/code-reviewer.md +3 -3
- package/agents/logic-coder.md +1 -1
- package/agents/logic-context-builder.md +1 -1
- package/agents/self-tester.md +16 -13
- package/dist/cli/config-store.js +127 -29
- package/dist/cli/config.js +9 -2
- package/dist/cli/init.js +367 -248
- package/dist/cli/uninstall.js +12 -7
- package/dist/context/index.js +19 -2
- package/env-requirements.json +19 -23
- package/package.json +1 -1
- package/resource/choose_editor.png +0 -0
- package/resource/set_environment.png +0 -0
- package/resource/set_multimodel.png +0 -0
- package/resource/set_sdk.png +0 -0
- package/skills/hmos-batch-ui-align/SKILL.md +6 -6
- package/skills/hmos-convert-pipeline/SKILL.md +7 -7
- package/skills/hmos-fix-build-errors/SKILL.md +4 -3
- package/skills/hmos-incremental-ui-align/README.md +9 -12
- package/skills/hmos-incremental-ui-align/SKILL.md +9 -9
- package/skills/hmos-incremental-ui-align/scripts/__pycache__/app_feature_verify.cpython-314.pyc +0 -0
- package/skills/hmos-incremental-ui-align/scripts/app_feature_verify.py +128 -29
- package/skills/hmos-incremental-ui-align/scripts/navigation-capure.md +37 -37
- package/skills/hmos-incremental-ui-align/scripts/page_capture.py +7 -2
- package/skills/hmos-integration-test/README.md +3 -3
- package/skills/hmos-integration-test/SKILL.md +7 -7
- package/skills/hmos-spec-generate/SKILL.md +45 -52
- package/tools/test-tools/autotest/README.md +10 -11
- package/tools/test-tools/autotest/self_test_runner.py +40 -12
- package/resource/common_config.png +0 -0
- package/resource/integration_test_config.png +0 -0
- package/resource/set_env.png +0 -0
- 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
|
|
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
|
|
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
|
-
|
|
15
|
+
uv run page_capture.py --device hdc -o ./output
|
|
11
16
|
|
|
12
17
|
# Android
|
|
13
|
-
|
|
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`
|
|
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
|
-
-
|
|
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:
|
|
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`)
|
|
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
|
|
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 `
|
|
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`
|
|
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
|
|
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
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
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
|
|
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 `
|
|
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 `
|
|
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
|
-
|
|
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
|
-
|
|
131
|
-
- Otherwise → STOP. Tell the user:
|
|
131
|
+
### Step 2 — Ensure homegraph has a fresh index for the project
|
|
132
132
|
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
142
|
+
1. Run `homegraph status <android_project_dir>` to check index state.
|
|
154
143
|
2. Decide based on the output:
|
|
155
|
-
- **Not
|
|
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
|
-
- `
|
|
162
|
-
- `
|
|
163
|
-
- `
|
|
164
|
-
- Any `
|
|
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 `
|
|
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 `
|
|
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 `
|
|
187
|
-
| **Path 2 · Reverse-by-usage** | Take the most discriminative keyword (a settings key literal, a persistence column / preference key, a unique business term). `
|
|
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 `
|
|
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**: `
|
|
197
|
-
- **Pass B · Settings breadth**: For EACH distinct keyword in the REQ body, run `
|
|
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 `
|
|
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 | `
|
|
218
|
-
| 2 | Full call chain (callers / callees, up and down) | `
|
|
219
|
-
| 3 | Cross-class
|
|
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
|
-
|
|
242
|
-
backend:
|
|
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>",
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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` 的
|
|
49
|
-
-
|
|
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`
|
|
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
|
-
-
|
|
437
|
-
|
|
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
|
-
#
|
|
466
|
-
|
|
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
|
-
|
|
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 环境变量
|
|
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
|
|
540
|
-
f"`python self_test_runner.py run ...`
|
|
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
|
-
|
|
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
|
package/resource/set_env.png
DELETED
|
Binary file
|
|
Binary file
|