#!/usr/bin/env bun /** * Skills to OKF Converter * * Converts frappe-bench skills to OKF format for pi-harness-runtime * - Sanitizes sensitive info (IPs, URLs, API keys) * - Converts to OKF markdown format * - Preserves procedures and best practices */ import { readdir, readFile, writeFile, mkdir } from "fs/promises"; import { existsSync } from "fs"; const SOURCE_DIR = "/home/frappe/frappe-bench/.claude-plugins/moocoding-skills/skills"; const OUTPUT_DIR = "./OKF/skills"; // Sensitive patterns to sanitize const SANITIZE_PATTERNS: [RegExp, string][] = [ // IP addresses [/(\b\d{1,3}\.){3}\d{1,3}\b/g, "{IP}"], // Specific domains (high priority - before generic) [/grist\.bunchee\.online/gi, "{GRIST_DOMAIN}"], [ /(https?:\/\/)?[\w.-]+\.(bunchee\.online|hetzner\.com|digitalocean\.com)/gi, "{DOMAIN}", ], // API keys (generic pattern - 32+ chars) [/(? { const sections: Record = {}; const lines = content.split("\n"); let currentSection = "Details"; let currentBody: string[] = []; for (const line of lines) { const h2Match = line.match(/^## (.+)$/); const h3Match = line.match(/^### (.+)$/); if (h2Match) { if (currentBody.length > 0) { sections[currentSection] = currentBody.join("\n"); } currentSection = h2Match[1]; currentBody = []; } else if (h3Match) { currentBody.push(`### ${h3Match[1]}`); } else if ( !line.startsWith("---") && !line.match(/^name:|^description:|^scope:/) ) { currentBody.push(line); } } if (currentBody.length > 0) { sections[currentSection] = currentBody.join("\n"); } return sections; } async function convertSkills(): Promise { if (!existsSync(OUTPUT_DIR)) { await mkdir(OUTPUT_DIR, { recursive: true }); } const skillDirs = await readdir(SOURCE_DIR); let converted = 0; let skipped = 0; for (const dir of skillDirs) { const skillFile = `${SOURCE_DIR}/${dir}/SKILL.md`; if (!existsSync(skillFile)) { skipped++; continue; } try { const content = await readFile(skillFile, "utf-8"); const okfContent = convertToOKF(skillFile, content); if (okfContent) { const outputPath = `${OUTPUT_DIR}/${dir}.md`; await writeFile(outputPath, okfContent); console.log(`āœ… Converted: ${dir}`); converted++; } else { console.log(`āš ļø Skipped (no metadata): ${dir}`); skipped++; } } catch (err) { console.error(`āŒ Error converting ${dir}:`, err); skipped++; } } console.log(`\nšŸ“Š Results: ${converted} converted, ${skipped} skipped`); } convertSkills();