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/src/render.js ADDED
@@ -0,0 +1,459 @@
1
+ import Handlebars from 'handlebars';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import chalk from 'chalk';
6
+ import { getPlatformVars } from './platforms/index.js';
7
+ import { registerI18n } from '../i18n/index.js';
8
+
9
+ // Register helpers
10
+ Handlebars.registerHelper('if', function (conditional, options) {
11
+ if (conditional) {
12
+ return options.fn(this);
13
+ }
14
+ return options.inverse(this);
15
+ });
16
+
17
+ const TODAY = new Date().toISOString().slice(0, 10).replace(/-/g, '.');
18
+
19
+ function getSmartDefaults(platform, lang) {
20
+ const defaults = {
21
+ Android: {
22
+ zh: {
23
+ DEPENDENCY_RULES: 'App → Feature → Common → Base(单向依赖,禁止反向引用)',
24
+ COMMUNICATION_MECHANISM: 'ARouter 跨模块路由 + EventBus/LiveData 事件通信',
25
+ INHERITANCE_RULES: 'Activity 继承 BaseActivity,Fragment 继承 BaseFragment,ViewModel 继承 BaseViewModel',
26
+ FORBIDDEN_PATTERNS_TABLE: '| 1 | 硬编码颜色值 | 使用 colors.xml 资源引用 | 主题适配无法统一 |\n| 2 | 直接 Intent 跳转 | 使用 ARouter | 模块解耦 |\n| 3 | 主线程网络请求 | 使用 suspend + Dispatchers.IO | ANR 风险 |\n| 4 | 非 ViewBinding 视图访问 | 使用 ViewBinding | 空指针风险 |',
27
+ CLASS_NAMING: 'Activity: 功能名+Activity (如 LoginActivity)\nFragment: 功能名+Fragment\nViewModel: 功能名+ViewModel\nAdapter: 功能名+Adapter',
28
+ RESOURCE_PREFIX: '模块名_ (如 home_btn_submit, login_tv_title)',
29
+ LAYOUT_NAMING: '类型_模块_描述 (如 activity_login, fragment_home, item_user_list)',
30
+ },
31
+ en: {
32
+ DEPENDENCY_RULES: 'App → Feature → Common → Base (unidirectional, no reverse references)',
33
+ COMMUNICATION_MECHANISM: 'ARouter cross-module routing + EventBus/LiveData event communication',
34
+ INHERITANCE_RULES: 'Activity extends BaseActivity, Fragment extends BaseFragment, ViewModel extends BaseViewModel',
35
+ FORBIDDEN_PATTERNS_TABLE: '| 1 | Hardcoded colors | Use colors.xml resource reference | Cannot unify themes |\n| 2 | Direct Intent navigation | Use ARouter | Module decoupling |\n| 3 | Network requests on main thread | Use suspend + Dispatchers.IO | ANR risk |\n| 4 | Non-ViewBinding view access | Use ViewBinding | Null pointer risk |',
36
+ CLASS_NAMING: 'Activity: FeatureName+Activity (e.g. LoginActivity)\nFragment: FeatureName+Fragment\nViewModel: FeatureName+ViewModel\nAdapter: FeatureName+Adapter',
37
+ RESOURCE_PREFIX: 'module_name_ (e.g. home_btn_submit, login_tv_title)',
38
+ LAYOUT_NAMING: 'type_module_desc (e.g. activity_login, fragment_home, item_user_list)',
39
+ }
40
+ },
41
+ iOS: {
42
+ zh: {
43
+ DEPENDENCY_RULES: 'Feature → Core → Foundation(单向依赖,禁止 Feature 间直接引用)',
44
+ COMMUNICATION_MECHANISM: 'Protocol 抽象接口 + NotificationCenter 事件通知 + Combine Publisher',
45
+ INHERITANCE_RULES: 'ViewController 继承 BaseViewController,View 继承 BaseView',
46
+ FORBIDDEN_PATTERNS_TABLE: '| 1 | 强引用循环 | 使用 [weak self] / [unowned self] | 内存泄漏 |\n| 2 | 主线程阻塞 | 使用 async/await 或 DispatchQueue | UI 卡顿 |\n| 3 | 硬编码字符串 | 使用 Localizable.strings | 国际化支持 |\n| 4 | 直接访问 UserDefaults | 使用封装的 Storage 层 | 数据层解耦 |',
47
+ CLASS_NAMING: 'ViewController: 功能名+ViewController\nView: 功能名+View\nViewModel: 功能名+ViewModel\nService: 功能名+Service',
48
+ RESOURCE_PREFIX: '模块名前缀 (如 Home_, Login_)',
49
+ LAYOUT_NAMING: 'N/A (iOS 使用代码布局或 XIB/Storyboard)',
50
+ },
51
+ en: {
52
+ DEPENDENCY_RULES: 'Feature → Core → Foundation (unidirectional, no direct Feature-to-Feature references)',
53
+ COMMUNICATION_MECHANISM: 'Protocol abstraction + NotificationCenter events + Combine Publisher',
54
+ INHERITANCE_RULES: 'ViewController extends BaseViewController, View extends BaseView',
55
+ FORBIDDEN_PATTERNS_TABLE: '| 1 | Strong reference cycles | Use [weak self] / [unowned self] | Memory leak |\n| 2 | Main thread blocking | Use async/await or DispatchQueue | UI freeze |\n| 3 | Hardcoded strings | Use Localizable.strings | i18n support |\n| 4 | Direct UserDefaults access | Use wrapped Storage layer | Data layer decoupling |',
56
+ CLASS_NAMING: 'ViewController: FeatureName+ViewController\nView: FeatureName+View\nViewModel: FeatureName+ViewModel\nService: FeatureName+Service',
57
+ RESOURCE_PREFIX: 'Module prefix (e.g. Home_, Login_)',
58
+ LAYOUT_NAMING: 'N/A (iOS uses code layout or XIB/Storyboard)',
59
+ }
60
+ },
61
+ Flutter: {
62
+ zh: {
63
+ DEPENDENCY_RULES: 'Feature → Domain → Data → Core(Clean Architecture 分层,禁止反向依赖)',
64
+ COMMUNICATION_MECHANISM: 'Provider/Riverpod 状态管理 + GoRouter 路由导航',
65
+ INHERITANCE_RULES: 'StatefulWidget 使用 mixin 复用逻辑,StatelessWidget 优先',
66
+ FORBIDDEN_PATTERNS_TABLE: '| 1 | BuildContext 跨异步使用 | 异步前检查 mounted | Context 失效崩溃 |\n| 2 | setState 深层嵌套 | 使用状态管理方案 | 性能问题 |\n| 3 | 硬编码尺寸值 | 使用 MediaQuery / LayoutBuilder | 适配问题 |\n| 4 | Widget 树过深 | 拆分为子 Widget | 可维护性 |',
67
+ CLASS_NAMING: 'Page: 功能名+Page\nWidget: 功能名+Widget\nProvider: 功能名+Provider\nRepository: 功能名+Repository',
68
+ RESOURCE_PREFIX: 'N/A (Flutter 使用 assets/ 目录结构)',
69
+ LAYOUT_NAMING: 'N/A (Flutter 使用 Widget 组合)',
70
+ },
71
+ en: {
72
+ DEPENDENCY_RULES: 'Feature → Domain → Data → Core (Clean Architecture layers, no reverse deps)',
73
+ COMMUNICATION_MECHANISM: 'Provider/Riverpod state management + GoRouter navigation',
74
+ INHERITANCE_RULES: 'StatefulWidget uses mixin for logic reuse, prefer StatelessWidget',
75
+ FORBIDDEN_PATTERNS_TABLE: '| 1 | BuildContext across async | Check mounted before async | Context invalidation crash |\n| 2 | Deep setState nesting | Use state management solution | Performance issues |\n| 3 | Hardcoded dimensions | Use MediaQuery / LayoutBuilder | Adaptation issues |\n| 4 | Deep Widget tree | Split into sub-widgets | Maintainability |',
76
+ CLASS_NAMING: 'Page: FeatureName+Page\nWidget: FeatureName+Widget\nProvider: FeatureName+Provider\nRepository: FeatureName+Repository',
77
+ RESOURCE_PREFIX: 'N/A (Flutter uses assets/ directory structure)',
78
+ LAYOUT_NAMING: 'N/A (Flutter uses Widget composition)',
79
+ }
80
+ },
81
+ HarmonyOS: {
82
+ zh: {
83
+ DEPENDENCY_RULES: 'Feature HAP → Common HSP → Base HSP(单向依赖)',
84
+ COMMUNICATION_MECHANISM: 'Router 页面路由 + EventHub 组件通信 + AppStorage 全局状态',
85
+ INHERITANCE_RULES: '页面组件继承基础页面封装,使用 @Component 装饰器',
86
+ FORBIDDEN_PATTERNS_TABLE: '| 1 | 非 @State 状态直接修改 | 使用 @State/@Link/@Prop 装饰器 | UI 不更新 |\n| 2 | 主线程耗时操作 | 使用 TaskPool/Worker | 界面卡顿 |\n| 3 | 硬编码资源引用 | 使用 $r() 资源引用 | 多设备适配失败 |\n| 4 | 直接操作 DOM | 使用声明式 UI | 框架约束 |',
87
+ CLASS_NAMING: 'Page: 功能名+Page\nComponent: 功能名+Comp\nModel: 功能名+Model\nService: 功能名+Service',
88
+ RESOURCE_PREFIX: '模块名_ (如 home_icon_, login_bg_)',
89
+ LAYOUT_NAMING: 'N/A (HarmonyOS 使用 ArkUI 声明式布局)',
90
+ },
91
+ en: {
92
+ DEPENDENCY_RULES: 'Feature HAP → Common HSP → Base HSP (unidirectional)',
93
+ COMMUNICATION_MECHANISM: 'Router page navigation + EventHub component communication + AppStorage global state',
94
+ INHERITANCE_RULES: 'Page components extend base page wrapper, use @Component decorator',
95
+ FORBIDDEN_PATTERNS_TABLE: '| 1 | Direct state mutation without @State | Use @State/@Link/@Prop decorators | UI not updating |\n| 2 | Heavy operations on main thread | Use TaskPool/Worker | UI freeze |\n| 3 | Hardcoded resource references | Use $r() resource reference | Multi-device adaptation failure |\n| 4 | Direct DOM manipulation | Use declarative UI | Framework constraint |',
96
+ CLASS_NAMING: 'Page: FeatureName+Page\nComponent: FeatureName+Comp\nModel: FeatureName+Model\nService: FeatureName+Service',
97
+ RESOURCE_PREFIX: 'module_name_ (e.g. home_icon_, login_bg_)',
98
+ LAYOUT_NAMING: 'N/A (HarmonyOS uses ArkUI declarative layout)',
99
+ }
100
+ },
101
+ 'React Native': {
102
+ zh: {
103
+ DEPENDENCY_RULES: 'Screen → Component → Hook → Service(分层依赖,禁止 Service 引用 UI 层)',
104
+ COMMUNICATION_MECHANISM: 'React Navigation 路由 + Context/Redux 状态管理 + EventEmitter 事件',
105
+ INHERITANCE_RULES: '函数组件 + Custom Hooks 复用逻辑,禁止 Class Component',
106
+ FORBIDDEN_PATTERNS_TABLE: '| 1 | 桥接传输大数据 | 使用分页/流式传输 | 性能瓶颈 |\n| 2 | 内联样式 | 使用 StyleSheet.create | 重复创建对象 |\n| 3 | 直接操作 Native 不做错误处理 | try-catch 包裹 Native 调用 | 崩溃风险 |\n| 4 | FlatList 不使用 keyExtractor | 必须提供 keyExtractor | 渲染性能 |',
107
+ CLASS_NAMING: 'Screen: 功能名+Screen\nComponent: 功能名+组件类型\nHook: use+功能名\nService: 功能名+Service',
108
+ RESOURCE_PREFIX: 'N/A (React Native 使用 assets/ 目录)',
109
+ LAYOUT_NAMING: 'N/A (React Native 使用 JSX 组合)',
110
+ },
111
+ en: {
112
+ DEPENDENCY_RULES: 'Screen → Component → Hook → Service (layered deps, Service must not reference UI)',
113
+ COMMUNICATION_MECHANISM: 'React Navigation routing + Context/Redux state management + EventEmitter events',
114
+ INHERITANCE_RULES: 'Function components + Custom Hooks for logic reuse, no Class Components',
115
+ FORBIDDEN_PATTERNS_TABLE: '| 1 | Large data bridge transfer | Use pagination/streaming | Performance bottleneck |\n| 2 | Inline styles | Use StyleSheet.create | Repeated object creation |\n| 3 | Native calls without error handling | Wrap Native calls in try-catch | Crash risk |\n| 4 | FlatList without keyExtractor | Must provide keyExtractor | Render performance |',
116
+ CLASS_NAMING: 'Screen: FeatureName+Screen\nComponent: FeatureName+ComponentType\nHook: use+FeatureName\nService: FeatureName+Service',
117
+ RESOURCE_PREFIX: 'N/A (React Native uses assets/ directory)',
118
+ LAYOUT_NAMING: 'N/A (React Native uses JSX composition)',
119
+ }
120
+ },
121
+ };
122
+
123
+ const platformDefaults = defaults[platform];
124
+ if (!platformDefaults) return {};
125
+ return platformDefaults[lang] || platformDefaults.en || {};
126
+ }
127
+
128
+ export async function renderTemplates(templateRoot, targetDir, config, detection) {
129
+ registerI18n(config.lang);
130
+
131
+ // Build template variables
132
+ const vars = await buildVars(config, detection);
133
+
134
+ // Language-dependent files (i18n handled via t helper)
135
+ const langDependentFiles = [
136
+ { src: 'CHANGELOG.md', dest: `${config.dir}/CHANGELOG.md` },
137
+ { src: 'rules/project_rule.md', dest: `${config.dir}/rules/project_rule.md` },
138
+ { src: 'rules/conflict_resolution.md', dest: `${config.dir}/rules/conflict_resolution.md` },
139
+ { src: 'skills/plan_mode/SKILL.md', dest: `${config.dir}/skills/plan_mode/SKILL.md` },
140
+ { src: 'skills/code_review/SKILL.md', dest: `${config.dir}/skills/code_review/SKILL.md` },
141
+ { src: 'agents/arch-review.md', dest: `${config.dir}/agents/arch-review.md` },
142
+ { src: 'agents/resource-sync.md', dest: `${config.dir}/agents/resource-sync.md` },
143
+ { src: 'agents/proactive-correction.md', dest: `${config.dir}/agents/proactive-correction.md` },
144
+ { src: 'agents/cpp-memory-review.md', dest: `${config.dir}/agents/cpp-memory-review.md` },
145
+ ];
146
+
147
+ // Language-independent files (from root template dir)
148
+ const langIndependentFiles = [
149
+ { src: 'settings.json', dest: `${config.dir}/settings.json` },
150
+ { src: 'settings.local.json', dest: `${config.dir}/settings.local.json` },
151
+ ];
152
+
153
+ // Script files (from hooks/ and scripts/ relative to cli root)
154
+ const scriptRoot = path.resolve(templateRoot, '..');
155
+ const scriptFiles = [
156
+ { src: path.join(scriptRoot, 'hooks/post-edit-tracker.sh'), dest: `${config.dir}/hooks/post-edit-tracker.sh` },
157
+ { src: path.join(scriptRoot, 'hooks/check-review-needed.sh'), dest: `${config.dir}/hooks/check-review-needed.sh` },
158
+ { src: path.join(scriptRoot, 'scripts/gen_references.py'), dest: `${config.dir}/scripts/gen_references.py` },
159
+ ];
160
+
161
+ // === Transaction Safety: use temp directory + atomic commit ===
162
+ // Phase 1: Create temporary directory
163
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ai-scaffold-'));
164
+
165
+ try {
166
+ // Phase 2: Generate all files to temporary directory
167
+ // Render language-dependent templates
168
+ for (const file of langDependentFiles) {
169
+ const srcPath = path.join(templateRoot, file.src);
170
+ if (fs.existsSync(srcPath)) {
171
+ const content = fs.readFileSync(srcPath, 'utf-8');
172
+ const rendered = renderContent(content, vars);
173
+ writeOutput(tempDir, file.dest, rendered);
174
+ }
175
+ }
176
+
177
+ // Copy language-independent files
178
+ for (const file of langIndependentFiles) {
179
+ const srcPath = path.join(templateRoot, file.src);
180
+ if (fs.existsSync(srcPath)) {
181
+ const content = fs.readFileSync(srcPath, 'utf-8');
182
+ const rendered = renderContent(content, vars);
183
+ writeOutput(tempDir, file.dest, rendered);
184
+ }
185
+ }
186
+
187
+ // Copy script files
188
+ for (const file of scriptFiles) {
189
+ if (fs.existsSync(file.src)) {
190
+ const content = fs.readFileSync(file.src, 'utf-8');
191
+ const rendered = renderContent(content, vars);
192
+ writeOutput(tempDir, file.dest, rendered);
193
+ }
194
+ }
195
+
196
+ // Generate entry file (CLAUDE.md or AGENTS.md)
197
+ const entryContent = generateEntry(vars, config.lang);
198
+ writeOutput(tempDir, config.entry, entryContent);
199
+
200
+ // Phase 3: Validate generated results
201
+ const unreplaced = checkUnreplacedVars(tempDir);
202
+ if (unreplaced.length > 0) {
203
+ console.warn(chalk.yellow(`\u26a0 Warning: Found ${unreplaced.length} unreplaced template variable(s):`));
204
+ unreplaced.forEach(item => console.warn(chalk.yellow(` - ${item.file}: ${item.vars.join(', ')}`)));
205
+ }
206
+
207
+ // Phase 4: Atomic commit - copy temp directory contents to target directory
208
+ copyDirRecursive(tempDir, targetDir);
209
+
210
+ // Output success message
211
+ console.log(chalk.green(`\n\u2713 Generated ${countFiles(tempDir)} files successfully.`));
212
+
213
+ } catch (err) {
214
+ // On failure, clean up temp dir; target directory is not affected
215
+ console.error(chalk.red(`\n\u2717 Generation failed: ${err.message}`));
216
+ console.error(chalk.red(' No files were written to the target directory.'));
217
+ throw err;
218
+ } finally {
219
+ // Always clean up temporary directory
220
+ fs.rmSync(tempDir, { recursive: true, force: true });
221
+ }
222
+ }
223
+
224
+ async function buildVars(config, detection) {
225
+ const platformVars = await getPlatformVars(detection.platform, config.lang);
226
+ const smartDefaults = getSmartDefaults(detection.platform, config.lang);
227
+
228
+ // Helper: use user-provided value if it's not empty/placeholder, otherwise use smart default
229
+ const resolve = (userValue, defaultKey) => {
230
+ if (userValue && !userValue.startsWith('Define ') && userValue !== '') {
231
+ return userValue;
232
+ }
233
+ return smartDefaults[defaultKey] || userValue;
234
+ };
235
+
236
+ return {
237
+ DIR: config.dir,
238
+ ENTRY: config.entry,
239
+ ENTRY_VERSION_LINE: `<!-- ${config.dir}-version: v1.0.0 (${TODAY}) -->`,
240
+ GENERATION_DATE: TODAY,
241
+ PROJECT_NAME: config.projectName,
242
+ PROJECT_DESCRIPTION: config.projectDescription,
243
+ PACKAGE_NAME: config.packageName,
244
+ AI_NAME: config.aiName,
245
+ PLATFORM: detection.platform,
246
+ BUILD_SYSTEM: detection.buildSystem,
247
+ LANGUAGE: detection.language,
248
+ HAS_NDK: detection.hasNdk,
249
+ HAS_TESTS: config.hasTests,
250
+ BUILD_COMMAND_DEBUG: config.buildDebug,
251
+ BUILD_COMMAND_RELEASE: config.buildRelease,
252
+ REVIEW_FILE_THRESHOLD: config.reviewThreshold,
253
+ ARCH_REVIEW_MODULE_THRESHOLD: config.archThreshold,
254
+ BUILD_ENV_TABLE: `| Language | ${detection.language} |\n| Build System | ${detection.buildSystem} |\n| Platform | ${detection.platform} |`,
255
+ DOMAINS: 'general, code_quality, architecture',
256
+ DEPENDENCY_RULES: resolve('Define module dependency direction rules here', 'DEPENDENCY_RULES'),
257
+ COMMUNICATION_MECHANISM: resolve('Define inter-module communication mechanism here', 'COMMUNICATION_MECHANISM'),
258
+ INHERITANCE_RULES: resolve('Define inheritance hierarchy rules here', 'INHERITANCE_RULES'),
259
+ FORBIDDEN_PATTERNS_TABLE: resolve('| # | Forbidden | Alternative | Reason |\n|---|----------|-------------|--------|\n| 1 | Define patterns | Define alternatives | Define reasons |', 'FORBIDDEN_PATTERNS_TABLE'),
260
+ FORBIDDEN_PATTERNS_COUNT: smartDefaults.FORBIDDEN_PATTERNS_TABLE ? '4' : '0',
261
+ FORBIDDEN_PATTERNS_SEARCH_TABLE: '| Pattern | Search | Replace |\n|---------|--------|---------|',
262
+ RESOURCE_PREFIXES: resolve('Define resource prefixes here', 'RESOURCE_PREFIX'),
263
+ CLASS_NAMING: resolve('Define class naming conventions here', 'CLASS_NAMING'),
264
+ LAYOUT_NAMING: resolve('Define layout naming conventions here', 'LAYOUT_NAMING'),
265
+ PLATFORM_SPECIFIC_RULES: platformVars.PLATFORM_SPECIFIC_RULES || `Add ${detection.platform}-specific rules here`,
266
+ PLATFORM_FATAL_CHECKS: platformVars.PLATFORM_FATAL_CHECKS || '',
267
+ PLATFORM_WARNING_CHECKS: platformVars.PLATFORM_WARNING_CHECKS || '',
268
+ PLATFORM_SUGGESTION_CHECKS: platformVars.PLATFORM_SUGGESTION_CHECKS || '',
269
+ PLATFORM_SELF_CHECK_ITEMS: platformVars.PLATFORM_SELF_CHECK_ITEMS || '',
270
+ PLATFORM_RESOURCE_SYNC_CONTENT: platformVars.PLATFORM_RESOURCE_SYNC_CONTENT || 'Add platform-specific resource sync checks here',
271
+ PLATFORM_SPECIFIC_TASK_TEMPLATES: platformVars.PLATFORM_SPECIFIC_TASK_TEMPLATES || `Add ${detection.platform} task templates here`,
272
+ PLATFORM_RULES_SUMMARY: platformVars.PLATFORM_RULES_SUMMARY || `${detection.platform} specific rules`,
273
+ DEPENDENCY_RULES_SUMMARY: smartDefaults.DEPENDENCY_RULES || 'Define dependency rules',
274
+ NAMING_SUMMARY: smartDefaults.CLASS_NAMING || 'Define naming conventions',
275
+ ARCHITECTURE: 'Define architecture',
276
+ NAVIGATION: 'Define navigation',
277
+ COMMUNICATION_MECHANISM_CHECKS: smartDefaults.COMMUNICATION_MECHANISM || 'Define communication checks',
278
+ INHERITANCE_CHECKS: smartDefaults.INHERITANCE_RULES || 'Define inheritance checks',
279
+ };
280
+ }
281
+
282
+ function renderContent(content, vars) {
283
+ try {
284
+ const template = Handlebars.compile(content);
285
+ return template(vars);
286
+ } catch {
287
+ // If Handlebars fails, do simple variable replacement
288
+ let result = content;
289
+ for (const [key, value] of Object.entries(vars)) {
290
+ result = result.replaceAll(`{{${key}}}`, String(value));
291
+ }
292
+ return result;
293
+ }
294
+ }
295
+
296
+ function writeOutput(targetDir, relativePath, content) {
297
+ const fullPath = path.join(targetDir, relativePath);
298
+ const dir = path.dirname(fullPath);
299
+ fs.mkdirSync(dir, { recursive: true });
300
+ fs.writeFileSync(fullPath, content, 'utf-8');
301
+ console.log(chalk.green(` \u2713 ${relativePath}`));
302
+ }
303
+
304
+ /**
305
+ * Check generated files for unreplaced template variables
306
+ */
307
+ function checkUnreplacedVars(dir) {
308
+ const results = [];
309
+ const varPattern = /\{\{(?!#|\/|!|>)([A-Z_][A-Z0-9_]*)\}\}/g;
310
+
311
+ function scanDir(currentDir) {
312
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
313
+ for (const entry of entries) {
314
+ const fullPath = path.join(currentDir, entry.name);
315
+ if (entry.isDirectory()) {
316
+ scanDir(fullPath);
317
+ } else if (entry.name.endsWith('.md') || entry.name.endsWith('.json') || entry.name.endsWith('.sh') || entry.name.endsWith('.py')) {
318
+ const content = fs.readFileSync(fullPath, 'utf-8');
319
+ const matches = [...content.matchAll(varPattern)];
320
+ if (matches.length > 0) {
321
+ results.push({
322
+ file: path.relative(dir, fullPath),
323
+ vars: [...new Set(matches.map(m => m[1]))]
324
+ });
325
+ }
326
+ }
327
+ }
328
+ }
329
+
330
+ scanDir(dir);
331
+ return results;
332
+ }
333
+
334
+ /**
335
+ * Recursively copy directory contents to target
336
+ */
337
+ function copyDirRecursive(src, dest) {
338
+ const entries = fs.readdirSync(src, { withFileTypes: true });
339
+ for (const entry of entries) {
340
+ const srcPath = path.join(src, entry.name);
341
+ const destPath = path.join(dest, entry.name);
342
+ if (entry.isDirectory()) {
343
+ fs.mkdirSync(destPath, { recursive: true });
344
+ copyDirRecursive(srcPath, destPath);
345
+ } else {
346
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
347
+ fs.copyFileSync(srcPath, destPath);
348
+ }
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Count files in a directory recursively
354
+ */
355
+ function countFiles(dir) {
356
+ let count = 0;
357
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
358
+ for (const entry of entries) {
359
+ if (entry.isDirectory()) {
360
+ count += countFiles(path.join(dir, entry.name));
361
+ } else {
362
+ count++;
363
+ }
364
+ }
365
+ return count;
366
+ }
367
+
368
+ function generateEntry(vars, lang) {
369
+ if (lang === 'en') {
370
+ return `${vars.ENTRY_VERSION_LINE}
371
+ # ${vars.ENTRY}
372
+ Your name is ${vars.AI_NAME}. You are an AI assistant helping me solve problems in this codebase.
373
+
374
+ ## Project Overview
375
+
376
+ ${vars.PROJECT_DESCRIPTION}. Package: \`${vars.PACKAGE_NAME}\`. Built with ${vars.LANGUAGE}, using ${vars.ARCHITECTURE} architecture.
377
+
378
+ ## Build Commands
379
+
380
+ \`\`\`bash
381
+ # Debug build
382
+ ${vars.BUILD_COMMAND_DEBUG}
383
+
384
+ # Release build
385
+ ${vars.BUILD_COMMAND_RELEASE}
386
+ \`\`\`
387
+
388
+ ${vars.HAS_TESTS ? 'This project has automated tests.' : 'This project does not have automated tests.'}
389
+
390
+ ## Build Environment
391
+
392
+ | Item | Value |
393
+ |------|-------|
394
+ ${vars.BUILD_ENV_TABLE}
395
+
396
+ ---
397
+
398
+ ## Rules and Skill Trigger Strategy
399
+
400
+ ### Mandatory Rules
401
+
402
+ 1. **Read rules before modifying**: Before any code modification, read \`${vars.DIR}/rules/project_rule.md\` in full
403
+ 2. **${vars.REVIEW_FILE_THRESHOLD}+ files triggers review**: After modification, trigger \`code_review\` skill
404
+ 3. **${vars.ARCH_REVIEW_MODULE_THRESHOLD}+ modules triggers arch review**: Delegate \`arch-review\` agent
405
+ 4. **Resource changes trigger sync check**: Delegate \`resource-sync\` agent
406
+ 5. **${vars.DIR} config changes require CHANGELOG**: Update \`${vars.DIR}/CHANGELOG.md\` and bump version
407
+ ${vars.HAS_CODEGRAPH ? `
408
+ ### CodeGraph Integration
409
+
410
+ This project uses CodeGraph. When exploring the codebase, prefer the \`codegraph_explore\` tool for structural search. The \`${vars.DIR}/references/\` directory contains lightweight architecture decisions, business logic summaries, and conventions — use it alongside CodeGraph.
411
+ ` : ''}
412
+ `;
413
+ }
414
+
415
+ return `${vars.ENTRY_VERSION_LINE}
416
+ # ${vars.ENTRY}
417
+ 你的名字是${vars.AI_NAME},你是一个AI助手,你的任务是帮助我解决代码仓库中的问题。
418
+
419
+ ## 项目概述
420
+
421
+ ${vars.PROJECT_DESCRIPTION}。包名:\`${vars.PACKAGE_NAME}\`。采用${vars.LANGUAGE}开发,使用${vars.ARCHITECTURE}架构。
422
+
423
+ ## 构建命令
424
+
425
+ \`\`\`bash
426
+ # Debug 构建
427
+ ${vars.BUILD_COMMAND_DEBUG}
428
+
429
+ # Release 构建
430
+ ${vars.BUILD_COMMAND_RELEASE}
431
+ \`\`\`
432
+
433
+ 本项目${vars.HAS_TESTS ? '有' : '无'}自动化测试。
434
+
435
+ ## 构建环境
436
+
437
+ | 项目 | 值 |
438
+ |------|-----|
439
+ ${vars.BUILD_ENV_TABLE}
440
+
441
+ ---
442
+
443
+ ## 规则与技能触发策略
444
+
445
+ ### 强制执行规则
446
+
447
+ 1. **修改前必读规则**:执行任何代码修改前,必须先阅读 \`${vars.DIR}/rules/project_rule.md\` 全文
448
+ 2. **${vars.REVIEW_FILE_THRESHOLD}+ 文件修改必触发审查**:修改完成后**必须**触发 \`code_review\` 技能
449
+ 3. **${vars.ARCH_REVIEW_MODULE_THRESHOLD}+ 模块修改必触发架构审查**:修改完成后**必须**委派 \`arch-review\` agent
450
+ 4. **资源文件变更必触发同步检查**:**必须**委派 \`resource-sync\` agent
451
+ 5. **${vars.DIR} 配置变更必更新 CHANGELOG**:凡修改 \`${vars.DIR}/\` 下配置文件,**必须**同步更新 \`${vars.DIR}/CHANGELOG.md\` 并升级版本号
452
+ ${vars.HAS_CODEGRAPH ? `
453
+ ### CodeGraph 集成
454
+
455
+ 本项目已集成 CodeGraph。探索代码库时优先使用 \`codegraph_explore\` 工具进行结构化搜索。\`${vars.DIR}/references/\` 目录采用轻量模式,仅包含架构决策、业务逻辑摘要和项目约定——请与 CodeGraph 配合使用。
456
+ ` : ''}
457
+ `;
458
+ }
459
+
@@ -0,0 +1,21 @@
1
+ # {{DIR}} {{t "changelog.title"}}
2
+
3
+ > {{t "changelog.format_line1"}}
4
+ > {{t "changelog.format_line2"}}
5
+
6
+ ## v1.0.0 ({{GENERATION_DATE}})
7
+
8
+ ### Added
9
+ - {{ENTRY}}: {{t "changelog.added_entry"}}
10
+ - rules/project_rule.md: {{PROJECT_NAME}} {{t "changelog.added_project_rule"}}
11
+ - rules/conflict_resolution.md: {{t "changelog.added_conflict_resolution"}}
12
+ - skills/plan_mode/SKILL.md: {{t "changelog.added_plan_mode"}}
13
+ - skills/code_review/SKILL.md: {{t "changelog.added_code_review"}}
14
+ - agents/arch-review.md: {{t "changelog.added_arch_review"}}
15
+ - agents/resource-sync.md: {{t "changelog.added_resource_sync"}}
16
+ - agents/proactive-correction.md: {{t "changelog.added_proactive_correction"}}
17
+ - hooks/post-edit-tracker.sh: {{t "changelog.added_post_edit_tracker"}}
18
+ - hooks/check-review-needed.sh: {{t "changelog.added_check_review"}}
19
+ - settings.json: {{t "changelog.added_settings"}}
20
+ - scripts/gen_references.py: {{t "changelog.added_gen_references"}}
21
+ - references/: {{t "changelog.added_references"}}
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: arch-review
3
+ description: {{t "arch_review.description"}}
4
+ tools: Read, Grep, Glob
5
+ ---
6
+
7
+ # {{t "arch_review.title"}}
8
+
9
+ {{t "arch_review.intro"}} {{PROJECT_NAME}} {{t "arch_review.intro_suffix"}}
10
+
11
+ ## {{t "arch_review.dimension_title"}}
12
+
13
+ ### 1. {{t "arch_review.dep_title"}}
14
+
15
+ {{t "arch_review.dep_check_title"}}
16
+ {{DEPENDENCY_RULES}}
17
+
18
+ {{t "arch_review.dep_check_method"}}
19
+
20
+ ### 2. {{t "arch_review.forbidden_title"}}
21
+
22
+ {{t "arch_review.forbidden_desc"}}
23
+
24
+ {{t "arch_review.forbidden_table_header"}}
25
+ |---------|---------|---------|
26
+ {{FORBIDDEN_PATTERNS_SEARCH_TABLE}}
27
+
28
+ ### 3. {{t "arch_review.comm_title"}}
29
+
30
+ {{t "arch_review.comm_desc"}}
31
+ {{COMMUNICATION_MECHANISM_CHECKS}}
32
+
33
+ ### 4. {{t "arch_review.inherit_title"}}
34
+
35
+ {{INHERITANCE_CHECKS}}
36
+
37
+ {{t "arch_review.inherit_check_rules"}}
38
+
39
+ {{t "arch_review.inherit_check_method_title"}}
40
+ {{t "arch_review.inherit_check_method_steps"}}
41
+
42
+ ### 5. {{t "arch_review.interface_isolation_title"}}
43
+
44
+ {{t "arch_review.interface_isolation_rules"}}
45
+
46
+ {{t "arch_review.interface_isolation_method_title"}}
47
+ {{t "arch_review.interface_isolation_method_steps"}}
48
+
49
+ ### 6. {{t "arch_review.cycle_detection_title"}}
50
+
51
+ {{t "arch_review.cycle_detection_method_title"}}
52
+ {{t "arch_review.cycle_detection_method_steps"}}
53
+
54
+ ## {{t "arch_review.output_title"}}
55
+
56
+ ```
57
+ ## 🏗️ {{t "arch_review.title"}}
58
+
59
+ **{{t "arch_review.output_scope"}}**:[{{t "arch_review.output_scope_value"}}]
60
+ **{{t "arch_review.output_result"}}**:✅ {{t "arch_review.output_pass"}} / ⚠️ {{t "arch_review.output_warning"}} / ❌ {{t "arch_review.output_violation"}}
61
+
62
+ ### ❌ {{t "arch_review.output_violation_section"}}
63
+
64
+ | # | {{t "arch_review.output_col_file"}} | {{t "arch_review.output_col_type"}} | {{t "arch_review.output_col_issue"}} | {{t "arch_review.output_col_fix"}} |
65
+ |---|----------|---------|---------|---------|
66
+
67
+ ### ✅ {{t "arch_review.output_passed_section"}}
68
+ - [x] {{t "arch_review.output_dep_ok"}}
69
+ - [x] {{t "arch_review.output_forbidden_ok"}}
70
+ ```
71
+
72
+ ## {{t "arch_review.constraints_title"}}
73
+
74
+ {{t "arch_review.must_do_title"}}
75
+ {{t "arch_review.must_do_rules"}}
76
+
77
+ {{t "arch_review.must_not_title"}}
78
+ {{t "arch_review.must_not_rules"}}
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: cpp-memory-review
3
+ description: {{t "cpp_memory_review.description"}}
4
+ tools: Read, Grep, Glob
5
+ ---
6
+
7
+ # {{t "cpp_memory_review.title"}}
8
+
9
+ {{t "cpp_memory_review.intro"}}
10
+
11
+ ## {{t "cpp_memory_review.dimensions_title"}}
12
+
13
+ ### 1. {{t "cpp_memory_review.jni_ref_title"}}
14
+
15
+ {{t "cpp_memory_review.jni_ref_desc"}}
16
+
17
+ {{t "cpp_memory_review.jni_ref_table_header"}}
18
+ |--------|---------|---------|
19
+ {{t "cpp_memory_review.jni_ref_row1"}}
20
+ {{t "cpp_memory_review.jni_ref_row2"}}
21
+ {{t "cpp_memory_review.jni_ref_row3"}}
22
+ {{t "cpp_memory_review.jni_ref_row4"}}
23
+ {{t "cpp_memory_review.jni_ref_row5"}}
24
+
25
+ {{t "cpp_memory_review.jni_ref_method_title"}}
26
+ {{t "cpp_memory_review.jni_ref_method_steps"}}
27
+
28
+ ### 2. {{t "cpp_memory_review.heap_title"}}
29
+
30
+ {{t "cpp_memory_review.heap_table_header"}}
31
+ |--------|---------|---------|
32
+ {{t "cpp_memory_review.heap_row1"}}
33
+ {{t "cpp_memory_review.heap_row2"}}
34
+ {{t "cpp_memory_review.heap_row3"}}
35
+
36
+ {{t "cpp_memory_review.heap_method_title"}}
37
+ {{t "cpp_memory_review.heap_method_steps"}}
38
+
39
+ ### 3. {{t "cpp_memory_review.buffer_title"}}
40
+
41
+ {{t "cpp_memory_review.buffer_table_header"}}
42
+ |--------|---------|---------|
43
+ {{t "cpp_memory_review.buffer_row1"}}
44
+ {{t "cpp_memory_review.buffer_row2"}}
45
+ {{t "cpp_memory_review.buffer_row3"}}
46
+ {{t "cpp_memory_review.buffer_row4"}}
47
+
48
+ ### 4. {{t "cpp_memory_review.sensitive_title"}}
49
+
50
+ {{t "cpp_memory_review.sensitive_table_header"}}
51
+ |--------|---------|---------|
52
+ {{t "cpp_memory_review.sensitive_row1"}}
53
+ {{t "cpp_memory_review.sensitive_row2"}}
54
+
55
+ ### 5. {{t "cpp_memory_review.rationality_title"}}
56
+
57
+ {{t "cpp_memory_review.rationality_table_header"}}
58
+ |--------|---------|---------|
59
+ {{t "cpp_memory_review.rationality_row1"}}
60
+ {{t "cpp_memory_review.rationality_row2"}}
61
+ {{t "cpp_memory_review.rationality_row3"}}
62
+ {{t "cpp_memory_review.rationality_row4"}}
63
+ {{t "cpp_memory_review.rationality_row5"}}
64
+ {{t "cpp_memory_review.rationality_row6"}}
65
+
66
+ ## {{t "cpp_memory_review.output_title"}}
67
+
68
+ ```
69
+ {{t "cpp_memory_review.output_content"}}
70
+ ```
71
+
72
+ ## {{t "cpp_memory_review.flow_title"}}
73
+
74
+ ```
75
+ {{t "cpp_memory_review.flow_content"}}
76
+ ```
77
+
78
+ ## {{t "cpp_memory_review.constraints_title"}}
79
+
80
+ {{t "cpp_memory_review.must_do_title"}}
81
+ {{t "cpp_memory_review.must_do_rules"}}
82
+
83
+ {{t "cpp_memory_review.must_not_title"}}
84
+ {{t "cpp_memory_review.must_not_rules"}}