/** * Forager — 扫描 Skill 并写入数据库 * * 支持扫描单个 Skill(per-skill 隔离)或全部 Skill。 * * 用法: * forageOneSkill('code-review') # 只扫描一个 Skill * forageSkills() # 扫描全部 Skill */ const path = require('path'); const fs = require('fs'); const { serializeSkillToGenome, scanSkillsDirectory } = require('./genome/serializer'); const evolutionQueries = require('./queries').evolutionQueries; const DEFAULT_SKILLS_DIR = path.join(require('os').homedir(), '.gemini/antigravity/skills'); /** * 将一个基因组写入数据库 */ async function persistGenome(genome, runId) { const row = await evolutionQueries.insertGenome({ runId, skillName: genome.meta.name, generation: 0, parentIds: [], mentalModels: genome.mentalModels, patternLibrary: genome.patternLibrary, judgment: genome.judgment, toolFluency: genome.toolFluency, taste: genome.taste, scarTissue: genome.scarTissue, isElite: false, }); const total = [ genome.mentalModels, genome.patternLibrary, genome.judgment, genome.toolFluency, genome.taste, genome.scarTissue, ].reduce((sum, dim) => { return sum + Object.values(dim).reduce((s, v) => s + (Array.isArray(v) ? v.length : (typeof v === 'object' ? Object.keys(v).length : 1)), 0); }, 0); return { name: genome.meta.name, id: row.id, total }; } /** * 扫描单个 Skill 并写入数据库 * * 每个 Skill 创建独立的 Run。 * * @param {string} skillName - Skill 名称 (目录名) * @param {string} skillsDir - Skills 根目录 * @returns {object|null} { name, id, total } 或 null */ async function forageOneSkill(skillName, skillsDir = DEFAULT_SKILLS_DIR) { const skillMd = path.join(skillsDir, skillName, 'SKILL.md'); if (!fs.existsSync(skillMd)) { console.log(`❌ 未找到: ${skillMd}`); return null; } console.log(`\n🔍 Forage: ${skillName}`); const genome = serializeSkillToGenome(skillMd); // 为该 Skill 创建独立的 Run const run = await evolutionQueries.createRun(skillName, { maxGenerations: 0, populationSize: 1, description: `${skillName} 进化 - ${new Date().toISOString()}`, }); const result = await persistGenome(genome, run.id); console.log(` ✅ ${result.name.padEnd(25)} ${String(result.total).padStart(3)} 条目 → ${result.id.slice(0, 8)}`); console.log(` 📦 Run: ${run.id}\n`); return result; } /** * 扫描全部 Skill(兼容旧调用) */ async function forageSkills(skillsDir = DEFAULT_SKILLS_DIR) { console.log(`\n🔍 扫描 Skills 目录: ${skillsDir}\n`); const genomes = scanSkillsDirectory(skillsDir); if (genomes.length === 0) { console.log('⚠️ 没有找到任何 Skill'); return []; } const results = []; for (const genome of genomes) { try { const run = await evolutionQueries.createRun(genome.meta.name, { maxGenerations: 0, populationSize: 1, description: `${genome.meta.name} 进化 - ${new Date().toISOString()}`, }); const result = await persistGenome(genome, run.id); console.log(` ✅ ${result.name.padEnd(25)} ${String(result.total).padStart(3)} 条目 → Run ${run.id.slice(0, 8)}`); results.push(result); } catch (err) { console.error(` ❌ ${genome.meta.name}: ${err.message}`); } } console.log(`\n✅ 入库完成: ${results.length}/${genomes.length} 个 Skill`); return results; } // CLI if (require.main === module) { const args = process.argv.slice(2); const skillName = args.find(a => !a.startsWith('--')); const skillsDir = args.find(a => a.startsWith('--dir='))?.split('=')[1] || DEFAULT_SKILLS_DIR; if (skillName) { forageOneSkill(skillName, skillsDir) .then(() => process.exit(0)) .catch(err => { console.error('Fatal:', err); process.exit(1); }); } else { forageSkills(skillsDir) .then(() => process.exit(0)) .catch(err => { console.error('Fatal:', err); process.exit(1); }); } } module.exports = { forageOneSkill, forageSkills };