SDK Tutorial

Use @skillscraft/core and @skillscraft/spec programmatically in your own tools, CI pipelines, and editors.

1. Install the SDK

The framework ships as two npm packages. Install both for full type safety and runtime functionality.

# Runtime: parser, validator, linter, manifest loader
npm install @skillscraft/core

# Types, JSON schemas, and spec constants
npm install @skillscraft/spec

Both packages are pure ESM and support Node 18+. They have zero runtime dependencies.

TypeScript users@skillscraft/spec ships its own .d.ts files. No separate @types/ package is needed.

2. Parse a SKILL.md

parseSkill() reads a SKILL.md file and returns its YAML frontmatter as a typed object alongside the Markdown body.

import { parseSkill } from "@skillscraft/core";

// Parse from a file path
const parsed = await parseSkill("./my-skill/SKILL.md");

console.log(parsed.frontmatter.name);        // "my-skill"
console.log(parsed.frontmatter.description);  // "Review PRs for..."
console.log(parsed.body);                     // Markdown body string

The returned ParsedSkill object contains:

PropertyTypeDescription
frontmatterSkillFrontmatterParsed YAML fields: name, description, license, metadata, etc.
bodystringEverything after the closing --- delimiter.
rawstringThe original file content, unchanged.

3. Validate

validateSkill() checks a parsed skill against the Agent Skills specification. It returns a structured result with pass/fail status and any errors.

import { parseSkill, validateSkill } from "@skillscraft/core";

const parsed = await parseSkill("./my-skill/SKILL.md");
const result = validateSkill(parsed);

if (result.valid) {
  console.log("Skill passes all spec checks");
} else {
  result.errors.forEach(err => {
    console.error(`[${err.field}] ${err.message}`);
  });
}

Each error in the errors array has:

PropertyTypeExample
fieldstring"name", "description"
messagestring"name exceeds 64 characters"
severity"error"Always "error" for validation.

4. Lint

lintSkill() goes beyond spec compliance. It checks context budgets, description quality, generic instructions, progressive disclosure, and more.

import { parseSkill, lintSkill } from "@skillscraft/core";

const parsed = await parseSkill("./my-skill/SKILL.md");
const lint = lintSkill(parsed);

lint.diagnostics.forEach(d => {
  console.log(`[${d.severity}] ${d.rule}: ${d.message}`);
});

// Example output:
// [warn] context-budget: ~6200 tokens (max 5000). Move content to references/.
// [warn] description-quality: add "Use when..." trigger clause
// [info] gotchas-present: consider adding a "## Gotchas" section

Each diagnostic in the diagnostics array has:

PropertyTypeDescription
rulestringRule identifier, e.g. "context-budget"
severity"error" | "warn" | "info"Severity level
messagestringHuman-readable explanation
linenumber | undefinedLine number, when applicable

5. Load a Manifest

loadSkillManifest() reads an entire skill directory and returns the parsed skill plus a list of all bundled files (scripts, references, templates).

import { loadSkillManifest } from "@skillscraft/core";

const manifest = await loadSkillManifest("./my-skill");

console.log(manifest.skill.frontmatter.name);  // "my-skill"
console.log(manifest.files);
// [
//   "SKILL.md",
//   "scripts/setup.sh",
//   "references/api-patterns.md",
//   "templates/component.tsx"
// ]

The manifest respects .skillignore patterns automatically, excluding files like node_modules/ or .git/ from the file list.

6. Use .skillignore

The .skillignore file uses gitignore syntax to exclude files from the skill manifest. Two functions help you work with it programmatically.

import { loadSkillIgnore, filterFiles } from "@skillscraft/core";

// Load ignore patterns from a skill directory
const patterns = await loadSkillIgnore("./my-skill");
// ["node_modules/", ".git/", "*.log", "dist/"]

// Filter a file list against the patterns
const allFiles = [
  "SKILL.md",
  "scripts/setup.sh",
  "node_modules/foo/index.js",
  "debug.log"
];
const included = filterFiles(allFiles, patterns);
// ["SKILL.md", "scripts/setup.sh"]

If no .skillignore file exists, loadSkillIgnore() returns sensible defaults (node_modules/, .git/, .DS_Store).

7. Use Types

Import TypeScript interfaces from @skillscraft/spec for full type safety in your tooling.

import type {
  SkillFrontmatter,
  ParsedSkill,
  ValidationResult,
  LintResult,
  LintDiagnostic,
  SkillManifest
} from "@skillscraft/spec";

// SkillFrontmatter — the YAML header fields
const fm: SkillFrontmatter = {
  name: "code-review",
  description: "Review PRs. Use when the user submits a pull request.",
  license: "MIT",
  metadata: { author: "acme-corp", version: "1.0" }
};

// ParsedSkill — output of parseSkill()
const skill: ParsedSkill = {
  frontmatter: fm,
  body: "# Code Review\n\n## Instructions\n...",
  raw: "---\nname: code-review\n---\n# Code Review..."
};

// ValidationResult — output of validateSkill()
const vr: ValidationResult = {
  valid: true,
  errors: []
};

// LintResult — output of lintSkill()
const lr: LintResult = {
  diagnostics: []
};

8. Use Constants

Spec constants are exported so you can reuse them in your own validation logic without hardcoding values.

import { SKILL_NAME_REGEX, SPEC_LIMITS } from "@skillscraft/spec";

// SKILL_NAME_REGEX — validates the `name` field
// Lowercase alphanumeric + hyphens, 2-64 chars, no leading/trailing hyphens
console.log(SKILL_NAME_REGEX.test("code-review"));   // true
console.log(SKILL_NAME_REGEX.test("Code Review"));   // false
console.log(SKILL_NAME_REGEX.test("-bad-name-"));    // false

// SPEC_LIMITS — numeric limits from the spec
console.log(SPEC_LIMITS.nameMaxLength);          // 64
console.log(SPEC_LIMITS.descriptionMaxLength);   // 1024
console.log(SPEC_LIMITS.bodyMaxTokens);          // 5000
console.log(SPEC_LIMITS.bodyMaxLines);           // 500

9. Use JSON Schema

A JSON Schema for skill frontmatter is bundled in @skillscraft/spec. Use it with any JSON Schema validator (Ajv, Zod, etc.) for your own tooling.

import Ajv from "ajv";
import skillSchema from "@skillscraft/spec/schemas/skill.schema.json" with { type: "json" };

const ajv = new Ajv();
const validate = ajv.compile(skillSchema);

const frontmatter = {
  name: "code-review",
  description: "Review PRs. Use when the user submits a PR."
};

if (validate(frontmatter)) {
  console.log("Valid frontmatter");
} else {
  console.log(validate.errors);
}

The schema file lives at node_modules/@skillscraft/spec/schemas/skill.schema.json and can also be referenced by URL in editor configurations for YAML autocompletion.

10. Build a Custom Validator

Combine parse, validate, and lint into a single pipeline for CI or editor integrations.

import { parseSkill, validateSkill, lintSkill } from "@skillscraft/core";
import type { ValidationResult, LintResult } from "@skillscraft/spec";

interface PipelineResult {
  valid: boolean;
  validation: ValidationResult;
  lint: LintResult;
}

async function checkSkill(path: string): Promise<PipelineResult> {
  // 1. Parse
  const parsed = await parseSkill(path);

  // 2. Validate against spec
  const validation = validateSkill(parsed);

  // 3. Lint for best practices
  const lint = lintSkill(parsed);

  // Fail if any validation errors or lint errors (not warnings)
  const hasLintErrors = lint.diagnostics.some(d => d.severity === "error");

  return {
    valid: validation.valid && !hasLintErrors,
    validation,
    lint
  };
}

// Usage in CI
const result = await checkSkill("./my-skill/SKILL.md");

if (!result.valid) {
  console.error("Skill check failed:");
  result.validation.errors.forEach(e =>
    console.error(`  [spec] ${e.field}: ${e.message}`)
  );
  result.lint.diagnostics
    .filter(d => d.severity === "error")
    .forEach(d =>
      console.error(`  [lint] ${d.rule}: ${d.message}`)
    );
  process.exit(1);
}

console.log("All checks passed");
CI tip — run this in a GitHub Action on every PR that modifies a skill directory. The CLI also supports this via skill validate && skill lint --error-on warn.

11. CLI Usage Recap

The @skillscraft/cli package wraps the SDK into convenient terminal commands. Here are all seven commands.

skill init

Scaffold a new skill from a template.

# Basic skill
skill init my-skill

# With scripts directory
skill init my-skill --template with-scripts

# With references directory
skill init my-skill --template with-references

skill validate

Validate a skill against the Agent Skills specification.

# Validate a specific skill directory
skill validate ./my-skill

# Validate from within the skill directory
cd my-skill && skill validate

skill lint

Lint a skill for best practices.

# Lint with default rules
skill lint ./my-skill

# Auto-fix what can be fixed
skill lint ./my-skill --fix

# Fail on warnings (useful in CI)
skill lint ./my-skill --error-on warn

skill check

Run both validate and lint in one command.

# Full check: validate + lint
skill check ./my-skill

skill manifest

Print the skill manifest (parsed frontmatter + file list).

# JSON output
skill manifest ./my-skill

# Pretty-printed
skill manifest ./my-skill --pretty

skill pack

Bundle a skill directory into a distributable archive.

# Create a .tar.gz archive
skill pack ./my-skill

# Output to a specific path
skill pack ./my-skill -o ./dist/my-skill.tar.gz

skill info

Display summary information about a skill.

# Show skill info
skill info ./my-skill

# Example output:
#   name:        code-review
#   description: Review PRs for security and style issues.
#   license:     MIT
#   files:       4
#   body tokens: ~1,200