@harmonyos-arkts/d2h 0.1.26 → 0.1.28

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 (52) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/AGENTS.md +2 -0
  3. package/README.md +34 -2
  4. package/agents/d2h.md +24 -23
  5. package/dist/bin/install-opencode.js +36 -24
  6. package/dist/src/install-codex.js +5 -126
  7. package/dist/src/install-skills.js +147 -0
  8. package/dist/src/install-tui-extension.js +133 -0
  9. package/dist/src/install-vscode.js +2 -2
  10. package/dist/src/toolchain.js +2 -2
  11. package/docs/environment-check.md +3 -3
  12. package/package.json +6 -6
  13. package/skills/arkts-mvvm-architecture/SKILL.md +2 -0
  14. package/skills/d2h-architecture-plan/SKILL.md +21 -19
  15. package/skills/d2h-architecture-plan/references/architecture-plan.example.json +122 -0
  16. package/skills/d2h-architecture-plan/references/directory-creation.md +27 -29
  17. package/skills/d2h-architecture-plan/references/hybrid-design.md +13 -15
  18. package/skills/d2h-architecture-plan/references/json-contract.md +49 -0
  19. package/skills/d2h-architecture-plan/references/layout-reference.md +30 -27
  20. package/skills/d2h-architecture-plan/references/plan-contract.md +27 -32
  21. package/skills/d2h-architecture-plan/references/source-review.md +1 -1
  22. package/skills/d2h-architecture-plan/scripts/architecture_plan.py +147 -0
  23. package/skills/d2h-architecture-plan/scripts/test_architecture_plan.py +241 -0
  24. package/skills/d2h-dashboard/SKILL.md +12 -1
  25. package/skills/d2h-spec-develop/SKILL.md +52 -42
  26. package/skills/d2h-spec-generate/SKILL.md +30 -29
  27. package/skills/d2h-spec-plan/SKILL.md +20 -16
  28. package/skills/d2h-spec-plan/agents/openai.yaml +3 -3
  29. package/skills/d2h-spec-plan/references/plan-contract.md +32 -7
  30. package/skills/d2h-spec-plan/references/planning-method.md +12 -62
  31. package/skills/d2h-spec-plan/references/planning-review.md +9 -21
  32. package/skills/d2h-spec-plan/references/round-scope-assessment.md +12 -39
  33. package/skills/d2h-spec-plan/references/work-packets.md +6 -28
  34. package/{install-opencode.sh → tooling/install-opencode.sh} +3 -3
  35. package/{opencode → tooling/opencode}/interactive-tools/advance-progress-demo.mjs +1 -1
  36. package/{opencode → tooling/opencode}/interactive-tools/demo.mjs +3 -3
  37. package/{opencode → tooling/opencode}/interactive-tools/prepare-demo.mjs +1 -1
  38. package/{scripts → tooling/scripts}/check-environment.mjs +3 -3
  39. /package/{opencode → tooling/opencode}/agents.json +0 -0
  40. /package/{opencode → tooling/opencode}/interactive-tools/browser-host.ts +0 -0
  41. /package/{opencode → tooling/opencode}/interactive-tools/core.ts +0 -0
  42. /package/{opencode → tooling/opencode}/interactive-tools/demo-workflow.ts +0 -0
  43. /package/{opencode → tooling/opencode}/interactive-tools/package-lock.json +0 -0
  44. /package/{opencode → tooling/opencode}/interactive-tools/package.json +0 -0
  45. /package/{opencode → tooling/opencode}/interactive-tools/panel.ts +0 -0
  46. /package/{opencode → tooling/opencode}/interactive-tools/prepare-progress-demo.mjs +0 -0
  47. /package/{opencode → tooling/opencode}/interactive-tools/progress.ts +0 -0
  48. /package/{opencode → tooling/opencode}/interactive-tools/server-legacy.mjs +0 -0
  49. /package/{opencode → tooling/opencode}/interactive-tools/server.mjs +0 -0
  50. /package/{opencode → tooling/opencode}/interactive-tools/tui.tsx +0 -0
  51. /package/{opencode → tooling/opencode}/vscode-d2h-package.json +0 -0
  52. /package/{opencode → tooling/opencode}/vscode-d2h-plugin.mjs +0 -0
@@ -0,0 +1,133 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { run } from './toolchain.js';
5
+ import { locateVSCodeExtension } from './install-vscode.js';
6
+ export const tuiExtension = { name: 'opencode-tui-extension', version: '0.1.1' };
7
+ function bundledOpenCodeBinary(extensionRoot) {
8
+ const executable = process.platform === 'win32' ? 'opencode.exe' : 'opencode';
9
+ return path.join(extensionRoot, 'resources', 'opencode', 'bin', process.platform, process.arch, executable);
10
+ }
11
+ function majorVersion(output) {
12
+ const major = Number(output.match(/(\d+)\.\d+\.\d+/)?.[1]);
13
+ return Number.isInteger(major) && major > 0 ? major : null;
14
+ }
15
+ function bundledMajorVersion(binary, runner) {
16
+ const version = runner(binary, ['--version']);
17
+ if (version.status === 0) {
18
+ const major = majorVersion(`${version.stdout}\n${version.stderr}`);
19
+ if (major === 1 || major === 2)
20
+ return major;
21
+ }
22
+ const help = runner(binary, ['--help']);
23
+ const text = `${help.stdout}\n${help.stderr}`;
24
+ if (help.status !== 0 || !/Commands:/.test(text))
25
+ return null;
26
+ return /^\s*USAGE\b/m.test(text) ? 2 : 1;
27
+ }
28
+ export function installTuiExtension({ dryRun = false, force = false, extensionRoot, runner = run, log = console.log } = {}) {
29
+ const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
30
+ const globalRoot = runner(npm, ['root', '--global']);
31
+ if (globalRoot.status !== 0) {
32
+ log(`warning: cannot inspect global npm root; skipped ${tuiExtension.name}`);
33
+ return;
34
+ }
35
+ const linkedPath = path.join(globalRoot.stdout.trim(), tuiExtension.name);
36
+ const linked = fs.existsSync(linkedPath) && fs.lstatSync(linkedPath).isSymbolicLink();
37
+ if (linked && !force) {
38
+ log(`preserved npm link ${tuiExtension.name} at ${linkedPath}`);
39
+ }
40
+ else {
41
+ const coordinate = `${tuiExtension.name}@${tuiExtension.version}`;
42
+ if (dryRun) {
43
+ log(`would install ${coordinate}`);
44
+ return;
45
+ }
46
+ const install = runner(npm, ['install', '--global', coordinate]);
47
+ if (install.status !== 0) {
48
+ log(`warning: failed to install ${coordinate}: ${install.stderr || install.error?.message || 'unknown error'}`);
49
+ return;
50
+ }
51
+ log(`installed ${coordinate}`);
52
+ }
53
+ let setupArgs = ['setup'];
54
+ let env;
55
+ if (extensionRoot) {
56
+ const configDir = path.join(extensionRoot, 'resources', 'opencode');
57
+ const binary = bundledOpenCodeBinary(extensionRoot);
58
+ if (!fs.existsSync(binary)) {
59
+ log(`warning: bundled opencode binary not found: ${binary}; skipped ${tuiExtension.name} registration`);
60
+ return;
61
+ }
62
+ const major = bundledMajorVersion(binary, runner);
63
+ if (major !== 1 && major !== 2) {
64
+ log(`warning: cannot detect bundled opencode version at ${binary}; skipped ${tuiExtension.name} registration`);
65
+ return;
66
+ }
67
+ setupArgs = ['setup', major === 1 ? '--v1' : '--v2'];
68
+ env = { ...process.env, OPENCODE_CONFIG_DIR: configDir };
69
+ if (dryRun) {
70
+ log(`would register ${tuiExtension.name} for bundled opencode v${major} into ${configDir}`);
71
+ return;
72
+ }
73
+ }
74
+ else if (dryRun) {
75
+ log(`would register ${tuiExtension.name} (setup)`);
76
+ return;
77
+ }
78
+ const bin = process.platform === 'win32' ? `${tuiExtension.name}.cmd` : tuiExtension.name;
79
+ const setup = runner(bin, setupArgs, env ? { env } : undefined);
80
+ if (setup.status !== 0) {
81
+ log(`warning: ${tuiExtension.name} setup failed: ${setup.stderr || 'unknown error'}`);
82
+ return;
83
+ }
84
+ log(`registered ${tuiExtension.name} (TUI status banner)${extensionRoot ? ` into ${path.join(extensionRoot, 'resources', 'opencode')}` : ''}; restart to activate`);
85
+ }
86
+ function readJsonIfPresent(file) {
87
+ try {
88
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
89
+ }
90
+ catch {
91
+ return undefined;
92
+ }
93
+ }
94
+ function registeredIn(config, key) {
95
+ return Array.isArray(config?.[key]) && config[key].some(entry => String(entry).includes(tuiExtension.name));
96
+ }
97
+ export function inspectTuiExtension({ agent, extensionDir, runner = run }) {
98
+ const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
99
+ const globalRoot = runner(npm, ['root', '--global']);
100
+ const installedDir = globalRoot.status === 0 ? path.join(globalRoot.stdout.trim(), tuiExtension.name) : undefined;
101
+ const manifest = installedDir ? readJsonIfPresent(path.join(installedDir, 'package.json')) : undefined;
102
+ const installedVersion = typeof manifest?.version === 'string' ? manifest.version : undefined;
103
+ if (!installedVersion)
104
+ return { status: 'warning', summary: 'not installed globally; rerun d2h install' };
105
+ if (agent === 'opencode') {
106
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'opencode');
107
+ const where = [
108
+ registeredIn(readJsonIfPresent(path.join(configDir, 'tui.json')), 'plugin') ? 'tui.json' : undefined,
109
+ registeredIn(readJsonIfPresent(path.join(configDir, 'cli.json')), 'plugins') ? 'cli.json' : undefined,
110
+ ].filter(Boolean);
111
+ if (!where.length)
112
+ return { status: 'warning', summary: `${installedVersion} installed but not registered under ${configDir}; rerun d2h install` };
113
+ return { status: 'passed', summary: `${installedVersion} registered (${where.join(', ')})` };
114
+ }
115
+ try {
116
+ const root = locateVSCodeExtension(extensionDir);
117
+ const binary = bundledOpenCodeBinary(root);
118
+ if (!fs.existsSync(binary))
119
+ return { status: 'warning', summary: `${installedVersion} installed but bundled opencode binary missing: ${binary}` };
120
+ const major = bundledMajorVersion(binary, runner);
121
+ if (major !== 1 && major !== 2)
122
+ return { status: 'warning', summary: `${installedVersion} installed but bundled opencode version undetected` };
123
+ const file = major === 1 ? 'tui.json' : 'cli.json';
124
+ const configDir = path.join(root, 'resources', 'opencode');
125
+ if (!registeredIn(readJsonIfPresent(path.join(configDir, file)), major === 1 ? 'plugin' : 'plugins')) {
126
+ return { status: 'warning', summary: `${installedVersion} installed but not registered in ${path.join(configDir, file)}; rerun d2h install` };
127
+ }
128
+ return { status: 'passed', summary: `${installedVersion} registered for bundled opencode v${major} (${file})` };
129
+ }
130
+ catch (error) {
131
+ return { status: 'warning', summary: `${installedVersion} installed but VS Code extension not found: ${error instanceof Error ? error.message : String(error)}` };
132
+ }
133
+ }
@@ -133,8 +133,8 @@ export function installVSCode({ sourceDir, extensionDir, bundleCacheRoot, plugin
133
133
  { source: path.join(sourceRoot, 'skills'), destination: path.join(pluginRoot, 'skills') },
134
134
  { source: path.join(sourceRoot, 'agents'), destination: path.join(pluginRoot, 'agents') },
135
135
  { source: path.join(sourceRoot, 'AGENTS.md'), destination: path.join(pluginRoot, 'AGENTS.md') },
136
- { source: path.join(sourceRoot, 'opencode', 'vscode-d2h-plugin.mjs'), destination: path.join(pluginRoot, 'src', 'index.js') },
137
- { source: path.join(sourceRoot, 'opencode', 'vscode-d2h-package.json'), destination: path.join(pluginRoot, 'package.json') },
136
+ { source: path.join(sourceRoot, 'tooling', 'opencode', 'vscode-d2h-plugin.mjs'), destination: path.join(pluginRoot, 'src', 'index.js') },
137
+ { source: path.join(sourceRoot, 'tooling', 'opencode', 'vscode-d2h-package.json'), destination: path.join(pluginRoot, 'package.json') },
138
138
  ];
139
139
  for (const entry of copies)
140
140
  if (!fs.existsSync(entry.source))
@@ -18,7 +18,7 @@ export function loadToolchain(root) {
18
18
  }
19
19
  return value;
20
20
  }
21
- export const run = (command, args) => {
21
+ export const run = (command, args, options) => {
22
22
  let executable = command;
23
23
  let parameters = args;
24
24
  if (process.platform === 'win32') {
@@ -28,7 +28,7 @@ export const run = (command, args) => {
28
28
  executable = 'powershell.exe';
29
29
  parameters = ['-NoProfile', '-NonInteractive', '-EncodedCommand', Buffer.from(script, 'utf16le').toString('base64')];
30
30
  }
31
- const result = spawnSync(executable, parameters, { encoding: 'utf8', windowsHide: true, timeout: 300000 });
31
+ const result = spawnSync(executable, parameters, { encoding: 'utf8', windowsHide: true, timeout: 300000, env: options?.env, cwd: options?.cwd });
32
32
  return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '', error: result.error };
33
33
  };
34
34
  function version(output) { return output.match(/\b\d+\.\d+\.\d+\b/)?.[0] ?? null; }
@@ -11,11 +11,11 @@ npx --yes @harmonyos-arkts/d2h@0.1.5 --check-env --output-dir ./environment-repo
11
11
  本地构建后:`node dist/bin/install-opencode.js --check-env`。检查参数在 --check-env 后传入;使用 --check-env --help 查看。检查模式不执行 Agent/Skill 安装;不能与 --force、--agent、--config-dir 等安装参数混用,误用时返回参数错误。npx 自身可能下载并缓存 npm 包。
12
12
 
13
13
  ```powershell
14
- node scripts/check-environment.mjs --output-dir D:/reports/hmos-environment
14
+ node tooling/scripts/check-environment.mjs --output-dir D:/reports/hmos-environment
15
15
  ```
16
16
 
17
17
  ```sh
18
- node scripts/check-environment.mjs --output-dir ~/reports/hmos-environment
18
+ node tooling/scripts/check-environment.mjs --output-dir ~/reports/hmos-environment
19
19
  ```
20
20
 
21
21
  ## 当前检查项
@@ -99,5 +99,5 @@ npx --yes @harmonyos-arkts/d2h@0.1.5 --check-env --requirements ./environment-re
99
99
  测试:
100
100
 
101
101
  ```sh
102
- node --test --test-concurrency=1 scripts/tests/environment.test.mjs
102
+ node --test --test-concurrency=1 tooling/scripts/tests/environment.test.mjs
103
103
  ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@harmonyos-arkts/d2h",
3
- "version": "0.1.26",
4
- "description": "Install Android-to-HarmonyOS migration agents and skills into OpenCode or DevEco Code",
3
+ "version": "0.1.28",
4
+ "description": "Install Android-to-HarmonyOS migration agents and skills into AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "d2h": "dist/bin/install-opencode.js"
@@ -15,10 +15,10 @@
15
15
  "schemas/",
16
16
  "workflow/",
17
17
  "contracts/",
18
- "opencode/",
18
+ "tooling/opencode/",
19
19
  ".claude-plugin/",
20
- "install-opencode.sh",
21
- "scripts/check-environment.mjs",
20
+ "tooling/install-opencode.sh",
21
+ "tooling/scripts/check-environment.mjs",
22
22
  "docs/environment-check.md",
23
23
  "docs/incremental-spec-evaluation-design.md",
24
24
  "docs/spec-plan-guide.md",
@@ -32,7 +32,7 @@
32
32
  "scripts": {
33
33
  "build": "tsc -p tsconfig.json",
34
34
  "check": "tsc --noEmit && npm run build && node dist/scripts/check-skill-refs.js && node dist/scripts/check-toolchain.js",
35
- "test": "npm run build && node --test dist/test/*.test.js scripts/tests/*.test.mjs",
35
+ "test": "npm run build && node --test dist/test/*.test.js tooling/scripts/tests/*.test.mjs",
36
36
  "prepack": "npm run build",
37
37
  "prepublishOnly": "npm run check && npm test"
38
38
  },
@@ -20,6 +20,8 @@ tags: [arkts, mvvm, state-management-v2, navigation, architecture, harmonyos]
20
20
 
21
21
  ## 1. 目录结构规范
22
22
 
23
+ 优先遵循工程已有的目录、模块归属和共享边界,在其内部落实 MVVM 分层。以下目录树和模块拆分建议供新建工程或尚无明确约定的部分参考,不要求现有工程按示例重组。
24
+
23
25
  按「功能 + 分层」组织。`core/` 放跨模块共享资产,业务按 feature 内聚。
24
26
 
25
27
  ```
@@ -1,36 +1,38 @@
1
1
  ---
2
2
  name: d2h-architecture-plan
3
3
  description: >-
4
- 用于 Android 转 HarmonyOS 的架构评估、目录规划和方案选型。默认读取 Android 源码,交付原结构对照、鸿蒙优选、融合三套结构选项,详列推荐方案目录;
5
- 用户确认方案并要求落地时,优先复用 Preflight 已建基础工程、补齐所选目录;无基础工程时可创建。
4
+ 用于 Android 转 HarmonyOS 的目录级架构评估、三方案比较与选型。确认后交付规划 Markdown 和最终目录 JSON;
5
+ 按授权创建或复用鸿蒙基础工程、补齐关键目录。
6
6
  ---
7
7
 
8
8
  # Android 源码 → 鸿蒙架构与目录规划
9
9
 
10
- 基于 Android 工程结构与必要源码,为迁移确定职责归属、依赖方向和可落地的鸿蒙目录,兼顾原开发者维护便利性。默认做目录级架构分析,不开展完整业务理解、逐页行为分析或实现级迁移设计。
10
+ 基于 Android 工程结构与必要源码,确定职责归属、依赖方向和可落地的鸿蒙目录,兼顾原开发者维护便利性。默认做目录级架构分析,不开展完整业务理解、逐页行为分析或实现级迁移设计。
11
11
 
12
12
  ## 输入与边界
13
13
 
14
14
  - 必需输入只有 Android 源码根目录,可独立执行。按用户范围识别实际应用、运行时模块、技术栈和目标设备;源码缺失或目标能力未知时标明影响。
15
- - 默认读取 Android 工程源码及其相关资源、构建声明;不自动读取鸿蒙业务源码,也不为寻找鸿蒙参考实现扩大扫描范围。上下文已指向鸿蒙目标时,按 [source-review.md](references/source-review.md) 做最小辨别,区分自动生成的模板壳、已有业务工程和性质未明的工程。
16
- - 已确认仍为自动生成模板壳的工程(包括 Preflight 创建的工程)作为后续落地基础,不作为业务架构依据,不触发“是否结合现有鸿蒙工程”的询问。职责与目录由 Android 事实及目标要求决定,不能被空壳的默认结构限制。
17
- - 确认鸿蒙工程已存在,且含业务实现或最小辨别后性质仍不明确,用户又尚未决定是否结合时,询问一次:“是否结合现有鸿蒙工程的业务实现进行架构规划?默认仅分析 Android 工程。”仅提供输出路径或配置中的目标路径不单独证明已有业务工程或用户选择结合。
18
- - 用户明确要求结合/复用鸿蒙业务实现时,按约定范围读取;明确选择不结合时,只分析 Android。同一工程、同一任务范围内沿用已有明确选择,不重复询问;用户改变选择时据此调整。尚未答复期间可继续 Android 侧分析,不扩读鸿蒙业务源码,也不把沉默记为用户拒绝。复用基础工程、核对配置或创建目录的授权不自动扩大为分析已有业务实现。
19
- - 已有功能清单、Facts、规划可作导航线索,用当前源码核验。
20
- - 默认只交付架构 Markdown;不生成持久化的全量文件清单、源码快照、import/调用图或扫描基线。已有大型扫描产物按问题查询,不整体读入或为更新规划重新生成。
21
- - 源工程只读;默认在同级 `<工程名>-migration/.migration/architecture-plan/` 交付。已有文件先阅读,保留用户批注,修订时同步受影响部分。
22
- - 分析分支和目录组织依据本次读取范围内的工程事实与迁移目标确定,参考示例按实际需要选用。基础配置核对可用于工程复用判断;未读取的鸿蒙业务实现不作已核验的能力复用依据。
15
+ - 默认读取 Android 源码及相关资源、构建声明。上下文已指向鸿蒙目标时,按 [source-review.md](references/source-review.md) 辨别其性质:模板壳仅作落地基础,无需询问是否结合业务;已有业务或性质未明且用户尚未决定时,询问是否结合其业务实现,默认仅分析 Android。
16
+ - 沿用同工程、同范围的明确选择;等待答复时可继续 Android 分析,不扩读鸿蒙业务。配置核对与目录创建不自动扩大为业务分析;模板的默认目录不限制架构设计。
17
+ - 已有功能清单、Facts、规划可作定位线索,用当前源码核验。不生成全量文件清单、源码快照、import/调用图或扫描基线;已有大型产物按问题查询。
18
+ - 源工程只读;默认在同级 `<工程名>-migration/.migration/architecture-plan/` 交付。已有文件先阅读,保留用户批注,修订受影响部分。
23
19
 
24
- ## 架构规划
20
+ ## 规划 → 确认 → 展示
25
21
 
26
- 1. 按 [source-review.md](references/source-review.md) 从构建声明、入口/导航和主要源码目录建立结构地图,识别基础依赖、页面业务、共享业务及装配。仅当证据不足会改变拆合、归属或依赖方向时,定点读取相关源码;边界可解释后停止扩读。
27
- 2. 按 [layout-reference.md](references/layout-reference.md) 设计原结构对照 `android-mirror`、鸿蒙优选 `harmony-practice`、融合 `hybrid`,共用范围地图;已有选定方案时只修订它及受影响的对照。融合结构以基础依赖保留原模块/包族、业务按 feature 承载页面为原则,需要详细展开时读 [hybrid-design.md](references/hybrid-design.md),不展开逐页实施计划。
28
- 3. 按 [plan-contract.md](references/plan-contract.md) 详细展开推荐方案的关键目录、归属与主要依赖,另两套给结构差异对照;已有选定方案则围绕它展开。用户明确要求三套完整目录时再全部展开,共享事实仍只写一次。复核目录/归属/依赖一致性,不重复业务链或完整分析报告。
22
+ 1. 按 [source-review.md](references/source-review.md) 从构建声明、入口/导航和主要源码目录建立结构地图,识别基础依赖、页面业务、共享业务及装配。仅当证据不足会改变拆合、归属或依赖时扩读;边界可解释后停止。
23
+ 2. 按 [layout-reference.md](references/layout-reference.md) 设计原结构对照 `android-mirror`、鸿蒙优选 `harmony-practice`、融合 `hybrid`,共用范围地图。在对话中展示三套目录结构、职责、取舍及推荐理由,供用户判断,暂不生成交付文件。融合方案设计时读 [hybrid-design.md](references/hybrid-design.md)。已有有效选择时围绕它修订,不重新强制选型。
24
+ 3. 用可用的 question 工具或对话询问方案选择,三个选项对应上述方案并标识推荐,说明本次只选型还是确认后落地。只接受实际回答,预选项、取消或超时不算选择;先处理修改意见,仅询问仍缺失的用户决定。技术核验由 Agent 完成。已有当前有效选择和授权直接沿用,不重复询问;仅比较、尚未选型时在对话中交付分析,不生成最终 JSON/Markdown。
25
+ 4. 用户回答完成后,按 [plan-contract.md](references/plan-contract.md) 生成 `architecture-plan.md`,保存三套目录及实际选型/授权;按 [json-contract.md](references/json-contract.md) 生成最终 `architecture-plan.json`,只含 `projectName`、`directoryTree`、`decisions`。两份产物使用同一组已确认的路径与决定;JSON 先在临时文件中生成,校验通过后替换正式文件,再交给只读看板。任一产物保存失败或不一致时,先修复,不进入实施。
26
+ 5. 有创建/复用授权时,读取 [directory-creation.md](references/directory-creation.md),重新读取最终 JSON 和 Markdown 确认记录后实施。完整迁移已有创建要求并确认方案时直接继续;仅选型不扩大为创建授权。
29
27
 
30
- 本 Skill 交付架构与转换交接设计;并行候选表示具备独立转换的架构条件,实际派发、开发和验收由调用方的执行流程决定。
28
+ 校验命令:`python <本Skill目录>/scripts/architecture_plan.py validate <产物目录>/architecture-plan.json`。辅助脚本要求 Python 3.10+,只校验展示数据,不处理选择、写入文件或创建工程。
31
29
 
32
- ## 选型与可选落地
30
+ ## 交付与修订
33
31
 
34
- 只请求分析/规划时,交付推荐和可选方案即完成本次规划,不为结束任务强求用户立即选型。用户选定后按意见修订、复核并记录最终方案。**落地或搬迁前须有对当前方案的实际确认及落地要求**;已有“采用推荐并创建”的明确授权则直接按授权执行,已有基础工程时在其上补齐目录,无须再次要求新建工程授权。
32
+ `architecture-plan.md` 保存项目理解、三套对照、推荐、用户回答与范围、最终归属/依赖及风险;`architecture-plan.json` 是确认后生成的最终展示和目录实施输入,两者应一致。
35
33
 
36
- 需要落地时才读 [directory-creation.md](references/directory-creation.md)。完整迁移优先沿用 Preflight 的目标根与基础工程,使用普通文件工具落地所选目录;确实没有基础工程时才使用本机 DevEco CLI 创建。不生成业务实现,不构建或运行应用。
34
+ 用户通过对话修改时同步两份产物。影响目录、依赖、目标或授权范围时,先将旧展示 JSON 移为不覆盖历史文件的备份,注明待修订并暂停受影响的实施,处理后重新校验发布;明确授权的调整不再重复确认。缺少当前有效确认的旧产物不作为最终结果继续展示。
35
+
36
+ 恢复实施须重读 Markdown 的实际确认与最终 JSON,核对项目、目标和目录;文件存在或校验通过不代表用户授权。看板只展示最终 JSON,加载、刷新及修订期间的缓存处理由调用方适配。
37
+
38
+ 本 Skill 交付架构与转换交接设计。页面族是候选,目录壳子不是已实现能力;实际派发、开发和验收由调用方流程决定。
@@ -0,0 +1,122 @@
1
+ {
2
+ "projectName": "示例邮件客户端",
3
+ "directoryTree": {
4
+ "name": "示例邮件客户端",
5
+ "path": ".",
6
+ "description": "已选单 HAP 结构,复用现有 entry;本树描述目标职责,不表示已完成实现。",
7
+ "children": [
8
+ {
9
+ "name": "AppScope",
10
+ "path": "AppScope",
11
+ "description": "应用级名称、图标等配置与资源。",
12
+ "children": []
13
+ },
14
+ {
15
+ "name": "entry",
16
+ "path": "entry",
17
+ "description": "产品 HAP,负责启动、装配和页面承载。",
18
+ "kind": "HAP",
19
+ "children": [
20
+ {
21
+ "name": "src",
22
+ "path": "entry/src",
23
+ "description": "产品模块源码与资源。",
24
+ "children": [
25
+ {
26
+ "name": "main",
27
+ "path": "entry/src/main",
28
+ "description": "产品的生产源码和资源根。",
29
+ "children": [
30
+ {
31
+ "name": "ets",
32
+ "path": "entry/src/main/ets",
33
+ "description": "ArkTS 源码,按装配、页面和共享数据职责组织。",
34
+ "children": [
35
+ {
36
+ "name": "entryability",
37
+ "path": "entry/src/main/ets/entryability",
38
+ "description": "应用入口和生命周期,调用装配并承载导航。",
39
+ "children": []
40
+ },
41
+ {
42
+ "name": "composition",
43
+ "path": "entry/src/main/ets/composition",
44
+ "description": "创建并持有共享邮件仓库,为页面提供同一实例。",
45
+ "children": []
46
+ },
47
+ {
48
+ "name": "navigation",
49
+ "path": "entry/src/main/ets/navigation",
50
+ "description": "注册列表与阅读路由,跨页只传邮件标识。",
51
+ "children": []
52
+ },
53
+ {
54
+ "name": "features",
55
+ "path": "entry/src/main/ets/features",
56
+ "description": "页面业务区,不额外拆成独立构建模块。",
57
+ "children": [
58
+ {
59
+ "name": "mail",
60
+ "path": "entry/src/main/ets/features/mail",
61
+ "description": "邮件页面业务,消费共享邮件仓库。",
62
+ "children": [
63
+ {
64
+ "name": "messagelist",
65
+ "path": "entry/src/main/ets/features/mail/messagelist",
66
+ "description": "邮件列表展示、页面状态和筛选交互。",
67
+ "children": []
68
+ },
69
+ {
70
+ "name": "messageview",
71
+ "path": "entry/src/main/ets/features/mail/messageview",
72
+ "description": "邮件阅读与附件展示,只持有当前页面的展示状态。",
73
+ "children": []
74
+ }
75
+ ]
76
+ }
77
+ ]
78
+ },
79
+ {
80
+ "name": "shared",
81
+ "path": "entry/src/main/ets/shared",
82
+ "description": "跨页面共享业务及数据所有者。",
83
+ "children": [
84
+ {
85
+ "name": "mailstore",
86
+ "path": "entry/src/main/ets/shared/mailstore",
87
+ "description": "邮件仓库唯一所有者,封装数据库与查询;不引用页面。",
88
+ "children": []
89
+ }
90
+ ]
91
+ }
92
+ ]
93
+ },
94
+ {
95
+ "name": "resources",
96
+ "path": "entry/src/main/resources",
97
+ "description": "产品和页面资源,共用模块资源根并区分命名归属。",
98
+ "children": []
99
+ }
100
+ ]
101
+ }
102
+ ]
103
+ }
104
+ ]
105
+ }
106
+ ]
107
+ },
108
+ "decisions": [
109
+ {
110
+ "title": "复用 entry,保持单 HAP",
111
+ "description": "当前没有业务独立安装、多 HAP 共享或分包加载需求,页面规模无需拆库;产品入口与装配直接落在现有 entry。"
112
+ },
113
+ {
114
+ "title": "列表与阅读按页面分开",
115
+ "description": "保留 messagelist/messageview 业务名方便定位,分别组织页面和展示状态;它们是 HAP 内的普通目录,跨页导航传邮件标识,不互传可变页面状态。"
116
+ },
117
+ {
118
+ "title": "入口装配仓库,页面依赖仓库公开能力",
119
+ "description": "shared/mailstore 是邮件数据唯一所有者;entryability 调用 composition 创建并提供同一仓库实例,列表和阅读通过仓库查询邮件及访问附件。mailstore 不引用页面,数据库初始化和迁移由它负责,实例的提供与释放由 composition 负责。"
120
+ }
121
+ ]
122
+ }
@@ -1,48 +1,46 @@
1
1
  # 用户确认后的目录落地
2
2
 
3
- 仅在用户要求落地/搬迁且已确认当前方案时读取。本阶段优先复用基础工程,用普通文件工具补齐关键目录;确实无基础工程时才使用本机 DevEco CLI 创建。不实现业务、不构建或运行应用。
3
+ 仅在用户要求创建/复用基础工程并已确认当前方案时读取。按 [SKILL.md](../SKILL.md) 的主流程完成提问及 JSON/Markdown 交付后继续;看板仅展示,不再确认。本阶段优先复用基础工程,普通文件工具补齐关键目录;确实无基础工程时才用本机 DevEco CLI 创建。不实现业务、不构建或运行应用。
4
4
 
5
- ## 1. 固定实际选择与目标
5
+ ## 1. 重读最终方案、授权与目标
6
6
 
7
- - 记录用户选择的方案ID、当前规划文件、修改意见和落地要求;明确推荐委托同样有效,不能自动把模型推荐当用户授权。
8
- - 完整迁移核对调用方交接的目标根、`.migration/config.json` 的 `harmonyProject` 与 Preflight 已建工程位置,沿用同一个实际目标根。方案内的 `products/features/commons` 布局变化不另起工程;目标指向冲突时先查明,不能静默换路径再建一套。用户明确要求更换目标时,交由调用方通过现有 CLI 配置入口同步。
9
- - 从选中方案推导目录清单,核对路径位于目标根内,无绝对子路径、上级跳转、大小写冲突或模块嵌套。不得跟随链接写入源/参考工程或目标根之外。
10
- - 先检查目标实际状态和已有工程;保留用户文件。新增目录通常可直接执行;重用、移动或覆盖存在冲突的内容先查清,未授权覆盖则停止该动作并说明具体冲突。
11
- - 目标设备/SDK与方案一致;未知设备能力不以默认phone掩盖,也不把所有Android设备名直接写入鸿蒙配置。
7
+ - 从磁盘重新读取 `architecture-plan.md` 的当前选型记录和 `architecture-plan.json`,运行 `python <本Skill目录>/scripts/architecture_plan.py validate <产物目录>/architecture-plan.json`。核对项目、当前已选方案的目录和实际创建要求。JSON 合法不等于有授权;只有规划/选型授权时不创建。
8
+ - 读取当前最终 JSON 的 `directoryTree`,结合 `decisions` 中的主要决定和依赖说明实施。Markdown/JSON 不一致、有未处理的布局意见或仍在修订时先解决,不沿用旧树;不再读取 `proposals`、选择回传或推荐字段。
9
+ - 恢复时先读现有 `shell-receipt.json` 和实际工程,对照当前最终树核对实际路径。旧记录的 complete 不证明新树也已完成;已完成的动作核对后返回,部分完成只补仍获授权的缺项,不重复初始化、搬迁或覆盖。不同方案已有落地时比较实际冲突,不清空旧目录或用户业务。
10
+ - 完整迁移核对调用方目标根、`.migration/config.json` 的 `harmonyProject` 与 Preflight 已建工程位置,沿用同一个实际目标。目录布局变化不另起工程;路径冲突先查明。用户明确更换目标时,交调用方通过现有 CLI 配置入口同步。
11
+ - JSON 不保存绝对路径;结合配置、Preflight、Markdown 和明确用户上下文核对当前源工程及实际目标。独立调用缺少目标时只问该位置,不将旧位置的授权自动用于新位置。
12
+ - 所有树路径相对于实际目标根;检查无越界、大小写冲突或模块源码树交叠,不跟随符号链接/联接点写入源、参考工程或目标根外。先查已有文件,保留用户内容;未授权的移动/覆盖遇冲突时暂停该动作并说明。
13
+ - 设备/SDK须与已确认要求一致;未知能力不以默认 phone 掩盖,也不将 Android 设备名直接写入鸿蒙配置。
12
14
 
13
15
  ## 2. 复用基础工程,缺失时才创建
14
16
 
15
- 已有基础工程(包括 Preflight 生成的模板壳)先核对根构建配置、注册模块及本地依赖,确认可承载所选目录;直接在原工程落地,不再次运行创建/初始化命令或复制一份模板。保留 CLI 模板和用户文件,不因模板布局、设备或 SDK 默认值与所选方案不同而另建工程,记录并处理必要的配置调整。
17
+ 已有基础工程(含 Preflight 模板壳)先核对根配置、注册模块和本地依赖;直接在原工程落地,不再次初始化或复制模板。不因默认布局、设备或 SDK 与所选方案不同而另建工程,记录必要调整。
16
18
 
17
- 需要把模板 `entry` 放到选中产品路径时,先核对目标无冲突,再同步根注册 `srcPath`、相关本地依赖及测试路径。检查下游工具是否仍假设原模块位置;需要目录落地之外的工具改动时记录交接,不用第二份 `entry` 或第二套工程掩盖不兼容。无法确认安全时不移动,报告具体差异与必要改法。
19
+ 最终树若确认产品 HAP 就是现有 `entry`,直接复用;若确认移到 `products/产品`,先查目标冲突,再同步根注册 `srcPath`、本地依赖及测试路径。核对下游是否假定旧模块位置;所需工具改动超出本阶段时记录交接,不用两份 HAP 或两套工程掩盖不兼容。无法确认安全时不移动,报告具体差异。
18
20
 
19
- 仅当实际目标根尚无基础工程时,才按当前方案的确认与创建要求定位本机 DevEco CLI,阅读其帮助核对当前 create 命令、应用名、bundle、设备和输出路径等参数;独立使用本 Skill 同样先检查是否已有工程。目标非空但缺少基础配置时先报告冲突,不覆盖初始化;Preflight 记录与实际工程不一致时先查明原因。不把 Android 构建文件复制成鸿蒙配置;需要用户身份信息时只询问缺少的实际值。
21
+ 仅实际目标根尚无基础工程时才定位本机 DevEco CLI,读取其帮助核对当前 create 命令、应用名、bundle、设备和输出路径。目标非空但缺基础配置时先报告冲突,不覆盖初始化;Preflight 记录与实际不符时先查明。不把 Android 构建文件复制成鸿蒙配置;只询问确实缺少的身份或目标值。
20
22
 
21
- 创建/复用授权所需的目标配置核对、目录冲突检查与创建结果验证,不等于选择结合鸿蒙源码进行架构分析。若还需阅读现有业务实现来判断复用或调整架构,先按 [SKILL.md](../SKILL.md) 询问是否结合,已有明确选择则按其范围执行。
23
+ 创建所需的配置核对和结果验证不等于选择结合鸿蒙业务进行架构分析。如需扩读业务实现,按 [SKILL.md](../SKILL.md) 的现有选择执行,尚未选择时只问缺失项。
22
24
 
23
- 确需新建但 CLI 不可用或不支持目标设备时,保留已完成规划,给出实际错误及未完成创建项;普通空目录不能冒充可用鸿蒙基础工程。复用现有工程并补目录不以重新执行创建 CLI 为前提。不要为本阶段无限安装/重试工具或索要与创建无关的凭据。
25
+ 确需新建但 CLI 不可用或不支持目标时,保留规划并报告实际错误/未完成项;空目录不能冒充可用工程。已有工程补目录不以再次运行 CLI 为前提,不无限安装或重试。
24
26
 
25
- ## 3. 补齐选中目录
27
+ ## 3. 按最终树补齐目录
26
28
 
27
- 只创建选中方案的实际模块/关键子目录。模板之外不生成业务文件、空实现、额外模块配置或提前注册所有HAR/HSP;这些目录须明确标注为待下游配置的目录壳子。
29
+ 只创建 `directoryTree` 中的实际模块/关键子目录,根 `.` 表示目标根。不得创建另外两套对照方案,也不在实施时静默增加未展示的关键路径。发现归属或布局矛盾时回到主流程修订,处理影响后再继续,不为补壳子重做业务分析。
28
30
 
29
- 选中原本仅作差异对照的方案时,先据已确认的结构差异补齐它的目录树。规划使用共用 feature 子目录模式时,从关键树与模式展开本次实际清单,按实际职责选取子目录即可;不为展开或创建壳子重做业务分析。若发现归属冲突或影响实际布局的待决,局部澄清该边界后再创建受影响目录。
31
+ 模板之外不生成业务文件、空实现、额外模块配置或提前注册所有 HAR/HSP;未配置的模块须记为目录壳子。单 HAP 的 feature 放在实际源码根内,资源服从所属模块;共享契约与所有权随规划交接,不用接口桩或重复共享状态冒充前置完成。
30
32
 
31
- 选中融合方案时从已确认映射创建基础包族与页面 feature 目录,保留逻辑目录和实际模块的区别;单 HAP 的 feature 目录落在实际源码根内,资源服从所属模块规则,不套用独立库的 `src/main`。共享契约和集成所有权随规划交接,不以生成接口桩或复制共享状态充当完成前置。
33
+ 完成后检查基础配置、注册产品路径和关键目录确实存在,设备与要求一致,用户文件未被覆盖。执行中若规划被修改,核对影响后再继续;调整过结构/配置时向调用方交接需要刷新的验证,旧 Preflight 不能证明新布局已通过。本 Skill 不自行构建或运行。
32
34
 
33
- 命令成功后检查目标根基础配置、注册产品路径及关键目录确实存在,设备配置与选择一致,已有文件没有被覆盖;若执行中用户修改规划,先核对影响,不能按旧目录继续宣称完整。
35
+ ## 4. 保存实际结果并交接
34
36
 
35
- 结构或构建配置发生变化时,向调用方交接需要刷新的工程验证;初始 Preflight 构建结果不能证明调整后的布局已通过。本 Skill 不自行构建或运行应用。
37
+ 在规划产物目录写 `shell-receipt.json`;复用并补目录同样记录:
36
38
 
37
- ## 4. 保存实际记录并交接
39
+ - `status`:complete / partial / blocked,按实际落地结果。
40
+ - `planFile`、`planViewFile`、`proposalId`、`target`:最终 JSON、规划 Markdown、当前选定方案及实际目标根;方案身份来自实际确认记录。
41
+ - `confirmation`:本次执行依据的真实回答、来源、创建要求和适用范围,指明 Markdown 记录位置;不伪造请求 ID、修订或看板确认。更新已有 receipt 时保留历史执行依据和实际落地记录。
42
+ - `baseProject`:新建/复用、来源与依据、实际注册产品路径、设备及本阶段实际执行的 CLI/日志。引用先前 Preflight 证据时注明来源,不冒充本阶段执行。
43
+ - `createdDirectories`、`existingDirectories`、`directoryOnlyModules`:新增、已存在、尚未注册的模块目录。
44
+ - `verification`、`remainingIssues`:真实检查结果、配置差异和未完成项,不编造日志或验证。
38
45
 
39
- 在规划产物目录写 `shell-receipt.json`,记录可核验的实际落地结果,复用基础工程并补目录同样需要记录:
40
-
41
- - `status`:complete / partial / blocked,按真实落地结果填写。
42
- - `planFile`、`proposalId`、`target`:当前规划、选中方案和实际目标根。
43
- - `confirmation`:实际用户选择/推荐委托及对应修订,未确认不得执行落地。
44
- - `baseProject`:新建或复用、工程来源与辨别依据、实际注册产品路径、设备类型及本阶段实际使用的 CLI/命令/日志位置。Preflight 已建工程记为复用;引用先前创建证据时注明来源,不冒充本阶段执行。
45
- - `createdDirectories`、`existingDirectories`、`directoryOnlyModules`:分别记录新增、已存在和未注册的模块目录。
46
- - `verification`、`remainingIssues`:实际检查结果、路径/配置差异和未完成项,不编造日志或检查。
47
-
48
- 落地完成只表示基础工程已创建或复用、所选目录壳子已落地。交接最终规划、receipt与未解决风险;其他两套保留为对照。模型生成的记录不能替代目标文件/目录和用户授权证据。
46
+ 落地完成仅表示基础工程和所选目录壳子已落地。交接最终规划、receipt 和未解决风险,不宣称业务迁移完成。
@@ -21,7 +21,7 @@
21
21
 
22
22
  保留的是可追溯结构与合理协作,不是 Android 实现机制:Android API、资源格式、JNI/运行时和 SDK 坐标按目标承接方式替换。原结构中反向依赖页面、应用单例、环或业务混杂的部分需要最小拆分/改向,逐项记录原路径、保留部分、调整部分、目标所有者、消费者与理由。未确认的 SDK/Native/运行时能力记录受影响边界和验证前置,不能凭保留目录认定可直接转换。
23
23
 
24
- 不要为了套 `core/network/storage/model` 模板再次打散已清晰的原包族;也不要为了保留路径把对页面/应用单例的依赖、展示状态或业务策略留在基础层。平台适配所需上下文由装配按生命周期提供,避免基础能力持有页面实例。跨层拆分成本明显超过收益时说明替代边界及仍受耦合限制的页面。
24
+ 原包族已经清楚时,保留其组织方式,不为套用 `core/network/storage/model` 重新拆散。原包中混有页面状态、业务策略或直接访问应用全局对象的代码时,分别移回业务侧或改为接收明确依赖;基础库需要配置、应用环境等信息时由应用入口提供,不长期持有页面对象。拆分改动很大且收益有限时,说明保留部分及哪些页面仍需一起修改和验证。
25
25
 
26
26
  ## 3. 业务层:feature 是转换边界
27
27
 
@@ -29,34 +29,36 @@
29
29
 
30
30
  每个 feature 内聚页面、ViewModel/等效状态、私有数据访问、组件和资源;按真实复杂度选目录,保留原业务名及源码映射便于定位。共享会话、缓存、任务队列等由一个领域所有者维护,页面只保留展示投影;导航传必要标识/值或约定结果,不传其他页面的 ViewModel、组件实例或可变状态。
31
31
 
32
- `feature` 是职责与目录边界,不是“一页一个 HAR/HSP”。先确定页面归属,再按部署、运行时共享和构建成本决定哪些目录构成 HAP/HAR/HSP。不同 HAR 中重复打包或初始化同一状态不能当作进程级单例;需共享状态时说明实际实例如何由装配创建、提供、持有与释放。
32
+ `feature` 在这里指业务目录,不等于 feature 类型的 HAP,也不要求一页一个 HAR/HSP。包类型按 [layout-reference.md](layout-reference.md) 的选型规则确定:没有拆库收益时留在产品 HAP,需要封装引用时用 HAR,有实际运行时共享或按需加载需求时再评估 HSP。HAP 不能作为供其他模块直接 import 的业务库。
33
33
 
34
- 下面仅示意已存在 `infra` 基础包、商品列表/详情共享业务及独立设置页的工程。树只列目录,名字和包粒度从实际源码确定;`commons`、`features` 分别可承载两层,`products` 承载业务侧装配。
34
+ 同一个 HAR 被多个 HAP/HSP 引用时可能重复打包;无论选择 HAR 还是 HSP,都不能因此认定业务状态只有一个实例。共享仓库/会话由应用入口统一创建和提供,说明其有效期;跨进程不假设共享同一实例。
35
+
36
+ 下面仅示意需要单独维护 `infra` 基础库、商品业务库和设置业务库的工程:一个产品 HAP 引用这些 HAR,商品列表/详情由产品导航打开,没有独立安装需求。树只列目录,名字和包粒度从实际源码确定;`commons`、`features` 分别可承载两层,`products` 承载业务侧装配。
35
37
 
36
38
  ```text
37
39
  <HarmonyOS>/
38
40
  ├── AppScope/
39
- ├── products/<产品>/src/main/ # HAP 示例
41
+ ├── products/<产品>/src/main/ # entry HAP:启动、装配与导航
40
42
  │ ├── ets/{entryability,navigation,composition}/
41
43
  │ └── resources/
42
- ├── commons/infra/src/main/ # 基础 HAR 示例
44
+ ├── commons/infra/src/main/ # HAR:供产品与业务模块引用的基础库
43
45
  │ ├── ets/com/acme/infra/ # 原基础包族相对层级
44
46
  │ │ ├── net/
45
47
  │ │ └── store/
46
48
  │ └── resources/ # 原基础能力实际拥有的资源
47
49
  └── features/
48
- ├── catalog/src/main/ # 业务 HAR 示例,含两个页面 feature
50
+ ├── catalog/src/main/ # HAR:产品 HAP 引用,含列表/详情,无独立安装需求
49
51
  │ ├── ets/
50
52
  │ │ ├── list/{pages,viewmodel,data,components}/
51
53
  │ │ ├── detail/{pages,viewmodel,data}/
52
54
  │ │ └── shared/{model,repository,state}/
53
55
  │ └── resources/ # 同模块资源,划分页面命名与集成归属
54
- └── settings/src/main/ # 另一业务 HAR 示例
56
+ └── settings/src/main/ # HAR:产品 HAP 引用的设置业务库
55
57
  ├── ets/{pages,viewmodel}/
56
58
  └── resources/
57
59
  ```
58
60
 
59
- 上例列表/详情共享一个业务 HAR,页面 feature 是其内部独立目录。实际工程可以合入产品 HAP,也可在独立演进收益足够时拆库;目录树需标注实际模块及源码根,不把业务域分组自动变成构建模块。共享状态实例仍按装配契约提供,不由 HAR 名称保证唯一性。
61
+ 上例列表/详情同属一个业务 HAR,页面 feature 是库内的普通目录。若只是这几个页面且无拆库收益,直接放在产品 HAP 内更简单;确有独立安装/部署需求时才评估 feature HAP,并重新设计 Ability 入口及跨 HAP 协作,不能只改树上的类型注释。目录树标注实际模块及源码根,不把业务域分组自动变成构建模块。此例没有需要 HSP 的共享或加载要求,不为展示包类型凑一个 HSP。
60
62
 
61
63
  单 HAP 可在 `entry/src/main/ets/` 下使用 `foundation/<原包族>/`、`features/<业务>/<页面>/`、必要的 `features/<业务>/shared/` 和装配目录;资源按目标模块规则放置并划分命名/写入所有权。模块内目录也能定义页面转换边界,不能虚构每页独立的资源根或构建能力。
62
64
 
@@ -71,14 +73,10 @@
71
73
 
72
74
  在架构稿中简记关键共享能力、目标所有者、主要消费者及协作/装配方式,未明确的契约列为后续细化前置。只有输入输出、错误或生命周期约束会改变拆合与所有权时才展开;不要求完整契约设计,不预定接口文件,也不把规划能力写成已实现。
73
75
 
74
- 导航可参考 [HarmonyOS 应用规划](https://developer.huawei.com/consumer/cn/app/planning/) 中的分层、模块化和 Navigation 设计,以及 [Navigation 跨包路由](https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/arkts-navigation-cross-package) 的模块解耦机制(核查日期:2026-10-29)。两层融合、原包族保留及按页面分工是本 Skill 的迁移设计选择;不将一页一模块作为平台要求。具体路由与打包机制按实际 SDK/设备核实,不仅凭目录选定。
76
+ 跨模块页面导航参考 [Navigation 跨包路由](https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/arkts-navigation-cross-package),核对系统路由表及 HAR/HSP 页面路由的配置方式(核查日期:2026-11-02)。这份资料只作为导航集成依据;本 Skill 的两层划分、原包族保留及按页面组织是迁移设计选择,不宣称来自官方统一分层规范。具体路由与打包机制按实际 SDK/设备核实,不仅凭目录选定。
75
77
 
76
78
  ## 5. 从分层到独立页面转换的交接
77
79
 
78
- 本阶段交接“源入口/页面族 → feature 目录 → 共享依赖/所有者”及会影响分工的已知前置,复用归属表即可。转换建议按消费者说明最小基础/共享前置,再列可独立转换的页面候选和集成区域;不要求全部基础库先迁完,无关页面不被不相关的 Native/SDK 风险阻塞。
79
-
80
- 路由、资源聚合、公共导出、构建配置与装配等已知共同修改区域指定集成归属;页面私有目录与共享区域分开。共享修改先回所有者,精确的独占文件、测试资产、完整契约与验收条件由后续页面规划/实现确定,不作为架构稿交付前置。
81
-
82
- 并行建议附条件:契约明确、实现前置满足、写入范围不冲突且共同修改有集成归属;仅导航关联不自动形成实现顺序。条件未核验时写候选或待细化,强耦合已证实才要求合组/串行;规划契约与空目录不能证明已可开工。集成仍需验证真实能力和跨页协作。
80
+ 交接源入口/页面族、目标 feature、共享依赖及所有者,说明各候选真正需要的前置;不要求无关基础库全部迁完。路由、资源、公共导出、构建配置和应用装配等共同修改区域要有集成归属。
83
81
 
84
- 已有稳定 ID 可沿用,没有则以源入口关联候选,不造页面/Spec 身份、账本或任务协议。实际派发、工作区隔离、构建/设备资源协调与验收由执行方决定,架构目录不自动开放并发。
82
+ 页面目录分开只表示候选边界。能否独立转换,还需后续确认契约、实现前置和写入范围无冲突;强耦合已证实时说明需要合组的部分。具体页面/Spec 身份、任务派发和验收由下游确定,不在架构阶段生成任务协议或把目录壳子记为已可开工。