aodw-skill 0.7.7 → 0.7.8

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 (2) hide show
  1. package/bin/aodw.js +228 -7
  2. package/package.json +1 -1
package/bin/aodw.js CHANGED
@@ -115,6 +115,95 @@ async function copyRecursive(sourceDir, targetDir, processorClass, renameFn = nu
115
115
  }
116
116
  }
117
117
 
118
+ // Helper: Check if a file is a user-generated file (not a template)
119
+ function isUserGeneratedFile(filePath, coreDir) {
120
+ const relativePath = path.relative(coreDir, filePath);
121
+
122
+ // User-generated files that should be preserved
123
+ const userGeneratedFiles = [
124
+ '06-project/ai-overview.md',
125
+ '06-project/modules-index.yaml',
126
+ 'tools-status.yaml'
127
+ ];
128
+
129
+ return userGeneratedFiles.some(pattern => relativePath === pattern || relativePath.endsWith(pattern));
130
+ }
131
+
132
+ // Helper: Check if a file is still a template (has template markers)
133
+ async function isTemplateFile(filePath) {
134
+ if (!fs.existsSync(filePath)) {
135
+ return false;
136
+ }
137
+
138
+ try {
139
+ const content = await fs.readFile(filePath, 'utf8');
140
+
141
+ // Check for template markers in ai-overview.md
142
+ if (filePath.endsWith('ai-overview.md')) {
143
+ // Template has empty tech stack sections like "- 前端:\n- 后端:"
144
+ const hasEmptyTechStack = /- 前端:\s*\n\s*- 后端:/.test(content);
145
+ // Template has placeholder text
146
+ const hasPlaceholder = /(由 AI 或人工在首次接入 AODW 时填写/.test(content);
147
+ return hasEmptyTechStack || hasPlaceholder;
148
+ }
149
+
150
+ // Check for template markers in modules-index.yaml
151
+ if (filePath.endsWith('modules-index.yaml')) {
152
+ // Template has only example comments, no actual modules
153
+ const hasOnlyComments = /^version: 1\s*\n\s*# 模块索引/.test(content) &&
154
+ !/^modules:\s*\n\s*- name:/.test(content);
155
+ return hasOnlyComments;
156
+ }
157
+
158
+ // tools-status.yaml is always user-generated if it exists
159
+ if (filePath.endsWith('tools-status.yaml')) {
160
+ return false; // If it exists, it's user-generated
161
+ }
162
+
163
+ return false;
164
+ } catch (e) {
165
+ return false;
166
+ }
167
+ }
168
+
169
+ // Helper: Smart copy that preserves user-generated files
170
+ async function copyCoreWithPreservation(sourceDir, targetDir, isUpdate = false) {
171
+ const entries = await fs.readdir(sourceDir, { withFileTypes: true });
172
+
173
+ for (const entry of entries) {
174
+ const srcPath = path.join(sourceDir, entry.name);
175
+ const destPath = path.join(targetDir, entry.name);
176
+
177
+ if (entry.isDirectory()) {
178
+ await fs.ensureDir(destPath);
179
+ await copyCoreWithPreservation(srcPath, destPath, isUpdate);
180
+ } else {
181
+ // Check if this is a user-generated file
182
+ if (isUserGeneratedFile(destPath, targetDir)) {
183
+ if (isUpdate) {
184
+ // During update, check if file exists and is not a template
185
+ if (fs.existsSync(destPath)) {
186
+ const isTemplate = await isTemplateFile(destPath);
187
+ if (!isTemplate) {
188
+ // File exists and is user-generated, skip overwriting
189
+ console.log(chalk.gray(` ⊖ 保留用户文件: ${path.relative(process.cwd(), destPath)}`));
190
+ continue;
191
+ } else {
192
+ // File exists but is still a template, can be overwritten
193
+ console.log(chalk.yellow(` ↻ 更新模板文件: ${path.relative(process.cwd(), destPath)}`));
194
+ }
195
+ }
196
+ }
197
+ // During init, always copy (first time)
198
+ }
199
+
200
+ // Copy the file
201
+ await fs.ensureDir(path.dirname(destPath));
202
+ await fs.copy(srcPath, destPath);
203
+ }
204
+ }
205
+ }
206
+
118
207
  // Helper: Return to menu pause
119
208
  async function returnToMenu() {
120
209
  console.log(); // empty line
@@ -241,7 +330,15 @@ async function runInit() {
241
330
 
242
331
  // 1. Install Core Rules (channel-aware core dir)
243
332
  const targetCore = path.join(process.cwd(), CORE_DIRNAME);
244
- await fs.copy(SOURCE_CORE, targetCore);
333
+ const isUpdate = fs.existsSync(targetCore);
334
+
335
+ if (isUpdate) {
336
+ console.log(chalk.blue('正在更新核心规则(保留用户生成的文件)...'));
337
+ await copyCoreWithPreservation(SOURCE_CORE, targetCore, true);
338
+ } else {
339
+ console.log(chalk.blue('正在安装核心规则...'));
340
+ await fs.copy(SOURCE_CORE, targetCore);
341
+ }
245
342
 
246
343
  // 3. Install Adapters based on selected platforms
247
344
  console.log(chalk.blue('正在安装适配器...'));
@@ -430,6 +527,130 @@ async function showHelp() {
430
527
  }
431
528
  }
432
529
 
530
+ async function generateOverviewPrompt() {
531
+ console.clear();
532
+ console.log(chalk.bold.cyan('\n=== 项目概览初始化提示词 ===\n'));
533
+ console.log(chalk.yellow('📋 请将以下提示词复制给您的 AI 助手(Cursor/Claude/Gemini 等):\n'));
534
+
535
+ const overviewFile = path.join(process.cwd(), CORE_DIRNAME, '06-project/ai-overview.md');
536
+ const modulesIndexFile = path.join(process.cwd(), CORE_DIRNAME, '06-project/modules-index.yaml');
537
+ const hasOverview = fs.existsSync(overviewFile);
538
+ const hasModulesIndex = fs.existsSync(modulesIndexFile);
539
+
540
+ let overviewPrompt = `请帮我${hasOverview || hasModulesIndex ? '更新' : '初始化'}项目的 AODW 项目概览文档。
541
+
542
+ **任务说明**:
543
+ 根据当前项目的代码结构、技术栈和架构,生成或完善以下文档:
544
+ 1. \`${CORE_DIRNAME}/06-project/ai-overview.md\` - 项目概览文档
545
+ 2. \`${CORE_DIRNAME}/06-project/modules-index.yaml\` - 模块索引文件
546
+
547
+ **文件位置**:
548
+ - 项目概览文档:\`${overviewFile}\`
549
+ - 模块索引文件:\`${modulesIndexFile}\`
550
+
551
+ **参考规则**:
552
+ - 请参考 \`${CORE_DIRNAME}/01-core/ai-project-overview-rules.md\` 中的详细规则
553
+ - 需要检测项目的技术栈(前端、后端、数据库、消息系统等)
554
+ - 需要识别项目的模块结构
555
+ - 需要分析项目的架构模式
556
+
557
+ **执行步骤**:
558
+ 1. 先读取 \`${CORE_DIRNAME}/01-core/ai-project-overview-rules.md\` 了解规则
559
+ 2. ${hasOverview ? `读取现有的 \`${CORE_DIRNAME}/06-project/ai-overview.md\` 了解当前项目信息` : '分析项目结构,检测技术栈'}
560
+ 3. ${hasModulesIndex ? `读取现有的 \`${CORE_DIRNAME}/06-project/modules-index.yaml\` 了解当前模块结构` : '识别项目模块'}
561
+ 4. 生成或更新 \`ai-overview.md\` 和 \`modules-index.yaml\`
562
+
563
+ **重要提示**:
564
+ - ✅ **这些文件在更新 AODW 时会被保护,不会被覆盖**
565
+ - ${hasOverview ? '如果项目已经有部分概览文档,请基于现有内容进行完善' : '如果项目已经有部分概览文档,请基于现有内容进行完善'}
566
+ - 确保技术栈信息准确
567
+ - 确保模块索引完整
568
+ - **此命令可以重复执行**,每次执行会基于现有内容进行更新和完善
569
+
570
+ 请开始执行。`;
571
+
572
+ console.log(chalk.white(overviewPrompt));
573
+ console.log(chalk.gray('\n' + '='.repeat(60)));
574
+ console.log(chalk.green('\n✅ 提示词已生成,请复制上面的内容给您的 AI 助手。'));
575
+ if (hasOverview || hasModulesIndex) {
576
+ console.log(chalk.blue('\n📝 检测到已有文件,将基于现有内容进行更新。'));
577
+ }
578
+ console.log(chalk.yellow('\n💡 提示:完成项目概览初始化后,再执行"工具初始化"可以更准确地识别技术栈。\n'));
579
+ }
580
+
581
+ async function generateToolsPrompt() {
582
+ console.clear();
583
+ console.log(chalk.bold.cyan('\n=== 工具初始化提示词 ===\n'));
584
+ console.log(chalk.yellow('📋 请将以下提示词复制给您的 AI 助手(Cursor/Claude/Gemini 等):\n'));
585
+
586
+ // 检查是否已有项目概览和工具状态
587
+ const overviewFile = path.join(process.cwd(), CORE_DIRNAME, '06-project/ai-overview.md');
588
+ const toolsStatusFile = path.join(process.cwd(), CORE_DIRNAME, 'tools-status.yaml');
589
+ const hasOverview = fs.existsSync(overviewFile);
590
+ const hasToolsStatus = fs.existsSync(toolsStatusFile);
591
+
592
+ let toolsPrompt = `请帮我${hasToolsStatus ? '更新' : '初始化'}项目的开发工具配置。
593
+
594
+ **任务说明**:
595
+ 根据当前项目的技术栈,初始化相应的代码质量工具(ESLint、Prettier、Ruff、Black、rustfmt、clippy 等)。
596
+
597
+ **文件位置**:
598
+ - 工具状态文件:\`${toolsStatusFile}\`
599
+ - 工具配置文件:根据技术栈生成在项目根目录(如 \`.eslintrc.json\`, \`ruff.toml\`, \`rustfmt.toml\` 等)
600
+
601
+ **参考规则**:
602
+ - 请参考 \`${CORE_DIRNAME}/05-tooling/ai-tools-init-rules.md\` 中的详细规则
603
+ - 需要根据项目的技术栈选择合适的工具
604
+ - 需要生成相应的配置文件
605
+ - 需要设置 pre-commit hooks(如适用)
606
+
607
+ **执行步骤**:
608
+ 1. 先读取 \`${CORE_DIRNAME}/05-tooling/ai-tools-init-rules.md\` 了解规则
609
+ 2. ${hasOverview ? `读取 \`${CORE_DIRNAME}/06-project/ai-overview.md\` 了解技术栈` : '检测项目的技术栈(如果项目概览文档不存在,请先分析项目结构识别技术栈)'}
610
+ 3. ${hasToolsStatus ? `读取现有的 \`${CORE_DIRNAME}/tools-status.yaml\` 了解当前工具状态` : '检查当前工具安装状态'}
611
+ 4. 根据技术栈选择需要初始化的工具:
612
+ - 前端(React/Vue):ESLint、Prettier、PostCSS
613
+ - 后端(Python):Ruff、Black、pre-commit
614
+ - 后端(Java):Maven、Checkstyle、Spotless、pre-commit
615
+ - 后端(Rust):rustfmt、clippy、pre-commit
616
+ 5. 生成工具配置文件(参考 \`${CORE_DIRNAME}/templates/tools-config/\` 中的模板)
617
+ 6. 更新 \`${CORE_DIRNAME}/tools-status.yaml\` 记录工具状态
618
+
619
+ **重要提示**:
620
+ `;
621
+
622
+ if (!hasOverview) {
623
+ toolsPrompt += `- ⚠️ **建议先执行"项目概览初始化"**,以便更准确地识别技术栈
624
+ - 如果项目概览文档不存在,请先分析项目结构识别技术栈
625
+ `;
626
+ } else {
627
+ toolsPrompt += `- ✅ 项目概览文档已存在,请先读取 \`${CORE_DIRNAME}/06-project/ai-overview.md\` 了解技术栈
628
+ `;
629
+ }
630
+
631
+ toolsPrompt += `- ✅ **工具状态文件在更新 AODW 时会被保护,不会被覆盖**
632
+ - ✅ **工具配置文件在项目根目录,不会被 AODW 更新影响**
633
+ - 确保生成的配置文件符合项目规范
634
+ - 如果工具已存在,请检查配置是否需要更新
635
+ - **此命令可以重复执行**,每次执行会检查并更新工具配置
636
+
637
+ 请开始执行。`;
638
+
639
+ console.log(chalk.white(toolsPrompt));
640
+ console.log(chalk.gray('\n' + '='.repeat(60)));
641
+ console.log(chalk.green('\n✅ 提示词已生成,请复制上面的内容给您的 AI 助手。'));
642
+
643
+ if (!hasOverview) {
644
+ console.log(chalk.yellow('\n⚠️ 检测到项目概览文档不存在,建议先执行"项目概览初始化"。\n'));
645
+ } else {
646
+ console.log(chalk.green('\n✅ 项目概览文档已存在,可以基于它来初始化工具。\n'));
647
+ }
648
+
649
+ if (hasToolsStatus) {
650
+ console.log(chalk.blue('📝 检测到已有工具状态文件,将基于现有状态进行更新。\n'));
651
+ }
652
+ }
653
+
433
654
  async function configureMode(pause = true, forceConnect = false) {
434
655
  const { mode } = await inquirer.prompt([{
435
656
  type: 'list',
@@ -515,8 +736,8 @@ async function showMainMenu() {
515
736
  { name: '2. 配置全局开发模式 (单机/联网)', value: 'config' },
516
737
 
517
738
  new inquirer.Separator('--- 工具箱 ---'),
518
- { name: '3. 工具初始化 (ESLint/Ruff/Stack)', value: 'init-tools' },
519
- { name: '4. 项目概览初始化 (Architecture)', value: 'init-overview' },
739
+ { name: '3. 项目概览初始化 (Architecture) - 生成提示词', value: 'init-overview-prompt' },
740
+ { name: '4. 工具初始化 (ESLint/Ruff/Stack) - 生成提示词', value: 'init-tools-prompt' },
520
741
 
521
742
  new inquirer.Separator('--- 帮助与维护 ---'),
522
743
  { name: '5. 查看帮助 & 部署指南', value: 'help' },
@@ -541,12 +762,12 @@ async function showMainMenu() {
541
762
  await configureMode();
542
763
  await returnToMenu();
543
764
  break;
544
- case 'init-tools':
545
- await initTools();
765
+ case 'init-overview-prompt':
766
+ await generateOverviewPrompt();
546
767
  await returnToMenu();
547
768
  break;
548
- case 'init-overview':
549
- await initOverview({ interactive: true });
769
+ case 'init-tools-prompt':
770
+ await generateToolsPrompt();
550
771
  await returnToMenu();
551
772
  break;
552
773
  case 'help':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aodw-skill",
3
- "version": "0.7.7",
3
+ "version": "0.7.8",
4
4
  "description": "Next-channel CLI tool to scaffold AODW in your project",
5
5
  "main": "bin/aodw.js",
6
6
  "files": [