llm-workbench / scripts /gen-agents.js
nad707's picture
feat: flatten repo and rebootstrap hf workspace
bf96836
#!/usr/bin/env node
/**
* Agent Generator
*
* Generates platform-specific agent files from the shared source in .ai/agents/.
* Each agent body is stored once in .ai/agents/{name}.md (no frontmatter).
* Per-platform frontmatter is defined in scripts/agent-config.json.
*
* Outputs:
* .claude/agents/{name}.md — Claude Code agents
* .gemini/agents/{name}.md — Gemini CLI agents
*
* Usage:
* node scripts/gen-agents.js
* node scripts/gen-agents.js --dry-run (preview only, no writes)
*/
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const AI_AGENTS_DIR = path.join(ROOT, '.ai', 'agents');
const CLAUDE_AGENTS_DIR = path.join(ROOT, '.claude', 'agents');
const GEMINI_AGENTS_DIR = path.join(ROOT, '.gemini', 'agents');
const CONFIG_FILE = path.join(__dirname, 'agent-config.json');
const isDryRun = process.argv.includes('--dry-run');
function ensureDir(dir) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
function buildFrontmatter(config) {
const lines = ['---'];
for (const [key, value] of Object.entries(config)) {
if (Array.isArray(value)) {
lines.push(`${key}: [${value.map(v => `"${v}"`).join(', ')}]`);
} else if (typeof value === 'string') {
lines.push(`${key}: ${value}`);
} else {
lines.push(`${key}: ${value}`);
}
}
lines.push('---');
return lines.join('\n');
}
function generateAgent(name, body, platformConfig, outputDir) {
const frontmatter = buildFrontmatter(platformConfig);
const content = `${frontmatter}\n\n${body.trimStart()}`;
const outputFile = path.join(outputDir, `${name}.md`);
if (isDryRun) {
console.log(`[dry-run] Would write: ${outputFile}`);
console.log(` Frontmatter keys: ${Object.keys(platformConfig).join(', ')}`);
return;
}
fs.writeFileSync(outputFile, content, 'utf8');
console.log(` Written: ${outputFile}`);
}
function main() {
// Read config
if (!fs.existsSync(CONFIG_FILE)) {
console.error(`Error: Config file not found: ${CONFIG_FILE}`);
process.exit(1);
}
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
// Ensure output dirs exist
if (!isDryRun) {
ensureDir(CLAUDE_AGENTS_DIR);
ensureDir(GEMINI_AGENTS_DIR);
}
const agentNames = Object.keys(config);
console.log(`Generating ${agentNames.length} agents for 2 platforms...\n`);
let claudeCount = 0;
let geminiCount = 0;
const errors = [];
for (const name of agentNames) {
const sourceFile = path.join(AI_AGENTS_DIR, `${name}.md`);
if (!fs.existsSync(sourceFile)) {
errors.push(`Missing source: ${sourceFile}`);
continue;
}
const body = fs.readFileSync(sourceFile, 'utf8');
const agentConfig = config[name];
console.log(`${name}:`);
// Generate Claude agent
if (agentConfig.claude) {
generateAgent(name, body, agentConfig.claude, CLAUDE_AGENTS_DIR);
claudeCount++;
}
// Generate Gemini agent
if (agentConfig.gemini) {
generateAgent(name, body, agentConfig.gemini, GEMINI_AGENTS_DIR);
geminiCount++;
}
}
console.log(`\nDone.`);
if (isDryRun) {
console.log(`[dry-run] No files written.`);
} else {
console.log(` Claude agents: ${claudeCount}${CLAUDE_AGENTS_DIR}`);
console.log(` Gemini agents: ${geminiCount}${GEMINI_AGENTS_DIR}`);
}
if (errors.length > 0) {
console.error('\nErrors:');
for (const err of errors) {
console.error(` ${err}`);
}
process.exit(1);
}
}
main();