ai-scaffold-pro 1.1.1
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/bin/cli.js +86 -0
- package/hooks/check-review-needed.sh +28 -0
- package/hooks/post-edit-tracker.sh +21 -0
- package/i18n/en.json +536 -0
- package/i18n/index.js +27 -0
- package/i18n/zh.json +536 -0
- package/package.json +29 -0
- package/scripts/gen_references.py +877 -0
- package/src/detect.js +320 -0
- package/src/platforms/android.js +206 -0
- package/src/platforms/default.js +26 -0
- package/src/platforms/flutter.js +278 -0
- package/src/platforms/harmonyos.js +26 -0
- package/src/platforms/index.js +40 -0
- package/src/platforms/ios.js +252 -0
- package/src/platforms/react-native.js +26 -0
- package/src/prompts.js +191 -0
- package/src/render.js +459 -0
- package/templates/CHANGELOG.md +21 -0
- package/templates/agents/arch-review.md +78 -0
- package/templates/agents/cpp-memory-review.md +84 -0
- package/templates/agents/proactive-correction.md +204 -0
- package/templates/agents/resource-sync.md +34 -0
- package/templates/rules/conflict_resolution.md +39 -0
- package/templates/rules/project_rule.md +210 -0
- package/templates/settings.json +27 -0
- package/templates/settings.local.json +5 -0
- package/templates/skills/code_review/SKILL.md +117 -0
- package/templates/skills/plan_mode/SKILL.md +92 -0
package/src/detect.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
// AI 工具检测配置
|
|
6
|
+
const AI_TOOLS = {
|
|
7
|
+
'.qoder': { target: 'qoder', dir: '.qoder', entry: 'AGENTS.md' },
|
|
8
|
+
'CLAUDE.md': { target: 'claude', dir: '.claude', entry: 'CLAUDE.md' },
|
|
9
|
+
'.codex': { target: 'codex', dir: '.codex', entry: 'AGENTS.md' },
|
|
10
|
+
'.opencode': { target: 'opencode', dir: '.opencode', entry: 'AGENTS.md' },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// 平台指标优先级配置(priority 越高越优先)
|
|
14
|
+
const PLATFORM_INDICATORS = [
|
|
15
|
+
{ file: 'hvigor-config.json5', platform: 'HarmonyOS', build: 'Hvigor', priority: 100 },
|
|
16
|
+
{ file: 'settings.gradle.kts', platform: 'Android', build: 'Gradle Kotlin DSL', priority: 90 },
|
|
17
|
+
{ file: 'settings.gradle', platform: 'Android', build: 'Gradle', priority: 89 },
|
|
18
|
+
{ file: 'pubspec.yaml', platform: 'Flutter', build: 'Flutter', priority: 85 },
|
|
19
|
+
{ file: 'Podfile', platform: 'iOS', build: 'CocoaPods', priority: 80 },
|
|
20
|
+
{ file: 'Package.swift', platform: 'iOS', build: 'SPM', priority: 80 },
|
|
21
|
+
{ file: 'Cargo.toml', platform: 'Rust', build: 'Cargo', priority: 70 },
|
|
22
|
+
{ file: 'go.mod', platform: 'Go', build: 'Go Modules', priority: 70 },
|
|
23
|
+
{ file: 'pyproject.toml', platform: 'Python', build: 'Poetry/pip', priority: 61 },
|
|
24
|
+
{ file: 'requirements.txt', platform: 'Python', build: 'pip', priority: 60 },
|
|
25
|
+
{ file: 'package.json', platform: null, build: null, priority: 10 },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 检测项目平台、构建系统、NDK、语言和已有 AI 工具
|
|
30
|
+
*/
|
|
31
|
+
export async function detectProject(targetDir) {
|
|
32
|
+
const result = {
|
|
33
|
+
platform: 'Unknown',
|
|
34
|
+
buildSystem: 'Unknown',
|
|
35
|
+
hasNdk: false,
|
|
36
|
+
hasCodeGraph: false,
|
|
37
|
+
existingTool: null,
|
|
38
|
+
language: 'Unknown',
|
|
39
|
+
allDetected: [],
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// 1. 检测已有 AI 工具
|
|
43
|
+
for (const [indicator, tool] of Object.entries(AI_TOOLS)) {
|
|
44
|
+
if (fs.existsSync(path.join(targetDir, indicator))) {
|
|
45
|
+
result.existingTool = tool;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2. 收集所有匹配的平台指标
|
|
51
|
+
const matched = [];
|
|
52
|
+
for (const indicator of PLATFORM_INDICATORS) {
|
|
53
|
+
if (fs.existsSync(path.join(targetDir, indicator.file))) {
|
|
54
|
+
matched.push(indicator);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 按优先级降序排序
|
|
59
|
+
matched.sort((a, b) => b.priority - a.priority);
|
|
60
|
+
|
|
61
|
+
// 输出检测日志
|
|
62
|
+
if (matched.length > 0) {
|
|
63
|
+
console.log(chalk.gray(' 检测到以下项目指标:'));
|
|
64
|
+
for (const m of matched) {
|
|
65
|
+
const label = m.platform || 'package.json';
|
|
66
|
+
console.log(chalk.gray(` • ${m.file} → ${label} (priority: ${m.priority})`));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 3. 确定平台
|
|
71
|
+
if (matched.length > 0) {
|
|
72
|
+
const top = matched[0];
|
|
73
|
+
|
|
74
|
+
if (top.platform === 'Android') {
|
|
75
|
+
// 区分 Android 和纯 JVM Gradle
|
|
76
|
+
const isAndroid = verifyAndroidProject(targetDir);
|
|
77
|
+
if (isAndroid) {
|
|
78
|
+
result.platform = 'Android';
|
|
79
|
+
result.buildSystem = top.build;
|
|
80
|
+
} else {
|
|
81
|
+
result.platform = 'JVM';
|
|
82
|
+
result.buildSystem = top.build;
|
|
83
|
+
}
|
|
84
|
+
} else if (top.platform === null && top.file === 'package.json') {
|
|
85
|
+
// package.json 深度检测
|
|
86
|
+
const detected = detectFromPackageJson(targetDir);
|
|
87
|
+
result.platform = detected.platform;
|
|
88
|
+
result.buildSystem = detected.build;
|
|
89
|
+
} else {
|
|
90
|
+
result.platform = top.platform;
|
|
91
|
+
result.buildSystem = top.build;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 记录所有检测到的平台
|
|
95
|
+
result.allDetected = matched.map(m => ({
|
|
96
|
+
platform: m.platform,
|
|
97
|
+
build: m.build,
|
|
98
|
+
priority: m.priority,
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
// 混合项目日志
|
|
102
|
+
if (matched.length > 1) {
|
|
103
|
+
console.log(chalk.yellow(` ⚠ 检测到多个平台指标,已选择最高优先级: ${result.platform} (${result.buildSystem})`));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 4. 检测 NDK/C++
|
|
108
|
+
result.hasNdk = detectNdk(targetDir);
|
|
109
|
+
|
|
110
|
+
// 5. 检测语言
|
|
111
|
+
result.language = detectLanguage(targetDir, result.platform, result.buildSystem);
|
|
112
|
+
|
|
113
|
+
// 6. 检测 CodeGraph
|
|
114
|
+
result.hasCodeGraph = detectCodeGraph();
|
|
115
|
+
if (result.hasCodeGraph) {
|
|
116
|
+
console.log(chalk.gray(' CodeGraph: detected'));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 验证是否为 Android 项目(区别于纯 JVM Gradle 项目)
|
|
124
|
+
*/
|
|
125
|
+
function verifyAndroidProject(targetDir) {
|
|
126
|
+
// 检查 AndroidManifest.xml
|
|
127
|
+
if (fs.existsSync(path.join(targetDir, 'app/src/main/AndroidManifest.xml'))) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 检查根 build.gradle(.kts) 中是否包含 com.android 插件
|
|
132
|
+
const buildFiles = ['build.gradle.kts', 'build.gradle'];
|
|
133
|
+
for (const file of buildFiles) {
|
|
134
|
+
const filePath = path.join(targetDir, file);
|
|
135
|
+
if (fs.existsSync(filePath)) {
|
|
136
|
+
try {
|
|
137
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
138
|
+
if (content.includes('com.android')) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
} catch {}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 检查 app/build.gradle(.kts) 中是否包含 com.android 插件
|
|
146
|
+
const appBuildFiles = ['app/build.gradle.kts', 'app/build.gradle'];
|
|
147
|
+
for (const file of appBuildFiles) {
|
|
148
|
+
const filePath = path.join(targetDir, file);
|
|
149
|
+
if (fs.existsSync(filePath)) {
|
|
150
|
+
try {
|
|
151
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
152
|
+
if (content.includes('com.android')) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
} catch {}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* package.json 深度检测:基于依赖推断具体前端/Node 框架
|
|
164
|
+
*/
|
|
165
|
+
function detectFromPackageJson(targetDir) {
|
|
166
|
+
try {
|
|
167
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(targetDir, 'package.json'), 'utf-8'));
|
|
168
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
169
|
+
|
|
170
|
+
if (allDeps['react-native']) return { platform: 'React Native', build: 'Metro' };
|
|
171
|
+
if (allDeps['next']) return { platform: 'Next.js', build: 'Next.js' };
|
|
172
|
+
if (allDeps['nuxt']) return { platform: 'Nuxt', build: 'Nuxt' };
|
|
173
|
+
if (allDeps['react']) return { platform: 'React', build: 'Webpack/Vite' };
|
|
174
|
+
if (allDeps['vue']) return { platform: 'Vue', build: 'Vite' };
|
|
175
|
+
return { platform: 'Node', build: 'npm' };
|
|
176
|
+
} catch {
|
|
177
|
+
return { platform: 'Node', build: 'npm' };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 增强 NDK 检测:检查根目录和 Gradle 模块子目录
|
|
183
|
+
*/
|
|
184
|
+
function detectNdk(targetDir) {
|
|
185
|
+
// 根目录检测
|
|
186
|
+
const rootIndicators = [
|
|
187
|
+
'jni',
|
|
188
|
+
'CMakeLists.txt',
|
|
189
|
+
'Android.mk',
|
|
190
|
+
'app/src/main/cpp',
|
|
191
|
+
'app/src/main/jni',
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
if (rootIndicators.some(f => fs.existsSync(path.join(targetDir, f)))) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 检查 .cpp/.c/.h 文件是否存在于根目录
|
|
199
|
+
if (hasFileWithExt(targetDir, ['.cpp', '.c', '.h'])) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// 检查 Gradle 模块子目录中的 NDK 文件
|
|
204
|
+
try {
|
|
205
|
+
const modules = getGradleModules(targetDir);
|
|
206
|
+
for (const mod of modules) {
|
|
207
|
+
const modDir = path.join(targetDir, mod.replace(/:/g, '/'));
|
|
208
|
+
const modIndicators = ['src/main/cpp', 'src/main/jni', 'CMakeLists.txt', 'Android.mk'];
|
|
209
|
+
if (modIndicators.some(f => fs.existsSync(path.join(modDir, f)))) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
} catch {}
|
|
214
|
+
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 解析 settings.gradle(.kts) 获取模块列表
|
|
220
|
+
*/
|
|
221
|
+
function getGradleModules(targetDir) {
|
|
222
|
+
const files = ['settings.gradle.kts', 'settings.gradle'];
|
|
223
|
+
for (const file of files) {
|
|
224
|
+
const filePath = path.join(targetDir, file);
|
|
225
|
+
if (fs.existsSync(filePath)) {
|
|
226
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
227
|
+
const modules = [];
|
|
228
|
+
// 匹配 include(":module") 和 include ':module' 和 includeFlat 'module'
|
|
229
|
+
const includeRegex = /include\s*\(?["':]+([^"')]+)["']\)?/g;
|
|
230
|
+
const flatRegex = /includeFlat\s*\(?["']([^"']+)["']\)?/g;
|
|
231
|
+
let match;
|
|
232
|
+
while ((match = includeRegex.exec(content))) modules.push(match[1]);
|
|
233
|
+
while ((match = flatRegex.exec(content))) modules.push(match[1]);
|
|
234
|
+
return modules;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* 语言检测:基于构建文件推断主要开发语言
|
|
242
|
+
*/
|
|
243
|
+
function detectLanguage(targetDir, platform, buildSystem) {
|
|
244
|
+
// 基于已知平台的快速推断
|
|
245
|
+
if (platform === 'Flutter') return 'Dart';
|
|
246
|
+
if (platform === 'Rust') return 'Rust';
|
|
247
|
+
if (platform === 'Go') return 'Go';
|
|
248
|
+
if (platform === 'Python') return 'Python';
|
|
249
|
+
if (platform === 'HarmonyOS') return 'ArkTS';
|
|
250
|
+
if (platform === 'iOS') return 'Swift';
|
|
251
|
+
if (platform === 'React Native' || platform === 'Next.js' || platform === 'Nuxt' ||
|
|
252
|
+
platform === 'React' || platform === 'Vue' || platform === 'Node') {
|
|
253
|
+
return 'TypeScript/JavaScript';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Android / JVM 项目的语言检测
|
|
257
|
+
if (platform === 'Android' || platform === 'JVM') {
|
|
258
|
+
if (fs.existsSync(path.join(targetDir, 'build.gradle.kts'))) {
|
|
259
|
+
return 'Kotlin';
|
|
260
|
+
}
|
|
261
|
+
if (fs.existsSync(path.join(targetDir, 'app/build.gradle.kts'))) {
|
|
262
|
+
return 'Kotlin';
|
|
263
|
+
}
|
|
264
|
+
// 检查 build.gradle 中是否应用了 kotlin 插件
|
|
265
|
+
const buildGradlePath = path.join(targetDir, 'build.gradle');
|
|
266
|
+
if (fs.existsSync(buildGradlePath)) {
|
|
267
|
+
try {
|
|
268
|
+
const content = fs.readFileSync(buildGradlePath, 'utf-8');
|
|
269
|
+
if (content.includes('kotlin') || content.includes('org.jetbrains.kotlin')) {
|
|
270
|
+
return 'Kotlin';
|
|
271
|
+
}
|
|
272
|
+
} catch {}
|
|
273
|
+
}
|
|
274
|
+
return 'Java/Kotlin';
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 兜底:通过文件存在性推断
|
|
278
|
+
if (fs.existsSync(path.join(targetDir, 'build.gradle.kts'))) return 'Kotlin';
|
|
279
|
+
if (fs.existsSync(path.join(targetDir, 'build.gradle'))) return 'Java/Kotlin';
|
|
280
|
+
if (fs.existsSync(path.join(targetDir, 'pubspec.yaml'))) return 'Dart';
|
|
281
|
+
if (fs.existsSync(path.join(targetDir, 'Package.swift'))) return 'Swift';
|
|
282
|
+
if (fs.existsSync(path.join(targetDir, 'package.json'))) return 'TypeScript/JavaScript';
|
|
283
|
+
if (fs.existsSync(path.join(targetDir, 'Cargo.toml'))) return 'Rust';
|
|
284
|
+
if (fs.existsSync(path.join(targetDir, 'go.mod'))) return 'Go';
|
|
285
|
+
if (fs.existsSync(path.join(targetDir, 'pyproject.toml')) || fs.existsSync(path.join(targetDir, 'requirements.txt'))) return 'Python';
|
|
286
|
+
|
|
287
|
+
return 'Unknown';
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 递归检查目录中是否有指定扩展名的文件
|
|
292
|
+
*/
|
|
293
|
+
function hasFileWithExt(dir, extensions, maxDepth = 3) {
|
|
294
|
+
try {
|
|
295
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
296
|
+
for (const entry of entries) {
|
|
297
|
+
if (entry.name.startsWith('.')) continue;
|
|
298
|
+
const fullPath = path.join(dir, entry.name);
|
|
299
|
+
if (entry.isFile() && extensions.some(ext => entry.name.endsWith(ext))) {
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
if (entry.isDirectory() && maxDepth > 0) {
|
|
303
|
+
if (hasFileWithExt(fullPath, extensions, maxDepth - 1)) return true;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} catch {}
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* 检测 CodeGraph CLI 是否已全局安装
|
|
312
|
+
*/
|
|
313
|
+
function detectCodeGraph() {
|
|
314
|
+
try {
|
|
315
|
+
execSync('codegraph --version', { stdio: 'pipe', timeout: 3000 });
|
|
316
|
+
return true;
|
|
317
|
+
} catch {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
const strings = {
|
|
2
|
+
zh: {
|
|
3
|
+
PLATFORM_RULES_SUMMARY: 'Android: MVVM架构, ARouter路由, Retrofit网络, ViewBinding, Paging3',
|
|
4
|
+
|
|
5
|
+
PLATFORM_SPECIFIC_RULES: `### 5.1 生命周期
|
|
6
|
+
|
|
7
|
+
- 禁止在 \`onCreate()\` / \`onViewCreated()\` 中直接执行耗时操作
|
|
8
|
+
- \`LiveData\` 观察必须使用 \`viewLifecycleOwner\`(Fragment 中)
|
|
9
|
+
- \`onDestroy()\` / \`onDestroyView()\` 必须清理所有注册的监听器和回调
|
|
10
|
+
- ViewModel 中禁止持有 Activity/Fragment 引用
|
|
11
|
+
- 使用 \`ViewModelProvider\` 或 Hilt 注入获取 ViewModel,禁止手动 new
|
|
12
|
+
|
|
13
|
+
### 5.2 路由(ARouter)
|
|
14
|
+
|
|
15
|
+
- 所有可跳转页面必须使用 \`@Route(path = "/模块/页面")\` 注解
|
|
16
|
+
- 路由路径格式:\`/{feature-module}/{page}\`,如 \`/home/main\`, \`/product/detail\`
|
|
17
|
+
- 跨模块传参使用 \`ARouter\` 的 \`withXxx()\` 方法,禁止 \`Bundle\` 直接传递自定义对象
|
|
18
|
+
- 路由拦截器使用 \`IInterceptor\`,统一处理鉴权、埋点等逻辑
|
|
19
|
+
- 禁止使用 \`Intent\` 直接启动跨模块 Activity
|
|
20
|
+
|
|
21
|
+
### 5.3 网络请求
|
|
22
|
+
|
|
23
|
+
- 所有网络请求通过 \`Retrofit\` + \`suspend\` 协程执行
|
|
24
|
+
- 请求必须在 \`ViewModel.viewModelScope\` 中发起
|
|
25
|
+
- 响应必须经过统一的 \`ApiResponse<T>\` 封装处理
|
|
26
|
+
- 禁止在 View 层直接发起网络请求
|
|
27
|
+
- 错误处理统一在 Repository 层捕获,通过 sealed class 传递状态
|
|
28
|
+
- 禁止使用 \`HttpURLConnection\` / \`OkHttp\` 直接调用
|
|
29
|
+
|
|
30
|
+
### 5.4 RecyclerView
|
|
31
|
+
|
|
32
|
+
- 使用 \`ListAdapter\` + \`DiffUtil\` 实现列表,禁止 \`notifyDataSetChanged()\`
|
|
33
|
+
- ViewHolder 必须使用 \`ViewBinding\`
|
|
34
|
+
- 列表数据使用 \`Paging 3\` 分页加载
|
|
35
|
+
- 列表项点击事件通过接口回调传递,禁止在 Adapter 中直接处理业务逻辑
|
|
36
|
+
|
|
37
|
+
### 5.5 多语言
|
|
38
|
+
|
|
39
|
+
- 所有用户可见字符串必须放入 \`strings.xml\`,支持中/英双语
|
|
40
|
+
- 禁止在代码中直接拼接 UI 字符串
|
|
41
|
+
- 日期/数字格式化使用 \`DateFormat\` / \`NumberFormat\`
|
|
42
|
+
- 布局文件中的硬编码字符串也必须替换为 \`@string/xxx\``,
|
|
43
|
+
|
|
44
|
+
PLATFORM_FATAL_CHECKS: `- 在主线程执行网络请求或数据库查询
|
|
45
|
+
- 跨模块使用 \`Intent\` 直接启动 Activity(应使用 ARouter)
|
|
46
|
+
- Activity/Fragment 未继承正确的 Base 类
|
|
47
|
+
- 缺少 \`@Route\` 注解的可跳转页面
|
|
48
|
+
- Fragment 中 LiveData 观察未使用 \`viewLifecycleOwner\`
|
|
49
|
+
- ViewModel 持有 Activity/Fragment 引用`,
|
|
50
|
+
|
|
51
|
+
PLATFORM_WARNING_CHECKS: `- 硬编码颜色值(应使用 \`R.color.xxx\`)
|
|
52
|
+
- 硬编码尺寸 \`XXdp\` / \`XXsp\`(应使用 \`R.dimen.xxx\`)
|
|
53
|
+
- 使用 \`notifyDataSetChanged()\`(应使用 \`DiffUtil\`)
|
|
54
|
+
- 硬编码字符串(应使用 \`R.string.xxx\`)
|
|
55
|
+
- LiveData 观察使用 \`this\` 而非 \`viewLifecycleOwner\`(Fragment 中)
|
|
56
|
+
- 使用 \`Log.d()\` / \`Log.e()\` 直接调用(应使用统一日志工具)`,
|
|
57
|
+
|
|
58
|
+
PLATFORM_SUGGESTION_CHECKS: `- 使用 \`ListAdapter\` + \`DiffUtil\` 替代 BaseAdapter
|
|
59
|
+
- ViewHolder 使用 ViewBinding 替代 findViewById
|
|
60
|
+
- 列表使用 Paging 3 分页加载
|
|
61
|
+
- 使用 weak reference 处理回调
|
|
62
|
+
- 使用统一的日志工具类`,
|
|
63
|
+
|
|
64
|
+
PLATFORM_SELF_CHECK_ITEMS: `| A1 | Activity/Fragment 继承正确的 Base 类? |
|
|
65
|
+
| A2 | ARouter 路径已添加 @Route 注解? |
|
|
66
|
+
| A3 | 资源命名前缀符合规范? |
|
|
67
|
+
| A4 | LiveData 观察使用 viewLifecycleOwner? |
|
|
68
|
+
| A5 | 网络请求在 ViewModelScope 中发起? |
|
|
69
|
+
| A6 | 无硬编码字符串/颜色/尺寸? |
|
|
70
|
+
| A7 | RecyclerView 使用 ListAdapter + DiffUtil? |
|
|
71
|
+
| A8 | 日志使用统一工具类? |`,
|
|
72
|
+
|
|
73
|
+
PLATFORM_RESOURCE_SYNC_CONTENT: `#### Android 资源同步检查
|
|
74
|
+
|
|
75
|
+
- drawable 密度变体齐全(mdpi/hdpi/xhdpi/xxhdpi/xxxhdpi)
|
|
76
|
+
- 布局限定词目录一致(layout-land, layout-sw600dp 等)
|
|
77
|
+
- values 语言目录一致(values-zh, values-en 等)
|
|
78
|
+
- mipmap 密度变体齐全(启动图标)
|
|
79
|
+
- 资源命名前缀符合项目规范`,
|
|
80
|
+
|
|
81
|
+
PLATFORM_SPECIFIC_TASK_TEMPLATES: `#### Android 任务模板
|
|
82
|
+
|
|
83
|
+
**新增页面**:
|
|
84
|
+
1. 创建 Activity/Fragment(继承 Base 类)
|
|
85
|
+
2. 创建对应 ViewModel(继承 BaseViewModel)
|
|
86
|
+
3. 创建布局文件(命名: activity_xxx.xml / fragment_xxx.xml)
|
|
87
|
+
4. 添加 @Route 注解
|
|
88
|
+
5. 创建对应的 ApiService 和 Repository(如需网络请求)
|
|
89
|
+
|
|
90
|
+
**新增 API 接口**:
|
|
91
|
+
1. 在 ApiService 中定义接口方法
|
|
92
|
+
2. 创建 Repository 封装调用逻辑
|
|
93
|
+
3. 在 ViewModel 中通过 suspend 调用
|
|
94
|
+
4. 通过 LiveData 暴露结果
|
|
95
|
+
|
|
96
|
+
**新增 RecyclerView 列表**:
|
|
97
|
+
1. 创建数据模型
|
|
98
|
+
2. 创建 ViewHolder(使用 ViewBinding)
|
|
99
|
+
3. 创建 ListAdapter + DiffUtil.ItemCallback
|
|
100
|
+
4. 配置 Paging 3(如需分页)`,
|
|
101
|
+
},
|
|
102
|
+
en: {
|
|
103
|
+
PLATFORM_RULES_SUMMARY: 'Android: MVVM, ARouter navigation, Retrofit networking, ViewBinding, Paging 3',
|
|
104
|
+
|
|
105
|
+
PLATFORM_SPECIFIC_RULES: `### 5.1 Lifecycle
|
|
106
|
+
|
|
107
|
+
- Do NOT perform heavy operations directly in \`onCreate()\` / \`viewCreated()\`
|
|
108
|
+
- LiveData observation must use \`viewLifecycleOwner\` (in Fragments)
|
|
109
|
+
- \`onDestroy()\` / \`onDestroyView()\` must clean up all registered listeners and callbacks
|
|
110
|
+
- ViewModel must NOT hold Activity/Fragment references
|
|
111
|
+
- Obtain ViewModel via \`ViewModelProvider\` or Hilt injection; creating with \`new\` is prohibited
|
|
112
|
+
|
|
113
|
+
### 5.2 Routing (ARouter)
|
|
114
|
+
|
|
115
|
+
- All navigable pages must use \`@Route(path = "/module/page")\` annotation
|
|
116
|
+
- Route path format: \`/{feature-module}/{page}\`, e.g., \`/home/main\`, \`/product/detail\`
|
|
117
|
+
- Cross-module parameters pass via \`ARouter\` \`withXxx()\` methods; direct \`Bundle\` for custom objects is prohibited
|
|
118
|
+
- Route interceptors use \`IInterceptor\` for unified auth, analytics, etc.
|
|
119
|
+
- Using \`Intent\` to directly launch cross-module Activities is prohibited
|
|
120
|
+
|
|
121
|
+
### 5.3 Network Requests
|
|
122
|
+
|
|
123
|
+
- All network requests use \`Retrofit\` + \`suspend\` coroutines
|
|
124
|
+
- Requests must execute within \`ViewModel.viewModelScope\`
|
|
125
|
+
- Responses must be wrapped in unified \`ApiResponse<T>\`
|
|
126
|
+
- Network requests directly in View layer are prohibited
|
|
127
|
+
- Error handling unified at Repository layer via sealed class
|
|
128
|
+
- Direct \`HttpURLConnection\` / \`OkHttp\` usage is prohibited
|
|
129
|
+
|
|
130
|
+
### 5.4 RecyclerView
|
|
131
|
+
|
|
132
|
+
- Use \`ListAdapter\` + \`DiffUtil\` for lists; \`notifyDataSetChanged()\` is prohibited
|
|
133
|
+
- ViewHolder must use \`ViewBinding\`
|
|
134
|
+
- List data uses \`Paging 3\` for pagination
|
|
135
|
+
- List item click events pass via interface callback; direct business logic in Adapter is prohibited
|
|
136
|
+
|
|
137
|
+
### 5.5 Internationalization
|
|
138
|
+
|
|
139
|
+
- All user-visible strings must go into \`strings.xml\`, supporting Chinese/English
|
|
140
|
+
- Direct string concatenation in code for UI is prohibited
|
|
141
|
+
- Date/number formatting uses \`DateFormat\` / \`NumberFormat\`
|
|
142
|
+
- Hardcoded strings in layout files must be replaced with \`@string/xxx\``,
|
|
143
|
+
|
|
144
|
+
PLATFORM_FATAL_CHECKS: `- Network request or database query on main thread
|
|
145
|
+
- Cross-module Activity launch via \`Intent\` (should use ARouter)
|
|
146
|
+
- Activity/Fragment not inheriting correct Base class
|
|
147
|
+
- Navigable pages missing \`@Route\` annotation
|
|
148
|
+
- LiveData observation in Fragment not using \`viewLifecycleOwner\`
|
|
149
|
+
- ViewModel holding Activity/Fragment reference`,
|
|
150
|
+
|
|
151
|
+
PLATFORM_WARNING_CHECKS: `- Hardcoded color values (should use \`R.color.xxx\`)
|
|
152
|
+
- Hardcoded dimensions \`XXdp\` / \`XXsp\` (should use \`R.dimen.xxx\`)
|
|
153
|
+
- Using \`notifyDataSetChanged()\` (should use \`DiffUtil\`)
|
|
154
|
+
- Hardcoded strings (should use \`R.string.xxx\`)
|
|
155
|
+
- LiveData observation using \`this\` instead of \`viewLifecycleOwner\` (in Fragment)
|
|
156
|
+
- Direct \`Log.d()\` / \`Log.e()\` calls (should use unified logging tool)`,
|
|
157
|
+
|
|
158
|
+
PLATFORM_SUGGESTION_CHECKS: `- Use \`ListAdapter\` + \`DiffUtil\` instead of BaseAdapter
|
|
159
|
+
- ViewHolder uses ViewBinding instead of findViewById
|
|
160
|
+
- Lists use Paging 3 for pagination
|
|
161
|
+
- Use weak reference for callbacks
|
|
162
|
+
- Use unified logging utility class`,
|
|
163
|
+
|
|
164
|
+
PLATFORM_SELF_CHECK_ITEMS: `| A1 | Activity/Fragment inherits correct Base class? |
|
|
165
|
+
| A2 | @Route annotation added for navigable pages? |
|
|
166
|
+
| A3 | Resource naming prefix compliant? |
|
|
167
|
+
| A4 | LiveData observation uses viewLifecycleOwner? |
|
|
168
|
+
| A5 | Network requests execute in ViewModelScope? |
|
|
169
|
+
| A6 | No hardcoded strings/colors/dimensions? |
|
|
170
|
+
| A7 | RecyclerView uses ListAdapter + DiffUtil? |
|
|
171
|
+
| A8 | Logging uses unified utility class? |`,
|
|
172
|
+
|
|
173
|
+
PLATFORM_RESOURCE_SYNC_CONTENT: `#### Android Resource Sync Checks
|
|
174
|
+
|
|
175
|
+
- Drawable density variants complete (mdpi/hdpi/xhdpi/xxhdpi/xxxhdpi)
|
|
176
|
+
- Layout qualifier directories consistent (layout-land, layout-sw600dp, etc.)
|
|
177
|
+
- Values language directories consistent (values-zh, values-en, etc.)
|
|
178
|
+
- Mipmap density variants complete (launcher icons)
|
|
179
|
+
- Resource naming prefix compliant with project standards`,
|
|
180
|
+
|
|
181
|
+
PLATFORM_SPECIFIC_TASK_TEMPLATES: `#### Android Task Templates
|
|
182
|
+
|
|
183
|
+
**Add new page**:
|
|
184
|
+
1. Create Activity/Fragment (inheriting Base class)
|
|
185
|
+
2. Create corresponding ViewModel (inheriting BaseViewModel)
|
|
186
|
+
3. Create layout file (naming: activity_xxx.xml / fragment_xxx.xml)
|
|
187
|
+
4. Add @Route annotation
|
|
188
|
+
5. Create ApiService and Repository (if network needed)
|
|
189
|
+
|
|
190
|
+
**Add API endpoint**:
|
|
191
|
+
1. Define method in ApiService
|
|
192
|
+
2. Create Repository wrapper
|
|
193
|
+
3. Call via suspend in ViewModel
|
|
194
|
+
4. Expose result via LiveData
|
|
195
|
+
|
|
196
|
+
**Add RecyclerView list**:
|
|
197
|
+
1. Create data model
|
|
198
|
+
2. Create ViewHolder (with ViewBinding)
|
|
199
|
+
3. Create ListAdapter + DiffUtil.ItemCallback
|
|
200
|
+
4. Configure Paging 3 (if pagination needed)`,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export default function getPlatformVars(lang) {
|
|
205
|
+
return strings[lang] || strings.en;
|
|
206
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const strings = {
|
|
2
|
+
zh: {
|
|
3
|
+
PLATFORM_SPECIFIC_RULES: '请根据项目实际平台补充专项规则',
|
|
4
|
+
PLATFORM_FATAL_CHECKS: '',
|
|
5
|
+
PLATFORM_WARNING_CHECKS: '',
|
|
6
|
+
PLATFORM_SUGGESTION_CHECKS: '',
|
|
7
|
+
PLATFORM_SELF_CHECK_ITEMS: '',
|
|
8
|
+
PLATFORM_RESOURCE_SYNC_CONTENT: '请根据项目实际平台补充资源同步检查项',
|
|
9
|
+
PLATFORM_SPECIFIC_TASK_TEMPLATES: '请根据项目实际平台补充任务模板',
|
|
10
|
+
PLATFORM_RULES_SUMMARY: '请根据项目实际平台补充规则摘要',
|
|
11
|
+
},
|
|
12
|
+
en: {
|
|
13
|
+
PLATFORM_SPECIFIC_RULES: 'Add platform-specific rules based on your project',
|
|
14
|
+
PLATFORM_FATAL_CHECKS: '',
|
|
15
|
+
PLATFORM_WARNING_CHECKS: '',
|
|
16
|
+
PLATFORM_SUGGESTION_CHECKS: '',
|
|
17
|
+
PLATFORM_SELF_CHECK_ITEMS: '',
|
|
18
|
+
PLATFORM_RESOURCE_SYNC_CONTENT: 'Add platform-specific resource sync checks based on your project',
|
|
19
|
+
PLATFORM_SPECIFIC_TASK_TEMPLATES: 'Add platform-specific task templates based on your project',
|
|
20
|
+
PLATFORM_RULES_SUMMARY: 'Add platform-specific rules summary based on your project',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default function getPlatformVars(lang) {
|
|
25
|
+
return strings[lang] || strings.en;
|
|
26
|
+
}
|