npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vibe-agent-toolkit/runtime-claude-skills

v0.1.3

Published

Build and package VAT agents as Claude Skills

Readme

@vibe-agent-toolkit/runtime-claude-skills

Build, validate, and import Claude Skills for Claude Desktop, Claude Code, and VAT agents.

Overview

This package provides runtime support for Claude Skills including:

  • Validation - Audit Claude Skills for quality and compatibility
  • Import/Export - Convert between SKILL.md and agent.yaml formats
  • Building - Package VAT agents as Claude Skills
  • Frontmatter Parsing - Extract and validate Claude Skills frontmatter

Features

  • Skill Validation - Comprehensive validation against Agent Skills specification
  • Format Conversion - Import SKILL.md to agent.yaml (and vice versa)
  • Build Pipeline - Generate SKILL.md from agent manifests
  • Schema Validation - Zod-based frontmatter validation with TypeScript types
  • Link Integrity - Validate all markdown links and references
  • Console Compatibility - Detect tool usage incompatible with console mode

Installation

npm install @vibe-agent-toolkit/runtime-claude-skills

Quick Start

Validate a Claude Skill

import { validateSkill } from '@vibe-agent-toolkit/runtime-claude-skills';

const result = await validateSkill({
  skillPath: './my-skill/SKILL.md',
});

if (result.status === 'error') {
  console.error('Validation failed:');
  for (const issue of result.issues) {
    console.error(`  [${issue.code}] ${issue.message}`);
  }
}

Import SKILL.md to agent.yaml

import { importSkillToAgent } from '@vibe-agent-toolkit/runtime-claude-skills';

const result = await importSkillToAgent({
  skillPath: './my-skill/SKILL.md',
  outputPath: './my-agent/agent.yaml', // Optional
  force: false, // Optional
});

if (result.success) {
  console.log(`Imported to: ${result.agentPath}`);
} else {
  console.error(`Import failed: ${result.error}`);
}

Build Claude Skill from VAT Agent

import { buildClaudeSkill } from '@vibe-agent-toolkit/runtime-claude-skills';

await buildClaudeSkill({
  agentPath: './my-agent',
  outputPath: './dist/skills/my-agent',
});

API Reference

validateSkill(options): Promise

Validate a Claude Skill (SKILL.md) for quality and compatibility.

Options:

interface ValidateOptions {
  skillPath: string;         // Path to SKILL.md
  rootDir?: string;          // Root directory for resolving links
  isVATGenerated?: boolean;  // Treat as VAT-generated (stricter validation)
}

Returns:

interface ValidationResult {
  path: string;                       // Path to skill file
  type: 'claude-skill' | 'vat-agent'; // Detected type
  status: 'success' | 'warning' | 'error';
  summary: string;                    // Human-readable summary
  issues: ValidationIssue[];          // All validation issues
  metadata?: {                        // Extracted metadata
    name?: string;
    description?: string;
    lineCount?: number;
  };
}

interface ValidationIssue {
  severity: 'error' | 'warning' | 'info';
  code: IssueCode;              // Machine-readable code
  message: string;              // Human-readable message
  location?: string;            // File:line location
  fix?: string;                 // Suggested fix
}

Example:

const result = await validateSkill({
  skillPath: './my-skill/SKILL.md',
  rootDir: './my-skill',
});

console.log(`Status: ${result.status}`);
console.log(`Summary: ${result.summary}`);

// Check for specific error types
const nameErrors = result.issues.filter(
  i => i.code === 'SKILL_NAME_INVALID'
);

if (nameErrors.length > 0) {
  console.error('Name validation failed');
}

Validation Rules

The validator checks for:

Critical Errors (Blocking)

Frontmatter Errors:

  • SKILL_MISSING_FRONTMATTER - No YAML frontmatter found
  • SKILL_MISSING_NAME - Required "name" field missing
  • SKILL_MISSING_DESCRIPTION - Required "description" field missing
  • SKILL_NAME_INVALID - Name doesn't match pattern: ^[a-z0-9]+(-[a-z0-9]+)*$
  • SKILL_NAME_RESERVED_WORD - Name contains "claude" or "anthropic"
  • SKILL_NAME_XML_TAGS - Name contains < or > characters
  • SKILL_DESCRIPTION_TOO_LONG - Description exceeds 1024 characters
  • SKILL_DESCRIPTION_EMPTY - Description is empty or whitespace-only
  • SKILL_DESCRIPTION_XML_TAGS - Description contains < or > characters

Link Errors:

  • LINK_INTEGRITY_BROKEN - Link points to non-existent file
  • PATH_STYLE_WINDOWS - Link uses Windows backslashes ()

Warnings (Non-Blocking)

  • SKILL_TOO_LONG - Skill exceeds 5000 lines
  • SKILL_CONSOLE_INCOMPATIBLE - References Write, Edit, Bash, or NotebookEdit tools

See Best Practices Guide for detailed guidance.

importSkillToAgent(options): Promise

Convert a Claude Skill (SKILL.md) to VAT agent format (agent.yaml).

Options:

interface ImportOptions {
  skillPath: string;      // Path to SKILL.md
  outputPath?: string;    // Custom output path (default: same dir as SKILL.md)
  force?: boolean;        // Overwrite existing agent.yaml (default: false)
}

Returns:

type ImportResult =
  | { success: true; agentPath: string }
  | { success: false; error: string };

Example:

// Basic import
const result = await importSkillToAgent({
  skillPath: './my-skill/SKILL.md',
});

// Custom output path
const result = await importSkillToAgent({
  skillPath: './my-skill/SKILL.md',
  outputPath: './agents/my-agent/agent.yaml',
});

// Force overwrite
const result = await importSkillToAgent({
  skillPath: './my-skill/SKILL.md',
  force: true,
});

if (!result.success) {
  console.error(`Import failed: ${result.error}`);
  process.exit(1);
}

What Gets Converted:

| SKILL.md Field | agent.yaml Field | Notes | |----------------|------------------|-------| | name | metadata.name | Required | | description | metadata.description | Required | | metadata.version | metadata.version | Defaults to "0.1.0" | | metadata.tags | metadata.tags | Optional | | license | metadata.license | Optional | | compatibility | spec.compatibility | Optional |

The generated agent.yaml will have:

metadata:
  name: skill-name
  description: Skill description
  version: 1.0.0
spec:
  runtime: claude-skills

buildClaudeSkill(options): Promise

Build a Claude Skill from a VAT agent manifest.

Options:

interface BuildOptions {
  agentPath: string;    // Path to agent directory or manifest
  outputPath: string;   // Where to write skill bundle
}

Returns:

interface BuildResult {
  outputPath: string;   // Path to generated SKILL.md
  metadata: {
    name: string;
    description: string;
    version: string;
  };
}

Example:

const result = await buildClaudeSkill({
  agentPath: './my-agent',
  outputPath: './dist/skills/my-agent',
});

console.log(`Built skill: ${result.outputPath}`);
console.log(`Version: ${result.metadata.version}`);

Frontmatter Schemas

ClaudeSkillFrontmatterSchema

Strict schema for console-compatible Claude Skills:

import { ClaudeSkillFrontmatterSchema } from '@vibe-agent-toolkit/runtime-claude-skills';
import { z } from 'zod';

const frontmatter = {
  name: 'my-skill',
  description: 'Does something useful',
  compatibility: 'Requires Node.js 18+',
};

const result = ClaudeSkillFrontmatterSchema.safeParse(frontmatter);
if (result.success) {
  // TypeScript type: ClaudeSkillFrontmatter
  const validated = result.data;
}

Required Fields:

  • name - Skill identifier (lowercase, hyphens, max 64 chars)
  • description - What skill does (max 1024 chars)

Optional Fields:

  • license - License identifier
  • compatibility - Environment requirements (max 500 chars)
  • metadata - Additional properties (Record<string, string>)
  • allowed-tools - Pre-approved tools (experimental)

VATClaudeSkillFrontmatterSchema

Extended schema for VAT-generated skills:

import { VATClaudeSkillFrontmatterSchema } from '@vibe-agent-toolkit/runtime-claude-skills';

const frontmatter = {
  name: 'my-skill',
  description: 'Does something useful',
  metadata: {
    version: '1.0.0',   // Required for VAT skills
    tags: ['utility', 'data'],
  },
};

const result = VATClaudeSkillFrontmatterSchema.safeParse(frontmatter);

Additional Requirements:

  • metadata.version - Semantic version (required for VAT)

parseFrontmatter(content): ParseResult

Parse YAML frontmatter from SKILL.md content.

Example:

import { parseFrontmatter } from '@vibe-agent-toolkit/runtime-claude-skills';
import * as fs from 'node:fs';

const content = fs.readFileSync('./SKILL.md', 'utf-8');
const result = parseFrontmatter(content);

if (result.success) {
  console.log('Frontmatter:', result.frontmatter);
  console.log('Body:', result.body);
} else {
  console.error('Parse error:', result.error);
}

Usage Examples

Validate Before Import

import {
  validateSkill,
  importSkillToAgent
} from '@vibe-agent-toolkit/runtime-claude-skills';

// Validate first
const validation = await validateSkill({
  skillPath: './my-skill/SKILL.md',
});

if (validation.status === 'error') {
  console.error('Validation failed, cannot import');
  process.exit(1);
}

// Import if validation passes
const importResult = await importSkillToAgent({
  skillPath: './my-skill/SKILL.md',
});

if (importResult.success) {
  console.log(`Imported to: ${importResult.agentPath}`);
}

Batch Validation

import { validateSkill } from '@vibe-agent-toolkit/runtime-claude-skills';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';

async function validateAllSkills(dir: string) {
  const entries = await fs.readdir(dir, { withFileTypes: true });
  const results = [];

  for (const entry of entries) {
    if (entry.isDirectory()) {
      const skillPath = path.join(dir, entry.name, 'SKILL.md');
      try {
        const result = await validateSkill({ skillPath });
        results.push({ skill: entry.name, result });
      } catch (error) {
        console.error(`Error validating ${entry.name}:`, error);
      }
    }
  }

  return results;
}

const results = await validateAllSkills('./skills');
const failed = results.filter(r => r.result.status === 'error');

console.log(`Validated ${results.length} skills`);
console.log(`Failed: ${failed.length}`);

Custom Validation Logic

import { validateSkill } from '@vibe-agent-toolkit/runtime-claude-skills';

const result = await validateSkill({
  skillPath: './my-skill/SKILL.md',
});

// Filter by severity
const errors = result.issues.filter(i => i.severity === 'error');
const warnings = result.issues.filter(i => i.severity === 'warning');

// Group by error code
const byCode = result.issues.reduce((acc, issue) => {
  if (!acc[issue.code]) acc[issue.code] = [];
  acc[issue.code].push(issue);
  return acc;
}, {} as Record<string, typeof result.issues>);

// Check for specific issues
const hasLinkErrors = result.issues.some(
  i => i.code === 'LINK_INTEGRITY_BROKEN'
);

if (hasLinkErrors) {
  console.error('Fix broken links before proceeding');
}

Integration

CLI Integration

This package powers these CLI commands:

  • vat agent audit - Validates skills using validateSkill()
  • vat agent import - Imports skills using importSkillToAgent()

See CLI Documentation for command usage.

CI/CD Integration

// ci-validate.ts
import { validateSkill } from '@vibe-agent-toolkit/runtime-claude-skills';
import * as fs from 'node:fs/promises';

async function validateInCI() {
  const files = await fs.readdir('./skills', { recursive: true });
  const skills = files.filter(f => f.endsWith('SKILL.md'));

  let hasErrors = false;

  for (const skill of skills) {
    const result = await validateSkill({
      skillPath: `./skills/${skill}`,
    });

    if (result.status === 'error') {
      console.error(`❌ ${skill}:`);
      for (const issue of result.issues) {
        if (issue.severity === 'error') {
          console.error(`  ${issue.message}`);
        }
      }
      hasErrors = true;
    }
  }

  if (hasErrors) {
    process.exit(1);
  }

  console.log('✅ All skills validated successfully');
}

validateInCI();

TypeScript Types

The package exports all types for TypeScript users:

import type {
  ValidateOptions,
  ValidationResult,
  ValidationIssue,
  IssueCode,
  IssueSeverity,
  ClaudeSkillFrontmatter,
  VATClaudeSkillFrontmatter,
  ImportOptions,
  ImportResult,
  BuildOptions,
  BuildResult,
} from '@vibe-agent-toolkit/runtime-claude-skills';

Error Handling

All async functions use standard Promise rejection for errors:

try {
  const result = await validateSkill({
    skillPath: './invalid-path/SKILL.md',
  });
} catch (error) {
  if (error instanceof Error) {
    console.error(`Validation error: ${error.message}`);
  }
}

Validation errors are returned in the ValidationResult object, not thrown:

const result = await validateSkill({ skillPath: './skill/SKILL.md' });

// result.status will be 'error', not thrown
if (result.status === 'error') {
  // Handle validation failures
}

Performance

Validation Performance

Validation is optimized for:

  • Fast parsing - Efficient YAML frontmatter extraction
  • Cached git checks - Gitignore status cached per directory
  • Parallel validation - Validate multiple skills concurrently

Typical performance:

  • Single skill: ~10-50ms
  • 10 skills: ~100-200ms (parallel)
  • 100 skills: ~1-2s (parallel)

Memory Usage

Memory usage is proportional to skill size:

  • Small skill (<100KB): ~2MB
  • Medium skill (500KB): ~5MB
  • Large skill (2MB): ~10MB

For large-scale validation, validate in batches to control memory.

Cross-Platform Compatibility

The package is fully cross-platform:

  • Path handling - Uses node:path for Windows/Unix compatibility
  • Line endings - Handles CRLF and LF correctly
  • File system - Works with case-sensitive and case-insensitive file systems

Tested on:

  • Windows 10/11
  • macOS (Apple Silicon and Intel)
  • Linux (Ubuntu, Debian, Alpine)

Related Packages

Related Documentation

Testing

The package includes comprehensive tests:

# Unit tests
bun run test:unit

# Integration tests
bun run test:integration

# Watch mode
bun run test:watch

License

MIT