Skip to main content
Glama

generate_skill

Generate Claude skills by analyzing repeated feedback patterns. Clusters failures by tags and creates SKILL.md files with actionable DO/INSTEAD rules.

Instructions

Auto-generate Claude skills from repeated feedback patterns. Clusters failure patterns by tags and produces SKILL.md files with DO/INSTEAD rules.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
minOccurrencesNoMinimum pattern occurrences to trigger skill generation (default 3)
tagsNoFilter to specific tags

Implementation Reference

  • The handler for 'generate_skill' which calls the `generateSkills` function imported from `scripts/skill-generator.js`.
    case 'generate_skill':
      return toTextResult({
        skills: generateSkills({
          minClusterSize: Number(args.minOccurrences || 3),
        }).filter((entry) => {
          if (!Array.isArray(args.tags) || args.tags.length === 0) return true;
          return args.tags.some((tag) => entry.skillName.includes(String(tag)));
        }),
      });
  • The core implementation of the skill generation logic, which processes feedback, clusters it, and writes SKILL.md files.
    function generateSkills(options) {
      if (!options) options = {};
      const feedbackDir = options.feedbackDir || discoverFeedbackDir();
      const minClusterSize = options.minClusterSize || MIN_CLUSTER_SIZE;
      const minTagOverlap = options.minTagOverlap || MIN_TAG_OVERLAP;
      const dryRun = options.dryRun || false;
    
      const logPath = path.join(feedbackDir, 'feedback-log.jsonl');
      const outputDir = path.join(feedbackDir, 'generated-skills');
      const auditLogPath = path.join(feedbackDir, 'skill-generation-audit.jsonl');
    
      const entries = parseFeedbackFile(logPath);
      if (entries.length === 0) return [];
    
      // Separate positive and negative entries
      const posEntries = [];
      const negEntries = [];
      for (const entry of entries) {
        const cls = classifySignal(entry);
        if (cls === 'positive') posEntries.push(entry);
        else if (cls === 'negative') negEntries.push(entry);
      }
    
      if (negEntries.length === 0) return [];
    
      // Cluster negative feedback by tag overlap
      const clusters = clusterByTags(negEntries, minTagOverlap);
    
      const results = [];
    
      for (const [key, cluster] of clusters) {
        if (cluster.entries.length < minClusterSize) continue;
    
        // Compute domain-scoped approval rate
        const clusterTags = cluster.tags;
        let domainPos = 0;
        const domainNeg = cluster.entries.length;
        for (const pe of posEntries) {
          if (tagOverlap(extractTags(pe), clusterTags) >= 1) domainPos++;
        }
        const domainTotal = domainPos + domainNeg;
        const approvalRate = domainTotal > 0
          ? `${((domainPos / domainTotal) * 100).toFixed(1)}%`
          : '0.0%';
    
        const doRules = buildDoRules(posEntries, clusterTags);
        const insteadRules = buildInsteadRules(cluster.entries);
        const ruleCount = doRules.length + insteadRules.length;
    
        const skillContent = generateSkillFromCluster({
          tags: clusterTags,
          entries: cluster.entries,
          doRules,
          insteadRules,
          approvalRate,
        });
    
        const skillName = slugify(clusterTags.slice(0, 3).join('-')) || 'unnamed';
        const fileName = `${skillName}.SKILL.md`;
        const filePath = path.join(outputDir, fileName);
    
        if (!dryRun) {
          ensureDir(outputDir);
          fs.writeFileSync(filePath, skillContent, 'utf8');
    
          // Audit log
          appendJSONL(auditLogPath, {
            event: 'skill_generated',
            skillName,
            filePath,
            ruleCount,
            evidenceCount: cluster.entries.length,
            tags: clusterTags,
            approvalRate,
            timestamp: new Date().toISOString(),
          });
        }
    
        results.push({
          skillName,
          filePath,
          ruleCount,
          evidenceCount: cluster.entries.length,
        });
      }
    
      return results;
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/IgorGanapolsky/mcp-memory-gateway'

If you have feedback or need assistance with the MCP directory API, please join our Discord server