feat: add well-known skills discovery endpoint

This commit is contained in:
Tommy D. Rossi
2026-02-04 19:20:33 +01:00
parent 0b60fe844e
commit a718299b73
3 changed files with 119 additions and 0 deletions
+78
View File
@@ -7,10 +7,13 @@
*
* Source of truth:
* - playwriter/src/skill.md - manually edited, contains full docs including CLI usage
* - skills/playwriter/SKILL.md - stub with frontmatter for agent discovery
*
* Generated files:
* - playwriter/dist/prompt.md - MCP prompt (skill.md minus CLI sections)
* - website/public/SKILL.md - full copy for playwriter.dev/SKILL.md
* - website/public/.well-known/skills/index.json - Agent Skills Discovery endpoint
* - website/public/.well-known/skills/playwriter/SKILL.md - skill file with frontmatter
*/
import fs from 'node:fs'
@@ -181,10 +184,85 @@ function buildPromptFromSkill() {
console.log('Generated website/public/SKILL.md')
}
/**
* Parses YAML frontmatter from a markdown file.
* Returns { frontmatter, body } where frontmatter is the parsed YAML object.
*/
function parseFrontmatter(content: string): { frontmatter: Record<string, string>; body: string } {
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/)
if (!match) {
return { frontmatter: {}, body: content }
}
const yamlContent = match[1]
const body = match[2]
// Simple YAML parsing for key: value pairs
const frontmatter: Record<string, string> = {}
for (const line of yamlContent.split('\n')) {
const colonIndex = line.indexOf(':')
if (colonIndex > 0) {
const key = line.slice(0, colonIndex).trim()
const value = line.slice(colonIndex + 1).trim()
frontmatter[key] = value
}
}
return { frontmatter, body }
}
/**
* Builds the Well-Known Skills Discovery structure.
*
* Creates:
* - /.well-known/skills/index.json - discovery endpoint
* - /.well-known/skills/playwriter/SKILL.md - skill file
*
* See: https://agentskills.io/specification
*/
function buildWellKnownSkills() {
const repoRoot = path.join(playwriterDir, '..')
const skillSourcePath = path.join(repoRoot, 'skills', 'playwriter', 'SKILL.md')
const websitePublicRoot = path.join(repoRoot, 'website', 'public')
const wellKnownDir = path.join(websitePublicRoot, '.well-known', 'skills')
const playwriterSkillDir = path.join(wellKnownDir, 'playwriter')
// Read and parse the skill file
const skillContent = fs.readFileSync(skillSourcePath, 'utf-8')
const { frontmatter } = parseFrontmatter(skillContent)
// Ensure directories exist
ensureDir(wellKnownDir)
ensureDir(playwriterSkillDir)
// Copy SKILL.md to well-known location
fs.writeFileSync(path.join(playwriterSkillDir, 'SKILL.md'), skillContent, 'utf-8')
console.log('Generated website/public/.well-known/skills/playwriter/SKILL.md')
// Generate index.json
const indexJson = {
skills: [
{
name: frontmatter.name || 'playwriter',
description: frontmatter.description || '',
files: ['SKILL.md']
}
]
}
fs.writeFileSync(
path.join(wellKnownDir, 'index.json'),
JSON.stringify(indexJson, null, 2) + '\n',
'utf-8'
)
console.log('Generated website/public/.well-known/skills/index.json')
}
// Run all builds
buildDebuggerApi()
buildEditorApi()
buildStylesApi()
buildPromptFromSkill()
buildWellKnownSkills()
console.log('Resource files generated successfully')
@@ -0,0 +1,11 @@
{
"skills": [
{
"name": "playwriter",
"description": "Control the user own Chrome browser via Playwriter extension with Playwright code snippets in a stateful local js sandbox via playwriter cli. Automate web interactions, take screenshots, inspect accessibility trees, debug & profile web applications. Run `playwriter skill` command to read the complete up to date skill",
"files": [
"SKILL.md"
]
}
]
}
@@ -0,0 +1,30 @@
---
name: playwriter
description: Control the user own Chrome browser via Playwriter extension with Playwright code snippets in a stateful local js sandbox via playwriter cli. Automate web interactions, take screenshots, inspect accessibility trees, debug & profile web applications. Run `playwriter skill` command to read the complete up to date skill
---
## REQUIRED: Read Full Documentation First
**Before using playwriter, you MUST run this command:**
```bash
playwriter skill
```
This outputs the complete documentation including:
- Session management and timeout configuration
- Selector strategies (and which ones to AVOID)
- Rules to prevent timeouts and failures
- Best practices for slow pages and SPAs
- Context variables, utility functions, and more
**Do NOT skip this step.** The quick examples below will fail without understanding timeouts, selector rules, and common pitfalls from the full docs.
## Minimal Example (after reading full docs)
```bash
playwriter session new
playwriter -s 1 -e "await page.goto('https://example.com')"
```
If `playwriter` is not found, use `npx playwriter@latest` or `bunx playwriter@latest`.