{"version":3,"file":"skill-evolve.mjs","names":[],"sources":["../../../src/tools/skill-evolve.ts"],"sourcesContent":["/**\n * Skill Evolution Tool — agent-initiated skill creation and improvement.\n *\n * Lets the agent create new skills from experience and patch existing ones.\n * Skills are stored in ~/.openclawnch/learned-skills/ (separate from the 27\n * static skills that ship with the plugin, so upgrades don't overwrite them).\n *\n * Actions:\n *   create  — Write a new SKILL.md from a complex workflow just completed\n *   patch   — Targeted find-and-replace within an existing learned skill\n *   list    — List all learned skills (name + description)\n *   view    — Read the full content of a learned skill\n *   delete  — Remove a learned skill\n *\n * All writes pass through the skill security scanner (skill-guard.ts).\n * Only available when evolution mode is \"evolving\".\n *\n * Inspired by Hermes Agent's skill_manager_tool.py.\n */\n\nimport { Type } from '@sinclair/typebox';\nimport { stringEnum, jsonResult, textResult, errorResult, readStringParam } from '../lib/tool-helpers.js';\nimport { scanSkillContent, validateSkillFrontmatter, formatScanReport } from '../lib/skill-guard.js';\nimport { getEvolutionMode } from '../services/evolution-mode.js';\nimport { getSkillRegistry } from '../services/skill-registry.js';\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, unlinkSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst ACTIONS = ['create', 'patch', 'list', 'view', 'delete'] as const;\n\nconst SkillEvolveSchema = Type.Object({\n  action: stringEnum(ACTIONS, { description: 'Operation to perform' }),\n  name: Type.Optional(Type.String({\n    description: 'Skill name in kebab-case (e.g., \"uniswap-v3-snipe\"). Required for create/patch/view/delete.',\n  })),\n  description: Type.Optional(Type.String({\n    description: 'Brief skill description (for create action).',\n  })),\n  content: Type.Optional(Type.String({\n    description: 'Full skill markdown content (for create action). Should include when-to-use, steps, and tips.',\n  })),\n  old_string: Type.Optional(Type.String({\n    description: 'Text to find in the skill (for patch action). Must be an exact substring match.',\n  })),\n  new_string: Type.Optional(Type.String({\n    description: 'Replacement text (for patch action).',\n  })),\n});\n\n// ─── Skill Directory ─────────────────────────────────────────────────────\n\nfunction getLearnedSkillsDir(): string {\n  return join(process.env.HOME ?? '/tmp', '.openclawnch', 'learned-skills');\n}\n\nfunction getSkillDir(name: string): string {\n  return join(getLearnedSkillsDir(), name);\n}\n\nfunction getSkillPath(name: string): string {\n  return join(getSkillDir(name), 'SKILL.md');\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────\n\nfunction buildSkillContent(name: string, description: string, body: string): string {\n  return `---\nname: ${name}\ndescription: ${description}\nversion: 1.0.0\nmetadata:\n  openclawnch:\n    source: agent-learned\n    createdAt: \"${new Date().toISOString()}\"\n---\n\n${body}\n`;\n}\n\nfunction parseFrontmatter(content: string): { frontmatter: Record<string, unknown>; body: string } | null {\n  const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/);\n  if (!match) return null;\n\n  try {\n    // Simple YAML parsing for the fields we care about\n    const yamlText = match[1] ?? '';\n    const body = match[2] ?? '';\n    const fm: Record<string, unknown> = {};\n\n    for (const line of yamlText.split('\\n')) {\n      const kvMatch = line.match(/^(\\w+):\\s*(.+)$/);\n      if (kvMatch) {\n        const key = kvMatch[1]!;\n        let value: string | unknown = (kvMatch[2] ?? '').trim();\n        // Strip quotes\n        if (typeof value === 'string' && value.startsWith('\"') && value.endsWith('\"')) {\n          value = value.slice(1, -1);\n        }\n        fm[key] = value;\n      }\n    }\n\n    return { frontmatter: fm, body };\n  } catch {\n    return null;\n  }\n}\n\nfunction listLearnedSkills(): Array<{ name: string; description: string; path: string }> {\n  const dir = getLearnedSkillsDir();\n  if (!existsSync(dir)) return [];\n\n  const results: Array<{ name: string; description: string; path: string }> = [];\n\n  try {\n    const entries = readdirSync(dir, { withFileTypes: true });\n    for (const entry of entries) {\n      if (!entry.isDirectory()) continue;\n      const skillPath = join(dir, entry.name, 'SKILL.md');\n      if (!existsSync(skillPath)) continue;\n\n      try {\n        const content = readFileSync(skillPath, 'utf8');\n        const parsed = parseFrontmatter(content);\n        results.push({\n          name: entry.name,\n          description: parsed?.frontmatter?.description as string ?? '(no description)',\n          path: skillPath,\n        });\n      } catch {\n        results.push({ name: entry.name, description: '(unreadable)', path: skillPath });\n      }\n    }\n  } catch {\n    // Directory read failed\n  }\n\n  return results;\n}\n\n// ─── Tool ────────────────────────────────────────────────────────────────\n\nexport function createSkillEvolveTool() {\n  return {\n    name: 'skill_evolve',\n    label: 'Skill Evolution',\n    ownerOnly: false,\n    description:\n      'Create and improve learned skills from experience. ' +\n      'After completing a complex task (5+ tool calls), fixing a tricky error, or discovering ' +\n      'a non-trivial DeFi workflow, save it as a skill so you can reuse it next time. ' +\n      'Actions: create (new skill), patch (update existing), list, view, delete. ' +\n      'Only available in evolving mode (/evolve).',\n    parameters: SkillEvolveSchema,\n    execute: async (_toolCallId: string, args: unknown) => {\n      const params = args as Record<string, unknown>;\n      const action = readStringParam(params, 'action', { required: true })!;\n\n      // Check evolution mode (extract userId from context if available)\n      // The tool itself doesn't get ctx directly, but the readonly gate pattern\n      // can be used. For now, we check via a default.\n      // Note: the actual gate is in index.ts via the registration wrapper.\n\n      switch (action) {\n        case 'list':\n          return handleList();\n        case 'view':\n          return handleView(params);\n        case 'create':\n          return handleCreate(params);\n        case 'patch':\n          return handlePatch(params);\n        case 'delete':\n          return handleDelete(params);\n        default:\n          return errorResult(`Unknown action: ${action}`);\n      }\n    },\n  };\n}\n\n// ─── Action Handlers ─────────────────────────────────────────────────────\n\nfunction handleList() {\n  const learned = listLearnedSkills();\n\n  // Also report static skill count from registry\n  let staticCount = 0;\n  try {\n    const registry = getSkillRegistry();\n    staticCount = registry.list({ source: 'static' }).length;\n  } catch { /* registry not available */ }\n\n  const lines: string[] = [];\n\n  if (learned.length > 0) {\n    lines.push(`**Learned Skills** (${learned.length}):`, '');\n    for (const s of learned) {\n      lines.push(`- **${s.name}**: ${s.description}`);\n    }\n  } else {\n    lines.push('No learned skills yet. Use action \"create\" after completing a complex workflow to save it as a reusable skill.');\n  }\n\n  if (staticCount > 0) {\n    lines.push('', `${staticCount} built-in skills also available. Use \\`/skills\\` to browse all skills.`);\n  }\n\n  return textResult(lines.join('\\n'));\n}\n\nfunction handleView(params: Record<string, unknown>) {\n  const name = readStringParam(params, 'name', { required: true });\n  if (!name) return errorResult('Skill name is required for view action.');\n\n  // Try learned skills first (original behavior)\n  const learnedPath = getSkillPath(name);\n  if (existsSync(learnedPath)) {\n    try {\n      const content = readFileSync(learnedPath, 'utf8');\n      return textResult(content);\n    } catch (err) {\n      return errorResult(`Failed to read skill: ${err instanceof Error ? err.message : String(err)}`);\n    }\n  }\n\n  // Fall through to the unified skill registry (includes static skills)\n  try {\n    const registry = getSkillRegistry();\n    const content = registry.readContent(name);\n    if (content) {\n      return textResult(content);\n    }\n  } catch {\n    // Registry not available — continue to error\n  }\n\n  return errorResult(`Skill \"${name}\" not found. Use action \"list\" or \\`/skills\\` to see available skills.`);\n}\n\nfunction handleCreate(params: Record<string, unknown>) {\n  const name = readStringParam(params, 'name', { required: true });\n  const description = readStringParam(params, 'description', { required: true });\n  const content = readStringParam(params, 'content', { required: true });\n\n  if (!name) return errorResult('Skill name is required (kebab-case, e.g., \"uniswap-v3-snipe\").');\n  if (!description) return errorResult('Skill description is required.');\n  if (!content) return errorResult('Skill content (markdown body) is required.');\n\n  // Validate name format\n  if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(name) || name.length > 60) {\n    return errorResult('Invalid skill name. Must be lowercase kebab-case, 2-60 chars (e.g., \"my-skill\").');\n  }\n\n  // Check if already exists\n  if (existsSync(getSkillPath(name))) {\n    return errorResult(`Skill \"${name}\" already exists. Use action \"patch\" to update it, or \"delete\" first.`);\n  }\n\n  // Build full skill content\n  const fullContent = buildSkillContent(name, description, content);\n\n  // Validate frontmatter\n  const parsed = parseFrontmatter(fullContent);\n  if (parsed) {\n    const fmErrors = validateSkillFrontmatter(parsed.frontmatter);\n    if (fmErrors.length > 0) {\n      return errorResult('Skill frontmatter validation failed:\\n' + fmErrors.map(e => `- ${e}`).join('\\n'));\n    }\n  }\n\n  // Security scan\n  const scanResult = scanSkillContent(fullContent, 'learned');\n  if (!scanResult.safe) {\n    return errorResult(\n      'Skill creation BLOCKED by security scanner:\\n\\n' +\n      formatScanReport(scanResult) +\n      '\\n\\nRemove the flagged content and try again.',\n    );\n  }\n\n  // Write to disk\n  try {\n    const dir = getSkillDir(name);\n    if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n    writeFileSync(getSkillPath(name), fullContent, 'utf8');\n  } catch (err) {\n    return errorResult(`Failed to write skill: ${err instanceof Error ? err.message : String(err)}`);\n  }\n\n  const infoNote = scanResult.findings.length > 0\n    ? `\\n\\nNote: ${scanResult.findings.length} informational finding(s) detected but did not block creation.`\n    : '';\n\n  return jsonResult({\n    status: 'created',\n    name,\n    description,\n    path: getSkillPath(name),\n    message: `Learned skill \"${name}\" created successfully. It will be available in your next session's skill index.${infoNote}`,\n  });\n}\n\nfunction handlePatch(params: Record<string, unknown>) {\n  const name = readStringParam(params, 'name', { required: true });\n  const oldString = readStringParam(params, 'old_string', { required: true });\n  const newString = readStringParam(params, 'new_string', { required: true });\n\n  if (!name) return errorResult('Skill name is required for patch.');\n  if (!oldString) return errorResult('old_string is required (the text to find).');\n  if (!newString) return errorResult('new_string is required (the replacement text).');\n\n  const path = getSkillPath(name);\n  if (!existsSync(path)) {\n    return errorResult(`Learned skill \"${name}\" not found.`);\n  }\n\n  let content: string;\n  try {\n    content = readFileSync(path, 'utf8');\n  } catch (err) {\n    return errorResult(`Failed to read skill: ${err instanceof Error ? err.message : String(err)}`);\n  }\n\n  // Find the old string\n  if (!content.includes(oldString)) {\n    return errorResult(\n      `old_string not found in skill \"${name}\". ` +\n      'Provide an exact substring match. Use action \"view\" to see the current content.',\n    );\n  }\n\n  // Apply the patch\n  const patched = content.replace(oldString, newString);\n\n  // Security scan the result\n  const scanResult = scanSkillContent(patched, 'learned');\n  if (!scanResult.safe) {\n    return errorResult(\n      'Skill patch BLOCKED by security scanner:\\n\\n' +\n      formatScanReport(scanResult) +\n      '\\n\\nThe original skill was not modified.',\n    );\n  }\n\n  // Write\n  try {\n    writeFileSync(path, patched, 'utf8');\n  } catch (err) {\n    return errorResult(`Failed to write patched skill: ${err instanceof Error ? err.message : String(err)}`);\n  }\n\n  return jsonResult({\n    status: 'patched',\n    name,\n    message: `Skill \"${name}\" patched successfully. Changes will be visible in the next session.`,\n  });\n}\n\nfunction handleDelete(params: Record<string, unknown>) {\n  const name = readStringParam(params, 'name', { required: true });\n  if (!name) return errorResult('Skill name is required for delete.');\n\n  const dir = getSkillDir(name);\n  if (!existsSync(dir)) {\n    return errorResult(`Learned skill \"${name}\" not found.`);\n  }\n\n  try {\n    rmSync(dir, { recursive: true });\n  } catch (err) {\n    return errorResult(`Failed to delete skill: ${err instanceof Error ? err.message : String(err)}`);\n  }\n\n  return jsonResult({\n    status: 'deleted',\n    name,\n    message: `Learned skill \"${name}\" deleted.`,\n  });\n}\n\n// ─── Utility: Build Learned Skills Index for System Prompt ───────────────\n\n/**\n * Build a compact index of all learned skills for injection into the\n * system prompt. Format: \"- name: description\" per skill.\n */\nexport function buildLearnedSkillsIndex(): string {\n  const skills = listLearnedSkills();\n  if (skills.length === 0) return '';\n\n  const lines = [\n    '## Learned Skills (agent-created)',\n    'These skills were created by you from past experience. ' +\n    'If one matches the current task, load it with skill_evolve(action: \"view\", name: \"...\") for detailed instructions.',\n    '',\n  ];\n\n  for (const s of skills) {\n    lines.push(`- **${s.name}**: ${s.description}`);\n  }\n\n  return lines.join('\\n');\n}\n"],"mappings":"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"}