automagik-genie
Version:
Self-evolving AI agent orchestration framework with Model Context Protocol support
290 lines (252 loc) • 7.28 kB
JavaScript
/**
* Bullet Retrieval Tool
*
* Find and query structured learning bullets.
*
* Usage:
* genie helper bullet-find ID
* genie helper bullet-find --top-helpful --limit=10
* genie helper bullet-find --top-harmful --limit=10
* genie helper bullet-find --file=learn.md --section="Section"
* genie helper bullet-find --search="keyword"
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
/**
* Parse bullet line into structured data
*/
function parseBullet(line, file, lineNum) {
const match = line.match(/^- \[([a-z]+-\d{3})\] helpful=(\d+) harmful=(\d+): (.+)$/);
if (!match) {
return null;
}
return {
id: match[1],
helpful: parseInt(match[2], 10),
harmful: parseInt(match[3], 10),
content: match[4],
file: path.relative(process.cwd(), file),
line: lineNum
};
}
/**
* Find bullet by ID
*/
function findById(id) {
try {
const result = execSync(
`rg -n "^- \\[${id}\\]" .genie/`,
{ encoding: 'utf-8', cwd: process.cwd() }
).trim();
if (!result) {
return null;
}
// Parse: file:line:content
const match = result.match(/^([^:]+):(\d+):(.+)$/);
if (!match) {
return null;
}
const [, file, lineNum, content] = match;
return parseBullet(content, file, parseInt(lineNum, 10));
} catch (err) {
if (err.status === 1) {
return null; // Not found
}
throw err;
}
}
/**
* Find all bullets in framework
*/
function findAllBullets() {
try {
const result = execSync(
'rg -n "^- \\[[a-z]+-\\d{3}\\] helpful=\\d+ harmful=\\d+:" .genie/',
{ encoding: 'utf-8', cwd: process.cwd() }
).trim();
if (!result) {
return [];
}
const lines = result.split('\n');
const bullets = [];
for (const line of lines) {
const match = line.match(/^([^:]+):(\d+):(.+)$/);
if (match) {
const [, file, lineNum, content] = match;
const bullet = parseBullet(content, file, parseInt(lineNum, 10));
if (bullet) {
bullets.push(bullet);
}
}
}
return bullets;
} catch (err) {
if (err.status === 1) {
return []; // None found
}
throw err;
}
}
/**
* Find bullets in specific file
*/
function findInFile(filePath) {
const fullPath = path.resolve(process.cwd(), filePath);
if (!fs.existsSync(fullPath)) {
return [];
}
try {
const result = execSync(
`rg -n "^- \\[[a-z]+-\\d{3}\\] helpful=\\d+ harmful=\\d+:" "${fullPath}"`,
{ encoding: 'utf-8' }
).trim();
if (!result) {
return [];
}
const lines = result.split('\n');
const bullets = [];
for (const line of lines) {
// When rg searches a single file, output is "line:content" not "file:line:content"
const match = line.match(/^(\d+):(.+)$/);
if (match) {
const [, lineNum, content] = match;
const bullet = parseBullet(content, fullPath, parseInt(lineNum, 10));
if (bullet) {
bullets.push(bullet);
}
}
}
return bullets;
} catch (err) {
if (err.status === 1) {
return []; // None found
}
throw err;
}
}
/**
* Search bullets by content keyword
*/
function searchByKeyword(keyword) {
const allBullets = findAllBullets();
return allBullets.filter(b =>
b.content.toLowerCase().includes(keyword.toLowerCase()) ||
b.id.includes(keyword)
);
}
/**
* Get top N bullets by helpful count
*/
function getTopHelpful(limit = 10) {
const bullets = findAllBullets();
bullets.sort((a, b) => b.helpful - a.helpful);
return bullets.slice(0, limit);
}
/**
* Get top N bullets by harmful count
*/
function getTopHarmful(limit = 10) {
const bullets = findAllBullets();
bullets.sort((a, b) => b.harmful - a.harmful);
return bullets.slice(0, limit);
}
/**
* Display bullets
*/
function displayBullets(bullets, title = null) {
if (title) {
console.log(`\n${title}\n${'='.repeat(title.length)}\n`);
}
if (bullets.length === 0) {
console.log('No bullets found');
return;
}
for (const bullet of bullets) {
console.log(`[${bullet.id}] helpful=${bullet.helpful} harmful=${bullet.harmful}`);
console.log(` ${bullet.file}:${bullet.line}`);
console.log(` ${bullet.content.substring(0, 100)}${bullet.content.length > 100 ? '...' : ''}`);
console.log('');
}
console.log(`Total: ${bullets.length} bullet(s)`);
}
/**
* Main CLI
*/
async function main() {
const args = process.argv.slice(2);
// Help
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
console.log('Usage:');
console.log(' genie helper bullet-find ID');
console.log(' Find specific bullet by ID');
console.log('');
console.log(' genie helper bullet-find --top-helpful --limit=10');
console.log(' Find top 10 most helpful bullets');
console.log('');
console.log(' genie helper bullet-find --top-harmful --limit=10');
console.log(' Find top 10 most harmful bullets');
console.log('');
console.log(' genie helper bullet-find --file=file.md');
console.log(' Find all bullets in specific file');
console.log('');
console.log(' genie helper bullet-find --search="keyword"');
console.log(' Search bullets by content');
console.log('');
console.log('Examples:');
console.log(' $ genie helper bullet-find learn-042');
console.log(' $ genie helper bullet-find --top-helpful --limit=5');
console.log(' $ genie helper bullet-find --search="delegate"');
return;
}
// Find by ID
if (args[0] && !args[0].startsWith('--')) {
const bullet = findById(args[0]);
if (!bullet) {
console.error(`Error: Bullet [${args[0]}] not found`);
process.exit(1);
}
displayBullets([bullet]);
return;
}
// Top helpful
if (args.includes('--top-helpful')) {
const limitArg = args.find(a => a.startsWith('--limit='));
const limit = limitArg ? parseInt(limitArg.split('=')[1], 10) : 10;
const bullets = getTopHelpful(limit);
displayBullets(bullets, `Top ${limit} Most Helpful Bullets`);
return;
}
// Top harmful
if (args.includes('--top-harmful')) {
const limitArg = args.find(a => a.startsWith('--limit='));
const limit = limitArg ? parseInt(limitArg.split('=')[1], 10) : 10;
const bullets = getTopHarmful(limit);
displayBullets(bullets, `Top ${limit} Most Harmful Bullets`);
return;
}
// Find in file
const fileArg = args.find(a => a.startsWith('--file='));
if (fileArg) {
const filePath = fileArg.split('=')[1];
const bullets = findInFile(filePath);
displayBullets(bullets, `Bullets in ${filePath}`);
return;
}
// Search by keyword
const searchArg = args.find(a => a.startsWith('--search='));
if (searchArg) {
const keyword = searchArg.split('=')[1];
const bullets = searchByKeyword(keyword);
displayBullets(bullets, `Search results for "${keyword}"`);
return;
}
console.error('Error: No valid operation specified');
console.error('Use --help to see available options');
process.exit(1);
}
main().catch(err => {
console.error('ERROR:', err.message);
process.exit(1);
});