
Skill Builder
- 518 installs
- 133 repo stars
- Updated February 24, 2026
- jwynia/agent-skills
skill-builder is a meta-development skill that creates, packages, and iterates reusable agent skills shareable via Skillselion for developers who need structured, installable capability modules other agents can consume.
About
skill-builder is a jwynia/agent-skills package ranked #22 on skills.sh with 494 installs that guides developers through creating, packaging, and iterating reusable agent skills. The skill covers skill structure, metadata, triggers, and distribution so capabilities can be shared via Skillselion or installed by other agents. Developers reach for skill-builder when converting ad-hoc prompt patterns into maintainable, versioned skills with clear invocation boundaries. It fits platform engineers standardizing agent capability libraries across Claude Code, Cursor, and Codex environments. Rather than one-off instructions buried in project docs, skill-builder produces installable artifacts other team members and agents can discover and invoke consistently.
- Creates new agent skills following the official Skillselion SKILL.md template and taxonomy
- Generates complete JSON catalog metadata for journey hubs and faceted search
- Produces consistent editorial copy optimized for AEO and LLM consumption
- Enforces exact taxonomy compliance across 30+ structured fields
- Supports full journey-wide, multi-phase, and phase-specific skill definitions
Skill Builder by the numbers
- 518 all-time installs (skills.sh)
- +2 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #1,736 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/jwynia/agent-skills --skill skill-builderAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 518 |
|---|---|
| repo stars | ★ 133 |
| Last updated | February 24, 2026 |
| Repository | jwynia/agent-skills ↗ |
How do you create reusable agent skills for distribution?
Create, package, and iterate on reusable agent skills that can be shared via Skillselion or installed by other agents.
Who is it for?
Developers formalizing ad-hoc agent prompts into versioned, shareable skills for team or catalog distribution.
Skip if: One-off coding tasks with no intent to reuse the workflow as an installable agent capability.
When should I use this skill?
Developer wants to create, refactor, package, or publish a new agent skill for Skillselion or team sharing.
What you get
Packaged SKILL.md files, skill metadata manifests, and publishable agent capability modules.
- SKILL.md skill files
- Skill metadata manifests
By the numbers
- 494 installs documented on skills.sh
- Ranked #22 in the jwynia/agent-skills catalog
Files
Skill-Builder: Meta-Skill for Creating Skills
You help create new agent skills that follow established patterns. Your role is to guide skill design, generate scaffolding, and validate completeness.
Core Principle
Skills are diagnostic frameworks with tools, not feature checklists.
A skill diagnoses a problem space, identifies states, and provides interventions. Scripts provide randomization and structure; the LLM provides judgment. Each does what it's best at.
Skill Anatomy
Every skill has these components:
skill-name/
├── SKILL.md # Diagnostic framework + documentation
├── scripts/ # Deno TypeScript tools
│ └── *.ts
├── data/ # JSON datasets (if needed)
│ └── *.json
└── references/ # Supporting documentation (optional)
└── *.mdSKILL.md Structure
---
name: skill-name
description: One sentence starting with action verb
license: MIT
metadata:
author: your-name
version: "1.0"
maturity_score: [0-20] # Optional
---
# Skill Name: Subtitle
You [role description]. Your role is to [specific function].
## Core Principle
**Bold statement capturing diagnostic essence.**
## The States
### State X1: Name
**Symptoms:** What the user notices
**Key Questions:** What to ask
**Interventions:** What framework/tool to apply
[Repeat for each state]
## Diagnostic Process
1. Step one
2. Step two
...
## Key Questions
### For Category A
- Question?
- Question?
## Anti-Patterns
### The [Problem Name]
**Problem:** Description
**Fix:** Solution
## Available Tools
### script.ts
Description of what it does.
\`\`\`bash
deno run --allow-read scripts/script.ts [args]
\`\`\`
## Example Interaction
**User:** "Problem description"
**Your approach:**
1. Action
2. Action
## What You Do NOT Do
- List of boundaries
- Things the skill never does
## Integration Graph
### Inbound (From Other Skills)
| Source Skill | Source State | Leads to State |
|--------------|--------------|----------------|
| [skill] | [state] | [state] |
### Outbound (To Other Skills)
| This State | Leads to Skill | Target State |
|------------|----------------|--------------|
| [state] | [skill] | [state] |
### Complementary Skills
| Skill | Relationship |
|-------|--------------|
| [skill] | [how they relate] |Skill Types
Type D: Diagnostic Skills
Purpose: Identify problems, recommend interventions Pattern: States → Questions → Interventions Examples: story-sense, worldbuilding, conlang
Key characteristics:
- Problem states with symptoms/questions
- Cross-references to intervention tools
- "What you do NOT do" section enforces boundaries
- Integration tables mapping to other skills
Type G: Generator Skills
Purpose: Produce structured output from parameters Pattern: Parameters → Generation → Output Examples: Functions in story-sense, phonology in conlang
Key characteristics:
- Input parameters with defaults
- Randomization with optional seeding
- Multiple output formats (human, JSON, brief)
- Quality levels (starter → comprehensive)
Type U: Utility Skills
Purpose: Support other skills, build infrastructure Pattern: Input → Analysis/Transformation → Report Examples: list-builder, skill-builder
Key characteristics:
- Meta-level operation
- Quality metrics and validation
- Templates and scaffolding
- Cross-skill applicability
Type O: Orchestrator Skills
Purpose: Coordinate multiple skills into autonomous workflows Pattern: Input → Multi-Pass Evaluation Loop → Polished Output Examples: chapter-drafter
Key characteristics:
- Invokes multiple sub-skills sequentially
- Iterates until quality thresholds met
- Accumulates context across work units
- Operates autonomously without human checkpoints
Required frontmatter:
metadata:
orchestrates: # Sub-skills to coordinate
- skill-one
- skill-two
pass_order: # Evaluation sequence
- skill-one
- skill-two
pass_weights: # Weight per skill (sum to 100)
skill-one: 50
skill-two: 50
max_iterations: 3 # Per-pass iteration limit
global_max_iterations: 50 # Total capSee skills/fiction/orchestrators/README.md for architectural details.
Skill Maturity Scoring (24 points)
Skills are evaluated on a 24-point scale parallel to the framework 24-point system.
Completeness (11 points)
| Check | Points | Criteria |
|---|---|---|
| Core Principle | 1 | Bold statement capturing diagnostic essence |
| States | 2 | 3-7 states for diagnostic skills (N/A for generator/utility) |
| State Components | 2 | Symptoms, Key Questions, Interventions for each state |
| Diagnostic Process | 1 | Step-by-step process documented |
| Anti-Patterns | 2 | 3+ anti-patterns with Problem/Fix structure |
| Examples | 2 | 2+ worked examples showing skill application |
| Boundaries | 1 | "What You Do NOT Do" section |
Quality (5 points)
| Check | Points | Criteria |
|---|---|---|
| Self-Contained | 1 | Can be used without reading other skills |
| Type+Mode Declared | 1 | Required frontmatter fields present |
| State Naming | 1 | Consistent state prefix matching skill abbreviation |
| Integration Map | 1 | Documents connections to other skills |
| Tools Documented | 1 | All scripts have usage documentation |
Usability (4 points)
| Check | Points | Criteria |
|---|---|---|
| Output Persistence | 1 | Customized (not boilerplate) persistence section |
| Progressive Disclosure | 1 | Quick reference section for at-a-glance use |
| Decision Tree | 1 | Routing logic for common scenarios |
| Actionability | 1 | Clear next steps for each diagnosis |
Execution Intelligence (4 points) — NEW
| Check | Points | Criteria |
|---|---|---|
| Reasoning Requirements | 1 | Specifies when extended thinking benefits the task |
| Execution Strategy | 1 | Documents sequential vs. parallelizable work |
| Subagent Guidance | 1 | Identifies when to spawn specialized subagents |
| Context Management | 1 | Documents token footprint and optimization strategies |
Maturity Levels
| Level | Score | Description |
|---|---|---|
| Draft | 0-8 | Missing core elements |
| Developing | 9-14 | Functional but incomplete |
| Stable | 15-20 | Production-ready |
| Battle-Tested | 21-24 | Has case studies + full execution intelligence |
Required Metadata
Type (Required)
Every skill must declare its type in frontmatter:
metadata:| Type | Definition | Required Sections |
|---|---|---|
| diagnostic | Identifies problems, recommends interventions | States, Diagnostic Process, Anti-Patterns |
| generator | Produces structured output from parameters | Parameters, Generation Logic, Output Formats |
| utility | Supports other skills, builds infrastructure | Process, Templates, Validation |
| orchestrator | Coordinates multiple skills into autonomous workflows | Orchestration Loop, Pass Criteria, Iteration Limits |
Mode (Required)
Every skill must declare its mode in frontmatter:
metadata:| Mode | Definition | User Relationship |
|---|---|---|
| diagnostic | Identifies problem states and recommends | Agent diagnoses, user decides |
| assistive | Guides without producing content | Agent asks questions, user creates |
| collaborative | Works alongside user | Agent produces, user guides |
| evaluative | Assesses existing work | Agent reviews, user responds |
| application | Operates in real-time context | Agent runs, user participates |
| generative | Creates output from parameters | Agent produces, user selects |
Compound modes (e.g., diagnostic+generative) are allowed when skills perform multiple functions.
Optional Metadata
metadata:
maturity_score: 15State Naming Convention
States must follow a consistent naming pattern:
Convention: {ABBREV}{NUMBER}: {State Name}
Rules: 1. Abbreviation is 1-3 uppercase letters derived from skill name 2. Numbers start at 0 (for "no X exists" states) or 1 3. State names are descriptive, not just numbers 4. Sub-states use decimal notation (4.5, 5.75) when inserting between existing states
Standard Abbreviations:
| Skill | Abbreviation | Example |
|---|---|---|
| story-sense | SS | State SS1: Concept Without Foundation |
| dialogue | D | State D1: Identical Voices |
| conlang | L | State L1: No Language |
| worldbuilding | W | State W1: Backdrop World |
| revision | R | State R1: Overwhelmed |
| endings | E | State E1: Arbitrary Ending |
| character-arc | CA | State CA1: Static Character |
| scene-sequencing | SQ | State SQ1: Scene-Only Pacing |
| brainstorming | B | State B1: Convergent Ideas |
| research | RS | State RS1: No Research |
| requirements-analysis | RA | State RA1: Vague Requirements |
| system-design | SD | State SD1: No Architecture |
| chapter-drafter | CD | (Orchestrator - uses pass scores, not states) |
New skills should claim an unused abbreviation and document it here.
Integration Graph Requirements
Every skill must document its connections to other skills.
Required Format:
## Integration Graph
### Inbound (From Other Skills)
| Source Skill | Source State | Leads to State |
|--------------|--------------|----------------|
| story-sense | SS5: Plot Without Purpose | D4: No Subtext |
### Outbound (To Other Skills)
| This State | Leads to Skill | Target State |
|------------|----------------|--------------|
| D6: Pacing Mismatch | scene-sequencing | SQ2: Sequel Missing |
### Complementary Skills
| Skill | Relationship |
|-------|--------------|
| character-arc | Voice reflects transformation |
| worldbuilding | Speech reflects culture |Requirements:
- Minimum 1 inbound OR 1 outbound connection
- Complementary skills list for context
- State-level specificity (not just skill-to-skill)
- Bidirectional documentation (if A references B, B should reference A)
Execution Intelligence Requirements
Skills should document how they're best executed by Claude Code.
Reasoning Requirements Section
Document when extended thinking (ultrathink) benefits the skill:
## Reasoning Requirements
### Standard Reasoning
- Initial diagnosis and symptom matching
- Simple state identification
- Script execution and output interpretation
### Extended Reasoning (ultrathink)
Use extended thinking for:
- Multi-framework synthesis - [Why: requires holding multiple models simultaneously]
- Complex worldbuilding systems - [Why: many interdependent variables]
- Cascade analysis across states - [Why: second-order effects compound]
**Trigger phrases:** "deep analysis", "comprehensive review", "multi-framework synthesis"Why this matters: LLMs have a completion reward bias—they rush toward visible goals. Extended thinking allocates reasoning time before output, improving quality on complex tasks. This aligns with the LLM Process Design Framework principle.
Execution Strategy Section
Document sequential vs. parallel work:
## Execution Strategy
### Sequential (Default)
- Diagnosis must complete before intervention
- State identification before framework selection
### Parallelizable
- Multiple script runs (entropy + functions) can run concurrently
- Research across multiple frameworks can parallelize
- Use when: Tasks are independent and can merge results
### Subagent Candidates
| Task | Agent Type | When to Spawn |
|------|------------|---------------|
| Codebase exploration | Explore | When skill needs project context |
| Framework research | general-purpose | When synthesizing across 3+ frameworks |Context Management Section
Document token usage and optimization:
## Context Management
### Approximate Token Footprint
- **Skill base:** ~2k tokens
- **With full state definitions:** ~4k tokens
- **With scripts inline:** ~8k tokens (avoid unless debugging)
### Context Optimization
- Load scripts on-demand rather than including inline
- Reference framework documentation by name rather than embedding
- Use Quick Reference section for common cases
### When Context Gets Tight
- Prioritize: Current state diagnosis and immediate intervention
- Defer: Integration graph, full anti-patterns list
- Drop: Script source code, historical examplesAnti-Pattern Requirements
Every skill must document common mistakes.
Minimum Requirements:
- 3 anti-patterns for diagnostic skills
- 2 anti-patterns for generator/utility skills
Required Structure:
### The {Anti-Pattern Name}
**Pattern:** What the problematic behavior looks like
**Problem:** Why this causes harm
**Fix:** How to resolve it
**Detection:** [Optional] How to recognize this happeningCommon Anti-Pattern Categories:
| Category | Example Names |
|---|---|
| Scope Creep | The Kitchen Sink, The Mission Creep |
| Missing Depth | The Surface Treatment, The Checklist |
| Wrong Level | The Bottom-Up Edit, The Premature Optimization |
| User Relationship | The Puppet Master, The Passive Recipient |
| Integration | The Orphan Skill, The Boundary Ignorer |
Script Patterns
Standard Script Template
#!/usr/bin/env -S deno run --allow-read
/**
* Script Name
*
* Description of what it does.
*
* Usage:
* deno run --allow-read script.ts [args]
*/
// === INTERFACES ===
interface ResultType {
field: string;
// ...
}
// === DATA ===
const DATA: Record<string, string[]> = {
category: ["item1", "item2"],
};
// === UTILITIES ===
function randomFrom<T>(arr: T[], count: number = 1): T[] {
const shuffled = [...arr].sort(() => Math.random() - 0.5);
return shuffled.slice(0, Math.min(count, arr.length));
}
// === CORE LOGIC ===
function generate(/* params */): ResultType {
// Generation logic
}
// === FORMATTING ===
function formatResult(result: ResultType): string {
const lines: string[] = [];
// Format output
return lines.join("\n");
}
// === MAIN ===
function main(): void {
const args = Deno.args;
// Help
if (args.includes("--help") || args.includes("-h")) {
console.log(`Script Name
Usage:
deno run --allow-read script.ts [options]
Options:
--flag Description
--json Output as JSON
`);
Deno.exit(0);
}
// Parse arguments
const flagIndex = args.indexOf("--flag");
const flagValue = flagIndex !== -1 ? args[flagIndex + 1] : null;
const jsonOutput = args.includes("--json");
// Skip indices for positional arg detection
const skipIndices = new Set<number>();
if (flagIndex !== -1) {
skipIndices.add(flagIndex);
skipIndices.add(flagIndex + 1);
}
// Find positional argument
let positionalArg: string | null = null;
for (let i = 0; i < args.length; i++) {
if (!args[i].startsWith("--") && !skipIndices.has(i)) {
positionalArg = args[i];
break;
}
}
// Generate
const result = generate(/* params */);
// Output
if (jsonOutput) {
console.log(JSON.stringify(result, null, 2));
} else {
console.log(formatResult(result));
}
}
main();Argument Parsing Pattern
// 1. Help check first
if (args.includes("--help") || args.includes("-h")) { ... }
// 2. Parse --flag value pairs
const flagIndex = args.indexOf("--flag");
const flagValue = flagIndex !== -1 ? args[flagIndex + 1] : defaultValue;
// 3. Boolean flags
const boolFlag = args.includes("--bool");
// 4. Track consumed indices
const skipIndices = new Set<number>();
if (flagIndex !== -1) {
skipIndices.add(flagIndex);
skipIndices.add(flagIndex + 1);
}
// 5. Find positional args
for (let i = 0; i < args.length; i++) {
if (!args[i].startsWith("--") && !skipIndices.has(i)) {
positionalArg = args[i];
break;
}
}Data Loading Pattern
// For external JSON files
async function loadData<T>(path: string): Promise<T> {
try {
const text = await Deno.readTextFile(path);
return JSON.parse(text);
} catch (e) {
console.error(`Error loading ${path}: ${e}`);
Deno.exit(1);
}
}
// Relative path from script
const scriptDir = new URL(".", import.meta.url).pathname;
const dataPath = `${scriptDir}../data/file.json`;Output Pattern
// Always support multiple formats
if (jsonOutput) {
console.log(JSON.stringify(result, null, 2));
} else if (briefOutput) {
console.log(formatBrief(result));
} else {
console.log(formatFull(result));
}Data File Patterns
Simple List (for entropy/randomization)
{
"list_name": [
"Specific item with enough detail to spark ideas",
"Another item that's 20-60 characters ideally",
"Items should be concrete not vague"
]
}Quality thresholds:
- Starter: 10-30 items (demo only)
- Functional: 30-75 items (usable)
- Production: 75-150 items (ready)
- Comprehensive: 150+ items (reference quality)
Structured Data (for complex generation)
{
"_meta": {
"description": "What this data is for",
"usage": "How to use it",
"source": "Where it came from (optional)"
},
"category": {
"item_name": {
"property": "value",
"frequency": 0.85,
"tags": ["tag1", "tag2"]
}
}
}Frequency-Weighted Data
{
"tier_universal": {
"description": "Found in nearly all cases",
"items": { "a": { "frequency": 0.95 }, "b": { "frequency": 0.90 } }
},
"tier_common": {
"description": "Found in most cases",
"items": { "c": { "frequency": 0.70 } }
},
"tier_rare": {
"description": "Unusual but attested",
"items": { "d": { "frequency": 0.15 } }
}
}Diagnostic Process for Building Skills
When creating a new skill:
1. Identify the Problem Space
- What problems does this skill diagnose?
- What are the symptoms a user would notice?
- How does this connect to existing skills?
2. Define States
- Create 3-7 distinct states
- Each state needs: symptoms, key questions, interventions
- States should be mutually exclusive but cover the space
3. Design Tools
- What can scripts do better than LLM judgment?
- Randomization? Structure generation? Validation?
- What data does the script need?
4. Map Integrations
- Which other skills does this connect to?
- What states in other skills lead here?
- What states here lead elsewhere?
5. Validate Completeness
Run validate-skill.ts to check:
- Required frontmatter fields
- State definitions with all components
- Script documentation
- Integration references
Available Tools
scaffold.ts
Generates skill directory structure and template files.
# Create new skill scaffolding
deno run --allow-read --allow-write scripts/scaffold.ts skill-name
# With type specification
deno run --allow-read --allow-write scripts/scaffold.ts skill-name --type diagnostic
# Preview without writing
deno run --allow-read scripts/scaffold.ts skill-name --dry-runvalidate-skill.ts
Checks skill completeness and pattern conformance.
# Validate a skill
deno run --allow-read scripts/validate-skill.ts ../worldbuilding
# Validate all skills in fiction cluster
deno run --allow-read scripts/validate-skill.ts --all
# JSON output for CI
deno run --allow-read scripts/validate-skill.ts ../conlang --jsonAnti-Patterns
The Feature List Skill
Problem: Skill is a list of things it can do, not a diagnostic framework. Fix: Restructure around problem states. What are users stuck on?
The Kitchen Sink
Problem: Skill tries to do too much, covers multiple problem domains. Fix: Split into focused skills. One skill = one diagnostic space.
The Script Without Skill
Problem: Script exists but no SKILL.md explains when to use it. Fix: Every script belongs to a skill with documented purpose.
The Orphan Skill
Problem: Skill doesn't reference or get referenced by other skills. Fix: Add integration section. Map state transitions to/from other skills.
The Clone Skill
Problem: Skill duplicates another skill's states with different names. Fix: Merge or clearly differentiate the problem spaces.
Verification (Oracle)
This section documents what this skill can reliably verify vs. what requires human judgment. See organization/architecture/context-packet-architecture.md for background on oracles.
What This Skill Can Verify
- Structural completeness - validate-skill.ts checks required sections exist (High confidence)
- State naming conventions - Validates
{ABBREV}{N}: Namepattern (High confidence) - Frontmatter presence - Required type/mode fields present (High confidence)
- Section presence - Anti-patterns, Integration Graph, Output Persistence exist (High confidence)
- State component structure - Symptoms/Questions/Interventions present per state (Medium confidence)
What Requires Human Judgment
- State quality - Are states mutually exclusive and comprehensive? (Semantic)
- Integration accuracy - Do state transitions make sense across skills? (Contextual)
- Anti-pattern usefulness - Do they capture real failure modes? (Experiential)
- Example relevance - Do examples match real use cases? (Domain knowledge)
- Boundary appropriateness - Is the scope correct for one skill? (Design judgment)
Available Validation Scripts
| Script | Verifies | Confidence |
|---|---|---|
| validate-skill.ts | 20-point maturity scoring across Completeness/Quality/Usability | High for structure, Low for semantics |
| scaffold.ts | Generated files match expected structure | High |
Oracle Limitations
The validate-skill.ts script:
- Cannot detect duplicated skill coverage (The Clone Skill anti-pattern)
- Cannot detect scope creep (The Kitchen Sink anti-pattern)
- Cannot verify integration bidirectionality (must check both skills manually)
- Reports presence, not quality - a section existing doesn't mean it's good
Feedback Loop
This section documents how outputs persist and inform future sessions. See organization/architecture/context-packet-architecture.md for background on feedback loops.
Session Persistence
- Output location:
skills/{cluster}/{skill-name}/(directory structure) - What to save: SKILL.md, scripts/, data/, templates/
- Naming pattern: Skill name becomes directory name
Cross-Session Learning
- Before starting: Check if a skill for this problem space already exists
- If prior skill exists: Extend rather than duplicate; check integration graph
- What feedback improves this skill:
- New anti-patterns discovered during skill creation
- State naming collisions found
- Integration patterns that work well
Improvement Triggers
- When validate-skill.ts reveals common failures → Update maturity criteria
- When skill creation struggles → Add to anti-patterns
- When integration mapping is unclear → Improve Integration Graph section
Design Constraints
This section documents preconditions and boundaries. See organization/architecture/context-packet-architecture.md for background on constraints.
This Skill Assumes
- User has a clear problem domain to address (not vague "make a skill")
- Problem domain has identifiable states (symptoms a user would notice)
- Some automation is possible (script + LLM split makes sense)
This Skill Does Not Handle
- Framework development (higher abstraction) - Route to: framework-development methodology
- Single-use scripts (no diagnostic model) - Route to: simple script writing
- Skills without states (pure generators) - Route to: generator template (simpler structure)
Degradation Signals
Signs this skill is being misapplied:
- Cannot identify 3+ distinct states for the problem space
- All "states" are really parameters to a single generator
- No connection to existing skills makes sense (orphan problem space)
- Problem space overlaps significantly with existing skill
Example: Building a New Skill
Request: "Create a skill for diagnosing dialogue problems"
Step 1: Identify Problem Space
Dialogue problems are distinct from scene-sequencing (structure) and character-arc (transformation). This is about how characters speak.
Step 2: Define States
- D1: No Dialogue (narrative summary only)
- D2: Same-Voice Characters (everyone sounds identical)
- D3: On-the-Nose Dialogue (no subtext)
- D4: Talking Heads (dialogue without context)
- D5: Functional-Only Dialogue (moves plot, reveals nothing)
Step 3: Design Tools
Script: voice-check.ts - generates voice differentiation questionnaire Data: speech-patterns.json - regional, class, personality markers
Step 4: Map Integrations
- From story-sense State 5.5 (dialogue-specific issues)
- To character-arc (voice reflects character growth)
- To worldbuilding (speech reflects culture)
Step 5: Generate Scaffolding
deno run --allow-read --allow-write scripts/scaffold.ts dialogue --type diagnosticOutput Persistence
This skill writes primary output to files so work persists across sessions.
Output Discovery
Before doing any other work:
1. Check for context/output-config.md in the project 2. If found, look for this skill's entry 3. If not found or no entry for this skill, ask the user first:
- "Where should I save output from this skill-builder session?"
- Suggest:
skills/{cluster}/{skill-name}/as the standard skill location
4. Store the user's preference:
- In
context/output-config.mdif context network exists - In
.skill-builder-output.mdat project root otherwise
Primary Output
For this skill, persist:
- Skill scaffolding - SKILL.md, scripts/, templates/, data/
- State definitions - the diagnostic model
- Script templates - generated utility scripts
- Integration map - connections to other skills
Conversation vs. File
| Goes to File | Stays in Conversation |
|---|---|
| Generated SKILL.md | Discussion of problem space |
| Script templates | State definition iteration |
| Data file stubs | Integration planning |
| Validation results | Real-time feedback |
File Naming
Pattern: skills/{cluster}/{skill-name}/ (directory structure) Example: skills/fiction/dialogue/
What You Do NOT Do
- You do not build skills without clear problem states
- You do not create scripts without SKILL.md documentation
- You do not duplicate existing skill coverage
- You do not skip integration mapping
- You build the framework; the user decides what skills to create
Integration with Other Skills
With list-builder (fiction cluster)
Use list-builder quality criteria for any data files:
- Validate list maturity before marking skill production-ready
- Follow dimensional frameworks for list variety
Cross-Cluster Skills
Skills can reference skills in other clusters:
- Document integration in both skills
- Use full path references when crossing clusters
Cluster Conventions
When building skills within a cluster:
- Set
clusterin frontmatter to the parent skill - Add integration tables mapping states between skills
- Follow the cluster's established patterns for scripts and data
#!/usr/bin/env -S deno run --allow-read --allow-write
/**
* Skill Scaffolder
*
* Generates skill directory structure and template files.
* Creates SKILL.md, scripts/, and data/ directories with templates.
*
* Usage:
* deno run --allow-read --allow-write scaffold.ts skill-name
* deno run --allow-read --allow-write scaffold.ts skill-name --type diagnostic
* deno run --allow-read --allow-write scaffold.ts skill-name --dry-run
*/
interface ScaffoldConfig {
skillName: string;
skillType: "diagnostic" | "generator" | "utility";
statePrefix: string;
stateCount: number;
includeScript: boolean;
includeData: boolean;
domain: string;
cluster: string | null;
outputDir: string;
}
const SKILL_TYPES = ["diagnostic", "generator", "utility"];
function toTitleCase(str: string): string {
return str
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
}
function toPrefix(str: string): string {
// First letter of each word, uppercase
const words = str.split("-");
if (words.length === 1) {
return str.charAt(0).toUpperCase();
}
return words.map((w) => w.charAt(0).toUpperCase()).join("");
}
function generateSkillMd(config: ScaffoldConfig): string {
const title = toTitleCase(config.skillName);
const prefix = config.statePrefix;
let content = `---
name: ${config.skillName}
description: [TODO: One sentence starting with action verb - "Diagnose...", "Generate...", "Transform..."]
license: MIT
metadata:
author: [TODO: your-name]
version: "1.0"
domain: ${config.domain}
`;
if (config.cluster) {
content += ` cluster: ${config.cluster}\n`;
}
if (config.skillType === "utility") {
content += ` type: utility\n`;
}
content += `---
# ${title}: [Subtitle]
You [TODO: role description]. Your role is to [TODO: specific function].
## Core Principle
**[TODO: Bold statement capturing the diagnostic/functional essence]**
## The States
`;
// Generate state templates
for (let i = 1; i <= config.stateCount; i++) {
content += `### State ${prefix}${i}: [State Name]
**Symptoms:** [What the user notices]
**Key Questions:** [What to ask to diagnose]
**Interventions:** [What framework/tool to apply]
`;
}
content += `## Diagnostic Process
When a writer presents a ${config.skillName.replace("-", " ")} problem:
1. **Listen for symptoms** - What specifically feels wrong?
2. **Identify the state** - Match symptoms to states above
3. **Ask key questions** - Gather information needed for diagnosis
4. **Recommend intervention** - Point to specific framework/tool
5. **Suggest first step** - What's the minimal viable fix?
## Key Questions
### For [Category A]
- [Question 1]?
- [Question 2]?
- [Question 3]?
### For [Category B]
- [Question 4]?
- [Question 5]?
- [Question 6]?
## Anti-Patterns
### The [Anti-Pattern Name]
**Problem:** [What goes wrong]
**Fix:** [How to fix it]
### The [Another Anti-Pattern]
**Problem:** [What goes wrong]
**Fix:** [How to fix it]
`;
if (config.includeScript) {
content += `## Available Tools
### ${config.skillName.replace(/-/g, "_")}.ts
[TODO: Description of what this script does]
\`\`\`bash
deno run --allow-read scripts/${config.skillName.replace(/-/g, "_")}.ts
deno run --allow-read scripts/${config.skillName.replace(/-/g, "_")}.ts --option value
\`\`\`
**Output:** [What the script produces]
`;
}
content += `## Example Interaction
**Writer:** "[Example problem statement]"
**Your approach:**
1. Identify State ${prefix}[N] ([State Name])
2. Ask: "[Clarifying question]"
3. [Action you take]
4. Suggest: "[Specific recommendation]"
## What You Do NOT Do
- You do not [boundary 1]
- You do not [boundary 2]
- You diagnose, recommend, and explain—the writer decides
## Integration with story-sense
| story-sense State | May Lead to ${title} |
|-------------------|${"-".repeat(title.length + 14)}|
| State [N] | ${prefix}[N] when [condition] |
## Integration with [Other Skill]
[TODO: How this skill connects to other skills in the cluster]
`;
return content;
}
function generateScriptTemplate(skillName: string): string {
const scriptName = skillName.replace(/-/g, "_");
const resultType = toTitleCase(skillName).replace(/ /g, "") + "Result";
return `#!/usr/bin/env -S deno run --allow-read
/**
* ${toTitleCase(skillName)} Generator
*
* [TODO: Description of what this script does]
*
* Usage:
* deno run --allow-read ${scriptName}.ts
* deno run --allow-read ${scriptName}.ts --option value
* deno run --allow-read ${scriptName}.ts "input" --json
*/
// === INTERFACES ===
interface ${resultType} {
name: string;
// TODO: Add result fields
}
// === DATA ===
const DATA: Record<string, string[]> = {
category_one: [
"Item with specific detail",
"Another concrete item",
// TODO: Add more items (target 30+ for functional, 75+ for production)
],
};
// === UTILITIES ===
function randomFrom<T>(arr: T[], count: number = 1): T[] {
const shuffled = [...arr].sort(() => Math.random() - 0.5);
return shuffled.slice(0, Math.min(count, arr.length));
}
// === CORE LOGIC ===
function generate(
input: string | null,
option: string | null
): ${resultType} {
// TODO: Implement generation logic
return {
name: input || "Default",
};
}
// === FORMATTING ===
function formatResult(result: ${resultType}): string {
const lines: string[] = [];
lines.push(\`# ${toTitleCase(skillName)}: \${result.name}\\n\`);
lines.push("## Section\\n");
lines.push("[TODO: Format output]");
lines.push("");
return lines.join("\\n");
}
// === MAIN ===
function main(): void {
const args = Deno.args;
if (args.includes("--help") || args.includes("-h")) {
console.log(\`${toTitleCase(skillName)} Generator
[TODO: Description]
Usage:
deno run --allow-read ${scriptName}.ts [options] [input]
Options:
--option S [Description]
--json Output as JSON
Examples:
deno run --allow-read ${scriptName}.ts
deno run --allow-read ${scriptName}.ts "Custom Input"
\`);
Deno.exit(0);
}
// Parse arguments
const optionIndex = args.indexOf("--option");
const jsonOutput = args.includes("--json");
const option = optionIndex !== -1 && args[optionIndex + 1]
? args[optionIndex + 1]
: null;
// Find positional argument
const skipIndices = new Set<number>();
if (optionIndex !== -1) {
skipIndices.add(optionIndex);
skipIndices.add(optionIndex + 1);
}
let positionalArg: string | null = null;
for (let i = 0; i < args.length; i++) {
if (!args[i].startsWith("--") && !skipIndices.has(i)) {
positionalArg = args[i];
break;
}
}
// Generate
const result = generate(positionalArg, option);
// Output
if (jsonOutput) {
console.log(JSON.stringify(result, null, 2));
} else {
console.log(formatResult(result));
}
}
main();
`;
}
function generateDataTemplate(skillName: string): string {
return JSON.stringify(
{
_meta: {
description: `Data for ${toTitleCase(skillName)} skill`,
usage: "Load with Deno.readTextFile and JSON.parse",
},
category_one: [
"Specific item with enough detail to spark ideas",
"Another item that is 20-60 characters ideally",
"Items should be concrete not vague",
],
category_two: [
"Different category of items",
"Organized by type for easier selection",
],
},
null,
2
);
}
async function createDirectory(path: string, dryRun: boolean): Promise<void> {
if (dryRun) {
console.log(` [dry-run] Would create directory: ${path}`);
return;
}
try {
await Deno.mkdir(path, { recursive: true });
console.log(` Created directory: ${path}`);
} catch (e) {
if (!(e instanceof Deno.errors.AlreadyExists)) {
throw e;
}
}
}
async function writeFile(
path: string,
content: string,
dryRun: boolean
): Promise<void> {
if (dryRun) {
console.log(` [dry-run] Would create file: ${path}`);
console.log(` [dry-run] Content preview: ${content.slice(0, 100)}...`);
return;
}
// Check if file exists
try {
await Deno.stat(path);
console.log(` Skipped (exists): ${path}`);
return;
} catch {
// File doesn't exist, create it
}
await Deno.writeTextFile(path, content);
console.log(` Created file: ${path}`);
}
async function scaffold(config: ScaffoldConfig, dryRun: boolean): Promise<void> {
const skillDir = `${config.outputDir}/${config.skillName}`;
console.log(`\nScaffolding skill: ${config.skillName}`);
console.log(`Type: ${config.skillType}`);
console.log(`Domain: ${config.domain}`);
if (config.cluster) {
console.log(`Cluster: ${config.cluster}`);
}
console.log(`Location: ${skillDir}\n`);
// Create directories
await createDirectory(skillDir, dryRun);
await createDirectory(`${skillDir}/scripts`, dryRun);
if (config.includeData) {
await createDirectory(`${skillDir}/data`, dryRun);
}
// Create SKILL.md
const skillMd = generateSkillMd(config);
await writeFile(`${skillDir}/SKILL.md`, skillMd, dryRun);
// Create script template
if (config.includeScript) {
const scriptName = config.skillName.replace(/-/g, "_");
const script = generateScriptTemplate(config.skillName);
await writeFile(`${skillDir}/scripts/${scriptName}.ts`, script, dryRun);
}
// Create data template
if (config.includeData) {
const data = generateDataTemplate(config.skillName);
await writeFile(`${skillDir}/data/${config.skillName.replace(/-/g, "-")}-data.json`, data, dryRun);
}
console.log("\nScaffolding complete!");
console.log("\nNext steps:");
console.log("1. Edit SKILL.md - fill in [TODO] sections");
console.log("2. Define your states with symptoms/questions/interventions");
console.log("3. Implement script logic if applicable");
console.log("4. Run validate-skill.ts to check completeness");
}
function main(): void {
const args = Deno.args;
if (args.includes("--help") || args.includes("-h")) {
console.log(`Skill Scaffolder
Generates skill directory structure and template files.
Usage:
deno run --allow-read --allow-write scaffold.ts <skill-name> [options]
Options:
--type T Skill type: diagnostic, generator, utility
Default: diagnostic
--domain D Domain for the skill (fiction, research, agile-software, etc.)
Default: custom
--cluster C Parent skill if part of a cluster (optional)
--out O Output directory for the skill
Default: current directory
--states N Number of state templates to generate
Default: 5
--no-script Don't create script template
--no-data Don't create data directory/template
--dry-run Preview without creating files
Examples:
deno run --allow-read --allow-write scaffold.ts dialogue --domain fiction --cluster story-sense
deno run --allow-read --allow-write scaffold.ts sprint-health --domain agile-software
deno run --allow-read --allow-write scaffold.ts my-skill --dry-run
`);
Deno.exit(0);
}
// Parse arguments
const typeIndex = args.indexOf("--type");
const statesIndex = args.indexOf("--states");
const domainIndex = args.indexOf("--domain");
const clusterIndex = args.indexOf("--cluster");
const outIndex = args.indexOf("--out");
const dryRun = args.includes("--dry-run");
const noScript = args.includes("--no-script");
const noData = args.includes("--no-data");
const skillType = typeIndex !== -1 && args[typeIndex + 1]
? args[typeIndex + 1] as "diagnostic" | "generator" | "utility"
: "diagnostic";
const stateCount = statesIndex !== -1 && args[statesIndex + 1]
? parseInt(args[statesIndex + 1]) || 5
: 5;
const domain = domainIndex !== -1 && args[domainIndex + 1]
? args[domainIndex + 1]
: "custom";
const cluster = clusterIndex !== -1 && args[clusterIndex + 1]
? args[clusterIndex + 1]
: null;
const scriptDir = new URL(".", import.meta.url).pathname;
const defaultOutDir = Deno.cwd();
const outputDir = outIndex !== -1 && args[outIndex + 1]
? (args[outIndex + 1].startsWith("/") ? args[outIndex + 1] : `${scriptDir}${args[outIndex + 1]}`)
: defaultOutDir;
if (!SKILL_TYPES.includes(skillType)) {
console.error(`Error: Invalid skill type '${skillType}'`);
console.error(`Valid types: ${SKILL_TYPES.join(", ")}`);
Deno.exit(1);
}
// Find skill name (first positional argument)
const skipIndices = new Set<number>();
if (typeIndex !== -1) {
skipIndices.add(typeIndex);
skipIndices.add(typeIndex + 1);
}
if (statesIndex !== -1) {
skipIndices.add(statesIndex);
skipIndices.add(statesIndex + 1);
}
if (domainIndex !== -1) {
skipIndices.add(domainIndex);
skipIndices.add(domainIndex + 1);
}
if (clusterIndex !== -1) {
skipIndices.add(clusterIndex);
skipIndices.add(clusterIndex + 1);
}
if (outIndex !== -1) {
skipIndices.add(outIndex);
skipIndices.add(outIndex + 1);
}
let skillName: string | null = null;
for (let i = 0; i < args.length; i++) {
if (!args[i].startsWith("--") && !skipIndices.has(i)) {
skillName = args[i];
break;
}
}
if (!skillName) {
console.error("Error: No skill name specified");
console.error("Usage: scaffold.ts <skill-name>");
Deno.exit(1);
}
// Validate skill name
if (!/^[a-z][a-z0-9-]*$/.test(skillName)) {
console.error("Error: Skill name must be lowercase with hyphens only");
console.error("Example: my-skill-name");
Deno.exit(1);
}
const config: ScaffoldConfig = {
skillName,
skillType,
statePrefix: toPrefix(skillName),
stateCount,
includeScript: !noScript,
includeData: !noData && skillType !== "utility",
domain,
cluster,
outputDir,
};
scaffold(config, dryRun);
}
main();
#!/usr/bin/env -S deno run --allow-read
/**
* Skill Validator
*
* Validates skill completeness and pattern conformance.
* Checks for required frontmatter, state definitions, script docs, and integrations.
*
* Usage:
* deno run --allow-read validate-skill.ts ../worldbuilding
* deno run --allow-read validate-skill.ts --all
* deno run --allow-read validate-skill.ts ../conlang --json
*/
interface ValidationResult {
skillPath: string;
skillName: string;
valid: boolean;
score: number;
maxScore: number;
maturityScore: MaturityScore;
maturityLevel: string;
checks: CheckResult[];
summary: string;
}
interface CheckResult {
category: string;
check: string;
passed: boolean;
message: string;
weight: number;
dimension?: "completeness" | "quality" | "usability";
}
interface MaturityScore {
completeness: { score: number; max: number };
quality: { score: number; max: number };
usability: { score: number; max: number };
total: number;
}
// Required frontmatter fields
const REQUIRED_FRONTMATTER = ["name", "description", "license"];
const REQUIRED_METADATA = ["author", "version", "domain"];
const REQUIRED_METADATA_NEW = ["type", "mode"]; // New required fields
// Valid type and mode values
const VALID_TYPES = ["diagnostic", "generator", "utility"];
const VALID_MODES = ["diagnostic", "assistive", "collaborative", "evaluative", "application", "generative"];
// Skill types detected from frontmatter
type SkillType = "diagnostic" | "generator" | "utility";
function detectSkillType(frontmatter: Record<string, unknown>): SkillType {
const metadata = frontmatter.metadata as Record<string, string> | undefined;
// Check for explicit type in metadata
if (metadata?.type === "utility") return "utility";
if (metadata?.type === "generator") return "generator";
if (metadata?.type === "diagnostic") return "diagnostic";
// Fallback detection based on mode
if (metadata?.mode === "generative") return "generator";
// Default to diagnostic
return "diagnostic";
}
// Required sections vary by skill type
const REQUIRED_SECTIONS: Record<SkillType, string[]> = {
diagnostic: [
"Core Principle",
"State",
"Diagnostic Process",
"What You Do NOT Do",
"Integration",
],
generator: [
"Core Principle",
"What You Do NOT Do",
],
utility: [
"Core Principle",
],
};
const RECOMMENDED_SECTIONS: Record<SkillType, string[]> = {
diagnostic: [
"Available Tools",
"Anti-Pattern",
"Example",
"Key Question",
],
generator: [
"Available Tools",
"Example",
"Integration",
],
utility: [
"Available Tools",
"Example",
"Integration",
],
};
async function fileExists(path: string): Promise<boolean> {
try {
await Deno.stat(path);
return true;
} catch {
return false;
}
}
async function readSkillMd(skillPath: string): Promise<string | null> {
const path = `${skillPath}/SKILL.md`;
try {
return await Deno.readTextFile(path);
} catch {
return null;
}
}
function parseFrontmatter(content: string): Record<string, unknown> | null {
const match = content.match(/^---\n([\s\S]*?)\n---/);
if (!match) return null;
const frontmatter: Record<string, unknown> = {};
const lines = match[1].split("\n");
let currentKey = "";
let inMetadata = false;
const metadata: Record<string, string> = {};
for (const line of lines) {
if (line.startsWith("metadata:")) {
inMetadata = true;
continue;
}
if (inMetadata && line.startsWith(" ")) {
const metaMatch = line.match(/^\s+(\w+):\s*"?([^"]*)"?$/);
if (metaMatch) {
metadata[metaMatch[1]] = metaMatch[2];
}
} else if (!line.startsWith(" ")) {
inMetadata = false;
const keyMatch = line.match(/^(\w+):\s*(.*)$/);
if (keyMatch) {
currentKey = keyMatch[1];
frontmatter[currentKey] = keyMatch[2].replace(/^"(.*)"$/, "$1");
}
}
}
if (Object.keys(metadata).length > 0) {
frontmatter.metadata = metadata;
}
return frontmatter;
}
function checkSection(content: string, sectionName: string): boolean {
// Look for ## Section or # Section patterns
const pattern = new RegExp(`^##?\\s+.*${sectionName}`, "im");
return pattern.test(content);
}
function countStates(content: string): number {
// Count ### State patterns
const matches = content.match(/^###\s+State\s+\w+\d+/gim);
return matches ? matches.length : 0;
}
function hasStateComponents(content: string): {
hasSymptoms: boolean;
hasQuestions: boolean;
hasInterventions: boolean;
} {
return {
hasSymptoms: /\*\*Symptoms:\*\*/i.test(content),
hasQuestions: /\*\*Key Questions:\*\*/i.test(content),
hasInterventions: /\*\*Interventions:\*\*/i.test(content),
};
}
function countAntiPatterns(content: string): number {
// Count ### The [Name] patterns in Anti-Patterns section
const antiPatternSection = content.match(/## Anti-Patterns[\s\S]*?(?=\n## |$)/i);
if (!antiPatternSection) return 0;
const matches = antiPatternSection[0].match(/^###\s+The\s+/gim);
return matches ? matches.length : 0;
}
function checkStateNamingConvention(content: string): {
consistent: boolean;
prefix: string | null;
issues: string[];
} {
const stateMatches = content.match(/^###\s+State\s+([A-Z]+)(\d+(?:\.\d+)?)/gim);
if (!stateMatches || stateMatches.length === 0) {
return { consistent: true, prefix: null, issues: [] };
}
const prefixes = new Set<string>();
const issues: string[] = [];
for (const match of stateMatches) {
const prefixMatch = match.match(/State\s+([A-Z]+)\d/i);
if (prefixMatch) {
prefixes.add(prefixMatch[1].toUpperCase());
}
}
if (prefixes.size > 1) {
issues.push(`Multiple prefixes found: ${[...prefixes].join(", ")}`);
}
const prefix = prefixes.size === 1 ? [...prefixes][0] : null;
return {
consistent: prefixes.size <= 1,
prefix,
issues,
};
}
function hasIntegrationGraph(content: string): {
hasInbound: boolean;
hasOutbound: boolean;
hasComplementary: boolean;
} {
const integrationSection = content.match(/## Integration[\s\S]*?(?=\n## |$)/i);
if (!integrationSection) {
return { hasInbound: false, hasOutbound: false, hasComplementary: false };
}
const section = integrationSection[0];
return {
hasInbound: /### Inbound/i.test(section) || /From Other Skills/i.test(section),
hasOutbound: /### Outbound/i.test(section) || /To Other Skills/i.test(section),
hasComplementary: /### Complementary/i.test(section) || /Complementary Skills/i.test(section),
};
}
function hasOutputPersistence(content: string): boolean {
return /## Output Persistence/i.test(content);
}
function hasQuickReference(content: string): boolean {
return /## Quick Reference/i.test(content) || /### Quick Reference/i.test(content);
}
function hasDecisionTree(content: string): boolean {
return /decision tree/i.test(content) || /routing logic/i.test(content) || /when to use/i.test(content);
}
function calculateMaturityLevel(score: number): string {
if (score >= 18) return "Battle-Tested";
if (score >= 13) return "Stable";
if (score >= 8) return "Developing";
return "Draft";
}
async function getScripts(skillPath: string): Promise<string[]> {
const scriptsPath = `${skillPath}/scripts`;
const scripts: string[] = [];
try {
for await (const entry of Deno.readDir(scriptsPath)) {
if (entry.isFile && entry.name.endsWith(".ts")) {
scripts.push(entry.name);
}
}
} catch {
// No scripts directory
}
return scripts;
}
async function getDataFiles(skillPath: string): Promise<string[]> {
const dataPath = `${skillPath}/data`;
const files: string[] = [];
try {
for await (const entry of Deno.readDir(dataPath)) {
if (entry.isFile && entry.name.endsWith(".json")) {
files.push(entry.name);
}
}
} catch {
// No data directory
}
return files;
}
function scriptDocumented(content: string, scriptName: string): boolean {
const baseName = scriptName.replace(".ts", "");
return content.includes(`${baseName}.ts`) || content.includes(`### ${baseName}`);
}
async function validateSkill(skillPath: string): Promise<ValidationResult> {
const checks: CheckResult[] = [];
const skillName = skillPath.split("/").pop() || "unknown";
// Check SKILL.md exists
const content = await readSkillMd(skillPath);
if (!content) {
return {
skillPath,
skillName,
valid: false,
score: 0,
maxScore: 100,
checks: [
{
category: "Structure",
check: "SKILL.md exists",
passed: false,
message: "SKILL.md not found",
weight: 20,
},
],
summary: "SKILL.md not found - cannot validate",
};
}
checks.push({
category: "Structure",
check: "SKILL.md exists",
passed: true,
message: "SKILL.md found",
weight: 5,
});
// Check frontmatter
const frontmatter = parseFrontmatter(content);
if (!frontmatter) {
checks.push({
category: "Frontmatter",
check: "Valid frontmatter",
passed: false,
message: "Could not parse frontmatter",
weight: 10,
});
} else {
checks.push({
category: "Frontmatter",
check: "Valid frontmatter",
passed: true,
message: "Frontmatter parsed successfully",
weight: 5,
});
// Check required fields
for (const field of REQUIRED_FRONTMATTER) {
const has = field in frontmatter;
checks.push({
category: "Frontmatter",
check: `Has ${field}`,
passed: has,
message: has ? `${field} present` : `Missing ${field}`,
weight: 3,
});
}
// Check metadata
const metadata = frontmatter.metadata as Record<string, string> | undefined;
for (const field of REQUIRED_METADATA) {
const has = metadata && field in metadata;
checks.push({
category: "Frontmatter",
check: `Has metadata.${field}`,
passed: !!has,
message: has ? `metadata.${field} present` : `Missing metadata.${field}`,
weight: 2,
});
}
// Check new required metadata: type and mode
for (const field of REQUIRED_METADATA_NEW) {
const has = metadata && field in metadata;
const value = metadata?.[field];
let valid = false;
let message = "";
if (!has) {
message = `Missing metadata.${field} (REQUIRED)`;
} else if (field === "type") {
valid = VALID_TYPES.includes(value);
message = valid
? `type: ${value}`
: `Invalid type: ${value}. Must be: ${VALID_TYPES.join(" | ")}`;
} else if (field === "mode") {
// Mode can be compound (e.g., "diagnostic+generative")
const modes = value.split("+").map((m: string) => m.trim());
valid = modes.every((m: string) => VALID_MODES.includes(m));
message = valid
? `mode: ${value}`
: `Invalid mode: ${value}. Must be: ${VALID_MODES.join(" | ")}`;
}
checks.push({
category: "Frontmatter",
check: `Has valid metadata.${field}`,
passed: has && valid,
message,
weight: 3,
dimension: "quality",
});
}
}
// Detect skill type for appropriate validation
const skillType = frontmatter ? detectSkillType(frontmatter) : "diagnostic";
checks.push({
category: "Type",
check: "Skill type detected",
passed: true,
message: `Detected as ${skillType} skill`,
weight: 0, // Informational only
});
// Check required sections for this skill type
for (const section of REQUIRED_SECTIONS[skillType]) {
const has = checkSection(content, section);
checks.push({
category: "Structure",
check: `Has ${section} section`,
passed: has,
message: has ? `${section} section found` : `Missing ${section} section`,
weight: 5,
});
}
// Check recommended sections for this skill type
for (const section of RECOMMENDED_SECTIONS[skillType]) {
const has = checkSection(content, section);
checks.push({
category: "Recommended",
check: `Has ${section} section`,
passed: has,
message: has ? `${section} section found` : `Could add ${section} section`,
weight: 2,
});
}
// Only check states for diagnostic skills
if (skillType === "diagnostic") {
const stateCount = countStates(content);
checks.push({
category: "Diagnostic",
check: "Has defined states",
passed: stateCount >= 3,
message:
stateCount >= 3
? `${stateCount} states defined`
: `Only ${stateCount} states (recommend 3-7)`,
weight: 5,
});
// Check state components
const components = hasStateComponents(content);
checks.push({
category: "Diagnostic",
check: "States have symptoms",
passed: components.hasSymptoms,
message: components.hasSymptoms
? "States include symptoms"
: "States missing symptoms",
weight: 3,
});
checks.push({
category: "Diagnostic",
check: "States have key questions",
passed: components.hasQuestions,
message: components.hasQuestions
? "States include key questions"
: "States missing key questions",
weight: 3,
});
checks.push({
category: "Diagnostic",
check: "States have interventions",
passed: components.hasInterventions,
message: components.hasInterventions
? "States include interventions"
: "States missing interventions",
weight: 3,
dimension: "completeness",
});
// Check state naming convention
const stateNaming = checkStateNamingConvention(content);
checks.push({
category: "Quality",
check: "Consistent state naming",
passed: stateNaming.consistent,
message: stateNaming.consistent
? stateNaming.prefix
? `Consistent prefix: ${stateNaming.prefix}`
: "No states to check"
: stateNaming.issues.join(", "),
weight: 2,
dimension: "quality",
});
}
// Check anti-patterns (all skill types)
const antiPatternCount = countAntiPatterns(content);
const minAntiPatterns = skillType === "diagnostic" ? 3 : 2;
checks.push({
category: "Completeness",
check: "Has anti-patterns",
passed: antiPatternCount >= minAntiPatterns,
message: antiPatternCount >= minAntiPatterns
? `${antiPatternCount} anti-patterns documented`
: `Only ${antiPatternCount} anti-patterns (minimum: ${minAntiPatterns})`,
weight: 4,
dimension: "completeness",
});
// Check integration graph structure
const integration = hasIntegrationGraph(content);
const hasAnyIntegration = integration.hasInbound || integration.hasOutbound;
checks.push({
category: "Quality",
check: "Has integration connections",
passed: hasAnyIntegration,
message: hasAnyIntegration
? `Integration: ${[
integration.hasInbound ? "inbound" : "",
integration.hasOutbound ? "outbound" : "",
integration.hasComplementary ? "complementary" : "",
]
.filter(Boolean)
.join(", ")}`
: "Missing integration connections (need inbound OR outbound)",
weight: 2,
dimension: "quality",
});
// Check usability features
const hasOutput = hasOutputPersistence(content);
checks.push({
category: "Usability",
check: "Has output persistence",
passed: hasOutput,
message: hasOutput
? "Output persistence section present"
: "Missing output persistence section",
weight: 2,
dimension: "usability",
});
const hasQuickRef = hasQuickReference(content);
checks.push({
category: "Usability",
check: "Has quick reference",
passed: hasQuickRef,
message: hasQuickRef
? "Quick reference section present"
: "No quick reference section",
weight: 2,
dimension: "usability",
});
const hasRouting = hasDecisionTree(content);
checks.push({
category: "Usability",
check: "Has decision guidance",
passed: hasRouting,
message: hasRouting
? "Decision/routing logic present"
: "No decision tree or routing logic",
weight: 2,
dimension: "usability",
});
// Check scripts
const scripts = await getScripts(skillPath);
checks.push({
category: "Tools",
check: "Has scripts",
passed: scripts.length > 0,
message:
scripts.length > 0
? `${scripts.length} script(s): ${scripts.join(", ")}`
: "No scripts found",
weight: 3,
});
// Check script documentation
for (const script of scripts) {
const documented = scriptDocumented(content, script);
checks.push({
category: "Tools",
check: `${script} documented`,
passed: documented,
message: documented
? `${script} documented in SKILL.md`
: `${script} not documented in SKILL.md`,
weight: 2,
});
}
// Check data files
const dataFiles = await getDataFiles(skillPath);
if (dataFiles.length > 0) {
checks.push({
category: "Data",
check: "Has data files",
passed: true,
message: `${dataFiles.length} data file(s): ${dataFiles.join(", ")}`,
weight: 2,
});
}
// Calculate legacy score
let score = 0;
let maxScore = 0;
for (const check of checks) {
maxScore += check.weight;
if (check.passed) {
score += check.weight;
}
}
// Calculate 20-point maturity score by dimension
const maturityScore: MaturityScore = {
completeness: { score: 0, max: 11 },
quality: { score: 0, max: 5 },
usability: { score: 0, max: 4 },
total: 0,
};
// Count dimension scores based on passed checks
for (const check of checks) {
if (check.dimension && check.passed) {
// Map weight to maturity points (simplified: passed = 1 point per check)
if (check.dimension === "completeness") {
maturityScore.completeness.score = Math.min(
maturityScore.completeness.score + 1,
maturityScore.completeness.max
);
} else if (check.dimension === "quality") {
maturityScore.quality.score = Math.min(
maturityScore.quality.score + 1,
maturityScore.quality.max
);
} else if (check.dimension === "usability") {
maturityScore.usability.score = Math.min(
maturityScore.usability.score + 1,
maturityScore.usability.max
);
}
}
}
// Also count core sections toward completeness
const hasCorePrinciple = checks.some(c => c.check.includes("Core Principle") && c.passed);
const hasStates = checks.some(c => c.check.includes("defined states") && c.passed);
const hasProcess = checks.some(c => c.check.includes("Diagnostic Process") && c.passed);
const hasBoundaries = checks.some(c => c.check.includes("What You Do NOT Do") && c.passed);
const hasExamples = checks.some(c => c.check.includes("Example") && c.passed);
if (hasCorePrinciple) maturityScore.completeness.score = Math.min(maturityScore.completeness.score + 1, 11);
if (hasStates) maturityScore.completeness.score = Math.min(maturityScore.completeness.score + 2, 11);
if (hasProcess) maturityScore.completeness.score = Math.min(maturityScore.completeness.score + 1, 11);
if (hasBoundaries) maturityScore.completeness.score = Math.min(maturityScore.completeness.score + 1, 11);
if (hasExamples) maturityScore.completeness.score = Math.min(maturityScore.completeness.score + 2, 11);
// Quality: self-contained (assumed if SKILL.md exists), type+mode, state naming, integration, tools documented
const hasTypeMode = checks.filter(c => c.check.includes("metadata.type") || c.check.includes("metadata.mode")).every(c => c.passed);
const hasToolsDocs = checks.filter(c => c.check.includes("documented")).every(c => c.passed);
maturityScore.quality.score = Math.min(maturityScore.quality.score + 1, 5); // Self-contained baseline
if (hasTypeMode) maturityScore.quality.score = Math.min(maturityScore.quality.score + 1, 5);
if (hasToolsDocs) maturityScore.quality.score = Math.min(maturityScore.quality.score + 1, 5);
maturityScore.total =
maturityScore.completeness.score +
maturityScore.quality.score +
maturityScore.usability.score;
const maturityLevel = calculateMaturityLevel(maturityScore.total);
const percentage = Math.round((score / maxScore) * 100);
const valid = percentage >= 70;
let summary: string;
if (maturityScore.total >= 18) {
summary = "Battle-Tested - production proven";
} else if (maturityScore.total >= 13) {
summary = "Stable - production ready";
} else if (maturityScore.total >= 8) {
summary = "Developing - functional but incomplete";
} else {
summary = "Draft - needs significant work";
}
return {
skillPath,
skillName,
valid,
score,
maxScore,
maturityScore,
maturityLevel,
checks,
summary: `${maturityScore.total}/20 ${maturityLevel} - ${summary}`,
};
}
function formatResult(result: ValidationResult): string {
const lines: string[] = [];
const statusIcon = result.valid ? "✓" : "⚠";
lines.push(`# Skill Validation: ${result.skillName}\n`);
lines.push(`${statusIcon} **${result.summary}**\n`);
// Maturity score breakdown
lines.push(`## Maturity Score: ${result.maturityScore.total}/20 (${result.maturityLevel})\n`);
lines.push(`| Dimension | Score |`);
lines.push(`|-----------|-------|`);
lines.push(`| Completeness | ${result.maturityScore.completeness.score}/${result.maturityScore.completeness.max} |`);
lines.push(`| Quality | ${result.maturityScore.quality.score}/${result.maturityScore.quality.max} |`);
lines.push(`| Usability | ${result.maturityScore.usability.score}/${result.maturityScore.usability.max} |`);
lines.push("");
// Group by category
const categories = new Map<string, CheckResult[]>();
for (const check of result.checks) {
if (!categories.has(check.category)) {
categories.set(check.category, []);
}
categories.get(check.category)!.push(check);
}
for (const [category, checks] of categories) {
const passed = checks.filter((c) => c.passed).length;
const total = checks.length;
lines.push(`## ${category} (${passed}/${total})\n`);
for (const check of checks) {
const icon = check.passed ? "✓" : "✗";
lines.push(`${icon} ${check.check}: ${check.message}`);
}
lines.push("");
}
// Failed checks summary
const failed = result.checks.filter((c) => !c.passed);
if (failed.length > 0) {
lines.push("## To Fix\n");
for (const check of failed) {
if (check.weight >= 3) {
lines.push(`- [ ] ${check.check}`);
}
}
}
return lines.join("\n");
}
async function getAllSkills(basePath: string): Promise<string[]> {
const skills: string[] = [];
try {
for await (const entry of Deno.readDir(basePath)) {
if (entry.isDirectory && !entry.name.startsWith(".")) {
const skillMdPath = `${basePath}/${entry.name}/SKILL.md`;
if (await fileExists(skillMdPath)) {
skills.push(`${basePath}/${entry.name}`);
}
}
}
} catch {
// Directory doesn't exist
}
return skills;
}
async function main(): Promise<void> {
const args = Deno.args;
if (args.includes("--help") || args.includes("-h")) {
console.log(`Skill Validator
Validates skill completeness and pattern conformance.
Usage:
deno run --allow-read validate-skill.ts <skill-path>
deno run --allow-read validate-skill.ts --all
deno run --allow-read validate-skill.ts <skill-path> --json
Options:
--all Validate all skills in a directory
--dir D Directory to scan for skills (default: ../fiction or ..)
--json Output as JSON
Examples:
deno run --allow-read validate-skill.ts /path/to/skill
deno run --allow-read validate-skill.ts --all --dir ../fiction
deno run --allow-read validate-skill.ts skill-name --json
`);
Deno.exit(0);
}
const jsonOutput = args.includes("--json");
const validateAll = args.includes("--all");
// Parse --dir option
const dirIndex = args.indexOf("--dir");
const scriptDir = new URL(".", import.meta.url).pathname;
const skillsDir = `${scriptDir}../..`; // skills/ directory
let targetDir: string;
if (dirIndex !== -1 && args[dirIndex + 1]) {
const dirArg = args[dirIndex + 1];
targetDir = dirArg.startsWith("/") ? dirArg : `${scriptDir}${dirArg}`;
} else {
// Default: look in parent skills/ directory
targetDir = skillsDir;
}
let skillPaths: string[] = [];
if (validateAll) {
// Find all skills in target directory (recursively check subdirs)
skillPaths = await getAllSkills(targetDir);
// Also check subdirectories (like fiction/, research/, etc.)
try {
for await (const entry of Deno.readDir(targetDir)) {
if (entry.isDirectory && !entry.name.startsWith(".") && entry.name !== "skill-builder") {
const subSkills = await getAllSkills(`${targetDir}/${entry.name}`);
skillPaths.push(...subSkills);
}
}
} catch {
// Directory doesn't exist or not readable
}
} else {
// Find skill path argument (skip flags and their values)
const skipIndices = new Set<number>();
if (dirIndex !== -1) {
skipIndices.add(dirIndex);
skipIndices.add(dirIndex + 1);
}
let skillPath: string | null = null;
for (let i = 0; i < args.length; i++) {
if (!args[i].startsWith("--") && !skipIndices.has(i)) {
skillPath = args[i];
break;
}
}
if (!skillPath) {
console.error("Error: No skill path specified");
console.error("Usage: validate-skill.ts <skill-path> or --all");
Deno.exit(1);
}
// Resolve path - try multiple strategies
if (skillPath.startsWith("/")) {
// Absolute path
skillPaths = [skillPath];
} else if (skillPath.startsWith("..") || skillPath.startsWith("./")) {
// Relative to script location
skillPaths = [`${scriptDir}${skillPath}`];
} else {
// Try to find skill by name in skills directory tree
// First check direct child
let found = false;
if (await fileExists(`${skillsDir}/${skillPath}/SKILL.md`)) {
skillPaths = [`${skillsDir}/${skillPath}`];
found = true;
} else {
// Check in subdirectories (fiction/skillname, research/skillname, etc.)
try {
for await (const entry of Deno.readDir(skillsDir)) {
if (entry.isDirectory && !entry.name.startsWith(".")) {
if (await fileExists(`${skillsDir}/${entry.name}/${skillPath}/SKILL.md`)) {
skillPaths = [`${skillsDir}/${entry.name}/${skillPath}`];
found = true;
break;
}
}
}
} catch {
// Directory not readable
}
}
if (!found) {
console.error(`Error: Could not find skill '${skillPath}'`);
Deno.exit(1);
}
}
}
const results: ValidationResult[] = [];
for (const path of skillPaths) {
results.push(await validateSkill(path));
}
if (jsonOutput) {
console.log(JSON.stringify(results.length === 1 ? results[0] : results, null, 2));
} else {
for (const result of results) {
console.log(formatResult(result));
if (results.length > 1) {
console.log("\n---\n");
}
}
// Summary if multiple
if (results.length > 1) {
const valid = results.filter((r) => r.valid).length;
console.log(`\n## Summary: ${valid}/${results.length} skills valid`);
}
}
}
main();
Output Persistence Section Template
Add this section to SKILL.md files, customizing the skill-specific content.
---
Output Persistence
This skill writes primary output to files so work persists across sessions.
Output Discovery
Before doing any other work:
1. Check for context/output-config.md in the project 2. If found, look for this skill's entry 3. If not found or no entry for this skill, ask the user first:
- "Where should I save output from this {{SKILL_NAME}} session?"
- Suggest a sensible location for this project
4. Store the user's preference:
- In
context/output-config.mdif context network exists - In
.{{SKILL_NAME}}-output.mdat project root otherwise
Primary Output
For this skill, persist:
- {{PRIMARY_OUTPUT_1}}
- {{PRIMARY_OUTPUT_2}}
- {{PRIMARY_OUTPUT_3}}
Conversation vs. File
| Goes to File | Stays in Conversation |
|---|---|
| {{FILE_OUTPUT_1}} | {{CONVERSATION_1}} |
| {{FILE_OUTPUT_2}} | {{CONVERSATION_2}} |
| {{FILE_OUTPUT_3}} | {{CONVERSATION_3}} |
File Naming
Pattern: {{NAMING_PATTERN}} Example: {{NAMING_EXAMPLE}}
---
Skill-Type Examples
For Diagnostic Skills
### Primary Output
For this skill, persist:
- Diagnosed state with evidence
- Recommended interventions
- Action items and next steps
### Conversation vs. File
| Goes to File | Stays in Conversation |
|--------------|----------------------|
| State diagnosis and evidence | Clarifying questions |
| Intervention recommendations | Discussion of options |
| Action items | Follow-up questions |
### File Naming
Pattern: `{project}-{skill}-{date}.md`
Example: `novel-draft-worldbuilding-2025-01-15.md`For Generative Skills
### Primary Output
For this skill, persist:
- Generated ideas and alternatives
- Constraints explored and axis rotations
- Selected/promising options with rationale
### Conversation vs. File
| Goes to File | Stays in Conversation |
|--------------|----------------------|
| Final/selected ideas | Iteration process |
| Evaluation criteria used | Discarded options |
| Promising combinations | Real-time feedback |
### File Naming
Pattern: `{topic}-{date}.md`
Example: `product-naming-2025-01-15.md`For Research Skills
### Primary Output
For this skill, persist:
- Summary of findings with confidence levels
- Vocabulary map (if built)
- Source list with assessments
- Identified gaps and next steps
### Conversation vs. File
| Goes to File | Stays in Conversation |
|--------------|----------------------|
| Synthesis document | Search refinement |
| Confidence-marked findings | Source evaluation discussion |
| Gap analysis | Follow-up questions |
### File Naming
Pattern: `{topic}-research-{date}.md`
Example: `competency-frameworks-research-2025-01-15.md`For Session-Based Skills
### Primary Output
For this skill, persist:
- Session log and narrative events
- Character/NPC states and changes
- World state updates
- Decisions and their consequences
### Conversation vs. File
| Goes to File | Stays in Conversation |
|--------------|----------------------|
| What happened (narrative) | Active play |
| State changes | Real-time decisions |
| Important revelations | Moment-to-moment action |
### File Naming
Pattern: `session-{date}.md` or `{campaign}-session-{number}-{date}.md`
Example: `session-2025-01-15.md`For Utility Skills
### Primary Output
For this skill, persist:
- Generated lists or data
- Configuration or settings produced
- Structured output artifacts
### Conversation vs. File
| Goes to File | Stays in Conversation |
|--------------|----------------------|
| The artifact itself | Validation discussion |
| Metadata about generation | Refinement requests |
### File Naming
Pattern: `{output-name}.{ext}` (often JSON or specialized format)
Example: `professions-unusual.json`#!/usr/bin/env -S deno run --allow-read
/**
* {{SCRIPT_TITLE}}
*
* {{SCRIPT_DESCRIPTION}}
*
* Usage:
* deno run --allow-read {{SCRIPT_NAME}}.ts # Default behavior
* deno run --allow-read {{SCRIPT_NAME}}.ts --option value # With options
* deno run --allow-read {{SCRIPT_NAME}}.ts "input" # With positional arg
* deno run --allow-read {{SCRIPT_NAME}}.ts --json # JSON output
*/
// === INTERFACES ===
interface {{RESULT_TYPE}} {
name: string;
// Add result fields
}
// === DATA ===
// Inline data for simple cases, or load from ../data/ for complex cases
const DATA: Record<string, string[]> = {
category_one: [
"Item with specific detail",
"Another concrete item",
"Third item for variety",
],
category_two: [
"Different category item",
"Second item here",
],
};
// === UTILITIES ===
function randomFrom<T>(arr: T[], count: number = 1): T[] {
const shuffled = [...arr].sort(() => Math.random() - 0.5);
return shuffled.slice(0, Math.min(count, arr.length));
}
// For loading external data files
async function loadData<T>(filename: string): Promise<T> {
const scriptDir = new URL(".", import.meta.url).pathname;
const dataPath = `${scriptDir}../data/${filename}`;
try {
const text = await Deno.readTextFile(dataPath);
return JSON.parse(text);
} catch (e) {
console.error(`Error loading ${dataPath}: ${e}`);
Deno.exit(1);
}
}
// === CORE LOGIC ===
function generate(
input: string | null,
option: string | null
): {{RESULT_TYPE}} {
// Generation logic here
return {
name: input || "Default",
// Populate result
};
}
// === FORMATTING ===
function formatResult(result: {{RESULT_TYPE}}): string {
const lines: string[] = [];
lines.push(`# {{OUTPUT_TITLE}}: ${result.name}\n`);
// Format sections
lines.push("## Section\n");
lines.push("Content here");
lines.push("");
return lines.join("\n");
}
function formatBrief(result: {{RESULT_TYPE}}): string {
return `${result.name} [brief output]`;
}
// === MAIN ===
function main(): void {
const args = Deno.args;
// Help
if (args.includes("--help") || args.includes("-h")) {
console.log(`{{SCRIPT_TITLE}}
{{SCRIPT_DESCRIPTION}}
Usage:
deno run --allow-read {{SCRIPT_NAME}}.ts [options] [input]
Options:
--option S Description of option
Available: value1, value2, value3
Default: value1
--count N Number of results to generate
Default: 1
--brief Short output format
--json Output as JSON
Examples:
deno run --allow-read {{SCRIPT_NAME}}.ts
deno run --allow-read {{SCRIPT_NAME}}.ts --option value2
deno run --allow-read {{SCRIPT_NAME}}.ts "Custom Input" --json
`);
Deno.exit(0);
}
// Parse arguments
const optionIndex = args.indexOf("--option");
const countIndex = args.indexOf("--count");
const jsonOutput = args.includes("--json");
const briefOutput = args.includes("--brief");
const option = optionIndex !== -1 && args[optionIndex + 1]
? args[optionIndex + 1]
: null;
const count = countIndex !== -1 && args[countIndex + 1]
? parseInt(args[countIndex + 1]) || 1
: 1;
// Build skip indices for positional arg detection
const skipIndices = new Set<number>();
if (optionIndex !== -1) {
skipIndices.add(optionIndex);
skipIndices.add(optionIndex + 1);
}
if (countIndex !== -1) {
skipIndices.add(countIndex);
skipIndices.add(countIndex + 1);
}
// Find positional argument (first arg that's not a flag)
let positionalArg: string | null = null;
for (let i = 0; i < args.length; i++) {
if (!args[i].startsWith("--") && !skipIndices.has(i)) {
positionalArg = args[i];
break;
}
}
// Generate results
const results: {{RESULT_TYPE}}[] = [];
for (let i = 0; i < count; i++) {
results.push(generate(positionalArg, option));
}
// Output
if (jsonOutput) {
console.log(JSON.stringify(count === 1 ? results[0] : results, null, 2));
} else if (briefOutput) {
for (const result of results) {
console.log(formatBrief(result));
}
} else {
for (const result of results) {
console.log(formatResult(result));
if (results.length > 1) {
console.log("---\n");
}
}
}
}
main();
{{SKILL_TITLE}}: {{SUBTITLE}}
You {{ROLE_DESCRIPTION}}. Your role is to {{SPECIFIC_FUNCTION}}.
Core Principle
{{CORE_PRINCIPLE}}
Quick Reference
Use this skill when:
- {{QUICK_REFERENCE_USE_CASE_1}}
- {{QUICK_REFERENCE_USE_CASE_2}}
Key states:
- {{PREFIX}}1: {{STATE_1_NAME}} - {{STATE_1_BRIEF}}
- {{PREFIX}}2: {{STATE_2_NAME}} - {{STATE_2_BRIEF}}
- {{PREFIX}}3: {{STATE_3_NAME}} - {{STATE_3_BRIEF}}
The States
State {{PREFIX}}1: {{STATE_1_NAME}}
Symptoms: {{STATE_1_SYMPTOMS}} Key Questions: {{STATE_1_QUESTIONS}} Interventions: {{STATE_1_INTERVENTIONS}}
State {{PREFIX}}2: {{STATE_2_NAME}}
Symptoms: {{STATE_2_SYMPTOMS}} Key Questions: {{STATE_2_QUESTIONS}} Interventions: {{STATE_2_INTERVENTIONS}}
State {{PREFIX}}3: {{STATE_3_NAME}}
Symptoms: {{STATE_3_SYMPTOMS}} Key Questions: {{STATE_3_QUESTIONS}} Interventions: {{STATE_3_INTERVENTIONS}}
<!-- Add more states as needed (aim for 3-7 total) -->
Diagnostic Process
When a writer presents a {{DOMAIN}} problem:
1. Listen for symptoms - What specifically feels wrong? 2. Identify the state - Match symptoms to states above 3. Ask key questions - Gather information needed for diagnosis 4. Recommend intervention - Point to specific framework/tool 5. Suggest first step - What's the minimal viable fix?
Key Questions
For {{CATEGORY_A}}
- {{QUESTION_1}}
- {{QUESTION_2}}
- {{QUESTION_3}}
For {{CATEGORY_B}}
- {{QUESTION_4}}
- {{QUESTION_5}}
- {{QUESTION_6}}
Anti-Patterns
<!-- Minimum 3 anti-patterns for diagnostic skills, 2 for generator/utility -->
The {{ANTIPATTERN_1_NAME}}
Pattern: {{ANTIPATTERN_1_PATTERN}} Problem: {{ANTIPATTERN_1_PROBLEM}} Fix: {{ANTIPATTERN_1_FIX}}
The {{ANTIPATTERN_2_NAME}}
Pattern: {{ANTIPATTERN_2_PATTERN}} Problem: {{ANTIPATTERN_2_PROBLEM}} Fix: {{ANTIPATTERN_2_FIX}}
The {{ANTIPATTERN_3_NAME}}
Pattern: {{ANTIPATTERN_3_PATTERN}} Problem: {{ANTIPATTERN_3_PROBLEM}} Fix: {{ANTIPATTERN_3_FIX}}
Verification (Oracle)
This section documents what this skill can reliably verify vs. what requires human judgment. See organization/architecture/context-packet-architecture.md for background on oracles.
What This Skill Can Verify
- {{VERIFIABLE_1}} - [How: symptom matching / script output / checklist]
- {{VERIFIABLE_2}}
- {{VERIFIABLE_3}}
What Requires Human Judgment
- {{JUDGMENT_1}} - [Why: semantic quality / contextual fit / creative choice]
- {{JUDGMENT_2}}
Available Validation Scripts
<!-- If no scripts, write: "No validation scripts yet. Diagnostic process serves as the oracle." -->
| Script | Verifies | Confidence |
|---|---|---|
| {{SCRIPT_NAME}}.ts | {{WHAT_IT_CHECKS}} | {{HIGH_MEDIUM_LOW}} |
Feedback Loop
This section documents how outputs persist and inform future sessions. See organization/architecture/context-packet-architecture.md for background on feedback loops.
Session Persistence
- Output location: Check
context/output-config.mdor ask user - What to save: {{PRIMARY_OUTPUTS_TO_PERSIST}}
- Naming pattern:
{{NAMING_PATTERN}}
Cross-Session Learning
- Before starting: Check for prior outputs in configured location
- If prior output exists: Review previous state diagnosis, check if resolved
- What feedback improves this skill: {{FEEDBACK_THAT_IMPROVES}}
Design Constraints
This section documents preconditions and boundaries. See organization/architecture/context-packet-architecture.md for background on constraints.
This Skill Assumes
- {{ASSUMPTION_1}}
- {{ASSUMPTION_2}}
- {{ASSUMPTION_3}}
This Skill Does Not Handle
- {{NOT_HANDLED_1}} - Route to: {{ROUTE_TO_SKILL_1}}
- {{NOT_HANDLED_2}} - Route to: {{ROUTE_TO_SKILL_2}}
Degradation Signals
Signs this skill is being misapplied:
- {{DEGRADATION_1}}
- {{DEGRADATION_2}}
Available Tools
{{SCRIPT_NAME}}.ts
{{SCRIPT_DESCRIPTION}}
deno run --allow-read scripts/{{SCRIPT_NAME}}.ts [args]
deno run --allow-read scripts/{{SCRIPT_NAME}}.ts --option valueOutput: {{SCRIPT_OUTPUT_DESCRIPTION}}
Example Interaction
Writer: "{{EXAMPLE_PROBLEM}}"
Your approach: 1. Identify State {{PREFIX}}{{EXAMPLE_STATE}} ({{EXAMPLE_STATE_NAME}}) 2. Ask: "{{EXAMPLE_QUESTION}}" 3. {{EXAMPLE_ACTION}} 4. Suggest: "{{EXAMPLE_SUGGESTION}}"
Output Persistence
This skill writes primary output to files so work persists across sessions.
Output Discovery
Before doing any other work:
1. Check for context/output-config.md in the project 2. If found, look for this skill's entry 3. If not found or no entry for this skill, ask the user first:
- "Where should I save output from this {{SKILL_NAME}} session?"
- Suggest a sensible location for this project
4. Store the user's preference:
- In
context/output-config.mdif context network exists - In
.{{SKILL_NAME}}-output.mdat project root otherwise
Primary Output
For this skill, persist:
- {{PRIMARY_OUTPUT_1}}
- {{PRIMARY_OUTPUT_2}}
- {{PRIMARY_OUTPUT_3}}
Conversation vs. File
| Goes to File | Stays in Conversation |
|---|---|
| {{FILE_OUTPUT_1}} | {{CONVERSATION_1}} |
| {{FILE_OUTPUT_2}} | {{CONVERSATION_2}} |
File Naming
Pattern: {{NAMING_PATTERN}} Example: {{NAMING_EXAMPLE}}
What You Do NOT Do
- You do not {{BOUNDARY_1}}
- You do not {{BOUNDARY_2}}
- You diagnose, recommend, and explain—the writer decides
Reasoning Requirements
This section documents when this skill benefits from extended thinking time.
Standard Reasoning
- {{STANDARD_TASK_1}}
- {{STANDARD_TASK_2}}
Extended Reasoning (ultrathink)
Use extended thinking for:
- {{EXTENDED_TASK_1}} - [Why: {{EXTENDED_REASON_1}}]
- {{EXTENDED_TASK_2}} - [Why: {{EXTENDED_REASON_2}}]
Trigger phrases: "deep analysis", "comprehensive review", "multi-framework synthesis"
Execution Strategy
This section documents when to parallelize work or spawn subagents.
Sequential (Default)
- {{SEQUENTIAL_TASK}} - must complete before next step
Parallelizable
- {{PARALLEL_TASK_1}} and {{PARALLEL_TASK_2}} can run concurrently
- Use when: {{PARALLEL_CONDITION}}
Subagent Candidates
| Task | Agent Type | When to Spawn |
|---|---|---|
| {{SUBAGENT_TASK}} | Explore | {{SUBAGENT_CONDITION}} |
Context Management
This section documents token usage and optimization strategies.
Approximate Token Footprint
- Skill base: ~{{BASE_TOKENS}}k tokens
- With full state definitions: ~{{FULL_TOKENS}}k tokens
- With scripts inline: ~{{SCRIPTS_TOKENS}}k tokens (avoid unless needed)
Context Optimization
- Load scripts on-demand rather than including inline
- Reference framework documentation by name rather than embedding
- {{CONTEXT_TIP}}
When Context Gets Tight
- Prioritize: {{PRIORITY_CONTENT}}
- Defer: {{DEFER_CONTENT}}
- Drop: {{DROP_CONTENT}}
Integration Graph
Inbound (From Other Skills)
| Source Skill | Source State | Leads to State |
|---|---|---|
| story-sense | SS{{SS_STATE_1}}: {{SS_STATE_1_NAME}} | {{PREFIX}}{{INTEGRATION_1}}: {{INTEGRATION_1_NAME}} |
| {{OTHER_SOURCE_SKILL}} | {{OTHER_SOURCE_STATE}} | {{PREFIX}}{{OTHER_INTEGRATION}}: {{OTHER_INTEGRATION_NAME}} |
Outbound (To Other Skills)
| This State | Leads to Skill | Target State |
|---|---|---|
| {{PREFIX}}{{OUTBOUND_STATE}}: {{OUTBOUND_STATE_NAME}} | {{TARGET_SKILL}} | {{TARGET_STATE}} |
Complementary Skills
| Skill | Relationship |
|---|---|
| story-sense | Parent diagnostic skill |
| {{COMPLEMENTARY_SKILL_1}} | {{COMPLEMENTARY_RELATIONSHIP_1}} |
| {{COMPLEMENTARY_SKILL_2}} | {{COMPLEMENTARY_RELATIONSHIP_2}} |
Related skills
How it compares
Use skill-builder to author new skills; use find-skills when searching for existing capabilities to install.
FAQ
What does skill-builder help developers produce?
skill-builder guides creation, packaging, and iteration of reusable agent skills with proper metadata and triggers. Output includes SKILL.md files and manifests ready for Skillselion catalog publishing or direct installation by other agents.
Who should use skill-builder?
skill-builder fits developers converting repeated agent workflows into installable capabilities. With 494 installs and rank #22 on skills.sh, it targets teams building shared agent skill libraries rather than one-time prompt hacks.