Installation:
composer require stolt/skill-md
Ensure your composer.json has "php": "^8.2" in require section.
First Use Case: Define a skill in your Laravel app (e.g., for a service or API endpoint):
use Stolt\SkillMd\SkillMd;
$skill = SkillMd::fromArray([
'name' => 'User Authentication',
'description' => 'Handles JWT-based authentication.',
'body' => '# Features
- Login/Logout
- Token refresh
- Role-based access',
'version' => '1.0.0',
'tags' => ['auth', 'security'],
]);
Where to Look First:
SkillMd class: Core abstraction for SKILL.md files.toMarkdown(): Convert skill data to Markdown for documentation.fromArray()/create(): Initialize from structured data.Skill Documentation Generation:
// In a Laravel controller or service
public function generateSkillDocs()
{
$skill = SkillMd::fromArray([...]);
$markdown = $skill->toMarkdown();
// Save to file or render in a view
Storage::put('skills/auth.md', $markdown);
return response()->view('skills.show', ['markdown' => $markdown]);
}
Dynamic Skill Metadata: Use in Laravel Service Providers or API Resources to attach metadata:
// app/Providers/AppServiceProvider.php
public function boot()
{
$skills = collect([
['name' => 'Email Service', 'tags' => ['mail', 'queue']],
['name' => 'Payment Gateway', 'tags' => ['payments']],
])->map(fn($data) => SkillMd::fromArray($data));
view()->share('skills', $skills);
}
Validation Integration: Combine with Laravel’s Form Requests for skill validation:
use Stolt\SkillMd\SkillMd;
public function rules()
{
return [
'skill.name' => 'required|string|max:255',
'skill.description' => 'required|string',
'skill.tags' => 'array',
'skill.tags.*' => 'string',
];
}
protected function prepareForValidation()
{
$this->merge([
'skill' => SkillMd::fromArray($this->all())->toArray(),
]);
}
Markdown Rendering:
Use with Laravel Markdown packages (e.g., spatie/laravel-markdown) to render skills in Blade:
@markdown($skill->toMarkdown())
Laravel Scout: Attach skills to models for searchable documentation:
class ApiEndpoint extends Model
{
public function skill()
{
return $this->morphOne(Skill::class, 'skillable');
}
}
$endpoint->skill = SkillMd::fromArray([...])->toMarkdown();
$endpoint->save();
API Metadata: Return skill data in API responses:
return response()->json([
'data' => $resource,
'metadata' => [
'skill' => SkillMd::fromArray($skillData)->toArray(),
],
]);
Caching: Cache generated Markdown for performance:
$markdown = Cache::remember("skill:{$skill->name()}", now()->addHours(1), function() use ($skill) {
return $skill->toMarkdown();
});
Field Whitelisting:
The package filters unsupported fields (e.g., unsupported-field returns null). Always check has() before accessing:
if ($skill->has('custom-field')) {
$value = $skill->get('custom-field');
}
Markdown Parsing:
body is treated as raw Markdown. Escape dynamic content to avoid XSS:
$safeBody = e($skill->body());
htmlspecialchars() if rendering user-provided Markdown.Case Sensitivity:
name → name:). Ensure consistency when parsing back.Versioning:
The package lacks semver validation. Validate version manually:
use Symfony\Component\Validator\Constraints as Assert;
$validator = Validator::make(['version' => $skill->version()], [
'version' => [new Assert\Regex('/^\d+\.\d+\.\d+$/')],
]);
dd($skill->toArray()); // Debug unsupported fields
Log::debug('Skill Markdown:', ['markdown' => $skill->toMarkdown()]);
Custom Fields: Extend the class to support additional fields:
class ExtendedSkillMd extends SkillMd
{
protected $customFields = ['author', 'maintainer'];
public function author()
{
return $this->get('author');
}
}
Markdown Templates:
Override toMarkdown() to customize frontmatter or body:
$skill->toMarkdown(function ($markdown) {
return str_replace('---', '---\ncustom-field: value', $markdown);
});
Laravel Service Container: Bind the package to a custom interface for dependency injection:
$this->app->bind(SkillInterface::class, function () {
return new SkillMd(...);
});
Configuration:
Store default skill metadata in config/skill.php:
return [
'default' => [
'license' => 'MIT',
'version' => '1.0.0',
],
];
Merge defaults in SkillMd constructor:
$skill = new SkillMd(
'Name',
'Description',
'Body',
array_merge(config('skill.default'), $customMetadata)
);
Artisan Commands: Generate skills from database records:
class GenerateSkillsCommand extends Command
{
public function handle()
{
$skills = Model::all()->map(fn($model) => SkillMd::fromArray([
'name' => $model->name,
'body' => $model->description,
'tags' => $model->tags,
]));
foreach ($skills as $skill) {
Storage::put("skills/{$skill->name()}.md", $skill->toMarkdown());
}
}
}
How can I help you explore Laravel packages today?