162 lines
4.7 KiB
JavaScript
162 lines
4.7 KiB
JavaScript
#!/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)}`);
|
|
}
|
|
}
|
|
}
|