/** * Genome Serializer — 将 SKILL.md 文件分解为 6 维认知能力基因组 * * 输入: SKILL.md 文件路径 * 输出: { mentalModels, patternLibrary, judgment, toolFluency, taste, scarTissue } * * 分解规则(从 SKILL.md 的结构化内容中提取): * * SKILL.md 区域 → 认知维度 * ───────────────────────────────────────── * 第一性原理 / 铁律 → mentalModels (因果链 + 类比) * 反模式检查 / Anti-Pattern → scarTissue (失败教训 + 反模式) * SOP 步骤 + 工具引用 → toolFluency (工具 + 流程) * When to Use / NOT for → judgment (决策规则 + 边界) * 输出模板 / 质量清单 → taste (标杆 + 质量信号) * 模式匹配规则 / 分类标记 → patternLibrary (问题模式 + 解决模式) */ const fs = require('fs'); const path = require('path'); // ══════════════════════════════════════ // SKILL.md 解析器 // ══════════════════════════════════════ function parseSkillFrontmatter(content) { const match = content.match(/^---\n([\s\S]*?)\n---/); if (!match) return {}; const yaml = match[1]; const result = {}; // 简易 YAML 解析(不依赖外部库) const lines = yaml.split('\n'); let currentKey = null; let currentValue = ''; for (const line of lines) { const keyMatch = line.match(/^(\w[\w.]*?):\s*(.*)/); if (keyMatch && !line.startsWith(' ')) { if (currentKey) result[currentKey] = currentValue.trim(); currentKey = keyMatch[1]; currentValue = keyMatch[2]; } else if (currentKey) { currentValue += ' ' + line.trim(); } } if (currentKey) result[currentKey] = currentValue.trim(); return result; } function parseMarkdownSections(content) { // 去掉 frontmatter const body = content.replace(/^---[\s\S]*?---\n*/, ''); const sections = []; const lines = body.split('\n'); let currentSection = null; let currentContent = []; for (const line of lines) { const headingMatch = line.match(/^(#{1,4})\s+(.+)/); if (headingMatch) { if (currentSection) { currentSection.content = currentContent.join('\n').trim(); sections.push(currentSection); } currentSection = { level: headingMatch[1].length, title: headingMatch[2].trim(), content: '', }; currentContent = []; } else { currentContent.push(line); } } if (currentSection) { currentSection.content = currentContent.join('\n').trim(); sections.push(currentSection); } return sections; } function extractBlockquotes(text) { const quotes = []; const regex = /^>\s*\*?\*?(.+?)\*?\*?\s*$/gm; let match; while ((match = regex.exec(text)) !== null) { quotes.push(match[1].trim()); } return quotes; } function extractTableRows(text) { const rows = []; const lines = text.split('\n').filter(l => l.includes('|') && !l.match(/^\s*\|[-\s|]+\|\s*$/)); for (const line of lines) { const cells = line.split('|').map(c => c.trim()).filter(Boolean); if (cells.length >= 2) rows.push(cells); } // 去掉表头(第一行) return rows.length > 1 ? rows.slice(1) : rows; } function extractCodeBlocks(text) { const blocks = []; const regex = /```(\w*)\n([\s\S]*?)```/g; let match; while ((match = regex.exec(text)) !== null) { blocks.push({ language: match[1] || 'text', code: match[2].trim() }); } return blocks; } function extractTools(text) { const tools = new Set(); const toolNames = [ 'grep_search', 'find_by_name', 'view_file', 'view_code_item', 'view_file_outline', 'search_web', 'read_url_content', 'run_command', 'write_to_file', 'replace_file_content', 'multi_replace_file_content', 'generate_image', 'list_dir', ]; for (const tool of toolNames) { if (text.includes(tool)) tools.add(tool); } return [...tools]; } // ══════════════════════════════════════ // 6 维分解器 // ══════════════════════════════════════ function extractMentalModels(sections) { const models = { causalChains: [], systemDynamics: [], analogies: [], ironLaws: [], }; for (const sec of sections) { const titleLower = sec.title.toLowerCase(); // 第一性原理、理论、原理 if (titleLower.includes('第一性') || titleLower.includes('原理') || titleLower.includes('principle')) { const quotes = extractBlockquotes(sec.content); for (const q of quotes) { if (q.includes('→') || q.includes('因为') || q.includes('所以') || q.includes('导致')) { models.causalChains.push(q); } else if (q.includes('效应') || q.includes('理论') || q.includes('模型')) { models.analogies.push(q); } } // 来自 xxx 的子节标题 → 类比 const analogyMatch = sec.title.match(/来自(.+?)[::]/); if (analogyMatch) { models.analogies.push({ domain: analogyMatch[1].trim(), insight: sec.content.split('\n').find(l => l.startsWith('>'))?.replace(/^>\s*/, '') || sec.title, }); } } // 铁律、铁则 if (titleLower.includes('铁律') || titleLower.includes('iron') || sec.content.includes('铁律')) { const quotes = extractBlockquotes(sec.content); models.ironLaws.push(...quotes); } // 推论 if (sec.content.includes('推论')) { const numbered = sec.content.match(/\d+\.\s+\*\*(.+?)\*\*/g); if (numbered) { models.causalChains.push(...numbered.map(n => n.replace(/\d+\.\s+\*\*|\*\*/g, ''))); } } } // 从整个文档提取铁律格式的 blockquotes const allContent = sections.map(s => s.content).join('\n'); const ironLawQuotes = allContent.match(/>\s*\*\*铁律[::]\*\*\s*(.+)/g); if (ironLawQuotes) { models.ironLaws.push(...ironLawQuotes.map(q => q.replace(/>\s*\*\*铁律[::]\*\*\s*/, ''))); } return models; } function extractPatternLibrary(sections) { const patterns = { problemPatterns: [], solutionPatterns: [], classificationRules: [], }; for (const sec of sections) { // 分类标记表 → 解决方案模式 if (sec.content.includes('分类标记') || sec.content.includes('✅') && sec.content.includes('❌')) { const rows = extractTableRows(sec.content); for (const row of rows) { if (row.length >= 3) { patterns.classificationRules.push({ marker: row[0], meaning: row[1], action: row[2], }); } } } // 输出模板 → 模式 if (sec.title.includes('模板') || sec.title.includes('template')) { const codeBlocks = extractCodeBlocks(sec.content); for (const block of codeBlocks) { patterns.solutionPatterns.push({ name: sec.title, template: block.code, format: block.language, }); } } // grep 什么表 → 问题识别模式 if (sec.content.includes('grep 什么')) { const rows = extractTableRows(sec.content); for (const row of rows) { if (row.length >= 3) { patterns.problemPatterns.push({ dimension: row[0], searchTerms: row[1], rationale: row[2], }); } } } } return patterns; } function extractJudgment(sections, frontmatter) { const judgment = { decisionRules: [], tradeoffs: [], boundaries: [], triggers: [], exclusions: [], }; // frontmatter triggers if (frontmatter.triggers) { judgment.triggers = frontmatter.triggers.split(',').map(t => t.trim()).filter(Boolean); } for (const sec of sections) { const titleLower = sec.title.toLowerCase(); // When to Use → 决策规则 if (titleLower.includes('when to use') || titleLower.includes('何时使用')) { const items = sec.content.match(/^-\s+(.+)/gm); if (items) { judgment.decisionRules.push(...items.map(i => i.replace(/^-\s+/, ''))); } } // NOT for → 能力边界 if (sec.content.includes('NOT for') || sec.content.includes('不适用')) { const items = sec.content.match(/^-\s+(.+)/gm); if (items) { judgment.exclusions.push(...items.map(i => i.replace(/^-\s+/, ''))); } } // 止损规则 → 边界 if (sec.content.includes('止损') || sec.content.includes('暂停')) { const stopRules = sec.content.match(/\*\*止损.+?\*\*[::]\s*(.+)/g); if (stopRules) { judgment.boundaries.push(...stopRules.map(r => r.replace(/\*\*/g, ''))); } } // 耗时比 → 权衡偏好 if (sec.content.includes('耗时比') || sec.content.includes('时间分配')) { const ratios = sec.content.match(/\d+%\s*[::]\s*\w+/g); if (ratios) { judgment.tradeoffs.push({ type: 'time-allocation', rule: ratios.join(', ') }); } } } return judgment; } function extractToolFluency(sections, skillDir) { const fluency = { coreTools: [], idioms: [], automations: [], dataAccess: [], }; // 从全文提取工具引用 const allContent = sections.map(s => s.content).join('\n'); const tools = extractTools(allContent); fluency.coreTools = tools.map(t => ({ name: t, proficiency: 'documented' })); // SOP 步骤中的工具使用惯用法 for (const sec of sections) { if (sec.title.includes('Step') || sec.title.includes('步骤')) { const toolsInStep = extractTools(sec.content); if (toolsInStep.length > 0) { fluency.idioms.push({ step: sec.title, tools: toolsInStep, description: sec.content.split('\n').find(l => l.startsWith('>'))?.replace(/^>\s*/, '') || '', }); } } // 外部依赖验证 → 数据获取 if (sec.title.includes('外部') || sec.title.includes('External') || sec.title.includes('API')) { if (sec.content.includes('search_web') || sec.content.includes('read_url_content')) { fluency.dataAccess.push({ type: 'external-api-verification', tools: ['search_web', 'read_url_content'], sop: sec.content.match(/\d+\.\s+\*\*(.+?)\*\*/g)?.map(s => s.replace(/\d+\.\s+\*\*|\*\*/g, '')) || [], }); } } } // 检测脚本目录 if (skillDir) { const scriptsDir = path.join(skillDir, 'scripts'); if (fs.existsSync(scriptsDir)) { const scripts = fs.readdirSync(scriptsDir) .filter(f => f.endsWith('.js') || f.endsWith('.ts') || f.endsWith('.sh')) .filter(f => !f.includes('node_modules')); for (const script of scripts) { fluency.automations.push({ name: script, path: `scripts/${script}`, type: path.extname(script).slice(1), }); } } } return fluency; } function extractTaste(sections) { const taste = { exemplars: [], qualitySignals: [], outputStandards: [], }; for (const sec of sections) { // 输出产物表 → 输出标准 if (sec.title.includes('输出') || sec.title.includes('Output') || sec.title.includes('产物')) { const rows = extractTableRows(sec.content); for (const row of rows) { taste.outputStandards.push({ artifact: row[0], format: row[1], location: row[2], audience: row[3], }); } } // 质量清单 → 质量信号 if (sec.content.includes('质量') || sec.content.includes('DoD') || sec.content.includes('清单')) { const items = sec.content.match(/^-\s+(.+)/gm); if (items) { taste.qualitySignals.push(...items.map(i => ({ signal: i.replace(/^-\s+/, ''), source: sec.title, }))); } } // 输出模板中的 markdown 代码块 → 标杆 if (sec.content.includes('```markdown')) { const blocks = extractCodeBlocks(sec.content); for (const block of blocks) { if (block.language === 'markdown') { taste.exemplars.push({ type: 'output-template', name: sec.title, content: block.code, }); } } } } return taste; } function extractScarTissue(sections) { const scars = { failures: [], antiPatterns: [], antiRationalizations: [], }; for (const sec of sections) { const titleLower = sec.title.toLowerCase(); // 反模式检查清单 if (titleLower.includes('反模式') || titleLower.includes('anti-pattern') || titleLower.includes('antipattern')) { const rows = extractTableRows(sec.content); for (const row of rows) { if (row.length >= 3) { scars.antiPatterns.push({ pattern: row[0], symptom: row[1], correction: row[2], }); } } } // 反借口 / Anti-Rationalization if (sec.content.includes('反借口') || sec.content.includes('Anti-Rationalization')) { const rows = extractTableRows(sec.content); for (const row of rows) { if (row.length >= 2) { scars.antiRationalizations.push({ excuse: row[0], rebuttal: row[1], }); } } } // 被推翻的假设 → failures if (sec.content.includes('被推翻') || sec.content.includes('❌')) { const items = sec.content.match(/❌\s*[\""]?(.+?)[\""]?\s*→\s*✅\s*(.+)/g); if (items) { for (const item of items) { const parts = item.match(/❌\s*[\""]?(.+?)[\""]?\s*→\s*✅\s*(.+)/); if (parts) { scars.failures.push({ wrongAssumption: parts[1].trim(), correctReality: parts[2].trim(), lesson: `假设 "${parts[1].trim()}" 是错误的`, }); } } } } // 反模式检查中的 ❌ 标记 const antiItems = sec.content.match(/^-\s+❌\s+(.+)/gm); if (antiItems) { for (const item of antiItems) { const text = item.replace(/^-\s+❌\s+/, ''); if (!scars.antiPatterns.some(a => a.pattern === text)) { scars.antiPatterns.push({ pattern: text, symptom: '', correction: '', }); } } } } return scars; } // ══════════════════════════════════════ // 主入口 // ══════════════════════════════════════ /** * 将 SKILL.md 分解为 6 维基因组 * @param {string} skillPath - SKILL.md 的绝对路径 * @returns {object} 6 维基因组对象 */ function serializeSkillToGenome(skillPath) { const content = fs.readFileSync(skillPath, 'utf-8'); const skillDir = path.dirname(skillPath); const frontmatter = parseSkillFrontmatter(content); const sections = parseMarkdownSections(content); return { // 元信息 meta: { name: frontmatter.name || path.basename(skillDir), description: frontmatter.description || '', category: frontmatter['metadata']?.match(/category:\s*(\w+)/)?.[1] || 'unknown', triggers: (frontmatter.triggers || frontmatter['metadata']?.match(/triggers:\s*(.+)/)?.[1] || '').split(',').map(t => t.trim()).filter(Boolean), sourceFile: skillPath, parsedAt: new Date().toISOString(), }, // 6 维认知能力 mentalModels: extractMentalModels(sections), patternLibrary: extractPatternLibrary(sections), judgment: extractJudgment(sections, frontmatter), toolFluency: extractToolFluency(sections, skillDir), taste: extractTaste(sections), scarTissue: extractScarTissue(sections), }; } /** * 批量扫描 skills 目录 * @param {string} skillsDir - skills 目录路径 * @returns {object[]} 基因组数组 */ function scanSkillsDirectory(skillsDir) { if (!fs.existsSync(skillsDir)) return []; const entries = fs.readdirSync(skillsDir, { withFileTypes: true }); const genomes = []; for (const entry of entries) { if (!entry.isDirectory()) continue; const skillMd = path.join(skillsDir, entry.name, 'SKILL.md'); if (!fs.existsSync(skillMd)) continue; try { const genome = serializeSkillToGenome(skillMd); genomes.push(genome); } catch (err) { console.error(`Failed to parse ${skillMd}: ${err.message}`); } } return genomes; } module.exports = { serializeSkillToGenome, scanSkillsDirectory, // 暴露内部函数用于测试 parseSkillFrontmatter, parseMarkdownSections, extractMentalModels, extractPatternLibrary, extractJudgment, extractToolFluency, extractTaste, extractScarTissue, };