@harmonyos-arkts/d2h 0.1.22 → 0.1.26
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/.claude-plugin/plugin.json +1 -1
- package/README.md +3 -1
- package/agents/d2h.md +24 -43
- package/dist/bin/install-opencode.js +5 -0
- package/dist/src/install-opencode.js +12 -2
- package/dist/src/install-vscode.js +7 -2
- package/dist/src/metrics-plugin.js +54 -0
- package/package.json +1 -1
- package/skills/d2h-app-solution/SKILL.md +2 -0
- package/skills/d2h-architecture-plan/SKILL.md +8 -7
- package/skills/d2h-architecture-plan/references/directory-creation.md +16 -11
- package/skills/d2h-architecture-plan/references/plan-contract.md +2 -1
- package/skills/d2h-architecture-plan/references/source-review.md +14 -0
- package/skills/d2h-cloud-knowledge/SKILL.md +1 -1
- package/skills/d2h-dashboard/SKILL.md +23 -20
- package/skills/d2h-spec-develop/SKILL.md +126 -41
- package/skills/d2h-spec-generate/SKILL.md +31 -80
- package/skills/d2h-spec-generate/assets/spec-template.md +1 -1
- package/skills/d2h-spec-generate/references/from-plan.md +13 -5
- package/skills/d2h-spec-generate/references/spec-content.md +4 -6
- package/skills/d2h-spec-plan/SKILL.md +19 -24
- package/skills/d2h-spec-plan/agents/openai.yaml +2 -2
- package/skills/d2h-spec-plan/references/plan-contract.md +1 -1
- package/skills/d2h-spec-plan/references/planning-method.md +34 -8
- package/skills/d2h-spec-plan/references/planning-review.md +35 -0
- package/skills/d2h-spec-plan/references/round-scope-assessment.md +7 -7
- package/skills/d2h-spec-plan/references/work-packets.md +8 -34
- package/skills/managing-d2h-toolchain/environment-requirements.json +1 -1
- package/skills/managing-d2h-toolchain/toolchain.json +1 -1
- package/skills/d2h-spec-develop/agents/openai.yaml +0 -7
- package/skills/d2h-spec-develop/references/worker-workflow.md +0 -26
- package/skills/d2h-spec-plan/references/architecture-handoff.md +0 -9
package/README.md
CHANGED
|
@@ -46,6 +46,8 @@ npx --yes @harmonyos-arkts/d2h@latest install --agent vscode --force
|
|
|
46
46
|
npx --yes @harmonyos-arkts/d2h@latest install --agent vscode --force --remote
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
安装或升级时会自动清除指标插件缓存(`~/.cache/opencode/{packages,node_modules}`,macOS/Linux/Windows 路径一致;VS Code 场景为 `~/.harmonybot/opencode/vscode/cache/opencode/{packages,node_modules}`)中 `opencode-metrics-plugin*` 目录,OpenCode 会在下次启动时重新拉取最新版插件;`--dry-run` 只预览不删除。
|
|
50
|
+
|
|
49
51
|
安装器会安装清单中验证过的 AppGraph 和 droid2hmos 版本,并安装迁移 Skill 和 `d2h` Agent。安装完成后请完全退出并重新启动对应应用。
|
|
50
52
|
|
|
51
53
|
## 检查与升级
|
|
@@ -108,4 +110,4 @@ npx --yes @harmonyos-arkts/d2h@latest doctor --agent codex
|
|
|
108
110
|
|
|
109
111
|
`d2h workflow --help` 查看能力,`d2h workflow contract artifacts` 查看产物所有权。运行时与契约随 D2H 包分发,不再位于规划 Skill 内;只复制或删除某个 Skill 不影响公共工具。Python 解释器可用 `D2H_PYTHON` 显式指定。详见 [工作流解耦说明](docs/workflow-decoupling.md)。
|
|
110
112
|
|
|
111
|
-
增量流程可先用 `d2h-spec-plan` 输出可评审波次计划,再由原 `d2h-spec-generate` 的 `from-plan` 模式展开所选成员,旧独立生成模式保留。新计划以 `spec_generation_policy=required-v1` 对未生成或失效正文阻止派发;评审用 `d2h workflow validate --planning-only`,生成交接见 `d2h workflow contract generation`。主入口 `d2h.md` 由人工适配。
|
|
113
|
+
增量流程可先用 `d2h-spec-plan` 输出可评审波次计划,再由原 `d2h-spec-generate` 的 `from-plan` 模式展开所选成员,旧独立生成模式保留。新计划以 `spec_generation_policy=required-v1` 对未生成或失效正文阻止派发;评审用 `d2h workflow validate --planning-only`,生成交接见 `d2h workflow contract generation`。主入口 `d2h.md` 由人工适配。
|
package/agents/d2h.md
CHANGED
|
@@ -11,17 +11,18 @@ description: Android 应用到 HarmonyOS 的 AI 驱动迁移主 Agent;按迁
|
|
|
11
11
|
|
|
12
12
|
必须覆盖所有页面、可见控件和状态、导航与 Dialog 流程、业务功能,以及影响行为的资源和配置。Android 源码与真实运行行为决定“做什么”,HarmonyOS 原生机制决定“怎么做”。无法等价的差异必须明确记录,不能静默省略或伪装成完成。
|
|
13
13
|
|
|
14
|
-
迁移由 AI 读取 Facts、`.migration/utg/page.json`、页面 Spec、`batch_report.json`、Android 源码、HarmonyOS
|
|
14
|
+
迁移由 AI 读取 Facts、`.migration/utg/page.json`、页面 Spec、`batch_report.json`、Android 源码、HarmonyOS 代码和实际测试结果后作出决策。
|
|
15
15
|
|
|
16
16
|
## 产物边界
|
|
17
17
|
|
|
18
|
-
- `.migration/progress.json` 汇总七个阶段的进度。工作区就绪后执行 `droid2hmos progress init <ANDROID_PROJECT>`,恢复时保留已有记录;用 `progress update <ANDROID_PROJECT> --stage <STAGE_ID> --status <in_progress|completed|blocked>` 更新状态,阻塞原因用 `--blocker`
|
|
18
|
+
- `.migration/progress.json` 汇总七个阶段的进度。工作区就绪后执行 `droid2hmos progress init <ANDROID_PROJECT>`,恢复时保留已有记录;用 `progress update <ANDROID_PROJECT> --stage <STAGE_ID> --status <in_progress|completed|blocked>` 更新状态,阻塞原因用 `--blocker` 记录。
|
|
19
|
+
按 CLI 的语义 ID 更新:架构规划为 `harmony-architecture`,Android 分析为 `android-analysis`。仅在实际交付条件满足后标记完成,返工时同步受影响阶段;不手改 JSON。
|
|
19
20
|
- `.migration/architecture-plan/architecture-plan.md` 保存架构选项、推荐/选定方案、源到目标目录归属、主要依赖、共享所有者及风险。实际创建/复用基础工程与目录时才附 `shell-receipt.json`;规划或目录壳子不能作为目标能力已实现的证据。
|
|
20
21
|
- `.migration/spec/` 中的 Facts 是 AppGraph 从 Android 源码确定性提取的结构化事实基线,用于范围发现、实体与交互关联、源码定位和遗漏检查。Facts 可能包含 unresolved 项或存在抽取遗漏,不能替代对 Android 源码及必要运行行为的阅读核验。
|
|
21
22
|
- `.migration/utg/page.json` 是页面、到达路径和导航计划的输入,也是页面 Spec 使用的运行路径依据。
|
|
22
23
|
- `.migration/page-specs/pages.json` 保存 AI 作出的页面范围、顺序、评分和证据决策。
|
|
23
|
-
- 每个 `.migration/page-specs/page_NNNN_<name>-<id>/` 只包含 `spec.md
|
|
24
|
-
- `.migration/page-specs/batch_report.json`
|
|
24
|
+
- 每个 `.migration/page-specs/page_NNNN_<name>-<id>/` 只包含 `spec.md`。
|
|
25
|
+
- `.migration/page-specs/batch_report.json` 是唯一执行账本,记录逐 Spec 的实现、证据、差异和回归结论。主 Agent 在进入开发时按有效计划初始化,恢复时保留已有账本;后续原位更新并保留历史,不得重新生成覆盖。
|
|
25
26
|
- Spec、账本、测试文件和状态字段都是辅助证据,不是应用实现本身。只存在于文档、注释、桩代码或不可达文件中的能力均视为未完成。
|
|
26
27
|
|
|
27
28
|
## 场景与 Skill 路由
|
|
@@ -35,8 +36,9 @@ description: Android 应用到 HarmonyOS 的 AI 驱动迁移主 Agent;按迁
|
|
|
35
36
|
| 从源码生成可审计功能清单、MVP/core/full 阶段、逐功能技术栈/三方 SDK/SO/权限与跨仓实现状态 | `d2h-app-solution` |
|
|
36
37
|
| 从 Android 源码生成或刷新 Facts、UTG、点击路径和 `.migration/utg/page.json` | `d2h-utg-static-generate` |
|
|
37
38
|
| 需要 AI 修复源码派生 UTG,或在 Android 设备上按已有计划采集运行时证据 | `d2h-utg-ai-generate` |
|
|
38
|
-
|
|
|
39
|
-
|
|
|
39
|
+
| 根据已确认功能范围、架构和分析结果生成或修订 Spec 波次计划 | `d2h-spec-plan` |
|
|
40
|
+
| 基于已确认计划生成逐页 `spec.md` 与生成记录 | `d2h-spec-generate` |
|
|
41
|
+
| 按固定 Spec 范围串行实施、构建、检查并返回提交与证据 | `d2h-spec-develop` |
|
|
40
42
|
| 根据 Android 工程结构与必要源码做目录级架构规划,详列推荐方案并对照另两套;确认并要求创建后落地目录壳子 | `d2h-architecture-plan` |
|
|
41
43
|
| 规划或编写 ArkTS 架构、状态、导航和页面代码 | `arkts-mvvm-architecture` |
|
|
42
44
|
| 生成或执行 Python Hypium 端到端功能测试 | `d2h-hypium-forge` |
|
|
@@ -54,13 +56,9 @@ description: Android 应用到 HarmonyOS 的 AI 驱动迁移主 Agent;按迁
|
|
|
54
56
|
|
|
55
57
|
### 一、启动或恢复
|
|
56
58
|
|
|
57
|
-
加载 `managing-d2h-toolchain` skill 完成基础工具链预检和安装,仅需要执行一次
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
`.migration/progress.json` 就绪(`progress init` 首次创建或恢复时已存在)后,立即加载 `d2h-dashboard` 自动启动实时看板并用默认浏览器打开网页;看板启动或打开失败只记录,不阻塞迁移。
|
|
62
|
-
|
|
63
|
-
设备暂不可用时可以继续静态分析、页面规划和不依赖设备的实现,但交互页面不能标记为验证通过,也不能进入最终完成门禁。
|
|
59
|
+
1. 加载 `managing-d2h-toolchain` skill 完成基础工具链预检和安装,仅需要执行一次
|
|
60
|
+
2. 加载 `preparing-migration-workspace`,完成工具链、Android/HarmonyOS 工程、迁移工作区、配置、基线和设备检查。迁移工作区使用 Android 工程父目录下的 `<工程名>-migration/.migration/`,不得写入 Android 工程内部。
|
|
61
|
+
3. `.migration/progress.json` 就绪(`progress init` 首次创建或恢复时已存在)后,立即加载 `d2h-dashboard` 自动启动实时看板并用默认浏览器打开网页;看板启动或打开失败只记录,不阻塞迁移。
|
|
64
62
|
|
|
65
63
|
### 二、功能清单生成
|
|
66
64
|
|
|
@@ -68,29 +66,22 @@ description: Android 应用到 HarmonyOS 的 AI 驱动迁移主 Agent;按迁
|
|
|
68
66
|
|
|
69
67
|
### 三、HarmonyOS 工程架构设计
|
|
70
68
|
|
|
71
|
-
1. 加载 `d2h-architecture-plan`,输入 Android
|
|
69
|
+
1. 加载 `d2h-architecture-plan`,输入 Android 工程目录、已确认功能范围和第二阶段已有功能分析作为定位线索。遵循该 Skill 的默认 Android 阅读范围、鸿蒙工程结合选择及目录级分析深度,不另做全量扫描或完整业务链审计。
|
|
72
70
|
2. 将规划写入 `.migration/architecture-plan/architecture-plan.md`:默认详列推荐方案目录与归属,另两套给结构差异对照。沿用该 Skill 的基础依赖/业务 feature 分层和共享所有权规则;候选页面族是目录设计依据,不是最终页面/Spec 清单。
|
|
73
71
|
3. 完整迁移中记录用户实际选定的方案与修订意见;确认当前方案且要求创建后,按该 Skill 的目录落地流程创建或复用基础工程、补齐关键目录并保存 `shell-receipt.json`。已有“采用推荐并创建”授权直接执行;预检已有壳则复用,不把目录壳子当成注册完成的模块或业务实现。
|
|
74
72
|
4. 创建/复用后核对 `.migration/config.json` 的 `harmonyProject` 与实际目标根一致;路径变化时通过现有 CLI 配置入口同步。向后续阶段交接当前规划路径、选定方案、目标根、模块/目录归属、共享所有者与必要前置,实际落地时再附 receipt 及未完成项。完整迁移的架构阶段须完成选型,以及本次要求的创建/复用后才能标记完成;仅请求规划则按 Skill 的规划交付条件结束。
|
|
75
73
|
|
|
76
|
-
选型尚未明确时,可以继续下一阶段不依赖目标结构的 Android 分析;确认前不据候选方案创建业务目录或固定 Spec 的目标归属。只影响实现的 SDK/Native 未知保留为相关能力前置;仍影响模块/目录的未知明确待决范围,不伪称结构已经确定。
|
|
77
|
-
|
|
78
74
|
### 四、Android 工程分析
|
|
79
75
|
|
|
80
76
|
1. 加载 `d2h-utg-static-generate`,生成或刷新 Facts、UTG、点击路径和 `.migration/utg/page.json`,并对照真实 Android 源码核验页面、入口、交互、数据流和 unresolved 项。
|
|
81
|
-
2.
|
|
82
|
-
3. Facts 或 UTG 未覆盖、但源码中真实存在的行为仍须纳入页面 Spec,并在 `batch_report.json` 中记录发现与处理情况。
|
|
83
|
-
4. 发现架构规划遗漏的职责、共享状态或依赖冲突时,返回架构阶段局部修订受影响归属,保留已有用户选择;涉及重要取舍时展示变更,不重做全部架构分析,也不由页面候选静默裁剪真实业务。
|
|
77
|
+
2. 加载 `d2h-utg-ai-generate` 补充 utg 为识别的项目。
|
|
84
78
|
|
|
85
79
|
### 五、spec 设计与生成
|
|
86
|
-
加载 `d2h-spec-generate`,由 AI 基于当前 Facts、`.migration/feature-analysis/` 的功能清单、`.migration/utg/page.json`、Android 源码、已选定架构和实际 HarmonyOS 工程状态完成:
|
|
87
80
|
|
|
88
|
-
1.
|
|
89
|
-
2.
|
|
90
|
-
3.
|
|
91
|
-
4. 使用该 Skill 自带的 `build_batch_report.py` 生成初始 `batch_report.json`。
|
|
81
|
+
1. 将已确认的功能清单与范围、Facts、`.migration/utg/page.json`、源码证据和选定架构交给 `d2h-spec-plan`,明确执行正式规划,保存 `features.json` 与 `pages.json`。功能范围项映射到独立 Spec,页面保存归属;按交付目标组织波次,再将同轮同页的 Spec 组织为开发批次,保留逐成员分组依据与独立验收身份。
|
|
82
|
+
2. 加载 `d2h-spec-generate`,按确认计划及选定 Spec 生成详细 `spec.md` 和生成记录;沿用已验证的 Spec 模板与内容要求,发现计划问题时返回修订建议。
|
|
83
|
+
3. 将详细规格与生成结果交回 `d2h-spec-plan`,复核分组和依赖,交接开发前的计划版本、范围版本及选定批次/Spec;无实质变化时保留原版本。发生修订时核对受影响的生成绑定,规格缺失或失效的成员不能进入开发。
|
|
92
84
|
|
|
93
|
-
`d2h-spec-generate` 是页面范围、顺序和 Spec 格式的规则真源;在已选目录/共享边界内细化页面与实现范围,不能直接把架构候选当最终页面身份,也不重新选一套目录。新证据需要改变边界时返回最小修订。主 Agent 不在本文重复实现页面拆分算法,也不使用其他静态脚本生成、选择或执行任务。
|
|
94
85
|
|
|
95
86
|
### 六、代码转换
|
|
96
87
|
|
|
@@ -101,33 +92,23 @@ description: Android 应用到 HarmonyOS 的 AI 驱动迁移主 Agent;按迁
|
|
|
101
92
|
- 迁移字符串和多语言时使用 `arkts-i18n`,禁止用 `.ets` 硬编码代替字符串资源迁移。
|
|
102
93
|
- 转换图片和 drawable 时使用 `android2hmos_resources_convert`;发现尺寸或比例问题时使用 `arkts-icon-sizing`。
|
|
103
94
|
|
|
95
|
+
2. 加载 `d2h-spec-develop`,主 Agent 按有效计划初始化或恢复 `batch_report.json`,以严格串行方式推进页面批次。任意时刻只派发一个同页批次,由一个独立 page-worker 在固定窗口内逐 Spec 实施:
|
|
104
96
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
d. 页面实现与测试通过后,在 `batch_report.json` 当前页面明细中记录真实实现文件、Android 源码依据、本次构建与 Forge 测试的实际结果及证据路径,以及差异或阻塞。未产出或未保存的结果、日志如实记录为缺失,不得填写假定存在的路径或据此标记通过。
|
|
111
|
-
e. 主 Agent 完成当前页面的实现提交,原位更新 `batch_report.json`,并按 Skill 的页面完成清单复核实现、构建、测试、证据、提交和工作区状态。
|
|
112
|
-
f. 前页面未闭环、仍有页面所属未提交修改或复核失败时,只能继续修复当前页面。当前页面完整闭环并通过主 Agent 复核后,同一 worker 才能继续批次内下一页;当前批次全部闭环后,主 Agent 才能选择下一批页面并派发新的 worker。
|
|
97
|
+
a. 主 Agent 根据计划、账本及实际前置证据选择当前波次内就绪的页面批次,固定本次 `batch_id/spec_ids`、版本和允许路径。
|
|
98
|
+
b. 向 page-worker 交接详细 Spec、源码与目标工程、Git 基线、真实共享接口和检查要求。worker 加载 `d2h-spec-develop`,只实施固定成员,返回逐 Spec 的变更、提交、证据和缺口;不自行扩组或更新账本。
|
|
99
|
+
c. 主 Agent 按计划的验证安排组织检查;需要生成或更新 Python Hypium 测试资产时加载 `d2h-hypium-forge`。核对实际断言、执行结果和证据,未执行、缺失或过期的结果不能标记通过。
|
|
100
|
+
d. 主 Agent 复核作用域提交和工作区,逐 Spec 更新 `batch_report.json`,记录真实实现文件、来源、本次构建与测试证据、差异及阻塞;分组不得合并验收状态。
|
|
101
|
+
e. 当前窗口成员闭环后再选择下一批次;受阻时显式暂停并保存进度,按依赖协调后续工作。同一页面可跨波次再次开发,当前批次完成只表示本次增量完成,全部约定范围解决后才能标记整页完成。
|
|
113
102
|
|
|
114
103
|
若当前环境不支持独立 page-worker,必须明确报告该开发模式无法执行,不能静默改成主 Agent 批量开发。
|
|
115
104
|
|
|
116
|
-
### 七、测试与交付
|
|
117
|
-
|
|
118
|
-
全部页面达到 `implemented/passed/passed` 后:
|
|
119
|
-
|
|
120
|
-
1. 加载 `d2h-hypium-forge`,在已安装当前 HAP 的目标设备上执行覆盖全部迁移范围内功能场景的完整 Python Hypium 回归,保留本次实际执行结果和日志。核对用例范围、执行数和各项状态;零执行、失败、错误或必测场景被跳过、未执行时均不能通过,历史 PASS 不能替代本次完整回归。
|
|
121
|
-
2. 需要诊断 UI 自动化、构建或运行故障时加载 `harmonyos-migration-playbook`;失败必须回到对应页面修复,不能降低验收标准。
|
|
122
|
-
3. 主 Agent 对照各页 Spec、源码、实际实现和设备结果完成行为审查,将结论、被测 HAP 与设备、完整回归结果和实际证据路径、差异及未解决问题记录到 `batch_report.json` 的批次回归部分。直接核对账本队列、页面明细、汇总和引用证据的一致性,满足完成条件后才能宣布迁移完成。
|
|
123
|
-
4. 完整结束或明确中止后加载 `reviewing-migration-process` 生成流程复盘;复盘不是完成条件。
|
|
124
|
-
|
|
125
105
|
## 持续执行与人工介入
|
|
126
106
|
|
|
127
107
|
迁移开始后默认连续执行,不得在页面或阶段之间询问“是否继续”。只有以下情况允许暂停:
|
|
128
108
|
|
|
129
109
|
- 禁止依据模型自行估算的“剩余 token”“上下文百分比”“本轮剩余容量”等决定停止、收尾、汇报后等待或要求用户开启新会话。此类估算不可信。
|
|
130
110
|
- 用户主动中断、修改需求或发出新指令。
|
|
111
|
+
- 首次范围尚未确认或用户正在调整功能清单;收到明确回答后从对应环节继续,已有有效选择不重复询问。
|
|
131
112
|
- 架构选型或重要修订需要用户决定时,只等待依赖该选择的目标目录落地/Spec 分配;独立的 Android 分析可继续。仅请求架构规划则交付即可,不强求立即选型;已有“采用推荐并创建”明确授权时记录后继续,不重复询问。
|
|
132
113
|
- 真机签名、开发者账号、权限或凭据必须由用户完成,且没有可用替代方案。
|
|
133
114
|
- 设备、CLI、构建环境或外部服务持续不可用,完成自诊和有限重试后仍确认是外部问题。
|
|
@@ -158,7 +139,7 @@ f. 前页面未闭环、仍有页面所属未提交修改或复核失败时,
|
|
|
158
139
|
- Preflight、Facts、UTG、页面 Spec、`batch_report.json` 和证据对应当前源码。
|
|
159
140
|
- `pages.json` 覆盖全部已知页面或明确记录排除原因。
|
|
160
141
|
- 每个已知页面、Dialog、Sheet 和关键模式都有唯一 `spec.md`,页面包中没有额外产物。
|
|
161
|
-
- `batch_report.json`
|
|
142
|
+
- `batch_report.json` 覆盖当前确认范围内的全部 Spec,逐项验收、波次目标和页面约定范围均已满足,没有未解决阻塞;以当前账本契约与实际证据核验,不以旧页面状态字段替代逐项验收。
|
|
162
143
|
- 所有 Spec 功能项都有真实、可达的 HarmonyOS 实现。
|
|
163
144
|
- HarmonyOS 工程构建、安装、启动成功;当前 HAP 的 Forge 完整回归通过,没有失败、错误或必测场景未执行。
|
|
164
145
|
- `batch_report.json` 已记录最终行为审查、完整回归结论及实际证据路径,队列、页面明细和汇总一致。
|
|
@@ -11,6 +11,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
11
11
|
import path from 'node:path';
|
|
12
12
|
import os from 'node:os';
|
|
13
13
|
import { inspectInstallation, installOpenCode } from '../src/install-opencode.js';
|
|
14
|
+
import { findMetricsPluginCacheDirs, metricsPluginCacheRoots } from '../src/metrics-plugin.js';
|
|
14
15
|
import { inspectToolchain, installToolchain, loadToolchain } from '../src/toolchain.js';
|
|
15
16
|
import fs from 'node:fs';
|
|
16
17
|
import { inspectVSCodeInstallation, installVSCode } from '../src/install-vscode.js';
|
|
@@ -86,6 +87,8 @@ try {
|
|
|
86
87
|
const results = inspectToolchain(packageRoot);
|
|
87
88
|
for (const item of results)
|
|
88
89
|
console.log(`${item.status}: ${item.package} expected ${item.expectedVersion}, installed ${item.installedVersion ?? 'missing'}; capabilities ${JSON.stringify(item.capabilities)}`);
|
|
90
|
+
const pluginCache = agent === 'codex' ? [] : findMetricsPluginCacheDirs(metricsPluginCacheRoots(agent));
|
|
91
|
+
console.log(`plugin cache: ${pluginCache.length ? `${pluginCache.join(', ')}; cleared by d2h install/upgrade` : 'none'}`);
|
|
89
92
|
const checkerUrl = new URL('../../scripts/check-environment.mjs', import.meta.url);
|
|
90
93
|
const { main } = await import(__rewriteRelativeImportExtension(checkerUrl.href));
|
|
91
94
|
const environmentExit = await main([]);
|
|
@@ -157,6 +160,8 @@ try {
|
|
|
157
160
|
console.log(`definitions ${installation.status}: ${installation.summary}`);
|
|
158
161
|
for (const item of inspectToolchain(packageRoot))
|
|
159
162
|
console.log(`${item.status}: ${item.package} expected ${item.expectedVersion}, installed ${item.installedVersion ?? 'missing'}`);
|
|
163
|
+
const pluginCache = options.agent === 'codex' ? [] : findMetricsPluginCacheDirs(metricsPluginCacheRoots(options.agent));
|
|
164
|
+
console.log(`plugin cache: ${pluginCache.length ? `${pluginCache.join(', ')}; cleared by d2h install/upgrade` : 'none'}`);
|
|
160
165
|
}
|
|
161
166
|
else if (!skipTools)
|
|
162
167
|
installToolchain(packageRoot, { dryRun: options.dryRun, force: options.force });
|
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import crypto from 'node:crypto';
|
|
5
|
-
import { updateMetricsPlugin } from './metrics-plugin.js';
|
|
5
|
+
import { clearMetricsPluginCache, findMetricsPluginCacheDirs, metricsPluginCacheRoots, updateMetricsPlugin } from './metrics-plugin.js';
|
|
6
6
|
export function stat(file) {
|
|
7
7
|
try {
|
|
8
8
|
return fs.lstatSync(file);
|
|
@@ -136,7 +136,7 @@ export function resolvedLocation(directory) {
|
|
|
136
136
|
const parent = path.dirname(directory);
|
|
137
137
|
return parent === directory ? directory : path.join(resolvedLocation(parent), path.basename(directory));
|
|
138
138
|
}
|
|
139
|
-
export function installOpenCode({ sourceDir, configDir, agent = 'opencode', link = false, force = false, dryRun = false, remote = false, log = console.log }) {
|
|
139
|
+
export function installOpenCode({ sourceDir, configDir, agent = 'opencode', link = false, force = false, dryRun = false, remote = false, pluginCacheRoots, log = console.log }) {
|
|
140
140
|
if (!['opencode', 'deveco'].includes(agent))
|
|
141
141
|
throw new Error(`unsupported agent: ${agent}`);
|
|
142
142
|
const application = agent === 'deveco' ? 'DevEco Code' : 'OpenCode';
|
|
@@ -257,6 +257,16 @@ export function installOpenCode({ sourceDir, configDir, agent = 'opencode', link
|
|
|
257
257
|
fs.rmSync(temporary);
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
|
+
const pluginCacheRoot = pluginCacheRoots ?? metricsPluginCacheRoots(agent);
|
|
261
|
+
if (dryRun) {
|
|
262
|
+
for (const dir of findMetricsPluginCacheDirs(pluginCacheRoot))
|
|
263
|
+
log(`would remove metrics plugin cache: ${dir}`);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
const cleared = clearMetricsPluginCache({ roots: pluginCacheRoot, log });
|
|
267
|
+
for (const failure of cleared.failures)
|
|
268
|
+
log(`warning: failed to clear metrics plugin cache: ${failure}`);
|
|
269
|
+
}
|
|
260
270
|
log(`${application} installation ${dryRun ? 'preview' : 'complete'}: ${skills} skill(s), ${agents} agent(s).`);
|
|
261
271
|
log(`Config directory: ${configRoot}`);
|
|
262
272
|
if (!dryRun)
|
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import crypto from 'node:crypto';
|
|
5
|
-
import { updateMetricsPlugin } from './metrics-plugin.js';
|
|
5
|
+
import { clearMetricsPluginCache, findMetricsPluginCacheDirs, metricsPluginCacheRoots, updateMetricsPlugin } from './metrics-plugin.js';
|
|
6
6
|
const extensionPrefix = 'huaweideveloperserviceplatform.harmonyosdevassistantforvscode-';
|
|
7
7
|
const modeLabel = '安卓转鸿蒙';
|
|
8
8
|
function digest(file) {
|
|
@@ -118,7 +118,7 @@ function copyWithBackup(source, destination, backupRoot, root) {
|
|
|
118
118
|
}
|
|
119
119
|
return true;
|
|
120
120
|
}
|
|
121
|
-
export function installVSCode({ sourceDir, extensionDir, bundleCacheRoot, remote = false, dryRun = false, log = console.log }) {
|
|
121
|
+
export function installVSCode({ sourceDir, extensionDir, bundleCacheRoot, pluginCacheRoots, remote = false, dryRun = false, log = console.log }) {
|
|
122
122
|
const sourceRoot = fs.realpathSync(sourceDir);
|
|
123
123
|
const root = validatedRoot(locateVSCodeExtension(extensionDir));
|
|
124
124
|
const cacheRoot = path.resolve(bundleCacheRoot ?? path.join(os.homedir(), '.harmonybot', 'opencode-bundle', 'vscode'));
|
|
@@ -147,6 +147,8 @@ export function installVSCode({ sourceDir, extensionDir, bundleCacheRoot, remote
|
|
|
147
147
|
for (const entry of patches)
|
|
148
148
|
log(`would update ${entry.file}`);
|
|
149
149
|
log(`would remove VS Code bundle cache ${cacheRoot}`);
|
|
150
|
+
for (const dir of findMetricsPluginCacheDirs(pluginCacheRoots ?? metricsPluginCacheRoots('vscode')))
|
|
151
|
+
log(`would remove metrics plugin cache: ${dir}`);
|
|
150
152
|
return { extensionRoot: root, changed: false };
|
|
151
153
|
}
|
|
152
154
|
if (!/^name: d2h\r?$/m.test(fs.readFileSync(path.join(sourceRoot, 'agents', 'd2h.md'), 'utf8'))) {
|
|
@@ -176,6 +178,9 @@ export function installVSCode({ sourceDir, extensionDir, bundleCacheRoot, remote
|
|
|
176
178
|
if (!changed)
|
|
177
179
|
fs.rmdirSync(backupRoot);
|
|
178
180
|
fs.rmSync(cacheRoot, { recursive: true, force: true });
|
|
181
|
+
const cleared = clearMetricsPluginCache({ roots: pluginCacheRoots ?? metricsPluginCacheRoots('vscode'), log });
|
|
182
|
+
for (const failure of cleared.failures)
|
|
183
|
+
log(`warning: failed to clear metrics plugin cache: ${failure}`);
|
|
179
184
|
log(`VS Code D2H ${changed ? 'installed' : 'unchanged'} at ${root}${changed ? `; backup: ${backupRoot}` : ''}; cleared bundle cache: ${cacheRoot}; restart VS Code to rebuild it`);
|
|
180
185
|
return { extensionRoot: root, changed, backupRoot: changed ? backupRoot : undefined };
|
|
181
186
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
1
4
|
export const liteMetricsPlugin = 'opencode-metrics-plugin-lite';
|
|
2
5
|
export const remoteMetricsPlugin = 'opencode-metrics-plugin';
|
|
6
|
+
const metricsPluginCachePrefix = 'opencode-metrics-plugin';
|
|
3
7
|
export function updateMetricsPlugin(config, remote = false) {
|
|
4
8
|
const plugins = config.plugin ?? [];
|
|
5
9
|
if (!Array.isArray(plugins))
|
|
@@ -18,3 +22,53 @@ export function updateMetricsPlugin(config, remote = false) {
|
|
|
18
22
|
filtered[filtered.findIndex(isMetrics)] = selectedEntry;
|
|
19
23
|
config.plugin = filtered;
|
|
20
24
|
}
|
|
25
|
+
// OpenCode resolves npm plugins with Bun into XDG-style cache directories on every
|
|
26
|
+
// platform (no ~/Library/Caches on macOS, no %LocalAppData% on Windows).
|
|
27
|
+
export function metricsPluginCacheRoots(agent, home = os.homedir()) {
|
|
28
|
+
const children = ['packages', 'node_modules'];
|
|
29
|
+
if (agent === 'vscode') {
|
|
30
|
+
const cache = path.join(home, '.harmonybot', 'opencode', 'vscode', 'cache', 'opencode');
|
|
31
|
+
return children.map(child => path.join(cache, child));
|
|
32
|
+
}
|
|
33
|
+
const cacheHome = process.env.XDG_CACHE_HOME || path.join(home, '.cache');
|
|
34
|
+
const names = agent === 'deveco' ? ['opencode', 'deveco'] : ['opencode'];
|
|
35
|
+
return names.flatMap(name => children.map(child => path.join(cacheHome, name, child)));
|
|
36
|
+
}
|
|
37
|
+
export function findMetricsPluginCacheDirs(roots) {
|
|
38
|
+
const dirs = [];
|
|
39
|
+
for (const root of roots) {
|
|
40
|
+
let children;
|
|
41
|
+
try {
|
|
42
|
+
children = fs.readdirSync(root, { withFileTypes: true });
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error.code === 'ENOENT')
|
|
46
|
+
continue;
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
for (const child of children.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
50
|
+
if (child.isDirectory() && child.name.toLowerCase().startsWith(metricsPluginCachePrefix))
|
|
51
|
+
dirs.push(path.join(root, child.name));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return dirs;
|
|
55
|
+
}
|
|
56
|
+
export function clearMetricsPluginCache({ roots, dryRun = false, log = () => { } }) {
|
|
57
|
+
const deleted = [];
|
|
58
|
+
const failures = [];
|
|
59
|
+
for (const dir of findMetricsPluginCacheDirs(roots)) {
|
|
60
|
+
if (dryRun) {
|
|
61
|
+
log(`would remove metrics plugin cache: ${dir}`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
66
|
+
deleted.push(dir);
|
|
67
|
+
log(`cleared metrics plugin cache: ${dir}`);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
failures.push(`${dir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { deleted, failures };
|
|
74
|
+
}
|
package/package.json
CHANGED
|
@@ -7,6 +7,8 @@ description: Extract an auditable inventory.json containing a one-to-four-level
|
|
|
7
7
|
|
|
8
8
|
从“用户能看到并操作什么”出发重建功能树。不要把目录树、类名列表或字符串资源列表直接当成功能清单。
|
|
9
9
|
|
|
10
|
+
本 Skill 负责代码事实与功能清单,不处理迁移范围、架构选型或实施计划调整。仅用户选择变化时交调用方规划处理;补充分析时保留已有用户范围决定,不重新推算或覆盖 `should_develop`。
|
|
11
|
+
|
|
10
12
|
## 输入与默认值
|
|
11
13
|
|
|
12
14
|
收集以下输入;只有缺失项会实质改变结果时才询问:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: d2h-architecture-plan
|
|
3
3
|
description: >-
|
|
4
4
|
用于 Android 转 HarmonyOS 的架构评估、目录规划和方案选型。默认读取 Android 源码,交付原结构对照、鸿蒙优选、融合三套结构选项,详列推荐方案目录;
|
|
5
|
-
|
|
5
|
+
用户确认方案并要求落地时,优先复用 Preflight 已建基础工程、补齐所选目录;无基础工程时可创建。
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Android 源码 → 鸿蒙架构与目录规划
|
|
@@ -12,13 +12,14 @@ description: >-
|
|
|
12
12
|
## 输入与边界
|
|
13
13
|
|
|
14
14
|
- 必需输入只有 Android 源码根目录,可独立执行。按用户范围识别实际应用、运行时模块、技术栈和目标设备;源码缺失或目标能力未知时标明影响。
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
15
|
+
- 默认读取 Android 工程源码及其相关资源、构建声明;不自动读取鸿蒙业务源码,也不为寻找鸿蒙参考实现扩大扫描范围。上下文已指向鸿蒙目标时,按 [source-review.md](references/source-review.md) 做最小辨别,区分自动生成的模板壳、已有业务工程和性质未明的工程。
|
|
16
|
+
- 已确认仍为自动生成模板壳的工程(包括 Preflight 创建的工程)作为后续落地基础,不作为业务架构依据,不触发“是否结合现有鸿蒙工程”的询问。职责与目录由 Android 事实及目标要求决定,不能被空壳的默认结构限制。
|
|
17
|
+
- 确认鸿蒙工程已存在,且含业务实现或最小辨别后性质仍不明确,用户又尚未决定是否结合时,询问一次:“是否结合现有鸿蒙工程的业务实现进行架构规划?默认仅分析 Android 工程。”仅提供输出路径或配置中的目标路径不单独证明已有业务工程或用户选择结合。
|
|
18
|
+
- 用户明确要求结合/复用鸿蒙业务实现时,按约定范围读取;明确选择不结合时,只分析 Android。同一工程、同一任务范围内沿用已有明确选择,不重复询问;用户改变选择时据此调整。尚未答复期间可继续 Android 侧分析,不扩读鸿蒙业务源码,也不把沉默记为用户拒绝。复用基础工程、核对配置或创建目录的授权不自动扩大为分析已有业务实现。
|
|
18
19
|
- 已有功能清单、Facts、规划可作导航线索,用当前源码核验。
|
|
19
20
|
- 默认只交付架构 Markdown;不生成持久化的全量文件清单、源码快照、import/调用图或扫描基线。已有大型扫描产物按问题查询,不整体读入或为更新规划重新生成。
|
|
20
21
|
- 源工程只读;默认在同级 `<工程名>-migration/.migration/architecture-plan/` 交付。已有文件先阅读,保留用户批注,修订时同步受影响部分。
|
|
21
|
-
-
|
|
22
|
+
- 分析分支和目录组织依据本次读取范围内的工程事实与迁移目标确定,参考示例按实际需要选用。基础配置核对可用于工程复用判断;未读取的鸿蒙业务实现不作已核验的能力复用依据。
|
|
22
23
|
|
|
23
24
|
## 架构规划
|
|
24
25
|
|
|
@@ -30,6 +31,6 @@ description: >-
|
|
|
30
31
|
|
|
31
32
|
## 选型与可选落地
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
只请求分析/规划时,交付推荐和可选方案即完成本次规划,不为结束任务强求用户立即选型。用户选定后按意见修订、复核并记录最终方案。**落地或搬迁前须有对当前方案的实际确认及落地要求**;已有“采用推荐并创建”的明确授权则直接按授权执行,已有基础工程时在其上补齐目录,无须再次要求新建工程授权。
|
|
34
35
|
|
|
35
|
-
需要落地时才读 [directory-creation.md](references/directory-creation.md)
|
|
36
|
+
需要落地时才读 [directory-creation.md](references/directory-creation.md)。完整迁移优先沿用 Preflight 的目标根与基础工程,使用普通文件工具落地所选目录;确实没有基础工程时才使用本机 DevEco CLI 创建。不生成业务实现,不构建或运行应用。
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
# 用户确认后的目录落地
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
仅在用户要求落地/搬迁且已确认当前方案时读取。本阶段优先复用基础工程,用普通文件工具补齐关键目录;确实无基础工程时才使用本机 DevEco CLI 创建。不实现业务、不构建或运行应用。
|
|
4
4
|
|
|
5
5
|
## 1. 固定实际选择与目标
|
|
6
6
|
|
|
7
|
-
- 记录用户选择的方案ID
|
|
7
|
+
- 记录用户选择的方案ID、当前规划文件、修改意见和落地要求;明确推荐委托同样有效,不能自动把模型推荐当用户授权。
|
|
8
|
+
- 完整迁移核对调用方交接的目标根、`.migration/config.json` 的 `harmonyProject` 与 Preflight 已建工程位置,沿用同一个实际目标根。方案内的 `products/features/commons` 布局变化不另起工程;目标指向冲突时先查明,不能静默换路径再建一套。用户明确要求更换目标时,交由调用方通过现有 CLI 配置入口同步。
|
|
8
9
|
- 从选中方案推导目录清单,核对路径位于目标根内,无绝对子路径、上级跳转、大小写冲突或模块嵌套。不得跟随链接写入源/参考工程或目标根之外。
|
|
9
10
|
- 先检查目标实际状态和已有工程;保留用户文件。新增目录通常可直接执行;重用、移动或覆盖存在冲突的内容先查清,未授权覆盖则停止该动作并说明具体冲突。
|
|
10
11
|
- 目标设备/SDK与方案一致;未知设备能力不以默认phone掩盖,也不把所有Android设备名直接写入鸿蒙配置。
|
|
11
12
|
|
|
12
|
-
## 2.
|
|
13
|
+
## 2. 复用基础工程,缺失时才创建
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
已有基础工程(包括 Preflight 生成的模板壳)先核对根构建配置、注册模块及本地依赖,确认可承载所选目录;直接在原工程落地,不再次运行创建/初始化命令或复制一份模板。保留 CLI 模板和用户文件,不因模板布局、设备或 SDK 默认值与所选方案不同而另建工程,记录并处理必要的配置调整。
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
需要把模板 `entry` 放到选中产品路径时,先核对目标无冲突,再同步根注册 `srcPath`、相关本地依赖及测试路径。检查下游工具是否仍假设原模块位置;需要目录落地之外的工具改动时记录交接,不用第二份 `entry` 或第二套工程掩盖不兼容。无法确认安全时不移动,报告具体差异与必要改法。
|
|
18
|
+
|
|
19
|
+
仅当实际目标根尚无基础工程时,才按当前方案的确认与创建要求定位本机 DevEco CLI,阅读其帮助核对当前 create 命令、应用名、bundle、设备和输出路径等参数;独立使用本 Skill 同样先检查是否已有工程。目标非空但缺少基础配置时先报告冲突,不覆盖初始化;Preflight 记录与实际工程不一致时先查明原因。不把 Android 构建文件复制成鸿蒙配置;需要用户身份信息时只询问缺少的实际值。
|
|
17
20
|
|
|
18
21
|
创建/复用授权所需的目标配置核对、目录冲突检查与创建结果验证,不等于选择结合鸿蒙源码进行架构分析。若还需阅读现有业务实现来判断复用或调整架构,先按 [SKILL.md](../SKILL.md) 询问是否结合,已有明确选择则按其范围执行。
|
|
19
22
|
|
|
20
|
-
CLI
|
|
23
|
+
确需新建但 CLI 不可用或不支持目标设备时,保留已完成规划,给出实际错误及未完成创建项;普通空目录不能冒充可用鸿蒙基础工程。复用现有工程并补目录不以重新执行创建 CLI 为前提。不要为本阶段无限安装/重试工具或索要与创建无关的凭据。
|
|
21
24
|
|
|
22
25
|
## 3. 补齐选中目录
|
|
23
26
|
|
|
@@ -29,15 +32,17 @@ CLI不可用或不支持目标设备时,保留已完成规划,给出实际
|
|
|
29
32
|
|
|
30
33
|
命令成功后检查目标根基础配置、注册产品路径及关键目录确实存在,设备配置与选择一致,已有文件没有被覆盖;若执行中用户修改规划,先核对影响,不能按旧目录继续宣称完整。
|
|
31
34
|
|
|
35
|
+
结构或构建配置发生变化时,向调用方交接需要刷新的工程验证;初始 Preflight 构建结果不能证明调整后的布局已通过。本 Skill 不自行构建或运行应用。
|
|
36
|
+
|
|
32
37
|
## 4. 保存实际记录并交接
|
|
33
38
|
|
|
34
|
-
在规划产物目录写 `shell-receipt.json
|
|
39
|
+
在规划产物目录写 `shell-receipt.json`,记录可核验的实际落地结果,复用基础工程并补目录同样需要记录:
|
|
35
40
|
|
|
36
|
-
- `status`:complete / partial / blocked
|
|
41
|
+
- `status`:complete / partial / blocked,按真实落地结果填写。
|
|
37
42
|
- `planFile`、`proposalId`、`target`:当前规划、选中方案和实际目标根。
|
|
38
|
-
- `confirmation
|
|
39
|
-
- `baseProject
|
|
43
|
+
- `confirmation`:实际用户选择/推荐委托及对应修订,未确认不得执行落地。
|
|
44
|
+
- `baseProject`:新建或复用、工程来源与辨别依据、实际注册产品路径、设备类型及本阶段实际使用的 CLI/命令/日志位置。Preflight 已建工程记为复用;引用先前创建证据时注明来源,不冒充本阶段执行。
|
|
40
45
|
- `createdDirectories`、`existingDirectories`、`directoryOnlyModules`:分别记录新增、已存在和未注册的模块目录。
|
|
41
46
|
- `verification`、`remainingIssues`:实际检查结果、路径/配置差异和未完成项,不编造日志或检查。
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
落地完成只表示基础工程已创建或复用、所选目录壳子已落地。交接最终规划、receipt与未解决风险;其他两套保留为对照。模型生成的记录不能替代目标文件/目录和用户授权证据。
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 1. 共用一次项目理解
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
简述应用用途、原结构、目标范围及实际读取范围。已定位鸿蒙目标时,简记实际目标根、模板壳/已有业务/性质未明的判断及证据;模板壳仅作为落地基础。已有明确的结合/不结合鸿蒙业务实现选择一并简记,后续同范围修订沿用;未答复只记默认 Android,不记为拒绝。基础工程可复用与业务能力已实现分开表述,未读取的目标业务不作为已核验的能力复用依据。
|
|
8
8
|
|
|
9
9
|
把项目理解并入后面的归属表:源路径、职责/层次及必要证据。资源、配置、后台/外部入口和 Native/混合栈边界归入对应区域;事实、候选和未决分开。不另写长篇源码导读、完整阅读清单、文件统计或业务链报告。
|
|
10
10
|
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
- 已知运行时模块、主要职责与资源/非 UI 入口都有去向;未排除用户业务,三套范围一致。
|
|
43
43
|
- 树、映射、模块类型及主要依赖互相吻合,原包族保留承诺可定位,共享所有权唯一,无未解释的目标环或反向页面依赖。
|
|
44
44
|
- 方案不夹带未验证的平台替代/进程合并/功能裁剪结论;目录待决与实现前置分开,未读取鸿蒙源码时没有伪称目标已实现。
|
|
45
|
+
- 空模板的目录、模块数量和默认参数未被当成架构约束;已建基础工程的复用安排与所选目录一致,必须调整的配置或路径假设有交接说明。
|
|
45
46
|
- 融合分层与页面候选有证据,已知共同写入点有归属;没有将未细化的契约或目录壳子记为已可开工。
|
|
46
47
|
|
|
47
48
|
有矛盾时局部修订并补查相关证据,不重跑全仓分析。架构选择可以在保留实现风险的情况下完成;未知仍影响具体目录时明确条件分支或草案范围。
|
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
默认分析 Android,鸿蒙源码的读取选择遵循 [SKILL.md](../SKILL.md)。目标是确定模块/包族如何保留、业务放进哪些 feature、共享职责归谁以及主要依赖怎样走;不以读懂全部业务为前置。
|
|
4
4
|
|
|
5
|
+
## 鸿蒙目标的最小辨别
|
|
6
|
+
|
|
7
|
+
只辨别上下文或迁移配置已指向的目标,不遍历同级或嵌套工程寻找参考实现。优先使用本次 Preflight 的创建结果、已有命令日志和用户说明;必要时仅核对根构建配置、模块注册及样板入口的局部内容。发现业务定制即停止辨别,不顺带追踪其实现。
|
|
8
|
+
|
|
9
|
+
| 当前性质 | 判断依据 | 对规划的影响 |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| 自动生成的模板壳 | 有可信生成来源,且当前仍仅含模板、初始化配置和测试骨架 | 不询问是否结合业务实现;以 Android 事实和目标要求独立设计,确认后复用基础工程 |
|
|
12
|
+
| 已有业务工程 | 已含迁移成果或定制的页面、状态、服务等业务实现,无论最初是否由 Preflight 创建 | 按已明确的结合范围读取;尚未决定时按 SKILL.md 询问,不擅自裁剪或搬迁已有业务 |
|
|
13
|
+
| 性质未明 | 工程确已存在,但生成来源或当前修改情况不足以确认仅为模板壳 | 保留未知,按 SKILL.md 询问结合范围;可继续 Android 分析,不为分类全量扫描鸿蒙源码 |
|
|
14
|
+
|
|
15
|
+
工程路径、生成标记或一次 Preflight 通过不能单独证明当前仍为空壳;历史新建记录不能覆盖后续业务修改。没有已存在目标的证据时,直接分析 Android,不为此触发结合询问。
|
|
16
|
+
|
|
17
|
+
模板的 `entry`、目录命名、模块数量及默认设备/SDK 参数只是生成现状,不代表用户已选架构或产品约束。三套方案仍按 Android 职责和目标要求设计;真实的平台约束单独核实,必要的模板配置调整留到确认后的落地,不为适应空壳缩减或重组业务范围。
|
|
18
|
+
|
|
5
19
|
## 1. 先建立结构地图
|
|
6
20
|
|
|
7
21
|
优先定位下列结构证据,按需读相关片段;同一地图支撑三套方案。
|
|
@@ -27,7 +27,7 @@ version: 1.3.0
|
|
|
27
27
|
|
|
28
28
|
| 环节 | 触发条件 | 查什么 |
|
|
29
29
|
|------|----------|--------|
|
|
30
|
-
|
|
|
30
|
+
| Spec 行为或平台能力核验 | spec 标记了平台能力项(相机/定位/文件/通知/蓝牙/分享/Web/后台任务等) | 该能力对应 Kit/API 是否可用、是否受系统能力限制,写入 spec 依据 |
|
|
31
31
|
| 页面实现前(`d2h-spec-develop` 步骤 1-2) | 功能清单(F-xxx)中存在平台能力或不确定的 @kit.* Kit / @ohos.* 模块 API | API 签名、参数/返回值、所需权限、错误码、推荐用法与指南 |
|
|
32
32
|
| 页面实现中 | 不确定 ArkUI 组件/属性行为(如 Tabs、Swiper、手势、懒加载) | 开发指南与 FAQ 中的权威行为描述 |
|
|
33
33
|
| 构建失败分诊 | 编译错误指向 @kit.* / @ohos.* API 签名、类型、import 模块名 | 核对该 API 的真实签名与起始版本,避免臆造签名反复试错 |
|
|
@@ -14,43 +14,46 @@ npx --yes d2h-dashboard@latest --dataDir <项目>/.migration [--port 8080] [--ho
|
|
|
14
14
|
- `--dataDir` 必填,指向迁移工作区 `.migration`;`--port` 默认 8080,占用自动 +1 重试至 8090;`--host` 默认 `127.0.0.1`。
|
|
15
15
|
- 启动链:npx 拉包并执行 bin → `node …/server/serve.mjs` 常驻,同时托管前端与 `/api/data/*`。
|
|
16
16
|
- 成功后 stdout 固定两行,实际 URL 以第二行为准:`[dashboard] dataDir=<绝对路径>`、`[dashboard] http://<host>:<实际端口>`。
|
|
17
|
-
- 健康检查:`GET /api/data/manifest` 返回 200 且 `root` 等于传入 dataDir(绝对路径规范化比较)。
|
|
18
17
|
|
|
19
18
|
## 自动启动(迁移会话内)
|
|
20
19
|
|
|
21
20
|
- `.migration/progress.json` 出现(`progress init` 后)或恢复会话已存在 → 自动启动看板并用默认浏览器打开 URL,不等用户请求;每轮会话执行一次,复用实例不重复启动、不重复打开。
|
|
22
21
|
- 启动或打开失败只记录不阻塞迁移,下次阶段更新重试一次。
|
|
23
22
|
|
|
24
|
-
##
|
|
23
|
+
## 后台启动与验证(必做)
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
用 `--detach` 自守护启动,一条命令完成(服务器常驻、日志按项目隔离、父进程就绪后自行退出),无需任何 cmd 包装或 shell 重定向:
|
|
27
26
|
|
|
28
|
-
```
|
|
29
|
-
#
|
|
30
|
-
|
|
27
|
+
```powershell
|
|
28
|
+
# Windows:npx 必须用 npx.cmd(npx.ps1 被执行策略拦截)
|
|
29
|
+
npx.cmd --yes d2h-dashboard@latest --dataDir "$dataDir" --detach
|
|
31
30
|
```
|
|
32
31
|
|
|
33
|
-
```
|
|
34
|
-
#
|
|
35
|
-
|
|
36
|
-
$log = "$env:TEMP\d2h-dashboard.log"
|
|
37
|
-
$arg = "/c npx.cmd --yes d2h-dashboard@latest --dataDir `"$dataDir`" > `"$log`" 2> `"$log.err`""
|
|
38
|
-
Start-Process -FilePath cmd.exe -ArgumentList $arg -WindowStyle Hidden | Out-Null
|
|
39
|
-
Start-Sleep -Seconds 2; Get-Content $log -ErrorAction SilentlyContinue
|
|
32
|
+
```bash
|
|
33
|
+
# bash(macOS/Linux)
|
|
34
|
+
npx --yes d2h-dashboard@latest --dataDir "$DATA_DIR" --detach
|
|
40
35
|
```
|
|
41
36
|
|
|
42
|
-
|
|
37
|
+
事实源铁律:**实例唯一事实源 = 端口探测的 manifest root;日志只是辅助排查,禁止用日志内容判定实例归属或端口。** 启动后必须轮询验证成功,禁止只看输出就宣布就绪:
|
|
43
38
|
|
|
44
39
|
```powershell
|
|
45
|
-
|
|
40
|
+
$deadline = (Get-Date).AddSeconds(20); $url = $null
|
|
41
|
+
do {
|
|
42
|
+
Start-Sleep -Milliseconds 500
|
|
43
|
+
foreach ($p in 8080..8090) {
|
|
44
|
+
try { $j = (Invoke-WebRequest "http://127.0.0.1:$p/api/data/manifest" -TimeoutSec 1 -UseBasicParsing).Content | ConvertFrom-Json
|
|
45
|
+
if ($j.root -eq $dataDir) { $url = "http://127.0.0.1:$p"; break } } catch {}
|
|
46
|
+
}
|
|
47
|
+
} while (-not $url -and (Get-Date) -lt $deadline)
|
|
46
48
|
```
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
未命中 → 读 stdout 里的 `log=…` 路径尾部定位原因,重试一次;仍失败只记录不阻塞。
|
|
51
|
+
|
|
52
|
+
确认 `$url` 后打开浏览器(Windows 必须 `cmd /c start "" "<url>"`,`Start-Process "<url>"` 静默不弹窗;macOS `open`、Linux `xdg-open`)。
|
|
53
|
+
|
|
54
|
+
**URL 告知规范**:URL 单独成行或用反引号包裹,其后不得紧跟任何文字(含中文括号说明)——否则 markdown 会把 URL+后缀渲染成单个失效链接,说明放下一行。
|
|
52
55
|
|
|
53
56
|
## 复用与停止
|
|
54
57
|
|
|
55
|
-
- **复用**:启动前探测 8080–8090 `GET /api/data/manifest`(1s 超时),`root` == 目标 dataDir 且 `GET /` 返回 200 才复用该 URL;仅 root 匹配但 `GET /` 非 200
|
|
58
|
+
- **复用**:启动前探测 8080–8090 `GET /api/data/manifest`(1s 超时),`root` == 目标 dataDir 且 `GET /` 返回 200 才复用该 URL;仅 root 匹配但 `GET /` 非 200 属损坏实例(资产被删/版本切换残留),按停止规则杀掉后重新启动。root 不匹配跳过(其他流程的服务器)。服务器常驻,会话结束不回收,残留进程属预期。
|
|
56
59
|
- **停止**:仅用户显式要求(「关闭看板」「关闭 XX 项目看板」)才执行,且必须 root 归属校验通过:Windows `Get-NetTCPConnection -LocalPort <port> -State Listen | ForEach-Object { Stop-Process -Id $_.OwningProcess }`(杀 node 真实 PID,cmd 包装 PID 无效);bash `kill <pid>`。未找到 → 报告未在运行;root 不匹配 → 跳过并说明属于其他流程。
|