deveco_hmigbot 0.21.5

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 (101) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +50 -0
  3. package/agents/hmigbot-worker.md +61 -0
  4. package/agents/hmigbot.md +22 -0
  5. package/agents/workflow-subagent.md +55 -0
  6. package/commands/hmigbot.md +17 -0
  7. package/dist/index.js +1 -0
  8. package/manifest.json +19 -0
  9. package/package.json +29 -0
  10. package/skills/migrate-core/FILES.md +26 -0
  11. package/skills/migrate-core/SKILL.md +484 -0
  12. package/skills/migrate-core/references/README.md +64 -0
  13. package/skills/migrate-core/references/flow/arkts-vector-gate.md +55 -0
  14. package/skills/migrate-core/references/flow/build-error-patterns.md +52 -0
  15. package/skills/migrate-core/references/flow/conventions-template.md +244 -0
  16. package/skills/migrate-core/references/flow/navigation-migration.md +42 -0
  17. package/skills/migrate-core/references/flow/platform-api-guards.md +59 -0
  18. package/skills/migrate-core/references/flow/platform-model-gaps.md +43 -0
  19. package/skills/migrate-core/references/flow/resource-conversion.md +46 -0
  20. package/skills/migrate-core/references/flow/ui-layout-semantics.md +124 -0
  21. package/skills/migrate-core/references/flow/unit-breakdown.md +42 -0
  22. package/skills/migrate-core/references/host-capabilities.md +24 -0
  23. package/skills/migrate-core/references/topics/app-identity.md +214 -0
  24. package/skills/migrate-core/references/topics/env-doctor.md +245 -0
  25. package/skills/migrate-core/references/topics/i18n/README.md +458 -0
  26. package/skills/migrate-core/references/topics/i18n/references/code-examples.md +304 -0
  27. package/skills/migrate-core/references/topics/i18n/references/common-pitfalls.md +354 -0
  28. package/skills/migrate-core/references/topics/i18n/references/dynamic-language-switch.md +464 -0
  29. package/skills/migrate-core/references/topics/i18n/references/language-codes.md +104 -0
  30. package/skills/migrate-core/references/topics/icon-sizing.md +98 -0
  31. package/skills/migrate-core/references/topics/library-migration/README.md +234 -0
  32. package/skills/migrate-core/references/topics/library-migration/closed-source-sdk.md +128 -0
  33. package/skills/migrate-core/references/topics/library-migration/download-api-decision.md +84 -0
  34. package/skills/migrate-core/references/topics/library-migration/library-mapping-table.md +100 -0
  35. package/skills/migrate-core/references/topics/library-migration/napi-compile-guide.md +84 -0
  36. package/skills/migrate-core/references/topics/library-migration/ohpm-search-guide.md +73 -0
  37. package/skills/migrate-core/references/topics/library-migration/stdlib-mapping-table.md +34 -0
  38. package/skills/migrate-core/references/topics/resources/aar-decompile.md +25 -0
  39. package/skills/migrate-core/references/topics/resources/conversion-rules.md +625 -0
  40. package/skills/migrate-core/references/topics/resources/dependency-analysis-rules.md +328 -0
  41. package/skills/migrate-core/references/topics/resources/material-design-icons.md +173 -0
  42. package/skills/migrate-core/references/topics/resources/svg-fix-patterns.md +175 -0
  43. package/skills/migrate-core/references/topics/resources/xml-drawable-to-svg-rules.md +513 -0
  44. package/skills/migrate-core/references/topics/system-capabilities/README.md +331 -0
  45. package/skills/migrate-core/references/topics/system-capabilities/avplayer-guide.md +161 -0
  46. package/skills/migrate-core/references/topics/system-capabilities/background-tasks.md +403 -0
  47. package/skills/migrate-core/references/topics/system-capabilities/browser-intent.md +121 -0
  48. package/skills/migrate-core/references/topics/system-capabilities/camera-picker.md +118 -0
  49. package/skills/migrate-core/references/topics/system-capabilities/document-picker.md +246 -0
  50. package/skills/migrate-core/references/topics/system-capabilities/file-utils.md +131 -0
  51. package/skills/migrate-core/references/topics/system-capabilities/permission-helper.md +112 -0
  52. package/skills/migrate-core/references/topics/system-capabilities/photo-access-helper.md +208 -0
  53. package/skills/migrate-core/references/topics/system-capabilities/print-management.md +213 -0
  54. package/skills/migrate-core/references/topics/system-capabilities/share-panel.md +177 -0
  55. package/skills/migrate-core/references/topics/system-capabilities/system-settings.md +322 -0
  56. package/skills/migrate-core/references/topics/system-capabilities/telephony-dial.md +49 -0
  57. package/skills/migrate-core/references/topics/system-capabilities/video-playback.md +42 -0
  58. package/skills/migrate-core/references/topics/system-capabilities/webview-patterns.md +38 -0
  59. package/skills/migrate-core/references/topics/ui-alignment/README.md +344 -0
  60. package/skills/migrate-core/references/topics/ui-alignment/references/dark-mode.md +47 -0
  61. package/skills/migrate-core/references/topics/ui-alignment/references/layout-mapping.md +301 -0
  62. package/skills/migrate-core/references/topics/ui-alignment/references/visual-patterns.md +411 -0
  63. package/skills/migrate-core/scripts/closure/check-anchors.mjs +186 -0
  64. package/skills/migrate-core/scripts/closure/check-api-guards.mjs +175 -0
  65. package/skills/migrate-core/scripts/closure/check-consumers.mjs +301 -0
  66. package/skills/migrate-core/scripts/closure/check-permissions.mjs +165 -0
  67. package/skills/migrate-core/scripts/closure/check-resources.mjs +130 -0
  68. package/skills/migrate-core/scripts/closure/check-routes.mjs +527 -0
  69. package/skills/migrate-core/scripts/closure/check-safearea.mjs +122 -0
  70. package/skills/migrate-core/scripts/closure/check-stubs.mjs +69 -0
  71. package/skills/migrate-core/scripts/closure/closure-suite.mjs +256 -0
  72. package/skills/migrate-core/scripts/closure/idioms.json +105 -0
  73. package/skills/migrate-core/scripts/convert/convert-resources.mjs +437 -0
  74. package/skills/migrate-core/scripts/feasibility/feasibility.mjs +235 -0
  75. package/skills/migrate-core/scripts/feasibility/tables/cross-platform.json +11 -0
  76. package/skills/migrate-core/scripts/feasibility/tables/deprecated-api.json +10 -0
  77. package/skills/migrate-core/scripts/feasibility/tables/imported-arkts-core.json +425 -0
  78. package/skills/migrate-core/scripts/feasibility/tables/lib-equivalence.json +206 -0
  79. package/skills/migrate-core/scripts/feasibility/tables/system-capabilities.json +22 -0
  80. package/skills/migrate-core/scripts/front.mjs +107 -0
  81. package/skills/migrate-core/scripts/interface/ark-extract.mjs +172 -0
  82. package/skills/migrate-core/scripts/interface/interface.mjs +152 -0
  83. package/skills/migrate-core/scripts/ledger/ledger.mjs +383 -0
  84. package/skills/migrate-core/scripts/ledger/parse-cards.mjs +98 -0
  85. package/skills/migrate-core/scripts/lib/literals.mjs +37 -0
  86. package/skills/migrate-core/scripts/lib/scan.mjs +315 -0
  87. package/skills/migrate-core/scripts/smoke/align-sdk.mjs +118 -0
  88. package/skills/migrate-core/scripts/smoke/ensure-sign.mjs +53 -0
  89. package/skills/migrate-core/scripts/smoke/smoke.mjs +238 -0
  90. package/skills/migrate-core/scripts/smoke/verdict.mjs +31 -0
  91. package/skills/migrate-core/scripts/smoke/walk.mjs +480 -0
  92. package/skills/migrate-core/scripts/transpile/mapping.json +76 -0
  93. package/skills/migrate-core/scripts/transpile/transpile-layout.mjs +404 -0
  94. package/skills/migrate-core/scripts/vectors/run-arkts-vectors.mjs +107 -0
  95. package/skills/migrate-core/scripts/vectors/setup-arkts-test.mjs +90 -0
  96. package/skills/migrate-core/scripts/wire/extractors.mjs +258 -0
  97. package/skills/migrate-core/scripts/wire/wire-routes.mjs +507 -0
  98. package/skills/migrate-core/templates/acceptance.js +365 -0
  99. package/skills/migrate-core/templates/explore.js +86 -0
  100. package/skills/migrate-core/templates/implement.js +211 -0
  101. package/skills/migrate-core/templates/mig_slices.js +491 -0
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * feasibility.mjs — 开工体检 / 红黄绿自动分诊(纯脚本,分钟级,零模型)
4
+ *
5
+ * 三张账:三方库 × 等价物表(库墙) / manifest 系统集成面 × 开放度表(平台墙) / UI 技术判型。
6
+ * 结论:逐能力三档(green/yellow/red),应用级颜色推导(红项才红;黄项=带预案继续)。
7
+ * 全自动纪律:本工具只出画像与处置建议,不问人。红灯的停/走由上层策略(尽力而为/快速失败)决定。
8
+ *
9
+ * 用法:node feasibility.mjs --src <Android 工程根> [--json out.json] [--gate]
10
+ * 退出码:默认恒 0;--gate 时 green=0 / yellow=4 / red=5(供工作流 check() 分诊)。
11
+ */
12
+ import { readFileSync, readdirSync, statSync, writeFileSync, existsSync } from 'node:fs';
13
+ import { join, resolve, relative, sep, dirname } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+ import { extractNavGraph, extractComposeProject } from '../wire/extractors.mjs';
16
+
17
+ const argv = process.argv.slice(2);
18
+ const argOf = (n, d) => { const i = argv.indexOf(n); return i >= 0 && argv[i + 1] ? argv[i + 1] : d; };
19
+ const SRC = argOf('--src', null);
20
+ const OUT = argOf('--json', null);
21
+ const GATE = argv.includes('--gate');
22
+ if (!SRC) { console.error('usage: feasibility.mjs --src <android root> [--json out] [--gate]'); process.exit(3); }
23
+ const HERE = dirname(fileURLToPath(import.meta.url));
24
+ const LIBS = JSON.parse(readFileSync(join(HERE, 'tables', 'lib-equivalence.json'), 'utf8'));
25
+ // 工作区级裁决缓存(spec/lib-adjudications.json):本轮"待裁决依赖"的结论落工作区合并读——
26
+ // 套件表只由维护者审后更新(回流走 feedback,不直接改语料/表)
27
+ const ADJ = join(dirname(resolve(argOf('--json', 'spec/feasibility.json'))), 'lib-adjudications.json');
28
+ if (existsSync(ADJ)) {
29
+ try {
30
+ const adj = JSON.parse(readFileSync(ADJ, 'utf8'));
31
+ for (const e of (Array.isArray(adj) ? adj : adj.entries || [])) if (e && e.match) LIBS.entries.push({ source: 'adjudicated(workspace)', ...e });
32
+ } catch { console.error('[feasibility] lib-adjudications.json 解析失败,忽略'); }
33
+ }
34
+ const CAPS = JSON.parse(readFileSync(join(HERE, 'tables', 'system-capabilities.json'), 'utf8'));
35
+ // 第②层:权威映射数据导入——优先级低于种子实证缓存(数组顺序即优先级)。
36
+ // 默认自动装载 tables/imported-arkts-core.json(由套件仓 devtools/import-arkts-core-map.mjs 从 ArkTs-Core
37
+ // 官方 50+ 库映射表转换而来);--extra-map 可再叠加(openharmony-tpc 三方库地图等)。
38
+ for (const mapFile of [join(HERE, 'tables', 'imported-arkts-core.json'), argOf('--extra-map', null)].filter(Boolean)) {
39
+ if (existsSync(mapFile)) {
40
+ const em = JSON.parse(readFileSync(mapFile, 'utf8'));
41
+ LIBS.entries.push(...(em.entries || em));
42
+ }
43
+ }
44
+ const PROBE = argv.includes('--probe-ohpm');
45
+ async function probeOhpm(artifact) {
46
+ // 第③层:ohpm 生态确定性探测(在线,best-effort;失败=无线索,不定罪)
47
+ try {
48
+ const q = artifact.split(':').pop();
49
+ const url = (LIBS.ohpmSearch || '').replace('{q}', encodeURIComponent(q));
50
+ if (!url) return null;
51
+ const res = await fetch(url, { signal: AbortSignal.timeout(6000) });
52
+ if (!res.ok) return null;
53
+ const j = await res.json();
54
+ const rows = j?.body?.rows || j?.rows || [];
55
+ return rows.slice(0, 3).map(r => r.name || r.packageName).filter(Boolean);
56
+ } catch { return null; }
57
+ }
58
+
59
+ const EXCLUDE = new Set(['build', '.git', '.gradle', 'node_modules', '.idea']);
60
+ function walk(dir, acc, depth = 0) {
61
+ if (depth > 14) return acc;
62
+ let es; try { es = readdirSync(dir); } catch { return acc; }
63
+ for (const e of es) {
64
+ if (EXCLUDE.has(e)) continue;
65
+ const p = join(dir, e);
66
+ let st; try { st = statSync(p); } catch { continue; }
67
+ if (st.isDirectory()) walk(p, acc, depth + 1);
68
+ else acc.push(p);
69
+ }
70
+ return acc;
71
+ }
72
+ const files = walk(resolve(SRC), []);
73
+ const rel = f => String(f).replace(resolve(SRC) + sep, '');
74
+
75
+ // ---------- 1) 依赖清单(build.gradle / .kts / libs.versions.toml) ----------
76
+ const gradleFiles = files.filter(f => /build\.gradle(\.kts)?$/.test(f));
77
+ const tomlFiles = files.filter(f => f.endsWith('libs.versions.toml'));
78
+ const deps = new Set();
79
+ for (const f of gradleFiles) {
80
+ const code = readFileSync(f, 'utf8');
81
+ for (const m of code.matchAll(/(?:implementation|api|compileOnly|runtimeOnly|kapt|ksp)\s*[\(\s]\s*["']([\w.\-]+:[\w.\-]+)(?::[^"']*)?["']/g)) deps.add(m[1]);
82
+ }
83
+ for (const f of tomlFiles) {
84
+ const code = readFileSync(f, 'utf8');
85
+ for (const m of code.matchAll(/module\s*=\s*["']([\w.\-]+:[\w.\-]+)["']/g)) deps.add(m[1]);
86
+ for (const m of code.matchAll(/group\s*=\s*["']([\w.\-]+)["']\s*,\s*name\s*=\s*["']([\w.\-]+)["']/g)) deps.add(`${m[1]}:${m[2]}`);
87
+ for (const m of code.matchAll(/name\s*=\s*["']([\w.\-]+)["']\s*,\s*group\s*=\s*["']([\w.\-]+)["']/g)) deps.add(`${m[2]}:${m[1]}`); // 内联表 name 在前的写法
88
+ }
89
+ // settings.gradle(.kts) 的 versionCatalogs { library(...) } DSL——盲区会让库墙假阴偏绿
90
+ for (const f of files.filter(x => /settings\.gradle(\.kts)?$/.test(x))) {
91
+ const code = readFileSync(f, 'utf8');
92
+ for (const m of code.matchAll(/library\s*\(\s*["'][^"']+["']\s*,\s*["']([\w.\-]+)["']\s*,\s*["']([\w.\-]+)["']\s*\)/g)) deps.add(`${m[1]}:${m[2]}`);
93
+ for (const m of code.matchAll(/library\s*\(\s*["'][^"']+["']\s*,\s*["']([\w.\-]+:[\w.\-]+)(?::[^"']*)?["']\s*\)/g)) deps.add(m[1]);
94
+ }
95
+ // 源码规模 + import 面(供 none 库的调用面统计)
96
+ const srcFiles = files.filter(f => /\.(java|kt)$/.test(f) && f.includes(`${sep}src${sep}`) && !f.includes(`${sep}test`));
97
+ let totalLines = 0;
98
+ const importLines = [];
99
+ for (const f of srcFiles) {
100
+ const code = readFileSync(f, 'utf8');
101
+ totalLines += code.split('\n').length;
102
+ for (const m of code.matchAll(/^import\s+([\w.]+)/gm)) importLines.push(m[1]);
103
+ }
104
+ const libFindings = [];
105
+ for (const d of deps) {
106
+ const entry = LIBS.entries.find(e => new RegExp(e.match, e.source === 'official-map' ? 'i' : '').test(d));
107
+ if (!entry) {
108
+ // 缓存未命中:不猜。可选 ohpm 探测给线索,最终 status=unadjudicated,
109
+ // 由迁移流程的裁决步(模型+证据)定夺并回写缓存(source=adjudicated)。
110
+ const f = { dep: d, status: 'unadjudicated', note: '缓存未命中——待裁决回写' };
111
+ // 调用面无论如何先量出来(身份无关的确定性事实,供裁决用)
112
+ const guessPrefix = d.split(':')[0];
113
+ f.callFace = importLines.filter(i => i.startsWith(guessPrefix)).length;
114
+ if (PROBE) {
115
+ const hits = await probeOhpm(d);
116
+ if (hits && hits.length) f.ohpmHints = hits;
117
+ }
118
+ libFindings.push(f);
119
+ continue;
120
+ }
121
+ if (entry.status === 'ignore') continue;
122
+ const finding = { dep: d, status: entry.status, harmony: entry.harmony || null, note: entry.note, evidence: entry.evidence, source: entry.source || 'seed' };
123
+ if (entry.status === 'none' && entry.importPrefix) {
124
+ finding.callFace = importLines.filter(i => i.startsWith(entry.importPrefix)).length;
125
+ finding.overThreshold = finding.callFace >= (LIBS.noneCallFaceThreshold || 20);
126
+ }
127
+ libFindings.push(finding);
128
+ }
129
+
130
+ // ---------- 2) 系统集成面(manifest + 关键源码标记) ----------
131
+ const manifests = files.filter(f => f.endsWith('AndroidManifest.xml') && f.includes(`${sep}main${sep}`));
132
+ let manifestText = manifests.map(f => readFileSync(f, 'utf8')).join('\n');
133
+ // 源码级标记补充(AppOpsManager 这类 manifest 看不到的);超采样上限必须告知——静默截断=漏检谎报
134
+ const SAMPLE_CAP = 800;
135
+ const srcSampleTruncated = srcFiles.length > SAMPLE_CAP;
136
+ const srcSample = srcFiles.slice(0, SAMPLE_CAP).map(f => readFileSync(f, 'utf8')).join('\n');
137
+ const capFindings = [];
138
+ for (const e of CAPS.entries) {
139
+ const re = new RegExp(e.marker);
140
+ const inManifest = re.test(manifestText);
141
+ const inSrc = re.test(srcSample);
142
+ if (inManifest || inSrc) {
143
+ // 平台墙按调用面分档:边缘能力(全工程命中 ≤ coreThreshold,默认 3)不拦全应用——降黄
144
+ // 置灰照迁;主业级能力(高频命中,如拨号器 telecom 全家桶)才红。表条目可覆写阈值。
145
+ const g = new RegExp(e.marker, 'g');
146
+ const face = ((srcSample.match(g) || []).length) + ((manifestText.match(g) || []).length);
147
+ let wall = e.wall, disposition = e.disposition;
148
+ const th = e.coreThreshold ?? 3;
149
+ if (wall === 'red' && face <= th) { wall = 'yellow'; disposition = `边缘能力(命中 ${face} 处)置灰降级:${e.disposition}`; }
150
+ capFindings.push({ cap: e.cap, wall, callFace: face, where: inManifest ? 'manifest' : 'source', disposition, evidence: e.evidence });
151
+ }
152
+ }
153
+
154
+ // ---------- 3) UI 技术判型 ----------
155
+ const layoutXml = files.filter(f => /\/res\/layout[^/]*\/.*\.xml$/.test(f.replace(/\\/g, '/'))).length;
156
+ const hasCompose = [...deps].some(d => d.includes('compose')) || /androidx\.compose/.test(srcSample);
157
+ const uiTech = hasCompose && layoutXml > 5 ? 'mixed' : hasCompose ? 'compose' : 'xml';
158
+
159
+ // ---------- 3.5) 跨端框架检测(壳应用改道,不走原生重写) ----------
160
+ const XP = JSON.parse(readFileSync(join(HERE, 'tables', 'cross-platform.json'), 'utf8'));
161
+ // 强证据=依赖清单/manifest 命中;文件名命中(排除文档类)只算弱证据——
162
+ // docs/uni-app-对比.md 这类路径不许把整单打成红灯改道
163
+ const xpStrongHay = [...deps].join('\n') + '\n' + manifestText.slice(0, 20000);
164
+ const xpPathHay = files.filter(f => !/\.(md|txt|html?|pdf)$/i.test(f)).slice(0, 4000).map(f => f.split(sep).pop()).join('\n');
165
+ const crossPlatform = XP.entries.map(e => {
166
+ const re = new RegExp(e.marker, 'i');
167
+ if (re.test(xpStrongHay)) return { framework: e.framework, disposition: e.disposition };
168
+ if (re.test(xpPathHay)) return { framework: e.framework, disposition: e.disposition, weak: true };
169
+ return null;
170
+ }).filter(Boolean);
171
+ const xpStrong = crossPlatform.filter(c => !c.weak);
172
+ const xpWeak = crossPlatform.filter(c => c.weak);
173
+
174
+ // ---------- 4) 裁决 ----------
175
+ const redLibs = libFindings.filter(l => l.status === 'none' && l.overThreshold);
176
+ const redCaps = capFindings.filter(c => c.wall === 'red');
177
+ const yellowItems = [
178
+ ...libFindings.filter(l => (l.status === 'none' && !l.overThreshold) || l.status === 'partial'),
179
+ ...capFindings.filter(c => c.wall === 'yellow'),
180
+ ];
181
+ const verdict = xpStrong.length ? 'red' : (redLibs.length || redCaps.length) ? 'red' : ((yellowItems.length || xpWeak.length) ? 'yellow' : 'green');
182
+
183
+ // ---------- 应用形态画像(下游按此分派:路由提取轨/语料加载/门配置) ----------
184
+ const srcTexts = srcFiles.map(f => { try { return readFileSync(f, 'utf8'); } catch { return ''; } });
185
+ const cnt = (re) => srcTexts.reduce((n, t) => n + (t.match(re) || []).length, 0);
186
+ const navGraphXml = files.filter(f => /[\\/]res[\\/]navigation[\\/][^\\/]+\.xml$/.test(f)).length;
187
+ const manifestActs = files.filter(f => f.endsWith('AndroidManifest.xml') && f.includes(`${sep}main${sep}`))
188
+ .reduce((n, f) => { try { return n + ((readFileSync(f, 'utf8').match(/<activity[\s>]/g) || []).length); } catch { return n; } }, 0);
189
+ const composableN = cnt(/@Composable/g), navHostN = cnt(/\bNavHost\s*\(/g), fragmentN = cnt(/:\s*(?:Dialog)?Fragment\(|extends\s+(?:Dialog)?Fragment\b/g);
190
+ const presenterN = cnt(/\bPresenter\b/g), viewModelN = cnt(/:\s*ViewModel\(|extends\s+ViewModel\b|AndroidViewModel/g);
191
+ // 规模档建议值(标准只给默认,模型结合调用深度可改判并写理由)
192
+ // 页面数按路由真源数(与 wire-routes 同一套提取器):manifest Activity + navGraph 目的地 + Compose 目的地;三者都空时才退回 Fragment 计数
193
+ const navDest = new Set();
194
+ for (const f of files.filter(f => /[\\/]res[\\/]navigation[\\/][^\\/]+\.xml$/.test(f))) { try { for (const r of extractNavGraph(readFileSync(f, 'utf8'))) navDest.add(r.route); } catch { /* 坏 XML 不计 */ } }
195
+ const composeDest = new Set();
196
+ if (composableN) { try { for (const r of extractComposeProject(srcFiles.map((f, i) => ({ path: f, text: srcTexts[i] })))) composeDest.add(r.route); } catch { /* 提取失败不计 */ } }
197
+ const pagesApprox = manifestActs + navDest.size + composeDest.size + ((navDest.size || composeDest.size) ? 0 : Math.round(fragmentN / 2));
198
+ const sizeClass = (totalLines <= 15000 && srcFiles.length <= 120 && pagesApprox <= 8 && deps.size <= 25) ? 'small' : 'standard';
199
+ // 阶段形态一次定(写进 feasibility.json,各阶段只读 shape.*,不再现场判断"要不要跑"):
200
+ // explore inline=主会话亲读源码产卡 / workflow=按域并行探索
201
+ // foundation inline=地基片作 wave-0 并入切片流水 / wave=独立地基波再派生 manifest
202
+ // adversary false=不置 risk / derived=按台账 degradations 派生 risk
203
+ // 只是默认值:模型结合调用深度可改判,改判连理由写回 feasibility.json(实测 20 个几百行应用里 9 个跑了空地基波)。
204
+ const shape = sizeClass === 'small'
205
+ ? { explore: 'inline', foundation: 'inline', adversary: false }
206
+ : { explore: 'workflow', foundation: 'wave', adversary: 'derived' };
207
+ const profile = {
208
+ activities: manifestActs,
209
+ uiParadigm: composableN > 20 ? (layoutXml > 10 ? 'mixed' : 'compose') : 'xml',
210
+ navSources: [manifestActs > 1 ? 'manifest' : null, navGraphXml ? 'navgraph' : null, navHostN ? 'compose-navhost' : null].filter(Boolean),
211
+ fragments: fragmentN, archHints: [presenterN > 20 ? 'mvp' : null, viewModelN > 5 ? 'mvvm' : null].filter(Boolean),
212
+ sizeClass, sizeSignals: { lines: totalLines, files: srcFiles.length, pagesApprox, deps: deps.size }, shape,
213
+ };
214
+
215
+ const report = {
216
+ src: relative(process.cwd(), resolve(SRC)) || '.', verdict, profile,
217
+ scale: { srcFiles: srcFiles.length, lines: totalLines, uiTech, layoutXml, sampleTruncated: srcSampleTruncated ? `能力标记源码采样 ${SAMPLE_CAP}/${srcFiles.length} 文件(超限部分未扫,结论可能偏绿)` : null },
218
+ deps: [...deps].sort(),
219
+ libFindings, capFindings,
220
+ crossPlatform,
221
+ summary: {
222
+ red: [...xpStrong.map(c => `跨端框架 ${c.framework}——改道:${c.disposition}`), ...redLibs.map(l => `库墙 ${l.dep}(调用面 ${l.callFace}):${l.note}`), ...redCaps.map(c => `平台墙 ${c.cap} → 处置:${c.disposition}`)],
223
+ yellow: [...xpWeak.map(c => `疑似跨端痕迹 ${c.framework}(仅文件名弱证据)——人工回源码确认,坐实再改道`), ...yellowItems.map(i => i.dep ? `${i.dep}(${i.status})→ ${i.harmony || i.note}` : `${i.cap} → ${i.disposition}`)],
224
+ unadjudicated: libFindings.filter(l => l.status === 'unadjudicated').map(l => `${l.dep}(调用面 ${l.callFace}${l.ohpmHints ? ',ohpm 线索: ' + l.ohpmHints.join('/') : ''})`),
225
+ },
226
+ };
227
+ if (OUT) writeFileSync(OUT, JSON.stringify(report, null, 2));
228
+ const C = { red: '🔴', yellow: '🟡', green: '🟢' }[verdict];
229
+ console.log(`[feasibility] ${resolve(SRC)}`);
230
+ console.log(` ${C} ${verdict.toUpperCase()} · 源 ${srcFiles.length} 文件/${totalLines} 行 · UI=${uiTech}(layout xml ${layoutXml}) · 依赖 ${deps.size}`);
231
+ if (srcSampleTruncated) console.log(` ⚠ ${report.scale.sampleTruncated}`);
232
+ for (const s of report.summary.red) console.log(` 🔴 ${s}`);
233
+ for (const s of report.summary.yellow.slice(0, 12)) console.log(` 🟡 ${s}`);
234
+ if (report.summary.unadjudicated.length) console.log(` ?待裁决依赖 ${report.summary.unadjudicated.length} 个(流程裁决步定夺后回写缓存):${report.summary.unadjudicated.slice(0, 5).join('; ')}${report.summary.unadjudicated.length > 5 ? '…' : ''}`);
235
+ process.exit(GATE ? ({ red: 5, yellow: 4, green: 0 })[verdict] : 0);
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": "0.1.0",
3
+ "_doc": "跨端框架检测表(数据即语料)。命中即整单分诊为 red-reroute:这类应用的安卓层只是壳,按原生流程重写是错路——正确处置是改走对应框架的鸿蒙方案。marker 匹配依赖坐标/文件路径/清单内容。",
4
+ "entries": [
5
+ { "framework": "Flutter", "marker": "io\\.flutter|libflutter\\.so|flutter_assets", "disposition": "改走 Flutter-ohos(官方鸿蒙支持)或 ArkUI-X 评估;原生重写=重做整个 Dart 层,错路" },
6
+ { "framework": "React Native", "marker": "com\\.facebook\\.react|index\\.android\\.bundle|libreactnative", "disposition": "改走 react-native-harmony(RNOH);JS 业务层可大部复用" },
7
+ { "framework": "Taro/小程序壳", "marker": "com\\.tarojs|taro-runtime", "disposition": "Taro 鸿蒙端编译目标评估" },
8
+ { "framework": "uni-app", "marker": "io\\.dcloud|uni-app", "disposition": "uni-app x 鸿蒙目标评估" },
9
+ { "framework": "Cordova/WebView 壳", "marker": "org\\.apache\\.cordova|cordova\\.js", "disposition": "鸿蒙 ArkWeb 壳 + 原 H5 资源复用,仅重写桥接层" }
10
+ ]
11
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": "0.1.0",
3
+ "_doc": "废弃/过时 API → 替代写法对照表(数据即语料;官方 hmos-arkts-deprecated-interface-checker 的蒸馏落点)。当前只建表未接检查器——接入闭合检查时须配基准靶再启用。match 匹配 .ets 源码文本;[待查证] 条目首用按本工程 SDK 核实。种子 4 条蒸馏自内部构建错误材料。",
4
+ "entries": [
5
+ { "match": "@ohos\\.router|(?<![\\w.])router\\.(pushUrl|replaceUrl|back)\\(", "deprecated": "@ohos.router 页面路由", "replacement": "Navigation + NavPathStack(壳层路由)", "note": "官方将 @ohos.router 标为“不推荐”(未正式 deprecated),推荐 Navigation 作为路由框架;新工程路由一律 Navigation 体系" },
6
+ { "match": "getContext\\(this\\)|getContext\\(\\)", "deprecated": "getContext(this) 取上下文", "replacement": "this.getUIContext().getHostContext()", "note": "组件内取 UIAbilityContext 走 UIContext 实例链" },
7
+ { "match": "AlertDialog\\.show\\(|(?<![\\w.])promptAction\\.(showToast|showDialog|openCustomDialog)\\(", "deprecated": "AlertDialog.show / promptAction 全局形", "replacement": "UIContext 实例形(this.getUIContext().showAlertDialog(...) / this.getUIContext().getPromptAction().showToast|showDialog|openCustomDialog(...))", "note": "AlertDialog.show 与 promptAction 全局形自 API 18 废弃(官方 API 参考已核实);改 UIContext 实例调用" },
8
+ { "match": "from ['\"]@ohos\\.", "deprecated": "@ohos.* 裸模块导入", "replacement": "@kit.* 聚合导入(如 @kit.ArkUI / @kit.AbilityKit / @kit.CoreFileKit)", "note": "Kit 聚合是官方推荐导入形态;逐模块对照 Kit 归属" }
9
+ ]
10
+ }