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.
- package/bin/aodw.js +228 -7
- 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
|
-
|
|
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.
|
|
519
|
-
{ name: '4.
|
|
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-
|
|
545
|
-
await
|
|
765
|
+
case 'init-overview-prompt':
|
|
766
|
+
await generateOverviewPrompt();
|
|
546
767
|
await returnToMenu();
|
|
547
768
|
break;
|
|
548
|
-
case 'init-
|
|
549
|
-
await
|
|
769
|
+
case 'init-tools-prompt':
|
|
770
|
+
await generateToolsPrompt();
|
|
550
771
|
await returnToMenu();
|
|
551
772
|
break;
|
|
552
773
|
case 'help':
|