import fs from 'node:fs/promises'; import path from 'node:path'; const OPENCODE_PKG_DIR = './packages/opencode'; const TOOLS_DIR = './tools'; const DOCS_AGENTS_DIR = './documentation/docs/20-instructions/.generated'; /** * Sync skills from tools/ to opencode package (direct copy) */ async function sync_skills() { const source = path.join(TOOLS_DIR, 'skills'); const dest = path.join(OPENCODE_PKG_DIR, 'skills'); await fs.rm(dest, { recursive: true, force: true }); await fs.cp(source, dest, { recursive: true }); console.log('Synced skills to opencode package'); } /** * Sync AGENTS.md from tools/ to opencode package and documentation site */ async function sync_agents_md() { const source = path.join(TOOLS_DIR, 'instructions', 'AGENTS.md'); const opencode_dest = path.join(OPENCODE_PKG_DIR, 'instructions', 'opencode-agents.md'); const docs_dest = path.join(DOCS_AGENTS_DIR, 'agents.md'); await fs.mkdir(path.dirname(opencode_dest), { recursive: true }); await fs.mkdir(DOCS_AGENTS_DIR, { recursive: true }); const content = await fs.readFile(source, 'utf-8'); await fs.writeFile(opencode_dest, content); await fs.writeFile(docs_dest, content); console.log('Synced AGENTS.md to opencode package and documentation'); } interface AgentData { name: string; description: string; prompt: string; } /** * Parse a markdown agent file with frontmatter (name, description) and body (prompt) */ function parse_agent_md(content: string, file_path: string): AgentData | null { const frontmatter_match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); if (!frontmatter_match) { console.warn(`Skipping ${file_path}: no frontmatter found`); return null; } const frontmatter = frontmatter_match[1] ?? ''; const body = (frontmatter_match[2] ?? '').trim(); const name = frontmatter.match(/^name:\s*(.+)$/m)?.[1]?.trim(); const description = frontmatter.match(/^description:\s*(.+)$/m)?.[1]?.trim(); if (!name || !description) { console.warn(`Skipping ${file_path}: missing name or description in frontmatter`); return null; } return { name, description, prompt: body }; } /** * Generate agents.ts module from tools/agents/*.md files */ async function sync_agents() { const agents_dir = path.join(TOOLS_DIR, 'agents'); const entries = await fs.readdir(agents_dir, { withFileTypes: true }); const md_files = entries.filter((e) => e.isFile() && e.name.endsWith('.md')); const agents: AgentData[] = []; for (const file of md_files) { const file_path = path.join(agents_dir, file.name); const content = await fs.readFile(file_path, 'utf-8'); const agent = parse_agent_md(content, file_path); if (agent) { agents.push(agent); } } const output = [ '// This file is auto-generated by scripts/sync-opencode-plugin.ts', '// Do not edit manually — edit the markdown files in tools/agents/ instead.', '', `export const agents = ${JSON.stringify( Object.fromEntries( agents.map((a) => [a.name, { description: a.description, prompt: a.prompt }]), ), null, '\t', )} as const;`, '', ].join('\n'); const dest = path.join(OPENCODE_PKG_DIR, 'agents.ts'); await fs.writeFile(dest, output); console.log(`Generated agents.ts with ${agents.length} agent(s)`); } await sync_skills(); await sync_agents_md(); await sync_agents(); console.log('OpenCode plugin sync complete');