Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ dist/
coverage.out
coverage.html

# IDE
# IDE / agent workspaces
.claude/
.idea/
.vscode/
*.swp
Expand Down
34 changes: 31 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ On subsequent encounters, the agent finds the existing skill via `skern skill se
---
name: skill-name
description: |
What this skill does and when to use it
Use when <triggering conditions for this skill>.
allowed-tools: []
metadata:
author:
Expand All @@ -184,13 +184,41 @@ metadata:
date: "2025-07-15T10:30:00Z"
---

## Instructions
## Overview

Step-by-step instructions for the agent.
1-2 sentence core principle of the skill.

## When to Use

- Triggering conditions and symptoms

## Core Pattern

The main technique or pattern (before/after for techniques).

## Quick Reference

- Scannable summary for fast lookup

## Common Mistakes

- Frequent errors and fixes
```

Required fields: `name`, `description`. Directory name must match the `name` field.

### Writing Skills Guidelines

When creating or editing skills (via `skern skill create` or manually), follow these guidelines adapted from [superpowers/writing-skills](https://github.com/obra/superpowers/tree/main/skills/writing-skills). For full details, see [docs/writing-skills.md](docs/writing-skills.md).

Key points:

- **Description**: Start with "Use when..." — describe triggering conditions, not a workflow summary
- **Naming**: `kebab-case`, verb-first active voice (`creating-skills` not `skill-creation`)
- **Body structure**: Overview → When to Use → Core Pattern → Quick Reference → Common Mistakes
- **Token budget**: Getting-started < 150 words; frequently-loaded < 200 words; others < 500 words
- **Examples**: One excellent example beats many mediocre ones

### Skill Name Validation

Names must match `^[a-z0-9]+(-[a-z0-9]+)*$` and be 1-64 characters.
Expand Down
152 changes: 152 additions & 0 deletions docs/writing-skills.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Writing Effective Skills

This guide covers how to write high-quality, discoverable, well-structured skills for skern. These guidelines are adapted from the [superpowers writing-skills](https://github.com/obra/superpowers/tree/main/skills/writing-skills) project and are built into skern's scaffolding and validation.

## Skill Types

| Type | Description | Example Name |
|------|-------------|--------------|
| **Technique** | Concrete method with actionable steps | `condition-based-waiting` |
| **Pattern** | Mental model for problem-solving | `flatten-with-flags` |
| **Reference** | API docs, syntax guides, lookup tables | `office-docs` |

## When to Create a Skill

Create a skill when:

- The technique was not intuitively obvious
- You would reference it across multiple projects
- The pattern applies broadly beyond one codebase
- Others would benefit from it

Do **not** create a skill for:

- One-time solutions or narratives about specific sessions
- Patterns already well-documented in official tooling

## Description: Claude Search Optimization (CSO)

The `description` field in your SKILL.md frontmatter is the most critical field for agent discoverability. Agents match skills by description, so how you write it directly affects whether your skill gets loaded.

**Start with "Use when..."** and describe the triggering conditions:

```yaml
# Good
description: |
Use when tests hang or flake due to timing-dependent assertions.
Symptoms: intermittent CI failures, setTimeout in test code.

# Bad — summarizes the workflow (agents may shortcut)
description: |
A skill that teaches agents how to write better async tests
using condition-based waiting patterns.
```

**Why this matters:** When a description summarizes the skill's workflow, an agent may follow the description as a shortcut instead of reading the full SKILL.md body.

**Keyword tips:**

- Include error messages agents might encounter
- Add symptom words: "flaky", "hanging", "race condition", "timeout"
- Use tool names and common synonyms

## Naming Conventions

- Use `kebab-case` with lowercase letters, numbers, and hyphens
- Prefer verb-first active voice: `creating-skills` not `skill-creation`
- Be specific: `condition-based-waiting` not `async-test-helpers`

Names must match `^[a-z0-9]+(-[a-z0-9]+)*$` and be 1-64 characters.

## Recommended Body Structure

When you run `skern skill create`, the scaffold includes these sections by default:

### Overview

1-2 sentences describing the core principle or technique. Keep it concise.

### When to Use

List triggering conditions as bullet points:

- Symptoms that signal this skill is needed
- Specific use cases
- When **not** to use this skill (counter-examples help agents avoid false matches)

### Core Pattern

The main technique or pattern. For techniques, use a before/after comparison:

```markdown
## Core Pattern

**Before (anti-pattern):**
`setTimeout(check, 1000)` — arbitrary delay, still flaky

**After (correct):**
`waitFor(() => expect(value).toBe(true))` — condition-based, deterministic
```

### Quick Reference

A scannable table or bullet list for fast lookup. Agents should be able to use this section as a cheat sheet without reading the full skill.

### Common Mistakes

Frequent errors and their fixes. Helps agents self-correct.

## Token Efficiency

Skills are loaded into agent context windows, so brevity matters:

| Skill Category | Target Word Count |
|----------------|-------------------|
| Getting-started workflows | < 150 words |
| Frequently-loaded skills | < 200 words |
| Other skills | < 500 words |

**Tips:**

- Use cross-references instead of repeating shared concepts
- Compress examples to the minimum that demonstrates the point
- Move heavy reference material (100+ lines) to supporting files

## Code Examples

- **One excellent example beats many mediocre ones**
- Use the most relevant language for the skill's domain
- Keep examples copy-pasteable and runnable

## File Organization

| Layout | When to Use |
|--------|------------|
| Single `SKILL.md` | All content fits in one file |
| `SKILL.md` + supporting files | Heavy reference (100+ lines) or reusable tool code |

Supporting files live in the same directory as `SKILL.md`. Reference them in the body.

## Anti-Patterns to Avoid

- **Narrative storytelling** — "In session 2025-10-03, I encountered..." Skills are reusable references, not journals.
- **Multi-language examples** — Pick one language. Multiple languages dilute quality and waste tokens.
- **Generic labels** — Avoid `helper1`, `step3`, `utils`. Use descriptive names.
- **Workflow summaries in description** — The description field is for triggering conditions only.

## Validation Hints

Skern's validator provides hints when your skill deviates from these guidelines:

- **Description missing trigger prefix** — Hints when the description doesn't start with "Use when", "Use for", "Use to", etc.
- **Missing recommended sections** — Hints when the body is missing "When to Use", "Core Pattern", "Quick Reference", or "Common Mistakes" sections.
- **Body too short** — Hints when the body has fewer than 20 words.
- **Description too vague** — Hints when the description has fewer than 3 words.

These are non-blocking hints (not errors), designed to guide you toward better skills.

## Next Steps

- [Quick Start](/guide/quick-start) — create and install your first skill
- [Skill Format](/concepts/skill-format) — full SKILL.md specification
- [Validation Reference](/reference/validation) — all validation rules
42 changes: 40 additions & 2 deletions internal/skill/scaffold.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,46 @@
package skill

// DefaultBody returns the default body content for a new skill.
// The template follows the writing-skills guidelines: Overview, When to Use,
// Core Pattern, Quick Reference, and Common Mistakes sections.
func DefaultBody() string {
return "## Instructions\n\nTODO: Add step-by-step instructions for the agent.\n"
return `## Overview

<!-- 1-2 sentences: the core principle or technique this skill provides. -->

TODO: Describe the core principle of this skill.

## When to Use

<!-- Triggering conditions — symptoms and use cases that signal this skill is needed. -->

- TODO: Add triggering conditions

## Core Pattern

<!-- The main technique or pattern. Use before/after examples for techniques. -->

TODO: Describe the core pattern or technique.

## Quick Reference

<!-- Scannable summary — a table or bullet list for fast lookup. -->

- TODO: Add quick reference items

## Common Mistakes

<!-- Frequent errors and their fixes. -->

- TODO: Add common mistakes and how to avoid them
`
}

// defaultDescription returns the default placeholder description for a new skill.
// Follows the writing-skills guideline: start with "Use when..." to describe
// triggering conditions, not a workflow summary.
func defaultDescription() string {
return "Use when TODO: describe the triggering conditions for this skill.\n"
}

// NewSkill creates a new Skill with sensible defaults.
Expand All @@ -13,7 +51,7 @@ func NewSkill(name, description, authorName, authorType, authorPlatform string)
// NewSkillWithBody creates a new Skill with a custom body. If body is empty, DefaultBody() is used.
func NewSkillWithBody(name, description, authorName, authorType, authorPlatform, body string) *Skill {
if description == "" {
description = "TODO: Describe what this skill does and when to use it.\n"
description = defaultDescription()
}

if body == "" {
Expand Down
20 changes: 18 additions & 2 deletions internal/skill/scaffold_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ func TestNewSkill_NameOnly(t *testing.T) {
s := NewSkill("my-skill", "", "", "", "")

assert.Equal(t, "my-skill", s.Name)
assert.Contains(t, s.Description, "Use when")
assert.Contains(t, s.Description, "TODO")
assert.Equal(t, "0.0.1", s.Metadata.Version)
assert.Contains(t, s.Body, "## Instructions")
assert.Contains(t, s.Body, "## Overview")
assert.Contains(t, s.Body, "## When to Use")
assert.Contains(t, s.Body, "## Core Pattern")
assert.Contains(t, s.Body, "## Quick Reference")
assert.Contains(t, s.Body, "## Common Mistakes")
}

func TestNewSkill_WithDescription(t *testing.T) {
Expand All @@ -39,6 +44,17 @@ func TestNewSkill_WithAgentAuthor(t *testing.T) {

func TestDefaultBody(t *testing.T) {
body := DefaultBody()
assert.Contains(t, body, "## Instructions")
assert.Contains(t, body, "## Overview")
assert.Contains(t, body, "## When to Use")
assert.Contains(t, body, "## Core Pattern")
assert.Contains(t, body, "## Quick Reference")
assert.Contains(t, body, "## Common Mistakes")
assert.Contains(t, body, "TODO")
}

func TestDefaultDescription(t *testing.T) {
desc := defaultDescription()
assert.True(t, len(desc) > 0)
assert.Contains(t, desc, "Use when")
assert.Contains(t, desc, "TODO")
}
Loading
Loading