forager.js
4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/**
* 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 };