#!/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)}`); } } }