Tutorial: Build a Skill End-to-End

Build hello-skill from zero to installed — covering every spec feature and CLI command.

Step 1 — Install the CLI

# Install globally
npm install -g @skillscraft/cli

# Verify
skill --version
# 0.9.0

Or use npx: npx @skillscraft/cli validate ...

Step 2 — Scaffold with skill init

# Scaffold a new skill (basic template)
skill init hello-skill

# Or use a template with scripts
skill init hello-skill --template with-scripts

This creates:

hello-skill/
  SKILL.md          # Frontmatter + body template

With the --template with-scripts flag, it also creates a scripts/ directory with a starter script.

Step 3 — Write the SKILL.md (all frontmatter fields)

Replace the generated SKILL.md with a complete example using every frontmatter field:

# hello-skill/SKILL.md
---
name: hello-skill
description: >
  Generate friendly greeting messages in multiple languages.
  Use when the user asks for a hello message, welcome text,
  or localized greeting.
license: MIT
compatibility: Node.js 22+ or any JavaScript runtime
metadata:
  author: your-name
  version: "1.0"
  category: utilities
  tags: greeting localization i18n
allowed-tools: Bash Read
---

# Hello Skill

## When to use this skill

Activate when the user wants to:
- Generate a greeting message in a specific language
- Create welcome text for an application or document
- Translate "hello" into multiple languages

## Instructions

1. Ask the user which language they need (or default to English)
2. Run the greeting script:
   ```
   node scripts/greet.js --lang <language-code>
   ```
3. If the language is not supported, check `references/LANGUAGES.md`
   for the full list and suggest the closest match
4. For custom templates, use the assets in `assets/templates/`

## Output format

The script outputs JSON:

```json
{
  "language": "es",
  "greeting": "Hola, mundo!",
  "formal": "Buenos dias, estimado usuario."
}
```

## Gotchas

- Language codes follow ISO 639-1 (e.g., "en", "es", "ja", "de")
- The script defaults to English if an unknown code is passed
- Formal greetings are only available for: en, es, fr, de, ja
- Keep messages under 200 characters for chat compatibility
- See `references/LANGUAGES.md` for the complete supported list

Frontmatter field reference

FieldRequiredMax LengthUsed Above
nameYes64 charshello-skill
descriptionYes1024 charsMulti-line with > (folded scalar)
licenseNo-MIT
compatibilityNo500 charsNode.js 22+...
metadataNo-author, version, category, tags
allowed-toolsNo-Bash Read
Name rules: Lowercase letters, digits, and hyphens only. Must match ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$. The name must match the directory name.

Step 4 — Add scripts/

Scripts are executable files that agents can run. Create hello-skill/scripts/greet.js:

#!/usr/bin/env node
// hello-skill/scripts/greet.js

const { parseArgs } = require("node:util");

const GREETINGS = {
  en: { greeting: "Hello, world!",     formal: "Good day, dear user." },
  es: { greeting: "Hola, mundo!",      formal: "Buenos dias, estimado usuario." },
  fr: { greeting: "Bonjour, le monde!", formal: "Bonjour, cher utilisateur." },
  de: { greeting: "Hallo, Welt!",      formal: "Guten Tag, sehr geehrter Benutzer." },
  ja: { greeting: "こんにちは世界!",      formal: "こんにちは、ユーザー様。" },
};

const { values } = parseArgs({
  options: { lang: { type: "string", default: "en" } },
});

const lang = values.lang;
const data = GREETINGS[lang] || GREETINGS.en;

console.log(JSON.stringify({
  language: lang,
  greeting: data.greeting,
  formal: data.formal || null,
}, null, 2));

Test it directly:

node hello-skill/scripts/greet.js --lang es
# { "language": "es", "greeting": "Hola, mundo!", "formal": "Buenos dias..." }

Step 5 — Add references/

Reference docs give agents extra context. Create hello-skill/references/LANGUAGES.md:

# hello-skill/references/LANGUAGES.md

# Supported Languages

| Code | Language   | Formal Support |
|------|------------|----------------|
| en   | English    | Yes            |
| es   | Spanish    | Yes            |
| fr   | French     | Yes            |
| de   | German     | Yes            |
| ja   | Japanese   | Yes            |
| zh   | Chinese    | No             |
| ko   | Korean     | No             |
| pt   | Portuguese | No             |
| ar   | Arabic     | No             |
| hi   | Hindi      | No             |

## Adding a new language

1. Add the ISO 639-1 code and greeting to `scripts/greet.js`
2. Update this table
3. Run `skill validate` to verify

Step 6 — Add assets/

Assets hold static files agents can use. Create hello-skill/assets/templates/welcome.txt:

# hello-skill/assets/templates/welcome.txt

Welcome to {{APP_NAME}}!

We are glad to have you here. If you need help,
just ask and our assistant will guide you through.

Best regards,
The {{APP_NAME}} Team

Agents can read this template and substitute {{APP_NAME}} with the project name.

Step 7 — Add tests/

Test scenarios validate the skill works correctly. Create hello-skill/tests/scenarios.json:

// hello-skill/tests/scenarios.json
[
  {
    "name": "english-greeting",
    "input": "Say hello in English",
    "expectedOutput": "Hello, world!",
    "tags": ["smoke", "en"]
  },
  {
    "name": "spanish-greeting",
    "input": "Greet me in Spanish",
    "expectedOutput": "Hola, mundo!",
    "tags": ["smoke", "es"]
  },
  {
    "name": "unknown-language-fallback",
    "input": "Say hello in Klingon",
    "expectedOutput": "Hello, world!",
    "tags": ["edge-case"]
  },
  {
    "name": "formal-greeting",
    "input": "Give me a formal German greeting",
    "expectedOutput": "Guten Tag, sehr geehrter Benutzer.",
    "tags": ["formal", "de"]
  }
]

These follow the TestScenario interface from the extended spec.

Step 8 — Validate with skill validate

Check your skill against the spec:

# Validate the skill
skill validate hello-skill/SKILL.md

Expected output if everything is correct:

✓ Skill "hello-skill" is valid.

Checked:
  ✓ name is present and within 1-64 characters
  ✓ name matches regex ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$
  ✓ name matches directory name
  ✓ description is present and within 1-1024 characters
  ✓ compatibility is within 500 characters
  ✓ frontmatter contains no unknown fields
  ✓ YAML frontmatter is valid

If there are errors:

✗ Skill has validation errors:
  - [error] Name "Hello-Skill" does not match pattern ^[a-z0-9]...
  - [error] Description exceeds maximum length of 1024 characters

Fix all errors before proceeding. Use --strict to also fail on warnings.

Step 9 — Lint with skill lint

Check best practices beyond spec compliance:

# Lint for best practices
skill lint hello-skill/SKILL.md

# With fix suggestions
skill lint hello-skill/SKILL.md --fix

Expected output for our skill:

✓ Lint passed — 0 issues found.

Rules checked:
  ✓ description-quality     Description starts with action verb or "Use when"
  ✓ gotchas-section          Body contains a "Gotchas" section
  ✓ body-length              Body is under 500 lines
  ✓ frontmatter-completeness Has license, compatibility, and metadata
  ✓ allowed-tools-declared   allowed-tools field is present
  ✓ description-length       Description is concise (<200 chars recommended)
  ✓ name-matches-directory   name matches parent directory name

The linter has 7 rules covering description quality, body length, completeness, and naming.

Step 10 — Install with skill install

Install the skill for a specific agent:

# Install for Claude Code
skill install hello-skill --target claude
# Installed "hello-skill" for claude (project scope)
#   Source: /path/to/hello-skill
#   Target: .claude/skills/hello-skill

# Install for GitHub Copilot
skill install hello-skill --target copilot
#   Target: .github/skills/hello-skill

# Install for OpenAI Codex
skill install hello-skill --target codex
#   Target: .codex/skills/hello-skill

# Install generically (works with any agent)
skill install hello-skill --target generic
#   Target: .agents/skills/hello-skill

# Install to user scope (global)
skill install hello-skill --target claude --scope user
#   Target: ~/.claude/skills/hello-skill

# Force overwrite existing installation
skill install hello-skill --target claude --force

Target paths

TargetProject ScopeUser Scope
claude.claude/skills/<name>/~/.claude/skills/<name>/
copilot.github/skills/<name>/Not supported
codex.codex/skills/<name>/Not supported
generic.agents/skills/<name>/~/.agents/skills/<name>/

Step 11 — List installed skills

# List all installed skills
skill list

# Filter by target
skill list --target claude

# Filter by scope
skill list --target generic --scope user

Example output:

claude (project):
  Path: .claude/skills
  - hello-skill - Generate friendly greeting messages in multiple languages.

generic (project):
  Path: .agents/skills
  - hello-skill - Generate friendly greeting messages in multiple languages.

2 skill(s) found.

Step 12 — Publish with skill publish

# Preview what would be published
skill publish hello-skill --dry-run

# Output:
# Validating skill...
#   ✓ Validation passed
#   ✓ Lint clean
#
# Package contents (6 files, 3.2 KB):
#   SKILL.md (1.1 KB)
#   scripts/greet.js (0.8 KB)
#   references/LANGUAGES.md (0.5 KB)
#   assets/templates/welcome.txt (0.2 KB)
#   tests/scenarios.json (0.6 KB)
#
# [dry-run] Would publish skill: hello-skill

# Package for real
skill publish hello-skill --out-dir ./dist

# Then publish to npm:
#   cd dist/hello-skill && npm publish --access public

Step 13 — Use it programmatically

The framework is also a TypeScript SDK:

// Using @skillscraft/core in your own tooling
import { parseSkill, validateSkill, lintSkill } from "@skillscraft/core";

// Parse a SKILL.md file
const skill = await parseSkill("hello-skill/SKILL.md");
console.log(skill.frontmatter.name);        // "hello-skill"
console.log(skill.frontmatter.description);  // "Generate friendly..."
console.log(skill.frontmatter.license);      // "MIT"
console.log(skill.frontmatter.metadata);     // { author: "your-name", ... }

// Validate against the spec
const result = validateSkill(skill);
console.log(result.valid);    // true
console.log(result.errors);   // []

// Lint for best practices
const lint = lintSkill(skill);
console.log(lint.passed);       // true
console.log(lint.diagnostics);  // []
// Using @skillscraft/spec for types and constants
import type { SkillFrontmatter, ParsedSkill } from "@skillscraft/spec";
import { SKILL_NAME_REGEX, SPEC_LIMITS } from "@skillscraft/spec";

console.log(SPEC_LIMITS.NAME_MAX_LENGTH);        // 64
console.log(SPEC_LIMITS.DESCRIPTION_MAX_LENGTH);  // 1024
console.log(SKILL_NAME_REGEX.test("hello-skill")); // true
console.log(SKILL_NAME_REGEX.test("Hello-Skill")); // false

Final directory structure

hello-skill/
  SKILL.md                        # Required — frontmatter + instructions
  scripts/
    greet.js                      # Executable script (Node.js)
  references/
    LANGUAGES.md                  # Reference doc for agents
  assets/
    templates/
      welcome.txt                 # Static template file
  tests/
    scenarios.json                # Test scenarios (extended spec)

CLI commands recap

CommandPurpose
skill init <name>Scaffold a new skill
skill validate <path>Validate against the spec
skill lint <path>Check best practices
skill install <path>Install for an agent
skill listList installed skills
skill publish <path>Package for distribution
Next steps: Browse the Skill Gallery for more examples, or read the Specification Reference for complete field details and validation rules.