bookworm-smart-assistant/scripts/project-detector.js

162 lines
4.7 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/**
* 项目类型检测器 (v5.0)
*
* 基于文件存在性 + 内容模式匹配检测 9 种项目类型,
* 为匹配的技能提供 boost 加成
*
* 核心函数:
* detectProjectType(cwd) 检测到的项目类型列表
* getProjectBoost(cwd) { skillName boost } 映射
*/
const fs = require('fs');
const path = require('path');
// 9 种项目签名
const PROJECT_SIGNATURES = {
react: {
files: ['package.json'],
contentPatterns: [{ file: 'package.json', pattern: /"react"/ }],
skills: ['frontend-expert', 'react-specialist', 'performance-expert'],
},
vue: {
files: ['package.json'],
contentPatterns: [{ file: 'package.json', pattern: /"vue"/ }],
skills: ['frontend-expert', 'vue-specialist'],
},
nextjs: {
files: ['next.config.js', 'next.config.mjs', 'next.config.ts'],
contentPatterns: [{ file: 'package.json', pattern: /"next"/ }],
skills: ['frontend-expert', 'fullstack-expert', 'ssr-expert'],
},
node: {
files: ['package.json', 'server.js', 'app.js', 'index.js'],
contentPatterns: [{ file: 'package.json', pattern: /"express"|"koa"|"fastify"|"hapi"/ }],
skills: ['backend-builder', 'api-expert', 'node-specialist'],
},
python: {
files: ['requirements.txt', 'setup.py', 'pyproject.toml', 'Pipfile'],
contentPatterns: [],
skills: ['python-expert', 'backend-builder'],
},
'python-ml': {
files: ['requirements.txt', 'setup.py'],
contentPatterns: [
{ file: 'requirements.txt', pattern: /torch|tensorflow|sklearn|keras|numpy|pandas/ },
],
skills: ['ai-ml-expert', 'data-scientist', 'python-expert'],
},
docker: {
files: ['Dockerfile', 'docker-compose.yml', 'docker-compose.yaml', '.dockerignore'],
contentPatterns: [],
skills: ['devops-expert', 'sre-expert', 'docker-specialist'],
},
k8s: {
files: ['k8s/', 'kubernetes/', 'helm/', 'Chart.yaml', 'kustomization.yaml'],
contentPatterns: [],
skills: ['devops-expert', 'sre-expert', 'k8s-specialist'],
},
miniprogram: {
files: ['app.json', 'project.config.json'],
contentPatterns: [{ file: 'app.json', pattern: /"pages"/ }],
skills: ['miniprogram-expert', 'frontend-expert'],
},
};
/**
* 检测项目类型
* @param {string} cwd - 项目目录
* @returns {string[]} 检测到的项目类型列表
*/
function detectProjectType(cwd) {
if (!cwd || !fs.existsSync(cwd)) return [];
const detected = [];
for (const [type, sig] of Object.entries(PROJECT_SIGNATURES)) {
let fileMatch = false;
let contentMatch = false;
// 文件存在性检查
for (const file of sig.files) {
const filePath = path.join(cwd, file);
if (fs.existsSync(filePath)) {
fileMatch = true;
break;
}
}
if (!fileMatch) continue;
// 内容模式匹配
if (sig.contentPatterns.length === 0) {
// 无内容模式要求,文件存在即可
detected.push(type);
} else {
for (const cp of sig.contentPatterns) {
try {
const filePath = path.join(cwd, cp.file);
if (fs.existsSync(filePath)) {
const content = fs.readFileSync(filePath, 'utf8');
if (cp.pattern.test(content)) {
contentMatch = true;
break;
}
}
} catch {}
}
if (contentMatch) detected.push(type);
}
}
return detected;
}
/**
* 获取项目类型对技能的 boost 映射
* @param {string} cwd - 项目目录
* @returns {Object} { skillName boost } 每匹配 +0.15
*/
function getProjectBoost(cwd) {
const types = detectProjectType(cwd);
const boosts = {};
for (const type of types) {
const sig = PROJECT_SIGNATURES[type];
if (!sig) continue;
for (const skill of sig.skills) {
boosts[skill] = (boosts[skill] || 0) + 0.15;
}
}
return boosts;
}
// 模块导出
if (typeof module !== 'undefined') {
module.exports = {
PROJECT_SIGNATURES,
detectProjectType,
getProjectBoost,
};
}
// CLI 入口
if (require.main === module) {
const cwd = process.argv[2] || process.cwd();
const types = detectProjectType(cwd);
const boosts = getProjectBoost(cwd);
console.log(`=== 项目类型检测 ===`);
console.log(`目录: ${cwd}`);
console.log(`检测到: ${types.length > 0 ? types.join(', ') : '无匹配'}`);
if (Object.keys(boosts).length > 0) {
console.log('\n技能加成:');
for (const [skill, boost] of Object.entries(boosts).sort((a, b) => b[1] - a[1])) {
console.log(` ${skill.padEnd(30)} +${boost.toFixed(2)}`);
}
}
}