skill-scaffolder
Create new skills following pitlane-agent conventions. Use when adding new functionality that requires skill scaffolding, script generation, or test creation.
SKILL.md
| Name | skill-scaffolder |
| Description | Create new skills following pitlane-agent conventions. Use when adding new functionality that requires skill scaffolding, script generation, or test creation. |
name: skill-scaffolder description: Create new skills following pitlane-agent conventions. Use when adding new functionality that requires skill scaffolding, script generation, or test creation. allowed-tools: Write, Read, Bash(mkdir:*), Glob
Skill Scaffolder
This skill provides comprehensive instructions and templates for creating new skills in the pitlane-agent project. Use this skill when you need to scaffold new capabilities, add scripts, or set up test infrastructure.
When to Use This Skill
Use this skill when:
- Adding new functionality: Creating a new skill to handle specific user requests
- Building script-backed features: Need a click-based Python script with the skill
- Setting up tests: Creating test infrastructure for new scripts
- Creating skill hierarchies: Building parent skills with multiple related subskills
Naming Conventions
Skills
- Directory name:
kebab-case(e.g.,skill-scaffolder,f1-analyst) - YAML name field: Must match directory name exactly
- Markdown title: Title Case (e.g., "Skill Scaffolder", "F1 Data Analyst")
Scripts
- File name:
snake_case.py(e.g.,event_schedule.py,lap_times.py) - Business logic functions:
snake_case(e.g.,get_event_schedule,generate_chart) - CLI function: Always named
cli - Invocation:
python -m pitlane_agent.scripts.script_name
Tests
- File name:
test_<script_name>.py(e.g.,test_event_schedule.py) - Test classes:
Test<ClassName>(e.g.,TestBusinessLogic,TestCLI) - Test methods:
test_<what_it_tests>(e.g.,test_cli_success,test_validation_error)
Directory Structure
PitLane-AI/
├── packages/
│ └── pitlane-agent/
│ ├── src/
│ │ └── pitlane_agent/
│ │ ├── .claude/
│ │ │ └── skills/
│ │ │ ├── skill-name/ # Flat skill
│ │ │ │ └── SKILL.md
│ │ │ └── parent-skill/ # Hierarchical skill
│ │ │ ├── SKILL.md # Parent with table
│ │ │ ├── subskill-1.md # Subskill 1
│ │ │ └── subskill-2.md # Subskill 2
│ │ └── scripts/
│ │ └── script_name.py
│ └── tests/
│ └── scripts/
│ └── test_script_name.py
Step 1: Create a Flat Skill
A flat skill is a standalone skill contained in a single SKILL.md file.
Instructions
-
Create the skill directory:
mkdir -p packages/pitlane-agent/src/pitlane_agent/.claude/skills/your-skill-name -
Create SKILL.md using the template below
-
Customize the template:
- Replace
skill-namewith your kebab-case skill name - Update
descriptionto explain when to use the skill - Set appropriate
allowed-tools(common:Bash(python:*),Read,Write,Skill) - Fill in the skill content with instructions for Claude
- Replace
Flat Skill Template (SKILL.md)
---
name: skill-name
description: Brief description of when to use this skill. Should explain the trigger conditions.
allowed-tools: Bash(python:*), Read, Write
---
# Skill Title
Introduction paragraph explaining what this skill does and its purpose.
## When to Use This Skill
Use this skill when users ask about:
- **Use case 1**: Describe when this applies (e.g., "Race calendars and event dates")
- **Use case 2**: Another trigger scenario
- **Use case 3**: Additional context
## Step 1: Understand the Request
Provide guidance on how to parse and understand the user's request. Include:
- What parameters to extract
- Default values to use
- How to handle ambiguous requests
## Step 2: Execute the Main Task
Instructions for performing the core functionality. This might include:
- Running a script with bash
- Reading/analyzing files
- Using other tools
### Example Commands
If this skill uses scripts, provide examples:
```bash
python -m pitlane_agent.scripts.script_name --param1 value --param2 value
Step 3: Format the Response
Guidance on how Claude should structure the response to the user:
- What format to use (table, list, prose)
- What information to highlight
- How to handle edge cases
Data Reference (Optional)
If applicable, include reference data like:
- Field descriptions
- Valid values
- Lookup tables
- Common abbreviations
Example Questions and Approaches
Provide 3-5 realistic example questions with suggested approaches:
"Example question 1?"
- Step to handle it
- What to do next
- How to respond
"Example question 2?"
- Different approach for this scenario
- Handling variations
- Response format
Notes
Any important caveats, limitations, or additional context.
## Step 2: Create a Hierarchical Skill
Hierarchical skills have a parent SKILL.md that references multiple subskill markdown files.
### Instructions
1. Create the parent skill directory:
```bash
mkdir -p packages/pitlane-agent/src/pitlane_agent/.claude/skills/parent-skill-name
- Create the parent SKILL.md with a table of subskills
- Create separate markdown files for each subskill (e.g.,
subskill-1.md) - Keep hierarchy to 1 level (parent → subskills only)
Hierarchical Parent Template (SKILL.md)
---
name: parent-skill
description: Parent skill managing multiple related subskills. Briefly explain the domain.
allowed-tools: Skill
---
# Parent Skill Title
This skill provides access to multiple related subskills for [domain/topic area].
## Available Subskills
| Subskill | Description | Documentation |
|----------|-------------|---------------|
| subskill-1 | Brief description of what it does | [subskill-1.md](./subskill-1.md) |
| subskill-2 | Brief description of what it does | [subskill-2.md](./subskill-2.md) |
| subskill-3 | Brief description of what it does | [subskill-3.md](./subskill-3.md) |
## How to Use
When a user asks about [topic], determine which subskill is most appropriate based on:
- **Criteria 1**: Use subskill-1
- **Criteria 2**: Use subskill-2
- **Criteria 3**: Use subskill-3
Invoke the appropriate subskill using the Skill tool.
## Example Questions
Map common user questions to the right subskill:
- "Question pattern 1" → Use subskill-1
- "Question pattern 2" → Use subskill-2
- "Question pattern 3" → Use subskill-3
## Notes
Any cross-cutting concerns or general guidance for using these subskills together.
Subskill File Template (subskill-1.md)
# Subskill Title
Detailed instructions for this specific subskill. This file has the same structure as a flat SKILL.md but without the YAML front matter (that's only in the parent).
## When to Use
Specific trigger conditions for this subskill.
## Step 1: [First Step]
Detailed instructions...
## Step 2: [Second Step]
More instructions...
## Example Usage
Examples specific to this subskill...
Step 3: Create a Click-Based Script (Optional)
If your skill needs a Python script to perform actions, create it using click.
Instructions
-
Create the script file:
touch packages/pitlane-agent/src/pitlane_agent/scripts/your_script_name.py -
Use the template below as a starting point
-
Customize:
- Replace
script_nameandmain_functionwith your names - Add appropriate click options
- Implement business logic in the main function
- Ensure JSON output to stdout
- Handle errors with JSON to stderr and sys.exit(1)
- Replace
Click Script Template
"""
Brief description of what this script does.
Usage:
python -m pitlane_agent.scripts.script_name [OPTIONS]
"""
import json
import sys
from pathlib import Path
import click
def main_function(
param1: str,
param2: int,
) -> dict:
"""Business logic function.
Separate the business logic from CLI handling to make it easier to test.
Args:
param1: Description of first parameter
param2: Description of second parameter
Returns:
Dictionary with results in a structured format
Raises:
ValueError: If validation fails
RuntimeError: If operation fails
"""
# Input validation
if not param1:
raise ValueError("param1 cannot be empty")
if param2 < 0:
raise ValueError("param2 must be non-negative")
# Business logic implementation
# ... your code here ...
# Return structured result
result = {
"param1": param1,
"param2": param2,
"status": "success",
"data": {
# Your actual results
}
}
return result
@click.command()
@click.option(
"--param1",
required=True,
help="Description of param1"
)
@click.option(
"--param2",
type=int,
default=100,
help="Description of param2 (default: 100)"
)
@click.option(
"--output",
type=click.Path(),
help="Optional output file path"
)
def cli(param1: str, param2: int, output: str | None):
"""Command description that appears in --help."""
try:
result = main_function(param1=param1, param2=param2)
# Pretty-print JSON output
output_json = json.dumps(result, indent=2)
if output:
# Write to file if specified
Path(output).parent.mkdir(parents=True, exist_ok=True)
Path(output).write_text(output_json)
click.echo(json.dumps({"status": "written", "path": output}))
else:
# Print to stdout
click.echo(output_json)
except Exception as e:
# Error output to stderr with exit code 1
click.echo(json.dumps({"error": str(e)}), err=True)
sys.exit(1)
if __name__ == "__main__":
cli()
Common Click Patterns
Multiple values for an option:
@click.option(
"--driver",
multiple=True,
help="Driver abbreviation (can specify multiple)"
)
def cli(driver: tuple[str, ...]):
drivers = list(driver) # Convert tuple to list
Boolean flags:
@click.option(
"--include-testing/--no-testing",
default=True,
help="Include testing sessions"
)
def cli(include_testing: bool):
pass
Choice from list:
@click.option(
"--session",
type=click.Choice(['FP1', 'FP2', 'FP3', 'Q', 'S', 'R']),
required=True,
help="Session type"
)
def cli(session: str):
pass
File path:
@click.option(
"--config",
type=click.Path(exists=True, dir_okay=False),
help="Path to config file"
)
def cli(config: str):
pass
Step 4: Create Tests (Optional)
If you created a script, create comprehensive tests using pytest and click.testing.CliRunner.
Instructions
-
Create the test file:
mkdir -p packages/pitlane-agent/tests/scripts touch packages/pitlane-agent/tests/scripts/test_your_script_name.py -
Use the template below
-
Create two test classes:
TestBusinessLogic: Unit tests for business functionsTestCLI: Integration tests for CLI interface
Test Template
"""Tests for script_name script."""
import json
from unittest.mock import MagicMock, patch
import pytest
from click.testing import CliRunner
from pitlane_agent.scripts.script_name import cli, main_function
class TestMainFunctionBusinessLogic:
"""Unit tests for business logic functions.
These tests directly call business functions without going through the CLI.
Mock any external dependencies (APIs, file I/O, etc.).
"""
def test_main_function_success(self):
"""Test successful execution with valid inputs."""
result = main_function(param1="test", param2=100)
assert result["param1"] == "test"
assert result["param2"] == 100
assert result["status"] == "success"
assert "data" in result
def test_main_function_validation_error_empty_param1(self):
"""Test that empty param1 raises ValueError."""
with pytest.raises(ValueError, match="param1 cannot be empty"):
main_function(param1="", param2=100)
def test_main_function_validation_error_negative_param2(self):
"""Test that negative param2 raises ValueError."""
with pytest.raises(ValueError, match="param2 must be non-negative"):
main_function(param1="test", param2=-1)
@patch("pitlane_agent.scripts.script_name.some_external_dependency")
def test_main_function_with_mocked_dependency(self, mock_dependency):
"""Test with mocked external dependencies."""
# Setup mock
mock_dependency.return_value = {"data": "mocked"}
result = main_function(param1="test", param2=100)
# Verify mock was called correctly
mock_dependency.assert_called_once()
assert result["status"] == "success"
class TestCLI:
"""Integration tests for CLI interface using CliRunner.
These tests invoke the CLI as a user would and verify the output.
"""
def test_cli_help(self):
"""Test that --help displays usage information."""
runner = CliRunner()
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
assert "--param1" in result.output
assert "--param2" in result.output
assert "Command description" in result.output
def test_cli_success(self):
"""Test successful CLI execution."""
runner = CliRunner()
result = runner.invoke(cli, [
"--param1", "test",
"--param2", "200"
])
assert result.exit_code == 0
# Parse JSON output
output = json.loads(result.output)
assert output["param1"] == "test"
assert output["param2"] == 200
assert output["status"] == "success"
def test_cli_with_defaults(self):
"""Test CLI with default values for optional params."""
runner = CliRunner()
result = runner.invoke(cli, [
"--param1", "test"
# param2 should use default value of 100
])
assert result.exit_code == 0
output = json.loads(result.output)
assert output["param2"] == 100
def test_cli_missing_required(self):
"""Test that missing required option shows error."""
runner = CliRunner()
result = runner.invoke(cli, [])
assert result.exit_code != 0
assert "param1" in result.output or "required" in result.output.lower()
def test_cli_error_handling(self):
"""Test that errors are properly formatted as JSON to stderr."""
runner = CliRunner()
result = runner.invoke(cli, [
"--param1", "", # This should trigger validation error
"--param2", "100"
])
assert result.exit_code == 1
# Error should be JSON formatted
error_output = json.loads(result.output)
assert "error" in error_output
assert "cannot be empty" in error_output["error"]
def test_cli_with_output_file(self, tmp_path):
"""Test writing output to a file."""
runner = CliRunner()
output_file = tmp_path / "output.json"
result = runner.invoke(cli, [
"--param1", "test",
"--param2", "100",
"--output", str(output_file)
])
assert result.exit_code == 0
assert output_file.exists()
# Verify file contents
saved_data = json.loads(output_file.read_text())
assert saved_data["param1"] == "test"
@patch("pitlane_agent.scripts.script_name.main_function")
def test_cli_calls_business_logic(self, mock_main_function):
"""Test that CLI correctly calls business logic function."""
mock_main_function.return_value = {"status": "mocked"}
runner = CliRunner()
result = runner.invoke(cli, [
"--param1", "test",
"--param2", "100"
])
# Verify business function was called with correct args
mock_main_function.assert_called_once_with(
param1="test",
param2=100
)
Running Tests
# Run all tests for a script
uv run pytest packages/pitlane-agent/tests/scripts/test_script_name.py -v
# Run specific test class
uv run pytest packages/pitlane-agent/tests/scripts/test_script_name.py::TestCLI -v
# Run specific test method
uv run pytest packages/pitlane-agent/tests/scripts/test_script_name.py::TestCLI::test_cli_success -v
# Run with coverage
uv run pytest packages/pitlane-agent/tests/scripts/test_script_name.py --cov=pitlane_agent.scripts.script_name
Step 5: Verify Setup
After creating your skill, script, and tests, verify everything works:
1. Verify Skill Structure
- SKILL.md exists in correct location
- YAML front matter is valid (name, description, allowed-tools)
- Instructions are clear and actionable
- Templates are properly formatted
- For hierarchical skills: parent links to subskills correctly
2. Test Script Execution
# Test help output
python -m pitlane_agent.scripts.your_script_name --help
# Test with sample inputs
python -m pitlane_agent.scripts.your_script_name --param1 test --param2 100
# Verify JSON output format
python -m pitlane_agent.scripts.your_script_name --param1 test | jq .
3. Run Tests
# Run all tests
uv run pytest packages/pitlane-agent/tests/scripts/test_your_script_name.py -v
# Check coverage
uv run pytest packages/pitlane-agent/tests/scripts/test_your_script_name.py \
--cov=pitlane_agent.scripts.your_script_name \
--cov-report=term-missing
4. Test Skill Usage
Try using the skill in a Claude conversation to ensure it works as expected.
Examples
Example 1: Simple Flat Skill (No Script)
A skill that provides guidance without needing a script:
---
name: code-review-helper
description: Assist with code review tasks. Use when user asks for code reviews, style checks, or best practices.
allowed-tools: Read, Glob, Grep
---
# Code Review Helper
This skill helps perform thorough code reviews following best practices.
## When to Use This Skill
Use when users ask to:
- Review code changes
- Check coding standards
- Suggest improvements
- Identify bugs or issues
## Step 1: Understand Scope
Determine what needs to be reviewed:
- Specific files: Use Read tool
- Pattern-based: Use Glob to find files
- Search for patterns: Use Grep
## Step 2: Analyze Code
Review for:
- Code style and formatting
- Potential bugs
- Performance issues
- Security vulnerabilities
- Best practices
## Step 3: Provide Feedback
Structure feedback as:
1. Summary of findings
2. Critical issues (must fix)
3. Suggestions (should consider)
4. Positive observations
Example 2: Script-Backed Flat Skill
The f1-schedule skill is a good example:
- SKILL.md provides instructions
- References
event_schedule.pyscript - Explains how to use script with different parameters
- Shows how to format responses
Example 3: Hierarchical Skill
A data analysis skill with subskills:
SKILL.md (Parent):
---
name: data-analysis
description: Comprehensive data analysis tools. Use when user needs data processing, visualization, or statistical analysis.
allowed-tools: Skill
---
# Data Analysis
This skill provides access to specialized data analysis subskills.
## Available Subskills
| Subskill | Description | Documentation |
|----------|-------------|---------------|
| data-cleaning | Clean and prepare datasets | [data-cleaning.md](./data-cleaning.md) |
| data-viz | Create visualizations | [data-viz.md](./data-viz.md) |
| statistics | Statistical analysis | [statistics.md](./statistics.md) |
## How to Use
- Data preparation tasks → data-cleaning
- Chart/graph requests → data-viz
- Statistical questions → statistics
data-cleaning.md (Subskill):
# Data Cleaning
Instructions for cleaning and preparing datasets.
## When to Use
Use when data needs:
- Missing value handling
- Outlier detection
- Format standardization
- Deduplication
## Steps...
Common Patterns
Allowed Tools
Choose tools based on what the skill needs to do:
- Read-only analysis:
Read, Glob, Grep - Script execution:
Bash(python:*), Read, Write - File creation:
Write, Read, Bash(mkdir:*) - Hierarchical skills:
Skill(to invoke subskills) - Web access:
WebFetch, WebSearch(if needed)
Restrictions
You can restrict Bash usage:
Bash(python:*)- Only Python commandsBash(git:*)- Only Git commandsBash(npm:*)- Only npm commands
Error Handling in Scripts
Always handle errors gracefully:
try:
result = main_function(...)
click.echo(json.dumps(result, indent=2))
except ValueError as e:
click.echo(json.dumps({"error": f"Validation error: {str(e)}"}), err=True)
sys.exit(1)
except Exception as e:
click.echo(json.dumps({"error": f"Unexpected error: {str(e)}"}), err=True)
sys.exit(1)
Best Practices
- Keep skills focused: One skill should do one thing well
- Provide examples: Include 3-5 realistic example questions
- Document edge cases: Note limitations and special cases
- Use consistent formatting: Follow the templates provided
- Test thoroughly: Ensure scripts work before creating the skill
- Write clear instructions: Claude should be able to follow without guessing
- Separate concerns: Business logic separate from CLI handling
- Type hints: Use Python type hints in all functions
- JSON output: Always output structured JSON from scripts
- Test coverage: Aim for >90% coverage with meaningful tests
Troubleshooting
Issue: YAML parsing error
Solution: Ensure YAML front matter has exactly 3 dashes before and after, no extra whitespace
Issue: Script not found
Solution: Verify script is in packages/pitlane-agent/src/pitlane_agent/scripts/ and has .py extension
Issue: Tests failing
Solution: Check imports match the actual module structure, ensure mocks are properly configured
Issue: Click options not working
Solution: Verify option names match function parameters, check types and defaults
Issue: Hierarchical skill table not rendering
Solution: Ensure table has proper markdown format with pipes and dashes, check file paths are relative
Notes
- No automation script: This skill provides instructions only. You'll create files using Write tool.
- Convention adherence: Following conventions ensures consistency across the codebase.
- Test-first approach: Consider writing tests before implementing complex business logic.
- Click is required: All new scripts must use click, not argparse.
- Flat is preferred: Only create hierarchical skills when you have 3+ related subskills.